diff --git a/.venv/bin/accelerate b/.venv/bin/accelerate new file mode 100644 index 0000000000000000000000000000000000000000..ad4287eb6ca2676231840edaaf62f6d225187969 --- /dev/null +++ b/.venv/bin/accelerate @@ -0,0 +1,10 @@ +#!/home/ubuntu/lyl/QwenIllustrious/.venv/bin/python3 +# -*- coding: utf-8 -*- +import sys +from accelerate.commands.accelerate_cli import main +if __name__ == "__main__": + if sys.argv[0].endswith("-script.pyw"): + sys.argv[0] = sys.argv[0][:-11] + elif sys.argv[0].endswith(".exe"): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/accelerate-config b/.venv/bin/accelerate-config new file mode 100644 index 0000000000000000000000000000000000000000..e8728ec7ef45431cc368aa266e1496631263ce77 --- /dev/null +++ b/.venv/bin/accelerate-config @@ -0,0 +1,10 @@ +#!/home/ubuntu/lyl/QwenIllustrious/.venv/bin/python3 +# -*- coding: utf-8 -*- +import sys +from accelerate.commands.config import main +if __name__ == "__main__": + if sys.argv[0].endswith("-script.pyw"): + sys.argv[0] = sys.argv[0][:-11] + elif sys.argv[0].endswith(".exe"): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/accelerate-estimate-memory b/.venv/bin/accelerate-estimate-memory new file mode 100644 index 0000000000000000000000000000000000000000..7f2a598d7cd099bfaffd5b7ba4541103775b52ee --- /dev/null +++ b/.venv/bin/accelerate-estimate-memory @@ -0,0 +1,10 @@ +#!/home/ubuntu/lyl/QwenIllustrious/.venv/bin/python3 +# -*- coding: utf-8 -*- +import sys +from accelerate.commands.estimate import main +if __name__ == "__main__": + if sys.argv[0].endswith("-script.pyw"): + sys.argv[0] = sys.argv[0][:-11] + elif sys.argv[0].endswith(".exe"): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/accelerate-launch b/.venv/bin/accelerate-launch new file mode 100644 index 0000000000000000000000000000000000000000..ff3c43a0fab78000f20b3a6aec0add115426a5a0 --- /dev/null +++ b/.venv/bin/accelerate-launch @@ -0,0 +1,10 @@ +#!/home/ubuntu/lyl/QwenIllustrious/.venv/bin/python3 +# -*- coding: utf-8 -*- +import sys +from accelerate.commands.launch import main +if __name__ == "__main__": + if sys.argv[0].endswith("-script.pyw"): + sys.argv[0] = sys.argv[0][:-11] + elif sys.argv[0].endswith(".exe"): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/accelerate-merge-weights b/.venv/bin/accelerate-merge-weights new file mode 100644 index 0000000000000000000000000000000000000000..99ba5c66e705adcc2c757b0f8eaa4349b19c6f23 --- /dev/null +++ b/.venv/bin/accelerate-merge-weights @@ -0,0 +1,10 @@ +#!/home/ubuntu/lyl/QwenIllustrious/.venv/bin/python3 +# -*- coding: utf-8 -*- +import sys +from accelerate.commands.merge import main +if __name__ == "__main__": + if sys.argv[0].endswith("-script.pyw"): + sys.argv[0] = sys.argv[0][:-11] + elif sys.argv[0].endswith(".exe"): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/activate b/.venv/bin/activate new file mode 100644 index 0000000000000000000000000000000000000000..32a5b3f0b96e0461f616dc4c50a9b59f397d8070 --- /dev/null +++ b/.venv/bin/activate @@ -0,0 +1,130 @@ +# Copyright (c) 2020-202x The virtualenv developers +# +# 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. + +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + +if ! [ -z "${SCRIPT_PATH+_}" ] ; then + _OLD_SCRIPT_PATH="$SCRIPT_PATH" +fi + +# Get script path (only used if environment is relocatable). +if [ -n "${BASH_VERSION:+x}" ] ; then + SCRIPT_PATH="${BASH_SOURCE[0]}" + if [ "$SCRIPT_PATH" = "$0" ]; then + # Only bash has a reasonably robust check for source'dness. + echo "You must source this script: \$ source $0" >&2 + exit 33 + fi +elif [ -n "${ZSH_VERSION:+x}" ] ; then + SCRIPT_PATH="${(%):-%x}" +elif [ -n "${KSH_VERSION:+x}" ] ; then + SCRIPT_PATH="${.sh.file}" +fi + +deactivate () { + unset -f pydoc >/dev/null 2>&1 || true + + # reset old environment variables + # ! [ -z ${VAR+_} ] returns true if VAR is declared at all + if ! [ -z "${_OLD_VIRTUAL_PATH:+_}" ] ; then + PATH="$_OLD_VIRTUAL_PATH" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if ! [ -z "${_OLD_VIRTUAL_PYTHONHOME+_}" ] ; then + PYTHONHOME="$_OLD_VIRTUAL_PYTHONHOME" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # The hash command must be called to get it to forget past + # commands. Without forgetting past commands the $PATH changes + # we made may not be respected + hash -r 2>/dev/null + + if ! [ -z "${_OLD_VIRTUAL_PS1+_}" ] ; then + PS1="$_OLD_VIRTUAL_PS1" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + unset VIRTUAL_ENV_PROMPT + if [ ! "${1-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV='/home/ubuntu/lyl/QwenIllustrious/.venv' +if ([ "$OSTYPE" = "cygwin" ] || [ "$OSTYPE" = "msys" ]) && $(command -v cygpath &> /dev/null) ; then + VIRTUAL_ENV=$(cygpath -u "$VIRTUAL_ENV") +fi +export VIRTUAL_ENV + +# Unset the `SCRIPT_PATH` variable, now that the `VIRTUAL_ENV` variable +# has been set. This is important for relocatable environments. +if ! [ -z "${_OLD_SCRIPT_PATH+_}" ] ; then + SCRIPT_PATH="$_OLD_SCRIPT_PATH" + export SCRIPT_PATH + unset _OLD_SCRIPT_PATH +else + unset SCRIPT_PATH +fi + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +if [ "xQwenIllustrious" != x ] ; then + VIRTUAL_ENV_PROMPT="(QwenIllustrious) " +else + VIRTUAL_ENV_PROMPT="($(basename "$VIRTUAL_ENV")) " +fi +export VIRTUAL_ENV_PROMPT + +# unset PYTHONHOME if set +if ! [ -z "${PYTHONHOME+_}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="$PYTHONHOME" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1-}" + PS1="${VIRTUAL_ENV_PROMPT}${PS1-}" + export PS1 +fi + +# Make sure to unalias pydoc if it's already there +alias pydoc 2>/dev/null >/dev/null && unalias pydoc || true + +pydoc () { + python -m pydoc "$@" +} + +# The hash command must be called to get it 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.bat b/.venv/bin/activate.bat new file mode 100644 index 0000000000000000000000000000000000000000..b55530a45ecf59edd04060311c2b158c5a0e6d2b --- /dev/null +++ b/.venv/bin/activate.bat @@ -0,0 +1,71 @@ +@REM Copyright (c) 2020-202x The virtualenv developers +@REM +@REM Permission is hereby granted, free of charge, to any person obtaining +@REM a copy of this software and associated documentation files (the +@REM "Software"), to deal in the Software without restriction, including +@REM without limitation the rights to use, copy, modify, merge, publish, +@REM distribute, sublicense, and/or sell copies of the Software, and to +@REM permit persons to whom the Software is furnished to do so, subject to +@REM the following conditions: +@REM +@REM The above copyright notice and this permission notice shall be +@REM included in all copies or substantial portions of the Software. +@REM +@REM THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +@REM EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +@REM MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +@REM NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +@REM LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +@REM OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +@REM WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +@REM This file is UTF-8 encoded, so we need to update the current code page while executing it +@for /f "tokens=2 delims=:." %%a in ('"%SystemRoot%\System32\chcp.com"') do @set _OLD_CODEPAGE=%%a +@if defined _OLD_CODEPAGE ( + @"%SystemRoot%\System32\chcp.com" 65001 > nul +) + +@for %%i in ("/home/ubuntu/lyl/QwenIllustrious/.venv") do @set "VIRTUAL_ENV=%%~fi" + +@set "VIRTUAL_ENV_PROMPT=QwenIllustrious" +@if NOT DEFINED VIRTUAL_ENV_PROMPT ( + @for %%d in ("%VIRTUAL_ENV%") do @set "VIRTUAL_ENV_PROMPT=%%~nxd" +) + +@if defined _OLD_VIRTUAL_PROMPT ( + @set "PROMPT=%_OLD_VIRTUAL_PROMPT%" +) else ( + @if not defined PROMPT ( + @set "PROMPT=$P$G" + ) + @if not defined VIRTUAL_ENV_DISABLE_PROMPT ( + @set "_OLD_VIRTUAL_PROMPT=%PROMPT%" + ) +) +@if not defined VIRTUAL_ENV_DISABLE_PROMPT ( + @set "PROMPT=(%VIRTUAL_ENV_PROMPT%) %PROMPT%" +) + +@REM Don't use () to avoid problems with them in %PATH% +@if defined _OLD_VIRTUAL_PYTHONHOME @goto ENDIFVHOME + @set "_OLD_VIRTUAL_PYTHONHOME=%PYTHONHOME%" +:ENDIFVHOME + +@set PYTHONHOME= + +@REM if defined _OLD_VIRTUAL_PATH ( +@if not defined _OLD_VIRTUAL_PATH @goto ENDIFVPATH1 + @set "PATH=%_OLD_VIRTUAL_PATH%" +:ENDIFVPATH1 +@REM ) else ( +@if defined _OLD_VIRTUAL_PATH @goto ENDIFVPATH2 + @set "_OLD_VIRTUAL_PATH=%PATH%" +:ENDIFVPATH2 + +@set "PATH=%VIRTUAL_ENV%\bin;%PATH%" + +:END +@if defined _OLD_CODEPAGE ( + @"%SystemRoot%\System32\chcp.com" %_OLD_CODEPAGE% > nul + @set _OLD_CODEPAGE= +) diff --git a/.venv/bin/activate.csh b/.venv/bin/activate.csh new file mode 100644 index 0000000000000000000000000000000000000000..7db440dd5cb1f63aa4e17aa3e3a0aa1167452345 --- /dev/null +++ b/.venv/bin/activate.csh @@ -0,0 +1,76 @@ +# Copyright (c) 2020-202x The virtualenv developers +# +# 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. + +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . + +set newline='\ +' + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH:q" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT:q" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate && unalias pydoc' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV '/home/ubuntu/lyl/QwenIllustrious/.venv' + +set _OLD_VIRTUAL_PATH="$PATH:q" +setenv PATH "$VIRTUAL_ENV:q/bin:$PATH:q" + + + +if ('QwenIllustrious' != "") then + setenv VIRTUAL_ENV_PROMPT 'QwenIllustrious' +else + setenv VIRTUAL_ENV_PROMPT "$VIRTUAL_ENV:t:q" +endif + +if ( $?VIRTUAL_ENV_DISABLE_PROMPT ) then + if ( $VIRTUAL_ENV_DISABLE_PROMPT == "" ) then + set do_prompt = "1" + else + set do_prompt = "0" + endif +else + set do_prompt = "1" +endif + +if ( $do_prompt == "1" ) then + # Could be in a non-interactive environment, + # in which case, $prompt is undefined and we wouldn't + # care about the prompt anyway. + if ( $?prompt ) then + set _OLD_VIRTUAL_PROMPT="$prompt:q" + if ( "$prompt:q" =~ *"$newline:q"* ) then + : + else + set prompt = '('"$VIRTUAL_ENV_PROMPT:q"') '"$prompt:q" + endif + endif +endif + +unset env_name +unset do_prompt + +alias pydoc python -m pydoc + +rehash diff --git a/.venv/bin/activate.fish b/.venv/bin/activate.fish new file mode 100644 index 0000000000000000000000000000000000000000..de1e8d4865261739060299d45c2c6522947c3313 --- /dev/null +++ b/.venv/bin/activate.fish @@ -0,0 +1,124 @@ +# Copyright (c) 2020-202x The virtualenv developers +# +# 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. + +# This file must be used using `source bin/activate.fish` *within a running fish ( http://fishshell.com ) session*. +# Do not run it directly. + +function _bashify_path -d "Converts a fish path to something bash can recognize" + set fishy_path $argv + set bashy_path $fishy_path[1] + for path_part in $fishy_path[2..-1] + set bashy_path "$bashy_path:$path_part" + end + echo $bashy_path +end + +function _fishify_path -d "Converts a bash path to something fish can recognize" + echo $argv | tr ':' '\n' +end + +function deactivate -d 'Exit virtualenv mode and return to the normal environment.' + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + # https://github.com/fish-shell/fish-shell/issues/436 altered PATH handling + if test (echo $FISH_VERSION | head -c 1) -lt 3 + set -gx PATH (_fishify_path "$_OLD_VIRTUAL_PATH") + else + set -gx PATH $_OLD_VIRTUAL_PATH + end + 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" + and functions -q _old_fish_prompt + # Set an empty local `$fish_function_path` to allow the removal of `fish_prompt` using `functions -e`. + set -l fish_function_path + + # Erase virtualenv's `fish_prompt` and restore the original. + functions -e fish_prompt + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + set -e _OLD_FISH_PROMPT_OVERRIDE + end + + set -e VIRTUAL_ENV + set -e VIRTUAL_ENV_PROMPT + + if test "$argv[1]" != 'nondestructive' + # Self-destruct! + functions -e pydoc + functions -e deactivate + functions -e _bashify_path + functions -e _fishify_path + end +end + +# Unset irrelevant variables. +deactivate nondestructive + +set -gx VIRTUAL_ENV '/home/ubuntu/lyl/QwenIllustrious/.venv' + +# https://github.com/fish-shell/fish-shell/issues/436 altered PATH handling +if test (echo $FISH_VERSION | head -c 1) -lt 3 + set -gx _OLD_VIRTUAL_PATH (_bashify_path $PATH) +else + set -gx _OLD_VIRTUAL_PATH $PATH +end +set -gx PATH "$VIRTUAL_ENV"'/bin' $PATH + +# Prompt override provided? +# If not, just use the environment name. +if test -n 'QwenIllustrious' + set -gx VIRTUAL_ENV_PROMPT 'QwenIllustrious' +else + set -gx VIRTUAL_ENV_PROMPT (basename "$VIRTUAL_ENV") +end + +# Unset `$PYTHONHOME` if set. +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +function pydoc + python -m pydoc $argv +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # Copy the current `fish_prompt` function as `_old_fish_prompt`. + functions -c fish_prompt _old_fish_prompt + + function fish_prompt + # Run the user's prompt first; it might depend on (pipe)status. + set -l prompt (_old_fish_prompt) + + printf '(%s) ' $VIRTUAL_ENV_PROMPT + + string join -- \n $prompt # handle multi-line prompts + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" +end diff --git a/.venv/bin/activate.nu b/.venv/bin/activate.nu new file mode 100644 index 0000000000000000000000000000000000000000..3b241bebb31c1f656ce772a42db790a8337e36bd --- /dev/null +++ b/.venv/bin/activate.nu @@ -0,0 +1,117 @@ +# Copyright (c) 2020-202x The virtualenv developers +# +# 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. + +# virtualenv activation module +# Activate with `overlay use activate.nu` +# Deactivate with `deactivate`, as usual +# +# To customize the overlay name, you can call `overlay use activate.nu as foo`, +# but then simply `deactivate` won't work because it is just an alias to hide +# the "activate" overlay. You'd need to call `overlay hide foo` manually. + +export-env { + def is-string [x] { + ($x | describe) == 'string' + } + + def has-env [...names] { + $names | each {|n| + $n in $env + } | all {|i| $i == true} + } + + # Emulates a `test -z`, but better as it handles e.g 'false' + def is-env-true [name: string] { + if (has-env $name) { + # Try to parse 'true', '0', '1', and fail if not convertible + let parsed = (do -i { $env | get $name | into bool }) + if ($parsed | describe) == 'bool' { + $parsed + } else { + not ($env | get -i $name | is-empty) + } + } else { + false + } + } + + let virtual_env = '/home/ubuntu/lyl/QwenIllustrious/.venv' + let bin = 'bin' + + let is_windows = ($nu.os-info.family) == 'windows' + let path_name = (if (has-env 'Path') { + 'Path' + } else { + 'PATH' + } + ) + + let venv_path = ([$virtual_env $bin] | path join) + let new_path = ($env | get $path_name | prepend $venv_path) + + # If there is no default prompt, then use the env name instead + let virtual_env_prompt = (if ('QwenIllustrious' | is-empty) { + ($virtual_env | path basename) + } else { + 'QwenIllustrious' + }) + + let new_env = { + $path_name : $new_path + VIRTUAL_ENV : $virtual_env + VIRTUAL_ENV_PROMPT : $virtual_env_prompt + } + + let new_env = (if (is-env-true 'VIRTUAL_ENV_DISABLE_PROMPT') { + $new_env + } else { + # Creating the new prompt for the session + let virtual_prefix = $'(char lparen)($virtual_env_prompt)(char rparen) ' + + # Back up the old prompt builder + let old_prompt_command = (if (has-env 'PROMPT_COMMAND') { + $env.PROMPT_COMMAND + } else { + '' + }) + + let new_prompt = (if (has-env 'PROMPT_COMMAND') { + if 'closure' in ($old_prompt_command | describe) { + {|| $'($virtual_prefix)(do $old_prompt_command)' } + } else { + {|| $'($virtual_prefix)($old_prompt_command)' } + } + } else { + {|| $'($virtual_prefix)' } + }) + + $new_env | merge { + PROMPT_COMMAND : $new_prompt + VIRTUAL_PREFIX : $virtual_prefix + } + }) + + # Environment variables that will be loaded as the virtual env + load-env $new_env +} + +export alias pydoc = python -m pydoc +export alias deactivate = overlay hide activate diff --git a/.venv/bin/activate.ps1 b/.venv/bin/activate.ps1 new file mode 100644 index 0000000000000000000000000000000000000000..d93165190c86af8413426c5373a1216cda3d8a14 --- /dev/null +++ b/.venv/bin/activate.ps1 @@ -0,0 +1,82 @@ +# Copyright (c) 2020-202x The virtualenv developers +# +# 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. + +$script:THIS_PATH = $myinvocation.mycommand.path +$script:BASE_DIR = Split-Path (Resolve-Path "$THIS_PATH/..") -Parent + +function global:deactivate([switch] $NonDestructive) { + if (Test-Path variable:_OLD_VIRTUAL_PATH) { + $env:PATH = $variable:_OLD_VIRTUAL_PATH + Remove-Variable "_OLD_VIRTUAL_PATH" -Scope global + } + + if (Test-Path function:_old_virtual_prompt) { + $function:prompt = $function:_old_virtual_prompt + Remove-Item function:\_old_virtual_prompt + } + + if ($env:VIRTUAL_ENV) { + Remove-Item env:VIRTUAL_ENV -ErrorAction SilentlyContinue + } + + if ($env:VIRTUAL_ENV_PROMPT) { + Remove-Item env:VIRTUAL_ENV_PROMPT -ErrorAction SilentlyContinue + } + + if (!$NonDestructive) { + # Self destruct! + Remove-Item function:deactivate + Remove-Item function:pydoc + } +} + +function global:pydoc { + python -m pydoc $args +} + +# unset irrelevant variables +deactivate -nondestructive + +$VIRTUAL_ENV = $BASE_DIR +$env:VIRTUAL_ENV = $VIRTUAL_ENV + +if ("QwenIllustrious" -ne "") { + $env:VIRTUAL_ENV_PROMPT = "QwenIllustrious" +} +else { + $env:VIRTUAL_ENV_PROMPT = $( Split-Path $env:VIRTUAL_ENV -Leaf ) +} + +New-Variable -Scope global -Name _OLD_VIRTUAL_PATH -Value $env:PATH + +$env:PATH = "$env:VIRTUAL_ENV/bin:" + $env:PATH +if (!$env:VIRTUAL_ENV_DISABLE_PROMPT) { + function global:_old_virtual_prompt { + "" + } + $function:_old_virtual_prompt = $function:prompt + + function global:prompt { + # Add the custom prefix to the existing prompt + $previous_prompt_value = & $function:_old_virtual_prompt + ("(" + $env:VIRTUAL_ENV_PROMPT + ") " + $previous_prompt_value) + } +} diff --git a/.venv/bin/activate_this.py b/.venv/bin/activate_this.py new file mode 100644 index 0000000000000000000000000000000000000000..828a48aab545886663d68085f331191a92a851bb --- /dev/null +++ b/.venv/bin/activate_this.py @@ -0,0 +1,59 @@ +# Copyright (c) 2020-202x The virtualenv developers +# +# 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. + +""" +Activate virtualenv for current interpreter: + +import runpy +runpy.run_path(this_file) + +This can be used when you must use an existing Python interpreter, not the virtualenv bin/python. +""" # noqa: D415 + +from __future__ import annotations + +import os +import site +import sys + +try: + abs_file = os.path.abspath(__file__) +except NameError as exc: + msg = "You must use import runpy; runpy.run_path(this_file)" + raise AssertionError(msg) from exc + +bin_dir = os.path.dirname(abs_file) +base = bin_dir[: -len("bin") - 1] # strip away the bin part from the __file__, plus the path separator + +# prepend bin to PATH (this file is inside the bin directory) +os.environ["PATH"] = os.pathsep.join([bin_dir, *os.environ.get("PATH", "").split(os.pathsep)]) +os.environ["VIRTUAL_ENV"] = base # virtual env is right above bin directory +os.environ["VIRTUAL_ENV_PROMPT"] = "QwenIllustrious" or os.path.basename(base) # noqa: SIM222 + +# add the virtual environments libraries to the host python import mechanism +prev_length = len(sys.path) +for lib in "../lib/python3.12/site-packages".split(os.pathsep): + path = os.path.realpath(os.path.join(bin_dir, lib)) + site.addsitedir(path) +sys.path[:] = sys.path[prev_length:] + sys.path[0:prev_length] + +sys.real_prefix = sys.prefix +sys.prefix = base diff --git a/.venv/bin/deactivate.bat b/.venv/bin/deactivate.bat new file mode 100644 index 0000000000000000000000000000000000000000..07041bc45129994cbb4b338f6fb61aaf502571fb --- /dev/null +++ b/.venv/bin/deactivate.bat @@ -0,0 +1,39 @@ +@REM Copyright (c) 2020-202x The virtualenv developers +@REM +@REM Permission is hereby granted, free of charge, to any person obtaining +@REM a copy of this software and associated documentation files (the +@REM "Software"), to deal in the Software without restriction, including +@REM without limitation the rights to use, copy, modify, merge, publish, +@REM distribute, sublicense, and/or sell copies of the Software, and to +@REM permit persons to whom the Software is furnished to do so, subject to +@REM the following conditions: +@REM +@REM The above copyright notice and this permission notice shall be +@REM included in all copies or substantial portions of the Software. +@REM +@REM THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +@REM EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +@REM MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +@REM NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +@REM LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +@REM OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +@REM WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +@set VIRTUAL_ENV= +@set VIRTUAL_ENV_PROMPT= + +@REM Don't use () to avoid problems with them in %PATH% +@if not defined _OLD_VIRTUAL_PROMPT @goto ENDIFVPROMPT + @set "PROMPT=%_OLD_VIRTUAL_PROMPT%" + @set _OLD_VIRTUAL_PROMPT= +:ENDIFVPROMPT + +@if not defined _OLD_VIRTUAL_PYTHONHOME @goto ENDIFVHOME + @set "PYTHONHOME=%_OLD_VIRTUAL_PYTHONHOME%" + @set _OLD_VIRTUAL_PYTHONHOME= +:ENDIFVHOME + +@if not defined _OLD_VIRTUAL_PATH @goto ENDIFVPATH + @set "PATH=%_OLD_VIRTUAL_PATH%" + @set _OLD_VIRTUAL_PATH= +:ENDIFVPATH \ No newline at end of file diff --git a/.venv/bin/diffusers-cli b/.venv/bin/diffusers-cli new file mode 100644 index 0000000000000000000000000000000000000000..0db8928058bcaa1c644df8a3296889eb96fe7a41 --- /dev/null +++ b/.venv/bin/diffusers-cli @@ -0,0 +1,10 @@ +#!/home/ubuntu/lyl/QwenIllustrious/.venv/bin/python3 +# -*- coding: utf-8 -*- +import sys +from diffusers.commands.diffusers_cli import main +if __name__ == "__main__": + if sys.argv[0].endswith("-script.pyw"): + sys.argv[0] = sys.argv[0][:-11] + elif sys.argv[0].endswith(".exe"): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/f2py b/.venv/bin/f2py new file mode 100644 index 0000000000000000000000000000000000000000..66615b7166d6994a6a1589aa43c30432df7ce235 --- /dev/null +++ b/.venv/bin/f2py @@ -0,0 +1,10 @@ +#!/home/ubuntu/lyl/QwenIllustrious/.venv/bin/python3 +# -*- coding: utf-8 -*- +import sys +from numpy.f2py.f2py2e import main +if __name__ == "__main__": + if sys.argv[0].endswith("-script.pyw"): + sys.argv[0] = sys.argv[0][:-11] + elif sys.argv[0].endswith(".exe"): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/hf b/.venv/bin/hf new file mode 100644 index 0000000000000000000000000000000000000000..0decb6abf24bb684183ecf4faec731cc8c251f87 --- /dev/null +++ b/.venv/bin/hf @@ -0,0 +1,10 @@ +#!/home/ubuntu/lyl/QwenIllustrious/.venv/bin/python3 +# -*- coding: utf-8 -*- +import sys +from huggingface_hub.cli.hf import main +if __name__ == "__main__": + if sys.argv[0].endswith("-script.pyw"): + sys.argv[0] = sys.argv[0][:-11] + elif sys.argv[0].endswith(".exe"): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/huggingface-cli b/.venv/bin/huggingface-cli new file mode 100644 index 0000000000000000000000000000000000000000..7a27d2aae8f7a3567a4425fb0394e1a3c715cebd --- /dev/null +++ b/.venv/bin/huggingface-cli @@ -0,0 +1,10 @@ +#!/home/ubuntu/lyl/QwenIllustrious/.venv/bin/python3 +# -*- coding: utf-8 -*- +import sys +from huggingface_hub.commands.huggingface_cli import main +if __name__ == "__main__": + if sys.argv[0].endswith("-script.pyw"): + sys.argv[0] = sys.argv[0][:-11] + elif sys.argv[0].endswith(".exe"): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/isympy b/.venv/bin/isympy new file mode 100644 index 0000000000000000000000000000000000000000..eb484a5115a78906e2915aaae209b44ad0693086 --- /dev/null +++ b/.venv/bin/isympy @@ -0,0 +1,10 @@ +#!/home/ubuntu/lyl/QwenIllustrious/.venv/bin/python3 +# -*- coding: utf-8 -*- +import sys +from isympy import main +if __name__ == "__main__": + if sys.argv[0].endswith("-script.pyw"): + sys.argv[0] = sys.argv[0][:-11] + elif sys.argv[0].endswith(".exe"): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/normalizer b/.venv/bin/normalizer new file mode 100644 index 0000000000000000000000000000000000000000..16696cfc8ee729d997c077dfa129125ca2a32d4d --- /dev/null +++ b/.venv/bin/normalizer @@ -0,0 +1,10 @@ +#!/home/ubuntu/lyl/QwenIllustrious/.venv/bin/python3 +# -*- coding: utf-8 -*- +import sys +from charset_normalizer.cli import cli_detect +if __name__ == "__main__": + if sys.argv[0].endswith("-script.pyw"): + sys.argv[0] = sys.argv[0][:-11] + elif sys.argv[0].endswith(".exe"): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(cli_detect()) diff --git a/.venv/bin/numpy-config b/.venv/bin/numpy-config new file mode 100644 index 0000000000000000000000000000000000000000..561fddae35956db367a87bb5f36f52f682f28277 --- /dev/null +++ b/.venv/bin/numpy-config @@ -0,0 +1,10 @@ +#!/home/ubuntu/lyl/QwenIllustrious/.venv/bin/python3 +# -*- coding: utf-8 -*- +import sys +from numpy._configtool import main +if __name__ == "__main__": + if sys.argv[0].endswith("-script.pyw"): + sys.argv[0] = sys.argv[0][:-11] + elif sys.argv[0].endswith(".exe"): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/proton b/.venv/bin/proton new file mode 100644 index 0000000000000000000000000000000000000000..8e3bfb646e74efe8d6d5f8ff392f4a86d5688c25 --- /dev/null +++ b/.venv/bin/proton @@ -0,0 +1,10 @@ +#!/home/ubuntu/lyl/QwenIllustrious/.venv/bin/python3 +# -*- coding: utf-8 -*- +import sys +from triton.profiler.proton import main +if __name__ == "__main__": + if sys.argv[0].endswith("-script.pyw"): + sys.argv[0] = sys.argv[0][:-11] + elif sys.argv[0].endswith(".exe"): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/proton-viewer b/.venv/bin/proton-viewer new file mode 100644 index 0000000000000000000000000000000000000000..3356458f880e54d29055110f1fe69eafc522bffa --- /dev/null +++ b/.venv/bin/proton-viewer @@ -0,0 +1,10 @@ +#!/home/ubuntu/lyl/QwenIllustrious/.venv/bin/python3 +# -*- coding: utf-8 -*- +import sys +from triton.profiler.viewer import main +if __name__ == "__main__": + if sys.argv[0].endswith("-script.pyw"): + sys.argv[0] = sys.argv[0][:-11] + elif sys.argv[0].endswith(".exe"): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/pydoc.bat b/.venv/bin/pydoc.bat new file mode 100644 index 0000000000000000000000000000000000000000..daa20590b181e9f5a1f4f642f6c4eaf471cff00f --- /dev/null +++ b/.venv/bin/pydoc.bat @@ -0,0 +1,22 @@ +@REM Copyright (c) 2020-202x The virtualenv developers +@REM +@REM Permission is hereby granted, free of charge, to any person obtaining +@REM a copy of this software and associated documentation files (the +@REM "Software"), to deal in the Software without restriction, including +@REM without limitation the rights to use, copy, modify, merge, publish, +@REM distribute, sublicense, and/or sell copies of the Software, and to +@REM permit persons to whom the Software is furnished to do so, subject to +@REM the following conditions: +@REM +@REM The above copyright notice and this permission notice shall be +@REM included in all copies or substantial portions of the Software. +@REM +@REM THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +@REM EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +@REM MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +@REM NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +@REM LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +@REM OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +@REM WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +python.exe -m pydoc %* \ No newline at end of file diff --git a/.venv/bin/tiny-agents b/.venv/bin/tiny-agents new file mode 100644 index 0000000000000000000000000000000000000000..b7be768f4539ab36ea4127390488ff00b38eec79 --- /dev/null +++ b/.venv/bin/tiny-agents @@ -0,0 +1,10 @@ +#!/home/ubuntu/lyl/QwenIllustrious/.venv/bin/python3 +# -*- coding: utf-8 -*- +import sys +from huggingface_hub.inference._mcp.cli import app +if __name__ == "__main__": + if sys.argv[0].endswith("-script.pyw"): + sys.argv[0] = sys.argv[0][:-11] + elif sys.argv[0].endswith(".exe"): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(app()) diff --git a/.venv/bin/torchfrtrace b/.venv/bin/torchfrtrace new file mode 100644 index 0000000000000000000000000000000000000000..9996e20c147a4917358944fa2bcc2ed9999935c0 --- /dev/null +++ b/.venv/bin/torchfrtrace @@ -0,0 +1,10 @@ +#!/home/ubuntu/lyl/QwenIllustrious/.venv/bin/python3 +# -*- coding: utf-8 -*- +import sys +from tools.flight_recorder.fr_trace import main +if __name__ == "__main__": + if sys.argv[0].endswith("-script.pyw"): + sys.argv[0] = sys.argv[0][:-11] + elif sys.argv[0].endswith(".exe"): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/torchrun b/.venv/bin/torchrun new file mode 100644 index 0000000000000000000000000000000000000000..dbf5ab80a5f65fa672522a60acd08057771238ee --- /dev/null +++ b/.venv/bin/torchrun @@ -0,0 +1,10 @@ +#!/home/ubuntu/lyl/QwenIllustrious/.venv/bin/python3 +# -*- coding: utf-8 -*- +import sys +from torch.distributed.run import main +if __name__ == "__main__": + if sys.argv[0].endswith("-script.pyw"): + sys.argv[0] = sys.argv[0][:-11] + elif sys.argv[0].endswith(".exe"): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/tqdm b/.venv/bin/tqdm new file mode 100644 index 0000000000000000000000000000000000000000..d213dcbef0360726c2e5f5f15c2ec82675872528 --- /dev/null +++ b/.venv/bin/tqdm @@ -0,0 +1,10 @@ +#!/home/ubuntu/lyl/QwenIllustrious/.venv/bin/python3 +# -*- coding: utf-8 -*- +import sys +from tqdm.cli import main +if __name__ == "__main__": + if sys.argv[0].endswith("-script.pyw"): + sys.argv[0] = sys.argv[0][:-11] + elif sys.argv[0].endswith(".exe"): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/transformers b/.venv/bin/transformers new file mode 100644 index 0000000000000000000000000000000000000000..ed495de47393a9540a69e248083a56431b1ea610 --- /dev/null +++ b/.venv/bin/transformers @@ -0,0 +1,10 @@ +#!/home/ubuntu/lyl/QwenIllustrious/.venv/bin/python3 +# -*- coding: utf-8 -*- +import sys +from transformers.commands.transformers_cli import main +if __name__ == "__main__": + if sys.argv[0].endswith("-script.pyw"): + sys.argv[0] = sys.argv[0][:-11] + elif sys.argv[0].endswith(".exe"): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main()) diff --git a/.venv/bin/transformers-cli b/.venv/bin/transformers-cli new file mode 100644 index 0000000000000000000000000000000000000000..6565770055781b34a920f1981823d519e7c386ae --- /dev/null +++ b/.venv/bin/transformers-cli @@ -0,0 +1,10 @@ +#!/home/ubuntu/lyl/QwenIllustrious/.venv/bin/python3 +# -*- coding: utf-8 -*- +import sys +from transformers.commands.transformers_cli import main_cli +if __name__ == "__main__": + if sys.argv[0].endswith("-script.pyw"): + sys.argv[0] = sys.argv[0][:-11] + elif sys.argv[0].endswith(".exe"): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(main_cli()) diff --git a/.venv/bin/wandb b/.venv/bin/wandb new file mode 100644 index 0000000000000000000000000000000000000000..cf829d1bfa8ffbe2ec4c4f2c15b80679fed86707 --- /dev/null +++ b/.venv/bin/wandb @@ -0,0 +1,10 @@ +#!/home/ubuntu/lyl/QwenIllustrious/.venv/bin/python3 +# -*- coding: utf-8 -*- +import sys +from wandb.cli.cli import cli +if __name__ == "__main__": + if sys.argv[0].endswith("-script.pyw"): + sys.argv[0] = sys.argv[0][:-11] + elif sys.argv[0].endswith(".exe"): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(cli()) diff --git a/.venv/bin/wb b/.venv/bin/wb new file mode 100644 index 0000000000000000000000000000000000000000..cf829d1bfa8ffbe2ec4c4f2c15b80679fed86707 --- /dev/null +++ b/.venv/bin/wb @@ -0,0 +1,10 @@ +#!/home/ubuntu/lyl/QwenIllustrious/.venv/bin/python3 +# -*- coding: utf-8 -*- +import sys +from wandb.cli.cli import cli +if __name__ == "__main__": + if sys.argv[0].endswith("-script.pyw"): + sys.argv[0] = sys.argv[0][:-11] + elif sys.argv[0].endswith(".exe"): + sys.argv[0] = sys.argv[0][:-4] + sys.exit(cli()) diff --git a/.venv/lib/python3.12/site-packages/PIL/AvifImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/AvifImagePlugin.py new file mode 100644 index 0000000000000000000000000000000000000000..366e0c864bf6ece4c401fe827430e07fd7fc4a09 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/AvifImagePlugin.py @@ -0,0 +1,291 @@ +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) + 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 new file mode 100644 index 0000000000000000000000000000000000000000..f175e2f4f80b1b232d79f15a6db0667296917c97 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/BdfFontFile.py @@ -0,0 +1,122 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..f7be7746d84bbc076e0a41124a903a8b2b05ae61 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/BlpImagePlugin.py @@ -0,0 +1,497 @@ +""" +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: + 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""" + 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) + file_info["colors"] = ( + file_info["colors"] + if file_info.get("colors", 0) + else (1 << file_info["bits"]) + ) + assert isinstance(file_info["colors"], int) + if offset == 14 + file_info["header_size"] and file_info["bits"] <= 8: + offset += 4 * 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: + assert isinstance(file_info["palette_padding"], int) + 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 + 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 = self.fd.read(2) + 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 new file mode 100644 index 0000000000000000000000000000000000000000..8c5da14f5f6769127e0ed23d36fa8f3dd0b086c2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/BufrStubImagePlugin.py @@ -0,0 +1,75 @@ +# +# 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: + 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 + + loader = self._load() + if loader: + loader.open(self) + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..ec9e66c714fbbfec8c597f6127e5d932b0da521f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ContainerIO.py @@ -0,0 +1,173 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..b817dbc87b8c291d4bffd5f0b83d5e63a5e6ecbb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/CurImagePlugin.py @@ -0,0 +1,75 @@ +# +# 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, ImageFile +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: + 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 + d, e, o, a = self.tile[0] + self.tile[0] = ImageFile._Tile(d, (0, 0) + self.size, o, a) + + +# +# -------------------------------------------------------------------- + +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 new file mode 100644 index 0000000000000000000000000000000000000000..aea661b9cb6eef184696e377678ee69f66c5f772 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/DcxImagePlugin.py @@ -0,0 +1,83 @@ +# +# 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 + 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 new file mode 100644 index 0000000000000000000000000000000000000000..f9ade18f9a1edf431524dd86a238f6b0445e6ab0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/DdsImagePlugin.py @@ -0,0 +1,624 @@ +""" +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 io +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: + 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) + ) + 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 new file mode 100644 index 0000000000000000000000000000000000000000..5e2ddad99e99565fee795d49c9563f5c9ba5dac1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/EpsImagePlugin.py @@ -0,0 +1,476 @@ +# +# 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: + (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 + 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: + 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 new file mode 100644 index 0000000000000000000000000000000000000000..2280d5ce84b9badabe16cfb0db37f739d50d51c6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ExifTags.py @@ -0,0 +1,382 @@ +# +# 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 + + +"""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 new file mode 100644 index 0000000000000000000000000000000000000000..a3fdc0efeec6f7ec195112ded41d8ff1e248a6a0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/FitsImagePlugin.py @@ -0,0 +1,152 @@ +# +# 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 + value = gzip.decompress(self.fd.read()) + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..7c5bfeefa1bf24ee536e13e26ce6ca8aa4ba319d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/FliImagePlugin.py @@ -0,0 +1,178 @@ +# +# 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) >= 6 + 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 + s = self.fp.read(128) + if not (_accept(s) and s[20:22] == b"\x00\x00"): + 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.__offset = self.__offset + i32(s) + self.fp.seek(self.__offset) + 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 + 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 new file mode 100644 index 0000000000000000000000000000000000000000..1e0c1c166b5932a7621e510eba047586465e03d8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/FontFile.py @@ -0,0 +1,134 @@ +# +# 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, _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 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") + for id in range(256): + m = self.metrics[id] + if not m: + puti16(fp, (0,) * 10) + else: + puti16(fp, m[0] + m[1] + m[2]) diff --git a/.venv/lib/python3.12/site-packages/PIL/FpxImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/FpxImagePlugin.py new file mode 100644 index 0000000000000000000000000000000000000000..fd992cd9e20eb7cf0c6de347ac0a76f928ffc238 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/FpxImagePlugin.py @@ -0,0 +1,257 @@ +# +# 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 + + 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) + tilesize = 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 + xtile, ytile = tilesize + 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 = x + xtile + if x >= xsize: + x, y = 0, y + ytile + if y >= ysize: + break # isn't really required + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..d60e75bb60bdb5113c7cb3c48840918207ced694 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/FtexImagePlugin.py @@ -0,0 +1,114 @@ +""" +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: + 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 new file mode 100644 index 0000000000000000000000000000000000000000..f319d7e846e4c7ecb32a43751204bd1fbee168c0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/GbrImagePlugin.py @@ -0,0 +1,103 @@ +# +# 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: + 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)) + + comment = self.fp.read(comment_length)[:-1] + + if color_depth == 1: + self._mode = "L" + else: + self._mode = "RGBA" + + self._size = width, height + + self.info["comment"] = comment + + # 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: + 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 new file mode 100644 index 0000000000000000000000000000000000000000..891225ce2fd034a11963bb64212cfa7311190441 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/GdImageFile.py @@ -0,0 +1,102 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..b03aa7f1505e8624b1a50551adbc4488ac3bd1fa --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/GifImagePlugin.py @@ -0,0 +1,1213 @@ +# +# 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 IO, Any, Literal, NamedTuple, Union, 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 . 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: + 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 + 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): + p = ImagePalette.raw("RGB", p) + self.global_palette = self.palette = p + + 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 + for f in range(self.__frame + 1, frame + 1): + try: + 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 = None + 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 = None + + 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 = Union[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.getdata()) + 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"): + # 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 new file mode 100644 index 0000000000000000000000000000000000000000..ec62f8e4ebc37d3aef9b171a0d03b7deeab702c4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/GimpGradientFile.py @@ -0,0 +1,149 @@ +# +# 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 typing import IO, Callable + +from ._binary import o8 + +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 new file mode 100644 index 0000000000000000000000000000000000000000..379ffd739182c4caaad3bce92e0e8344ced2eef4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/GimpPaletteFile.py @@ -0,0 +1,72 @@ +# +# 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 +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 new file mode 100644 index 0000000000000000000000000000000000000000..439fc5a3eda8414add95d53660eca8d11bf6ab8f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/GribStubImagePlugin.py @@ -0,0 +1,75 @@ +# +# 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 prefix.startswith(b"GRIB") and prefix[7] == 1 + + +class GribStubImageFile(ImageFile.StubImageFile): + format = "GRIB" + format_description = "GRIB" + + def _open(self) -> 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 + + loader = self._load() + if loader: + loader.open(self) + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..76e640f15abfe60a56a571380133a0463c104035 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/Hdf5StubImagePlugin.py @@ -0,0 +1,75 @@ +# +# 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: + 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 + + loader = self._load() + if loader: + loader.open(self) + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..5a88429e5e4b3be4e57ce85b70fdaa4c7927fe09 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/IcnsImagePlugin.py @@ -0,0 +1,411 @@ +# +# 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 +from ._deprecate import deprecate + +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) + for i in range(blocksize): + data.append(byte) + 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: + 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 # type: ignore[override] + def size(self) -> tuple[int, int] | tuple[int, int, int]: + return self._size + + @size.setter + def size(self, value: tuple[int, int] | tuple[int, int, int]) -> None: + if len(value) == 3: + deprecate("Setting size to (width, height, scale)", 12, "load(scale)") + if value in self.info["sizes"]: + self._size = value # type: ignore[assignment] + return + else: + # 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 or len(self.size) == 3: + if scale is None and len(self.size) == 3: + scale = self.size[2] + assert 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 new file mode 100644 index 0000000000000000000000000000000000000000..bd35ac890e6cf824e9c890404416d871e5b94f7c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/IcoImagePlugin.py @@ -0,0 +1,381 @@ +# +# 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 +# +# 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: + 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 new file mode 100644 index 0000000000000000000000000000000000000000..71b9996780ce8dfc420670b5732216f934a1f677 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImImagePlugin.py @@ -0,0 +1,389 @@ +# +# 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. + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..d209405c4c5e3c00179aa82c706730bca7bc7b28 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/Image.py @@ -0,0 +1,4245 @@ +# +# 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 Callable, Iterator, MutableMapping, Sequence +from enum import IntEnum +from types import ModuleType +from typing import IO, Any, Literal, 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 + +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: + core = DeferredError.new(ImportError("The _imaging C module is not installed.")) + # 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 + + +def isImageType(t: Any) -> TypeGuard[Image]: + """ + Checks if an object is an image object. + + .. warning:: + + This function is for internal use only. + + :param t: object to check if it's an image + :returns: True if the object is an image + """ + deprecate("Image.isImageType(im)", 12, "isinstance(im, Image.Image)") + return hasattr(t, "im") + + +# +# 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, TypeGuard +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 + + +def preinit() -> None: + """ + Explicitly loads BMP, GIF, JPEG, PPM and PPM 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 + + parent_name = __name__.rpartition(".")[0] + for plugin in _plugins: + try: + logger.debug("Importing %s", plugin) + __import__(f"{parent_name}.{plugin}", globals(), locals(), []) + except ImportError as e: + 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): + return self + + def __exit__(self, *args): + from . import ImageFile + + if isinstance(self, ImageFile.ImageFile): + if getattr(self, "_exclusive_fp", False): + self._close_fp() + self.fp = None + + 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) + + 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) + 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" + else: + 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. + """ + + if mode in ("BGR;15", "BGR;16", "BGR;24"): + deprecate(mode, 12) + + 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 mode == "P" and self.mode == "RGBA": + return self.quantize(colors) + + 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 == "P" 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 _expand(self, xmargin: int, ymargin: int | None = None) -> Image: + if ymargin is None: + ymargin = xmargin + self.load() + return self._new(self.im.expand(xmargin, ymargin)) + + 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. + """ + + self.load() + if band is not None: + return self.im.getband(band) + return self.im # could be abused + + 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"): + self._exif.bigtiff = self.tag_v2._bigtiff + self._exif.endian = self.tag_v2._endian + 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. + + 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. + :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", or a mode + that can be transformed to "RGB" or "RGBA" (e.g. "R", "BGR;15", "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 + self.palette.mode = "RGBA" if "A" in rawmode else "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. + + 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. + """ + + if self.readonly: + self._copy() + self.load() + + 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`. If the image mode is "BGR;15", + "BGR;16" or "BGR;24", then the default filter is + :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: + bgr = self.mode.startswith("BGR;") + resample = Resampling.NEAREST if bgr else 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, + ) + + return self._new(self.im.resize(size, resample, box)) + + 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) + + preinit() + + filename_ext = os.path.splitext(filename)[1].lower() + ext = filename_ext.decode() if isinstance(filename_ext, bytes) else filename_ext + + if not format: + 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. + """ + + _show(self, title=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. If the color is + None, the image is not initialised. + :returns: An :py:class:`~PIL.Image.Image` object. + """ + + if mode in ("BGR;15", "BGR;16", "BGR;24"): + deprecate(mode, 12) + + _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``. Will be determined from + type if ``None``. Deprecated. + + This will not be used to convert the data after reading, but will be used to + change how the data is read:: + + from PIL import Image + import numpy as np + a = np.full((1, 1), 300) + im = Image.fromarray(a, mode="L") + im.getpixel((0, 0)) # 44 + im = Image.fromarray(a, mode="RGB") + im.getpixel((0, 0)) # (44, 1, 0) + + 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) + if mode is None: + try: + typekey = (1, 1) + shape[2:], arr["typestr"] + except KeyError as e: + msg = "Cannot handle this data type" + raise TypeError(msg) from e + try: + mode, rawmode = _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 + else: + deprecate("'mode' parameter", 13) + rawmode = mode + 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 + # first two members of shape are set to one + ((1, 1), "|b1"): ("1", "1;8"), + ((1, 1), "|u1"): ("L", "L"), + ((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"), + ((1, 1, 3), "|u1"): ("RGB", "RGB"), + ((1, 1, 4), "|u1"): ("RGBA", "RGBA"), + # 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) + + 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: + checked_formats = ID.copy() + if init(): + im = _open_core( + fp, + filename, + prefix, + tuple(format for format in formats if format not in checked_formats), + ) + + 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. + :param im2: The second image. Must have mode RGBA, and the same 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 + + 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 + + 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 + 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] + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..29a5c995fd802c9be16784f80707cfecb88b2002 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImageChops.py @@ -0,0 +1,311 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..a1584f111d4a6c33a62dcb94ae583a539138f0c3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImageCms.py @@ -0,0 +1,1123 @@ +# 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, __version__ +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" + + +def __getattr__(name: str) -> Any: + if name == "DESCRIPTION": + deprecate("PIL.ImageCms.DESCRIPTION", 12) + return _DESCRIPTION + elif name == "VERSION": + deprecate("PIL.ImageCms.VERSION", 12) + return _VERSION + elif name == "FLAGS": + deprecate("PIL.ImageCms.FLAGS", 12, "PIL.ImageCms.Flags") + return _FLAGS + msg = f"module '{__name__}' has no attribute '{name}'" + raise AttributeError(msg) + + +# --------------------------------------------------------------------. + + +# +# 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 = None + self.product_name = None # profile.product_name + self.product_info = None # profile.product_info + + 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 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, + ): + supported_modes = ( + "RGB", + "RGBA", + "RGBX", + "CMYK", + "I;16", + "I;16L", + "I;16B", + "YCbCr", + "LAB", + "L", + "1", + ) + for mode in (input_mode, output_mode): + if mode not in supported_modes: + deprecate( + mode, + 12, + { + "L;16": "I;16 or I;16L", + "L:16B": "I;16B", + "YCCA": "YCbCr", + "YCC": "YCbCr", + }.get(mode), + ) + 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 + + +def versions() -> tuple[str, str | None, str, str]: + """ + (pyCMS) Fetches versions. + """ + + deprecate( + "PIL.ImageCms.versions()", + 12, + '(PIL.features.version("littlecms2"), sys.version, PIL.__version__)', + ) + return _VERSION, core.littlecms_version, sys.version.split()[0], __version__ diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageColor.py b/.venv/lib/python3.12/site-packages/PIL/ImageColor.py new file mode 100644 index 0000000000000000000000000000000000000000..9a15a8eb7597998f1bc9a01e8eae3588c087838b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImageColor.py @@ -0,0 +1,320 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..6cf1ee62659f07b21f257a37565a6f81022d9910 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImageDraw.py @@ -0,0 +1,1232 @@ +# +# 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 types import ModuleType +from typing import Any, AnyStr, Callable, Union, cast + +from . import Image, ImageColor +from ._deprecate import deprecate +from ._typing import Coords + +# experimental access to the outline API +Outline: Callable[[], Image.core._Outline] = Image.core.outline + +TYPE_CHECKING = False +if TYPE_CHECKING: + from . import ImageDraw2, ImageFont + +_Ink = Union[float, tuple[int, ...], str] + +""" +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.load() + if im.readonly: + im._copy() # make it writeable + 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 _multiline_check(self, text: AnyStr) -> bool: + split_character = "\n" if isinstance(text, str) else b"\n" + + return split_character in text + + def 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, + *args: Any, + **kwargs: Any, + ) -> None: + """Draw text.""" + if embedded_color and self.mode not in ("RGB", "RGBA"): + msg = "Embedded color supported only in RGB and RGBA modes" + raise ValueError(msg) + + if font is None: + font = self._getfont(kwargs.get("font_size")) + + if self._multiline_check(text): + return self.multiline_text( + xy, + text, + fill, + font, + anchor, + spacing, + align, + direction, + features, + language, + stroke_width, + stroke_fill, + embedded_color, + ) + + 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 + + def draw_text(ink: int, stroke_width: float = 0) -> None: + mode = self.fontmode + if stroke_width == 0 and embedded_color: + mode = "RGBA" + coord = [] + for i in range(2): + coord.append(int(xy[i])) + start = (math.modf(xy[0])[0], math.modf(xy[1])[0]) + try: + mask, offset = font.getmask2( # type: ignore[union-attr,misc] + text, + mode, + direction=direction, + features=features, + language=language, + stroke_width=stroke_width, + stroke_filled=True, + anchor=anchor, + ink=ink, + start=start, + *args, + **kwargs, + ) + coord = [coord[0] + offset[0], coord[1] + offset[1]] + except AttributeError: + try: + mask = font.getmask( # type: ignore[misc] + text, + mode, + direction, + features, + language, + stroke_width, + anchor, + ink, + start=start, + *args, + **kwargs, + ) + except TypeError: + mask = font.getmask(text) + if mode == "RGBA": + # 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) + x, y = coord + 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(coord, mask, ink) + + ink = getink(fill) + if ink is not None: + stroke_ink = None + if stroke_width: + stroke_ink = getink(stroke_fill) if stroke_fill is not None else ink + + if stroke_ink is not None: + # Draw stroked text + draw_text(stroke_ink, stroke_width) + + # Draw normal text + if ink != stroke_ink: + draw_text(ink) + else: + # Only draw normal text + draw_text(ink) + + def _prepare_multiline_text( + self, + xy: tuple[float, float], + text: AnyStr, + font: ( + ImageFont.ImageFont + | ImageFont.FreeTypeFont + | ImageFont.TransposedFont + | None + ), + anchor: str | None, + spacing: float, + align: str, + direction: str | None, + features: list[str] | None, + language: str | None, + stroke_width: float, + embedded_color: bool, + font_size: float | None, + ) -> tuple[ + ImageFont.ImageFont | ImageFont.FreeTypeFont | ImageFont.TransposedFont, + list[tuple[tuple[float, float], str, AnyStr]], + ]: + if anchor is None: + anchor = "lt" if direction == "ttb" else "la" + elif len(anchor) != 2: + msg = "anchor must be a 2 character string" + raise ValueError(msg) + elif anchor[1] in "tb" and direction != "ttb": + msg = "anchor not supported for multiline text" + raise ValueError(msg) + + if font is None: + font = self._getfont(font_size) + + lines = text.split("\n" if isinstance(text, str) else b"\n") + line_spacing = ( + self.textbbox((0, 0), "A", font, stroke_width=stroke_width)[3] + + stroke_width + + spacing + ) + + top = xy[1] + parts = [] + if direction == "ttb": + left = xy[0] + for line in lines: + parts.append(((left, top), anchor, line)) + left += line_spacing + else: + widths = [] + max_width: float = 0 + for line in lines: + line_width = self.textlength( + line, + font, + direction=direction, + features=features, + language=language, + embedded_color=embedded_color, + ) + 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 + + for idx, line in enumerate(lines): + left = xy[0] + 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(text, str) else 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.textlength( + word, + font, + direction=direction, + features=features, + language=language, + embedded_color=embedded_color, + ) + for word in words + ] + word_anchor = "l" + anchor[1] + width_difference = max_width - sum(word_widths) + for i, word in enumerate(words): + parts.append(((left, top), word_anchor, word)) + left += word_widths[i] + width_difference / (len(words) - 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(((left, top), anchor, line)) + top += line_spacing + + return font, parts + + 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: + font, lines = self._prepare_multiline_text( + xy, + text, + font, + anchor, + spacing, + align, + direction, + features, + language, + stroke_width, + embedded_color, + font_size, + ) + + for xy, anchor, line in lines: + self.text( + xy, + line, + fill, + font, + anchor, + direction=direction, + features=features, + language=language, + stroke_width=stroke_width, + stroke_fill=stroke_fill, + embedded_color=embedded_color, + ) + + 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 self._multiline_check(text): + msg = "can't measure length of multiline text" + raise ValueError(msg) + if embedded_color and self.mode not in ("RGB", "RGBA"): + msg = "Embedded color supported only in RGB and RGBA modes" + raise ValueError(msg) + + if font is None: + font = self._getfont(font_size) + mode = "RGBA" if embedded_color else self.fontmode + return font.getlength(text, mode, direction, features, language) + + 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 embedded_color and self.mode not in ("RGB", "RGBA"): + msg = "Embedded color supported only in RGB and RGBA modes" + raise ValueError(msg) + + if font is None: + font = self._getfont(font_size) + + if self._multiline_check(text): + return self.multiline_textbbox( + xy, + text, + font, + anchor, + spacing, + align, + direction, + features, + language, + stroke_width, + embedded_color, + ) + + mode = "RGBA" if embedded_color else self.fontmode + bbox = font.getbbox( + text, mode, direction, features, language, stroke_width, anchor + ) + return bbox[0] + xy[0], bbox[1] + xy[1], bbox[2] + xy[0], bbox[3] + xy[1] + + 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]: + font, lines = self._prepare_multiline_text( + xy, + text, + font, + anchor, + spacing, + align, + direction, + features, + language, + stroke_width, + embedded_color, + font_size, + ) + + bbox: tuple[float, float, float, float] | None = None + + for xy, anchor, line in lines: + bbox_line = self.textbbox( + xy, + line, + font, + anchor, + direction=direction, + features=features, + language=language, + stroke_width=stroke_width, + embedded_color=embedded_color, + ) + 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]), + ) + + if bbox is None: + return xy[0], xy[1], xy[0], xy[1] + return bbox + + +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, hints: list[str] | None = None +) -> tuple[ImageDraw2.Draw | None, ModuleType]: + """ + :param im: The image to draw in. + :param hints: An optional list of hints. Deprecated. + :returns: A (drawing context, drawing resource factory) tuple. + """ + if hints is not None: + deprecate("'hints' parameter", 12) + 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 new file mode 100644 index 0000000000000000000000000000000000000000..3d68658ed5b79a36597e4953b888c41aa82fc7da --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImageDraw2.py @@ -0,0 +1,243 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..0e7e6dd8ae631ad3577bda1d3e823bd2a3227536 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImageEnhance.py @@ -0,0 +1,113 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..bf556a2c69036902d4b842cd8f9fa33ee04fa633 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImageFile.py @@ -0,0 +1,922 @@ +# +# 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 ._deprecate import deprecate +from ._util import DeferredError, is_path + +TYPE_CHECKING = False +if TYPE_CHECKING: + from ._typing import StrOrBytesPath + +logger = logging.getLogger(__name__) + +MAXBLOCK = 65536 + +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 raise_oserror(error: int) -> OSError: + deprecate( + "raise_oserror", + 12, + action="It is only useful for translating error codes returned by a codec's " + "decode() method, which ImageFile already does automatically.", + ) + raise _get_oserror(error, encoder=False) + + +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 + + 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() + 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 + + def _close_fp(self): + 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 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,) + for subifd_offset in subifd_offsets: + ifds.append((exif._get_ifd_dict(subifd_offset), subifd_offset)) + 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: + 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 + + 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 + ): + 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 flag or len(im.tile) != 1: + # custom load code, or multiple tiles + self.decode = None + else: + # 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 + else: + (x0, y0, x1, y1) = (0, 0, 0, 0) + + if x0 == 0 and x1 == 0: + self.state.xsize, self.state.ysize = self.im.size + else: + self.state.xoff = x0 + self.state.yoff = y0 + self.state.xsize = x1 - x0 + self.state.ysize = y1 - y0 + + if self.state.xsize <= 0 or self.state.ysize <= 0: + msg = "Size cannot be negative" + raise ValueError(msg) + + if ( + self.state.xsize + self.state.xoff > self.im.size[0] + or self.state.ysize + self.state.yoff > self.im.size[1] + ): + msg = "Tile cannot extend outside image" + 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 new file mode 100644 index 0000000000000000000000000000000000000000..b9ed54ab20a132aa9d2aec894dd846736923f70e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImageFilter.py @@ -0,0 +1,604 @@ +# +# 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 types import ModuleType +from typing import Any, Callable, cast + +TYPE_CHECKING = False +if TYPE_CHECKING: + 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 new file mode 100644 index 0000000000000000000000000000000000000000..329c463ff864191849506bd61f7c0559af671f8f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImageFont.py @@ -0,0 +1,1339 @@ +# +# 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, features +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 and 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: + # read PILfont header + if file.readline() != b"PILfont\n": + msg = "Not a PILfont file" + raise SyntaxError(msg) + file.readline().split(b";") + 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) + + # check image + if image.mode not in ("1", "L"): + msg = "invalid font image mode" + raise TypeError(msg) + + 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 + + try: + from packaging.version import parse as parse_version + except ImportError: + pass + else: + if freetype_version := features.version_module("freetype2"): + if parse_version(freetype_version) < parse_version("2.9.1"): + warnings.warn( + "Support for FreeType 2.9.0 is deprecated and will be removed " + "in Pillow 12 (2025-10-15). Please upgrade to FreeType 2.9.1 " + "or newer, preferably FreeType 2.10.4 which fixes " + "CVE-2020-15999.", + DeprecationWarning, + ) + + 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. + """ + try: + names = self.font.getvarnames() + except AttributeError as e: + msg = "FreeType 2.9.1 or greater is required" + raise NotImplementedError(msg) from e + return [name.replace(b"\x00", b"") for name in 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. + """ + try: + axes = self.font.getvaraxes() + except AttributeError as e: + msg = "FreeType 2.9.1 or greater is required" + raise NotImplementedError(msg) from e + 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. + """ + try: + self.font.setvaraxes(axes) + except AttributeError as e: + msg = "FreeType 2.9.1 or greater is required" + raise NotImplementedError(msg) from e + + +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: + 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 new file mode 100644 index 0000000000000000000000000000000000000000..1eb4507344cd412bed5c113d4c52e39f26cecab2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImageGrab.py @@ -0,0 +1,196 @@ +# +# 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 bbox: + left, top, right, bottom = bbox + args += ["-R", f"{left},{top},{right-left},{bottom-top}"] + subprocess.call(args + ["-x", filepath]) + im = Image.open(filepath) + im.load() + os.unlink(filepath) + if bbox: + 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) + subprocess.call(args + [filepath]) + 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 new file mode 100644 index 0000000000000000000000000000000000000000..c33809ced890e437d10102a6c065d9efe3207685 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImageMath.py @@ -0,0 +1,368 @@ +# +# 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 types import CodeType +from typing import Any, Callable + +from . import Image, _imagingmath +from ._deprecate import deprecate + + +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], + options: dict[str, 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 options: Values to add to the function's dictionary. Deprecated. + You can instead use one or more keyword arguments. + :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. + """ + + if options: + deprecate( + "ImageMath.lambda_eval options", + 12, + "ImageMath.lambda_eval keyword arguments", + ) + + args: dict[str, Any] = ops.copy() + args.update(options) + 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, + options: dict[str, Any] = {}, + **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 options: Values to add to the evaluation context. Deprecated. + You can instead use one or more keyword arguments. + :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. + """ + + if options: + deprecate( + "ImageMath.unsafe_eval options", + 12, + "ImageMath.unsafe_eval keyword arguments", + ) + + # build execution namespace + args: dict[str, Any] = ops.copy() + for k in [*options, *kw]: + if "__" in k or hasattr(builtins, k): + msg = f"'{k}' not allowed" + raise ValueError(msg) + + args.update(options) + 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 + + +def eval( + expression: str, + _dict: dict[str, Any] = {}, + **kw: Any, +) -> Any: + """ + Evaluates an image expression. + + Deprecated. Use lambda_eval() or unsafe_eval() instead. + + :param expression: A string containing a Python-style expression. + :param _dict: Values to add to the evaluation context. You + can either use a dictionary, or one or more keyword + arguments. + :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. + + .. deprecated:: 10.3.0 + """ + + deprecate( + "ImageMath.eval", + 12, + "ImageMath.lambda_eval or ImageMath.unsafe_eval", + ) + return unsafe_eval(expression, _dict, **kw) diff --git a/.venv/lib/python3.12/site-packages/PIL/ImageMode.py b/.venv/lib/python3.12/site-packages/PIL/ImageMode.py new file mode 100644 index 0000000000000000000000000000000000000000..92a08d2cbcb4f8f2f7e24a265b83cd6c1012b41f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImageMode.py @@ -0,0 +1,92 @@ +# +# 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 + +from ._deprecate import deprecate + + +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"), + "BGR;15": ("RGB", "L", ("B", "G", "R"), "|u1"), + "BGR;16": ("RGB", "L", ("B", "G", "R"), "|u1"), + "BGR;24": ("RGB", "L", ("B", "G", "R"), "|u1"), + "LA": ("L", "L", ("L", "A"), "|u1"), + "La": ("L", "L", ("L", "a"), "|u1"), + "PA": ("RGB", "L", ("P", "A"), "|u1"), + } + if mode in modes: + if mode in ("BGR;15", "BGR;16", "BGR;24"): + deprecate(mode, 12) + 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: + if patterns is not None: + self.patterns = patterns + else: + self.patterns = [] + 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] + + def add_patterns(self, patterns: list[str]) -> None: + self.patterns += patterns + + def build_default_lut(self) -> None: + symbols = [0, 1] + m = 1 << 4 # pos of current pixel + self.lut = bytearray(symbols[(i & m) > 0] for i in range(LUT_SIZE)) + + def get_lut(self) -> bytearray | None: + return self.lut + + def _string_permute(self, pattern: str, permutation: list[int]) -> str: + """string_permute 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]]: + """pattern_permute 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. + + TBD :Build based on (file) morphlut:modify_lut + """ + 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 + # caught overrides + 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""" + self.lut = lut + if op_name is not None: + self.lut = LutBuilder(op_name=op_name).build_lut() + elif patterns is not None: + self.lut = LutBuilder(patterns=patterns).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""" + if self.lut is None: + msg = "No operator loaded" + raise Exception(msg) + + if image.mode != "L": + msg = "Image mode must be L" + raise ValueError(msg) + outimage = Image.new(image.mode, image.size, None) + 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`.""" + if self.lut is None: + msg = "No operator loaded" + raise Exception(msg) + + if image.mode != "L": + msg = "Image mode must be 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 binary image + + Returns a list of tuples of (x,y) coordinates + of all matching pixels. See :ref:`coordinate-system`.""" + + if image.mode != "L": + msg = "Image mode must be 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""" + 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""" + 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""" + 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 new file mode 100644 index 0000000000000000000000000000000000000000..da28854b5745459cc682beabb53480855092e7a4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImageOps.py @@ -0,0 +1,745 @@ +# +# 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: + palette = ImagePalette.ImagePalette(palette=image.getpalette()) + 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) + 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 new file mode 100644 index 0000000000000000000000000000000000000000..103697117b92a3dca9794fbea5b9c92306b9b198 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImagePalette.py @@ -0,0 +1,286 @@ +# +# 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) // 3 + 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 + if index * 3 < len(self.palette): + self._palette = ( + self._palette[: index * 3] + + bytes(color) + + self._palette[index * 3 + 3 :] + ) + 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) + if isinstance(fp, str): + fp = open(fp, "w") + fp.write("# Palette\n") + fp.write(f"# Mode: {self.mode}\n") + for i in range(256): + fp.write(f"{i}") + for j in range(i * len(self.mode), (i + 1) * len(self.mode)): + try: + fp.write(f" {self.palette[j]}") + except IndexError: + fp.write(" 0") + fp.write("\n") + 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 new file mode 100644 index 0000000000000000000000000000000000000000..77e8a609a552ae7d8c6b87e78a36ecbfc1cdce89 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImagePath.py @@ -0,0 +1,20 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..df7a57b652cd18b43c33774ca0006546ff4af274 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImageQt.py @@ -0,0 +1,220 @@ +# +# 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 typing import Any, Callable, Union + +from . import Image +from ._util import is_path + +TYPE_CHECKING = False +if TYPE_CHECKING: + import PyQt6 + import PySide6 + + from . import ImageFile + + QBuffer: type + QByteArray = Union[PyQt6.QtCore.QByteArray, PySide6.QtCore.QByteArray] + QIODevice = Union[PyQt6.QtCore.QIODevice, PySide6.QtCore.QIODevice] + QImage = Union[PyQt6.QtGui.QImage, PySide6.QtGui.QImage] + QPixmap = Union[PyQt6.QtGui.QPixmap, PySide6.QtGui.QPixmap] + +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, QIODevice + from PyQt6.QtGui import QImage, QPixmap, qRgba + elif qt_module == "PySide6": + from PySide6.QtCore import QBuffer, QIODevice + from PySide6.QtGui import QImage, QPixmap, qRgba + 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): # type: ignore[misc] + 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 new file mode 100644 index 0000000000000000000000000000000000000000..a6fc340d55f5516934349b3fa62c1276a204b03b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImageSequence.py @@ -0,0 +1,86 @@ +# +# 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 typing import Callable + +from . import Image + + +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 new file mode 100644 index 0000000000000000000000000000000000000000..7705608e3eccd5e82cfca87daa1264df2c81dacd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImageShow.py @@ -0,0 +1,362 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..8bc504526f0a00cde1229798234d4f0c5db95138 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImageStat.py @@ -0,0 +1,160 @@ +# +# 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] 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]) 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] + 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/ImageTk.py b/.venv/lib/python3.12/site-packages/PIL/ImageTk.py new file mode 100644 index 0000000000000000000000000000000000000000..3a4cb81e9ef5ef4abe617d4a364074c2203571ad --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImageTk.py @@ -0,0 +1,266 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..fb144ff38a1ee7ff77cc01f3b941756a60b2b4cd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImageTransform.py @@ -0,0 +1,136 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..98c28f29f1dbbb069b68dc9359051b6629148f0d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImageWin.py @@ -0,0 +1,247 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..c4eccee3423dc6c273bdc1ea88eda5ef4e17cf7d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/ImtImagePlugin.py @@ -0,0 +1,103 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..fc024d668f2f42c4b0ea4c90c702ccc6d7c528f0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/IptcImagePlugin.py @@ -0,0 +1,250 @@ +# +# 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 collections.abc import Sequence +from io import BytesIO +from typing import cast + +from . import Image, ImageFile +from ._binary import i16be as i16 +from ._binary import i32be as i32 +from ._deprecate import deprecate + +COMPRESSION = {1: "raw", 5: "jpeg"} + + +def __getattr__(name: str) -> bytes: + if name == "PAD": + deprecate("IptcImagePlugin.PAD", 12) + return b"\0\0\0\0" + msg = f"module '{__name__}' has no attribute '{name}'" + raise AttributeError(msg) + + +# +# Helpers + + +def _i(c: bytes) -> int: + return i32((b"\0\0\0\0" + c)[-4:]) + + +def _i8(c: int | bytes) -> int: + return c if isinstance(c, int) else c[0] + + +def i(c: bytes) -> int: + """.. deprecated:: 10.2.0""" + deprecate("IptcImagePlugin.i", 12) + return _i(c) + + +def dump(c: Sequence[int | bytes]) -> None: + """.. deprecated:: 10.2.0""" + deprecate("IptcImagePlugin.dump", 12) + for i in c: + print(f"{_i8(i):02x}", end=" ") + print() + + +## +# 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 + 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 + 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 (3, 65) in self.info: + id = self.info[(3, 65)][0] - 1 + else: + id = 0 + if layers == 1 and not component: + self._mode = "L" + elif layers == 3 and component: + self._mode = "RGB"[id] + elif layers == 4 and component: + self._mode = "CMYK"[id] + + # 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) + ] + + def load(self) -> Image.core.PixelAccess | None: + if len(self.tile) != 1 or self.tile[0][0] != "iptc": + return ImageFile.ImageFile.load(self) + + offset, compression = self.tile[0][2:] + + self.fp.seek(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: + _im.load() + self.im = _im.im + self.tile = [] + return Image.Image.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 + + info: dict[tuple[int, int], bytes | list[bytes]] = {} + if isinstance(im, IptcImageFile): + # return info dictionary right away + for k, v in im.info.items(): + if isinstance(k, tuple): + info[k] = v + return info + + 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 + + for k, v in iptc_im.info.items(): + if isinstance(k, tuple): + info[k] = v + return info diff --git a/.venv/lib/python3.12/site-packages/PIL/Jpeg2KImagePlugin.py b/.venv/lib/python3.12/site-packages/PIL/Jpeg2KImagePlugin.py new file mode 100644 index 0000000000000000000000000000000000000000..e0f4ecae595d3f1aef3f529d91efeefa560c5134 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/Jpeg2KImagePlugin.py @@ -0,0 +1,442 @@ +# +# 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 collections.abc import Callable +from typing import IO, cast + +from . import Image, ImageFile, ImagePalette, _binary + + +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 + + 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" and nc == 4: + meth, _, _, enumcs = header.read_fields(">BBBI") + if meth == 1 and enumcs == 12: + mode = "CMYK" + elif tbox == b"pclr" and mode in ("L", "LA"): + 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: + palette = ImagePalette.ImagePalette("RGBA" if npc == 4 else "RGB") + 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: + 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: + 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 new file mode 100644 index 0000000000000000000000000000000000000000..defe9f773f9215c9f5f31f918edfdaeda6474a16 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/JpegImagePlugin.py @@ -0,0 +1,902 @@ +# +# 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 typing import IO, Any + +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 ._deprecate import deprecate +from .JpegPresets import presets + +TYPE_CHECKING = False +if TYPE_CHECKING: + from .MpoImagePlugin import MpoImageFile + +# +# Parser + + +def Skip(self: JpegImageFile, marker: int) -> 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. + + 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", {}) + while s[offset : offset + 4] == b"8BIM": + try: + 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: + break # 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. + 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. + + n = i16(self.fp.read(2)) - 2 + s = ImageFile._safe_read(self.fp, n) + self._size = i16(s, 3), i16(s, 1) + + 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. + + 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: + 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 __getattr__(self, name: str) -> Any: + if name in ("huffman_ac", "huffman_dc"): + deprecate(name, 12) + return getattr(self, "_" + name) + raise AttributeError(name) + + 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 + """ + 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: + if im.width == 0 or im.height == 0: + msg = "cannot write empty image as JPEG" + raise ValueError(msg) + + 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) + for idx, table in enumerate(qtables): + try: + if len(table) != 64: + msg = "Invalid quantization table" + raise TypeError(msg) + table_array = array.array("H", table) + except TypeError as e: + msg = "Invalid quantization table" + raise ValueError(msg) from e + else: + qtables[idx] = list(table_array) + 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 + ) + + +def _save_cjpeg(im: Image.Image, fp: IO[bytes], filename: str | bytes) -> None: + # ALTERNATIVE: handle JPEGs via the IJG command line utilities. + tempfile = im._dump() + subprocess.check_call(["cjpeg", "-outfile", filename, tempfile]) + try: + os.unlink(tempfile) + except OSError: + pass + + +## +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..d0e64a35ee1b6fe3ac6da792682a3129253993bb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/JpegPresets.py @@ -0,0 +1,242 @@ +""" +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 new file mode 100644 index 0000000000000000000000000000000000000000..9a47933b69cbdc628faafb67b2fca8de703abfc1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/McIdasImagePlugin.py @@ -0,0 +1,78 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..9ce38c427b6c19be9e0c5092181a54b936a7a2f3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/MicImagePlugin.py @@ -0,0 +1,102 @@ +# +# 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 + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..47ebe9d62c4edd3b5e97f760ff7e9b0417e5b5ab --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/MpegImagePlugin.py @@ -0,0 +1,84 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..b1ae07873ac215b7abeeed9fe32d0f17db45d124 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/MpoImagePlugin.py @@ -0,0 +1,202 @@ +# +# 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 + ) + exif = im_frame.encoderinfo.get("exif") + if isinstance(exif, Image.Exif): + exif = exif.tobytes() + im_frame.encoderinfo["exif"] = exif + if 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: + 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 + 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 new file mode 100644 index 0000000000000000000000000000000000000000..277087a8677708a3a5fe21a3f6d2c3b27f880d03 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/MspImagePlugin.py @@ -0,0 +1,200 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..7fd4c5c94cfa7ec46332f4da78f3e402fd5b311b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/PSDraw.py @@ -0,0 +1,237 @@ +# +# 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. + """ + text_bytes = bytes(text, "UTF-8") + 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 new file mode 100644 index 0000000000000000000000000000000000000000..2a26e5d4e223ba0bc80ad1bfb37b4c3927e222ac --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/PaletteFile.py @@ -0,0 +1,54 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..15f71290816c5fa6a5178842260a1520eb0b372f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/PalmImagePlugin.py @@ -0,0 +1,217 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..3aa249988c8b035822ab994866e405990d1cc96e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/PcdImagePlugin.py @@ -0,0 +1,64 @@ +# +# 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(2048) + + 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 = -90 + + self._mode = "RGB" + self._size = 768, 512 # FIXME: not correct for rotated images! + self.tile = [ImageFile._Tile("pcd", (0, 0) + self.size, 96 * 2048)] + + def load_end(self) -> None: + if self.tile_post_rotate: + # Handle rotated PCDs + self.im = self.im.rotate(self.tile_post_rotate) + self._size = self.im.size + + +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..0d1968b140a93fb3d1a026d2fd9186e8696e2d1b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/PcfFontFile.py @@ -0,0 +1,254 @@ +# +# 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 typing import BinaryIO, Callable + +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 + +# -------------------------------------------------------------------- +# 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: + # 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 new file mode 100644 index 0000000000000000000000000000000000000000..458d586c463ce5355d807f2a2dd583545ccc8229 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/PcxImagePlugin.py @@ -0,0 +1,228 @@ +# +# 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 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: + 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 new file mode 100644 index 0000000000000000000000000000000000000000..e9c20ddc159e150676ead01cf314420008f05232 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/PdfImagePlugin.py @@ -0,0 +1,311 @@ +# +# 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, __version__, 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(f"created by Pillow {__version__} 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 new file mode 100644 index 0000000000000000000000000000000000000000..73d8c21c023c95304c6fddfc2e5fce6d331401b1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/PdfParser.py @@ -0,0 +1,1074 @@ +from __future__ import annotations + +import calendar +import codecs +import collections +import mmap +import os +import re +import time +import zlib +from typing import IO, Any, NamedTuple, Union + + +# 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" ]" + + +TYPE_CHECKING = False +if TYPE_CHECKING: + _DictBase = collections.UserDict[Union[str, bytes], Any] +else: + _DictBase = collections.UserDict + + +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) -> 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: + self.read_prev_trailer(trailer_dict[b"Prev"]) + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..d2b6d0a97e4bd230134d4741fc997baca5b4507f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/PixarImagePlugin.py @@ -0,0 +1,72 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..1b9a89aef0dd15a292b5e2e55b6e56369dc31b62 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/PngImagePlugin.py @@ -0,0 +1,1551 @@ +# +# 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 collections.abc import Callable +from enum import IntEnum +from typing import IO, Any, NamedTuple, NoReturn, 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 . 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 in ("1", "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: + 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() + + if self._exclusive_fp: + self.fp.close() + self.fp = None + + 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 + for f in range(self.__frame + 1, frame + 1): + try: + 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 + 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 + 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 + + +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: + if im.mode != mode: + im = im.convert(mode) + ImageFile._save( + 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 = int(round(encoderinfo.get("duration", 0))) + 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(frame_duration), # delay_numerator + o16(1000), # delay_denominator + o8(frame_disposal), # dispose_op + o8(frame_blend), # blend_op + ) + seq_num += 1 + # frame data + 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 + 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(im.palette.getdata()[1]) // 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}" + + # encoder options + im.encoderconfig = ( + im.encoderinfo.get("optimize", False), + im.encoderinfo.get("compress_level", -1), + im.encoderinfo.get("compress_type", -1), + im.encoderinfo.get("dictionary", b""), + ) + + # 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"] + + icc = im.encoderinfo.get("icc_profile", im.info.get("icc_profile")) + if icc: + # 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") + + info = im.encoderinfo.get("pnginfo") + if info: + 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 = im.im.getpalette("RGB")[: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]) + + dpi = im.encoderinfo.get("dpi") + if 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) + + exif = im.encoderinfo.get("exif") + if 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: + 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 new file mode 100644 index 0000000000000000000000000000000000000000..db34d107a4f4a9a7a27fe332e211133d88870fce --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/PpmImagePlugin.py @@ -0,0 +1,375 @@ +# +# 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 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 new file mode 100644 index 0000000000000000000000000000000000000000..f49aaeeb1f55cd2b6b17d9aa10f68876384fd410 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/PsdImagePlugin.py @@ -0,0 +1,333 @@ +# +# 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: + 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) + _, 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 new file mode 100644 index 0000000000000000000000000000000000000000..dba5d809fef75e281ac10f92f1868c58b1b4508c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/QoiImagePlugin.py @@ -0,0 +1,234 @@ +# +# 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: + 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 new file mode 100644 index 0000000000000000000000000000000000000000..853022150ae849e490378e41e831897050c207a2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/SgiImagePlugin.py @@ -0,0 +1,231 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..868019e80a80cffc5e9f193ddbf96a0ba64ad9ea --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/SpiderImagePlugin.py @@ -0,0 +1,331 @@ +# +# 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 + 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 = 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 + 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 + im = im.transpose(Image.Transpose.FLIP_LEFT_RIGHT) + print( + f"saving a flipped version of {os.path.basename(filename)} " + f"as {outfile} " + ) + 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 new file mode 100644 index 0000000000000000000000000000000000000000..8912379ea3e7801cdac9a557d2bc0c557bce8991 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/SunImagePlugin.py @@ -0,0 +1,145 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..86490a496f3f106fcc042c03fb235ed5fb41f3a7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/TarIO.py @@ -0,0 +1,61 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..90d5b5cf4ee17fc050784bf591adae3247407b56 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/TgaImagePlugin.py @@ -0,0 +1,264 @@ +# +# 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 warnings +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 + +# +# -------------------------------------------------------------------- +# 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._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 new file mode 100644 index 0000000000000000000000000000000000000000..daf20f2e899608c28905272de7b5c0620ef5938c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/TiffImagePlugin.py @@ -0,0 +1,2339 @@ +# +# 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 Iterator, MutableMapping +from fractions import Fraction +from numbers import Number, Rational +from typing import IO, Any, Callable, NoReturn, 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 ._deprecate import deprecate +from ._typing import StrOrBytesPath +from ._util import DeferredError, is_path +from .TiffTags import TYPES + +TYPE_CHECKING = False +if TYPE_CHECKING: + from ._typing import Buffer, IntegralLike + +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"), + (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 +] + +if not getattr(Image.core, "libtiff_support_custom_tags", True): + deprecate("Support for LibTIFF earlier than version 4", 12) + + +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 + 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"): + id = i16(val[4:6]) + n = math.ceil((val[6] + 1) / 2) * 2 + size = i32(val[6 + n : 10 + n]) + 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. + 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) == 1: + # 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 = (1,) + + bps_tuple = self.tag_v2.get(BITSPERSAMPLE, (1,)) + extra_tuple = self.tag_v2.get(EXTRASAMPLES, ()) + if photo in (2, 6, 8): # RGB, YCbCr, LAB + bps_count = 3 + elif photo == 5: # CMYK + bps_count = 4 + else: + bps_count = 1 + bps_count += len(extra_tuple) + bps_actual_count = len(bps_tuple) + samples_per_pixel = self.tag_v2.get( + SAMPLESPERPIXEL, + 3 if self._compression == "tiff_jpeg" and photo in (2, 6) else 1, + ) + + 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", {})} + 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 not getattr(Image.core, "libtiff_support_custom_tags", False): + continue + + if tag in TiffTags.TAGS_V2_GROUPS: + types[tag] = TiffTags.LONG8 + elif tag in ifd.tagtype: + types[tag] = ifd.tagtype[tag] + elif not (isinstance(value, (int, float, str, bytes))): + continue + else: + 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 new file mode 100644 index 0000000000000000000000000000000000000000..86adaa45857338f9fa8864296077393a3dc3350f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/TiffTags.py @@ -0,0 +1,562 @@ +# +# 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), + 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 +LIBTIFF_CORE.remove(333) # Ink Names either + +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..87e32878b1970876913e88fd5e8480d6813392c4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/WalImageFile.py @@ -0,0 +1,127 @@ +# +# 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 + 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] + next_name = header[56 : 56 + 32].split(b"\0", 1)[0] + if next_name: + self.info["next_name"] = next_name + + def load(self) -> Image.core.PixelAccess | None: + if self._im is 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 new file mode 100644 index 0000000000000000000000000000000000000000..1716a18ccda48ea47c6176c22914ab1f8b035e7c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/WebPImagePlugin.py @@ -0,0 +1,320 @@ +from __future__ import annotations + +from io import BytesIO +from typing import IO, Any + +from . import Image, ImageFile + +try: + from . import _webp + + SUPPORTED = True +except ImportError: + SUPPORTED = False + + +_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. + self._decoder = _webp.WebPAnimDecoder(self.fp.read()) + + # Get info from decoder + self._size, loop_count, bgcolor, frame_count, mode = self._decoder.get_info() + self.info["loop"] = loop_count + bg_a, bg_r, bg_g, bg_b = ( + (bgcolor >> 24) & 0xFF, + (bgcolor >> 16) & 0xFF, + (bgcolor >> 8) & 0xFF, + bgcolor & 0xFF, + ) + self.info["background"] = (bg_r, bg_g, bg_b, bg_a) + self.n_frames = frame_count + self.is_animated = self.n_frames > 1 + self._mode = "RGB" if mode == "RGBX" else mode + self.rawmode = mode + + # Attempt to read ICC / EXIF / XMP chunks from file + icc_profile = self._decoder.get_chunk("ICCP") + exif = self._decoder.get_chunk("EXIF") + xmp = self._decoder.get_chunk("XMP ") + if icc_profile: + self.info["icc_profile"] = icc_profile + if exif: + self.info["exif"] = exif + if xmp: + self.info["xmp"] = xmp + + # 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, timestamp, duration = self._get_next() + self.info["timestamp"] = timestamp + self.info["duration"] = duration + 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 new file mode 100644 index 0000000000000000000000000000000000000000..d569cb4b819db35966c67fbe75518c719973ef59 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/WmfImagePlugin.py @@ -0,0 +1,186 @@ +# +# 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: + im._mode = "RGB" + self.bbox = im.info["wmf_bbox"] + + def load(self, im: ImageFile.StubImageFile) -> Image.Image: + 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 placable header + 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 + + loader = self._load() + if loader: + loader.open(self) + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..cde28388ff0535262770dd0336ee2b48db2178ba --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/XVThumbImagePlugin.py @@ -0,0 +1,83 @@ +# +# 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) + s = s.strip().split() + + self._mode = "P" + self._size = int(s[0]), int(s[1]) + + 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 new file mode 100644 index 0000000000000000000000000000000000000000..1e57aa162ea4f8618dac66cf042352f73d2199c8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/XbmImagePlugin.py @@ -0,0 +1,98 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..3be240fbc1aeb7660de46fbd4f99f309ce9915dd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/XpmImagePlugin.py @@ -0,0 +1,157 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..6e4c23f897f83ef72fc10070bd22e9dc70614cf9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/__init__.py @@ -0,0 +1,87 @@ +"""Pillow (Fork of the Python Imaging Library) + +Pillow is the friendly PIL fork by Jeffrey A. 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 new file mode 100644 index 0000000000000000000000000000000000000000..043156e892dadc4fb1222b33f5eda33251cd15aa --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/__main__.py @@ -0,0 +1,7 @@ +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-x86_64-linux-gnu.so b/.venv/lib/python3.12/site-packages/PIL/_avif.cpython-312-x86_64-linux-gnu.so new file mode 100644 index 0000000000000000000000000000000000000000..733dcbe8b1c422467c42d0d86b50b1ddc38f21cb Binary files /dev/null and b/.venv/lib/python3.12/site-packages/PIL/_avif.cpython-312-x86_64-linux-gnu.so differ diff --git a/.venv/lib/python3.12/site-packages/PIL/_avif.pyi b/.venv/lib/python3.12/site-packages/PIL/_avif.pyi new file mode 100644 index 0000000000000000000000000000000000000000..e27843e5338213713e26973127c738c14313ff98 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/_avif.pyi @@ -0,0 +1,3 @@ +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 new file mode 100644 index 0000000000000000000000000000000000000000..4594ccce361168cf77e630cb88ffb09bb4362831 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/_binary.py @@ -0,0 +1,112 @@ +# +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..170d444904996cac753bf33deedcd13e442c9027 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/_deprecate.py @@ -0,0 +1,72 @@ +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 == 12: + removed = "Pillow 12 (2025-10-15)" + elif when == 13: + removed = "Pillow 13 (2026-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.pyi b/.venv/lib/python3.12/site-packages/PIL/_imaging.pyi new file mode 100644 index 0000000000000000000000000000000000000000..998bc52eb8a73b5ee5868cd2c8e5c87c4e6d3037 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/_imaging.pyi @@ -0,0 +1,31 @@ +from typing import Any + +class ImagingCore: + def __getitem__(self, index: int) -> float: ... + 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.pyi b/.venv/lib/python3.12/site-packages/PIL/_imagingcms.pyi new file mode 100644 index 0000000000000000000000000000000000000000..ddcf93ab1ebd51947f900ad2dba1aee9392338bb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/_imagingcms.pyi @@ -0,0 +1,143 @@ +import datetime +import sys +from typing import Literal, SupportsFloat, TypedDict + +from ._typing import CapsuleType + +littlecms_version: str | None + +_Tuple3f = tuple[float, float, float] +_Tuple2x3f = tuple[_Tuple3f, _Tuple3f] +_Tuple3x3f = 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.pyi b/.venv/lib/python3.12/site-packages/PIL/_imagingft.pyi new file mode 100644 index 0000000000000000000000000000000000000000..1cb1429d6cf6f29432fbd7f56d3aa0e45f0722f3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/_imagingft.pyi @@ -0,0 +1,69 @@ +from typing import Any, Callable + +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.pyi b/.venv/lib/python3.12/site-packages/PIL/_imagingmath.pyi new file mode 100644 index 0000000000000000000000000000000000000000..e27843e5338213713e26973127c738c14313ff98 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/_imagingmath.pyi @@ -0,0 +1,3 @@ +from typing import Any + +def __getattr__(name: str) -> Any: ... diff --git a/.venv/lib/python3.12/site-packages/PIL/_imagingmorph.cpython-312-x86_64-linux-gnu.so b/.venv/lib/python3.12/site-packages/PIL/_imagingmorph.cpython-312-x86_64-linux-gnu.so new file mode 100644 index 0000000000000000000000000000000000000000..718646267fe68d307aec6e65e801942e10679521 Binary files /dev/null and b/.venv/lib/python3.12/site-packages/PIL/_imagingmorph.cpython-312-x86_64-linux-gnu.so differ diff --git a/.venv/lib/python3.12/site-packages/PIL/_imagingmorph.pyi b/.venv/lib/python3.12/site-packages/PIL/_imagingmorph.pyi new file mode 100644 index 0000000000000000000000000000000000000000..e27843e5338213713e26973127c738c14313ff98 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/_imagingmorph.pyi @@ -0,0 +1,3 @@ +from typing import Any + +def __getattr__(name: str) -> Any: ... diff --git a/.venv/lib/python3.12/site-packages/PIL/_imagingtk.cpython-312-x86_64-linux-gnu.so b/.venv/lib/python3.12/site-packages/PIL/_imagingtk.cpython-312-x86_64-linux-gnu.so new file mode 100644 index 0000000000000000000000000000000000000000..19429c80ce14c8287365594ad58080f9ab9fd4ff Binary files /dev/null and b/.venv/lib/python3.12/site-packages/PIL/_imagingtk.cpython-312-x86_64-linux-gnu.so differ diff --git a/.venv/lib/python3.12/site-packages/PIL/_imagingtk.pyi b/.venv/lib/python3.12/site-packages/PIL/_imagingtk.pyi new file mode 100644 index 0000000000000000000000000000000000000000..e27843e5338213713e26973127c738c14313ff98 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/_imagingtk.pyi @@ -0,0 +1,3 @@ +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 new file mode 100644 index 0000000000000000000000000000000000000000..9c0143003a7320dd475cfcd168168b82e4f64964 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/_tkinter_finder.py @@ -0,0 +1,20 @@ +"""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 new file mode 100644 index 0000000000000000000000000000000000000000..373938e71e0331ed5d9ce2517dcf5f5098ffca72 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/_typing.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +import os +import sys +from collections.abc import Sequence +from typing import Any, Protocol, TypeVar, Union + +TYPE_CHECKING = False +if TYPE_CHECKING: + from numbers import _IntegralLike as IntegralLike + + try: + import numpy.typing as npt + + NumpyArray = npt.NDArray[Any] # requires numpy>=1.21 + except (ImportError, AttributeError): + 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 + +if sys.version_info >= (3, 10): + from typing import TypeGuard +else: + try: + from typing_extensions import TypeGuard + except ImportError: + + class TypeGuard: # type: ignore[no-redef] + def __class_getitem__(cls, item: Any) -> type[bool]: + return bool + + +Coords = Union[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 = Union[str, bytes, os.PathLike[str], os.PathLike[bytes]] + + +__all__ = ["Buffer", "IntegralLike", "StrOrBytesPath", "SupportsRead", "TypeGuard"] diff --git a/.venv/lib/python3.12/site-packages/PIL/_util.py b/.venv/lib/python3.12/site-packages/PIL/_util.py new file mode 100644 index 0000000000000000000000000000000000000000..8ef0d36f7545a85e57829e036818bc4fa2eb72c7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/_util.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +import os +from typing import Any, NoReturn + +from ._typing import StrOrBytesPath, TypeGuard + + +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 new file mode 100644 index 0000000000000000000000000000000000000000..74e63356c95519ff405d0fdd29ce997d3dd5c8f5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/_version.py @@ -0,0 +1,4 @@ +# Master version for Pillow +from __future__ import annotations + +__version__ = "11.3.0" diff --git a/.venv/lib/python3.12/site-packages/PIL/_webp.cpython-312-x86_64-linux-gnu.so b/.venv/lib/python3.12/site-packages/PIL/_webp.cpython-312-x86_64-linux-gnu.so new file mode 100644 index 0000000000000000000000000000000000000000..2b39d49e3b83e0f61d3c1d0ce41b7d16ee323eb4 Binary files /dev/null and b/.venv/lib/python3.12/site-packages/PIL/_webp.cpython-312-x86_64-linux-gnu.so differ diff --git a/.venv/lib/python3.12/site-packages/PIL/_webp.pyi b/.venv/lib/python3.12/site-packages/PIL/_webp.pyi new file mode 100644 index 0000000000000000000000000000000000000000..e27843e5338213713e26973127c738c14313ff98 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/_webp.pyi @@ -0,0 +1,3 @@ +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 new file mode 100644 index 0000000000000000000000000000000000000000..573f1d41256012ccf1a021055222c1f5c3c23339 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/features.py @@ -0,0 +1,361 @@ +from __future__ import annotations + +import collections +import os +import sys +import warnings +from typing import IO + +import PIL + +from . import Image +from ._deprecate import deprecate + +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 | bool, str | None]] = { + "webp_anim": ("PIL._webp", True, None), + "webp_mux": ("PIL._webp", True, None), + "transp_webp": ("PIL._webp", True, 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] + + if isinstance(flag, bool): + deprecate(f'check_feature("{feature}")', 12) + try: + imported_module = __import__(module, fromlist=["PIL"]) + if isinstance(flag, bool): + return flag + 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. + """ + supported_features = [] + for f, (module, flag, _) in features.items(): + if flag is True: + for feature, (feature_module, _) in modules.items(): + if feature_module == module: + if check_module(feature): + supported_features.append(f) + break + elif check_feature(f): + supported_features.append(f) + return supported_features + + +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 new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/PIL/report.py b/.venv/lib/python3.12/site-packages/PIL/report.py new file mode 100644 index 0000000000000000000000000000000000000000..d2815e8455e2ead803de4417314987ce7e9b7598 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PIL/report.py @@ -0,0 +1,5 @@ +from __future__ import annotations + +from .features import pilinfo + +pilinfo(supported_formats=False) diff --git a/.venv/lib/python3.12/site-packages/__editable___sentence_transformers_5_1_0_dev0_finder.py b/.venv/lib/python3.12/site-packages/__editable___sentence_transformers_5_1_0_dev0_finder.py new file mode 100644 index 0000000000000000000000000000000000000000..418abcf7ee1d800689c28521c19b33d47aa19e8d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/__editable___sentence_transformers_5_1_0_dev0_finder.py @@ -0,0 +1,85 @@ +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] = {'sentence_transformers': '/home/ubuntu/lyl/QwenIllustrious/sentence-transformers/sentence_transformers'} +NAMESPACES: dict[str, list[str]] = {} +PATH_PLACEHOLDER = '__editable__.sentence_transformers-5.1.0.dev0.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/_distutils_hack/__init__.py b/.venv/lib/python3.12/site-packages/_distutils_hack/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..94f71b99ecefd27e6cf3fd06fc3c0eef5fd73910 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_distutils_hack/__init__.py @@ -0,0 +1,239 @@ +# don't import any costly modules +import os +import sys + +report_url = ( + "https://github.com/pypa/setuptools/issues/new?template=distutils-deprecation.yml" +) + + +def warn_distutils_present(): + if 'distutils' not in sys.modules: + return + import warnings + + warnings.warn( + "Distutils was imported before Setuptools, but importing Setuptools " + "also replaces the `distutils` module in `sys.modules`. This may lead " + "to undesirable behaviors or errors. To avoid these issues, avoid " + "using distutils directly, ensure that setuptools is installed in the " + "traditional way (e.g. not an editable install), and/or make sure " + "that setuptools is always imported before distutils." + ) + + +def clear_distutils(): + if 'distutils' not in sys.modules: + return + import warnings + + warnings.warn( + "Setuptools is replacing distutils. Support for replacing " + "an already imported distutils is deprecated. In the future, " + "this condition will fail. " + f"Register concerns at {report_url}" + ) + mods = [ + name + for name in sys.modules + if name == "distutils" or name.startswith("distutils.") + ] + for name in mods: + del sys.modules[name] + + +def enabled(): + """ + Allow selection of distutils by environment variable. + """ + which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'local') + if which == 'stdlib': + import warnings + + warnings.warn( + "Reliance on distutils from stdlib is deprecated. Users " + "must rely on setuptools to provide the distutils module. " + "Avoid importing distutils or import setuptools first, " + "and avoid setting SETUPTOOLS_USE_DISTUTILS=stdlib. " + f"Register concerns at {report_url}" + ) + return which == 'local' + + +def ensure_local_distutils(): + import importlib + + clear_distutils() + + # With the DistutilsMetaFinder in place, + # perform an import to cause distutils to be + # loaded from setuptools._distutils. Ref #2906. + with shim(): + importlib.import_module('distutils') + + # check that submodules load as expected + core = importlib.import_module('distutils.core') + assert '_distutils' in core.__file__, core.__file__ + assert 'setuptools._distutils.log' not in sys.modules + + +def do_override(): + """ + Ensure that the local copy of distutils is preferred over stdlib. + + See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401 + for more motivation. + """ + if enabled(): + warn_distutils_present() + ensure_local_distutils() + + +class _TrivialRe: + def __init__(self, *patterns) -> None: + self._patterns = patterns + + def match(self, string): + return all(pat in string for pat in self._patterns) + + +class DistutilsMetaFinder: + def find_spec(self, fullname, path, target=None): + # optimization: only consider top level modules and those + # found in the CPython test suite. + if path is not None and not fullname.startswith('test.'): + return None + + method_name = 'spec_for_{fullname}'.format(**locals()) + method = getattr(self, method_name, lambda: None) + return method() + + def spec_for_distutils(self): + if self.is_cpython(): + return None + + import importlib + import importlib.abc + import importlib.util + + try: + mod = importlib.import_module('setuptools._distutils') + except Exception: + # There are a couple of cases where setuptools._distutils + # may not be present: + # - An older Setuptools without a local distutils is + # taking precedence. Ref #2957. + # - Path manipulation during sitecustomize removes + # setuptools from the path but only after the hook + # has been loaded. Ref #2980. + # In either case, fall back to stdlib behavior. + return None + + class DistutilsLoader(importlib.abc.Loader): + def create_module(self, spec): + mod.__name__ = 'distutils' + return mod + + def exec_module(self, module): + pass + + return importlib.util.spec_from_loader( + 'distutils', DistutilsLoader(), origin=mod.__file__ + ) + + @staticmethod + def is_cpython(): + """ + Suppress supplying distutils for CPython (build and tests). + Ref #2965 and #3007. + """ + return os.path.isfile('pybuilddir.txt') + + def spec_for_pip(self): + """ + Ensure stdlib distutils when running under pip. + See pypa/pip#8761 for rationale. + """ + if sys.version_info >= (3, 12) or self.pip_imported_during_build(): + return + clear_distutils() + self.spec_for_distutils = lambda: None + + @classmethod + def pip_imported_during_build(cls): + """ + Detect if pip is being imported in a build script. Ref #2355. + """ + import traceback + + return any( + cls.frame_file_is_setup(frame) for frame, line in traceback.walk_stack(None) + ) + + @staticmethod + def frame_file_is_setup(frame): + """ + Return True if the indicated frame suggests a setup.py file. + """ + # some frames may not have __file__ (#2940) + return frame.f_globals.get('__file__', '').endswith('setup.py') + + def spec_for_sensitive_tests(self): + """ + Ensure stdlib distutils when running select tests under CPython. + + python/cpython#91169 + """ + clear_distutils() + self.spec_for_distutils = lambda: None + + sensitive_tests = ( + [ + 'test.test_distutils', + 'test.test_peg_generator', + 'test.test_importlib', + ] + if sys.version_info < (3, 10) + else [ + 'test.test_distutils', + ] + ) + + +for name in DistutilsMetaFinder.sensitive_tests: + setattr( + DistutilsMetaFinder, + f'spec_for_{name}', + DistutilsMetaFinder.spec_for_sensitive_tests, + ) + + +DISTUTILS_FINDER = DistutilsMetaFinder() + + +def add_shim(): + DISTUTILS_FINDER in sys.meta_path or insert_shim() + + +class shim: + def __enter__(self) -> None: + insert_shim() + + def __exit__(self, exc: object, value: object, tb: object) -> None: + _remove_shim() + + +def insert_shim(): + sys.meta_path.insert(0, DISTUTILS_FINDER) + + +def _remove_shim(): + try: + sys.meta_path.remove(DISTUTILS_FINDER) + except ValueError: + pass + + +if sys.version_info < (3, 12): + # DistutilsMetaFinder can only be disabled in Python < 3.12 (PEP 632) + remove_shim = _remove_shim diff --git a/.venv/lib/python3.12/site-packages/_distutils_hack/override.py b/.venv/lib/python3.12/site-packages/_distutils_hack/override.py new file mode 100644 index 0000000000000000000000000000000000000000..2cc433a4a55e3b41fa31089918fb62096092f89f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_distutils_hack/override.py @@ -0,0 +1 @@ +__import__('_distutils_hack').do_override() diff --git a/.venv/lib/python3.12/site-packages/_virtualenv.py b/.venv/lib/python3.12/site-packages/_virtualenv.py new file mode 100644 index 0000000000000000000000000000000000000000..6c1f22640d567f04c9880b90566436ee5ba14400 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/_virtualenv.py @@ -0,0 +1,101 @@ +"""Patches that are applied at runtime to the virtual environment.""" + +import os +import sys + +VIRTUALENV_PATCH_FILE = os.path.join(__file__) + + +def patch_dist(dist): + """ + Distutils allows user to configure some arguments via a configuration file: + https://docs.python.org/3.11/install/index.html#distutils-configuration-files. + + Some of this arguments though don't make sense in context of the virtual environment files, let's fix them up. + """ # noqa: D205 + # we cannot allow some install config as that would get packages installed outside of the virtual environment + old_parse_config_files = dist.Distribution.parse_config_files + + def parse_config_files(self, *args, **kwargs): + result = old_parse_config_files(self, *args, **kwargs) + install = self.get_option_dict("install") + + if "prefix" in install: # the prefix governs where to install the libraries + install["prefix"] = VIRTUALENV_PATCH_FILE, os.path.abspath(sys.prefix) + for base in ("purelib", "platlib", "headers", "scripts", "data"): + key = f"install_{base}" + if key in install: # do not allow global configs to hijack venv paths + install.pop(key, None) + return result + + dist.Distribution.parse_config_files = parse_config_files + + +# Import hook that patches some modules to ignore configuration values that break package installation in case +# of virtual environments. +_DISTUTILS_PATCH = "distutils.dist", "setuptools.dist" +# https://docs.python.org/3/library/importlib.html#setting-up-an-importer + + +class _Finder: + """A meta path finder that allows patching the imported distutils modules.""" + + fullname = None + + # lock[0] is threading.Lock(), but initialized lazily to avoid importing threading very early at startup, + # because there are gevent-based applications that need to be first to import threading by themselves. + # See https://github.com/pypa/virtualenv/issues/1895 for details. + lock = [] # noqa: RUF012 + + def find_spec(self, fullname, path, target=None): # noqa: ARG002 + if fullname in _DISTUTILS_PATCH and self.fullname is None: + # initialize lock[0] lazily + if len(self.lock) == 0: + import threading + + lock = threading.Lock() + # there is possibility that two threads T1 and T2 are simultaneously running into find_spec, + # observing .lock as empty, and further going into hereby initialization. However due to the GIL, + # list.append() operation is atomic and this way only one of the threads will "win" to put the lock + # - that every thread will use - into .lock[0]. + # https://docs.python.org/3/faq/library.html#what-kinds-of-global-value-mutation-are-thread-safe + self.lock.append(lock) + + from functools import partial + from importlib.util import find_spec + + with self.lock[0]: + self.fullname = fullname + try: + spec = find_spec(fullname, path) + if spec is not None: + # https://www.python.org/dev/peps/pep-0451/#how-loading-will-work + is_new_api = hasattr(spec.loader, "exec_module") + func_name = "exec_module" if is_new_api else "load_module" + old = getattr(spec.loader, func_name) + func = self.exec_module if is_new_api else self.load_module + if old is not func: + try: # noqa: SIM105 + setattr(spec.loader, func_name, partial(func, old)) + except AttributeError: + pass # C-Extension loaders are r/o such as zipimporter with <3.7 + return spec + finally: + self.fullname = None + return None + + @staticmethod + def exec_module(old, module): + old(module) + if module.__name__ in _DISTUTILS_PATCH: + patch_dist(module) + + @staticmethod + def load_module(old, name): + module = old(name) + if module.__name__ in _DISTUTILS_PATCH: + patch_dist(module) + return module + + +sys.meta_path.insert(0, _Finder()) diff --git a/.venv/lib/python3.12/site-packages/annotated_types/__init__.py b/.venv/lib/python3.12/site-packages/annotated_types/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..74e0deeab3f5904260ac2d36d64fbdec7e0ee0bf --- /dev/null +++ b/.venv/lib/python3.12/site-packages/annotated_types/__init__.py @@ -0,0 +1,432 @@ +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 new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 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 new file mode 100644 index 0000000000000000000000000000000000000000..d9164d6883d2dd47cb766b483592ca3730f6f09d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/annotated_types/test_cases.py @@ -0,0 +1,151 @@ +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/certifi-2025.10.5.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/certifi-2025.10.5.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/certifi-2025.10.5.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/certifi-2025.10.5.dist-info/METADATA b/.venv/lib/python3.12/site-packages/certifi-2025.10.5.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..ca094b1076dc7c85af4db0fefbdf5522fb1b8375 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/certifi-2025.10.5.dist-info/METADATA @@ -0,0 +1,78 @@ +Metadata-Version: 2.4 +Name: certifi +Version: 2025.10.5 +Summary: Python package for providing Mozilla's CA Bundle. +Home-page: https://github.com/certifi/python-certifi +Author: Kenneth Reitz +Author-email: me@kennethreitz.com +License: MPL-2.0 +Project-URL: Source, https://github.com/certifi/python-certifi +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0) +Classifier: Natural Language :: English +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Requires-Python: >=3.7 +License-File: LICENSE +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: home-page +Dynamic: license +Dynamic: license-file +Dynamic: project-url +Dynamic: requires-python +Dynamic: summary + +Certifi: Python SSL Certificates +================================ + +Certifi provides Mozilla's carefully curated collection of Root Certificates for +validating the trustworthiness of SSL certificates while verifying the identity +of TLS hosts. It has been extracted from the `Requests`_ project. + +Installation +------------ + +``certifi`` is available on PyPI. Simply install it with ``pip``:: + + $ pip install certifi + +Usage +----- + +To reference the installed certificate authority (CA) bundle, you can use the +built-in function:: + + >>> import certifi + + >>> certifi.where() + '/usr/local/lib/python3.7/site-packages/certifi/cacert.pem' + +Or from the command line:: + + $ python -m certifi + /usr/local/lib/python3.7/site-packages/certifi/cacert.pem + +Enjoy! + +.. _`Requests`: https://requests.readthedocs.io/en/master/ + +Addition/Removal of Certificates +-------------------------------- + +Certifi does not support any addition/removal or other modification of the +CA trust store content. This project is intended to provide a reliable and +highly portable root of trust to python deployments. Look to upstream projects +for methods to use alternate trust. diff --git a/.venv/lib/python3.12/site-packages/certifi-2025.10.5.dist-info/RECORD b/.venv/lib/python3.12/site-packages/certifi-2025.10.5.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..7aeac6d4e59787cae49afa5524ddea22197f8175 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/certifi-2025.10.5.dist-info/RECORD @@ -0,0 +1,12 @@ +certifi-2025.10.5.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +certifi-2025.10.5.dist-info/METADATA,sha256=RzyR4sT6xRN1pNNy24IHVOlZuDJh1BNfaMa04zEadtk,2474 +certifi-2025.10.5.dist-info/RECORD,, +certifi-2025.10.5.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +certifi-2025.10.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91 +certifi-2025.10.5.dist-info/licenses/LICENSE,sha256=6TcW2mucDVpKHfYP5pWzcPBpVgPSH2-D8FPkLPwQyvc,989 +certifi-2025.10.5.dist-info/top_level.txt,sha256=KMu4vUCfsjLrkPbSNdgdekS-pVJzBAJFO__nI8NF6-U,8 +certifi/__init__.py,sha256=jWkaYHMk4oIPSSBEK5bLMbO_qrkyNm_cRFx-D16-3Ks,94 +certifi/__main__.py,sha256=xBBoj905TUWBLRGANOcf7oi6e-3dMP4cEoG9OyMs11g,243 +certifi/cacert.pem,sha256=IIn8WiWDZAH67pn3IkYLAbOTmZdGoPuBeUNmbW7MBFg,291366 +certifi/core.py,sha256=XFXycndG5pf37ayeF8N32HUuDafsyhkVMbO4BAPWHa0,3394 +certifi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.venv/lib/python3.12/site-packages/certifi-2025.10.5.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/certifi-2025.10.5.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/certifi-2025.10.5.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/certifi-2025.10.5.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..e7fa31b6f3f78deb1022c1f7927f07d4d16da822 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/certifi-2025.10.5.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.12/site-packages/certifi-2025.10.5.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/certifi-2025.10.5.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..963eac530b9bc28d704d1bc410299c68e3216d4d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/certifi-2025.10.5.dist-info/top_level.txt @@ -0,0 +1 @@ +certifi diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/METADATA b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..8d32edcc9f0aa2057cc7844f7bb36b2c8c2e41d7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/METADATA @@ -0,0 +1,764 @@ +Metadata-Version: 2.4 +Name: charset-normalizer +Version: 3.4.4 +Summary: The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet. +Author-email: "Ahmed R. TAHRI" +Maintainer-email: "Ahmed R. TAHRI" +License: MIT +Project-URL: Changelog, https://github.com/jawah/charset_normalizer/blob/master/CHANGELOG.md +Project-URL: Documentation, https://charset-normalizer.readthedocs.io/ +Project-URL: Code, https://github.com/jawah/charset_normalizer +Project-URL: Issue tracker, https://github.com/jawah/charset_normalizer/issues +Keywords: encoding,charset,charset-detector,detector,normalization,unicode,chardet,detect +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Text Processing :: Linguistic +Classifier: Topic :: Utilities +Classifier: Typing :: Typed +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +License-File: LICENSE +Provides-Extra: unicode-backport +Dynamic: license-file + +

Charset Detection, for Everyone 👋

+ +

+ The Real First Universal Charset Detector
+ + + + + Download Count Total + + + + +

+

+ Featured Packages
+ + Static Badge + + + Static Badge + +

+

+ In other language (unofficial port - by the community)
+ + Static Badge + +

+ +> A library that helps you read text from an unknown charset encoding.
Motivated by `chardet`, +> I'm trying to resolve the issue by taking a new approach. +> All IANA character set names for which the Python core library provides codecs are supported. + +

+ >>>>> 👉 Try Me Online Now, Then Adopt Me 👈 <<<<< +

+ +This project offers you an alternative to **Universal Charset Encoding Detector**, also known as **Chardet**. + +| Feature | [Chardet](https://github.com/chardet/chardet) | Charset Normalizer | [cChardet](https://github.com/PyYoshi/cChardet) | +|--------------------------------------------------|:---------------------------------------------:|:--------------------------------------------------------------------------------------------------:|:-----------------------------------------------:| +| `Fast` | ❌ | ✅ | ✅ | +| `Universal**` | ❌ | ✅ | ❌ | +| `Reliable` **without** distinguishable standards | ❌ | ✅ | ✅ | +| `Reliable` **with** distinguishable standards | ✅ | ✅ | ✅ | +| `License` | LGPL-2.1
_restrictive_ | MIT | MPL-1.1
_restrictive_ | +| `Native Python` | ✅ | ✅ | ❌ | +| `Detect spoken language` | ❌ | ✅ | N/A | +| `UnicodeDecodeError Safety` | ❌ | ✅ | ❌ | +| `Whl Size (min)` | 193.6 kB | 42 kB | ~200 kB | +| `Supported Encoding` | 33 | 🎉 [99](https://charset-normalizer.readthedocs.io/en/latest/user/support.html#supported-encodings) | 40 | + +

+Reading Normalized TextCat Reading Text +

+ +*\*\* : They are clearly using specific code for a specific encoding even if covering most of used one*
+ +## ⚡ Performance + +This package offer better performance than its counterpart Chardet. Here are some numbers. + +| Package | Accuracy | Mean per file (ms) | File per sec (est) | +|-----------------------------------------------|:--------:|:------------------:|:------------------:| +| [chardet](https://github.com/chardet/chardet) | 86 % | 63 ms | 16 file/sec | +| charset-normalizer | **98 %** | **10 ms** | 100 file/sec | + +| Package | 99th percentile | 95th percentile | 50th percentile | +|-----------------------------------------------|:---------------:|:---------------:|:---------------:| +| [chardet](https://github.com/chardet/chardet) | 265 ms | 71 ms | 7 ms | +| charset-normalizer | 100 ms | 50 ms | 5 ms | + +_updated as of december 2024 using CPython 3.12_ + +Chardet's performance on larger file (1MB+) are very poor. Expect huge difference on large payload. + +> Stats are generated using 400+ files using default parameters. More details on used files, see GHA workflows. +> And yes, these results might change at any time. The dataset can be updated to include more files. +> The actual delays heavily depends on your CPU capabilities. The factors should remain the same. +> Keep in mind that the stats are generous and that Chardet accuracy vs our is measured using Chardet initial capability +> (e.g. Supported Encoding) Challenge-them if you want. + +## ✨ Installation + +Using pip: + +```sh +pip install charset-normalizer -U +``` + +## 🚀 Basic Usage + +### CLI +This package comes with a CLI. + +``` +usage: normalizer [-h] [-v] [-a] [-n] [-m] [-r] [-f] [-t THRESHOLD] + file [file ...] + +The Real First Universal Charset Detector. Discover originating encoding used +on text file. Normalize text to unicode. + +positional arguments: + files File(s) to be analysed + +optional arguments: + -h, --help show this help message and exit + -v, --verbose Display complementary information about file if any. + Stdout will contain logs about the detection process. + -a, --with-alternative + Output complementary possibilities if any. Top-level + JSON WILL be a list. + -n, --normalize Permit to normalize input file. If not set, program + does not write anything. + -m, --minimal Only output the charset detected to STDOUT. Disabling + JSON output. + -r, --replace Replace file when trying to normalize it instead of + creating a new one. + -f, --force Replace file without asking if you are sure, use this + flag with caution. + -t THRESHOLD, --threshold THRESHOLD + Define a custom maximum amount of chaos allowed in + decoded content. 0. <= chaos <= 1. + --version Show version information and exit. +``` + +```bash +normalizer ./data/sample.1.fr.srt +``` + +or + +```bash +python -m charset_normalizer ./data/sample.1.fr.srt +``` + +🎉 Since version 1.4.0 the CLI produce easily usable stdout result in JSON format. + +```json +{ + "path": "/home/default/projects/charset_normalizer/data/sample.1.fr.srt", + "encoding": "cp1252", + "encoding_aliases": [ + "1252", + "windows_1252" + ], + "alternative_encodings": [ + "cp1254", + "cp1256", + "cp1258", + "iso8859_14", + "iso8859_15", + "iso8859_16", + "iso8859_3", + "iso8859_9", + "latin_1", + "mbcs" + ], + "language": "French", + "alphabets": [ + "Basic Latin", + "Latin-1 Supplement" + ], + "has_sig_or_bom": false, + "chaos": 0.149, + "coherence": 97.152, + "unicode_path": null, + "is_preferred": true +} +``` + +### Python +*Just print out normalized text* +```python +from charset_normalizer import from_path + +results = from_path('./my_subtitle.srt') + +print(str(results.best())) +``` + +*Upgrade your code without effort* +```python +from charset_normalizer import detect +``` + +The above code will behave the same as **chardet**. We ensure that we offer the best (reasonable) BC result possible. + +See the docs for advanced usage : [readthedocs.io](https://charset-normalizer.readthedocs.io/en/latest/) + +## 😇 Why + +When I started using Chardet, I noticed that it was not suited to my expectations, and I wanted to propose a +reliable alternative using a completely different method. Also! I never back down on a good challenge! + +I **don't care** about the **originating charset** encoding, because **two different tables** can +produce **two identical rendered string.** +What I want is to get readable text, the best I can. + +In a way, **I'm brute forcing text decoding.** How cool is that ? 😎 + +Don't confuse package **ftfy** with charset-normalizer or chardet. ftfy goal is to repair Unicode string whereas charset-normalizer to convert raw file in unknown encoding to unicode. + +## 🍰 How + + - Discard all charset encoding table that could not fit the binary content. + - Measure noise, or the mess once opened (by chunks) with a corresponding charset encoding. + - Extract matches with the lowest mess detected. + - Additionally, we measure coherence / probe for a language. + +**Wait a minute**, what is noise/mess and coherence according to **YOU ?** + +*Noise :* I opened hundred of text files, **written by humans**, with the wrong encoding table. **I observed**, then +**I established** some ground rules about **what is obvious** when **it seems like** a mess (aka. defining noise in rendered text). + I know that my interpretation of what is noise is probably incomplete, feel free to contribute in order to + improve or rewrite it. + +*Coherence :* For each language there is on earth, we have computed ranked letter appearance occurrences (the best we can). So I thought +that intel is worth something here. So I use those records against decoded text to check if I can detect intelligent design. + +## ⚡ Known limitations + + - Language detection is unreliable when text contains two or more languages sharing identical letters. (eg. HTML (english tags) + Turkish content (Sharing Latin characters)) + - Every charset detector heavily depends on sufficient content. In common cases, do not bother run detection on very tiny content. + +## ⚠️ About Python EOLs + +**If you are running:** + +- Python >=2.7,<3.5: Unsupported +- Python 3.5: charset-normalizer < 2.1 +- Python 3.6: charset-normalizer < 3.1 +- Python 3.7: charset-normalizer < 4.0 + +Upgrade your Python interpreter as soon as possible. + +## 👤 Contributing + +Contributions, issues and feature requests are very much welcome.
+Feel free to check [issues page](https://github.com/ousret/charset_normalizer/issues) if you want to contribute. + +## 📝 License + +Copyright © [Ahmed TAHRI @Ousret](https://github.com/Ousret).
+This project is [MIT](https://github.com/Ousret/charset_normalizer/blob/master/LICENSE) licensed. + +Characters frequencies used in this project © 2012 [Denny Vrandečić](http://simia.net/letters/) + +## 💼 For Enterprise + +Professional support for charset-normalizer is available as part of the [Tidelift +Subscription][1]. Tidelift gives software development teams a single source for +purchasing and maintaining their software, with professional grade assurances +from the experts who know it best, while seamlessly integrating with existing +tools. + +[1]: https://tidelift.com/subscription/pkg/pypi-charset-normalizer?utm_source=pypi-charset-normalizer&utm_medium=readme + +[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/7297/badge)](https://www.bestpractices.dev/projects/7297) + +# Changelog +All notable changes to charset-normalizer will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). + +## [3.4.4](https://github.com/Ousret/charset_normalizer/compare/3.4.2...3.4.4) (2025-10-13) + +### Changed +- Bound `setuptools` to a specific constraint `setuptools>=68,<=81`. +- Raised upper bound of mypyc for the optional pre-built extension to v1.18.2 + +### Removed +- `setuptools-scm` as a build dependency. + +### Misc +- Enforced hashes in `dev-requirements.txt` and created `ci-requirements.txt` for security purposes. +- Additional pre-built wheels for riscv64, s390x, and armv7l architectures. +- Restore ` multiple.intoto.jsonl` in GitHub releases in addition to individual attestation file per wheel. + +## [3.4.3](https://github.com/Ousret/charset_normalizer/compare/3.4.2...3.4.3) (2025-08-09) + +### Changed +- mypy(c) is no longer a required dependency at build time if `CHARSET_NORMALIZER_USE_MYPYC` isn't set to `1`. (#595) (#583) +- automatically lower confidence on small bytes samples that are not Unicode in `detect` output legacy function. (#391) + +### Added +- Custom build backend to overcome inability to mark mypy as an optional dependency in the build phase. +- Support for Python 3.14 + +### Fixed +- sdist archive contained useless directories. +- automatically fallback on valid UTF-16 or UTF-32 even if the md says it's noisy. (#633) + +### Misc +- SBOM are automatically published to the relevant GitHub release to comply with regulatory changes. + Each published wheel comes with its SBOM. We choose CycloneDX as the format. +- Prebuilt optimized wheel are no longer distributed by default for CPython 3.7 due to a change in cibuildwheel. + +## [3.4.2](https://github.com/Ousret/charset_normalizer/compare/3.4.1...3.4.2) (2025-05-02) + +### Fixed +- Addressed the DeprecationWarning in our CLI regarding `argparse.FileType` by backporting the target class into the package. (#591) +- Improved the overall reliability of the detector with CJK Ideographs. (#605) (#587) + +### Changed +- Optional mypyc compilation upgraded to version 1.15 for Python >= 3.8 + +## [3.4.1](https://github.com/Ousret/charset_normalizer/compare/3.4.0...3.4.1) (2024-12-24) + +### Changed +- Project metadata are now stored using `pyproject.toml` instead of `setup.cfg` using setuptools as the build backend. +- Enforce annotation delayed loading for a simpler and consistent types in the project. +- Optional mypyc compilation upgraded to version 1.14 for Python >= 3.8 + +### Added +- pre-commit configuration. +- noxfile. + +### Removed +- `build-requirements.txt` as per using `pyproject.toml` native build configuration. +- `bin/integration.py` and `bin/serve.py` in favor of downstream integration test (see noxfile). +- `setup.cfg` in favor of `pyproject.toml` metadata configuration. +- Unused `utils.range_scan` function. + +### Fixed +- Converting content to Unicode bytes may insert `utf_8` instead of preferred `utf-8`. (#572) +- Deprecation warning "'count' is passed as positional argument" when converting to Unicode bytes on Python 3.13+ + +## [3.4.0](https://github.com/Ousret/charset_normalizer/compare/3.3.2...3.4.0) (2024-10-08) + +### Added +- Argument `--no-preemptive` in the CLI to prevent the detector to search for hints. +- Support for Python 3.13 (#512) + +### Fixed +- Relax the TypeError exception thrown when trying to compare a CharsetMatch with anything else than a CharsetMatch. +- Improved the general reliability of the detector based on user feedbacks. (#520) (#509) (#498) (#407) (#537) +- Declared charset in content (preemptive detection) not changed when converting to utf-8 bytes. (#381) + +## [3.3.2](https://github.com/Ousret/charset_normalizer/compare/3.3.1...3.3.2) (2023-10-31) + +### Fixed +- Unintentional memory usage regression when using large payload that match several encoding (#376) +- Regression on some detection case showcased in the documentation (#371) + +### Added +- Noise (md) probe that identify malformed arabic representation due to the presence of letters in isolated form (credit to my wife) + +## [3.3.1](https://github.com/Ousret/charset_normalizer/compare/3.3.0...3.3.1) (2023-10-22) + +### Changed +- Optional mypyc compilation upgraded to version 1.6.1 for Python >= 3.8 +- Improved the general detection reliability based on reports from the community + +## [3.3.0](https://github.com/Ousret/charset_normalizer/compare/3.2.0...3.3.0) (2023-09-30) + +### Added +- Allow to execute the CLI (e.g. normalizer) through `python -m charset_normalizer.cli` or `python -m charset_normalizer` +- Support for 9 forgotten encoding that are supported by Python but unlisted in `encoding.aliases` as they have no alias (#323) + +### Removed +- (internal) Redundant utils.is_ascii function and unused function is_private_use_only +- (internal) charset_normalizer.assets is moved inside charset_normalizer.constant + +### Changed +- (internal) Unicode code blocks in constants are updated using the latest v15.0.0 definition to improve detection +- Optional mypyc compilation upgraded to version 1.5.1 for Python >= 3.8 + +### Fixed +- Unable to properly sort CharsetMatch when both chaos/noise and coherence were close due to an unreachable condition in \_\_lt\_\_ (#350) + +## [3.2.0](https://github.com/Ousret/charset_normalizer/compare/3.1.0...3.2.0) (2023-06-07) + +### Changed +- Typehint for function `from_path` no longer enforce `PathLike` as its first argument +- Minor improvement over the global detection reliability + +### Added +- Introduce function `is_binary` that relies on main capabilities, and optimized to detect binaries +- Propagate `enable_fallback` argument throughout `from_bytes`, `from_path`, and `from_fp` that allow a deeper control over the detection (default True) +- Explicit support for Python 3.12 + +### Fixed +- Edge case detection failure where a file would contain 'very-long' camel cased word (Issue #289) + +## [3.1.0](https://github.com/Ousret/charset_normalizer/compare/3.0.1...3.1.0) (2023-03-06) + +### Added +- Argument `should_rename_legacy` for legacy function `detect` and disregard any new arguments without errors (PR #262) + +### Removed +- Support for Python 3.6 (PR #260) + +### Changed +- Optional speedup provided by mypy/c 1.0.1 + +## [3.0.1](https://github.com/Ousret/charset_normalizer/compare/3.0.0...3.0.1) (2022-11-18) + +### Fixed +- Multi-bytes cutter/chunk generator did not always cut correctly (PR #233) + +### Changed +- Speedup provided by mypy/c 0.990 on Python >= 3.7 + +## [3.0.0](https://github.com/Ousret/charset_normalizer/compare/2.1.1...3.0.0) (2022-10-20) + +### Added +- Extend the capability of explain=True when cp_isolation contains at most two entries (min one), will log in details of the Mess-detector results +- Support for alternative language frequency set in charset_normalizer.assets.FREQUENCIES +- Add parameter `language_threshold` in `from_bytes`, `from_path` and `from_fp` to adjust the minimum expected coherence ratio +- `normalizer --version` now specify if current version provide extra speedup (meaning mypyc compilation whl) + +### Changed +- Build with static metadata using 'build' frontend +- Make the language detection stricter +- Optional: Module `md.py` can be compiled using Mypyc to provide an extra speedup up to 4x faster than v2.1 + +### Fixed +- CLI with opt --normalize fail when using full path for files +- TooManyAccentuatedPlugin induce false positive on the mess detection when too few alpha character have been fed to it +- Sphinx warnings when generating the documentation + +### Removed +- Coherence detector no longer return 'Simple English' instead return 'English' +- Coherence detector no longer return 'Classical Chinese' instead return 'Chinese' +- Breaking: Method `first()` and `best()` from CharsetMatch +- UTF-7 will no longer appear as "detected" without a recognized SIG/mark (is unreliable/conflict with ASCII) +- Breaking: Class aliases CharsetDetector, CharsetDoctor, CharsetNormalizerMatch and CharsetNormalizerMatches +- Breaking: Top-level function `normalize` +- Breaking: Properties `chaos_secondary_pass`, `coherence_non_latin` and `w_counter` from CharsetMatch +- Support for the backport `unicodedata2` + +## [3.0.0rc1](https://github.com/Ousret/charset_normalizer/compare/3.0.0b2...3.0.0rc1) (2022-10-18) + +### Added +- Extend the capability of explain=True when cp_isolation contains at most two entries (min one), will log in details of the Mess-detector results +- Support for alternative language frequency set in charset_normalizer.assets.FREQUENCIES +- Add parameter `language_threshold` in `from_bytes`, `from_path` and `from_fp` to adjust the minimum expected coherence ratio + +### Changed +- Build with static metadata using 'build' frontend +- Make the language detection stricter + +### Fixed +- CLI with opt --normalize fail when using full path for files +- TooManyAccentuatedPlugin induce false positive on the mess detection when too few alpha character have been fed to it + +### Removed +- Coherence detector no longer return 'Simple English' instead return 'English' +- Coherence detector no longer return 'Classical Chinese' instead return 'Chinese' + +## [3.0.0b2](https://github.com/Ousret/charset_normalizer/compare/3.0.0b1...3.0.0b2) (2022-08-21) + +### Added +- `normalizer --version` now specify if current version provide extra speedup (meaning mypyc compilation whl) + +### Removed +- Breaking: Method `first()` and `best()` from CharsetMatch +- UTF-7 will no longer appear as "detected" without a recognized SIG/mark (is unreliable/conflict with ASCII) + +### Fixed +- Sphinx warnings when generating the documentation + +## [3.0.0b1](https://github.com/Ousret/charset_normalizer/compare/2.1.0...3.0.0b1) (2022-08-15) + +### Changed +- Optional: Module `md.py` can be compiled using Mypyc to provide an extra speedup up to 4x faster than v2.1 + +### Removed +- Breaking: Class aliases CharsetDetector, CharsetDoctor, CharsetNormalizerMatch and CharsetNormalizerMatches +- Breaking: Top-level function `normalize` +- Breaking: Properties `chaos_secondary_pass`, `coherence_non_latin` and `w_counter` from CharsetMatch +- Support for the backport `unicodedata2` + +## [2.1.1](https://github.com/Ousret/charset_normalizer/compare/2.1.0...2.1.1) (2022-08-19) + +### Deprecated +- Function `normalize` scheduled for removal in 3.0 + +### Changed +- Removed useless call to decode in fn is_unprintable (#206) + +### Fixed +- Third-party library (i18n xgettext) crashing not recognizing utf_8 (PEP 263) with underscore from [@aleksandernovikov](https://github.com/aleksandernovikov) (#204) + +## [2.1.0](https://github.com/Ousret/charset_normalizer/compare/2.0.12...2.1.0) (2022-06-19) + +### Added +- Output the Unicode table version when running the CLI with `--version` (PR #194) + +### Changed +- Re-use decoded buffer for single byte character sets from [@nijel](https://github.com/nijel) (PR #175) +- Fixing some performance bottlenecks from [@deedy5](https://github.com/deedy5) (PR #183) + +### Fixed +- Workaround potential bug in cpython with Zero Width No-Break Space located in Arabic Presentation Forms-B, Unicode 1.1 not acknowledged as space (PR #175) +- CLI default threshold aligned with the API threshold from [@oleksandr-kuzmenko](https://github.com/oleksandr-kuzmenko) (PR #181) + +### Removed +- Support for Python 3.5 (PR #192) + +### Deprecated +- Use of backport unicodedata from `unicodedata2` as Python is quickly catching up, scheduled for removal in 3.0 (PR #194) + +## [2.0.12](https://github.com/Ousret/charset_normalizer/compare/2.0.11...2.0.12) (2022-02-12) + +### Fixed +- ASCII miss-detection on rare cases (PR #170) + +## [2.0.11](https://github.com/Ousret/charset_normalizer/compare/2.0.10...2.0.11) (2022-01-30) + +### Added +- Explicit support for Python 3.11 (PR #164) + +### Changed +- The logging behavior have been completely reviewed, now using only TRACE and DEBUG levels (PR #163 #165) + +## [2.0.10](https://github.com/Ousret/charset_normalizer/compare/2.0.9...2.0.10) (2022-01-04) + +### Fixed +- Fallback match entries might lead to UnicodeDecodeError for large bytes sequence (PR #154) + +### Changed +- Skipping the language-detection (CD) on ASCII (PR #155) + +## [2.0.9](https://github.com/Ousret/charset_normalizer/compare/2.0.8...2.0.9) (2021-12-03) + +### Changed +- Moderating the logging impact (since 2.0.8) for specific environments (PR #147) + +### Fixed +- Wrong logging level applied when setting kwarg `explain` to True (PR #146) + +## [2.0.8](https://github.com/Ousret/charset_normalizer/compare/2.0.7...2.0.8) (2021-11-24) +### Changed +- Improvement over Vietnamese detection (PR #126) +- MD improvement on trailing data and long foreign (non-pure latin) data (PR #124) +- Efficiency improvements in cd/alphabet_languages from [@adbar](https://github.com/adbar) (PR #122) +- call sum() without an intermediary list following PEP 289 recommendations from [@adbar](https://github.com/adbar) (PR #129) +- Code style as refactored by Sourcery-AI (PR #131) +- Minor adjustment on the MD around european words (PR #133) +- Remove and replace SRTs from assets / tests (PR #139) +- Initialize the library logger with a `NullHandler` by default from [@nmaynes](https://github.com/nmaynes) (PR #135) +- Setting kwarg `explain` to True will add provisionally (bounded to function lifespan) a specific stream handler (PR #135) + +### Fixed +- Fix large (misleading) sequence giving UnicodeDecodeError (PR #137) +- Avoid using too insignificant chunk (PR #137) + +### Added +- Add and expose function `set_logging_handler` to configure a specific StreamHandler from [@nmaynes](https://github.com/nmaynes) (PR #135) +- Add `CHANGELOG.md` entries, format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) (PR #141) + +## [2.0.7](https://github.com/Ousret/charset_normalizer/compare/2.0.6...2.0.7) (2021-10-11) +### Added +- Add support for Kazakh (Cyrillic) language detection (PR #109) + +### Changed +- Further, improve inferring the language from a given single-byte code page (PR #112) +- Vainly trying to leverage PEP263 when PEP3120 is not supported (PR #116) +- Refactoring for potential performance improvements in loops from [@adbar](https://github.com/adbar) (PR #113) +- Various detection improvement (MD+CD) (PR #117) + +### Removed +- Remove redundant logging entry about detected language(s) (PR #115) + +### Fixed +- Fix a minor inconsistency between Python 3.5 and other versions regarding language detection (PR #117 #102) + +## [2.0.6](https://github.com/Ousret/charset_normalizer/compare/2.0.5...2.0.6) (2021-09-18) +### Fixed +- Unforeseen regression with the loss of the backward-compatibility with some older minor of Python 3.5.x (PR #100) +- Fix CLI crash when using --minimal output in certain cases (PR #103) + +### Changed +- Minor improvement to the detection efficiency (less than 1%) (PR #106 #101) + +## [2.0.5](https://github.com/Ousret/charset_normalizer/compare/2.0.4...2.0.5) (2021-09-14) +### Changed +- The project now comply with: flake8, mypy, isort and black to ensure a better overall quality (PR #81) +- The BC-support with v1.x was improved, the old staticmethods are restored (PR #82) +- The Unicode detection is slightly improved (PR #93) +- Add syntax sugar \_\_bool\_\_ for results CharsetMatches list-container (PR #91) + +### Removed +- The project no longer raise warning on tiny content given for detection, will be simply logged as warning instead (PR #92) + +### Fixed +- In some rare case, the chunks extractor could cut in the middle of a multi-byte character and could mislead the mess detection (PR #95) +- Some rare 'space' characters could trip up the UnprintablePlugin/Mess detection (PR #96) +- The MANIFEST.in was not exhaustive (PR #78) + +## [2.0.4](https://github.com/Ousret/charset_normalizer/compare/2.0.3...2.0.4) (2021-07-30) +### Fixed +- The CLI no longer raise an unexpected exception when no encoding has been found (PR #70) +- Fix accessing the 'alphabets' property when the payload contains surrogate characters (PR #68) +- The logger could mislead (explain=True) on detected languages and the impact of one MBCS match (PR #72) +- Submatch factoring could be wrong in rare edge cases (PR #72) +- Multiple files given to the CLI were ignored when publishing results to STDOUT. (After the first path) (PR #72) +- Fix line endings from CRLF to LF for certain project files (PR #67) + +### Changed +- Adjust the MD to lower the sensitivity, thus improving the global detection reliability (PR #69 #76) +- Allow fallback on specified encoding if any (PR #71) + +## [2.0.3](https://github.com/Ousret/charset_normalizer/compare/2.0.2...2.0.3) (2021-07-16) +### Changed +- Part of the detection mechanism has been improved to be less sensitive, resulting in more accurate detection results. Especially ASCII. (PR #63) +- According to the community wishes, the detection will fall back on ASCII or UTF-8 in a last-resort case. (PR #64) + +## [2.0.2](https://github.com/Ousret/charset_normalizer/compare/2.0.1...2.0.2) (2021-07-15) +### Fixed +- Empty/Too small JSON payload miss-detection fixed. Report from [@tseaver](https://github.com/tseaver) (PR #59) + +### Changed +- Don't inject unicodedata2 into sys.modules from [@akx](https://github.com/akx) (PR #57) + +## [2.0.1](https://github.com/Ousret/charset_normalizer/compare/2.0.0...2.0.1) (2021-07-13) +### Fixed +- Make it work where there isn't a filesystem available, dropping assets frequencies.json. Report from [@sethmlarson](https://github.com/sethmlarson). (PR #55) +- Using explain=False permanently disable the verbose output in the current runtime (PR #47) +- One log entry (language target preemptive) was not show in logs when using explain=True (PR #47) +- Fix undesired exception (ValueError) on getitem of instance CharsetMatches (PR #52) + +### Changed +- Public function normalize default args values were not aligned with from_bytes (PR #53) + +### Added +- You may now use charset aliases in cp_isolation and cp_exclusion arguments (PR #47) + +## [2.0.0](https://github.com/Ousret/charset_normalizer/compare/1.4.1...2.0.0) (2021-07-02) +### Changed +- 4x to 5 times faster than the previous 1.4.0 release. At least 2x faster than Chardet. +- Accent has been made on UTF-8 detection, should perform rather instantaneous. +- The backward compatibility with Chardet has been greatly improved. The legacy detect function returns an identical charset name whenever possible. +- The detection mechanism has been slightly improved, now Turkish content is detected correctly (most of the time) +- The program has been rewritten to ease the readability and maintainability. (+Using static typing)+ +- utf_7 detection has been reinstated. + +### Removed +- This package no longer require anything when used with Python 3.5 (Dropped cached_property) +- Removed support for these languages: Catalan, Esperanto, Kazakh, Baque, Volapük, Azeri, Galician, Nynorsk, Macedonian, and Serbocroatian. +- The exception hook on UnicodeDecodeError has been removed. + +### Deprecated +- Methods coherence_non_latin, w_counter, chaos_secondary_pass of the class CharsetMatch are now deprecated and scheduled for removal in v3.0 + +### Fixed +- The CLI output used the relative path of the file(s). Should be absolute. + +## [1.4.1](https://github.com/Ousret/charset_normalizer/compare/1.4.0...1.4.1) (2021-05-28) +### Fixed +- Logger configuration/usage no longer conflict with others (PR #44) + +## [1.4.0](https://github.com/Ousret/charset_normalizer/compare/1.3.9...1.4.0) (2021-05-21) +### Removed +- Using standard logging instead of using the package loguru. +- Dropping nose test framework in favor of the maintained pytest. +- Choose to not use dragonmapper package to help with gibberish Chinese/CJK text. +- Require cached_property only for Python 3.5 due to constraint. Dropping for every other interpreter version. +- Stop support for UTF-7 that does not contain a SIG. +- Dropping PrettyTable, replaced with pure JSON output in CLI. + +### Fixed +- BOM marker in a CharsetNormalizerMatch instance could be False in rare cases even if obviously present. Due to the sub-match factoring process. +- Not searching properly for the BOM when trying utf32/16 parent codec. + +### Changed +- Improving the package final size by compressing frequencies.json. +- Huge improvement over the larges payload. + +### Added +- CLI now produces JSON consumable output. +- Return ASCII if given sequences fit. Given reasonable confidence. + +## [1.3.9](https://github.com/Ousret/charset_normalizer/compare/1.3.8...1.3.9) (2021-05-13) + +### Fixed +- In some very rare cases, you may end up getting encode/decode errors due to a bad bytes payload (PR #40) + +## [1.3.8](https://github.com/Ousret/charset_normalizer/compare/1.3.7...1.3.8) (2021-05-12) + +### Fixed +- Empty given payload for detection may cause an exception if trying to access the `alphabets` property. (PR #39) + +## [1.3.7](https://github.com/Ousret/charset_normalizer/compare/1.3.6...1.3.7) (2021-05-12) + +### Fixed +- The legacy detect function should return UTF-8-SIG if sig is present in the payload. (PR #38) + +## [1.3.6](https://github.com/Ousret/charset_normalizer/compare/1.3.5...1.3.6) (2021-02-09) + +### Changed +- Amend the previous release to allow prettytable 2.0 (PR #35) + +## [1.3.5](https://github.com/Ousret/charset_normalizer/compare/1.3.4...1.3.5) (2021-02-08) + +### Fixed +- Fix error while using the package with a python pre-release interpreter (PR #33) + +### Changed +- Dependencies refactoring, constraints revised. + +### Added +- Add python 3.9 and 3.10 to the supported interpreters + +MIT License + +Copyright (c) 2025 TAHRI Ahmed R. + +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/charset_normalizer-3.4.4.dist-info/RECORD b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..7a0dbfc1ba06f662ba24acf2787ac6653dffc2e0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/RECORD @@ -0,0 +1,24 @@ +../../../bin/normalizer,sha256=VrIThwuodndkKIeFd0Qn7agUddQoKZ7RcY-H0bwAiCE,350 +charset_normalizer-3.4.4.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +charset_normalizer-3.4.4.dist-info/METADATA,sha256=jVuUFBti8dav19YLvWissTihVdF2ozUY4KKMw7jdkBQ,37303 +charset_normalizer-3.4.4.dist-info/RECORD,, +charset_normalizer-3.4.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +charset_normalizer-3.4.4.dist-info/WHEEL,sha256=DxRnWQz-Kp9-4a4hdDHsSv0KUC3H7sN9Nbef3-8RjXU,190 +charset_normalizer-3.4.4.dist-info/entry_points.txt,sha256=ADSTKrkXZ3hhdOVFi6DcUEHQRS0xfxDIE_pEz4wLIXA,65 +charset_normalizer-3.4.4.dist-info/licenses/LICENSE,sha256=bQ1Bv-FwrGx9wkjJpj4lTQ-0WmDVCoJX0K-SxuJJuIc,1071 +charset_normalizer-3.4.4.dist-info/top_level.txt,sha256=7ASyzePr8_xuZWJsnqJjIBtyV8vhEo0wBCv1MPRRi3Q,19 +charset_normalizer/__init__.py,sha256=OKRxRv2Zhnqk00tqkN0c1BtJjm165fWXLydE52IKuHc,1590 +charset_normalizer/__main__.py,sha256=yzYxMR-IhKRHYwcSlavEv8oGdwxsR89mr2X09qXGdps,109 +charset_normalizer/api.py,sha256=V07i8aVeCD8T2fSia3C-fn0i9t8qQguEBhsqszg32Ns,22668 +charset_normalizer/cd.py,sha256=WKTo1HDb-H9HfCDc3Bfwq5jzS25Ziy9SE2a74SgTq88,12522 +charset_normalizer/cli/__init__.py,sha256=D8I86lFk2-py45JvqxniTirSj_sFyE6sjaY_0-G1shc,136 +charset_normalizer/cli/__main__.py,sha256=dMaXG6IJXRvqq8z2tig7Qb83-BpWTln55ooiku5_uvg,12646 +charset_normalizer/constant.py,sha256=7UVY4ldYhmQMHUdgQ_sgZmzcQ0xxYxpBunqSZ-XJZ8U,42713 +charset_normalizer/legacy.py,sha256=sYBzSpzsRrg_wF4LP536pG64BItw7Tqtc3SMQAHvFLM,2731 +charset_normalizer/md.cpython-312-x86_64-linux-gnu.so,sha256=sZ7umtJLjKfA83NFJ7npkiDyr06zDT8cWtl6uIx2MsM,15912 +charset_normalizer/md.py,sha256=-_oN3h3_X99nkFfqamD3yu45DC_wfk5odH0Tr_CQiXs,20145 +charset_normalizer/md__mypyc.cpython-312-x86_64-linux-gnu.so,sha256=J2WWgLBQiO8sqdFsENp9u5V9uEH0tTwvTLszPdqhsv0,290584 +charset_normalizer/models.py,sha256=lKXhOnIPtiakbK3i__J9wpOfzx3JDTKj7Dn3Rg0VaRI,12394 +charset_normalizer/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +charset_normalizer/utils.py,sha256=sTejPgrdlNsKNucZfJCxJ95lMTLA0ShHLLE3n5wpT9Q,12170 +charset_normalizer/version.py,sha256=nKE4qBNk5WA4LIJ_yIH_aSDfvtsyizkWMg-PUG-UZVk,115 diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..f3e8a970f16adf4526f1722547053522d94bf860 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/WHEEL @@ -0,0 +1,7 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: false +Tag: cp312-cp312-manylinux_2_17_x86_64 +Tag: cp312-cp312-manylinux2014_x86_64 +Tag: cp312-cp312-manylinux_2_28_x86_64 + diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/entry_points.txt b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/entry_points.txt new file mode 100644 index 0000000000000000000000000000000000000000..65619e73ec06c20c2a70c9507b872ad624d1a85c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +normalizer = charset_normalizer.cli:cli_detect diff --git a/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..66958f0a069d7aea7939bed40b9197608e93b243 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/charset_normalizer-3.4.4.dist-info/top_level.txt @@ -0,0 +1 @@ +charset_normalizer diff --git a/.venv/lib/python3.12/site-packages/click-8.3.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/click-8.3.0.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/click-8.3.0.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/click-8.3.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/click-8.3.0.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..534eb5725e3a42e1979b8584bc28f45d5fe0293b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/click-8.3.0.dist-info/METADATA @@ -0,0 +1,84 @@ +Metadata-Version: 2.4 +Name: click +Version: 8.3.0 +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.3.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/click-8.3.0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..bb4705b2f739a2574d14ee4ddfc26c6a0a617794 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/click-8.3.0.dist-info/RECORD @@ -0,0 +1,24 @@ +click-8.3.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +click-8.3.0.dist-info/METADATA,sha256=P6vpEHZ_MLBt4SO2eB-QaadcOdiznkzaZtJImRo7_V4,2621 +click-8.3.0.dist-info/RECORD,, +click-8.3.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +click-8.3.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82 +click-8.3.0.dist-info/licenses/LICENSE.txt,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 +click/__init__.py,sha256=6YyS1aeyknZ0LYweWozNZy0A9nZ_11wmYIhv3cbQrYo,4473 +click/_compat.py,sha256=v3xBZkFbvA1BXPRkFfBJc6-pIwPI7345m-kQEnpVAs4,18693 +click/_termui_impl.py,sha256=ktpAHyJtNkhyR-x64CQFD6xJQI11fTA3qg2AV3iCToU,26799 +click/_textwrap.py,sha256=BOae0RQ6vg3FkNgSJyOoGzG1meGMxJ_ukWVZKx_v-0o,1400 +click/_utils.py,sha256=kZwtTf5gMuCilJJceS2iTCvRvCY-0aN5rJq8gKw7p8g,943 +click/_winconsole.py,sha256=_vxUuUaxwBhoR0vUWCNuHY8VUefiMdCIyU2SXPqoF-A,8465 +click/core.py,sha256=1A5T8UoAXklIGPTJ83_DJbVi35ehtJS2FTkP_wQ7es0,128855 +click/decorators.py,sha256=5P7abhJtAQYp_KHgjUvhMv464ERwOzrv2enNknlwHyQ,18461 +click/exceptions.py,sha256=8utf8w6V5hJXMnO_ic1FNrtbwuEn1NUu1aDwV8UqnG4,9954 +click/formatting.py,sha256=RVfwwr0rwWNpgGr8NaHodPzkIr7_tUyVh_nDdanLMNc,9730 +click/globals.py,sha256=gM-Nh6A4M0HB_SgkaF5M4ncGGMDHc_flHXu9_oh4GEU,1923 +click/parser.py,sha256=Q31pH0FlQZEq-UXE_ABRzlygEfvxPTuZbWNh4xfXmzw,19010 +click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +click/shell_completion.py,sha256=Cc4GQUFuWpfQBa9sF5qXeeYI7n3tI_1k6ZdSn4BZbT0,20994 +click/termui.py,sha256=vAYrKC2a7f_NfEIhAThEVYfa__ib5XQbTSCGtJlABRA,30847 +click/testing.py,sha256=EERbzcl1br0mW0qBS9EqkknfNfXB9WQEW0ELIpkvuSs,19102 +click/types.py,sha256=ek54BNSFwPKsqtfT7jsqcc4WHui8AIFVMKM4oVZIXhc,39927 +click/utils.py,sha256=gCUoewdAhA-QLBUUHxrLh4uj6m7T1WjZZMNPvR0I7YA,20257 diff --git a/.venv/lib/python3.12/site-packages/click-8.3.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/click-8.3.0.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/click-8.3.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/click-8.3.0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..d8b9936dad9ab2513fa6979f411560d3b6b57e37 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/click-8.3.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.12.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/fsspec-2025.9.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/fsspec-2025.9.0.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec-2025.9.0.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/fsspec-2025.9.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/fsspec-2025.9.0.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..559eb3ebd454aad3270983ebe8972c59a9a6d71b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec-2025.9.0.dist-info/METADATA @@ -0,0 +1,256 @@ +Metadata-Version: 2.4 +Name: fsspec +Version: 2025.9.0 +Summary: File-system specification +Project-URL: Changelog, https://filesystem-spec.readthedocs.io/en/latest/changelog.html +Project-URL: Documentation, https://filesystem-spec.readthedocs.io/en/latest/ +Project-URL: Homepage, https://github.com/fsspec/filesystem_spec +Maintainer-email: Martin Durant +License-Expression: BSD-3-Clause +License-File: LICENSE +Keywords: file +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +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 +Requires-Python: >=3.9 +Provides-Extra: abfs +Requires-Dist: adlfs; extra == 'abfs' +Provides-Extra: adl +Requires-Dist: adlfs; extra == 'adl' +Provides-Extra: arrow +Requires-Dist: pyarrow>=1; extra == 'arrow' +Provides-Extra: dask +Requires-Dist: dask; extra == 'dask' +Requires-Dist: distributed; extra == 'dask' +Provides-Extra: dev +Requires-Dist: pre-commit; extra == 'dev' +Requires-Dist: ruff>=0.5; extra == 'dev' +Provides-Extra: doc +Requires-Dist: numpydoc; extra == 'doc' +Requires-Dist: sphinx; extra == 'doc' +Requires-Dist: sphinx-design; extra == 'doc' +Requires-Dist: sphinx-rtd-theme; extra == 'doc' +Requires-Dist: yarl; extra == 'doc' +Provides-Extra: dropbox +Requires-Dist: dropbox; extra == 'dropbox' +Requires-Dist: dropboxdrivefs; extra == 'dropbox' +Requires-Dist: requests; extra == 'dropbox' +Provides-Extra: entrypoints +Provides-Extra: full +Requires-Dist: adlfs; extra == 'full' +Requires-Dist: aiohttp!=4.0.0a0,!=4.0.0a1; extra == 'full' +Requires-Dist: dask; extra == 'full' +Requires-Dist: distributed; extra == 'full' +Requires-Dist: dropbox; extra == 'full' +Requires-Dist: dropboxdrivefs; extra == 'full' +Requires-Dist: fusepy; extra == 'full' +Requires-Dist: gcsfs; extra == 'full' +Requires-Dist: libarchive-c; extra == 'full' +Requires-Dist: ocifs; extra == 'full' +Requires-Dist: panel; extra == 'full' +Requires-Dist: paramiko; extra == 'full' +Requires-Dist: pyarrow>=1; extra == 'full' +Requires-Dist: pygit2; extra == 'full' +Requires-Dist: requests; extra == 'full' +Requires-Dist: s3fs; extra == 'full' +Requires-Dist: smbprotocol; extra == 'full' +Requires-Dist: tqdm; extra == 'full' +Provides-Extra: fuse +Requires-Dist: fusepy; extra == 'fuse' +Provides-Extra: gcs +Requires-Dist: gcsfs; extra == 'gcs' +Provides-Extra: git +Requires-Dist: pygit2; extra == 'git' +Provides-Extra: github +Requires-Dist: requests; extra == 'github' +Provides-Extra: gs +Requires-Dist: gcsfs; extra == 'gs' +Provides-Extra: gui +Requires-Dist: panel; extra == 'gui' +Provides-Extra: hdfs +Requires-Dist: pyarrow>=1; extra == 'hdfs' +Provides-Extra: http +Requires-Dist: aiohttp!=4.0.0a0,!=4.0.0a1; extra == 'http' +Provides-Extra: libarchive +Requires-Dist: libarchive-c; extra == 'libarchive' +Provides-Extra: oci +Requires-Dist: ocifs; extra == 'oci' +Provides-Extra: s3 +Requires-Dist: s3fs; extra == 's3' +Provides-Extra: sftp +Requires-Dist: paramiko; extra == 'sftp' +Provides-Extra: smb +Requires-Dist: smbprotocol; extra == 'smb' +Provides-Extra: ssh +Requires-Dist: paramiko; extra == 'ssh' +Provides-Extra: test +Requires-Dist: aiohttp!=4.0.0a0,!=4.0.0a1; extra == 'test' +Requires-Dist: numpy; extra == 'test' +Requires-Dist: pytest; extra == 'test' +Requires-Dist: pytest-asyncio!=0.22.0; extra == 'test' +Requires-Dist: pytest-benchmark; extra == 'test' +Requires-Dist: pytest-cov; extra == 'test' +Requires-Dist: pytest-mock; extra == 'test' +Requires-Dist: pytest-recording; extra == 'test' +Requires-Dist: pytest-rerunfailures; extra == 'test' +Requires-Dist: requests; extra == 'test' +Provides-Extra: test-downstream +Requires-Dist: aiobotocore<3.0.0,>=2.5.4; extra == 'test-downstream' +Requires-Dist: dask[dataframe,test]; extra == 'test-downstream' +Requires-Dist: moto[server]<5,>4; extra == 'test-downstream' +Requires-Dist: pytest-timeout; extra == 'test-downstream' +Requires-Dist: xarray; extra == 'test-downstream' +Provides-Extra: test-full +Requires-Dist: adlfs; extra == 'test-full' +Requires-Dist: aiohttp!=4.0.0a0,!=4.0.0a1; extra == 'test-full' +Requires-Dist: cloudpickle; extra == 'test-full' +Requires-Dist: dask; extra == 'test-full' +Requires-Dist: distributed; extra == 'test-full' +Requires-Dist: dropbox; extra == 'test-full' +Requires-Dist: dropboxdrivefs; extra == 'test-full' +Requires-Dist: fastparquet; extra == 'test-full' +Requires-Dist: fusepy; extra == 'test-full' +Requires-Dist: gcsfs; extra == 'test-full' +Requires-Dist: jinja2; extra == 'test-full' +Requires-Dist: kerchunk; extra == 'test-full' +Requires-Dist: libarchive-c; extra == 'test-full' +Requires-Dist: lz4; extra == 'test-full' +Requires-Dist: notebook; extra == 'test-full' +Requires-Dist: numpy; extra == 'test-full' +Requires-Dist: ocifs; extra == 'test-full' +Requires-Dist: pandas; extra == 'test-full' +Requires-Dist: panel; extra == 'test-full' +Requires-Dist: paramiko; extra == 'test-full' +Requires-Dist: pyarrow; extra == 'test-full' +Requires-Dist: pyarrow>=1; extra == 'test-full' +Requires-Dist: pyftpdlib; extra == 'test-full' +Requires-Dist: pygit2; extra == 'test-full' +Requires-Dist: pytest; extra == 'test-full' +Requires-Dist: pytest-asyncio!=0.22.0; extra == 'test-full' +Requires-Dist: pytest-benchmark; extra == 'test-full' +Requires-Dist: pytest-cov; extra == 'test-full' +Requires-Dist: pytest-mock; extra == 'test-full' +Requires-Dist: pytest-recording; extra == 'test-full' +Requires-Dist: pytest-rerunfailures; extra == 'test-full' +Requires-Dist: python-snappy; extra == 'test-full' +Requires-Dist: requests; extra == 'test-full' +Requires-Dist: smbprotocol; extra == 'test-full' +Requires-Dist: tqdm; extra == 'test-full' +Requires-Dist: urllib3; extra == 'test-full' +Requires-Dist: zarr; extra == 'test-full' +Requires-Dist: zstandard; (python_version < '3.14') and extra == 'test-full' +Provides-Extra: tqdm +Requires-Dist: tqdm; extra == 'tqdm' +Description-Content-Type: text/markdown + +# filesystem_spec + +[![PyPI version](https://badge.fury.io/py/fsspec.svg)](https://pypi.python.org/pypi/fsspec/) +[![Anaconda-Server Badge](https://anaconda.org/conda-forge/fsspec/badges/version.svg)](https://anaconda.org/conda-forge/fsspec) +![Build](https://github.com/fsspec/filesystem_spec/workflows/CI/badge.svg) +[![Docs](https://readthedocs.org/projects/filesystem-spec/badge/?version=latest)](https://filesystem-spec.readthedocs.io/en/latest/?badge=latest) + +A specification for pythonic filesystems. + +## Install + +```bash +pip install fsspec +``` + +would install the base fsspec. Various optionally supported features might require specification of custom +extra require, e.g. `pip install fsspec[ssh]` will install dependencies for `ssh` backends support. +Use `pip install fsspec[full]` for installation of all known extra dependencies. + +Up-to-date package also provided through conda-forge distribution: + +```bash +conda install -c conda-forge fsspec +``` + + +## Purpose + +To produce a template or specification for a file-system interface, that specific implementations should follow, +so that applications making use of them can rely on a common behaviour and not have to worry about the specific +internal implementation decisions with any given backend. Many such implementations are included in this package, +or in sister projects such as `s3fs` and `gcsfs`. + +In addition, if this is well-designed, then additional functionality, such as a key-value store or FUSE +mounting of the file-system implementation may be available for all implementations "for free". + +## Documentation + +Please refer to [RTD](https://filesystem-spec.readthedocs.io/en/latest/?badge=latest) + +## Develop + +fsspec uses GitHub Actions for CI. Environment files can be found +in the "ci/" directory. Note that the main environment is called "py38", +but it is expected that the version of python installed be adjustable at +CI runtime. For local use, pick a version suitable for you. + +```bash +# For a new environment (mamba / conda). +mamba create -n fsspec -c conda-forge python=3.9 -y +conda activate fsspec + +# Standard dev install with docs and tests. +pip install -e ".[dev,doc,test]" + +# Full tests except for downstream +pip install s3fs +pip uninstall s3fs +pip install -e .[dev,doc,test_full] +pip install s3fs --no-deps +pytest -v + +# Downstream tests. +sh install_s3fs.sh +# Windows powershell. +install_s3fs.sh +``` + +### Testing + +Tests can be run in the dev environment, if activated, via ``pytest fsspec``. + +The full fsspec suite requires a system-level docker, docker-compose, and fuse +installation. If only making changes to one backend implementation, it is +not generally necessary to run all tests locally. + +It is expected that contributors ensure that any change to fsspec does not +cause issues or regressions for either other fsspec-related packages such +as gcsfs and s3fs, nor for downstream users of fsspec. The "downstream" CI +run and corresponding environment file run a set of tests from the dask +test suite, and very minimal tests against pandas and zarr from the +test_downstream.py module in this repo. + +### Code Formatting + +fsspec uses [Black](https://black.readthedocs.io/en/stable) to ensure +a consistent code format throughout the project. +Run ``black fsspec`` from the root of the filesystem_spec repository to +auto-format your code. Additionally, many editors have plugins that will apply +``black`` as you edit files. ``black`` is included in the ``tox`` environments. + +Optionally, you may wish to setup [pre-commit hooks](https://pre-commit.com) to +automatically run ``black`` when you make a git commit. +Run ``pre-commit install --install-hooks`` from the root of the +filesystem_spec repository to setup pre-commit hooks. ``black`` will now be run +before you commit, reformatting any changed files. You can format without +committing via ``pre-commit run`` or skip these checks with ``git commit +--no-verify``. + +## Support + +Work on this repository is supported in part by: + +"Anaconda, Inc. - Advancing AI through open source." + +anaconda logo diff --git a/.venv/lib/python3.12/site-packages/fsspec-2025.9.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/fsspec-2025.9.0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..332a9e835e4fe62cb09ab4a85d2aaf9f0facf98e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec-2025.9.0.dist-info/RECORD @@ -0,0 +1,62 @@ +fsspec-2025.9.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +fsspec-2025.9.0.dist-info/METADATA,sha256=VmMoGluoRhQXlQigYs9kzwlXfPIg1KBkRL7V2F5O2B0,10397 +fsspec-2025.9.0.dist-info/RECORD,, +fsspec-2025.9.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +fsspec-2025.9.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87 +fsspec-2025.9.0.dist-info/licenses/LICENSE,sha256=LcNUls5TpzB5FcAIqESq1T53K0mzTN0ARFBnaRQH7JQ,1513 +fsspec/__init__.py,sha256=L7qwNBU1iMNQd8Of87HYSNFT9gWlNMSESaJC8fY0AaQ,2053 +fsspec/_version.py,sha256=LkyV4dUHpfGx8N-SvSE0eARRvdCyg8wWXOl3qM2ZAZ4,710 +fsspec/archive.py,sha256=vM6t_lgV6lBWbBYwpm3S4ofBQFQxUPr5KkDQrrQcQro,2411 +fsspec/asyn.py,sha256=mE55tO_MmGcxD14cUuaiS3veAqo0h6ZqANfnUuCN3sk,36365 +fsspec/caching.py,sha256=86uSgPa5E55b28XEhuC-dMcKAxJtZZnpQqnHTwaF3hI,34294 +fsspec/callbacks.py,sha256=BDIwLzK6rr_0V5ch557fSzsivCElpdqhXr5dZ9Te-EE,9210 +fsspec/compression.py,sha256=gBK2MV_oTFVW2XDq8bZVbYQKYrl6JDUou6_-kyvmxuk,5086 +fsspec/config.py,sha256=LF4Zmu1vhJW7Je9Q-cwkRc3xP7Rhyy7Xnwj26Z6sv2g,4279 +fsspec/conftest.py,sha256=fVfx-NLrH_OZS1TIpYNoPzM7efEcMoL62reHOdYeFCA,1245 +fsspec/core.py,sha256=1tLctwr7sF1VO3djc_UkjhJ8IAEy0TUMH_bb07Sw17E,23828 +fsspec/dircache.py,sha256=YzogWJrhEastHU7vWz-cJiJ7sdtLXFXhEpInGKd4EcM,2717 +fsspec/exceptions.py,sha256=pauSLDMxzTJMOjvX1WEUK0cMyFkrFxpWJsyFywav7A8,331 +fsspec/fuse.py,sha256=Q-3NOOyLqBfYa4Db5E19z_ZY36zzYHtIs1mOUasItBQ,10177 +fsspec/generic.py,sha256=K-b03ifKidHUo99r8nz2pB6oGyf88RtTKahCuBF9ZVU,13409 +fsspec/gui.py,sha256=CQ7QsrTpaDlWSLNOpwNoJc7khOcYXIZxmrAJN9bHWQU,14002 +fsspec/implementations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +fsspec/implementations/arrow.py,sha256=721Dikne_lV_0tlgk9jyKmHL6W-5MT0h2LKGvOYQTPI,8623 +fsspec/implementations/asyn_wrapper.py,sha256=fox9yjsEu7NCgzdAZJYfNALtUnFkIc_QmeKzaSllZho,3679 +fsspec/implementations/cache_mapper.py,sha256=W4wlxyPxZbSp9ItJ0pYRVBMh6bw9eFypgP6kUYuuiI4,2421 +fsspec/implementations/cache_metadata.py,sha256=rddh5-0SXIeyWCPpBpOFcaAyWoPyeYmFfeubEWt-nRM,8536 +fsspec/implementations/cached.py,sha256=TETvCyf0x-Ak8Y4uiuvIKx2IFYOzvcV0LMUIt4AoJzM,35168 +fsspec/implementations/dask.py,sha256=CXZbJzIVOhKV8ILcxuy3bTvcacCueAbyQxmvAkbPkrk,4466 +fsspec/implementations/data.py,sha256=LDLczxRh8h7x39Zjrd-GgzdQHr78yYxDlrv2C9Uxb5E,1658 +fsspec/implementations/dbfs.py,sha256=1cvvC6KBWOb8pBVpc01xavVbEPXO1xsgZvPD7H73M9k,16217 +fsspec/implementations/dirfs.py,sha256=f1sGnQ9Vf0xTxrXo4jDeBy4Qfq3RTqAEemqBSeb0hwY,12108 +fsspec/implementations/ftp.py,sha256=bzL_TgH77nMMtTMewRGkbq4iObSHGu7YoMRCXBH4nrc,11639 +fsspec/implementations/gist.py,sha256=Ost985hmFr50KsA-QD0shY3hP4KX5qJ9rb5C-X4ehK8,8341 +fsspec/implementations/git.py,sha256=qBDWMz5LNllPqVjr5jf_1FuNha4P5lyQI3IlhYg-wUE,3731 +fsspec/implementations/github.py,sha256=aCsZL8UvXZgdkcB1RUs3DdLeNrjLKcFsFYeQFDWbBFo,11653 +fsspec/implementations/http.py,sha256=f_542L40574onk0tiOsSBABeNq1TXHYUyEBpCeO6vhA,30435 +fsspec/implementations/http_sync.py,sha256=UydDqSdUBdhiJ1KufzV8rKGrTftFR4QmNV0safILb8g,30133 +fsspec/implementations/jupyter.py,sha256=B2uj7OEm7yIk-vRSsO37_ND0t0EBvn4B-Su43ibN4Pg,3811 +fsspec/implementations/libarchive.py,sha256=5_I2DiLXwQ1JC8x-K7jXu-tBwhO9dj7tFLnb0bTnVMQ,7102 +fsspec/implementations/local.py,sha256=DQeK7jRGv4_mJAweLKALO5WzIIkjXxZ_jRvwQ_xadSA,16936 +fsspec/implementations/memory.py,sha256=Kc6TZSbZ4tdi-6cE5ttEPIgMyq9aAt6cDdVLFRTJvf8,10488 +fsspec/implementations/reference.py,sha256=npYj49AmR8rmON9t_BLpfEXqhgsardUeynamqyraOXo,48704 +fsspec/implementations/sftp.py,sha256=fMY9XZcmpjszQ2tCqO_TPaJesaeD_Dv7ptYzgUPGoO0,5631 +fsspec/implementations/smb.py,sha256=5fhu8h06nOLBPh2c48aT7WBRqh9cEcbIwtyu06wTjec,15236 +fsspec/implementations/tar.py,sha256=dam78Tp_CozybNqCY2JYgGBS3Uc9FuJUAT9oB0lolOs,4111 +fsspec/implementations/webhdfs.py,sha256=G9wGywj7BkZk4Mu9zXu6HaDlEqX4F8Gw1i4k46CP_-o,16769 +fsspec/implementations/zip.py,sha256=9LBMHPft2OutJl2Ft-r9u_z3GptLkc2n91ur2A3bCbg,6072 +fsspec/json.py,sha256=3BfNSQ96MB4Xao_ocjheINeqZM2ev7oljUzR5XmNXrE,3814 +fsspec/mapping.py,sha256=m2ndB_gtRBXYmNJg0Ie1-BVR75TFleHmIQBzC-yWhjU,8343 +fsspec/parquet.py,sha256=6ibAmG527L5JNFS0VO8BDNlxHdA3bVYqdByeiFgpUVM,19448 +fsspec/registry.py,sha256=epoYryFFzDWjbkQJfh6xkF3nEu8RTiOzV3-voi8Pshs,12048 +fsspec/spec.py,sha256=7cOUe5PC5Uyf56HtGBUHEoym8ktPj-BI8G4HR8Xd_C8,77298 +fsspec/tests/abstract/__init__.py,sha256=4xUJrv7gDgc85xAOz1p-V_K1hrsdMWTSa0rviALlJk8,10181 +fsspec/tests/abstract/common.py,sha256=1GQwNo5AONzAnzZj0fWgn8NJPLXALehbsuGxS3FzWVU,4973 +fsspec/tests/abstract/copy.py,sha256=gU5-d97U3RSde35Vp4RxPY4rWwL744HiSrJ8IBOp9-8,19967 +fsspec/tests/abstract/get.py,sha256=vNR4HztvTR7Cj56AMo7_tx7TeYz1Jgr_2Wb8Lv-UiBY,20755 +fsspec/tests/abstract/mv.py,sha256=k8eUEBIrRrGMsBY5OOaDXdGnQUKGwDIfQyduB6YD3Ns,1982 +fsspec/tests/abstract/open.py,sha256=Fi2PBPYLbRqysF8cFm0rwnB41kMdQVYjq8cGyDXp3BU,329 +fsspec/tests/abstract/pipe.py,sha256=LFzIrLCB5GLXf9rzFKJmE8AdG7LQ_h4bJo70r8FLPqM,402 +fsspec/tests/abstract/put.py,sha256=7aih17OKB_IZZh1Mkq1eBDIjobhtMQmI8x-Pw-S_aZk,21201 +fsspec/transaction.py,sha256=xliRG6U2Zf3khG4xcw9WiB-yAoqJSHEGK_VjHOdtgo0,2398 +fsspec/utils.py,sha256=HC8RFbb7KpEDedsYxExvWvsTObEuUcuuWxd0B_MyGpo,22995 diff --git a/.venv/lib/python3.12/site-packages/fsspec-2025.9.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/fsspec-2025.9.0.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/fsspec-2025.9.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/fsspec-2025.9.0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..12228d414b6cfed7c39d3781c85c63256a1d7fb5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec-2025.9.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.27.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/fsspec/__init__.py b/.venv/lib/python3.12/site-packages/fsspec/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..452c78a055e72a6d04f1013d1a98fda33fdc449e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec/__init__.py @@ -0,0 +1,71 @@ +from . import caching +from ._version import __version__ # noqa: F401 +from .callbacks import Callback +from .compression import available_compressions +from .core import get_fs_token_paths, open, open_files, open_local, url_to_fs +from .exceptions import FSTimeoutError +from .mapping import FSMap, get_mapper +from .registry import ( + available_protocols, + filesystem, + get_filesystem_class, + register_implementation, + registry, +) +from .spec import AbstractFileSystem + +__all__ = [ + "AbstractFileSystem", + "FSTimeoutError", + "FSMap", + "filesystem", + "register_implementation", + "get_filesystem_class", + "get_fs_token_paths", + "get_mapper", + "open", + "open_files", + "open_local", + "registry", + "caching", + "Callback", + "available_protocols", + "available_compressions", + "url_to_fs", +] + + +def process_entries(): + try: + from importlib.metadata import entry_points + except ImportError: + return + if entry_points is not None: + try: + eps = entry_points() + except TypeError: + pass # importlib-metadata < 0.8 + else: + if hasattr(eps, "select"): # Python 3.10+ / importlib_metadata >= 3.9.0 + specs = eps.select(group="fsspec.specs") + else: + specs = eps.get("fsspec.specs", []) + registered_names = {} + for spec in specs: + err_msg = f"Unable to load filesystem from {spec}" + name = spec.name + if name in registered_names: + continue + registered_names[name] = True + register_implementation( + name, + spec.value.replace(":", "."), + errtxt=err_msg, + # We take our implementations as the ones to overload with if + # for some reason we encounter some, may be the same, already + # registered + clobber=True, + ) + + +process_entries() diff --git a/.venv/lib/python3.12/site-packages/fsspec/_version.py b/.venv/lib/python3.12/site-packages/fsspec/_version.py new file mode 100644 index 0000000000000000000000000000000000000000..668eaa38c6505a9898469a23ff6c19dd63947148 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec/_version.py @@ -0,0 +1,34 @@ +# file generated by setuptools-scm +# don't change, don't track in version control + +__all__ = [ + "__version__", + "__version_tuple__", + "version", + "version_tuple", + "__commit_id__", + "commit_id", +] + +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import Tuple + from typing import Union + + VERSION_TUPLE = Tuple[Union[int, str], ...] + COMMIT_ID = Union[str, None] +else: + VERSION_TUPLE = object + COMMIT_ID = object + +version: str +__version__: str +__version_tuple__: VERSION_TUPLE +version_tuple: VERSION_TUPLE +commit_id: COMMIT_ID +__commit_id__: COMMIT_ID + +__version__ = version = '2025.9.0' +__version_tuple__ = version_tuple = (2025, 9, 0) + +__commit_id__ = commit_id = None diff --git a/.venv/lib/python3.12/site-packages/fsspec/archive.py b/.venv/lib/python3.12/site-packages/fsspec/archive.py new file mode 100644 index 0000000000000000000000000000000000000000..13a4da8df7c9405297cdd7d37476be2f725b2f57 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec/archive.py @@ -0,0 +1,75 @@ +import operator + +from fsspec import AbstractFileSystem +from fsspec.utils import tokenize + + +class AbstractArchiveFileSystem(AbstractFileSystem): + """ + A generic superclass for implementing Archive-based filesystems. + + Currently, it is shared amongst + :class:`~fsspec.implementations.zip.ZipFileSystem`, + :class:`~fsspec.implementations.libarchive.LibArchiveFileSystem` and + :class:`~fsspec.implementations.tar.TarFileSystem`. + """ + + def __str__(self): + return f"" + + __repr__ = __str__ + + def ukey(self, path): + return tokenize(path, self.fo, self.protocol) + + def _all_dirnames(self, paths): + """Returns *all* directory names for each path in paths, including intermediate + ones. + + Parameters + ---------- + paths: Iterable of path strings + """ + if len(paths) == 0: + return set() + + dirnames = {self._parent(path) for path in paths} - {self.root_marker} + return dirnames | self._all_dirnames(dirnames) + + def info(self, path, **kwargs): + self._get_dirs() + path = self._strip_protocol(path) + if path in {"", "/"} and self.dir_cache: + return {"name": "", "type": "directory", "size": 0} + if path in self.dir_cache: + return self.dir_cache[path] + elif path + "/" in self.dir_cache: + return self.dir_cache[path + "/"] + else: + raise FileNotFoundError(path) + + def ls(self, path, detail=True, **kwargs): + self._get_dirs() + paths = {} + for p, f in self.dir_cache.items(): + p = p.rstrip("/") + if "/" in p: + root = p.rsplit("/", 1)[0] + else: + root = "" + if root == path.rstrip("/"): + paths[p] = f + elif all( + (a == b) + for a, b in zip(path.split("/"), [""] + p.strip("/").split("/")) + ): + # root directory entry + ppath = p.rstrip("/").split("/", 1)[0] + if ppath not in paths: + out = {"name": ppath, "size": 0, "type": "directory"} + paths[ppath] = out + if detail: + out = sorted(paths.values(), key=operator.itemgetter("name")) + return out + else: + return sorted(paths) diff --git a/.venv/lib/python3.12/site-packages/fsspec/asyn.py b/.venv/lib/python3.12/site-packages/fsspec/asyn.py new file mode 100644 index 0000000000000000000000000000000000000000..83772839450ec74a8fb37f9be323341f948e3748 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec/asyn.py @@ -0,0 +1,1097 @@ +import asyncio +import asyncio.events +import functools +import inspect +import io +import numbers +import os +import re +import threading +from collections.abc import Iterable +from glob import has_magic +from typing import TYPE_CHECKING + +from .callbacks import DEFAULT_CALLBACK +from .exceptions import FSTimeoutError +from .implementations.local import LocalFileSystem, make_path_posix, trailing_sep +from .spec import AbstractBufferedFile, AbstractFileSystem +from .utils import glob_translate, is_exception, other_paths + +private = re.compile("_[^_]") +iothread = [None] # dedicated fsspec IO thread +loop = [None] # global event loop for any non-async instance +_lock = None # global lock placeholder +get_running_loop = asyncio.get_running_loop + + +def get_lock(): + """Allocate or return a threading lock. + + The lock is allocated on first use to allow setting one lock per forked process. + """ + global _lock + if not _lock: + _lock = threading.Lock() + return _lock + + +def reset_lock(): + """Reset the global lock. + + This should be called only on the init of a forked process to reset the lock to + None, enabling the new forked process to get a new lock. + """ + global _lock + + iothread[0] = None + loop[0] = None + _lock = None + + +async def _runner(event, coro, result, timeout=None): + timeout = timeout if timeout else None # convert 0 or 0.0 to None + if timeout is not None: + coro = asyncio.wait_for(coro, timeout=timeout) + try: + result[0] = await coro + except Exception as ex: + result[0] = ex + finally: + event.set() + + +def sync(loop, func, *args, timeout=None, **kwargs): + """ + Make loop run coroutine until it returns. Runs in other thread + + Examples + -------- + >>> fsspec.asyn.sync(fsspec.asyn.get_loop(), func, *args, + timeout=timeout, **kwargs) + """ + timeout = timeout if timeout else None # convert 0 or 0.0 to None + # NB: if the loop is not running *yet*, it is OK to submit work + # and we will wait for it + if loop is None or loop.is_closed(): + raise RuntimeError("Loop is not running") + try: + loop0 = asyncio.events.get_running_loop() + if loop0 is loop: + raise NotImplementedError("Calling sync() from within a running loop") + except NotImplementedError: + raise + except RuntimeError: + pass + coro = func(*args, **kwargs) + result = [None] + event = threading.Event() + asyncio.run_coroutine_threadsafe(_runner(event, coro, result, timeout), loop) + while True: + # this loops allows thread to get interrupted + if event.wait(1): + break + if timeout is not None: + timeout -= 1 + if timeout < 0: + raise FSTimeoutError + + return_result = result[0] + if isinstance(return_result, asyncio.TimeoutError): + # suppress asyncio.TimeoutError, raise FSTimeoutError + raise FSTimeoutError from return_result + elif isinstance(return_result, BaseException): + raise return_result + else: + return return_result + + +def sync_wrapper(func, obj=None): + """Given a function, make so can be called in blocking contexts + + Leave obj=None if defining within a class. Pass the instance if attaching + as an attribute of the instance. + """ + + @functools.wraps(func) + def wrapper(*args, **kwargs): + self = obj or args[0] + return sync(self.loop, func, *args, **kwargs) + + return wrapper + + +def get_loop(): + """Create or return the default fsspec IO loop + + The loop will be running on a separate thread. + """ + if loop[0] is None: + with get_lock(): + # repeat the check just in case the loop got filled between the + # previous two calls from another thread + if loop[0] is None: + loop[0] = asyncio.new_event_loop() + th = threading.Thread(target=loop[0].run_forever, name="fsspecIO") + th.daemon = True + th.start() + iothread[0] = th + return loop[0] + + +def reset_after_fork(): + global lock + loop[0] = None + iothread[0] = None + lock = None + + +if hasattr(os, "register_at_fork"): + # should be posix; this will do nothing for spawn or forkserver subprocesses + os.register_at_fork(after_in_child=reset_after_fork) + + +if TYPE_CHECKING: + import resource + + ResourceError = resource.error +else: + try: + import resource + except ImportError: + resource = None + ResourceError = OSError + else: + ResourceError = getattr(resource, "error", OSError) + +_DEFAULT_BATCH_SIZE = 128 +_NOFILES_DEFAULT_BATCH_SIZE = 1280 + + +def _get_batch_size(nofiles=False): + from fsspec.config import conf + + if nofiles: + if "nofiles_gather_batch_size" in conf: + return conf["nofiles_gather_batch_size"] + else: + if "gather_batch_size" in conf: + return conf["gather_batch_size"] + if nofiles: + return _NOFILES_DEFAULT_BATCH_SIZE + if resource is None: + return _DEFAULT_BATCH_SIZE + + try: + soft_limit, _ = resource.getrlimit(resource.RLIMIT_NOFILE) + except (ImportError, ValueError, ResourceError): + return _DEFAULT_BATCH_SIZE + + if soft_limit == resource.RLIM_INFINITY: + return -1 + else: + return soft_limit // 8 + + +def running_async() -> bool: + """Being executed by an event loop?""" + try: + asyncio.get_running_loop() + return True + except RuntimeError: + return False + + +async def _run_coros_in_chunks( + coros, + batch_size=None, + callback=DEFAULT_CALLBACK, + timeout=None, + return_exceptions=False, + nofiles=False, +): + """Run the given coroutines in chunks. + + Parameters + ---------- + coros: list of coroutines to run + batch_size: int or None + Number of coroutines to submit/wait on simultaneously. + If -1, then it will not be any throttling. If + None, it will be inferred from _get_batch_size() + callback: fsspec.callbacks.Callback instance + Gets a relative_update when each coroutine completes + timeout: number or None + If given, each coroutine times out after this time. Note that, since + there are multiple batches, the total run time of this function will in + general be longer + return_exceptions: bool + Same meaning as in asyncio.gather + nofiles: bool + If inferring the batch_size, does this operation involve local files? + If yes, you normally expect smaller batches. + """ + + if batch_size is None: + batch_size = _get_batch_size(nofiles=nofiles) + + if batch_size == -1: + batch_size = len(coros) + + assert batch_size > 0 + + async def _run_coro(coro, i): + try: + return await asyncio.wait_for(coro, timeout=timeout), i + except Exception as e: + if not return_exceptions: + raise + return e, i + finally: + callback.relative_update(1) + + i = 0 + n = len(coros) + results = [None] * n + pending = set() + + while pending or i < n: + while len(pending) < batch_size and i < n: + pending.add(asyncio.ensure_future(_run_coro(coros[i], i))) + i += 1 + + if not pending: + break + + done, pending = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED) + while done: + result, k = await done.pop() + results[k] = result + + return results + + +# these methods should be implemented as async by any async-able backend +async_methods = [ + "_ls", + "_cat_file", + "_get_file", + "_put_file", + "_rm_file", + "_cp_file", + "_pipe_file", + "_expand_path", + "_info", + "_isfile", + "_isdir", + "_exists", + "_walk", + "_glob", + "_find", + "_du", + "_size", + "_mkdir", + "_makedirs", +] + + +class AsyncFileSystem(AbstractFileSystem): + """Async file operations, default implementations + + Passes bulk operations to asyncio.gather for concurrent operation. + + Implementations that have concurrent batch operations and/or async methods + should inherit from this class instead of AbstractFileSystem. Docstrings are + copied from the un-underscored method in AbstractFileSystem, if not given. + """ + + # note that methods do not have docstring here; they will be copied + # for _* methods and inferred for overridden methods. + + async_impl = True + mirror_sync_methods = True + disable_throttling = False + + def __init__(self, *args, asynchronous=False, loop=None, batch_size=None, **kwargs): + self.asynchronous = asynchronous + self._pid = os.getpid() + if not asynchronous: + self._loop = loop or get_loop() + else: + self._loop = None + self.batch_size = batch_size + super().__init__(*args, **kwargs) + + @property + def loop(self): + if self._pid != os.getpid(): + raise RuntimeError("This class is not fork-safe") + return self._loop + + async def _rm_file(self, path, **kwargs): + raise NotImplementedError + + async def _rm(self, path, recursive=False, batch_size=None, **kwargs): + # TODO: implement on_error + batch_size = batch_size or self.batch_size + path = await self._expand_path(path, recursive=recursive) + return await _run_coros_in_chunks( + [self._rm_file(p, **kwargs) for p in reversed(path)], + batch_size=batch_size, + nofiles=True, + ) + + async def _cp_file(self, path1, path2, **kwargs): + raise NotImplementedError + + async def _mv_file(self, path1, path2): + await self._cp_file(path1, path2) + await self._rm_file(path1) + + async def _copy( + self, + path1, + path2, + recursive=False, + on_error=None, + maxdepth=None, + batch_size=None, + **kwargs, + ): + if on_error is None and recursive: + on_error = "ignore" + elif on_error is None: + on_error = "raise" + + if isinstance(path1, list) and isinstance(path2, list): + # No need to expand paths when both source and destination + # are provided as lists + paths1 = path1 + paths2 = path2 + else: + source_is_str = isinstance(path1, str) + paths1 = await self._expand_path( + path1, maxdepth=maxdepth, recursive=recursive + ) + if source_is_str and (not recursive or maxdepth is not None): + # Non-recursive glob does not copy directories + paths1 = [ + p for p in paths1 if not (trailing_sep(p) or await self._isdir(p)) + ] + if not paths1: + return + + source_is_file = len(paths1) == 1 + dest_is_dir = isinstance(path2, str) and ( + trailing_sep(path2) or await self._isdir(path2) + ) + + exists = source_is_str and ( + (has_magic(path1) and source_is_file) + or (not has_magic(path1) and dest_is_dir and not trailing_sep(path1)) + ) + paths2 = other_paths( + paths1, + path2, + exists=exists, + flatten=not source_is_str, + ) + + batch_size = batch_size or self.batch_size + coros = [self._cp_file(p1, p2, **kwargs) for p1, p2 in zip(paths1, paths2)] + result = await _run_coros_in_chunks( + coros, batch_size=batch_size, return_exceptions=True, nofiles=True + ) + + for ex in filter(is_exception, result): + if on_error == "ignore" and isinstance(ex, FileNotFoundError): + continue + raise ex + + async def _pipe_file(self, path, value, mode="overwrite", **kwargs): + raise NotImplementedError + + async def _pipe(self, path, value=None, batch_size=None, **kwargs): + if isinstance(path, str): + path = {path: value} + batch_size = batch_size or self.batch_size + return await _run_coros_in_chunks( + [self._pipe_file(k, v, **kwargs) for k, v in path.items()], + batch_size=batch_size, + nofiles=True, + ) + + async def _process_limits(self, url, start, end): + """Helper for "Range"-based _cat_file""" + size = None + suff = False + if start is not None and start < 0: + # if start is negative and end None, end is the "suffix length" + if end is None: + end = -start + start = "" + suff = True + else: + size = size or (await self._info(url))["size"] + start = size + start + elif start is None: + start = 0 + if not suff: + if end is not None and end < 0: + if start is not None: + size = size or (await self._info(url))["size"] + end = size + end + elif end is None: + end = "" + if isinstance(end, numbers.Integral): + end -= 1 # bytes range is inclusive + return f"bytes={start}-{end}" + + async def _cat_file(self, path, start=None, end=None, **kwargs): + raise NotImplementedError + + async def _cat( + self, path, recursive=False, on_error="raise", batch_size=None, **kwargs + ): + paths = await self._expand_path(path, recursive=recursive) + coros = [self._cat_file(path, **kwargs) for path in paths] + batch_size = batch_size or self.batch_size + out = await _run_coros_in_chunks( + coros, batch_size=batch_size, nofiles=True, return_exceptions=True + ) + if on_error == "raise": + ex = next(filter(is_exception, out), False) + if ex: + raise ex + if ( + len(paths) > 1 + or isinstance(path, list) + or paths[0] != self._strip_protocol(path) + ): + return { + k: v + for k, v in zip(paths, out) + if on_error != "omit" or not is_exception(v) + } + else: + return out[0] + + async def _cat_ranges( + self, + paths, + starts, + ends, + max_gap=None, + batch_size=None, + on_error="return", + **kwargs, + ): + """Get the contents of byte ranges from one or more files + + Parameters + ---------- + paths: list + A list of of filepaths on this filesystems + starts, ends: int or list + Bytes limits of the read. If using a single int, the same value will be + used to read all the specified files. + """ + # TODO: on_error + if max_gap is not None: + # use utils.merge_offset_ranges + raise NotImplementedError + if not isinstance(paths, list): + raise TypeError + if not isinstance(starts, Iterable): + starts = [starts] * len(paths) + if not isinstance(ends, Iterable): + ends = [ends] * len(paths) + if len(starts) != len(paths) or len(ends) != len(paths): + raise ValueError + coros = [ + self._cat_file(p, start=s, end=e, **kwargs) + for p, s, e in zip(paths, starts, ends) + ] + batch_size = batch_size or self.batch_size + return await _run_coros_in_chunks( + coros, batch_size=batch_size, nofiles=True, return_exceptions=True + ) + + async def _put_file(self, lpath, rpath, mode="overwrite", **kwargs): + raise NotImplementedError + + async def _put( + self, + lpath, + rpath, + recursive=False, + callback=DEFAULT_CALLBACK, + batch_size=None, + maxdepth=None, + **kwargs, + ): + """Copy file(s) from local. + + Copies a specific file or tree of files (if recursive=True). If rpath + ends with a "/", it will be assumed to be a directory, and target files + will go within. + + The put_file method will be called concurrently on a batch of files. The + batch_size option can configure the amount of futures that can be executed + at the same time. If it is -1, then all the files will be uploaded concurrently. + The default can be set for this instance by passing "batch_size" in the + constructor, or for all instances by setting the "gather_batch_size" key + in ``fsspec.config.conf``, falling back to 1/8th of the system limit . + """ + if isinstance(lpath, list) and isinstance(rpath, list): + # No need to expand paths when both source and destination + # are provided as lists + rpaths = rpath + lpaths = lpath + else: + source_is_str = isinstance(lpath, str) + if source_is_str: + lpath = make_path_posix(lpath) + fs = LocalFileSystem() + lpaths = fs.expand_path(lpath, recursive=recursive, maxdepth=maxdepth) + if source_is_str and (not recursive or maxdepth is not None): + # Non-recursive glob does not copy directories + lpaths = [p for p in lpaths if not (trailing_sep(p) or fs.isdir(p))] + if not lpaths: + return + + source_is_file = len(lpaths) == 1 + dest_is_dir = isinstance(rpath, str) and ( + trailing_sep(rpath) or await self._isdir(rpath) + ) + + rpath = self._strip_protocol(rpath) + exists = source_is_str and ( + (has_magic(lpath) and source_is_file) + or (not has_magic(lpath) and dest_is_dir and not trailing_sep(lpath)) + ) + rpaths = other_paths( + lpaths, + rpath, + exists=exists, + flatten=not source_is_str, + ) + + is_dir = {l: os.path.isdir(l) for l in lpaths} + rdirs = [r for l, r in zip(lpaths, rpaths) if is_dir[l]] + file_pairs = [(l, r) for l, r in zip(lpaths, rpaths) if not is_dir[l]] + + await asyncio.gather(*[self._makedirs(d, exist_ok=True) for d in rdirs]) + batch_size = batch_size or self.batch_size + + coros = [] + callback.set_size(len(file_pairs)) + for lfile, rfile in file_pairs: + put_file = callback.branch_coro(self._put_file) + coros.append(put_file(lfile, rfile, **kwargs)) + + return await _run_coros_in_chunks( + coros, batch_size=batch_size, callback=callback + ) + + async def _get_file(self, rpath, lpath, **kwargs): + raise NotImplementedError + + async def _get( + self, + rpath, + lpath, + recursive=False, + callback=DEFAULT_CALLBACK, + maxdepth=None, + **kwargs, + ): + """Copy file(s) to local. + + Copies a specific file or tree of files (if recursive=True). If lpath + ends with a "/", it will be assumed to be a directory, and target files + will go within. Can submit a list of paths, which may be glob-patterns + and will be expanded. + + The get_file method will be called concurrently on a batch of files. The + batch_size option can configure the amount of futures that can be executed + at the same time. If it is -1, then all the files will be uploaded concurrently. + The default can be set for this instance by passing "batch_size" in the + constructor, or for all instances by setting the "gather_batch_size" key + in ``fsspec.config.conf``, falling back to 1/8th of the system limit . + """ + if isinstance(lpath, list) and isinstance(rpath, list): + # No need to expand paths when both source and destination + # are provided as lists + rpaths = rpath + lpaths = lpath + else: + source_is_str = isinstance(rpath, str) + # First check for rpath trailing slash as _strip_protocol removes it. + source_not_trailing_sep = source_is_str and not trailing_sep(rpath) + rpath = self._strip_protocol(rpath) + rpaths = await self._expand_path( + rpath, recursive=recursive, maxdepth=maxdepth + ) + if source_is_str and (not recursive or maxdepth is not None): + # Non-recursive glob does not copy directories + rpaths = [ + p for p in rpaths if not (trailing_sep(p) or await self._isdir(p)) + ] + if not rpaths: + return + + lpath = make_path_posix(lpath) + source_is_file = len(rpaths) == 1 + dest_is_dir = isinstance(lpath, str) and ( + trailing_sep(lpath) or LocalFileSystem().isdir(lpath) + ) + + exists = source_is_str and ( + (has_magic(rpath) and source_is_file) + or (not has_magic(rpath) and dest_is_dir and source_not_trailing_sep) + ) + lpaths = other_paths( + rpaths, + lpath, + exists=exists, + flatten=not source_is_str, + ) + + [os.makedirs(os.path.dirname(lp), exist_ok=True) for lp in lpaths] + batch_size = kwargs.pop("batch_size", self.batch_size) + + coros = [] + callback.set_size(len(lpaths)) + for lpath, rpath in zip(lpaths, rpaths): + get_file = callback.branch_coro(self._get_file) + coros.append(get_file(rpath, lpath, **kwargs)) + return await _run_coros_in_chunks( + coros, batch_size=batch_size, callback=callback + ) + + async def _isfile(self, path): + try: + return (await self._info(path))["type"] == "file" + except: # noqa: E722 + return False + + async def _isdir(self, path): + try: + return (await self._info(path))["type"] == "directory" + except OSError: + return False + + async def _size(self, path): + return (await self._info(path)).get("size", None) + + async def _sizes(self, paths, batch_size=None): + batch_size = batch_size or self.batch_size + return await _run_coros_in_chunks( + [self._size(p) for p in paths], batch_size=batch_size + ) + + async def _exists(self, path, **kwargs): + try: + await self._info(path, **kwargs) + return True + except FileNotFoundError: + return False + + async def _info(self, path, **kwargs): + raise NotImplementedError + + async def _ls(self, path, detail=True, **kwargs): + raise NotImplementedError + + async def _walk(self, path, maxdepth=None, on_error="omit", **kwargs): + if maxdepth is not None and maxdepth < 1: + raise ValueError("maxdepth must be at least 1") + + path = self._strip_protocol(path) + full_dirs = {} + dirs = {} + files = {} + + detail = kwargs.pop("detail", False) + try: + listing = await self._ls(path, detail=True, **kwargs) + except (FileNotFoundError, OSError) as e: + if on_error == "raise": + raise + elif callable(on_error): + on_error(e) + if detail: + yield path, {}, {} + else: + yield path, [], [] + return + + for info in listing: + # each info name must be at least [path]/part , but here + # we check also for names like [path]/part/ + pathname = info["name"].rstrip("/") + name = pathname.rsplit("/", 1)[-1] + if info["type"] == "directory" and pathname != path: + # do not include "self" path + full_dirs[name] = pathname + dirs[name] = info + elif pathname == path: + # file-like with same name as give path + files[""] = info + else: + files[name] = info + + if detail: + yield path, dirs, files + else: + yield path, list(dirs), list(files) + + if maxdepth is not None: + maxdepth -= 1 + if maxdepth < 1: + return + + for d in dirs: + async for _ in self._walk( + full_dirs[d], maxdepth=maxdepth, detail=detail, **kwargs + ): + yield _ + + async def _glob(self, path, maxdepth=None, **kwargs): + if maxdepth is not None and maxdepth < 1: + raise ValueError("maxdepth must be at least 1") + + import re + + seps = (os.path.sep, os.path.altsep) if os.path.altsep else (os.path.sep,) + ends_with_sep = path.endswith(seps) # _strip_protocol strips trailing slash + path = self._strip_protocol(path) + append_slash_to_dirname = ends_with_sep or path.endswith( + tuple(sep + "**" for sep in seps) + ) + idx_star = path.find("*") if path.find("*") >= 0 else len(path) + idx_qmark = path.find("?") if path.find("?") >= 0 else len(path) + idx_brace = path.find("[") if path.find("[") >= 0 else len(path) + + min_idx = min(idx_star, idx_qmark, idx_brace) + + detail = kwargs.pop("detail", False) + + if not has_magic(path): + if await self._exists(path, **kwargs): + if not detail: + return [path] + else: + return {path: await self._info(path, **kwargs)} + else: + if not detail: + return [] # glob of non-existent returns empty + else: + return {} + elif "/" in path[:min_idx]: + min_idx = path[:min_idx].rindex("/") + root = path[: min_idx + 1] + depth = path[min_idx + 1 :].count("/") + 1 + else: + root = "" + depth = path[min_idx + 1 :].count("/") + 1 + + if "**" in path: + if maxdepth is not None: + idx_double_stars = path.find("**") + depth_double_stars = path[idx_double_stars:].count("/") + 1 + depth = depth - depth_double_stars + maxdepth + else: + depth = None + + allpaths = await self._find( + root, maxdepth=depth, withdirs=True, detail=True, **kwargs + ) + + pattern = glob_translate(path + ("/" if ends_with_sep else "")) + pattern = re.compile(pattern) + + out = { + p: info + for p, info in sorted(allpaths.items()) + if pattern.match( + p + "/" + if append_slash_to_dirname and info["type"] == "directory" + else p + ) + } + + if detail: + return out + else: + return list(out) + + async def _du(self, path, total=True, maxdepth=None, **kwargs): + sizes = {} + # async for? + for f in await self._find(path, maxdepth=maxdepth, **kwargs): + info = await self._info(f) + sizes[info["name"]] = info["size"] + if total: + return sum(sizes.values()) + else: + return sizes + + async def _find(self, path, maxdepth=None, withdirs=False, **kwargs): + path = self._strip_protocol(path) + out = {} + detail = kwargs.pop("detail", False) + + # Add the root directory if withdirs is requested + # This is needed for posix glob compliance + if withdirs and path != "" and await self._isdir(path): + out[path] = await self._info(path) + + # async for? + async for _, dirs, files in self._walk(path, maxdepth, detail=True, **kwargs): + if withdirs: + files.update(dirs) + out.update({info["name"]: info for name, info in files.items()}) + if not out and (await self._isfile(path)): + # walk works on directories, but find should also return [path] + # when path happens to be a file + out[path] = {} + names = sorted(out) + if not detail: + return names + else: + return {name: out[name] for name in names} + + async def _expand_path(self, path, recursive=False, maxdepth=None): + if maxdepth is not None and maxdepth < 1: + raise ValueError("maxdepth must be at least 1") + + if isinstance(path, str): + out = await self._expand_path([path], recursive, maxdepth) + else: + out = set() + path = [self._strip_protocol(p) for p in path] + for p in path: # can gather here + if has_magic(p): + bit = set(await self._glob(p, maxdepth=maxdepth)) + out |= bit + if recursive: + # glob call above expanded one depth so if maxdepth is defined + # then decrement it in expand_path call below. If it is zero + # after decrementing then avoid expand_path call. + if maxdepth is not None and maxdepth <= 1: + continue + out |= set( + await self._expand_path( + list(bit), + recursive=recursive, + maxdepth=maxdepth - 1 if maxdepth is not None else None, + ) + ) + continue + elif recursive: + rec = set(await self._find(p, maxdepth=maxdepth, withdirs=True)) + out |= rec + if p not in out and (recursive is False or (await self._exists(p))): + # should only check once, for the root + out.add(p) + if not out: + raise FileNotFoundError(path) + return sorted(out) + + async def _mkdir(self, path, create_parents=True, **kwargs): + pass # not necessary to implement, may not have directories + + async def _makedirs(self, path, exist_ok=False): + pass # not necessary to implement, may not have directories + + async def open_async(self, path, mode="rb", **kwargs): + if "b" not in mode or kwargs.get("compression"): + raise ValueError + raise NotImplementedError + + +def mirror_sync_methods(obj): + """Populate sync and async methods for obj + + For each method will create a sync version if the name refers to an async method + (coroutine) and there is no override in the child class; will create an async + method for the corresponding sync method if there is no implementation. + + Uses the methods specified in + - async_methods: the set that an implementation is expected to provide + - default_async_methods: that can be derived from their sync version in + AbstractFileSystem + - AsyncFileSystem: async-specific default coroutines + """ + from fsspec import AbstractFileSystem + + for method in async_methods + dir(AsyncFileSystem): + if not method.startswith("_"): + continue + smethod = method[1:] + if private.match(method): + isco = inspect.iscoroutinefunction(getattr(obj, method, None)) + unsync = getattr(getattr(obj, smethod, False), "__func__", None) + is_default = unsync is getattr(AbstractFileSystem, smethod, "") + if isco and is_default: + mth = sync_wrapper(getattr(obj, method), obj=obj) + setattr(obj, smethod, mth) + if not mth.__doc__: + mth.__doc__ = getattr( + getattr(AbstractFileSystem, smethod, None), "__doc__", "" + ) + + +class FSSpecCoroutineCancel(Exception): + pass + + +def _dump_running_tasks( + printout=True, cancel=True, exc=FSSpecCoroutineCancel, with_task=False +): + import traceback + + tasks = [t for t in asyncio.tasks.all_tasks(loop[0]) if not t.done()] + if printout: + [task.print_stack() for task in tasks] + out = [ + { + "locals": task._coro.cr_frame.f_locals, + "file": task._coro.cr_frame.f_code.co_filename, + "firstline": task._coro.cr_frame.f_code.co_firstlineno, + "linelo": task._coro.cr_frame.f_lineno, + "stack": traceback.format_stack(task._coro.cr_frame), + "task": task if with_task else None, + } + for task in tasks + ] + if cancel: + for t in tasks: + cbs = t._callbacks + t.cancel() + asyncio.futures.Future.set_exception(t, exc) + asyncio.futures.Future.cancel(t) + [cb[0](t) for cb in cbs] # cancels any dependent concurrent.futures + try: + t._coro.throw(exc) # exits coro, unless explicitly handled + except exc: + pass + return out + + +class AbstractAsyncStreamedFile(AbstractBufferedFile): + # no read buffering, and always auto-commit + # TODO: readahead might still be useful here, but needs async version + + async def read(self, length=-1): + """ + Return data from cache, or fetch pieces as necessary + + Parameters + ---------- + length: int (-1) + Number of bytes to read; if <0, all remaining bytes. + """ + length = -1 if length is None else int(length) + if self.mode != "rb": + raise ValueError("File not in read mode") + if length < 0: + length = self.size - self.loc + if self.closed: + raise ValueError("I/O operation on closed file.") + if length == 0: + # don't even bother calling fetch + return b"" + out = await self._fetch_range(self.loc, self.loc + length) + self.loc += len(out) + return out + + async def write(self, data): + """ + Write data to buffer. + + Buffer only sent on flush() or if buffer is greater than + or equal to blocksize. + + Parameters + ---------- + data: bytes + Set of bytes to be written. + """ + if self.mode not in {"wb", "ab"}: + raise ValueError("File not in write mode") + if self.closed: + raise ValueError("I/O operation on closed file.") + if self.forced: + raise ValueError("This file has been force-flushed, can only close") + out = self.buffer.write(data) + self.loc += out + if self.buffer.tell() >= self.blocksize: + await self.flush() + return out + + async def close(self): + """Close file + + Finalizes writes, discards cache + """ + if getattr(self, "_unclosable", False): + return + if self.closed: + return + if self.mode == "rb": + self.cache = None + else: + if not self.forced: + await self.flush(force=True) + + if self.fs is not None: + self.fs.invalidate_cache(self.path) + self.fs.invalidate_cache(self.fs._parent(self.path)) + + self.closed = True + + async def flush(self, force=False): + if self.closed: + raise ValueError("Flush on closed file") + if force and self.forced: + raise ValueError("Force flush cannot be called more than once") + if force: + self.forced = True + + if self.mode not in {"wb", "ab"}: + # no-op to flush on read-mode + return + + if not force and self.buffer.tell() < self.blocksize: + # Defer write on small block + return + + if self.offset is None: + # Initialize a multipart upload + self.offset = 0 + try: + await self._initiate_upload() + except: + self.closed = True + raise + + if await self._upload_chunk(final=force) is not False: + self.offset += self.buffer.seek(0, 2) + self.buffer = io.BytesIO() + + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + await self.close() + + async def _fetch_range(self, start, end): + raise NotImplementedError + + async def _initiate_upload(self): + pass + + async def _upload_chunk(self, final=False): + raise NotImplementedError diff --git a/.venv/lib/python3.12/site-packages/fsspec/caching.py b/.venv/lib/python3.12/site-packages/fsspec/caching.py new file mode 100644 index 0000000000000000000000000000000000000000..de6a4e3407a62a1c2123bf2b4a81afb96c54150a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec/caching.py @@ -0,0 +1,1004 @@ +from __future__ import annotations + +import collections +import functools +import logging +import math +import os +import threading +import warnings +from collections import OrderedDict +from concurrent.futures import Future, ThreadPoolExecutor +from itertools import groupby +from operator import itemgetter +from typing import ( + TYPE_CHECKING, + Any, + Callable, + ClassVar, + Generic, + NamedTuple, + TypeVar, +) + +if TYPE_CHECKING: + import mmap + + from typing_extensions import ParamSpec + + P = ParamSpec("P") +else: + P = TypeVar("P") + +T = TypeVar("T") + + +logger = logging.getLogger("fsspec") + +Fetcher = Callable[[int, int], bytes] # Maps (start, end) to bytes +MultiFetcher = Callable[[list[int, int]], bytes] # Maps [(start, end)] to bytes + + +class BaseCache: + """Pass-though cache: doesn't keep anything, calls every time + + Acts as base class for other cachers + + Parameters + ---------- + blocksize: int + How far to read ahead in numbers of bytes + fetcher: func + Function of the form f(start, end) which gets bytes from remote as + specified + size: int + How big this file is + """ + + name: ClassVar[str] = "none" + + def __init__(self, blocksize: int, fetcher: Fetcher, size: int) -> None: + self.blocksize = blocksize + self.nblocks = 0 + self.fetcher = fetcher + self.size = size + self.hit_count = 0 + self.miss_count = 0 + # the bytes that we actually requested + self.total_requested_bytes = 0 + + def _fetch(self, start: int | None, stop: int | None) -> bytes: + if start is None: + start = 0 + if stop is None: + stop = self.size + if start >= self.size or start >= stop: + return b"" + return self.fetcher(start, stop) + + def _reset_stats(self) -> None: + """Reset hit and miss counts for a more ganular report e.g. by file.""" + self.hit_count = 0 + self.miss_count = 0 + self.total_requested_bytes = 0 + + def _log_stats(self) -> str: + """Return a formatted string of the cache statistics.""" + if self.hit_count == 0 and self.miss_count == 0: + # a cache that does nothing, this is for logs only + return "" + return f" , {self.name}: {self.hit_count} hits, {self.miss_count} misses, {self.total_requested_bytes} total requested bytes" + + def __repr__(self) -> str: + # TODO: use rich for better formatting + return f""" + <{self.__class__.__name__}: + block size : {self.blocksize} + block count : {self.nblocks} + file size : {self.size} + cache hits : {self.hit_count} + cache misses: {self.miss_count} + total requested bytes: {self.total_requested_bytes}> + """ + + +class MMapCache(BaseCache): + """memory-mapped sparse file cache + + Opens temporary file, which is filled blocks-wise when data is requested. + Ensure there is enough disc space in the temporary location. + + This cache method might only work on posix + + Parameters + ---------- + blocksize: int + How far to read ahead in numbers of bytes + fetcher: Fetcher + Function of the form f(start, end) which gets bytes from remote as + specified + size: int + How big this file is + location: str + Where to create the temporary file. If None, a temporary file is + created using tempfile.TemporaryFile(). + blocks: set[int] + Set of block numbers that have already been fetched. If None, an empty + set is created. + multi_fetcher: MultiFetcher + Function of the form f([(start, end)]) which gets bytes from remote + as specified. This function is used to fetch multiple blocks at once. + If not specified, the fetcher function is used instead. + """ + + name = "mmap" + + def __init__( + self, + blocksize: int, + fetcher: Fetcher, + size: int, + location: str | None = None, + blocks: set[int] | None = None, + multi_fetcher: MultiFetcher | None = None, + ) -> None: + super().__init__(blocksize, fetcher, size) + self.blocks = set() if blocks is None else blocks + self.location = location + self.multi_fetcher = multi_fetcher + self.cache = self._makefile() + + def _makefile(self) -> mmap.mmap | bytearray: + import mmap + import tempfile + + if self.size == 0: + return bytearray() + + # posix version + if self.location is None or not os.path.exists(self.location): + if self.location is None: + fd = tempfile.TemporaryFile() + self.blocks = set() + else: + fd = open(self.location, "wb+") + fd.seek(self.size - 1) + fd.write(b"1") + fd.flush() + else: + fd = open(self.location, "r+b") + + return mmap.mmap(fd.fileno(), self.size) + + def _fetch(self, start: int | None, end: int | None) -> bytes: + logger.debug(f"MMap cache fetching {start}-{end}") + if start is None: + start = 0 + if end is None: + end = self.size + if start >= self.size or start >= end: + return b"" + start_block = start // self.blocksize + end_block = end // self.blocksize + block_range = range(start_block, end_block + 1) + # Determine which blocks need to be fetched. This sequence is sorted by construction. + need = (i for i in block_range if i not in self.blocks) + # Count the number of blocks already cached + self.hit_count += sum(1 for i in block_range if i in self.blocks) + + ranges = [] + + # Consolidate needed blocks. + # Algorithm adapted from Python 2.x itertools documentation. + # We are grouping an enumerated sequence of blocks. By comparing when the difference + # between an ascending range (provided by enumerate) and the needed block numbers + # we can detect when the block number skips values. The key computes this difference. + # Whenever the difference changes, we know that we have previously cached block(s), + # and a new group is started. In other words, this algorithm neatly groups + # runs of consecutive block numbers so they can be fetched together. + for _, _blocks in groupby(enumerate(need), key=lambda x: x[0] - x[1]): + # Extract the blocks from the enumerated sequence + _blocks = tuple(map(itemgetter(1), _blocks)) + # Compute start of first block + sstart = _blocks[0] * self.blocksize + # Compute the end of the last block. Last block may not be full size. + send = min(_blocks[-1] * self.blocksize + self.blocksize, self.size) + + # Fetch bytes (could be multiple consecutive blocks) + self.total_requested_bytes += send - sstart + logger.debug( + f"MMap get blocks {_blocks[0]}-{_blocks[-1]} ({sstart}-{send})" + ) + ranges.append((sstart, send)) + + # Update set of cached blocks + self.blocks.update(_blocks) + # Update cache statistics with number of blocks we had to cache + self.miss_count += len(_blocks) + + if not ranges: + return self.cache[start:end] + + if self.multi_fetcher: + logger.debug(f"MMap get blocks {ranges}") + for idx, r in enumerate(self.multi_fetcher(ranges)): + (sstart, send) = ranges[idx] + logger.debug(f"MMap copy block ({sstart}-{send}") + self.cache[sstart:send] = r + else: + for sstart, send in ranges: + logger.debug(f"MMap get block ({sstart}-{send}") + self.cache[sstart:send] = self.fetcher(sstart, send) + + return self.cache[start:end] + + def __getstate__(self) -> dict[str, Any]: + state = self.__dict__.copy() + # Remove the unpicklable entries. + del state["cache"] + return state + + def __setstate__(self, state: dict[str, Any]) -> None: + # Restore instance attributes + self.__dict__.update(state) + self.cache = self._makefile() + + +class ReadAheadCache(BaseCache): + """Cache which reads only when we get beyond a block of data + + This is a much simpler version of BytesCache, and does not attempt to + fill holes in the cache or keep fragments alive. It is best suited to + many small reads in a sequential order (e.g., reading lines from a file). + """ + + name = "readahead" + + def __init__(self, blocksize: int, fetcher: Fetcher, size: int) -> None: + super().__init__(blocksize, fetcher, size) + self.cache = b"" + self.start = 0 + self.end = 0 + + def _fetch(self, start: int | None, end: int | None) -> bytes: + if start is None: + start = 0 + if end is None or end > self.size: + end = self.size + if start >= self.size or start >= end: + return b"" + l = end - start + if start >= self.start and end <= self.end: + # cache hit + self.hit_count += 1 + return self.cache[start - self.start : end - self.start] + elif self.start <= start < self.end: + # partial hit + self.miss_count += 1 + part = self.cache[start - self.start :] + l -= len(part) + start = self.end + else: + # miss + self.miss_count += 1 + part = b"" + end = min(self.size, end + self.blocksize) + self.total_requested_bytes += end - start + self.cache = self.fetcher(start, end) # new block replaces old + self.start = start + self.end = self.start + len(self.cache) + return part + self.cache[:l] + + +class FirstChunkCache(BaseCache): + """Caches the first block of a file only + + This may be useful for file types where the metadata is stored in the header, + but is randomly accessed. + """ + + name = "first" + + def __init__(self, blocksize: int, fetcher: Fetcher, size: int) -> None: + if blocksize > size: + # this will buffer the whole thing + blocksize = size + super().__init__(blocksize, fetcher, size) + self.cache: bytes | None = None + + def _fetch(self, start: int | None, end: int | None) -> bytes: + start = start or 0 + if start > self.size: + logger.debug("FirstChunkCache: requested start > file size") + return b"" + + end = min(end, self.size) + + if start < self.blocksize: + if self.cache is None: + self.miss_count += 1 + if end > self.blocksize: + self.total_requested_bytes += end + data = self.fetcher(0, end) + self.cache = data[: self.blocksize] + return data[start:] + self.cache = self.fetcher(0, self.blocksize) + self.total_requested_bytes += self.blocksize + part = self.cache[start:end] + if end > self.blocksize: + self.total_requested_bytes += end - self.blocksize + part += self.fetcher(self.blocksize, end) + self.hit_count += 1 + return part + else: + self.miss_count += 1 + self.total_requested_bytes += end - start + return self.fetcher(start, end) + + +class BlockCache(BaseCache): + """ + Cache holding memory as a set of blocks. + + Requests are only ever made ``blocksize`` at a time, and are + stored in an LRU cache. The least recently accessed block is + discarded when more than ``maxblocks`` are stored. + + Parameters + ---------- + blocksize : int + The number of bytes to store in each block. + Requests are only ever made for ``blocksize``, so this + should balance the overhead of making a request against + the granularity of the blocks. + fetcher : Callable + size : int + The total size of the file being cached. + maxblocks : int + The maximum number of blocks to cache for. The maximum memory + use for this cache is then ``blocksize * maxblocks``. + """ + + name = "blockcache" + + def __init__( + self, blocksize: int, fetcher: Fetcher, size: int, maxblocks: int = 32 + ) -> None: + super().__init__(blocksize, fetcher, size) + self.nblocks = math.ceil(size / blocksize) + self.maxblocks = maxblocks + self._fetch_block_cached = functools.lru_cache(maxblocks)(self._fetch_block) + + def cache_info(self): + """ + The statistics on the block cache. + + Returns + ------- + NamedTuple + Returned directly from the LRU Cache used internally. + """ + return self._fetch_block_cached.cache_info() + + def __getstate__(self) -> dict[str, Any]: + state = self.__dict__ + del state["_fetch_block_cached"] + return state + + def __setstate__(self, state: dict[str, Any]) -> None: + self.__dict__.update(state) + self._fetch_block_cached = functools.lru_cache(state["maxblocks"])( + self._fetch_block + ) + + def _fetch(self, start: int | None, end: int | None) -> bytes: + if start is None: + start = 0 + if end is None: + end = self.size + if start >= self.size or start >= end: + return b"" + + # byte position -> block numbers + start_block_number = start // self.blocksize + end_block_number = end // self.blocksize + + # these are cached, so safe to do multiple calls for the same start and end. + for block_number in range(start_block_number, end_block_number + 1): + self._fetch_block_cached(block_number) + + return self._read_cache( + start, + end, + start_block_number=start_block_number, + end_block_number=end_block_number, + ) + + def _fetch_block(self, block_number: int) -> bytes: + """ + Fetch the block of data for `block_number`. + """ + if block_number > self.nblocks: + raise ValueError( + f"'block_number={block_number}' is greater than " + f"the number of blocks ({self.nblocks})" + ) + + start = block_number * self.blocksize + end = start + self.blocksize + self.total_requested_bytes += end - start + self.miss_count += 1 + logger.info("BlockCache fetching block %d", block_number) + block_contents = super()._fetch(start, end) + return block_contents + + def _read_cache( + self, start: int, end: int, start_block_number: int, end_block_number: int + ) -> bytes: + """ + Read from our block cache. + + Parameters + ---------- + start, end : int + The start and end byte positions. + start_block_number, end_block_number : int + The start and end block numbers. + """ + start_pos = start % self.blocksize + end_pos = end % self.blocksize + + self.hit_count += 1 + if start_block_number == end_block_number: + block: bytes = self._fetch_block_cached(start_block_number) + return block[start_pos:end_pos] + + else: + # read from the initial + out = [self._fetch_block_cached(start_block_number)[start_pos:]] + + # intermediate blocks + # Note: it'd be nice to combine these into one big request. However + # that doesn't play nicely with our LRU cache. + out.extend( + map( + self._fetch_block_cached, + range(start_block_number + 1, end_block_number), + ) + ) + + # final block + out.append(self._fetch_block_cached(end_block_number)[:end_pos]) + + return b"".join(out) + + +class BytesCache(BaseCache): + """Cache which holds data in a in-memory bytes object + + Implements read-ahead by the block size, for semi-random reads progressing + through the file. + + Parameters + ---------- + trim: bool + As we read more data, whether to discard the start of the buffer when + we are more than a blocksize ahead of it. + """ + + name: ClassVar[str] = "bytes" + + def __init__( + self, blocksize: int, fetcher: Fetcher, size: int, trim: bool = True + ) -> None: + super().__init__(blocksize, fetcher, size) + self.cache = b"" + self.start: int | None = None + self.end: int | None = None + self.trim = trim + + def _fetch(self, start: int | None, end: int | None) -> bytes: + # TODO: only set start/end after fetch, in case it fails? + # is this where retry logic might go? + if start is None: + start = 0 + if end is None: + end = self.size + if start >= self.size or start >= end: + return b"" + if ( + self.start is not None + and start >= self.start + and self.end is not None + and end < self.end + ): + # cache hit: we have all the required data + offset = start - self.start + self.hit_count += 1 + return self.cache[offset : offset + end - start] + + if self.blocksize: + bend = min(self.size, end + self.blocksize) + else: + bend = end + + if bend == start or start > self.size: + return b"" + + if (self.start is None or start < self.start) and ( + self.end is None or end > self.end + ): + # First read, or extending both before and after + self.total_requested_bytes += bend - start + self.miss_count += 1 + self.cache = self.fetcher(start, bend) + self.start = start + else: + assert self.start is not None + assert self.end is not None + self.miss_count += 1 + + if start < self.start: + if self.end is None or self.end - end > self.blocksize: + self.total_requested_bytes += bend - start + self.cache = self.fetcher(start, bend) + self.start = start + else: + self.total_requested_bytes += self.start - start + new = self.fetcher(start, self.start) + self.start = start + self.cache = new + self.cache + elif self.end is not None and bend > self.end: + if self.end > self.size: + pass + elif end - self.end > self.blocksize: + self.total_requested_bytes += bend - start + self.cache = self.fetcher(start, bend) + self.start = start + else: + self.total_requested_bytes += bend - self.end + new = self.fetcher(self.end, bend) + self.cache = self.cache + new + + self.end = self.start + len(self.cache) + offset = start - self.start + out = self.cache[offset : offset + end - start] + if self.trim: + num = (self.end - self.start) // (self.blocksize + 1) + if num > 1: + self.start += self.blocksize * num + self.cache = self.cache[self.blocksize * num :] + return out + + def __len__(self) -> int: + return len(self.cache) + + +class AllBytes(BaseCache): + """Cache entire contents of the file""" + + name: ClassVar[str] = "all" + + def __init__( + self, + blocksize: int | None = None, + fetcher: Fetcher | None = None, + size: int | None = None, + data: bytes | None = None, + ) -> None: + super().__init__(blocksize, fetcher, size) # type: ignore[arg-type] + if data is None: + self.miss_count += 1 + self.total_requested_bytes += self.size + data = self.fetcher(0, self.size) + self.data = data + + def _fetch(self, start: int | None, stop: int | None) -> bytes: + self.hit_count += 1 + return self.data[start:stop] + + +class KnownPartsOfAFile(BaseCache): + """ + Cache holding known file parts. + + Parameters + ---------- + blocksize: int + How far to read ahead in numbers of bytes + fetcher: func + Function of the form f(start, end) which gets bytes from remote as + specified + size: int + How big this file is + data: dict + A dictionary mapping explicit `(start, stop)` file-offset tuples + with known bytes. + strict: bool, default True + Whether to fetch reads that go beyond a known byte-range boundary. + If `False`, any read that ends outside a known part will be zero + padded. Note that zero padding will not be used for reads that + begin outside a known byte-range. + """ + + name: ClassVar[str] = "parts" + + def __init__( + self, + blocksize: int, + fetcher: Fetcher, + size: int, + data: dict[tuple[int, int], bytes] | None = None, + strict: bool = True, + **_: Any, + ): + super().__init__(blocksize, fetcher, size) + self.strict = strict + + # simple consolidation of contiguous blocks + if data: + old_offsets = sorted(data.keys()) + offsets = [old_offsets[0]] + blocks = [data.pop(old_offsets[0])] + for start, stop in old_offsets[1:]: + start0, stop0 = offsets[-1] + if start == stop0: + offsets[-1] = (start0, stop) + blocks[-1] += data.pop((start, stop)) + else: + offsets.append((start, stop)) + blocks.append(data.pop((start, stop))) + + self.data = dict(zip(offsets, blocks)) + else: + self.data = {} + + def _fetch(self, start: int | None, stop: int | None) -> bytes: + if start is None: + start = 0 + if stop is None: + stop = self.size + + out = b"" + for (loc0, loc1), data in self.data.items(): + # If self.strict=False, use zero-padded data + # for reads beyond the end of a "known" buffer + if loc0 <= start < loc1: + off = start - loc0 + out = data[off : off + stop - start] + if not self.strict or loc0 <= stop <= loc1: + # The request is within a known range, or + # it begins within a known range, and we + # are allowed to pad reads beyond the + # buffer with zero + out += b"\x00" * (stop - start - len(out)) + self.hit_count += 1 + return out + else: + # The request ends outside a known range, + # and we are being "strict" about reads + # beyond the buffer + start = loc1 + break + + # We only get here if there is a request outside the + # known parts of the file. In an ideal world, this + # should never happen + if self.fetcher is None: + # We cannot fetch the data, so raise an error + raise ValueError(f"Read is outside the known file parts: {(start, stop)}. ") + # We can fetch the data, but should warn the user + # that this may be slow + warnings.warn( + f"Read is outside the known file parts: {(start, stop)}. " + f"IO/caching performance may be poor!" + ) + logger.debug(f"KnownPartsOfAFile cache fetching {start}-{stop}") + self.total_requested_bytes += stop - start + self.miss_count += 1 + return out + super()._fetch(start, stop) + + +class UpdatableLRU(Generic[P, T]): + """ + Custom implementation of LRU cache that allows updating keys + + Used by BackgroudBlockCache + """ + + class CacheInfo(NamedTuple): + hits: int + misses: int + maxsize: int + currsize: int + + def __init__(self, func: Callable[P, T], max_size: int = 128) -> None: + self._cache: OrderedDict[Any, T] = collections.OrderedDict() + self._func = func + self._max_size = max_size + self._hits = 0 + self._misses = 0 + self._lock = threading.Lock() + + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> T: + if kwargs: + raise TypeError(f"Got unexpected keyword argument {kwargs.keys()}") + with self._lock: + if args in self._cache: + self._cache.move_to_end(args) + self._hits += 1 + return self._cache[args] + + result = self._func(*args, **kwargs) + + with self._lock: + self._cache[args] = result + self._misses += 1 + if len(self._cache) > self._max_size: + self._cache.popitem(last=False) + + return result + + def is_key_cached(self, *args: Any) -> bool: + with self._lock: + return args in self._cache + + def add_key(self, result: T, *args: Any) -> None: + with self._lock: + self._cache[args] = result + if len(self._cache) > self._max_size: + self._cache.popitem(last=False) + + def cache_info(self) -> UpdatableLRU.CacheInfo: + with self._lock: + return self.CacheInfo( + maxsize=self._max_size, + currsize=len(self._cache), + hits=self._hits, + misses=self._misses, + ) + + +class BackgroundBlockCache(BaseCache): + """ + Cache holding memory as a set of blocks with pre-loading of + the next block in the background. + + Requests are only ever made ``blocksize`` at a time, and are + stored in an LRU cache. The least recently accessed block is + discarded when more than ``maxblocks`` are stored. If the + next block is not in cache, it is loaded in a separate thread + in non-blocking way. + + Parameters + ---------- + blocksize : int + The number of bytes to store in each block. + Requests are only ever made for ``blocksize``, so this + should balance the overhead of making a request against + the granularity of the blocks. + fetcher : Callable + size : int + The total size of the file being cached. + maxblocks : int + The maximum number of blocks to cache for. The maximum memory + use for this cache is then ``blocksize * maxblocks``. + """ + + name: ClassVar[str] = "background" + + def __init__( + self, blocksize: int, fetcher: Fetcher, size: int, maxblocks: int = 32 + ) -> None: + super().__init__(blocksize, fetcher, size) + self.nblocks = math.ceil(size / blocksize) + self.maxblocks = maxblocks + self._fetch_block_cached = UpdatableLRU(self._fetch_block, maxblocks) + + self._thread_executor = ThreadPoolExecutor(max_workers=1) + self._fetch_future_block_number: int | None = None + self._fetch_future: Future[bytes] | None = None + self._fetch_future_lock = threading.Lock() + + def cache_info(self) -> UpdatableLRU.CacheInfo: + """ + The statistics on the block cache. + + Returns + ------- + NamedTuple + Returned directly from the LRU Cache used internally. + """ + return self._fetch_block_cached.cache_info() + + def __getstate__(self) -> dict[str, Any]: + state = self.__dict__ + del state["_fetch_block_cached"] + del state["_thread_executor"] + del state["_fetch_future_block_number"] + del state["_fetch_future"] + del state["_fetch_future_lock"] + return state + + def __setstate__(self, state) -> None: + self.__dict__.update(state) + self._fetch_block_cached = UpdatableLRU(self._fetch_block, state["maxblocks"]) + self._thread_executor = ThreadPoolExecutor(max_workers=1) + self._fetch_future_block_number = None + self._fetch_future = None + self._fetch_future_lock = threading.Lock() + + def _fetch(self, start: int | None, end: int | None) -> bytes: + if start is None: + start = 0 + if end is None: + end = self.size + if start >= self.size or start >= end: + return b"" + + # byte position -> block numbers + start_block_number = start // self.blocksize + end_block_number = end // self.blocksize + + fetch_future_block_number = None + fetch_future = None + with self._fetch_future_lock: + # Background thread is running. Check we we can or must join it. + if self._fetch_future is not None: + assert self._fetch_future_block_number is not None + if self._fetch_future.done(): + logger.info("BlockCache joined background fetch without waiting.") + self._fetch_block_cached.add_key( + self._fetch_future.result(), self._fetch_future_block_number + ) + # Cleanup the fetch variables. Done with fetching the block. + self._fetch_future_block_number = None + self._fetch_future = None + else: + # Must join if we need the block for the current fetch + must_join = bool( + start_block_number + <= self._fetch_future_block_number + <= end_block_number + ) + if must_join: + # Copy to the local variables to release lock + # before waiting for result + fetch_future_block_number = self._fetch_future_block_number + fetch_future = self._fetch_future + + # Cleanup the fetch variables. Have a local copy. + self._fetch_future_block_number = None + self._fetch_future = None + + # Need to wait for the future for the current read + if fetch_future is not None: + logger.info("BlockCache waiting for background fetch.") + # Wait until result and put it in cache + self._fetch_block_cached.add_key( + fetch_future.result(), fetch_future_block_number + ) + + # these are cached, so safe to do multiple calls for the same start and end. + for block_number in range(start_block_number, end_block_number + 1): + self._fetch_block_cached(block_number) + + # fetch next block in the background if nothing is running in the background, + # the block is within file and it is not already cached + end_block_plus_1 = end_block_number + 1 + with self._fetch_future_lock: + if ( + self._fetch_future is None + and end_block_plus_1 <= self.nblocks + and not self._fetch_block_cached.is_key_cached(end_block_plus_1) + ): + self._fetch_future_block_number = end_block_plus_1 + self._fetch_future = self._thread_executor.submit( + self._fetch_block, end_block_plus_1, "async" + ) + + return self._read_cache( + start, + end, + start_block_number=start_block_number, + end_block_number=end_block_number, + ) + + def _fetch_block(self, block_number: int, log_info: str = "sync") -> bytes: + """ + Fetch the block of data for `block_number`. + """ + if block_number > self.nblocks: + raise ValueError( + f"'block_number={block_number}' is greater than " + f"the number of blocks ({self.nblocks})" + ) + + start = block_number * self.blocksize + end = start + self.blocksize + logger.info("BlockCache fetching block (%s) %d", log_info, block_number) + self.total_requested_bytes += end - start + self.miss_count += 1 + block_contents = super()._fetch(start, end) + return block_contents + + def _read_cache( + self, start: int, end: int, start_block_number: int, end_block_number: int + ) -> bytes: + """ + Read from our block cache. + + Parameters + ---------- + start, end : int + The start and end byte positions. + start_block_number, end_block_number : int + The start and end block numbers. + """ + start_pos = start % self.blocksize + end_pos = end % self.blocksize + + # kind of pointless to count this as a hit, but it is + self.hit_count += 1 + + if start_block_number == end_block_number: + block = self._fetch_block_cached(start_block_number) + return block[start_pos:end_pos] + + else: + # read from the initial + out = [self._fetch_block_cached(start_block_number)[start_pos:]] + + # intermediate blocks + # Note: it'd be nice to combine these into one big request. However + # that doesn't play nicely with our LRU cache. + out.extend( + map( + self._fetch_block_cached, + range(start_block_number + 1, end_block_number), + ) + ) + + # final block + out.append(self._fetch_block_cached(end_block_number)[:end_pos]) + + return b"".join(out) + + +caches: dict[str | None, type[BaseCache]] = { + # one custom case + None: BaseCache, +} + + +def register_cache(cls: type[BaseCache], clobber: bool = False) -> None: + """'Register' cache implementation. + + Parameters + ---------- + clobber: bool, optional + If set to True (default is False) - allow to overwrite existing + entry. + + Raises + ------ + ValueError + """ + name = cls.name + if not clobber and name in caches: + raise ValueError(f"Cache with name {name!r} is already known: {caches[name]}") + caches[name] = cls + + +for c in ( + BaseCache, + MMapCache, + BytesCache, + ReadAheadCache, + BlockCache, + FirstChunkCache, + AllBytes, + KnownPartsOfAFile, + BackgroundBlockCache, +): + register_cache(c) diff --git a/.venv/lib/python3.12/site-packages/fsspec/callbacks.py b/.venv/lib/python3.12/site-packages/fsspec/callbacks.py new file mode 100644 index 0000000000000000000000000000000000000000..7ca99ca6ac3cd69b28bcd1550f6550e8e648c5fe --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec/callbacks.py @@ -0,0 +1,324 @@ +from functools import wraps + + +class Callback: + """ + Base class and interface for callback mechanism + + This class can be used directly for monitoring file transfers by + providing ``callback=Callback(hooks=...)`` (see the ``hooks`` argument, + below), or subclassed for more specialised behaviour. + + Parameters + ---------- + size: int (optional) + Nominal quantity for the value that corresponds to a complete + transfer, e.g., total number of tiles or total number of + bytes + value: int (0) + Starting internal counter value + hooks: dict or None + A dict of named functions to be called on each update. The signature + of these must be ``f(size, value, **kwargs)`` + """ + + def __init__(self, size=None, value=0, hooks=None, **kwargs): + self.size = size + self.value = value + self.hooks = hooks or {} + self.kw = kwargs + + def __enter__(self): + return self + + def __exit__(self, *exc_args): + self.close() + + def close(self): + """Close callback.""" + + def branched(self, path_1, path_2, **kwargs): + """ + Return callback for child transfers + + If this callback is operating at a higher level, e.g., put, which may + trigger transfers that can also be monitored. The function returns a callback + that has to be passed to the child method, e.g., put_file, + as `callback=` argument. + + The implementation uses `callback.branch` for compatibility. + When implementing callbacks, it is recommended to override this function instead + of `branch` and avoid calling `super().branched(...)`. + + Prefer using this function over `branch`. + + Parameters + ---------- + path_1: str + Child's source path + path_2: str + Child's destination path + **kwargs: + Arbitrary keyword arguments + + Returns + ------- + callback: Callback + A callback instance to be passed to the child method + """ + self.branch(path_1, path_2, kwargs) + # mutate kwargs so that we can force the caller to pass "callback=" explicitly + return kwargs.pop("callback", DEFAULT_CALLBACK) + + def branch_coro(self, fn): + """ + Wraps a coroutine, and pass a new child callback to it. + """ + + @wraps(fn) + async def func(path1, path2: str, **kwargs): + with self.branched(path1, path2, **kwargs) as child: + return await fn(path1, path2, callback=child, **kwargs) + + return func + + def set_size(self, size): + """ + Set the internal maximum size attribute + + Usually called if not initially set at instantiation. Note that this + triggers a ``call()``. + + Parameters + ---------- + size: int + """ + self.size = size + self.call() + + def absolute_update(self, value): + """ + Set the internal value state + + Triggers ``call()`` + + Parameters + ---------- + value: int + """ + self.value = value + self.call() + + def relative_update(self, inc=1): + """ + Delta increment the internal counter + + Triggers ``call()`` + + Parameters + ---------- + inc: int + """ + self.value += inc + self.call() + + def call(self, hook_name=None, **kwargs): + """ + Execute hook(s) with current state + + Each function is passed the internal size and current value + + Parameters + ---------- + hook_name: str or None + If given, execute on this hook + kwargs: passed on to (all) hook(s) + """ + if not self.hooks: + return + kw = self.kw.copy() + kw.update(kwargs) + if hook_name: + if hook_name not in self.hooks: + return + return self.hooks[hook_name](self.size, self.value, **kw) + for hook in self.hooks.values() or []: + hook(self.size, self.value, **kw) + + def wrap(self, iterable): + """ + Wrap an iterable to call ``relative_update`` on each iterations + + Parameters + ---------- + iterable: Iterable + The iterable that is being wrapped + """ + for item in iterable: + self.relative_update() + yield item + + def branch(self, path_1, path_2, kwargs): + """ + Set callbacks for child transfers + + If this callback is operating at a higher level, e.g., put, which may + trigger transfers that can also be monitored. The passed kwargs are + to be *mutated* to add ``callback=``, if this class supports branching + to children. + + Parameters + ---------- + path_1: str + Child's source path + path_2: str + Child's destination path + kwargs: dict + arguments passed to child method, e.g., put_file. + + Returns + ------- + + """ + return None + + def no_op(self, *_, **__): + pass + + def __getattr__(self, item): + """ + If undefined methods are called on this class, nothing happens + """ + return self.no_op + + @classmethod + def as_callback(cls, maybe_callback=None): + """Transform callback=... into Callback instance + + For the special value of ``None``, return the global instance of + ``NoOpCallback``. This is an alternative to including + ``callback=DEFAULT_CALLBACK`` directly in a method signature. + """ + if maybe_callback is None: + return DEFAULT_CALLBACK + return maybe_callback + + +class NoOpCallback(Callback): + """ + This implementation of Callback does exactly nothing + """ + + def call(self, *args, **kwargs): + return None + + +class DotPrinterCallback(Callback): + """ + Simple example Callback implementation + + Almost identical to Callback with a hook that prints a char; here we + demonstrate how the outer layer may print "#" and the inner layer "." + """ + + def __init__(self, chr_to_print="#", **kwargs): + self.chr = chr_to_print + super().__init__(**kwargs) + + def branch(self, path_1, path_2, kwargs): + """Mutate kwargs to add new instance with different print char""" + kwargs["callback"] = DotPrinterCallback(".") + + def call(self, **kwargs): + """Just outputs a character""" + print(self.chr, end="") + + +class TqdmCallback(Callback): + """ + A callback to display a progress bar using tqdm + + Parameters + ---------- + tqdm_kwargs : dict, (optional) + Any argument accepted by the tqdm constructor. + See the `tqdm doc `_. + Will be forwarded to `tqdm_cls`. + tqdm_cls: (optional) + subclass of `tqdm.tqdm`. If not passed, it will default to `tqdm.tqdm`. + + Examples + -------- + >>> import fsspec + >>> from fsspec.callbacks import TqdmCallback + >>> fs = fsspec.filesystem("memory") + >>> path2distant_data = "/your-path" + >>> fs.upload( + ".", + path2distant_data, + recursive=True, + callback=TqdmCallback(), + ) + + You can forward args to tqdm using the ``tqdm_kwargs`` parameter. + + >>> fs.upload( + ".", + path2distant_data, + recursive=True, + callback=TqdmCallback(tqdm_kwargs={"desc": "Your tqdm description"}), + ) + + You can also customize the progress bar by passing a subclass of `tqdm`. + + .. code-block:: python + + class TqdmFormat(tqdm): + '''Provides a `total_time` format parameter''' + @property + def format_dict(self): + d = super().format_dict + total_time = d["elapsed"] * (d["total"] or 0) / max(d["n"], 1) + d.update(total_time=self.format_interval(total_time) + " in total") + return d + + >>> with TqdmCallback( + tqdm_kwargs={ + "desc": "desc", + "bar_format": "{total_time}: {percentage:.0f}%|{bar}{r_bar}", + }, + tqdm_cls=TqdmFormat, + ) as callback: + fs.upload(".", path2distant_data, recursive=True, callback=callback) + """ + + def __init__(self, tqdm_kwargs=None, *args, **kwargs): + try: + from tqdm import tqdm + + except ImportError as exce: + raise ImportError( + "Using TqdmCallback requires tqdm to be installed" + ) from exce + + self._tqdm_cls = kwargs.pop("tqdm_cls", tqdm) + self._tqdm_kwargs = tqdm_kwargs or {} + self.tqdm = None + super().__init__(*args, **kwargs) + + def call(self, *args, **kwargs): + if self.tqdm is None: + self.tqdm = self._tqdm_cls(total=self.size, **self._tqdm_kwargs) + self.tqdm.total = self.size + self.tqdm.update(self.value - self.tqdm.n) + + def close(self): + if self.tqdm is not None: + self.tqdm.close() + self.tqdm = None + + def __del__(self): + return self.close() + + +DEFAULT_CALLBACK = _DEFAULT_CALLBACK = NoOpCallback() diff --git a/.venv/lib/python3.12/site-packages/fsspec/compression.py b/.venv/lib/python3.12/site-packages/fsspec/compression.py new file mode 100644 index 0000000000000000000000000000000000000000..e21da562bbab49c2ad60e9d9beb546af8dadea45 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec/compression.py @@ -0,0 +1,182 @@ +"""Helper functions for a standard streaming compression API""" + +from zipfile import ZipFile + +import fsspec.utils +from fsspec.spec import AbstractBufferedFile + + +def noop_file(file, mode, **kwargs): + return file + + +# TODO: files should also be available as contexts +# should be functions of the form func(infile, mode=, **kwargs) -> file-like +compr = {None: noop_file} + + +def register_compression(name, callback, extensions, force=False): + """Register an "inferable" file compression type. + + Registers transparent file compression type for use with fsspec.open. + Compression can be specified by name in open, or "infer"-ed for any files + ending with the given extensions. + + Args: + name: (str) The compression type name. Eg. "gzip". + callback: A callable of form (infile, mode, **kwargs) -> file-like. + Accepts an input file-like object, the target mode and kwargs. + Returns a wrapped file-like object. + extensions: (str, Iterable[str]) A file extension, or list of file + extensions for which to infer this compression scheme. Eg. "gz". + force: (bool) Force re-registration of compression type or extensions. + + Raises: + ValueError: If name or extensions already registered, and not force. + + """ + if isinstance(extensions, str): + extensions = [extensions] + + # Validate registration + if name in compr and not force: + raise ValueError(f"Duplicate compression registration: {name}") + + for ext in extensions: + if ext in fsspec.utils.compressions and not force: + raise ValueError(f"Duplicate compression file extension: {ext} ({name})") + + compr[name] = callback + + for ext in extensions: + fsspec.utils.compressions[ext] = name + + +def unzip(infile, mode="rb", filename=None, **kwargs): + if "r" not in mode: + filename = filename or "file" + z = ZipFile(infile, mode="w", **kwargs) + fo = z.open(filename, mode="w") + fo.close = lambda closer=fo.close: closer() or z.close() + return fo + z = ZipFile(infile) + if filename is None: + filename = z.namelist()[0] + return z.open(filename, mode="r", **kwargs) + + +register_compression("zip", unzip, "zip") + +try: + from bz2 import BZ2File +except ImportError: + pass +else: + register_compression("bz2", BZ2File, "bz2") + +try: # pragma: no cover + from isal import igzip + + def isal(infile, mode="rb", **kwargs): + return igzip.IGzipFile(fileobj=infile, mode=mode, **kwargs) + + register_compression("gzip", isal, "gz") +except ImportError: + from gzip import GzipFile + + register_compression( + "gzip", lambda f, **kwargs: GzipFile(fileobj=f, **kwargs), "gz" + ) + +try: + from lzma import LZMAFile + + register_compression("lzma", LZMAFile, "lzma") + register_compression("xz", LZMAFile, "xz") +except ImportError: + pass + +try: + import lzmaffi + + register_compression("lzma", lzmaffi.LZMAFile, "lzma", force=True) + register_compression("xz", lzmaffi.LZMAFile, "xz", force=True) +except ImportError: + pass + + +class SnappyFile(AbstractBufferedFile): + def __init__(self, infile, mode, **kwargs): + import snappy + + super().__init__( + fs=None, path="snappy", mode=mode.strip("b") + "b", size=999999999, **kwargs + ) + self.infile = infile + if "r" in mode: + self.codec = snappy.StreamDecompressor() + else: + self.codec = snappy.StreamCompressor() + + def _upload_chunk(self, final=False): + self.buffer.seek(0) + out = self.codec.add_chunk(self.buffer.read()) + self.infile.write(out) + return True + + def seek(self, loc, whence=0): + raise NotImplementedError("SnappyFile is not seekable") + + def seekable(self): + return False + + def _fetch_range(self, start, end): + """Get the specified set of bytes from remote""" + data = self.infile.read(end - start) + return self.codec.decompress(data) + + +try: + import snappy + + snappy.compress(b"") + # Snappy may use the .sz file extension, but this is not part of the + # standard implementation. + register_compression("snappy", SnappyFile, []) + +except (ImportError, NameError, AttributeError): + pass + +try: + import lz4.frame + + register_compression("lz4", lz4.frame.open, "lz4") +except ImportError: + pass + +try: + # zstd in the standard library for python >= 3.14 + from compression.zstd import ZstdFile + + register_compression("zstd", ZstdFile, "zst") + +except ImportError: + try: + import zstandard as zstd + + def zstandard_file(infile, mode="rb"): + if "r" in mode: + cctx = zstd.ZstdDecompressor() + return cctx.stream_reader(infile) + else: + cctx = zstd.ZstdCompressor(level=10) + return cctx.stream_writer(infile) + + register_compression("zstd", zstandard_file, "zst") + except ImportError: + pass + + +def available_compressions(): + """Return a list of the implemented compressions.""" + return list(compr) diff --git a/.venv/lib/python3.12/site-packages/fsspec/config.py b/.venv/lib/python3.12/site-packages/fsspec/config.py new file mode 100644 index 0000000000000000000000000000000000000000..76d9af14aaf7df47c4551c169f27b05abf9c269e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec/config.py @@ -0,0 +1,131 @@ +from __future__ import annotations + +import configparser +import json +import os +import warnings +from typing import Any + +conf: dict[str, dict[str, Any]] = {} +default_conf_dir = os.path.join(os.path.expanduser("~"), ".config/fsspec") +conf_dir = os.environ.get("FSSPEC_CONFIG_DIR", default_conf_dir) + + +def set_conf_env(conf_dict, envdict=os.environ): + """Set config values from environment variables + + Looks for variables of the form ``FSSPEC_`` and + ``FSSPEC__``. For ``FSSPEC_`` the value is parsed + as a json dictionary and used to ``update`` the config of the + corresponding protocol. For ``FSSPEC__`` there is no + attempt to convert the string value, but the kwarg keys will be lower-cased. + + The ``FSSPEC__`` variables are applied after the + ``FSSPEC_`` ones. + + Parameters + ---------- + conf_dict : dict(str, dict) + This dict will be mutated + envdict : dict-like(str, str) + Source for the values - usually the real environment + """ + kwarg_keys = [] + for key in envdict: + if key.startswith("FSSPEC_") and len(key) > 7 and key[7] != "_": + if key.count("_") > 1: + kwarg_keys.append(key) + continue + try: + value = json.loads(envdict[key]) + except json.decoder.JSONDecodeError as ex: + warnings.warn( + f"Ignoring environment variable {key} due to a parse failure: {ex}" + ) + else: + if isinstance(value, dict): + _, proto = key.split("_", 1) + conf_dict.setdefault(proto.lower(), {}).update(value) + else: + warnings.warn( + f"Ignoring environment variable {key} due to not being a dict:" + f" {type(value)}" + ) + elif key.startswith("FSSPEC"): + warnings.warn( + f"Ignoring environment variable {key} due to having an unexpected name" + ) + + for key in kwarg_keys: + _, proto, kwarg = key.split("_", 2) + conf_dict.setdefault(proto.lower(), {})[kwarg.lower()] = envdict[key] + + +def set_conf_files(cdir, conf_dict): + """Set config values from files + + Scans for INI and JSON files in the given dictionary, and uses their + contents to set the config. In case of repeated values, later values + win. + + In the case of INI files, all values are strings, and these will not + be converted. + + Parameters + ---------- + cdir : str + Directory to search + conf_dict : dict(str, dict) + This dict will be mutated + """ + if not os.path.isdir(cdir): + return + allfiles = sorted(os.listdir(cdir)) + for fn in allfiles: + if fn.endswith(".ini"): + ini = configparser.ConfigParser() + ini.read(os.path.join(cdir, fn)) + for key in ini: + if key == "DEFAULT": + continue + conf_dict.setdefault(key, {}).update(dict(ini[key])) + if fn.endswith(".json"): + with open(os.path.join(cdir, fn)) as f: + js = json.load(f) + for key in js: + conf_dict.setdefault(key, {}).update(dict(js[key])) + + +def apply_config(cls, kwargs, conf_dict=None): + """Supply default values for kwargs when instantiating class + + Augments the passed kwargs, by finding entries in the config dict + which match the classes ``.protocol`` attribute (one or more str) + + Parameters + ---------- + cls : file system implementation + kwargs : dict + conf_dict : dict of dict + Typically this is the global configuration + + Returns + ------- + dict : the modified set of kwargs + """ + if conf_dict is None: + conf_dict = conf + protos = cls.protocol if isinstance(cls.protocol, (tuple, list)) else [cls.protocol] + kw = {} + for proto in protos: + # default kwargs from the current state of the config + if proto in conf_dict: + kw.update(conf_dict[proto]) + # explicit kwargs always win + kw.update(**kwargs) + kwargs = kw + return kwargs + + +set_conf_files(conf_dir, conf) +set_conf_env(conf) diff --git a/.venv/lib/python3.12/site-packages/fsspec/conftest.py b/.venv/lib/python3.12/site-packages/fsspec/conftest.py new file mode 100644 index 0000000000000000000000000000000000000000..6874a42c4895c3c7b973dc5d63fd4488a4e60b44 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec/conftest.py @@ -0,0 +1,55 @@ +import os +import shutil +import subprocess +import sys +import time + +import pytest + +import fsspec +from fsspec.implementations.cached import CachingFileSystem + + +@pytest.fixture() +def m(): + """ + Fixture providing a memory filesystem. + """ + m = fsspec.filesystem("memory") + m.store.clear() + m.pseudo_dirs.clear() + m.pseudo_dirs.append("") + try: + yield m + finally: + m.store.clear() + m.pseudo_dirs.clear() + m.pseudo_dirs.append("") + + +@pytest.fixture +def ftp_writable(tmpdir): + """ + Fixture providing a writable FTP filesystem. + """ + pytest.importorskip("pyftpdlib") + from fsspec.implementations.ftp import FTPFileSystem + + FTPFileSystem.clear_instance_cache() # remove lingering connections + CachingFileSystem.clear_instance_cache() + d = str(tmpdir) + with open(os.path.join(d, "out"), "wb") as f: + f.write(b"hello" * 10000) + P = subprocess.Popen( + [sys.executable, "-m", "pyftpdlib", "-d", d, "-u", "user", "-P", "pass", "-w"] + ) + try: + time.sleep(1) + yield "localhost", 2121, "user", "pass" + finally: + P.terminate() + P.wait() + try: + shutil.rmtree(tmpdir) + except Exception: + pass diff --git a/.venv/lib/python3.12/site-packages/fsspec/core.py b/.venv/lib/python3.12/site-packages/fsspec/core.py new file mode 100644 index 0000000000000000000000000000000000000000..d8e75572bc0e31b5a12faee73275760e421aadb0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec/core.py @@ -0,0 +1,743 @@ +from __future__ import annotations + +import io +import logging +import os +import re +from glob import has_magic +from pathlib import Path + +# for backwards compat, we export cache things from here too +from fsspec.caching import ( # noqa: F401 + BaseCache, + BlockCache, + BytesCache, + MMapCache, + ReadAheadCache, + caches, +) +from fsspec.compression import compr +from fsspec.config import conf +from fsspec.registry import filesystem, get_filesystem_class +from fsspec.utils import ( + _unstrip_protocol, + build_name_function, + infer_compression, + stringify_path, +) + +logger = logging.getLogger("fsspec") + + +class OpenFile: + """ + File-like object to be used in a context + + Can layer (buffered) text-mode and compression over any file-system, which + are typically binary-only. + + These instances are safe to serialize, as the low-level file object + is not created until invoked using ``with``. + + Parameters + ---------- + fs: FileSystem + The file system to use for opening the file. Should be a subclass or duck-type + with ``fsspec.spec.AbstractFileSystem`` + path: str + Location to open + mode: str like 'rb', optional + Mode of the opened file + compression: str or None, optional + Compression to apply + encoding: str or None, optional + The encoding to use if opened in text mode. + errors: str or None, optional + How to handle encoding errors if opened in text mode. + newline: None or str + Passed to TextIOWrapper in text mode, how to handle line endings. + autoopen: bool + If True, calls open() immediately. Mostly used by pickle + pos: int + If given and autoopen is True, seek to this location immediately + """ + + def __init__( + self, + fs, + path, + mode="rb", + compression=None, + encoding=None, + errors=None, + newline=None, + ): + self.fs = fs + self.path = path + self.mode = mode + self.compression = get_compression(path, compression) + self.encoding = encoding + self.errors = errors + self.newline = newline + self.fobjects = [] + + def __reduce__(self): + return ( + OpenFile, + ( + self.fs, + self.path, + self.mode, + self.compression, + self.encoding, + self.errors, + self.newline, + ), + ) + + def __repr__(self): + return f"" + + def __enter__(self): + mode = self.mode.replace("t", "").replace("b", "") + "b" + + try: + f = self.fs.open(self.path, mode=mode) + except FileNotFoundError as e: + if has_magic(self.path): + raise FileNotFoundError( + "%s not found. The URL contains glob characters: you maybe needed\n" + "to pass expand=True in fsspec.open() or the storage_options of \n" + "your library. You can also set the config value 'open_expand'\n" + "before import, or fsspec.core.DEFAULT_EXPAND at runtime, to True.", + self.path, + ) from e + raise + + self.fobjects = [f] + + if self.compression is not None: + compress = compr[self.compression] + f = compress(f, mode=mode[0]) + self.fobjects.append(f) + + if "b" not in self.mode: + # assume, for example, that 'r' is equivalent to 'rt' as in builtin + f = PickleableTextIOWrapper( + f, encoding=self.encoding, errors=self.errors, newline=self.newline + ) + self.fobjects.append(f) + + return self.fobjects[-1] + + def __exit__(self, *args): + self.close() + + @property + def full_name(self): + return _unstrip_protocol(self.path, self.fs) + + def open(self): + """Materialise this as a real open file without context + + The OpenFile object should be explicitly closed to avoid enclosed file + instances persisting. You must, therefore, keep a reference to the OpenFile + during the life of the file-like it generates. + """ + return self.__enter__() + + def close(self): + """Close all encapsulated file objects""" + for f in reversed(self.fobjects): + if "r" not in self.mode and not f.closed: + f.flush() + f.close() + self.fobjects.clear() + + +class OpenFiles(list): + """List of OpenFile instances + + Can be used in a single context, which opens and closes all of the + contained files. Normal list access to get the elements works as + normal. + + A special case is made for caching filesystems - the files will + be down/uploaded together at the start or end of the context, and + this may happen concurrently, if the target filesystem supports it. + """ + + def __init__(self, *args, mode="rb", fs=None): + self.mode = mode + self.fs = fs + self.files = [] + super().__init__(*args) + + def __enter__(self): + if self.fs is None: + raise ValueError("Context has already been used") + + fs = self.fs + while True: + if hasattr(fs, "open_many"): + # check for concurrent cache download; or set up for upload + self.files = fs.open_many(self) + return self.files + if hasattr(fs, "fs") and fs.fs is not None: + fs = fs.fs + else: + break + return [s.__enter__() for s in self] + + def __exit__(self, *args): + fs = self.fs + [s.__exit__(*args) for s in self] + if "r" not in self.mode: + while True: + if hasattr(fs, "open_many"): + # check for concurrent cache upload + fs.commit_many(self.files) + return + if hasattr(fs, "fs") and fs.fs is not None: + fs = fs.fs + else: + break + + def __getitem__(self, item): + out = super().__getitem__(item) + if isinstance(item, slice): + return OpenFiles(out, mode=self.mode, fs=self.fs) + return out + + def __repr__(self): + return f"" + + +def open_files( + urlpath, + mode="rb", + compression=None, + encoding="utf8", + errors=None, + name_function=None, + num=1, + protocol=None, + newline=None, + auto_mkdir=True, + expand=True, + **kwargs, +): + """Given a path or paths, return a list of ``OpenFile`` objects. + + For writing, a str path must contain the "*" character, which will be filled + in by increasing numbers, e.g., "part*" -> "part1", "part2" if num=2. + + For either reading or writing, can instead provide explicit list of paths. + + Parameters + ---------- + urlpath: string or list + Absolute or relative filepath(s). Prefix with a protocol like ``s3://`` + to read from alternative filesystems. To read from multiple files you + can pass a globstring or a list of paths, with the caveat that they + must all have the same protocol. + mode: 'rb', 'wt', etc. + compression: string or None + If given, open file using compression codec. Can either be a compression + name (a key in ``fsspec.compression.compr``) or "infer" to guess the + compression from the filename suffix. + encoding: str + For text mode only + errors: None or str + Passed to TextIOWrapper in text mode + name_function: function or None + if opening a set of files for writing, those files do not yet exist, + so we need to generate their names by formatting the urlpath for + each sequence number + num: int [1] + if writing mode, number of files we expect to create (passed to + name+function) + protocol: str or None + If given, overrides the protocol found in the URL. + newline: bytes or None + Used for line terminator in text mode. If None, uses system default; + if blank, uses no translation. + auto_mkdir: bool (True) + If in write mode, this will ensure the target directory exists before + writing, by calling ``fs.mkdirs(exist_ok=True)``. + expand: bool + **kwargs: dict + Extra options that make sense to a particular storage connection, e.g. + host, port, username, password, etc. + + Examples + -------- + >>> files = open_files('2015-*-*.csv') # doctest: +SKIP + >>> files = open_files( + ... 's3://bucket/2015-*-*.csv.gz', compression='gzip' + ... ) # doctest: +SKIP + + Returns + ------- + An ``OpenFiles`` instance, which is a list of ``OpenFile`` objects that can + be used as a single context + + Notes + ----- + For a full list of the available protocols and the implementations that + they map across to see the latest online documentation: + + - For implementations built into ``fsspec`` see + https://filesystem-spec.readthedocs.io/en/latest/api.html#built-in-implementations + - For implementations in separate packages see + https://filesystem-spec.readthedocs.io/en/latest/api.html#other-known-implementations + """ + fs, fs_token, paths = get_fs_token_paths( + urlpath, + mode, + num=num, + name_function=name_function, + storage_options=kwargs, + protocol=protocol, + expand=expand, + ) + if fs.protocol == "file": + fs.auto_mkdir = auto_mkdir + elif "r" not in mode and auto_mkdir: + parents = {fs._parent(path) for path in paths} + for parent in parents: + try: + fs.makedirs(parent, exist_ok=True) + except PermissionError: + pass + return OpenFiles( + [ + OpenFile( + fs, + path, + mode=mode, + compression=compression, + encoding=encoding, + errors=errors, + newline=newline, + ) + for path in paths + ], + mode=mode, + fs=fs, + ) + + +def _un_chain(path, kwargs): + # Avoid a circular import + from fsspec.implementations.cached import CachingFileSystem + + if "::" in path: + x = re.compile(".*[^a-z]+.*") # test for non protocol-like single word + bits = [] + for p in path.split("::"): + if "://" in p or x.match(p): + bits.append(p) + else: + bits.append(p + "://") + else: + bits = [path] + # [[url, protocol, kwargs], ...] + out = [] + previous_bit = None + kwargs = kwargs.copy() + for bit in reversed(bits): + protocol = kwargs.pop("protocol", None) or split_protocol(bit)[0] or "file" + cls = get_filesystem_class(protocol) + extra_kwargs = cls._get_kwargs_from_urls(bit) + kws = kwargs.pop(protocol, {}) + if bit is bits[0]: + kws.update(kwargs) + kw = dict( + **{k: v for k, v in extra_kwargs.items() if k not in kws or v != kws[k]}, + **kws, + ) + bit = cls._strip_protocol(bit) + if "target_protocol" not in kw and issubclass(cls, CachingFileSystem): + bit = previous_bit + out.append((bit, protocol, kw)) + previous_bit = bit + out.reverse() + return out + + +def url_to_fs(url, **kwargs): + """ + Turn fully-qualified and potentially chained URL into filesystem instance + + Parameters + ---------- + url : str + The fsspec-compatible URL + **kwargs: dict + Extra options that make sense to a particular storage connection, e.g. + host, port, username, password, etc. + + Returns + ------- + filesystem : FileSystem + The new filesystem discovered from ``url`` and created with + ``**kwargs``. + urlpath : str + The file-systems-specific URL for ``url``. + """ + url = stringify_path(url) + # non-FS arguments that appear in fsspec.open() + # inspect could keep this in sync with open()'s signature + known_kwargs = { + "compression", + "encoding", + "errors", + "expand", + "mode", + "name_function", + "newline", + "num", + } + kwargs = {k: v for k, v in kwargs.items() if k not in known_kwargs} + chain = _un_chain(url, kwargs) + inkwargs = {} + # Reverse iterate the chain, creating a nested target_* structure + for i, ch in enumerate(reversed(chain)): + urls, protocol, kw = ch + if i == len(chain) - 1: + inkwargs = dict(**kw, **inkwargs) + continue + inkwargs["target_options"] = dict(**kw, **inkwargs) + inkwargs["target_protocol"] = protocol + inkwargs["fo"] = urls + urlpath, protocol, _ = chain[0] + fs = filesystem(protocol, **inkwargs) + return fs, urlpath + + +DEFAULT_EXPAND = conf.get("open_expand", False) + + +def open( + urlpath, + mode="rb", + compression=None, + encoding="utf8", + errors=None, + protocol=None, + newline=None, + expand=None, + **kwargs, +): + """Given a path or paths, return one ``OpenFile`` object. + + Parameters + ---------- + urlpath: string or list + Absolute or relative filepath. Prefix with a protocol like ``s3://`` + to read from alternative filesystems. Should not include glob + character(s). + mode: 'rb', 'wt', etc. + compression: string or None + If given, open file using compression codec. Can either be a compression + name (a key in ``fsspec.compression.compr``) or "infer" to guess the + compression from the filename suffix. + encoding: str + For text mode only + errors: None or str + Passed to TextIOWrapper in text mode + protocol: str or None + If given, overrides the protocol found in the URL. + newline: bytes or None + Used for line terminator in text mode. If None, uses system default; + if blank, uses no translation. + expand: bool or None + Whether to regard file paths containing special glob characters as needing + expansion (finding the first match) or absolute. Setting False allows using + paths which do embed such characters. If None (default), this argument + takes its value from the DEFAULT_EXPAND module variable, which takes + its initial value from the "open_expand" config value at startup, which will + be False if not set. + **kwargs: dict + Extra options that make sense to a particular storage connection, e.g. + host, port, username, password, etc. + + Examples + -------- + >>> openfile = open('2015-01-01.csv') # doctest: +SKIP + >>> openfile = open( + ... 's3://bucket/2015-01-01.csv.gz', compression='gzip' + ... ) # doctest: +SKIP + >>> with openfile as f: + ... df = pd.read_csv(f) # doctest: +SKIP + ... + + Returns + ------- + ``OpenFile`` object. + + Notes + ----- + For a full list of the available protocols and the implementations that + they map across to see the latest online documentation: + + - For implementations built into ``fsspec`` see + https://filesystem-spec.readthedocs.io/en/latest/api.html#built-in-implementations + - For implementations in separate packages see + https://filesystem-spec.readthedocs.io/en/latest/api.html#other-known-implementations + """ + expand = DEFAULT_EXPAND if expand is None else expand + out = open_files( + urlpath=[urlpath], + mode=mode, + compression=compression, + encoding=encoding, + errors=errors, + protocol=protocol, + newline=newline, + expand=expand, + **kwargs, + ) + if not out: + raise FileNotFoundError(urlpath) + return out[0] + + +def open_local( + url: str | list[str] | Path | list[Path], + mode: str = "rb", + **storage_options: dict, +) -> str | list[str]: + """Open file(s) which can be resolved to local + + For files which either are local, or get downloaded upon open + (e.g., by file caching) + + Parameters + ---------- + url: str or list(str) + mode: str + Must be read mode + storage_options: + passed on to FS for or used by open_files (e.g., compression) + """ + if "r" not in mode: + raise ValueError("Can only ensure local files when reading") + of = open_files(url, mode=mode, **storage_options) + if not getattr(of[0].fs, "local_file", False): + raise ValueError( + "open_local can only be used on a filesystem which" + " has attribute local_file=True" + ) + with of as files: + paths = [f.name for f in files] + if (isinstance(url, str) and not has_magic(url)) or isinstance(url, Path): + return paths[0] + return paths + + +def get_compression(urlpath, compression): + if compression == "infer": + compression = infer_compression(urlpath) + if compression is not None and compression not in compr: + raise ValueError(f"Compression type {compression} not supported") + return compression + + +def split_protocol(urlpath): + """Return protocol, path pair""" + urlpath = stringify_path(urlpath) + if "://" in urlpath: + protocol, path = urlpath.split("://", 1) + if len(protocol) > 1: + # excludes Windows paths + return protocol, path + if urlpath.startswith("data:"): + return urlpath.split(":", 1) + return None, urlpath + + +def strip_protocol(urlpath): + """Return only path part of full URL, according to appropriate backend""" + protocol, _ = split_protocol(urlpath) + cls = get_filesystem_class(protocol) + return cls._strip_protocol(urlpath) + + +def expand_paths_if_needed(paths, mode, num, fs, name_function): + """Expand paths if they have a ``*`` in them (write mode) or any of ``*?[]`` + in them (read mode). + + :param paths: list of paths + mode: str + Mode in which to open files. + num: int + If opening in writing mode, number of files we expect to create. + fs: filesystem object + name_function: callable + If opening in writing mode, this callable is used to generate path + names. Names are generated for each partition by + ``urlpath.replace('*', name_function(partition_index))``. + :return: list of paths + """ + expanded_paths = [] + paths = list(paths) + + if "w" in mode: # read mode + if sum(1 for p in paths if "*" in p) > 1: + raise ValueError( + "When writing data, only one filename mask can be specified." + ) + num = max(num, len(paths)) + + for curr_path in paths: + if "*" in curr_path: + # expand using name_function + expanded_paths.extend(_expand_paths(curr_path, name_function, num)) + else: + expanded_paths.append(curr_path) + # if we generated more paths that asked for, trim the list + if len(expanded_paths) > num: + expanded_paths = expanded_paths[:num] + + else: # read mode + for curr_path in paths: + if has_magic(curr_path): + # expand using glob + expanded_paths.extend(fs.glob(curr_path)) + else: + expanded_paths.append(curr_path) + + return expanded_paths + + +def get_fs_token_paths( + urlpath, + mode="rb", + num=1, + name_function=None, + storage_options=None, + protocol=None, + expand=True, +): + """Filesystem, deterministic token, and paths from a urlpath and options. + + Parameters + ---------- + urlpath: string or iterable + Absolute or relative filepath, URL (may include protocols like + ``s3://``), or globstring pointing to data. + mode: str, optional + Mode in which to open files. + num: int, optional + If opening in writing mode, number of files we expect to create. + name_function: callable, optional + If opening in writing mode, this callable is used to generate path + names. Names are generated for each partition by + ``urlpath.replace('*', name_function(partition_index))``. + storage_options: dict, optional + Additional keywords to pass to the filesystem class. + protocol: str or None + To override the protocol specifier in the URL + expand: bool + Expand string paths for writing, assuming the path is a directory + """ + if isinstance(urlpath, (list, tuple, set)): + if not urlpath: + raise ValueError("empty urlpath sequence") + urlpath0 = stringify_path(next(iter(urlpath))) + else: + urlpath0 = stringify_path(urlpath) + storage_options = storage_options or {} + if protocol: + storage_options["protocol"] = protocol + chain = _un_chain(urlpath0, storage_options or {}) + inkwargs = {} + # Reverse iterate the chain, creating a nested target_* structure + for i, ch in enumerate(reversed(chain)): + urls, nested_protocol, kw = ch + if i == len(chain) - 1: + inkwargs = dict(**kw, **inkwargs) + continue + inkwargs["target_options"] = dict(**kw, **inkwargs) + inkwargs["target_protocol"] = nested_protocol + inkwargs["fo"] = urls + paths, protocol, _ = chain[0] + fs = filesystem(protocol, **inkwargs) + if isinstance(urlpath, (list, tuple, set)): + pchains = [ + _un_chain(stringify_path(u), storage_options or {})[0] for u in urlpath + ] + if len({pc[1] for pc in pchains}) > 1: + raise ValueError("Protocol mismatch getting fs from %s", urlpath) + paths = [pc[0] for pc in pchains] + else: + paths = fs._strip_protocol(paths) + if isinstance(paths, (list, tuple, set)): + if expand: + paths = expand_paths_if_needed(paths, mode, num, fs, name_function) + elif not isinstance(paths, list): + paths = list(paths) + else: + if ("w" in mode or "x" in mode) and expand: + paths = _expand_paths(paths, name_function, num) + elif "*" in paths: + paths = [f for f in sorted(fs.glob(paths)) if not fs.isdir(f)] + else: + paths = [paths] + + return fs, fs._fs_token, paths + + +def _expand_paths(path, name_function, num): + if isinstance(path, str): + if path.count("*") > 1: + raise ValueError("Output path spec must contain exactly one '*'.") + elif "*" not in path: + path = os.path.join(path, "*.part") + + if name_function is None: + name_function = build_name_function(num - 1) + + paths = [path.replace("*", name_function(i)) for i in range(num)] + if paths != sorted(paths): + logger.warning( + "In order to preserve order between partitions" + " paths created with ``name_function`` should " + "sort to partition order" + ) + elif isinstance(path, (tuple, list)): + assert len(path) == num + paths = list(path) + else: + raise ValueError( + "Path should be either\n" + "1. A list of paths: ['foo.json', 'bar.json', ...]\n" + "2. A directory: 'foo/\n" + "3. A path with a '*' in it: 'foo.*.json'" + ) + return paths + + +class PickleableTextIOWrapper(io.TextIOWrapper): + """TextIOWrapper cannot be pickled. This solves it. + + Requires that ``buffer`` be pickleable, which all instances of + AbstractBufferedFile are. + """ + + def __init__( + self, + buffer, + encoding=None, + errors=None, + newline=None, + line_buffering=False, + write_through=False, + ): + self.args = buffer, encoding, errors, newline, line_buffering, write_through + super().__init__(*self.args) + + def __reduce__(self): + return PickleableTextIOWrapper, self.args diff --git a/.venv/lib/python3.12/site-packages/fsspec/dircache.py b/.venv/lib/python3.12/site-packages/fsspec/dircache.py new file mode 100644 index 0000000000000000000000000000000000000000..eca19566b135e5a7a4f6e7407d56411ec58bfe44 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec/dircache.py @@ -0,0 +1,98 @@ +import time +from collections.abc import MutableMapping +from functools import lru_cache + + +class DirCache(MutableMapping): + """ + Caching of directory listings, in a structure like:: + + {"path0": [ + {"name": "path0/file0", + "size": 123, + "type": "file", + ... + }, + {"name": "path0/file1", + }, + ... + ], + "path1": [...] + } + + Parameters to this class control listing expiry or indeed turn + caching off + """ + + def __init__( + self, + use_listings_cache=True, + listings_expiry_time=None, + max_paths=None, + **kwargs, + ): + """ + + Parameters + ---------- + use_listings_cache: bool + If False, this cache never returns items, but always reports KeyError, + and setting items has no effect + listings_expiry_time: int or float (optional) + Time in seconds that a listing is considered valid. If None, + listings do not expire. + max_paths: int (optional) + The number of most recent listings that are considered valid; 'recent' + refers to when the entry was set. + """ + self._cache = {} + self._times = {} + if max_paths: + self._q = lru_cache(max_paths + 1)(lambda key: self._cache.pop(key, None)) + self.use_listings_cache = use_listings_cache + self.listings_expiry_time = listings_expiry_time + self.max_paths = max_paths + + def __getitem__(self, item): + if self.listings_expiry_time is not None: + if self._times.get(item, 0) - time.time() < -self.listings_expiry_time: + del self._cache[item] + if self.max_paths: + self._q(item) + return self._cache[item] # maybe raises KeyError + + def clear(self): + self._cache.clear() + + def __len__(self): + return len(self._cache) + + def __contains__(self, item): + try: + self[item] + return True + except KeyError: + return False + + def __setitem__(self, key, value): + if not self.use_listings_cache: + return + if self.max_paths: + self._q(key) + self._cache[key] = value + if self.listings_expiry_time is not None: + self._times[key] = time.time() + + def __delitem__(self, key): + del self._cache[key] + + def __iter__(self): + entries = list(self._cache) + + return (k for k in entries if k in self) + + def __reduce__(self): + return ( + DirCache, + (self.use_listings_cache, self.listings_expiry_time, self.max_paths), + ) diff --git a/.venv/lib/python3.12/site-packages/fsspec/exceptions.py b/.venv/lib/python3.12/site-packages/fsspec/exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..ae8905475f02655f4fc5863931d99ca9da55db78 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec/exceptions.py @@ -0,0 +1,18 @@ +""" +fsspec user-defined exception classes +""" + +import asyncio + + +class BlocksizeMismatchError(ValueError): + """ + Raised when a cached file is opened with a different blocksize than it was + written with + """ + + +class FSTimeoutError(asyncio.TimeoutError): + """ + Raised when a fsspec function timed out occurs + """ diff --git a/.venv/lib/python3.12/site-packages/fsspec/fuse.py b/.venv/lib/python3.12/site-packages/fsspec/fuse.py new file mode 100644 index 0000000000000000000000000000000000000000..566d520fce3e94e3bbaee48c3c6acc9f1db315a8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec/fuse.py @@ -0,0 +1,324 @@ +import argparse +import logging +import os +import stat +import threading +import time +from errno import EIO, ENOENT + +from fuse import FUSE, FuseOSError, LoggingMixIn, Operations + +from fsspec import __version__ +from fsspec.core import url_to_fs + +logger = logging.getLogger("fsspec.fuse") + + +class FUSEr(Operations): + def __init__(self, fs, path, ready_file=False): + self.fs = fs + self.cache = {} + self.root = path.rstrip("/") + "/" + self.counter = 0 + logger.info("Starting FUSE at %s", path) + self._ready_file = ready_file + + def getattr(self, path, fh=None): + logger.debug("getattr %s", path) + if self._ready_file and path in ["/.fuse_ready", ".fuse_ready"]: + return {"type": "file", "st_size": 5} + + path = "".join([self.root, path.lstrip("/")]).rstrip("/") + try: + info = self.fs.info(path) + except FileNotFoundError as exc: + raise FuseOSError(ENOENT) from exc + + data = {"st_uid": info.get("uid", 1000), "st_gid": info.get("gid", 1000)} + perm = info.get("mode", 0o777) + + if info["type"] != "file": + data["st_mode"] = stat.S_IFDIR | perm + data["st_size"] = 0 + data["st_blksize"] = 0 + else: + data["st_mode"] = stat.S_IFREG | perm + data["st_size"] = info["size"] + data["st_blksize"] = 5 * 2**20 + data["st_nlink"] = 1 + data["st_atime"] = info["atime"] if "atime" in info else time.time() + data["st_ctime"] = info["ctime"] if "ctime" in info else time.time() + data["st_mtime"] = info["mtime"] if "mtime" in info else time.time() + return data + + def readdir(self, path, fh): + logger.debug("readdir %s", path) + path = "".join([self.root, path.lstrip("/")]) + files = self.fs.ls(path, False) + files = [os.path.basename(f.rstrip("/")) for f in files] + return [".", ".."] + files + + def mkdir(self, path, mode): + path = "".join([self.root, path.lstrip("/")]) + self.fs.mkdir(path) + return 0 + + def rmdir(self, path): + path = "".join([self.root, path.lstrip("/")]) + self.fs.rmdir(path) + return 0 + + def read(self, path, size, offset, fh): + logger.debug("read %s", (path, size, offset)) + if self._ready_file and path in ["/.fuse_ready", ".fuse_ready"]: + # status indicator + return b"ready" + + f = self.cache[fh] + f.seek(offset) + out = f.read(size) + return out + + def write(self, path, data, offset, fh): + logger.debug("write %s", (path, offset)) + f = self.cache[fh] + f.seek(offset) + f.write(data) + return len(data) + + def create(self, path, flags, fi=None): + logger.debug("create %s", (path, flags)) + fn = "".join([self.root, path.lstrip("/")]) + self.fs.touch(fn) # OS will want to get attributes immediately + f = self.fs.open(fn, "wb") + self.cache[self.counter] = f + self.counter += 1 + return self.counter - 1 + + def open(self, path, flags): + logger.debug("open %s", (path, flags)) + fn = "".join([self.root, path.lstrip("/")]) + if flags % 2 == 0: + # read + mode = "rb" + else: + # write/create + mode = "wb" + self.cache[self.counter] = self.fs.open(fn, mode) + self.counter += 1 + return self.counter - 1 + + def truncate(self, path, length, fh=None): + fn = "".join([self.root, path.lstrip("/")]) + if length != 0: + raise NotImplementedError + # maybe should be no-op since open with write sets size to zero anyway + self.fs.touch(fn) + + def unlink(self, path): + fn = "".join([self.root, path.lstrip("/")]) + try: + self.fs.rm(fn, False) + except (OSError, FileNotFoundError) as exc: + raise FuseOSError(EIO) from exc + + def release(self, path, fh): + try: + if fh in self.cache: + f = self.cache[fh] + f.close() + self.cache.pop(fh) + except Exception as e: + print(e) + return 0 + + def chmod(self, path, mode): + if hasattr(self.fs, "chmod"): + path = "".join([self.root, path.lstrip("/")]) + return self.fs.chmod(path, mode) + raise NotImplementedError + + +def run( + fs, + path, + mount_point, + foreground=True, + threads=False, + ready_file=False, + ops_class=FUSEr, +): + """Mount stuff in a local directory + + This uses fusepy to make it appear as if a given path on an fsspec + instance is in fact resident within the local file-system. + + This requires that fusepy by installed, and that FUSE be available on + the system (typically requiring a package to be installed with + apt, yum, brew, etc.). + + Parameters + ---------- + fs: file-system instance + From one of the compatible implementations + path: str + Location on that file-system to regard as the root directory to + mount. Note that you typically should include the terminating "/" + character. + mount_point: str + An empty directory on the local file-system where the contents of + the remote path will appear. + foreground: bool + Whether or not calling this function will block. Operation will + typically be more stable if True. + threads: bool + Whether or not to create threads when responding to file operations + within the mounter directory. Operation will typically be more + stable if False. + ready_file: bool + Whether the FUSE process is ready. The ``.fuse_ready`` file will + exist in the ``mount_point`` directory if True. Debugging purpose. + ops_class: FUSEr or Subclass of FUSEr + To override the default behavior of FUSEr. For Example, logging + to file. + + """ + func = lambda: FUSE( + ops_class(fs, path, ready_file=ready_file), + mount_point, + nothreads=not threads, + foreground=foreground, + ) + if not foreground: + th = threading.Thread(target=func) + th.daemon = True + th.start() + return th + else: # pragma: no cover + try: + func() + except KeyboardInterrupt: + pass + + +def main(args): + """Mount filesystem from chained URL to MOUNT_POINT. + + Examples: + + python3 -m fsspec.fuse memory /usr/share /tmp/mem + + python3 -m fsspec.fuse local /tmp/source /tmp/local \\ + -l /tmp/fsspecfuse.log + + You can also mount chained-URLs and use special settings: + + python3 -m fsspec.fuse 'filecache::zip::file://data.zip' \\ + / /tmp/zip \\ + -o 'filecache-cache_storage=/tmp/simplecache' + + You can specify the type of the setting by using `[int]` or `[bool]`, + (`true`, `yes`, `1` represents the Boolean value `True`): + + python3 -m fsspec.fuse 'simplecache::ftp://ftp1.at.proftpd.org' \\ + /historic/packages/RPMS /tmp/ftp \\ + -o 'simplecache-cache_storage=/tmp/simplecache' \\ + -o 'simplecache-check_files=false[bool]' \\ + -o 'ftp-listings_expiry_time=60[int]' \\ + -o 'ftp-username=anonymous' \\ + -o 'ftp-password=xieyanbo' + """ + + class RawDescriptionArgumentParser(argparse.ArgumentParser): + def format_help(self): + usage = super().format_help() + parts = usage.split("\n\n") + parts[1] = self.description.rstrip() + return "\n\n".join(parts) + + parser = RawDescriptionArgumentParser(prog="fsspec.fuse", description=main.__doc__) + parser.add_argument("--version", action="version", version=__version__) + parser.add_argument("url", type=str, help="fs url") + parser.add_argument("source_path", type=str, help="source directory in fs") + parser.add_argument("mount_point", type=str, help="local directory") + parser.add_argument( + "-o", + "--option", + action="append", + help="Any options of protocol included in the chained URL", + ) + parser.add_argument( + "-l", "--log-file", type=str, help="Logging FUSE debug info (Default: '')" + ) + parser.add_argument( + "-f", + "--foreground", + action="store_false", + help="Running in foreground or not (Default: False)", + ) + parser.add_argument( + "-t", + "--threads", + action="store_false", + help="Running with threads support (Default: False)", + ) + parser.add_argument( + "-r", + "--ready-file", + action="store_false", + help="The `.fuse_ready` file will exist after FUSE is ready. " + "(Debugging purpose, Default: False)", + ) + args = parser.parse_args(args) + + kwargs = {} + for item in args.option or []: + key, sep, value = item.partition("=") + if not sep: + parser.error(message=f"Wrong option: {item!r}") + val = value.lower() + if val.endswith("[int]"): + value = int(value[: -len("[int]")]) + elif val.endswith("[bool]"): + value = val[: -len("[bool]")] in ["1", "yes", "true"] + + if "-" in key: + fs_name, setting_name = key.split("-", 1) + if fs_name in kwargs: + kwargs[fs_name][setting_name] = value + else: + kwargs[fs_name] = {setting_name: value} + else: + kwargs[key] = value + + if args.log_file: + logging.basicConfig( + level=logging.DEBUG, + filename=args.log_file, + format="%(asctime)s %(message)s", + ) + + class LoggingFUSEr(FUSEr, LoggingMixIn): + pass + + fuser = LoggingFUSEr + else: + fuser = FUSEr + + fs, url_path = url_to_fs(args.url, **kwargs) + logger.debug("Mounting %s to %s", url_path, str(args.mount_point)) + run( + fs, + args.source_path, + args.mount_point, + foreground=args.foreground, + threads=args.threads, + ready_file=args.ready_file, + ops_class=fuser, + ) + + +if __name__ == "__main__": + import sys + + main(sys.argv[1:]) diff --git a/.venv/lib/python3.12/site-packages/fsspec/generic.py b/.venv/lib/python3.12/site-packages/fsspec/generic.py new file mode 100644 index 0000000000000000000000000000000000000000..2156d354a87bcf044bef04a62b4d61f5864cf33c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec/generic.py @@ -0,0 +1,394 @@ +from __future__ import annotations + +import inspect +import logging +import os +import shutil +import uuid + +from .asyn import AsyncFileSystem, _run_coros_in_chunks, sync_wrapper +from .callbacks import DEFAULT_CALLBACK +from .core import filesystem, get_filesystem_class, split_protocol, url_to_fs + +_generic_fs = {} +logger = logging.getLogger("fsspec.generic") + + +def set_generic_fs(protocol, **storage_options): + """Populate the dict used for method=="generic" lookups""" + _generic_fs[protocol] = filesystem(protocol, **storage_options) + + +def _resolve_fs(url, method, protocol=None, storage_options=None): + """Pick instance of backend FS""" + url = url[0] if isinstance(url, (list, tuple)) else url + protocol = protocol or split_protocol(url)[0] + storage_options = storage_options or {} + if method == "default": + return filesystem(protocol) + if method == "generic": + return _generic_fs[protocol] + if method == "current": + cls = get_filesystem_class(protocol) + return cls.current() + if method == "options": + fs, _ = url_to_fs(url, **storage_options.get(protocol, {})) + return fs + raise ValueError(f"Unknown FS resolution method: {method}") + + +def rsync( + source, + destination, + delete_missing=False, + source_field="size", + dest_field="size", + update_cond="different", + inst_kwargs=None, + fs=None, + **kwargs, +): + """Sync files between two directory trees + + (experimental) + + Parameters + ---------- + source: str + Root of the directory tree to take files from. This must be a directory, but + do not include any terminating "/" character + destination: str + Root path to copy into. The contents of this location should be + identical to the contents of ``source`` when done. This will be made a + directory, and the terminal "/" should not be included. + delete_missing: bool + If there are paths in the destination that don't exist in the + source and this is True, delete them. Otherwise, leave them alone. + source_field: str | callable + If ``update_field`` is "different", this is the key in the info + of source files to consider for difference. Maybe a function of the + info dict. + dest_field: str | callable + If ``update_field`` is "different", this is the key in the info + of destination files to consider for difference. May be a function of + the info dict. + update_cond: "different"|"always"|"never" + If "always", every file is copied, regardless of whether it exists in + the destination. If "never", files that exist in the destination are + not copied again. If "different" (default), only copy if the info + fields given by ``source_field`` and ``dest_field`` (usually "size") + are different. Other comparisons may be added in the future. + inst_kwargs: dict|None + If ``fs`` is None, use this set of keyword arguments to make a + GenericFileSystem instance + fs: GenericFileSystem|None + Instance to use if explicitly given. The instance defines how to + to make downstream file system instances from paths. + + Returns + ------- + dict of the copy operations that were performed, {source: destination} + """ + fs = fs or GenericFileSystem(**(inst_kwargs or {})) + source = fs._strip_protocol(source) + destination = fs._strip_protocol(destination) + allfiles = fs.find(source, withdirs=True, detail=True) + if not fs.isdir(source): + raise ValueError("Can only rsync on a directory") + otherfiles = fs.find(destination, withdirs=True, detail=True) + dirs = [ + a + for a, v in allfiles.items() + if v["type"] == "directory" and a.replace(source, destination) not in otherfiles + ] + logger.debug(f"{len(dirs)} directories to create") + if dirs: + fs.make_many_dirs( + [dirn.replace(source, destination) for dirn in dirs], exist_ok=True + ) + allfiles = {a: v for a, v in allfiles.items() if v["type"] == "file"} + logger.debug(f"{len(allfiles)} files to consider for copy") + to_delete = [ + o + for o, v in otherfiles.items() + if o.replace(destination, source) not in allfiles and v["type"] == "file" + ] + for k, v in allfiles.copy().items(): + otherfile = k.replace(source, destination) + if otherfile in otherfiles: + if update_cond == "always": + allfiles[k] = otherfile + elif update_cond == "different": + inf1 = source_field(v) if callable(source_field) else v[source_field] + v2 = otherfiles[otherfile] + inf2 = dest_field(v2) if callable(dest_field) else v2[dest_field] + if inf1 != inf2: + # details mismatch, make copy + allfiles[k] = otherfile + else: + # details match, don't copy + allfiles.pop(k) + else: + # file not in target yet + allfiles[k] = otherfile + logger.debug(f"{len(allfiles)} files to copy") + if allfiles: + source_files, target_files = zip(*allfiles.items()) + fs.cp(source_files, target_files, **kwargs) + logger.debug(f"{len(to_delete)} files to delete") + if delete_missing and to_delete: + fs.rm(to_delete) + return allfiles + + +class GenericFileSystem(AsyncFileSystem): + """Wrapper over all other FS types + + + + This implementation is a single unified interface to be able to run FS operations + over generic URLs, and dispatch to the specific implementations using the URL + protocol prefix. + + Note: instances of this FS are always async, even if you never use it with any async + backend. + """ + + protocol = "generic" # there is no real reason to ever use a protocol with this FS + + def __init__(self, default_method="default", storage_options=None, **kwargs): + """ + + Parameters + ---------- + default_method: str (optional) + Defines how to configure backend FS instances. Options are: + - "default": instantiate like FSClass(), with no + extra arguments; this is the default instance of that FS, and can be + configured via the config system + - "generic": takes instances from the `_generic_fs` dict in this module, + which you must populate before use. Keys are by protocol + - "options": expects storage_options, a dict mapping protocol to + kwargs to use when constructing the filesystem + - "current": takes the most recently instantiated version of each FS + """ + self.method = default_method + self.st_opts = storage_options + super().__init__(**kwargs) + + def _parent(self, path): + fs = _resolve_fs(path, self.method, storage_options=self.st_opts) + return fs.unstrip_protocol(fs._parent(path)) + + def _strip_protocol(self, path): + # normalization only + fs = _resolve_fs(path, self.method, storage_options=self.st_opts) + return fs.unstrip_protocol(fs._strip_protocol(path)) + + async def _find(self, path, maxdepth=None, withdirs=False, detail=False, **kwargs): + fs = _resolve_fs(path, self.method, storage_options=self.st_opts) + if fs.async_impl: + out = await fs._find( + path, maxdepth=maxdepth, withdirs=withdirs, detail=True, **kwargs + ) + else: + out = fs.find( + path, maxdepth=maxdepth, withdirs=withdirs, detail=True, **kwargs + ) + result = {} + for k, v in out.items(): + v = v.copy() # don't corrupt target FS dircache + name = fs.unstrip_protocol(k) + v["name"] = name + result[name] = v + if detail: + return result + return list(result) + + async def _info(self, url, **kwargs): + fs = _resolve_fs(url, self.method) + if fs.async_impl: + out = await fs._info(url, **kwargs) + else: + out = fs.info(url, **kwargs) + out = out.copy() # don't edit originals + out["name"] = fs.unstrip_protocol(out["name"]) + return out + + async def _ls( + self, + url, + detail=True, + **kwargs, + ): + fs = _resolve_fs(url, self.method) + if fs.async_impl: + out = await fs._ls(url, detail=True, **kwargs) + else: + out = fs.ls(url, detail=True, **kwargs) + out = [o.copy() for o in out] # don't edit originals + for o in out: + o["name"] = fs.unstrip_protocol(o["name"]) + if detail: + return out + else: + return [o["name"] for o in out] + + async def _cat_file( + self, + url, + **kwargs, + ): + fs = _resolve_fs(url, self.method) + if fs.async_impl: + return await fs._cat_file(url, **kwargs) + else: + return fs.cat_file(url, **kwargs) + + async def _pipe_file( + self, + path, + value, + **kwargs, + ): + fs = _resolve_fs(path, self.method, storage_options=self.st_opts) + if fs.async_impl: + return await fs._pipe_file(path, value, **kwargs) + else: + return fs.pipe_file(path, value, **kwargs) + + async def _rm(self, url, **kwargs): + urls = url + if isinstance(urls, str): + urls = [urls] + fs = _resolve_fs(urls[0], self.method) + if fs.async_impl: + await fs._rm(urls, **kwargs) + else: + fs.rm(url, **kwargs) + + async def _makedirs(self, path, exist_ok=False): + logger.debug("Make dir %s", path) + fs = _resolve_fs(path, self.method, storage_options=self.st_opts) + if fs.async_impl: + await fs._makedirs(path, exist_ok=exist_ok) + else: + fs.makedirs(path, exist_ok=exist_ok) + + def rsync(self, source, destination, **kwargs): + """Sync files between two directory trees + + See `func:rsync` for more details. + """ + rsync(source, destination, fs=self, **kwargs) + + async def _cp_file( + self, + url, + url2, + blocksize=2**20, + callback=DEFAULT_CALLBACK, + tempdir: str | None = None, + **kwargs, + ): + fs = _resolve_fs(url, self.method) + fs2 = _resolve_fs(url2, self.method) + if fs is fs2: + # pure remote + if fs.async_impl: + return await fs._copy(url, url2, **kwargs) + else: + return fs.copy(url, url2, **kwargs) + await copy_file_op(fs, [url], fs2, [url2], tempdir, 1, on_error="raise") + + async def _make_many_dirs(self, urls, exist_ok=True): + fs = _resolve_fs(urls[0], self.method) + if fs.async_impl: + coros = [fs._makedirs(u, exist_ok=exist_ok) for u in urls] + await _run_coros_in_chunks(coros) + else: + for u in urls: + fs.makedirs(u, exist_ok=exist_ok) + + make_many_dirs = sync_wrapper(_make_many_dirs) + + async def _copy( + self, + path1: list[str], + path2: list[str], + recursive: bool = False, + on_error: str = "ignore", + maxdepth: int | None = None, + batch_size: int | None = None, + tempdir: str | None = None, + **kwargs, + ): + # TODO: special case for one FS being local, which can use get/put + # TODO: special case for one being memFS, which can use cat/pipe + if recursive: + raise NotImplementedError("Please use fsspec.generic.rsync") + path1 = [path1] if isinstance(path1, str) else path1 + path2 = [path2] if isinstance(path2, str) else path2 + + fs = _resolve_fs(path1, self.method) + fs2 = _resolve_fs(path2, self.method) + + if fs is fs2: + if fs.async_impl: + return await fs._copy(path1, path2, **kwargs) + else: + return fs.copy(path1, path2, **kwargs) + + await copy_file_op( + fs, path1, fs2, path2, tempdir, batch_size, on_error=on_error + ) + + +async def copy_file_op( + fs1, url1, fs2, url2, tempdir=None, batch_size=20, on_error="ignore" +): + import tempfile + + tempdir = tempdir or tempfile.mkdtemp() + try: + coros = [ + _copy_file_op( + fs1, + u1, + fs2, + u2, + os.path.join(tempdir, uuid.uuid4().hex), + ) + for u1, u2 in zip(url1, url2) + ] + out = await _run_coros_in_chunks( + coros, batch_size=batch_size, return_exceptions=True + ) + finally: + shutil.rmtree(tempdir) + if on_error == "return": + return out + elif on_error == "raise": + for o in out: + if isinstance(o, Exception): + raise o + + +async def _copy_file_op(fs1, url1, fs2, url2, local, on_error="ignore"): + if fs1.async_impl: + await fs1._get_file(url1, local) + else: + fs1.get_file(url1, local) + if fs2.async_impl: + await fs2._put_file(local, url2) + else: + fs2.put_file(local, url2) + os.unlink(local) + logger.debug("Copy %s -> %s; done", url1, url2) + + +async def maybe_await(cor): + if inspect.iscoroutine(cor): + return await cor + else: + return cor diff --git a/.venv/lib/python3.12/site-packages/fsspec/gui.py b/.venv/lib/python3.12/site-packages/fsspec/gui.py new file mode 100644 index 0000000000000000000000000000000000000000..9d914c8beb6cabb2c2700eb8eee31028559be2bd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec/gui.py @@ -0,0 +1,417 @@ +import ast +import contextlib +import logging +import os +import re +from collections.abc import Sequence +from typing import ClassVar + +import panel as pn + +from .core import OpenFile, get_filesystem_class, split_protocol +from .registry import known_implementations + +pn.extension() +logger = logging.getLogger("fsspec.gui") + + +class SigSlot: + """Signal-slot mixin, for Panel event passing + + Include this class in a widget manager's superclasses to be able to + register events and callbacks on Panel widgets managed by that class. + + The method ``_register`` should be called as widgets are added, and external + code should call ``connect`` to associate callbacks. + + By default, all signals emit a DEBUG logging statement. + """ + + # names of signals that this class may emit each of which must be + # set by _register for any new instance + signals: ClassVar[Sequence[str]] = [] + # names of actions that this class may respond to + slots: ClassVar[Sequence[str]] = [] + + # each of which must be a method name + + def __init__(self): + self._ignoring_events = False + self._sigs = {} + self._map = {} + self._setup() + + def _setup(self): + """Create GUI elements and register signals""" + self.panel = pn.pane.PaneBase() + # no signals to set up in the base class + + def _register( + self, widget, name, thing="value", log_level=logging.DEBUG, auto=False + ): + """Watch the given attribute of a widget and assign it a named event + + This is normally called at the time a widget is instantiated, in the + class which owns it. + + Parameters + ---------- + widget : pn.layout.Panel or None + Widget to watch. If None, an anonymous signal not associated with + any widget. + name : str + Name of this event + thing : str + Attribute of the given widget to watch + log_level : int + When the signal is triggered, a logging event of the given level + will be fired in the dfviz logger. + auto : bool + If True, automatically connects with a method in this class of the + same name. + """ + if name not in self.signals: + raise ValueError(f"Attempt to assign an undeclared signal: {name}") + self._sigs[name] = { + "widget": widget, + "callbacks": [], + "thing": thing, + "log": log_level, + } + wn = "-".join( + [ + getattr(widget, "name", str(widget)) if widget is not None else "none", + thing, + ] + ) + self._map[wn] = name + if widget is not None: + widget.param.watch(self._signal, thing, onlychanged=True) + if auto and hasattr(self, name): + self.connect(name, getattr(self, name)) + + def _repr_mimebundle_(self, *args, **kwargs): + """Display in a notebook or a server""" + try: + return self.panel._repr_mimebundle_(*args, **kwargs) + except (ValueError, AttributeError) as exc: + raise NotImplementedError( + "Panel does not seem to be set up properly" + ) from exc + + def connect(self, signal, slot): + """Associate call back with given event + + The callback must be a function which takes the "new" value of the + watched attribute as the only parameter. If the callback return False, + this cancels any further processing of the given event. + + Alternatively, the callback can be a string, in which case it means + emitting the correspondingly-named event (i.e., connect to self) + """ + self._sigs[signal]["callbacks"].append(slot) + + def _signal(self, event): + """This is called by a an action on a widget + + Within an self.ignore_events context, nothing happens. + + Tests can execute this method by directly changing the values of + widget components. + """ + if not self._ignoring_events: + wn = "-".join([event.obj.name, event.name]) + if wn in self._map and self._map[wn] in self._sigs: + self._emit(self._map[wn], event.new) + + @contextlib.contextmanager + def ignore_events(self): + """Temporarily turn off events processing in this instance + + (does not propagate to children) + """ + self._ignoring_events = True + try: + yield + finally: + self._ignoring_events = False + + def _emit(self, sig, value=None): + """An event happened, call its callbacks + + This method can be used in tests to simulate message passing without + directly changing visual elements. + + Calling of callbacks will halt whenever one returns False. + """ + logger.log(self._sigs[sig]["log"], f"{sig}: {value}") + for callback in self._sigs[sig]["callbacks"]: + if isinstance(callback, str): + self._emit(callback) + else: + try: + # running callbacks should not break the interface + ret = callback(value) + if ret is False: + break + except Exception as e: + logger.exception( + "Exception (%s) while executing callback for signal: %s", + e, + sig, + ) + + def show(self, threads=False): + """Open a new browser tab and display this instance's interface""" + self.panel.show(threads=threads, verbose=False) + return self + + +class SingleSelect(SigSlot): + """A multiselect which only allows you to select one item for an event""" + + signals = ["_selected", "selected"] # the first is internal + slots = ["set_options", "set_selection", "add", "clear", "select"] + + def __init__(self, **kwargs): + self.kwargs = kwargs + super().__init__() + + def _setup(self): + self.panel = pn.widgets.MultiSelect(**self.kwargs) + self._register(self.panel, "_selected", "value") + self._register(None, "selected") + self.connect("_selected", self.select_one) + + def _signal(self, *args, **kwargs): + super()._signal(*args, **kwargs) + + def select_one(self, *_): + with self.ignore_events(): + val = [self.panel.value[-1]] if self.panel.value else [] + self.panel.value = val + self._emit("selected", self.panel.value) + + def set_options(self, options): + self.panel.options = options + + def clear(self): + self.panel.options = [] + + @property + def value(self): + return self.panel.value + + def set_selection(self, selection): + self.panel.value = [selection] + + +class FileSelector(SigSlot): + """Panel-based graphical file selector widget + + Instances of this widget are interactive and can be displayed in jupyter by having + them as the output of a cell, or in a separate browser tab using ``.show()``. + """ + + signals = [ + "protocol_changed", + "selection_changed", + "directory_entered", + "home_clicked", + "up_clicked", + "go_clicked", + "filters_changed", + ] + slots = ["set_filters", "go_home"] + + def __init__(self, url=None, filters=None, ignore=None, kwargs=None): + """ + + Parameters + ---------- + url : str (optional) + Initial value of the URL to populate the dialog; should include protocol + filters : list(str) (optional) + File endings to include in the listings. If not included, all files are + allowed. Does not affect directories. + If given, the endings will appear as checkboxes in the interface + ignore : list(str) (optional) + Regex(s) of file basename patterns to ignore, e.g., "\\." for typical + hidden files on posix + kwargs : dict (optional) + To pass to file system instance + """ + if url: + self.init_protocol, url = split_protocol(url) + else: + self.init_protocol, url = "file", os.getcwd() + self.init_url = url + self.init_kwargs = (kwargs if isinstance(kwargs, str) else str(kwargs)) or "{}" + self.filters = filters + self.ignore = [re.compile(i) for i in ignore or []] + self._fs = None + super().__init__() + + def _setup(self): + self.url = pn.widgets.TextInput( + name="url", + value=self.init_url, + align="end", + sizing_mode="stretch_width", + width_policy="max", + ) + self.protocol = pn.widgets.Select( + options=sorted(known_implementations), + value=self.init_protocol, + name="protocol", + align="center", + ) + self.kwargs = pn.widgets.TextInput( + name="kwargs", value=self.init_kwargs, align="center" + ) + self.go = pn.widgets.Button(name="⇨", align="end", width=45) + self.main = SingleSelect(size=10) + self.home = pn.widgets.Button(name="🏠", width=40, height=30, align="end") + self.up = pn.widgets.Button(name="‹", width=30, height=30, align="end") + + self._register(self.protocol, "protocol_changed", auto=True) + self._register(self.go, "go_clicked", "clicks", auto=True) + self._register(self.up, "up_clicked", "clicks", auto=True) + self._register(self.home, "home_clicked", "clicks", auto=True) + self._register(None, "selection_changed") + self.main.connect("selected", self.selection_changed) + self._register(None, "directory_entered") + self.prev_protocol = self.protocol.value + self.prev_kwargs = self.storage_options + + self.filter_sel = pn.widgets.CheckBoxGroup( + value=[], options=[], inline=False, align="end", width_policy="min" + ) + self._register(self.filter_sel, "filters_changed", auto=True) + + self.panel = pn.Column( + pn.Row(self.protocol, self.kwargs), + pn.Row(self.home, self.up, self.url, self.go, self.filter_sel), + self.main.panel, + ) + self.set_filters(self.filters) + self.go_clicked() + + def set_filters(self, filters=None): + self.filters = filters + if filters: + self.filter_sel.options = filters + self.filter_sel.value = filters + else: + self.filter_sel.options = [] + self.filter_sel.value = [] + + @property + def storage_options(self): + """Value of the kwargs box as a dictionary""" + return ast.literal_eval(self.kwargs.value) or {} + + @property + def fs(self): + """Current filesystem instance""" + if self._fs is None: + cls = get_filesystem_class(self.protocol.value) + self._fs = cls(**self.storage_options) + return self._fs + + @property + def urlpath(self): + """URL of currently selected item""" + return ( + (f"{self.protocol.value}://{self.main.value[0]}") + if self.main.value + else None + ) + + def open_file(self, mode="rb", compression=None, encoding=None): + """Create OpenFile instance for the currently selected item + + For example, in a notebook you might do something like + + .. code-block:: + + [ ]: sel = FileSelector(); sel + + # user selects their file + + [ ]: with sel.open_file('rb') as f: + ... out = f.read() + + Parameters + ---------- + mode: str (optional) + Open mode for the file. + compression: str (optional) + The interact with the file as compressed. Set to 'infer' to guess + compression from the file ending + encoding: str (optional) + If using text mode, use this encoding; defaults to UTF8. + """ + if self.urlpath is None: + raise ValueError("No file selected") + return OpenFile(self.fs, self.urlpath, mode, compression, encoding) + + def filters_changed(self, values): + self.filters = values + self.go_clicked() + + def selection_changed(self, *_): + if self.urlpath is None: + return + if self.fs.isdir(self.urlpath): + self.url.value = self.fs._strip_protocol(self.urlpath) + self.go_clicked() + + def go_clicked(self, *_): + if ( + self.prev_protocol != self.protocol.value + or self.prev_kwargs != self.storage_options + ): + self._fs = None # causes fs to be recreated + self.prev_protocol = self.protocol.value + self.prev_kwargs = self.storage_options + listing = sorted( + self.fs.ls(self.url.value, detail=True), key=lambda x: x["name"] + ) + listing = [ + l + for l in listing + if not any(i.match(l["name"].rsplit("/", 1)[-1]) for i in self.ignore) + ] + folders = { + "📁 " + o["name"].rsplit("/", 1)[-1]: o["name"] + for o in listing + if o["type"] == "directory" + } + files = { + "📄 " + o["name"].rsplit("/", 1)[-1]: o["name"] + for o in listing + if o["type"] == "file" + } + if self.filters: + files = { + k: v + for k, v in files.items() + if any(v.endswith(ext) for ext in self.filters) + } + self.main.set_options(dict(**folders, **files)) + + def protocol_changed(self, *_): + self._fs = None + self.main.options = [] + self.url.value = "" + + def home_clicked(self, *_): + self.protocol.value = self.init_protocol + self.kwargs.value = self.init_kwargs + self.url.value = self.init_url + self.go_clicked() + + def up_clicked(self, *_): + self.url.value = self.fs._parent(self.url.value) + self.go_clicked() diff --git a/.venv/lib/python3.12/site-packages/fsspec/json.py b/.venv/lib/python3.12/site-packages/fsspec/json.py new file mode 100644 index 0000000000000000000000000000000000000000..3bd2485ef1cc581d608f8627cb4133c198e35293 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec/json.py @@ -0,0 +1,117 @@ +import json +from collections.abc import Mapping, Sequence +from contextlib import suppress +from pathlib import PurePath +from typing import ( + Any, + Callable, + ClassVar, + Optional, +) + +from .registry import _import_class, get_filesystem_class +from .spec import AbstractFileSystem + + +class FilesystemJSONEncoder(json.JSONEncoder): + include_password: ClassVar[bool] = True + + def default(self, o: Any) -> Any: + if isinstance(o, AbstractFileSystem): + return o.to_dict(include_password=self.include_password) + if isinstance(o, PurePath): + cls = type(o) + return {"cls": f"{cls.__module__}.{cls.__name__}", "str": str(o)} + + return super().default(o) + + def make_serializable(self, obj: Any) -> Any: + """ + Recursively converts an object so that it can be JSON serialized via + :func:`json.dumps` and :func:`json.dump`, without actually calling + said functions. + """ + if isinstance(obj, (str, int, float, bool)): + return obj + if isinstance(obj, Mapping): + return {k: self.make_serializable(v) for k, v in obj.items()} + if isinstance(obj, Sequence): + return [self.make_serializable(v) for v in obj] + + return self.default(obj) + + +class FilesystemJSONDecoder(json.JSONDecoder): + def __init__( + self, + *, + object_hook: Optional[Callable[[dict[str, Any]], Any]] = None, + parse_float: Optional[Callable[[str], Any]] = None, + parse_int: Optional[Callable[[str], Any]] = None, + parse_constant: Optional[Callable[[str], Any]] = None, + strict: bool = True, + object_pairs_hook: Optional[Callable[[list[tuple[str, Any]]], Any]] = None, + ) -> None: + self.original_object_hook = object_hook + + super().__init__( + object_hook=self.custom_object_hook, + parse_float=parse_float, + parse_int=parse_int, + parse_constant=parse_constant, + strict=strict, + object_pairs_hook=object_pairs_hook, + ) + + @classmethod + def try_resolve_path_cls(cls, dct: dict[str, Any]): + with suppress(Exception): + fqp = dct["cls"] + + path_cls = _import_class(fqp) + + if issubclass(path_cls, PurePath): + return path_cls + + return None + + @classmethod + def try_resolve_fs_cls(cls, dct: dict[str, Any]): + with suppress(Exception): + if "cls" in dct: + try: + fs_cls = _import_class(dct["cls"]) + if issubclass(fs_cls, AbstractFileSystem): + return fs_cls + except Exception: + if "protocol" in dct: # Fallback if cls cannot be imported + return get_filesystem_class(dct["protocol"]) + + raise + + return None + + def custom_object_hook(self, dct: dict[str, Any]): + if "cls" in dct: + if (obj_cls := self.try_resolve_fs_cls(dct)) is not None: + return AbstractFileSystem.from_dict(dct) + if (obj_cls := self.try_resolve_path_cls(dct)) is not None: + return obj_cls(dct["str"]) + + if self.original_object_hook is not None: + return self.original_object_hook(dct) + + return dct + + def unmake_serializable(self, obj: Any) -> Any: + """ + Inverse function of :meth:`FilesystemJSONEncoder.make_serializable`. + """ + if isinstance(obj, dict): + obj = self.custom_object_hook(obj) + if isinstance(obj, dict): + return {k: self.unmake_serializable(v) for k, v in obj.items()} + if isinstance(obj, (list, tuple)): + return [self.unmake_serializable(v) for v in obj] + + return obj diff --git a/.venv/lib/python3.12/site-packages/fsspec/mapping.py b/.venv/lib/python3.12/site-packages/fsspec/mapping.py new file mode 100644 index 0000000000000000000000000000000000000000..752eef35273b13eded7297e2e801b58e436a25b1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec/mapping.py @@ -0,0 +1,251 @@ +import array +import logging +import posixpath +import warnings +from collections.abc import MutableMapping +from functools import cached_property + +from fsspec.core import url_to_fs + +logger = logging.getLogger("fsspec.mapping") + + +class FSMap(MutableMapping): + """Wrap a FileSystem instance as a mutable wrapping. + + The keys of the mapping become files under the given root, and the + values (which must be bytes) the contents of those files. + + Parameters + ---------- + root: string + prefix for all the files + fs: FileSystem instance + check: bool (=True) + performs a touch at the location, to check for write access. + + Examples + -------- + >>> fs = FileSystem(**parameters) # doctest: +SKIP + >>> d = FSMap('my-data/path/', fs) # doctest: +SKIP + or, more likely + >>> d = fs.get_mapper('my-data/path/') + + >>> d['loc1'] = b'Hello World' # doctest: +SKIP + >>> list(d.keys()) # doctest: +SKIP + ['loc1'] + >>> d['loc1'] # doctest: +SKIP + b'Hello World' + """ + + def __init__(self, root, fs, check=False, create=False, missing_exceptions=None): + self.fs = fs + self.root = fs._strip_protocol(root) + self._root_key_to_str = fs._strip_protocol(posixpath.join(root, "x"))[:-1] + if missing_exceptions is None: + missing_exceptions = ( + FileNotFoundError, + IsADirectoryError, + NotADirectoryError, + ) + self.missing_exceptions = missing_exceptions + self.check = check + self.create = create + if create: + if not self.fs.exists(root): + self.fs.mkdir(root) + if check: + if not self.fs.exists(root): + raise ValueError( + f"Path {root} does not exist. Create " + f" with the ``create=True`` keyword" + ) + self.fs.touch(root + "/a") + self.fs.rm(root + "/a") + + @cached_property + def dirfs(self): + """dirfs instance that can be used with the same keys as the mapper""" + from .implementations.dirfs import DirFileSystem + + return DirFileSystem(path=self._root_key_to_str, fs=self.fs) + + def clear(self): + """Remove all keys below root - empties out mapping""" + logger.info("Clear mapping at %s", self.root) + try: + self.fs.rm(self.root, True) + self.fs.mkdir(self.root) + except: # noqa: E722 + pass + + def getitems(self, keys, on_error="raise"): + """Fetch multiple items from the store + + If the backend is async-able, this might proceed concurrently + + Parameters + ---------- + keys: list(str) + They keys to be fetched + on_error : "raise", "omit", "return" + If raise, an underlying exception will be raised (converted to KeyError + if the type is in self.missing_exceptions); if omit, keys with exception + will simply not be included in the output; if "return", all keys are + included in the output, but the value will be bytes or an exception + instance. + + Returns + ------- + dict(key, bytes|exception) + """ + keys2 = [self._key_to_str(k) for k in keys] + oe = on_error if on_error == "raise" else "return" + try: + out = self.fs.cat(keys2, on_error=oe) + if isinstance(out, bytes): + out = {keys2[0]: out} + except self.missing_exceptions as e: + raise KeyError from e + out = { + k: (KeyError() if isinstance(v, self.missing_exceptions) else v) + for k, v in out.items() + } + return { + key: out[k2] if on_error == "raise" else out.get(k2, KeyError(k2)) + for key, k2 in zip(keys, keys2) + if on_error == "return" or not isinstance(out[k2], BaseException) + } + + def setitems(self, values_dict): + """Set the values of multiple items in the store + + Parameters + ---------- + values_dict: dict(str, bytes) + """ + values = {self._key_to_str(k): maybe_convert(v) for k, v in values_dict.items()} + self.fs.pipe(values) + + def delitems(self, keys): + """Remove multiple keys from the store""" + self.fs.rm([self._key_to_str(k) for k in keys]) + + def _key_to_str(self, key): + """Generate full path for the key""" + if not isinstance(key, str): + # raise TypeError("key must be of type `str`, got `{type(key).__name__}`" + warnings.warn( + "from fsspec 2023.5 onward FSMap non-str keys will raise TypeError", + DeprecationWarning, + ) + if isinstance(key, list): + key = tuple(key) + key = str(key) + return f"{self._root_key_to_str}{key}".rstrip("/") + + def _str_to_key(self, s): + """Strip path of to leave key name""" + return s[len(self.root) :].lstrip("/") + + def __getitem__(self, key, default=None): + """Retrieve data""" + k = self._key_to_str(key) + try: + result = self.fs.cat(k) + except self.missing_exceptions as exc: + if default is not None: + return default + raise KeyError(key) from exc + return result + + def pop(self, key, default=None): + """Pop data""" + result = self.__getitem__(key, default) + try: + del self[key] + except KeyError: + pass + return result + + def __setitem__(self, key, value): + """Store value in key""" + key = self._key_to_str(key) + self.fs.mkdirs(self.fs._parent(key), exist_ok=True) + self.fs.pipe_file(key, maybe_convert(value)) + + def __iter__(self): + return (self._str_to_key(x) for x in self.fs.find(self.root)) + + def __len__(self): + return len(self.fs.find(self.root)) + + def __delitem__(self, key): + """Remove key""" + try: + self.fs.rm(self._key_to_str(key)) + except Exception as exc: + raise KeyError from exc + + def __contains__(self, key): + """Does key exist in mapping?""" + path = self._key_to_str(key) + return self.fs.isfile(path) + + def __reduce__(self): + return FSMap, (self.root, self.fs, False, False, self.missing_exceptions) + + +def maybe_convert(value): + if isinstance(value, array.array) or hasattr(value, "__array__"): + # bytes-like things + if hasattr(value, "dtype") and value.dtype.kind in "Mm": + # The buffer interface doesn't support datetime64/timdelta64 numpy + # arrays + value = value.view("int64") + value = bytes(memoryview(value)) + return value + + +def get_mapper( + url="", + check=False, + create=False, + missing_exceptions=None, + alternate_root=None, + **kwargs, +): + """Create key-value interface for given URL and options + + The URL will be of the form "protocol://location" and point to the root + of the mapper required. All keys will be file-names below this location, + and their values the contents of each key. + + Also accepts compound URLs like zip::s3://bucket/file.zip , see ``fsspec.open``. + + Parameters + ---------- + url: str + Root URL of mapping + check: bool + Whether to attempt to read from the location before instantiation, to + check that the mapping does exist + create: bool + Whether to make the directory corresponding to the root before + instantiating + missing_exceptions: None or tuple + If given, these exception types will be regarded as missing keys and + return KeyError when trying to read data. By default, you get + (FileNotFoundError, IsADirectoryError, NotADirectoryError) + alternate_root: None or str + In cases of complex URLs, the parser may fail to pick the correct part + for the mapper root, so this arg can override + + Returns + ------- + ``FSMap`` instance, the dict-like key-value store. + """ + # Removing protocol here - could defer to each open() on the backend + fs, urlpath = url_to_fs(url, **kwargs) + root = alternate_root if alternate_root is not None else urlpath + return FSMap(root, fs, check, create, missing_exceptions=missing_exceptions) diff --git a/.venv/lib/python3.12/site-packages/fsspec/parquet.py b/.venv/lib/python3.12/site-packages/fsspec/parquet.py new file mode 100644 index 0000000000000000000000000000000000000000..faedb7b9e0aa90b6fb7cba33e794c4b4fb35eb77 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec/parquet.py @@ -0,0 +1,541 @@ +import io +import json +import warnings + +from .core import url_to_fs +from .utils import merge_offset_ranges + +# Parquet-Specific Utilities for fsspec +# +# Most of the functions defined in this module are NOT +# intended for public consumption. The only exception +# to this is `open_parquet_file`, which should be used +# place of `fs.open()` to open parquet-formatted files +# on remote file systems. + + +def open_parquet_file( + path, + mode="rb", + fs=None, + metadata=None, + columns=None, + row_groups=None, + storage_options=None, + strict=False, + engine="auto", + max_gap=64_000, + max_block=256_000_000, + footer_sample_size=1_000_000, + **kwargs, +): + """ + Return a file-like object for a single Parquet file. + + The specified parquet `engine` will be used to parse the + footer metadata, and determine the required byte ranges + from the file. The target path will then be opened with + the "parts" (`KnownPartsOfAFile`) caching strategy. + + Note that this method is intended for usage with remote + file systems, and is unlikely to improve parquet-read + performance on local file systems. + + Parameters + ---------- + path: str + Target file path. + mode: str, optional + Mode option to be passed through to `fs.open`. Default is "rb". + metadata: Any, optional + Parquet metadata object. Object type must be supported + by the backend parquet engine. For now, only the "fastparquet" + engine supports an explicit `ParquetFile` metadata object. + If a metadata object is supplied, the remote footer metadata + will not need to be transferred into local memory. + fs: AbstractFileSystem, optional + Filesystem object to use for opening the file. If nothing is + specified, an `AbstractFileSystem` object will be inferred. + engine : str, default "auto" + Parquet engine to use for metadata parsing. Allowed options + include "fastparquet", "pyarrow", and "auto". The specified + engine must be installed in the current environment. If + "auto" is specified, and both engines are installed, + "fastparquet" will take precedence over "pyarrow". + columns: list, optional + List of all column names that may be read from the file. + row_groups : list, optional + List of all row-groups that may be read from the file. This + may be a list of row-group indices (integers), or it may be + a list of `RowGroup` metadata objects (if the "fastparquet" + engine is used). + storage_options : dict, optional + Used to generate an `AbstractFileSystem` object if `fs` was + not specified. + strict : bool, optional + Whether the resulting `KnownPartsOfAFile` cache should + fetch reads that go beyond a known byte-range boundary. + If `False` (the default), any read that ends outside a + known part will be zero padded. Note that using + `strict=True` may be useful for debugging. + max_gap : int, optional + Neighboring byte ranges will only be merged when their + inter-range gap is <= `max_gap`. Default is 64KB. + max_block : int, optional + Neighboring byte ranges will only be merged when the size of + the aggregated range is <= `max_block`. Default is 256MB. + footer_sample_size : int, optional + Number of bytes to read from the end of the path to look + for the footer metadata. If the sampled bytes do not contain + the footer, a second read request will be required, and + performance will suffer. Default is 1MB. + **kwargs : + Optional key-word arguments to pass to `fs.open` + """ + + # Make sure we have an `AbstractFileSystem` object + # to work with + if fs is None: + fs = url_to_fs(path, **(storage_options or {}))[0] + + # For now, `columns == []` not supported. Just use + # default `open` command with `path` input + if columns is not None and len(columns) == 0: + return fs.open(path, mode=mode) + + # Set the engine + engine = _set_engine(engine) + + # Fetch the known byte ranges needed to read + # `columns` and/or `row_groups` + data = _get_parquet_byte_ranges( + [path], + fs, + metadata=metadata, + columns=columns, + row_groups=row_groups, + engine=engine, + max_gap=max_gap, + max_block=max_block, + footer_sample_size=footer_sample_size, + ) + + # Extract file name from `data` + fn = next(iter(data)) if data else path + + # Call self.open with "parts" caching + options = kwargs.pop("cache_options", {}).copy() + return fs.open( + fn, + mode=mode, + cache_type="parts", + cache_options={ + **options, + "data": data.get(fn, {}), + "strict": strict, + }, + **kwargs, + ) + + +def _get_parquet_byte_ranges( + paths, + fs, + metadata=None, + columns=None, + row_groups=None, + max_gap=64_000, + max_block=256_000_000, + footer_sample_size=1_000_000, + engine="auto", +): + """Get a dictionary of the known byte ranges needed + to read a specific column/row-group selection from a + Parquet dataset. Each value in the output dictionary + is intended for use as the `data` argument for the + `KnownPartsOfAFile` caching strategy of a single path. + """ + + # Set engine if necessary + if isinstance(engine, str): + engine = _set_engine(engine) + + # Pass to specialized function if metadata is defined + if metadata is not None: + # Use the provided parquet metadata object + # to avoid transferring/parsing footer metadata + return _get_parquet_byte_ranges_from_metadata( + metadata, + fs, + engine, + columns=columns, + row_groups=row_groups, + max_gap=max_gap, + max_block=max_block, + ) + + # Get file sizes asynchronously + file_sizes = fs.sizes(paths) + + # Populate global paths, starts, & ends + result = {} + data_paths = [] + data_starts = [] + data_ends = [] + add_header_magic = True + if columns is None and row_groups is None: + # We are NOT selecting specific columns or row-groups. + # + # We can avoid sampling the footers, and just transfer + # all file data with cat_ranges + for i, path in enumerate(paths): + result[path] = {} + for b in range(0, file_sizes[i], max_block): + data_paths.append(path) + data_starts.append(b) + data_ends.append(min(b + max_block, file_sizes[i])) + add_header_magic = False # "Magic" should already be included + else: + # We ARE selecting specific columns or row-groups. + # + # Gather file footers. + # We just take the last `footer_sample_size` bytes of each + # file (or the entire file if it is smaller than that) + footer_starts = [] + footer_ends = [] + for i, path in enumerate(paths): + footer_ends.append(file_sizes[i]) + sample_size = max(0, file_sizes[i] - footer_sample_size) + footer_starts.append(sample_size) + footer_samples = fs.cat_ranges(paths, footer_starts, footer_ends) + + # Check our footer samples and re-sample if necessary. + missing_footer_starts = footer_starts.copy() + large_footer = 0 + for i, path in enumerate(paths): + footer_size = int.from_bytes(footer_samples[i][-8:-4], "little") + real_footer_start = file_sizes[i] - (footer_size + 8) + if real_footer_start < footer_starts[i]: + missing_footer_starts[i] = real_footer_start + large_footer = max(large_footer, (footer_size + 8)) + if large_footer: + warnings.warn( + f"Not enough data was used to sample the parquet footer. " + f"Try setting footer_sample_size >= {large_footer}." + ) + for i, block in enumerate( + fs.cat_ranges( + paths, + missing_footer_starts, + footer_starts, + ) + ): + footer_samples[i] = block + footer_samples[i] + footer_starts[i] = missing_footer_starts[i] + + # Calculate required byte ranges for each path + for i, path in enumerate(paths): + # Deal with small-file case. + # Just include all remaining bytes of the file + # in a single range. + if file_sizes[i] < max_block: + if footer_starts[i] > 0: + # Only need to transfer the data if the + # footer sample isn't already the whole file + data_paths.append(path) + data_starts.append(0) + data_ends.append(footer_starts[i]) + continue + + # Use "engine" to collect data byte ranges + path_data_starts, path_data_ends = engine._parquet_byte_ranges( + columns, + row_groups=row_groups, + footer=footer_samples[i], + footer_start=footer_starts[i], + ) + + data_paths += [path] * len(path_data_starts) + data_starts += path_data_starts + data_ends += path_data_ends + + # Merge adjacent offset ranges + data_paths, data_starts, data_ends = merge_offset_ranges( + data_paths, + data_starts, + data_ends, + max_gap=max_gap, + max_block=max_block, + sort=False, # Should already be sorted + ) + + # Start by populating `result` with footer samples + for i, path in enumerate(paths): + result[path] = {(footer_starts[i], footer_ends[i]): footer_samples[i]} + + # Transfer the data byte-ranges into local memory + _transfer_ranges(fs, result, data_paths, data_starts, data_ends) + + # Add b"PAR1" to header if necessary + if add_header_magic: + _add_header_magic(result) + + return result + + +def _get_parquet_byte_ranges_from_metadata( + metadata, + fs, + engine, + columns=None, + row_groups=None, + max_gap=64_000, + max_block=256_000_000, +): + """Simplified version of `_get_parquet_byte_ranges` for + the case that an engine-specific `metadata` object is + provided, and the remote footer metadata does not need to + be transferred before calculating the required byte ranges. + """ + + # Use "engine" to collect data byte ranges + data_paths, data_starts, data_ends = engine._parquet_byte_ranges( + columns, + row_groups=row_groups, + metadata=metadata, + ) + + # Merge adjacent offset ranges + data_paths, data_starts, data_ends = merge_offset_ranges( + data_paths, + data_starts, + data_ends, + max_gap=max_gap, + max_block=max_block, + sort=False, # Should be sorted + ) + + # Transfer the data byte-ranges into local memory + result = {fn: {} for fn in list(set(data_paths))} + _transfer_ranges(fs, result, data_paths, data_starts, data_ends) + + # Add b"PAR1" to header + _add_header_magic(result) + + return result + + +def _transfer_ranges(fs, blocks, paths, starts, ends): + # Use cat_ranges to gather the data byte_ranges + ranges = (paths, starts, ends) + for path, start, stop, data in zip(*ranges, fs.cat_ranges(*ranges)): + blocks[path][(start, stop)] = data + + +def _add_header_magic(data): + # Add b"PAR1" to file headers + for path in list(data.keys()): + add_magic = True + for k in data[path]: + if k[0] == 0 and k[1] >= 4: + add_magic = False + break + if add_magic: + data[path][(0, 4)] = b"PAR1" + + +def _set_engine(engine_str): + # Define a list of parquet engines to try + if engine_str == "auto": + try_engines = ("fastparquet", "pyarrow") + elif not isinstance(engine_str, str): + raise ValueError( + "Failed to set parquet engine! " + "Please pass 'fastparquet', 'pyarrow', or 'auto'" + ) + elif engine_str not in ("fastparquet", "pyarrow"): + raise ValueError(f"{engine_str} engine not supported by `fsspec.parquet`") + else: + try_engines = [engine_str] + + # Try importing the engines in `try_engines`, + # and choose the first one that succeeds + for engine in try_engines: + try: + if engine == "fastparquet": + return FastparquetEngine() + elif engine == "pyarrow": + return PyarrowEngine() + except ImportError: + pass + + # Raise an error if a supported parquet engine + # was not found + raise ImportError( + f"The following parquet engines are not installed " + f"in your python environment: {try_engines}." + f"Please install 'fastparquert' or 'pyarrow' to " + f"utilize the `fsspec.parquet` module." + ) + + +class FastparquetEngine: + # The purpose of the FastparquetEngine class is + # to check if fastparquet can be imported (on initialization) + # and to define a `_parquet_byte_ranges` method. In the + # future, this class may also be used to define other + # methods/logic that are specific to fastparquet. + + def __init__(self): + import fastparquet as fp + + self.fp = fp + + def _row_group_filename(self, row_group, pf): + return pf.row_group_filename(row_group) + + def _parquet_byte_ranges( + self, + columns, + row_groups=None, + metadata=None, + footer=None, + footer_start=None, + ): + # Initialize offset ranges and define ParqetFile metadata + pf = metadata + data_paths, data_starts, data_ends = [], [], [] + if pf is None: + pf = self.fp.ParquetFile(io.BytesIO(footer)) + + # Convert columns to a set and add any index columns + # specified in the pandas metadata (just in case) + column_set = None if columns is None else set(columns) + if column_set is not None and hasattr(pf, "pandas_metadata"): + md_index = [ + ind + for ind in pf.pandas_metadata.get("index_columns", []) + # Ignore RangeIndex information + if not isinstance(ind, dict) + ] + column_set |= set(md_index) + + # Check if row_groups is a list of integers + # or a list of row-group metadata + if row_groups and not isinstance(row_groups[0], int): + # Input row_groups contains row-group metadata + row_group_indices = None + else: + # Input row_groups contains row-group indices + row_group_indices = row_groups + row_groups = pf.row_groups + + # Loop through column chunks to add required byte ranges + for r, row_group in enumerate(row_groups): + # Skip this row-group if we are targeting + # specific row-groups + if row_group_indices is None or r in row_group_indices: + # Find the target parquet-file path for `row_group` + fn = self._row_group_filename(row_group, pf) + + for column in row_group.columns: + name = column.meta_data.path_in_schema[0] + # Skip this column if we are targeting a + # specific columns + if column_set is None or name in column_set: + file_offset0 = column.meta_data.dictionary_page_offset + if file_offset0 is None: + file_offset0 = column.meta_data.data_page_offset + num_bytes = column.meta_data.total_compressed_size + if footer_start is None or file_offset0 < footer_start: + data_paths.append(fn) + data_starts.append(file_offset0) + data_ends.append( + min( + file_offset0 + num_bytes, + footer_start or (file_offset0 + num_bytes), + ) + ) + + if metadata: + # The metadata in this call may map to multiple + # file paths. Need to include `data_paths` + return data_paths, data_starts, data_ends + return data_starts, data_ends + + +class PyarrowEngine: + # The purpose of the PyarrowEngine class is + # to check if pyarrow can be imported (on initialization) + # and to define a `_parquet_byte_ranges` method. In the + # future, this class may also be used to define other + # methods/logic that are specific to pyarrow. + + def __init__(self): + import pyarrow.parquet as pq + + self.pq = pq + + def _row_group_filename(self, row_group, metadata): + raise NotImplementedError + + def _parquet_byte_ranges( + self, + columns, + row_groups=None, + metadata=None, + footer=None, + footer_start=None, + ): + if metadata is not None: + raise ValueError("metadata input not supported for PyarrowEngine") + + data_starts, data_ends = [], [] + md = self.pq.ParquetFile(io.BytesIO(footer)).metadata + + # Convert columns to a set and add any index columns + # specified in the pandas metadata (just in case) + column_set = None if columns is None else set(columns) + if column_set is not None: + schema = md.schema.to_arrow_schema() + has_pandas_metadata = ( + schema.metadata is not None and b"pandas" in schema.metadata + ) + if has_pandas_metadata: + md_index = [ + ind + for ind in json.loads( + schema.metadata[b"pandas"].decode("utf8") + ).get("index_columns", []) + # Ignore RangeIndex information + if not isinstance(ind, dict) + ] + column_set |= set(md_index) + + # Loop through column chunks to add required byte ranges + for r in range(md.num_row_groups): + # Skip this row-group if we are targeting + # specific row-groups + if row_groups is None or r in row_groups: + row_group = md.row_group(r) + for c in range(row_group.num_columns): + column = row_group.column(c) + name = column.path_in_schema + # Skip this column if we are targeting a + # specific columns + split_name = name.split(".")[0] + if ( + column_set is None + or name in column_set + or split_name in column_set + ): + file_offset0 = column.dictionary_page_offset + if file_offset0 is None: + file_offset0 = column.data_page_offset + num_bytes = column.total_compressed_size + if file_offset0 < footer_start: + data_starts.append(file_offset0) + data_ends.append( + min(file_offset0 + num_bytes, footer_start) + ) + return data_starts, data_ends diff --git a/.venv/lib/python3.12/site-packages/fsspec/registry.py b/.venv/lib/python3.12/site-packages/fsspec/registry.py new file mode 100644 index 0000000000000000000000000000000000000000..96ffad7f4a889662c8fb4a006e1c497652992430 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec/registry.py @@ -0,0 +1,330 @@ +from __future__ import annotations + +import importlib +import types +import warnings + +__all__ = ["registry", "get_filesystem_class", "default"] + +# internal, mutable +_registry: dict[str, type] = {} + +# external, immutable +registry = types.MappingProxyType(_registry) +default = "file" + + +def register_implementation(name, cls, clobber=False, errtxt=None): + """Add implementation class to the registry + + Parameters + ---------- + name: str + Protocol name to associate with the class + cls: class or str + if a class: fsspec-compliant implementation class (normally inherits from + ``fsspec.AbstractFileSystem``, gets added straight to the registry. If a + str, the full path to an implementation class like package.module.class, + which gets added to known_implementations, + so the import is deferred until the filesystem is actually used. + clobber: bool (optional) + Whether to overwrite a protocol with the same name; if False, will raise + instead. + errtxt: str (optional) + If given, then a failure to import the given class will result in this + text being given. + """ + if isinstance(cls, str): + if name in known_implementations and clobber is False: + if cls != known_implementations[name]["class"]: + raise ValueError( + f"Name ({name}) already in the known_implementations and clobber " + f"is False" + ) + else: + known_implementations[name] = { + "class": cls, + "err": errtxt or f"{cls} import failed for protocol {name}", + } + + else: + if name in registry and clobber is False: + if _registry[name] is not cls: + raise ValueError( + f"Name ({name}) already in the registry and clobber is False" + ) + else: + _registry[name] = cls + + +# protocols mapped to the class which implements them. This dict can be +# updated with register_implementation +known_implementations = { + "abfs": { + "class": "adlfs.AzureBlobFileSystem", + "err": "Install adlfs to access Azure Datalake Gen2 and Azure Blob Storage", + }, + "adl": { + "class": "adlfs.AzureDatalakeFileSystem", + "err": "Install adlfs to access Azure Datalake Gen1", + }, + "arrow_hdfs": { + "class": "fsspec.implementations.arrow.HadoopFileSystem", + "err": "pyarrow and local java libraries required for HDFS", + }, + "asynclocal": { + "class": "morefs.asyn_local.AsyncLocalFileSystem", + "err": "Install 'morefs[asynclocalfs]' to use AsyncLocalFileSystem", + }, + "asyncwrapper": { + "class": "fsspec.implementations.asyn_wrapper.AsyncFileSystemWrapper", + }, + "az": { + "class": "adlfs.AzureBlobFileSystem", + "err": "Install adlfs to access Azure Datalake Gen2 and Azure Blob Storage", + }, + "blockcache": {"class": "fsspec.implementations.cached.CachingFileSystem"}, + "box": { + "class": "boxfs.BoxFileSystem", + "err": "Please install boxfs to access BoxFileSystem", + }, + "cached": {"class": "fsspec.implementations.cached.CachingFileSystem"}, + "dask": { + "class": "fsspec.implementations.dask.DaskWorkerFileSystem", + "err": "Install dask distributed to access worker file system", + }, + "data": {"class": "fsspec.implementations.data.DataFileSystem"}, + "dbfs": { + "class": "fsspec.implementations.dbfs.DatabricksFileSystem", + "err": "Install the requests package to use the DatabricksFileSystem", + }, + "dir": {"class": "fsspec.implementations.dirfs.DirFileSystem"}, + "dropbox": { + "class": "dropboxdrivefs.DropboxDriveFileSystem", + "err": ( + 'DropboxFileSystem requires "dropboxdrivefs","requests" and "' + '"dropbox" to be installed' + ), + }, + "dvc": { + "class": "dvc.api.DVCFileSystem", + "err": "Install dvc to access DVCFileSystem", + }, + "file": {"class": "fsspec.implementations.local.LocalFileSystem"}, + "filecache": {"class": "fsspec.implementations.cached.WholeFileCacheFileSystem"}, + "ftp": {"class": "fsspec.implementations.ftp.FTPFileSystem"}, + "gcs": { + "class": "gcsfs.GCSFileSystem", + "err": "Please install gcsfs to access Google Storage", + }, + "gdrive": { + "class": "gdrive_fsspec.GoogleDriveFileSystem", + "err": "Please install gdrive_fs for access to Google Drive", + }, + "generic": {"class": "fsspec.generic.GenericFileSystem"}, + "gist": { + "class": "fsspec.implementations.gist.GistFileSystem", + "err": "Install the requests package to use the gist FS", + }, + "git": { + "class": "fsspec.implementations.git.GitFileSystem", + "err": "Install pygit2 to browse local git repos", + }, + "github": { + "class": "fsspec.implementations.github.GithubFileSystem", + "err": "Install the requests package to use the github FS", + }, + "gs": { + "class": "gcsfs.GCSFileSystem", + "err": "Please install gcsfs to access Google Storage", + }, + "hdfs": { + "class": "fsspec.implementations.arrow.HadoopFileSystem", + "err": "pyarrow and local java libraries required for HDFS", + }, + "hf": { + "class": "huggingface_hub.HfFileSystem", + "err": "Install huggingface_hub to access HfFileSystem", + }, + "http": { + "class": "fsspec.implementations.http.HTTPFileSystem", + "err": 'HTTPFileSystem requires "requests" and "aiohttp" to be installed', + }, + "https": { + "class": "fsspec.implementations.http.HTTPFileSystem", + "err": 'HTTPFileSystem requires "requests" and "aiohttp" to be installed', + }, + "jlab": { + "class": "fsspec.implementations.jupyter.JupyterFileSystem", + "err": "Jupyter FS requires requests to be installed", + }, + "jupyter": { + "class": "fsspec.implementations.jupyter.JupyterFileSystem", + "err": "Jupyter FS requires requests to be installed", + }, + "lakefs": { + "class": "lakefs_spec.LakeFSFileSystem", + "err": "Please install lakefs-spec to access LakeFSFileSystem", + }, + "libarchive": { + "class": "fsspec.implementations.libarchive.LibArchiveFileSystem", + "err": "LibArchive requires to be installed", + }, + "local": {"class": "fsspec.implementations.local.LocalFileSystem"}, + "memory": {"class": "fsspec.implementations.memory.MemoryFileSystem"}, + "oci": { + "class": "ocifs.OCIFileSystem", + "err": "Install ocifs to access OCI Object Storage", + }, + "ocilake": { + "class": "ocifs.OCIFileSystem", + "err": "Install ocifs to access OCI Data Lake", + }, + "oss": { + "class": "ossfs.OSSFileSystem", + "err": "Install ossfs to access Alibaba Object Storage System", + }, + "pyscript": { + "class": "pyscript_fsspec_client.client.PyscriptFileSystem", + "err": "Install requests (cpython) or run in pyscript", + }, + "reference": {"class": "fsspec.implementations.reference.ReferenceFileSystem"}, + "root": { + "class": "fsspec_xrootd.XRootDFileSystem", + "err": ( + "Install fsspec-xrootd to access xrootd storage system. " + "Note: 'root' is the protocol name for xrootd storage systems, " + "not referring to root directories" + ), + }, + "s3": {"class": "s3fs.S3FileSystem", "err": "Install s3fs to access S3"}, + "s3a": {"class": "s3fs.S3FileSystem", "err": "Install s3fs to access S3"}, + "sftp": { + "class": "fsspec.implementations.sftp.SFTPFileSystem", + "err": 'SFTPFileSystem requires "paramiko" to be installed', + }, + "simplecache": {"class": "fsspec.implementations.cached.SimpleCacheFileSystem"}, + "smb": { + "class": "fsspec.implementations.smb.SMBFileSystem", + "err": 'SMB requires "smbprotocol" or "smbprotocol[kerberos]" installed', + }, + "ssh": { + "class": "fsspec.implementations.sftp.SFTPFileSystem", + "err": 'SFTPFileSystem requires "paramiko" to be installed', + }, + "tar": {"class": "fsspec.implementations.tar.TarFileSystem"}, + "tos": { + "class": "tosfs.TosFileSystem", + "err": "Install tosfs to access ByteDance volcano engine Tinder Object Storage", + }, + "tosfs": { + "class": "tosfs.TosFileSystem", + "err": "Install tosfs to access ByteDance volcano engine Tinder Object Storage", + }, + "wandb": {"class": "wandbfs.WandbFS", "err": "Install wandbfs to access wandb"}, + "webdav": { + "class": "webdav4.fsspec.WebdavFileSystem", + "err": "Install webdav4 to access WebDAV", + }, + "webhdfs": { + "class": "fsspec.implementations.webhdfs.WebHDFS", + "err": 'webHDFS access requires "requests" to be installed', + }, + "zip": {"class": "fsspec.implementations.zip.ZipFileSystem"}, +} + +assert list(known_implementations) == sorted(known_implementations), ( + "Not in alphabetical order" +) + + +def get_filesystem_class(protocol): + """Fetch named protocol implementation from the registry + + The dict ``known_implementations`` maps protocol names to the locations + of classes implementing the corresponding file-system. When used for the + first time, appropriate imports will happen and the class will be placed in + the registry. All subsequent calls will fetch directly from the registry. + + Some protocol implementations require additional dependencies, and so the + import may fail. In this case, the string in the "err" field of the + ``known_implementations`` will be given as the error message. + """ + if not protocol: + protocol = default + + if protocol not in registry: + if protocol not in known_implementations: + raise ValueError(f"Protocol not known: {protocol}") + bit = known_implementations[protocol] + try: + register_implementation(protocol, _import_class(bit["class"])) + except ImportError as e: + raise ImportError(bit.get("err")) from e + cls = registry[protocol] + if getattr(cls, "protocol", None) in ("abstract", None): + cls.protocol = protocol + + return cls + + +s3_msg = """Your installed version of s3fs is very old and known to cause +severe performance issues, see also https://github.com/dask/dask/issues/10276 + +To fix, you should specify a lower version bound on s3fs, or +update the current installation. +""" + + +def _import_class(fqp: str): + """Take a fully-qualified path and return the imported class or identifier. + + ``fqp`` is of the form "package.module.klass" or + "package.module:subobject.klass". + + Warnings + -------- + This can import arbitrary modules. Make sure you haven't installed any modules + that may execute malicious code at import time. + """ + if ":" in fqp: + mod, name = fqp.rsplit(":", 1) + else: + mod, name = fqp.rsplit(".", 1) + + is_s3 = mod == "s3fs" + mod = importlib.import_module(mod) + if is_s3 and mod.__version__.split(".") < ["0", "5"]: + warnings.warn(s3_msg) + for part in name.split("."): + mod = getattr(mod, part) + + if not isinstance(mod, type): + raise TypeError(f"{fqp} is not a class") + + return mod + + +def filesystem(protocol, **storage_options): + """Instantiate filesystems for given protocol and arguments + + ``storage_options`` are specific to the protocol being chosen, and are + passed directly to the class. + """ + if protocol == "arrow_hdfs": + warnings.warn( + "The 'arrow_hdfs' protocol has been deprecated and will be " + "removed in the future. Specify it as 'hdfs'.", + DeprecationWarning, + ) + + cls = get_filesystem_class(protocol) + return cls(**storage_options) + + +def available_protocols(): + """Return a list of the implemented protocols. + + Note that any given protocol may require extra packages to be importable. + """ + return list(known_implementations) diff --git a/.venv/lib/python3.12/site-packages/fsspec/spec.py b/.venv/lib/python3.12/site-packages/fsspec/spec.py new file mode 100644 index 0000000000000000000000000000000000000000..5f6f9a10441c12ef8db4870c4e5dc72262037c6e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec/spec.py @@ -0,0 +1,2270 @@ +from __future__ import annotations + +import io +import json +import logging +import os +import threading +import warnings +import weakref +from errno import ESPIPE +from glob import has_magic +from hashlib import sha256 +from typing import Any, ClassVar + +from .callbacks import DEFAULT_CALLBACK +from .config import apply_config, conf +from .dircache import DirCache +from .transaction import Transaction +from .utils import ( + _unstrip_protocol, + glob_translate, + isfilelike, + other_paths, + read_block, + stringify_path, + tokenize, +) + +logger = logging.getLogger("fsspec") + + +def make_instance(cls, args, kwargs): + return cls(*args, **kwargs) + + +class _Cached(type): + """ + Metaclass for caching file system instances. + + Notes + ----- + Instances are cached according to + + * The values of the class attributes listed in `_extra_tokenize_attributes` + * The arguments passed to ``__init__``. + + This creates an additional reference to the filesystem, which prevents the + filesystem from being garbage collected when all *user* references go away. + A call to the :meth:`AbstractFileSystem.clear_instance_cache` must *also* + be made for a filesystem instance to be garbage collected. + """ + + def __init__(cls, *args, **kwargs): + super().__init__(*args, **kwargs) + # Note: we intentionally create a reference here, to avoid garbage + # collecting instances when all other references are gone. To really + # delete a FileSystem, the cache must be cleared. + if conf.get("weakref_instance_cache"): # pragma: no cover + # debug option for analysing fork/spawn conditions + cls._cache = weakref.WeakValueDictionary() + else: + cls._cache = {} + cls._pid = os.getpid() + + def __call__(cls, *args, **kwargs): + kwargs = apply_config(cls, kwargs) + extra_tokens = tuple( + getattr(cls, attr, None) for attr in cls._extra_tokenize_attributes + ) + token = tokenize( + cls, cls._pid, threading.get_ident(), *args, *extra_tokens, **kwargs + ) + skip = kwargs.pop("skip_instance_cache", False) + if os.getpid() != cls._pid: + cls._cache.clear() + cls._pid = os.getpid() + if not skip and cls.cachable and token in cls._cache: + cls._latest = token + return cls._cache[token] + else: + obj = super().__call__(*args, **kwargs) + # Setting _fs_token here causes some static linters to complain. + obj._fs_token_ = token + obj.storage_args = args + obj.storage_options = kwargs + if obj.async_impl and obj.mirror_sync_methods: + from .asyn import mirror_sync_methods + + mirror_sync_methods(obj) + + if cls.cachable and not skip: + cls._latest = token + cls._cache[token] = obj + return obj + + +class AbstractFileSystem(metaclass=_Cached): + """ + An abstract super-class for pythonic file-systems + + Implementations are expected to be compatible with or, better, subclass + from here. + """ + + cachable = True # this class can be cached, instances reused + _cached = False + blocksize = 2**22 + sep = "/" + protocol: ClassVar[str | tuple[str, ...]] = "abstract" + _latest = None + async_impl = False + mirror_sync_methods = False + root_marker = "" # For some FSs, may require leading '/' or other character + transaction_type = Transaction + + #: Extra *class attributes* that should be considered when hashing. + _extra_tokenize_attributes = () + + # Set by _Cached metaclass + storage_args: tuple[Any, ...] + storage_options: dict[str, Any] + + def __init__(self, *args, **storage_options): + """Create and configure file-system instance + + Instances may be cachable, so if similar enough arguments are seen + a new instance is not required. The token attribute exists to allow + implementations to cache instances if they wish. + + A reasonable default should be provided if there are no arguments. + + Subclasses should call this method. + + Parameters + ---------- + use_listings_cache, listings_expiry_time, max_paths: + passed to ``DirCache``, if the implementation supports + directory listing caching. Pass use_listings_cache=False + to disable such caching. + skip_instance_cache: bool + If this is a cachable implementation, pass True here to force + creating a new instance even if a matching instance exists, and prevent + storing this instance. + asynchronous: bool + loop: asyncio-compatible IOLoop or None + """ + if self._cached: + # reusing instance, don't change + return + self._cached = True + self._intrans = False + self._transaction = None + self._invalidated_caches_in_transaction = [] + self.dircache = DirCache(**storage_options) + + if storage_options.pop("add_docs", None): + warnings.warn("add_docs is no longer supported.", FutureWarning) + + if storage_options.pop("add_aliases", None): + warnings.warn("add_aliases has been removed.", FutureWarning) + # This is set in _Cached + self._fs_token_ = None + + @property + def fsid(self): + """Persistent filesystem id that can be used to compare filesystems + across sessions. + """ + raise NotImplementedError + + @property + def _fs_token(self): + return self._fs_token_ + + def __dask_tokenize__(self): + return self._fs_token + + def __hash__(self): + return int(self._fs_token, 16) + + def __eq__(self, other): + return isinstance(other, type(self)) and self._fs_token == other._fs_token + + def __reduce__(self): + return make_instance, (type(self), self.storage_args, self.storage_options) + + @classmethod + def _strip_protocol(cls, path): + """Turn path from fully-qualified to file-system-specific + + May require FS-specific handling, e.g., for relative paths or links. + """ + if isinstance(path, list): + return [cls._strip_protocol(p) for p in path] + path = stringify_path(path) + protos = (cls.protocol,) if isinstance(cls.protocol, str) else cls.protocol + for protocol in protos: + if path.startswith(protocol + "://"): + path = path[len(protocol) + 3 :] + elif path.startswith(protocol + "::"): + path = path[len(protocol) + 2 :] + path = path.rstrip("/") + # use of root_marker to make minimum required path, e.g., "/" + return path or cls.root_marker + + def unstrip_protocol(self, name: str) -> str: + """Format FS-specific path to generic, including protocol""" + protos = (self.protocol,) if isinstance(self.protocol, str) else self.protocol + for protocol in protos: + if name.startswith(f"{protocol}://"): + return name + return f"{protos[0]}://{name}" + + @staticmethod + def _get_kwargs_from_urls(path): + """If kwargs can be encoded in the paths, extract them here + + This should happen before instantiation of the class; incoming paths + then should be amended to strip the options in methods. + + Examples may look like an sftp path "sftp://user@host:/my/path", where + the user and host should become kwargs and later get stripped. + """ + # by default, nothing happens + return {} + + @classmethod + def current(cls): + """Return the most recently instantiated FileSystem + + If no instance has been created, then create one with defaults + """ + if cls._latest in cls._cache: + return cls._cache[cls._latest] + return cls() + + @property + def transaction(self): + """A context within which files are committed together upon exit + + Requires the file class to implement `.commit()` and `.discard()` + for the normal and exception cases. + """ + if self._transaction is None: + self._transaction = self.transaction_type(self) + return self._transaction + + def start_transaction(self): + """Begin write transaction for deferring files, non-context version""" + self._intrans = True + self._transaction = self.transaction_type(self) + return self.transaction + + def end_transaction(self): + """Finish write transaction, non-context version""" + self.transaction.complete() + self._transaction = None + # The invalid cache must be cleared after the transaction is completed. + for path in self._invalidated_caches_in_transaction: + self.invalidate_cache(path) + self._invalidated_caches_in_transaction.clear() + + def invalidate_cache(self, path=None): + """ + Discard any cached directory information + + Parameters + ---------- + path: string or None + If None, clear all listings cached else listings at or under given + path. + """ + # Not necessary to implement invalidation mechanism, may have no cache. + # But if have, you should call this method of parent class from your + # subclass to ensure expiring caches after transacations correctly. + # See the implementation of FTPFileSystem in ftp.py + if self._intrans: + self._invalidated_caches_in_transaction.append(path) + + def mkdir(self, path, create_parents=True, **kwargs): + """ + Create directory entry at path + + For systems that don't have true directories, may create an for + this instance only and not touch the real filesystem + + Parameters + ---------- + path: str + location + create_parents: bool + if True, this is equivalent to ``makedirs`` + kwargs: + may be permissions, etc. + """ + pass # not necessary to implement, may not have directories + + def makedirs(self, path, exist_ok=False): + """Recursively make directories + + Creates directory at path and any intervening required directories. + Raises exception if, for instance, the path already exists but is a + file. + + Parameters + ---------- + path: str + leaf directory name + exist_ok: bool (False) + If False, will error if the target already exists + """ + pass # not necessary to implement, may not have directories + + def rmdir(self, path): + """Remove a directory, if empty""" + pass # not necessary to implement, may not have directories + + def ls(self, path, detail=True, **kwargs): + """List objects at path. + + This should include subdirectories and files at that location. The + difference between a file and a directory must be clear when details + are requested. + + The specific keys, or perhaps a FileInfo class, or similar, is TBD, + but must be consistent across implementations. + Must include: + + - full path to the entry (without protocol) + - size of the entry, in bytes. If the value cannot be determined, will + be ``None``. + - type of entry, "file", "directory" or other + + Additional information + may be present, appropriate to the file-system, e.g., generation, + checksum, etc. + + May use refresh=True|False to allow use of self._ls_from_cache to + check for a saved listing and avoid calling the backend. This would be + common where listing may be expensive. + + Parameters + ---------- + path: str + detail: bool + if True, gives a list of dictionaries, where each is the same as + the result of ``info(path)``. If False, gives a list of paths + (str). + kwargs: may have additional backend-specific options, such as version + information + + Returns + ------- + List of strings if detail is False, or list of directory information + dicts if detail is True. + """ + raise NotImplementedError + + def _ls_from_cache(self, path): + """Check cache for listing + + Returns listing, if found (may be empty list for a directly that exists + but contains nothing), None if not in cache. + """ + parent = self._parent(path) + try: + return self.dircache[path.rstrip("/")] + except KeyError: + pass + try: + files = [ + f + for f in self.dircache[parent] + if f["name"] == path + or (f["name"] == path.rstrip("/") and f["type"] == "directory") + ] + if len(files) == 0: + # parent dir was listed but did not contain this file + raise FileNotFoundError(path) + return files + except KeyError: + pass + + def walk(self, path, maxdepth=None, topdown=True, on_error="omit", **kwargs): + """Return all files under the given path. + + List all files, recursing into subdirectories; output is iterator-style, + like ``os.walk()``. For a simple list of files, ``find()`` is available. + + When topdown is True, the caller can modify the dirnames list in-place (perhaps + using del or slice assignment), and walk() will + only recurse into the subdirectories whose names remain in dirnames; + this can be used to prune the search, impose a specific order of visiting, + or even to inform walk() about directories the caller creates or renames before + it resumes walk() again. + Modifying dirnames when topdown is False has no effect. (see os.walk) + + Note that the "files" outputted will include anything that is not + a directory, such as links. + + Parameters + ---------- + path: str + Root to recurse into + maxdepth: int + Maximum recursion depth. None means limitless, but not recommended + on link-based file-systems. + topdown: bool (True) + Whether to walk the directory tree from the top downwards or from + the bottom upwards. + on_error: "omit", "raise", a callable + if omit (default), path with exception will simply be empty; + If raise, an underlying exception will be raised; + if callable, it will be called with a single OSError instance as argument + kwargs: passed to ``ls`` + """ + if maxdepth is not None and maxdepth < 1: + raise ValueError("maxdepth must be at least 1") + + path = self._strip_protocol(path) + full_dirs = {} + dirs = {} + files = {} + + detail = kwargs.pop("detail", False) + try: + listing = self.ls(path, detail=True, **kwargs) + except (FileNotFoundError, OSError) as e: + if on_error == "raise": + raise + if callable(on_error): + on_error(e) + return + + for info in listing: + # each info name must be at least [path]/part , but here + # we check also for names like [path]/part/ + pathname = info["name"].rstrip("/") + name = pathname.rsplit("/", 1)[-1] + if info["type"] == "directory" and pathname != path: + # do not include "self" path + full_dirs[name] = pathname + dirs[name] = info + elif pathname == path: + # file-like with same name as give path + files[""] = info + else: + files[name] = info + + if not detail: + dirs = list(dirs) + files = list(files) + + if topdown: + # Yield before recursion if walking top down + yield path, dirs, files + + if maxdepth is not None: + maxdepth -= 1 + if maxdepth < 1: + if not topdown: + yield path, dirs, files + return + + for d in dirs: + yield from self.walk( + full_dirs[d], + maxdepth=maxdepth, + detail=detail, + topdown=topdown, + **kwargs, + ) + + if not topdown: + # Yield after recursion if walking bottom up + yield path, dirs, files + + def find(self, path, maxdepth=None, withdirs=False, detail=False, **kwargs): + """List all files below path. + + Like posix ``find`` command without conditions + + Parameters + ---------- + path : str + maxdepth: int or None + If not None, the maximum number of levels to descend + withdirs: bool + Whether to include directory paths in the output. This is True + when used by glob, but users usually only want files. + kwargs are passed to ``ls``. + """ + # TODO: allow equivalent of -name parameter + path = self._strip_protocol(path) + out = {} + + # Add the root directory if withdirs is requested + # This is needed for posix glob compliance + if withdirs and path != "" and self.isdir(path): + out[path] = self.info(path) + + for _, dirs, files in self.walk(path, maxdepth, detail=True, **kwargs): + if withdirs: + files.update(dirs) + out.update({info["name"]: info for name, info in files.items()}) + if not out and self.isfile(path): + # walk works on directories, but find should also return [path] + # when path happens to be a file + out[path] = {} + names = sorted(out) + if not detail: + return names + else: + return {name: out[name] for name in names} + + def du(self, path, total=True, maxdepth=None, withdirs=False, **kwargs): + """Space used by files and optionally directories within a path + + Directory size does not include the size of its contents. + + Parameters + ---------- + path: str + total: bool + Whether to sum all the file sizes + maxdepth: int or None + Maximum number of directory levels to descend, None for unlimited. + withdirs: bool + Whether to include directory paths in the output. + kwargs: passed to ``find`` + + Returns + ------- + Dict of {path: size} if total=False, or int otherwise, where numbers + refer to bytes used. + """ + sizes = {} + if withdirs and self.isdir(path): + # Include top-level directory in output + info = self.info(path) + sizes[info["name"]] = info["size"] + for f in self.find(path, maxdepth=maxdepth, withdirs=withdirs, **kwargs): + info = self.info(f) + sizes[info["name"]] = info["size"] + if total: + return sum(sizes.values()) + else: + return sizes + + def glob(self, path, maxdepth=None, **kwargs): + """Find files by glob-matching. + + Pattern matching capabilities for finding files that match the given pattern. + + Parameters + ---------- + path: str + The glob pattern to match against + maxdepth: int or None + Maximum depth for ``'**'`` patterns. Applied on the first ``'**'`` found. + Must be at least 1 if provided. + kwargs: + Additional arguments passed to ``find`` (e.g., detail=True) + + Returns + ------- + List of matched paths, or dict of paths and their info if detail=True + + Notes + ----- + Supported patterns: + - '*': Matches any sequence of characters within a single directory level + - ``'**'``: Matches any number of directory levels (must be an entire path component) + - '?': Matches exactly one character + - '[abc]': Matches any character in the set + - '[a-z]': Matches any character in the range + - '[!abc]': Matches any character NOT in the set + + Special behaviors: + - If the path ends with '/', only folders are returned + - Consecutive '*' characters are compressed into a single '*' + - Empty brackets '[]' never match anything + - Negated empty brackets '[!]' match any single character + - Special characters in character classes are escaped properly + + Limitations: + - ``'**'`` must be a complete path component (e.g., ``'a/**/b'``, not ``'a**b'``) + - No brace expansion ('{a,b}.txt') + - No extended glob patterns ('+(pattern)', '!(pattern)') + """ + if maxdepth is not None and maxdepth < 1: + raise ValueError("maxdepth must be at least 1") + + import re + + seps = (os.path.sep, os.path.altsep) if os.path.altsep else (os.path.sep,) + ends_with_sep = path.endswith(seps) # _strip_protocol strips trailing slash + path = self._strip_protocol(path) + append_slash_to_dirname = ends_with_sep or path.endswith( + tuple(sep + "**" for sep in seps) + ) + idx_star = path.find("*") if path.find("*") >= 0 else len(path) + idx_qmark = path.find("?") if path.find("?") >= 0 else len(path) + idx_brace = path.find("[") if path.find("[") >= 0 else len(path) + + min_idx = min(idx_star, idx_qmark, idx_brace) + + detail = kwargs.pop("detail", False) + + if not has_magic(path): + if self.exists(path, **kwargs): + if not detail: + return [path] + else: + return {path: self.info(path, **kwargs)} + else: + if not detail: + return [] # glob of non-existent returns empty + else: + return {} + elif "/" in path[:min_idx]: + min_idx = path[:min_idx].rindex("/") + root = path[: min_idx + 1] + depth = path[min_idx + 1 :].count("/") + 1 + else: + root = "" + depth = path[min_idx + 1 :].count("/") + 1 + + if "**" in path: + if maxdepth is not None: + idx_double_stars = path.find("**") + depth_double_stars = path[idx_double_stars:].count("/") + 1 + depth = depth - depth_double_stars + maxdepth + else: + depth = None + + allpaths = self.find(root, maxdepth=depth, withdirs=True, detail=True, **kwargs) + + pattern = glob_translate(path + ("/" if ends_with_sep else "")) + pattern = re.compile(pattern) + + out = { + p: info + for p, info in sorted(allpaths.items()) + if pattern.match( + p + "/" + if append_slash_to_dirname and info["type"] == "directory" + else p + ) + } + + if detail: + return out + else: + return list(out) + + def exists(self, path, **kwargs): + """Is there a file at the given path""" + try: + self.info(path, **kwargs) + return True + except: # noqa: E722 + # any exception allowed bar FileNotFoundError? + return False + + def lexists(self, path, **kwargs): + """If there is a file at the given path (including + broken links)""" + return self.exists(path) + + def info(self, path, **kwargs): + """Give details of entry at path + + Returns a single dictionary, with exactly the same information as ``ls`` + would with ``detail=True``. + + The default implementation calls ls and could be overridden by a + shortcut. kwargs are passed on to ```ls()``. + + Some file systems might not be able to measure the file's size, in + which case, the returned dict will include ``'size': None``. + + Returns + ------- + dict with keys: name (full path in the FS), size (in bytes), type (file, + directory, or something else) and other FS-specific keys. + """ + path = self._strip_protocol(path) + out = self.ls(self._parent(path), detail=True, **kwargs) + out = [o for o in out if o["name"].rstrip("/") == path] + if out: + return out[0] + out = self.ls(path, detail=True, **kwargs) + path = path.rstrip("/") + out1 = [o for o in out if o["name"].rstrip("/") == path] + if len(out1) == 1: + if "size" not in out1[0]: + out1[0]["size"] = None + return out1[0] + elif len(out1) > 1 or out: + return {"name": path, "size": 0, "type": "directory"} + else: + raise FileNotFoundError(path) + + def checksum(self, path): + """Unique value for current version of file + + If the checksum is the same from one moment to another, the contents + are guaranteed to be the same. If the checksum changes, the contents + *might* have changed. + + This should normally be overridden; default will probably capture + creation/modification timestamp (which would be good) or maybe + access timestamp (which would be bad) + """ + return int(tokenize(self.info(path)), 16) + + def size(self, path): + """Size in bytes of file""" + return self.info(path).get("size", None) + + def sizes(self, paths): + """Size in bytes of each file in a list of paths""" + return [self.size(p) for p in paths] + + def isdir(self, path): + """Is this entry directory-like?""" + try: + return self.info(path)["type"] == "directory" + except OSError: + return False + + def isfile(self, path): + """Is this entry file-like?""" + try: + return self.info(path)["type"] == "file" + except: # noqa: E722 + return False + + def read_text(self, path, encoding=None, errors=None, newline=None, **kwargs): + """Get the contents of the file as a string. + + Parameters + ---------- + path: str + URL of file on this filesystems + encoding, errors, newline: same as `open`. + """ + with self.open( + path, + mode="r", + encoding=encoding, + errors=errors, + newline=newline, + **kwargs, + ) as f: + return f.read() + + def write_text( + self, path, value, encoding=None, errors=None, newline=None, **kwargs + ): + """Write the text to the given file. + + An existing file will be overwritten. + + Parameters + ---------- + path: str + URL of file on this filesystems + value: str + Text to write. + encoding, errors, newline: same as `open`. + """ + with self.open( + path, + mode="w", + encoding=encoding, + errors=errors, + newline=newline, + **kwargs, + ) as f: + return f.write(value) + + def cat_file(self, path, start=None, end=None, **kwargs): + """Get the content of a file + + Parameters + ---------- + path: URL of file on this filesystems + start, end: int + Bytes limits of the read. If negative, backwards from end, + like usual python slices. Either can be None for start or + end of file, respectively + kwargs: passed to ``open()``. + """ + # explicitly set buffering off? + with self.open(path, "rb", **kwargs) as f: + if start is not None: + if start >= 0: + f.seek(start) + else: + f.seek(max(0, f.size + start)) + if end is not None: + if end < 0: + end = f.size + end + return f.read(end - f.tell()) + return f.read() + + def pipe_file(self, path, value, mode="overwrite", **kwargs): + """Set the bytes of given file""" + if mode == "create" and self.exists(path): + # non-atomic but simple way; or could use "xb" in open(), which is likely + # not as well supported + raise FileExistsError + with self.open(path, "wb", **kwargs) as f: + f.write(value) + + def pipe(self, path, value=None, **kwargs): + """Put value into path + + (counterpart to ``cat``) + + Parameters + ---------- + path: string or dict(str, bytes) + If a string, a single remote location to put ``value`` bytes; if a dict, + a mapping of {path: bytesvalue}. + value: bytes, optional + If using a single path, these are the bytes to put there. Ignored if + ``path`` is a dict + """ + if isinstance(path, str): + self.pipe_file(self._strip_protocol(path), value, **kwargs) + elif isinstance(path, dict): + for k, v in path.items(): + self.pipe_file(self._strip_protocol(k), v, **kwargs) + else: + raise ValueError("path must be str or dict") + + def cat_ranges( + self, paths, starts, ends, max_gap=None, on_error="return", **kwargs + ): + """Get the contents of byte ranges from one or more files + + Parameters + ---------- + paths: list + A list of of filepaths on this filesystems + starts, ends: int or list + Bytes limits of the read. If using a single int, the same value will be + used to read all the specified files. + """ + if max_gap is not None: + raise NotImplementedError + if not isinstance(paths, list): + raise TypeError + if not isinstance(starts, list): + starts = [starts] * len(paths) + if not isinstance(ends, list): + ends = [ends] * len(paths) + if len(starts) != len(paths) or len(ends) != len(paths): + raise ValueError + out = [] + for p, s, e in zip(paths, starts, ends): + try: + out.append(self.cat_file(p, s, e)) + except Exception as e: + if on_error == "return": + out.append(e) + else: + raise + return out + + def cat(self, path, recursive=False, on_error="raise", **kwargs): + """Fetch (potentially multiple) paths' contents + + Parameters + ---------- + recursive: bool + If True, assume the path(s) are directories, and get all the + contained files + on_error : "raise", "omit", "return" + If raise, an underlying exception will be raised (converted to KeyError + if the type is in self.missing_exceptions); if omit, keys with exception + will simply not be included in the output; if "return", all keys are + included in the output, but the value will be bytes or an exception + instance. + kwargs: passed to cat_file + + Returns + ------- + dict of {path: contents} if there are multiple paths + or the path has been otherwise expanded + """ + paths = self.expand_path(path, recursive=recursive) + if ( + len(paths) > 1 + or isinstance(path, list) + or paths[0] != self._strip_protocol(path) + ): + out = {} + for path in paths: + try: + out[path] = self.cat_file(path, **kwargs) + except Exception as e: + if on_error == "raise": + raise + if on_error == "return": + out[path] = e + return out + else: + return self.cat_file(paths[0], **kwargs) + + def get_file(self, rpath, lpath, callback=DEFAULT_CALLBACK, outfile=None, **kwargs): + """Copy single remote file to local""" + from .implementations.local import LocalFileSystem + + if isfilelike(lpath): + outfile = lpath + elif self.isdir(rpath): + os.makedirs(lpath, exist_ok=True) + return None + + fs = LocalFileSystem(auto_mkdir=True) + fs.makedirs(fs._parent(lpath), exist_ok=True) + + with self.open(rpath, "rb", **kwargs) as f1: + if outfile is None: + outfile = open(lpath, "wb") + + try: + callback.set_size(getattr(f1, "size", None)) + data = True + while data: + data = f1.read(self.blocksize) + segment_len = outfile.write(data) + if segment_len is None: + segment_len = len(data) + callback.relative_update(segment_len) + finally: + if not isfilelike(lpath): + outfile.close() + + def get( + self, + rpath, + lpath, + recursive=False, + callback=DEFAULT_CALLBACK, + maxdepth=None, + **kwargs, + ): + """Copy file(s) to local. + + Copies a specific file or tree of files (if recursive=True). If lpath + ends with a "/", it will be assumed to be a directory, and target files + will go within. Can submit a list of paths, which may be glob-patterns + and will be expanded. + + Calls get_file for each source. + """ + if isinstance(lpath, list) and isinstance(rpath, list): + # No need to expand paths when both source and destination + # are provided as lists + rpaths = rpath + lpaths = lpath + else: + from .implementations.local import ( + LocalFileSystem, + make_path_posix, + trailing_sep, + ) + + source_is_str = isinstance(rpath, str) + rpaths = self.expand_path(rpath, recursive=recursive, maxdepth=maxdepth) + if source_is_str and (not recursive or maxdepth is not None): + # Non-recursive glob does not copy directories + rpaths = [p for p in rpaths if not (trailing_sep(p) or self.isdir(p))] + if not rpaths: + return + + if isinstance(lpath, str): + lpath = make_path_posix(lpath) + + source_is_file = len(rpaths) == 1 + dest_is_dir = isinstance(lpath, str) and ( + trailing_sep(lpath) or LocalFileSystem().isdir(lpath) + ) + + exists = source_is_str and ( + (has_magic(rpath) and source_is_file) + or (not has_magic(rpath) and dest_is_dir and not trailing_sep(rpath)) + ) + lpaths = other_paths( + rpaths, + lpath, + exists=exists, + flatten=not source_is_str, + ) + + callback.set_size(len(lpaths)) + for lpath, rpath in callback.wrap(zip(lpaths, rpaths)): + with callback.branched(rpath, lpath) as child: + self.get_file(rpath, lpath, callback=child, **kwargs) + + def put_file( + self, lpath, rpath, callback=DEFAULT_CALLBACK, mode="overwrite", **kwargs + ): + """Copy single file to remote""" + if mode == "create" and self.exists(rpath): + raise FileExistsError + if os.path.isdir(lpath): + self.makedirs(rpath, exist_ok=True) + return None + + with open(lpath, "rb") as f1: + size = f1.seek(0, 2) + callback.set_size(size) + f1.seek(0) + + self.mkdirs(self._parent(os.fspath(rpath)), exist_ok=True) + with self.open(rpath, "wb", **kwargs) as f2: + while f1.tell() < size: + data = f1.read(self.blocksize) + segment_len = f2.write(data) + if segment_len is None: + segment_len = len(data) + callback.relative_update(segment_len) + + def put( + self, + lpath, + rpath, + recursive=False, + callback=DEFAULT_CALLBACK, + maxdepth=None, + **kwargs, + ): + """Copy file(s) from local. + + Copies a specific file or tree of files (if recursive=True). If rpath + ends with a "/", it will be assumed to be a directory, and target files + will go within. + + Calls put_file for each source. + """ + if isinstance(lpath, list) and isinstance(rpath, list): + # No need to expand paths when both source and destination + # are provided as lists + rpaths = rpath + lpaths = lpath + else: + from .implementations.local import ( + LocalFileSystem, + make_path_posix, + trailing_sep, + ) + + source_is_str = isinstance(lpath, str) + if source_is_str: + lpath = make_path_posix(lpath) + fs = LocalFileSystem() + lpaths = fs.expand_path(lpath, recursive=recursive, maxdepth=maxdepth) + if source_is_str and (not recursive or maxdepth is not None): + # Non-recursive glob does not copy directories + lpaths = [p for p in lpaths if not (trailing_sep(p) or fs.isdir(p))] + if not lpaths: + return + + source_is_file = len(lpaths) == 1 + dest_is_dir = isinstance(rpath, str) and ( + trailing_sep(rpath) or self.isdir(rpath) + ) + + rpath = ( + self._strip_protocol(rpath) + if isinstance(rpath, str) + else [self._strip_protocol(p) for p in rpath] + ) + exists = source_is_str and ( + (has_magic(lpath) and source_is_file) + or (not has_magic(lpath) and dest_is_dir and not trailing_sep(lpath)) + ) + rpaths = other_paths( + lpaths, + rpath, + exists=exists, + flatten=not source_is_str, + ) + + callback.set_size(len(rpaths)) + for lpath, rpath in callback.wrap(zip(lpaths, rpaths)): + with callback.branched(lpath, rpath) as child: + self.put_file(lpath, rpath, callback=child, **kwargs) + + def head(self, path, size=1024): + """Get the first ``size`` bytes from file""" + with self.open(path, "rb") as f: + return f.read(size) + + def tail(self, path, size=1024): + """Get the last ``size`` bytes from file""" + with self.open(path, "rb") as f: + f.seek(max(-size, -f.size), 2) + return f.read() + + def cp_file(self, path1, path2, **kwargs): + raise NotImplementedError + + def copy( + self, path1, path2, recursive=False, maxdepth=None, on_error=None, **kwargs + ): + """Copy within two locations in the filesystem + + on_error : "raise", "ignore" + If raise, any not-found exceptions will be raised; if ignore any + not-found exceptions will cause the path to be skipped; defaults to + raise unless recursive is true, where the default is ignore + """ + if on_error is None and recursive: + on_error = "ignore" + elif on_error is None: + on_error = "raise" + + if isinstance(path1, list) and isinstance(path2, list): + # No need to expand paths when both source and destination + # are provided as lists + paths1 = path1 + paths2 = path2 + else: + from .implementations.local import trailing_sep + + source_is_str = isinstance(path1, str) + paths1 = self.expand_path(path1, recursive=recursive, maxdepth=maxdepth) + if source_is_str and (not recursive or maxdepth is not None): + # Non-recursive glob does not copy directories + paths1 = [p for p in paths1 if not (trailing_sep(p) or self.isdir(p))] + if not paths1: + return + + source_is_file = len(paths1) == 1 + dest_is_dir = isinstance(path2, str) and ( + trailing_sep(path2) or self.isdir(path2) + ) + + exists = source_is_str and ( + (has_magic(path1) and source_is_file) + or (not has_magic(path1) and dest_is_dir and not trailing_sep(path1)) + ) + paths2 = other_paths( + paths1, + path2, + exists=exists, + flatten=not source_is_str, + ) + + for p1, p2 in zip(paths1, paths2): + try: + self.cp_file(p1, p2, **kwargs) + except FileNotFoundError: + if on_error == "raise": + raise + + def expand_path(self, path, recursive=False, maxdepth=None, **kwargs): + """Turn one or more globs or directories into a list of all matching paths + to files or directories. + + kwargs are passed to ``glob`` or ``find``, which may in turn call ``ls`` + """ + + if maxdepth is not None and maxdepth < 1: + raise ValueError("maxdepth must be at least 1") + + if isinstance(path, (str, os.PathLike)): + out = self.expand_path([path], recursive, maxdepth) + else: + out = set() + path = [self._strip_protocol(p) for p in path] + for p in path: + if has_magic(p): + bit = set(self.glob(p, maxdepth=maxdepth, **kwargs)) + out |= bit + if recursive: + # glob call above expanded one depth so if maxdepth is defined + # then decrement it in expand_path call below. If it is zero + # after decrementing then avoid expand_path call. + if maxdepth is not None and maxdepth <= 1: + continue + out |= set( + self.expand_path( + list(bit), + recursive=recursive, + maxdepth=maxdepth - 1 if maxdepth is not None else None, + **kwargs, + ) + ) + continue + elif recursive: + rec = set( + self.find( + p, maxdepth=maxdepth, withdirs=True, detail=False, **kwargs + ) + ) + out |= rec + if p not in out and (recursive is False or self.exists(p)): + # should only check once, for the root + out.add(p) + if not out: + raise FileNotFoundError(path) + return sorted(out) + + def mv(self, path1, path2, recursive=False, maxdepth=None, **kwargs): + """Move file(s) from one location to another""" + if path1 == path2: + logger.debug("%s mv: The paths are the same, so no files were moved.", self) + else: + # explicitly raise exception to prevent data corruption + self.copy( + path1, path2, recursive=recursive, maxdepth=maxdepth, onerror="raise" + ) + self.rm(path1, recursive=recursive) + + def rm_file(self, path): + """Delete a file""" + self._rm(path) + + def _rm(self, path): + """Delete one file""" + # this is the old name for the method, prefer rm_file + raise NotImplementedError + + def rm(self, path, recursive=False, maxdepth=None): + """Delete files. + + Parameters + ---------- + path: str or list of str + File(s) to delete. + recursive: bool + If file(s) are directories, recursively delete contents and then + also remove the directory + maxdepth: int or None + Depth to pass to walk for finding files to delete, if recursive. + If None, there will be no limit and infinite recursion may be + possible. + """ + path = self.expand_path(path, recursive=recursive, maxdepth=maxdepth) + for p in reversed(path): + self.rm_file(p) + + @classmethod + def _parent(cls, path): + path = cls._strip_protocol(path) + if "/" in path: + parent = path.rsplit("/", 1)[0].lstrip(cls.root_marker) + return cls.root_marker + parent + else: + return cls.root_marker + + def _open( + self, + path, + mode="rb", + block_size=None, + autocommit=True, + cache_options=None, + **kwargs, + ): + """Return raw bytes-mode file-like from the file-system""" + return AbstractBufferedFile( + self, + path, + mode, + block_size, + autocommit, + cache_options=cache_options, + **kwargs, + ) + + def open( + self, + path, + mode="rb", + block_size=None, + cache_options=None, + compression=None, + **kwargs, + ): + """ + Return a file-like object from the filesystem + + The resultant instance must function correctly in a context ``with`` + block. + + Parameters + ---------- + path: str + Target file + mode: str like 'rb', 'w' + See builtin ``open()`` + Mode "x" (exclusive write) may be implemented by the backend. Even if + it is, whether it is checked up front or on commit, and whether it is + atomic is implementation-dependent. + block_size: int + Some indication of buffering - this is a value in bytes + cache_options : dict, optional + Extra arguments to pass through to the cache. + compression: string or None + If given, open file using compression codec. Can either be a compression + name (a key in ``fsspec.compression.compr``) or "infer" to guess the + compression from the filename suffix. + encoding, errors, newline: passed on to TextIOWrapper for text mode + """ + import io + + path = self._strip_protocol(path) + if "b" not in mode: + mode = mode.replace("t", "") + "b" + + text_kwargs = { + k: kwargs.pop(k) + for k in ["encoding", "errors", "newline"] + if k in kwargs + } + return io.TextIOWrapper( + self.open( + path, + mode, + block_size=block_size, + cache_options=cache_options, + compression=compression, + **kwargs, + ), + **text_kwargs, + ) + else: + ac = kwargs.pop("autocommit", not self._intrans) + f = self._open( + path, + mode=mode, + block_size=block_size, + autocommit=ac, + cache_options=cache_options, + **kwargs, + ) + if compression is not None: + from fsspec.compression import compr + from fsspec.core import get_compression + + compression = get_compression(path, compression) + compress = compr[compression] + f = compress(f, mode=mode[0]) + + if not ac and "r" not in mode: + self.transaction.files.append(f) + return f + + def touch(self, path, truncate=True, **kwargs): + """Create empty file, or update timestamp + + Parameters + ---------- + path: str + file location + truncate: bool + If True, always set file size to 0; if False, update timestamp and + leave file unchanged, if backend allows this + """ + if truncate or not self.exists(path): + with self.open(path, "wb", **kwargs): + pass + else: + raise NotImplementedError # update timestamp, if possible + + def ukey(self, path): + """Hash of file properties, to tell if it has changed""" + return sha256(str(self.info(path)).encode()).hexdigest() + + def read_block(self, fn, offset, length, delimiter=None): + """Read a block of bytes from + + Starting at ``offset`` of the file, read ``length`` bytes. If + ``delimiter`` is set then we ensure that the read starts and stops at + delimiter boundaries that follow the locations ``offset`` and ``offset + + length``. If ``offset`` is zero then we start at zero. The + bytestring returned WILL include the end delimiter string. + + If offset+length is beyond the eof, reads to eof. + + Parameters + ---------- + fn: string + Path to filename + offset: int + Byte offset to start read + length: int + Number of bytes to read. If None, read to end. + delimiter: bytes (optional) + Ensure reading starts and stops at delimiter bytestring + + Examples + -------- + >>> fs.read_block('data/file.csv', 0, 13) # doctest: +SKIP + b'Alice, 100\\nBo' + >>> fs.read_block('data/file.csv', 0, 13, delimiter=b'\\n') # doctest: +SKIP + b'Alice, 100\\nBob, 200\\n' + + Use ``length=None`` to read to the end of the file. + >>> fs.read_block('data/file.csv', 0, None, delimiter=b'\\n') # doctest: +SKIP + b'Alice, 100\\nBob, 200\\nCharlie, 300' + + See Also + -------- + :func:`fsspec.utils.read_block` + """ + with self.open(fn, "rb") as f: + size = f.size + if length is None: + length = size + if size is not None and offset + length > size: + length = size - offset + return read_block(f, offset, length, delimiter) + + def to_json(self, *, include_password: bool = True) -> str: + """ + JSON representation of this filesystem instance. + + Parameters + ---------- + include_password: bool, default True + Whether to include the password (if any) in the output. + + Returns + ------- + JSON string with keys ``cls`` (the python location of this class), + protocol (text name of this class's protocol, first one in case of + multiple), ``args`` (positional args, usually empty), and all other + keyword arguments as their own keys. + + Warnings + -------- + Serialized filesystems may contain sensitive information which have been + passed to the constructor, such as passwords and tokens. Make sure you + store and send them in a secure environment! + """ + from .json import FilesystemJSONEncoder + + return json.dumps( + self, + cls=type( + "_FilesystemJSONEncoder", + (FilesystemJSONEncoder,), + {"include_password": include_password}, + ), + ) + + @staticmethod + def from_json(blob: str) -> AbstractFileSystem: + """ + Recreate a filesystem instance from JSON representation. + + See ``.to_json()`` for the expected structure of the input. + + Parameters + ---------- + blob: str + + Returns + ------- + file system instance, not necessarily of this particular class. + + Warnings + -------- + This can import arbitrary modules (as determined by the ``cls`` key). + Make sure you haven't installed any modules that may execute malicious code + at import time. + """ + from .json import FilesystemJSONDecoder + + return json.loads(blob, cls=FilesystemJSONDecoder) + + def to_dict(self, *, include_password: bool = True) -> dict[str, Any]: + """ + JSON-serializable dictionary representation of this filesystem instance. + + Parameters + ---------- + include_password: bool, default True + Whether to include the password (if any) in the output. + + Returns + ------- + Dictionary with keys ``cls`` (the python location of this class), + protocol (text name of this class's protocol, first one in case of + multiple), ``args`` (positional args, usually empty), and all other + keyword arguments as their own keys. + + Warnings + -------- + Serialized filesystems may contain sensitive information which have been + passed to the constructor, such as passwords and tokens. Make sure you + store and send them in a secure environment! + """ + from .json import FilesystemJSONEncoder + + json_encoder = FilesystemJSONEncoder() + + cls = type(self) + proto = self.protocol + + storage_options = dict(self.storage_options) + if not include_password: + storage_options.pop("password", None) + + return dict( + cls=f"{cls.__module__}:{cls.__name__}", + protocol=proto[0] if isinstance(proto, (tuple, list)) else proto, + args=json_encoder.make_serializable(self.storage_args), + **json_encoder.make_serializable(storage_options), + ) + + @staticmethod + def from_dict(dct: dict[str, Any]) -> AbstractFileSystem: + """ + Recreate a filesystem instance from dictionary representation. + + See ``.to_dict()`` for the expected structure of the input. + + Parameters + ---------- + dct: Dict[str, Any] + + Returns + ------- + file system instance, not necessarily of this particular class. + + Warnings + -------- + This can import arbitrary modules (as determined by the ``cls`` key). + Make sure you haven't installed any modules that may execute malicious code + at import time. + """ + from .json import FilesystemJSONDecoder + + json_decoder = FilesystemJSONDecoder() + + dct = dict(dct) # Defensive copy + + cls = FilesystemJSONDecoder.try_resolve_fs_cls(dct) + if cls is None: + raise ValueError("Not a serialized AbstractFileSystem") + + dct.pop("cls", None) + dct.pop("protocol", None) + + return cls( + *json_decoder.unmake_serializable(dct.pop("args", ())), + **json_decoder.unmake_serializable(dct), + ) + + def _get_pyarrow_filesystem(self): + """ + Make a version of the FS instance which will be acceptable to pyarrow + """ + # all instances already also derive from pyarrow + return self + + def get_mapper(self, root="", check=False, create=False, missing_exceptions=None): + """Create key/value store based on this file-system + + Makes a MutableMapping interface to the FS at the given root path. + See ``fsspec.mapping.FSMap`` for further details. + """ + from .mapping import FSMap + + return FSMap( + root, + self, + check=check, + create=create, + missing_exceptions=missing_exceptions, + ) + + @classmethod + def clear_instance_cache(cls): + """ + Clear the cache of filesystem instances. + + Notes + ----- + Unless overridden by setting the ``cachable`` class attribute to False, + the filesystem class stores a reference to newly created instances. This + prevents Python's normal rules around garbage collection from working, + since the instances refcount will not drop to zero until + ``clear_instance_cache`` is called. + """ + cls._cache.clear() + + def created(self, path): + """Return the created timestamp of a file as a datetime.datetime""" + raise NotImplementedError + + def modified(self, path): + """Return the modified timestamp of a file as a datetime.datetime""" + raise NotImplementedError + + def tree( + self, + path: str = "/", + recursion_limit: int = 2, + max_display: int = 25, + display_size: bool = False, + prefix: str = "", + is_last: bool = True, + first: bool = True, + indent_size: int = 4, + ) -> str: + """ + Return a tree-like structure of the filesystem starting from the given path as a string. + + Parameters + ---------- + path: Root path to start traversal from + recursion_limit: Maximum depth of directory traversal + max_display: Maximum number of items to display per directory + display_size: Whether to display file sizes + prefix: Current line prefix for visual tree structure + is_last: Whether current item is last in its level + first: Whether this is the first call (displays root path) + indent_size: Number of spaces by indent + + Returns + ------- + str: A string representing the tree structure. + + Example + ------- + >>> from fsspec import filesystem + + >>> fs = filesystem('ftp', host='test.rebex.net', user='demo', password='password') + >>> tree = fs.tree(display_size=True, recursion_limit=3, indent_size=8, max_display=10) + >>> print(tree) + """ + + def format_bytes(n: int) -> str: + """Format bytes as text.""" + for prefix, k in ( + ("P", 2**50), + ("T", 2**40), + ("G", 2**30), + ("M", 2**20), + ("k", 2**10), + ): + if n >= 0.9 * k: + return f"{n / k:.2f} {prefix}b" + return f"{n}B" + + result = [] + + if first: + result.append(path) + + if recursion_limit: + indent = " " * indent_size + contents = self.ls(path, detail=True) + contents.sort( + key=lambda x: (x.get("type") != "directory", x.get("name", "")) + ) + + if max_display is not None and len(contents) > max_display: + displayed_contents = contents[:max_display] + remaining_count = len(contents) - max_display + else: + displayed_contents = contents + remaining_count = 0 + + for i, item in enumerate(displayed_contents): + is_last_item = (i == len(displayed_contents) - 1) and ( + remaining_count == 0 + ) + + branch = ( + "└" + ("─" * (indent_size - 2)) + if is_last_item + else "├" + ("─" * (indent_size - 2)) + ) + branch += " " + new_prefix = prefix + ( + indent if is_last_item else "│" + " " * (indent_size - 1) + ) + + name = os.path.basename(item.get("name", "")) + + if display_size and item.get("type") == "directory": + sub_contents = self.ls(item.get("name", ""), detail=True) + num_files = sum( + 1 for sub_item in sub_contents if sub_item.get("type") == "file" + ) + num_folders = sum( + 1 + for sub_item in sub_contents + if sub_item.get("type") == "directory" + ) + + if num_files == 0 and num_folders == 0: + size = " (empty folder)" + elif num_files == 0: + size = f" ({num_folders} subfolder{'s' if num_folders > 1 else ''})" + elif num_folders == 0: + size = f" ({num_files} file{'s' if num_files > 1 else ''})" + else: + size = f" ({num_files} file{'s' if num_files > 1 else ''}, {num_folders} subfolder{'s' if num_folders > 1 else ''})" + elif display_size and item.get("type") == "file": + size = f" ({format_bytes(item.get('size', 0))})" + else: + size = "" + + result.append(f"{prefix}{branch}{name}{size}") + + if item.get("type") == "directory" and recursion_limit > 0: + result.append( + self.tree( + path=item.get("name", ""), + recursion_limit=recursion_limit - 1, + max_display=max_display, + display_size=display_size, + prefix=new_prefix, + is_last=is_last_item, + first=False, + indent_size=indent_size, + ) + ) + + if remaining_count > 0: + more_message = f"{remaining_count} more item(s) not displayed." + result.append( + f"{prefix}{'└' + ('─' * (indent_size - 2))} {more_message}" + ) + + return "\n".join(_ for _ in result if _) + + # ------------------------------------------------------------------------ + # Aliases + + def read_bytes(self, path, start=None, end=None, **kwargs): + """Alias of `AbstractFileSystem.cat_file`.""" + return self.cat_file(path, start=start, end=end, **kwargs) + + def write_bytes(self, path, value, **kwargs): + """Alias of `AbstractFileSystem.pipe_file`.""" + self.pipe_file(path, value, **kwargs) + + def makedir(self, path, create_parents=True, **kwargs): + """Alias of `AbstractFileSystem.mkdir`.""" + return self.mkdir(path, create_parents=create_parents, **kwargs) + + def mkdirs(self, path, exist_ok=False): + """Alias of `AbstractFileSystem.makedirs`.""" + return self.makedirs(path, exist_ok=exist_ok) + + def listdir(self, path, detail=True, **kwargs): + """Alias of `AbstractFileSystem.ls`.""" + return self.ls(path, detail=detail, **kwargs) + + def cp(self, path1, path2, **kwargs): + """Alias of `AbstractFileSystem.copy`.""" + return self.copy(path1, path2, **kwargs) + + def move(self, path1, path2, **kwargs): + """Alias of `AbstractFileSystem.mv`.""" + return self.mv(path1, path2, **kwargs) + + def stat(self, path, **kwargs): + """Alias of `AbstractFileSystem.info`.""" + return self.info(path, **kwargs) + + def disk_usage(self, path, total=True, maxdepth=None, **kwargs): + """Alias of `AbstractFileSystem.du`.""" + return self.du(path, total=total, maxdepth=maxdepth, **kwargs) + + def rename(self, path1, path2, **kwargs): + """Alias of `AbstractFileSystem.mv`.""" + return self.mv(path1, path2, **kwargs) + + def delete(self, path, recursive=False, maxdepth=None): + """Alias of `AbstractFileSystem.rm`.""" + return self.rm(path, recursive=recursive, maxdepth=maxdepth) + + def upload(self, lpath, rpath, recursive=False, **kwargs): + """Alias of `AbstractFileSystem.put`.""" + return self.put(lpath, rpath, recursive=recursive, **kwargs) + + def download(self, rpath, lpath, recursive=False, **kwargs): + """Alias of `AbstractFileSystem.get`.""" + return self.get(rpath, lpath, recursive=recursive, **kwargs) + + def sign(self, path, expiration=100, **kwargs): + """Create a signed URL representing the given path + + Some implementations allow temporary URLs to be generated, as a + way of delegating credentials. + + Parameters + ---------- + path : str + The path on the filesystem + expiration : int + Number of seconds to enable the URL for (if supported) + + Returns + ------- + URL : str + The signed URL + + Raises + ------ + NotImplementedError : if method is not implemented for a filesystem + """ + raise NotImplementedError("Sign is not implemented for this filesystem") + + def _isfilestore(self): + # Originally inherited from pyarrow DaskFileSystem. Keeping this + # here for backwards compatibility as long as pyarrow uses its + # legacy fsspec-compatible filesystems and thus accepts fsspec + # filesystems as well + return False + + +class AbstractBufferedFile(io.IOBase): + """Convenient class to derive from to provide buffering + + In the case that the backend does not provide a pythonic file-like object + already, this class contains much of the logic to build one. The only + methods that need to be overridden are ``_upload_chunk``, + ``_initiate_upload`` and ``_fetch_range``. + """ + + DEFAULT_BLOCK_SIZE = 5 * 2**20 + _details = None + + def __init__( + self, + fs, + path, + mode="rb", + block_size="default", + autocommit=True, + cache_type="readahead", + cache_options=None, + size=None, + **kwargs, + ): + """ + Template for files with buffered reading and writing + + Parameters + ---------- + fs: instance of FileSystem + path: str + location in file-system + mode: str + Normal file modes. Currently only 'wb', 'ab' or 'rb'. Some file + systems may be read-only, and some may not support append. + block_size: int + Buffer size for reading or writing, 'default' for class default + autocommit: bool + Whether to write to final destination; may only impact what + happens when file is being closed. + cache_type: {"readahead", "none", "mmap", "bytes"}, default "readahead" + Caching policy in read mode. See the definitions in ``core``. + cache_options : dict + Additional options passed to the constructor for the cache specified + by `cache_type`. + size: int + If given and in read mode, suppressed having to look up the file size + kwargs: + Gets stored as self.kwargs + """ + from .core import caches + + self.path = path + self.fs = fs + self.mode = mode + self.blocksize = ( + self.DEFAULT_BLOCK_SIZE if block_size in ["default", None] else block_size + ) + self.loc = 0 + self.autocommit = autocommit + self.end = None + self.start = None + self.closed = False + + if cache_options is None: + cache_options = {} + + if "trim" in kwargs: + warnings.warn( + "Passing 'trim' to control the cache behavior has been deprecated. " + "Specify it within the 'cache_options' argument instead.", + FutureWarning, + ) + cache_options["trim"] = kwargs.pop("trim") + + self.kwargs = kwargs + + if mode not in {"ab", "rb", "wb", "xb"}: + raise NotImplementedError("File mode not supported") + if mode == "rb": + if size is not None: + self.size = size + else: + self.size = self.details["size"] + self.cache = caches[cache_type]( + self.blocksize, self._fetch_range, self.size, **cache_options + ) + else: + self.buffer = io.BytesIO() + self.offset = None + self.forced = False + self.location = None + + @property + def details(self): + if self._details is None: + self._details = self.fs.info(self.path) + return self._details + + @details.setter + def details(self, value): + self._details = value + self.size = value["size"] + + @property + def full_name(self): + return _unstrip_protocol(self.path, self.fs) + + @property + def closed(self): + # get around this attr being read-only in IOBase + # use getattr here, since this can be called during del + return getattr(self, "_closed", True) + + @closed.setter + def closed(self, c): + self._closed = c + + def __hash__(self): + if "w" in self.mode: + return id(self) + else: + return int(tokenize(self.details), 16) + + def __eq__(self, other): + """Files are equal if they have the same checksum, only in read mode""" + if self is other: + return True + return ( + isinstance(other, type(self)) + and self.mode == "rb" + and other.mode == "rb" + and hash(self) == hash(other) + ) + + def commit(self): + """Move from temp to final destination""" + + def discard(self): + """Throw away temporary file""" + + def info(self): + """File information about this path""" + if self.readable(): + return self.details + else: + raise ValueError("Info not available while writing") + + def tell(self): + """Current file location""" + return self.loc + + def seek(self, loc, whence=0): + """Set current file location + + Parameters + ---------- + loc: int + byte location + whence: {0, 1, 2} + from start of file, current location or end of file, resp. + """ + loc = int(loc) + if not self.mode == "rb": + raise OSError(ESPIPE, "Seek only available in read mode") + if whence == 0: + nloc = loc + elif whence == 1: + nloc = self.loc + loc + elif whence == 2: + nloc = self.size + loc + else: + raise ValueError(f"invalid whence ({whence}, should be 0, 1 or 2)") + if nloc < 0: + raise ValueError("Seek before start of file") + self.loc = nloc + return self.loc + + def write(self, data): + """ + Write data to buffer. + + Buffer only sent on flush() or if buffer is greater than + or equal to blocksize. + + Parameters + ---------- + data: bytes + Set of bytes to be written. + """ + if not self.writable(): + raise ValueError("File not in write mode") + if self.closed: + raise ValueError("I/O operation on closed file.") + if self.forced: + raise ValueError("This file has been force-flushed, can only close") + out = self.buffer.write(data) + self.loc += out + if self.buffer.tell() >= self.blocksize: + self.flush() + return out + + def flush(self, force=False): + """ + Write buffered data to backend store. + + Writes the current buffer, if it is larger than the block-size, or if + the file is being closed. + + Parameters + ---------- + force: bool + When closing, write the last block even if it is smaller than + blocks are allowed to be. Disallows further writing to this file. + """ + + if self.closed: + raise ValueError("Flush on closed file") + if force and self.forced: + raise ValueError("Force flush cannot be called more than once") + if force: + self.forced = True + + if self.readable(): + # no-op to flush on read-mode + return + + if not force and self.buffer.tell() < self.blocksize: + # Defer write on small block + return + + if self.offset is None: + # Initialize a multipart upload + self.offset = 0 + try: + self._initiate_upload() + except: + self.closed = True + raise + + if self._upload_chunk(final=force) is not False: + self.offset += self.buffer.seek(0, 2) + self.buffer = io.BytesIO() + + def _upload_chunk(self, final=False): + """Write one part of a multi-block file upload + + Parameters + ========== + final: bool + This is the last block, so should complete file, if + self.autocommit is True. + """ + # may not yet have been initialized, may need to call _initialize_upload + + def _initiate_upload(self): + """Create remote file/upload""" + pass + + def _fetch_range(self, start, end): + """Get the specified set of bytes from remote""" + return self.fs.cat_file(self.path, start=start, end=end) + + def read(self, length=-1): + """ + Return data from cache, or fetch pieces as necessary + + Parameters + ---------- + length: int (-1) + Number of bytes to read; if <0, all remaining bytes. + """ + length = -1 if length is None else int(length) + if self.mode != "rb": + raise ValueError("File not in read mode") + if length < 0: + length = self.size - self.loc + if self.closed: + raise ValueError("I/O operation on closed file.") + if length == 0: + # don't even bother calling fetch + return b"" + out = self.cache._fetch(self.loc, self.loc + length) + + logger.debug( + "%s read: %i - %i %s", + self, + self.loc, + self.loc + length, + self.cache._log_stats(), + ) + self.loc += len(out) + return out + + def readinto(self, b): + """mirrors builtin file's readinto method + + https://docs.python.org/3/library/io.html#io.RawIOBase.readinto + """ + out = memoryview(b).cast("B") + data = self.read(out.nbytes) + out[: len(data)] = data + return len(data) + + def readuntil(self, char=b"\n", blocks=None): + """Return data between current position and first occurrence of char + + char is included in the output, except if the end of the tile is + encountered first. + + Parameters + ---------- + char: bytes + Thing to find + blocks: None or int + How much to read in each go. Defaults to file blocksize - which may + mean a new read on every call. + """ + out = [] + while True: + start = self.tell() + part = self.read(blocks or self.blocksize) + if len(part) == 0: + break + found = part.find(char) + if found > -1: + out.append(part[: found + len(char)]) + self.seek(start + found + len(char)) + break + out.append(part) + return b"".join(out) + + def readline(self): + """Read until and including the first occurrence of newline character + + Note that, because of character encoding, this is not necessarily a + true line ending. + """ + return self.readuntil(b"\n") + + def __next__(self): + out = self.readline() + if out: + return out + raise StopIteration + + def __iter__(self): + return self + + def readlines(self): + """Return all data, split by the newline character, including the newline character""" + data = self.read() + lines = data.split(b"\n") + out = [l + b"\n" for l in lines[:-1]] + if data.endswith(b"\n"): + return out + else: + return out + [lines[-1]] + # return list(self) ??? + + def readinto1(self, b): + return self.readinto(b) + + def close(self): + """Close file + + Finalizes writes, discards cache + """ + if getattr(self, "_unclosable", False): + return + if self.closed: + return + try: + if self.mode == "rb": + self.cache = None + else: + if not self.forced: + self.flush(force=True) + + if self.fs is not None: + self.fs.invalidate_cache(self.path) + self.fs.invalidate_cache(self.fs._parent(self.path)) + finally: + self.closed = True + + def readable(self): + """Whether opened for reading""" + return "r" in self.mode and not self.closed + + def seekable(self): + """Whether is seekable (only in read mode)""" + return self.readable() + + def writable(self): + """Whether opened for writing""" + return self.mode in {"wb", "ab", "xb"} and not self.closed + + def __reduce__(self): + if self.mode != "rb": + raise RuntimeError("Pickling a writeable file is not supported") + + return reopen, ( + self.fs, + self.path, + self.mode, + self.blocksize, + self.loc, + self.size, + self.autocommit, + self.cache.name if self.cache else "none", + self.kwargs, + ) + + def __del__(self): + if not self.closed: + self.close() + + def __str__(self): + return f"" + + __repr__ = __str__ + + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + + +def reopen(fs, path, mode, blocksize, loc, size, autocommit, cache_type, kwargs): + file = fs.open( + path, + mode=mode, + block_size=blocksize, + autocommit=autocommit, + cache_type=cache_type, + size=size, + **kwargs, + ) + if loc > 0: + file.seek(loc) + return file diff --git a/.venv/lib/python3.12/site-packages/fsspec/transaction.py b/.venv/lib/python3.12/site-packages/fsspec/transaction.py new file mode 100644 index 0000000000000000000000000000000000000000..77293f63ecc5f611e19d849ef236d53e9c258efc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec/transaction.py @@ -0,0 +1,90 @@ +from collections import deque + + +class Transaction: + """Filesystem transaction write context + + Gathers files for deferred commit or discard, so that several write + operations can be finalized semi-atomically. This works by having this + instance as the ``.transaction`` attribute of the given filesystem + """ + + def __init__(self, fs, **kwargs): + """ + Parameters + ---------- + fs: FileSystem instance + """ + self.fs = fs + self.files = deque() + + def __enter__(self): + self.start() + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + """End transaction and commit, if exit is not due to exception""" + # only commit if there was no exception + self.complete(commit=exc_type is None) + if self.fs: + self.fs._intrans = False + self.fs._transaction = None + self.fs = None + + def start(self): + """Start a transaction on this FileSystem""" + self.files = deque() # clean up after previous failed completions + self.fs._intrans = True + + def complete(self, commit=True): + """Finish transaction: commit or discard all deferred files""" + while self.files: + f = self.files.popleft() + if commit: + f.commit() + else: + f.discard() + self.fs._intrans = False + self.fs._transaction = None + self.fs = None + + +class FileActor: + def __init__(self): + self.files = [] + + def commit(self): + for f in self.files: + f.commit() + self.files.clear() + + def discard(self): + for f in self.files: + f.discard() + self.files.clear() + + def append(self, f): + self.files.append(f) + + +class DaskTransaction(Transaction): + def __init__(self, fs): + """ + Parameters + ---------- + fs: FileSystem instance + """ + import distributed + + super().__init__(fs) + client = distributed.default_client() + self.files = client.submit(FileActor, actor=True).result() + + def complete(self, commit=True): + """Finish transaction: commit or discard all deferred files""" + if commit: + self.files.commit().result() + else: + self.files.discard().result() + self.fs._intrans = False + self.fs = None diff --git a/.venv/lib/python3.12/site-packages/fsspec/utils.py b/.venv/lib/python3.12/site-packages/fsspec/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..6441c5b1d3a766e7911161e54c6d7fba8eb3032e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/fsspec/utils.py @@ -0,0 +1,737 @@ +from __future__ import annotations + +import contextlib +import logging +import math +import os +import re +import sys +import tempfile +from collections.abc import Iterable, Iterator, Sequence +from functools import partial +from hashlib import md5 +from importlib.metadata import version +from typing import ( + IO, + TYPE_CHECKING, + Any, + Callable, + TypeVar, +) +from urllib.parse import urlsplit + +if TYPE_CHECKING: + import pathlib + + from typing_extensions import TypeGuard + + from fsspec.spec import AbstractFileSystem + + +DEFAULT_BLOCK_SIZE = 5 * 2**20 + +T = TypeVar("T") + + +def infer_storage_options( + urlpath: str, inherit_storage_options: dict[str, Any] | None = None +) -> dict[str, Any]: + """Infer storage options from URL path and merge it with existing storage + options. + + Parameters + ---------- + urlpath: str or unicode + Either local absolute file path or URL (hdfs://namenode:8020/file.csv) + inherit_storage_options: dict (optional) + Its contents will get merged with the inferred information from the + given path + + Returns + ------- + Storage options dict. + + Examples + -------- + >>> infer_storage_options('/mnt/datasets/test.csv') # doctest: +SKIP + {"protocol": "file", "path", "/mnt/datasets/test.csv"} + >>> infer_storage_options( + ... 'hdfs://username:pwd@node:123/mnt/datasets/test.csv?q=1', + ... inherit_storage_options={'extra': 'value'}, + ... ) # doctest: +SKIP + {"protocol": "hdfs", "username": "username", "password": "pwd", + "host": "node", "port": 123, "path": "/mnt/datasets/test.csv", + "url_query": "q=1", "extra": "value"} + """ + # Handle Windows paths including disk name in this special case + if ( + re.match(r"^[a-zA-Z]:[\\/]", urlpath) + or re.match(r"^[a-zA-Z0-9]+://", urlpath) is None + ): + return {"protocol": "file", "path": urlpath} + + parsed_path = urlsplit(urlpath) + protocol = parsed_path.scheme or "file" + if parsed_path.fragment: + path = "#".join([parsed_path.path, parsed_path.fragment]) + else: + path = parsed_path.path + if protocol == "file": + # Special case parsing file protocol URL on Windows according to: + # https://msdn.microsoft.com/en-us/library/jj710207.aspx + windows_path = re.match(r"^/([a-zA-Z])[:|]([\\/].*)$", path) + if windows_path: + drive, path = windows_path.groups() + path = f"{drive}:{path}" + + if protocol in ["http", "https"]: + # for HTTP, we don't want to parse, as requests will anyway + return {"protocol": protocol, "path": urlpath} + + options: dict[str, Any] = {"protocol": protocol, "path": path} + + if parsed_path.netloc: + # Parse `hostname` from netloc manually because `parsed_path.hostname` + # lowercases the hostname which is not always desirable (e.g. in S3): + # https://github.com/dask/dask/issues/1417 + options["host"] = parsed_path.netloc.rsplit("@", 1)[-1].rsplit(":", 1)[0] + + if protocol in ("s3", "s3a", "gcs", "gs"): + options["path"] = options["host"] + options["path"] + else: + options["host"] = options["host"] + if parsed_path.port: + options["port"] = parsed_path.port + if parsed_path.username: + options["username"] = parsed_path.username + if parsed_path.password: + options["password"] = parsed_path.password + + if parsed_path.query: + options["url_query"] = parsed_path.query + if parsed_path.fragment: + options["url_fragment"] = parsed_path.fragment + + if inherit_storage_options: + update_storage_options(options, inherit_storage_options) + + return options + + +def update_storage_options( + options: dict[str, Any], inherited: dict[str, Any] | None = None +) -> None: + if not inherited: + inherited = {} + collisions = set(options) & set(inherited) + if collisions: + for collision in collisions: + if options.get(collision) != inherited.get(collision): + raise KeyError( + f"Collision between inferred and specified storage " + f"option:\n{collision}" + ) + options.update(inherited) + + +# Compression extensions registered via fsspec.compression.register_compression +compressions: dict[str, str] = {} + + +def infer_compression(filename: str) -> str | None: + """Infer compression, if available, from filename. + + Infer a named compression type, if registered and available, from filename + extension. This includes builtin (gz, bz2, zip) compressions, as well as + optional compressions. See fsspec.compression.register_compression. + """ + extension = os.path.splitext(filename)[-1].strip(".").lower() + if extension in compressions: + return compressions[extension] + return None + + +def build_name_function(max_int: float) -> Callable[[int], str]: + """Returns a function that receives a single integer + and returns it as a string padded by enough zero characters + to align with maximum possible integer + + >>> name_f = build_name_function(57) + + >>> name_f(7) + '07' + >>> name_f(31) + '31' + >>> build_name_function(1000)(42) + '0042' + >>> build_name_function(999)(42) + '042' + >>> build_name_function(0)(0) + '0' + """ + # handle corner cases max_int is 0 or exact power of 10 + max_int += 1e-8 + + pad_length = int(math.ceil(math.log10(max_int))) + + def name_function(i: int) -> str: + return str(i).zfill(pad_length) + + return name_function + + +def seek_delimiter(file: IO[bytes], delimiter: bytes, blocksize: int) -> bool: + r"""Seek current file to file start, file end, or byte after delimiter seq. + + Seeks file to next chunk delimiter, where chunks are defined on file start, + a delimiting sequence, and file end. Use file.tell() to see location afterwards. + Note that file start is a valid split, so must be at offset > 0 to seek for + delimiter. + + Parameters + ---------- + file: a file + delimiter: bytes + a delimiter like ``b'\n'`` or message sentinel, matching file .read() type + blocksize: int + Number of bytes to read from the file at once. + + + Returns + ------- + Returns True if a delimiter was found, False if at file start or end. + + """ + + if file.tell() == 0: + # beginning-of-file, return without seek + return False + + # Interface is for binary IO, with delimiter as bytes, but initialize last + # with result of file.read to preserve compatibility with text IO. + last: bytes | None = None + while True: + current = file.read(blocksize) + if not current: + # end-of-file without delimiter + return False + full = last + current if last else current + try: + if delimiter in full: + i = full.index(delimiter) + file.seek(file.tell() - (len(full) - i) + len(delimiter)) + return True + elif len(current) < blocksize: + # end-of-file without delimiter + return False + except (OSError, ValueError): + pass + last = full[-len(delimiter) :] + + +def read_block( + f: IO[bytes], + offset: int, + length: int | None, + delimiter: bytes | None = None, + split_before: bool = False, +) -> bytes: + """Read a block of bytes from a file + + Parameters + ---------- + f: File + Open file + offset: int + Byte offset to start read + length: int + Number of bytes to read, read through end of file if None + delimiter: bytes (optional) + Ensure reading starts and stops at delimiter bytestring + split_before: bool (optional) + Start/stop read *before* delimiter bytestring. + + + If using the ``delimiter=`` keyword argument we ensure that the read + starts and stops at delimiter boundaries that follow the locations + ``offset`` and ``offset + length``. If ``offset`` is zero then we + start at zero, regardless of delimiter. The bytestring returned WILL + include the terminating delimiter string. + + Examples + -------- + + >>> from io import BytesIO # doctest: +SKIP + >>> f = BytesIO(b'Alice, 100\\nBob, 200\\nCharlie, 300') # doctest: +SKIP + >>> read_block(f, 0, 13) # doctest: +SKIP + b'Alice, 100\\nBo' + + >>> read_block(f, 0, 13, delimiter=b'\\n') # doctest: +SKIP + b'Alice, 100\\nBob, 200\\n' + + >>> read_block(f, 10, 10, delimiter=b'\\n') # doctest: +SKIP + b'Bob, 200\\nCharlie, 300' + """ + if delimiter: + f.seek(offset) + found_start_delim = seek_delimiter(f, delimiter, 2**16) + if length is None: + return f.read() + start = f.tell() + length -= start - offset + + f.seek(start + length) + found_end_delim = seek_delimiter(f, delimiter, 2**16) + end = f.tell() + + # Adjust split location to before delimiter if seek found the + # delimiter sequence, not start or end of file. + if found_start_delim and split_before: + start -= len(delimiter) + + if found_end_delim and split_before: + end -= len(delimiter) + + offset = start + length = end - start + + f.seek(offset) + + # TODO: allow length to be None and read to the end of the file? + assert length is not None + b = f.read(length) + return b + + +def tokenize(*args: Any, **kwargs: Any) -> str: + """Deterministic token + + (modified from dask.base) + + >>> tokenize([1, 2, '3']) + '9d71491b50023b06fc76928e6eddb952' + + >>> tokenize('Hello') == tokenize('Hello') + True + """ + if kwargs: + args += (kwargs,) + try: + h = md5(str(args).encode()) + except ValueError: + # FIPS systems: https://github.com/fsspec/filesystem_spec/issues/380 + h = md5(str(args).encode(), usedforsecurity=False) + return h.hexdigest() + + +def stringify_path(filepath: str | os.PathLike[str] | pathlib.Path) -> str: + """Attempt to convert a path-like object to a string. + + Parameters + ---------- + filepath: object to be converted + + Returns + ------- + filepath_str: maybe a string version of the object + + Notes + ----- + Objects supporting the fspath protocol are coerced according to its + __fspath__ method. + + For backwards compatibility with older Python version, pathlib.Path + objects are specially coerced. + + Any other object is passed through unchanged, which includes bytes, + strings, buffers, or anything else that's not even path-like. + """ + if isinstance(filepath, str): + return filepath + elif hasattr(filepath, "__fspath__"): + return filepath.__fspath__() + elif hasattr(filepath, "path"): + return filepath.path + else: + return filepath # type: ignore[return-value] + + +def make_instance( + cls: Callable[..., T], args: Sequence[Any], kwargs: dict[str, Any] +) -> T: + inst = cls(*args, **kwargs) + inst._determine_worker() # type: ignore[attr-defined] + return inst + + +def common_prefix(paths: Iterable[str]) -> str: + """For a list of paths, find the shortest prefix common to all""" + parts = [p.split("/") for p in paths] + lmax = min(len(p) for p in parts) + end = 0 + for i in range(lmax): + end = all(p[i] == parts[0][i] for p in parts) + if not end: + break + i += end + return "/".join(parts[0][:i]) + + +def other_paths( + paths: list[str], + path2: str | list[str], + exists: bool = False, + flatten: bool = False, +) -> list[str]: + """In bulk file operations, construct a new file tree from a list of files + + Parameters + ---------- + paths: list of str + The input file tree + path2: str or list of str + Root to construct the new list in. If this is already a list of str, we just + assert it has the right number of elements. + exists: bool (optional) + For a str destination, it is already exists (and is a dir), files should + end up inside. + flatten: bool (optional) + Whether to flatten the input directory tree structure so that the output files + are in the same directory. + + Returns + ------- + list of str + """ + + if isinstance(path2, str): + path2 = path2.rstrip("/") + + if flatten: + path2 = ["/".join((path2, p.split("/")[-1])) for p in paths] + else: + cp = common_prefix(paths) + if exists: + cp = cp.rsplit("/", 1)[0] + if not cp and all(not s.startswith("/") for s in paths): + path2 = ["/".join([path2, p]) for p in paths] + else: + path2 = [p.replace(cp, path2, 1) for p in paths] + else: + assert len(paths) == len(path2) + return path2 + + +def is_exception(obj: Any) -> bool: + return isinstance(obj, BaseException) + + +def isfilelike(f: Any) -> TypeGuard[IO[bytes]]: + return all(hasattr(f, attr) for attr in ["read", "close", "tell"]) + + +def get_protocol(url: str) -> str: + url = stringify_path(url) + parts = re.split(r"(\:\:|\://)", url, maxsplit=1) + if len(parts) > 1: + return parts[0] + return "file" + + +def can_be_local(path: str) -> bool: + """Can the given URL be used with open_local?""" + from fsspec import get_filesystem_class + + try: + return getattr(get_filesystem_class(get_protocol(path)), "local_file", False) + except (ValueError, ImportError): + # not in registry or import failed + return False + + +def get_package_version_without_import(name: str) -> str | None: + """For given package name, try to find the version without importing it + + Import and package.__version__ is still the backup here, so an import + *might* happen. + + Returns either the version string, or None if the package + or the version was not readily found. + """ + if name in sys.modules: + mod = sys.modules[name] + if hasattr(mod, "__version__"): + return mod.__version__ + try: + return version(name) + except: # noqa: E722 + pass + try: + import importlib + + mod = importlib.import_module(name) + return mod.__version__ + except (ImportError, AttributeError): + return None + + +def setup_logging( + logger: logging.Logger | None = None, + logger_name: str | None = None, + level: str = "DEBUG", + clear: bool = True, +) -> logging.Logger: + if logger is None and logger_name is None: + raise ValueError("Provide either logger object or logger name") + logger = logger or logging.getLogger(logger_name) + handle = logging.StreamHandler() + formatter = logging.Formatter( + "%(asctime)s - %(name)s - %(levelname)s - %(funcName)s -- %(message)s" + ) + handle.setFormatter(formatter) + if clear: + logger.handlers.clear() + logger.addHandler(handle) + logger.setLevel(level) + return logger + + +def _unstrip_protocol(name: str, fs: AbstractFileSystem) -> str: + return fs.unstrip_protocol(name) + + +def mirror_from( + origin_name: str, methods: Iterable[str] +) -> Callable[[type[T]], type[T]]: + """Mirror attributes and methods from the given + origin_name attribute of the instance to the + decorated class""" + + def origin_getter(method: str, self: Any) -> Any: + origin = getattr(self, origin_name) + return getattr(origin, method) + + def wrapper(cls: type[T]) -> type[T]: + for method in methods: + wrapped_method = partial(origin_getter, method) + setattr(cls, method, property(wrapped_method)) + return cls + + return wrapper + + +@contextlib.contextmanager +def nullcontext(obj: T) -> Iterator[T]: + yield obj + + +def merge_offset_ranges( + paths: list[str], + starts: list[int] | int, + ends: list[int] | int, + max_gap: int = 0, + max_block: int | None = None, + sort: bool = True, +) -> tuple[list[str], list[int], list[int]]: + """Merge adjacent byte-offset ranges when the inter-range + gap is <= `max_gap`, and when the merged byte range does not + exceed `max_block` (if specified). By default, this function + will re-order the input paths and byte ranges to ensure sorted + order. If the user can guarantee that the inputs are already + sorted, passing `sort=False` will skip the re-ordering. + """ + # Check input + if not isinstance(paths, list): + raise TypeError + if not isinstance(starts, list): + starts = [starts] * len(paths) + if not isinstance(ends, list): + ends = [ends] * len(paths) + if len(starts) != len(paths) or len(ends) != len(paths): + raise ValueError + + # Early Return + if len(starts) <= 1: + return paths, starts, ends + + starts = [s or 0 for s in starts] + # Sort by paths and then ranges if `sort=True` + if sort: + paths, starts, ends = ( + list(v) + for v in zip( + *sorted( + zip(paths, starts, ends), + ) + ) + ) + + if paths: + # Loop through the coupled `paths`, `starts`, and + # `ends`, and merge adjacent blocks when appropriate + new_paths = paths[:1] + new_starts = starts[:1] + new_ends = ends[:1] + for i in range(1, len(paths)): + if paths[i] == paths[i - 1] and new_ends[-1] is None: + continue + elif ( + paths[i] != paths[i - 1] + or ((starts[i] - new_ends[-1]) > max_gap) + or (max_block is not None and (ends[i] - new_starts[-1]) > max_block) + ): + # Cannot merge with previous block. + # Add new `paths`, `starts`, and `ends` elements + new_paths.append(paths[i]) + new_starts.append(starts[i]) + new_ends.append(ends[i]) + else: + # Merge with previous block by updating the + # last element of `ends` + new_ends[-1] = ends[i] + return new_paths, new_starts, new_ends + + # `paths` is empty. Just return input lists + return paths, starts, ends + + +def file_size(filelike: IO[bytes]) -> int: + """Find length of any open read-mode file-like""" + pos = filelike.tell() + try: + return filelike.seek(0, 2) + finally: + filelike.seek(pos) + + +@contextlib.contextmanager +def atomic_write(path: str, mode: str = "wb"): + """ + A context manager that opens a temporary file next to `path` and, on exit, + replaces `path` with the temporary file, thereby updating `path` + atomically. + """ + fd, fn = tempfile.mkstemp( + dir=os.path.dirname(path), prefix=os.path.basename(path) + "-" + ) + try: + with open(fd, mode) as fp: + yield fp + except BaseException: + with contextlib.suppress(FileNotFoundError): + os.unlink(fn) + raise + else: + os.replace(fn, path) + + +def _translate(pat, STAR, QUESTION_MARK): + # Copied from: https://github.com/python/cpython/pull/106703. + res: list[str] = [] + add = res.append + i, n = 0, len(pat) + while i < n: + c = pat[i] + i = i + 1 + if c == "*": + # compress consecutive `*` into one + if (not res) or res[-1] is not STAR: + add(STAR) + elif c == "?": + add(QUESTION_MARK) + elif c == "[": + j = i + if j < n and pat[j] == "!": + j = j + 1 + if j < n and pat[j] == "]": + j = j + 1 + while j < n and pat[j] != "]": + j = j + 1 + if j >= n: + add("\\[") + else: + stuff = pat[i:j] + if "-" not in stuff: + stuff = stuff.replace("\\", r"\\") + else: + chunks = [] + k = i + 2 if pat[i] == "!" else i + 1 + while True: + k = pat.find("-", k, j) + if k < 0: + break + chunks.append(pat[i:k]) + i = k + 1 + k = k + 3 + chunk = pat[i:j] + if chunk: + chunks.append(chunk) + else: + chunks[-1] += "-" + # Remove empty ranges -- invalid in RE. + for k in range(len(chunks) - 1, 0, -1): + if chunks[k - 1][-1] > chunks[k][0]: + chunks[k - 1] = chunks[k - 1][:-1] + chunks[k][1:] + del chunks[k] + # Escape backslashes and hyphens for set difference (--). + # Hyphens that create ranges shouldn't be escaped. + stuff = "-".join( + s.replace("\\", r"\\").replace("-", r"\-") for s in chunks + ) + # Escape set operations (&&, ~~ and ||). + stuff = re.sub(r"([&~|])", r"\\\1", stuff) + i = j + 1 + if not stuff: + # Empty range: never match. + add("(?!)") + elif stuff == "!": + # Negated empty range: match any character. + add(".") + else: + if stuff[0] == "!": + stuff = "^" + stuff[1:] + elif stuff[0] in ("^", "["): + stuff = "\\" + stuff + add(f"[{stuff}]") + else: + add(re.escape(c)) + assert i == n + return res + + +def glob_translate(pat): + # Copied from: https://github.com/python/cpython/pull/106703. + # The keyword parameters' values are fixed to: + # recursive=True, include_hidden=True, seps=None + """Translate a pathname with shell wildcards to a regular expression.""" + if os.path.altsep: + seps = os.path.sep + os.path.altsep + else: + seps = os.path.sep + escaped_seps = "".join(map(re.escape, seps)) + any_sep = f"[{escaped_seps}]" if len(seps) > 1 else escaped_seps + not_sep = f"[^{escaped_seps}]" + one_last_segment = f"{not_sep}+" + one_segment = f"{one_last_segment}{any_sep}" + any_segments = f"(?:.+{any_sep})?" + any_last_segments = ".*" + results = [] + parts = re.split(any_sep, pat) + last_part_idx = len(parts) - 1 + for idx, part in enumerate(parts): + if part == "*": + results.append(one_segment if idx < last_part_idx else one_last_segment) + continue + if part == "**": + results.append(any_segments if idx < last_part_idx else any_last_segments) + continue + elif "**" in part: + raise ValueError( + "Invalid pattern: '**' can only be an entire path component" + ) + if part: + results.extend(_translate(part, f"{not_sep}*", not_sep)) + if idx < last_part_idx: + results.append(any_sep) + res = "".join(results) + return rf"(?s:{res})\Z" diff --git a/.venv/lib/python3.12/site-packages/gitpython-3.1.45.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/gitpython-3.1.45.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/gitpython-3.1.45.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/gitpython-3.1.45.dist-info/METADATA b/.venv/lib/python3.12/site-packages/gitpython-3.1.45.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..90375af41aea2db0fe6baaf00c156873db905e83 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/gitpython-3.1.45.dist-info/METADATA @@ -0,0 +1,306 @@ +Metadata-Version: 2.4 +Name: GitPython +Version: 3.1.45 +Summary: GitPython is a Python library used to interact with Git repositories +Home-page: https://github.com/gitpython-developers/GitPython +Author: Sebastian Thiel, Michael Trier +Author-email: byronimo@gmail.com, mtrier@gmail.com +License: BSD-3-Clause +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +Classifier: Operating System :: POSIX +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Typing :: Typed +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +License-File: LICENSE +License-File: AUTHORS +Requires-Dist: gitdb<5,>=4.0.1 +Requires-Dist: typing-extensions>=3.10.0.2; python_version < "3.10" +Provides-Extra: test +Requires-Dist: coverage[toml]; extra == "test" +Requires-Dist: ddt!=1.4.3,>=1.1.1; extra == "test" +Requires-Dist: mock; python_version < "3.8" and extra == "test" +Requires-Dist: mypy; extra == "test" +Requires-Dist: pre-commit; extra == "test" +Requires-Dist: pytest>=7.3.1; extra == "test" +Requires-Dist: pytest-cov; extra == "test" +Requires-Dist: pytest-instafail; extra == "test" +Requires-Dist: pytest-mock; extra == "test" +Requires-Dist: pytest-sugar; extra == "test" +Requires-Dist: typing-extensions; python_version < "3.11" and extra == "test" +Provides-Extra: doc +Requires-Dist: sphinx<7.2,>=7.1.2; extra == "doc" +Requires-Dist: sphinx_rtd_theme; extra == "doc" +Requires-Dist: sphinx-autodoc-typehints; extra == "doc" +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: description-content-type +Dynamic: home-page +Dynamic: license +Dynamic: license-file +Dynamic: provides-extra +Dynamic: requires-dist +Dynamic: requires-python +Dynamic: summary + +![Python package](https://github.com/gitpython-developers/GitPython/workflows/Python%20package/badge.svg) +[![Documentation Status](https://readthedocs.org/projects/gitpython/badge/?version=stable)](https://readthedocs.org/projects/gitpython/?badge=stable) +[![Packaging status](https://repology.org/badge/tiny-repos/python:gitpython.svg)](https://repology.org/metapackage/python:gitpython/versions) + +## [Gitoxide](https://github.com/Byron/gitoxide): A peek into the future… + +I started working on GitPython in 2009, back in the days when Python was 'my thing' and I had great plans with it. +Of course, back in the days, I didn't really know what I was doing and this shows in many places. Somewhat similar to +Python this happens to be 'good enough', but at the same time is deeply flawed and broken beyond repair. + +By now, GitPython is widely used and I am sure there is a good reason for that, it's something to be proud of and happy about. +The community is maintaining the software and is keeping it relevant for which I am absolutely grateful. For the time to come I am happy to continue maintaining GitPython, remaining hopeful that one day it won't be needed anymore. + +More than 15 years after my first meeting with 'git' I am still in excited about it, and am happy to finally have the tools and +probably the skills to scratch that itch of mine: implement `git` in a way that makes tool creation a piece of cake for most. + +If you like the idea and want to learn more, please head over to [gitoxide](https://github.com/Byron/gitoxide), an +implementation of 'git' in [Rust](https://www.rust-lang.org). + +*(Please note that `gitoxide` is not currently available for use in Python, and that Rust is required.)* + +## GitPython + +GitPython is a python library used to interact with git repositories, high-level like git-porcelain, +or low-level like git-plumbing. + +It provides abstractions of git objects for easy access of repository data often backed by calling the `git` +command-line program. + +### DEVELOPMENT STATUS + +This project is in **maintenance mode**, which means that + +- …there will be no feature development, unless these are contributed +- …there will be no bug fixes, unless they are relevant to the safety of users, or contributed +- …issues will be responded to with waiting times of up to a month + +The project is open to contributions of all kinds, as well as new maintainers. + +### REQUIREMENTS + +GitPython needs the `git` executable to be installed on the system and available in your +`PATH` for most operations. If it is not in your `PATH`, you can help GitPython find it +by setting the `GIT_PYTHON_GIT_EXECUTABLE=` environment variable. + +- Git (1.7.x or newer) +- Python >= 3.7 + +The list of dependencies are listed in `./requirements.txt` and `./test-requirements.txt`. +The installer takes care of installing them for you. + +### INSTALL + +GitPython and its required package dependencies can be installed in any of the following ways, all of which should typically be done in a [virtual environment](https://docs.python.org/3/tutorial/venv.html). + +#### From PyPI + +To obtain and install a copy [from PyPI](https://pypi.org/project/GitPython/), run: + +```sh +pip install GitPython +``` + +(A distribution package can also be downloaded for manual installation at [the PyPI page](https://pypi.org/project/GitPython/).) + +#### From downloaded source code + +If you have downloaded the source code, run this from inside the unpacked `GitPython` directory: + +```sh +pip install . +``` + +#### By cloning the source code repository + +To clone the [the GitHub repository](https://github.com/gitpython-developers/GitPython) from source to work on the code, you can do it like so: + +```sh +git clone https://github.com/gitpython-developers/GitPython +cd GitPython +./init-tests-after-clone.sh +``` + +On Windows, `./init-tests-after-clone.sh` can be run in a Git Bash shell. + +If you are cloning [your own fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks), then replace the above `git clone` command with one that gives the URL of your fork. Or use this [`gh`](https://cli.github.com/) command (assuming you have `gh` and your fork is called `GitPython`): + +```sh +gh repo clone GitPython +``` + +Having cloned the repo, create and activate your [virtual environment](https://docs.python.org/3/tutorial/venv.html). + +Then make an [editable install](https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs): + +```sh +pip install -e ".[test]" +``` + +In the less common case that you do not want to install test dependencies, `pip install -e .` can be used instead. + +#### With editable *dependencies* (not preferred, and rarely needed) + +In rare cases, you may want to work on GitPython and one or both of its [gitdb](https://github.com/gitpython-developers/gitdb) and [smmap](https://github.com/gitpython-developers/smmap) dependencies at the same time, with changes in your local working copy of gitdb or smmap immediately reflected in the behavior of your local working copy of GitPython. This can be done by making editable installations of those dependencies in the same virtual environment where you install GitPython. + +If you want to do that *and* you want the versions in GitPython's git submodules to be used, then pass `-e git/ext/gitdb` and/or `-e git/ext/gitdb/gitdb/ext/smmap` to `pip install`. This can be done in any order, and in separate `pip install` commands or the same one, so long as `-e` appears before *each* path. For example, you can install GitPython, gitdb, and smmap editably in the currently active virtual environment this way: + +```sh +pip install -e ".[test]" -e git/ext/gitdb -e git/ext/gitdb/gitdb/ext/smmap +``` + +The submodules must have been cloned for that to work, but that will already be the case if you have run `./init-tests-after-clone.sh`. You can use `pip list` to check which packages are installed editably and which are installed normally. + +To reiterate, this approach should only rarely be used. For most development it is preferable to allow the gitdb and smmap dependencices to be retrieved automatically from PyPI in their latest stable packaged versions. + +### Limitations + +#### Leakage of System Resources + +GitPython is not suited for long-running processes (like daemons) as it tends to +leak system resources. It was written in a time where destructors (as implemented +in the `__del__` method) still ran deterministically. + +In case you still want to use it in such a context, you will want to search the +codebase for `__del__` implementations and call these yourself when you see fit. + +Another way assure proper cleanup of resources is to factor out GitPython into a +separate process which can be dropped periodically. + +#### Windows support + +See [Issue #525](https://github.com/gitpython-developers/GitPython/issues/525). + +### RUNNING TESTS + +_Important_: Right after cloning this repository, please be sure to have executed +the `./init-tests-after-clone.sh` script in the repository root. Otherwise +you will encounter test failures. + +#### Install test dependencies + +Ensure testing libraries are installed. This is taken care of already if you installed with: + +```sh +pip install -e ".[test]" +``` + +If you had installed with a command like `pip install -e .` instead, you can still run +the above command to add the testing dependencies. + +#### Test commands + +To test, run: + +```sh +pytest +``` + +To lint, and apply some linting fixes as well as automatic code formatting, run: + +```sh +pre-commit run --all-files +``` + +This includes the linting and autoformatting done by Ruff, as well as some other checks. + +To typecheck, run: + +```sh +mypy +``` + +#### CI (and tox) + +Style and formatting checks, and running tests on all the different supported Python versions, will be performed: + +- Upon submitting a pull request. +- On each push, *if* you have a fork with GitHub Actions enabled. +- Locally, if you run [`tox`](https://tox.wiki/) (this skips any Python versions you don't have installed). + +#### Configuration files + +Specific tools are all configured in the `./pyproject.toml` file: + +- `pytest` (test runner) +- `coverage.py` (code coverage) +- `ruff` (linter and formatter) +- `mypy` (type checker) + +Orchestration tools: + +- Configuration for `pre-commit` is in the `./.pre-commit-config.yaml` file. +- Configuration for `tox` is in `./tox.ini`. +- Configuration for GitHub Actions (CI) is in files inside `./.github/workflows/`. + +### Contributions + +Please have a look at the [contributions file][contributing]. + +### INFRASTRUCTURE + +- [User Documentation](http://gitpython.readthedocs.org) +- [Questions and Answers](http://stackexchange.com/filters/167317/gitpython) +- Please post on Stack Overflow and use the `gitpython` tag +- [Issue Tracker](https://github.com/gitpython-developers/GitPython/issues) + - Post reproducible bugs and feature requests as a new issue. + Please be sure to provide the following information if posting bugs: + - GitPython version (e.g. `import git; git.__version__`) + - Python version (e.g. `python --version`) + - The encountered stack-trace, if applicable + - Enough information to allow reproducing the issue + +### How to make a new release + +1. Update/verify the **version** in the `VERSION` file. +2. Update/verify that the `doc/source/changes.rst` changelog file was updated. It should include a link to the forthcoming release page: `https://github.com/gitpython-developers/GitPython/releases/tag/` +3. Commit everything. +4. Run `git tag -s ` to tag the version in Git. +5. _Optionally_ create and activate a [virtual environment](https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/#creating-a-virtual-environment). (Then the next step can install `build` and `twine`.) +6. Run `make release`. +7. Go to [GitHub Releases](https://github.com/gitpython-developers/GitPython/releases) and publish a new one with the recently pushed tag. Generate the changelog. + +### Projects using GitPython + +- [PyDriller](https://github.com/ishepard/pydriller) +- [Kivy Designer](https://github.com/kivy/kivy-designer) +- [Prowl](https://github.com/nettitude/Prowl) +- [Python Taint](https://github.com/python-security/pyt) +- [Buster](https://github.com/axitkhurana/buster) +- [git-ftp](https://github.com/ezyang/git-ftp) +- [Git-Pandas](https://github.com/wdm0006/git-pandas) +- [PyGitUp](https://github.com/msiemens/PyGitUp) +- [PyJFuzz](https://github.com/mseclab/PyJFuzz) +- [Loki](https://github.com/Neo23x0/Loki) +- [Omniwallet](https://github.com/OmniLayer/omniwallet) +- [GitViper](https://github.com/BeayemX/GitViper) +- [Git Gud](https://github.com/bthayer2365/git-gud) + +### LICENSE + +[3-Clause BSD License](https://opensource.org/license/bsd-3-clause/), also known as the New BSD License. See the [LICENSE file][license]. + +One file exclusively used for fuzz testing is subject to [a separate license, detailed here](./fuzzing/README.md#license). +This file is not included in the wheel or sdist packages published by the maintainers of GitPython. + +[contributing]: https://github.com/gitpython-developers/GitPython/blob/main/CONTRIBUTING.md +[license]: https://github.com/gitpython-developers/GitPython/blob/main/LICENSE diff --git a/.venv/lib/python3.12/site-packages/gitpython-3.1.45.dist-info/RECORD b/.venv/lib/python3.12/site-packages/gitpython-3.1.45.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..836252dbaae46c43209874f19df6b4ac9fd02c84 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/gitpython-3.1.45.dist-info/RECORD @@ -0,0 +1,46 @@ +git/__init__.py,sha256=uSOfxveXReCHpCye__P5GcPeFwjdO-i1w3XScDWAdtA,8899 +git/cmd.py,sha256=y01yN8P2JFuFP5dGzHIW9vd-uBQjPzK8yfZMZIZZb34,67472 +git/compat.py,sha256=y1E6y6O2q5r8clSlr8ZNmuIWG9nmHuehQEsVsmBffs8,4526 +git/config.py,sha256=ozS9-YPa6ihLRJP6iXHOk3EDK3QNdvdqYC7Zhx-itWM,35330 +git/db.py,sha256=vIW9uWSbqu99zbuU2ZDmOhVOv1UPTmxrnqiCtRHCfjE,2368 +git/diff.py,sha256=wmpMCIdMiVOqreGVPOGYyO4gFboGOAicyrvvI7PPjEg,27095 +git/exc.py,sha256=Gc7g1pHpn8OmTse30NHmJVsBJ2CYH8LxaR8y8UA3lIM,7119 +git/index/__init__.py,sha256=i-Nqb8Lufp9aFbmxpQBORmmQnjEVVM1Pn58fsQkyGgQ,406 +git/index/base.py,sha256=qG1hOdiZ0OyyerlN49z8kosOY-Fk8elOOH6_4O4aKsQ,61065 +git/index/fun.py,sha256=tbE2qyVo35fRyH_eDu65PTX3jG7Ii0NrdH6L5zswFv4,16810 +git/index/typ.py,sha256=uuKNwitUw83FhVaLSwo4pY7PHDQudtZTLJrLGym4jcI,6570 +git/index/util.py,sha256=fULi7GPG-MvprKrRCD5c15GNdzku_1E38We0d97WB3A,3659 +git/objects/__init__.py,sha256=O6ZL_olX7e5-8iIbKviRPkVSJxN37WA-EC0q9d48U5Y,637 +git/objects/base.py,sha256=5-p9uSGWvPxX9hidvkGH9tQOJoH2eaRggUbMr9VJiL0,10285 +git/objects/blob.py,sha256=zwwq0KfOMYeP5J2tW5CQatoLyeqFRlfkxP1Vwx1h07s,1215 +git/objects/commit.py,sha256=oHdYcKxsW5HDHCEa225nHXKYdcQeOwdVrBbGjupJK9Y,30560 +git/objects/fun.py,sha256=B4jCqhAjm6Hl79GK58FPzW1H9K6Wc7Tx0rssyWmAcEE,8935 +git/objects/submodule/__init__.py,sha256=6xySp767LVz3UylWgUalntS_nGXRuVzXxDuFAv_Wc2c,303 +git/objects/submodule/base.py,sha256=uSaBBs_y9eg3xbf3viP173HKQOVWY9TpWp2FX5VGFPI,64343 +git/objects/submodule/root.py,sha256=5eTtYNHasqdPq6q0oDCPr7IaO6uAHL3b4DxMoiO2LhE,20246 +git/objects/submodule/util.py,sha256=sQqAYaiSJdFkZa9NlAuK_wTsMNiS-kkQnQjvIoJtc_o,3509 +git/objects/tag.py,sha256=jAGESnpmTEv-dLakPzheT5ILZFFArcItnXYqfxfDrgc,4441 +git/objects/tree.py,sha256=QzFHPk3bgQVeoA-lId6gi_0QyGGI6V2wp_icAhitdzI,13847 +git/objects/util.py,sha256=Nlza4zLgdPmr_Yasyvvs6c1rKtW_wMxI6wDmQpQ3ufw,23846 +git/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +git/refs/__init__.py,sha256=DWlJNnsx-4jM_E-VycbP-FZUdn6iWhjnH_uZ_pZXBro,509 +git/refs/head.py,sha256=SGa3N301HfAi79X6UR5Mcg7mO9TnCH3Bk549kHlJVaQ,10513 +git/refs/log.py,sha256=w31EeCsG1_8ZvW3RkgifmzQIcaBhFuN7fIZ3XTQxLHU,12490 +git/refs/reference.py,sha256=l6mhF4YLSEwtjz6b9PpOQH-fkng7EYWMaJhkjn-2jXA,5630 +git/refs/remote.py,sha256=WwqV9T7BbYf3F_WZNUQivu9xktIIKGklCjDpwQrhD-A,2806 +git/refs/symbolic.py,sha256=z5rUgeqRzMcXbbMlF8L13j3lu-ycI9azHa3-vToVuIM,34769 +git/refs/tag.py,sha256=kgzV2vhpL4FD2TqHb0BJuMRAHgAvJF-TcoyWlaB-djQ,5010 +git/remote.py,sha256=pYn9dAlz-QwvNMWXD1M57pMPQitthOM86qTRK_cpTqU,46786 +git/repo/__init__.py,sha256=CILSVH36fX_WxVFSjD9o1WF5LgsNedPiJvSngKZqfVU,210 +git/repo/base.py,sha256=P81qtQiz5lcNaO97sFtmg1Tk9tasc0bgpgioF1rLApo,59972 +git/repo/fun.py,sha256=LZewqHrAngGH5Q7YyUwIGP93GjlGD3WZbtzsh8LwGak,13803 +git/types.py,sha256=MQzIDEOnoueXGsAJF_0MgUc_osH7Eu0Sw3DQofYzCVE,10272 +git/util.py,sha256=vOspIY3BSXR3UUhlVBA9lCryCzeUWQwvatFNYt2AkfQ,43981 +gitpython-3.1.45.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +gitpython-3.1.45.dist-info/METADATA,sha256=Qu9hdYes2KAIsOoRh7JI-Zm7qeLrp_ccH36FvllVuKk,13456 +gitpython-3.1.45.dist-info/RECORD,, +gitpython-3.1.45.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +gitpython-3.1.45.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91 +gitpython-3.1.45.dist-info/licenses/AUTHORS,sha256=_upJOkW3eLK_ZywFzQDkKDp4JtF6xBf_Qk0ObAqTPvE,2347 +gitpython-3.1.45.dist-info/licenses/LICENSE,sha256=hvyUwyGpr7wRUUcTURuv3tIl8lEA3MD3NQ6CvCMbi-s,1503 +gitpython-3.1.45.dist-info/top_level.txt,sha256=0hzDuIp8obv624V3GmbqsagBWkk8ohtGU-Bc1PmTT0o,4 diff --git a/.venv/lib/python3.12/site-packages/gitpython-3.1.45.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/gitpython-3.1.45.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/gitpython-3.1.45.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/gitpython-3.1.45.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..e7fa31b6f3f78deb1022c1f7927f07d4d16da822 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/gitpython-3.1.45.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.12/site-packages/gitpython-3.1.45.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/gitpython-3.1.45.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..5664e303b5dc2e9ef8e14a0845d9486ec1920afd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/gitpython-3.1.45.dist-info/top_level.txt @@ -0,0 +1 @@ +git diff --git a/.venv/lib/python3.12/site-packages/hf_xet-1.1.10.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/hf_xet-1.1.10.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/hf_xet-1.1.10.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/hf_xet-1.1.10.dist-info/METADATA b/.venv/lib/python3.12/site-packages/hf_xet-1.1.10.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..f65384bd117520e138c896ad5b3b5cdff9a18281 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/hf_xet-1.1.10.dist-info/METADATA @@ -0,0 +1,83 @@ +Metadata-Version: 2.4 +Name: hf-xet +Version: 1.1.10 +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Programming Language :: Rust +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +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: Topic :: Scientific/Engineering :: Artificial Intelligence +Requires-Dist: pytest ; extra == 'tests' +Provides-Extra: tests +License-File: LICENSE +Summary: Fast transfer of large files with the Hugging Face Hub. +Maintainer-email: Rajat Arya , Jared Sulzdorf , Di Xiao , Assaf Vayner , Hoyt Koepke +License-Expression: Apache-2.0 +Requires-Python: >=3.8 +Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM +Project-URL: Homepage, https://github.com/huggingface/xet-core +Project-URL: Documentation, https://huggingface.co/docs/hub/en/storage-backends#using-xet-storage +Project-URL: Issues, https://github.com/huggingface/xet-core/issues +Project-URL: Repository, https://github.com/huggingface/xet-core.git + + +

+ License + GitHub release + Contributor Covenant +

+ +

+

🤗 hf-xet - xet client tech, used in huggingface_hub

+

+ +## Welcome + +`hf-xet` enables `huggingface_hub` to utilize xet storage for uploading and downloading to HF Hub. Xet storage provides chunk-based deduplication, efficient storage/retrieval with local disk caching, and backwards compatibility with Git LFS. This library is not meant to be used directly, and is instead intended to be used from [huggingface_hub](https://pypi.org/project/huggingface-hub). + +## Key features + +♻ **chunk-based deduplication implementation**: avoid transferring and storing chunks that are shared across binary files (models, datasets, etc). + +🤗 **Python bindings**: bindings for [huggingface_hub](https://github.com/huggingface/huggingface_hub/) package. + +↔ **network communications**: concurrent communication to HF Hub Xet backend services (CAS). + +🔖 **local disk caching**: chunk-based cache that sits alongside the existing [huggingface_hub disk cache](https://huggingface.co/docs/huggingface_hub/guides/manage-cache). + +## Installation + +Install the `hf_xet` package with [pip](https://pypi.org/project/hf-xet/): + +```bash +pip install hf_xet +``` + +## Quick Start + +`hf_xet` is not intended to be run independently as it is expected to be used from `huggingface_hub`, so to get started with `huggingface_hub` check out the documentation [here]("https://hf.co/docs/huggingface_hub"). + +## Contributions (feature requests, bugs, etc.) are encouraged & appreciated 💙💚💛💜🧡❤️ + +Please join us in making hf-xet better. We value everyone's contributions. Code is not the only way to help. Answering questions, helping each other, improving documentation, filing issues all help immensely. If you are interested in contributing (please do!), check out the [contribution guide](https://github.com/huggingface/xet-core/blob/main/CONTRIBUTING.md) for this repository. diff --git a/.venv/lib/python3.12/site-packages/hf_xet-1.1.10.dist-info/RECORD b/.venv/lib/python3.12/site-packages/hf_xet-1.1.10.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..da48301cc3622c2d49d4144ed86ddb6bb80cc758 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/hf_xet-1.1.10.dist-info/RECORD @@ -0,0 +1,8 @@ +hf_xet-1.1.10.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +hf_xet-1.1.10.dist-info/METADATA,sha256=uFNyfUe0we3oHWG-SPUCBDAWYkHCT1JBVQaRZg4_D5Q,4670 +hf_xet-1.1.10.dist-info/RECORD,, +hf_xet-1.1.10.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +hf_xet-1.1.10.dist-info/WHEEL,sha256=i6A8fOeKDbiLWJKJ4CSZ_oMsfL41B4i_w3i76Xh_k5M,127 +hf_xet-1.1.10.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357 +hf_xet/__init__.py,sha256=E8UDdyQ8glZ_nve9hHEf22bPang8-RKx4VuApXYeQUo,107 +hf_xet/hf_xet.abi3.so,sha256=WBX7yFb3btpsQ1Qx6U_PQU29USHaK8Vb7sRpYkW9BFs,7942656 diff --git a/.venv/lib/python3.12/site-packages/hf_xet-1.1.10.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/hf_xet-1.1.10.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/hf_xet-1.1.10.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/hf_xet-1.1.10.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..704a44e90006c1bdf009b283620ff686fe9b9ba7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/hf_xet-1.1.10.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: maturin (1.9.4) +Root-Is-Purelib: false +Tag: cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64 diff --git a/.venv/lib/python3.12/site-packages/hf_xet/__init__.py b/.venv/lib/python3.12/site-packages/hf_xet/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..96ed54a8a066d681e4973e5841a0f5577b619698 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/hf_xet/__init__.py @@ -0,0 +1,5 @@ +from .hf_xet import * + +__doc__ = hf_xet.__doc__ +if hasattr(hf_xet, "__all__"): + __all__ = hf_xet.__all__ \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/__init__.py b/.venv/lib/python3.12/site-packages/huggingface_hub/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..59b0d759a9661970e085dce2639573f8116c0640 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/__init__.py @@ -0,0 +1,1548 @@ +# Copyright 2020 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# *********** +# `huggingface_hub` init has 2 modes: +# - Normal usage: +# If imported to use it, all modules and functions are lazy-loaded. This means +# they exist at top level in module but are imported only the first time they are +# used. This way, `from huggingface_hub import something` will import `something` +# quickly without the hassle of importing all the features from `huggingface_hub`. +# - Static check: +# If statically analyzed, all modules and functions are loaded normally. This way +# static typing check works properly as well as autocomplete in text editors and +# IDEs. +# +# The static model imports are done inside the `if TYPE_CHECKING:` statement at +# the bottom of this file. Since module/functions imports are duplicated, it is +# mandatory to make sure to add them twice when adding one. This is checked in the +# `make quality` command. +# +# To update the static imports, please run the following command and commit the changes. +# ``` +# # Use script +# python utils/check_static_imports.py --update-file +# +# # Or run style on codebase +# make style +# ``` +# +# *********** +# Lazy loader vendored from https://github.com/scientific-python/lazy_loader +import importlib +import os +import sys +from typing import TYPE_CHECKING + + +__version__ = "0.35.3" + +# Alphabetical order of definitions is ensured in tests +# WARNING: any comment added in this dictionary definition will be lost when +# re-generating the file ! +_SUBMOD_ATTRS = { + "_commit_scheduler": [ + "CommitScheduler", + ], + "_inference_endpoints": [ + "InferenceEndpoint", + "InferenceEndpointError", + "InferenceEndpointStatus", + "InferenceEndpointTimeoutError", + "InferenceEndpointType", + ], + "_jobs_api": [ + "JobInfo", + "JobOwner", + "JobStage", + "JobStatus", + ], + "_login": [ + "auth_list", + "auth_switch", + "interpreter_login", + "login", + "logout", + "notebook_login", + ], + "_oauth": [ + "OAuthInfo", + "OAuthOrgInfo", + "OAuthUserInfo", + "attach_huggingface_oauth", + "parse_huggingface_oauth", + ], + "_snapshot_download": [ + "snapshot_download", + ], + "_space_api": [ + "SpaceHardware", + "SpaceRuntime", + "SpaceStage", + "SpaceStorage", + "SpaceVariable", + ], + "_tensorboard_logger": [ + "HFSummaryWriter", + ], + "_webhooks_payload": [ + "WebhookPayload", + "WebhookPayloadComment", + "WebhookPayloadDiscussion", + "WebhookPayloadDiscussionChanges", + "WebhookPayloadEvent", + "WebhookPayloadMovedTo", + "WebhookPayloadRepo", + "WebhookPayloadUrl", + "WebhookPayloadWebhook", + ], + "_webhooks_server": [ + "WebhooksServer", + "webhook_endpoint", + ], + "community": [ + "Discussion", + "DiscussionComment", + "DiscussionCommit", + "DiscussionEvent", + "DiscussionStatusChange", + "DiscussionTitleChange", + "DiscussionWithDetails", + ], + "constants": [ + "CONFIG_NAME", + "FLAX_WEIGHTS_NAME", + "HUGGINGFACE_CO_URL_HOME", + "HUGGINGFACE_CO_URL_TEMPLATE", + "PYTORCH_WEIGHTS_NAME", + "REPO_TYPE_DATASET", + "REPO_TYPE_MODEL", + "REPO_TYPE_SPACE", + "TF2_WEIGHTS_NAME", + "TF_WEIGHTS_NAME", + ], + "fastai_utils": [ + "_save_pretrained_fastai", + "from_pretrained_fastai", + "push_to_hub_fastai", + ], + "file_download": [ + "HfFileMetadata", + "_CACHED_NO_EXIST", + "get_hf_file_metadata", + "hf_hub_download", + "hf_hub_url", + "try_to_load_from_cache", + ], + "hf_api": [ + "Collection", + "CollectionItem", + "CommitInfo", + "CommitOperation", + "CommitOperationAdd", + "CommitOperationCopy", + "CommitOperationDelete", + "DatasetInfo", + "GitCommitInfo", + "GitRefInfo", + "GitRefs", + "HfApi", + "ModelInfo", + "RepoUrl", + "SpaceInfo", + "User", + "UserLikes", + "WebhookInfo", + "WebhookWatchedItem", + "accept_access_request", + "add_collection_item", + "add_space_secret", + "add_space_variable", + "auth_check", + "cancel_access_request", + "cancel_job", + "change_discussion_status", + "comment_discussion", + "create_branch", + "create_collection", + "create_commit", + "create_discussion", + "create_inference_endpoint", + "create_inference_endpoint_from_catalog", + "create_pull_request", + "create_repo", + "create_scheduled_job", + "create_scheduled_uv_job", + "create_tag", + "create_webhook", + "dataset_info", + "delete_branch", + "delete_collection", + "delete_collection_item", + "delete_file", + "delete_folder", + "delete_inference_endpoint", + "delete_repo", + "delete_scheduled_job", + "delete_space_secret", + "delete_space_storage", + "delete_space_variable", + "delete_tag", + "delete_webhook", + "disable_webhook", + "duplicate_space", + "edit_discussion_comment", + "enable_webhook", + "fetch_job_logs", + "file_exists", + "get_collection", + "get_dataset_tags", + "get_discussion_details", + "get_full_repo_name", + "get_inference_endpoint", + "get_model_tags", + "get_paths_info", + "get_repo_discussions", + "get_safetensors_metadata", + "get_space_runtime", + "get_space_variables", + "get_token_permission", + "get_user_overview", + "get_webhook", + "grant_access", + "inspect_job", + "inspect_scheduled_job", + "list_accepted_access_requests", + "list_collections", + "list_datasets", + "list_inference_catalog", + "list_inference_endpoints", + "list_jobs", + "list_lfs_files", + "list_liked_repos", + "list_models", + "list_organization_members", + "list_papers", + "list_pending_access_requests", + "list_rejected_access_requests", + "list_repo_commits", + "list_repo_files", + "list_repo_likers", + "list_repo_refs", + "list_repo_tree", + "list_spaces", + "list_user_followers", + "list_user_following", + "list_webhooks", + "merge_pull_request", + "model_info", + "move_repo", + "paper_info", + "parse_safetensors_file_metadata", + "pause_inference_endpoint", + "pause_space", + "permanently_delete_lfs_files", + "preupload_lfs_files", + "reject_access_request", + "rename_discussion", + "repo_exists", + "repo_info", + "repo_type_and_id_from_hf_id", + "request_space_hardware", + "request_space_storage", + "restart_space", + "resume_inference_endpoint", + "resume_scheduled_job", + "revision_exists", + "run_as_future", + "run_job", + "run_uv_job", + "scale_to_zero_inference_endpoint", + "set_space_sleep_time", + "space_info", + "super_squash_history", + "suspend_scheduled_job", + "unlike", + "update_collection_item", + "update_collection_metadata", + "update_inference_endpoint", + "update_repo_settings", + "update_repo_visibility", + "update_webhook", + "upload_file", + "upload_folder", + "upload_large_folder", + "whoami", + ], + "hf_file_system": [ + "HfFileSystem", + "HfFileSystemFile", + "HfFileSystemResolvedPath", + "HfFileSystemStreamFile", + ], + "hub_mixin": [ + "ModelHubMixin", + "PyTorchModelHubMixin", + ], + "inference._client": [ + "InferenceClient", + "InferenceTimeoutError", + ], + "inference._generated._async_client": [ + "AsyncInferenceClient", + ], + "inference._generated.types": [ + "AudioClassificationInput", + "AudioClassificationOutputElement", + "AudioClassificationOutputTransform", + "AudioClassificationParameters", + "AudioToAudioInput", + "AudioToAudioOutputElement", + "AutomaticSpeechRecognitionEarlyStoppingEnum", + "AutomaticSpeechRecognitionGenerationParameters", + "AutomaticSpeechRecognitionInput", + "AutomaticSpeechRecognitionOutput", + "AutomaticSpeechRecognitionOutputChunk", + "AutomaticSpeechRecognitionParameters", + "ChatCompletionInput", + "ChatCompletionInputFunctionDefinition", + "ChatCompletionInputFunctionName", + "ChatCompletionInputGrammarType", + "ChatCompletionInputJSONSchema", + "ChatCompletionInputMessage", + "ChatCompletionInputMessageChunk", + "ChatCompletionInputMessageChunkType", + "ChatCompletionInputResponseFormatJSONObject", + "ChatCompletionInputResponseFormatJSONSchema", + "ChatCompletionInputResponseFormatText", + "ChatCompletionInputStreamOptions", + "ChatCompletionInputTool", + "ChatCompletionInputToolCall", + "ChatCompletionInputToolChoiceClass", + "ChatCompletionInputToolChoiceEnum", + "ChatCompletionInputURL", + "ChatCompletionOutput", + "ChatCompletionOutputComplete", + "ChatCompletionOutputFunctionDefinition", + "ChatCompletionOutputLogprob", + "ChatCompletionOutputLogprobs", + "ChatCompletionOutputMessage", + "ChatCompletionOutputToolCall", + "ChatCompletionOutputTopLogprob", + "ChatCompletionOutputUsage", + "ChatCompletionStreamOutput", + "ChatCompletionStreamOutputChoice", + "ChatCompletionStreamOutputDelta", + "ChatCompletionStreamOutputDeltaToolCall", + "ChatCompletionStreamOutputFunction", + "ChatCompletionStreamOutputLogprob", + "ChatCompletionStreamOutputLogprobs", + "ChatCompletionStreamOutputTopLogprob", + "ChatCompletionStreamOutputUsage", + "DepthEstimationInput", + "DepthEstimationOutput", + "DocumentQuestionAnsweringInput", + "DocumentQuestionAnsweringInputData", + "DocumentQuestionAnsweringOutputElement", + "DocumentQuestionAnsweringParameters", + "FeatureExtractionInput", + "FeatureExtractionInputTruncationDirection", + "FillMaskInput", + "FillMaskOutputElement", + "FillMaskParameters", + "ImageClassificationInput", + "ImageClassificationOutputElement", + "ImageClassificationOutputTransform", + "ImageClassificationParameters", + "ImageSegmentationInput", + "ImageSegmentationOutputElement", + "ImageSegmentationParameters", + "ImageSegmentationSubtask", + "ImageToImageInput", + "ImageToImageOutput", + "ImageToImageParameters", + "ImageToImageTargetSize", + "ImageToTextEarlyStoppingEnum", + "ImageToTextGenerationParameters", + "ImageToTextInput", + "ImageToTextOutput", + "ImageToTextParameters", + "ImageToVideoInput", + "ImageToVideoOutput", + "ImageToVideoParameters", + "ImageToVideoTargetSize", + "ObjectDetectionBoundingBox", + "ObjectDetectionInput", + "ObjectDetectionOutputElement", + "ObjectDetectionParameters", + "Padding", + "QuestionAnsweringInput", + "QuestionAnsweringInputData", + "QuestionAnsweringOutputElement", + "QuestionAnsweringParameters", + "SentenceSimilarityInput", + "SentenceSimilarityInputData", + "SummarizationInput", + "SummarizationOutput", + "SummarizationParameters", + "SummarizationTruncationStrategy", + "TableQuestionAnsweringInput", + "TableQuestionAnsweringInputData", + "TableQuestionAnsweringOutputElement", + "TableQuestionAnsweringParameters", + "Text2TextGenerationInput", + "Text2TextGenerationOutput", + "Text2TextGenerationParameters", + "Text2TextGenerationTruncationStrategy", + "TextClassificationInput", + "TextClassificationOutputElement", + "TextClassificationOutputTransform", + "TextClassificationParameters", + "TextGenerationInput", + "TextGenerationInputGenerateParameters", + "TextGenerationInputGrammarType", + "TextGenerationOutput", + "TextGenerationOutputBestOfSequence", + "TextGenerationOutputDetails", + "TextGenerationOutputFinishReason", + "TextGenerationOutputPrefillToken", + "TextGenerationOutputToken", + "TextGenerationStreamOutput", + "TextGenerationStreamOutputStreamDetails", + "TextGenerationStreamOutputToken", + "TextToAudioEarlyStoppingEnum", + "TextToAudioGenerationParameters", + "TextToAudioInput", + "TextToAudioOutput", + "TextToAudioParameters", + "TextToImageInput", + "TextToImageOutput", + "TextToImageParameters", + "TextToSpeechEarlyStoppingEnum", + "TextToSpeechGenerationParameters", + "TextToSpeechInput", + "TextToSpeechOutput", + "TextToSpeechParameters", + "TextToVideoInput", + "TextToVideoOutput", + "TextToVideoParameters", + "TokenClassificationAggregationStrategy", + "TokenClassificationInput", + "TokenClassificationOutputElement", + "TokenClassificationParameters", + "TranslationInput", + "TranslationOutput", + "TranslationParameters", + "TranslationTruncationStrategy", + "TypeEnum", + "VideoClassificationInput", + "VideoClassificationOutputElement", + "VideoClassificationOutputTransform", + "VideoClassificationParameters", + "VisualQuestionAnsweringInput", + "VisualQuestionAnsweringInputData", + "VisualQuestionAnsweringOutputElement", + "VisualQuestionAnsweringParameters", + "ZeroShotClassificationInput", + "ZeroShotClassificationOutputElement", + "ZeroShotClassificationParameters", + "ZeroShotImageClassificationInput", + "ZeroShotImageClassificationOutputElement", + "ZeroShotImageClassificationParameters", + "ZeroShotObjectDetectionBoundingBox", + "ZeroShotObjectDetectionInput", + "ZeroShotObjectDetectionOutputElement", + "ZeroShotObjectDetectionParameters", + ], + "inference._mcp.agent": [ + "Agent", + ], + "inference._mcp.mcp_client": [ + "MCPClient", + ], + "inference_api": [ + "InferenceApi", + ], + "keras_mixin": [ + "KerasModelHubMixin", + "from_pretrained_keras", + "push_to_hub_keras", + "save_pretrained_keras", + ], + "repocard": [ + "DatasetCard", + "ModelCard", + "RepoCard", + "SpaceCard", + "metadata_eval_result", + "metadata_load", + "metadata_save", + "metadata_update", + ], + "repocard_data": [ + "CardData", + "DatasetCardData", + "EvalResult", + "ModelCardData", + "SpaceCardData", + ], + "repository": [ + "Repository", + ], + "serialization": [ + "StateDictSplit", + "get_tf_storage_size", + "get_torch_storage_id", + "get_torch_storage_size", + "load_state_dict_from_file", + "load_torch_model", + "save_torch_model", + "save_torch_state_dict", + "split_state_dict_into_shards_factory", + "split_tf_state_dict_into_shards", + "split_torch_state_dict_into_shards", + ], + "serialization._dduf": [ + "DDUFEntry", + "export_entries_as_dduf", + "export_folder_as_dduf", + "read_dduf_file", + ], + "utils": [ + "CacheNotFound", + "CachedFileInfo", + "CachedRepoInfo", + "CachedRevisionInfo", + "CorruptedCacheException", + "DeleteCacheStrategy", + "HFCacheInfo", + "HfFolder", + "cached_assets_path", + "configure_http_backend", + "dump_environment_info", + "get_session", + "get_token", + "logging", + "scan_cache_dir", + ], +} + +# WARNING: __all__ is generated automatically, Any manual edit will be lost when re-generating this file ! +# +# To update the static imports, please run the following command and commit the changes. +# ``` +# # Use script +# python utils/check_all_variable.py --update +# +# # Or run style on codebase +# make style +# ``` + +__all__ = [ + "Agent", + "AsyncInferenceClient", + "AudioClassificationInput", + "AudioClassificationOutputElement", + "AudioClassificationOutputTransform", + "AudioClassificationParameters", + "AudioToAudioInput", + "AudioToAudioOutputElement", + "AutomaticSpeechRecognitionEarlyStoppingEnum", + "AutomaticSpeechRecognitionGenerationParameters", + "AutomaticSpeechRecognitionInput", + "AutomaticSpeechRecognitionOutput", + "AutomaticSpeechRecognitionOutputChunk", + "AutomaticSpeechRecognitionParameters", + "CONFIG_NAME", + "CacheNotFound", + "CachedFileInfo", + "CachedRepoInfo", + "CachedRevisionInfo", + "CardData", + "ChatCompletionInput", + "ChatCompletionInputFunctionDefinition", + "ChatCompletionInputFunctionName", + "ChatCompletionInputGrammarType", + "ChatCompletionInputJSONSchema", + "ChatCompletionInputMessage", + "ChatCompletionInputMessageChunk", + "ChatCompletionInputMessageChunkType", + "ChatCompletionInputResponseFormatJSONObject", + "ChatCompletionInputResponseFormatJSONSchema", + "ChatCompletionInputResponseFormatText", + "ChatCompletionInputStreamOptions", + "ChatCompletionInputTool", + "ChatCompletionInputToolCall", + "ChatCompletionInputToolChoiceClass", + "ChatCompletionInputToolChoiceEnum", + "ChatCompletionInputURL", + "ChatCompletionOutput", + "ChatCompletionOutputComplete", + "ChatCompletionOutputFunctionDefinition", + "ChatCompletionOutputLogprob", + "ChatCompletionOutputLogprobs", + "ChatCompletionOutputMessage", + "ChatCompletionOutputToolCall", + "ChatCompletionOutputTopLogprob", + "ChatCompletionOutputUsage", + "ChatCompletionStreamOutput", + "ChatCompletionStreamOutputChoice", + "ChatCompletionStreamOutputDelta", + "ChatCompletionStreamOutputDeltaToolCall", + "ChatCompletionStreamOutputFunction", + "ChatCompletionStreamOutputLogprob", + "ChatCompletionStreamOutputLogprobs", + "ChatCompletionStreamOutputTopLogprob", + "ChatCompletionStreamOutputUsage", + "Collection", + "CollectionItem", + "CommitInfo", + "CommitOperation", + "CommitOperationAdd", + "CommitOperationCopy", + "CommitOperationDelete", + "CommitScheduler", + "CorruptedCacheException", + "DDUFEntry", + "DatasetCard", + "DatasetCardData", + "DatasetInfo", + "DeleteCacheStrategy", + "DepthEstimationInput", + "DepthEstimationOutput", + "Discussion", + "DiscussionComment", + "DiscussionCommit", + "DiscussionEvent", + "DiscussionStatusChange", + "DiscussionTitleChange", + "DiscussionWithDetails", + "DocumentQuestionAnsweringInput", + "DocumentQuestionAnsweringInputData", + "DocumentQuestionAnsweringOutputElement", + "DocumentQuestionAnsweringParameters", + "EvalResult", + "FLAX_WEIGHTS_NAME", + "FeatureExtractionInput", + "FeatureExtractionInputTruncationDirection", + "FillMaskInput", + "FillMaskOutputElement", + "FillMaskParameters", + "GitCommitInfo", + "GitRefInfo", + "GitRefs", + "HFCacheInfo", + "HFSummaryWriter", + "HUGGINGFACE_CO_URL_HOME", + "HUGGINGFACE_CO_URL_TEMPLATE", + "HfApi", + "HfFileMetadata", + "HfFileSystem", + "HfFileSystemFile", + "HfFileSystemResolvedPath", + "HfFileSystemStreamFile", + "HfFolder", + "ImageClassificationInput", + "ImageClassificationOutputElement", + "ImageClassificationOutputTransform", + "ImageClassificationParameters", + "ImageSegmentationInput", + "ImageSegmentationOutputElement", + "ImageSegmentationParameters", + "ImageSegmentationSubtask", + "ImageToImageInput", + "ImageToImageOutput", + "ImageToImageParameters", + "ImageToImageTargetSize", + "ImageToTextEarlyStoppingEnum", + "ImageToTextGenerationParameters", + "ImageToTextInput", + "ImageToTextOutput", + "ImageToTextParameters", + "ImageToVideoInput", + "ImageToVideoOutput", + "ImageToVideoParameters", + "ImageToVideoTargetSize", + "InferenceApi", + "InferenceClient", + "InferenceEndpoint", + "InferenceEndpointError", + "InferenceEndpointStatus", + "InferenceEndpointTimeoutError", + "InferenceEndpointType", + "InferenceTimeoutError", + "JobInfo", + "JobOwner", + "JobStage", + "JobStatus", + "KerasModelHubMixin", + "MCPClient", + "ModelCard", + "ModelCardData", + "ModelHubMixin", + "ModelInfo", + "OAuthInfo", + "OAuthOrgInfo", + "OAuthUserInfo", + "ObjectDetectionBoundingBox", + "ObjectDetectionInput", + "ObjectDetectionOutputElement", + "ObjectDetectionParameters", + "PYTORCH_WEIGHTS_NAME", + "Padding", + "PyTorchModelHubMixin", + "QuestionAnsweringInput", + "QuestionAnsweringInputData", + "QuestionAnsweringOutputElement", + "QuestionAnsweringParameters", + "REPO_TYPE_DATASET", + "REPO_TYPE_MODEL", + "REPO_TYPE_SPACE", + "RepoCard", + "RepoUrl", + "Repository", + "SentenceSimilarityInput", + "SentenceSimilarityInputData", + "SpaceCard", + "SpaceCardData", + "SpaceHardware", + "SpaceInfo", + "SpaceRuntime", + "SpaceStage", + "SpaceStorage", + "SpaceVariable", + "StateDictSplit", + "SummarizationInput", + "SummarizationOutput", + "SummarizationParameters", + "SummarizationTruncationStrategy", + "TF2_WEIGHTS_NAME", + "TF_WEIGHTS_NAME", + "TableQuestionAnsweringInput", + "TableQuestionAnsweringInputData", + "TableQuestionAnsweringOutputElement", + "TableQuestionAnsweringParameters", + "Text2TextGenerationInput", + "Text2TextGenerationOutput", + "Text2TextGenerationParameters", + "Text2TextGenerationTruncationStrategy", + "TextClassificationInput", + "TextClassificationOutputElement", + "TextClassificationOutputTransform", + "TextClassificationParameters", + "TextGenerationInput", + "TextGenerationInputGenerateParameters", + "TextGenerationInputGrammarType", + "TextGenerationOutput", + "TextGenerationOutputBestOfSequence", + "TextGenerationOutputDetails", + "TextGenerationOutputFinishReason", + "TextGenerationOutputPrefillToken", + "TextGenerationOutputToken", + "TextGenerationStreamOutput", + "TextGenerationStreamOutputStreamDetails", + "TextGenerationStreamOutputToken", + "TextToAudioEarlyStoppingEnum", + "TextToAudioGenerationParameters", + "TextToAudioInput", + "TextToAudioOutput", + "TextToAudioParameters", + "TextToImageInput", + "TextToImageOutput", + "TextToImageParameters", + "TextToSpeechEarlyStoppingEnum", + "TextToSpeechGenerationParameters", + "TextToSpeechInput", + "TextToSpeechOutput", + "TextToSpeechParameters", + "TextToVideoInput", + "TextToVideoOutput", + "TextToVideoParameters", + "TokenClassificationAggregationStrategy", + "TokenClassificationInput", + "TokenClassificationOutputElement", + "TokenClassificationParameters", + "TranslationInput", + "TranslationOutput", + "TranslationParameters", + "TranslationTruncationStrategy", + "TypeEnum", + "User", + "UserLikes", + "VideoClassificationInput", + "VideoClassificationOutputElement", + "VideoClassificationOutputTransform", + "VideoClassificationParameters", + "VisualQuestionAnsweringInput", + "VisualQuestionAnsweringInputData", + "VisualQuestionAnsweringOutputElement", + "VisualQuestionAnsweringParameters", + "WebhookInfo", + "WebhookPayload", + "WebhookPayloadComment", + "WebhookPayloadDiscussion", + "WebhookPayloadDiscussionChanges", + "WebhookPayloadEvent", + "WebhookPayloadMovedTo", + "WebhookPayloadRepo", + "WebhookPayloadUrl", + "WebhookPayloadWebhook", + "WebhookWatchedItem", + "WebhooksServer", + "ZeroShotClassificationInput", + "ZeroShotClassificationOutputElement", + "ZeroShotClassificationParameters", + "ZeroShotImageClassificationInput", + "ZeroShotImageClassificationOutputElement", + "ZeroShotImageClassificationParameters", + "ZeroShotObjectDetectionBoundingBox", + "ZeroShotObjectDetectionInput", + "ZeroShotObjectDetectionOutputElement", + "ZeroShotObjectDetectionParameters", + "_CACHED_NO_EXIST", + "_save_pretrained_fastai", + "accept_access_request", + "add_collection_item", + "add_space_secret", + "add_space_variable", + "attach_huggingface_oauth", + "auth_check", + "auth_list", + "auth_switch", + "cached_assets_path", + "cancel_access_request", + "cancel_job", + "change_discussion_status", + "comment_discussion", + "configure_http_backend", + "create_branch", + "create_collection", + "create_commit", + "create_discussion", + "create_inference_endpoint", + "create_inference_endpoint_from_catalog", + "create_pull_request", + "create_repo", + "create_scheduled_job", + "create_scheduled_uv_job", + "create_tag", + "create_webhook", + "dataset_info", + "delete_branch", + "delete_collection", + "delete_collection_item", + "delete_file", + "delete_folder", + "delete_inference_endpoint", + "delete_repo", + "delete_scheduled_job", + "delete_space_secret", + "delete_space_storage", + "delete_space_variable", + "delete_tag", + "delete_webhook", + "disable_webhook", + "dump_environment_info", + "duplicate_space", + "edit_discussion_comment", + "enable_webhook", + "export_entries_as_dduf", + "export_folder_as_dduf", + "fetch_job_logs", + "file_exists", + "from_pretrained_fastai", + "from_pretrained_keras", + "get_collection", + "get_dataset_tags", + "get_discussion_details", + "get_full_repo_name", + "get_hf_file_metadata", + "get_inference_endpoint", + "get_model_tags", + "get_paths_info", + "get_repo_discussions", + "get_safetensors_metadata", + "get_session", + "get_space_runtime", + "get_space_variables", + "get_tf_storage_size", + "get_token", + "get_token_permission", + "get_torch_storage_id", + "get_torch_storage_size", + "get_user_overview", + "get_webhook", + "grant_access", + "hf_hub_download", + "hf_hub_url", + "inspect_job", + "inspect_scheduled_job", + "interpreter_login", + "list_accepted_access_requests", + "list_collections", + "list_datasets", + "list_inference_catalog", + "list_inference_endpoints", + "list_jobs", + "list_lfs_files", + "list_liked_repos", + "list_models", + "list_organization_members", + "list_papers", + "list_pending_access_requests", + "list_rejected_access_requests", + "list_repo_commits", + "list_repo_files", + "list_repo_likers", + "list_repo_refs", + "list_repo_tree", + "list_spaces", + "list_user_followers", + "list_user_following", + "list_webhooks", + "load_state_dict_from_file", + "load_torch_model", + "logging", + "login", + "logout", + "merge_pull_request", + "metadata_eval_result", + "metadata_load", + "metadata_save", + "metadata_update", + "model_info", + "move_repo", + "notebook_login", + "paper_info", + "parse_huggingface_oauth", + "parse_safetensors_file_metadata", + "pause_inference_endpoint", + "pause_space", + "permanently_delete_lfs_files", + "preupload_lfs_files", + "push_to_hub_fastai", + "push_to_hub_keras", + "read_dduf_file", + "reject_access_request", + "rename_discussion", + "repo_exists", + "repo_info", + "repo_type_and_id_from_hf_id", + "request_space_hardware", + "request_space_storage", + "restart_space", + "resume_inference_endpoint", + "resume_scheduled_job", + "revision_exists", + "run_as_future", + "run_job", + "run_uv_job", + "save_pretrained_keras", + "save_torch_model", + "save_torch_state_dict", + "scale_to_zero_inference_endpoint", + "scan_cache_dir", + "set_space_sleep_time", + "snapshot_download", + "space_info", + "split_state_dict_into_shards_factory", + "split_tf_state_dict_into_shards", + "split_torch_state_dict_into_shards", + "super_squash_history", + "suspend_scheduled_job", + "try_to_load_from_cache", + "unlike", + "update_collection_item", + "update_collection_metadata", + "update_inference_endpoint", + "update_repo_settings", + "update_repo_visibility", + "update_webhook", + "upload_file", + "upload_folder", + "upload_large_folder", + "webhook_endpoint", + "whoami", +] + + +def _attach(package_name, submodules=None, submod_attrs=None): + """Attach lazily loaded submodules, functions, or other attributes. + + Typically, modules import submodules and attributes as follows: + + ```py + import mysubmodule + import anothersubmodule + + from .foo import someattr + ``` + + The idea is to replace a package's `__getattr__`, `__dir__`, such that all imports + work exactly the way they would with normal imports, except that the import occurs + upon first use. + + The typical way to call this function, replacing the above imports, is: + + ```python + __getattr__, __dir__ = lazy.attach( + __name__, + ['mysubmodule', 'anothersubmodule'], + {'foo': ['someattr']} + ) + ``` + This functionality requires Python 3.7 or higher. + + Args: + package_name (`str`): + Typically use `__name__`. + submodules (`set`): + List of submodules to attach. + submod_attrs (`dict`): + Dictionary of submodule -> list of attributes / functions. + These attributes are imported as they are used. + + Returns: + __getattr__, __dir__, __all__ + + """ + if submod_attrs is None: + submod_attrs = {} + + if submodules is None: + submodules = set() + else: + submodules = set(submodules) + + attr_to_modules = {attr: mod for mod, attrs in submod_attrs.items() for attr in attrs} + + def __getattr__(name): + if name in submodules: + try: + return importlib.import_module(f"{package_name}.{name}") + except Exception as e: + print(f"Error importing {package_name}.{name}: {e}") + raise + elif name in attr_to_modules: + submod_path = f"{package_name}.{attr_to_modules[name]}" + try: + submod = importlib.import_module(submod_path) + except Exception as e: + print(f"Error importing {submod_path}: {e}") + raise + attr = getattr(submod, name) + + # If the attribute lives in a file (module) with the same + # name as the attribute, ensure that the attribute and *not* + # the module is accessible on the package. + if name == attr_to_modules[name]: + pkg = sys.modules[package_name] + pkg.__dict__[name] = attr + + return attr + else: + raise AttributeError(f"No {package_name} attribute {name}") + + def __dir__(): + return __all__ + + return __getattr__, __dir__ + + +__getattr__, __dir__ = _attach(__name__, submodules=[], submod_attrs=_SUBMOD_ATTRS) + +if os.environ.get("EAGER_IMPORT", ""): + for attr in __all__: + __getattr__(attr) + +# WARNING: any content below this statement is generated automatically. Any manual edit +# will be lost when re-generating this file ! +# +# To update the static imports, please run the following command and commit the changes. +# ``` +# # Use script +# python utils/check_static_imports.py --update +# +# # Or run style on codebase +# make style +# ``` +if TYPE_CHECKING: # pragma: no cover + from ._commit_scheduler import CommitScheduler # noqa: F401 + from ._inference_endpoints import ( + InferenceEndpoint, # noqa: F401 + InferenceEndpointError, # noqa: F401 + InferenceEndpointStatus, # noqa: F401 + InferenceEndpointTimeoutError, # noqa: F401 + InferenceEndpointType, # noqa: F401 + ) + from ._jobs_api import ( + JobInfo, # noqa: F401 + JobOwner, # noqa: F401 + JobStage, # noqa: F401 + JobStatus, # noqa: F401 + ) + from ._login import ( + auth_list, # noqa: F401 + auth_switch, # noqa: F401 + interpreter_login, # noqa: F401 + login, # noqa: F401 + logout, # noqa: F401 + notebook_login, # noqa: F401 + ) + from ._oauth import ( + OAuthInfo, # noqa: F401 + OAuthOrgInfo, # noqa: F401 + OAuthUserInfo, # noqa: F401 + attach_huggingface_oauth, # noqa: F401 + parse_huggingface_oauth, # noqa: F401 + ) + from ._snapshot_download import snapshot_download # noqa: F401 + from ._space_api import ( + SpaceHardware, # noqa: F401 + SpaceRuntime, # noqa: F401 + SpaceStage, # noqa: F401 + SpaceStorage, # noqa: F401 + SpaceVariable, # noqa: F401 + ) + from ._tensorboard_logger import HFSummaryWriter # noqa: F401 + from ._webhooks_payload import ( + WebhookPayload, # noqa: F401 + WebhookPayloadComment, # noqa: F401 + WebhookPayloadDiscussion, # noqa: F401 + WebhookPayloadDiscussionChanges, # noqa: F401 + WebhookPayloadEvent, # noqa: F401 + WebhookPayloadMovedTo, # noqa: F401 + WebhookPayloadRepo, # noqa: F401 + WebhookPayloadUrl, # noqa: F401 + WebhookPayloadWebhook, # noqa: F401 + ) + from ._webhooks_server import ( + WebhooksServer, # noqa: F401 + webhook_endpoint, # noqa: F401 + ) + from .community import ( + Discussion, # noqa: F401 + DiscussionComment, # noqa: F401 + DiscussionCommit, # noqa: F401 + DiscussionEvent, # noqa: F401 + DiscussionStatusChange, # noqa: F401 + DiscussionTitleChange, # noqa: F401 + DiscussionWithDetails, # noqa: F401 + ) + from .constants import ( + CONFIG_NAME, # noqa: F401 + FLAX_WEIGHTS_NAME, # noqa: F401 + HUGGINGFACE_CO_URL_HOME, # noqa: F401 + HUGGINGFACE_CO_URL_TEMPLATE, # noqa: F401 + PYTORCH_WEIGHTS_NAME, # noqa: F401 + REPO_TYPE_DATASET, # noqa: F401 + REPO_TYPE_MODEL, # noqa: F401 + REPO_TYPE_SPACE, # noqa: F401 + TF2_WEIGHTS_NAME, # noqa: F401 + TF_WEIGHTS_NAME, # noqa: F401 + ) + from .fastai_utils import ( + _save_pretrained_fastai, # noqa: F401 + from_pretrained_fastai, # noqa: F401 + push_to_hub_fastai, # noqa: F401 + ) + from .file_download import ( + _CACHED_NO_EXIST, # noqa: F401 + HfFileMetadata, # noqa: F401 + get_hf_file_metadata, # noqa: F401 + hf_hub_download, # noqa: F401 + hf_hub_url, # noqa: F401 + try_to_load_from_cache, # noqa: F401 + ) + from .hf_api import ( + Collection, # noqa: F401 + CollectionItem, # noqa: F401 + CommitInfo, # noqa: F401 + CommitOperation, # noqa: F401 + CommitOperationAdd, # noqa: F401 + CommitOperationCopy, # noqa: F401 + CommitOperationDelete, # noqa: F401 + DatasetInfo, # noqa: F401 + GitCommitInfo, # noqa: F401 + GitRefInfo, # noqa: F401 + GitRefs, # noqa: F401 + HfApi, # noqa: F401 + ModelInfo, # noqa: F401 + RepoUrl, # noqa: F401 + SpaceInfo, # noqa: F401 + User, # noqa: F401 + UserLikes, # noqa: F401 + WebhookInfo, # noqa: F401 + WebhookWatchedItem, # noqa: F401 + accept_access_request, # noqa: F401 + add_collection_item, # noqa: F401 + add_space_secret, # noqa: F401 + add_space_variable, # noqa: F401 + auth_check, # noqa: F401 + cancel_access_request, # noqa: F401 + cancel_job, # noqa: F401 + change_discussion_status, # noqa: F401 + comment_discussion, # noqa: F401 + create_branch, # noqa: F401 + create_collection, # noqa: F401 + create_commit, # noqa: F401 + create_discussion, # noqa: F401 + create_inference_endpoint, # noqa: F401 + create_inference_endpoint_from_catalog, # noqa: F401 + create_pull_request, # noqa: F401 + create_repo, # noqa: F401 + create_scheduled_job, # noqa: F401 + create_scheduled_uv_job, # noqa: F401 + create_tag, # noqa: F401 + create_webhook, # noqa: F401 + dataset_info, # noqa: F401 + delete_branch, # noqa: F401 + delete_collection, # noqa: F401 + delete_collection_item, # noqa: F401 + delete_file, # noqa: F401 + delete_folder, # noqa: F401 + delete_inference_endpoint, # noqa: F401 + delete_repo, # noqa: F401 + delete_scheduled_job, # noqa: F401 + delete_space_secret, # noqa: F401 + delete_space_storage, # noqa: F401 + delete_space_variable, # noqa: F401 + delete_tag, # noqa: F401 + delete_webhook, # noqa: F401 + disable_webhook, # noqa: F401 + duplicate_space, # noqa: F401 + edit_discussion_comment, # noqa: F401 + enable_webhook, # noqa: F401 + fetch_job_logs, # noqa: F401 + file_exists, # noqa: F401 + get_collection, # noqa: F401 + get_dataset_tags, # noqa: F401 + get_discussion_details, # noqa: F401 + get_full_repo_name, # noqa: F401 + get_inference_endpoint, # noqa: F401 + get_model_tags, # noqa: F401 + get_paths_info, # noqa: F401 + get_repo_discussions, # noqa: F401 + get_safetensors_metadata, # noqa: F401 + get_space_runtime, # noqa: F401 + get_space_variables, # noqa: F401 + get_token_permission, # noqa: F401 + get_user_overview, # noqa: F401 + get_webhook, # noqa: F401 + grant_access, # noqa: F401 + inspect_job, # noqa: F401 + inspect_scheduled_job, # noqa: F401 + list_accepted_access_requests, # noqa: F401 + list_collections, # noqa: F401 + list_datasets, # noqa: F401 + list_inference_catalog, # noqa: F401 + list_inference_endpoints, # noqa: F401 + list_jobs, # noqa: F401 + list_lfs_files, # noqa: F401 + list_liked_repos, # noqa: F401 + list_models, # noqa: F401 + list_organization_members, # noqa: F401 + list_papers, # noqa: F401 + list_pending_access_requests, # noqa: F401 + list_rejected_access_requests, # noqa: F401 + list_repo_commits, # noqa: F401 + list_repo_files, # noqa: F401 + list_repo_likers, # noqa: F401 + list_repo_refs, # noqa: F401 + list_repo_tree, # noqa: F401 + list_spaces, # noqa: F401 + list_user_followers, # noqa: F401 + list_user_following, # noqa: F401 + list_webhooks, # noqa: F401 + merge_pull_request, # noqa: F401 + model_info, # noqa: F401 + move_repo, # noqa: F401 + paper_info, # noqa: F401 + parse_safetensors_file_metadata, # noqa: F401 + pause_inference_endpoint, # noqa: F401 + pause_space, # noqa: F401 + permanently_delete_lfs_files, # noqa: F401 + preupload_lfs_files, # noqa: F401 + reject_access_request, # noqa: F401 + rename_discussion, # noqa: F401 + repo_exists, # noqa: F401 + repo_info, # noqa: F401 + repo_type_and_id_from_hf_id, # noqa: F401 + request_space_hardware, # noqa: F401 + request_space_storage, # noqa: F401 + restart_space, # noqa: F401 + resume_inference_endpoint, # noqa: F401 + resume_scheduled_job, # noqa: F401 + revision_exists, # noqa: F401 + run_as_future, # noqa: F401 + run_job, # noqa: F401 + run_uv_job, # noqa: F401 + scale_to_zero_inference_endpoint, # noqa: F401 + set_space_sleep_time, # noqa: F401 + space_info, # noqa: F401 + super_squash_history, # noqa: F401 + suspend_scheduled_job, # noqa: F401 + unlike, # noqa: F401 + update_collection_item, # noqa: F401 + update_collection_metadata, # noqa: F401 + update_inference_endpoint, # noqa: F401 + update_repo_settings, # noqa: F401 + update_repo_visibility, # noqa: F401 + update_webhook, # noqa: F401 + upload_file, # noqa: F401 + upload_folder, # noqa: F401 + upload_large_folder, # noqa: F401 + whoami, # noqa: F401 + ) + from .hf_file_system import ( + HfFileSystem, # noqa: F401 + HfFileSystemFile, # noqa: F401 + HfFileSystemResolvedPath, # noqa: F401 + HfFileSystemStreamFile, # noqa: F401 + ) + from .hub_mixin import ( + ModelHubMixin, # noqa: F401 + PyTorchModelHubMixin, # noqa: F401 + ) + from .inference._client import ( + InferenceClient, # noqa: F401 + InferenceTimeoutError, # noqa: F401 + ) + from .inference._generated._async_client import AsyncInferenceClient # noqa: F401 + from .inference._generated.types import ( + AudioClassificationInput, # noqa: F401 + AudioClassificationOutputElement, # noqa: F401 + AudioClassificationOutputTransform, # noqa: F401 + AudioClassificationParameters, # noqa: F401 + AudioToAudioInput, # noqa: F401 + AudioToAudioOutputElement, # noqa: F401 + AutomaticSpeechRecognitionEarlyStoppingEnum, # noqa: F401 + AutomaticSpeechRecognitionGenerationParameters, # noqa: F401 + AutomaticSpeechRecognitionInput, # noqa: F401 + AutomaticSpeechRecognitionOutput, # noqa: F401 + AutomaticSpeechRecognitionOutputChunk, # noqa: F401 + AutomaticSpeechRecognitionParameters, # noqa: F401 + ChatCompletionInput, # noqa: F401 + ChatCompletionInputFunctionDefinition, # noqa: F401 + ChatCompletionInputFunctionName, # noqa: F401 + ChatCompletionInputGrammarType, # noqa: F401 + ChatCompletionInputJSONSchema, # noqa: F401 + ChatCompletionInputMessage, # noqa: F401 + ChatCompletionInputMessageChunk, # noqa: F401 + ChatCompletionInputMessageChunkType, # noqa: F401 + ChatCompletionInputResponseFormatJSONObject, # noqa: F401 + ChatCompletionInputResponseFormatJSONSchema, # noqa: F401 + ChatCompletionInputResponseFormatText, # noqa: F401 + ChatCompletionInputStreamOptions, # noqa: F401 + ChatCompletionInputTool, # noqa: F401 + ChatCompletionInputToolCall, # noqa: F401 + ChatCompletionInputToolChoiceClass, # noqa: F401 + ChatCompletionInputToolChoiceEnum, # noqa: F401 + ChatCompletionInputURL, # noqa: F401 + ChatCompletionOutput, # noqa: F401 + ChatCompletionOutputComplete, # noqa: F401 + ChatCompletionOutputFunctionDefinition, # noqa: F401 + ChatCompletionOutputLogprob, # noqa: F401 + ChatCompletionOutputLogprobs, # noqa: F401 + ChatCompletionOutputMessage, # noqa: F401 + ChatCompletionOutputToolCall, # noqa: F401 + ChatCompletionOutputTopLogprob, # noqa: F401 + ChatCompletionOutputUsage, # noqa: F401 + ChatCompletionStreamOutput, # noqa: F401 + ChatCompletionStreamOutputChoice, # noqa: F401 + ChatCompletionStreamOutputDelta, # noqa: F401 + ChatCompletionStreamOutputDeltaToolCall, # noqa: F401 + ChatCompletionStreamOutputFunction, # noqa: F401 + ChatCompletionStreamOutputLogprob, # noqa: F401 + ChatCompletionStreamOutputLogprobs, # noqa: F401 + ChatCompletionStreamOutputTopLogprob, # noqa: F401 + ChatCompletionStreamOutputUsage, # noqa: F401 + DepthEstimationInput, # noqa: F401 + DepthEstimationOutput, # noqa: F401 + DocumentQuestionAnsweringInput, # noqa: F401 + DocumentQuestionAnsweringInputData, # noqa: F401 + DocumentQuestionAnsweringOutputElement, # noqa: F401 + DocumentQuestionAnsweringParameters, # noqa: F401 + FeatureExtractionInput, # noqa: F401 + FeatureExtractionInputTruncationDirection, # noqa: F401 + FillMaskInput, # noqa: F401 + FillMaskOutputElement, # noqa: F401 + FillMaskParameters, # noqa: F401 + ImageClassificationInput, # noqa: F401 + ImageClassificationOutputElement, # noqa: F401 + ImageClassificationOutputTransform, # noqa: F401 + ImageClassificationParameters, # noqa: F401 + ImageSegmentationInput, # noqa: F401 + ImageSegmentationOutputElement, # noqa: F401 + ImageSegmentationParameters, # noqa: F401 + ImageSegmentationSubtask, # noqa: F401 + ImageToImageInput, # noqa: F401 + ImageToImageOutput, # noqa: F401 + ImageToImageParameters, # noqa: F401 + ImageToImageTargetSize, # noqa: F401 + ImageToTextEarlyStoppingEnum, # noqa: F401 + ImageToTextGenerationParameters, # noqa: F401 + ImageToTextInput, # noqa: F401 + ImageToTextOutput, # noqa: F401 + ImageToTextParameters, # noqa: F401 + ImageToVideoInput, # noqa: F401 + ImageToVideoOutput, # noqa: F401 + ImageToVideoParameters, # noqa: F401 + ImageToVideoTargetSize, # noqa: F401 + ObjectDetectionBoundingBox, # noqa: F401 + ObjectDetectionInput, # noqa: F401 + ObjectDetectionOutputElement, # noqa: F401 + ObjectDetectionParameters, # noqa: F401 + Padding, # noqa: F401 + QuestionAnsweringInput, # noqa: F401 + QuestionAnsweringInputData, # noqa: F401 + QuestionAnsweringOutputElement, # noqa: F401 + QuestionAnsweringParameters, # noqa: F401 + SentenceSimilarityInput, # noqa: F401 + SentenceSimilarityInputData, # noqa: F401 + SummarizationInput, # noqa: F401 + SummarizationOutput, # noqa: F401 + SummarizationParameters, # noqa: F401 + SummarizationTruncationStrategy, # noqa: F401 + TableQuestionAnsweringInput, # noqa: F401 + TableQuestionAnsweringInputData, # noqa: F401 + TableQuestionAnsweringOutputElement, # noqa: F401 + TableQuestionAnsweringParameters, # noqa: F401 + Text2TextGenerationInput, # noqa: F401 + Text2TextGenerationOutput, # noqa: F401 + Text2TextGenerationParameters, # noqa: F401 + Text2TextGenerationTruncationStrategy, # noqa: F401 + TextClassificationInput, # noqa: F401 + TextClassificationOutputElement, # noqa: F401 + TextClassificationOutputTransform, # noqa: F401 + TextClassificationParameters, # noqa: F401 + TextGenerationInput, # noqa: F401 + TextGenerationInputGenerateParameters, # noqa: F401 + TextGenerationInputGrammarType, # noqa: F401 + TextGenerationOutput, # noqa: F401 + TextGenerationOutputBestOfSequence, # noqa: F401 + TextGenerationOutputDetails, # noqa: F401 + TextGenerationOutputFinishReason, # noqa: F401 + TextGenerationOutputPrefillToken, # noqa: F401 + TextGenerationOutputToken, # noqa: F401 + TextGenerationStreamOutput, # noqa: F401 + TextGenerationStreamOutputStreamDetails, # noqa: F401 + TextGenerationStreamOutputToken, # noqa: F401 + TextToAudioEarlyStoppingEnum, # noqa: F401 + TextToAudioGenerationParameters, # noqa: F401 + TextToAudioInput, # noqa: F401 + TextToAudioOutput, # noqa: F401 + TextToAudioParameters, # noqa: F401 + TextToImageInput, # noqa: F401 + TextToImageOutput, # noqa: F401 + TextToImageParameters, # noqa: F401 + TextToSpeechEarlyStoppingEnum, # noqa: F401 + TextToSpeechGenerationParameters, # noqa: F401 + TextToSpeechInput, # noqa: F401 + TextToSpeechOutput, # noqa: F401 + TextToSpeechParameters, # noqa: F401 + TextToVideoInput, # noqa: F401 + TextToVideoOutput, # noqa: F401 + TextToVideoParameters, # noqa: F401 + TokenClassificationAggregationStrategy, # noqa: F401 + TokenClassificationInput, # noqa: F401 + TokenClassificationOutputElement, # noqa: F401 + TokenClassificationParameters, # noqa: F401 + TranslationInput, # noqa: F401 + TranslationOutput, # noqa: F401 + TranslationParameters, # noqa: F401 + TranslationTruncationStrategy, # noqa: F401 + TypeEnum, # noqa: F401 + VideoClassificationInput, # noqa: F401 + VideoClassificationOutputElement, # noqa: F401 + VideoClassificationOutputTransform, # noqa: F401 + VideoClassificationParameters, # noqa: F401 + VisualQuestionAnsweringInput, # noqa: F401 + VisualQuestionAnsweringInputData, # noqa: F401 + VisualQuestionAnsweringOutputElement, # noqa: F401 + VisualQuestionAnsweringParameters, # noqa: F401 + ZeroShotClassificationInput, # noqa: F401 + ZeroShotClassificationOutputElement, # noqa: F401 + ZeroShotClassificationParameters, # noqa: F401 + ZeroShotImageClassificationInput, # noqa: F401 + ZeroShotImageClassificationOutputElement, # noqa: F401 + ZeroShotImageClassificationParameters, # noqa: F401 + ZeroShotObjectDetectionBoundingBox, # noqa: F401 + ZeroShotObjectDetectionInput, # noqa: F401 + ZeroShotObjectDetectionOutputElement, # noqa: F401 + ZeroShotObjectDetectionParameters, # noqa: F401 + ) + from .inference._mcp.agent import Agent # noqa: F401 + from .inference._mcp.mcp_client import MCPClient # noqa: F401 + from .inference_api import InferenceApi # noqa: F401 + from .keras_mixin import ( + KerasModelHubMixin, # noqa: F401 + from_pretrained_keras, # noqa: F401 + push_to_hub_keras, # noqa: F401 + save_pretrained_keras, # noqa: F401 + ) + from .repocard import ( + DatasetCard, # noqa: F401 + ModelCard, # noqa: F401 + RepoCard, # noqa: F401 + SpaceCard, # noqa: F401 + metadata_eval_result, # noqa: F401 + metadata_load, # noqa: F401 + metadata_save, # noqa: F401 + metadata_update, # noqa: F401 + ) + from .repocard_data import ( + CardData, # noqa: F401 + DatasetCardData, # noqa: F401 + EvalResult, # noqa: F401 + ModelCardData, # noqa: F401 + SpaceCardData, # noqa: F401 + ) + from .repository import Repository # noqa: F401 + from .serialization import ( + StateDictSplit, # noqa: F401 + get_tf_storage_size, # noqa: F401 + get_torch_storage_id, # noqa: F401 + get_torch_storage_size, # noqa: F401 + load_state_dict_from_file, # noqa: F401 + load_torch_model, # noqa: F401 + save_torch_model, # noqa: F401 + save_torch_state_dict, # noqa: F401 + split_state_dict_into_shards_factory, # noqa: F401 + split_tf_state_dict_into_shards, # noqa: F401 + split_torch_state_dict_into_shards, # noqa: F401 + ) + from .serialization._dduf import ( + DDUFEntry, # noqa: F401 + export_entries_as_dduf, # noqa: F401 + export_folder_as_dduf, # noqa: F401 + read_dduf_file, # noqa: F401 + ) + from .utils import ( + CachedFileInfo, # noqa: F401 + CachedRepoInfo, # noqa: F401 + CachedRevisionInfo, # noqa: F401 + CacheNotFound, # noqa: F401 + CorruptedCacheException, # noqa: F401 + DeleteCacheStrategy, # noqa: F401 + HFCacheInfo, # noqa: F401 + HfFolder, # noqa: F401 + cached_assets_path, # noqa: F401 + configure_http_backend, # noqa: F401 + dump_environment_info, # noqa: F401 + get_session, # noqa: F401 + get_token, # noqa: F401 + logging, # noqa: F401 + scan_cache_dir, # noqa: F401 + ) diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/_commit_api.py b/.venv/lib/python3.12/site-packages/huggingface_hub/_commit_api.py new file mode 100644 index 0000000000000000000000000000000000000000..9e8fa86e6caf9e2db6ff5ce90f147267a2ab6e7d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/_commit_api.py @@ -0,0 +1,908 @@ +""" +Type definitions and utilities for the `create_commit` API +""" + +import base64 +import io +import os +import warnings +from collections import defaultdict +from contextlib import contextmanager +from dataclasses import dataclass, field +from itertools import groupby +from pathlib import Path, PurePosixPath +from typing import TYPE_CHECKING, Any, BinaryIO, Dict, Iterable, Iterator, List, Literal, Optional, Tuple, Union + +from tqdm.contrib.concurrent import thread_map + +from . import constants +from .errors import EntryNotFoundError, HfHubHTTPError, XetAuthorizationError, XetRefreshTokenError +from .file_download import hf_hub_url +from .lfs import UploadInfo, lfs_upload, post_lfs_batch_info +from .utils import ( + FORBIDDEN_FOLDERS, + XetTokenType, + are_progress_bars_disabled, + chunk_iterable, + fetch_xet_connection_info_from_repo_info, + get_session, + hf_raise_for_status, + logging, + sha, + tqdm_stream_file, + validate_hf_hub_args, +) +from .utils import tqdm as hf_tqdm + + +if TYPE_CHECKING: + from .hf_api import RepoFile + + +logger = logging.get_logger(__name__) + + +UploadMode = Literal["lfs", "regular"] + +# Max is 1,000 per request on the Hub for HfApi.get_paths_info +# Otherwise we get: +# HfHubHTTPError: 413 Client Error: Payload Too Large for url: https://huggingface.co/api/datasets/xxx (Request ID: xxx)\n\ntoo many parameters +# See https://github.com/huggingface/huggingface_hub/issues/1503 +FETCH_LFS_BATCH_SIZE = 500 + +UPLOAD_BATCH_MAX_NUM_FILES = 256 + + +@dataclass +class CommitOperationDelete: + """ + Data structure holding necessary info to delete a file or a folder from a repository + on the Hub. + + Args: + path_in_repo (`str`): + Relative filepath in the repo, for example: `"checkpoints/1fec34a/weights.bin"` + for a file or `"checkpoints/1fec34a/"` for a folder. + is_folder (`bool` or `Literal["auto"]`, *optional*) + Whether the Delete Operation applies to a folder or not. If "auto", the path + type (file or folder) is guessed automatically by looking if path ends with + a "/" (folder) or not (file). To explicitly set the path type, you can set + `is_folder=True` or `is_folder=False`. + """ + + path_in_repo: str + is_folder: Union[bool, Literal["auto"]] = "auto" + + def __post_init__(self): + self.path_in_repo = _validate_path_in_repo(self.path_in_repo) + + if self.is_folder == "auto": + self.is_folder = self.path_in_repo.endswith("/") + if not isinstance(self.is_folder, bool): + raise ValueError( + f"Wrong value for `is_folder`. Must be one of [`True`, `False`, `'auto'`]. Got '{self.is_folder}'." + ) + + +@dataclass +class CommitOperationCopy: + """ + Data structure holding necessary info to copy a file in a repository on the Hub. + + Limitations: + - Only LFS files can be copied. To copy a regular file, you need to download it locally and re-upload it + - Cross-repository copies are not supported. + + Note: you can combine a [`CommitOperationCopy`] and a [`CommitOperationDelete`] to rename an LFS file on the Hub. + + Args: + src_path_in_repo (`str`): + Relative filepath in the repo of the file to be copied, e.g. `"checkpoints/1fec34a/weights.bin"`. + path_in_repo (`str`): + Relative filepath in the repo where to copy the file, e.g. `"checkpoints/1fec34a/weights_copy.bin"`. + src_revision (`str`, *optional*): + The git revision of the file to be copied. Can be any valid git revision. + Default to the target commit revision. + """ + + src_path_in_repo: str + path_in_repo: str + src_revision: Optional[str] = None + # set to the OID of the file to be copied if it has already been uploaded + # useful to determine if a commit will be empty or not. + _src_oid: Optional[str] = None + # set to the OID of the file to copy to if it has already been uploaded + # useful to determine if a commit will be empty or not. + _dest_oid: Optional[str] = None + + def __post_init__(self): + self.src_path_in_repo = _validate_path_in_repo(self.src_path_in_repo) + self.path_in_repo = _validate_path_in_repo(self.path_in_repo) + + +@dataclass +class CommitOperationAdd: + """ + Data structure holding necessary info to upload a file to a repository on the Hub. + + Args: + path_in_repo (`str`): + Relative filepath in the repo, for example: `"checkpoints/1fec34a/weights.bin"` + path_or_fileobj (`str`, `Path`, `bytes`, or `BinaryIO`): + Either: + - a path to a local file (as `str` or `pathlib.Path`) to upload + - a buffer of bytes (`bytes`) holding the content of the file to upload + - a "file object" (subclass of `io.BufferedIOBase`), typically obtained + with `open(path, "rb")`. It must support `seek()` and `tell()` methods. + + Raises: + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If `path_or_fileobj` is not one of `str`, `Path`, `bytes` or `io.BufferedIOBase`. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If `path_or_fileobj` is a `str` or `Path` but not a path to an existing file. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If `path_or_fileobj` is a `io.BufferedIOBase` but it doesn't support both + `seek()` and `tell()`. + """ + + path_in_repo: str + path_or_fileobj: Union[str, Path, bytes, BinaryIO] + upload_info: UploadInfo = field(init=False, repr=False) + + # Internal attributes + + # set to "lfs" or "regular" once known + _upload_mode: Optional[UploadMode] = field(init=False, repr=False, default=None) + + # set to True if .gitignore rules prevent the file from being uploaded as LFS + # (server-side check) + _should_ignore: Optional[bool] = field(init=False, repr=False, default=None) + + # set to the remote OID of the file if it has already been uploaded + # useful to determine if a commit will be empty or not + _remote_oid: Optional[str] = field(init=False, repr=False, default=None) + + # set to True once the file has been uploaded as LFS + _is_uploaded: bool = field(init=False, repr=False, default=False) + + # set to True once the file has been committed + _is_committed: bool = field(init=False, repr=False, default=False) + + def __post_init__(self) -> None: + """Validates `path_or_fileobj` and compute `upload_info`.""" + self.path_in_repo = _validate_path_in_repo(self.path_in_repo) + + # Validate `path_or_fileobj` value + if isinstance(self.path_or_fileobj, Path): + self.path_or_fileobj = str(self.path_or_fileobj) + if isinstance(self.path_or_fileobj, str): + path_or_fileobj = os.path.normpath(os.path.expanduser(self.path_or_fileobj)) + if not os.path.isfile(path_or_fileobj): + raise ValueError(f"Provided path: '{path_or_fileobj}' is not a file on the local file system") + elif not isinstance(self.path_or_fileobj, (io.BufferedIOBase, bytes)): + # ^^ Inspired from: https://stackoverflow.com/questions/44584829/how-to-determine-if-file-is-opened-in-binary-or-text-mode + raise ValueError( + "path_or_fileobj must be either an instance of str, bytes or" + " io.BufferedIOBase. If you passed a file-like object, make sure it is" + " in binary mode." + ) + if isinstance(self.path_or_fileobj, io.BufferedIOBase): + try: + self.path_or_fileobj.tell() + self.path_or_fileobj.seek(0, os.SEEK_CUR) + except (OSError, AttributeError) as exc: + raise ValueError( + "path_or_fileobj is a file-like object but does not implement seek() and tell()" + ) from exc + + # Compute "upload_info" attribute + if isinstance(self.path_or_fileobj, str): + self.upload_info = UploadInfo.from_path(self.path_or_fileobj) + elif isinstance(self.path_or_fileobj, bytes): + self.upload_info = UploadInfo.from_bytes(self.path_or_fileobj) + else: + self.upload_info = UploadInfo.from_fileobj(self.path_or_fileobj) + + @contextmanager + def as_file(self, with_tqdm: bool = False) -> Iterator[BinaryIO]: + """ + A context manager that yields a file-like object allowing to read the underlying + data behind `path_or_fileobj`. + + Args: + with_tqdm (`bool`, *optional*, defaults to `False`): + If True, iterating over the file object will display a progress bar. Only + works if the file-like object is a path to a file. Pure bytes and buffers + are not supported. + + Example: + + ```python + >>> operation = CommitOperationAdd( + ... path_in_repo="remote/dir/weights.h5", + ... path_or_fileobj="./local/weights.h5", + ... ) + CommitOperationAdd(path_in_repo='remote/dir/weights.h5', path_or_fileobj='./local/weights.h5') + + >>> with operation.as_file() as file: + ... content = file.read() + + >>> with operation.as_file(with_tqdm=True) as file: + ... while True: + ... data = file.read(1024) + ... if not data: + ... break + config.json: 100%|█████████████████████████| 8.19k/8.19k [00:02<00:00, 3.72kB/s] + + >>> with operation.as_file(with_tqdm=True) as file: + ... requests.put(..., data=file) + config.json: 100%|█████████████████████████| 8.19k/8.19k [00:02<00:00, 3.72kB/s] + ``` + """ + if isinstance(self.path_or_fileobj, str) or isinstance(self.path_or_fileobj, Path): + if with_tqdm: + with tqdm_stream_file(self.path_or_fileobj) as file: + yield file + else: + with open(self.path_or_fileobj, "rb") as file: + yield file + elif isinstance(self.path_or_fileobj, bytes): + yield io.BytesIO(self.path_or_fileobj) + elif isinstance(self.path_or_fileobj, io.BufferedIOBase): + prev_pos = self.path_or_fileobj.tell() + yield self.path_or_fileobj + self.path_or_fileobj.seek(prev_pos, io.SEEK_SET) + + def b64content(self) -> bytes: + """ + The base64-encoded content of `path_or_fileobj` + + Returns: `bytes` + """ + with self.as_file() as file: + return base64.b64encode(file.read()) + + @property + def _local_oid(self) -> Optional[str]: + """Return the OID of the local file. + + This OID is then compared to `self._remote_oid` to check if the file has changed compared to the remote one. + If the file did not change, we won't upload it again to prevent empty commits. + + For LFS files, the OID corresponds to the SHA256 of the file content (used a LFS ref). + For regular files, the OID corresponds to the SHA1 of the file content. + Note: this is slightly different to git OID computation since the oid of an LFS file is usually the git-SHA1 of the + pointer file content (not the actual file content). However, using the SHA256 is enough to detect changes + and more convenient client-side. + """ + if self._upload_mode is None: + return None + elif self._upload_mode == "lfs": + return self.upload_info.sha256.hex() + else: + # Regular file => compute sha1 + # => no need to read by chunk since the file is guaranteed to be <=5MB. + with self.as_file() as file: + return sha.git_hash(file.read()) + + +def _validate_path_in_repo(path_in_repo: str) -> str: + # Validate `path_in_repo` value to prevent a server-side issue + if path_in_repo.startswith("/"): + path_in_repo = path_in_repo[1:] + if path_in_repo == "." or path_in_repo == ".." or path_in_repo.startswith("../"): + raise ValueError(f"Invalid `path_in_repo` in CommitOperation: '{path_in_repo}'") + if path_in_repo.startswith("./"): + path_in_repo = path_in_repo[2:] + for forbidden in FORBIDDEN_FOLDERS: + if any(part == forbidden for part in path_in_repo.split("/")): + raise ValueError( + f"Invalid `path_in_repo` in CommitOperation: cannot update files under a '{forbidden}/' folder (path:" + f" '{path_in_repo}')." + ) + return path_in_repo + + +CommitOperation = Union[CommitOperationAdd, CommitOperationCopy, CommitOperationDelete] + + +def _warn_on_overwriting_operations(operations: List[CommitOperation]) -> None: + """ + Warn user when a list of operations is expected to overwrite itself in a single + commit. + + Rules: + - If a filepath is updated by multiple `CommitOperationAdd` operations, a warning + message is triggered. + - If a filepath is updated at least once by a `CommitOperationAdd` and then deleted + by a `CommitOperationDelete`, a warning is triggered. + - If a `CommitOperationDelete` deletes a filepath that is then updated by a + `CommitOperationAdd`, no warning is triggered. This is usually useless (no need to + delete before upload) but can happen if a user deletes an entire folder and then + add new files to it. + """ + nb_additions_per_path: Dict[str, int] = defaultdict(int) + for operation in operations: + path_in_repo = operation.path_in_repo + if isinstance(operation, CommitOperationAdd): + if nb_additions_per_path[path_in_repo] > 0: + warnings.warn( + "About to update multiple times the same file in the same commit:" + f" '{path_in_repo}'. This can cause undesired inconsistencies in" + " your repo." + ) + nb_additions_per_path[path_in_repo] += 1 + for parent in PurePosixPath(path_in_repo).parents: + # Also keep track of number of updated files per folder + # => warns if deleting a folder overwrite some contained files + nb_additions_per_path[str(parent)] += 1 + if isinstance(operation, CommitOperationDelete): + if nb_additions_per_path[str(PurePosixPath(path_in_repo))] > 0: + if operation.is_folder: + warnings.warn( + "About to delete a folder containing files that have just been" + f" updated within the same commit: '{path_in_repo}'. This can" + " cause undesired inconsistencies in your repo." + ) + else: + warnings.warn( + "About to delete a file that have just been updated within the" + f" same commit: '{path_in_repo}'. This can cause undesired" + " inconsistencies in your repo." + ) + + +@validate_hf_hub_args +def _upload_lfs_files( + *, + additions: List[CommitOperationAdd], + repo_type: str, + repo_id: str, + headers: Dict[str, str], + endpoint: Optional[str] = None, + num_threads: int = 5, + revision: Optional[str] = None, +): + """ + Uploads the content of `additions` to the Hub using the large file storage protocol. + + Relevant external documentation: + - LFS Batch API: https://github.com/git-lfs/git-lfs/blob/main/docs/api/batch.md + + Args: + additions (`List` of `CommitOperationAdd`): + The files to be uploaded + repo_type (`str`): + Type of the repo to upload to: `"model"`, `"dataset"` or `"space"`. + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + headers (`Dict[str, str]`): + Headers to use for the request, including authorization headers and user agent. + num_threads (`int`, *optional*): + The number of concurrent threads to use when uploading. Defaults to 5. + revision (`str`, *optional*): + The git revision to upload to. + + Raises: + [`EnvironmentError`](https://docs.python.org/3/library/exceptions.html#EnvironmentError) + If an upload failed for any reason + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If the server returns malformed responses + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError) + If the LFS batch endpoint returned an HTTP error. + """ + # Step 1: retrieve upload instructions from the LFS batch endpoint. + # Upload instructions are retrieved by chunk of 256 files to avoid reaching + # the payload limit. + batch_actions: List[Dict] = [] + for chunk in chunk_iterable(additions, chunk_size=UPLOAD_BATCH_MAX_NUM_FILES): + batch_actions_chunk, batch_errors_chunk = post_lfs_batch_info( + upload_infos=[op.upload_info for op in chunk], + repo_id=repo_id, + repo_type=repo_type, + revision=revision, + endpoint=endpoint, + headers=headers, + token=None, # already passed in 'headers' + ) + + # If at least 1 error, we do not retrieve information for other chunks + if batch_errors_chunk: + message = "\n".join( + [ + f"Encountered error for file with OID {err.get('oid')}: `{err.get('error', {}).get('message')}" + for err in batch_errors_chunk + ] + ) + raise ValueError(f"LFS batch endpoint returned errors:\n{message}") + + batch_actions += batch_actions_chunk + oid2addop = {add_op.upload_info.sha256.hex(): add_op for add_op in additions} + + # Step 2: ignore files that have already been uploaded + filtered_actions = [] + for action in batch_actions: + if action.get("actions") is None: + logger.debug( + f"Content of file {oid2addop[action['oid']].path_in_repo} is already" + " present upstream - skipping upload." + ) + else: + filtered_actions.append(action) + + if len(filtered_actions) == 0: + logger.debug("No LFS files to upload.") + return + + # Step 3: upload files concurrently according to these instructions + def _wrapped_lfs_upload(batch_action) -> None: + try: + operation = oid2addop[batch_action["oid"]] + lfs_upload(operation=operation, lfs_batch_action=batch_action, headers=headers, endpoint=endpoint) + except Exception as exc: + raise RuntimeError(f"Error while uploading '{operation.path_in_repo}' to the Hub.") from exc + + if constants.HF_HUB_ENABLE_HF_TRANSFER: + logger.debug(f"Uploading {len(filtered_actions)} LFS files to the Hub using `hf_transfer`.") + for action in hf_tqdm(filtered_actions, name="huggingface_hub.lfs_upload"): + _wrapped_lfs_upload(action) + elif len(filtered_actions) == 1: + logger.debug("Uploading 1 LFS file to the Hub") + _wrapped_lfs_upload(filtered_actions[0]) + else: + logger.debug( + f"Uploading {len(filtered_actions)} LFS files to the Hub using up to {num_threads} threads concurrently" + ) + thread_map( + _wrapped_lfs_upload, + filtered_actions, + desc=f"Upload {len(filtered_actions)} LFS files", + max_workers=num_threads, + tqdm_class=hf_tqdm, + ) + + +@validate_hf_hub_args +def _upload_xet_files( + *, + additions: List[CommitOperationAdd], + repo_type: str, + repo_id: str, + headers: Dict[str, str], + endpoint: Optional[str] = None, + revision: Optional[str] = None, + create_pr: Optional[bool] = None, +): + """ + Uploads the content of `additions` to the Hub using the xet storage protocol. + This chunks the files and deduplicates the chunks before uploading them to xetcas storage. + + Args: + additions (`List` of `CommitOperationAdd`): + The files to be uploaded. + repo_type (`str`): + Type of the repo to upload to: `"model"`, `"dataset"` or `"space"`. + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + headers (`Dict[str, str]`): + Headers to use for the request, including authorization headers and user agent. + endpoint: (`str`, *optional*): + The endpoint to use for the xetcas service. Defaults to `constants.ENDPOINT`. + revision (`str`, *optional*): + The git revision to upload to. + create_pr (`bool`, *optional*): + Whether or not to create a Pull Request with that commit. + + Raises: + [`EnvironmentError`](https://docs.python.org/3/library/exceptions.html#EnvironmentError) + If an upload failed for any reason. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If the server returns malformed responses or if the user is unauthorized to upload to xet storage. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError) + If the LFS batch endpoint returned an HTTP error. + + **How it works:** + The file download system uses Xet storage, which is a content-addressable storage system that breaks files into chunks + for efficient storage and transfer. + + `hf_xet.upload_files` manages uploading files by: + - Taking a list of file paths to upload + - Breaking files into smaller chunks for efficient storage + - Avoiding duplicate storage by recognizing identical chunks across files + - Connecting to a storage server (CAS server) that manages these chunks + + The upload process works like this: + 1. Create a local folder at ~/.cache/huggingface/xet/chunk-cache to store file chunks for reuse. + 2. Process files in parallel (up to 8 files at once): + 2.1. Read the file content. + 2.2. Split the file content into smaller chunks based on content patterns: each chunk gets a unique ID based on what's in it. + 2.3. For each chunk: + - Check if it already exists in storage. + - Skip uploading chunks that already exist. + 2.4. Group chunks into larger blocks for efficient transfer. + 2.5. Upload these blocks to the storage server. + 2.6. Create and upload information about how the file is structured. + 3. Return reference files that contain information about the uploaded files, which can be used later to download them. + """ + if len(additions) == 0: + return + + # at this point, we know that hf_xet is installed + from hf_xet import upload_bytes, upload_files + + from .utils._xet_progress_reporting import XetProgressReporter + + try: + xet_connection_info = fetch_xet_connection_info_from_repo_info( + token_type=XetTokenType.WRITE, + repo_id=repo_id, + repo_type=repo_type, + revision=revision, + headers=headers, + endpoint=endpoint, + params={"create_pr": "1"} if create_pr else None, + ) + except HfHubHTTPError as e: + if e.response.status_code == 401: + raise XetAuthorizationError( + f"You are unauthorized to upload to xet storage for {repo_type}/{repo_id}. " + f"Please check that you have configured your access token with write access to the repo." + ) from e + raise + + xet_endpoint = xet_connection_info.endpoint + access_token_info = (xet_connection_info.access_token, xet_connection_info.expiration_unix_epoch) + + def token_refresher() -> Tuple[str, int]: + new_xet_connection = fetch_xet_connection_info_from_repo_info( + token_type=XetTokenType.WRITE, + repo_id=repo_id, + repo_type=repo_type, + revision=revision, + headers=headers, + endpoint=endpoint, + params={"create_pr": "1"} if create_pr else None, + ) + if new_xet_connection is None: + raise XetRefreshTokenError("Failed to refresh xet token") + return new_xet_connection.access_token, new_xet_connection.expiration_unix_epoch + + if not are_progress_bars_disabled(): + progress = XetProgressReporter() + progress_callback = progress.update_progress + else: + progress, progress_callback = None, None + + try: + for i, chunk in enumerate(chunk_iterable(additions, chunk_size=UPLOAD_BATCH_MAX_NUM_FILES)): + _chunk = [op for op in chunk] + + bytes_ops = [op for op in _chunk if isinstance(op.path_or_fileobj, bytes)] + paths_ops = [op for op in _chunk if isinstance(op.path_or_fileobj, (str, Path))] + + if len(paths_ops) > 0: + upload_files( + [str(op.path_or_fileobj) for op in paths_ops], + xet_endpoint, + access_token_info, + token_refresher, + progress_callback, + repo_type, + ) + if len(bytes_ops) > 0: + upload_bytes( + [op.path_or_fileobj for op in bytes_ops], + xet_endpoint, + access_token_info, + token_refresher, + progress_callback, + repo_type, + ) + + finally: + if progress is not None: + progress.close(False) + + return + + +def _validate_preupload_info(preupload_info: dict): + files = preupload_info.get("files") + if not isinstance(files, list): + raise ValueError("preupload_info is improperly formatted") + for file_info in files: + if not ( + isinstance(file_info, dict) + and isinstance(file_info.get("path"), str) + and isinstance(file_info.get("uploadMode"), str) + and (file_info["uploadMode"] in ("lfs", "regular")) + ): + raise ValueError("preupload_info is improperly formatted:") + return preupload_info + + +@validate_hf_hub_args +def _fetch_upload_modes( + additions: Iterable[CommitOperationAdd], + repo_type: str, + repo_id: str, + headers: Dict[str, str], + revision: str, + endpoint: Optional[str] = None, + create_pr: bool = False, + gitignore_content: Optional[str] = None, +) -> None: + """ + Requests the Hub "preupload" endpoint to determine whether each input file should be uploaded as a regular git blob, + as a git LFS blob, or as a XET file. Input `additions` are mutated in-place with the upload mode. + + Args: + additions (`Iterable` of :class:`CommitOperationAdd`): + Iterable of :class:`CommitOperationAdd` describing the files to + upload to the Hub. + repo_type (`str`): + Type of the repo to upload to: `"model"`, `"dataset"` or `"space"`. + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + headers (`Dict[str, str]`): + Headers to use for the request, including authorization headers and user agent. + revision (`str`): + The git revision to upload the files to. Can be any valid git revision. + gitignore_content (`str`, *optional*): + The content of the `.gitignore` file to know which files should be ignored. The order of priority + is to first check if `gitignore_content` is passed, then check if the `.gitignore` file is present + in the list of files to commit and finally default to the `.gitignore` file already hosted on the Hub + (if any). + Raises: + [`~utils.HfHubHTTPError`] + If the Hub API returned an error. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If the Hub API response is improperly formatted. + """ + endpoint = endpoint if endpoint is not None else constants.ENDPOINT + + # Fetch upload mode (LFS or regular) chunk by chunk. + upload_modes: Dict[str, UploadMode] = {} + should_ignore_info: Dict[str, bool] = {} + oid_info: Dict[str, Optional[str]] = {} + + for chunk in chunk_iterable(additions, 256): + payload: Dict = { + "files": [ + { + "path": op.path_in_repo, + "sample": base64.b64encode(op.upload_info.sample).decode("ascii"), + "size": op.upload_info.size, + } + for op in chunk + ] + } + if gitignore_content is not None: + payload["gitIgnore"] = gitignore_content + + resp = get_session().post( + f"{endpoint}/api/{repo_type}s/{repo_id}/preupload/{revision}", + json=payload, + headers=headers, + params={"create_pr": "1"} if create_pr else None, + ) + hf_raise_for_status(resp) + preupload_info = _validate_preupload_info(resp.json()) + upload_modes.update(**{file["path"]: file["uploadMode"] for file in preupload_info["files"]}) + should_ignore_info.update(**{file["path"]: file["shouldIgnore"] for file in preupload_info["files"]}) + oid_info.update(**{file["path"]: file.get("oid") for file in preupload_info["files"]}) + + # Set upload mode for each addition operation + for addition in additions: + addition._upload_mode = upload_modes[addition.path_in_repo] + addition._should_ignore = should_ignore_info[addition.path_in_repo] + addition._remote_oid = oid_info[addition.path_in_repo] + + # Empty files cannot be uploaded as LFS (S3 would fail with a 501 Not Implemented) + # => empty files are uploaded as "regular" to still allow users to commit them. + for addition in additions: + if addition.upload_info.size == 0: + addition._upload_mode = "regular" + + +@validate_hf_hub_args +def _fetch_files_to_copy( + copies: Iterable[CommitOperationCopy], + repo_type: str, + repo_id: str, + headers: Dict[str, str], + revision: str, + endpoint: Optional[str] = None, +) -> Dict[Tuple[str, Optional[str]], Union["RepoFile", bytes]]: + """ + Fetch information about the files to copy. + + For LFS files, we only need their metadata (file size and sha256) while for regular files + we need to download the raw content from the Hub. + + Args: + copies (`Iterable` of :class:`CommitOperationCopy`): + Iterable of :class:`CommitOperationCopy` describing the files to + copy on the Hub. + repo_type (`str`): + Type of the repo to upload to: `"model"`, `"dataset"` or `"space"`. + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + headers (`Dict[str, str]`): + Headers to use for the request, including authorization headers and user agent. + revision (`str`): + The git revision to upload the files to. Can be any valid git revision. + + Returns: `Dict[Tuple[str, Optional[str]], Union[RepoFile, bytes]]]` + Key is the file path and revision of the file to copy. + Value is the raw content as bytes (for regular files) or the file information as a RepoFile (for LFS files). + + Raises: + [`~utils.HfHubHTTPError`] + If the Hub API returned an error. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If the Hub API response is improperly formatted. + """ + from .hf_api import HfApi, RepoFolder + + hf_api = HfApi(endpoint=endpoint, headers=headers) + files_to_copy: Dict[Tuple[str, Optional[str]], Union["RepoFile", bytes]] = {} + # Store (path, revision) -> oid mapping + oid_info: Dict[Tuple[str, Optional[str]], Optional[str]] = {} + # 1. Fetch OIDs for destination paths in batches. + dest_paths = [op.path_in_repo for op in copies] + for offset in range(0, len(dest_paths), FETCH_LFS_BATCH_SIZE): + dest_repo_files = hf_api.get_paths_info( + repo_id=repo_id, + paths=dest_paths[offset : offset + FETCH_LFS_BATCH_SIZE], + revision=revision, + repo_type=repo_type, + ) + for file in dest_repo_files: + if not isinstance(file, RepoFolder): + oid_info[(file.path, revision)] = file.blob_id + + # 2. Group by source revision and fetch source file info in batches. + for src_revision, operations in groupby(copies, key=lambda op: op.src_revision): + operations = list(operations) # type: ignore + src_paths = [op.src_path_in_repo for op in operations] + for offset in range(0, len(src_paths), FETCH_LFS_BATCH_SIZE): + src_repo_files = hf_api.get_paths_info( + repo_id=repo_id, + paths=src_paths[offset : offset + FETCH_LFS_BATCH_SIZE], + revision=src_revision or revision, + repo_type=repo_type, + ) + + for src_repo_file in src_repo_files: + if isinstance(src_repo_file, RepoFolder): + raise NotImplementedError("Copying a folder is not implemented.") + oid_info[(src_repo_file.path, src_revision)] = src_repo_file.blob_id + # If it's an LFS file, store the RepoFile object. Otherwise, download raw bytes. + if src_repo_file.lfs: + files_to_copy[(src_repo_file.path, src_revision)] = src_repo_file + else: + # TODO: (optimization) download regular files to copy concurrently + url = hf_hub_url( + endpoint=endpoint, + repo_type=repo_type, + repo_id=repo_id, + revision=src_revision or revision, + filename=src_repo_file.path, + ) + response = get_session().get(url, headers=headers) + hf_raise_for_status(response) + files_to_copy[(src_repo_file.path, src_revision)] = response.content + # 3. Ensure all operations found a corresponding file in the Hub + # and track src/dest OIDs for each operation. + for operation in operations: + if (operation.src_path_in_repo, src_revision) not in files_to_copy: + raise EntryNotFoundError( + f"Cannot copy {operation.src_path_in_repo} at revision " + f"{src_revision or revision}: file is missing on repo." + ) + operation._src_oid = oid_info.get((operation.src_path_in_repo, operation.src_revision)) + operation._dest_oid = oid_info.get((operation.path_in_repo, revision)) + return files_to_copy + + +def _prepare_commit_payload( + operations: Iterable[CommitOperation], + files_to_copy: Dict[Tuple[str, Optional[str]], Union["RepoFile", bytes]], + commit_message: str, + commit_description: Optional[str] = None, + parent_commit: Optional[str] = None, +) -> Iterable[Dict[str, Any]]: + """ + Builds the payload to POST to the `/commit` API of the Hub. + + Payload is returned as an iterator so that it can be streamed as a ndjson in the + POST request. + + For more information, see: + - https://github.com/huggingface/huggingface_hub/issues/1085#issuecomment-1265208073 + - http://ndjson.org/ + """ + commit_description = commit_description if commit_description is not None else "" + + # 1. Send a header item with the commit metadata + header_value = {"summary": commit_message, "description": commit_description} + if parent_commit is not None: + header_value["parentCommit"] = parent_commit + yield {"key": "header", "value": header_value} + + nb_ignored_files = 0 + + # 2. Send operations, one per line + for operation in operations: + # Skip ignored files + if isinstance(operation, CommitOperationAdd) and operation._should_ignore: + logger.debug(f"Skipping file '{operation.path_in_repo}' in commit (ignored by gitignore file).") + nb_ignored_files += 1 + continue + + # 2.a. Case adding a regular file + if isinstance(operation, CommitOperationAdd) and operation._upload_mode == "regular": + yield { + "key": "file", + "value": { + "content": operation.b64content().decode(), + "path": operation.path_in_repo, + "encoding": "base64", + }, + } + # 2.b. Case adding an LFS file + elif isinstance(operation, CommitOperationAdd) and operation._upload_mode == "lfs": + yield { + "key": "lfsFile", + "value": { + "path": operation.path_in_repo, + "algo": "sha256", + "oid": operation.upload_info.sha256.hex(), + "size": operation.upload_info.size, + }, + } + # 2.c. Case deleting a file or folder + elif isinstance(operation, CommitOperationDelete): + yield { + "key": "deletedFolder" if operation.is_folder else "deletedFile", + "value": {"path": operation.path_in_repo}, + } + # 2.d. Case copying a file or folder + elif isinstance(operation, CommitOperationCopy): + file_to_copy = files_to_copy[(operation.src_path_in_repo, operation.src_revision)] + if isinstance(file_to_copy, bytes): + yield { + "key": "file", + "value": { + "content": base64.b64encode(file_to_copy).decode(), + "path": operation.path_in_repo, + "encoding": "base64", + }, + } + elif file_to_copy.lfs: + yield { + "key": "lfsFile", + "value": { + "path": operation.path_in_repo, + "algo": "sha256", + "oid": file_to_copy.lfs.sha256, + }, + } + else: + raise ValueError( + "Malformed files_to_copy (should be raw file content as bytes or RepoFile objects with LFS info." + ) + # 2.e. Never expected to happen + else: + raise ValueError( + f"Unknown operation to commit. Operation: {operation}. Upload mode:" + f" {getattr(operation, '_upload_mode', None)}" + ) + + if nb_ignored_files > 0: + logger.info(f"Skipped {nb_ignored_files} file(s) in commit (ignored by gitignore file).") diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/_commit_scheduler.py b/.venv/lib/python3.12/site-packages/huggingface_hub/_commit_scheduler.py new file mode 100644 index 0000000000000000000000000000000000000000..f1f20339e7df2d17588623dc13bb3c6be6a46b53 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/_commit_scheduler.py @@ -0,0 +1,353 @@ +import atexit +import logging +import os +import time +from concurrent.futures import Future +from dataclasses import dataclass +from io import SEEK_END, SEEK_SET, BytesIO +from pathlib import Path +from threading import Lock, Thread +from typing import Dict, List, Optional, Union + +from .hf_api import DEFAULT_IGNORE_PATTERNS, CommitInfo, CommitOperationAdd, HfApi +from .utils import filter_repo_objects + + +logger = logging.getLogger(__name__) + + +@dataclass(frozen=True) +class _FileToUpload: + """Temporary dataclass to store info about files to upload. Not meant to be used directly.""" + + local_path: Path + path_in_repo: str + size_limit: int + last_modified: float + + +class CommitScheduler: + """ + Scheduler to upload a local folder to the Hub at regular intervals (e.g. push to hub every 5 minutes). + + The recommended way to use the scheduler is to use it as a context manager. This ensures that the scheduler is + properly stopped and the last commit is triggered when the script ends. The scheduler can also be stopped manually + with the `stop` method. Checkout the [upload guide](https://huggingface.co/docs/huggingface_hub/guides/upload#scheduled-uploads) + to learn more about how to use it. + + Args: + repo_id (`str`): + The id of the repo to commit to. + folder_path (`str` or `Path`): + Path to the local folder to upload regularly. + every (`int` or `float`, *optional*): + The number of minutes between each commit. Defaults to 5 minutes. + path_in_repo (`str`, *optional*): + Relative path of the directory in the repo, for example: `"checkpoints/"`. Defaults to the root folder + of the repository. + repo_type (`str`, *optional*): + The type of the repo to commit to. Defaults to `model`. + revision (`str`, *optional*): + The revision of the repo to commit to. Defaults to `main`. + private (`bool`, *optional*): + Whether to make the repo private. If `None` (default), the repo will be public unless the organization's default is private. This value is ignored if the repo already exists. + token (`str`, *optional*): + The token to use to commit to the repo. Defaults to the token saved on the machine. + allow_patterns (`List[str]` or `str`, *optional*): + If provided, only files matching at least one pattern are uploaded. + ignore_patterns (`List[str]` or `str`, *optional*): + If provided, files matching any of the patterns are not uploaded. + squash_history (`bool`, *optional*): + Whether to squash the history of the repo after each commit. Defaults to `False`. Squashing commits is + useful to avoid degraded performances on the repo when it grows too large. + hf_api (`HfApi`, *optional*): + The [`HfApi`] client to use to commit to the Hub. Can be set with custom settings (user agent, token,...). + + Example: + ```py + >>> from pathlib import Path + >>> from huggingface_hub import CommitScheduler + + # Scheduler uploads every 10 minutes + >>> csv_path = Path("watched_folder/data.csv") + >>> CommitScheduler(repo_id="test_scheduler", repo_type="dataset", folder_path=csv_path.parent, every=10) + + >>> with csv_path.open("a") as f: + ... f.write("first line") + + # Some time later (...) + >>> with csv_path.open("a") as f: + ... f.write("second line") + ``` + + Example using a context manager: + ```py + >>> from pathlib import Path + >>> from huggingface_hub import CommitScheduler + + >>> with CommitScheduler(repo_id="test_scheduler", repo_type="dataset", folder_path="watched_folder", every=10) as scheduler: + ... csv_path = Path("watched_folder/data.csv") + ... with csv_path.open("a") as f: + ... f.write("first line") + ... (...) + ... with csv_path.open("a") as f: + ... f.write("second line") + + # Scheduler is now stopped and last commit have been triggered + ``` + """ + + def __init__( + self, + *, + repo_id: str, + folder_path: Union[str, Path], + every: Union[int, float] = 5, + path_in_repo: Optional[str] = None, + repo_type: Optional[str] = None, + revision: Optional[str] = None, + private: Optional[bool] = None, + token: Optional[str] = None, + allow_patterns: Optional[Union[List[str], str]] = None, + ignore_patterns: Optional[Union[List[str], str]] = None, + squash_history: bool = False, + hf_api: Optional["HfApi"] = None, + ) -> None: + self.api = hf_api or HfApi(token=token) + + # Folder + self.folder_path = Path(folder_path).expanduser().resolve() + self.path_in_repo = path_in_repo or "" + self.allow_patterns = allow_patterns + + if ignore_patterns is None: + ignore_patterns = [] + elif isinstance(ignore_patterns, str): + ignore_patterns = [ignore_patterns] + self.ignore_patterns = ignore_patterns + DEFAULT_IGNORE_PATTERNS + + if self.folder_path.is_file(): + raise ValueError(f"'folder_path' must be a directory, not a file: '{self.folder_path}'.") + self.folder_path.mkdir(parents=True, exist_ok=True) + + # Repository + repo_url = self.api.create_repo(repo_id=repo_id, private=private, repo_type=repo_type, exist_ok=True) + self.repo_id = repo_url.repo_id + self.repo_type = repo_type + self.revision = revision + self.token = token + + # Keep track of already uploaded files + self.last_uploaded: Dict[Path, float] = {} # key is local path, value is timestamp + + # Scheduler + if not every > 0: + raise ValueError(f"'every' must be a positive integer, not '{every}'.") + self.lock = Lock() + self.every = every + self.squash_history = squash_history + + logger.info(f"Scheduled job to push '{self.folder_path}' to '{self.repo_id}' every {self.every} minutes.") + self._scheduler_thread = Thread(target=self._run_scheduler, daemon=True) + self._scheduler_thread.start() + atexit.register(self._push_to_hub) + + self.__stopped = False + + def stop(self) -> None: + """Stop the scheduler. + + A stopped scheduler cannot be restarted. Mostly for tests purposes. + """ + self.__stopped = True + + def __enter__(self) -> "CommitScheduler": + return self + + def __exit__(self, exc_type, exc_value, traceback) -> None: + # Upload last changes before exiting + self.trigger().result() + self.stop() + return + + def _run_scheduler(self) -> None: + """Dumb thread waiting between each scheduled push to Hub.""" + while True: + self.last_future = self.trigger() + time.sleep(self.every * 60) + if self.__stopped: + break + + def trigger(self) -> Future: + """Trigger a `push_to_hub` and return a future. + + This method is automatically called every `every` minutes. You can also call it manually to trigger a commit + immediately, without waiting for the next scheduled commit. + """ + return self.api.run_as_future(self._push_to_hub) + + def _push_to_hub(self) -> Optional[CommitInfo]: + if self.__stopped: # If stopped, already scheduled commits are ignored + return None + + logger.info("(Background) scheduled commit triggered.") + try: + value = self.push_to_hub() + if self.squash_history: + logger.info("(Background) squashing repo history.") + self.api.super_squash_history(repo_id=self.repo_id, repo_type=self.repo_type, branch=self.revision) + return value + except Exception as e: + logger.error(f"Error while pushing to Hub: {e}") # Depending on the setup, error might be silenced + raise + + def push_to_hub(self) -> Optional[CommitInfo]: + """ + Push folder to the Hub and return the commit info. + + + + This method is not meant to be called directly. It is run in the background by the scheduler, respecting a + queue mechanism to avoid concurrent commits. Making a direct call to the method might lead to concurrency + issues. + + + + The default behavior of `push_to_hub` is to assume an append-only folder. It lists all files in the folder and + uploads only changed files. If no changes are found, the method returns without committing anything. If you want + to change this behavior, you can inherit from [`CommitScheduler`] and override this method. This can be useful + for example to compress data together in a single file before committing. For more details and examples, check + out our [integration guide](https://huggingface.co/docs/huggingface_hub/main/en/guides/upload#scheduled-uploads). + """ + # Check files to upload (with lock) + with self.lock: + logger.debug("Listing files to upload for scheduled commit.") + + # List files from folder (taken from `_prepare_upload_folder_additions`) + relpath_to_abspath = { + path.relative_to(self.folder_path).as_posix(): path + for path in sorted(self.folder_path.glob("**/*")) # sorted to be deterministic + if path.is_file() + } + prefix = f"{self.path_in_repo.strip('/')}/" if self.path_in_repo else "" + + # Filter with pattern + filter out unchanged files + retrieve current file size + files_to_upload: List[_FileToUpload] = [] + for relpath in filter_repo_objects( + relpath_to_abspath.keys(), allow_patterns=self.allow_patterns, ignore_patterns=self.ignore_patterns + ): + local_path = relpath_to_abspath[relpath] + stat = local_path.stat() + if self.last_uploaded.get(local_path) is None or self.last_uploaded[local_path] != stat.st_mtime: + files_to_upload.append( + _FileToUpload( + local_path=local_path, + path_in_repo=prefix + relpath, + size_limit=stat.st_size, + last_modified=stat.st_mtime, + ) + ) + + # Return if nothing to upload + if len(files_to_upload) == 0: + logger.debug("Dropping schedule commit: no changed file to upload.") + return None + + # Convert `_FileToUpload` as `CommitOperationAdd` (=> compute file shas + limit to file size) + logger.debug("Removing unchanged files since previous scheduled commit.") + add_operations = [ + CommitOperationAdd( + # Cap the file to its current size, even if the user append data to it while a scheduled commit is happening + path_or_fileobj=PartialFileIO(file_to_upload.local_path, size_limit=file_to_upload.size_limit), + path_in_repo=file_to_upload.path_in_repo, + ) + for file_to_upload in files_to_upload + ] + + # Upload files (append mode expected - no need for lock) + logger.debug("Uploading files for scheduled commit.") + commit_info = self.api.create_commit( + repo_id=self.repo_id, + repo_type=self.repo_type, + operations=add_operations, + commit_message="Scheduled Commit", + revision=self.revision, + ) + + # Successful commit: keep track of the latest "last_modified" for each file + for file in files_to_upload: + self.last_uploaded[file.local_path] = file.last_modified + return commit_info + + +class PartialFileIO(BytesIO): + """A file-like object that reads only the first part of a file. + + Useful to upload a file to the Hub when the user might still be appending data to it. Only the first part of the + file is uploaded (i.e. the part that was available when the filesystem was first scanned). + + In practice, only used internally by the CommitScheduler to regularly push a folder to the Hub with minimal + disturbance for the user. The object is passed to `CommitOperationAdd`. + + Only supports `read`, `tell` and `seek` methods. + + Args: + file_path (`str` or `Path`): + Path to the file to read. + size_limit (`int`): + The maximum number of bytes to read from the file. If the file is larger than this, only the first part + will be read (and uploaded). + """ + + def __init__(self, file_path: Union[str, Path], size_limit: int) -> None: + self._file_path = Path(file_path) + self._file = self._file_path.open("rb") + self._size_limit = min(size_limit, os.fstat(self._file.fileno()).st_size) + + def __del__(self) -> None: + self._file.close() + return super().__del__() + + def __repr__(self) -> str: + return f"" + + def __len__(self) -> int: + return self._size_limit + + def __getattribute__(self, name: str): + if name.startswith("_") or name in ("read", "tell", "seek"): # only 3 public methods supported + return super().__getattribute__(name) + raise NotImplementedError(f"PartialFileIO does not support '{name}'.") + + def tell(self) -> int: + """Return the current file position.""" + return self._file.tell() + + def seek(self, __offset: int, __whence: int = SEEK_SET) -> int: + """Change the stream position to the given offset. + + Behavior is the same as a regular file, except that the position is capped to the size limit. + """ + if __whence == SEEK_END: + # SEEK_END => set from the truncated end + __offset = len(self) + __offset + __whence = SEEK_SET + + pos = self._file.seek(__offset, __whence) + if pos > self._size_limit: + return self._file.seek(self._size_limit) + return pos + + def read(self, __size: Optional[int] = -1) -> bytes: + """Read at most `__size` bytes from the file. + + Behavior is the same as a regular file, except that it is capped to the size limit. + """ + current = self._file.tell() + if __size is None or __size < 0: + # Read until file limit + truncated_size = self._size_limit - current + else: + # Read until file limit or __size + truncated_size = min(__size, self._size_limit - current) + return self._file.read(truncated_size) diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/_inference_endpoints.py b/.venv/lib/python3.12/site-packages/huggingface_hub/_inference_endpoints.py new file mode 100644 index 0000000000000000000000000000000000000000..37f772bfbe28013ff5329d0a19a438706d50a19c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/_inference_endpoints.py @@ -0,0 +1,413 @@ +import time +from dataclasses import dataclass, field +from datetime import datetime +from enum import Enum +from typing import TYPE_CHECKING, Dict, Optional, Union + +from huggingface_hub.errors import InferenceEndpointError, InferenceEndpointTimeoutError + +from .utils import get_session, logging, parse_datetime + + +if TYPE_CHECKING: + from .hf_api import HfApi + from .inference._client import InferenceClient + from .inference._generated._async_client import AsyncInferenceClient + +logger = logging.get_logger(__name__) + + +class InferenceEndpointStatus(str, Enum): + PENDING = "pending" + INITIALIZING = "initializing" + UPDATING = "updating" + UPDATE_FAILED = "updateFailed" + RUNNING = "running" + PAUSED = "paused" + FAILED = "failed" + SCALED_TO_ZERO = "scaledToZero" + + +class InferenceEndpointType(str, Enum): + PUBlIC = "public" + PROTECTED = "protected" + PRIVATE = "private" + + +@dataclass +class InferenceEndpoint: + """ + Contains information about a deployed Inference Endpoint. + + Args: + name (`str`): + The unique name of the Inference Endpoint. + namespace (`str`): + The namespace where the Inference Endpoint is located. + repository (`str`): + The name of the model repository deployed on this Inference Endpoint. + status ([`InferenceEndpointStatus`]): + The current status of the Inference Endpoint. + url (`str`, *optional*): + The URL of the Inference Endpoint, if available. Only a deployed Inference Endpoint will have a URL. + framework (`str`): + The machine learning framework used for the model. + revision (`str`): + The specific model revision deployed on the Inference Endpoint. + task (`str`): + The task associated with the deployed model. + created_at (`datetime.datetime`): + The timestamp when the Inference Endpoint was created. + updated_at (`datetime.datetime`): + The timestamp of the last update of the Inference Endpoint. + type ([`InferenceEndpointType`]): + The type of the Inference Endpoint (public, protected, private). + raw (`Dict`): + The raw dictionary data returned from the API. + token (`str` or `bool`, *optional*): + Authentication token for the Inference Endpoint, if set when requesting the API. Will default to the + locally saved token if not provided. Pass `token=False` if you don't want to send your token to the server. + + Example: + ```python + >>> from huggingface_hub import get_inference_endpoint + >>> endpoint = get_inference_endpoint("my-text-to-image") + >>> endpoint + InferenceEndpoint(name='my-text-to-image', ...) + + # Get status + >>> endpoint.status + 'running' + >>> endpoint.url + 'https://my-text-to-image.region.vendor.endpoints.huggingface.cloud' + + # Run inference + >>> endpoint.client.text_to_image(...) + + # Pause endpoint to save $$$ + >>> endpoint.pause() + + # ... + # Resume and wait for deployment + >>> endpoint.resume() + >>> endpoint.wait() + >>> endpoint.client.text_to_image(...) + ``` + """ + + # Field in __repr__ + name: str = field(init=False) + namespace: str + repository: str = field(init=False) + status: InferenceEndpointStatus = field(init=False) + health_route: str = field(init=False) + url: Optional[str] = field(init=False) + + # Other fields + framework: str = field(repr=False, init=False) + revision: str = field(repr=False, init=False) + task: str = field(repr=False, init=False) + created_at: datetime = field(repr=False, init=False) + updated_at: datetime = field(repr=False, init=False) + type: InferenceEndpointType = field(repr=False, init=False) + + # Raw dict from the API + raw: Dict = field(repr=False) + + # Internal fields + _token: Union[str, bool, None] = field(repr=False, compare=False) + _api: "HfApi" = field(repr=False, compare=False) + + @classmethod + def from_raw( + cls, raw: Dict, namespace: str, token: Union[str, bool, None] = None, api: Optional["HfApi"] = None + ) -> "InferenceEndpoint": + """Initialize object from raw dictionary.""" + if api is None: + from .hf_api import HfApi + + api = HfApi() + if token is None: + token = api.token + + # All other fields are populated in __post_init__ + return cls(raw=raw, namespace=namespace, _token=token, _api=api) + + def __post_init__(self) -> None: + """Populate fields from raw dictionary.""" + self._populate_from_raw() + + @property + def client(self) -> "InferenceClient": + """Returns a client to make predictions on this Inference Endpoint. + + Returns: + [`InferenceClient`]: an inference client pointing to the deployed endpoint. + + Raises: + [`InferenceEndpointError`]: If the Inference Endpoint is not yet deployed. + """ + if self.url is None: + raise InferenceEndpointError( + "Cannot create a client for this Inference Endpoint as it is not yet deployed. " + "Please wait for the Inference Endpoint to be deployed using `endpoint.wait()` and try again." + ) + from .inference._client import InferenceClient + + return InferenceClient( + model=self.url, + token=self._token, # type: ignore[arg-type] # boolean token shouldn't be possible. In practice it's ok. + ) + + @property + def async_client(self) -> "AsyncInferenceClient": + """Returns a client to make predictions on this Inference Endpoint. + + Returns: + [`AsyncInferenceClient`]: an asyncio-compatible inference client pointing to the deployed endpoint. + + Raises: + [`InferenceEndpointError`]: If the Inference Endpoint is not yet deployed. + """ + if self.url is None: + raise InferenceEndpointError( + "Cannot create a client for this Inference Endpoint as it is not yet deployed. " + "Please wait for the Inference Endpoint to be deployed using `endpoint.wait()` and try again." + ) + from .inference._generated._async_client import AsyncInferenceClient + + return AsyncInferenceClient( + model=self.url, + token=self._token, # type: ignore[arg-type] # boolean token shouldn't be possible. In practice it's ok. + ) + + def wait(self, timeout: Optional[int] = None, refresh_every: int = 5) -> "InferenceEndpoint": + """Wait for the Inference Endpoint to be deployed. + + Information from the server will be fetched every 1s. If the Inference Endpoint is not deployed after `timeout` + seconds, a [`InferenceEndpointTimeoutError`] will be raised. The [`InferenceEndpoint`] will be mutated in place with the latest + data. + + Args: + timeout (`int`, *optional*): + The maximum time to wait for the Inference Endpoint to be deployed, in seconds. If `None`, will wait + indefinitely. + refresh_every (`int`, *optional*): + The time to wait between each fetch of the Inference Endpoint status, in seconds. Defaults to 5s. + + Returns: + [`InferenceEndpoint`]: the same Inference Endpoint, mutated in place with the latest data. + + Raises: + [`InferenceEndpointError`] + If the Inference Endpoint ended up in a failed state. + [`InferenceEndpointTimeoutError`] + If the Inference Endpoint is not deployed after `timeout` seconds. + """ + if timeout is not None and timeout < 0: + raise ValueError("`timeout` cannot be negative.") + if refresh_every <= 0: + raise ValueError("`refresh_every` must be positive.") + + start = time.time() + while True: + if self.status == InferenceEndpointStatus.FAILED: + raise InferenceEndpointError( + f"Inference Endpoint {self.name} failed to deploy. Please check the logs for more information." + ) + if self.status == InferenceEndpointStatus.UPDATE_FAILED: + raise InferenceEndpointError( + f"Inference Endpoint {self.name} failed to update. Please check the logs for more information." + ) + if self.status == InferenceEndpointStatus.RUNNING and self.url is not None: + # Verify the endpoint is actually reachable + _health_url = f"{self.url.rstrip('/')}/{self.health_route.lstrip('/')}" + response = get_session().get(_health_url, headers=self._api._build_hf_headers(token=self._token)) + if response.status_code == 200: + logger.info("Inference Endpoint is ready to be used.") + return self + + if timeout is not None: + if time.time() - start > timeout: + raise InferenceEndpointTimeoutError("Timeout while waiting for Inference Endpoint to be deployed.") + logger.info(f"Inference Endpoint is not deployed yet ({self.status}). Waiting {refresh_every}s...") + time.sleep(refresh_every) + self.fetch() + + def fetch(self) -> "InferenceEndpoint": + """Fetch latest information about the Inference Endpoint. + + Returns: + [`InferenceEndpoint`]: the same Inference Endpoint, mutated in place with the latest data. + """ + obj = self._api.get_inference_endpoint(name=self.name, namespace=self.namespace, token=self._token) # type: ignore [arg-type] + self.raw = obj.raw + self._populate_from_raw() + return self + + def update( + self, + *, + # Compute update + accelerator: Optional[str] = None, + instance_size: Optional[str] = None, + instance_type: Optional[str] = None, + min_replica: Optional[int] = None, + max_replica: Optional[int] = None, + scale_to_zero_timeout: Optional[int] = None, + # Model update + repository: Optional[str] = None, + framework: Optional[str] = None, + revision: Optional[str] = None, + task: Optional[str] = None, + custom_image: Optional[Dict] = None, + secrets: Optional[Dict[str, str]] = None, + ) -> "InferenceEndpoint": + """Update the Inference Endpoint. + + This method allows the update of either the compute configuration, the deployed model, or both. All arguments are + optional but at least one must be provided. + + This is an alias for [`HfApi.update_inference_endpoint`]. The current object is mutated in place with the + latest data from the server. + + Args: + accelerator (`str`, *optional*): + The hardware accelerator to be used for inference (e.g. `"cpu"`). + instance_size (`str`, *optional*): + The size or type of the instance to be used for hosting the model (e.g. `"x4"`). + instance_type (`str`, *optional*): + The cloud instance type where the Inference Endpoint will be deployed (e.g. `"intel-icl"`). + min_replica (`int`, *optional*): + The minimum number of replicas (instances) to keep running for the Inference Endpoint. + max_replica (`int`, *optional*): + The maximum number of replicas (instances) to scale to for the Inference Endpoint. + scale_to_zero_timeout (`int`, *optional*): + The duration in minutes before an inactive endpoint is scaled to zero. + + repository (`str`, *optional*): + The name of the model repository associated with the Inference Endpoint (e.g. `"gpt2"`). + framework (`str`, *optional*): + The machine learning framework used for the model (e.g. `"custom"`). + revision (`str`, *optional*): + The specific model revision to deploy on the Inference Endpoint (e.g. `"6c0e6080953db56375760c0471a8c5f2929baf11"`). + task (`str`, *optional*): + The task on which to deploy the model (e.g. `"text-classification"`). + custom_image (`Dict`, *optional*): + A custom Docker image to use for the Inference Endpoint. This is useful if you want to deploy an + Inference Endpoint running on the `text-generation-inference` (TGI) framework (see examples). + secrets (`Dict[str, str]`, *optional*): + Secret values to inject in the container environment. + Returns: + [`InferenceEndpoint`]: the same Inference Endpoint, mutated in place with the latest data. + """ + # Make API call + obj = self._api.update_inference_endpoint( + name=self.name, + namespace=self.namespace, + accelerator=accelerator, + instance_size=instance_size, + instance_type=instance_type, + min_replica=min_replica, + max_replica=max_replica, + scale_to_zero_timeout=scale_to_zero_timeout, + repository=repository, + framework=framework, + revision=revision, + task=task, + custom_image=custom_image, + secrets=secrets, + token=self._token, # type: ignore [arg-type] + ) + + # Mutate current object + self.raw = obj.raw + self._populate_from_raw() + return self + + def pause(self) -> "InferenceEndpoint": + """Pause the Inference Endpoint. + + A paused Inference Endpoint will not be charged. It can be resumed at any time using [`InferenceEndpoint.resume`]. + This is different than scaling the Inference Endpoint to zero with [`InferenceEndpoint.scale_to_zero`], which + would be automatically restarted when a request is made to it. + + This is an alias for [`HfApi.pause_inference_endpoint`]. The current object is mutated in place with the + latest data from the server. + + Returns: + [`InferenceEndpoint`]: the same Inference Endpoint, mutated in place with the latest data. + """ + obj = self._api.pause_inference_endpoint(name=self.name, namespace=self.namespace, token=self._token) # type: ignore [arg-type] + self.raw = obj.raw + self._populate_from_raw() + return self + + def resume(self, running_ok: bool = True) -> "InferenceEndpoint": + """Resume the Inference Endpoint. + + This is an alias for [`HfApi.resume_inference_endpoint`]. The current object is mutated in place with the + latest data from the server. + + Args: + running_ok (`bool`, *optional*): + If `True`, the method will not raise an error if the Inference Endpoint is already running. Defaults to + `True`. + + Returns: + [`InferenceEndpoint`]: the same Inference Endpoint, mutated in place with the latest data. + """ + obj = self._api.resume_inference_endpoint( + name=self.name, namespace=self.namespace, running_ok=running_ok, token=self._token + ) # type: ignore [arg-type] + self.raw = obj.raw + self._populate_from_raw() + return self + + def scale_to_zero(self) -> "InferenceEndpoint": + """Scale Inference Endpoint to zero. + + An Inference Endpoint scaled to zero will not be charged. It will be resume on the next request to it, with a + cold start delay. This is different than pausing the Inference Endpoint with [`InferenceEndpoint.pause`], which + would require a manual resume with [`InferenceEndpoint.resume`]. + + This is an alias for [`HfApi.scale_to_zero_inference_endpoint`]. The current object is mutated in place with the + latest data from the server. + + Returns: + [`InferenceEndpoint`]: the same Inference Endpoint, mutated in place with the latest data. + """ + obj = self._api.scale_to_zero_inference_endpoint(name=self.name, namespace=self.namespace, token=self._token) # type: ignore [arg-type] + self.raw = obj.raw + self._populate_from_raw() + return self + + def delete(self) -> None: + """Delete the Inference Endpoint. + + This operation is not reversible. If you don't want to be charged for an Inference Endpoint, it is preferable + to pause it with [`InferenceEndpoint.pause`] or scale it to zero with [`InferenceEndpoint.scale_to_zero`]. + + This is an alias for [`HfApi.delete_inference_endpoint`]. + """ + self._api.delete_inference_endpoint(name=self.name, namespace=self.namespace, token=self._token) # type: ignore [arg-type] + + def _populate_from_raw(self) -> None: + """Populate fields from raw dictionary. + + Called in __post_init__ + each time the Inference Endpoint is updated. + """ + # Repr fields + self.name = self.raw["name"] + self.repository = self.raw["model"]["repository"] + self.status = self.raw["status"]["state"] + self.url = self.raw["status"].get("url") + self.health_route = self.raw["healthRoute"] + + # Other fields + self.framework = self.raw["model"]["framework"] + self.revision = self.raw["model"]["revision"] + self.task = self.raw["model"]["task"] + self.created_at = parse_datetime(self.raw["status"]["createdAt"]) + self.updated_at = parse_datetime(self.raw["status"]["updatedAt"]) + self.type = self.raw["type"] diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/_jobs_api.py b/.venv/lib/python3.12/site-packages/huggingface_hub/_jobs_api.py new file mode 100644 index 0000000000000000000000000000000000000000..623fd9dc9dc77456ca2aec27042b7e99cda97d8c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/_jobs_api.py @@ -0,0 +1,301 @@ +# coding=utf-8 +# Copyright 2025-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from dataclasses import dataclass +from datetime import datetime +from enum import Enum +from typing import Any, Dict, List, Optional, Union + +from huggingface_hub import constants +from huggingface_hub._space_api import SpaceHardware +from huggingface_hub.utils._datetime import parse_datetime + + +class JobStage(str, Enum): + """ + Enumeration of possible stage of a Job on the Hub. + + Value can be compared to a string: + ```py + assert JobStage.COMPLETED == "COMPLETED" + ``` + + Taken from https://github.com/huggingface/moon-landing/blob/main/server/job_types/JobInfo.ts#L61 (private url). + """ + + # Copied from moon-landing > server > lib > Job.ts + COMPLETED = "COMPLETED" + CANCELED = "CANCELED" + ERROR = "ERROR" + DELETED = "DELETED" + RUNNING = "RUNNING" + + +@dataclass +class JobStatus: + stage: JobStage + message: Optional[str] + + +@dataclass +class JobOwner: + id: str + name: str + type: str + + +@dataclass +class JobInfo: + """ + Contains information about a Job. + + Args: + id (`str`): + Job ID. + created_at (`datetime` or `None`): + When the Job was created. + docker_image (`str` or `None`): + The Docker image from Docker Hub used for the Job. + Can be None if space_id is present instead. + space_id (`str` or `None`): + The Docker image from Hugging Face Spaces used for the Job. + Can be None if docker_image is present instead. + command (`List[str]` or `None`): + Command of the Job, e.g. `["python", "-c", "print('hello world')"]` + arguments (`List[str]` or `None`): + Arguments passed to the command + environment (`Dict[str]` or `None`): + Environment variables of the Job as a dictionary. + secrets (`Dict[str]` or `None`): + Secret environment variables of the Job (encrypted). + flavor (`str` or `None`): + Flavor for the hardware, as in Hugging Face Spaces. See [`SpaceHardware`] for possible values. + E.g. `"cpu-basic"`. + status: (`JobStatus` or `None`): + Status of the Job, e.g. `JobStatus(stage="RUNNING", message=None)` + See [`JobStage`] for possible stage values. + owner: (`JobOwner` or `None`): + Owner of the Job, e.g. `JobOwner(id="5e9ecfc04957053f60648a3e", name="lhoestq", type="user")` + + Example: + + ```python + >>> from huggingface_hub import run_job + >>> job = run_job( + ... image="python:3.12", + ... command=["python", "-c", "print('Hello from the cloud!')"] + ... ) + >>> job + JobInfo(id='687fb701029421ae5549d998', created_at=datetime.datetime(2025, 7, 22, 16, 6, 25, 79000, tzinfo=datetime.timezone.utc), docker_image='python:3.12', space_id=None, command=['python', '-c', "print('Hello from the cloud!')"], arguments=[], environment={}, secrets={}, flavor='cpu-basic', status=JobStatus(stage='RUNNING', message=None), owner=JobOwner(id='5e9ecfc04957053f60648a3e', name='lhoestq', type='user'), endpoint='https://huggingface.co', url='https://huggingface.co/jobs/lhoestq/687fb701029421ae5549d998') + >>> job.id + '687fb701029421ae5549d998' + >>> job.url + 'https://huggingface.co/jobs/lhoestq/687fb701029421ae5549d998' + >>> job.status.stage + 'RUNNING' + ``` + """ + + id: str + created_at: Optional[datetime] + docker_image: Optional[str] + space_id: Optional[str] + command: Optional[List[str]] + arguments: Optional[List[str]] + environment: Optional[Dict[str, Any]] + secrets: Optional[Dict[str, Any]] + flavor: Optional[SpaceHardware] + status: JobStatus + owner: JobOwner + + # Inferred fields + endpoint: str + url: str + + def __init__(self, **kwargs) -> None: + self.id = kwargs["id"] + created_at = kwargs.get("createdAt") or kwargs.get("created_at") + self.created_at = parse_datetime(created_at) if created_at else None + self.docker_image = kwargs.get("dockerImage") or kwargs.get("docker_image") + self.space_id = kwargs.get("spaceId") or kwargs.get("space_id") + owner = kwargs.get("owner", {}) + self.owner = JobOwner(id=owner["id"], name=owner["name"], type=owner["type"]) + self.command = kwargs.get("command") + self.arguments = kwargs.get("arguments") + self.environment = kwargs.get("environment") + self.secrets = kwargs.get("secrets") + self.flavor = kwargs.get("flavor") + status = kwargs.get("status", {}) + self.status = JobStatus(stage=status["stage"], message=status.get("message")) + + # Inferred fields + self.endpoint = kwargs.get("endpoint", constants.ENDPOINT) + self.url = f"{self.endpoint}/jobs/{self.owner.name}/{self.id}" + + +@dataclass +class JobSpec: + docker_image: Optional[str] + space_id: Optional[str] + command: Optional[List[str]] + arguments: Optional[List[str]] + environment: Optional[Dict[str, Any]] + secrets: Optional[Dict[str, Any]] + flavor: Optional[SpaceHardware] + timeout: Optional[int] + tags: Optional[List[str]] + arch: Optional[str] + + def __init__(self, **kwargs) -> None: + self.docker_image = kwargs.get("dockerImage") or kwargs.get("docker_image") + self.space_id = kwargs.get("spaceId") or kwargs.get("space_id") + self.command = kwargs.get("command") + self.arguments = kwargs.get("arguments") + self.environment = kwargs.get("environment") + self.secrets = kwargs.get("secrets") + self.flavor = kwargs.get("flavor") + self.timeout = kwargs.get("timeout") + self.tags = kwargs.get("tags") + self.arch = kwargs.get("arch") + + +@dataclass +class LastJobInfo: + id: str + at: datetime + + def __init__(self, **kwargs) -> None: + self.id = kwargs["id"] + self.at = parse_datetime(kwargs["at"]) + + +@dataclass +class ScheduledJobStatus: + last_job: Optional[LastJobInfo] + next_job_run_at: Optional[datetime] + + def __init__(self, **kwargs) -> None: + last_job = kwargs.get("lastJob") or kwargs.get("last_job") + self.last_job = LastJobInfo(**last_job) if last_job else None + next_job_run_at = kwargs.get("nextJobRunAt") or kwargs.get("next_job_run_at") + self.next_job_run_at = parse_datetime(str(next_job_run_at)) if next_job_run_at else None + + +@dataclass +class ScheduledJobInfo: + """ + Contains information about a Job. + + Args: + id (`str`): + Scheduled Job ID. + created_at (`datetime` or `None`): + When the scheduled Job was created. + tags (`List[str]` or `None`): + The tags of the scheduled Job. + schedule (`str` or `None`): + One of "@annually", "@yearly", "@monthly", "@weekly", "@daily", "@hourly", or a + CRON schedule expression (e.g., '0 9 * * 1' for 9 AM every Monday). + suspend (`bool` or `None`): + Whether the scheduled job is suspended (paused). + concurrency (`bool` or `None`): + Whether multiple instances of this Job can run concurrently. + status (`ScheduledJobStatus` or `None`): + Status of the scheduled Job. + owner: (`JobOwner` or `None`): + Owner of the scheduled Job, e.g. `JobOwner(id="5e9ecfc04957053f60648a3e", name="lhoestq", type="user")` + job_spec: (`JobSpec` or `None`): + Specifications of the Job. + + Example: + + ```python + >>> from huggingface_hub import run_job + >>> scheduled_job = create_scheduled_job( + ... image="python:3.12", + ... command=["python", "-c", "print('Hello from the cloud!')"], + ... schedule="@hourly", + ... ) + >>> scheduled_job.id + '687fb701029421ae5549d999' + >>> scheduled_job.status.next_job_run_at + datetime.datetime(2025, 7, 22, 17, 6, 25, 79000, tzinfo=datetime.timezone.utc) + ``` + """ + + id: str + created_at: Optional[datetime] + job_spec: JobSpec + schedule: Optional[str] + suspend: Optional[bool] + concurrency: Optional[bool] + status: ScheduledJobStatus + owner: JobOwner + + def __init__(self, **kwargs) -> None: + self.id = kwargs["id"] + created_at = kwargs.get("createdAt") or kwargs.get("created_at") + self.created_at = parse_datetime(created_at) if created_at else None + self.job_spec = JobSpec(**(kwargs.get("job_spec") or kwargs.get("jobSpec", {}))) + self.schedule = kwargs.get("schedule") + self.suspend = kwargs.get("suspend") + self.concurrency = kwargs.get("concurrency") + status = kwargs.get("status", {}) + self.status = ScheduledJobStatus( + last_job=status.get("last_job") or status.get("lastJob"), + next_job_run_at=status.get("next_job_run_at") or status.get("nextJobRunAt"), + ) + owner = kwargs.get("owner", {}) + self.owner = JobOwner(id=owner["id"], name=owner["name"], type=owner["type"]) + + +def _create_job_spec( + *, + image: str, + command: List[str], + env: Optional[Dict[str, Any]], + secrets: Optional[Dict[str, Any]], + flavor: Optional[SpaceHardware], + timeout: Optional[Union[int, float, str]], +) -> Dict[str, Any]: + # prepare job spec to send to HF Jobs API + job_spec: Dict[str, Any] = { + "command": command, + "arguments": [], + "environment": env or {}, + "flavor": flavor or SpaceHardware.CPU_BASIC, + } + # secrets are optional + if secrets: + job_spec["secrets"] = secrets + # timeout is optional + if timeout: + time_units_factors = {"s": 1, "m": 60, "h": 3600, "d": 3600 * 24} + if isinstance(timeout, str) and timeout[-1] in time_units_factors: + job_spec["timeoutSeconds"] = int(float(timeout[:-1]) * time_units_factors[timeout[-1]]) + else: + job_spec["timeoutSeconds"] = int(timeout) + # input is either from docker hub or from HF spaces + for prefix in ( + "https://huggingface.co/spaces/", + "https://hf.co/spaces/", + "huggingface.co/spaces/", + "hf.co/spaces/", + ): + if image.startswith(prefix): + job_spec["spaceId"] = image[len(prefix) :] + break + else: + job_spec["dockerImage"] = image + return job_spec diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/_local_folder.py b/.venv/lib/python3.12/site-packages/huggingface_hub/_local_folder.py new file mode 100644 index 0000000000000000000000000000000000000000..37f6c32a760ecf03794c129735fe2e15516952d1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/_local_folder.py @@ -0,0 +1,447 @@ +# coding=utf-8 +# Copyright 2024-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains utilities to handle the `../.cache/huggingface` folder in local directories. + +First discussed in https://github.com/huggingface/huggingface_hub/issues/1738 to store +download metadata when downloading files from the hub to a local directory (without +using the cache). + +./.cache/huggingface folder structure: +[4.0K] data +├── [4.0K] .cache +│ └── [4.0K] huggingface +│ └── [4.0K] download +│ ├── [ 16] file.parquet.metadata +│ ├── [ 16] file.txt.metadata +│ └── [4.0K] folder +│ └── [ 16] file.parquet.metadata +│ +├── [6.5G] file.parquet +├── [1.5K] file.txt +└── [4.0K] folder + └── [ 16] file.parquet + + +Download metadata file structure: +``` +# file.txt.metadata +11c5a3d5811f50298f278a704980280950aedb10 +a16a55fda99d2f2e7b69cce5cf93ff4ad3049930 +1712656091.123 + +# file.parquet.metadata +11c5a3d5811f50298f278a704980280950aedb10 +7c5d3f4b8b76583b422fcb9189ad6c89d5d97a094541ce8932dce3ecabde1421 +1712656091.123 +} +``` +""" + +import base64 +import hashlib +import logging +import os +import time +from dataclasses import dataclass +from pathlib import Path +from typing import Optional + +from .utils import WeakFileLock + + +logger = logging.getLogger(__name__) + + +@dataclass +class LocalDownloadFilePaths: + """ + Paths to the files related to a download process in a local dir. + + Returned by [`get_local_download_paths`]. + + Attributes: + file_path (`Path`): + Path where the file will be saved. + lock_path (`Path`): + Path to the lock file used to ensure atomicity when reading/writing metadata. + metadata_path (`Path`): + Path to the metadata file. + """ + + file_path: Path + lock_path: Path + metadata_path: Path + + def incomplete_path(self, etag: str) -> Path: + """Return the path where a file will be temporarily downloaded before being moved to `file_path`.""" + path = self.metadata_path.parent / f"{_short_hash(self.metadata_path.name)}.{etag}.incomplete" + resolved_path = str(path.resolve()) + # Some Windows versions do not allow for paths longer than 255 characters. + # In this case, we must specify it as an extended path by using the "\\?\" prefix. + if os.name == "nt" and len(resolved_path) > 255 and not resolved_path.startswith("\\\\?\\"): + path = Path("\\\\?\\" + resolved_path) + return path + + +@dataclass(frozen=True) +class LocalUploadFilePaths: + """ + Paths to the files related to an upload process in a local dir. + + Returned by [`get_local_upload_paths`]. + + Attributes: + path_in_repo (`str`): + Path of the file in the repo. + file_path (`Path`): + Path where the file will be saved. + lock_path (`Path`): + Path to the lock file used to ensure atomicity when reading/writing metadata. + metadata_path (`Path`): + Path to the metadata file. + """ + + path_in_repo: str + file_path: Path + lock_path: Path + metadata_path: Path + + +@dataclass +class LocalDownloadFileMetadata: + """ + Metadata about a file in the local directory related to a download process. + + Attributes: + filename (`str`): + Path of the file in the repo. + commit_hash (`str`): + Commit hash of the file in the repo. + etag (`str`): + ETag of the file in the repo. Used to check if the file has changed. + For LFS files, this is the sha256 of the file. For regular files, it corresponds to the git hash. + timestamp (`int`): + Unix timestamp of when the metadata was saved i.e. when the metadata was accurate. + """ + + filename: str + commit_hash: str + etag: str + timestamp: float + + +@dataclass +class LocalUploadFileMetadata: + """ + Metadata about a file in the local directory related to an upload process. + """ + + size: int + + # Default values correspond to "we don't know yet" + timestamp: Optional[float] = None + should_ignore: Optional[bool] = None + sha256: Optional[str] = None + upload_mode: Optional[str] = None + remote_oid: Optional[str] = None + is_uploaded: bool = False + is_committed: bool = False + + def save(self, paths: LocalUploadFilePaths) -> None: + """Save the metadata to disk.""" + with WeakFileLock(paths.lock_path): + with paths.metadata_path.open("w") as f: + new_timestamp = time.time() + f.write(str(new_timestamp) + "\n") + + f.write(str(self.size)) # never None + f.write("\n") + + if self.should_ignore is not None: + f.write(str(int(self.should_ignore))) + f.write("\n") + + if self.sha256 is not None: + f.write(self.sha256) + f.write("\n") + + if self.upload_mode is not None: + f.write(self.upload_mode) + f.write("\n") + + if self.remote_oid is not None: + f.write(self.remote_oid) + f.write("\n") + + f.write(str(int(self.is_uploaded)) + "\n") + f.write(str(int(self.is_committed)) + "\n") + + self.timestamp = new_timestamp + + +def get_local_download_paths(local_dir: Path, filename: str) -> LocalDownloadFilePaths: + """Compute paths to the files related to a download process. + + Folders containing the paths are all guaranteed to exist. + + Args: + local_dir (`Path`): + Path to the local directory in which files are downloaded. + filename (`str`): + Path of the file in the repo. + + Return: + [`LocalDownloadFilePaths`]: the paths to the files (file_path, lock_path, metadata_path, incomplete_path). + """ + # filename is the path in the Hub repository (separated by '/') + # make sure to have a cross platform transcription + sanitized_filename = os.path.join(*filename.split("/")) + if os.name == "nt": + if sanitized_filename.startswith("..\\") or "\\..\\" in sanitized_filename: + raise ValueError( + f"Invalid filename: cannot handle filename '{sanitized_filename}' on Windows. Please ask the repository" + " owner to rename this file." + ) + file_path = local_dir / sanitized_filename + metadata_path = _huggingface_dir(local_dir) / "download" / f"{sanitized_filename}.metadata" + lock_path = metadata_path.with_suffix(".lock") + + # Some Windows versions do not allow for paths longer than 255 characters. + # In this case, we must specify it as an extended path by using the "\\?\" prefix + if os.name == "nt": + if not str(local_dir).startswith("\\\\?\\") and len(os.path.abspath(lock_path)) > 255: + file_path = Path("\\\\?\\" + os.path.abspath(file_path)) + lock_path = Path("\\\\?\\" + os.path.abspath(lock_path)) + metadata_path = Path("\\\\?\\" + os.path.abspath(metadata_path)) + + file_path.parent.mkdir(parents=True, exist_ok=True) + metadata_path.parent.mkdir(parents=True, exist_ok=True) + return LocalDownloadFilePaths(file_path=file_path, lock_path=lock_path, metadata_path=metadata_path) + + +def get_local_upload_paths(local_dir: Path, filename: str) -> LocalUploadFilePaths: + """Compute paths to the files related to an upload process. + + Folders containing the paths are all guaranteed to exist. + + Args: + local_dir (`Path`): + Path to the local directory that is uploaded. + filename (`str`): + Path of the file in the repo. + + Return: + [`LocalUploadFilePaths`]: the paths to the files (file_path, lock_path, metadata_path). + """ + # filename is the path in the Hub repository (separated by '/') + # make sure to have a cross platform transcription + sanitized_filename = os.path.join(*filename.split("/")) + if os.name == "nt": + if sanitized_filename.startswith("..\\") or "\\..\\" in sanitized_filename: + raise ValueError( + f"Invalid filename: cannot handle filename '{sanitized_filename}' on Windows. Please ask the repository" + " owner to rename this file." + ) + file_path = local_dir / sanitized_filename + metadata_path = _huggingface_dir(local_dir) / "upload" / f"{sanitized_filename}.metadata" + lock_path = metadata_path.with_suffix(".lock") + + # Some Windows versions do not allow for paths longer than 255 characters. + # In this case, we must specify it as an extended path by using the "\\?\" prefix + if os.name == "nt": + if not str(local_dir).startswith("\\\\?\\") and len(os.path.abspath(lock_path)) > 255: + file_path = Path("\\\\?\\" + os.path.abspath(file_path)) + lock_path = Path("\\\\?\\" + os.path.abspath(lock_path)) + metadata_path = Path("\\\\?\\" + os.path.abspath(metadata_path)) + + file_path.parent.mkdir(parents=True, exist_ok=True) + metadata_path.parent.mkdir(parents=True, exist_ok=True) + return LocalUploadFilePaths( + path_in_repo=filename, file_path=file_path, lock_path=lock_path, metadata_path=metadata_path + ) + + +def read_download_metadata(local_dir: Path, filename: str) -> Optional[LocalDownloadFileMetadata]: + """Read metadata about a file in the local directory related to a download process. + + Args: + local_dir (`Path`): + Path to the local directory in which files are downloaded. + filename (`str`): + Path of the file in the repo. + + Return: + `[LocalDownloadFileMetadata]` or `None`: the metadata if it exists, `None` otherwise. + """ + paths = get_local_download_paths(local_dir, filename) + with WeakFileLock(paths.lock_path): + if paths.metadata_path.exists(): + try: + with paths.metadata_path.open() as f: + commit_hash = f.readline().strip() + etag = f.readline().strip() + timestamp = float(f.readline().strip()) + metadata = LocalDownloadFileMetadata( + filename=filename, + commit_hash=commit_hash, + etag=etag, + timestamp=timestamp, + ) + except Exception as e: + # remove the metadata file if it is corrupted / not the right format + logger.warning( + f"Invalid metadata file {paths.metadata_path}: {e}. Removing it from disk and continue." + ) + try: + paths.metadata_path.unlink() + except Exception as e: + logger.warning(f"Could not remove corrupted metadata file {paths.metadata_path}: {e}") + + try: + # check if the file exists and hasn't been modified since the metadata was saved + stat = paths.file_path.stat() + if ( + stat.st_mtime - 1 <= metadata.timestamp + ): # allow 1s difference as stat.st_mtime might not be precise + return metadata + logger.info(f"Ignored metadata for '{filename}' (outdated). Will re-compute hash.") + except FileNotFoundError: + # file does not exist => metadata is outdated + return None + return None + + +def read_upload_metadata(local_dir: Path, filename: str) -> LocalUploadFileMetadata: + """Read metadata about a file in the local directory related to an upload process. + + TODO: factorize logic with `read_download_metadata`. + + Args: + local_dir (`Path`): + Path to the local directory in which files are downloaded. + filename (`str`): + Path of the file in the repo. + + Return: + `[LocalUploadFileMetadata]` or `None`: the metadata if it exists, `None` otherwise. + """ + paths = get_local_upload_paths(local_dir, filename) + with WeakFileLock(paths.lock_path): + if paths.metadata_path.exists(): + try: + with paths.metadata_path.open() as f: + timestamp = float(f.readline().strip()) + + size = int(f.readline().strip()) # never None + + _should_ignore = f.readline().strip() + should_ignore = None if _should_ignore == "" else bool(int(_should_ignore)) + + _sha256 = f.readline().strip() + sha256 = None if _sha256 == "" else _sha256 + + _upload_mode = f.readline().strip() + upload_mode = None if _upload_mode == "" else _upload_mode + if upload_mode not in (None, "regular", "lfs"): + raise ValueError(f"Invalid upload mode in metadata {paths.path_in_repo}: {upload_mode}") + + _remote_oid = f.readline().strip() + remote_oid = None if _remote_oid == "" else _remote_oid + + is_uploaded = bool(int(f.readline().strip())) + is_committed = bool(int(f.readline().strip())) + + metadata = LocalUploadFileMetadata( + timestamp=timestamp, + size=size, + should_ignore=should_ignore, + sha256=sha256, + upload_mode=upload_mode, + remote_oid=remote_oid, + is_uploaded=is_uploaded, + is_committed=is_committed, + ) + except Exception as e: + # remove the metadata file if it is corrupted / not the right format + logger.warning( + f"Invalid metadata file {paths.metadata_path}: {e}. Removing it from disk and continue." + ) + try: + paths.metadata_path.unlink() + except Exception as e: + logger.warning(f"Could not remove corrupted metadata file {paths.metadata_path}: {e}") + + # TODO: can we do better? + if ( + metadata.timestamp is not None + and metadata.is_uploaded # file was uploaded + and not metadata.is_committed # but not committed + and time.time() - metadata.timestamp > 20 * 3600 # and it's been more than 20 hours + ): # => we consider it as garbage-collected by S3 + metadata.is_uploaded = False + + # check if the file exists and hasn't been modified since the metadata was saved + try: + if metadata.timestamp is not None and paths.file_path.stat().st_mtime <= metadata.timestamp: + return metadata + logger.info(f"Ignored metadata for '{filename}' (outdated). Will re-compute hash.") + except FileNotFoundError: + # file does not exist => metadata is outdated + pass + + # empty metadata => we don't know anything expect its size + return LocalUploadFileMetadata(size=paths.file_path.stat().st_size) + + +def write_download_metadata(local_dir: Path, filename: str, commit_hash: str, etag: str) -> None: + """Write metadata about a file in the local directory related to a download process. + + Args: + local_dir (`Path`): + Path to the local directory in which files are downloaded. + """ + paths = get_local_download_paths(local_dir, filename) + with WeakFileLock(paths.lock_path): + with paths.metadata_path.open("w") as f: + f.write(f"{commit_hash}\n{etag}\n{time.time()}\n") + + +def _huggingface_dir(local_dir: Path) -> Path: + """Return the path to the `.cache/huggingface` directory in a local directory.""" + # Wrap in lru_cache to avoid overwriting the .gitignore file if called multiple times + path = local_dir / ".cache" / "huggingface" + path.mkdir(exist_ok=True, parents=True) + + # Create a .gitignore file in the .cache/huggingface directory if it doesn't exist + # Should be thread-safe enough like this. + gitignore = path / ".gitignore" + gitignore_lock = path / ".gitignore.lock" + if not gitignore.exists(): + try: + with WeakFileLock(gitignore_lock, timeout=0.1): + gitignore.write_text("*") + except IndexError: + pass + except OSError: # TimeoutError, FileNotFoundError, PermissionError, etc. + pass + try: + gitignore_lock.unlink() + except OSError: + pass + return path + + +def _short_hash(filename: str) -> str: + return base64.urlsafe_b64encode(hashlib.sha1(filename.encode()).digest()).decode() diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/_login.py b/.venv/lib/python3.12/site-packages/huggingface_hub/_login.py new file mode 100644 index 0000000000000000000000000000000000000000..303cd2b35d834cecfaeee646644e49ffcbcd89a2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/_login.py @@ -0,0 +1,520 @@ +# Copyright 2020 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains methods to log in to the Hub.""" + +import os +import subprocess +from getpass import getpass +from pathlib import Path +from typing import Optional + +from . import constants +from .commands._cli_utils import ANSI +from .utils import ( + capture_output, + get_token, + is_google_colab, + is_notebook, + list_credential_helpers, + logging, + run_subprocess, + set_git_credential, + unset_git_credential, +) +from .utils._auth import ( + _get_token_by_name, + _get_token_from_environment, + _get_token_from_file, + _get_token_from_google_colab, + _save_stored_tokens, + _save_token, + get_stored_tokens, +) +from .utils._deprecation import _deprecate_arguments, _deprecate_positional_args + + +logger = logging.get_logger(__name__) + +_HF_LOGO_ASCII = """ + _| _| _| _| _|_|_| _|_|_| _|_|_| _| _| _|_|_| _|_|_|_| _|_| _|_|_| _|_|_|_| + _| _| _| _| _| _| _| _|_| _| _| _| _| _| _| _| + _|_|_|_| _| _| _| _|_| _| _|_| _| _| _| _| _| _|_| _|_|_| _|_|_|_| _| _|_|_| + _| _| _| _| _| _| _| _| _| _| _|_| _| _| _| _| _| _| _| + _| _| _|_| _|_|_| _|_|_| _|_|_| _| _| _|_|_| _| _| _| _|_|_| _|_|_|_| +""" + + +@_deprecate_arguments( + version="1.0", + deprecated_args="write_permission", + custom_message="Fine-grained tokens added complexity to the permissions, making it irrelevant to check if a token has 'write' access.", +) +@_deprecate_positional_args(version="1.0") +def login( + token: Optional[str] = None, + *, + add_to_git_credential: bool = False, + new_session: bool = True, + write_permission: bool = False, +) -> None: + """Login the machine to access the Hub. + + The `token` is persisted in cache and set as a git credential. Once done, the machine + is logged in and the access token will be available across all `huggingface_hub` + components. If `token` is not provided, it will be prompted to the user either with + a widget (in a notebook) or via the terminal. + + To log in from outside of a script, one can also use `hf auth login` which is + a cli command that wraps [`login`]. + + + + [`login`] is a drop-in replacement method for [`notebook_login`] as it wraps and + extends its capabilities. + + + + + + When the token is not passed, [`login`] will automatically detect if the script runs + in a notebook or not. However, this detection might not be accurate due to the + variety of notebooks that exists nowadays. If that is the case, you can always force + the UI by using [`notebook_login`] or [`interpreter_login`]. + + + + Args: + token (`str`, *optional*): + User access token to generate from https://huggingface.co/settings/token. + add_to_git_credential (`bool`, defaults to `False`): + If `True`, token will be set as git credential. If no git credential helper + is configured, a warning will be displayed to the user. If `token` is `None`, + the value of `add_to_git_credential` is ignored and will be prompted again + to the end user. + new_session (`bool`, defaults to `True`): + If `True`, will request a token even if one is already saved on the machine. + write_permission (`bool`): + Ignored and deprecated argument. + Raises: + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If an organization token is passed. Only personal account tokens are valid + to log in. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If token is invalid. + [`ImportError`](https://docs.python.org/3/library/exceptions.html#ImportError) + If running in a notebook but `ipywidgets` is not installed. + """ + if token is not None: + if not add_to_git_credential: + logger.info( + "The token has not been saved to the git credentials helper. Pass " + "`add_to_git_credential=True` in this function directly or " + "`--add-to-git-credential` if using via `hf`CLI if " + "you want to set the git credential as well." + ) + _login(token, add_to_git_credential=add_to_git_credential) + elif is_notebook(): + notebook_login(new_session=new_session) + else: + interpreter_login(new_session=new_session) + + +def logout(token_name: Optional[str] = None) -> None: + """Logout the machine from the Hub. + + Token is deleted from the machine and removed from git credential. + + Args: + token_name (`str`, *optional*): + Name of the access token to logout from. If `None`, will logout from all saved access tokens. + Raises: + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError): + If the access token name is not found. + """ + if get_token() is None and not get_stored_tokens(): # No active token and no saved access tokens + logger.warning("Not logged in!") + return + if not token_name: + # Delete all saved access tokens and token + for file_path in (constants.HF_TOKEN_PATH, constants.HF_STORED_TOKENS_PATH): + try: + Path(file_path).unlink() + except FileNotFoundError: + pass + logger.info("Successfully logged out from all access tokens.") + else: + _logout_from_token(token_name) + logger.info(f"Successfully logged out from access token: {token_name}.") + + unset_git_credential() + + # Check if still logged in + if _get_token_from_google_colab() is not None: + raise EnvironmentError( + "You are automatically logged in using a Google Colab secret.\n" + "To log out, you must unset the `HF_TOKEN` secret in your Colab settings." + ) + if _get_token_from_environment() is not None: + raise EnvironmentError( + "Token has been deleted from your machine but you are still logged in.\n" + "To log out, you must clear out both `HF_TOKEN` and `HUGGING_FACE_HUB_TOKEN` environment variables." + ) + + +def auth_switch(token_name: str, add_to_git_credential: bool = False) -> None: + """Switch to a different access token. + + Args: + token_name (`str`): + Name of the access token to switch to. + add_to_git_credential (`bool`, defaults to `False`): + If `True`, token will be set as git credential. If no git credential helper + is configured, a warning will be displayed to the user. If `token` is `None`, + the value of `add_to_git_credential` is ignored and will be prompted again + to the end user. + + Raises: + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError): + If the access token name is not found. + """ + token = _get_token_by_name(token_name) + if not token: + raise ValueError(f"Access token {token_name} not found in {constants.HF_STORED_TOKENS_PATH}") + # Write token to HF_TOKEN_PATH + _set_active_token(token_name, add_to_git_credential) + logger.info(f"The current active token is: {token_name}") + token_from_environment = _get_token_from_environment() + if token_from_environment is not None and token_from_environment != token: + logger.warning( + "The environment variable `HF_TOKEN` is set and will override the access token you've just switched to." + ) + + +def auth_list() -> None: + """List all stored access tokens.""" + tokens = get_stored_tokens() + + if not tokens: + logger.info("No access tokens found.") + return + # Find current token + current_token = get_token() + current_token_name = None + for token_name in tokens: + if tokens.get(token_name) == current_token: + current_token_name = token_name + # Print header + max_offset = max(len("token"), max(len(token) for token in tokens)) + 2 + print(f" {{:<{max_offset}}}| {{:<15}}".format("name", "token")) + print("-" * (max_offset + 2) + "|" + "-" * 15) + + # Print saved access tokens + for token_name in tokens: + token = tokens.get(token_name, "") + masked_token = f"{token[:3]}****{token[-4:]}" if token != "" else token + is_current = "*" if token == current_token else " " + + print(f"{is_current} {{:<{max_offset}}}| {{:<15}}".format(token_name, masked_token)) + + if _get_token_from_environment(): + logger.warning( + "\nNote: Environment variable `HF_TOKEN` is set and is the current active token independently from the stored tokens listed above." + ) + elif current_token_name is None: + logger.warning( + "\nNote: No active token is set and no environment variable `HF_TOKEN` is found. Use `hf auth login` to log in." + ) + + +### +# Interpreter-based login (text) +### + + +@_deprecate_arguments( + version="1.0", + deprecated_args="write_permission", + custom_message="Fine-grained tokens added complexity to the permissions, making it irrelevant to check if a token has 'write' access.", +) +@_deprecate_positional_args(version="1.0") +def interpreter_login(*, new_session: bool = True, write_permission: bool = False) -> None: + """ + Displays a prompt to log in to the HF website and store the token. + + This is equivalent to [`login`] without passing a token when not run in a notebook. + [`interpreter_login`] is useful if you want to force the use of the terminal prompt + instead of a notebook widget. + + For more details, see [`login`]. + + Args: + new_session (`bool`, defaults to `True`): + If `True`, will request a token even if one is already saved on the machine. + write_permission (`bool`): + Ignored and deprecated argument. + """ + if not new_session and get_token() is not None: + logger.info("User is already logged in.") + return + + from .commands.delete_cache import _ask_for_confirmation_no_tui + + print(_HF_LOGO_ASCII) + if get_token() is not None: + logger.info( + " A token is already saved on your machine. Run `hf auth whoami`" + " to get more information or `hf auth logout` if you want" + " to log out." + ) + logger.info(" Setting a new token will erase the existing one.") + + logger.info( + " To log in, `huggingface_hub` requires a token generated from https://huggingface.co/settings/tokens ." + ) + if os.name == "nt": + logger.info("Token can be pasted using 'Right-Click'.") + token = getpass("Enter your token (input will not be visible): ") + add_to_git_credential = _ask_for_confirmation_no_tui("Add token as git credential?") + + _login(token=token, add_to_git_credential=add_to_git_credential) + + +### +# Notebook-based login (widget) +### + +NOTEBOOK_LOGIN_PASSWORD_HTML = """

Immediately click login after typing your password or +it might be stored in plain text in this notebook file.
""" + + +NOTEBOOK_LOGIN_TOKEN_HTML_START = """

Copy a token from your Hugging Face +tokens page and paste it below.
Immediately click login after copying +your token or it might be stored in plain text in this notebook file.
""" + + +NOTEBOOK_LOGIN_TOKEN_HTML_END = """ +Pro Tip: If you don't already have one, you can create a dedicated +'notebooks' token with 'write' access, that you can then easily reuse for all +notebooks. """ + + +@_deprecate_arguments( + version="1.0", + deprecated_args="write_permission", + custom_message="Fine-grained tokens added complexity to the permissions, making it irrelevant to check if a token has 'write' access.", +) +@_deprecate_positional_args(version="1.0") +def notebook_login(*, new_session: bool = True, write_permission: bool = False) -> None: + """ + Displays a widget to log in to the HF website and store the token. + + This is equivalent to [`login`] without passing a token when run in a notebook. + [`notebook_login`] is useful if you want to force the use of the notebook widget + instead of a prompt in the terminal. + + For more details, see [`login`]. + + Args: + new_session (`bool`, defaults to `True`): + If `True`, will request a token even if one is already saved on the machine. + write_permission (`bool`): + Ignored and deprecated argument. + """ + try: + import ipywidgets.widgets as widgets # type: ignore + from IPython.display import display # type: ignore + except ImportError: + raise ImportError( + "The `notebook_login` function can only be used in a notebook (Jupyter or" + " Colab) and you need the `ipywidgets` module: `pip install ipywidgets`." + ) + if not new_session and get_token() is not None: + logger.info("User is already logged in.") + return + + box_layout = widgets.Layout(display="flex", flex_flow="column", align_items="center", width="50%") + + token_widget = widgets.Password(description="Token:") + git_checkbox_widget = widgets.Checkbox(value=True, description="Add token as git credential?") + token_finish_button = widgets.Button(description="Login") + + login_token_widget = widgets.VBox( + [ + widgets.HTML(NOTEBOOK_LOGIN_TOKEN_HTML_START), + token_widget, + git_checkbox_widget, + token_finish_button, + widgets.HTML(NOTEBOOK_LOGIN_TOKEN_HTML_END), + ], + layout=box_layout, + ) + display(login_token_widget) + + # On click events + def login_token_event(t): + """Event handler for the login button.""" + token = token_widget.value + add_to_git_credential = git_checkbox_widget.value + # Erase token and clear value to make sure it's not saved in the notebook. + token_widget.value = "" + # Hide inputs + login_token_widget.children = [widgets.Label("Connecting...")] + try: + with capture_output() as captured: + _login(token, add_to_git_credential=add_to_git_credential) + message = captured.getvalue() + except Exception as error: + message = str(error) + # Print result (success message or error) + login_token_widget.children = [widgets.Label(line) for line in message.split("\n") if line.strip()] + + token_finish_button.on_click(login_token_event) + + +### +# Login private helpers +### + + +def _login( + token: str, + add_to_git_credential: bool, +) -> None: + from .hf_api import whoami # avoid circular import + + if token.startswith("api_org"): + raise ValueError("You must use your personal account token, not an organization token.") + + token_info = whoami(token) + permission = token_info["auth"]["accessToken"]["role"] + logger.info(f"Token is valid (permission: {permission}).") + + token_name = token_info["auth"]["accessToken"]["displayName"] + # Store token locally + _save_token(token=token, token_name=token_name) + # Set active token + _set_active_token(token_name=token_name, add_to_git_credential=add_to_git_credential) + logger.info("Login successful.") + if _get_token_from_environment(): + logger.warning( + "Note: Environment variable`HF_TOKEN` is set and is the current active token independently from the token you've just configured." + ) + else: + logger.info(f"The current active token is: `{token_name}`") + + +def _logout_from_token(token_name: str) -> None: + """Logout from a specific access token. + + Args: + token_name (`str`): + The name of the access token to logout from. + Raises: + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError): + If the access token name is not found. + """ + stored_tokens = get_stored_tokens() + # If there is no access tokens saved or the access token name is not found, do nothing + if not stored_tokens or token_name not in stored_tokens: + return + + token = stored_tokens.pop(token_name) + _save_stored_tokens(stored_tokens) + + if token == _get_token_from_file(): + logger.warning(f"Active token '{token_name}' has been deleted.") + Path(constants.HF_TOKEN_PATH).unlink(missing_ok=True) + + +def _set_active_token( + token_name: str, + add_to_git_credential: bool, +) -> None: + """Set the active access token. + + Args: + token_name (`str`): + The name of the token to set as active. + """ + token = _get_token_by_name(token_name) + if not token: + raise ValueError(f"Token {token_name} not found in {constants.HF_STORED_TOKENS_PATH}") + if add_to_git_credential: + if _is_git_credential_helper_configured(): + set_git_credential(token) + logger.info( + "Your token has been saved in your configured git credential helpers" + + f" ({','.join(list_credential_helpers())})." + ) + else: + logger.warning("Token has not been saved to git credential helper.") + # Write token to HF_TOKEN_PATH + path = Path(constants.HF_TOKEN_PATH) + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(token) + logger.info(f"Your token has been saved to {constants.HF_TOKEN_PATH}") + + +def _is_git_credential_helper_configured() -> bool: + """Check if a git credential helper is configured. + + Warns user if not the case (except for Google Colab where "store" is set by default + by `huggingface_hub`). + """ + helpers = list_credential_helpers() + if len(helpers) > 0: + return True # Do not warn: at least 1 helper is set + + # Only in Google Colab to avoid the warning message + # See https://github.com/huggingface/huggingface_hub/issues/1043#issuecomment-1247010710 + if is_google_colab(): + _set_store_as_git_credential_helper_globally() + return True # Do not warn: "store" is used by default in Google Colab + + # Otherwise, warn user + print( + ANSI.red( + "Cannot authenticate through git-credential as no helper is defined on your" + " machine.\nYou might have to re-authenticate when pushing to the Hugging" + " Face Hub.\nRun the following command in your terminal in case you want to" + " set the 'store' credential helper as default.\n\ngit config --global" + " credential.helper store\n\nRead" + " https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage for more" + " details." + ) + ) + return False + + +def _set_store_as_git_credential_helper_globally() -> None: + """Set globally the credential.helper to `store`. + + To be used only in Google Colab as we assume the user doesn't care about the git + credential config. It is the only particular case where we don't want to display the + warning message in [`notebook_login()`]. + + Related: + - https://github.com/huggingface/huggingface_hub/issues/1043 + - https://github.com/huggingface/huggingface_hub/issues/1051 + - https://git-scm.com/docs/git-credential-store + """ + try: + run_subprocess("git config --global credential.helper store") + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/_oauth.py b/.venv/lib/python3.12/site-packages/huggingface_hub/_oauth.py new file mode 100644 index 0000000000000000000000000000000000000000..9f8eb607962bc18fec348fed18ce269524983e23 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/_oauth.py @@ -0,0 +1,460 @@ +import datetime +import hashlib +import logging +import os +import time +import urllib.parse +import warnings +from dataclasses import dataclass +from typing import TYPE_CHECKING, Dict, List, Literal, Optional, Tuple, Union + +from . import constants +from .hf_api import whoami +from .utils import experimental, get_token + + +logger = logging.getLogger(__name__) + +if TYPE_CHECKING: + import fastapi + + +@dataclass +class OAuthOrgInfo: + """ + Information about an organization linked to a user logged in with OAuth. + + Attributes: + sub (`str`): + Unique identifier for the org. OpenID Connect field. + name (`str`): + The org's full name. OpenID Connect field. + preferred_username (`str`): + The org's username. OpenID Connect field. + picture (`str`): + The org's profile picture URL. OpenID Connect field. + is_enterprise (`bool`): + Whether the org is an enterprise org. Hugging Face field. + can_pay (`Optional[bool]`, *optional*): + Whether the org has a payment method set up. Hugging Face field. + role_in_org (`Optional[str]`, *optional*): + The user's role in the org. Hugging Face field. + security_restrictions (`Optional[List[Literal["ip", "token-policy", "mfa", "sso"]]]`, *optional*): + Array of security restrictions that the user hasn't completed for this org. Possible values: "ip", "token-policy", "mfa", "sso". Hugging Face field. + """ + + sub: str + name: str + preferred_username: str + picture: str + is_enterprise: bool + can_pay: Optional[bool] = None + role_in_org: Optional[str] = None + security_restrictions: Optional[List[Literal["ip", "token-policy", "mfa", "sso"]]] = None + + +@dataclass +class OAuthUserInfo: + """ + Information about a user logged in with OAuth. + + Attributes: + sub (`str`): + Unique identifier for the user, even in case of rename. OpenID Connect field. + name (`str`): + The user's full name. OpenID Connect field. + preferred_username (`str`): + The user's username. OpenID Connect field. + email_verified (`Optional[bool]`, *optional*): + Indicates if the user's email is verified. OpenID Connect field. + email (`Optional[str]`, *optional*): + The user's email address. OpenID Connect field. + picture (`str`): + The user's profile picture URL. OpenID Connect field. + profile (`str`): + The user's profile URL. OpenID Connect field. + website (`Optional[str]`, *optional*): + The user's website URL. OpenID Connect field. + is_pro (`bool`): + Whether the user is a pro user. Hugging Face field. + can_pay (`Optional[bool]`, *optional*): + Whether the user has a payment method set up. Hugging Face field. + orgs (`Optional[List[OrgInfo]]`, *optional*): + List of organizations the user is part of. Hugging Face field. + """ + + sub: str + name: str + preferred_username: str + email_verified: Optional[bool] + email: Optional[str] + picture: str + profile: str + website: Optional[str] + is_pro: bool + can_pay: Optional[bool] + orgs: Optional[List[OAuthOrgInfo]] + + +@dataclass +class OAuthInfo: + """ + Information about the OAuth login. + + Attributes: + access_token (`str`): + The access token. + access_token_expires_at (`datetime.datetime`): + The expiration date of the access token. + user_info ([`OAuthUserInfo`]): + The user information. + state (`str`, *optional*): + State passed to the OAuth provider in the original request to the OAuth provider. + scope (`str`): + Granted scope. + """ + + access_token: str + access_token_expires_at: datetime.datetime + user_info: OAuthUserInfo + state: Optional[str] + scope: str + + +@experimental +def attach_huggingface_oauth(app: "fastapi.FastAPI", route_prefix: str = "/"): + """ + Add OAuth endpoints to a FastAPI app to enable OAuth login with Hugging Face. + + How to use: + - Call this method on your FastAPI app to add the OAuth endpoints. + - Inside your route handlers, call `parse_huggingface_oauth(request)` to retrieve the OAuth info. + - If user is logged in, an [`OAuthInfo`] object is returned with the user's info. If not, `None` is returned. + - In your app, make sure to add links to `/oauth/huggingface/login` and `/oauth/huggingface/logout` for the user to log in and out. + + Example: + ```py + from huggingface_hub import attach_huggingface_oauth, parse_huggingface_oauth + + # Create a FastAPI app + app = FastAPI() + + # Add OAuth endpoints to the FastAPI app + attach_huggingface_oauth(app) + + # Add a route that greets the user if they are logged in + @app.get("/") + def greet_json(request: Request): + # Retrieve the OAuth info from the request + oauth_info = parse_huggingface_oauth(request) # e.g. OAuthInfo dataclass + if oauth_info is None: + return {"msg": "Not logged in!"} + return {"msg": f"Hello, {oauth_info.user_info.preferred_username}!"} + ``` + """ + # TODO: handle generic case (handling OAuth in a non-Space environment with custom dev values) (low priority) + + # Add SessionMiddleware to the FastAPI app to store the OAuth info in the session. + # Session Middleware requires a secret key to sign the cookies. Let's use a hash + # of the OAuth secret key to make it unique to the Space + updated in case OAuth + # config gets updated. When ran locally, we use an empty string as a secret key. + try: + from starlette.middleware.sessions import SessionMiddleware + except ImportError as e: + raise ImportError( + "Cannot initialize OAuth to due a missing library. Please run `pip install huggingface_hub[oauth]` or add " + "`huggingface_hub[oauth]` to your requirements.txt file in order to install the required dependencies." + ) from e + session_secret = (constants.OAUTH_CLIENT_SECRET or "") + "-v1" + app.add_middleware( + SessionMiddleware, # type: ignore[arg-type] + secret_key=hashlib.sha256(session_secret.encode()).hexdigest(), + same_site="none", + https_only=True, + ) # type: ignore + + # Add OAuth endpoints to the FastAPI app: + # - {route_prefix}/oauth/huggingface/login + # - {route_prefix}/oauth/huggingface/callback + # - {route_prefix}/oauth/huggingface/logout + # If the app is running in a Space, OAuth is enabled normally. + # Otherwise, we mock the endpoints to make the user log in with a fake user profile - without any calls to hf.co. + route_prefix = route_prefix.strip("/") + if os.getenv("SPACE_ID") is not None: + logger.info("OAuth is enabled in the Space. Adding OAuth routes.") + _add_oauth_routes(app, route_prefix=route_prefix) + else: + logger.info("App is not running in a Space. Adding mocked OAuth routes.") + _add_mocked_oauth_routes(app, route_prefix=route_prefix) + + +def parse_huggingface_oauth(request: "fastapi.Request") -> Optional[OAuthInfo]: + """ + Returns the information from a logged in user as a [`OAuthInfo`] object. + + For flexibility and future-proofing, this method is very lax in its parsing and does not raise errors. + Missing fields are set to `None` without a warning. + + Return `None`, if the user is not logged in (no info in session cookie). + + See [`attach_huggingface_oauth`] for an example on how to use this method. + """ + if "oauth_info" not in request.session: + logger.debug("No OAuth info in session.") + return None + + logger.debug("Parsing OAuth info from session.") + oauth_data = request.session["oauth_info"] + user_data = oauth_data.get("userinfo", {}) + orgs_data = user_data.get("orgs", []) + + orgs = ( + [ + OAuthOrgInfo( + sub=org.get("sub"), + name=org.get("name"), + preferred_username=org.get("preferred_username"), + picture=org.get("picture"), + is_enterprise=org.get("isEnterprise"), + can_pay=org.get("canPay"), + role_in_org=org.get("roleInOrg"), + security_restrictions=org.get("securityRestrictions"), + ) + for org in orgs_data + ] + if orgs_data + else None + ) + + user_info = OAuthUserInfo( + sub=user_data.get("sub"), + name=user_data.get("name"), + preferred_username=user_data.get("preferred_username"), + email_verified=user_data.get("email_verified"), + email=user_data.get("email"), + picture=user_data.get("picture"), + profile=user_data.get("profile"), + website=user_data.get("website"), + is_pro=user_data.get("isPro"), + can_pay=user_data.get("canPay"), + orgs=orgs, + ) + + return OAuthInfo( + access_token=oauth_data.get("access_token"), + access_token_expires_at=datetime.datetime.fromtimestamp(oauth_data.get("expires_at")), + user_info=user_info, + state=oauth_data.get("state"), + scope=oauth_data.get("scope"), + ) + + +def _add_oauth_routes(app: "fastapi.FastAPI", route_prefix: str) -> None: + """Add OAuth routes to the FastAPI app (login, callback handler and logout).""" + try: + import fastapi + from authlib.integrations.base_client.errors import MismatchingStateError + from authlib.integrations.starlette_client import OAuth + from fastapi.responses import RedirectResponse + except ImportError as e: + raise ImportError( + "Cannot initialize OAuth to due a missing library. Please run `pip install huggingface_hub[oauth]` or add " + "`huggingface_hub[oauth]` to your requirements.txt file." + ) from e + + # Check environment variables + msg = ( + "OAuth is required but '{}' environment variable is not set. Make sure you've enabled OAuth in your Space by" + " setting `hf_oauth: true` in the Space metadata." + ) + if constants.OAUTH_CLIENT_ID is None: + raise ValueError(msg.format("OAUTH_CLIENT_ID")) + if constants.OAUTH_CLIENT_SECRET is None: + raise ValueError(msg.format("OAUTH_CLIENT_SECRET")) + if constants.OAUTH_SCOPES is None: + raise ValueError(msg.format("OAUTH_SCOPES")) + if constants.OPENID_PROVIDER_URL is None: + raise ValueError(msg.format("OPENID_PROVIDER_URL")) + + # Register OAuth server + oauth = OAuth() + oauth.register( + name="huggingface", + client_id=constants.OAUTH_CLIENT_ID, + client_secret=constants.OAUTH_CLIENT_SECRET, + client_kwargs={"scope": constants.OAUTH_SCOPES}, + server_metadata_url=constants.OPENID_PROVIDER_URL + "/.well-known/openid-configuration", + ) + + login_uri, callback_uri, logout_uri = _get_oauth_uris(route_prefix) + + # Register OAuth endpoints + @app.get(login_uri) + async def oauth_login(request: fastapi.Request) -> RedirectResponse: + """Endpoint that redirects to HF OAuth page.""" + redirect_uri = _generate_redirect_uri(request) + return await oauth.huggingface.authorize_redirect(request, redirect_uri) # type: ignore + + @app.get(callback_uri) + async def oauth_redirect_callback(request: fastapi.Request) -> RedirectResponse: + """Endpoint that handles the OAuth callback.""" + try: + oauth_info = await oauth.huggingface.authorize_access_token(request) # type: ignore + except MismatchingStateError: + # Parse query params + nb_redirects = int(request.query_params.get("_nb_redirects", 0)) + target_url = request.query_params.get("_target_url") + + # Build redirect URI with the same query params as before and bump nb_redirects count + query_params: Dict[str, Union[int, str]] = {"_nb_redirects": nb_redirects + 1} + if target_url: + query_params["_target_url"] = target_url + + redirect_uri = f"{login_uri}?{urllib.parse.urlencode(query_params)}" + + # If the user is redirected more than 3 times, it is very likely that the cookie is not working properly. + # (e.g. browser is blocking third-party cookies in iframe). In this case, redirect the user in the + # non-iframe view. + if nb_redirects > constants.OAUTH_MAX_REDIRECTS: + host = os.environ.get("SPACE_HOST") + if host is None: # cannot happen in a Space + raise RuntimeError( + "App is not running in a Space (SPACE_HOST environment variable is not set). Cannot redirect to non-iframe view." + ) from None + host_url = "https://" + host.rstrip("/") + return RedirectResponse(host_url + redirect_uri) + + # Redirect the user to the login page again + return RedirectResponse(redirect_uri) + + # OAuth login worked => store the user info in the session and redirect + logger.debug("Successfully logged in with OAuth. Storing user info in session.") + request.session["oauth_info"] = oauth_info + return RedirectResponse(_get_redirect_target(request)) + + @app.get(logout_uri) + async def oauth_logout(request: fastapi.Request) -> RedirectResponse: + """Endpoint that logs out the user (e.g. delete info from cookie session).""" + logger.debug("Logged out with OAuth. Removing user info from session.") + request.session.pop("oauth_info", None) + return RedirectResponse(_get_redirect_target(request)) + + +def _add_mocked_oauth_routes(app: "fastapi.FastAPI", route_prefix: str = "/") -> None: + """Add fake oauth routes if app is run locally and OAuth is enabled. + + Using OAuth will have the same behavior as in a Space but instead of authenticating with HF, a mocked user profile + is added to the session. + """ + try: + import fastapi + from fastapi.responses import RedirectResponse + from starlette.datastructures import URL + except ImportError as e: + raise ImportError( + "Cannot initialize OAuth to due a missing library. Please run `pip install huggingface_hub[oauth]` or add " + "`huggingface_hub[oauth]` to your requirements.txt file." + ) from e + + warnings.warn( + "OAuth is not supported outside of a Space environment. To help you debug your app locally, the oauth endpoints" + " are mocked to return your profile and token. To make it work, your machine must be logged in to Huggingface." + ) + mocked_oauth_info = _get_mocked_oauth_info() + + login_uri, callback_uri, logout_uri = _get_oauth_uris(route_prefix) + + # Define OAuth routes + @app.get(login_uri) + async def oauth_login(request: fastapi.Request) -> RedirectResponse: + """Fake endpoint that redirects to HF OAuth page.""" + # Define target (where to redirect after login) + redirect_uri = _generate_redirect_uri(request) + return RedirectResponse(callback_uri + "?" + urllib.parse.urlencode({"_target_url": redirect_uri})) + + @app.get(callback_uri) + async def oauth_redirect_callback(request: fastapi.Request) -> RedirectResponse: + """Endpoint that handles the OAuth callback.""" + request.session["oauth_info"] = mocked_oauth_info + return RedirectResponse(_get_redirect_target(request)) + + @app.get(logout_uri) + async def oauth_logout(request: fastapi.Request) -> RedirectResponse: + """Endpoint that logs out the user (e.g. delete cookie session).""" + request.session.pop("oauth_info", None) + logout_url = URL("/").include_query_params(**request.query_params) + return RedirectResponse(url=logout_url, status_code=302) # see https://github.com/gradio-app/gradio/pull/9659 + + +def _generate_redirect_uri(request: "fastapi.Request") -> str: + if "_target_url" in request.query_params: + # if `_target_url` already in query params => respect it + target = request.query_params["_target_url"] + else: + # otherwise => keep query params + target = "/?" + urllib.parse.urlencode(request.query_params) + + redirect_uri = request.url_for("oauth_redirect_callback").include_query_params(_target_url=target) + redirect_uri_as_str = str(redirect_uri) + if redirect_uri.netloc.endswith(".hf.space"): + # In Space, FastAPI redirect as http but we want https + redirect_uri_as_str = redirect_uri_as_str.replace("http://", "https://") + return redirect_uri_as_str + + +def _get_redirect_target(request: "fastapi.Request", default_target: str = "/") -> str: + return request.query_params.get("_target_url", default_target) + + +def _get_mocked_oauth_info() -> Dict: + token = get_token() + if token is None: + raise ValueError( + "Your machine must be logged in to HF to debug an OAuth app locally. Please" + " run `hf auth login` or set `HF_TOKEN` as environment variable " + "with one of your access token. You can generate a new token in your " + "settings page (https://huggingface.co/settings/tokens)." + ) + + user = whoami() + if user["type"] != "user": + raise ValueError( + "Your machine is not logged in with a personal account. Please use a " + "personal access token. You can generate a new token in your settings page" + " (https://huggingface.co/settings/tokens)." + ) + + return { + "access_token": token, + "token_type": "bearer", + "expires_in": 8 * 60 * 60, # 8 hours + "id_token": "FOOBAR", + "scope": "openid profile", + "refresh_token": "hf_oauth__refresh_token", + "expires_at": int(time.time()) + 8 * 60 * 60, # 8 hours + "userinfo": { + "sub": "0123456789", + "name": user["fullname"], + "preferred_username": user["name"], + "profile": f"https://huggingface.co/{user['name']}", + "picture": user["avatarUrl"], + "website": "", + "aud": "00000000-0000-0000-0000-000000000000", + "auth_time": 1691672844, + "nonce": "aaaaaaaaaaaaaaaaaaa", + "iat": 1691672844, + "exp": 1691676444, + "iss": "https://huggingface.co", + }, + } + + +def _get_oauth_uris(route_prefix: str = "/") -> Tuple[str, str, str]: + route_prefix = route_prefix.strip("/") + if route_prefix: + route_prefix = f"/{route_prefix}" + return ( + f"{route_prefix}/oauth/huggingface/login", + f"{route_prefix}/oauth/huggingface/callback", + f"{route_prefix}/oauth/huggingface/logout", + ) diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/_snapshot_download.py b/.venv/lib/python3.12/site-packages/huggingface_hub/_snapshot_download.py new file mode 100644 index 0000000000000000000000000000000000000000..0db8a29f7e65a4841590d033f6b7b51d46647bf0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/_snapshot_download.py @@ -0,0 +1,343 @@ +import os +from pathlib import Path +from typing import Dict, Iterable, List, Literal, Optional, Type, Union + +import requests +from tqdm.auto import tqdm as base_tqdm +from tqdm.contrib.concurrent import thread_map + +from . import constants +from .errors import ( + GatedRepoError, + HfHubHTTPError, + LocalEntryNotFoundError, + RepositoryNotFoundError, + RevisionNotFoundError, +) +from .file_download import REGEX_COMMIT_HASH, hf_hub_download, repo_folder_name +from .hf_api import DatasetInfo, HfApi, ModelInfo, RepoFile, SpaceInfo +from .utils import OfflineModeIsEnabled, filter_repo_objects, logging, validate_hf_hub_args +from .utils import tqdm as hf_tqdm + + +logger = logging.get_logger(__name__) + +VERY_LARGE_REPO_THRESHOLD = 50000 # After this limit, we don't consider `repo_info.siblings` to be reliable enough + + +@validate_hf_hub_args +def snapshot_download( + repo_id: str, + *, + repo_type: Optional[str] = None, + revision: Optional[str] = None, + cache_dir: Union[str, Path, None] = None, + local_dir: Union[str, Path, None] = None, + library_name: Optional[str] = None, + library_version: Optional[str] = None, + user_agent: Optional[Union[Dict, str]] = None, + proxies: Optional[Dict] = None, + etag_timeout: float = constants.DEFAULT_ETAG_TIMEOUT, + force_download: bool = False, + token: Optional[Union[bool, str]] = None, + local_files_only: bool = False, + allow_patterns: Optional[Union[List[str], str]] = None, + ignore_patterns: Optional[Union[List[str], str]] = None, + max_workers: int = 8, + tqdm_class: Optional[Type[base_tqdm]] = None, + headers: Optional[Dict[str, str]] = None, + endpoint: Optional[str] = None, + # Deprecated args + local_dir_use_symlinks: Union[bool, Literal["auto"]] = "auto", + resume_download: Optional[bool] = None, +) -> str: + """Download repo files. + + Download a whole snapshot of a repo's files at the specified revision. This is useful when you want all files from + a repo, because you don't know which ones you will need a priori. All files are nested inside a folder in order + to keep their actual filename relative to that folder. You can also filter which files to download using + `allow_patterns` and `ignore_patterns`. + + If `local_dir` is provided, the file structure from the repo will be replicated in this location. When using this + option, the `cache_dir` will not be used and a `.cache/huggingface/` folder will be created at the root of `local_dir` + to store some metadata related to the downloaded files. While this mechanism is not as robust as the main + cache-system, it's optimized for regularly pulling the latest version of a repository. + + An alternative would be to clone the repo but this requires git and git-lfs to be installed and properly + configured. It is also not possible to filter which files to download when cloning a repository using git. + + Args: + repo_id (`str`): + A user or an organization name and a repo name separated by a `/`. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if downloading from a dataset or space, + `None` or `"model"` if downloading from a model. Default is `None`. + revision (`str`, *optional*): + An optional Git revision id which can be a branch name, a tag, or a + commit hash. + cache_dir (`str`, `Path`, *optional*): + Path to the folder where cached files are stored. + local_dir (`str` or `Path`, *optional*): + If provided, the downloaded files will be placed under this directory. + library_name (`str`, *optional*): + The name of the library to which the object corresponds. + library_version (`str`, *optional*): + The version of the library. + user_agent (`str`, `dict`, *optional*): + The user-agent info in the form of a dictionary or a string. + proxies (`dict`, *optional*): + Dictionary mapping protocol to the URL of the proxy passed to + `requests.request`. + etag_timeout (`float`, *optional*, defaults to `10`): + When fetching ETag, how many seconds to wait for the server to send + data before giving up which is passed to `requests.request`. + force_download (`bool`, *optional*, defaults to `False`): + Whether the file should be downloaded even if it already exists in the local cache. + token (`str`, `bool`, *optional*): + A token to be used for the download. + - If `True`, the token is read from the HuggingFace config + folder. + - If a string, it's used as the authentication token. + headers (`dict`, *optional*): + Additional headers to include in the request. Those headers take precedence over the others. + local_files_only (`bool`, *optional*, defaults to `False`): + If `True`, avoid downloading the file and return the path to the + local cached file if it exists. + allow_patterns (`List[str]` or `str`, *optional*): + If provided, only files matching at least one pattern are downloaded. + ignore_patterns (`List[str]` or `str`, *optional*): + If provided, files matching any of the patterns are not downloaded. + max_workers (`int`, *optional*): + Number of concurrent threads to download files (1 thread = 1 file download). + Defaults to 8. + tqdm_class (`tqdm`, *optional*): + If provided, overwrites the default behavior for the progress bar. Passed + argument must inherit from `tqdm.auto.tqdm` or at least mimic its behavior. + Note that the `tqdm_class` is not passed to each individual download. + Defaults to the custom HF progress bar that can be disabled by setting + `HF_HUB_DISABLE_PROGRESS_BARS` environment variable. + + Returns: + `str`: folder path of the repo snapshot. + + Raises: + [`~utils.RepositoryNotFoundError`] + If the repository to download from cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + [`~utils.RevisionNotFoundError`] + If the revision to download from cannot be found. + [`EnvironmentError`](https://docs.python.org/3/library/exceptions.html#EnvironmentError) + If `token=True` and the token cannot be found. + [`OSError`](https://docs.python.org/3/library/exceptions.html#OSError) if + ETag cannot be determined. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + if some parameter value is invalid. + """ + if cache_dir is None: + cache_dir = constants.HF_HUB_CACHE + if revision is None: + revision = constants.DEFAULT_REVISION + if isinstance(cache_dir, Path): + cache_dir = str(cache_dir) + + if repo_type is None: + repo_type = "model" + if repo_type not in constants.REPO_TYPES: + raise ValueError(f"Invalid repo type: {repo_type}. Accepted repo types are: {str(constants.REPO_TYPES)}") + + storage_folder = os.path.join(cache_dir, repo_folder_name(repo_id=repo_id, repo_type=repo_type)) + + api = HfApi( + library_name=library_name, + library_version=library_version, + user_agent=user_agent, + endpoint=endpoint, + headers=headers, + token=token, + ) + + repo_info: Union[ModelInfo, DatasetInfo, SpaceInfo, None] = None + api_call_error: Optional[Exception] = None + if not local_files_only: + # try/except logic to handle different errors => taken from `hf_hub_download` + try: + # if we have internet connection we want to list files to download + repo_info = api.repo_info(repo_id=repo_id, repo_type=repo_type, revision=revision) + except (requests.exceptions.SSLError, requests.exceptions.ProxyError): + # Actually raise for those subclasses of ConnectionError + raise + except ( + requests.exceptions.ConnectionError, + requests.exceptions.Timeout, + OfflineModeIsEnabled, + ) as error: + # Internet connection is down + # => will try to use local files only + api_call_error = error + pass + except RevisionNotFoundError: + # The repo was found but the revision doesn't exist on the Hub (never existed or got deleted) + raise + except requests.HTTPError as error: + # Multiple reasons for an http error: + # - Repository is private and invalid/missing token sent + # - Repository is gated and invalid/missing token sent + # - Hub is down (error 500 or 504) + # => let's switch to 'local_files_only=True' to check if the files are already cached. + # (if it's not the case, the error will be re-raised) + api_call_error = error + pass + + # At this stage, if `repo_info` is None it means either: + # - internet connection is down + # - internet connection is deactivated (local_files_only=True or HF_HUB_OFFLINE=True) + # - repo is private/gated and invalid/missing token sent + # - Hub is down + # => let's look if we can find the appropriate folder in the cache: + # - if the specified revision is a commit hash, look inside "snapshots". + # - f the specified revision is a branch or tag, look inside "refs". + # => if local_dir is not None, we will return the path to the local folder if it exists. + if repo_info is None: + # Try to get which commit hash corresponds to the specified revision + commit_hash = None + if REGEX_COMMIT_HASH.match(revision): + commit_hash = revision + else: + ref_path = os.path.join(storage_folder, "refs", revision) + if os.path.exists(ref_path): + # retrieve commit_hash from refs file + with open(ref_path) as f: + commit_hash = f.read() + + # Try to locate snapshot folder for this commit hash + if commit_hash is not None and local_dir is None: + snapshot_folder = os.path.join(storage_folder, "snapshots", commit_hash) + if os.path.exists(snapshot_folder): + # Snapshot folder exists => let's return it + # (but we can't check if all the files are actually there) + return snapshot_folder + + # If local_dir is not None, return it if it exists and is not empty + if local_dir is not None: + local_dir = Path(local_dir) + if local_dir.is_dir() and any(local_dir.iterdir()): + logger.warning( + f"Returning existing local_dir `{local_dir}` as remote repo cannot be accessed in `snapshot_download` ({api_call_error})." + ) + return str(local_dir.resolve()) + # If we couldn't find the appropriate folder on disk, raise an error. + if local_files_only: + raise LocalEntryNotFoundError( + "Cannot find an appropriate cached snapshot folder for the specified revision on the local disk and " + "outgoing traffic has been disabled. To enable repo look-ups and downloads online, pass " + "'local_files_only=False' as input." + ) + elif isinstance(api_call_error, OfflineModeIsEnabled): + raise LocalEntryNotFoundError( + "Cannot find an appropriate cached snapshot folder for the specified revision on the local disk and " + "outgoing traffic has been disabled. To enable repo look-ups and downloads online, set " + "'HF_HUB_OFFLINE=0' as environment variable." + ) from api_call_error + elif isinstance(api_call_error, (RepositoryNotFoundError, GatedRepoError)) or ( + isinstance(api_call_error, HfHubHTTPError) and api_call_error.response.status_code == 401 + ): + # Repo not found, gated, or specific authentication error => let's raise the actual error + raise api_call_error + else: + # Otherwise: most likely a connection issue or Hub downtime => let's warn the user + raise LocalEntryNotFoundError( + "An error happened while trying to locate the files on the Hub and we cannot find the appropriate" + " snapshot folder for the specified revision on the local disk. Please check your internet connection" + " and try again." + ) from api_call_error + + # At this stage, internet connection is up and running + # => let's download the files! + assert repo_info.sha is not None, "Repo info returned from server must have a revision sha." + + # Corner case: on very large repos, the siblings list in `repo_info` might not contain all files. + # In that case, we need to use the `list_repo_tree` method to prevent caching issues. + repo_files: Iterable[str] = [f.rfilename for f in repo_info.siblings] if repo_info.siblings is not None else [] + unreliable_nb_files = ( + repo_info.siblings is None + or len(repo_info.siblings) == 0 + or len(repo_info.siblings) > VERY_LARGE_REPO_THRESHOLD + ) + if unreliable_nb_files: + logger.info( + "Number of files in the repo is unreliable. Using `list_repo_tree` to ensure all files are listed." + ) + repo_files = ( + f.rfilename + for f in api.list_repo_tree(repo_id=repo_id, recursive=True, revision=revision, repo_type=repo_type) + if isinstance(f, RepoFile) + ) + + filtered_repo_files: Iterable[str] = filter_repo_objects( + items=repo_files, + allow_patterns=allow_patterns, + ignore_patterns=ignore_patterns, + ) + + if not unreliable_nb_files: + filtered_repo_files = list(filtered_repo_files) + tqdm_desc = f"Fetching {len(filtered_repo_files)} files" + else: + tqdm_desc = "Fetching ... files" + + commit_hash = repo_info.sha + snapshot_folder = os.path.join(storage_folder, "snapshots", commit_hash) + # if passed revision is not identical to commit_hash + # then revision has to be a branch name or tag name. + # In that case store a ref. + if revision != commit_hash: + ref_path = os.path.join(storage_folder, "refs", revision) + try: + os.makedirs(os.path.dirname(ref_path), exist_ok=True) + with open(ref_path, "w") as f: + f.write(commit_hash) + except OSError as e: + logger.warning(f"Ignored error while writing commit hash to {ref_path}: {e}.") + + # we pass the commit_hash to hf_hub_download + # so no network call happens if we already + # have the file locally. + def _inner_hf_hub_download(repo_file: str): + return hf_hub_download( + repo_id, + filename=repo_file, + repo_type=repo_type, + revision=commit_hash, + endpoint=endpoint, + cache_dir=cache_dir, + local_dir=local_dir, + local_dir_use_symlinks=local_dir_use_symlinks, + library_name=library_name, + library_version=library_version, + user_agent=user_agent, + proxies=proxies, + etag_timeout=etag_timeout, + resume_download=resume_download, + force_download=force_download, + token=token, + headers=headers, + ) + + if constants.HF_HUB_ENABLE_HF_TRANSFER: + # when using hf_transfer we don't want extra parallelism + # from the one hf_transfer provides + for file in filtered_repo_files: + _inner_hf_hub_download(file) + else: + thread_map( + _inner_hf_hub_download, + filtered_repo_files, + desc=tqdm_desc, + max_workers=max_workers, + # User can use its own tqdm class or the default one from `huggingface_hub.utils` + tqdm_class=tqdm_class or hf_tqdm, + ) + + if local_dir is not None: + return str(os.path.realpath(local_dir)) + return snapshot_folder diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/_space_api.py b/.venv/lib/python3.12/site-packages/huggingface_hub/_space_api.py new file mode 100644 index 0000000000000000000000000000000000000000..05fccfbc1ebdfc14840a88751914b8fc0d1a498d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/_space_api.py @@ -0,0 +1,168 @@ +# coding=utf-8 +# Copyright 2019-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from dataclasses import dataclass +from datetime import datetime +from enum import Enum +from typing import Dict, Optional + +from huggingface_hub.utils import parse_datetime + + +class SpaceStage(str, Enum): + """ + Enumeration of possible stage of a Space on the Hub. + + Value can be compared to a string: + ```py + assert SpaceStage.BUILDING == "BUILDING" + ``` + + Taken from https://github.com/huggingface/moon-landing/blob/main/server/repo_types/SpaceInfo.ts#L61 (private url). + """ + + # Copied from moon-landing > server > repo_types > SpaceInfo.ts (private repo) + NO_APP_FILE = "NO_APP_FILE" + CONFIG_ERROR = "CONFIG_ERROR" + BUILDING = "BUILDING" + BUILD_ERROR = "BUILD_ERROR" + RUNNING = "RUNNING" + RUNNING_BUILDING = "RUNNING_BUILDING" + RUNTIME_ERROR = "RUNTIME_ERROR" + DELETING = "DELETING" + STOPPED = "STOPPED" + PAUSED = "PAUSED" + + +class SpaceHardware(str, Enum): + """ + Enumeration of hardwares available to run your Space on the Hub. + + Value can be compared to a string: + ```py + assert SpaceHardware.CPU_BASIC == "cpu-basic" + ``` + + Taken from https://github.com/huggingface-internal/moon-landing/blob/main/server/repo_types/SpaceHardwareFlavor.ts (private url). + """ + + # CPU + CPU_BASIC = "cpu-basic" + CPU_UPGRADE = "cpu-upgrade" + CPU_XL = "cpu-xl" + + # ZeroGPU + ZERO_A10G = "zero-a10g" + + # GPU + T4_SMALL = "t4-small" + T4_MEDIUM = "t4-medium" + L4X1 = "l4x1" + L4X4 = "l4x4" + L40SX1 = "l40sx1" + L40SX4 = "l40sx4" + L40SX8 = "l40sx8" + A10G_SMALL = "a10g-small" + A10G_LARGE = "a10g-large" + A10G_LARGEX2 = "a10g-largex2" + A10G_LARGEX4 = "a10g-largex4" + A100_LARGE = "a100-large" + H100 = "h100" + H100X8 = "h100x8" + + +class SpaceStorage(str, Enum): + """ + Enumeration of persistent storage available for your Space on the Hub. + + Value can be compared to a string: + ```py + assert SpaceStorage.SMALL == "small" + ``` + + Taken from https://github.com/huggingface/moon-landing/blob/main/server/repo_types/SpaceHardwareFlavor.ts#L24 (private url). + """ + + SMALL = "small" + MEDIUM = "medium" + LARGE = "large" + + +@dataclass +class SpaceRuntime: + """ + Contains information about the current runtime of a Space. + + Args: + stage (`str`): + Current stage of the space. Example: RUNNING. + hardware (`str` or `None`): + Current hardware of the space. Example: "cpu-basic". Can be `None` if Space + is `BUILDING` for the first time. + requested_hardware (`str` or `None`): + Requested hardware. Can be different than `hardware` especially if the request + has just been made. Example: "t4-medium". Can be `None` if no hardware has + been requested yet. + sleep_time (`int` or `None`): + Number of seconds the Space will be kept alive after the last request. By default (if value is `None`), the + Space will never go to sleep if it's running on an upgraded hardware, while it will go to sleep after 48 + hours on a free 'cpu-basic' hardware. For more details, see https://huggingface.co/docs/hub/spaces-gpus#sleep-time. + raw (`dict`): + Raw response from the server. Contains more information about the Space + runtime like number of replicas, number of cpu, memory size,... + """ + + stage: SpaceStage + hardware: Optional[SpaceHardware] + requested_hardware: Optional[SpaceHardware] + sleep_time: Optional[int] + storage: Optional[SpaceStorage] + raw: Dict + + def __init__(self, data: Dict) -> None: + self.stage = data["stage"] + self.hardware = data.get("hardware", {}).get("current") + self.requested_hardware = data.get("hardware", {}).get("requested") + self.sleep_time = data.get("gcTimeout") + self.storage = data.get("storage") + self.raw = data + + +@dataclass +class SpaceVariable: + """ + Contains information about the current variables of a Space. + + Args: + key (`str`): + Variable key. Example: `"MODEL_REPO_ID"` + value (`str`): + Variable value. Example: `"the_model_repo_id"`. + description (`str` or None): + Description of the variable. Example: `"Model Repo ID of the implemented model"`. + updatedAt (`datetime` or None): + datetime of the last update of the variable (if the variable has been updated at least once). + """ + + key: str + value: str + description: Optional[str] + updated_at: Optional[datetime] + + def __init__(self, key: str, values: Dict) -> None: + self.key = key + self.value = values["value"] + self.description = values.get("description") + updated_at = values.get("updatedAt") + self.updated_at = parse_datetime(updated_at) if updated_at is not None else None diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/_tensorboard_logger.py b/.venv/lib/python3.12/site-packages/huggingface_hub/_tensorboard_logger.py new file mode 100644 index 0000000000000000000000000000000000000000..fb172accebd76b0d51a87e2734b86bf75960651e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/_tensorboard_logger.py @@ -0,0 +1,193 @@ +# Copyright 2023 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains a logger to push training logs to the Hub, using Tensorboard.""" + +from pathlib import Path +from typing import List, Optional, Union + +from ._commit_scheduler import CommitScheduler +from .errors import EntryNotFoundError +from .repocard import ModelCard +from .utils import experimental + + +# Depending on user's setup, SummaryWriter can come either from 'tensorboardX' +# or from 'torch.utils.tensorboard'. Both are compatible so let's try to load +# from either of them. +try: + from tensorboardX import SummaryWriter as _RuntimeSummaryWriter + + is_summary_writer_available = True +except ImportError: + try: + from torch.utils.tensorboard import SummaryWriter as _RuntimeSummaryWriter + + is_summary_writer_available = True + except ImportError: + # Dummy class to avoid failing at import. Will raise on instance creation. + class _DummySummaryWriter: + pass + + _RuntimeSummaryWriter = _DummySummaryWriter # type: ignore[assignment] + is_summary_writer_available = False + + +class HFSummaryWriter(_RuntimeSummaryWriter): + """ + Wrapper around the tensorboard's `SummaryWriter` to push training logs to the Hub. + + Data is logged locally and then pushed to the Hub asynchronously. Pushing data to the Hub is done in a separate + thread to avoid blocking the training script. In particular, if the upload fails for any reason (e.g. a connection + issue), the main script will not be interrupted. Data is automatically pushed to the Hub every `commit_every` + minutes (default to every 5 minutes). + + + + `HFSummaryWriter` is experimental. Its API is subject to change in the future without prior notice. + + + + Args: + repo_id (`str`): + The id of the repo to which the logs will be pushed. + logdir (`str`, *optional*): + The directory where the logs will be written. If not specified, a local directory will be created by the + underlying `SummaryWriter` object. + commit_every (`int` or `float`, *optional*): + The frequency (in minutes) at which the logs will be pushed to the Hub. Defaults to 5 minutes. + squash_history (`bool`, *optional*): + Whether to squash the history of the repo after each commit. Defaults to `False`. Squashing commits is + useful to avoid degraded performances on the repo when it grows too large. + repo_type (`str`, *optional*): + The type of the repo to which the logs will be pushed. Defaults to "model". + repo_revision (`str`, *optional*): + The revision of the repo to which the logs will be pushed. Defaults to "main". + repo_private (`bool`, *optional*): + Whether to make the repo private. If `None` (default), the repo will be public unless the organization's default is private. This value is ignored if the repo already exists. + path_in_repo (`str`, *optional*): + The path to the folder in the repo where the logs will be pushed. Defaults to "tensorboard/". + repo_allow_patterns (`List[str]` or `str`, *optional*): + A list of patterns to include in the upload. Defaults to `"*.tfevents.*"`. Check out the + [upload guide](https://huggingface.co/docs/huggingface_hub/guides/upload#upload-a-folder) for more details. + repo_ignore_patterns (`List[str]` or `str`, *optional*): + A list of patterns to exclude in the upload. Check out the + [upload guide](https://huggingface.co/docs/huggingface_hub/guides/upload#upload-a-folder) for more details. + token (`str`, *optional*): + Authentication token. Will default to the stored token. See https://huggingface.co/settings/token for more + details + kwargs: + Additional keyword arguments passed to `SummaryWriter`. + + Examples: + ```diff + # Taken from https://pytorch.org/docs/stable/tensorboard.html + - from torch.utils.tensorboard import SummaryWriter + + from huggingface_hub import HFSummaryWriter + + import numpy as np + + - writer = SummaryWriter() + + writer = HFSummaryWriter(repo_id="username/my-trained-model") + + for n_iter in range(100): + writer.add_scalar('Loss/train', np.random.random(), n_iter) + writer.add_scalar('Loss/test', np.random.random(), n_iter) + writer.add_scalar('Accuracy/train', np.random.random(), n_iter) + writer.add_scalar('Accuracy/test', np.random.random(), n_iter) + ``` + + ```py + >>> from huggingface_hub import HFSummaryWriter + + # Logs are automatically pushed every 15 minutes (5 by default) + when exiting the context manager + >>> with HFSummaryWriter(repo_id="test_hf_logger", commit_every=15) as logger: + ... logger.add_scalar("a", 1) + ... logger.add_scalar("b", 2) + ``` + """ + + @experimental + def __new__(cls, *args, **kwargs) -> "HFSummaryWriter": + if not is_summary_writer_available: + raise ImportError( + "You must have `tensorboard` installed to use `HFSummaryWriter`. Please run `pip install --upgrade" + " tensorboardX` first." + ) + return super().__new__(cls) + + def __init__( + self, + repo_id: str, + *, + logdir: Optional[str] = None, + commit_every: Union[int, float] = 5, + squash_history: bool = False, + repo_type: Optional[str] = None, + repo_revision: Optional[str] = None, + repo_private: Optional[bool] = None, + path_in_repo: Optional[str] = "tensorboard", + repo_allow_patterns: Optional[Union[List[str], str]] = "*.tfevents.*", + repo_ignore_patterns: Optional[Union[List[str], str]] = None, + token: Optional[str] = None, + **kwargs, + ): + # Initialize SummaryWriter + super().__init__(logdir=logdir, **kwargs) + + # Check logdir has been correctly initialized and fail early otherwise. In practice, SummaryWriter takes care of it. + if not isinstance(self.logdir, str): + raise ValueError(f"`self.logdir` must be a string. Got '{self.logdir}' of type {type(self.logdir)}.") + + # Append logdir name to `path_in_repo` + if path_in_repo is None or path_in_repo == "": + path_in_repo = Path(self.logdir).name + else: + path_in_repo = path_in_repo.strip("/") + "/" + Path(self.logdir).name + + # Initialize scheduler + self.scheduler = CommitScheduler( + folder_path=self.logdir, + path_in_repo=path_in_repo, + repo_id=repo_id, + repo_type=repo_type, + revision=repo_revision, + private=repo_private, + token=token, + allow_patterns=repo_allow_patterns, + ignore_patterns=repo_ignore_patterns, + every=commit_every, + squash_history=squash_history, + ) + + # Exposing some high-level info at root level + self.repo_id = self.scheduler.repo_id + self.repo_type = self.scheduler.repo_type + self.repo_revision = self.scheduler.revision + + # Add `hf-summary-writer` tag to the model card metadata + try: + card = ModelCard.load(repo_id_or_path=self.repo_id, repo_type=self.repo_type) + except EntryNotFoundError: + card = ModelCard("") + tags = card.data.get("tags", []) + if "hf-summary-writer" not in tags: + tags.append("hf-summary-writer") + card.data["tags"] = tags + card.push_to_hub(repo_id=self.repo_id, repo_type=self.repo_type) + + def __exit__(self, exc_type, exc_val, exc_tb): + """Push to hub in a non-blocking way when exiting the logger's context manager.""" + super().__exit__(exc_type, exc_val, exc_tb) + future = self.scheduler.trigger() + future.result() diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/_upload_large_folder.py b/.venv/lib/python3.12/site-packages/huggingface_hub/_upload_large_folder.py new file mode 100644 index 0000000000000000000000000000000000000000..1ccbc07d39d3d03e9bb8c39f1bb16aa2ca4ab41f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/_upload_large_folder.py @@ -0,0 +1,755 @@ +# coding=utf-8 +# Copyright 2024-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import enum +import logging +import os +import queue +import shutil +import sys +import threading +import time +import traceback +from datetime import datetime +from pathlib import Path +from threading import Lock +from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union +from urllib.parse import quote + +from . import constants +from ._commit_api import CommitOperationAdd, UploadInfo, _fetch_upload_modes +from ._local_folder import LocalUploadFileMetadata, LocalUploadFilePaths, get_local_upload_paths, read_upload_metadata +from .constants import DEFAULT_REVISION, REPO_TYPES +from .utils import DEFAULT_IGNORE_PATTERNS, filter_repo_objects, tqdm +from .utils._cache_manager import _format_size +from .utils._runtime import is_xet_available +from .utils.sha import sha_fileobj + + +if TYPE_CHECKING: + from .hf_api import HfApi + +logger = logging.getLogger(__name__) + +WAITING_TIME_IF_NO_TASKS = 10 # seconds +MAX_NB_FILES_FETCH_UPLOAD_MODE = 100 +COMMIT_SIZE_SCALE: List[int] = [20, 50, 75, 100, 125, 200, 250, 400, 600, 1000] + +UPLOAD_BATCH_SIZE_XET = 256 # Max 256 files per upload batch for XET-enabled repos +UPLOAD_BATCH_SIZE_LFS = 1 # Otherwise, batches of 1 for regular LFS upload + +# Repository limits (from https://huggingface.co/docs/hub/repositories-recommendations) +MAX_FILES_PER_REPO = 100_000 # Recommended maximum number of files per repository +MAX_FILES_PER_FOLDER = 10_000 # Recommended maximum number of files per folder +MAX_FILE_SIZE_GB = 50 # Hard limit for individual file size +RECOMMENDED_FILE_SIZE_GB = 20 # Recommended maximum for individual file size + + +def _validate_upload_limits(paths_list: List[LocalUploadFilePaths]) -> None: + """ + Validate upload against repository limits and warn about potential issues. + + Args: + paths_list: List of file paths to be uploaded + + Warns about: + - Too many files in the repository (>100k) + - Too many entries (files or subdirectories) in a single folder (>10k) + - Files exceeding size limits (>20GB recommended, >50GB hard limit) + """ + logger.info("Running validation checks on files to upload...") + + # Check 1: Total file count + if len(paths_list) > MAX_FILES_PER_REPO: + logger.warning( + f"You are about to upload {len(paths_list):,} files. " + f"This exceeds the recommended limit of {MAX_FILES_PER_REPO:,} files per repository.\n" + f"Consider:\n" + f" - Splitting your data into multiple repositories\n" + f" - Using fewer, larger files (e.g., parquet files)\n" + f" - See: https://huggingface.co/docs/hub/repositories-recommendations" + ) + + # Check 2: Files and subdirectories per folder + # Track immediate children (files and subdirs) for each folder + from collections import defaultdict + + entries_per_folder: Dict[str, Any] = defaultdict(lambda: {"files": 0, "subdirs": set()}) + + for paths in paths_list: + path = Path(paths.path_in_repo) + parts = path.parts + + # Count this file in its immediate parent directory + parent = str(path.parent) if str(path.parent) != "." else "." + entries_per_folder[parent]["files"] += 1 + + # Track immediate subdirectories for each parent folder + # Walk through the path components to track parent-child relationships + for i, child in enumerate(parts[:-1]): + parent = "." if i == 0 else "/".join(parts[:i]) + entries_per_folder[parent]["subdirs"].add(child) + + # Check limits for each folder + for folder, data in entries_per_folder.items(): + file_count = data["files"] + subdir_count = len(data["subdirs"]) + total_entries = file_count + subdir_count + + if total_entries > MAX_FILES_PER_FOLDER: + folder_display = "root" if folder == "." else folder + logger.warning( + f"Folder '{folder_display}' contains {total_entries:,} entries " + f"({file_count:,} files and {subdir_count:,} subdirectories). " + f"This exceeds the recommended {MAX_FILES_PER_FOLDER:,} entries per folder.\n" + "Consider reorganising into sub-folders." + ) + + # Check 3: File sizes + large_files = [] + very_large_files = [] + + for paths in paths_list: + size = paths.file_path.stat().st_size + size_gb = size / 1_000_000_000 # Use decimal GB as per Hub limits + + if size_gb > MAX_FILE_SIZE_GB: + very_large_files.append((paths.path_in_repo, size_gb)) + elif size_gb > RECOMMENDED_FILE_SIZE_GB: + large_files.append((paths.path_in_repo, size_gb)) + + # Warn about very large files (>50GB) + if very_large_files: + files_str = "\n - ".join(f"{path}: {size:.1f}GB" for path, size in very_large_files[:5]) + more_str = f"\n ... and {len(very_large_files) - 5} more files" if len(very_large_files) > 5 else "" + logger.warning( + f"Found {len(very_large_files)} files exceeding the {MAX_FILE_SIZE_GB}GB hard limit:\n" + f" - {files_str}{more_str}\n" + f"These files may fail to upload. Consider splitting them into smaller chunks." + ) + + # Warn about large files (>20GB) + if large_files: + files_str = "\n - ".join(f"{path}: {size:.1f}GB" for path, size in large_files[:5]) + more_str = f"\n ... and {len(large_files) - 5} more files" if len(large_files) > 5 else "" + logger.warning( + f"Found {len(large_files)} files larger than {RECOMMENDED_FILE_SIZE_GB}GB (recommended limit):\n" + f" - {files_str}{more_str}\n" + f"Large files may slow down loading and processing." + ) + + logger.info("Validation checks complete.") + + +def upload_large_folder_internal( + api: "HfApi", + repo_id: str, + folder_path: Union[str, Path], + *, + repo_type: str, # Repo type is required! + revision: Optional[str] = None, + private: Optional[bool] = None, + allow_patterns: Optional[Union[List[str], str]] = None, + ignore_patterns: Optional[Union[List[str], str]] = None, + num_workers: Optional[int] = None, + print_report: bool = True, + print_report_every: int = 60, +): + """Upload a large folder to the Hub in the most resilient way possible. + + See [`HfApi.upload_large_folder`] for the full documentation. + """ + # 1. Check args and setup + if repo_type is None: + raise ValueError( + "For large uploads, `repo_type` is explicitly required. Please set it to `model`, `dataset` or `space`." + " If you are using the CLI, pass it as `--repo-type=model`." + ) + if repo_type not in REPO_TYPES: + raise ValueError(f"Invalid repo type, must be one of {REPO_TYPES}") + if revision is None: + revision = DEFAULT_REVISION + + folder_path = Path(folder_path).expanduser().resolve() + if not folder_path.is_dir(): + raise ValueError(f"Provided path: '{folder_path}' is not a directory") + + if ignore_patterns is None: + ignore_patterns = [] + elif isinstance(ignore_patterns, str): + ignore_patterns = [ignore_patterns] + ignore_patterns += DEFAULT_IGNORE_PATTERNS + + if num_workers is None: + nb_cores = os.cpu_count() or 1 + num_workers = max(nb_cores - 2, 2) # Use all but 2 cores, or at least 2 cores + + # 2. Create repo if missing + repo_url = api.create_repo(repo_id=repo_id, repo_type=repo_type, private=private, exist_ok=True) + logger.info(f"Repo created: {repo_url}") + repo_id = repo_url.repo_id + # 2.1 Check if xet is enabled to set batch file upload size + is_xet_enabled = ( + is_xet_available() + and api.repo_info( + repo_id=repo_id, + repo_type=repo_type, + revision=revision, + expand="xetEnabled", + ).xet_enabled + ) + upload_batch_size = UPLOAD_BATCH_SIZE_XET if is_xet_enabled else UPLOAD_BATCH_SIZE_LFS + + # 3. List files to upload + filtered_paths_list = filter_repo_objects( + (path.relative_to(folder_path).as_posix() for path in folder_path.glob("**/*") if path.is_file()), + allow_patterns=allow_patterns, + ignore_patterns=ignore_patterns, + ) + paths_list = [get_local_upload_paths(folder_path, relpath) for relpath in filtered_paths_list] + logger.info(f"Found {len(paths_list)} candidate files to upload") + + # Validate upload against repository limits + _validate_upload_limits(paths_list) + + logger.info("Starting upload...") + + # Read metadata for each file + items = [ + (paths, read_upload_metadata(folder_path, paths.path_in_repo)) + for paths in tqdm(paths_list, desc="Recovering from metadata files") + ] + + # 4. Start workers + status = LargeUploadStatus(items, upload_batch_size) + threads = [ + threading.Thread( + target=_worker_job, + kwargs={ + "status": status, + "api": api, + "repo_id": repo_id, + "repo_type": repo_type, + "revision": revision, + }, + ) + for _ in range(num_workers) + ] + + for thread in threads: + thread.start() + + # 5. Print regular reports + if print_report: + print("\n\n" + status.current_report()) + last_report_ts = time.time() + while True: + time.sleep(1) + if time.time() - last_report_ts >= print_report_every: + if print_report: + _print_overwrite(status.current_report()) + last_report_ts = time.time() + if status.is_done(): + logging.info("Is done: exiting main loop") + break + + for thread in threads: + thread.join() + + logger.info(status.current_report()) + logging.info("Upload is complete!") + + +#################### +# Logic to manage workers and synchronize tasks +#################### + + +class WorkerJob(enum.Enum): + SHA256 = enum.auto() + GET_UPLOAD_MODE = enum.auto() + PREUPLOAD_LFS = enum.auto() + COMMIT = enum.auto() + WAIT = enum.auto() # if no tasks are available but we don't want to exit + + +JOB_ITEM_T = Tuple[LocalUploadFilePaths, LocalUploadFileMetadata] + + +class LargeUploadStatus: + """Contains information, queues and tasks for a large upload process.""" + + def __init__(self, items: List[JOB_ITEM_T], upload_batch_size: int = 1): + self.items = items + self.queue_sha256: "queue.Queue[JOB_ITEM_T]" = queue.Queue() + self.queue_get_upload_mode: "queue.Queue[JOB_ITEM_T]" = queue.Queue() + self.queue_preupload_lfs: "queue.Queue[JOB_ITEM_T]" = queue.Queue() + self.queue_commit: "queue.Queue[JOB_ITEM_T]" = queue.Queue() + self.lock = Lock() + + self.nb_workers_sha256: int = 0 + self.nb_workers_get_upload_mode: int = 0 + self.nb_workers_preupload_lfs: int = 0 + self.upload_batch_size: int = upload_batch_size + self.nb_workers_commit: int = 0 + self.nb_workers_waiting: int = 0 + self.last_commit_attempt: Optional[float] = None + + self._started_at = datetime.now() + self._chunk_idx: int = 1 + self._chunk_lock: Lock = Lock() + + # Setup queues + for item in self.items: + paths, metadata = item + if metadata.sha256 is None: + self.queue_sha256.put(item) + elif metadata.upload_mode is None: + self.queue_get_upload_mode.put(item) + elif metadata.upload_mode == "lfs" and not metadata.is_uploaded: + self.queue_preupload_lfs.put(item) + elif not metadata.is_committed: + self.queue_commit.put(item) + else: + logger.debug(f"Skipping file {paths.path_in_repo} (already uploaded and committed)") + + def target_chunk(self) -> int: + with self._chunk_lock: + return COMMIT_SIZE_SCALE[self._chunk_idx] + + def update_chunk(self, success: bool, nb_items: int, duration: float) -> None: + with self._chunk_lock: + if not success: + logger.warning(f"Failed to commit {nb_items} files at once. Will retry with less files in next batch.") + self._chunk_idx -= 1 + elif nb_items >= COMMIT_SIZE_SCALE[self._chunk_idx] and duration < 40: + logger.info(f"Successfully committed {nb_items} at once. Increasing the limit for next batch.") + self._chunk_idx += 1 + + self._chunk_idx = max(0, min(self._chunk_idx, len(COMMIT_SIZE_SCALE) - 1)) + + def current_report(self) -> str: + """Generate a report of the current status of the large upload.""" + nb_hashed = 0 + size_hashed = 0 + nb_preuploaded = 0 + nb_lfs = 0 + nb_lfs_unsure = 0 + size_preuploaded = 0 + nb_committed = 0 + size_committed = 0 + total_size = 0 + ignored_files = 0 + total_files = 0 + + with self.lock: + for _, metadata in self.items: + if metadata.should_ignore: + ignored_files += 1 + continue + total_size += metadata.size + total_files += 1 + if metadata.sha256 is not None: + nb_hashed += 1 + size_hashed += metadata.size + if metadata.upload_mode == "lfs": + nb_lfs += 1 + if metadata.upload_mode is None: + nb_lfs_unsure += 1 + if metadata.is_uploaded: + nb_preuploaded += 1 + size_preuploaded += metadata.size + if metadata.is_committed: + nb_committed += 1 + size_committed += metadata.size + total_size_str = _format_size(total_size) + + now = datetime.now() + now_str = now.strftime("%Y-%m-%d %H:%M:%S") + elapsed = now - self._started_at + elapsed_str = str(elapsed).split(".")[0] # remove milliseconds + + message = "\n" + "-" * 10 + message += f" {now_str} ({elapsed_str}) " + message += "-" * 10 + "\n" + + message += "Files: " + message += f"hashed {nb_hashed}/{total_files} ({_format_size(size_hashed)}/{total_size_str}) | " + message += f"pre-uploaded: {nb_preuploaded}/{nb_lfs} ({_format_size(size_preuploaded)}/{total_size_str})" + if nb_lfs_unsure > 0: + message += f" (+{nb_lfs_unsure} unsure)" + message += f" | committed: {nb_committed}/{total_files} ({_format_size(size_committed)}/{total_size_str})" + message += f" | ignored: {ignored_files}\n" + + message += "Workers: " + message += f"hashing: {self.nb_workers_sha256} | " + message += f"get upload mode: {self.nb_workers_get_upload_mode} | " + message += f"pre-uploading: {self.nb_workers_preupload_lfs} | " + message += f"committing: {self.nb_workers_commit} | " + message += f"waiting: {self.nb_workers_waiting}\n" + message += "-" * 51 + + return message + + def is_done(self) -> bool: + with self.lock: + return all(metadata.is_committed or metadata.should_ignore for _, metadata in self.items) + + +def _worker_job( + status: LargeUploadStatus, + api: "HfApi", + repo_id: str, + repo_type: str, + revision: str, +): + """ + Main process for a worker. The worker will perform tasks based on the priority list until all files are uploaded + and committed. If no tasks are available, the worker will wait for 10 seconds before checking again. + + If a task fails for any reason, the item(s) are put back in the queue for another worker to pick up. + + Read `upload_large_folder` docstring for more information on how tasks are prioritized. + """ + while True: + next_job: Optional[Tuple[WorkerJob, List[JOB_ITEM_T]]] = None + + # Determine next task + next_job = _determine_next_job(status) + if next_job is None: + return + job, items = next_job + + # Perform task + if job == WorkerJob.SHA256: + item = items[0] # single item + try: + _compute_sha256(item) + status.queue_get_upload_mode.put(item) + except KeyboardInterrupt: + raise + except Exception as e: + logger.error(f"Failed to compute sha256: {e}") + traceback.format_exc() + status.queue_sha256.put(item) + + with status.lock: + status.nb_workers_sha256 -= 1 + + elif job == WorkerJob.GET_UPLOAD_MODE: + try: + _get_upload_mode(items, api=api, repo_id=repo_id, repo_type=repo_type, revision=revision) + except KeyboardInterrupt: + raise + except Exception as e: + logger.error(f"Failed to get upload mode: {e}") + traceback.format_exc() + + # Items are either: + # - dropped (if should_ignore) + # - put in LFS queue (if LFS) + # - put in commit queue (if regular) + # - or put back (if error occurred). + for item in items: + _, metadata = item + if metadata.should_ignore: + continue + if metadata.upload_mode == "lfs": + status.queue_preupload_lfs.put(item) + elif metadata.upload_mode == "regular": + status.queue_commit.put(item) + else: + status.queue_get_upload_mode.put(item) + + with status.lock: + status.nb_workers_get_upload_mode -= 1 + + elif job == WorkerJob.PREUPLOAD_LFS: + try: + _preupload_lfs(items, api=api, repo_id=repo_id, repo_type=repo_type, revision=revision) + for item in items: + status.queue_commit.put(item) + except KeyboardInterrupt: + raise + except Exception as e: + logger.error(f"Failed to preupload LFS: {e}") + traceback.format_exc() + for item in items: + status.queue_preupload_lfs.put(item) + + with status.lock: + status.nb_workers_preupload_lfs -= 1 + + elif job == WorkerJob.COMMIT: + start_ts = time.time() + success = True + try: + _commit(items, api=api, repo_id=repo_id, repo_type=repo_type, revision=revision) + except KeyboardInterrupt: + raise + except Exception as e: + logger.error(f"Failed to commit: {e}") + traceback.format_exc() + for item in items: + status.queue_commit.put(item) + success = False + duration = time.time() - start_ts + status.update_chunk(success, len(items), duration) + with status.lock: + status.last_commit_attempt = time.time() + status.nb_workers_commit -= 1 + + elif job == WorkerJob.WAIT: + time.sleep(WAITING_TIME_IF_NO_TASKS) + with status.lock: + status.nb_workers_waiting -= 1 + + +def _determine_next_job(status: LargeUploadStatus) -> Optional[Tuple[WorkerJob, List[JOB_ITEM_T]]]: + with status.lock: + # 1. Commit if more than 5 minutes since last commit attempt (and at least 1 file) + if ( + status.nb_workers_commit == 0 + and status.queue_commit.qsize() > 0 + and status.last_commit_attempt is not None + and time.time() - status.last_commit_attempt > 5 * 60 + ): + status.nb_workers_commit += 1 + logger.debug("Job: commit (more than 5 minutes since last commit attempt)") + return (WorkerJob.COMMIT, _get_n(status.queue_commit, status.target_chunk())) + + # 2. Commit if at least 100 files are ready to commit + elif status.nb_workers_commit == 0 and status.queue_commit.qsize() >= 150: + status.nb_workers_commit += 1 + logger.debug("Job: commit (>100 files ready)") + return (WorkerJob.COMMIT, _get_n(status.queue_commit, status.target_chunk())) + + # 3. Get upload mode if at least 100 files + elif status.queue_get_upload_mode.qsize() >= MAX_NB_FILES_FETCH_UPLOAD_MODE: + status.nb_workers_get_upload_mode += 1 + logger.debug(f"Job: get upload mode (>{MAX_NB_FILES_FETCH_UPLOAD_MODE} files ready)") + return (WorkerJob.GET_UPLOAD_MODE, _get_n(status.queue_get_upload_mode, MAX_NB_FILES_FETCH_UPLOAD_MODE)) + + # 4. Preupload LFS file if at least `status.upload_batch_size` files and no worker is preuploading LFS + elif status.queue_preupload_lfs.qsize() >= status.upload_batch_size and status.nb_workers_preupload_lfs == 0: + status.nb_workers_preupload_lfs += 1 + logger.debug("Job: preupload LFS (no other worker preuploading LFS)") + return (WorkerJob.PREUPLOAD_LFS, _get_n(status.queue_preupload_lfs, status.upload_batch_size)) + + # 5. Compute sha256 if at least 1 file and no worker is computing sha256 + elif status.queue_sha256.qsize() > 0 and status.nb_workers_sha256 == 0: + status.nb_workers_sha256 += 1 + logger.debug("Job: sha256 (no other worker computing sha256)") + return (WorkerJob.SHA256, _get_one(status.queue_sha256)) + + # 6. Get upload mode if at least 1 file and no worker is getting upload mode + elif status.queue_get_upload_mode.qsize() > 0 and status.nb_workers_get_upload_mode == 0: + status.nb_workers_get_upload_mode += 1 + logger.debug("Job: get upload mode (no other worker getting upload mode)") + return (WorkerJob.GET_UPLOAD_MODE, _get_n(status.queue_get_upload_mode, MAX_NB_FILES_FETCH_UPLOAD_MODE)) + + # 7. Preupload LFS file if at least `status.upload_batch_size` files + # Skip if hf_transfer is enabled and there is already a worker preuploading LFS + elif status.queue_preupload_lfs.qsize() >= status.upload_batch_size and ( + status.nb_workers_preupload_lfs == 0 or not constants.HF_HUB_ENABLE_HF_TRANSFER + ): + status.nb_workers_preupload_lfs += 1 + logger.debug("Job: preupload LFS") + return (WorkerJob.PREUPLOAD_LFS, _get_n(status.queue_preupload_lfs, status.upload_batch_size)) + + # 8. Compute sha256 if at least 1 file + elif status.queue_sha256.qsize() > 0: + status.nb_workers_sha256 += 1 + logger.debug("Job: sha256") + return (WorkerJob.SHA256, _get_one(status.queue_sha256)) + + # 9. Get upload mode if at least 1 file + elif status.queue_get_upload_mode.qsize() > 0: + status.nb_workers_get_upload_mode += 1 + logger.debug("Job: get upload mode") + return (WorkerJob.GET_UPLOAD_MODE, _get_n(status.queue_get_upload_mode, MAX_NB_FILES_FETCH_UPLOAD_MODE)) + + # 10. Preupload LFS file if at least 1 file + elif status.queue_preupload_lfs.qsize() > 0: + status.nb_workers_preupload_lfs += 1 + logger.debug("Job: preupload LFS") + return (WorkerJob.PREUPLOAD_LFS, _get_n(status.queue_preupload_lfs, status.upload_batch_size)) + + # 11. Commit if at least 1 file and 1 min since last commit attempt + elif ( + status.nb_workers_commit == 0 + and status.queue_commit.qsize() > 0 + and status.last_commit_attempt is not None + and time.time() - status.last_commit_attempt > 1 * 60 + ): + status.nb_workers_commit += 1 + logger.debug("Job: commit (1 min since last commit attempt)") + return (WorkerJob.COMMIT, _get_n(status.queue_commit, status.target_chunk())) + + # 12. Commit if at least 1 file all other queues are empty and all workers are waiting + # e.g. when it's the last commit + elif ( + status.nb_workers_commit == 0 + and status.queue_commit.qsize() > 0 + and status.queue_sha256.qsize() == 0 + and status.queue_get_upload_mode.qsize() == 0 + and status.queue_preupload_lfs.qsize() == 0 + and status.nb_workers_sha256 == 0 + and status.nb_workers_get_upload_mode == 0 + and status.nb_workers_preupload_lfs == 0 + ): + status.nb_workers_commit += 1 + logger.debug("Job: commit") + return (WorkerJob.COMMIT, _get_n(status.queue_commit, status.target_chunk())) + + # 13. If all queues are empty, exit + elif all(metadata.is_committed or metadata.should_ignore for _, metadata in status.items): + logger.info("All files have been processed! Exiting worker.") + return None + + # 14. If no task is available, wait + else: + status.nb_workers_waiting += 1 + logger.debug(f"No task available, waiting... ({WAITING_TIME_IF_NO_TASKS}s)") + return (WorkerJob.WAIT, []) + + +#################### +# Atomic jobs (sha256, get_upload_mode, preupload_lfs, commit) +#################### + + +def _compute_sha256(item: JOB_ITEM_T) -> None: + """Compute sha256 of a file and save it in metadata.""" + paths, metadata = item + if metadata.sha256 is None: + with paths.file_path.open("rb") as f: + metadata.sha256 = sha_fileobj(f).hex() + metadata.save(paths) + + +def _get_upload_mode(items: List[JOB_ITEM_T], api: "HfApi", repo_id: str, repo_type: str, revision: str) -> None: + """Get upload mode for each file and update metadata. + + Also receive info if the file should be ignored. + """ + additions = [_build_hacky_operation(item) for item in items] + _fetch_upload_modes( + additions=additions, + repo_type=repo_type, + repo_id=repo_id, + headers=api._build_hf_headers(), + revision=quote(revision, safe=""), + endpoint=api.endpoint, + ) + for item, addition in zip(items, additions): + paths, metadata = item + metadata.upload_mode = addition._upload_mode + metadata.should_ignore = addition._should_ignore + metadata.remote_oid = addition._remote_oid + metadata.save(paths) + + +def _preupload_lfs(items: List[JOB_ITEM_T], api: "HfApi", repo_id: str, repo_type: str, revision: str) -> None: + """Preupload LFS files and update metadata.""" + additions = [_build_hacky_operation(item) for item in items] + api.preupload_lfs_files( + repo_id=repo_id, + repo_type=repo_type, + revision=revision, + additions=additions, + ) + + for paths, metadata in items: + metadata.is_uploaded = True + metadata.save(paths) + + +def _commit(items: List[JOB_ITEM_T], api: "HfApi", repo_id: str, repo_type: str, revision: str) -> None: + """Commit files to the repo.""" + additions = [_build_hacky_operation(item) for item in items] + api.create_commit( + repo_id=repo_id, + repo_type=repo_type, + revision=revision, + operations=additions, + commit_message="Add files using upload-large-folder tool", + ) + for paths, metadata in items: + metadata.is_committed = True + metadata.save(paths) + + +#################### +# Hacks with CommitOperationAdd to bypass checks/sha256 calculation +#################### + + +class HackyCommitOperationAdd(CommitOperationAdd): + def __post_init__(self) -> None: + if isinstance(self.path_or_fileobj, Path): + self.path_or_fileobj = str(self.path_or_fileobj) + + +def _build_hacky_operation(item: JOB_ITEM_T) -> HackyCommitOperationAdd: + paths, metadata = item + operation = HackyCommitOperationAdd(path_in_repo=paths.path_in_repo, path_or_fileobj=paths.file_path) + with paths.file_path.open("rb") as file: + sample = file.peek(512)[:512] + if metadata.sha256 is None: + raise ValueError("sha256 must have been computed by now!") + operation.upload_info = UploadInfo(sha256=bytes.fromhex(metadata.sha256), size=metadata.size, sample=sample) + operation._upload_mode = metadata.upload_mode # type: ignore[assignment] + operation._should_ignore = metadata.should_ignore + operation._remote_oid = metadata.remote_oid + return operation + + +#################### +# Misc helpers +#################### + + +def _get_one(queue: "queue.Queue[JOB_ITEM_T]") -> List[JOB_ITEM_T]: + return [queue.get()] + + +def _get_n(queue: "queue.Queue[JOB_ITEM_T]", n: int) -> List[JOB_ITEM_T]: + return [queue.get() for _ in range(min(queue.qsize(), n))] + + +def _print_overwrite(report: str) -> None: + """Print a report, overwriting the previous lines. + + Since tqdm in using `sys.stderr` to (re-)write progress bars, we need to use `sys.stdout` + to print the report. + + Note: works well only if no other process is writing to `sys.stdout`! + """ + report += "\n" + # Get terminal width + terminal_width = shutil.get_terminal_size().columns + + # Count number of lines that should be cleared + nb_lines = sum(len(line) // terminal_width + 1 for line in report.splitlines()) + + # Clear previous lines based on the number of lines in the report + for _ in range(nb_lines): + sys.stdout.write("\r\033[K") # Clear line + sys.stdout.write("\033[F") # Move cursor up one line + + # Print the new report, filling remaining space with whitespace + sys.stdout.write(report) + sys.stdout.write(" " * (terminal_width - len(report.splitlines()[-1]))) + sys.stdout.flush() diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/_webhooks_payload.py b/.venv/lib/python3.12/site-packages/huggingface_hub/_webhooks_payload.py new file mode 100644 index 0000000000000000000000000000000000000000..288f4b08b9428980e99ca06703442eab62fad277 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/_webhooks_payload.py @@ -0,0 +1,137 @@ +# coding=utf-8 +# Copyright 2023-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains data structures to parse the webhooks payload.""" + +from typing import List, Literal, Optional + +from .utils import is_pydantic_available + + +if is_pydantic_available(): + from pydantic import BaseModel +else: + # Define a dummy BaseModel to avoid import errors when pydantic is not installed + # Import error will be raised when trying to use the class + + class BaseModel: # type: ignore [no-redef] + def __init__(self, *args, **kwargs) -> None: + raise ImportError( + "You must have `pydantic` installed to use `WebhookPayload`. This is an optional dependency that" + " should be installed separately. Please run `pip install --upgrade pydantic` and retry." + ) + + +# This is an adaptation of the ReportV3 interface implemented in moon-landing. V0, V1 and V2 have been ignored as they +# are not in used anymore. To keep in sync when format is updated in +# https://github.com/huggingface/moon-landing/blob/main/server/lib/HFWebhooks.ts (internal link). + + +WebhookEvent_T = Literal[ + "create", + "delete", + "move", + "update", +] +RepoChangeEvent_T = Literal[ + "add", + "move", + "remove", + "update", +] +RepoType_T = Literal[ + "dataset", + "model", + "space", +] +DiscussionStatus_T = Literal[ + "closed", + "draft", + "open", + "merged", +] +SupportedWebhookVersion = Literal[3] + + +class ObjectId(BaseModel): + id: str + + +class WebhookPayloadUrl(BaseModel): + web: str + api: Optional[str] = None + + +class WebhookPayloadMovedTo(BaseModel): + name: str + owner: ObjectId + + +class WebhookPayloadWebhook(ObjectId): + version: SupportedWebhookVersion + + +class WebhookPayloadEvent(BaseModel): + action: WebhookEvent_T + scope: str + + +class WebhookPayloadDiscussionChanges(BaseModel): + base: str + mergeCommitId: Optional[str] = None + + +class WebhookPayloadComment(ObjectId): + author: ObjectId + hidden: bool + content: Optional[str] = None + url: WebhookPayloadUrl + + +class WebhookPayloadDiscussion(ObjectId): + num: int + author: ObjectId + url: WebhookPayloadUrl + title: str + isPullRequest: bool + status: DiscussionStatus_T + changes: Optional[WebhookPayloadDiscussionChanges] = None + pinned: Optional[bool] = None + + +class WebhookPayloadRepo(ObjectId): + owner: ObjectId + head_sha: Optional[str] = None + name: str + private: bool + subdomain: Optional[str] = None + tags: Optional[List[str]] = None + type: Literal["dataset", "model", "space"] + url: WebhookPayloadUrl + + +class WebhookPayloadUpdatedRef(BaseModel): + ref: str + oldSha: Optional[str] = None + newSha: Optional[str] = None + + +class WebhookPayload(BaseModel): + event: WebhookPayloadEvent + repo: WebhookPayloadRepo + discussion: Optional[WebhookPayloadDiscussion] = None + comment: Optional[WebhookPayloadComment] = None + webhook: WebhookPayloadWebhook + movedTo: Optional[WebhookPayloadMovedTo] = None + updatedRefs: Optional[List[WebhookPayloadUpdatedRef]] = None diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/_webhooks_server.py b/.venv/lib/python3.12/site-packages/huggingface_hub/_webhooks_server.py new file mode 100644 index 0000000000000000000000000000000000000000..a7bd6c86261b1cc26dfcfe3a65f5aec9851a1162 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/_webhooks_server.py @@ -0,0 +1,388 @@ +# coding=utf-8 +# Copyright 2023-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains `WebhooksServer` and `webhook_endpoint` to create a webhook server easily.""" + +import atexit +import inspect +import os +from functools import wraps +from typing import TYPE_CHECKING, Any, Callable, Dict, Optional + +from .utils import experimental, is_fastapi_available, is_gradio_available + + +if TYPE_CHECKING: + import gradio as gr + from fastapi import Request + +if is_fastapi_available(): + from fastapi import FastAPI, Request + from fastapi.responses import JSONResponse +else: + # Will fail at runtime if FastAPI is not available + FastAPI = Request = JSONResponse = None # type: ignore [misc, assignment] + + +_global_app: Optional["WebhooksServer"] = None +_is_local = os.environ.get("SPACE_ID") is None + + +@experimental +class WebhooksServer: + """ + The [`WebhooksServer`] class lets you create an instance of a Gradio app that can receive Huggingface webhooks. + These webhooks can be registered using the [`~WebhooksServer.add_webhook`] decorator. Webhook endpoints are added to + the app as a POST endpoint to the FastAPI router. Once all the webhooks are registered, the `launch` method has to be + called to start the app. + + It is recommended to accept [`WebhookPayload`] as the first argument of the webhook function. It is a Pydantic + model that contains all the information about the webhook event. The data will be parsed automatically for you. + + Check out the [webhooks guide](../guides/webhooks_server) for a step-by-step tutorial on how to setup your + WebhooksServer and deploy it on a Space. + + + + `WebhooksServer` is experimental. Its API is subject to change in the future. + + + + + + You must have `gradio` installed to use `WebhooksServer` (`pip install --upgrade gradio`). + + + + Args: + ui (`gradio.Blocks`, optional): + A Gradio UI instance to be used as the Space landing page. If `None`, a UI displaying instructions + about the configured webhooks is created. + webhook_secret (`str`, optional): + A secret key to verify incoming webhook requests. You can set this value to any secret you want as long as + you also configure it in your [webhooks settings panel](https://huggingface.co/settings/webhooks). You + can also set this value as the `WEBHOOK_SECRET` environment variable. If no secret is provided, the + webhook endpoints are opened without any security. + + Example: + + ```python + import gradio as gr + from huggingface_hub import WebhooksServer, WebhookPayload + + with gr.Blocks() as ui: + ... + + app = WebhooksServer(ui=ui, webhook_secret="my_secret_key") + + @app.add_webhook("/say_hello") + async def hello(payload: WebhookPayload): + return {"message": "hello"} + + app.launch() + ``` + """ + + def __new__(cls, *args, **kwargs) -> "WebhooksServer": + if not is_gradio_available(): + raise ImportError( + "You must have `gradio` installed to use `WebhooksServer`. Please run `pip install --upgrade gradio`" + " first." + ) + if not is_fastapi_available(): + raise ImportError( + "You must have `fastapi` installed to use `WebhooksServer`. Please run `pip install --upgrade fastapi`" + " first." + ) + return super().__new__(cls) + + def __init__( + self, + ui: Optional["gr.Blocks"] = None, + webhook_secret: Optional[str] = None, + ) -> None: + self._ui = ui + + self.webhook_secret = webhook_secret or os.getenv("WEBHOOK_SECRET") + self.registered_webhooks: Dict[str, Callable] = {} + _warn_on_empty_secret(self.webhook_secret) + + def add_webhook(self, path: Optional[str] = None) -> Callable: + """ + Decorator to add a webhook to the [`WebhooksServer`] server. + + Args: + path (`str`, optional): + The URL path to register the webhook function. If not provided, the function name will be used as the + path. In any case, all webhooks are registered under `/webhooks`. + + Raises: + ValueError: If the provided path is already registered as a webhook. + + Example: + ```python + from huggingface_hub import WebhooksServer, WebhookPayload + + app = WebhooksServer() + + @app.add_webhook + async def trigger_training(payload: WebhookPayload): + if payload.repo.type == "dataset" and payload.event.action == "update": + # Trigger a training job if a dataset is updated + ... + + app.launch() + ``` + """ + # Usage: directly as decorator. Example: `@app.add_webhook` + if callable(path): + # If path is a function, it means it was used as a decorator without arguments + return self.add_webhook()(path) + + # Usage: provide a path. Example: `@app.add_webhook(...)` + @wraps(FastAPI.post) + def _inner_post(*args, **kwargs): + func = args[0] + abs_path = f"/webhooks/{(path or func.__name__).strip('/')}" + if abs_path in self.registered_webhooks: + raise ValueError(f"Webhook {abs_path} already exists.") + self.registered_webhooks[abs_path] = func + + return _inner_post + + def launch(self, prevent_thread_lock: bool = False, **launch_kwargs: Any) -> None: + """Launch the Gradio app and register webhooks to the underlying FastAPI server. + + Input parameters are forwarded to Gradio when launching the app. + """ + ui = self._ui or self._get_default_ui() + + # Start Gradio App + # - as non-blocking so that webhooks can be added afterwards + # - as shared if launch locally (to debug webhooks) + launch_kwargs.setdefault("share", _is_local) + self.fastapi_app, _, _ = ui.launch(prevent_thread_lock=True, **launch_kwargs) + + # Register webhooks to FastAPI app + for path, func in self.registered_webhooks.items(): + # Add secret check if required + if self.webhook_secret is not None: + func = _wrap_webhook_to_check_secret(func, webhook_secret=self.webhook_secret) + + # Add route to FastAPI app + self.fastapi_app.post(path)(func) + + # Print instructions and block main thread + space_host = os.environ.get("SPACE_HOST") + url = "https://" + space_host if space_host is not None else (ui.share_url or ui.local_url) + if url is None: + raise ValueError("Cannot find the URL of the app. Please provide a valid `ui` or update `gradio` version.") + url = url.strip("/") + message = "\nWebhooks are correctly setup and ready to use:" + message += "\n" + "\n".join(f" - POST {url}{webhook}" for webhook in self.registered_webhooks) + message += "\nGo to https://huggingface.co/settings/webhooks to setup your webhooks." + print(message) + + if not prevent_thread_lock: + ui.block_thread() + + def _get_default_ui(self) -> "gr.Blocks": + """Default UI if not provided (lists webhooks and provides basic instructions).""" + import gradio as gr + + with gr.Blocks() as ui: + gr.Markdown("# This is an app to process 🤗 Webhooks") + gr.Markdown( + "Webhooks are a foundation for MLOps-related features. They allow you to listen for new changes on" + " specific repos or to all repos belonging to particular set of users/organizations (not just your" + " repos, but any repo). Check out this [guide](https://huggingface.co/docs/hub/webhooks) to get to" + " know more about webhooks on the Huggingface Hub." + ) + gr.Markdown( + f"{len(self.registered_webhooks)} webhook(s) are registered:" + + "\n\n" + + "\n ".join( + f"- [{webhook_path}]({_get_webhook_doc_url(webhook.__name__, webhook_path)})" + for webhook_path, webhook in self.registered_webhooks.items() + ) + ) + gr.Markdown( + "Go to https://huggingface.co/settings/webhooks to setup your webhooks." + + "\nYou app is running locally. Please look at the logs to check the full URL you need to set." + if _is_local + else ( + "\nThis app is running on a Space. You can find the corresponding URL in the options menu" + " (top-right) > 'Embed the Space'. The URL looks like 'https://{username}-{repo_name}.hf.space'." + ) + ) + return ui + + +@experimental +def webhook_endpoint(path: Optional[str] = None) -> Callable: + """Decorator to start a [`WebhooksServer`] and register the decorated function as a webhook endpoint. + + This is a helper to get started quickly. If you need more flexibility (custom landing page or webhook secret), + you can use [`WebhooksServer`] directly. You can register multiple webhook endpoints (to the same server) by using + this decorator multiple times. + + Check out the [webhooks guide](../guides/webhooks_server) for a step-by-step tutorial on how to setup your + server and deploy it on a Space. + + + + `webhook_endpoint` is experimental. Its API is subject to change in the future. + + + + + + You must have `gradio` installed to use `webhook_endpoint` (`pip install --upgrade gradio`). + + + + Args: + path (`str`, optional): + The URL path to register the webhook function. If not provided, the function name will be used as the path. + In any case, all webhooks are registered under `/webhooks`. + + Examples: + The default usage is to register a function as a webhook endpoint. The function name will be used as the path. + The server will be started automatically at exit (i.e. at the end of the script). + + ```python + from huggingface_hub import webhook_endpoint, WebhookPayload + + @webhook_endpoint + async def trigger_training(payload: WebhookPayload): + if payload.repo.type == "dataset" and payload.event.action == "update": + # Trigger a training job if a dataset is updated + ... + + # Server is automatically started at the end of the script. + ``` + + Advanced usage: register a function as a webhook endpoint and start the server manually. This is useful if you + are running it in a notebook. + + ```python + from huggingface_hub import webhook_endpoint, WebhookPayload + + @webhook_endpoint + async def trigger_training(payload: WebhookPayload): + if payload.repo.type == "dataset" and payload.event.action == "update": + # Trigger a training job if a dataset is updated + ... + + # Start the server manually + trigger_training.launch() + ``` + """ + if callable(path): + # If path is a function, it means it was used as a decorator without arguments + return webhook_endpoint()(path) + + @wraps(WebhooksServer.add_webhook) + def _inner(func: Callable) -> Callable: + app = _get_global_app() + app.add_webhook(path)(func) + if len(app.registered_webhooks) == 1: + # Register `app.launch` to run at exit (only once) + atexit.register(app.launch) + + @wraps(app.launch) + def _launch_now(): + # Run the app directly (without waiting atexit) + atexit.unregister(app.launch) + app.launch() + + func.launch = _launch_now # type: ignore + return func + + return _inner + + +def _get_global_app() -> WebhooksServer: + global _global_app + if _global_app is None: + _global_app = WebhooksServer() + return _global_app + + +def _warn_on_empty_secret(webhook_secret: Optional[str]) -> None: + if webhook_secret is None: + print("Webhook secret is not defined. This means your webhook endpoints will be open to everyone.") + print( + "To add a secret, set `WEBHOOK_SECRET` as environment variable or pass it at initialization: " + "\n\t`app = WebhooksServer(webhook_secret='my_secret', ...)`" + ) + print( + "For more details about webhook secrets, please refer to" + " https://huggingface.co/docs/hub/webhooks#webhook-secret." + ) + else: + print("Webhook secret is correctly defined.") + + +def _get_webhook_doc_url(webhook_name: str, webhook_path: str) -> str: + """Returns the anchor to a given webhook in the docs (experimental)""" + return "/docs#/default/" + webhook_name + webhook_path.replace("/", "_") + "_post" + + +def _wrap_webhook_to_check_secret(func: Callable, webhook_secret: str) -> Callable: + """Wraps a webhook function to check the webhook secret before calling the function. + + This is a hacky way to add the `request` parameter to the function signature. Since FastAPI based itself on route + parameters to inject the values to the function, we need to hack the function signature to retrieve the `Request` + object (and hence the headers). A far cleaner solution would be to use a middleware. However, since + `fastapi==0.90.1`, a middleware cannot be added once the app has started. And since the FastAPI app is started by + Gradio internals (and not by us), we cannot add a middleware. + + This method is called only when a secret has been defined by the user. If a request is sent without the + "x-webhook-secret", the function will return a 401 error (unauthorized). If the header is sent but is incorrect, + the function will return a 403 error (forbidden). + + Inspired by https://stackoverflow.com/a/33112180. + """ + initial_sig = inspect.signature(func) + + @wraps(func) + async def _protected_func(request: Request, **kwargs): + request_secret = request.headers.get("x-webhook-secret") + if request_secret is None: + return JSONResponse({"error": "x-webhook-secret header not set."}, status_code=401) + if request_secret != webhook_secret: + return JSONResponse({"error": "Invalid webhook secret."}, status_code=403) + + # Inject `request` in kwargs if required + if "request" in initial_sig.parameters: + kwargs["request"] = request + + # Handle both sync and async routes + if inspect.iscoroutinefunction(func): + return await func(**kwargs) + else: + return func(**kwargs) + + # Update signature to include request + if "request" not in initial_sig.parameters: + _protected_func.__signature__ = initial_sig.replace( # type: ignore + parameters=( + inspect.Parameter(name="request", kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=Request), + ) + + tuple(initial_sig.parameters.values()) + ) + + # Return protected route + return _protected_func diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/community.py b/.venv/lib/python3.12/site-packages/huggingface_hub/community.py new file mode 100644 index 0000000000000000000000000000000000000000..16f2f02428dd5c2ce6437534af0397801bda45c5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/community.py @@ -0,0 +1,355 @@ +""" +Data structures to interact with Discussions and Pull Requests on the Hub. + +See [the Discussions and Pull Requests guide](https://huggingface.co/docs/hub/repositories-pull-requests-discussions) +for more information on Pull Requests, Discussions, and the community tab. +""" + +from dataclasses import dataclass +from datetime import datetime +from typing import List, Literal, Optional, Union + +from . import constants +from .utils import parse_datetime + + +DiscussionStatus = Literal["open", "closed", "merged", "draft"] + + +@dataclass +class Discussion: + """ + A Discussion or Pull Request on the Hub. + + This dataclass is not intended to be instantiated directly. + + Attributes: + title (`str`): + The title of the Discussion / Pull Request + status (`str`): + The status of the Discussion / Pull Request. + It must be one of: + * `"open"` + * `"closed"` + * `"merged"` (only for Pull Requests ) + * `"draft"` (only for Pull Requests ) + num (`int`): + The number of the Discussion / Pull Request. + repo_id (`str`): + The id (`"{namespace}/{repo_name}"`) of the repo on which + the Discussion / Pull Request was open. + repo_type (`str`): + The type of the repo on which the Discussion / Pull Request was open. + Possible values are: `"model"`, `"dataset"`, `"space"`. + author (`str`): + The username of the Discussion / Pull Request author. + Can be `"deleted"` if the user has been deleted since. + is_pull_request (`bool`): + Whether or not this is a Pull Request. + created_at (`datetime`): + The `datetime` of creation of the Discussion / Pull Request. + endpoint (`str`): + Endpoint of the Hub. Default is https://huggingface.co. + git_reference (`str`, *optional*): + (property) Git reference to which changes can be pushed if this is a Pull Request, `None` otherwise. + url (`str`): + (property) URL of the discussion on the Hub. + """ + + title: str + status: DiscussionStatus + num: int + repo_id: str + repo_type: str + author: str + is_pull_request: bool + created_at: datetime + endpoint: str + + @property + def git_reference(self) -> Optional[str]: + """ + If this is a Pull Request , returns the git reference to which changes can be pushed. + Returns `None` otherwise. + """ + if self.is_pull_request: + return f"refs/pr/{self.num}" + return None + + @property + def url(self) -> str: + """Returns the URL of the discussion on the Hub.""" + if self.repo_type is None or self.repo_type == constants.REPO_TYPE_MODEL: + return f"{self.endpoint}/{self.repo_id}/discussions/{self.num}" + return f"{self.endpoint}/{self.repo_type}s/{self.repo_id}/discussions/{self.num}" + + +@dataclass +class DiscussionWithDetails(Discussion): + """ + Subclass of [`Discussion`]. + + Attributes: + title (`str`): + The title of the Discussion / Pull Request + status (`str`): + The status of the Discussion / Pull Request. + It can be one of: + * `"open"` + * `"closed"` + * `"merged"` (only for Pull Requests ) + * `"draft"` (only for Pull Requests ) + num (`int`): + The number of the Discussion / Pull Request. + repo_id (`str`): + The id (`"{namespace}/{repo_name}"`) of the repo on which + the Discussion / Pull Request was open. + repo_type (`str`): + The type of the repo on which the Discussion / Pull Request was open. + Possible values are: `"model"`, `"dataset"`, `"space"`. + author (`str`): + The username of the Discussion / Pull Request author. + Can be `"deleted"` if the user has been deleted since. + is_pull_request (`bool`): + Whether or not this is a Pull Request. + created_at (`datetime`): + The `datetime` of creation of the Discussion / Pull Request. + events (`list` of [`DiscussionEvent`]) + The list of [`DiscussionEvents`] in this Discussion or Pull Request. + conflicting_files (`Union[List[str], bool, None]`, *optional*): + A list of conflicting files if this is a Pull Request. + `None` if `self.is_pull_request` is `False`. + `True` if there are conflicting files but the list can't be retrieved. + target_branch (`str`, *optional*): + The branch into which changes are to be merged if this is a + Pull Request . `None` if `self.is_pull_request` is `False`. + merge_commit_oid (`str`, *optional*): + If this is a merged Pull Request , this is set to the OID / SHA of + the merge commit, `None` otherwise. + diff (`str`, *optional*): + The git diff if this is a Pull Request , `None` otherwise. + endpoint (`str`): + Endpoint of the Hub. Default is https://huggingface.co. + git_reference (`str`, *optional*): + (property) Git reference to which changes can be pushed if this is a Pull Request, `None` otherwise. + url (`str`): + (property) URL of the discussion on the Hub. + """ + + events: List["DiscussionEvent"] + conflicting_files: Union[List[str], bool, None] + target_branch: Optional[str] + merge_commit_oid: Optional[str] + diff: Optional[str] + + +@dataclass +class DiscussionEvent: + """ + An event in a Discussion or Pull Request. + + Use concrete classes: + * [`DiscussionComment`] + * [`DiscussionStatusChange`] + * [`DiscussionCommit`] + * [`DiscussionTitleChange`] + + Attributes: + id (`str`): + The ID of the event. An hexadecimal string. + type (`str`): + The type of the event. + created_at (`datetime`): + A [`datetime`](https://docs.python.org/3/library/datetime.html?highlight=datetime#datetime.datetime) + object holding the creation timestamp for the event. + author (`str`): + The username of the Discussion / Pull Request author. + Can be `"deleted"` if the user has been deleted since. + """ + + id: str + type: str + created_at: datetime + author: str + + _event: dict + """Stores the original event data, in case we need to access it later.""" + + +@dataclass +class DiscussionComment(DiscussionEvent): + """A comment in a Discussion / Pull Request. + + Subclass of [`DiscussionEvent`]. + + + Attributes: + id (`str`): + The ID of the event. An hexadecimal string. + type (`str`): + The type of the event. + created_at (`datetime`): + A [`datetime`](https://docs.python.org/3/library/datetime.html?highlight=datetime#datetime.datetime) + object holding the creation timestamp for the event. + author (`str`): + The username of the Discussion / Pull Request author. + Can be `"deleted"` if the user has been deleted since. + content (`str`): + The raw markdown content of the comment. Mentions, links and images are not rendered. + edited (`bool`): + Whether or not this comment has been edited. + hidden (`bool`): + Whether or not this comment has been hidden. + """ + + content: str + edited: bool + hidden: bool + + @property + def rendered(self) -> str: + """The rendered comment, as a HTML string""" + return self._event["data"]["latest"]["html"] + + @property + def last_edited_at(self) -> datetime: + """The last edit time, as a `datetime` object.""" + return parse_datetime(self._event["data"]["latest"]["updatedAt"]) + + @property + def last_edited_by(self) -> str: + """The last edit time, as a `datetime` object.""" + return self._event["data"]["latest"].get("author", {}).get("name", "deleted") + + @property + def edit_history(self) -> List[dict]: + """The edit history of the comment""" + return self._event["data"]["history"] + + @property + def number_of_edits(self) -> int: + return len(self.edit_history) + + +@dataclass +class DiscussionStatusChange(DiscussionEvent): + """A change of status in a Discussion / Pull Request. + + Subclass of [`DiscussionEvent`]. + + Attributes: + id (`str`): + The ID of the event. An hexadecimal string. + type (`str`): + The type of the event. + created_at (`datetime`): + A [`datetime`](https://docs.python.org/3/library/datetime.html?highlight=datetime#datetime.datetime) + object holding the creation timestamp for the event. + author (`str`): + The username of the Discussion / Pull Request author. + Can be `"deleted"` if the user has been deleted since. + new_status (`str`): + The status of the Discussion / Pull Request after the change. + It can be one of: + * `"open"` + * `"closed"` + * `"merged"` (only for Pull Requests ) + """ + + new_status: str + + +@dataclass +class DiscussionCommit(DiscussionEvent): + """A commit in a Pull Request. + + Subclass of [`DiscussionEvent`]. + + Attributes: + id (`str`): + The ID of the event. An hexadecimal string. + type (`str`): + The type of the event. + created_at (`datetime`): + A [`datetime`](https://docs.python.org/3/library/datetime.html?highlight=datetime#datetime.datetime) + object holding the creation timestamp for the event. + author (`str`): + The username of the Discussion / Pull Request author. + Can be `"deleted"` if the user has been deleted since. + summary (`str`): + The summary of the commit. + oid (`str`): + The OID / SHA of the commit, as a hexadecimal string. + """ + + summary: str + oid: str + + +@dataclass +class DiscussionTitleChange(DiscussionEvent): + """A rename event in a Discussion / Pull Request. + + Subclass of [`DiscussionEvent`]. + + Attributes: + id (`str`): + The ID of the event. An hexadecimal string. + type (`str`): + The type of the event. + created_at (`datetime`): + A [`datetime`](https://docs.python.org/3/library/datetime.html?highlight=datetime#datetime.datetime) + object holding the creation timestamp for the event. + author (`str`): + The username of the Discussion / Pull Request author. + Can be `"deleted"` if the user has been deleted since. + old_title (`str`): + The previous title for the Discussion / Pull Request. + new_title (`str`): + The new title. + """ + + old_title: str + new_title: str + + +def deserialize_event(event: dict) -> DiscussionEvent: + """Instantiates a [`DiscussionEvent`] from a dict""" + event_id: str = event["id"] + event_type: str = event["type"] + created_at = parse_datetime(event["createdAt"]) + + common_args = dict( + id=event_id, + type=event_type, + created_at=created_at, + author=event.get("author", {}).get("name", "deleted"), + _event=event, + ) + + if event_type == "comment": + return DiscussionComment( + **common_args, + edited=event["data"]["edited"], + hidden=event["data"]["hidden"], + content=event["data"]["latest"]["raw"], + ) + if event_type == "status-change": + return DiscussionStatusChange( + **common_args, + new_status=event["data"]["status"], + ) + if event_type == "commit": + return DiscussionCommit( + **common_args, + summary=event["data"]["subject"], + oid=event["data"]["oid"], + ) + if event_type == "title-change": + return DiscussionTitleChange( + **common_args, + old_title=event["data"]["from"], + new_title=event["data"]["to"], + ) + + return DiscussionEvent(**common_args) diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/constants.py b/.venv/lib/python3.12/site-packages/huggingface_hub/constants.py new file mode 100644 index 0000000000000000000000000000000000000000..b30b2c01d99c5ee5428875f3711227024f5d0829 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/constants.py @@ -0,0 +1,294 @@ +import os +import re +import typing +from typing import Literal, Optional, Tuple + + +# Possible values for env variables + + +ENV_VARS_TRUE_VALUES = {"1", "ON", "YES", "TRUE"} +ENV_VARS_TRUE_AND_AUTO_VALUES = ENV_VARS_TRUE_VALUES.union({"AUTO"}) + + +def _is_true(value: Optional[str]) -> bool: + if value is None: + return False + return value.upper() in ENV_VARS_TRUE_VALUES + + +def _as_int(value: Optional[str]) -> Optional[int]: + if value is None: + return None + return int(value) + + +# Constants for file downloads + +PYTORCH_WEIGHTS_NAME = "pytorch_model.bin" +TF2_WEIGHTS_NAME = "tf_model.h5" +TF_WEIGHTS_NAME = "model.ckpt" +FLAX_WEIGHTS_NAME = "flax_model.msgpack" +CONFIG_NAME = "config.json" +REPOCARD_NAME = "README.md" +DEFAULT_ETAG_TIMEOUT = 10 +DEFAULT_DOWNLOAD_TIMEOUT = 10 +DEFAULT_REQUEST_TIMEOUT = 10 +DOWNLOAD_CHUNK_SIZE = 10 * 1024 * 1024 +HF_TRANSFER_CONCURRENCY = 100 +MAX_HTTP_DOWNLOAD_SIZE = 50 * 1000 * 1000 * 1000 # 50 GB + +# Constants for serialization + +PYTORCH_WEIGHTS_FILE_PATTERN = "pytorch_model{suffix}.bin" # Unsafe pickle: use safetensors instead +SAFETENSORS_WEIGHTS_FILE_PATTERN = "model{suffix}.safetensors" +TF2_WEIGHTS_FILE_PATTERN = "tf_model{suffix}.h5" + +# Constants for safetensors repos + +SAFETENSORS_SINGLE_FILE = "model.safetensors" +SAFETENSORS_INDEX_FILE = "model.safetensors.index.json" +SAFETENSORS_MAX_HEADER_LENGTH = 25_000_000 + +# Timeout of aquiring file lock and logging the attempt +FILELOCK_LOG_EVERY_SECONDS = 10 + +# Git-related constants + +DEFAULT_REVISION = "main" +REGEX_COMMIT_OID = re.compile(r"[A-Fa-f0-9]{5,40}") + +HUGGINGFACE_CO_URL_HOME = "https://huggingface.co/" + +_staging_mode = _is_true(os.environ.get("HUGGINGFACE_CO_STAGING")) + +_HF_DEFAULT_ENDPOINT = "https://huggingface.co" +_HF_DEFAULT_STAGING_ENDPOINT = "https://hub-ci.huggingface.co" +ENDPOINT = os.getenv("HF_ENDPOINT", _HF_DEFAULT_ENDPOINT).rstrip("/") +HUGGINGFACE_CO_URL_TEMPLATE = ENDPOINT + "/{repo_id}/resolve/{revision}/{filename}" + +if _staging_mode: + ENDPOINT = _HF_DEFAULT_STAGING_ENDPOINT + HUGGINGFACE_CO_URL_TEMPLATE = _HF_DEFAULT_STAGING_ENDPOINT + "/{repo_id}/resolve/{revision}/{filename}" + +HUGGINGFACE_HEADER_X_REPO_COMMIT = "X-Repo-Commit" +HUGGINGFACE_HEADER_X_LINKED_ETAG = "X-Linked-Etag" +HUGGINGFACE_HEADER_X_LINKED_SIZE = "X-Linked-Size" +HUGGINGFACE_HEADER_X_BILL_TO = "X-HF-Bill-To" + +INFERENCE_ENDPOINT = os.environ.get("HF_INFERENCE_ENDPOINT", "https://api-inference.huggingface.co") + +# See https://huggingface.co/docs/inference-endpoints/index +INFERENCE_ENDPOINTS_ENDPOINT = "https://api.endpoints.huggingface.cloud/v2" +INFERENCE_CATALOG_ENDPOINT = "https://endpoints.huggingface.co/api/catalog" + +# See https://api.endpoints.huggingface.cloud/#post-/v2/endpoint/-namespace- +INFERENCE_ENDPOINT_IMAGE_KEYS = [ + "custom", + "huggingface", + "huggingfaceNeuron", + "llamacpp", + "tei", + "tgi", + "tgiNeuron", +] + +# Proxy for third-party providers +INFERENCE_PROXY_TEMPLATE = "https://router.huggingface.co/{provider}" + +REPO_ID_SEPARATOR = "--" +# ^ this substring is not allowed in repo_ids on hf.co +# and is the canonical one we use for serialization of repo ids elsewhere. + + +REPO_TYPE_DATASET = "dataset" +REPO_TYPE_SPACE = "space" +REPO_TYPE_MODEL = "model" +REPO_TYPES = [None, REPO_TYPE_MODEL, REPO_TYPE_DATASET, REPO_TYPE_SPACE] +SPACES_SDK_TYPES = ["gradio", "streamlit", "docker", "static"] + +REPO_TYPES_URL_PREFIXES = { + REPO_TYPE_DATASET: "datasets/", + REPO_TYPE_SPACE: "spaces/", +} +REPO_TYPES_MAPPING = { + "datasets": REPO_TYPE_DATASET, + "spaces": REPO_TYPE_SPACE, + "models": REPO_TYPE_MODEL, +} + +DiscussionTypeFilter = Literal["all", "discussion", "pull_request"] +DISCUSSION_TYPES: Tuple[DiscussionTypeFilter, ...] = typing.get_args(DiscussionTypeFilter) +DiscussionStatusFilter = Literal["all", "open", "closed"] +DISCUSSION_STATUS: Tuple[DiscussionTypeFilter, ...] = typing.get_args(DiscussionStatusFilter) + +# Webhook subscription types +WEBHOOK_DOMAIN_T = Literal["repo", "discussions"] + +# default cache +default_home = os.path.join(os.path.expanduser("~"), ".cache") +HF_HOME = os.path.expandvars( + os.path.expanduser( + os.getenv( + "HF_HOME", + os.path.join(os.getenv("XDG_CACHE_HOME", default_home), "huggingface"), + ) + ) +) +hf_cache_home = HF_HOME # for backward compatibility. TODO: remove this in 1.0.0 + +default_cache_path = os.path.join(HF_HOME, "hub") +default_assets_cache_path = os.path.join(HF_HOME, "assets") + +# Legacy env variables +HUGGINGFACE_HUB_CACHE = os.getenv("HUGGINGFACE_HUB_CACHE", default_cache_path) +HUGGINGFACE_ASSETS_CACHE = os.getenv("HUGGINGFACE_ASSETS_CACHE", default_assets_cache_path) + +# New env variables +HF_HUB_CACHE = os.path.expandvars( + os.path.expanduser( + os.getenv( + "HF_HUB_CACHE", + HUGGINGFACE_HUB_CACHE, + ) + ) +) +HF_ASSETS_CACHE = os.path.expandvars( + os.path.expanduser( + os.getenv( + "HF_ASSETS_CACHE", + HUGGINGFACE_ASSETS_CACHE, + ) + ) +) + +HF_HUB_OFFLINE = _is_true(os.environ.get("HF_HUB_OFFLINE") or os.environ.get("TRANSFORMERS_OFFLINE")) + +# If set, log level will be set to DEBUG and all requests made to the Hub will be logged +# as curl commands for reproducibility. +HF_DEBUG = _is_true(os.environ.get("HF_DEBUG")) + +# Opt-out from telemetry requests +HF_HUB_DISABLE_TELEMETRY = ( + _is_true(os.environ.get("HF_HUB_DISABLE_TELEMETRY")) # HF-specific env variable + or _is_true(os.environ.get("DISABLE_TELEMETRY")) + or _is_true(os.environ.get("DO_NOT_TRACK")) # https://consoledonottrack.com/ +) + +HF_TOKEN_PATH = os.path.expandvars( + os.path.expanduser( + os.getenv( + "HF_TOKEN_PATH", + os.path.join(HF_HOME, "token"), + ) + ) +) +HF_STORED_TOKENS_PATH = os.path.join(os.path.dirname(HF_TOKEN_PATH), "stored_tokens") + +if _staging_mode: + # In staging mode, we use a different cache to ensure we don't mix up production and staging data or tokens + # In practice in `huggingface_hub` tests, we monkeypatch these values with temporary directories. The following + # lines are only used in third-party libraries tests (e.g. `transformers`, `diffusers`, etc.). + _staging_home = os.path.join(os.path.expanduser("~"), ".cache", "huggingface_staging") + HUGGINGFACE_HUB_CACHE = os.path.join(_staging_home, "hub") + HF_TOKEN_PATH = os.path.join(_staging_home, "token") + +# Here, `True` will disable progress bars globally without possibility of enabling it +# programmatically. `False` will enable them without possibility of disabling them. +# If environment variable is not set (None), then the user is free to enable/disable +# them programmatically. +# TL;DR: env variable has priority over code +__HF_HUB_DISABLE_PROGRESS_BARS = os.environ.get("HF_HUB_DISABLE_PROGRESS_BARS") +HF_HUB_DISABLE_PROGRESS_BARS: Optional[bool] = ( + _is_true(__HF_HUB_DISABLE_PROGRESS_BARS) if __HF_HUB_DISABLE_PROGRESS_BARS is not None else None +) + +# Disable warning on machines that do not support symlinks (e.g. Windows non-developer) +HF_HUB_DISABLE_SYMLINKS_WARNING: bool = _is_true(os.environ.get("HF_HUB_DISABLE_SYMLINKS_WARNING")) + +# Disable warning when using experimental features +HF_HUB_DISABLE_EXPERIMENTAL_WARNING: bool = _is_true(os.environ.get("HF_HUB_DISABLE_EXPERIMENTAL_WARNING")) + +# Disable sending the cached token by default is all HTTP requests to the Hub +HF_HUB_DISABLE_IMPLICIT_TOKEN: bool = _is_true(os.environ.get("HF_HUB_DISABLE_IMPLICIT_TOKEN")) + +# Enable fast-download using external dependency "hf_transfer" +# See: +# - https://pypi.org/project/hf-transfer/ +# - https://github.com/huggingface/hf_transfer (private) +HF_HUB_ENABLE_HF_TRANSFER: bool = _is_true(os.environ.get("HF_HUB_ENABLE_HF_TRANSFER")) + + +# UNUSED +# We don't use symlinks in local dir anymore. +HF_HUB_LOCAL_DIR_AUTO_SYMLINK_THRESHOLD: int = ( + _as_int(os.environ.get("HF_HUB_LOCAL_DIR_AUTO_SYMLINK_THRESHOLD")) or 5 * 1024 * 1024 +) + +# Used to override the etag timeout on a system level +HF_HUB_ETAG_TIMEOUT: int = _as_int(os.environ.get("HF_HUB_ETAG_TIMEOUT")) or DEFAULT_ETAG_TIMEOUT + +# Used to override the get request timeout on a system level +HF_HUB_DOWNLOAD_TIMEOUT: int = _as_int(os.environ.get("HF_HUB_DOWNLOAD_TIMEOUT")) or DEFAULT_DOWNLOAD_TIMEOUT + +# Allows to add information about the requester in the user-agent (eg. partner name) +HF_HUB_USER_AGENT_ORIGIN: Optional[str] = os.environ.get("HF_HUB_USER_AGENT_ORIGIN") + +# List frameworks that are handled by the InferenceAPI service. Useful to scan endpoints and check which models are +# deployed and running. Since 95% of the models are using the top 4 frameworks listed below, we scan only those by +# default. We still keep the full list of supported frameworks in case we want to scan all of them. +MAIN_INFERENCE_API_FRAMEWORKS = [ + "diffusers", + "sentence-transformers", + "text-generation-inference", + "transformers", +] + +ALL_INFERENCE_API_FRAMEWORKS = MAIN_INFERENCE_API_FRAMEWORKS + [ + "adapter-transformers", + "allennlp", + "asteroid", + "bertopic", + "doctr", + "espnet", + "fairseq", + "fastai", + "fasttext", + "flair", + "k2", + "keras", + "mindspore", + "nemo", + "open_clip", + "paddlenlp", + "peft", + "pyannote-audio", + "sklearn", + "spacy", + "span-marker", + "speechbrain", + "stanza", + "timm", +] + +# If OAuth didn't work after 2 redirects, there's likely a third-party cookie issue in the Space iframe view. +# In this case, we redirect the user to the non-iframe view. +OAUTH_MAX_REDIRECTS = 2 + +# OAuth-related environment variables injected by the Space +OAUTH_CLIENT_ID = os.environ.get("OAUTH_CLIENT_ID") +OAUTH_CLIENT_SECRET = os.environ.get("OAUTH_CLIENT_SECRET") +OAUTH_SCOPES = os.environ.get("OAUTH_SCOPES") +OPENID_PROVIDER_URL = os.environ.get("OPENID_PROVIDER_URL") + +# Xet constants +HUGGINGFACE_HEADER_X_XET_ENDPOINT = "X-Xet-Cas-Url" +HUGGINGFACE_HEADER_X_XET_ACCESS_TOKEN = "X-Xet-Access-Token" +HUGGINGFACE_HEADER_X_XET_EXPIRATION = "X-Xet-Token-Expiration" +HUGGINGFACE_HEADER_X_XET_HASH = "X-Xet-Hash" +HUGGINGFACE_HEADER_X_XET_REFRESH_ROUTE = "X-Xet-Refresh-Route" +HUGGINGFACE_HEADER_LINK_XET_AUTH_KEY = "xet-auth" + +default_xet_cache_path = os.path.join(HF_HOME, "xet") +HF_XET_CACHE = os.getenv("HF_XET_CACHE", default_xet_cache_path) +HF_HUB_DISABLE_XET: bool = _is_true(os.environ.get("HF_HUB_DISABLE_XET")) diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/dataclasses.py b/.venv/lib/python3.12/site-packages/huggingface_hub/dataclasses.py new file mode 100644 index 0000000000000000000000000000000000000000..636a0ac64b327448e6f8f56b10add54528071f29 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/dataclasses.py @@ -0,0 +1,484 @@ +import inspect +from dataclasses import _MISSING_TYPE, MISSING, Field, field, fields +from functools import wraps +from typing import ( + Any, + Callable, + Dict, + ForwardRef, + List, + Literal, + Optional, + Tuple, + Type, + TypeVar, + Union, + get_args, + get_origin, + overload, +) + +from .errors import ( + StrictDataclassClassValidationError, + StrictDataclassDefinitionError, + StrictDataclassFieldValidationError, +) + + +Validator_T = Callable[[Any], None] +T = TypeVar("T") + + +# The overload decorator helps type checkers understand the different return types +@overload +def strict(cls: Type[T]) -> Type[T]: ... + + +@overload +def strict(*, accept_kwargs: bool = False) -> Callable[[Type[T]], Type[T]]: ... + + +def strict( + cls: Optional[Type[T]] = None, *, accept_kwargs: bool = False +) -> Union[Type[T], Callable[[Type[T]], Type[T]]]: + """ + Decorator to add strict validation to a dataclass. + + This decorator must be used on top of `@dataclass` to ensure IDEs and static typing tools + recognize the class as a dataclass. + + Can be used with or without arguments: + - `@strict` + - `@strict(accept_kwargs=True)` + + Args: + cls: + The class to convert to a strict dataclass. + accept_kwargs (`bool`, *optional*): + If True, allows arbitrary keyword arguments in `__init__`. Defaults to False. + + Returns: + The enhanced dataclass with strict validation on field assignment. + + Example: + ```py + >>> from dataclasses import dataclass + >>> from huggingface_hub.dataclasses import as_validated_field, strict, validated_field + + >>> @as_validated_field + >>> def positive_int(value: int): + ... if not value >= 0: + ... raise ValueError(f"Value must be positive, got {value}") + + >>> @strict(accept_kwargs=True) + ... @dataclass + ... class User: + ... name: str + ... age: int = positive_int(default=10) + + # Initialize + >>> User(name="John") + User(name='John', age=10) + + # Extra kwargs are accepted + >>> User(name="John", age=30, lastname="Doe") + User(name='John', age=30, *lastname='Doe') + + # Invalid type => raises + >>> User(name="John", age="30") + huggingface_hub.errors.StrictDataclassFieldValidationError: Validation error for field 'age': + TypeError: Field 'age' expected int, got str (value: '30') + + # Invalid value => raises + >>> User(name="John", age=-1) + huggingface_hub.errors.StrictDataclassFieldValidationError: Validation error for field 'age': + ValueError: Value must be positive, got -1 + ``` + """ + + def wrap(cls: Type[T]) -> Type[T]: + if not hasattr(cls, "__dataclass_fields__"): + raise StrictDataclassDefinitionError( + f"Class '{cls.__name__}' must be a dataclass before applying @strict." + ) + + # List and store validators + field_validators: Dict[str, List[Validator_T]] = {} + for f in fields(cls): # type: ignore [arg-type] + validators = [] + validators.append(_create_type_validator(f)) + custom_validator = f.metadata.get("validator") + if custom_validator is not None: + if not isinstance(custom_validator, list): + custom_validator = [custom_validator] + for validator in custom_validator: + if not _is_validator(validator): + raise StrictDataclassDefinitionError( + f"Invalid validator for field '{f.name}': {validator}. Must be a callable taking a single argument." + ) + validators.extend(custom_validator) + field_validators[f.name] = validators + cls.__validators__ = field_validators # type: ignore + + # Override __setattr__ to validate fields on assignment + original_setattr = cls.__setattr__ + + def __strict_setattr__(self: Any, name: str, value: Any) -> None: + """Custom __setattr__ method for strict dataclasses.""" + # Run all validators + for validator in self.__validators__.get(name, []): + try: + validator(value) + except (ValueError, TypeError) as e: + raise StrictDataclassFieldValidationError(field=name, cause=e) from e + + # If validation passed, set the attribute + original_setattr(self, name, value) + + cls.__setattr__ = __strict_setattr__ # type: ignore[method-assign] + + if accept_kwargs: + # (optional) Override __init__ to accept arbitrary keyword arguments + original_init = cls.__init__ + + @wraps(original_init) + def __init__(self, **kwargs: Any) -> None: + # Extract only the fields that are part of the dataclass + dataclass_fields = {f.name for f in fields(cls)} # type: ignore [arg-type] + standard_kwargs = {k: v for k, v in kwargs.items() if k in dataclass_fields} + + # Call the original __init__ with standard fields + original_init(self, **standard_kwargs) + + # Add any additional kwargs as attributes + for name, value in kwargs.items(): + if name not in dataclass_fields: + self.__setattr__(name, value) + + cls.__init__ = __init__ # type: ignore[method-assign] + + # (optional) Override __repr__ to include additional kwargs + original_repr = cls.__repr__ + + @wraps(original_repr) + def __repr__(self) -> str: + # Call the original __repr__ to get the standard fields + standard_repr = original_repr(self) + + # Get additional kwargs + additional_kwargs = [ + # add a '*' in front of additional kwargs to let the user know they are not part of the dataclass + f"*{k}={v!r}" + for k, v in self.__dict__.items() + if k not in cls.__dataclass_fields__ # type: ignore [attr-defined] + ] + additional_repr = ", ".join(additional_kwargs) + + # Combine both representations + return f"{standard_repr[:-1]}, {additional_repr})" if additional_kwargs else standard_repr + + cls.__repr__ = __repr__ # type: ignore [method-assign] + + # List all public methods starting with `validate_` => class validators. + class_validators = [] + + for name in dir(cls): + if not name.startswith("validate_"): + continue + method = getattr(cls, name) + if not callable(method): + continue + if len(inspect.signature(method).parameters) != 1: + raise StrictDataclassDefinitionError( + f"Class '{cls.__name__}' has a class validator '{name}' that takes more than one argument." + " Class validators must take only 'self' as an argument. Methods starting with 'validate_'" + " are considered to be class validators." + ) + class_validators.append(method) + + cls.__class_validators__ = class_validators # type: ignore [attr-defined] + + # Add `validate` method to the class, but first check if it already exists + def validate(self: T) -> None: + """Run class validators on the instance.""" + for validator in cls.__class_validators__: # type: ignore [attr-defined] + try: + validator(self) + except (ValueError, TypeError) as e: + raise StrictDataclassClassValidationError(validator=validator.__name__, cause=e) from e + + # Hack to be able to raise if `.validate()` already exists except if it was created by this decorator on a parent class + # (in which case we just override it) + validate.__is_defined_by_strict_decorator__ = True # type: ignore [attr-defined] + + if hasattr(cls, "validate"): + if not getattr(cls.validate, "__is_defined_by_strict_decorator__", False): # type: ignore [attr-defined] + raise StrictDataclassDefinitionError( + f"Class '{cls.__name__}' already implements a method called 'validate'." + " This method name is reserved when using the @strict decorator on a dataclass." + " If you want to keep your own method, please rename it." + ) + + cls.validate = validate # type: ignore + + # Run class validators after initialization + initial_init = cls.__init__ + + @wraps(initial_init) + def init_with_validate(self, *args, **kwargs) -> None: + """Run class validators after initialization.""" + initial_init(self, *args, **kwargs) # type: ignore [call-arg] + cls.validate(self) # type: ignore [attr-defined] + + setattr(cls, "__init__", init_with_validate) + + return cls + + # Return wrapped class or the decorator itself + return wrap(cls) if cls is not None else wrap + + +def validated_field( + validator: Union[List[Validator_T], Validator_T], + default: Union[Any, _MISSING_TYPE] = MISSING, + default_factory: Union[Callable[[], Any], _MISSING_TYPE] = MISSING, + init: bool = True, + repr: bool = True, + hash: Optional[bool] = None, + compare: bool = True, + metadata: Optional[Dict] = None, + **kwargs: Any, +) -> Any: + """ + Create a dataclass field with a custom validator. + + Useful to apply several checks to a field. If only applying one rule, check out the [`as_validated_field`] decorator. + + Args: + validator (`Callable` or `List[Callable]`): + A method that takes a value as input and raises ValueError/TypeError if the value is invalid. + Can be a list of validators to apply multiple checks. + **kwargs: + Additional arguments to pass to `dataclasses.field()`. + + Returns: + A field with the validator attached in metadata + """ + if not isinstance(validator, list): + validator = [validator] + if metadata is None: + metadata = {} + metadata["validator"] = validator + return field( # type: ignore + default=default, # type: ignore [arg-type] + default_factory=default_factory, # type: ignore [arg-type] + init=init, + repr=repr, + hash=hash, + compare=compare, + metadata=metadata, + **kwargs, + ) + + +def as_validated_field(validator: Validator_T): + """ + Decorates a validator function as a [`validated_field`] (i.e. a dataclass field with a custom validator). + + Args: + validator (`Callable`): + A method that takes a value as input and raises ValueError/TypeError if the value is invalid. + """ + + def _inner( + default: Union[Any, _MISSING_TYPE] = MISSING, + default_factory: Union[Callable[[], Any], _MISSING_TYPE] = MISSING, + init: bool = True, + repr: bool = True, + hash: Optional[bool] = None, + compare: bool = True, + metadata: Optional[Dict] = None, + **kwargs: Any, + ): + return validated_field( + validator, + default=default, + default_factory=default_factory, + init=init, + repr=repr, + hash=hash, + compare=compare, + metadata=metadata, + **kwargs, + ) + + return _inner + + +def type_validator(name: str, value: Any, expected_type: Any) -> None: + """Validate that 'value' matches 'expected_type'.""" + origin = get_origin(expected_type) + args = get_args(expected_type) + + if expected_type is Any: + return + elif validator := _BASIC_TYPE_VALIDATORS.get(origin): + validator(name, value, args) + elif isinstance(expected_type, type): # simple types + _validate_simple_type(name, value, expected_type) + elif isinstance(expected_type, ForwardRef) or isinstance(expected_type, str): + return + else: + raise TypeError(f"Unsupported type for field '{name}': {expected_type}") + + +def _validate_union(name: str, value: Any, args: Tuple[Any, ...]) -> None: + """Validate that value matches one of the types in a Union.""" + errors = [] + for t in args: + try: + type_validator(name, value, t) + return # Valid if any type matches + except TypeError as e: + errors.append(str(e)) + + raise TypeError( + f"Field '{name}' with value {repr(value)} doesn't match any type in {args}. Errors: {'; '.join(errors)}" + ) + + +def _validate_literal(name: str, value: Any, args: Tuple[Any, ...]) -> None: + """Validate Literal type.""" + if value not in args: + raise TypeError(f"Field '{name}' expected one of {args}, got {value}") + + +def _validate_list(name: str, value: Any, args: Tuple[Any, ...]) -> None: + """Validate List[T] type.""" + if not isinstance(value, list): + raise TypeError(f"Field '{name}' expected a list, got {type(value).__name__}") + + # Validate each item in the list + item_type = args[0] + for i, item in enumerate(value): + try: + type_validator(f"{name}[{i}]", item, item_type) + except TypeError as e: + raise TypeError(f"Invalid item at index {i} in list '{name}'") from e + + +def _validate_dict(name: str, value: Any, args: Tuple[Any, ...]) -> None: + """Validate Dict[K, V] type.""" + if not isinstance(value, dict): + raise TypeError(f"Field '{name}' expected a dict, got {type(value).__name__}") + + # Validate keys and values + key_type, value_type = args + for k, v in value.items(): + try: + type_validator(f"{name}.key", k, key_type) + type_validator(f"{name}[{k!r}]", v, value_type) + except TypeError as e: + raise TypeError(f"Invalid key or value in dict '{name}'") from e + + +def _validate_tuple(name: str, value: Any, args: Tuple[Any, ...]) -> None: + """Validate Tuple type.""" + if not isinstance(value, tuple): + raise TypeError(f"Field '{name}' expected a tuple, got {type(value).__name__}") + + # Handle variable-length tuples: Tuple[T, ...] + if len(args) == 2 and args[1] is Ellipsis: + for i, item in enumerate(value): + try: + type_validator(f"{name}[{i}]", item, args[0]) + except TypeError as e: + raise TypeError(f"Invalid item at index {i} in tuple '{name}'") from e + # Handle fixed-length tuples: Tuple[T1, T2, ...] + elif len(args) != len(value): + raise TypeError(f"Field '{name}' expected a tuple of length {len(args)}, got {len(value)}") + else: + for i, (item, expected) in enumerate(zip(value, args)): + try: + type_validator(f"{name}[{i}]", item, expected) + except TypeError as e: + raise TypeError(f"Invalid item at index {i} in tuple '{name}'") from e + + +def _validate_set(name: str, value: Any, args: Tuple[Any, ...]) -> None: + """Validate Set[T] type.""" + if not isinstance(value, set): + raise TypeError(f"Field '{name}' expected a set, got {type(value).__name__}") + + # Validate each item in the set + item_type = args[0] + for i, item in enumerate(value): + try: + type_validator(f"{name} item", item, item_type) + except TypeError as e: + raise TypeError(f"Invalid item in set '{name}'") from e + + +def _validate_simple_type(name: str, value: Any, expected_type: type) -> None: + """Validate simple type (int, str, etc.).""" + if not isinstance(value, expected_type): + raise TypeError( + f"Field '{name}' expected {expected_type.__name__}, got {type(value).__name__} (value: {repr(value)})" + ) + + +def _create_type_validator(field: Field) -> Validator_T: + """Create a type validator function for a field.""" + # Hacky: we cannot use a lambda here because of reference issues + + def validator(value: Any) -> None: + type_validator(field.name, value, field.type) + + return validator + + +def _is_validator(validator: Any) -> bool: + """Check if a function is a validator. + + A validator is a Callable that can be called with a single positional argument. + The validator can have more arguments with default values. + + Basically, returns True if `validator(value)` is possible. + """ + if not callable(validator): + return False + + signature = inspect.signature(validator) + parameters = list(signature.parameters.values()) + if len(parameters) == 0: + return False + if parameters[0].kind not in ( + inspect.Parameter.POSITIONAL_OR_KEYWORD, + inspect.Parameter.POSITIONAL_ONLY, + inspect.Parameter.VAR_POSITIONAL, + ): + return False + for parameter in parameters[1:]: + if parameter.default == inspect.Parameter.empty: + return False + return True + + +_BASIC_TYPE_VALIDATORS = { + Union: _validate_union, + Literal: _validate_literal, + list: _validate_list, + dict: _validate_dict, + tuple: _validate_tuple, + set: _validate_set, +} + + +__all__ = [ + "strict", + "validated_field", + "Validator_T", + "StrictDataclassClassValidationError", + "StrictDataclassDefinitionError", + "StrictDataclassFieldValidationError", +] diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/errors.py b/.venv/lib/python3.12/site-packages/huggingface_hub/errors.py new file mode 100644 index 0000000000000000000000000000000000000000..a0f7ed80e35a7cbe1dcc0f21dfa0354e467676f3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/errors.py @@ -0,0 +1,377 @@ +"""Contains all custom errors.""" + +from pathlib import Path +from typing import Optional, Union + +from requests import HTTPError, Response + + +# CACHE ERRORS + + +class CacheNotFound(Exception): + """Exception thrown when the Huggingface cache is not found.""" + + cache_dir: Union[str, Path] + + def __init__(self, msg: str, cache_dir: Union[str, Path], *args, **kwargs): + super().__init__(msg, *args, **kwargs) + self.cache_dir = cache_dir + + +class CorruptedCacheException(Exception): + """Exception for any unexpected structure in the Huggingface cache-system.""" + + +# HEADERS ERRORS + + +class LocalTokenNotFoundError(EnvironmentError): + """Raised if local token is required but not found.""" + + +# HTTP ERRORS + + +class OfflineModeIsEnabled(ConnectionError): + """Raised when a request is made but `HF_HUB_OFFLINE=1` is set as environment variable.""" + + +class HfHubHTTPError(HTTPError): + """ + HTTPError to inherit from for any custom HTTP Error raised in HF Hub. + + Any HTTPError is converted at least into a `HfHubHTTPError`. If some information is + sent back by the server, it will be added to the error message. + + Added details: + - Request id from "X-Request-Id" header if exists. If not, fallback to "X-Amzn-Trace-Id" header if exists. + - Server error message from the header "X-Error-Message". + - Server error message if we can found one in the response body. + + Example: + ```py + import requests + from huggingface_hub.utils import get_session, hf_raise_for_status, HfHubHTTPError + + response = get_session().post(...) + try: + hf_raise_for_status(response) + except HfHubHTTPError as e: + print(str(e)) # formatted message + e.request_id, e.server_message # details returned by server + + # Complete the error message with additional information once it's raised + e.append_to_message("\n`create_commit` expects the repository to exist.") + raise + ``` + """ + + def __init__(self, message: str, response: Optional[Response] = None, *, server_message: Optional[str] = None): + self.request_id = ( + response.headers.get("x-request-id") or response.headers.get("X-Amzn-Trace-Id") + if response is not None + else None + ) + self.server_message = server_message + + super().__init__( + message, + response=response, # type: ignore [arg-type] + request=response.request if response is not None else None, # type: ignore [arg-type] + ) + + def append_to_message(self, additional_message: str) -> None: + """Append additional information to the `HfHubHTTPError` initial message.""" + self.args = (self.args[0] + additional_message,) + self.args[1:] + + +# INFERENCE CLIENT ERRORS + + +class InferenceTimeoutError(HTTPError, TimeoutError): + """Error raised when a model is unavailable or the request times out.""" + + +# INFERENCE ENDPOINT ERRORS + + +class InferenceEndpointError(Exception): + """Generic exception when dealing with Inference Endpoints.""" + + +class InferenceEndpointTimeoutError(InferenceEndpointError, TimeoutError): + """Exception for timeouts while waiting for Inference Endpoint.""" + + +# SAFETENSORS ERRORS + + +class SafetensorsParsingError(Exception): + """Raised when failing to parse a safetensors file metadata. + + This can be the case if the file is not a safetensors file or does not respect the specification. + """ + + +class NotASafetensorsRepoError(Exception): + """Raised when a repo is not a Safetensors repo i.e. doesn't have either a `model.safetensors` or a + `model.safetensors.index.json` file. + """ + + +# TEXT GENERATION ERRORS + + +class TextGenerationError(HTTPError): + """Generic error raised if text-generation went wrong.""" + + +# Text Generation Inference Errors +class ValidationError(TextGenerationError): + """Server-side validation error.""" + + +class GenerationError(TextGenerationError): + pass + + +class OverloadedError(TextGenerationError): + pass + + +class IncompleteGenerationError(TextGenerationError): + pass + + +class UnknownError(TextGenerationError): + pass + + +# VALIDATION ERRORS + + +class HFValidationError(ValueError): + """Generic exception thrown by `huggingface_hub` validators. + + Inherits from [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError). + """ + + +# FILE METADATA ERRORS + + +class FileMetadataError(OSError): + """Error triggered when the metadata of a file on the Hub cannot be retrieved (missing ETag or commit_hash). + + Inherits from `OSError` for backward compatibility. + """ + + +# REPOSITORY ERRORS + + +class RepositoryNotFoundError(HfHubHTTPError): + """ + Raised when trying to access a hf.co URL with an invalid repository name, or + with a private repo name the user does not have access to. + + Example: + + ```py + >>> from huggingface_hub import model_info + >>> model_info("") + (...) + huggingface_hub.utils._errors.RepositoryNotFoundError: 401 Client Error. (Request ID: PvMw_VjBMjVdMz53WKIzP) + + Repository Not Found for url: https://huggingface.co/api/models/%3Cnon_existent_repository%3E. + Please make sure you specified the correct `repo_id` and `repo_type`. + If the repo is private, make sure you are authenticated. + Invalid username or password. + ``` + """ + + +class GatedRepoError(RepositoryNotFoundError): + """ + Raised when trying to access a gated repository for which the user is not on the + authorized list. + + Note: derives from `RepositoryNotFoundError` to ensure backward compatibility. + + Example: + + ```py + >>> from huggingface_hub import model_info + >>> model_info("") + (...) + huggingface_hub.utils._errors.GatedRepoError: 403 Client Error. (Request ID: ViT1Bf7O_026LGSQuVqfa) + + Cannot access gated repo for url https://huggingface.co/api/models/ardent-figment/gated-model. + Access to model ardent-figment/gated-model is restricted and you are not in the authorized list. + Visit https://huggingface.co/ardent-figment/gated-model to ask for access. + ``` + """ + + +class DisabledRepoError(HfHubHTTPError): + """ + Raised when trying to access a repository that has been disabled by its author. + + Example: + + ```py + >>> from huggingface_hub import dataset_info + >>> dataset_info("laion/laion-art") + (...) + huggingface_hub.utils._errors.DisabledRepoError: 403 Client Error. (Request ID: Root=1-659fc3fa-3031673e0f92c71a2260dbe2;bc6f4dfb-b30a-4862-af0a-5cfe827610d8) + + Cannot access repository for url https://huggingface.co/api/datasets/laion/laion-art. + Access to this resource is disabled. + ``` + """ + + +# REVISION ERROR + + +class RevisionNotFoundError(HfHubHTTPError): + """ + Raised when trying to access a hf.co URL with a valid repository but an invalid + revision. + + Example: + + ```py + >>> from huggingface_hub import hf_hub_download + >>> hf_hub_download('bert-base-cased', 'config.json', revision='') + (...) + huggingface_hub.utils._errors.RevisionNotFoundError: 404 Client Error. (Request ID: Mwhe_c3Kt650GcdKEFomX) + + Revision Not Found for url: https://huggingface.co/bert-base-cased/resolve/%3Cnon-existent-revision%3E/config.json. + ``` + """ + + +# ENTRY ERRORS +class EntryNotFoundError(HfHubHTTPError): + """ + Raised when trying to access a hf.co URL with a valid repository and revision + but an invalid filename. + + Example: + + ```py + >>> from huggingface_hub import hf_hub_download + >>> hf_hub_download('bert-base-cased', '') + (...) + huggingface_hub.utils._errors.EntryNotFoundError: 404 Client Error. (Request ID: 53pNl6M0MxsnG5Sw8JA6x) + + Entry Not Found for url: https://huggingface.co/bert-base-cased/resolve/main/%3Cnon-existent-file%3E. + ``` + """ + + +class LocalEntryNotFoundError(EntryNotFoundError, FileNotFoundError, ValueError): + """ + Raised when trying to access a file or snapshot that is not on the disk when network is + disabled or unavailable (connection issue). The entry may exist on the Hub. + + Note: `ValueError` type is to ensure backward compatibility. + Note: `LocalEntryNotFoundError` derives from `HTTPError` because of `EntryNotFoundError` + even when it is not a network issue. + + Example: + + ```py + >>> from huggingface_hub import hf_hub_download + >>> hf_hub_download('bert-base-cased', '', local_files_only=True) + (...) + huggingface_hub.utils._errors.LocalEntryNotFoundError: Cannot find the requested files in the disk cache and outgoing traffic has been disabled. To enable hf.co look-ups and downloads online, set 'local_files_only' to False. + ``` + """ + + def __init__(self, message: str): + super().__init__(message, response=None) + + +# REQUEST ERROR +class BadRequestError(HfHubHTTPError, ValueError): + """ + Raised by `hf_raise_for_status` when the server returns a HTTP 400 error. + + Example: + + ```py + >>> resp = requests.post("hf.co/api/check", ...) + >>> hf_raise_for_status(resp, endpoint_name="check") + huggingface_hub.utils._errors.BadRequestError: Bad request for check endpoint: {details} (Request ID: XXX) + ``` + """ + + +# DDUF file format ERROR + + +class DDUFError(Exception): + """Base exception for errors related to the DDUF format.""" + + +class DDUFCorruptedFileError(DDUFError): + """Exception thrown when the DDUF file is corrupted.""" + + +class DDUFExportError(DDUFError): + """Base exception for errors during DDUF export.""" + + +class DDUFInvalidEntryNameError(DDUFExportError): + """Exception thrown when the entry name is invalid.""" + + +# STRICT DATACLASSES ERRORS + + +class StrictDataclassError(Exception): + """Base exception for strict dataclasses.""" + + +class StrictDataclassDefinitionError(StrictDataclassError): + """Exception thrown when a strict dataclass is defined incorrectly.""" + + +class StrictDataclassFieldValidationError(StrictDataclassError): + """Exception thrown when a strict dataclass fails validation for a given field.""" + + def __init__(self, field: str, cause: Exception): + error_message = f"Validation error for field '{field}':" + error_message += f"\n {cause.__class__.__name__}: {cause}" + super().__init__(error_message) + + +class StrictDataclassClassValidationError(StrictDataclassError): + """Exception thrown when a strict dataclass fails validation on a class validator.""" + + def __init__(self, validator: str, cause: Exception): + error_message = f"Class validation error for validator '{validator}':" + error_message += f"\n {cause.__class__.__name__}: {cause}" + super().__init__(error_message) + + +# XET ERRORS + + +class XetError(Exception): + """Base exception for errors related to Xet Storage.""" + + +class XetAuthorizationError(XetError): + """Exception thrown when the user does not have the right authorization to use Xet Storage.""" + + +class XetRefreshTokenError(XetError): + """Exception thrown when the refresh token is invalid.""" + + +class XetDownloadError(Exception): + """Exception thrown when the download from Xet Storage fails.""" diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/fastai_utils.py b/.venv/lib/python3.12/site-packages/huggingface_hub/fastai_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..e75eba2a8baee7bdeb8d36a1c06bd950cf857c44 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/fastai_utils.py @@ -0,0 +1,425 @@ +import json +import os +from pathlib import Path +from pickle import DEFAULT_PROTOCOL, PicklingError +from typing import Any, Dict, List, Optional, Union + +from packaging import version + +from huggingface_hub import constants, snapshot_download +from huggingface_hub.hf_api import HfApi +from huggingface_hub.utils import ( + SoftTemporaryDirectory, + get_fastai_version, + get_fastcore_version, + get_python_version, +) + +from .utils import logging, validate_hf_hub_args +from .utils._runtime import _PY_VERSION # noqa: F401 # for backward compatibility... + + +logger = logging.get_logger(__name__) + + +def _check_fastai_fastcore_versions( + fastai_min_version: str = "2.4", + fastcore_min_version: str = "1.3.27", +): + """ + Checks that the installed fastai and fastcore versions are compatible for pickle serialization. + + Args: + fastai_min_version (`str`, *optional*): + The minimum fastai version supported. + fastcore_min_version (`str`, *optional*): + The minimum fastcore version supported. + + + Raises the following error: + + - [`ImportError`](https://docs.python.org/3/library/exceptions.html#ImportError) + if the fastai or fastcore libraries are not available or are of an invalid version. + + + """ + + if (get_fastcore_version() or get_fastai_version()) == "N/A": + raise ImportError( + f"fastai>={fastai_min_version} and fastcore>={fastcore_min_version} are" + f" required. Currently using fastai=={get_fastai_version()} and" + f" fastcore=={get_fastcore_version()}." + ) + + current_fastai_version = version.Version(get_fastai_version()) + current_fastcore_version = version.Version(get_fastcore_version()) + + if current_fastai_version < version.Version(fastai_min_version): + raise ImportError( + "`push_to_hub_fastai` and `from_pretrained_fastai` require a" + f" fastai>={fastai_min_version} version, but you are using fastai version" + f" {get_fastai_version()} which is incompatible. Upgrade with `pip install" + " fastai==2.5.6`." + ) + + if current_fastcore_version < version.Version(fastcore_min_version): + raise ImportError( + "`push_to_hub_fastai` and `from_pretrained_fastai` require a" + f" fastcore>={fastcore_min_version} version, but you are using fastcore" + f" version {get_fastcore_version()} which is incompatible. Upgrade with" + " `pip install fastcore==1.3.27`." + ) + + +def _check_fastai_fastcore_pyproject_versions( + storage_folder: str, + fastai_min_version: str = "2.4", + fastcore_min_version: str = "1.3.27", +): + """ + Checks that the `pyproject.toml` file in the directory `storage_folder` has fastai and fastcore versions + that are compatible with `from_pretrained_fastai` and `push_to_hub_fastai`. If `pyproject.toml` does not exist + or does not contain versions for fastai and fastcore, then it logs a warning. + + Args: + storage_folder (`str`): + Folder to look for the `pyproject.toml` file. + fastai_min_version (`str`, *optional*): + The minimum fastai version supported. + fastcore_min_version (`str`, *optional*): + The minimum fastcore version supported. + + + Raises the following errors: + + - [`ImportError`](https://docs.python.org/3/library/exceptions.html#ImportError) + if the `toml` module is not installed. + - [`ImportError`](https://docs.python.org/3/library/exceptions.html#ImportError) + if the `pyproject.toml` indicates a lower than minimum supported version of fastai or fastcore. + + + """ + + try: + import toml + except ModuleNotFoundError: + raise ImportError( + "`push_to_hub_fastai` and `from_pretrained_fastai` require the toml module." + " Install it with `pip install toml`." + ) + + # Checks that a `pyproject.toml`, with `build-system` and `requires` sections, exists in the repository. If so, get a list of required packages. + if not os.path.isfile(f"{storage_folder}/pyproject.toml"): + logger.warning( + "There is no `pyproject.toml` in the repository that contains the fastai" + " `Learner`. The `pyproject.toml` would allow us to verify that your fastai" + " and fastcore versions are compatible with those of the model you want to" + " load." + ) + return + pyproject_toml = toml.load(f"{storage_folder}/pyproject.toml") + + if "build-system" not in pyproject_toml.keys(): + logger.warning( + "There is no `build-system` section in the pyproject.toml of the repository" + " that contains the fastai `Learner`. The `build-system` would allow us to" + " verify that your fastai and fastcore versions are compatible with those" + " of the model you want to load." + ) + return + build_system_toml = pyproject_toml["build-system"] + + if "requires" not in build_system_toml.keys(): + logger.warning( + "There is no `requires` section in the pyproject.toml of the repository" + " that contains the fastai `Learner`. The `requires` would allow us to" + " verify that your fastai and fastcore versions are compatible with those" + " of the model you want to load." + ) + return + package_versions = build_system_toml["requires"] + + # Extracts contains fastai and fastcore versions from `pyproject.toml` if available. + # If the package is specified but not the version (e.g. "fastai" instead of "fastai=2.4"), the default versions are the highest. + fastai_packages = [pck for pck in package_versions if pck.startswith("fastai")] + if len(fastai_packages) == 0: + logger.warning("The repository does not have a fastai version specified in the `pyproject.toml`.") + # fastai_version is an empty string if not specified + else: + fastai_version = str(fastai_packages[0]).partition("=")[2] + if fastai_version != "" and version.Version(fastai_version) < version.Version(fastai_min_version): + raise ImportError( + "`from_pretrained_fastai` requires" + f" fastai>={fastai_min_version} version but the model to load uses" + f" {fastai_version} which is incompatible." + ) + + fastcore_packages = [pck for pck in package_versions if pck.startswith("fastcore")] + if len(fastcore_packages) == 0: + logger.warning("The repository does not have a fastcore version specified in the `pyproject.toml`.") + # fastcore_version is an empty string if not specified + else: + fastcore_version = str(fastcore_packages[0]).partition("=")[2] + if fastcore_version != "" and version.Version(fastcore_version) < version.Version(fastcore_min_version): + raise ImportError( + "`from_pretrained_fastai` requires" + f" fastcore>={fastcore_min_version} version, but you are using fastcore" + f" version {fastcore_version} which is incompatible." + ) + + +README_TEMPLATE = """--- +tags: +- fastai +--- + +# Amazing! + +🥳 Congratulations on hosting your fastai model on the Hugging Face Hub! + +# Some next steps +1. Fill out this model card with more information (see the template below and the [documentation here](https://huggingface.co/docs/hub/model-repos))! + +2. Create a demo in Gradio or Streamlit using 🤗 Spaces ([documentation here](https://huggingface.co/docs/hub/spaces)). + +3. Join the fastai community on the [Fastai Discord](https://discord.com/invite/YKrxeNn)! + +Greetings fellow fastlearner 🤝! Don't forget to delete this content from your model card. + + +--- + + +# Model card + +## Model description +More information needed + +## Intended uses & limitations +More information needed + +## Training and evaluation data +More information needed +""" + +PYPROJECT_TEMPLATE = f"""[build-system] +requires = ["setuptools>=40.8.0", "wheel", "python={get_python_version()}", "fastai={get_fastai_version()}", "fastcore={get_fastcore_version()}"] +build-backend = "setuptools.build_meta:__legacy__" +""" + + +def _create_model_card(repo_dir: Path): + """ + Creates a model card for the repository. + + Args: + repo_dir (`Path`): + Directory where model card is created. + """ + readme_path = repo_dir / "README.md" + + if not readme_path.exists(): + with readme_path.open("w", encoding="utf-8") as f: + f.write(README_TEMPLATE) + + +def _create_model_pyproject(repo_dir: Path): + """ + Creates a `pyproject.toml` for the repository. + + Args: + repo_dir (`Path`): + Directory where `pyproject.toml` is created. + """ + pyproject_path = repo_dir / "pyproject.toml" + + if not pyproject_path.exists(): + with pyproject_path.open("w", encoding="utf-8") as f: + f.write(PYPROJECT_TEMPLATE) + + +def _save_pretrained_fastai( + learner, + save_directory: Union[str, Path], + config: Optional[Dict[str, Any]] = None, +): + """ + Saves a fastai learner to `save_directory` in pickle format using the default pickle protocol for the version of python used. + + Args: + learner (`Learner`): + The `fastai.Learner` you'd like to save. + save_directory (`str` or `Path`): + Specific directory in which you want to save the fastai learner. + config (`dict`, *optional*): + Configuration object. Will be uploaded as a .json file. Example: 'https://huggingface.co/espejelomar/fastai-pet-breeds-classification/blob/main/config.json'. + + + + Raises the following error: + + - [`RuntimeError`](https://docs.python.org/3/library/exceptions.html#RuntimeError) + if the config file provided is not a dictionary. + + + """ + _check_fastai_fastcore_versions() + + os.makedirs(save_directory, exist_ok=True) + + # if the user provides config then we update it with the fastai and fastcore versions in CONFIG_TEMPLATE. + if config is not None: + if not isinstance(config, dict): + raise RuntimeError(f"Provided config should be a dict. Got: '{type(config)}'") + path = os.path.join(save_directory, constants.CONFIG_NAME) + with open(path, "w") as f: + json.dump(config, f) + + _create_model_card(Path(save_directory)) + _create_model_pyproject(Path(save_directory)) + + # learner.export saves the model in `self.path`. + learner.path = Path(save_directory) + os.makedirs(save_directory, exist_ok=True) + try: + learner.export( + fname="model.pkl", + pickle_protocol=DEFAULT_PROTOCOL, + ) + except PicklingError: + raise PicklingError( + "You are using a lambda function, i.e., an anonymous function. `pickle`" + " cannot pickle function objects and requires that all functions have" + " names. One possible solution is to name the function." + ) + + +@validate_hf_hub_args +def from_pretrained_fastai( + repo_id: str, + revision: Optional[str] = None, +): + """ + Load pretrained fastai model from the Hub or from a local directory. + + Args: + repo_id (`str`): + The location where the pickled fastai.Learner is. It can be either of the two: + - Hosted on the Hugging Face Hub. E.g.: 'espejelomar/fatai-pet-breeds-classification' or 'distilgpt2'. + You can add a `revision` by appending `@` at the end of `repo_id`. E.g.: `dbmdz/bert-base-german-cased@main`. + Revision is the specific model version to use. Since we use a git-based system for storing models and other + artifacts on the Hugging Face Hub, it can be a branch name, a tag name, or a commit id. + - Hosted locally. `repo_id` would be a directory containing the pickle and a pyproject.toml + indicating the fastai and fastcore versions used to build the `fastai.Learner`. E.g.: `./my_model_directory/`. + revision (`str`, *optional*): + Revision at which the repo's files are downloaded. See documentation of `snapshot_download`. + + Returns: + The `fastai.Learner` model in the `repo_id` repo. + """ + _check_fastai_fastcore_versions() + + # Load the `repo_id` repo. + # `snapshot_download` returns the folder where the model was stored. + # `cache_dir` will be the default '/root/.cache/huggingface/hub' + if not os.path.isdir(repo_id): + storage_folder = snapshot_download( + repo_id=repo_id, + revision=revision, + library_name="fastai", + library_version=get_fastai_version(), + ) + else: + storage_folder = repo_id + + _check_fastai_fastcore_pyproject_versions(storage_folder) + + from fastai.learner import load_learner # type: ignore + + return load_learner(os.path.join(storage_folder, "model.pkl")) + + +@validate_hf_hub_args +def push_to_hub_fastai( + learner, + *, + repo_id: str, + commit_message: str = "Push FastAI model using huggingface_hub.", + private: Optional[bool] = None, + token: Optional[str] = None, + config: Optional[dict] = None, + branch: Optional[str] = None, + create_pr: Optional[bool] = None, + allow_patterns: Optional[Union[List[str], str]] = None, + ignore_patterns: Optional[Union[List[str], str]] = None, + delete_patterns: Optional[Union[List[str], str]] = None, + api_endpoint: Optional[str] = None, +): + """ + Upload learner checkpoint files to the Hub. + + Use `allow_patterns` and `ignore_patterns` to precisely filter which files should be pushed to the hub. Use + `delete_patterns` to delete existing remote files in the same commit. See [`upload_folder`] reference for more + details. + + Args: + learner (`Learner`): + The `fastai.Learner' you'd like to push to the Hub. + repo_id (`str`): + The repository id for your model in Hub in the format of "namespace/repo_name". The namespace can be your individual account or an organization to which you have write access (for example, 'stanfordnlp/stanza-de'). + commit_message (`str`, *optional*): + Message to commit while pushing. Will default to :obj:`"add model"`. + private (`bool`, *optional*): + Whether or not the repository created should be private. + If `None` (default), will default to been public except if the organization's default is private. + token (`str`, *optional*): + The Hugging Face account token to use as HTTP bearer authorization for remote files. If :obj:`None`, the token will be asked by a prompt. + config (`dict`, *optional*): + Configuration object to be saved alongside the model weights. + branch (`str`, *optional*): + The git branch on which to push the model. This defaults to + the default branch as specified in your repository, which + defaults to `"main"`. + create_pr (`boolean`, *optional*): + Whether or not to create a Pull Request from `branch` with that commit. + Defaults to `False`. + api_endpoint (`str`, *optional*): + The API endpoint to use when pushing the model to the hub. + allow_patterns (`List[str]` or `str`, *optional*): + If provided, only files matching at least one pattern are pushed. + ignore_patterns (`List[str]` or `str`, *optional*): + If provided, files matching any of the patterns are not pushed. + delete_patterns (`List[str]` or `str`, *optional*): + If provided, remote files matching any of the patterns will be deleted from the repo. + + Returns: + The url of the commit of your model in the given repository. + + + + Raises the following error: + + - [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + if the user is not log on to the Hugging Face Hub. + + + """ + _check_fastai_fastcore_versions() + api = HfApi(endpoint=api_endpoint) + repo_id = api.create_repo(repo_id=repo_id, token=token, private=private, exist_ok=True).repo_id + + # Push the files to the repo in a single commit + with SoftTemporaryDirectory() as tmp: + saved_path = Path(tmp) / repo_id + _save_pretrained_fastai(learner, saved_path, config=config) + return api.upload_folder( + repo_id=repo_id, + token=token, + folder_path=saved_path, + commit_message=commit_message, + revision=branch, + create_pr=create_pr, + allow_patterns=allow_patterns, + ignore_patterns=ignore_patterns, + delete_patterns=delete_patterns, + ) diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/file_download.py b/.venv/lib/python3.12/site-packages/huggingface_hub/file_download.py new file mode 100644 index 0000000000000000000000000000000000000000..27dddcf69be097931a546345bf0231eca0b3f494 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/file_download.py @@ -0,0 +1,1816 @@ +import copy +import errno +import inspect +import os +import re +import shutil +import stat +import time +import uuid +import warnings +from dataclasses import dataclass +from pathlib import Path +from typing import Any, BinaryIO, Dict, Literal, NoReturn, Optional, Tuple, Union +from urllib.parse import quote, urlparse + +import requests + +from . import ( + __version__, # noqa: F401 # for backward compatibility + constants, +) +from ._local_folder import get_local_download_paths, read_download_metadata, write_download_metadata +from .constants import ( + HUGGINGFACE_CO_URL_TEMPLATE, # noqa: F401 # for backward compatibility + HUGGINGFACE_HUB_CACHE, # noqa: F401 # for backward compatibility +) +from .errors import ( + EntryNotFoundError, + FileMetadataError, + GatedRepoError, + HfHubHTTPError, + LocalEntryNotFoundError, + RepositoryNotFoundError, + RevisionNotFoundError, +) +from .utils import ( + OfflineModeIsEnabled, + SoftTemporaryDirectory, + WeakFileLock, + XetFileData, + build_hf_headers, + get_fastai_version, # noqa: F401 # for backward compatibility + get_fastcore_version, # noqa: F401 # for backward compatibility + get_graphviz_version, # noqa: F401 # for backward compatibility + get_jinja_version, # noqa: F401 # for backward compatibility + get_pydot_version, # noqa: F401 # for backward compatibility + get_tf_version, # noqa: F401 # for backward compatibility + get_torch_version, # noqa: F401 # for backward compatibility + hf_raise_for_status, + is_fastai_available, # noqa: F401 # for backward compatibility + is_fastcore_available, # noqa: F401 # for backward compatibility + is_graphviz_available, # noqa: F401 # for backward compatibility + is_jinja_available, # noqa: F401 # for backward compatibility + is_pydot_available, # noqa: F401 # for backward compatibility + is_tf_available, # noqa: F401 # for backward compatibility + is_torch_available, # noqa: F401 # for backward compatibility + logging, + parse_xet_file_data_from_response, + refresh_xet_connection_info, + reset_sessions, + tqdm, + validate_hf_hub_args, +) +from .utils._http import _adjust_range_header, http_backoff +from .utils._runtime import _PY_VERSION, is_xet_available # noqa: F401 # for backward compatibility +from .utils._typing import HTTP_METHOD_T +from .utils.sha import sha_fileobj +from .utils.tqdm import _get_progress_bar_context + + +logger = logging.get_logger(__name__) + +# Return value when trying to load a file from cache but the file does not exist in the distant repo. +_CACHED_NO_EXIST = object() +_CACHED_NO_EXIST_T = Any + +# Regex to get filename from a "Content-Disposition" header for CDN-served files +HEADER_FILENAME_PATTERN = re.compile(r'filename="(?P.*?)";') + +# Regex to check if the revision IS directly a commit_hash +REGEX_COMMIT_HASH = re.compile(r"^[0-9a-f]{40}$") + +# Regex to check if the file etag IS a valid sha256 +REGEX_SHA256 = re.compile(r"^[0-9a-f]{64}$") + +_are_symlinks_supported_in_dir: Dict[str, bool] = {} + + +def are_symlinks_supported(cache_dir: Union[str, Path, None] = None) -> bool: + """Return whether the symlinks are supported on the machine. + + Since symlinks support can change depending on the mounted disk, we need to check + on the precise cache folder. By default, the default HF cache directory is checked. + + Args: + cache_dir (`str`, `Path`, *optional*): + Path to the folder where cached files are stored. + + Returns: [bool] Whether symlinks are supported in the directory. + """ + # Defaults to HF cache + if cache_dir is None: + cache_dir = constants.HF_HUB_CACHE + cache_dir = str(Path(cache_dir).expanduser().resolve()) # make it unique + + # Check symlink compatibility only once (per cache directory) at first time use + if cache_dir not in _are_symlinks_supported_in_dir: + _are_symlinks_supported_in_dir[cache_dir] = True + + os.makedirs(cache_dir, exist_ok=True) + with SoftTemporaryDirectory(dir=cache_dir) as tmpdir: + src_path = Path(tmpdir) / "dummy_file_src" + src_path.touch() + dst_path = Path(tmpdir) / "dummy_file_dst" + + # Relative source path as in `_create_symlink`` + relative_src = os.path.relpath(src_path, start=os.path.dirname(dst_path)) + try: + os.symlink(relative_src, dst_path) + except OSError: + # Likely running on Windows + _are_symlinks_supported_in_dir[cache_dir] = False + + if not constants.HF_HUB_DISABLE_SYMLINKS_WARNING: + message = ( + "`huggingface_hub` cache-system uses symlinks by default to" + " efficiently store duplicated files but your machine does not" + f" support them in {cache_dir}. Caching files will still work" + " but in a degraded version that might require more space on" + " your disk. This warning can be disabled by setting the" + " `HF_HUB_DISABLE_SYMLINKS_WARNING` environment variable. For" + " more details, see" + " https://huggingface.co/docs/huggingface_hub/how-to-cache#limitations." + ) + if os.name == "nt": + message += ( + "\nTo support symlinks on Windows, you either need to" + " activate Developer Mode or to run Python as an" + " administrator. In order to activate developer mode," + " see this article:" + " https://docs.microsoft.com/en-us/windows/apps/get-started/enable-your-device-for-development" + ) + warnings.warn(message) + + return _are_symlinks_supported_in_dir[cache_dir] + + +@dataclass(frozen=True) +class HfFileMetadata: + """Data structure containing information about a file versioned on the Hub. + + Returned by [`get_hf_file_metadata`] based on a URL. + + Args: + commit_hash (`str`, *optional*): + The commit_hash related to the file. + etag (`str`, *optional*): + Etag of the file on the server. + location (`str`): + Location where to download the file. Can be a Hub url or not (CDN). + size (`size`): + Size of the file. In case of an LFS file, contains the size of the actual + LFS file, not the pointer. + xet_file_data (`XetFileData`, *optional*): + Xet information for the file. This is only set if the file is stored using Xet storage. + """ + + commit_hash: Optional[str] + etag: Optional[str] + location: str + size: Optional[int] + xet_file_data: Optional[XetFileData] + + +@validate_hf_hub_args +def hf_hub_url( + repo_id: str, + filename: str, + *, + subfolder: Optional[str] = None, + repo_type: Optional[str] = None, + revision: Optional[str] = None, + endpoint: Optional[str] = None, +) -> str: + """Construct the URL of a file from the given information. + + The resolved address can either be a huggingface.co-hosted url, or a link to + Cloudfront (a Content Delivery Network, or CDN) for large files which are + more than a few MBs. + + Args: + repo_id (`str`): + A namespace (user or an organization) name and a repo name separated + by a `/`. + filename (`str`): + The name of the file in the repo. + subfolder (`str`, *optional*): + An optional value corresponding to a folder inside the repo. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if downloading from a dataset or space, + `None` or `"model"` if downloading from a model. Default is `None`. + revision (`str`, *optional*): + An optional Git revision id which can be a branch name, a tag, or a + commit hash. + + Example: + + ```python + >>> from huggingface_hub import hf_hub_url + + >>> hf_hub_url( + ... repo_id="julien-c/EsperBERTo-small", filename="pytorch_model.bin" + ... ) + 'https://huggingface.co/julien-c/EsperBERTo-small/resolve/main/pytorch_model.bin' + ``` + + + + Notes: + + Cloudfront is replicated over the globe so downloads are way faster for + the end user (and it also lowers our bandwidth costs). + + Cloudfront aggressively caches files by default (default TTL is 24 + hours), however this is not an issue here because we implement a + git-based versioning system on huggingface.co, which means that we store + the files on S3/Cloudfront in a content-addressable way (i.e., the file + name is its hash). Using content-addressable filenames means cache can't + ever be stale. + + In terms of client-side caching from this library, we base our caching + on the objects' entity tag (`ETag`), which is an identifier of a + specific version of a resource [1]_. An object's ETag is: its git-sha1 + if stored in git, or its sha256 if stored in git-lfs. + + + + References: + + - [1] https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag + """ + if subfolder == "": + subfolder = None + if subfolder is not None: + filename = f"{subfolder}/{filename}" + + if repo_type not in constants.REPO_TYPES: + raise ValueError("Invalid repo type") + + if repo_type in constants.REPO_TYPES_URL_PREFIXES: + repo_id = constants.REPO_TYPES_URL_PREFIXES[repo_type] + repo_id + + if revision is None: + revision = constants.DEFAULT_REVISION + url = HUGGINGFACE_CO_URL_TEMPLATE.format( + repo_id=repo_id, revision=quote(revision, safe=""), filename=quote(filename) + ) + # Update endpoint if provided + if endpoint is not None and url.startswith(constants.ENDPOINT): + url = endpoint + url[len(constants.ENDPOINT) :] + return url + + +def _request_wrapper( + method: HTTP_METHOD_T, url: str, *, follow_relative_redirects: bool = False, **params +) -> requests.Response: + """Wrapper around requests methods to follow relative redirects if `follow_relative_redirects=True` even when + `allow_redirection=False`. + + A backoff mechanism retries the HTTP call on 5xx errors and network errors. + + Args: + method (`str`): + HTTP method, such as 'GET' or 'HEAD'. + url (`str`): + The URL of the resource to fetch. + follow_relative_redirects (`bool`, *optional*, defaults to `False`) + If True, relative redirection (redirection to the same site) will be resolved even when `allow_redirection` + kwarg is set to False. Useful when we want to follow a redirection to a renamed repository without + following redirection to a CDN. + **params (`dict`, *optional*): + Params to pass to `requests.request`. + """ + # Recursively follow relative redirects + if follow_relative_redirects: + response = _request_wrapper( + method=method, + url=url, + follow_relative_redirects=False, + **params, + ) + + # If redirection, we redirect only relative paths. + # This is useful in case of a renamed repository. + if 300 <= response.status_code <= 399: + parsed_target = urlparse(response.headers["Location"]) + if parsed_target.netloc == "": + # This means it is a relative 'location' headers, as allowed by RFC 7231. + # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource') + # We want to follow this relative redirect ! + # + # Highly inspired by `resolve_redirects` from requests library. + # See https://github.com/psf/requests/blob/main/requests/sessions.py#L159 + next_url = urlparse(url)._replace(path=parsed_target.path).geturl() + return _request_wrapper(method=method, url=next_url, follow_relative_redirects=True, **params) + return response + + # Perform request and return if status_code is not in the retry list. + response = http_backoff(method=method, url=url, **params) + hf_raise_for_status(response) + return response + + +def _get_file_length_from_http_response(response: requests.Response) -> Optional[int]: + """ + Get the length of the file from the HTTP response headers. + + This function extracts the file size from the HTTP response headers, either from the + `Content-Range` or `Content-Length` header, if available (in that order). + + Args: + response (`requests.Response`): + The HTTP response object. + + Returns: + `int` or `None`: The length of the file in bytes, or None if not available. + """ + + # If HTTP response contains compressed body (e.g. gzip), the `Content-Length` header will + # contain the length of the compressed body, not the uncompressed file size. + # And at the start of transmission there's no way to know the uncompressed file size for gzip, + # thus we return None in that case. + content_encoding = response.headers.get("Content-Encoding", "identity").lower() + if content_encoding != "identity": + # gzip/br/deflate/zstd etc + return None + + content_range = response.headers.get("Content-Range") + if content_range is not None: + return int(content_range.rsplit("/")[-1]) + + content_length = response.headers.get("Content-Length") + if content_length is not None: + return int(content_length) + + return None + + +def http_get( + url: str, + temp_file: BinaryIO, + *, + proxies: Optional[Dict] = None, + resume_size: int = 0, + headers: Optional[Dict[str, Any]] = None, + expected_size: Optional[int] = None, + displayed_filename: Optional[str] = None, + _nb_retries: int = 5, + _tqdm_bar: Optional[tqdm] = None, +) -> None: + """ + Download a remote file. Do not gobble up errors, and will return errors tailored to the Hugging Face Hub. + + If ConnectionError (SSLError) or ReadTimeout happen while streaming data from the server, it is most likely a + transient error (network outage?). We log a warning message and try to resume the download a few times before + giving up. The method gives up after 5 attempts if no new data has being received from the server. + + Args: + url (`str`): + The URL of the file to download. + temp_file (`BinaryIO`): + The file-like object where to save the file. + proxies (`dict`, *optional*): + Dictionary mapping protocol to the URL of the proxy passed to `requests.request`. + resume_size (`int`, *optional*): + The number of bytes already downloaded. If set to 0 (default), the whole file is download. If set to a + positive number, the download will resume at the given position. + headers (`dict`, *optional*): + Dictionary of HTTP Headers to send with the request. + expected_size (`int`, *optional*): + The expected size of the file to download. If set, the download will raise an error if the size of the + received content is different from the expected one. + displayed_filename (`str`, *optional*): + The filename of the file that is being downloaded. Value is used only to display a nice progress bar. If + not set, the filename is guessed from the URL or the `Content-Disposition` header. + """ + if expected_size is not None and resume_size == expected_size: + # If the file is already fully downloaded, we don't need to download it again. + return + + has_custom_range_header = headers is not None and any(h.lower() == "range" for h in headers) + hf_transfer = None + if constants.HF_HUB_ENABLE_HF_TRANSFER: + if resume_size != 0: + warnings.warn("'hf_transfer' does not support `resume_size`: falling back to regular download method") + elif proxies is not None: + warnings.warn("'hf_transfer' does not support `proxies`: falling back to regular download method") + elif has_custom_range_header: + warnings.warn("'hf_transfer' ignores custom 'Range' headers; falling back to regular download method") + else: + try: + import hf_transfer # type: ignore[no-redef] + except ImportError: + raise ValueError( + "Fast download using 'hf_transfer' is enabled" + " (HF_HUB_ENABLE_HF_TRANSFER=1) but 'hf_transfer' package is not" + " available in your environment. Try `pip install hf_transfer`." + ) + + initial_headers = headers + headers = copy.deepcopy(headers) or {} + if resume_size > 0: + headers["Range"] = _adjust_range_header(headers.get("Range"), resume_size) + elif expected_size and expected_size > constants.MAX_HTTP_DOWNLOAD_SIZE: + # Any files over 50GB will not be available through basic http request. + # Setting the range header to 0-0 will force the server to return the file size in the Content-Range header. + # Since hf_transfer splits the download into chunks, the process will succeed afterwards. + if hf_transfer: + headers["Range"] = "bytes=0-0" + else: + raise ValueError( + "The file is too large to be downloaded using the regular download method. Use `hf_transfer` or `hf_xet` instead." + " Try `pip install hf_transfer` or `pip install hf_xet`." + ) + + r = _request_wrapper( + method="GET", url=url, stream=True, proxies=proxies, headers=headers, timeout=constants.HF_HUB_DOWNLOAD_TIMEOUT + ) + + hf_raise_for_status(r) + total: Optional[int] = _get_file_length_from_http_response(r) + + if displayed_filename is None: + displayed_filename = url + content_disposition = r.headers.get("Content-Disposition") + if content_disposition is not None: + match = HEADER_FILENAME_PATTERN.search(content_disposition) + if match is not None: + # Means file is on CDN + displayed_filename = match.groupdict()["filename"] + + # Truncate filename if too long to display + if len(displayed_filename) > 40: + displayed_filename = f"(…){displayed_filename[-40:]}" + + consistency_error_message = ( + f"Consistency check failed: file should be of size {expected_size} but has size" + f" {{actual_size}} ({displayed_filename}).\nThis is usually due to network issues while downloading the file." + " Please retry with `force_download=True`." + ) + progress_cm = _get_progress_bar_context( + desc=displayed_filename, + log_level=logger.getEffectiveLevel(), + total=total, + initial=resume_size, + name="huggingface_hub.http_get", + _tqdm_bar=_tqdm_bar, + ) + + with progress_cm as progress: + if hf_transfer and total is not None and total > 5 * constants.DOWNLOAD_CHUNK_SIZE: + supports_callback = "callback" in inspect.signature(hf_transfer.download).parameters + if not supports_callback: + warnings.warn( + "You are using an outdated version of `hf_transfer`. " + "Consider upgrading to latest version to enable progress bars " + "using `pip install -U hf_transfer`." + ) + try: + hf_transfer.download( + url=url, + filename=temp_file.name, + max_files=constants.HF_TRANSFER_CONCURRENCY, + chunk_size=constants.DOWNLOAD_CHUNK_SIZE, + headers=initial_headers, + parallel_failures=3, + max_retries=5, + **({"callback": progress.update} if supports_callback else {}), + ) + except Exception as e: + raise RuntimeError( + "An error occurred while downloading using `hf_transfer`. Consider" + " disabling HF_HUB_ENABLE_HF_TRANSFER for better error handling." + ) from e + if not supports_callback: + progress.update(total) + if expected_size is not None and expected_size != os.path.getsize(temp_file.name): + raise EnvironmentError( + consistency_error_message.format( + actual_size=os.path.getsize(temp_file.name), + ) + ) + return + new_resume_size = resume_size + try: + for chunk in r.iter_content(chunk_size=constants.DOWNLOAD_CHUNK_SIZE): + if chunk: # filter out keep-alive new chunks + progress.update(len(chunk)) + temp_file.write(chunk) + new_resume_size += len(chunk) + # Some data has been downloaded from the server so we reset the number of retries. + _nb_retries = 5 + except (requests.ConnectionError, requests.ReadTimeout) as e: + # If ConnectionError (SSLError) or ReadTimeout happen while streaming data from the server, it is most likely + # a transient error (network outage?). We log a warning message and try to resume the download a few times + # before giving up. Tre retry mechanism is basic but should be enough in most cases. + if _nb_retries <= 0: + logger.warning("Error while downloading from %s: %s\nMax retries exceeded.", url, str(e)) + raise + logger.warning("Error while downloading from %s: %s\nTrying to resume download...", url, str(e)) + time.sleep(1) + reset_sessions() # In case of SSLError it's best to reset the shared requests.Session objects + return http_get( + url=url, + temp_file=temp_file, + proxies=proxies, + resume_size=new_resume_size, + headers=initial_headers, + expected_size=expected_size, + _nb_retries=_nb_retries - 1, + _tqdm_bar=_tqdm_bar, + ) + + if expected_size is not None and expected_size != temp_file.tell(): + raise EnvironmentError( + consistency_error_message.format( + actual_size=temp_file.tell(), + ) + ) + + +def xet_get( + *, + incomplete_path: Path, + xet_file_data: XetFileData, + headers: Dict[str, str], + expected_size: Optional[int] = None, + displayed_filename: Optional[str] = None, + _tqdm_bar: Optional[tqdm] = None, +) -> None: + """ + Download a file using Xet storage service. + + Args: + incomplete_path (`Path`): + The path to the file to download. + xet_file_data (`XetFileData`): + The file metadata needed to make the request to the xet storage service. + headers (`Dict[str, str]`): + The headers to send to the xet storage service. + expected_size (`int`, *optional*): + The expected size of the file to download. If set, the download will raise an error if the size of the + received content is different from the expected one. + displayed_filename (`str`, *optional*): + The filename of the file that is being downloaded. Value is used only to display a nice progress bar. If + not set, the filename is guessed from the URL or the `Content-Disposition` header. + + **How it works:** + The file download system uses Xet storage, which is a content-addressable storage system that breaks files into chunks + for efficient storage and transfer. + + `hf_xet.download_files` manages downloading files by: + - Taking a list of files to download (each with its unique content hash) + - Connecting to a storage server (CAS server) that knows how files are chunked + - Using authentication to ensure secure access + - Providing progress updates during download + + Authentication works by regularly refreshing access tokens through `refresh_xet_connection_info` to maintain a valid + connection to the storage server. + + The download process works like this: + 1. Create a local cache folder at `~/.cache/huggingface/xet/chunk-cache` to store reusable file chunks + 2. Download files in parallel: + 2.1. Prepare to write the file to disk + 2.2. Ask the server "how is this file split into chunks?" using the file's unique hash + The server responds with: + - Which chunks make up the complete file + - Where each chunk can be downloaded from + 2.3. For each needed chunk: + - Checks if we already have it in our local cache + - If not, download it from cloud storage (S3) + - Save it to cache for future use + - Assemble the chunks in order to recreate the original file + + """ + try: + from hf_xet import PyXetDownloadInfo, download_files # type: ignore[no-redef] + except ImportError: + raise ValueError( + "To use optimized download using Xet storage, you need to install the hf_xet package. " + 'Try `pip install "huggingface_hub[hf_xet]"` or `pip install hf_xet`.' + ) + + connection_info = refresh_xet_connection_info(file_data=xet_file_data, headers=headers) + + def token_refresher() -> Tuple[str, int]: + connection_info = refresh_xet_connection_info(file_data=xet_file_data, headers=headers) + if connection_info is None: + raise ValueError("Failed to refresh token using xet metadata.") + return connection_info.access_token, connection_info.expiration_unix_epoch + + xet_download_info = [ + PyXetDownloadInfo( + destination_path=str(incomplete_path.absolute()), hash=xet_file_data.file_hash, file_size=expected_size + ) + ] + + if not displayed_filename: + displayed_filename = incomplete_path.name + + # Truncate filename if too long to display + if len(displayed_filename) > 40: + displayed_filename = f"{displayed_filename[:40]}(…)" + + progress_cm = _get_progress_bar_context( + desc=displayed_filename, + log_level=logger.getEffectiveLevel(), + total=expected_size, + initial=0, + name="huggingface_hub.xet_get", + _tqdm_bar=_tqdm_bar, + ) + + with progress_cm as progress: + + def progress_updater(progress_bytes: float): + progress.update(progress_bytes) + + download_files( + xet_download_info, + endpoint=connection_info.endpoint, + token_info=(connection_info.access_token, connection_info.expiration_unix_epoch), + token_refresher=token_refresher, + progress_updater=[progress_updater], + ) + + +def _normalize_etag(etag: Optional[str]) -> Optional[str]: + """Normalize ETag HTTP header, so it can be used to create nice filepaths. + + The HTTP spec allows two forms of ETag: + ETag: W/"" + ETag: "" + + For now, we only expect the second form from the server, but we want to be future-proof so we support both. For + more context, see `TestNormalizeEtag` tests and https://github.com/huggingface/huggingface_hub/pull/1428. + + Args: + etag (`str`, *optional*): HTTP header + + Returns: + `str` or `None`: string that can be used as a nice directory name. + Returns `None` if input is None. + """ + if etag is None: + return None + return etag.lstrip("W/").strip('"') + + +def _create_relative_symlink(src: str, dst: str, new_blob: bool = False) -> None: + """Alias method used in `transformers` conversion script.""" + return _create_symlink(src=src, dst=dst, new_blob=new_blob) + + +def _create_symlink(src: str, dst: str, new_blob: bool = False) -> None: + """Create a symbolic link named dst pointing to src. + + By default, it will try to create a symlink using a relative path. Relative paths have 2 advantages: + - If the cache_folder is moved (example: back-up on a shared drive), relative paths within the cache folder will + not break. + - Relative paths seems to be better handled on Windows. Issue was reported 3 times in less than a week when + changing from relative to absolute paths. See https://github.com/huggingface/huggingface_hub/issues/1398, + https://github.com/huggingface/diffusers/issues/2729 and https://github.com/huggingface/transformers/pull/22228. + NOTE: The issue with absolute paths doesn't happen on admin mode. + When creating a symlink from the cache to a local folder, it is possible that a relative path cannot be created. + This happens when paths are not on the same volume. In that case, we use absolute paths. + + + The result layout looks something like + └── [ 128] snapshots + ├── [ 128] 2439f60ef33a0d46d85da5001d52aeda5b00ce9f + │ ├── [ 52] README.md -> ../../../blobs/d7edf6bd2a681fb0175f7735299831ee1b22b812 + │ └── [ 76] pytorch_model.bin -> ../../../blobs/403450e234d65943a7dcf7e05a771ce3c92faa84dd07db4ac20f592037a1e4bd + + If symlinks cannot be created on this platform (most likely to be Windows), the workaround is to avoid symlinks by + having the actual file in `dst`. If it is a new file (`new_blob=True`), we move it to `dst`. If it is not a new file + (`new_blob=False`), we don't know if the blob file is already referenced elsewhere. To avoid breaking existing + cache, the file is duplicated on the disk. + + In case symlinks are not supported, a warning message is displayed to the user once when loading `huggingface_hub`. + The warning message can be disabled with the `DISABLE_SYMLINKS_WARNING` environment variable. + """ + try: + os.remove(dst) + except OSError: + pass + + abs_src = os.path.abspath(os.path.expanduser(src)) + abs_dst = os.path.abspath(os.path.expanduser(dst)) + abs_dst_folder = os.path.dirname(abs_dst) + + # Use relative_dst in priority + try: + relative_src = os.path.relpath(abs_src, abs_dst_folder) + except ValueError: + # Raised on Windows if src and dst are not on the same volume. This is the case when creating a symlink to a + # local_dir instead of within the cache directory. + # See https://docs.python.org/3/library/os.path.html#os.path.relpath + relative_src = None + + try: + commonpath = os.path.commonpath([abs_src, abs_dst]) + _support_symlinks = are_symlinks_supported(commonpath) + except ValueError: + # Raised if src and dst are not on the same volume. Symlinks will still work on Linux/Macos. + # See https://docs.python.org/3/library/os.path.html#os.path.commonpath + _support_symlinks = os.name != "nt" + except PermissionError: + # Permission error means src and dst are not in the same volume (e.g. destination path has been provided + # by the user via `local_dir`. Let's test symlink support there) + _support_symlinks = are_symlinks_supported(abs_dst_folder) + except OSError as e: + # OS error (errno=30) means that the commonpath is readonly on Linux/MacOS. + if e.errno == errno.EROFS: + _support_symlinks = are_symlinks_supported(abs_dst_folder) + else: + raise + + # Symlinks are supported => let's create a symlink. + if _support_symlinks: + src_rel_or_abs = relative_src or abs_src + logger.debug(f"Creating pointer from {src_rel_or_abs} to {abs_dst}") + try: + os.symlink(src_rel_or_abs, abs_dst) + return + except FileExistsError: + if os.path.islink(abs_dst) and os.path.realpath(abs_dst) == os.path.realpath(abs_src): + # `abs_dst` already exists and is a symlink to the `abs_src` blob. It is most likely that the file has + # been cached twice concurrently (exactly between `os.remove` and `os.symlink`). Do nothing. + return + else: + # Very unlikely to happen. Means a file `dst` has been created exactly between `os.remove` and + # `os.symlink` and is not a symlink to the `abs_src` blob file. Raise exception. + raise + except PermissionError: + # Permission error means src and dst are not in the same volume (e.g. download to local dir) and symlink + # is supported on both volumes but not between them. Let's just make a hard copy in that case. + pass + + # Symlinks are not supported => let's move or copy the file. + if new_blob: + logger.info(f"Symlink not supported. Moving file from {abs_src} to {abs_dst}") + shutil.move(abs_src, abs_dst, copy_function=_copy_no_matter_what) + else: + logger.info(f"Symlink not supported. Copying file from {abs_src} to {abs_dst}") + shutil.copyfile(abs_src, abs_dst) + + +def _cache_commit_hash_for_specific_revision(storage_folder: str, revision: str, commit_hash: str) -> None: + """Cache reference between a revision (tag, branch or truncated commit hash) and the corresponding commit hash. + + Does nothing if `revision` is already a proper `commit_hash` or reference is already cached. + """ + if revision != commit_hash: + ref_path = Path(storage_folder) / "refs" / revision + ref_path.parent.mkdir(parents=True, exist_ok=True) + if not ref_path.exists() or commit_hash != ref_path.read_text(): + # Update ref only if has been updated. Could cause useless error in case + # repo is already cached and user doesn't have write access to cache folder. + # See https://github.com/huggingface/huggingface_hub/issues/1216. + ref_path.write_text(commit_hash) + + +@validate_hf_hub_args +def repo_folder_name(*, repo_id: str, repo_type: str) -> str: + """Return a serialized version of a hf.co repo name and type, safe for disk storage + as a single non-nested folder. + + Example: models--julien-c--EsperBERTo-small + """ + # remove all `/` occurrences to correctly convert repo to directory name + parts = [f"{repo_type}s", *repo_id.split("/")] + return constants.REPO_ID_SEPARATOR.join(parts) + + +def _check_disk_space(expected_size: int, target_dir: Union[str, Path]) -> None: + """Check disk usage and log a warning if there is not enough disk space to download the file. + + Args: + expected_size (`int`): + The expected size of the file in bytes. + target_dir (`str`): + The directory where the file will be stored after downloading. + """ + + target_dir = Path(target_dir) # format as `Path` + for path in [target_dir] + list(target_dir.parents): # first check target_dir, then each parents one by one + try: + target_dir_free = shutil.disk_usage(path).free + if target_dir_free < expected_size: + warnings.warn( + "Not enough free disk space to download the file. " + f"The expected file size is: {expected_size / 1e6:.2f} MB. " + f"The target location {target_dir} only has {target_dir_free / 1e6:.2f} MB free disk space." + ) + return + except OSError: # raise on anything: file does not exist or space disk cannot be checked + pass + + +@validate_hf_hub_args +def hf_hub_download( + repo_id: str, + filename: str, + *, + subfolder: Optional[str] = None, + repo_type: Optional[str] = None, + revision: Optional[str] = None, + library_name: Optional[str] = None, + library_version: Optional[str] = None, + cache_dir: Union[str, Path, None] = None, + local_dir: Union[str, Path, None] = None, + user_agent: Union[Dict, str, None] = None, + force_download: bool = False, + proxies: Optional[Dict] = None, + etag_timeout: float = constants.DEFAULT_ETAG_TIMEOUT, + token: Union[bool, str, None] = None, + local_files_only: bool = False, + headers: Optional[Dict[str, str]] = None, + endpoint: Optional[str] = None, + resume_download: Optional[bool] = None, + force_filename: Optional[str] = None, + local_dir_use_symlinks: Union[bool, Literal["auto"]] = "auto", +) -> str: + """Download a given file if it's not already present in the local cache. + + The new cache file layout looks like this: + - The cache directory contains one subfolder per repo_id (namespaced by repo type) + - inside each repo folder: + - refs is a list of the latest known revision => commit_hash pairs + - blobs contains the actual file blobs (identified by their git-sha or sha256, depending on + whether they're LFS files or not) + - snapshots contains one subfolder per commit, each "commit" contains the subset of the files + that have been resolved at that particular commit. Each filename is a symlink to the blob + at that particular commit. + + ``` + [ 96] . + └── [ 160] models--julien-c--EsperBERTo-small + ├── [ 160] blobs + │ ├── [321M] 403450e234d65943a7dcf7e05a771ce3c92faa84dd07db4ac20f592037a1e4bd + │ ├── [ 398] 7cb18dc9bafbfcf74629a4b760af1b160957a83e + │ └── [1.4K] d7edf6bd2a681fb0175f7735299831ee1b22b812 + ├── [ 96] refs + │ └── [ 40] main + └── [ 128] snapshots + ├── [ 128] 2439f60ef33a0d46d85da5001d52aeda5b00ce9f + │ ├── [ 52] README.md -> ../../blobs/d7edf6bd2a681fb0175f7735299831ee1b22b812 + │ └── [ 76] pytorch_model.bin -> ../../blobs/403450e234d65943a7dcf7e05a771ce3c92faa84dd07db4ac20f592037a1e4bd + └── [ 128] bbc77c8132af1cc5cf678da3f1ddf2de43606d48 + ├── [ 52] README.md -> ../../blobs/7cb18dc9bafbfcf74629a4b760af1b160957a83e + └── [ 76] pytorch_model.bin -> ../../blobs/403450e234d65943a7dcf7e05a771ce3c92faa84dd07db4ac20f592037a1e4bd + ``` + + If `local_dir` is provided, the file structure from the repo will be replicated in this location. When using this + option, the `cache_dir` will not be used and a `.cache/huggingface/` folder will be created at the root of `local_dir` + to store some metadata related to the downloaded files. While this mechanism is not as robust as the main + cache-system, it's optimized for regularly pulling the latest version of a repository. + + Args: + repo_id (`str`): + A user or an organization name and a repo name separated by a `/`. + filename (`str`): + The name of the file in the repo. + subfolder (`str`, *optional*): + An optional value corresponding to a folder inside the model repo. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if downloading from a dataset or space, + `None` or `"model"` if downloading from a model. Default is `None`. + revision (`str`, *optional*): + An optional Git revision id which can be a branch name, a tag, or a + commit hash. + library_name (`str`, *optional*): + The name of the library to which the object corresponds. + library_version (`str`, *optional*): + The version of the library. + cache_dir (`str`, `Path`, *optional*): + Path to the folder where cached files are stored. + local_dir (`str` or `Path`, *optional*): + If provided, the downloaded file will be placed under this directory. + user_agent (`dict`, `str`, *optional*): + The user-agent info in the form of a dictionary or a string. + force_download (`bool`, *optional*, defaults to `False`): + Whether the file should be downloaded even if it already exists in + the local cache. + proxies (`dict`, *optional*): + Dictionary mapping protocol to the URL of the proxy passed to + `requests.request`. + etag_timeout (`float`, *optional*, defaults to `10`): + When fetching ETag, how many seconds to wait for the server to send + data before giving up which is passed to `requests.request`. + token (`str`, `bool`, *optional*): + A token to be used for the download. + - If `True`, the token is read from the HuggingFace config + folder. + - If a string, it's used as the authentication token. + local_files_only (`bool`, *optional*, defaults to `False`): + If `True`, avoid downloading the file and return the path to the + local cached file if it exists. + headers (`dict`, *optional*): + Additional headers to be sent with the request. + + Returns: + `str`: Local path of file or if networking is off, last version of file cached on disk. + + Raises: + [`~utils.RepositoryNotFoundError`] + If the repository to download from cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + [`~utils.RevisionNotFoundError`] + If the revision to download from cannot be found. + [`~utils.EntryNotFoundError`] + If the file to download cannot be found. + [`~utils.LocalEntryNotFoundError`] + If network is disabled or unavailable and file is not found in cache. + [`EnvironmentError`](https://docs.python.org/3/library/exceptions.html#EnvironmentError) + If `token=True` but the token cannot be found. + [`OSError`](https://docs.python.org/3/library/exceptions.html#OSError) + If ETag cannot be determined. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If some parameter value is invalid. + + """ + if constants.HF_HUB_ETAG_TIMEOUT != constants.DEFAULT_ETAG_TIMEOUT: + # Respect environment variable above user value + etag_timeout = constants.HF_HUB_ETAG_TIMEOUT + + if force_filename is not None: + warnings.warn( + "The `force_filename` parameter is deprecated as a new caching system, " + "which keeps the filenames as they are on the Hub, is now in place.", + FutureWarning, + ) + if resume_download is not None: + warnings.warn( + "`resume_download` is deprecated and will be removed in version 1.0.0. " + "Downloads always resume when possible. " + "If you want to force a new download, use `force_download=True`.", + FutureWarning, + ) + + if cache_dir is None: + cache_dir = constants.HF_HUB_CACHE + if revision is None: + revision = constants.DEFAULT_REVISION + if isinstance(cache_dir, Path): + cache_dir = str(cache_dir) + if isinstance(local_dir, Path): + local_dir = str(local_dir) + + if subfolder == "": + subfolder = None + if subfolder is not None: + # This is used to create a URL, and not a local path, hence the forward slash. + filename = f"{subfolder}/{filename}" + + if repo_type is None: + repo_type = "model" + if repo_type not in constants.REPO_TYPES: + raise ValueError(f"Invalid repo type: {repo_type}. Accepted repo types are: {str(constants.REPO_TYPES)}") + + hf_headers = build_hf_headers( + token=token, + library_name=library_name, + library_version=library_version, + user_agent=user_agent, + headers=headers, + ) + + if local_dir is not None: + if local_dir_use_symlinks != "auto": + warnings.warn( + "`local_dir_use_symlinks` parameter is deprecated and will be ignored. " + "The process to download files to a local folder has been updated and do " + "not rely on symlinks anymore. You only need to pass a destination folder " + "as`local_dir`.\n" + "For more details, check out https://huggingface.co/docs/huggingface_hub/main/en/guides/download#download-files-to-local-folder." + ) + + return _hf_hub_download_to_local_dir( + # Destination + local_dir=local_dir, + # File info + repo_id=repo_id, + repo_type=repo_type, + filename=filename, + revision=revision, + # HTTP info + endpoint=endpoint, + etag_timeout=etag_timeout, + headers=hf_headers, + proxies=proxies, + token=token, + # Additional options + cache_dir=cache_dir, + force_download=force_download, + local_files_only=local_files_only, + ) + else: + return _hf_hub_download_to_cache_dir( + # Destination + cache_dir=cache_dir, + # File info + repo_id=repo_id, + filename=filename, + repo_type=repo_type, + revision=revision, + # HTTP info + endpoint=endpoint, + etag_timeout=etag_timeout, + headers=hf_headers, + proxies=proxies, + token=token, + # Additional options + local_files_only=local_files_only, + force_download=force_download, + ) + + +def _hf_hub_download_to_cache_dir( + *, + # Destination + cache_dir: str, + # File info + repo_id: str, + filename: str, + repo_type: str, + revision: str, + # HTTP info + endpoint: Optional[str], + etag_timeout: float, + headers: Dict[str, str], + proxies: Optional[Dict], + token: Optional[Union[bool, str]], + # Additional options + local_files_only: bool, + force_download: bool, +) -> str: + """Download a given file to a cache folder, if not already present. + + Method should not be called directly. Please use `hf_hub_download` instead. + """ + locks_dir = os.path.join(cache_dir, ".locks") + storage_folder = os.path.join(cache_dir, repo_folder_name(repo_id=repo_id, repo_type=repo_type)) + + # cross platform transcription of filename, to be used as a local file path. + relative_filename = os.path.join(*filename.split("/")) + if os.name == "nt": + if relative_filename.startswith("..\\") or "\\..\\" in relative_filename: + raise ValueError( + f"Invalid filename: cannot handle filename '{relative_filename}' on Windows. Please ask the repository" + " owner to rename this file." + ) + + # if user provides a commit_hash and they already have the file on disk, shortcut everything. + if REGEX_COMMIT_HASH.match(revision): + pointer_path = _get_pointer_path(storage_folder, revision, relative_filename) + if os.path.exists(pointer_path) and not force_download: + return pointer_path + + # Try to get metadata (etag, commit_hash, url, size) from the server. + # If we can't, a HEAD request error is returned. + (url_to_download, etag, commit_hash, expected_size, xet_file_data, head_call_error) = _get_metadata_or_catch_error( + repo_id=repo_id, + filename=filename, + repo_type=repo_type, + revision=revision, + endpoint=endpoint, + proxies=proxies, + etag_timeout=etag_timeout, + headers=headers, + token=token, + local_files_only=local_files_only, + storage_folder=storage_folder, + relative_filename=relative_filename, + ) + + # etag can be None for several reasons: + # 1. we passed local_files_only. + # 2. we don't have a connection + # 3. Hub is down (HTTP 500, 503, 504) + # 4. repo is not found -for example private or gated- and invalid/missing token sent + # 5. Hub is blocked by a firewall or proxy is not set correctly. + # => Try to get the last downloaded one from the specified revision. + # + # If the specified revision is a commit hash, look inside "snapshots". + # If the specified revision is a branch or tag, look inside "refs". + if head_call_error is not None: + # Couldn't make a HEAD call => let's try to find a local file + if not force_download: + commit_hash = None + if REGEX_COMMIT_HASH.match(revision): + commit_hash = revision + else: + ref_path = os.path.join(storage_folder, "refs", revision) + if os.path.isfile(ref_path): + with open(ref_path) as f: + commit_hash = f.read() + + # Return pointer file if exists + if commit_hash is not None: + pointer_path = _get_pointer_path(storage_folder, commit_hash, relative_filename) + if os.path.exists(pointer_path) and not force_download: + return pointer_path + + # Otherwise, raise appropriate error + _raise_on_head_call_error(head_call_error, force_download, local_files_only) + + # From now on, etag, commit_hash, url and size are not None. + assert etag is not None, "etag must have been retrieved from server" + assert commit_hash is not None, "commit_hash must have been retrieved from server" + assert url_to_download is not None, "file location must have been retrieved from server" + assert expected_size is not None, "expected_size must have been retrieved from server" + blob_path = os.path.join(storage_folder, "blobs", etag) + pointer_path = _get_pointer_path(storage_folder, commit_hash, relative_filename) + + os.makedirs(os.path.dirname(blob_path), exist_ok=True) + os.makedirs(os.path.dirname(pointer_path), exist_ok=True) + + # if passed revision is not identical to commit_hash + # then revision has to be a branch name or tag name. + # In that case store a ref. + _cache_commit_hash_for_specific_revision(storage_folder, revision, commit_hash) + + # Prevent parallel downloads of the same file with a lock. + # etag could be duplicated across repos, + lock_path = os.path.join(locks_dir, repo_folder_name(repo_id=repo_id, repo_type=repo_type), f"{etag}.lock") + + # Some Windows versions do not allow for paths longer than 255 characters. + # In this case, we must specify it as an extended path by using the "\\?\" prefix. + if ( + os.name == "nt" + and len(os.path.abspath(lock_path)) > 255 + and not os.path.abspath(lock_path).startswith("\\\\?\\") + ): + lock_path = "\\\\?\\" + os.path.abspath(lock_path) + + if ( + os.name == "nt" + and len(os.path.abspath(blob_path)) > 255 + and not os.path.abspath(blob_path).startswith("\\\\?\\") + ): + blob_path = "\\\\?\\" + os.path.abspath(blob_path) + + Path(lock_path).parent.mkdir(parents=True, exist_ok=True) + + # pointer already exists -> immediate return + if not force_download and os.path.exists(pointer_path): + return pointer_path + + # Blob exists but pointer must be (safely) created -> take the lock + if not force_download and os.path.exists(blob_path): + with WeakFileLock(lock_path): + if not os.path.exists(pointer_path): + _create_symlink(blob_path, pointer_path, new_blob=False) + return pointer_path + + # Local file doesn't exist or etag isn't a match => retrieve file from remote (or cache) + + with WeakFileLock(lock_path): + _download_to_tmp_and_move( + incomplete_path=Path(blob_path + ".incomplete"), + destination_path=Path(blob_path), + url_to_download=url_to_download, + proxies=proxies, + headers=headers, + expected_size=expected_size, + filename=filename, + force_download=force_download, + etag=etag, + xet_file_data=xet_file_data, + ) + if not os.path.exists(pointer_path): + _create_symlink(blob_path, pointer_path, new_blob=True) + + return pointer_path + + +def _hf_hub_download_to_local_dir( + *, + # Destination + local_dir: Union[str, Path], + # File info + repo_id: str, + repo_type: str, + filename: str, + revision: str, + # HTTP info + endpoint: Optional[str], + etag_timeout: float, + headers: Dict[str, str], + proxies: Optional[Dict], + token: Union[bool, str, None], + # Additional options + cache_dir: str, + force_download: bool, + local_files_only: bool, +) -> str: + """Download a given file to a local folder, if not already present. + + Method should not be called directly. Please use `hf_hub_download` instead. + """ + # Some Windows versions do not allow for paths longer than 255 characters. + # In this case, we must specify it as an extended path by using the "\\?\" prefix. + if os.name == "nt" and len(os.path.abspath(local_dir)) > 255: + local_dir = "\\\\?\\" + os.path.abspath(local_dir) + local_dir = Path(local_dir) + paths = get_local_download_paths(local_dir=local_dir, filename=filename) + local_metadata = read_download_metadata(local_dir=local_dir, filename=filename) + + # Local file exists + metadata exists + commit_hash matches => return file + if ( + not force_download + and REGEX_COMMIT_HASH.match(revision) + and paths.file_path.is_file() + and local_metadata is not None + and local_metadata.commit_hash == revision + ): + return str(paths.file_path) + + # Local file doesn't exist or commit_hash doesn't match => we need the etag + (url_to_download, etag, commit_hash, expected_size, xet_file_data, head_call_error) = _get_metadata_or_catch_error( + repo_id=repo_id, + filename=filename, + repo_type=repo_type, + revision=revision, + endpoint=endpoint, + proxies=proxies, + etag_timeout=etag_timeout, + headers=headers, + token=token, + local_files_only=local_files_only, + ) + + if head_call_error is not None: + # No HEAD call but local file exists => default to local file + if not force_download and paths.file_path.is_file(): + logger.warning( + f"Couldn't access the Hub to check for update but local file already exists. Defaulting to existing file. (error: {head_call_error})" + ) + return str(paths.file_path) + # Otherwise => raise + _raise_on_head_call_error(head_call_error, force_download, local_files_only) + + # From now on, etag, commit_hash, url and size are not None. + assert etag is not None, "etag must have been retrieved from server" + assert commit_hash is not None, "commit_hash must have been retrieved from server" + assert url_to_download is not None, "file location must have been retrieved from server" + assert expected_size is not None, "expected_size must have been retrieved from server" + + # Local file exists => check if it's up-to-date + if not force_download and paths.file_path.is_file(): + # etag matches => update metadata and return file + if local_metadata is not None and local_metadata.etag == etag: + write_download_metadata(local_dir=local_dir, filename=filename, commit_hash=commit_hash, etag=etag) + return str(paths.file_path) + + # metadata is outdated + etag is a sha256 + # => means it's an LFS file (large) + # => let's compute local hash and compare + # => if match, update metadata and return file + if local_metadata is None and REGEX_SHA256.match(etag) is not None: + with open(paths.file_path, "rb") as f: + file_hash = sha_fileobj(f).hex() + if file_hash == etag: + write_download_metadata(local_dir=local_dir, filename=filename, commit_hash=commit_hash, etag=etag) + return str(paths.file_path) + + # Local file doesn't exist or etag isn't a match => retrieve file from remote (or cache) + + # If we are lucky enough, the file is already in the cache => copy it + if not force_download: + cached_path = try_to_load_from_cache( + repo_id=repo_id, + filename=filename, + cache_dir=cache_dir, + revision=commit_hash, + repo_type=repo_type, + ) + if isinstance(cached_path, str): + with WeakFileLock(paths.lock_path): + paths.file_path.parent.mkdir(parents=True, exist_ok=True) + shutil.copyfile(cached_path, paths.file_path) + write_download_metadata(local_dir=local_dir, filename=filename, commit_hash=commit_hash, etag=etag) + return str(paths.file_path) + + # Otherwise, let's download the file! + with WeakFileLock(paths.lock_path): + paths.file_path.unlink(missing_ok=True) # delete outdated file first + _download_to_tmp_and_move( + incomplete_path=paths.incomplete_path(etag), + destination_path=paths.file_path, + url_to_download=url_to_download, + proxies=proxies, + headers=headers, + expected_size=expected_size, + filename=filename, + force_download=force_download, + etag=etag, + xet_file_data=xet_file_data, + ) + + write_download_metadata(local_dir=local_dir, filename=filename, commit_hash=commit_hash, etag=etag) + return str(paths.file_path) + + +@validate_hf_hub_args +def try_to_load_from_cache( + repo_id: str, + filename: str, + cache_dir: Union[str, Path, None] = None, + revision: Optional[str] = None, + repo_type: Optional[str] = None, +) -> Union[str, _CACHED_NO_EXIST_T, None]: + """ + Explores the cache to return the latest cached file for a given revision if found. + + This function will not raise any exception if the file in not cached. + + Args: + cache_dir (`str` or `os.PathLike`): + The folder where the cached files lie. + repo_id (`str`): + The ID of the repo on huggingface.co. + filename (`str`): + The filename to look for inside `repo_id`. + revision (`str`, *optional*): + The specific model version to use. Will default to `"main"` if it's not provided and no `commit_hash` is + provided either. + repo_type (`str`, *optional*): + The type of the repository. Will default to `"model"`. + + Returns: + `Optional[str]` or `_CACHED_NO_EXIST`: + Will return `None` if the file was not cached. Otherwise: + - The exact path to the cached file if it's found in the cache + - A special value `_CACHED_NO_EXIST` if the file does not exist at the given commit hash and this fact was + cached. + + Example: + + ```python + from huggingface_hub import try_to_load_from_cache, _CACHED_NO_EXIST + + filepath = try_to_load_from_cache() + if isinstance(filepath, str): + # file exists and is cached + ... + elif filepath is _CACHED_NO_EXIST: + # non-existence of file is cached + ... + else: + # file is not cached + ... + ``` + """ + if revision is None: + revision = "main" + if repo_type is None: + repo_type = "model" + if repo_type not in constants.REPO_TYPES: + raise ValueError(f"Invalid repo type: {repo_type}. Accepted repo types are: {str(constants.REPO_TYPES)}") + if cache_dir is None: + cache_dir = constants.HF_HUB_CACHE + + object_id = repo_id.replace("/", "--") + repo_cache = os.path.join(cache_dir, f"{repo_type}s--{object_id}") + if not os.path.isdir(repo_cache): + # No cache for this model + return None + + refs_dir = os.path.join(repo_cache, "refs") + snapshots_dir = os.path.join(repo_cache, "snapshots") + no_exist_dir = os.path.join(repo_cache, ".no_exist") + + # Resolve refs (for instance to convert main to the associated commit sha) + if os.path.isdir(refs_dir): + revision_file = os.path.join(refs_dir, revision) + if os.path.isfile(revision_file): + with open(revision_file) as f: + revision = f.read() + + # Check if file is cached as "no_exist" + if os.path.isfile(os.path.join(no_exist_dir, revision, filename)): + return _CACHED_NO_EXIST + + # Check if revision folder exists + if not os.path.exists(snapshots_dir): + return None + cached_shas = os.listdir(snapshots_dir) + if revision not in cached_shas: + # No cache for this revision and we won't try to return a random revision + return None + + # Check if file exists in cache + cached_file = os.path.join(snapshots_dir, revision, filename) + return cached_file if os.path.isfile(cached_file) else None + + +@validate_hf_hub_args +def get_hf_file_metadata( + url: str, + token: Union[bool, str, None] = None, + proxies: Optional[Dict] = None, + timeout: Optional[float] = constants.DEFAULT_REQUEST_TIMEOUT, + library_name: Optional[str] = None, + library_version: Optional[str] = None, + user_agent: Union[Dict, str, None] = None, + headers: Optional[Dict[str, str]] = None, + endpoint: Optional[str] = None, +) -> HfFileMetadata: + """Fetch metadata of a file versioned on the Hub for a given url. + + Args: + url (`str`): + File url, for example returned by [`hf_hub_url`]. + token (`str` or `bool`, *optional*): + A token to be used for the download. + - If `True`, the token is read from the HuggingFace config + folder. + - If `False` or `None`, no token is provided. + - If a string, it's used as the authentication token. + proxies (`dict`, *optional*): + Dictionary mapping protocol to the URL of the proxy passed to + `requests.request`. + timeout (`float`, *optional*, defaults to 10): + How many seconds to wait for the server to send metadata before giving up. + library_name (`str`, *optional*): + The name of the library to which the object corresponds. + library_version (`str`, *optional*): + The version of the library. + user_agent (`dict`, `str`, *optional*): + The user-agent info in the form of a dictionary or a string. + headers (`dict`, *optional*): + Additional headers to be sent with the request. + endpoint (`str`, *optional*): + Endpoint of the Hub. Defaults to . + + Returns: + A [`HfFileMetadata`] object containing metadata such as location, etag, size and + commit_hash. + """ + hf_headers = build_hf_headers( + token=token, + library_name=library_name, + library_version=library_version, + user_agent=user_agent, + headers=headers, + ) + hf_headers["Accept-Encoding"] = "identity" # prevent any compression => we want to know the real size of the file + + # Retrieve metadata + r = _request_wrapper( + method="HEAD", + url=url, + headers=hf_headers, + allow_redirects=False, + follow_relative_redirects=True, + proxies=proxies, + timeout=timeout, + ) + hf_raise_for_status(r) + + # Return + return HfFileMetadata( + commit_hash=r.headers.get(constants.HUGGINGFACE_HEADER_X_REPO_COMMIT), + # We favor a custom header indicating the etag of the linked resource, and + # we fallback to the regular etag header. + etag=_normalize_etag(r.headers.get(constants.HUGGINGFACE_HEADER_X_LINKED_ETAG) or r.headers.get("ETag")), + # Either from response headers (if redirected) or defaults to request url + # Do not use directly `url`, as `_request_wrapper` might have followed relative + # redirects. + location=r.headers.get("Location") or r.request.url, # type: ignore + size=_int_or_none( + r.headers.get(constants.HUGGINGFACE_HEADER_X_LINKED_SIZE) or r.headers.get("Content-Length") + ), + xet_file_data=parse_xet_file_data_from_response(r, endpoint=endpoint), # type: ignore + ) + + +def _get_metadata_or_catch_error( + *, + repo_id: str, + filename: str, + repo_type: str, + revision: str, + endpoint: Optional[str], + proxies: Optional[Dict], + etag_timeout: Optional[float], + headers: Dict[str, str], # mutated inplace! + token: Union[bool, str, None], + local_files_only: bool, + relative_filename: Optional[str] = None, # only used to store `.no_exists` in cache + storage_folder: Optional[str] = None, # only used to store `.no_exists` in cache +) -> Union[ + # Either an exception is caught and returned + Tuple[None, None, None, None, None, Exception], + # Or the metadata is returned as + # `(url_to_download, etag, commit_hash, expected_size, xet_file_data, None)` + Tuple[str, str, str, int, Optional[XetFileData], None], +]: + """Get metadata for a file on the Hub, safely handling network issues. + + Returns either the etag, commit_hash and expected size of the file, or the error + raised while fetching the metadata. + + NOTE: This function mutates `headers` inplace! It removes the `authorization` header + if the file is a LFS blob and the domain of the url is different from the + domain of the location (typically an S3 bucket). + """ + if local_files_only: + return ( + None, + None, + None, + None, + None, + OfflineModeIsEnabled( + f"Cannot access file since 'local_files_only=True' as been set. (repo_id: {repo_id}, repo_type: {repo_type}, revision: {revision}, filename: {filename})" + ), + ) + + url = hf_hub_url(repo_id, filename, repo_type=repo_type, revision=revision, endpoint=endpoint) + url_to_download: str = url + etag: Optional[str] = None + commit_hash: Optional[str] = None + expected_size: Optional[int] = None + head_error_call: Optional[Exception] = None + xet_file_data: Optional[XetFileData] = None + + # Try to get metadata from the server. + # Do not raise yet if the file is not found or not accessible. + if not local_files_only: + try: + try: + metadata = get_hf_file_metadata( + url=url, proxies=proxies, timeout=etag_timeout, headers=headers, token=token, endpoint=endpoint + ) + except EntryNotFoundError as http_error: + if storage_folder is not None and relative_filename is not None: + # Cache the non-existence of the file + commit_hash = http_error.response.headers.get(constants.HUGGINGFACE_HEADER_X_REPO_COMMIT) + if commit_hash is not None: + no_exist_file_path = Path(storage_folder) / ".no_exist" / commit_hash / relative_filename + try: + no_exist_file_path.parent.mkdir(parents=True, exist_ok=True) + no_exist_file_path.touch() + except OSError as e: + logger.error( + f"Could not cache non-existence of file. Will ignore error and continue. Error: {e}" + ) + _cache_commit_hash_for_specific_revision(storage_folder, revision, commit_hash) + raise + + # Commit hash must exist + commit_hash = metadata.commit_hash + if commit_hash is None: + raise FileMetadataError( + "Distant resource does not seem to be on huggingface.co. It is possible that a configuration issue" + " prevents you from downloading resources from https://huggingface.co. Please check your firewall" + " and proxy settings and make sure your SSL certificates are updated." + ) + + # Etag must exist + # If we don't have any of those, raise an error. + etag = metadata.etag + if etag is None: + raise FileMetadataError( + "Distant resource does not have an ETag, we won't be able to reliably ensure reproducibility." + ) + + # Size must exist + expected_size = metadata.size + if expected_size is None: + raise FileMetadataError("Distant resource does not have a Content-Length.") + + xet_file_data = metadata.xet_file_data + + # In case of a redirect, save an extra redirect on the request.get call, + # and ensure we download the exact atomic version even if it changed + # between the HEAD and the GET (unlikely, but hey). + # + # If url domain is different => we are downloading from a CDN => url is signed => don't send auth + # If url domain is the same => redirect due to repo rename AND downloading a regular file => keep auth + if xet_file_data is None and url != metadata.location: + url_to_download = metadata.location + if urlparse(url).netloc != urlparse(metadata.location).netloc: + # Remove authorization header when downloading a LFS blob + headers.pop("authorization", None) + except (requests.exceptions.SSLError, requests.exceptions.ProxyError): + # Actually raise for those subclasses of ConnectionError + raise + except ( + requests.exceptions.ConnectionError, + requests.exceptions.Timeout, + OfflineModeIsEnabled, + ) as error: + # Otherwise, our Internet connection is down. + # etag is None + head_error_call = error + except (RevisionNotFoundError, EntryNotFoundError): + # The repo was found but the revision or entry doesn't exist on the Hub (never existed or got deleted) + raise + except requests.HTTPError as error: + # Multiple reasons for an http error: + # - Repository is private and invalid/missing token sent + # - Repository is gated and invalid/missing token sent + # - Hub is down (error 500 or 504) + # => let's switch to 'local_files_only=True' to check if the files are already cached. + # (if it's not the case, the error will be re-raised) + head_error_call = error + except FileMetadataError as error: + # Multiple reasons for a FileMetadataError: + # - Wrong network configuration (proxy, firewall, SSL certificates) + # - Inconsistency on the Hub + # => let's switch to 'local_files_only=True' to check if the files are already cached. + # (if it's not the case, the error will be re-raised) + head_error_call = error + + if not (local_files_only or etag is not None or head_error_call is not None): + raise RuntimeError("etag is empty due to uncovered problems") + + return (url_to_download, etag, commit_hash, expected_size, xet_file_data, head_error_call) # type: ignore [return-value] + + +def _raise_on_head_call_error(head_call_error: Exception, force_download: bool, local_files_only: bool) -> NoReturn: + """Raise an appropriate error when the HEAD call failed and we cannot locate a local file.""" + # No head call => we cannot force download. + if force_download: + if local_files_only: + raise ValueError("Cannot pass 'force_download=True' and 'local_files_only=True' at the same time.") + elif isinstance(head_call_error, OfflineModeIsEnabled): + raise ValueError("Cannot pass 'force_download=True' when offline mode is enabled.") from head_call_error + else: + raise ValueError("Force download failed due to the above error.") from head_call_error + + # No head call + couldn't find an appropriate file on disk => raise an error. + if local_files_only: + raise LocalEntryNotFoundError( + "Cannot find the requested files in the disk cache and outgoing traffic has been disabled. To enable" + " hf.co look-ups and downloads online, set 'local_files_only' to False." + ) + elif isinstance(head_call_error, (RepositoryNotFoundError, GatedRepoError)) or ( + isinstance(head_call_error, HfHubHTTPError) and head_call_error.response.status_code == 401 + ): + # Repo not found or gated => let's raise the actual error + # Unauthorized => likely a token issue => let's raise the actual error + raise head_call_error + else: + # Otherwise: most likely a connection issue or Hub downtime => let's warn the user + raise LocalEntryNotFoundError( + "An error happened while trying to locate the file on the Hub and we cannot find the requested files" + " in the local cache. Please check your connection and try again or make sure your Internet connection" + " is on." + ) from head_call_error + + +def _download_to_tmp_and_move( + incomplete_path: Path, + destination_path: Path, + url_to_download: str, + proxies: Optional[Dict], + headers: Dict[str, str], + expected_size: Optional[int], + filename: str, + force_download: bool, + etag: Optional[str], + xet_file_data: Optional[XetFileData], +) -> None: + """Download content from a URL to a destination path. + + Internal logic: + - return early if file is already downloaded + - resume download if possible (from incomplete file) + - do not resume download if `force_download=True` or `HF_HUB_ENABLE_HF_TRANSFER=True` + - check disk space before downloading + - download content to a temporary file + - set correct permissions on temporary file + - move the temporary file to the destination path + + Both `incomplete_path` and `destination_path` must be on the same volume to avoid a local copy. + """ + if destination_path.exists() and not force_download: + # Do nothing if already exists (except if force_download=True) + return + + if incomplete_path.exists() and (force_download or (constants.HF_HUB_ENABLE_HF_TRANSFER and not proxies)): + # By default, we will try to resume the download if possible. + # However, if the user has set `force_download=True` or if `hf_transfer` is enabled, then we should + # not resume the download => delete the incomplete file. + message = f"Removing incomplete file '{incomplete_path}'" + if force_download: + message += " (force_download=True)" + elif constants.HF_HUB_ENABLE_HF_TRANSFER and not proxies: + message += " (hf_transfer=True)" + logger.info(message) + incomplete_path.unlink(missing_ok=True) + + with incomplete_path.open("ab") as f: + resume_size = f.tell() + message = f"Downloading '{filename}' to '{incomplete_path}'" + if resume_size > 0 and expected_size is not None: + message += f" (resume from {resume_size}/{expected_size})" + logger.info(message) + + if expected_size is not None: # might be None if HTTP header not set correctly + # Check disk space in both tmp and destination path + _check_disk_space(expected_size, incomplete_path.parent) + _check_disk_space(expected_size, destination_path.parent) + + if xet_file_data is not None and is_xet_available(): + logger.debug("Xet Storage is enabled for this repo. Downloading file from Xet Storage..") + xet_get( + incomplete_path=incomplete_path, + xet_file_data=xet_file_data, + headers=headers, + expected_size=expected_size, + displayed_filename=filename, + ) + else: + if xet_file_data is not None and not constants.HF_HUB_DISABLE_XET: + logger.warning( + "Xet Storage is enabled for this repo, but the 'hf_xet' package is not installed. " + "Falling back to regular HTTP download. " + "For better performance, install the package with: `pip install huggingface_hub[hf_xet]` or `pip install hf_xet`" + ) + + http_get( + url_to_download, + f, + proxies=proxies, + resume_size=resume_size, + headers=headers, + expected_size=expected_size, + ) + + logger.info(f"Download complete. Moving file to {destination_path}") + _chmod_and_move(incomplete_path, destination_path) + + +def _int_or_none(value: Optional[str]) -> Optional[int]: + try: + return int(value) # type: ignore + except (TypeError, ValueError): + return None + + +def _chmod_and_move(src: Path, dst: Path) -> None: + """Set correct permission before moving a blob from tmp directory to cache dir. + + Do not take into account the `umask` from the process as there is no convenient way + to get it that is thread-safe. + + See: + - About umask: https://docs.python.org/3/library/os.html#os.umask + - Thread-safety: https://stackoverflow.com/a/70343066 + - About solution: https://github.com/huggingface/huggingface_hub/pull/1220#issuecomment-1326211591 + - Fix issue: https://github.com/huggingface/huggingface_hub/issues/1141 + - Fix issue: https://github.com/huggingface/huggingface_hub/issues/1215 + """ + # Get umask by creating a temporary file in the cached repo folder. + tmp_file = dst.parent.parent / f"tmp_{uuid.uuid4()}" + try: + tmp_file.touch() + cache_dir_mode = Path(tmp_file).stat().st_mode + os.chmod(str(src), stat.S_IMODE(cache_dir_mode)) + except OSError as e: + logger.warning( + f"Could not set the permissions on the file '{src}'. Error: {e}.\nContinuing without setting permissions." + ) + finally: + try: + tmp_file.unlink() + except OSError: + # fails if `tmp_file.touch()` failed => do nothing + # See https://github.com/huggingface/huggingface_hub/issues/2359 + pass + + shutil.move(str(src), str(dst), copy_function=_copy_no_matter_what) + + +def _copy_no_matter_what(src: str, dst: str) -> None: + """Copy file from src to dst. + + If `shutil.copy2` fails, fallback to `shutil.copyfile`. + """ + try: + # Copy file with metadata and permission + # Can fail e.g. if dst is an S3 mount + shutil.copy2(src, dst) + except OSError: + # Copy only file content + shutil.copyfile(src, dst) + + +def _get_pointer_path(storage_folder: str, revision: str, relative_filename: str) -> str: + # Using `os.path.abspath` instead of `Path.resolve()` to avoid resolving symlinks + snapshot_path = os.path.join(storage_folder, "snapshots") + pointer_path = os.path.join(snapshot_path, revision, relative_filename) + if Path(os.path.abspath(snapshot_path)) not in Path(os.path.abspath(pointer_path)).parents: + raise ValueError( + "Invalid pointer path: cannot create pointer path in snapshot folder if" + f" `storage_folder='{storage_folder}'`, `revision='{revision}'` and" + f" `relative_filename='{relative_filename}'`." + ) + return pointer_path diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/hf_api.py b/.venv/lib/python3.12/site-packages/huggingface_hub/hf_api.py new file mode 100644 index 0000000000000000000000000000000000000000..e2827a6f194da1b4858a90a4626b1ccb7e353a3c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/hf_api.py @@ -0,0 +1,11063 @@ +# coding=utf-8 +# Copyright 2019-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import annotations + +import inspect +import io +import json +import re +import struct +import time +import warnings +from collections import defaultdict +from concurrent.futures import Future, ThreadPoolExecutor +from dataclasses import asdict, dataclass, field +from datetime import datetime +from functools import wraps +from itertools import islice +from pathlib import Path +from textwrap import dedent +from typing import ( + TYPE_CHECKING, + Any, + BinaryIO, + Callable, + Dict, + Iterable, + Iterator, + List, + Literal, + Optional, + Tuple, + Type, + TypeVar, + Union, + overload, +) +from urllib.parse import quote, unquote + +import requests +from requests.exceptions import HTTPError +from tqdm.auto import tqdm as base_tqdm +from tqdm.contrib.concurrent import thread_map + +from . import constants +from ._commit_api import ( + CommitOperation, + CommitOperationAdd, + CommitOperationCopy, + CommitOperationDelete, + _fetch_files_to_copy, + _fetch_upload_modes, + _prepare_commit_payload, + _upload_lfs_files, + _upload_xet_files, + _warn_on_overwriting_operations, +) +from ._inference_endpoints import InferenceEndpoint, InferenceEndpointType +from ._jobs_api import JobInfo, ScheduledJobInfo, _create_job_spec +from ._space_api import SpaceHardware, SpaceRuntime, SpaceStorage, SpaceVariable +from ._upload_large_folder import upload_large_folder_internal +from .community import ( + Discussion, + DiscussionComment, + DiscussionStatusChange, + DiscussionTitleChange, + DiscussionWithDetails, + deserialize_event, +) +from .constants import ( + DEFAULT_ETAG_TIMEOUT, # noqa: F401 # kept for backward compatibility + DEFAULT_REQUEST_TIMEOUT, # noqa: F401 # kept for backward compatibility + DEFAULT_REVISION, # noqa: F401 # kept for backward compatibility + DISCUSSION_STATUS, # noqa: F401 # kept for backward compatibility + DISCUSSION_TYPES, # noqa: F401 # kept for backward compatibility + ENDPOINT, # noqa: F401 # kept for backward compatibility + INFERENCE_ENDPOINTS_ENDPOINT, # noqa: F401 # kept for backward compatibility + REGEX_COMMIT_OID, # noqa: F401 # kept for backward compatibility + REPO_TYPE_MODEL, # noqa: F401 # kept for backward compatibility + REPO_TYPES, # noqa: F401 # kept for backward compatibility + REPO_TYPES_MAPPING, # noqa: F401 # kept for backward compatibility + REPO_TYPES_URL_PREFIXES, # noqa: F401 # kept for backward compatibility + SAFETENSORS_INDEX_FILE, # noqa: F401 # kept for backward compatibility + SAFETENSORS_MAX_HEADER_LENGTH, # noqa: F401 # kept for backward compatibility + SAFETENSORS_SINGLE_FILE, # noqa: F401 # kept for backward compatibility + SPACES_SDK_TYPES, # noqa: F401 # kept for backward compatibility + WEBHOOK_DOMAIN_T, # noqa: F401 # kept for backward compatibility + DiscussionStatusFilter, # noqa: F401 # kept for backward compatibility + DiscussionTypeFilter, # noqa: F401 # kept for backward compatibility +) +from .errors import ( + BadRequestError, + EntryNotFoundError, + GatedRepoError, + HfHubHTTPError, + RepositoryNotFoundError, + RevisionNotFoundError, +) +from .file_download import HfFileMetadata, get_hf_file_metadata, hf_hub_url +from .repocard_data import DatasetCardData, ModelCardData, SpaceCardData +from .utils import ( + DEFAULT_IGNORE_PATTERNS, + HfFolder, # noqa: F401 # kept for backward compatibility + LocalTokenNotFoundError, + NotASafetensorsRepoError, + SafetensorsFileMetadata, + SafetensorsParsingError, + SafetensorsRepoMetadata, + TensorInfo, + build_hf_headers, + chunk_iterable, + experimental, + filter_repo_objects, + fix_hf_endpoint_in_url, + get_session, + get_token, + hf_raise_for_status, + logging, + paginate, + parse_datetime, + validate_hf_hub_args, +) +from .utils import tqdm as hf_tqdm +from .utils._auth import ( + _get_token_from_environment, + _get_token_from_file, + _get_token_from_google_colab, +) +from .utils._deprecation import _deprecate_arguments, _deprecate_method +from .utils._runtime import is_xet_available +from .utils._typing import CallableT +from .utils.endpoint_helpers import _is_emission_within_threshold + + +if TYPE_CHECKING: + from .inference._providers import PROVIDER_T + +R = TypeVar("R") # Return type +CollectionItemType_T = Literal["model", "dataset", "space", "paper", "collection"] + +ExpandModelProperty_T = Literal[ + "author", + "baseModels", + "cardData", + "childrenModelCount", + "config", + "createdAt", + "disabled", + "downloads", + "downloadsAllTime", + "gated", + "gguf", + "inference", + "inferenceProviderMapping", + "lastModified", + "library_name", + "likes", + "mask_token", + "model-index", + "pipeline_tag", + "private", + "resourceGroup", + "safetensors", + "sha", + "siblings", + "spaces", + "tags", + "transformersInfo", + "trendingScore", + "usedStorage", + "widgetData", + "xetEnabled", +] + +ExpandDatasetProperty_T = Literal[ + "author", + "cardData", + "citation", + "createdAt", + "description", + "disabled", + "downloads", + "downloadsAllTime", + "gated", + "lastModified", + "likes", + "paperswithcode_id", + "private", + "resourceGroup", + "sha", + "siblings", + "tags", + "trendingScore", + "usedStorage", + "xetEnabled", +] + +ExpandSpaceProperty_T = Literal[ + "author", + "cardData", + "createdAt", + "datasets", + "disabled", + "lastModified", + "likes", + "models", + "private", + "resourceGroup", + "runtime", + "sdk", + "sha", + "siblings", + "subdomain", + "tags", + "trendingScore", + "usedStorage", + "xetEnabled", +] + +USERNAME_PLACEHOLDER = "hf_user" +_REGEX_DISCUSSION_URL = re.compile(r".*/discussions/(\d+)$") + +_CREATE_COMMIT_NO_REPO_ERROR_MESSAGE = ( + "\nNote: Creating a commit assumes that the repo already exists on the" + " Huggingface Hub. Please use `create_repo` if it's not the case." +) +_AUTH_CHECK_NO_REPO_ERROR_MESSAGE = ( + "\nNote: The repository either does not exist or you do not have access rights." + " Please check the repository ID and your access permissions." + " If this is a private repository, ensure that your token is correct." +) +logger = logging.get_logger(__name__) + + +def repo_type_and_id_from_hf_id(hf_id: str, hub_url: Optional[str] = None) -> Tuple[Optional[str], Optional[str], str]: + """ + Returns the repo type and ID from a huggingface.co URL linking to a + repository + + Args: + hf_id (`str`): + An URL or ID of a repository on the HF hub. Accepted values are: + + - https://huggingface.co/// + - https://huggingface.co// + - hf://// + - hf:/// + - // + - / + - + hub_url (`str`, *optional*): + The URL of the HuggingFace Hub, defaults to https://huggingface.co + + Returns: + A tuple with three items: repo_type (`str` or `None`), namespace (`str` or + `None`) and repo_id (`str`). + + Raises: + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If URL cannot be parsed. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If `repo_type` is unknown. + """ + input_hf_id = hf_id + + hub_url = re.sub(r"https?://", "", hub_url if hub_url is not None else constants.ENDPOINT) + is_hf_url = hub_url in hf_id and "@" not in hf_id + + HFFS_PREFIX = "hf://" + if hf_id.startswith(HFFS_PREFIX): # Remove "hf://" prefix if exists + hf_id = hf_id[len(HFFS_PREFIX) :] + + url_segments = hf_id.split("/") + is_hf_id = len(url_segments) <= 3 + + namespace: Optional[str] + if is_hf_url: + namespace, repo_id = url_segments[-2:] + if namespace == hub_url: + namespace = None + if len(url_segments) > 2 and hub_url not in url_segments[-3]: + repo_type = url_segments[-3] + elif namespace in constants.REPO_TYPES_MAPPING: + # Mean canonical dataset or model + repo_type = constants.REPO_TYPES_MAPPING[namespace] + namespace = None + else: + repo_type = None + elif is_hf_id: + if len(url_segments) == 3: + # Passed // or // + repo_type, namespace, repo_id = url_segments[-3:] + elif len(url_segments) == 2: + if url_segments[0] in constants.REPO_TYPES_MAPPING: + # Passed '' or 'datasets/' for a canonical model or dataset + repo_type = constants.REPO_TYPES_MAPPING[url_segments[0]] + namespace = None + repo_id = hf_id.split("/")[-1] + else: + # Passed / or / + namespace, repo_id = hf_id.split("/")[-2:] + repo_type = None + else: + # Passed + repo_id = url_segments[0] + namespace, repo_type = None, None + else: + raise ValueError(f"Unable to retrieve user and repo ID from the passed HF ID: {hf_id}") + + # Check if repo type is known (mapping "spaces" => "space" + empty value => `None`) + if repo_type in constants.REPO_TYPES_MAPPING: + repo_type = constants.REPO_TYPES_MAPPING[repo_type] + if repo_type == "": + repo_type = None + if repo_type not in constants.REPO_TYPES: + raise ValueError(f"Unknown `repo_type`: '{repo_type}' ('{input_hf_id}')") + + return repo_type, namespace, repo_id + + +@dataclass +class LastCommitInfo(dict): + oid: str + title: str + date: datetime + + def __post_init__(self): # hack to make LastCommitInfo backward compatible + self.update(asdict(self)) + + +@dataclass +class BlobLfsInfo(dict): + size: int + sha256: str + pointer_size: int + + def __post_init__(self): # hack to make BlobLfsInfo backward compatible + self.update(asdict(self)) + + +@dataclass +class BlobSecurityInfo(dict): + safe: bool # duplicate information with "status" field, keeping it for backward compatibility + status: str + av_scan: Optional[Dict] + pickle_import_scan: Optional[Dict] + + def __post_init__(self): # hack to make BlogSecurityInfo backward compatible + self.update(asdict(self)) + + +@dataclass +class TransformersInfo(dict): + auto_model: str + custom_class: Optional[str] = None + # possible `pipeline_tag` values: https://github.com/huggingface/huggingface.js/blob/3ee32554b8620644a6287e786b2a83bf5caf559c/packages/tasks/src/pipelines.ts#L72 + pipeline_tag: Optional[str] = None + processor: Optional[str] = None + + def __post_init__(self): # hack to make TransformersInfo backward compatible + self.update(asdict(self)) + + +@dataclass +class SafeTensorsInfo(dict): + parameters: Dict[str, int] + total: int + + def __post_init__(self): # hack to make SafeTensorsInfo backward compatible + self.update(asdict(self)) + + +@dataclass +class CommitInfo(str): + """Data structure containing information about a newly created commit. + + Returned by any method that creates a commit on the Hub: [`create_commit`], [`upload_file`], [`upload_folder`], + [`delete_file`], [`delete_folder`]. It inherits from `str` for backward compatibility but using methods specific + to `str` is deprecated. + + Attributes: + commit_url (`str`): + Url where to find the commit. + + commit_message (`str`): + The summary (first line) of the commit that has been created. + + commit_description (`str`): + Description of the commit that has been created. Can be empty. + + oid (`str`): + Commit hash id. Example: `"91c54ad1727ee830252e457677f467be0bfd8a57"`. + + pr_url (`str`, *optional*): + Url to the PR that has been created, if any. Populated when `create_pr=True` + is passed. + + pr_revision (`str`, *optional*): + Revision of the PR that has been created, if any. Populated when + `create_pr=True` is passed. Example: `"refs/pr/1"`. + + pr_num (`int`, *optional*): + Number of the PR discussion that has been created, if any. Populated when + `create_pr=True` is passed. Can be passed as `discussion_num` in + [`get_discussion_details`]. Example: `1`. + + repo_url (`RepoUrl`): + Repo URL of the commit containing info like repo_id, repo_type, etc. + + _url (`str`, *optional*): + Legacy url for `str` compatibility. Can be the url to the uploaded file on the Hub (if returned by + [`upload_file`]), to the uploaded folder on the Hub (if returned by [`upload_folder`]) or to the commit on + the Hub (if returned by [`create_commit`]). Defaults to `commit_url`. It is deprecated to use this + attribute. Please use `commit_url` instead. + """ + + commit_url: str + commit_message: str + commit_description: str + oid: str + pr_url: Optional[str] = None + + # Computed from `commit_url` in `__post_init__` + repo_url: RepoUrl = field(init=False) + + # Computed from `pr_url` in `__post_init__` + pr_revision: Optional[str] = field(init=False) + pr_num: Optional[str] = field(init=False) + + # legacy url for `str` compatibility (ex: url to uploaded file, url to uploaded folder, url to PR, etc.) + _url: str = field(repr=False, default=None) # type: ignore # defaults to `commit_url` + + def __new__(cls, *args, commit_url: str, _url: Optional[str] = None, **kwargs): + return str.__new__(cls, _url or commit_url) + + def __post_init__(self): + """Populate pr-related fields after initialization. + + See https://docs.python.org/3.10/library/dataclasses.html#post-init-processing. + """ + # Repo info + self.repo_url = RepoUrl(self.commit_url.split("/commit/")[0]) + + # PR info + if self.pr_url is not None: + self.pr_revision = _parse_revision_from_pr_url(self.pr_url) + self.pr_num = int(self.pr_revision.split("/")[-1]) + else: + self.pr_revision = None + self.pr_num = None + + +@dataclass +class AccessRequest: + """Data structure containing information about a user access request. + + Attributes: + username (`str`): + Username of the user who requested access. + fullname (`str`): + Fullname of the user who requested access. + email (`Optional[str]`): + Email of the user who requested access. + Can only be `None` in the /accepted list if the user was granted access manually. + timestamp (`datetime`): + Timestamp of the request. + status (`Literal["pending", "accepted", "rejected"]`): + Status of the request. Can be one of `["pending", "accepted", "rejected"]`. + fields (`Dict[str, Any]`, *optional*): + Additional fields filled by the user in the gate form. + """ + + username: str + fullname: str + email: Optional[str] + timestamp: datetime + status: Literal["pending", "accepted", "rejected"] + + # Additional fields filled by the user in the gate form + fields: Optional[Dict[str, Any]] = None + + +@dataclass +class WebhookWatchedItem: + """Data structure containing information about the items watched by a webhook. + + Attributes: + type (`Literal["dataset", "model", "org", "space", "user"]`): + Type of the item to be watched. Can be one of `["dataset", "model", "org", "space", "user"]`. + name (`str`): + Name of the item to be watched. Can be the username, organization name, model name, dataset name or space name. + """ + + type: Literal["dataset", "model", "org", "space", "user"] + name: str + + +@dataclass +class WebhookInfo: + """Data structure containing information about a webhook. + + Attributes: + id (`str`): + ID of the webhook. + url (`str`): + URL of the webhook. + watched (`List[WebhookWatchedItem]`): + List of items watched by the webhook, see [`WebhookWatchedItem`]. + domains (`List[WEBHOOK_DOMAIN_T]`): + List of domains the webhook is watching. Can be one of `["repo", "discussions"]`. + secret (`str`, *optional*): + Secret of the webhook. + disabled (`bool`): + Whether the webhook is disabled or not. + """ + + id: str + url: str + watched: List[WebhookWatchedItem] + domains: List[constants.WEBHOOK_DOMAIN_T] + secret: Optional[str] + disabled: bool + + +class RepoUrl(str): + """Subclass of `str` describing a repo URL on the Hub. + + `RepoUrl` is returned by `HfApi.create_repo`. It inherits from `str` for backward + compatibility. At initialization, the URL is parsed to populate properties: + - endpoint (`str`) + - namespace (`Optional[str]`) + - repo_name (`str`) + - repo_id (`str`) + - repo_type (`Literal["model", "dataset", "space"]`) + - url (`str`) + + Args: + url (`Any`): + String value of the repo url. + endpoint (`str`, *optional*): + Endpoint of the Hub. Defaults to . + + Example: + ```py + >>> RepoUrl('https://huggingface.co/gpt2') + RepoUrl('https://huggingface.co/gpt2', endpoint='https://huggingface.co', repo_type='model', repo_id='gpt2') + + >>> RepoUrl('https://hub-ci.huggingface.co/datasets/dummy_user/dummy_dataset', endpoint='https://hub-ci.huggingface.co') + RepoUrl('https://hub-ci.huggingface.co/datasets/dummy_user/dummy_dataset', endpoint='https://hub-ci.huggingface.co', repo_type='dataset', repo_id='dummy_user/dummy_dataset') + + >>> RepoUrl('hf://datasets/my-user/my-dataset') + RepoUrl('hf://datasets/my-user/my-dataset', endpoint='https://huggingface.co', repo_type='dataset', repo_id='user/dataset') + + >>> HfApi.create_repo("dummy_model") + RepoUrl('https://huggingface.co/Wauplin/dummy_model', endpoint='https://huggingface.co', repo_type='model', repo_id='Wauplin/dummy_model') + ``` + + Raises: + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If URL cannot be parsed. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If `repo_type` is unknown. + """ + + def __new__(cls, url: Any, endpoint: Optional[str] = None): + url = fix_hf_endpoint_in_url(url, endpoint=endpoint) + return super(RepoUrl, cls).__new__(cls, url) + + def __init__(self, url: Any, endpoint: Optional[str] = None) -> None: + super().__init__() + # Parse URL + self.endpoint = endpoint or constants.ENDPOINT + repo_type, namespace, repo_name = repo_type_and_id_from_hf_id(self, hub_url=self.endpoint) + + # Populate fields + self.namespace = namespace + self.repo_name = repo_name + self.repo_id = repo_name if namespace is None else f"{namespace}/{repo_name}" + self.repo_type = repo_type or constants.REPO_TYPE_MODEL + self.url = str(self) # just in case it's needed + + def __repr__(self) -> str: + return f"RepoUrl('{self}', endpoint='{self.endpoint}', repo_type='{self.repo_type}', repo_id='{self.repo_id}')" + + +@dataclass +class RepoSibling: + """ + Contains basic information about a repo file inside a repo on the Hub. + + + + All attributes of this class are optional except `rfilename`. This is because only the file names are returned when + listing repositories on the Hub (with [`list_models`], [`list_datasets`] or [`list_spaces`]). If you need more + information like file size, blob id or lfs details, you must request them specifically from one repo at a time + (using [`model_info`], [`dataset_info`] or [`space_info`]) as it adds more constraints on the backend server to + retrieve these. + + + + Attributes: + rfilename (str): + file name, relative to the repo root. + size (`int`, *optional*): + The file's size, in bytes. This attribute is defined when `files_metadata` argument of [`repo_info`] is set + to `True`. It's `None` otherwise. + blob_id (`str`, *optional*): + The file's git OID. This attribute is defined when `files_metadata` argument of [`repo_info`] is set to + `True`. It's `None` otherwise. + lfs (`BlobLfsInfo`, *optional*): + The file's LFS metadata. This attribute is defined when`files_metadata` argument of [`repo_info`] is set to + `True` and the file is stored with Git LFS. It's `None` otherwise. + """ + + rfilename: str + size: Optional[int] = None + blob_id: Optional[str] = None + lfs: Optional[BlobLfsInfo] = None + + +@dataclass +class RepoFile: + """ + Contains information about a file on the Hub. + + Attributes: + path (str): + file path relative to the repo root. + size (`int`): + The file's size, in bytes. + blob_id (`str`): + The file's git OID. + lfs (`BlobLfsInfo`): + The file's LFS metadata. + last_commit (`LastCommitInfo`, *optional*): + The file's last commit metadata. Only defined if [`list_repo_tree`] and [`get_paths_info`] + are called with `expand=True`. + security (`BlobSecurityInfo`, *optional*): + The file's security scan metadata. Only defined if [`list_repo_tree`] and [`get_paths_info`] + are called with `expand=True`. + """ + + path: str + size: int + blob_id: str + lfs: Optional[BlobLfsInfo] = None + last_commit: Optional[LastCommitInfo] = None + security: Optional[BlobSecurityInfo] = None + + def __init__(self, **kwargs): + self.path = kwargs.pop("path") + self.size = kwargs.pop("size") + self.blob_id = kwargs.pop("oid") + lfs = kwargs.pop("lfs", None) + if lfs is not None: + lfs = BlobLfsInfo(size=lfs["size"], sha256=lfs["oid"], pointer_size=lfs["pointerSize"]) + self.lfs = lfs + last_commit = kwargs.pop("lastCommit", None) or kwargs.pop("last_commit", None) + if last_commit is not None: + last_commit = LastCommitInfo( + oid=last_commit["id"], title=last_commit["title"], date=parse_datetime(last_commit["date"]) + ) + self.last_commit = last_commit + security = kwargs.pop("securityFileStatus", None) + if security is not None: + safe = security["status"] == "safe" + security = BlobSecurityInfo( + safe=safe, + status=security["status"], + av_scan=security["avScan"], + pickle_import_scan=security["pickleImportScan"], + ) + self.security = security + + # backwards compatibility + self.rfilename = self.path + self.lastCommit = self.last_commit + + +@dataclass +class RepoFolder: + """ + Contains information about a folder on the Hub. + + Attributes: + path (str): + folder path relative to the repo root. + tree_id (`str`): + The folder's git OID. + last_commit (`LastCommitInfo`, *optional*): + The folder's last commit metadata. Only defined if [`list_repo_tree`] and [`get_paths_info`] + are called with `expand=True`. + """ + + path: str + tree_id: str + last_commit: Optional[LastCommitInfo] = None + + def __init__(self, **kwargs): + self.path = kwargs.pop("path") + self.tree_id = kwargs.pop("oid") + last_commit = kwargs.pop("lastCommit", None) or kwargs.pop("last_commit", None) + if last_commit is not None: + last_commit = LastCommitInfo( + oid=last_commit["id"], title=last_commit["title"], date=parse_datetime(last_commit["date"]) + ) + self.last_commit = last_commit + + +@dataclass +class InferenceProviderMapping: + provider: "PROVIDER_T" # Provider name + hf_model_id: str # ID of the model on the Hugging Face Hub + provider_id: str # ID of the model on the provider's side + status: Literal["error", "live", "staging"] + task: str + + adapter: Optional[str] = None + adapter_weights_path: Optional[str] = None + type: Optional[Literal["single-model", "tag-filter"]] = None + + def __init__(self, **kwargs): + self.provider = kwargs.pop("provider") + self.hf_model_id = kwargs.pop("hf_model_id") + self.provider_id = kwargs.pop("providerId") + self.status = kwargs.pop("status") + self.task = kwargs.pop("task") + + self.adapter = kwargs.pop("adapter", None) + self.adapter_weights_path = kwargs.pop("adapterWeightsPath", None) + self.type = kwargs.pop("type", None) + self.__dict__.update(**kwargs) + + +@dataclass +class ModelInfo: + """ + Contains information about a model on the Hub. This object is returned by [`model_info`] and [`list_models`]. + + + + Most attributes of this class are optional. This is because the data returned by the Hub depends on the query made. + In general, the more specific the query, the more information is returned. On the contrary, when listing models + using [`list_models`] only a subset of the attributes are returned. + + + + Attributes: + id (`str`): + ID of model. + author (`str`, *optional*): + Author of the model. + sha (`str`, *optional*): + Repo SHA at this particular revision. + created_at (`datetime`, *optional*): + Date of creation of the repo on the Hub. Note that the lowest value is `2022-03-02T23:29:04.000Z`, + corresponding to the date when we began to store creation dates. + last_modified (`datetime`, *optional*): + Date of last commit to the repo. + private (`bool`): + Is the repo private. + disabled (`bool`, *optional*): + Is the repo disabled. + downloads (`int`): + Number of downloads of the model over the last 30 days. + downloads_all_time (`int`): + Cumulated number of downloads of the model since its creation. + gated (`Literal["auto", "manual", False]`, *optional*): + Is the repo gated. + If so, whether there is manual or automatic approval. + gguf (`Dict`, *optional*): + GGUF information of the model. + inference (`Literal["warm"]`, *optional*): + Status of the model on Inference Providers. Warm if the model is served by at least one provider. + inference_provider_mapping (`List[InferenceProviderMapping]`, *optional*): + A list of [`InferenceProviderMapping`] ordered after the user's provider order. + likes (`int`): + Number of likes of the model. + library_name (`str`, *optional*): + Library associated with the model. + tags (`List[str]`): + List of tags of the model. Compared to `card_data.tags`, contains extra tags computed by the Hub + (e.g. supported libraries, model's arXiv). + pipeline_tag (`str`, *optional*): + Pipeline tag associated with the model. + mask_token (`str`, *optional*): + Mask token used by the model. + widget_data (`Any`, *optional*): + Widget data associated with the model. + model_index (`Dict`, *optional*): + Model index for evaluation. + config (`Dict`, *optional*): + Model configuration. + transformers_info (`TransformersInfo`, *optional*): + Transformers-specific info (auto class, processor, etc.) associated with the model. + trending_score (`int`, *optional*): + Trending score of the model. + card_data (`ModelCardData`, *optional*): + Model Card Metadata as a [`huggingface_hub.repocard_data.ModelCardData`] object. + siblings (`List[RepoSibling]`): + List of [`huggingface_hub.hf_api.RepoSibling`] objects that constitute the model. + spaces (`List[str]`, *optional*): + List of spaces using the model. + safetensors (`SafeTensorsInfo`, *optional*): + Model's safetensors information. + security_repo_status (`Dict`, *optional*): + Model's security scan status. + """ + + id: str + author: Optional[str] + sha: Optional[str] + created_at: Optional[datetime] + last_modified: Optional[datetime] + private: Optional[bool] + disabled: Optional[bool] + downloads: Optional[int] + downloads_all_time: Optional[int] + gated: Optional[Literal["auto", "manual", False]] + gguf: Optional[Dict] + inference: Optional[Literal["warm"]] + inference_provider_mapping: Optional[List[InferenceProviderMapping]] + likes: Optional[int] + library_name: Optional[str] + tags: Optional[List[str]] + pipeline_tag: Optional[str] + mask_token: Optional[str] + card_data: Optional[ModelCardData] + widget_data: Optional[Any] + model_index: Optional[Dict] + config: Optional[Dict] + transformers_info: Optional[TransformersInfo] + trending_score: Optional[int] + siblings: Optional[List[RepoSibling]] + spaces: Optional[List[str]] + safetensors: Optional[SafeTensorsInfo] + security_repo_status: Optional[Dict] + xet_enabled: Optional[bool] + + def __init__(self, **kwargs): + self.id = kwargs.pop("id") + self.author = kwargs.pop("author", None) + self.sha = kwargs.pop("sha", None) + last_modified = kwargs.pop("lastModified", None) or kwargs.pop("last_modified", None) + self.last_modified = parse_datetime(last_modified) if last_modified else None + created_at = kwargs.pop("createdAt", None) or kwargs.pop("created_at", None) + self.created_at = parse_datetime(created_at) if created_at else None + self.private = kwargs.pop("private", None) + self.gated = kwargs.pop("gated", None) + self.disabled = kwargs.pop("disabled", None) + self.downloads = kwargs.pop("downloads", None) + self.downloads_all_time = kwargs.pop("downloadsAllTime", None) + self.likes = kwargs.pop("likes", None) + self.library_name = kwargs.pop("library_name", None) + self.gguf = kwargs.pop("gguf", None) + + self.inference = kwargs.pop("inference", None) + + # little hack to simplify Inference Providers logic and make it backward and forward compatible + # right now, API returns a dict on model_info and a list on list_models. Let's harmonize to list. + mapping = kwargs.pop("inferenceProviderMapping", None) + if isinstance(mapping, list): + self.inference_provider_mapping = [ + InferenceProviderMapping(**{**value, "hf_model_id": self.id}) for value in mapping + ] + elif isinstance(mapping, dict): + self.inference_provider_mapping = [ + InferenceProviderMapping(**{**value, "hf_model_id": self.id, "provider": provider}) + for provider, value in mapping.items() + ] + elif mapping is None: + self.inference_provider_mapping = None + else: + raise ValueError( + f"Unexpected type for `inferenceProviderMapping`. Expecting `dict` or `list`. Got {mapping}." + ) + + self.tags = kwargs.pop("tags", None) + self.pipeline_tag = kwargs.pop("pipeline_tag", None) + self.mask_token = kwargs.pop("mask_token", None) + self.trending_score = kwargs.pop("trendingScore", None) + + card_data = kwargs.pop("cardData", None) or kwargs.pop("card_data", None) + self.card_data = ( + ModelCardData(**card_data, ignore_metadata_errors=True) if isinstance(card_data, dict) else card_data + ) + + self.widget_data = kwargs.pop("widgetData", None) + self.model_index = kwargs.pop("model-index", None) or kwargs.pop("model_index", None) + self.config = kwargs.pop("config", None) + transformers_info = kwargs.pop("transformersInfo", None) or kwargs.pop("transformers_info", None) + self.transformers_info = TransformersInfo(**transformers_info) if transformers_info else None + siblings = kwargs.pop("siblings", None) + self.siblings = ( + [ + RepoSibling( + rfilename=sibling["rfilename"], + size=sibling.get("size"), + blob_id=sibling.get("blobId"), + lfs=( + BlobLfsInfo( + size=sibling["lfs"]["size"], + sha256=sibling["lfs"]["sha256"], + pointer_size=sibling["lfs"]["pointerSize"], + ) + if sibling.get("lfs") + else None + ), + ) + for sibling in siblings + ] + if siblings is not None + else None + ) + self.spaces = kwargs.pop("spaces", None) + safetensors = kwargs.pop("safetensors", None) + self.safetensors = ( + SafeTensorsInfo( + parameters=safetensors["parameters"], + total=safetensors["total"], + ) + if safetensors + else None + ) + self.security_repo_status = kwargs.pop("securityRepoStatus", None) + self.xet_enabled = kwargs.pop("xetEnabled", None) + # backwards compatibility + self.lastModified = self.last_modified + self.cardData = self.card_data + self.transformersInfo = self.transformers_info + self.__dict__.update(**kwargs) + + +@dataclass +class DatasetInfo: + """ + Contains information about a dataset on the Hub. This object is returned by [`dataset_info`] and [`list_datasets`]. + + + + Most attributes of this class are optional. This is because the data returned by the Hub depends on the query made. + In general, the more specific the query, the more information is returned. On the contrary, when listing datasets + using [`list_datasets`] only a subset of the attributes are returned. + + + + Attributes: + id (`str`): + ID of dataset. + author (`str`): + Author of the dataset. + sha (`str`): + Repo SHA at this particular revision. + created_at (`datetime`, *optional*): + Date of creation of the repo on the Hub. Note that the lowest value is `2022-03-02T23:29:04.000Z`, + corresponding to the date when we began to store creation dates. + last_modified (`datetime`, *optional*): + Date of last commit to the repo. + private (`bool`): + Is the repo private. + disabled (`bool`, *optional*): + Is the repo disabled. + gated (`Literal["auto", "manual", False]`, *optional*): + Is the repo gated. + If so, whether there is manual or automatic approval. + downloads (`int`): + Number of downloads of the dataset over the last 30 days. + downloads_all_time (`int`): + Cumulated number of downloads of the model since its creation. + likes (`int`): + Number of likes of the dataset. + tags (`List[str]`): + List of tags of the dataset. + card_data (`DatasetCardData`, *optional*): + Model Card Metadata as a [`huggingface_hub.repocard_data.DatasetCardData`] object. + siblings (`List[RepoSibling]`): + List of [`huggingface_hub.hf_api.RepoSibling`] objects that constitute the dataset. + paperswithcode_id (`str`, *optional*): + Papers with code ID of the dataset. + trending_score (`int`, *optional*): + Trending score of the dataset. + """ + + id: str + author: Optional[str] + sha: Optional[str] + created_at: Optional[datetime] + last_modified: Optional[datetime] + private: Optional[bool] + gated: Optional[Literal["auto", "manual", False]] + disabled: Optional[bool] + downloads: Optional[int] + downloads_all_time: Optional[int] + likes: Optional[int] + paperswithcode_id: Optional[str] + tags: Optional[List[str]] + trending_score: Optional[int] + card_data: Optional[DatasetCardData] + siblings: Optional[List[RepoSibling]] + xet_enabled: Optional[bool] + + def __init__(self, **kwargs): + self.id = kwargs.pop("id") + self.author = kwargs.pop("author", None) + self.sha = kwargs.pop("sha", None) + created_at = kwargs.pop("createdAt", None) or kwargs.pop("created_at", None) + self.created_at = parse_datetime(created_at) if created_at else None + last_modified = kwargs.pop("lastModified", None) or kwargs.pop("last_modified", None) + self.last_modified = parse_datetime(last_modified) if last_modified else None + self.private = kwargs.pop("private", None) + self.gated = kwargs.pop("gated", None) + self.disabled = kwargs.pop("disabled", None) + self.downloads = kwargs.pop("downloads", None) + self.downloads_all_time = kwargs.pop("downloadsAllTime", None) + self.likes = kwargs.pop("likes", None) + self.paperswithcode_id = kwargs.pop("paperswithcode_id", None) + self.tags = kwargs.pop("tags", None) + self.trending_score = kwargs.pop("trendingScore", None) + + card_data = kwargs.pop("cardData", None) or kwargs.pop("card_data", None) + self.card_data = ( + DatasetCardData(**card_data, ignore_metadata_errors=True) if isinstance(card_data, dict) else card_data + ) + siblings = kwargs.pop("siblings", None) + self.siblings = ( + [ + RepoSibling( + rfilename=sibling["rfilename"], + size=sibling.get("size"), + blob_id=sibling.get("blobId"), + lfs=( + BlobLfsInfo( + size=sibling["lfs"]["size"], + sha256=sibling["lfs"]["sha256"], + pointer_size=sibling["lfs"]["pointerSize"], + ) + if sibling.get("lfs") + else None + ), + ) + for sibling in siblings + ] + if siblings is not None + else None + ) + self.xet_enabled = kwargs.pop("xetEnabled", None) + # backwards compatibility + self.lastModified = self.last_modified + self.cardData = self.card_data + self.__dict__.update(**kwargs) + + +@dataclass +class SpaceInfo: + """ + Contains information about a Space on the Hub. This object is returned by [`space_info`] and [`list_spaces`]. + + + + Most attributes of this class are optional. This is because the data returned by the Hub depends on the query made. + In general, the more specific the query, the more information is returned. On the contrary, when listing spaces + using [`list_spaces`] only a subset of the attributes are returned. + + + + Attributes: + id (`str`): + ID of the Space. + author (`str`, *optional*): + Author of the Space. + sha (`str`, *optional*): + Repo SHA at this particular revision. + created_at (`datetime`, *optional*): + Date of creation of the repo on the Hub. Note that the lowest value is `2022-03-02T23:29:04.000Z`, + corresponding to the date when we began to store creation dates. + last_modified (`datetime`, *optional*): + Date of last commit to the repo. + private (`bool`): + Is the repo private. + gated (`Literal["auto", "manual", False]`, *optional*): + Is the repo gated. + If so, whether there is manual or automatic approval. + disabled (`bool`, *optional*): + Is the Space disabled. + host (`str`, *optional*): + Host URL of the Space. + subdomain (`str`, *optional*): + Subdomain of the Space. + likes (`int`): + Number of likes of the Space. + tags (`List[str]`): + List of tags of the Space. + siblings (`List[RepoSibling]`): + List of [`huggingface_hub.hf_api.RepoSibling`] objects that constitute the Space. + card_data (`SpaceCardData`, *optional*): + Space Card Metadata as a [`huggingface_hub.repocard_data.SpaceCardData`] object. + runtime (`SpaceRuntime`, *optional*): + Space runtime information as a [`huggingface_hub.hf_api.SpaceRuntime`] object. + sdk (`str`, *optional*): + SDK used by the Space. + models (`List[str]`, *optional*): + List of models used by the Space. + datasets (`List[str]`, *optional*): + List of datasets used by the Space. + trending_score (`int`, *optional*): + Trending score of the Space. + """ + + id: str + author: Optional[str] + sha: Optional[str] + created_at: Optional[datetime] + last_modified: Optional[datetime] + private: Optional[bool] + gated: Optional[Literal["auto", "manual", False]] + disabled: Optional[bool] + host: Optional[str] + subdomain: Optional[str] + likes: Optional[int] + sdk: Optional[str] + tags: Optional[List[str]] + siblings: Optional[List[RepoSibling]] + trending_score: Optional[int] + card_data: Optional[SpaceCardData] + runtime: Optional[SpaceRuntime] + models: Optional[List[str]] + datasets: Optional[List[str]] + xet_enabled: Optional[bool] + + def __init__(self, **kwargs): + self.id = kwargs.pop("id") + self.author = kwargs.pop("author", None) + self.sha = kwargs.pop("sha", None) + created_at = kwargs.pop("createdAt", None) or kwargs.pop("created_at", None) + self.created_at = parse_datetime(created_at) if created_at else None + last_modified = kwargs.pop("lastModified", None) or kwargs.pop("last_modified", None) + self.last_modified = parse_datetime(last_modified) if last_modified else None + self.private = kwargs.pop("private", None) + self.gated = kwargs.pop("gated", None) + self.disabled = kwargs.pop("disabled", None) + self.host = kwargs.pop("host", None) + self.subdomain = kwargs.pop("subdomain", None) + self.likes = kwargs.pop("likes", None) + self.sdk = kwargs.pop("sdk", None) + self.tags = kwargs.pop("tags", None) + self.trending_score = kwargs.pop("trendingScore", None) + card_data = kwargs.pop("cardData", None) or kwargs.pop("card_data", None) + self.card_data = ( + SpaceCardData(**card_data, ignore_metadata_errors=True) if isinstance(card_data, dict) else card_data + ) + siblings = kwargs.pop("siblings", None) + self.siblings = ( + [ + RepoSibling( + rfilename=sibling["rfilename"], + size=sibling.get("size"), + blob_id=sibling.get("blobId"), + lfs=( + BlobLfsInfo( + size=sibling["lfs"]["size"], + sha256=sibling["lfs"]["sha256"], + pointer_size=sibling["lfs"]["pointerSize"], + ) + if sibling.get("lfs") + else None + ), + ) + for sibling in siblings + ] + if siblings is not None + else None + ) + runtime = kwargs.pop("runtime", None) + self.runtime = SpaceRuntime(runtime) if runtime else None + self.models = kwargs.pop("models", None) + self.datasets = kwargs.pop("datasets", None) + self.xet_enabled = kwargs.pop("xetEnabled", None) + # backwards compatibility + self.lastModified = self.last_modified + self.cardData = self.card_data + self.__dict__.update(**kwargs) + + +@dataclass +class CollectionItem: + """ + Contains information about an item of a Collection (model, dataset, Space, paper or collection). + + Attributes: + item_object_id (`str`): + Unique ID of the item in the collection. + item_id (`str`): + ID of the underlying object on the Hub. Can be either a repo_id, a paper id or a collection slug. + e.g. `"jbilcke-hf/ai-comic-factory"`, `"2307.09288"`, `"celinah/cerebras-function-calling-682607169c35fbfa98b30b9a"`. + item_type (`str`): + Type of the underlying object. Can be one of `"model"`, `"dataset"`, `"space"`, `"paper"` or `"collection"`. + position (`int`): + Position of the item in the collection. + note (`str`, *optional*): + Note associated with the item, as plain text. + """ + + item_object_id: str # id in database + item_id: str # repo_id or paper id + item_type: str + position: int + note: Optional[str] = None + + def __init__( + self, + _id: str, + id: str, + type: CollectionItemType_T, + position: int, + note: Optional[Dict] = None, + **kwargs, + ) -> None: + self.item_object_id: str = _id # id in database + self.item_id: str = id # repo_id or paper id + # if the item is a collection, override item_id with the slug + slug = kwargs.get("slug") + if slug is not None: + self.item_id = slug # collection slug + self.item_type: CollectionItemType_T = type + self.position: int = position + self.note: str = note["text"] if note is not None else None + + +@dataclass +class Collection: + """ + Contains information about a Collection on the Hub. + + Attributes: + slug (`str`): + Slug of the collection. E.g. `"TheBloke/recent-models-64f9a55bb3115b4f513ec026"`. + title (`str`): + Title of the collection. E.g. `"Recent models"`. + owner (`str`): + Owner of the collection. E.g. `"TheBloke"`. + items (`List[CollectionItem]`): + List of items in the collection. + last_updated (`datetime`): + Date of the last update of the collection. + position (`int`): + Position of the collection in the list of collections of the owner. + private (`bool`): + Whether the collection is private or not. + theme (`str`): + Theme of the collection. E.g. `"green"`. + upvotes (`int`): + Number of upvotes of the collection. + description (`str`, *optional*): + Description of the collection, as plain text. + url (`str`): + (property) URL of the collection on the Hub. + """ + + slug: str + title: str + owner: str + items: List[CollectionItem] + last_updated: datetime + position: int + private: bool + theme: str + upvotes: int + description: Optional[str] = None + + def __init__(self, **kwargs) -> None: + self.slug = kwargs.pop("slug") + self.title = kwargs.pop("title") + self.owner = kwargs.pop("owner") + self.items = [CollectionItem(**item) for item in kwargs.pop("items")] + self.last_updated = parse_datetime(kwargs.pop("lastUpdated")) + self.position = kwargs.pop("position") + self.private = kwargs.pop("private") + self.theme = kwargs.pop("theme") + self.upvotes = kwargs.pop("upvotes") + self.description = kwargs.pop("description", None) + endpoint = kwargs.pop("endpoint", None) + if endpoint is None: + endpoint = constants.ENDPOINT + self._url = f"{endpoint}/collections/{self.slug}" + + @property + def url(self) -> str: + """Returns the URL of the collection on the Hub.""" + return self._url + + +@dataclass +class GitRefInfo: + """ + Contains information about a git reference for a repo on the Hub. + + Attributes: + name (`str`): + Name of the reference (e.g. tag name or branch name). + ref (`str`): + Full git ref on the Hub (e.g. `"refs/heads/main"` or `"refs/tags/v1.0"`). + target_commit (`str`): + OID of the target commit for the ref (e.g. `"e7da7f221d5bf496a48136c0cd264e630fe9fcc8"`) + """ + + name: str + ref: str + target_commit: str + + +@dataclass +class GitRefs: + """ + Contains information about all git references for a repo on the Hub. + + Object is returned by [`list_repo_refs`]. + + Attributes: + branches (`List[GitRefInfo]`): + A list of [`GitRefInfo`] containing information about branches on the repo. + converts (`List[GitRefInfo]`): + A list of [`GitRefInfo`] containing information about "convert" refs on the repo. + Converts are refs used (internally) to push preprocessed data in Dataset repos. + tags (`List[GitRefInfo]`): + A list of [`GitRefInfo`] containing information about tags on the repo. + pull_requests (`List[GitRefInfo]`, *optional*): + A list of [`GitRefInfo`] containing information about pull requests on the repo. + Only returned if `include_prs=True` is set. + """ + + branches: List[GitRefInfo] + converts: List[GitRefInfo] + tags: List[GitRefInfo] + pull_requests: Optional[List[GitRefInfo]] = None + + +@dataclass +class GitCommitInfo: + """ + Contains information about a git commit for a repo on the Hub. Check out [`list_repo_commits`] for more details. + + Attributes: + commit_id (`str`): + OID of the commit (e.g. `"e7da7f221d5bf496a48136c0cd264e630fe9fcc8"`) + authors (`List[str]`): + List of authors of the commit. + created_at (`datetime`): + Datetime when the commit was created. + title (`str`): + Title of the commit. This is a free-text value entered by the authors. + message (`str`): + Description of the commit. This is a free-text value entered by the authors. + formatted_title (`str`): + Title of the commit formatted as HTML. Only returned if `formatted=True` is set. + formatted_message (`str`): + Description of the commit formatted as HTML. Only returned if `formatted=True` is set. + """ + + commit_id: str + + authors: List[str] + created_at: datetime + title: str + message: str + + formatted_title: Optional[str] + formatted_message: Optional[str] + + +@dataclass +class UserLikes: + """ + Contains information about a user likes on the Hub. + + Attributes: + user (`str`): + Name of the user for which we fetched the likes. + total (`int`): + Total number of likes. + datasets (`List[str]`): + List of datasets liked by the user (as repo_ids). + models (`List[str]`): + List of models liked by the user (as repo_ids). + spaces (`List[str]`): + List of spaces liked by the user (as repo_ids). + """ + + # Metadata + user: str + total: int + + # User likes + datasets: List[str] + models: List[str] + spaces: List[str] + + +@dataclass +class Organization: + """ + Contains information about an organization on the Hub. + + Attributes: + avatar_url (`str`): + URL of the organization's avatar. + name (`str`): + Name of the organization on the Hub (unique). + fullname (`str`): + Organization's full name. + """ + + avatar_url: str + name: str + fullname: str + + def __init__(self, **kwargs) -> None: + self.avatar_url = kwargs.pop("avatarUrl", "") + self.name = kwargs.pop("name", "") + self.fullname = kwargs.pop("fullname", "") + + # forward compatibility + self.__dict__.update(**kwargs) + + +@dataclass +class User: + """ + Contains information about a user on the Hub. + + Attributes: + username (`str`): + Name of the user on the Hub (unique). + fullname (`str`): + User's full name. + avatar_url (`str`): + URL of the user's avatar. + details (`str`, *optional*): + User's details. + is_following (`bool`, *optional*): + Whether the authenticated user is following this user. + is_pro (`bool`, *optional*): + Whether the user is a pro user. + num_models (`int`, *optional*): + Number of models created by the user. + num_datasets (`int`, *optional*): + Number of datasets created by the user. + num_spaces (`int`, *optional*): + Number of spaces created by the user. + num_discussions (`int`, *optional*): + Number of discussions initiated by the user. + num_papers (`int`, *optional*): + Number of papers authored by the user. + num_upvotes (`int`, *optional*): + Number of upvotes received by the user. + num_likes (`int`, *optional*): + Number of likes given by the user. + num_following (`int`, *optional*): + Number of users this user is following. + num_followers (`int`, *optional*): + Number of users following this user. + orgs (list of [`Organization`]): + List of organizations the user is part of. + """ + + # Metadata + username: str + fullname: str + avatar_url: str + details: Optional[str] = None + is_following: Optional[bool] = None + is_pro: Optional[bool] = None + num_models: Optional[int] = None + num_datasets: Optional[int] = None + num_spaces: Optional[int] = None + num_discussions: Optional[int] = None + num_papers: Optional[int] = None + num_upvotes: Optional[int] = None + num_likes: Optional[int] = None + num_following: Optional[int] = None + num_followers: Optional[int] = None + orgs: List[Organization] = field(default_factory=list) + + def __init__(self, **kwargs) -> None: + self.username = kwargs.pop("user", "") + self.fullname = kwargs.pop("fullname", "") + self.avatar_url = kwargs.pop("avatarUrl", "") + self.is_following = kwargs.pop("isFollowing", None) + self.is_pro = kwargs.pop("isPro", None) + self.details = kwargs.pop("details", None) + self.num_models = kwargs.pop("numModels", None) + self.num_datasets = kwargs.pop("numDatasets", None) + self.num_spaces = kwargs.pop("numSpaces", None) + self.num_discussions = kwargs.pop("numDiscussions", None) + self.num_papers = kwargs.pop("numPapers", None) + self.num_upvotes = kwargs.pop("numUpvotes", None) + self.num_likes = kwargs.pop("numLikes", None) + self.num_following = kwargs.pop("numFollowing", None) + self.num_followers = kwargs.pop("numFollowers", None) + self.user_type = kwargs.pop("type", None) + self.orgs = [Organization(**org) for org in kwargs.pop("orgs", [])] + + # forward compatibility + self.__dict__.update(**kwargs) + + +@dataclass +class PaperInfo: + """ + Contains information about a paper on the Hub. + + Attributes: + id (`str`): + arXiv paper ID. + authors (`List[str]`, **optional**): + Names of paper authors + published_at (`datetime`, **optional**): + Date paper published. + title (`str`, **optional**): + Title of the paper. + summary (`str`, **optional**): + Summary of the paper. + upvotes (`int`, **optional**): + Number of upvotes for the paper on the Hub. + discussion_id (`str`, **optional**): + Discussion ID for the paper on the Hub. + source (`str`, **optional**): + Source of the paper. + comments (`int`, **optional**): + Number of comments for the paper on the Hub. + submitted_at (`datetime`, **optional**): + Date paper appeared in daily papers on the Hub. + submitted_by (`User`, **optional**): + Information about who submitted the daily paper. + """ + + id: str + authors: Optional[List[str]] + published_at: Optional[datetime] + title: Optional[str] + summary: Optional[str] + upvotes: Optional[int] + discussion_id: Optional[str] + source: Optional[str] + comments: Optional[int] + submitted_at: Optional[datetime] + submitted_by: Optional[User] + + def __init__(self, **kwargs) -> None: + paper = kwargs.pop("paper", {}) + self.id = kwargs.pop("id", None) or paper.pop("id", None) + authors = paper.pop("authors", None) or kwargs.pop("authors", None) + self.authors = [author.pop("name", None) for author in authors] if authors else None + published_at = paper.pop("publishedAt", None) or kwargs.pop("publishedAt", None) + self.published_at = parse_datetime(published_at) if published_at else None + self.title = kwargs.pop("title", None) + self.source = kwargs.pop("source", None) + self.summary = paper.pop("summary", None) or kwargs.pop("summary", None) + self.upvotes = paper.pop("upvotes", None) or kwargs.pop("upvotes", None) + self.discussion_id = paper.pop("discussionId", None) or kwargs.pop("discussionId", None) + self.comments = kwargs.pop("numComments", 0) + submitted_at = kwargs.pop("publishedAt", None) or kwargs.pop("submittedOnDailyAt", None) + self.submitted_at = parse_datetime(submitted_at) if submitted_at else None + submitted_by = kwargs.pop("submittedBy", None) or kwargs.pop("submittedOnDailyBy", None) + self.submitted_by = User(**submitted_by) if submitted_by else None + + # forward compatibility + self.__dict__.update(**kwargs) + + +@dataclass +class LFSFileInfo: + """ + Contains information about a file stored as LFS on a repo on the Hub. + + Used in the context of listing and permanently deleting LFS files from a repo to free-up space. + See [`list_lfs_files`] and [`permanently_delete_lfs_files`] for more details. + + Git LFS files are tracked using SHA-256 object IDs, rather than file paths, to optimize performance + This approach is necessary because a single object can be referenced by multiple paths across different commits, + making it impractical to search and resolve these connections. Check out [our documentation](https://huggingface.co/docs/hub/storage-limits#advanced-track-lfs-file-references) + to learn how to know which filename(s) is(are) associated with each SHA. + + Attributes: + file_oid (`str`): + SHA-256 object ID of the file. This is the identifier to pass when permanently deleting the file. + filename (`str`): + Possible filename for the LFS object. See the note above for more information. + oid (`str`): + OID of the LFS object. + pushed_at (`datetime`): + Date the LFS object was pushed to the repo. + ref (`str`, *optional*): + Ref where the LFS object has been pushed (if any). + size (`int`): + Size of the LFS object. + + Example: + ```py + >>> from huggingface_hub import HfApi + >>> api = HfApi() + >>> lfs_files = api.list_lfs_files("username/my-cool-repo") + + # Filter files files to delete based on a combination of `filename`, `pushed_at`, `ref` or `size`. + # e.g. select only LFS files in the "checkpoints" folder + >>> lfs_files_to_delete = (lfs_file for lfs_file in lfs_files if lfs_file.filename.startswith("checkpoints/")) + + # Permanently delete LFS files + >>> api.permanently_delete_lfs_files("username/my-cool-repo", lfs_files_to_delete) + ``` + """ + + file_oid: str + filename: str + oid: str + pushed_at: datetime + ref: Optional[str] + size: int + + def __init__(self, **kwargs) -> None: + self.file_oid = kwargs.pop("fileOid") + self.filename = kwargs.pop("filename") + self.oid = kwargs.pop("oid") + self.pushed_at = parse_datetime(kwargs.pop("pushedAt")) + self.ref = kwargs.pop("ref", None) + self.size = kwargs.pop("size") + + # forward compatibility + self.__dict__.update(**kwargs) + + +def future_compatible(fn: CallableT) -> CallableT: + """Wrap a method of `HfApi` to handle `run_as_future=True`. + + A method flagged as "future_compatible" will be called in a thread if `run_as_future=True` and return a + `concurrent.futures.Future` instance. Otherwise, it will be called normally and return the result. + """ + sig = inspect.signature(fn) + args_params = list(sig.parameters)[1:] # remove "self" from list + + @wraps(fn) + def _inner(self, *args, **kwargs): + # Get `run_as_future` value if provided (default to False) + if "run_as_future" in kwargs: + run_as_future = kwargs["run_as_future"] + kwargs["run_as_future"] = False # avoid recursion error + else: + run_as_future = False + for param, value in zip(args_params, args): + if param == "run_as_future": + run_as_future = value + break + + # Call the function in a thread if `run_as_future=True` + if run_as_future: + return self.run_as_future(fn, self, *args, **kwargs) + + # Otherwise, call the function normally + return fn(self, *args, **kwargs) + + _inner.is_future_compatible = True # type: ignore + return _inner # type: ignore + + +class HfApi: + """ + Client to interact with the Hugging Face Hub via HTTP. + + The client is initialized with some high-level settings used in all requests + made to the Hub (HF endpoint, authentication, user agents...). Using the `HfApi` + client is preferred but not mandatory as all of its public methods are exposed + directly at the root of `huggingface_hub`. + + Args: + endpoint (`str`, *optional*): + Endpoint of the Hub. Defaults to . + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + library_name (`str`, *optional*): + The name of the library that is making the HTTP request. Will be added to + the user-agent header. Example: `"transformers"`. + library_version (`str`, *optional*): + The version of the library that is making the HTTP request. Will be added + to the user-agent header. Example: `"4.24.0"`. + user_agent (`str`, `dict`, *optional*): + The user agent info in the form of a dictionary or a single string. It will + be completed with information about the installed packages. + headers (`dict`, *optional*): + Additional headers to be sent with each request. Example: `{"X-My-Header": "value"}`. + Headers passed here are taking precedence over the default headers. + """ + + def __init__( + self, + endpoint: Optional[str] = None, + token: Union[str, bool, None] = None, + library_name: Optional[str] = None, + library_version: Optional[str] = None, + user_agent: Union[Dict, str, None] = None, + headers: Optional[Dict[str, str]] = None, + ) -> None: + self.endpoint = endpoint if endpoint is not None else constants.ENDPOINT + self.token = token + self.library_name = library_name + self.library_version = library_version + self.user_agent = user_agent + self.headers = headers + self._thread_pool: Optional[ThreadPoolExecutor] = None + + def run_as_future(self, fn: Callable[..., R], *args, **kwargs) -> Future[R]: + """ + Run a method in the background and return a Future instance. + + The main goal is to run methods without blocking the main thread (e.g. to push data during a training). + Background jobs are queued to preserve order but are not ran in parallel. If you need to speed-up your scripts + by parallelizing lots of call to the API, you must setup and use your own [ThreadPoolExecutor](https://docs.python.org/3/library/concurrent.futures.html#threadpoolexecutor). + + Note: Most-used methods like [`upload_file`], [`upload_folder`] and [`create_commit`] have a `run_as_future: bool` + argument to directly call them in the background. This is equivalent to calling `api.run_as_future(...)` on them + but less verbose. + + Args: + fn (`Callable`): + The method to run in the background. + *args, **kwargs: + Arguments with which the method will be called. + + Return: + `Future`: a [Future](https://docs.python.org/3/library/concurrent.futures.html#future-objects) instance to + get the result of the task. + + Example: + ```py + >>> from huggingface_hub import HfApi + >>> api = HfApi() + >>> future = api.run_as_future(api.whoami) # instant + >>> future.done() + False + >>> future.result() # wait until complete and return result + (...) + >>> future.done() + True + ``` + """ + if self._thread_pool is None: + self._thread_pool = ThreadPoolExecutor(max_workers=1) + self._thread_pool + return self._thread_pool.submit(fn, *args, **kwargs) + + @validate_hf_hub_args + def whoami(self, token: Union[bool, str, None] = None) -> Dict: + """ + Call HF API to know "whoami". + + Args: + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + """ + # Get the effective token using the helper function get_token + effective_token = token or self.token or get_token() or True + r = get_session().get( + f"{self.endpoint}/api/whoami-v2", + headers=self._build_hf_headers(token=effective_token), + ) + try: + hf_raise_for_status(r) + except HTTPError as e: + if e.response.status_code == 401: + error_message = "Invalid user token." + # Check which token is the effective one and generate the error message accordingly + if effective_token == _get_token_from_google_colab(): + error_message += " The token from Google Colab vault is invalid. Please update it from the UI." + elif effective_token == _get_token_from_environment(): + error_message += ( + " The token from HF_TOKEN environment variable is invalid. " + "Note that HF_TOKEN takes precedence over `hf auth login`." + ) + elif effective_token == _get_token_from_file(): + error_message += " The token stored is invalid. Please run `hf auth login` to update it." + raise HTTPError(error_message, request=e.request, response=e.response) from e + raise + return r.json() + + @_deprecate_method( + version="1.0", + message=( + "Permissions are more complex than when `get_token_permission` was first introduced. " + "OAuth and fine-grain tokens allows for more detailed permissions. " + "If you need to know the permissions associated with a token, please use `whoami` and check the `'auth'` key." + ), + ) + def get_token_permission( + self, token: Union[bool, str, None] = None + ) -> Literal["read", "write", "fineGrained", None]: + """ + Check if a given `token` is valid and return its permissions. + + + + This method is deprecated and will be removed in version 1.0. Permissions are more complex than when + `get_token_permission` was first introduced. OAuth and fine-grain tokens allows for more detailed permissions. + If you need to know the permissions associated with a token, please use `whoami` and check the `'auth'` key. + + + + For more details about tokens, please refer to https://huggingface.co/docs/hub/security-tokens#what-are-user-access-tokens. + + Args: + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + `Literal["read", "write", "fineGrained", None]`: Permission granted by the token ("read" or "write"). Returns `None` if no + token passed, if token is invalid or if role is not returned by the server. This typically happens when the token is an OAuth token. + """ + try: + return self.whoami(token=token)["auth"]["accessToken"]["role"] + except (LocalTokenNotFoundError, HTTPError, KeyError): + return None + + def get_model_tags(self) -> Dict: + """ + List all valid model tags as a nested namespace object + """ + path = f"{self.endpoint}/api/models-tags-by-type" + r = get_session().get(path) + hf_raise_for_status(r) + return r.json() + + def get_dataset_tags(self) -> Dict: + """ + List all valid dataset tags as a nested namespace object. + """ + path = f"{self.endpoint}/api/datasets-tags-by-type" + r = get_session().get(path) + hf_raise_for_status(r) + return r.json() + + @_deprecate_arguments( + version="1.0", deprecated_args=["language", "library", "task", "tags"], custom_message="Use `filter` instead." + ) + @validate_hf_hub_args + def list_models( + self, + *, + # Search-query parameter + filter: Union[str, Iterable[str], None] = None, + author: Optional[str] = None, + apps: Optional[Union[str, List[str]]] = None, + gated: Optional[bool] = None, + inference: Optional[Literal["warm"]] = None, + inference_provider: Optional[Union[Literal["all"], "PROVIDER_T", List["PROVIDER_T"]]] = None, + model_name: Optional[str] = None, + trained_dataset: Optional[Union[str, List[str]]] = None, + search: Optional[str] = None, + pipeline_tag: Optional[str] = None, + emissions_thresholds: Optional[Tuple[float, float]] = None, + # Sorting and pagination parameters + sort: Union[Literal["last_modified"], str, None] = None, + direction: Optional[Literal[-1]] = None, + limit: Optional[int] = None, + # Additional data to fetch + expand: Optional[List[ExpandModelProperty_T]] = None, + full: Optional[bool] = None, + cardData: bool = False, + fetch_config: bool = False, + token: Union[bool, str, None] = None, + # Deprecated arguments - use `filter` instead + language: Optional[Union[str, List[str]]] = None, + library: Optional[Union[str, List[str]]] = None, + tags: Optional[Union[str, List[str]]] = None, + task: Optional[Union[str, List[str]]] = None, + ) -> Iterable[ModelInfo]: + """ + List models hosted on the Huggingface Hub, given some filters. + + Args: + filter (`str` or `Iterable[str]`, *optional*): + A string or list of string to filter models on the Hub. + Models can be filtered by library, language, task, tags, and more. + author (`str`, *optional*): + A string which identify the author (user or organization) of the + returned models. + apps (`str` or `List`, *optional*): + A string or list of strings to filter models on the Hub that + support the specified apps. Example values include `"ollama"` or `["ollama", "vllm"]`. + gated (`bool`, *optional*): + A boolean to filter models on the Hub that are gated or not. By default, all models are returned. + If `gated=True` is passed, only gated models are returned. + If `gated=False` is passed, only non-gated models are returned. + inference (`Literal["warm"]`, *optional*): + If "warm", filter models on the Hub currently served by at least one provider. + inference_provider (`Literal["all"]` or `str`, *optional*): + A string to filter models on the Hub that are served by a specific provider. + Pass `"all"` to get all models served by at least one provider. + library (`str` or `List`, *optional*): + Deprecated. Pass a library name in `filter` to filter models by library. + language (`str` or `List`, *optional*): + Deprecated. Pass a language in `filter` to filter models by language. + model_name (`str`, *optional*): + A string that contain complete or partial names for models on the + Hub, such as "bert" or "bert-base-cased" + task (`str` or `List`, *optional*): + Deprecated. Pass a task in `filter` to filter models by task. + trained_dataset (`str` or `List`, *optional*): + A string tag or a list of string tags of the trained dataset for a + model on the Hub. + tags (`str` or `List`, *optional*): + Deprecated. Pass tags in `filter` to filter models by tags. + search (`str`, *optional*): + A string that will be contained in the returned model ids. + pipeline_tag (`str`, *optional*): + A string pipeline tag to filter models on the Hub by, such as `summarization`. + emissions_thresholds (`Tuple`, *optional*): + A tuple of two ints or floats representing a minimum and maximum + carbon footprint to filter the resulting models with in grams. + sort (`Literal["last_modified"]` or `str`, *optional*): + The key with which to sort the resulting models. Possible values are "last_modified", "trending_score", + "created_at", "downloads" and "likes". + direction (`Literal[-1]` or `int`, *optional*): + Direction in which to sort. The value `-1` sorts by descending + order while all other values sort by ascending order. + limit (`int`, *optional*): + The limit on the number of models fetched. Leaving this option + to `None` fetches all models. + expand (`List[ExpandModelProperty_T]`, *optional*): + List properties to return in the response. When used, only the properties in the list will be returned. + This parameter cannot be used if `full`, `cardData` or `fetch_config` are passed. + Possible values are `"author"`, `"cardData"`, `"config"`, `"createdAt"`, `"disabled"`, `"downloads"`, `"downloadsAllTime"`, `"gated"`, `"gguf"`, `"inference"`, `"inferenceProviderMapping"`, `"lastModified"`, `"library_name"`, `"likes"`, `"mask_token"`, `"model-index"`, `"pipeline_tag"`, `"private"`, `"safetensors"`, `"sha"`, `"siblings"`, `"spaces"`, `"tags"`, `"transformersInfo"`, `"trendingScore"`, `"widgetData"`, `"resourceGroup"` and `"xetEnabled"`. + full (`bool`, *optional*): + Whether to fetch all model data, including the `last_modified`, + the `sha`, the files and the `tags`. This is set to `True` by + default when using a filter. + cardData (`bool`, *optional*): + Whether to grab the metadata for the model as well. Can contain + useful information such as carbon emissions, metrics, and + datasets trained on. + fetch_config (`bool`, *optional*): + Whether to fetch the model configs as well. This is not included + in `full` due to its size. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + + Returns: + `Iterable[ModelInfo]`: an iterable of [`huggingface_hub.hf_api.ModelInfo`] objects. + + Example: + + ```python + >>> from huggingface_hub import HfApi + + >>> api = HfApi() + + # List all models + >>> api.list_models() + + # List text classification models + >>> api.list_models(filter="text-classification") + + # List models from the KerasHub library + >>> api.list_models(filter="keras-hub") + + # List models served by Cohere + >>> api.list_models(inference_provider="cohere") + + # List models with "bert" in their name + >>> api.list_models(search="bert") + + # List models with "bert" in their name and pushed by google + >>> api.list_models(search="bert", author="google") + ``` + """ + if expand and (full or cardData or fetch_config): + raise ValueError("`expand` cannot be used if `full`, `cardData` or `fetch_config` are passed.") + + if emissions_thresholds is not None and not cardData: + raise ValueError("`emissions_thresholds` were passed without setting `cardData=True`.") + + path = f"{self.endpoint}/api/models" + headers = self._build_hf_headers(token=token) + params: Dict[str, Any] = {} + + # Build the filter list + filter_list: List[str] = [] + if filter: + filter_list.extend([filter] if isinstance(filter, str) else filter) + if library: + filter_list.extend([library] if isinstance(library, str) else library) + if task: + filter_list.extend([task] if isinstance(task, str) else task) + if trained_dataset: + if isinstance(trained_dataset, str): + trained_dataset = [trained_dataset] + for dataset in trained_dataset: + if not dataset.startswith("dataset:"): + dataset = f"dataset:{dataset}" + filter_list.append(dataset) + if language: + filter_list.extend([language] if isinstance(language, str) else language) + if tags: + filter_list.extend([tags] if isinstance(tags, str) else tags) + if len(filter_list) > 0: + params["filter"] = filter_list + + # Handle other query params + if author: + params["author"] = author + if apps: + if isinstance(apps, str): + apps = [apps] + params["apps"] = apps + if gated is not None: + params["gated"] = gated + if inference is not None: + params["inference"] = inference + if inference_provider is not None: + params["inference_provider"] = inference_provider + if pipeline_tag: + params["pipeline_tag"] = pipeline_tag + search_list = [] + if model_name: + search_list.append(model_name) + if search: + search_list.append(search) + if len(search_list) > 0: + params["search"] = search_list + if sort is not None: + params["sort"] = ( + "lastModified" + if sort == "last_modified" + else "trendingScore" + if sort == "trending_score" + else "createdAt" + if sort == "created_at" + else sort + ) + if direction is not None: + params["direction"] = direction + if limit is not None: + params["limit"] = limit + + # Request additional data + if full: + params["full"] = True + if fetch_config: + params["config"] = True + if cardData: + params["cardData"] = True + if expand: + params["expand"] = expand + + # `items` is a generator + items = paginate(path, params=params, headers=headers) + if limit is not None: + items = islice(items, limit) # Do not iterate over all pages + for item in items: + if "siblings" not in item: + item["siblings"] = None + model_info = ModelInfo(**item) + if emissions_thresholds is None or _is_emission_within_threshold(model_info, *emissions_thresholds): + yield model_info + + @_deprecate_arguments(version="1.0", deprecated_args=["tags"], custom_message="Use `filter` instead.") + @validate_hf_hub_args + def list_datasets( + self, + *, + # Search-query parameter + filter: Union[str, Iterable[str], None] = None, + author: Optional[str] = None, + benchmark: Optional[Union[str, List[str]]] = None, + dataset_name: Optional[str] = None, + gated: Optional[bool] = None, + language_creators: Optional[Union[str, List[str]]] = None, + language: Optional[Union[str, List[str]]] = None, + multilinguality: Optional[Union[str, List[str]]] = None, + size_categories: Optional[Union[str, List[str]]] = None, + task_categories: Optional[Union[str, List[str]]] = None, + task_ids: Optional[Union[str, List[str]]] = None, + search: Optional[str] = None, + # Sorting and pagination parameters + sort: Optional[Union[Literal["last_modified"], str]] = None, + direction: Optional[Literal[-1]] = None, + limit: Optional[int] = None, + # Additional data to fetch + expand: Optional[List[ExpandDatasetProperty_T]] = None, + full: Optional[bool] = None, + token: Union[bool, str, None] = None, + # Deprecated arguments - use `filter` instead + tags: Optional[Union[str, List[str]]] = None, + ) -> Iterable[DatasetInfo]: + """ + List datasets hosted on the Huggingface Hub, given some filters. + + Args: + filter (`str` or `Iterable[str]`, *optional*): + A string or list of string to filter datasets on the hub. + author (`str`, *optional*): + A string which identify the author of the returned datasets. + benchmark (`str` or `List`, *optional*): + A string or list of strings that can be used to identify datasets on + the Hub by their official benchmark. + dataset_name (`str`, *optional*): + A string or list of strings that can be used to identify datasets on + the Hub by its name, such as `SQAC` or `wikineural` + gated (`bool`, *optional*): + A boolean to filter datasets on the Hub that are gated or not. By default, all datasets are returned. + If `gated=True` is passed, only gated datasets are returned. + If `gated=False` is passed, only non-gated datasets are returned. + language_creators (`str` or `List`, *optional*): + A string or list of strings that can be used to identify datasets on + the Hub with how the data was curated, such as `crowdsourced` or + `machine_generated`. + language (`str` or `List`, *optional*): + A string or list of strings representing a two-character language to + filter datasets by on the Hub. + multilinguality (`str` or `List`, *optional*): + A string or list of strings representing a filter for datasets that + contain multiple languages. + size_categories (`str` or `List`, *optional*): + A string or list of strings that can be used to identify datasets on + the Hub by the size of the dataset such as `100K>> from huggingface_hub import HfApi + + >>> api = HfApi() + + # List all datasets + >>> api.list_datasets() + + + # List only the text classification datasets + >>> api.list_datasets(filter="task_categories:text-classification") + + + # List only the datasets in russian for language modeling + >>> api.list_datasets( + ... filter=("language:ru", "task_ids:language-modeling") + ... ) + + # List FiftyOne datasets (identified by the tag "fiftyone" in dataset card) + >>> api.list_datasets(tags="fiftyone") + ``` + + Example usage with the `search` argument: + + ```python + >>> from huggingface_hub import HfApi + + >>> api = HfApi() + + # List all datasets with "text" in their name + >>> api.list_datasets(search="text") + + # List all datasets with "text" in their name made by google + >>> api.list_datasets(search="text", author="google") + ``` + """ + if expand and full: + raise ValueError("`expand` cannot be used if `full` is passed.") + + path = f"{self.endpoint}/api/datasets" + headers = self._build_hf_headers(token=token) + params: Dict[str, Any] = {} + + # Build `filter` list + filter_list = [] + if filter is not None: + if isinstance(filter, str): + filter_list.append(filter) + else: + filter_list.extend(filter) + for key, value in ( + ("benchmark", benchmark), + ("language_creators", language_creators), + ("language", language), + ("multilinguality", multilinguality), + ("size_categories", size_categories), + ("task_categories", task_categories), + ("task_ids", task_ids), + ): + if value: + if isinstance(value, str): + value = [value] + for value_item in value: + if not value_item.startswith(f"{key}:"): + data = f"{key}:{value_item}" + filter_list.append(data) + if tags is not None: + filter_list.extend([tags] if isinstance(tags, str) else tags) + if len(filter_list) > 0: + params["filter"] = filter_list + + # Handle other query params + if author: + params["author"] = author + if gated is not None: + params["gated"] = gated + search_list = [] + if dataset_name: + search_list.append(dataset_name) + if search: + search_list.append(search) + if len(search_list) > 0: + params["search"] = search_list + if sort is not None: + params["sort"] = ( + "lastModified" + if sort == "last_modified" + else "trendingScore" + if sort == "trending_score" + else "createdAt" + if sort == "created_at" + else sort + ) + if direction is not None: + params["direction"] = direction + if limit is not None: + params["limit"] = limit + + # Request additional data + if expand: + params["expand"] = expand + if full: + params["full"] = True + + items = paginate(path, params=params, headers=headers) + if limit is not None: + items = islice(items, limit) # Do not iterate over all pages + for item in items: + if "siblings" not in item: + item["siblings"] = None + yield DatasetInfo(**item) + + @validate_hf_hub_args + def list_spaces( + self, + *, + # Search-query parameter + filter: Union[str, Iterable[str], None] = None, + author: Optional[str] = None, + search: Optional[str] = None, + datasets: Union[str, Iterable[str], None] = None, + models: Union[str, Iterable[str], None] = None, + linked: bool = False, + # Sorting and pagination parameters + sort: Union[Literal["last_modified"], str, None] = None, + direction: Optional[Literal[-1]] = None, + limit: Optional[int] = None, + # Additional data to fetch + expand: Optional[List[ExpandSpaceProperty_T]] = None, + full: Optional[bool] = None, + token: Union[bool, str, None] = None, + ) -> Iterable[SpaceInfo]: + """ + List spaces hosted on the Huggingface Hub, given some filters. + + Args: + filter (`str` or `Iterable`, *optional*): + A string tag or list of tags that can be used to identify Spaces on the Hub. + author (`str`, *optional*): + A string which identify the author of the returned Spaces. + search (`str`, *optional*): + A string that will be contained in the returned Spaces. + datasets (`str` or `Iterable`, *optional*): + Whether to return Spaces that make use of a dataset. + The name of a specific dataset can be passed as a string. + models (`str` or `Iterable`, *optional*): + Whether to return Spaces that make use of a model. + The name of a specific model can be passed as a string. + linked (`bool`, *optional*): + Whether to return Spaces that make use of either a model or a dataset. + sort (`Literal["last_modified"]` or `str`, *optional*): + The key with which to sort the resulting models. Possible values are "last_modified", "trending_score", + "created_at" and "likes". + direction (`Literal[-1]` or `int`, *optional*): + Direction in which to sort. The value `-1` sorts by descending + order while all other values sort by ascending order. + limit (`int`, *optional*): + The limit on the number of Spaces fetched. Leaving this option + to `None` fetches all Spaces. + expand (`List[ExpandSpaceProperty_T]`, *optional*): + List properties to return in the response. When used, only the properties in the list will be returned. + This parameter cannot be used if `full` is passed. + Possible values are `"author"`, `"cardData"`, `"datasets"`, `"disabled"`, `"lastModified"`, `"createdAt"`, `"likes"`, `"models"`, `"private"`, `"runtime"`, `"sdk"`, `"siblings"`, `"sha"`, `"subdomain"`, `"tags"`, `"trendingScore"`, `"usedStorage"`, `"resourceGroup"` and `"xetEnabled"`. + full (`bool`, *optional*): + Whether to fetch all Spaces data, including the `last_modified`, `siblings` + and `card_data` fields. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + `Iterable[SpaceInfo]`: an iterable of [`huggingface_hub.hf_api.SpaceInfo`] objects. + """ + if expand and full: + raise ValueError("`expand` cannot be used if `full` is passed.") + + path = f"{self.endpoint}/api/spaces" + headers = self._build_hf_headers(token=token) + params: Dict[str, Any] = {} + if filter is not None: + params["filter"] = filter + if author is not None: + params["author"] = author + if search is not None: + params["search"] = search + if sort is not None: + params["sort"] = ( + "lastModified" + if sort == "last_modified" + else "trendingScore" + if sort == "trending_score" + else "createdAt" + if sort == "created_at" + else sort + ) + if direction is not None: + params["direction"] = direction + if limit is not None: + params["limit"] = limit + if linked: + params["linked"] = True + if datasets is not None: + params["datasets"] = datasets + if models is not None: + params["models"] = models + + # Request additional data + if expand: + params["expand"] = expand + if full: + params["full"] = True + + items = paginate(path, params=params, headers=headers) + if limit is not None: + items = islice(items, limit) # Do not iterate over all pages + for item in items: + if "siblings" not in item: + item["siblings"] = None + yield SpaceInfo(**item) + + @validate_hf_hub_args + def unlike( + self, + repo_id: str, + *, + token: Union[bool, str, None] = None, + repo_type: Optional[str] = None, + ) -> None: + """ + Unlike a given repo on the Hub (e.g. remove from favorite list). + + To prevent spam usage, it is not possible to `like` a repository from a script. + + See also [`list_liked_repos`]. + + Args: + repo_id (`str`): + The repository to unlike. Example: `"user/my-cool-model"`. + + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if unliking a dataset or space, `None` or + `"model"` if unliking a model. Default is `None`. + + Raises: + [`~utils.RepositoryNotFoundError`]: + If repository is not found (error 404): wrong repo_id/repo_type, private + but not authenticated or repo does not exist. + + Example: + ```python + >>> from huggingface_hub import list_liked_repos, unlike + >>> "gpt2" in list_liked_repos().models # we assume you have already liked gpt2 + True + >>> unlike("gpt2") + >>> "gpt2" in list_liked_repos().models + False + ``` + """ + if repo_type is None: + repo_type = constants.REPO_TYPE_MODEL + response = get_session().delete( + url=f"{self.endpoint}/api/{repo_type}s/{repo_id}/like", headers=self._build_hf_headers(token=token) + ) + hf_raise_for_status(response) + + @validate_hf_hub_args + def list_liked_repos( + self, + user: Optional[str] = None, + *, + token: Union[bool, str, None] = None, + ) -> UserLikes: + """ + List all public repos liked by a user on huggingface.co. + + This list is public so token is optional. If `user` is not passed, it defaults to + the logged in user. + + See also [`unlike`]. + + Args: + user (`str`, *optional*): + Name of the user for which you want to fetch the likes. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`UserLikes`]: object containing the user name and 3 lists of repo ids (1 for + models, 1 for datasets and 1 for Spaces). + + Raises: + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If `user` is not passed and no token found (either from argument or from machine). + + Example: + ```python + >>> from huggingface_hub import list_liked_repos + + >>> likes = list_liked_repos("julien-c") + + >>> likes.user + "julien-c" + + >>> likes.models + ["osanseviero/streamlit_1.15", "Xhaheen/ChatGPT_HF", ...] + ``` + """ + # User is either provided explicitly or retrieved from current token. + if user is None: + me = self.whoami(token=token) + if me["type"] == "user": + user = me["name"] + else: + raise ValueError( + "Cannot list liked repos. You must provide a 'user' as input or be logged in as a user." + ) + + path = f"{self.endpoint}/api/users/{user}/likes" + headers = self._build_hf_headers(token=token) + + likes = list(paginate(path, params={}, headers=headers)) + # Looping over a list of items similar to: + # { + # 'createdAt': '2021-09-09T21:53:27.000Z', + # 'repo': { + # 'name': 'PaddlePaddle/PaddleOCR', + # 'type': 'space' + # } + # } + # Let's loop 3 times over the received list. Less efficient but more straightforward to read. + return UserLikes( + user=user, + total=len(likes), + models=[like["repo"]["name"] for like in likes if like["repo"]["type"] == "model"], + datasets=[like["repo"]["name"] for like in likes if like["repo"]["type"] == "dataset"], + spaces=[like["repo"]["name"] for like in likes if like["repo"]["type"] == "space"], + ) + + @validate_hf_hub_args + def list_repo_likers( + self, + repo_id: str, + *, + repo_type: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> Iterable[User]: + """ + List all users who liked a given repo on the hugging Face Hub. + + See also [`list_liked_repos`]. + + Args: + repo_id (`str`): + The repository to retrieve . Example: `"user/my-cool-model"`. + + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if uploading to a dataset or + space, `None` or `"model"` if uploading to a model. Default is + `None`. + + Returns: + `Iterable[User]`: an iterable of [`huggingface_hub.hf_api.User`] objects. + """ + + # Construct the API endpoint + if repo_type is None: + repo_type = constants.REPO_TYPE_MODEL + path = f"{self.endpoint}/api/{repo_type}s/{repo_id}/likers" + for liker in paginate(path, params={}, headers=self._build_hf_headers(token=token)): + yield User(username=liker["user"], fullname=liker["fullname"], avatar_url=liker["avatarUrl"]) + + @validate_hf_hub_args + def model_info( + self, + repo_id: str, + *, + revision: Optional[str] = None, + timeout: Optional[float] = None, + securityStatus: Optional[bool] = None, + files_metadata: bool = False, + expand: Optional[List[ExpandModelProperty_T]] = None, + token: Union[bool, str, None] = None, + ) -> ModelInfo: + """ + Get info on one specific model on huggingface.co + + Model can be private if you pass an acceptable token or are logged in. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + revision (`str`, *optional*): + The revision of the model repository from which to get the + information. + timeout (`float`, *optional*): + Whether to set a timeout for the request to the Hub. + securityStatus (`bool`, *optional*): + Whether to retrieve the security status from the model + repository as well. The security status will be returned in the `security_repo_status` field. + files_metadata (`bool`, *optional*): + Whether or not to retrieve metadata for files in the repository + (size, LFS metadata, etc). Defaults to `False`. + expand (`List[ExpandModelProperty_T]`, *optional*): + List properties to return in the response. When used, only the properties in the list will be returned. + This parameter cannot be used if `securityStatus` or `files_metadata` are passed. + Possible values are `"author"`, `"baseModels"`, `"cardData"`, `"childrenModelCount"`, `"config"`, `"createdAt"`, `"disabled"`, `"downloads"`, `"downloadsAllTime"`, `"gated"`, `"gguf"`, `"inference"`, `"inferenceProviderMapping"`, `"lastModified"`, `"library_name"`, `"likes"`, `"mask_token"`, `"model-index"`, `"pipeline_tag"`, `"private"`, `"safetensors"`, `"sha"`, `"siblings"`, `"spaces"`, `"tags"`, `"transformersInfo"`, `"trendingScore"`, `"widgetData"`, `"usedStorage"`, `"resourceGroup"` and `"xetEnabled"`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`huggingface_hub.hf_api.ModelInfo`]: The model repository information. + + + + Raises the following errors: + + - [`~utils.RepositoryNotFoundError`] + If the repository to download from cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + - [`~utils.RevisionNotFoundError`] + If the revision to download from cannot be found. + + + """ + if expand and (securityStatus or files_metadata): + raise ValueError("`expand` cannot be used if `securityStatus` or `files_metadata` are set.") + + headers = self._build_hf_headers(token=token) + path = ( + f"{self.endpoint}/api/models/{repo_id}" + if revision is None + else (f"{self.endpoint}/api/models/{repo_id}/revision/{quote(revision, safe='')}") + ) + params: Dict = {} + if securityStatus: + params["securityStatus"] = True + if files_metadata: + params["blobs"] = True + if expand: + params["expand"] = expand + r = get_session().get(path, headers=headers, timeout=timeout, params=params) + hf_raise_for_status(r) + data = r.json() + return ModelInfo(**data) + + @validate_hf_hub_args + def dataset_info( + self, + repo_id: str, + *, + revision: Optional[str] = None, + timeout: Optional[float] = None, + files_metadata: bool = False, + expand: Optional[List[ExpandDatasetProperty_T]] = None, + token: Union[bool, str, None] = None, + ) -> DatasetInfo: + """ + Get info on one specific dataset on huggingface.co. + + Dataset can be private if you pass an acceptable token. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + revision (`str`, *optional*): + The revision of the dataset repository from which to get the + information. + timeout (`float`, *optional*): + Whether to set a timeout for the request to the Hub. + files_metadata (`bool`, *optional*): + Whether or not to retrieve metadata for files in the repository + (size, LFS metadata, etc). Defaults to `False`. + expand (`List[ExpandDatasetProperty_T]`, *optional*): + List properties to return in the response. When used, only the properties in the list will be returned. + This parameter cannot be used if `files_metadata` is passed. + Possible values are `"author"`, `"cardData"`, `"citation"`, `"createdAt"`, `"disabled"`, `"description"`, `"downloads"`, `"downloadsAllTime"`, `"gated"`, `"lastModified"`, `"likes"`, `"paperswithcode_id"`, `"private"`, `"siblings"`, `"sha"`, `"tags"`, `"trendingScore"`,`"usedStorage"`, `"resourceGroup"` and `"xetEnabled"`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`hf_api.DatasetInfo`]: The dataset repository information. + + + + Raises the following errors: + + - [`~utils.RepositoryNotFoundError`] + If the repository to download from cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + - [`~utils.RevisionNotFoundError`] + If the revision to download from cannot be found. + + + """ + if expand and files_metadata: + raise ValueError("`expand` cannot be used if `files_metadata` is set.") + + headers = self._build_hf_headers(token=token) + path = ( + f"{self.endpoint}/api/datasets/{repo_id}" + if revision is None + else (f"{self.endpoint}/api/datasets/{repo_id}/revision/{quote(revision, safe='')}") + ) + params: Dict = {} + if files_metadata: + params["blobs"] = True + if expand: + params["expand"] = expand + + r = get_session().get(path, headers=headers, timeout=timeout, params=params) + hf_raise_for_status(r) + data = r.json() + return DatasetInfo(**data) + + @validate_hf_hub_args + def space_info( + self, + repo_id: str, + *, + revision: Optional[str] = None, + timeout: Optional[float] = None, + files_metadata: bool = False, + expand: Optional[List[ExpandSpaceProperty_T]] = None, + token: Union[bool, str, None] = None, + ) -> SpaceInfo: + """ + Get info on one specific Space on huggingface.co. + + Space can be private if you pass an acceptable token. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + revision (`str`, *optional*): + The revision of the space repository from which to get the + information. + timeout (`float`, *optional*): + Whether to set a timeout for the request to the Hub. + files_metadata (`bool`, *optional*): + Whether or not to retrieve metadata for files in the repository + (size, LFS metadata, etc). Defaults to `False`. + expand (`List[ExpandSpaceProperty_T]`, *optional*): + List properties to return in the response. When used, only the properties in the list will be returned. + This parameter cannot be used if `full` is passed. + Possible values are `"author"`, `"cardData"`, `"createdAt"`, `"datasets"`, `"disabled"`, `"lastModified"`, `"likes"`, `"models"`, `"private"`, `"runtime"`, `"sdk"`, `"siblings"`, `"sha"`, `"subdomain"`, `"tags"`, `"trendingScore"`, `"usedStorage"`, `"resourceGroup"` and `"xetEnabled"`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`~hf_api.SpaceInfo`]: The space repository information. + + + + Raises the following errors: + + - [`~utils.RepositoryNotFoundError`] + If the repository to download from cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + - [`~utils.RevisionNotFoundError`] + If the revision to download from cannot be found. + + + """ + if expand and files_metadata: + raise ValueError("`expand` cannot be used if `files_metadata` is set.") + + headers = self._build_hf_headers(token=token) + path = ( + f"{self.endpoint}/api/spaces/{repo_id}" + if revision is None + else (f"{self.endpoint}/api/spaces/{repo_id}/revision/{quote(revision, safe='')}") + ) + params: Dict = {} + if files_metadata: + params["blobs"] = True + if expand: + params["expand"] = expand + + r = get_session().get(path, headers=headers, timeout=timeout, params=params) + hf_raise_for_status(r) + data = r.json() + return SpaceInfo(**data) + + @validate_hf_hub_args + def repo_info( + self, + repo_id: str, + *, + revision: Optional[str] = None, + repo_type: Optional[str] = None, + timeout: Optional[float] = None, + files_metadata: bool = False, + expand: Optional[Union[ExpandModelProperty_T, ExpandDatasetProperty_T, ExpandSpaceProperty_T]] = None, + token: Union[bool, str, None] = None, + ) -> Union[ModelInfo, DatasetInfo, SpaceInfo]: + """ + Get the info object for a given repo of a given type. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + revision (`str`, *optional*): + The revision of the repository from which to get the + information. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if getting repository info from a dataset or a space, + `None` or `"model"` if getting repository info from a model. Default is `None`. + timeout (`float`, *optional*): + Whether to set a timeout for the request to the Hub. + expand (`ExpandModelProperty_T` or `ExpandDatasetProperty_T` or `ExpandSpaceProperty_T`, *optional*): + List properties to return in the response. When used, only the properties in the list will be returned. + This parameter cannot be used if `files_metadata` is passed. + For an exhaustive list of available properties, check out [`model_info`], [`dataset_info`] or [`space_info`]. + files_metadata (`bool`, *optional*): + Whether or not to retrieve metadata for files in the repository + (size, LFS metadata, etc). Defaults to `False`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + `Union[SpaceInfo, DatasetInfo, ModelInfo]`: The repository information, as a + [`huggingface_hub.hf_api.DatasetInfo`], [`huggingface_hub.hf_api.ModelInfo`] + or [`huggingface_hub.hf_api.SpaceInfo`] object. + + + + Raises the following errors: + + - [`~utils.RepositoryNotFoundError`] + If the repository to download from cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + - [`~utils.RevisionNotFoundError`] + If the revision to download from cannot be found. + + + """ + if repo_type is None or repo_type == "model": + method = self.model_info + elif repo_type == "dataset": + method = self.dataset_info # type: ignore + elif repo_type == "space": + method = self.space_info # type: ignore + else: + raise ValueError("Unsupported repo type.") + return method( + repo_id, + revision=revision, + token=token, + timeout=timeout, + expand=expand, # type: ignore[arg-type] + files_metadata=files_metadata, + ) + + @validate_hf_hub_args + def repo_exists( + self, + repo_id: str, + *, + repo_type: Optional[str] = None, + token: Union[str, bool, None] = None, + ) -> bool: + """ + Checks if a repository exists on the Hugging Face Hub. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if getting repository info from a dataset or a space, + `None` or `"model"` if getting repository info from a model. Default is `None`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + True if the repository exists, False otherwise. + + Examples: + ```py + >>> from huggingface_hub import repo_exists + >>> repo_exists("google/gemma-7b") + True + >>> repo_exists("google/not-a-repo") + False + ``` + """ + try: + self.repo_info(repo_id=repo_id, repo_type=repo_type, token=token) + return True + except GatedRepoError: + return True # we don't have access but it exists + except RepositoryNotFoundError: + return False + + @validate_hf_hub_args + def revision_exists( + self, + repo_id: str, + revision: str, + *, + repo_type: Optional[str] = None, + token: Union[str, bool, None] = None, + ) -> bool: + """ + Checks if a specific revision exists on a repo on the Hugging Face Hub. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + revision (`str`): + The revision of the repository to check. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if getting repository info from a dataset or a space, + `None` or `"model"` if getting repository info from a model. Default is `None`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + True if the repository and the revision exists, False otherwise. + + Examples: + ```py + >>> from huggingface_hub import revision_exists + >>> revision_exists("google/gemma-7b", "float16") + True + >>> revision_exists("google/gemma-7b", "not-a-revision") + False + ``` + """ + try: + self.repo_info(repo_id=repo_id, revision=revision, repo_type=repo_type, token=token) + return True + except RevisionNotFoundError: + return False + except RepositoryNotFoundError: + return False + + @validate_hf_hub_args + def file_exists( + self, + repo_id: str, + filename: str, + *, + repo_type: Optional[str] = None, + revision: Optional[str] = None, + token: Union[str, bool, None] = None, + ) -> bool: + """ + Checks if a file exists in a repository on the Hugging Face Hub. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + filename (`str`): + The name of the file to check, for example: + `"config.json"` + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if getting repository info from a dataset or a space, + `None` or `"model"` if getting repository info from a model. Default is `None`. + revision (`str`, *optional*): + The revision of the repository from which to get the information. Defaults to `"main"` branch. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + True if the file exists, False otherwise. + + Examples: + ```py + >>> from huggingface_hub import file_exists + >>> file_exists("bigcode/starcoder", "config.json") + True + >>> file_exists("bigcode/starcoder", "not-a-file") + False + >>> file_exists("bigcode/not-a-repo", "config.json") + False + ``` + """ + url = hf_hub_url( + repo_id=repo_id, repo_type=repo_type, revision=revision, filename=filename, endpoint=self.endpoint + ) + try: + if token is None: + token = self.token + get_hf_file_metadata(url, token=token) + return True + except GatedRepoError: # raise specifically on gated repo + raise + except (RepositoryNotFoundError, EntryNotFoundError, RevisionNotFoundError): + return False + + @validate_hf_hub_args + def list_repo_files( + self, + repo_id: str, + *, + revision: Optional[str] = None, + repo_type: Optional[str] = None, + token: Union[str, bool, None] = None, + ) -> List[str]: + """ + Get the list of files in a given repo. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated by a `/`. + revision (`str`, *optional*): + The revision of the repository from which to get the information. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if uploading to a dataset or space, `None` or `"model"` if uploading to + a model. Default is `None`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + `List[str]`: the list of files in a given repository. + """ + return [ + f.rfilename + for f in self.list_repo_tree( + repo_id=repo_id, recursive=True, revision=revision, repo_type=repo_type, token=token + ) + if isinstance(f, RepoFile) + ] + + @validate_hf_hub_args + def list_repo_tree( + self, + repo_id: str, + path_in_repo: Optional[str] = None, + *, + recursive: bool = False, + expand: bool = False, + revision: Optional[str] = None, + repo_type: Optional[str] = None, + token: Union[str, bool, None] = None, + ) -> Iterable[Union[RepoFile, RepoFolder]]: + """ + List a repo tree's files and folders and get information about them. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated by a `/`. + path_in_repo (`str`, *optional*): + Relative path of the tree (folder) in the repo, for example: + `"checkpoints/1fec34a/results"`. Will default to the root tree (folder) of the repository. + recursive (`bool`, *optional*, defaults to `False`): + Whether to list tree's files and folders recursively. + expand (`bool`, *optional*, defaults to `False`): + Whether to fetch more information about the tree's files and folders (e.g. last commit and files' security scan results). This + operation is more expensive for the server so only 50 results are returned per page (instead of 1000). + As pagination is implemented in `huggingface_hub`, this is transparent for you except for the time it + takes to get the results. + revision (`str`, *optional*): + The revision of the repository from which to get the tree. Defaults to `"main"` branch. + repo_type (`str`, *optional*): + The type of the repository from which to get the tree (`"model"`, `"dataset"` or `"space"`. + Defaults to `"model"`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + `Iterable[Union[RepoFile, RepoFolder]]`: + The information about the tree's files and folders, as an iterable of [`RepoFile`] and [`RepoFolder`] objects. The order of the files and folders is + not guaranteed. + + Raises: + [`~utils.RepositoryNotFoundError`]: + If repository is not found (error 404): wrong repo_id/repo_type, private but not authenticated or repo + does not exist. + [`~utils.RevisionNotFoundError`]: + If revision is not found (error 404) on the repo. + [`~utils.EntryNotFoundError`]: + If the tree (folder) does not exist (error 404) on the repo. + + Examples: + + Get information about a repo's tree. + ```py + >>> from huggingface_hub import list_repo_tree + >>> repo_tree = list_repo_tree("lysandre/arxiv-nlp") + >>> repo_tree + + >>> list(repo_tree) + [ + RepoFile(path='.gitattributes', size=391, blob_id='ae8c63daedbd4206d7d40126955d4e6ab1c80f8f', lfs=None, last_commit=None, security=None), + RepoFile(path='README.md', size=391, blob_id='43bd404b159de6fba7c2f4d3264347668d43af25', lfs=None, last_commit=None, security=None), + RepoFile(path='config.json', size=554, blob_id='2f9618c3a19b9a61add74f70bfb121335aeef666', lfs=None, last_commit=None, security=None), + RepoFile( + path='flax_model.msgpack', size=497764107, blob_id='8095a62ccb4d806da7666fcda07467e2d150218e', + lfs={'size': 497764107, 'sha256': 'd88b0d6a6ff9c3f8151f9d3228f57092aaea997f09af009eefd7373a77b5abb9', 'pointer_size': 134}, last_commit=None, security=None + ), + RepoFile(path='merges.txt', size=456318, blob_id='226b0752cac7789c48f0cb3ec53eda48b7be36cc', lfs=None, last_commit=None, security=None), + RepoFile( + path='pytorch_model.bin', size=548123560, blob_id='64eaa9c526867e404b68f2c5d66fd78e27026523', + lfs={'size': 548123560, 'sha256': '9be78edb5b928eba33aa88f431551348f7466ba9f5ef3daf1d552398722a5436', 'pointer_size': 134}, last_commit=None, security=None + ), + RepoFile(path='vocab.json', size=898669, blob_id='b00361fece0387ca34b4b8b8539ed830d644dbeb', lfs=None, last_commit=None, security=None)] + ] + ``` + + Get even more information about a repo's tree (last commit and files' security scan results) + ```py + >>> from huggingface_hub import list_repo_tree + >>> repo_tree = list_repo_tree("prompthero/openjourney-v4", expand=True) + >>> list(repo_tree) + [ + RepoFolder( + path='feature_extractor', + tree_id='aa536c4ea18073388b5b0bc791057a7296a00398', + last_commit={ + 'oid': '47b62b20b20e06b9de610e840282b7e6c3d51190', + 'title': 'Upload diffusers weights (#48)', + 'date': datetime.datetime(2023, 3, 21, 9, 5, 27, tzinfo=datetime.timezone.utc) + } + ), + RepoFolder( + path='safety_checker', + tree_id='65aef9d787e5557373fdf714d6c34d4fcdd70440', + last_commit={ + 'oid': '47b62b20b20e06b9de610e840282b7e6c3d51190', + 'title': 'Upload diffusers weights (#48)', + 'date': datetime.datetime(2023, 3, 21, 9, 5, 27, tzinfo=datetime.timezone.utc) + } + ), + RepoFile( + path='model_index.json', + size=582, + blob_id='d3d7c1e8c3e78eeb1640b8e2041ee256e24c9ee1', + lfs=None, + last_commit={ + 'oid': 'b195ed2d503f3eb29637050a886d77bd81d35f0e', + 'title': 'Fix deprecation warning by changing `CLIPFeatureExtractor` to `CLIPImageProcessor`. (#54)', + 'date': datetime.datetime(2023, 5, 15, 21, 41, 59, tzinfo=datetime.timezone.utc) + }, + security={ + 'safe': True, + 'av_scan': {'virusFound': False, 'virusNames': None}, + 'pickle_import_scan': None + } + ) + ... + ] + ``` + """ + repo_type = repo_type or constants.REPO_TYPE_MODEL + revision = quote(revision, safe="") if revision is not None else constants.DEFAULT_REVISION + headers = self._build_hf_headers(token=token) + + encoded_path_in_repo = "/" + quote(path_in_repo, safe="") if path_in_repo else "" + tree_url = f"{self.endpoint}/api/{repo_type}s/{repo_id}/tree/{revision}{encoded_path_in_repo}" + for path_info in paginate(path=tree_url, headers=headers, params={"recursive": recursive, "expand": expand}): + yield (RepoFile(**path_info) if path_info["type"] == "file" else RepoFolder(**path_info)) + + @validate_hf_hub_args + def list_repo_refs( + self, + repo_id: str, + *, + repo_type: Optional[str] = None, + include_pull_requests: bool = False, + token: Union[str, bool, None] = None, + ) -> GitRefs: + """ + Get the list of refs of a given repo (both tags and branches). + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if listing refs from a dataset or a Space, + `None` or `"model"` if listing from a model. Default is `None`. + include_pull_requests (`bool`, *optional*): + Whether to include refs from pull requests in the list. Defaults to `False`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Example: + ```py + >>> from huggingface_hub import HfApi + >>> api = HfApi() + >>> api.list_repo_refs("gpt2") + GitRefs(branches=[GitRefInfo(name='main', ref='refs/heads/main', target_commit='e7da7f221d5bf496a48136c0cd264e630fe9fcc8')], converts=[], tags=[]) + + >>> api.list_repo_refs("bigcode/the-stack", repo_type='dataset') + GitRefs( + branches=[ + GitRefInfo(name='main', ref='refs/heads/main', target_commit='18edc1591d9ce72aa82f56c4431b3c969b210ae3'), + GitRefInfo(name='v1.1.a1', ref='refs/heads/v1.1.a1', target_commit='f9826b862d1567f3822d3d25649b0d6d22ace714') + ], + converts=[], + tags=[ + GitRefInfo(name='v1.0', ref='refs/tags/v1.0', target_commit='c37a8cd1e382064d8aced5e05543c5f7753834da') + ] + ) + ``` + + Returns: + [`GitRefs`]: object containing all information about branches and tags for a + repo on the Hub. + """ + repo_type = repo_type or constants.REPO_TYPE_MODEL + response = get_session().get( + f"{self.endpoint}/api/{repo_type}s/{repo_id}/refs", + headers=self._build_hf_headers(token=token), + params={"include_prs": 1} if include_pull_requests else {}, + ) + hf_raise_for_status(response) + data = response.json() + + def _format_as_git_ref_info(item: Dict) -> GitRefInfo: + return GitRefInfo(name=item["name"], ref=item["ref"], target_commit=item["targetCommit"]) + + return GitRefs( + branches=[_format_as_git_ref_info(item) for item in data["branches"]], + converts=[_format_as_git_ref_info(item) for item in data["converts"]], + tags=[_format_as_git_ref_info(item) for item in data["tags"]], + pull_requests=[_format_as_git_ref_info(item) for item in data["pullRequests"]] + if include_pull_requests + else None, + ) + + @validate_hf_hub_args + def list_repo_commits( + self, + repo_id: str, + *, + repo_type: Optional[str] = None, + token: Union[bool, str, None] = None, + revision: Optional[str] = None, + formatted: bool = False, + ) -> List[GitCommitInfo]: + """ + Get the list of commits of a given revision for a repo on the Hub. + + Commits are sorted by date (last commit first). + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated by a `/`. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if listing commits from a dataset or a Space, `None` or `"model"` if + listing from a model. Default is `None`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + revision (`str`, *optional*): + The git revision to commit from. Defaults to the head of the `"main"` branch. + formatted (`bool`): + Whether to return the HTML-formatted title and description of the commits. Defaults to False. + + Example: + ```py + >>> from huggingface_hub import HfApi + >>> api = HfApi() + + # Commits are sorted by date (last commit first) + >>> initial_commit = api.list_repo_commits("gpt2")[-1] + + # Initial commit is always a system commit containing the `.gitattributes` file. + >>> initial_commit + GitCommitInfo( + commit_id='9b865efde13a30c13e0a33e536cf3e4a5a9d71d8', + authors=['system'], + created_at=datetime.datetime(2019, 2, 18, 10, 36, 15, tzinfo=datetime.timezone.utc), + title='initial commit', + message='', + formatted_title=None, + formatted_message=None + ) + + # Create an empty branch by deriving from initial commit + >>> api.create_branch("gpt2", "new_empty_branch", revision=initial_commit.commit_id) + ``` + + Returns: + List[[`GitCommitInfo`]]: list of objects containing information about the commits for a repo on the Hub. + + Raises: + [`~utils.RepositoryNotFoundError`]: + If repository is not found (error 404): wrong repo_id/repo_type, private but not authenticated or repo + does not exist. + [`~utils.RevisionNotFoundError`]: + If revision is not found (error 404) on the repo. + """ + repo_type = repo_type or constants.REPO_TYPE_MODEL + revision = quote(revision, safe="") if revision is not None else constants.DEFAULT_REVISION + + # Paginate over results and return the list of commits. + return [ + GitCommitInfo( + commit_id=item["id"], + authors=[author["user"] for author in item["authors"]], + created_at=parse_datetime(item["date"]), + title=item["title"], + message=item["message"], + formatted_title=item.get("formatted", {}).get("title"), + formatted_message=item.get("formatted", {}).get("message"), + ) + for item in paginate( + f"{self.endpoint}/api/{repo_type}s/{repo_id}/commits/{revision}", + headers=self._build_hf_headers(token=token), + params={"expand[]": "formatted"} if formatted else {}, + ) + ] + + @validate_hf_hub_args + def get_paths_info( + self, + repo_id: str, + paths: Union[List[str], str], + *, + expand: bool = False, + revision: Optional[str] = None, + repo_type: Optional[str] = None, + token: Union[str, bool, None] = None, + ) -> List[Union[RepoFile, RepoFolder]]: + """ + Get information about a repo's paths. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated by a `/`. + paths (`Union[List[str], str]`, *optional*): + The paths to get information about. If a path do not exist, it is ignored without raising + an exception. + expand (`bool`, *optional*, defaults to `False`): + Whether to fetch more information about the paths (e.g. last commit and files' security scan results). This + operation is more expensive for the server so only 50 results are returned per page (instead of 1000). + As pagination is implemented in `huggingface_hub`, this is transparent for you except for the time it + takes to get the results. + revision (`str`, *optional*): + The revision of the repository from which to get the information. Defaults to `"main"` branch. + repo_type (`str`, *optional*): + The type of the repository from which to get the information (`"model"`, `"dataset"` or `"space"`. + Defaults to `"model"`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + `List[Union[RepoFile, RepoFolder]]`: + The information about the paths, as a list of [`RepoFile`] and [`RepoFolder`] objects. + + Raises: + [`~utils.RepositoryNotFoundError`]: + If repository is not found (error 404): wrong repo_id/repo_type, private but not authenticated or repo + does not exist. + [`~utils.RevisionNotFoundError`]: + If revision is not found (error 404) on the repo. + + Example: + ```py + >>> from huggingface_hub import get_paths_info + >>> paths_info = get_paths_info("allenai/c4", ["README.md", "en"], repo_type="dataset") + >>> paths_info + [ + RepoFile(path='README.md', size=2379, blob_id='f84cb4c97182890fc1dbdeaf1a6a468fd27b4fff', lfs=None, last_commit=None, security=None), + RepoFolder(path='en', tree_id='dc943c4c40f53d02b31ced1defa7e5f438d5862e', last_commit=None) + ] + ``` + """ + repo_type = repo_type or constants.REPO_TYPE_MODEL + revision = quote(revision, safe="") if revision is not None else constants.DEFAULT_REVISION + headers = self._build_hf_headers(token=token) + + response = get_session().post( + f"{self.endpoint}/api/{repo_type}s/{repo_id}/paths-info/{revision}", + data={ + "paths": paths if isinstance(paths, list) else [paths], + "expand": expand, + }, + headers=headers, + ) + hf_raise_for_status(response) + paths_info = response.json() + return [ + RepoFile(**path_info) if path_info["type"] == "file" else RepoFolder(**path_info) + for path_info in paths_info + ] + + @validate_hf_hub_args + def super_squash_history( + self, + repo_id: str, + *, + branch: Optional[str] = None, + commit_message: Optional[str] = None, + repo_type: Optional[str] = None, + token: Union[str, bool, None] = None, + ) -> None: + """Squash commit history on a branch for a repo on the Hub. + + Squashing the repo history is useful when you know you'll make hundreds of commits and you don't want to + clutter the history. Squashing commits can only be performed from the head of a branch. + + + + Once squashed, the commit history cannot be retrieved. This is a non-revertible operation. + + + + + + Once the history of a branch has been squashed, it is not possible to merge it back into another branch since + their history will have diverged. + + + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated by a `/`. + branch (`str`, *optional*): + The branch to squash. Defaults to the head of the `"main"` branch. + commit_message (`str`, *optional*): + The commit message to use for the squashed commit. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if listing commits from a dataset or a Space, `None` or `"model"` if + listing from a model. Default is `None`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Raises: + [`~utils.RepositoryNotFoundError`]: + If repository is not found (error 404): wrong repo_id/repo_type, private but not authenticated or repo + does not exist. + [`~utils.RevisionNotFoundError`]: + If the branch to squash cannot be found. + [`~utils.BadRequestError`]: + If invalid reference for a branch. You cannot squash history on tags. + + Example: + ```py + >>> from huggingface_hub import HfApi + >>> api = HfApi() + + # Create repo + >>> repo_id = api.create_repo("test-squash").repo_id + + # Make a lot of commits. + >>> api.upload_file(repo_id=repo_id, path_in_repo="file.txt", path_or_fileobj=b"content") + >>> api.upload_file(repo_id=repo_id, path_in_repo="lfs.bin", path_or_fileobj=b"content") + >>> api.upload_file(repo_id=repo_id, path_in_repo="file.txt", path_or_fileobj=b"another_content") + + # Squash history + >>> api.super_squash_history(repo_id=repo_id) + ``` + """ + if repo_type is None: + repo_type = constants.REPO_TYPE_MODEL + if repo_type not in constants.REPO_TYPES: + raise ValueError("Invalid repo type") + if branch is None: + branch = constants.DEFAULT_REVISION + + # Prepare request + url = f"{self.endpoint}/api/{repo_type}s/{repo_id}/super-squash/{quote(branch, safe='')}" + headers = self._build_hf_headers(token=token) + commit_message = commit_message or f"Super-squash branch '{branch}' using huggingface_hub" + + # Super-squash + response = get_session().post(url=url, headers=headers, json={"message": commit_message}) + hf_raise_for_status(response) + + @validate_hf_hub_args + def list_lfs_files( + self, + repo_id: str, + *, + repo_type: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> Iterable[LFSFileInfo]: + """ + List all LFS files in a repo on the Hub. + + This is primarily useful to count how much storage a repo is using and to eventually clean up large files + with [`permanently_delete_lfs_files`]. Note that this would be a permanent action that will affect all commits + referencing this deleted files and that cannot be undone. + + Args: + repo_id (`str`): + The repository for which you are listing LFS files. + repo_type (`str`, *optional*): + Type of repository. Set to `"dataset"` or `"space"` if listing from a dataset or space, `None` or + `"model"` if listing from a model. Default is `None`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + `Iterable[LFSFileInfo]`: An iterator of [`LFSFileInfo`] objects. + + Example: + ```py + >>> from huggingface_hub import HfApi + >>> api = HfApi() + >>> lfs_files = api.list_lfs_files("username/my-cool-repo") + + # Filter files files to delete based on a combination of `filename`, `pushed_at`, `ref` or `size`. + # e.g. select only LFS files in the "checkpoints" folder + >>> lfs_files_to_delete = (lfs_file for lfs_file in lfs_files if lfs_file.filename.startswith("checkpoints/")) + + # Permanently delete LFS files + >>> api.permanently_delete_lfs_files("username/my-cool-repo", lfs_files_to_delete) + ``` + """ + # Prepare request + if repo_type is None: + repo_type = constants.REPO_TYPE_MODEL + url = f"{self.endpoint}/api/{repo_type}s/{repo_id}/lfs-files" + headers = self._build_hf_headers(token=token) + + # Paginate over LFS items + for item in paginate(url, params={}, headers=headers): + yield LFSFileInfo(**item) + + @validate_hf_hub_args + def permanently_delete_lfs_files( + self, + repo_id: str, + lfs_files: Iterable[LFSFileInfo], + *, + rewrite_history: bool = True, + repo_type: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> None: + """ + Permanently delete LFS files from a repo on the Hub. + + + + This is a permanent action that will affect all commits referencing the deleted files and might corrupt your + repository. This is a non-revertible operation. Use it only if you know what you are doing. + + + + Args: + repo_id (`str`): + The repository for which you are listing LFS files. + lfs_files (`Iterable[LFSFileInfo]`): + An iterable of [`LFSFileInfo`] items to permanently delete from the repo. Use [`list_lfs_files`] to list + all LFS files from a repo. + rewrite_history (`bool`, *optional*, default to `True`): + Whether to rewrite repository history to remove file pointers referencing the deleted LFS files (recommended). + repo_type (`str`, *optional*): + Type of repository. Set to `"dataset"` or `"space"` if listing from a dataset or space, `None` or + `"model"` if listing from a model. Default is `None`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Example: + ```py + >>> from huggingface_hub import HfApi + >>> api = HfApi() + >>> lfs_files = api.list_lfs_files("username/my-cool-repo") + + # Filter files files to delete based on a combination of `filename`, `pushed_at`, `ref` or `size`. + # e.g. select only LFS files in the "checkpoints" folder + >>> lfs_files_to_delete = (lfs_file for lfs_file in lfs_files if lfs_file.filename.startswith("checkpoints/")) + + # Permanently delete LFS files + >>> api.permanently_delete_lfs_files("username/my-cool-repo", lfs_files_to_delete) + ``` + """ + # Prepare request + if repo_type is None: + repo_type = constants.REPO_TYPE_MODEL + url = f"{self.endpoint}/api/{repo_type}s/{repo_id}/lfs-files/batch" + headers = self._build_hf_headers(token=token) + + # Delete LFS items by batches of 1000 + for batch in chunk_iterable(lfs_files, 1000): + shas = [item.file_oid for item in batch] + if len(shas) == 0: + return + payload = { + "deletions": { + "sha": shas, + "rewriteHistory": rewrite_history, + } + } + response = get_session().post(url, headers=headers, json=payload) + hf_raise_for_status(response) + + @validate_hf_hub_args + def create_repo( + self, + repo_id: str, + *, + token: Union[str, bool, None] = None, + private: Optional[bool] = None, + repo_type: Optional[str] = None, + exist_ok: bool = False, + resource_group_id: Optional[str] = None, + space_sdk: Optional[str] = None, + space_hardware: Optional[SpaceHardware] = None, + space_storage: Optional[SpaceStorage] = None, + space_sleep_time: Optional[int] = None, + space_secrets: Optional[List[Dict[str, str]]] = None, + space_variables: Optional[List[Dict[str, str]]] = None, + ) -> RepoUrl: + """Create an empty repo on the HuggingFace Hub. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + private (`bool`, *optional*): + Whether to make the repo private. If `None` (default), the repo will be public unless the organization's default is private. This value is ignored if the repo already exists. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if uploading to a dataset or + space, `None` or `"model"` if uploading to a model. Default is + `None`. + exist_ok (`bool`, *optional*, defaults to `False`): + If `True`, do not raise an error if repo already exists. + resource_group_id (`str`, *optional*): + Resource group in which to create the repo. Resource groups is only available for Enterprise Hub organizations and + allow to define which members of the organization can access the resource. The ID of a resource group + can be found in the URL of the resource's page on the Hub (e.g. `"66670e5163145ca562cb1988"`). + To learn more about resource groups, see https://huggingface.co/docs/hub/en/security-resource-groups. + space_sdk (`str`, *optional*): + Choice of SDK to use if repo_type is "space". Can be "streamlit", "gradio", "docker", or "static". + space_hardware (`SpaceHardware` or `str`, *optional*): + Choice of Hardware if repo_type is "space". See [`SpaceHardware`] for a complete list. + space_storage (`SpaceStorage` or `str`, *optional*): + Choice of persistent storage tier. Example: `"small"`. See [`SpaceStorage`] for a complete list. + space_sleep_time (`int`, *optional*): + Number of seconds of inactivity to wait before a Space is put to sleep. Set to `-1` if you don't want + your Space to sleep (default behavior for upgraded hardware). For free hardware, you can't configure + the sleep time (value is fixed to 48 hours of inactivity). + See https://huggingface.co/docs/hub/spaces-gpus#sleep-time for more details. + space_secrets (`List[Dict[str, str]]`, *optional*): + A list of secret keys to set in your Space. Each item is in the form `{"key": ..., "value": ..., "description": ...}` where description is optional. + For more details, see https://huggingface.co/docs/hub/spaces-overview#managing-secrets. + space_variables (`List[Dict[str, str]]`, *optional*): + A list of public environment variables to set in your Space. Each item is in the form `{"key": ..., "value": ..., "description": ...}` where description is optional. + For more details, see https://huggingface.co/docs/hub/spaces-overview#managing-secrets-and-environment-variables. + + Returns: + [`RepoUrl`]: URL to the newly created repo. Value is a subclass of `str` containing + attributes like `endpoint`, `repo_type` and `repo_id`. + """ + organization, name = repo_id.split("/") if "/" in repo_id else (None, repo_id) + + path = f"{self.endpoint}/api/repos/create" + + if repo_type not in constants.REPO_TYPES: + raise ValueError("Invalid repo type") + + json: Dict[str, Any] = {"name": name, "organization": organization} + if private is not None: + json["private"] = private + if repo_type is not None: + json["type"] = repo_type + if repo_type == "space": + if space_sdk is None: + raise ValueError( + "No space_sdk provided. `create_repo` expects space_sdk to be one" + f" of {constants.SPACES_SDK_TYPES} when repo_type is 'space'`" + ) + if space_sdk not in constants.SPACES_SDK_TYPES: + raise ValueError(f"Invalid space_sdk. Please choose one of {constants.SPACES_SDK_TYPES}.") + json["sdk"] = space_sdk + + if space_sdk is not None and repo_type != "space": + warnings.warn("Ignoring provided space_sdk because repo_type is not 'space'.") + + function_args = [ + "space_hardware", + "space_storage", + "space_sleep_time", + "space_secrets", + "space_variables", + ] + json_keys = ["hardware", "storageTier", "sleepTimeSeconds", "secrets", "variables"] + values = [space_hardware, space_storage, space_sleep_time, space_secrets, space_variables] + + if repo_type == "space": + json.update({k: v for k, v in zip(json_keys, values) if v is not None}) + else: + provided_space_args = [key for key, value in zip(function_args, values) if value is not None] + + if provided_space_args: + warnings.warn(f"Ignoring provided {', '.join(provided_space_args)} because repo_type is not 'space'.") + + if getattr(self, "_lfsmultipartthresh", None): + # Testing purposes only. + # See https://github.com/huggingface/huggingface_hub/pull/733/files#r820604472 + json["lfsmultipartthresh"] = self._lfsmultipartthresh # type: ignore + + if resource_group_id is not None: + json["resourceGroupId"] = resource_group_id + + headers = self._build_hf_headers(token=token) + while True: + r = get_session().post(path, headers=headers, json=json) + if r.status_code == 409 and "Cannot create repo: another conflicting operation is in progress" in r.text: + # Since https://github.com/huggingface/moon-landing/pull/7272 (private repo), it is not possible to + # concurrently create repos on the Hub for a same user. This is rarely an issue, except when running + # tests. To avoid any inconvenience, we retry to create the repo for this specific error. + # NOTE: This could have being fixed directly in the tests but adding it here should fixed CIs for all + # dependent libraries. + # NOTE: If a fix is implemented server-side, we should be able to remove this retry mechanism. + logger.debug("Create repo failed due to a concurrency issue. Retrying...") + continue + break + + try: + hf_raise_for_status(r) + except HTTPError as err: + if exist_ok and err.response.status_code == 409: + # Repo already exists and `exist_ok=True` + pass + elif exist_ok and err.response.status_code == 403: + # No write permission on the namespace but repo might already exist + try: + self.repo_info(repo_id=repo_id, repo_type=repo_type, token=token) + if repo_type is None or repo_type == constants.REPO_TYPE_MODEL: + return RepoUrl(f"{self.endpoint}/{repo_id}") + return RepoUrl(f"{self.endpoint}/{repo_type}/{repo_id}") + except HfHubHTTPError: + raise err + else: + raise + + d = r.json() + return RepoUrl(d["url"], endpoint=self.endpoint) + + @validate_hf_hub_args + def delete_repo( + self, + repo_id: str, + *, + token: Union[str, bool, None] = None, + repo_type: Optional[str] = None, + missing_ok: bool = False, + ) -> None: + """ + Delete a repo from the HuggingFace Hub. CAUTION: this is irreversible. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if uploading to a dataset or + space, `None` or `"model"` if uploading to a model. + missing_ok (`bool`, *optional*, defaults to `False`): + If `True`, do not raise an error if repo does not exist. + + Raises: + [`~utils.RepositoryNotFoundError`] + If the repository to delete from cannot be found and `missing_ok` is set to False (default). + """ + organization, name = repo_id.split("/") if "/" in repo_id else (None, repo_id) + + path = f"{self.endpoint}/api/repos/delete" + + if repo_type not in constants.REPO_TYPES: + raise ValueError("Invalid repo type") + + json = {"name": name, "organization": organization} + if repo_type is not None: + json["type"] = repo_type + + headers = self._build_hf_headers(token=token) + r = get_session().delete(path, headers=headers, json=json) + try: + hf_raise_for_status(r) + except RepositoryNotFoundError: + if not missing_ok: + raise + + @_deprecate_method(version="0.32", message="Please use `update_repo_settings` instead.") + @validate_hf_hub_args + def update_repo_visibility( + self, + repo_id: str, + private: bool = False, + *, + token: Union[str, bool, None] = None, + repo_type: Optional[str] = None, + ) -> Dict[str, bool]: + """Update the visibility setting of a repository. + + Deprecated. Use `update_repo_settings` instead. + + Args: + repo_id (`str`, *optional*): + A namespace (user or an organization) and a repo name separated by a `/`. + private (`bool`, *optional*, defaults to `False`): + Whether the repository should be private. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if uploading to a dataset or + space, `None` or `"model"` if uploading to a model. Default is + `None`. + + Returns: + The HTTP response in json. + + + + Raises the following errors: + + - [`~utils.RepositoryNotFoundError`] + If the repository to download from cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + + + """ + if repo_type not in constants.REPO_TYPES: + raise ValueError(f"Invalid repo type, must be one of {constants.REPO_TYPES}") + if repo_type is None: + repo_type = constants.REPO_TYPE_MODEL # default repo type + + r = get_session().put( + url=f"{self.endpoint}/api/{repo_type}s/{repo_id}/settings", + headers=self._build_hf_headers(token=token), + json={"private": private}, + ) + hf_raise_for_status(r) + return r.json() + + @validate_hf_hub_args + def update_repo_settings( + self, + repo_id: str, + *, + gated: Optional[Literal["auto", "manual", False]] = None, + private: Optional[bool] = None, + token: Union[str, bool, None] = None, + repo_type: Optional[str] = None, + xet_enabled: Optional[bool] = None, + ) -> None: + """ + Update the settings of a repository, including gated access and visibility. + + To give more control over how repos are used, the Hub allows repo authors to enable + access requests for their repos, and also to set the visibility of the repo to private. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated by a /. + gated (`Literal["auto", "manual", False]`, *optional*): + The gated status for the repository. If set to `None` (default), the `gated` setting of the repository won't be updated. + * "auto": The repository is gated, and access requests are automatically approved or denied based on predefined criteria. + * "manual": The repository is gated, and access requests require manual approval. + * False : The repository is not gated, and anyone can access it. + private (`bool`, *optional*): + Whether the repository should be private. + token (`Union[str, bool, None]`, *optional*): + A valid user access token (string). Defaults to the locally saved token, + which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass False. + repo_type (`str`, *optional*): + The type of the repository to update settings from (`"model"`, `"dataset"` or `"space"`). + Defaults to `"model"`. + xet_enabled (`bool`, *optional*): + Whether the repository should be enabled for Xet Storage. + Raises: + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If gated is not one of "auto", "manual", or False. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If repo_type is not one of the values in constants.REPO_TYPES. + [`~utils.HfHubHTTPError`]: + If the request to the Hugging Face Hub API fails. + [`~utils.RepositoryNotFoundError`] + If the repository to download from cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + """ + + if repo_type not in constants.REPO_TYPES: + raise ValueError(f"Invalid repo type, must be one of {constants.REPO_TYPES}") + if repo_type is None: + repo_type = constants.REPO_TYPE_MODEL # default repo type + + # Prepare the JSON payload for the PUT request + payload: Dict = {} + + if gated is not None: + if gated not in ["auto", "manual", False]: + raise ValueError(f"Invalid gated status, must be one of 'auto', 'manual', or False. Got '{gated}'.") + payload["gated"] = gated + + if private is not None: + payload["private"] = private + + if xet_enabled is not None: + payload["xetEnabled"] = xet_enabled + + if len(payload) == 0: + raise ValueError("At least one setting must be updated.") + + # Build headers + headers = self._build_hf_headers(token=token) + + r = get_session().put( + url=f"{self.endpoint}/api/{repo_type}s/{repo_id}/settings", + headers=headers, + json=payload, + ) + hf_raise_for_status(r) + + def move_repo( + self, + from_id: str, + to_id: str, + *, + repo_type: Optional[str] = None, + token: Union[str, bool, None] = None, + ): + """ + Moving a repository from namespace1/repo_name1 to namespace2/repo_name2 + + Note there are certain limitations. For more information about moving + repositories, please see + https://hf.co/docs/hub/repositories-settings#renaming-or-transferring-a-repo. + + Args: + from_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. Original repository identifier. + to_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. Final repository identifier. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if uploading to a dataset or + space, `None` or `"model"` if uploading to a model. Default is + `None`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + + + Raises the following errors: + + - [`~utils.RepositoryNotFoundError`] + If the repository to download from cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + + + """ + if len(from_id.split("/")) != 2: + raise ValueError(f"Invalid repo_id: {from_id}. It should have a namespace (:namespace:/:repo_name:)") + + if len(to_id.split("/")) != 2: + raise ValueError(f"Invalid repo_id: {to_id}. It should have a namespace (:namespace:/:repo_name:)") + + if repo_type is None: + repo_type = constants.REPO_TYPE_MODEL # Hub won't accept `None`. + + json = {"fromRepo": from_id, "toRepo": to_id, "type": repo_type} + + path = f"{self.endpoint}/api/repos/move" + headers = self._build_hf_headers(token=token) + r = get_session().post(path, headers=headers, json=json) + try: + hf_raise_for_status(r) + except HfHubHTTPError as e: + e.append_to_message( + "\nFor additional documentation please see" + " https://hf.co/docs/hub/repositories-settings#renaming-or-transferring-a-repo." + ) + raise + + @overload + def create_commit( # type: ignore + self, + repo_id: str, + operations: Iterable[CommitOperation], + *, + commit_message: str, + commit_description: Optional[str] = None, + token: Union[str, bool, None] = None, + repo_type: Optional[str] = None, + revision: Optional[str] = None, + create_pr: Optional[bool] = None, + num_threads: int = 5, + parent_commit: Optional[str] = None, + run_as_future: Literal[False] = ..., + ) -> CommitInfo: ... + + @overload + def create_commit( + self, + repo_id: str, + operations: Iterable[CommitOperation], + *, + commit_message: str, + commit_description: Optional[str] = None, + token: Union[str, bool, None] = None, + repo_type: Optional[str] = None, + revision: Optional[str] = None, + create_pr: Optional[bool] = None, + num_threads: int = 5, + parent_commit: Optional[str] = None, + run_as_future: Literal[True] = ..., + ) -> Future[CommitInfo]: ... + + @validate_hf_hub_args + @future_compatible + def create_commit( + self, + repo_id: str, + operations: Iterable[CommitOperation], + *, + commit_message: str, + commit_description: Optional[str] = None, + token: Union[str, bool, None] = None, + repo_type: Optional[str] = None, + revision: Optional[str] = None, + create_pr: Optional[bool] = None, + num_threads: int = 5, + parent_commit: Optional[str] = None, + run_as_future: bool = False, + ) -> Union[CommitInfo, Future[CommitInfo]]: + """ + Creates a commit in the given repo, deleting & uploading files as needed. + + + + The input list of `CommitOperation` will be mutated during the commit process. Do not reuse the same objects + for multiple commits. + + + + + + `create_commit` assumes that the repo already exists on the Hub. If you get a + Client error 404, please make sure you are authenticated and that `repo_id` and + `repo_type` are set correctly. If repo does not exist, create it first using + [`~hf_api.create_repo`]. + + + + + + `create_commit` is limited to 25k LFS files and a 1GB payload for regular files. + + + + Args: + repo_id (`str`): + The repository in which the commit will be created, for example: + `"username/custom_transformers"` + + operations (`Iterable` of [`~hf_api.CommitOperation`]): + An iterable of operations to include in the commit, either: + + - [`~hf_api.CommitOperationAdd`] to upload a file + - [`~hf_api.CommitOperationDelete`] to delete a file + - [`~hf_api.CommitOperationCopy`] to copy a file + + Operation objects will be mutated to include information relative to the upload. Do not reuse the + same objects for multiple commits. + + commit_message (`str`): + The summary (first line) of the commit that will be created. + + commit_description (`str`, *optional*): + The description of the commit that will be created + + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if uploading to a dataset or + space, `None` or `"model"` if uploading to a model. Default is + `None`. + + revision (`str`, *optional*): + The git revision to commit from. Defaults to the head of the `"main"` branch. + + create_pr (`boolean`, *optional*): + Whether or not to create a Pull Request with that commit. Defaults to `False`. + If `revision` is not set, PR is opened against the `"main"` branch. If + `revision` is set and is a branch, PR is opened against this branch. If + `revision` is set and is not a branch name (example: a commit oid), an + `RevisionNotFoundError` is returned by the server. + + num_threads (`int`, *optional*): + Number of concurrent threads for uploading files. Defaults to 5. + Setting it to 2 means at most 2 files will be uploaded concurrently. + + parent_commit (`str`, *optional*): + The OID / SHA of the parent commit, as a hexadecimal string. + Shorthands (7 first characters) are also supported. If specified and `create_pr` is `False`, + the commit will fail if `revision` does not point to `parent_commit`. If specified and `create_pr` + is `True`, the pull request will be created from `parent_commit`. Specifying `parent_commit` + ensures the repo has not changed before committing the changes, and can be especially useful + if the repo is updated / committed to concurrently. + run_as_future (`bool`, *optional*): + Whether or not to run this method in the background. Background jobs are run sequentially without + blocking the main thread. Passing `run_as_future=True` will return a [Future](https://docs.python.org/3/library/concurrent.futures.html#future-objects) + object. Defaults to `False`. + + Returns: + [`CommitInfo`] or `Future`: + Instance of [`CommitInfo`] containing information about the newly created commit (commit hash, commit + url, pr url, commit message,...). If `run_as_future=True` is passed, returns a Future object which will + contain the result when executed. + + Raises: + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If commit message is empty. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If parent commit is not a valid commit OID. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If a README.md file with an invalid metadata section is committed. In this case, the commit will fail + early, before trying to upload any file. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If `create_pr` is `True` and revision is neither `None` nor `"main"`. + [`~utils.RepositoryNotFoundError`]: + If repository is not found (error 404): wrong repo_id/repo_type, private + but not authenticated or repo does not exist. + """ + if parent_commit is not None and not constants.REGEX_COMMIT_OID.fullmatch(parent_commit): + raise ValueError( + f"`parent_commit` is not a valid commit OID. It must match the following regex: {constants.REGEX_COMMIT_OID}" + ) + + if commit_message is None or len(commit_message) == 0: + raise ValueError("`commit_message` can't be empty, please pass a value.") + + commit_description = commit_description if commit_description is not None else "" + repo_type = repo_type if repo_type is not None else constants.REPO_TYPE_MODEL + if repo_type not in constants.REPO_TYPES: + raise ValueError(f"Invalid repo type, must be one of {constants.REPO_TYPES}") + unquoted_revision = revision or constants.DEFAULT_REVISION + revision = quote(unquoted_revision, safe="") + create_pr = create_pr if create_pr is not None else False + + headers = self._build_hf_headers(token=token) + + operations = list(operations) + additions = [op for op in operations if isinstance(op, CommitOperationAdd)] + copies = [op for op in operations if isinstance(op, CommitOperationCopy)] + nb_additions = len(additions) + nb_copies = len(copies) + nb_deletions = len(operations) - nb_additions - nb_copies + + for addition in additions: + if addition._is_committed: + raise ValueError( + f"CommitOperationAdd {addition} has already being committed and cannot be reused. Please create a" + " new CommitOperationAdd object if you want to create a new commit." + ) + + if repo_type != "dataset": + for addition in additions: + if addition.path_in_repo.endswith((".arrow", ".parquet")): + warnings.warn( + f"It seems that you are about to commit a data file ({addition.path_in_repo}) to a {repo_type}" + " repository. You are sure this is intended? If you are trying to upload a dataset, please" + " set `repo_type='dataset'` or `--repo-type=dataset` in a CLI." + ) + + logger.debug( + f"About to commit to the hub: {len(additions)} addition(s), {len(copies)} copie(s) and" + f" {nb_deletions} deletion(s)." + ) + + # If updating a README.md file, make sure the metadata format is valid + # It's better to fail early than to fail after all the files have been uploaded. + for addition in additions: + if addition.path_in_repo == "README.md": + with addition.as_file() as file: + content = file.read().decode() + self._validate_yaml(content, repo_type=repo_type, token=token) + # Skip other additions after `README.md` has been processed + break + + # If updating twice the same file or update then delete a file in a single commit + _warn_on_overwriting_operations(operations) + + self.preupload_lfs_files( + repo_id=repo_id, + additions=additions, + token=token, + repo_type=repo_type, + revision=unquoted_revision, # first-class methods take unquoted revision + create_pr=create_pr, + num_threads=num_threads, + free_memory=False, # do not remove `CommitOperationAdd.path_or_fileobj` on LFS files for "normal" users + ) + + files_to_copy = _fetch_files_to_copy( + copies=copies, + repo_type=repo_type, + repo_id=repo_id, + headers=headers, + revision=unquoted_revision, + endpoint=self.endpoint, + ) + # Remove no-op operations (files that have not changed) + operations_without_no_op = [] + for operation in operations: + if ( + isinstance(operation, CommitOperationAdd) + and operation._remote_oid is not None + and operation._remote_oid == operation._local_oid + ): + # File already exists on the Hub and has not changed: we can skip it. + logger.debug(f"Skipping upload for '{operation.path_in_repo}' as the file has not changed.") + continue + if ( + isinstance(operation, CommitOperationCopy) + and operation._dest_oid is not None + and operation._dest_oid == operation._src_oid + ): + # Source and destination files are identical - skip + logger.debug( + f"Skipping copy for '{operation.src_path_in_repo}' -> '{operation.path_in_repo}' as the content of the source file is the same as the destination file." + ) + continue + operations_without_no_op.append(operation) + if len(operations) != len(operations_without_no_op): + logger.info( + f"Removing {len(operations) - len(operations_without_no_op)} file(s) from commit that have not changed." + ) + + # Return early if empty commit + if len(operations_without_no_op) == 0: + logger.warning("No files have been modified since last commit. Skipping to prevent empty commit.") + + # Get latest commit info + try: + info = self.repo_info(repo_id=repo_id, repo_type=repo_type, revision=unquoted_revision, token=token) + except RepositoryNotFoundError as e: + e.append_to_message(_CREATE_COMMIT_NO_REPO_ERROR_MESSAGE) + raise + + # Return commit info based on latest commit + url_prefix = self.endpoint + if repo_type is not None and repo_type != constants.REPO_TYPE_MODEL: + url_prefix = f"{url_prefix}/{repo_type}s" + return CommitInfo( + commit_url=f"{url_prefix}/{repo_id}/commit/{info.sha}", + commit_message=commit_message, + commit_description=commit_description, + oid=info.sha, # type: ignore[arg-type] + ) + + commit_payload = _prepare_commit_payload( + operations=operations, + files_to_copy=files_to_copy, + commit_message=commit_message, + commit_description=commit_description, + parent_commit=parent_commit, + ) + commit_url = f"{self.endpoint}/api/{repo_type}s/{repo_id}/commit/{revision}" + + def _payload_as_ndjson() -> Iterable[bytes]: + for item in commit_payload: + yield json.dumps(item).encode() + yield b"\n" + + headers = { + # See https://github.com/huggingface/huggingface_hub/issues/1085#issuecomment-1265208073 + "Content-Type": "application/x-ndjson", + **headers, + } + data = b"".join(_payload_as_ndjson()) + params = {"create_pr": "1"} if create_pr else None + + try: + commit_resp = get_session().post(url=commit_url, headers=headers, data=data, params=params) + hf_raise_for_status(commit_resp, endpoint_name="commit") + except RepositoryNotFoundError as e: + e.append_to_message(_CREATE_COMMIT_NO_REPO_ERROR_MESSAGE) + raise + except EntryNotFoundError as e: + if nb_deletions > 0 and "A file with this name doesn't exist" in str(e): + e.append_to_message( + "\nMake sure to differentiate file and folder paths in delete" + " operations with a trailing '/' or using `is_folder=True/False`." + ) + raise + + # Mark additions as committed (cannot be reused in another commit) + for addition in additions: + addition._is_committed = True + + commit_data = commit_resp.json() + return CommitInfo( + commit_url=commit_data["commitUrl"], + commit_message=commit_message, + commit_description=commit_description, + oid=commit_data["commitOid"], + pr_url=commit_data["pullRequestUrl"] if create_pr else None, + ) + + def preupload_lfs_files( + self, + repo_id: str, + additions: Iterable[CommitOperationAdd], + *, + token: Union[str, bool, None] = None, + repo_type: Optional[str] = None, + revision: Optional[str] = None, + create_pr: Optional[bool] = None, + num_threads: int = 5, + free_memory: bool = True, + gitignore_content: Optional[str] = None, + ): + """Pre-upload LFS files to S3 in preparation on a future commit. + + This method is useful if you are generating the files to upload on-the-fly and you don't want to store them + in memory before uploading them all at once. + + + + This is a power-user method. You shouldn't need to call it directly to make a normal commit. + Use [`create_commit`] directly instead. + + + + + + Commit operations will be mutated during the process. In particular, the attached `path_or_fileobj` will be + removed after the upload to save memory (and replaced by an empty `bytes` object). Do not reuse the same + objects except to pass them to [`create_commit`]. If you don't want to remove the attached content from the + commit operation object, pass `free_memory=False`. + + + + Args: + repo_id (`str`): + The repository in which you will commit the files, for example: `"username/custom_transformers"`. + + operations (`Iterable` of [`CommitOperationAdd`]): + The list of files to upload. Warning: the objects in this list will be mutated to include information + relative to the upload. Do not reuse the same objects for multiple commits. + + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + repo_type (`str`, *optional*): + The type of repository to upload to (e.g. `"model"` -default-, `"dataset"` or `"space"`). + + revision (`str`, *optional*): + The git revision to commit from. Defaults to the head of the `"main"` branch. + + create_pr (`boolean`, *optional*): + Whether or not you plan to create a Pull Request with that commit. Defaults to `False`. + + num_threads (`int`, *optional*): + Number of concurrent threads for uploading files. Defaults to 5. + Setting it to 2 means at most 2 files will be uploaded concurrently. + + gitignore_content (`str`, *optional*): + The content of the `.gitignore` file to know which files should be ignored. The order of priority + is to first check if `gitignore_content` is passed, then check if the `.gitignore` file is present + in the list of files to commit and finally default to the `.gitignore` file already hosted on the Hub + (if any). + + Example: + ```py + >>> from huggingface_hub import CommitOperationAdd, preupload_lfs_files, create_commit, create_repo + + >>> repo_id = create_repo("test_preupload").repo_id + + # Generate and preupload LFS files one by one + >>> operations = [] # List of all `CommitOperationAdd` objects that will be generated + >>> for i in range(5): + ... content = ... # generate binary content + ... addition = CommitOperationAdd(path_in_repo=f"shard_{i}_of_5.bin", path_or_fileobj=content) + ... preupload_lfs_files(repo_id, additions=[addition]) # upload + free memory + ... operations.append(addition) + + # Create commit + >>> create_commit(repo_id, operations=operations, commit_message="Commit all shards") + ``` + """ + repo_type = repo_type if repo_type is not None else constants.REPO_TYPE_MODEL + if repo_type not in constants.REPO_TYPES: + raise ValueError(f"Invalid repo type, must be one of {constants.REPO_TYPES}") + revision = quote(revision, safe="") if revision is not None else constants.DEFAULT_REVISION + create_pr = create_pr if create_pr is not None else False + headers = self._build_hf_headers(token=token) + + # Check if a `gitignore` file is being committed to the Hub. + additions = list(additions) + if gitignore_content is None: + for addition in additions: + if addition.path_in_repo == ".gitignore": + with addition.as_file() as f: + gitignore_content = f.read().decode() + break + + # Filter out already uploaded files + new_additions = [addition for addition in additions if not addition._is_uploaded] + + # Check which new files are LFS + # For some items, we might have already fetched the upload mode (in case of upload_large_folder) + additions_no_upload_mode = [addition for addition in new_additions if addition._upload_mode is None] + if len(additions_no_upload_mode) > 0: + try: + _fetch_upload_modes( + additions=additions_no_upload_mode, + repo_type=repo_type, + repo_id=repo_id, + headers=headers, + revision=revision, + endpoint=self.endpoint, + create_pr=create_pr or False, + gitignore_content=gitignore_content, + ) + except RepositoryNotFoundError as e: + e.append_to_message(_CREATE_COMMIT_NO_REPO_ERROR_MESSAGE) + raise + + # Filter out regular files + new_lfs_additions = [addition for addition in new_additions if addition._upload_mode == "lfs"] + + # Filter out files listed in .gitignore + new_lfs_additions_to_upload = [] + for addition in new_lfs_additions: + if addition._should_ignore: + logger.debug(f"Skipping upload for LFS file '{addition.path_in_repo}' (ignored by gitignore file).") + else: + new_lfs_additions_to_upload.append(addition) + if len(new_lfs_additions) != len(new_lfs_additions_to_upload): + logger.info( + f"Skipped upload for {len(new_lfs_additions) - len(new_lfs_additions_to_upload)} LFS file(s) " + "(ignored by gitignore file)." + ) + # Prepare upload parameters + upload_kwargs = { + "additions": new_lfs_additions_to_upload, + "repo_type": repo_type, + "repo_id": repo_id, + "headers": headers, + "endpoint": self.endpoint, + # If `create_pr`, we don't want to check user permission on the revision as users with read permission + # should still be able to create PRs even if they don't have write permission on the target branch of the + # PR (i.e. `revision`). + "revision": revision if not create_pr else None, + } + # Upload files using Xet protocol if all of the following are true: + # - xet is enabled for the repo, + # - the files are provided as str or paths objects, + # - the library is installed. + # Otherwise, default back to LFS. + xet_enabled = self.repo_info( + repo_id=repo_id, + repo_type=repo_type, + revision=unquote(revision) if revision is not None else revision, + expand="xetEnabled", + token=token, + ).xet_enabled + has_buffered_io_data = any( + isinstance(addition.path_or_fileobj, io.BufferedIOBase) for addition in new_lfs_additions_to_upload + ) + if xet_enabled and not has_buffered_io_data and is_xet_available(): + logger.debug("Uploading files using Xet Storage..") + _upload_xet_files(**upload_kwargs, create_pr=create_pr) # type: ignore [arg-type] + else: + if xet_enabled and is_xet_available(): + if has_buffered_io_data: + logger.warning( + "Uploading files as a binary IO buffer is not supported by Xet Storage. " + "Falling back to HTTP upload." + ) + _upload_lfs_files(**upload_kwargs, num_threads=num_threads) # type: ignore [arg-type] + for addition in new_lfs_additions_to_upload: + addition._is_uploaded = True + if free_memory: + addition.path_or_fileobj = b"" + + @overload + def upload_file( # type: ignore + self, + *, + path_or_fileobj: Union[str, Path, bytes, BinaryIO], + path_in_repo: str, + repo_id: str, + token: Union[str, bool, None] = None, + repo_type: Optional[str] = None, + revision: Optional[str] = None, + commit_message: Optional[str] = None, + commit_description: Optional[str] = None, + create_pr: Optional[bool] = None, + parent_commit: Optional[str] = None, + run_as_future: Literal[False] = ..., + ) -> CommitInfo: ... + + @overload + def upload_file( + self, + *, + path_or_fileobj: Union[str, Path, bytes, BinaryIO], + path_in_repo: str, + repo_id: str, + token: Union[str, bool, None] = None, + repo_type: Optional[str] = None, + revision: Optional[str] = None, + commit_message: Optional[str] = None, + commit_description: Optional[str] = None, + create_pr: Optional[bool] = None, + parent_commit: Optional[str] = None, + run_as_future: Literal[True] = ..., + ) -> Future[CommitInfo]: ... + + @validate_hf_hub_args + @future_compatible + def upload_file( + self, + *, + path_or_fileobj: Union[str, Path, bytes, BinaryIO], + path_in_repo: str, + repo_id: str, + token: Union[str, bool, None] = None, + repo_type: Optional[str] = None, + revision: Optional[str] = None, + commit_message: Optional[str] = None, + commit_description: Optional[str] = None, + create_pr: Optional[bool] = None, + parent_commit: Optional[str] = None, + run_as_future: bool = False, + ) -> Union[CommitInfo, Future[CommitInfo]]: + """ + Upload a local file (up to 50 GB) to the given repo. The upload is done + through a HTTP post request, and doesn't require git or git-lfs to be + installed. + + Args: + path_or_fileobj (`str`, `Path`, `bytes`, or `IO`): + Path to a file on the local machine or binary data stream / + fileobj / buffer. + path_in_repo (`str`): + Relative filepath in the repo, for example: + `"checkpoints/1fec34a/weights.bin"` + repo_id (`str`): + The repository to which the file will be uploaded, for example: + `"username/custom_transformers"` + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if uploading to a dataset or + space, `None` or `"model"` if uploading to a model. Default is + `None`. + revision (`str`, *optional*): + The git revision to commit from. Defaults to the head of the `"main"` branch. + commit_message (`str`, *optional*): + The summary / title / first line of the generated commit + commit_description (`str` *optional*) + The description of the generated commit + create_pr (`boolean`, *optional*): + Whether or not to create a Pull Request with that commit. Defaults to `False`. + If `revision` is not set, PR is opened against the `"main"` branch. If + `revision` is set and is a branch, PR is opened against this branch. If + `revision` is set and is not a branch name (example: a commit oid), an + `RevisionNotFoundError` is returned by the server. + parent_commit (`str`, *optional*): + The OID / SHA of the parent commit, as a hexadecimal string. Shorthands (7 first characters) are also supported. + If specified and `create_pr` is `False`, the commit will fail if `revision` does not point to `parent_commit`. + If specified and `create_pr` is `True`, the pull request will be created from `parent_commit`. + Specifying `parent_commit` ensures the repo has not changed before committing the changes, and can be + especially useful if the repo is updated / committed to concurrently. + run_as_future (`bool`, *optional*): + Whether or not to run this method in the background. Background jobs are run sequentially without + blocking the main thread. Passing `run_as_future=True` will return a [Future](https://docs.python.org/3/library/concurrent.futures.html#future-objects) + object. Defaults to `False`. + + + Returns: + [`CommitInfo`] or `Future`: + Instance of [`CommitInfo`] containing information about the newly created commit (commit hash, commit + url, pr url, commit message,...). If `run_as_future=True` is passed, returns a Future object which will + contain the result when executed. + + + Raises the following errors: + + - [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError) + if the HuggingFace API returned an error + - [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + if some parameter value is invalid + - [`~utils.RepositoryNotFoundError`] + If the repository to download from cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + - [`~utils.RevisionNotFoundError`] + If the revision to download from cannot be found. + + + + + + `upload_file` assumes that the repo already exists on the Hub. If you get a + Client error 404, please make sure you are authenticated and that `repo_id` and + `repo_type` are set correctly. If repo does not exist, create it first using + [`~hf_api.create_repo`]. + + + + Example: + + ```python + >>> from huggingface_hub import upload_file + + >>> with open("./local/filepath", "rb") as fobj: + ... upload_file( + ... path_or_fileobj=fileobj, + ... path_in_repo="remote/file/path.h5", + ... repo_id="username/my-dataset", + ... repo_type="dataset", + ... token="my_token", + ... ) + "https://huggingface.co/datasets/username/my-dataset/blob/main/remote/file/path.h5" + + >>> upload_file( + ... path_or_fileobj=".\\\\local\\\\file\\\\path", + ... path_in_repo="remote/file/path.h5", + ... repo_id="username/my-model", + ... token="my_token", + ... ) + "https://huggingface.co/username/my-model/blob/main/remote/file/path.h5" + + >>> upload_file( + ... path_or_fileobj=".\\\\local\\\\file\\\\path", + ... path_in_repo="remote/file/path.h5", + ... repo_id="username/my-model", + ... token="my_token", + ... create_pr=True, + ... ) + "https://huggingface.co/username/my-model/blob/refs%2Fpr%2F1/remote/file/path.h5" + ``` + """ + if repo_type not in constants.REPO_TYPES: + raise ValueError(f"Invalid repo type, must be one of {constants.REPO_TYPES}") + + commit_message = ( + commit_message if commit_message is not None else f"Upload {path_in_repo} with huggingface_hub" + ) + operation = CommitOperationAdd( + path_or_fileobj=path_or_fileobj, + path_in_repo=path_in_repo, + ) + + commit_info = self.create_commit( + repo_id=repo_id, + repo_type=repo_type, + operations=[operation], + commit_message=commit_message, + commit_description=commit_description, + token=token, + revision=revision, + create_pr=create_pr, + parent_commit=parent_commit, + ) + + if commit_info.pr_url is not None: + revision = quote(_parse_revision_from_pr_url(commit_info.pr_url), safe="") + if repo_type in constants.REPO_TYPES_URL_PREFIXES: + repo_id = constants.REPO_TYPES_URL_PREFIXES[repo_type] + repo_id + revision = revision if revision is not None else constants.DEFAULT_REVISION + + return CommitInfo( + commit_url=commit_info.commit_url, + commit_message=commit_info.commit_message, + commit_description=commit_info.commit_description, + oid=commit_info.oid, + pr_url=commit_info.pr_url, + # Similar to `hf_hub_url` but it's "blob" instead of "resolve" + # TODO: remove this in v1.0 + _url=f"{self.endpoint}/{repo_id}/blob/{revision}/{path_in_repo}", + ) + + @overload + def upload_folder( # type: ignore + self, + *, + repo_id: str, + folder_path: Union[str, Path], + path_in_repo: Optional[str] = None, + commit_message: Optional[str] = None, + commit_description: Optional[str] = None, + token: Union[str, bool, None] = None, + repo_type: Optional[str] = None, + revision: Optional[str] = None, + create_pr: Optional[bool] = None, + parent_commit: Optional[str] = None, + allow_patterns: Optional[Union[List[str], str]] = None, + ignore_patterns: Optional[Union[List[str], str]] = None, + delete_patterns: Optional[Union[List[str], str]] = None, + run_as_future: Literal[False] = ..., + ) -> CommitInfo: ... + + @overload + def upload_folder( # type: ignore + self, + *, + repo_id: str, + folder_path: Union[str, Path], + path_in_repo: Optional[str] = None, + commit_message: Optional[str] = None, + commit_description: Optional[str] = None, + token: Union[str, bool, None] = None, + repo_type: Optional[str] = None, + revision: Optional[str] = None, + create_pr: Optional[bool] = None, + parent_commit: Optional[str] = None, + allow_patterns: Optional[Union[List[str], str]] = None, + ignore_patterns: Optional[Union[List[str], str]] = None, + delete_patterns: Optional[Union[List[str], str]] = None, + run_as_future: Literal[True] = ..., + ) -> Future[CommitInfo]: ... + + @validate_hf_hub_args + @future_compatible + def upload_folder( + self, + *, + repo_id: str, + folder_path: Union[str, Path], + path_in_repo: Optional[str] = None, + commit_message: Optional[str] = None, + commit_description: Optional[str] = None, + token: Union[str, bool, None] = None, + repo_type: Optional[str] = None, + revision: Optional[str] = None, + create_pr: Optional[bool] = None, + parent_commit: Optional[str] = None, + allow_patterns: Optional[Union[List[str], str]] = None, + ignore_patterns: Optional[Union[List[str], str]] = None, + delete_patterns: Optional[Union[List[str], str]] = None, + run_as_future: bool = False, + ) -> Union[CommitInfo, Future[CommitInfo]]: + """ + Upload a local folder to the given repo. The upload is done through a HTTP requests, and doesn't require git or + git-lfs to be installed. + + The structure of the folder will be preserved. Files with the same name already present in the repository will + be overwritten. Others will be left untouched. + + Use the `allow_patterns` and `ignore_patterns` arguments to specify which files to upload. These parameters + accept either a single pattern or a list of patterns. Patterns are Standard Wildcards (globbing patterns) as + documented [here](https://tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm). If both `allow_patterns` and + `ignore_patterns` are provided, both constraints apply. By default, all files from the folder are uploaded. + + Use the `delete_patterns` argument to specify remote files you want to delete. Input type is the same as for + `allow_patterns` (see above). If `path_in_repo` is also provided, the patterns are matched against paths + relative to this folder. For example, `upload_folder(..., path_in_repo="experiment", delete_patterns="logs/*")` + will delete any remote file under `./experiment/logs/`. Note that the `.gitattributes` file will not be deleted + even if it matches the patterns. + + Any `.git/` folder present in any subdirectory will be ignored. However, please be aware that the `.gitignore` + file is not taken into account. + + Uses `HfApi.create_commit` under the hood. + + Args: + repo_id (`str`): + The repository to which the file will be uploaded, for example: + `"username/custom_transformers"` + folder_path (`str` or `Path`): + Path to the folder to upload on the local file system + path_in_repo (`str`, *optional*): + Relative path of the directory in the repo, for example: + `"checkpoints/1fec34a/results"`. Will default to the root folder of the repository. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if uploading to a dataset or + space, `None` or `"model"` if uploading to a model. Default is + `None`. + revision (`str`, *optional*): + The git revision to commit from. Defaults to the head of the `"main"` branch. + commit_message (`str`, *optional*): + The summary / title / first line of the generated commit. Defaults to: + `f"Upload {path_in_repo} with huggingface_hub"` + commit_description (`str` *optional*): + The description of the generated commit + create_pr (`boolean`, *optional*): + Whether or not to create a Pull Request with that commit. Defaults to `False`. If `revision` is not + set, PR is opened against the `"main"` branch. If `revision` is set and is a branch, PR is opened + against this branch. If `revision` is set and is not a branch name (example: a commit oid), an + `RevisionNotFoundError` is returned by the server. + parent_commit (`str`, *optional*): + The OID / SHA of the parent commit, as a hexadecimal string. Shorthands (7 first characters) are also supported. + If specified and `create_pr` is `False`, the commit will fail if `revision` does not point to `parent_commit`. + If specified and `create_pr` is `True`, the pull request will be created from `parent_commit`. + Specifying `parent_commit` ensures the repo has not changed before committing the changes, and can be + especially useful if the repo is updated / committed to concurrently. + allow_patterns (`List[str]` or `str`, *optional*): + If provided, only files matching at least one pattern are uploaded. + ignore_patterns (`List[str]` or `str`, *optional*): + If provided, files matching any of the patterns are not uploaded. + delete_patterns (`List[str]` or `str`, *optional*): + If provided, remote files matching any of the patterns will be deleted from the repo while committing + new files. This is useful if you don't know which files have already been uploaded. + Note: to avoid discrepancies the `.gitattributes` file is not deleted even if it matches the pattern. + run_as_future (`bool`, *optional*): + Whether or not to run this method in the background. Background jobs are run sequentially without + blocking the main thread. Passing `run_as_future=True` will return a [Future](https://docs.python.org/3/library/concurrent.futures.html#future-objects) + object. Defaults to `False`. + + Returns: + [`CommitInfo`] or `Future`: + Instance of [`CommitInfo`] containing information about the newly created commit (commit hash, commit + url, pr url, commit message,...). If `run_as_future=True` is passed, returns a Future object which will + contain the result when executed. + + + + Raises the following errors: + + - [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError) + if the HuggingFace API returned an error + - [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + if some parameter value is invalid + + + + + + `upload_folder` assumes that the repo already exists on the Hub. If you get a Client error 404, please make + sure you are authenticated and that `repo_id` and `repo_type` are set correctly. If repo does not exist, create + it first using [`~hf_api.create_repo`]. + + + + + + When dealing with a large folder (thousands of files or hundreds of GB), we recommend using [`~hf_api.upload_large_folder`] instead. + + + + Example: + + ```python + # Upload checkpoints folder except the log files + >>> upload_folder( + ... folder_path="local/checkpoints", + ... path_in_repo="remote/experiment/checkpoints", + ... repo_id="username/my-dataset", + ... repo_type="datasets", + ... token="my_token", + ... ignore_patterns="**/logs/*.txt", + ... ) + # "https://huggingface.co/datasets/username/my-dataset/tree/main/remote/experiment/checkpoints" + + # Upload checkpoints folder including logs while deleting existing logs from the repo + # Useful if you don't know exactly which log files have already being pushed + >>> upload_folder( + ... folder_path="local/checkpoints", + ... path_in_repo="remote/experiment/checkpoints", + ... repo_id="username/my-dataset", + ... repo_type="datasets", + ... token="my_token", + ... delete_patterns="**/logs/*.txt", + ... ) + "https://huggingface.co/datasets/username/my-dataset/tree/main/remote/experiment/checkpoints" + + # Upload checkpoints folder while creating a PR + >>> upload_folder( + ... folder_path="local/checkpoints", + ... path_in_repo="remote/experiment/checkpoints", + ... repo_id="username/my-dataset", + ... repo_type="datasets", + ... token="my_token", + ... create_pr=True, + ... ) + "https://huggingface.co/datasets/username/my-dataset/tree/refs%2Fpr%2F1/remote/experiment/checkpoints" + + ``` + """ + if repo_type not in constants.REPO_TYPES: + raise ValueError(f"Invalid repo type, must be one of {constants.REPO_TYPES}") + + # By default, upload folder to the root directory in repo. + if path_in_repo is None: + path_in_repo = "" + + # Do not upload .git folder + if ignore_patterns is None: + ignore_patterns = [] + elif isinstance(ignore_patterns, str): + ignore_patterns = [ignore_patterns] + ignore_patterns += DEFAULT_IGNORE_PATTERNS + + delete_operations = self._prepare_folder_deletions( + repo_id=repo_id, + repo_type=repo_type, + revision=constants.DEFAULT_REVISION if create_pr else revision, + token=token, + path_in_repo=path_in_repo, + delete_patterns=delete_patterns, + ) + add_operations = self._prepare_upload_folder_additions( + folder_path, + path_in_repo, + allow_patterns=allow_patterns, + ignore_patterns=ignore_patterns, + token=token, + repo_type=repo_type, + ) + + # Optimize operations: if some files will be overwritten, we don't need to delete them first + if len(add_operations) > 0: + added_paths = set(op.path_in_repo for op in add_operations) + delete_operations = [ + delete_op for delete_op in delete_operations if delete_op.path_in_repo not in added_paths + ] + commit_operations = delete_operations + add_operations + + commit_message = commit_message or "Upload folder using huggingface_hub" + + commit_info = self.create_commit( + repo_type=repo_type, + repo_id=repo_id, + operations=commit_operations, + commit_message=commit_message, + commit_description=commit_description, + token=token, + revision=revision, + create_pr=create_pr, + parent_commit=parent_commit, + ) + + # Create url to uploaded folder (for legacy return value) + if create_pr and commit_info.pr_url is not None: + revision = quote(_parse_revision_from_pr_url(commit_info.pr_url), safe="") + if repo_type in constants.REPO_TYPES_URL_PREFIXES: + repo_id = constants.REPO_TYPES_URL_PREFIXES[repo_type] + repo_id + revision = revision if revision is not None else constants.DEFAULT_REVISION + + return CommitInfo( + commit_url=commit_info.commit_url, + commit_message=commit_info.commit_message, + commit_description=commit_info.commit_description, + oid=commit_info.oid, + pr_url=commit_info.pr_url, + # Similar to `hf_hub_url` but it's "tree" instead of "resolve" + # TODO: remove this in v1.0 + _url=f"{self.endpoint}/{repo_id}/tree/{revision}/{path_in_repo}", + ) + + @validate_hf_hub_args + def delete_file( + self, + path_in_repo: str, + repo_id: str, + *, + token: Union[str, bool, None] = None, + repo_type: Optional[str] = None, + revision: Optional[str] = None, + commit_message: Optional[str] = None, + commit_description: Optional[str] = None, + create_pr: Optional[bool] = None, + parent_commit: Optional[str] = None, + ) -> CommitInfo: + """ + Deletes a file in the given repo. + + Args: + path_in_repo (`str`): + Relative filepath in the repo, for example: + `"checkpoints/1fec34a/weights.bin"` + repo_id (`str`): + The repository from which the file will be deleted, for example: + `"username/custom_transformers"` + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if the file is in a dataset or + space, `None` or `"model"` if in a model. Default is `None`. + revision (`str`, *optional*): + The git revision to commit from. Defaults to the head of the `"main"` branch. + commit_message (`str`, *optional*): + The summary / title / first line of the generated commit. Defaults to + `f"Delete {path_in_repo} with huggingface_hub"`. + commit_description (`str` *optional*) + The description of the generated commit + create_pr (`boolean`, *optional*): + Whether or not to create a Pull Request with that commit. Defaults to `False`. + If `revision` is not set, PR is opened against the `"main"` branch. If + `revision` is set and is a branch, PR is opened against this branch. If + `revision` is set and is not a branch name (example: a commit oid), an + `RevisionNotFoundError` is returned by the server. + parent_commit (`str`, *optional*): + The OID / SHA of the parent commit, as a hexadecimal string. Shorthands (7 first characters) are also supported. + If specified and `create_pr` is `False`, the commit will fail if `revision` does not point to `parent_commit`. + If specified and `create_pr` is `True`, the pull request will be created from `parent_commit`. + Specifying `parent_commit` ensures the repo has not changed before committing the changes, and can be + especially useful if the repo is updated / committed to concurrently. + + + + + Raises the following errors: + + - [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError) + if the HuggingFace API returned an error + - [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + if some parameter value is invalid + - [`~utils.RepositoryNotFoundError`] + If the repository to download from cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + - [`~utils.RevisionNotFoundError`] + If the revision to download from cannot be found. + - [`~utils.EntryNotFoundError`] + If the file to download cannot be found. + + + + """ + commit_message = ( + commit_message if commit_message is not None else f"Delete {path_in_repo} with huggingface_hub" + ) + + operations = [CommitOperationDelete(path_in_repo=path_in_repo)] + + return self.create_commit( + repo_id=repo_id, + repo_type=repo_type, + token=token, + operations=operations, + revision=revision, + commit_message=commit_message, + commit_description=commit_description, + create_pr=create_pr, + parent_commit=parent_commit, + ) + + @validate_hf_hub_args + def delete_files( + self, + repo_id: str, + delete_patterns: List[str], + *, + token: Union[bool, str, None] = None, + repo_type: Optional[str] = None, + revision: Optional[str] = None, + commit_message: Optional[str] = None, + commit_description: Optional[str] = None, + create_pr: Optional[bool] = None, + parent_commit: Optional[str] = None, + ) -> CommitInfo: + """ + Delete files from a repository on the Hub. + + If a folder path is provided, the entire folder is deleted as well as + all files it contained. + + Args: + repo_id (`str`): + The repository from which the folder will be deleted, for example: + `"username/custom_transformers"` + delete_patterns (`List[str]`): + List of files or folders to delete. Each string can either be + a file path, a folder path or a Unix shell-style wildcard. + E.g. `["file.txt", "folder/", "data/*.parquet"]` + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + to the stored token. + repo_type (`str`, *optional*): + Type of the repo to delete files from. Can be `"model"`, + `"dataset"` or `"space"`. Defaults to `"model"`. + revision (`str`, *optional*): + The git revision to commit from. Defaults to the head of the `"main"` branch. + commit_message (`str`, *optional*): + The summary (first line) of the generated commit. Defaults to + `f"Delete files using huggingface_hub"`. + commit_description (`str` *optional*) + The description of the generated commit. + create_pr (`boolean`, *optional*): + Whether or not to create a Pull Request with that commit. Defaults to `False`. + If `revision` is not set, PR is opened against the `"main"` branch. If + `revision` is set and is a branch, PR is opened against this branch. If + `revision` is set and is not a branch name (example: a commit oid), an + `RevisionNotFoundError` is returned by the server. + parent_commit (`str`, *optional*): + The OID / SHA of the parent commit, as a hexadecimal string. Shorthands (7 first characters) are also supported. + If specified and `create_pr` is `False`, the commit will fail if `revision` does not point to `parent_commit`. + If specified and `create_pr` is `True`, the pull request will be created from `parent_commit`. + Specifying `parent_commit` ensures the repo has not changed before committing the changes, and can be + especially useful if the repo is updated / committed to concurrently. + """ + operations = self._prepare_folder_deletions( + repo_id=repo_id, repo_type=repo_type, delete_patterns=delete_patterns, path_in_repo="", revision=revision + ) + + if commit_message is None: + commit_message = f"Delete files {' '.join(delete_patterns)} with huggingface_hub" + + return self.create_commit( + repo_id=repo_id, + repo_type=repo_type, + token=token, + operations=operations, + revision=revision, + commit_message=commit_message, + commit_description=commit_description, + create_pr=create_pr, + parent_commit=parent_commit, + ) + + @validate_hf_hub_args + def delete_folder( + self, + path_in_repo: str, + repo_id: str, + *, + token: Union[bool, str, None] = None, + repo_type: Optional[str] = None, + revision: Optional[str] = None, + commit_message: Optional[str] = None, + commit_description: Optional[str] = None, + create_pr: Optional[bool] = None, + parent_commit: Optional[str] = None, + ) -> CommitInfo: + """ + Deletes a folder in the given repo. + + Simple wrapper around [`create_commit`] method. + + Args: + path_in_repo (`str`): + Relative folder path in the repo, for example: `"checkpoints/1fec34a"`. + repo_id (`str`): + The repository from which the folder will be deleted, for example: + `"username/custom_transformers"` + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + to the stored token. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if the folder is in a dataset or + space, `None` or `"model"` if in a model. Default is `None`. + revision (`str`, *optional*): + The git revision to commit from. Defaults to the head of the `"main"` branch. + commit_message (`str`, *optional*): + The summary / title / first line of the generated commit. Defaults to + `f"Delete folder {path_in_repo} with huggingface_hub"`. + commit_description (`str` *optional*) + The description of the generated commit. + create_pr (`boolean`, *optional*): + Whether or not to create a Pull Request with that commit. Defaults to `False`. + If `revision` is not set, PR is opened against the `"main"` branch. If + `revision` is set and is a branch, PR is opened against this branch. If + `revision` is set and is not a branch name (example: a commit oid), an + `RevisionNotFoundError` is returned by the server. + parent_commit (`str`, *optional*): + The OID / SHA of the parent commit, as a hexadecimal string. Shorthands (7 first characters) are also supported. + If specified and `create_pr` is `False`, the commit will fail if `revision` does not point to `parent_commit`. + If specified and `create_pr` is `True`, the pull request will be created from `parent_commit`. + Specifying `parent_commit` ensures the repo has not changed before committing the changes, and can be + especially useful if the repo is updated / committed to concurrently. + """ + return self.create_commit( + repo_id=repo_id, + repo_type=repo_type, + token=token, + operations=[CommitOperationDelete(path_in_repo=path_in_repo, is_folder=True)], + revision=revision, + commit_message=( + commit_message if commit_message is not None else f"Delete folder {path_in_repo} with huggingface_hub" + ), + commit_description=commit_description, + create_pr=create_pr, + parent_commit=parent_commit, + ) + + def upload_large_folder( + self, + repo_id: str, + folder_path: Union[str, Path], + *, + repo_type: str, # Repo type is required! + revision: Optional[str] = None, + private: Optional[bool] = None, + allow_patterns: Optional[Union[List[str], str]] = None, + ignore_patterns: Optional[Union[List[str], str]] = None, + num_workers: Optional[int] = None, + print_report: bool = True, + print_report_every: int = 60, + ) -> None: + """Upload a large folder to the Hub in the most resilient way possible. + + Several workers are started to upload files in an optimized way. Before being committed to a repo, files must be + hashed and be pre-uploaded if they are LFS files. Workers will perform these tasks for each file in the folder. + At each step, some metadata information about the upload process is saved in the folder under `.cache/.huggingface/` + to be able to resume the process if interrupted. The whole process might result in several commits. + + Args: + repo_id (`str`): + The repository to which the file will be uploaded. + E.g. `"HuggingFaceTB/smollm-corpus"`. + folder_path (`str` or `Path`): + Path to the folder to upload on the local file system. + repo_type (`str`): + Type of the repository. Must be one of `"model"`, `"dataset"` or `"space"`. + Unlike in all other `HfApi` methods, `repo_type` is explicitly required here. This is to avoid + any mistake when uploading a large folder to the Hub, and therefore prevent from having to re-upload + everything. + revision (`str`, `optional`): + The branch to commit to. If not provided, the `main` branch will be used. + private (`bool`, `optional`): + Whether the repository should be private. + If `None` (default), the repo will be public unless the organization's default is private. + allow_patterns (`List[str]` or `str`, *optional*): + If provided, only files matching at least one pattern are uploaded. + ignore_patterns (`List[str]` or `str`, *optional*): + If provided, files matching any of the patterns are not uploaded. + num_workers (`int`, *optional*): + Number of workers to start. Defaults to `os.cpu_count() - 2` (minimum 2). + A higher number of workers may speed up the process if your machine allows it. However, on machines with a + slower connection, it is recommended to keep the number of workers low to ensure better resumability. + Indeed, partially uploaded files will have to be completely re-uploaded if the process is interrupted. + print_report (`bool`, *optional*): + Whether to print a report of the upload progress. Defaults to True. + Report is printed to `sys.stdout` every X seconds (60 by defaults) and overwrites the previous report. + print_report_every (`int`, *optional*): + Frequency at which the report is printed. Defaults to 60 seconds. + + + + A few things to keep in mind: + - Repository limits still apply: https://huggingface.co/docs/hub/repositories-recommendations + - Do not start several processes in parallel. + - You can interrupt and resume the process at any time. + - Do not upload the same folder to several repositories. If you need to do so, you must delete the local `.cache/.huggingface/` folder first. + + + + + + While being much more robust to upload large folders, `upload_large_folder` is more limited than [`upload_folder`] feature-wise. In practice: + - you cannot set a custom `path_in_repo`. If you want to upload to a subfolder, you need to set the proper structure locally. + - you cannot set a custom `commit_message` and `commit_description` since multiple commits are created. + - you cannot delete from the repo while uploading. Please make a separate commit first. + - you cannot create a PR directly. Please create a PR first (from the UI or using [`create_pull_request`]) and then commit to it by passing `revision`. + + + + **Technical details:** + + `upload_large_folder` process is as follow: + 1. (Check parameters and setup.) + 2. Create repo if missing. + 3. List local files to upload. + 4. Run validation checks and display warnings if repository limits might be exceeded: + - Warns if the total number of files exceeds 100k (recommended limit). + - Warns if any folder contains more than 10k files (recommended limit). + - Warns about files larger than 20GB (recommended) or 50GB (hard limit). + 5. Start workers. Workers can perform the following tasks: + - Hash a file. + - Get upload mode (regular or LFS) for a list of files. + - Pre-upload an LFS file. + - Commit a bunch of files. + Once a worker finishes a task, it will move on to the next task based on the priority list (see below) until + all files are uploaded and committed. + 6. While workers are up, regularly print a report to sys.stdout. + + Order of priority: + 1. Commit if more than 5 minutes since last commit attempt (and at least 1 file). + 2. Commit if at least 150 files are ready to commit. + 3. Get upload mode if at least 10 files have been hashed. + 4. Pre-upload LFS file if at least 1 file and no worker is pre-uploading. + 5. Hash file if at least 1 file and no worker is hashing. + 6. Get upload mode if at least 1 file and no worker is getting upload mode. + 7. Pre-upload LFS file if at least 1 file (exception: if hf_transfer is enabled, only 1 worker can preupload LFS at a time). + 8. Hash file if at least 1 file to hash. + 9. Get upload mode if at least 1 file to get upload mode. + 10. Commit if at least 1 file to commit and at least 1 min since last commit attempt. + 11. Commit if at least 1 file to commit and all other queues are empty. + + Special rules: + - If `hf_transfer` is enabled, only 1 LFS uploader at a time. Otherwise the CPU would be bloated by `hf_transfer`. + - Only one worker can commit at a time. + - If no tasks are available, the worker waits for 10 seconds before checking again. + """ + return upload_large_folder_internal( + self, + repo_id=repo_id, + folder_path=folder_path, + repo_type=repo_type, + revision=revision, + private=private, + allow_patterns=allow_patterns, + ignore_patterns=ignore_patterns, + num_workers=num_workers, + print_report=print_report, + print_report_every=print_report_every, + ) + + @validate_hf_hub_args + def get_hf_file_metadata( + self, + *, + url: str, + token: Union[bool, str, None] = None, + proxies: Optional[Dict] = None, + timeout: Optional[float] = constants.DEFAULT_REQUEST_TIMEOUT, + ) -> HfFileMetadata: + """Fetch metadata of a file versioned on the Hub for a given url. + + Args: + url (`str`): + File url, for example returned by [`hf_hub_url`]. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + proxies (`dict`, *optional*): + Dictionary mapping protocol to the URL of the proxy passed to `requests.request`. + timeout (`float`, *optional*, defaults to 10): + How many seconds to wait for the server to send metadata before giving up. + + Returns: + A [`HfFileMetadata`] object containing metadata such as location, etag, size and commit_hash. + """ + if token is None: + # Cannot do `token = token or self.token` as token can be `False`. + token = self.token + + return get_hf_file_metadata( + url=url, + token=token, + proxies=proxies, + timeout=timeout, + library_name=self.library_name, + library_version=self.library_version, + user_agent=self.user_agent, + endpoint=self.endpoint, + ) + + @validate_hf_hub_args + def hf_hub_download( + self, + repo_id: str, + filename: str, + *, + subfolder: Optional[str] = None, + repo_type: Optional[str] = None, + revision: Optional[str] = None, + cache_dir: Union[str, Path, None] = None, + local_dir: Union[str, Path, None] = None, + force_download: bool = False, + proxies: Optional[Dict] = None, + etag_timeout: float = constants.DEFAULT_ETAG_TIMEOUT, + token: Union[bool, str, None] = None, + local_files_only: bool = False, + # Deprecated args + resume_download: Optional[bool] = None, + force_filename: Optional[str] = None, + local_dir_use_symlinks: Union[bool, Literal["auto"]] = "auto", + ) -> str: + """Download a given file if it's not already present in the local cache. + + The new cache file layout looks like this: + - The cache directory contains one subfolder per repo_id (namespaced by repo type) + - inside each repo folder: + - refs is a list of the latest known revision => commit_hash pairs + - blobs contains the actual file blobs (identified by their git-sha or sha256, depending on + whether they're LFS files or not) + - snapshots contains one subfolder per commit, each "commit" contains the subset of the files + that have been resolved at that particular commit. Each filename is a symlink to the blob + at that particular commit. + + ``` + [ 96] . + └── [ 160] models--julien-c--EsperBERTo-small + ├── [ 160] blobs + │ ├── [321M] 403450e234d65943a7dcf7e05a771ce3c92faa84dd07db4ac20f592037a1e4bd + │ ├── [ 398] 7cb18dc9bafbfcf74629a4b760af1b160957a83e + │ └── [1.4K] d7edf6bd2a681fb0175f7735299831ee1b22b812 + ├── [ 96] refs + │ └── [ 40] main + └── [ 128] snapshots + ├── [ 128] 2439f60ef33a0d46d85da5001d52aeda5b00ce9f + │ ├── [ 52] README.md -> ../../blobs/d7edf6bd2a681fb0175f7735299831ee1b22b812 + │ └── [ 76] pytorch_model.bin -> ../../blobs/403450e234d65943a7dcf7e05a771ce3c92faa84dd07db4ac20f592037a1e4bd + └── [ 128] bbc77c8132af1cc5cf678da3f1ddf2de43606d48 + ├── [ 52] README.md -> ../../blobs/7cb18dc9bafbfcf74629a4b760af1b160957a83e + └── [ 76] pytorch_model.bin -> ../../blobs/403450e234d65943a7dcf7e05a771ce3c92faa84dd07db4ac20f592037a1e4bd + ``` + + If `local_dir` is provided, the file structure from the repo will be replicated in this location. When using this + option, the `cache_dir` will not be used and a `.cache/huggingface/` folder will be created at the root of `local_dir` + to store some metadata related to the downloaded files. While this mechanism is not as robust as the main + cache-system, it's optimized for regularly pulling the latest version of a repository. + + Args: + repo_id (`str`): + A user or an organization name and a repo name separated by a `/`. + filename (`str`): + The name of the file in the repo. + subfolder (`str`, *optional*): + An optional value corresponding to a folder inside the repository. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if downloading from a dataset or space, + `None` or `"model"` if downloading from a model. Default is `None`. + revision (`str`, *optional*): + An optional Git revision id which can be a branch name, a tag, or a + commit hash. + cache_dir (`str`, `Path`, *optional*): + Path to the folder where cached files are stored. + local_dir (`str` or `Path`, *optional*): + If provided, the downloaded file will be placed under this directory. + force_download (`bool`, *optional*, defaults to `False`): + Whether the file should be downloaded even if it already exists in + the local cache. + proxies (`dict`, *optional*): + Dictionary mapping protocol to the URL of the proxy passed to + `requests.request`. + etag_timeout (`float`, *optional*, defaults to `10`): + When fetching ETag, how many seconds to wait for the server to send + data before giving up which is passed to `requests.request`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + local_files_only (`bool`, *optional*, defaults to `False`): + If `True`, avoid downloading the file and return the path to the + local cached file if it exists. + + Returns: + `str`: Local path of file or if networking is off, last version of file cached on disk. + + Raises: + [`~utils.RepositoryNotFoundError`] + If the repository to download from cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + [`~utils.RevisionNotFoundError`] + If the revision to download from cannot be found. + [`~utils.EntryNotFoundError`] + If the file to download cannot be found. + [`~utils.LocalEntryNotFoundError`] + If network is disabled or unavailable and file is not found in cache. + [`EnvironmentError`](https://docs.python.org/3/library/exceptions.html#EnvironmentError) + If `token=True` but the token cannot be found. + [`OSError`](https://docs.python.org/3/library/exceptions.html#OSError) + If ETag cannot be determined. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If some parameter value is invalid. + """ + from .file_download import hf_hub_download + + if token is None: + # Cannot do `token = token or self.token` as token can be `False`. + token = self.token + + return hf_hub_download( + repo_id=repo_id, + filename=filename, + subfolder=subfolder, + repo_type=repo_type, + revision=revision, + endpoint=self.endpoint, + library_name=self.library_name, + library_version=self.library_version, + cache_dir=cache_dir, + local_dir=local_dir, + local_dir_use_symlinks=local_dir_use_symlinks, + user_agent=self.user_agent, + force_download=force_download, + force_filename=force_filename, + proxies=proxies, + etag_timeout=etag_timeout, + resume_download=resume_download, + token=token, + headers=self.headers, + local_files_only=local_files_only, + ) + + @validate_hf_hub_args + def snapshot_download( + self, + repo_id: str, + *, + repo_type: Optional[str] = None, + revision: Optional[str] = None, + cache_dir: Union[str, Path, None] = None, + local_dir: Union[str, Path, None] = None, + proxies: Optional[Dict] = None, + etag_timeout: float = constants.DEFAULT_ETAG_TIMEOUT, + force_download: bool = False, + token: Union[bool, str, None] = None, + local_files_only: bool = False, + allow_patterns: Optional[Union[List[str], str]] = None, + ignore_patterns: Optional[Union[List[str], str]] = None, + max_workers: int = 8, + tqdm_class: Optional[Type[base_tqdm]] = None, + # Deprecated args + local_dir_use_symlinks: Union[bool, Literal["auto"]] = "auto", + resume_download: Optional[bool] = None, + ) -> str: + """Download repo files. + + Download a whole snapshot of a repo's files at the specified revision. This is useful when you want all files from + a repo, because you don't know which ones you will need a priori. All files are nested inside a folder in order + to keep their actual filename relative to that folder. You can also filter which files to download using + `allow_patterns` and `ignore_patterns`. + + If `local_dir` is provided, the file structure from the repo will be replicated in this location. When using this + option, the `cache_dir` will not be used and a `.cache/huggingface/` folder will be created at the root of `local_dir` + to store some metadata related to the downloaded files.While this mechanism is not as robust as the main + cache-system, it's optimized for regularly pulling the latest version of a repository. + + An alternative would be to clone the repo but this requires git and git-lfs to be installed and properly + configured. It is also not possible to filter which files to download when cloning a repository using git. + + Args: + repo_id (`str`): + A user or an organization name and a repo name separated by a `/`. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if downloading from a dataset or space, + `None` or `"model"` if downloading from a model. Default is `None`. + revision (`str`, *optional*): + An optional Git revision id which can be a branch name, a tag, or a + commit hash. + cache_dir (`str`, `Path`, *optional*): + Path to the folder where cached files are stored. + local_dir (`str` or `Path`, *optional*): + If provided, the downloaded files will be placed under this directory. + proxies (`dict`, *optional*): + Dictionary mapping protocol to the URL of the proxy passed to + `requests.request`. + etag_timeout (`float`, *optional*, defaults to `10`): + When fetching ETag, how many seconds to wait for the server to send + data before giving up which is passed to `requests.request`. + force_download (`bool`, *optional*, defaults to `False`): + Whether the file should be downloaded even if it already exists in the local cache. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + local_files_only (`bool`, *optional*, defaults to `False`): + If `True`, avoid downloading the file and return the path to the + local cached file if it exists. + allow_patterns (`List[str]` or `str`, *optional*): + If provided, only files matching at least one pattern are downloaded. + ignore_patterns (`List[str]` or `str`, *optional*): + If provided, files matching any of the patterns are not downloaded. + max_workers (`int`, *optional*): + Number of concurrent threads to download files (1 thread = 1 file download). + Defaults to 8. + tqdm_class (`tqdm`, *optional*): + If provided, overwrites the default behavior for the progress bar. Passed + argument must inherit from `tqdm.auto.tqdm` or at least mimic its behavior. + Note that the `tqdm_class` is not passed to each individual download. + Defaults to the custom HF progress bar that can be disabled by setting + `HF_HUB_DISABLE_PROGRESS_BARS` environment variable. + + Returns: + `str`: folder path of the repo snapshot. + + Raises: + [`~utils.RepositoryNotFoundError`] + If the repository to download from cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + [`~utils.RevisionNotFoundError`] + If the revision to download from cannot be found. + [`EnvironmentError`](https://docs.python.org/3/library/exceptions.html#EnvironmentError) + If `token=True` and the token cannot be found. + [`OSError`](https://docs.python.org/3/library/exceptions.html#OSError) if + ETag cannot be determined. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + if some parameter value is invalid. + """ + from ._snapshot_download import snapshot_download + + if token is None: + # Cannot do `token = token or self.token` as token can be `False`. + token = self.token + + return snapshot_download( + repo_id=repo_id, + repo_type=repo_type, + revision=revision, + endpoint=self.endpoint, + cache_dir=cache_dir, + local_dir=local_dir, + local_dir_use_symlinks=local_dir_use_symlinks, + library_name=self.library_name, + library_version=self.library_version, + user_agent=self.user_agent, + proxies=proxies, + etag_timeout=etag_timeout, + resume_download=resume_download, + force_download=force_download, + token=token, + local_files_only=local_files_only, + allow_patterns=allow_patterns, + ignore_patterns=ignore_patterns, + max_workers=max_workers, + tqdm_class=tqdm_class, + ) + + def get_safetensors_metadata( + self, + repo_id: str, + *, + repo_type: Optional[str] = None, + revision: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> SafetensorsRepoMetadata: + """ + Parse metadata for a safetensors repo on the Hub. + + We first check if the repo has a single safetensors file or a sharded safetensors repo. If it's a single + safetensors file, we parse the metadata from this file. If it's a sharded safetensors repo, we parse the + metadata from the index file and then parse the metadata from each shard. + + To parse metadata from a single safetensors file, use [`parse_safetensors_file_metadata`]. + + For more details regarding the safetensors format, check out https://huggingface.co/docs/safetensors/index#format. + + Args: + repo_id (`str`): + A user or an organization name and a repo name separated by a `/`. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if the file is in a dataset or space, `None` or `"model"` if in a + model. Default is `None`. + revision (`str`, *optional*): + The git revision to fetch the file from. Can be a branch name, a tag, or a commit hash. Defaults to the + head of the `"main"` branch. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`SafetensorsRepoMetadata`]: information related to safetensors repo. + + Raises: + [`NotASafetensorsRepoError`] + If the repo is not a safetensors repo i.e. doesn't have either a + `model.safetensors` or a `model.safetensors.index.json` file. + [`SafetensorsParsingError`] + If a safetensors file header couldn't be parsed correctly. + + Example: + ```py + # Parse repo with single weights file + >>> metadata = get_safetensors_metadata("bigscience/bloomz-560m") + >>> metadata + SafetensorsRepoMetadata( + metadata=None, + sharded=False, + weight_map={'h.0.input_layernorm.bias': 'model.safetensors', ...}, + files_metadata={'model.safetensors': SafetensorsFileMetadata(...)} + ) + >>> metadata.files_metadata["model.safetensors"].metadata + {'format': 'pt'} + + # Parse repo with sharded model + >>> metadata = get_safetensors_metadata("bigscience/bloom") + Parse safetensors files: 100%|██████████████████████████████████████████| 72/72 [00:12<00:00, 5.78it/s] + >>> metadata + SafetensorsRepoMetadata(metadata={'total_size': 352494542848}, sharded=True, weight_map={...}, files_metadata={...}) + >>> len(metadata.files_metadata) + 72 # All safetensors files have been fetched + + # Parse repo with sharded model + >>> get_safetensors_metadata("runwayml/stable-diffusion-v1-5") + NotASafetensorsRepoError: 'runwayml/stable-diffusion-v1-5' is not a safetensors repo. Couldn't find 'model.safetensors.index.json' or 'model.safetensors' files. + ``` + """ + if self.file_exists( # Single safetensors file => non-sharded model + repo_id=repo_id, + filename=constants.SAFETENSORS_SINGLE_FILE, + repo_type=repo_type, + revision=revision, + token=token, + ): + file_metadata = self.parse_safetensors_file_metadata( + repo_id=repo_id, + filename=constants.SAFETENSORS_SINGLE_FILE, + repo_type=repo_type, + revision=revision, + token=token, + ) + return SafetensorsRepoMetadata( + metadata=None, + sharded=False, + weight_map={ + tensor_name: constants.SAFETENSORS_SINGLE_FILE for tensor_name in file_metadata.tensors.keys() + }, + files_metadata={constants.SAFETENSORS_SINGLE_FILE: file_metadata}, + ) + elif self.file_exists( # Multiple safetensors files => sharded with index + repo_id=repo_id, + filename=constants.SAFETENSORS_INDEX_FILE, + repo_type=repo_type, + revision=revision, + token=token, + ): + # Fetch index + index_file = self.hf_hub_download( + repo_id=repo_id, + filename=constants.SAFETENSORS_INDEX_FILE, + repo_type=repo_type, + revision=revision, + token=token, + ) + with open(index_file) as f: + index = json.load(f) + + weight_map = index.get("weight_map", {}) + + # Fetch metadata per shard + files_metadata = {} + + def _parse(filename: str) -> None: + files_metadata[filename] = self.parse_safetensors_file_metadata( + repo_id=repo_id, filename=filename, repo_type=repo_type, revision=revision, token=token + ) + + thread_map( + _parse, + set(weight_map.values()), + desc="Parse safetensors files", + tqdm_class=hf_tqdm, + ) + + return SafetensorsRepoMetadata( + metadata=index.get("metadata", None), + sharded=True, + weight_map=weight_map, + files_metadata=files_metadata, + ) + else: + # Not a safetensors repo + raise NotASafetensorsRepoError( + f"'{repo_id}' is not a safetensors repo. Couldn't find '{constants.SAFETENSORS_INDEX_FILE}' or '{constants.SAFETENSORS_SINGLE_FILE}' files." + ) + + def parse_safetensors_file_metadata( + self, + repo_id: str, + filename: str, + *, + repo_type: Optional[str] = None, + revision: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> SafetensorsFileMetadata: + """ + Parse metadata from a safetensors file on the Hub. + + To parse metadata from all safetensors files in a repo at once, use [`get_safetensors_metadata`]. + + For more details regarding the safetensors format, check out https://huggingface.co/docs/safetensors/index#format. + + Args: + repo_id (`str`): + A user or an organization name and a repo name separated by a `/`. + filename (`str`): + The name of the file in the repo. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if the file is in a dataset or space, `None` or `"model"` if in a + model. Default is `None`. + revision (`str`, *optional*): + The git revision to fetch the file from. Can be a branch name, a tag, or a commit hash. Defaults to the + head of the `"main"` branch. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`SafetensorsFileMetadata`]: information related to a safetensors file. + + Raises: + [`NotASafetensorsRepoError`]: + If the repo is not a safetensors repo i.e. doesn't have either a + `model.safetensors` or a `model.safetensors.index.json` file. + [`SafetensorsParsingError`]: + If a safetensors file header couldn't be parsed correctly. + """ + url = hf_hub_url( + repo_id=repo_id, filename=filename, repo_type=repo_type, revision=revision, endpoint=self.endpoint + ) + _headers = self._build_hf_headers(token=token) + + # 1. Fetch first 100kb + # Empirically, 97% of safetensors files have a metadata size < 100kb (over the top 1000 models on the Hub). + # We assume fetching 100kb is faster than making 2 GET requests. Therefore we always fetch the first 100kb to + # avoid the 2nd GET in most cases. + # See https://github.com/huggingface/huggingface_hub/pull/1855#discussion_r1404286419. + response = get_session().get(url, headers={**_headers, "range": "bytes=0-100000"}) + hf_raise_for_status(response) + + # 2. Parse metadata size + metadata_size = struct.unpack(" constants.SAFETENSORS_MAX_HEADER_LENGTH: + raise SafetensorsParsingError( + f"Failed to parse safetensors header for '{filename}' (repo '{repo_id}', revision " + f"'{revision or constants.DEFAULT_REVISION}'): safetensors header is too big. Maximum supported size is " + f"{constants.SAFETENSORS_MAX_HEADER_LENGTH} bytes (got {metadata_size})." + ) + + # 3.a. Get metadata from payload + if metadata_size <= 100000: + metadata_as_bytes = response.content[8 : 8 + metadata_size] + else: # 3.b. Request full metadata + response = get_session().get(url, headers={**_headers, "range": f"bytes=8-{metadata_size + 7}"}) + hf_raise_for_status(response) + metadata_as_bytes = response.content + + # 4. Parse json header + try: + metadata_as_dict = json.loads(metadata_as_bytes.decode(errors="ignore")) + except json.JSONDecodeError as e: + raise SafetensorsParsingError( + f"Failed to parse safetensors header for '{filename}' (repo '{repo_id}', revision " + f"'{revision or constants.DEFAULT_REVISION}'): header is not json-encoded string. Please make sure this is a " + "correctly formatted safetensors file." + ) from e + + try: + return SafetensorsFileMetadata( + metadata=metadata_as_dict.get("__metadata__", {}), + tensors={ + key: TensorInfo( + dtype=tensor["dtype"], + shape=tensor["shape"], + data_offsets=tuple(tensor["data_offsets"]), # type: ignore + ) + for key, tensor in metadata_as_dict.items() + if key != "__metadata__" + }, + ) + except (KeyError, IndexError) as e: + raise SafetensorsParsingError( + f"Failed to parse safetensors header for '{filename}' (repo '{repo_id}', revision " + f"'{revision or constants.DEFAULT_REVISION}'): header format not recognized. Please make sure this is a correctly" + " formatted safetensors file." + ) from e + + @validate_hf_hub_args + def create_branch( + self, + repo_id: str, + *, + branch: str, + revision: Optional[str] = None, + token: Union[bool, str, None] = None, + repo_type: Optional[str] = None, + exist_ok: bool = False, + ) -> None: + """ + Create a new branch for a repo on the Hub, starting from the specified revision (defaults to `main`). + To find a revision suiting your needs, you can use [`list_repo_refs`] or [`list_repo_commits`]. + + Args: + repo_id (`str`): + The repository in which the branch will be created. + Example: `"user/my-cool-model"`. + + branch (`str`): + The name of the branch to create. + + revision (`str`, *optional*): + The git revision to create the branch from. It can be a branch name or + the OID/SHA of a commit, as a hexadecimal string. Defaults to the head + of the `"main"` branch. + + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if creating a branch on a dataset or + space, `None` or `"model"` if tagging a model. Default is `None`. + + exist_ok (`bool`, *optional*, defaults to `False`): + If `True`, do not raise an error if branch already exists. + + Raises: + [`~utils.RepositoryNotFoundError`]: + If repository is not found (error 404): wrong repo_id/repo_type, private + but not authenticated or repo does not exist. + [`~utils.BadRequestError`]: + If invalid reference for a branch. Ex: `refs/pr/5` or 'refs/foo/bar'. + [`~utils.HfHubHTTPError`]: + If the branch already exists on the repo (error 409) and `exist_ok` is + set to `False`. + """ + if repo_type is None: + repo_type = constants.REPO_TYPE_MODEL + branch = quote(branch, safe="") + + # Prepare request + branch_url = f"{self.endpoint}/api/{repo_type}s/{repo_id}/branch/{branch}" + headers = self._build_hf_headers(token=token) + payload = {} + if revision is not None: + payload["startingPoint"] = revision + + # Create branch + response = get_session().post(url=branch_url, headers=headers, json=payload) + try: + hf_raise_for_status(response) + except HfHubHTTPError as e: + if exist_ok and e.response.status_code == 409: + return + elif exist_ok and e.response.status_code == 403: + # No write permission on the namespace but branch might already exist + try: + refs = self.list_repo_refs(repo_id=repo_id, repo_type=repo_type, token=token) + for branch_ref in refs.branches: + if branch_ref.name == branch: + return # Branch already exists => do not raise + except HfHubHTTPError: + pass # We raise the original error if the branch does not exist + raise + + @validate_hf_hub_args + def delete_branch( + self, + repo_id: str, + *, + branch: str, + token: Union[bool, str, None] = None, + repo_type: Optional[str] = None, + ) -> None: + """ + Delete a branch from a repo on the Hub. + + Args: + repo_id (`str`): + The repository in which a branch will be deleted. + Example: `"user/my-cool-model"`. + + branch (`str`): + The name of the branch to delete. + + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if creating a branch on a dataset or + space, `None` or `"model"` if tagging a model. Default is `None`. + + Raises: + [`~utils.RepositoryNotFoundError`]: + If repository is not found (error 404): wrong repo_id/repo_type, private + but not authenticated or repo does not exist. + [`~utils.HfHubHTTPError`]: + If trying to delete a protected branch. Ex: `main` cannot be deleted. + [`~utils.HfHubHTTPError`]: + If trying to delete a branch that does not exist. + + """ + if repo_type is None: + repo_type = constants.REPO_TYPE_MODEL + branch = quote(branch, safe="") + + # Prepare request + branch_url = f"{self.endpoint}/api/{repo_type}s/{repo_id}/branch/{branch}" + headers = self._build_hf_headers(token=token) + + # Delete branch + response = get_session().delete(url=branch_url, headers=headers) + hf_raise_for_status(response) + + @validate_hf_hub_args + def create_tag( + self, + repo_id: str, + *, + tag: str, + tag_message: Optional[str] = None, + revision: Optional[str] = None, + token: Union[bool, str, None] = None, + repo_type: Optional[str] = None, + exist_ok: bool = False, + ) -> None: + """ + Tag a given commit of a repo on the Hub. + + Args: + repo_id (`str`): + The repository in which a commit will be tagged. + Example: `"user/my-cool-model"`. + + tag (`str`): + The name of the tag to create. + + tag_message (`str`, *optional*): + The description of the tag to create. + + revision (`str`, *optional*): + The git revision to tag. It can be a branch name or the OID/SHA of a + commit, as a hexadecimal string. Shorthands (7 first characters) are + also supported. Defaults to the head of the `"main"` branch. + + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if tagging a dataset or + space, `None` or `"model"` if tagging a model. Default is + `None`. + + exist_ok (`bool`, *optional*, defaults to `False`): + If `True`, do not raise an error if tag already exists. + + Raises: + [`~utils.RepositoryNotFoundError`]: + If repository is not found (error 404): wrong repo_id/repo_type, private + but not authenticated or repo does not exist. + [`~utils.RevisionNotFoundError`]: + If revision is not found (error 404) on the repo. + [`~utils.HfHubHTTPError`]: + If the branch already exists on the repo (error 409) and `exist_ok` is + set to `False`. + """ + if repo_type is None: + repo_type = constants.REPO_TYPE_MODEL + revision = quote(revision, safe="") if revision is not None else constants.DEFAULT_REVISION + + # Prepare request + tag_url = f"{self.endpoint}/api/{repo_type}s/{repo_id}/tag/{revision}" + headers = self._build_hf_headers(token=token) + payload = {"tag": tag} + if tag_message is not None: + payload["message"] = tag_message + + # Tag + response = get_session().post(url=tag_url, headers=headers, json=payload) + try: + hf_raise_for_status(response) + except HfHubHTTPError as e: + if not (e.response.status_code == 409 and exist_ok): + raise + + @validate_hf_hub_args + def delete_tag( + self, + repo_id: str, + *, + tag: str, + token: Union[bool, str, None] = None, + repo_type: Optional[str] = None, + ) -> None: + """ + Delete a tag from a repo on the Hub. + + Args: + repo_id (`str`): + The repository in which a tag will be deleted. + Example: `"user/my-cool-model"`. + + tag (`str`): + The name of the tag to delete. + + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if tagging a dataset or space, `None` or + `"model"` if tagging a model. Default is `None`. + + Raises: + [`~utils.RepositoryNotFoundError`]: + If repository is not found (error 404): wrong repo_id/repo_type, private + but not authenticated or repo does not exist. + [`~utils.RevisionNotFoundError`]: + If tag is not found. + """ + if repo_type is None: + repo_type = constants.REPO_TYPE_MODEL + tag = quote(tag, safe="") + + # Prepare request + tag_url = f"{self.endpoint}/api/{repo_type}s/{repo_id}/tag/{tag}" + headers = self._build_hf_headers(token=token) + + # Un-tag + response = get_session().delete(url=tag_url, headers=headers) + hf_raise_for_status(response) + + @validate_hf_hub_args + def get_full_repo_name( + self, + model_id: str, + *, + organization: Optional[str] = None, + token: Union[bool, str, None] = None, + ): + """ + Returns the repository name for a given model ID and optional + organization. + + Args: + model_id (`str`): + The name of the model. + organization (`str`, *optional*): + If passed, the repository name will be in the organization + namespace instead of the user namespace. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + `str`: The repository name in the user's namespace + ({username}/{model_id}) if no organization is passed, and under the + organization namespace ({organization}/{model_id}) otherwise. + """ + if organization is None: + if "/" in model_id: + username = model_id.split("/")[0] + else: + username = self.whoami(token=token)["name"] # type: ignore + return f"{username}/{model_id}" + else: + return f"{organization}/{model_id}" + + @validate_hf_hub_args + def get_repo_discussions( + self, + repo_id: str, + *, + author: Optional[str] = None, + discussion_type: Optional[constants.DiscussionTypeFilter] = None, + discussion_status: Optional[constants.DiscussionStatusFilter] = None, + repo_type: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> Iterator[Discussion]: + """ + Fetches Discussions and Pull Requests for the given repo. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + author (`str`, *optional*): + Pass a value to filter by discussion author. `None` means no filter. + Default is `None`. + discussion_type (`str`, *optional*): + Set to `"pull_request"` to fetch only pull requests, `"discussion"` + to fetch only discussions. Set to `"all"` or `None` to fetch both. + Default is `None`. + discussion_status (`str`, *optional*): + Set to `"open"` (respectively `"closed"`) to fetch only open + (respectively closed) discussions. Set to `"all"` or `None` + to fetch both. + Default is `None`. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if fetching from a dataset or + space, `None` or `"model"` if fetching from a model. Default is + `None`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + `Iterator[Discussion]`: An iterator of [`Discussion`] objects. + + Example: + Collecting all discussions of a repo in a list: + + ```python + >>> from huggingface_hub import get_repo_discussions + >>> discussions_list = list(get_repo_discussions(repo_id="bert-base-uncased")) + ``` + + Iterating over discussions of a repo: + + ```python + >>> from huggingface_hub import get_repo_discussions + >>> for discussion in get_repo_discussions(repo_id="bert-base-uncased"): + ... print(discussion.num, discussion.title) + ``` + """ + if repo_type not in constants.REPO_TYPES: + raise ValueError(f"Invalid repo type, must be one of {constants.REPO_TYPES}") + if repo_type is None: + repo_type = constants.REPO_TYPE_MODEL + if discussion_type is not None and discussion_type not in constants.DISCUSSION_TYPES: + raise ValueError(f"Invalid discussion_type, must be one of {constants.DISCUSSION_TYPES}") + if discussion_status is not None and discussion_status not in constants.DISCUSSION_STATUS: + raise ValueError(f"Invalid discussion_status, must be one of {constants.DISCUSSION_STATUS}") + + headers = self._build_hf_headers(token=token) + path = f"{self.endpoint}/api/{repo_type}s/{repo_id}/discussions" + + params: Dict[str, Union[str, int]] = {} + if discussion_type is not None: + params["type"] = discussion_type + if discussion_status is not None: + params["status"] = discussion_status + if author is not None: + params["author"] = author + + def _fetch_discussion_page(page_index: int): + params["p"] = page_index + resp = get_session().get(path, headers=headers, params=params) + hf_raise_for_status(resp) + paginated_discussions = resp.json() + total = paginated_discussions["count"] + start = paginated_discussions["start"] + discussions = paginated_discussions["discussions"] + has_next = (start + len(discussions)) < total + return discussions, has_next + + has_next, page_index = True, 0 + + while has_next: + discussions, has_next = _fetch_discussion_page(page_index=page_index) + for discussion in discussions: + yield Discussion( + title=discussion["title"], + num=discussion["num"], + author=discussion.get("author", {}).get("name", "deleted"), + created_at=parse_datetime(discussion["createdAt"]), + status=discussion["status"], + repo_id=discussion["repo"]["name"], + repo_type=discussion["repo"]["type"], + is_pull_request=discussion["isPullRequest"], + endpoint=self.endpoint, + ) + page_index = page_index + 1 + + @validate_hf_hub_args + def get_discussion_details( + self, + repo_id: str, + discussion_num: int, + *, + repo_type: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> DiscussionWithDetails: + """Fetches a Discussion's / Pull Request 's details from the Hub. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + discussion_num (`int`): + The number of the Discussion or Pull Request . Must be a strictly positive integer. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if uploading to a dataset or + space, `None` or `"model"` if uploading to a model. Default is + `None`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: [`DiscussionWithDetails`] + + + + Raises the following errors: + + - [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError) + if the HuggingFace API returned an error + - [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + if some parameter value is invalid + - [`~utils.RepositoryNotFoundError`] + If the repository to download from cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + + + """ + if not isinstance(discussion_num, int) or discussion_num <= 0: + raise ValueError("Invalid discussion_num, must be a positive integer") + if repo_type not in constants.REPO_TYPES: + raise ValueError(f"Invalid repo type, must be one of {constants.REPO_TYPES}") + if repo_type is None: + repo_type = constants.REPO_TYPE_MODEL + + path = f"{self.endpoint}/api/{repo_type}s/{repo_id}/discussions/{discussion_num}" + headers = self._build_hf_headers(token=token) + resp = get_session().get(path, params={"diff": "1"}, headers=headers) + hf_raise_for_status(resp) + + discussion_details = resp.json() + is_pull_request = discussion_details["isPullRequest"] + + target_branch = discussion_details["changes"]["base"] if is_pull_request else None + conflicting_files = discussion_details["filesWithConflicts"] if is_pull_request else None + merge_commit_oid = discussion_details["changes"].get("mergeCommitId", None) if is_pull_request else None + + return DiscussionWithDetails( + title=discussion_details["title"], + num=discussion_details["num"], + author=discussion_details.get("author", {}).get("name", "deleted"), + created_at=parse_datetime(discussion_details["createdAt"]), + status=discussion_details["status"], + repo_id=discussion_details["repo"]["name"], + repo_type=discussion_details["repo"]["type"], + is_pull_request=discussion_details["isPullRequest"], + events=[deserialize_event(evt) for evt in discussion_details["events"]], + conflicting_files=conflicting_files, + target_branch=target_branch, + merge_commit_oid=merge_commit_oid, + diff=discussion_details.get("diff"), + endpoint=self.endpoint, + ) + + @validate_hf_hub_args + def create_discussion( + self, + repo_id: str, + title: str, + *, + token: Union[bool, str, None] = None, + description: Optional[str] = None, + repo_type: Optional[str] = None, + pull_request: bool = False, + ) -> DiscussionWithDetails: + """Creates a Discussion or Pull Request. + + Pull Requests created programmatically will be in `"draft"` status. + + Creating a Pull Request with changes can also be done at once with [`HfApi.create_commit`]. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + title (`str`): + The title of the discussion. It can be up to 200 characters long, + and must be at least 3 characters long. Leading and trailing whitespaces + will be stripped. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + description (`str`, *optional*): + An optional description for the Pull Request. + Defaults to `"Discussion opened with the huggingface_hub Python library"` + pull_request (`bool`, *optional*): + Whether to create a Pull Request or discussion. If `True`, creates a Pull Request. + If `False`, creates a discussion. Defaults to `False`. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if uploading to a dataset or + space, `None` or `"model"` if uploading to a model. Default is + `None`. + + Returns: [`DiscussionWithDetails`] + + + + Raises the following errors: + + - [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError) + if the HuggingFace API returned an error + - [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + if some parameter value is invalid + - [`~utils.RepositoryNotFoundError`] + If the repository to download from cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + + """ + if repo_type not in constants.REPO_TYPES: + raise ValueError(f"Invalid repo type, must be one of {constants.REPO_TYPES}") + if repo_type is None: + repo_type = constants.REPO_TYPE_MODEL + + if description is not None: + description = description.strip() + description = ( + description + if description + else ( + f"{'Pull Request' if pull_request else 'Discussion'} opened with the" + " [huggingface_hub Python" + " library](https://huggingface.co/docs/huggingface_hub)" + ) + ) + + headers = self._build_hf_headers(token=token) + resp = get_session().post( + f"{self.endpoint}/api/{repo_type}s/{repo_id}/discussions", + json={ + "title": title.strip(), + "description": description, + "pullRequest": pull_request, + }, + headers=headers, + ) + hf_raise_for_status(resp) + num = resp.json()["num"] + return self.get_discussion_details( + repo_id=repo_id, + repo_type=repo_type, + discussion_num=num, + token=token, + ) + + @validate_hf_hub_args + def create_pull_request( + self, + repo_id: str, + title: str, + *, + token: Union[bool, str, None] = None, + description: Optional[str] = None, + repo_type: Optional[str] = None, + ) -> DiscussionWithDetails: + """Creates a Pull Request . Pull Requests created programmatically will be in `"draft"` status. + + Creating a Pull Request with changes can also be done at once with [`HfApi.create_commit`]; + + This is a wrapper around [`HfApi.create_discussion`]. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + title (`str`): + The title of the discussion. It can be up to 200 characters long, + and must be at least 3 characters long. Leading and trailing whitespaces + will be stripped. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + description (`str`, *optional*): + An optional description for the Pull Request. + Defaults to `"Discussion opened with the huggingface_hub Python library"` + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if uploading to a dataset or + space, `None` or `"model"` if uploading to a model. Default is + `None`. + + Returns: [`DiscussionWithDetails`] + + + + Raises the following errors: + + - [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError) + if the HuggingFace API returned an error + - [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + if some parameter value is invalid + - [`~utils.RepositoryNotFoundError`] + If the repository to download from cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + + """ + return self.create_discussion( + repo_id=repo_id, + title=title, + token=token, + description=description, + repo_type=repo_type, + pull_request=True, + ) + + def _post_discussion_changes( + self, + *, + repo_id: str, + discussion_num: int, + resource: str, + body: Optional[dict] = None, + token: Union[bool, str, None] = None, + repo_type: Optional[str] = None, + ) -> requests.Response: + """Internal utility to POST changes to a Discussion or Pull Request""" + if not isinstance(discussion_num, int) or discussion_num <= 0: + raise ValueError("Invalid discussion_num, must be a positive integer") + if repo_type not in constants.REPO_TYPES: + raise ValueError(f"Invalid repo type, must be one of {constants.REPO_TYPES}") + if repo_type is None: + repo_type = constants.REPO_TYPE_MODEL + repo_id = f"{repo_type}s/{repo_id}" + + path = f"{self.endpoint}/api/{repo_id}/discussions/{discussion_num}/{resource}" + + headers = self._build_hf_headers(token=token) + resp = requests.post(path, headers=headers, json=body) + hf_raise_for_status(resp) + return resp + + @validate_hf_hub_args + def comment_discussion( + self, + repo_id: str, + discussion_num: int, + comment: str, + *, + token: Union[bool, str, None] = None, + repo_type: Optional[str] = None, + ) -> DiscussionComment: + """Creates a new comment on the given Discussion. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + discussion_num (`int`): + The number of the Discussion or Pull Request . Must be a strictly positive integer. + comment (`str`): + The content of the comment to create. Comments support markdown formatting. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if uploading to a dataset or + space, `None` or `"model"` if uploading to a model. Default is + `None`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`DiscussionComment`]: the newly created comment + + + Examples: + ```python + + >>> comment = \"\"\" + ... Hello @otheruser! + ... + ... # This is a title + ... + ... **This is bold**, *this is italic* and ~this is strikethrough~ + ... And [this](http://url) is a link + ... \"\"\" + + >>> HfApi().comment_discussion( + ... repo_id="username/repo_name", + ... discussion_num=34 + ... comment=comment + ... ) + # DiscussionComment(id='deadbeef0000000', type='comment', ...) + + ``` + + + + Raises the following errors: + + - [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError) + if the HuggingFace API returned an error + - [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + if some parameter value is invalid + - [`~utils.RepositoryNotFoundError`] + If the repository to download from cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + + + """ + resp = self._post_discussion_changes( + repo_id=repo_id, + repo_type=repo_type, + discussion_num=discussion_num, + token=token, + resource="comment", + body={"comment": comment}, + ) + return deserialize_event(resp.json()["newMessage"]) # type: ignore + + @validate_hf_hub_args + def rename_discussion( + self, + repo_id: str, + discussion_num: int, + new_title: str, + *, + token: Union[bool, str, None] = None, + repo_type: Optional[str] = None, + ) -> DiscussionTitleChange: + """Renames a Discussion. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + discussion_num (`int`): + The number of the Discussion or Pull Request . Must be a strictly positive integer. + new_title (`str`): + The new title for the discussion + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if uploading to a dataset or + space, `None` or `"model"` if uploading to a model. Default is + `None`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`DiscussionTitleChange`]: the title change event + + + Examples: + ```python + >>> new_title = "New title, fixing a typo" + >>> HfApi().rename_discussion( + ... repo_id="username/repo_name", + ... discussion_num=34 + ... new_title=new_title + ... ) + # DiscussionTitleChange(id='deadbeef0000000', type='title-change', ...) + + ``` + + + + Raises the following errors: + + - [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError) + if the HuggingFace API returned an error + - [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + if some parameter value is invalid + - [`~utils.RepositoryNotFoundError`] + If the repository to download from cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + + + """ + resp = self._post_discussion_changes( + repo_id=repo_id, + repo_type=repo_type, + discussion_num=discussion_num, + token=token, + resource="title", + body={"title": new_title}, + ) + return deserialize_event(resp.json()["newTitle"]) # type: ignore + + @validate_hf_hub_args + def change_discussion_status( + self, + repo_id: str, + discussion_num: int, + new_status: Literal["open", "closed"], + *, + token: Union[bool, str, None] = None, + comment: Optional[str] = None, + repo_type: Optional[str] = None, + ) -> DiscussionStatusChange: + """Closes or re-opens a Discussion or Pull Request. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + discussion_num (`int`): + The number of the Discussion or Pull Request . Must be a strictly positive integer. + new_status (`str`): + The new status for the discussion, either `"open"` or `"closed"`. + comment (`str`, *optional*): + An optional comment to post with the status change. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if uploading to a dataset or + space, `None` or `"model"` if uploading to a model. Default is + `None`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`DiscussionStatusChange`]: the status change event + + + Examples: + ```python + >>> new_title = "New title, fixing a typo" + >>> HfApi().rename_discussion( + ... repo_id="username/repo_name", + ... discussion_num=34 + ... new_title=new_title + ... ) + # DiscussionStatusChange(id='deadbeef0000000', type='status-change', ...) + + ``` + + + + Raises the following errors: + + - [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError) + if the HuggingFace API returned an error + - [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + if some parameter value is invalid + - [`~utils.RepositoryNotFoundError`] + If the repository to download from cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + + + """ + if new_status not in ["open", "closed"]: + raise ValueError("Invalid status, valid statuses are: 'open' and 'closed'") + body: Dict[str, str] = {"status": new_status} + if comment and comment.strip(): + body["comment"] = comment.strip() + resp = self._post_discussion_changes( + repo_id=repo_id, + repo_type=repo_type, + discussion_num=discussion_num, + token=token, + resource="status", + body=body, + ) + return deserialize_event(resp.json()["newStatus"]) # type: ignore + + @validate_hf_hub_args + def merge_pull_request( + self, + repo_id: str, + discussion_num: int, + *, + token: Union[bool, str, None] = None, + comment: Optional[str] = None, + repo_type: Optional[str] = None, + ): + """Merges a Pull Request. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + discussion_num (`int`): + The number of the Discussion or Pull Request . Must be a strictly positive integer. + comment (`str`, *optional*): + An optional comment to post with the status change. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if uploading to a dataset or + space, `None` or `"model"` if uploading to a model. Default is + `None`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`DiscussionStatusChange`]: the status change event + + + + Raises the following errors: + + - [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError) + if the HuggingFace API returned an error + - [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + if some parameter value is invalid + - [`~utils.RepositoryNotFoundError`] + If the repository to download from cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + + + """ + self._post_discussion_changes( + repo_id=repo_id, + repo_type=repo_type, + discussion_num=discussion_num, + token=token, + resource="merge", + body={"comment": comment.strip()} if comment and comment.strip() else None, + ) + + @validate_hf_hub_args + def edit_discussion_comment( + self, + repo_id: str, + discussion_num: int, + comment_id: str, + new_content: str, + *, + token: Union[bool, str, None] = None, + repo_type: Optional[str] = None, + ) -> DiscussionComment: + """Edits a comment on a Discussion / Pull Request. + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + discussion_num (`int`): + The number of the Discussion or Pull Request . Must be a strictly positive integer. + comment_id (`str`): + The ID of the comment to edit. + new_content (`str`): + The new content of the comment. Comments support markdown formatting. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if uploading to a dataset or + space, `None` or `"model"` if uploading to a model. Default is + `None`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`DiscussionComment`]: the edited comment + + + + Raises the following errors: + + - [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError) + if the HuggingFace API returned an error + - [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + if some parameter value is invalid + - [`~utils.RepositoryNotFoundError`] + If the repository to download from cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + + + """ + resp = self._post_discussion_changes( + repo_id=repo_id, + repo_type=repo_type, + discussion_num=discussion_num, + token=token, + resource=f"comment/{comment_id.lower()}/edit", + body={"content": new_content}, + ) + return deserialize_event(resp.json()["updatedComment"]) # type: ignore + + @validate_hf_hub_args + def hide_discussion_comment( + self, + repo_id: str, + discussion_num: int, + comment_id: str, + *, + token: Union[bool, str, None] = None, + repo_type: Optional[str] = None, + ) -> DiscussionComment: + """Hides a comment on a Discussion / Pull Request. + + + Hidden comments' content cannot be retrieved anymore. Hiding a comment is irreversible. + + + Args: + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + discussion_num (`int`): + The number of the Discussion or Pull Request . Must be a strictly positive integer. + comment_id (`str`): + The ID of the comment to edit. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if uploading to a dataset or + space, `None` or `"model"` if uploading to a model. Default is + `None`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`DiscussionComment`]: the hidden comment + + + + Raises the following errors: + + - [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError) + if the HuggingFace API returned an error + - [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + if some parameter value is invalid + - [`~utils.RepositoryNotFoundError`] + If the repository to download from cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + + + """ + warnings.warn( + "Hidden comments' content cannot be retrieved anymore. Hiding a comment is irreversible.", + UserWarning, + ) + resp = self._post_discussion_changes( + repo_id=repo_id, + repo_type=repo_type, + discussion_num=discussion_num, + token=token, + resource=f"comment/{comment_id.lower()}/hide", + ) + return deserialize_event(resp.json()["updatedComment"]) # type: ignore + + @validate_hf_hub_args + def add_space_secret( + self, + repo_id: str, + key: str, + value: str, + *, + description: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> None: + """Adds or updates a secret in a Space. + + Secrets allow to set secret keys or tokens to a Space without hardcoding them. + For more details, see https://huggingface.co/docs/hub/spaces-overview#managing-secrets. + + Args: + repo_id (`str`): + ID of the repo to update. Example: `"bigcode/in-the-stack"`. + key (`str`): + Secret key. Example: `"GITHUB_API_KEY"` + value (`str`): + Secret value. Example: `"your_github_api_key"`. + description (`str`, *optional*): + Secret description. Example: `"Github API key to access the Github API"`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + """ + payload = {"key": key, "value": value} + if description is not None: + payload["description"] = description + r = get_session().post( + f"{self.endpoint}/api/spaces/{repo_id}/secrets", + headers=self._build_hf_headers(token=token), + json=payload, + ) + hf_raise_for_status(r) + + @validate_hf_hub_args + def delete_space_secret(self, repo_id: str, key: str, *, token: Union[bool, str, None] = None) -> None: + """Deletes a secret from a Space. + + Secrets allow to set secret keys or tokens to a Space without hardcoding them. + For more details, see https://huggingface.co/docs/hub/spaces-overview#managing-secrets. + + Args: + repo_id (`str`): + ID of the repo to update. Example: `"bigcode/in-the-stack"`. + key (`str`): + Secret key. Example: `"GITHUB_API_KEY"`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + """ + r = get_session().delete( + f"{self.endpoint}/api/spaces/{repo_id}/secrets", + headers=self._build_hf_headers(token=token), + json={"key": key}, + ) + hf_raise_for_status(r) + + @validate_hf_hub_args + def get_space_variables(self, repo_id: str, *, token: Union[bool, str, None] = None) -> Dict[str, SpaceVariable]: + """Gets all variables from a Space. + + Variables allow to set environment variables to a Space without hardcoding them. + For more details, see https://huggingface.co/docs/hub/spaces-overview#managing-secrets-and-environment-variables + + Args: + repo_id (`str`): + ID of the repo to query. Example: `"bigcode/in-the-stack"`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + """ + r = get_session().get( + f"{self.endpoint}/api/spaces/{repo_id}/variables", + headers=self._build_hf_headers(token=token), + ) + hf_raise_for_status(r) + return {k: SpaceVariable(k, v) for k, v in r.json().items()} + + @validate_hf_hub_args + def add_space_variable( + self, + repo_id: str, + key: str, + value: str, + *, + description: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> Dict[str, SpaceVariable]: + """Adds or updates a variable in a Space. + + Variables allow to set environment variables to a Space without hardcoding them. + For more details, see https://huggingface.co/docs/hub/spaces-overview#managing-secrets-and-environment-variables + + Args: + repo_id (`str`): + ID of the repo to update. Example: `"bigcode/in-the-stack"`. + key (`str`): + Variable key. Example: `"MODEL_REPO_ID"` + value (`str`): + Variable value. Example: `"the_model_repo_id"`. + description (`str`): + Description of the variable. Example: `"Model Repo ID of the implemented model"`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + """ + payload = {"key": key, "value": value} + if description is not None: + payload["description"] = description + r = get_session().post( + f"{self.endpoint}/api/spaces/{repo_id}/variables", + headers=self._build_hf_headers(token=token), + json=payload, + ) + hf_raise_for_status(r) + return {k: SpaceVariable(k, v) for k, v in r.json().items()} + + @validate_hf_hub_args + def delete_space_variable( + self, repo_id: str, key: str, *, token: Union[bool, str, None] = None + ) -> Dict[str, SpaceVariable]: + """Deletes a variable from a Space. + + Variables allow to set environment variables to a Space without hardcoding them. + For more details, see https://huggingface.co/docs/hub/spaces-overview#managing-secrets-and-environment-variables + + Args: + repo_id (`str`): + ID of the repo to update. Example: `"bigcode/in-the-stack"`. + key (`str`): + Variable key. Example: `"MODEL_REPO_ID"` + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + """ + r = get_session().delete( + f"{self.endpoint}/api/spaces/{repo_id}/variables", + headers=self._build_hf_headers(token=token), + json={"key": key}, + ) + hf_raise_for_status(r) + return {k: SpaceVariable(k, v) for k, v in r.json().items()} + + @validate_hf_hub_args + def get_space_runtime(self, repo_id: str, *, token: Union[bool, str, None] = None) -> SpaceRuntime: + """Gets runtime information about a Space. + + Args: + repo_id (`str`): + ID of the repo to update. Example: `"bigcode/in-the-stack"`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + Returns: + [`SpaceRuntime`]: Runtime information about a Space including Space stage and hardware. + """ + r = get_session().get( + f"{self.endpoint}/api/spaces/{repo_id}/runtime", headers=self._build_hf_headers(token=token) + ) + hf_raise_for_status(r) + return SpaceRuntime(r.json()) + + @validate_hf_hub_args + def request_space_hardware( + self, + repo_id: str, + hardware: SpaceHardware, + *, + token: Union[bool, str, None] = None, + sleep_time: Optional[int] = None, + ) -> SpaceRuntime: + """Request new hardware for a Space. + + Args: + repo_id (`str`): + ID of the repo to update. Example: `"bigcode/in-the-stack"`. + hardware (`str` or [`SpaceHardware`]): + Hardware on which to run the Space. Example: `"t4-medium"`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + sleep_time (`int`, *optional*): + Number of seconds of inactivity to wait before a Space is put to sleep. Set to `-1` if you don't want + your Space to sleep (default behavior for upgraded hardware). For free hardware, you can't configure + the sleep time (value is fixed to 48 hours of inactivity). + See https://huggingface.co/docs/hub/spaces-gpus#sleep-time for more details. + Returns: + [`SpaceRuntime`]: Runtime information about a Space including Space stage and hardware. + + + + It is also possible to request hardware directly when creating the Space repo! See [`create_repo`] for details. + + + """ + if sleep_time is not None and hardware == SpaceHardware.CPU_BASIC: + warnings.warn( + "If your Space runs on the default 'cpu-basic' hardware, it will go to sleep if inactive for more" + " than 48 hours. This value is not configurable. If you don't want your Space to deactivate or if" + " you want to set a custom sleep time, you need to upgrade to a paid Hardware.", + UserWarning, + ) + payload: Dict[str, Any] = {"flavor": hardware} + if sleep_time is not None: + payload["sleepTimeSeconds"] = sleep_time + r = get_session().post( + f"{self.endpoint}/api/spaces/{repo_id}/hardware", + headers=self._build_hf_headers(token=token), + json=payload, + ) + hf_raise_for_status(r) + return SpaceRuntime(r.json()) + + @validate_hf_hub_args + def set_space_sleep_time( + self, repo_id: str, sleep_time: int, *, token: Union[bool, str, None] = None + ) -> SpaceRuntime: + """Set a custom sleep time for a Space running on upgraded hardware.. + + Your Space will go to sleep after X seconds of inactivity. You are not billed when your Space is in "sleep" + mode. If a new visitor lands on your Space, it will "wake it up". Only upgraded hardware can have a + configurable sleep time. To know more about the sleep stage, please refer to + https://huggingface.co/docs/hub/spaces-gpus#sleep-time. + + Args: + repo_id (`str`): + ID of the repo to update. Example: `"bigcode/in-the-stack"`. + sleep_time (`int`, *optional*): + Number of seconds of inactivity to wait before a Space is put to sleep. Set to `-1` if you don't want + your Space to pause (default behavior for upgraded hardware). For free hardware, you can't configure + the sleep time (value is fixed to 48 hours of inactivity). + See https://huggingface.co/docs/hub/spaces-gpus#sleep-time for more details. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + Returns: + [`SpaceRuntime`]: Runtime information about a Space including Space stage and hardware. + + + + It is also possible to set a custom sleep time when requesting hardware with [`request_space_hardware`]. + + + """ + r = get_session().post( + f"{self.endpoint}/api/spaces/{repo_id}/sleeptime", + headers=self._build_hf_headers(token=token), + json={"seconds": sleep_time}, + ) + hf_raise_for_status(r) + runtime = SpaceRuntime(r.json()) + + hardware = runtime.requested_hardware or runtime.hardware + if hardware == SpaceHardware.CPU_BASIC: + warnings.warn( + "If your Space runs on the default 'cpu-basic' hardware, it will go to sleep if inactive for more" + " than 48 hours. This value is not configurable. If you don't want your Space to deactivate or if" + " you want to set a custom sleep time, you need to upgrade to a paid Hardware.", + UserWarning, + ) + return runtime + + @validate_hf_hub_args + def pause_space(self, repo_id: str, *, token: Union[bool, str, None] = None) -> SpaceRuntime: + """Pause your Space. + + A paused Space stops executing until manually restarted by its owner. This is different from the sleeping + state in which free Spaces go after 48h of inactivity. Paused time is not billed to your account, no matter the + hardware you've selected. To restart your Space, use [`restart_space`] and go to your Space settings page. + + For more details, please visit [the docs](https://huggingface.co/docs/hub/spaces-gpus#pause). + + Args: + repo_id (`str`): + ID of the Space to pause. Example: `"Salesforce/BLIP2"`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`SpaceRuntime`]: Runtime information about your Space including `stage=PAUSED` and requested hardware. + + Raises: + [`~utils.RepositoryNotFoundError`]: + If your Space is not found (error 404). Most probably wrong repo_id or your space is private but you + are not authenticated. + [`~utils.HfHubHTTPError`]: + 403 Forbidden: only the owner of a Space can pause it. If you want to manage a Space that you don't + own, either ask the owner by opening a Discussion or duplicate the Space. + [`~utils.BadRequestError`]: + If your Space is a static Space. Static Spaces are always running and never billed. If you want to hide + a static Space, you can set it to private. + """ + r = get_session().post( + f"{self.endpoint}/api/spaces/{repo_id}/pause", headers=self._build_hf_headers(token=token) + ) + hf_raise_for_status(r) + return SpaceRuntime(r.json()) + + @validate_hf_hub_args + def restart_space( + self, repo_id: str, *, token: Union[bool, str, None] = None, factory_reboot: bool = False + ) -> SpaceRuntime: + """Restart your Space. + + This is the only way to programmatically restart a Space if you've put it on Pause (see [`pause_space`]). You + must be the owner of the Space to restart it. If you are using an upgraded hardware, your account will be + billed as soon as the Space is restarted. You can trigger a restart no matter the current state of a Space. + + For more details, please visit [the docs](https://huggingface.co/docs/hub/spaces-gpus#pause). + + Args: + repo_id (`str`): + ID of the Space to restart. Example: `"Salesforce/BLIP2"`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + factory_reboot (`bool`, *optional*): + If `True`, the Space will be rebuilt from scratch without caching any requirements. + + Returns: + [`SpaceRuntime`]: Runtime information about your Space. + + Raises: + [`~utils.RepositoryNotFoundError`]: + If your Space is not found (error 404). Most probably wrong repo_id or your space is private but you + are not authenticated. + [`~utils.HfHubHTTPError`]: + 403 Forbidden: only the owner of a Space can restart it. If you want to restart a Space that you don't + own, either ask the owner by opening a Discussion or duplicate the Space. + [`~utils.BadRequestError`]: + If your Space is a static Space. Static Spaces are always running and never billed. If you want to hide + a static Space, you can set it to private. + """ + params = {} + if factory_reboot: + params["factory"] = "true" + r = get_session().post( + f"{self.endpoint}/api/spaces/{repo_id}/restart", headers=self._build_hf_headers(token=token), params=params + ) + hf_raise_for_status(r) + return SpaceRuntime(r.json()) + + @validate_hf_hub_args + def duplicate_space( + self, + from_id: str, + to_id: Optional[str] = None, + *, + private: Optional[bool] = None, + token: Union[bool, str, None] = None, + exist_ok: bool = False, + hardware: Optional[SpaceHardware] = None, + storage: Optional[SpaceStorage] = None, + sleep_time: Optional[int] = None, + secrets: Optional[List[Dict[str, str]]] = None, + variables: Optional[List[Dict[str, str]]] = None, + ) -> RepoUrl: + """Duplicate a Space. + + Programmatically duplicate a Space. The new Space will be created in your account and will be in the same state + as the original Space (running or paused). You can duplicate a Space no matter the current state of a Space. + + Args: + from_id (`str`): + ID of the Space to duplicate. Example: `"pharma/CLIP-Interrogator"`. + to_id (`str`, *optional*): + ID of the new Space. Example: `"dog/CLIP-Interrogator"`. If not provided, the new Space will have the same + name as the original Space, but in your account. + private (`bool`, *optional*): + Whether the new Space should be private or not. Defaults to the same privacy as the original Space. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + exist_ok (`bool`, *optional*, defaults to `False`): + If `True`, do not raise an error if repo already exists. + hardware (`SpaceHardware` or `str`, *optional*): + Choice of Hardware. Example: `"t4-medium"`. See [`SpaceHardware`] for a complete list. + storage (`SpaceStorage` or `str`, *optional*): + Choice of persistent storage tier. Example: `"small"`. See [`SpaceStorage`] for a complete list. + sleep_time (`int`, *optional*): + Number of seconds of inactivity to wait before a Space is put to sleep. Set to `-1` if you don't want + your Space to sleep (default behavior for upgraded hardware). For free hardware, you can't configure + the sleep time (value is fixed to 48 hours of inactivity). + See https://huggingface.co/docs/hub/spaces-gpus#sleep-time for more details. + secrets (`List[Dict[str, str]]`, *optional*): + A list of secret keys to set in your Space. Each item is in the form `{"key": ..., "value": ..., "description": ...}` where description is optional. + For more details, see https://huggingface.co/docs/hub/spaces-overview#managing-secrets. + variables (`List[Dict[str, str]]`, *optional*): + A list of public environment variables to set in your Space. Each item is in the form `{"key": ..., "value": ..., "description": ...}` where description is optional. + For more details, see https://huggingface.co/docs/hub/spaces-overview#managing-secrets-and-environment-variables. + + Returns: + [`RepoUrl`]: URL to the newly created repo. Value is a subclass of `str` containing + attributes like `endpoint`, `repo_type` and `repo_id`. + + Raises: + [`~utils.RepositoryNotFoundError`]: + If one of `from_id` or `to_id` cannot be found. This may be because it doesn't exist, + or because it is set to `private` and you do not have access. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + If the HuggingFace API returned an error + + Example: + ```python + >>> from huggingface_hub import duplicate_space + + # Duplicate a Space to your account + >>> duplicate_space("multimodalart/dreambooth-training") + RepoUrl('https://huggingface.co/spaces/nateraw/dreambooth-training',...) + + # Can set custom destination id and visibility flag. + >>> duplicate_space("multimodalart/dreambooth-training", to_id="my-dreambooth", private=True) + RepoUrl('https://huggingface.co/spaces/nateraw/my-dreambooth',...) + ``` + """ + # Parse to_id if provided + parsed_to_id = RepoUrl(to_id) if to_id is not None else None + + # Infer target repo_id + to_namespace = ( # set namespace manually or default to username + parsed_to_id.namespace + if parsed_to_id is not None and parsed_to_id.namespace is not None + else self.whoami(token)["name"] + ) + to_repo_name = parsed_to_id.repo_name if to_id is not None else RepoUrl(from_id).repo_name # type: ignore + + # repository must be a valid repo_id (namespace/repo_name). + payload: Dict[str, Any] = {"repository": f"{to_namespace}/{to_repo_name}"} + + keys = ["private", "hardware", "storageTier", "sleepTimeSeconds", "secrets", "variables"] + values = [private, hardware, storage, sleep_time, secrets, variables] + payload.update({k: v for k, v in zip(keys, values) if v is not None}) + + if sleep_time is not None and hardware == SpaceHardware.CPU_BASIC: + warnings.warn( + "If your Space runs on the default 'cpu-basic' hardware, it will go to sleep if inactive for more" + " than 48 hours. This value is not configurable. If you don't want your Space to deactivate or if" + " you want to set a custom sleep time, you need to upgrade to a paid Hardware.", + UserWarning, + ) + + r = get_session().post( + f"{self.endpoint}/api/spaces/{from_id}/duplicate", + headers=self._build_hf_headers(token=token), + json=payload, + ) + + try: + hf_raise_for_status(r) + except HTTPError as err: + if exist_ok and err.response.status_code == 409: + # Repo already exists and `exist_ok=True` + pass + else: + raise + + return RepoUrl(r.json()["url"], endpoint=self.endpoint) + + @validate_hf_hub_args + def request_space_storage( + self, + repo_id: str, + storage: SpaceStorage, + *, + token: Union[bool, str, None] = None, + ) -> SpaceRuntime: + """Request persistent storage for a Space. + + Args: + repo_id (`str`): + ID of the Space to update. Example: `"open-llm-leaderboard/open_llm_leaderboard"`. + storage (`str` or [`SpaceStorage`]): + Storage tier. Either 'small', 'medium', or 'large'. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + Returns: + [`SpaceRuntime`]: Runtime information about a Space including Space stage and hardware. + + + + It is not possible to decrease persistent storage after its granted. To do so, you must delete it + via [`delete_space_storage`]. + + + """ + payload: Dict[str, SpaceStorage] = {"tier": storage} + r = get_session().post( + f"{self.endpoint}/api/spaces/{repo_id}/storage", + headers=self._build_hf_headers(token=token), + json=payload, + ) + hf_raise_for_status(r) + return SpaceRuntime(r.json()) + + @validate_hf_hub_args + def delete_space_storage( + self, + repo_id: str, + *, + token: Union[bool, str, None] = None, + ) -> SpaceRuntime: + """Delete persistent storage for a Space. + + Args: + repo_id (`str`): + ID of the Space to update. Example: `"open-llm-leaderboard/open_llm_leaderboard"`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + Returns: + [`SpaceRuntime`]: Runtime information about a Space including Space stage and hardware. + Raises: + [`BadRequestError`] + If space has no persistent storage. + + """ + r = get_session().delete( + f"{self.endpoint}/api/spaces/{repo_id}/storage", + headers=self._build_hf_headers(token=token), + ) + hf_raise_for_status(r) + return SpaceRuntime(r.json()) + + ####################### + # Inference Endpoints # + ####################### + + def list_inference_endpoints( + self, namespace: Optional[str] = None, *, token: Union[bool, str, None] = None + ) -> List[InferenceEndpoint]: + """Lists all inference endpoints for the given namespace. + + Args: + namespace (`str`, *optional*): + The namespace to list endpoints for. Defaults to the current user. Set to `"*"` to list all endpoints + from all namespaces (i.e. personal namespace and all orgs the user belongs to). + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + List[`InferenceEndpoint`]: A list of all inference endpoints for the given namespace. + + Example: + ```python + >>> from huggingface_hub import HfApi + >>> api = HfApi() + >>> api.list_inference_endpoints() + [InferenceEndpoint(name='my-endpoint', ...), ...] + ``` + """ + # Special case: list all endpoints for all namespaces the user has access to + if namespace == "*": + user = self.whoami(token=token) + + # List personal endpoints first + endpoints: List[InferenceEndpoint] = list_inference_endpoints(namespace=self._get_namespace(token=token)) + + # Then list endpoints for all orgs the user belongs to and ignore 401 errors (no billing or no access) + for org in user.get("orgs", []): + try: + endpoints += list_inference_endpoints(namespace=org["name"], token=token) + except HfHubHTTPError as error: + if error.response.status_code == 401: # Either no billing or user don't have access) + logger.debug("Cannot list Inference Endpoints for org '%s': %s", org["name"], error) + pass + + return endpoints + + # Normal case: list endpoints for a specific namespace + namespace = namespace or self._get_namespace(token=token) + + response = get_session().get( + f"{constants.INFERENCE_ENDPOINTS_ENDPOINT}/endpoint/{namespace}", + headers=self._build_hf_headers(token=token), + ) + hf_raise_for_status(response) + + return [ + InferenceEndpoint.from_raw(endpoint, namespace=namespace, token=token) + for endpoint in response.json()["items"] + ] + + def create_inference_endpoint( + self, + name: str, + *, + repository: str, + framework: str, + accelerator: str, + instance_size: str, + instance_type: str, + region: str, + vendor: str, + account_id: Optional[str] = None, + min_replica: int = 1, + max_replica: int = 1, + scale_to_zero_timeout: Optional[int] = None, + revision: Optional[str] = None, + task: Optional[str] = None, + custom_image: Optional[Dict] = None, + env: Optional[Dict[str, str]] = None, + secrets: Optional[Dict[str, str]] = None, + type: InferenceEndpointType = InferenceEndpointType.PROTECTED, + domain: Optional[str] = None, + path: Optional[str] = None, + cache_http_responses: Optional[bool] = None, + tags: Optional[List[str]] = None, + namespace: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> InferenceEndpoint: + """Create a new Inference Endpoint. + + Args: + name (`str`): + The unique name for the new Inference Endpoint. + repository (`str`): + The name of the model repository associated with the Inference Endpoint (e.g. `"gpt2"`). + framework (`str`): + The machine learning framework used for the model (e.g. `"custom"`). + accelerator (`str`): + The hardware accelerator to be used for inference (e.g. `"cpu"`). + instance_size (`str`): + The size or type of the instance to be used for hosting the model (e.g. `"x4"`). + instance_type (`str`): + The cloud instance type where the Inference Endpoint will be deployed (e.g. `"intel-icl"`). + region (`str`): + The cloud region in which the Inference Endpoint will be created (e.g. `"us-east-1"`). + vendor (`str`): + The cloud provider or vendor where the Inference Endpoint will be hosted (e.g. `"aws"`). + account_id (`str`, *optional*): + The account ID used to link a VPC to a private Inference Endpoint (if applicable). + min_replica (`int`, *optional*): + The minimum number of replicas (instances) to keep running for the Inference Endpoint. To enable + scaling to zero, set this value to 0 and adjust `scale_to_zero_timeout` accordingly. Defaults to 1. + max_replica (`int`, *optional*): + The maximum number of replicas (instances) to scale to for the Inference Endpoint. Defaults to 1. + scale_to_zero_timeout (`int`, *optional*): + The duration in minutes before an inactive endpoint is scaled to zero, or no scaling to zero if + set to None and `min_replica` is not 0. Defaults to None. + revision (`str`, *optional*): + The specific model revision to deploy on the Inference Endpoint (e.g. `"6c0e6080953db56375760c0471a8c5f2929baf11"`). + task (`str`, *optional*): + The task on which to deploy the model (e.g. `"text-classification"`). + custom_image (`Dict`, *optional*): + A custom Docker image to use for the Inference Endpoint. This is useful if you want to deploy an + Inference Endpoint running on the `text-generation-inference` (TGI) framework (see examples). + env (`Dict[str, str]`, *optional*): + Non-secret environment variables to inject in the container environment. + secrets (`Dict[str, str]`, *optional*): + Secret values to inject in the container environment. + type ([`InferenceEndpointType]`, *optional*): + The type of the Inference Endpoint, which can be `"protected"` (default), `"public"` or `"private"`. + domain (`str`, *optional*): + The custom domain for the Inference Endpoint deployment, if setup the inference endpoint will be available at this domain (e.g. `"my-new-domain.cool-website.woof"`). + path (`str`, *optional*): + The custom path to the deployed model, should start with a `/` (e.g. `"/models/google-bert/bert-base-uncased"`). + cache_http_responses (`bool`, *optional*): + Whether to cache HTTP responses from the Inference Endpoint. Defaults to `False`. + tags (`List[str]`, *optional*): + A list of tags to associate with the Inference Endpoint. + namespace (`str`, *optional*): + The namespace where the Inference Endpoint will be created. Defaults to the current user's namespace. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`InferenceEndpoint`]: information about the updated Inference Endpoint. + + Example: + ```python + >>> from huggingface_hub import HfApi + >>> api = HfApi() + >>> endpoint = api.create_inference_endpoint( + ... "my-endpoint-name", + ... repository="gpt2", + ... framework="pytorch", + ... task="text-generation", + ... accelerator="cpu", + ... vendor="aws", + ... region="us-east-1", + ... type="protected", + ... instance_size="x2", + ... instance_type="intel-icl", + ... ) + >>> endpoint + InferenceEndpoint(name='my-endpoint-name', status="pending",...) + + # Run inference on the endpoint + >>> endpoint.client.text_generation(...) + "..." + ``` + + ```python + # Start an Inference Endpoint running Zephyr-7b-beta on TGI + >>> from huggingface_hub import HfApi + >>> api = HfApi() + >>> endpoint = api.create_inference_endpoint( + ... "aws-zephyr-7b-beta-0486", + ... repository="HuggingFaceH4/zephyr-7b-beta", + ... framework="pytorch", + ... task="text-generation", + ... accelerator="gpu", + ... vendor="aws", + ... region="us-east-1", + ... type="protected", + ... instance_size="x1", + ... instance_type="nvidia-a10g", + ... env={ + ... "MAX_BATCH_PREFILL_TOKENS": "2048", + ... "MAX_INPUT_LENGTH": "1024", + ... "MAX_TOTAL_TOKENS": "1512", + ... "MODEL_ID": "/repository" + ... }, + ... custom_image={ + ... "health_route": "/health", + ... "url": "ghcr.io/huggingface/text-generation-inference:1.1.0", + ... }, + ... secrets={"MY_SECRET_KEY": "secret_value"}, + ... tags=["dev", "text-generation"], + ... ) + ``` + + ```python + # Start an Inference Endpoint running ProsusAI/finbert while scaling to zero in 15 minutes + >>> from huggingface_hub import HfApi + >>> api = HfApi() + >>> endpoint = api.create_inference_endpoint( + ... "finbert-classifier", + ... repository="ProsusAI/finbert", + ... framework="pytorch", + ... task="text-classification", + ... min_replica=0, + ... scale_to_zero_timeout=15, + ... accelerator="cpu", + ... vendor="aws", + ... region="us-east-1", + ... type="protected", + ... instance_size="x2", + ... instance_type="intel-icl", + ... ) + >>> endpoint.wait(timeout=300) + # Run inference on the endpoint + >>> endpoint.client.text_generation(...) + TextClassificationOutputElement(label='positive', score=0.8983615040779114) + ``` + + """ + namespace = namespace or self._get_namespace(token=token) + + if custom_image is not None: + image = ( + custom_image + if next(iter(custom_image)) in constants.INFERENCE_ENDPOINT_IMAGE_KEYS + else {"custom": custom_image} + ) + else: + image = {"huggingface": {}} + + payload: Dict = { + "accountId": account_id, + "compute": { + "accelerator": accelerator, + "instanceSize": instance_size, + "instanceType": instance_type, + "scaling": { + "maxReplica": max_replica, + "minReplica": min_replica, + "scaleToZeroTimeout": scale_to_zero_timeout, + }, + }, + "model": { + "framework": framework, + "repository": repository, + "revision": revision, + "task": task, + "image": image, + }, + "name": name, + "provider": { + "region": region, + "vendor": vendor, + }, + "type": type, + } + if env: + payload["model"]["env"] = env + if secrets: + payload["model"]["secrets"] = secrets + if domain is not None or path is not None: + payload["route"] = {} + if domain is not None: + payload["route"]["domain"] = domain + if path is not None: + payload["route"]["path"] = path + if cache_http_responses is not None: + payload["cacheHttpResponses"] = cache_http_responses + if tags is not None: + payload["tags"] = tags + + response = get_session().post( + f"{constants.INFERENCE_ENDPOINTS_ENDPOINT}/endpoint/{namespace}", + headers=self._build_hf_headers(token=token), + json=payload, + ) + hf_raise_for_status(response) + + return InferenceEndpoint.from_raw(response.json(), namespace=namespace, token=token) + + @experimental + @validate_hf_hub_args + def create_inference_endpoint_from_catalog( + self, + repo_id: str, + *, + name: Optional[str] = None, + token: Union[bool, str, None] = None, + namespace: Optional[str] = None, + ) -> InferenceEndpoint: + """Create a new Inference Endpoint from a model in the Hugging Face Inference Catalog. + + The goal of the Inference Catalog is to provide a curated list of models that are optimized for inference + and for which default configurations have been tested. See https://endpoints.huggingface.co/catalog for a list + of available models in the catalog. + + Args: + repo_id (`str`): + The ID of the model in the catalog to deploy as an Inference Endpoint. + name (`str`, *optional*): + The unique name for the new Inference Endpoint. If not provided, a random name will be generated. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + namespace (`str`, *optional*): + The namespace where the Inference Endpoint will be created. Defaults to the current user's namespace. + + Returns: + [`InferenceEndpoint`]: information about the new Inference Endpoint. + + + + `create_inference_endpoint_from_catalog` is experimental. Its API is subject to change in the future. Please provide feedback + if you have any suggestions or requests. + + + """ + token = token or self.token or get_token() + payload: Dict = { + "namespace": namespace or self._get_namespace(token=token), + "repoId": repo_id, + } + if name is not None: + payload["endpointName"] = name + + response = get_session().post( + f"{constants.INFERENCE_CATALOG_ENDPOINT}/deploy", + headers=self._build_hf_headers(token=token), + json=payload, + ) + hf_raise_for_status(response) + data = response.json()["endpoint"] + return InferenceEndpoint.from_raw(data, namespace=data["name"], token=token) + + @experimental + @validate_hf_hub_args + def list_inference_catalog(self, *, token: Union[bool, str, None] = None) -> List[str]: + """List models available in the Hugging Face Inference Catalog. + + The goal of the Inference Catalog is to provide a curated list of models that are optimized for inference + and for which default configurations have been tested. See https://endpoints.huggingface.co/catalog for a list + of available models in the catalog. + + Use [`create_inference_endpoint_from_catalog`] to deploy a model from the catalog. + + Args: + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + + Returns: + List[`str`]: A list of model IDs available in the catalog. + + + `list_inference_catalog` is experimental. Its API is subject to change in the future. Please provide feedback + if you have any suggestions or requests. + + + """ + response = get_session().get( + f"{constants.INFERENCE_CATALOG_ENDPOINT}/repo-list", + headers=self._build_hf_headers(token=token), + ) + hf_raise_for_status(response) + return response.json()["models"] + + def get_inference_endpoint( + self, name: str, *, namespace: Optional[str] = None, token: Union[bool, str, None] = None + ) -> InferenceEndpoint: + """Get information about an Inference Endpoint. + + Args: + name (`str`): + The name of the Inference Endpoint to retrieve information about. + namespace (`str`, *optional*): + The namespace in which the Inference Endpoint is located. Defaults to the current user. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`InferenceEndpoint`]: information about the requested Inference Endpoint. + + Example: + ```python + >>> from huggingface_hub import HfApi + >>> api = HfApi() + >>> endpoint = api.get_inference_endpoint("my-text-to-image") + >>> endpoint + InferenceEndpoint(name='my-text-to-image', ...) + + # Get status + >>> endpoint.status + 'running' + >>> endpoint.url + 'https://my-text-to-image.region.vendor.endpoints.huggingface.cloud' + + # Run inference + >>> endpoint.client.text_to_image(...) + ``` + """ + namespace = namespace or self._get_namespace(token=token) + + response = get_session().get( + f"{constants.INFERENCE_ENDPOINTS_ENDPOINT}/endpoint/{namespace}/{name}", + headers=self._build_hf_headers(token=token), + ) + hf_raise_for_status(response) + + return InferenceEndpoint.from_raw(response.json(), namespace=namespace, token=token) + + def update_inference_endpoint( + self, + name: str, + *, + # Compute update + accelerator: Optional[str] = None, + instance_size: Optional[str] = None, + instance_type: Optional[str] = None, + min_replica: Optional[int] = None, + max_replica: Optional[int] = None, + scale_to_zero_timeout: Optional[int] = None, + # Model update + repository: Optional[str] = None, + framework: Optional[str] = None, + revision: Optional[str] = None, + task: Optional[str] = None, + custom_image: Optional[Dict] = None, + env: Optional[Dict[str, str]] = None, + secrets: Optional[Dict[str, str]] = None, + # Route update + domain: Optional[str] = None, + path: Optional[str] = None, + # Other + cache_http_responses: Optional[bool] = None, + tags: Optional[List[str]] = None, + namespace: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> InferenceEndpoint: + """Update an Inference Endpoint. + + This method allows the update of either the compute configuration, the deployed model, the route, or any combination. + All arguments are optional but at least one must be provided. + + For convenience, you can also update an Inference Endpoint using [`InferenceEndpoint.update`]. + + Args: + name (`str`): + The name of the Inference Endpoint to update. + + accelerator (`str`, *optional*): + The hardware accelerator to be used for inference (e.g. `"cpu"`). + instance_size (`str`, *optional*): + The size or type of the instance to be used for hosting the model (e.g. `"x4"`). + instance_type (`str`, *optional*): + The cloud instance type where the Inference Endpoint will be deployed (e.g. `"intel-icl"`). + min_replica (`int`, *optional*): + The minimum number of replicas (instances) to keep running for the Inference Endpoint. + max_replica (`int`, *optional*): + The maximum number of replicas (instances) to scale to for the Inference Endpoint. + scale_to_zero_timeout (`int`, *optional*): + The duration in minutes before an inactive endpoint is scaled to zero. + + repository (`str`, *optional*): + The name of the model repository associated with the Inference Endpoint (e.g. `"gpt2"`). + framework (`str`, *optional*): + The machine learning framework used for the model (e.g. `"custom"`). + revision (`str`, *optional*): + The specific model revision to deploy on the Inference Endpoint (e.g. `"6c0e6080953db56375760c0471a8c5f2929baf11"`). + task (`str`, *optional*): + The task on which to deploy the model (e.g. `"text-classification"`). + custom_image (`Dict`, *optional*): + A custom Docker image to use for the Inference Endpoint. This is useful if you want to deploy an + Inference Endpoint running on the `text-generation-inference` (TGI) framework (see examples). + env (`Dict[str, str]`, *optional*): + Non-secret environment variables to inject in the container environment + secrets (`Dict[str, str]`, *optional*): + Secret values to inject in the container environment. + + domain (`str`, *optional*): + The custom domain for the Inference Endpoint deployment, if setup the inference endpoint will be available at this domain (e.g. `"my-new-domain.cool-website.woof"`). + path (`str`, *optional*): + The custom path to the deployed model, should start with a `/` (e.g. `"/models/google-bert/bert-base-uncased"`). + + cache_http_responses (`bool`, *optional*): + Whether to cache HTTP responses from the Inference Endpoint. + tags (`List[str]`, *optional*): + A list of tags to associate with the Inference Endpoint. + + namespace (`str`, *optional*): + The namespace where the Inference Endpoint will be updated. Defaults to the current user's namespace. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`InferenceEndpoint`]: information about the updated Inference Endpoint. + """ + namespace = namespace or self._get_namespace(token=token) + + # Populate only the fields that are not None + payload: Dict = defaultdict(lambda: defaultdict(dict)) + if accelerator is not None: + payload["compute"]["accelerator"] = accelerator + if instance_size is not None: + payload["compute"]["instanceSize"] = instance_size + if instance_type is not None: + payload["compute"]["instanceType"] = instance_type + if max_replica is not None: + payload["compute"]["scaling"]["maxReplica"] = max_replica + if min_replica is not None: + payload["compute"]["scaling"]["minReplica"] = min_replica + if scale_to_zero_timeout is not None: + payload["compute"]["scaling"]["scaleToZeroTimeout"] = scale_to_zero_timeout + if repository is not None: + payload["model"]["repository"] = repository + if framework is not None: + payload["model"]["framework"] = framework + if revision is not None: + payload["model"]["revision"] = revision + if task is not None: + payload["model"]["task"] = task + if custom_image is not None: + payload["model"]["image"] = {"custom": custom_image} + if env is not None: + payload["model"]["env"] = env + if secrets is not None: + payload["model"]["secrets"] = secrets + if domain is not None: + payload["route"]["domain"] = domain + if path is not None: + payload["route"]["path"] = path + if cache_http_responses is not None: + payload["cacheHttpResponses"] = cache_http_responses + if tags is not None: + payload["tags"] = tags + + response = get_session().put( + f"{constants.INFERENCE_ENDPOINTS_ENDPOINT}/endpoint/{namespace}/{name}", + headers=self._build_hf_headers(token=token), + json=payload, + ) + hf_raise_for_status(response) + + return InferenceEndpoint.from_raw(response.json(), namespace=namespace, token=token) + + def delete_inference_endpoint( + self, name: str, *, namespace: Optional[str] = None, token: Union[bool, str, None] = None + ) -> None: + """Delete an Inference Endpoint. + + This operation is not reversible. If you don't want to be charged for an Inference Endpoint, it is preferable + to pause it with [`pause_inference_endpoint`] or scale it to zero with [`scale_to_zero_inference_endpoint`]. + + For convenience, you can also delete an Inference Endpoint using [`InferenceEndpoint.delete`]. + + Args: + name (`str`): + The name of the Inference Endpoint to delete. + namespace (`str`, *optional*): + The namespace in which the Inference Endpoint is located. Defaults to the current user. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + """ + namespace = namespace or self._get_namespace(token=token) + response = get_session().delete( + f"{constants.INFERENCE_ENDPOINTS_ENDPOINT}/endpoint/{namespace}/{name}", + headers=self._build_hf_headers(token=token), + ) + hf_raise_for_status(response) + + def pause_inference_endpoint( + self, name: str, *, namespace: Optional[str] = None, token: Union[bool, str, None] = None + ) -> InferenceEndpoint: + """Pause an Inference Endpoint. + + A paused Inference Endpoint will not be charged. It can be resumed at any time using [`resume_inference_endpoint`]. + This is different than scaling the Inference Endpoint to zero with [`scale_to_zero_inference_endpoint`], which + would be automatically restarted when a request is made to it. + + For convenience, you can also pause an Inference Endpoint using [`pause_inference_endpoint`]. + + Args: + name (`str`): + The name of the Inference Endpoint to pause. + namespace (`str`, *optional*): + The namespace in which the Inference Endpoint is located. Defaults to the current user. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`InferenceEndpoint`]: information about the paused Inference Endpoint. + """ + namespace = namespace or self._get_namespace(token=token) + + response = get_session().post( + f"{constants.INFERENCE_ENDPOINTS_ENDPOINT}/endpoint/{namespace}/{name}/pause", + headers=self._build_hf_headers(token=token), + ) + hf_raise_for_status(response) + + return InferenceEndpoint.from_raw(response.json(), namespace=namespace, token=token) + + def resume_inference_endpoint( + self, + name: str, + *, + namespace: Optional[str] = None, + running_ok: bool = True, + token: Union[bool, str, None] = None, + ) -> InferenceEndpoint: + """Resume an Inference Endpoint. + + For convenience, you can also resume an Inference Endpoint using [`InferenceEndpoint.resume`]. + + Args: + name (`str`): + The name of the Inference Endpoint to resume. + namespace (`str`, *optional*): + The namespace in which the Inference Endpoint is located. Defaults to the current user. + running_ok (`bool`, *optional*): + If `True`, the method will not raise an error if the Inference Endpoint is already running. Defaults to + `True`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`InferenceEndpoint`]: information about the resumed Inference Endpoint. + """ + namespace = namespace or self._get_namespace(token=token) + + response = get_session().post( + f"{constants.INFERENCE_ENDPOINTS_ENDPOINT}/endpoint/{namespace}/{name}/resume", + headers=self._build_hf_headers(token=token), + ) + try: + hf_raise_for_status(response) + except HfHubHTTPError as error: + # If already running (and it's ok), then fetch current status and return + if running_ok and error.response.status_code == 400 and "already running" in error.response.text: + return self.get_inference_endpoint(name, namespace=namespace, token=token) + # Otherwise, raise the error + raise + + return InferenceEndpoint.from_raw(response.json(), namespace=namespace, token=token) + + def scale_to_zero_inference_endpoint( + self, name: str, *, namespace: Optional[str] = None, token: Union[bool, str, None] = None + ) -> InferenceEndpoint: + """Scale Inference Endpoint to zero. + + An Inference Endpoint scaled to zero will not be charged. It will be resume on the next request to it, with a + cold start delay. This is different than pausing the Inference Endpoint with [`pause_inference_endpoint`], which + would require a manual resume with [`resume_inference_endpoint`]. + + For convenience, you can also scale an Inference Endpoint to zero using [`InferenceEndpoint.scale_to_zero`]. + + Args: + name (`str`): + The name of the Inference Endpoint to scale to zero. + namespace (`str`, *optional*): + The namespace in which the Inference Endpoint is located. Defaults to the current user. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`InferenceEndpoint`]: information about the scaled-to-zero Inference Endpoint. + """ + namespace = namespace or self._get_namespace(token=token) + + response = get_session().post( + f"{constants.INFERENCE_ENDPOINTS_ENDPOINT}/endpoint/{namespace}/{name}/scale-to-zero", + headers=self._build_hf_headers(token=token), + ) + hf_raise_for_status(response) + + return InferenceEndpoint.from_raw(response.json(), namespace=namespace, token=token) + + def _get_namespace(self, token: Union[bool, str, None] = None) -> str: + """Get the default namespace for the current user.""" + me = self.whoami(token=token) + if me["type"] == "user": + return me["name"] + else: + raise ValueError( + "Cannot determine default namespace. You must provide a 'namespace' as input or be logged in as a" + " user." + ) + + ######################## + # Collection Endpoints # + ######################## + @validate_hf_hub_args + def list_collections( + self, + *, + owner: Union[List[str], str, None] = None, + item: Union[List[str], str, None] = None, + sort: Optional[Literal["lastModified", "trending", "upvotes"]] = None, + limit: Optional[int] = None, + token: Union[bool, str, None] = None, + ) -> Iterable[Collection]: + """List collections on the Huggingface Hub, given some filters. + + + + When listing collections, the item list per collection is truncated to 4 items maximum. To retrieve all items + from a collection, you must use [`get_collection`]. + + + + Args: + owner (`List[str]` or `str`, *optional*): + Filter by owner's username. + item (`List[str]` or `str`, *optional*): + Filter collections containing a particular items. Example: `"models/teknium/OpenHermes-2.5-Mistral-7B"`, `"datasets/squad"` or `"papers/2311.12983"`. + sort (`Literal["lastModified", "trending", "upvotes"]`, *optional*): + Sort collections by last modified, trending or upvotes. + limit (`int`, *optional*): + Maximum number of collections to be returned. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + `Iterable[Collection]`: an iterable of [`Collection`] objects. + """ + # Construct the API endpoint + path = f"{self.endpoint}/api/collections" + headers = self._build_hf_headers(token=token) + params: Dict = {} + if owner is not None: + params.update({"owner": owner}) + if item is not None: + params.update({"item": item}) + if sort is not None: + params.update({"sort": sort}) + if limit is not None: + params.update({"limit": limit}) + + # Paginate over the results until limit is reached + items = paginate(path, headers=headers, params=params) + if limit is not None: + items = islice(items, limit) # Do not iterate over all pages + + # Parse as Collection and return + for position, collection_data in enumerate(items): + yield Collection(position=position, **collection_data) + + def get_collection(self, collection_slug: str, *, token: Union[bool, str, None] = None) -> Collection: + """Gets information about a Collection on the Hub. + + Args: + collection_slug (`str`): + Slug of the collection of the Hub. Example: `"TheBloke/recent-models-64f9a55bb3115b4f513ec026"`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: [`Collection`] + + Example: + + ```py + >>> from huggingface_hub import get_collection + >>> collection = get_collection("TheBloke/recent-models-64f9a55bb3115b4f513ec026") + >>> collection.title + 'Recent models' + >>> len(collection.items) + 37 + >>> collection.items[0] + CollectionItem( + item_object_id='651446103cd773a050bf64c2', + item_id='TheBloke/U-Amethyst-20B-AWQ', + item_type='model', + position=88, + note=None + ) + ``` + """ + r = get_session().get( + f"{self.endpoint}/api/collections/{collection_slug}", headers=self._build_hf_headers(token=token) + ) + hf_raise_for_status(r) + return Collection(**{**r.json(), "endpoint": self.endpoint}) + + def create_collection( + self, + title: str, + *, + namespace: Optional[str] = None, + description: Optional[str] = None, + private: bool = False, + exists_ok: bool = False, + token: Union[bool, str, None] = None, + ) -> Collection: + """Create a new Collection on the Hub. + + Args: + title (`str`): + Title of the collection to create. Example: `"Recent models"`. + namespace (`str`, *optional*): + Namespace of the collection to create (username or org). Will default to the owner name. + description (`str`, *optional*): + Description of the collection to create. + private (`bool`, *optional*): + Whether the collection should be private or not. Defaults to `False` (i.e. public collection). + exists_ok (`bool`, *optional*): + If `True`, do not raise an error if collection already exists. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: [`Collection`] + + Example: + + ```py + >>> from huggingface_hub import create_collection + >>> collection = create_collection( + ... title="ICCV 2023", + ... description="Portfolio of models, papers and demos I presented at ICCV 2023", + ... ) + >>> collection.slug + "username/iccv-2023-64f9a55bb3115b4f513ec026" + ``` + """ + if namespace is None: + namespace = self.whoami(token)["name"] + + payload = { + "title": title, + "namespace": namespace, + "private": private, + } + if description is not None: + payload["description"] = description + + r = get_session().post( + f"{self.endpoint}/api/collections", headers=self._build_hf_headers(token=token), json=payload + ) + try: + hf_raise_for_status(r) + except HTTPError as err: + if exists_ok and err.response.status_code == 409: + # Collection already exists and `exists_ok=True` + slug = r.json()["slug"] + return self.get_collection(slug, token=token) + else: + raise + return Collection(**{**r.json(), "endpoint": self.endpoint}) + + def update_collection_metadata( + self, + collection_slug: str, + *, + title: Optional[str] = None, + description: Optional[str] = None, + position: Optional[int] = None, + private: Optional[bool] = None, + theme: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> Collection: + """Update metadata of a collection on the Hub. + + All arguments are optional. Only provided metadata will be updated. + + Args: + collection_slug (`str`): + Slug of the collection to update. Example: `"TheBloke/recent-models-64f9a55bb3115b4f513ec026"`. + title (`str`): + Title of the collection to update. + description (`str`, *optional*): + Description of the collection to update. + position (`int`, *optional*): + New position of the collection in the list of collections of the user. + private (`bool`, *optional*): + Whether the collection should be private or not. + theme (`str`, *optional*): + Theme of the collection on the Hub. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: [`Collection`] + + Example: + + ```py + >>> from huggingface_hub import update_collection_metadata + >>> collection = update_collection_metadata( + ... collection_slug="username/iccv-2023-64f9a55bb3115b4f513ec026", + ... title="ICCV Oct. 2023" + ... description="Portfolio of models, datasets, papers and demos I presented at ICCV Oct. 2023", + ... private=False, + ... theme="pink", + ... ) + >>> collection.slug + "username/iccv-oct-2023-64f9a55bb3115b4f513ec026" + # ^collection slug got updated but not the trailing ID + ``` + """ + payload = { + "position": position, + "private": private, + "theme": theme, + "title": title, + "description": description, + } + r = get_session().patch( + f"{self.endpoint}/api/collections/{collection_slug}", + headers=self._build_hf_headers(token=token), + # Only send not-none values to the API + json={key: value for key, value in payload.items() if value is not None}, + ) + hf_raise_for_status(r) + return Collection(**{**r.json()["data"], "endpoint": self.endpoint}) + + def delete_collection( + self, collection_slug: str, *, missing_ok: bool = False, token: Union[bool, str, None] = None + ) -> None: + """Delete a collection on the Hub. + + Args: + collection_slug (`str`): + Slug of the collection to delete. Example: `"TheBloke/recent-models-64f9a55bb3115b4f513ec026"`. + missing_ok (`bool`, *optional*): + If `True`, do not raise an error if collection doesn't exists. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Example: + + ```py + >>> from huggingface_hub import delete_collection + >>> collection = delete_collection("username/useless-collection-64f9a55bb3115b4f513ec026", missing_ok=True) + ``` + + + + This is a non-revertible action. A deleted collection cannot be restored. + + + """ + r = get_session().delete( + f"{self.endpoint}/api/collections/{collection_slug}", headers=self._build_hf_headers(token=token) + ) + try: + hf_raise_for_status(r) + except HTTPError as err: + if missing_ok and err.response.status_code == 404: + # Collection doesn't exists and `missing_ok=True` + return + else: + raise + + def add_collection_item( + self, + collection_slug: str, + item_id: str, + item_type: CollectionItemType_T, + *, + note: Optional[str] = None, + exists_ok: bool = False, + token: Union[bool, str, None] = None, + ) -> Collection: + """Add an item to a collection on the Hub. + + Args: + collection_slug (`str`): + Slug of the collection to update. Example: `"TheBloke/recent-models-64f9a55bb3115b4f513ec026"`. + item_id (`str`): + ID of the item to add to the collection. It can be the ID of a repo on the Hub (e.g. `"facebook/bart-large-mnli"`) + or a paper id (e.g. `"2307.09288"`). + item_type (`str`): + Type of the item to add. Can be one of `"model"`, `"dataset"`, `"space"` or `"paper"`. + note (`str`, *optional*): + A note to attach to the item in the collection. The maximum size for a note is 500 characters. + exists_ok (`bool`, *optional*): + If `True`, do not raise an error if item already exists. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: [`Collection`] + + Raises: + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 403 if you only have read-only access to the repo. This can be the case if you don't have `write` + or `admin` role in the organization the repo belongs to or if you passed a `read` token. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 404 if the item you try to add to the collection does not exist on the Hub. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 409 if the item you try to add to the collection is already in the collection (and exists_ok=False) + + Example: + + ```py + >>> from huggingface_hub import add_collection_item + >>> collection = add_collection_item( + ... collection_slug="davanstrien/climate-64f99dc2a5067f6b65531bab", + ... item_id="pierre-loic/climate-news-articles", + ... item_type="dataset" + ... ) + >>> collection.items[-1].item_id + "pierre-loic/climate-news-articles" + # ^item got added to the collection on last position + + # Add item with a note + >>> add_collection_item( + ... collection_slug="davanstrien/climate-64f99dc2a5067f6b65531bab", + ... item_id="datasets/climate_fever", + ... item_type="dataset" + ... note="This dataset adopts the FEVER methodology that consists of 1,535 real-world claims regarding climate-change collected on the internet." + ... ) + (...) + ``` + """ + payload: Dict[str, Any] = {"item": {"id": item_id, "type": item_type}} + if note is not None: + payload["note"] = note + r = get_session().post( + f"{self.endpoint}/api/collections/{collection_slug}/items", + headers=self._build_hf_headers(token=token), + json=payload, + ) + try: + hf_raise_for_status(r) + except HTTPError as err: + if exists_ok and err.response.status_code == 409: + # Item already exists and `exists_ok=True` + return self.get_collection(collection_slug, token=token) + else: + raise + return Collection(**{**r.json(), "endpoint": self.endpoint}) + + def update_collection_item( + self, + collection_slug: str, + item_object_id: str, + *, + note: Optional[str] = None, + position: Optional[int] = None, + token: Union[bool, str, None] = None, + ) -> None: + """Update an item in a collection. + + Args: + collection_slug (`str`): + Slug of the collection to update. Example: `"TheBloke/recent-models-64f9a55bb3115b4f513ec026"`. + item_object_id (`str`): + ID of the item in the collection. This is not the id of the item on the Hub (repo_id or paper id). + It must be retrieved from a [`CollectionItem`] object. Example: `collection.items[0].item_object_id`. + note (`str`, *optional*): + A note to attach to the item in the collection. The maximum size for a note is 500 characters. + position (`int`, *optional*): + New position of the item in the collection. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Example: + + ```py + >>> from huggingface_hub import get_collection, update_collection_item + + # Get collection first + >>> collection = get_collection("TheBloke/recent-models-64f9a55bb3115b4f513ec026") + + # Update item based on its ID (add note + update position) + >>> update_collection_item( + ... collection_slug="TheBloke/recent-models-64f9a55bb3115b4f513ec026", + ... item_object_id=collection.items[-1].item_object_id, + ... note="Newly updated model!" + ... position=0, + ... ) + ``` + """ + payload = {"position": position, "note": note} + r = get_session().patch( + f"{self.endpoint}/api/collections/{collection_slug}/items/{item_object_id}", + headers=self._build_hf_headers(token=token), + # Only send not-none values to the API + json={key: value for key, value in payload.items() if value is not None}, + ) + hf_raise_for_status(r) + + def delete_collection_item( + self, + collection_slug: str, + item_object_id: str, + *, + missing_ok: bool = False, + token: Union[bool, str, None] = None, + ) -> None: + """Delete an item from a collection. + + Args: + collection_slug (`str`): + Slug of the collection to update. Example: `"TheBloke/recent-models-64f9a55bb3115b4f513ec026"`. + item_object_id (`str`): + ID of the item in the collection. This is not the id of the item on the Hub (repo_id or paper id). + It must be retrieved from a [`CollectionItem`] object. Example: `collection.items[0].item_object_id`. + missing_ok (`bool`, *optional*): + If `True`, do not raise an error if item doesn't exists. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Example: + + ```py + >>> from huggingface_hub import get_collection, delete_collection_item + + # Get collection first + >>> collection = get_collection("TheBloke/recent-models-64f9a55bb3115b4f513ec026") + + # Delete item based on its ID + >>> delete_collection_item( + ... collection_slug="TheBloke/recent-models-64f9a55bb3115b4f513ec026", + ... item_object_id=collection.items[-1].item_object_id, + ... ) + ``` + """ + r = get_session().delete( + f"{self.endpoint}/api/collections/{collection_slug}/items/{item_object_id}", + headers=self._build_hf_headers(token=token), + ) + try: + hf_raise_for_status(r) + except HTTPError as err: + if missing_ok and err.response.status_code == 404: + # Item already deleted and `missing_ok=True` + return + else: + raise + + ########################## + # Manage access requests # + ########################## + + @validate_hf_hub_args + def list_pending_access_requests( + self, repo_id: str, *, repo_type: Optional[str] = None, token: Union[bool, str, None] = None + ) -> List[AccessRequest]: + """ + Get pending access requests for a given gated repo. + + A pending request means the user has requested access to the repo but the request has not been processed yet. + If the approval mode is automatic, this list should be empty. Pending requests can be accepted or rejected + using [`accept_access_request`] and [`reject_access_request`]. + + For more info about gated repos, see https://huggingface.co/docs/hub/models-gated. + + Args: + repo_id (`str`): + The id of the repo to get access requests for. + repo_type (`str`, *optional*): + The type of the repo to get access requests for. Must be one of `model`, `dataset` or `space`. + Defaults to `model`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + `List[AccessRequest]`: A list of [`AccessRequest`] objects. Each time contains a `username`, `email`, + `status` and `timestamp` attribute. If the gated repo has a custom form, the `fields` attribute will + be populated with user's answers. + + Raises: + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 400 if the repo is not gated. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 403 if you only have read-only access to the repo. This can be the case if you don't have `write` + or `admin` role in the organization the repo belongs to or if you passed a `read` token. + + Example: + ```py + >>> from huggingface_hub import list_pending_access_requests, accept_access_request + + # List pending requests + >>> requests = list_pending_access_requests("meta-llama/Llama-2-7b") + >>> len(requests) + 411 + >>> requests[0] + [ + AccessRequest( + username='clem', + fullname='Clem 🤗', + email='***', + timestamp=datetime.datetime(2023, 11, 23, 18, 4, 53, 828000, tzinfo=datetime.timezone.utc), + status='pending', + fields=None, + ), + ... + ] + + # Accept Clem's request + >>> accept_access_request("meta-llama/Llama-2-7b", "clem") + ``` + """ + return self._list_access_requests(repo_id, "pending", repo_type=repo_type, token=token) + + @validate_hf_hub_args + def list_accepted_access_requests( + self, repo_id: str, *, repo_type: Optional[str] = None, token: Union[bool, str, None] = None + ) -> List[AccessRequest]: + """ + Get accepted access requests for a given gated repo. + + An accepted request means the user has requested access to the repo and the request has been accepted. The user + can download any file of the repo. If the approval mode is automatic, this list should contains by default all + requests. Accepted requests can be cancelled or rejected at any time using [`cancel_access_request`] and + [`reject_access_request`]. A cancelled request will go back to the pending list while a rejected request will + go to the rejected list. In both cases, the user will lose access to the repo. + + For more info about gated repos, see https://huggingface.co/docs/hub/models-gated. + + Args: + repo_id (`str`): + The id of the repo to get access requests for. + repo_type (`str`, *optional*): + The type of the repo to get access requests for. Must be one of `model`, `dataset` or `space`. + Defaults to `model`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + `List[AccessRequest]`: A list of [`AccessRequest`] objects. Each time contains a `username`, `email`, + `status` and `timestamp` attribute. If the gated repo has a custom form, the `fields` attribute will + be populated with user's answers. + + Raises: + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 400 if the repo is not gated. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 403 if you only have read-only access to the repo. This can be the case if you don't have `write` + or `admin` role in the organization the repo belongs to or if you passed a `read` token. + + Example: + ```py + >>> from huggingface_hub import list_accepted_access_requests + + >>> requests = list_accepted_access_requests("meta-llama/Llama-2-7b") + >>> len(requests) + 411 + >>> requests[0] + [ + AccessRequest( + username='clem', + fullname='Clem 🤗', + email='***', + timestamp=datetime.datetime(2023, 11, 23, 18, 4, 53, 828000, tzinfo=datetime.timezone.utc), + status='accepted', + fields=None, + ), + ... + ] + ``` + """ + return self._list_access_requests(repo_id, "accepted", repo_type=repo_type, token=token) + + @validate_hf_hub_args + def list_rejected_access_requests( + self, repo_id: str, *, repo_type: Optional[str] = None, token: Union[bool, str, None] = None + ) -> List[AccessRequest]: + """ + Get rejected access requests for a given gated repo. + + A rejected request means the user has requested access to the repo and the request has been explicitly rejected + by a repo owner (either you or another user from your organization). The user cannot download any file of the + repo. Rejected requests can be accepted or cancelled at any time using [`accept_access_request`] and + [`cancel_access_request`]. A cancelled request will go back to the pending list while an accepted request will + go to the accepted list. + + For more info about gated repos, see https://huggingface.co/docs/hub/models-gated. + + Args: + repo_id (`str`): + The id of the repo to get access requests for. + repo_type (`str`, *optional*): + The type of the repo to get access requests for. Must be one of `model`, `dataset` or `space`. + Defaults to `model`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + `List[AccessRequest]`: A list of [`AccessRequest`] objects. Each time contains a `username`, `email`, + `status` and `timestamp` attribute. If the gated repo has a custom form, the `fields` attribute will + be populated with user's answers. + + Raises: + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 400 if the repo is not gated. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 403 if you only have read-only access to the repo. This can be the case if you don't have `write` + or `admin` role in the organization the repo belongs to or if you passed a `read` token. + + Example: + ```py + >>> from huggingface_hub import list_rejected_access_requests + + >>> requests = list_rejected_access_requests("meta-llama/Llama-2-7b") + >>> len(requests) + 411 + >>> requests[0] + [ + AccessRequest( + username='clem', + fullname='Clem 🤗', + email='***', + timestamp=datetime.datetime(2023, 11, 23, 18, 4, 53, 828000, tzinfo=datetime.timezone.utc), + status='rejected', + fields=None, + ), + ... + ] + ``` + """ + return self._list_access_requests(repo_id, "rejected", repo_type=repo_type, token=token) + + def _list_access_requests( + self, + repo_id: str, + status: Literal["accepted", "rejected", "pending"], + repo_type: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> List[AccessRequest]: + if repo_type not in constants.REPO_TYPES: + raise ValueError(f"Invalid repo type, must be one of {constants.REPO_TYPES}") + if repo_type is None: + repo_type = constants.REPO_TYPE_MODEL + + response = get_session().get( + f"{constants.ENDPOINT}/api/{repo_type}s/{repo_id}/user-access-request/{status}", + headers=self._build_hf_headers(token=token), + ) + hf_raise_for_status(response) + return [ + AccessRequest( + username=request["user"]["user"], + fullname=request["user"]["fullname"], + email=request["user"].get("email"), + status=request["status"], + timestamp=parse_datetime(request["timestamp"]), + fields=request.get("fields"), # only if custom fields in form + ) + for request in response.json() + ] + + @validate_hf_hub_args + def cancel_access_request( + self, repo_id: str, user: str, *, repo_type: Optional[str] = None, token: Union[bool, str, None] = None + ) -> None: + """ + Cancel an access request from a user for a given gated repo. + + A cancelled request will go back to the pending list and the user will lose access to the repo. + + For more info about gated repos, see https://huggingface.co/docs/hub/models-gated. + + Args: + repo_id (`str`): + The id of the repo to cancel access request for. + user (`str`): + The username of the user which access request should be cancelled. + repo_type (`str`, *optional*): + The type of the repo to cancel access request for. Must be one of `model`, `dataset` or `space`. + Defaults to `model`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Raises: + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 400 if the repo is not gated. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 403 if you only have read-only access to the repo. This can be the case if you don't have `write` + or `admin` role in the organization the repo belongs to or if you passed a `read` token. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 404 if the user does not exist on the Hub. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 404 if the user access request cannot be found. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 404 if the user access request is already in the pending list. + """ + self._handle_access_request(repo_id, user, "pending", repo_type=repo_type, token=token) + + @validate_hf_hub_args + def accept_access_request( + self, repo_id: str, user: str, *, repo_type: Optional[str] = None, token: Union[bool, str, None] = None + ) -> None: + """ + Accept an access request from a user for a given gated repo. + + Once the request is accepted, the user will be able to download any file of the repo and access the community + tab. If the approval mode is automatic, you don't have to accept requests manually. An accepted request can be + cancelled or rejected at any time using [`cancel_access_request`] and [`reject_access_request`]. + + For more info about gated repos, see https://huggingface.co/docs/hub/models-gated. + + Args: + repo_id (`str`): + The id of the repo to accept access request for. + user (`str`): + The username of the user which access request should be accepted. + repo_type (`str`, *optional*): + The type of the repo to accept access request for. Must be one of `model`, `dataset` or `space`. + Defaults to `model`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Raises: + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 400 if the repo is not gated. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 403 if you only have read-only access to the repo. This can be the case if you don't have `write` + or `admin` role in the organization the repo belongs to or if you passed a `read` token. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 404 if the user does not exist on the Hub. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 404 if the user access request cannot be found. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 404 if the user access request is already in the accepted list. + """ + self._handle_access_request(repo_id, user, "accepted", repo_type=repo_type, token=token) + + @validate_hf_hub_args + def reject_access_request( + self, + repo_id: str, + user: str, + *, + repo_type: Optional[str] = None, + rejection_reason: Optional[str], + token: Union[bool, str, None] = None, + ) -> None: + """ + Reject an access request from a user for a given gated repo. + + A rejected request will go to the rejected list. The user cannot download any file of the repo. Rejected + requests can be accepted or cancelled at any time using [`accept_access_request`] and [`cancel_access_request`]. + A cancelled request will go back to the pending list while an accepted request will go to the accepted list. + + For more info about gated repos, see https://huggingface.co/docs/hub/models-gated. + + Args: + repo_id (`str`): + The id of the repo to reject access request for. + user (`str`): + The username of the user which access request should be rejected. + repo_type (`str`, *optional*): + The type of the repo to reject access request for. Must be one of `model`, `dataset` or `space`. + Defaults to `model`. + rejection_reason (`str`, *optional*): + Optional rejection reason that will be visible to the user (max 200 characters). + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Raises: + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 400 if the repo is not gated. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 403 if you only have read-only access to the repo. This can be the case if you don't have `write` + or `admin` role in the organization the repo belongs to or if you passed a `read` token. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 404 if the user does not exist on the Hub. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 404 if the user access request cannot be found. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 404 if the user access request is already in the rejected list. + """ + self._handle_access_request( + repo_id, user, "rejected", repo_type=repo_type, rejection_reason=rejection_reason, token=token + ) + + @validate_hf_hub_args + def _handle_access_request( + self, + repo_id: str, + user: str, + status: Literal["accepted", "rejected", "pending"], + repo_type: Optional[str] = None, + rejection_reason: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> None: + if repo_type not in constants.REPO_TYPES: + raise ValueError(f"Invalid repo type, must be one of {constants.REPO_TYPES}") + if repo_type is None: + repo_type = constants.REPO_TYPE_MODEL + + payload = {"user": user, "status": status} + + if rejection_reason is not None: + if status != "rejected": + raise ValueError("`rejection_reason` can only be passed when rejecting an access request.") + payload["rejectionReason"] = rejection_reason + + response = get_session().post( + f"{constants.ENDPOINT}/api/{repo_type}s/{repo_id}/user-access-request/handle", + headers=self._build_hf_headers(token=token), + json=payload, + ) + hf_raise_for_status(response) + + @validate_hf_hub_args + def grant_access( + self, repo_id: str, user: str, *, repo_type: Optional[str] = None, token: Union[bool, str, None] = None + ) -> None: + """ + Grant access to a user for a given gated repo. + + Granting access don't require for the user to send an access request by themselves. The user is automatically + added to the accepted list meaning they can download the files You can revoke the granted access at any time + using [`cancel_access_request`] or [`reject_access_request`]. + + For more info about gated repos, see https://huggingface.co/docs/hub/models-gated. + + Args: + repo_id (`str`): + The id of the repo to grant access to. + user (`str`): + The username of the user to grant access. + repo_type (`str`, *optional*): + The type of the repo to grant access to. Must be one of `model`, `dataset` or `space`. + Defaults to `model`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Raises: + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 400 if the repo is not gated. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 400 if the user already has access to the repo. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 403 if you only have read-only access to the repo. This can be the case if you don't have `write` + or `admin` role in the organization the repo belongs to or if you passed a `read` token. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 404 if the user does not exist on the Hub. + """ + if repo_type not in constants.REPO_TYPES: + raise ValueError(f"Invalid repo type, must be one of {constants.REPO_TYPES}") + if repo_type is None: + repo_type = constants.REPO_TYPE_MODEL + + response = get_session().post( + f"{constants.ENDPOINT}/api/{repo_type}s/{repo_id}/user-access-request/grant", + headers=self._build_hf_headers(token=token), + json={"user": user}, + ) + hf_raise_for_status(response) + return response.json() + + ################### + # Manage webhooks # + ################### + + @validate_hf_hub_args + def get_webhook(self, webhook_id: str, *, token: Union[bool, str, None] = None) -> WebhookInfo: + """Get a webhook by its id. + + Args: + webhook_id (`str`): + The unique identifier of the webhook to get. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved token, which is the recommended + method for authentication (see https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`WebhookInfo`]: + Info about the webhook. + + Example: + ```python + >>> from huggingface_hub import get_webhook + >>> webhook = get_webhook("654bbbc16f2ec14d77f109cc") + >>> print(webhook) + WebhookInfo( + id="654bbbc16f2ec14d77f109cc", + watched=[WebhookWatchedItem(type="user", name="julien-c"), WebhookWatchedItem(type="org", name="HuggingFaceH4")], + url="https://webhook.site/a2176e82-5720-43ee-9e06-f91cb4c91548", + secret="my-secret", + domains=["repo", "discussion"], + disabled=False, + ) + ``` + """ + response = get_session().get( + f"{constants.ENDPOINT}/api/settings/webhooks/{webhook_id}", + headers=self._build_hf_headers(token=token), + ) + hf_raise_for_status(response) + webhook_data = response.json()["webhook"] + + watched_items = [WebhookWatchedItem(type=item["type"], name=item["name"]) for item in webhook_data["watched"]] + + webhook = WebhookInfo( + id=webhook_data["id"], + url=webhook_data["url"], + watched=watched_items, + domains=webhook_data["domains"], + secret=webhook_data.get("secret"), + disabled=webhook_data["disabled"], + ) + + return webhook + + @validate_hf_hub_args + def list_webhooks(self, *, token: Union[bool, str, None] = None) -> List[WebhookInfo]: + """List all configured webhooks. + + Args: + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved token, which is the recommended + method for authentication (see https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + `List[WebhookInfo]`: + List of webhook info objects. + + Example: + ```python + >>> from huggingface_hub import list_webhooks + >>> webhooks = list_webhooks() + >>> len(webhooks) + 2 + >>> webhooks[0] + WebhookInfo( + id="654bbbc16f2ec14d77f109cc", + watched=[WebhookWatchedItem(type="user", name="julien-c"), WebhookWatchedItem(type="org", name="HuggingFaceH4")], + url="https://webhook.site/a2176e82-5720-43ee-9e06-f91cb4c91548", + secret="my-secret", + domains=["repo", "discussion"], + disabled=False, + ) + ``` + """ + response = get_session().get( + f"{constants.ENDPOINT}/api/settings/webhooks", + headers=self._build_hf_headers(token=token), + ) + hf_raise_for_status(response) + webhooks_data = response.json() + + return [ + WebhookInfo( + id=webhook["id"], + url=webhook["url"], + watched=[WebhookWatchedItem(type=item["type"], name=item["name"]) for item in webhook["watched"]], + domains=webhook["domains"], + secret=webhook.get("secret"), + disabled=webhook["disabled"], + ) + for webhook in webhooks_data + ] + + @validate_hf_hub_args + def create_webhook( + self, + *, + url: str, + watched: List[Union[Dict, WebhookWatchedItem]], + domains: Optional[List[constants.WEBHOOK_DOMAIN_T]] = None, + secret: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> WebhookInfo: + """Create a new webhook. + + Args: + url (`str`): + URL to send the payload to. + watched (`List[WebhookWatchedItem]`): + List of [`WebhookWatchedItem`] to be watched by the webhook. It can be users, orgs, models, datasets or spaces. + Watched items can also be provided as plain dictionaries. + domains (`List[Literal["repo", "discussion"]]`, optional): + List of domains to watch. It can be "repo", "discussion" or both. + secret (`str`, optional): + A secret to sign the payload with. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved token, which is the recommended + method for authentication (see https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`WebhookInfo`]: + Info about the newly created webhook. + + Example: + ```python + >>> from huggingface_hub import create_webhook + >>> payload = create_webhook( + ... watched=[{"type": "user", "name": "julien-c"}, {"type": "org", "name": "HuggingFaceH4"}], + ... url="https://webhook.site/a2176e82-5720-43ee-9e06-f91cb4c91548", + ... domains=["repo", "discussion"], + ... secret="my-secret", + ... ) + >>> print(payload) + WebhookInfo( + id="654bbbc16f2ec14d77f109cc", + url="https://webhook.site/a2176e82-5720-43ee-9e06-f91cb4c91548", + watched=[WebhookWatchedItem(type="user", name="julien-c"), WebhookWatchedItem(type="org", name="HuggingFaceH4")], + domains=["repo", "discussion"], + secret="my-secret", + disabled=False, + ) + ``` + """ + watched_dicts = [asdict(item) if isinstance(item, WebhookWatchedItem) else item for item in watched] + + response = get_session().post( + f"{constants.ENDPOINT}/api/settings/webhooks", + json={"watched": watched_dicts, "url": url, "domains": domains, "secret": secret}, + headers=self._build_hf_headers(token=token), + ) + hf_raise_for_status(response) + webhook_data = response.json()["webhook"] + watched_items = [WebhookWatchedItem(type=item["type"], name=item["name"]) for item in webhook_data["watched"]] + + webhook = WebhookInfo( + id=webhook_data["id"], + url=webhook_data["url"], + watched=watched_items, + domains=webhook_data["domains"], + secret=webhook_data.get("secret"), + disabled=webhook_data["disabled"], + ) + + return webhook + + @validate_hf_hub_args + def update_webhook( + self, + webhook_id: str, + *, + url: Optional[str] = None, + watched: Optional[List[Union[Dict, WebhookWatchedItem]]] = None, + domains: Optional[List[constants.WEBHOOK_DOMAIN_T]] = None, + secret: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> WebhookInfo: + """Update an existing webhook. + + Args: + webhook_id (`str`): + The unique identifier of the webhook to be updated. + url (`str`, optional): + The URL to which the payload will be sent. + watched (`List[WebhookWatchedItem]`, optional): + List of items to watch. It can be users, orgs, models, datasets, or spaces. + Refer to [`WebhookWatchedItem`] for more details. Watched items can also be provided as plain dictionaries. + domains (`List[Literal["repo", "discussion"]]`, optional): + The domains to watch. This can include "repo", "discussion", or both. + secret (`str`, optional): + A secret to sign the payload with, providing an additional layer of security. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved token, which is the recommended + method for authentication (see https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`WebhookInfo`]: + Info about the updated webhook. + + Example: + ```python + >>> from huggingface_hub import update_webhook + >>> updated_payload = update_webhook( + ... webhook_id="654bbbc16f2ec14d77f109cc", + ... url="https://new.webhook.site/a2176e82-5720-43ee-9e06-f91cb4c91548", + ... watched=[{"type": "user", "name": "julien-c"}, {"type": "org", "name": "HuggingFaceH4"}], + ... domains=["repo"], + ... secret="my-secret", + ... ) + >>> print(updated_payload) + WebhookInfo( + id="654bbbc16f2ec14d77f109cc", + url="https://new.webhook.site/a2176e82-5720-43ee-9e06-f91cb4c91548", + watched=[WebhookWatchedItem(type="user", name="julien-c"), WebhookWatchedItem(type="org", name="HuggingFaceH4")], + domains=["repo"], + secret="my-secret", + disabled=False, + ``` + """ + if watched is None: + watched = [] + watched_dicts = [asdict(item) if isinstance(item, WebhookWatchedItem) else item for item in watched] + + response = get_session().post( + f"{constants.ENDPOINT}/api/settings/webhooks/{webhook_id}", + json={"watched": watched_dicts, "url": url, "domains": domains, "secret": secret}, + headers=self._build_hf_headers(token=token), + ) + hf_raise_for_status(response) + webhook_data = response.json()["webhook"] + + watched_items = [WebhookWatchedItem(type=item["type"], name=item["name"]) for item in webhook_data["watched"]] + + webhook = WebhookInfo( + id=webhook_data["id"], + url=webhook_data["url"], + watched=watched_items, + domains=webhook_data["domains"], + secret=webhook_data.get("secret"), + disabled=webhook_data["disabled"], + ) + + return webhook + + @validate_hf_hub_args + def enable_webhook(self, webhook_id: str, *, token: Union[bool, str, None] = None) -> WebhookInfo: + """Enable a webhook (makes it "active"). + + Args: + webhook_id (`str`): + The unique identifier of the webhook to enable. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved token, which is the recommended + method for authentication (see https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`WebhookInfo`]: + Info about the enabled webhook. + + Example: + ```python + >>> from huggingface_hub import enable_webhook + >>> enabled_webhook = enable_webhook("654bbbc16f2ec14d77f109cc") + >>> enabled_webhook + WebhookInfo( + id="654bbbc16f2ec14d77f109cc", + url="https://webhook.site/a2176e82-5720-43ee-9e06-f91cb4c91548", + watched=[WebhookWatchedItem(type="user", name="julien-c"), WebhookWatchedItem(type="org", name="HuggingFaceH4")], + domains=["repo", "discussion"], + secret="my-secret", + disabled=False, + ) + ``` + """ + response = get_session().post( + f"{constants.ENDPOINT}/api/settings/webhooks/{webhook_id}/enable", + headers=self._build_hf_headers(token=token), + ) + hf_raise_for_status(response) + webhook_data = response.json()["webhook"] + + watched_items = [WebhookWatchedItem(type=item["type"], name=item["name"]) for item in webhook_data["watched"]] + + webhook = WebhookInfo( + id=webhook_data["id"], + url=webhook_data["url"], + watched=watched_items, + domains=webhook_data["domains"], + secret=webhook_data.get("secret"), + disabled=webhook_data["disabled"], + ) + + return webhook + + @validate_hf_hub_args + def disable_webhook(self, webhook_id: str, *, token: Union[bool, str, None] = None) -> WebhookInfo: + """Disable a webhook (makes it "disabled"). + + Args: + webhook_id (`str`): + The unique identifier of the webhook to disable. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved token, which is the recommended + method for authentication (see https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + [`WebhookInfo`]: + Info about the disabled webhook. + + Example: + ```python + >>> from huggingface_hub import disable_webhook + >>> disabled_webhook = disable_webhook("654bbbc16f2ec14d77f109cc") + >>> disabled_webhook + WebhookInfo( + id="654bbbc16f2ec14d77f109cc", + url="https://webhook.site/a2176e82-5720-43ee-9e06-f91cb4c91548", + watched=[WebhookWatchedItem(type="user", name="julien-c"), WebhookWatchedItem(type="org", name="HuggingFaceH4")], + domains=["repo", "discussion"], + secret="my-secret", + disabled=True, + ) + ``` + """ + response = get_session().post( + f"{constants.ENDPOINT}/api/settings/webhooks/{webhook_id}/disable", + headers=self._build_hf_headers(token=token), + ) + hf_raise_for_status(response) + webhook_data = response.json()["webhook"] + + watched_items = [WebhookWatchedItem(type=item["type"], name=item["name"]) for item in webhook_data["watched"]] + + webhook = WebhookInfo( + id=webhook_data["id"], + url=webhook_data["url"], + watched=watched_items, + domains=webhook_data["domains"], + secret=webhook_data.get("secret"), + disabled=webhook_data["disabled"], + ) + + return webhook + + @validate_hf_hub_args + def delete_webhook(self, webhook_id: str, *, token: Union[bool, str, None] = None) -> None: + """Delete a webhook. + + Args: + webhook_id (`str`): + The unique identifier of the webhook to delete. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved token, which is the recommended + method for authentication (see https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + `None` + + Example: + ```python + >>> from huggingface_hub import delete_webhook + >>> delete_webhook("654bbbc16f2ec14d77f109cc") + ``` + """ + response = get_session().delete( + f"{constants.ENDPOINT}/api/settings/webhooks/{webhook_id}", + headers=self._build_hf_headers(token=token), + ) + hf_raise_for_status(response) + + ############# + # Internals # + ############# + + def _build_hf_headers( + self, + token: Union[bool, str, None] = None, + library_name: Optional[str] = None, + library_version: Optional[str] = None, + user_agent: Union[Dict, str, None] = None, + ) -> Dict[str, str]: + """ + Alias for [`build_hf_headers`] that uses the token from [`HfApi`] client + when `token` is not provided. + """ + if token is None: + # Cannot do `token = token or self.token` as token can be `False`. + token = self.token + return build_hf_headers( + token=token, + library_name=library_name or self.library_name, + library_version=library_version or self.library_version, + user_agent=user_agent or self.user_agent, + headers=self.headers, + ) + + def _prepare_folder_deletions( + self, + repo_id: str, + repo_type: Optional[str], + revision: Optional[str], + path_in_repo: str, + delete_patterns: Optional[Union[List[str], str]], + token: Union[bool, str, None] = None, + ) -> List[CommitOperationDelete]: + """Generate the list of Delete operations for a commit to delete files from a repo. + + List remote files and match them against the `delete_patterns` constraints. Returns a list of [`CommitOperationDelete`] + with the matching items. + + Note: `.gitattributes` file is essential to make a repo work properly on the Hub. This file will always be + kept even if it matches the `delete_patterns` constraints. + """ + if delete_patterns is None: + # If no delete patterns, no need to list and filter remote files + return [] + + # List remote files + filenames = self.list_repo_files(repo_id=repo_id, revision=revision, repo_type=repo_type, token=token) + + # Compute relative path in repo + if path_in_repo and path_in_repo not in (".", "./"): + path_in_repo = path_in_repo.strip("/") + "/" # harmonize + relpath_to_abspath = { + file[len(path_in_repo) :]: file for file in filenames if file.startswith(path_in_repo) + } + else: + relpath_to_abspath = {file: file for file in filenames} + + # Apply filter on relative paths and return + return [ + CommitOperationDelete(path_in_repo=relpath_to_abspath[relpath], is_folder=False) + for relpath in filter_repo_objects(relpath_to_abspath.keys(), allow_patterns=delete_patterns) + if relpath_to_abspath[relpath] != ".gitattributes" + ] + + def _prepare_upload_folder_additions( + self, + folder_path: Union[str, Path], + path_in_repo: str, + allow_patterns: Optional[Union[List[str], str]] = None, + ignore_patterns: Optional[Union[List[str], str]] = None, + repo_type: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> List[CommitOperationAdd]: + """Generate the list of Add operations for a commit to upload a folder. + + Files not matching the `allow_patterns` (allowlist) and `ignore_patterns` (denylist) + constraints are discarded. + """ + + folder_path = Path(folder_path).expanduser().resolve() + if not folder_path.is_dir(): + raise ValueError(f"Provided path: '{folder_path}' is not a directory") + + # List files from folder + relpath_to_abspath = { + path.relative_to(folder_path).as_posix(): path + for path in sorted(folder_path.glob("**/*")) # sorted to be deterministic + if path.is_file() + } + + # Filter files + # Patterns are applied on the path relative to `folder_path`. `path_in_repo` is prefixed after the filtering. + filtered_repo_objects = list( + filter_repo_objects( + relpath_to_abspath.keys(), allow_patterns=allow_patterns, ignore_patterns=ignore_patterns + ) + ) + + prefix = f"{path_in_repo.strip('/')}/" if path_in_repo else "" + + # If updating a README.md file, make sure the metadata format is valid + # It's better to fail early than to fail after all the files have been hashed. + if "README.md" in filtered_repo_objects: + self._validate_yaml( + content=relpath_to_abspath["README.md"].read_text(encoding="utf8"), + repo_type=repo_type, + token=token, + ) + if len(filtered_repo_objects) > 30: + log = logger.warning if len(filtered_repo_objects) > 200 else logger.info + log( + "It seems you are trying to upload a large folder at once. This might take some time and then fail if " + "the folder is too large. For such cases, it is recommended to upload in smaller batches or to use " + "`HfApi().upload_large_folder(...)`/`hf upload-large-folder` instead. For more details, " + "check out https://huggingface.co/docs/huggingface_hub/main/en/guides/upload#upload-a-large-folder." + ) + + logger.info(f"Start hashing {len(filtered_repo_objects)} files.") + operations = [ + CommitOperationAdd( + path_or_fileobj=relpath_to_abspath[relpath], # absolute path on disk + path_in_repo=prefix + relpath, # "absolute" path in repo + ) + for relpath in filtered_repo_objects + ] + logger.info(f"Finished hashing {len(filtered_repo_objects)} files.") + return operations + + def _validate_yaml(self, content: str, *, repo_type: Optional[str] = None, token: Union[bool, str, None] = None): + """ + Validate YAML from `README.md`, used before file hashing and upload. + + Args: + content (`str`): + Content of `README.md` to validate. + repo_type (`str`, *optional*): + The type of the repo to grant access to. Must be one of `model`, `dataset` or `space`. + Defaults to `model`. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Raises: + - [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + if YAML is invalid + """ + repo_type = repo_type if repo_type is not None else constants.REPO_TYPE_MODEL + headers = self._build_hf_headers(token=token) + + response = get_session().post( + f"{self.endpoint}/api/validate-yaml", + json={"content": content, "repoType": repo_type}, + headers=headers, + ) + # Handle warnings (example: empty metadata) + response_content = response.json() + message = "\n".join([f"- {warning.get('message')}" for warning in response_content.get("warnings", [])]) + if message: + warnings.warn(f"Warnings while validating metadata in README.md:\n{message}") + + # Raise on errors + try: + hf_raise_for_status(response) + except BadRequestError as e: + errors = response_content.get("errors", []) + message = "\n".join([f"- {error.get('message')}" for error in errors]) + raise ValueError(f"Invalid metadata in README.md.\n{message}") from e + + def get_user_overview(self, username: str, token: Union[bool, str, None] = None) -> User: + """ + Get an overview of a user on the Hub. + + Args: + username (`str`): + Username of the user to get an overview of. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + `User`: A [`User`] object with the user's overview. + + Raises: + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 404 If the user does not exist on the Hub. + """ + r = get_session().get( + f"{constants.ENDPOINT}/api/users/{username}/overview", headers=self._build_hf_headers(token=token) + ) + hf_raise_for_status(r) + return User(**r.json()) + + def list_organization_members(self, organization: str, token: Union[bool, str, None] = None) -> Iterable[User]: + """ + List of members of an organization on the Hub. + + Args: + organization (`str`): + Name of the organization to get the members of. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + `Iterable[User]`: A list of [`User`] objects with the members of the organization. + + Raises: + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 404 If the organization does not exist on the Hub. + + """ + for member in paginate( + path=f"{constants.ENDPOINT}/api/organizations/{organization}/members", + params={}, + headers=self._build_hf_headers(token=token), + ): + yield User(**member) + + def list_user_followers(self, username: str, token: Union[bool, str, None] = None) -> Iterable[User]: + """ + Get the list of followers of a user on the Hub. + + Args: + username (`str`): + Username of the user to get the followers of. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + `Iterable[User]`: A list of [`User`] objects with the followers of the user. + + Raises: + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 404 If the user does not exist on the Hub. + + """ + for follower in paginate( + path=f"{constants.ENDPOINT}/api/users/{username}/followers", + params={}, + headers=self._build_hf_headers(token=token), + ): + yield User(**follower) + + def list_user_following(self, username: str, token: Union[bool, str, None] = None) -> Iterable[User]: + """ + Get the list of users followed by a user on the Hub. + + Args: + username (`str`): + Username of the user to get the users followed by. + token (Union[bool, str, None], optional): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + `Iterable[User]`: A list of [`User`] objects with the users followed by the user. + + Raises: + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 404 If the user does not exist on the Hub. + + """ + for followed_user in paginate( + path=f"{constants.ENDPOINT}/api/users/{username}/following", + params={}, + headers=self._build_hf_headers(token=token), + ): + yield User(**followed_user) + + def list_papers( + self, + *, + query: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> Iterable[PaperInfo]: + """ + List daily papers on the Hugging Face Hub given a search query. + + Args: + query (`str`, *optional*): + A search query string to find papers. + If provided, returns papers that match the query. + token (Union[bool, str, None], *optional*): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + + Returns: + `Iterable[PaperInfo]`: an iterable of [`huggingface_hub.hf_api.PaperInfo`] objects. + + Example: + + ```python + >>> from huggingface_hub import HfApi + + >>> api = HfApi() + + # List all papers with "attention" in their title + >>> api.list_papers(query="attention") + ``` + """ + path = f"{self.endpoint}/api/papers/search" + params = {} + if query: + params["q"] = query + r = get_session().get( + path, + params=params, + headers=self._build_hf_headers(token=token), + ) + hf_raise_for_status(r) + for paper in r.json(): + yield PaperInfo(**paper) + + def paper_info(self, id: str) -> PaperInfo: + """ + Get information for a paper on the Hub. + + Args: + id (`str`, **optional**): + ArXiv id of the paper. + + Returns: + `PaperInfo`: A `PaperInfo` object. + + Raises: + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError): + HTTP 404 If the paper does not exist on the Hub. + """ + path = f"{self.endpoint}/api/papers/{id}" + r = get_session().get(path) + hf_raise_for_status(r) + return PaperInfo(**r.json()) + + def auth_check( + self, repo_id: str, *, repo_type: Optional[str] = None, token: Union[bool, str, None] = None + ) -> None: + """ + Check if the provided user token has access to a specific repository on the Hugging Face Hub. + + This method verifies whether the user, authenticated via the provided token, has access to the specified + repository. If the repository is not found or if the user lacks the required permissions to access it, + the method raises an appropriate exception. + + Args: + repo_id (`str`): + The repository to check for access. Format should be `"user/repo_name"`. + Example: `"user/my-cool-model"`. + + repo_type (`str`, *optional*): + The type of the repository. Should be one of `"model"`, `"dataset"`, or `"space"`. + If not specified, the default is `"model"`. + + token `(Union[bool, str, None]`, *optional*): + A valid user access token. If not provided, the locally saved token will be used, which is the + recommended authentication method. Set to `False` to disable authentication. + Refer to: https://huggingface.co/docs/huggingface_hub/quick-start#authentication. + + Raises: + [`~utils.RepositoryNotFoundError`]: + Raised if the repository does not exist, is private, or the user does not have access. This can + occur if the `repo_id` or `repo_type` is incorrect or if the repository is private but the user + is not authenticated. + + [`~utils.GatedRepoError`]: + Raised if the repository exists but is gated and the user is not authorized to access it. + + Example: + Check if the user has access to a repository: + + ```python + >>> from huggingface_hub import auth_check + >>> from huggingface_hub.utils import GatedRepoError, RepositoryNotFoundError + + try: + auth_check("user/my-cool-model") + except GatedRepoError: + # Handle gated repository error + print("You do not have permission to access this gated repository.") + except RepositoryNotFoundError: + # Handle repository not found error + print("The repository was not found or you do not have access.") + ``` + + In this example: + - If the user has access, the method completes successfully. + - If the repository is gated or does not exist, appropriate exceptions are raised, allowing the user + to handle them accordingly. + """ + headers = self._build_hf_headers(token=token) + if repo_type is None: + repo_type = constants.REPO_TYPE_MODEL + if repo_type not in constants.REPO_TYPES: + raise ValueError(f"Invalid repo type, must be one of {constants.REPO_TYPES}") + path = f"{self.endpoint}/api/{repo_type}s/{repo_id}/auth-check" + r = get_session().get(path, headers=headers) + hf_raise_for_status(r) + + def run_job( + self, + *, + image: str, + command: List[str], + env: Optional[Dict[str, Any]] = None, + secrets: Optional[Dict[str, Any]] = None, + flavor: Optional[SpaceHardware] = None, + timeout: Optional[Union[int, float, str]] = None, + namespace: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> JobInfo: + """ + Run compute Jobs on Hugging Face infrastructure. + + Args: + image (`str`): + The Docker image to use. + Examples: `"ubuntu"`, `"python:3.12"`, `"pytorch/pytorch:2.6.0-cuda12.4-cudnn9-devel"`. + Example with an image from a Space: `"hf.co/spaces/lhoestq/duckdb"`. + + command (`List[str]`): + The command to run. Example: `["echo", "hello"]`. + + env (`Dict[str, Any]`, *optional*): + Defines the environment variables for the Job. + + secrets (`Dict[str, Any]`, *optional*): + Defines the secret environment variables for the Job. + + flavor (`str`, *optional*): + Flavor for the hardware, as in Hugging Face Spaces. See [`SpaceHardware`] for possible values. + Defaults to `"cpu-basic"`. + + timeout (`Union[int, float, str]`, *optional*): + Max duration for the Job: int/float with s (seconds, default), m (minutes), h (hours) or d (days). + Example: `300` or `"5m"` for 5 minutes. + + namespace (`str`, *optional*): + The namespace where the Job will be created. Defaults to the current user's namespace. + + token `(Union[bool, str, None]`, *optional*): + A valid user access token. If not provided, the locally saved token will be used, which is the + recommended authentication method. Set to `False` to disable authentication. + Refer to: https://huggingface.co/docs/huggingface_hub/quick-start#authentication. + + Example: + Run your first Job: + + ```python + >>> from huggingface_hub import run_job + >>> run_job(image="python:3.12", command=["python", "-c" ,"print('Hello from HF compute!')"]) + ``` + + Run a GPU Job: + + ```python + >>> from huggingface_hub import run_job + >>> image = "pytorch/pytorch:2.6.0-cuda12.4-cudnn9-devel" + >>> command = ["python", "-c", "import torch; print(f"This code ran with the following GPU: {torch.cuda.get_device_name()}")"] + >>> run_job(image=image, command=command, flavor="a10g-small") + ``` + + """ + if namespace is None: + namespace = self.whoami(token=token)["name"] + job_spec = _create_job_spec( + image=image, + command=command, + env=env, + secrets=secrets, + flavor=flavor, + timeout=timeout, + ) + response = get_session().post( + f"https://huggingface.co/api/jobs/{namespace}", + json=job_spec, + headers=self._build_hf_headers(token=token), + ) + hf_raise_for_status(response) + job_info = response.json() + return JobInfo(**job_info, endpoint=self.endpoint) + + def fetch_job_logs( + self, + *, + job_id: str, + namespace: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> Iterable[str]: + """ + Fetch all the logs from a compute Job on Hugging Face infrastructure. + + Args: + job_id (`str`): + ID of the Job. + + namespace (`str`, *optional*): + The namespace where the Job is running. Defaults to the current user's namespace. + + token `(Union[bool, str, None]`, *optional*): + A valid user access token. If not provided, the locally saved token will be used, which is the + recommended authentication method. Set to `False` to disable authentication. + Refer to: https://huggingface.co/docs/huggingface_hub/quick-start#authentication. + + Example: + + ```python + >>> from huggingface_hub import fetch_job_logs, run_job + >>> job = run_job(image="python:3.12", command=["python", "-c" ,"print('Hello from HF compute!')"]) + >>> for log in fetch_job_logs(job.id): + ... print(log) + Hello from HF compute! + ``` + """ + if namespace is None: + namespace = self.whoami(token=token)["name"] + logging_finished = logging_started = False + job_finished = False + # - We need to retry because sometimes the /logs doesn't return logs when the job just started. + # (for example it can return only two lines: one for "Job started" and one empty line) + # - Timeouts can happen in case of build errors + # - ChunkedEncodingError can happen in case of stopped logging in the middle of streaming + # - Infinite empty log stream can happen in case of build error + # (the logs stream is infinite and empty except for the Job started message) + # - there is a ": keep-alive" every 30 seconds + + # We don't use http_backoff since we need to check ourselves if ConnectionError.__context__ is a TimeoutError + max_retries = 5 + min_wait_time = 1 + max_wait_time = 10 + sleep_time = 0 + for _ in range(max_retries): + time.sleep(sleep_time) + sleep_time = min(max_wait_time, max(min_wait_time, sleep_time * 2)) + try: + resp = get_session().get( + f"https://huggingface.co/api/jobs/{namespace}/{job_id}/logs", + headers=self._build_hf_headers(token=token), + stream=True, + timeout=120, + ) + log = None + for line in resp.iter_lines(chunk_size=1): + line = line.decode("utf-8") + if line and line.startswith("data: {"): + data = json.loads(line[len("data: ") :]) + # timestamp = data["timestamp"] + if not data["data"].startswith("===== Job started"): + logging_started = True + log = data["data"] + yield log + logging_finished = logging_started + except requests.exceptions.ChunkedEncodingError: + # Response ended prematurely + break + except KeyboardInterrupt: + break + except requests.exceptions.ConnectionError as err: + is_timeout = err.__context__ and isinstance(getattr(err.__context__, "__cause__", None), TimeoutError) + if logging_started or not is_timeout: + raise + if logging_finished or job_finished: + break + job_status = ( + get_session() + .get( + f"https://huggingface.co/api/jobs/{namespace}/{job_id}", + headers=self._build_hf_headers(token=token), + ) + .json() + ) + if "status" in job_status and job_status["status"]["stage"] not in ("RUNNING", "UPDATING"): + job_finished = True + + def list_jobs( + self, + *, + timeout: Optional[int] = None, + namespace: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> List[JobInfo]: + """ + List compute Jobs on Hugging Face infrastructure. + + Args: + timeout (`float`, *optional*): + Whether to set a timeout for the request to the Hub. + + namespace (`str`, *optional*): + The namespace from where it lists the jobs. Defaults to the current user's namespace. + + token `(Union[bool, str, None]`, *optional*): + A valid user access token. If not provided, the locally saved token will be used, which is the + recommended authentication method. Set to `False` to disable authentication. + Refer to: https://huggingface.co/docs/huggingface_hub/quick-start#authentication. + """ + if namespace is None: + namespace = whoami(token=token)["name"] + response = get_session().get( + f"{self.endpoint}/api/jobs/{namespace}", + headers=self._build_hf_headers(token=token), + timeout=timeout, + ) + response.raise_for_status() + return [JobInfo(**job_info, endpoint=self.endpoint) for job_info in response.json()] + + def inspect_job( + self, + *, + job_id: str, + namespace: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> JobInfo: + """ + Inspect a compute Job on Hugging Face infrastructure. + + Args: + job_id (`str`): + ID of the Job. + + namespace (`str`, *optional*): + The namespace where the Job is running. Defaults to the current user's namespace. + + token `(Union[bool, str, None]`, *optional*): + A valid user access token. If not provided, the locally saved token will be used, which is the + recommended authentication method. Set to `False` to disable authentication. + Refer to: https://huggingface.co/docs/huggingface_hub/quick-start#authentication. + + Example: + + ```python + >>> from huggingface_hub import inspect_job, run_job + >>> job = run_job(image="python:3.12", command=["python", "-c" ,"print('Hello from HF compute!')"]) + >>> inspect_job(job.id) + JobInfo( + id='68780d00bbe36d38803f645f', + created_at=datetime.datetime(2025, 7, 16, 20, 35, 12, 808000, tzinfo=datetime.timezone.utc), + docker_image='python:3.12', + space_id=None, + command=['python', '-c', "print('Hello from HF compute!')"], + arguments=[], + environment={}, + secrets={}, + flavor='cpu-basic', + status=JobStatus(stage='RUNNING', message=None) + ) + ``` + """ + if namespace is None: + namespace = self.whoami(token=token)["name"] + response = get_session().get( + f"{self.endpoint}/api/jobs/{namespace}/{job_id}", + headers=self._build_hf_headers(token=token), + ) + response.raise_for_status() + return JobInfo(**response.json(), endpoint=self.endpoint) + + def cancel_job( + self, + *, + job_id: str, + namespace: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> None: + """ + Cancel a compute Job on Hugging Face infrastructure. + + Args: + job_id (`str`): + ID of the Job. + + namespace (`str`, *optional*): + The namespace where the Job is running. Defaults to the current user's namespace. + + token `(Union[bool, str, None]`, *optional*): + A valid user access token. If not provided, the locally saved token will be used, which is the + recommended authentication method. Set to `False` to disable authentication. + Refer to: https://huggingface.co/docs/huggingface_hub/quick-start#authentication. + """ + if namespace is None: + namespace = self.whoami(token=token)["name"] + get_session().post( + f"{self.endpoint}/api/jobs/{namespace}/{job_id}/cancel", + headers=self._build_hf_headers(token=token), + ).raise_for_status() + + @experimental + def run_uv_job( + self, + script: str, + *, + script_args: Optional[List[str]] = None, + dependencies: Optional[List[str]] = None, + python: Optional[str] = None, + image: Optional[str] = None, + env: Optional[Dict[str, Any]] = None, + secrets: Optional[Dict[str, Any]] = None, + flavor: Optional[SpaceHardware] = None, + timeout: Optional[Union[int, float, str]] = None, + namespace: Optional[str] = None, + token: Union[bool, str, None] = None, + _repo: Optional[str] = None, + ) -> JobInfo: + """ + Run a UV script Job on Hugging Face infrastructure. + + Args: + script (`str`): + Path or URL of the UV script, or a command. + + script_args (`List[str]`, *optional*) + Arguments to pass to the script or command. + + dependencies (`List[str]`, *optional*) + Dependencies to use to run the UV script. + + python (`str`, *optional*) + Use a specific Python version. Default is 3.12. + + image (`str`, *optional*, defaults to "ghcr.io/astral-sh/uv:python3.12-bookworm"): + Use a custom Docker image with `uv` installed. + + env (`Dict[str, Any]`, *optional*): + Defines the environment variables for the Job. + + secrets (`Dict[str, Any]`, *optional*): + Defines the secret environment variables for the Job. + + flavor (`str`, *optional*): + Flavor for the hardware, as in Hugging Face Spaces. See [`SpaceHardware`] for possible values. + Defaults to `"cpu-basic"`. + + timeout (`Union[int, float, str]`, *optional*): + Max duration for the Job: int/float with s (seconds, default), m (minutes), h (hours) or d (days). + Example: `300` or `"5m"` for 5 minutes. + + namespace (`str`, *optional*): + The namespace where the Job will be created. Defaults to the current user's namespace. + + token `(Union[bool, str, None]`, *optional*): + A valid user access token. If not provided, the locally saved token will be used, which is the + recommended authentication method. Set to `False` to disable authentication. + Refer to: https://huggingface.co/docs/huggingface_hub/quick-start#authentication. + + Example: + + Run a script from a URL: + + ```python + >>> from huggingface_hub import run_uv_job + >>> script = "https://raw.githubusercontent.com/huggingface/trl/refs/heads/main/trl/scripts/sft.py" + >>> script_args = ["--model_name_or_path", "Qwen/Qwen2-0.5B", "--dataset_name", "trl-lib/Capybara", "--push_to_hub"] + >>> run_uv_job(script, script_args=script_args, dependencies=["trl"], flavor="a10g-small") + ``` + + Run a local script: + + ```python + >>> from huggingface_hub import run_uv_job + >>> script = "my_sft.py" + >>> script_args = ["--model_name_or_path", "Qwen/Qwen2-0.5B", "--dataset_name", "trl-lib/Capybara", "--push_to_hub"] + >>> run_uv_job(script, script_args=script_args, dependencies=["trl"], flavor="a10g-small") + ``` + + Run a command: + + ```python + >>> from huggingface_hub import run_uv_job + >>> script = "lighteval" + >>> script_args= ["endpoint", "inference-providers", "model_name=openai/gpt-oss-20b,provider=auto", "lighteval|gsm8k|0|0"] + >>> run_uv_job(script, script_args=script_args, dependencies=["lighteval"], flavor="a10g-small") + ``` + """ + image = image or "ghcr.io/astral-sh/uv:python3.12-bookworm" + env = env or {} + secrets = secrets or {} + + # Build command + command, env, secrets = self._create_uv_command_env_and_secrets( + script=script, + script_args=script_args, + dependencies=dependencies, + python=python, + env=env, + secrets=secrets, + namespace=namespace, + token=token, + _repo=_repo, + ) + # Create RunCommand args + return self.run_job( + image=image, + command=command, + env=env, + secrets=secrets, + flavor=flavor, + timeout=timeout, + namespace=namespace, + token=token, + ) + + def create_scheduled_job( + self, + *, + image: str, + command: List[str], + schedule: str, + suspend: Optional[bool] = None, + concurrency: Optional[bool] = None, + env: Optional[Dict[str, Any]] = None, + secrets: Optional[Dict[str, Any]] = None, + flavor: Optional[SpaceHardware] = None, + timeout: Optional[Union[int, float, str]] = None, + namespace: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> ScheduledJobInfo: + """ + Create scheduled compute Jobs on Hugging Face infrastructure. + + Args: + image (`str`): + The Docker image to use. + Examples: `"ubuntu"`, `"python:3.12"`, `"pytorch/pytorch:2.6.0-cuda12.4-cudnn9-devel"`. + Example with an image from a Space: `"hf.co/spaces/lhoestq/duckdb"`. + + command (`List[str]`): + The command to run. Example: `["echo", "hello"]`. + + schedule (`str`): + One of "@annually", "@yearly", "@monthly", "@weekly", "@daily", "@hourly", or a + CRON schedule expression (e.g., '0 9 * * 1' for 9 AM every Monday). + + suspend (`bool`, *optional*): + If True, the scheduled Job is suspended (paused). Defaults to False. + + concurrency (`bool`, *optional*): + If True, multiple instances of this Job can run concurrently. Defaults to False. + + env (`Dict[str, Any]`, *optional*): + Defines the environment variables for the Job. + + secrets (`Dict[str, Any]`, *optional*): + Defines the secret environment variables for the Job. + + flavor (`str`, *optional*): + Flavor for the hardware, as in Hugging Face Spaces. See [`SpaceHardware`] for possible values. + Defaults to `"cpu-basic"`. + + timeout (`Union[int, float, str]`, *optional*): + Max duration for the Job: int/float with s (seconds, default), m (minutes), h (hours) or d (days). + Example: `300` or `"5m"` for 5 minutes. + + namespace (`str`, *optional*): + The namespace where the Job will be created. Defaults to the current user's namespace. + + token `(Union[bool, str, None]`, *optional*): + A valid user access token. If not provided, the locally saved token will be used, which is the + recommended authentication method. Set to `False` to disable authentication. + Refer to: https://huggingface.co/docs/huggingface_hub/quick-start#authentication. + + Example: + Create your first scheduled Job: + + ```python + >>> from huggingface_hub import create_scheduled_job + >>> create_scheduled_job(image="python:3.12", command=["python", "-c" ,"print('Hello from HF compute!')"], schedule="@hourly") + ``` + + Use a CRON schedule expression: + + ```python + >>> from huggingface_hub import create_scheduled_job + >>> create_scheduled_job(image="python:3.12", command=["python", "-c" ,"print('this runs every 5min')"], schedule="*/5 * * * *") + ``` + + Create a scheduled GPU Job: + + ```python + >>> from huggingface_hub import create_scheduled_job + >>> image = "pytorch/pytorch:2.6.0-cuda12.4-cudnn9-devel" + >>> command = ["python", "-c", "import torch; print(f"This code ran with the following GPU: {torch.cuda.get_device_name()}")"] + >>> create_scheduled_job(image, command, flavor="a10g-small", schedule="@hourly") + ``` + + """ + if namespace is None: + namespace = self.whoami(token=token)["name"] + + # prepare payload to send to HF Jobs API + job_spec = _create_job_spec( + image=image, + command=command, + env=env, + secrets=secrets, + flavor=flavor, + timeout=timeout, + ) + input_json: Dict[str, Any] = { + "jobSpec": job_spec, + "schedule": schedule, + } + if concurrency is not None: + input_json["concurrency"] = concurrency + if suspend is not None: + input_json["suspend"] = suspend + response = get_session().post( + f"https://huggingface.co/api/scheduled-jobs/{namespace}", + json=input_json, + headers=self._build_hf_headers(token=token), + ) + hf_raise_for_status(response) + scheduled_job_info = response.json() + return ScheduledJobInfo(**scheduled_job_info) + + def list_scheduled_jobs( + self, + *, + timeout: Optional[int] = None, + namespace: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> List[ScheduledJobInfo]: + """ + List scheduled compute Jobs on Hugging Face infrastructure. + + Args: + timeout (`float`, *optional*): + Whether to set a timeout for the request to the Hub. + + namespace (`str`, *optional*): + The namespace from where it lists the jobs. Defaults to the current user's namespace. + + token `(Union[bool, str, None]`, *optional*): + A valid user access token. If not provided, the locally saved token will be used, which is the + recommended authentication method. Set to `False` to disable authentication. + Refer to: https://huggingface.co/docs/huggingface_hub/quick-start#authentication. + """ + if namespace is None: + namespace = self.whoami(token=token)["name"] + response = get_session().get( + f"{self.endpoint}/api/scheduled-jobs/{namespace}", + headers=self._build_hf_headers(token=token), + timeout=timeout, + ) + hf_raise_for_status(response) + return [ScheduledJobInfo(**scheduled_job_info) for scheduled_job_info in response.json()] + + def inspect_scheduled_job( + self, + *, + scheduled_job_id: str, + namespace: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> ScheduledJobInfo: + """ + Inspect a scheduled compute Job on Hugging Face infrastructure. + + Args: + scheduled_job_id (`str`): + ID of the scheduled Job. + + namespace (`str`, *optional*): + The namespace where the scheduled Job is. Defaults to the current user's namespace. + + token `(Union[bool, str, None]`, *optional*): + A valid user access token. If not provided, the locally saved token will be used, which is the + recommended authentication method. Set to `False` to disable authentication. + Refer to: https://huggingface.co/docs/huggingface_hub/quick-start#authentication. + + Example: + + ```python + >>> from huggingface_hub import inspect_job, create_scheduled_job + >>> scheduled_job = create_scheduled_job(image="python:3.12", command=["python", "-c" ,"print('Hello from HF compute!')"], schedule="@hourly") + >>> inspect_scheduled_job(scheduled_job.id) + ``` + """ + if namespace is None: + namespace = self.whoami(token=token)["name"] + response = get_session().get( + f"{self.endpoint}/api/scheduled-jobs/{namespace}/{scheduled_job_id}", + headers=self._build_hf_headers(token=token), + ) + hf_raise_for_status(response) + return ScheduledJobInfo(**response.json()) + + def delete_scheduled_job( + self, + *, + scheduled_job_id: str, + namespace: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> None: + """ + Delete a scheduled compute Job on Hugging Face infrastructure. + + Args: + scheduled_job_id (`str`): + ID of the scheduled Job. + + namespace (`str`, *optional*): + The namespace where the scheduled Job is. Defaults to the current user's namespace. + + token `(Union[bool, str, None]`, *optional*): + A valid user access token. If not provided, the locally saved token will be used, which is the + recommended authentication method. Set to `False` to disable authentication. + Refer to: https://huggingface.co/docs/huggingface_hub/quick-start#authentication. + """ + if namespace is None: + namespace = self.whoami(token=token)["name"] + response = get_session().delete( + f"{self.endpoint}/api/scheduled-jobs/{namespace}/{scheduled_job_id}", + headers=self._build_hf_headers(token=token), + ) + hf_raise_for_status(response) + + def suspend_scheduled_job( + self, + *, + scheduled_job_id: str, + namespace: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> None: + """ + Suspend (pause) a scheduled compute Job on Hugging Face infrastructure. + + Args: + scheduled_job_id (`str`): + ID of the scheduled Job. + + namespace (`str`, *optional*): + The namespace where the scheduled Job is. Defaults to the current user's namespace. + + token `(Union[bool, str, None]`, *optional*): + A valid user access token. If not provided, the locally saved token will be used, which is the + recommended authentication method. Set to `False` to disable authentication. + Refer to: https://huggingface.co/docs/huggingface_hub/quick-start#authentication. + """ + if namespace is None: + namespace = self.whoami(token=token)["name"] + get_session().post( + f"{self.endpoint}/api/scheduled-jobs/{namespace}/{scheduled_job_id}/suspend", + headers=self._build_hf_headers(token=token), + ).raise_for_status() + + def resume_scheduled_job( + self, + *, + scheduled_job_id: str, + namespace: Optional[str] = None, + token: Union[bool, str, None] = None, + ) -> None: + """ + Resume (unpause) a scheduled compute Job on Hugging Face infrastructure. + + Args: + scheduled_job_id (`str`): + ID of the scheduled Job. + + namespace (`str`, *optional*): + The namespace where the scheduled Job is. Defaults to the current user's namespace. + + token `(Union[bool, str, None]`, *optional*): + A valid user access token. If not provided, the locally saved token will be used, which is the + recommended authentication method. Set to `False` to disable authentication. + Refer to: https://huggingface.co/docs/huggingface_hub/quick-start#authentication. + """ + if namespace is None: + namespace = self.whoami(token=token)["name"] + get_session().post( + f"{self.endpoint}/api/scheduled-jobs/{namespace}/{scheduled_job_id}/resume", + headers=self._build_hf_headers(token=token), + ).raise_for_status() + + @experimental + def create_scheduled_uv_job( + self, + script: str, + *, + script_args: Optional[List[str]] = None, + schedule: str, + suspend: Optional[bool] = None, + concurrency: Optional[bool] = None, + dependencies: Optional[List[str]] = None, + python: Optional[str] = None, + image: Optional[str] = None, + env: Optional[Dict[str, Any]] = None, + secrets: Optional[Dict[str, Any]] = None, + flavor: Optional[SpaceHardware] = None, + timeout: Optional[Union[int, float, str]] = None, + namespace: Optional[str] = None, + token: Union[bool, str, None] = None, + _repo: Optional[str] = None, + ) -> ScheduledJobInfo: + """ + Run a UV script Job on Hugging Face infrastructure. + + Args: + script (`str`): + Path or URL of the UV script, or a command. + + script_args (`List[str]`, *optional*) + Arguments to pass to the script, or a command. + + schedule (`str`): + One of "@annually", "@yearly", "@monthly", "@weekly", "@daily", "@hourly", or a + CRON schedule expression (e.g., '0 9 * * 1' for 9 AM every Monday). + + suspend (`bool`, *optional*): + If True, the scheduled Job is suspended (paused). Defaults to False. + + concurrency (`bool`, *optional*): + If True, multiple instances of this Job can run concurrently. Defaults to False. + + dependencies (`List[str]`, *optional*) + Dependencies to use to run the UV script. + + python (`str`, *optional*) + Use a specific Python version. Default is 3.12. + + image (`str`, *optional*, defaults to "ghcr.io/astral-sh/uv:python3.12-bookworm"): + Use a custom Docker image with `uv` installed. + + env (`Dict[str, Any]`, *optional*): + Defines the environment variables for the Job. + + secrets (`Dict[str, Any]`, *optional*): + Defines the secret environment variables for the Job. + + flavor (`str`, *optional*): + Flavor for the hardware, as in Hugging Face Spaces. See [`SpaceHardware`] for possible values. + Defaults to `"cpu-basic"`. + + timeout (`Union[int, float, str]`, *optional*): + Max duration for the Job: int/float with s (seconds, default), m (minutes), h (hours) or d (days). + Example: `300` or `"5m"` for 5 minutes. + + namespace (`str`, *optional*): + The namespace where the Job will be created. Defaults to the current user's namespace. + + token `(Union[bool, str, None]`, *optional*): + A valid user access token. If not provided, the locally saved token will be used, which is the + recommended authentication method. Set to `False` to disable authentication. + Refer to: https://huggingface.co/docs/huggingface_hub/quick-start#authentication. + + Example: + + Schedule a script from a URL: + + ```python + >>> from huggingface_hub import create_scheduled_uv_job + >>> script = "https://raw.githubusercontent.com/huggingface/trl/refs/heads/main/trl/scripts/sft.py" + >>> script_args = ["--model_name_or_path", "Qwen/Qwen2-0.5B", "--dataset_name", "trl-lib/Capybara", "--push_to_hub"] + >>> create_scheduled_uv_job(script, script_args=script_args, dependencies=["trl"], flavor="a10g-small", schedule="@weekly") + ``` + + Schedule a local script: + + ```python + >>> from huggingface_hub import create_scheduled_uv_job + >>> script = "my_sft.py" + >>> script_args = ["--model_name_or_path", "Qwen/Qwen2-0.5B", "--dataset_name", "trl-lib/Capybara", "--push_to_hub"] + >>> create_scheduled_uv_job(script, script_args=script_args, dependencies=["trl"], flavor="a10g-small", schedule="@weekly") + ``` + + Schedule a command: + + ```python + >>> from huggingface_hub import create_scheduled_uv_job + >>> script = "lighteval" + >>> script_args= ["endpoint", "inference-providers", "model_name=openai/gpt-oss-20b,provider=auto", "lighteval|gsm8k|0|0"] + >>> create_scheduled_uv_job(script, script_args=script_args, dependencies=["lighteval"], flavor="a10g-small", schedule="@weekly") + ``` + """ + image = image or "ghcr.io/astral-sh/uv:python3.12-bookworm" + # Build command + command, env, secrets = self._create_uv_command_env_and_secrets( + script=script, + script_args=script_args, + dependencies=dependencies, + python=python, + env=env, + secrets=secrets, + namespace=namespace, + token=token, + _repo=_repo, + ) + # Create RunCommand args + return self.create_scheduled_job( + image=image, + command=command, + schedule=schedule, + suspend=suspend, + concurrency=concurrency, + env=env, + secrets=secrets, + flavor=flavor, + timeout=timeout, + namespace=namespace, + token=token, + ) + + def _create_uv_command_env_and_secrets( + self, + *, + script: str, + script_args: Optional[List[str]], + dependencies: Optional[List[str]], + python: Optional[str], + env: Optional[Dict[str, Any]], + secrets: Optional[Dict[str, Any]], + namespace: Optional[str], + token: Union[bool, str, None], + _repo: Optional[str], + ) -> Tuple[List[str], Dict[str, Any], Dict[str, Any]]: + env = env or {} + secrets = secrets or {} + + # Build command + uv_args = [] + if dependencies: + for dependency in dependencies: + uv_args += ["--with", dependency] + if python: + uv_args += ["--python", python] + script_args = script_args or [] + + if namespace is None: + namespace = self.whoami(token=token)["name"] + + is_url = script.startswith("http://") or script.startswith("https://") + if is_url or not Path(script).is_file(): + # Direct URL execution or command - no upload needed + command = ["uv", "run"] + uv_args + [script] + script_args + else: + # Local file - upload to HF + script_path = Path(script) + filename = script_path.name + # Parse repo + if _repo: + repo_id = _repo + if "/" not in repo_id: + repo_id = f"{namespace}/{repo_id}" + else: + repo_id = f"{namespace}/hf-cli-jobs-uv-run-scripts" + + # Create repo if needed + try: + self.repo_info(repo_id, repo_type="dataset") + logger.debug(f"Using existing repository: {repo_id}") + except RepositoryNotFoundError: + logger.info(f"Creating repository: {repo_id}") + create_repo(repo_id, repo_type="dataset", private=True, exist_ok=True) + + # Upload script + logger.info(f"Uploading {script_path.name} to {repo_id}...") + with open(script_path, "r") as f: + script_content = f.read() + + commit_hash = self.upload_file( + path_or_fileobj=script_content.encode(), + path_in_repo=filename, + repo_id=repo_id, + repo_type="dataset", + ).oid + + script_url = f"{self.endpoint}/datasets/{repo_id}/resolve/{commit_hash}/{filename}" + repo_url = f"{self.endpoint}/datasets/{repo_id}" + + logger.debug(f"✓ Script uploaded to: {repo_url}/blob/main/{filename}") + + # Create and upload minimal README + timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S UTC") + readme_content = dedent( + f""" + --- + tags: + - hf-cli-jobs-uv-script + - ephemeral + viewer: false + --- + + # UV Script: {filename} + + Executed via `hf jobs uv run` on {timestamp} + + ## Run this script + + ```bash + hf jobs uv run {filename} + ``` + + --- + *Created with [hf jobs](https://huggingface.co/docs/huggingface_hub/main/en/guides/jobs)* + """ + ) + self.upload_file( + path_or_fileobj=readme_content.encode(), + path_in_repo="README.md", + repo_id=repo_id, + repo_type="dataset", + ) + + secrets["UV_SCRIPT_HF_TOKEN"] = token or self.token or get_token() + env["UV_SCRIPT_URL"] = script_url + + pre_command = ( + dedent( + """ + import urllib.request + import os + from pathlib import Path + o = urllib.request.build_opener() + o.addheaders = [("Authorization", "Bearer " + os.environ["UV_SCRIPT_HF_TOKEN"])] + Path("/tmp/script.py").write_bytes(o.open(os.environ["UV_SCRIPT_URL"]).read()) + """ + ) + .strip() + .replace('"', r"\"") + .split("\n") + ) + pre_command = ["python", "-c", '"' + "; ".join(pre_command) + '"'] + command = ["uv", "run"] + uv_args + ["/tmp/script.py"] + script_args + command = ["bash", "-c", " ".join(pre_command) + " && " + " ".join(command)] + return command, env, secrets + + +def _parse_revision_from_pr_url(pr_url: str) -> str: + """Safely parse revision number from a PR url. + + Example: + ```py + >>> _parse_revision_from_pr_url("https://huggingface.co/bigscience/bloom/discussions/2") + "refs/pr/2" + ``` + """ + re_match = re.match(_REGEX_DISCUSSION_URL, pr_url) + if re_match is None: + raise RuntimeError(f"Unexpected response from the hub, expected a Pull Request URL but got: '{pr_url}'") + return f"refs/pr/{re_match[1]}" + + +api = HfApi() + +whoami = api.whoami +auth_check = api.auth_check +get_token_permission = api.get_token_permission + +list_models = api.list_models +model_info = api.model_info + +list_datasets = api.list_datasets +dataset_info = api.dataset_info + +list_spaces = api.list_spaces +space_info = api.space_info + +list_papers = api.list_papers +paper_info = api.paper_info + +repo_exists = api.repo_exists +revision_exists = api.revision_exists +file_exists = api.file_exists +repo_info = api.repo_info +list_repo_files = api.list_repo_files +list_repo_refs = api.list_repo_refs +list_repo_commits = api.list_repo_commits +list_repo_tree = api.list_repo_tree +get_paths_info = api.get_paths_info + +get_model_tags = api.get_model_tags +get_dataset_tags = api.get_dataset_tags + +create_commit = api.create_commit +create_repo = api.create_repo +delete_repo = api.delete_repo +update_repo_visibility = api.update_repo_visibility +update_repo_settings = api.update_repo_settings +move_repo = api.move_repo +upload_file = api.upload_file +upload_folder = api.upload_folder +delete_file = api.delete_file +delete_folder = api.delete_folder +delete_files = api.delete_files +upload_large_folder = api.upload_large_folder +preupload_lfs_files = api.preupload_lfs_files +create_branch = api.create_branch +delete_branch = api.delete_branch +create_tag = api.create_tag +delete_tag = api.delete_tag +get_full_repo_name = api.get_full_repo_name + +# Danger-zone API +super_squash_history = api.super_squash_history +list_lfs_files = api.list_lfs_files +permanently_delete_lfs_files = api.permanently_delete_lfs_files + +# Safetensors helpers +get_safetensors_metadata = api.get_safetensors_metadata +parse_safetensors_file_metadata = api.parse_safetensors_file_metadata + +# Background jobs +run_as_future = api.run_as_future + +# Activity API +list_liked_repos = api.list_liked_repos +list_repo_likers = api.list_repo_likers +unlike = api.unlike + +# Community API +get_discussion_details = api.get_discussion_details +get_repo_discussions = api.get_repo_discussions +create_discussion = api.create_discussion +create_pull_request = api.create_pull_request +change_discussion_status = api.change_discussion_status +comment_discussion = api.comment_discussion +edit_discussion_comment = api.edit_discussion_comment +rename_discussion = api.rename_discussion +merge_pull_request = api.merge_pull_request + +# Space API +add_space_secret = api.add_space_secret +delete_space_secret = api.delete_space_secret +get_space_variables = api.get_space_variables +add_space_variable = api.add_space_variable +delete_space_variable = api.delete_space_variable +get_space_runtime = api.get_space_runtime +request_space_hardware = api.request_space_hardware +set_space_sleep_time = api.set_space_sleep_time +pause_space = api.pause_space +restart_space = api.restart_space +duplicate_space = api.duplicate_space +request_space_storage = api.request_space_storage +delete_space_storage = api.delete_space_storage + +# Inference Endpoint API +list_inference_endpoints = api.list_inference_endpoints +create_inference_endpoint = api.create_inference_endpoint +get_inference_endpoint = api.get_inference_endpoint +update_inference_endpoint = api.update_inference_endpoint +delete_inference_endpoint = api.delete_inference_endpoint +pause_inference_endpoint = api.pause_inference_endpoint +resume_inference_endpoint = api.resume_inference_endpoint +scale_to_zero_inference_endpoint = api.scale_to_zero_inference_endpoint +create_inference_endpoint_from_catalog = api.create_inference_endpoint_from_catalog +list_inference_catalog = api.list_inference_catalog + +# Collections API +get_collection = api.get_collection +list_collections = api.list_collections +create_collection = api.create_collection +update_collection_metadata = api.update_collection_metadata +delete_collection = api.delete_collection +add_collection_item = api.add_collection_item +update_collection_item = api.update_collection_item +delete_collection_item = api.delete_collection_item +delete_collection_item = api.delete_collection_item + +# Access requests API +list_pending_access_requests = api.list_pending_access_requests +list_accepted_access_requests = api.list_accepted_access_requests +list_rejected_access_requests = api.list_rejected_access_requests +cancel_access_request = api.cancel_access_request +accept_access_request = api.accept_access_request +reject_access_request = api.reject_access_request +grant_access = api.grant_access + +# Webhooks API +create_webhook = api.create_webhook +disable_webhook = api.disable_webhook +delete_webhook = api.delete_webhook +enable_webhook = api.enable_webhook +get_webhook = api.get_webhook +list_webhooks = api.list_webhooks +update_webhook = api.update_webhook + + +# User API +get_user_overview = api.get_user_overview +list_organization_members = api.list_organization_members +list_user_followers = api.list_user_followers +list_user_following = api.list_user_following + +# Jobs API +run_job = api.run_job +fetch_job_logs = api.fetch_job_logs +list_jobs = api.list_jobs +inspect_job = api.inspect_job +cancel_job = api.cancel_job +run_uv_job = api.run_uv_job +create_scheduled_job = api.create_scheduled_job +list_scheduled_jobs = api.list_scheduled_jobs +inspect_scheduled_job = api.inspect_scheduled_job +delete_scheduled_job = api.delete_scheduled_job +suspend_scheduled_job = api.suspend_scheduled_job +resume_scheduled_job = api.resume_scheduled_job +create_scheduled_uv_job = api.create_scheduled_uv_job diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/hf_file_system.py b/.venv/lib/python3.12/site-packages/huggingface_hub/hf_file_system.py new file mode 100644 index 0000000000000000000000000000000000000000..d8cb16031ca70bd089a1bf5c3acbfd273a498248 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/hf_file_system.py @@ -0,0 +1,1145 @@ +import os +import re +import tempfile +from collections import deque +from dataclasses import dataclass, field +from datetime import datetime +from itertools import chain +from pathlib import Path +from typing import Any, Dict, Iterator, List, NoReturn, Optional, Tuple, Union +from urllib.parse import quote, unquote + +import fsspec +from fsspec.callbacks import _DEFAULT_CALLBACK, NoOpCallback, TqdmCallback +from fsspec.utils import isfilelike +from requests import Response + +from . import constants +from ._commit_api import CommitOperationCopy, CommitOperationDelete +from .errors import EntryNotFoundError, RepositoryNotFoundError, RevisionNotFoundError +from .file_download import hf_hub_url, http_get +from .hf_api import HfApi, LastCommitInfo, RepoFile +from .utils import HFValidationError, hf_raise_for_status, http_backoff + + +# Regex used to match special revisions with "/" in them (see #1710) +SPECIAL_REFS_REVISION_REGEX = re.compile( + r""" + (^refs\/convert\/\w+) # `refs/convert/parquet` revisions + | + (^refs\/pr\/\d+) # PR revisions + """, + re.VERBOSE, +) + + +@dataclass +class HfFileSystemResolvedPath: + """Data structure containing information about a resolved Hugging Face file system path.""" + + repo_type: str + repo_id: str + revision: str + path_in_repo: str + # The part placed after '@' in the initial path. It can be a quoted or unquoted refs revision. + # Used to reconstruct the unresolved path to return to the user. + _raw_revision: Optional[str] = field(default=None, repr=False) + + def unresolve(self) -> str: + repo_path = constants.REPO_TYPES_URL_PREFIXES.get(self.repo_type, "") + self.repo_id + if self._raw_revision: + return f"{repo_path}@{self._raw_revision}/{self.path_in_repo}".rstrip("/") + elif self.revision != constants.DEFAULT_REVISION: + return f"{repo_path}@{safe_revision(self.revision)}/{self.path_in_repo}".rstrip("/") + else: + return f"{repo_path}/{self.path_in_repo}".rstrip("/") + + +class HfFileSystem(fsspec.AbstractFileSystem): + """ + Access a remote Hugging Face Hub repository as if were a local file system. + + + + [`HfFileSystem`] provides fsspec compatibility, which is useful for libraries that require it (e.g., reading + Hugging Face datasets directly with `pandas`). However, it introduces additional overhead due to this compatibility + layer. For better performance and reliability, it's recommended to use `HfApi` methods when possible. + + + + Args: + token (`str` or `bool`, *optional*): + A valid user access token (string). Defaults to the locally saved + token, which is the recommended method for authentication (see + https://huggingface.co/docs/huggingface_hub/quick-start#authentication). + To disable authentication, pass `False`. + endpoint (`str`, *optional*): + Endpoint of the Hub. Defaults to . + Usage: + + ```python + >>> from huggingface_hub import HfFileSystem + + >>> fs = HfFileSystem() + + >>> # List files + >>> fs.glob("my-username/my-model/*.bin") + ['my-username/my-model/pytorch_model.bin'] + >>> fs.ls("datasets/my-username/my-dataset", detail=False) + ['datasets/my-username/my-dataset/.gitattributes', 'datasets/my-username/my-dataset/README.md', 'datasets/my-username/my-dataset/data.json'] + + >>> # Read/write files + >>> with fs.open("my-username/my-model/pytorch_model.bin") as f: + ... data = f.read() + >>> with fs.open("my-username/my-model/pytorch_model.bin", "wb") as f: + ... f.write(data) + ``` + """ + + root_marker = "" + protocol = "hf" + + def __init__( + self, + *args, + endpoint: Optional[str] = None, + token: Union[bool, str, None] = None, + **storage_options, + ): + super().__init__(*args, **storage_options) + self.endpoint = endpoint or constants.ENDPOINT + self.token = token + self._api = HfApi(endpoint=endpoint, token=token) + # Maps (repo_type, repo_id, revision) to a 2-tuple with: + # * the 1st element indicating whether the repositoy and the revision exist + # * the 2nd element being the exception raised if the repository or revision doesn't exist + self._repo_and_revision_exists_cache: Dict[ + Tuple[str, str, Optional[str]], Tuple[bool, Optional[Exception]] + ] = {} + + def _repo_and_revision_exist( + self, repo_type: str, repo_id: str, revision: Optional[str] + ) -> Tuple[bool, Optional[Exception]]: + if (repo_type, repo_id, revision) not in self._repo_and_revision_exists_cache: + try: + self._api.repo_info( + repo_id, revision=revision, repo_type=repo_type, timeout=constants.HF_HUB_ETAG_TIMEOUT + ) + except (RepositoryNotFoundError, HFValidationError) as e: + self._repo_and_revision_exists_cache[(repo_type, repo_id, revision)] = False, e + self._repo_and_revision_exists_cache[(repo_type, repo_id, None)] = False, e + except RevisionNotFoundError as e: + self._repo_and_revision_exists_cache[(repo_type, repo_id, revision)] = False, e + self._repo_and_revision_exists_cache[(repo_type, repo_id, None)] = True, None + else: + self._repo_and_revision_exists_cache[(repo_type, repo_id, revision)] = True, None + self._repo_and_revision_exists_cache[(repo_type, repo_id, None)] = True, None + return self._repo_and_revision_exists_cache[(repo_type, repo_id, revision)] + + def resolve_path(self, path: str, revision: Optional[str] = None) -> HfFileSystemResolvedPath: + """ + Resolve a Hugging Face file system path into its components. + + Args: + path (`str`): + Path to resolve. + revision (`str`, *optional*): + The revision of the repo to resolve. Defaults to the revision specified in the path. + + Returns: + [`HfFileSystemResolvedPath`]: Resolved path information containing `repo_type`, `repo_id`, `revision` and `path_in_repo`. + + Raises: + `ValueError`: + If path contains conflicting revision information. + `NotImplementedError`: + If trying to list repositories. + """ + + def _align_revision_in_path_with_revision( + revision_in_path: Optional[str], revision: Optional[str] + ) -> Optional[str]: + if revision is not None: + if revision_in_path is not None and revision_in_path != revision: + raise ValueError( + f'Revision specified in path ("{revision_in_path}") and in `revision` argument ("{revision}")' + " are not the same." + ) + else: + revision = revision_in_path + return revision + + path = self._strip_protocol(path) + if not path: + # can't list repositories at root + raise NotImplementedError("Access to repositories lists is not implemented.") + elif path.split("/")[0] + "/" in constants.REPO_TYPES_URL_PREFIXES.values(): + if "/" not in path: + # can't list repositories at the repository type level + raise NotImplementedError("Access to repositories lists is not implemented.") + repo_type, path = path.split("/", 1) + repo_type = constants.REPO_TYPES_MAPPING[repo_type] + else: + repo_type = constants.REPO_TYPE_MODEL + if path.count("/") > 0: + if "@" in path: + repo_id, revision_in_path = path.split("@", 1) + if "/" in revision_in_path: + match = SPECIAL_REFS_REVISION_REGEX.search(revision_in_path) + if match is not None and revision in (None, match.group()): + # Handle `refs/convert/parquet` and PR revisions separately + path_in_repo = SPECIAL_REFS_REVISION_REGEX.sub("", revision_in_path).lstrip("/") + revision_in_path = match.group() + else: + revision_in_path, path_in_repo = revision_in_path.split("/", 1) + else: + path_in_repo = "" + revision = _align_revision_in_path_with_revision(unquote(revision_in_path), revision) + repo_and_revision_exist, err = self._repo_and_revision_exist(repo_type, repo_id, revision) + if not repo_and_revision_exist: + _raise_file_not_found(path, err) + else: + revision_in_path = None + repo_id_with_namespace = "/".join(path.split("/")[:2]) + path_in_repo_with_namespace = "/".join(path.split("/")[2:]) + repo_id_without_namespace = path.split("/")[0] + path_in_repo_without_namespace = "/".join(path.split("/")[1:]) + repo_id = repo_id_with_namespace + path_in_repo = path_in_repo_with_namespace + repo_and_revision_exist, err = self._repo_and_revision_exist(repo_type, repo_id, revision) + if not repo_and_revision_exist: + if isinstance(err, (RepositoryNotFoundError, HFValidationError)): + repo_id = repo_id_without_namespace + path_in_repo = path_in_repo_without_namespace + repo_and_revision_exist, _ = self._repo_and_revision_exist(repo_type, repo_id, revision) + if not repo_and_revision_exist: + _raise_file_not_found(path, err) + else: + _raise_file_not_found(path, err) + else: + repo_id = path + path_in_repo = "" + if "@" in path: + repo_id, revision_in_path = path.split("@", 1) + revision = _align_revision_in_path_with_revision(unquote(revision_in_path), revision) + else: + revision_in_path = None + repo_and_revision_exist, _ = self._repo_and_revision_exist(repo_type, repo_id, revision) + if not repo_and_revision_exist: + raise NotImplementedError("Access to repositories lists is not implemented.") + + revision = revision if revision is not None else constants.DEFAULT_REVISION + return HfFileSystemResolvedPath(repo_type, repo_id, revision, path_in_repo, _raw_revision=revision_in_path) + + def invalidate_cache(self, path: Optional[str] = None) -> None: + """ + Clear the cache for a given path. + + For more details, refer to [fsspec documentation](https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.spec.AbstractFileSystem.invalidate_cache). + + Args: + path (`str`, *optional*): + Path to clear from cache. If not provided, clear the entire cache. + + """ + if not path: + self.dircache.clear() + self._repo_and_revision_exists_cache.clear() + else: + resolved_path = self.resolve_path(path) + path = resolved_path.unresolve() + while path: + self.dircache.pop(path, None) + path = self._parent(path) + + # Only clear repo cache if path is to repo root + if not resolved_path.path_in_repo: + self._repo_and_revision_exists_cache.pop((resolved_path.repo_type, resolved_path.repo_id, None), None) + self._repo_and_revision_exists_cache.pop( + (resolved_path.repo_type, resolved_path.repo_id, resolved_path.revision), None + ) + + def _open( + self, + path: str, + mode: str = "rb", + revision: Optional[str] = None, + block_size: Optional[int] = None, + **kwargs, + ) -> "HfFileSystemFile": + if "a" in mode: + raise NotImplementedError("Appending to remote files is not yet supported.") + if block_size == 0: + return HfFileSystemStreamFile(self, path, mode=mode, revision=revision, block_size=block_size, **kwargs) + else: + return HfFileSystemFile(self, path, mode=mode, revision=revision, block_size=block_size, **kwargs) + + def _rm(self, path: str, revision: Optional[str] = None, **kwargs) -> None: + resolved_path = self.resolve_path(path, revision=revision) + self._api.delete_file( + path_in_repo=resolved_path.path_in_repo, + repo_id=resolved_path.repo_id, + token=self.token, + repo_type=resolved_path.repo_type, + revision=resolved_path.revision, + commit_message=kwargs.get("commit_message"), + commit_description=kwargs.get("commit_description"), + ) + self.invalidate_cache(path=resolved_path.unresolve()) + + def rm( + self, + path: str, + recursive: bool = False, + maxdepth: Optional[int] = None, + revision: Optional[str] = None, + **kwargs, + ) -> None: + """ + Delete files from a repository. + + For more details, refer to [fsspec documentation](https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.spec.AbstractFileSystem.rm). + + + + Note: When possible, use `HfApi.delete_file()` for better performance. + + + + Args: + path (`str`): + Path to delete. + recursive (`bool`, *optional*): + If True, delete directory and all its contents. Defaults to False. + maxdepth (`int`, *optional*): + Maximum number of subdirectories to visit when deleting recursively. + revision (`str`, *optional*): + The git revision to delete from. + + """ + resolved_path = self.resolve_path(path, revision=revision) + paths = self.expand_path(path, recursive=recursive, maxdepth=maxdepth, revision=revision) + paths_in_repo = [self.resolve_path(path).path_in_repo for path in paths if not self.isdir(path)] + operations = [CommitOperationDelete(path_in_repo=path_in_repo) for path_in_repo in paths_in_repo] + commit_message = f"Delete {path} " + commit_message += "recursively " if recursive else "" + commit_message += f"up to depth {maxdepth} " if maxdepth is not None else "" + # TODO: use `commit_description` to list all the deleted paths? + self._api.create_commit( + repo_id=resolved_path.repo_id, + repo_type=resolved_path.repo_type, + token=self.token, + operations=operations, + revision=resolved_path.revision, + commit_message=kwargs.get("commit_message", commit_message), + commit_description=kwargs.get("commit_description"), + ) + self.invalidate_cache(path=resolved_path.unresolve()) + + def ls( + self, path: str, detail: bool = True, refresh: bool = False, revision: Optional[str] = None, **kwargs + ) -> List[Union[str, Dict[str, Any]]]: + """ + List the contents of a directory. + + For more details, refer to [fsspec documentation](https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.spec.AbstractFileSystem.ls). + + + + Note: When possible, use `HfApi.list_repo_tree()` for better performance. + + + + Args: + path (`str`): + Path to the directory. + detail (`bool`, *optional*): + If True, returns a list of dictionaries containing file information. If False, + returns a list of file paths. Defaults to True. + refresh (`bool`, *optional*): + If True, bypass the cache and fetch the latest data. Defaults to False. + revision (`str`, *optional*): + The git revision to list from. + + Returns: + `List[Union[str, Dict[str, Any]]]`: List of file paths (if detail=False) or list of file information + dictionaries (if detail=True). + """ + resolved_path = self.resolve_path(path, revision=revision) + path = resolved_path.unresolve() + try: + out = self._ls_tree(path, refresh=refresh, revision=revision, **kwargs) + except EntryNotFoundError: + # Path could be a file + if not resolved_path.path_in_repo: + _raise_file_not_found(path, None) + out = self._ls_tree(self._parent(path), refresh=refresh, revision=revision, **kwargs) + out = [o for o in out if o["name"] == path] + if len(out) == 0: + _raise_file_not_found(path, None) + return out if detail else [o["name"] for o in out] + + def _ls_tree( + self, + path: str, + recursive: bool = False, + refresh: bool = False, + revision: Optional[str] = None, + expand_info: bool = False, + maxdepth: Optional[int] = None, + ): + resolved_path = self.resolve_path(path, revision=revision) + path = resolved_path.unresolve() + root_path = HfFileSystemResolvedPath( + resolved_path.repo_type, + resolved_path.repo_id, + resolved_path.revision, + path_in_repo="", + _raw_revision=resolved_path._raw_revision, + ).unresolve() + + out = [] + if path in self.dircache and not refresh: + cached_path_infos = self.dircache[path] + out.extend(cached_path_infos) + dirs_not_in_dircache = [] + if recursive: + # Use BFS to traverse the cache and build the "recursive "output + # (The Hub uses a so-called "tree first" strategy for the tree endpoint but we sort the output to follow the spec so the result is (eventually) the same) + depth = 2 + dirs_to_visit = deque( + [(depth, path_info) for path_info in cached_path_infos if path_info["type"] == "directory"] + ) + while dirs_to_visit: + depth, dir_info = dirs_to_visit.popleft() + if maxdepth is None or depth <= maxdepth: + if dir_info["name"] not in self.dircache: + dirs_not_in_dircache.append(dir_info["name"]) + else: + cached_path_infos = self.dircache[dir_info["name"]] + out.extend(cached_path_infos) + dirs_to_visit.extend( + [ + (depth + 1, path_info) + for path_info in cached_path_infos + if path_info["type"] == "directory" + ] + ) + + dirs_not_expanded = [] + if expand_info: + # Check if there are directories with non-expanded entries + dirs_not_expanded = [self._parent(o["name"]) for o in out if o["last_commit"] is None] + + if (recursive and dirs_not_in_dircache) or (expand_info and dirs_not_expanded): + # If the dircache is incomplete, find the common path of the missing and non-expanded entries + # and extend the output with the result of `_ls_tree(common_path, recursive=True)` + common_prefix = os.path.commonprefix(dirs_not_in_dircache + dirs_not_expanded) + # Get the parent directory if the common prefix itself is not a directory + common_path = ( + common_prefix.rstrip("/") + if common_prefix.endswith("/") + or common_prefix == root_path + or common_prefix in chain(dirs_not_in_dircache, dirs_not_expanded) + else self._parent(common_prefix) + ) + if maxdepth is not None: + common_path_depth = common_path[len(path) :].count("/") + maxdepth -= common_path_depth + out = [o for o in out if not o["name"].startswith(common_path + "/")] + for cached_path in self.dircache: + if cached_path.startswith(common_path + "/"): + self.dircache.pop(cached_path, None) + self.dircache.pop(common_path, None) + out.extend( + self._ls_tree( + common_path, + recursive=recursive, + refresh=True, + revision=revision, + expand_info=expand_info, + maxdepth=maxdepth, + ) + ) + else: + tree = self._api.list_repo_tree( + resolved_path.repo_id, + resolved_path.path_in_repo, + recursive=recursive, + expand=expand_info, + revision=resolved_path.revision, + repo_type=resolved_path.repo_type, + ) + for path_info in tree: + cache_path = root_path + "/" + path_info.path + if isinstance(path_info, RepoFile): + cache_path_info = { + "name": cache_path, + "size": path_info.size, + "type": "file", + "blob_id": path_info.blob_id, + "lfs": path_info.lfs, + "last_commit": path_info.last_commit, + "security": path_info.security, + } + else: + cache_path_info = { + "name": cache_path, + "size": 0, + "type": "directory", + "tree_id": path_info.tree_id, + "last_commit": path_info.last_commit, + } + parent_path = self._parent(cache_path_info["name"]) + self.dircache.setdefault(parent_path, []).append(cache_path_info) + depth = cache_path[len(path) :].count("/") + if maxdepth is None or depth <= maxdepth: + out.append(cache_path_info) + return out + + def walk(self, path: str, *args, **kwargs) -> Iterator[Tuple[str, List[str], List[str]]]: + """ + Return all files below the given path. + + For more details, refer to [fsspec documentation](https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.spec.AbstractFileSystem.walk). + + Args: + path (`str`): + Root path to list files from. + + Returns: + `Iterator[Tuple[str, List[str], List[str]]]`: An iterator of (path, list of directory names, list of file names) tuples. + """ + path = self.resolve_path(path, revision=kwargs.get("revision")).unresolve() + yield from super().walk(path, *args, **kwargs) + + def glob(self, path: str, **kwargs) -> List[str]: + """ + Find files by glob-matching. + + For more details, refer to [fsspec documentation](https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.spec.AbstractFileSystem.glob). + + Args: + path (`str`): + Path pattern to match. + + Returns: + `List[str]`: List of paths matching the pattern. + """ + path = self.resolve_path(path, revision=kwargs.get("revision")).unresolve() + return super().glob(path, **kwargs) + + def find( + self, + path: str, + maxdepth: Optional[int] = None, + withdirs: bool = False, + detail: bool = False, + refresh: bool = False, + revision: Optional[str] = None, + **kwargs, + ) -> Union[List[str], Dict[str, Dict[str, Any]]]: + """ + List all files below path. + + For more details, refer to [fsspec documentation](https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.spec.AbstractFileSystem.find). + + Args: + path (`str`): + Root path to list files from. + maxdepth (`int`, *optional*): + Maximum depth to descend into subdirectories. + withdirs (`bool`, *optional*): + Include directory paths in the output. Defaults to False. + detail (`bool`, *optional*): + If True, returns a dict mapping paths to file information. Defaults to False. + refresh (`bool`, *optional*): + If True, bypass the cache and fetch the latest data. Defaults to False. + revision (`str`, *optional*): + The git revision to list from. + + Returns: + `Union[List[str], Dict[str, Dict[str, Any]]]`: List of paths or dict of file information. + """ + if maxdepth is not None and maxdepth < 1: + raise ValueError("maxdepth must be at least 1") + resolved_path = self.resolve_path(path, revision=revision) + path = resolved_path.unresolve() + try: + out = self._ls_tree( + path, recursive=True, refresh=refresh, revision=resolved_path.revision, maxdepth=maxdepth, **kwargs + ) + except EntryNotFoundError: + # Path could be a file + try: + if self.info(path, revision=revision, **kwargs)["type"] == "file": + out = {path: {}} + else: + out = {} + except FileNotFoundError: + out = {} + else: + if not withdirs: + out = [o for o in out if o["type"] != "directory"] + else: + # If `withdirs=True`, include the directory itself to be consistent with the spec + path_info = self.info(path, revision=resolved_path.revision, **kwargs) + out = [path_info] + out if path_info["type"] == "directory" else out + out = {o["name"]: o for o in out} + names = sorted(out) + if not detail: + return names + else: + return {name: out[name] for name in names} + + def cp_file(self, path1: str, path2: str, revision: Optional[str] = None, **kwargs) -> None: + """ + Copy a file within or between repositories. + + + + Note: When possible, use `HfApi.upload_file()` for better performance. + + + + Args: + path1 (`str`): + Source path to copy from. + path2 (`str`): + Destination path to copy to. + revision (`str`, *optional*): + The git revision to copy from. + + """ + resolved_path1 = self.resolve_path(path1, revision=revision) + resolved_path2 = self.resolve_path(path2, revision=revision) + + same_repo = ( + resolved_path1.repo_type == resolved_path2.repo_type and resolved_path1.repo_id == resolved_path2.repo_id + ) + + if same_repo: + commit_message = f"Copy {path1} to {path2}" + self._api.create_commit( + repo_id=resolved_path1.repo_id, + repo_type=resolved_path1.repo_type, + revision=resolved_path2.revision, + commit_message=kwargs.get("commit_message", commit_message), + commit_description=kwargs.get("commit_description", ""), + operations=[ + CommitOperationCopy( + src_path_in_repo=resolved_path1.path_in_repo, + path_in_repo=resolved_path2.path_in_repo, + src_revision=resolved_path1.revision, + ) + ], + ) + else: + with self.open(path1, "rb", revision=resolved_path1.revision) as f: + content = f.read() + commit_message = f"Copy {path1} to {path2}" + self._api.upload_file( + path_or_fileobj=content, + path_in_repo=resolved_path2.path_in_repo, + repo_id=resolved_path2.repo_id, + token=self.token, + repo_type=resolved_path2.repo_type, + revision=resolved_path2.revision, + commit_message=kwargs.get("commit_message", commit_message), + commit_description=kwargs.get("commit_description"), + ) + self.invalidate_cache(path=resolved_path1.unresolve()) + self.invalidate_cache(path=resolved_path2.unresolve()) + + def modified(self, path: str, **kwargs) -> datetime: + """ + Get the last modified time of a file. + + For more details, refer to [fsspec documentation](https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.spec.AbstractFileSystem.modified). + + Args: + path (`str`): + Path to the file. + + Returns: + `datetime`: Last commit date of the file. + """ + info = self.info(path, **{**kwargs, "expand_info": True}) + return info["last_commit"]["date"] + + def info(self, path: str, refresh: bool = False, revision: Optional[str] = None, **kwargs) -> Dict[str, Any]: + """ + Get information about a file or directory. + + For more details, refer to [fsspec documentation](https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.spec.AbstractFileSystem.info). + + + + Note: When possible, use `HfApi.get_paths_info()` or `HfApi.repo_info()` for better performance. + + + + Args: + path (`str`): + Path to get info for. + refresh (`bool`, *optional*): + If True, bypass the cache and fetch the latest data. Defaults to False. + revision (`str`, *optional*): + The git revision to get info from. + + Returns: + `Dict[str, Any]`: Dictionary containing file information (type, size, commit info, etc.). + + """ + resolved_path = self.resolve_path(path, revision=revision) + path = resolved_path.unresolve() + expand_info = kwargs.get( + "expand_info", False + ) # don't expose it as a parameter in the public API to follow the spec + if not resolved_path.path_in_repo: + # Path is the root directory + out = { + "name": path, + "size": 0, + "type": "directory", + "last_commit": None, + } + if expand_info: + last_commit = self._api.list_repo_commits( + resolved_path.repo_id, repo_type=resolved_path.repo_type, revision=resolved_path.revision + )[-1] + out = { + **out, + "tree_id": None, # TODO: tree_id of the root directory? + "last_commit": LastCommitInfo( + oid=last_commit.commit_id, title=last_commit.title, date=last_commit.created_at + ), + } + else: + out = None + parent_path = self._parent(path) + if not expand_info and parent_path not in self.dircache: + # Fill the cache with cheap call + self.ls(parent_path) + if parent_path in self.dircache: + # Check if the path is in the cache + out1 = [o for o in self.dircache[parent_path] if o["name"] == path] + if not out1: + _raise_file_not_found(path, None) + out = out1[0] + if refresh or out is None or (expand_info and out and out["last_commit"] is None): + paths_info = self._api.get_paths_info( + resolved_path.repo_id, + resolved_path.path_in_repo, + expand=expand_info, + revision=resolved_path.revision, + repo_type=resolved_path.repo_type, + ) + if not paths_info: + _raise_file_not_found(path, None) + path_info = paths_info[0] + root_path = HfFileSystemResolvedPath( + resolved_path.repo_type, + resolved_path.repo_id, + resolved_path.revision, + path_in_repo="", + _raw_revision=resolved_path._raw_revision, + ).unresolve() + if isinstance(path_info, RepoFile): + out = { + "name": root_path + "/" + path_info.path, + "size": path_info.size, + "type": "file", + "blob_id": path_info.blob_id, + "lfs": path_info.lfs, + "last_commit": path_info.last_commit, + "security": path_info.security, + } + else: + out = { + "name": root_path + "/" + path_info.path, + "size": 0, + "type": "directory", + "tree_id": path_info.tree_id, + "last_commit": path_info.last_commit, + } + if not expand_info: + out = {k: out[k] for k in ["name", "size", "type"]} + assert out is not None + return out + + def exists(self, path, **kwargs): + """ + Check if a file exists. + + For more details, refer to [fsspec documentation](https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.spec.AbstractFileSystem.exists). + + + + Note: When possible, use `HfApi.file_exists()` for better performance. + + + + Args: + path (`str`): + Path to check. + + Returns: + `bool`: True if file exists, False otherwise. + """ + try: + if kwargs.get("refresh", False): + self.invalidate_cache(path) + + self.info(path, **kwargs) + return True + except: # noqa: E722 + return False + + def isdir(self, path): + """ + Check if a path is a directory. + + For more details, refer to [fsspec documentation](https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.spec.AbstractFileSystem.isdir). + + Args: + path (`str`): + Path to check. + + Returns: + `bool`: True if path is a directory, False otherwise. + """ + try: + return self.info(path)["type"] == "directory" + except OSError: + return False + + def isfile(self, path): + """ + Check if a path is a file. + + For more details, refer to [fsspec documentation](https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.spec.AbstractFileSystem.isfile). + + Args: + path (`str`): + Path to check. + + Returns: + `bool`: True if path is a file, False otherwise. + """ + try: + return self.info(path)["type"] == "file" + except: # noqa: E722 + return False + + def url(self, path: str) -> str: + """ + Get the HTTP URL of the given path. + + Args: + path (`str`): + Path to get URL for. + + Returns: + `str`: HTTP URL to access the file or directory on the Hub. + """ + resolved_path = self.resolve_path(path) + url = hf_hub_url( + resolved_path.repo_id, + resolved_path.path_in_repo, + repo_type=resolved_path.repo_type, + revision=resolved_path.revision, + endpoint=self.endpoint, + ) + if self.isdir(path): + url = url.replace("/resolve/", "/tree/", 1) + return url + + def get_file(self, rpath, lpath, callback=_DEFAULT_CALLBACK, outfile=None, **kwargs) -> None: + """ + Copy single remote file to local. + + + + Note: When possible, use `HfApi.hf_hub_download()` for better performance. + + + + Args: + rpath (`str`): + Remote path to download from. + lpath (`str`): + Local path to download to. + callback (`Callback`, *optional*): + Optional callback to track download progress. Defaults to no callback. + outfile (`IO`, *optional*): + Optional file-like object to write to. If provided, `lpath` is ignored. + + """ + revision = kwargs.get("revision") + unhandled_kwargs = set(kwargs.keys()) - {"revision"} + if not isinstance(callback, (NoOpCallback, TqdmCallback)) or len(unhandled_kwargs) > 0: + # for now, let's not handle custom callbacks + # and let's not handle custom kwargs + return super().get_file(rpath, lpath, callback=callback, outfile=outfile, **kwargs) + + # Taken from https://github.com/fsspec/filesystem_spec/blob/47b445ae4c284a82dd15e0287b1ffc410e8fc470/fsspec/spec.py#L883 + if isfilelike(lpath): + outfile = lpath + elif self.isdir(rpath): + os.makedirs(lpath, exist_ok=True) + return None + + if isinstance(lpath, (str, Path)): # otherwise, let's assume it's a file-like object + os.makedirs(os.path.dirname(lpath), exist_ok=True) + + # Open file if not already open + close_file = False + if outfile is None: + outfile = open(lpath, "wb") + close_file = True + initial_pos = outfile.tell() + + # Custom implementation of `get_file` to use `http_get`. + resolve_remote_path = self.resolve_path(rpath, revision=revision) + expected_size = self.info(rpath, revision=revision)["size"] + callback.set_size(expected_size) + try: + http_get( + url=hf_hub_url( + repo_id=resolve_remote_path.repo_id, + revision=resolve_remote_path.revision, + filename=resolve_remote_path.path_in_repo, + repo_type=resolve_remote_path.repo_type, + endpoint=self.endpoint, + ), + temp_file=outfile, # type: ignore[arg-type] + displayed_filename=rpath, + expected_size=expected_size, + resume_size=0, + headers=self._api._build_hf_headers(), + _tqdm_bar=callback.tqdm if isinstance(callback, TqdmCallback) else None, + ) + outfile.seek(initial_pos) + finally: + # Close file only if we opened it ourselves + if close_file: + outfile.close() + + @property + def transaction(self): + """A context within which files are committed together upon exit + + Requires the file class to implement `.commit()` and `.discard()` + for the normal and exception cases. + """ + # Taken from https://github.com/fsspec/filesystem_spec/blob/3fbb6fee33b46cccb015607630843dea049d3243/fsspec/spec.py#L231 + # See https://github.com/huggingface/huggingface_hub/issues/1733 + raise NotImplementedError("Transactional commits are not supported.") + + def start_transaction(self): + """Begin write transaction for deferring files, non-context version""" + # Taken from https://github.com/fsspec/filesystem_spec/blob/3fbb6fee33b46cccb015607630843dea049d3243/fsspec/spec.py#L241 + # See https://github.com/huggingface/huggingface_hub/issues/1733 + raise NotImplementedError("Transactional commits are not supported.") + + +class HfFileSystemFile(fsspec.spec.AbstractBufferedFile): + def __init__(self, fs: HfFileSystem, path: str, revision: Optional[str] = None, **kwargs): + try: + self.resolved_path = fs.resolve_path(path, revision=revision) + except FileNotFoundError as e: + if "w" in kwargs.get("mode", ""): + raise FileNotFoundError( + f"{e}.\nMake sure the repository and revision exist before writing data." + ) from e + raise + super().__init__(fs, self.resolved_path.unresolve(), **kwargs) + self.fs: HfFileSystem + + def __del__(self): + if not hasattr(self, "resolved_path"): + # Means that the constructor failed. Nothing to do. + return + return super().__del__() + + def _fetch_range(self, start: int, end: int) -> bytes: + headers = { + "range": f"bytes={start}-{end - 1}", + **self.fs._api._build_hf_headers(), + } + url = hf_hub_url( + repo_id=self.resolved_path.repo_id, + revision=self.resolved_path.revision, + filename=self.resolved_path.path_in_repo, + repo_type=self.resolved_path.repo_type, + endpoint=self.fs.endpoint, + ) + r = http_backoff("GET", url, headers=headers, timeout=constants.HF_HUB_DOWNLOAD_TIMEOUT) + hf_raise_for_status(r) + return r.content + + def _initiate_upload(self) -> None: + self.temp_file = tempfile.NamedTemporaryFile(prefix="hffs-", delete=False) + + def _upload_chunk(self, final: bool = False) -> None: + self.buffer.seek(0) + block = self.buffer.read() + self.temp_file.write(block) + if final: + self.temp_file.close() + self.fs._api.upload_file( + path_or_fileobj=self.temp_file.name, + path_in_repo=self.resolved_path.path_in_repo, + repo_id=self.resolved_path.repo_id, + token=self.fs.token, + repo_type=self.resolved_path.repo_type, + revision=self.resolved_path.revision, + commit_message=self.kwargs.get("commit_message"), + commit_description=self.kwargs.get("commit_description"), + ) + os.remove(self.temp_file.name) + self.fs.invalidate_cache( + path=self.resolved_path.unresolve(), + ) + + def read(self, length=-1): + """Read remote file. + + If `length` is not provided or is -1, the entire file is downloaded and read. On POSIX systems and if + `hf_transfer` is not enabled, the file is loaded in memory directly. Otherwise, the file is downloaded to a + temporary file and read from there. + """ + if self.mode == "rb" and (length is None or length == -1) and self.loc == 0: + with self.fs.open(self.path, "rb", block_size=0) as f: # block_size=0 enables fast streaming + out = f.read() + self.loc += len(out) + return out + return super().read(length) + + def url(self) -> str: + return self.fs.url(self.path) + + +class HfFileSystemStreamFile(fsspec.spec.AbstractBufferedFile): + def __init__( + self, + fs: HfFileSystem, + path: str, + mode: str = "rb", + revision: Optional[str] = None, + block_size: int = 0, + cache_type: str = "none", + **kwargs, + ): + if block_size != 0: + raise ValueError(f"HfFileSystemStreamFile only supports block_size=0 but got {block_size}") + if cache_type != "none": + raise ValueError(f"HfFileSystemStreamFile only supports cache_type='none' but got {cache_type}") + if "w" in mode: + raise ValueError(f"HfFileSystemStreamFile only supports reading but got mode='{mode}'") + try: + self.resolved_path = fs.resolve_path(path, revision=revision) + except FileNotFoundError as e: + if "w" in kwargs.get("mode", ""): + raise FileNotFoundError( + f"{e}.\nMake sure the repository and revision exist before writing data." + ) from e + # avoid an unnecessary .info() call to instantiate .details + self.details = {"name": self.resolved_path.unresolve(), "size": None} + super().__init__( + fs, self.resolved_path.unresolve(), mode=mode, block_size=block_size, cache_type=cache_type, **kwargs + ) + self.response: Optional[Response] = None + self.fs: HfFileSystem + + def seek(self, loc: int, whence: int = 0): + if loc == 0 and whence == 1: + return + if loc == self.loc and whence == 0: + return + raise ValueError("Cannot seek streaming HF file") + + def read(self, length: int = -1): + read_args = (length,) if length >= 0 else () + if self.response is None: + url = hf_hub_url( + repo_id=self.resolved_path.repo_id, + revision=self.resolved_path.revision, + filename=self.resolved_path.path_in_repo, + repo_type=self.resolved_path.repo_type, + endpoint=self.fs.endpoint, + ) + self.response = http_backoff( + "GET", + url, + headers=self.fs._api._build_hf_headers(), + stream=True, + timeout=constants.HF_HUB_DOWNLOAD_TIMEOUT, + ) + hf_raise_for_status(self.response) + try: + self.response.raw.decode_content = True + out = self.response.raw.read(*read_args) + except Exception: + self.response.close() + + # Retry by recreating the connection + url = hf_hub_url( + repo_id=self.resolved_path.repo_id, + revision=self.resolved_path.revision, + filename=self.resolved_path.path_in_repo, + repo_type=self.resolved_path.repo_type, + endpoint=self.fs.endpoint, + ) + self.response = http_backoff( + "GET", + url, + headers={"Range": "bytes=%d-" % self.loc, **self.fs._api._build_hf_headers()}, + stream=True, + timeout=constants.HF_HUB_DOWNLOAD_TIMEOUT, + ) + hf_raise_for_status(self.response) + try: + self.response.raw.decode_content = True + out = self.response.raw.read(*read_args) + except Exception: + self.response.close() + raise + self.loc += len(out) + return out + + def url(self) -> str: + return self.fs.url(self.path) + + def __del__(self): + if not hasattr(self, "resolved_path"): + # Means that the constructor failed. Nothing to do. + return + return super().__del__() + + def __reduce__(self): + return reopen, (self.fs, self.path, self.mode, self.blocksize, self.cache.name) + + +def safe_revision(revision: str) -> str: + return revision if SPECIAL_REFS_REVISION_REGEX.match(revision) else safe_quote(revision) + + +def safe_quote(s: str) -> str: + return quote(s, safe="") + + +def _raise_file_not_found(path: str, err: Optional[Exception]) -> NoReturn: + msg = path + if isinstance(err, RepositoryNotFoundError): + msg = f"{path} (repository not found)" + elif isinstance(err, RevisionNotFoundError): + msg = f"{path} (revision not found)" + elif isinstance(err, HFValidationError): + msg = f"{path} (invalid repository id)" + raise FileNotFoundError(msg) from err + + +def reopen(fs: HfFileSystem, path: str, mode: str, block_size: int, cache_type: str): + return fs.open(path, mode=mode, block_size=block_size, cache_type=cache_type) diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/hub_mixin.py b/.venv/lib/python3.12/site-packages/huggingface_hub/hub_mixin.py new file mode 100644 index 0000000000000000000000000000000000000000..9fa702ceda97318a817cb1a325223e26a78e2710 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/hub_mixin.py @@ -0,0 +1,853 @@ +import inspect +import json +import os +from dataclasses import Field, asdict, dataclass, is_dataclass +from pathlib import Path +from typing import Any, Callable, ClassVar, Dict, List, Optional, Protocol, Tuple, Type, TypeVar, Union + +import packaging.version + +from . import constants +from .errors import EntryNotFoundError, HfHubHTTPError +from .file_download import hf_hub_download +from .hf_api import HfApi +from .repocard import ModelCard, ModelCardData +from .utils import ( + SoftTemporaryDirectory, + is_jsonable, + is_safetensors_available, + is_simple_optional_type, + is_torch_available, + logging, + unwrap_simple_optional_type, + validate_hf_hub_args, +) + + +if is_torch_available(): + import torch # type: ignore + +if is_safetensors_available(): + import safetensors + from safetensors.torch import load_model as load_model_as_safetensor + from safetensors.torch import save_model as save_model_as_safetensor + + +logger = logging.get_logger(__name__) + + +# Type alias for dataclass instances, copied from https://github.com/python/typeshed/blob/9f28171658b9ca6c32a7cb93fbb99fc92b17858b/stdlib/_typeshed/__init__.pyi#L349 +class DataclassInstance(Protocol): + __dataclass_fields__: ClassVar[Dict[str, Field]] + + +# Generic variable that is either ModelHubMixin or a subclass thereof +T = TypeVar("T", bound="ModelHubMixin") +# Generic variable to represent an args type +ARGS_T = TypeVar("ARGS_T") +ENCODER_T = Callable[[ARGS_T], Any] +DECODER_T = Callable[[Any], ARGS_T] +CODER_T = Tuple[ENCODER_T, DECODER_T] + + +DEFAULT_MODEL_CARD = """ +--- +# For reference on model card metadata, see the spec: https://github.com/huggingface/hub-docs/blob/main/modelcard.md?plain=1 +# Doc / guide: https://huggingface.co/docs/hub/model-cards +{{ card_data }} +--- + +This model has been pushed to the Hub using the [PytorchModelHubMixin](https://huggingface.co/docs/huggingface_hub/package_reference/mixins#huggingface_hub.PyTorchModelHubMixin) integration: +- Code: {{ repo_url | default("[More Information Needed]", true) }} +- Paper: {{ paper_url | default("[More Information Needed]", true) }} +- Docs: {{ docs_url | default("[More Information Needed]", true) }} +""" + + +@dataclass +class MixinInfo: + model_card_template: str + model_card_data: ModelCardData + docs_url: Optional[str] = None + paper_url: Optional[str] = None + repo_url: Optional[str] = None + + +class ModelHubMixin: + """ + A generic mixin to integrate ANY machine learning framework with the Hub. + + To integrate your framework, your model class must inherit from this class. Custom logic for saving/loading models + have to be overwritten in [`_from_pretrained`] and [`_save_pretrained`]. [`PyTorchModelHubMixin`] is a good example + of mixin integration with the Hub. Check out our [integration guide](../guides/integrations) for more instructions. + + When inheriting from [`ModelHubMixin`], you can define class-level attributes. These attributes are not passed to + `__init__` but to the class definition itself. This is useful to define metadata about the library integrating + [`ModelHubMixin`]. + + For more details on how to integrate the mixin with your library, checkout the [integration guide](../guides/integrations). + + Args: + repo_url (`str`, *optional*): + URL of the library repository. Used to generate model card. + paper_url (`str`, *optional*): + URL of the library paper. Used to generate model card. + docs_url (`str`, *optional*): + URL of the library documentation. Used to generate model card. + model_card_template (`str`, *optional*): + Template of the model card. Used to generate model card. Defaults to a generic template. + language (`str` or `List[str]`, *optional*): + Language supported by the library. Used to generate model card. + library_name (`str`, *optional*): + Name of the library integrating ModelHubMixin. Used to generate model card. + license (`str`, *optional*): + License of the library integrating ModelHubMixin. Used to generate model card. + E.g: "apache-2.0" + license_name (`str`, *optional*): + Name of the library integrating ModelHubMixin. Used to generate model card. + Only used if `license` is set to `other`. + E.g: "coqui-public-model-license". + license_link (`str`, *optional*): + URL to the license of the library integrating ModelHubMixin. Used to generate model card. + Only used if `license` is set to `other` and `license_name` is set. + E.g: "https://coqui.ai/cpml". + pipeline_tag (`str`, *optional*): + Tag of the pipeline. Used to generate model card. E.g. "text-classification". + tags (`List[str]`, *optional*): + Tags to be added to the model card. Used to generate model card. E.g. ["computer-vision"] + coders (`Dict[Type, Tuple[Callable, Callable]]`, *optional*): + Dictionary of custom types and their encoders/decoders. Used to encode/decode arguments that are not + jsonable by default. E.g dataclasses, argparse.Namespace, OmegaConf, etc. + + Example: + + ```python + >>> from huggingface_hub import ModelHubMixin + + # Inherit from ModelHubMixin + >>> class MyCustomModel( + ... ModelHubMixin, + ... library_name="my-library", + ... tags=["computer-vision"], + ... repo_url="https://github.com/huggingface/my-cool-library", + ... paper_url="https://arxiv.org/abs/2304.12244", + ... docs_url="https://huggingface.co/docs/my-cool-library", + ... # ^ optional metadata to generate model card + ... ): + ... def __init__(self, size: int = 512, device: str = "cpu"): + ... # define how to initialize your model + ... super().__init__() + ... ... + ... + ... def _save_pretrained(self, save_directory: Path) -> None: + ... # define how to serialize your model + ... ... + ... + ... @classmethod + ... def from_pretrained( + ... cls: Type[T], + ... pretrained_model_name_or_path: Union[str, Path], + ... *, + ... force_download: bool = False, + ... resume_download: Optional[bool] = None, + ... proxies: Optional[Dict] = None, + ... token: Optional[Union[str, bool]] = None, + ... cache_dir: Optional[Union[str, Path]] = None, + ... local_files_only: bool = False, + ... revision: Optional[str] = None, + ... **model_kwargs, + ... ) -> T: + ... # define how to deserialize your model + ... ... + + >>> model = MyCustomModel(size=256, device="gpu") + + # Save model weights to local directory + >>> model.save_pretrained("my-awesome-model") + + # Push model weights to the Hub + >>> model.push_to_hub("my-awesome-model") + + # Download and initialize weights from the Hub + >>> reloaded_model = MyCustomModel.from_pretrained("username/my-awesome-model") + >>> reloaded_model.size + 256 + + # Model card has been correctly populated + >>> from huggingface_hub import ModelCard + >>> card = ModelCard.load("username/my-awesome-model") + >>> card.data.tags + ["x-custom-tag", "pytorch_model_hub_mixin", "model_hub_mixin"] + >>> card.data.library_name + "my-library" + ``` + """ + + _hub_mixin_config: Optional[Union[dict, DataclassInstance]] = None + # ^ optional config attribute automatically set in `from_pretrained` + _hub_mixin_info: MixinInfo + # ^ information about the library integrating ModelHubMixin (used to generate model card) + _hub_mixin_inject_config: bool # whether `_from_pretrained` expects `config` or not + _hub_mixin_init_parameters: Dict[str, inspect.Parameter] # __init__ parameters + _hub_mixin_jsonable_default_values: Dict[str, Any] # default values for __init__ parameters + _hub_mixin_jsonable_custom_types: Tuple[Type, ...] # custom types that can be encoded/decoded + _hub_mixin_coders: Dict[Type, CODER_T] # encoders/decoders for custom types + # ^ internal values to handle config + + def __init_subclass__( + cls, + *, + # Generic info for model card + repo_url: Optional[str] = None, + paper_url: Optional[str] = None, + docs_url: Optional[str] = None, + # Model card template + model_card_template: str = DEFAULT_MODEL_CARD, + # Model card metadata + language: Optional[List[str]] = None, + library_name: Optional[str] = None, + license: Optional[str] = None, + license_name: Optional[str] = None, + license_link: Optional[str] = None, + pipeline_tag: Optional[str] = None, + tags: Optional[List[str]] = None, + # How to encode/decode arguments with custom type into a JSON config? + coders: Optional[ + Dict[Type, CODER_T] + # Key is a type. + # Value is a tuple (encoder, decoder). + # Example: {MyCustomType: (lambda x: x.value, lambda data: MyCustomType(data))} + ] = None, + ) -> None: + """Inspect __init__ signature only once when subclassing + handle modelcard.""" + super().__init_subclass__() + + # Will be reused when creating modelcard + tags = tags or [] + tags.append("model_hub_mixin") + + # Initialize MixinInfo if not existent + info = MixinInfo(model_card_template=model_card_template, model_card_data=ModelCardData()) + + # If parent class has a MixinInfo, inherit from it as a copy + if hasattr(cls, "_hub_mixin_info"): + # Inherit model card template from parent class if not explicitly set + if model_card_template == DEFAULT_MODEL_CARD: + info.model_card_template = cls._hub_mixin_info.model_card_template + + # Inherit from parent model card data + info.model_card_data = ModelCardData(**cls._hub_mixin_info.model_card_data.to_dict()) + + # Inherit other info + info.docs_url = cls._hub_mixin_info.docs_url + info.paper_url = cls._hub_mixin_info.paper_url + info.repo_url = cls._hub_mixin_info.repo_url + cls._hub_mixin_info = info + + # Update MixinInfo with metadata + if model_card_template is not None and model_card_template != DEFAULT_MODEL_CARD: + info.model_card_template = model_card_template + if repo_url is not None: + info.repo_url = repo_url + if paper_url is not None: + info.paper_url = paper_url + if docs_url is not None: + info.docs_url = docs_url + if language is not None: + info.model_card_data.language = language + if library_name is not None: + info.model_card_data.library_name = library_name + if license is not None: + info.model_card_data.license = license + if license_name is not None: + info.model_card_data.license_name = license_name + if license_link is not None: + info.model_card_data.license_link = license_link + if pipeline_tag is not None: + info.model_card_data.pipeline_tag = pipeline_tag + if tags is not None: + normalized_tags = list(tags) + if info.model_card_data.tags is not None: + info.model_card_data.tags.extend(normalized_tags) + else: + info.model_card_data.tags = normalized_tags + + if info.model_card_data.tags is not None: + info.model_card_data.tags = sorted(set(info.model_card_data.tags)) + + # Handle encoders/decoders for args + cls._hub_mixin_coders = coders or {} + cls._hub_mixin_jsonable_custom_types = tuple(cls._hub_mixin_coders.keys()) + + # Inspect __init__ signature to handle config + cls._hub_mixin_init_parameters = dict(inspect.signature(cls.__init__).parameters) + cls._hub_mixin_jsonable_default_values = { + param.name: cls._encode_arg(param.default) + for param in cls._hub_mixin_init_parameters.values() + if param.default is not inspect.Parameter.empty and cls._is_jsonable(param.default) + } + cls._hub_mixin_inject_config = "config" in inspect.signature(cls._from_pretrained).parameters + + def __new__(cls: Type[T], *args, **kwargs) -> T: + """Create a new instance of the class and handle config. + + 3 cases: + - If `self._hub_mixin_config` is already set, do nothing. + - If `config` is passed as a dataclass, set it as `self._hub_mixin_config`. + - Otherwise, build `self._hub_mixin_config` from default values and passed values. + """ + instance = super().__new__(cls) + + # If `config` is already set, return early + if instance._hub_mixin_config is not None: + return instance + + # Infer passed values + passed_values = { + **{ + key: value + for key, value in zip( + # [1:] to skip `self` parameter + list(cls._hub_mixin_init_parameters)[1:], + args, + ) + }, + **kwargs, + } + + # If config passed as dataclass => set it and return early + if is_dataclass(passed_values.get("config")): + instance._hub_mixin_config = passed_values["config"] + return instance + + # Otherwise, build config from default + passed values + init_config = { + # default values + **cls._hub_mixin_jsonable_default_values, + # passed values + **{ + key: cls._encode_arg(value) # Encode custom types as jsonable value + for key, value in passed_values.items() + if instance._is_jsonable(value) # Only if jsonable or we have a custom encoder + }, + } + passed_config = init_config.pop("config", {}) + + # Populate `init_config` with provided config + if isinstance(passed_config, dict): + init_config.update(passed_config) + + # Set `config` attribute and return + if init_config != {}: + instance._hub_mixin_config = init_config + return instance + + @classmethod + def _is_jsonable(cls, value: Any) -> bool: + """Check if a value is JSON serializable.""" + if is_dataclass(value): + return True + if isinstance(value, cls._hub_mixin_jsonable_custom_types): + return True + return is_jsonable(value) + + @classmethod + def _encode_arg(cls, arg: Any) -> Any: + """Encode an argument into a JSON serializable format.""" + if is_dataclass(arg): + return asdict(arg) # type: ignore[arg-type] + for type_, (encoder, _) in cls._hub_mixin_coders.items(): + if isinstance(arg, type_): + if arg is None: + return None + return encoder(arg) + return arg + + @classmethod + def _decode_arg(cls, expected_type: Type[ARGS_T], value: Any) -> Optional[ARGS_T]: + """Decode a JSON serializable value into an argument.""" + if is_simple_optional_type(expected_type): + if value is None: + return None + expected_type = unwrap_simple_optional_type(expected_type) + # Dataclass => handle it + if is_dataclass(expected_type): + return _load_dataclass(expected_type, value) # type: ignore[return-value] + # Otherwise => check custom decoders + for type_, (_, decoder) in cls._hub_mixin_coders.items(): + if inspect.isclass(expected_type) and issubclass(expected_type, type_): + return decoder(value) + # Otherwise => don't decode + return value + + def save_pretrained( + self, + save_directory: Union[str, Path], + *, + config: Optional[Union[dict, DataclassInstance]] = None, + repo_id: Optional[str] = None, + push_to_hub: bool = False, + model_card_kwargs: Optional[Dict[str, Any]] = None, + **push_to_hub_kwargs, + ) -> Optional[str]: + """ + Save weights in local directory. + + Args: + save_directory (`str` or `Path`): + Path to directory in which the model weights and configuration will be saved. + config (`dict` or `DataclassInstance`, *optional*): + Model configuration specified as a key/value dictionary or a dataclass instance. + push_to_hub (`bool`, *optional*, defaults to `False`): + Whether or not to push your model to the Huggingface Hub after saving it. + repo_id (`str`, *optional*): + ID of your repository on the Hub. Used only if `push_to_hub=True`. Will default to the folder name if + not provided. + model_card_kwargs (`Dict[str, Any]`, *optional*): + Additional arguments passed to the model card template to customize the model card. + push_to_hub_kwargs: + Additional key word arguments passed along to the [`~ModelHubMixin.push_to_hub`] method. + Returns: + `str` or `None`: url of the commit on the Hub if `push_to_hub=True`, `None` otherwise. + """ + save_directory = Path(save_directory) + save_directory.mkdir(parents=True, exist_ok=True) + + # Remove config.json if already exists. After `_save_pretrained` we don't want to overwrite config.json + # as it might have been saved by the custom `_save_pretrained` already. However we do want to overwrite + # an existing config.json if it was not saved by `_save_pretrained`. + config_path = save_directory / constants.CONFIG_NAME + config_path.unlink(missing_ok=True) + + # save model weights/files (framework-specific) + self._save_pretrained(save_directory) + + # save config (if provided and if not serialized yet in `_save_pretrained`) + if config is None: + config = self._hub_mixin_config + if config is not None: + if is_dataclass(config): + config = asdict(config) # type: ignore[arg-type] + if not config_path.exists(): + config_str = json.dumps(config, sort_keys=True, indent=2) + config_path.write_text(config_str) + + # save model card + model_card_path = save_directory / "README.md" + model_card_kwargs = model_card_kwargs if model_card_kwargs is not None else {} + if not model_card_path.exists(): # do not overwrite if already exists + self.generate_model_card(**model_card_kwargs).save(save_directory / "README.md") + + # push to the Hub if required + if push_to_hub: + kwargs = push_to_hub_kwargs.copy() # soft-copy to avoid mutating input + if config is not None: # kwarg for `push_to_hub` + kwargs["config"] = config + if repo_id is None: + repo_id = save_directory.name # Defaults to `save_directory` name + return self.push_to_hub(repo_id=repo_id, model_card_kwargs=model_card_kwargs, **kwargs) + return None + + def _save_pretrained(self, save_directory: Path) -> None: + """ + Overwrite this method in subclass to define how to save your model. + Check out our [integration guide](../guides/integrations) for instructions. + + Args: + save_directory (`str` or `Path`): + Path to directory in which the model weights and configuration will be saved. + """ + raise NotImplementedError + + @classmethod + @validate_hf_hub_args + def from_pretrained( + cls: Type[T], + pretrained_model_name_or_path: Union[str, Path], + *, + force_download: bool = False, + resume_download: Optional[bool] = None, + proxies: Optional[Dict] = None, + token: Optional[Union[str, bool]] = None, + cache_dir: Optional[Union[str, Path]] = None, + local_files_only: bool = False, + revision: Optional[str] = None, + **model_kwargs, + ) -> T: + """ + Download a model from the Huggingface Hub and instantiate it. + + Args: + pretrained_model_name_or_path (`str`, `Path`): + - Either the `model_id` (string) of a model hosted on the Hub, e.g. `bigscience/bloom`. + - Or a path to a `directory` containing model weights saved using + [`~transformers.PreTrainedModel.save_pretrained`], e.g., `../path/to/my_model_directory/`. + revision (`str`, *optional*): + Revision of the model on the Hub. Can be a branch name, a git tag or any commit id. + Defaults to the latest commit on `main` branch. + force_download (`bool`, *optional*, defaults to `False`): + Whether to force (re-)downloading the model weights and configuration files from the Hub, overriding + the existing cache. + proxies (`Dict[str, str]`, *optional*): + A dictionary of proxy servers to use by protocol or endpoint, e.g., `{'http': 'foo.bar:3128', + 'http://hostname': 'foo.bar:4012'}`. The proxies are used on every request. + token (`str` or `bool`, *optional*): + The token to use as HTTP bearer authorization for remote files. By default, it will use the token + cached when running `hf auth login`. + cache_dir (`str`, `Path`, *optional*): + Path to the folder where cached files are stored. + local_files_only (`bool`, *optional*, defaults to `False`): + If `True`, avoid downloading the file and return the path to the local cached file if it exists. + model_kwargs (`Dict`, *optional*): + Additional kwargs to pass to the model during initialization. + """ + model_id = str(pretrained_model_name_or_path) + config_file: Optional[str] = None + if os.path.isdir(model_id): + if constants.CONFIG_NAME in os.listdir(model_id): + config_file = os.path.join(model_id, constants.CONFIG_NAME) + else: + logger.warning(f"{constants.CONFIG_NAME} not found in {Path(model_id).resolve()}") + else: + try: + config_file = hf_hub_download( + repo_id=model_id, + filename=constants.CONFIG_NAME, + revision=revision, + cache_dir=cache_dir, + force_download=force_download, + proxies=proxies, + resume_download=resume_download, + token=token, + local_files_only=local_files_only, + ) + except HfHubHTTPError as e: + logger.info(f"{constants.CONFIG_NAME} not found on the HuggingFace Hub: {str(e)}") + + # Read config + config = None + if config_file is not None: + with open(config_file, "r", encoding="utf-8") as f: + config = json.load(f) + + # Decode custom types in config + for key, value in config.items(): + if key in cls._hub_mixin_init_parameters: + expected_type = cls._hub_mixin_init_parameters[key].annotation + if expected_type is not inspect.Parameter.empty: + config[key] = cls._decode_arg(expected_type, value) + + # Populate model_kwargs from config + for param in cls._hub_mixin_init_parameters.values(): + if param.name not in model_kwargs and param.name in config: + model_kwargs[param.name] = config[param.name] + + # Check if `config` argument was passed at init + if "config" in cls._hub_mixin_init_parameters and "config" not in model_kwargs: + # Decode `config` argument if it was passed + config_annotation = cls._hub_mixin_init_parameters["config"].annotation + config = cls._decode_arg(config_annotation, config) + + # Forward config to model initialization + model_kwargs["config"] = config + + # Inject config if `**kwargs` are expected + if is_dataclass(cls): + for key in cls.__dataclass_fields__: + if key not in model_kwargs and key in config: + model_kwargs[key] = config[key] + elif any(param.kind == inspect.Parameter.VAR_KEYWORD for param in cls._hub_mixin_init_parameters.values()): + for key, value in config.items(): + if key not in model_kwargs: + model_kwargs[key] = value + + # Finally, also inject if `_from_pretrained` expects it + if cls._hub_mixin_inject_config and "config" not in model_kwargs: + model_kwargs["config"] = config + + instance = cls._from_pretrained( + model_id=str(model_id), + revision=revision, + cache_dir=cache_dir, + force_download=force_download, + proxies=proxies, + resume_download=resume_download, + local_files_only=local_files_only, + token=token, + **model_kwargs, + ) + + # Implicitly set the config as instance attribute if not already set by the class + # This way `config` will be available when calling `save_pretrained` or `push_to_hub`. + if config is not None and (getattr(instance, "_hub_mixin_config", None) in (None, {})): + instance._hub_mixin_config = config + + return instance + + @classmethod + def _from_pretrained( + cls: Type[T], + *, + model_id: str, + revision: Optional[str], + cache_dir: Optional[Union[str, Path]], + force_download: bool, + proxies: Optional[Dict], + resume_download: Optional[bool], + local_files_only: bool, + token: Optional[Union[str, bool]], + **model_kwargs, + ) -> T: + """Overwrite this method in subclass to define how to load your model from pretrained. + + Use [`hf_hub_download`] or [`snapshot_download`] to download files from the Hub before loading them. Most + args taken as input can be directly passed to those 2 methods. If needed, you can add more arguments to this + method using "model_kwargs". For example [`PyTorchModelHubMixin._from_pretrained`] takes as input a `map_location` + parameter to set on which device the model should be loaded. + + Check out our [integration guide](../guides/integrations) for more instructions. + + Args: + model_id (`str`): + ID of the model to load from the Huggingface Hub (e.g. `bigscience/bloom`). + revision (`str`, *optional*): + Revision of the model on the Hub. Can be a branch name, a git tag or any commit id. Defaults to the + latest commit on `main` branch. + force_download (`bool`, *optional*, defaults to `False`): + Whether to force (re-)downloading the model weights and configuration files from the Hub, overriding + the existing cache. + proxies (`Dict[str, str]`, *optional*): + A dictionary of proxy servers to use by protocol or endpoint (e.g., `{'http': 'foo.bar:3128', + 'http://hostname': 'foo.bar:4012'}`). + token (`str` or `bool`, *optional*): + The token to use as HTTP bearer authorization for remote files. By default, it will use the token + cached when running `hf auth login`. + cache_dir (`str`, `Path`, *optional*): + Path to the folder where cached files are stored. + local_files_only (`bool`, *optional*, defaults to `False`): + If `True`, avoid downloading the file and return the path to the local cached file if it exists. + model_kwargs: + Additional keyword arguments passed along to the [`~ModelHubMixin._from_pretrained`] method. + """ + raise NotImplementedError + + @validate_hf_hub_args + def push_to_hub( + self, + repo_id: str, + *, + config: Optional[Union[dict, DataclassInstance]] = None, + commit_message: str = "Push model using huggingface_hub.", + private: Optional[bool] = None, + token: Optional[str] = None, + branch: Optional[str] = None, + create_pr: Optional[bool] = None, + allow_patterns: Optional[Union[List[str], str]] = None, + ignore_patterns: Optional[Union[List[str], str]] = None, + delete_patterns: Optional[Union[List[str], str]] = None, + model_card_kwargs: Optional[Dict[str, Any]] = None, + ) -> str: + """ + Upload model checkpoint to the Hub. + + Use `allow_patterns` and `ignore_patterns` to precisely filter which files should be pushed to the hub. Use + `delete_patterns` to delete existing remote files in the same commit. See [`upload_folder`] reference for more + details. + + Args: + repo_id (`str`): + ID of the repository to push to (example: `"username/my-model"`). + config (`dict` or `DataclassInstance`, *optional*): + Model configuration specified as a key/value dictionary or a dataclass instance. + commit_message (`str`, *optional*): + Message to commit while pushing. + private (`bool`, *optional*): + Whether the repository created should be private. + If `None` (default), the repo will be public unless the organization's default is private. + token (`str`, *optional*): + The token to use as HTTP bearer authorization for remote files. By default, it will use the token + cached when running `hf auth login`. + branch (`str`, *optional*): + The git branch on which to push the model. This defaults to `"main"`. + create_pr (`boolean`, *optional*): + Whether or not to create a Pull Request from `branch` with that commit. Defaults to `False`. + allow_patterns (`List[str]` or `str`, *optional*): + If provided, only files matching at least one pattern are pushed. + ignore_patterns (`List[str]` or `str`, *optional*): + If provided, files matching any of the patterns are not pushed. + delete_patterns (`List[str]` or `str`, *optional*): + If provided, remote files matching any of the patterns will be deleted from the repo. + model_card_kwargs (`Dict[str, Any]`, *optional*): + Additional arguments passed to the model card template to customize the model card. + + Returns: + The url of the commit of your model in the given repository. + """ + api = HfApi(token=token) + repo_id = api.create_repo(repo_id=repo_id, private=private, exist_ok=True).repo_id + + # Push the files to the repo in a single commit + with SoftTemporaryDirectory() as tmp: + saved_path = Path(tmp) / repo_id + self.save_pretrained(saved_path, config=config, model_card_kwargs=model_card_kwargs) + return api.upload_folder( + repo_id=repo_id, + repo_type="model", + folder_path=saved_path, + commit_message=commit_message, + revision=branch, + create_pr=create_pr, + allow_patterns=allow_patterns, + ignore_patterns=ignore_patterns, + delete_patterns=delete_patterns, + ) + + def generate_model_card(self, *args, **kwargs) -> ModelCard: + card = ModelCard.from_template( + card_data=self._hub_mixin_info.model_card_data, + template_str=self._hub_mixin_info.model_card_template, + repo_url=self._hub_mixin_info.repo_url, + paper_url=self._hub_mixin_info.paper_url, + docs_url=self._hub_mixin_info.docs_url, + **kwargs, + ) + return card + + +class PyTorchModelHubMixin(ModelHubMixin): + """ + Implementation of [`ModelHubMixin`] to provide model Hub upload/download capabilities to PyTorch models. The model + is set in evaluation mode by default using `model.eval()` (dropout modules are deactivated). To train the model, + you should first set it back in training mode with `model.train()`. + + See [`ModelHubMixin`] for more details on how to use the mixin. + + Example: + + ```python + >>> import torch + >>> import torch.nn as nn + >>> from huggingface_hub import PyTorchModelHubMixin + + >>> class MyModel( + ... nn.Module, + ... PyTorchModelHubMixin, + ... library_name="keras-nlp", + ... repo_url="https://github.com/keras-team/keras-nlp", + ... paper_url="https://arxiv.org/abs/2304.12244", + ... docs_url="https://keras.io/keras_nlp/", + ... # ^ optional metadata to generate model card + ... ): + ... def __init__(self, hidden_size: int = 512, vocab_size: int = 30000, output_size: int = 4): + ... super().__init__() + ... self.param = nn.Parameter(torch.rand(hidden_size, vocab_size)) + ... self.linear = nn.Linear(output_size, vocab_size) + + ... def forward(self, x): + ... return self.linear(x + self.param) + >>> model = MyModel(hidden_size=256) + + # Save model weights to local directory + >>> model.save_pretrained("my-awesome-model") + + # Push model weights to the Hub + >>> model.push_to_hub("my-awesome-model") + + # Download and initialize weights from the Hub + >>> model = MyModel.from_pretrained("username/my-awesome-model") + >>> model.hidden_size + 256 + ``` + """ + + def __init_subclass__(cls, *args, tags: Optional[List[str]] = None, **kwargs) -> None: + tags = tags or [] + tags.append("pytorch_model_hub_mixin") + kwargs["tags"] = tags + return super().__init_subclass__(*args, **kwargs) + + def _save_pretrained(self, save_directory: Path) -> None: + """Save weights from a Pytorch model to a local directory.""" + model_to_save = self.module if hasattr(self, "module") else self # type: ignore + save_model_as_safetensor(model_to_save, str(save_directory / constants.SAFETENSORS_SINGLE_FILE)) # type: ignore [arg-type] + + @classmethod + def _from_pretrained( + cls, + *, + model_id: str, + revision: Optional[str], + cache_dir: Optional[Union[str, Path]], + force_download: bool, + proxies: Optional[Dict], + resume_download: Optional[bool], + local_files_only: bool, + token: Union[str, bool, None], + map_location: str = "cpu", + strict: bool = False, + **model_kwargs, + ): + """Load Pytorch pretrained weights and return the loaded model.""" + model = cls(**model_kwargs) + if os.path.isdir(model_id): + print("Loading weights from local directory") + model_file = os.path.join(model_id, constants.SAFETENSORS_SINGLE_FILE) + return cls._load_as_safetensor(model, model_file, map_location, strict) + else: + try: + model_file = hf_hub_download( + repo_id=model_id, + filename=constants.SAFETENSORS_SINGLE_FILE, + revision=revision, + cache_dir=cache_dir, + force_download=force_download, + proxies=proxies, + resume_download=resume_download, + token=token, + local_files_only=local_files_only, + ) + return cls._load_as_safetensor(model, model_file, map_location, strict) + except EntryNotFoundError: + model_file = hf_hub_download( + repo_id=model_id, + filename=constants.PYTORCH_WEIGHTS_NAME, + revision=revision, + cache_dir=cache_dir, + force_download=force_download, + proxies=proxies, + resume_download=resume_download, + token=token, + local_files_only=local_files_only, + ) + return cls._load_as_pickle(model, model_file, map_location, strict) + + @classmethod + def _load_as_pickle(cls, model: T, model_file: str, map_location: str, strict: bool) -> T: + state_dict = torch.load(model_file, map_location=torch.device(map_location), weights_only=True) + model.load_state_dict(state_dict, strict=strict) # type: ignore + model.eval() # type: ignore + return model + + @classmethod + def _load_as_safetensor(cls, model: T, model_file: str, map_location: str, strict: bool) -> T: + if packaging.version.parse(safetensors.__version__) < packaging.version.parse("0.4.3"): # type: ignore [attr-defined] + load_model_as_safetensor(model, model_file, strict=strict) # type: ignore [arg-type] + if map_location != "cpu": + logger.warning( + "Loading model weights on other devices than 'cpu' is not supported natively in your version of safetensors." + " This means that the model is loaded on 'cpu' first and then copied to the device." + " This leads to a slower loading time." + " Please update safetensors to version 0.4.3 or above for improved performance." + ) + model.to(map_location) # type: ignore [attr-defined] + else: + safetensors.torch.load_model(model, model_file, strict=strict, device=map_location) # type: ignore [arg-type] + return model + + +def _load_dataclass(datacls: Type[DataclassInstance], data: dict) -> DataclassInstance: + """Load a dataclass instance from a dictionary. + + Fields not expected by the dataclass are ignored. + """ + return datacls(**{k: v for k, v in data.items() if k in datacls.__dataclass_fields__}) diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/inference_api.py b/.venv/lib/python3.12/site-packages/huggingface_hub/inference_api.py new file mode 100644 index 0000000000000000000000000000000000000000..f895fcc61c3867838b013ecd3f6789cbc010b5b3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/inference_api.py @@ -0,0 +1,217 @@ +import io +from typing import Any, Dict, List, Optional, Union + +from . import constants +from .hf_api import HfApi +from .utils import build_hf_headers, get_session, is_pillow_available, logging, validate_hf_hub_args +from .utils._deprecation import _deprecate_method + + +logger = logging.get_logger(__name__) + + +ALL_TASKS = [ + # NLP + "text-classification", + "token-classification", + "table-question-answering", + "question-answering", + "zero-shot-classification", + "translation", + "summarization", + "conversational", + "feature-extraction", + "text-generation", + "text2text-generation", + "fill-mask", + "sentence-similarity", + # Audio + "text-to-speech", + "automatic-speech-recognition", + "audio-to-audio", + "audio-classification", + "voice-activity-detection", + # Computer vision + "image-classification", + "object-detection", + "image-segmentation", + "text-to-image", + "image-to-image", + # Others + "tabular-classification", + "tabular-regression", +] + + +class InferenceApi: + """Client to configure requests and make calls to the HuggingFace Inference API. + + Example: + + ```python + >>> from huggingface_hub.inference_api import InferenceApi + + >>> # Mask-fill example + >>> inference = InferenceApi("bert-base-uncased") + >>> inference(inputs="The goal of life is [MASK].") + [{'sequence': 'the goal of life is life.', 'score': 0.10933292657136917, 'token': 2166, 'token_str': 'life'}] + + >>> # Question Answering example + >>> inference = InferenceApi("deepset/roberta-base-squad2") + >>> inputs = { + ... "question": "What's my name?", + ... "context": "My name is Clara and I live in Berkeley.", + ... } + >>> inference(inputs) + {'score': 0.9326569437980652, 'start': 11, 'end': 16, 'answer': 'Clara'} + + >>> # Zero-shot example + >>> inference = InferenceApi("typeform/distilbert-base-uncased-mnli") + >>> inputs = "Hi, I recently bought a device from your company but it is not working as advertised and I would like to get reimbursed!" + >>> params = {"candidate_labels": ["refund", "legal", "faq"]} + >>> inference(inputs, params) + {'sequence': 'Hi, I recently bought a device from your company but it is not working as advertised and I would like to get reimbursed!', 'labels': ['refund', 'faq', 'legal'], 'scores': [0.9378499388694763, 0.04914155602455139, 0.013008488342165947]} + + >>> # Overriding configured task + >>> inference = InferenceApi("bert-base-uncased", task="feature-extraction") + + >>> # Text-to-image + >>> inference = InferenceApi("stabilityai/stable-diffusion-2-1") + >>> inference("cat") + + + >>> # Return as raw response to parse the output yourself + >>> inference = InferenceApi("mio/amadeus") + >>> response = inference("hello world", raw_response=True) + >>> response.headers + {"Content-Type": "audio/flac", ...} + >>> response.content # raw bytes from server + b'(...)' + ``` + """ + + @validate_hf_hub_args + @_deprecate_method( + version="1.0", + message=( + "`InferenceApi` client is deprecated in favor of the more feature-complete `InferenceClient`. Check out" + " this guide to learn how to convert your script to use it:" + " https://huggingface.co/docs/huggingface_hub/guides/inference#legacy-inferenceapi-client." + ), + ) + def __init__( + self, + repo_id: str, + task: Optional[str] = None, + token: Optional[str] = None, + gpu: bool = False, + ): + """Inits headers and API call information. + + Args: + repo_id (``str``): + Id of repository (e.g. `user/bert-base-uncased`). + task (``str``, `optional`, defaults ``None``): + Whether to force a task instead of using task specified in the + repository. + token (`str`, `optional`): + The API token to use as HTTP bearer authorization. This is not + the authentication token. You can find the token in + https://huggingface.co/settings/token. Alternatively, you can + find both your organizations and personal API tokens using + `HfApi().whoami(token)`. + gpu (`bool`, `optional`, defaults `False`): + Whether to use GPU instead of CPU for inference(requires Startup + plan at least). + """ + self.options = {"wait_for_model": True, "use_gpu": gpu} + self.headers = build_hf_headers(token=token) + + # Configure task + model_info = HfApi(token=token).model_info(repo_id=repo_id) + if not model_info.pipeline_tag and not task: + raise ValueError( + "Task not specified in the repository. Please add it to the model card" + " using pipeline_tag" + " (https://huggingface.co/docs#how-is-a-models-type-of-inference-api-and-widget-determined)" + ) + + if task and task != model_info.pipeline_tag: + if task not in ALL_TASKS: + raise ValueError(f"Invalid task {task}. Make sure it's valid.") + + logger.warning( + "You're using a different task than the one specified in the" + " repository. Be sure to know what you're doing :)" + ) + self.task = task + else: + assert model_info.pipeline_tag is not None, "Pipeline tag cannot be None" + self.task = model_info.pipeline_tag + + self.api_url = f"{constants.INFERENCE_ENDPOINT}/pipeline/{self.task}/{repo_id}" + + def __repr__(self): + # Do not add headers to repr to avoid leaking token. + return f"InferenceAPI(api_url='{self.api_url}', task='{self.task}', options={self.options})" + + def __call__( + self, + inputs: Optional[Union[str, Dict, List[str], List[List[str]]]] = None, + params: Optional[Dict] = None, + data: Optional[bytes] = None, + raw_response: bool = False, + ) -> Any: + """Make a call to the Inference API. + + Args: + inputs (`str` or `Dict` or `List[str]` or `List[List[str]]`, *optional*): + Inputs for the prediction. + params (`Dict`, *optional*): + Additional parameters for the models. Will be sent as `parameters` in the + payload. + data (`bytes`, *optional*): + Bytes content of the request. In this case, leave `inputs` and `params` empty. + raw_response (`bool`, defaults to `False`): + If `True`, the raw `Response` object is returned. You can parse its content + as preferred. By default, the content is parsed into a more practical format + (json dictionary or PIL Image for example). + """ + # Build payload + payload: Dict[str, Any] = { + "options": self.options, + } + if inputs: + payload["inputs"] = inputs + if params: + payload["parameters"] = params + + # Make API call + response = get_session().post(self.api_url, headers=self.headers, json=payload, data=data) + + # Let the user handle the response + if raw_response: + return response + + # By default, parse the response for the user. + content_type = response.headers.get("Content-Type") or "" + if content_type.startswith("image"): + if not is_pillow_available(): + raise ImportError( + f"Task '{self.task}' returned as image but Pillow is not installed." + " Please install it (`pip install Pillow`) or pass" + " `raw_response=True` to get the raw `Response` object and parse" + " the image by yourself." + ) + + from PIL import Image + + return Image.open(io.BytesIO(response.content)) + elif content_type == "application/json": + return response.json() + else: + raise NotImplementedError( + f"{content_type} output type is not implemented yet. You can pass" + " `raw_response=True` to get the raw `Response` object and parse the" + " output by yourself." + ) diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/keras_mixin.py b/.venv/lib/python3.12/site-packages/huggingface_hub/keras_mixin.py new file mode 100644 index 0000000000000000000000000000000000000000..45d0eaf8a7737a7c5dac029f16607871e4bfccd4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/keras_mixin.py @@ -0,0 +1,500 @@ +import collections.abc as collections +import json +import os +import warnings +from functools import wraps +from pathlib import Path +from shutil import copytree +from typing import Any, Dict, List, Optional, Union + +from huggingface_hub import ModelHubMixin, snapshot_download +from huggingface_hub.utils import ( + get_tf_version, + is_graphviz_available, + is_pydot_available, + is_tf_available, + yaml_dump, +) + +from . import constants +from .hf_api import HfApi +from .utils import SoftTemporaryDirectory, logging, validate_hf_hub_args +from .utils._typing import CallableT + + +logger = logging.get_logger(__name__) + +keras = None +if is_tf_available(): + # Depending on which version of TensorFlow is installed, we need to import + # keras from the correct location. + # See https://github.com/tensorflow/tensorflow/releases/tag/v2.16.1. + # Note: saving a keras model only works with Keras<3.0. + try: + import tf_keras as keras # type: ignore + except ImportError: + import tensorflow as tf # type: ignore + + keras = tf.keras + + +def _requires_keras_2_model(fn: CallableT) -> CallableT: + # Wrapper to raise if user tries to save a Keras 3.x model + @wraps(fn) + def _inner(model, *args, **kwargs): + if not hasattr(model, "history"): # hacky way to check if model is Keras 2.x + raise NotImplementedError( + f"Cannot use '{fn.__name__}': Keras 3.x is not supported." + " Please save models manually and upload them using `upload_folder` or `hf upload`." + ) + return fn(model, *args, **kwargs) + + return _inner # type: ignore [return-value] + + +def _flatten_dict(dictionary, parent_key=""): + """Flatten a nested dictionary. + Reference: https://stackoverflow.com/a/6027615/10319735 + + Args: + dictionary (`dict`): + The nested dictionary to be flattened. + parent_key (`str`): + The parent key to be prefixed to the children keys. + Necessary for recursing over the nested dictionary. + + Returns: + The flattened dictionary. + """ + items = [] + for key, value in dictionary.items(): + new_key = f"{parent_key}.{key}" if parent_key else key + if isinstance(value, collections.MutableMapping): + items.extend( + _flatten_dict( + value, + new_key, + ).items() + ) + else: + items.append((new_key, value)) + return dict(items) + + +def _create_hyperparameter_table(model): + """Parse hyperparameter dictionary into a markdown table.""" + table = None + if model.optimizer is not None: + optimizer_params = model.optimizer.get_config() + # flatten the configuration + optimizer_params = _flatten_dict(optimizer_params) + optimizer_params["training_precision"] = keras.mixed_precision.global_policy().name + table = "| Hyperparameters | Value |\n| :-- | :-- |\n" + for key, value in optimizer_params.items(): + table += f"| {key} | {value} |\n" + return table + + +def _plot_network(model, save_directory): + keras.utils.plot_model( + model, + to_file=f"{save_directory}/model.png", + show_shapes=False, + show_dtype=False, + show_layer_names=True, + rankdir="TB", + expand_nested=False, + dpi=96, + layer_range=None, + ) + + +def _create_model_card( + model, + repo_dir: Path, + plot_model: bool = True, + metadata: Optional[dict] = None, +): + """ + Creates a model card for the repository. + + Do not overwrite an existing README.md file. + """ + readme_path = repo_dir / "README.md" + if readme_path.exists(): + return + + hyperparameters = _create_hyperparameter_table(model) + if plot_model and is_graphviz_available() and is_pydot_available(): + _plot_network(model, repo_dir) + if metadata is None: + metadata = {} + metadata["library_name"] = "keras" + model_card: str = "---\n" + model_card += yaml_dump(metadata, default_flow_style=False) + model_card += "---\n" + model_card += "\n## Model description\n\nMore information needed\n" + model_card += "\n## Intended uses & limitations\n\nMore information needed\n" + model_card += "\n## Training and evaluation data\n\nMore information needed\n" + if hyperparameters is not None: + model_card += "\n## Training procedure\n" + model_card += "\n### Training hyperparameters\n" + model_card += "\nThe following hyperparameters were used during training:\n\n" + model_card += hyperparameters + model_card += "\n" + if plot_model and os.path.exists(f"{repo_dir}/model.png"): + model_card += "\n ## Model Plot\n" + model_card += "\n
" + model_card += "\nView Model Plot\n" + path_to_plot = "./model.png" + model_card += f"\n![Model Image]({path_to_plot})\n" + model_card += "\n
" + + readme_path.write_text(model_card) + + +@_requires_keras_2_model +def save_pretrained_keras( + model, + save_directory: Union[str, Path], + config: Optional[Dict[str, Any]] = None, + include_optimizer: bool = False, + plot_model: bool = True, + tags: Optional[Union[list, str]] = None, + **model_save_kwargs, +): + """ + Saves a Keras model to save_directory in SavedModel format. Use this if + you're using the Functional or Sequential APIs. + + Args: + model (`Keras.Model`): + The [Keras + model](https://www.tensorflow.org/api_docs/python/tf/keras/Model) + you'd like to save. The model must be compiled and built. + save_directory (`str` or `Path`): + Specify directory in which you want to save the Keras model. + config (`dict`, *optional*): + Configuration object to be saved alongside the model weights. + include_optimizer(`bool`, *optional*, defaults to `False`): + Whether or not to include optimizer in serialization. + plot_model (`bool`, *optional*, defaults to `True`): + Setting this to `True` will plot the model and put it in the model + card. Requires graphviz and pydot to be installed. + tags (Union[`str`,`list`], *optional*): + List of tags that are related to model or string of a single tag. See example tags + [here](https://github.com/huggingface/hub-docs/blob/main/modelcard.md?plain=1). + model_save_kwargs(`dict`, *optional*): + model_save_kwargs will be passed to + [`tf.keras.models.save_model()`](https://www.tensorflow.org/api_docs/python/tf/keras/models/save_model). + """ + if keras is None: + raise ImportError("Called a Tensorflow-specific function but could not import it.") + + if not model.built: + raise ValueError("Model should be built before trying to save") + + save_directory = Path(save_directory) + save_directory.mkdir(parents=True, exist_ok=True) + + # saving config + if config: + if not isinstance(config, dict): + raise RuntimeError(f"Provided config to save_pretrained_keras should be a dict. Got: '{type(config)}'") + + with (save_directory / constants.CONFIG_NAME).open("w") as f: + json.dump(config, f) + + metadata = {} + if isinstance(tags, list): + metadata["tags"] = tags + elif isinstance(tags, str): + metadata["tags"] = [tags] + + task_name = model_save_kwargs.pop("task_name", None) + if task_name is not None: + warnings.warn( + "`task_name` input argument is deprecated. Pass `tags` instead.", + FutureWarning, + ) + if "tags" in metadata: + metadata["tags"].append(task_name) + else: + metadata["tags"] = [task_name] + + if model.history is not None: + if model.history.history != {}: + path = save_directory / "history.json" + if path.exists(): + warnings.warn( + "`history.json` file already exists, it will be overwritten by the history of this version.", + UserWarning, + ) + with path.open("w", encoding="utf-8") as f: + json.dump(model.history.history, f, indent=2, sort_keys=True) + + _create_model_card(model, save_directory, plot_model, metadata) + keras.models.save_model(model, save_directory, include_optimizer=include_optimizer, **model_save_kwargs) + + +def from_pretrained_keras(*args, **kwargs) -> "KerasModelHubMixin": + r""" + Instantiate a pretrained Keras model from a pre-trained model from the Hub. + The model is expected to be in `SavedModel` format. + + Args: + pretrained_model_name_or_path (`str` or `os.PathLike`): + Can be either: + - A string, the `model id` of a pretrained model hosted inside a + model repo on huggingface.co. Valid model ids can be located + at the root-level, like `bert-base-uncased`, or namespaced + under a user or organization name, like + `dbmdz/bert-base-german-cased`. + - You can add `revision` by appending `@` at the end of model_id + simply like this: `dbmdz/bert-base-german-cased@main` Revision + is the specific model version to use. It can be a branch name, + a tag name, or a commit id, since we use a git-based system + for storing models and other artifacts on huggingface.co, so + `revision` can be any identifier allowed by git. + - A path to a `directory` containing model weights saved using + [`~transformers.PreTrainedModel.save_pretrained`], e.g., + `./my_model_directory/`. + - `None` if you are both providing the configuration and state + dictionary (resp. with keyword arguments `config` and + `state_dict`). + force_download (`bool`, *optional*, defaults to `False`): + Whether to force the (re-)download of the model weights and + configuration files, overriding the cached versions if they exist. + proxies (`Dict[str, str]`, *optional*): + A dictionary of proxy servers to use by protocol or endpoint, e.g., + `{'http': 'foo.bar:3128', 'http://hostname': 'foo.bar:4012'}`. The + proxies are used on each request. + token (`str` or `bool`, *optional*): + The token to use as HTTP bearer authorization for remote files. If + `True`, will use the token generated when running `transformers-cli + login` (stored in `~/.huggingface`). + cache_dir (`Union[str, os.PathLike]`, *optional*): + Path to a directory in which a downloaded pretrained model + configuration should be cached if the standard cache should not be + used. + local_files_only(`bool`, *optional*, defaults to `False`): + Whether to only look at local files (i.e., do not try to download + the model). + model_kwargs (`Dict`, *optional*): + model_kwargs will be passed to the model during initialization + + + + Passing `token=True` is required when you want to use a private + model. + + + """ + return KerasModelHubMixin.from_pretrained(*args, **kwargs) + + +@validate_hf_hub_args +@_requires_keras_2_model +def push_to_hub_keras( + model, + repo_id: str, + *, + config: Optional[dict] = None, + commit_message: str = "Push Keras model using huggingface_hub.", + private: Optional[bool] = None, + api_endpoint: Optional[str] = None, + token: Optional[str] = None, + branch: Optional[str] = None, + create_pr: Optional[bool] = None, + allow_patterns: Optional[Union[List[str], str]] = None, + ignore_patterns: Optional[Union[List[str], str]] = None, + delete_patterns: Optional[Union[List[str], str]] = None, + log_dir: Optional[str] = None, + include_optimizer: bool = False, + tags: Optional[Union[list, str]] = None, + plot_model: bool = True, + **model_save_kwargs, +): + """ + Upload model checkpoint to the Hub. + + Use `allow_patterns` and `ignore_patterns` to precisely filter which files should be pushed to the hub. Use + `delete_patterns` to delete existing remote files in the same commit. See [`upload_folder`] reference for more + details. + + Args: + model (`Keras.Model`): + The [Keras model](`https://www.tensorflow.org/api_docs/python/tf/keras/Model`) you'd like to push to the + Hub. The model must be compiled and built. + repo_id (`str`): + ID of the repository to push to (example: `"username/my-model"`). + commit_message (`str`, *optional*, defaults to "Add Keras model"): + Message to commit while pushing. + private (`bool`, *optional*): + Whether the repository created should be private. + If `None` (default), the repo will be public unless the organization's default is private. + api_endpoint (`str`, *optional*): + The API endpoint to use when pushing the model to the hub. + token (`str`, *optional*): + The token to use as HTTP bearer authorization for remote files. If + not set, will use the token set when logging in with + `hf auth login` (stored in `~/.huggingface`). + branch (`str`, *optional*): + The git branch on which to push the model. This defaults to + the default branch as specified in your repository, which + defaults to `"main"`. + create_pr (`boolean`, *optional*): + Whether or not to create a Pull Request from `branch` with that commit. + Defaults to `False`. + config (`dict`, *optional*): + Configuration object to be saved alongside the model weights. + allow_patterns (`List[str]` or `str`, *optional*): + If provided, only files matching at least one pattern are pushed. + ignore_patterns (`List[str]` or `str`, *optional*): + If provided, files matching any of the patterns are not pushed. + delete_patterns (`List[str]` or `str`, *optional*): + If provided, remote files matching any of the patterns will be deleted from the repo. + log_dir (`str`, *optional*): + TensorBoard logging directory to be pushed. The Hub automatically + hosts and displays a TensorBoard instance if log files are included + in the repository. + include_optimizer (`bool`, *optional*, defaults to `False`): + Whether or not to include optimizer during serialization. + tags (Union[`list`, `str`], *optional*): + List of tags that are related to model or string of a single tag. See example tags + [here](https://github.com/huggingface/hub-docs/blob/main/modelcard.md?plain=1). + plot_model (`bool`, *optional*, defaults to `True`): + Setting this to `True` will plot the model and put it in the model + card. Requires graphviz and pydot to be installed. + model_save_kwargs(`dict`, *optional*): + model_save_kwargs will be passed to + [`tf.keras.models.save_model()`](https://www.tensorflow.org/api_docs/python/tf/keras/models/save_model). + + Returns: + The url of the commit of your model in the given repository. + """ + api = HfApi(endpoint=api_endpoint) + repo_id = api.create_repo(repo_id=repo_id, token=token, private=private, exist_ok=True).repo_id + + # Push the files to the repo in a single commit + with SoftTemporaryDirectory() as tmp: + saved_path = Path(tmp) / repo_id + save_pretrained_keras( + model, + saved_path, + config=config, + include_optimizer=include_optimizer, + tags=tags, + plot_model=plot_model, + **model_save_kwargs, + ) + + # If `log_dir` provided, delete remote logs and upload new ones + if log_dir is not None: + delete_patterns = ( + [] + if delete_patterns is None + else ( + [delete_patterns] # convert `delete_patterns` to a list + if isinstance(delete_patterns, str) + else delete_patterns + ) + ) + delete_patterns.append("logs/*") + copytree(log_dir, saved_path / "logs") + + return api.upload_folder( + repo_type="model", + repo_id=repo_id, + folder_path=saved_path, + commit_message=commit_message, + token=token, + revision=branch, + create_pr=create_pr, + allow_patterns=allow_patterns, + ignore_patterns=ignore_patterns, + delete_patterns=delete_patterns, + ) + + +class KerasModelHubMixin(ModelHubMixin): + """ + Implementation of [`ModelHubMixin`] to provide model Hub upload/download + capabilities to Keras models. + + + ```python + >>> import tensorflow as tf + >>> from huggingface_hub import KerasModelHubMixin + + + >>> class MyModel(tf.keras.Model, KerasModelHubMixin): + ... def __init__(self, **kwargs): + ... super().__init__() + ... self.config = kwargs.pop("config", None) + ... self.dummy_inputs = ... + ... self.layer = ... + + ... def call(self, *args): + ... return ... + + + >>> # Initialize and compile the model as you normally would + >>> model = MyModel() + >>> model.compile(...) + >>> # Build the graph by training it or passing dummy inputs + >>> _ = model(model.dummy_inputs) + >>> # Save model weights to local directory + >>> model.save_pretrained("my-awesome-model") + >>> # Push model weights to the Hub + >>> model.push_to_hub("my-awesome-model") + >>> # Download and initialize weights from the Hub + >>> model = MyModel.from_pretrained("username/super-cool-model") + ``` + """ + + def _save_pretrained(self, save_directory): + save_pretrained_keras(self, save_directory) + + @classmethod + def _from_pretrained( + cls, + model_id, + revision, + cache_dir, + force_download, + proxies, + resume_download, + local_files_only, + token, + config: Optional[Dict[str, Any]] = None, + **model_kwargs, + ): + """Here we just call [`from_pretrained_keras`] function so both the mixin and + functional APIs stay in sync. + + TODO - Some args above aren't used since we are calling + snapshot_download instead of hf_hub_download. + """ + if keras is None: + raise ImportError("Called a TensorFlow-specific function but could not import it.") + + # Root is either a local filepath matching model_id or a cached snapshot + if not os.path.isdir(model_id): + storage_folder = snapshot_download( + repo_id=model_id, + revision=revision, + cache_dir=cache_dir, + library_name="keras", + library_version=get_tf_version(), + ) + else: + storage_folder = model_id + + # TODO: change this in a future PR. We are not returning a KerasModelHubMixin instance here... + model = keras.models.load_model(storage_folder) + + # For now, we add a new attribute, config, to store the config loaded from the hub/a local dir. + model.config = config + + return model diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/lfs.py b/.venv/lib/python3.12/site-packages/huggingface_hub/lfs.py new file mode 100644 index 0000000000000000000000000000000000000000..883abddb4b49b0e800ffa9bb7d0060ec43ec57ca --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/lfs.py @@ -0,0 +1,458 @@ +# coding=utf-8 +# Copyright 2019-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Git LFS related type definitions and utilities""" + +import inspect +import io +import re +import warnings +from dataclasses import dataclass +from math import ceil +from os.path import getsize +from pathlib import Path +from typing import TYPE_CHECKING, BinaryIO, Dict, Iterable, List, Optional, Tuple, TypedDict +from urllib.parse import unquote + +from huggingface_hub import constants + +from .utils import ( + build_hf_headers, + fix_hf_endpoint_in_url, + get_session, + hf_raise_for_status, + http_backoff, + logging, + tqdm, + validate_hf_hub_args, +) +from .utils._lfs import SliceFileObj +from .utils.sha import sha256, sha_fileobj +from .utils.tqdm import is_tqdm_disabled + + +if TYPE_CHECKING: + from ._commit_api import CommitOperationAdd + +logger = logging.get_logger(__name__) + +OID_REGEX = re.compile(r"^[0-9a-f]{40}$") + +LFS_MULTIPART_UPLOAD_COMMAND = "lfs-multipart-upload" + +LFS_HEADERS = { + "Accept": "application/vnd.git-lfs+json", + "Content-Type": "application/vnd.git-lfs+json", +} + + +@dataclass +class UploadInfo: + """ + Dataclass holding required information to determine whether a blob + should be uploaded to the hub using the LFS protocol or the regular protocol + + Args: + sha256 (`bytes`): + SHA256 hash of the blob + size (`int`): + Size in bytes of the blob + sample (`bytes`): + First 512 bytes of the blob + """ + + sha256: bytes + size: int + sample: bytes + + @classmethod + def from_path(cls, path: str): + size = getsize(path) + with io.open(path, "rb") as file: + sample = file.peek(512)[:512] + sha = sha_fileobj(file) + return cls(size=size, sha256=sha, sample=sample) + + @classmethod + def from_bytes(cls, data: bytes): + sha = sha256(data).digest() + return cls(size=len(data), sample=data[:512], sha256=sha) + + @classmethod + def from_fileobj(cls, fileobj: BinaryIO): + sample = fileobj.read(512) + fileobj.seek(0, io.SEEK_SET) + sha = sha_fileobj(fileobj) + size = fileobj.tell() + fileobj.seek(0, io.SEEK_SET) + return cls(size=size, sha256=sha, sample=sample) + + +@validate_hf_hub_args +def post_lfs_batch_info( + upload_infos: Iterable[UploadInfo], + token: Optional[str], + repo_type: str, + repo_id: str, + revision: Optional[str] = None, + endpoint: Optional[str] = None, + headers: Optional[Dict[str, str]] = None, +) -> Tuple[List[dict], List[dict]]: + """ + Requests the LFS batch endpoint to retrieve upload instructions + + Learn more: https://github.com/git-lfs/git-lfs/blob/main/docs/api/batch.md + + Args: + upload_infos (`Iterable` of `UploadInfo`): + `UploadInfo` for the files that are being uploaded, typically obtained + from `CommitOperationAdd.upload_info` + repo_type (`str`): + Type of the repo to upload to: `"model"`, `"dataset"` or `"space"`. + repo_id (`str`): + A namespace (user or an organization) and a repo name separated + by a `/`. + revision (`str`, *optional*): + The git revision to upload to. + headers (`dict`, *optional*): + Additional headers to include in the request + + Returns: + `LfsBatchInfo`: 2-tuple: + - First element is the list of upload instructions from the server + - Second element is an list of errors, if any + + Raises: + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If an argument is invalid or the server response is malformed. + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError) + If the server returned an error. + """ + endpoint = endpoint if endpoint is not None else constants.ENDPOINT + url_prefix = "" + if repo_type in constants.REPO_TYPES_URL_PREFIXES: + url_prefix = constants.REPO_TYPES_URL_PREFIXES[repo_type] + batch_url = f"{endpoint}/{url_prefix}{repo_id}.git/info/lfs/objects/batch" + payload: Dict = { + "operation": "upload", + "transfers": ["basic", "multipart"], + "objects": [ + { + "oid": upload.sha256.hex(), + "size": upload.size, + } + for upload in upload_infos + ], + "hash_algo": "sha256", + } + if revision is not None: + payload["ref"] = {"name": unquote(revision)} # revision has been previously 'quoted' + + headers = { + **LFS_HEADERS, + **build_hf_headers(token=token), + **(headers or {}), + } + resp = get_session().post(batch_url, headers=headers, json=payload) + hf_raise_for_status(resp) + batch_info = resp.json() + + objects = batch_info.get("objects", None) + if not isinstance(objects, list): + raise ValueError("Malformed response from server") + + return ( + [_validate_batch_actions(obj) for obj in objects if "error" not in obj], + [_validate_batch_error(obj) for obj in objects if "error" in obj], + ) + + +class PayloadPartT(TypedDict): + partNumber: int + etag: str + + +class CompletionPayloadT(TypedDict): + """Payload that will be sent to the Hub when uploading multi-part.""" + + oid: str + parts: List[PayloadPartT] + + +def lfs_upload( + operation: "CommitOperationAdd", + lfs_batch_action: Dict, + token: Optional[str] = None, + headers: Optional[Dict[str, str]] = None, + endpoint: Optional[str] = None, +) -> None: + """ + Handles uploading a given object to the Hub with the LFS protocol. + + Can be a No-op if the content of the file is already present on the hub large file storage. + + Args: + operation (`CommitOperationAdd`): + The add operation triggering this upload. + lfs_batch_action (`dict`): + Upload instructions from the LFS batch endpoint for this object. See [`~utils.lfs.post_lfs_batch_info`] for + more details. + headers (`dict`, *optional*): + Headers to include in the request, including authentication and user agent headers. + + Raises: + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If `lfs_batch_action` is improperly formatted + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError) + If the upload resulted in an error + """ + # 0. If LFS file is already present, skip upload + _validate_batch_actions(lfs_batch_action) + actions = lfs_batch_action.get("actions") + if actions is None: + # The file was already uploaded + logger.debug(f"Content of file {operation.path_in_repo} is already present upstream - skipping upload") + return + + # 1. Validate server response (check required keys in dict) + upload_action = lfs_batch_action["actions"]["upload"] + _validate_lfs_action(upload_action) + verify_action = lfs_batch_action["actions"].get("verify") + if verify_action is not None: + _validate_lfs_action(verify_action) + + # 2. Upload file (either single part or multi-part) + header = upload_action.get("header", {}) + chunk_size = header.get("chunk_size") + upload_url = fix_hf_endpoint_in_url(upload_action["href"], endpoint=endpoint) + if chunk_size is not None: + try: + chunk_size = int(chunk_size) + except (ValueError, TypeError): + raise ValueError( + f"Malformed response from LFS batch endpoint: `chunk_size` should be an integer. Got '{chunk_size}'." + ) + _upload_multi_part(operation=operation, header=header, chunk_size=chunk_size, upload_url=upload_url) + else: + _upload_single_part(operation=operation, upload_url=upload_url) + + # 3. Verify upload went well + if verify_action is not None: + _validate_lfs_action(verify_action) + verify_url = fix_hf_endpoint_in_url(verify_action["href"], endpoint) + verify_resp = get_session().post( + verify_url, + headers=build_hf_headers(token=token, headers=headers), + json={"oid": operation.upload_info.sha256.hex(), "size": operation.upload_info.size}, + ) + hf_raise_for_status(verify_resp) + logger.debug(f"{operation.path_in_repo}: Upload successful") + + +def _validate_lfs_action(lfs_action: dict): + """validates response from the LFS batch endpoint""" + if not ( + isinstance(lfs_action.get("href"), str) + and (lfs_action.get("header") is None or isinstance(lfs_action.get("header"), dict)) + ): + raise ValueError("lfs_action is improperly formatted") + return lfs_action + + +def _validate_batch_actions(lfs_batch_actions: dict): + """validates response from the LFS batch endpoint""" + if not (isinstance(lfs_batch_actions.get("oid"), str) and isinstance(lfs_batch_actions.get("size"), int)): + raise ValueError("lfs_batch_actions is improperly formatted") + + upload_action = lfs_batch_actions.get("actions", {}).get("upload") + verify_action = lfs_batch_actions.get("actions", {}).get("verify") + if upload_action is not None: + _validate_lfs_action(upload_action) + if verify_action is not None: + _validate_lfs_action(verify_action) + return lfs_batch_actions + + +def _validate_batch_error(lfs_batch_error: dict): + """validates response from the LFS batch endpoint""" + if not (isinstance(lfs_batch_error.get("oid"), str) and isinstance(lfs_batch_error.get("size"), int)): + raise ValueError("lfs_batch_error is improperly formatted") + error_info = lfs_batch_error.get("error") + if not ( + isinstance(error_info, dict) + and isinstance(error_info.get("message"), str) + and isinstance(error_info.get("code"), int) + ): + raise ValueError("lfs_batch_error is improperly formatted") + return lfs_batch_error + + +def _upload_single_part(operation: "CommitOperationAdd", upload_url: str) -> None: + """ + Uploads `fileobj` as a single PUT HTTP request (basic LFS transfer protocol) + + Args: + upload_url (`str`): + The URL to PUT the file to. + fileobj: + The file-like object holding the data to upload. + + Returns: `requests.Response` + + Raises: + [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError) + If the upload resulted in an error. + """ + with operation.as_file(with_tqdm=True) as fileobj: + # S3 might raise a transient 500 error -> let's retry if that happens + response = http_backoff("PUT", upload_url, data=fileobj) + hf_raise_for_status(response) + + +def _upload_multi_part(operation: "CommitOperationAdd", header: Dict, chunk_size: int, upload_url: str) -> None: + """ + Uploads file using HF multipart LFS transfer protocol. + """ + # 1. Get upload URLs for each part + sorted_parts_urls = _get_sorted_parts_urls(header=header, upload_info=operation.upload_info, chunk_size=chunk_size) + + # 2. Upload parts (either with hf_transfer or in pure Python) + use_hf_transfer = constants.HF_HUB_ENABLE_HF_TRANSFER + if ( + constants.HF_HUB_ENABLE_HF_TRANSFER + and not isinstance(operation.path_or_fileobj, str) + and not isinstance(operation.path_or_fileobj, Path) + ): + warnings.warn( + "hf_transfer is enabled but does not support uploading from bytes or BinaryIO, falling back to regular" + " upload" + ) + use_hf_transfer = False + + response_headers = ( + _upload_parts_hf_transfer(operation=operation, sorted_parts_urls=sorted_parts_urls, chunk_size=chunk_size) + if use_hf_transfer + else _upload_parts_iteratively(operation=operation, sorted_parts_urls=sorted_parts_urls, chunk_size=chunk_size) + ) + + # 3. Send completion request + completion_res = get_session().post( + upload_url, + json=_get_completion_payload(response_headers, operation.upload_info.sha256.hex()), + headers=LFS_HEADERS, + ) + hf_raise_for_status(completion_res) + + +def _get_sorted_parts_urls(header: Dict, upload_info: UploadInfo, chunk_size: int) -> List[str]: + sorted_part_upload_urls = [ + upload_url + for _, upload_url in sorted( + [ + (int(part_num, 10), upload_url) + for part_num, upload_url in header.items() + if part_num.isdigit() and len(part_num) > 0 + ], + key=lambda t: t[0], + ) + ] + num_parts = len(sorted_part_upload_urls) + if num_parts != ceil(upload_info.size / chunk_size): + raise ValueError("Invalid server response to upload large LFS file") + return sorted_part_upload_urls + + +def _get_completion_payload(response_headers: List[Dict], oid: str) -> CompletionPayloadT: + parts: List[PayloadPartT] = [] + for part_number, header in enumerate(response_headers): + etag = header.get("etag") + if etag is None or etag == "": + raise ValueError(f"Invalid etag (`{etag}`) returned for part {part_number + 1}") + parts.append( + { + "partNumber": part_number + 1, + "etag": etag, + } + ) + return {"oid": oid, "parts": parts} + + +def _upload_parts_iteratively( + operation: "CommitOperationAdd", sorted_parts_urls: List[str], chunk_size: int +) -> List[Dict]: + headers = [] + with operation.as_file(with_tqdm=True) as fileobj: + for part_idx, part_upload_url in enumerate(sorted_parts_urls): + with SliceFileObj( + fileobj, + seek_from=chunk_size * part_idx, + read_limit=chunk_size, + ) as fileobj_slice: + # S3 might raise a transient 500 error -> let's retry if that happens + part_upload_res = http_backoff("PUT", part_upload_url, data=fileobj_slice) + hf_raise_for_status(part_upload_res) + headers.append(part_upload_res.headers) + return headers # type: ignore + + +def _upload_parts_hf_transfer( + operation: "CommitOperationAdd", sorted_parts_urls: List[str], chunk_size: int +) -> List[Dict]: + # Upload file using an external Rust-based package. Upload is faster but support less features (no progress bars). + try: + from hf_transfer import multipart_upload + except ImportError: + raise ValueError( + "Fast uploading using 'hf_transfer' is enabled (HF_HUB_ENABLE_HF_TRANSFER=1) but 'hf_transfer' package is" + " not available in your environment. Try `pip install hf_transfer`." + ) + + supports_callback = "callback" in inspect.signature(multipart_upload).parameters + if not supports_callback: + warnings.warn( + "You are using an outdated version of `hf_transfer`. Consider upgrading to latest version to enable progress bars using `pip install -U hf_transfer`." + ) + + total = operation.upload_info.size + desc = operation.path_in_repo + if len(desc) > 40: + desc = f"(…){desc[-40:]}" + + with tqdm( + unit="B", + unit_scale=True, + total=total, + initial=0, + desc=desc, + disable=is_tqdm_disabled(logger.getEffectiveLevel()), + name="huggingface_hub.lfs_upload", + ) as progress: + try: + output = multipart_upload( + file_path=operation.path_or_fileobj, + parts_urls=sorted_parts_urls, + chunk_size=chunk_size, + max_files=128, + parallel_failures=127, # could be removed + max_retries=5, + **({"callback": progress.update} if supports_callback else {}), + ) + except Exception as e: + raise RuntimeError( + "An error occurred while uploading using `hf_transfer`. Consider disabling HF_HUB_ENABLE_HF_TRANSFER for" + " better error handling." + ) from e + if not supports_callback: + progress.update(total) + return output diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/py.typed b/.venv/lib/python3.12/site-packages/huggingface_hub/py.typed new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/repocard.py b/.venv/lib/python3.12/site-packages/huggingface_hub/repocard.py new file mode 100644 index 0000000000000000000000000000000000000000..bb7de8c59a842ff427620a9c7561667a06fff092 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/repocard.py @@ -0,0 +1,831 @@ +import os +import re +from pathlib import Path +from typing import Any, Dict, Literal, Optional, Type, Union + +import requests +import yaml + +from huggingface_hub.file_download import hf_hub_download +from huggingface_hub.hf_api import upload_file +from huggingface_hub.repocard_data import ( + CardData, + DatasetCardData, + EvalResult, + ModelCardData, + SpaceCardData, + eval_results_to_model_index, + model_index_to_eval_results, +) +from huggingface_hub.utils import get_session, is_jinja_available, yaml_dump + +from . import constants +from .errors import EntryNotFoundError +from .utils import SoftTemporaryDirectory, logging, validate_hf_hub_args + + +logger = logging.get_logger(__name__) + + +TEMPLATE_MODELCARD_PATH = Path(__file__).parent / "templates" / "modelcard_template.md" +TEMPLATE_DATASETCARD_PATH = Path(__file__).parent / "templates" / "datasetcard_template.md" + +# exact same regex as in the Hub server. Please keep in sync. +# See https://github.com/huggingface/moon-landing/blob/main/server/lib/ViewMarkdown.ts#L18 +REGEX_YAML_BLOCK = re.compile(r"^(\s*---[\r\n]+)([\S\s]*?)([\r\n]+---(\r\n|\n|$))") + + +class RepoCard: + card_data_class = CardData + default_template_path = TEMPLATE_MODELCARD_PATH + repo_type = "model" + + def __init__(self, content: str, ignore_metadata_errors: bool = False): + """Initialize a RepoCard from string content. The content should be a + Markdown file with a YAML block at the beginning and a Markdown body. + + Args: + content (`str`): The content of the Markdown file. + + Example: + ```python + >>> from huggingface_hub.repocard import RepoCard + >>> text = ''' + ... --- + ... language: en + ... license: mit + ... --- + ... + ... # My repo + ... ''' + >>> card = RepoCard(text) + >>> card.data.to_dict() + {'language': 'en', 'license': 'mit'} + >>> card.text + '\\n# My repo\\n' + + ``` + + Raises the following error: + + - [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + when the content of the repo card metadata is not a dictionary. + + + """ + + # Set the content of the RepoCard, as well as underlying .data and .text attributes. + # See the `content` property setter for more details. + self.ignore_metadata_errors = ignore_metadata_errors + self.content = content + + @property + def content(self): + """The content of the RepoCard, including the YAML block and the Markdown body.""" + line_break = _detect_line_ending(self._content) or "\n" + return f"---{line_break}{self.data.to_yaml(line_break=line_break, original_order=self._original_order)}{line_break}---{line_break}{self.text}" + + @content.setter + def content(self, content: str): + """Set the content of the RepoCard.""" + self._content = content + + match = REGEX_YAML_BLOCK.search(content) + if match: + # Metadata found in the YAML block + yaml_block = match.group(2) + self.text = content[match.end() :] + data_dict = yaml.safe_load(yaml_block) + + if data_dict is None: + data_dict = {} + + # The YAML block's data should be a dictionary + if not isinstance(data_dict, dict): + raise ValueError("repo card metadata block should be a dict") + else: + # Model card without metadata... create empty metadata + logger.warning("Repo card metadata block was not found. Setting CardData to empty.") + data_dict = {} + self.text = content + + self.data = self.card_data_class(**data_dict, ignore_metadata_errors=self.ignore_metadata_errors) + self._original_order = list(data_dict.keys()) + + def __str__(self): + return self.content + + def save(self, filepath: Union[Path, str]): + r"""Save a RepoCard to a file. + + Args: + filepath (`Union[Path, str]`): Filepath to the markdown file to save. + + Example: + ```python + >>> from huggingface_hub.repocard import RepoCard + >>> card = RepoCard("---\nlanguage: en\n---\n# This is a test repo card") + >>> card.save("/tmp/test.md") + + ``` + """ + filepath = Path(filepath) + filepath.parent.mkdir(parents=True, exist_ok=True) + # Preserve newlines as in the existing file. + with open(filepath, mode="w", newline="", encoding="utf-8") as f: + f.write(str(self)) + + @classmethod + def load( + cls, + repo_id_or_path: Union[str, Path], + repo_type: Optional[str] = None, + token: Optional[str] = None, + ignore_metadata_errors: bool = False, + ): + """Initialize a RepoCard from a Hugging Face Hub repo's README.md or a local filepath. + + Args: + repo_id_or_path (`Union[str, Path]`): + The repo ID associated with a Hugging Face Hub repo or a local filepath. + repo_type (`str`, *optional*): + The type of Hugging Face repo to push to. Defaults to None, which will use use "model". Other options + are "dataset" and "space". Not used when loading from a local filepath. If this is called from a child + class, the default value will be the child class's `repo_type`. + token (`str`, *optional*): + Authentication token, obtained with `huggingface_hub.HfApi.login` method. Will default to the stored token. + ignore_metadata_errors (`str`): + If True, errors while parsing the metadata section will be ignored. Some information might be lost during + the process. Use it at your own risk. + + Returns: + [`huggingface_hub.repocard.RepoCard`]: The RepoCard (or subclass) initialized from the repo's + README.md file or filepath. + + Example: + ```python + >>> from huggingface_hub.repocard import RepoCard + >>> card = RepoCard.load("nateraw/food") + >>> assert card.data.tags == ["generated_from_trainer", "image-classification", "pytorch"] + + ``` + """ + + if Path(repo_id_or_path).is_file(): + card_path = Path(repo_id_or_path) + elif isinstance(repo_id_or_path, str): + card_path = Path( + hf_hub_download( + repo_id_or_path, + constants.REPOCARD_NAME, + repo_type=repo_type or cls.repo_type, + token=token, + ) + ) + else: + raise ValueError(f"Cannot load RepoCard: path not found on disk ({repo_id_or_path}).") + + # Preserve newlines in the existing file. + with card_path.open(mode="r", newline="", encoding="utf-8") as f: + return cls(f.read(), ignore_metadata_errors=ignore_metadata_errors) + + def validate(self, repo_type: Optional[str] = None): + """Validates card against Hugging Face Hub's card validation logic. + Using this function requires access to the internet, so it is only called + internally by [`huggingface_hub.repocard.RepoCard.push_to_hub`]. + + Args: + repo_type (`str`, *optional*, defaults to "model"): + The type of Hugging Face repo to push to. Options are "model", "dataset", and "space". + If this function is called from a child class, the default will be the child class's `repo_type`. + + + Raises the following errors: + + - [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + if the card fails validation checks. + - [`HTTPError`](https://requests.readthedocs.io/en/latest/api/#requests.HTTPError) + if the request to the Hub API fails for any other reason. + + + """ + + # If repo type is provided, otherwise, use the repo type of the card. + repo_type = repo_type or self.repo_type + + body = { + "repoType": repo_type, + "content": str(self), + } + headers = {"Accept": "text/plain"} + + try: + r = get_session().post("https://huggingface.co/api/validate-yaml", body, headers=headers) + r.raise_for_status() + except requests.exceptions.HTTPError as exc: + if r.status_code == 400: + raise ValueError(r.text) + else: + raise exc + + def push_to_hub( + self, + repo_id: str, + token: Optional[str] = None, + repo_type: Optional[str] = None, + commit_message: Optional[str] = None, + commit_description: Optional[str] = None, + revision: Optional[str] = None, + create_pr: Optional[bool] = None, + parent_commit: Optional[str] = None, + ): + """Push a RepoCard to a Hugging Face Hub repo. + + Args: + repo_id (`str`): + The repo ID of the Hugging Face Hub repo to push to. Example: "nateraw/food". + token (`str`, *optional*): + Authentication token, obtained with `huggingface_hub.HfApi.login` method. Will default to + the stored token. + repo_type (`str`, *optional*, defaults to "model"): + The type of Hugging Face repo to push to. Options are "model", "dataset", and "space". If this + function is called by a child class, it will default to the child class's `repo_type`. + commit_message (`str`, *optional*): + The summary / title / first line of the generated commit. + commit_description (`str`, *optional*) + The description of the generated commit. + revision (`str`, *optional*): + The git revision to commit from. Defaults to the head of the `"main"` branch. + create_pr (`bool`, *optional*): + Whether or not to create a Pull Request with this commit. Defaults to `False`. + parent_commit (`str`, *optional*): + The OID / SHA of the parent commit, as a hexadecimal string. Shorthands (7 first characters) are also supported. + If specified and `create_pr` is `False`, the commit will fail if `revision` does not point to `parent_commit`. + If specified and `create_pr` is `True`, the pull request will be created from `parent_commit`. + Specifying `parent_commit` ensures the repo has not changed before committing the changes, and can be + especially useful if the repo is updated / committed to concurrently. + Returns: + `str`: URL of the commit which updated the card metadata. + """ + + # If repo type is provided, otherwise, use the repo type of the card. + repo_type = repo_type or self.repo_type + + # Validate card before pushing to hub + self.validate(repo_type=repo_type) + + with SoftTemporaryDirectory() as tmpdir: + tmp_path = Path(tmpdir) / constants.REPOCARD_NAME + tmp_path.write_text(str(self), encoding="utf-8") + url = upload_file( + path_or_fileobj=str(tmp_path), + path_in_repo=constants.REPOCARD_NAME, + repo_id=repo_id, + token=token, + repo_type=repo_type, + commit_message=commit_message, + commit_description=commit_description, + create_pr=create_pr, + revision=revision, + parent_commit=parent_commit, + ) + return url + + @classmethod + def from_template( + cls, + card_data: CardData, + template_path: Optional[str] = None, + template_str: Optional[str] = None, + **template_kwargs, + ): + """Initialize a RepoCard from a template. By default, it uses the default template. + + Templates are Jinja2 templates that can be customized by passing keyword arguments. + + Args: + card_data (`huggingface_hub.CardData`): + A huggingface_hub.CardData instance containing the metadata you want to include in the YAML + header of the repo card on the Hugging Face Hub. + template_path (`str`, *optional*): + A path to a markdown file with optional Jinja template variables that can be filled + in with `template_kwargs`. Defaults to the default template. + + Returns: + [`huggingface_hub.repocard.RepoCard`]: A RepoCard instance with the specified card data and content from the + template. + """ + if is_jinja_available(): + import jinja2 + else: + raise ImportError( + "Using RepoCard.from_template requires Jinja2 to be installed. Please" + " install it with `pip install Jinja2`." + ) + + kwargs = card_data.to_dict().copy() + kwargs.update(template_kwargs) # Template_kwargs have priority + + if template_path is not None: + template_str = Path(template_path).read_text() + if template_str is None: + template_str = Path(cls.default_template_path).read_text() + template = jinja2.Template(template_str) + content = template.render(card_data=card_data.to_yaml(), **kwargs) + return cls(content) + + +class ModelCard(RepoCard): + card_data_class = ModelCardData + default_template_path = TEMPLATE_MODELCARD_PATH + repo_type = "model" + + @classmethod + def from_template( # type: ignore # violates Liskov property but easier to use + cls, + card_data: ModelCardData, + template_path: Optional[str] = None, + template_str: Optional[str] = None, + **template_kwargs, + ): + """Initialize a ModelCard from a template. By default, it uses the default template, which can be found here: + https://github.com/huggingface/huggingface_hub/blob/main/src/huggingface_hub/templates/modelcard_template.md + + Templates are Jinja2 templates that can be customized by passing keyword arguments. + + Args: + card_data (`huggingface_hub.ModelCardData`): + A huggingface_hub.ModelCardData instance containing the metadata you want to include in the YAML + header of the model card on the Hugging Face Hub. + template_path (`str`, *optional*): + A path to a markdown file with optional Jinja template variables that can be filled + in with `template_kwargs`. Defaults to the default template. + + Returns: + [`huggingface_hub.ModelCard`]: A ModelCard instance with the specified card data and content from the + template. + + Example: + ```python + >>> from huggingface_hub import ModelCard, ModelCardData, EvalResult + + >>> # Using the Default Template + >>> card_data = ModelCardData( + ... language='en', + ... license='mit', + ... library_name='timm', + ... tags=['image-classification', 'resnet'], + ... datasets=['beans'], + ... metrics=['accuracy'], + ... ) + >>> card = ModelCard.from_template( + ... card_data, + ... model_description='This model does x + y...' + ... ) + + >>> # Including Evaluation Results + >>> card_data = ModelCardData( + ... language='en', + ... tags=['image-classification', 'resnet'], + ... eval_results=[ + ... EvalResult( + ... task_type='image-classification', + ... dataset_type='beans', + ... dataset_name='Beans', + ... metric_type='accuracy', + ... metric_value=0.9, + ... ), + ... ], + ... model_name='my-cool-model', + ... ) + >>> card = ModelCard.from_template(card_data) + + >>> # Using a Custom Template + >>> card_data = ModelCardData( + ... language='en', + ... tags=['image-classification', 'resnet'] + ... ) + >>> card = ModelCard.from_template( + ... card_data=card_data, + ... template_path='./src/huggingface_hub/templates/modelcard_template.md', + ... custom_template_var='custom value', # will be replaced in template if it exists + ... ) + + ``` + """ + return super().from_template(card_data, template_path, template_str, **template_kwargs) + + +class DatasetCard(RepoCard): + card_data_class = DatasetCardData + default_template_path = TEMPLATE_DATASETCARD_PATH + repo_type = "dataset" + + @classmethod + def from_template( # type: ignore # violates Liskov property but easier to use + cls, + card_data: DatasetCardData, + template_path: Optional[str] = None, + template_str: Optional[str] = None, + **template_kwargs, + ): + """Initialize a DatasetCard from a template. By default, it uses the default template, which can be found here: + https://github.com/huggingface/huggingface_hub/blob/main/src/huggingface_hub/templates/datasetcard_template.md + + Templates are Jinja2 templates that can be customized by passing keyword arguments. + + Args: + card_data (`huggingface_hub.DatasetCardData`): + A huggingface_hub.DatasetCardData instance containing the metadata you want to include in the YAML + header of the dataset card on the Hugging Face Hub. + template_path (`str`, *optional*): + A path to a markdown file with optional Jinja template variables that can be filled + in with `template_kwargs`. Defaults to the default template. + + Returns: + [`huggingface_hub.DatasetCard`]: A DatasetCard instance with the specified card data and content from the + template. + + Example: + ```python + >>> from huggingface_hub import DatasetCard, DatasetCardData + + >>> # Using the Default Template + >>> card_data = DatasetCardData( + ... language='en', + ... license='mit', + ... annotations_creators='crowdsourced', + ... task_categories=['text-classification'], + ... task_ids=['sentiment-classification', 'text-scoring'], + ... multilinguality='monolingual', + ... pretty_name='My Text Classification Dataset', + ... ) + >>> card = DatasetCard.from_template( + ... card_data, + ... pretty_name=card_data.pretty_name, + ... ) + + >>> # Using a Custom Template + >>> card_data = DatasetCardData( + ... language='en', + ... license='mit', + ... ) + >>> card = DatasetCard.from_template( + ... card_data=card_data, + ... template_path='./src/huggingface_hub/templates/datasetcard_template.md', + ... custom_template_var='custom value', # will be replaced in template if it exists + ... ) + + ``` + """ + return super().from_template(card_data, template_path, template_str, **template_kwargs) + + +class SpaceCard(RepoCard): + card_data_class = SpaceCardData + default_template_path = TEMPLATE_MODELCARD_PATH + repo_type = "space" + + +def _detect_line_ending(content: str) -> Literal["\r", "\n", "\r\n", None]: # noqa: F722 + """Detect the line ending of a string. Used by RepoCard to avoid making huge diff on newlines. + + Uses same implementation as in Hub server, keep it in sync. + + Returns: + str: The detected line ending of the string. + """ + cr = content.count("\r") + lf = content.count("\n") + crlf = content.count("\r\n") + if cr + lf == 0: + return None + if crlf == cr and crlf == lf: + return "\r\n" + if cr > lf: + return "\r" + else: + return "\n" + + +def metadata_load(local_path: Union[str, Path]) -> Optional[Dict]: + content = Path(local_path).read_text() + match = REGEX_YAML_BLOCK.search(content) + if match: + yaml_block = match.group(2) + data = yaml.safe_load(yaml_block) + if data is None or isinstance(data, dict): + return data + raise ValueError("repo card metadata block should be a dict") + else: + return None + + +def metadata_save(local_path: Union[str, Path], data: Dict) -> None: + """ + Save the metadata dict in the upper YAML part Trying to preserve newlines as + in the existing file. Docs about open() with newline="" parameter: + https://docs.python.org/3/library/functions.html?highlight=open#open Does + not work with "^M" linebreaks, which are replaced by \n + """ + line_break = "\n" + content = "" + # try to detect existing newline character + if os.path.exists(local_path): + with open(local_path, "r", newline="", encoding="utf8") as readme: + content = readme.read() + if isinstance(readme.newlines, tuple): + line_break = readme.newlines[0] + elif isinstance(readme.newlines, str): + line_break = readme.newlines + + # creates a new file if it not + with open(local_path, "w", newline="", encoding="utf8") as readme: + data_yaml = yaml_dump(data, sort_keys=False, line_break=line_break) + # sort_keys: keep dict order + match = REGEX_YAML_BLOCK.search(content) + if match: + output = content[: match.start()] + f"---{line_break}{data_yaml}---{line_break}" + content[match.end() :] + else: + output = f"---{line_break}{data_yaml}---{line_break}{content}" + + readme.write(output) + readme.close() + + +def metadata_eval_result( + *, + model_pretty_name: str, + task_pretty_name: str, + task_id: str, + metrics_pretty_name: str, + metrics_id: str, + metrics_value: Any, + dataset_pretty_name: str, + dataset_id: str, + metrics_config: Optional[str] = None, + metrics_verified: bool = False, + dataset_config: Optional[str] = None, + dataset_split: Optional[str] = None, + dataset_revision: Optional[str] = None, + metrics_verification_token: Optional[str] = None, +) -> Dict: + """ + Creates a metadata dict with the result from a model evaluated on a dataset. + + Args: + model_pretty_name (`str`): + The name of the model in natural language. + task_pretty_name (`str`): + The name of a task in natural language. + task_id (`str`): + Example: automatic-speech-recognition. A task id. + metrics_pretty_name (`str`): + A name for the metric in natural language. Example: Test WER. + metrics_id (`str`): + Example: wer. A metric id from https://hf.co/metrics. + metrics_value (`Any`): + The value from the metric. Example: 20.0 or "20.0 ± 1.2". + dataset_pretty_name (`str`): + The name of the dataset in natural language. + dataset_id (`str`): + Example: common_voice. A dataset id from https://hf.co/datasets. + metrics_config (`str`, *optional*): + The name of the metric configuration used in `load_metric()`. + Example: bleurt-large-512 in `load_metric("bleurt", "bleurt-large-512")`. + metrics_verified (`bool`, *optional*, defaults to `False`): + Indicates whether the metrics originate from Hugging Face's [evaluation service](https://huggingface.co/spaces/autoevaluate/model-evaluator) or not. Automatically computed by Hugging Face, do not set. + dataset_config (`str`, *optional*): + Example: fr. The name of the dataset configuration used in `load_dataset()`. + dataset_split (`str`, *optional*): + Example: test. The name of the dataset split used in `load_dataset()`. + dataset_revision (`str`, *optional*): + Example: 5503434ddd753f426f4b38109466949a1217c2bb. The name of the dataset dataset revision + used in `load_dataset()`. + metrics_verification_token (`bool`, *optional*): + A JSON Web Token that is used to verify whether the metrics originate from Hugging Face's [evaluation service](https://huggingface.co/spaces/autoevaluate/model-evaluator) or not. + + Returns: + `dict`: a metadata dict with the result from a model evaluated on a dataset. + + Example: + ```python + >>> from huggingface_hub import metadata_eval_result + >>> results = metadata_eval_result( + ... model_pretty_name="RoBERTa fine-tuned on ReactionGIF", + ... task_pretty_name="Text Classification", + ... task_id="text-classification", + ... metrics_pretty_name="Accuracy", + ... metrics_id="accuracy", + ... metrics_value=0.2662102282047272, + ... dataset_pretty_name="ReactionJPEG", + ... dataset_id="julien-c/reactionjpeg", + ... dataset_config="default", + ... dataset_split="test", + ... ) + >>> results == { + ... 'model-index': [ + ... { + ... 'name': 'RoBERTa fine-tuned on ReactionGIF', + ... 'results': [ + ... { + ... 'task': { + ... 'type': 'text-classification', + ... 'name': 'Text Classification' + ... }, + ... 'dataset': { + ... 'name': 'ReactionJPEG', + ... 'type': 'julien-c/reactionjpeg', + ... 'config': 'default', + ... 'split': 'test' + ... }, + ... 'metrics': [ + ... { + ... 'type': 'accuracy', + ... 'value': 0.2662102282047272, + ... 'name': 'Accuracy', + ... 'verified': False + ... } + ... ] + ... } + ... ] + ... } + ... ] + ... } + True + + ``` + """ + + return { + "model-index": eval_results_to_model_index( + model_name=model_pretty_name, + eval_results=[ + EvalResult( + task_name=task_pretty_name, + task_type=task_id, + metric_name=metrics_pretty_name, + metric_type=metrics_id, + metric_value=metrics_value, + dataset_name=dataset_pretty_name, + dataset_type=dataset_id, + metric_config=metrics_config, + verified=metrics_verified, + verify_token=metrics_verification_token, + dataset_config=dataset_config, + dataset_split=dataset_split, + dataset_revision=dataset_revision, + ) + ], + ) + } + + +@validate_hf_hub_args +def metadata_update( + repo_id: str, + metadata: Dict, + *, + repo_type: Optional[str] = None, + overwrite: bool = False, + token: Optional[str] = None, + commit_message: Optional[str] = None, + commit_description: Optional[str] = None, + revision: Optional[str] = None, + create_pr: bool = False, + parent_commit: Optional[str] = None, +) -> str: + """ + Updates the metadata in the README.md of a repository on the Hugging Face Hub. + If the README.md file doesn't exist yet, a new one is created with metadata and an + the default ModelCard or DatasetCard template. For `space` repo, an error is thrown + as a Space cannot exist without a `README.md` file. + + Args: + repo_id (`str`): + The name of the repository. + metadata (`dict`): + A dictionary containing the metadata to be updated. + repo_type (`str`, *optional*): + Set to `"dataset"` or `"space"` if updating to a dataset or space, + `None` or `"model"` if updating to a model. Default is `None`. + overwrite (`bool`, *optional*, defaults to `False`): + If set to `True` an existing field can be overwritten, otherwise + attempting to overwrite an existing field will cause an error. + token (`str`, *optional*): + The Hugging Face authentication token. + commit_message (`str`, *optional*): + The summary / title / first line of the generated commit. Defaults to + `f"Update metadata with huggingface_hub"` + commit_description (`str` *optional*) + The description of the generated commit + revision (`str`, *optional*): + The git revision to commit from. Defaults to the head of the + `"main"` branch. + create_pr (`boolean`, *optional*): + Whether or not to create a Pull Request from `revision` with that commit. + Defaults to `False`. + parent_commit (`str`, *optional*): + The OID / SHA of the parent commit, as a hexadecimal string. Shorthands (7 first characters) are also supported. + If specified and `create_pr` is `False`, the commit will fail if `revision` does not point to `parent_commit`. + If specified and `create_pr` is `True`, the pull request will be created from `parent_commit`. + Specifying `parent_commit` ensures the repo has not changed before committing the changes, and can be + especially useful if the repo is updated / committed to concurrently. + Returns: + `str`: URL of the commit which updated the card metadata. + + Example: + ```python + >>> from huggingface_hub import metadata_update + >>> metadata = {'model-index': [{'name': 'RoBERTa fine-tuned on ReactionGIF', + ... 'results': [{'dataset': {'name': 'ReactionGIF', + ... 'type': 'julien-c/reactiongif'}, + ... 'metrics': [{'name': 'Recall', + ... 'type': 'recall', + ... 'value': 0.7762102282047272}], + ... 'task': {'name': 'Text Classification', + ... 'type': 'text-classification'}}]}]} + >>> url = metadata_update("hf-internal-testing/reactiongif-roberta-card", metadata) + + ``` + """ + commit_message = commit_message if commit_message is not None else "Update metadata with huggingface_hub" + + # Card class given repo_type + card_class: Type[RepoCard] + if repo_type is None or repo_type == "model": + card_class = ModelCard + elif repo_type == "dataset": + card_class = DatasetCard + elif repo_type == "space": + card_class = RepoCard + else: + raise ValueError(f"Unknown repo_type: {repo_type}") + + # Either load repo_card from the Hub or create an empty one. + # NOTE: Will not create the repo if it doesn't exist. + try: + card = card_class.load(repo_id, token=token, repo_type=repo_type) + except EntryNotFoundError: + if repo_type == "space": + raise ValueError("Cannot update metadata on a Space that doesn't contain a `README.md` file.") + + # Initialize a ModelCard or DatasetCard from default template and no data. + # Cast to the concrete expected card type to satisfy type checkers. + card = card_class.from_template(CardData()) # type: ignore[return-value] + + for key, value in metadata.items(): + if key == "model-index": + # if the new metadata doesn't include a name, either use existing one or repo name + if "name" not in value[0]: + value[0]["name"] = getattr(card, "model_name", repo_id) + model_name, new_results = model_index_to_eval_results(value) + if card.data.eval_results is None: + card.data.eval_results = new_results + card.data.model_name = model_name + else: + existing_results = card.data.eval_results + + # Iterate over new results + # Iterate over existing results + # If both results describe the same metric but value is different: + # If overwrite=True: overwrite the metric value + # Else: raise ValueError + # Else: append new result to existing ones. + for new_result in new_results: + result_found = False + for existing_result in existing_results: + if new_result.is_equal_except_value(existing_result): + if new_result != existing_result and not overwrite: + raise ValueError( + "You passed a new value for the existing metric" + f" 'name: {new_result.metric_name}, type: " + f"{new_result.metric_type}'. Set `overwrite=True`" + " to overwrite existing metrics." + ) + result_found = True + existing_result.metric_value = new_result.metric_value + if existing_result.verified is True: + existing_result.verify_token = new_result.verify_token + if not result_found: + card.data.eval_results.append(new_result) + else: + # Any metadata that is not a result metric + if card.data.get(key) is not None and not overwrite and card.data.get(key) != value: + raise ValueError( + f"You passed a new value for the existing meta data field '{key}'." + " Set `overwrite=True` to overwrite existing metadata." + ) + else: + card.data[key] = value + + return card.push_to_hub( + repo_id, + token=token, + repo_type=repo_type, + commit_message=commit_message, + commit_description=commit_description, + create_pr=create_pr, + revision=revision, + parent_commit=parent_commit, + ) diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/repocard_data.py b/.venv/lib/python3.12/site-packages/huggingface_hub/repocard_data.py new file mode 100644 index 0000000000000000000000000000000000000000..62215f2274e482d4ed69a1d6deeafdf34fc5a6a4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/repocard_data.py @@ -0,0 +1,770 @@ +import copy +from collections import defaultdict +from dataclasses import dataclass +from typing import Any, Dict, List, Optional, Tuple, Union + +from huggingface_hub.utils import logging, yaml_dump + + +logger = logging.get_logger(__name__) + + +@dataclass +class EvalResult: + """ + Flattened representation of individual evaluation results found in model-index of Model Cards. + + For more information on the model-index spec, see https://github.com/huggingface/hub-docs/blob/main/modelcard.md?plain=1. + + Args: + task_type (`str`): + The task identifier. Example: "image-classification". + dataset_type (`str`): + The dataset identifier. Example: "common_voice". Use dataset id from https://hf.co/datasets. + dataset_name (`str`): + A pretty name for the dataset. Example: "Common Voice (French)". + metric_type (`str`): + The metric identifier. Example: "wer". Use metric id from https://hf.co/metrics. + metric_value (`Any`): + The metric value. Example: 0.9 or "20.0 ± 1.2". + task_name (`str`, *optional*): + A pretty name for the task. Example: "Speech Recognition". + dataset_config (`str`, *optional*): + The name of the dataset configuration used in `load_dataset()`. + Example: fr in `load_dataset("common_voice", "fr")`. See the `datasets` docs for more info: + https://hf.co/docs/datasets/package_reference/loading_methods#datasets.load_dataset.name + dataset_split (`str`, *optional*): + The split used in `load_dataset()`. Example: "test". + dataset_revision (`str`, *optional*): + The revision (AKA Git Sha) of the dataset used in `load_dataset()`. + Example: 5503434ddd753f426f4b38109466949a1217c2bb + dataset_args (`Dict[str, Any]`, *optional*): + The arguments passed during `Metric.compute()`. Example for `bleu`: `{"max_order": 4}` + metric_name (`str`, *optional*): + A pretty name for the metric. Example: "Test WER". + metric_config (`str`, *optional*): + The name of the metric configuration used in `load_metric()`. + Example: bleurt-large-512 in `load_metric("bleurt", "bleurt-large-512")`. + See the `datasets` docs for more info: https://huggingface.co/docs/datasets/v2.1.0/en/loading#load-configurations + metric_args (`Dict[str, Any]`, *optional*): + The arguments passed during `Metric.compute()`. Example for `bleu`: max_order: 4 + verified (`bool`, *optional*): + Indicates whether the metrics originate from Hugging Face's [evaluation service](https://huggingface.co/spaces/autoevaluate/model-evaluator) or not. Automatically computed by Hugging Face, do not set. + verify_token (`str`, *optional*): + A JSON Web Token that is used to verify whether the metrics originate from Hugging Face's [evaluation service](https://huggingface.co/spaces/autoevaluate/model-evaluator) or not. + source_name (`str`, *optional*): + The name of the source of the evaluation result. Example: "Open LLM Leaderboard". + source_url (`str`, *optional*): + The URL of the source of the evaluation result. Example: "https://huggingface.co/spaces/open-llm-leaderboard/open_llm_leaderboard". + """ + + # Required + + # The task identifier + # Example: automatic-speech-recognition + task_type: str + + # The dataset identifier + # Example: common_voice. Use dataset id from https://hf.co/datasets + dataset_type: str + + # A pretty name for the dataset. + # Example: Common Voice (French) + dataset_name: str + + # The metric identifier + # Example: wer. Use metric id from https://hf.co/metrics + metric_type: str + + # Value of the metric. + # Example: 20.0 or "20.0 ± 1.2" + metric_value: Any + + # Optional + + # A pretty name for the task. + # Example: Speech Recognition + task_name: Optional[str] = None + + # The name of the dataset configuration used in `load_dataset()`. + # Example: fr in `load_dataset("common_voice", "fr")`. + # See the `datasets` docs for more info: + # https://huggingface.co/docs/datasets/package_reference/loading_methods#datasets.load_dataset.name + dataset_config: Optional[str] = None + + # The split used in `load_dataset()`. + # Example: test + dataset_split: Optional[str] = None + + # The revision (AKA Git Sha) of the dataset used in `load_dataset()`. + # Example: 5503434ddd753f426f4b38109466949a1217c2bb + dataset_revision: Optional[str] = None + + # The arguments passed during `Metric.compute()`. + # Example for `bleu`: max_order: 4 + dataset_args: Optional[Dict[str, Any]] = None + + # A pretty name for the metric. + # Example: Test WER + metric_name: Optional[str] = None + + # The name of the metric configuration used in `load_metric()`. + # Example: bleurt-large-512 in `load_metric("bleurt", "bleurt-large-512")`. + # See the `datasets` docs for more info: https://huggingface.co/docs/datasets/v2.1.0/en/loading#load-configurations + metric_config: Optional[str] = None + + # The arguments passed during `Metric.compute()`. + # Example for `bleu`: max_order: 4 + metric_args: Optional[Dict[str, Any]] = None + + # Indicates whether the metrics originate from Hugging Face's [evaluation service](https://huggingface.co/spaces/autoevaluate/model-evaluator) or not. Automatically computed by Hugging Face, do not set. + verified: Optional[bool] = None + + # A JSON Web Token that is used to verify whether the metrics originate from Hugging Face's [evaluation service](https://huggingface.co/spaces/autoevaluate/model-evaluator) or not. + verify_token: Optional[str] = None + + # The name of the source of the evaluation result. + # Example: Open LLM Leaderboard + source_name: Optional[str] = None + + # The URL of the source of the evaluation result. + # Example: https://huggingface.co/spaces/open-llm-leaderboard/open_llm_leaderboard + source_url: Optional[str] = None + + @property + def unique_identifier(self) -> tuple: + """Returns a tuple that uniquely identifies this evaluation.""" + return ( + self.task_type, + self.dataset_type, + self.dataset_config, + self.dataset_split, + self.dataset_revision, + ) + + def is_equal_except_value(self, other: "EvalResult") -> bool: + """ + Return True if `self` and `other` describe exactly the same metric but with a + different value. + """ + for key, _ in self.__dict__.items(): + if key == "metric_value": + continue + # For metrics computed by Hugging Face's evaluation service, `verify_token` is derived from `metric_value`, + # so we exclude it here in the comparison. + if key != "verify_token" and getattr(self, key) != getattr(other, key): + return False + return True + + def __post_init__(self) -> None: + if self.source_name is not None and self.source_url is None: + raise ValueError("If `source_name` is provided, `source_url` must also be provided.") + + +@dataclass +class CardData: + """Structure containing metadata from a RepoCard. + + [`CardData`] is the parent class of [`ModelCardData`] and [`DatasetCardData`]. + + Metadata can be exported as a dictionary or YAML. Export can be customized to alter the representation of the data + (example: flatten evaluation results). `CardData` behaves as a dictionary (can get, pop, set values) but do not + inherit from `dict` to allow this export step. + """ + + def __init__(self, ignore_metadata_errors: bool = False, **kwargs): + self.__dict__.update(kwargs) + + def to_dict(self): + """Converts CardData to a dict. + + Returns: + `dict`: CardData represented as a dictionary ready to be dumped to a YAML + block for inclusion in a README.md file. + """ + + data_dict = copy.deepcopy(self.__dict__) + self._to_dict(data_dict) + return {key: value for key, value in data_dict.items() if value is not None} + + def _to_dict(self, data_dict): + """Use this method in child classes to alter the dict representation of the data. Alter the dict in-place. + + Args: + data_dict (`dict`): The raw dict representation of the card data. + """ + pass + + def to_yaml(self, line_break=None, original_order: Optional[List[str]] = None) -> str: + """Dumps CardData to a YAML block for inclusion in a README.md file. + + Args: + line_break (str, *optional*): + The line break to use when dumping to yaml. + + Returns: + `str`: CardData represented as a YAML block. + """ + if original_order: + self.__dict__ = { + k: self.__dict__[k] + for k in original_order + list(set(self.__dict__.keys()) - set(original_order)) + if k in self.__dict__ + } + return yaml_dump(self.to_dict(), sort_keys=False, line_break=line_break).strip() + + def __repr__(self): + return repr(self.__dict__) + + def __str__(self): + return self.to_yaml() + + def get(self, key: str, default: Any = None) -> Any: + """Get value for a given metadata key.""" + value = self.__dict__.get(key) + return default if value is None else value + + def pop(self, key: str, default: Any = None) -> Any: + """Pop value for a given metadata key.""" + return self.__dict__.pop(key, default) + + def __getitem__(self, key: str) -> Any: + """Get value for a given metadata key.""" + return self.__dict__[key] + + def __setitem__(self, key: str, value: Any) -> None: + """Set value for a given metadata key.""" + self.__dict__[key] = value + + def __contains__(self, key: str) -> bool: + """Check if a given metadata key is set.""" + return key in self.__dict__ + + def __len__(self) -> int: + """Return the number of metadata keys set.""" + return len(self.__dict__) + + +def _validate_eval_results( + eval_results: Optional[Union[EvalResult, List[EvalResult]]], + model_name: Optional[str], +) -> List[EvalResult]: + if eval_results is None: + return [] + if isinstance(eval_results, EvalResult): + eval_results = [eval_results] + if not isinstance(eval_results, list) or not all(isinstance(r, EvalResult) for r in eval_results): + raise ValueError( + f"`eval_results` should be of type `EvalResult` or a list of `EvalResult`, got {type(eval_results)}." + ) + if model_name is None: + raise ValueError("Passing `eval_results` requires `model_name` to be set.") + return eval_results + + +class ModelCardData(CardData): + """Model Card Metadata that is used by Hugging Face Hub when included at the top of your README.md + + Args: + base_model (`str` or `List[str]`, *optional*): + The identifier of the base model from which the model derives. This is applicable for example if your model is a + fine-tune or adapter of an existing model. The value must be the ID of a model on the Hub (or a list of IDs + if your model derives from multiple models). Defaults to None. + datasets (`Union[str, List[str]]`, *optional*): + Dataset or list of datasets that were used to train this model. Should be a dataset ID + found on https://hf.co/datasets. Defaults to None. + eval_results (`Union[List[EvalResult], EvalResult]`, *optional*): + List of `huggingface_hub.EvalResult` that define evaluation results of the model. If provided, + `model_name` is used to as a name on PapersWithCode's leaderboards. Defaults to `None`. + language (`Union[str, List[str]]`, *optional*): + Language of model's training data or metadata. It must be an ISO 639-1, 639-2 or + 639-3 code (two/three letters), or a special value like "code", "multilingual". Defaults to `None`. + library_name (`str`, *optional*): + Name of library used by this model. Example: keras or any library from + https://github.com/huggingface/huggingface.js/blob/main/packages/tasks/src/model-libraries.ts. + Defaults to None. + license (`str`, *optional*): + License of this model. Example: apache-2.0 or any license from + https://huggingface.co/docs/hub/repositories-licenses. Defaults to None. + license_name (`str`, *optional*): + Name of the license of this model. Defaults to None. To be used in conjunction with `license_link`. + Common licenses (Apache-2.0, MIT, CC-BY-SA-4.0) do not need a name. In that case, use `license` instead. + license_link (`str`, *optional*): + Link to the license of this model. Defaults to None. To be used in conjunction with `license_name`. + Common licenses (Apache-2.0, MIT, CC-BY-SA-4.0) do not need a link. In that case, use `license` instead. + metrics (`List[str]`, *optional*): + List of metrics used to evaluate this model. Should be a metric name that can be found + at https://hf.co/metrics. Example: 'accuracy'. Defaults to None. + model_name (`str`, *optional*): + A name for this model. It is used along with + `eval_results` to construct the `model-index` within the card's metadata. The name + you supply here is what will be used on PapersWithCode's leaderboards. If None is provided + then the repo name is used as a default. Defaults to None. + pipeline_tag (`str`, *optional*): + The pipeline tag associated with the model. Example: "text-classification". + tags (`List[str]`, *optional*): + List of tags to add to your model that can be used when filtering on the Hugging + Face Hub. Defaults to None. + ignore_metadata_errors (`str`): + If True, errors while parsing the metadata section will be ignored. Some information might be lost during + the process. Use it at your own risk. + kwargs (`dict`, *optional*): + Additional metadata that will be added to the model card. Defaults to None. + + Example: + ```python + >>> from huggingface_hub import ModelCardData + >>> card_data = ModelCardData( + ... language="en", + ... license="mit", + ... library_name="timm", + ... tags=['image-classification', 'resnet'], + ... ) + >>> card_data.to_dict() + {'language': 'en', 'license': 'mit', 'library_name': 'timm', 'tags': ['image-classification', 'resnet']} + + ``` + """ + + def __init__( + self, + *, + base_model: Optional[Union[str, List[str]]] = None, + datasets: Optional[Union[str, List[str]]] = None, + eval_results: Optional[List[EvalResult]] = None, + language: Optional[Union[str, List[str]]] = None, + library_name: Optional[str] = None, + license: Optional[str] = None, + license_name: Optional[str] = None, + license_link: Optional[str] = None, + metrics: Optional[List[str]] = None, + model_name: Optional[str] = None, + pipeline_tag: Optional[str] = None, + tags: Optional[List[str]] = None, + ignore_metadata_errors: bool = False, + **kwargs, + ): + self.base_model = base_model + self.datasets = datasets + self.eval_results = eval_results + self.language = language + self.library_name = library_name + self.license = license + self.license_name = license_name + self.license_link = license_link + self.metrics = metrics + self.model_name = model_name + self.pipeline_tag = pipeline_tag + self.tags = _to_unique_list(tags) + + model_index = kwargs.pop("model-index", None) + if model_index: + try: + model_name, eval_results = model_index_to_eval_results(model_index) + self.model_name = model_name + self.eval_results = eval_results + except (KeyError, TypeError) as error: + if ignore_metadata_errors: + logger.warning("Invalid model-index. Not loading eval results into CardData.") + else: + raise ValueError( + f"Invalid `model_index` in metadata cannot be parsed: {error.__class__} {error}. Pass" + " `ignore_metadata_errors=True` to ignore this error while loading a Model Card. Warning:" + " some information will be lost. Use it at your own risk." + ) + + super().__init__(**kwargs) + + if self.eval_results: + try: + self.eval_results = _validate_eval_results(self.eval_results, self.model_name) + except Exception as e: + if ignore_metadata_errors: + logger.warning(f"Failed to validate eval_results: {e}. Not loading eval results into CardData.") + else: + raise ValueError(f"Failed to validate eval_results: {e}") from e + + def _to_dict(self, data_dict): + """Format the internal data dict. In this case, we convert eval results to a valid model index""" + if self.eval_results is not None: + data_dict["model-index"] = eval_results_to_model_index(self.model_name, self.eval_results) + del data_dict["eval_results"], data_dict["model_name"] + + +class DatasetCardData(CardData): + """Dataset Card Metadata that is used by Hugging Face Hub when included at the top of your README.md + + Args: + language (`List[str]`, *optional*): + Language of dataset's data or metadata. It must be an ISO 639-1, 639-2 or + 639-3 code (two/three letters), or a special value like "code", "multilingual". + license (`Union[str, List[str]]`, *optional*): + License(s) of this dataset. Example: apache-2.0 or any license from + https://huggingface.co/docs/hub/repositories-licenses. + annotations_creators (`Union[str, List[str]]`, *optional*): + How the annotations for the dataset were created. + Options are: 'found', 'crowdsourced', 'expert-generated', 'machine-generated', 'no-annotation', 'other'. + language_creators (`Union[str, List[str]]`, *optional*): + How the text-based data in the dataset was created. + Options are: 'found', 'crowdsourced', 'expert-generated', 'machine-generated', 'other' + multilinguality (`Union[str, List[str]]`, *optional*): + Whether the dataset is multilingual. + Options are: 'monolingual', 'multilingual', 'translation', 'other'. + size_categories (`Union[str, List[str]]`, *optional*): + The number of examples in the dataset. Options are: 'n<1K', '1K1T', and 'other'. + source_datasets (`List[str]]`, *optional*): + Indicates whether the dataset is an original dataset or extended from another existing dataset. + Options are: 'original' and 'extended'. + task_categories (`Union[str, List[str]]`, *optional*): + What categories of task does the dataset support? + task_ids (`Union[str, List[str]]`, *optional*): + What specific tasks does the dataset support? + paperswithcode_id (`str`, *optional*): + ID of the dataset on PapersWithCode. + pretty_name (`str`, *optional*): + A more human-readable name for the dataset. (ex. "Cats vs. Dogs") + train_eval_index (`Dict`, *optional*): + A dictionary that describes the necessary spec for doing evaluation on the Hub. + If not provided, it will be gathered from the 'train-eval-index' key of the kwargs. + config_names (`Union[str, List[str]]`, *optional*): + A list of the available dataset configs for the dataset. + """ + + def __init__( + self, + *, + language: Optional[Union[str, List[str]]] = None, + license: Optional[Union[str, List[str]]] = None, + annotations_creators: Optional[Union[str, List[str]]] = None, + language_creators: Optional[Union[str, List[str]]] = None, + multilinguality: Optional[Union[str, List[str]]] = None, + size_categories: Optional[Union[str, List[str]]] = None, + source_datasets: Optional[List[str]] = None, + task_categories: Optional[Union[str, List[str]]] = None, + task_ids: Optional[Union[str, List[str]]] = None, + paperswithcode_id: Optional[str] = None, + pretty_name: Optional[str] = None, + train_eval_index: Optional[Dict] = None, + config_names: Optional[Union[str, List[str]]] = None, + ignore_metadata_errors: bool = False, + **kwargs, + ): + self.annotations_creators = annotations_creators + self.language_creators = language_creators + self.language = language + self.license = license + self.multilinguality = multilinguality + self.size_categories = size_categories + self.source_datasets = source_datasets + self.task_categories = task_categories + self.task_ids = task_ids + self.paperswithcode_id = paperswithcode_id + self.pretty_name = pretty_name + self.config_names = config_names + + # TODO - maybe handle this similarly to EvalResult? + self.train_eval_index = train_eval_index or kwargs.pop("train-eval-index", None) + super().__init__(**kwargs) + + def _to_dict(self, data_dict): + data_dict["train-eval-index"] = data_dict.pop("train_eval_index") + + +class SpaceCardData(CardData): + """Space Card Metadata that is used by Hugging Face Hub when included at the top of your README.md + + To get an exhaustive reference of Spaces configuration, please visit https://huggingface.co/docs/hub/spaces-config-reference#spaces-configuration-reference. + + Args: + title (`str`, *optional*) + Title of the Space. + sdk (`str`, *optional*) + SDK of the Space (one of `gradio`, `streamlit`, `docker`, or `static`). + sdk_version (`str`, *optional*) + Version of the used SDK (if Gradio/Streamlit sdk). + python_version (`str`, *optional*) + Python version used in the Space (if Gradio/Streamlit sdk). + app_file (`str`, *optional*) + Path to your main application file (which contains either gradio or streamlit Python code, or static html code). + Path is relative to the root of the repository. + app_port (`str`, *optional*) + Port on which your application is running. Used only if sdk is `docker`. + license (`str`, *optional*) + License of this model. Example: apache-2.0 or any license from + https://huggingface.co/docs/hub/repositories-licenses. + duplicated_from (`str`, *optional*) + ID of the original Space if this is a duplicated Space. + models (List[`str`], *optional*) + List of models related to this Space. Should be a dataset ID found on https://hf.co/models. + datasets (`List[str]`, *optional*) + List of datasets related to this Space. Should be a dataset ID found on https://hf.co/datasets. + tags (`List[str]`, *optional*) + List of tags to add to your Space that can be used when filtering on the Hub. + ignore_metadata_errors (`str`): + If True, errors while parsing the metadata section will be ignored. Some information might be lost during + the process. Use it at your own risk. + kwargs (`dict`, *optional*): + Additional metadata that will be added to the space card. + + Example: + ```python + >>> from huggingface_hub import SpaceCardData + >>> card_data = SpaceCardData( + ... title="Dreambooth Training", + ... license="mit", + ... sdk="gradio", + ... duplicated_from="multimodalart/dreambooth-training" + ... ) + >>> card_data.to_dict() + {'title': 'Dreambooth Training', 'sdk': 'gradio', 'license': 'mit', 'duplicated_from': 'multimodalart/dreambooth-training'} + ``` + """ + + def __init__( + self, + *, + title: Optional[str] = None, + sdk: Optional[str] = None, + sdk_version: Optional[str] = None, + python_version: Optional[str] = None, + app_file: Optional[str] = None, + app_port: Optional[int] = None, + license: Optional[str] = None, + duplicated_from: Optional[str] = None, + models: Optional[List[str]] = None, + datasets: Optional[List[str]] = None, + tags: Optional[List[str]] = None, + ignore_metadata_errors: bool = False, + **kwargs, + ): + self.title = title + self.sdk = sdk + self.sdk_version = sdk_version + self.python_version = python_version + self.app_file = app_file + self.app_port = app_port + self.license = license + self.duplicated_from = duplicated_from + self.models = models + self.datasets = datasets + self.tags = _to_unique_list(tags) + super().__init__(**kwargs) + + +def model_index_to_eval_results(model_index: List[Dict[str, Any]]) -> Tuple[str, List[EvalResult]]: + """Takes in a model index and returns the model name and a list of `huggingface_hub.EvalResult` objects. + + A detailed spec of the model index can be found here: + https://github.com/huggingface/hub-docs/blob/main/modelcard.md?plain=1 + + Args: + model_index (`List[Dict[str, Any]]`): + A model index data structure, likely coming from a README.md file on the + Hugging Face Hub. + + Returns: + model_name (`str`): + The name of the model as found in the model index. This is used as the + identifier for the model on leaderboards like PapersWithCode. + eval_results (`List[EvalResult]`): + A list of `huggingface_hub.EvalResult` objects containing the metrics + reported in the provided model_index. + + Example: + ```python + >>> from huggingface_hub.repocard_data import model_index_to_eval_results + >>> # Define a minimal model index + >>> model_index = [ + ... { + ... "name": "my-cool-model", + ... "results": [ + ... { + ... "task": { + ... "type": "image-classification" + ... }, + ... "dataset": { + ... "type": "beans", + ... "name": "Beans" + ... }, + ... "metrics": [ + ... { + ... "type": "accuracy", + ... "value": 0.9 + ... } + ... ] + ... } + ... ] + ... } + ... ] + >>> model_name, eval_results = model_index_to_eval_results(model_index) + >>> model_name + 'my-cool-model' + >>> eval_results[0].task_type + 'image-classification' + >>> eval_results[0].metric_type + 'accuracy' + + ``` + """ + + eval_results = [] + for elem in model_index: + name = elem["name"] + results = elem["results"] + for result in results: + task_type = result["task"]["type"] + task_name = result["task"].get("name") + dataset_type = result["dataset"]["type"] + dataset_name = result["dataset"]["name"] + dataset_config = result["dataset"].get("config") + dataset_split = result["dataset"].get("split") + dataset_revision = result["dataset"].get("revision") + dataset_args = result["dataset"].get("args") + source_name = result.get("source", {}).get("name") + source_url = result.get("source", {}).get("url") + + for metric in result["metrics"]: + metric_type = metric["type"] + metric_value = metric["value"] + metric_name = metric.get("name") + metric_args = metric.get("args") + metric_config = metric.get("config") + verified = metric.get("verified") + verify_token = metric.get("verifyToken") + + eval_result = EvalResult( + task_type=task_type, # Required + dataset_type=dataset_type, # Required + dataset_name=dataset_name, # Required + metric_type=metric_type, # Required + metric_value=metric_value, # Required + task_name=task_name, + dataset_config=dataset_config, + dataset_split=dataset_split, + dataset_revision=dataset_revision, + dataset_args=dataset_args, + metric_name=metric_name, + metric_args=metric_args, + metric_config=metric_config, + verified=verified, + verify_token=verify_token, + source_name=source_name, + source_url=source_url, + ) + eval_results.append(eval_result) + return name, eval_results + + +def _remove_none(obj): + """ + Recursively remove `None` values from a dict. Borrowed from: https://stackoverflow.com/a/20558778 + """ + if isinstance(obj, (list, tuple, set)): + return type(obj)(_remove_none(x) for x in obj if x is not None) + elif isinstance(obj, dict): + return type(obj)((_remove_none(k), _remove_none(v)) for k, v in obj.items() if k is not None and v is not None) + else: + return obj + + +def eval_results_to_model_index(model_name: str, eval_results: List[EvalResult]) -> List[Dict[str, Any]]: + """Takes in given model name and list of `huggingface_hub.EvalResult` and returns a + valid model-index that will be compatible with the format expected by the + Hugging Face Hub. + + Args: + model_name (`str`): + Name of the model (ex. "my-cool-model"). This is used as the identifier + for the model on leaderboards like PapersWithCode. + eval_results (`List[EvalResult]`): + List of `huggingface_hub.EvalResult` objects containing the metrics to be + reported in the model-index. + + Returns: + model_index (`List[Dict[str, Any]]`): The eval_results converted to a model-index. + + Example: + ```python + >>> from huggingface_hub.repocard_data import eval_results_to_model_index, EvalResult + >>> # Define minimal eval_results + >>> eval_results = [ + ... EvalResult( + ... task_type="image-classification", # Required + ... dataset_type="beans", # Required + ... dataset_name="Beans", # Required + ... metric_type="accuracy", # Required + ... metric_value=0.9, # Required + ... ) + ... ] + >>> eval_results_to_model_index("my-cool-model", eval_results) + [{'name': 'my-cool-model', 'results': [{'task': {'type': 'image-classification'}, 'dataset': {'name': 'Beans', 'type': 'beans'}, 'metrics': [{'type': 'accuracy', 'value': 0.9}]}]}] + + ``` + """ + + # Metrics are reported on a unique task-and-dataset basis. + # Here, we make a map of those pairs and the associated EvalResults. + task_and_ds_types_map: Dict[Any, List[EvalResult]] = defaultdict(list) + for eval_result in eval_results: + task_and_ds_types_map[eval_result.unique_identifier].append(eval_result) + + # Use the map from above to generate the model index data. + model_index_data = [] + for results in task_and_ds_types_map.values(): + # All items from `results` share same metadata + sample_result = results[0] + data = { + "task": { + "type": sample_result.task_type, + "name": sample_result.task_name, + }, + "dataset": { + "name": sample_result.dataset_name, + "type": sample_result.dataset_type, + "config": sample_result.dataset_config, + "split": sample_result.dataset_split, + "revision": sample_result.dataset_revision, + "args": sample_result.dataset_args, + }, + "metrics": [ + { + "type": result.metric_type, + "value": result.metric_value, + "name": result.metric_name, + "config": result.metric_config, + "args": result.metric_args, + "verified": result.verified, + "verifyToken": result.verify_token, + } + for result in results + ], + } + if sample_result.source_url is not None: + source = { + "url": sample_result.source_url, + } + if sample_result.source_name is not None: + source["name"] = sample_result.source_name + data["source"] = source + model_index_data.append(data) + + # TODO - Check if there cases where this list is longer than one? + # Finally, the model index itself is list of dicts. + model_index = [ + { + "name": model_name, + "results": model_index_data, + } + ] + return _remove_none(model_index) + + +def _to_unique_list(tags: Optional[List[str]]) -> Optional[List[str]]: + if tags is None: + return tags + unique_tags = [] # make tags unique + keep order explicitly + for tag in tags: + if tag not in unique_tags: + unique_tags.append(tag) + return unique_tags diff --git a/.venv/lib/python3.12/site-packages/huggingface_hub/repository.py b/.venv/lib/python3.12/site-packages/huggingface_hub/repository.py new file mode 100644 index 0000000000000000000000000000000000000000..d4a904f458b8c2cc03a786832dd3f97005be9e56 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/huggingface_hub/repository.py @@ -0,0 +1,1477 @@ +import atexit +import os +import re +import subprocess +import threading +import time +from contextlib import contextmanager +from pathlib import Path +from typing import Callable, Dict, Iterator, List, Optional, Tuple, TypedDict, Union +from urllib.parse import urlparse + +from huggingface_hub import constants +from huggingface_hub.repocard import metadata_load, metadata_save + +from .hf_api import HfApi, repo_type_and_id_from_hf_id +from .lfs import LFS_MULTIPART_UPLOAD_COMMAND +from .utils import ( + SoftTemporaryDirectory, + get_token, + logging, + run_subprocess, + tqdm, + validate_hf_hub_args, +) +from .utils._deprecation import _deprecate_method + + +logger = logging.get_logger(__name__) + + +class CommandInProgress: + """ + Utility to follow commands launched asynchronously. + """ + + def __init__( + self, + title: str, + is_done_method: Callable, + status_method: Callable, + process: subprocess.Popen, + post_method: Optional[Callable] = None, + ): + self.title = title + self._is_done = is_done_method + self._status = status_method + self._process = process + self._stderr = "" + self._stdout = "" + self._post_method = post_method + + @property + def is_done(self) -> bool: + """ + Whether the process is done. + """ + result = self._is_done() + + if result and self._post_method is not None: + self._post_method() + self._post_method = None + + return result + + @property + def status(self) -> int: + """ + The exit code/status of the current action. Will return `0` if the + command has completed successfully, and a number between 1 and 255 if + the process errored-out. + + Will return -1 if the command is still ongoing. + """ + return self._status() + + @property + def failed(self) -> bool: + """ + Whether the process errored-out. + """ + return self.status > 0 + + @property + def stderr(self) -> str: + """ + The current output message on the standard error. + """ + if self._process.stderr is not None: + self._stderr += self._process.stderr.read() + return self._stderr + + @property + def stdout(self) -> str: + """ + The current output message on the standard output. + """ + if self._process.stdout is not None: + self._stdout += self._process.stdout.read() + return self._stdout + + def __repr__(self): + status = self.status + + if status == -1: + status = "running" + + return ( + f"[{self.title} command, status code: {status}," + f" {'in progress.' if not self.is_done else 'finished.'} PID:" + f" {self._process.pid}]" + ) + + +def is_git_repo(folder: Union[str, Path]) -> bool: + """ + Check if the folder is the root or part of a git repository + + Args: + folder (`str`): + The folder in which to run the command. + + Returns: + `bool`: `True` if the repository is part of a repository, `False` + otherwise. + """ + folder_exists = os.path.exists(os.path.join(folder, ".git")) + git_branch = subprocess.run("git branch".split(), cwd=folder, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + return folder_exists and git_branch.returncode == 0 + + +def is_local_clone(folder: Union[str, Path], remote_url: str) -> bool: + """ + Check if the folder is a local clone of the remote_url + + Args: + folder (`str` or `Path`): + The folder in which to run the command. + remote_url (`str`): + The url of a git repository. + + Returns: + `bool`: `True` if the repository is a local clone of the remote + repository specified, `False` otherwise. + """ + if not is_git_repo(folder): + return False + + remotes = run_subprocess("git remote -v", folder).stdout + + # Remove token for the test with remotes. + remote_url = re.sub(r"https://.*@", "https://", remote_url) + remotes = [re.sub(r"https://.*@", "https://", remote) for remote in remotes.split()] + return remote_url in remotes + + +def is_tracked_with_lfs(filename: Union[str, Path]) -> bool: + """ + Check if the file passed is tracked with git-lfs. + + Args: + filename (`str` or `Path`): + The filename to check. + + Returns: + `bool`: `True` if the file passed is tracked with git-lfs, `False` + otherwise. + """ + folder = Path(filename).parent + filename = Path(filename).name + + try: + p = run_subprocess("git check-attr -a".split() + [filename], folder) + attributes = p.stdout.strip() + except subprocess.CalledProcessError as exc: + if not is_git_repo(folder): + return False + else: + raise OSError(exc.stderr) + + if len(attributes) == 0: + return False + + found_lfs_tag = {"diff": False, "merge": False, "filter": False} + + for attribute in attributes.split("\n"): + for tag in found_lfs_tag.keys(): + if tag in attribute and "lfs" in attribute: + found_lfs_tag[tag] = True + + return all(found_lfs_tag.values()) + + +def is_git_ignored(filename: Union[str, Path]) -> bool: + """ + Check if file is git-ignored. Supports nested .gitignore files. + + Args: + filename (`str` or `Path`): + The filename to check. + + Returns: + `bool`: `True` if the file passed is ignored by `git`, `False` + otherwise. + """ + folder = Path(filename).parent + filename = Path(filename).name + + try: + p = run_subprocess("git check-ignore".split() + [filename], folder, check=False) + # Will return exit code 1 if not gitignored + is_ignored = not bool(p.returncode) + except subprocess.CalledProcessError as exc: + raise OSError(exc.stderr) + + return is_ignored + + +def is_binary_file(filename: Union[str, Path]) -> bool: + """ + Check if file is a binary file. + + Args: + filename (`str` or `Path`): + The filename to check. + + Returns: + `bool`: `True` if the file passed is a binary file, `False` otherwise. + """ + try: + with open(filename, "rb") as f: + content = f.read(10 * (1024**2)) # Read a maximum of 10MB + + # Code sample taken from the following stack overflow thread + # https://stackoverflow.com/questions/898669/how-can-i-detect-if-a-file-is-binary-non-text-in-python/7392391#7392391 + text_chars = bytearray({7, 8, 9, 10, 12, 13, 27} | set(range(0x20, 0x100)) - {0x7F}) + return bool(content.translate(None, text_chars)) + except UnicodeDecodeError: + return True + + +def files_to_be_staged(pattern: str = ".", folder: Union[str, Path, None] = None) -> List[str]: + """ + Returns a list of filenames that are to be staged. + + Args: + pattern (`str` or `Path`): + The pattern of filenames to check. Put `.` to get all files. + folder (`str` or `Path`): + The folder in which to run the command. + + Returns: + `List[str]`: List of files that are to be staged. + """ + try: + p = run_subprocess("git ls-files --exclude-standard -mo".split() + [pattern], folder) + if len(p.stdout.strip()): + files = p.stdout.strip().split("\n") + else: + files = [] + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + return files + + +def is_tracked_upstream(folder: Union[str, Path]) -> bool: + """ + Check if the current checked-out branch is tracked upstream. + + Args: + folder (`str` or `Path`): + The folder in which to run the command. + + Returns: + `bool`: `True` if the current checked-out branch is tracked upstream, + `False` otherwise. + """ + try: + run_subprocess("git rev-parse --symbolic-full-name --abbrev-ref @{u}", folder) + return True + except subprocess.CalledProcessError as exc: + if "HEAD" in exc.stderr: + raise OSError("No branch checked out") + + return False + + +def commits_to_push(folder: Union[str, Path], upstream: Optional[str] = None) -> int: + """ + Check the number of commits that would be pushed upstream + + Args: + folder (`str` or `Path`): + The folder in which to run the command. + upstream (`str`, *optional*): + The name of the upstream repository with which the comparison should be + made. + + Returns: + `int`: Number of commits that would be pushed upstream were a `git + push` to proceed. + """ + try: + result = run_subprocess(f"git cherry -v {upstream or ''}", folder) + return len(result.stdout.split("\n")) - 1 + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + +class PbarT(TypedDict): + # Used to store an opened progress bar in `_lfs_log_progress` + bar: tqdm + past_bytes: int + + +@contextmanager +def _lfs_log_progress(): + """ + This is a context manager that will log the Git LFS progress of cleaning, + smudging, pulling and pushing. + """ + + if logger.getEffectiveLevel() >= logging.ERROR: + try: + yield + except Exception: + pass + return + + def output_progress(stopping_event: threading.Event): + """ + To be launched as a separate thread with an event meaning it should stop + the tail. + """ + # Key is tuple(state, filename), value is a dict(tqdm bar and a previous value) + pbars: Dict[Tuple[str, str], PbarT] = {} + + def close_pbars(): + for pbar in pbars.values(): + pbar["bar"].update(pbar["bar"].total - pbar["past_bytes"]) + pbar["bar"].refresh() + pbar["bar"].close() + + def tail_file(filename) -> Iterator[str]: + """ + Creates a generator to be iterated through, which will return each + line one by one. Will stop tailing the file if the stopping_event is + set. + """ + with open(filename, "r") as file: + current_line = "" + while True: + if stopping_event.is_set(): + close_pbars() + break + + line_bit = file.readline() + if line_bit is not None and not len(line_bit.strip()) == 0: + current_line += line_bit + if current_line.endswith("\n"): + yield current_line + current_line = "" + else: + time.sleep(1) + + # If the file isn't created yet, wait for a few seconds before trying again. + # Can be interrupted with the stopping_event. + while not os.path.exists(os.environ["GIT_LFS_PROGRESS"]): + if stopping_event.is_set(): + close_pbars() + return + + time.sleep(2) + + for line in tail_file(os.environ["GIT_LFS_PROGRESS"]): + try: + state, file_progress, byte_progress, filename = line.split() + except ValueError as error: + # Try/except to ease debugging. See https://github.com/huggingface/huggingface_hub/issues/1373. + raise ValueError(f"Cannot unpack LFS progress line:\n{line}") from error + description = f"{state.capitalize()} file {filename}" + + current_bytes, total_bytes = byte_progress.split("/") + current_bytes_int = int(current_bytes) + total_bytes_int = int(total_bytes) + + pbar = pbars.get((state, filename)) + if pbar is None: + # Initialize progress bar + pbars[(state, filename)] = { + "bar": tqdm( + desc=description, + initial=current_bytes_int, + total=total_bytes_int, + unit="B", + unit_scale=True, + unit_divisor=1024, + name="huggingface_hub.lfs_upload", + ), + "past_bytes": int(current_bytes), + } + else: + # Update progress bar + pbar["bar"].update(current_bytes_int - pbar["past_bytes"]) + pbar["past_bytes"] = current_bytes_int + + current_lfs_progress_value = os.environ.get("GIT_LFS_PROGRESS", "") + + with SoftTemporaryDirectory() as tmpdir: + os.environ["GIT_LFS_PROGRESS"] = os.path.join(tmpdir, "lfs_progress") + logger.debug(f"Following progress in {os.environ['GIT_LFS_PROGRESS']}") + + exit_event = threading.Event() + x = threading.Thread(target=output_progress, args=(exit_event,), daemon=True) + x.start() + + try: + yield + finally: + exit_event.set() + x.join() + + os.environ["GIT_LFS_PROGRESS"] = current_lfs_progress_value + + +class Repository: + """ + Helper class to wrap the git and git-lfs commands. + + The aim is to facilitate interacting with huggingface.co hosted model or + dataset repos, though not a lot here (if any) is actually specific to + huggingface.co. + + + + [`Repository`] is deprecated in favor of the http-based alternatives implemented in + [`HfApi`]. Given its large adoption in legacy code, the complete removal of + [`Repository`] will only happen in release `v1.0`. For more details, please read + https://huggingface.co/docs/huggingface_hub/concepts/git_vs_http. + + + """ + + command_queue: List[CommandInProgress] + + @validate_hf_hub_args + @_deprecate_method( + version="1.0", + message=( + "Please prefer the http-based alternatives instead. Given its large adoption in legacy code, the complete" + " removal is only planned on next major release.\nFor more details, please read" + " https://huggingface.co/docs/huggingface_hub/concepts/git_vs_http." + ), + ) + def __init__( + self, + local_dir: Union[str, Path], + clone_from: Optional[str] = None, + repo_type: Optional[str] = None, + token: Union[bool, str] = True, + git_user: Optional[str] = None, + git_email: Optional[str] = None, + revision: Optional[str] = None, + skip_lfs_files: bool = False, + client: Optional[HfApi] = None, + ): + """ + Instantiate a local clone of a git repo. + + If `clone_from` is set, the repo will be cloned from an existing remote repository. + If the remote repo does not exist, a `EnvironmentError` exception will be thrown. + Please create the remote repo first using [`create_repo`]. + + `Repository` uses the local git credentials by default. If explicitly set, the `token` + or the `git_user`/`git_email` pair will be used instead. + + Args: + local_dir (`str` or `Path`): + path (e.g. `'my_trained_model/'`) to the local directory, where + the `Repository` will be initialized. + clone_from (`str`, *optional*): + Either a repository url or `repo_id`. + Example: + - `"https://huggingface.co/philschmid/playground-tests"` + - `"philschmid/playground-tests"` + repo_type (`str`, *optional*): + To set when cloning a repo from a repo_id. Default is model. + token (`bool` or `str`, *optional*): + A valid authentication token (see https://huggingface.co/settings/token). + If `None` or `True` and machine is logged in (through `hf auth login` + or [`~huggingface_hub.login`]), token will be retrieved from the cache. + If `False`, token is not sent in the request header. + git_user (`str`, *optional*): + will override the `git config user.name` for committing and + pushing files to the hub. + git_email (`str`, *optional*): + will override the `git config user.email` for committing and + pushing files to the hub. + revision (`str`, *optional*): + Revision to checkout after initializing the repository. If the + revision doesn't exist, a branch will be created with that + revision name from the default branch's current HEAD. + skip_lfs_files (`bool`, *optional*, defaults to `False`): + whether to skip git-LFS files or not. + client (`HfApi`, *optional*): + Instance of [`HfApi`] to use when calling the HF Hub API. A new + instance will be created if this is left to `None`. + + Raises: + [`EnvironmentError`](https://docs.python.org/3/library/exceptions.html#EnvironmentError) + If the remote repository set in `clone_from` does not exist. + """ + if isinstance(local_dir, Path): + local_dir = str(local_dir) + os.makedirs(local_dir, exist_ok=True) + self.local_dir = os.path.join(os.getcwd(), local_dir) + self._repo_type = repo_type + self.command_queue = [] + self.skip_lfs_files = skip_lfs_files + self.client = client if client is not None else HfApi() + + self.check_git_versions() + + if isinstance(token, str): + self.huggingface_token: Optional[str] = token + elif token is False: + self.huggingface_token = None + else: + # if `True` -> explicit use of the cached token + # if `None` -> implicit use of the cached token + self.huggingface_token = get_token() + + if clone_from is not None: + self.clone_from(repo_url=clone_from) + else: + if is_git_repo(self.local_dir): + logger.debug("[Repository] is a valid git repo") + else: + raise ValueError("If not specifying `clone_from`, you need to pass Repository a valid git clone.") + + if self.huggingface_token is not None and (git_email is None or git_user is None): + user = self.client.whoami(self.huggingface_token) + + if git_email is None: + git_email = user.get("email") + + if git_user is None: + git_user = user.get("fullname") + + if git_user is not None or git_email is not None: + self.git_config_username_and_email(git_user, git_email) + + self.lfs_enable_largefiles() + self.git_credential_helper_store() + + if revision is not None: + self.git_checkout(revision, create_branch_ok=True) + + # This ensures that all commands exit before exiting the Python runtime. + # This will ensure all pushes register on the hub, even if other errors happen in subsequent operations. + atexit.register(self.wait_for_commands) + + @property + def current_branch(self) -> str: + """ + Returns the current checked out branch. + + Returns: + `str`: Current checked out branch. + """ + try: + result = run_subprocess("git rev-parse --abbrev-ref HEAD", self.local_dir).stdout.strip() + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + return result + + def check_git_versions(self): + """ + Checks that `git` and `git-lfs` can be run. + + Raises: + [`EnvironmentError`](https://docs.python.org/3/library/exceptions.html#EnvironmentError) + If `git` or `git-lfs` are not installed. + """ + try: + git_version = run_subprocess("git --version", self.local_dir).stdout.strip() + except FileNotFoundError: + raise EnvironmentError("Looks like you do not have git installed, please install.") + + try: + lfs_version = run_subprocess("git-lfs --version", self.local_dir).stdout.strip() + except FileNotFoundError: + raise EnvironmentError( + "Looks like you do not have git-lfs installed, please install." + " You can install from https://git-lfs.github.com/." + " Then run `git lfs install` (you only have to do this once)." + ) + logger.info(git_version + "\n" + lfs_version) + + @validate_hf_hub_args + def clone_from(self, repo_url: str, token: Union[bool, str, None] = None): + """ + Clone from a remote. If the folder already exists, will try to clone the + repository within it. + + If this folder is a git repository with linked history, will try to + update the repository. + + Args: + repo_url (`str`): + The URL from which to clone the repository + token (`Union[str, bool]`, *optional*): + Whether to use the authentication token. It can be: + - a string which is the token itself + - `False`, which would not use the authentication token + - `True`, which would fetch the authentication token from the + local folder and use it (you should be logged in for this to + work). + - `None`, which would retrieve the value of + `self.huggingface_token`. + + + + Raises the following error: + + - [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + if an organization token (starts with "api_org") is passed. Use must use + your own personal access token (see https://hf.co/settings/tokens). + + - [`EnvironmentError`](https://docs.python.org/3/library/exceptions.html#EnvironmentError) + if you are trying to clone the repository in a non-empty folder, or if the + `git` operations raise errors. + + + """ + token = ( + token # str -> use it + if isinstance(token, str) + else ( + None # `False` -> explicit no token + if token is False + else self.huggingface_token # `None` or `True` -> use default + ) + ) + if token is not None and token.startswith("api_org"): + raise ValueError( + "You must use your personal access token, not an Organization token" + " (see https://hf.co/settings/tokens)." + ) + + hub_url = self.client.endpoint + if hub_url in repo_url or ("http" not in repo_url and len(repo_url.split("/")) <= 2): + repo_type, namespace, repo_name = repo_type_and_id_from_hf_id(repo_url, hub_url=hub_url) + repo_id = f"{namespace}/{repo_name}" if namespace is not None else repo_name + + if repo_type is not None: + self._repo_type = repo_type + + repo_url = hub_url + "/" + + if self._repo_type in constants.REPO_TYPES_URL_PREFIXES: + repo_url += constants.REPO_TYPES_URL_PREFIXES[self._repo_type] + + if token is not None: + # Add token in git url when provided + scheme = urlparse(repo_url).scheme + repo_url = repo_url.replace(f"{scheme}://", f"{scheme}://user:{token}@") + + repo_url += repo_id + + # For error messages, it's cleaner to show the repo url without the token. + clean_repo_url = re.sub(r"(https?)://.*@", r"\1://", repo_url) + try: + run_subprocess("git lfs install", self.local_dir) + + # checks if repository is initialized in a empty repository or in one with files + if len(os.listdir(self.local_dir)) == 0: + logger.warning(f"Cloning {clean_repo_url} into local empty directory.") + + with _lfs_log_progress(): + env = os.environ.copy() + + if self.skip_lfs_files: + env.update({"GIT_LFS_SKIP_SMUDGE": "1"}) + + run_subprocess( + # 'git lfs clone' is deprecated (will display a warning in the terminal) + # but we still use it as it provides a nicer UX when downloading large + # files (shows progress). + f"{'git clone' if self.skip_lfs_files else 'git lfs clone'} {repo_url} .", + self.local_dir, + env=env, + ) + else: + # Check if the folder is the root of a git repository + if not is_git_repo(self.local_dir): + raise EnvironmentError( + "Tried to clone a repository in a non-empty folder that isn't" + f" a git repository ('{self.local_dir}'). If you really want to" + f" do this, do it manually:\n cd {self.local_dir} && git init" + " && git remote add origin && git pull origin main\n or clone" + " repo to a new folder and move your existing files there" + " afterwards." + ) + + if is_local_clone(self.local_dir, repo_url): + logger.warning( + f"{self.local_dir} is already a clone of {clean_repo_url}." + " Make sure you pull the latest changes with" + " `repo.git_pull()`." + ) + else: + output = run_subprocess("git remote get-url origin", self.local_dir, check=False) + + error_msg = ( + f"Tried to clone {clean_repo_url} in an unrelated git" + " repository.\nIf you believe this is an error, please add" + f" a remote with the following URL: {clean_repo_url}." + ) + if output.returncode == 0: + clean_local_remote_url = re.sub(r"https://.*@", "https://", output.stdout) + error_msg += f"\nLocal path has its origin defined as: {clean_local_remote_url}" + raise EnvironmentError(error_msg) + + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + def git_config_username_and_email(self, git_user: Optional[str] = None, git_email: Optional[str] = None): + """ + Sets git username and email (only in the current repo). + + Args: + git_user (`str`, *optional*): + The username to register through `git`. + git_email (`str`, *optional*): + The email to register through `git`. + """ + try: + if git_user is not None: + run_subprocess("git config user.name".split() + [git_user], self.local_dir) + + if git_email is not None: + run_subprocess(f"git config user.email {git_email}".split(), self.local_dir) + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + def git_credential_helper_store(self): + """ + Sets the git credential helper to `store` + """ + try: + run_subprocess("git config credential.helper store", self.local_dir) + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + def git_head_hash(self) -> str: + """ + Get commit sha on top of HEAD. + + Returns: + `str`: The current checked out commit SHA. + """ + try: + p = run_subprocess("git rev-parse HEAD", self.local_dir) + return p.stdout.strip() + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + def git_remote_url(self) -> str: + """ + Get URL to origin remote. + + Returns: + `str`: The URL of the `origin` remote. + """ + try: + p = run_subprocess("git config --get remote.origin.url", self.local_dir) + url = p.stdout.strip() + # Strip basic auth info. + return re.sub(r"https://.*@", "https://", url) + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + def git_head_commit_url(self) -> str: + """ + Get URL to last commit on HEAD. We assume it's been pushed, and the url + scheme is the same one as for GitHub or HuggingFace. + + Returns: + `str`: The URL to the current checked-out commit. + """ + sha = self.git_head_hash() + url = self.git_remote_url() + if url.endswith("/"): + url = url[:-1] + return f"{url}/commit/{sha}" + + def list_deleted_files(self) -> List[str]: + """ + Returns a list of the files that are deleted in the working directory or + index. + + Returns: + `List[str]`: A list of files that have been deleted in the working + directory or index. + """ + try: + git_status = run_subprocess("git status -s", self.local_dir).stdout.strip() + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + if len(git_status) == 0: + return [] + + # Receives a status like the following + # D .gitignore + # D new_file.json + # AD new_file1.json + # ?? new_file2.json + # ?? new_file4.json + + # Strip each line of whitespaces + modified_files_statuses = [status.strip() for status in git_status.split("\n")] + + # Only keep files that are deleted using the D prefix + deleted_files_statuses = [status for status in modified_files_statuses if "D" in status.split()[0]] + + # Remove the D prefix and strip to keep only the relevant filename + deleted_files = [status.split()[-1].strip() for status in deleted_files_statuses] + + return deleted_files + + def lfs_track(self, patterns: Union[str, List[str]], filename: bool = False): + """ + Tell git-lfs to track files according to a pattern. + + Setting the `filename` argument to `True` will treat the arguments as + literal filenames, not as patterns. Any special glob characters in the + filename will be escaped when writing to the `.gitattributes` file. + + Args: + patterns (`Union[str, List[str]]`): + The pattern, or list of patterns, to track with git-lfs. + filename (`bool`, *optional*, defaults to `False`): + Whether to use the patterns as literal filenames. + """ + if isinstance(patterns, str): + patterns = [patterns] + try: + for pattern in patterns: + run_subprocess( + f"git lfs track {'--filename' if filename else ''} {pattern}", + self.local_dir, + ) + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + def lfs_untrack(self, patterns: Union[str, List[str]]): + """ + Tell git-lfs to untrack those files. + + Args: + patterns (`Union[str, List[str]]`): + The pattern, or list of patterns, to untrack with git-lfs. + """ + if isinstance(patterns, str): + patterns = [patterns] + try: + for pattern in patterns: + run_subprocess("git lfs untrack".split() + [pattern], self.local_dir) + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + def lfs_enable_largefiles(self): + """ + HF-specific. This enables upload support of files >5GB. + """ + try: + lfs_config = "git config lfs.customtransfer.multipart" + run_subprocess(f"{lfs_config}.path hf", self.local_dir) + run_subprocess( + f"{lfs_config}.args {LFS_MULTIPART_UPLOAD_COMMAND}", + self.local_dir, + ) + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + def auto_track_binary_files(self, pattern: str = ".") -> List[str]: + """ + Automatically track binary files with git-lfs. + + Args: + pattern (`str`, *optional*, defaults to "."): + The pattern with which to track files that are binary. + + Returns: + `List[str]`: List of filenames that are now tracked due to being + binary files + """ + files_to_be_tracked_with_lfs = [] + + deleted_files = self.list_deleted_files() + + for filename in files_to_be_staged(pattern, folder=self.local_dir): + if filename in deleted_files: + continue + + path_to_file = os.path.join(os.getcwd(), self.local_dir, filename) + + if not (is_tracked_with_lfs(path_to_file) or is_git_ignored(path_to_file)): + size_in_mb = os.path.getsize(path_to_file) / (1024 * 1024) + + if size_in_mb >= 10: + logger.warning( + "Parsing a large file to check if binary or not. Tracking large" + " files using `repository.auto_track_large_files` is" + " recommended so as to not load the full file in memory." + ) + + is_binary = is_binary_file(path_to_file) + + if is_binary: + self.lfs_track(filename) + files_to_be_tracked_with_lfs.append(filename) + + # Cleanup the .gitattributes if files were deleted + self.lfs_untrack(deleted_files) + + return files_to_be_tracked_with_lfs + + def auto_track_large_files(self, pattern: str = ".") -> List[str]: + """ + Automatically track large files (files that weigh more than 10MBs) with + git-lfs. + + Args: + pattern (`str`, *optional*, defaults to "."): + The pattern with which to track files that are above 10MBs. + + Returns: + `List[str]`: List of filenames that are now tracked due to their + size. + """ + files_to_be_tracked_with_lfs = [] + + deleted_files = self.list_deleted_files() + + for filename in files_to_be_staged(pattern, folder=self.local_dir): + if filename in deleted_files: + continue + + path_to_file = os.path.join(os.getcwd(), self.local_dir, filename) + size_in_mb = os.path.getsize(path_to_file) / (1024 * 1024) + + if size_in_mb >= 10 and not is_tracked_with_lfs(path_to_file) and not is_git_ignored(path_to_file): + self.lfs_track(filename) + files_to_be_tracked_with_lfs.append(filename) + + # Cleanup the .gitattributes if files were deleted + self.lfs_untrack(deleted_files) + + return files_to_be_tracked_with_lfs + + def lfs_prune(self, recent=False): + """ + git lfs prune + + Args: + recent (`bool`, *optional*, defaults to `False`): + Whether to prune files even if they were referenced by recent + commits. See the following + [link](https://github.com/git-lfs/git-lfs/blob/f3d43f0428a84fc4f1e5405b76b5a73ec2437e65/docs/man/git-lfs-prune.1.ronn#recent-files) + for more information. + """ + try: + with _lfs_log_progress(): + result = run_subprocess(f"git lfs prune {'--recent' if recent else ''}", self.local_dir) + logger.info(result.stdout) + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + def git_pull(self, rebase: bool = False, lfs: bool = False): + """ + git pull + + Args: + rebase (`bool`, *optional*, defaults to `False`): + Whether to rebase the current branch on top of the upstream + branch after fetching. + lfs (`bool`, *optional*, defaults to `False`): + Whether to fetch the LFS files too. This option only changes the + behavior when a repository was cloned without fetching the LFS + files; calling `repo.git_pull(lfs=True)` will then fetch the LFS + file from the remote repository. + """ + command = "git pull" if not lfs else "git lfs pull" + if rebase: + command += " --rebase" + try: + with _lfs_log_progress(): + result = run_subprocess(command, self.local_dir) + logger.info(result.stdout) + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + def git_add(self, pattern: str = ".", auto_lfs_track: bool = False): + """ + git add + + Setting the `auto_lfs_track` parameter to `True` will automatically + track files that are larger than 10MB with `git-lfs`. + + Args: + pattern (`str`, *optional*, defaults to "."): + The pattern with which to add files to staging. + auto_lfs_track (`bool`, *optional*, defaults to `False`): + Whether to automatically track large and binary files with + git-lfs. Any file over 10MB in size, or in binary format, will + be automatically tracked. + """ + if auto_lfs_track: + # Track files according to their size (>=10MB) + tracked_files = self.auto_track_large_files(pattern) + + # Read the remaining files and track them if they're binary + tracked_files.extend(self.auto_track_binary_files(pattern)) + + if tracked_files: + logger.warning( + f"Adding files tracked by Git LFS: {tracked_files}. This may take a" + " bit of time if the files are large." + ) + + try: + result = run_subprocess("git add -v".split() + [pattern], self.local_dir) + logger.info(f"Adding to index:\n{result.stdout}\n") + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + def git_commit(self, commit_message: str = "commit files to HF hub"): + """ + git commit + + Args: + commit_message (`str`, *optional*, defaults to "commit files to HF hub"): + The message attributed to the commit. + """ + try: + result = run_subprocess("git commit -v -m".split() + [commit_message], self.local_dir) + logger.info(f"Committed:\n{result.stdout}\n") + except subprocess.CalledProcessError as exc: + if len(exc.stderr) > 0: + raise EnvironmentError(exc.stderr) + else: + raise EnvironmentError(exc.stdout) + + def git_push( + self, + upstream: Optional[str] = None, + blocking: bool = True, + auto_lfs_prune: bool = False, + ) -> Union[str, Tuple[str, CommandInProgress]]: + """ + git push + + If used without setting `blocking`, will return url to commit on remote + repo. If used with `blocking=True`, will return a tuple containing the + url to commit and the command object to follow for information about the + process. + + Args: + upstream (`str`, *optional*): + Upstream to which this should push. If not specified, will push + to the lastly defined upstream or to the default one (`origin + main`). + blocking (`bool`, *optional*, defaults to `True`): + Whether the function should return only when the push has + finished. Setting this to `False` will return an + `CommandInProgress` object which has an `is_done` property. This + property will be set to `True` when the push is finished. + auto_lfs_prune (`bool`, *optional*, defaults to `False`): + Whether to automatically prune files once they have been pushed + to the remote. + """ + command = "git push" + + if upstream: + command += f" --set-upstream {upstream}" + + number_of_commits = commits_to_push(self.local_dir, upstream) + + if number_of_commits > 1: + logger.warning(f"Several commits ({number_of_commits}) will be pushed upstream.") + if blocking: + logger.warning("The progress bars may be unreliable.") + + try: + with _lfs_log_progress(): + process = subprocess.Popen( + command.split(), + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, + encoding="utf-8", + cwd=self.local_dir, + ) + + if blocking: + stdout, stderr = process.communicate() + return_code = process.poll() + process.kill() + + if len(stderr): + logger.warning(stderr) + + if return_code: + raise subprocess.CalledProcessError(return_code, process.args, output=stdout, stderr=stderr) + + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + if not blocking: + + def status_method(): + status = process.poll() + if status is None: + return -1 + else: + return status + + command_in_progress = CommandInProgress( + "push", + is_done_method=lambda: process.poll() is not None, + status_method=status_method, + process=process, + post_method=self.lfs_prune if auto_lfs_prune else None, + ) + + self.command_queue.append(command_in_progress) + + return self.git_head_commit_url(), command_in_progress + + if auto_lfs_prune: + self.lfs_prune() + + return self.git_head_commit_url() + + def git_checkout(self, revision: str, create_branch_ok: bool = False): + """ + git checkout a given revision + + Specifying `create_branch_ok` to `True` will create the branch to the + given revision if that revision doesn't exist. + + Args: + revision (`str`): + The revision to checkout. + create_branch_ok (`str`, *optional*, defaults to `False`): + Whether creating a branch named with the `revision` passed at + the current checked-out reference if `revision` isn't an + existing revision is allowed. + """ + try: + result = run_subprocess(f"git checkout {revision}", self.local_dir) + logger.warning(f"Checked out {revision} from {self.current_branch}.") + logger.warning(result.stdout) + except subprocess.CalledProcessError as exc: + if not create_branch_ok: + raise EnvironmentError(exc.stderr) + else: + try: + result = run_subprocess(f"git checkout -b {revision}", self.local_dir) + logger.warning( + f"Revision `{revision}` does not exist. Created and checked out branch `{revision}`." + ) + logger.warning(result.stdout) + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + def tag_exists(self, tag_name: str, remote: Optional[str] = None) -> bool: + """ + Check if a tag exists or not. + + Args: + tag_name (`str`): + The name of the tag to check. + remote (`str`, *optional*): + Whether to check if the tag exists on a remote. This parameter + should be the identifier of the remote. + + Returns: + `bool`: Whether the tag exists. + """ + if remote: + try: + result = run_subprocess(f"git ls-remote origin refs/tags/{tag_name}", self.local_dir).stdout.strip() + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + return len(result) != 0 + else: + try: + git_tags = run_subprocess("git tag", self.local_dir).stdout.strip() + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + git_tags = git_tags.split("\n") + return tag_name in git_tags + + def delete_tag(self, tag_name: str, remote: Optional[str] = None) -> bool: + """ + Delete a tag, both local and remote, if it exists + + Args: + tag_name (`str`): + The tag name to delete. + remote (`str`, *optional*): + The remote on which to delete the tag. + + Returns: + `bool`: `True` if deleted, `False` if the tag didn't exist. + If remote is not passed, will just be updated locally + """ + delete_locally = True + delete_remotely = True + + if not self.tag_exists(tag_name): + delete_locally = False + + if not self.tag_exists(tag_name, remote=remote): + delete_remotely = False + + if delete_locally: + try: + run_subprocess(["git", "tag", "-d", tag_name], self.local_dir).stdout.strip() + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + if remote and delete_remotely: + try: + run_subprocess(f"git push {remote} --delete {tag_name}", self.local_dir).stdout.strip() + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + return True + + def add_tag(self, tag_name: str, message: Optional[str] = None, remote: Optional[str] = None): + """ + Add a tag at the current head and push it + + If remote is None, will just be updated locally + + If no message is provided, the tag will be lightweight. if a message is + provided, the tag will be annotated. + + Args: + tag_name (`str`): + The name of the tag to be added. + message (`str`, *optional*): + The message that accompanies the tag. The tag will turn into an + annotated tag if a message is passed. + remote (`str`, *optional*): + The remote on which to add the tag. + """ + if message: + tag_args = ["git", "tag", "-a", tag_name, "-m", message] + else: + tag_args = ["git", "tag", tag_name] + + try: + run_subprocess(tag_args, self.local_dir).stdout.strip() + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + if remote: + try: + run_subprocess(f"git push {remote} {tag_name}", self.local_dir).stdout.strip() + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + def is_repo_clean(self) -> bool: + """ + Return whether or not the git status is clean or not + + Returns: + `bool`: `True` if the git status is clean, `False` otherwise. + """ + try: + git_status = run_subprocess("git status --porcelain", self.local_dir).stdout.strip() + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + return len(git_status) == 0 + + def push_to_hub( + self, + commit_message: str = "commit files to HF hub", + blocking: bool = True, + clean_ok: bool = True, + auto_lfs_prune: bool = False, + ) -> Union[None, str, Tuple[str, CommandInProgress]]: + """ + Helper to add, commit, and push files to remote repository on the + HuggingFace Hub. Will automatically track large files (>10MB). + + Args: + commit_message (`str`): + Message to use for the commit. + blocking (`bool`, *optional*, defaults to `True`): + Whether the function should return only when the `git push` has + finished. + clean_ok (`bool`, *optional*, defaults to `True`): + If True, this function will return None if the repo is + untouched. Default behavior is to fail because the git command + fails. + auto_lfs_prune (`bool`, *optional*, defaults to `False`): + Whether to automatically prune files once they have been pushed + to the remote. + """ + if clean_ok and self.is_repo_clean(): + logger.info("Repo currently clean. Ignoring push_to_hub") + return None + self.git_add(auto_lfs_track=True) + self.git_commit(commit_message) + return self.git_push( + upstream=f"origin {self.current_branch}", + blocking=blocking, + auto_lfs_prune=auto_lfs_prune, + ) + + @contextmanager + def commit( + self, + commit_message: str, + branch: Optional[str] = None, + track_large_files: bool = True, + blocking: bool = True, + auto_lfs_prune: bool = False, + ): + """ + Context manager utility to handle committing to a repository. This + automatically tracks large files (>10Mb) with git-lfs. Set the + `track_large_files` argument to `False` if you wish to ignore that + behavior. + + Args: + commit_message (`str`): + Message to use for the commit. + branch (`str`, *optional*): + The branch on which the commit will appear. This branch will be + checked-out before any operation. + track_large_files (`bool`, *optional*, defaults to `True`): + Whether to automatically track large files or not. Will do so by + default. + blocking (`bool`, *optional*, defaults to `True`): + Whether the function should return only when the `git push` has + finished. + auto_lfs_prune (`bool`, defaults to `True`): + Whether to automatically prune files once they have been pushed + to the remote. + + Examples: + + ```python + >>> with Repository( + ... "text-files", + ... clone_from="/text-files", + ... token=True, + >>> ).commit("My first file :)"): + ... with open("file.txt", "w+") as f: + ... f.write(json.dumps({"hey": 8})) + + >>> import torch + + >>> model = torch.nn.Transformer() + >>> with Repository( + ... "torch-model", + ... clone_from="/torch-model", + ... token=True, + >>> ).commit("My cool model :)"): + ... torch.save(model.state_dict(), "model.pt") + ``` + + """ + + files_to_stage = files_to_be_staged(".", folder=self.local_dir) + + if len(files_to_stage): + files_in_msg = str(files_to_stage[:5])[:-1] + ", ...]" if len(files_to_stage) > 5 else str(files_to_stage) + logger.error( + "There exists some updated files in the local repository that are not" + f" committed: {files_in_msg}. This may lead to errors if checking out" + " a branch. These files and their modifications will be added to the" + " current commit." + ) + + if branch is not None: + self.git_checkout(branch, create_branch_ok=True) + + if is_tracked_upstream(self.local_dir): + logger.warning("Pulling changes ...") + self.git_pull(rebase=True) + else: + logger.warning(f"The current branch has no upstream branch. Will push to 'origin {self.current_branch}'") + + current_working_directory = os.getcwd() + os.chdir(os.path.join(current_working_directory, self.local_dir)) + + try: + yield self + finally: + self.git_add(auto_lfs_track=track_large_files) + + try: + self.git_commit(commit_message) + except OSError as e: + # If no changes are detected, there is nothing to commit. + if "nothing to commit" not in str(e): + raise e + + try: + self.git_push( + upstream=f"origin {self.current_branch}", + blocking=blocking, + auto_lfs_prune=auto_lfs_prune, + ) + except OSError as e: + # If no changes are detected, there is nothing to commit. + if "could not read Username" in str(e): + raise OSError("Couldn't authenticate user for push. Did you set `token` to `True`?") from e + else: + raise e + + os.chdir(current_working_directory) + + def repocard_metadata_load(self) -> Optional[Dict]: + filepath = os.path.join(self.local_dir, constants.REPOCARD_NAME) + if os.path.isfile(filepath): + return metadata_load(filepath) + return None + + def repocard_metadata_save(self, data: Dict) -> None: + return metadata_save(os.path.join(self.local_dir, constants.REPOCARD_NAME), data) + + @property + def commands_failed(self): + """ + Returns the asynchronous commands that failed. + """ + return [c for c in self.command_queue if c.status > 0] + + @property + def commands_in_progress(self): + """ + Returns the asynchronous commands that are currently in progress. + """ + return [c for c in self.command_queue if not c.is_done] + + def wait_for_commands(self): + """ + Blocking method: blocks all subsequent execution until all commands have + been processed. + """ + index = 0 + for command_failed in self.commands_failed: + logger.error(f"The {command_failed.title} command with PID {command_failed._process.pid} failed.") + logger.error(command_failed.stderr) + + while self.commands_in_progress: + if index % 10 == 0: + logger.warning( + f"Waiting for the following commands to finish before shutting down: {self.commands_in_progress}." + ) + + index += 1 + + time.sleep(1) diff --git a/.venv/lib/python3.12/site-packages/idna-3.11.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/idna-3.11.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/idna-3.11.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/idna-3.11.dist-info/METADATA b/.venv/lib/python3.12/site-packages/idna-3.11.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..7a4a4b7a7e9adbe4542d98f6b2f40e65ee64b182 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/idna-3.11.dist-info/METADATA @@ -0,0 +1,209 @@ +Metadata-Version: 2.4 +Name: idna +Version: 3.11 +Summary: Internationalized Domain Names in Applications (IDNA) +Author-email: Kim Davies +Requires-Python: >=3.8 +Description-Content-Type: text/x-rst +License-Expression: BSD-3-Clause +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: System Administrators +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 :: Internet :: Name Service (DNS) +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Utilities +License-File: LICENSE.md +Requires-Dist: ruff >= 0.6.2 ; extra == "all" +Requires-Dist: mypy >= 1.11.2 ; extra == "all" +Requires-Dist: pytest >= 8.3.2 ; extra == "all" +Requires-Dist: flake8 >= 7.1.1 ; extra == "all" +Project-URL: Changelog, https://github.com/kjd/idna/blob/master/HISTORY.rst +Project-URL: Issue tracker, https://github.com/kjd/idna/issues +Project-URL: Source, https://github.com/kjd/idna +Provides-Extra: all + +Internationalized Domain Names in Applications (IDNA) +===================================================== + +Support for `Internationalized Domain Names in +Applications (IDNA) `_ +and `Unicode IDNA Compatibility Processing +`_. + +The latest versions of these standards supplied here provide +more comprehensive language coverage and reduce the potential of +allowing domains with known security vulnerabilities. This library +is a suitable replacement for the “encodings.idna” +module that comes with the Python standard library, but which +only supports an older superseded IDNA specification from 2003. + +Basic functions are simply executed: + +.. code-block:: pycon + + >>> import idna + >>> idna.encode('ドメイン.テスト') + b'xn--eckwd4c7c.xn--zckzah' + >>> print(idna.decode('xn--eckwd4c7c.xn--zckzah')) + ドメイン.テスト + + +Installation +------------ + +This package is available for installation from PyPI via the +typical mechanisms, such as: + +.. code-block:: bash + + $ python3 -m pip install idna + + +Usage +----- + +For typical usage, the ``encode`` and ``decode`` functions will take a +domain name argument and perform a conversion to ASCII compatible encoding +(known as A-labels), or to Unicode strings (known as U-labels) +respectively. + +.. code-block:: pycon + + >>> import idna + >>> idna.encode('ドメイン.テスト') + b'xn--eckwd4c7c.xn--zckzah' + >>> print(idna.decode('xn--eckwd4c7c.xn--zckzah')) + ドメイン.テスト + +Conversions can be applied at a per-label basis using the ``ulabel`` or +``alabel`` functions if necessary: + +.. code-block:: pycon + + >>> idna.alabel('测试') + b'xn--0zwm56d' + + +Compatibility Mapping (UTS #46) ++++++++++++++++++++++++++++++++ + +This library provides support for `Unicode IDNA Compatibility +Processing `_ which normalizes input from +different potential ways a user may input a domain prior to performing the IDNA +conversion operations. This functionality, known as a +`mapping `_, is considered by the +specification to be a local user-interface issue distinct from IDNA +conversion functionality. + +For example, “Königsgäßchen” is not a permissible label as *LATIN +CAPITAL LETTER K* is not allowed (nor are capital letters in general). +UTS 46 will convert this into lower case prior to applying the IDNA +conversion. + +.. code-block:: pycon + + >>> import idna + >>> idna.encode('Königsgäßchen') + ... + idna.core.InvalidCodepoint: Codepoint U+004B at position 1 of 'Königsgäßchen' not allowed + >>> idna.encode('Königsgäßchen', uts46=True) + b'xn--knigsgchen-b4a3dun' + >>> print(idna.decode('xn--knigsgchen-b4a3dun')) + königsgäßchen + + +Exceptions +---------- + +All errors raised during the conversion following the specification +should raise an exception derived from the ``idna.IDNAError`` base +class. + +More specific exceptions that may be generated as ``idna.IDNABidiError`` +when the error reflects an illegal combination of left-to-right and +right-to-left characters in a label; ``idna.InvalidCodepoint`` when +a specific codepoint is an illegal character in an IDN label (i.e. +INVALID); and ``idna.InvalidCodepointContext`` when the codepoint is +illegal based on its position in the string (i.e. it is CONTEXTO or CONTEXTJ +but the contextual requirements are not satisfied.) + +Building and Diagnostics +------------------------ + +The IDNA and UTS 46 functionality relies upon pre-calculated lookup +tables for performance. These tables are derived from computing against +eligibility criteria in the respective standards using the command-line +script ``tools/idna-data``. + +This tool will fetch relevant codepoint data from the Unicode repository +and perform the required calculations to identify eligibility. There are +three main modes: + +* ``idna-data make-libdata``. Generates ``idnadata.py`` and + ``uts46data.py``, the pre-calculated lookup tables used for IDNA and + UTS 46 conversions. Implementers who wish to track this library against + a different Unicode version may use this tool to manually generate a + different version of the ``idnadata.py`` and ``uts46data.py`` files. + +* ``idna-data make-table``. Generate a table of the IDNA disposition + (e.g. PVALID, CONTEXTJ, CONTEXTO) in the format found in Appendix + B.1 of RFC 5892 and the pre-computed tables published by `IANA + `_. + +* ``idna-data U+0061``. Prints debugging output on the various + properties associated with an individual Unicode codepoint (in this + case, U+0061), that are used to assess the IDNA and UTS 46 status of a + codepoint. This is helpful in debugging or analysis. + +The tool accepts a number of arguments, described using ``idna-data +-h``. Most notably, the ``--version`` argument allows the specification +of the version of Unicode to be used in computing the table data. For +example, ``idna-data --version 9.0.0 make-libdata`` will generate +library data against Unicode 9.0.0. + + +Additional Notes +---------------- + +* **Packages**. The latest tagged release version is published in the + `Python Package Index `_. + +* **Version support**. This library supports Python 3.8 and higher. + As this library serves as a low-level toolkit for a variety of + applications, many of which strive for broad compatibility with older + Python versions, there is no rush to remove older interpreter support. + Support for older versions are likely to be removed from new releases + as automated tests can no longer easily be run, i.e. once the Python + version is officially end-of-life. + +* **Testing**. The library has a test suite based on each rule of the + IDNA specification, as well as tests that are provided as part of the + Unicode Technical Standard 46, `Unicode IDNA Compatibility Processing + `_. + +* **Emoji**. It is an occasional request to support emoji domains in + this library. Encoding of symbols like emoji is expressly prohibited by + the technical standard IDNA 2008 and emoji domains are broadly phased + out across the domain industry due to associated security risks. For + now, applications that need to support these non-compliant labels + may wish to consider trying the encode/decode operation in this library + first, and then falling back to using `encodings.idna`. See `the Github + project `_ for more discussion. + +* **Transitional processing**. Unicode 16.0.0 removed transitional + processing so the `transitional` argument for the encode() method + no longer has any effect and will be removed at a later date. + diff --git a/.venv/lib/python3.12/site-packages/idna-3.11.dist-info/RECORD b/.venv/lib/python3.12/site-packages/idna-3.11.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..50aa613863fc3ee074f263ca186d5f7141b95841 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/idna-3.11.dist-info/RECORD @@ -0,0 +1,15 @@ +idna-3.11.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +idna-3.11.dist-info/METADATA,sha256=fCwSww9SuiN8TIHllFSASUQCW55hAs8dzKnr9RaEEbA,8378 +idna-3.11.dist-info/RECORD,, +idna-3.11.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +idna-3.11.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82 +idna-3.11.dist-info/licenses/LICENSE.md,sha256=t6M2q_OwThgOwGXN0W5wXQeeHMehT5EKpukYfza5zYc,1541 +idna/__init__.py,sha256=MPqNDLZbXqGaNdXxAFhiqFPKEQXju2jNQhCey6-5eJM,868 +idna/codec.py,sha256=M2SGWN7cs_6B32QmKTyTN6xQGZeYQgQ2wiX3_DR6loE,3438 +idna/compat.py,sha256=RzLy6QQCdl9784aFhb2EX9EKGCJjg0P3PilGdeXXcx8,316 +idna/core.py,sha256=P26_XVycuMTZ1R2mNK1ZREVzM5mvTzdabBXfyZVU1Lc,13246 +idna/idnadata.py,sha256=SG8jhaGE53iiD6B49pt2pwTv_UvClciWE-N54oR2p4U,79623 +idna/intranges.py,sha256=amUtkdhYcQG8Zr-CoMM_kVRacxkivC1WgxN1b63KKdU,1898 +idna/package_data.py,sha256=_CUavOxobnbyNG2FLyHoN8QHP3QM9W1tKuw7eq9QwBk,21 +idna/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +idna/uts46data.py,sha256=H9J35VkD0F9L9mKOqjeNGd2A-Va6FlPoz6Jz4K7h-ps,243725 diff --git a/.venv/lib/python3.12/site-packages/idna-3.11.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/idna-3.11.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/idna-3.11.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/idna-3.11.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..d8b9936dad9ab2513fa6979f411560d3b6b57e37 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/idna-3.11.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.12.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/idna/__init__.py b/.venv/lib/python3.12/site-packages/idna/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..cfdc030a751b089fc7e38fc88093b791605d501d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/idna/__init__.py @@ -0,0 +1,45 @@ +from .core import ( + IDNABidiError, + IDNAError, + InvalidCodepoint, + InvalidCodepointContext, + alabel, + check_bidi, + check_hyphen_ok, + check_initial_combiner, + check_label, + check_nfc, + decode, + encode, + ulabel, + uts46_remap, + valid_contextj, + valid_contexto, + valid_label_length, + valid_string_length, +) +from .intranges import intranges_contain +from .package_data import __version__ + +__all__ = [ + "__version__", + "IDNABidiError", + "IDNAError", + "InvalidCodepoint", + "InvalidCodepointContext", + "alabel", + "check_bidi", + "check_hyphen_ok", + "check_initial_combiner", + "check_label", + "check_nfc", + "decode", + "encode", + "intranges_contain", + "ulabel", + "uts46_remap", + "valid_contextj", + "valid_contexto", + "valid_label_length", + "valid_string_length", +] diff --git a/.venv/lib/python3.12/site-packages/idna/codec.py b/.venv/lib/python3.12/site-packages/idna/codec.py new file mode 100644 index 0000000000000000000000000000000000000000..cbc2e4ff4ec3e2318d47615bab44ea0ca3dba978 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/idna/codec.py @@ -0,0 +1,122 @@ +import codecs +import re +from typing import Any, Optional, Tuple + +from .core import IDNAError, alabel, decode, encode, ulabel + +_unicode_dots_re = re.compile("[\u002e\u3002\uff0e\uff61]") + + +class Codec(codecs.Codec): + def encode(self, data: str, errors: str = "strict") -> Tuple[bytes, int]: + if errors != "strict": + raise IDNAError('Unsupported error handling "{}"'.format(errors)) + + if not data: + return b"", 0 + + return encode(data), len(data) + + def decode(self, data: bytes, errors: str = "strict") -> Tuple[str, int]: + if errors != "strict": + raise IDNAError('Unsupported error handling "{}"'.format(errors)) + + if not data: + return "", 0 + + return decode(data), len(data) + + +class IncrementalEncoder(codecs.BufferedIncrementalEncoder): + def _buffer_encode(self, data: str, errors: str, final: bool) -> Tuple[bytes, int]: + if errors != "strict": + raise IDNAError('Unsupported error handling "{}"'.format(errors)) + + if not data: + return b"", 0 + + labels = _unicode_dots_re.split(data) + trailing_dot = b"" + if labels: + if not labels[-1]: + trailing_dot = b"." + del labels[-1] + elif not final: + # Keep potentially unfinished label until the next call + del labels[-1] + if labels: + trailing_dot = b"." + + result = [] + size = 0 + for label in labels: + result.append(alabel(label)) + if size: + size += 1 + size += len(label) + + # Join with U+002E + result_bytes = b".".join(result) + trailing_dot + size += len(trailing_dot) + return result_bytes, size + + +class IncrementalDecoder(codecs.BufferedIncrementalDecoder): + def _buffer_decode(self, data: Any, errors: str, final: bool) -> Tuple[str, int]: + if errors != "strict": + raise IDNAError('Unsupported error handling "{}"'.format(errors)) + + if not data: + return ("", 0) + + if not isinstance(data, str): + data = str(data, "ascii") + + labels = _unicode_dots_re.split(data) + trailing_dot = "" + if labels: + if not labels[-1]: + trailing_dot = "." + del labels[-1] + elif not final: + # Keep potentially unfinished label until the next call + del labels[-1] + if labels: + trailing_dot = "." + + result = [] + size = 0 + for label in labels: + result.append(ulabel(label)) + if size: + size += 1 + size += len(label) + + result_str = ".".join(result) + trailing_dot + size += len(trailing_dot) + return (result_str, size) + + +class StreamWriter(Codec, codecs.StreamWriter): + pass + + +class StreamReader(Codec, codecs.StreamReader): + pass + + +def search_function(name: str) -> Optional[codecs.CodecInfo]: + if name != "idna2008": + return None + return codecs.CodecInfo( + name=name, + encode=Codec().encode, + decode=Codec().decode, # type: ignore + incrementalencoder=IncrementalEncoder, + incrementaldecoder=IncrementalDecoder, + streamwriter=StreamWriter, + streamreader=StreamReader, + ) + + +codecs.register(search_function) diff --git a/.venv/lib/python3.12/site-packages/idna/compat.py b/.venv/lib/python3.12/site-packages/idna/compat.py new file mode 100644 index 0000000000000000000000000000000000000000..1df9f2a70e6815908f2784e88897a9a359eef84c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/idna/compat.py @@ -0,0 +1,15 @@ +from typing import Any, Union + +from .core import decode, encode + + +def ToASCII(label: str) -> bytes: + return encode(label) + + +def ToUnicode(label: Union[bytes, bytearray]) -> str: + return decode(label) + + +def nameprep(s: Any) -> None: + raise NotImplementedError("IDNA 2008 does not utilise nameprep protocol") diff --git a/.venv/lib/python3.12/site-packages/idna/core.py b/.venv/lib/python3.12/site-packages/idna/core.py new file mode 100644 index 0000000000000000000000000000000000000000..8177bf7a324f9f54a29e41e867f5d56f2dd0a924 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/idna/core.py @@ -0,0 +1,437 @@ +import bisect +import re +import unicodedata +from typing import Optional, Union + +from . import idnadata +from .intranges import intranges_contain + +_virama_combining_class = 9 +_alabel_prefix = b"xn--" +_unicode_dots_re = re.compile("[\u002e\u3002\uff0e\uff61]") + + +class IDNAError(UnicodeError): + """Base exception for all IDNA-encoding related problems""" + + pass + + +class IDNABidiError(IDNAError): + """Exception when bidirectional requirements are not satisfied""" + + pass + + +class InvalidCodepoint(IDNAError): + """Exception when a disallowed or unallocated codepoint is used""" + + pass + + +class InvalidCodepointContext(IDNAError): + """Exception when the codepoint is not valid in the context it is used""" + + pass + + +def _combining_class(cp: int) -> int: + v = unicodedata.combining(chr(cp)) + if v == 0: + if not unicodedata.name(chr(cp)): + raise ValueError("Unknown character in unicodedata") + return v + + +def _is_script(cp: str, script: str) -> bool: + return intranges_contain(ord(cp), idnadata.scripts[script]) + + +def _punycode(s: str) -> bytes: + return s.encode("punycode") + + +def _unot(s: int) -> str: + return "U+{:04X}".format(s) + + +def valid_label_length(label: Union[bytes, str]) -> bool: + if len(label) > 63: + return False + return True + + +def valid_string_length(label: Union[bytes, str], trailing_dot: bool) -> bool: + if len(label) > (254 if trailing_dot else 253): + return False + return True + + +def check_bidi(label: str, check_ltr: bool = False) -> bool: + # Bidi rules should only be applied if string contains RTL characters + bidi_label = False + for idx, cp in enumerate(label, 1): + direction = unicodedata.bidirectional(cp) + if direction == "": + # String likely comes from a newer version of Unicode + raise IDNABidiError("Unknown directionality in label {} at position {}".format(repr(label), idx)) + if direction in ["R", "AL", "AN"]: + bidi_label = True + if not bidi_label and not check_ltr: + return True + + # Bidi rule 1 + direction = unicodedata.bidirectional(label[0]) + if direction in ["R", "AL"]: + rtl = True + elif direction == "L": + rtl = False + else: + raise IDNABidiError("First codepoint in label {} must be directionality L, R or AL".format(repr(label))) + + valid_ending = False + number_type: Optional[str] = None + for idx, cp in enumerate(label, 1): + direction = unicodedata.bidirectional(cp) + + if rtl: + # Bidi rule 2 + if direction not in [ + "R", + "AL", + "AN", + "EN", + "ES", + "CS", + "ET", + "ON", + "BN", + "NSM", + ]: + raise IDNABidiError("Invalid direction for codepoint at position {} in a right-to-left label".format(idx)) + # Bidi rule 3 + if direction in ["R", "AL", "EN", "AN"]: + valid_ending = True + elif direction != "NSM": + valid_ending = False + # Bidi rule 4 + if direction in ["AN", "EN"]: + if not number_type: + number_type = direction + else: + if number_type != direction: + raise IDNABidiError("Can not mix numeral types in a right-to-left label") + else: + # Bidi rule 5 + if direction not in ["L", "EN", "ES", "CS", "ET", "ON", "BN", "NSM"]: + raise IDNABidiError("Invalid direction for codepoint at position {} in a left-to-right label".format(idx)) + # Bidi rule 6 + if direction in ["L", "EN"]: + valid_ending = True + elif direction != "NSM": + valid_ending = False + + if not valid_ending: + raise IDNABidiError("Label ends with illegal codepoint directionality") + + return True + + +def check_initial_combiner(label: str) -> bool: + if unicodedata.category(label[0])[0] == "M": + raise IDNAError("Label begins with an illegal combining character") + return True + + +def check_hyphen_ok(label: str) -> bool: + if label[2:4] == "--": + raise IDNAError("Label has disallowed hyphens in 3rd and 4th position") + if label[0] == "-" or label[-1] == "-": + raise IDNAError("Label must not start or end with a hyphen") + return True + + +def check_nfc(label: str) -> None: + if unicodedata.normalize("NFC", label) != label: + raise IDNAError("Label must be in Normalization Form C") + + +def valid_contextj(label: str, pos: int) -> bool: + cp_value = ord(label[pos]) + + if cp_value == 0x200C: + if pos > 0: + if _combining_class(ord(label[pos - 1])) == _virama_combining_class: + return True + + ok = False + for i in range(pos - 1, -1, -1): + joining_type = idnadata.joining_types.get(ord(label[i])) + if joining_type == ord("T"): + continue + elif joining_type in [ord("L"), ord("D")]: + ok = True + break + else: + break + + if not ok: + return False + + ok = False + for i in range(pos + 1, len(label)): + joining_type = idnadata.joining_types.get(ord(label[i])) + if joining_type == ord("T"): + continue + elif joining_type in [ord("R"), ord("D")]: + ok = True + break + else: + break + return ok + + if cp_value == 0x200D: + if pos > 0: + if _combining_class(ord(label[pos - 1])) == _virama_combining_class: + return True + return False + + else: + return False + + +def valid_contexto(label: str, pos: int, exception: bool = False) -> bool: + cp_value = ord(label[pos]) + + if cp_value == 0x00B7: + if 0 < pos < len(label) - 1: + if ord(label[pos - 1]) == 0x006C and ord(label[pos + 1]) == 0x006C: + return True + return False + + elif cp_value == 0x0375: + if pos < len(label) - 1 and len(label) > 1: + return _is_script(label[pos + 1], "Greek") + return False + + elif cp_value == 0x05F3 or cp_value == 0x05F4: + if pos > 0: + return _is_script(label[pos - 1], "Hebrew") + return False + + elif cp_value == 0x30FB: + for cp in label: + if cp == "\u30fb": + continue + if _is_script(cp, "Hiragana") or _is_script(cp, "Katakana") or _is_script(cp, "Han"): + return True + return False + + elif 0x660 <= cp_value <= 0x669: + for cp in label: + if 0x6F0 <= ord(cp) <= 0x06F9: + return False + return True + + elif 0x6F0 <= cp_value <= 0x6F9: + for cp in label: + if 0x660 <= ord(cp) <= 0x0669: + return False + return True + + return False + + +def check_label(label: Union[str, bytes, bytearray]) -> None: + if isinstance(label, (bytes, bytearray)): + label = label.decode("utf-8") + if len(label) == 0: + raise IDNAError("Empty Label") + + check_nfc(label) + check_hyphen_ok(label) + check_initial_combiner(label) + + for pos, cp in enumerate(label): + cp_value = ord(cp) + if intranges_contain(cp_value, idnadata.codepoint_classes["PVALID"]): + continue + elif intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTJ"]): + try: + if not valid_contextj(label, pos): + raise InvalidCodepointContext( + "Joiner {} not allowed at position {} in {}".format(_unot(cp_value), pos + 1, repr(label)) + ) + except ValueError: + raise IDNAError( + "Unknown codepoint adjacent to joiner {} at position {} in {}".format( + _unot(cp_value), pos + 1, repr(label) + ) + ) + elif intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTO"]): + if not valid_contexto(label, pos): + raise InvalidCodepointContext( + "Codepoint {} not allowed at position {} in {}".format(_unot(cp_value), pos + 1, repr(label)) + ) + else: + raise InvalidCodepoint( + "Codepoint {} at position {} of {} not allowed".format(_unot(cp_value), pos + 1, repr(label)) + ) + + check_bidi(label) + + +def alabel(label: str) -> bytes: + try: + label_bytes = label.encode("ascii") + ulabel(label_bytes) + if not valid_label_length(label_bytes): + raise IDNAError("Label too long") + return label_bytes + except UnicodeEncodeError: + pass + + check_label(label) + label_bytes = _alabel_prefix + _punycode(label) + + if not valid_label_length(label_bytes): + raise IDNAError("Label too long") + + return label_bytes + + +def ulabel(label: Union[str, bytes, bytearray]) -> str: + if not isinstance(label, (bytes, bytearray)): + try: + label_bytes = label.encode("ascii") + except UnicodeEncodeError: + check_label(label) + return label + else: + label_bytes = bytes(label) + + label_bytes = label_bytes.lower() + if label_bytes.startswith(_alabel_prefix): + label_bytes = label_bytes[len(_alabel_prefix) :] + if not label_bytes: + raise IDNAError("Malformed A-label, no Punycode eligible content found") + if label_bytes.decode("ascii")[-1] == "-": + raise IDNAError("A-label must not end with a hyphen") + else: + check_label(label_bytes) + return label_bytes.decode("ascii") + + try: + label = label_bytes.decode("punycode") + except UnicodeError: + raise IDNAError("Invalid A-label") + check_label(label) + return label + + +def uts46_remap(domain: str, std3_rules: bool = True, transitional: bool = False) -> str: + """Re-map the characters in the string according to UTS46 processing.""" + from .uts46data import uts46data + + output = "" + + for pos, char in enumerate(domain): + code_point = ord(char) + try: + uts46row = uts46data[code_point if code_point < 256 else bisect.bisect_left(uts46data, (code_point, "Z")) - 1] + status = uts46row[1] + replacement: Optional[str] = None + if len(uts46row) == 3: + replacement = uts46row[2] + if ( + status == "V" + or (status == "D" and not transitional) + or (status == "3" and not std3_rules and replacement is None) + ): + output += char + elif replacement is not None and ( + status == "M" or (status == "3" and not std3_rules) or (status == "D" and transitional) + ): + output += replacement + elif status != "I": + raise IndexError() + except IndexError: + raise InvalidCodepoint( + "Codepoint {} not allowed at position {} in {}".format(_unot(code_point), pos + 1, repr(domain)) + ) + + return unicodedata.normalize("NFC", output) + + +def encode( + s: Union[str, bytes, bytearray], + strict: bool = False, + uts46: bool = False, + std3_rules: bool = False, + transitional: bool = False, +) -> bytes: + if not isinstance(s, str): + try: + s = str(s, "ascii") + except UnicodeDecodeError: + raise IDNAError("should pass a unicode string to the function rather than a byte string.") + if uts46: + s = uts46_remap(s, std3_rules, transitional) + trailing_dot = False + result = [] + if strict: + labels = s.split(".") + else: + labels = _unicode_dots_re.split(s) + if not labels or labels == [""]: + raise IDNAError("Empty domain") + if labels[-1] == "": + del labels[-1] + trailing_dot = True + for label in labels: + s = alabel(label) + if s: + result.append(s) + else: + raise IDNAError("Empty label") + if trailing_dot: + result.append(b"") + s = b".".join(result) + if not valid_string_length(s, trailing_dot): + raise IDNAError("Domain too long") + return s + + +def decode( + s: Union[str, bytes, bytearray], + strict: bool = False, + uts46: bool = False, + std3_rules: bool = False, +) -> str: + try: + if not isinstance(s, str): + s = str(s, "ascii") + except UnicodeDecodeError: + raise IDNAError("Invalid ASCII in A-label") + if uts46: + s = uts46_remap(s, std3_rules, False) + trailing_dot = False + result = [] + if not strict: + labels = _unicode_dots_re.split(s) + else: + labels = s.split(".") + if not labels or labels == [""]: + raise IDNAError("Empty domain") + if not labels[-1]: + del labels[-1] + trailing_dot = True + for label in labels: + s = ulabel(label) + if s: + result.append(s) + else: + raise IDNAError("Empty label") + if trailing_dot: + result.append("") + return ".".join(result) diff --git a/.venv/lib/python3.12/site-packages/idna/idnadata.py b/.venv/lib/python3.12/site-packages/idna/idnadata.py new file mode 100644 index 0000000000000000000000000000000000000000..ded47cae0b16977aae69f3895ecfe8b8980f58d0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/idna/idnadata.py @@ -0,0 +1,4309 @@ +# This file is automatically generated by tools/idna-data + +__version__ = "16.0.0" + +scripts = { + "Greek": ( + 0x37000000374, + 0x37500000378, + 0x37A0000037E, + 0x37F00000380, + 0x38400000385, + 0x38600000387, + 0x3880000038B, + 0x38C0000038D, + 0x38E000003A2, + 0x3A3000003E2, + 0x3F000000400, + 0x1D2600001D2B, + 0x1D5D00001D62, + 0x1D6600001D6B, + 0x1DBF00001DC0, + 0x1F0000001F16, + 0x1F1800001F1E, + 0x1F2000001F46, + 0x1F4800001F4E, + 0x1F5000001F58, + 0x1F5900001F5A, + 0x1F5B00001F5C, + 0x1F5D00001F5E, + 0x1F5F00001F7E, + 0x1F8000001FB5, + 0x1FB600001FC5, + 0x1FC600001FD4, + 0x1FD600001FDC, + 0x1FDD00001FF0, + 0x1FF200001FF5, + 0x1FF600001FFF, + 0x212600002127, + 0xAB650000AB66, + 0x101400001018F, + 0x101A0000101A1, + 0x1D2000001D246, + ), + "Han": ( + 0x2E8000002E9A, + 0x2E9B00002EF4, + 0x2F0000002FD6, + 0x300500003006, + 0x300700003008, + 0x30210000302A, + 0x30380000303C, + 0x340000004DC0, + 0x4E000000A000, + 0xF9000000FA6E, + 0xFA700000FADA, + 0x16FE200016FE4, + 0x16FF000016FF2, + 0x200000002A6E0, + 0x2A7000002B73A, + 0x2B7400002B81E, + 0x2B8200002CEA2, + 0x2CEB00002EBE1, + 0x2EBF00002EE5E, + 0x2F8000002FA1E, + 0x300000003134B, + 0x31350000323B0, + ), + "Hebrew": ( + 0x591000005C8, + 0x5D0000005EB, + 0x5EF000005F5, + 0xFB1D0000FB37, + 0xFB380000FB3D, + 0xFB3E0000FB3F, + 0xFB400000FB42, + 0xFB430000FB45, + 0xFB460000FB50, + ), + "Hiragana": ( + 0x304100003097, + 0x309D000030A0, + 0x1B0010001B120, + 0x1B1320001B133, + 0x1B1500001B153, + 0x1F2000001F201, + ), + "Katakana": ( + 0x30A1000030FB, + 0x30FD00003100, + 0x31F000003200, + 0x32D0000032FF, + 0x330000003358, + 0xFF660000FF70, + 0xFF710000FF9E, + 0x1AFF00001AFF4, + 0x1AFF50001AFFC, + 0x1AFFD0001AFFF, + 0x1B0000001B001, + 0x1B1200001B123, + 0x1B1550001B156, + 0x1B1640001B168, + ), +} +joining_types = { + 0xAD: 84, + 0x300: 84, + 0x301: 84, + 0x302: 84, + 0x303: 84, + 0x304: 84, + 0x305: 84, + 0x306: 84, + 0x307: 84, + 0x308: 84, + 0x309: 84, + 0x30A: 84, + 0x30B: 84, + 0x30C: 84, + 0x30D: 84, + 0x30E: 84, + 0x30F: 84, + 0x310: 84, + 0x311: 84, + 0x312: 84, + 0x313: 84, + 0x314: 84, + 0x315: 84, + 0x316: 84, + 0x317: 84, + 0x318: 84, + 0x319: 84, + 0x31A: 84, + 0x31B: 84, + 0x31C: 84, + 0x31D: 84, + 0x31E: 84, + 0x31F: 84, + 0x320: 84, + 0x321: 84, + 0x322: 84, + 0x323: 84, + 0x324: 84, + 0x325: 84, + 0x326: 84, + 0x327: 84, + 0x328: 84, + 0x329: 84, + 0x32A: 84, + 0x32B: 84, + 0x32C: 84, + 0x32D: 84, + 0x32E: 84, + 0x32F: 84, + 0x330: 84, + 0x331: 84, + 0x332: 84, + 0x333: 84, + 0x334: 84, + 0x335: 84, + 0x336: 84, + 0x337: 84, + 0x338: 84, + 0x339: 84, + 0x33A: 84, + 0x33B: 84, + 0x33C: 84, + 0x33D: 84, + 0x33E: 84, + 0x33F: 84, + 0x340: 84, + 0x341: 84, + 0x342: 84, + 0x343: 84, + 0x344: 84, + 0x345: 84, + 0x346: 84, + 0x347: 84, + 0x348: 84, + 0x349: 84, + 0x34A: 84, + 0x34B: 84, + 0x34C: 84, + 0x34D: 84, + 0x34E: 84, + 0x34F: 84, + 0x350: 84, + 0x351: 84, + 0x352: 84, + 0x353: 84, + 0x354: 84, + 0x355: 84, + 0x356: 84, + 0x357: 84, + 0x358: 84, + 0x359: 84, + 0x35A: 84, + 0x35B: 84, + 0x35C: 84, + 0x35D: 84, + 0x35E: 84, + 0x35F: 84, + 0x360: 84, + 0x361: 84, + 0x362: 84, + 0x363: 84, + 0x364: 84, + 0x365: 84, + 0x366: 84, + 0x367: 84, + 0x368: 84, + 0x369: 84, + 0x36A: 84, + 0x36B: 84, + 0x36C: 84, + 0x36D: 84, + 0x36E: 84, + 0x36F: 84, + 0x483: 84, + 0x484: 84, + 0x485: 84, + 0x486: 84, + 0x487: 84, + 0x488: 84, + 0x489: 84, + 0x591: 84, + 0x592: 84, + 0x593: 84, + 0x594: 84, + 0x595: 84, + 0x596: 84, + 0x597: 84, + 0x598: 84, + 0x599: 84, + 0x59A: 84, + 0x59B: 84, + 0x59C: 84, + 0x59D: 84, + 0x59E: 84, + 0x59F: 84, + 0x5A0: 84, + 0x5A1: 84, + 0x5A2: 84, + 0x5A3: 84, + 0x5A4: 84, + 0x5A5: 84, + 0x5A6: 84, + 0x5A7: 84, + 0x5A8: 84, + 0x5A9: 84, + 0x5AA: 84, + 0x5AB: 84, + 0x5AC: 84, + 0x5AD: 84, + 0x5AE: 84, + 0x5AF: 84, + 0x5B0: 84, + 0x5B1: 84, + 0x5B2: 84, + 0x5B3: 84, + 0x5B4: 84, + 0x5B5: 84, + 0x5B6: 84, + 0x5B7: 84, + 0x5B8: 84, + 0x5B9: 84, + 0x5BA: 84, + 0x5BB: 84, + 0x5BC: 84, + 0x5BD: 84, + 0x5BF: 84, + 0x5C1: 84, + 0x5C2: 84, + 0x5C4: 84, + 0x5C5: 84, + 0x5C7: 84, + 0x610: 84, + 0x611: 84, + 0x612: 84, + 0x613: 84, + 0x614: 84, + 0x615: 84, + 0x616: 84, + 0x617: 84, + 0x618: 84, + 0x619: 84, + 0x61A: 84, + 0x61C: 84, + 0x620: 68, + 0x622: 82, + 0x623: 82, + 0x624: 82, + 0x625: 82, + 0x626: 68, + 0x627: 82, + 0x628: 68, + 0x629: 82, + 0x62A: 68, + 0x62B: 68, + 0x62C: 68, + 0x62D: 68, + 0x62E: 68, + 0x62F: 82, + 0x630: 82, + 0x631: 82, + 0x632: 82, + 0x633: 68, + 0x634: 68, + 0x635: 68, + 0x636: 68, + 0x637: 68, + 0x638: 68, + 0x639: 68, + 0x63A: 68, + 0x63B: 68, + 0x63C: 68, + 0x63D: 68, + 0x63E: 68, + 0x63F: 68, + 0x640: 67, + 0x641: 68, + 0x642: 68, + 0x643: 68, + 0x644: 68, + 0x645: 68, + 0x646: 68, + 0x647: 68, + 0x648: 82, + 0x649: 68, + 0x64A: 68, + 0x64B: 84, + 0x64C: 84, + 0x64D: 84, + 0x64E: 84, + 0x64F: 84, + 0x650: 84, + 0x651: 84, + 0x652: 84, + 0x653: 84, + 0x654: 84, + 0x655: 84, + 0x656: 84, + 0x657: 84, + 0x658: 84, + 0x659: 84, + 0x65A: 84, + 0x65B: 84, + 0x65C: 84, + 0x65D: 84, + 0x65E: 84, + 0x65F: 84, + 0x66E: 68, + 0x66F: 68, + 0x670: 84, + 0x671: 82, + 0x672: 82, + 0x673: 82, + 0x675: 82, + 0x676: 82, + 0x677: 82, + 0x678: 68, + 0x679: 68, + 0x67A: 68, + 0x67B: 68, + 0x67C: 68, + 0x67D: 68, + 0x67E: 68, + 0x67F: 68, + 0x680: 68, + 0x681: 68, + 0x682: 68, + 0x683: 68, + 0x684: 68, + 0x685: 68, + 0x686: 68, + 0x687: 68, + 0x688: 82, + 0x689: 82, + 0x68A: 82, + 0x68B: 82, + 0x68C: 82, + 0x68D: 82, + 0x68E: 82, + 0x68F: 82, + 0x690: 82, + 0x691: 82, + 0x692: 82, + 0x693: 82, + 0x694: 82, + 0x695: 82, + 0x696: 82, + 0x697: 82, + 0x698: 82, + 0x699: 82, + 0x69A: 68, + 0x69B: 68, + 0x69C: 68, + 0x69D: 68, + 0x69E: 68, + 0x69F: 68, + 0x6A0: 68, + 0x6A1: 68, + 0x6A2: 68, + 0x6A3: 68, + 0x6A4: 68, + 0x6A5: 68, + 0x6A6: 68, + 0x6A7: 68, + 0x6A8: 68, + 0x6A9: 68, + 0x6AA: 68, + 0x6AB: 68, + 0x6AC: 68, + 0x6AD: 68, + 0x6AE: 68, + 0x6AF: 68, + 0x6B0: 68, + 0x6B1: 68, + 0x6B2: 68, + 0x6B3: 68, + 0x6B4: 68, + 0x6B5: 68, + 0x6B6: 68, + 0x6B7: 68, + 0x6B8: 68, + 0x6B9: 68, + 0x6BA: 68, + 0x6BB: 68, + 0x6BC: 68, + 0x6BD: 68, + 0x6BE: 68, + 0x6BF: 68, + 0x6C0: 82, + 0x6C1: 68, + 0x6C2: 68, + 0x6C3: 82, + 0x6C4: 82, + 0x6C5: 82, + 0x6C6: 82, + 0x6C7: 82, + 0x6C8: 82, + 0x6C9: 82, + 0x6CA: 82, + 0x6CB: 82, + 0x6CC: 68, + 0x6CD: 82, + 0x6CE: 68, + 0x6CF: 82, + 0x6D0: 68, + 0x6D1: 68, + 0x6D2: 82, + 0x6D3: 82, + 0x6D5: 82, + 0x6D6: 84, + 0x6D7: 84, + 0x6D8: 84, + 0x6D9: 84, + 0x6DA: 84, + 0x6DB: 84, + 0x6DC: 84, + 0x6DF: 84, + 0x6E0: 84, + 0x6E1: 84, + 0x6E2: 84, + 0x6E3: 84, + 0x6E4: 84, + 0x6E7: 84, + 0x6E8: 84, + 0x6EA: 84, + 0x6EB: 84, + 0x6EC: 84, + 0x6ED: 84, + 0x6EE: 82, + 0x6EF: 82, + 0x6FA: 68, + 0x6FB: 68, + 0x6FC: 68, + 0x6FF: 68, + 0x70F: 84, + 0x710: 82, + 0x711: 84, + 0x712: 68, + 0x713: 68, + 0x714: 68, + 0x715: 82, + 0x716: 82, + 0x717: 82, + 0x718: 82, + 0x719: 82, + 0x71A: 68, + 0x71B: 68, + 0x71C: 68, + 0x71D: 68, + 0x71E: 82, + 0x71F: 68, + 0x720: 68, + 0x721: 68, + 0x722: 68, + 0x723: 68, + 0x724: 68, + 0x725: 68, + 0x726: 68, + 0x727: 68, + 0x728: 82, + 0x729: 68, + 0x72A: 82, + 0x72B: 68, + 0x72C: 82, + 0x72D: 68, + 0x72E: 68, + 0x72F: 82, + 0x730: 84, + 0x731: 84, + 0x732: 84, + 0x733: 84, + 0x734: 84, + 0x735: 84, + 0x736: 84, + 0x737: 84, + 0x738: 84, + 0x739: 84, + 0x73A: 84, + 0x73B: 84, + 0x73C: 84, + 0x73D: 84, + 0x73E: 84, + 0x73F: 84, + 0x740: 84, + 0x741: 84, + 0x742: 84, + 0x743: 84, + 0x744: 84, + 0x745: 84, + 0x746: 84, + 0x747: 84, + 0x748: 84, + 0x749: 84, + 0x74A: 84, + 0x74D: 82, + 0x74E: 68, + 0x74F: 68, + 0x750: 68, + 0x751: 68, + 0x752: 68, + 0x753: 68, + 0x754: 68, + 0x755: 68, + 0x756: 68, + 0x757: 68, + 0x758: 68, + 0x759: 82, + 0x75A: 82, + 0x75B: 82, + 0x75C: 68, + 0x75D: 68, + 0x75E: 68, + 0x75F: 68, + 0x760: 68, + 0x761: 68, + 0x762: 68, + 0x763: 68, + 0x764: 68, + 0x765: 68, + 0x766: 68, + 0x767: 68, + 0x768: 68, + 0x769: 68, + 0x76A: 68, + 0x76B: 82, + 0x76C: 82, + 0x76D: 68, + 0x76E: 68, + 0x76F: 68, + 0x770: 68, + 0x771: 82, + 0x772: 68, + 0x773: 82, + 0x774: 82, + 0x775: 68, + 0x776: 68, + 0x777: 68, + 0x778: 82, + 0x779: 82, + 0x77A: 68, + 0x77B: 68, + 0x77C: 68, + 0x77D: 68, + 0x77E: 68, + 0x77F: 68, + 0x7A6: 84, + 0x7A7: 84, + 0x7A8: 84, + 0x7A9: 84, + 0x7AA: 84, + 0x7AB: 84, + 0x7AC: 84, + 0x7AD: 84, + 0x7AE: 84, + 0x7AF: 84, + 0x7B0: 84, + 0x7CA: 68, + 0x7CB: 68, + 0x7CC: 68, + 0x7CD: 68, + 0x7CE: 68, + 0x7CF: 68, + 0x7D0: 68, + 0x7D1: 68, + 0x7D2: 68, + 0x7D3: 68, + 0x7D4: 68, + 0x7D5: 68, + 0x7D6: 68, + 0x7D7: 68, + 0x7D8: 68, + 0x7D9: 68, + 0x7DA: 68, + 0x7DB: 68, + 0x7DC: 68, + 0x7DD: 68, + 0x7DE: 68, + 0x7DF: 68, + 0x7E0: 68, + 0x7E1: 68, + 0x7E2: 68, + 0x7E3: 68, + 0x7E4: 68, + 0x7E5: 68, + 0x7E6: 68, + 0x7E7: 68, + 0x7E8: 68, + 0x7E9: 68, + 0x7EA: 68, + 0x7EB: 84, + 0x7EC: 84, + 0x7ED: 84, + 0x7EE: 84, + 0x7EF: 84, + 0x7F0: 84, + 0x7F1: 84, + 0x7F2: 84, + 0x7F3: 84, + 0x7FA: 67, + 0x7FD: 84, + 0x816: 84, + 0x817: 84, + 0x818: 84, + 0x819: 84, + 0x81B: 84, + 0x81C: 84, + 0x81D: 84, + 0x81E: 84, + 0x81F: 84, + 0x820: 84, + 0x821: 84, + 0x822: 84, + 0x823: 84, + 0x825: 84, + 0x826: 84, + 0x827: 84, + 0x829: 84, + 0x82A: 84, + 0x82B: 84, + 0x82C: 84, + 0x82D: 84, + 0x840: 82, + 0x841: 68, + 0x842: 68, + 0x843: 68, + 0x844: 68, + 0x845: 68, + 0x846: 82, + 0x847: 82, + 0x848: 68, + 0x849: 82, + 0x84A: 68, + 0x84B: 68, + 0x84C: 68, + 0x84D: 68, + 0x84E: 68, + 0x84F: 68, + 0x850: 68, + 0x851: 68, + 0x852: 68, + 0x853: 68, + 0x854: 82, + 0x855: 68, + 0x856: 82, + 0x857: 82, + 0x858: 82, + 0x859: 84, + 0x85A: 84, + 0x85B: 84, + 0x860: 68, + 0x862: 68, + 0x863: 68, + 0x864: 68, + 0x865: 68, + 0x867: 82, + 0x868: 68, + 0x869: 82, + 0x86A: 82, + 0x870: 82, + 0x871: 82, + 0x872: 82, + 0x873: 82, + 0x874: 82, + 0x875: 82, + 0x876: 82, + 0x877: 82, + 0x878: 82, + 0x879: 82, + 0x87A: 82, + 0x87B: 82, + 0x87C: 82, + 0x87D: 82, + 0x87E: 82, + 0x87F: 82, + 0x880: 82, + 0x881: 82, + 0x882: 82, + 0x883: 67, + 0x884: 67, + 0x885: 67, + 0x886: 68, + 0x889: 68, + 0x88A: 68, + 0x88B: 68, + 0x88C: 68, + 0x88D: 68, + 0x88E: 82, + 0x897: 84, + 0x898: 84, + 0x899: 84, + 0x89A: 84, + 0x89B: 84, + 0x89C: 84, + 0x89D: 84, + 0x89E: 84, + 0x89F: 84, + 0x8A0: 68, + 0x8A1: 68, + 0x8A2: 68, + 0x8A3: 68, + 0x8A4: 68, + 0x8A5: 68, + 0x8A6: 68, + 0x8A7: 68, + 0x8A8: 68, + 0x8A9: 68, + 0x8AA: 82, + 0x8AB: 82, + 0x8AC: 82, + 0x8AE: 82, + 0x8AF: 68, + 0x8B0: 68, + 0x8B1: 82, + 0x8B2: 82, + 0x8B3: 68, + 0x8B4: 68, + 0x8B5: 68, + 0x8B6: 68, + 0x8B7: 68, + 0x8B8: 68, + 0x8B9: 82, + 0x8BA: 68, + 0x8BB: 68, + 0x8BC: 68, + 0x8BD: 68, + 0x8BE: 68, + 0x8BF: 68, + 0x8C0: 68, + 0x8C1: 68, + 0x8C2: 68, + 0x8C3: 68, + 0x8C4: 68, + 0x8C5: 68, + 0x8C6: 68, + 0x8C7: 68, + 0x8C8: 68, + 0x8CA: 84, + 0x8CB: 84, + 0x8CC: 84, + 0x8CD: 84, + 0x8CE: 84, + 0x8CF: 84, + 0x8D0: 84, + 0x8D1: 84, + 0x8D2: 84, + 0x8D3: 84, + 0x8D4: 84, + 0x8D5: 84, + 0x8D6: 84, + 0x8D7: 84, + 0x8D8: 84, + 0x8D9: 84, + 0x8DA: 84, + 0x8DB: 84, + 0x8DC: 84, + 0x8DD: 84, + 0x8DE: 84, + 0x8DF: 84, + 0x8E0: 84, + 0x8E1: 84, + 0x8E3: 84, + 0x8E4: 84, + 0x8E5: 84, + 0x8E6: 84, + 0x8E7: 84, + 0x8E8: 84, + 0x8E9: 84, + 0x8EA: 84, + 0x8EB: 84, + 0x8EC: 84, + 0x8ED: 84, + 0x8EE: 84, + 0x8EF: 84, + 0x8F0: 84, + 0x8F1: 84, + 0x8F2: 84, + 0x8F3: 84, + 0x8F4: 84, + 0x8F5: 84, + 0x8F6: 84, + 0x8F7: 84, + 0x8F8: 84, + 0x8F9: 84, + 0x8FA: 84, + 0x8FB: 84, + 0x8FC: 84, + 0x8FD: 84, + 0x8FE: 84, + 0x8FF: 84, + 0x900: 84, + 0x901: 84, + 0x902: 84, + 0x93A: 84, + 0x93C: 84, + 0x941: 84, + 0x942: 84, + 0x943: 84, + 0x944: 84, + 0x945: 84, + 0x946: 84, + 0x947: 84, + 0x948: 84, + 0x94D: 84, + 0x951: 84, + 0x952: 84, + 0x953: 84, + 0x954: 84, + 0x955: 84, + 0x956: 84, + 0x957: 84, + 0x962: 84, + 0x963: 84, + 0x981: 84, + 0x9BC: 84, + 0x9C1: 84, + 0x9C2: 84, + 0x9C3: 84, + 0x9C4: 84, + 0x9CD: 84, + 0x9E2: 84, + 0x9E3: 84, + 0x9FE: 84, + 0xA01: 84, + 0xA02: 84, + 0xA3C: 84, + 0xA41: 84, + 0xA42: 84, + 0xA47: 84, + 0xA48: 84, + 0xA4B: 84, + 0xA4C: 84, + 0xA4D: 84, + 0xA51: 84, + 0xA70: 84, + 0xA71: 84, + 0xA75: 84, + 0xA81: 84, + 0xA82: 84, + 0xABC: 84, + 0xAC1: 84, + 0xAC2: 84, + 0xAC3: 84, + 0xAC4: 84, + 0xAC5: 84, + 0xAC7: 84, + 0xAC8: 84, + 0xACD: 84, + 0xAE2: 84, + 0xAE3: 84, + 0xAFA: 84, + 0xAFB: 84, + 0xAFC: 84, + 0xAFD: 84, + 0xAFE: 84, + 0xAFF: 84, + 0xB01: 84, + 0xB3C: 84, + 0xB3F: 84, + 0xB41: 84, + 0xB42: 84, + 0xB43: 84, + 0xB44: 84, + 0xB4D: 84, + 0xB55: 84, + 0xB56: 84, + 0xB62: 84, + 0xB63: 84, + 0xB82: 84, + 0xBC0: 84, + 0xBCD: 84, + 0xC00: 84, + 0xC04: 84, + 0xC3C: 84, + 0xC3E: 84, + 0xC3F: 84, + 0xC40: 84, + 0xC46: 84, + 0xC47: 84, + 0xC48: 84, + 0xC4A: 84, + 0xC4B: 84, + 0xC4C: 84, + 0xC4D: 84, + 0xC55: 84, + 0xC56: 84, + 0xC62: 84, + 0xC63: 84, + 0xC81: 84, + 0xCBC: 84, + 0xCBF: 84, + 0xCC6: 84, + 0xCCC: 84, + 0xCCD: 84, + 0xCE2: 84, + 0xCE3: 84, + 0xD00: 84, + 0xD01: 84, + 0xD3B: 84, + 0xD3C: 84, + 0xD41: 84, + 0xD42: 84, + 0xD43: 84, + 0xD44: 84, + 0xD4D: 84, + 0xD62: 84, + 0xD63: 84, + 0xD81: 84, + 0xDCA: 84, + 0xDD2: 84, + 0xDD3: 84, + 0xDD4: 84, + 0xDD6: 84, + 0xE31: 84, + 0xE34: 84, + 0xE35: 84, + 0xE36: 84, + 0xE37: 84, + 0xE38: 84, + 0xE39: 84, + 0xE3A: 84, + 0xE47: 84, + 0xE48: 84, + 0xE49: 84, + 0xE4A: 84, + 0xE4B: 84, + 0xE4C: 84, + 0xE4D: 84, + 0xE4E: 84, + 0xEB1: 84, + 0xEB4: 84, + 0xEB5: 84, + 0xEB6: 84, + 0xEB7: 84, + 0xEB8: 84, + 0xEB9: 84, + 0xEBA: 84, + 0xEBB: 84, + 0xEBC: 84, + 0xEC8: 84, + 0xEC9: 84, + 0xECA: 84, + 0xECB: 84, + 0xECC: 84, + 0xECD: 84, + 0xECE: 84, + 0xF18: 84, + 0xF19: 84, + 0xF35: 84, + 0xF37: 84, + 0xF39: 84, + 0xF71: 84, + 0xF72: 84, + 0xF73: 84, + 0xF74: 84, + 0xF75: 84, + 0xF76: 84, + 0xF77: 84, + 0xF78: 84, + 0xF79: 84, + 0xF7A: 84, + 0xF7B: 84, + 0xF7C: 84, + 0xF7D: 84, + 0xF7E: 84, + 0xF80: 84, + 0xF81: 84, + 0xF82: 84, + 0xF83: 84, + 0xF84: 84, + 0xF86: 84, + 0xF87: 84, + 0xF8D: 84, + 0xF8E: 84, + 0xF8F: 84, + 0xF90: 84, + 0xF91: 84, + 0xF92: 84, + 0xF93: 84, + 0xF94: 84, + 0xF95: 84, + 0xF96: 84, + 0xF97: 84, + 0xF99: 84, + 0xF9A: 84, + 0xF9B: 84, + 0xF9C: 84, + 0xF9D: 84, + 0xF9E: 84, + 0xF9F: 84, + 0xFA0: 84, + 0xFA1: 84, + 0xFA2: 84, + 0xFA3: 84, + 0xFA4: 84, + 0xFA5: 84, + 0xFA6: 84, + 0xFA7: 84, + 0xFA8: 84, + 0xFA9: 84, + 0xFAA: 84, + 0xFAB: 84, + 0xFAC: 84, + 0xFAD: 84, + 0xFAE: 84, + 0xFAF: 84, + 0xFB0: 84, + 0xFB1: 84, + 0xFB2: 84, + 0xFB3: 84, + 0xFB4: 84, + 0xFB5: 84, + 0xFB6: 84, + 0xFB7: 84, + 0xFB8: 84, + 0xFB9: 84, + 0xFBA: 84, + 0xFBB: 84, + 0xFBC: 84, + 0xFC6: 84, + 0x102D: 84, + 0x102E: 84, + 0x102F: 84, + 0x1030: 84, + 0x1032: 84, + 0x1033: 84, + 0x1034: 84, + 0x1035: 84, + 0x1036: 84, + 0x1037: 84, + 0x1039: 84, + 0x103A: 84, + 0x103D: 84, + 0x103E: 84, + 0x1058: 84, + 0x1059: 84, + 0x105E: 84, + 0x105F: 84, + 0x1060: 84, + 0x1071: 84, + 0x1072: 84, + 0x1073: 84, + 0x1074: 84, + 0x1082: 84, + 0x1085: 84, + 0x1086: 84, + 0x108D: 84, + 0x109D: 84, + 0x135D: 84, + 0x135E: 84, + 0x135F: 84, + 0x1712: 84, + 0x1713: 84, + 0x1714: 84, + 0x1732: 84, + 0x1733: 84, + 0x1752: 84, + 0x1753: 84, + 0x1772: 84, + 0x1773: 84, + 0x17B4: 84, + 0x17B5: 84, + 0x17B7: 84, + 0x17B8: 84, + 0x17B9: 84, + 0x17BA: 84, + 0x17BB: 84, + 0x17BC: 84, + 0x17BD: 84, + 0x17C6: 84, + 0x17C9: 84, + 0x17CA: 84, + 0x17CB: 84, + 0x17CC: 84, + 0x17CD: 84, + 0x17CE: 84, + 0x17CF: 84, + 0x17D0: 84, + 0x17D1: 84, + 0x17D2: 84, + 0x17D3: 84, + 0x17DD: 84, + 0x1807: 68, + 0x180A: 67, + 0x180B: 84, + 0x180C: 84, + 0x180D: 84, + 0x180F: 84, + 0x1820: 68, + 0x1821: 68, + 0x1822: 68, + 0x1823: 68, + 0x1824: 68, + 0x1825: 68, + 0x1826: 68, + 0x1827: 68, + 0x1828: 68, + 0x1829: 68, + 0x182A: 68, + 0x182B: 68, + 0x182C: 68, + 0x182D: 68, + 0x182E: 68, + 0x182F: 68, + 0x1830: 68, + 0x1831: 68, + 0x1832: 68, + 0x1833: 68, + 0x1834: 68, + 0x1835: 68, + 0x1836: 68, + 0x1837: 68, + 0x1838: 68, + 0x1839: 68, + 0x183A: 68, + 0x183B: 68, + 0x183C: 68, + 0x183D: 68, + 0x183E: 68, + 0x183F: 68, + 0x1840: 68, + 0x1841: 68, + 0x1842: 68, + 0x1843: 68, + 0x1844: 68, + 0x1845: 68, + 0x1846: 68, + 0x1847: 68, + 0x1848: 68, + 0x1849: 68, + 0x184A: 68, + 0x184B: 68, + 0x184C: 68, + 0x184D: 68, + 0x184E: 68, + 0x184F: 68, + 0x1850: 68, + 0x1851: 68, + 0x1852: 68, + 0x1853: 68, + 0x1854: 68, + 0x1855: 68, + 0x1856: 68, + 0x1857: 68, + 0x1858: 68, + 0x1859: 68, + 0x185A: 68, + 0x185B: 68, + 0x185C: 68, + 0x185D: 68, + 0x185E: 68, + 0x185F: 68, + 0x1860: 68, + 0x1861: 68, + 0x1862: 68, + 0x1863: 68, + 0x1864: 68, + 0x1865: 68, + 0x1866: 68, + 0x1867: 68, + 0x1868: 68, + 0x1869: 68, + 0x186A: 68, + 0x186B: 68, + 0x186C: 68, + 0x186D: 68, + 0x186E: 68, + 0x186F: 68, + 0x1870: 68, + 0x1871: 68, + 0x1872: 68, + 0x1873: 68, + 0x1874: 68, + 0x1875: 68, + 0x1876: 68, + 0x1877: 68, + 0x1878: 68, + 0x1885: 84, + 0x1886: 84, + 0x1887: 68, + 0x1888: 68, + 0x1889: 68, + 0x188A: 68, + 0x188B: 68, + 0x188C: 68, + 0x188D: 68, + 0x188E: 68, + 0x188F: 68, + 0x1890: 68, + 0x1891: 68, + 0x1892: 68, + 0x1893: 68, + 0x1894: 68, + 0x1895: 68, + 0x1896: 68, + 0x1897: 68, + 0x1898: 68, + 0x1899: 68, + 0x189A: 68, + 0x189B: 68, + 0x189C: 68, + 0x189D: 68, + 0x189E: 68, + 0x189F: 68, + 0x18A0: 68, + 0x18A1: 68, + 0x18A2: 68, + 0x18A3: 68, + 0x18A4: 68, + 0x18A5: 68, + 0x18A6: 68, + 0x18A7: 68, + 0x18A8: 68, + 0x18A9: 84, + 0x18AA: 68, + 0x1920: 84, + 0x1921: 84, + 0x1922: 84, + 0x1927: 84, + 0x1928: 84, + 0x1932: 84, + 0x1939: 84, + 0x193A: 84, + 0x193B: 84, + 0x1A17: 84, + 0x1A18: 84, + 0x1A1B: 84, + 0x1A56: 84, + 0x1A58: 84, + 0x1A59: 84, + 0x1A5A: 84, + 0x1A5B: 84, + 0x1A5C: 84, + 0x1A5D: 84, + 0x1A5E: 84, + 0x1A60: 84, + 0x1A62: 84, + 0x1A65: 84, + 0x1A66: 84, + 0x1A67: 84, + 0x1A68: 84, + 0x1A69: 84, + 0x1A6A: 84, + 0x1A6B: 84, + 0x1A6C: 84, + 0x1A73: 84, + 0x1A74: 84, + 0x1A75: 84, + 0x1A76: 84, + 0x1A77: 84, + 0x1A78: 84, + 0x1A79: 84, + 0x1A7A: 84, + 0x1A7B: 84, + 0x1A7C: 84, + 0x1A7F: 84, + 0x1AB0: 84, + 0x1AB1: 84, + 0x1AB2: 84, + 0x1AB3: 84, + 0x1AB4: 84, + 0x1AB5: 84, + 0x1AB6: 84, + 0x1AB7: 84, + 0x1AB8: 84, + 0x1AB9: 84, + 0x1ABA: 84, + 0x1ABB: 84, + 0x1ABC: 84, + 0x1ABD: 84, + 0x1ABE: 84, + 0x1ABF: 84, + 0x1AC0: 84, + 0x1AC1: 84, + 0x1AC2: 84, + 0x1AC3: 84, + 0x1AC4: 84, + 0x1AC5: 84, + 0x1AC6: 84, + 0x1AC7: 84, + 0x1AC8: 84, + 0x1AC9: 84, + 0x1ACA: 84, + 0x1ACB: 84, + 0x1ACC: 84, + 0x1ACD: 84, + 0x1ACE: 84, + 0x1B00: 84, + 0x1B01: 84, + 0x1B02: 84, + 0x1B03: 84, + 0x1B34: 84, + 0x1B36: 84, + 0x1B37: 84, + 0x1B38: 84, + 0x1B39: 84, + 0x1B3A: 84, + 0x1B3C: 84, + 0x1B42: 84, + 0x1B6B: 84, + 0x1B6C: 84, + 0x1B6D: 84, + 0x1B6E: 84, + 0x1B6F: 84, + 0x1B70: 84, + 0x1B71: 84, + 0x1B72: 84, + 0x1B73: 84, + 0x1B80: 84, + 0x1B81: 84, + 0x1BA2: 84, + 0x1BA3: 84, + 0x1BA4: 84, + 0x1BA5: 84, + 0x1BA8: 84, + 0x1BA9: 84, + 0x1BAB: 84, + 0x1BAC: 84, + 0x1BAD: 84, + 0x1BE6: 84, + 0x1BE8: 84, + 0x1BE9: 84, + 0x1BED: 84, + 0x1BEF: 84, + 0x1BF0: 84, + 0x1BF1: 84, + 0x1C2C: 84, + 0x1C2D: 84, + 0x1C2E: 84, + 0x1C2F: 84, + 0x1C30: 84, + 0x1C31: 84, + 0x1C32: 84, + 0x1C33: 84, + 0x1C36: 84, + 0x1C37: 84, + 0x1CD0: 84, + 0x1CD1: 84, + 0x1CD2: 84, + 0x1CD4: 84, + 0x1CD5: 84, + 0x1CD6: 84, + 0x1CD7: 84, + 0x1CD8: 84, + 0x1CD9: 84, + 0x1CDA: 84, + 0x1CDB: 84, + 0x1CDC: 84, + 0x1CDD: 84, + 0x1CDE: 84, + 0x1CDF: 84, + 0x1CE0: 84, + 0x1CE2: 84, + 0x1CE3: 84, + 0x1CE4: 84, + 0x1CE5: 84, + 0x1CE6: 84, + 0x1CE7: 84, + 0x1CE8: 84, + 0x1CED: 84, + 0x1CF4: 84, + 0x1CF8: 84, + 0x1CF9: 84, + 0x1DC0: 84, + 0x1DC1: 84, + 0x1DC2: 84, + 0x1DC3: 84, + 0x1DC4: 84, + 0x1DC5: 84, + 0x1DC6: 84, + 0x1DC7: 84, + 0x1DC8: 84, + 0x1DC9: 84, + 0x1DCA: 84, + 0x1DCB: 84, + 0x1DCC: 84, + 0x1DCD: 84, + 0x1DCE: 84, + 0x1DCF: 84, + 0x1DD0: 84, + 0x1DD1: 84, + 0x1DD2: 84, + 0x1DD3: 84, + 0x1DD4: 84, + 0x1DD5: 84, + 0x1DD6: 84, + 0x1DD7: 84, + 0x1DD8: 84, + 0x1DD9: 84, + 0x1DDA: 84, + 0x1DDB: 84, + 0x1DDC: 84, + 0x1DDD: 84, + 0x1DDE: 84, + 0x1DDF: 84, + 0x1DE0: 84, + 0x1DE1: 84, + 0x1DE2: 84, + 0x1DE3: 84, + 0x1DE4: 84, + 0x1DE5: 84, + 0x1DE6: 84, + 0x1DE7: 84, + 0x1DE8: 84, + 0x1DE9: 84, + 0x1DEA: 84, + 0x1DEB: 84, + 0x1DEC: 84, + 0x1DED: 84, + 0x1DEE: 84, + 0x1DEF: 84, + 0x1DF0: 84, + 0x1DF1: 84, + 0x1DF2: 84, + 0x1DF3: 84, + 0x1DF4: 84, + 0x1DF5: 84, + 0x1DF6: 84, + 0x1DF7: 84, + 0x1DF8: 84, + 0x1DF9: 84, + 0x1DFA: 84, + 0x1DFB: 84, + 0x1DFC: 84, + 0x1DFD: 84, + 0x1DFE: 84, + 0x1DFF: 84, + 0x200B: 84, + 0x200D: 67, + 0x200E: 84, + 0x200F: 84, + 0x202A: 84, + 0x202B: 84, + 0x202C: 84, + 0x202D: 84, + 0x202E: 84, + 0x2060: 84, + 0x2061: 84, + 0x2062: 84, + 0x2063: 84, + 0x2064: 84, + 0x206A: 84, + 0x206B: 84, + 0x206C: 84, + 0x206D: 84, + 0x206E: 84, + 0x206F: 84, + 0x20D0: 84, + 0x20D1: 84, + 0x20D2: 84, + 0x20D3: 84, + 0x20D4: 84, + 0x20D5: 84, + 0x20D6: 84, + 0x20D7: 84, + 0x20D8: 84, + 0x20D9: 84, + 0x20DA: 84, + 0x20DB: 84, + 0x20DC: 84, + 0x20DD: 84, + 0x20DE: 84, + 0x20DF: 84, + 0x20E0: 84, + 0x20E1: 84, + 0x20E2: 84, + 0x20E3: 84, + 0x20E4: 84, + 0x20E5: 84, + 0x20E6: 84, + 0x20E7: 84, + 0x20E8: 84, + 0x20E9: 84, + 0x20EA: 84, + 0x20EB: 84, + 0x20EC: 84, + 0x20ED: 84, + 0x20EE: 84, + 0x20EF: 84, + 0x20F0: 84, + 0x2CEF: 84, + 0x2CF0: 84, + 0x2CF1: 84, + 0x2D7F: 84, + 0x2DE0: 84, + 0x2DE1: 84, + 0x2DE2: 84, + 0x2DE3: 84, + 0x2DE4: 84, + 0x2DE5: 84, + 0x2DE6: 84, + 0x2DE7: 84, + 0x2DE8: 84, + 0x2DE9: 84, + 0x2DEA: 84, + 0x2DEB: 84, + 0x2DEC: 84, + 0x2DED: 84, + 0x2DEE: 84, + 0x2DEF: 84, + 0x2DF0: 84, + 0x2DF1: 84, + 0x2DF2: 84, + 0x2DF3: 84, + 0x2DF4: 84, + 0x2DF5: 84, + 0x2DF6: 84, + 0x2DF7: 84, + 0x2DF8: 84, + 0x2DF9: 84, + 0x2DFA: 84, + 0x2DFB: 84, + 0x2DFC: 84, + 0x2DFD: 84, + 0x2DFE: 84, + 0x2DFF: 84, + 0x302A: 84, + 0x302B: 84, + 0x302C: 84, + 0x302D: 84, + 0x3099: 84, + 0x309A: 84, + 0xA66F: 84, + 0xA670: 84, + 0xA671: 84, + 0xA672: 84, + 0xA674: 84, + 0xA675: 84, + 0xA676: 84, + 0xA677: 84, + 0xA678: 84, + 0xA679: 84, + 0xA67A: 84, + 0xA67B: 84, + 0xA67C: 84, + 0xA67D: 84, + 0xA69E: 84, + 0xA69F: 84, + 0xA6F0: 84, + 0xA6F1: 84, + 0xA802: 84, + 0xA806: 84, + 0xA80B: 84, + 0xA825: 84, + 0xA826: 84, + 0xA82C: 84, + 0xA840: 68, + 0xA841: 68, + 0xA842: 68, + 0xA843: 68, + 0xA844: 68, + 0xA845: 68, + 0xA846: 68, + 0xA847: 68, + 0xA848: 68, + 0xA849: 68, + 0xA84A: 68, + 0xA84B: 68, + 0xA84C: 68, + 0xA84D: 68, + 0xA84E: 68, + 0xA84F: 68, + 0xA850: 68, + 0xA851: 68, + 0xA852: 68, + 0xA853: 68, + 0xA854: 68, + 0xA855: 68, + 0xA856: 68, + 0xA857: 68, + 0xA858: 68, + 0xA859: 68, + 0xA85A: 68, + 0xA85B: 68, + 0xA85C: 68, + 0xA85D: 68, + 0xA85E: 68, + 0xA85F: 68, + 0xA860: 68, + 0xA861: 68, + 0xA862: 68, + 0xA863: 68, + 0xA864: 68, + 0xA865: 68, + 0xA866: 68, + 0xA867: 68, + 0xA868: 68, + 0xA869: 68, + 0xA86A: 68, + 0xA86B: 68, + 0xA86C: 68, + 0xA86D: 68, + 0xA86E: 68, + 0xA86F: 68, + 0xA870: 68, + 0xA871: 68, + 0xA872: 76, + 0xA8C4: 84, + 0xA8C5: 84, + 0xA8E0: 84, + 0xA8E1: 84, + 0xA8E2: 84, + 0xA8E3: 84, + 0xA8E4: 84, + 0xA8E5: 84, + 0xA8E6: 84, + 0xA8E7: 84, + 0xA8E8: 84, + 0xA8E9: 84, + 0xA8EA: 84, + 0xA8EB: 84, + 0xA8EC: 84, + 0xA8ED: 84, + 0xA8EE: 84, + 0xA8EF: 84, + 0xA8F0: 84, + 0xA8F1: 84, + 0xA8FF: 84, + 0xA926: 84, + 0xA927: 84, + 0xA928: 84, + 0xA929: 84, + 0xA92A: 84, + 0xA92B: 84, + 0xA92C: 84, + 0xA92D: 84, + 0xA947: 84, + 0xA948: 84, + 0xA949: 84, + 0xA94A: 84, + 0xA94B: 84, + 0xA94C: 84, + 0xA94D: 84, + 0xA94E: 84, + 0xA94F: 84, + 0xA950: 84, + 0xA951: 84, + 0xA980: 84, + 0xA981: 84, + 0xA982: 84, + 0xA9B3: 84, + 0xA9B6: 84, + 0xA9B7: 84, + 0xA9B8: 84, + 0xA9B9: 84, + 0xA9BC: 84, + 0xA9BD: 84, + 0xA9E5: 84, + 0xAA29: 84, + 0xAA2A: 84, + 0xAA2B: 84, + 0xAA2C: 84, + 0xAA2D: 84, + 0xAA2E: 84, + 0xAA31: 84, + 0xAA32: 84, + 0xAA35: 84, + 0xAA36: 84, + 0xAA43: 84, + 0xAA4C: 84, + 0xAA7C: 84, + 0xAAB0: 84, + 0xAAB2: 84, + 0xAAB3: 84, + 0xAAB4: 84, + 0xAAB7: 84, + 0xAAB8: 84, + 0xAABE: 84, + 0xAABF: 84, + 0xAAC1: 84, + 0xAAEC: 84, + 0xAAED: 84, + 0xAAF6: 84, + 0xABE5: 84, + 0xABE8: 84, + 0xABED: 84, + 0xFB1E: 84, + 0xFE00: 84, + 0xFE01: 84, + 0xFE02: 84, + 0xFE03: 84, + 0xFE04: 84, + 0xFE05: 84, + 0xFE06: 84, + 0xFE07: 84, + 0xFE08: 84, + 0xFE09: 84, + 0xFE0A: 84, + 0xFE0B: 84, + 0xFE0C: 84, + 0xFE0D: 84, + 0xFE0E: 84, + 0xFE0F: 84, + 0xFE20: 84, + 0xFE21: 84, + 0xFE22: 84, + 0xFE23: 84, + 0xFE24: 84, + 0xFE25: 84, + 0xFE26: 84, + 0xFE27: 84, + 0xFE28: 84, + 0xFE29: 84, + 0xFE2A: 84, + 0xFE2B: 84, + 0xFE2C: 84, + 0xFE2D: 84, + 0xFE2E: 84, + 0xFE2F: 84, + 0xFEFF: 84, + 0xFFF9: 84, + 0xFFFA: 84, + 0xFFFB: 84, + 0x101FD: 84, + 0x102E0: 84, + 0x10376: 84, + 0x10377: 84, + 0x10378: 84, + 0x10379: 84, + 0x1037A: 84, + 0x10A01: 84, + 0x10A02: 84, + 0x10A03: 84, + 0x10A05: 84, + 0x10A06: 84, + 0x10A0C: 84, + 0x10A0D: 84, + 0x10A0E: 84, + 0x10A0F: 84, + 0x10A38: 84, + 0x10A39: 84, + 0x10A3A: 84, + 0x10A3F: 84, + 0x10AC0: 68, + 0x10AC1: 68, + 0x10AC2: 68, + 0x10AC3: 68, + 0x10AC4: 68, + 0x10AC5: 82, + 0x10AC7: 82, + 0x10AC9: 82, + 0x10ACA: 82, + 0x10ACD: 76, + 0x10ACE: 82, + 0x10ACF: 82, + 0x10AD0: 82, + 0x10AD1: 82, + 0x10AD2: 82, + 0x10AD3: 68, + 0x10AD4: 68, + 0x10AD5: 68, + 0x10AD6: 68, + 0x10AD7: 76, + 0x10AD8: 68, + 0x10AD9: 68, + 0x10ADA: 68, + 0x10ADB: 68, + 0x10ADC: 68, + 0x10ADD: 82, + 0x10ADE: 68, + 0x10ADF: 68, + 0x10AE0: 68, + 0x10AE1: 82, + 0x10AE4: 82, + 0x10AE5: 84, + 0x10AE6: 84, + 0x10AEB: 68, + 0x10AEC: 68, + 0x10AED: 68, + 0x10AEE: 68, + 0x10AEF: 82, + 0x10B80: 68, + 0x10B81: 82, + 0x10B82: 68, + 0x10B83: 82, + 0x10B84: 82, + 0x10B85: 82, + 0x10B86: 68, + 0x10B87: 68, + 0x10B88: 68, + 0x10B89: 82, + 0x10B8A: 68, + 0x10B8B: 68, + 0x10B8C: 82, + 0x10B8D: 68, + 0x10B8E: 82, + 0x10B8F: 82, + 0x10B90: 68, + 0x10B91: 82, + 0x10BA9: 82, + 0x10BAA: 82, + 0x10BAB: 82, + 0x10BAC: 82, + 0x10BAD: 68, + 0x10BAE: 68, + 0x10D00: 76, + 0x10D01: 68, + 0x10D02: 68, + 0x10D03: 68, + 0x10D04: 68, + 0x10D05: 68, + 0x10D06: 68, + 0x10D07: 68, + 0x10D08: 68, + 0x10D09: 68, + 0x10D0A: 68, + 0x10D0B: 68, + 0x10D0C: 68, + 0x10D0D: 68, + 0x10D0E: 68, + 0x10D0F: 68, + 0x10D10: 68, + 0x10D11: 68, + 0x10D12: 68, + 0x10D13: 68, + 0x10D14: 68, + 0x10D15: 68, + 0x10D16: 68, + 0x10D17: 68, + 0x10D18: 68, + 0x10D19: 68, + 0x10D1A: 68, + 0x10D1B: 68, + 0x10D1C: 68, + 0x10D1D: 68, + 0x10D1E: 68, + 0x10D1F: 68, + 0x10D20: 68, + 0x10D21: 68, + 0x10D22: 82, + 0x10D23: 68, + 0x10D24: 84, + 0x10D25: 84, + 0x10D26: 84, + 0x10D27: 84, + 0x10D69: 84, + 0x10D6A: 84, + 0x10D6B: 84, + 0x10D6C: 84, + 0x10D6D: 84, + 0x10EAB: 84, + 0x10EAC: 84, + 0x10EC2: 82, + 0x10EC3: 68, + 0x10EC4: 68, + 0x10EFC: 84, + 0x10EFD: 84, + 0x10EFE: 84, + 0x10EFF: 84, + 0x10F30: 68, + 0x10F31: 68, + 0x10F32: 68, + 0x10F33: 82, + 0x10F34: 68, + 0x10F35: 68, + 0x10F36: 68, + 0x10F37: 68, + 0x10F38: 68, + 0x10F39: 68, + 0x10F3A: 68, + 0x10F3B: 68, + 0x10F3C: 68, + 0x10F3D: 68, + 0x10F3E: 68, + 0x10F3F: 68, + 0x10F40: 68, + 0x10F41: 68, + 0x10F42: 68, + 0x10F43: 68, + 0x10F44: 68, + 0x10F46: 84, + 0x10F47: 84, + 0x10F48: 84, + 0x10F49: 84, + 0x10F4A: 84, + 0x10F4B: 84, + 0x10F4C: 84, + 0x10F4D: 84, + 0x10F4E: 84, + 0x10F4F: 84, + 0x10F50: 84, + 0x10F51: 68, + 0x10F52: 68, + 0x10F53: 68, + 0x10F54: 82, + 0x10F70: 68, + 0x10F71: 68, + 0x10F72: 68, + 0x10F73: 68, + 0x10F74: 82, + 0x10F75: 82, + 0x10F76: 68, + 0x10F77: 68, + 0x10F78: 68, + 0x10F79: 68, + 0x10F7A: 68, + 0x10F7B: 68, + 0x10F7C: 68, + 0x10F7D: 68, + 0x10F7E: 68, + 0x10F7F: 68, + 0x10F80: 68, + 0x10F81: 68, + 0x10F82: 84, + 0x10F83: 84, + 0x10F84: 84, + 0x10F85: 84, + 0x10FB0: 68, + 0x10FB2: 68, + 0x10FB3: 68, + 0x10FB4: 82, + 0x10FB5: 82, + 0x10FB6: 82, + 0x10FB8: 68, + 0x10FB9: 82, + 0x10FBA: 82, + 0x10FBB: 68, + 0x10FBC: 68, + 0x10FBD: 82, + 0x10FBE: 68, + 0x10FBF: 68, + 0x10FC1: 68, + 0x10FC2: 82, + 0x10FC3: 82, + 0x10FC4: 68, + 0x10FC9: 82, + 0x10FCA: 68, + 0x10FCB: 76, + 0x11001: 84, + 0x11038: 84, + 0x11039: 84, + 0x1103A: 84, + 0x1103B: 84, + 0x1103C: 84, + 0x1103D: 84, + 0x1103E: 84, + 0x1103F: 84, + 0x11040: 84, + 0x11041: 84, + 0x11042: 84, + 0x11043: 84, + 0x11044: 84, + 0x11045: 84, + 0x11046: 84, + 0x11070: 84, + 0x11073: 84, + 0x11074: 84, + 0x1107F: 84, + 0x11080: 84, + 0x11081: 84, + 0x110B3: 84, + 0x110B4: 84, + 0x110B5: 84, + 0x110B6: 84, + 0x110B9: 84, + 0x110BA: 84, + 0x110C2: 84, + 0x11100: 84, + 0x11101: 84, + 0x11102: 84, + 0x11127: 84, + 0x11128: 84, + 0x11129: 84, + 0x1112A: 84, + 0x1112B: 84, + 0x1112D: 84, + 0x1112E: 84, + 0x1112F: 84, + 0x11130: 84, + 0x11131: 84, + 0x11132: 84, + 0x11133: 84, + 0x11134: 84, + 0x11173: 84, + 0x11180: 84, + 0x11181: 84, + 0x111B6: 84, + 0x111B7: 84, + 0x111B8: 84, + 0x111B9: 84, + 0x111BA: 84, + 0x111BB: 84, + 0x111BC: 84, + 0x111BD: 84, + 0x111BE: 84, + 0x111C9: 84, + 0x111CA: 84, + 0x111CB: 84, + 0x111CC: 84, + 0x111CF: 84, + 0x1122F: 84, + 0x11230: 84, + 0x11231: 84, + 0x11234: 84, + 0x11236: 84, + 0x11237: 84, + 0x1123E: 84, + 0x11241: 84, + 0x112DF: 84, + 0x112E3: 84, + 0x112E4: 84, + 0x112E5: 84, + 0x112E6: 84, + 0x112E7: 84, + 0x112E8: 84, + 0x112E9: 84, + 0x112EA: 84, + 0x11300: 84, + 0x11301: 84, + 0x1133B: 84, + 0x1133C: 84, + 0x11340: 84, + 0x11366: 84, + 0x11367: 84, + 0x11368: 84, + 0x11369: 84, + 0x1136A: 84, + 0x1136B: 84, + 0x1136C: 84, + 0x11370: 84, + 0x11371: 84, + 0x11372: 84, + 0x11373: 84, + 0x11374: 84, + 0x113BB: 84, + 0x113BC: 84, + 0x113BD: 84, + 0x113BE: 84, + 0x113BF: 84, + 0x113C0: 84, + 0x113CE: 84, + 0x113D0: 84, + 0x113D2: 84, + 0x113E1: 84, + 0x113E2: 84, + 0x11438: 84, + 0x11439: 84, + 0x1143A: 84, + 0x1143B: 84, + 0x1143C: 84, + 0x1143D: 84, + 0x1143E: 84, + 0x1143F: 84, + 0x11442: 84, + 0x11443: 84, + 0x11444: 84, + 0x11446: 84, + 0x1145E: 84, + 0x114B3: 84, + 0x114B4: 84, + 0x114B5: 84, + 0x114B6: 84, + 0x114B7: 84, + 0x114B8: 84, + 0x114BA: 84, + 0x114BF: 84, + 0x114C0: 84, + 0x114C2: 84, + 0x114C3: 84, + 0x115B2: 84, + 0x115B3: 84, + 0x115B4: 84, + 0x115B5: 84, + 0x115BC: 84, + 0x115BD: 84, + 0x115BF: 84, + 0x115C0: 84, + 0x115DC: 84, + 0x115DD: 84, + 0x11633: 84, + 0x11634: 84, + 0x11635: 84, + 0x11636: 84, + 0x11637: 84, + 0x11638: 84, + 0x11639: 84, + 0x1163A: 84, + 0x1163D: 84, + 0x1163F: 84, + 0x11640: 84, + 0x116AB: 84, + 0x116AD: 84, + 0x116B0: 84, + 0x116B1: 84, + 0x116B2: 84, + 0x116B3: 84, + 0x116B4: 84, + 0x116B5: 84, + 0x116B7: 84, + 0x1171D: 84, + 0x1171F: 84, + 0x11722: 84, + 0x11723: 84, + 0x11724: 84, + 0x11725: 84, + 0x11727: 84, + 0x11728: 84, + 0x11729: 84, + 0x1172A: 84, + 0x1172B: 84, + 0x1182F: 84, + 0x11830: 84, + 0x11831: 84, + 0x11832: 84, + 0x11833: 84, + 0x11834: 84, + 0x11835: 84, + 0x11836: 84, + 0x11837: 84, + 0x11839: 84, + 0x1183A: 84, + 0x1193B: 84, + 0x1193C: 84, + 0x1193E: 84, + 0x11943: 84, + 0x119D4: 84, + 0x119D5: 84, + 0x119D6: 84, + 0x119D7: 84, + 0x119DA: 84, + 0x119DB: 84, + 0x119E0: 84, + 0x11A01: 84, + 0x11A02: 84, + 0x11A03: 84, + 0x11A04: 84, + 0x11A05: 84, + 0x11A06: 84, + 0x11A07: 84, + 0x11A08: 84, + 0x11A09: 84, + 0x11A0A: 84, + 0x11A33: 84, + 0x11A34: 84, + 0x11A35: 84, + 0x11A36: 84, + 0x11A37: 84, + 0x11A38: 84, + 0x11A3B: 84, + 0x11A3C: 84, + 0x11A3D: 84, + 0x11A3E: 84, + 0x11A47: 84, + 0x11A51: 84, + 0x11A52: 84, + 0x11A53: 84, + 0x11A54: 84, + 0x11A55: 84, + 0x11A56: 84, + 0x11A59: 84, + 0x11A5A: 84, + 0x11A5B: 84, + 0x11A8A: 84, + 0x11A8B: 84, + 0x11A8C: 84, + 0x11A8D: 84, + 0x11A8E: 84, + 0x11A8F: 84, + 0x11A90: 84, + 0x11A91: 84, + 0x11A92: 84, + 0x11A93: 84, + 0x11A94: 84, + 0x11A95: 84, + 0x11A96: 84, + 0x11A98: 84, + 0x11A99: 84, + 0x11C30: 84, + 0x11C31: 84, + 0x11C32: 84, + 0x11C33: 84, + 0x11C34: 84, + 0x11C35: 84, + 0x11C36: 84, + 0x11C38: 84, + 0x11C39: 84, + 0x11C3A: 84, + 0x11C3B: 84, + 0x11C3C: 84, + 0x11C3D: 84, + 0x11C3F: 84, + 0x11C92: 84, + 0x11C93: 84, + 0x11C94: 84, + 0x11C95: 84, + 0x11C96: 84, + 0x11C97: 84, + 0x11C98: 84, + 0x11C99: 84, + 0x11C9A: 84, + 0x11C9B: 84, + 0x11C9C: 84, + 0x11C9D: 84, + 0x11C9E: 84, + 0x11C9F: 84, + 0x11CA0: 84, + 0x11CA1: 84, + 0x11CA2: 84, + 0x11CA3: 84, + 0x11CA4: 84, + 0x11CA5: 84, + 0x11CA6: 84, + 0x11CA7: 84, + 0x11CAA: 84, + 0x11CAB: 84, + 0x11CAC: 84, + 0x11CAD: 84, + 0x11CAE: 84, + 0x11CAF: 84, + 0x11CB0: 84, + 0x11CB2: 84, + 0x11CB3: 84, + 0x11CB5: 84, + 0x11CB6: 84, + 0x11D31: 84, + 0x11D32: 84, + 0x11D33: 84, + 0x11D34: 84, + 0x11D35: 84, + 0x11D36: 84, + 0x11D3A: 84, + 0x11D3C: 84, + 0x11D3D: 84, + 0x11D3F: 84, + 0x11D40: 84, + 0x11D41: 84, + 0x11D42: 84, + 0x11D43: 84, + 0x11D44: 84, + 0x11D45: 84, + 0x11D47: 84, + 0x11D90: 84, + 0x11D91: 84, + 0x11D95: 84, + 0x11D97: 84, + 0x11EF3: 84, + 0x11EF4: 84, + 0x11F00: 84, + 0x11F01: 84, + 0x11F36: 84, + 0x11F37: 84, + 0x11F38: 84, + 0x11F39: 84, + 0x11F3A: 84, + 0x11F40: 84, + 0x11F42: 84, + 0x11F5A: 84, + 0x13430: 84, + 0x13431: 84, + 0x13432: 84, + 0x13433: 84, + 0x13434: 84, + 0x13435: 84, + 0x13436: 84, + 0x13437: 84, + 0x13438: 84, + 0x13439: 84, + 0x1343A: 84, + 0x1343B: 84, + 0x1343C: 84, + 0x1343D: 84, + 0x1343E: 84, + 0x1343F: 84, + 0x13440: 84, + 0x13447: 84, + 0x13448: 84, + 0x13449: 84, + 0x1344A: 84, + 0x1344B: 84, + 0x1344C: 84, + 0x1344D: 84, + 0x1344E: 84, + 0x1344F: 84, + 0x13450: 84, + 0x13451: 84, + 0x13452: 84, + 0x13453: 84, + 0x13454: 84, + 0x13455: 84, + 0x1611E: 84, + 0x1611F: 84, + 0x16120: 84, + 0x16121: 84, + 0x16122: 84, + 0x16123: 84, + 0x16124: 84, + 0x16125: 84, + 0x16126: 84, + 0x16127: 84, + 0x16128: 84, + 0x16129: 84, + 0x1612D: 84, + 0x1612E: 84, + 0x1612F: 84, + 0x16AF0: 84, + 0x16AF1: 84, + 0x16AF2: 84, + 0x16AF3: 84, + 0x16AF4: 84, + 0x16B30: 84, + 0x16B31: 84, + 0x16B32: 84, + 0x16B33: 84, + 0x16B34: 84, + 0x16B35: 84, + 0x16B36: 84, + 0x16F4F: 84, + 0x16F8F: 84, + 0x16F90: 84, + 0x16F91: 84, + 0x16F92: 84, + 0x16FE4: 84, + 0x1BC9D: 84, + 0x1BC9E: 84, + 0x1BCA0: 84, + 0x1BCA1: 84, + 0x1BCA2: 84, + 0x1BCA3: 84, + 0x1CF00: 84, + 0x1CF01: 84, + 0x1CF02: 84, + 0x1CF03: 84, + 0x1CF04: 84, + 0x1CF05: 84, + 0x1CF06: 84, + 0x1CF07: 84, + 0x1CF08: 84, + 0x1CF09: 84, + 0x1CF0A: 84, + 0x1CF0B: 84, + 0x1CF0C: 84, + 0x1CF0D: 84, + 0x1CF0E: 84, + 0x1CF0F: 84, + 0x1CF10: 84, + 0x1CF11: 84, + 0x1CF12: 84, + 0x1CF13: 84, + 0x1CF14: 84, + 0x1CF15: 84, + 0x1CF16: 84, + 0x1CF17: 84, + 0x1CF18: 84, + 0x1CF19: 84, + 0x1CF1A: 84, + 0x1CF1B: 84, + 0x1CF1C: 84, + 0x1CF1D: 84, + 0x1CF1E: 84, + 0x1CF1F: 84, + 0x1CF20: 84, + 0x1CF21: 84, + 0x1CF22: 84, + 0x1CF23: 84, + 0x1CF24: 84, + 0x1CF25: 84, + 0x1CF26: 84, + 0x1CF27: 84, + 0x1CF28: 84, + 0x1CF29: 84, + 0x1CF2A: 84, + 0x1CF2B: 84, + 0x1CF2C: 84, + 0x1CF2D: 84, + 0x1CF30: 84, + 0x1CF31: 84, + 0x1CF32: 84, + 0x1CF33: 84, + 0x1CF34: 84, + 0x1CF35: 84, + 0x1CF36: 84, + 0x1CF37: 84, + 0x1CF38: 84, + 0x1CF39: 84, + 0x1CF3A: 84, + 0x1CF3B: 84, + 0x1CF3C: 84, + 0x1CF3D: 84, + 0x1CF3E: 84, + 0x1CF3F: 84, + 0x1CF40: 84, + 0x1CF41: 84, + 0x1CF42: 84, + 0x1CF43: 84, + 0x1CF44: 84, + 0x1CF45: 84, + 0x1CF46: 84, + 0x1D167: 84, + 0x1D168: 84, + 0x1D169: 84, + 0x1D173: 84, + 0x1D174: 84, + 0x1D175: 84, + 0x1D176: 84, + 0x1D177: 84, + 0x1D178: 84, + 0x1D179: 84, + 0x1D17A: 84, + 0x1D17B: 84, + 0x1D17C: 84, + 0x1D17D: 84, + 0x1D17E: 84, + 0x1D17F: 84, + 0x1D180: 84, + 0x1D181: 84, + 0x1D182: 84, + 0x1D185: 84, + 0x1D186: 84, + 0x1D187: 84, + 0x1D188: 84, + 0x1D189: 84, + 0x1D18A: 84, + 0x1D18B: 84, + 0x1D1AA: 84, + 0x1D1AB: 84, + 0x1D1AC: 84, + 0x1D1AD: 84, + 0x1D242: 84, + 0x1D243: 84, + 0x1D244: 84, + 0x1DA00: 84, + 0x1DA01: 84, + 0x1DA02: 84, + 0x1DA03: 84, + 0x1DA04: 84, + 0x1DA05: 84, + 0x1DA06: 84, + 0x1DA07: 84, + 0x1DA08: 84, + 0x1DA09: 84, + 0x1DA0A: 84, + 0x1DA0B: 84, + 0x1DA0C: 84, + 0x1DA0D: 84, + 0x1DA0E: 84, + 0x1DA0F: 84, + 0x1DA10: 84, + 0x1DA11: 84, + 0x1DA12: 84, + 0x1DA13: 84, + 0x1DA14: 84, + 0x1DA15: 84, + 0x1DA16: 84, + 0x1DA17: 84, + 0x1DA18: 84, + 0x1DA19: 84, + 0x1DA1A: 84, + 0x1DA1B: 84, + 0x1DA1C: 84, + 0x1DA1D: 84, + 0x1DA1E: 84, + 0x1DA1F: 84, + 0x1DA20: 84, + 0x1DA21: 84, + 0x1DA22: 84, + 0x1DA23: 84, + 0x1DA24: 84, + 0x1DA25: 84, + 0x1DA26: 84, + 0x1DA27: 84, + 0x1DA28: 84, + 0x1DA29: 84, + 0x1DA2A: 84, + 0x1DA2B: 84, + 0x1DA2C: 84, + 0x1DA2D: 84, + 0x1DA2E: 84, + 0x1DA2F: 84, + 0x1DA30: 84, + 0x1DA31: 84, + 0x1DA32: 84, + 0x1DA33: 84, + 0x1DA34: 84, + 0x1DA35: 84, + 0x1DA36: 84, + 0x1DA3B: 84, + 0x1DA3C: 84, + 0x1DA3D: 84, + 0x1DA3E: 84, + 0x1DA3F: 84, + 0x1DA40: 84, + 0x1DA41: 84, + 0x1DA42: 84, + 0x1DA43: 84, + 0x1DA44: 84, + 0x1DA45: 84, + 0x1DA46: 84, + 0x1DA47: 84, + 0x1DA48: 84, + 0x1DA49: 84, + 0x1DA4A: 84, + 0x1DA4B: 84, + 0x1DA4C: 84, + 0x1DA4D: 84, + 0x1DA4E: 84, + 0x1DA4F: 84, + 0x1DA50: 84, + 0x1DA51: 84, + 0x1DA52: 84, + 0x1DA53: 84, + 0x1DA54: 84, + 0x1DA55: 84, + 0x1DA56: 84, + 0x1DA57: 84, + 0x1DA58: 84, + 0x1DA59: 84, + 0x1DA5A: 84, + 0x1DA5B: 84, + 0x1DA5C: 84, + 0x1DA5D: 84, + 0x1DA5E: 84, + 0x1DA5F: 84, + 0x1DA60: 84, + 0x1DA61: 84, + 0x1DA62: 84, + 0x1DA63: 84, + 0x1DA64: 84, + 0x1DA65: 84, + 0x1DA66: 84, + 0x1DA67: 84, + 0x1DA68: 84, + 0x1DA69: 84, + 0x1DA6A: 84, + 0x1DA6B: 84, + 0x1DA6C: 84, + 0x1DA75: 84, + 0x1DA84: 84, + 0x1DA9B: 84, + 0x1DA9C: 84, + 0x1DA9D: 84, + 0x1DA9E: 84, + 0x1DA9F: 84, + 0x1DAA1: 84, + 0x1DAA2: 84, + 0x1DAA3: 84, + 0x1DAA4: 84, + 0x1DAA5: 84, + 0x1DAA6: 84, + 0x1DAA7: 84, + 0x1DAA8: 84, + 0x1DAA9: 84, + 0x1DAAA: 84, + 0x1DAAB: 84, + 0x1DAAC: 84, + 0x1DAAD: 84, + 0x1DAAE: 84, + 0x1DAAF: 84, + 0x1E000: 84, + 0x1E001: 84, + 0x1E002: 84, + 0x1E003: 84, + 0x1E004: 84, + 0x1E005: 84, + 0x1E006: 84, + 0x1E008: 84, + 0x1E009: 84, + 0x1E00A: 84, + 0x1E00B: 84, + 0x1E00C: 84, + 0x1E00D: 84, + 0x1E00E: 84, + 0x1E00F: 84, + 0x1E010: 84, + 0x1E011: 84, + 0x1E012: 84, + 0x1E013: 84, + 0x1E014: 84, + 0x1E015: 84, + 0x1E016: 84, + 0x1E017: 84, + 0x1E018: 84, + 0x1E01B: 84, + 0x1E01C: 84, + 0x1E01D: 84, + 0x1E01E: 84, + 0x1E01F: 84, + 0x1E020: 84, + 0x1E021: 84, + 0x1E023: 84, + 0x1E024: 84, + 0x1E026: 84, + 0x1E027: 84, + 0x1E028: 84, + 0x1E029: 84, + 0x1E02A: 84, + 0x1E08F: 84, + 0x1E130: 84, + 0x1E131: 84, + 0x1E132: 84, + 0x1E133: 84, + 0x1E134: 84, + 0x1E135: 84, + 0x1E136: 84, + 0x1E2AE: 84, + 0x1E2EC: 84, + 0x1E2ED: 84, + 0x1E2EE: 84, + 0x1E2EF: 84, + 0x1E4EC: 84, + 0x1E4ED: 84, + 0x1E4EE: 84, + 0x1E4EF: 84, + 0x1E5EE: 84, + 0x1E5EF: 84, + 0x1E8D0: 84, + 0x1E8D1: 84, + 0x1E8D2: 84, + 0x1E8D3: 84, + 0x1E8D4: 84, + 0x1E8D5: 84, + 0x1E8D6: 84, + 0x1E900: 68, + 0x1E901: 68, + 0x1E902: 68, + 0x1E903: 68, + 0x1E904: 68, + 0x1E905: 68, + 0x1E906: 68, + 0x1E907: 68, + 0x1E908: 68, + 0x1E909: 68, + 0x1E90A: 68, + 0x1E90B: 68, + 0x1E90C: 68, + 0x1E90D: 68, + 0x1E90E: 68, + 0x1E90F: 68, + 0x1E910: 68, + 0x1E911: 68, + 0x1E912: 68, + 0x1E913: 68, + 0x1E914: 68, + 0x1E915: 68, + 0x1E916: 68, + 0x1E917: 68, + 0x1E918: 68, + 0x1E919: 68, + 0x1E91A: 68, + 0x1E91B: 68, + 0x1E91C: 68, + 0x1E91D: 68, + 0x1E91E: 68, + 0x1E91F: 68, + 0x1E920: 68, + 0x1E921: 68, + 0x1E922: 68, + 0x1E923: 68, + 0x1E924: 68, + 0x1E925: 68, + 0x1E926: 68, + 0x1E927: 68, + 0x1E928: 68, + 0x1E929: 68, + 0x1E92A: 68, + 0x1E92B: 68, + 0x1E92C: 68, + 0x1E92D: 68, + 0x1E92E: 68, + 0x1E92F: 68, + 0x1E930: 68, + 0x1E931: 68, + 0x1E932: 68, + 0x1E933: 68, + 0x1E934: 68, + 0x1E935: 68, + 0x1E936: 68, + 0x1E937: 68, + 0x1E938: 68, + 0x1E939: 68, + 0x1E93A: 68, + 0x1E93B: 68, + 0x1E93C: 68, + 0x1E93D: 68, + 0x1E93E: 68, + 0x1E93F: 68, + 0x1E940: 68, + 0x1E941: 68, + 0x1E942: 68, + 0x1E943: 68, + 0x1E944: 84, + 0x1E945: 84, + 0x1E946: 84, + 0x1E947: 84, + 0x1E948: 84, + 0x1E949: 84, + 0x1E94A: 84, + 0x1E94B: 84, + 0xE0001: 84, + 0xE0020: 84, + 0xE0021: 84, + 0xE0022: 84, + 0xE0023: 84, + 0xE0024: 84, + 0xE0025: 84, + 0xE0026: 84, + 0xE0027: 84, + 0xE0028: 84, + 0xE0029: 84, + 0xE002A: 84, + 0xE002B: 84, + 0xE002C: 84, + 0xE002D: 84, + 0xE002E: 84, + 0xE002F: 84, + 0xE0030: 84, + 0xE0031: 84, + 0xE0032: 84, + 0xE0033: 84, + 0xE0034: 84, + 0xE0035: 84, + 0xE0036: 84, + 0xE0037: 84, + 0xE0038: 84, + 0xE0039: 84, + 0xE003A: 84, + 0xE003B: 84, + 0xE003C: 84, + 0xE003D: 84, + 0xE003E: 84, + 0xE003F: 84, + 0xE0040: 84, + 0xE0041: 84, + 0xE0042: 84, + 0xE0043: 84, + 0xE0044: 84, + 0xE0045: 84, + 0xE0046: 84, + 0xE0047: 84, + 0xE0048: 84, + 0xE0049: 84, + 0xE004A: 84, + 0xE004B: 84, + 0xE004C: 84, + 0xE004D: 84, + 0xE004E: 84, + 0xE004F: 84, + 0xE0050: 84, + 0xE0051: 84, + 0xE0052: 84, + 0xE0053: 84, + 0xE0054: 84, + 0xE0055: 84, + 0xE0056: 84, + 0xE0057: 84, + 0xE0058: 84, + 0xE0059: 84, + 0xE005A: 84, + 0xE005B: 84, + 0xE005C: 84, + 0xE005D: 84, + 0xE005E: 84, + 0xE005F: 84, + 0xE0060: 84, + 0xE0061: 84, + 0xE0062: 84, + 0xE0063: 84, + 0xE0064: 84, + 0xE0065: 84, + 0xE0066: 84, + 0xE0067: 84, + 0xE0068: 84, + 0xE0069: 84, + 0xE006A: 84, + 0xE006B: 84, + 0xE006C: 84, + 0xE006D: 84, + 0xE006E: 84, + 0xE006F: 84, + 0xE0070: 84, + 0xE0071: 84, + 0xE0072: 84, + 0xE0073: 84, + 0xE0074: 84, + 0xE0075: 84, + 0xE0076: 84, + 0xE0077: 84, + 0xE0078: 84, + 0xE0079: 84, + 0xE007A: 84, + 0xE007B: 84, + 0xE007C: 84, + 0xE007D: 84, + 0xE007E: 84, + 0xE007F: 84, + 0xE0100: 84, + 0xE0101: 84, + 0xE0102: 84, + 0xE0103: 84, + 0xE0104: 84, + 0xE0105: 84, + 0xE0106: 84, + 0xE0107: 84, + 0xE0108: 84, + 0xE0109: 84, + 0xE010A: 84, + 0xE010B: 84, + 0xE010C: 84, + 0xE010D: 84, + 0xE010E: 84, + 0xE010F: 84, + 0xE0110: 84, + 0xE0111: 84, + 0xE0112: 84, + 0xE0113: 84, + 0xE0114: 84, + 0xE0115: 84, + 0xE0116: 84, + 0xE0117: 84, + 0xE0118: 84, + 0xE0119: 84, + 0xE011A: 84, + 0xE011B: 84, + 0xE011C: 84, + 0xE011D: 84, + 0xE011E: 84, + 0xE011F: 84, + 0xE0120: 84, + 0xE0121: 84, + 0xE0122: 84, + 0xE0123: 84, + 0xE0124: 84, + 0xE0125: 84, + 0xE0126: 84, + 0xE0127: 84, + 0xE0128: 84, + 0xE0129: 84, + 0xE012A: 84, + 0xE012B: 84, + 0xE012C: 84, + 0xE012D: 84, + 0xE012E: 84, + 0xE012F: 84, + 0xE0130: 84, + 0xE0131: 84, + 0xE0132: 84, + 0xE0133: 84, + 0xE0134: 84, + 0xE0135: 84, + 0xE0136: 84, + 0xE0137: 84, + 0xE0138: 84, + 0xE0139: 84, + 0xE013A: 84, + 0xE013B: 84, + 0xE013C: 84, + 0xE013D: 84, + 0xE013E: 84, + 0xE013F: 84, + 0xE0140: 84, + 0xE0141: 84, + 0xE0142: 84, + 0xE0143: 84, + 0xE0144: 84, + 0xE0145: 84, + 0xE0146: 84, + 0xE0147: 84, + 0xE0148: 84, + 0xE0149: 84, + 0xE014A: 84, + 0xE014B: 84, + 0xE014C: 84, + 0xE014D: 84, + 0xE014E: 84, + 0xE014F: 84, + 0xE0150: 84, + 0xE0151: 84, + 0xE0152: 84, + 0xE0153: 84, + 0xE0154: 84, + 0xE0155: 84, + 0xE0156: 84, + 0xE0157: 84, + 0xE0158: 84, + 0xE0159: 84, + 0xE015A: 84, + 0xE015B: 84, + 0xE015C: 84, + 0xE015D: 84, + 0xE015E: 84, + 0xE015F: 84, + 0xE0160: 84, + 0xE0161: 84, + 0xE0162: 84, + 0xE0163: 84, + 0xE0164: 84, + 0xE0165: 84, + 0xE0166: 84, + 0xE0167: 84, + 0xE0168: 84, + 0xE0169: 84, + 0xE016A: 84, + 0xE016B: 84, + 0xE016C: 84, + 0xE016D: 84, + 0xE016E: 84, + 0xE016F: 84, + 0xE0170: 84, + 0xE0171: 84, + 0xE0172: 84, + 0xE0173: 84, + 0xE0174: 84, + 0xE0175: 84, + 0xE0176: 84, + 0xE0177: 84, + 0xE0178: 84, + 0xE0179: 84, + 0xE017A: 84, + 0xE017B: 84, + 0xE017C: 84, + 0xE017D: 84, + 0xE017E: 84, + 0xE017F: 84, + 0xE0180: 84, + 0xE0181: 84, + 0xE0182: 84, + 0xE0183: 84, + 0xE0184: 84, + 0xE0185: 84, + 0xE0186: 84, + 0xE0187: 84, + 0xE0188: 84, + 0xE0189: 84, + 0xE018A: 84, + 0xE018B: 84, + 0xE018C: 84, + 0xE018D: 84, + 0xE018E: 84, + 0xE018F: 84, + 0xE0190: 84, + 0xE0191: 84, + 0xE0192: 84, + 0xE0193: 84, + 0xE0194: 84, + 0xE0195: 84, + 0xE0196: 84, + 0xE0197: 84, + 0xE0198: 84, + 0xE0199: 84, + 0xE019A: 84, + 0xE019B: 84, + 0xE019C: 84, + 0xE019D: 84, + 0xE019E: 84, + 0xE019F: 84, + 0xE01A0: 84, + 0xE01A1: 84, + 0xE01A2: 84, + 0xE01A3: 84, + 0xE01A4: 84, + 0xE01A5: 84, + 0xE01A6: 84, + 0xE01A7: 84, + 0xE01A8: 84, + 0xE01A9: 84, + 0xE01AA: 84, + 0xE01AB: 84, + 0xE01AC: 84, + 0xE01AD: 84, + 0xE01AE: 84, + 0xE01AF: 84, + 0xE01B0: 84, + 0xE01B1: 84, + 0xE01B2: 84, + 0xE01B3: 84, + 0xE01B4: 84, + 0xE01B5: 84, + 0xE01B6: 84, + 0xE01B7: 84, + 0xE01B8: 84, + 0xE01B9: 84, + 0xE01BA: 84, + 0xE01BB: 84, + 0xE01BC: 84, + 0xE01BD: 84, + 0xE01BE: 84, + 0xE01BF: 84, + 0xE01C0: 84, + 0xE01C1: 84, + 0xE01C2: 84, + 0xE01C3: 84, + 0xE01C4: 84, + 0xE01C5: 84, + 0xE01C6: 84, + 0xE01C7: 84, + 0xE01C8: 84, + 0xE01C9: 84, + 0xE01CA: 84, + 0xE01CB: 84, + 0xE01CC: 84, + 0xE01CD: 84, + 0xE01CE: 84, + 0xE01CF: 84, + 0xE01D0: 84, + 0xE01D1: 84, + 0xE01D2: 84, + 0xE01D3: 84, + 0xE01D4: 84, + 0xE01D5: 84, + 0xE01D6: 84, + 0xE01D7: 84, + 0xE01D8: 84, + 0xE01D9: 84, + 0xE01DA: 84, + 0xE01DB: 84, + 0xE01DC: 84, + 0xE01DD: 84, + 0xE01DE: 84, + 0xE01DF: 84, + 0xE01E0: 84, + 0xE01E1: 84, + 0xE01E2: 84, + 0xE01E3: 84, + 0xE01E4: 84, + 0xE01E5: 84, + 0xE01E6: 84, + 0xE01E7: 84, + 0xE01E8: 84, + 0xE01E9: 84, + 0xE01EA: 84, + 0xE01EB: 84, + 0xE01EC: 84, + 0xE01ED: 84, + 0xE01EE: 84, + 0xE01EF: 84, +} +codepoint_classes = { + "PVALID": ( + 0x2D0000002E, + 0x300000003A, + 0x610000007B, + 0xDF000000F7, + 0xF800000100, + 0x10100000102, + 0x10300000104, + 0x10500000106, + 0x10700000108, + 0x1090000010A, + 0x10B0000010C, + 0x10D0000010E, + 0x10F00000110, + 0x11100000112, + 0x11300000114, + 0x11500000116, + 0x11700000118, + 0x1190000011A, + 0x11B0000011C, + 0x11D0000011E, + 0x11F00000120, + 0x12100000122, + 0x12300000124, + 0x12500000126, + 0x12700000128, + 0x1290000012A, + 0x12B0000012C, + 0x12D0000012E, + 0x12F00000130, + 0x13100000132, + 0x13500000136, + 0x13700000139, + 0x13A0000013B, + 0x13C0000013D, + 0x13E0000013F, + 0x14200000143, + 0x14400000145, + 0x14600000147, + 0x14800000149, + 0x14B0000014C, + 0x14D0000014E, + 0x14F00000150, + 0x15100000152, + 0x15300000154, + 0x15500000156, + 0x15700000158, + 0x1590000015A, + 0x15B0000015C, + 0x15D0000015E, + 0x15F00000160, + 0x16100000162, + 0x16300000164, + 0x16500000166, + 0x16700000168, + 0x1690000016A, + 0x16B0000016C, + 0x16D0000016E, + 0x16F00000170, + 0x17100000172, + 0x17300000174, + 0x17500000176, + 0x17700000178, + 0x17A0000017B, + 0x17C0000017D, + 0x17E0000017F, + 0x18000000181, + 0x18300000184, + 0x18500000186, + 0x18800000189, + 0x18C0000018E, + 0x19200000193, + 0x19500000196, + 0x1990000019C, + 0x19E0000019F, + 0x1A1000001A2, + 0x1A3000001A4, + 0x1A5000001A6, + 0x1A8000001A9, + 0x1AA000001AC, + 0x1AD000001AE, + 0x1B0000001B1, + 0x1B4000001B5, + 0x1B6000001B7, + 0x1B9000001BC, + 0x1BD000001C4, + 0x1CE000001CF, + 0x1D0000001D1, + 0x1D2000001D3, + 0x1D4000001D5, + 0x1D6000001D7, + 0x1D8000001D9, + 0x1DA000001DB, + 0x1DC000001DE, + 0x1DF000001E0, + 0x1E1000001E2, + 0x1E3000001E4, + 0x1E5000001E6, + 0x1E7000001E8, + 0x1E9000001EA, + 0x1EB000001EC, + 0x1ED000001EE, + 0x1EF000001F1, + 0x1F5000001F6, + 0x1F9000001FA, + 0x1FB000001FC, + 0x1FD000001FE, + 0x1FF00000200, + 0x20100000202, + 0x20300000204, + 0x20500000206, + 0x20700000208, + 0x2090000020A, + 0x20B0000020C, + 0x20D0000020E, + 0x20F00000210, + 0x21100000212, + 0x21300000214, + 0x21500000216, + 0x21700000218, + 0x2190000021A, + 0x21B0000021C, + 0x21D0000021E, + 0x21F00000220, + 0x22100000222, + 0x22300000224, + 0x22500000226, + 0x22700000228, + 0x2290000022A, + 0x22B0000022C, + 0x22D0000022E, + 0x22F00000230, + 0x23100000232, + 0x2330000023A, + 0x23C0000023D, + 0x23F00000241, + 0x24200000243, + 0x24700000248, + 0x2490000024A, + 0x24B0000024C, + 0x24D0000024E, + 0x24F000002B0, + 0x2B9000002C2, + 0x2C6000002D2, + 0x2EC000002ED, + 0x2EE000002EF, + 0x30000000340, + 0x34200000343, + 0x3460000034F, + 0x35000000370, + 0x37100000372, + 0x37300000374, + 0x37700000378, + 0x37B0000037E, + 0x39000000391, + 0x3AC000003CF, + 0x3D7000003D8, + 0x3D9000003DA, + 0x3DB000003DC, + 0x3DD000003DE, + 0x3DF000003E0, + 0x3E1000003E2, + 0x3E3000003E4, + 0x3E5000003E6, + 0x3E7000003E8, + 0x3E9000003EA, + 0x3EB000003EC, + 0x3ED000003EE, + 0x3EF000003F0, + 0x3F3000003F4, + 0x3F8000003F9, + 0x3FB000003FD, + 0x43000000460, + 0x46100000462, + 0x46300000464, + 0x46500000466, + 0x46700000468, + 0x4690000046A, + 0x46B0000046C, + 0x46D0000046E, + 0x46F00000470, + 0x47100000472, + 0x47300000474, + 0x47500000476, + 0x47700000478, + 0x4790000047A, + 0x47B0000047C, + 0x47D0000047E, + 0x47F00000480, + 0x48100000482, + 0x48300000488, + 0x48B0000048C, + 0x48D0000048E, + 0x48F00000490, + 0x49100000492, + 0x49300000494, + 0x49500000496, + 0x49700000498, + 0x4990000049A, + 0x49B0000049C, + 0x49D0000049E, + 0x49F000004A0, + 0x4A1000004A2, + 0x4A3000004A4, + 0x4A5000004A6, + 0x4A7000004A8, + 0x4A9000004AA, + 0x4AB000004AC, + 0x4AD000004AE, + 0x4AF000004B0, + 0x4B1000004B2, + 0x4B3000004B4, + 0x4B5000004B6, + 0x4B7000004B8, + 0x4B9000004BA, + 0x4BB000004BC, + 0x4BD000004BE, + 0x4BF000004C0, + 0x4C2000004C3, + 0x4C4000004C5, + 0x4C6000004C7, + 0x4C8000004C9, + 0x4CA000004CB, + 0x4CC000004CD, + 0x4CE000004D0, + 0x4D1000004D2, + 0x4D3000004D4, + 0x4D5000004D6, + 0x4D7000004D8, + 0x4D9000004DA, + 0x4DB000004DC, + 0x4DD000004DE, + 0x4DF000004E0, + 0x4E1000004E2, + 0x4E3000004E4, + 0x4E5000004E6, + 0x4E7000004E8, + 0x4E9000004EA, + 0x4EB000004EC, + 0x4ED000004EE, + 0x4EF000004F0, + 0x4F1000004F2, + 0x4F3000004F4, + 0x4F5000004F6, + 0x4F7000004F8, + 0x4F9000004FA, + 0x4FB000004FC, + 0x4FD000004FE, + 0x4FF00000500, + 0x50100000502, + 0x50300000504, + 0x50500000506, + 0x50700000508, + 0x5090000050A, + 0x50B0000050C, + 0x50D0000050E, + 0x50F00000510, + 0x51100000512, + 0x51300000514, + 0x51500000516, + 0x51700000518, + 0x5190000051A, + 0x51B0000051C, + 0x51D0000051E, + 0x51F00000520, + 0x52100000522, + 0x52300000524, + 0x52500000526, + 0x52700000528, + 0x5290000052A, + 0x52B0000052C, + 0x52D0000052E, + 0x52F00000530, + 0x5590000055A, + 0x56000000587, + 0x58800000589, + 0x591000005BE, + 0x5BF000005C0, + 0x5C1000005C3, + 0x5C4000005C6, + 0x5C7000005C8, + 0x5D0000005EB, + 0x5EF000005F3, + 0x6100000061B, + 0x62000000640, + 0x64100000660, + 0x66E00000675, + 0x679000006D4, + 0x6D5000006DD, + 0x6DF000006E9, + 0x6EA000006F0, + 0x6FA00000700, + 0x7100000074B, + 0x74D000007B2, + 0x7C0000007F6, + 0x7FD000007FE, + 0x8000000082E, + 0x8400000085C, + 0x8600000086B, + 0x87000000888, + 0x8890000088F, + 0x897000008E2, + 0x8E300000958, + 0x96000000964, + 0x96600000970, + 0x97100000984, + 0x9850000098D, + 0x98F00000991, + 0x993000009A9, + 0x9AA000009B1, + 0x9B2000009B3, + 0x9B6000009BA, + 0x9BC000009C5, + 0x9C7000009C9, + 0x9CB000009CF, + 0x9D7000009D8, + 0x9E0000009E4, + 0x9E6000009F2, + 0x9FC000009FD, + 0x9FE000009FF, + 0xA0100000A04, + 0xA0500000A0B, + 0xA0F00000A11, + 0xA1300000A29, + 0xA2A00000A31, + 0xA3200000A33, + 0xA3500000A36, + 0xA3800000A3A, + 0xA3C00000A3D, + 0xA3E00000A43, + 0xA4700000A49, + 0xA4B00000A4E, + 0xA5100000A52, + 0xA5C00000A5D, + 0xA6600000A76, + 0xA8100000A84, + 0xA8500000A8E, + 0xA8F00000A92, + 0xA9300000AA9, + 0xAAA00000AB1, + 0xAB200000AB4, + 0xAB500000ABA, + 0xABC00000AC6, + 0xAC700000ACA, + 0xACB00000ACE, + 0xAD000000AD1, + 0xAE000000AE4, + 0xAE600000AF0, + 0xAF900000B00, + 0xB0100000B04, + 0xB0500000B0D, + 0xB0F00000B11, + 0xB1300000B29, + 0xB2A00000B31, + 0xB3200000B34, + 0xB3500000B3A, + 0xB3C00000B45, + 0xB4700000B49, + 0xB4B00000B4E, + 0xB5500000B58, + 0xB5F00000B64, + 0xB6600000B70, + 0xB7100000B72, + 0xB8200000B84, + 0xB8500000B8B, + 0xB8E00000B91, + 0xB9200000B96, + 0xB9900000B9B, + 0xB9C00000B9D, + 0xB9E00000BA0, + 0xBA300000BA5, + 0xBA800000BAB, + 0xBAE00000BBA, + 0xBBE00000BC3, + 0xBC600000BC9, + 0xBCA00000BCE, + 0xBD000000BD1, + 0xBD700000BD8, + 0xBE600000BF0, + 0xC0000000C0D, + 0xC0E00000C11, + 0xC1200000C29, + 0xC2A00000C3A, + 0xC3C00000C45, + 0xC4600000C49, + 0xC4A00000C4E, + 0xC5500000C57, + 0xC5800000C5B, + 0xC5D00000C5E, + 0xC6000000C64, + 0xC6600000C70, + 0xC8000000C84, + 0xC8500000C8D, + 0xC8E00000C91, + 0xC9200000CA9, + 0xCAA00000CB4, + 0xCB500000CBA, + 0xCBC00000CC5, + 0xCC600000CC9, + 0xCCA00000CCE, + 0xCD500000CD7, + 0xCDD00000CDF, + 0xCE000000CE4, + 0xCE600000CF0, + 0xCF100000CF4, + 0xD0000000D0D, + 0xD0E00000D11, + 0xD1200000D45, + 0xD4600000D49, + 0xD4A00000D4F, + 0xD5400000D58, + 0xD5F00000D64, + 0xD6600000D70, + 0xD7A00000D80, + 0xD8100000D84, + 0xD8500000D97, + 0xD9A00000DB2, + 0xDB300000DBC, + 0xDBD00000DBE, + 0xDC000000DC7, + 0xDCA00000DCB, + 0xDCF00000DD5, + 0xDD600000DD7, + 0xDD800000DE0, + 0xDE600000DF0, + 0xDF200000DF4, + 0xE0100000E33, + 0xE3400000E3B, + 0xE4000000E4F, + 0xE5000000E5A, + 0xE8100000E83, + 0xE8400000E85, + 0xE8600000E8B, + 0xE8C00000EA4, + 0xEA500000EA6, + 0xEA700000EB3, + 0xEB400000EBE, + 0xEC000000EC5, + 0xEC600000EC7, + 0xEC800000ECF, + 0xED000000EDA, + 0xEDE00000EE0, + 0xF0000000F01, + 0xF0B00000F0C, + 0xF1800000F1A, + 0xF2000000F2A, + 0xF3500000F36, + 0xF3700000F38, + 0xF3900000F3A, + 0xF3E00000F43, + 0xF4400000F48, + 0xF4900000F4D, + 0xF4E00000F52, + 0xF5300000F57, + 0xF5800000F5C, + 0xF5D00000F69, + 0xF6A00000F6D, + 0xF7100000F73, + 0xF7400000F75, + 0xF7A00000F81, + 0xF8200000F85, + 0xF8600000F93, + 0xF9400000F98, + 0xF9900000F9D, + 0xF9E00000FA2, + 0xFA300000FA7, + 0xFA800000FAC, + 0xFAD00000FB9, + 0xFBA00000FBD, + 0xFC600000FC7, + 0x10000000104A, + 0x10500000109E, + 0x10D0000010FB, + 0x10FD00001100, + 0x120000001249, + 0x124A0000124E, + 0x125000001257, + 0x125800001259, + 0x125A0000125E, + 0x126000001289, + 0x128A0000128E, + 0x1290000012B1, + 0x12B2000012B6, + 0x12B8000012BF, + 0x12C0000012C1, + 0x12C2000012C6, + 0x12C8000012D7, + 0x12D800001311, + 0x131200001316, + 0x13180000135B, + 0x135D00001360, + 0x138000001390, + 0x13A0000013F6, + 0x14010000166D, + 0x166F00001680, + 0x16810000169B, + 0x16A0000016EB, + 0x16F1000016F9, + 0x170000001716, + 0x171F00001735, + 0x174000001754, + 0x17600000176D, + 0x176E00001771, + 0x177200001774, + 0x1780000017B4, + 0x17B6000017D4, + 0x17D7000017D8, + 0x17DC000017DE, + 0x17E0000017EA, + 0x18100000181A, + 0x182000001879, + 0x1880000018AB, + 0x18B0000018F6, + 0x19000000191F, + 0x19200000192C, + 0x19300000193C, + 0x19460000196E, + 0x197000001975, + 0x1980000019AC, + 0x19B0000019CA, + 0x19D0000019DA, + 0x1A0000001A1C, + 0x1A2000001A5F, + 0x1A6000001A7D, + 0x1A7F00001A8A, + 0x1A9000001A9A, + 0x1AA700001AA8, + 0x1AB000001ABE, + 0x1ABF00001ACF, + 0x1B0000001B4D, + 0x1B5000001B5A, + 0x1B6B00001B74, + 0x1B8000001BF4, + 0x1C0000001C38, + 0x1C4000001C4A, + 0x1C4D00001C7E, + 0x1C8A00001C8B, + 0x1CD000001CD3, + 0x1CD400001CFB, + 0x1D0000001D2C, + 0x1D2F00001D30, + 0x1D3B00001D3C, + 0x1D4E00001D4F, + 0x1D6B00001D78, + 0x1D7900001D9B, + 0x1DC000001E00, + 0x1E0100001E02, + 0x1E0300001E04, + 0x1E0500001E06, + 0x1E0700001E08, + 0x1E0900001E0A, + 0x1E0B00001E0C, + 0x1E0D00001E0E, + 0x1E0F00001E10, + 0x1E1100001E12, + 0x1E1300001E14, + 0x1E1500001E16, + 0x1E1700001E18, + 0x1E1900001E1A, + 0x1E1B00001E1C, + 0x1E1D00001E1E, + 0x1E1F00001E20, + 0x1E2100001E22, + 0x1E2300001E24, + 0x1E2500001E26, + 0x1E2700001E28, + 0x1E2900001E2A, + 0x1E2B00001E2C, + 0x1E2D00001E2E, + 0x1E2F00001E30, + 0x1E3100001E32, + 0x1E3300001E34, + 0x1E3500001E36, + 0x1E3700001E38, + 0x1E3900001E3A, + 0x1E3B00001E3C, + 0x1E3D00001E3E, + 0x1E3F00001E40, + 0x1E4100001E42, + 0x1E4300001E44, + 0x1E4500001E46, + 0x1E4700001E48, + 0x1E4900001E4A, + 0x1E4B00001E4C, + 0x1E4D00001E4E, + 0x1E4F00001E50, + 0x1E5100001E52, + 0x1E5300001E54, + 0x1E5500001E56, + 0x1E5700001E58, + 0x1E5900001E5A, + 0x1E5B00001E5C, + 0x1E5D00001E5E, + 0x1E5F00001E60, + 0x1E6100001E62, + 0x1E6300001E64, + 0x1E6500001E66, + 0x1E6700001E68, + 0x1E6900001E6A, + 0x1E6B00001E6C, + 0x1E6D00001E6E, + 0x1E6F00001E70, + 0x1E7100001E72, + 0x1E7300001E74, + 0x1E7500001E76, + 0x1E7700001E78, + 0x1E7900001E7A, + 0x1E7B00001E7C, + 0x1E7D00001E7E, + 0x1E7F00001E80, + 0x1E8100001E82, + 0x1E8300001E84, + 0x1E8500001E86, + 0x1E8700001E88, + 0x1E8900001E8A, + 0x1E8B00001E8C, + 0x1E8D00001E8E, + 0x1E8F00001E90, + 0x1E9100001E92, + 0x1E9300001E94, + 0x1E9500001E9A, + 0x1E9C00001E9E, + 0x1E9F00001EA0, + 0x1EA100001EA2, + 0x1EA300001EA4, + 0x1EA500001EA6, + 0x1EA700001EA8, + 0x1EA900001EAA, + 0x1EAB00001EAC, + 0x1EAD00001EAE, + 0x1EAF00001EB0, + 0x1EB100001EB2, + 0x1EB300001EB4, + 0x1EB500001EB6, + 0x1EB700001EB8, + 0x1EB900001EBA, + 0x1EBB00001EBC, + 0x1EBD00001EBE, + 0x1EBF00001EC0, + 0x1EC100001EC2, + 0x1EC300001EC4, + 0x1EC500001EC6, + 0x1EC700001EC8, + 0x1EC900001ECA, + 0x1ECB00001ECC, + 0x1ECD00001ECE, + 0x1ECF00001ED0, + 0x1ED100001ED2, + 0x1ED300001ED4, + 0x1ED500001ED6, + 0x1ED700001ED8, + 0x1ED900001EDA, + 0x1EDB00001EDC, + 0x1EDD00001EDE, + 0x1EDF00001EE0, + 0x1EE100001EE2, + 0x1EE300001EE4, + 0x1EE500001EE6, + 0x1EE700001EE8, + 0x1EE900001EEA, + 0x1EEB00001EEC, + 0x1EED00001EEE, + 0x1EEF00001EF0, + 0x1EF100001EF2, + 0x1EF300001EF4, + 0x1EF500001EF6, + 0x1EF700001EF8, + 0x1EF900001EFA, + 0x1EFB00001EFC, + 0x1EFD00001EFE, + 0x1EFF00001F08, + 0x1F1000001F16, + 0x1F2000001F28, + 0x1F3000001F38, + 0x1F4000001F46, + 0x1F5000001F58, + 0x1F6000001F68, + 0x1F7000001F71, + 0x1F7200001F73, + 0x1F7400001F75, + 0x1F7600001F77, + 0x1F7800001F79, + 0x1F7A00001F7B, + 0x1F7C00001F7D, + 0x1FB000001FB2, + 0x1FB600001FB7, + 0x1FC600001FC7, + 0x1FD000001FD3, + 0x1FD600001FD8, + 0x1FE000001FE3, + 0x1FE400001FE8, + 0x1FF600001FF7, + 0x214E0000214F, + 0x218400002185, + 0x2C3000002C60, + 0x2C6100002C62, + 0x2C6500002C67, + 0x2C6800002C69, + 0x2C6A00002C6B, + 0x2C6C00002C6D, + 0x2C7100002C72, + 0x2C7300002C75, + 0x2C7600002C7C, + 0x2C8100002C82, + 0x2C8300002C84, + 0x2C8500002C86, + 0x2C8700002C88, + 0x2C8900002C8A, + 0x2C8B00002C8C, + 0x2C8D00002C8E, + 0x2C8F00002C90, + 0x2C9100002C92, + 0x2C9300002C94, + 0x2C9500002C96, + 0x2C9700002C98, + 0x2C9900002C9A, + 0x2C9B00002C9C, + 0x2C9D00002C9E, + 0x2C9F00002CA0, + 0x2CA100002CA2, + 0x2CA300002CA4, + 0x2CA500002CA6, + 0x2CA700002CA8, + 0x2CA900002CAA, + 0x2CAB00002CAC, + 0x2CAD00002CAE, + 0x2CAF00002CB0, + 0x2CB100002CB2, + 0x2CB300002CB4, + 0x2CB500002CB6, + 0x2CB700002CB8, + 0x2CB900002CBA, + 0x2CBB00002CBC, + 0x2CBD00002CBE, + 0x2CBF00002CC0, + 0x2CC100002CC2, + 0x2CC300002CC4, + 0x2CC500002CC6, + 0x2CC700002CC8, + 0x2CC900002CCA, + 0x2CCB00002CCC, + 0x2CCD00002CCE, + 0x2CCF00002CD0, + 0x2CD100002CD2, + 0x2CD300002CD4, + 0x2CD500002CD6, + 0x2CD700002CD8, + 0x2CD900002CDA, + 0x2CDB00002CDC, + 0x2CDD00002CDE, + 0x2CDF00002CE0, + 0x2CE100002CE2, + 0x2CE300002CE5, + 0x2CEC00002CED, + 0x2CEE00002CF2, + 0x2CF300002CF4, + 0x2D0000002D26, + 0x2D2700002D28, + 0x2D2D00002D2E, + 0x2D3000002D68, + 0x2D7F00002D97, + 0x2DA000002DA7, + 0x2DA800002DAF, + 0x2DB000002DB7, + 0x2DB800002DBF, + 0x2DC000002DC7, + 0x2DC800002DCF, + 0x2DD000002DD7, + 0x2DD800002DDF, + 0x2DE000002E00, + 0x2E2F00002E30, + 0x300500003008, + 0x302A0000302E, + 0x303C0000303D, + 0x304100003097, + 0x30990000309B, + 0x309D0000309F, + 0x30A1000030FB, + 0x30FC000030FF, + 0x310500003130, + 0x31A0000031C0, + 0x31F000003200, + 0x340000004DC0, + 0x4E000000A48D, + 0xA4D00000A4FE, + 0xA5000000A60D, + 0xA6100000A62C, + 0xA6410000A642, + 0xA6430000A644, + 0xA6450000A646, + 0xA6470000A648, + 0xA6490000A64A, + 0xA64B0000A64C, + 0xA64D0000A64E, + 0xA64F0000A650, + 0xA6510000A652, + 0xA6530000A654, + 0xA6550000A656, + 0xA6570000A658, + 0xA6590000A65A, + 0xA65B0000A65C, + 0xA65D0000A65E, + 0xA65F0000A660, + 0xA6610000A662, + 0xA6630000A664, + 0xA6650000A666, + 0xA6670000A668, + 0xA6690000A66A, + 0xA66B0000A66C, + 0xA66D0000A670, + 0xA6740000A67E, + 0xA67F0000A680, + 0xA6810000A682, + 0xA6830000A684, + 0xA6850000A686, + 0xA6870000A688, + 0xA6890000A68A, + 0xA68B0000A68C, + 0xA68D0000A68E, + 0xA68F0000A690, + 0xA6910000A692, + 0xA6930000A694, + 0xA6950000A696, + 0xA6970000A698, + 0xA6990000A69A, + 0xA69B0000A69C, + 0xA69E0000A6E6, + 0xA6F00000A6F2, + 0xA7170000A720, + 0xA7230000A724, + 0xA7250000A726, + 0xA7270000A728, + 0xA7290000A72A, + 0xA72B0000A72C, + 0xA72D0000A72E, + 0xA72F0000A732, + 0xA7330000A734, + 0xA7350000A736, + 0xA7370000A738, + 0xA7390000A73A, + 0xA73B0000A73C, + 0xA73D0000A73E, + 0xA73F0000A740, + 0xA7410000A742, + 0xA7430000A744, + 0xA7450000A746, + 0xA7470000A748, + 0xA7490000A74A, + 0xA74B0000A74C, + 0xA74D0000A74E, + 0xA74F0000A750, + 0xA7510000A752, + 0xA7530000A754, + 0xA7550000A756, + 0xA7570000A758, + 0xA7590000A75A, + 0xA75B0000A75C, + 0xA75D0000A75E, + 0xA75F0000A760, + 0xA7610000A762, + 0xA7630000A764, + 0xA7650000A766, + 0xA7670000A768, + 0xA7690000A76A, + 0xA76B0000A76C, + 0xA76D0000A76E, + 0xA76F0000A770, + 0xA7710000A779, + 0xA77A0000A77B, + 0xA77C0000A77D, + 0xA77F0000A780, + 0xA7810000A782, + 0xA7830000A784, + 0xA7850000A786, + 0xA7870000A789, + 0xA78C0000A78D, + 0xA78E0000A790, + 0xA7910000A792, + 0xA7930000A796, + 0xA7970000A798, + 0xA7990000A79A, + 0xA79B0000A79C, + 0xA79D0000A79E, + 0xA79F0000A7A0, + 0xA7A10000A7A2, + 0xA7A30000A7A4, + 0xA7A50000A7A6, + 0xA7A70000A7A8, + 0xA7A90000A7AA, + 0xA7AF0000A7B0, + 0xA7B50000A7B6, + 0xA7B70000A7B8, + 0xA7B90000A7BA, + 0xA7BB0000A7BC, + 0xA7BD0000A7BE, + 0xA7BF0000A7C0, + 0xA7C10000A7C2, + 0xA7C30000A7C4, + 0xA7C80000A7C9, + 0xA7CA0000A7CB, + 0xA7CD0000A7CE, + 0xA7D10000A7D2, + 0xA7D30000A7D4, + 0xA7D50000A7D6, + 0xA7D70000A7D8, + 0xA7D90000A7DA, + 0xA7DB0000A7DC, + 0xA7F60000A7F8, + 0xA7FA0000A828, + 0xA82C0000A82D, + 0xA8400000A874, + 0xA8800000A8C6, + 0xA8D00000A8DA, + 0xA8E00000A8F8, + 0xA8FB0000A8FC, + 0xA8FD0000A92E, + 0xA9300000A954, + 0xA9800000A9C1, + 0xA9CF0000A9DA, + 0xA9E00000A9FF, + 0xAA000000AA37, + 0xAA400000AA4E, + 0xAA500000AA5A, + 0xAA600000AA77, + 0xAA7A0000AAC3, + 0xAADB0000AADE, + 0xAAE00000AAF0, + 0xAAF20000AAF7, + 0xAB010000AB07, + 0xAB090000AB0F, + 0xAB110000AB17, + 0xAB200000AB27, + 0xAB280000AB2F, + 0xAB300000AB5B, + 0xAB600000AB69, + 0xABC00000ABEB, + 0xABEC0000ABEE, + 0xABF00000ABFA, + 0xAC000000D7A4, + 0xFA0E0000FA10, + 0xFA110000FA12, + 0xFA130000FA15, + 0xFA1F0000FA20, + 0xFA210000FA22, + 0xFA230000FA25, + 0xFA270000FA2A, + 0xFB1E0000FB1F, + 0xFE200000FE30, + 0xFE730000FE74, + 0x100000001000C, + 0x1000D00010027, + 0x100280001003B, + 0x1003C0001003E, + 0x1003F0001004E, + 0x100500001005E, + 0x10080000100FB, + 0x101FD000101FE, + 0x102800001029D, + 0x102A0000102D1, + 0x102E0000102E1, + 0x1030000010320, + 0x1032D00010341, + 0x103420001034A, + 0x103500001037B, + 0x103800001039E, + 0x103A0000103C4, + 0x103C8000103D0, + 0x104280001049E, + 0x104A0000104AA, + 0x104D8000104FC, + 0x1050000010528, + 0x1053000010564, + 0x10597000105A2, + 0x105A3000105B2, + 0x105B3000105BA, + 0x105BB000105BD, + 0x105C0000105F4, + 0x1060000010737, + 0x1074000010756, + 0x1076000010768, + 0x1078000010781, + 0x1080000010806, + 0x1080800010809, + 0x1080A00010836, + 0x1083700010839, + 0x1083C0001083D, + 0x1083F00010856, + 0x1086000010877, + 0x108800001089F, + 0x108E0000108F3, + 0x108F4000108F6, + 0x1090000010916, + 0x109200001093A, + 0x10980000109B8, + 0x109BE000109C0, + 0x10A0000010A04, + 0x10A0500010A07, + 0x10A0C00010A14, + 0x10A1500010A18, + 0x10A1900010A36, + 0x10A3800010A3B, + 0x10A3F00010A40, + 0x10A6000010A7D, + 0x10A8000010A9D, + 0x10AC000010AC8, + 0x10AC900010AE7, + 0x10B0000010B36, + 0x10B4000010B56, + 0x10B6000010B73, + 0x10B8000010B92, + 0x10C0000010C49, + 0x10CC000010CF3, + 0x10D0000010D28, + 0x10D3000010D3A, + 0x10D4000010D50, + 0x10D6900010D6E, + 0x10D6F00010D86, + 0x10E8000010EAA, + 0x10EAB00010EAD, + 0x10EB000010EB2, + 0x10EC200010EC5, + 0x10EFC00010F1D, + 0x10F2700010F28, + 0x10F3000010F51, + 0x10F7000010F86, + 0x10FB000010FC5, + 0x10FE000010FF7, + 0x1100000011047, + 0x1106600011076, + 0x1107F000110BB, + 0x110C2000110C3, + 0x110D0000110E9, + 0x110F0000110FA, + 0x1110000011135, + 0x1113600011140, + 0x1114400011148, + 0x1115000011174, + 0x1117600011177, + 0x11180000111C5, + 0x111C9000111CD, + 0x111CE000111DB, + 0x111DC000111DD, + 0x1120000011212, + 0x1121300011238, + 0x1123E00011242, + 0x1128000011287, + 0x1128800011289, + 0x1128A0001128E, + 0x1128F0001129E, + 0x1129F000112A9, + 0x112B0000112EB, + 0x112F0000112FA, + 0x1130000011304, + 0x113050001130D, + 0x1130F00011311, + 0x1131300011329, + 0x1132A00011331, + 0x1133200011334, + 0x113350001133A, + 0x1133B00011345, + 0x1134700011349, + 0x1134B0001134E, + 0x1135000011351, + 0x1135700011358, + 0x1135D00011364, + 0x113660001136D, + 0x1137000011375, + 0x113800001138A, + 0x1138B0001138C, + 0x1138E0001138F, + 0x11390000113B6, + 0x113B7000113C1, + 0x113C2000113C3, + 0x113C5000113C6, + 0x113C7000113CB, + 0x113CC000113D4, + 0x113E1000113E3, + 0x114000001144B, + 0x114500001145A, + 0x1145E00011462, + 0x11480000114C6, + 0x114C7000114C8, + 0x114D0000114DA, + 0x11580000115B6, + 0x115B8000115C1, + 0x115D8000115DE, + 0x1160000011641, + 0x1164400011645, + 0x116500001165A, + 0x11680000116B9, + 0x116C0000116CA, + 0x116D0000116E4, + 0x117000001171B, + 0x1171D0001172C, + 0x117300001173A, + 0x1174000011747, + 0x118000001183B, + 0x118C0000118EA, + 0x118FF00011907, + 0x119090001190A, + 0x1190C00011914, + 0x1191500011917, + 0x1191800011936, + 0x1193700011939, + 0x1193B00011944, + 0x119500001195A, + 0x119A0000119A8, + 0x119AA000119D8, + 0x119DA000119E2, + 0x119E3000119E5, + 0x11A0000011A3F, + 0x11A4700011A48, + 0x11A5000011A9A, + 0x11A9D00011A9E, + 0x11AB000011AF9, + 0x11BC000011BE1, + 0x11BF000011BFA, + 0x11C0000011C09, + 0x11C0A00011C37, + 0x11C3800011C41, + 0x11C5000011C5A, + 0x11C7200011C90, + 0x11C9200011CA8, + 0x11CA900011CB7, + 0x11D0000011D07, + 0x11D0800011D0A, + 0x11D0B00011D37, + 0x11D3A00011D3B, + 0x11D3C00011D3E, + 0x11D3F00011D48, + 0x11D5000011D5A, + 0x11D6000011D66, + 0x11D6700011D69, + 0x11D6A00011D8F, + 0x11D9000011D92, + 0x11D9300011D99, + 0x11DA000011DAA, + 0x11EE000011EF7, + 0x11F0000011F11, + 0x11F1200011F3B, + 0x11F3E00011F43, + 0x11F5000011F5B, + 0x11FB000011FB1, + 0x120000001239A, + 0x1248000012544, + 0x12F9000012FF1, + 0x1300000013430, + 0x1344000013456, + 0x13460000143FB, + 0x1440000014647, + 0x161000001613A, + 0x1680000016A39, + 0x16A4000016A5F, + 0x16A6000016A6A, + 0x16A7000016ABF, + 0x16AC000016ACA, + 0x16AD000016AEE, + 0x16AF000016AF5, + 0x16B0000016B37, + 0x16B4000016B44, + 0x16B5000016B5A, + 0x16B6300016B78, + 0x16B7D00016B90, + 0x16D4000016D6D, + 0x16D7000016D7A, + 0x16E6000016E80, + 0x16F0000016F4B, + 0x16F4F00016F88, + 0x16F8F00016FA0, + 0x16FE000016FE2, + 0x16FE300016FE5, + 0x16FF000016FF2, + 0x17000000187F8, + 0x1880000018CD6, + 0x18CFF00018D09, + 0x1AFF00001AFF4, + 0x1AFF50001AFFC, + 0x1AFFD0001AFFF, + 0x1B0000001B123, + 0x1B1320001B133, + 0x1B1500001B153, + 0x1B1550001B156, + 0x1B1640001B168, + 0x1B1700001B2FC, + 0x1BC000001BC6B, + 0x1BC700001BC7D, + 0x1BC800001BC89, + 0x1BC900001BC9A, + 0x1BC9D0001BC9F, + 0x1CCF00001CCFA, + 0x1CF000001CF2E, + 0x1CF300001CF47, + 0x1DA000001DA37, + 0x1DA3B0001DA6D, + 0x1DA750001DA76, + 0x1DA840001DA85, + 0x1DA9B0001DAA0, + 0x1DAA10001DAB0, + 0x1DF000001DF1F, + 0x1DF250001DF2B, + 0x1E0000001E007, + 0x1E0080001E019, + 0x1E01B0001E022, + 0x1E0230001E025, + 0x1E0260001E02B, + 0x1E08F0001E090, + 0x1E1000001E12D, + 0x1E1300001E13E, + 0x1E1400001E14A, + 0x1E14E0001E14F, + 0x1E2900001E2AF, + 0x1E2C00001E2FA, + 0x1E4D00001E4FA, + 0x1E5D00001E5FB, + 0x1E7E00001E7E7, + 0x1E7E80001E7EC, + 0x1E7ED0001E7EF, + 0x1E7F00001E7FF, + 0x1E8000001E8C5, + 0x1E8D00001E8D7, + 0x1E9220001E94C, + 0x1E9500001E95A, + 0x200000002A6E0, + 0x2A7000002B73A, + 0x2B7400002B81E, + 0x2B8200002CEA2, + 0x2CEB00002EBE1, + 0x2EBF00002EE5E, + 0x300000003134B, + 0x31350000323B0, + ), + "CONTEXTJ": (0x200C0000200E,), + "CONTEXTO": ( + 0xB7000000B8, + 0x37500000376, + 0x5F3000005F5, + 0x6600000066A, + 0x6F0000006FA, + 0x30FB000030FC, + ), +} diff --git a/.venv/lib/python3.12/site-packages/idna/intranges.py b/.venv/lib/python3.12/site-packages/idna/intranges.py new file mode 100644 index 0000000000000000000000000000000000000000..7bfaa8d80d7dc471d572db0f949460901126e8bd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/idna/intranges.py @@ -0,0 +1,57 @@ +""" +Given a list of integers, made up of (hopefully) a small number of long runs +of consecutive integers, compute a representation of the form +((start1, end1), (start2, end2) ...). Then answer the question "was x present +in the original list?" in time O(log(# runs)). +""" + +import bisect +from typing import List, Tuple + + +def intranges_from_list(list_: List[int]) -> Tuple[int, ...]: + """Represent a list of integers as a sequence of ranges: + ((start_0, end_0), (start_1, end_1), ...), such that the original + integers are exactly those x such that start_i <= x < end_i for some i. + + Ranges are encoded as single integers (start << 32 | end), not as tuples. + """ + + sorted_list = sorted(list_) + ranges = [] + last_write = -1 + for i in range(len(sorted_list)): + if i + 1 < len(sorted_list): + if sorted_list[i] == sorted_list[i + 1] - 1: + continue + current_range = sorted_list[last_write + 1 : i + 1] + ranges.append(_encode_range(current_range[0], current_range[-1] + 1)) + last_write = i + + return tuple(ranges) + + +def _encode_range(start: int, end: int) -> int: + return (start << 32) | end + + +def _decode_range(r: int) -> Tuple[int, int]: + return (r >> 32), (r & ((1 << 32) - 1)) + + +def intranges_contain(int_: int, ranges: Tuple[int, ...]) -> bool: + """Determine if `int_` falls into one of the ranges in `ranges`.""" + tuple_ = _encode_range(int_, 0) + pos = bisect.bisect_left(ranges, tuple_) + # we could be immediately ahead of a tuple (start, end) + # with start < int_ <= end + if pos > 0: + left, right = _decode_range(ranges[pos - 1]) + if left <= int_ < right: + return True + # or we could be immediately behind a tuple (int_, end) + if pos < len(ranges): + left, _ = _decode_range(ranges[pos]) + if left == int_: + return True + return False diff --git a/.venv/lib/python3.12/site-packages/idna/package_data.py b/.venv/lib/python3.12/site-packages/idna/package_data.py new file mode 100644 index 0000000000000000000000000000000000000000..7272c8d92364886c51fefd22837ed5ceab145606 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/idna/package_data.py @@ -0,0 +1 @@ +__version__ = "3.11" diff --git a/.venv/lib/python3.12/site-packages/idna/py.typed b/.venv/lib/python3.12/site-packages/idna/py.typed new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/idna/uts46data.py b/.venv/lib/python3.12/site-packages/idna/uts46data.py new file mode 100644 index 0000000000000000000000000000000000000000..4610b71dad9196838d4e1e04e76d5e7c9baf8cd9 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/idna/uts46data.py @@ -0,0 +1,8841 @@ +# This file is automatically generated by tools/idna-data +# vim: set fileencoding=utf-8 : + +from typing import List, Tuple, Union + +"""IDNA Mapping Table from UTS46.""" + + +__version__ = "16.0.0" + + +def _seg_0() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x0, "V"), + (0x1, "V"), + (0x2, "V"), + (0x3, "V"), + (0x4, "V"), + (0x5, "V"), + (0x6, "V"), + (0x7, "V"), + (0x8, "V"), + (0x9, "V"), + (0xA, "V"), + (0xB, "V"), + (0xC, "V"), + (0xD, "V"), + (0xE, "V"), + (0xF, "V"), + (0x10, "V"), + (0x11, "V"), + (0x12, "V"), + (0x13, "V"), + (0x14, "V"), + (0x15, "V"), + (0x16, "V"), + (0x17, "V"), + (0x18, "V"), + (0x19, "V"), + (0x1A, "V"), + (0x1B, "V"), + (0x1C, "V"), + (0x1D, "V"), + (0x1E, "V"), + (0x1F, "V"), + (0x20, "V"), + (0x21, "V"), + (0x22, "V"), + (0x23, "V"), + (0x24, "V"), + (0x25, "V"), + (0x26, "V"), + (0x27, "V"), + (0x28, "V"), + (0x29, "V"), + (0x2A, "V"), + (0x2B, "V"), + (0x2C, "V"), + (0x2D, "V"), + (0x2E, "V"), + (0x2F, "V"), + (0x30, "V"), + (0x31, "V"), + (0x32, "V"), + (0x33, "V"), + (0x34, "V"), + (0x35, "V"), + (0x36, "V"), + (0x37, "V"), + (0x38, "V"), + (0x39, "V"), + (0x3A, "V"), + (0x3B, "V"), + (0x3C, "V"), + (0x3D, "V"), + (0x3E, "V"), + (0x3F, "V"), + (0x40, "V"), + (0x41, "M", "a"), + (0x42, "M", "b"), + (0x43, "M", "c"), + (0x44, "M", "d"), + (0x45, "M", "e"), + (0x46, "M", "f"), + (0x47, "M", "g"), + (0x48, "M", "h"), + (0x49, "M", "i"), + (0x4A, "M", "j"), + (0x4B, "M", "k"), + (0x4C, "M", "l"), + (0x4D, "M", "m"), + (0x4E, "M", "n"), + (0x4F, "M", "o"), + (0x50, "M", "p"), + (0x51, "M", "q"), + (0x52, "M", "r"), + (0x53, "M", "s"), + (0x54, "M", "t"), + (0x55, "M", "u"), + (0x56, "M", "v"), + (0x57, "M", "w"), + (0x58, "M", "x"), + (0x59, "M", "y"), + (0x5A, "M", "z"), + (0x5B, "V"), + (0x5C, "V"), + (0x5D, "V"), + (0x5E, "V"), + (0x5F, "V"), + (0x60, "V"), + (0x61, "V"), + (0x62, "V"), + (0x63, "V"), + ] + + +def _seg_1() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x64, "V"), + (0x65, "V"), + (0x66, "V"), + (0x67, "V"), + (0x68, "V"), + (0x69, "V"), + (0x6A, "V"), + (0x6B, "V"), + (0x6C, "V"), + (0x6D, "V"), + (0x6E, "V"), + (0x6F, "V"), + (0x70, "V"), + (0x71, "V"), + (0x72, "V"), + (0x73, "V"), + (0x74, "V"), + (0x75, "V"), + (0x76, "V"), + (0x77, "V"), + (0x78, "V"), + (0x79, "V"), + (0x7A, "V"), + (0x7B, "V"), + (0x7C, "V"), + (0x7D, "V"), + (0x7E, "V"), + (0x7F, "V"), + (0x80, "X"), + (0x81, "X"), + (0x82, "X"), + (0x83, "X"), + (0x84, "X"), + (0x85, "X"), + (0x86, "X"), + (0x87, "X"), + (0x88, "X"), + (0x89, "X"), + (0x8A, "X"), + (0x8B, "X"), + (0x8C, "X"), + (0x8D, "X"), + (0x8E, "X"), + (0x8F, "X"), + (0x90, "X"), + (0x91, "X"), + (0x92, "X"), + (0x93, "X"), + (0x94, "X"), + (0x95, "X"), + (0x96, "X"), + (0x97, "X"), + (0x98, "X"), + (0x99, "X"), + (0x9A, "X"), + (0x9B, "X"), + (0x9C, "X"), + (0x9D, "X"), + (0x9E, "X"), + (0x9F, "X"), + (0xA0, "M", " "), + (0xA1, "V"), + (0xA2, "V"), + (0xA3, "V"), + (0xA4, "V"), + (0xA5, "V"), + (0xA6, "V"), + (0xA7, "V"), + (0xA8, "M", " ̈"), + (0xA9, "V"), + (0xAA, "M", "a"), + (0xAB, "V"), + (0xAC, "V"), + (0xAD, "I"), + (0xAE, "V"), + (0xAF, "M", " ̄"), + (0xB0, "V"), + (0xB1, "V"), + (0xB2, "M", "2"), + (0xB3, "M", "3"), + (0xB4, "M", " ́"), + (0xB5, "M", "μ"), + (0xB6, "V"), + (0xB7, "V"), + (0xB8, "M", " ̧"), + (0xB9, "M", "1"), + (0xBA, "M", "o"), + (0xBB, "V"), + (0xBC, "M", "1⁄4"), + (0xBD, "M", "1⁄2"), + (0xBE, "M", "3⁄4"), + (0xBF, "V"), + (0xC0, "M", "à"), + (0xC1, "M", "á"), + (0xC2, "M", "â"), + (0xC3, "M", "ã"), + (0xC4, "M", "ä"), + (0xC5, "M", "å"), + (0xC6, "M", "æ"), + (0xC7, "M", "ç"), + ] + + +def _seg_2() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xC8, "M", "è"), + (0xC9, "M", "é"), + (0xCA, "M", "ê"), + (0xCB, "M", "ë"), + (0xCC, "M", "ì"), + (0xCD, "M", "í"), + (0xCE, "M", "î"), + (0xCF, "M", "ï"), + (0xD0, "M", "ð"), + (0xD1, "M", "ñ"), + (0xD2, "M", "ò"), + (0xD3, "M", "ó"), + (0xD4, "M", "ô"), + (0xD5, "M", "õ"), + (0xD6, "M", "ö"), + (0xD7, "V"), + (0xD8, "M", "ø"), + (0xD9, "M", "ù"), + (0xDA, "M", "ú"), + (0xDB, "M", "û"), + (0xDC, "M", "ü"), + (0xDD, "M", "ý"), + (0xDE, "M", "þ"), + (0xDF, "D", "ss"), + (0xE0, "V"), + (0xE1, "V"), + (0xE2, "V"), + (0xE3, "V"), + (0xE4, "V"), + (0xE5, "V"), + (0xE6, "V"), + (0xE7, "V"), + (0xE8, "V"), + (0xE9, "V"), + (0xEA, "V"), + (0xEB, "V"), + (0xEC, "V"), + (0xED, "V"), + (0xEE, "V"), + (0xEF, "V"), + (0xF0, "V"), + (0xF1, "V"), + (0xF2, "V"), + (0xF3, "V"), + (0xF4, "V"), + (0xF5, "V"), + (0xF6, "V"), + (0xF7, "V"), + (0xF8, "V"), + (0xF9, "V"), + (0xFA, "V"), + (0xFB, "V"), + (0xFC, "V"), + (0xFD, "V"), + (0xFE, "V"), + (0xFF, "V"), + (0x100, "M", "ā"), + (0x101, "V"), + (0x102, "M", "ă"), + (0x103, "V"), + (0x104, "M", "ą"), + (0x105, "V"), + (0x106, "M", "ć"), + (0x107, "V"), + (0x108, "M", "ĉ"), + (0x109, "V"), + (0x10A, "M", "ċ"), + (0x10B, "V"), + (0x10C, "M", "č"), + (0x10D, "V"), + (0x10E, "M", "ď"), + (0x10F, "V"), + (0x110, "M", "đ"), + (0x111, "V"), + (0x112, "M", "ē"), + (0x113, "V"), + (0x114, "M", "ĕ"), + (0x115, "V"), + (0x116, "M", "ė"), + (0x117, "V"), + (0x118, "M", "ę"), + (0x119, "V"), + (0x11A, "M", "ě"), + (0x11B, "V"), + (0x11C, "M", "ĝ"), + (0x11D, "V"), + (0x11E, "M", "ğ"), + (0x11F, "V"), + (0x120, "M", "ġ"), + (0x121, "V"), + (0x122, "M", "ģ"), + (0x123, "V"), + (0x124, "M", "ĥ"), + (0x125, "V"), + (0x126, "M", "ħ"), + (0x127, "V"), + (0x128, "M", "ĩ"), + (0x129, "V"), + (0x12A, "M", "ī"), + (0x12B, "V"), + ] + + +def _seg_3() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x12C, "M", "ĭ"), + (0x12D, "V"), + (0x12E, "M", "į"), + (0x12F, "V"), + (0x130, "M", "i̇"), + (0x131, "V"), + (0x132, "M", "ij"), + (0x134, "M", "ĵ"), + (0x135, "V"), + (0x136, "M", "ķ"), + (0x137, "V"), + (0x139, "M", "ĺ"), + (0x13A, "V"), + (0x13B, "M", "ļ"), + (0x13C, "V"), + (0x13D, "M", "ľ"), + (0x13E, "V"), + (0x13F, "M", "l·"), + (0x141, "M", "ł"), + (0x142, "V"), + (0x143, "M", "ń"), + (0x144, "V"), + (0x145, "M", "ņ"), + (0x146, "V"), + (0x147, "M", "ň"), + (0x148, "V"), + (0x149, "M", "ʼn"), + (0x14A, "M", "ŋ"), + (0x14B, "V"), + (0x14C, "M", "ō"), + (0x14D, "V"), + (0x14E, "M", "ŏ"), + (0x14F, "V"), + (0x150, "M", "ő"), + (0x151, "V"), + (0x152, "M", "œ"), + (0x153, "V"), + (0x154, "M", "ŕ"), + (0x155, "V"), + (0x156, "M", "ŗ"), + (0x157, "V"), + (0x158, "M", "ř"), + (0x159, "V"), + (0x15A, "M", "ś"), + (0x15B, "V"), + (0x15C, "M", "ŝ"), + (0x15D, "V"), + (0x15E, "M", "ş"), + (0x15F, "V"), + (0x160, "M", "š"), + (0x161, "V"), + (0x162, "M", "ţ"), + (0x163, "V"), + (0x164, "M", "ť"), + (0x165, "V"), + (0x166, "M", "ŧ"), + (0x167, "V"), + (0x168, "M", "ũ"), + (0x169, "V"), + (0x16A, "M", "ū"), + (0x16B, "V"), + (0x16C, "M", "ŭ"), + (0x16D, "V"), + (0x16E, "M", "ů"), + (0x16F, "V"), + (0x170, "M", "ű"), + (0x171, "V"), + (0x172, "M", "ų"), + (0x173, "V"), + (0x174, "M", "ŵ"), + (0x175, "V"), + (0x176, "M", "ŷ"), + (0x177, "V"), + (0x178, "M", "ÿ"), + (0x179, "M", "ź"), + (0x17A, "V"), + (0x17B, "M", "ż"), + (0x17C, "V"), + (0x17D, "M", "ž"), + (0x17E, "V"), + (0x17F, "M", "s"), + (0x180, "V"), + (0x181, "M", "ɓ"), + (0x182, "M", "ƃ"), + (0x183, "V"), + (0x184, "M", "ƅ"), + (0x185, "V"), + (0x186, "M", "ɔ"), + (0x187, "M", "ƈ"), + (0x188, "V"), + (0x189, "M", "ɖ"), + (0x18A, "M", "ɗ"), + (0x18B, "M", "ƌ"), + (0x18C, "V"), + (0x18E, "M", "ǝ"), + (0x18F, "M", "ə"), + (0x190, "M", "ɛ"), + (0x191, "M", "ƒ"), + (0x192, "V"), + (0x193, "M", "ɠ"), + ] + + +def _seg_4() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x194, "M", "ɣ"), + (0x195, "V"), + (0x196, "M", "ɩ"), + (0x197, "M", "ɨ"), + (0x198, "M", "ƙ"), + (0x199, "V"), + (0x19C, "M", "ɯ"), + (0x19D, "M", "ɲ"), + (0x19E, "V"), + (0x19F, "M", "ɵ"), + (0x1A0, "M", "ơ"), + (0x1A1, "V"), + (0x1A2, "M", "ƣ"), + (0x1A3, "V"), + (0x1A4, "M", "ƥ"), + (0x1A5, "V"), + (0x1A6, "M", "ʀ"), + (0x1A7, "M", "ƨ"), + (0x1A8, "V"), + (0x1A9, "M", "ʃ"), + (0x1AA, "V"), + (0x1AC, "M", "ƭ"), + (0x1AD, "V"), + (0x1AE, "M", "ʈ"), + (0x1AF, "M", "ư"), + (0x1B0, "V"), + (0x1B1, "M", "ʊ"), + (0x1B2, "M", "ʋ"), + (0x1B3, "M", "ƴ"), + (0x1B4, "V"), + (0x1B5, "M", "ƶ"), + (0x1B6, "V"), + (0x1B7, "M", "ʒ"), + (0x1B8, "M", "ƹ"), + (0x1B9, "V"), + (0x1BC, "M", "ƽ"), + (0x1BD, "V"), + (0x1C4, "M", "dž"), + (0x1C7, "M", "lj"), + (0x1CA, "M", "nj"), + (0x1CD, "M", "ǎ"), + (0x1CE, "V"), + (0x1CF, "M", "ǐ"), + (0x1D0, "V"), + (0x1D1, "M", "ǒ"), + (0x1D2, "V"), + (0x1D3, "M", "ǔ"), + (0x1D4, "V"), + (0x1D5, "M", "ǖ"), + (0x1D6, "V"), + (0x1D7, "M", "ǘ"), + (0x1D8, "V"), + (0x1D9, "M", "ǚ"), + (0x1DA, "V"), + (0x1DB, "M", "ǜ"), + (0x1DC, "V"), + (0x1DE, "M", "ǟ"), + (0x1DF, "V"), + (0x1E0, "M", "ǡ"), + (0x1E1, "V"), + (0x1E2, "M", "ǣ"), + (0x1E3, "V"), + (0x1E4, "M", "ǥ"), + (0x1E5, "V"), + (0x1E6, "M", "ǧ"), + (0x1E7, "V"), + (0x1E8, "M", "ǩ"), + (0x1E9, "V"), + (0x1EA, "M", "ǫ"), + (0x1EB, "V"), + (0x1EC, "M", "ǭ"), + (0x1ED, "V"), + (0x1EE, "M", "ǯ"), + (0x1EF, "V"), + (0x1F1, "M", "dz"), + (0x1F4, "M", "ǵ"), + (0x1F5, "V"), + (0x1F6, "M", "ƕ"), + (0x1F7, "M", "ƿ"), + (0x1F8, "M", "ǹ"), + (0x1F9, "V"), + (0x1FA, "M", "ǻ"), + (0x1FB, "V"), + (0x1FC, "M", "ǽ"), + (0x1FD, "V"), + (0x1FE, "M", "ǿ"), + (0x1FF, "V"), + (0x200, "M", "ȁ"), + (0x201, "V"), + (0x202, "M", "ȃ"), + (0x203, "V"), + (0x204, "M", "ȅ"), + (0x205, "V"), + (0x206, "M", "ȇ"), + (0x207, "V"), + (0x208, "M", "ȉ"), + (0x209, "V"), + (0x20A, "M", "ȋ"), + (0x20B, "V"), + (0x20C, "M", "ȍ"), + ] + + +def _seg_5() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x20D, "V"), + (0x20E, "M", "ȏ"), + (0x20F, "V"), + (0x210, "M", "ȑ"), + (0x211, "V"), + (0x212, "M", "ȓ"), + (0x213, "V"), + (0x214, "M", "ȕ"), + (0x215, "V"), + (0x216, "M", "ȗ"), + (0x217, "V"), + (0x218, "M", "ș"), + (0x219, "V"), + (0x21A, "M", "ț"), + (0x21B, "V"), + (0x21C, "M", "ȝ"), + (0x21D, "V"), + (0x21E, "M", "ȟ"), + (0x21F, "V"), + (0x220, "M", "ƞ"), + (0x221, "V"), + (0x222, "M", "ȣ"), + (0x223, "V"), + (0x224, "M", "ȥ"), + (0x225, "V"), + (0x226, "M", "ȧ"), + (0x227, "V"), + (0x228, "M", "ȩ"), + (0x229, "V"), + (0x22A, "M", "ȫ"), + (0x22B, "V"), + (0x22C, "M", "ȭ"), + (0x22D, "V"), + (0x22E, "M", "ȯ"), + (0x22F, "V"), + (0x230, "M", "ȱ"), + (0x231, "V"), + (0x232, "M", "ȳ"), + (0x233, "V"), + (0x23A, "M", "ⱥ"), + (0x23B, "M", "ȼ"), + (0x23C, "V"), + (0x23D, "M", "ƚ"), + (0x23E, "M", "ⱦ"), + (0x23F, "V"), + (0x241, "M", "ɂ"), + (0x242, "V"), + (0x243, "M", "ƀ"), + (0x244, "M", "ʉ"), + (0x245, "M", "ʌ"), + (0x246, "M", "ɇ"), + (0x247, "V"), + (0x248, "M", "ɉ"), + (0x249, "V"), + (0x24A, "M", "ɋ"), + (0x24B, "V"), + (0x24C, "M", "ɍ"), + (0x24D, "V"), + (0x24E, "M", "ɏ"), + (0x24F, "V"), + (0x2B0, "M", "h"), + (0x2B1, "M", "ɦ"), + (0x2B2, "M", "j"), + (0x2B3, "M", "r"), + (0x2B4, "M", "ɹ"), + (0x2B5, "M", "ɻ"), + (0x2B6, "M", "ʁ"), + (0x2B7, "M", "w"), + (0x2B8, "M", "y"), + (0x2B9, "V"), + (0x2D8, "M", " ̆"), + (0x2D9, "M", " ̇"), + (0x2DA, "M", " ̊"), + (0x2DB, "M", " ̨"), + (0x2DC, "M", " ̃"), + (0x2DD, "M", " ̋"), + (0x2DE, "V"), + (0x2E0, "M", "ɣ"), + (0x2E1, "M", "l"), + (0x2E2, "M", "s"), + (0x2E3, "M", "x"), + (0x2E4, "M", "ʕ"), + (0x2E5, "V"), + (0x340, "M", "̀"), + (0x341, "M", "́"), + (0x342, "V"), + (0x343, "M", "̓"), + (0x344, "M", "̈́"), + (0x345, "M", "ι"), + (0x346, "V"), + (0x34F, "I"), + (0x350, "V"), + (0x370, "M", "ͱ"), + (0x371, "V"), + (0x372, "M", "ͳ"), + (0x373, "V"), + (0x374, "M", "ʹ"), + (0x375, "V"), + (0x376, "M", "ͷ"), + (0x377, "V"), + ] + + +def _seg_6() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x378, "X"), + (0x37A, "M", " ι"), + (0x37B, "V"), + (0x37E, "M", ";"), + (0x37F, "M", "ϳ"), + (0x380, "X"), + (0x384, "M", " ́"), + (0x385, "M", " ̈́"), + (0x386, "M", "ά"), + (0x387, "M", "·"), + (0x388, "M", "έ"), + (0x389, "M", "ή"), + (0x38A, "M", "ί"), + (0x38B, "X"), + (0x38C, "M", "ό"), + (0x38D, "X"), + (0x38E, "M", "ύ"), + (0x38F, "M", "ώ"), + (0x390, "V"), + (0x391, "M", "α"), + (0x392, "M", "β"), + (0x393, "M", "γ"), + (0x394, "M", "δ"), + (0x395, "M", "ε"), + (0x396, "M", "ζ"), + (0x397, "M", "η"), + (0x398, "M", "θ"), + (0x399, "M", "ι"), + (0x39A, "M", "κ"), + (0x39B, "M", "λ"), + (0x39C, "M", "μ"), + (0x39D, "M", "ν"), + (0x39E, "M", "ξ"), + (0x39F, "M", "ο"), + (0x3A0, "M", "π"), + (0x3A1, "M", "ρ"), + (0x3A2, "X"), + (0x3A3, "M", "σ"), + (0x3A4, "M", "τ"), + (0x3A5, "M", "υ"), + (0x3A6, "M", "φ"), + (0x3A7, "M", "χ"), + (0x3A8, "M", "ψ"), + (0x3A9, "M", "ω"), + (0x3AA, "M", "ϊ"), + (0x3AB, "M", "ϋ"), + (0x3AC, "V"), + (0x3C2, "D", "σ"), + (0x3C3, "V"), + (0x3CF, "M", "ϗ"), + (0x3D0, "M", "β"), + (0x3D1, "M", "θ"), + (0x3D2, "M", "υ"), + (0x3D3, "M", "ύ"), + (0x3D4, "M", "ϋ"), + (0x3D5, "M", "φ"), + (0x3D6, "M", "π"), + (0x3D7, "V"), + (0x3D8, "M", "ϙ"), + (0x3D9, "V"), + (0x3DA, "M", "ϛ"), + (0x3DB, "V"), + (0x3DC, "M", "ϝ"), + (0x3DD, "V"), + (0x3DE, "M", "ϟ"), + (0x3DF, "V"), + (0x3E0, "M", "ϡ"), + (0x3E1, "V"), + (0x3E2, "M", "ϣ"), + (0x3E3, "V"), + (0x3E4, "M", "ϥ"), + (0x3E5, "V"), + (0x3E6, "M", "ϧ"), + (0x3E7, "V"), + (0x3E8, "M", "ϩ"), + (0x3E9, "V"), + (0x3EA, "M", "ϫ"), + (0x3EB, "V"), + (0x3EC, "M", "ϭ"), + (0x3ED, "V"), + (0x3EE, "M", "ϯ"), + (0x3EF, "V"), + (0x3F0, "M", "κ"), + (0x3F1, "M", "ρ"), + (0x3F2, "M", "σ"), + (0x3F3, "V"), + (0x3F4, "M", "θ"), + (0x3F5, "M", "ε"), + (0x3F6, "V"), + (0x3F7, "M", "ϸ"), + (0x3F8, "V"), + (0x3F9, "M", "σ"), + (0x3FA, "M", "ϻ"), + (0x3FB, "V"), + (0x3FD, "M", "ͻ"), + (0x3FE, "M", "ͼ"), + (0x3FF, "M", "ͽ"), + (0x400, "M", "ѐ"), + (0x401, "M", "ё"), + (0x402, "M", "ђ"), + ] + + +def _seg_7() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x403, "M", "ѓ"), + (0x404, "M", "є"), + (0x405, "M", "ѕ"), + (0x406, "M", "і"), + (0x407, "M", "ї"), + (0x408, "M", "ј"), + (0x409, "M", "љ"), + (0x40A, "M", "њ"), + (0x40B, "M", "ћ"), + (0x40C, "M", "ќ"), + (0x40D, "M", "ѝ"), + (0x40E, "M", "ў"), + (0x40F, "M", "џ"), + (0x410, "M", "а"), + (0x411, "M", "б"), + (0x412, "M", "в"), + (0x413, "M", "г"), + (0x414, "M", "д"), + (0x415, "M", "е"), + (0x416, "M", "ж"), + (0x417, "M", "з"), + (0x418, "M", "и"), + (0x419, "M", "й"), + (0x41A, "M", "к"), + (0x41B, "M", "л"), + (0x41C, "M", "м"), + (0x41D, "M", "н"), + (0x41E, "M", "о"), + (0x41F, "M", "п"), + (0x420, "M", "р"), + (0x421, "M", "с"), + (0x422, "M", "т"), + (0x423, "M", "у"), + (0x424, "M", "ф"), + (0x425, "M", "х"), + (0x426, "M", "ц"), + (0x427, "M", "ч"), + (0x428, "M", "ш"), + (0x429, "M", "щ"), + (0x42A, "M", "ъ"), + (0x42B, "M", "ы"), + (0x42C, "M", "ь"), + (0x42D, "M", "э"), + (0x42E, "M", "ю"), + (0x42F, "M", "я"), + (0x430, "V"), + (0x460, "M", "ѡ"), + (0x461, "V"), + (0x462, "M", "ѣ"), + (0x463, "V"), + (0x464, "M", "ѥ"), + (0x465, "V"), + (0x466, "M", "ѧ"), + (0x467, "V"), + (0x468, "M", "ѩ"), + (0x469, "V"), + (0x46A, "M", "ѫ"), + (0x46B, "V"), + (0x46C, "M", "ѭ"), + (0x46D, "V"), + (0x46E, "M", "ѯ"), + (0x46F, "V"), + (0x470, "M", "ѱ"), + (0x471, "V"), + (0x472, "M", "ѳ"), + (0x473, "V"), + (0x474, "M", "ѵ"), + (0x475, "V"), + (0x476, "M", "ѷ"), + (0x477, "V"), + (0x478, "M", "ѹ"), + (0x479, "V"), + (0x47A, "M", "ѻ"), + (0x47B, "V"), + (0x47C, "M", "ѽ"), + (0x47D, "V"), + (0x47E, "M", "ѿ"), + (0x47F, "V"), + (0x480, "M", "ҁ"), + (0x481, "V"), + (0x48A, "M", "ҋ"), + (0x48B, "V"), + (0x48C, "M", "ҍ"), + (0x48D, "V"), + (0x48E, "M", "ҏ"), + (0x48F, "V"), + (0x490, "M", "ґ"), + (0x491, "V"), + (0x492, "M", "ғ"), + (0x493, "V"), + (0x494, "M", "ҕ"), + (0x495, "V"), + (0x496, "M", "җ"), + (0x497, "V"), + (0x498, "M", "ҙ"), + (0x499, "V"), + (0x49A, "M", "қ"), + (0x49B, "V"), + (0x49C, "M", "ҝ"), + (0x49D, "V"), + ] + + +def _seg_8() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x49E, "M", "ҟ"), + (0x49F, "V"), + (0x4A0, "M", "ҡ"), + (0x4A1, "V"), + (0x4A2, "M", "ң"), + (0x4A3, "V"), + (0x4A4, "M", "ҥ"), + (0x4A5, "V"), + (0x4A6, "M", "ҧ"), + (0x4A7, "V"), + (0x4A8, "M", "ҩ"), + (0x4A9, "V"), + (0x4AA, "M", "ҫ"), + (0x4AB, "V"), + (0x4AC, "M", "ҭ"), + (0x4AD, "V"), + (0x4AE, "M", "ү"), + (0x4AF, "V"), + (0x4B0, "M", "ұ"), + (0x4B1, "V"), + (0x4B2, "M", "ҳ"), + (0x4B3, "V"), + (0x4B4, "M", "ҵ"), + (0x4B5, "V"), + (0x4B6, "M", "ҷ"), + (0x4B7, "V"), + (0x4B8, "M", "ҹ"), + (0x4B9, "V"), + (0x4BA, "M", "һ"), + (0x4BB, "V"), + (0x4BC, "M", "ҽ"), + (0x4BD, "V"), + (0x4BE, "M", "ҿ"), + (0x4BF, "V"), + (0x4C0, "M", "ӏ"), + (0x4C1, "M", "ӂ"), + (0x4C2, "V"), + (0x4C3, "M", "ӄ"), + (0x4C4, "V"), + (0x4C5, "M", "ӆ"), + (0x4C6, "V"), + (0x4C7, "M", "ӈ"), + (0x4C8, "V"), + (0x4C9, "M", "ӊ"), + (0x4CA, "V"), + (0x4CB, "M", "ӌ"), + (0x4CC, "V"), + (0x4CD, "M", "ӎ"), + (0x4CE, "V"), + (0x4D0, "M", "ӑ"), + (0x4D1, "V"), + (0x4D2, "M", "ӓ"), + (0x4D3, "V"), + (0x4D4, "M", "ӕ"), + (0x4D5, "V"), + (0x4D6, "M", "ӗ"), + (0x4D7, "V"), + (0x4D8, "M", "ә"), + (0x4D9, "V"), + (0x4DA, "M", "ӛ"), + (0x4DB, "V"), + (0x4DC, "M", "ӝ"), + (0x4DD, "V"), + (0x4DE, "M", "ӟ"), + (0x4DF, "V"), + (0x4E0, "M", "ӡ"), + (0x4E1, "V"), + (0x4E2, "M", "ӣ"), + (0x4E3, "V"), + (0x4E4, "M", "ӥ"), + (0x4E5, "V"), + (0x4E6, "M", "ӧ"), + (0x4E7, "V"), + (0x4E8, "M", "ө"), + (0x4E9, "V"), + (0x4EA, "M", "ӫ"), + (0x4EB, "V"), + (0x4EC, "M", "ӭ"), + (0x4ED, "V"), + (0x4EE, "M", "ӯ"), + (0x4EF, "V"), + (0x4F0, "M", "ӱ"), + (0x4F1, "V"), + (0x4F2, "M", "ӳ"), + (0x4F3, "V"), + (0x4F4, "M", "ӵ"), + (0x4F5, "V"), + (0x4F6, "M", "ӷ"), + (0x4F7, "V"), + (0x4F8, "M", "ӹ"), + (0x4F9, "V"), + (0x4FA, "M", "ӻ"), + (0x4FB, "V"), + (0x4FC, "M", "ӽ"), + (0x4FD, "V"), + (0x4FE, "M", "ӿ"), + (0x4FF, "V"), + (0x500, "M", "ԁ"), + (0x501, "V"), + (0x502, "M", "ԃ"), + ] + + +def _seg_9() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x503, "V"), + (0x504, "M", "ԅ"), + (0x505, "V"), + (0x506, "M", "ԇ"), + (0x507, "V"), + (0x508, "M", "ԉ"), + (0x509, "V"), + (0x50A, "M", "ԋ"), + (0x50B, "V"), + (0x50C, "M", "ԍ"), + (0x50D, "V"), + (0x50E, "M", "ԏ"), + (0x50F, "V"), + (0x510, "M", "ԑ"), + (0x511, "V"), + (0x512, "M", "ԓ"), + (0x513, "V"), + (0x514, "M", "ԕ"), + (0x515, "V"), + (0x516, "M", "ԗ"), + (0x517, "V"), + (0x518, "M", "ԙ"), + (0x519, "V"), + (0x51A, "M", "ԛ"), + (0x51B, "V"), + (0x51C, "M", "ԝ"), + (0x51D, "V"), + (0x51E, "M", "ԟ"), + (0x51F, "V"), + (0x520, "M", "ԡ"), + (0x521, "V"), + (0x522, "M", "ԣ"), + (0x523, "V"), + (0x524, "M", "ԥ"), + (0x525, "V"), + (0x526, "M", "ԧ"), + (0x527, "V"), + (0x528, "M", "ԩ"), + (0x529, "V"), + (0x52A, "M", "ԫ"), + (0x52B, "V"), + (0x52C, "M", "ԭ"), + (0x52D, "V"), + (0x52E, "M", "ԯ"), + (0x52F, "V"), + (0x530, "X"), + (0x531, "M", "ա"), + (0x532, "M", "բ"), + (0x533, "M", "գ"), + (0x534, "M", "դ"), + (0x535, "M", "ե"), + (0x536, "M", "զ"), + (0x537, "M", "է"), + (0x538, "M", "ը"), + (0x539, "M", "թ"), + (0x53A, "M", "ժ"), + (0x53B, "M", "ի"), + (0x53C, "M", "լ"), + (0x53D, "M", "խ"), + (0x53E, "M", "ծ"), + (0x53F, "M", "կ"), + (0x540, "M", "հ"), + (0x541, "M", "ձ"), + (0x542, "M", "ղ"), + (0x543, "M", "ճ"), + (0x544, "M", "մ"), + (0x545, "M", "յ"), + (0x546, "M", "ն"), + (0x547, "M", "շ"), + (0x548, "M", "ո"), + (0x549, "M", "չ"), + (0x54A, "M", "պ"), + (0x54B, "M", "ջ"), + (0x54C, "M", "ռ"), + (0x54D, "M", "ս"), + (0x54E, "M", "վ"), + (0x54F, "M", "տ"), + (0x550, "M", "ր"), + (0x551, "M", "ց"), + (0x552, "M", "ւ"), + (0x553, "M", "փ"), + (0x554, "M", "ք"), + (0x555, "M", "օ"), + (0x556, "M", "ֆ"), + (0x557, "X"), + (0x559, "V"), + (0x587, "M", "եւ"), + (0x588, "V"), + (0x58B, "X"), + (0x58D, "V"), + (0x590, "X"), + (0x591, "V"), + (0x5C8, "X"), + (0x5D0, "V"), + (0x5EB, "X"), + (0x5EF, "V"), + (0x5F5, "X"), + (0x606, "V"), + (0x61C, "X"), + (0x61D, "V"), + ] + + +def _seg_10() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x675, "M", "اٴ"), + (0x676, "M", "وٴ"), + (0x677, "M", "ۇٴ"), + (0x678, "M", "يٴ"), + (0x679, "V"), + (0x6DD, "X"), + (0x6DE, "V"), + (0x70E, "X"), + (0x710, "V"), + (0x74B, "X"), + (0x74D, "V"), + (0x7B2, "X"), + (0x7C0, "V"), + (0x7FB, "X"), + (0x7FD, "V"), + (0x82E, "X"), + (0x830, "V"), + (0x83F, "X"), + (0x840, "V"), + (0x85C, "X"), + (0x85E, "V"), + (0x85F, "X"), + (0x860, "V"), + (0x86B, "X"), + (0x870, "V"), + (0x88F, "X"), + (0x897, "V"), + (0x8E2, "X"), + (0x8E3, "V"), + (0x958, "M", "क़"), + (0x959, "M", "ख़"), + (0x95A, "M", "ग़"), + (0x95B, "M", "ज़"), + (0x95C, "M", "ड़"), + (0x95D, "M", "ढ़"), + (0x95E, "M", "फ़"), + (0x95F, "M", "य़"), + (0x960, "V"), + (0x984, "X"), + (0x985, "V"), + (0x98D, "X"), + (0x98F, "V"), + (0x991, "X"), + (0x993, "V"), + (0x9A9, "X"), + (0x9AA, "V"), + (0x9B1, "X"), + (0x9B2, "V"), + (0x9B3, "X"), + (0x9B6, "V"), + (0x9BA, "X"), + (0x9BC, "V"), + (0x9C5, "X"), + (0x9C7, "V"), + (0x9C9, "X"), + (0x9CB, "V"), + (0x9CF, "X"), + (0x9D7, "V"), + (0x9D8, "X"), + (0x9DC, "M", "ড়"), + (0x9DD, "M", "ঢ়"), + (0x9DE, "X"), + (0x9DF, "M", "য়"), + (0x9E0, "V"), + (0x9E4, "X"), + (0x9E6, "V"), + (0x9FF, "X"), + (0xA01, "V"), + (0xA04, "X"), + (0xA05, "V"), + (0xA0B, "X"), + (0xA0F, "V"), + (0xA11, "X"), + (0xA13, "V"), + (0xA29, "X"), + (0xA2A, "V"), + (0xA31, "X"), + (0xA32, "V"), + (0xA33, "M", "ਲ਼"), + (0xA34, "X"), + (0xA35, "V"), + (0xA36, "M", "ਸ਼"), + (0xA37, "X"), + (0xA38, "V"), + (0xA3A, "X"), + (0xA3C, "V"), + (0xA3D, "X"), + (0xA3E, "V"), + (0xA43, "X"), + (0xA47, "V"), + (0xA49, "X"), + (0xA4B, "V"), + (0xA4E, "X"), + (0xA51, "V"), + (0xA52, "X"), + (0xA59, "M", "ਖ਼"), + (0xA5A, "M", "ਗ਼"), + (0xA5B, "M", "ਜ਼"), + (0xA5C, "V"), + (0xA5D, "X"), + ] + + +def _seg_11() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xA5E, "M", "ਫ਼"), + (0xA5F, "X"), + (0xA66, "V"), + (0xA77, "X"), + (0xA81, "V"), + (0xA84, "X"), + (0xA85, "V"), + (0xA8E, "X"), + (0xA8F, "V"), + (0xA92, "X"), + (0xA93, "V"), + (0xAA9, "X"), + (0xAAA, "V"), + (0xAB1, "X"), + (0xAB2, "V"), + (0xAB4, "X"), + (0xAB5, "V"), + (0xABA, "X"), + (0xABC, "V"), + (0xAC6, "X"), + (0xAC7, "V"), + (0xACA, "X"), + (0xACB, "V"), + (0xACE, "X"), + (0xAD0, "V"), + (0xAD1, "X"), + (0xAE0, "V"), + (0xAE4, "X"), + (0xAE6, "V"), + (0xAF2, "X"), + (0xAF9, "V"), + (0xB00, "X"), + (0xB01, "V"), + (0xB04, "X"), + (0xB05, "V"), + (0xB0D, "X"), + (0xB0F, "V"), + (0xB11, "X"), + (0xB13, "V"), + (0xB29, "X"), + (0xB2A, "V"), + (0xB31, "X"), + (0xB32, "V"), + (0xB34, "X"), + (0xB35, "V"), + (0xB3A, "X"), + (0xB3C, "V"), + (0xB45, "X"), + (0xB47, "V"), + (0xB49, "X"), + (0xB4B, "V"), + (0xB4E, "X"), + (0xB55, "V"), + (0xB58, "X"), + (0xB5C, "M", "ଡ଼"), + (0xB5D, "M", "ଢ଼"), + (0xB5E, "X"), + (0xB5F, "V"), + (0xB64, "X"), + (0xB66, "V"), + (0xB78, "X"), + (0xB82, "V"), + (0xB84, "X"), + (0xB85, "V"), + (0xB8B, "X"), + (0xB8E, "V"), + (0xB91, "X"), + (0xB92, "V"), + (0xB96, "X"), + (0xB99, "V"), + (0xB9B, "X"), + (0xB9C, "V"), + (0xB9D, "X"), + (0xB9E, "V"), + (0xBA0, "X"), + (0xBA3, "V"), + (0xBA5, "X"), + (0xBA8, "V"), + (0xBAB, "X"), + (0xBAE, "V"), + (0xBBA, "X"), + (0xBBE, "V"), + (0xBC3, "X"), + (0xBC6, "V"), + (0xBC9, "X"), + (0xBCA, "V"), + (0xBCE, "X"), + (0xBD0, "V"), + (0xBD1, "X"), + (0xBD7, "V"), + (0xBD8, "X"), + (0xBE6, "V"), + (0xBFB, "X"), + (0xC00, "V"), + (0xC0D, "X"), + (0xC0E, "V"), + (0xC11, "X"), + (0xC12, "V"), + (0xC29, "X"), + (0xC2A, "V"), + ] + + +def _seg_12() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xC3A, "X"), + (0xC3C, "V"), + (0xC45, "X"), + (0xC46, "V"), + (0xC49, "X"), + (0xC4A, "V"), + (0xC4E, "X"), + (0xC55, "V"), + (0xC57, "X"), + (0xC58, "V"), + (0xC5B, "X"), + (0xC5D, "V"), + (0xC5E, "X"), + (0xC60, "V"), + (0xC64, "X"), + (0xC66, "V"), + (0xC70, "X"), + (0xC77, "V"), + (0xC8D, "X"), + (0xC8E, "V"), + (0xC91, "X"), + (0xC92, "V"), + (0xCA9, "X"), + (0xCAA, "V"), + (0xCB4, "X"), + (0xCB5, "V"), + (0xCBA, "X"), + (0xCBC, "V"), + (0xCC5, "X"), + (0xCC6, "V"), + (0xCC9, "X"), + (0xCCA, "V"), + (0xCCE, "X"), + (0xCD5, "V"), + (0xCD7, "X"), + (0xCDD, "V"), + (0xCDF, "X"), + (0xCE0, "V"), + (0xCE4, "X"), + (0xCE6, "V"), + (0xCF0, "X"), + (0xCF1, "V"), + (0xCF4, "X"), + (0xD00, "V"), + (0xD0D, "X"), + (0xD0E, "V"), + (0xD11, "X"), + (0xD12, "V"), + (0xD45, "X"), + (0xD46, "V"), + (0xD49, "X"), + (0xD4A, "V"), + (0xD50, "X"), + (0xD54, "V"), + (0xD64, "X"), + (0xD66, "V"), + (0xD80, "X"), + (0xD81, "V"), + (0xD84, "X"), + (0xD85, "V"), + (0xD97, "X"), + (0xD9A, "V"), + (0xDB2, "X"), + (0xDB3, "V"), + (0xDBC, "X"), + (0xDBD, "V"), + (0xDBE, "X"), + (0xDC0, "V"), + (0xDC7, "X"), + (0xDCA, "V"), + (0xDCB, "X"), + (0xDCF, "V"), + (0xDD5, "X"), + (0xDD6, "V"), + (0xDD7, "X"), + (0xDD8, "V"), + (0xDE0, "X"), + (0xDE6, "V"), + (0xDF0, "X"), + (0xDF2, "V"), + (0xDF5, "X"), + (0xE01, "V"), + (0xE33, "M", "ํา"), + (0xE34, "V"), + (0xE3B, "X"), + (0xE3F, "V"), + (0xE5C, "X"), + (0xE81, "V"), + (0xE83, "X"), + (0xE84, "V"), + (0xE85, "X"), + (0xE86, "V"), + (0xE8B, "X"), + (0xE8C, "V"), + (0xEA4, "X"), + (0xEA5, "V"), + (0xEA6, "X"), + (0xEA7, "V"), + (0xEB3, "M", "ໍາ"), + (0xEB4, "V"), + ] + + +def _seg_13() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xEBE, "X"), + (0xEC0, "V"), + (0xEC5, "X"), + (0xEC6, "V"), + (0xEC7, "X"), + (0xEC8, "V"), + (0xECF, "X"), + (0xED0, "V"), + (0xEDA, "X"), + (0xEDC, "M", "ຫນ"), + (0xEDD, "M", "ຫມ"), + (0xEDE, "V"), + (0xEE0, "X"), + (0xF00, "V"), + (0xF0C, "M", "་"), + (0xF0D, "V"), + (0xF43, "M", "གྷ"), + (0xF44, "V"), + (0xF48, "X"), + (0xF49, "V"), + (0xF4D, "M", "ཌྷ"), + (0xF4E, "V"), + (0xF52, "M", "དྷ"), + (0xF53, "V"), + (0xF57, "M", "བྷ"), + (0xF58, "V"), + (0xF5C, "M", "ཛྷ"), + (0xF5D, "V"), + (0xF69, "M", "ཀྵ"), + (0xF6A, "V"), + (0xF6D, "X"), + (0xF71, "V"), + (0xF73, "M", "ཱི"), + (0xF74, "V"), + (0xF75, "M", "ཱུ"), + (0xF76, "M", "ྲྀ"), + (0xF77, "M", "ྲཱྀ"), + (0xF78, "M", "ླྀ"), + (0xF79, "M", "ླཱྀ"), + (0xF7A, "V"), + (0xF81, "M", "ཱྀ"), + (0xF82, "V"), + (0xF93, "M", "ྒྷ"), + (0xF94, "V"), + (0xF98, "X"), + (0xF99, "V"), + (0xF9D, "M", "ྜྷ"), + (0xF9E, "V"), + (0xFA2, "M", "ྡྷ"), + (0xFA3, "V"), + (0xFA7, "M", "ྦྷ"), + (0xFA8, "V"), + (0xFAC, "M", "ྫྷ"), + (0xFAD, "V"), + (0xFB9, "M", "ྐྵ"), + (0xFBA, "V"), + (0xFBD, "X"), + (0xFBE, "V"), + (0xFCD, "X"), + (0xFCE, "V"), + (0xFDB, "X"), + (0x1000, "V"), + (0x10A0, "M", "ⴀ"), + (0x10A1, "M", "ⴁ"), + (0x10A2, "M", "ⴂ"), + (0x10A3, "M", "ⴃ"), + (0x10A4, "M", "ⴄ"), + (0x10A5, "M", "ⴅ"), + (0x10A6, "M", "ⴆ"), + (0x10A7, "M", "ⴇ"), + (0x10A8, "M", "ⴈ"), + (0x10A9, "M", "ⴉ"), + (0x10AA, "M", "ⴊ"), + (0x10AB, "M", "ⴋ"), + (0x10AC, "M", "ⴌ"), + (0x10AD, "M", "ⴍ"), + (0x10AE, "M", "ⴎ"), + (0x10AF, "M", "ⴏ"), + (0x10B0, "M", "ⴐ"), + (0x10B1, "M", "ⴑ"), + (0x10B2, "M", "ⴒ"), + (0x10B3, "M", "ⴓ"), + (0x10B4, "M", "ⴔ"), + (0x10B5, "M", "ⴕ"), + (0x10B6, "M", "ⴖ"), + (0x10B7, "M", "ⴗ"), + (0x10B8, "M", "ⴘ"), + (0x10B9, "M", "ⴙ"), + (0x10BA, "M", "ⴚ"), + (0x10BB, "M", "ⴛ"), + (0x10BC, "M", "ⴜ"), + (0x10BD, "M", "ⴝ"), + (0x10BE, "M", "ⴞ"), + (0x10BF, "M", "ⴟ"), + (0x10C0, "M", "ⴠ"), + (0x10C1, "M", "ⴡ"), + (0x10C2, "M", "ⴢ"), + (0x10C3, "M", "ⴣ"), + (0x10C4, "M", "ⴤ"), + (0x10C5, "M", "ⴥ"), + ] + + +def _seg_14() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x10C6, "X"), + (0x10C7, "M", "ⴧ"), + (0x10C8, "X"), + (0x10CD, "M", "ⴭ"), + (0x10CE, "X"), + (0x10D0, "V"), + (0x10FC, "M", "ნ"), + (0x10FD, "V"), + (0x115F, "I"), + (0x1161, "V"), + (0x1249, "X"), + (0x124A, "V"), + (0x124E, "X"), + (0x1250, "V"), + (0x1257, "X"), + (0x1258, "V"), + (0x1259, "X"), + (0x125A, "V"), + (0x125E, "X"), + (0x1260, "V"), + (0x1289, "X"), + (0x128A, "V"), + (0x128E, "X"), + (0x1290, "V"), + (0x12B1, "X"), + (0x12B2, "V"), + (0x12B6, "X"), + (0x12B8, "V"), + (0x12BF, "X"), + (0x12C0, "V"), + (0x12C1, "X"), + (0x12C2, "V"), + (0x12C6, "X"), + (0x12C8, "V"), + (0x12D7, "X"), + (0x12D8, "V"), + (0x1311, "X"), + (0x1312, "V"), + (0x1316, "X"), + (0x1318, "V"), + (0x135B, "X"), + (0x135D, "V"), + (0x137D, "X"), + (0x1380, "V"), + (0x139A, "X"), + (0x13A0, "V"), + (0x13F6, "X"), + (0x13F8, "M", "Ᏸ"), + (0x13F9, "M", "Ᏹ"), + (0x13FA, "M", "Ᏺ"), + (0x13FB, "M", "Ᏻ"), + (0x13FC, "M", "Ᏼ"), + (0x13FD, "M", "Ᏽ"), + (0x13FE, "X"), + (0x1400, "V"), + (0x1680, "X"), + (0x1681, "V"), + (0x169D, "X"), + (0x16A0, "V"), + (0x16F9, "X"), + (0x1700, "V"), + (0x1716, "X"), + (0x171F, "V"), + (0x1737, "X"), + (0x1740, "V"), + (0x1754, "X"), + (0x1760, "V"), + (0x176D, "X"), + (0x176E, "V"), + (0x1771, "X"), + (0x1772, "V"), + (0x1774, "X"), + (0x1780, "V"), + (0x17B4, "I"), + (0x17B6, "V"), + (0x17DE, "X"), + (0x17E0, "V"), + (0x17EA, "X"), + (0x17F0, "V"), + (0x17FA, "X"), + (0x1800, "V"), + (0x180B, "I"), + (0x1810, "V"), + (0x181A, "X"), + (0x1820, "V"), + (0x1879, "X"), + (0x1880, "V"), + (0x18AB, "X"), + (0x18B0, "V"), + (0x18F6, "X"), + (0x1900, "V"), + (0x191F, "X"), + (0x1920, "V"), + (0x192C, "X"), + (0x1930, "V"), + (0x193C, "X"), + (0x1940, "V"), + (0x1941, "X"), + (0x1944, "V"), + (0x196E, "X"), + ] + + +def _seg_15() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1970, "V"), + (0x1975, "X"), + (0x1980, "V"), + (0x19AC, "X"), + (0x19B0, "V"), + (0x19CA, "X"), + (0x19D0, "V"), + (0x19DB, "X"), + (0x19DE, "V"), + (0x1A1C, "X"), + (0x1A1E, "V"), + (0x1A5F, "X"), + (0x1A60, "V"), + (0x1A7D, "X"), + (0x1A7F, "V"), + (0x1A8A, "X"), + (0x1A90, "V"), + (0x1A9A, "X"), + (0x1AA0, "V"), + (0x1AAE, "X"), + (0x1AB0, "V"), + (0x1ACF, "X"), + (0x1B00, "V"), + (0x1B4D, "X"), + (0x1B4E, "V"), + (0x1BF4, "X"), + (0x1BFC, "V"), + (0x1C38, "X"), + (0x1C3B, "V"), + (0x1C4A, "X"), + (0x1C4D, "V"), + (0x1C80, "M", "в"), + (0x1C81, "M", "д"), + (0x1C82, "M", "о"), + (0x1C83, "M", "с"), + (0x1C84, "M", "т"), + (0x1C86, "M", "ъ"), + (0x1C87, "M", "ѣ"), + (0x1C88, "M", "ꙋ"), + (0x1C89, "M", "ᲊ"), + (0x1C8A, "V"), + (0x1C8B, "X"), + (0x1C90, "M", "ა"), + (0x1C91, "M", "ბ"), + (0x1C92, "M", "გ"), + (0x1C93, "M", "დ"), + (0x1C94, "M", "ე"), + (0x1C95, "M", "ვ"), + (0x1C96, "M", "ზ"), + (0x1C97, "M", "თ"), + (0x1C98, "M", "ი"), + (0x1C99, "M", "კ"), + (0x1C9A, "M", "ლ"), + (0x1C9B, "M", "მ"), + (0x1C9C, "M", "ნ"), + (0x1C9D, "M", "ო"), + (0x1C9E, "M", "პ"), + (0x1C9F, "M", "ჟ"), + (0x1CA0, "M", "რ"), + (0x1CA1, "M", "ს"), + (0x1CA2, "M", "ტ"), + (0x1CA3, "M", "უ"), + (0x1CA4, "M", "ფ"), + (0x1CA5, "M", "ქ"), + (0x1CA6, "M", "ღ"), + (0x1CA7, "M", "ყ"), + (0x1CA8, "M", "შ"), + (0x1CA9, "M", "ჩ"), + (0x1CAA, "M", "ც"), + (0x1CAB, "M", "ძ"), + (0x1CAC, "M", "წ"), + (0x1CAD, "M", "ჭ"), + (0x1CAE, "M", "ხ"), + (0x1CAF, "M", "ჯ"), + (0x1CB0, "M", "ჰ"), + (0x1CB1, "M", "ჱ"), + (0x1CB2, "M", "ჲ"), + (0x1CB3, "M", "ჳ"), + (0x1CB4, "M", "ჴ"), + (0x1CB5, "M", "ჵ"), + (0x1CB6, "M", "ჶ"), + (0x1CB7, "M", "ჷ"), + (0x1CB8, "M", "ჸ"), + (0x1CB9, "M", "ჹ"), + (0x1CBA, "M", "ჺ"), + (0x1CBB, "X"), + (0x1CBD, "M", "ჽ"), + (0x1CBE, "M", "ჾ"), + (0x1CBF, "M", "ჿ"), + (0x1CC0, "V"), + (0x1CC8, "X"), + (0x1CD0, "V"), + (0x1CFB, "X"), + (0x1D00, "V"), + (0x1D2C, "M", "a"), + (0x1D2D, "M", "æ"), + (0x1D2E, "M", "b"), + (0x1D2F, "V"), + (0x1D30, "M", "d"), + (0x1D31, "M", "e"), + ] + + +def _seg_16() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D32, "M", "ǝ"), + (0x1D33, "M", "g"), + (0x1D34, "M", "h"), + (0x1D35, "M", "i"), + (0x1D36, "M", "j"), + (0x1D37, "M", "k"), + (0x1D38, "M", "l"), + (0x1D39, "M", "m"), + (0x1D3A, "M", "n"), + (0x1D3B, "V"), + (0x1D3C, "M", "o"), + (0x1D3D, "M", "ȣ"), + (0x1D3E, "M", "p"), + (0x1D3F, "M", "r"), + (0x1D40, "M", "t"), + (0x1D41, "M", "u"), + (0x1D42, "M", "w"), + (0x1D43, "M", "a"), + (0x1D44, "M", "ɐ"), + (0x1D45, "M", "ɑ"), + (0x1D46, "M", "ᴂ"), + (0x1D47, "M", "b"), + (0x1D48, "M", "d"), + (0x1D49, "M", "e"), + (0x1D4A, "M", "ə"), + (0x1D4B, "M", "ɛ"), + (0x1D4C, "M", "ɜ"), + (0x1D4D, "M", "g"), + (0x1D4E, "V"), + (0x1D4F, "M", "k"), + (0x1D50, "M", "m"), + (0x1D51, "M", "ŋ"), + (0x1D52, "M", "o"), + (0x1D53, "M", "ɔ"), + (0x1D54, "M", "ᴖ"), + (0x1D55, "M", "ᴗ"), + (0x1D56, "M", "p"), + (0x1D57, "M", "t"), + (0x1D58, "M", "u"), + (0x1D59, "M", "ᴝ"), + (0x1D5A, "M", "ɯ"), + (0x1D5B, "M", "v"), + (0x1D5C, "M", "ᴥ"), + (0x1D5D, "M", "β"), + (0x1D5E, "M", "γ"), + (0x1D5F, "M", "δ"), + (0x1D60, "M", "φ"), + (0x1D61, "M", "χ"), + (0x1D62, "M", "i"), + (0x1D63, "M", "r"), + (0x1D64, "M", "u"), + (0x1D65, "M", "v"), + (0x1D66, "M", "β"), + (0x1D67, "M", "γ"), + (0x1D68, "M", "ρ"), + (0x1D69, "M", "φ"), + (0x1D6A, "M", "χ"), + (0x1D6B, "V"), + (0x1D78, "M", "н"), + (0x1D79, "V"), + (0x1D9B, "M", "ɒ"), + (0x1D9C, "M", "c"), + (0x1D9D, "M", "ɕ"), + (0x1D9E, "M", "ð"), + (0x1D9F, "M", "ɜ"), + (0x1DA0, "M", "f"), + (0x1DA1, "M", "ɟ"), + (0x1DA2, "M", "ɡ"), + (0x1DA3, "M", "ɥ"), + (0x1DA4, "M", "ɨ"), + (0x1DA5, "M", "ɩ"), + (0x1DA6, "M", "ɪ"), + (0x1DA7, "M", "ᵻ"), + (0x1DA8, "M", "ʝ"), + (0x1DA9, "M", "ɭ"), + (0x1DAA, "M", "ᶅ"), + (0x1DAB, "M", "ʟ"), + (0x1DAC, "M", "ɱ"), + (0x1DAD, "M", "ɰ"), + (0x1DAE, "M", "ɲ"), + (0x1DAF, "M", "ɳ"), + (0x1DB0, "M", "ɴ"), + (0x1DB1, "M", "ɵ"), + (0x1DB2, "M", "ɸ"), + (0x1DB3, "M", "ʂ"), + (0x1DB4, "M", "ʃ"), + (0x1DB5, "M", "ƫ"), + (0x1DB6, "M", "ʉ"), + (0x1DB7, "M", "ʊ"), + (0x1DB8, "M", "ᴜ"), + (0x1DB9, "M", "ʋ"), + (0x1DBA, "M", "ʌ"), + (0x1DBB, "M", "z"), + (0x1DBC, "M", "ʐ"), + (0x1DBD, "M", "ʑ"), + (0x1DBE, "M", "ʒ"), + (0x1DBF, "M", "θ"), + (0x1DC0, "V"), + (0x1E00, "M", "ḁ"), + (0x1E01, "V"), + ] + + +def _seg_17() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1E02, "M", "ḃ"), + (0x1E03, "V"), + (0x1E04, "M", "ḅ"), + (0x1E05, "V"), + (0x1E06, "M", "ḇ"), + (0x1E07, "V"), + (0x1E08, "M", "ḉ"), + (0x1E09, "V"), + (0x1E0A, "M", "ḋ"), + (0x1E0B, "V"), + (0x1E0C, "M", "ḍ"), + (0x1E0D, "V"), + (0x1E0E, "M", "ḏ"), + (0x1E0F, "V"), + (0x1E10, "M", "ḑ"), + (0x1E11, "V"), + (0x1E12, "M", "ḓ"), + (0x1E13, "V"), + (0x1E14, "M", "ḕ"), + (0x1E15, "V"), + (0x1E16, "M", "ḗ"), + (0x1E17, "V"), + (0x1E18, "M", "ḙ"), + (0x1E19, "V"), + (0x1E1A, "M", "ḛ"), + (0x1E1B, "V"), + (0x1E1C, "M", "ḝ"), + (0x1E1D, "V"), + (0x1E1E, "M", "ḟ"), + (0x1E1F, "V"), + (0x1E20, "M", "ḡ"), + (0x1E21, "V"), + (0x1E22, "M", "ḣ"), + (0x1E23, "V"), + (0x1E24, "M", "ḥ"), + (0x1E25, "V"), + (0x1E26, "M", "ḧ"), + (0x1E27, "V"), + (0x1E28, "M", "ḩ"), + (0x1E29, "V"), + (0x1E2A, "M", "ḫ"), + (0x1E2B, "V"), + (0x1E2C, "M", "ḭ"), + (0x1E2D, "V"), + (0x1E2E, "M", "ḯ"), + (0x1E2F, "V"), + (0x1E30, "M", "ḱ"), + (0x1E31, "V"), + (0x1E32, "M", "ḳ"), + (0x1E33, "V"), + (0x1E34, "M", "ḵ"), + (0x1E35, "V"), + (0x1E36, "M", "ḷ"), + (0x1E37, "V"), + (0x1E38, "M", "ḹ"), + (0x1E39, "V"), + (0x1E3A, "M", "ḻ"), + (0x1E3B, "V"), + (0x1E3C, "M", "ḽ"), + (0x1E3D, "V"), + (0x1E3E, "M", "ḿ"), + (0x1E3F, "V"), + (0x1E40, "M", "ṁ"), + (0x1E41, "V"), + (0x1E42, "M", "ṃ"), + (0x1E43, "V"), + (0x1E44, "M", "ṅ"), + (0x1E45, "V"), + (0x1E46, "M", "ṇ"), + (0x1E47, "V"), + (0x1E48, "M", "ṉ"), + (0x1E49, "V"), + (0x1E4A, "M", "ṋ"), + (0x1E4B, "V"), + (0x1E4C, "M", "ṍ"), + (0x1E4D, "V"), + (0x1E4E, "M", "ṏ"), + (0x1E4F, "V"), + (0x1E50, "M", "ṑ"), + (0x1E51, "V"), + (0x1E52, "M", "ṓ"), + (0x1E53, "V"), + (0x1E54, "M", "ṕ"), + (0x1E55, "V"), + (0x1E56, "M", "ṗ"), + (0x1E57, "V"), + (0x1E58, "M", "ṙ"), + (0x1E59, "V"), + (0x1E5A, "M", "ṛ"), + (0x1E5B, "V"), + (0x1E5C, "M", "ṝ"), + (0x1E5D, "V"), + (0x1E5E, "M", "ṟ"), + (0x1E5F, "V"), + (0x1E60, "M", "ṡ"), + (0x1E61, "V"), + (0x1E62, "M", "ṣ"), + (0x1E63, "V"), + (0x1E64, "M", "ṥ"), + (0x1E65, "V"), + ] + + +def _seg_18() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1E66, "M", "ṧ"), + (0x1E67, "V"), + (0x1E68, "M", "ṩ"), + (0x1E69, "V"), + (0x1E6A, "M", "ṫ"), + (0x1E6B, "V"), + (0x1E6C, "M", "ṭ"), + (0x1E6D, "V"), + (0x1E6E, "M", "ṯ"), + (0x1E6F, "V"), + (0x1E70, "M", "ṱ"), + (0x1E71, "V"), + (0x1E72, "M", "ṳ"), + (0x1E73, "V"), + (0x1E74, "M", "ṵ"), + (0x1E75, "V"), + (0x1E76, "M", "ṷ"), + (0x1E77, "V"), + (0x1E78, "M", "ṹ"), + (0x1E79, "V"), + (0x1E7A, "M", "ṻ"), + (0x1E7B, "V"), + (0x1E7C, "M", "ṽ"), + (0x1E7D, "V"), + (0x1E7E, "M", "ṿ"), + (0x1E7F, "V"), + (0x1E80, "M", "ẁ"), + (0x1E81, "V"), + (0x1E82, "M", "ẃ"), + (0x1E83, "V"), + (0x1E84, "M", "ẅ"), + (0x1E85, "V"), + (0x1E86, "M", "ẇ"), + (0x1E87, "V"), + (0x1E88, "M", "ẉ"), + (0x1E89, "V"), + (0x1E8A, "M", "ẋ"), + (0x1E8B, "V"), + (0x1E8C, "M", "ẍ"), + (0x1E8D, "V"), + (0x1E8E, "M", "ẏ"), + (0x1E8F, "V"), + (0x1E90, "M", "ẑ"), + (0x1E91, "V"), + (0x1E92, "M", "ẓ"), + (0x1E93, "V"), + (0x1E94, "M", "ẕ"), + (0x1E95, "V"), + (0x1E9A, "M", "aʾ"), + (0x1E9B, "M", "ṡ"), + (0x1E9C, "V"), + (0x1E9E, "M", "ß"), + (0x1E9F, "V"), + (0x1EA0, "M", "ạ"), + (0x1EA1, "V"), + (0x1EA2, "M", "ả"), + (0x1EA3, "V"), + (0x1EA4, "M", "ấ"), + (0x1EA5, "V"), + (0x1EA6, "M", "ầ"), + (0x1EA7, "V"), + (0x1EA8, "M", "ẩ"), + (0x1EA9, "V"), + (0x1EAA, "M", "ẫ"), + (0x1EAB, "V"), + (0x1EAC, "M", "ậ"), + (0x1EAD, "V"), + (0x1EAE, "M", "ắ"), + (0x1EAF, "V"), + (0x1EB0, "M", "ằ"), + (0x1EB1, "V"), + (0x1EB2, "M", "ẳ"), + (0x1EB3, "V"), + (0x1EB4, "M", "ẵ"), + (0x1EB5, "V"), + (0x1EB6, "M", "ặ"), + (0x1EB7, "V"), + (0x1EB8, "M", "ẹ"), + (0x1EB9, "V"), + (0x1EBA, "M", "ẻ"), + (0x1EBB, "V"), + (0x1EBC, "M", "ẽ"), + (0x1EBD, "V"), + (0x1EBE, "M", "ế"), + (0x1EBF, "V"), + (0x1EC0, "M", "ề"), + (0x1EC1, "V"), + (0x1EC2, "M", "ể"), + (0x1EC3, "V"), + (0x1EC4, "M", "ễ"), + (0x1EC5, "V"), + (0x1EC6, "M", "ệ"), + (0x1EC7, "V"), + (0x1EC8, "M", "ỉ"), + (0x1EC9, "V"), + (0x1ECA, "M", "ị"), + (0x1ECB, "V"), + (0x1ECC, "M", "ọ"), + (0x1ECD, "V"), + (0x1ECE, "M", "ỏ"), + ] + + +def _seg_19() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1ECF, "V"), + (0x1ED0, "M", "ố"), + (0x1ED1, "V"), + (0x1ED2, "M", "ồ"), + (0x1ED3, "V"), + (0x1ED4, "M", "ổ"), + (0x1ED5, "V"), + (0x1ED6, "M", "ỗ"), + (0x1ED7, "V"), + (0x1ED8, "M", "ộ"), + (0x1ED9, "V"), + (0x1EDA, "M", "ớ"), + (0x1EDB, "V"), + (0x1EDC, "M", "ờ"), + (0x1EDD, "V"), + (0x1EDE, "M", "ở"), + (0x1EDF, "V"), + (0x1EE0, "M", "ỡ"), + (0x1EE1, "V"), + (0x1EE2, "M", "ợ"), + (0x1EE3, "V"), + (0x1EE4, "M", "ụ"), + (0x1EE5, "V"), + (0x1EE6, "M", "ủ"), + (0x1EE7, "V"), + (0x1EE8, "M", "ứ"), + (0x1EE9, "V"), + (0x1EEA, "M", "ừ"), + (0x1EEB, "V"), + (0x1EEC, "M", "ử"), + (0x1EED, "V"), + (0x1EEE, "M", "ữ"), + (0x1EEF, "V"), + (0x1EF0, "M", "ự"), + (0x1EF1, "V"), + (0x1EF2, "M", "ỳ"), + (0x1EF3, "V"), + (0x1EF4, "M", "ỵ"), + (0x1EF5, "V"), + (0x1EF6, "M", "ỷ"), + (0x1EF7, "V"), + (0x1EF8, "M", "ỹ"), + (0x1EF9, "V"), + (0x1EFA, "M", "ỻ"), + (0x1EFB, "V"), + (0x1EFC, "M", "ỽ"), + (0x1EFD, "V"), + (0x1EFE, "M", "ỿ"), + (0x1EFF, "V"), + (0x1F08, "M", "ἀ"), + (0x1F09, "M", "ἁ"), + (0x1F0A, "M", "ἂ"), + (0x1F0B, "M", "ἃ"), + (0x1F0C, "M", "ἄ"), + (0x1F0D, "M", "ἅ"), + (0x1F0E, "M", "ἆ"), + (0x1F0F, "M", "ἇ"), + (0x1F10, "V"), + (0x1F16, "X"), + (0x1F18, "M", "ἐ"), + (0x1F19, "M", "ἑ"), + (0x1F1A, "M", "ἒ"), + (0x1F1B, "M", "ἓ"), + (0x1F1C, "M", "ἔ"), + (0x1F1D, "M", "ἕ"), + (0x1F1E, "X"), + (0x1F20, "V"), + (0x1F28, "M", "ἠ"), + (0x1F29, "M", "ἡ"), + (0x1F2A, "M", "ἢ"), + (0x1F2B, "M", "ἣ"), + (0x1F2C, "M", "ἤ"), + (0x1F2D, "M", "ἥ"), + (0x1F2E, "M", "ἦ"), + (0x1F2F, "M", "ἧ"), + (0x1F30, "V"), + (0x1F38, "M", "ἰ"), + (0x1F39, "M", "ἱ"), + (0x1F3A, "M", "ἲ"), + (0x1F3B, "M", "ἳ"), + (0x1F3C, "M", "ἴ"), + (0x1F3D, "M", "ἵ"), + (0x1F3E, "M", "ἶ"), + (0x1F3F, "M", "ἷ"), + (0x1F40, "V"), + (0x1F46, "X"), + (0x1F48, "M", "ὀ"), + (0x1F49, "M", "ὁ"), + (0x1F4A, "M", "ὂ"), + (0x1F4B, "M", "ὃ"), + (0x1F4C, "M", "ὄ"), + (0x1F4D, "M", "ὅ"), + (0x1F4E, "X"), + (0x1F50, "V"), + (0x1F58, "X"), + (0x1F59, "M", "ὑ"), + (0x1F5A, "X"), + (0x1F5B, "M", "ὓ"), + (0x1F5C, "X"), + (0x1F5D, "M", "ὕ"), + ] + + +def _seg_20() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1F5E, "X"), + (0x1F5F, "M", "ὗ"), + (0x1F60, "V"), + (0x1F68, "M", "ὠ"), + (0x1F69, "M", "ὡ"), + (0x1F6A, "M", "ὢ"), + (0x1F6B, "M", "ὣ"), + (0x1F6C, "M", "ὤ"), + (0x1F6D, "M", "ὥ"), + (0x1F6E, "M", "ὦ"), + (0x1F6F, "M", "ὧ"), + (0x1F70, "V"), + (0x1F71, "M", "ά"), + (0x1F72, "V"), + (0x1F73, "M", "έ"), + (0x1F74, "V"), + (0x1F75, "M", "ή"), + (0x1F76, "V"), + (0x1F77, "M", "ί"), + (0x1F78, "V"), + (0x1F79, "M", "ό"), + (0x1F7A, "V"), + (0x1F7B, "M", "ύ"), + (0x1F7C, "V"), + (0x1F7D, "M", "ώ"), + (0x1F7E, "X"), + (0x1F80, "M", "ἀι"), + (0x1F81, "M", "ἁι"), + (0x1F82, "M", "ἂι"), + (0x1F83, "M", "ἃι"), + (0x1F84, "M", "ἄι"), + (0x1F85, "M", "ἅι"), + (0x1F86, "M", "ἆι"), + (0x1F87, "M", "ἇι"), + (0x1F88, "M", "ἀι"), + (0x1F89, "M", "ἁι"), + (0x1F8A, "M", "ἂι"), + (0x1F8B, "M", "ἃι"), + (0x1F8C, "M", "ἄι"), + (0x1F8D, "M", "ἅι"), + (0x1F8E, "M", "ἆι"), + (0x1F8F, "M", "ἇι"), + (0x1F90, "M", "ἠι"), + (0x1F91, "M", "ἡι"), + (0x1F92, "M", "ἢι"), + (0x1F93, "M", "ἣι"), + (0x1F94, "M", "ἤι"), + (0x1F95, "M", "ἥι"), + (0x1F96, "M", "ἦι"), + (0x1F97, "M", "ἧι"), + (0x1F98, "M", "ἠι"), + (0x1F99, "M", "ἡι"), + (0x1F9A, "M", "ἢι"), + (0x1F9B, "M", "ἣι"), + (0x1F9C, "M", "ἤι"), + (0x1F9D, "M", "ἥι"), + (0x1F9E, "M", "ἦι"), + (0x1F9F, "M", "ἧι"), + (0x1FA0, "M", "ὠι"), + (0x1FA1, "M", "ὡι"), + (0x1FA2, "M", "ὢι"), + (0x1FA3, "M", "ὣι"), + (0x1FA4, "M", "ὤι"), + (0x1FA5, "M", "ὥι"), + (0x1FA6, "M", "ὦι"), + (0x1FA7, "M", "ὧι"), + (0x1FA8, "M", "ὠι"), + (0x1FA9, "M", "ὡι"), + (0x1FAA, "M", "ὢι"), + (0x1FAB, "M", "ὣι"), + (0x1FAC, "M", "ὤι"), + (0x1FAD, "M", "ὥι"), + (0x1FAE, "M", "ὦι"), + (0x1FAF, "M", "ὧι"), + (0x1FB0, "V"), + (0x1FB2, "M", "ὰι"), + (0x1FB3, "M", "αι"), + (0x1FB4, "M", "άι"), + (0x1FB5, "X"), + (0x1FB6, "V"), + (0x1FB7, "M", "ᾶι"), + (0x1FB8, "M", "ᾰ"), + (0x1FB9, "M", "ᾱ"), + (0x1FBA, "M", "ὰ"), + (0x1FBB, "M", "ά"), + (0x1FBC, "M", "αι"), + (0x1FBD, "M", " ̓"), + (0x1FBE, "M", "ι"), + (0x1FBF, "M", " ̓"), + (0x1FC0, "M", " ͂"), + (0x1FC1, "M", " ̈͂"), + (0x1FC2, "M", "ὴι"), + (0x1FC3, "M", "ηι"), + (0x1FC4, "M", "ήι"), + (0x1FC5, "X"), + (0x1FC6, "V"), + (0x1FC7, "M", "ῆι"), + (0x1FC8, "M", "ὲ"), + (0x1FC9, "M", "έ"), + (0x1FCA, "M", "ὴ"), + ] + + +def _seg_21() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1FCB, "M", "ή"), + (0x1FCC, "M", "ηι"), + (0x1FCD, "M", " ̓̀"), + (0x1FCE, "M", " ̓́"), + (0x1FCF, "M", " ̓͂"), + (0x1FD0, "V"), + (0x1FD3, "M", "ΐ"), + (0x1FD4, "X"), + (0x1FD6, "V"), + (0x1FD8, "M", "ῐ"), + (0x1FD9, "M", "ῑ"), + (0x1FDA, "M", "ὶ"), + (0x1FDB, "M", "ί"), + (0x1FDC, "X"), + (0x1FDD, "M", " ̔̀"), + (0x1FDE, "M", " ̔́"), + (0x1FDF, "M", " ̔͂"), + (0x1FE0, "V"), + (0x1FE3, "M", "ΰ"), + (0x1FE4, "V"), + (0x1FE8, "M", "ῠ"), + (0x1FE9, "M", "ῡ"), + (0x1FEA, "M", "ὺ"), + (0x1FEB, "M", "ύ"), + (0x1FEC, "M", "ῥ"), + (0x1FED, "M", " ̈̀"), + (0x1FEE, "M", " ̈́"), + (0x1FEF, "M", "`"), + (0x1FF0, "X"), + (0x1FF2, "M", "ὼι"), + (0x1FF3, "M", "ωι"), + (0x1FF4, "M", "ώι"), + (0x1FF5, "X"), + (0x1FF6, "V"), + (0x1FF7, "M", "ῶι"), + (0x1FF8, "M", "ὸ"), + (0x1FF9, "M", "ό"), + (0x1FFA, "M", "ὼ"), + (0x1FFB, "M", "ώ"), + (0x1FFC, "M", "ωι"), + (0x1FFD, "M", " ́"), + (0x1FFE, "M", " ̔"), + (0x1FFF, "X"), + (0x2000, "M", " "), + (0x200B, "I"), + (0x200C, "D", ""), + (0x200E, "X"), + (0x2010, "V"), + (0x2011, "M", "‐"), + (0x2012, "V"), + (0x2017, "M", " ̳"), + (0x2018, "V"), + (0x2024, "X"), + (0x2027, "V"), + (0x2028, "X"), + (0x202F, "M", " "), + (0x2030, "V"), + (0x2033, "M", "′′"), + (0x2034, "M", "′′′"), + (0x2035, "V"), + (0x2036, "M", "‵‵"), + (0x2037, "M", "‵‵‵"), + (0x2038, "V"), + (0x203C, "M", "!!"), + (0x203D, "V"), + (0x203E, "M", " ̅"), + (0x203F, "V"), + (0x2047, "M", "??"), + (0x2048, "M", "?!"), + (0x2049, "M", "!?"), + (0x204A, "V"), + (0x2057, "M", "′′′′"), + (0x2058, "V"), + (0x205F, "M", " "), + (0x2060, "I"), + (0x2065, "X"), + (0x206A, "I"), + (0x2070, "M", "0"), + (0x2071, "M", "i"), + (0x2072, "X"), + (0x2074, "M", "4"), + (0x2075, "M", "5"), + (0x2076, "M", "6"), + (0x2077, "M", "7"), + (0x2078, "M", "8"), + (0x2079, "M", "9"), + (0x207A, "M", "+"), + (0x207B, "M", "−"), + (0x207C, "M", "="), + (0x207D, "M", "("), + (0x207E, "M", ")"), + (0x207F, "M", "n"), + (0x2080, "M", "0"), + (0x2081, "M", "1"), + (0x2082, "M", "2"), + (0x2083, "M", "3"), + (0x2084, "M", "4"), + (0x2085, "M", "5"), + (0x2086, "M", "6"), + (0x2087, "M", "7"), + ] + + +def _seg_22() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2088, "M", "8"), + (0x2089, "M", "9"), + (0x208A, "M", "+"), + (0x208B, "M", "−"), + (0x208C, "M", "="), + (0x208D, "M", "("), + (0x208E, "M", ")"), + (0x208F, "X"), + (0x2090, "M", "a"), + (0x2091, "M", "e"), + (0x2092, "M", "o"), + (0x2093, "M", "x"), + (0x2094, "M", "ə"), + (0x2095, "M", "h"), + (0x2096, "M", "k"), + (0x2097, "M", "l"), + (0x2098, "M", "m"), + (0x2099, "M", "n"), + (0x209A, "M", "p"), + (0x209B, "M", "s"), + (0x209C, "M", "t"), + (0x209D, "X"), + (0x20A0, "V"), + (0x20A8, "M", "rs"), + (0x20A9, "V"), + (0x20C1, "X"), + (0x20D0, "V"), + (0x20F1, "X"), + (0x2100, "M", "a/c"), + (0x2101, "M", "a/s"), + (0x2102, "M", "c"), + (0x2103, "M", "°c"), + (0x2104, "V"), + (0x2105, "M", "c/o"), + (0x2106, "M", "c/u"), + (0x2107, "M", "ɛ"), + (0x2108, "V"), + (0x2109, "M", "°f"), + (0x210A, "M", "g"), + (0x210B, "M", "h"), + (0x210F, "M", "ħ"), + (0x2110, "M", "i"), + (0x2112, "M", "l"), + (0x2114, "V"), + (0x2115, "M", "n"), + (0x2116, "M", "no"), + (0x2117, "V"), + (0x2119, "M", "p"), + (0x211A, "M", "q"), + (0x211B, "M", "r"), + (0x211E, "V"), + (0x2120, "M", "sm"), + (0x2121, "M", "tel"), + (0x2122, "M", "tm"), + (0x2123, "V"), + (0x2124, "M", "z"), + (0x2125, "V"), + (0x2126, "M", "ω"), + (0x2127, "V"), + (0x2128, "M", "z"), + (0x2129, "V"), + (0x212A, "M", "k"), + (0x212B, "M", "å"), + (0x212C, "M", "b"), + (0x212D, "M", "c"), + (0x212E, "V"), + (0x212F, "M", "e"), + (0x2131, "M", "f"), + (0x2132, "M", "ⅎ"), + (0x2133, "M", "m"), + (0x2134, "M", "o"), + (0x2135, "M", "א"), + (0x2136, "M", "ב"), + (0x2137, "M", "ג"), + (0x2138, "M", "ד"), + (0x2139, "M", "i"), + (0x213A, "V"), + (0x213B, "M", "fax"), + (0x213C, "M", "π"), + (0x213D, "M", "γ"), + (0x213F, "M", "π"), + (0x2140, "M", "∑"), + (0x2141, "V"), + (0x2145, "M", "d"), + (0x2147, "M", "e"), + (0x2148, "M", "i"), + (0x2149, "M", "j"), + (0x214A, "V"), + (0x2150, "M", "1⁄7"), + (0x2151, "M", "1⁄9"), + (0x2152, "M", "1⁄10"), + (0x2153, "M", "1⁄3"), + (0x2154, "M", "2⁄3"), + (0x2155, "M", "1⁄5"), + (0x2156, "M", "2⁄5"), + (0x2157, "M", "3⁄5"), + (0x2158, "M", "4⁄5"), + (0x2159, "M", "1⁄6"), + (0x215A, "M", "5⁄6"), + (0x215B, "M", "1⁄8"), + ] + + +def _seg_23() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x215C, "M", "3⁄8"), + (0x215D, "M", "5⁄8"), + (0x215E, "M", "7⁄8"), + (0x215F, "M", "1⁄"), + (0x2160, "M", "i"), + (0x2161, "M", "ii"), + (0x2162, "M", "iii"), + (0x2163, "M", "iv"), + (0x2164, "M", "v"), + (0x2165, "M", "vi"), + (0x2166, "M", "vii"), + (0x2167, "M", "viii"), + (0x2168, "M", "ix"), + (0x2169, "M", "x"), + (0x216A, "M", "xi"), + (0x216B, "M", "xii"), + (0x216C, "M", "l"), + (0x216D, "M", "c"), + (0x216E, "M", "d"), + (0x216F, "M", "m"), + (0x2170, "M", "i"), + (0x2171, "M", "ii"), + (0x2172, "M", "iii"), + (0x2173, "M", "iv"), + (0x2174, "M", "v"), + (0x2175, "M", "vi"), + (0x2176, "M", "vii"), + (0x2177, "M", "viii"), + (0x2178, "M", "ix"), + (0x2179, "M", "x"), + (0x217A, "M", "xi"), + (0x217B, "M", "xii"), + (0x217C, "M", "l"), + (0x217D, "M", "c"), + (0x217E, "M", "d"), + (0x217F, "M", "m"), + (0x2180, "V"), + (0x2183, "M", "ↄ"), + (0x2184, "V"), + (0x2189, "M", "0⁄3"), + (0x218A, "V"), + (0x218C, "X"), + (0x2190, "V"), + (0x222C, "M", "∫∫"), + (0x222D, "M", "∫∫∫"), + (0x222E, "V"), + (0x222F, "M", "∮∮"), + (0x2230, "M", "∮∮∮"), + (0x2231, "V"), + (0x2329, "M", "〈"), + (0x232A, "M", "〉"), + (0x232B, "V"), + (0x242A, "X"), + (0x2440, "V"), + (0x244B, "X"), + (0x2460, "M", "1"), + (0x2461, "M", "2"), + (0x2462, "M", "3"), + (0x2463, "M", "4"), + (0x2464, "M", "5"), + (0x2465, "M", "6"), + (0x2466, "M", "7"), + (0x2467, "M", "8"), + (0x2468, "M", "9"), + (0x2469, "M", "10"), + (0x246A, "M", "11"), + (0x246B, "M", "12"), + (0x246C, "M", "13"), + (0x246D, "M", "14"), + (0x246E, "M", "15"), + (0x246F, "M", "16"), + (0x2470, "M", "17"), + (0x2471, "M", "18"), + (0x2472, "M", "19"), + (0x2473, "M", "20"), + (0x2474, "M", "(1)"), + (0x2475, "M", "(2)"), + (0x2476, "M", "(3)"), + (0x2477, "M", "(4)"), + (0x2478, "M", "(5)"), + (0x2479, "M", "(6)"), + (0x247A, "M", "(7)"), + (0x247B, "M", "(8)"), + (0x247C, "M", "(9)"), + (0x247D, "M", "(10)"), + (0x247E, "M", "(11)"), + (0x247F, "M", "(12)"), + (0x2480, "M", "(13)"), + (0x2481, "M", "(14)"), + (0x2482, "M", "(15)"), + (0x2483, "M", "(16)"), + (0x2484, "M", "(17)"), + (0x2485, "M", "(18)"), + (0x2486, "M", "(19)"), + (0x2487, "M", "(20)"), + (0x2488, "X"), + (0x249C, "M", "(a)"), + (0x249D, "M", "(b)"), + (0x249E, "M", "(c)"), + (0x249F, "M", "(d)"), + ] + + +def _seg_24() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x24A0, "M", "(e)"), + (0x24A1, "M", "(f)"), + (0x24A2, "M", "(g)"), + (0x24A3, "M", "(h)"), + (0x24A4, "M", "(i)"), + (0x24A5, "M", "(j)"), + (0x24A6, "M", "(k)"), + (0x24A7, "M", "(l)"), + (0x24A8, "M", "(m)"), + (0x24A9, "M", "(n)"), + (0x24AA, "M", "(o)"), + (0x24AB, "M", "(p)"), + (0x24AC, "M", "(q)"), + (0x24AD, "M", "(r)"), + (0x24AE, "M", "(s)"), + (0x24AF, "M", "(t)"), + (0x24B0, "M", "(u)"), + (0x24B1, "M", "(v)"), + (0x24B2, "M", "(w)"), + (0x24B3, "M", "(x)"), + (0x24B4, "M", "(y)"), + (0x24B5, "M", "(z)"), + (0x24B6, "M", "a"), + (0x24B7, "M", "b"), + (0x24B8, "M", "c"), + (0x24B9, "M", "d"), + (0x24BA, "M", "e"), + (0x24BB, "M", "f"), + (0x24BC, "M", "g"), + (0x24BD, "M", "h"), + (0x24BE, "M", "i"), + (0x24BF, "M", "j"), + (0x24C0, "M", "k"), + (0x24C1, "M", "l"), + (0x24C2, "M", "m"), + (0x24C3, "M", "n"), + (0x24C4, "M", "o"), + (0x24C5, "M", "p"), + (0x24C6, "M", "q"), + (0x24C7, "M", "r"), + (0x24C8, "M", "s"), + (0x24C9, "M", "t"), + (0x24CA, "M", "u"), + (0x24CB, "M", "v"), + (0x24CC, "M", "w"), + (0x24CD, "M", "x"), + (0x24CE, "M", "y"), + (0x24CF, "M", "z"), + (0x24D0, "M", "a"), + (0x24D1, "M", "b"), + (0x24D2, "M", "c"), + (0x24D3, "M", "d"), + (0x24D4, "M", "e"), + (0x24D5, "M", "f"), + (0x24D6, "M", "g"), + (0x24D7, "M", "h"), + (0x24D8, "M", "i"), + (0x24D9, "M", "j"), + (0x24DA, "M", "k"), + (0x24DB, "M", "l"), + (0x24DC, "M", "m"), + (0x24DD, "M", "n"), + (0x24DE, "M", "o"), + (0x24DF, "M", "p"), + (0x24E0, "M", "q"), + (0x24E1, "M", "r"), + (0x24E2, "M", "s"), + (0x24E3, "M", "t"), + (0x24E4, "M", "u"), + (0x24E5, "M", "v"), + (0x24E6, "M", "w"), + (0x24E7, "M", "x"), + (0x24E8, "M", "y"), + (0x24E9, "M", "z"), + (0x24EA, "M", "0"), + (0x24EB, "V"), + (0x2A0C, "M", "∫∫∫∫"), + (0x2A0D, "V"), + (0x2A74, "M", "::="), + (0x2A75, "M", "=="), + (0x2A76, "M", "==="), + (0x2A77, "V"), + (0x2ADC, "M", "⫝̸"), + (0x2ADD, "V"), + (0x2B74, "X"), + (0x2B76, "V"), + (0x2B96, "X"), + (0x2B97, "V"), + (0x2C00, "M", "ⰰ"), + (0x2C01, "M", "ⰱ"), + (0x2C02, "M", "ⰲ"), + (0x2C03, "M", "ⰳ"), + (0x2C04, "M", "ⰴ"), + (0x2C05, "M", "ⰵ"), + (0x2C06, "M", "ⰶ"), + (0x2C07, "M", "ⰷ"), + (0x2C08, "M", "ⰸ"), + (0x2C09, "M", "ⰹ"), + (0x2C0A, "M", "ⰺ"), + (0x2C0B, "M", "ⰻ"), + ] + + +def _seg_25() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2C0C, "M", "ⰼ"), + (0x2C0D, "M", "ⰽ"), + (0x2C0E, "M", "ⰾ"), + (0x2C0F, "M", "ⰿ"), + (0x2C10, "M", "ⱀ"), + (0x2C11, "M", "ⱁ"), + (0x2C12, "M", "ⱂ"), + (0x2C13, "M", "ⱃ"), + (0x2C14, "M", "ⱄ"), + (0x2C15, "M", "ⱅ"), + (0x2C16, "M", "ⱆ"), + (0x2C17, "M", "ⱇ"), + (0x2C18, "M", "ⱈ"), + (0x2C19, "M", "ⱉ"), + (0x2C1A, "M", "ⱊ"), + (0x2C1B, "M", "ⱋ"), + (0x2C1C, "M", "ⱌ"), + (0x2C1D, "M", "ⱍ"), + (0x2C1E, "M", "ⱎ"), + (0x2C1F, "M", "ⱏ"), + (0x2C20, "M", "ⱐ"), + (0x2C21, "M", "ⱑ"), + (0x2C22, "M", "ⱒ"), + (0x2C23, "M", "ⱓ"), + (0x2C24, "M", "ⱔ"), + (0x2C25, "M", "ⱕ"), + (0x2C26, "M", "ⱖ"), + (0x2C27, "M", "ⱗ"), + (0x2C28, "M", "ⱘ"), + (0x2C29, "M", "ⱙ"), + (0x2C2A, "M", "ⱚ"), + (0x2C2B, "M", "ⱛ"), + (0x2C2C, "M", "ⱜ"), + (0x2C2D, "M", "ⱝ"), + (0x2C2E, "M", "ⱞ"), + (0x2C2F, "M", "ⱟ"), + (0x2C30, "V"), + (0x2C60, "M", "ⱡ"), + (0x2C61, "V"), + (0x2C62, "M", "ɫ"), + (0x2C63, "M", "ᵽ"), + (0x2C64, "M", "ɽ"), + (0x2C65, "V"), + (0x2C67, "M", "ⱨ"), + (0x2C68, "V"), + (0x2C69, "M", "ⱪ"), + (0x2C6A, "V"), + (0x2C6B, "M", "ⱬ"), + (0x2C6C, "V"), + (0x2C6D, "M", "ɑ"), + (0x2C6E, "M", "ɱ"), + (0x2C6F, "M", "ɐ"), + (0x2C70, "M", "ɒ"), + (0x2C71, "V"), + (0x2C72, "M", "ⱳ"), + (0x2C73, "V"), + (0x2C75, "M", "ⱶ"), + (0x2C76, "V"), + (0x2C7C, "M", "j"), + (0x2C7D, "M", "v"), + (0x2C7E, "M", "ȿ"), + (0x2C7F, "M", "ɀ"), + (0x2C80, "M", "ⲁ"), + (0x2C81, "V"), + (0x2C82, "M", "ⲃ"), + (0x2C83, "V"), + (0x2C84, "M", "ⲅ"), + (0x2C85, "V"), + (0x2C86, "M", "ⲇ"), + (0x2C87, "V"), + (0x2C88, "M", "ⲉ"), + (0x2C89, "V"), + (0x2C8A, "M", "ⲋ"), + (0x2C8B, "V"), + (0x2C8C, "M", "ⲍ"), + (0x2C8D, "V"), + (0x2C8E, "M", "ⲏ"), + (0x2C8F, "V"), + (0x2C90, "M", "ⲑ"), + (0x2C91, "V"), + (0x2C92, "M", "ⲓ"), + (0x2C93, "V"), + (0x2C94, "M", "ⲕ"), + (0x2C95, "V"), + (0x2C96, "M", "ⲗ"), + (0x2C97, "V"), + (0x2C98, "M", "ⲙ"), + (0x2C99, "V"), + (0x2C9A, "M", "ⲛ"), + (0x2C9B, "V"), + (0x2C9C, "M", "ⲝ"), + (0x2C9D, "V"), + (0x2C9E, "M", "ⲟ"), + (0x2C9F, "V"), + (0x2CA0, "M", "ⲡ"), + (0x2CA1, "V"), + (0x2CA2, "M", "ⲣ"), + (0x2CA3, "V"), + (0x2CA4, "M", "ⲥ"), + (0x2CA5, "V"), + ] + + +def _seg_26() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2CA6, "M", "ⲧ"), + (0x2CA7, "V"), + (0x2CA8, "M", "ⲩ"), + (0x2CA9, "V"), + (0x2CAA, "M", "ⲫ"), + (0x2CAB, "V"), + (0x2CAC, "M", "ⲭ"), + (0x2CAD, "V"), + (0x2CAE, "M", "ⲯ"), + (0x2CAF, "V"), + (0x2CB0, "M", "ⲱ"), + (0x2CB1, "V"), + (0x2CB2, "M", "ⲳ"), + (0x2CB3, "V"), + (0x2CB4, "M", "ⲵ"), + (0x2CB5, "V"), + (0x2CB6, "M", "ⲷ"), + (0x2CB7, "V"), + (0x2CB8, "M", "ⲹ"), + (0x2CB9, "V"), + (0x2CBA, "M", "ⲻ"), + (0x2CBB, "V"), + (0x2CBC, "M", "ⲽ"), + (0x2CBD, "V"), + (0x2CBE, "M", "ⲿ"), + (0x2CBF, "V"), + (0x2CC0, "M", "ⳁ"), + (0x2CC1, "V"), + (0x2CC2, "M", "ⳃ"), + (0x2CC3, "V"), + (0x2CC4, "M", "ⳅ"), + (0x2CC5, "V"), + (0x2CC6, "M", "ⳇ"), + (0x2CC7, "V"), + (0x2CC8, "M", "ⳉ"), + (0x2CC9, "V"), + (0x2CCA, "M", "ⳋ"), + (0x2CCB, "V"), + (0x2CCC, "M", "ⳍ"), + (0x2CCD, "V"), + (0x2CCE, "M", "ⳏ"), + (0x2CCF, "V"), + (0x2CD0, "M", "ⳑ"), + (0x2CD1, "V"), + (0x2CD2, "M", "ⳓ"), + (0x2CD3, "V"), + (0x2CD4, "M", "ⳕ"), + (0x2CD5, "V"), + (0x2CD6, "M", "ⳗ"), + (0x2CD7, "V"), + (0x2CD8, "M", "ⳙ"), + (0x2CD9, "V"), + (0x2CDA, "M", "ⳛ"), + (0x2CDB, "V"), + (0x2CDC, "M", "ⳝ"), + (0x2CDD, "V"), + (0x2CDE, "M", "ⳟ"), + (0x2CDF, "V"), + (0x2CE0, "M", "ⳡ"), + (0x2CE1, "V"), + (0x2CE2, "M", "ⳣ"), + (0x2CE3, "V"), + (0x2CEB, "M", "ⳬ"), + (0x2CEC, "V"), + (0x2CED, "M", "ⳮ"), + (0x2CEE, "V"), + (0x2CF2, "M", "ⳳ"), + (0x2CF3, "V"), + (0x2CF4, "X"), + (0x2CF9, "V"), + (0x2D26, "X"), + (0x2D27, "V"), + (0x2D28, "X"), + (0x2D2D, "V"), + (0x2D2E, "X"), + (0x2D30, "V"), + (0x2D68, "X"), + (0x2D6F, "M", "ⵡ"), + (0x2D70, "V"), + (0x2D71, "X"), + (0x2D7F, "V"), + (0x2D97, "X"), + (0x2DA0, "V"), + (0x2DA7, "X"), + (0x2DA8, "V"), + (0x2DAF, "X"), + (0x2DB0, "V"), + (0x2DB7, "X"), + (0x2DB8, "V"), + (0x2DBF, "X"), + (0x2DC0, "V"), + (0x2DC7, "X"), + (0x2DC8, "V"), + (0x2DCF, "X"), + (0x2DD0, "V"), + (0x2DD7, "X"), + (0x2DD8, "V"), + (0x2DDF, "X"), + (0x2DE0, "V"), + (0x2E5E, "X"), + ] + + +def _seg_27() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2E80, "V"), + (0x2E9A, "X"), + (0x2E9B, "V"), + (0x2E9F, "M", "母"), + (0x2EA0, "V"), + (0x2EF3, "M", "龟"), + (0x2EF4, "X"), + (0x2F00, "M", "一"), + (0x2F01, "M", "丨"), + (0x2F02, "M", "丶"), + (0x2F03, "M", "丿"), + (0x2F04, "M", "乙"), + (0x2F05, "M", "亅"), + (0x2F06, "M", "二"), + (0x2F07, "M", "亠"), + (0x2F08, "M", "人"), + (0x2F09, "M", "儿"), + (0x2F0A, "M", "入"), + (0x2F0B, "M", "八"), + (0x2F0C, "M", "冂"), + (0x2F0D, "M", "冖"), + (0x2F0E, "M", "冫"), + (0x2F0F, "M", "几"), + (0x2F10, "M", "凵"), + (0x2F11, "M", "刀"), + (0x2F12, "M", "力"), + (0x2F13, "M", "勹"), + (0x2F14, "M", "匕"), + (0x2F15, "M", "匚"), + (0x2F16, "M", "匸"), + (0x2F17, "M", "十"), + (0x2F18, "M", "卜"), + (0x2F19, "M", "卩"), + (0x2F1A, "M", "厂"), + (0x2F1B, "M", "厶"), + (0x2F1C, "M", "又"), + (0x2F1D, "M", "口"), + (0x2F1E, "M", "囗"), + (0x2F1F, "M", "土"), + (0x2F20, "M", "士"), + (0x2F21, "M", "夂"), + (0x2F22, "M", "夊"), + (0x2F23, "M", "夕"), + (0x2F24, "M", "大"), + (0x2F25, "M", "女"), + (0x2F26, "M", "子"), + (0x2F27, "M", "宀"), + (0x2F28, "M", "寸"), + (0x2F29, "M", "小"), + (0x2F2A, "M", "尢"), + (0x2F2B, "M", "尸"), + (0x2F2C, "M", "屮"), + (0x2F2D, "M", "山"), + (0x2F2E, "M", "巛"), + (0x2F2F, "M", "工"), + (0x2F30, "M", "己"), + (0x2F31, "M", "巾"), + (0x2F32, "M", "干"), + (0x2F33, "M", "幺"), + (0x2F34, "M", "广"), + (0x2F35, "M", "廴"), + (0x2F36, "M", "廾"), + (0x2F37, "M", "弋"), + (0x2F38, "M", "弓"), + (0x2F39, "M", "彐"), + (0x2F3A, "M", "彡"), + (0x2F3B, "M", "彳"), + (0x2F3C, "M", "心"), + (0x2F3D, "M", "戈"), + (0x2F3E, "M", "戶"), + (0x2F3F, "M", "手"), + (0x2F40, "M", "支"), + (0x2F41, "M", "攴"), + (0x2F42, "M", "文"), + (0x2F43, "M", "斗"), + (0x2F44, "M", "斤"), + (0x2F45, "M", "方"), + (0x2F46, "M", "无"), + (0x2F47, "M", "日"), + (0x2F48, "M", "曰"), + (0x2F49, "M", "月"), + (0x2F4A, "M", "木"), + (0x2F4B, "M", "欠"), + (0x2F4C, "M", "止"), + (0x2F4D, "M", "歹"), + (0x2F4E, "M", "殳"), + (0x2F4F, "M", "毋"), + (0x2F50, "M", "比"), + (0x2F51, "M", "毛"), + (0x2F52, "M", "氏"), + (0x2F53, "M", "气"), + (0x2F54, "M", "水"), + (0x2F55, "M", "火"), + (0x2F56, "M", "爪"), + (0x2F57, "M", "父"), + (0x2F58, "M", "爻"), + (0x2F59, "M", "爿"), + (0x2F5A, "M", "片"), + (0x2F5B, "M", "牙"), + (0x2F5C, "M", "牛"), + ] + + +def _seg_28() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F5D, "M", "犬"), + (0x2F5E, "M", "玄"), + (0x2F5F, "M", "玉"), + (0x2F60, "M", "瓜"), + (0x2F61, "M", "瓦"), + (0x2F62, "M", "甘"), + (0x2F63, "M", "生"), + (0x2F64, "M", "用"), + (0x2F65, "M", "田"), + (0x2F66, "M", "疋"), + (0x2F67, "M", "疒"), + (0x2F68, "M", "癶"), + (0x2F69, "M", "白"), + (0x2F6A, "M", "皮"), + (0x2F6B, "M", "皿"), + (0x2F6C, "M", "目"), + (0x2F6D, "M", "矛"), + (0x2F6E, "M", "矢"), + (0x2F6F, "M", "石"), + (0x2F70, "M", "示"), + (0x2F71, "M", "禸"), + (0x2F72, "M", "禾"), + (0x2F73, "M", "穴"), + (0x2F74, "M", "立"), + (0x2F75, "M", "竹"), + (0x2F76, "M", "米"), + (0x2F77, "M", "糸"), + (0x2F78, "M", "缶"), + (0x2F79, "M", "网"), + (0x2F7A, "M", "羊"), + (0x2F7B, "M", "羽"), + (0x2F7C, "M", "老"), + (0x2F7D, "M", "而"), + (0x2F7E, "M", "耒"), + (0x2F7F, "M", "耳"), + (0x2F80, "M", "聿"), + (0x2F81, "M", "肉"), + (0x2F82, "M", "臣"), + (0x2F83, "M", "自"), + (0x2F84, "M", "至"), + (0x2F85, "M", "臼"), + (0x2F86, "M", "舌"), + (0x2F87, "M", "舛"), + (0x2F88, "M", "舟"), + (0x2F89, "M", "艮"), + (0x2F8A, "M", "色"), + (0x2F8B, "M", "艸"), + (0x2F8C, "M", "虍"), + (0x2F8D, "M", "虫"), + (0x2F8E, "M", "血"), + (0x2F8F, "M", "行"), + (0x2F90, "M", "衣"), + (0x2F91, "M", "襾"), + (0x2F92, "M", "見"), + (0x2F93, "M", "角"), + (0x2F94, "M", "言"), + (0x2F95, "M", "谷"), + (0x2F96, "M", "豆"), + (0x2F97, "M", "豕"), + (0x2F98, "M", "豸"), + (0x2F99, "M", "貝"), + (0x2F9A, "M", "赤"), + (0x2F9B, "M", "走"), + (0x2F9C, "M", "足"), + (0x2F9D, "M", "身"), + (0x2F9E, "M", "車"), + (0x2F9F, "M", "辛"), + (0x2FA0, "M", "辰"), + (0x2FA1, "M", "辵"), + (0x2FA2, "M", "邑"), + (0x2FA3, "M", "酉"), + (0x2FA4, "M", "釆"), + (0x2FA5, "M", "里"), + (0x2FA6, "M", "金"), + (0x2FA7, "M", "長"), + (0x2FA8, "M", "門"), + (0x2FA9, "M", "阜"), + (0x2FAA, "M", "隶"), + (0x2FAB, "M", "隹"), + (0x2FAC, "M", "雨"), + (0x2FAD, "M", "靑"), + (0x2FAE, "M", "非"), + (0x2FAF, "M", "面"), + (0x2FB0, "M", "革"), + (0x2FB1, "M", "韋"), + (0x2FB2, "M", "韭"), + (0x2FB3, "M", "音"), + (0x2FB4, "M", "頁"), + (0x2FB5, "M", "風"), + (0x2FB6, "M", "飛"), + (0x2FB7, "M", "食"), + (0x2FB8, "M", "首"), + (0x2FB9, "M", "香"), + (0x2FBA, "M", "馬"), + (0x2FBB, "M", "骨"), + (0x2FBC, "M", "高"), + (0x2FBD, "M", "髟"), + (0x2FBE, "M", "鬥"), + (0x2FBF, "M", "鬯"), + (0x2FC0, "M", "鬲"), + ] + + +def _seg_29() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2FC1, "M", "鬼"), + (0x2FC2, "M", "魚"), + (0x2FC3, "M", "鳥"), + (0x2FC4, "M", "鹵"), + (0x2FC5, "M", "鹿"), + (0x2FC6, "M", "麥"), + (0x2FC7, "M", "麻"), + (0x2FC8, "M", "黃"), + (0x2FC9, "M", "黍"), + (0x2FCA, "M", "黑"), + (0x2FCB, "M", "黹"), + (0x2FCC, "M", "黽"), + (0x2FCD, "M", "鼎"), + (0x2FCE, "M", "鼓"), + (0x2FCF, "M", "鼠"), + (0x2FD0, "M", "鼻"), + (0x2FD1, "M", "齊"), + (0x2FD2, "M", "齒"), + (0x2FD3, "M", "龍"), + (0x2FD4, "M", "龜"), + (0x2FD5, "M", "龠"), + (0x2FD6, "X"), + (0x3000, "M", " "), + (0x3001, "V"), + (0x3002, "M", "."), + (0x3003, "V"), + (0x3036, "M", "〒"), + (0x3037, "V"), + (0x3038, "M", "十"), + (0x3039, "M", "卄"), + (0x303A, "M", "卅"), + (0x303B, "V"), + (0x3040, "X"), + (0x3041, "V"), + (0x3097, "X"), + (0x3099, "V"), + (0x309B, "M", " ゙"), + (0x309C, "M", " ゚"), + (0x309D, "V"), + (0x309F, "M", "より"), + (0x30A0, "V"), + (0x30FF, "M", "コト"), + (0x3100, "X"), + (0x3105, "V"), + (0x3130, "X"), + (0x3131, "M", "ᄀ"), + (0x3132, "M", "ᄁ"), + (0x3133, "M", "ᆪ"), + (0x3134, "M", "ᄂ"), + (0x3135, "M", "ᆬ"), + (0x3136, "M", "ᆭ"), + (0x3137, "M", "ᄃ"), + (0x3138, "M", "ᄄ"), + (0x3139, "M", "ᄅ"), + (0x313A, "M", "ᆰ"), + (0x313B, "M", "ᆱ"), + (0x313C, "M", "ᆲ"), + (0x313D, "M", "ᆳ"), + (0x313E, "M", "ᆴ"), + (0x313F, "M", "ᆵ"), + (0x3140, "M", "ᄚ"), + (0x3141, "M", "ᄆ"), + (0x3142, "M", "ᄇ"), + (0x3143, "M", "ᄈ"), + (0x3144, "M", "ᄡ"), + (0x3145, "M", "ᄉ"), + (0x3146, "M", "ᄊ"), + (0x3147, "M", "ᄋ"), + (0x3148, "M", "ᄌ"), + (0x3149, "M", "ᄍ"), + (0x314A, "M", "ᄎ"), + (0x314B, "M", "ᄏ"), + (0x314C, "M", "ᄐ"), + (0x314D, "M", "ᄑ"), + (0x314E, "M", "ᄒ"), + (0x314F, "M", "ᅡ"), + (0x3150, "M", "ᅢ"), + (0x3151, "M", "ᅣ"), + (0x3152, "M", "ᅤ"), + (0x3153, "M", "ᅥ"), + (0x3154, "M", "ᅦ"), + (0x3155, "M", "ᅧ"), + (0x3156, "M", "ᅨ"), + (0x3157, "M", "ᅩ"), + (0x3158, "M", "ᅪ"), + (0x3159, "M", "ᅫ"), + (0x315A, "M", "ᅬ"), + (0x315B, "M", "ᅭ"), + (0x315C, "M", "ᅮ"), + (0x315D, "M", "ᅯ"), + (0x315E, "M", "ᅰ"), + (0x315F, "M", "ᅱ"), + (0x3160, "M", "ᅲ"), + (0x3161, "M", "ᅳ"), + (0x3162, "M", "ᅴ"), + (0x3163, "M", "ᅵ"), + (0x3164, "I"), + (0x3165, "M", "ᄔ"), + (0x3166, "M", "ᄕ"), + (0x3167, "M", "ᇇ"), + ] + + +def _seg_30() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x3168, "M", "ᇈ"), + (0x3169, "M", "ᇌ"), + (0x316A, "M", "ᇎ"), + (0x316B, "M", "ᇓ"), + (0x316C, "M", "ᇗ"), + (0x316D, "M", "ᇙ"), + (0x316E, "M", "ᄜ"), + (0x316F, "M", "ᇝ"), + (0x3170, "M", "ᇟ"), + (0x3171, "M", "ᄝ"), + (0x3172, "M", "ᄞ"), + (0x3173, "M", "ᄠ"), + (0x3174, "M", "ᄢ"), + (0x3175, "M", "ᄣ"), + (0x3176, "M", "ᄧ"), + (0x3177, "M", "ᄩ"), + (0x3178, "M", "ᄫ"), + (0x3179, "M", "ᄬ"), + (0x317A, "M", "ᄭ"), + (0x317B, "M", "ᄮ"), + (0x317C, "M", "ᄯ"), + (0x317D, "M", "ᄲ"), + (0x317E, "M", "ᄶ"), + (0x317F, "M", "ᅀ"), + (0x3180, "M", "ᅇ"), + (0x3181, "M", "ᅌ"), + (0x3182, "M", "ᇱ"), + (0x3183, "M", "ᇲ"), + (0x3184, "M", "ᅗ"), + (0x3185, "M", "ᅘ"), + (0x3186, "M", "ᅙ"), + (0x3187, "M", "ᆄ"), + (0x3188, "M", "ᆅ"), + (0x3189, "M", "ᆈ"), + (0x318A, "M", "ᆑ"), + (0x318B, "M", "ᆒ"), + (0x318C, "M", "ᆔ"), + (0x318D, "M", "ᆞ"), + (0x318E, "M", "ᆡ"), + (0x318F, "X"), + (0x3190, "V"), + (0x3192, "M", "一"), + (0x3193, "M", "二"), + (0x3194, "M", "三"), + (0x3195, "M", "四"), + (0x3196, "M", "上"), + (0x3197, "M", "中"), + (0x3198, "M", "下"), + (0x3199, "M", "甲"), + (0x319A, "M", "乙"), + (0x319B, "M", "丙"), + (0x319C, "M", "丁"), + (0x319D, "M", "天"), + (0x319E, "M", "地"), + (0x319F, "M", "人"), + (0x31A0, "V"), + (0x31E6, "X"), + (0x31F0, "V"), + (0x3200, "M", "(ᄀ)"), + (0x3201, "M", "(ᄂ)"), + (0x3202, "M", "(ᄃ)"), + (0x3203, "M", "(ᄅ)"), + (0x3204, "M", "(ᄆ)"), + (0x3205, "M", "(ᄇ)"), + (0x3206, "M", "(ᄉ)"), + (0x3207, "M", "(ᄋ)"), + (0x3208, "M", "(ᄌ)"), + (0x3209, "M", "(ᄎ)"), + (0x320A, "M", "(ᄏ)"), + (0x320B, "M", "(ᄐ)"), + (0x320C, "M", "(ᄑ)"), + (0x320D, "M", "(ᄒ)"), + (0x320E, "M", "(가)"), + (0x320F, "M", "(나)"), + (0x3210, "M", "(다)"), + (0x3211, "M", "(라)"), + (0x3212, "M", "(마)"), + (0x3213, "M", "(바)"), + (0x3214, "M", "(사)"), + (0x3215, "M", "(아)"), + (0x3216, "M", "(자)"), + (0x3217, "M", "(차)"), + (0x3218, "M", "(카)"), + (0x3219, "M", "(타)"), + (0x321A, "M", "(파)"), + (0x321B, "M", "(하)"), + (0x321C, "M", "(주)"), + (0x321D, "M", "(오전)"), + (0x321E, "M", "(오후)"), + (0x321F, "X"), + (0x3220, "M", "(一)"), + (0x3221, "M", "(二)"), + (0x3222, "M", "(三)"), + (0x3223, "M", "(四)"), + (0x3224, "M", "(五)"), + (0x3225, "M", "(六)"), + (0x3226, "M", "(七)"), + (0x3227, "M", "(八)"), + (0x3228, "M", "(九)"), + (0x3229, "M", "(十)"), + ] + + +def _seg_31() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x322A, "M", "(月)"), + (0x322B, "M", "(火)"), + (0x322C, "M", "(水)"), + (0x322D, "M", "(木)"), + (0x322E, "M", "(金)"), + (0x322F, "M", "(土)"), + (0x3230, "M", "(日)"), + (0x3231, "M", "(株)"), + (0x3232, "M", "(有)"), + (0x3233, "M", "(社)"), + (0x3234, "M", "(名)"), + (0x3235, "M", "(特)"), + (0x3236, "M", "(財)"), + (0x3237, "M", "(祝)"), + (0x3238, "M", "(労)"), + (0x3239, "M", "(代)"), + (0x323A, "M", "(呼)"), + (0x323B, "M", "(学)"), + (0x323C, "M", "(監)"), + (0x323D, "M", "(企)"), + (0x323E, "M", "(資)"), + (0x323F, "M", "(協)"), + (0x3240, "M", "(祭)"), + (0x3241, "M", "(休)"), + (0x3242, "M", "(自)"), + (0x3243, "M", "(至)"), + (0x3244, "M", "問"), + (0x3245, "M", "幼"), + (0x3246, "M", "文"), + (0x3247, "M", "箏"), + (0x3248, "V"), + (0x3250, "M", "pte"), + (0x3251, "M", "21"), + (0x3252, "M", "22"), + (0x3253, "M", "23"), + (0x3254, "M", "24"), + (0x3255, "M", "25"), + (0x3256, "M", "26"), + (0x3257, "M", "27"), + (0x3258, "M", "28"), + (0x3259, "M", "29"), + (0x325A, "M", "30"), + (0x325B, "M", "31"), + (0x325C, "M", "32"), + (0x325D, "M", "33"), + (0x325E, "M", "34"), + (0x325F, "M", "35"), + (0x3260, "M", "ᄀ"), + (0x3261, "M", "ᄂ"), + (0x3262, "M", "ᄃ"), + (0x3263, "M", "ᄅ"), + (0x3264, "M", "ᄆ"), + (0x3265, "M", "ᄇ"), + (0x3266, "M", "ᄉ"), + (0x3267, "M", "ᄋ"), + (0x3268, "M", "ᄌ"), + (0x3269, "M", "ᄎ"), + (0x326A, "M", "ᄏ"), + (0x326B, "M", "ᄐ"), + (0x326C, "M", "ᄑ"), + (0x326D, "M", "ᄒ"), + (0x326E, "M", "가"), + (0x326F, "M", "나"), + (0x3270, "M", "다"), + (0x3271, "M", "라"), + (0x3272, "M", "마"), + (0x3273, "M", "바"), + (0x3274, "M", "사"), + (0x3275, "M", "아"), + (0x3276, "M", "자"), + (0x3277, "M", "차"), + (0x3278, "M", "카"), + (0x3279, "M", "타"), + (0x327A, "M", "파"), + (0x327B, "M", "하"), + (0x327C, "M", "참고"), + (0x327D, "M", "주의"), + (0x327E, "M", "우"), + (0x327F, "V"), + (0x3280, "M", "一"), + (0x3281, "M", "二"), + (0x3282, "M", "三"), + (0x3283, "M", "四"), + (0x3284, "M", "五"), + (0x3285, "M", "六"), + (0x3286, "M", "七"), + (0x3287, "M", "八"), + (0x3288, "M", "九"), + (0x3289, "M", "十"), + (0x328A, "M", "月"), + (0x328B, "M", "火"), + (0x328C, "M", "水"), + (0x328D, "M", "木"), + (0x328E, "M", "金"), + (0x328F, "M", "土"), + (0x3290, "M", "日"), + (0x3291, "M", "株"), + (0x3292, "M", "有"), + (0x3293, "M", "社"), + (0x3294, "M", "名"), + ] + + +def _seg_32() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x3295, "M", "特"), + (0x3296, "M", "財"), + (0x3297, "M", "祝"), + (0x3298, "M", "労"), + (0x3299, "M", "秘"), + (0x329A, "M", "男"), + (0x329B, "M", "女"), + (0x329C, "M", "適"), + (0x329D, "M", "優"), + (0x329E, "M", "印"), + (0x329F, "M", "注"), + (0x32A0, "M", "項"), + (0x32A1, "M", "休"), + (0x32A2, "M", "写"), + (0x32A3, "M", "正"), + (0x32A4, "M", "上"), + (0x32A5, "M", "中"), + (0x32A6, "M", "下"), + (0x32A7, "M", "左"), + (0x32A8, "M", "右"), + (0x32A9, "M", "医"), + (0x32AA, "M", "宗"), + (0x32AB, "M", "学"), + (0x32AC, "M", "監"), + (0x32AD, "M", "企"), + (0x32AE, "M", "資"), + (0x32AF, "M", "協"), + (0x32B0, "M", "夜"), + (0x32B1, "M", "36"), + (0x32B2, "M", "37"), + (0x32B3, "M", "38"), + (0x32B4, "M", "39"), + (0x32B5, "M", "40"), + (0x32B6, "M", "41"), + (0x32B7, "M", "42"), + (0x32B8, "M", "43"), + (0x32B9, "M", "44"), + (0x32BA, "M", "45"), + (0x32BB, "M", "46"), + (0x32BC, "M", "47"), + (0x32BD, "M", "48"), + (0x32BE, "M", "49"), + (0x32BF, "M", "50"), + (0x32C0, "M", "1月"), + (0x32C1, "M", "2月"), + (0x32C2, "M", "3月"), + (0x32C3, "M", "4月"), + (0x32C4, "M", "5月"), + (0x32C5, "M", "6月"), + (0x32C6, "M", "7月"), + (0x32C7, "M", "8月"), + (0x32C8, "M", "9月"), + (0x32C9, "M", "10月"), + (0x32CA, "M", "11月"), + (0x32CB, "M", "12月"), + (0x32CC, "M", "hg"), + (0x32CD, "M", "erg"), + (0x32CE, "M", "ev"), + (0x32CF, "M", "ltd"), + (0x32D0, "M", "ア"), + (0x32D1, "M", "イ"), + (0x32D2, "M", "ウ"), + (0x32D3, "M", "エ"), + (0x32D4, "M", "オ"), + (0x32D5, "M", "カ"), + (0x32D6, "M", "キ"), + (0x32D7, "M", "ク"), + (0x32D8, "M", "ケ"), + (0x32D9, "M", "コ"), + (0x32DA, "M", "サ"), + (0x32DB, "M", "シ"), + (0x32DC, "M", "ス"), + (0x32DD, "M", "セ"), + (0x32DE, "M", "ソ"), + (0x32DF, "M", "タ"), + (0x32E0, "M", "チ"), + (0x32E1, "M", "ツ"), + (0x32E2, "M", "テ"), + (0x32E3, "M", "ト"), + (0x32E4, "M", "ナ"), + (0x32E5, "M", "ニ"), + (0x32E6, "M", "ヌ"), + (0x32E7, "M", "ネ"), + (0x32E8, "M", "ノ"), + (0x32E9, "M", "ハ"), + (0x32EA, "M", "ヒ"), + (0x32EB, "M", "フ"), + (0x32EC, "M", "ヘ"), + (0x32ED, "M", "ホ"), + (0x32EE, "M", "マ"), + (0x32EF, "M", "ミ"), + (0x32F0, "M", "ム"), + (0x32F1, "M", "メ"), + (0x32F2, "M", "モ"), + (0x32F3, "M", "ヤ"), + (0x32F4, "M", "ユ"), + (0x32F5, "M", "ヨ"), + (0x32F6, "M", "ラ"), + (0x32F7, "M", "リ"), + (0x32F8, "M", "ル"), + ] + + +def _seg_33() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x32F9, "M", "レ"), + (0x32FA, "M", "ロ"), + (0x32FB, "M", "ワ"), + (0x32FC, "M", "ヰ"), + (0x32FD, "M", "ヱ"), + (0x32FE, "M", "ヲ"), + (0x32FF, "M", "令和"), + (0x3300, "M", "アパート"), + (0x3301, "M", "アルファ"), + (0x3302, "M", "アンペア"), + (0x3303, "M", "アール"), + (0x3304, "M", "イニング"), + (0x3305, "M", "インチ"), + (0x3306, "M", "ウォン"), + (0x3307, "M", "エスクード"), + (0x3308, "M", "エーカー"), + (0x3309, "M", "オンス"), + (0x330A, "M", "オーム"), + (0x330B, "M", "カイリ"), + (0x330C, "M", "カラット"), + (0x330D, "M", "カロリー"), + (0x330E, "M", "ガロン"), + (0x330F, "M", "ガンマ"), + (0x3310, "M", "ギガ"), + (0x3311, "M", "ギニー"), + (0x3312, "M", "キュリー"), + (0x3313, "M", "ギルダー"), + (0x3314, "M", "キロ"), + (0x3315, "M", "キログラム"), + (0x3316, "M", "キロメートル"), + (0x3317, "M", "キロワット"), + (0x3318, "M", "グラム"), + (0x3319, "M", "グラムトン"), + (0x331A, "M", "クルゼイロ"), + (0x331B, "M", "クローネ"), + (0x331C, "M", "ケース"), + (0x331D, "M", "コルナ"), + (0x331E, "M", "コーポ"), + (0x331F, "M", "サイクル"), + (0x3320, "M", "サンチーム"), + (0x3321, "M", "シリング"), + (0x3322, "M", "センチ"), + (0x3323, "M", "セント"), + (0x3324, "M", "ダース"), + (0x3325, "M", "デシ"), + (0x3326, "M", "ドル"), + (0x3327, "M", "トン"), + (0x3328, "M", "ナノ"), + (0x3329, "M", "ノット"), + (0x332A, "M", "ハイツ"), + (0x332B, "M", "パーセント"), + (0x332C, "M", "パーツ"), + (0x332D, "M", "バーレル"), + (0x332E, "M", "ピアストル"), + (0x332F, "M", "ピクル"), + (0x3330, "M", "ピコ"), + (0x3331, "M", "ビル"), + (0x3332, "M", "ファラッド"), + (0x3333, "M", "フィート"), + (0x3334, "M", "ブッシェル"), + (0x3335, "M", "フラン"), + (0x3336, "M", "ヘクタール"), + (0x3337, "M", "ペソ"), + (0x3338, "M", "ペニヒ"), + (0x3339, "M", "ヘルツ"), + (0x333A, "M", "ペンス"), + (0x333B, "M", "ページ"), + (0x333C, "M", "ベータ"), + (0x333D, "M", "ポイント"), + (0x333E, "M", "ボルト"), + (0x333F, "M", "ホン"), + (0x3340, "M", "ポンド"), + (0x3341, "M", "ホール"), + (0x3342, "M", "ホーン"), + (0x3343, "M", "マイクロ"), + (0x3344, "M", "マイル"), + (0x3345, "M", "マッハ"), + (0x3346, "M", "マルク"), + (0x3347, "M", "マンション"), + (0x3348, "M", "ミクロン"), + (0x3349, "M", "ミリ"), + (0x334A, "M", "ミリバール"), + (0x334B, "M", "メガ"), + (0x334C, "M", "メガトン"), + (0x334D, "M", "メートル"), + (0x334E, "M", "ヤード"), + (0x334F, "M", "ヤール"), + (0x3350, "M", "ユアン"), + (0x3351, "M", "リットル"), + (0x3352, "M", "リラ"), + (0x3353, "M", "ルピー"), + (0x3354, "M", "ルーブル"), + (0x3355, "M", "レム"), + (0x3356, "M", "レントゲン"), + (0x3357, "M", "ワット"), + (0x3358, "M", "0点"), + (0x3359, "M", "1点"), + (0x335A, "M", "2点"), + (0x335B, "M", "3点"), + (0x335C, "M", "4点"), + ] + + +def _seg_34() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x335D, "M", "5点"), + (0x335E, "M", "6点"), + (0x335F, "M", "7点"), + (0x3360, "M", "8点"), + (0x3361, "M", "9点"), + (0x3362, "M", "10点"), + (0x3363, "M", "11点"), + (0x3364, "M", "12点"), + (0x3365, "M", "13点"), + (0x3366, "M", "14点"), + (0x3367, "M", "15点"), + (0x3368, "M", "16点"), + (0x3369, "M", "17点"), + (0x336A, "M", "18点"), + (0x336B, "M", "19点"), + (0x336C, "M", "20点"), + (0x336D, "M", "21点"), + (0x336E, "M", "22点"), + (0x336F, "M", "23点"), + (0x3370, "M", "24点"), + (0x3371, "M", "hpa"), + (0x3372, "M", "da"), + (0x3373, "M", "au"), + (0x3374, "M", "bar"), + (0x3375, "M", "ov"), + (0x3376, "M", "pc"), + (0x3377, "M", "dm"), + (0x3378, "M", "dm2"), + (0x3379, "M", "dm3"), + (0x337A, "M", "iu"), + (0x337B, "M", "平成"), + (0x337C, "M", "昭和"), + (0x337D, "M", "大正"), + (0x337E, "M", "明治"), + (0x337F, "M", "株式会社"), + (0x3380, "M", "pa"), + (0x3381, "M", "na"), + (0x3382, "M", "μa"), + (0x3383, "M", "ma"), + (0x3384, "M", "ka"), + (0x3385, "M", "kb"), + (0x3386, "M", "mb"), + (0x3387, "M", "gb"), + (0x3388, "M", "cal"), + (0x3389, "M", "kcal"), + (0x338A, "M", "pf"), + (0x338B, "M", "nf"), + (0x338C, "M", "μf"), + (0x338D, "M", "μg"), + (0x338E, "M", "mg"), + (0x338F, "M", "kg"), + (0x3390, "M", "hz"), + (0x3391, "M", "khz"), + (0x3392, "M", "mhz"), + (0x3393, "M", "ghz"), + (0x3394, "M", "thz"), + (0x3395, "M", "μl"), + (0x3396, "M", "ml"), + (0x3397, "M", "dl"), + (0x3398, "M", "kl"), + (0x3399, "M", "fm"), + (0x339A, "M", "nm"), + (0x339B, "M", "μm"), + (0x339C, "M", "mm"), + (0x339D, "M", "cm"), + (0x339E, "M", "km"), + (0x339F, "M", "mm2"), + (0x33A0, "M", "cm2"), + (0x33A1, "M", "m2"), + (0x33A2, "M", "km2"), + (0x33A3, "M", "mm3"), + (0x33A4, "M", "cm3"), + (0x33A5, "M", "m3"), + (0x33A6, "M", "km3"), + (0x33A7, "M", "m∕s"), + (0x33A8, "M", "m∕s2"), + (0x33A9, "M", "pa"), + (0x33AA, "M", "kpa"), + (0x33AB, "M", "mpa"), + (0x33AC, "M", "gpa"), + (0x33AD, "M", "rad"), + (0x33AE, "M", "rad∕s"), + (0x33AF, "M", "rad∕s2"), + (0x33B0, "M", "ps"), + (0x33B1, "M", "ns"), + (0x33B2, "M", "μs"), + (0x33B3, "M", "ms"), + (0x33B4, "M", "pv"), + (0x33B5, "M", "nv"), + (0x33B6, "M", "μv"), + (0x33B7, "M", "mv"), + (0x33B8, "M", "kv"), + (0x33B9, "M", "mv"), + (0x33BA, "M", "pw"), + (0x33BB, "M", "nw"), + (0x33BC, "M", "μw"), + (0x33BD, "M", "mw"), + (0x33BE, "M", "kw"), + (0x33BF, "M", "mw"), + (0x33C0, "M", "kω"), + ] + + +def _seg_35() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x33C1, "M", "mω"), + (0x33C2, "X"), + (0x33C3, "M", "bq"), + (0x33C4, "M", "cc"), + (0x33C5, "M", "cd"), + (0x33C6, "M", "c∕kg"), + (0x33C7, "X"), + (0x33C8, "M", "db"), + (0x33C9, "M", "gy"), + (0x33CA, "M", "ha"), + (0x33CB, "M", "hp"), + (0x33CC, "M", "in"), + (0x33CD, "M", "kk"), + (0x33CE, "M", "km"), + (0x33CF, "M", "kt"), + (0x33D0, "M", "lm"), + (0x33D1, "M", "ln"), + (0x33D2, "M", "log"), + (0x33D3, "M", "lx"), + (0x33D4, "M", "mb"), + (0x33D5, "M", "mil"), + (0x33D6, "M", "mol"), + (0x33D7, "M", "ph"), + (0x33D8, "X"), + (0x33D9, "M", "ppm"), + (0x33DA, "M", "pr"), + (0x33DB, "M", "sr"), + (0x33DC, "M", "sv"), + (0x33DD, "M", "wb"), + (0x33DE, "M", "v∕m"), + (0x33DF, "M", "a∕m"), + (0x33E0, "M", "1日"), + (0x33E1, "M", "2日"), + (0x33E2, "M", "3日"), + (0x33E3, "M", "4日"), + (0x33E4, "M", "5日"), + (0x33E5, "M", "6日"), + (0x33E6, "M", "7日"), + (0x33E7, "M", "8日"), + (0x33E8, "M", "9日"), + (0x33E9, "M", "10日"), + (0x33EA, "M", "11日"), + (0x33EB, "M", "12日"), + (0x33EC, "M", "13日"), + (0x33ED, "M", "14日"), + (0x33EE, "M", "15日"), + (0x33EF, "M", "16日"), + (0x33F0, "M", "17日"), + (0x33F1, "M", "18日"), + (0x33F2, "M", "19日"), + (0x33F3, "M", "20日"), + (0x33F4, "M", "21日"), + (0x33F5, "M", "22日"), + (0x33F6, "M", "23日"), + (0x33F7, "M", "24日"), + (0x33F8, "M", "25日"), + (0x33F9, "M", "26日"), + (0x33FA, "M", "27日"), + (0x33FB, "M", "28日"), + (0x33FC, "M", "29日"), + (0x33FD, "M", "30日"), + (0x33FE, "M", "31日"), + (0x33FF, "M", "gal"), + (0x3400, "V"), + (0xA48D, "X"), + (0xA490, "V"), + (0xA4C7, "X"), + (0xA4D0, "V"), + (0xA62C, "X"), + (0xA640, "M", "ꙁ"), + (0xA641, "V"), + (0xA642, "M", "ꙃ"), + (0xA643, "V"), + (0xA644, "M", "ꙅ"), + (0xA645, "V"), + (0xA646, "M", "ꙇ"), + (0xA647, "V"), + (0xA648, "M", "ꙉ"), + (0xA649, "V"), + (0xA64A, "M", "ꙋ"), + (0xA64B, "V"), + (0xA64C, "M", "ꙍ"), + (0xA64D, "V"), + (0xA64E, "M", "ꙏ"), + (0xA64F, "V"), + (0xA650, "M", "ꙑ"), + (0xA651, "V"), + (0xA652, "M", "ꙓ"), + (0xA653, "V"), + (0xA654, "M", "ꙕ"), + (0xA655, "V"), + (0xA656, "M", "ꙗ"), + (0xA657, "V"), + (0xA658, "M", "ꙙ"), + (0xA659, "V"), + (0xA65A, "M", "ꙛ"), + (0xA65B, "V"), + (0xA65C, "M", "ꙝ"), + (0xA65D, "V"), + (0xA65E, "M", "ꙟ"), + ] + + +def _seg_36() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xA65F, "V"), + (0xA660, "M", "ꙡ"), + (0xA661, "V"), + (0xA662, "M", "ꙣ"), + (0xA663, "V"), + (0xA664, "M", "ꙥ"), + (0xA665, "V"), + (0xA666, "M", "ꙧ"), + (0xA667, "V"), + (0xA668, "M", "ꙩ"), + (0xA669, "V"), + (0xA66A, "M", "ꙫ"), + (0xA66B, "V"), + (0xA66C, "M", "ꙭ"), + (0xA66D, "V"), + (0xA680, "M", "ꚁ"), + (0xA681, "V"), + (0xA682, "M", "ꚃ"), + (0xA683, "V"), + (0xA684, "M", "ꚅ"), + (0xA685, "V"), + (0xA686, "M", "ꚇ"), + (0xA687, "V"), + (0xA688, "M", "ꚉ"), + (0xA689, "V"), + (0xA68A, "M", "ꚋ"), + (0xA68B, "V"), + (0xA68C, "M", "ꚍ"), + (0xA68D, "V"), + (0xA68E, "M", "ꚏ"), + (0xA68F, "V"), + (0xA690, "M", "ꚑ"), + (0xA691, "V"), + (0xA692, "M", "ꚓ"), + (0xA693, "V"), + (0xA694, "M", "ꚕ"), + (0xA695, "V"), + (0xA696, "M", "ꚗ"), + (0xA697, "V"), + (0xA698, "M", "ꚙ"), + (0xA699, "V"), + (0xA69A, "M", "ꚛ"), + (0xA69B, "V"), + (0xA69C, "M", "ъ"), + (0xA69D, "M", "ь"), + (0xA69E, "V"), + (0xA6F8, "X"), + (0xA700, "V"), + (0xA722, "M", "ꜣ"), + (0xA723, "V"), + (0xA724, "M", "ꜥ"), + (0xA725, "V"), + (0xA726, "M", "ꜧ"), + (0xA727, "V"), + (0xA728, "M", "ꜩ"), + (0xA729, "V"), + (0xA72A, "M", "ꜫ"), + (0xA72B, "V"), + (0xA72C, "M", "ꜭ"), + (0xA72D, "V"), + (0xA72E, "M", "ꜯ"), + (0xA72F, "V"), + (0xA732, "M", "ꜳ"), + (0xA733, "V"), + (0xA734, "M", "ꜵ"), + (0xA735, "V"), + (0xA736, "M", "ꜷ"), + (0xA737, "V"), + (0xA738, "M", "ꜹ"), + (0xA739, "V"), + (0xA73A, "M", "ꜻ"), + (0xA73B, "V"), + (0xA73C, "M", "ꜽ"), + (0xA73D, "V"), + (0xA73E, "M", "ꜿ"), + (0xA73F, "V"), + (0xA740, "M", "ꝁ"), + (0xA741, "V"), + (0xA742, "M", "ꝃ"), + (0xA743, "V"), + (0xA744, "M", "ꝅ"), + (0xA745, "V"), + (0xA746, "M", "ꝇ"), + (0xA747, "V"), + (0xA748, "M", "ꝉ"), + (0xA749, "V"), + (0xA74A, "M", "ꝋ"), + (0xA74B, "V"), + (0xA74C, "M", "ꝍ"), + (0xA74D, "V"), + (0xA74E, "M", "ꝏ"), + (0xA74F, "V"), + (0xA750, "M", "ꝑ"), + (0xA751, "V"), + (0xA752, "M", "ꝓ"), + (0xA753, "V"), + (0xA754, "M", "ꝕ"), + (0xA755, "V"), + (0xA756, "M", "ꝗ"), + (0xA757, "V"), + ] + + +def _seg_37() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xA758, "M", "ꝙ"), + (0xA759, "V"), + (0xA75A, "M", "ꝛ"), + (0xA75B, "V"), + (0xA75C, "M", "ꝝ"), + (0xA75D, "V"), + (0xA75E, "M", "ꝟ"), + (0xA75F, "V"), + (0xA760, "M", "ꝡ"), + (0xA761, "V"), + (0xA762, "M", "ꝣ"), + (0xA763, "V"), + (0xA764, "M", "ꝥ"), + (0xA765, "V"), + (0xA766, "M", "ꝧ"), + (0xA767, "V"), + (0xA768, "M", "ꝩ"), + (0xA769, "V"), + (0xA76A, "M", "ꝫ"), + (0xA76B, "V"), + (0xA76C, "M", "ꝭ"), + (0xA76D, "V"), + (0xA76E, "M", "ꝯ"), + (0xA76F, "V"), + (0xA770, "M", "ꝯ"), + (0xA771, "V"), + (0xA779, "M", "ꝺ"), + (0xA77A, "V"), + (0xA77B, "M", "ꝼ"), + (0xA77C, "V"), + (0xA77D, "M", "ᵹ"), + (0xA77E, "M", "ꝿ"), + (0xA77F, "V"), + (0xA780, "M", "ꞁ"), + (0xA781, "V"), + (0xA782, "M", "ꞃ"), + (0xA783, "V"), + (0xA784, "M", "ꞅ"), + (0xA785, "V"), + (0xA786, "M", "ꞇ"), + (0xA787, "V"), + (0xA78B, "M", "ꞌ"), + (0xA78C, "V"), + (0xA78D, "M", "ɥ"), + (0xA78E, "V"), + (0xA790, "M", "ꞑ"), + (0xA791, "V"), + (0xA792, "M", "ꞓ"), + (0xA793, "V"), + (0xA796, "M", "ꞗ"), + (0xA797, "V"), + (0xA798, "M", "ꞙ"), + (0xA799, "V"), + (0xA79A, "M", "ꞛ"), + (0xA79B, "V"), + (0xA79C, "M", "ꞝ"), + (0xA79D, "V"), + (0xA79E, "M", "ꞟ"), + (0xA79F, "V"), + (0xA7A0, "M", "ꞡ"), + (0xA7A1, "V"), + (0xA7A2, "M", "ꞣ"), + (0xA7A3, "V"), + (0xA7A4, "M", "ꞥ"), + (0xA7A5, "V"), + (0xA7A6, "M", "ꞧ"), + (0xA7A7, "V"), + (0xA7A8, "M", "ꞩ"), + (0xA7A9, "V"), + (0xA7AA, "M", "ɦ"), + (0xA7AB, "M", "ɜ"), + (0xA7AC, "M", "ɡ"), + (0xA7AD, "M", "ɬ"), + (0xA7AE, "M", "ɪ"), + (0xA7AF, "V"), + (0xA7B0, "M", "ʞ"), + (0xA7B1, "M", "ʇ"), + (0xA7B2, "M", "ʝ"), + (0xA7B3, "M", "ꭓ"), + (0xA7B4, "M", "ꞵ"), + (0xA7B5, "V"), + (0xA7B6, "M", "ꞷ"), + (0xA7B7, "V"), + (0xA7B8, "M", "ꞹ"), + (0xA7B9, "V"), + (0xA7BA, "M", "ꞻ"), + (0xA7BB, "V"), + (0xA7BC, "M", "ꞽ"), + (0xA7BD, "V"), + (0xA7BE, "M", "ꞿ"), + (0xA7BF, "V"), + (0xA7C0, "M", "ꟁ"), + (0xA7C1, "V"), + (0xA7C2, "M", "ꟃ"), + (0xA7C3, "V"), + (0xA7C4, "M", "ꞔ"), + (0xA7C5, "M", "ʂ"), + (0xA7C6, "M", "ᶎ"), + (0xA7C7, "M", "ꟈ"), + (0xA7C8, "V"), + ] + + +def _seg_38() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xA7C9, "M", "ꟊ"), + (0xA7CA, "V"), + (0xA7CB, "M", "ɤ"), + (0xA7CC, "M", "ꟍ"), + (0xA7CD, "V"), + (0xA7CE, "X"), + (0xA7D0, "M", "ꟑ"), + (0xA7D1, "V"), + (0xA7D2, "X"), + (0xA7D3, "V"), + (0xA7D4, "X"), + (0xA7D5, "V"), + (0xA7D6, "M", "ꟗ"), + (0xA7D7, "V"), + (0xA7D8, "M", "ꟙ"), + (0xA7D9, "V"), + (0xA7DA, "M", "ꟛ"), + (0xA7DB, "V"), + (0xA7DC, "M", "ƛ"), + (0xA7DD, "X"), + (0xA7F2, "M", "c"), + (0xA7F3, "M", "f"), + (0xA7F4, "M", "q"), + (0xA7F5, "M", "ꟶ"), + (0xA7F6, "V"), + (0xA7F8, "M", "ħ"), + (0xA7F9, "M", "œ"), + (0xA7FA, "V"), + (0xA82D, "X"), + (0xA830, "V"), + (0xA83A, "X"), + (0xA840, "V"), + (0xA878, "X"), + (0xA880, "V"), + (0xA8C6, "X"), + (0xA8CE, "V"), + (0xA8DA, "X"), + (0xA8E0, "V"), + (0xA954, "X"), + (0xA95F, "V"), + (0xA97D, "X"), + (0xA980, "V"), + (0xA9CE, "X"), + (0xA9CF, "V"), + (0xA9DA, "X"), + (0xA9DE, "V"), + (0xA9FF, "X"), + (0xAA00, "V"), + (0xAA37, "X"), + (0xAA40, "V"), + (0xAA4E, "X"), + (0xAA50, "V"), + (0xAA5A, "X"), + (0xAA5C, "V"), + (0xAAC3, "X"), + (0xAADB, "V"), + (0xAAF7, "X"), + (0xAB01, "V"), + (0xAB07, "X"), + (0xAB09, "V"), + (0xAB0F, "X"), + (0xAB11, "V"), + (0xAB17, "X"), + (0xAB20, "V"), + (0xAB27, "X"), + (0xAB28, "V"), + (0xAB2F, "X"), + (0xAB30, "V"), + (0xAB5C, "M", "ꜧ"), + (0xAB5D, "M", "ꬷ"), + (0xAB5E, "M", "ɫ"), + (0xAB5F, "M", "ꭒ"), + (0xAB60, "V"), + (0xAB69, "M", "ʍ"), + (0xAB6A, "V"), + (0xAB6C, "X"), + (0xAB70, "M", "Ꭰ"), + (0xAB71, "M", "Ꭱ"), + (0xAB72, "M", "Ꭲ"), + (0xAB73, "M", "Ꭳ"), + (0xAB74, "M", "Ꭴ"), + (0xAB75, "M", "Ꭵ"), + (0xAB76, "M", "Ꭶ"), + (0xAB77, "M", "Ꭷ"), + (0xAB78, "M", "Ꭸ"), + (0xAB79, "M", "Ꭹ"), + (0xAB7A, "M", "Ꭺ"), + (0xAB7B, "M", "Ꭻ"), + (0xAB7C, "M", "Ꭼ"), + (0xAB7D, "M", "Ꭽ"), + (0xAB7E, "M", "Ꭾ"), + (0xAB7F, "M", "Ꭿ"), + (0xAB80, "M", "Ꮀ"), + (0xAB81, "M", "Ꮁ"), + (0xAB82, "M", "Ꮂ"), + (0xAB83, "M", "Ꮃ"), + (0xAB84, "M", "Ꮄ"), + (0xAB85, "M", "Ꮅ"), + (0xAB86, "M", "Ꮆ"), + (0xAB87, "M", "Ꮇ"), + ] + + +def _seg_39() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xAB88, "M", "Ꮈ"), + (0xAB89, "M", "Ꮉ"), + (0xAB8A, "M", "Ꮊ"), + (0xAB8B, "M", "Ꮋ"), + (0xAB8C, "M", "Ꮌ"), + (0xAB8D, "M", "Ꮍ"), + (0xAB8E, "M", "Ꮎ"), + (0xAB8F, "M", "Ꮏ"), + (0xAB90, "M", "Ꮐ"), + (0xAB91, "M", "Ꮑ"), + (0xAB92, "M", "Ꮒ"), + (0xAB93, "M", "Ꮓ"), + (0xAB94, "M", "Ꮔ"), + (0xAB95, "M", "Ꮕ"), + (0xAB96, "M", "Ꮖ"), + (0xAB97, "M", "Ꮗ"), + (0xAB98, "M", "Ꮘ"), + (0xAB99, "M", "Ꮙ"), + (0xAB9A, "M", "Ꮚ"), + (0xAB9B, "M", "Ꮛ"), + (0xAB9C, "M", "Ꮜ"), + (0xAB9D, "M", "Ꮝ"), + (0xAB9E, "M", "Ꮞ"), + (0xAB9F, "M", "Ꮟ"), + (0xABA0, "M", "Ꮠ"), + (0xABA1, "M", "Ꮡ"), + (0xABA2, "M", "Ꮢ"), + (0xABA3, "M", "Ꮣ"), + (0xABA4, "M", "Ꮤ"), + (0xABA5, "M", "Ꮥ"), + (0xABA6, "M", "Ꮦ"), + (0xABA7, "M", "Ꮧ"), + (0xABA8, "M", "Ꮨ"), + (0xABA9, "M", "Ꮩ"), + (0xABAA, "M", "Ꮪ"), + (0xABAB, "M", "Ꮫ"), + (0xABAC, "M", "Ꮬ"), + (0xABAD, "M", "Ꮭ"), + (0xABAE, "M", "Ꮮ"), + (0xABAF, "M", "Ꮯ"), + (0xABB0, "M", "Ꮰ"), + (0xABB1, "M", "Ꮱ"), + (0xABB2, "M", "Ꮲ"), + (0xABB3, "M", "Ꮳ"), + (0xABB4, "M", "Ꮴ"), + (0xABB5, "M", "Ꮵ"), + (0xABB6, "M", "Ꮶ"), + (0xABB7, "M", "Ꮷ"), + (0xABB8, "M", "Ꮸ"), + (0xABB9, "M", "Ꮹ"), + (0xABBA, "M", "Ꮺ"), + (0xABBB, "M", "Ꮻ"), + (0xABBC, "M", "Ꮼ"), + (0xABBD, "M", "Ꮽ"), + (0xABBE, "M", "Ꮾ"), + (0xABBF, "M", "Ꮿ"), + (0xABC0, "V"), + (0xABEE, "X"), + (0xABF0, "V"), + (0xABFA, "X"), + (0xAC00, "V"), + (0xD7A4, "X"), + (0xD7B0, "V"), + (0xD7C7, "X"), + (0xD7CB, "V"), + (0xD7FC, "X"), + (0xF900, "M", "豈"), + (0xF901, "M", "更"), + (0xF902, "M", "車"), + (0xF903, "M", "賈"), + (0xF904, "M", "滑"), + (0xF905, "M", "串"), + (0xF906, "M", "句"), + (0xF907, "M", "龜"), + (0xF909, "M", "契"), + (0xF90A, "M", "金"), + (0xF90B, "M", "喇"), + (0xF90C, "M", "奈"), + (0xF90D, "M", "懶"), + (0xF90E, "M", "癩"), + (0xF90F, "M", "羅"), + (0xF910, "M", "蘿"), + (0xF911, "M", "螺"), + (0xF912, "M", "裸"), + (0xF913, "M", "邏"), + (0xF914, "M", "樂"), + (0xF915, "M", "洛"), + (0xF916, "M", "烙"), + (0xF917, "M", "珞"), + (0xF918, "M", "落"), + (0xF919, "M", "酪"), + (0xF91A, "M", "駱"), + (0xF91B, "M", "亂"), + (0xF91C, "M", "卵"), + (0xF91D, "M", "欄"), + (0xF91E, "M", "爛"), + (0xF91F, "M", "蘭"), + (0xF920, "M", "鸞"), + (0xF921, "M", "嵐"), + (0xF922, "M", "濫"), + ] + + +def _seg_40() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xF923, "M", "藍"), + (0xF924, "M", "襤"), + (0xF925, "M", "拉"), + (0xF926, "M", "臘"), + (0xF927, "M", "蠟"), + (0xF928, "M", "廊"), + (0xF929, "M", "朗"), + (0xF92A, "M", "浪"), + (0xF92B, "M", "狼"), + (0xF92C, "M", "郎"), + (0xF92D, "M", "來"), + (0xF92E, "M", "冷"), + (0xF92F, "M", "勞"), + (0xF930, "M", "擄"), + (0xF931, "M", "櫓"), + (0xF932, "M", "爐"), + (0xF933, "M", "盧"), + (0xF934, "M", "老"), + (0xF935, "M", "蘆"), + (0xF936, "M", "虜"), + (0xF937, "M", "路"), + (0xF938, "M", "露"), + (0xF939, "M", "魯"), + (0xF93A, "M", "鷺"), + (0xF93B, "M", "碌"), + (0xF93C, "M", "祿"), + (0xF93D, "M", "綠"), + (0xF93E, "M", "菉"), + (0xF93F, "M", "錄"), + (0xF940, "M", "鹿"), + (0xF941, "M", "論"), + (0xF942, "M", "壟"), + (0xF943, "M", "弄"), + (0xF944, "M", "籠"), + (0xF945, "M", "聾"), + (0xF946, "M", "牢"), + (0xF947, "M", "磊"), + (0xF948, "M", "賂"), + (0xF949, "M", "雷"), + (0xF94A, "M", "壘"), + (0xF94B, "M", "屢"), + (0xF94C, "M", "樓"), + (0xF94D, "M", "淚"), + (0xF94E, "M", "漏"), + (0xF94F, "M", "累"), + (0xF950, "M", "縷"), + (0xF951, "M", "陋"), + (0xF952, "M", "勒"), + (0xF953, "M", "肋"), + (0xF954, "M", "凜"), + (0xF955, "M", "凌"), + (0xF956, "M", "稜"), + (0xF957, "M", "綾"), + (0xF958, "M", "菱"), + (0xF959, "M", "陵"), + (0xF95A, "M", "讀"), + (0xF95B, "M", "拏"), + (0xF95C, "M", "樂"), + (0xF95D, "M", "諾"), + (0xF95E, "M", "丹"), + (0xF95F, "M", "寧"), + (0xF960, "M", "怒"), + (0xF961, "M", "率"), + (0xF962, "M", "異"), + (0xF963, "M", "北"), + (0xF964, "M", "磻"), + (0xF965, "M", "便"), + (0xF966, "M", "復"), + (0xF967, "M", "不"), + (0xF968, "M", "泌"), + (0xF969, "M", "數"), + (0xF96A, "M", "索"), + (0xF96B, "M", "參"), + (0xF96C, "M", "塞"), + (0xF96D, "M", "省"), + (0xF96E, "M", "葉"), + (0xF96F, "M", "說"), + (0xF970, "M", "殺"), + (0xF971, "M", "辰"), + (0xF972, "M", "沈"), + (0xF973, "M", "拾"), + (0xF974, "M", "若"), + (0xF975, "M", "掠"), + (0xF976, "M", "略"), + (0xF977, "M", "亮"), + (0xF978, "M", "兩"), + (0xF979, "M", "凉"), + (0xF97A, "M", "梁"), + (0xF97B, "M", "糧"), + (0xF97C, "M", "良"), + (0xF97D, "M", "諒"), + (0xF97E, "M", "量"), + (0xF97F, "M", "勵"), + (0xF980, "M", "呂"), + (0xF981, "M", "女"), + (0xF982, "M", "廬"), + (0xF983, "M", "旅"), + (0xF984, "M", "濾"), + (0xF985, "M", "礪"), + (0xF986, "M", "閭"), + ] + + +def _seg_41() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xF987, "M", "驪"), + (0xF988, "M", "麗"), + (0xF989, "M", "黎"), + (0xF98A, "M", "力"), + (0xF98B, "M", "曆"), + (0xF98C, "M", "歷"), + (0xF98D, "M", "轢"), + (0xF98E, "M", "年"), + (0xF98F, "M", "憐"), + (0xF990, "M", "戀"), + (0xF991, "M", "撚"), + (0xF992, "M", "漣"), + (0xF993, "M", "煉"), + (0xF994, "M", "璉"), + (0xF995, "M", "秊"), + (0xF996, "M", "練"), + (0xF997, "M", "聯"), + (0xF998, "M", "輦"), + (0xF999, "M", "蓮"), + (0xF99A, "M", "連"), + (0xF99B, "M", "鍊"), + (0xF99C, "M", "列"), + (0xF99D, "M", "劣"), + (0xF99E, "M", "咽"), + (0xF99F, "M", "烈"), + (0xF9A0, "M", "裂"), + (0xF9A1, "M", "說"), + (0xF9A2, "M", "廉"), + (0xF9A3, "M", "念"), + (0xF9A4, "M", "捻"), + (0xF9A5, "M", "殮"), + (0xF9A6, "M", "簾"), + (0xF9A7, "M", "獵"), + (0xF9A8, "M", "令"), + (0xF9A9, "M", "囹"), + (0xF9AA, "M", "寧"), + (0xF9AB, "M", "嶺"), + (0xF9AC, "M", "怜"), + (0xF9AD, "M", "玲"), + (0xF9AE, "M", "瑩"), + (0xF9AF, "M", "羚"), + (0xF9B0, "M", "聆"), + (0xF9B1, "M", "鈴"), + (0xF9B2, "M", "零"), + (0xF9B3, "M", "靈"), + (0xF9B4, "M", "領"), + (0xF9B5, "M", "例"), + (0xF9B6, "M", "禮"), + (0xF9B7, "M", "醴"), + (0xF9B8, "M", "隸"), + (0xF9B9, "M", "惡"), + (0xF9BA, "M", "了"), + (0xF9BB, "M", "僚"), + (0xF9BC, "M", "寮"), + (0xF9BD, "M", "尿"), + (0xF9BE, "M", "料"), + (0xF9BF, "M", "樂"), + (0xF9C0, "M", "燎"), + (0xF9C1, "M", "療"), + (0xF9C2, "M", "蓼"), + (0xF9C3, "M", "遼"), + (0xF9C4, "M", "龍"), + (0xF9C5, "M", "暈"), + (0xF9C6, "M", "阮"), + (0xF9C7, "M", "劉"), + (0xF9C8, "M", "杻"), + (0xF9C9, "M", "柳"), + (0xF9CA, "M", "流"), + (0xF9CB, "M", "溜"), + (0xF9CC, "M", "琉"), + (0xF9CD, "M", "留"), + (0xF9CE, "M", "硫"), + (0xF9CF, "M", "紐"), + (0xF9D0, "M", "類"), + (0xF9D1, "M", "六"), + (0xF9D2, "M", "戮"), + (0xF9D3, "M", "陸"), + (0xF9D4, "M", "倫"), + (0xF9D5, "M", "崙"), + (0xF9D6, "M", "淪"), + (0xF9D7, "M", "輪"), + (0xF9D8, "M", "律"), + (0xF9D9, "M", "慄"), + (0xF9DA, "M", "栗"), + (0xF9DB, "M", "率"), + (0xF9DC, "M", "隆"), + (0xF9DD, "M", "利"), + (0xF9DE, "M", "吏"), + (0xF9DF, "M", "履"), + (0xF9E0, "M", "易"), + (0xF9E1, "M", "李"), + (0xF9E2, "M", "梨"), + (0xF9E3, "M", "泥"), + (0xF9E4, "M", "理"), + (0xF9E5, "M", "痢"), + (0xF9E6, "M", "罹"), + (0xF9E7, "M", "裏"), + (0xF9E8, "M", "裡"), + (0xF9E9, "M", "里"), + (0xF9EA, "M", "離"), + ] + + +def _seg_42() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xF9EB, "M", "匿"), + (0xF9EC, "M", "溺"), + (0xF9ED, "M", "吝"), + (0xF9EE, "M", "燐"), + (0xF9EF, "M", "璘"), + (0xF9F0, "M", "藺"), + (0xF9F1, "M", "隣"), + (0xF9F2, "M", "鱗"), + (0xF9F3, "M", "麟"), + (0xF9F4, "M", "林"), + (0xF9F5, "M", "淋"), + (0xF9F6, "M", "臨"), + (0xF9F7, "M", "立"), + (0xF9F8, "M", "笠"), + (0xF9F9, "M", "粒"), + (0xF9FA, "M", "狀"), + (0xF9FB, "M", "炙"), + (0xF9FC, "M", "識"), + (0xF9FD, "M", "什"), + (0xF9FE, "M", "茶"), + (0xF9FF, "M", "刺"), + (0xFA00, "M", "切"), + (0xFA01, "M", "度"), + (0xFA02, "M", "拓"), + (0xFA03, "M", "糖"), + (0xFA04, "M", "宅"), + (0xFA05, "M", "洞"), + (0xFA06, "M", "暴"), + (0xFA07, "M", "輻"), + (0xFA08, "M", "行"), + (0xFA09, "M", "降"), + (0xFA0A, "M", "見"), + (0xFA0B, "M", "廓"), + (0xFA0C, "M", "兀"), + (0xFA0D, "M", "嗀"), + (0xFA0E, "V"), + (0xFA10, "M", "塚"), + (0xFA11, "V"), + (0xFA12, "M", "晴"), + (0xFA13, "V"), + (0xFA15, "M", "凞"), + (0xFA16, "M", "猪"), + (0xFA17, "M", "益"), + (0xFA18, "M", "礼"), + (0xFA19, "M", "神"), + (0xFA1A, "M", "祥"), + (0xFA1B, "M", "福"), + (0xFA1C, "M", "靖"), + (0xFA1D, "M", "精"), + (0xFA1E, "M", "羽"), + (0xFA1F, "V"), + (0xFA20, "M", "蘒"), + (0xFA21, "V"), + (0xFA22, "M", "諸"), + (0xFA23, "V"), + (0xFA25, "M", "逸"), + (0xFA26, "M", "都"), + (0xFA27, "V"), + (0xFA2A, "M", "飯"), + (0xFA2B, "M", "飼"), + (0xFA2C, "M", "館"), + (0xFA2D, "M", "鶴"), + (0xFA2E, "M", "郞"), + (0xFA2F, "M", "隷"), + (0xFA30, "M", "侮"), + (0xFA31, "M", "僧"), + (0xFA32, "M", "免"), + (0xFA33, "M", "勉"), + (0xFA34, "M", "勤"), + (0xFA35, "M", "卑"), + (0xFA36, "M", "喝"), + (0xFA37, "M", "嘆"), + (0xFA38, "M", "器"), + (0xFA39, "M", "塀"), + (0xFA3A, "M", "墨"), + (0xFA3B, "M", "層"), + (0xFA3C, "M", "屮"), + (0xFA3D, "M", "悔"), + (0xFA3E, "M", "慨"), + (0xFA3F, "M", "憎"), + (0xFA40, "M", "懲"), + (0xFA41, "M", "敏"), + (0xFA42, "M", "既"), + (0xFA43, "M", "暑"), + (0xFA44, "M", "梅"), + (0xFA45, "M", "海"), + (0xFA46, "M", "渚"), + (0xFA47, "M", "漢"), + (0xFA48, "M", "煮"), + (0xFA49, "M", "爫"), + (0xFA4A, "M", "琢"), + (0xFA4B, "M", "碑"), + (0xFA4C, "M", "社"), + (0xFA4D, "M", "祉"), + (0xFA4E, "M", "祈"), + (0xFA4F, "M", "祐"), + (0xFA50, "M", "祖"), + (0xFA51, "M", "祝"), + (0xFA52, "M", "禍"), + (0xFA53, "M", "禎"), + ] + + +def _seg_43() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFA54, "M", "穀"), + (0xFA55, "M", "突"), + (0xFA56, "M", "節"), + (0xFA57, "M", "練"), + (0xFA58, "M", "縉"), + (0xFA59, "M", "繁"), + (0xFA5A, "M", "署"), + (0xFA5B, "M", "者"), + (0xFA5C, "M", "臭"), + (0xFA5D, "M", "艹"), + (0xFA5F, "M", "著"), + (0xFA60, "M", "褐"), + (0xFA61, "M", "視"), + (0xFA62, "M", "謁"), + (0xFA63, "M", "謹"), + (0xFA64, "M", "賓"), + (0xFA65, "M", "贈"), + (0xFA66, "M", "辶"), + (0xFA67, "M", "逸"), + (0xFA68, "M", "難"), + (0xFA69, "M", "響"), + (0xFA6A, "M", "頻"), + (0xFA6B, "M", "恵"), + (0xFA6C, "M", "𤋮"), + (0xFA6D, "M", "舘"), + (0xFA6E, "X"), + (0xFA70, "M", "並"), + (0xFA71, "M", "况"), + (0xFA72, "M", "全"), + (0xFA73, "M", "侀"), + (0xFA74, "M", "充"), + (0xFA75, "M", "冀"), + (0xFA76, "M", "勇"), + (0xFA77, "M", "勺"), + (0xFA78, "M", "喝"), + (0xFA79, "M", "啕"), + (0xFA7A, "M", "喙"), + (0xFA7B, "M", "嗢"), + (0xFA7C, "M", "塚"), + (0xFA7D, "M", "墳"), + (0xFA7E, "M", "奄"), + (0xFA7F, "M", "奔"), + (0xFA80, "M", "婢"), + (0xFA81, "M", "嬨"), + (0xFA82, "M", "廒"), + (0xFA83, "M", "廙"), + (0xFA84, "M", "彩"), + (0xFA85, "M", "徭"), + (0xFA86, "M", "惘"), + (0xFA87, "M", "慎"), + (0xFA88, "M", "愈"), + (0xFA89, "M", "憎"), + (0xFA8A, "M", "慠"), + (0xFA8B, "M", "懲"), + (0xFA8C, "M", "戴"), + (0xFA8D, "M", "揄"), + (0xFA8E, "M", "搜"), + (0xFA8F, "M", "摒"), + (0xFA90, "M", "敖"), + (0xFA91, "M", "晴"), + (0xFA92, "M", "朗"), + (0xFA93, "M", "望"), + (0xFA94, "M", "杖"), + (0xFA95, "M", "歹"), + (0xFA96, "M", "殺"), + (0xFA97, "M", "流"), + (0xFA98, "M", "滛"), + (0xFA99, "M", "滋"), + (0xFA9A, "M", "漢"), + (0xFA9B, "M", "瀞"), + (0xFA9C, "M", "煮"), + (0xFA9D, "M", "瞧"), + (0xFA9E, "M", "爵"), + (0xFA9F, "M", "犯"), + (0xFAA0, "M", "猪"), + (0xFAA1, "M", "瑱"), + (0xFAA2, "M", "甆"), + (0xFAA3, "M", "画"), + (0xFAA4, "M", "瘝"), + (0xFAA5, "M", "瘟"), + (0xFAA6, "M", "益"), + (0xFAA7, "M", "盛"), + (0xFAA8, "M", "直"), + (0xFAA9, "M", "睊"), + (0xFAAA, "M", "着"), + (0xFAAB, "M", "磌"), + (0xFAAC, "M", "窱"), + (0xFAAD, "M", "節"), + (0xFAAE, "M", "类"), + (0xFAAF, "M", "絛"), + (0xFAB0, "M", "練"), + (0xFAB1, "M", "缾"), + (0xFAB2, "M", "者"), + (0xFAB3, "M", "荒"), + (0xFAB4, "M", "華"), + (0xFAB5, "M", "蝹"), + (0xFAB6, "M", "襁"), + (0xFAB7, "M", "覆"), + (0xFAB8, "M", "視"), + (0xFAB9, "M", "調"), + ] + + +def _seg_44() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFABA, "M", "諸"), + (0xFABB, "M", "請"), + (0xFABC, "M", "謁"), + (0xFABD, "M", "諾"), + (0xFABE, "M", "諭"), + (0xFABF, "M", "謹"), + (0xFAC0, "M", "變"), + (0xFAC1, "M", "贈"), + (0xFAC2, "M", "輸"), + (0xFAC3, "M", "遲"), + (0xFAC4, "M", "醙"), + (0xFAC5, "M", "鉶"), + (0xFAC6, "M", "陼"), + (0xFAC7, "M", "難"), + (0xFAC8, "M", "靖"), + (0xFAC9, "M", "韛"), + (0xFACA, "M", "響"), + (0xFACB, "M", "頋"), + (0xFACC, "M", "頻"), + (0xFACD, "M", "鬒"), + (0xFACE, "M", "龜"), + (0xFACF, "M", "𢡊"), + (0xFAD0, "M", "𢡄"), + (0xFAD1, "M", "𣏕"), + (0xFAD2, "M", "㮝"), + (0xFAD3, "M", "䀘"), + (0xFAD4, "M", "䀹"), + (0xFAD5, "M", "𥉉"), + (0xFAD6, "M", "𥳐"), + (0xFAD7, "M", "𧻓"), + (0xFAD8, "M", "齃"), + (0xFAD9, "M", "龎"), + (0xFADA, "X"), + (0xFB00, "M", "ff"), + (0xFB01, "M", "fi"), + (0xFB02, "M", "fl"), + (0xFB03, "M", "ffi"), + (0xFB04, "M", "ffl"), + (0xFB05, "M", "st"), + (0xFB07, "X"), + (0xFB13, "M", "մն"), + (0xFB14, "M", "մե"), + (0xFB15, "M", "մի"), + (0xFB16, "M", "վն"), + (0xFB17, "M", "մխ"), + (0xFB18, "X"), + (0xFB1D, "M", "יִ"), + (0xFB1E, "V"), + (0xFB1F, "M", "ײַ"), + (0xFB20, "M", "ע"), + (0xFB21, "M", "א"), + (0xFB22, "M", "ד"), + (0xFB23, "M", "ה"), + (0xFB24, "M", "כ"), + (0xFB25, "M", "ל"), + (0xFB26, "M", "ם"), + (0xFB27, "M", "ר"), + (0xFB28, "M", "ת"), + (0xFB29, "M", "+"), + (0xFB2A, "M", "שׁ"), + (0xFB2B, "M", "שׂ"), + (0xFB2C, "M", "שּׁ"), + (0xFB2D, "M", "שּׂ"), + (0xFB2E, "M", "אַ"), + (0xFB2F, "M", "אָ"), + (0xFB30, "M", "אּ"), + (0xFB31, "M", "בּ"), + (0xFB32, "M", "גּ"), + (0xFB33, "M", "דּ"), + (0xFB34, "M", "הּ"), + (0xFB35, "M", "וּ"), + (0xFB36, "M", "זּ"), + (0xFB37, "X"), + (0xFB38, "M", "טּ"), + (0xFB39, "M", "יּ"), + (0xFB3A, "M", "ךּ"), + (0xFB3B, "M", "כּ"), + (0xFB3C, "M", "לּ"), + (0xFB3D, "X"), + (0xFB3E, "M", "מּ"), + (0xFB3F, "X"), + (0xFB40, "M", "נּ"), + (0xFB41, "M", "סּ"), + (0xFB42, "X"), + (0xFB43, "M", "ףּ"), + (0xFB44, "M", "פּ"), + (0xFB45, "X"), + (0xFB46, "M", "צּ"), + (0xFB47, "M", "קּ"), + (0xFB48, "M", "רּ"), + (0xFB49, "M", "שּ"), + (0xFB4A, "M", "תּ"), + (0xFB4B, "M", "וֹ"), + (0xFB4C, "M", "בֿ"), + (0xFB4D, "M", "כֿ"), + (0xFB4E, "M", "פֿ"), + (0xFB4F, "M", "אל"), + (0xFB50, "M", "ٱ"), + (0xFB52, "M", "ٻ"), + (0xFB56, "M", "پ"), + ] + + +def _seg_45() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFB5A, "M", "ڀ"), + (0xFB5E, "M", "ٺ"), + (0xFB62, "M", "ٿ"), + (0xFB66, "M", "ٹ"), + (0xFB6A, "M", "ڤ"), + (0xFB6E, "M", "ڦ"), + (0xFB72, "M", "ڄ"), + (0xFB76, "M", "ڃ"), + (0xFB7A, "M", "چ"), + (0xFB7E, "M", "ڇ"), + (0xFB82, "M", "ڍ"), + (0xFB84, "M", "ڌ"), + (0xFB86, "M", "ڎ"), + (0xFB88, "M", "ڈ"), + (0xFB8A, "M", "ژ"), + (0xFB8C, "M", "ڑ"), + (0xFB8E, "M", "ک"), + (0xFB92, "M", "گ"), + (0xFB96, "M", "ڳ"), + (0xFB9A, "M", "ڱ"), + (0xFB9E, "M", "ں"), + (0xFBA0, "M", "ڻ"), + (0xFBA4, "M", "ۀ"), + (0xFBA6, "M", "ہ"), + (0xFBAA, "M", "ھ"), + (0xFBAE, "M", "ے"), + (0xFBB0, "M", "ۓ"), + (0xFBB2, "V"), + (0xFBC3, "X"), + (0xFBD3, "M", "ڭ"), + (0xFBD7, "M", "ۇ"), + (0xFBD9, "M", "ۆ"), + (0xFBDB, "M", "ۈ"), + (0xFBDD, "M", "ۇٴ"), + (0xFBDE, "M", "ۋ"), + (0xFBE0, "M", "ۅ"), + (0xFBE2, "M", "ۉ"), + (0xFBE4, "M", "ې"), + (0xFBE8, "M", "ى"), + (0xFBEA, "M", "ئا"), + (0xFBEC, "M", "ئە"), + (0xFBEE, "M", "ئو"), + (0xFBF0, "M", "ئۇ"), + (0xFBF2, "M", "ئۆ"), + (0xFBF4, "M", "ئۈ"), + (0xFBF6, "M", "ئې"), + (0xFBF9, "M", "ئى"), + (0xFBFC, "M", "ی"), + (0xFC00, "M", "ئج"), + (0xFC01, "M", "ئح"), + (0xFC02, "M", "ئم"), + (0xFC03, "M", "ئى"), + (0xFC04, "M", "ئي"), + (0xFC05, "M", "بج"), + (0xFC06, "M", "بح"), + (0xFC07, "M", "بخ"), + (0xFC08, "M", "بم"), + (0xFC09, "M", "بى"), + (0xFC0A, "M", "بي"), + (0xFC0B, "M", "تج"), + (0xFC0C, "M", "تح"), + (0xFC0D, "M", "تخ"), + (0xFC0E, "M", "تم"), + (0xFC0F, "M", "تى"), + (0xFC10, "M", "تي"), + (0xFC11, "M", "ثج"), + (0xFC12, "M", "ثم"), + (0xFC13, "M", "ثى"), + (0xFC14, "M", "ثي"), + (0xFC15, "M", "جح"), + (0xFC16, "M", "جم"), + (0xFC17, "M", "حج"), + (0xFC18, "M", "حم"), + (0xFC19, "M", "خج"), + (0xFC1A, "M", "خح"), + (0xFC1B, "M", "خم"), + (0xFC1C, "M", "سج"), + (0xFC1D, "M", "سح"), + (0xFC1E, "M", "سخ"), + (0xFC1F, "M", "سم"), + (0xFC20, "M", "صح"), + (0xFC21, "M", "صم"), + (0xFC22, "M", "ضج"), + (0xFC23, "M", "ضح"), + (0xFC24, "M", "ضخ"), + (0xFC25, "M", "ضم"), + (0xFC26, "M", "طح"), + (0xFC27, "M", "طم"), + (0xFC28, "M", "ظم"), + (0xFC29, "M", "عج"), + (0xFC2A, "M", "عم"), + (0xFC2B, "M", "غج"), + (0xFC2C, "M", "غم"), + (0xFC2D, "M", "فج"), + (0xFC2E, "M", "فح"), + (0xFC2F, "M", "فخ"), + (0xFC30, "M", "فم"), + (0xFC31, "M", "فى"), + (0xFC32, "M", "في"), + (0xFC33, "M", "قح"), + ] + + +def _seg_46() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFC34, "M", "قم"), + (0xFC35, "M", "قى"), + (0xFC36, "M", "قي"), + (0xFC37, "M", "كا"), + (0xFC38, "M", "كج"), + (0xFC39, "M", "كح"), + (0xFC3A, "M", "كخ"), + (0xFC3B, "M", "كل"), + (0xFC3C, "M", "كم"), + (0xFC3D, "M", "كى"), + (0xFC3E, "M", "كي"), + (0xFC3F, "M", "لج"), + (0xFC40, "M", "لح"), + (0xFC41, "M", "لخ"), + (0xFC42, "M", "لم"), + (0xFC43, "M", "لى"), + (0xFC44, "M", "لي"), + (0xFC45, "M", "مج"), + (0xFC46, "M", "مح"), + (0xFC47, "M", "مخ"), + (0xFC48, "M", "مم"), + (0xFC49, "M", "مى"), + (0xFC4A, "M", "مي"), + (0xFC4B, "M", "نج"), + (0xFC4C, "M", "نح"), + (0xFC4D, "M", "نخ"), + (0xFC4E, "M", "نم"), + (0xFC4F, "M", "نى"), + (0xFC50, "M", "ني"), + (0xFC51, "M", "هج"), + (0xFC52, "M", "هم"), + (0xFC53, "M", "هى"), + (0xFC54, "M", "هي"), + (0xFC55, "M", "يج"), + (0xFC56, "M", "يح"), + (0xFC57, "M", "يخ"), + (0xFC58, "M", "يم"), + (0xFC59, "M", "يى"), + (0xFC5A, "M", "يي"), + (0xFC5B, "M", "ذٰ"), + (0xFC5C, "M", "رٰ"), + (0xFC5D, "M", "ىٰ"), + (0xFC5E, "M", " ٌّ"), + (0xFC5F, "M", " ٍّ"), + (0xFC60, "M", " َّ"), + (0xFC61, "M", " ُّ"), + (0xFC62, "M", " ِّ"), + (0xFC63, "M", " ّٰ"), + (0xFC64, "M", "ئر"), + (0xFC65, "M", "ئز"), + (0xFC66, "M", "ئم"), + (0xFC67, "M", "ئن"), + (0xFC68, "M", "ئى"), + (0xFC69, "M", "ئي"), + (0xFC6A, "M", "بر"), + (0xFC6B, "M", "بز"), + (0xFC6C, "M", "بم"), + (0xFC6D, "M", "بن"), + (0xFC6E, "M", "بى"), + (0xFC6F, "M", "بي"), + (0xFC70, "M", "تر"), + (0xFC71, "M", "تز"), + (0xFC72, "M", "تم"), + (0xFC73, "M", "تن"), + (0xFC74, "M", "تى"), + (0xFC75, "M", "تي"), + (0xFC76, "M", "ثر"), + (0xFC77, "M", "ثز"), + (0xFC78, "M", "ثم"), + (0xFC79, "M", "ثن"), + (0xFC7A, "M", "ثى"), + (0xFC7B, "M", "ثي"), + (0xFC7C, "M", "فى"), + (0xFC7D, "M", "في"), + (0xFC7E, "M", "قى"), + (0xFC7F, "M", "قي"), + (0xFC80, "M", "كا"), + (0xFC81, "M", "كل"), + (0xFC82, "M", "كم"), + (0xFC83, "M", "كى"), + (0xFC84, "M", "كي"), + (0xFC85, "M", "لم"), + (0xFC86, "M", "لى"), + (0xFC87, "M", "لي"), + (0xFC88, "M", "ما"), + (0xFC89, "M", "مم"), + (0xFC8A, "M", "نر"), + (0xFC8B, "M", "نز"), + (0xFC8C, "M", "نم"), + (0xFC8D, "M", "نن"), + (0xFC8E, "M", "نى"), + (0xFC8F, "M", "ني"), + (0xFC90, "M", "ىٰ"), + (0xFC91, "M", "ير"), + (0xFC92, "M", "يز"), + (0xFC93, "M", "يم"), + (0xFC94, "M", "ين"), + (0xFC95, "M", "يى"), + (0xFC96, "M", "يي"), + (0xFC97, "M", "ئج"), + ] + + +def _seg_47() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFC98, "M", "ئح"), + (0xFC99, "M", "ئخ"), + (0xFC9A, "M", "ئم"), + (0xFC9B, "M", "ئه"), + (0xFC9C, "M", "بج"), + (0xFC9D, "M", "بح"), + (0xFC9E, "M", "بخ"), + (0xFC9F, "M", "بم"), + (0xFCA0, "M", "به"), + (0xFCA1, "M", "تج"), + (0xFCA2, "M", "تح"), + (0xFCA3, "M", "تخ"), + (0xFCA4, "M", "تم"), + (0xFCA5, "M", "ته"), + (0xFCA6, "M", "ثم"), + (0xFCA7, "M", "جح"), + (0xFCA8, "M", "جم"), + (0xFCA9, "M", "حج"), + (0xFCAA, "M", "حم"), + (0xFCAB, "M", "خج"), + (0xFCAC, "M", "خم"), + (0xFCAD, "M", "سج"), + (0xFCAE, "M", "سح"), + (0xFCAF, "M", "سخ"), + (0xFCB0, "M", "سم"), + (0xFCB1, "M", "صح"), + (0xFCB2, "M", "صخ"), + (0xFCB3, "M", "صم"), + (0xFCB4, "M", "ضج"), + (0xFCB5, "M", "ضح"), + (0xFCB6, "M", "ضخ"), + (0xFCB7, "M", "ضم"), + (0xFCB8, "M", "طح"), + (0xFCB9, "M", "ظم"), + (0xFCBA, "M", "عج"), + (0xFCBB, "M", "عم"), + (0xFCBC, "M", "غج"), + (0xFCBD, "M", "غم"), + (0xFCBE, "M", "فج"), + (0xFCBF, "M", "فح"), + (0xFCC0, "M", "فخ"), + (0xFCC1, "M", "فم"), + (0xFCC2, "M", "قح"), + (0xFCC3, "M", "قم"), + (0xFCC4, "M", "كج"), + (0xFCC5, "M", "كح"), + (0xFCC6, "M", "كخ"), + (0xFCC7, "M", "كل"), + (0xFCC8, "M", "كم"), + (0xFCC9, "M", "لج"), + (0xFCCA, "M", "لح"), + (0xFCCB, "M", "لخ"), + (0xFCCC, "M", "لم"), + (0xFCCD, "M", "له"), + (0xFCCE, "M", "مج"), + (0xFCCF, "M", "مح"), + (0xFCD0, "M", "مخ"), + (0xFCD1, "M", "مم"), + (0xFCD2, "M", "نج"), + (0xFCD3, "M", "نح"), + (0xFCD4, "M", "نخ"), + (0xFCD5, "M", "نم"), + (0xFCD6, "M", "نه"), + (0xFCD7, "M", "هج"), + (0xFCD8, "M", "هم"), + (0xFCD9, "M", "هٰ"), + (0xFCDA, "M", "يج"), + (0xFCDB, "M", "يح"), + (0xFCDC, "M", "يخ"), + (0xFCDD, "M", "يم"), + (0xFCDE, "M", "يه"), + (0xFCDF, "M", "ئم"), + (0xFCE0, "M", "ئه"), + (0xFCE1, "M", "بم"), + (0xFCE2, "M", "به"), + (0xFCE3, "M", "تم"), + (0xFCE4, "M", "ته"), + (0xFCE5, "M", "ثم"), + (0xFCE6, "M", "ثه"), + (0xFCE7, "M", "سم"), + (0xFCE8, "M", "سه"), + (0xFCE9, "M", "شم"), + (0xFCEA, "M", "شه"), + (0xFCEB, "M", "كل"), + (0xFCEC, "M", "كم"), + (0xFCED, "M", "لم"), + (0xFCEE, "M", "نم"), + (0xFCEF, "M", "نه"), + (0xFCF0, "M", "يم"), + (0xFCF1, "M", "يه"), + (0xFCF2, "M", "ـَّ"), + (0xFCF3, "M", "ـُّ"), + (0xFCF4, "M", "ـِّ"), + (0xFCF5, "M", "طى"), + (0xFCF6, "M", "طي"), + (0xFCF7, "M", "عى"), + (0xFCF8, "M", "عي"), + (0xFCF9, "M", "غى"), + (0xFCFA, "M", "غي"), + (0xFCFB, "M", "سى"), + ] + + +def _seg_48() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFCFC, "M", "سي"), + (0xFCFD, "M", "شى"), + (0xFCFE, "M", "شي"), + (0xFCFF, "M", "حى"), + (0xFD00, "M", "حي"), + (0xFD01, "M", "جى"), + (0xFD02, "M", "جي"), + (0xFD03, "M", "خى"), + (0xFD04, "M", "خي"), + (0xFD05, "M", "صى"), + (0xFD06, "M", "صي"), + (0xFD07, "M", "ضى"), + (0xFD08, "M", "ضي"), + (0xFD09, "M", "شج"), + (0xFD0A, "M", "شح"), + (0xFD0B, "M", "شخ"), + (0xFD0C, "M", "شم"), + (0xFD0D, "M", "شر"), + (0xFD0E, "M", "سر"), + (0xFD0F, "M", "صر"), + (0xFD10, "M", "ضر"), + (0xFD11, "M", "طى"), + (0xFD12, "M", "طي"), + (0xFD13, "M", "عى"), + (0xFD14, "M", "عي"), + (0xFD15, "M", "غى"), + (0xFD16, "M", "غي"), + (0xFD17, "M", "سى"), + (0xFD18, "M", "سي"), + (0xFD19, "M", "شى"), + (0xFD1A, "M", "شي"), + (0xFD1B, "M", "حى"), + (0xFD1C, "M", "حي"), + (0xFD1D, "M", "جى"), + (0xFD1E, "M", "جي"), + (0xFD1F, "M", "خى"), + (0xFD20, "M", "خي"), + (0xFD21, "M", "صى"), + (0xFD22, "M", "صي"), + (0xFD23, "M", "ضى"), + (0xFD24, "M", "ضي"), + (0xFD25, "M", "شج"), + (0xFD26, "M", "شح"), + (0xFD27, "M", "شخ"), + (0xFD28, "M", "شم"), + (0xFD29, "M", "شر"), + (0xFD2A, "M", "سر"), + (0xFD2B, "M", "صر"), + (0xFD2C, "M", "ضر"), + (0xFD2D, "M", "شج"), + (0xFD2E, "M", "شح"), + (0xFD2F, "M", "شخ"), + (0xFD30, "M", "شم"), + (0xFD31, "M", "سه"), + (0xFD32, "M", "شه"), + (0xFD33, "M", "طم"), + (0xFD34, "M", "سج"), + (0xFD35, "M", "سح"), + (0xFD36, "M", "سخ"), + (0xFD37, "M", "شج"), + (0xFD38, "M", "شح"), + (0xFD39, "M", "شخ"), + (0xFD3A, "M", "طم"), + (0xFD3B, "M", "ظم"), + (0xFD3C, "M", "اً"), + (0xFD3E, "V"), + (0xFD50, "M", "تجم"), + (0xFD51, "M", "تحج"), + (0xFD53, "M", "تحم"), + (0xFD54, "M", "تخم"), + (0xFD55, "M", "تمج"), + (0xFD56, "M", "تمح"), + (0xFD57, "M", "تمخ"), + (0xFD58, "M", "جمح"), + (0xFD5A, "M", "حمي"), + (0xFD5B, "M", "حمى"), + (0xFD5C, "M", "سحج"), + (0xFD5D, "M", "سجح"), + (0xFD5E, "M", "سجى"), + (0xFD5F, "M", "سمح"), + (0xFD61, "M", "سمج"), + (0xFD62, "M", "سمم"), + (0xFD64, "M", "صحح"), + (0xFD66, "M", "صمم"), + (0xFD67, "M", "شحم"), + (0xFD69, "M", "شجي"), + (0xFD6A, "M", "شمخ"), + (0xFD6C, "M", "شمم"), + (0xFD6E, "M", "ضحى"), + (0xFD6F, "M", "ضخم"), + (0xFD71, "M", "طمح"), + (0xFD73, "M", "طمم"), + (0xFD74, "M", "طمي"), + (0xFD75, "M", "عجم"), + (0xFD76, "M", "عمم"), + (0xFD78, "M", "عمى"), + (0xFD79, "M", "غمم"), + (0xFD7A, "M", "غمي"), + (0xFD7B, "M", "غمى"), + (0xFD7C, "M", "فخم"), + ] + + +def _seg_49() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFD7E, "M", "قمح"), + (0xFD7F, "M", "قمم"), + (0xFD80, "M", "لحم"), + (0xFD81, "M", "لحي"), + (0xFD82, "M", "لحى"), + (0xFD83, "M", "لجج"), + (0xFD85, "M", "لخم"), + (0xFD87, "M", "لمح"), + (0xFD89, "M", "محج"), + (0xFD8A, "M", "محم"), + (0xFD8B, "M", "محي"), + (0xFD8C, "M", "مجح"), + (0xFD8D, "M", "مجم"), + (0xFD8E, "M", "مخج"), + (0xFD8F, "M", "مخم"), + (0xFD90, "X"), + (0xFD92, "M", "مجخ"), + (0xFD93, "M", "همج"), + (0xFD94, "M", "همم"), + (0xFD95, "M", "نحم"), + (0xFD96, "M", "نحى"), + (0xFD97, "M", "نجم"), + (0xFD99, "M", "نجى"), + (0xFD9A, "M", "نمي"), + (0xFD9B, "M", "نمى"), + (0xFD9C, "M", "يمم"), + (0xFD9E, "M", "بخي"), + (0xFD9F, "M", "تجي"), + (0xFDA0, "M", "تجى"), + (0xFDA1, "M", "تخي"), + (0xFDA2, "M", "تخى"), + (0xFDA3, "M", "تمي"), + (0xFDA4, "M", "تمى"), + (0xFDA5, "M", "جمي"), + (0xFDA6, "M", "جحى"), + (0xFDA7, "M", "جمى"), + (0xFDA8, "M", "سخى"), + (0xFDA9, "M", "صحي"), + (0xFDAA, "M", "شحي"), + (0xFDAB, "M", "ضحي"), + (0xFDAC, "M", "لجي"), + (0xFDAD, "M", "لمي"), + (0xFDAE, "M", "يحي"), + (0xFDAF, "M", "يجي"), + (0xFDB0, "M", "يمي"), + (0xFDB1, "M", "ممي"), + (0xFDB2, "M", "قمي"), + (0xFDB3, "M", "نحي"), + (0xFDB4, "M", "قمح"), + (0xFDB5, "M", "لحم"), + (0xFDB6, "M", "عمي"), + (0xFDB7, "M", "كمي"), + (0xFDB8, "M", "نجح"), + (0xFDB9, "M", "مخي"), + (0xFDBA, "M", "لجم"), + (0xFDBB, "M", "كمم"), + (0xFDBC, "M", "لجم"), + (0xFDBD, "M", "نجح"), + (0xFDBE, "M", "جحي"), + (0xFDBF, "M", "حجي"), + (0xFDC0, "M", "مجي"), + (0xFDC1, "M", "فمي"), + (0xFDC2, "M", "بحي"), + (0xFDC3, "M", "كمم"), + (0xFDC4, "M", "عجم"), + (0xFDC5, "M", "صمم"), + (0xFDC6, "M", "سخي"), + (0xFDC7, "M", "نجي"), + (0xFDC8, "X"), + (0xFDCF, "V"), + (0xFDD0, "X"), + (0xFDF0, "M", "صلے"), + (0xFDF1, "M", "قلے"), + (0xFDF2, "M", "الله"), + (0xFDF3, "M", "اكبر"), + (0xFDF4, "M", "محمد"), + (0xFDF5, "M", "صلعم"), + (0xFDF6, "M", "رسول"), + (0xFDF7, "M", "عليه"), + (0xFDF8, "M", "وسلم"), + (0xFDF9, "M", "صلى"), + (0xFDFA, "M", "صلى الله عليه وسلم"), + (0xFDFB, "M", "جل جلاله"), + (0xFDFC, "M", "ریال"), + (0xFDFD, "V"), + (0xFE00, "I"), + (0xFE10, "M", ","), + (0xFE11, "M", "、"), + (0xFE12, "X"), + (0xFE13, "M", ":"), + (0xFE14, "M", ";"), + (0xFE15, "M", "!"), + (0xFE16, "M", "?"), + (0xFE17, "M", "〖"), + (0xFE18, "M", "〗"), + (0xFE19, "X"), + (0xFE20, "V"), + (0xFE30, "X"), + (0xFE31, "M", "—"), + (0xFE32, "M", "–"), + ] + + +def _seg_50() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFE33, "M", "_"), + (0xFE35, "M", "("), + (0xFE36, "M", ")"), + (0xFE37, "M", "{"), + (0xFE38, "M", "}"), + (0xFE39, "M", "〔"), + (0xFE3A, "M", "〕"), + (0xFE3B, "M", "【"), + (0xFE3C, "M", "】"), + (0xFE3D, "M", "《"), + (0xFE3E, "M", "》"), + (0xFE3F, "M", "〈"), + (0xFE40, "M", "〉"), + (0xFE41, "M", "「"), + (0xFE42, "M", "」"), + (0xFE43, "M", "『"), + (0xFE44, "M", "』"), + (0xFE45, "V"), + (0xFE47, "M", "["), + (0xFE48, "M", "]"), + (0xFE49, "M", " ̅"), + (0xFE4D, "M", "_"), + (0xFE50, "M", ","), + (0xFE51, "M", "、"), + (0xFE52, "X"), + (0xFE54, "M", ";"), + (0xFE55, "M", ":"), + (0xFE56, "M", "?"), + (0xFE57, "M", "!"), + (0xFE58, "M", "—"), + (0xFE59, "M", "("), + (0xFE5A, "M", ")"), + (0xFE5B, "M", "{"), + (0xFE5C, "M", "}"), + (0xFE5D, "M", "〔"), + (0xFE5E, "M", "〕"), + (0xFE5F, "M", "#"), + (0xFE60, "M", "&"), + (0xFE61, "M", "*"), + (0xFE62, "M", "+"), + (0xFE63, "M", "-"), + (0xFE64, "M", "<"), + (0xFE65, "M", ">"), + (0xFE66, "M", "="), + (0xFE67, "X"), + (0xFE68, "M", "\\"), + (0xFE69, "M", "$"), + (0xFE6A, "M", "%"), + (0xFE6B, "M", "@"), + (0xFE6C, "X"), + (0xFE70, "M", " ً"), + (0xFE71, "M", "ـً"), + (0xFE72, "M", " ٌ"), + (0xFE73, "V"), + (0xFE74, "M", " ٍ"), + (0xFE75, "X"), + (0xFE76, "M", " َ"), + (0xFE77, "M", "ـَ"), + (0xFE78, "M", " ُ"), + (0xFE79, "M", "ـُ"), + (0xFE7A, "M", " ِ"), + (0xFE7B, "M", "ـِ"), + (0xFE7C, "M", " ّ"), + (0xFE7D, "M", "ـّ"), + (0xFE7E, "M", " ْ"), + (0xFE7F, "M", "ـْ"), + (0xFE80, "M", "ء"), + (0xFE81, "M", "آ"), + (0xFE83, "M", "أ"), + (0xFE85, "M", "ؤ"), + (0xFE87, "M", "إ"), + (0xFE89, "M", "ئ"), + (0xFE8D, "M", "ا"), + (0xFE8F, "M", "ب"), + (0xFE93, "M", "ة"), + (0xFE95, "M", "ت"), + (0xFE99, "M", "ث"), + (0xFE9D, "M", "ج"), + (0xFEA1, "M", "ح"), + (0xFEA5, "M", "خ"), + (0xFEA9, "M", "د"), + (0xFEAB, "M", "ذ"), + (0xFEAD, "M", "ر"), + (0xFEAF, "M", "ز"), + (0xFEB1, "M", "س"), + (0xFEB5, "M", "ش"), + (0xFEB9, "M", "ص"), + (0xFEBD, "M", "ض"), + (0xFEC1, "M", "ط"), + (0xFEC5, "M", "ظ"), + (0xFEC9, "M", "ع"), + (0xFECD, "M", "غ"), + (0xFED1, "M", "ف"), + (0xFED5, "M", "ق"), + (0xFED9, "M", "ك"), + (0xFEDD, "M", "ل"), + (0xFEE1, "M", "م"), + (0xFEE5, "M", "ن"), + (0xFEE9, "M", "ه"), + (0xFEED, "M", "و"), + ] + + +def _seg_51() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFEEF, "M", "ى"), + (0xFEF1, "M", "ي"), + (0xFEF5, "M", "لآ"), + (0xFEF7, "M", "لأ"), + (0xFEF9, "M", "لإ"), + (0xFEFB, "M", "لا"), + (0xFEFD, "X"), + (0xFEFF, "I"), + (0xFF00, "X"), + (0xFF01, "M", "!"), + (0xFF02, "M", '"'), + (0xFF03, "M", "#"), + (0xFF04, "M", "$"), + (0xFF05, "M", "%"), + (0xFF06, "M", "&"), + (0xFF07, "M", "'"), + (0xFF08, "M", "("), + (0xFF09, "M", ")"), + (0xFF0A, "M", "*"), + (0xFF0B, "M", "+"), + (0xFF0C, "M", ","), + (0xFF0D, "M", "-"), + (0xFF0E, "M", "."), + (0xFF0F, "M", "/"), + (0xFF10, "M", "0"), + (0xFF11, "M", "1"), + (0xFF12, "M", "2"), + (0xFF13, "M", "3"), + (0xFF14, "M", "4"), + (0xFF15, "M", "5"), + (0xFF16, "M", "6"), + (0xFF17, "M", "7"), + (0xFF18, "M", "8"), + (0xFF19, "M", "9"), + (0xFF1A, "M", ":"), + (0xFF1B, "M", ";"), + (0xFF1C, "M", "<"), + (0xFF1D, "M", "="), + (0xFF1E, "M", ">"), + (0xFF1F, "M", "?"), + (0xFF20, "M", "@"), + (0xFF21, "M", "a"), + (0xFF22, "M", "b"), + (0xFF23, "M", "c"), + (0xFF24, "M", "d"), + (0xFF25, "M", "e"), + (0xFF26, "M", "f"), + (0xFF27, "M", "g"), + (0xFF28, "M", "h"), + (0xFF29, "M", "i"), + (0xFF2A, "M", "j"), + (0xFF2B, "M", "k"), + (0xFF2C, "M", "l"), + (0xFF2D, "M", "m"), + (0xFF2E, "M", "n"), + (0xFF2F, "M", "o"), + (0xFF30, "M", "p"), + (0xFF31, "M", "q"), + (0xFF32, "M", "r"), + (0xFF33, "M", "s"), + (0xFF34, "M", "t"), + (0xFF35, "M", "u"), + (0xFF36, "M", "v"), + (0xFF37, "M", "w"), + (0xFF38, "M", "x"), + (0xFF39, "M", "y"), + (0xFF3A, "M", "z"), + (0xFF3B, "M", "["), + (0xFF3C, "M", "\\"), + (0xFF3D, "M", "]"), + (0xFF3E, "M", "^"), + (0xFF3F, "M", "_"), + (0xFF40, "M", "`"), + (0xFF41, "M", "a"), + (0xFF42, "M", "b"), + (0xFF43, "M", "c"), + (0xFF44, "M", "d"), + (0xFF45, "M", "e"), + (0xFF46, "M", "f"), + (0xFF47, "M", "g"), + (0xFF48, "M", "h"), + (0xFF49, "M", "i"), + (0xFF4A, "M", "j"), + (0xFF4B, "M", "k"), + (0xFF4C, "M", "l"), + (0xFF4D, "M", "m"), + (0xFF4E, "M", "n"), + (0xFF4F, "M", "o"), + (0xFF50, "M", "p"), + (0xFF51, "M", "q"), + (0xFF52, "M", "r"), + (0xFF53, "M", "s"), + (0xFF54, "M", "t"), + (0xFF55, "M", "u"), + (0xFF56, "M", "v"), + (0xFF57, "M", "w"), + (0xFF58, "M", "x"), + (0xFF59, "M", "y"), + (0xFF5A, "M", "z"), + (0xFF5B, "M", "{"), + ] + + +def _seg_52() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFF5C, "M", "|"), + (0xFF5D, "M", "}"), + (0xFF5E, "M", "~"), + (0xFF5F, "M", "⦅"), + (0xFF60, "M", "⦆"), + (0xFF61, "M", "."), + (0xFF62, "M", "「"), + (0xFF63, "M", "」"), + (0xFF64, "M", "、"), + (0xFF65, "M", "・"), + (0xFF66, "M", "ヲ"), + (0xFF67, "M", "ァ"), + (0xFF68, "M", "ィ"), + (0xFF69, "M", "ゥ"), + (0xFF6A, "M", "ェ"), + (0xFF6B, "M", "ォ"), + (0xFF6C, "M", "ャ"), + (0xFF6D, "M", "ュ"), + (0xFF6E, "M", "ョ"), + (0xFF6F, "M", "ッ"), + (0xFF70, "M", "ー"), + (0xFF71, "M", "ア"), + (0xFF72, "M", "イ"), + (0xFF73, "M", "ウ"), + (0xFF74, "M", "エ"), + (0xFF75, "M", "オ"), + (0xFF76, "M", "カ"), + (0xFF77, "M", "キ"), + (0xFF78, "M", "ク"), + (0xFF79, "M", "ケ"), + (0xFF7A, "M", "コ"), + (0xFF7B, "M", "サ"), + (0xFF7C, "M", "シ"), + (0xFF7D, "M", "ス"), + (0xFF7E, "M", "セ"), + (0xFF7F, "M", "ソ"), + (0xFF80, "M", "タ"), + (0xFF81, "M", "チ"), + (0xFF82, "M", "ツ"), + (0xFF83, "M", "テ"), + (0xFF84, "M", "ト"), + (0xFF85, "M", "ナ"), + (0xFF86, "M", "ニ"), + (0xFF87, "M", "ヌ"), + (0xFF88, "M", "ネ"), + (0xFF89, "M", "ノ"), + (0xFF8A, "M", "ハ"), + (0xFF8B, "M", "ヒ"), + (0xFF8C, "M", "フ"), + (0xFF8D, "M", "ヘ"), + (0xFF8E, "M", "ホ"), + (0xFF8F, "M", "マ"), + (0xFF90, "M", "ミ"), + (0xFF91, "M", "ム"), + (0xFF92, "M", "メ"), + (0xFF93, "M", "モ"), + (0xFF94, "M", "ヤ"), + (0xFF95, "M", "ユ"), + (0xFF96, "M", "ヨ"), + (0xFF97, "M", "ラ"), + (0xFF98, "M", "リ"), + (0xFF99, "M", "ル"), + (0xFF9A, "M", "レ"), + (0xFF9B, "M", "ロ"), + (0xFF9C, "M", "ワ"), + (0xFF9D, "M", "ン"), + (0xFF9E, "M", "゙"), + (0xFF9F, "M", "゚"), + (0xFFA0, "I"), + (0xFFA1, "M", "ᄀ"), + (0xFFA2, "M", "ᄁ"), + (0xFFA3, "M", "ᆪ"), + (0xFFA4, "M", "ᄂ"), + (0xFFA5, "M", "ᆬ"), + (0xFFA6, "M", "ᆭ"), + (0xFFA7, "M", "ᄃ"), + (0xFFA8, "M", "ᄄ"), + (0xFFA9, "M", "ᄅ"), + (0xFFAA, "M", "ᆰ"), + (0xFFAB, "M", "ᆱ"), + (0xFFAC, "M", "ᆲ"), + (0xFFAD, "M", "ᆳ"), + (0xFFAE, "M", "ᆴ"), + (0xFFAF, "M", "ᆵ"), + (0xFFB0, "M", "ᄚ"), + (0xFFB1, "M", "ᄆ"), + (0xFFB2, "M", "ᄇ"), + (0xFFB3, "M", "ᄈ"), + (0xFFB4, "M", "ᄡ"), + (0xFFB5, "M", "ᄉ"), + (0xFFB6, "M", "ᄊ"), + (0xFFB7, "M", "ᄋ"), + (0xFFB8, "M", "ᄌ"), + (0xFFB9, "M", "ᄍ"), + (0xFFBA, "M", "ᄎ"), + (0xFFBB, "M", "ᄏ"), + (0xFFBC, "M", "ᄐ"), + (0xFFBD, "M", "ᄑ"), + (0xFFBE, "M", "ᄒ"), + (0xFFBF, "X"), + ] + + +def _seg_53() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0xFFC2, "M", "ᅡ"), + (0xFFC3, "M", "ᅢ"), + (0xFFC4, "M", "ᅣ"), + (0xFFC5, "M", "ᅤ"), + (0xFFC6, "M", "ᅥ"), + (0xFFC7, "M", "ᅦ"), + (0xFFC8, "X"), + (0xFFCA, "M", "ᅧ"), + (0xFFCB, "M", "ᅨ"), + (0xFFCC, "M", "ᅩ"), + (0xFFCD, "M", "ᅪ"), + (0xFFCE, "M", "ᅫ"), + (0xFFCF, "M", "ᅬ"), + (0xFFD0, "X"), + (0xFFD2, "M", "ᅭ"), + (0xFFD3, "M", "ᅮ"), + (0xFFD4, "M", "ᅯ"), + (0xFFD5, "M", "ᅰ"), + (0xFFD6, "M", "ᅱ"), + (0xFFD7, "M", "ᅲ"), + (0xFFD8, "X"), + (0xFFDA, "M", "ᅳ"), + (0xFFDB, "M", "ᅴ"), + (0xFFDC, "M", "ᅵ"), + (0xFFDD, "X"), + (0xFFE0, "M", "¢"), + (0xFFE1, "M", "£"), + (0xFFE2, "M", "¬"), + (0xFFE3, "M", " ̄"), + (0xFFE4, "M", "¦"), + (0xFFE5, "M", "¥"), + (0xFFE6, "M", "₩"), + (0xFFE7, "X"), + (0xFFE8, "M", "│"), + (0xFFE9, "M", "←"), + (0xFFEA, "M", "↑"), + (0xFFEB, "M", "→"), + (0xFFEC, "M", "↓"), + (0xFFED, "M", "■"), + (0xFFEE, "M", "○"), + (0xFFEF, "X"), + (0x10000, "V"), + (0x1000C, "X"), + (0x1000D, "V"), + (0x10027, "X"), + (0x10028, "V"), + (0x1003B, "X"), + (0x1003C, "V"), + (0x1003E, "X"), + (0x1003F, "V"), + (0x1004E, "X"), + (0x10050, "V"), + (0x1005E, "X"), + (0x10080, "V"), + (0x100FB, "X"), + (0x10100, "V"), + (0x10103, "X"), + (0x10107, "V"), + (0x10134, "X"), + (0x10137, "V"), + (0x1018F, "X"), + (0x10190, "V"), + (0x1019D, "X"), + (0x101A0, "V"), + (0x101A1, "X"), + (0x101D0, "V"), + (0x101FE, "X"), + (0x10280, "V"), + (0x1029D, "X"), + (0x102A0, "V"), + (0x102D1, "X"), + (0x102E0, "V"), + (0x102FC, "X"), + (0x10300, "V"), + (0x10324, "X"), + (0x1032D, "V"), + (0x1034B, "X"), + (0x10350, "V"), + (0x1037B, "X"), + (0x10380, "V"), + (0x1039E, "X"), + (0x1039F, "V"), + (0x103C4, "X"), + (0x103C8, "V"), + (0x103D6, "X"), + (0x10400, "M", "𐐨"), + (0x10401, "M", "𐐩"), + (0x10402, "M", "𐐪"), + (0x10403, "M", "𐐫"), + (0x10404, "M", "𐐬"), + (0x10405, "M", "𐐭"), + (0x10406, "M", "𐐮"), + (0x10407, "M", "𐐯"), + (0x10408, "M", "𐐰"), + (0x10409, "M", "𐐱"), + (0x1040A, "M", "𐐲"), + (0x1040B, "M", "𐐳"), + (0x1040C, "M", "𐐴"), + (0x1040D, "M", "𐐵"), + (0x1040E, "M", "𐐶"), + ] + + +def _seg_54() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1040F, "M", "𐐷"), + (0x10410, "M", "𐐸"), + (0x10411, "M", "𐐹"), + (0x10412, "M", "𐐺"), + (0x10413, "M", "𐐻"), + (0x10414, "M", "𐐼"), + (0x10415, "M", "𐐽"), + (0x10416, "M", "𐐾"), + (0x10417, "M", "𐐿"), + (0x10418, "M", "𐑀"), + (0x10419, "M", "𐑁"), + (0x1041A, "M", "𐑂"), + (0x1041B, "M", "𐑃"), + (0x1041C, "M", "𐑄"), + (0x1041D, "M", "𐑅"), + (0x1041E, "M", "𐑆"), + (0x1041F, "M", "𐑇"), + (0x10420, "M", "𐑈"), + (0x10421, "M", "𐑉"), + (0x10422, "M", "𐑊"), + (0x10423, "M", "𐑋"), + (0x10424, "M", "𐑌"), + (0x10425, "M", "𐑍"), + (0x10426, "M", "𐑎"), + (0x10427, "M", "𐑏"), + (0x10428, "V"), + (0x1049E, "X"), + (0x104A0, "V"), + (0x104AA, "X"), + (0x104B0, "M", "𐓘"), + (0x104B1, "M", "𐓙"), + (0x104B2, "M", "𐓚"), + (0x104B3, "M", "𐓛"), + (0x104B4, "M", "𐓜"), + (0x104B5, "M", "𐓝"), + (0x104B6, "M", "𐓞"), + (0x104B7, "M", "𐓟"), + (0x104B8, "M", "𐓠"), + (0x104B9, "M", "𐓡"), + (0x104BA, "M", "𐓢"), + (0x104BB, "M", "𐓣"), + (0x104BC, "M", "𐓤"), + (0x104BD, "M", "𐓥"), + (0x104BE, "M", "𐓦"), + (0x104BF, "M", "𐓧"), + (0x104C0, "M", "𐓨"), + (0x104C1, "M", "𐓩"), + (0x104C2, "M", "𐓪"), + (0x104C3, "M", "𐓫"), + (0x104C4, "M", "𐓬"), + (0x104C5, "M", "𐓭"), + (0x104C6, "M", "𐓮"), + (0x104C7, "M", "𐓯"), + (0x104C8, "M", "𐓰"), + (0x104C9, "M", "𐓱"), + (0x104CA, "M", "𐓲"), + (0x104CB, "M", "𐓳"), + (0x104CC, "M", "𐓴"), + (0x104CD, "M", "𐓵"), + (0x104CE, "M", "𐓶"), + (0x104CF, "M", "𐓷"), + (0x104D0, "M", "𐓸"), + (0x104D1, "M", "𐓹"), + (0x104D2, "M", "𐓺"), + (0x104D3, "M", "𐓻"), + (0x104D4, "X"), + (0x104D8, "V"), + (0x104FC, "X"), + (0x10500, "V"), + (0x10528, "X"), + (0x10530, "V"), + (0x10564, "X"), + (0x1056F, "V"), + (0x10570, "M", "𐖗"), + (0x10571, "M", "𐖘"), + (0x10572, "M", "𐖙"), + (0x10573, "M", "𐖚"), + (0x10574, "M", "𐖛"), + (0x10575, "M", "𐖜"), + (0x10576, "M", "𐖝"), + (0x10577, "M", "𐖞"), + (0x10578, "M", "𐖟"), + (0x10579, "M", "𐖠"), + (0x1057A, "M", "𐖡"), + (0x1057B, "X"), + (0x1057C, "M", "𐖣"), + (0x1057D, "M", "𐖤"), + (0x1057E, "M", "𐖥"), + (0x1057F, "M", "𐖦"), + (0x10580, "M", "𐖧"), + (0x10581, "M", "𐖨"), + (0x10582, "M", "𐖩"), + (0x10583, "M", "𐖪"), + (0x10584, "M", "𐖫"), + (0x10585, "M", "𐖬"), + (0x10586, "M", "𐖭"), + (0x10587, "M", "𐖮"), + (0x10588, "M", "𐖯"), + (0x10589, "M", "𐖰"), + (0x1058A, "M", "𐖱"), + ] + + +def _seg_55() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1058B, "X"), + (0x1058C, "M", "𐖳"), + (0x1058D, "M", "𐖴"), + (0x1058E, "M", "𐖵"), + (0x1058F, "M", "𐖶"), + (0x10590, "M", "𐖷"), + (0x10591, "M", "𐖸"), + (0x10592, "M", "𐖹"), + (0x10593, "X"), + (0x10594, "M", "𐖻"), + (0x10595, "M", "𐖼"), + (0x10596, "X"), + (0x10597, "V"), + (0x105A2, "X"), + (0x105A3, "V"), + (0x105B2, "X"), + (0x105B3, "V"), + (0x105BA, "X"), + (0x105BB, "V"), + (0x105BD, "X"), + (0x105C0, "V"), + (0x105F4, "X"), + (0x10600, "V"), + (0x10737, "X"), + (0x10740, "V"), + (0x10756, "X"), + (0x10760, "V"), + (0x10768, "X"), + (0x10780, "V"), + (0x10781, "M", "ː"), + (0x10782, "M", "ˑ"), + (0x10783, "M", "æ"), + (0x10784, "M", "ʙ"), + (0x10785, "M", "ɓ"), + (0x10786, "X"), + (0x10787, "M", "ʣ"), + (0x10788, "M", "ꭦ"), + (0x10789, "M", "ʥ"), + (0x1078A, "M", "ʤ"), + (0x1078B, "M", "ɖ"), + (0x1078C, "M", "ɗ"), + (0x1078D, "M", "ᶑ"), + (0x1078E, "M", "ɘ"), + (0x1078F, "M", "ɞ"), + (0x10790, "M", "ʩ"), + (0x10791, "M", "ɤ"), + (0x10792, "M", "ɢ"), + (0x10793, "M", "ɠ"), + (0x10794, "M", "ʛ"), + (0x10795, "M", "ħ"), + (0x10796, "M", "ʜ"), + (0x10797, "M", "ɧ"), + (0x10798, "M", "ʄ"), + (0x10799, "M", "ʪ"), + (0x1079A, "M", "ʫ"), + (0x1079B, "M", "ɬ"), + (0x1079C, "M", "𝼄"), + (0x1079D, "M", "ꞎ"), + (0x1079E, "M", "ɮ"), + (0x1079F, "M", "𝼅"), + (0x107A0, "M", "ʎ"), + (0x107A1, "M", "𝼆"), + (0x107A2, "M", "ø"), + (0x107A3, "M", "ɶ"), + (0x107A4, "M", "ɷ"), + (0x107A5, "M", "q"), + (0x107A6, "M", "ɺ"), + (0x107A7, "M", "𝼈"), + (0x107A8, "M", "ɽ"), + (0x107A9, "M", "ɾ"), + (0x107AA, "M", "ʀ"), + (0x107AB, "M", "ʨ"), + (0x107AC, "M", "ʦ"), + (0x107AD, "M", "ꭧ"), + (0x107AE, "M", "ʧ"), + (0x107AF, "M", "ʈ"), + (0x107B0, "M", "ⱱ"), + (0x107B1, "X"), + (0x107B2, "M", "ʏ"), + (0x107B3, "M", "ʡ"), + (0x107B4, "M", "ʢ"), + (0x107B5, "M", "ʘ"), + (0x107B6, "M", "ǀ"), + (0x107B7, "M", "ǁ"), + (0x107B8, "M", "ǂ"), + (0x107B9, "M", "𝼊"), + (0x107BA, "M", "𝼞"), + (0x107BB, "X"), + (0x10800, "V"), + (0x10806, "X"), + (0x10808, "V"), + (0x10809, "X"), + (0x1080A, "V"), + (0x10836, "X"), + (0x10837, "V"), + (0x10839, "X"), + (0x1083C, "V"), + (0x1083D, "X"), + (0x1083F, "V"), + (0x10856, "X"), + ] + + +def _seg_56() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x10857, "V"), + (0x1089F, "X"), + (0x108A7, "V"), + (0x108B0, "X"), + (0x108E0, "V"), + (0x108F3, "X"), + (0x108F4, "V"), + (0x108F6, "X"), + (0x108FB, "V"), + (0x1091C, "X"), + (0x1091F, "V"), + (0x1093A, "X"), + (0x1093F, "V"), + (0x10940, "X"), + (0x10980, "V"), + (0x109B8, "X"), + (0x109BC, "V"), + (0x109D0, "X"), + (0x109D2, "V"), + (0x10A04, "X"), + (0x10A05, "V"), + (0x10A07, "X"), + (0x10A0C, "V"), + (0x10A14, "X"), + (0x10A15, "V"), + (0x10A18, "X"), + (0x10A19, "V"), + (0x10A36, "X"), + (0x10A38, "V"), + (0x10A3B, "X"), + (0x10A3F, "V"), + (0x10A49, "X"), + (0x10A50, "V"), + (0x10A59, "X"), + (0x10A60, "V"), + (0x10AA0, "X"), + (0x10AC0, "V"), + (0x10AE7, "X"), + (0x10AEB, "V"), + (0x10AF7, "X"), + (0x10B00, "V"), + (0x10B36, "X"), + (0x10B39, "V"), + (0x10B56, "X"), + (0x10B58, "V"), + (0x10B73, "X"), + (0x10B78, "V"), + (0x10B92, "X"), + (0x10B99, "V"), + (0x10B9D, "X"), + (0x10BA9, "V"), + (0x10BB0, "X"), + (0x10C00, "V"), + (0x10C49, "X"), + (0x10C80, "M", "𐳀"), + (0x10C81, "M", "𐳁"), + (0x10C82, "M", "𐳂"), + (0x10C83, "M", "𐳃"), + (0x10C84, "M", "𐳄"), + (0x10C85, "M", "𐳅"), + (0x10C86, "M", "𐳆"), + (0x10C87, "M", "𐳇"), + (0x10C88, "M", "𐳈"), + (0x10C89, "M", "𐳉"), + (0x10C8A, "M", "𐳊"), + (0x10C8B, "M", "𐳋"), + (0x10C8C, "M", "𐳌"), + (0x10C8D, "M", "𐳍"), + (0x10C8E, "M", "𐳎"), + (0x10C8F, "M", "𐳏"), + (0x10C90, "M", "𐳐"), + (0x10C91, "M", "𐳑"), + (0x10C92, "M", "𐳒"), + (0x10C93, "M", "𐳓"), + (0x10C94, "M", "𐳔"), + (0x10C95, "M", "𐳕"), + (0x10C96, "M", "𐳖"), + (0x10C97, "M", "𐳗"), + (0x10C98, "M", "𐳘"), + (0x10C99, "M", "𐳙"), + (0x10C9A, "M", "𐳚"), + (0x10C9B, "M", "𐳛"), + (0x10C9C, "M", "𐳜"), + (0x10C9D, "M", "𐳝"), + (0x10C9E, "M", "𐳞"), + (0x10C9F, "M", "𐳟"), + (0x10CA0, "M", "𐳠"), + (0x10CA1, "M", "𐳡"), + (0x10CA2, "M", "𐳢"), + (0x10CA3, "M", "𐳣"), + (0x10CA4, "M", "𐳤"), + (0x10CA5, "M", "𐳥"), + (0x10CA6, "M", "𐳦"), + (0x10CA7, "M", "𐳧"), + (0x10CA8, "M", "𐳨"), + (0x10CA9, "M", "𐳩"), + (0x10CAA, "M", "𐳪"), + (0x10CAB, "M", "𐳫"), + (0x10CAC, "M", "𐳬"), + (0x10CAD, "M", "𐳭"), + ] + + +def _seg_57() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x10CAE, "M", "𐳮"), + (0x10CAF, "M", "𐳯"), + (0x10CB0, "M", "𐳰"), + (0x10CB1, "M", "𐳱"), + (0x10CB2, "M", "𐳲"), + (0x10CB3, "X"), + (0x10CC0, "V"), + (0x10CF3, "X"), + (0x10CFA, "V"), + (0x10D28, "X"), + (0x10D30, "V"), + (0x10D3A, "X"), + (0x10D40, "V"), + (0x10D50, "M", "𐵰"), + (0x10D51, "M", "𐵱"), + (0x10D52, "M", "𐵲"), + (0x10D53, "M", "𐵳"), + (0x10D54, "M", "𐵴"), + (0x10D55, "M", "𐵵"), + (0x10D56, "M", "𐵶"), + (0x10D57, "M", "𐵷"), + (0x10D58, "M", "𐵸"), + (0x10D59, "M", "𐵹"), + (0x10D5A, "M", "𐵺"), + (0x10D5B, "M", "𐵻"), + (0x10D5C, "M", "𐵼"), + (0x10D5D, "M", "𐵽"), + (0x10D5E, "M", "𐵾"), + (0x10D5F, "M", "𐵿"), + (0x10D60, "M", "𐶀"), + (0x10D61, "M", "𐶁"), + (0x10D62, "M", "𐶂"), + (0x10D63, "M", "𐶃"), + (0x10D64, "M", "𐶄"), + (0x10D65, "M", "𐶅"), + (0x10D66, "X"), + (0x10D69, "V"), + (0x10D86, "X"), + (0x10D8E, "V"), + (0x10D90, "X"), + (0x10E60, "V"), + (0x10E7F, "X"), + (0x10E80, "V"), + (0x10EAA, "X"), + (0x10EAB, "V"), + (0x10EAE, "X"), + (0x10EB0, "V"), + (0x10EB2, "X"), + (0x10EC2, "V"), + (0x10EC5, "X"), + (0x10EFC, "V"), + (0x10F28, "X"), + (0x10F30, "V"), + (0x10F5A, "X"), + (0x10F70, "V"), + (0x10F8A, "X"), + (0x10FB0, "V"), + (0x10FCC, "X"), + (0x10FE0, "V"), + (0x10FF7, "X"), + (0x11000, "V"), + (0x1104E, "X"), + (0x11052, "V"), + (0x11076, "X"), + (0x1107F, "V"), + (0x110BD, "X"), + (0x110BE, "V"), + (0x110C3, "X"), + (0x110D0, "V"), + (0x110E9, "X"), + (0x110F0, "V"), + (0x110FA, "X"), + (0x11100, "V"), + (0x11135, "X"), + (0x11136, "V"), + (0x11148, "X"), + (0x11150, "V"), + (0x11177, "X"), + (0x11180, "V"), + (0x111E0, "X"), + (0x111E1, "V"), + (0x111F5, "X"), + (0x11200, "V"), + (0x11212, "X"), + (0x11213, "V"), + (0x11242, "X"), + (0x11280, "V"), + (0x11287, "X"), + (0x11288, "V"), + (0x11289, "X"), + (0x1128A, "V"), + (0x1128E, "X"), + (0x1128F, "V"), + (0x1129E, "X"), + (0x1129F, "V"), + (0x112AA, "X"), + (0x112B0, "V"), + (0x112EB, "X"), + (0x112F0, "V"), + (0x112FA, "X"), + ] + + +def _seg_58() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x11300, "V"), + (0x11304, "X"), + (0x11305, "V"), + (0x1130D, "X"), + (0x1130F, "V"), + (0x11311, "X"), + (0x11313, "V"), + (0x11329, "X"), + (0x1132A, "V"), + (0x11331, "X"), + (0x11332, "V"), + (0x11334, "X"), + (0x11335, "V"), + (0x1133A, "X"), + (0x1133B, "V"), + (0x11345, "X"), + (0x11347, "V"), + (0x11349, "X"), + (0x1134B, "V"), + (0x1134E, "X"), + (0x11350, "V"), + (0x11351, "X"), + (0x11357, "V"), + (0x11358, "X"), + (0x1135D, "V"), + (0x11364, "X"), + (0x11366, "V"), + (0x1136D, "X"), + (0x11370, "V"), + (0x11375, "X"), + (0x11380, "V"), + (0x1138A, "X"), + (0x1138B, "V"), + (0x1138C, "X"), + (0x1138E, "V"), + (0x1138F, "X"), + (0x11390, "V"), + (0x113B6, "X"), + (0x113B7, "V"), + (0x113C1, "X"), + (0x113C2, "V"), + (0x113C3, "X"), + (0x113C5, "V"), + (0x113C6, "X"), + (0x113C7, "V"), + (0x113CB, "X"), + (0x113CC, "V"), + (0x113D6, "X"), + (0x113D7, "V"), + (0x113D9, "X"), + (0x113E1, "V"), + (0x113E3, "X"), + (0x11400, "V"), + (0x1145C, "X"), + (0x1145D, "V"), + (0x11462, "X"), + (0x11480, "V"), + (0x114C8, "X"), + (0x114D0, "V"), + (0x114DA, "X"), + (0x11580, "V"), + (0x115B6, "X"), + (0x115B8, "V"), + (0x115DE, "X"), + (0x11600, "V"), + (0x11645, "X"), + (0x11650, "V"), + (0x1165A, "X"), + (0x11660, "V"), + (0x1166D, "X"), + (0x11680, "V"), + (0x116BA, "X"), + (0x116C0, "V"), + (0x116CA, "X"), + (0x116D0, "V"), + (0x116E4, "X"), + (0x11700, "V"), + (0x1171B, "X"), + (0x1171D, "V"), + (0x1172C, "X"), + (0x11730, "V"), + (0x11747, "X"), + (0x11800, "V"), + (0x1183C, "X"), + (0x118A0, "M", "𑣀"), + (0x118A1, "M", "𑣁"), + (0x118A2, "M", "𑣂"), + (0x118A3, "M", "𑣃"), + (0x118A4, "M", "𑣄"), + (0x118A5, "M", "𑣅"), + (0x118A6, "M", "𑣆"), + (0x118A7, "M", "𑣇"), + (0x118A8, "M", "𑣈"), + (0x118A9, "M", "𑣉"), + (0x118AA, "M", "𑣊"), + (0x118AB, "M", "𑣋"), + (0x118AC, "M", "𑣌"), + (0x118AD, "M", "𑣍"), + (0x118AE, "M", "𑣎"), + (0x118AF, "M", "𑣏"), + ] + + +def _seg_59() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x118B0, "M", "𑣐"), + (0x118B1, "M", "𑣑"), + (0x118B2, "M", "𑣒"), + (0x118B3, "M", "𑣓"), + (0x118B4, "M", "𑣔"), + (0x118B5, "M", "𑣕"), + (0x118B6, "M", "𑣖"), + (0x118B7, "M", "𑣗"), + (0x118B8, "M", "𑣘"), + (0x118B9, "M", "𑣙"), + (0x118BA, "M", "𑣚"), + (0x118BB, "M", "𑣛"), + (0x118BC, "M", "𑣜"), + (0x118BD, "M", "𑣝"), + (0x118BE, "M", "𑣞"), + (0x118BF, "M", "𑣟"), + (0x118C0, "V"), + (0x118F3, "X"), + (0x118FF, "V"), + (0x11907, "X"), + (0x11909, "V"), + (0x1190A, "X"), + (0x1190C, "V"), + (0x11914, "X"), + (0x11915, "V"), + (0x11917, "X"), + (0x11918, "V"), + (0x11936, "X"), + (0x11937, "V"), + (0x11939, "X"), + (0x1193B, "V"), + (0x11947, "X"), + (0x11950, "V"), + (0x1195A, "X"), + (0x119A0, "V"), + (0x119A8, "X"), + (0x119AA, "V"), + (0x119D8, "X"), + (0x119DA, "V"), + (0x119E5, "X"), + (0x11A00, "V"), + (0x11A48, "X"), + (0x11A50, "V"), + (0x11AA3, "X"), + (0x11AB0, "V"), + (0x11AF9, "X"), + (0x11B00, "V"), + (0x11B0A, "X"), + (0x11BC0, "V"), + (0x11BE2, "X"), + (0x11BF0, "V"), + (0x11BFA, "X"), + (0x11C00, "V"), + (0x11C09, "X"), + (0x11C0A, "V"), + (0x11C37, "X"), + (0x11C38, "V"), + (0x11C46, "X"), + (0x11C50, "V"), + (0x11C6D, "X"), + (0x11C70, "V"), + (0x11C90, "X"), + (0x11C92, "V"), + (0x11CA8, "X"), + (0x11CA9, "V"), + (0x11CB7, "X"), + (0x11D00, "V"), + (0x11D07, "X"), + (0x11D08, "V"), + (0x11D0A, "X"), + (0x11D0B, "V"), + (0x11D37, "X"), + (0x11D3A, "V"), + (0x11D3B, "X"), + (0x11D3C, "V"), + (0x11D3E, "X"), + (0x11D3F, "V"), + (0x11D48, "X"), + (0x11D50, "V"), + (0x11D5A, "X"), + (0x11D60, "V"), + (0x11D66, "X"), + (0x11D67, "V"), + (0x11D69, "X"), + (0x11D6A, "V"), + (0x11D8F, "X"), + (0x11D90, "V"), + (0x11D92, "X"), + (0x11D93, "V"), + (0x11D99, "X"), + (0x11DA0, "V"), + (0x11DAA, "X"), + (0x11EE0, "V"), + (0x11EF9, "X"), + (0x11F00, "V"), + (0x11F11, "X"), + (0x11F12, "V"), + (0x11F3B, "X"), + (0x11F3E, "V"), + (0x11F5B, "X"), + ] + + +def _seg_60() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x11FB0, "V"), + (0x11FB1, "X"), + (0x11FC0, "V"), + (0x11FF2, "X"), + (0x11FFF, "V"), + (0x1239A, "X"), + (0x12400, "V"), + (0x1246F, "X"), + (0x12470, "V"), + (0x12475, "X"), + (0x12480, "V"), + (0x12544, "X"), + (0x12F90, "V"), + (0x12FF3, "X"), + (0x13000, "V"), + (0x13430, "X"), + (0x13440, "V"), + (0x13456, "X"), + (0x13460, "V"), + (0x143FB, "X"), + (0x14400, "V"), + (0x14647, "X"), + (0x16100, "V"), + (0x1613A, "X"), + (0x16800, "V"), + (0x16A39, "X"), + (0x16A40, "V"), + (0x16A5F, "X"), + (0x16A60, "V"), + (0x16A6A, "X"), + (0x16A6E, "V"), + (0x16ABF, "X"), + (0x16AC0, "V"), + (0x16ACA, "X"), + (0x16AD0, "V"), + (0x16AEE, "X"), + (0x16AF0, "V"), + (0x16AF6, "X"), + (0x16B00, "V"), + (0x16B46, "X"), + (0x16B50, "V"), + (0x16B5A, "X"), + (0x16B5B, "V"), + (0x16B62, "X"), + (0x16B63, "V"), + (0x16B78, "X"), + (0x16B7D, "V"), + (0x16B90, "X"), + (0x16D40, "V"), + (0x16D7A, "X"), + (0x16E40, "M", "𖹠"), + (0x16E41, "M", "𖹡"), + (0x16E42, "M", "𖹢"), + (0x16E43, "M", "𖹣"), + (0x16E44, "M", "𖹤"), + (0x16E45, "M", "𖹥"), + (0x16E46, "M", "𖹦"), + (0x16E47, "M", "𖹧"), + (0x16E48, "M", "𖹨"), + (0x16E49, "M", "𖹩"), + (0x16E4A, "M", "𖹪"), + (0x16E4B, "M", "𖹫"), + (0x16E4C, "M", "𖹬"), + (0x16E4D, "M", "𖹭"), + (0x16E4E, "M", "𖹮"), + (0x16E4F, "M", "𖹯"), + (0x16E50, "M", "𖹰"), + (0x16E51, "M", "𖹱"), + (0x16E52, "M", "𖹲"), + (0x16E53, "M", "𖹳"), + (0x16E54, "M", "𖹴"), + (0x16E55, "M", "𖹵"), + (0x16E56, "M", "𖹶"), + (0x16E57, "M", "𖹷"), + (0x16E58, "M", "𖹸"), + (0x16E59, "M", "𖹹"), + (0x16E5A, "M", "𖹺"), + (0x16E5B, "M", "𖹻"), + (0x16E5C, "M", "𖹼"), + (0x16E5D, "M", "𖹽"), + (0x16E5E, "M", "𖹾"), + (0x16E5F, "M", "𖹿"), + (0x16E60, "V"), + (0x16E9B, "X"), + (0x16F00, "V"), + (0x16F4B, "X"), + (0x16F4F, "V"), + (0x16F88, "X"), + (0x16F8F, "V"), + (0x16FA0, "X"), + (0x16FE0, "V"), + (0x16FE5, "X"), + (0x16FF0, "V"), + (0x16FF2, "X"), + (0x17000, "V"), + (0x187F8, "X"), + (0x18800, "V"), + (0x18CD6, "X"), + (0x18CFF, "V"), + (0x18D09, "X"), + ] + + +def _seg_61() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1AFF0, "V"), + (0x1AFF4, "X"), + (0x1AFF5, "V"), + (0x1AFFC, "X"), + (0x1AFFD, "V"), + (0x1AFFF, "X"), + (0x1B000, "V"), + (0x1B123, "X"), + (0x1B132, "V"), + (0x1B133, "X"), + (0x1B150, "V"), + (0x1B153, "X"), + (0x1B155, "V"), + (0x1B156, "X"), + (0x1B164, "V"), + (0x1B168, "X"), + (0x1B170, "V"), + (0x1B2FC, "X"), + (0x1BC00, "V"), + (0x1BC6B, "X"), + (0x1BC70, "V"), + (0x1BC7D, "X"), + (0x1BC80, "V"), + (0x1BC89, "X"), + (0x1BC90, "V"), + (0x1BC9A, "X"), + (0x1BC9C, "V"), + (0x1BCA0, "I"), + (0x1BCA4, "X"), + (0x1CC00, "V"), + (0x1CCD6, "M", "a"), + (0x1CCD7, "M", "b"), + (0x1CCD8, "M", "c"), + (0x1CCD9, "M", "d"), + (0x1CCDA, "M", "e"), + (0x1CCDB, "M", "f"), + (0x1CCDC, "M", "g"), + (0x1CCDD, "M", "h"), + (0x1CCDE, "M", "i"), + (0x1CCDF, "M", "j"), + (0x1CCE0, "M", "k"), + (0x1CCE1, "M", "l"), + (0x1CCE2, "M", "m"), + (0x1CCE3, "M", "n"), + (0x1CCE4, "M", "o"), + (0x1CCE5, "M", "p"), + (0x1CCE6, "M", "q"), + (0x1CCE7, "M", "r"), + (0x1CCE8, "M", "s"), + (0x1CCE9, "M", "t"), + (0x1CCEA, "M", "u"), + (0x1CCEB, "M", "v"), + (0x1CCEC, "M", "w"), + (0x1CCED, "M", "x"), + (0x1CCEE, "M", "y"), + (0x1CCEF, "M", "z"), + (0x1CCF0, "M", "0"), + (0x1CCF1, "M", "1"), + (0x1CCF2, "M", "2"), + (0x1CCF3, "M", "3"), + (0x1CCF4, "M", "4"), + (0x1CCF5, "M", "5"), + (0x1CCF6, "M", "6"), + (0x1CCF7, "M", "7"), + (0x1CCF8, "M", "8"), + (0x1CCF9, "M", "9"), + (0x1CCFA, "X"), + (0x1CD00, "V"), + (0x1CEB4, "X"), + (0x1CF00, "V"), + (0x1CF2E, "X"), + (0x1CF30, "V"), + (0x1CF47, "X"), + (0x1CF50, "V"), + (0x1CFC4, "X"), + (0x1D000, "V"), + (0x1D0F6, "X"), + (0x1D100, "V"), + (0x1D127, "X"), + (0x1D129, "V"), + (0x1D15E, "M", "𝅗𝅥"), + (0x1D15F, "M", "𝅘𝅥"), + (0x1D160, "M", "𝅘𝅥𝅮"), + (0x1D161, "M", "𝅘𝅥𝅯"), + (0x1D162, "M", "𝅘𝅥𝅰"), + (0x1D163, "M", "𝅘𝅥𝅱"), + (0x1D164, "M", "𝅘𝅥𝅲"), + (0x1D165, "V"), + (0x1D173, "I"), + (0x1D17B, "V"), + (0x1D1BB, "M", "𝆹𝅥"), + (0x1D1BC, "M", "𝆺𝅥"), + (0x1D1BD, "M", "𝆹𝅥𝅮"), + (0x1D1BE, "M", "𝆺𝅥𝅮"), + (0x1D1BF, "M", "𝆹𝅥𝅯"), + (0x1D1C0, "M", "𝆺𝅥𝅯"), + (0x1D1C1, "V"), + (0x1D1EB, "X"), + (0x1D200, "V"), + (0x1D246, "X"), + ] + + +def _seg_62() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D2C0, "V"), + (0x1D2D4, "X"), + (0x1D2E0, "V"), + (0x1D2F4, "X"), + (0x1D300, "V"), + (0x1D357, "X"), + (0x1D360, "V"), + (0x1D379, "X"), + (0x1D400, "M", "a"), + (0x1D401, "M", "b"), + (0x1D402, "M", "c"), + (0x1D403, "M", "d"), + (0x1D404, "M", "e"), + (0x1D405, "M", "f"), + (0x1D406, "M", "g"), + (0x1D407, "M", "h"), + (0x1D408, "M", "i"), + (0x1D409, "M", "j"), + (0x1D40A, "M", "k"), + (0x1D40B, "M", "l"), + (0x1D40C, "M", "m"), + (0x1D40D, "M", "n"), + (0x1D40E, "M", "o"), + (0x1D40F, "M", "p"), + (0x1D410, "M", "q"), + (0x1D411, "M", "r"), + (0x1D412, "M", "s"), + (0x1D413, "M", "t"), + (0x1D414, "M", "u"), + (0x1D415, "M", "v"), + (0x1D416, "M", "w"), + (0x1D417, "M", "x"), + (0x1D418, "M", "y"), + (0x1D419, "M", "z"), + (0x1D41A, "M", "a"), + (0x1D41B, "M", "b"), + (0x1D41C, "M", "c"), + (0x1D41D, "M", "d"), + (0x1D41E, "M", "e"), + (0x1D41F, "M", "f"), + (0x1D420, "M", "g"), + (0x1D421, "M", "h"), + (0x1D422, "M", "i"), + (0x1D423, "M", "j"), + (0x1D424, "M", "k"), + (0x1D425, "M", "l"), + (0x1D426, "M", "m"), + (0x1D427, "M", "n"), + (0x1D428, "M", "o"), + (0x1D429, "M", "p"), + (0x1D42A, "M", "q"), + (0x1D42B, "M", "r"), + (0x1D42C, "M", "s"), + (0x1D42D, "M", "t"), + (0x1D42E, "M", "u"), + (0x1D42F, "M", "v"), + (0x1D430, "M", "w"), + (0x1D431, "M", "x"), + (0x1D432, "M", "y"), + (0x1D433, "M", "z"), + (0x1D434, "M", "a"), + (0x1D435, "M", "b"), + (0x1D436, "M", "c"), + (0x1D437, "M", "d"), + (0x1D438, "M", "e"), + (0x1D439, "M", "f"), + (0x1D43A, "M", "g"), + (0x1D43B, "M", "h"), + (0x1D43C, "M", "i"), + (0x1D43D, "M", "j"), + (0x1D43E, "M", "k"), + (0x1D43F, "M", "l"), + (0x1D440, "M", "m"), + (0x1D441, "M", "n"), + (0x1D442, "M", "o"), + (0x1D443, "M", "p"), + (0x1D444, "M", "q"), + (0x1D445, "M", "r"), + (0x1D446, "M", "s"), + (0x1D447, "M", "t"), + (0x1D448, "M", "u"), + (0x1D449, "M", "v"), + (0x1D44A, "M", "w"), + (0x1D44B, "M", "x"), + (0x1D44C, "M", "y"), + (0x1D44D, "M", "z"), + (0x1D44E, "M", "a"), + (0x1D44F, "M", "b"), + (0x1D450, "M", "c"), + (0x1D451, "M", "d"), + (0x1D452, "M", "e"), + (0x1D453, "M", "f"), + (0x1D454, "M", "g"), + (0x1D455, "X"), + (0x1D456, "M", "i"), + (0x1D457, "M", "j"), + (0x1D458, "M", "k"), + (0x1D459, "M", "l"), + (0x1D45A, "M", "m"), + (0x1D45B, "M", "n"), + ] + + +def _seg_63() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D45C, "M", "o"), + (0x1D45D, "M", "p"), + (0x1D45E, "M", "q"), + (0x1D45F, "M", "r"), + (0x1D460, "M", "s"), + (0x1D461, "M", "t"), + (0x1D462, "M", "u"), + (0x1D463, "M", "v"), + (0x1D464, "M", "w"), + (0x1D465, "M", "x"), + (0x1D466, "M", "y"), + (0x1D467, "M", "z"), + (0x1D468, "M", "a"), + (0x1D469, "M", "b"), + (0x1D46A, "M", "c"), + (0x1D46B, "M", "d"), + (0x1D46C, "M", "e"), + (0x1D46D, "M", "f"), + (0x1D46E, "M", "g"), + (0x1D46F, "M", "h"), + (0x1D470, "M", "i"), + (0x1D471, "M", "j"), + (0x1D472, "M", "k"), + (0x1D473, "M", "l"), + (0x1D474, "M", "m"), + (0x1D475, "M", "n"), + (0x1D476, "M", "o"), + (0x1D477, "M", "p"), + (0x1D478, "M", "q"), + (0x1D479, "M", "r"), + (0x1D47A, "M", "s"), + (0x1D47B, "M", "t"), + (0x1D47C, "M", "u"), + (0x1D47D, "M", "v"), + (0x1D47E, "M", "w"), + (0x1D47F, "M", "x"), + (0x1D480, "M", "y"), + (0x1D481, "M", "z"), + (0x1D482, "M", "a"), + (0x1D483, "M", "b"), + (0x1D484, "M", "c"), + (0x1D485, "M", "d"), + (0x1D486, "M", "e"), + (0x1D487, "M", "f"), + (0x1D488, "M", "g"), + (0x1D489, "M", "h"), + (0x1D48A, "M", "i"), + (0x1D48B, "M", "j"), + (0x1D48C, "M", "k"), + (0x1D48D, "M", "l"), + (0x1D48E, "M", "m"), + (0x1D48F, "M", "n"), + (0x1D490, "M", "o"), + (0x1D491, "M", "p"), + (0x1D492, "M", "q"), + (0x1D493, "M", "r"), + (0x1D494, "M", "s"), + (0x1D495, "M", "t"), + (0x1D496, "M", "u"), + (0x1D497, "M", "v"), + (0x1D498, "M", "w"), + (0x1D499, "M", "x"), + (0x1D49A, "M", "y"), + (0x1D49B, "M", "z"), + (0x1D49C, "M", "a"), + (0x1D49D, "X"), + (0x1D49E, "M", "c"), + (0x1D49F, "M", "d"), + (0x1D4A0, "X"), + (0x1D4A2, "M", "g"), + (0x1D4A3, "X"), + (0x1D4A5, "M", "j"), + (0x1D4A6, "M", "k"), + (0x1D4A7, "X"), + (0x1D4A9, "M", "n"), + (0x1D4AA, "M", "o"), + (0x1D4AB, "M", "p"), + (0x1D4AC, "M", "q"), + (0x1D4AD, "X"), + (0x1D4AE, "M", "s"), + (0x1D4AF, "M", "t"), + (0x1D4B0, "M", "u"), + (0x1D4B1, "M", "v"), + (0x1D4B2, "M", "w"), + (0x1D4B3, "M", "x"), + (0x1D4B4, "M", "y"), + (0x1D4B5, "M", "z"), + (0x1D4B6, "M", "a"), + (0x1D4B7, "M", "b"), + (0x1D4B8, "M", "c"), + (0x1D4B9, "M", "d"), + (0x1D4BA, "X"), + (0x1D4BB, "M", "f"), + (0x1D4BC, "X"), + (0x1D4BD, "M", "h"), + (0x1D4BE, "M", "i"), + (0x1D4BF, "M", "j"), + (0x1D4C0, "M", "k"), + (0x1D4C1, "M", "l"), + (0x1D4C2, "M", "m"), + ] + + +def _seg_64() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D4C3, "M", "n"), + (0x1D4C4, "X"), + (0x1D4C5, "M", "p"), + (0x1D4C6, "M", "q"), + (0x1D4C7, "M", "r"), + (0x1D4C8, "M", "s"), + (0x1D4C9, "M", "t"), + (0x1D4CA, "M", "u"), + (0x1D4CB, "M", "v"), + (0x1D4CC, "M", "w"), + (0x1D4CD, "M", "x"), + (0x1D4CE, "M", "y"), + (0x1D4CF, "M", "z"), + (0x1D4D0, "M", "a"), + (0x1D4D1, "M", "b"), + (0x1D4D2, "M", "c"), + (0x1D4D3, "M", "d"), + (0x1D4D4, "M", "e"), + (0x1D4D5, "M", "f"), + (0x1D4D6, "M", "g"), + (0x1D4D7, "M", "h"), + (0x1D4D8, "M", "i"), + (0x1D4D9, "M", "j"), + (0x1D4DA, "M", "k"), + (0x1D4DB, "M", "l"), + (0x1D4DC, "M", "m"), + (0x1D4DD, "M", "n"), + (0x1D4DE, "M", "o"), + (0x1D4DF, "M", "p"), + (0x1D4E0, "M", "q"), + (0x1D4E1, "M", "r"), + (0x1D4E2, "M", "s"), + (0x1D4E3, "M", "t"), + (0x1D4E4, "M", "u"), + (0x1D4E5, "M", "v"), + (0x1D4E6, "M", "w"), + (0x1D4E7, "M", "x"), + (0x1D4E8, "M", "y"), + (0x1D4E9, "M", "z"), + (0x1D4EA, "M", "a"), + (0x1D4EB, "M", "b"), + (0x1D4EC, "M", "c"), + (0x1D4ED, "M", "d"), + (0x1D4EE, "M", "e"), + (0x1D4EF, "M", "f"), + (0x1D4F0, "M", "g"), + (0x1D4F1, "M", "h"), + (0x1D4F2, "M", "i"), + (0x1D4F3, "M", "j"), + (0x1D4F4, "M", "k"), + (0x1D4F5, "M", "l"), + (0x1D4F6, "M", "m"), + (0x1D4F7, "M", "n"), + (0x1D4F8, "M", "o"), + (0x1D4F9, "M", "p"), + (0x1D4FA, "M", "q"), + (0x1D4FB, "M", "r"), + (0x1D4FC, "M", "s"), + (0x1D4FD, "M", "t"), + (0x1D4FE, "M", "u"), + (0x1D4FF, "M", "v"), + (0x1D500, "M", "w"), + (0x1D501, "M", "x"), + (0x1D502, "M", "y"), + (0x1D503, "M", "z"), + (0x1D504, "M", "a"), + (0x1D505, "M", "b"), + (0x1D506, "X"), + (0x1D507, "M", "d"), + (0x1D508, "M", "e"), + (0x1D509, "M", "f"), + (0x1D50A, "M", "g"), + (0x1D50B, "X"), + (0x1D50D, "M", "j"), + (0x1D50E, "M", "k"), + (0x1D50F, "M", "l"), + (0x1D510, "M", "m"), + (0x1D511, "M", "n"), + (0x1D512, "M", "o"), + (0x1D513, "M", "p"), + (0x1D514, "M", "q"), + (0x1D515, "X"), + (0x1D516, "M", "s"), + (0x1D517, "M", "t"), + (0x1D518, "M", "u"), + (0x1D519, "M", "v"), + (0x1D51A, "M", "w"), + (0x1D51B, "M", "x"), + (0x1D51C, "M", "y"), + (0x1D51D, "X"), + (0x1D51E, "M", "a"), + (0x1D51F, "M", "b"), + (0x1D520, "M", "c"), + (0x1D521, "M", "d"), + (0x1D522, "M", "e"), + (0x1D523, "M", "f"), + (0x1D524, "M", "g"), + (0x1D525, "M", "h"), + (0x1D526, "M", "i"), + (0x1D527, "M", "j"), + ] + + +def _seg_65() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D528, "M", "k"), + (0x1D529, "M", "l"), + (0x1D52A, "M", "m"), + (0x1D52B, "M", "n"), + (0x1D52C, "M", "o"), + (0x1D52D, "M", "p"), + (0x1D52E, "M", "q"), + (0x1D52F, "M", "r"), + (0x1D530, "M", "s"), + (0x1D531, "M", "t"), + (0x1D532, "M", "u"), + (0x1D533, "M", "v"), + (0x1D534, "M", "w"), + (0x1D535, "M", "x"), + (0x1D536, "M", "y"), + (0x1D537, "M", "z"), + (0x1D538, "M", "a"), + (0x1D539, "M", "b"), + (0x1D53A, "X"), + (0x1D53B, "M", "d"), + (0x1D53C, "M", "e"), + (0x1D53D, "M", "f"), + (0x1D53E, "M", "g"), + (0x1D53F, "X"), + (0x1D540, "M", "i"), + (0x1D541, "M", "j"), + (0x1D542, "M", "k"), + (0x1D543, "M", "l"), + (0x1D544, "M", "m"), + (0x1D545, "X"), + (0x1D546, "M", "o"), + (0x1D547, "X"), + (0x1D54A, "M", "s"), + (0x1D54B, "M", "t"), + (0x1D54C, "M", "u"), + (0x1D54D, "M", "v"), + (0x1D54E, "M", "w"), + (0x1D54F, "M", "x"), + (0x1D550, "M", "y"), + (0x1D551, "X"), + (0x1D552, "M", "a"), + (0x1D553, "M", "b"), + (0x1D554, "M", "c"), + (0x1D555, "M", "d"), + (0x1D556, "M", "e"), + (0x1D557, "M", "f"), + (0x1D558, "M", "g"), + (0x1D559, "M", "h"), + (0x1D55A, "M", "i"), + (0x1D55B, "M", "j"), + (0x1D55C, "M", "k"), + (0x1D55D, "M", "l"), + (0x1D55E, "M", "m"), + (0x1D55F, "M", "n"), + (0x1D560, "M", "o"), + (0x1D561, "M", "p"), + (0x1D562, "M", "q"), + (0x1D563, "M", "r"), + (0x1D564, "M", "s"), + (0x1D565, "M", "t"), + (0x1D566, "M", "u"), + (0x1D567, "M", "v"), + (0x1D568, "M", "w"), + (0x1D569, "M", "x"), + (0x1D56A, "M", "y"), + (0x1D56B, "M", "z"), + (0x1D56C, "M", "a"), + (0x1D56D, "M", "b"), + (0x1D56E, "M", "c"), + (0x1D56F, "M", "d"), + (0x1D570, "M", "e"), + (0x1D571, "M", "f"), + (0x1D572, "M", "g"), + (0x1D573, "M", "h"), + (0x1D574, "M", "i"), + (0x1D575, "M", "j"), + (0x1D576, "M", "k"), + (0x1D577, "M", "l"), + (0x1D578, "M", "m"), + (0x1D579, "M", "n"), + (0x1D57A, "M", "o"), + (0x1D57B, "M", "p"), + (0x1D57C, "M", "q"), + (0x1D57D, "M", "r"), + (0x1D57E, "M", "s"), + (0x1D57F, "M", "t"), + (0x1D580, "M", "u"), + (0x1D581, "M", "v"), + (0x1D582, "M", "w"), + (0x1D583, "M", "x"), + (0x1D584, "M", "y"), + (0x1D585, "M", "z"), + (0x1D586, "M", "a"), + (0x1D587, "M", "b"), + (0x1D588, "M", "c"), + (0x1D589, "M", "d"), + (0x1D58A, "M", "e"), + (0x1D58B, "M", "f"), + (0x1D58C, "M", "g"), + (0x1D58D, "M", "h"), + ] + + +def _seg_66() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D58E, "M", "i"), + (0x1D58F, "M", "j"), + (0x1D590, "M", "k"), + (0x1D591, "M", "l"), + (0x1D592, "M", "m"), + (0x1D593, "M", "n"), + (0x1D594, "M", "o"), + (0x1D595, "M", "p"), + (0x1D596, "M", "q"), + (0x1D597, "M", "r"), + (0x1D598, "M", "s"), + (0x1D599, "M", "t"), + (0x1D59A, "M", "u"), + (0x1D59B, "M", "v"), + (0x1D59C, "M", "w"), + (0x1D59D, "M", "x"), + (0x1D59E, "M", "y"), + (0x1D59F, "M", "z"), + (0x1D5A0, "M", "a"), + (0x1D5A1, "M", "b"), + (0x1D5A2, "M", "c"), + (0x1D5A3, "M", "d"), + (0x1D5A4, "M", "e"), + (0x1D5A5, "M", "f"), + (0x1D5A6, "M", "g"), + (0x1D5A7, "M", "h"), + (0x1D5A8, "M", "i"), + (0x1D5A9, "M", "j"), + (0x1D5AA, "M", "k"), + (0x1D5AB, "M", "l"), + (0x1D5AC, "M", "m"), + (0x1D5AD, "M", "n"), + (0x1D5AE, "M", "o"), + (0x1D5AF, "M", "p"), + (0x1D5B0, "M", "q"), + (0x1D5B1, "M", "r"), + (0x1D5B2, "M", "s"), + (0x1D5B3, "M", "t"), + (0x1D5B4, "M", "u"), + (0x1D5B5, "M", "v"), + (0x1D5B6, "M", "w"), + (0x1D5B7, "M", "x"), + (0x1D5B8, "M", "y"), + (0x1D5B9, "M", "z"), + (0x1D5BA, "M", "a"), + (0x1D5BB, "M", "b"), + (0x1D5BC, "M", "c"), + (0x1D5BD, "M", "d"), + (0x1D5BE, "M", "e"), + (0x1D5BF, "M", "f"), + (0x1D5C0, "M", "g"), + (0x1D5C1, "M", "h"), + (0x1D5C2, "M", "i"), + (0x1D5C3, "M", "j"), + (0x1D5C4, "M", "k"), + (0x1D5C5, "M", "l"), + (0x1D5C6, "M", "m"), + (0x1D5C7, "M", "n"), + (0x1D5C8, "M", "o"), + (0x1D5C9, "M", "p"), + (0x1D5CA, "M", "q"), + (0x1D5CB, "M", "r"), + (0x1D5CC, "M", "s"), + (0x1D5CD, "M", "t"), + (0x1D5CE, "M", "u"), + (0x1D5CF, "M", "v"), + (0x1D5D0, "M", "w"), + (0x1D5D1, "M", "x"), + (0x1D5D2, "M", "y"), + (0x1D5D3, "M", "z"), + (0x1D5D4, "M", "a"), + (0x1D5D5, "M", "b"), + (0x1D5D6, "M", "c"), + (0x1D5D7, "M", "d"), + (0x1D5D8, "M", "e"), + (0x1D5D9, "M", "f"), + (0x1D5DA, "M", "g"), + (0x1D5DB, "M", "h"), + (0x1D5DC, "M", "i"), + (0x1D5DD, "M", "j"), + (0x1D5DE, "M", "k"), + (0x1D5DF, "M", "l"), + (0x1D5E0, "M", "m"), + (0x1D5E1, "M", "n"), + (0x1D5E2, "M", "o"), + (0x1D5E3, "M", "p"), + (0x1D5E4, "M", "q"), + (0x1D5E5, "M", "r"), + (0x1D5E6, "M", "s"), + (0x1D5E7, "M", "t"), + (0x1D5E8, "M", "u"), + (0x1D5E9, "M", "v"), + (0x1D5EA, "M", "w"), + (0x1D5EB, "M", "x"), + (0x1D5EC, "M", "y"), + (0x1D5ED, "M", "z"), + (0x1D5EE, "M", "a"), + (0x1D5EF, "M", "b"), + (0x1D5F0, "M", "c"), + (0x1D5F1, "M", "d"), + ] + + +def _seg_67() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D5F2, "M", "e"), + (0x1D5F3, "M", "f"), + (0x1D5F4, "M", "g"), + (0x1D5F5, "M", "h"), + (0x1D5F6, "M", "i"), + (0x1D5F7, "M", "j"), + (0x1D5F8, "M", "k"), + (0x1D5F9, "M", "l"), + (0x1D5FA, "M", "m"), + (0x1D5FB, "M", "n"), + (0x1D5FC, "M", "o"), + (0x1D5FD, "M", "p"), + (0x1D5FE, "M", "q"), + (0x1D5FF, "M", "r"), + (0x1D600, "M", "s"), + (0x1D601, "M", "t"), + (0x1D602, "M", "u"), + (0x1D603, "M", "v"), + (0x1D604, "M", "w"), + (0x1D605, "M", "x"), + (0x1D606, "M", "y"), + (0x1D607, "M", "z"), + (0x1D608, "M", "a"), + (0x1D609, "M", "b"), + (0x1D60A, "M", "c"), + (0x1D60B, "M", "d"), + (0x1D60C, "M", "e"), + (0x1D60D, "M", "f"), + (0x1D60E, "M", "g"), + (0x1D60F, "M", "h"), + (0x1D610, "M", "i"), + (0x1D611, "M", "j"), + (0x1D612, "M", "k"), + (0x1D613, "M", "l"), + (0x1D614, "M", "m"), + (0x1D615, "M", "n"), + (0x1D616, "M", "o"), + (0x1D617, "M", "p"), + (0x1D618, "M", "q"), + (0x1D619, "M", "r"), + (0x1D61A, "M", "s"), + (0x1D61B, "M", "t"), + (0x1D61C, "M", "u"), + (0x1D61D, "M", "v"), + (0x1D61E, "M", "w"), + (0x1D61F, "M", "x"), + (0x1D620, "M", "y"), + (0x1D621, "M", "z"), + (0x1D622, "M", "a"), + (0x1D623, "M", "b"), + (0x1D624, "M", "c"), + (0x1D625, "M", "d"), + (0x1D626, "M", "e"), + (0x1D627, "M", "f"), + (0x1D628, "M", "g"), + (0x1D629, "M", "h"), + (0x1D62A, "M", "i"), + (0x1D62B, "M", "j"), + (0x1D62C, "M", "k"), + (0x1D62D, "M", "l"), + (0x1D62E, "M", "m"), + (0x1D62F, "M", "n"), + (0x1D630, "M", "o"), + (0x1D631, "M", "p"), + (0x1D632, "M", "q"), + (0x1D633, "M", "r"), + (0x1D634, "M", "s"), + (0x1D635, "M", "t"), + (0x1D636, "M", "u"), + (0x1D637, "M", "v"), + (0x1D638, "M", "w"), + (0x1D639, "M", "x"), + (0x1D63A, "M", "y"), + (0x1D63B, "M", "z"), + (0x1D63C, "M", "a"), + (0x1D63D, "M", "b"), + (0x1D63E, "M", "c"), + (0x1D63F, "M", "d"), + (0x1D640, "M", "e"), + (0x1D641, "M", "f"), + (0x1D642, "M", "g"), + (0x1D643, "M", "h"), + (0x1D644, "M", "i"), + (0x1D645, "M", "j"), + (0x1D646, "M", "k"), + (0x1D647, "M", "l"), + (0x1D648, "M", "m"), + (0x1D649, "M", "n"), + (0x1D64A, "M", "o"), + (0x1D64B, "M", "p"), + (0x1D64C, "M", "q"), + (0x1D64D, "M", "r"), + (0x1D64E, "M", "s"), + (0x1D64F, "M", "t"), + (0x1D650, "M", "u"), + (0x1D651, "M", "v"), + (0x1D652, "M", "w"), + (0x1D653, "M", "x"), + (0x1D654, "M", "y"), + (0x1D655, "M", "z"), + ] + + +def _seg_68() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D656, "M", "a"), + (0x1D657, "M", "b"), + (0x1D658, "M", "c"), + (0x1D659, "M", "d"), + (0x1D65A, "M", "e"), + (0x1D65B, "M", "f"), + (0x1D65C, "M", "g"), + (0x1D65D, "M", "h"), + (0x1D65E, "M", "i"), + (0x1D65F, "M", "j"), + (0x1D660, "M", "k"), + (0x1D661, "M", "l"), + (0x1D662, "M", "m"), + (0x1D663, "M", "n"), + (0x1D664, "M", "o"), + (0x1D665, "M", "p"), + (0x1D666, "M", "q"), + (0x1D667, "M", "r"), + (0x1D668, "M", "s"), + (0x1D669, "M", "t"), + (0x1D66A, "M", "u"), + (0x1D66B, "M", "v"), + (0x1D66C, "M", "w"), + (0x1D66D, "M", "x"), + (0x1D66E, "M", "y"), + (0x1D66F, "M", "z"), + (0x1D670, "M", "a"), + (0x1D671, "M", "b"), + (0x1D672, "M", "c"), + (0x1D673, "M", "d"), + (0x1D674, "M", "e"), + (0x1D675, "M", "f"), + (0x1D676, "M", "g"), + (0x1D677, "M", "h"), + (0x1D678, "M", "i"), + (0x1D679, "M", "j"), + (0x1D67A, "M", "k"), + (0x1D67B, "M", "l"), + (0x1D67C, "M", "m"), + (0x1D67D, "M", "n"), + (0x1D67E, "M", "o"), + (0x1D67F, "M", "p"), + (0x1D680, "M", "q"), + (0x1D681, "M", "r"), + (0x1D682, "M", "s"), + (0x1D683, "M", "t"), + (0x1D684, "M", "u"), + (0x1D685, "M", "v"), + (0x1D686, "M", "w"), + (0x1D687, "M", "x"), + (0x1D688, "M", "y"), + (0x1D689, "M", "z"), + (0x1D68A, "M", "a"), + (0x1D68B, "M", "b"), + (0x1D68C, "M", "c"), + (0x1D68D, "M", "d"), + (0x1D68E, "M", "e"), + (0x1D68F, "M", "f"), + (0x1D690, "M", "g"), + (0x1D691, "M", "h"), + (0x1D692, "M", "i"), + (0x1D693, "M", "j"), + (0x1D694, "M", "k"), + (0x1D695, "M", "l"), + (0x1D696, "M", "m"), + (0x1D697, "M", "n"), + (0x1D698, "M", "o"), + (0x1D699, "M", "p"), + (0x1D69A, "M", "q"), + (0x1D69B, "M", "r"), + (0x1D69C, "M", "s"), + (0x1D69D, "M", "t"), + (0x1D69E, "M", "u"), + (0x1D69F, "M", "v"), + (0x1D6A0, "M", "w"), + (0x1D6A1, "M", "x"), + (0x1D6A2, "M", "y"), + (0x1D6A3, "M", "z"), + (0x1D6A4, "M", "ı"), + (0x1D6A5, "M", "ȷ"), + (0x1D6A6, "X"), + (0x1D6A8, "M", "α"), + (0x1D6A9, "M", "β"), + (0x1D6AA, "M", "γ"), + (0x1D6AB, "M", "δ"), + (0x1D6AC, "M", "ε"), + (0x1D6AD, "M", "ζ"), + (0x1D6AE, "M", "η"), + (0x1D6AF, "M", "θ"), + (0x1D6B0, "M", "ι"), + (0x1D6B1, "M", "κ"), + (0x1D6B2, "M", "λ"), + (0x1D6B3, "M", "μ"), + (0x1D6B4, "M", "ν"), + (0x1D6B5, "M", "ξ"), + (0x1D6B6, "M", "ο"), + (0x1D6B7, "M", "π"), + (0x1D6B8, "M", "ρ"), + (0x1D6B9, "M", "θ"), + (0x1D6BA, "M", "σ"), + ] + + +def _seg_69() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D6BB, "M", "τ"), + (0x1D6BC, "M", "υ"), + (0x1D6BD, "M", "φ"), + (0x1D6BE, "M", "χ"), + (0x1D6BF, "M", "ψ"), + (0x1D6C0, "M", "ω"), + (0x1D6C1, "M", "∇"), + (0x1D6C2, "M", "α"), + (0x1D6C3, "M", "β"), + (0x1D6C4, "M", "γ"), + (0x1D6C5, "M", "δ"), + (0x1D6C6, "M", "ε"), + (0x1D6C7, "M", "ζ"), + (0x1D6C8, "M", "η"), + (0x1D6C9, "M", "θ"), + (0x1D6CA, "M", "ι"), + (0x1D6CB, "M", "κ"), + (0x1D6CC, "M", "λ"), + (0x1D6CD, "M", "μ"), + (0x1D6CE, "M", "ν"), + (0x1D6CF, "M", "ξ"), + (0x1D6D0, "M", "ο"), + (0x1D6D1, "M", "π"), + (0x1D6D2, "M", "ρ"), + (0x1D6D3, "M", "σ"), + (0x1D6D5, "M", "τ"), + (0x1D6D6, "M", "υ"), + (0x1D6D7, "M", "φ"), + (0x1D6D8, "M", "χ"), + (0x1D6D9, "M", "ψ"), + (0x1D6DA, "M", "ω"), + (0x1D6DB, "M", "∂"), + (0x1D6DC, "M", "ε"), + (0x1D6DD, "M", "θ"), + (0x1D6DE, "M", "κ"), + (0x1D6DF, "M", "φ"), + (0x1D6E0, "M", "ρ"), + (0x1D6E1, "M", "π"), + (0x1D6E2, "M", "α"), + (0x1D6E3, "M", "β"), + (0x1D6E4, "M", "γ"), + (0x1D6E5, "M", "δ"), + (0x1D6E6, "M", "ε"), + (0x1D6E7, "M", "ζ"), + (0x1D6E8, "M", "η"), + (0x1D6E9, "M", "θ"), + (0x1D6EA, "M", "ι"), + (0x1D6EB, "M", "κ"), + (0x1D6EC, "M", "λ"), + (0x1D6ED, "M", "μ"), + (0x1D6EE, "M", "ν"), + (0x1D6EF, "M", "ξ"), + (0x1D6F0, "M", "ο"), + (0x1D6F1, "M", "π"), + (0x1D6F2, "M", "ρ"), + (0x1D6F3, "M", "θ"), + (0x1D6F4, "M", "σ"), + (0x1D6F5, "M", "τ"), + (0x1D6F6, "M", "υ"), + (0x1D6F7, "M", "φ"), + (0x1D6F8, "M", "χ"), + (0x1D6F9, "M", "ψ"), + (0x1D6FA, "M", "ω"), + (0x1D6FB, "M", "∇"), + (0x1D6FC, "M", "α"), + (0x1D6FD, "M", "β"), + (0x1D6FE, "M", "γ"), + (0x1D6FF, "M", "δ"), + (0x1D700, "M", "ε"), + (0x1D701, "M", "ζ"), + (0x1D702, "M", "η"), + (0x1D703, "M", "θ"), + (0x1D704, "M", "ι"), + (0x1D705, "M", "κ"), + (0x1D706, "M", "λ"), + (0x1D707, "M", "μ"), + (0x1D708, "M", "ν"), + (0x1D709, "M", "ξ"), + (0x1D70A, "M", "ο"), + (0x1D70B, "M", "π"), + (0x1D70C, "M", "ρ"), + (0x1D70D, "M", "σ"), + (0x1D70F, "M", "τ"), + (0x1D710, "M", "υ"), + (0x1D711, "M", "φ"), + (0x1D712, "M", "χ"), + (0x1D713, "M", "ψ"), + (0x1D714, "M", "ω"), + (0x1D715, "M", "∂"), + (0x1D716, "M", "ε"), + (0x1D717, "M", "θ"), + (0x1D718, "M", "κ"), + (0x1D719, "M", "φ"), + (0x1D71A, "M", "ρ"), + (0x1D71B, "M", "π"), + (0x1D71C, "M", "α"), + (0x1D71D, "M", "β"), + (0x1D71E, "M", "γ"), + (0x1D71F, "M", "δ"), + (0x1D720, "M", "ε"), + ] + + +def _seg_70() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D721, "M", "ζ"), + (0x1D722, "M", "η"), + (0x1D723, "M", "θ"), + (0x1D724, "M", "ι"), + (0x1D725, "M", "κ"), + (0x1D726, "M", "λ"), + (0x1D727, "M", "μ"), + (0x1D728, "M", "ν"), + (0x1D729, "M", "ξ"), + (0x1D72A, "M", "ο"), + (0x1D72B, "M", "π"), + (0x1D72C, "M", "ρ"), + (0x1D72D, "M", "θ"), + (0x1D72E, "M", "σ"), + (0x1D72F, "M", "τ"), + (0x1D730, "M", "υ"), + (0x1D731, "M", "φ"), + (0x1D732, "M", "χ"), + (0x1D733, "M", "ψ"), + (0x1D734, "M", "ω"), + (0x1D735, "M", "∇"), + (0x1D736, "M", "α"), + (0x1D737, "M", "β"), + (0x1D738, "M", "γ"), + (0x1D739, "M", "δ"), + (0x1D73A, "M", "ε"), + (0x1D73B, "M", "ζ"), + (0x1D73C, "M", "η"), + (0x1D73D, "M", "θ"), + (0x1D73E, "M", "ι"), + (0x1D73F, "M", "κ"), + (0x1D740, "M", "λ"), + (0x1D741, "M", "μ"), + (0x1D742, "M", "ν"), + (0x1D743, "M", "ξ"), + (0x1D744, "M", "ο"), + (0x1D745, "M", "π"), + (0x1D746, "M", "ρ"), + (0x1D747, "M", "σ"), + (0x1D749, "M", "τ"), + (0x1D74A, "M", "υ"), + (0x1D74B, "M", "φ"), + (0x1D74C, "M", "χ"), + (0x1D74D, "M", "ψ"), + (0x1D74E, "M", "ω"), + (0x1D74F, "M", "∂"), + (0x1D750, "M", "ε"), + (0x1D751, "M", "θ"), + (0x1D752, "M", "κ"), + (0x1D753, "M", "φ"), + (0x1D754, "M", "ρ"), + (0x1D755, "M", "π"), + (0x1D756, "M", "α"), + (0x1D757, "M", "β"), + (0x1D758, "M", "γ"), + (0x1D759, "M", "δ"), + (0x1D75A, "M", "ε"), + (0x1D75B, "M", "ζ"), + (0x1D75C, "M", "η"), + (0x1D75D, "M", "θ"), + (0x1D75E, "M", "ι"), + (0x1D75F, "M", "κ"), + (0x1D760, "M", "λ"), + (0x1D761, "M", "μ"), + (0x1D762, "M", "ν"), + (0x1D763, "M", "ξ"), + (0x1D764, "M", "ο"), + (0x1D765, "M", "π"), + (0x1D766, "M", "ρ"), + (0x1D767, "M", "θ"), + (0x1D768, "M", "σ"), + (0x1D769, "M", "τ"), + (0x1D76A, "M", "υ"), + (0x1D76B, "M", "φ"), + (0x1D76C, "M", "χ"), + (0x1D76D, "M", "ψ"), + (0x1D76E, "M", "ω"), + (0x1D76F, "M", "∇"), + (0x1D770, "M", "α"), + (0x1D771, "M", "β"), + (0x1D772, "M", "γ"), + (0x1D773, "M", "δ"), + (0x1D774, "M", "ε"), + (0x1D775, "M", "ζ"), + (0x1D776, "M", "η"), + (0x1D777, "M", "θ"), + (0x1D778, "M", "ι"), + (0x1D779, "M", "κ"), + (0x1D77A, "M", "λ"), + (0x1D77B, "M", "μ"), + (0x1D77C, "M", "ν"), + (0x1D77D, "M", "ξ"), + (0x1D77E, "M", "ο"), + (0x1D77F, "M", "π"), + (0x1D780, "M", "ρ"), + (0x1D781, "M", "σ"), + (0x1D783, "M", "τ"), + (0x1D784, "M", "υ"), + (0x1D785, "M", "φ"), + (0x1D786, "M", "χ"), + ] + + +def _seg_71() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D787, "M", "ψ"), + (0x1D788, "M", "ω"), + (0x1D789, "M", "∂"), + (0x1D78A, "M", "ε"), + (0x1D78B, "M", "θ"), + (0x1D78C, "M", "κ"), + (0x1D78D, "M", "φ"), + (0x1D78E, "M", "ρ"), + (0x1D78F, "M", "π"), + (0x1D790, "M", "α"), + (0x1D791, "M", "β"), + (0x1D792, "M", "γ"), + (0x1D793, "M", "δ"), + (0x1D794, "M", "ε"), + (0x1D795, "M", "ζ"), + (0x1D796, "M", "η"), + (0x1D797, "M", "θ"), + (0x1D798, "M", "ι"), + (0x1D799, "M", "κ"), + (0x1D79A, "M", "λ"), + (0x1D79B, "M", "μ"), + (0x1D79C, "M", "ν"), + (0x1D79D, "M", "ξ"), + (0x1D79E, "M", "ο"), + (0x1D79F, "M", "π"), + (0x1D7A0, "M", "ρ"), + (0x1D7A1, "M", "θ"), + (0x1D7A2, "M", "σ"), + (0x1D7A3, "M", "τ"), + (0x1D7A4, "M", "υ"), + (0x1D7A5, "M", "φ"), + (0x1D7A6, "M", "χ"), + (0x1D7A7, "M", "ψ"), + (0x1D7A8, "M", "ω"), + (0x1D7A9, "M", "∇"), + (0x1D7AA, "M", "α"), + (0x1D7AB, "M", "β"), + (0x1D7AC, "M", "γ"), + (0x1D7AD, "M", "δ"), + (0x1D7AE, "M", "ε"), + (0x1D7AF, "M", "ζ"), + (0x1D7B0, "M", "η"), + (0x1D7B1, "M", "θ"), + (0x1D7B2, "M", "ι"), + (0x1D7B3, "M", "κ"), + (0x1D7B4, "M", "λ"), + (0x1D7B5, "M", "μ"), + (0x1D7B6, "M", "ν"), + (0x1D7B7, "M", "ξ"), + (0x1D7B8, "M", "ο"), + (0x1D7B9, "M", "π"), + (0x1D7BA, "M", "ρ"), + (0x1D7BB, "M", "σ"), + (0x1D7BD, "M", "τ"), + (0x1D7BE, "M", "υ"), + (0x1D7BF, "M", "φ"), + (0x1D7C0, "M", "χ"), + (0x1D7C1, "M", "ψ"), + (0x1D7C2, "M", "ω"), + (0x1D7C3, "M", "∂"), + (0x1D7C4, "M", "ε"), + (0x1D7C5, "M", "θ"), + (0x1D7C6, "M", "κ"), + (0x1D7C7, "M", "φ"), + (0x1D7C8, "M", "ρ"), + (0x1D7C9, "M", "π"), + (0x1D7CA, "M", "ϝ"), + (0x1D7CC, "X"), + (0x1D7CE, "M", "0"), + (0x1D7CF, "M", "1"), + (0x1D7D0, "M", "2"), + (0x1D7D1, "M", "3"), + (0x1D7D2, "M", "4"), + (0x1D7D3, "M", "5"), + (0x1D7D4, "M", "6"), + (0x1D7D5, "M", "7"), + (0x1D7D6, "M", "8"), + (0x1D7D7, "M", "9"), + (0x1D7D8, "M", "0"), + (0x1D7D9, "M", "1"), + (0x1D7DA, "M", "2"), + (0x1D7DB, "M", "3"), + (0x1D7DC, "M", "4"), + (0x1D7DD, "M", "5"), + (0x1D7DE, "M", "6"), + (0x1D7DF, "M", "7"), + (0x1D7E0, "M", "8"), + (0x1D7E1, "M", "9"), + (0x1D7E2, "M", "0"), + (0x1D7E3, "M", "1"), + (0x1D7E4, "M", "2"), + (0x1D7E5, "M", "3"), + (0x1D7E6, "M", "4"), + (0x1D7E7, "M", "5"), + (0x1D7E8, "M", "6"), + (0x1D7E9, "M", "7"), + (0x1D7EA, "M", "8"), + (0x1D7EB, "M", "9"), + (0x1D7EC, "M", "0"), + (0x1D7ED, "M", "1"), + ] + + +def _seg_72() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1D7EE, "M", "2"), + (0x1D7EF, "M", "3"), + (0x1D7F0, "M", "4"), + (0x1D7F1, "M", "5"), + (0x1D7F2, "M", "6"), + (0x1D7F3, "M", "7"), + (0x1D7F4, "M", "8"), + (0x1D7F5, "M", "9"), + (0x1D7F6, "M", "0"), + (0x1D7F7, "M", "1"), + (0x1D7F8, "M", "2"), + (0x1D7F9, "M", "3"), + (0x1D7FA, "M", "4"), + (0x1D7FB, "M", "5"), + (0x1D7FC, "M", "6"), + (0x1D7FD, "M", "7"), + (0x1D7FE, "M", "8"), + (0x1D7FF, "M", "9"), + (0x1D800, "V"), + (0x1DA8C, "X"), + (0x1DA9B, "V"), + (0x1DAA0, "X"), + (0x1DAA1, "V"), + (0x1DAB0, "X"), + (0x1DF00, "V"), + (0x1DF1F, "X"), + (0x1DF25, "V"), + (0x1DF2B, "X"), + (0x1E000, "V"), + (0x1E007, "X"), + (0x1E008, "V"), + (0x1E019, "X"), + (0x1E01B, "V"), + (0x1E022, "X"), + (0x1E023, "V"), + (0x1E025, "X"), + (0x1E026, "V"), + (0x1E02B, "X"), + (0x1E030, "M", "а"), + (0x1E031, "M", "б"), + (0x1E032, "M", "в"), + (0x1E033, "M", "г"), + (0x1E034, "M", "д"), + (0x1E035, "M", "е"), + (0x1E036, "M", "ж"), + (0x1E037, "M", "з"), + (0x1E038, "M", "и"), + (0x1E039, "M", "к"), + (0x1E03A, "M", "л"), + (0x1E03B, "M", "м"), + (0x1E03C, "M", "о"), + (0x1E03D, "M", "п"), + (0x1E03E, "M", "р"), + (0x1E03F, "M", "с"), + (0x1E040, "M", "т"), + (0x1E041, "M", "у"), + (0x1E042, "M", "ф"), + (0x1E043, "M", "х"), + (0x1E044, "M", "ц"), + (0x1E045, "M", "ч"), + (0x1E046, "M", "ш"), + (0x1E047, "M", "ы"), + (0x1E048, "M", "э"), + (0x1E049, "M", "ю"), + (0x1E04A, "M", "ꚉ"), + (0x1E04B, "M", "ә"), + (0x1E04C, "M", "і"), + (0x1E04D, "M", "ј"), + (0x1E04E, "M", "ө"), + (0x1E04F, "M", "ү"), + (0x1E050, "M", "ӏ"), + (0x1E051, "M", "а"), + (0x1E052, "M", "б"), + (0x1E053, "M", "в"), + (0x1E054, "M", "г"), + (0x1E055, "M", "д"), + (0x1E056, "M", "е"), + (0x1E057, "M", "ж"), + (0x1E058, "M", "з"), + (0x1E059, "M", "и"), + (0x1E05A, "M", "к"), + (0x1E05B, "M", "л"), + (0x1E05C, "M", "о"), + (0x1E05D, "M", "п"), + (0x1E05E, "M", "с"), + (0x1E05F, "M", "у"), + (0x1E060, "M", "ф"), + (0x1E061, "M", "х"), + (0x1E062, "M", "ц"), + (0x1E063, "M", "ч"), + (0x1E064, "M", "ш"), + (0x1E065, "M", "ъ"), + (0x1E066, "M", "ы"), + (0x1E067, "M", "ґ"), + (0x1E068, "M", "і"), + (0x1E069, "M", "ѕ"), + (0x1E06A, "M", "џ"), + (0x1E06B, "M", "ҫ"), + (0x1E06C, "M", "ꙑ"), + (0x1E06D, "M", "ұ"), + ] + + +def _seg_73() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1E06E, "X"), + (0x1E08F, "V"), + (0x1E090, "X"), + (0x1E100, "V"), + (0x1E12D, "X"), + (0x1E130, "V"), + (0x1E13E, "X"), + (0x1E140, "V"), + (0x1E14A, "X"), + (0x1E14E, "V"), + (0x1E150, "X"), + (0x1E290, "V"), + (0x1E2AF, "X"), + (0x1E2C0, "V"), + (0x1E2FA, "X"), + (0x1E2FF, "V"), + (0x1E300, "X"), + (0x1E4D0, "V"), + (0x1E4FA, "X"), + (0x1E5D0, "V"), + (0x1E5FB, "X"), + (0x1E5FF, "V"), + (0x1E600, "X"), + (0x1E7E0, "V"), + (0x1E7E7, "X"), + (0x1E7E8, "V"), + (0x1E7EC, "X"), + (0x1E7ED, "V"), + (0x1E7EF, "X"), + (0x1E7F0, "V"), + (0x1E7FF, "X"), + (0x1E800, "V"), + (0x1E8C5, "X"), + (0x1E8C7, "V"), + (0x1E8D7, "X"), + (0x1E900, "M", "𞤢"), + (0x1E901, "M", "𞤣"), + (0x1E902, "M", "𞤤"), + (0x1E903, "M", "𞤥"), + (0x1E904, "M", "𞤦"), + (0x1E905, "M", "𞤧"), + (0x1E906, "M", "𞤨"), + (0x1E907, "M", "𞤩"), + (0x1E908, "M", "𞤪"), + (0x1E909, "M", "𞤫"), + (0x1E90A, "M", "𞤬"), + (0x1E90B, "M", "𞤭"), + (0x1E90C, "M", "𞤮"), + (0x1E90D, "M", "𞤯"), + (0x1E90E, "M", "𞤰"), + (0x1E90F, "M", "𞤱"), + (0x1E910, "M", "𞤲"), + (0x1E911, "M", "𞤳"), + (0x1E912, "M", "𞤴"), + (0x1E913, "M", "𞤵"), + (0x1E914, "M", "𞤶"), + (0x1E915, "M", "𞤷"), + (0x1E916, "M", "𞤸"), + (0x1E917, "M", "𞤹"), + (0x1E918, "M", "𞤺"), + (0x1E919, "M", "𞤻"), + (0x1E91A, "M", "𞤼"), + (0x1E91B, "M", "𞤽"), + (0x1E91C, "M", "𞤾"), + (0x1E91D, "M", "𞤿"), + (0x1E91E, "M", "𞥀"), + (0x1E91F, "M", "𞥁"), + (0x1E920, "M", "𞥂"), + (0x1E921, "M", "𞥃"), + (0x1E922, "V"), + (0x1E94C, "X"), + (0x1E950, "V"), + (0x1E95A, "X"), + (0x1E95E, "V"), + (0x1E960, "X"), + (0x1EC71, "V"), + (0x1ECB5, "X"), + (0x1ED01, "V"), + (0x1ED3E, "X"), + (0x1EE00, "M", "ا"), + (0x1EE01, "M", "ب"), + (0x1EE02, "M", "ج"), + (0x1EE03, "M", "د"), + (0x1EE04, "X"), + (0x1EE05, "M", "و"), + (0x1EE06, "M", "ز"), + (0x1EE07, "M", "ح"), + (0x1EE08, "M", "ط"), + (0x1EE09, "M", "ي"), + (0x1EE0A, "M", "ك"), + (0x1EE0B, "M", "ل"), + (0x1EE0C, "M", "م"), + (0x1EE0D, "M", "ن"), + (0x1EE0E, "M", "س"), + (0x1EE0F, "M", "ع"), + (0x1EE10, "M", "ف"), + (0x1EE11, "M", "ص"), + (0x1EE12, "M", "ق"), + (0x1EE13, "M", "ر"), + (0x1EE14, "M", "ش"), + ] + + +def _seg_74() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1EE15, "M", "ت"), + (0x1EE16, "M", "ث"), + (0x1EE17, "M", "خ"), + (0x1EE18, "M", "ذ"), + (0x1EE19, "M", "ض"), + (0x1EE1A, "M", "ظ"), + (0x1EE1B, "M", "غ"), + (0x1EE1C, "M", "ٮ"), + (0x1EE1D, "M", "ں"), + (0x1EE1E, "M", "ڡ"), + (0x1EE1F, "M", "ٯ"), + (0x1EE20, "X"), + (0x1EE21, "M", "ب"), + (0x1EE22, "M", "ج"), + (0x1EE23, "X"), + (0x1EE24, "M", "ه"), + (0x1EE25, "X"), + (0x1EE27, "M", "ح"), + (0x1EE28, "X"), + (0x1EE29, "M", "ي"), + (0x1EE2A, "M", "ك"), + (0x1EE2B, "M", "ل"), + (0x1EE2C, "M", "م"), + (0x1EE2D, "M", "ن"), + (0x1EE2E, "M", "س"), + (0x1EE2F, "M", "ع"), + (0x1EE30, "M", "ف"), + (0x1EE31, "M", "ص"), + (0x1EE32, "M", "ق"), + (0x1EE33, "X"), + (0x1EE34, "M", "ش"), + (0x1EE35, "M", "ت"), + (0x1EE36, "M", "ث"), + (0x1EE37, "M", "خ"), + (0x1EE38, "X"), + (0x1EE39, "M", "ض"), + (0x1EE3A, "X"), + (0x1EE3B, "M", "غ"), + (0x1EE3C, "X"), + (0x1EE42, "M", "ج"), + (0x1EE43, "X"), + (0x1EE47, "M", "ح"), + (0x1EE48, "X"), + (0x1EE49, "M", "ي"), + (0x1EE4A, "X"), + (0x1EE4B, "M", "ل"), + (0x1EE4C, "X"), + (0x1EE4D, "M", "ن"), + (0x1EE4E, "M", "س"), + (0x1EE4F, "M", "ع"), + (0x1EE50, "X"), + (0x1EE51, "M", "ص"), + (0x1EE52, "M", "ق"), + (0x1EE53, "X"), + (0x1EE54, "M", "ش"), + (0x1EE55, "X"), + (0x1EE57, "M", "خ"), + (0x1EE58, "X"), + (0x1EE59, "M", "ض"), + (0x1EE5A, "X"), + (0x1EE5B, "M", "غ"), + (0x1EE5C, "X"), + (0x1EE5D, "M", "ں"), + (0x1EE5E, "X"), + (0x1EE5F, "M", "ٯ"), + (0x1EE60, "X"), + (0x1EE61, "M", "ب"), + (0x1EE62, "M", "ج"), + (0x1EE63, "X"), + (0x1EE64, "M", "ه"), + (0x1EE65, "X"), + (0x1EE67, "M", "ح"), + (0x1EE68, "M", "ط"), + (0x1EE69, "M", "ي"), + (0x1EE6A, "M", "ك"), + (0x1EE6B, "X"), + (0x1EE6C, "M", "م"), + (0x1EE6D, "M", "ن"), + (0x1EE6E, "M", "س"), + (0x1EE6F, "M", "ع"), + (0x1EE70, "M", "ف"), + (0x1EE71, "M", "ص"), + (0x1EE72, "M", "ق"), + (0x1EE73, "X"), + (0x1EE74, "M", "ش"), + (0x1EE75, "M", "ت"), + (0x1EE76, "M", "ث"), + (0x1EE77, "M", "خ"), + (0x1EE78, "X"), + (0x1EE79, "M", "ض"), + (0x1EE7A, "M", "ظ"), + (0x1EE7B, "M", "غ"), + (0x1EE7C, "M", "ٮ"), + (0x1EE7D, "X"), + (0x1EE7E, "M", "ڡ"), + (0x1EE7F, "X"), + (0x1EE80, "M", "ا"), + (0x1EE81, "M", "ب"), + (0x1EE82, "M", "ج"), + (0x1EE83, "M", "د"), + ] + + +def _seg_75() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1EE84, "M", "ه"), + (0x1EE85, "M", "و"), + (0x1EE86, "M", "ز"), + (0x1EE87, "M", "ح"), + (0x1EE88, "M", "ط"), + (0x1EE89, "M", "ي"), + (0x1EE8A, "X"), + (0x1EE8B, "M", "ل"), + (0x1EE8C, "M", "م"), + (0x1EE8D, "M", "ن"), + (0x1EE8E, "M", "س"), + (0x1EE8F, "M", "ع"), + (0x1EE90, "M", "ف"), + (0x1EE91, "M", "ص"), + (0x1EE92, "M", "ق"), + (0x1EE93, "M", "ر"), + (0x1EE94, "M", "ش"), + (0x1EE95, "M", "ت"), + (0x1EE96, "M", "ث"), + (0x1EE97, "M", "خ"), + (0x1EE98, "M", "ذ"), + (0x1EE99, "M", "ض"), + (0x1EE9A, "M", "ظ"), + (0x1EE9B, "M", "غ"), + (0x1EE9C, "X"), + (0x1EEA1, "M", "ب"), + (0x1EEA2, "M", "ج"), + (0x1EEA3, "M", "د"), + (0x1EEA4, "X"), + (0x1EEA5, "M", "و"), + (0x1EEA6, "M", "ز"), + (0x1EEA7, "M", "ح"), + (0x1EEA8, "M", "ط"), + (0x1EEA9, "M", "ي"), + (0x1EEAA, "X"), + (0x1EEAB, "M", "ل"), + (0x1EEAC, "M", "م"), + (0x1EEAD, "M", "ن"), + (0x1EEAE, "M", "س"), + (0x1EEAF, "M", "ع"), + (0x1EEB0, "M", "ف"), + (0x1EEB1, "M", "ص"), + (0x1EEB2, "M", "ق"), + (0x1EEB3, "M", "ر"), + (0x1EEB4, "M", "ش"), + (0x1EEB5, "M", "ت"), + (0x1EEB6, "M", "ث"), + (0x1EEB7, "M", "خ"), + (0x1EEB8, "M", "ذ"), + (0x1EEB9, "M", "ض"), + (0x1EEBA, "M", "ظ"), + (0x1EEBB, "M", "غ"), + (0x1EEBC, "X"), + (0x1EEF0, "V"), + (0x1EEF2, "X"), + (0x1F000, "V"), + (0x1F02C, "X"), + (0x1F030, "V"), + (0x1F094, "X"), + (0x1F0A0, "V"), + (0x1F0AF, "X"), + (0x1F0B1, "V"), + (0x1F0C0, "X"), + (0x1F0C1, "V"), + (0x1F0D0, "X"), + (0x1F0D1, "V"), + (0x1F0F6, "X"), + (0x1F101, "M", "0,"), + (0x1F102, "M", "1,"), + (0x1F103, "M", "2,"), + (0x1F104, "M", "3,"), + (0x1F105, "M", "4,"), + (0x1F106, "M", "5,"), + (0x1F107, "M", "6,"), + (0x1F108, "M", "7,"), + (0x1F109, "M", "8,"), + (0x1F10A, "M", "9,"), + (0x1F10B, "V"), + (0x1F110, "M", "(a)"), + (0x1F111, "M", "(b)"), + (0x1F112, "M", "(c)"), + (0x1F113, "M", "(d)"), + (0x1F114, "M", "(e)"), + (0x1F115, "M", "(f)"), + (0x1F116, "M", "(g)"), + (0x1F117, "M", "(h)"), + (0x1F118, "M", "(i)"), + (0x1F119, "M", "(j)"), + (0x1F11A, "M", "(k)"), + (0x1F11B, "M", "(l)"), + (0x1F11C, "M", "(m)"), + (0x1F11D, "M", "(n)"), + (0x1F11E, "M", "(o)"), + (0x1F11F, "M", "(p)"), + (0x1F120, "M", "(q)"), + (0x1F121, "M", "(r)"), + (0x1F122, "M", "(s)"), + (0x1F123, "M", "(t)"), + (0x1F124, "M", "(u)"), + (0x1F125, "M", "(v)"), + ] + + +def _seg_76() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1F126, "M", "(w)"), + (0x1F127, "M", "(x)"), + (0x1F128, "M", "(y)"), + (0x1F129, "M", "(z)"), + (0x1F12A, "M", "〔s〕"), + (0x1F12B, "M", "c"), + (0x1F12C, "M", "r"), + (0x1F12D, "M", "cd"), + (0x1F12E, "M", "wz"), + (0x1F12F, "V"), + (0x1F130, "M", "a"), + (0x1F131, "M", "b"), + (0x1F132, "M", "c"), + (0x1F133, "M", "d"), + (0x1F134, "M", "e"), + (0x1F135, "M", "f"), + (0x1F136, "M", "g"), + (0x1F137, "M", "h"), + (0x1F138, "M", "i"), + (0x1F139, "M", "j"), + (0x1F13A, "M", "k"), + (0x1F13B, "M", "l"), + (0x1F13C, "M", "m"), + (0x1F13D, "M", "n"), + (0x1F13E, "M", "o"), + (0x1F13F, "M", "p"), + (0x1F140, "M", "q"), + (0x1F141, "M", "r"), + (0x1F142, "M", "s"), + (0x1F143, "M", "t"), + (0x1F144, "M", "u"), + (0x1F145, "M", "v"), + (0x1F146, "M", "w"), + (0x1F147, "M", "x"), + (0x1F148, "M", "y"), + (0x1F149, "M", "z"), + (0x1F14A, "M", "hv"), + (0x1F14B, "M", "mv"), + (0x1F14C, "M", "sd"), + (0x1F14D, "M", "ss"), + (0x1F14E, "M", "ppv"), + (0x1F14F, "M", "wc"), + (0x1F150, "V"), + (0x1F16A, "M", "mc"), + (0x1F16B, "M", "md"), + (0x1F16C, "M", "mr"), + (0x1F16D, "V"), + (0x1F190, "M", "dj"), + (0x1F191, "V"), + (0x1F1AE, "X"), + (0x1F1E6, "V"), + (0x1F200, "M", "ほか"), + (0x1F201, "M", "ココ"), + (0x1F202, "M", "サ"), + (0x1F203, "X"), + (0x1F210, "M", "手"), + (0x1F211, "M", "字"), + (0x1F212, "M", "双"), + (0x1F213, "M", "デ"), + (0x1F214, "M", "二"), + (0x1F215, "M", "多"), + (0x1F216, "M", "解"), + (0x1F217, "M", "天"), + (0x1F218, "M", "交"), + (0x1F219, "M", "映"), + (0x1F21A, "M", "無"), + (0x1F21B, "M", "料"), + (0x1F21C, "M", "前"), + (0x1F21D, "M", "後"), + (0x1F21E, "M", "再"), + (0x1F21F, "M", "新"), + (0x1F220, "M", "初"), + (0x1F221, "M", "終"), + (0x1F222, "M", "生"), + (0x1F223, "M", "販"), + (0x1F224, "M", "声"), + (0x1F225, "M", "吹"), + (0x1F226, "M", "演"), + (0x1F227, "M", "投"), + (0x1F228, "M", "捕"), + (0x1F229, "M", "一"), + (0x1F22A, "M", "三"), + (0x1F22B, "M", "遊"), + (0x1F22C, "M", "左"), + (0x1F22D, "M", "中"), + (0x1F22E, "M", "右"), + (0x1F22F, "M", "指"), + (0x1F230, "M", "走"), + (0x1F231, "M", "打"), + (0x1F232, "M", "禁"), + (0x1F233, "M", "空"), + (0x1F234, "M", "合"), + (0x1F235, "M", "満"), + (0x1F236, "M", "有"), + (0x1F237, "M", "月"), + (0x1F238, "M", "申"), + (0x1F239, "M", "割"), + (0x1F23A, "M", "営"), + (0x1F23B, "M", "配"), + (0x1F23C, "X"), + ] + + +def _seg_77() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x1F240, "M", "〔本〕"), + (0x1F241, "M", "〔三〕"), + (0x1F242, "M", "〔二〕"), + (0x1F243, "M", "〔安〕"), + (0x1F244, "M", "〔点〕"), + (0x1F245, "M", "〔打〕"), + (0x1F246, "M", "〔盗〕"), + (0x1F247, "M", "〔勝〕"), + (0x1F248, "M", "〔敗〕"), + (0x1F249, "X"), + (0x1F250, "M", "得"), + (0x1F251, "M", "可"), + (0x1F252, "X"), + (0x1F260, "V"), + (0x1F266, "X"), + (0x1F300, "V"), + (0x1F6D8, "X"), + (0x1F6DC, "V"), + (0x1F6ED, "X"), + (0x1F6F0, "V"), + (0x1F6FD, "X"), + (0x1F700, "V"), + (0x1F777, "X"), + (0x1F77B, "V"), + (0x1F7DA, "X"), + (0x1F7E0, "V"), + (0x1F7EC, "X"), + (0x1F7F0, "V"), + (0x1F7F1, "X"), + (0x1F800, "V"), + (0x1F80C, "X"), + (0x1F810, "V"), + (0x1F848, "X"), + (0x1F850, "V"), + (0x1F85A, "X"), + (0x1F860, "V"), + (0x1F888, "X"), + (0x1F890, "V"), + (0x1F8AE, "X"), + (0x1F8B0, "V"), + (0x1F8BC, "X"), + (0x1F8C0, "V"), + (0x1F8C2, "X"), + (0x1F900, "V"), + (0x1FA54, "X"), + (0x1FA60, "V"), + (0x1FA6E, "X"), + (0x1FA70, "V"), + (0x1FA7D, "X"), + (0x1FA80, "V"), + (0x1FA8A, "X"), + (0x1FA8F, "V"), + (0x1FAC7, "X"), + (0x1FACE, "V"), + (0x1FADD, "X"), + (0x1FADF, "V"), + (0x1FAEA, "X"), + (0x1FAF0, "V"), + (0x1FAF9, "X"), + (0x1FB00, "V"), + (0x1FB93, "X"), + (0x1FB94, "V"), + (0x1FBF0, "M", "0"), + (0x1FBF1, "M", "1"), + (0x1FBF2, "M", "2"), + (0x1FBF3, "M", "3"), + (0x1FBF4, "M", "4"), + (0x1FBF5, "M", "5"), + (0x1FBF6, "M", "6"), + (0x1FBF7, "M", "7"), + (0x1FBF8, "M", "8"), + (0x1FBF9, "M", "9"), + (0x1FBFA, "X"), + (0x20000, "V"), + (0x2A6E0, "X"), + (0x2A700, "V"), + (0x2B73A, "X"), + (0x2B740, "V"), + (0x2B81E, "X"), + (0x2B820, "V"), + (0x2CEA2, "X"), + (0x2CEB0, "V"), + (0x2EBE1, "X"), + (0x2EBF0, "V"), + (0x2EE5E, "X"), + (0x2F800, "M", "丽"), + (0x2F801, "M", "丸"), + (0x2F802, "M", "乁"), + (0x2F803, "M", "𠄢"), + (0x2F804, "M", "你"), + (0x2F805, "M", "侮"), + (0x2F806, "M", "侻"), + (0x2F807, "M", "倂"), + (0x2F808, "M", "偺"), + (0x2F809, "M", "備"), + (0x2F80A, "M", "僧"), + (0x2F80B, "M", "像"), + (0x2F80C, "M", "㒞"), + (0x2F80D, "M", "𠘺"), + (0x2F80E, "M", "免"), + ] + + +def _seg_78() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F80F, "M", "兔"), + (0x2F810, "M", "兤"), + (0x2F811, "M", "具"), + (0x2F812, "M", "𠔜"), + (0x2F813, "M", "㒹"), + (0x2F814, "M", "內"), + (0x2F815, "M", "再"), + (0x2F816, "M", "𠕋"), + (0x2F817, "M", "冗"), + (0x2F818, "M", "冤"), + (0x2F819, "M", "仌"), + (0x2F81A, "M", "冬"), + (0x2F81B, "M", "况"), + (0x2F81C, "M", "𩇟"), + (0x2F81D, "M", "凵"), + (0x2F81E, "M", "刃"), + (0x2F81F, "M", "㓟"), + (0x2F820, "M", "刻"), + (0x2F821, "M", "剆"), + (0x2F822, "M", "割"), + (0x2F823, "M", "剷"), + (0x2F824, "M", "㔕"), + (0x2F825, "M", "勇"), + (0x2F826, "M", "勉"), + (0x2F827, "M", "勤"), + (0x2F828, "M", "勺"), + (0x2F829, "M", "包"), + (0x2F82A, "M", "匆"), + (0x2F82B, "M", "北"), + (0x2F82C, "M", "卉"), + (0x2F82D, "M", "卑"), + (0x2F82E, "M", "博"), + (0x2F82F, "M", "即"), + (0x2F830, "M", "卽"), + (0x2F831, "M", "卿"), + (0x2F834, "M", "𠨬"), + (0x2F835, "M", "灰"), + (0x2F836, "M", "及"), + (0x2F837, "M", "叟"), + (0x2F838, "M", "𠭣"), + (0x2F839, "M", "叫"), + (0x2F83A, "M", "叱"), + (0x2F83B, "M", "吆"), + (0x2F83C, "M", "咞"), + (0x2F83D, "M", "吸"), + (0x2F83E, "M", "呈"), + (0x2F83F, "M", "周"), + (0x2F840, "M", "咢"), + (0x2F841, "M", "哶"), + (0x2F842, "M", "唐"), + (0x2F843, "M", "啓"), + (0x2F844, "M", "啣"), + (0x2F845, "M", "善"), + (0x2F847, "M", "喙"), + (0x2F848, "M", "喫"), + (0x2F849, "M", "喳"), + (0x2F84A, "M", "嗂"), + (0x2F84B, "M", "圖"), + (0x2F84C, "M", "嘆"), + (0x2F84D, "M", "圗"), + (0x2F84E, "M", "噑"), + (0x2F84F, "M", "噴"), + (0x2F850, "M", "切"), + (0x2F851, "M", "壮"), + (0x2F852, "M", "城"), + (0x2F853, "M", "埴"), + (0x2F854, "M", "堍"), + (0x2F855, "M", "型"), + (0x2F856, "M", "堲"), + (0x2F857, "M", "報"), + (0x2F858, "M", "墬"), + (0x2F859, "M", "𡓤"), + (0x2F85A, "M", "売"), + (0x2F85B, "M", "壷"), + (0x2F85C, "M", "夆"), + (0x2F85D, "M", "多"), + (0x2F85E, "M", "夢"), + (0x2F85F, "M", "奢"), + (0x2F860, "M", "𡚨"), + (0x2F861, "M", "𡛪"), + (0x2F862, "M", "姬"), + (0x2F863, "M", "娛"), + (0x2F864, "M", "娧"), + (0x2F865, "M", "姘"), + (0x2F866, "M", "婦"), + (0x2F867, "M", "㛮"), + (0x2F868, "M", "㛼"), + (0x2F869, "M", "嬈"), + (0x2F86A, "M", "嬾"), + (0x2F86C, "M", "𡧈"), + (0x2F86D, "M", "寃"), + (0x2F86E, "M", "寘"), + (0x2F86F, "M", "寧"), + (0x2F870, "M", "寳"), + (0x2F871, "M", "𡬘"), + (0x2F872, "M", "寿"), + (0x2F873, "M", "将"), + (0x2F874, "M", "当"), + (0x2F875, "M", "尢"), + (0x2F876, "M", "㞁"), + ] + + +def _seg_79() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F877, "M", "屠"), + (0x2F878, "M", "屮"), + (0x2F879, "M", "峀"), + (0x2F87A, "M", "岍"), + (0x2F87B, "M", "𡷤"), + (0x2F87C, "M", "嵃"), + (0x2F87D, "M", "𡷦"), + (0x2F87E, "M", "嵮"), + (0x2F87F, "M", "嵫"), + (0x2F880, "M", "嵼"), + (0x2F881, "M", "巡"), + (0x2F882, "M", "巢"), + (0x2F883, "M", "㠯"), + (0x2F884, "M", "巽"), + (0x2F885, "M", "帨"), + (0x2F886, "M", "帽"), + (0x2F887, "M", "幩"), + (0x2F888, "M", "㡢"), + (0x2F889, "M", "𢆃"), + (0x2F88A, "M", "㡼"), + (0x2F88B, "M", "庰"), + (0x2F88C, "M", "庳"), + (0x2F88D, "M", "庶"), + (0x2F88E, "M", "廊"), + (0x2F88F, "M", "𪎒"), + (0x2F890, "M", "廾"), + (0x2F891, "M", "𢌱"), + (0x2F893, "M", "舁"), + (0x2F894, "M", "弢"), + (0x2F896, "M", "㣇"), + (0x2F897, "M", "𣊸"), + (0x2F898, "M", "𦇚"), + (0x2F899, "M", "形"), + (0x2F89A, "M", "彫"), + (0x2F89B, "M", "㣣"), + (0x2F89C, "M", "徚"), + (0x2F89D, "M", "忍"), + (0x2F89E, "M", "志"), + (0x2F89F, "M", "忹"), + (0x2F8A0, "M", "悁"), + (0x2F8A1, "M", "㤺"), + (0x2F8A2, "M", "㤜"), + (0x2F8A3, "M", "悔"), + (0x2F8A4, "M", "𢛔"), + (0x2F8A5, "M", "惇"), + (0x2F8A6, "M", "慈"), + (0x2F8A7, "M", "慌"), + (0x2F8A8, "M", "慎"), + (0x2F8A9, "M", "慌"), + (0x2F8AA, "M", "慺"), + (0x2F8AB, "M", "憎"), + (0x2F8AC, "M", "憲"), + (0x2F8AD, "M", "憤"), + (0x2F8AE, "M", "憯"), + (0x2F8AF, "M", "懞"), + (0x2F8B0, "M", "懲"), + (0x2F8B1, "M", "懶"), + (0x2F8B2, "M", "成"), + (0x2F8B3, "M", "戛"), + (0x2F8B4, "M", "扝"), + (0x2F8B5, "M", "抱"), + (0x2F8B6, "M", "拔"), + (0x2F8B7, "M", "捐"), + (0x2F8B8, "M", "𢬌"), + (0x2F8B9, "M", "挽"), + (0x2F8BA, "M", "拼"), + (0x2F8BB, "M", "捨"), + (0x2F8BC, "M", "掃"), + (0x2F8BD, "M", "揤"), + (0x2F8BE, "M", "𢯱"), + (0x2F8BF, "M", "搢"), + (0x2F8C0, "M", "揅"), + (0x2F8C1, "M", "掩"), + (0x2F8C2, "M", "㨮"), + (0x2F8C3, "M", "摩"), + (0x2F8C4, "M", "摾"), + (0x2F8C5, "M", "撝"), + (0x2F8C6, "M", "摷"), + (0x2F8C7, "M", "㩬"), + (0x2F8C8, "M", "敏"), + (0x2F8C9, "M", "敬"), + (0x2F8CA, "M", "𣀊"), + (0x2F8CB, "M", "旣"), + (0x2F8CC, "M", "書"), + (0x2F8CD, "M", "晉"), + (0x2F8CE, "M", "㬙"), + (0x2F8CF, "M", "暑"), + (0x2F8D0, "M", "㬈"), + (0x2F8D1, "M", "㫤"), + (0x2F8D2, "M", "冒"), + (0x2F8D3, "M", "冕"), + (0x2F8D4, "M", "最"), + (0x2F8D5, "M", "暜"), + (0x2F8D6, "M", "肭"), + (0x2F8D7, "M", "䏙"), + (0x2F8D8, "M", "朗"), + (0x2F8D9, "M", "望"), + (0x2F8DA, "M", "朡"), + (0x2F8DB, "M", "杞"), + (0x2F8DC, "M", "杓"), + ] + + +def _seg_80() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F8DD, "M", "𣏃"), + (0x2F8DE, "M", "㭉"), + (0x2F8DF, "M", "柺"), + (0x2F8E0, "M", "枅"), + (0x2F8E1, "M", "桒"), + (0x2F8E2, "M", "梅"), + (0x2F8E3, "M", "𣑭"), + (0x2F8E4, "M", "梎"), + (0x2F8E5, "M", "栟"), + (0x2F8E6, "M", "椔"), + (0x2F8E7, "M", "㮝"), + (0x2F8E8, "M", "楂"), + (0x2F8E9, "M", "榣"), + (0x2F8EA, "M", "槪"), + (0x2F8EB, "M", "檨"), + (0x2F8EC, "M", "𣚣"), + (0x2F8ED, "M", "櫛"), + (0x2F8EE, "M", "㰘"), + (0x2F8EF, "M", "次"), + (0x2F8F0, "M", "𣢧"), + (0x2F8F1, "M", "歔"), + (0x2F8F2, "M", "㱎"), + (0x2F8F3, "M", "歲"), + (0x2F8F4, "M", "殟"), + (0x2F8F5, "M", "殺"), + (0x2F8F6, "M", "殻"), + (0x2F8F7, "M", "𣪍"), + (0x2F8F8, "M", "𡴋"), + (0x2F8F9, "M", "𣫺"), + (0x2F8FA, "M", "汎"), + (0x2F8FB, "M", "𣲼"), + (0x2F8FC, "M", "沿"), + (0x2F8FD, "M", "泍"), + (0x2F8FE, "M", "汧"), + (0x2F8FF, "M", "洖"), + (0x2F900, "M", "派"), + (0x2F901, "M", "海"), + (0x2F902, "M", "流"), + (0x2F903, "M", "浩"), + (0x2F904, "M", "浸"), + (0x2F905, "M", "涅"), + (0x2F906, "M", "𣴞"), + (0x2F907, "M", "洴"), + (0x2F908, "M", "港"), + (0x2F909, "M", "湮"), + (0x2F90A, "M", "㴳"), + (0x2F90B, "M", "滋"), + (0x2F90C, "M", "滇"), + (0x2F90D, "M", "𣻑"), + (0x2F90E, "M", "淹"), + (0x2F90F, "M", "潮"), + (0x2F910, "M", "𣽞"), + (0x2F911, "M", "𣾎"), + (0x2F912, "M", "濆"), + (0x2F913, "M", "瀹"), + (0x2F914, "M", "瀞"), + (0x2F915, "M", "瀛"), + (0x2F916, "M", "㶖"), + (0x2F917, "M", "灊"), + (0x2F918, "M", "災"), + (0x2F919, "M", "灷"), + (0x2F91A, "M", "炭"), + (0x2F91B, "M", "𠔥"), + (0x2F91C, "M", "煅"), + (0x2F91D, "M", "𤉣"), + (0x2F91E, "M", "熜"), + (0x2F91F, "M", "𤎫"), + (0x2F920, "M", "爨"), + (0x2F921, "M", "爵"), + (0x2F922, "M", "牐"), + (0x2F923, "M", "𤘈"), + (0x2F924, "M", "犀"), + (0x2F925, "M", "犕"), + (0x2F926, "M", "𤜵"), + (0x2F927, "M", "𤠔"), + (0x2F928, "M", "獺"), + (0x2F929, "M", "王"), + (0x2F92A, "M", "㺬"), + (0x2F92B, "M", "玥"), + (0x2F92C, "M", "㺸"), + (0x2F92E, "M", "瑇"), + (0x2F92F, "M", "瑜"), + (0x2F930, "M", "瑱"), + (0x2F931, "M", "璅"), + (0x2F932, "M", "瓊"), + (0x2F933, "M", "㼛"), + (0x2F934, "M", "甤"), + (0x2F935, "M", "𤰶"), + (0x2F936, "M", "甾"), + (0x2F937, "M", "𤲒"), + (0x2F938, "M", "異"), + (0x2F939, "M", "𢆟"), + (0x2F93A, "M", "瘐"), + (0x2F93B, "M", "𤾡"), + (0x2F93C, "M", "𤾸"), + (0x2F93D, "M", "𥁄"), + (0x2F93E, "M", "㿼"), + (0x2F93F, "M", "䀈"), + (0x2F940, "M", "直"), + (0x2F941, "M", "𥃳"), + ] + + +def _seg_81() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F942, "M", "𥃲"), + (0x2F943, "M", "𥄙"), + (0x2F944, "M", "𥄳"), + (0x2F945, "M", "眞"), + (0x2F946, "M", "真"), + (0x2F948, "M", "睊"), + (0x2F949, "M", "䀹"), + (0x2F94A, "M", "瞋"), + (0x2F94B, "M", "䁆"), + (0x2F94C, "M", "䂖"), + (0x2F94D, "M", "𥐝"), + (0x2F94E, "M", "硎"), + (0x2F94F, "M", "碌"), + (0x2F950, "M", "磌"), + (0x2F951, "M", "䃣"), + (0x2F952, "M", "𥘦"), + (0x2F953, "M", "祖"), + (0x2F954, "M", "𥚚"), + (0x2F955, "M", "𥛅"), + (0x2F956, "M", "福"), + (0x2F957, "M", "秫"), + (0x2F958, "M", "䄯"), + (0x2F959, "M", "穀"), + (0x2F95A, "M", "穊"), + (0x2F95B, "M", "穏"), + (0x2F95C, "M", "𥥼"), + (0x2F95D, "M", "𥪧"), + (0x2F95F, "M", "竮"), + (0x2F960, "M", "䈂"), + (0x2F961, "M", "𥮫"), + (0x2F962, "M", "篆"), + (0x2F963, "M", "築"), + (0x2F964, "M", "䈧"), + (0x2F965, "M", "𥲀"), + (0x2F966, "M", "糒"), + (0x2F967, "M", "䊠"), + (0x2F968, "M", "糨"), + (0x2F969, "M", "糣"), + (0x2F96A, "M", "紀"), + (0x2F96B, "M", "𥾆"), + (0x2F96C, "M", "絣"), + (0x2F96D, "M", "䌁"), + (0x2F96E, "M", "緇"), + (0x2F96F, "M", "縂"), + (0x2F970, "M", "繅"), + (0x2F971, "M", "䌴"), + (0x2F972, "M", "𦈨"), + (0x2F973, "M", "𦉇"), + (0x2F974, "M", "䍙"), + (0x2F975, "M", "𦋙"), + (0x2F976, "M", "罺"), + (0x2F977, "M", "𦌾"), + (0x2F978, "M", "羕"), + (0x2F979, "M", "翺"), + (0x2F97A, "M", "者"), + (0x2F97B, "M", "𦓚"), + (0x2F97C, "M", "𦔣"), + (0x2F97D, "M", "聠"), + (0x2F97E, "M", "𦖨"), + (0x2F97F, "M", "聰"), + (0x2F980, "M", "𣍟"), + (0x2F981, "M", "䏕"), + (0x2F982, "M", "育"), + (0x2F983, "M", "脃"), + (0x2F984, "M", "䐋"), + (0x2F985, "M", "脾"), + (0x2F986, "M", "媵"), + (0x2F987, "M", "𦞧"), + (0x2F988, "M", "𦞵"), + (0x2F989, "M", "𣎓"), + (0x2F98A, "M", "𣎜"), + (0x2F98B, "M", "舁"), + (0x2F98C, "M", "舄"), + (0x2F98D, "M", "辞"), + (0x2F98E, "M", "䑫"), + (0x2F98F, "M", "芑"), + (0x2F990, "M", "芋"), + (0x2F991, "M", "芝"), + (0x2F992, "M", "劳"), + (0x2F993, "M", "花"), + (0x2F994, "M", "芳"), + (0x2F995, "M", "芽"), + (0x2F996, "M", "苦"), + (0x2F997, "M", "𦬼"), + (0x2F998, "M", "若"), + (0x2F999, "M", "茝"), + (0x2F99A, "M", "荣"), + (0x2F99B, "M", "莭"), + (0x2F99C, "M", "茣"), + (0x2F99D, "M", "莽"), + (0x2F99E, "M", "菧"), + (0x2F99F, "M", "著"), + (0x2F9A0, "M", "荓"), + (0x2F9A1, "M", "菊"), + (0x2F9A2, "M", "菌"), + (0x2F9A3, "M", "菜"), + (0x2F9A4, "M", "𦰶"), + (0x2F9A5, "M", "𦵫"), + (0x2F9A6, "M", "𦳕"), + (0x2F9A7, "M", "䔫"), + ] + + +def _seg_82() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2F9A8, "M", "蓱"), + (0x2F9A9, "M", "蓳"), + (0x2F9AA, "M", "蔖"), + (0x2F9AB, "M", "𧏊"), + (0x2F9AC, "M", "蕤"), + (0x2F9AD, "M", "𦼬"), + (0x2F9AE, "M", "䕝"), + (0x2F9AF, "M", "䕡"), + (0x2F9B0, "M", "𦾱"), + (0x2F9B1, "M", "𧃒"), + (0x2F9B2, "M", "䕫"), + (0x2F9B3, "M", "虐"), + (0x2F9B4, "M", "虜"), + (0x2F9B5, "M", "虧"), + (0x2F9B6, "M", "虩"), + (0x2F9B7, "M", "蚩"), + (0x2F9B8, "M", "蚈"), + (0x2F9B9, "M", "蜎"), + (0x2F9BA, "M", "蛢"), + (0x2F9BB, "M", "蝹"), + (0x2F9BC, "M", "蜨"), + (0x2F9BD, "M", "蝫"), + (0x2F9BE, "M", "螆"), + (0x2F9BF, "M", "䗗"), + (0x2F9C0, "M", "蟡"), + (0x2F9C1, "M", "蠁"), + (0x2F9C2, "M", "䗹"), + (0x2F9C3, "M", "衠"), + (0x2F9C4, "M", "衣"), + (0x2F9C5, "M", "𧙧"), + (0x2F9C6, "M", "裗"), + (0x2F9C7, "M", "裞"), + (0x2F9C8, "M", "䘵"), + (0x2F9C9, "M", "裺"), + (0x2F9CA, "M", "㒻"), + (0x2F9CB, "M", "𧢮"), + (0x2F9CC, "M", "𧥦"), + (0x2F9CD, "M", "䚾"), + (0x2F9CE, "M", "䛇"), + (0x2F9CF, "M", "誠"), + (0x2F9D0, "M", "諭"), + (0x2F9D1, "M", "變"), + (0x2F9D2, "M", "豕"), + (0x2F9D3, "M", "𧲨"), + (0x2F9D4, "M", "貫"), + (0x2F9D5, "M", "賁"), + (0x2F9D6, "M", "贛"), + (0x2F9D7, "M", "起"), + (0x2F9D8, "M", "𧼯"), + (0x2F9D9, "M", "𠠄"), + (0x2F9DA, "M", "跋"), + (0x2F9DB, "M", "趼"), + (0x2F9DC, "M", "跰"), + (0x2F9DD, "M", "𠣞"), + (0x2F9DE, "M", "軔"), + (0x2F9DF, "M", "輸"), + (0x2F9E0, "M", "𨗒"), + (0x2F9E1, "M", "𨗭"), + (0x2F9E2, "M", "邔"), + (0x2F9E3, "M", "郱"), + (0x2F9E4, "M", "鄑"), + (0x2F9E5, "M", "𨜮"), + (0x2F9E6, "M", "鄛"), + (0x2F9E7, "M", "鈸"), + (0x2F9E8, "M", "鋗"), + (0x2F9E9, "M", "鋘"), + (0x2F9EA, "M", "鉼"), + (0x2F9EB, "M", "鏹"), + (0x2F9EC, "M", "鐕"), + (0x2F9ED, "M", "𨯺"), + (0x2F9EE, "M", "開"), + (0x2F9EF, "M", "䦕"), + (0x2F9F0, "M", "閷"), + (0x2F9F1, "M", "𨵷"), + (0x2F9F2, "M", "䧦"), + (0x2F9F3, "M", "雃"), + (0x2F9F4, "M", "嶲"), + (0x2F9F5, "M", "霣"), + (0x2F9F6, "M", "𩅅"), + (0x2F9F7, "M", "𩈚"), + (0x2F9F8, "M", "䩮"), + (0x2F9F9, "M", "䩶"), + (0x2F9FA, "M", "韠"), + (0x2F9FB, "M", "𩐊"), + (0x2F9FC, "M", "䪲"), + (0x2F9FD, "M", "𩒖"), + (0x2F9FE, "M", "頋"), + (0x2FA00, "M", "頩"), + (0x2FA01, "M", "𩖶"), + (0x2FA02, "M", "飢"), + (0x2FA03, "M", "䬳"), + (0x2FA04, "M", "餩"), + (0x2FA05, "M", "馧"), + (0x2FA06, "M", "駂"), + (0x2FA07, "M", "駾"), + (0x2FA08, "M", "䯎"), + (0x2FA09, "M", "𩬰"), + (0x2FA0A, "M", "鬒"), + (0x2FA0B, "M", "鱀"), + (0x2FA0C, "M", "鳽"), + ] + + +def _seg_83() -> List[Union[Tuple[int, str], Tuple[int, str, str]]]: + return [ + (0x2FA0D, "M", "䳎"), + (0x2FA0E, "M", "䳭"), + (0x2FA0F, "M", "鵧"), + (0x2FA10, "M", "𪃎"), + (0x2FA11, "M", "䳸"), + (0x2FA12, "M", "𪄅"), + (0x2FA13, "M", "𪈎"), + (0x2FA14, "M", "𪊑"), + (0x2FA15, "M", "麻"), + (0x2FA16, "M", "䵖"), + (0x2FA17, "M", "黹"), + (0x2FA18, "M", "黾"), + (0x2FA19, "M", "鼅"), + (0x2FA1A, "M", "鼏"), + (0x2FA1B, "M", "鼖"), + (0x2FA1C, "M", "鼻"), + (0x2FA1D, "M", "𪘀"), + (0x2FA1E, "X"), + (0x30000, "V"), + (0x3134B, "X"), + (0x31350, "V"), + (0x323B0, "X"), + (0xE0100, "I"), + (0xE01F0, "X"), + ] + + +uts46data = tuple( + _seg_0() + + _seg_1() + + _seg_2() + + _seg_3() + + _seg_4() + + _seg_5() + + _seg_6() + + _seg_7() + + _seg_8() + + _seg_9() + + _seg_10() + + _seg_11() + + _seg_12() + + _seg_13() + + _seg_14() + + _seg_15() + + _seg_16() + + _seg_17() + + _seg_18() + + _seg_19() + + _seg_20() + + _seg_21() + + _seg_22() + + _seg_23() + + _seg_24() + + _seg_25() + + _seg_26() + + _seg_27() + + _seg_28() + + _seg_29() + + _seg_30() + + _seg_31() + + _seg_32() + + _seg_33() + + _seg_34() + + _seg_35() + + _seg_36() + + _seg_37() + + _seg_38() + + _seg_39() + + _seg_40() + + _seg_41() + + _seg_42() + + _seg_43() + + _seg_44() + + _seg_45() + + _seg_46() + + _seg_47() + + _seg_48() + + _seg_49() + + _seg_50() + + _seg_51() + + _seg_52() + + _seg_53() + + _seg_54() + + _seg_55() + + _seg_56() + + _seg_57() + + _seg_58() + + _seg_59() + + _seg_60() + + _seg_61() + + _seg_62() + + _seg_63() + + _seg_64() + + _seg_65() + + _seg_66() + + _seg_67() + + _seg_68() + + _seg_69() + + _seg_70() + + _seg_71() + + _seg_72() + + _seg_73() + + _seg_74() + + _seg_75() + + _seg_76() + + _seg_77() + + _seg_78() + + _seg_79() + + _seg_80() + + _seg_81() + + _seg_82() + + _seg_83() +) # type: Tuple[Union[Tuple[int, str], Tuple[int, str, str]], ...] diff --git a/.venv/lib/python3.12/site-packages/importlib_metadata-8.7.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/importlib_metadata-8.7.0.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/importlib_metadata-8.7.0.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/importlib_metadata-8.7.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/importlib_metadata-8.7.0.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..5dbad867d45b39beb24549a6a40c753d3a768c4b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/importlib_metadata-8.7.0.dist-info/METADATA @@ -0,0 +1,134 @@ +Metadata-Version: 2.4 +Name: importlib_metadata +Version: 8.7.0 +Summary: Read metadata from Python packages +Author-email: "Jason R. Coombs" +Project-URL: Source, https://github.com/python/importlib_metadata +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Requires-Python: >=3.9 +Description-Content-Type: text/x-rst +License-File: LICENSE +Requires-Dist: zipp>=3.20 +Requires-Dist: typing-extensions>=3.6.4; python_version < "3.8" +Provides-Extra: test +Requires-Dist: pytest!=8.1.*,>=6; extra == "test" +Requires-Dist: importlib_resources>=1.3; python_version < "3.9" and extra == "test" +Requires-Dist: packaging; extra == "test" +Requires-Dist: pyfakefs; extra == "test" +Requires-Dist: flufl.flake8; extra == "test" +Requires-Dist: pytest-perf>=0.9.2; extra == "test" +Requires-Dist: jaraco.test>=5.4; extra == "test" +Provides-Extra: doc +Requires-Dist: sphinx>=3.5; extra == "doc" +Requires-Dist: jaraco.packaging>=9.3; extra == "doc" +Requires-Dist: rst.linker>=1.9; extra == "doc" +Requires-Dist: furo; extra == "doc" +Requires-Dist: sphinx-lint; extra == "doc" +Requires-Dist: jaraco.tidelift>=1.4; extra == "doc" +Provides-Extra: perf +Requires-Dist: ipython; extra == "perf" +Provides-Extra: check +Requires-Dist: pytest-checkdocs>=2.4; extra == "check" +Requires-Dist: pytest-ruff>=0.2.1; sys_platform != "cygwin" and extra == "check" +Provides-Extra: cover +Requires-Dist: pytest-cov; extra == "cover" +Provides-Extra: enabler +Requires-Dist: pytest-enabler>=2.2; extra == "enabler" +Provides-Extra: type +Requires-Dist: pytest-mypy; extra == "type" +Dynamic: license-file + +.. image:: https://img.shields.io/pypi/v/importlib_metadata.svg + :target: https://pypi.org/project/importlib_metadata + +.. image:: https://img.shields.io/pypi/pyversions/importlib_metadata.svg + +.. image:: https://github.com/python/importlib_metadata/actions/workflows/main.yml/badge.svg + :target: https://github.com/python/importlib_metadata/actions?query=workflow%3A%22tests%22 + :alt: tests + +.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json + :target: https://github.com/astral-sh/ruff + :alt: Ruff + +.. image:: https://readthedocs.org/projects/importlib-metadata/badge/?version=latest + :target: https://importlib-metadata.readthedocs.io/en/latest/?badge=latest + +.. image:: https://img.shields.io/badge/skeleton-2025-informational + :target: https://blog.jaraco.com/skeleton + +.. image:: https://tidelift.com/badges/package/pypi/importlib-metadata + :target: https://tidelift.com/subscription/pkg/pypi-importlib-metadata?utm_source=pypi-importlib-metadata&utm_medium=readme + +Library to access the metadata for a Python package. + +This package supplies third-party access to the functionality of +`importlib.metadata `_ +including improvements added to subsequent Python versions. + + +Compatibility +============= + +New features are introduced in this third-party library and later merged +into CPython. The following table indicates which versions of this library +were contributed to different versions in the standard library: + +.. list-table:: + :header-rows: 1 + + * - importlib_metadata + - stdlib + * - 7.0 + - 3.13 + * - 6.5 + - 3.12 + * - 4.13 + - 3.11 + * - 4.6 + - 3.10 + * - 1.4 + - 3.8 + + +Usage +===== + +See the `online documentation `_ +for usage details. + +`Finder authors +`_ can +also add support for custom package installers. See the above documentation +for details. + + +Caveats +======= + +This project primarily supports third-party packages installed by PyPA +tools (or other conforming packages). It does not support: + +- Packages in the stdlib. +- Packages installed without metadata. + +Project details +=============== + + * Project home: https://github.com/python/importlib_metadata + * Report bugs at: https://github.com/python/importlib_metadata/issues + * Code hosting: https://github.com/python/importlib_metadata + * Documentation: https://importlib-metadata.readthedocs.io/ + +For Enterprise +============== + +Available as part of the Tidelift Subscription. + +This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use. + +`Learn more `_. diff --git a/.venv/lib/python3.12/site-packages/importlib_metadata-8.7.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/importlib_metadata-8.7.0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..b2dcfd28d1427b30a6922be638af3d6a353c33b3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/importlib_metadata-8.7.0.dist-info/RECORD @@ -0,0 +1,21 @@ +importlib_metadata-8.7.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +importlib_metadata-8.7.0.dist-info/METADATA,sha256=QJMudfqWDjfobdzipgUH5gskLahdXueIgy433mjk_Qw,4760 +importlib_metadata-8.7.0.dist-info/RECORD,, +importlib_metadata-8.7.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +importlib_metadata-8.7.0.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91 +importlib_metadata-8.7.0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358 +importlib_metadata-8.7.0.dist-info/top_level.txt,sha256=CO3fD9yylANiXkrMo4qHLV_mqXL2sC5JFKgt1yWAT-A,19 +importlib_metadata/__init__.py,sha256=R60WJL9VdkZ9SgxtzCH-AU604rJPmNBnntPrUfcNEqc,37062 +importlib_metadata/_adapters.py,sha256=9Y3FAlZuoo8pOMVLnKXm5Xx6hKgsdUQOXF5SkiDGqWo,3784 +importlib_metadata/_collections.py,sha256=CxAhzlF3g1rwu_fMiB53JtRQiUFh0RgiMpoOvmK_ocg,760 +importlib_metadata/_compat.py,sha256=VC5ZDLlT-BcshauCShdFJvMNLntJJfZzNK1meGa-enw,1313 +importlib_metadata/_functools.py,sha256=bSbAqC9-2niWM9364FYBx9GWtetnJEfo4mdLv8uMl7c,2895 +importlib_metadata/_itertools.py,sha256=nMvp9SfHAQ_JYwK4L2i64lr3GRXGlYlikGTVzWbys_E,5351 +importlib_metadata/_meta.py,sha256=EtHyiJ5kGzWFDfKyQ2XQp6Vu113CeadKW1Vf6aGc1B4,1765 +importlib_metadata/_text.py,sha256=HCsFksZpJLeTP3NEk_ngrAeXVRRtTrtyh9eOABoRP4A,2166 +importlib_metadata/_typing.py,sha256=EQKhhsEgz_Sa-FnePI-faC72rNOOQwopjA1i5pG8FDU,367 +importlib_metadata/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +importlib_metadata/compat/py311.py,sha256=uqm-K-uohyj1042TH4a9Er_I5o7667DvulcD-gC_fSA,608 +importlib_metadata/compat/py39.py,sha256=J3W7PUVRPNYMmcvT12RF8ndBU9e8_T0Ac4U87Bsrq70,1187 +importlib_metadata/diagnose.py,sha256=nkSRMiowlmkhLYhKhvCg9glmt_11Cox-EmLzEbqYTa8,379 +importlib_metadata/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.venv/lib/python3.12/site-packages/importlib_metadata-8.7.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/importlib_metadata-8.7.0.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/importlib_metadata-8.7.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/importlib_metadata-8.7.0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..8acb95590701b87bf84eec079cf4e3989f63b098 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/importlib_metadata-8.7.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (79.0.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.12/site-packages/importlib_metadata-8.7.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/importlib_metadata-8.7.0.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..bbb07547a19c30031d13c45cf01cba61dc434e47 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/importlib_metadata-8.7.0.dist-info/top_level.txt @@ -0,0 +1 @@ +importlib_metadata diff --git a/.venv/lib/python3.12/site-packages/isympy.py b/.venv/lib/python3.12/site-packages/isympy.py new file mode 100644 index 0000000000000000000000000000000000000000..f7f4f7cd751f78e7d526aa50a527a914cd07d9af --- /dev/null +++ b/.venv/lib/python3.12/site-packages/isympy.py @@ -0,0 +1,342 @@ +""" +Python shell for SymPy. + +This is just a normal Python shell (IPython shell if you have the +IPython package installed), that executes the following commands for +the user: + + >>> from __future__ import division + >>> from sympy import * + >>> x, y, z, t = symbols('x y z t') + >>> k, m, n = symbols('k m n', integer=True) + >>> f, g, h = symbols('f g h', cls=Function) + >>> init_printing() + +So starting 'isympy' is equivalent to starting Python (or IPython) and +executing the above commands by hand. It is intended for easy and quick +experimentation with SymPy. isympy is a good way to use SymPy as an +interactive calculator. If you have IPython and Matplotlib installed, then +interactive plotting is enabled by default. + +COMMAND LINE OPTIONS +-------------------- + +-c CONSOLE, --console=CONSOLE + + Use the specified shell (Python or IPython) shell as the console + backend instead of the default one (IPython if present, Python + otherwise), e.g.: + + $isympy -c python + + CONSOLE must be one of 'ipython' or 'python' + +-p PRETTY, --pretty PRETTY + + Setup pretty-printing in SymPy. When pretty-printing is enabled, + expressions can be printed with Unicode or ASCII. The default is + to use pretty-printing (with Unicode if the terminal supports it). + When this option is 'no', expressions will not be pretty-printed + and ASCII will be used: + + $isympy -p no + + PRETTY must be one of 'unicode', 'ascii', or 'no' + +-t TYPES, --types=TYPES + + Setup the ground types for the polys. By default, gmpy ground types + are used if gmpy2 or gmpy is installed, otherwise it falls back to python + ground types, which are a little bit slower. You can manually + choose python ground types even if gmpy is installed (e.g., for + testing purposes): + + $isympy -t python + + TYPES must be one of 'gmpy', 'gmpy1' or 'python' + + Note that the ground type gmpy1 is primarily intended for testing; it + forces the use of gmpy version 1 even if gmpy2 is available. + + This is the same as setting the environment variable + SYMPY_GROUND_TYPES to the given ground type (e.g., + SYMPY_GROUND_TYPES='gmpy') + + The ground types can be determined interactively from the variable + sympy.polys.domains.GROUND_TYPES. + +-o ORDER, --order ORDER + + Setup the ordering of terms for printing. The default is lex, which + orders terms lexicographically (e.g., x**2 + x + 1). You can choose + other orderings, such as rev-lex, which will use reverse + lexicographic ordering (e.g., 1 + x + x**2): + + $isympy -o rev-lex + + ORDER must be one of 'lex', 'rev-lex', 'grlex', 'rev-grlex', + 'grevlex', 'rev-grevlex', 'old', or 'none'. + + Note that for very large expressions, ORDER='none' may speed up + printing considerably but the terms will have no canonical order. + +-q, --quiet + + Print only Python's and SymPy's versions to stdout at startup. + +-d, --doctest + + Use the same format that should be used for doctests. This is + equivalent to -c python -p no. + +-C, --no-cache + + Disable the caching mechanism. Disabling the cache may slow certain + operations down considerably. This is useful for testing the cache, + or for benchmarking, as the cache can result in deceptive timings. + + This is equivalent to setting the environment variable + SYMPY_USE_CACHE to 'no'. + +-a, --auto-symbols (requires at least IPython 0.11) + + Automatically create missing symbols. Normally, typing a name of a + Symbol that has not been instantiated first would raise NameError, + but with this option enabled, any undefined name will be + automatically created as a Symbol. + + Note that this is intended only for interactive, calculator style + usage. In a script that uses SymPy, Symbols should be instantiated + at the top, so that it's clear what they are. + + This will not override any names that are already defined, which + includes the single character letters represented by the mnemonic + QCOSINE (see the "Gotchas and Pitfalls" document in the + documentation). You can delete existing names by executing "del + name". If a name is defined, typing "'name' in dir()" will return True. + + The Symbols that are created using this have default assumptions. + If you want to place assumptions on symbols, you should create them + using symbols() or var(). + + Finally, this only works in the top level namespace. So, for + example, if you define a function in isympy with an undefined + Symbol, it will not work. + + See also the -i and -I options. + +-i, --int-to-Integer (requires at least IPython 0.11) + + Automatically wrap int literals with Integer. This makes it so that + things like 1/2 will come out as Rational(1, 2), rather than 0.5. This + works by preprocessing the source and wrapping all int literals with + Integer. Note that this will not change the behavior of int literals + assigned to variables, and it also won't change the behavior of functions + that return int literals. + + If you want an int, you can wrap the literal in int(), e.g. int(3)/int(2) + gives 1.5 (with division imported from __future__). + +-I, --interactive (requires at least IPython 0.11) + + This is equivalent to --auto-symbols --int-to-Integer. Future options + designed for ease of interactive use may be added to this. + +-D, --debug + + Enable debugging output. This is the same as setting the + environment variable SYMPY_DEBUG to 'True'. The debug status is set + in the variable SYMPY_DEBUG within isympy. + +-- IPython options + + Additionally you can pass command line options directly to the IPython + interpreter (the standard Python shell is not supported). However you + need to add the '--' separator between two types of options, e.g the + startup banner option and the colors option. You need to enter the + options as required by the version of IPython that you are using, too: + + in IPython 0.11, + + $isympy -q -- --colors=NoColor + + or older versions of IPython, + + $isympy -q -- -colors NoColor + +See also isympy --help. +""" + +import os +import sys + +# DO NOT IMPORT SYMPY HERE! Or the setting of the sympy environment variables +# by the command line will break. + +def main() -> None: + from argparse import ArgumentParser, RawDescriptionHelpFormatter + + VERSION = None + if '--version' in sys.argv: + # We cannot import sympy before this is run, because flags like -C and + # -t set environment variables that must be set before SymPy is + # imported. The only thing we need to import it for is to get the + # version, which only matters with the --version flag. + import sympy + VERSION = sympy.__version__ + + usage = 'isympy [options] -- [ipython options]' + parser = ArgumentParser( + usage=usage, + description=__doc__, + formatter_class=RawDescriptionHelpFormatter, + ) + + parser.add_argument('--version', action='version', version=VERSION) + + parser.add_argument( + '-c', '--console', + dest='console', + action='store', + default=None, + choices=['ipython', 'python'], + metavar='CONSOLE', + help='select type of interactive session: ipython | python; defaults ' + 'to ipython if IPython is installed, otherwise python') + + parser.add_argument( + '-p', '--pretty', + dest='pretty', + action='store', + default=None, + metavar='PRETTY', + choices=['unicode', 'ascii', 'no'], + help='setup pretty printing: unicode | ascii | no; defaults to ' + 'unicode printing if the terminal supports it, otherwise ascii') + + parser.add_argument( + '-t', '--types', + dest='types', + action='store', + default=None, + metavar='TYPES', + choices=['gmpy', 'gmpy1', 'python'], + help='setup ground types: gmpy | gmpy1 | python; defaults to gmpy if gmpy2 ' + 'or gmpy is installed, otherwise python') + + parser.add_argument( + '-o', '--order', + dest='order', + action='store', + default=None, + metavar='ORDER', + choices=['lex', 'grlex', 'grevlex', 'rev-lex', 'rev-grlex', 'rev-grevlex', 'old', 'none'], + help='setup ordering of terms: [rev-]lex | [rev-]grlex | [rev-]grevlex | old | none; defaults to lex') + + parser.add_argument( + '-q', '--quiet', + dest='quiet', + action='store_true', + default=False, + help='print only version information at startup') + + parser.add_argument( + '-d', '--doctest', + dest='doctest', + action='store_true', + default=False, + help='use the doctest format for output (you can just copy and paste it)') + + parser.add_argument( + '-C', '--no-cache', + dest='cache', + action='store_false', + default=True, + help='disable caching mechanism') + + parser.add_argument( + '-a', '--auto-symbols', + dest='auto_symbols', + action='store_true', + default=False, + help='automatically construct missing symbols') + + parser.add_argument( + '-i', '--int-to-Integer', + dest='auto_int_to_Integer', + action='store_true', + default=False, + help="automatically wrap int literals with Integer") + + parser.add_argument( + '-I', '--interactive', + dest='interactive', + action='store_true', + default=False, + help="equivalent to -a -i") + + parser.add_argument( + '-D', '--debug', + dest='debug', + action='store_true', + default=False, + help='enable debugging output') + + (options, ipy_args) = parser.parse_known_args() + if '--' in ipy_args: + ipy_args.remove('--') + + if not options.cache: + os.environ['SYMPY_USE_CACHE'] = 'no' + + if options.types: + os.environ['SYMPY_GROUND_TYPES'] = options.types + + if options.debug: + os.environ['SYMPY_DEBUG'] = str(options.debug) + + if options.doctest: + options.pretty = 'no' + options.console = 'python' + + session = options.console + + if session is not None: + ipython = session == 'ipython' + else: + try: + import IPython # noqa: F401 + ipython = True + except ImportError: + if not options.quiet: + from sympy.interactive.session import no_ipython + print(no_ipython) + ipython = False + + args = { + 'pretty_print': True, + 'use_unicode': None, + 'use_latex': None, + 'order': None, + 'argv': ipy_args, + } + + if options.pretty == 'unicode': + args['use_unicode'] = True + elif options.pretty == 'ascii': + args['use_unicode'] = False + elif options.pretty == 'no': + args['pretty_print'] = False + + if options.order is not None: + args['order'] = options.order + + args['quiet'] = options.quiet + args['auto_symbols'] = options.auto_symbols or options.interactive + args['auto_int_to_Integer'] = options.auto_int_to_Integer or options.interactive + + from sympy.interactive import init_session + init_session(ipython, **args) + +if __name__ == "__main__": + main() diff --git a/.venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/METADATA b/.venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..ffef2ff3bfa0c42b6e6e3eefda700391d181c9a0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/METADATA @@ -0,0 +1,84 @@ +Metadata-Version: 2.4 +Name: Jinja2 +Version: 3.1.6 +Summary: A very fast and expressive template engine. +Maintainer-email: Pallets +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Classifier: Typing :: Typed +License-File: LICENSE.txt +Requires-Dist: MarkupSafe>=2.0 +Requires-Dist: Babel>=2.7 ; extra == "i18n" +Project-URL: Changes, https://jinja.palletsprojects.com/changes/ +Project-URL: Chat, https://discord.gg/pallets +Project-URL: Documentation, https://jinja.palletsprojects.com/ +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Source, https://github.com/pallets/jinja/ +Provides-Extra: i18n + +# Jinja + +Jinja is a fast, expressive, extensible templating engine. Special +placeholders in the template allow writing code similar to Python +syntax. Then the template is passed data to render the final document. + +It includes: + +- Template inheritance and inclusion. +- Define and import macros within templates. +- HTML templates can use autoescaping to prevent XSS from untrusted + user input. +- A sandboxed environment can safely render untrusted templates. +- AsyncIO support for generating templates and calling async + functions. +- I18N support with Babel. +- Templates are compiled to optimized Python code just-in-time and + cached, or can be compiled ahead-of-time. +- Exceptions point to the correct line in templates to make debugging + easier. +- Extensible filters, tests, functions, and even syntax. + +Jinja's philosophy is that while application logic belongs in Python if +possible, it shouldn't make the template designer's job difficult by +restricting functionality too much. + + +## In A Nutshell + +```jinja +{% extends "base.html" %} +{% block title %}Members{% endblock %} +{% block content %} + +{% endblock %} +``` + +## Donate + +The Pallets organization develops and supports Jinja and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, [please +donate today][]. + +[please donate today]: https://palletsprojects.com/donate + +## Contributing + +See our [detailed contributing documentation][contrib] for many ways to +contribute, including reporting issues, requesting features, asking or answering +questions, and making PRs. + +[contrib]: https://palletsprojects.com/contributing/ + diff --git a/.venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/RECORD b/.venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..fb6122febd37ff51cfac21c02a2e25535875028b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/RECORD @@ -0,0 +1,33 @@ +jinja2-3.1.6.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +jinja2-3.1.6.dist-info/METADATA,sha256=aMVUj7Z8QTKhOJjZsx7FDGvqKr3ZFdkh8hQ1XDpkmcg,2871 +jinja2-3.1.6.dist-info/RECORD,, +jinja2-3.1.6.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jinja2-3.1.6.dist-info/WHEEL,sha256=_2ozNFCLWc93bK4WKHCO-eDUENDlo-dgc9cU3qokYO4,82 +jinja2-3.1.6.dist-info/entry_points.txt,sha256=OL85gYU1eD8cuPlikifFngXpeBjaxl6rIJ8KkC_3r-I,58 +jinja2-3.1.6.dist-info/licenses/LICENSE.txt,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +jinja2/__init__.py,sha256=xxepO9i7DHsqkQrgBEduLtfoz2QCuT6_gbL4XSN1hbU,1928 +jinja2/_identifier.py,sha256=_zYctNKzRqlk_murTNlzrju1FFJL7Va_Ijqqd7ii2lU,1958 +jinja2/async_utils.py,sha256=vK-PdsuorOMnWSnEkT3iUJRIkTnYgO2T6MnGxDgHI5o,2834 +jinja2/bccache.py,sha256=gh0qs9rulnXo0PhX5jTJy2UHzI8wFnQ63o_vw7nhzRg,14061 +jinja2/compiler.py,sha256=9RpCQl5X88BHllJiPsHPh295Hh0uApvwFJNQuutULeM,74131 +jinja2/constants.py,sha256=GMoFydBF_kdpaRKPoM5cl5MviquVRLVyZtfp5-16jg0,1433 +jinja2/debug.py,sha256=CnHqCDHd-BVGvti_8ZsTolnXNhA3ECsY-6n_2pwU8Hw,6297 +jinja2/defaults.py,sha256=boBcSw78h-lp20YbaXSJsqkAI2uN_mD_TtCydpeq5wU,1267 +jinja2/environment.py,sha256=9nhrP7Ch-NbGX00wvyr4yy-uhNHq2OCc60ggGrni_fk,61513 +jinja2/exceptions.py,sha256=ioHeHrWwCWNaXX1inHmHVblvc4haO7AXsjCp3GfWvx0,5071 +jinja2/ext.py,sha256=5PF5eHfh8mXAIxXHHRB2xXbXohi8pE3nHSOxa66uS7E,31875 +jinja2/filters.py,sha256=PQ_Egd9n9jSgtnGQYyF4K5j2nYwhUIulhPnyimkdr-k,55212 +jinja2/idtracking.py,sha256=-ll5lIp73pML3ErUYiIJj7tdmWxcH_IlDv3yA_hiZYo,10555 +jinja2/lexer.py,sha256=LYiYio6br-Tep9nPcupWXsPEtjluw3p1mU-lNBVRUfk,29786 +jinja2/loaders.py,sha256=wIrnxjvcbqh5VwW28NSkfotiDq8qNCxIOSFbGUiSLB4,24055 +jinja2/meta.py,sha256=OTDPkaFvU2Hgvx-6akz7154F8BIWaRmvJcBFvwopHww,4397 +jinja2/nativetypes.py,sha256=7GIGALVJgdyL80oZJdQUaUfwSt5q2lSSZbXt0dNf_M4,4210 +jinja2/nodes.py,sha256=m1Duzcr6qhZI8JQ6VyJgUNinjAf5bQzijSmDnMsvUx8,34579 +jinja2/optimizer.py,sha256=rJnCRlQ7pZsEEmMhsQDgC_pKyDHxP5TPS6zVPGsgcu8,1651 +jinja2/parser.py,sha256=lLOFy3sEmHc5IaEHRiH1sQVnId2moUQzhyeJZTtdY30,40383 +jinja2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jinja2/runtime.py,sha256=gDk-GvdriJXqgsGbHgrcKTP0Yp6zPXzhzrIpCFH3jAU,34249 +jinja2/sandbox.py,sha256=Mw2aitlY2I8la7FYhcX2YG9BtUYcLnD0Gh3d29cDWrY,15009 +jinja2/tests.py,sha256=VLsBhVFnWg-PxSBz1MhRnNWgP1ovXk3neO1FLQMeC9Q,5926 +jinja2/utils.py,sha256=rRp3o9e7ZKS4fyrWRbELyLcpuGVTFcnooaOa1qx_FIk,24129 +jinja2/visitor.py,sha256=EcnL1PIwf_4RVCOMxsRNuR8AXHbS1qfAdMOE2ngKJz4,3557 diff --git a/.venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..23d2d7e9a5d381ef8a375db09f82052144d1fd96 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.11.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/entry_points.txt b/.venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/entry_points.txt new file mode 100644 index 0000000000000000000000000000000000000000..abc3eae3b3bc573957cf7401711948799b3465c0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/jinja2-3.1.6.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[babel.extractors] +jinja2=jinja2.ext:babel_extract[i18n] + diff --git a/.venv/lib/python3.12/site-packages/joblib-1.5.2.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/joblib-1.5.2.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib-1.5.2.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/joblib-1.5.2.dist-info/METADATA b/.venv/lib/python3.12/site-packages/joblib-1.5.2.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..8d75358e0093fdc2a69b03b2779a885d25911397 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib-1.5.2.dist-info/METADATA @@ -0,0 +1,173 @@ +Metadata-Version: 2.4 +Name: joblib +Version: 1.5.2 +Summary: Lightweight pipelining with Python functions +Author-email: Gael Varoquaux +License: BSD 3-Clause +Project-URL: Homepage, https://joblib.readthedocs.io +Project-URL: Source, https://github.com/joblib/joblib +Platform: any +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Science/Research +Classifier: Intended Audience :: Education +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +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: Topic :: Scientific/Engineering +Classifier: Topic :: Utilities +Classifier: Topic :: Software Development :: Libraries +Requires-Python: >=3.9 +Description-Content-Type: text/x-rst +License-File: LICENSE.txt +Dynamic: license-file + +|PyPi| |CIStatus| |ReadTheDocs| |Codecov| + +.. |PyPi| image:: https://badge.fury.io/py/joblib.svg + :target: https://badge.fury.io/py/joblib + :alt: Joblib version + +.. |CIStatus| image:: https://github.com/joblib/joblib/actions/workflows/test.yml/badge.svg + :target: https://github.com/joblib/joblib/actions/workflows/test.yml?query=branch%3Amain + :alt: CI status + +.. |ReadTheDocs| image:: https://readthedocs.org/projects/joblib/badge/?version=latest + :target: https://joblib.readthedocs.io/en/latest/?badge=latest + :alt: Documentation Status + +.. |Codecov| image:: https://codecov.io/gh/joblib/joblib/branch/main/graph/badge.svg + :target: https://codecov.io/gh/joblib/joblib + :alt: Codecov coverage + + +The homepage of joblib with user documentation is located on: + +https://joblib.readthedocs.io + +Getting the latest code +======================= + +To get the latest code using git, simply type:: + + git clone https://github.com/joblib/joblib.git + +If you don't have git installed, you can download a zip +of the latest code: https://github.com/joblib/joblib/archive/refs/heads/main.zip + +Installing +========== + +You can use `pip` to install joblib from any directory:: + + pip install joblib + +or install it in editable mode from the source directory:: + + pip install -e . + +Dependencies +============ + +- Joblib has no mandatory dependencies besides Python (supported versions are + 3.9+). +- Joblib has an optional dependency on Numpy (at least version 1.6.1) for array + manipulation. +- Joblib includes its own vendored copy of + `loky `_ for process management. +- Joblib can efficiently dump and load numpy arrays but does not require numpy + to be installed. +- Joblib has an optional dependency on + `python-lz4 `_ as a faster alternative to + zlib and gzip for compressed serialization. +- Joblib has an optional dependency on psutil to mitigate memory leaks in + parallel worker processes. +- Some examples require external dependencies such as pandas. See the + instructions in the `Building the docs`_ section for details. + +Workflow to contribute +====================== + +To contribute to joblib, first create an account on `github +`_. Once this is done, fork the `joblib repository +`_ to have your own repository, +clone it using ``git clone``. Make your changes in a branch of your clone, push +them to your github account, test them locally, and when you are happy with +them, send a pull request to the main repository. + +You can use `pre-commit `_ to run code style checks +before each commit:: + + pip install pre-commit + pre-commit install + +pre-commit checks can be disabled for a single commit with:: + + git commit -n + +Running the test suite +====================== + +To run the test suite, you need the pytest (version >= 3) and coverage modules. +Run the test suite using:: + + pytest joblib + +from the root of the project. + +Building the docs +================= + +To build the docs you need to have sphinx (>=1.4) and some dependencies +installed:: + + pip install -U -r .readthedocs-requirements.txt + +The docs can then be built with the following command:: + + make doc + +The html docs are located in the ``doc/_build/html`` directory. + + +Making a source tarball +======================= + +To create a source tarball, eg for packaging or distributing, run the +following command:: + + pip install build + python -m build --sdist + +The tarball will be created in the `dist` directory. This command will create +the resulting tarball that can be installed with no extra dependencies than the +Python standard library. + +Making a release and uploading it to PyPI +========================================= + +This command is only run by project manager, to make a release, and +upload in to PyPI:: + + pip install build + python -m build --sdist --wheel + twine upload dist/* + + +Note that the documentation should automatically get updated at each git +push. If that is not the case, try building th doc locally and resolve +any doc build error (in particular when running the examples). + +Updating the changelog +====================== + +Changes are listed in the CHANGES.rst file. They must be manually updated +but, the following git command may be used to generate the lines:: + + git log --abbrev-commit --date=short --no-merges --sparse diff --git a/.venv/lib/python3.12/site-packages/joblib-1.5.2.dist-info/RECORD b/.venv/lib/python3.12/site-packages/joblib-1.5.2.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..6fe54ff1330055e4c78ec3054fd74c1780956e73 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib-1.5.2.dist-info/RECORD @@ -0,0 +1,145 @@ +joblib-1.5.2.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +joblib-1.5.2.dist-info/METADATA,sha256=zzhbcb_OGqYw3ts7N0noQYJqXLjuFcXnXgba36zESj0,5582 +joblib-1.5.2.dist-info/RECORD,, +joblib-1.5.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +joblib-1.5.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91 +joblib-1.5.2.dist-info/licenses/LICENSE.txt,sha256=QmEpEcGHLF5LQ_auDo7llGfNNQMyJBz3LOkGQCZPrmo,1527 +joblib-1.5.2.dist-info/top_level.txt,sha256=P0LsoZ45gBL7ckL4lqQt7tdbrHD4xlVYhffmhHeeT_U,7 +joblib/__init__.py,sha256=Iv9buXB2WPDJpjCT1kuRCzfDRZkAXbIAOWYUjaGEOlg,5337 +joblib/_cloudpickle_wrapper.py,sha256=HSFxIio3jiGnwVCstAa6obbxs4-5aRAIMDDUAA-cDPc,416 +joblib/_dask.py,sha256=xUYA_2VVc0LvPavSiFy8M7TZc6KF0lIxcQhng5kPaXU,13217 +joblib/_memmapping_reducer.py,sha256=AZ6dqA6fXlm4-ehBCf9m1nq43jUPKman4_2whrOButc,28553 +joblib/_multiprocessing_helpers.py,sha256=f8-Vf_8ildmdg991eLz8xk4DJJFTS_bcrhj6CgQ4lxU,1878 +joblib/_parallel_backends.py,sha256=fgy_FgZiKeNvTWr4wKbSX4kUNx2YD6m7p5O1J96xhb4,28766 +joblib/_store_backends.py,sha256=hKMOjAe309jUKbe-9YHAyfhjnxkcwaWsdw2m7hFo-r8,17693 +joblib/_utils.py,sha256=J9keatbwMXMJ1oZiVhEFu0UgL_WTvoVi4Iberk0gfAg,2076 +joblib/backports.py,sha256=mITpG-yuEADimg89_LCdUY9QH9a5xQHsRNJnd7BmAMo,5450 +joblib/compressor.py,sha256=GDDVJmeOBqftc6tMkDupryojHhk_jIV8WrNoMiTxTdQ,19281 +joblib/disk.py,sha256=1J5hhMsCP5LDW65luTtArUxsMAJRrPB6wxSWf6GeBns,4332 +joblib/executor.py,sha256=fbVmE_KKywjJcIKmHO9k8M3VkaMqZXEP4YXBRz_p6xU,5229 +joblib/externals/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +joblib/externals/cloudpickle/__init__.py,sha256=IzKm9MzljfhH-QmN_o-zP5QimTwbtgJeRja8nrGFanQ,308 +joblib/externals/cloudpickle/cloudpickle.py,sha256=cNEBKdjBDlzFce_tvZL889uv71AnXTz1XBzkjKASSTo,58466 +joblib/externals/cloudpickle/cloudpickle_fast.py,sha256=AI5ZKf2AbLNxD8lXyLDpKZyzeZ2ofFtdK1ZWFq_ec1c,323 +joblib/externals/loky/__init__.py,sha256=8LzBTFpYfRFrjD1loIQpRF9QQ_8wwEkssJI6hYcGbfE,1105 +joblib/externals/loky/_base.py,sha256=LsQnEoKWKGhdeqGhMc68Aqwz4MrTnEs20KAYbFiUHzo,1057 +joblib/externals/loky/backend/__init__.py,sha256=Ix9KThV1CYk7-M5OQnJ_A_JrrrWJ-Jowa-HMMeGbp18,312 +joblib/externals/loky/backend/_posix_reduction.py,sha256=xgCSrIaLI0k_MI0XNOBSp5e1ox1WN9idgrWbkWpMUr4,1776 +joblib/externals/loky/backend/_win_reduction.py,sha256=WmNB0NXtyJ_o_WzfPUEGh5dPhXIeI6FkEnFNXUxO2ws,683 +joblib/externals/loky/backend/context.py,sha256=RPdZvzkEk7iA0rtdAILSHNzl6wsHpm6XD6IL30owAPE,14284 +joblib/externals/loky/backend/fork_exec.py,sha256=4DZ1iLBB-21rlg3Z4Kh9DTVZj35JPaWFE5rzWZaSDxk,2319 +joblib/externals/loky/backend/popen_loky_posix.py,sha256=3G-2_-ovZtjWcHI-xSyW5zQjAZ-_Z9IGjzY1RrZH4nc,5541 +joblib/externals/loky/backend/popen_loky_win32.py,sha256=bYkhRA0w8qUcYFwoezeGwcnlCocEdheWXc6SZ-_rVxo,5325 +joblib/externals/loky/backend/process.py,sha256=4-Y94EoIrg4btsjTNxUBHAHhR96Nrugn_7_PGL6aU50,2018 +joblib/externals/loky/backend/queues.py,sha256=eETFvbPHwKfdoYyOgNQCyKq_Zlm-lzH3fwwpUIh-_4U,7322 +joblib/externals/loky/backend/reduction.py,sha256=861drQAefXTJjfFWAEWmYAS315d8lAyqWx0RgyxFw_0,6926 +joblib/externals/loky/backend/resource_tracker.py,sha256=Jzbmb8otLR7etqhefKuZxAs1VvT1jV8d5Zev8vUcV6s,15403 +joblib/externals/loky/backend/spawn.py,sha256=t4PzEJ3tjwoF9t8qnQUF9R7Q-LmBpDBIcHURWNznz8M,8626 +joblib/externals/loky/backend/synchronize.py,sha256=nlDwBoLZB93m_l55qfZM_Ql-4L84PSYimoQqt5TzpDk,11768 +joblib/externals/loky/backend/utils.py,sha256=RVsxqyET4TJdbjc9uUHJmfhlQ2v4Uq-fiT_5b5rfC0s,5757 +joblib/externals/loky/cloudpickle_wrapper.py,sha256=jUnfhXI3qMXTlCeTUzpABQlv0VOLMJL1V7fpRlq2LgU,3609 +joblib/externals/loky/initializers.py,sha256=dtKtRsJUmVwiJu0yZ-Ih0m8PvW_MxmouG7mShEcsStc,2567 +joblib/externals/loky/process_executor.py,sha256=QPSKet0OCAWr6g_2fHwPt4yjQaAJsjfeJYFPiKhS9RE,52348 +joblib/externals/loky/reusable_executor.py,sha256=d9ksrTnJS8549Oq50iG08u5pEhuMbhQ3oSYUSq0twNQ,10863 +joblib/func_inspect.py,sha256=bhm_GpBe3H_Dmw5ripzP5BalA6wbq7ZFI3SEuAQbfek,14017 +joblib/hashing.py,sha256=38MM0zRl0Ebk78Ij6cMdrQ8ibYZP0pCJxu3L4Yrw1sc,10694 +joblib/logger.py,sha256=HK06qwNWJYInYIIXFYINAKCxjYxi0hoX45ckNKkogHQ,5342 +joblib/memory.py,sha256=va7zWG9s_X6eE-Cm1junrH-QwKTnguin5cEJIhUXo98,45404 +joblib/numpy_pickle.py,sha256=N_wQMf6_vgI71nRYLne0dc2kO6dfh0lkTaOZn8Tq5Hc,28791 +joblib/numpy_pickle_compat.py,sha256=JOlSfMT1uDIztOyQ3dzYgp5fGVnzPVWBCqXjdIZsjLQ,8451 +joblib/numpy_pickle_utils.py,sha256=j3GlI25QFvo-DTPn7uRptu-NtW16ztHM0DuglyQyEDI,9497 +joblib/parallel.py,sha256=SkJYk-cTHC8oMvZU79SDXV61IZ10YIHbBYhrHB47yM8,86989 +joblib/pool.py,sha256=JTc00PEAyPayo8mHdktmburp5OBsnNxwSQI4zzvtKYs,14134 +joblib/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +joblib/test/common.py,sha256=vpjpcJgMbmr8H3skc3qsr_KC-u-ZnhVFRk2vAxmJqvA,2102 +joblib/test/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +joblib/test/data/create_numpy_pickle.py,sha256=vZE7JNye9o0gYaxrn1555av6Igee0KeXacAWKNRhsu8,3334 +joblib/test/data/joblib_0.10.0_compressed_pickle_py27_np16.gz,sha256=QYRH6Q2DSGVorjCSqWCxjTWCMOJKyew4Nl2qmfQVvQ8,769 +joblib/test/data/joblib_0.10.0_compressed_pickle_py27_np17.gz,sha256=ofTozM_KlPJa50TR8FCwc09mMmO6OO0GQhgUBLNIsXs,757 +joblib/test/data/joblib_0.10.0_compressed_pickle_py33_np18.gz,sha256=2eIVeA-XjOaT5IEQ6tI2UuHG3hwhiRciMmkBmPcIh4g,792 +joblib/test/data/joblib_0.10.0_compressed_pickle_py34_np19.gz,sha256=Gr2z_1tVWDH1H3_wCVHmakknf8KqeHKT8Yz4d1vmUCM,794 +joblib/test/data/joblib_0.10.0_compressed_pickle_py35_np19.gz,sha256=pWw_xuDbOkECqu1KGf1OFU7s2VbzC2v5F5iXhE7TwB4,790 +joblib/test/data/joblib_0.10.0_pickle_py27_np17.pkl,sha256=icRQjj374B-AHk5znxre0T9oWUHokoHIBQ8MqKo8l-U,986 +joblib/test/data/joblib_0.10.0_pickle_py27_np17.pkl.bz2,sha256=oYQVIyMiUxyRgWSuBBSOvCWKzToA-kUpcoQWdV4UoV4,997 +joblib/test/data/joblib_0.10.0_pickle_py27_np17.pkl.gzip,sha256=Jpv3iGcDgKTv-O4nZsUreIbUK7qnt2cugZ-VMgNeEDQ,798 +joblib/test/data/joblib_0.10.0_pickle_py27_np17.pkl.lzma,sha256=c0wu0x8pPv4BcStj7pE61rZpf68FLG_pNzQZ4e82zH8,660 +joblib/test/data/joblib_0.10.0_pickle_py27_np17.pkl.xz,sha256=77FG1FDG0GHQav-1bxc4Tn9ky6ubUW_MbE0_iGmz5wc,712 +joblib/test/data/joblib_0.10.0_pickle_py33_np18.pkl,sha256=4GTC7s_cWNVShERn2nvVbspZYJgyK_0man4TEqvdVzU,1068 +joblib/test/data/joblib_0.10.0_pickle_py33_np18.pkl.bz2,sha256=6G1vbs_iYmz2kYJ6w4qB1k7D67UnxUMus0S4SWeBtFo,1000 +joblib/test/data/joblib_0.10.0_pickle_py33_np18.pkl.gzip,sha256=tlRUWeJS1BXmcwtLNSNK9L0hDHekFl07CqWxTShinmY,831 +joblib/test/data/joblib_0.10.0_pickle_py33_np18.pkl.lzma,sha256=CorPwnfv3rR5hjNtJI01-sEBMOnkSxNlRVaWTszMopA,694 +joblib/test/data/joblib_0.10.0_pickle_py33_np18.pkl.xz,sha256=Dppj3MffOKsKETeptEtDaxPOv6MA6xnbpK5LzlDQ-oE,752 +joblib/test/data/joblib_0.10.0_pickle_py34_np19.pkl,sha256=HL5Fb1uR9aPLjjhoOPJ2wwM1Qyo1FCZoYYd2HVw0Fos,1068 +joblib/test/data/joblib_0.10.0_pickle_py34_np19.pkl.bz2,sha256=Pyr2fqZnwfUxXdyrBr-kRwBYY8HA_Yi7fgSguKy5pUs,1021 +joblib/test/data/joblib_0.10.0_pickle_py34_np19.pkl.gzip,sha256=os8NJjQI9FhnlZM-Ay9dX_Uo35gZnoJCgQSIVvcBPfE,831 +joblib/test/data/joblib_0.10.0_pickle_py34_np19.pkl.lzma,sha256=Q_0y43qU7_GqAabJ8y3PWVhOisurnCAq3GzuCu04V58,697 +joblib/test/data/joblib_0.10.0_pickle_py34_np19.pkl.xz,sha256=BNfmiQfpeLVpdfkwlJK4hJ5Cpgl0vreVyekyc5d_PNM,752 +joblib/test/data/joblib_0.10.0_pickle_py35_np19.pkl,sha256=l7nvLolhBDIdPFznOz3lBHiMOPBPCMi1bXop1tFSCpY,1068 +joblib/test/data/joblib_0.10.0_pickle_py35_np19.pkl.bz2,sha256=pqGpuIS-ZU4uP8mkglHs8MaSDiVcPy7l3XHYJSppRgY,1005 +joblib/test/data/joblib_0.10.0_pickle_py35_np19.pkl.gzip,sha256=YRFXE6LEb6qK72yPqnXdqQVY8Ts8xKUS9PWQKhLxWvk,833 +joblib/test/data/joblib_0.10.0_pickle_py35_np19.pkl.lzma,sha256=Bf7gCUeTuTjCkbcIdyZYz69irblX4SAVQEzxCnMQhNU,701 +joblib/test/data/joblib_0.10.0_pickle_py35_np19.pkl.xz,sha256=As8w2LGWwwNmKy3QNdKljK63Yq46gjRf_RJ0lh5_WqA,752 +joblib/test/data/joblib_0.11.0_compressed_pickle_py36_np111.gz,sha256=1WrnXDqDoNEPYOZX1Q5Wr2463b8vVV6fw4Wm5S4bMt4,800 +joblib/test/data/joblib_0.11.0_pickle_py36_np111.pkl,sha256=XmsOFxeC1f1aYdGETclG6yfF9rLoB11DayOAhDMULrw,1068 +joblib/test/data/joblib_0.11.0_pickle_py36_np111.pkl.bz2,sha256=vI2yWb50LKL_NgZyd_XkoD5teIg93uI42mWnx9ee-AQ,991 +joblib/test/data/joblib_0.11.0_pickle_py36_np111.pkl.gzip,sha256=1WrnXDqDoNEPYOZX1Q5Wr2463b8vVV6fw4Wm5S4bMt4,800 +joblib/test/data/joblib_0.11.0_pickle_py36_np111.pkl.lzma,sha256=IWA0JlZG2ur53HgTUDl1m7q79dcVq6b0VOq33gKoJU0,715 +joblib/test/data/joblib_0.11.0_pickle_py36_np111.pkl.xz,sha256=3Xh_NbMZdBjYx7ynfJ3Fyke28izSRSSzzNB0z5D4k9Y,752 +joblib/test/data/joblib_0.8.4_compressed_pickle_py27_np17.gz,sha256=Sp-ZT7i6pj5on2gbptszu7RarzJpOmHJ67UKOmCPQMg,659 +joblib/test/data/joblib_0.9.2_compressed_pickle_py27_np16.gz,sha256=NLtDrvo2XIH0KvUUAvhOqMeoXEjGW0IuTk_osu5XiDw,658 +joblib/test/data/joblib_0.9.2_compressed_pickle_py27_np17.gz,sha256=NLtDrvo2XIH0KvUUAvhOqMeoXEjGW0IuTk_osu5XiDw,658 +joblib/test/data/joblib_0.9.2_compressed_pickle_py34_np19.gz,sha256=nzO9iiGkG3KbBdrF3usOho8higkrDj_lmICUzxZyF_Y,673 +joblib/test/data/joblib_0.9.2_compressed_pickle_py35_np19.gz,sha256=nzO9iiGkG3KbBdrF3usOho8higkrDj_lmICUzxZyF_Y,673 +joblib/test/data/joblib_0.9.2_pickle_py27_np16.pkl,sha256=naijdk2xIeKdIa3mfJw0JlmOdtiN6uRM1yOJg6-M73M,670 +joblib/test/data/joblib_0.9.2_pickle_py27_np16.pkl_01.npy,sha256=DvvX2c5-7DpuCg20HnleA5bMo9awN9rWxhtGSEPSiAk,120 +joblib/test/data/joblib_0.9.2_pickle_py27_np16.pkl_02.npy,sha256=HBzzbLeB-8whuVO7CgtF3wktoOrg52WILlljzNcBBbE,120 +joblib/test/data/joblib_0.9.2_pickle_py27_np16.pkl_03.npy,sha256=oMRa4qKJhBy-uiRDt-uqOzHAqencxzKUrKVynaAJJAU,236 +joblib/test/data/joblib_0.9.2_pickle_py27_np16.pkl_04.npy,sha256=PsviRClLqT4IR5sWwbmpQR41af9mDtBFncodJBOB3wU,104 +joblib/test/data/joblib_0.9.2_pickle_py27_np17.pkl,sha256=LynX8dLOygfxDfFywOgm7wgWOhSxLG7z-oDsU6X83Dw,670 +joblib/test/data/joblib_0.9.2_pickle_py27_np17.pkl_01.npy,sha256=DvvX2c5-7DpuCg20HnleA5bMo9awN9rWxhtGSEPSiAk,120 +joblib/test/data/joblib_0.9.2_pickle_py27_np17.pkl_02.npy,sha256=HBzzbLeB-8whuVO7CgtF3wktoOrg52WILlljzNcBBbE,120 +joblib/test/data/joblib_0.9.2_pickle_py27_np17.pkl_03.npy,sha256=oMRa4qKJhBy-uiRDt-uqOzHAqencxzKUrKVynaAJJAU,236 +joblib/test/data/joblib_0.9.2_pickle_py27_np17.pkl_04.npy,sha256=PsviRClLqT4IR5sWwbmpQR41af9mDtBFncodJBOB3wU,104 +joblib/test/data/joblib_0.9.2_pickle_py33_np18.pkl,sha256=w9TLxpDTzp5TI6cU6lRvMsAasXEChcQgGE9s30sm_CU,691 +joblib/test/data/joblib_0.9.2_pickle_py33_np18.pkl_01.npy,sha256=DvvX2c5-7DpuCg20HnleA5bMo9awN9rWxhtGSEPSiAk,120 +joblib/test/data/joblib_0.9.2_pickle_py33_np18.pkl_02.npy,sha256=HBzzbLeB-8whuVO7CgtF3wktoOrg52WILlljzNcBBbE,120 +joblib/test/data/joblib_0.9.2_pickle_py33_np18.pkl_03.npy,sha256=jt6aZKUrJdfbMJUJVsl47As5MrfRSs1avGMhbmS6vec,307 +joblib/test/data/joblib_0.9.2_pickle_py33_np18.pkl_04.npy,sha256=PsviRClLqT4IR5sWwbmpQR41af9mDtBFncodJBOB3wU,104 +joblib/test/data/joblib_0.9.2_pickle_py34_np19.pkl,sha256=ilOBAOaulLFvKrD32S1NfnpiK-LfzA9rC3O2I7xROuI,691 +joblib/test/data/joblib_0.9.2_pickle_py34_np19.pkl_01.npy,sha256=DvvX2c5-7DpuCg20HnleA5bMo9awN9rWxhtGSEPSiAk,120 +joblib/test/data/joblib_0.9.2_pickle_py34_np19.pkl_02.npy,sha256=HBzzbLeB-8whuVO7CgtF3wktoOrg52WILlljzNcBBbE,120 +joblib/test/data/joblib_0.9.2_pickle_py34_np19.pkl_03.npy,sha256=jt6aZKUrJdfbMJUJVsl47As5MrfRSs1avGMhbmS6vec,307 +joblib/test/data/joblib_0.9.2_pickle_py34_np19.pkl_04.npy,sha256=PsviRClLqT4IR5sWwbmpQR41af9mDtBFncodJBOB3wU,104 +joblib/test/data/joblib_0.9.2_pickle_py35_np19.pkl,sha256=WfDVIqKcMzzh1gSAshIfzBoIpdLdZQuG79yYf5kfpOo,691 +joblib/test/data/joblib_0.9.2_pickle_py35_np19.pkl_01.npy,sha256=DvvX2c5-7DpuCg20HnleA5bMo9awN9rWxhtGSEPSiAk,120 +joblib/test/data/joblib_0.9.2_pickle_py35_np19.pkl_02.npy,sha256=HBzzbLeB-8whuVO7CgtF3wktoOrg52WILlljzNcBBbE,120 +joblib/test/data/joblib_0.9.2_pickle_py35_np19.pkl_03.npy,sha256=jt6aZKUrJdfbMJUJVsl47As5MrfRSs1avGMhbmS6vec,307 +joblib/test/data/joblib_0.9.2_pickle_py35_np19.pkl_04.npy,sha256=PsviRClLqT4IR5sWwbmpQR41af9mDtBFncodJBOB3wU,104 +joblib/test/data/joblib_0.9.4.dev0_compressed_cache_size_pickle_py35_np19.gz,sha256=8jYfWJsx0oY2J-3LlmEigK5cClnJSW2J2rfeSTZw-Ts,802 +joblib/test/data/joblib_0.9.4.dev0_compressed_cache_size_pickle_py35_np19.gz_01.npy.z,sha256=YT9VvT3sEl2uWlOyvH2CkyE9Sok4od9O3kWtgeuUUqE,43 +joblib/test/data/joblib_0.9.4.dev0_compressed_cache_size_pickle_py35_np19.gz_02.npy.z,sha256=txA5RDI0PRuiU_UNKY8pGp-zQgQQ9vaVvMi60hOPaVs,43 +joblib/test/data/joblib_0.9.4.dev0_compressed_cache_size_pickle_py35_np19.gz_03.npy.z,sha256=d3AwICvU2MpSNjh2aPIsdJeGZLlDjANAF1Soa6uM0Po,37 +joblib/test/test_backports.py,sha256=ONt0JUPV1etZCO9DTLur1h84XmgHZYK_k73qmp4kRgg,1175 +joblib/test/test_cloudpickle_wrapper.py,sha256=9jx3hqNVO9GXdVHCxr9mN-GiLR0XK-O5d6YPaaG8Y14,729 +joblib/test/test_config.py,sha256=1Z102AO7Gb8Z8mHYahnZy2fxBA-9_vY0ZtWyNNk1cf4,5255 +joblib/test/test_dask.py,sha256=X2MBEYvz5WQwzGZRN04JNgk_75iIHF96yA1F1t1sK_Y,22932 +joblib/test/test_disk.py,sha256=0EaWGENlosrqwrSZvquPQw3jhqay1KD1NRlQ6YLHOOM,2223 +joblib/test/test_func_inspect.py,sha256=RsORR-j48SfXrNBQbb5i-SdmfU7zk2Mr0IKvcu8m1tw,9314 +joblib/test/test_func_inspect_special_encoding.py,sha256=5xILDjSO-xtjQAMLvMeVD-L7IG4ZURb2gvBiShaDE78,145 +joblib/test/test_hashing.py,sha256=wZeTJMX8C8ua3fJsKAI7MKtperUfZf1fLt0ZaOjvSKw,15820 +joblib/test/test_init.py,sha256=Y6y6Hcqa_cqwQ8S8ozUQ180y_RfkRajfZ_fDp2UXgbw,423 +joblib/test/test_logger.py,sha256=FA9ohTNcqIFViQK60_rwZ5PEGL2zoYN5qBOrDwFqVzI,941 +joblib/test/test_memmapping.py,sha256=z0aanbEs3yCDKShyW3IYlLkTARwdvqVTb4beTPRFmjk,43731 +joblib/test/test_memory.py,sha256=vTlNABkQzzHtRU_cXGr9eOEvrHAw7EEBmegMbX-gqZw,50660 +joblib/test/test_memory_async.py,sha256=tUoCI9dngR2AuJjAAKXElJIiz2Qm4AJGdXKn9c8lWaM,5245 +joblib/test/test_missing_multiprocessing.py,sha256=FVoS91krFZogIoDFScyZuJPpaeiq6O-aLAxug0qCQyY,1171 +joblib/test/test_module.py,sha256=IABzz5JmdeY_Adk_vZ0776JN94Ra7tWxDA7DPDNdJKI,1942 +joblib/test/test_numpy_pickle.py,sha256=QExCnBSG-EXdVKnoDkJjNFk6kbX0FDeGeR50wtLHiso,42130 +joblib/test/test_numpy_pickle_compat.py,sha256=paMz1G3Fr9SHdjFmKcG1ec6B5h_S-XE6WRtfHmX9r50,609 +joblib/test/test_numpy_pickle_utils.py,sha256=iB2Ve1TYYUEN3DQiNB5qUxk_QxeIXl7Jpgv4TwkFWTY,382 +joblib/test/test_parallel.py,sha256=_13kli8GYyclwh2QsxysXrRJa44o3gb3FEpSY61ag94,78095 +joblib/test/test_store_backends.py,sha256=DyK1f7PTSPErzhk27gaRoMe2UQrstIz6fnvZh4hKIf0,3057 +joblib/test/test_testing.py,sha256=jL-Ph5pzUJSXOgY2rqbjMRp2y3i3CCWmEi-Lbw4Wzr8,2520 +joblib/test/test_utils.py,sha256=urXuyQ40OV5sLMoNx30Azh3hGr-yJqiMtHRJwBb8mw0,570 +joblib/test/testutils.py,sha256=A1bm-A5Ydis2iZJVI2-r3aFKUufWR42NZ8Yttrp8mzg,252 +joblib/testing.py,sha256=lK8HOBvrpXcTYUCSvpE-M2ede_dTVJzcmyw-9BrBsOc,3029 diff --git a/.venv/lib/python3.12/site-packages/joblib-1.5.2.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/joblib-1.5.2.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/joblib-1.5.2.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/joblib-1.5.2.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..e7fa31b6f3f78deb1022c1f7927f07d4d16da822 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib-1.5.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.12/site-packages/joblib-1.5.2.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/joblib-1.5.2.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..ca4af27e2b6e9917d9600060588a18cc9e3cc78c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib-1.5.2.dist-info/top_level.txt @@ -0,0 +1 @@ +joblib diff --git a/.venv/lib/python3.12/site-packages/joblib/__init__.py b/.venv/lib/python3.12/site-packages/joblib/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..2d1ce66355257184f73739fc8e06897fc9fb0e5e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib/__init__.py @@ -0,0 +1,163 @@ +"""Joblib is a set of tools to provide **lightweight pipelining in +Python**. In particular: + +1. transparent disk-caching of functions and lazy re-evaluation + (memoize pattern) + +2. easy simple parallel computing + +Joblib is optimized to be **fast** and **robust** on large +data in particular and has specific optimizations for `numpy` arrays. It is +**BSD-licensed**. + + + ==================== =============================================== + **Documentation:** https://joblib.readthedocs.io + + **Download:** https://pypi.python.org/pypi/joblib#downloads + + **Source code:** https://github.com/joblib/joblib + + **Report issues:** https://github.com/joblib/joblib/issues + ==================== =============================================== + + +Vision +-------- + +The vision is to provide tools to easily achieve better performance and +reproducibility when working with long running jobs. + + * **Avoid computing the same thing twice**: code is often rerun again and + again, for instance when prototyping computational-heavy jobs (as in + scientific development), but hand-crafted solutions to alleviate this + issue are error-prone and often lead to unreproducible results. + + * **Persist to disk transparently**: efficiently persisting + arbitrary objects containing large data is hard. Using + joblib's caching mechanism avoids hand-written persistence and + implicitly links the file on disk to the execution context of + the original Python object. As a result, joblib's persistence is + good for resuming an application status or computational job, eg + after a crash. + +Joblib addresses these problems while **leaving your code and your flow +control as unmodified as possible** (no framework, no new paradigms). + +Main features +------------------ + +1) **Transparent and fast disk-caching of output value:** a memoize or + make-like functionality for Python functions that works well for + arbitrary Python objects, including very large numpy arrays. Separate + persistence and flow-execution logic from domain logic or algorithmic + code by writing the operations as a set of steps with well-defined + inputs and outputs: Python functions. Joblib can save their + computation to disk and rerun it only if necessary:: + + >>> from joblib import Memory + >>> location = 'your_cache_dir_goes_here' + >>> mem = Memory(location, verbose=1) + >>> import numpy as np + >>> a = np.vander(np.arange(3)).astype(float) + >>> square = mem.cache(np.square) + >>> b = square(a) # doctest: +ELLIPSIS + ______________________________________________________________________... + [Memory] Calling ...square... + square(array([[0., 0., 1.], + [1., 1., 1.], + [4., 2., 1.]])) + _________________________________________________...square - ...s, 0.0min + + >>> c = square(a) + >>> # The above call did not trigger an evaluation + +2) **Embarrassingly parallel helper:** to make it easy to write readable + parallel code and debug it quickly:: + + >>> from joblib import Parallel, delayed + >>> from math import sqrt + >>> Parallel(n_jobs=1)(delayed(sqrt)(i**2) for i in range(10)) + [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0] + + +3) **Fast compressed Persistence**: a replacement for pickle to work + efficiently on Python objects containing large data ( + *joblib.dump* & *joblib.load* ). + +.. + >>> import shutil ; shutil.rmtree(location) + +""" + +# PEP0440 compatible formatted version, see: +# https://www.python.org/dev/peps/pep-0440/ +# +# Generic release markers: +# X.Y +# X.Y.Z # For bugfix releases +# +# Admissible pre-release markers: +# X.YaN # Alpha release +# X.YbN # Beta release +# X.YrcN # Release Candidate +# X.Y # Final release +# +# Dev branch marker is: 'X.Y.dev' or 'X.Y.devN' where N is an integer. +# 'X.Y.dev0' is the canonical version of 'X.Y.dev' +# +__version__ = "1.5.2" + + +import os + +from ._cloudpickle_wrapper import wrap_non_picklable_objects +from ._parallel_backends import ParallelBackendBase +from ._store_backends import StoreBackendBase +from .compressor import register_compressor +from .hashing import hash +from .logger import Logger, PrintTime +from .memory import MemorizedResult, Memory, expires_after, register_store_backend +from .numpy_pickle import dump, load +from .parallel import ( + Parallel, + cpu_count, + delayed, + effective_n_jobs, + parallel_backend, + parallel_config, + register_parallel_backend, +) + +__all__ = [ + # On-disk result caching + "Memory", + "MemorizedResult", + "expires_after", + # Parallel code execution + "Parallel", + "delayed", + "cpu_count", + "effective_n_jobs", + "wrap_non_picklable_objects", + # Context to change the backend globally + "parallel_config", + "parallel_backend", + # Helpers to define and register store/parallel backends + "ParallelBackendBase", + "StoreBackendBase", + "register_compressor", + "register_parallel_backend", + "register_store_backend", + # Helpers kept for backward compatibility + "PrintTime", + "Logger", + "hash", + "dump", + "load", +] + + +# Workaround issue discovered in intel-openmp 2019.5: +# https://github.com/ContinuumIO/anaconda-issues/issues/11294 +os.environ.setdefault("KMP_INIT_AT_FORK", "FALSE") diff --git a/.venv/lib/python3.12/site-packages/joblib/_cloudpickle_wrapper.py b/.venv/lib/python3.12/site-packages/joblib/_cloudpickle_wrapper.py new file mode 100644 index 0000000000000000000000000000000000000000..b09ea068e80f9a64f111e93835db4d4f8cd93694 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib/_cloudpickle_wrapper.py @@ -0,0 +1,18 @@ +""" +Small shim of loky's cloudpickle_wrapper to avoid failure when +multiprocessing is not available. +""" + +from ._multiprocessing_helpers import mp + + +def _my_wrap_non_picklable_objects(obj, keep_wrapper=True): + return obj + + +if mp is not None: + from .externals.loky import wrap_non_picklable_objects +else: + wrap_non_picklable_objects = _my_wrap_non_picklable_objects + +__all__ = ["wrap_non_picklable_objects"] diff --git a/.venv/lib/python3.12/site-packages/joblib/_dask.py b/.venv/lib/python3.12/site-packages/joblib/_dask.py new file mode 100644 index 0000000000000000000000000000000000000000..fa2fea2d4029f2e429e3cdd6a4ff1401777b45c5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib/_dask.py @@ -0,0 +1,381 @@ +from __future__ import absolute_import, division, print_function + +import asyncio +import concurrent.futures +import contextlib +import time +import weakref +from uuid import uuid4 + +from ._utils import ( + _retrieve_traceback_capturing_wrapped_call, + _TracebackCapturingWrapper, +) +from .parallel import AutoBatchingMixin, ParallelBackendBase, parallel_config + +try: + import dask + import distributed +except ImportError: + dask = None + distributed = None + +if dask is not None and distributed is not None: + from dask.distributed import ( + Client, + as_completed, + get_client, + rejoin, + secede, + ) + from dask.sizeof import sizeof + from dask.utils import funcname + from distributed.utils import thread_state + + try: + # asyncio.TimeoutError, Python3-only error thrown by recent versions of + # distributed + from distributed.utils import TimeoutError as _TimeoutError + except ImportError: + from tornado.gen import TimeoutError as _TimeoutError + + +def is_weakrefable(obj): + try: + weakref.ref(obj) + return True + except TypeError: + return False + + +class _WeakKeyDictionary: + """A variant of weakref.WeakKeyDictionary for unhashable objects. + + This datastructure is used to store futures for broadcasted data objects + such as large numpy arrays or pandas dataframes that are not hashable and + therefore cannot be used as keys of traditional python dicts. + + Furthermore using a dict with id(array) as key is not safe because the + Python is likely to reuse id of recently collected arrays. + """ + + def __init__(self): + self._data = {} + + def __getitem__(self, obj): + ref, val = self._data[id(obj)] + if ref() is not obj: + # In case of a race condition with on_destroy. + raise KeyError(obj) + return val + + def __setitem__(self, obj, value): + key = id(obj) + try: + ref, _ = self._data[key] + if ref() is not obj: + # In case of race condition with on_destroy. + raise KeyError(obj) + except KeyError: + # Insert the new entry in the mapping along with a weakref + # callback to automatically delete the entry from the mapping + # as soon as the object used as key is garbage collected. + def on_destroy(_): + del self._data[key] + + ref = weakref.ref(obj, on_destroy) + self._data[key] = ref, value + + def __len__(self): + return len(self._data) + + def clear(self): + self._data.clear() + + +def _funcname(x): + try: + if isinstance(x, list): + x = x[0][0] + except Exception: + pass + return funcname(x) + + +def _make_tasks_summary(tasks): + """Summarize of list of (func, args, kwargs) function calls""" + unique_funcs = {func for func, args, kwargs in tasks} + + if len(unique_funcs) == 1: + mixed = False + else: + mixed = True + return len(tasks), mixed, _funcname(tasks) + + +class Batch: + """dask-compatible wrapper that executes a batch of tasks""" + + def __init__(self, tasks): + # collect some metadata from the tasks to ease Batch calls + # introspection when debugging + self._num_tasks, self._mixed, self._funcname = _make_tasks_summary(tasks) + + def __call__(self, tasks=None): + results = [] + with parallel_config(backend="dask"): + for func, args, kwargs in tasks: + results.append(func(*args, **kwargs)) + return results + + def __repr__(self): + descr = f"batch_of_{self._funcname}_{self._num_tasks}_calls" + if self._mixed: + descr = "mixed_" + descr + return descr + + +def _joblib_probe_task(): + # Noop used by the joblib connector to probe when workers are ready. + pass + + +class DaskDistributedBackend(AutoBatchingMixin, ParallelBackendBase): + MIN_IDEAL_BATCH_DURATION = 0.2 + MAX_IDEAL_BATCH_DURATION = 1.0 + supports_retrieve_callback = True + default_n_jobs = -1 + + def __init__( + self, + scheduler_host=None, + scatter=None, + client=None, + loop=None, + wait_for_workers_timeout=10, + **submit_kwargs, + ): + super().__init__() + + if distributed is None: + msg = ( + "You are trying to use 'dask' as a joblib parallel backend " + "but dask is not installed. Please install dask " + "to fix this error." + ) + raise ValueError(msg) + + if client is None: + if scheduler_host: + client = Client(scheduler_host, loop=loop, set_as_default=False) + else: + try: + client = get_client() + except ValueError as e: + msg = ( + "To use Joblib with Dask first create a Dask Client" + "\n\n" + " from dask.distributed import Client\n" + " client = Client()\n" + "or\n" + " client = Client('scheduler-address:8786')" + ) + raise ValueError(msg) from e + + self.client = client + + if scatter is not None and not isinstance(scatter, (list, tuple)): + raise TypeError( + "scatter must be a list/tuple, got `%s`" % type(scatter).__name__ + ) + + if scatter is not None and len(scatter) > 0: + # Keep a reference to the scattered data to keep the ids the same + self._scatter = list(scatter) + scattered = self.client.scatter(scatter, broadcast=True) + self.data_futures = {id(x): f for x, f in zip(scatter, scattered)} + else: + self._scatter = [] + self.data_futures = {} + self.wait_for_workers_timeout = wait_for_workers_timeout + self.submit_kwargs = submit_kwargs + self.waiting_futures = as_completed( + [], loop=client.loop, with_results=True, raise_errors=False + ) + self._results = {} + self._callbacks = {} + + async def _collect(self): + while self._continue: + async for future, result in self.waiting_futures: + cf_future = self._results.pop(future) + callback = self._callbacks.pop(future) + if future.status == "error": + typ, exc, tb = result + cf_future.set_exception(exc) + else: + cf_future.set_result(result) + callback(result) + await asyncio.sleep(0.01) + + def __reduce__(self): + return (DaskDistributedBackend, ()) + + def get_nested_backend(self): + return DaskDistributedBackend(client=self.client), -1 + + def configure(self, n_jobs=1, parallel=None, **backend_args): + self.parallel = parallel + return self.effective_n_jobs(n_jobs) + + def start_call(self): + self._continue = True + self.client.loop.add_callback(self._collect) + self.call_data_futures = _WeakKeyDictionary() + + def stop_call(self): + # The explicit call to clear is required to break a cycling reference + # to the futures. + self._continue = False + # wait for the future collection routine (self._backend._collect) to + # finish in order to limit asyncio warnings due to aborting _collect + # during a following backend termination call + time.sleep(0.01) + self.call_data_futures.clear() + + def effective_n_jobs(self, n_jobs): + effective_n_jobs = sum(self.client.ncores().values()) + if effective_n_jobs != 0 or not self.wait_for_workers_timeout: + return effective_n_jobs + + # If there is no worker, schedule a probe task to wait for the workers + # to come up and be available. If the dask cluster is in adaptive mode + # task might cause the cluster to provision some workers. + try: + self.client.submit(_joblib_probe_task).result( + timeout=self.wait_for_workers_timeout + ) + except _TimeoutError as e: + error_msg = ( + "DaskDistributedBackend has no worker after {} seconds. " + "Make sure that workers are started and can properly connect " + "to the scheduler and increase the joblib/dask connection " + "timeout with:\n\n" + "parallel_config(backend='dask', wait_for_workers_timeout={})" + ).format( + self.wait_for_workers_timeout, + max(10, 2 * self.wait_for_workers_timeout), + ) + raise TimeoutError(error_msg) from e + return sum(self.client.ncores().values()) + + async def _to_func_args(self, func): + itemgetters = dict() + + # Futures that are dynamically generated during a single call to + # Parallel.__call__. + call_data_futures = getattr(self, "call_data_futures", None) + + async def maybe_to_futures(args): + out = [] + for arg in args: + arg_id = id(arg) + if arg_id in itemgetters: + out.append(itemgetters[arg_id]) + continue + + f = self.data_futures.get(arg_id, None) + if f is None and call_data_futures is not None: + try: + f = await call_data_futures[arg] + except KeyError: + pass + if f is None: + if is_weakrefable(arg) and sizeof(arg) > 1e3: + # Automatically scatter large objects to some of + # the workers to avoid duplicated data transfers. + # Rely on automated inter-worker data stealing if + # more workers need to reuse this data + # concurrently. + # set hash=False - nested scatter calls (i.e + # calling client.scatter inside a dask worker) + # using hash=True often raise CancelledError, + # see dask/distributed#3703 + _coro = self.client.scatter( + arg, asynchronous=True, hash=False + ) + # Centralize the scattering of identical arguments + # between concurrent apply_async callbacks by + # exposing the running coroutine in + # call_data_futures before it completes. + t = asyncio.Task(_coro) + call_data_futures[arg] = t + + f = await t + + if f is not None: + out.append(f) + else: + out.append(arg) + return out + + tasks = [] + for f, args, kwargs in func.items: + args = list(await maybe_to_futures(args)) + kwargs = dict(zip(kwargs.keys(), await maybe_to_futures(kwargs.values()))) + tasks.append((f, args, kwargs)) + + return (Batch(tasks), tasks) + + def apply_async(self, func, callback=None): + cf_future = concurrent.futures.Future() + cf_future.get = cf_future.result # achieve AsyncResult API + + async def f(func, callback): + batch, tasks = await self._to_func_args(func) + key = f"{repr(batch)}-{uuid4().hex}" + + dask_future = self.client.submit( + _TracebackCapturingWrapper(batch), + tasks=tasks, + key=key, + **self.submit_kwargs, + ) + self.waiting_futures.add(dask_future) + self._callbacks[dask_future] = callback + self._results[dask_future] = cf_future + + self.client.loop.add_callback(f, func, callback) + + return cf_future + + def retrieve_result_callback(self, out): + return _retrieve_traceback_capturing_wrapped_call(out) + + def abort_everything(self, ensure_ready=True): + """Tell the client to cancel any task submitted via this instance + + joblib.Parallel will never access those results + """ + with self.waiting_futures.lock: + self.waiting_futures.futures.clear() + while not self.waiting_futures.queue.empty(): + self.waiting_futures.queue.get() + + @contextlib.contextmanager + def retrieval_context(self): + """Override ParallelBackendBase.retrieval_context to avoid deadlocks. + + This removes thread from the worker's thread pool (using 'secede'). + Seceding avoids deadlock in nested parallelism settings. + """ + # See 'joblib.Parallel.__call__' and 'joblib.Parallel.retrieve' for how + # this is used. + if hasattr(thread_state, "execution_state"): + # we are in a worker. Secede to avoid deadlock. + secede() + + yield + + if hasattr(thread_state, "execution_state"): + rejoin() diff --git a/.venv/lib/python3.12/site-packages/joblib/_memmapping_reducer.py b/.venv/lib/python3.12/site-packages/joblib/_memmapping_reducer.py new file mode 100644 index 0000000000000000000000000000000000000000..d11ec581de8d997162397c92690ef080ace2bb33 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib/_memmapping_reducer.py @@ -0,0 +1,715 @@ +""" +Reducer using memory mapping for numpy arrays +""" +# Author: Thomas Moreau +# Copyright: 2017, Thomas Moreau +# License: BSD 3 clause + +import atexit +import errno +import os +import stat +import tempfile +import threading +import time +import warnings +import weakref +from mmap import mmap +from multiprocessing import util +from pickle import HIGHEST_PROTOCOL, PicklingError, dumps, loads, whichmodule +from uuid import uuid4 + +try: + WindowsError +except NameError: + WindowsError = type(None) + +try: + import numpy as np + from numpy.lib.stride_tricks import as_strided +except ImportError: + np = None + +from .backports import make_memmap +from .disk import delete_folder +from .externals.loky.backend import resource_tracker +from .numpy_pickle import dump, load, load_temporary_memmap + +# Some system have a ramdisk mounted by default, we can use it instead of /tmp +# as the default folder to dump big arrays to share with subprocesses. +SYSTEM_SHARED_MEM_FS = "/dev/shm" + +# Minimal number of bytes available on SYSTEM_SHARED_MEM_FS to consider using +# it as the default folder to dump big arrays to share with subprocesses. +SYSTEM_SHARED_MEM_FS_MIN_SIZE = int(2e9) + +# Folder and file permissions to chmod temporary files generated by the +# memmapping pool. Only the owner of the Python process can access the +# temporary files and folder. +FOLDER_PERMISSIONS = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR +FILE_PERMISSIONS = stat.S_IRUSR | stat.S_IWUSR + +# Set used in joblib workers, referencing the filenames of temporary memmaps +# created by joblib to speed up data communication. In child processes, we add +# a finalizer to these memmaps that sends a maybe_unlink call to the +# resource_tracker, in order to free main memory as fast as possible. +JOBLIB_MMAPS = set() + + +def _log_and_unlink(filename): + from .externals.loky.backend.resource_tracker import _resource_tracker + + util.debug( + "[FINALIZER CALL] object mapping to {} about to be deleted," + " decrementing the refcount of the file (pid: {})".format( + os.path.basename(filename), os.getpid() + ) + ) + _resource_tracker.maybe_unlink(filename, "file") + + +def add_maybe_unlink_finalizer(memmap): + util.debug( + "[FINALIZER ADD] adding finalizer to {} (id {}, filename {}, pid {})".format( + type(memmap), id(memmap), os.path.basename(memmap.filename), os.getpid() + ) + ) + weakref.finalize(memmap, _log_and_unlink, memmap.filename) + + +def unlink_file(filename): + """Wrapper around os.unlink with a retry mechanism. + + The retry mechanism has been implemented primarily to overcome a race + condition happening during the finalizer of a np.memmap: when a process + holding the last reference to a mmap-backed np.memmap/np.array is about to + delete this array (and close the reference), it sends a maybe_unlink + request to the resource_tracker. This request can be processed faster than + it takes for the last reference of the memmap to be closed, yielding (on + Windows) a PermissionError in the resource_tracker loop. + """ + NUM_RETRIES = 10 + for retry_no in range(1, NUM_RETRIES + 1): + try: + os.unlink(filename) + break + except PermissionError: + util.debug( + "[ResourceTracker] tried to unlink {}, got PermissionError".format( + filename + ) + ) + if retry_no == NUM_RETRIES: + raise + else: + time.sleep(0.2) + except FileNotFoundError: + # In case of a race condition when deleting the temporary folder, + # avoid noisy FileNotFoundError exception in the resource tracker. + pass + + +resource_tracker._CLEANUP_FUNCS["file"] = unlink_file + + +class _WeakArrayKeyMap: + """A variant of weakref.WeakKeyDictionary for unhashable numpy arrays. + + This datastructure will be used with numpy arrays as obj keys, therefore we + do not use the __get__ / __set__ methods to avoid any conflict with the + numpy fancy indexing syntax. + """ + + def __init__(self): + self._data = {} + + def get(self, obj): + ref, val = self._data[id(obj)] + if ref() is not obj: + # In case of race condition with on_destroy: could never be + # triggered by the joblib tests with CPython. + raise KeyError(obj) + return val + + def set(self, obj, value): + key = id(obj) + try: + ref, _ = self._data[key] + if ref() is not obj: + # In case of race condition with on_destroy: could never be + # triggered by the joblib tests with CPython. + raise KeyError(obj) + except KeyError: + # Insert the new entry in the mapping along with a weakref + # callback to automatically delete the entry from the mapping + # as soon as the object used as key is garbage collected. + def on_destroy(_): + del self._data[key] + + ref = weakref.ref(obj, on_destroy) + self._data[key] = ref, value + + def __getstate__(self): + raise PicklingError("_WeakArrayKeyMap is not pickleable") + + +############################################################################### +# Support for efficient transient pickling of numpy data structures + + +def _get_backing_memmap(a): + """Recursively look up the original np.memmap instance base if any.""" + b = getattr(a, "base", None) + if b is None: + # TODO: check scipy sparse datastructure if scipy is installed + # a nor its descendants do not have a memmap base + return None + + elif isinstance(b, mmap): + # a is already a real memmap instance. + return a + + else: + # Recursive exploration of the base ancestry + return _get_backing_memmap(b) + + +def _get_temp_dir(pool_folder_name, temp_folder=None): + """Get the full path to a subfolder inside the temporary folder. + + Parameters + ---------- + pool_folder_name : str + Sub-folder name used for the serialization of a pool instance. + + temp_folder: str, optional + Folder to be used by the pool for memmapping large arrays + for sharing memory with worker processes. If None, this will try in + order: + + - a folder pointed by the JOBLIB_TEMP_FOLDER environment + variable, + - /dev/shm if the folder exists and is writable: this is a + RAMdisk filesystem available by default on modern Linux + distributions, + - the default system temporary folder that can be + overridden with TMP, TMPDIR or TEMP environment + variables, typically /tmp under Unix operating systems. + + Returns + ------- + pool_folder : str + full path to the temporary folder + use_shared_mem : bool + whether the temporary folder is written to the system shared memory + folder or some other temporary folder. + """ + use_shared_mem = False + if temp_folder is None: + temp_folder = os.environ.get("JOBLIB_TEMP_FOLDER", None) + if temp_folder is None: + if os.path.exists(SYSTEM_SHARED_MEM_FS) and hasattr(os, "statvfs"): + try: + shm_stats = os.statvfs(SYSTEM_SHARED_MEM_FS) + available_nbytes = shm_stats.f_bsize * shm_stats.f_bavail + if available_nbytes > SYSTEM_SHARED_MEM_FS_MIN_SIZE: + # Try to see if we have write access to the shared mem + # folder only if it is reasonably large (that is 2GB or + # more). + temp_folder = SYSTEM_SHARED_MEM_FS + pool_folder = os.path.join(temp_folder, pool_folder_name) + if not os.path.exists(pool_folder): + os.makedirs(pool_folder) + use_shared_mem = True + except (IOError, OSError): + # Missing rights in the /dev/shm partition, fallback to regular + # temp folder. + temp_folder = None + if temp_folder is None: + # Fallback to the default tmp folder, typically /tmp + temp_folder = tempfile.gettempdir() + temp_folder = os.path.abspath(os.path.expanduser(temp_folder)) + pool_folder = os.path.join(temp_folder, pool_folder_name) + return pool_folder, use_shared_mem + + +def has_shareable_memory(a): + """Return True if a is backed by some mmap buffer directly or not.""" + return _get_backing_memmap(a) is not None + + +def _strided_from_memmap( + filename, + dtype, + mode, + offset, + order, + shape, + strides, + total_buffer_len, + unlink_on_gc_collect, +): + """Reconstruct an array view on a memory mapped file.""" + if mode == "w+": + # Do not zero the original data when unpickling + mode = "r+" + + if strides is None: + # Simple, contiguous memmap + return make_memmap( + filename, + dtype=dtype, + shape=shape, + mode=mode, + offset=offset, + order=order, + unlink_on_gc_collect=unlink_on_gc_collect, + ) + else: + # For non-contiguous data, memmap the total enclosing buffer and then + # extract the non-contiguous view with the stride-tricks API + base = make_memmap( + filename, + dtype=dtype, + shape=total_buffer_len, + offset=offset, + mode=mode, + order=order, + unlink_on_gc_collect=unlink_on_gc_collect, + ) + return as_strided(base, shape=shape, strides=strides) + + +def _reduce_memmap_backed(a, m): + """Pickling reduction for memmap backed arrays. + + a is expected to be an instance of np.ndarray (or np.memmap) + m is expected to be an instance of np.memmap on the top of the ``base`` + attribute ancestry of a. ``m.base`` should be the real python mmap object. + """ + # offset that comes from the striding differences between a and m + util.debug( + "[MEMMAP REDUCE] reducing a memmap-backed array (shape, {}, pid: {})".format( + a.shape, os.getpid() + ) + ) + try: + from numpy.lib.array_utils import byte_bounds + except (ModuleNotFoundError, ImportError): + # Backward-compat for numpy < 2.0 + from numpy import byte_bounds + a_start, a_end = byte_bounds(a) + m_start = byte_bounds(m)[0] + offset = a_start - m_start + + # offset from the backing memmap + offset += m.offset + + # 1D arrays are both F and C contiguous, so only set the flag in + # higher dimensions. See https://github.com/joblib/joblib/pull/1704. + if m.ndim > 1 and m.flags["F_CONTIGUOUS"]: + order = "F" + else: + # The backing memmap buffer is necessarily contiguous hence C if not + # Fortran + order = "C" + + if a.flags["F_CONTIGUOUS"] or a.flags["C_CONTIGUOUS"]: + # If the array is a contiguous view, no need to pass the strides + strides = None + total_buffer_len = None + else: + # Compute the total number of items to map from which the strided + # view will be extracted. + strides = a.strides + total_buffer_len = (a_end - a_start) // a.itemsize + + return ( + _strided_from_memmap, + ( + m.filename, + a.dtype, + m.mode, + offset, + order, + a.shape, + strides, + total_buffer_len, + False, + ), + ) + + +def reduce_array_memmap_backward(a): + """reduce a np.array or a np.memmap from a child process""" + m = _get_backing_memmap(a) + if isinstance(m, np.memmap) and m.filename not in JOBLIB_MMAPS: + # if a is backed by a memmaped file, reconstruct a using the + # memmaped file. + return _reduce_memmap_backed(a, m) + else: + # a is either a regular (not memmap-backed) numpy array, or an array + # backed by a shared temporary file created by joblib. In the latter + # case, in order to limit the lifespan of these temporary files, we + # serialize the memmap as a regular numpy array, and decref the + # file backing the memmap (done implicitly in a previously registered + # finalizer, see ``unlink_on_gc_collect`` for more details) + return (loads, (dumps(np.asarray(a), protocol=HIGHEST_PROTOCOL),)) + + +class ArrayMemmapForwardReducer(object): + """Reducer callable to dump large arrays to memmap files. + + Parameters + ---------- + max_nbytes: int + Threshold to trigger memmapping of large arrays to files created + a folder. + temp_folder_resolver: callable + An callable in charge of resolving a temporary folder name where files + for backing memmapped arrays are created. + mmap_mode: 'r', 'r+' or 'c' + Mode for the created memmap datastructure. See the documentation of + numpy.memmap for more details. Note: 'w+' is coerced to 'r+' + automatically to avoid zeroing the data on unpickling. + verbose: int, optional, 0 by default + If verbose > 0, memmap creations are logged. + If verbose > 1, both memmap creations, reuse and array pickling are + logged. + prewarm: bool, optional, False by default. + Force a read on newly memmapped array to make sure that OS pre-cache it + memory. This can be useful to avoid concurrent disk access when the + same data array is passed to different worker processes. + """ + + def __init__( + self, + max_nbytes, + temp_folder_resolver, + mmap_mode, + unlink_on_gc_collect, + verbose=0, + prewarm=True, + ): + self._max_nbytes = max_nbytes + self._temp_folder_resolver = temp_folder_resolver + self._mmap_mode = mmap_mode + self.verbose = int(verbose) + if prewarm == "auto": + self._prewarm = not self._temp_folder.startswith(SYSTEM_SHARED_MEM_FS) + else: + self._prewarm = prewarm + self._prewarm = prewarm + self._memmaped_arrays = _WeakArrayKeyMap() + self._temporary_memmaped_filenames = set() + self._unlink_on_gc_collect = unlink_on_gc_collect + + @property + def _temp_folder(self): + return self._temp_folder_resolver() + + def __reduce__(self): + # The ArrayMemmapForwardReducer is passed to the children processes: it + # needs to be pickled but the _WeakArrayKeyMap need to be skipped as + # it's only guaranteed to be consistent with the parent process memory + # garbage collection. + # Although this reducer is pickled, it is not needed in its destination + # process (child processes), as we only use this reducer to send + # memmaps from the parent process to the children processes. For this + # reason, we can afford skipping the resolver, (which would otherwise + # be unpicklable), and pass it as None instead. + args = (self._max_nbytes, None, self._mmap_mode, self._unlink_on_gc_collect) + kwargs = { + "verbose": self.verbose, + "prewarm": self._prewarm, + } + return ArrayMemmapForwardReducer, args, kwargs + + def __call__(self, a): + m = _get_backing_memmap(a) + if m is not None and isinstance(m, np.memmap): + # a is already backed by a memmap file, let's reuse it directly + return _reduce_memmap_backed(a, m) + + if ( + not a.dtype.hasobject + and self._max_nbytes is not None + and a.nbytes > self._max_nbytes + ): + # check that the folder exists (lazily create the pool temp folder + # if required) + try: + os.makedirs(self._temp_folder) + os.chmod(self._temp_folder, FOLDER_PERMISSIONS) + except OSError as e: + if e.errno != errno.EEXIST: + raise e + + try: + basename = self._memmaped_arrays.get(a) + except KeyError: + # Generate a new unique random filename. The process and thread + # ids are only useful for debugging purpose and to make it + # easier to cleanup orphaned files in case of hard process + # kill (e.g. by "kill -9" or segfault). + basename = "{}-{}-{}.pkl".format( + os.getpid(), id(threading.current_thread()), uuid4().hex + ) + self._memmaped_arrays.set(a, basename) + filename = os.path.join(self._temp_folder, basename) + + # In case the same array with the same content is passed several + # times to the pool subprocess children, serialize it only once + + is_new_memmap = filename not in self._temporary_memmaped_filenames + + # add the memmap to the list of temporary memmaps created by joblib + self._temporary_memmaped_filenames.add(filename) + + if self._unlink_on_gc_collect: + # Bump reference count of the memmap by 1 to account for + # shared usage of the memmap by a child process. The + # corresponding decref call will be executed upon calling + # resource_tracker.maybe_unlink, registered as a finalizer in + # the child. + # the incref/decref calls here are only possible when the child + # and the parent share the same resource_tracker. It is not the + # case for the multiprocessing backend, but it does not matter + # because unlinking a memmap from a child process is only + # useful to control the memory usage of long-lasting child + # processes, while the multiprocessing-based pools terminate + # their workers at the end of a map() call. + resource_tracker.register(filename, "file") + + if is_new_memmap: + # Incref each temporary memmap created by joblib one extra + # time. This means that these memmaps will only be deleted + # once an extra maybe_unlink() is called, which is done once + # all the jobs have completed (or been canceled) in the + # Parallel._terminate_backend() method. + resource_tracker.register(filename, "file") + + if not os.path.exists(filename): + util.debug( + "[ARRAY DUMP] Pickling new array (shape={}, dtype={}) " + "creating a new memmap at {}".format(a.shape, a.dtype, filename) + ) + for dumped_filename in dump(a, filename): + os.chmod(dumped_filename, FILE_PERMISSIONS) + + if self._prewarm: + # Warm up the data by accessing it. This operation ensures + # that the disk access required to create the memmapping + # file are performed in the reducing process and avoids + # concurrent memmap creation in multiple children + # processes. + load(filename, mmap_mode=self._mmap_mode).max() + + else: + util.debug( + "[ARRAY DUMP] Pickling known array (shape={}, dtype={}) " + "reusing memmap file: {}".format( + a.shape, a.dtype, os.path.basename(filename) + ) + ) + + # The worker process will use joblib.load to memmap the data + return ( + load_temporary_memmap, + (filename, self._mmap_mode, self._unlink_on_gc_collect), + ) + else: + # do not convert a into memmap, let pickler do its usual copy with + # the default system pickler + util.debug( + "[ARRAY DUMP] Pickling array (NO MEMMAPPING) (shape={}, " + " dtype={}).".format(a.shape, a.dtype) + ) + return (loads, (dumps(a, protocol=HIGHEST_PROTOCOL),)) + + +def get_memmapping_reducers( + forward_reducers=None, + backward_reducers=None, + temp_folder_resolver=None, + max_nbytes=1e6, + mmap_mode="r", + verbose=0, + prewarm=False, + unlink_on_gc_collect=True, + **kwargs, +): + """Construct a pair of memmapping reducer linked to a tmpdir. + + This function manage the creation and the clean up of the temporary folders + underlying the memory maps and should be use to get the reducers necessary + to construct joblib pool or executor. + """ + if forward_reducers is None: + forward_reducers = dict() + if backward_reducers is None: + backward_reducers = dict() + + if np is not None: + # Register smart numpy.ndarray reducers that detects memmap backed + # arrays and that is also able to dump to memmap large in-memory + # arrays over the max_nbytes threshold + forward_reduce_ndarray = ArrayMemmapForwardReducer( + max_nbytes, + temp_folder_resolver, + mmap_mode, + unlink_on_gc_collect, + verbose, + prewarm=prewarm, + ) + forward_reducers[np.ndarray] = forward_reduce_ndarray + forward_reducers[np.memmap] = forward_reduce_ndarray + + # Communication from child process to the parent process always + # pickles in-memory numpy.ndarray without dumping them as memmap + # to avoid confusing the caller and make it tricky to collect the + # temporary folder + backward_reducers[np.ndarray] = reduce_array_memmap_backward + backward_reducers[np.memmap] = reduce_array_memmap_backward + + return forward_reducers, backward_reducers + + +class TemporaryResourcesManager(object): + """Stateful object able to manage temporary folder and pickles + + It exposes: + - a per-context folder name resolving API that memmap-based reducers will + rely on to know where to pickle the temporary memmaps + - a temporary file/folder management API that internally uses the + resource_tracker. + """ + + def __init__(self, temp_folder_root=None, context_id=None): + self._current_temp_folder = None + self._temp_folder_root = temp_folder_root + self._use_shared_mem = None + self._cached_temp_folders = dict() + self._id = uuid4().hex + self._finalizers = {} + if context_id is None: + # It would be safer to not assign a default context id (less silent + # bugs), but doing this while maintaining backward compatibility + # with the previous, context-unaware version get_memmaping_executor + # exposes too many low-level details. + context_id = uuid4().hex + self.set_current_context(context_id) + + def set_current_context(self, context_id): + self._current_context_id = context_id + self.register_new_context(context_id) + + def register_new_context(self, context_id): + # Prepare a sub-folder name specific to a context (usually a unique id + # generated by each instance of the Parallel class). Do not create in + # advance to spare FS write access if no array is to be dumped). + if context_id in self._cached_temp_folders: + return + else: + # During its lifecycle, one Parallel object can have several + # executors associated to it (for instance, if a loky worker raises + # an exception, joblib shutdowns the executor and instantly + # recreates a new one before raising the error - see + # ``ensure_ready``. Because we don't want two executors tied to + # the same Parallel object (and thus the same context id) to + # register/use/delete the same folder, we also add an id specific + # to the current Manager (and thus specific to its associated + # executor) to the folder name. + new_folder_name = "joblib_memmapping_folder_{}_{}_{}".format( + os.getpid(), self._id, context_id + ) + new_folder_path, _ = _get_temp_dir(new_folder_name, self._temp_folder_root) + self.register_folder_finalizer(new_folder_path, context_id) + self._cached_temp_folders[context_id] = new_folder_path + + def resolve_temp_folder_name(self): + """Return a folder name specific to the currently activated context""" + return self._cached_temp_folders[self._current_context_id] + + # resource management API + + def register_folder_finalizer(self, pool_subfolder, context_id): + # Register the garbage collector at program exit in case caller forgets + # to call terminate explicitly: note we do not pass any reference to + # ensure that this callback won't prevent garbage collection of + # parallel instance and related file handler resources such as POSIX + # semaphores and pipes + pool_module_name = whichmodule(delete_folder, "delete_folder") + resource_tracker.register(pool_subfolder, "folder") + + def _cleanup(): + # In some cases the Python runtime seems to set delete_folder to + # None just before exiting when accessing the delete_folder + # function from the closure namespace. So instead we reimport + # the delete_folder function explicitly. + # https://github.com/joblib/joblib/issues/328 + # We cannot just use from 'joblib.pool import delete_folder' + # because joblib should only use relative imports to allow + # easy vendoring. + delete_folder = __import__( + pool_module_name, fromlist=["delete_folder"] + ).delete_folder + try: + delete_folder(pool_subfolder, allow_non_empty=True) + resource_tracker.unregister(pool_subfolder, "folder") + except OSError: + warnings.warn( + "Failed to delete temporary folder: {}".format(pool_subfolder) + ) + + self._finalizers[context_id] = atexit.register(_cleanup) + + def _clean_temporary_resources( + self, context_id=None, force=False, allow_non_empty=False + ): + """Clean temporary resources created by a process-based pool""" + if context_id is None: + # Iterates over a copy of the cache keys to avoid Error due to + # iterating over a changing size dictionary. + for context_id in list(self._cached_temp_folders): + self._clean_temporary_resources( + context_id, force=force, allow_non_empty=allow_non_empty + ) + else: + temp_folder = self._cached_temp_folders.get(context_id) + if temp_folder and os.path.exists(temp_folder): + for filename in os.listdir(temp_folder): + if force: + # Some workers have failed and the ref counted might + # be off. The workers should have shut down by this + # time so forcefully clean up the files. + resource_tracker.unregister( + os.path.join(temp_folder, filename), "file" + ) + else: + resource_tracker.maybe_unlink( + os.path.join(temp_folder, filename), "file" + ) + + # When forcing clean-up, try to delete the folder even if some + # files are still in it. Otherwise, try to delete the folder + allow_non_empty |= force + + # Clean up the folder if possible, either if it is empty or + # if none of the files in it are in used and allow_non_empty. + try: + delete_folder(temp_folder, allow_non_empty=allow_non_empty) + # Forget the folder once it has been deleted + self._cached_temp_folders.pop(context_id, None) + resource_tracker.unregister(temp_folder, "folder") + + # Also cancel the finalizers that gets triggered at gc. + finalizer = self._finalizers.pop(context_id, None) + if finalizer is not None: + atexit.unregister(finalizer) + + except OSError: + # Temporary folder cannot be deleted right now. + # This folder will be cleaned up by an atexit + # finalizer registered by the memmapping_reducer. + pass diff --git a/.venv/lib/python3.12/site-packages/joblib/_multiprocessing_helpers.py b/.venv/lib/python3.12/site-packages/joblib/_multiprocessing_helpers.py new file mode 100644 index 0000000000000000000000000000000000000000..1b4e7d20e9280cf20befbc737ac019a0ec66f4ba --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib/_multiprocessing_helpers.py @@ -0,0 +1,51 @@ +"""Helper module to factorize the conditional multiprocessing import logic + +We use a distinct module to simplify import statements and avoid introducing +circular dependencies (for instance for the assert_spawning name). +""" + +import os +import warnings + +# Obtain possible configuration from the environment, assuming 1 (on) +# by default, upon 0 set to None. Should instructively fail if some non +# 0/1 value is set. +mp = int(os.environ.get("JOBLIB_MULTIPROCESSING", 1)) or None +if mp: + try: + import _multiprocessing # noqa + import multiprocessing as mp + except ImportError: + mp = None + +# 2nd stage: validate that locking is available on the system and +# issue a warning if not +if mp is not None: + try: + # try to create a named semaphore using SemLock to make sure they are + # available on this platform. We use the low level object + # _multiprocessing.SemLock to avoid spawning a resource tracker on + # Unix system or changing the default backend. + import tempfile + from _multiprocessing import SemLock + + _rand = tempfile._RandomNameSequence() + for i in range(100): + try: + name = "/joblib-{}-{}".format(os.getpid(), next(_rand)) + _sem = SemLock(0, 0, 1, name=name, unlink=True) + del _sem # cleanup + break + except FileExistsError as e: # pragma: no cover + if i >= 99: + raise FileExistsError("cannot find name for semaphore") from e + except (FileExistsError, AttributeError, ImportError, OSError) as e: + mp = None + warnings.warn("%s. joblib will operate in serial mode" % (e,)) + + +# 3rd stage: backward compat for the assert_spawning helper +if mp is not None: + from multiprocessing.context import assert_spawning +else: + assert_spawning = None diff --git a/.venv/lib/python3.12/site-packages/joblib/_parallel_backends.py b/.venv/lib/python3.12/site-packages/joblib/_parallel_backends.py new file mode 100644 index 0000000000000000000000000000000000000000..53114a8512fcbcd320f2d62cebe06f1cb9102fa8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib/_parallel_backends.py @@ -0,0 +1,753 @@ +""" +Backends for embarrassingly parallel code. +""" + +import contextlib +import gc +import os +import threading +import warnings +from abc import ABCMeta, abstractmethod + +from ._multiprocessing_helpers import mp +from ._utils import ( + _retrieve_traceback_capturing_wrapped_call, + _TracebackCapturingWrapper, +) + +if mp is not None: + from multiprocessing.pool import ThreadPool + + from .executor import get_memmapping_executor + + # Import loky only if multiprocessing is present + from .externals.loky import cpu_count, process_executor + from .externals.loky.process_executor import ShutdownExecutorError + from .pool import MemmappingPool + + +class ParallelBackendBase(metaclass=ABCMeta): + """Helper abc which defines all methods a ParallelBackend must implement""" + + default_n_jobs = 1 + + supports_inner_max_num_threads = False + + # This flag was introduced for backward compatibility reasons. + # New backends should always set it to True and implement the + # `retrieve_result_callback` method. + supports_retrieve_callback = False + + @property + def supports_return_generator(self): + return self.supports_retrieve_callback + + @property + def supports_timeout(self): + return self.supports_retrieve_callback + + nesting_level = None + + def __init__( + self, nesting_level=None, inner_max_num_threads=None, **backend_kwargs + ): + super().__init__() + self.nesting_level = nesting_level + self.inner_max_num_threads = inner_max_num_threads + self.backend_kwargs = backend_kwargs + + MAX_NUM_THREADS_VARS = [ + "OMP_NUM_THREADS", + "OPENBLAS_NUM_THREADS", + "MKL_NUM_THREADS", + "BLIS_NUM_THREADS", + "VECLIB_MAXIMUM_THREADS", + "NUMBA_NUM_THREADS", + "NUMEXPR_NUM_THREADS", + ] + + TBB_ENABLE_IPC_VAR = "ENABLE_IPC" + + @abstractmethod + def effective_n_jobs(self, n_jobs): + """Determine the number of jobs that can actually run in parallel + + n_jobs is the number of workers requested by the callers. Passing + n_jobs=-1 means requesting all available workers for instance matching + the number of CPU cores on the worker host(s). + + This method should return a guesstimate of the number of workers that + can actually perform work concurrently. The primary use case is to make + it possible for the caller to know in how many chunks to slice the + work. + + In general working on larger data chunks is more efficient (less + scheduling overhead and better use of CPU cache prefetching heuristics) + as long as all the workers have enough work to do. + """ + + def apply_async(self, func, callback=None): + """Deprecated: implement `submit` instead.""" + raise NotImplementedError("Implement `submit` instead.") + + def submit(self, func, callback=None): + """Schedule a function to be run and return a future-like object. + + This method should return a future-like object that allow tracking + the progress of the task. + + If ``supports_retrieve_callback`` is False, the return value of this + method is passed to ``retrieve_result`` instead of calling + ``retrieve_result_callback``. + + Parameters + ---------- + func: callable + The function to be run in parallel. + + callback: callable + A callable that will be called when the task is completed. This callable + is a wrapper around ``retrieve_result_callback``. This should be added + to the future-like object returned by this method, so that the callback + is called when the task is completed. + + For future-like backends, this can be achieved with something like + ``future.add_done_callback(callback)``. + + Returns + ------- + future: future-like + A future-like object to track the execution of the submitted function. + """ + warnings.warn( + "`apply_async` is deprecated, implement and use `submit` instead.", + DeprecationWarning, + ) + return self.apply_async(func, callback) + + def retrieve_result_callback(self, out): + """Called within the callback function passed to `submit`. + + This method can customise how the result of the function is retrieved + from the future-like object. + + Parameters + ---------- + future: future-like + The future-like object returned by the `submit` method. + + Returns + ------- + result: object + The result of the function executed in parallel. + """ + + def retrieve_result(self, out, timeout=None): + """Hook to retrieve the result when support_retrieve_callback=False. + + The argument `out` is the result of the `submit` call. This method + should return the result of the computation or raise an exception if + the computation failed. + """ + if self.supports_timeout: + return out.get(timeout=timeout) + else: + return out.get() + + def configure( + self, n_jobs=1, parallel=None, prefer=None, require=None, **backend_kwargs + ): + """Reconfigure the backend and return the number of workers. + + This makes it possible to reuse an existing backend instance for + successive independent calls to Parallel with different parameters. + """ + self.parallel = parallel + return self.effective_n_jobs(n_jobs) + + def start_call(self): + """Call-back method called at the beginning of a Parallel call""" + + def stop_call(self): + """Call-back method called at the end of a Parallel call""" + + def terminate(self): + """Shutdown the workers and free the shared memory.""" + + def compute_batch_size(self): + """Determine the optimal batch size""" + return 1 + + def batch_completed(self, batch_size, duration): + """Callback indicate how long it took to run a batch""" + + def abort_everything(self, ensure_ready=True): + """Abort any running tasks + + This is called when an exception has been raised when executing a task + and all the remaining tasks will be ignored and can therefore be + aborted to spare computation resources. + + If ensure_ready is True, the backend should be left in an operating + state as future tasks might be re-submitted via that same backend + instance. + + If ensure_ready is False, the implementer of this method can decide + to leave the backend in a closed / terminated state as no new task + are expected to be submitted to this backend. + + Setting ensure_ready to False is an optimization that can be leveraged + when aborting tasks via killing processes from a local process pool + managed by the backend it-self: if we expect no new tasks, there is no + point in re-creating new workers. + """ + # Does nothing by default: to be overridden in subclasses when + # canceling tasks is possible. + pass + + def get_nested_backend(self): + """Backend instance to be used by nested Parallel calls. + + By default a thread-based backend is used for the first level of + nesting. Beyond, switch to sequential backend to avoid spawning too + many threads on the host. + """ + nesting_level = getattr(self, "nesting_level", 0) + 1 + if nesting_level > 1: + return SequentialBackend(nesting_level=nesting_level), None + else: + return ThreadingBackend(nesting_level=nesting_level), None + + def _prepare_worker_env(self, n_jobs): + """Return environment variables limiting threadpools in external libs. + + This function return a dict containing environment variables to pass + when creating a pool of process. These environment variables limit the + number of threads to `n_threads` for OpenMP, MKL, Accelerated and + OpenBLAS libraries in the child processes. + """ + explicit_n_threads = self.inner_max_num_threads + default_n_threads = max(cpu_count() // n_jobs, 1) + + # Set the inner environment variables to self.inner_max_num_threads if + # it is given. Else, default to cpu_count // n_jobs unless the variable + # is already present in the parent process environment. + env = {} + for var in self.MAX_NUM_THREADS_VARS: + if explicit_n_threads is None: + var_value = os.environ.get(var, default_n_threads) + else: + var_value = explicit_n_threads + + env[var] = str(var_value) + + if self.TBB_ENABLE_IPC_VAR not in os.environ: + # To avoid over-subscription when using TBB, let the TBB schedulers + # use Inter Process Communication to coordinate: + env[self.TBB_ENABLE_IPC_VAR] = "1" + return env + + @contextlib.contextmanager + def retrieval_context(self): + """Context manager to manage an execution context. + + Calls to Parallel.retrieve will be made inside this context. + + By default, this does nothing. It may be useful for subclasses to + handle nested parallelism. In particular, it may be required to avoid + deadlocks if a backend manages a fixed number of workers, when those + workers may be asked to do nested Parallel calls. Without + 'retrieval_context' this could lead to deadlock, as all the workers + managed by the backend may be "busy" waiting for the nested parallel + calls to finish, but the backend has no free workers to execute those + tasks. + """ + yield + + @staticmethod + def in_main_thread(): + return isinstance(threading.current_thread(), threading._MainThread) + + +class SequentialBackend(ParallelBackendBase): + """A ParallelBackend which will execute all batches sequentially. + + Does not use/create any threading objects, and hence has minimal + overhead. Used when n_jobs == 1. + """ + + uses_threads = True + supports_timeout = False + supports_retrieve_callback = False + supports_sharedmem = True + + def effective_n_jobs(self, n_jobs): + """Determine the number of jobs which are going to run in parallel""" + if n_jobs == 0: + raise ValueError("n_jobs == 0 in Parallel has no meaning") + return 1 + + def submit(self, func, callback=None): + """Schedule a func to be run""" + raise RuntimeError("Should never be called for SequentialBackend.") + + def retrieve_result_callback(self, out): + raise RuntimeError("Should never be called for SequentialBackend.") + + def get_nested_backend(self): + # import is not top level to avoid cyclic import errors. + from .parallel import get_active_backend + + # SequentialBackend should neither change the nesting level, the + # default backend or the number of jobs. Just return the current one. + return get_active_backend() + + +class PoolManagerMixin(object): + """A helper class for managing pool of workers.""" + + _pool = None + + def effective_n_jobs(self, n_jobs): + """Determine the number of jobs which are going to run in parallel""" + if n_jobs == 0: + raise ValueError("n_jobs == 0 in Parallel has no meaning") + elif mp is None or n_jobs is None: + # multiprocessing is not available or disabled, fallback + # to sequential mode + return 1 + elif n_jobs < 0: + n_jobs = max(cpu_count() + 1 + n_jobs, 1) + return n_jobs + + def terminate(self): + """Shutdown the process or thread pool""" + if self._pool is not None: + self._pool.close() + self._pool.terminate() # terminate does a join() + self._pool = None + + def _get_pool(self): + """Used by `submit` to make it possible to implement lazy init""" + return self._pool + + def submit(self, func, callback=None): + """Schedule a func to be run""" + # Here, we need a wrapper to avoid crashes on KeyboardInterruptErrors. + # We also call the callback on error, to make sure the pool does not + # wait on crashed jobs. + return self._get_pool().apply_async( + _TracebackCapturingWrapper(func), + (), + callback=callback, + error_callback=callback, + ) + + def retrieve_result_callback(self, result): + """Mimic concurrent.futures results, raising an error if needed.""" + # In the multiprocessing Pool API, the callback are called with the + # result value as an argument so `result`(`out`) is the output of + # job.get(). It's either the result or the exception raised while + # collecting the result. + return _retrieve_traceback_capturing_wrapped_call(result) + + def abort_everything(self, ensure_ready=True): + """Shutdown the pool and restart a new one with the same parameters""" + self.terminate() + if ensure_ready: + self.configure( + n_jobs=self.parallel.n_jobs, + parallel=self.parallel, + **self.parallel._backend_kwargs, + ) + + +class AutoBatchingMixin(object): + """A helper class for automagically batching jobs.""" + + # In seconds, should be big enough to hide multiprocessing dispatching + # overhead. + # This settings was found by running benchmarks/bench_auto_batching.py + # with various parameters on various platforms. + MIN_IDEAL_BATCH_DURATION = 0.2 + + # Should not be too high to avoid stragglers: long jobs running alone + # on a single worker while other workers have no work to process any more. + MAX_IDEAL_BATCH_DURATION = 2 + + # Batching counters default values + _DEFAULT_EFFECTIVE_BATCH_SIZE = 1 + _DEFAULT_SMOOTHED_BATCH_DURATION = 0.0 + + def __init__(self, **kwargs): + super().__init__(**kwargs) + self._effective_batch_size = self._DEFAULT_EFFECTIVE_BATCH_SIZE + self._smoothed_batch_duration = self._DEFAULT_SMOOTHED_BATCH_DURATION + + def compute_batch_size(self): + """Determine the optimal batch size""" + old_batch_size = self._effective_batch_size + batch_duration = self._smoothed_batch_duration + if batch_duration > 0 and batch_duration < self.MIN_IDEAL_BATCH_DURATION: + # The current batch size is too small: the duration of the + # processing of a batch of task is not large enough to hide + # the scheduling overhead. + ideal_batch_size = int( + old_batch_size * self.MIN_IDEAL_BATCH_DURATION / batch_duration + ) + # Multiply by two to limit oscilations between min and max. + ideal_batch_size *= 2 + + # dont increase the batch size too fast to limit huge batch sizes + # potentially leading to starving worker + batch_size = min(2 * old_batch_size, ideal_batch_size) + + batch_size = max(batch_size, 1) + + self._effective_batch_size = batch_size + if self.parallel.verbose >= 10: + self.parallel._print( + f"Batch computation too fast ({batch_duration}s.) " + f"Setting batch_size={batch_size}." + ) + elif batch_duration > self.MAX_IDEAL_BATCH_DURATION and old_batch_size >= 2: + # The current batch size is too big. If we schedule overly long + # running batches some CPUs might wait with nothing left to do + # while a couple of CPUs a left processing a few long running + # batches. Better reduce the batch size a bit to limit the + # likelihood of scheduling such stragglers. + + # decrease the batch size quickly to limit potential starving + ideal_batch_size = int( + old_batch_size * self.MIN_IDEAL_BATCH_DURATION / batch_duration + ) + # Multiply by two to limit oscilations between min and max. + batch_size = max(2 * ideal_batch_size, 1) + self._effective_batch_size = batch_size + if self.parallel.verbose >= 10: + self.parallel._print( + f"Batch computation too slow ({batch_duration}s.) " + f"Setting batch_size={batch_size}." + ) + else: + # No batch size adjustment + batch_size = old_batch_size + + if batch_size != old_batch_size: + # Reset estimation of the smoothed mean batch duration: this + # estimate is updated in the multiprocessing apply_async + # CallBack as long as the batch_size is constant. Therefore + # we need to reset the estimate whenever we re-tune the batch + # size. + self._smoothed_batch_duration = self._DEFAULT_SMOOTHED_BATCH_DURATION + + return batch_size + + def batch_completed(self, batch_size, duration): + """Callback indicate how long it took to run a batch""" + if batch_size == self._effective_batch_size: + # Update the smoothed streaming estimate of the duration of a batch + # from dispatch to completion + old_duration = self._smoothed_batch_duration + if old_duration == self._DEFAULT_SMOOTHED_BATCH_DURATION: + # First record of duration for this batch size after the last + # reset. + new_duration = duration + else: + # Update the exponentially weighted average of the duration of + # batch for the current effective size. + new_duration = 0.8 * old_duration + 0.2 * duration + self._smoothed_batch_duration = new_duration + + def reset_batch_stats(self): + """Reset batch statistics to default values. + + This avoids interferences with future jobs. + """ + self._effective_batch_size = self._DEFAULT_EFFECTIVE_BATCH_SIZE + self._smoothed_batch_duration = self._DEFAULT_SMOOTHED_BATCH_DURATION + + +class ThreadingBackend(PoolManagerMixin, ParallelBackendBase): + """A ParallelBackend which will use a thread pool to execute batches in. + + This is a low-overhead backend but it suffers from the Python Global + Interpreter Lock if the called function relies a lot on Python objects. + Mostly useful when the execution bottleneck is a compiled extension that + explicitly releases the GIL (for instance a Cython loop wrapped in a "with + nogil" block or an expensive call to a library such as NumPy). + + The actual thread pool is lazily initialized: the actual thread pool + construction is delayed to the first call to apply_async. + + ThreadingBackend is used as the default backend for nested calls. + """ + + supports_retrieve_callback = True + uses_threads = True + supports_sharedmem = True + + def configure(self, n_jobs=1, parallel=None, **backend_kwargs): + """Build a process or thread pool and return the number of workers""" + n_jobs = self.effective_n_jobs(n_jobs) + if n_jobs == 1: + # Avoid unnecessary overhead and use sequential backend instead. + raise FallbackToBackend(SequentialBackend(nesting_level=self.nesting_level)) + self.parallel = parallel + self._n_jobs = n_jobs + return n_jobs + + def _get_pool(self): + """Lazily initialize the thread pool + + The actual pool of worker threads is only initialized at the first + call to apply_async. + """ + if self._pool is None: + self._pool = ThreadPool(self._n_jobs) + return self._pool + + +class MultiprocessingBackend(PoolManagerMixin, AutoBatchingMixin, ParallelBackendBase): + """A ParallelBackend which will use a multiprocessing.Pool. + + Will introduce some communication and memory overhead when exchanging + input and output data with the with the worker Python processes. + However, does not suffer from the Python Global Interpreter Lock. + """ + + supports_retrieve_callback = True + supports_return_generator = False + + def effective_n_jobs(self, n_jobs): + """Determine the number of jobs which are going to run in parallel. + + This also checks if we are attempting to create a nested parallel + loop. + """ + if mp is None: + return 1 + + if mp.current_process().daemon: + # Daemonic processes cannot have children + if n_jobs != 1: + if inside_dask_worker(): + msg = ( + "Inside a Dask worker with daemon=True, " + "setting n_jobs=1.\nPossible work-arounds:\n" + "- dask.config.set(" + "{'distributed.worker.daemon': False})" + "- set the environment variable " + "DASK_DISTRIBUTED__WORKER__DAEMON=False\n" + "before creating your Dask cluster." + ) + else: + msg = ( + "Multiprocessing-backed parallel loops " + "cannot be nested, setting n_jobs=1" + ) + warnings.warn(msg, stacklevel=3) + return 1 + + if process_executor._CURRENT_DEPTH > 0: + # Mixing loky and multiprocessing in nested loop is not supported + if n_jobs != 1: + warnings.warn( + "Multiprocessing-backed parallel loops cannot be nested," + " below loky, setting n_jobs=1", + stacklevel=3, + ) + return 1 + + elif not (self.in_main_thread() or self.nesting_level == 0): + # Prevent posix fork inside in non-main posix threads + if n_jobs != 1: + warnings.warn( + "Multiprocessing-backed parallel loops cannot be nested" + " below threads, setting n_jobs=1", + stacklevel=3, + ) + return 1 + + return super(MultiprocessingBackend, self).effective_n_jobs(n_jobs) + + def configure( + self, + n_jobs=1, + parallel=None, + prefer=None, + require=None, + **memmapping_pool_kwargs, + ): + """Build a process or thread pool and return the number of workers""" + n_jobs = self.effective_n_jobs(n_jobs) + if n_jobs == 1: + raise FallbackToBackend(SequentialBackend(nesting_level=self.nesting_level)) + + memmapping_pool_kwargs = { + **self.backend_kwargs, + **memmapping_pool_kwargs, + } + + # Make sure to free as much memory as possible before forking + gc.collect() + self._pool = MemmappingPool(n_jobs, **memmapping_pool_kwargs) + self.parallel = parallel + return n_jobs + + def terminate(self): + """Shutdown the process or thread pool""" + super(MultiprocessingBackend, self).terminate() + self.reset_batch_stats() + + +class LokyBackend(AutoBatchingMixin, ParallelBackendBase): + """Managing pool of workers with loky instead of multiprocessing.""" + + supports_retrieve_callback = True + supports_inner_max_num_threads = True + + def configure( + self, + n_jobs=1, + parallel=None, + prefer=None, + require=None, + idle_worker_timeout=None, + **memmapping_executor_kwargs, + ): + """Build a process executor and return the number of workers""" + n_jobs = self.effective_n_jobs(n_jobs) + if n_jobs == 1: + raise FallbackToBackend(SequentialBackend(nesting_level=self.nesting_level)) + + memmapping_executor_kwargs = { + **self.backend_kwargs, + **memmapping_executor_kwargs, + } + + # Prohibit the use of 'timeout' in the LokyBackend, as 'idle_worker_timeout' + # better describes the backend's behavior. + if "timeout" in memmapping_executor_kwargs: + raise ValueError( + "The 'timeout' parameter is not supported by the LokyBackend. " + "Please use the `idle_worker_timeout` parameter instead." + ) + if idle_worker_timeout is None: + idle_worker_timeout = self.backend_kwargs.get("idle_worker_timeout", 300) + + self._workers = get_memmapping_executor( + n_jobs, + timeout=idle_worker_timeout, + env=self._prepare_worker_env(n_jobs=n_jobs), + context_id=parallel._id, + **memmapping_executor_kwargs, + ) + self.parallel = parallel + return n_jobs + + def effective_n_jobs(self, n_jobs): + """Determine the number of jobs which are going to run in parallel""" + if n_jobs == 0: + raise ValueError("n_jobs == 0 in Parallel has no meaning") + elif mp is None or n_jobs is None: + # multiprocessing is not available or disabled, fallback + # to sequential mode + return 1 + elif mp.current_process().daemon: + # Daemonic processes cannot have children + if n_jobs != 1: + if inside_dask_worker(): + msg = ( + "Inside a Dask worker with daemon=True, " + "setting n_jobs=1.\nPossible work-arounds:\n" + "- dask.config.set(" + "{'distributed.worker.daemon': False})\n" + "- set the environment variable " + "DASK_DISTRIBUTED__WORKER__DAEMON=False\n" + "before creating your Dask cluster." + ) + else: + msg = ( + "Loky-backed parallel loops cannot be called in a" + " multiprocessing, setting n_jobs=1" + ) + warnings.warn(msg, stacklevel=3) + + return 1 + elif not (self.in_main_thread() or self.nesting_level == 0): + # Prevent posix fork inside in non-main posix threads + if n_jobs != 1: + warnings.warn( + "Loky-backed parallel loops cannot be nested below " + "threads, setting n_jobs=1", + stacklevel=3, + ) + return 1 + elif n_jobs < 0: + n_jobs = max(cpu_count() + 1 + n_jobs, 1) + return n_jobs + + def submit(self, func, callback=None): + """Schedule a func to be run""" + future = self._workers.submit(func) + if callback is not None: + future.add_done_callback(callback) + return future + + def retrieve_result_callback(self, future): + """Retrieve the result, here out is the future given by submit""" + try: + return future.result() + except ShutdownExecutorError: + raise RuntimeError( + "The executor underlying Parallel has been shutdown. " + "This is likely due to the garbage collection of a previous " + "generator from a call to Parallel with return_as='generator'." + " Make sure the generator is not garbage collected when " + "submitting a new job or that it is first properly exhausted." + ) + + def terminate(self): + if self._workers is not None: + # Don't terminate the workers as we want to reuse them in later + # calls, but cleanup the temporary resources that the Parallel call + # created. This 'hack' requires a private, low-level operation. + self._workers._temp_folder_manager._clean_temporary_resources( + context_id=self.parallel._id, force=False + ) + self._workers = None + + self.reset_batch_stats() + + def abort_everything(self, ensure_ready=True): + """Shutdown the workers and restart a new one with the same parameters""" + self._workers.terminate(kill_workers=True) + self._workers = None + + if ensure_ready: + self.configure(n_jobs=self.parallel.n_jobs, parallel=self.parallel) + + +class FallbackToBackend(Exception): + """Raised when configuration should fallback to another backend""" + + def __init__(self, backend): + self.backend = backend + + +def inside_dask_worker(): + """Check whether the current function is executed inside a Dask worker.""" + # This function can not be in joblib._dask because there would be a + # circular import: + # _dask imports _parallel_backend that imports _dask ... + try: + from distributed import get_worker + except ImportError: + return False + + try: + get_worker() + return True + except ValueError: + return False diff --git a/.venv/lib/python3.12/site-packages/joblib/_store_backends.py b/.venv/lib/python3.12/site-packages/joblib/_store_backends.py new file mode 100644 index 0000000000000000000000000000000000000000..3f1a2db87eefe9b2a6c1adf3408fb09e8f0aa213 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib/_store_backends.py @@ -0,0 +1,495 @@ +"""Storage providers backends for Memory caching.""" + +import collections +import datetime +import json +import operator +import os +import os.path +import re +import shutil +import threading +import time +import uuid +import warnings +from abc import ABCMeta, abstractmethod +from pickle import PicklingError + +from . import numpy_pickle +from .backports import concurrency_safe_rename +from .disk import memstr_to_bytes, mkdirp, rm_subdirs +from .logger import format_time + +CacheItemInfo = collections.namedtuple("CacheItemInfo", "path size last_access") + + +class CacheWarning(Warning): + """Warning to capture dump failures except for PicklingError.""" + + pass + + +def concurrency_safe_write(object_to_write, filename, write_func): + """Writes an object into a unique file in a concurrency-safe way.""" + # Temporary name is composed of UUID, process_id and thread_id to avoid + # collisions due to concurrent write. + # UUID is unique across nodes and time and help avoid collisions, even if + # the cache folder is shared by several Python processes with the same pid and + # thread id on different nodes of a cluster for instance. + thread_id = id(threading.current_thread()) + temporary_filename = f"{filename}.{uuid.uuid4().hex}-{os.getpid()}-{thread_id}" + + write_func(object_to_write, temporary_filename) + + return temporary_filename + + +class StoreBackendBase(metaclass=ABCMeta): + """Helper Abstract Base Class which defines all methods that + a StorageBackend must implement.""" + + location = None + + @abstractmethod + def _open_item(self, f, mode): + """Opens an item on the store and return a file-like object. + + This method is private and only used by the StoreBackendMixin object. + + Parameters + ---------- + f: a file-like object + The file-like object where an item is stored and retrieved + mode: string, optional + the mode in which the file-like object is opened allowed valued are + 'rb', 'wb' + + Returns + ------- + a file-like object + """ + + @abstractmethod + def _item_exists(self, location): + """Checks if an item location exists in the store. + + This method is private and only used by the StoreBackendMixin object. + + Parameters + ---------- + location: string + The location of an item. On a filesystem, this corresponds to the + absolute path, including the filename, of a file. + + Returns + ------- + True if the item exists, False otherwise + """ + + @abstractmethod + def _move_item(self, src, dst): + """Moves an item from src to dst in the store. + + This method is private and only used by the StoreBackendMixin object. + + Parameters + ---------- + src: string + The source location of an item + dst: string + The destination location of an item + """ + + @abstractmethod + def create_location(self, location): + """Creates a location on the store. + + Parameters + ---------- + location: string + The location in the store. On a filesystem, this corresponds to a + directory. + """ + + @abstractmethod + def clear_location(self, location): + """Clears a location on the store. + + Parameters + ---------- + location: string + The location in the store. On a filesystem, this corresponds to a + directory or a filename absolute path + """ + + @abstractmethod + def get_items(self): + """Returns the whole list of items available in the store. + + Returns + ------- + The list of items identified by their ids (e.g filename in a + filesystem). + """ + + @abstractmethod + def configure(self, location, verbose=0, backend_options=dict()): + """Configures the store. + + Parameters + ---------- + location: string + The base location used by the store. On a filesystem, this + corresponds to a directory. + verbose: int + The level of verbosity of the store + backend_options: dict + Contains a dictionary of named parameters used to configure the + store backend. + """ + + +class StoreBackendMixin(object): + """Class providing all logic for managing the store in a generic way. + + The StoreBackend subclass has to implement 3 methods: create_location, + clear_location and configure. The StoreBackend also has to provide + a private _open_item, _item_exists and _move_item methods. The _open_item + method has to have the same signature as the builtin open and return a + file-like object. + """ + + def load_item(self, call_id, verbose=1, timestamp=None, metadata=None): + """Load an item from the store given its id as a list of str.""" + full_path = os.path.join(self.location, *call_id) + + if verbose > 1: + ts_string = ( + "{: <16}".format(format_time(time.time() - timestamp)) + if timestamp is not None + else "" + ) + signature = os.path.basename(call_id[0]) + if metadata is not None and "input_args" in metadata: + kwargs = ", ".join( + "{}={}".format(*item) for item in metadata["input_args"].items() + ) + signature += "({})".format(kwargs) + msg = "[Memory]{}: Loading {}".format(ts_string, signature) + if verbose < 10: + print("{0}...".format(msg)) + else: + print("{0} from {1}".format(msg, full_path)) + + mmap_mode = None if not hasattr(self, "mmap_mode") else self.mmap_mode + + filename = os.path.join(full_path, "output.pkl") + if not self._item_exists(filename): + raise KeyError( + "Non-existing item (may have been " + "cleared).\nFile %s does not exist" % filename + ) + + # file-like object cannot be used when mmap_mode is set + if mmap_mode is None: + with self._open_item(filename, "rb") as f: + item = numpy_pickle.load(f) + else: + item = numpy_pickle.load(filename, mmap_mode=mmap_mode) + return item + + def dump_item(self, call_id, item, verbose=1): + """Dump an item in the store at the id given as a list of str.""" + try: + item_path = os.path.join(self.location, *call_id) + if not self._item_exists(item_path): + self.create_location(item_path) + filename = os.path.join(item_path, "output.pkl") + if verbose > 10: + print("Persisting in %s" % item_path) + + def write_func(to_write, dest_filename): + with self._open_item(dest_filename, "wb") as f: + try: + numpy_pickle.dump(to_write, f, compress=self.compress) + except PicklingError as e: + # TODO(1.5) turn into error + warnings.warn( + "Unable to cache to disk: failed to pickle " + "output. In version 1.5 this will raise an " + f"exception. Exception: {e}.", + FutureWarning, + ) + + self._concurrency_safe_write(item, filename, write_func) + except Exception as e: # noqa: E722 + warnings.warn( + "Unable to cache to disk. Possibly a race condition in the " + f"creation of the directory. Exception: {e}.", + CacheWarning, + ) + + def clear_item(self, call_id): + """Clear the item at the id, given as a list of str.""" + item_path = os.path.join(self.location, *call_id) + if self._item_exists(item_path): + self.clear_location(item_path) + + def contains_item(self, call_id): + """Check if there is an item at the id, given as a list of str.""" + item_path = os.path.join(self.location, *call_id) + filename = os.path.join(item_path, "output.pkl") + + return self._item_exists(filename) + + def get_item_info(self, call_id): + """Return information about item.""" + return {"location": os.path.join(self.location, *call_id)} + + def get_metadata(self, call_id): + """Return actual metadata of an item.""" + try: + item_path = os.path.join(self.location, *call_id) + filename = os.path.join(item_path, "metadata.json") + with self._open_item(filename, "rb") as f: + return json.loads(f.read().decode("utf-8")) + except: # noqa: E722 + return {} + + def store_metadata(self, call_id, metadata): + """Store metadata of a computation.""" + try: + item_path = os.path.join(self.location, *call_id) + self.create_location(item_path) + filename = os.path.join(item_path, "metadata.json") + + def write_func(to_write, dest_filename): + with self._open_item(dest_filename, "wb") as f: + f.write(json.dumps(to_write).encode("utf-8")) + + self._concurrency_safe_write(metadata, filename, write_func) + except: # noqa: E722 + pass + + def contains_path(self, call_id): + """Check cached function is available in store.""" + func_path = os.path.join(self.location, *call_id) + return self.object_exists(func_path) + + def clear_path(self, call_id): + """Clear all items with a common path in the store.""" + func_path = os.path.join(self.location, *call_id) + if self._item_exists(func_path): + self.clear_location(func_path) + + def store_cached_func_code(self, call_id, func_code=None): + """Store the code of the cached function.""" + func_path = os.path.join(self.location, *call_id) + if not self._item_exists(func_path): + self.create_location(func_path) + + if func_code is not None: + filename = os.path.join(func_path, "func_code.py") + with self._open_item(filename, "wb") as f: + f.write(func_code.encode("utf-8")) + + def get_cached_func_code(self, call_id): + """Store the code of the cached function.""" + filename = os.path.join(self.location, *call_id, "func_code.py") + try: + with self._open_item(filename, "rb") as f: + return f.read().decode("utf-8") + except: # noqa: E722 + raise + + def get_cached_func_info(self, call_id): + """Return information related to the cached function if it exists.""" + return {"location": os.path.join(self.location, *call_id)} + + def clear(self): + """Clear the whole store content.""" + self.clear_location(self.location) + + def enforce_store_limits(self, bytes_limit, items_limit=None, age_limit=None): + """ + Remove the store's oldest files to enforce item, byte, and age limits. + """ + items_to_delete = self._get_items_to_delete(bytes_limit, items_limit, age_limit) + + for item in items_to_delete: + if self.verbose > 10: + print("Deleting item {0}".format(item)) + try: + self.clear_location(item.path) + except OSError: + # Even with ignore_errors=True shutil.rmtree can raise OSError + # with: + # [Errno 116] Stale file handle if another process has deleted + # the folder already. + pass + + def _get_items_to_delete(self, bytes_limit, items_limit=None, age_limit=None): + """ + Get items to delete to keep the store under size, file, & age limits. + """ + if isinstance(bytes_limit, str): + bytes_limit = memstr_to_bytes(bytes_limit) + + items = self.get_items() + if not items: + return [] + + size = sum(item.size for item in items) + + if bytes_limit is not None: + to_delete_size = size - bytes_limit + else: + to_delete_size = 0 + + if items_limit is not None: + to_delete_items = len(items) - items_limit + else: + to_delete_items = 0 + + if age_limit is not None: + older_item = min(item.last_access for item in items) + if age_limit.total_seconds() < 0: + raise ValueError("age_limit has to be a positive timedelta") + deadline = datetime.datetime.now() - age_limit + else: + deadline = None + + if ( + to_delete_size <= 0 + and to_delete_items <= 0 + and (deadline is None or older_item > deadline) + ): + return [] + + # We want to delete first the cache items that were accessed a + # long time ago + items.sort(key=operator.attrgetter("last_access")) + + items_to_delete = [] + size_so_far = 0 + items_so_far = 0 + + for item in items: + if ( + (size_so_far >= to_delete_size) + and items_so_far >= to_delete_items + and (deadline is None or deadline < item.last_access) + ): + break + + items_to_delete.append(item) + size_so_far += item.size + items_so_far += 1 + + return items_to_delete + + def _concurrency_safe_write(self, to_write, filename, write_func): + """Writes an object into a file in a concurrency-safe way.""" + temporary_filename = concurrency_safe_write(to_write, filename, write_func) + self._move_item(temporary_filename, filename) + + def __repr__(self): + """Printable representation of the store location.""" + return '{class_name}(location="{location}")'.format( + class_name=self.__class__.__name__, location=self.location + ) + + +class FileSystemStoreBackend(StoreBackendBase, StoreBackendMixin): + """A StoreBackend used with local or network file systems.""" + + _open_item = staticmethod(open) + _item_exists = staticmethod(os.path.exists) + _move_item = staticmethod(concurrency_safe_rename) + + def clear_location(self, location): + """Delete location on store.""" + if location == self.location: + rm_subdirs(location) + else: + shutil.rmtree(location, ignore_errors=True) + + def create_location(self, location): + """Create object location on store""" + mkdirp(location) + + def get_items(self): + """Returns the whole list of items available in the store.""" + items = [] + + for dirpath, _, filenames in os.walk(self.location): + is_cache_hash_dir = re.match("[a-f0-9]{32}", os.path.basename(dirpath)) + + if is_cache_hash_dir: + output_filename = os.path.join(dirpath, "output.pkl") + try: + last_access = os.path.getatime(output_filename) + except OSError: + try: + last_access = os.path.getatime(dirpath) + except OSError: + # The directory has already been deleted + continue + + last_access = datetime.datetime.fromtimestamp(last_access) + try: + full_filenames = [os.path.join(dirpath, fn) for fn in filenames] + dirsize = sum(os.path.getsize(fn) for fn in full_filenames) + except OSError: + # Either output_filename or one of the files in + # dirpath does not exist any more. We assume this + # directory is being cleaned by another process already + continue + + items.append(CacheItemInfo(dirpath, dirsize, last_access)) + + return items + + def configure(self, location, verbose=1, backend_options=None): + """Configure the store backend. + + For this backend, valid store options are 'compress' and 'mmap_mode' + """ + if backend_options is None: + backend_options = {} + + # setup location directory + self.location = location + if not os.path.exists(self.location): + mkdirp(self.location) + + # Automatically add `.gitignore` file to the cache folder. + # XXX: the condition is necessary because in `Memory.__init__`, the user + # passed `location` param is modified to be either `{location}` or + # `{location}/joblib` depending on input type (`pathlib.Path` vs `str`). + # The proper resolution of this inconsistency is tracked in: + # https://github.com/joblib/joblib/issues/1684 + cache_directory = ( + os.path.dirname(location) + if os.path.dirname(location) and os.path.basename(location) == "joblib" + else location + ) + with open(os.path.join(cache_directory, ".gitignore"), "w") as file: + file.write("# Created by joblib automatically.\n") + file.write("*\n") + + # item can be stored compressed for faster I/O + self.compress = backend_options.get("compress", False) + + # FileSystemStoreBackend can be used with mmap_mode options under + # certain conditions. + mmap_mode = backend_options.get("mmap_mode") + if self.compress and mmap_mode is not None: + warnings.warn( + "Compressed items cannot be memmapped in a " + "filesystem store. Option will be ignored.", + stacklevel=2, + ) + + self.mmap_mode = mmap_mode + self.verbose = verbose diff --git a/.venv/lib/python3.12/site-packages/joblib/_utils.py b/.venv/lib/python3.12/site-packages/joblib/_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..1071c9f83800fbf03b664cd7f40fae2ba3f357ef --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib/_utils.py @@ -0,0 +1,83 @@ +# Adapted from https://stackoverflow.com/a/9558001/2536294 + +import ast +import operator as op +from dataclasses import dataclass + +from ._multiprocessing_helpers import mp + +if mp is not None: + from .externals.loky.process_executor import _ExceptionWithTraceback + + +# supported operators +operators = { + ast.Add: op.add, + ast.Sub: op.sub, + ast.Mult: op.mul, + ast.Div: op.truediv, + ast.FloorDiv: op.floordiv, + ast.Mod: op.mod, + ast.Pow: op.pow, + ast.USub: op.neg, +} + + +def eval_expr(expr): + """ + >>> eval_expr('2*6') + 12 + >>> eval_expr('2**6') + 64 + >>> eval_expr('1 + 2*3**(4) / (6 + -7)') + -161.0 + """ + try: + return eval_(ast.parse(expr, mode="eval").body) + except (TypeError, SyntaxError, KeyError) as e: + raise ValueError( + f"{expr!r} is not a valid or supported arithmetic expression." + ) from e + + +def eval_(node): + if isinstance(node, ast.Constant): # + return node.value + elif isinstance(node, ast.BinOp): # + return operators[type(node.op)](eval_(node.left), eval_(node.right)) + elif isinstance(node, ast.UnaryOp): # e.g., -1 + return operators[type(node.op)](eval_(node.operand)) + else: + raise TypeError(node) + + +@dataclass(frozen=True) +class _Sentinel: + """A sentinel to mark a parameter as not explicitly set""" + + default_value: object + + def __repr__(self): + return f"default({self.default_value!r})" + + +class _TracebackCapturingWrapper: + """Protect function call and return error with traceback.""" + + def __init__(self, func): + self.func = func + + def __call__(self, **kwargs): + try: + return self.func(**kwargs) + except BaseException as e: + return _ExceptionWithTraceback(e) + + +def _retrieve_traceback_capturing_wrapped_call(out): + if isinstance(out, _ExceptionWithTraceback): + rebuild, args = out.__reduce__() + out = rebuild(*args) + if isinstance(out, BaseException): + raise out + return out diff --git a/.venv/lib/python3.12/site-packages/joblib/backports.py b/.venv/lib/python3.12/site-packages/joblib/backports.py new file mode 100644 index 0000000000000000000000000000000000000000..495e2acb8b38e6676dd4cb0a219d9f3bb7c4bff5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib/backports.py @@ -0,0 +1,195 @@ +""" +Backports of fixes for joblib dependencies +""" + +import os +import re +import time +from multiprocessing import util +from os.path import basename + + +class Version: + """Backport from deprecated distutils + + We maintain this backport to avoid introducing a new dependency on + `packaging`. + + We might rexplore this choice in the future if all major Python projects + introduce a dependency on packaging anyway. + """ + + def __init__(self, vstring=None): + if vstring: + self.parse(vstring) + + def __repr__(self): + return "%s ('%s')" % (self.__class__.__name__, str(self)) + + def __eq__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c == 0 + + def __lt__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c < 0 + + def __le__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c <= 0 + + def __gt__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c > 0 + + def __ge__(self, other): + c = self._cmp(other) + if c is NotImplemented: + return c + return c >= 0 + + +class LooseVersion(Version): + """Backport from deprecated distutils + + We maintain this backport to avoid introducing a new dependency on + `packaging`. + + We might rexplore this choice in the future if all major Python projects + introduce a dependency on packaging anyway. + """ + + component_re = re.compile(r"(\d+ | [a-z]+ | \.)", re.VERBOSE) + + def __init__(self, vstring=None): + if vstring: + self.parse(vstring) + + def parse(self, vstring): + # I've given up on thinking I can reconstruct the version string + # from the parsed tuple -- so I just store the string here for + # use by __str__ + self.vstring = vstring + components = [x for x in self.component_re.split(vstring) if x and x != "."] + for i, obj in enumerate(components): + try: + components[i] = int(obj) + except ValueError: + pass + + self.version = components + + def __str__(self): + return self.vstring + + def __repr__(self): + return "LooseVersion ('%s')" % str(self) + + def _cmp(self, other): + if isinstance(other, str): + other = LooseVersion(other) + elif not isinstance(other, LooseVersion): + return NotImplemented + + if self.version == other.version: + return 0 + if self.version < other.version: + return -1 + if self.version > other.version: + return 1 + + +try: + import numpy as np + + def make_memmap( + filename, + dtype="uint8", + mode="r+", + offset=0, + shape=None, + order="C", + unlink_on_gc_collect=False, + ): + """Custom memmap constructor compatible with numpy.memmap. + + This function: + - is a backport the numpy memmap offset fix (See + https://github.com/numpy/numpy/pull/8443 for more details. + The numpy fix is available starting numpy 1.13) + - adds ``unlink_on_gc_collect``, which specifies explicitly whether + the process re-constructing the memmap owns a reference to the + underlying file. If set to True, it adds a finalizer to the + newly-created memmap that sends a maybe_unlink request for the + memmaped file to resource_tracker. + """ + util.debug( + "[MEMMAP READ] creating a memmap (shape {}, filename {}, pid {})".format( + shape, basename(filename), os.getpid() + ) + ) + + mm = np.memmap( + filename, dtype=dtype, mode=mode, offset=offset, shape=shape, order=order + ) + if LooseVersion(np.__version__) < "1.13": + mm.offset = offset + if unlink_on_gc_collect: + from ._memmapping_reducer import add_maybe_unlink_finalizer + + add_maybe_unlink_finalizer(mm) + return mm +except ImportError: + + def make_memmap( + filename, + dtype="uint8", + mode="r+", + offset=0, + shape=None, + order="C", + unlink_on_gc_collect=False, + ): + raise NotImplementedError( + "'joblib.backports.make_memmap' should not be used " + "if numpy is not installed." + ) + + +if os.name == "nt": + # https://github.com/joblib/joblib/issues/540 + access_denied_errors = (5, 13) + from os import replace + + def concurrency_safe_rename(src, dst): + """Renames ``src`` into ``dst`` overwriting ``dst`` if it exists. + + On Windows os.replace can yield permission errors if executed by two + different processes. + """ + max_sleep_time = 1 + total_sleep_time = 0 + sleep_time = 0.001 + while total_sleep_time < max_sleep_time: + try: + replace(src, dst) + break + except Exception as exc: + if getattr(exc, "winerror", None) in access_denied_errors: + time.sleep(sleep_time) + total_sleep_time += sleep_time + sleep_time *= 2 + else: + raise + else: + raise +else: + from os import replace as concurrency_safe_rename # noqa diff --git a/.venv/lib/python3.12/site-packages/joblib/compressor.py b/.venv/lib/python3.12/site-packages/joblib/compressor.py new file mode 100644 index 0000000000000000000000000000000000000000..55bc86c4855574e9b0eec8fad29a2cdde614fbdd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib/compressor.py @@ -0,0 +1,572 @@ +"""Classes and functions for managing compressors.""" + +import io +import zlib + +from joblib.backports import LooseVersion + +try: + from threading import RLock +except ImportError: + from dummy_threading import RLock + +try: + import bz2 +except ImportError: + bz2 = None + +try: + import lz4 + from lz4.frame import LZ4FrameFile +except ImportError: + lz4 = None + +try: + import lzma +except ImportError: + lzma = None + + +LZ4_NOT_INSTALLED_ERROR = ( + "LZ4 is not installed. Install it with pip: https://python-lz4.readthedocs.io/" +) + +# Registered compressors +_COMPRESSORS = {} + +# Magic numbers of supported compression file formats. +_ZFILE_PREFIX = b"ZF" # used with pickle files created before 0.9.3. +_ZLIB_PREFIX = b"\x78" +_GZIP_PREFIX = b"\x1f\x8b" +_BZ2_PREFIX = b"BZ" +_XZ_PREFIX = b"\xfd\x37\x7a\x58\x5a" +_LZMA_PREFIX = b"\x5d\x00" +_LZ4_PREFIX = b"\x04\x22\x4d\x18" + + +def register_compressor(compressor_name, compressor, force=False): + """Register a new compressor. + + Parameters + ---------- + compressor_name: str. + The name of the compressor. + compressor: CompressorWrapper + An instance of a 'CompressorWrapper'. + """ + global _COMPRESSORS + if not isinstance(compressor_name, str): + raise ValueError( + "Compressor name should be a string, '{}' given.".format(compressor_name) + ) + + if not isinstance(compressor, CompressorWrapper): + raise ValueError( + "Compressor should implement the CompressorWrapper " + "interface, '{}' given.".format(compressor) + ) + + if compressor.fileobj_factory is not None and ( + not hasattr(compressor.fileobj_factory, "read") + or not hasattr(compressor.fileobj_factory, "write") + or not hasattr(compressor.fileobj_factory, "seek") + or not hasattr(compressor.fileobj_factory, "tell") + ): + raise ValueError( + "Compressor 'fileobj_factory' attribute should " + "implement the file object interface, '{}' given.".format( + compressor.fileobj_factory + ) + ) + + if compressor_name in _COMPRESSORS and not force: + raise ValueError("Compressor '{}' already registered.".format(compressor_name)) + + _COMPRESSORS[compressor_name] = compressor + + +class CompressorWrapper: + """A wrapper around a compressor file object. + + Attributes + ---------- + obj: a file-like object + The object must implement the buffer interface and will be used + internally to compress/decompress the data. + prefix: bytestring + A bytestring corresponding to the magic number that identifies the + file format associated to the compressor. + extension: str + The file extension used to automatically select this compressor during + a dump to a file. + """ + + def __init__(self, obj, prefix=b"", extension=""): + self.fileobj_factory = obj + self.prefix = prefix + self.extension = extension + + def compressor_file(self, fileobj, compresslevel=None): + """Returns an instance of a compressor file object.""" + if compresslevel is None: + return self.fileobj_factory(fileobj, "wb") + else: + return self.fileobj_factory(fileobj, "wb", compresslevel=compresslevel) + + def decompressor_file(self, fileobj): + """Returns an instance of a decompressor file object.""" + return self.fileobj_factory(fileobj, "rb") + + +class BZ2CompressorWrapper(CompressorWrapper): + prefix = _BZ2_PREFIX + extension = ".bz2" + + def __init__(self): + if bz2 is not None: + self.fileobj_factory = bz2.BZ2File + else: + self.fileobj_factory = None + + def _check_versions(self): + if bz2 is None: + raise ValueError( + "bz2 module is not compiled on your python standard library." + ) + + def compressor_file(self, fileobj, compresslevel=None): + """Returns an instance of a compressor file object.""" + self._check_versions() + if compresslevel is None: + return self.fileobj_factory(fileobj, "wb") + else: + return self.fileobj_factory(fileobj, "wb", compresslevel=compresslevel) + + def decompressor_file(self, fileobj): + """Returns an instance of a decompressor file object.""" + self._check_versions() + fileobj = self.fileobj_factory(fileobj, "rb") + return fileobj + + +class LZMACompressorWrapper(CompressorWrapper): + prefix = _LZMA_PREFIX + extension = ".lzma" + _lzma_format_name = "FORMAT_ALONE" + + def __init__(self): + if lzma is not None: + self.fileobj_factory = lzma.LZMAFile + self._lzma_format = getattr(lzma, self._lzma_format_name) + else: + self.fileobj_factory = None + + def _check_versions(self): + if lzma is None: + raise ValueError( + "lzma module is not compiled on your python standard library." + ) + + def compressor_file(self, fileobj, compresslevel=None): + """Returns an instance of a compressor file object.""" + if compresslevel is None: + return self.fileobj_factory(fileobj, "wb", format=self._lzma_format) + else: + return self.fileobj_factory( + fileobj, "wb", format=self._lzma_format, preset=compresslevel + ) + + def decompressor_file(self, fileobj): + """Returns an instance of a decompressor file object.""" + return lzma.LZMAFile(fileobj, "rb") + + +class XZCompressorWrapper(LZMACompressorWrapper): + prefix = _XZ_PREFIX + extension = ".xz" + _lzma_format_name = "FORMAT_XZ" + + +class LZ4CompressorWrapper(CompressorWrapper): + prefix = _LZ4_PREFIX + extension = ".lz4" + + def __init__(self): + if lz4 is not None: + self.fileobj_factory = LZ4FrameFile + else: + self.fileobj_factory = None + + def _check_versions(self): + if lz4 is None: + raise ValueError(LZ4_NOT_INSTALLED_ERROR) + lz4_version = lz4.__version__ + if lz4_version.startswith("v"): + lz4_version = lz4_version[1:] + if LooseVersion(lz4_version) < LooseVersion("0.19"): + raise ValueError(LZ4_NOT_INSTALLED_ERROR) + + def compressor_file(self, fileobj, compresslevel=None): + """Returns an instance of a compressor file object.""" + self._check_versions() + if compresslevel is None: + return self.fileobj_factory(fileobj, "wb") + else: + return self.fileobj_factory(fileobj, "wb", compression_level=compresslevel) + + def decompressor_file(self, fileobj): + """Returns an instance of a decompressor file object.""" + self._check_versions() + return self.fileobj_factory(fileobj, "rb") + + +############################################################################### +# base file compression/decompression object definition +_MODE_CLOSED = 0 +_MODE_READ = 1 +_MODE_READ_EOF = 2 +_MODE_WRITE = 3 +_BUFFER_SIZE = 8192 + + +class BinaryZlibFile(io.BufferedIOBase): + """A file object providing transparent zlib (de)compression. + + TODO python2_drop: is it still needed since we dropped Python 2 support A + BinaryZlibFile can act as a wrapper for an existing file object, or refer + directly to a named file on disk. + + Note that BinaryZlibFile provides only a *binary* file interface: data read + is returned as bytes, and data to be written should be given as bytes. + + This object is an adaptation of the BZ2File object and is compatible with + versions of python >= 2.7. + + If filename is a str or bytes object, it gives the name + of the file to be opened. Otherwise, it should be a file object, + which will be used to read or write the compressed data. + + mode can be 'rb' for reading (default) or 'wb' for (over)writing + + If mode is 'wb', compresslevel can be a number between 1 + and 9 specifying the level of compression: 1 produces the least + compression, and 9 produces the most compression. 3 is the default. + """ + + wbits = zlib.MAX_WBITS + + def __init__(self, filename, mode="rb", compresslevel=3): + # This lock must be recursive, so that BufferedIOBase's + # readline(), readlines() and writelines() don't deadlock. + self._lock = RLock() + self._fp = None + self._closefp = False + self._mode = _MODE_CLOSED + self._pos = 0 + self._size = -1 + self.compresslevel = compresslevel + + if not isinstance(compresslevel, int) or not (1 <= compresslevel <= 9): + raise ValueError( + "'compresslevel' must be an integer " + "between 1 and 9. You provided 'compresslevel={}'".format(compresslevel) + ) + + if mode == "rb": + self._mode = _MODE_READ + self._decompressor = zlib.decompressobj(self.wbits) + self._buffer = b"" + self._buffer_offset = 0 + elif mode == "wb": + self._mode = _MODE_WRITE + self._compressor = zlib.compressobj( + self.compresslevel, zlib.DEFLATED, self.wbits, zlib.DEF_MEM_LEVEL, 0 + ) + else: + raise ValueError("Invalid mode: %r" % (mode,)) + + if isinstance(filename, str): + self._fp = io.open(filename, mode) + self._closefp = True + elif hasattr(filename, "read") or hasattr(filename, "write"): + self._fp = filename + else: + raise TypeError("filename must be a str or bytes object, or a file") + + def close(self): + """Flush and close the file. + + May be called more than once without error. Once the file is + closed, any other operation on it will raise a ValueError. + """ + with self._lock: + if self._mode == _MODE_CLOSED: + return + try: + if self._mode in (_MODE_READ, _MODE_READ_EOF): + self._decompressor = None + elif self._mode == _MODE_WRITE: + self._fp.write(self._compressor.flush()) + self._compressor = None + finally: + try: + if self._closefp: + self._fp.close() + finally: + self._fp = None + self._closefp = False + self._mode = _MODE_CLOSED + self._buffer = b"" + self._buffer_offset = 0 + + @property + def closed(self): + """True if this file is closed.""" + return self._mode == _MODE_CLOSED + + def fileno(self): + """Return the file descriptor for the underlying file.""" + self._check_not_closed() + return self._fp.fileno() + + def seekable(self): + """Return whether the file supports seeking.""" + return self.readable() and self._fp.seekable() + + def readable(self): + """Return whether the file was opened for reading.""" + self._check_not_closed() + return self._mode in (_MODE_READ, _MODE_READ_EOF) + + def writable(self): + """Return whether the file was opened for writing.""" + self._check_not_closed() + return self._mode == _MODE_WRITE + + # Mode-checking helper functions. + + def _check_not_closed(self): + if self.closed: + fname = getattr(self._fp, "name", None) + msg = "I/O operation on closed file" + if fname is not None: + msg += " {}".format(fname) + msg += "." + raise ValueError(msg) + + def _check_can_read(self): + if self._mode not in (_MODE_READ, _MODE_READ_EOF): + self._check_not_closed() + raise io.UnsupportedOperation("File not open for reading") + + def _check_can_write(self): + if self._mode != _MODE_WRITE: + self._check_not_closed() + raise io.UnsupportedOperation("File not open for writing") + + def _check_can_seek(self): + if self._mode not in (_MODE_READ, _MODE_READ_EOF): + self._check_not_closed() + raise io.UnsupportedOperation( + "Seeking is only supported on files open for reading" + ) + if not self._fp.seekable(): + raise io.UnsupportedOperation( + "The underlying file object does not support seeking" + ) + + # Fill the readahead buffer if it is empty. Returns False on EOF. + def _fill_buffer(self): + if self._mode == _MODE_READ_EOF: + return False + # Depending on the input data, our call to the decompressor may not + # return any data. In this case, try again after reading another block. + while self._buffer_offset == len(self._buffer): + try: + rawblock = self._decompressor.unused_data or self._fp.read(_BUFFER_SIZE) + if not rawblock: + raise EOFError + except EOFError: + # End-of-stream marker and end of file. We're good. + self._mode = _MODE_READ_EOF + self._size = self._pos + return False + else: + self._buffer = self._decompressor.decompress(rawblock) + self._buffer_offset = 0 + return True + + # Read data until EOF. + # If return_data is false, consume the data without returning it. + def _read_all(self, return_data=True): + # The loop assumes that _buffer_offset is 0. Ensure that this is true. + self._buffer = self._buffer[self._buffer_offset :] + self._buffer_offset = 0 + + blocks = [] + while self._fill_buffer(): + if return_data: + blocks.append(self._buffer) + self._pos += len(self._buffer) + self._buffer = b"" + if return_data: + return b"".join(blocks) + + # Read a block of up to n bytes. + # If return_data is false, consume the data without returning it. + def _read_block(self, n_bytes, return_data=True): + # If we have enough data buffered, return immediately. + end = self._buffer_offset + n_bytes + if end <= len(self._buffer): + data = self._buffer[self._buffer_offset : end] + self._buffer_offset = end + self._pos += len(data) + return data if return_data else None + + # The loop assumes that _buffer_offset is 0. Ensure that this is true. + self._buffer = self._buffer[self._buffer_offset :] + self._buffer_offset = 0 + + blocks = [] + while n_bytes > 0 and self._fill_buffer(): + if n_bytes < len(self._buffer): + data = self._buffer[:n_bytes] + self._buffer_offset = n_bytes + else: + data = self._buffer + self._buffer = b"" + if return_data: + blocks.append(data) + self._pos += len(data) + n_bytes -= len(data) + if return_data: + return b"".join(blocks) + + def read(self, size=-1): + """Read up to size uncompressed bytes from the file. + + If size is negative or omitted, read until EOF is reached. + Returns b'' if the file is already at EOF. + """ + with self._lock: + self._check_can_read() + if size == 0: + return b"" + elif size < 0: + return self._read_all() + else: + return self._read_block(size) + + def readinto(self, b): + """Read up to len(b) bytes into b. + + Returns the number of bytes read (0 for EOF). + """ + with self._lock: + return io.BufferedIOBase.readinto(self, b) + + def write(self, data): + """Write a byte string to the file. + + Returns the number of uncompressed bytes written, which is + always len(data). Note that due to buffering, the file on disk + may not reflect the data written until close() is called. + """ + with self._lock: + self._check_can_write() + # Convert data type if called by io.BufferedWriter. + if isinstance(data, memoryview): + data = data.tobytes() + + compressed = self._compressor.compress(data) + self._fp.write(compressed) + self._pos += len(data) + return len(data) + + # Rewind the file to the beginning of the data stream. + def _rewind(self): + self._fp.seek(0, 0) + self._mode = _MODE_READ + self._pos = 0 + self._decompressor = zlib.decompressobj(self.wbits) + self._buffer = b"" + self._buffer_offset = 0 + + def seek(self, offset, whence=0): + """Change the file position. + + The new position is specified by offset, relative to the + position indicated by whence. Values for whence are: + + 0: start of stream (default); offset must not be negative + 1: current stream position + 2: end of stream; offset must not be positive + + Returns the new file position. + + Note that seeking is emulated, so depending on the parameters, + this operation may be extremely slow. + """ + with self._lock: + self._check_can_seek() + + # Recalculate offset as an absolute file position. + if whence == 0: + pass + elif whence == 1: + offset = self._pos + offset + elif whence == 2: + # Seeking relative to EOF - we need to know the file's size. + if self._size < 0: + self._read_all(return_data=False) + offset = self._size + offset + else: + raise ValueError("Invalid value for whence: %s" % (whence,)) + + # Make it so that offset is the number of bytes to skip forward. + if offset < self._pos: + self._rewind() + else: + offset -= self._pos + + # Read and discard data until we reach the desired position. + self._read_block(offset, return_data=False) + + return self._pos + + def tell(self): + """Return the current file position.""" + with self._lock: + self._check_not_closed() + return self._pos + + +class ZlibCompressorWrapper(CompressorWrapper): + def __init__(self): + CompressorWrapper.__init__( + self, obj=BinaryZlibFile, prefix=_ZLIB_PREFIX, extension=".z" + ) + + +class BinaryGzipFile(BinaryZlibFile): + """A file object providing transparent gzip (de)compression. + + If filename is a str or bytes object, it gives the name + of the file to be opened. Otherwise, it should be a file object, + which will be used to read or write the compressed data. + + mode can be 'rb' for reading (default) or 'wb' for (over)writing + + If mode is 'wb', compresslevel can be a number between 1 + and 9 specifying the level of compression: 1 produces the least + compression, and 9 produces the most compression. 3 is the default. + """ + + wbits = 31 # zlib compressor/decompressor wbits value for gzip format. + + +class GzipCompressorWrapper(CompressorWrapper): + def __init__(self): + CompressorWrapper.__init__( + self, obj=BinaryGzipFile, prefix=_GZIP_PREFIX, extension=".gz" + ) diff --git a/.venv/lib/python3.12/site-packages/joblib/disk.py b/.venv/lib/python3.12/site-packages/joblib/disk.py new file mode 100644 index 0000000000000000000000000000000000000000..61222e2bb066b70ae92e621841c6f4f01309cca7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib/disk.py @@ -0,0 +1,131 @@ +""" +Disk management utilities. +""" + +# Authors: Gael Varoquaux +# Lars Buitinck +# Copyright (c) 2010 Gael Varoquaux +# License: BSD Style, 3 clauses. + +import errno +import os +import shutil +import sys +import time +from multiprocessing import util + +try: + WindowsError +except NameError: + WindowsError = OSError + + +def disk_used(path): + """Return the disk usage in a directory.""" + size = 0 + for file in os.listdir(path) + ["."]: + stat = os.stat(os.path.join(path, file)) + if hasattr(stat, "st_blocks"): + size += stat.st_blocks * 512 + else: + # on some platform st_blocks is not available (e.g., Windows) + # approximate by rounding to next multiple of 512 + size += (stat.st_size // 512 + 1) * 512 + # We need to convert to int to avoid having longs on some systems (we + # don't want longs to avoid problems we SQLite) + return int(size / 1024.0) + + +def memstr_to_bytes(text): + """Convert a memory text to its value in bytes.""" + kilo = 1024 + units = dict(K=kilo, M=kilo**2, G=kilo**3) + try: + size = int(units[text[-1]] * float(text[:-1])) + except (KeyError, ValueError) as e: + raise ValueError( + "Invalid literal for size give: %s (type %s) should be " + "alike '10G', '500M', '50K'." % (text, type(text)) + ) from e + return size + + +def mkdirp(d): + """Ensure directory d exists (like mkdir -p on Unix) + No guarantee that the directory is writable. + """ + try: + os.makedirs(d) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + +# if a rmtree operation fails in rm_subdirs, wait for this much time (in secs), +# then retry up to RM_SUBDIRS_N_RETRY times. If it still fails, raise the +# exception. this mechanism ensures that the sub-process gc have the time to +# collect and close the memmaps before we fail. +RM_SUBDIRS_RETRY_TIME = 0.1 +RM_SUBDIRS_N_RETRY = 10 + + +def rm_subdirs(path, onerror=None): + """Remove all subdirectories in this path. + + The directory indicated by `path` is left in place, and its subdirectories + are erased. + + If onerror is set, it is called to handle the error with arguments (func, + path, exc_info) where func is os.listdir, os.remove, or os.rmdir; + path is the argument to that function that caused it to fail; and + exc_info is a tuple returned by sys.exc_info(). If onerror is None, + an exception is raised. + """ + + # NOTE this code is adapted from the one in shutil.rmtree, and is + # just as fast + + names = [] + try: + names = os.listdir(path) + except os.error: + if onerror is not None: + onerror(os.listdir, path, sys.exc_info()) + else: + raise + + for name in names: + fullname = os.path.join(path, name) + delete_folder(fullname, onerror=onerror) + + +def delete_folder(folder_path, onerror=None, allow_non_empty=True): + """Utility function to cleanup a temporary folder if it still exists.""" + if os.path.isdir(folder_path): + if onerror is not None: + shutil.rmtree(folder_path, False, onerror) + else: + # allow the rmtree to fail once, wait and re-try. + # if the error is raised again, fail + err_count = 0 + while True: + files = os.listdir(folder_path) + try: + if len(files) == 0 or allow_non_empty: + shutil.rmtree(folder_path, ignore_errors=False, onerror=None) + util.debug("Successfully deleted {}".format(folder_path)) + break + else: + raise OSError( + "Expected empty folder {} but got {} files.".format( + folder_path, len(files) + ) + ) + except (OSError, WindowsError): + err_count += 1 + if err_count > RM_SUBDIRS_N_RETRY: + # the folder cannot be deleted right now. It maybe + # because some temporary files have not been deleted + # yet. + raise + time.sleep(RM_SUBDIRS_RETRY_TIME) diff --git a/.venv/lib/python3.12/site-packages/joblib/executor.py b/.venv/lib/python3.12/site-packages/joblib/executor.py new file mode 100644 index 0000000000000000000000000000000000000000..60aae8f7a8ed4d48116addfdeb9bd213152eed27 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib/executor.py @@ -0,0 +1,131 @@ +"""Utility function to construct a loky.ReusableExecutor with custom pickler. + +This module provides efficient ways of working with data stored in +shared memory with numpy.memmap arrays without inducing any memory +copy between the parent and child processes. +""" +# Author: Thomas Moreau +# Copyright: 2017, Thomas Moreau +# License: BSD 3 clause + +from ._memmapping_reducer import TemporaryResourcesManager, get_memmapping_reducers +from .externals.loky.reusable_executor import _ReusablePoolExecutor + +_executor_args = None + + +def get_memmapping_executor(n_jobs, **kwargs): + return MemmappingExecutor.get_memmapping_executor(n_jobs, **kwargs) + + +class MemmappingExecutor(_ReusablePoolExecutor): + @classmethod + def get_memmapping_executor( + cls, + n_jobs, + timeout=300, + initializer=None, + initargs=(), + env=None, + temp_folder=None, + context_id=None, + **backend_args, + ): + """Factory for ReusableExecutor with automatic memmapping for large + numpy arrays. + """ + global _executor_args + # Check if we can reuse the executor here instead of deferring the test + # to loky as the reducers are objects that changes at each call. + executor_args = backend_args.copy() + executor_args.update(env if env else {}) + executor_args.update( + dict(timeout=timeout, initializer=initializer, initargs=initargs) + ) + reuse = _executor_args is None or _executor_args == executor_args + _executor_args = executor_args + + manager = TemporaryResourcesManager(temp_folder) + + # reducers access the temporary folder in which to store temporary + # pickles through a call to manager.resolve_temp_folder_name. resolving + # the folder name dynamically is useful to use different folders across + # calls of a same reusable executor + job_reducers, result_reducers = get_memmapping_reducers( + unlink_on_gc_collect=True, + temp_folder_resolver=manager.resolve_temp_folder_name, + **backend_args, + ) + _executor, executor_is_reused = super().get_reusable_executor( + n_jobs, + job_reducers=job_reducers, + result_reducers=result_reducers, + reuse=reuse, + timeout=timeout, + initializer=initializer, + initargs=initargs, + env=env, + ) + + if not executor_is_reused: + # Only set a _temp_folder_manager for new executors. Reused + # executors already have a _temporary_folder_manager that must not + # be re-assigned like that because it is referenced in various + # places in the reducing machinery of the executor. + _executor._temp_folder_manager = manager + + if context_id is not None: + # Only register the specified context once we know which manager + # the current executor is using, in order to not register an atexit + # finalizer twice for the same folder. + _executor._temp_folder_manager.register_new_context(context_id) + + return _executor + + def terminate(self, kill_workers=False): + self.shutdown(kill_workers=kill_workers) + + # When workers are killed in a brutal manner, they cannot execute the + # finalizer of their shared memmaps. The refcount of those memmaps may + # be off by an unknown number, so instead of decref'ing them, we force + # delete the whole temporary folder, and unregister them. There is no + # risk of PermissionError at folder deletion because at this + # point, all child processes are dead, so all references to temporary + # memmaps are closed. Otherwise, just try to delete as much as possible + # with allow_non_empty=True but if we can't, it will be clean up later + # on by the resource_tracker. + with self._submit_resize_lock: + self._temp_folder_manager._clean_temporary_resources( + force=kill_workers, allow_non_empty=True + ) + + @property + def _temp_folder(self): + # Legacy property in tests. could be removed if we refactored the + # memmapping tests. SHOULD ONLY BE USED IN TESTS! + # We cache this property because it is called late in the tests - at + # this point, all context have been unregistered, and + # resolve_temp_folder_name raises an error. + if getattr(self, "_cached_temp_folder", None) is not None: + return self._cached_temp_folder + else: + self._cached_temp_folder = ( + self._temp_folder_manager.resolve_temp_folder_name() + ) # noqa + return self._cached_temp_folder + + +class _TestingMemmappingExecutor(MemmappingExecutor): + """Wrapper around ReusableExecutor to ease memmapping testing with Pool + and Executor. This is only for testing purposes. + + """ + + def apply_async(self, func, args): + """Schedule a func to be run""" + future = self.submit(func, *args) + future.get = future.result + return future + + def map(self, f, *args): + return list(super().map(f, *args)) diff --git a/.venv/lib/python3.12/site-packages/joblib/func_inspect.py b/.venv/lib/python3.12/site-packages/joblib/func_inspect.py new file mode 100644 index 0000000000000000000000000000000000000000..6f28f88cd0a06052672f1ccd8c110fed2537d905 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib/func_inspect.py @@ -0,0 +1,379 @@ +""" +My own variation on function-specific inspect-like features. +""" + +# Author: Gael Varoquaux +# Copyright (c) 2009 Gael Varoquaux +# License: BSD Style, 3 clauses. + +import collections +import inspect +import os +import re +import warnings +from itertools import islice +from tokenize import open as open_py_source + +from .logger import pformat + +full_argspec_fields = ( + "args varargs varkw defaults kwonlyargs kwonlydefaults annotations" +) +full_argspec_type = collections.namedtuple("FullArgSpec", full_argspec_fields) + + +def get_func_code(func): + """Attempts to retrieve a reliable function code hash. + + The reason we don't use inspect.getsource is that it caches the + source, whereas we want this to be modified on the fly when the + function is modified. + + Returns + ------- + func_code: string + The function code + source_file: string + The path to the file in which the function is defined. + first_line: int + The first line of the code in the source file. + + Notes + ------ + This function does a bit more magic than inspect, and is thus + more robust. + """ + source_file = None + try: + code = func.__code__ + source_file = code.co_filename + if not os.path.exists(source_file): + # Use inspect for lambda functions and functions defined in an + # interactive shell, or in doctests + source_code = "".join(inspect.getsourcelines(func)[0]) + line_no = 1 + if source_file.startswith("", source_file + ).groups() + line_no = int(line_no) + source_file = "" % source_file + return source_code, source_file, line_no + # Try to retrieve the source code. + with open_py_source(source_file) as source_file_obj: + first_line = code.co_firstlineno + # All the lines after the function definition: + source_lines = list(islice(source_file_obj, first_line - 1, None)) + return "".join(inspect.getblock(source_lines)), source_file, first_line + except: # noqa: E722 + # If the source code fails, we use the hash. This is fragile and + # might change from one session to another. + if hasattr(func, "__code__"): + # Python 3.X + return str(func.__code__.__hash__()), source_file, -1 + else: + # Weird objects like numpy ufunc don't have __code__ + # This is fragile, as quite often the id of the object is + # in the repr, so it might not persist across sessions, + # however it will work for ufuncs. + return repr(func), source_file, -1 + + +def _clean_win_chars(string): + """Windows cannot encode some characters in filename.""" + import urllib + + if hasattr(urllib, "quote"): + quote = urllib.quote + else: + # In Python 3, quote is elsewhere + import urllib.parse + + quote = urllib.parse.quote + for char in ("<", ">", "!", ":", "\\"): + string = string.replace(char, quote(char)) + return string + + +def get_func_name(func, resolv_alias=True, win_characters=True): + """Return the function import path (as a list of module names), and + a name for the function. + + Parameters + ---------- + func: callable + The func to inspect + resolv_alias: boolean, optional + If true, possible local aliases are indicated. + win_characters: boolean, optional + If true, substitute special characters using urllib.quote + This is useful in Windows, as it cannot encode some filenames + """ + if hasattr(func, "__module__"): + module = func.__module__ + else: + try: + module = inspect.getmodule(func) + except TypeError: + if hasattr(func, "__class__"): + module = func.__class__.__module__ + else: + module = "unknown" + if module is None: + # Happens in doctests, eg + module = "" + if module == "__main__": + try: + filename = os.path.abspath(inspect.getsourcefile(func)) + except: # noqa: E722 + filename = None + if filename is not None: + # mangling of full path to filename + parts = filename.split(os.sep) + if parts[-1].startswith(", where: + # - N is the cell number where the function was defined + # - XYZ is a hash representing the function's code (and name). + # It will be consistent across sessions and kernel restarts, + # and will change if the function's code/name changes + # We remove N so that cache is properly hit if the cell where + # the func is defined is re-exectuted. + # The XYZ hash should avoid collisions between functions with + # the same name, both within the same notebook but also across + # notebooks + split = parts[-1].split("-") + parts[-1] = "-".join(split[:2] + split[3:]) + elif len(parts) > 2 and parts[-2].startswith("ipykernel_"): + # In a notebook session (ipykernel). Filename seems to be 'xyz' + # of above. parts[-2] has the structure ipykernel_XXXXXX where + # XXXXXX is a six-digit number identifying the current run (?). + # If we split it off, the function again has the same + # identifier across runs. + parts[-2] = "ipykernel" + filename = "-".join(parts) + if filename.endswith(".py"): + filename = filename[:-3] + module = module + "-" + filename + module = module.split(".") + if hasattr(func, "func_name"): + name = func.func_name + elif hasattr(func, "__name__"): + name = func.__name__ + else: + name = "unknown" + # Hack to detect functions not defined at the module-level + if resolv_alias: + # TODO: Maybe add a warning here? + if hasattr(func, "func_globals") and name in func.func_globals: + if func.func_globals[name] is not func: + name = "%s-alias" % name + if hasattr(func, "__qualname__") and func.__qualname__ != name: + # Extend the module name in case of nested functions to avoid + # (module, name) collisions + module.extend(func.__qualname__.split(".")[:-1]) + if inspect.ismethod(func): + # We need to add the name of the class + if hasattr(func, "im_class"): + klass = func.im_class + module.append(klass.__name__) + if os.name == "nt" and win_characters: + # Windows can't encode certain characters in filenames + name = _clean_win_chars(name) + module = [_clean_win_chars(s) for s in module] + return module, name + + +def _signature_str(function_name, arg_sig): + """Helper function to output a function signature""" + return "{}{}".format(function_name, arg_sig) + + +def _function_called_str(function_name, args, kwargs): + """Helper function to output a function call""" + template_str = "{0}({1}, {2})" + + args_str = repr(args)[1:-1] + kwargs_str = ", ".join("%s=%s" % (k, v) for k, v in kwargs.items()) + return template_str.format(function_name, args_str, kwargs_str) + + +def filter_args(func, ignore_lst, args=(), kwargs=dict()): + """Filters the given args and kwargs using a list of arguments to + ignore, and a function specification. + + Parameters + ---------- + func: callable + Function giving the argument specification + ignore_lst: list of strings + List of arguments to ignore (either a name of an argument + in the function spec, or '*', or '**') + *args: list + Positional arguments passed to the function. + **kwargs: dict + Keyword arguments passed to the function + + Returns + ------- + filtered_args: list + List of filtered positional and keyword arguments. + """ + args = list(args) + if isinstance(ignore_lst, str): + # Catch a common mistake + raise ValueError( + "ignore_lst must be a list of parameters to ignore " + "%s (type %s) was given" % (ignore_lst, type(ignore_lst)) + ) + # Special case for functools.partial objects + if not inspect.ismethod(func) and not inspect.isfunction(func): + if ignore_lst: + warnings.warn( + "Cannot inspect object %s, ignore list will not work." % func, + stacklevel=2, + ) + return {"*": args, "**": kwargs} + arg_sig = inspect.signature(func) + arg_names = [] + arg_defaults = [] + arg_kwonlyargs = [] + arg_varargs = None + arg_varkw = None + for param in arg_sig.parameters.values(): + if param.kind is param.POSITIONAL_OR_KEYWORD: + arg_names.append(param.name) + elif param.kind is param.KEYWORD_ONLY: + arg_names.append(param.name) + arg_kwonlyargs.append(param.name) + elif param.kind is param.VAR_POSITIONAL: + arg_varargs = param.name + elif param.kind is param.VAR_KEYWORD: + arg_varkw = param.name + if param.default is not param.empty: + arg_defaults.append(param.default) + if inspect.ismethod(func): + # First argument is 'self', it has been removed by Python + # we need to add it back: + args = [ + func.__self__, + ] + args + # func is an instance method, inspect.signature(func) does not + # include self, we need to fetch it from the class method, i.e + # func.__func__ + class_method_sig = inspect.signature(func.__func__) + self_name = next(iter(class_method_sig.parameters)) + arg_names = [self_name] + arg_names + # XXX: Maybe I need an inspect.isbuiltin to detect C-level methods, such + # as on ndarrays. + + _, name = get_func_name(func, resolv_alias=False) + arg_dict = dict() + arg_position = -1 + for arg_position, arg_name in enumerate(arg_names): + if arg_position < len(args): + # Positional argument or keyword argument given as positional + if arg_name not in arg_kwonlyargs: + arg_dict[arg_name] = args[arg_position] + else: + raise ValueError( + "Keyword-only parameter '%s' was passed as " + "positional parameter for %s:\n" + " %s was called." + % ( + arg_name, + _signature_str(name, arg_sig), + _function_called_str(name, args, kwargs), + ) + ) + + else: + position = arg_position - len(arg_names) + if arg_name in kwargs: + arg_dict[arg_name] = kwargs[arg_name] + else: + try: + arg_dict[arg_name] = arg_defaults[position] + except (IndexError, KeyError) as e: + # Missing argument + raise ValueError( + "Wrong number of arguments for %s:\n" + " %s was called." + % ( + _signature_str(name, arg_sig), + _function_called_str(name, args, kwargs), + ) + ) from e + + varkwargs = dict() + for arg_name, arg_value in sorted(kwargs.items()): + if arg_name in arg_dict: + arg_dict[arg_name] = arg_value + elif arg_varkw is not None: + varkwargs[arg_name] = arg_value + else: + raise TypeError( + "Ignore list for %s() contains an unexpected " + "keyword argument '%s'" % (name, arg_name) + ) + + if arg_varkw is not None: + arg_dict["**"] = varkwargs + if arg_varargs is not None: + varargs = args[arg_position + 1 :] + arg_dict["*"] = varargs + + # Now remove the arguments to be ignored + for item in ignore_lst: + if item in arg_dict: + arg_dict.pop(item) + else: + raise ValueError( + "Ignore list: argument '%s' is not defined for " + "function %s" % (item, _signature_str(name, arg_sig)) + ) + # XXX: Return a sorted list of pairs? + return arg_dict + + +def _format_arg(arg): + formatted_arg = pformat(arg, indent=2) + if len(formatted_arg) > 1500: + formatted_arg = "%s..." % formatted_arg[:700] + return formatted_arg + + +def format_signature(func, *args, **kwargs): + # XXX: Should this use inspect.formatargvalues/formatargspec? + module, name = get_func_name(func) + module = [m for m in module if m] + if module: + module.append(name) + module_path = ".".join(module) + else: + module_path = name + arg_str = list() + previous_length = 0 + for arg in args: + formatted_arg = _format_arg(arg) + if previous_length > 80: + formatted_arg = "\n%s" % formatted_arg + previous_length = len(formatted_arg) + arg_str.append(formatted_arg) + arg_str.extend(["%s=%s" % (v, _format_arg(i)) for v, i in kwargs.items()]) + arg_str = ", ".join(arg_str) + + signature = "%s(%s)" % (name, arg_str) + return module_path, signature + + +def format_call(func, args, kwargs, object_name="Memory"): + """Returns a nicely formatted statement displaying the function + call with the given arguments. + """ + path, signature = format_signature(func, *args, **kwargs) + msg = "%s\n[%s] Calling %s...\n%s" % (80 * "_", object_name, path, signature) + return msg + # XXX: Not using logging framework + # self.debug(msg) diff --git a/.venv/lib/python3.12/site-packages/joblib/hashing.py b/.venv/lib/python3.12/site-packages/joblib/hashing.py new file mode 100644 index 0000000000000000000000000000000000000000..2055acf85cbd50a81a265c02f6d9dada554424fa --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib/hashing.py @@ -0,0 +1,270 @@ +""" +Fast cryptographic hash of Python objects, with a special case for fast +hashing of numpy arrays. +""" + +# Author: Gael Varoquaux +# Copyright (c) 2009 Gael Varoquaux +# License: BSD Style, 3 clauses. + +import decimal +import hashlib +import io +import pickle +import struct +import sys +import types + +Pickler = pickle._Pickler + + +class _ConsistentSet(object): + """Class used to ensure the hash of Sets is preserved + whatever the order of its items. + """ + + def __init__(self, set_sequence): + # Forces order of elements in set to ensure consistent hash. + try: + # Trying first to order the set assuming the type of elements is + # consistent and orderable. + # This fails on python 3 when elements are unorderable + # but we keep it in a try as it's faster. + self._sequence = sorted(set_sequence) + except (TypeError, decimal.InvalidOperation): + # If elements are unorderable, sorting them using their hash. + # This is slower but works in any case. + self._sequence = sorted((hash(e) for e in set_sequence)) + + +class _MyHash(object): + """Class used to hash objects that won't normally pickle""" + + def __init__(self, *args): + self.args = args + + +class Hasher(Pickler): + """A subclass of pickler, to do cryptographic hashing, rather than + pickling. This is used to produce a unique hash of the given + Python object that is not necessarily cryptographically secure. + """ + + def __init__(self, hash_name="md5"): + self.stream = io.BytesIO() + # By default we want a pickle protocol that only changes with + # the major python version and not the minor one + protocol = 3 + Pickler.__init__(self, self.stream, protocol=protocol) + # Initialise the hash obj + self._hash = hashlib.new(hash_name, usedforsecurity=False) + + def hash(self, obj, return_digest=True): + try: + self.dump(obj) + except pickle.PicklingError as e: + e.args += ("PicklingError while hashing %r: %r" % (obj, e),) + raise + dumps = self.stream.getvalue() + self._hash.update(dumps) + if return_digest: + return self._hash.hexdigest() + + def save(self, obj): + if isinstance(obj, (types.MethodType, type({}.pop))): + # the Pickler cannot pickle instance methods; here we decompose + # them into components that make them uniquely identifiable + if hasattr(obj, "__func__"): + func_name = obj.__func__.__name__ + else: + func_name = obj.__name__ + inst = obj.__self__ + if type(inst) is type(pickle): + obj = _MyHash(func_name, inst.__name__) + elif inst is None: + # type(None) or type(module) do not pickle + obj = _MyHash(func_name, inst) + else: + cls = obj.__self__.__class__ + obj = _MyHash(func_name, inst, cls) + Pickler.save(self, obj) + + def memoize(self, obj): + # We want hashing to be sensitive to value instead of reference. + # For example we want ['aa', 'aa'] and ['aa', 'aaZ'[:2]] + # to hash to the same value and that's why we disable memoization + # for strings + if isinstance(obj, (bytes, str)): + return + Pickler.memoize(self, obj) + + # The dispatch table of the pickler is not accessible in Python + # 3, as these lines are only bugware for IPython, we skip them. + def save_global(self, obj, name=None, pack=struct.pack): + # We have to override this method in order to deal with objects + # defined interactively in IPython that are not injected in + # __main__ + kwargs = dict(name=name, pack=pack) + del kwargs["pack"] + try: + Pickler.save_global(self, obj, **kwargs) + except pickle.PicklingError: + Pickler.save_global(self, obj, **kwargs) + module = getattr(obj, "__module__", None) + if module == "__main__": + my_name = name + if my_name is None: + my_name = obj.__name__ + mod = sys.modules[module] + if not hasattr(mod, my_name): + # IPython doesn't inject the variables define + # interactively in __main__ + setattr(mod, my_name, obj) + + dispatch = Pickler.dispatch.copy() + # builtin + dispatch[type(len)] = save_global + # type + dispatch[type(object)] = save_global + # classobj + dispatch[type(Pickler)] = save_global + # function + dispatch[type(pickle.dump)] = save_global + + # We use *args in _batch_setitems signature because _batch_setitems has an + # additional 'obj' argument in Python 3.14 + def _batch_setitems(self, items, *args): + # forces order of keys in dict to ensure consistent hash. + try: + # Trying first to compare dict assuming the type of keys is + # consistent and orderable. + # This fails on python 3 when keys are unorderable + # but we keep it in a try as it's faster. + Pickler._batch_setitems(self, iter(sorted(items)), *args) + except TypeError: + # If keys are unorderable, sorting them using their hash. This is + # slower but works in any case. + Pickler._batch_setitems( + self, iter(sorted((hash(k), v) for k, v in items)), *args + ) + + def save_set(self, set_items): + # forces order of items in Set to ensure consistent hash + Pickler.save(self, _ConsistentSet(set_items)) + + dispatch[type(set())] = save_set + + +class NumpyHasher(Hasher): + """Special case the hasher for when numpy is loaded.""" + + def __init__(self, hash_name="md5", coerce_mmap=False): + """ + Parameters + ---------- + hash_name: string + The hash algorithm to be used + coerce_mmap: boolean + Make no difference between np.memmap and np.ndarray + objects. + """ + self.coerce_mmap = coerce_mmap + Hasher.__init__(self, hash_name=hash_name) + # delayed import of numpy, to avoid tight coupling + import numpy as np + + self.np = np + if hasattr(np, "getbuffer"): + self._getbuffer = np.getbuffer + else: + self._getbuffer = memoryview + + def save(self, obj): + """Subclass the save method, to hash ndarray subclass, rather + than pickling them. Off course, this is a total abuse of + the Pickler class. + """ + if isinstance(obj, self.np.ndarray) and not obj.dtype.hasobject: + # Compute a hash of the object + # The update function of the hash requires a c_contiguous buffer. + if obj.shape == (): + # 0d arrays need to be flattened because viewing them as bytes + # raises a ValueError exception. + obj_c_contiguous = obj.flatten() + elif obj.flags.c_contiguous: + obj_c_contiguous = obj + elif obj.flags.f_contiguous: + obj_c_contiguous = obj.T + else: + # Cater for non-single-segment arrays: this creates a + # copy, and thus alleviates this issue. + # XXX: There might be a more efficient way of doing this + obj_c_contiguous = obj.flatten() + + # memoryview is not supported for some dtypes, e.g. datetime64, see + # https://github.com/numpy/numpy/issues/4983. The + # workaround is to view the array as bytes before + # taking the memoryview. + self._hash.update(self._getbuffer(obj_c_contiguous.view(self.np.uint8))) + + # We store the class, to be able to distinguish between + # Objects with the same binary content, but different + # classes. + if self.coerce_mmap and isinstance(obj, self.np.memmap): + # We don't make the difference between memmap and + # normal ndarrays, to be able to reload previously + # computed results with memmap. + klass = self.np.ndarray + else: + klass = obj.__class__ + # We also return the dtype and the shape, to distinguish + # different views on the same data with different dtypes. + + # The object will be pickled by the pickler hashed at the end. + obj = (klass, ("HASHED", obj.dtype, obj.shape, obj.strides)) + elif isinstance(obj, self.np.dtype): + # numpy.dtype consistent hashing is tricky to get right. This comes + # from the fact that atomic np.dtype objects are interned: + # ``np.dtype('f4') is np.dtype('f4')``. The situation is + # complicated by the fact that this interning does not resist a + # simple pickle.load/dump roundtrip: + # ``pickle.loads(pickle.dumps(np.dtype('f4'))) is not + # np.dtype('f4') Because pickle relies on memoization during + # pickling, it is easy to + # produce different hashes for seemingly identical objects, such as + # ``[np.dtype('f4'), np.dtype('f4')]`` + # and ``[np.dtype('f4'), pickle.loads(pickle.dumps('f4'))]``. + # To prevent memoization from interfering with hashing, we isolate + # the serialization (and thus the pickle memoization) of each dtype + # using each time a different ``pickle.dumps`` call unrelated to + # the current Hasher instance. + self._hash.update("_HASHED_DTYPE".encode("utf-8")) + self._hash.update(pickle.dumps(obj)) + return + Hasher.save(self, obj) + + +def hash(obj, hash_name="md5", coerce_mmap=False): + """Quick calculation of a hash to identify uniquely Python objects + containing numpy arrays. + + Parameters + ---------- + hash_name: 'md5' or 'sha1' + Hashing algorithm used. sha1 is supposedly safer, but md5 is + faster. + coerce_mmap: boolean + Make no difference between np.memmap and np.ndarray + """ + valid_hash_names = ("md5", "sha1") + if hash_name not in valid_hash_names: + raise ValueError( + "Valid options for 'hash_name' are {}. Got hash_name={!r} instead.".format( + valid_hash_names, hash_name + ) + ) + if "numpy" in sys.modules: + hasher = NumpyHasher(hash_name=hash_name, coerce_mmap=coerce_mmap) + else: + hasher = Hasher(hash_name=hash_name) + return hasher.hash(obj) diff --git a/.venv/lib/python3.12/site-packages/joblib/logger.py b/.venv/lib/python3.12/site-packages/joblib/logger.py new file mode 100644 index 0000000000000000000000000000000000000000..ed250fd1f2d15e519faf62f6b4392f944b70bb94 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib/logger.py @@ -0,0 +1,159 @@ +""" +Helpers for logging. + +This module needs much love to become useful. +""" + +# Author: Gael Varoquaux +# Copyright (c) 2008 Gael Varoquaux +# License: BSD Style, 3 clauses. + +from __future__ import print_function + +import logging +import os +import pprint +import shutil +import sys +import time + +from .disk import mkdirp + + +def _squeeze_time(t): + """Remove .1s to the time under Windows: this is the time it take to + stat files. This is needed to make results similar to timings under + Unix, for tests + """ + if sys.platform.startswith("win"): + return max(0, t - 0.1) + else: + return t + + +def format_time(t): + t = _squeeze_time(t) + return "%.1fs, %.1fmin" % (t, t / 60.0) + + +def short_format_time(t): + t = _squeeze_time(t) + if t > 60: + return "%4.1fmin" % (t / 60.0) + else: + return " %5.1fs" % (t) + + +def pformat(obj, indent=0, depth=3): + if "numpy" in sys.modules: + import numpy as np + + print_options = np.get_printoptions() + np.set_printoptions(precision=6, threshold=64, edgeitems=1) + else: + print_options = None + out = pprint.pformat(obj, depth=depth, indent=indent) + if print_options: + np.set_printoptions(**print_options) + return out + + +############################################################################### +# class `Logger` +############################################################################### +class Logger(object): + """Base class for logging messages.""" + + def __init__(self, depth=3, name=None): + """ + Parameters + ---------- + depth: int, optional + The depth of objects printed. + name: str, optional + The namespace to log to. If None, defaults to joblib. + """ + self.depth = depth + self._name = name if name else "joblib" + + def warn(self, msg): + logging.getLogger(self._name).warning("[%s]: %s" % (self, msg)) + + def info(self, msg): + logging.info("[%s]: %s" % (self, msg)) + + def debug(self, msg): + # XXX: This conflicts with the debug flag used in children class + logging.getLogger(self._name).debug("[%s]: %s" % (self, msg)) + + def format(self, obj, indent=0): + """Return the formatted representation of the object.""" + return pformat(obj, indent=indent, depth=self.depth) + + +############################################################################### +# class `PrintTime` +############################################################################### +class PrintTime(object): + """Print and log messages while keeping track of time.""" + + def __init__(self, logfile=None, logdir=None): + if logfile is not None and logdir is not None: + raise ValueError("Cannot specify both logfile and logdir") + # XXX: Need argument docstring + self.last_time = time.time() + self.start_time = self.last_time + if logdir is not None: + logfile = os.path.join(logdir, "joblib.log") + self.logfile = logfile + if logfile is not None: + mkdirp(os.path.dirname(logfile)) + if os.path.exists(logfile): + # Rotate the logs + for i in range(1, 9): + try: + shutil.move(logfile + ".%i" % i, logfile + ".%i" % (i + 1)) + except: # noqa: E722 + "No reason failing here" + # Use a copy rather than a move, so that a process + # monitoring this file does not get lost. + try: + shutil.copy(logfile, logfile + ".1") + except: # noqa: E722 + "No reason failing here" + try: + with open(logfile, "w") as logfile: + logfile.write("\nLogging joblib python script\n") + logfile.write("\n---%s---\n" % time.ctime(self.last_time)) + except: # noqa: E722 + """ Multiprocessing writing to files can create race + conditions. Rather fail silently than crash the + computation. + """ + # XXX: We actually need a debug flag to disable this + # silent failure. + + def __call__(self, msg="", total=False): + """Print the time elapsed between the last call and the current + call, with an optional message. + """ + if not total: + time_lapse = time.time() - self.last_time + full_msg = "%s: %s" % (msg, format_time(time_lapse)) + else: + # FIXME: Too much logic duplicated + time_lapse = time.time() - self.start_time + full_msg = "%s: %.2fs, %.1f min" % (msg, time_lapse, time_lapse / 60) + print(full_msg, file=sys.stderr) + if self.logfile is not None: + try: + with open(self.logfile, "a") as f: + print(full_msg, file=f) + except: # noqa: E722 + """ Multiprocessing writing to files can create race + conditions. Rather fail silently than crash the + calculation. + """ + # XXX: We actually need a debug flag to disable this + # silent failure. + self.last_time = time.time() diff --git a/.venv/lib/python3.12/site-packages/joblib/memory.py b/.venv/lib/python3.12/site-packages/joblib/memory.py new file mode 100644 index 0000000000000000000000000000000000000000..c4670c9121f9c1b1668b633d7ada9425cfb4aed3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib/memory.py @@ -0,0 +1,1242 @@ +""" +A context object for caching a function's return value each time it +is called with the same input arguments. + +""" + +# Author: Gael Varoquaux +# Copyright (c) 2009 Gael Varoquaux +# License: BSD Style, 3 clauses. + +import asyncio +import datetime +import functools +import inspect +import logging +import os +import pathlib +import pydoc +import re +import textwrap +import time +import tokenize +import traceback +import warnings +import weakref + +from . import hashing +from ._store_backends import ( + CacheWarning, # noqa + FileSystemStoreBackend, + StoreBackendBase, +) +from .func_inspect import ( + filter_args, + format_call, + format_signature, + get_func_code, + get_func_name, +) +from .logger import Logger, format_time, pformat + +FIRST_LINE_TEXT = "# first line:" + +# TODO: The following object should have a data store object as a sub +# object, and the interface to persist and query should be separated in +# the data store. +# +# This would enable creating 'Memory' objects with a different logic for +# pickling that would simply span a MemorizedFunc with the same +# store (or do we want to copy it to avoid cross-talks?), for instance to +# implement HDF5 pickling. + +# TODO: Same remark for the logger, and probably use the Python logging +# mechanism. + + +def extract_first_line(func_code): + """Extract the first line information from the function code + text if available. + """ + if func_code.startswith(FIRST_LINE_TEXT): + func_code = func_code.split("\n") + first_line = int(func_code[0][len(FIRST_LINE_TEXT) :]) + func_code = "\n".join(func_code[1:]) + else: + first_line = -1 + return func_code, first_line + + +class JobLibCollisionWarning(UserWarning): + """Warn that there might be a collision between names of functions.""" + + +_STORE_BACKENDS = {"local": FileSystemStoreBackend} + + +def register_store_backend(backend_name, backend): + """Extend available store backends. + + The Memory, MemorizeResult and MemorizeFunc objects are designed to be + agnostic to the type of store used behind. By default, the local file + system is used but this function gives the possibility to extend joblib's + memory pattern with other types of storage such as cloud storage (S3, GCS, + OpenStack, HadoopFS, etc) or blob DBs. + + Parameters + ---------- + backend_name: str + The name identifying the store backend being registered. For example, + 'local' is used with FileSystemStoreBackend. + backend: StoreBackendBase subclass + The name of a class that implements the StoreBackendBase interface. + + """ + if not isinstance(backend_name, str): + raise ValueError( + "Store backend name should be a string, '{0}' given.".format(backend_name) + ) + if backend is None or not issubclass(backend, StoreBackendBase): + raise ValueError( + "Store backend should inherit StoreBackendBase, '{0}' given.".format( + backend + ) + ) + + _STORE_BACKENDS[backend_name] = backend + + +def _store_backend_factory(backend, location, verbose=0, backend_options=None): + """Return the correct store object for the given location.""" + if backend_options is None: + backend_options = {} + + if isinstance(location, pathlib.Path): + location = str(location) + + if isinstance(location, StoreBackendBase): + return location + elif isinstance(location, str): + obj = None + location = os.path.expanduser(location) + # The location is not a local file system, we look in the + # registered backends if there's one matching the given backend + # name. + for backend_key, backend_obj in _STORE_BACKENDS.items(): + if backend == backend_key: + obj = backend_obj() + + # By default, we assume the FileSystemStoreBackend can be used if no + # matching backend could be found. + if obj is None: + raise TypeError( + "Unknown location {0} or backend {1}".format(location, backend) + ) + + # The store backend is configured with the extra named parameters, + # some of them are specific to the underlying store backend. + obj.configure(location, verbose=verbose, backend_options=backend_options) + return obj + elif location is not None: + warnings.warn( + "Instantiating a backend using a {} as a location is not " + "supported by joblib. Returning None instead.".format( + location.__class__.__name__ + ), + UserWarning, + ) + + return None + + +def _build_func_identifier(func): + """Build a roughly unique identifier for the cached function.""" + modules, funcname = get_func_name(func) + # We reuse historical fs-like way of building a function identifier + return os.path.join(*modules, funcname) + + +# An in-memory store to avoid looking at the disk-based function +# source code to check if a function definition has changed +_FUNCTION_HASHES = weakref.WeakKeyDictionary() + + +############################################################################### +# class `MemorizedResult` +############################################################################### +class MemorizedResult(Logger): + """Object representing a cached value. + + Attributes + ---------- + location: str + The location of joblib cache. Depends on the store backend used. + + func: function or str + function whose output is cached. The string case is intended only for + instantiation based on the output of repr() on another instance. + (namely eval(repr(memorized_instance)) works). + + argument_hash: str + hash of the function arguments. + + backend: str + Type of store backend for reading/writing cache files. + Default is 'local'. + + mmap_mode: {None, 'r+', 'r', 'w+', 'c'} + The memmapping mode used when loading from cache numpy arrays. See + numpy.load for the meaning of the different values. + + verbose: int + verbosity level (0 means no message). + + timestamp, metadata: string + for internal use only. + """ + + def __init__( + self, + location, + call_id, + backend="local", + mmap_mode=None, + verbose=0, + timestamp=None, + metadata=None, + ): + Logger.__init__(self) + self._call_id = call_id + self.store_backend = _store_backend_factory(backend, location, verbose=verbose) + self.mmap_mode = mmap_mode + + if metadata is not None: + self.metadata = metadata + else: + self.metadata = self.store_backend.get_metadata(self._call_id) + + self.duration = self.metadata.get("duration", None) + self.verbose = verbose + self.timestamp = timestamp + + @property + def func(self): + return self.func_id + + @property + def func_id(self): + return self._call_id[0] + + @property + def args_id(self): + return self._call_id[1] + + def get(self): + """Read value from cache and return it.""" + try: + return self.store_backend.load_item( + self._call_id, + timestamp=self.timestamp, + metadata=self.metadata, + verbose=self.verbose, + ) + except ValueError as exc: + new_exc = KeyError( + "Error while trying to load a MemorizedResult's value. " + "It seems that this folder is corrupted : {}".format( + os.path.join(self.store_backend.location, *self._call_id) + ) + ) + raise new_exc from exc + + def clear(self): + """Clear value from cache""" + self.store_backend.clear_item(self._call_id) + + def __repr__(self): + return '{}(location="{}", func="{}", args_id="{}")'.format( + self.__class__.__name__, self.store_backend.location, *self._call_id + ) + + def __getstate__(self): + state = self.__dict__.copy() + state["timestamp"] = None + return state + + +class NotMemorizedResult(object): + """Class representing an arbitrary value. + + This class is a replacement for MemorizedResult when there is no cache. + """ + + __slots__ = ("value", "valid") + + def __init__(self, value): + self.value = value + self.valid = True + + def get(self): + if self.valid: + return self.value + else: + raise KeyError("No value stored.") + + def clear(self): + self.valid = False + self.value = None + + def __repr__(self): + if self.valid: + return "{class_name}({value})".format( + class_name=self.__class__.__name__, value=pformat(self.value) + ) + else: + return self.__class__.__name__ + " with no value" + + # __getstate__ and __setstate__ are required because of __slots__ + def __getstate__(self): + return {"valid": self.valid, "value": self.value} + + def __setstate__(self, state): + self.valid = state["valid"] + self.value = state["value"] + + +############################################################################### +# class `NotMemorizedFunc` +############################################################################### +class NotMemorizedFunc(object): + """No-op object decorating a function. + + This class replaces MemorizedFunc when there is no cache. It provides an + identical API but does not write anything on disk. + + Attributes + ---------- + func: callable + Original undecorated function. + """ + + # Should be a light as possible (for speed) + def __init__(self, func): + self.func = func + + def __call__(self, *args, **kwargs): + return self.func(*args, **kwargs) + + def call_and_shelve(self, *args, **kwargs): + return NotMemorizedResult(self.func(*args, **kwargs)) + + def __repr__(self): + return "{0}(func={1})".format(self.__class__.__name__, self.func) + + def clear(self, warn=True): + # Argument "warn" is for compatibility with MemorizedFunc.clear + pass + + def call(self, *args, **kwargs): + return self.func(*args, **kwargs), {} + + def check_call_in_cache(self, *args, **kwargs): + return False + + +############################################################################### +# class `AsyncNotMemorizedFunc` +############################################################################### +class AsyncNotMemorizedFunc(NotMemorizedFunc): + async def call_and_shelve(self, *args, **kwargs): + return NotMemorizedResult(await self.func(*args, **kwargs)) + + +############################################################################### +# class `MemorizedFunc` +############################################################################### +class MemorizedFunc(Logger): + """Callable object decorating a function for caching its return value + each time it is called. + + Methods are provided to inspect the cache or clean it. + + Attributes + ---------- + func: callable + The original, undecorated, function. + + location: string + The location of joblib cache. Depends on the store backend used. + + backend: str + Type of store backend for reading/writing cache files. + Default is 'local', in which case the location is the path to a + disk storage. + + ignore: list or None + List of variable names to ignore when choosing whether to + recompute. + + mmap_mode: {None, 'r+', 'r', 'w+', 'c'} + The memmapping mode used when loading from cache + numpy arrays. See numpy.load for the meaning of the different + values. + + compress: boolean, or integer + Whether to zip the stored data on disk. If an integer is + given, it should be between 1 and 9, and sets the amount + of compression. Note that compressed arrays cannot be + read by memmapping. + + verbose: int, optional + The verbosity flag, controls messages that are issued as + the function is evaluated. + + cache_validation_callback: callable, optional + Callable to check if a result in cache is valid or is to be recomputed. + When the function is called with arguments for which a cache exists, + the callback is called with the cache entry's metadata as its sole + argument. If it returns True, the cached result is returned, else the + cache for these arguments is cleared and the result is recomputed. + """ + + # ------------------------------------------------------------------------ + # Public interface + # ------------------------------------------------------------------------ + + def __init__( + self, + func, + location, + backend="local", + ignore=None, + mmap_mode=None, + compress=False, + verbose=1, + timestamp=None, + cache_validation_callback=None, + ): + Logger.__init__(self) + self.mmap_mode = mmap_mode + self.compress = compress + self.func = func + self.cache_validation_callback = cache_validation_callback + self.func_id = _build_func_identifier(func) + self.ignore = ignore if ignore is not None else [] + self._verbose = verbose + + # retrieve store object from backend type and location. + self.store_backend = _store_backend_factory( + backend, + location, + verbose=verbose, + backend_options=dict(compress=compress, mmap_mode=mmap_mode), + ) + if self.store_backend is not None: + # Create func directory on demand. + self.store_backend.store_cached_func_code([self.func_id]) + + self.timestamp = timestamp if timestamp is not None else time.time() + try: + functools.update_wrapper(self, func) + except Exception: + pass # Objects like ufunc don't like that + if inspect.isfunction(func): + doc = pydoc.TextDoc().document(func) + # Remove blank line + doc = doc.replace("\n", "\n\n", 1) + # Strip backspace-overprints for compatibility with autodoc + doc = re.sub("\x08.", "", doc) + else: + # Pydoc does a poor job on other objects + doc = func.__doc__ + self.__doc__ = "Memoized version of %s" % doc + + self._func_code_info = None + self._func_code_id = None + + def _is_in_cache_and_valid(self, call_id): + """Check if the function call is cached and valid for given arguments. + + - Compare the function code with the one from the cached function, + asserting if it has changed. + - Check if the function call is present in the cache. + - Call `cache_validation_callback` for user define cache validation. + + Returns True if the function call is in cache and can be used, and + returns False otherwise. + """ + # Check if the code of the function has changed + if not self._check_previous_func_code(stacklevel=4): + return False + + # Check if this specific call is in the cache + if not self.store_backend.contains_item(call_id): + return False + + # Call the user defined cache validation callback + metadata = self.store_backend.get_metadata(call_id) + if ( + self.cache_validation_callback is not None + and not self.cache_validation_callback(metadata) + ): + self.store_backend.clear_item(call_id) + return False + + return True + + def _cached_call(self, args, kwargs, shelving): + """Call wrapped function and cache result, or read cache if available. + + This function returns the wrapped function output or a reference to + the cached result. + + Arguments: + ---------- + + args, kwargs: list and dict + input arguments for wrapped function + + shelving: bool + True when called via the call_and_shelve function. + + + Returns + ------- + output: Output of the wrapped function if shelving is false, or a + MemorizedResult reference to the value if shelving is true. + metadata: dict containing the metadata associated with the call. + """ + args_id = self._get_args_id(*args, **kwargs) + call_id = (self.func_id, args_id) + _, func_name = get_func_name(self.func) + func_info = self.store_backend.get_cached_func_info([self.func_id]) + location = func_info["location"] + + if self._verbose >= 20: + logging.basicConfig(level=logging.INFO) + _, signature = format_signature(self.func, *args, **kwargs) + self.info( + textwrap.dedent( + f""" + Querying {func_name} with signature + {signature}. + + (argument hash {args_id}) + + The store location is {location}. + """ + ) + ) + + # Compare the function code with the previous to see if the + # function code has changed and check if the results are present in + # the cache. + if self._is_in_cache_and_valid(call_id): + if shelving: + return self._get_memorized_result(call_id), {} + + try: + start_time = time.time() + output = self._load_item(call_id) + if self._verbose > 4: + self._print_duration( + time.time() - start_time, context="cache loaded " + ) + return output, {} + except Exception: + # XXX: Should use an exception logger + _, signature = format_signature(self.func, *args, **kwargs) + self.warn( + "Exception while loading results for {}\n {}".format( + signature, traceback.format_exc() + ) + ) + + if self._verbose > 10: + self.warn( + f"Computing func {func_name}, argument hash {args_id} " + f"in location {location}" + ) + + # Returns the output but not the metadata + return self._call(call_id, args, kwargs, shelving) + + @property + def func_code_info(self): + # 3-tuple property containing: the function source code, source file, + # and first line of the code inside the source file + if hasattr(self.func, "__code__"): + if self._func_code_id is None: + self._func_code_id = id(self.func.__code__) + elif id(self.func.__code__) != self._func_code_id: + # Be robust to dynamic reassignments of self.func.__code__ + self._func_code_info = None + + if self._func_code_info is None: + # Cache the source code of self.func . Provided that get_func_code + # (which should be called once on self) gets called in the process + # in which self.func was defined, this caching mechanism prevents + # undesired cache clearing when the cached function is called in + # an environment where the introspection utilities get_func_code + # relies on do not work (typically, in joblib child processes). + # See #1035 for more info + # TODO (pierreglaser): do the same with get_func_name? + self._func_code_info = get_func_code(self.func) + return self._func_code_info + + def call_and_shelve(self, *args, **kwargs): + """Call wrapped function, cache result and return a reference. + + This method returns a reference to the cached result instead of the + result itself. The reference object is small and picklable, allowing + to send or store it easily. Call .get() on reference object to get + result. + + Returns + ------- + cached_result: MemorizedResult or NotMemorizedResult + reference to the value returned by the wrapped function. The + class "NotMemorizedResult" is used when there is no cache + activated (e.g. location=None in Memory). + """ + # Return the wrapped output, without the metadata + return self._cached_call(args, kwargs, shelving=True)[0] + + def __call__(self, *args, **kwargs): + # Return the output, without the metadata + return self._cached_call(args, kwargs, shelving=False)[0] + + def __getstate__(self): + # Make sure self.func's source is introspected prior to being pickled - + # code introspection utilities typically do not work inside child + # processes + _ = self.func_code_info + + # We don't store the timestamp when pickling, to avoid the hash + # depending from it. + state = self.__dict__.copy() + state["timestamp"] = None + + # Invalidate the code id as id(obj) will be different in the child + state["_func_code_id"] = None + + return state + + def check_call_in_cache(self, *args, **kwargs): + """Check if the function call is cached and valid for given arguments. + + Does not call the function or do any work besides function inspection + and argument hashing. + + - Compare the function code with the one from the cached function, + asserting if it has changed. + - Check if the function call is present in the cache. + - Call `cache_validation_callback` for user define cache validation. + + Returns + ------- + is_call_in_cache: bool + Whether or not the function call is in cache and can be used. + """ + call_id = (self.func_id, self._get_args_id(*args, **kwargs)) + return self._is_in_cache_and_valid(call_id) + + # ------------------------------------------------------------------------ + # Private interface + # ------------------------------------------------------------------------ + + def _get_args_id(self, *args, **kwargs): + """Return the input parameter hash of a result.""" + return hashing.hash( + filter_args(self.func, self.ignore, args, kwargs), + coerce_mmap=self.mmap_mode is not None, + ) + + def _hash_func(self): + """Hash a function to key the online cache""" + func_code_h = hash(getattr(self.func, "__code__", None)) + return id(self.func), hash(self.func), func_code_h + + def _write_func_code(self, func_code, first_line): + """Write the function code and the filename to a file.""" + # We store the first line because the filename and the function + # name is not always enough to identify a function: people + # sometimes have several functions named the same way in a + # file. This is bad practice, but joblib should be robust to bad + # practice. + func_code = "%s %i\n%s" % (FIRST_LINE_TEXT, first_line, func_code) + self.store_backend.store_cached_func_code([self.func_id], func_code) + + # Also store in the in-memory store of function hashes + is_named_callable = ( + hasattr(self.func, "__name__") and self.func.__name__ != "" + ) + if is_named_callable: + # Don't do this for lambda functions or strange callable + # objects, as it ends up being too fragile + func_hash = self._hash_func() + try: + _FUNCTION_HASHES[self.func] = func_hash + except TypeError: + # Some callable are not hashable + pass + + def _check_previous_func_code(self, stacklevel=2): + """ + stacklevel is the depth a which this function is called, to + issue useful warnings to the user. + """ + # First check if our function is in the in-memory store. + # Using the in-memory store not only makes things faster, but it + # also renders us robust to variations of the files when the + # in-memory version of the code does not vary + try: + if self.func in _FUNCTION_HASHES: + # We use as an identifier the id of the function and its + # hash. This is more likely to falsely change than have hash + # collisions, thus we are on the safe side. + func_hash = self._hash_func() + if func_hash == _FUNCTION_HASHES[self.func]: + return True + except TypeError: + # Some callables are not hashable + pass + + # Here, we go through some effort to be robust to dynamically + # changing code and collision. We cannot inspect.getsource + # because it is not reliable when using IPython's magic "%run". + func_code, source_file, first_line = self.func_code_info + try: + old_func_code, old_first_line = extract_first_line( + self.store_backend.get_cached_func_code([self.func_id]) + ) + except (IOError, OSError): # some backend can also raise OSError + self._write_func_code(func_code, first_line) + return False + if old_func_code == func_code: + return True + + # We have differing code, is this because we are referring to + # different functions, or because the function we are referring to has + # changed? + + _, func_name = get_func_name( + self.func, resolv_alias=False, win_characters=False + ) + if old_first_line == first_line == -1 or func_name == "": + if not first_line == -1: + func_description = "{0} ({1}:{2})".format( + func_name, source_file, first_line + ) + else: + func_description = func_name + warnings.warn( + JobLibCollisionWarning( + "Cannot detect name collisions for function '{0}'".format( + func_description + ) + ), + stacklevel=stacklevel, + ) + + # Fetch the code at the old location and compare it. If it is the + # same than the code store, we have a collision: the code in the + # file has not changed, but the name we have is pointing to a new + # code block. + if not old_first_line == first_line and source_file is not None: + if os.path.exists(source_file): + _, func_name = get_func_name(self.func, resolv_alias=False) + num_lines = len(func_code.split("\n")) + with tokenize.open(source_file) as f: + on_disk_func_code = f.readlines()[ + old_first_line - 1 : old_first_line - 1 + num_lines - 1 + ] + on_disk_func_code = "".join(on_disk_func_code) + possible_collision = ( + on_disk_func_code.rstrip() == old_func_code.rstrip() + ) + else: + possible_collision = source_file.startswith(" 10: + _, func_name = get_func_name(self.func, resolv_alias=False) + self.warn( + "Function {0} (identified by {1}) has changed.".format( + func_name, self.func_id + ) + ) + self.clear(warn=True) + return False + + def clear(self, warn=True): + """Empty the function's cache.""" + func_id = self.func_id + if self._verbose > 0 and warn: + self.warn("Clearing function cache identified by %s" % func_id) + self.store_backend.clear_path( + [ + func_id, + ] + ) + + func_code, _, first_line = self.func_code_info + self._write_func_code(func_code, first_line) + + def call(self, *args, **kwargs): + """Force the execution of the function with the given arguments. + + The output values will be persisted, i.e., the cache will be updated + with any new values. + + Parameters + ---------- + *args: arguments + The arguments. + **kwargs: keyword arguments + Keyword arguments. + + Returns + ------- + output : object + The output of the function call. + metadata : dict + The metadata associated with the call. + """ + call_id = (self.func_id, self._get_args_id(*args, **kwargs)) + + # Return the output and the metadata + return self._call(call_id, args, kwargs) + + def _call(self, call_id, args, kwargs, shelving=False): + # Return the output and the metadata + self._before_call(args, kwargs) + start_time = time.time() + output = self.func(*args, **kwargs) + return self._after_call(call_id, args, kwargs, shelving, output, start_time) + + def _before_call(self, args, kwargs): + if self._verbose > 0: + print(format_call(self.func, args, kwargs)) + + def _after_call(self, call_id, args, kwargs, shelving, output, start_time): + self.store_backend.dump_item(call_id, output, verbose=self._verbose) + duration = time.time() - start_time + if self._verbose > 0: + self._print_duration(duration) + metadata = self._persist_input(duration, call_id, args, kwargs) + if shelving: + return self._get_memorized_result(call_id, metadata), metadata + + if self.mmap_mode is not None: + # Memmap the output at the first call to be consistent with + # later calls + output = self._load_item(call_id, metadata) + return output, metadata + + def _persist_input(self, duration, call_id, args, kwargs, this_duration_limit=0.5): + """Save a small summary of the call using json format in the + output directory. + + output_dir: string + directory where to write metadata. + + duration: float + time taken by hashing input arguments, calling the wrapped + function and persisting its output. + + args, kwargs: list and dict + input arguments for wrapped function + + this_duration_limit: float + Max execution time for this function before issuing a warning. + """ + start_time = time.time() + argument_dict = filter_args(self.func, self.ignore, args, kwargs) + + input_repr = dict((k, repr(v)) for k, v in argument_dict.items()) + # This can fail due to race-conditions with multiple + # concurrent joblibs removing the file or the directory + metadata = { + "duration": duration, + "input_args": input_repr, + "time": start_time, + } + + self.store_backend.store_metadata(call_id, metadata) + + this_duration = time.time() - start_time + if this_duration > this_duration_limit: + # This persistence should be fast. It will not be if repr() takes + # time and its output is large, because json.dump will have to + # write a large file. This should not be an issue with numpy arrays + # for which repr() always output a short representation, but can + # be with complex dictionaries. Fixing the problem should be a + # matter of replacing repr() above by something smarter. + warnings.warn( + "Persisting input arguments took %.2fs to run." + "If this happens often in your code, it can cause " + "performance problems " + "(results will be correct in all cases). " + "The reason for this is probably some large input " + "arguments for a wrapped function." % this_duration, + stacklevel=5, + ) + return metadata + + def _get_memorized_result(self, call_id, metadata=None): + return MemorizedResult( + self.store_backend, + call_id, + metadata=metadata, + timestamp=self.timestamp, + verbose=self._verbose - 1, + ) + + def _load_item(self, call_id, metadata=None): + return self.store_backend.load_item( + call_id, metadata=metadata, timestamp=self.timestamp, verbose=self._verbose + ) + + def _print_duration(self, duration, context=""): + _, name = get_func_name(self.func) + msg = f"{name} {context}- {format_time(duration)}" + print(max(0, (80 - len(msg))) * "_" + msg) + + # ------------------------------------------------------------------------ + # Private `object` interface + # ------------------------------------------------------------------------ + + def __repr__(self): + return "{class_name}(func={func}, location={location})".format( + class_name=self.__class__.__name__, + func=self.func, + location=self.store_backend.location, + ) + + +############################################################################### +# class `AsyncMemorizedFunc` +############################################################################### +class AsyncMemorizedFunc(MemorizedFunc): + async def __call__(self, *args, **kwargs): + out = self._cached_call(args, kwargs, shelving=False) + out = await out if asyncio.iscoroutine(out) else out + return out[0] # Don't return metadata + + async def call_and_shelve(self, *args, **kwargs): + out = self._cached_call(args, kwargs, shelving=True) + out = await out if asyncio.iscoroutine(out) else out + return out[0] # Don't return metadata + + async def call(self, *args, **kwargs): + out = super().call(*args, **kwargs) + return await out if asyncio.iscoroutine(out) else out + + async def _call(self, call_id, args, kwargs, shelving=False): + self._before_call(args, kwargs) + start_time = time.time() + output = await self.func(*args, **kwargs) + return self._after_call(call_id, args, kwargs, shelving, output, start_time) + + +############################################################################### +# class `Memory` +############################################################################### +class Memory(Logger): + """A context object for caching a function's return value each time it + is called with the same input arguments. + + All values are cached on the filesystem, in a deep directory + structure. + + Read more in the :ref:`User Guide `. + + Parameters + ---------- + location: str, pathlib.Path or None + The path of the base directory to use as a data store + or None. If None is given, no caching is done and + the Memory object is completely transparent. This option + replaces cachedir since version 0.12. + + backend: str, optional, default='local' + Type of store backend for reading/writing cache files. + The 'local' backend is using regular filesystem operations to + manipulate data (open, mv, etc) in the backend. + + mmap_mode: {None, 'r+', 'r', 'w+', 'c'}, optional + The memmapping mode used when loading from cache + numpy arrays. See numpy.load for the meaning of the + arguments. + + compress: boolean, or integer, optional + Whether to zip the stored data on disk. If an integer is + given, it should be between 1 and 9, and sets the amount + of compression. Note that compressed arrays cannot be + read by memmapping. + + verbose: int, optional + Verbosity flag, controls the debug messages that are issued + as functions are evaluated. + + backend_options: dict, optional + Contains a dictionary of named parameters used to configure + the store backend. + """ + + # ------------------------------------------------------------------------ + # Public interface + # ------------------------------------------------------------------------ + + def __init__( + self, + location=None, + backend="local", + mmap_mode=None, + compress=False, + verbose=1, + backend_options=None, + ): + Logger.__init__(self) + self._verbose = verbose + self.mmap_mode = mmap_mode + self.timestamp = time.time() + self.backend = backend + self.compress = compress + if backend_options is None: + backend_options = {} + self.backend_options = backend_options + + if compress and mmap_mode is not None: + warnings.warn("Compressed results cannot be memmapped", stacklevel=2) + + self.location = location + if isinstance(location, str): + location = os.path.join(location, "joblib") + + self.store_backend = _store_backend_factory( + backend, + location, + verbose=self._verbose, + backend_options=dict( + compress=compress, mmap_mode=mmap_mode, **backend_options + ), + ) + + def cache( + self, + func=None, + ignore=None, + verbose=None, + mmap_mode=False, + cache_validation_callback=None, + ): + """Decorates the given function func to only compute its return + value for input arguments not cached on disk. + + Parameters + ---------- + func: callable, optional + The function to be decorated + ignore: list of strings + A list of arguments name to ignore in the hashing + verbose: integer, optional + The verbosity mode of the function. By default that + of the memory object is used. + mmap_mode: {None, 'r+', 'r', 'w+', 'c'}, optional + The memmapping mode used when loading from cache + numpy arrays. See numpy.load for the meaning of the + arguments. By default that of the memory object is used. + cache_validation_callback: callable, optional + Callable to validate whether or not the cache is valid. When + the cached function is called with arguments for which a cache + exists, this callable is called with the metadata of the cached + result as its sole argument. If it returns True, then the + cached result is returned, else the cache for these arguments + is cleared and recomputed. + + Returns + ------- + decorated_func: MemorizedFunc object + The returned object is a MemorizedFunc object, that is + callable (behaves like a function), but offers extra + methods for cache lookup and management. See the + documentation for :class:`joblib.memory.MemorizedFunc`. + """ + if cache_validation_callback is not None and not callable( + cache_validation_callback + ): + raise ValueError( + "cache_validation_callback needs to be callable. " + f"Got {cache_validation_callback}." + ) + if func is None: + # Partial application, to be able to specify extra keyword + # arguments in decorators + return functools.partial( + self.cache, + ignore=ignore, + mmap_mode=mmap_mode, + verbose=verbose, + cache_validation_callback=cache_validation_callback, + ) + if self.store_backend is None: + cls = ( + AsyncNotMemorizedFunc + if inspect.iscoroutinefunction(func) + else NotMemorizedFunc + ) + return cls(func) + if verbose is None: + verbose = self._verbose + if mmap_mode is False: + mmap_mode = self.mmap_mode + if isinstance(func, MemorizedFunc): + func = func.func + cls = AsyncMemorizedFunc if inspect.iscoroutinefunction(func) else MemorizedFunc + return cls( + func, + location=self.store_backend, + backend=self.backend, + ignore=ignore, + mmap_mode=mmap_mode, + compress=self.compress, + verbose=verbose, + timestamp=self.timestamp, + cache_validation_callback=cache_validation_callback, + ) + + def clear(self, warn=True): + """Erase the complete cache directory.""" + if warn: + self.warn("Flushing completely the cache") + if self.store_backend is not None: + self.store_backend.clear() + + # As the cache is completely clear, make sure the _FUNCTION_HASHES + # cache is also reset. Else, for a function that is present in this + # table, results cached after this clear will be have cache miss + # as the function code is not re-written. + _FUNCTION_HASHES.clear() + + def reduce_size(self, bytes_limit=None, items_limit=None, age_limit=None): + """Remove cache elements to make the cache fit its limits. + + The limitation can impose that the cache size fits in ``bytes_limit``, + that the number of cache items is no more than ``items_limit``, and + that all files in cache are not older than ``age_limit``. + + Parameters + ---------- + bytes_limit: int | str, optional + Limit in bytes of the size of the cache. By default, the size of + the cache is unlimited. When reducing the size of the cache, + ``joblib`` keeps the most recently accessed items first. If a + str is passed, it is converted to a number of bytes using units + { K | M | G} for kilo, mega, giga. + + items_limit: int, optional + Number of items to limit the cache to. By default, the number of + items in the cache is unlimited. When reducing the size of the + cache, ``joblib`` keeps the most recently accessed items first. + + age_limit: datetime.timedelta, optional + Maximum age of items to limit the cache to. When reducing the size + of the cache, any items last accessed more than the given length of + time ago are deleted. Example: to remove files older than 5 days, + use datetime.timedelta(days=5). Negative timedelta are not + accepted. + """ + if self.store_backend is None: + # No cached results, this function does nothing. + return + + if bytes_limit is None and items_limit is None and age_limit is None: + # No limitation to impose, returning + return + + # Defers the actual limits enforcing to the store backend. + self.store_backend.enforce_store_limits(bytes_limit, items_limit, age_limit) + + def eval(self, func, *args, **kwargs): + """Eval function func with arguments `*args` and `**kwargs`, + in the context of the memory. + + This method works similarly to the builtin `apply`, except + that the function is called only if the cache is not + up to date. + + """ + if self.store_backend is None: + return func(*args, **kwargs) + return self.cache(func)(*args, **kwargs) + + # ------------------------------------------------------------------------ + # Private `object` interface + # ------------------------------------------------------------------------ + + def __repr__(self): + return "{class_name}(location={location})".format( + class_name=self.__class__.__name__, + location=( + None if self.store_backend is None else self.store_backend.location + ), + ) + + def __getstate__(self): + """We don't store the timestamp when pickling, to avoid the hash + depending from it. + """ + state = self.__dict__.copy() + state["timestamp"] = None + return state + + +############################################################################### +# cache_validation_callback helpers +############################################################################### + + +def expires_after( + days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0 +): + """Helper cache_validation_callback to force recompute after a duration. + + Parameters + ---------- + days, seconds, microseconds, milliseconds, minutes, hours, weeks: numbers + argument passed to a timedelta. + """ + delta = datetime.timedelta( + days=days, + seconds=seconds, + microseconds=microseconds, + milliseconds=milliseconds, + minutes=minutes, + hours=hours, + weeks=weeks, + ) + + def cache_validation_callback(metadata): + computation_age = time.time() - metadata["time"] + return computation_age < delta.total_seconds() + + return cache_validation_callback diff --git a/.venv/lib/python3.12/site-packages/joblib/numpy_pickle.py b/.venv/lib/python3.12/site-packages/joblib/numpy_pickle.py new file mode 100644 index 0000000000000000000000000000000000000000..169016d818102f9045f71a67d5f9b40b882f031c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib/numpy_pickle.py @@ -0,0 +1,756 @@ +"""Utilities for fast persistence of big data, with optional compression.""" + +# Author: Gael Varoquaux +# Copyright (c) 2009 Gael Varoquaux +# License: BSD Style, 3 clauses. + +import io +import os +import pickle +import warnings +from pathlib import Path + +from .backports import make_memmap +from .compressor import ( + _COMPRESSORS, + LZ4_NOT_INSTALLED_ERROR, + BinaryZlibFile, + BZ2CompressorWrapper, + GzipCompressorWrapper, + LZ4CompressorWrapper, + LZMACompressorWrapper, + XZCompressorWrapper, + ZlibCompressorWrapper, + lz4, + register_compressor, +) + +# For compatibility with old versions of joblib, we need ZNDArrayWrapper +# to be visible in the current namespace. +from .numpy_pickle_compat import ( + NDArrayWrapper, + ZNDArrayWrapper, # noqa: F401 + load_compatibility, +) +from .numpy_pickle_utils import ( + BUFFER_SIZE, + Pickler, + Unpickler, + _ensure_native_byte_order, + _read_bytes, + _reconstruct, + _validate_fileobject_and_memmap, + _write_fileobject, +) + +# Register supported compressors +register_compressor("zlib", ZlibCompressorWrapper()) +register_compressor("gzip", GzipCompressorWrapper()) +register_compressor("bz2", BZ2CompressorWrapper()) +register_compressor("lzma", LZMACompressorWrapper()) +register_compressor("xz", XZCompressorWrapper()) +register_compressor("lz4", LZ4CompressorWrapper()) + + +############################################################################### +# Utility objects for persistence. + +# For convenience, 16 bytes are used to be sure to cover all the possible +# dtypes' alignments. For reference, see: +# https://numpy.org/devdocs/dev/alignment.html +NUMPY_ARRAY_ALIGNMENT_BYTES = 16 + + +class NumpyArrayWrapper(object): + """An object to be persisted instead of numpy arrays. + + This object is used to hack into the pickle machinery and read numpy + array data from our custom persistence format. + More precisely, this object is used for: + * carrying the information of the persisted array: subclass, shape, order, + dtype. Those ndarray metadata are used to correctly reconstruct the array + with low level numpy functions. + * determining if memmap is allowed on the array. + * reading the array bytes from a file. + * reading the array using memorymap from a file. + * writing the array bytes to a file. + + Attributes + ---------- + subclass: numpy.ndarray subclass + Determine the subclass of the wrapped array. + shape: numpy.ndarray shape + Determine the shape of the wrapped array. + order: {'C', 'F'} + Determine the order of wrapped array data. 'C' is for C order, 'F' is + for fortran order. + dtype: numpy.ndarray dtype + Determine the data type of the wrapped array. + allow_mmap: bool + Determine if memory mapping is allowed on the wrapped array. + Default: False. + """ + + def __init__( + self, + subclass, + shape, + order, + dtype, + allow_mmap=False, + numpy_array_alignment_bytes=NUMPY_ARRAY_ALIGNMENT_BYTES, + ): + """Constructor. Store the useful information for later.""" + self.subclass = subclass + self.shape = shape + self.order = order + self.dtype = dtype + self.allow_mmap = allow_mmap + # We make numpy_array_alignment_bytes an instance attribute to allow us + # to change our mind about the default alignment and still load the old + # pickles (with the previous alignment) correctly + self.numpy_array_alignment_bytes = numpy_array_alignment_bytes + + def safe_get_numpy_array_alignment_bytes(self): + # NumpyArrayWrapper instances loaded from joblib <= 1.1 pickles don't + # have an numpy_array_alignment_bytes attribute + return getattr(self, "numpy_array_alignment_bytes", None) + + def write_array(self, array, pickler): + """Write array bytes to pickler file handle. + + This function is an adaptation of the numpy write_array function + available in version 1.10.1 in numpy/lib/format.py. + """ + # Set buffer size to 16 MiB to hide the Python loop overhead. + buffersize = max(16 * 1024**2 // array.itemsize, 1) + if array.dtype.hasobject: + # We contain Python objects so we cannot write out the data + # directly. Instead, we will pickle it out with version 5 of the + # pickle protocol. + pickle.dump(array, pickler.file_handle, protocol=5) + else: + numpy_array_alignment_bytes = self.safe_get_numpy_array_alignment_bytes() + if numpy_array_alignment_bytes is not None: + current_pos = pickler.file_handle.tell() + pos_after_padding_byte = current_pos + 1 + padding_length = numpy_array_alignment_bytes - ( + pos_after_padding_byte % numpy_array_alignment_bytes + ) + # A single byte is written that contains the padding length in + # bytes + padding_length_byte = int.to_bytes( + padding_length, length=1, byteorder="little" + ) + pickler.file_handle.write(padding_length_byte) + + if padding_length != 0: + padding = b"\xff" * padding_length + pickler.file_handle.write(padding) + + for chunk in pickler.np.nditer( + array, + flags=["external_loop", "buffered", "zerosize_ok"], + buffersize=buffersize, + order=self.order, + ): + pickler.file_handle.write(chunk.tobytes("C")) + + def read_array(self, unpickler, ensure_native_byte_order): + """Read array from unpickler file handle. + + This function is an adaptation of the numpy read_array function + available in version 1.10.1 in numpy/lib/format.py. + """ + if len(self.shape) == 0: + count = 1 + else: + # joblib issue #859: we cast the elements of self.shape to int64 to + # prevent a potential overflow when computing their product. + shape_int64 = [unpickler.np.int64(x) for x in self.shape] + count = unpickler.np.multiply.reduce(shape_int64) + # Now read the actual data. + if self.dtype.hasobject: + # The array contained Python objects. We need to unpickle the data. + array = pickle.load(unpickler.file_handle) + else: + numpy_array_alignment_bytes = self.safe_get_numpy_array_alignment_bytes() + if numpy_array_alignment_bytes is not None: + padding_byte = unpickler.file_handle.read(1) + padding_length = int.from_bytes(padding_byte, byteorder="little") + if padding_length != 0: + unpickler.file_handle.read(padding_length) + + # This is not a real file. We have to read it the + # memory-intensive way. + # crc32 module fails on reads greater than 2 ** 32 bytes, + # breaking large reads from gzip streams. Chunk reads to + # BUFFER_SIZE bytes to avoid issue and reduce memory overhead + # of the read. In non-chunked case count < max_read_count, so + # only one read is performed. + max_read_count = BUFFER_SIZE // min(BUFFER_SIZE, self.dtype.itemsize) + + array = unpickler.np.empty(count, dtype=self.dtype) + for i in range(0, count, max_read_count): + read_count = min(max_read_count, count - i) + read_size = int(read_count * self.dtype.itemsize) + data = _read_bytes(unpickler.file_handle, read_size, "array data") + array[i : i + read_count] = unpickler.np.frombuffer( + data, dtype=self.dtype, count=read_count + ) + del data + + if self.order == "F": + array.shape = self.shape[::-1] + array = array.transpose() + else: + array.shape = self.shape + + if ensure_native_byte_order: + # Detect byte order mismatch and swap as needed. + array = _ensure_native_byte_order(array) + + return array + + def read_mmap(self, unpickler): + """Read an array using numpy memmap.""" + current_pos = unpickler.file_handle.tell() + offset = current_pos + numpy_array_alignment_bytes = self.safe_get_numpy_array_alignment_bytes() + + if numpy_array_alignment_bytes is not None: + padding_byte = unpickler.file_handle.read(1) + padding_length = int.from_bytes(padding_byte, byteorder="little") + # + 1 is for the padding byte + offset += padding_length + 1 + + if unpickler.mmap_mode == "w+": + unpickler.mmap_mode = "r+" + + marray = make_memmap( + unpickler.filename, + dtype=self.dtype, + shape=self.shape, + order=self.order, + mode=unpickler.mmap_mode, + offset=offset, + ) + # update the offset so that it corresponds to the end of the read array + unpickler.file_handle.seek(offset + marray.nbytes) + + if ( + numpy_array_alignment_bytes is None + and current_pos % NUMPY_ARRAY_ALIGNMENT_BYTES != 0 + ): + message = ( + f"The memmapped array {marray} loaded from the file " + f"{unpickler.file_handle.name} is not byte aligned. " + "This may cause segmentation faults if this memmapped array " + "is used in some libraries like BLAS or PyTorch. " + "To get rid of this warning, regenerate your pickle file " + "with joblib >= 1.2.0. " + "See https://github.com/joblib/joblib/issues/563 " + "for more details" + ) + warnings.warn(message) + + return marray + + def read(self, unpickler, ensure_native_byte_order): + """Read the array corresponding to this wrapper. + + Use the unpickler to get all information to correctly read the array. + + Parameters + ---------- + unpickler: NumpyUnpickler + ensure_native_byte_order: bool + If true, coerce the array to use the native endianness of the + host system. + + Returns + ------- + array: numpy.ndarray + + """ + # When requested, only use memmap mode if allowed. + if unpickler.mmap_mode is not None and self.allow_mmap: + assert not ensure_native_byte_order, ( + "Memmaps cannot be coerced to a given byte order, " + "this code path is impossible." + ) + array = self.read_mmap(unpickler) + else: + array = self.read_array(unpickler, ensure_native_byte_order) + + # Manage array subclass case + if hasattr(array, "__array_prepare__") and self.subclass not in ( + unpickler.np.ndarray, + unpickler.np.memmap, + ): + # We need to reconstruct another subclass + new_array = _reconstruct(self.subclass, (0,), "b") + return new_array.__array_prepare__(array) + else: + return array + + +############################################################################### +# Pickler classes + + +class NumpyPickler(Pickler): + """A pickler to persist big data efficiently. + + The main features of this object are: + * persistence of numpy arrays in a single file. + * optional compression with a special care on avoiding memory copies. + + Attributes + ---------- + fp: file + File object handle used for serializing the input object. + protocol: int, optional + Pickle protocol used. Default is pickle.DEFAULT_PROTOCOL. + """ + + dispatch = Pickler.dispatch.copy() + + def __init__(self, fp, protocol=None): + self.file_handle = fp + self.buffered = isinstance(self.file_handle, BinaryZlibFile) + + # By default we want a pickle protocol that only changes with + # the major python version and not the minor one + if protocol is None: + protocol = pickle.DEFAULT_PROTOCOL + + Pickler.__init__(self, self.file_handle, protocol=protocol) + # delayed import of numpy, to avoid tight coupling + try: + import numpy as np + except ImportError: + np = None + self.np = np + + def _create_array_wrapper(self, array): + """Create and returns a numpy array wrapper from a numpy array.""" + order = ( + "F" if (array.flags.f_contiguous and not array.flags.c_contiguous) else "C" + ) + allow_mmap = not self.buffered and not array.dtype.hasobject + + kwargs = {} + try: + self.file_handle.tell() + except io.UnsupportedOperation: + kwargs = {"numpy_array_alignment_bytes": None} + + wrapper = NumpyArrayWrapper( + type(array), + array.shape, + order, + array.dtype, + allow_mmap=allow_mmap, + **kwargs, + ) + + return wrapper + + def save(self, obj): + """Subclass the Pickler `save` method. + + This is a total abuse of the Pickler class in order to use the numpy + persistence function `save` instead of the default pickle + implementation. The numpy array is replaced by a custom wrapper in the + pickle persistence stack and the serialized array is written right + after in the file. Warning: the file produced does not follow the + pickle format. As such it can not be read with `pickle.load`. + """ + if self.np is not None and type(obj) in ( + self.np.ndarray, + self.np.matrix, + self.np.memmap, + ): + if type(obj) is self.np.memmap: + # Pickling doesn't work with memmapped arrays + obj = self.np.asanyarray(obj) + + # The array wrapper is pickled instead of the real array. + wrapper = self._create_array_wrapper(obj) + Pickler.save(self, wrapper) + + # A framer was introduced with pickle protocol 4 and we want to + # ensure the wrapper object is written before the numpy array + # buffer in the pickle file. + # See https://www.python.org/dev/peps/pep-3154/#framing to get + # more information on the framer behavior. + if self.proto >= 4: + self.framer.commit_frame(force=True) + + # And then array bytes are written right after the wrapper. + wrapper.write_array(obj, self) + return + + return Pickler.save(self, obj) + + +class NumpyUnpickler(Unpickler): + """A subclass of the Unpickler to unpickle our numpy pickles. + + Attributes + ---------- + mmap_mode: str + The memorymap mode to use for reading numpy arrays. + file_handle: file_like + File object to unpickle from. + ensure_native_byte_order: bool + If True, coerce the array to use the native endianness of the + host system. + filename: str + Name of the file to unpickle from. It should correspond to file_handle. + This parameter is required when using mmap_mode. + np: module + Reference to numpy module if numpy is installed else None. + + """ + + dispatch = Unpickler.dispatch.copy() + + def __init__(self, filename, file_handle, ensure_native_byte_order, mmap_mode=None): + # The next line is for backward compatibility with pickle generated + # with joblib versions less than 0.10. + self._dirname = os.path.dirname(filename) + + self.mmap_mode = mmap_mode + self.file_handle = file_handle + # filename is required for numpy mmap mode. + self.filename = filename + self.compat_mode = False + self.ensure_native_byte_order = ensure_native_byte_order + Unpickler.__init__(self, self.file_handle) + try: + import numpy as np + except ImportError: + np = None + self.np = np + + def load_build(self): + """Called to set the state of a newly created object. + + We capture it to replace our place-holder objects, NDArrayWrapper or + NumpyArrayWrapper, by the array we are interested in. We + replace them directly in the stack of pickler. + NDArrayWrapper is used for backward compatibility with joblib <= 0.9. + """ + Unpickler.load_build(self) + + # For backward compatibility, we support NDArrayWrapper objects. + if isinstance(self.stack[-1], (NDArrayWrapper, NumpyArrayWrapper)): + if self.np is None: + raise ImportError( + "Trying to unpickle an ndarray, but numpy didn't import correctly" + ) + array_wrapper = self.stack.pop() + # If any NDArrayWrapper is found, we switch to compatibility mode, + # this will be used to raise a DeprecationWarning to the user at + # the end of the unpickling. + if isinstance(array_wrapper, NDArrayWrapper): + self.compat_mode = True + _array_payload = array_wrapper.read(self) + else: + _array_payload = array_wrapper.read(self, self.ensure_native_byte_order) + + self.stack.append(_array_payload) + + # Be careful to register our new method. + dispatch[pickle.BUILD[0]] = load_build + + +############################################################################### +# Utility functions + + +def dump(value, filename, compress=0, protocol=None): + """Persist an arbitrary Python object into one file. + + Read more in the :ref:`User Guide `. + + Parameters + ---------- + value: any Python object + The object to store to disk. + filename: str, pathlib.Path, or file object. + The file object or path of the file in which it is to be stored. + The compression method corresponding to one of the supported filename + extensions ('.z', '.gz', '.bz2', '.xz' or '.lzma') will be used + automatically. + compress: int from 0 to 9 or bool or 2-tuple, optional + Optional compression level for the data. 0 or False is no compression. + Higher value means more compression, but also slower read and + write times. Using a value of 3 is often a good compromise. + See the notes for more details. + If compress is True, the compression level used is 3. + If compress is a 2-tuple, the first element must correspond to a string + between supported compressors (e.g 'zlib', 'gzip', 'bz2', 'lzma' + 'xz'), the second element must be an integer from 0 to 9, corresponding + to the compression level. + protocol: int, optional + Pickle protocol, see pickle.dump documentation for more details. + + Returns + ------- + filenames: list of strings + The list of file names in which the data is stored. If + compress is false, each array is stored in a different file. + + See Also + -------- + joblib.load : corresponding loader + + Notes + ----- + Memmapping on load cannot be used for compressed files. Thus + using compression can significantly slow down loading. In + addition, compressed files take up extra memory during + dump and load. + + """ + + if Path is not None and isinstance(filename, Path): + filename = str(filename) + + is_filename = isinstance(filename, str) + is_fileobj = hasattr(filename, "write") + + compress_method = "zlib" # zlib is the default compression method. + if compress is True: + # By default, if compress is enabled, we want the default compress + # level of the compressor. + compress_level = None + elif isinstance(compress, tuple): + # a 2-tuple was set in compress + if len(compress) != 2: + raise ValueError( + "Compress argument tuple should contain exactly 2 elements: " + "(compress method, compress level), you passed {}".format(compress) + ) + compress_method, compress_level = compress + elif isinstance(compress, str): + compress_method = compress + compress_level = None # Use default compress level + compress = (compress_method, compress_level) + else: + compress_level = compress + + if compress_method == "lz4" and lz4 is None: + raise ValueError(LZ4_NOT_INSTALLED_ERROR) + + if ( + compress_level is not None + and compress_level is not False + and compress_level not in range(10) + ): + # Raising an error if a non valid compress level is given. + raise ValueError( + 'Non valid compress level given: "{}". Possible values are {}.'.format( + compress_level, list(range(10)) + ) + ) + + if compress_method not in _COMPRESSORS: + # Raising an error if an unsupported compression method is given. + raise ValueError( + 'Non valid compression method given: "{}". Possible values are {}.'.format( + compress_method, _COMPRESSORS + ) + ) + + if not is_filename and not is_fileobj: + # People keep inverting arguments, and the resulting error is + # incomprehensible + raise ValueError( + "Second argument should be a filename or a file-like object, " + "%s (type %s) was given." % (filename, type(filename)) + ) + + if is_filename and not isinstance(compress, tuple): + # In case no explicit compression was requested using both compression + # method and level in a tuple and the filename has an explicit + # extension, we select the corresponding compressor. + + # unset the variable to be sure no compression level is set afterwards. + compress_method = None + for name, compressor in _COMPRESSORS.items(): + if filename.endswith(compressor.extension): + compress_method = name + + if compress_method in _COMPRESSORS and compress_level == 0: + # we choose the default compress_level in case it was not given + # as an argument (using compress). + compress_level = None + + if compress_level != 0: + with _write_fileobject( + filename, compress=(compress_method, compress_level) + ) as f: + NumpyPickler(f, protocol=protocol).dump(value) + elif is_filename: + with open(filename, "wb") as f: + NumpyPickler(f, protocol=protocol).dump(value) + else: + NumpyPickler(filename, protocol=protocol).dump(value) + + # If the target container is a file object, nothing is returned. + if is_fileobj: + return + + # For compatibility, the list of created filenames (e.g with one element + # after 0.10.0) is returned by default. + return [filename] + + +def _unpickle(fobj, ensure_native_byte_order, filename="", mmap_mode=None): + """Internal unpickling function.""" + # We are careful to open the file handle early and keep it open to + # avoid race-conditions on renames. + # That said, if data is stored in companion files, which can be + # the case with the old persistence format, moving the directory + # will create a race when joblib tries to access the companion + # files. + unpickler = NumpyUnpickler( + filename, fobj, ensure_native_byte_order, mmap_mode=mmap_mode + ) + obj = None + try: + obj = unpickler.load() + if unpickler.compat_mode: + warnings.warn( + "The file '%s' has been generated with a " + "joblib version less than 0.10. " + "Please regenerate this pickle file." % filename, + DeprecationWarning, + stacklevel=3, + ) + except UnicodeDecodeError as exc: + # More user-friendly error message + new_exc = ValueError( + "You may be trying to read with " + "python 3 a joblib pickle generated with python 2. " + "This feature is not supported by joblib." + ) + new_exc.__cause__ = exc + raise new_exc + return obj + + +def load_temporary_memmap(filename, mmap_mode, unlink_on_gc_collect): + from ._memmapping_reducer import JOBLIB_MMAPS, add_maybe_unlink_finalizer + + with open(filename, "rb") as f: + with _validate_fileobject_and_memmap(f, filename, mmap_mode) as ( + fobj, + validated_mmap_mode, + ): + # Memmap are used for interprocess communication, which should + # keep the objects untouched. We pass `ensure_native_byte_order=False` + # to remain consistent with the loading behavior of non-memmaped arrays + # in workers, where the byte order is preserved. + # Note that we do not implement endianness change for memmaps, as this + # would result in inconsistent behavior. + obj = _unpickle( + fobj, + ensure_native_byte_order=False, + filename=filename, + mmap_mode=validated_mmap_mode, + ) + + JOBLIB_MMAPS.add(obj.filename) + if unlink_on_gc_collect: + add_maybe_unlink_finalizer(obj) + return obj + + +def load(filename, mmap_mode=None, ensure_native_byte_order="auto"): + """Reconstruct a Python object from a file persisted with joblib.dump. + + Read more in the :ref:`User Guide `. + + WARNING: joblib.load relies on the pickle module and can therefore + execute arbitrary Python code. It should therefore never be used + to load files from untrusted sources. + + Parameters + ---------- + filename: str, pathlib.Path, or file object. + The file object or path of the file from which to load the object + mmap_mode: {None, 'r+', 'r', 'w+', 'c'}, optional + If not None, the arrays are memory-mapped from the disk. This + mode has no effect for compressed files. Note that in this + case the reconstructed object might no longer match exactly + the originally pickled object. + ensure_native_byte_order: bool, or 'auto', default=='auto' + If True, ensures that the byte order of the loaded arrays matches the + native byte ordering (or _endianness_) of the host system. This is not + compatible with memory-mapped arrays and using non-null `mmap_mode` + parameter at the same time will raise an error. The default 'auto' + parameter is equivalent to True if `mmap_mode` is None, else False. + + Returns + ------- + result: any Python object + The object stored in the file. + + See Also + -------- + joblib.dump : function to save an object + + Notes + ----- + + This function can load numpy array files saved separately during the + dump. If the mmap_mode argument is given, it is passed to np.load and + arrays are loaded as memmaps. As a consequence, the reconstructed + object might not match the original pickled object. Note that if the + file was saved with compression, the arrays cannot be memmapped. + """ + if ensure_native_byte_order == "auto": + ensure_native_byte_order = mmap_mode is None + + if ensure_native_byte_order and mmap_mode is not None: + raise ValueError( + "Native byte ordering can only be enforced if 'mmap_mode' parameter " + f"is set to None, but got 'mmap_mode={mmap_mode}' instead." + ) + + if Path is not None and isinstance(filename, Path): + filename = str(filename) + + if hasattr(filename, "read"): + fobj = filename + filename = getattr(fobj, "name", "") + with _validate_fileobject_and_memmap(fobj, filename, mmap_mode) as (fobj, _): + obj = _unpickle(fobj, ensure_native_byte_order=ensure_native_byte_order) + else: + with open(filename, "rb") as f: + with _validate_fileobject_and_memmap(f, filename, mmap_mode) as ( + fobj, + validated_mmap_mode, + ): + if isinstance(fobj, str): + # if the returned file object is a string, this means we + # try to load a pickle file generated with an version of + # Joblib so we load it with joblib compatibility function. + return load_compatibility(fobj) + + # A memory-mapped array has to be mapped with the endianness + # it has been written with. Other arrays are coerced to the + # native endianness of the host system. + obj = _unpickle( + fobj, + ensure_native_byte_order=ensure_native_byte_order, + filename=filename, + mmap_mode=validated_mmap_mode, + ) + + return obj diff --git a/.venv/lib/python3.12/site-packages/joblib/numpy_pickle_compat.py b/.venv/lib/python3.12/site-packages/joblib/numpy_pickle_compat.py new file mode 100644 index 0000000000000000000000000000000000000000..5e26c13e2feaa53b62b29e6b6690c0349898189f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib/numpy_pickle_compat.py @@ -0,0 +1,250 @@ +"""Numpy pickle compatibility functions.""" + +import inspect +import os +import pickle +import zlib +from io import BytesIO + +from .numpy_pickle_utils import ( + _ZFILE_PREFIX, + Unpickler, + _ensure_native_byte_order, + _reconstruct, +) + + +def hex_str(an_int): + """Convert an int to an hexadecimal string.""" + return "{:#x}".format(an_int) + + +def asbytes(s): + if isinstance(s, bytes): + return s + return s.encode("latin1") + + +_MAX_LEN = len(hex_str(2**64)) +_CHUNK_SIZE = 64 * 1024 + + +def read_zfile(file_handle): + """Read the z-file and return the content as a string. + + Z-files are raw data compressed with zlib used internally by joblib + for persistence. Backward compatibility is not guaranteed. Do not + use for external purposes. + """ + file_handle.seek(0) + header_length = len(_ZFILE_PREFIX) + _MAX_LEN + length = file_handle.read(header_length) + length = length[len(_ZFILE_PREFIX) :] + length = int(length, 16) + + # With python2 and joblib version <= 0.8.4 compressed pickle header is one + # character wider so we need to ignore an additional space if present. + # Note: the first byte of the zlib data is guaranteed not to be a + # space according to + # https://tools.ietf.org/html/rfc6713#section-2.1 + next_byte = file_handle.read(1) + if next_byte != b" ": + # The zlib compressed data has started and we need to go back + # one byte + file_handle.seek(header_length) + + # We use the known length of the data to tell Zlib the size of the + # buffer to allocate. + data = zlib.decompress(file_handle.read(), 15, length) + assert len(data) == length, ( + "Incorrect data length while decompressing %s." + "The file could be corrupted." % file_handle + ) + return data + + +def write_zfile(file_handle, data, compress=1): + """Write the data in the given file as a Z-file. + + Z-files are raw data compressed with zlib used internally by joblib + for persistence. Backward compatibility is not guaranteed. Do not + use for external purposes. + """ + file_handle.write(_ZFILE_PREFIX) + length = hex_str(len(data)) + # Store the length of the data + file_handle.write(asbytes(length.ljust(_MAX_LEN))) + file_handle.write(zlib.compress(asbytes(data), compress)) + + +############################################################################### +# Utility objects for persistence. + + +class NDArrayWrapper(object): + """An object to be persisted instead of numpy arrays. + + The only thing this object does, is to carry the filename in which + the array has been persisted, and the array subclass. + """ + + def __init__(self, filename, subclass, allow_mmap=True): + """Constructor. Store the useful information for later.""" + self.filename = filename + self.subclass = subclass + self.allow_mmap = allow_mmap + + def read(self, unpickler): + """Reconstruct the array.""" + filename = os.path.join(unpickler._dirname, self.filename) + # Load the array from the disk + # use getattr instead of self.allow_mmap to ensure backward compat + # with NDArrayWrapper instances pickled with joblib < 0.9.0 + allow_mmap = getattr(self, "allow_mmap", True) + kwargs = {} + if allow_mmap: + kwargs["mmap_mode"] = unpickler.mmap_mode + if "allow_pickle" in inspect.signature(unpickler.np.load).parameters: + # Required in numpy 1.16.3 and later to acknowledge the security + # risk. + kwargs["allow_pickle"] = True + array = unpickler.np.load(filename, **kwargs) + + # Detect byte order mismatch and swap as needed. + array = _ensure_native_byte_order(array) + + # Reconstruct subclasses. This does not work with old + # versions of numpy + if hasattr(array, "__array_prepare__") and self.subclass not in ( + unpickler.np.ndarray, + unpickler.np.memmap, + ): + # We need to reconstruct another subclass + new_array = _reconstruct(self.subclass, (0,), "b") + return new_array.__array_prepare__(array) + else: + return array + + +class ZNDArrayWrapper(NDArrayWrapper): + """An object to be persisted instead of numpy arrays. + + This object store the Zfile filename in which + the data array has been persisted, and the meta information to + retrieve it. + The reason that we store the raw buffer data of the array and + the meta information, rather than array representation routine + (tobytes) is that it enables us to use completely the strided + model to avoid memory copies (a and a.T store as fast). In + addition saving the heavy information separately can avoid + creating large temporary buffers when unpickling data with + large arrays. + """ + + def __init__(self, filename, init_args, state): + """Constructor. Store the useful information for later.""" + self.filename = filename + self.state = state + self.init_args = init_args + + def read(self, unpickler): + """Reconstruct the array from the meta-information and the z-file.""" + # Here we a simply reproducing the unpickling mechanism for numpy + # arrays + filename = os.path.join(unpickler._dirname, self.filename) + array = _reconstruct(*self.init_args) + with open(filename, "rb") as f: + data = read_zfile(f) + state = self.state + (data,) + array.__setstate__(state) + return array + + +class ZipNumpyUnpickler(Unpickler): + """A subclass of the Unpickler to unpickle our numpy pickles.""" + + dispatch = Unpickler.dispatch.copy() + + def __init__(self, filename, file_handle, mmap_mode=None): + """Constructor.""" + self._filename = os.path.basename(filename) + self._dirname = os.path.dirname(filename) + self.mmap_mode = mmap_mode + self.file_handle = self._open_pickle(file_handle) + Unpickler.__init__(self, self.file_handle) + try: + import numpy as np + except ImportError: + np = None + self.np = np + + def _open_pickle(self, file_handle): + return BytesIO(read_zfile(file_handle)) + + def load_build(self): + """Set the state of a newly created object. + + We capture it to replace our place-holder objects, + NDArrayWrapper, by the array we are interested in. We + replace them directly in the stack of pickler. + """ + Unpickler.load_build(self) + if isinstance(self.stack[-1], NDArrayWrapper): + if self.np is None: + raise ImportError( + "Trying to unpickle an ndarray, but numpy didn't import correctly" + ) + nd_array_wrapper = self.stack.pop() + array = nd_array_wrapper.read(self) + self.stack.append(array) + + dispatch[pickle.BUILD[0]] = load_build + + +def load_compatibility(filename): + """Reconstruct a Python object from a file persisted with joblib.dump. + + This function ensures the compatibility with joblib old persistence format + (<= 0.9.3). + + Parameters + ---------- + filename: string + The name of the file from which to load the object + + Returns + ------- + result: any Python object + The object stored in the file. + + See Also + -------- + joblib.dump : function to save an object + + Notes + ----- + + This function can load numpy array files saved separately during the + dump. + """ + with open(filename, "rb") as file_handle: + # We are careful to open the file handle early and keep it open to + # avoid race-conditions on renames. That said, if data is stored in + # companion files, moving the directory will create a race when + # joblib tries to access the companion files. + unpickler = ZipNumpyUnpickler(filename, file_handle=file_handle) + try: + obj = unpickler.load() + except UnicodeDecodeError as exc: + # More user-friendly error message + new_exc = ValueError( + "You may be trying to read with " + "python 3 a joblib pickle generated with python 2. " + "This feature is not supported by joblib." + ) + new_exc.__cause__ = exc + raise new_exc + finally: + if hasattr(unpickler, "file_handle"): + unpickler.file_handle.close() + return obj diff --git a/.venv/lib/python3.12/site-packages/joblib/numpy_pickle_utils.py b/.venv/lib/python3.12/site-packages/joblib/numpy_pickle_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..4f7840c78d6e5b36dc5adb8f2a2acbeb40effa1d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib/numpy_pickle_utils.py @@ -0,0 +1,291 @@ +"""Utilities for fast persistence of big data, with optional compression.""" + +# Author: Gael Varoquaux +# Copyright (c) 2009 Gael Varoquaux +# License: BSD Style, 3 clauses. + +import contextlib +import io +import pickle +import sys +import warnings + +from .compressor import _COMPRESSORS, _ZFILE_PREFIX + +try: + import numpy as np +except ImportError: + np = None + +Unpickler = pickle._Unpickler +Pickler = pickle._Pickler +xrange = range + + +try: + # The python standard library can be built without bz2 so we make bz2 + # usage optional. + # see https://github.com/scikit-learn/scikit-learn/issues/7526 for more + # details. + import bz2 +except ImportError: + bz2 = None + +# Buffer size used in io.BufferedReader and io.BufferedWriter +_IO_BUFFER_SIZE = 1024**2 + + +def _is_raw_file(fileobj): + """Check if fileobj is a raw file object, e.g created with open.""" + fileobj = getattr(fileobj, "raw", fileobj) + return isinstance(fileobj, io.FileIO) + + +def _get_prefixes_max_len(): + # Compute the max prefix len of registered compressors. + prefixes = [len(compressor.prefix) for compressor in _COMPRESSORS.values()] + prefixes += [len(_ZFILE_PREFIX)] + return max(prefixes) + + +def _is_numpy_array_byte_order_mismatch(array): + """Check if numpy array is having byte order mismatch""" + return ( + sys.byteorder == "big" + and ( + array.dtype.byteorder == "<" + or ( + array.dtype.byteorder == "|" + and array.dtype.fields + and all(e[0].byteorder == "<" for e in array.dtype.fields.values()) + ) + ) + ) or ( + sys.byteorder == "little" + and ( + array.dtype.byteorder == ">" + or ( + array.dtype.byteorder == "|" + and array.dtype.fields + and all(e[0].byteorder == ">" for e in array.dtype.fields.values()) + ) + ) + ) + + +def _ensure_native_byte_order(array): + """Use the byte order of the host while preserving values + + Does nothing if array already uses the system byte order. + """ + if _is_numpy_array_byte_order_mismatch(array): + array = array.byteswap().view(array.dtype.newbyteorder("=")) + return array + + +############################################################################### +# Cache file utilities +def _detect_compressor(fileobj): + """Return the compressor matching fileobj. + + Parameters + ---------- + fileobj: file object + + Returns + ------- + str in {'zlib', 'gzip', 'bz2', 'lzma', 'xz', 'compat', 'not-compressed'} + """ + # Read the magic number in the first bytes of the file. + max_prefix_len = _get_prefixes_max_len() + if hasattr(fileobj, "peek"): + # Peek allows to read those bytes without moving the cursor in the + # file which. + first_bytes = fileobj.peek(max_prefix_len) + else: + # Fallback to seek if the fileobject is not peekable. + first_bytes = fileobj.read(max_prefix_len) + fileobj.seek(0) + + if first_bytes.startswith(_ZFILE_PREFIX): + return "compat" + else: + for name, compressor in _COMPRESSORS.items(): + if first_bytes.startswith(compressor.prefix): + return name + + return "not-compressed" + + +def _buffered_read_file(fobj): + """Return a buffered version of a read file object.""" + return io.BufferedReader(fobj, buffer_size=_IO_BUFFER_SIZE) + + +def _buffered_write_file(fobj): + """Return a buffered version of a write file object.""" + return io.BufferedWriter(fobj, buffer_size=_IO_BUFFER_SIZE) + + +@contextlib.contextmanager +def _validate_fileobject_and_memmap(fileobj, filename, mmap_mode=None): + """Utility function opening the right fileobject from a filename. + + The magic number is used to choose between the type of file object to open: + * regular file object (default) + * zlib file object + * gzip file object + * bz2 file object + * lzma file object (for xz and lzma compressor) + + Parameters + ---------- + fileobj: file object + filename: str + filename path corresponding to the fileobj parameter. + mmap_mode: str + memory map mode that should be used to open the pickle file. This + parameter is useful to verify that the user is not trying to one with + compression. Default: None. + + Returns + ------- + a tuple with a file like object, and the validated mmap_mode. + + """ + # Detect if the fileobj contains compressed data. + compressor = _detect_compressor(fileobj) + validated_mmap_mode = mmap_mode + + if compressor == "compat": + # Compatibility with old pickle mode: simply return the input + # filename "as-is" and let the compatibility function be called by the + # caller. + warnings.warn( + "The file '%s' has been generated with a joblib " + "version less than 0.10. " + "Please regenerate this pickle file." % filename, + DeprecationWarning, + stacklevel=2, + ) + yield filename, validated_mmap_mode + else: + if compressor in _COMPRESSORS: + # based on the compressor detected in the file, we open the + # correct decompressor file object, wrapped in a buffer. + compressor_wrapper = _COMPRESSORS[compressor] + inst = compressor_wrapper.decompressor_file(fileobj) + fileobj = _buffered_read_file(inst) + + # Checking if incompatible load parameters with the type of file: + # mmap_mode cannot be used with compressed file or in memory buffers + # such as io.BytesIO. + if mmap_mode is not None: + validated_mmap_mode = None + if isinstance(fileobj, io.BytesIO): + warnings.warn( + "In memory persistence is not compatible with " + 'mmap_mode "%(mmap_mode)s" flag passed. ' + "mmap_mode option will be ignored." % locals(), + stacklevel=2, + ) + elif compressor != "not-compressed": + warnings.warn( + 'mmap_mode "%(mmap_mode)s" is not compatible ' + "with compressed file %(filename)s. " + '"%(mmap_mode)s" flag will be ignored.' % locals(), + stacklevel=2, + ) + elif not _is_raw_file(fileobj): + warnings.warn( + '"%(fileobj)r" is not a raw file, mmap_mode ' + '"%(mmap_mode)s" flag will be ignored.' % locals(), + stacklevel=2, + ) + else: + validated_mmap_mode = mmap_mode + + yield fileobj, validated_mmap_mode + + +def _write_fileobject(filename, compress=("zlib", 3)): + """Return the right compressor file object in write mode.""" + compressmethod = compress[0] + compresslevel = compress[1] + + if compressmethod in _COMPRESSORS.keys(): + file_instance = _COMPRESSORS[compressmethod].compressor_file( + filename, compresslevel=compresslevel + ) + return _buffered_write_file(file_instance) + else: + file_instance = _COMPRESSORS["zlib"].compressor_file( + filename, compresslevel=compresslevel + ) + return _buffered_write_file(file_instance) + + +# Utility functions/variables from numpy required for writing arrays. +# We need at least the functions introduced in version 1.9 of numpy. Here, +# we use the ones from numpy 1.10.2. +BUFFER_SIZE = 2**18 # size of buffer for reading npz files in bytes + + +def _read_bytes(fp, size, error_template="ran out of data"): + """Read from file-like object until size bytes are read. + + TODO python2_drop: is it still needed? The docstring mentions python 2.6 + and it looks like this can be at least simplified ... + + Raises ValueError if not EOF is encountered before size bytes are read. + Non-blocking objects only supported if they derive from io objects. + + Required as e.g. ZipExtFile in python 2.6 can return less data than + requested. + + This function was taken from numpy/lib/format.py in version 1.10.2. + + Parameters + ---------- + fp: file-like object + size: int + error_template: str + + Returns + ------- + a bytes object + The data read in bytes. + + """ + data = bytes() + while True: + # io files (default in python3) return None or raise on + # would-block, python2 file will truncate, probably nothing can be + # done about that. note that regular files can't be non-blocking + try: + r = fp.read(size - len(data)) + data += r + if len(r) == 0 or len(data) == size: + break + except io.BlockingIOError: + pass + if len(data) != size: + msg = "EOF: reading %s, expected %d bytes got %d" + raise ValueError(msg % (error_template, size, len(data))) + else: + return data + + +def _reconstruct(*args, **kwargs): + # Wrapper for numpy._core.multiarray._reconstruct with backward compat + # for numpy 1.X + # + # XXX: Remove this function when numpy 1.X is not supported anymore + + np_major_version = np.__version__[:2] + if np_major_version == "1.": + from numpy.core.multiarray import _reconstruct as np_reconstruct + elif np_major_version == "2.": + from numpy._core.multiarray import _reconstruct as np_reconstruct + + return np_reconstruct(*args, **kwargs) diff --git a/.venv/lib/python3.12/site-packages/joblib/parallel.py b/.venv/lib/python3.12/site-packages/joblib/parallel.py new file mode 100644 index 0000000000000000000000000000000000000000..452bd3446fb2d590e7a0b7f812f3454f5ee909a0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib/parallel.py @@ -0,0 +1,2075 @@ +""" +Helpers for embarrassingly parallel code. +""" +# Author: Gael Varoquaux < gael dot varoquaux at normalesup dot org > +# Copyright: 2010, Gael Varoquaux +# License: BSD 3 clause + +from __future__ import division + +import collections +import functools +import itertools +import os +import queue +import sys +import threading +import time +import warnings +import weakref +from contextlib import nullcontext +from math import floor, log10, sqrt +from multiprocessing import TimeoutError +from numbers import Integral +from uuid import uuid4 + +from ._multiprocessing_helpers import mp + +# Make sure that those two classes are part of the public joblib.parallel API +# so that 3rd party backend implementers can import them from here. +from ._parallel_backends import ( + AutoBatchingMixin, # noqa + FallbackToBackend, + LokyBackend, + MultiprocessingBackend, + ParallelBackendBase, # noqa + SequentialBackend, + ThreadingBackend, +) +from ._utils import _Sentinel, eval_expr +from .disk import memstr_to_bytes +from .logger import Logger, short_format_time + +BACKENDS = { + "threading": ThreadingBackend, + "sequential": SequentialBackend, +} +# name of the backend used by default by Parallel outside of any context +# managed by ``parallel_config`` or ``parallel_backend``. + +# threading is the only backend that is always everywhere +DEFAULT_BACKEND = "threading" +DEFAULT_THREAD_BACKEND = "threading" +DEFAULT_PROCESS_BACKEND = "threading" + +MAYBE_AVAILABLE_BACKENDS = {"multiprocessing", "loky"} + +# if multiprocessing is available, so is loky, we set it as the default +# backend +if mp is not None: + BACKENDS["multiprocessing"] = MultiprocessingBackend + from .externals import loky + + BACKENDS["loky"] = LokyBackend + DEFAULT_BACKEND = "loky" + DEFAULT_PROCESS_BACKEND = "loky" + +# Thread local value that can be overridden by the ``parallel_config`` context +# manager +_backend = threading.local() + + +def _register_dask(): + """Register Dask Backend if called with parallel_config(backend="dask")""" + try: + from ._dask import DaskDistributedBackend + + register_parallel_backend("dask", DaskDistributedBackend) + except ImportError as e: + msg = ( + "To use the dask.distributed backend you must install both " + "the `dask` and distributed modules.\n\n" + "See https://dask.pydata.org/en/latest/install.html for more " + "information." + ) + raise ImportError(msg) from e + + +EXTERNAL_BACKENDS = { + "dask": _register_dask, +} + + +# Sentinels for the default values of the Parallel constructor and +# the parallel_config and parallel_backend context managers +default_parallel_config = { + "backend": _Sentinel(default_value=None), + "n_jobs": _Sentinel(default_value=None), + "verbose": _Sentinel(default_value=0), + "temp_folder": _Sentinel(default_value=None), + "max_nbytes": _Sentinel(default_value="1M"), + "mmap_mode": _Sentinel(default_value="r"), + "prefer": _Sentinel(default_value=None), + "require": _Sentinel(default_value=None), +} + + +VALID_BACKEND_HINTS = ("processes", "threads", None) +VALID_BACKEND_CONSTRAINTS = ("sharedmem", None) + + +def _get_config_param(param, context_config, key): + """Return the value of a parallel config parameter + + Explicitly setting it in Parallel has priority over setting in a + parallel_(config/backend) context manager. + """ + if param is not default_parallel_config[key]: + # param is explicitly set, return it + return param + + if context_config[key] is not default_parallel_config[key]: + # there's a context manager and the key is set, return it + return context_config[key] + + # Otherwise, we are in the default_parallel_config, + # return the default value + return param.default_value + + +def get_active_backend( + prefer=default_parallel_config["prefer"], + require=default_parallel_config["require"], + verbose=default_parallel_config["verbose"], +): + """Return the active default backend""" + backend, config = _get_active_backend(prefer, require, verbose) + n_jobs = _get_config_param(default_parallel_config["n_jobs"], config, "n_jobs") + return backend, n_jobs + + +def _get_active_backend( + prefer=default_parallel_config["prefer"], + require=default_parallel_config["require"], + verbose=default_parallel_config["verbose"], +): + """Return the active default backend""" + + backend_config = getattr(_backend, "config", default_parallel_config) + + backend = _get_config_param( + default_parallel_config["backend"], backend_config, "backend" + ) + + prefer = _get_config_param(prefer, backend_config, "prefer") + require = _get_config_param(require, backend_config, "require") + verbose = _get_config_param(verbose, backend_config, "verbose") + + if prefer not in VALID_BACKEND_HINTS: + raise ValueError( + f"prefer={prefer} is not a valid backend hint, " + f"expected one of {VALID_BACKEND_HINTS}" + ) + if require not in VALID_BACKEND_CONSTRAINTS: + raise ValueError( + f"require={require} is not a valid backend constraint, " + f"expected one of {VALID_BACKEND_CONSTRAINTS}" + ) + if prefer == "processes" and require == "sharedmem": + raise ValueError( + "prefer == 'processes' and require == 'sharedmem' are inconsistent settings" + ) + + explicit_backend = True + if backend is None: + # We are either outside of the scope of any parallel_(config/backend) + # context manager or the context manager did not set a backend. + # create the default backend instance now. + backend = BACKENDS[DEFAULT_BACKEND](nesting_level=0) + explicit_backend = False + + # Try to use the backend set by the user with the context manager. + + nesting_level = backend.nesting_level + uses_threads = getattr(backend, "uses_threads", False) + supports_sharedmem = getattr(backend, "supports_sharedmem", False) + # Force to use thread-based backend if the provided backend does not + # match the shared memory constraint or if the backend is not explicitly + # given and threads are preferred. + force_threads = (require == "sharedmem" and not supports_sharedmem) or ( + not explicit_backend and prefer == "threads" and not uses_threads + ) + force_processes = not explicit_backend and prefer == "processes" and uses_threads + + if force_threads: + # This backend does not match the shared memory constraint: + # fallback to the default thead-based backend. + sharedmem_backend = BACKENDS[DEFAULT_THREAD_BACKEND]( + nesting_level=nesting_level + ) + # Warn the user if we forced the backend to thread-based, while the + # user explicitly specified a non-thread-based backend. + if verbose >= 10 and explicit_backend: + print( + f"Using {sharedmem_backend.__class__.__name__} as " + f"joblib backend instead of {backend.__class__.__name__} " + "as the latter does not provide shared memory semantics." + ) + # Force to n_jobs=1 by default + thread_config = backend_config.copy() + thread_config["n_jobs"] = 1 + return sharedmem_backend, thread_config + + if force_processes: + # This backend does not match the prefer="processes" constraint: + # fallback to the default process-based backend. + process_backend = BACKENDS[DEFAULT_PROCESS_BACKEND](nesting_level=nesting_level) + + return process_backend, backend_config.copy() + + return backend, backend_config + + +class parallel_config: + """Set the default backend or configuration for :class:`~joblib.Parallel`. + + This is an alternative to directly passing keyword arguments to the + :class:`~joblib.Parallel` class constructor. It is particularly useful when + calling into library code that uses joblib internally but does not expose + the various parallel configuration arguments in its own API. + + Parameters + ---------- + backend: str or ParallelBackendBase instance, default=None + If ``backend`` is a string it must match a previously registered + implementation using the :func:`~register_parallel_backend` function. + + By default the following backends are available: + + - 'loky': single-host, process-based parallelism (used by default), + - 'threading': single-host, thread-based parallelism, + - 'multiprocessing': legacy single-host, process-based parallelism. + + 'loky' is recommended to run functions that manipulate Python objects. + 'threading' is a low-overhead alternative that is most efficient for + functions that release the Global Interpreter Lock: e.g. I/O-bound + code or CPU-bound code in a few calls to native code that explicitly + releases the GIL. Note that on some rare systems (such as pyodide), + multiprocessing and loky may not be available, in which case joblib + defaults to threading. + + In addition, if the ``dask`` and ``distributed`` Python packages are + installed, it is possible to use the 'dask' backend for better + scheduling of nested parallel calls without over-subscription and + potentially distribute parallel calls over a networked cluster of + several hosts. + + It is also possible to use the distributed 'ray' backend for + distributing the workload to a cluster of nodes. See more details + in the Examples section below. + + Alternatively the backend can be passed directly as an instance. + + n_jobs: int, default=None + The maximum number of concurrently running jobs, such as the number + of Python worker processes when ``backend="loky"`` or the size of the + thread-pool when ``backend="threading"``. + This argument is converted to an integer, rounded below for float. + If -1 is given, `joblib` tries to use all CPUs. The number of CPUs + ``n_cpus`` is obtained with :func:`~cpu_count`. + For n_jobs below -1, (n_cpus + 1 + n_jobs) are used. For instance, + using ``n_jobs=-2`` will result in all CPUs but one being used. + This argument can also go above ``n_cpus``, which will cause + oversubscription. In some cases, slight oversubscription can be + beneficial, e.g., for tasks with large I/O operations. + If 1 is given, no parallel computing code is used at all, and the + behavior amounts to a simple python `for` loop. This mode is not + compatible with `timeout`. + None is a marker for 'unset' that will be interpreted as n_jobs=1 + unless the call is performed under a :func:`~parallel_config` + context manager that sets another value for ``n_jobs``. + If n_jobs = 0 then a ValueError is raised. + + verbose: int, default=0 + The verbosity level: if non zero, progress messages are + printed. Above 50, the output is sent to stdout. + The frequency of the messages increases with the verbosity level. + If it more than 10, all iterations are reported. + + temp_folder: str or None, default=None + Folder to be used by the pool for memmapping large arrays + for sharing memory with worker processes. If None, this will try in + order: + + - a folder pointed by the ``JOBLIB_TEMP_FOLDER`` environment + variable, + - ``/dev/shm`` if the folder exists and is writable: this is a + RAM disk filesystem available by default on modern Linux + distributions, + - the default system temporary folder that can be + overridden with ``TMP``, ``TMPDIR`` or ``TEMP`` environment + variables, typically ``/tmp`` under Unix operating systems. + + max_nbytes: int, str, or None, optional, default='1M' + Threshold on the size of arrays passed to the workers that + triggers automated memory mapping in temp_folder. Can be an int + in Bytes, or a human-readable string, e.g., '1M' for 1 megabyte. + Use None to disable memmapping of large arrays. + + mmap_mode: {None, 'r+', 'r', 'w+', 'c'}, default='r' + Memmapping mode for numpy arrays passed to workers. None will + disable memmapping, other modes defined in the numpy.memmap doc: + https://numpy.org/doc/stable/reference/generated/numpy.memmap.html + Also, see 'max_nbytes' parameter documentation for more details. + + prefer: str in {'processes', 'threads'} or None, default=None + Soft hint to choose the default backend. + The default process-based backend is 'loky' and the default + thread-based backend is 'threading'. Ignored if the ``backend`` + parameter is specified. + + require: 'sharedmem' or None, default=None + Hard constraint to select the backend. If set to 'sharedmem', + the selected backend will be single-host and thread-based. + + inner_max_num_threads: int, default=None + If not None, overwrites the limit set on the number of threads + usable in some third-party library threadpools like OpenBLAS, + MKL or OpenMP. This is only used with the ``loky`` backend. + + backend_params: dict + Additional parameters to pass to the backend constructor when + backend is a string. + + Notes + ----- + Joblib tries to limit the oversubscription by limiting the number of + threads usable in some third-party library threadpools like OpenBLAS, MKL + or OpenMP. The default limit in each worker is set to + ``max(cpu_count() // effective_n_jobs, 1)`` but this limit can be + overwritten with the ``inner_max_num_threads`` argument which will be used + to set this limit in the child processes. + + .. versionadded:: 1.3 + + Examples + -------- + >>> from operator import neg + >>> with parallel_config(backend='threading'): + ... print(Parallel()(delayed(neg)(i + 1) for i in range(5))) + ... + [-1, -2, -3, -4, -5] + + To use the 'ray' joblib backend add the following lines: + + >>> from ray.util.joblib import register_ray # doctest: +SKIP + >>> register_ray() # doctest: +SKIP + >>> with parallel_config(backend="ray"): # doctest: +SKIP + ... print(Parallel()(delayed(neg)(i + 1) for i in range(5))) + [-1, -2, -3, -4, -5] + + """ + + def __init__( + self, + backend=default_parallel_config["backend"], + *, + n_jobs=default_parallel_config["n_jobs"], + verbose=default_parallel_config["verbose"], + temp_folder=default_parallel_config["temp_folder"], + max_nbytes=default_parallel_config["max_nbytes"], + mmap_mode=default_parallel_config["mmap_mode"], + prefer=default_parallel_config["prefer"], + require=default_parallel_config["require"], + inner_max_num_threads=None, + **backend_params, + ): + # Save the parallel info and set the active parallel config + self.old_parallel_config = getattr(_backend, "config", default_parallel_config) + + backend = self._check_backend(backend, inner_max_num_threads, **backend_params) + + new_config = { + "n_jobs": n_jobs, + "verbose": verbose, + "temp_folder": temp_folder, + "max_nbytes": max_nbytes, + "mmap_mode": mmap_mode, + "prefer": prefer, + "require": require, + "backend": backend, + } + self.parallel_config = self.old_parallel_config.copy() + self.parallel_config.update( + {k: v for k, v in new_config.items() if not isinstance(v, _Sentinel)} + ) + + setattr(_backend, "config", self.parallel_config) + + def _check_backend(self, backend, inner_max_num_threads, **backend_params): + if backend is default_parallel_config["backend"]: + if inner_max_num_threads is not None or len(backend_params) > 0: + raise ValueError( + "inner_max_num_threads and other constructor " + "parameters backend_params are only supported " + "when backend is not None." + ) + return backend + + if isinstance(backend, str): + # Handle non-registered or missing backends + if backend not in BACKENDS: + if backend in EXTERNAL_BACKENDS: + register = EXTERNAL_BACKENDS[backend] + register() + elif backend in MAYBE_AVAILABLE_BACKENDS: + warnings.warn( + f"joblib backend '{backend}' is not available on " + f"your system, falling back to {DEFAULT_BACKEND}.", + UserWarning, + stacklevel=2, + ) + BACKENDS[backend] = BACKENDS[DEFAULT_BACKEND] + else: + raise ValueError( + f"Invalid backend: {backend}, expected one of " + f"{sorted(BACKENDS.keys())}" + ) + + backend = BACKENDS[backend](**backend_params) + else: + if len(backend_params) > 0: + raise ValueError( + "Constructor parameters backend_params are only " + "supported when backend is a string." + ) + + if inner_max_num_threads is not None: + msg = ( + f"{backend.__class__.__name__} does not accept setting the " + "inner_max_num_threads argument." + ) + assert backend.supports_inner_max_num_threads, msg + backend.inner_max_num_threads = inner_max_num_threads + + # If the nesting_level of the backend is not set previously, use the + # nesting level from the previous active_backend to set it + if backend.nesting_level is None: + parent_backend = self.old_parallel_config["backend"] + if parent_backend is default_parallel_config["backend"]: + nesting_level = 0 + else: + nesting_level = parent_backend.nesting_level + backend.nesting_level = nesting_level + + return backend + + def __enter__(self): + return self.parallel_config + + def __exit__(self, type, value, traceback): + self.unregister() + + def unregister(self): + setattr(_backend, "config", self.old_parallel_config) + + +class parallel_backend(parallel_config): + """Change the default backend used by Parallel inside a with block. + + .. warning:: + It is advised to use the :class:`~joblib.parallel_config` context + manager instead, which allows more fine-grained control over the + backend configuration. + + If ``backend`` is a string it must match a previously registered + implementation using the :func:`~register_parallel_backend` function. + + By default the following backends are available: + + - 'loky': single-host, process-based parallelism (used by default), + - 'threading': single-host, thread-based parallelism, + - 'multiprocessing': legacy single-host, process-based parallelism. + + 'loky' is recommended to run functions that manipulate Python objects. + 'threading' is a low-overhead alternative that is most efficient for + functions that release the Global Interpreter Lock: e.g. I/O-bound code or + CPU-bound code in a few calls to native code that explicitly releases the + GIL. Note that on some rare systems (such as Pyodide), + multiprocessing and loky may not be available, in which case joblib + defaults to threading. + + You can also use the `Dask `_ joblib + backend to distribute work across machines. This works well with + scikit-learn estimators with the ``n_jobs`` parameter, for example:: + + >>> import joblib # doctest: +SKIP + >>> from sklearn.model_selection import GridSearchCV # doctest: +SKIP + >>> from dask.distributed import Client, LocalCluster # doctest: +SKIP + + >>> # create a local Dask cluster + >>> cluster = LocalCluster() # doctest: +SKIP + >>> client = Client(cluster) # doctest: +SKIP + >>> grid_search = GridSearchCV(estimator, param_grid, n_jobs=-1) + ... # doctest: +SKIP + >>> with joblib.parallel_backend("dask", scatter=[X, y]): # doctest: +SKIP + ... grid_search.fit(X, y) + + It is also possible to use the distributed 'ray' backend for distributing + the workload to a cluster of nodes. To use the 'ray' joblib backend add + the following lines:: + + >>> from ray.util.joblib import register_ray # doctest: +SKIP + >>> register_ray() # doctest: +SKIP + >>> with parallel_backend("ray"): # doctest: +SKIP + ... print(Parallel()(delayed(neg)(i + 1) for i in range(5))) + [-1, -2, -3, -4, -5] + + Alternatively the backend can be passed directly as an instance. + + By default all available workers will be used (``n_jobs=-1``) unless the + caller passes an explicit value for the ``n_jobs`` parameter. + + This is an alternative to passing a ``backend='backend_name'`` argument to + the :class:`~Parallel` class constructor. It is particularly useful when + calling into library code that uses joblib internally but does not expose + the backend argument in its own API. + + >>> from operator import neg + >>> with parallel_backend('threading'): + ... print(Parallel()(delayed(neg)(i + 1) for i in range(5))) + ... + [-1, -2, -3, -4, -5] + + Joblib also tries to limit the oversubscription by limiting the number of + threads usable in some third-party library threadpools like OpenBLAS, MKL + or OpenMP. The default limit in each worker is set to + ``max(cpu_count() // effective_n_jobs, 1)`` but this limit can be + overwritten with the ``inner_max_num_threads`` argument which will be used + to set this limit in the child processes. + + .. versionadded:: 0.10 + + See Also + -------- + joblib.parallel_config: context manager to change the backend configuration. + """ + + def __init__( + self, backend, n_jobs=-1, inner_max_num_threads=None, **backend_params + ): + super().__init__( + backend=backend, + n_jobs=n_jobs, + inner_max_num_threads=inner_max_num_threads, + **backend_params, + ) + + if self.old_parallel_config is None: + self.old_backend_and_jobs = None + else: + self.old_backend_and_jobs = ( + self.old_parallel_config["backend"], + self.old_parallel_config["n_jobs"], + ) + self.new_backend_and_jobs = ( + self.parallel_config["backend"], + self.parallel_config["n_jobs"], + ) + + def __enter__(self): + return self.new_backend_and_jobs + + +# Under Linux or OS X the default start method of multiprocessing +# can cause third party libraries to crash. Under Python 3.4+ it is possible +# to set an environment variable to switch the default start method from +# 'fork' to 'forkserver' or 'spawn' to avoid this issue albeit at the cost +# of causing semantic changes and some additional pool instantiation overhead. +DEFAULT_MP_CONTEXT = None +if hasattr(mp, "get_context"): + method = os.environ.get("JOBLIB_START_METHOD", "").strip() or None + if method is not None: + DEFAULT_MP_CONTEXT = mp.get_context(method=method) + + +class BatchedCalls(object): + """Wrap a sequence of (func, args, kwargs) tuples as a single callable""" + + def __init__( + self, iterator_slice, backend_and_jobs, reducer_callback=None, pickle_cache=None + ): + self.items = list(iterator_slice) + self._size = len(self.items) + self._reducer_callback = reducer_callback + if isinstance(backend_and_jobs, tuple): + self._backend, self._n_jobs = backend_and_jobs + else: + # this is for backward compatibility purposes. Before 0.12.6, + # nested backends were returned without n_jobs indications. + self._backend, self._n_jobs = backend_and_jobs, None + self._pickle_cache = pickle_cache if pickle_cache is not None else {} + + def __call__(self): + # Set the default nested backend to self._backend but do not set the + # change the default number of processes to -1 + with parallel_config(backend=self._backend, n_jobs=self._n_jobs): + return [func(*args, **kwargs) for func, args, kwargs in self.items] + + def __reduce__(self): + if self._reducer_callback is not None: + self._reducer_callback() + # no need to pickle the callback. + return ( + BatchedCalls, + (self.items, (self._backend, self._n_jobs), None, self._pickle_cache), + ) + + def __len__(self): + return self._size + + +# Possible exit status for a task +TASK_DONE = "Done" +TASK_ERROR = "Error" +TASK_PENDING = "Pending" + + +############################################################################### +# CPU count that works also when multiprocessing has been disabled via +# the JOBLIB_MULTIPROCESSING environment variable +def cpu_count(only_physical_cores=False): + """Return the number of CPUs. + + This delegates to loky.cpu_count that takes into account additional + constraints such as Linux CFS scheduler quotas (typically set by container + runtimes such as docker) and CPU affinity (for instance using the taskset + command on Linux). + + Parameters + ---------- + only_physical_cores : boolean, default=False + If True, does not take hyperthreading / SMT logical cores into account. + + """ + if mp is None: + return 1 + + return loky.cpu_count(only_physical_cores=only_physical_cores) + + +############################################################################### +# For verbosity + + +def _verbosity_filter(index, verbose): + """Returns False for indices increasingly apart, the distance + depending on the value of verbose. + + We use a lag increasing as the square of index + """ + if not verbose: + return True + elif verbose > 10: + return False + if index == 0: + return False + verbose = 0.5 * (11 - verbose) ** 2 + scale = sqrt(index / verbose) + next_scale = sqrt((index + 1) / verbose) + return int(next_scale) == int(scale) + + +############################################################################### +def delayed(function): + """Decorator used to capture the arguments of a function.""" + + def delayed_function(*args, **kwargs): + return function, args, kwargs + + try: + delayed_function = functools.wraps(function)(delayed_function) + except AttributeError: + " functools.wraps fails on some callable objects " + return delayed_function + + +############################################################################### +class BatchCompletionCallBack(object): + """Callback to keep track of completed results and schedule the next tasks. + + This callable is executed by the parent process whenever a worker process + has completed a batch of tasks. + + It is used for progress reporting, to update estimate of the batch + processing duration and to schedule the next batch of tasks to be + processed. + + It is assumed that this callback will always be triggered by the backend + right after the end of a task, in case of success as well as in case of + failure. + """ + + ########################################################################## + # METHODS CALLED BY THE MAIN THREAD # + ########################################################################## + def __init__(self, dispatch_timestamp, batch_size, parallel): + self.dispatch_timestamp = dispatch_timestamp + self.batch_size = batch_size + self.parallel = parallel + self.parallel_call_id = parallel._call_id + self._completion_timeout_counter = None + + # Internals to keep track of the status and outcome of the task. + + # Used to hold a reference to the future-like object returned by the + # backend after launching this task + # This will be set later when calling `register_job`, as it is only + # created once the task has been submitted. + self.job = None + + if not parallel._backend.supports_retrieve_callback: + # The status is only used for asynchronous result retrieval in the + # callback. + self.status = None + else: + # The initial status for the job is TASK_PENDING. + # Once it is done, it will be either TASK_DONE, or TASK_ERROR. + self.status = TASK_PENDING + + def register_job(self, job): + """Register the object returned by `submit`.""" + self.job = job + + def get_result(self, timeout): + """Returns the raw result of the task that was submitted. + + If the task raised an exception rather than returning, this same + exception will be raised instead. + + If the backend supports the retrieval callback, it is assumed that this + method is only called after the result has been registered. It is + ensured by checking that `self.status(timeout)` does not return + TASK_PENDING. In this case, `get_result` directly returns the + registered result (or raise the registered exception). + + For other backends, there are no such assumptions, but `get_result` + still needs to synchronously retrieve the result before it can + return it or raise. It will block at most `self.timeout` seconds + waiting for retrieval to complete, after that it raises a TimeoutError. + """ + + backend = self.parallel._backend + + if backend.supports_retrieve_callback: + # We assume that the result has already been retrieved by the + # callback thread, and is stored internally. It's just waiting to + # be returned. + return self._return_or_raise() + + # For other backends, the main thread needs to run the retrieval step. + try: + result = backend.retrieve_result(self.job, timeout=timeout) + outcome = dict(result=result, status=TASK_DONE) + except BaseException as e: + outcome = dict(result=e, status=TASK_ERROR) + self._register_outcome(outcome) + + return self._return_or_raise() + + def _return_or_raise(self): + try: + if self.status == TASK_ERROR: + raise self._result + return self._result + finally: + del self._result + + def get_status(self, timeout): + """Get the status of the task. + + This function also checks if the timeout has been reached and register + the TimeoutError outcome when it is the case. + """ + if timeout is None or self.status != TASK_PENDING: + return self.status + + # The computation are running and the status is pending. + # Check that we did not wait for this jobs more than `timeout`. + now = time.time() + if self._completion_timeout_counter is None: + self._completion_timeout_counter = now + + if (now - self._completion_timeout_counter) > timeout: + outcome = dict(result=TimeoutError(), status=TASK_ERROR) + self._register_outcome(outcome) + + return self.status + + ########################################################################## + # METHODS CALLED BY CALLBACK THREADS # + ########################################################################## + def __call__(self, *args, **kwargs): + """Function called by the callback thread after a job is completed.""" + + # If the backend doesn't support callback retrievals, the next batch of + # tasks is dispatched regardless. The result will be retrieved by the + # main thread when calling `get_result`. + if not self.parallel._backend.supports_retrieve_callback: + self._dispatch_new() + return + + # If the backend supports retrieving the result in the callback, it + # registers the task outcome (TASK_ERROR or TASK_DONE), and schedules + # the next batch if needed. + with self.parallel._lock: + # Edge case where while the task was processing, the `parallel` + # instance has been reset and a new call has been issued, but the + # worker managed to complete the task and trigger this callback + # call just before being aborted by the reset. + if self.parallel._call_id != self.parallel_call_id: + return + + # When aborting, stop as fast as possible and do not retrieve the + # result as it won't be returned by the Parallel call. + if self.parallel._aborting: + return + + # Retrieves the result of the task in the main process and dispatch + # a new batch if needed. + job_succeeded = self._retrieve_result(*args, **kwargs) + + if job_succeeded: + self._dispatch_new() + + def _dispatch_new(self): + """Schedule the next batch of tasks to be processed.""" + + # This steps ensure that auto-batching works as expected. + this_batch_duration = time.time() - self.dispatch_timestamp + self.parallel._backend.batch_completed(self.batch_size, this_batch_duration) + + # Schedule the next batch of tasks. + with self.parallel._lock: + self.parallel.n_completed_tasks += self.batch_size + self.parallel.print_progress() + if self.parallel._original_iterator is not None: + self.parallel.dispatch_next() + + def _retrieve_result(self, out): + """Fetch and register the outcome of a task. + + Return True if the task succeeded, False otherwise. + This function is only called by backends that support retrieving + the task result in the callback thread. + """ + try: + result = self.parallel._backend.retrieve_result_callback(out) + outcome = dict(status=TASK_DONE, result=result) + except BaseException as e: + # Avoid keeping references to parallel in the error. + e.__traceback__ = None + outcome = dict(result=e, status=TASK_ERROR) + + self._register_outcome(outcome) + return outcome["status"] != TASK_ERROR + + ########################################################################## + # This method can be called either in the main thread # + # or in the callback thread. # + ########################################################################## + def _register_outcome(self, outcome): + """Register the outcome of a task. + + This method can be called only once, future calls will be ignored. + """ + # Covers the edge case where the main thread tries to register a + # `TimeoutError` while the callback thread tries to register a result + # at the same time. + with self.parallel._lock: + if self.status not in (TASK_PENDING, None): + return + self.status = outcome["status"] + + self._result = outcome["result"] + + # Once the result and the status are extracted, the last reference to + # the job can be deleted. + self.job = None + + # As soon as an error as been spotted, early stopping flags are sent to + # the `parallel` instance. + if self.status == TASK_ERROR: + self.parallel._exception = True + self.parallel._aborting = True + + if self.parallel.return_ordered: + return + + with self.parallel._lock: + # For `return_as=generator_unordered`, append the job to the queue + # in the order of completion instead of submission. + self.parallel._jobs.append(self) + + +############################################################################### +def register_parallel_backend(name, factory, make_default=False): + """Register a new Parallel backend factory. + + The new backend can then be selected by passing its name as the backend + argument to the :class:`~Parallel` class. Moreover, the default backend can + be overwritten globally by setting make_default=True. + + The factory can be any callable that takes no argument and return an + instance of ``ParallelBackendBase``. + + Warning: this function is experimental and subject to change in a future + version of joblib. + + .. versionadded:: 0.10 + """ + BACKENDS[name] = factory + if make_default: + global DEFAULT_BACKEND + DEFAULT_BACKEND = name + + +def effective_n_jobs(n_jobs=-1): + """Determine the number of jobs that can actually run in parallel + + n_jobs is the number of workers requested by the callers. Passing n_jobs=-1 + means requesting all available workers for instance matching the number of + CPU cores on the worker host(s). + + This method should return a guesstimate of the number of workers that can + actually perform work concurrently with the currently enabled default + backend. The primary use case is to make it possible for the caller to know + in how many chunks to slice the work. + + In general working on larger data chunks is more efficient (less scheduling + overhead and better use of CPU cache prefetching heuristics) as long as all + the workers have enough work to do. + + Warning: this function is experimental and subject to change in a future + version of joblib. + + .. versionadded:: 0.10 + """ + if n_jobs == 1: + return 1 + + backend, backend_n_jobs = get_active_backend() + if n_jobs is None: + n_jobs = backend_n_jobs + return backend.effective_n_jobs(n_jobs=n_jobs) + + +############################################################################### +class Parallel(Logger): + """Helper class for readable parallel mapping. + + Read more in the :ref:`User Guide `. + + Parameters + ---------- + n_jobs: int, default=None + The maximum number of concurrently running jobs, such as the number + of Python worker processes when ``backend="loky"`` or the size of + the thread-pool when ``backend="threading"``. + This argument is converted to an integer, rounded below for float. + If -1 is given, `joblib` tries to use all CPUs. The number of CPUs + ``n_cpus`` is obtained with :func:`~cpu_count`. + For n_jobs below -1, (n_cpus + 1 + n_jobs) are used. For instance, + using ``n_jobs=-2`` will result in all CPUs but one being used. + This argument can also go above ``n_cpus``, which will cause + oversubscription. In some cases, slight oversubscription can be + beneficial, e.g., for tasks with large I/O operations. + If 1 is given, no parallel computing code is used at all, and the + behavior amounts to a simple python `for` loop. This mode is not + compatible with ``timeout``. + None is a marker for 'unset' that will be interpreted as n_jobs=1 + unless the call is performed under a :func:`~parallel_config` + context manager that sets another value for ``n_jobs``. + If n_jobs = 0 then a ValueError is raised. + backend: str, ParallelBackendBase instance or None, default='loky' + Specify the parallelization backend implementation. + Supported backends are: + + - "loky" used by default, can induce some + communication and memory overhead when exchanging input and + output data with the worker Python processes. On some rare + systems (such as Pyiodide), the loky backend may not be + available. + - "multiprocessing" previous process-based backend based on + `multiprocessing.Pool`. Less robust than `loky`. + - "threading" is a very low-overhead backend but it suffers + from the Python Global Interpreter Lock if the called function + relies a lot on Python objects. "threading" is mostly useful + when the execution bottleneck is a compiled extension that + explicitly releases the GIL (for instance a Cython loop wrapped + in a "with nogil" block or an expensive call to a library such + as NumPy). + - finally, you can register backends by calling + :func:`~register_parallel_backend`. This will allow you to + implement a backend of your liking. + + It is not recommended to hard-code the backend name in a call to + :class:`~Parallel` in a library. Instead it is recommended to set + soft hints (prefer) or hard constraints (require) so as to make it + possible for library users to change the backend from the outside + using the :func:`~parallel_config` context manager. + return_as: str in {'list', 'generator', 'generator_unordered'}, default='list' + If 'list', calls to this instance will return a list, only when + all results have been processed and retrieved. + If 'generator', it will return a generator that yields the results + as soon as they are available, in the order the tasks have been + submitted with. + If 'generator_unordered', the generator will immediately yield + available results independently of the submission order. The output + order is not deterministic in this case because it depends on the + concurrency of the workers. + prefer: str in {'processes', 'threads'} or None, default=None + Soft hint to choose the default backend if no specific backend + was selected with the :func:`~parallel_config` context manager. + The default process-based backend is 'loky' and the default + thread-based backend is 'threading'. Ignored if the ``backend`` + parameter is specified. + require: 'sharedmem' or None, default=None + Hard constraint to select the backend. If set to 'sharedmem', + the selected backend will be single-host and thread-based even + if the user asked for a non-thread based backend with + :func:`~joblib.parallel_config`. + verbose: int, default=0 + The verbosity level: if non zero, progress messages are + printed. Above 50, the output is sent to stdout. + The frequency of the messages increases with the verbosity level. + If it more than 10, all iterations are reported. + timeout: float or None, default=None + Timeout limit for each task to complete. If any task takes longer + a TimeOutError will be raised. Only applied when n_jobs != 1 + pre_dispatch: {'all', integer, or expression, as in '3*n_jobs'}, default='2*n_jobs' + The number of batches (of tasks) to be pre-dispatched. + Default is '2*n_jobs'. When batch_size="auto" this is reasonable + default and the workers should never starve. Note that only basic + arithmetic are allowed here and no modules can be used in this + expression. + batch_size: int or 'auto', default='auto' + The number of atomic tasks to dispatch at once to each + worker. When individual evaluations are very fast, dispatching + calls to workers can be slower than sequential computation because + of the overhead. Batching fast computations together can mitigate + this. + The ``'auto'`` strategy keeps track of the time it takes for a + batch to complete, and dynamically adjusts the batch size to keep + the time on the order of half a second, using a heuristic. The + initial batch size is 1. + ``batch_size="auto"`` with ``backend="threading"`` will dispatch + batches of a single task at a time as the threading backend has + very little overhead and using larger batch size has not proved to + bring any gain in that case. + temp_folder: str or None, default=None + Folder to be used by the pool for memmapping large arrays + for sharing memory with worker processes. If None, this will try in + order: + + - a folder pointed by the JOBLIB_TEMP_FOLDER environment + variable, + - /dev/shm if the folder exists and is writable: this is a + RAM disk filesystem available by default on modern Linux + distributions, + - the default system temporary folder that can be + overridden with TMP, TMPDIR or TEMP environment + variables, typically /tmp under Unix operating systems. + + Only active when ``backend="loky"`` or ``"multiprocessing"``. + max_nbytes int, str, or None, optional, default='1M' + Threshold on the size of arrays passed to the workers that + triggers automated memory mapping in temp_folder. Can be an int + in Bytes, or a human-readable string, e.g., '1M' for 1 megabyte. + Use None to disable memmapping of large arrays. + Only active when ``backend="loky"`` or ``"multiprocessing"``. + mmap_mode: {None, 'r+', 'r', 'w+', 'c'}, default='r' + Memmapping mode for numpy arrays passed to workers. None will + disable memmapping, other modes defined in the numpy.memmap doc: + https://numpy.org/doc/stable/reference/generated/numpy.memmap.html + Also, see 'max_nbytes' parameter documentation for more details. + backend_kwargs: dict, optional + Additional parameters to pass to the backend `configure` method. + + Notes + ----- + + This object uses workers to compute in parallel the application of a + function to many different arguments. The main functionality it brings + in addition to using the raw multiprocessing or concurrent.futures API + are (see examples for details): + + * More readable code, in particular since it avoids + constructing list of arguments. + + * Easier debugging: + - informative tracebacks even when the error happens on + the client side + - using 'n_jobs=1' enables to turn off parallel computing + for debugging without changing the codepath + - early capture of pickling errors + + * An optional progress meter. + + * Interruption of multiprocesses jobs with 'Ctrl-C' + + * Flexible pickling control for the communication to and from + the worker processes. + + * Ability to use shared memory efficiently with worker + processes for large numpy-based datastructures. + + Note that the intended usage is to run one call at a time. Multiple + calls to the same Parallel object will result in a ``RuntimeError`` + + Examples + -------- + + A simple example: + + >>> from math import sqrt + >>> from joblib import Parallel, delayed + >>> Parallel(n_jobs=1)(delayed(sqrt)(i**2) for i in range(10)) + [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0] + + Reshaping the output when the function has several return + values: + + >>> from math import modf + >>> from joblib import Parallel, delayed + >>> r = Parallel(n_jobs=1)(delayed(modf)(i/2.) for i in range(10)) + >>> res, i = zip(*r) + >>> res + (0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5) + >>> i + (0.0, 0.0, 1.0, 1.0, 2.0, 2.0, 3.0, 3.0, 4.0, 4.0) + + The progress meter: the higher the value of `verbose`, the more + messages: + + >>> from time import sleep + >>> from joblib import Parallel, delayed + >>> r = Parallel(n_jobs=2, verbose=10)( + ... delayed(sleep)(.2) for _ in range(10)) #doctest: +SKIP + [Parallel(n_jobs=2)]: Done 1 tasks | elapsed: 0.6s + [Parallel(n_jobs=2)]: Done 4 tasks | elapsed: 0.8s + [Parallel(n_jobs=2)]: Done 10 out of 10 | elapsed: 1.4s finished + + Traceback example, note how the line of the error is indicated + as well as the values of the parameter passed to the function that + triggered the exception, even though the traceback happens in the + child process: + + >>> from heapq import nlargest + >>> from joblib import Parallel, delayed + >>> Parallel(n_jobs=2)( + ... delayed(nlargest)(2, n) for n in (range(4), 'abcde', 3)) + ... # doctest: +SKIP + ----------------------------------------------------------------------- + Sub-process traceback: + ----------------------------------------------------------------------- + TypeError Mon Nov 12 11:37:46 2012 + PID: 12934 Python 2.7.3: /usr/bin/python + ........................................................................ + /usr/lib/python2.7/heapq.pyc in nlargest(n=2, iterable=3, key=None) + 419 if n >= size: + 420 return sorted(iterable, key=key, reverse=True)[:n] + 421 + 422 # When key is none, use simpler decoration + 423 if key is None: + --> 424 it = izip(iterable, count(0,-1)) # decorate + 425 result = _nlargest(n, it) + 426 return map(itemgetter(0), result) # undecorate + 427 + 428 # General case, slowest method + TypeError: izip argument #1 must support iteration + _______________________________________________________________________ + + + Using pre_dispatch in a producer/consumer situation, where the + data is generated on the fly. Note how the producer is first + called 3 times before the parallel loop is initiated, and then + called to generate new data on the fly: + + >>> from math import sqrt + >>> from joblib import Parallel, delayed + >>> def producer(): + ... for i in range(6): + ... print('Produced %s' % i) + ... yield i + >>> out = Parallel(n_jobs=2, verbose=100, pre_dispatch='1.5*n_jobs')( + ... delayed(sqrt)(i) for i in producer()) #doctest: +SKIP + Produced 0 + Produced 1 + Produced 2 + [Parallel(n_jobs=2)]: Done 1 jobs | elapsed: 0.0s + Produced 3 + [Parallel(n_jobs=2)]: Done 2 jobs | elapsed: 0.0s + Produced 4 + [Parallel(n_jobs=2)]: Done 3 jobs | elapsed: 0.0s + Produced 5 + [Parallel(n_jobs=2)]: Done 4 jobs | elapsed: 0.0s + [Parallel(n_jobs=2)]: Done 6 out of 6 | elapsed: 0.0s remaining: 0.0s + [Parallel(n_jobs=2)]: Done 6 out of 6 | elapsed: 0.0s finished + + """ # noqa: E501 + + def __init__( + self, + n_jobs=default_parallel_config["n_jobs"], + backend=default_parallel_config["backend"], + return_as="list", + verbose=default_parallel_config["verbose"], + timeout=None, + pre_dispatch="2 * n_jobs", + batch_size="auto", + temp_folder=default_parallel_config["temp_folder"], + max_nbytes=default_parallel_config["max_nbytes"], + mmap_mode=default_parallel_config["mmap_mode"], + prefer=default_parallel_config["prefer"], + require=default_parallel_config["require"], + **backend_kwargs, + ): + # Initiate parent Logger class state + super().__init__() + + # Interpret n_jobs=None as 'unset' + if n_jobs is None: + n_jobs = default_parallel_config["n_jobs"] + + active_backend, context_config = _get_active_backend( + prefer=prefer, require=require, verbose=verbose + ) + + nesting_level = active_backend.nesting_level + + self.verbose = _get_config_param(verbose, context_config, "verbose") + self.timeout = timeout + self.pre_dispatch = pre_dispatch + + if return_as not in {"list", "generator", "generator_unordered"}: + raise ValueError( + 'Expected `return_as` parameter to be a string equal to "list"' + f',"generator" or "generator_unordered", but got {return_as} ' + "instead." + ) + self.return_as = return_as + self.return_generator = return_as != "list" + self.return_ordered = return_as != "generator_unordered" + + # Check if we are under a parallel_config or parallel_backend + # context manager and use the config from the context manager + # for arguments that are not explicitly set. + self._backend_kwargs = { + **backend_kwargs, + **{ + k: _get_config_param(param, context_config, k) + for param, k in [ + (max_nbytes, "max_nbytes"), + (temp_folder, "temp_folder"), + (mmap_mode, "mmap_mode"), + (prefer, "prefer"), + (require, "require"), + (verbose, "verbose"), + ] + }, + } + + if isinstance(self._backend_kwargs["max_nbytes"], str): + self._backend_kwargs["max_nbytes"] = memstr_to_bytes( + self._backend_kwargs["max_nbytes"] + ) + self._backend_kwargs["verbose"] = max(0, self._backend_kwargs["verbose"] - 50) + + if DEFAULT_MP_CONTEXT is not None: + self._backend_kwargs["context"] = DEFAULT_MP_CONTEXT + elif hasattr(mp, "get_context"): + self._backend_kwargs["context"] = mp.get_context() + + if backend is default_parallel_config["backend"] or backend is None: + backend = active_backend + + elif isinstance(backend, ParallelBackendBase): + # Use provided backend as is, with the current nesting_level if it + # is not set yet. + if backend.nesting_level is None: + backend.nesting_level = nesting_level + + elif hasattr(backend, "Pool") and hasattr(backend, "Lock"): + # Make it possible to pass a custom multiprocessing context as + # backend to change the start method to forkserver or spawn or + # preload modules on the forkserver helper process. + self._backend_kwargs["context"] = backend + backend = MultiprocessingBackend(nesting_level=nesting_level) + + elif backend not in BACKENDS and backend in MAYBE_AVAILABLE_BACKENDS: + warnings.warn( + f"joblib backend '{backend}' is not available on " + f"your system, falling back to {DEFAULT_BACKEND}.", + UserWarning, + stacklevel=2, + ) + BACKENDS[backend] = BACKENDS[DEFAULT_BACKEND] + backend = BACKENDS[DEFAULT_BACKEND](nesting_level=nesting_level) + + else: + try: + backend_factory = BACKENDS[backend] + except KeyError as e: + raise ValueError( + "Invalid backend: %s, expected one of %r" + % (backend, sorted(BACKENDS.keys())) + ) from e + backend = backend_factory(nesting_level=nesting_level) + + n_jobs = _get_config_param(n_jobs, context_config, "n_jobs") + if n_jobs is None: + # No specific context override and no specific value request: + # default to the default of the backend. + n_jobs = backend.default_n_jobs + try: + n_jobs = int(n_jobs) + except ValueError: + raise ValueError("n_jobs could not be converted to int") + self.n_jobs = n_jobs + + if require == "sharedmem" and not getattr(backend, "supports_sharedmem", False): + raise ValueError("Backend %s does not support shared memory" % backend) + + if batch_size == "auto" or isinstance(batch_size, Integral) and batch_size > 0: + self.batch_size = batch_size + else: + raise ValueError( + "batch_size must be 'auto' or a positive integer, got: %r" % batch_size + ) + + if not isinstance(backend, SequentialBackend): + if self.return_generator and not backend.supports_return_generator: + raise ValueError( + "Backend {} does not support return_as={}".format( + backend, return_as + ) + ) + # This lock is used to coordinate the main thread of this process + # with the async callback thread of our the pool. + self._lock = threading.RLock() + self._jobs = collections.deque() + self._jobs_set = set() + self._pending_outputs = list() + self._ready_batches = queue.Queue() + self._reducer_callback = None + + # Internal variables + self._backend = backend + self._running = False + self._managed_backend = False + self._id = uuid4().hex + self._call_ref = None + + def __enter__(self): + self._managed_backend = True + self._calling = False + self._initialize_backend() + return self + + def __exit__(self, exc_type, exc_value, traceback): + self._managed_backend = False + if self.return_generator and self._calling: + self._abort() + self._terminate_and_reset() + + def _initialize_backend(self): + """Build a process or thread pool and return the number of workers""" + try: + n_jobs = self._backend.configure( + n_jobs=self.n_jobs, parallel=self, **self._backend_kwargs + ) + if self.timeout is not None and not self._backend.supports_timeout: + warnings.warn( + "The backend class {!r} does not support timeout. " + "You have set 'timeout={}' in Parallel but " + "the 'timeout' parameter will not be used.".format( + self._backend.__class__.__name__, self.timeout + ) + ) + + except FallbackToBackend as e: + # Recursively initialize the backend in case of requested fallback. + self._backend = e.backend + n_jobs = self._initialize_backend() + + return n_jobs + + def _effective_n_jobs(self): + if self._backend: + return self._backend.effective_n_jobs(self.n_jobs) + return 1 + + def _terminate_and_reset(self): + if hasattr(self._backend, "stop_call") and self._calling: + self._backend.stop_call() + self._calling = False + if not self._managed_backend: + self._backend.terminate() + + def _dispatch(self, batch): + """Queue the batch for computing, with or without multiprocessing + + WARNING: this method is not thread-safe: it should be only called + indirectly via dispatch_one_batch. + + """ + # If job.get() catches an exception, it closes the queue: + if self._aborting: + return + + batch_size = len(batch) + + self.n_dispatched_tasks += batch_size + self.n_dispatched_batches += 1 + + dispatch_timestamp = time.time() + + batch_tracker = BatchCompletionCallBack(dispatch_timestamp, batch_size, self) + + self._register_new_job(batch_tracker) + + # If return_ordered is False, the batch_tracker is not stored in the + # jobs queue at the time of submission. Instead, it will be appended to + # the queue by itself as soon as the callback is triggered to be able + # to return the results in the order of completion. + + job = self._backend.submit(batch, callback=batch_tracker) + batch_tracker.register_job(job) + + def _register_new_job(self, batch_tracker): + if self.return_ordered: + self._jobs.append(batch_tracker) + else: + self._jobs_set.add(batch_tracker) + + def dispatch_next(self): + """Dispatch more data for parallel processing + + This method is meant to be called concurrently by the multiprocessing + callback. We rely on the thread-safety of dispatch_one_batch to protect + against concurrent consumption of the unprotected iterator. + """ + if not self.dispatch_one_batch(self._original_iterator): + self._iterating = False + self._original_iterator = None + + def dispatch_one_batch(self, iterator): + """Prefetch the tasks for the next batch and dispatch them. + + The effective size of the batch is computed here. + If there are no more jobs to dispatch, return False, else return True. + + The iterator consumption and dispatching is protected by the same + lock so calling this function should be thread safe. + + """ + + if self._aborting: + return False + + batch_size = self._get_batch_size() + + with self._lock: + # to ensure an even distribution of the workload between workers, + # we look ahead in the original iterators more than batch_size + # tasks - However, we keep consuming only one batch at each + # dispatch_one_batch call. The extra tasks are stored in a local + # queue, _ready_batches, that is looked-up prior to re-consuming + # tasks from the origal iterator. + try: + tasks = self._ready_batches.get(block=False) + except queue.Empty: + # slice the iterator n_jobs * batchsize items at a time. If the + # slice returns less than that, then the current batchsize puts + # too much weight on a subset of workers, while other may end + # up starving. So in this case, re-scale the batch size + # accordingly to distribute evenly the last items between all + # workers. + n_jobs = self._cached_effective_n_jobs + big_batch_size = batch_size * n_jobs + + try: + islice = list(itertools.islice(iterator, big_batch_size)) + except Exception as e: + # Handle the fact that the generator of task raised an + # exception. As this part of the code can be executed in + # a thread internal to the backend, register a task with + # an error that will be raised in the user's thread. + if isinstance(e.__context__, queue.Empty): + # Suppress the cause of the exception if it is + # queue.Empty to avoid cluttered traceback. Only do it + # if the __context__ is really empty to avoid messing + # with causes of the original error. + e.__cause__ = None + batch_tracker = BatchCompletionCallBack(0, batch_size, self) + self._register_new_job(batch_tracker) + batch_tracker._register_outcome(dict(result=e, status=TASK_ERROR)) + return True + + if len(islice) == 0: + return False + elif ( + iterator is self._original_iterator and len(islice) < big_batch_size + ): + # We reached the end of the original iterator (unless + # iterator is the ``pre_dispatch``-long initial slice of + # the original iterator) -- decrease the batch size to + # account for potential variance in the batches running + # time. + final_batch_size = max(1, len(islice) // (10 * n_jobs)) + else: + final_batch_size = max(1, len(islice) // n_jobs) + + # enqueue n_jobs batches in a local queue + for i in range(0, len(islice), final_batch_size): + tasks = BatchedCalls( + islice[i : i + final_batch_size], + self._backend.get_nested_backend(), + self._reducer_callback, + self._pickle_cache, + ) + self._ready_batches.put(tasks) + + # finally, get one task. + tasks = self._ready_batches.get(block=False) + if len(tasks) == 0: + # No more tasks available in the iterator: tell caller to stop. + return False + else: + self._dispatch(tasks) + return True + + def _get_batch_size(self): + """Returns the effective batch size for dispatch""" + if self.batch_size == "auto": + return self._backend.compute_batch_size() + else: + # Fixed batch size strategy + return self.batch_size + + def _print(self, msg): + """Display the message on stout or stderr depending on verbosity""" + # XXX: Not using the logger framework: need to + # learn to use logger better. + if not self.verbose: + return + if self.verbose < 50: + writer = sys.stderr.write + else: + writer = sys.stdout.write + writer(f"[{self}]: {msg}\n") + + def _is_completed(self): + """Check if all tasks have been completed""" + return self.n_completed_tasks == self.n_dispatched_tasks and not ( + self._iterating or self._aborting + ) + + def print_progress(self): + """Display the process of the parallel execution only a fraction + of time, controlled by self.verbose. + """ + + if not self.verbose: + return + + if self.n_tasks is not None and self.n_tasks > 0: + width = floor(log10(self.n_tasks)) + 1 + else: + width = 3 + elapsed_time = time.time() - self._start_time + + if self._is_completed(): + # Make sure that we get a last message telling us we are done + self._print( + f"Done {self.n_completed_tasks:{width}d} out of " + f"{self.n_completed_tasks:{width}d} | elapsed: " + f"{short_format_time(elapsed_time)} finished" + ) + return + + # Original job iterator becomes None once it has been fully + # consumed: at this point we know the total number of jobs and we are + # able to display an estimation of the remaining time based on already + # completed jobs. Otherwise, we simply display the number of completed + # tasks. + elif self._original_iterator is not None: + if _verbosity_filter(self.n_dispatched_batches, self.verbose): + return + fmt_time = f"| elapsed: {short_format_time(elapsed_time)}" + index = self.n_completed_tasks + if self.n_tasks is not None: + self._print( + f"Done {index:{width}d} out of {self.n_tasks:{width}d} {fmt_time}" + ) + else: + pad = " " * (len("out of ") + width - len("tasks")) + self._print(f"Done {index:{width}d} tasks {pad}{fmt_time}") + else: + index = self.n_completed_tasks + # We are finished dispatching + total_tasks = self.n_dispatched_tasks + # We always display the first loop + if index != 0: + # Display depending on the number of remaining items + # A message as soon as we finish dispatching, cursor is 0 + cursor = total_tasks - index + 1 - self._pre_dispatch_amount + frequency = (total_tasks // self.verbose) + 1 + is_last_item = index + 1 == total_tasks + if is_last_item or cursor % frequency: + return + remaining_time = (elapsed_time / max(index, 1)) * ( + self.n_dispatched_tasks - index + ) + # only display status if remaining time is greater or equal to 0 + self._print( + f"Done {index:{width}d} out of {total_tasks:{width}d} " + f"| elapsed: {short_format_time(elapsed_time)} remaining: " + f"{short_format_time(remaining_time)}" + ) + + def _abort(self): + # Stop dispatching new jobs in the async callback thread + self._aborting = True + + # If the backend allows it, cancel or kill remaining running + # tasks without waiting for the results as we will raise + # the exception we got back to the caller instead of returning + # any result. + backend = self._backend + if not self._aborted and hasattr(backend, "abort_everything"): + # If the backend is managed externally we need to make sure + # to leave it in a working state to allow for future jobs + # scheduling. + ensure_ready = self._managed_backend + backend.abort_everything(ensure_ready=ensure_ready) + self._aborted = True + + def _start(self, iterator, pre_dispatch): + # Only set self._iterating to True if at least a batch + # was dispatched. In particular this covers the edge + # case of Parallel used with an exhausted iterator. If + # self._original_iterator is None, then this means either + # that pre_dispatch == "all", n_jobs == 1 or that the first batch + # was very quick and its callback already dispatched all the + # remaining jobs. + self._iterating = False + if self.dispatch_one_batch(iterator): + self._iterating = self._original_iterator is not None + + while self.dispatch_one_batch(iterator): + pass + + if pre_dispatch == "all": + # The iterable was consumed all at once by the above for loop. + # No need to wait for async callbacks to trigger to + # consumption. + self._iterating = False + + def _get_outputs(self, iterator, pre_dispatch): + """Iterator returning the tasks' output as soon as they are ready.""" + dispatch_thread_id = threading.get_ident() + detach_generator_exit = False + try: + self._start(iterator, pre_dispatch) + # first yield returns None, for internal use only. This ensures + # that we enter the try/except block and start dispatching the + # tasks. + yield + + with self._backend.retrieval_context(): + yield from self._retrieve() + + except GeneratorExit: + # The generator has been garbage collected before being fully + # consumed. This aborts the remaining tasks if possible and warn + # the user if necessary. + self._exception = True + + # In some interpreters such as PyPy, GeneratorExit can be raised in + # a different thread than the one used to start the dispatch of the + # parallel tasks. This can lead to hang when a thread attempts to + # join itself. As workaround, we detach the execution of the + # aborting code to a dedicated thread. We then need to make sure + # the rest of the function does not call `_terminate_and_reset` + # in finally. + if dispatch_thread_id != threading.get_ident(): + warnings.warn( + "A generator produced by joblib.Parallel has been " + "gc'ed in an unexpected thread. This behavior should " + "not cause major -issues but to make sure, please " + "report this warning and your use case at " + "https://github.com/joblib/joblib/issues so it can " + "be investigated." + ) + + detach_generator_exit = True + _parallel = self + + class _GeneratorExitThread(threading.Thread): + def run(self): + _parallel._abort() + if _parallel.return_generator: + _parallel._warn_exit_early() + _parallel._terminate_and_reset() + + _GeneratorExitThread(name="GeneratorExitThread").start() + return + + # Otherwise, we are in the thread that started the dispatch: we can + # safely abort the execution and warn the user. + self._abort() + if self.return_generator: + self._warn_exit_early() + + raise + + # Note: we catch any BaseException instead of just Exception instances + # to also include KeyboardInterrupt + except BaseException: + self._exception = True + self._abort() + raise + finally: + # Store the unconsumed tasks and terminate the workers if necessary + _remaining_outputs = [] if self._exception else self._jobs + self._jobs = collections.deque() + self._jobs_set = set() + self._running = False + if not detach_generator_exit: + self._terminate_and_reset() + + while len(_remaining_outputs) > 0: + batched_results = _remaining_outputs.popleft() + batched_results = batched_results.get_result(self.timeout) + for result in batched_results: + yield result + + def _wait_retrieval(self): + """Return True if we need to continue retrieving some tasks.""" + + # If the input load is still being iterated over, it means that tasks + # are still on the dispatch waitlist and their results will need to + # be retrieved later on. + if self._iterating: + return True + + # If some of the dispatched tasks are still being processed by the + # workers, wait for the compute to finish before starting retrieval + if self.n_completed_tasks < self.n_dispatched_tasks: + return True + + # For backends that does not support retrieving asynchronously the + # result to the main process, all results must be carefully retrieved + # in the _retrieve loop in the main thread while the backend is alive. + # For other backends, the actual retrieval is done asynchronously in + # the callback thread, and we can terminate the backend before the + # `self._jobs` result list has been emptied. The remaining results + # will be collected in the `finally` step of the generator. + if not self._backend.supports_retrieve_callback: + if len(self._jobs) > 0: + return True + + return False + + def _retrieve(self): + timeout_control_job = None + while self._wait_retrieval(): + # If the callback thread of a worker has signaled that its task + # triggered an exception, or if the retrieval loop has raised an + # exception (e.g. `GeneratorExit`), exit the loop and surface the + # worker traceback. + if self._aborting: + self._raise_error_fast() + break + + nb_jobs = len(self._jobs) + # Now wait for a job to be ready for retrieval. + if self.return_ordered: + # Case ordered: wait for completion (or error) of the next job + # that have been dispatched and not retrieved yet. If no job + # have been dispatched yet, wait for dispatch. + # We assume that the time to wait for the next job to be + # dispatched is always low, so that the timeout + # control only have to be done on the amount of time the next + # dispatched job is pending. + if (nb_jobs == 0) or ( + self._jobs[0].get_status(timeout=self.timeout) == TASK_PENDING + ): + time.sleep(0.01) + continue + + elif nb_jobs == 0: + # Case unordered: jobs are added to the list of jobs to + # retrieve `self._jobs` only once completed or in error, which + # is too late to enable timeout control in the same way than in + # the previous case. + # Instead, if no job is ready to be retrieved yet, we + # arbitrarily pick a dispatched job, and the timeout control is + # done such that an error is raised if this control job + # timeouts before any other dispatched job has completed and + # been added to `self._jobs` to be retrieved. + if timeout_control_job is None: + timeout_control_job = next(iter(self._jobs_set), None) + + # NB: it can be None if no job has been dispatched yet. + if timeout_control_job is not None: + timeout_control_job.get_status(timeout=self.timeout) + + time.sleep(0.01) + continue + + elif timeout_control_job is not None: + # Case unordered, when `nb_jobs > 0`: + # It means that a job is ready to be retrieved, so no timeout + # will occur during this iteration. + # Before proceeding to retrieval of the next ready job, reset + # the timeout control state to prepare the next iteration. + timeout_control_job._completion_timeout_counter = None + timeout_control_job = None + + # We need to be careful: the job list can be filling up as + # we empty it and Python list are not thread-safe by + # default hence the use of the lock + with self._lock: + batched_results = self._jobs.popleft() + if not self.return_ordered: + self._jobs_set.remove(batched_results) + + # Flatten the batched results to output one output at a time + batched_results = batched_results.get_result(self.timeout) + for result in batched_results: + self._nb_consumed += 1 + yield result + + def _raise_error_fast(self): + """If we are aborting, raise if a job caused an error.""" + + # Find the first job whose status is TASK_ERROR if it exists. + with self._lock: + error_job = next( + (job for job in self._jobs if job.status == TASK_ERROR), None + ) + + # If this error job exists, immediately raise the error by + # calling get_result. This job might not exists if abort has been + # called directly or if the generator is gc'ed. + if error_job is not None: + error_job.get_result(self.timeout) + + def _warn_exit_early(self): + """Warn the user if the generator is gc'ed before being consumned.""" + ready_outputs = self.n_completed_tasks - self._nb_consumed + is_completed = self._is_completed() + msg = "" + if ready_outputs: + msg += ( + f"{ready_outputs} tasks have been successfully executed but not used." + ) + if not is_completed: + msg += " Additionally, " + + if not is_completed: + msg += ( + f"{self.n_dispatched_tasks - self.n_completed_tasks} tasks " + "which were still being processed by the workers have been " + "cancelled." + ) + + if msg: + msg += ( + " You could benefit from adjusting the input task " + "iterator to limit unnecessary computation time." + ) + + warnings.warn(msg) + + def _get_sequential_output(self, iterable): + """Separate loop for sequential output. + + This simplifies the traceback in case of errors and reduces the + overhead of calling sequential tasks with `joblib`. + """ + try: + self._iterating = True + self._original_iterator = iterable + batch_size = self._get_batch_size() + + if batch_size != 1: + it = iter(iterable) + iterable_batched = iter( + lambda: tuple(itertools.islice(it, batch_size)), () + ) + iterable = (task for batch in iterable_batched for task in batch) + + # first yield returns None, for internal use only. This ensures + # that we enter the try/except block and setup the generator. + yield None + + # Sequentially call the tasks and yield the results. + for func, args, kwargs in iterable: + self.n_dispatched_batches += 1 + self.n_dispatched_tasks += 1 + res = func(*args, **kwargs) + self.n_completed_tasks += 1 + self.print_progress() + yield res + self._nb_consumed += 1 + except BaseException: + self._exception = True + self._aborting = True + self._aborted = True + raise + finally: + self._running = False + self._iterating = False + self._original_iterator = None + self.print_progress() + + def _reset_run_tracking(self): + """Reset the counters and flags used to track the execution.""" + + # Makes sur the parallel instance was not previously running in a + # thread-safe way. + with getattr(self, "_lock", nullcontext()): + if self._running: + msg = "This Parallel instance is already running !" + if self.return_generator is True: + msg += ( + " Before submitting new tasks, you must wait for the " + "completion of all the previous tasks, or clean all " + "references to the output generator." + ) + raise RuntimeError(msg) + self._running = True + + # Counter to keep track of the task dispatched and completed. + self.n_dispatched_batches = 0 + self.n_dispatched_tasks = 0 + self.n_completed_tasks = 0 + + # Following count is incremented by one each time the user iterates + # on the output generator, it is used to prepare an informative + # warning message in case the generator is deleted before all the + # dispatched tasks have been consumed. + self._nb_consumed = 0 + + # Following flags are used to synchronize the threads in case one of + # the tasks error-out to ensure that all workers abort fast and that + # the backend terminates properly. + + # Set to True as soon as a worker signals that a task errors-out + self._exception = False + # Set to True in case of early termination following an incident + self._aborting = False + # Set to True after abortion is complete + self._aborted = False + + def __call__(self, iterable): + """Main function to dispatch parallel tasks.""" + + self._reset_run_tracking() + self.n_tasks = len(iterable) if hasattr(iterable, "__len__") else None + self._start_time = time.time() + + if not self._managed_backend: + n_jobs = self._initialize_backend() + else: + n_jobs = self._effective_n_jobs() + + if n_jobs == 1: + # If n_jobs==1, run the computation sequentially and return + # immediately to avoid overheads. + output = self._get_sequential_output(iterable) + next(output) + return output if self.return_generator else list(output) + + # Let's create an ID that uniquely identifies the current call. If the + # call is interrupted early and that the same instance is immediately + # reused, this id will be used to prevent workers that were + # concurrently finalizing a task from the previous call to run the + # callback. + with self._lock: + self._call_id = uuid4().hex + + # self._effective_n_jobs should be called in the Parallel.__call__ + # thread only -- store its value in an attribute for further queries. + self._cached_effective_n_jobs = n_jobs + + if isinstance(self._backend, LokyBackend): + # For the loky backend, we add a callback executed when reducing + # BatchCalls, that makes the loky executor use a temporary folder + # specific to this Parallel object when pickling temporary memmaps. + # This callback is necessary to ensure that several Parallel + # objects using the same reusable executor don't use the same + # temporary resources. + + def _batched_calls_reducer_callback(): + # Relevant implementation detail: the following lines, called + # when reducing BatchedCalls, are called in a thread-safe + # situation, meaning that the context of the temporary folder + # manager will not be changed in between the callback execution + # and the end of the BatchedCalls pickling. The reason is that + # pickling (the only place where set_current_context is used) + # is done from a single thread (the queue_feeder_thread). + self._backend._workers._temp_folder_manager.set_current_context( # noqa + self._id + ) + + self._reducer_callback = _batched_calls_reducer_callback + + # self._effective_n_jobs should be called in the Parallel.__call__ + # thread only -- store its value in an attribute for further queries. + self._cached_effective_n_jobs = n_jobs + + backend_name = self._backend.__class__.__name__ + if n_jobs == 0: + raise RuntimeError("%s has no active worker." % backend_name) + + self._print(f"Using backend {backend_name} with {n_jobs} concurrent workers.") + if hasattr(self._backend, "start_call"): + self._backend.start_call() + + # Following flag prevents double calls to `backend.stop_call`. + self._calling = True + + iterator = iter(iterable) + pre_dispatch = self.pre_dispatch + + if pre_dispatch == "all": + # prevent further dispatch via multiprocessing callback thread + self._original_iterator = None + self._pre_dispatch_amount = 0 + else: + self._original_iterator = iterator + if hasattr(pre_dispatch, "endswith"): + pre_dispatch = eval_expr(pre_dispatch.replace("n_jobs", str(n_jobs))) + self._pre_dispatch_amount = pre_dispatch = int(pre_dispatch) + + # The main thread will consume the first pre_dispatch items and + # the remaining items will later be lazily dispatched by async + # callbacks upon task completions. + + # TODO: this iterator should be batch_size * n_jobs + iterator = itertools.islice(iterator, self._pre_dispatch_amount) + + # Use a caching dict for callables that are pickled with cloudpickle to + # improve performances. This cache is used only in the case of + # functions that are defined in the __main__ module, functions that + # are defined locally (inside another function) and lambda expressions. + self._pickle_cache = dict() + + output = self._get_outputs(iterator, pre_dispatch) + self._call_ref = weakref.ref(output) + + # The first item from the output is blank, but it makes the interpreter + # progress until it enters the Try/Except block of the generator and + # reaches the first `yield` statement. This starts the asynchronous + # dispatch of the tasks to the workers. + next(output) + + return output if self.return_generator else list(output) + + def __repr__(self): + return "%s(n_jobs=%s)" % (self.__class__.__name__, self.n_jobs) diff --git a/.venv/lib/python3.12/site-packages/joblib/pool.py b/.venv/lib/python3.12/site-packages/joblib/pool.py new file mode 100644 index 0000000000000000000000000000000000000000..6e961080703cd29dfbe441417aaeff37944a7118 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib/pool.py @@ -0,0 +1,362 @@ +"""Custom implementation of multiprocessing.Pool with custom pickler. + +This module provides efficient ways of working with data stored in +shared memory with numpy.memmap arrays without inducing any memory +copy between the parent and child processes. + +This module should not be imported if multiprocessing is not +available as it implements subclasses of multiprocessing Pool +that uses a custom alternative to SimpleQueue. + +""" +# Author: Olivier Grisel +# Copyright: 2012, Olivier Grisel +# License: BSD 3 clause + +import copyreg +import sys +import warnings +from time import sleep + +try: + WindowsError +except NameError: + WindowsError = type(None) + +from io import BytesIO + +# We need the class definition to derive from it, not the multiprocessing.Pool +# factory function +from multiprocessing.pool import Pool +from pickle import HIGHEST_PROTOCOL, Pickler + +from ._memmapping_reducer import TemporaryResourcesManager, get_memmapping_reducers +from ._multiprocessing_helpers import assert_spawning, mp + +try: + import numpy as np +except ImportError: + np = None + + +############################################################################### +# Enable custom pickling in Pool queues + + +class CustomizablePickler(Pickler): + """Pickler that accepts custom reducers. + + TODO python2_drop : can this be simplified ? + + HIGHEST_PROTOCOL is selected by default as this pickler is used + to pickle ephemeral datastructures for interprocess communication + hence no backward compatibility is required. + + `reducers` is expected to be a dictionary with key/values + being `(type, callable)` pairs where `callable` is a function that + give an instance of `type` will return a tuple `(constructor, + tuple_of_objects)` to rebuild an instance out of the pickled + `tuple_of_objects` as would return a `__reduce__` method. See the + standard library documentation on pickling for more details. + + """ + + # We override the pure Python pickler as its the only way to be able to + # customize the dispatch table without side effects in Python 2.7 + # to 3.2. For Python 3.3+ leverage the new dispatch_table + # feature from https://bugs.python.org/issue14166 that makes it possible + # to use the C implementation of the Pickler which is faster. + + def __init__(self, writer, reducers=None, protocol=HIGHEST_PROTOCOL): + Pickler.__init__(self, writer, protocol=protocol) + if reducers is None: + reducers = {} + if hasattr(Pickler, "dispatch"): + # Make the dispatch registry an instance level attribute instead of + # a reference to the class dictionary under Python 2 + self.dispatch = Pickler.dispatch.copy() + else: + # Under Python 3 initialize the dispatch table with a copy of the + # default registry + self.dispatch_table = copyreg.dispatch_table.copy() + for type, reduce_func in reducers.items(): + self.register(type, reduce_func) + + def register(self, type, reduce_func): + """Attach a reducer function to a given type in the dispatch table.""" + if hasattr(Pickler, "dispatch"): + # Python 2 pickler dispatching is not explicitly customizable. + # Let us use a closure to workaround this limitation. + def dispatcher(self, obj): + reduced = reduce_func(obj) + self.save_reduce(obj=obj, *reduced) + + self.dispatch[type] = dispatcher + else: + self.dispatch_table[type] = reduce_func + + +class CustomizablePicklingQueue(object): + """Locked Pipe implementation that uses a customizable pickler. + + This class is an alternative to the multiprocessing implementation + of SimpleQueue in order to make it possible to pass custom + pickling reducers, for instance to avoid memory copy when passing + memory mapped datastructures. + + `reducers` is expected to be a dict with key / values being + `(type, callable)` pairs where `callable` is a function that, given an + instance of `type`, will return a tuple `(constructor, tuple_of_objects)` + to rebuild an instance out of the pickled `tuple_of_objects` as would + return a `__reduce__` method. + + See the standard library documentation on pickling for more details. + """ + + def __init__(self, context, reducers=None): + self._reducers = reducers + self._reader, self._writer = context.Pipe(duplex=False) + self._rlock = context.Lock() + if sys.platform == "win32": + self._wlock = None + else: + self._wlock = context.Lock() + self._make_methods() + + def __getstate__(self): + assert_spawning(self) + return (self._reader, self._writer, self._rlock, self._wlock, self._reducers) + + def __setstate__(self, state): + (self._reader, self._writer, self._rlock, self._wlock, self._reducers) = state + self._make_methods() + + def empty(self): + return not self._reader.poll() + + def _make_methods(self): + self._recv = recv = self._reader.recv + racquire, rrelease = self._rlock.acquire, self._rlock.release + + def get(): + racquire() + try: + return recv() + finally: + rrelease() + + self.get = get + + if self._reducers: + + def send(obj): + buffer = BytesIO() + CustomizablePickler(buffer, self._reducers).dump(obj) + self._writer.send_bytes(buffer.getvalue()) + + self._send = send + else: + self._send = send = self._writer.send + if self._wlock is None: + # writes to a message oriented win32 pipe are atomic + self.put = send + else: + wlock_acquire, wlock_release = (self._wlock.acquire, self._wlock.release) + + def put(obj): + wlock_acquire() + try: + return send(obj) + finally: + wlock_release() + + self.put = put + + +class PicklingPool(Pool): + """Pool implementation with customizable pickling reducers. + + This is useful to control how data is shipped between processes + and makes it possible to use shared memory without useless + copies induces by the default pickling methods of the original + objects passed as arguments to dispatch. + + `forward_reducers` and `backward_reducers` are expected to be + dictionaries with key/values being `(type, callable)` pairs where + `callable` is a function that, given an instance of `type`, will return a + tuple `(constructor, tuple_of_objects)` to rebuild an instance out of the + pickled `tuple_of_objects` as would return a `__reduce__` method. + See the standard library documentation about pickling for more details. + + """ + + def __init__( + self, processes=None, forward_reducers=None, backward_reducers=None, **kwargs + ): + if forward_reducers is None: + forward_reducers = dict() + if backward_reducers is None: + backward_reducers = dict() + self._forward_reducers = forward_reducers + self._backward_reducers = backward_reducers + poolargs = dict(processes=processes) + poolargs.update(kwargs) + super(PicklingPool, self).__init__(**poolargs) + + def _setup_queues(self): + context = getattr(self, "_ctx", mp) + self._inqueue = CustomizablePicklingQueue(context, self._forward_reducers) + self._outqueue = CustomizablePicklingQueue(context, self._backward_reducers) + self._quick_put = self._inqueue._send + self._quick_get = self._outqueue._recv + + +class MemmappingPool(PicklingPool): + """Process pool that shares large arrays to avoid memory copy. + + This drop-in replacement for `multiprocessing.pool.Pool` makes + it possible to work efficiently with shared memory in a numpy + context. + + Existing instances of numpy.memmap are preserved: the child + suprocesses will have access to the same shared memory in the + original mode except for the 'w+' mode that is automatically + transformed as 'r+' to avoid zeroing the original data upon + instantiation. + + Furthermore large arrays from the parent process are automatically + dumped to a temporary folder on the filesystem such as child + processes to access their content via memmapping (file system + backed shared memory). + + Note: it is important to call the terminate method to collect + the temporary folder used by the pool. + + Parameters + ---------- + processes: int, optional + Number of worker processes running concurrently in the pool. + initializer: callable, optional + Callable executed on worker process creation. + initargs: tuple, optional + Arguments passed to the initializer callable. + temp_folder: (str, callable) optional + If str: + Folder to be used by the pool for memmapping large arrays + for sharing memory with worker processes. If None, this will try in + order: + - a folder pointed by the JOBLIB_TEMP_FOLDER environment variable, + - /dev/shm if the folder exists and is writable: this is a RAMdisk + filesystem available by default on modern Linux distributions, + - the default system temporary folder that can be overridden + with TMP, TMPDIR or TEMP environment variables, typically /tmp + under Unix operating systems. + if callable: + An callable in charge of dynamically resolving a temporary folder + for memmapping large arrays. + max_nbytes int or None, optional, 1e6 by default + Threshold on the size of arrays passed to the workers that + triggers automated memory mapping in temp_folder. + Use None to disable memmapping of large arrays. + mmap_mode: {'r+', 'r', 'w+', 'c'} + Memmapping mode for numpy arrays passed to workers. + See 'max_nbytes' parameter documentation for more details. + forward_reducers: dictionary, optional + Reducers used to pickle objects passed from main process to worker + processes: see below. + backward_reducers: dictionary, optional + Reducers used to pickle return values from workers back to the + main process. + verbose: int, optional + Make it possible to monitor how the communication of numpy arrays + with the subprocess is handled (pickling or memmapping) + prewarm: bool or str, optional, "auto" by default. + If True, force a read on newly memmapped array to make sure that OS + pre-cache it in memory. This can be useful to avoid concurrent disk + access when the same data array is passed to different worker + processes. If "auto" (by default), prewarm is set to True, unless the + Linux shared memory partition /dev/shm is available and used as temp + folder. + + `forward_reducers` and `backward_reducers` are expected to be + dictionaries with key/values being `(type, callable)` pairs where + `callable` is a function that give an instance of `type` will return + a tuple `(constructor, tuple_of_objects)` to rebuild an instance out + of the pickled `tuple_of_objects` as would return a `__reduce__` + method. See the standard library documentation on pickling for more + details. + + """ + + def __init__( + self, + processes=None, + temp_folder=None, + max_nbytes=1e6, + mmap_mode="r", + forward_reducers=None, + backward_reducers=None, + verbose=0, + prewarm=False, + **kwargs, + ): + manager = TemporaryResourcesManager(temp_folder) + self._temp_folder_manager = manager + + # The usage of a temp_folder_resolver over a simple temp_folder is + # superfluous for multiprocessing pools, as they don't get reused, see + # get_memmapping_executor for more details. We still use it for code + # simplicity. + forward_reducers, backward_reducers = get_memmapping_reducers( + temp_folder_resolver=manager.resolve_temp_folder_name, + max_nbytes=max_nbytes, + mmap_mode=mmap_mode, + forward_reducers=forward_reducers, + backward_reducers=backward_reducers, + verbose=verbose, + unlink_on_gc_collect=False, + prewarm=prewarm, + ) + + poolargs = dict( + processes=processes, + forward_reducers=forward_reducers, + backward_reducers=backward_reducers, + ) + poolargs.update(kwargs) + super(MemmappingPool, self).__init__(**poolargs) + + def terminate(self): + n_retries = 10 + for i in range(n_retries): + try: + super(MemmappingPool, self).terminate() + break + except OSError as e: + if isinstance(e, WindowsError): + # Workaround occasional "[Error 5] Access is denied" issue + # when trying to terminate a process under windows. + sleep(0.1) + if i + 1 == n_retries: + warnings.warn( + "Failed to terminate worker processes in" + " multiprocessing pool: %r" % e + ) + + # Clean up the temporary resources as the workers should now be off. + self._temp_folder_manager._clean_temporary_resources() + + @property + def _temp_folder(self): + # Legacy property in tests. could be removed if we refactored the + # memmapping tests. SHOULD ONLY BE USED IN TESTS! + # We cache this property because it is called late in the tests - at + # this point, all context have been unregistered, and + # resolve_temp_folder_name raises an error. + if getattr(self, "_cached_temp_folder", None) is not None: + return self._cached_temp_folder + else: + self._cached_temp_folder = ( + self._temp_folder_manager.resolve_temp_folder_name() + ) # noqa + return self._cached_temp_folder diff --git a/.venv/lib/python3.12/site-packages/joblib/testing.py b/.venv/lib/python3.12/site-packages/joblib/testing.py new file mode 100644 index 0000000000000000000000000000000000000000..3ac3e7027c7931ddc3f72482cd86ef5fa87cfdf8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/joblib/testing.py @@ -0,0 +1,96 @@ +""" +Helper for testing. +""" + +import os.path +import re +import subprocess +import sys +import threading +import warnings + +import _pytest +import pytest + +raises = pytest.raises +warns = pytest.warns +SkipTest = _pytest.runner.Skipped +skipif = pytest.mark.skipif +fixture = pytest.fixture +parametrize = pytest.mark.parametrize +timeout = pytest.mark.timeout +xfail = pytest.mark.xfail +param = pytest.param + + +def warnings_to_stdout(): + """Redirect all warnings to stdout.""" + showwarning_orig = warnings.showwarning + + def showwarning(msg, cat, fname, lno, file=None, line=0): + showwarning_orig(msg, cat, os.path.basename(fname), line, sys.stdout) + + warnings.showwarning = showwarning + # warnings.simplefilter('always') + + +def check_subprocess_call(cmd, timeout=5, stdout_regex=None, stderr_regex=None): + """Runs a command in a subprocess with timeout in seconds. + + A SIGTERM is sent after `timeout` and if it does not terminate, a + SIGKILL is sent after `2 * timeout`. + + Also checks returncode is zero, stdout if stdout_regex is set, and + stderr if stderr_regex is set. + """ + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + def terminate_process(): # pragma: no cover + """ + Attempt to terminate a leftover process spawned during test execution: + ideally this should not be needed but can help avoid clogging the CI + workers in case of deadlocks. + """ + warnings.warn(f"Timeout running {cmd}") + proc.terminate() + + def kill_process(): # pragma: no cover + """ + Kill a leftover process spawned during test execution: ideally this + should not be needed but can help avoid clogging the CI workers in + case of deadlocks. + """ + warnings.warn(f"Timeout running {cmd}") + proc.kill() + + try: + if timeout is not None: + terminate_timer = threading.Timer(timeout, terminate_process) + terminate_timer.start() + kill_timer = threading.Timer(2 * timeout, kill_process) + kill_timer.start() + stdout, stderr = proc.communicate() + stdout, stderr = stdout.decode(), stderr.decode() + if proc.returncode != 0: + message = ("Non-zero return code: {}.\nStdout:\n{}\nStderr:\n{}").format( + proc.returncode, stdout, stderr + ) + raise ValueError(message) + + if stdout_regex is not None and not re.search(stdout_regex, stdout): + raise ValueError( + "Unexpected stdout: {!r} does not match:\n{!r}".format( + stdout_regex, stdout + ) + ) + if stderr_regex is not None and not re.search(stderr_regex, stderr): + raise ValueError( + "Unexpected stderr: {!r} does not match:\n{!r}".format( + stderr_regex, stderr + ) + ) + + finally: + if timeout is not None: + terminate_timer.cancel() + kill_timer.cancel() diff --git a/.venv/lib/python3.12/site-packages/markupsafe-3.0.3.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/markupsafe-3.0.3.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/markupsafe-3.0.3.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/markupsafe-3.0.3.dist-info/METADATA b/.venv/lib/python3.12/site-packages/markupsafe-3.0.3.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..0282933652fda5b154bd36f91b14ca75fe9e0901 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/markupsafe-3.0.3.dist-info/METADATA @@ -0,0 +1,74 @@ +Metadata-Version: 2.4 +Name: MarkupSafe +Version: 3.0.3 +Summary: Safely add untrusted strings to HTML/XML markup. +Maintainer-email: Pallets +License-Expression: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://markupsafe.palletsprojects.com/ +Project-URL: Changes, https://markupsafe.palletsprojects.com/page/changes/ +Project-URL: Source, https://github.com/pallets/markupsafe/ +Project-URL: Chat, https://discord.gg/pallets +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Classifier: Typing :: Typed +Requires-Python: >=3.9 +Description-Content-Type: text/markdown +License-File: LICENSE.txt +Dynamic: license-file + +
+ +# MarkupSafe + +MarkupSafe implements a text object that escapes characters so it is +safe to use in HTML and XML. Characters that have special meanings are +replaced so that they display as the actual characters. This mitigates +injection attacks, meaning untrusted user input can safely be displayed +on a page. + + +## Examples + +```pycon +>>> from markupsafe import Markup, escape + +>>> # escape replaces special characters and wraps in Markup +>>> escape("") +Markup('<script>alert(document.cookie);</script>') + +>>> # wrap in Markup to mark text "safe" and prevent escaping +>>> Markup("Hello") +Markup('hello') + +>>> escape(Markup("Hello")) +Markup('hello') + +>>> # Markup is a str subclass +>>> # methods and operators escape their arguments +>>> template = Markup("Hello {name}") +>>> template.format(name='"World"') +Markup('Hello "World"') +``` + +## Donate + +The Pallets organization develops and supports MarkupSafe and other +popular packages. In order to grow the community of contributors and +users, and allow the maintainers to devote more time to the projects, +[please donate today][]. + +[please donate today]: https://palletsprojects.com/donate + +## 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/markupsafe-3.0.3.dist-info/RECORD b/.venv/lib/python3.12/site-packages/markupsafe-3.0.3.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..198397730bd815bd4e9835d07e36a9b9c4e2b228 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/markupsafe-3.0.3.dist-info/RECORD @@ -0,0 +1,13 @@ +markupsafe-3.0.3.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +markupsafe-3.0.3.dist-info/METADATA,sha256=ErTMYaf6KIz3Zn7j8hN4bZYZ21f7M_9vk0r7y1wS7IE,2690 +markupsafe-3.0.3.dist-info/RECORD,, +markupsafe-3.0.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +markupsafe-3.0.3.dist-info/WHEEL,sha256=DxRnWQz-Kp9-4a4hdDHsSv0KUC3H7sN9Nbef3-8RjXU,190 +markupsafe-3.0.3.dist-info/licenses/LICENSE.txt,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +markupsafe-3.0.3.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11 +markupsafe/__init__.py,sha256=u1fLcNCx0P8E7LT6z3OXf6ipnSmgm6aefne5y-d7o40,13248 +markupsafe/_native.py,sha256=hSLs8Jmz5aqayuengJJ3kdT5PwNpBWpKrmQSdipndC8,210 +markupsafe/_speedups.c,sha256=t3tC6oVV7-bmKUqvCO5pVSky-G8ACIXpWMaJwkNtJjg,4327 +markupsafe/_speedups.cpython-312-x86_64-linux-gnu.so,sha256=9KMd4HHFZZWQAtspvzbG-NNa55KfZHAK3uukb6T5Da8,44072 +markupsafe/_speedups.pyi,sha256=ENd1bYe7gbBUf2ywyYWOGUpnXOHNJ-cgTNqetlW8h5k,41 +markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.venv/lib/python3.12/site-packages/markupsafe-3.0.3.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/markupsafe-3.0.3.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/markupsafe-3.0.3.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/markupsafe-3.0.3.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..f3e8a970f16adf4526f1722547053522d94bf860 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/markupsafe-3.0.3.dist-info/WHEEL @@ -0,0 +1,7 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: false +Tag: cp312-cp312-manylinux_2_17_x86_64 +Tag: cp312-cp312-manylinux2014_x86_64 +Tag: cp312-cp312-manylinux_2_28_x86_64 + diff --git a/.venv/lib/python3.12/site-packages/markupsafe-3.0.3.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/markupsafe-3.0.3.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..75bf729258f9daef77370b6df1a57940f90fc23f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/markupsafe-3.0.3.dist-info/top_level.txt @@ -0,0 +1 @@ +markupsafe diff --git a/.venv/lib/python3.12/site-packages/networkx-3.5.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/networkx-3.5.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/networkx-3.5.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/networkx-3.5.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/networkx-3.5.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..4d07dfe2f85d6849d7f416dcce756b2501ba847e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/networkx-3.5.dist-info/top_level.txt @@ -0,0 +1 @@ +networkx diff --git a/.venv/lib/python3.12/site-packages/networkx/__init__.py b/.venv/lib/python3.12/site-packages/networkx/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..bed45becc8435e8c2cad9627847e91f806e5e038 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/networkx/__init__.py @@ -0,0 +1,53 @@ +""" +NetworkX +======== + +NetworkX is a Python package for the creation, manipulation, and study of the +structure, dynamics, and functions of complex networks. + +See https://networkx.org for complete documentation. +""" + +__version__ = "3.5" + + +# These are imported in order as listed +from networkx.lazy_imports import _lazy_import + +from networkx.exception import * + +from networkx import utils +from networkx.utils import _clear_cache, _dispatchable + +# load_and_call entry_points, set configs +config = utils.backends._set_configs_from_environment() +utils.config = utils.configs.config = config # type: ignore[attr-defined] + +from networkx import classes +from networkx.classes import filters +from networkx.classes import * + +from networkx import convert +from networkx.convert import * + +from networkx import convert_matrix +from networkx.convert_matrix import * + +from networkx import relabel +from networkx.relabel import * + +from networkx import generators +from networkx.generators import * + +from networkx import readwrite +from networkx.readwrite import * + +# Need to test with SciPy, when available +from networkx import algorithms +from networkx.algorithms import * + +from networkx import linalg +from networkx.linalg import * + +from networkx import drawing +from networkx.drawing import * diff --git a/.venv/lib/python3.12/site-packages/networkx/conftest.py b/.venv/lib/python3.12/site-packages/networkx/conftest.py new file mode 100644 index 0000000000000000000000000000000000000000..9f239227f808b36d842a59958cf583174eb30f02 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/networkx/conftest.py @@ -0,0 +1,257 @@ +""" +Testing +======= + +General guidelines for writing good tests: + +- doctests always assume ``import networkx as nx`` so don't add that +- prefer pytest fixtures over classes with setup methods. +- use the ``@pytest.mark.parametrize`` decorator +- use ``pytest.importorskip`` for numpy, scipy, pandas, and matplotlib b/c of PyPy. + and add the module to the relevant entries below. + +""" + +import os +import warnings +from importlib.metadata import entry_points + +import pytest + +import networkx as nx + + +def pytest_addoption(parser): + parser.addoption( + "--runslow", action="store_true", default=False, help="run slow tests" + ) + parser.addoption( + "--backend", + action="store", + default=None, + help="Run tests with a backend by auto-converting nx graphs to backend graphs", + ) + parser.addoption( + "--fallback-to-nx", + action="store_true", + default=False, + help="Run nx function if a backend doesn't implement a dispatchable function" + " (use with --backend)", + ) + + +def pytest_configure(config): + config.addinivalue_line("markers", "slow: mark test as slow to run") + backend = config.getoption("--backend") + if backend is None: + backend = os.environ.get("NETWORKX_TEST_BACKEND") + # nx_loopback backend is only available when testing with a backend + loopback_ep = entry_points(name="nx_loopback", group="networkx.backends") + if not loopback_ep: + warnings.warn( + "\n\n WARNING: Mixed NetworkX configuration! \n\n" + " This environment has mixed configuration for networkx.\n" + " The test object nx_loopback is not configured correctly.\n" + " You should not be seeing this message.\n" + " Try `pip install -e .`, or change your PYTHONPATH\n" + " Make sure python finds the networkx repo you are testing\n\n" + ) + config.backend = backend + if backend: + # We will update `networkx.config.backend_priority` below in `*_modify_items` + # to allow tests to get set up with normal networkx graphs. + nx.utils.backends.backends["nx_loopback"] = loopback_ep["nx_loopback"] + nx.utils.backends.backend_info["nx_loopback"] = {} + nx.config.backends = nx.utils.Config( + nx_loopback=nx.utils.Config(), + **nx.config.backends, + ) + fallback_to_nx = config.getoption("--fallback-to-nx") + if not fallback_to_nx: + fallback_to_nx = os.environ.get("NETWORKX_FALLBACK_TO_NX") + nx.config.fallback_to_nx = bool(fallback_to_nx) + nx.utils.backends._dispatchable.__call__ = ( + nx.utils.backends._dispatchable._call_if_any_backends_installed + ) + + +def pytest_collection_modifyitems(config, items): + # Setting this to True here allows tests to be set up before dispatching + # any function call to a backend. + if config.backend: + # Allow pluggable backends to add markers to tests (such as skip or xfail) + # when running in auto-conversion test mode + backend_name = config.backend + if backend_name != "networkx": + nx.utils.backends._dispatchable._is_testing = True + nx.config.backend_priority.algos = [backend_name] + nx.config.backend_priority.generators = [backend_name] + backend = nx.utils.backends.backends[backend_name].load() + if hasattr(backend, "on_start_tests"): + getattr(backend, "on_start_tests")(items) + + if config.getoption("--runslow"): + # --runslow given in cli: do not skip slow tests + return + skip_slow = pytest.mark.skip(reason="need --runslow option to run") + for item in items: + if "slow" in item.keywords: + item.add_marker(skip_slow) + + +# TODO: The warnings below need to be dealt with, but for now we silence them. +@pytest.fixture(autouse=True) +def set_warnings(): + warnings.filterwarnings( + "ignore", + category=UserWarning, + message=r"Exited (at iteration \d+|postprocessing) with accuracies.*", + ) + warnings.filterwarnings( + "ignore", category=DeprecationWarning, message="\n\nThe `normalized`" + ) + warnings.filterwarnings( + "ignore", category=DeprecationWarning, message="\n\n`compute_v_structures" + ) + warnings.filterwarnings( + "ignore", category=DeprecationWarning, message="Keyword argument 'link'" + ) + + +@pytest.fixture(autouse=True) +def add_nx(doctest_namespace): + doctest_namespace["nx"] = nx + + +# What dependencies are installed? + +try: + import numpy as np + + has_numpy = True +except ImportError: + has_numpy = False + +try: + import scipy as sp + + has_scipy = True +except ImportError: + has_scipy = False + +try: + import matplotlib as mpl + + has_matplotlib = True +except ImportError: + has_matplotlib = False + +try: + import pandas as pd + + has_pandas = True +except ImportError: + has_pandas = False + +try: + import pygraphviz + + has_pygraphviz = True +except ImportError: + has_pygraphviz = False + +try: + import pydot + + has_pydot = True +except ImportError: + has_pydot = False + +try: + import sympy + + has_sympy = True +except ImportError: + has_sympy = False + + +# List of files that pytest should ignore + +collect_ignore = [] + +needs_numpy = [ + "algorithms/approximation/traveling_salesman.py", + "algorithms/centrality/current_flow_closeness.py", + "algorithms/centrality/laplacian.py", + "algorithms/node_classification.py", + "algorithms/non_randomness.py", + "algorithms/polynomials.py", + "algorithms/shortest_paths/dense.py", + "algorithms/structuralholes.py", + "algorithms/tree/mst.py", + "drawing/nx_latex.py", + "generators/expanders.py", + "linalg/bethehessianmatrix.py", + "linalg/laplacianmatrix.py", + "utils/misc.py", +] +needs_scipy = [ + "algorithms/approximation/traveling_salesman.py", + "algorithms/assortativity/correlation.py", + "algorithms/assortativity/mixing.py", + "algorithms/assortativity/pairs.py", + "algorithms/bipartite/matrix.py", + "algorithms/bipartite/spectral.py", + "algorithms/bipartite/link_analysis.py", + "algorithms/centrality/current_flow_betweenness.py", + "algorithms/centrality/current_flow_betweenness_subset.py", + "algorithms/centrality/eigenvector.py", + "algorithms/centrality/katz.py", + "algorithms/centrality/laplacian.py", + "algorithms/centrality/second_order.py", + "algorithms/centrality/subgraph_alg.py", + "algorithms/communicability_alg.py", + "algorithms/community/divisive.py", + "algorithms/distance_measures.py", + "algorithms/link_analysis/hits_alg.py", + "algorithms/link_analysis/pagerank_alg.py", + "algorithms/node_classification.py", + "algorithms/similarity.py", + "algorithms/structuralholes.py", + "algorithms/tree/mst.py", + "algorithms/walks.py", + "convert_matrix.py", + "drawing/layout.py", + "drawing/nx_pylab.py", + "generators/spectral_graph_forge.py", + "generators/geometric.py", + "generators/expanders.py", + "linalg/algebraicconnectivity.py", + "linalg/attrmatrix.py", + "linalg/bethehessianmatrix.py", + "linalg/graphmatrix.py", + "linalg/laplacianmatrix.py", + "linalg/modularitymatrix.py", + "linalg/spectrum.py", + "utils/rcm.py", +] +needs_matplotlib = ["drawing/nx_pylab.py", "generators/classic.py"] +needs_pandas = ["convert_matrix.py"] +needs_pygraphviz = ["drawing/nx_agraph.py"] +needs_pydot = ["drawing/nx_pydot.py"] +needs_sympy = ["algorithms/polynomials.py"] + +if not has_numpy: + collect_ignore += needs_numpy +if not has_scipy: + collect_ignore += needs_scipy +if not has_matplotlib: + collect_ignore += needs_matplotlib +if not has_pandas: + collect_ignore += needs_pandas +if not has_pygraphviz: + collect_ignore += needs_pygraphviz +if not has_pydot: + collect_ignore += needs_pydot +if not has_sympy: + collect_ignore += needs_sympy diff --git a/.venv/lib/python3.12/site-packages/networkx/convert.py b/.venv/lib/python3.12/site-packages/networkx/convert.py new file mode 100644 index 0000000000000000000000000000000000000000..bf0a502a271f04594d7384f9eb7c4e6cf92f4edb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/networkx/convert.py @@ -0,0 +1,502 @@ +"""Functions to convert NetworkX graphs to and from other formats. + +The preferred way of converting data to a NetworkX graph is through the +graph constructor. The constructor calls the to_networkx_graph() function +which attempts to guess the input type and convert it automatically. + +Examples +-------- +Create a graph with a single edge from a dictionary of dictionaries + +>>> d = {0: {1: 1}} # dict-of-dicts single edge (0,1) +>>> G = nx.Graph(d) + +See Also +-------- +nx_agraph, nx_pydot +""" + +from collections.abc import Collection, Generator, Iterator + +import networkx as nx + +__all__ = [ + "to_networkx_graph", + "from_dict_of_dicts", + "to_dict_of_dicts", + "from_dict_of_lists", + "to_dict_of_lists", + "from_edgelist", + "to_edgelist", +] + + +def to_networkx_graph(data, create_using=None, multigraph_input=False): + """Make a NetworkX graph from a known data structure. + + The preferred way to call this is automatically + from the class constructor + + >>> d = {0: {1: {"weight": 1}}} # dict-of-dicts single edge (0,1) + >>> G = nx.Graph(d) + + instead of the equivalent + + >>> G = nx.from_dict_of_dicts(d) + + Parameters + ---------- + data : object to be converted + + Current known types are: + any NetworkX graph + dict-of-dicts + dict-of-lists + container (e.g. set, list, tuple) of edges + iterator (e.g. itertools.chain) that produces edges + generator of edges + Pandas DataFrame (row per edge) + 2D numpy array + scipy sparse array + pygraphviz agraph + + create_using : NetworkX graph constructor, optional (default=nx.Graph) + Graph type to create. If graph instance, then cleared before populated. + + multigraph_input : bool (default False) + If True and data is a dict_of_dicts, + try to create a multigraph assuming dict_of_dict_of_lists. + If data and create_using are both multigraphs then create + a multigraph from a multigraph. + + """ + # NX graph + if hasattr(data, "adj"): + try: + result = from_dict_of_dicts( + data.adj, + create_using=create_using, + multigraph_input=data.is_multigraph(), + ) + # data.graph should be dict-like + result.graph.update(data.graph) + # data.nodes should be dict-like + # result.add_node_from(data.nodes.items()) possible but + # for custom node_attr_dict_factory which may be hashable + # will be unexpected behavior + for n, dd in data.nodes.items(): + result._node[n].update(dd) + return result + except Exception as err: + raise nx.NetworkXError("Input is not a correct NetworkX graph.") from err + + # dict of dicts/lists + if isinstance(data, dict): + try: + return from_dict_of_dicts( + data, create_using=create_using, multigraph_input=multigraph_input + ) + except Exception as err1: + if multigraph_input is True: + raise nx.NetworkXError( + f"converting multigraph_input raised:\n{type(err1)}: {err1}" + ) + try: + return from_dict_of_lists(data, create_using=create_using) + except Exception as err2: + raise TypeError("Input is not known type.") from err2 + + # edgelists + if isinstance(data, list | tuple | nx.reportviews.EdgeViewABC | Iterator): + try: + return from_edgelist(data, create_using=create_using) + except: + pass + + # pygraphviz agraph + if hasattr(data, "is_strict"): + try: + return nx.nx_agraph.from_agraph(data, create_using=create_using) + except Exception as err: + raise nx.NetworkXError("Input is not a correct pygraphviz graph.") from err + + # Pandas DataFrame + try: + import pandas as pd + + if isinstance(data, pd.DataFrame): + if data.shape[0] == data.shape[1]: + try: + return nx.from_pandas_adjacency(data, create_using=create_using) + except Exception as err: + msg = "Input is not a correct Pandas DataFrame adjacency matrix." + raise nx.NetworkXError(msg) from err + else: + try: + return nx.from_pandas_edgelist( + data, edge_attr=True, create_using=create_using + ) + except Exception as err: + msg = "Input is not a correct Pandas DataFrame edge-list." + raise nx.NetworkXError(msg) from err + except ImportError: + pass + + # numpy array + try: + import numpy as np + + if isinstance(data, np.ndarray): + try: + return nx.from_numpy_array(data, create_using=create_using) + except Exception as err: + raise nx.NetworkXError( + f"Failed to interpret array as an adjacency matrix." + ) from err + except ImportError: + pass + + # scipy sparse array - any format + try: + import scipy as sp + + if hasattr(data, "format"): + try: + return nx.from_scipy_sparse_array(data, create_using=create_using) + except Exception as err: + raise nx.NetworkXError( + "Input is not a correct scipy sparse array type." + ) from err + except ImportError: + pass + + # Note: most general check - should remain last in order of execution + # Includes containers (e.g. list, set, dict, etc.), generators, and + # iterators (e.g. itertools.chain) of edges + + if isinstance(data, Collection | Generator | Iterator): + try: + return from_edgelist(data, create_using=create_using) + except Exception as err: + raise nx.NetworkXError("Input is not a valid edge list") from err + + raise nx.NetworkXError("Input is not a known data type for conversion.") + + +@nx._dispatchable +def to_dict_of_lists(G, nodelist=None): + """Returns adjacency representation of graph as a dictionary of lists. + + Parameters + ---------- + G : graph + A NetworkX graph + + nodelist : list + Use only nodes specified in nodelist + + Notes + ----- + Completely ignores edge data for MultiGraph and MultiDiGraph. + + """ + if nodelist is None: + nodelist = G + + d = {} + for n in nodelist: + d[n] = [nbr for nbr in G.neighbors(n) if nbr in nodelist] + return d + + +@nx._dispatchable(graphs=None, returns_graph=True) +def from_dict_of_lists(d, create_using=None): + """Returns a graph from a dictionary of lists. + + Parameters + ---------- + d : dictionary of lists + A dictionary of lists adjacency representation. + + create_using : NetworkX graph constructor, optional (default=nx.Graph) + Graph type to create. If graph instance, then cleared before populated. + + Examples + -------- + >>> dol = {0: [1]} # single edge (0,1) + >>> G = nx.from_dict_of_lists(dol) + + or + + >>> G = nx.Graph(dol) # use Graph constructor + + """ + G = nx.empty_graph(0, create_using) + G.add_nodes_from(d) + if G.is_multigraph() and not G.is_directed(): + # a dict_of_lists can't show multiedges. BUT for undirected graphs, + # each edge shows up twice in the dict_of_lists. + # So we need to treat this case separately. + seen = {} + for node, nbrlist in d.items(): + for nbr in nbrlist: + if nbr not in seen: + G.add_edge(node, nbr) + seen[node] = 1 # don't allow reverse edge to show up + else: + G.add_edges_from( + ((node, nbr) for node, nbrlist in d.items() for nbr in nbrlist) + ) + return G + + +def to_dict_of_dicts(G, nodelist=None, edge_data=None): + """Returns adjacency representation of graph as a dictionary of dictionaries. + + Parameters + ---------- + G : graph + A NetworkX graph + + nodelist : list + Use only nodes specified in nodelist. If None, all nodes in G. + + edge_data : scalar, optional (default: the G edgedatadict for each edge) + If provided, the value of the dictionary will be set to `edge_data` for + all edges. Usual values could be `1` or `True`. If `edge_data` is + `None` (the default), the edgedata in `G` is used, resulting in a + dict-of-dict-of-dicts. If `G` is a MultiGraph, the result will be a + dict-of-dict-of-dict-of-dicts. See Notes for an approach to customize + handling edge data. `edge_data` should *not* be a container as it will + be the same container for all the edges. + + Returns + ------- + dod : dict + A nested dictionary representation of `G`. Note that the level of + nesting depends on the type of `G` and the value of `edge_data` + (see Examples). + + See Also + -------- + from_dict_of_dicts, to_dict_of_lists + + Notes + ----- + For a more custom approach to handling edge data, try:: + + dod = { + n: {nbr: custom(n, nbr, dd) for nbr, dd in nbrdict.items()} + for n, nbrdict in G.adj.items() + } + + where `custom` returns the desired edge data for each edge between `n` and + `nbr`, given existing edge data `dd`. + + Examples + -------- + >>> G = nx.path_graph(3) + >>> nx.to_dict_of_dicts(G) + {0: {1: {}}, 1: {0: {}, 2: {}}, 2: {1: {}}} + + Edge data is preserved by default (``edge_data=None``), resulting + in dict-of-dict-of-dicts where the innermost dictionary contains the + edge data: + + >>> G = nx.Graph() + >>> G.add_edges_from( + ... [ + ... (0, 1, {"weight": 1.0}), + ... (1, 2, {"weight": 2.0}), + ... (2, 0, {"weight": 1.0}), + ... ] + ... ) + >>> d = nx.to_dict_of_dicts(G) + >>> d # doctest: +SKIP + {0: {1: {'weight': 1.0}, 2: {'weight': 1.0}}, + 1: {0: {'weight': 1.0}, 2: {'weight': 2.0}}, + 2: {1: {'weight': 2.0}, 0: {'weight': 1.0}}} + >>> d[1][2]["weight"] + 2.0 + + If `edge_data` is not `None`, edge data in the original graph (if any) is + replaced: + + >>> d = nx.to_dict_of_dicts(G, edge_data=1) + >>> d + {0: {1: 1, 2: 1}, 1: {0: 1, 2: 1}, 2: {1: 1, 0: 1}} + >>> d[1][2] + 1 + + This also applies to MultiGraphs: edge data is preserved by default: + + >>> G = nx.MultiGraph() + >>> G.add_edge(0, 1, key="a", weight=1.0) + 'a' + >>> G.add_edge(0, 1, key="b", weight=5.0) + 'b' + >>> d = nx.to_dict_of_dicts(G) + >>> d # doctest: +SKIP + {0: {1: {'a': {'weight': 1.0}, 'b': {'weight': 5.0}}}, + 1: {0: {'a': {'weight': 1.0}, 'b': {'weight': 5.0}}}} + >>> d[0][1]["b"]["weight"] + 5.0 + + But multi edge data is lost if `edge_data` is not `None`: + + >>> d = nx.to_dict_of_dicts(G, edge_data=10) + >>> d + {0: {1: 10}, 1: {0: 10}} + """ + dod = {} + if nodelist is None: + if edge_data is None: + for u, nbrdict in G.adjacency(): + dod[u] = nbrdict.copy() + else: # edge_data is not None + for u, nbrdict in G.adjacency(): + dod[u] = dod.fromkeys(nbrdict, edge_data) + else: # nodelist is not None + if edge_data is None: + for u in nodelist: + dod[u] = {} + for v, data in ((v, data) for v, data in G[u].items() if v in nodelist): + dod[u][v] = data + else: # nodelist and edge_data are not None + for u in nodelist: + dod[u] = {} + for v in (v for v in G[u] if v in nodelist): + dod[u][v] = edge_data + return dod + + +@nx._dispatchable(graphs=None, returns_graph=True) +def from_dict_of_dicts(d, create_using=None, multigraph_input=False): + """Returns a graph from a dictionary of dictionaries. + + Parameters + ---------- + d : dictionary of dictionaries + A dictionary of dictionaries adjacency representation. + + create_using : NetworkX graph constructor, optional (default=nx.Graph) + Graph type to create. If graph instance, then cleared before populated. + + multigraph_input : bool (default False) + When True, the dict `d` is assumed + to be a dict-of-dict-of-dict-of-dict structure keyed by + node to neighbor to edge keys to edge data for multi-edges. + Otherwise this routine assumes dict-of-dict-of-dict keyed by + node to neighbor to edge data. + + Examples + -------- + >>> dod = {0: {1: {"weight": 1}}} # single edge (0,1) + >>> G = nx.from_dict_of_dicts(dod) + + or + + >>> G = nx.Graph(dod) # use Graph constructor + + """ + G = nx.empty_graph(0, create_using) + G.add_nodes_from(d) + # does dict d represent a MultiGraph or MultiDiGraph? + if multigraph_input: + if G.is_directed(): + if G.is_multigraph(): + G.add_edges_from( + (u, v, key, data) + for u, nbrs in d.items() + for v, datadict in nbrs.items() + for key, data in datadict.items() + ) + else: + G.add_edges_from( + (u, v, data) + for u, nbrs in d.items() + for v, datadict in nbrs.items() + for key, data in datadict.items() + ) + else: # Undirected + if G.is_multigraph(): + seen = set() # don't add both directions of undirected graph + for u, nbrs in d.items(): + for v, datadict in nbrs.items(): + if (u, v) not in seen: + G.add_edges_from( + (u, v, key, data) for key, data in datadict.items() + ) + seen.add((v, u)) + else: + seen = set() # don't add both directions of undirected graph + for u, nbrs in d.items(): + for v, datadict in nbrs.items(): + if (u, v) not in seen: + G.add_edges_from( + (u, v, data) for key, data in datadict.items() + ) + seen.add((v, u)) + + else: # not a multigraph to multigraph transfer + if G.is_multigraph() and not G.is_directed(): + # d can have both representations u-v, v-u in dict. Only add one. + # We don't need this check for digraphs since we add both directions, + # or for Graph() since it is done implicitly (parallel edges not allowed) + seen = set() + for u, nbrs in d.items(): + for v, data in nbrs.items(): + if (u, v) not in seen: + G.add_edge(u, v, key=0) + G[u][v][0].update(data) + seen.add((v, u)) + else: + G.add_edges_from( + ((u, v, data) for u, nbrs in d.items() for v, data in nbrs.items()) + ) + return G + + +@nx._dispatchable(preserve_edge_attrs=True) +def to_edgelist(G, nodelist=None): + """Returns a list of edges in the graph. + + Parameters + ---------- + G : graph + A NetworkX graph + + nodelist : list + Use only nodes specified in nodelist + + """ + if nodelist is None: + return G.edges(data=True) + return G.edges(nodelist, data=True) + + +@nx._dispatchable(graphs=None, returns_graph=True) +def from_edgelist(edgelist, create_using=None): + """Returns a graph from a list of edges. + + Parameters + ---------- + edgelist : list or iterator + Edge tuples + + create_using : NetworkX graph constructor, optional (default=nx.Graph) + Graph type to create. If graph instance, then cleared before populated. + + Examples + -------- + >>> edgelist = [(0, 1)] # single edge (0,1) + >>> G = nx.from_edgelist(edgelist) + + or + + >>> G = nx.Graph(edgelist) # use Graph constructor + + """ + G = nx.empty_graph(0, create_using) + G.add_edges_from(edgelist) + return G diff --git a/.venv/lib/python3.12/site-packages/networkx/convert_matrix.py b/.venv/lib/python3.12/site-packages/networkx/convert_matrix.py new file mode 100644 index 0000000000000000000000000000000000000000..72a551798d13a16f8666d3a70274beaafb63d6a7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/networkx/convert_matrix.py @@ -0,0 +1,1314 @@ +"""Functions to convert NetworkX graphs to and from common data containers +like numpy arrays, scipy sparse arrays, and pandas DataFrames. + +The preferred way of converting data to a NetworkX graph is through the +graph constructor. The constructor calls the `~networkx.convert.to_networkx_graph` +function which attempts to guess the input type and convert it automatically. + +Examples +-------- +Create a 10 node random graph from a numpy array + +>>> import numpy as np +>>> rng = np.random.default_rng() +>>> a = rng.integers(low=0, high=2, size=(10, 10)) +>>> DG = nx.from_numpy_array(a, create_using=nx.DiGraph) + +or equivalently: + +>>> DG = nx.DiGraph(a) + +which calls `from_numpy_array` internally based on the type of ``a``. + +See Also +-------- +nx_agraph, nx_pydot +""" + +import itertools +from collections import defaultdict + +import networkx as nx + +__all__ = [ + "from_pandas_adjacency", + "to_pandas_adjacency", + "from_pandas_edgelist", + "to_pandas_edgelist", + "from_scipy_sparse_array", + "to_scipy_sparse_array", + "from_numpy_array", + "to_numpy_array", +] + + +@nx._dispatchable(edge_attrs="weight") +def to_pandas_adjacency( + G, + nodelist=None, + dtype=None, + order=None, + multigraph_weight=sum, + weight="weight", + nonedge=0.0, +): + """Returns the graph adjacency matrix as a Pandas DataFrame. + + Parameters + ---------- + G : graph + The NetworkX graph used to construct the Pandas DataFrame. + + nodelist : list, optional + The rows and columns are ordered according to the nodes in `nodelist`. + If `nodelist` is None, then the ordering is produced by G.nodes(). + + multigraph_weight : {sum, min, max}, optional + An operator that determines how weights in multigraphs are handled. + The default is to sum the weights of the multiple edges. + + weight : string or None, optional + The edge attribute that holds the numerical value used for + the edge weight. If an edge does not have that attribute, then the + value 1 is used instead. + + nonedge : float, optional + The matrix values corresponding to nonedges are typically set to zero. + However, this could be undesirable if there are matrix values + corresponding to actual edges that also have the value zero. If so, + one might prefer nonedges to have some other value, such as nan. + + Returns + ------- + df : Pandas DataFrame + Graph adjacency matrix + + Notes + ----- + For directed graphs, entry i,j corresponds to an edge from i to j. + + The DataFrame entries are assigned to the weight edge attribute. When + an edge does not have a weight attribute, the value of the entry is set to + the number 1. For multiple (parallel) edges, the values of the entries + are determined by the 'multigraph_weight' parameter. The default is to + sum the weight attributes for each of the parallel edges. + + When `nodelist` does not contain every node in `G`, the matrix is built + from the subgraph of `G` that is induced by the nodes in `nodelist`. + + The convention used for self-loop edges in graphs is to assign the + diagonal matrix entry value to the weight attribute of the edge + (or the number 1 if the edge has no weight attribute). If the + alternate convention of doubling the edge weight is desired the + resulting Pandas DataFrame can be modified as follows:: + + >>> import pandas as pd + >>> G = nx.Graph([(1, 1), (2, 2)]) + >>> df = nx.to_pandas_adjacency(G) + >>> df + 1 2 + 1 1.0 0.0 + 2 0.0 1.0 + >>> diag_idx = list(range(len(df))) + >>> df.iloc[diag_idx, diag_idx] *= 2 + >>> df + 1 2 + 1 2.0 0.0 + 2 0.0 2.0 + + Examples + -------- + >>> G = nx.MultiDiGraph() + >>> G.add_edge(0, 1, weight=2) + 0 + >>> G.add_edge(1, 0) + 0 + >>> G.add_edge(2, 2, weight=3) + 0 + >>> G.add_edge(2, 2) + 1 + >>> nx.to_pandas_adjacency(G, nodelist=[0, 1, 2], dtype=int) + 0 1 2 + 0 0 2 0 + 1 1 0 0 + 2 0 0 4 + + """ + import pandas as pd + + M = to_numpy_array( + G, + nodelist=nodelist, + dtype=dtype, + order=order, + multigraph_weight=multigraph_weight, + weight=weight, + nonedge=nonedge, + ) + if nodelist is None: + nodelist = list(G) + return pd.DataFrame(data=M, index=nodelist, columns=nodelist) + + +@nx._dispatchable(graphs=None, returns_graph=True) +def from_pandas_adjacency(df, create_using=None): + r"""Returns a graph from Pandas DataFrame. + + The Pandas DataFrame is interpreted as an adjacency matrix for the graph. + + Parameters + ---------- + df : Pandas DataFrame + An adjacency matrix representation of a graph + + create_using : NetworkX graph constructor, optional (default=nx.Graph) + Graph type to create. If graph instance, then cleared before populated. + + Notes + ----- + For directed graphs, explicitly mention create_using=nx.DiGraph, + and entry i,j of df corresponds to an edge from i to j. + + If `df` has a single data type for each entry it will be converted to an + appropriate Python data type. + + If you have node attributes stored in a separate dataframe `df_nodes`, + you can load those attributes to the graph `G` using the following code:: + + df_nodes = pd.DataFrame({"node_id": [1, 2, 3], "attribute1": ["A", "B", "C"]}) + G.add_nodes_from((n, dict(d)) for n, d in df_nodes.iterrows()) + + If `df` has a user-specified compound data type the names + of the data fields will be used as attribute keys in the resulting + NetworkX graph. + + See Also + -------- + to_pandas_adjacency + + Examples + -------- + Simple integer weights on edges: + + >>> import pandas as pd + >>> pd.options.display.max_columns = 20 + >>> df = pd.DataFrame([[1, 1], [2, 1]]) + >>> df + 0 1 + 0 1 1 + 1 2 1 + >>> G = nx.from_pandas_adjacency(df) + >>> G.name = "Graph from pandas adjacency matrix" + >>> print(G) + Graph named 'Graph from pandas adjacency matrix' with 2 nodes and 3 edges + """ + + try: + df = df[df.index] + except Exception as err: + missing = list(set(df.index).difference(set(df.columns))) + msg = f"{missing} not in columns" + raise nx.NetworkXError("Columns must match Indices.", msg) from err + + A = df.values + G = from_numpy_array(A, create_using=create_using, nodelist=df.columns) + + return G + + +@nx._dispatchable(preserve_edge_attrs=True) +def to_pandas_edgelist( + G, + source="source", + target="target", + nodelist=None, + dtype=None, + edge_key=None, +): + """Returns the graph edge list as a Pandas DataFrame. + + Parameters + ---------- + G : graph + The NetworkX graph used to construct the Pandas DataFrame. + + source : str or int, optional + A valid column name (string or integer) for the source nodes (for the + directed case). + + target : str or int, optional + A valid column name (string or integer) for the target nodes (for the + directed case). + + nodelist : list, optional + Use only nodes specified in nodelist + + dtype : dtype, default None + Use to create the DataFrame. Data type to force. + Only a single dtype is allowed. If None, infer. + + edge_key : str or int or None, optional (default=None) + A valid column name (string or integer) for the edge keys (for the + multigraph case). If None, edge keys are not stored in the DataFrame. + + Returns + ------- + df : Pandas DataFrame + Graph edge list + + Examples + -------- + >>> G = nx.Graph( + ... [ + ... ("A", "B", {"cost": 1, "weight": 7}), + ... ("C", "E", {"cost": 9, "weight": 10}), + ... ] + ... ) + >>> df = nx.to_pandas_edgelist(G, nodelist=["A", "C"]) + >>> df[["source", "target", "cost", "weight"]] + source target cost weight + 0 A B 1 7 + 1 C E 9 10 + + >>> G = nx.MultiGraph([("A", "B", {"cost": 1}), ("A", "B", {"cost": 9})]) + >>> df = nx.to_pandas_edgelist(G, nodelist=["A", "C"], edge_key="ekey") + >>> df[["source", "target", "cost", "ekey"]] + source target cost ekey + 0 A B 1 0 + 1 A B 9 1 + + """ + import pandas as pd + + if nodelist is None: + edgelist = G.edges(data=True) + else: + edgelist = G.edges(nodelist, data=True) + source_nodes = [s for s, _, _ in edgelist] + target_nodes = [t for _, t, _ in edgelist] + + all_attrs = set().union(*(d.keys() for _, _, d in edgelist)) + if source in all_attrs: + raise nx.NetworkXError(f"Source name {source!r} is an edge attr name") + if target in all_attrs: + raise nx.NetworkXError(f"Target name {target!r} is an edge attr name") + + nan = float("nan") + edge_attr = {k: [d.get(k, nan) for _, _, d in edgelist] for k in all_attrs} + + if G.is_multigraph() and edge_key is not None: + if edge_key in all_attrs: + raise nx.NetworkXError(f"Edge key name {edge_key!r} is an edge attr name") + edge_keys = [k for _, _, k in G.edges(keys=True)] + edgelistdict = {source: source_nodes, target: target_nodes, edge_key: edge_keys} + else: + edgelistdict = {source: source_nodes, target: target_nodes} + + edgelistdict.update(edge_attr) + return pd.DataFrame(edgelistdict, dtype=dtype) + + +@nx._dispatchable(graphs=None, returns_graph=True) +def from_pandas_edgelist( + df, + source="source", + target="target", + edge_attr=None, + create_using=None, + edge_key=None, +): + """Returns a graph from Pandas DataFrame containing an edge list. + + The Pandas DataFrame should contain at least two columns of node names and + zero or more columns of edge attributes. Each row will be processed as one + edge instance. + + Note: This function iterates over DataFrame.values, which is not + guaranteed to retain the data type across columns in the row. This is only + a problem if your row is entirely numeric and a mix of ints and floats. In + that case, all values will be returned as floats. See the + DataFrame.iterrows documentation for an example. + + Parameters + ---------- + df : Pandas DataFrame + An edge list representation of a graph + + source : str or int + A valid column name (string or integer) for the source nodes (for the + directed case). + + target : str or int + A valid column name (string or integer) for the target nodes (for the + directed case). + + edge_attr : str or int, iterable, True, or None + A valid column name (str or int) or iterable of column names that are + used to retrieve items and add them to the graph as edge attributes. + If `True`, all columns will be added except `source`, `target` and `edge_key`. + If `None`, no edge attributes are added to the graph. + + create_using : NetworkX graph constructor, optional (default=nx.Graph) + Graph type to create. If graph instance, then cleared before populated. + + edge_key : str or None, optional (default=None) + A valid column name for the edge keys (for a MultiGraph). The values in + this column are used for the edge keys when adding edges if create_using + is a multigraph. + + Notes + ----- + If you have node attributes stored in a separate dataframe `df_nodes`, + you can load those attributes to the graph `G` using the following code:: + + df_nodes = pd.DataFrame({"node_id": [1, 2, 3], "attribute1": ["A", "B", "C"]}) + G.add_nodes_from((n, dict(d)) for n, d in df_nodes.iterrows()) + + See Also + -------- + to_pandas_edgelist + + Examples + -------- + Simple integer weights on edges: + + >>> import pandas as pd + >>> pd.options.display.max_columns = 20 + >>> import numpy as np + >>> rng = np.random.RandomState(seed=5) + >>> ints = rng.randint(1, 11, size=(3, 2)) + >>> a = ["A", "B", "C"] + >>> b = ["D", "A", "E"] + >>> df = pd.DataFrame(ints, columns=["weight", "cost"]) + >>> df[0] = a + >>> df["b"] = b + >>> df[["weight", "cost", 0, "b"]] + weight cost 0 b + 0 4 7 A D + 1 7 1 B A + 2 10 9 C E + >>> G = nx.from_pandas_edgelist(df, 0, "b", ["weight", "cost"]) + >>> G["E"]["C"]["weight"] + 10 + >>> G["E"]["C"]["cost"] + 9 + >>> edges = pd.DataFrame( + ... { + ... "source": [0, 1, 2], + ... "target": [2, 2, 3], + ... "weight": [3, 4, 5], + ... "color": ["red", "blue", "blue"], + ... } + ... ) + >>> G = nx.from_pandas_edgelist(edges, edge_attr=True) + >>> G[0][2]["color"] + 'red' + + Build multigraph with custom keys: + + >>> edges = pd.DataFrame( + ... { + ... "source": [0, 1, 2, 0], + ... "target": [2, 2, 3, 2], + ... "my_edge_key": ["A", "B", "C", "D"], + ... "weight": [3, 4, 5, 6], + ... "color": ["red", "blue", "blue", "blue"], + ... } + ... ) + >>> G = nx.from_pandas_edgelist( + ... edges, + ... edge_key="my_edge_key", + ... edge_attr=["weight", "color"], + ... create_using=nx.MultiGraph(), + ... ) + >>> G[0][2] + AtlasView({'A': {'weight': 3, 'color': 'red'}, 'D': {'weight': 6, 'color': 'blue'}}) + + + """ + g = nx.empty_graph(0, create_using) + + if edge_attr is None: + if g.is_multigraph() and edge_key is not None: + for u, v, k in zip(df[source], df[target], df[edge_key]): + g.add_edge(u, v, k) + else: + g.add_edges_from(zip(df[source], df[target])) + return g + + reserved_columns = [source, target] + if g.is_multigraph() and edge_key is not None: + reserved_columns.append(edge_key) + + # Additional columns requested + attr_col_headings = [] + attribute_data = [] + if edge_attr is True: + attr_col_headings = [c for c in df.columns if c not in reserved_columns] + elif isinstance(edge_attr, list | tuple): + attr_col_headings = edge_attr + else: + attr_col_headings = [edge_attr] + if len(attr_col_headings) == 0: + raise nx.NetworkXError( + f"Invalid edge_attr argument: No columns found with name: {attr_col_headings}" + ) + + try: + attribute_data = zip(*[df[col] for col in attr_col_headings]) + except (KeyError, TypeError) as err: + msg = f"Invalid edge_attr argument: {edge_attr}" + raise nx.NetworkXError(msg) from err + + if g.is_multigraph(): + # => append the edge keys from the df to the bundled data + if edge_key is not None: + try: + multigraph_edge_keys = df[edge_key] + attribute_data = zip(attribute_data, multigraph_edge_keys) + except (KeyError, TypeError) as err: + msg = f"Invalid edge_key argument: {edge_key}" + raise nx.NetworkXError(msg) from err + + for s, t, attrs in zip(df[source], df[target], attribute_data): + if edge_key is not None: + attrs, multigraph_edge_key = attrs + key = g.add_edge(s, t, key=multigraph_edge_key) + else: + key = g.add_edge(s, t) + + g[s][t][key].update(zip(attr_col_headings, attrs)) + else: + for s, t, attrs in zip(df[source], df[target], attribute_data): + g.add_edge(s, t) + g[s][t].update(zip(attr_col_headings, attrs)) + + return g + + +@nx._dispatchable(edge_attrs="weight") +def to_scipy_sparse_array(G, nodelist=None, dtype=None, weight="weight", format="csr"): + """Returns the graph adjacency matrix as a SciPy sparse array. + + Parameters + ---------- + G : graph + The NetworkX graph used to construct the sparse array. + + nodelist : list, optional + The rows and columns are ordered according to the nodes in `nodelist`. + If `nodelist` is None, then the ordering is produced by ``G.nodes()``. + + dtype : NumPy data-type, optional + A valid NumPy dtype used to initialize the array. If None, then the + NumPy default is used. + + weight : string or None, optional (default='weight') + The edge attribute that holds the numerical value used for + the edge weight. If None then all edge weights are 1. + + format : str in {'bsr', 'csr', 'csc', 'coo', 'lil', 'dia', 'dok'} + The format of the sparse array to be returned (default 'csr'). For + some algorithms different implementations of sparse arrays + can perform better. See [1]_ for details. + + Returns + ------- + A : SciPy sparse array + Graph adjacency matrix. + + Notes + ----- + For directed graphs, matrix entry ``i, j`` corresponds to an edge from + ``i`` to ``j``. + + The values of the adjacency matrix are populated using the edge attribute held in + parameter `weight`. When an edge does not have that attribute, the + value of the entry is 1. + + For multiple edges the matrix values are the sums of the edge weights. + + When `nodelist` does not contain every node in `G`, the adjacency matrix + is built from the subgraph of `G` that is induced by the nodes in + `nodelist`. + + The convention used for self-loop edges in graphs is to assign the + diagonal matrix entry value to the weight attribute of the edge + (or the number 1 if the edge has no weight attribute). If the + alternate convention of doubling the edge weight is desired the + resulting array can be modified as follows:: + + >>> G = nx.Graph([(1, 1)]) + >>> A = nx.to_scipy_sparse_array(G) + >>> A.toarray() + array([[1]]) + >>> A.setdiag(A.diagonal() * 2) + >>> A.toarray() + array([[2]]) + + Examples + -------- + + Basic usage: + + >>> G = nx.path_graph(4) + >>> A = nx.to_scipy_sparse_array(G) + >>> A # doctest: +SKIP + + + >>> A.toarray() + array([[0, 1, 0, 0], + [1, 0, 1, 0], + [0, 1, 0, 1], + [0, 0, 1, 0]]) + + .. note:: The `toarray` method is used in these examples to better visualize + the adjacency matrix. For a dense representation of the adjaceny matrix, + use `to_numpy_array` instead. + + Directed graphs: + + >>> G = nx.DiGraph([(0, 1), (1, 2), (2, 3)]) + >>> nx.to_scipy_sparse_array(G).toarray() + array([[0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1], + [0, 0, 0, 0]]) + + >>> H = G.reverse() + >>> H.edges + OutEdgeView([(1, 0), (2, 1), (3, 2)]) + >>> nx.to_scipy_sparse_array(H).toarray() + array([[0, 0, 0, 0], + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0]]) + + By default, the order of the rows/columns of the adjacency matrix is determined + by the ordering of the nodes in `G`: + + >>> G = nx.Graph() + >>> G.add_nodes_from([3, 5, 0, 1]) + >>> G.add_edges_from([(1, 3), (1, 5)]) + >>> nx.to_scipy_sparse_array(G).toarray() + array([[0, 0, 0, 1], + [0, 0, 0, 1], + [0, 0, 0, 0], + [1, 1, 0, 0]]) + + The ordering of the rows can be changed with `nodelist`: + + >>> ordered = [0, 1, 3, 5] + >>> nx.to_scipy_sparse_array(G, nodelist=ordered).toarray() + array([[0, 0, 0, 0], + [0, 0, 1, 1], + [0, 1, 0, 0], + [0, 1, 0, 0]]) + + If `nodelist` contains a subset of the nodes in `G`, the adjacency matrix + for the node-induced subgraph is produced: + + >>> nx.to_scipy_sparse_array(G, nodelist=[1, 3, 5]).toarray() + array([[0, 1, 1], + [1, 0, 0], + [1, 0, 0]]) + + The values of the adjacency matrix are drawn from the edge attribute + specified by the `weight` parameter: + + >>> G = nx.path_graph(4) + >>> nx.set_edge_attributes( + ... G, values={(0, 1): 1, (1, 2): 10, (2, 3): 2}, name="weight" + ... ) + >>> nx.set_edge_attributes( + ... G, values={(0, 1): 50, (1, 2): 35, (2, 3): 10}, name="capacity" + ... ) + >>> nx.to_scipy_sparse_array(G).toarray() # Default weight="weight" + array([[ 0, 1, 0, 0], + [ 1, 0, 10, 0], + [ 0, 10, 0, 2], + [ 0, 0, 2, 0]]) + >>> nx.to_scipy_sparse_array(G, weight="capacity").toarray() + array([[ 0, 50, 0, 0], + [50, 0, 35, 0], + [ 0, 35, 0, 10], + [ 0, 0, 10, 0]]) + + Any edges that don't have a `weight` attribute default to 1: + + >>> G[1][2].pop("capacity") + 35 + >>> nx.to_scipy_sparse_array(G, weight="capacity").toarray() + array([[ 0, 50, 0, 0], + [50, 0, 1, 0], + [ 0, 1, 0, 10], + [ 0, 0, 10, 0]]) + + When `G` is a multigraph, the values in the adjacency matrix are given by + the sum of the `weight` edge attribute over each edge key: + + >>> G = nx.MultiDiGraph([(0, 1), (0, 1), (0, 1), (2, 0)]) + >>> nx.to_scipy_sparse_array(G).toarray() + array([[0, 3, 0], + [0, 0, 0], + [1, 0, 0]]) + + References + ---------- + .. [1] Scipy Dev. References, "Sparse Arrays", + https://docs.scipy.org/doc/scipy/reference/sparse.html + """ + import scipy as sp + + if len(G) == 0: + raise nx.NetworkXError("Graph has no nodes or edges") + + if nodelist is None: + nodelist = list(G) + nlen = len(G) + else: + nlen = len(nodelist) + if nlen == 0: + raise nx.NetworkXError("nodelist has no nodes") + nodeset = set(G.nbunch_iter(nodelist)) + if nlen != len(nodeset): + for n in nodelist: + if n not in G: + raise nx.NetworkXError(f"Node {n} in nodelist is not in G") + raise nx.NetworkXError("nodelist contains duplicates.") + if nlen < len(G): + G = G.subgraph(nodelist) + + index = dict(zip(nodelist, range(nlen))) + coefficients = zip( + *((index[u], index[v], wt) for u, v, wt in G.edges(data=weight, default=1)) + ) + try: + row, col, data = coefficients + except ValueError: + # there is no edge in the subgraph + row, col, data = [], [], [] + + if G.is_directed(): + A = sp.sparse.coo_array((data, (row, col)), shape=(nlen, nlen), dtype=dtype) + else: + # symmetrize matrix + d = data + data + r = row + col + c = col + row + # selfloop entries get double counted when symmetrizing + # so we subtract the data on the diagonal + selfloops = list(nx.selfloop_edges(G, data=weight, default=1)) + if selfloops: + diag_index, diag_data = zip(*((index[u], -wt) for u, v, wt in selfloops)) + d += diag_data + r += diag_index + c += diag_index + A = sp.sparse.coo_array((d, (r, c)), shape=(nlen, nlen), dtype=dtype) + try: + return A.asformat(format) + except ValueError as err: + raise nx.NetworkXError(f"Unknown sparse matrix format: {format}") from err + + +def _csr_gen_triples(A): + """Converts a SciPy sparse array in **Compressed Sparse Row** format to + an iterable of weighted edge triples. + + """ + nrows = A.shape[0] + indptr, dst_indices, data = A.indptr, A.indices, A.data + import numpy as np + + src_indices = np.repeat(np.arange(nrows), np.diff(indptr)) + return zip(src_indices.tolist(), dst_indices.tolist(), A.data.tolist()) + + +def _csc_gen_triples(A): + """Converts a SciPy sparse array in **Compressed Sparse Column** format to + an iterable of weighted edge triples. + + """ + ncols = A.shape[1] + indptr, src_indices, data = A.indptr, A.indices, A.data + import numpy as np + + dst_indices = np.repeat(np.arange(ncols), np.diff(indptr)) + return zip(src_indices.tolist(), dst_indices.tolist(), A.data.tolist()) + + +def _coo_gen_triples(A): + """Converts a SciPy sparse array in **Coordinate** format to an iterable + of weighted edge triples. + + """ + return zip(A.row.tolist(), A.col.tolist(), A.data.tolist()) + + +def _dok_gen_triples(A): + """Converts a SciPy sparse array in **Dictionary of Keys** format to an + iterable of weighted edge triples. + + """ + for (r, c), v in A.items(): + # Use `v.item()` to convert a NumPy scalar to the appropriate Python scalar + yield int(r), int(c), v.item() + + +def _generate_weighted_edges(A): + """Returns an iterable over (u, v, w) triples, where u and v are adjacent + vertices and w is the weight of the edge joining u and v. + + `A` is a SciPy sparse array (in any format). + + """ + if A.format == "csr": + return _csr_gen_triples(A) + if A.format == "csc": + return _csc_gen_triples(A) + if A.format == "dok": + return _dok_gen_triples(A) + # If A is in any other format (including COO), convert it to COO format. + return _coo_gen_triples(A.tocoo()) + + +@nx._dispatchable(graphs=None, returns_graph=True) +def from_scipy_sparse_array( + A, parallel_edges=False, create_using=None, edge_attribute="weight" +): + """Creates a new graph from an adjacency matrix given as a SciPy sparse + array. + + Parameters + ---------- + A: scipy.sparse array + An adjacency matrix representation of a graph + + parallel_edges : Boolean + If this is True, `create_using` is a multigraph, and `A` is an + integer matrix, then entry *(i, j)* in the matrix is interpreted as the + number of parallel edges joining vertices *i* and *j* in the graph. + If it is False, then the entries in the matrix are interpreted as + the weight of a single edge joining the vertices. + + create_using : NetworkX graph constructor, optional (default=nx.Graph) + Graph type to create. If graph instance, then cleared before populated. + + edge_attribute: string + Name of edge attribute to store matrix numeric value. The data will + have the same type as the matrix entry (int, float, (real,imag)). + + Notes + ----- + For directed graphs, explicitly mention create_using=nx.DiGraph, + and entry i,j of A corresponds to an edge from i to j. + + If `create_using` is :class:`networkx.MultiGraph` or + :class:`networkx.MultiDiGraph`, `parallel_edges` is True, and the + entries of `A` are of type :class:`int`, then this function returns a + multigraph (constructed from `create_using`) with parallel edges. + In this case, `edge_attribute` will be ignored. + + If `create_using` indicates an undirected multigraph, then only the edges + indicated by the upper triangle of the matrix `A` will be added to the + graph. + + Examples + -------- + >>> import scipy as sp + >>> A = sp.sparse.eye(2, 2, 1) + >>> G = nx.from_scipy_sparse_array(A) + + If `create_using` indicates a multigraph and the matrix has only integer + entries and `parallel_edges` is False, then the entries will be treated + as weights for edges joining the nodes (without creating parallel edges): + + >>> A = sp.sparse.csr_array([[1, 1], [1, 2]]) + >>> G = nx.from_scipy_sparse_array(A, create_using=nx.MultiGraph) + >>> G[1][1] + AtlasView({0: {'weight': 2}}) + + If `create_using` indicates a multigraph and the matrix has only integer + entries and `parallel_edges` is True, then the entries will be treated + as the number of parallel edges joining those two vertices: + + >>> A = sp.sparse.csr_array([[1, 1], [1, 2]]) + >>> G = nx.from_scipy_sparse_array( + ... A, parallel_edges=True, create_using=nx.MultiGraph + ... ) + >>> G[1][1] + AtlasView({0: {'weight': 1}, 1: {'weight': 1}}) + + """ + G = nx.empty_graph(0, create_using) + n, m = A.shape + if n != m: + raise nx.NetworkXError(f"Adjacency matrix not square: nx,ny={A.shape}") + # Make sure we get even the isolated nodes of the graph. + G.add_nodes_from(range(n)) + # Create an iterable over (u, v, w) triples and for each triple, add an + # edge from u to v with weight w. + triples = _generate_weighted_edges(A) + # If the entries in the adjacency matrix are integers, the graph is a + # multigraph, and parallel_edges is True, then create parallel edges, each + # with weight 1, for each entry in the adjacency matrix. Otherwise, create + # one edge for each positive entry in the adjacency matrix and set the + # weight of that edge to be the entry in the matrix. + if A.dtype.kind in ("i", "u") and G.is_multigraph() and parallel_edges: + chain = itertools.chain.from_iterable + # The following line is equivalent to: + # + # for (u, v) in edges: + # for d in range(A[u, v]): + # G.add_edge(u, v, weight=1) + # + triples = chain(((u, v, 1) for d in range(w)) for (u, v, w) in triples) + # If we are creating an undirected multigraph, only add the edges from the + # upper triangle of the matrix. Otherwise, add all the edges. This relies + # on the fact that the vertices created in the + # `_generated_weighted_edges()` function are actually the row/column + # indices for the matrix `A`. + # + # Without this check, we run into a problem where each edge is added twice + # when `G.add_weighted_edges_from()` is invoked below. + if G.is_multigraph() and not G.is_directed(): + triples = ((u, v, d) for u, v, d in triples if u <= v) + G.add_weighted_edges_from(triples, weight=edge_attribute) + return G + + +@nx._dispatchable(edge_attrs="weight") # edge attrs may also be obtained from `dtype` +def to_numpy_array( + G, + nodelist=None, + dtype=None, + order=None, + multigraph_weight=sum, + weight="weight", + nonedge=0.0, +): + """Returns the graph adjacency matrix as a NumPy array. + + Parameters + ---------- + G : graph + The NetworkX graph used to construct the NumPy array. + + nodelist : list, optional + The rows and columns are ordered according to the nodes in `nodelist`. + If `nodelist` is ``None``, then the ordering is produced by ``G.nodes()``. + + dtype : NumPy data type, optional + A NumPy data type used to initialize the array. If None, then the NumPy + default is used. The dtype can be structured if `weight=None`, in which + case the dtype field names are used to look up edge attributes. The + result is a structured array where each named field in the dtype + corresponds to the adjacency for that edge attribute. See examples for + details. + + order : {'C', 'F'}, optional + Whether to store multidimensional data in C- or Fortran-contiguous + (row- or column-wise) order in memory. If None, then the NumPy default + is used. + + multigraph_weight : callable, optional + An function that determines how weights in multigraphs are handled. + The function should accept a sequence of weights and return a single + value. The default is to sum the weights of the multiple edges. + + weight : string or None optional (default = 'weight') + The edge attribute that holds the numerical value used for + the edge weight. If an edge does not have that attribute, then the + value 1 is used instead. `weight` must be ``None`` if a structured + dtype is used. + + nonedge : array_like (default = 0.0) + The value used to represent non-edges in the adjacency matrix. + The array values corresponding to nonedges are typically set to zero. + However, this could be undesirable if there are array values + corresponding to actual edges that also have the value zero. If so, + one might prefer nonedges to have some other value, such as ``nan``. + + Returns + ------- + A : NumPy ndarray + Graph adjacency matrix + + Raises + ------ + NetworkXError + If `dtype` is a structured dtype and `G` is a multigraph + ValueError + If `dtype` is a structured dtype and `weight` is not `None` + + See Also + -------- + from_numpy_array + + Notes + ----- + For directed graphs, entry ``i, j`` corresponds to an edge from ``i`` to ``j``. + + Entries in the adjacency matrix are given by the `weight` edge attribute. + When an edge does not have a weight attribute, the value of the entry is + set to the number 1. For multiple (parallel) edges, the values of the + entries are determined by the `multigraph_weight` parameter. The default is + to sum the weight attributes for each of the parallel edges. + + When `nodelist` does not contain every node in `G`, the adjacency matrix is + built from the subgraph of `G` that is induced by the nodes in `nodelist`. + + The convention used for self-loop edges in graphs is to assign the + diagonal array entry value to the weight attribute of the edge + (or the number 1 if the edge has no weight attribute). If the + alternate convention of doubling the edge weight is desired the + resulting NumPy array can be modified as follows: + + >>> import numpy as np + >>> G = nx.Graph([(1, 1)]) + >>> A = nx.to_numpy_array(G) + >>> A + array([[1.]]) + >>> A[np.diag_indices_from(A)] *= 2 + >>> A + array([[2.]]) + + Examples + -------- + >>> G = nx.MultiDiGraph() + >>> G.add_edge(0, 1, weight=2) + 0 + >>> G.add_edge(1, 0) + 0 + >>> G.add_edge(2, 2, weight=3) + 0 + >>> G.add_edge(2, 2) + 1 + >>> nx.to_numpy_array(G, nodelist=[0, 1, 2]) + array([[0., 2., 0.], + [1., 0., 0.], + [0., 0., 4.]]) + + When `nodelist` argument is used, nodes of `G` which do not appear in the `nodelist` + and their edges are not included in the adjacency matrix. Here is an example: + + >>> G = nx.Graph() + >>> G.add_edge(3, 1) + >>> G.add_edge(2, 0) + >>> G.add_edge(2, 1) + >>> G.add_edge(3, 0) + >>> nx.to_numpy_array(G, nodelist=[1, 2, 3]) + array([[0., 1., 1.], + [1., 0., 0.], + [1., 0., 0.]]) + + This function can also be used to create adjacency matrices for multiple + edge attributes with structured dtypes: + + >>> G = nx.Graph() + >>> G.add_edge(0, 1, weight=10) + >>> G.add_edge(1, 2, cost=5) + >>> G.add_edge(2, 3, weight=3, cost=-4.0) + >>> dtype = np.dtype([("weight", int), ("cost", float)]) + >>> A = nx.to_numpy_array(G, dtype=dtype, weight=None) + >>> A["weight"] + array([[ 0, 10, 0, 0], + [10, 0, 1, 0], + [ 0, 1, 0, 3], + [ 0, 0, 3, 0]]) + >>> A["cost"] + array([[ 0., 1., 0., 0.], + [ 1., 0., 5., 0.], + [ 0., 5., 0., -4.], + [ 0., 0., -4., 0.]]) + + As stated above, the argument "nonedge" is useful especially when there are + actually edges with weight 0 in the graph. Setting a nonedge value different than 0, + makes it much clearer to differentiate such 0-weighted edges and actual nonedge values. + + >>> G = nx.Graph() + >>> G.add_edge(3, 1, weight=2) + >>> G.add_edge(2, 0, weight=0) + >>> G.add_edge(2, 1, weight=0) + >>> G.add_edge(3, 0, weight=1) + >>> nx.to_numpy_array(G, nonedge=-1.0) + array([[-1., 2., -1., 1.], + [ 2., -1., 0., -1.], + [-1., 0., -1., 0.], + [ 1., -1., 0., -1.]]) + """ + import numpy as np + + if nodelist is None: + nodelist = list(G) + nlen = len(nodelist) + + # Input validation + nodeset = set(nodelist) + if nodeset - set(G): + raise nx.NetworkXError(f"Nodes {nodeset - set(G)} in nodelist is not in G") + if len(nodeset) < nlen: + raise nx.NetworkXError("nodelist contains duplicates.") + + A = np.full((nlen, nlen), fill_value=nonedge, dtype=dtype, order=order) + + # Corner cases: empty nodelist or graph without any edges + if nlen == 0 or G.number_of_edges() == 0: + return A + + # If dtype is structured and weight is None, use dtype field names as + # edge attributes + edge_attrs = None # Only single edge attribute by default + if A.dtype.names: + if weight is None: + edge_attrs = dtype.names + else: + raise ValueError( + "Specifying `weight` not supported for structured dtypes\n." + "To create adjacency matrices from structured dtypes, use `weight=None`." + ) + + # Map nodes to row/col in matrix + idx = dict(zip(nodelist, range(nlen))) + if len(nodelist) < len(G): + G = G.subgraph(nodelist).copy() + + # Collect all edge weights and reduce with `multigraph_weights` + if G.is_multigraph(): + if edge_attrs: + raise nx.NetworkXError( + "Structured arrays are not supported for MultiGraphs" + ) + d = defaultdict(list) + for u, v, wt in G.edges(data=weight, default=1.0): + d[(idx[u], idx[v])].append(wt) + i, j = np.array(list(d.keys())).T # indices + wts = [multigraph_weight(ws) for ws in d.values()] # reduced weights + else: + i, j, wts = [], [], [] + + # Special branch: multi-attr adjacency from structured dtypes + if edge_attrs: + # Extract edges with all data + for u, v, data in G.edges(data=True): + i.append(idx[u]) + j.append(idx[v]) + wts.append(data) + # Map each attribute to the appropriate named field in the + # structured dtype + for attr in edge_attrs: + attr_data = [wt.get(attr, 1.0) for wt in wts] + A[attr][i, j] = attr_data + if not G.is_directed(): + A[attr][j, i] = attr_data + return A + + for u, v, wt in G.edges(data=weight, default=1.0): + i.append(idx[u]) + j.append(idx[v]) + wts.append(wt) + + # Set array values with advanced indexing + A[i, j] = wts + if not G.is_directed(): + A[j, i] = wts + + return A + + +@nx._dispatchable(graphs=None, returns_graph=True) +def from_numpy_array( + A, parallel_edges=False, create_using=None, edge_attr="weight", *, nodelist=None +): + """Returns a graph from a 2D NumPy array. + + The 2D NumPy array is interpreted as an adjacency matrix for the graph. + + Parameters + ---------- + A : a 2D numpy.ndarray + An adjacency matrix representation of a graph + + parallel_edges : Boolean + If this is True, `create_using` is a multigraph, and `A` is an + integer array, then entry *(i, j)* in the array is interpreted as the + number of parallel edges joining vertices *i* and *j* in the graph. + If it is False, then the entries in the array are interpreted as + the weight of a single edge joining the vertices. + + create_using : NetworkX graph constructor, optional (default=nx.Graph) + Graph type to create. If graph instance, then cleared before populated. + + edge_attr : String, optional (default="weight") + The attribute to which the array values are assigned on each edge. If + it is None, edge attributes will not be assigned. + + nodelist : sequence of nodes, optional + A sequence of objects to use as the nodes in the graph. If provided, the + list of nodes must be the same length as the dimensions of `A`. The + default is `None`, in which case the nodes are drawn from ``range(n)``. + + Notes + ----- + For directed graphs, explicitly mention create_using=nx.DiGraph, + and entry i,j of A corresponds to an edge from i to j. + + If `create_using` is :class:`networkx.MultiGraph` or + :class:`networkx.MultiDiGraph`, `parallel_edges` is True, and the + entries of `A` are of type :class:`int`, then this function returns a + multigraph (of the same type as `create_using`) with parallel edges. + + If `create_using` indicates an undirected multigraph, then only the edges + indicated by the upper triangle of the array `A` will be added to the + graph. + + If `edge_attr` is Falsy (False or None), edge attributes will not be + assigned, and the array data will be treated like a binary mask of + edge presence or absence. Otherwise, the attributes will be assigned + as follows: + + If the NumPy array has a single data type for each array entry it + will be converted to an appropriate Python data type. + + If the NumPy array has a user-specified compound data type the names + of the data fields will be used as attribute keys in the resulting + NetworkX graph. + + See Also + -------- + to_numpy_array + + Examples + -------- + Simple integer weights on edges: + + >>> import numpy as np + >>> A = np.array([[1, 1], [2, 1]]) + >>> G = nx.from_numpy_array(A) + >>> G.edges(data=True) + EdgeDataView([(0, 0, {'weight': 1}), (0, 1, {'weight': 2}), (1, 1, {'weight': 1})]) + + If `create_using` indicates a multigraph and the array has only integer + entries and `parallel_edges` is False, then the entries will be treated + as weights for edges joining the nodes (without creating parallel edges): + + >>> A = np.array([[1, 1], [1, 2]]) + >>> G = nx.from_numpy_array(A, create_using=nx.MultiGraph) + >>> G[1][1] + AtlasView({0: {'weight': 2}}) + + If `create_using` indicates a multigraph and the array has only integer + entries and `parallel_edges` is True, then the entries will be treated + as the number of parallel edges joining those two vertices: + + >>> A = np.array([[1, 1], [1, 2]]) + >>> temp = nx.MultiGraph() + >>> G = nx.from_numpy_array(A, parallel_edges=True, create_using=temp) + >>> G[1][1] + AtlasView({0: {'weight': 1}, 1: {'weight': 1}}) + + User defined compound data type on edges: + + >>> dt = [("weight", float), ("cost", int)] + >>> A = np.array([[(1.0, 2)]], dtype=dt) + >>> G = nx.from_numpy_array(A) + >>> G.edges() + EdgeView([(0, 0)]) + >>> G[0][0]["cost"] + 2 + >>> G[0][0]["weight"] + 1.0 + + """ + kind_to_python_type = { + "f": float, + "i": int, + "u": int, + "b": bool, + "c": complex, + "S": str, + "U": str, + "V": "void", + } + G = nx.empty_graph(0, create_using) + if A.ndim != 2: + raise nx.NetworkXError(f"Input array must be 2D, not {A.ndim}") + n, m = A.shape + if n != m: + raise nx.NetworkXError(f"Adjacency matrix not square: nx,ny={A.shape}") + dt = A.dtype + try: + python_type = kind_to_python_type[dt.kind] + except Exception as err: + raise TypeError(f"Unknown numpy data type: {dt}") from err + if _default_nodes := (nodelist is None): + nodelist = range(n) + else: + if len(nodelist) != n: + raise ValueError("nodelist must have the same length as A.shape[0]") + + # Make sure we get even the isolated nodes of the graph. + G.add_nodes_from(nodelist) + # Get a list of all the entries in the array with nonzero entries. These + # coordinates become edges in the graph. (convert to int from np.int64) + edges = ((int(e[0]), int(e[1])) for e in zip(*A.nonzero())) + # handle numpy constructed data type + if python_type == "void": + # Sort the fields by their offset, then by dtype, then by name. + fields = sorted( + (offset, dtype, name) for name, (dtype, offset) in A.dtype.fields.items() + ) + triples = ( + ( + u, + v, + {} + if edge_attr in [False, None] + else { + name: kind_to_python_type[dtype.kind](val) + for (_, dtype, name), val in zip(fields, A[u, v]) + }, + ) + for u, v in edges + ) + # If the entries in the adjacency matrix are integers, the graph is a + # multigraph, and parallel_edges is True, then create parallel edges, each + # with weight 1, for each entry in the adjacency matrix. Otherwise, create + # one edge for each positive entry in the adjacency matrix and set the + # weight of that edge to be the entry in the matrix. + elif python_type is int and G.is_multigraph() and parallel_edges: + chain = itertools.chain.from_iterable + # The following line is equivalent to: + # + # for (u, v) in edges: + # for d in range(A[u, v]): + # G.add_edge(u, v, weight=1) + # + if edge_attr in [False, None]: + triples = chain(((u, v, {}) for d in range(A[u, v])) for (u, v) in edges) + else: + triples = chain( + ((u, v, {edge_attr: 1}) for d in range(A[u, v])) for (u, v) in edges + ) + else: # basic data type + if edge_attr in [False, None]: + triples = ((u, v, {}) for u, v in edges) + else: + triples = ((u, v, {edge_attr: python_type(A[u, v])}) for u, v in edges) + # If we are creating an undirected multigraph, only add the edges from the + # upper triangle of the matrix. Otherwise, add all the edges. This relies + # on the fact that the vertices created in the + # `_generated_weighted_edges()` function are actually the row/column + # indices for the matrix `A`. + # + # Without this check, we run into a problem where each edge is added twice + # when `G.add_edges_from()` is invoked below. + if G.is_multigraph() and not G.is_directed(): + triples = ((u, v, d) for u, v, d in triples if u <= v) + # Remap nodes if user provided custom `nodelist` + if not _default_nodes: + idx_to_node = dict(enumerate(nodelist)) + triples = ((idx_to_node[u], idx_to_node[v], d) for u, v, d in triples) + G.add_edges_from(triples) + return G diff --git a/.venv/lib/python3.12/site-packages/networkx/exception.py b/.venv/lib/python3.12/site-packages/networkx/exception.py new file mode 100644 index 0000000000000000000000000000000000000000..c960cf13fd5a8e4da0ca68c66350b8baa1728c34 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/networkx/exception.py @@ -0,0 +1,131 @@ +""" +********** +Exceptions +********** + +Base exceptions and errors for NetworkX. +""" + +__all__ = [ + "HasACycle", + "NodeNotFound", + "PowerIterationFailedConvergence", + "ExceededMaxIterations", + "AmbiguousSolution", + "NetworkXAlgorithmError", + "NetworkXException", + "NetworkXError", + "NetworkXNoCycle", + "NetworkXNoPath", + "NetworkXNotImplemented", + "NetworkXPointlessConcept", + "NetworkXUnbounded", + "NetworkXUnfeasible", +] + + +class NetworkXException(Exception): + """Base class for exceptions in NetworkX.""" + + +class NetworkXError(NetworkXException): + """Exception for a serious error in NetworkX""" + + +class NetworkXPointlessConcept(NetworkXException): + """Raised when a null graph is provided as input to an algorithm + that cannot use it. + + The null graph is sometimes considered a pointless concept [1]_, + thus the name of the exception. + + Notes + ----- + Null graphs and empty graphs are often used interchangeably but they + are well defined in NetworkX. An ``empty_graph`` is a graph with ``n`` nodes + and 0 edges, and a ``null_graph`` is a graph with 0 nodes and 0 edges. + + References + ---------- + .. [1] Harary, F. and Read, R. "Is the Null Graph a Pointless + Concept?" In Graphs and Combinatorics Conference, George + Washington University. New York: Springer-Verlag, 1973. + + """ + + +class NetworkXAlgorithmError(NetworkXException): + """Exception for unexpected termination of algorithms.""" + + +class NetworkXUnfeasible(NetworkXAlgorithmError): + """Exception raised by algorithms trying to solve a problem + instance that has no feasible solution.""" + + +class NetworkXNoPath(NetworkXUnfeasible): + """Exception for algorithms that should return a path when running + on graphs where such a path does not exist.""" + + +class NetworkXNoCycle(NetworkXUnfeasible): + """Exception for algorithms that should return a cycle when running + on graphs where such a cycle does not exist.""" + + +class HasACycle(NetworkXException): + """Raised if a graph has a cycle when an algorithm expects that it + will have no cycles. + + """ + + +class NetworkXUnbounded(NetworkXAlgorithmError): + """Exception raised by algorithms trying to solve a maximization + or a minimization problem instance that is unbounded.""" + + +class NetworkXNotImplemented(NetworkXException): + """Exception raised by algorithms not implemented for a type of graph.""" + + +class NodeNotFound(NetworkXException): + """Exception raised if requested node is not present in the graph""" + + +class AmbiguousSolution(NetworkXException): + """Raised if more than one valid solution exists for an intermediary step + of an algorithm. + + In the face of ambiguity, refuse the temptation to guess. + This may occur, for example, when trying to determine the + bipartite node sets in a disconnected bipartite graph when + computing bipartite matchings. + + """ + + +class ExceededMaxIterations(NetworkXException): + """Raised if a loop iterates too many times without breaking. + + This may occur, for example, in an algorithm that computes + progressively better approximations to a value but exceeds an + iteration bound specified by the user. + + """ + + +class PowerIterationFailedConvergence(ExceededMaxIterations): + """Raised when the power iteration method fails to converge within a + specified iteration limit. + + `num_iterations` is the number of iterations that have been + completed when this exception was raised. + + """ + + def __init__(self, num_iterations, *args, **kw): + msg = f"power iteration failed to converge within {num_iterations} iterations" + exception_message = msg + superinit = super().__init__ + superinit(self, exception_message, *args, **kw) diff --git a/.venv/lib/python3.12/site-packages/networkx/lazy_imports.py b/.venv/lib/python3.12/site-packages/networkx/lazy_imports.py new file mode 100644 index 0000000000000000000000000000000000000000..c5c05ca5462868fbccc270ea1e93b301037dfc8c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/networkx/lazy_imports.py @@ -0,0 +1,188 @@ +import importlib +import importlib.util +import inspect +import os +import sys +import types + +__all__ = ["attach", "_lazy_import"] + + +def attach(module_name, submodules=None, submod_attrs=None): + """Attach lazily loaded submodules, and functions or other attributes. + + Typically, modules import submodules and attributes as follows:: + + import mysubmodule + import anothersubmodule + + from .foo import someattr + + The idea of this function is to replace the `__init__.py` + module's `__getattr__`, `__dir__`, and `__all__` attributes such that + all imports work exactly the way they normally would, except that the + actual import is delayed until the resulting module object is first used. + + The typical way to call this function, replacing the above imports, is:: + + __getattr__, __lazy_dir__, __all__ = lazy.attach( + __name__, ["mysubmodule", "anothersubmodule"], {"foo": "someattr"} + ) + + This functionality requires Python 3.7 or higher. + + Parameters + ---------- + module_name : str + Typically use __name__. + submodules : set + List of submodules to lazily import. + submod_attrs : dict + Dictionary of submodule -> list of attributes / functions. + These attributes are imported as they are used. + + Returns + ------- + __getattr__, __dir__, __all__ + + """ + if submod_attrs is None: + submod_attrs = {} + + if submodules is None: + submodules = set() + else: + submodules = set(submodules) + + attr_to_modules = { + attr: mod for mod, attrs in submod_attrs.items() for attr in attrs + } + + __all__ = list(submodules | attr_to_modules.keys()) + + def __getattr__(name): + if name in submodules: + return importlib.import_module(f"{module_name}.{name}") + elif name in attr_to_modules: + submod = importlib.import_module(f"{module_name}.{attr_to_modules[name]}") + return getattr(submod, name) + else: + raise AttributeError(f"No {module_name} attribute {name}") + + def __dir__(): + return __all__ + + if os.environ.get("EAGER_IMPORT", ""): + for attr in set(attr_to_modules.keys()) | submodules: + __getattr__(attr) + + return __getattr__, __dir__, list(__all__) + + +class DelayedImportErrorModule(types.ModuleType): + def __init__(self, frame_data, *args, **kwargs): + self.__frame_data = frame_data + super().__init__(*args, **kwargs) + + def __getattr__(self, x): + if x in ("__class__", "__file__", "__frame_data"): + super().__getattr__(x) + else: + fd = self.__frame_data + raise ModuleNotFoundError( + f"No module named '{fd['spec']}'\n\n" + "This error is lazily reported, having originally occurred in\n" + f" File {fd['filename']}, line {fd['lineno']}, in {fd['function']}\n\n" + f"----> {''.join(fd['code_context'] or '').strip()}" + ) + + +def _lazy_import(fullname): + """Return a lazily imported proxy for a module or library. + + Warning + ------- + Importing using this function can currently cause trouble + when the user tries to import from a subpackage of a module before + the package is fully imported. In particular, this idiom may not work: + + np = lazy_import("numpy") + from numpy.lib import recfunctions + + This is due to a difference in the way Python's LazyLoader handles + subpackage imports compared to the normal import process. Hopefully + we will get Python's LazyLoader to fix this, or find a workaround. + In the meantime, this is a potential problem. + + The workaround is to import numpy before importing from the subpackage. + + Notes + ----- + We often see the following pattern:: + + def myfunc(): + import scipy as sp + sp.argmin(...) + .... + + This is to prevent a library, in this case `scipy`, from being + imported at function definition time, since that can be slow. + + This function provides a proxy module that, upon access, imports + the actual module. So the idiom equivalent to the above example is:: + + sp = lazy.load("scipy") + + def myfunc(): + sp.argmin(...) + .... + + The initial import time is fast because the actual import is delayed + until the first attribute is requested. The overall import time may + decrease as well for users that don't make use of large portions + of the library. + + Parameters + ---------- + fullname : str + The full name of the package or subpackage to import. For example:: + + sp = lazy.load("scipy") # import scipy as sp + spla = lazy.load("scipy.linalg") # import scipy.linalg as spla + + Returns + ------- + pm : importlib.util._LazyModule + Proxy module. Can be used like any regularly imported module. + Actual loading of the module occurs upon first attribute request. + + """ + try: + return sys.modules[fullname] + except: + pass + + # Not previously loaded -- look it up + spec = importlib.util.find_spec(fullname) + + if spec is None: + try: + parent = inspect.stack()[1] + frame_data = { + "spec": fullname, + "filename": parent.filename, + "lineno": parent.lineno, + "function": parent.function, + "code_context": parent.code_context, + } + return DelayedImportErrorModule(frame_data, "DelayedImportErrorModule") + finally: + del parent + + module = importlib.util.module_from_spec(spec) + sys.modules[fullname] = module + + loader = importlib.util.LazyLoader(spec.loader) + loader.exec_module(module) + + return module diff --git a/.venv/lib/python3.12/site-packages/networkx/relabel.py b/.venv/lib/python3.12/site-packages/networkx/relabel.py new file mode 100644 index 0000000000000000000000000000000000000000..4b870f726ef42e0bcaa7bf724e2ae6ab4145f288 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/networkx/relabel.py @@ -0,0 +1,285 @@ +import networkx as nx + +__all__ = ["convert_node_labels_to_integers", "relabel_nodes"] + + +@nx._dispatchable( + preserve_all_attrs=True, mutates_input={"not copy": 2}, returns_graph=True +) +def relabel_nodes(G, mapping, copy=True): + """Relabel the nodes of the graph G according to a given mapping. + + The original node ordering may not be preserved if `copy` is `False` and the + mapping includes overlap between old and new labels. + + Parameters + ---------- + G : graph + A NetworkX graph + + mapping : dictionary + A dictionary with the old labels as keys and new labels as values. + A partial mapping is allowed. Mapping 2 nodes to a single node is allowed. + Any non-node keys in the mapping are ignored. + + copy : bool (optional, default=True) + If True return a copy, or if False relabel the nodes in place. + + Examples + -------- + To create a new graph with nodes relabeled according to a given + dictionary: + + >>> G = nx.path_graph(3) + >>> sorted(G) + [0, 1, 2] + >>> mapping = {0: "a", 1: "b", 2: "c"} + >>> H = nx.relabel_nodes(G, mapping) + >>> sorted(H) + ['a', 'b', 'c'] + + Nodes can be relabeled with any hashable object, including numbers + and strings: + + >>> import string + >>> G = nx.path_graph(26) # nodes are integers 0 through 25 + >>> sorted(G)[:3] + [0, 1, 2] + >>> mapping = dict(zip(G, string.ascii_lowercase)) + >>> G = nx.relabel_nodes(G, mapping) # nodes are characters a through z + >>> sorted(G)[:3] + ['a', 'b', 'c'] + >>> mapping = dict(zip(G, range(1, 27))) + >>> G = nx.relabel_nodes(G, mapping) # nodes are integers 1 through 26 + >>> sorted(G)[:3] + [1, 2, 3] + + To perform a partial in-place relabeling, provide a dictionary + mapping only a subset of the nodes, and set the `copy` keyword + argument to False: + + >>> G = nx.path_graph(3) # nodes 0-1-2 + >>> mapping = {0: "a", 1: "b"} # 0->'a' and 1->'b' + >>> G = nx.relabel_nodes(G, mapping, copy=False) + >>> sorted(G, key=str) + [2, 'a', 'b'] + + A mapping can also be given as a function: + + >>> G = nx.path_graph(3) + >>> H = nx.relabel_nodes(G, lambda x: x**2) + >>> list(H) + [0, 1, 4] + + In a multigraph, relabeling two or more nodes to the same new node + will retain all edges, but may change the edge keys in the process: + + >>> G = nx.MultiGraph() + >>> G.add_edge(0, 1, value="a") # returns the key for this edge + 0 + >>> G.add_edge(0, 2, value="b") + 0 + >>> G.add_edge(0, 3, value="c") + 0 + >>> mapping = {1: 4, 2: 4, 3: 4} + >>> H = nx.relabel_nodes(G, mapping, copy=True) + >>> print(H[0]) + {4: {0: {'value': 'a'}, 1: {'value': 'b'}, 2: {'value': 'c'}}} + + This works for in-place relabeling too: + + >>> G = nx.relabel_nodes(G, mapping, copy=False) + >>> print(G[0]) + {4: {0: {'value': 'a'}, 1: {'value': 'b'}, 2: {'value': 'c'}}} + + Notes + ----- + Only the nodes specified in the mapping will be relabeled. + Any non-node keys in the mapping are ignored. + + The keyword setting copy=False modifies the graph in place. + Relabel_nodes avoids naming collisions by building a + directed graph from ``mapping`` which specifies the order of + relabelings. Naming collisions, such as a->b, b->c, are ordered + such that "b" gets renamed to "c" before "a" gets renamed "b". + In cases of circular mappings (e.g. a->b, b->a), modifying the + graph is not possible in-place and an exception is raised. + In that case, use copy=True. + + If a relabel operation on a multigraph would cause two or more + edges to have the same source, target and key, the second edge must + be assigned a new key to retain all edges. The new key is set + to the lowest non-negative integer not already used as a key + for edges between these two nodes. Note that this means non-numeric + keys may be replaced by numeric keys. + + See Also + -------- + convert_node_labels_to_integers + """ + # you can pass any callable e.g. f(old_label) -> new_label or + # e.g. str(old_label) -> new_label, but we'll just make a dictionary here regardless + m = {n: mapping(n) for n in G} if callable(mapping) else mapping + + if copy: + return _relabel_copy(G, m) + else: + return _relabel_inplace(G, m) + + +def _relabel_inplace(G, mapping): + if len(mapping.keys() & mapping.values()) > 0: + # labels sets overlap + # can we topological sort and still do the relabeling? + D = nx.DiGraph(list(mapping.items())) + D.remove_edges_from(nx.selfloop_edges(D)) + try: + nodes = reversed(list(nx.topological_sort(D))) + except nx.NetworkXUnfeasible as err: + raise nx.NetworkXUnfeasible( + "The node label sets are overlapping and no ordering can " + "resolve the mapping. Use copy=True." + ) from err + else: + # non-overlapping label sets, sort them in the order of G nodes + nodes = [n for n in G if n in mapping] + + multigraph = G.is_multigraph() + directed = G.is_directed() + + for old in nodes: + # Test that old is in both mapping and G, otherwise ignore. + try: + new = mapping[old] + G.add_node(new, **G.nodes[old]) + except KeyError: + continue + if new == old: + continue + if multigraph: + new_edges = [ + (new, new if old == target else target, key, data) + for (_, target, key, data) in G.edges(old, data=True, keys=True) + ] + if directed: + new_edges += [ + (new if old == source else source, new, key, data) + for (source, _, key, data) in G.in_edges(old, data=True, keys=True) + ] + # Ensure new edges won't overwrite existing ones + seen = set() + for i, (source, target, key, data) in enumerate(new_edges): + if target in G[source] and key in G[source][target]: + new_key = 0 if not isinstance(key, int | float) else key + while new_key in G[source][target] or (target, new_key) in seen: + new_key += 1 + new_edges[i] = (source, target, new_key, data) + seen.add((target, new_key)) + else: + new_edges = [ + (new, new if old == target else target, data) + for (_, target, data) in G.edges(old, data=True) + ] + if directed: + new_edges += [ + (new if old == source else source, new, data) + for (source, _, data) in G.in_edges(old, data=True) + ] + G.remove_node(old) + G.add_edges_from(new_edges) + return G + + +def _relabel_copy(G, mapping): + H = G.__class__() + H.add_nodes_from(mapping.get(n, n) for n in G) + H._node.update((mapping.get(n, n), d.copy()) for n, d in G.nodes.items()) + if G.is_multigraph(): + new_edges = [ + (mapping.get(n1, n1), mapping.get(n2, n2), k, d.copy()) + for (n1, n2, k, d) in G.edges(keys=True, data=True) + ] + + # check for conflicting edge-keys + undirected = not G.is_directed() + seen_edges = set() + for i, (source, target, key, data) in enumerate(new_edges): + while (source, target, key) in seen_edges: + if not isinstance(key, int | float): + key = 0 + key += 1 + seen_edges.add((source, target, key)) + if undirected: + seen_edges.add((target, source, key)) + new_edges[i] = (source, target, key, data) + + H.add_edges_from(new_edges) + else: + H.add_edges_from( + (mapping.get(n1, n1), mapping.get(n2, n2), d.copy()) + for (n1, n2, d) in G.edges(data=True) + ) + H.graph.update(G.graph) + return H + + +@nx._dispatchable(preserve_all_attrs=True, returns_graph=True) +def convert_node_labels_to_integers( + G, first_label=0, ordering="default", label_attribute=None +): + """Returns a copy of the graph G with the nodes relabeled using + consecutive integers. + + Parameters + ---------- + G : graph + A NetworkX graph + + first_label : int, optional (default=0) + An integer specifying the starting offset in numbering nodes. + The new integer labels are numbered first_label, ..., n-1+first_label. + + ordering : string + "default" : inherit node ordering from G.nodes() + "sorted" : inherit node ordering from sorted(G.nodes()) + "increasing degree" : nodes are sorted by increasing degree + "decreasing degree" : nodes are sorted by decreasing degree + + label_attribute : string, optional (default=None) + Name of node attribute to store old label. If None no attribute + is created. + + Notes + ----- + Node and edge attribute data are copied to the new (relabeled) graph. + + There is no guarantee that the relabeling of nodes to integers will + give the same two integers for two (even identical graphs). + Use the `ordering` argument to try to preserve the order. + + See Also + -------- + relabel_nodes + """ + N = G.number_of_nodes() + first_label + if ordering == "default": + mapping = dict(zip(G.nodes(), range(first_label, N))) + elif ordering == "sorted": + nlist = sorted(G.nodes()) + mapping = dict(zip(nlist, range(first_label, N))) + elif ordering == "increasing degree": + dv_pairs = [(d, n) for (n, d) in G.degree()] + dv_pairs.sort() # in-place sort from lowest to highest degree + mapping = dict(zip([n for d, n in dv_pairs], range(first_label, N))) + elif ordering == "decreasing degree": + dv_pairs = [(d, n) for (n, d) in G.degree()] + dv_pairs.sort() # in-place sort from lowest to highest degree + dv_pairs.reverse() + mapping = dict(zip([n for d, n in dv_pairs], range(first_label, N))) + else: + raise nx.NetworkXError(f"Unknown node ordering: {ordering}") + H = relabel_nodes(G, mapping) + # create node attribute with the old label + if label_attribute is not None: + nx.set_node_attributes(H, {v: k for k, v in mapping.items()}, label_attribute) + return H diff --git a/.venv/lib/python3.12/site-packages/numpy-2.3.3.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/numpy-2.3.3.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy-2.3.3.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/numpy-2.3.3.dist-info/LICENSE.txt b/.venv/lib/python3.12/site-packages/numpy-2.3.3.dist-info/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..284458b0bb0351e3212358af1640cfab29ef1de3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy-2.3.3.dist-info/LICENSE.txt @@ -0,0 +1,971 @@ +Copyright (c) 2005-2025, NumPy Developers. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of the NumPy Developers nor the names of any + 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. + +---- + +The NumPy repository and source distributions bundle several libraries that are +compatibly licensed. We list these here. + +Name: lapack-lite +Files: numpy/linalg/lapack_lite/* +License: BSD-3-Clause + For details, see numpy/linalg/lapack_lite/LICENSE.txt + +Name: dragon4 +Files: numpy/_core/src/multiarray/dragon4.c +License: MIT + For license text, see numpy/_core/src/multiarray/dragon4.c + +Name: libdivide +Files: numpy/_core/include/numpy/libdivide/* +License: Zlib + For license text, see numpy/_core/include/numpy/libdivide/LICENSE.txt + + +Note that the following files are vendored in the repository and sdist but not +installed in built numpy packages: + +Name: Meson +Files: vendored-meson/meson/* +License: Apache 2.0 + For license text, see vendored-meson/meson/COPYING + +Name: spin +Files: .spin/cmds.py +License: BSD-3 + For license text, see .spin/LICENSE + +Name: tempita +Files: numpy/_build_utils/tempita/* +License: MIT + For details, see numpy/_build_utils/tempita/LICENCE.txt + +---- + +This binary distribution of NumPy also bundles the following software: + + +Name: OpenBLAS +Files: numpy.libs/libscipy_openblas*.so +Description: bundled as a dynamically linked library +Availability: https://github.com/OpenMathLib/OpenBLAS/ +License: BSD-3-Clause + Copyright (c) 2011-2014, The OpenBLAS Project + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. Neither the name of the OpenBLAS project nor the names of + its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Name: LAPACK +Files: numpy.libs/libscipy_openblas*.so +Description: bundled in OpenBLAS +Availability: https://github.com/OpenMathLib/OpenBLAS/ +License: BSD-3-Clause-Open-MPI + Copyright (c) 1992-2013 The University of Tennessee and The University + of Tennessee Research Foundation. All rights + reserved. + Copyright (c) 2000-2013 The University of California Berkeley. All + rights reserved. + Copyright (c) 2006-2013 The University of Colorado Denver. All rights + reserved. + + $COPYRIGHT$ + + Additional copyrights may follow + + $HEADER$ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer listed + in this license in the documentation and/or other materials + provided with the distribution. + + - Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + The copyright holders provide no reassurances that the source code + provided does not infringe any patent, copyright, or any other + intellectual property rights of third parties. The copyright holders + disclaim any liability to any recipient for claims brought against + recipient by any third party for infringement of that parties + intellectual property rights. + + 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. + + +Name: GCC runtime library +Files: numpy.libs/libgfortran*.so +Description: dynamically linked to files compiled with gcc +Availability: https://gcc.gnu.org/git/?p=gcc.git;a=tree;f=libgfortran +License: GPL-3.0-or-later WITH GCC-exception-3.1 + Copyright (C) 2002-2017 Free Software Foundation, Inc. + + Libgfortran is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Libgfortran is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + . + +---- + +Full text of license texts referred to above follows (that they are +listed below does not necessarily imply the conditions apply to the +present binary release): + +---- + +GCC RUNTIME LIBRARY EXCEPTION + +Version 3.1, 31 March 2009 + +Copyright (C) 2009 Free Software Foundation, Inc. + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +This GCC Runtime Library Exception ("Exception") is an additional +permission under section 7 of the GNU General Public License, version +3 ("GPLv3"). It applies to a given file (the "Runtime Library") that +bears a notice placed by the copyright holder of the file stating that +the file is governed by GPLv3 along with this Exception. + +When you use GCC to compile a program, GCC may combine portions of +certain GCC header files and runtime libraries with the compiled +program. The purpose of this Exception is to allow compilation of +non-GPL (including proprietary) programs to use, in this way, the +header files and runtime libraries covered by this Exception. + +0. Definitions. + +A file is an "Independent Module" if it either requires the Runtime +Library for execution after a Compilation Process, or makes use of an +interface provided by the Runtime Library, but is not otherwise based +on the Runtime Library. + +"GCC" means a version of the GNU Compiler Collection, with or without +modifications, governed by version 3 (or a specified later version) of +the GNU General Public License (GPL) with the option of using any +subsequent versions published by the FSF. + +"GPL-compatible Software" is software whose conditions of propagation, +modification and use would permit combination with GCC in accord with +the license of GCC. + +"Target Code" refers to output from any compiler for a real or virtual +target processor architecture, in executable form or suitable for +input to an assembler, loader, linker and/or execution +phase. Notwithstanding that, Target Code does not include data in any +format that is used as a compiler intermediate representation, or used +for producing a compiler intermediate representation. + +The "Compilation Process" transforms code entirely represented in +non-intermediate languages designed for human-written code, and/or in +Java Virtual Machine byte code, into Target Code. Thus, for example, +use of source code generators and preprocessors need not be considered +part of the Compilation Process, since the Compilation Process can be +understood as starting with the output of the generators or +preprocessors. + +A Compilation Process is "Eligible" if it is done using GCC, alone or +with other GPL-compatible software, or if it is done without using any +work based on GCC. For example, using non-GPL-compatible Software to +optimize any GCC intermediate representations would not qualify as an +Eligible Compilation Process. + +1. Grant of Additional Permission. + +You have permission to propagate a work of Target Code formed by +combining the Runtime Library with Independent Modules, even if such +propagation would otherwise violate the terms of GPLv3, provided that +all Target Code was generated by Eligible Compilation Processes. You +may then convey such a combination under terms of your choice, +consistent with the licensing of the Independent Modules. + +2. No Weakening of GCC Copyleft. + +The availability of this Exception does not imply any general +presumption that third-party software is unaffected by the copyleft +requirements of the license of GCC. + +---- + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + +Name: libquadmath +Files: numpy.libs/libquadmath*.so +Description: dynamically linked to files compiled with gcc +Availability: https://gcc.gnu.org/git/?p=gcc.git;a=tree;f=libquadmath +License: LGPL-2.1-or-later + + GCC Quad-Precision Math Library + Copyright (C) 2010-2019 Free Software Foundation, Inc. + Written by Francois-Xavier Coudert + + This file is part of the libquadmath library. + Libquadmath is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + Libquadmath is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html diff --git a/.venv/lib/python3.12/site-packages/numpy-2.3.3.dist-info/METADATA b/.venv/lib/python3.12/site-packages/numpy-2.3.3.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..f9788044005fd4bbf518b0407f25025ef0d6b8cb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy-2.3.3.dist-info/METADATA @@ -0,0 +1,1093 @@ +Metadata-Version: 2.1 +Name: numpy +Version: 2.3.3 +Summary: Fundamental package for array computing in Python +Author: Travis E. Oliphant et al. +Maintainer-Email: NumPy Developers +License: Copyright (c) 2005-2025, NumPy Developers. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of the NumPy Developers nor the names of any + 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. + + ---- + + The NumPy repository and source distributions bundle several libraries that are + compatibly licensed. We list these here. + + Name: lapack-lite + Files: numpy/linalg/lapack_lite/* + License: BSD-3-Clause + For details, see numpy/linalg/lapack_lite/LICENSE.txt + + Name: dragon4 + Files: numpy/_core/src/multiarray/dragon4.c + License: MIT + For license text, see numpy/_core/src/multiarray/dragon4.c + + Name: libdivide + Files: numpy/_core/include/numpy/libdivide/* + License: Zlib + For license text, see numpy/_core/include/numpy/libdivide/LICENSE.txt + + + Note that the following files are vendored in the repository and sdist but not + installed in built numpy packages: + + Name: Meson + Files: vendored-meson/meson/* + License: Apache 2.0 + For license text, see vendored-meson/meson/COPYING + + Name: spin + Files: .spin/cmds.py + License: BSD-3 + For license text, see .spin/LICENSE + + Name: tempita + Files: numpy/_build_utils/tempita/* + License: MIT + For details, see numpy/_build_utils/tempita/LICENCE.txt + + ---- + + This binary distribution of NumPy also bundles the following software: + + + Name: OpenBLAS + Files: numpy.libs/libscipy_openblas*.so + Description: bundled as a dynamically linked library + Availability: https://github.com/OpenMathLib/OpenBLAS/ + License: BSD-3-Clause + Copyright (c) 2011-2014, The OpenBLAS Project + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. Neither the name of the OpenBLAS project nor the names of + its contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + Name: LAPACK + Files: numpy.libs/libscipy_openblas*.so + Description: bundled in OpenBLAS + Availability: https://github.com/OpenMathLib/OpenBLAS/ + License: BSD-3-Clause-Open-MPI + Copyright (c) 1992-2013 The University of Tennessee and The University + of Tennessee Research Foundation. All rights + reserved. + Copyright (c) 2000-2013 The University of California Berkeley. All + rights reserved. + Copyright (c) 2006-2013 The University of Colorado Denver. All rights + reserved. + + $COPYRIGHT$ + + Additional copyrights may follow + + $HEADER$ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer listed + in this license in the documentation and/or other materials + provided with the distribution. + + - Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + The copyright holders provide no reassurances that the source code + provided does not infringe any patent, copyright, or any other + intellectual property rights of third parties. The copyright holders + disclaim any liability to any recipient for claims brought against + recipient by any third party for infringement of that parties + intellectual property rights. + + 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. + + + Name: GCC runtime library + Files: numpy.libs/libgfortran*.so + Description: dynamically linked to files compiled with gcc + Availability: https://gcc.gnu.org/git/?p=gcc.git;a=tree;f=libgfortran + License: GPL-3.0-or-later WITH GCC-exception-3.1 + Copyright (C) 2002-2017 Free Software Foundation, Inc. + + Libgfortran is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Libgfortran is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + Under Section 7 of GPL version 3, you are granted additional + permissions described in the GCC Runtime Library Exception, version + 3.1, as published by the Free Software Foundation. + + You should have received a copy of the GNU General Public License and + a copy of the GCC Runtime Library Exception along with this program; + see the files COPYING3 and COPYING.RUNTIME respectively. If not, see + . + + ---- + + Full text of license texts referred to above follows (that they are + listed below does not necessarily imply the conditions apply to the + present binary release): + + ---- + + GCC RUNTIME LIBRARY EXCEPTION + + Version 3.1, 31 March 2009 + + Copyright (C) 2009 Free Software Foundation, Inc. + + Everyone is permitted to copy and distribute verbatim copies of this + license document, but changing it is not allowed. + + This GCC Runtime Library Exception ("Exception") is an additional + permission under section 7 of the GNU General Public License, version + 3 ("GPLv3"). It applies to a given file (the "Runtime Library") that + bears a notice placed by the copyright holder of the file stating that + the file is governed by GPLv3 along with this Exception. + + When you use GCC to compile a program, GCC may combine portions of + certain GCC header files and runtime libraries with the compiled + program. The purpose of this Exception is to allow compilation of + non-GPL (including proprietary) programs to use, in this way, the + header files and runtime libraries covered by this Exception. + + 0. Definitions. + + A file is an "Independent Module" if it either requires the Runtime + Library for execution after a Compilation Process, or makes use of an + interface provided by the Runtime Library, but is not otherwise based + on the Runtime Library. + + "GCC" means a version of the GNU Compiler Collection, with or without + modifications, governed by version 3 (or a specified later version) of + the GNU General Public License (GPL) with the option of using any + subsequent versions published by the FSF. + + "GPL-compatible Software" is software whose conditions of propagation, + modification and use would permit combination with GCC in accord with + the license of GCC. + + "Target Code" refers to output from any compiler for a real or virtual + target processor architecture, in executable form or suitable for + input to an assembler, loader, linker and/or execution + phase. Notwithstanding that, Target Code does not include data in any + format that is used as a compiler intermediate representation, or used + for producing a compiler intermediate representation. + + The "Compilation Process" transforms code entirely represented in + non-intermediate languages designed for human-written code, and/or in + Java Virtual Machine byte code, into Target Code. Thus, for example, + use of source code generators and preprocessors need not be considered + part of the Compilation Process, since the Compilation Process can be + understood as starting with the output of the generators or + preprocessors. + + A Compilation Process is "Eligible" if it is done using GCC, alone or + with other GPL-compatible software, or if it is done without using any + work based on GCC. For example, using non-GPL-compatible Software to + optimize any GCC intermediate representations would not qualify as an + Eligible Compilation Process. + + 1. Grant of Additional Permission. + + You have permission to propagate a work of Target Code formed by + combining the Runtime Library with Independent Modules, even if such + propagation would otherwise violate the terms of GPLv3, provided that + all Target Code was generated by Eligible Compilation Processes. You + may then convey such a combination under terms of your choice, + consistent with the licensing of the Independent Modules. + + 2. No Weakening of GCC Copyleft. + + The availability of this Exception does not imply any general + presumption that third-party software is unaffected by the copyleft + requirements of the license of GCC. + + ---- + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for + software and other kinds of works. + + The licenses for most software and other practical works are designed + to take away your freedom to share and change the works. By contrast, + the GNU General Public License is intended to guarantee your freedom to + share and change all versions of a program--to make sure it remains free + software for all its users. We, the Free Software Foundation, use the + GNU General Public License for most of our software; it applies also to + any other work released this way by its authors. You can apply it to + your programs, too. + + When we speak of free software, we are referring to freedom, not + price. Our General Public Licenses are designed to make sure that you + have the freedom to distribute copies of free software (and charge for + them if you wish), that you receive source code or can get it if you + want it, that you can change the software or use pieces of it in new + free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you + these rights or asking you to surrender the rights. Therefore, you have + certain responsibilities if you distribute copies of the software, or if + you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether + gratis or for a fee, you must pass on to the recipients the same + freedoms that you received. You must make sure that they, too, receive + or can get the source code. And you must show them these terms so they + know their rights. + + Developers that use the GNU GPL protect your rights with two steps: + (1) assert copyright on the software, and (2) offer you this License + giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains + that there is no warranty for this free software. For both users' and + authors' sake, the GPL requires that modified versions be marked as + changed, so that their problems will not be attributed erroneously to + authors of previous versions. + + Some devices are designed to deny users access to install or run + modified versions of the software inside them, although the manufacturer + can do so. This is fundamentally incompatible with the aim of + protecting users' freedom to change the software. The systematic + pattern of such abuse occurs in the area of products for individuals to + use, which is precisely where it is most unacceptable. Therefore, we + have designed this version of the GPL to prohibit the practice for those + products. If such problems arise substantially in other domains, we + stand ready to extend this provision to those domains in future versions + of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. + States should not allow patents to restrict development and use of + software on general-purpose computers, but in those that do, we wish to + avoid the special danger that patents applied to a free program could + make it effectively proprietary. To prevent this, the GPL assures that + patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and + modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of + works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this + License. Each licensee is addressed as "you". "Licensees" and + "recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work + in a fashion requiring copyright permission, other than the making of an + exact copy. The resulting work is called a "modified version" of the + earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based + on the Program. + + To "propagate" a work means to do anything with it that, without + permission, would make you directly or secondarily liable for + infringement under applicable copyright law, except executing it on a + computer or modifying a private copy. Propagation includes copying, + distribution (with or without modification), making available to the + public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other + parties to make or receive copies. Mere interaction with a user through + a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" + to the extent that it includes a convenient and prominently visible + feature that (1) displays an appropriate copyright notice, and (2) + tells the user that there is no warranty for the work (except to the + extent that warranties are provided), that licensees may convey the + work under this License, and how to view a copy of this License. If + the interface presents a list of user commands or options, such as a + menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work + for making modifications to it. "Object code" means any non-source + form of a work. + + A "Standard Interface" means an interface that either is an official + standard defined by a recognized standards body, or, in the case of + interfaces specified for a particular programming language, one that + is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other + than the work as a whole, that (a) is included in the normal form of + packaging a Major Component, but which is not part of that Major + Component, and (b) serves only to enable use of the work with that + Major Component, or to implement a Standard Interface for which an + implementation is available to the public in source code form. A + "Major Component", in this context, means a major essential component + (kernel, window system, and so on) of the specific operating system + (if any) on which the executable work runs, or a compiler used to + produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all + the source code needed to generate, install, and (for an executable + work) run the object code and to modify the work, including scripts to + control those activities. However, it does not include the work's + System Libraries, or general-purpose tools or generally available free + programs which are used unmodified in performing those activities but + which are not part of the work. For example, Corresponding Source + includes interface definition files associated with source files for + the work, and the source code for shared libraries and dynamically + linked subprograms that the work is specifically designed to require, + such as by intimate data communication or control flow between those + subprograms and other parts of the work. + + The Corresponding Source need not include anything that users + can regenerate automatically from other parts of the Corresponding + Source. + + The Corresponding Source for a work in source code form is that + same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of + copyright on the Program, and are irrevocable provided the stated + conditions are met. This License explicitly affirms your unlimited + permission to run the unmodified Program. The output from running a + covered work is covered by this License only if the output, given its + content, constitutes a covered work. This License acknowledges your + rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not + convey, without conditions so long as your license otherwise remains + in force. You may convey covered works to others for the sole purpose + of having them make modifications exclusively for you, or provide you + with facilities for running those works, provided that you comply with + the terms of this License in conveying all material for which you do + not control copyright. Those thus making or running the covered works + for you must do so exclusively on your behalf, under your direction + and control, on terms that prohibit them from making any copies of + your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under + the conditions stated below. Sublicensing is not allowed; section 10 + makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological + measure under any applicable law fulfilling obligations under article + 11 of the WIPO copyright treaty adopted on 20 December 1996, or + similar laws prohibiting or restricting circumvention of such + measures. + + When you convey a covered work, you waive any legal power to forbid + circumvention of technological measures to the extent such circumvention + is effected by exercising rights under this License with respect to + the covered work, and you disclaim any intention to limit operation or + modification of the work as a means of enforcing, against the work's + users, your or third parties' legal rights to forbid circumvention of + technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you + receive it, in any medium, provided that you conspicuously and + appropriately publish on each copy an appropriate copyright notice; + keep intact all notices stating that this License and any + non-permissive terms added in accord with section 7 apply to the code; + keep intact all notices of the absence of any warranty; and give all + recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, + and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to + produce it from the Program, in the form of source code under the + terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent + works, which are not by their nature extensions of the covered work, + and which are not combined with it such as to form a larger program, + in or on a volume of a storage or distribution medium, is called an + "aggregate" if the compilation and its resulting copyright are not + used to limit the access or legal rights of the compilation's users + beyond what the individual works permit. Inclusion of a covered work + in an aggregate does not cause this License to apply to the other + parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms + of sections 4 and 5, provided that you also convey the + machine-readable Corresponding Source under the terms of this License, + in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded + from the Corresponding Source as a System Library, need not be + included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any + tangible personal property which is normally used for personal, family, + or household purposes, or (2) anything designed or sold for incorporation + into a dwelling. In determining whether a product is a consumer product, + doubtful cases shall be resolved in favor of coverage. For a particular + product received by a particular user, "normally used" refers to a + typical or common use of that class of product, regardless of the status + of the particular user or of the way in which the particular user + actually uses, or expects or is expected to use, the product. A product + is a consumer product regardless of whether the product has substantial + commercial, industrial or non-consumer uses, unless such uses represent + the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, + procedures, authorization keys, or other information required to install + and execute modified versions of a covered work in that User Product from + a modified version of its Corresponding Source. The information must + suffice to ensure that the continued functioning of the modified object + code is in no case prevented or interfered with solely because + modification has been made. + + If you convey an object code work under this section in, or with, or + specifically for use in, a User Product, and the conveying occurs as + part of a transaction in which the right of possession and use of the + User Product is transferred to the recipient in perpetuity or for a + fixed term (regardless of how the transaction is characterized), the + Corresponding Source conveyed under this section must be accompanied + by the Installation Information. But this requirement does not apply + if neither you nor any third party retains the ability to install + modified object code on the User Product (for example, the work has + been installed in ROM). + + The requirement to provide Installation Information does not include a + requirement to continue to provide support service, warranty, or updates + for a work that has been modified or installed by the recipient, or for + the User Product in which it has been modified or installed. Access to a + network may be denied when the modification itself materially and + adversely affects the operation of the network or violates the rules and + protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, + in accord with this section must be in a format that is publicly + documented (and with an implementation available to the public in + source code form), and must require no special password or key for + unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this + License by making exceptions from one or more of its conditions. + Additional permissions that are applicable to the entire Program shall + be treated as though they were included in this License, to the extent + that they are valid under applicable law. If additional permissions + apply only to part of the Program, that part may be used separately + under those permissions, but the entire Program remains governed by + this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option + remove any additional permissions from that copy, or from any part of + it. (Additional permissions may be written to require their own + removal in certain cases when you modify the work.) You may place + additional permissions on material, added by you to a covered work, + for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you + add to a covered work, you may (if authorized by the copyright holders of + that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further + restrictions" within the meaning of section 10. If the Program as you + received it, or any part of it, contains a notice stating that it is + governed by this License along with a term that is a further + restriction, you may remove that term. If a license document contains + a further restriction but permits relicensing or conveying under this + License, you may add to a covered work material governed by the terms + of that license document, provided that the further restriction does + not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you + must place, in the relevant source files, a statement of the + additional terms that apply to those files, or a notice indicating + where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the + form of a separately written license, or stated as exceptions; + the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly + provided under this License. Any attempt otherwise to propagate or + modify it is void, and will automatically terminate your rights under + this License (including any patent licenses granted under the third + paragraph of section 11). + + However, if you cease all violation of this License, then your + license from a particular copyright holder is reinstated (a) + provisionally, unless and until the copyright holder explicitly and + finally terminates your license, and (b) permanently, if the copyright + holder fails to notify you of the violation by some reasonable means + prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is + reinstated permanently if the copyright holder notifies you of the + violation by some reasonable means, this is the first time you have + received notice of violation of this License (for any work) from that + copyright holder, and you cure the violation prior to 30 days after + your receipt of the notice. + + Termination of your rights under this section does not terminate the + licenses of parties who have received copies or rights from you under + this License. If your rights have been terminated and not permanently + reinstated, you do not qualify to receive new licenses for the same + material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or + run a copy of the Program. Ancillary propagation of a covered work + occurring solely as a consequence of using peer-to-peer transmission + to receive a copy likewise does not require acceptance. However, + nothing other than this License grants you permission to propagate or + modify any covered work. These actions infringe copyright if you do + not accept this License. Therefore, by modifying or propagating a + covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically + receives a license from the original licensors, to run, modify and + propagate that work, subject to this License. You are not responsible + for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an + organization, or substantially all assets of one, or subdividing an + organization, or merging organizations. If propagation of a covered + work results from an entity transaction, each party to that + transaction who receives a copy of the work also receives whatever + licenses to the work the party's predecessor in interest had or could + give under the previous paragraph, plus a right to possession of the + Corresponding Source of the work from the predecessor in interest, if + the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the + rights granted or affirmed under this License. For example, you may + not impose a license fee, royalty, or other charge for exercise of + rights granted under this License, and you may not initiate litigation + (including a cross-claim or counterclaim in a lawsuit) alleging that + any patent claim is infringed by making, using, selling, offering for + sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this + License of the Program or a work on which the Program is based. The + work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims + owned or controlled by the contributor, whether already acquired or + hereafter acquired, that would be infringed by some manner, permitted + by this License, of making, using, or selling its contributor version, + but do not include claims that would be infringed only as a + consequence of further modification of the contributor version. For + purposes of this definition, "control" includes the right to grant + patent sublicenses in a manner consistent with the requirements of + this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free + patent license under the contributor's essential patent claims, to + make, use, sell, offer for sale, import and otherwise run, modify and + propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express + agreement or commitment, however denominated, not to enforce a patent + (such as an express permission to practice a patent or covenant not to + sue for patent infringement). To "grant" such a patent license to a + party means to make such an agreement or commitment not to enforce a + patent against the party. + + If you convey a covered work, knowingly relying on a patent license, + and the Corresponding Source of the work is not available for anyone + to copy, free of charge and under the terms of this License, through a + publicly available network server or other readily accessible means, + then you must either (1) cause the Corresponding Source to be so + available, or (2) arrange to deprive yourself of the benefit of the + patent license for this particular work, or (3) arrange, in a manner + consistent with the requirements of this License, to extend the patent + license to downstream recipients. "Knowingly relying" means you have + actual knowledge that, but for the patent license, your conveying the + covered work in a country, or your recipient's use of the covered work + in a country, would infringe one or more identifiable patents in that + country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or + arrangement, you convey, or propagate by procuring conveyance of, a + covered work, and grant a patent license to some of the parties + receiving the covered work authorizing them to use, propagate, modify + or convey a specific copy of the covered work, then the patent license + you grant is automatically extended to all recipients of the covered + work and works based on it. + + A patent license is "discriminatory" if it does not include within + the scope of its coverage, prohibits the exercise of, or is + conditioned on the non-exercise of one or more of the rights that are + specifically granted under this License. You may not convey a covered + work if you are a party to an arrangement with a third party that is + in the business of distributing software, under which you make payment + to the third party based on the extent of your activity of conveying + the work, and under which the third party grants, to any of the + parties who would receive the covered work from you, a discriminatory + patent license (a) in connection with copies of the covered work + conveyed by you (or copies made from those copies), or (b) primarily + for and in connection with specific products or compilations that + contain the covered work, unless you entered into that arrangement, + or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting + any implied license or other defenses to infringement that may + otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot convey a + covered work so as to satisfy simultaneously your obligations under this + License and any other pertinent obligations, then as a consequence you may + not convey it at all. For example, if you agree to terms that obligate you + to collect a royalty for further conveying from those to whom you convey + the Program, the only way you could satisfy both those terms and this + License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have + permission to link or combine any covered work with a work licensed + under version 3 of the GNU Affero General Public License into a single + combined work, and to convey the resulting work. The terms of this + License will continue to apply to the part which is the covered work, + but the special requirements of the GNU Affero General Public License, + section 13, concerning interaction through a network will apply to the + combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of + the GNU General Public License from time to time. Such new versions will + be similar in spirit to the present version, but may differ in detail to + address new problems or concerns. + + Each version is given a distinguishing version number. If the + Program specifies that a certain numbered version of the GNU General + Public License "or any later version" applies to it, you have the + option of following the terms and conditions either of that numbered + version or of any later version published by the Free Software + Foundation. If the Program does not specify a version number of the + GNU General Public License, you may choose any version ever published + by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future + versions of the GNU General Public License can be used, that proxy's + public statement of acceptance of a version permanently authorizes you + to choose that version for the Program. + + Later license versions may give you additional or different + permissions. However, no additional obligations are imposed on any + author or copyright holder as a result of your choosing to follow a + later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY + APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT + HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY + OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM + IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF + ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING + WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS + THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY + GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE + USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF + DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD + PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), + EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF + SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided + above cannot be given local legal effect according to their terms, + reviewing courts shall apply local law that most closely approximates + an absolute waiver of all civil liability in connection with the + Program, unless a warranty or assumption of liability accompanies a + copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest + possible use to the public, the best way to achieve this is to make it + free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest + to attach them to the start of each source file to most effectively + state the exclusion of warranty; and each file should have at least + the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short + notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + + The hypothetical commands `show w' and `show c' should show the appropriate + parts of the General Public License. Of course, your program's commands + might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, + if any, to sign a "copyright disclaimer" for the program, if necessary. + For more information on this, and how to apply and follow the GNU GPL, see + . + + The GNU General Public License does not permit incorporating your program + into proprietary programs. If your program is a subroutine library, you + may consider it more useful to permit linking proprietary applications with + the library. If this is what you want to do, use the GNU Lesser General + Public License instead of this License. But first, please read + . + + Name: libquadmath + Files: numpy.libs/libquadmath*.so + Description: dynamically linked to files compiled with gcc + Availability: https://gcc.gnu.org/git/?p=gcc.git;a=tree;f=libquadmath + License: LGPL-2.1-or-later + + GCC Quad-Precision Math Library + Copyright (C) 2010-2019 Free Software Foundation, Inc. + Written by Francois-Xavier Coudert + + This file is part of the libquadmath library. + Libquadmath is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + Libquadmath is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html + +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Science/Research +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Programming Language :: C +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +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 :: Only +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Topic :: Software Development +Classifier: Topic :: Scientific/Engineering +Classifier: Typing :: Typed +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX +Classifier: Operating System :: Unix +Classifier: Operating System :: MacOS +Project-URL: homepage, https://numpy.org +Project-URL: documentation, https://numpy.org/doc/ +Project-URL: source, https://github.com/numpy/numpy +Project-URL: download, https://pypi.org/project/numpy/#files +Project-URL: tracker, https://github.com/numpy/numpy/issues +Project-URL: release notes, https://numpy.org/doc/stable/release +Requires-Python: >=3.11 +Description-Content-Type: text/markdown + +

+ +


+ + +[![Powered by NumFOCUS](https://img.shields.io/badge/powered%20by-NumFOCUS-orange.svg?style=flat&colorA=E1523D&colorB=007D8A)]( +https://numfocus.org) +[![PyPI Downloads](https://img.shields.io/pypi/dm/numpy.svg?label=PyPI%20downloads)]( +https://pypi.org/project/numpy/) +[![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/numpy.svg?label=Conda%20downloads)]( +https://anaconda.org/conda-forge/numpy) +[![Stack Overflow](https://img.shields.io/badge/stackoverflow-Ask%20questions-blue.svg)]( +https://stackoverflow.com/questions/tagged/numpy) +[![Nature Paper](https://img.shields.io/badge/DOI-10.1038%2Fs41586--020--2649--2-blue)]( +https://doi.org/10.1038/s41586-020-2649-2) +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/numpy/numpy/badge)](https://securityscorecards.dev/viewer/?uri=github.com/numpy/numpy) +[![Typing](https://img.shields.io/pypi/types/numpy)](https://pypi.org/project/numpy/) + + +NumPy is the fundamental package for scientific computing with Python. + +- **Website:** https://numpy.org +- **Documentation:** https://numpy.org/doc +- **Mailing list:** https://mail.python.org/mailman/listinfo/numpy-discussion +- **Source code:** https://github.com/numpy/numpy +- **Contributing:** https://numpy.org/devdocs/dev/index.html +- **Bug reports:** https://github.com/numpy/numpy/issues +- **Report a security vulnerability:** https://tidelift.com/docs/security + +It provides: + +- a powerful N-dimensional array object +- sophisticated (broadcasting) functions +- tools for integrating C/C++ and Fortran code +- useful linear algebra, Fourier transform, and random number capabilities + +Testing: + +NumPy requires `pytest` and `hypothesis`. Tests can then be run after installation with: + + python -c "import numpy, sys; sys.exit(numpy.test() is False)" + +Code of Conduct +---------------------- + +NumPy is a community-driven open source project developed by a diverse group of +[contributors](https://numpy.org/teams/). The NumPy leadership has made a strong +commitment to creating an open, inclusive, and positive community. Please read the +[NumPy Code of Conduct](https://numpy.org/code-of-conduct/) for guidance on how to interact +with others in a way that makes our community thrive. + +Call for Contributions +---------------------- + +The NumPy project welcomes your expertise and enthusiasm! + +Small improvements or fixes are always appreciated. If you are considering larger contributions +to the source code, please contact us through the [mailing +list](https://mail.python.org/mailman/listinfo/numpy-discussion) first. + +Writing code isn’t the only way to contribute to NumPy. You can also: +- review pull requests +- help us stay on top of new and old issues +- develop tutorials, presentations, and other educational materials +- maintain and improve [our website](https://github.com/numpy/numpy.org) +- develop graphic design for our brand assets and promotional materials +- translate website content +- help with outreach and onboard new contributors +- write grant proposals and help with other fundraising efforts + +For more information about the ways you can contribute to NumPy, visit [our website](https://numpy.org/contribute/). +If you’re unsure where to start or how your skills fit in, reach out! You can +ask on the mailing list or here, on GitHub, by opening a new issue or leaving a +comment on a relevant issue that is already open. + +Our preferred channels of communication are all public, but if you’d like to +speak to us in private first, contact our community coordinators at +numpy-team@googlegroups.com or on Slack (write numpy-team@googlegroups.com for +an invitation). + +We also have a biweekly community call, details of which are announced on the +mailing list. You are very welcome to join. + +If you are new to contributing to open source, [this +guide](https://opensource.guide/how-to-contribute/) helps explain why, what, +and how to successfully get involved. diff --git a/.venv/lib/python3.12/site-packages/numpy-2.3.3.dist-info/RECORD b/.venv/lib/python3.12/site-packages/numpy-2.3.3.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..2694efc9fbde74e106dacf00ca65ceae1fe63d8d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy-2.3.3.dist-info/RECORD @@ -0,0 +1,905 @@ +../../../bin/f2py,sha256=4CIq8D2pRvj_UhP18sZ0MYUitvpW5tv0Q1mpLe268_c,333 +../../../bin/numpy-config,sha256=WEfgVPGxGPfZ2_ZX9-tSY-BDxbb_lQyp2o5hVgBrdsA,333 +numpy-2.3.3.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +numpy-2.3.3.dist-info/LICENSE.txt,sha256=IEajEw5QsRwBZZs6DZY-auC3Q2_46Jy8_Z6HvGES1ZU,47768 +numpy-2.3.3.dist-info/METADATA,sha256=Zh7Sk4Ex4WGl9yMJnHI25wnnBd7IVtHvN1J9N9CsTcY,62117 +numpy-2.3.3.dist-info/RECORD,, +numpy-2.3.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +numpy-2.3.3.dist-info/WHEEL,sha256=rJCplGeGFjRoUvxJWhVONRgyGYGZoOkhyej0s3KyjoI,138 +numpy-2.3.3.dist-info/entry_points.txt,sha256=7Cb63gyL2sIRpsHdADpl6xaIW5JTlUI-k_yqEVr0BSw,220 +numpy.libs/libgfortran-040039e1-0352e75f.so.5.0.0,sha256=xgkASOzMdjUiwS7wFvgdprYnyzoET1XPBHmoOcQcCYA,2833617 +numpy.libs/libquadmath-96973f99-934c22de.so.0.0.0,sha256=btUTf0Enga14Y0OftUNhP2ILQ8MrYykqACkkYWL1u8Y,250985 +numpy.libs/libscipy_openblas64_-8fb3d286.so,sha256=N7prNzoi_0KETgFPXcu0KBg_IyX9mTNhtB_M9oJBMz0,25050385 +numpy/__config__.py,sha256=y4ewV7-YUXH7wquW-VHV-E1dVrJRPCESUK76xy73RJA,5281 +numpy/__config__.pyi,sha256=7nE-kUNs2lWPIpofTastbf2PCMgCka7FCiK5jrFkDYE,2367 +numpy/__init__.cython-30.pxd,sha256=qT7d9_TWkj4UsfpY1uaBUmcYflptcjZfDGZsYJth8rU,47123 +numpy/__init__.pxd,sha256=BFYYkcQUcrl0Ee8ReoQiA0wgtxsWeIGovC8jYeEw5qg,43758 +numpy/__init__.py,sha256=gjanU4Bds0wp75zQNpTgr4g7YyXrT9JGzIPSvguEfok,25226 +numpy/__init__.pyi,sha256=lFZru8V_KWUuK-oD5tgqCNy1pJSZ-aKyJASjncTlRGo,219154 +numpy/_array_api_info.py,sha256=NzJSuf8vutjGSqiqahq3jRI3SxMX4X1cva4J6dFv4EU,10354 +numpy/_array_api_info.pyi,sha256=QP_tYDbjtTOPtJECk3ehRXOQ24QM8TZjAfWX8XAsZCM,4864 +numpy/_configtool.py,sha256=EFRJ3pazTxYhE9op-ocWyKTLZrrpFhfmmS_tWrq8Cxo,1007 +numpy/_configtool.pyi,sha256=d4f22QGwpb1ZtDk-1Sn72ftvo4incC5E2JAikmjzfJI,24 +numpy/_core/__init__.py,sha256=yJ0iy1fXk9ogCFnflCWzBBLwlKSS-xlQWCpWCozaT6c,5542 +numpy/_core/__init__.pyi,sha256=Mj2I4BtqBVNUZVs5o1T58Z7wSaWjfhX0nCl-a0ULjgA,86 +numpy/_core/_add_newdocs.py,sha256=ySKuP_4sVPNLHp1ojgTMhSRWi3d18CcBFHFHkD8Xf-U,208893 +numpy/_core/_add_newdocs.pyi,sha256=r__d_-GHkfjzuZ0qyjDztsKgdc1eIyeN-cBoYVgMBuo,168 +numpy/_core/_add_newdocs_scalars.py,sha256=Z5WcIAXy2Vs8kWLCzgyvxWVH0CAl-O64YFK3ttbU7yc,12600 +numpy/_core/_add_newdocs_scalars.pyi,sha256=ZnIk0TgL0szrv6SPCH-4dF469Q_92UvV5_ek47Oj7HM,573 +numpy/_core/_asarray.py,sha256=fCNHLaaCP-5Ia-RR_bIrHxWY3xklcmvlZiGhJIDiKLM,3911 +numpy/_core/_asarray.pyi,sha256=QHyb8DM_9U0otRugoNIyKjtvTVS3dZLn6DSxGi_ZU4U,1073 +numpy/_core/_dtype.py,sha256=cM6JnjoHLURWCHgN8VmQyjeiiDjcwhB5L_fPMOe1uuM,10547 +numpy/_core/_dtype.pyi,sha256=turm6RyVVEGKm6antqWWnyA0bnS2AuMwmKeFj-9mYHA,1851 +numpy/_core/_dtype_ctypes.py,sha256=KPPlakDsPkuThSOr5qFwW0jJ9VnjbvW4EWhObCHYGIE,3726 +numpy/_core/_dtype_ctypes.pyi,sha256=VwEZFViCPuHlCURv2jpJp9sbHh2hYUpzC_FRZNNGMMw,3682 +numpy/_core/_exceptions.py,sha256=X8Eg1hq1uU8L9wiOwFo2jRq6S0vnjCdgYFHj3hAW9Co,5159 +numpy/_core/_exceptions.pyi,sha256=ESXpijoEK0HrPy0dQYtjO62-Krd0419WLlrDROqwTyU,1900 +numpy/_core/_internal.py,sha256=YZ6nMGVOvfTD1nzk2XqRdz8k05WVnYGiljb1TnHvMq8,28981 +numpy/_core/_internal.pyi,sha256=2V2rXMQocZZHw8z_9HSrUi3LNGxaxA1nm0B0fcofjU8,2654 +numpy/_core/_machar.py,sha256=YUX24XYbxXJ79KrWar27FlDYKfeodr_RCkE7w0bETqs,11569 +numpy/_core/_machar.pyi,sha256=ESXpijoEK0HrPy0dQYtjO62-Krd0419WLlrDROqwTyU,1900 +numpy/_core/_methods.py,sha256=4qiUUES5wnOFeXnPavtqqMVhZ09ZZeSKlwqdPw2eKSI,9430 +numpy/_core/_methods.pyi,sha256=5HzEt2Z0-vxQfS1QJKDlTvNyLXcinNsja-xQiehMGbw,526 +numpy/_core/_multiarray_tests.cpython-312-x86_64-linux-gnu.so,sha256=-uOqys8wnCdsxMjL3BNzURd2PxxfZSy0i7l-zJwbndg,141888 +numpy/_core/_multiarray_umath.cpython-312-x86_64-linux-gnu.so,sha256=O1JIY1ShYFehh2iXHmUi3baCZRoigYbS-wXkNYzEdN4,10808937 +numpy/_core/_operand_flag_tests.cpython-312-x86_64-linux-gnu.so,sha256=LaAKYVcg0fwPuQL89z8q1-GrpQykaaNK4kJuXRArmDs,16800 +numpy/_core/_rational_tests.cpython-312-x86_64-linux-gnu.so,sha256=L-0ufvXmFouELpWO_1IJOyZp7m1EiznGwGSZC3lGRVA,59592 +numpy/_core/_simd.cpython-312-x86_64-linux-gnu.so,sha256=NGUI17JKGWykF1UY3TsJruMrbH-YCfePwDFQRDbA0_c,2882368 +numpy/_core/_simd.pyi,sha256=2z2sFPgXr3KRzHltbt31HVrhkXM0VwXFp1lUjxaRMAM,669 +numpy/_core/_string_helpers.py,sha256=6Smgoi6oD2CunjwBSr9BZ20HkCnvW6nTPblTOU3pWng,2845 +numpy/_core/_string_helpers.pyi,sha256=xLlLKJHutEYzyKnTG2k7clcWvVUTvD319SjnKmDXuac,358 +numpy/_core/_struct_ufunc_tests.cpython-312-x86_64-linux-gnu.so,sha256=U__bzTJORJ4S1Ax393Y1ui1vpwNmQKJzNAIkICwK-hk,16936 +numpy/_core/_type_aliases.py,sha256=msFHBkZ2s1wKQyuguK_cF6NBS0_3AOww7j3oh26mo3Q,3489 +numpy/_core/_type_aliases.pyi,sha256=Tn1Ex4bAGQa1HuMx0Vn-tEBl3HDF_uesTzmiSrz81kQ,2388 +numpy/_core/_ufunc_config.py,sha256=hVIyOmLjFYdZQY5plKWuOMk-U7UzeYSEo4ygiXOFcBU,15052 +numpy/_core/_ufunc_config.pyi,sha256=rh1jhYnkafjGvrc3ytC5mOSwRnjwhoggw8yDeLCS3jc,972 +numpy/_core/_umath_tests.cpython-312-x86_64-linux-gnu.so,sha256=7Q7bbK-i26WJdhCDw-22TnLzD9gWwyqa93FEClt5M7A,50312 +numpy/_core/arrayprint.py,sha256=AAAvkrI0U6Pa_wZOnpuVZBpdsCCjpYpcWF8sA_SPYbg,65278 +numpy/_core/arrayprint.pyi,sha256=ogMYnp2ipEfagADzRaRK9ySGAfH_oabGNJegiA6LicY,6971 +numpy/_core/cversions.py,sha256=H_iNIpx9-hY1cQNxqjT2d_5SXZhJbMo_caq4_q6LB7I,347 +numpy/_core/defchararray.py,sha256=1tSvLWEeac20DodpDBxapJKwwczpJG1lVy2qjScIVXg,38007 +numpy/_core/defchararray.pyi,sha256=Mq-ytnNliY2jEYAl_0l5ZTRx9IpNMaJpDmkoerRUILE,27985 +numpy/_core/einsumfunc.py,sha256=heFeCiEKji-qfVk8zAZ1b5bKm-MUMLzCETMQ7yyHBhc,52820 +numpy/_core/einsumfunc.pyi,sha256=b10CKdAeLEryabwRMdiW1cKdNyqWLa5kMV7O2_X8g3A,4893 +numpy/_core/fromnumeric.py,sha256=s0f6WfkIRVwFZMlDrdYb3EjyF9vMGr0bms0Pc-VcOAM,143882 +numpy/_core/fromnumeric.pyi,sha256=VoUF-d31OuZYaRIi-duoYAABOADe4KjbBhFFx3Hd_Mc,42034 +numpy/_core/function_base.py,sha256=QT1pbll_8rf_3ZsGtLQoAeQ1OSqCqeAGtMTzPAE1I_w,19683 +numpy/_core/function_base.pyi,sha256=A9BlWQeiX08iIwDQJ6W1FUhy2qrRPVenXtHiEnPkt0k,7064 +numpy/_core/getlimits.py,sha256=32Qe7tlBFdyiDvdSjG1cp2a0NJ0rSMxeDRij3agiPrg,26101 +numpy/_core/getlimits.pyi,sha256=q30hQ3wDenmxoZUSoSOqyVrZZVGlsixXCHe6QUthbp8,61 +numpy/_core/include/numpy/__multiarray_api.c,sha256=ndBF5wbdd7F8_zWvR52MDO0Qm15_PrCCBlSk4dky4F8,12698 +numpy/_core/include/numpy/__multiarray_api.h,sha256=6ep4M4s0Cxoj4DgJGns-0___TdSqDJoUPnZr0BBYwkU,61639 +numpy/_core/include/numpy/__ufunc_api.c,sha256=Fg7WlH4Ow6jETKRArVL_QF11ABKYz1VpOve56_U3E0w,1755 +numpy/_core/include/numpy/__ufunc_api.h,sha256=J5h9KHdntM27XQdq1PwHwI7V2v-sOx6AIbgCwP8mg9M,13175 +numpy/_core/include/numpy/_neighborhood_iterator_imp.h,sha256=s-Hw_l5WRwKtYvsiIghF0bg-mA_CgWnzFFOYVFJ-q4k,1857 +numpy/_core/include/numpy/_numpyconfig.h,sha256=lfgEF_31SixqOweZEHjn19bN5ng62MSwuVWEXS1_p_U,926 +numpy/_core/include/numpy/_public_dtype_api_table.h,sha256=n6_Kb98SyvsR_X7stiNA6VuGp_c5W1e4fMVcJdO0wis,4574 +numpy/_core/include/numpy/arrayobject.h,sha256=mU5vpcQ95PH1j3bp8KYhJOFHB-GxwRjSUsR7nxlTSRk,204 +numpy/_core/include/numpy/arrayscalars.h,sha256=LlyrZIa_5td11BfqfMCv1hYbiG6__zxxGv1MRj8uIVo,4243 +numpy/_core/include/numpy/dtype_api.h,sha256=Gn37RzObmcTsL6YUYY9aG22Ct8F-r4ZaC53NPFqaIso,19238 +numpy/_core/include/numpy/halffloat.h,sha256=TRZfXgipa-dFppX2uNgkrjrPli-1BfJtadWjAembJ4s,1959 +numpy/_core/include/numpy/ndarrayobject.h,sha256=MnykWmchyS05ler_ZyhFIr_0j6c0IcndEi3X3n0ZWDk,12057 +numpy/_core/include/numpy/ndarraytypes.h,sha256=kS9uirBf_ewXdIgsmRQETk3aQXeSPjLPCa6hlX5By-0,65810 +numpy/_core/include/numpy/npy_2_compat.h,sha256=wdjB7_-AtW3op67Xbj3EVH6apSF7cRG6h3c5hBz-YMs,8546 +numpy/_core/include/numpy/npy_2_complexcompat.h,sha256=eE9dV_Iq3jEfGGJFH_pQjJnvC6eQ12WgOB7cZMmHByE,857 +numpy/_core/include/numpy/npy_3kcompat.h,sha256=grN6W1n7benj3F2pSAOpl_s6vn1Y50QfAP-DaleD7cA,9648 +numpy/_core/include/numpy/npy_common.h,sha256=-05bavbk44KUjy5Q-qnM5YzU32VJRv0N8ozfCI_SKcE,32586 +numpy/_core/include/numpy/npy_cpu.h,sha256=Vw8mVPm1fGmLdeLV3RoBZnBMMXA8cghgwRdWhlkDLi4,4225 +numpy/_core/include/numpy/npy_endian.h,sha256=vvK7ZlOt0vgqTVrIyviWzoxQz70S-BvflS4Z_k6X5XE,2834 +numpy/_core/include/numpy/npy_math.h,sha256=aeSFs60QbWPy1gIPyHDPrYExifm5mbDAcjP_mLk_PF0,18858 +numpy/_core/include/numpy/npy_no_deprecated_api.h,sha256=0yZrJcQEJ6MCHJInQk5TP9_qZ4t7EfBuoLOJ34IlJd4,678 +numpy/_core/include/numpy/npy_os.h,sha256=hlQsg_7-RkvS3s8OM8KXy99xxyJbCm-W1AYVcdnO1cw,1256 +numpy/_core/include/numpy/numpyconfig.h,sha256=FGuDPIr0gTFYgUzhVMXqq5BIQL-WqgmXfp003cUwpWE,7333 +numpy/_core/include/numpy/random/LICENSE.txt,sha256=-8U59H0M-DvGE3gID7hz1cFGMBJsrL_nVANcOSbapew,1018 +numpy/_core/include/numpy/random/bitgen.h,sha256=49AwKOR552r-NkhuSOF1usb_URiMSRMvD22JF5pKIng,488 +numpy/_core/include/numpy/random/distributions.h,sha256=W5tOyETd0m1W0GdaZ5dJP8fKlBtsTpG23V2Zlmrlqpg,9861 +numpy/_core/include/numpy/random/libdivide.h,sha256=ew9MNhPQd1LsCZiWiFmj9IZ7yOnA3HKOXffDeR9X1jw,80138 +numpy/_core/include/numpy/ufuncobject.h,sha256=BengvqXqiy4ipzz23KQi1Kldy9ybYUs4Sp5yA73VgiU,11780 +numpy/_core/include/numpy/utils.h,sha256=wMNomSH3Dfj0q78PrjLVtFtN-FPo7UJ4o0ifCUO-6Es,1185 +numpy/_core/lib/libnpymath.a,sha256=oXeSGrMy3L_zDbnj58as1hihfFFftHWb73ah3KPeCT4,54312 +numpy/_core/lib/npy-pkg-config/mlib.ini,sha256=_LsWV1eStNqwhdiYPa2538GL46dnfVwT4MrI1zbsoFw,147 +numpy/_core/lib/npy-pkg-config/npymath.ini,sha256=0iMzarBfkkZ_EXO95_kz-SHZRcNIEwIeOjE_esVBkRQ,361 +numpy/_core/lib/pkgconfig/numpy.pc,sha256=SBZcZL5NZ_HgJxW6wc5xZJOoOjAxwSTo539d_6v-1tk,191 +numpy/_core/memmap.py,sha256=yIsQ6n9kpZulggRJJFkTbjVwnB4leoyizvUpc2iU4n8,12651 +numpy/_core/memmap.pyi,sha256=_LKjb_PuhcQwpqc2lFaL379DYzQ9PtuKdlVV3jXOYEM,47 +numpy/_core/multiarray.py,sha256=zwHBdyOoxiBRcOhG2QB_xBAYm-p8ARSpQbye9EzrrBo,58155 +numpy/_core/multiarray.pyi,sha256=Uy5Unmczfk7Pyz8Ohgh_5g4ASY7aZ0ZYpmhhmPnG6OA,32150 +numpy/_core/numeric.py,sha256=_DcnvXu6oaHXSi9Q-BV9yGzfx7tc9iCx69r9MnJDm5g,82322 +numpy/_core/numeric.pyi,sha256=ZSWTBi2kdP7BPG3KMGJWJIlqM9BLKFmgq_xgK_GnDUo,19042 +numpy/_core/numerictypes.py,sha256=mKPbsOzX9vyWQEv4jlf4xnlPfP4IYAXeILHFdb2FS0I,15957 +numpy/_core/numerictypes.pyi,sha256=Kp4_fEg_Wj_Yv8xvI7H1TJXrDVsxb96oIH5EmnQyW1c,3270 +numpy/_core/overrides.py,sha256=MtgzOBavG7wzQYCA7O7ArdCJVV72STIb_cvkWBuDLJE,7241 +numpy/_core/overrides.pyi,sha256=2lHte4EbOTDQvknjVfO71RgiLXnOpGQky5j2meS09JU,1713 +numpy/_core/printoptions.py,sha256=NFpvy5bnjbvqnKeqQt0veEExpAAYAVNoiGXH3pglWAc,1056 +numpy/_core/printoptions.pyi,sha256=eNiliCnDuZBxla6X9kwZ-7YiCn-UtMbT-U_qTnw8l9w,594 +numpy/_core/records.py,sha256=hoXCDswM6hbytiGdYGkhRISzQjnqImXcIdGlNuOUDX4,36767 +numpy/_core/records.pyi,sha256=tob9AxABbCXsO--gWXX-pD5Bo50NgCXKOt4JstVESjY,8935 +numpy/_core/shape_base.py,sha256=7yDPrIXTmmBnZMUStHXsq1iJNiGmIxEAcepxQ9o-JVQ,32738 +numpy/_core/shape_base.pyi,sha256=Qgfi1izbvKgRWAojCMXw3HsONgvsryFCsDhAvNI1dZE,4753 +numpy/_core/strings.py,sha256=yjdeNG2e0wpljpnwGISi7NXVLD4ttCM5vAYSSV1yI8k,50642 +numpy/_core/strings.pyi,sha256=Fyjq70ZP70BzV3Ov490dxX5EOv76sgnxA7qVBxeXuRU,13502 +numpy/_core/tests/_locales.py,sha256=lvHqUJVMsrE7Jh3N_KpO5fGBZgID-l3Zr4-_RrH1ZNM,2176 +numpy/_core/tests/_natype.py,sha256=YCAkuhvWuMjTjt-C0VjA8zzui-KoioNwOmAYnvf6KR0,6525 +numpy/_core/tests/data/astype_copy.pkl,sha256=lWSzCcvzRB_wpuRGj92spGIw-rNPFcd9hwJaRVvfWdk,716 +numpy/_core/tests/data/generate_umath_validation_data.cpp,sha256=BQakB5o8Mq60zex5ovVO0IatNa7xbF8JvXmtk6373So,5842 +numpy/_core/tests/data/recarray_from_file.fits,sha256=NA0kliz31FlLnYxv3ppzeruONqNYkuEvts5wzXEeIc4,8640 +numpy/_core/tests/data/umath-validation-set-README.txt,sha256=pxWwOaGGahaRd-AlAidDfocLyrAiDp0whf5hC7hYwqM,967 +numpy/_core/tests/data/umath-validation-set-arccos.csv,sha256=yBlz8r6RnnAYhdlobzGGo2FKY-DoSTQaP26y8138a3I,61365 +numpy/_core/tests/data/umath-validation-set-arccosh.csv,sha256=0GXe7XG1Z3jXAcK-OlEot_Df3MetDQSlbm3MJ__iMQk,61365 +numpy/_core/tests/data/umath-validation-set-arcsin.csv,sha256=w_Sv2NDn-mLZSAqb56JT2g4bqBzxYAihedWxHuf82uU,61339 +numpy/_core/tests/data/umath-validation-set-arcsinh.csv,sha256=DZrMYoZZZyM1DDyXNUxSlzx6bOgajnRSLWAzxcPck8k,60289 +numpy/_core/tests/data/umath-validation-set-arctan.csv,sha256=0aosXZ-9DYTop0lj4bfcBNwYVvjZdW13hbMRTRRTmV0,60305 +numpy/_core/tests/data/umath-validation-set-arctanh.csv,sha256=HEK9ePx1OkKrXIKkMUV0IxrmsDqIlgKddiI-LvF2J20,61339 +numpy/_core/tests/data/umath-validation-set-cbrt.csv,sha256=v855MTZih-fZp_GuEDst2qaIsxU4a7vlAbeIJy2xKpc,60846 +numpy/_core/tests/data/umath-validation-set-cos.csv,sha256=0PNnDqKkokZ7ERVDgbes8KNZc-ISJrZUlVZc5LkW18E,59122 +numpy/_core/tests/data/umath-validation-set-cosh.csv,sha256=JKC4nKr3wTzA_XNSiQvVUq9zkYy4djvtu2-j4ZZ_7Oc,60869 +numpy/_core/tests/data/umath-validation-set-exp.csv,sha256=rUAWIbvyeKh9rPfp2n0Zq7AKq_nvHpgbgzLjAllhsek,17491 +numpy/_core/tests/data/umath-validation-set-exp2.csv,sha256=djosT-3fTpiN_f_2WOumgMuuKgC_XhpVO-QsUFwI6uU,58624 +numpy/_core/tests/data/umath-validation-set-expm1.csv,sha256=K7jL6N4KQGX71fj5hvYkzcMXk7MmQes8FwrNfyrPpgU,60299 +numpy/_core/tests/data/umath-validation-set-log.csv,sha256=ynzbVbKxFzxWFwxHnxX7Fpm-va09oI3oK1_lTe19g4w,11692 +numpy/_core/tests/data/umath-validation-set-log10.csv,sha256=NOBD-rOWI_FPG4Vmbzu3JtX9UA838f2AaDFA-waiqGA,68922 +numpy/_core/tests/data/umath-validation-set-log1p.csv,sha256=tdbYWPqWIz8BEbIyklynh_tpQJzo970Edd4ek6DsPb8,60303 +numpy/_core/tests/data/umath-validation-set-log2.csv,sha256=39EUD0vFMbwyoXoOhgCmid6NeEAQU7Ff7QFjPsVObIE,68917 +numpy/_core/tests/data/umath-validation-set-sin.csv,sha256=8PUjnQ_YfmxFb42XJrvpvmkeSpEOlEXSmNvIK4VgfAM,58611 +numpy/_core/tests/data/umath-validation-set-sinh.csv,sha256=XOsBUuPcMjiO_pevMalpmd0iRv2gmnh9u7bV9ZLLg8I,60293 +numpy/_core/tests/data/umath-validation-set-tan.csv,sha256=Hv2WUMIscfvQJ5Y5BipuHk4oE4VY6QKbQp_kNRdCqYQ,60299 +numpy/_core/tests/data/umath-validation-set-tanh.csv,sha256=iolZF_MOyWRgYSa-SsD4df5mnyFK18zrICI740SWoTc,60299 +numpy/_core/tests/examples/cython/checks.pyx,sha256=nw6o0nlj3SfNQP3McS10zVH9UCZiITBdAi5yO4gm9Qo,10774 +numpy/_core/tests/examples/cython/meson.build,sha256=uuXVPKemNVMQ5MiEDqS4BXhwGHa96JHjS50WxZuJS_8,1268 +numpy/_core/tests/examples/cython/setup.py,sha256=JM6UnDql7LsAnRo6p9G-nRz3dfnoy9fHF6YVKy1OzdA,859 +numpy/_core/tests/examples/limited_api/limited_api1.c,sha256=htSR9ER3S8AJqv4EZMsrxQ-SufTIlXNpuFI6MXQs87w,346 +numpy/_core/tests/examples/limited_api/limited_api2.pyx,sha256=1q4I59pdkCmMhLcYngN_XwQnPoLmDEo1uTGnhrLRjDc,203 +numpy/_core/tests/examples/limited_api/limited_api_latest.c,sha256=ltBLbrl1g9XxD2wvN_-g3NhIizc8mxnh2Z6wCyXo-8E,452 +numpy/_core/tests/examples/limited_api/meson.build,sha256=YM5RwW_waFymlWSHFhCCOHO6KCknooN0jCiqScL0i5M,1627 +numpy/_core/tests/examples/limited_api/setup.py,sha256=Y6tgsOF58qe7eG2QmRQHG2wacZWfpbJLT8u-5OamjqA,437 +numpy/_core/tests/test__exceptions.py,sha256=luMT6vPIdf6LuwFNGyT-xLMZaKZEYYOFzFpMaesojoE,2922 +numpy/_core/tests/test_abc.py,sha256=9y2SsJdkPeV0oW6dsROPZOcQ72_mXie1uU2yPN93wzo,2221 +numpy/_core/tests/test_api.py,sha256=NiqlxYyBOZlKVKIWs_vQTg6ZnOk5iE63nbz1GBdHXeI,22954 +numpy/_core/tests/test_argparse.py,sha256=pfFfRr0grfOt-6Y7D8q9yPmz8Fcx4UbUxLpe96Tk9Xg,2870 +numpy/_core/tests/test_array_api_info.py,sha256=PZ2EzS9pq4nLZRAvvUSOb2Ke5p7pb4u4P4HKLRZjstw,3063 +numpy/_core/tests/test_array_coercion.py,sha256=PJ3s7psngDM084R2x7luAHVkHoa31TDiH1FiZpUWSfs,34897 +numpy/_core/tests/test_array_interface.py,sha256=l39VuV4nCdIeV1RUvMtjjPohAgIvJP-V3GQ5MaPrVK8,7843 +numpy/_core/tests/test_arraymethod.py,sha256=my4I9YjpVGLwN1GMbuoEhBZEJN0PuH6R2wtvGHcfoWI,3223 +numpy/_core/tests/test_arrayobject.py,sha256=aVv2eGjunCMEDFgmFujxMpk4xb-zo1MQrFcwQLfblx0,2596 +numpy/_core/tests/test_arrayprint.py,sha256=6UmL93wltbIDKdhF_WcdPRH5mztX0wyzuBy6PYW3R_o,50738 +numpy/_core/tests/test_casting_floatingpoint_errors.py,sha256=cER1YCNEwq67uAPX0QhkJonb5oA4Ws1_t0Z2AWJjYJg,5076 +numpy/_core/tests/test_casting_unittests.py,sha256=HH849h4ox1dejLB4aFX2B9tSGf0WhVvPZBPJT4yTOAA,34336 +numpy/_core/tests/test_conversion_utils.py,sha256=HAIdSRUit1lhSQEn-UVPTwyNxKjP9bSr8NGeHXnp6ew,6362 +numpy/_core/tests/test_cpu_dispatcher.py,sha256=26vob-nCPkjtxf9lRlQvwoTR92lqquyDGPgE5DIoii8,1570 +numpy/_core/tests/test_cpu_features.py,sha256=lS9iIWWznKZgR8-G4ABZqznMTJGC343-FBaCG9ZHXmQ,15703 +numpy/_core/tests/test_custom_dtypes.py,sha256=LZCbBeoyCcluhz_drg5neyiAsoTaK-6DjB4l3LaNnTw,11766 +numpy/_core/tests/test_cython.py,sha256=hLdTcd5wbzMXOx_OyQEzNyFWm-rIcWto7LpCl1SNdIU,10186 +numpy/_core/tests/test_datetime.py,sha256=gbArTFwyvmbQSkvTwa7oCv6UXDuvYV3_AbFEvK4ImOo,122685 +numpy/_core/tests/test_defchararray.py,sha256=hmMd5Wv5PjTEIuBXq_DopSqJsnp-qJ8ub5BBGRKIUEw,30629 +numpy/_core/tests/test_deprecations.py,sha256=CayfNUVMMj4BYTIFdYR4xvL2Sy2CTLN7VTABe0HIlxg,17101 +numpy/_core/tests/test_dlpack.py,sha256=Lfi3Xd2umxJ4W8fJht5epHlYWwTKx7MB47i7dcOIpq8,5830 +numpy/_core/tests/test_dtype.py,sha256=e1ZLn0xj8FrlxK3FeHOOsoQ-xV17-FMM7mh7VpuuVhs,78797 +numpy/_core/tests/test_einsum.py,sha256=Sixz-ZogKZmnFz3t49voD6AsCxmxUl_c_DHxT9rdscE,56277 +numpy/_core/tests/test_errstate.py,sha256=czhSWJJ8mdDpkh76pAxU2-d4ebMyopyk2D_CC-2lzI0,4627 +numpy/_core/tests/test_extint128.py,sha256=F6TAH3PlGON3CNz-B4hunClNUTQYQ2R8CkvaX2Zqeo4,5625 +numpy/_core/tests/test_function_base.py,sha256=x6rHdbqXtHj07Oml_5DslnG6y8jm0XfW4RdV0Q_lHHA,17651 +numpy/_core/tests/test_getlimits.py,sha256=CAHTLA8QIYVXTLWCGAISUZaAJ-xd_cBnSdYaOGuLWn8,6976 +numpy/_core/tests/test_half.py,sha256=QSKuHAfa8NWvl0A51-XcV0UOIvk-ooLy6pndq90hr6k,24425 +numpy/_core/tests/test_hashtable.py,sha256=m9-IRALLhU5liPuAk4v-ZQTVQ4s5XtLhL6xRXf5QTOE,1147 +numpy/_core/tests/test_indexerrors.py,sha256=mU2MJbdpbrcvxLZqZR293So4ZJxMH4apAjqXufRyOis,4726 +numpy/_core/tests/test_indexing.py,sha256=lU0jP4UvEe2_MUiAhy4_GD1zvpdIwUrHviu0MJhW_wQ,55421 +numpy/_core/tests/test_item_selection.py,sha256=AoPUe3llYwKjv3dO1PW1qSml4SWrAAL3fNqpwKAku6w,6631 +numpy/_core/tests/test_limited_api.py,sha256=75nz_t-jBdjKim6j-WW7WsD2rPnJ_KQ-zrRUiP3nVic,3463 +numpy/_core/tests/test_longdouble.py,sha256=FjuntHkYe158dwWr7eYe_mlqkj7sQ9lQXKZ93CKF0Pc,12391 +numpy/_core/tests/test_machar.py,sha256=Aw8icmrolAGmbIuXhUIYd4YvqIRR1I8GkcSx0J2c6yM,1067 +numpy/_core/tests/test_mem_overlap.py,sha256=IGpRF2GnkLQxEiIizsVT0eWUtlgCcJQ4w0-BEjSpT_8,29219 +numpy/_core/tests/test_mem_policy.py,sha256=pL6kBK8fgtRDTfMubFGGWnliTPWnS64uZ9l1H5qI8hk,16794 +numpy/_core/tests/test_memmap.py,sha256=LtghbNqt9AOmAalIyZF3lepthcKircyNfb2-5_Tkj1c,8186 +numpy/_core/tests/test_multiarray.py,sha256=au2BIcxXH1rXMVBm4VKNA3aogJu3Qtd8bAwcoZzpDcM,400390 +numpy/_core/tests/test_multithreading.py,sha256=VkvO2311ch8a_EeF7RTmhAQWvtHXuTZhqLVZZH1ovKI,8601 +numpy/_core/tests/test_nditer.py,sha256=7y1wdYzpGdwEbHRc5xppx8FZ45cKxNrm3JKzUPvkhrE,136568 +numpy/_core/tests/test_nep50_promotions.py,sha256=i6KpABBWFB5PWCdEv8kIjNQd7ryAPINS5m_Tnu7sDj4,10068 +numpy/_core/tests/test_numeric.py,sha256=aM2TfTaSVE2fz0Z3nN72XoxSDvZzAdatwWpLYWGBBws,159748 +numpy/_core/tests/test_numerictypes.py,sha256=r4ZvEN0E8efuqZhx2spCXA5Mr14mK1BRpmOZFRp0LhU,23271 +numpy/_core/tests/test_overrides.py,sha256=0sDSmDWIr88GuCj0gOxdE3l0X_T5Hb5Wj2zfJDkOtvU,27518 +numpy/_core/tests/test_print.py,sha256=_cuM-DIpljOkzErb2ggIgs9HvOYrtpRppaECF6xAo0c,6787 +numpy/_core/tests/test_protocols.py,sha256=pbfumoRNnPhDP6PAPNIgLHUPPlmCdamCo4akkO8afjo,1173 +numpy/_core/tests/test_records.py,sha256=PAMHzIPp2WWDm4JHFQ-cjPBWf4BDuQumIYo7UX-zElk,20547 +numpy/_core/tests/test_regression.py,sha256=fJJnesLRUyPziCbYVM9LfLSS3qAMUz1-mzddhV9Br-U,95565 +numpy/_core/tests/test_scalar_ctors.py,sha256=I3akKp6WdwsTGic8pYQC_c6AxPXPEXStywWOF0n_ivU,6724 +numpy/_core/tests/test_scalar_methods.py,sha256=tx1RoZ03QsWblqg3Dv_JkaBFUOOILKZIqaEsFEs4tfE,9117 +numpy/_core/tests/test_scalarbuffer.py,sha256=2mZblaScwhN8mdlQvUULAKt273B2ia-mjtNmL_2UxfQ,5638 +numpy/_core/tests/test_scalarinherit.py,sha256=OIvSjrltdNSSP2c5HvDQ6pza3aKfmfgtixu1Zbahpcg,2587 +numpy/_core/tests/test_scalarmath.py,sha256=gBHBZ5SQMru1A57FUEaIMk19GFdVLTRXiO9vVh4XVVc,46583 +numpy/_core/tests/test_scalarprint.py,sha256=NS-FQDWICDcuDF5gxTQuG1Td1-EiOXIXufI-dwvKwxU,19705 +numpy/_core/tests/test_shape_base.py,sha256=mRSruY7S84ula25ZoOvbcRg_ea_3C3338e1tmdmv1Uk,31536 +numpy/_core/tests/test_simd.py,sha256=u8xSZ6HNLJ9-siYNIuyd0RA7FbD1BLEmnV5TGUrt1FU,48823 +numpy/_core/tests/test_simd_module.py,sha256=JjXH4Yq-0K-R8FHqVDinNaqY_grb1fQFFyVTHGQ0pBg,3904 +numpy/_core/tests/test_stringdtype.py,sha256=NwBb0NsnnnFmjfWAemc_FEHTq_ArJ48mNj33AD_zOYM,57072 +numpy/_core/tests/test_strings.py,sha256=16hEUxlHI89-8YsoW9RfI-V4eU-GKwnJXEak-dB7lW8,57959 +numpy/_core/tests/test_ufunc.py,sha256=yO1DbSTyonZWsz8HoXV0E4YN5Xlg-aIHi6xn2gTi928,136356 +numpy/_core/tests/test_umath.py,sha256=D7wSX7JvIk80znwd8GsxYZIzp62It75SBzvKOZHeOXE,193840 +numpy/_core/tests/test_umath_accuracy.py,sha256=QCFAeiPN6rEO8fwDwJun4J1pCKm0bPsQK6-1pTYCMIY,5478 +numpy/_core/tests/test_umath_complex.py,sha256=LZMd-divBHQQ7dS34obwvmStXa8aNez45VIVTwPg_jM,23627 +numpy/_core/tests/test_unicode.py,sha256=qrQ7UC0yndXFYI7MiJu8y_I5jCK2lxOQcehE289MElk,12967 +numpy/_core/umath.py,sha256=t_SQIHR7dkMF-VRp8dKyroOEd90oqNlzmgGwaH28qW8,2130 +numpy/_core/umath.pyi,sha256=FIqmlQwQIueIrs-_QehV3guNEnJE2LxVs3NPCj38Vdo,2643 +numpy/_distributor_init.py,sha256=FBSJdgVHlQca5BrQEVYPoFm6KSTJhIFnWtWbEkEhTSo,421 +numpy/_distributor_init.pyi,sha256=6IvMzAmr0-Z6oqTkZcgXgrkJrQXVMjBih2AZvLdDgOE,27 +numpy/_expired_attrs_2_0.py,sha256=zP31EXmbwygcOEzyetDEp-RxL9cUfbUUht956zaOSf8,3826 +numpy/_expired_attrs_2_0.pyi,sha256=n2ipDUFTFS4puCD56dlNWGkVkw_b0M6cEyugo4Qh3HM,1253 +numpy/_globals.py,sha256=k5ZVnzUbKNSLPmZ0URYwJN5C_7xIzfMNaaSsBSrPTuI,3091 +numpy/_globals.pyi,sha256=IrHHIXmibXzgK0VUlECQLw4IEkveXSHo_ZWnTkfnLe4,280 +numpy/_pyinstaller/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +numpy/_pyinstaller/__init__.pyi,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +numpy/_pyinstaller/hook-numpy.py,sha256=MU22pQ4AkUYPQWu5C8pRDpnYXElLJ8R0FGNYJUQpiVE,1362 +numpy/_pyinstaller/hook-numpy.pyi,sha256=tAvtMPovoi-sur0D1NAo3_evSmYKLTh0bgRSC7QrCIk,349 +numpy/_pyinstaller/tests/__init__.py,sha256=pdPbCTRwpCJamlyvIi9HZTlqAvK5HPbGu3oMA0cu2Rs,329 +numpy/_pyinstaller/tests/pyinstaller-smoke.py,sha256=6iL-eHMQaG3rxnS5EgcvrCqElm9aKL07Cjr1FZJSXls,1143 +numpy/_pyinstaller/tests/test_pyinstaller.py,sha256=8K-7QxmfoXCG0NwR0bhIgCNrDjGlrTzWnrR1sR8btgU,1135 +numpy/_pytesttester.py,sha256=DjlYL8uINN2XWa3nnlX6gPGuoLjcx1Bie_PQzbp2cpA,6328 +numpy/_pytesttester.pyi,sha256=VXCuwPYTb9-PF6nxXwibwBbre0hW9jIB4nkzmtm2kls,497 +numpy/_typing/__init__.py,sha256=MG5Wv9dc3ZyOmDfidH5cFtykeyNM77ArC4R3UW7Tn-Y,7188 +numpy/_typing/_add_docstring.py,sha256=_3g7D-6HAQ3MT4X6DE07yLua9LqWFhskNVx1TS7X9O4,3999 +numpy/_typing/_array_like.py,sha256=EPZUfJSjamvsWJ6Rs5ZwwA_5FhBpYdoifcVVtVcWPn0,4188 +numpy/_typing/_callable.pyi,sha256=_nn_VLm2TgIoGk4BIbZBpgubwoJCiDjIOFTz0WkxjXg,9139 +numpy/_typing/_char_codes.py,sha256=j07npk82Nb7Ira2z7ZTlU3UcOPwt2gM7qZKrPLdjT48,8764 +numpy/_typing/_dtype_like.py,sha256=8M5RekLqdheEjWMIn4RnbkEzsS7jCatCiT0D5hg-53c,3762 +numpy/_typing/_extended_precision.py,sha256=pknUqgak0FBNM-sERPqW-pFGH71_K-iehFSee5oQiqE,434 +numpy/_typing/_nbit.py,sha256=KSbKwOKttob-5ytT5vCVkHrDMn0YHvyptTTyj_6AYcw,632 +numpy/_typing/_nbit_base.py,sha256=nPZpsQltuR5B0iaAYF9qD2he_kXnmssv_RhaUNFsW-s,3058 +numpy/_typing/_nbit_base.pyi,sha256=kHAqTmpYUWbQyTUVRs4NKKcDwiEJgUzWvvT1FQgQ89I,740 +numpy/_typing/_nested_sequence.py,sha256=so1agYGHd5gDo_IBvvHqBB5lsqGbHqN_imyC5UHU-HI,2505 +numpy/_typing/_scalars.py,sha256=LhXY2BTHmeYKzeIZfpjvuMn-5eOLjU2n9z7z1l5bKf8,944 +numpy/_typing/_shape.py,sha256=6cFv-LbSyG9mlfSBOGGyul9Q_GUrlcHQC9JZa-m20cA,275 +numpy/_typing/_ufunc.py,sha256=HOkaE-6wV0fd3rmHZGC39YAHIIf8tyvlzekD4y4GQxA,156 +numpy/_typing/_ufunc.pyi,sha256=1Ni26dsi2fbH2oNvXDNNXaBPQQzdhkwA7VQ8eyuJS_c,26575 +numpy/_utils/__init__.py,sha256=hVnZ7C0MCSNbMw-Zyq-MKCYStaGX6RzqFMnnh7ed4dE,3477 +numpy/_utils/__init__.pyi,sha256=VxEygNvp90alV8zYsUSuDYNdF7BEucXUx3w55Ef7YXI,726 +numpy/_utils/_convertions.py,sha256=0xMxdeLOziDmHsRM_8luEh4S-kQdMoMg6GxNDDas69k,329 +numpy/_utils/_convertions.pyi,sha256=4l-0UmPCyVA70UJ8WAd2A45HrKFSzgC0sFDBSnKcYiQ,118 +numpy/_utils/_inspect.py,sha256=zFuJABH08D1Kgq_eecYkD1Ogg0OXp1t4oqjZxM0kdLk,7436 +numpy/_utils/_inspect.pyi,sha256=wFajmQpCTXpMbJBbdiiyJMb29HkaMW0jEWLMqbQcQ5k,2255 +numpy/_utils/_pep440.py,sha256=it9P4_oHXWw3BxdoVz7JPMuj5kxF5M7_BJ8Z1m9nu0w,13988 +numpy/_utils/_pep440.pyi,sha256=xzYJoZ6DnjvgaKr8OsBwim77fAJ0xeQJI9XAt75gvfI,3870 +numpy/char/__init__.py,sha256=xs6pprMdmNeXVfuTRkU3nF9qdhutWdPu5oaep2AjWmc,93 +numpy/char/__init__.pyi,sha256=siwqDh7X7u4e0HGx3xg8eDaJVqy0_nac5y8UMzz-BcM,1540 +numpy/conftest.py,sha256=pXdv-CKocoIEpr0DsYstu7TgqvNdzSvfiDNMlMwmqYk,8577 +numpy/core/__init__.py,sha256=wJNaRF1UFOnZKqiBrsshWLjTGiEZ9rvWlcit0xj7Y0w,1290 +numpy/core/__init__.pyi,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +numpy/core/_dtype.py,sha256=GHBhfVtsVrP7v13IujEz9aGIENkYIdbfuRu-New1UnU,323 +numpy/core/_dtype.pyi,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +numpy/core/_dtype_ctypes.py,sha256=wX4m37b0zQgxlzT5OjE_uj2E5CpiX9E7HLFpO6h_lDY,351 +numpy/core/_dtype_ctypes.pyi,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +numpy/core/_internal.py,sha256=qxpHJELXNUcYJkJt1LktQuZm4BwYu4bXnMuBEOp6POU,949 +numpy/core/_multiarray_umath.py,sha256=T88HZgFD5VCuXRCSeLbPoj99nKUSdgyw8xWyf6eqhxQ,2098 +numpy/core/_utils.py,sha256=5fk18JN43Rg6YHvan6QjdrOeOuLtRlLVmP6MadBEJVA,923 +numpy/core/arrayprint.py,sha256=Lbe4smWXYFzd9sO9LLJ5PZS4C3bSvLt6HRtwSE56xN8,339 +numpy/core/defchararray.py,sha256=a9luvvni8gRrGVdKO7U_xwsFFvkzlxnVgxL75jLRmCI,347 +numpy/core/einsumfunc.py,sha256=CNucINgUIrpiLQn4xPI_mogwjfKlFA3h7gwAvRVwb5M,339 +numpy/core/fromnumeric.py,sha256=5TaonJVuC110qv3f3cqTtmjayTX0BmqJAgoAJn5H3ZI,343 +numpy/core/function_base.py,sha256=vhjhzsEzDd11RHg6pilfMJO3X6k94an5RAJqj-nlzms,351 +numpy/core/getlimits.py,sha256=6nCk4Tw0LjW7joWsprI5LiMzje1gsOjO2lSQ_OwBB8I,335 +numpy/core/multiarray.py,sha256=bjdPLbvJuj61M6TZkbB5NXOCNmH4QbUq6g3ePkKP6TA,793 +numpy/core/numeric.py,sha256=Ctk_QikyB2mM0xI0lBeB8YTUfTwQSXfVdpIMRtunbMo,360 +numpy/core/numerictypes.py,sha256=bXwTwzUahzbHrFGhS5RkJOvb6TYEsQnQC5ww9mN-1Vw,347 +numpy/core/overrides.py,sha256=1FZyb0U6JJuyojtxFvQ7HSJ2rpfhWec0F-X0mapCjc8,335 +numpy/core/overrides.pyi,sha256=-3xfjHfa4UaCuhTVwwRN4EOM5uz9vZR0gMeTVvEdbYI,525 +numpy/core/records.py,sha256=9yfFDxyOc68lXqfbaosgRNlw1dbWP8CRHzIPEtEtSgc,327 +numpy/core/shape_base.py,sha256=2srdQtF1d8LpUbDjGMXT-Tqz2K2NaTO-ZEC4viCYswY,339 +numpy/core/umath.py,sha256=hMVmNrICdqXRiiRG7UMV0Gr-9xYqJGmkONGQn20iK98,319 +numpy/ctypeslib/__init__.py,sha256=WFwMhpV2LJP-IQOspaInhV8c6XPKZwqppE-cvtIpqvU,193 +numpy/ctypeslib/__init__.pyi,sha256=R0tHAk1P0jw-HLYjjKBqXEjDyXhByrtbjrgOxht9tE4,619 +numpy/ctypeslib/_ctypeslib.py,sha256=NtEUpisQhDfETBLAkqYf7Ajq0xiNhZurb5SmGGH54pA,19079 +numpy/ctypeslib/_ctypeslib.pyi,sha256=xS-NLEO6xwjUr-AUWfGxz3N7X5jwIGBVl6RhOUUYZ74,8084 +numpy/doc/ufuncs.py,sha256=9xt8H34GhrXrFq9cWFUGvJFePa9YuH9Tq1DzAnm2E2E,5414 +numpy/dtypes.py,sha256=zuPwgC0ijF2oDRAOJ6I9JKhaJuhXFAygByLQaoVtT54,1312 +numpy/dtypes.pyi,sha256=sNN4kzUfhArHuKaMRKofBNZ57trl35UaZ51oDWrMmJ4,15544 +numpy/exceptions.py,sha256=x1z7C2RjrDFW8tLewbZjyMiQok0WBm5kKuRPIxVLUjg,7800 +numpy/exceptions.pyi,sha256=MJbCHjwFGps97WaVOPkaoUb8wi-l5OUbcFHdWZgBGbI,751 +numpy/f2py/__init__.py,sha256=cAgUHWgJQZZsfv8co8KBNr_m8B6fpzdBaUNvJeBf_No,2448 +numpy/f2py/__init__.pyi,sha256=UbgqGZKYnDHGHX9MlwBB3aBZ2T470ojrNREIhkwt6gc,132 +numpy/f2py/__main__.py,sha256=6i2jVH2fPriV1aocTY_dUFvWK18qa-zjpnISA-OpF3w,130 +numpy/f2py/__version__.py,sha256=99S6mSevuhwGmO9ku--7VUJekhN0ot4-J0cZKiHcqpw,48 +numpy/f2py/__version__.pyi,sha256=L4V6f6B-wuPi82B0MzeQsgN0NuHUQs9rKYl1jy3tG7s,45 +numpy/f2py/_backends/__init__.py,sha256=7_bA7c_xDpLc4_8vPfH32-Lxn9fcUTgjQ25srdvwvAM,299 +numpy/f2py/_backends/__init__.pyi,sha256=i4XhDRwbrl0ta6QGJPxhYGfSgugNGdtoWf1_27eSd60,136 +numpy/f2py/_backends/_backend.py,sha256=oFXZ8-VwcQSbltl8_pgWLPqCOZ8Y_px7oeTk_BlxJTc,1151 +numpy/f2py/_backends/_backend.pyi,sha256=sU4YiHvGfMkzDFbhZqqQPT-kwJZsWpGemkLxDion7ss,1342 +numpy/f2py/_backends/_distutils.py,sha256=hET0WB4qy-D4BznekGAWhk945k5weq2lGUDR6hriXMo,2385 +numpy/f2py/_backends/_distutils.pyi,sha256=-L8K1KQShPGGd1vgr4DlnYf6AshHFaRzAcgGqKv205g,463 +numpy/f2py/_backends/_meson.py,sha256=VouUQkWRUk74WhDtkf6HR79QoK-Wrx8E7qO7gVpyDnk,8107 +numpy/f2py/_backends/_meson.pyi,sha256=wvYtBdippKeiSeLzaYKehql0_3ThS8T8Aqat03hhjQ4,1869 +numpy/f2py/_backends/meson.build.template,sha256=hQeTapAY0xtni5Li-QaEtWx9DH9WDKah2lcEuSZfLLo,1599 +numpy/f2py/_isocbind.py,sha256=zaBgpfPNRmxVG3doUIlbZIiyB990MsXiwDabrSj9HnQ,2360 +numpy/f2py/_isocbind.pyi,sha256=KuzqHJQk0YSQnRnb8xqnyh8T0DGNnDD6bNI880tadCY,339 +numpy/f2py/_src_pyf.py,sha256=PHpo9D28Kq3q_3-KFX8D3sFD9eX8A1c3LuLNzXzByOw,7695 +numpy/f2py/_src_pyf.pyi,sha256=9NKnovhbLibbQkjCrRnyiTPDw3MBqycOHl1--BNrIqw,1012 +numpy/f2py/auxfuncs.py,sha256=dnaUwrdAv4-LbEiHNbS1vrjQNCO0lBuyWkj3Rt_UizE,26920 +numpy/f2py/auxfuncs.pyi,sha256=7RUoWWaHrqSYEmdNd5zCNnmbjUYE5pCe0FCxMXejbhg,8011 +numpy/f2py/capi_maps.py,sha256=7C-NndI2UbStNGXbhgbWOmr9tLAxfQvw1zf7Z7w5SFk,30079 +numpy/f2py/capi_maps.pyi,sha256=pR0pVZhUxaCpctq7FOWFSAGI_gaLdE-NWAyT96cWWZg,1066 +numpy/f2py/cb_rules.py,sha256=6KbPu9yfJ-7pAa24Ij9H34Ll15Qc8CXTqCFiUJI6R8Y,25051 +numpy/f2py/cb_rules.pyi,sha256=X_it8-Q0188EDlXd-QxhRdc3OUoA2t6V_jgM5TiQC88,495 +numpy/f2py/cfuncs.py,sha256=4J4P12oGpyWZHb1AVKAl7YJ3QUgngwGMCnB1IhrJn7U,52660 +numpy/f2py/cfuncs.pyi,sha256=EiAtSQxw4x-UlxsGKIEOJnld1d7dNYrk0bt_rlqLSp0,802 +numpy/f2py/common_rules.py,sha256=_9yzIolJMGgpd3D94LdBsODnfUskMRgt2v03rECIHJQ,5030 +numpy/f2py/common_rules.pyi,sha256=1uzTkcwiin6dVBbWUiOVB1ZppjKBHoRHG_Byvw-1UbI,323 +numpy/f2py/crackfortran.py,sha256=vbAvWj6XszLS-nU0nOedaNNtwtqvkkM8gqZAP9MvPBI,146879 +numpy/f2py/crackfortran.pyi,sha256=AvV_KPeE9jLG9EdmPdb2u7-gPJXc1H2yWVmmihHzCgM,10276 +numpy/f2py/diagnose.py,sha256=YWNj1vM68e47Lb270wlZk5yrcU-yTlzGaYNPBZ7nTAU,5075 +numpy/f2py/diagnose.pyi,sha256=ZFVCWTwf_xzL736p9FcfCYWftOXcNqSMCmq-K27KNN8,23 +numpy/f2py/f2py2e.py,sha256=krSW4RpZPDHNX2IWLdn28KWzj0lzFNSc_6fScbGQMfI,28763 +numpy/f2py/f2py2e.pyi,sha256=Qt6ZeOYBugJLFpAY3F9K_4hcm0sZt_3APTtdKLKObWA,2153 +numpy/f2py/f90mod_rules.py,sha256=7Z5vorU4whX405xML66hr4i1icCUc9gr6an4R-AMh7M,9810 +numpy/f2py/f90mod_rules.pyi,sha256=r6w0DuH2Jdt8wPdDYAnXZAQmytIYUqPOxVz-QaWwt74,451 +numpy/f2py/func2subr.py,sha256=9igCMMDttIgF1MG6kBOagkjI_SF-UlGjACAj3Ncv0-o,10049 +numpy/f2py/func2subr.pyi,sha256=-MDbOrhanuizf3rlcwBQooCF4GnoGprA8ypeFV_m8d0,386 +numpy/f2py/rules.py,sha256=Irj-13oLGowNHYElFV-TZUs0VEd0NQpRsnomnI1NTx8,63091 +numpy/f2py/rules.pyi,sha256=9GfFmNA8Unlg3pxcGwqwFl7yeKyIcTmx7wiPuiBAT-k,1326 +numpy/f2py/setup.cfg,sha256=Fpn4sjqTl5OT5sp8haqKIRnUcTPZNM6MIvUJBU7BIhg,48 +numpy/f2py/src/fortranobject.c,sha256=kLiHOty8fUruzfOmL5MQeVNFJSGHBjn7W6QbPYgQb30,46356 +numpy/f2py/src/fortranobject.h,sha256=7cfRN_tToAQ1Na13VQ2Kzb2ujMHUAgGsbScnfLVOHqs,5823 +numpy/f2py/symbolic.py,sha256=UuFs411WYSqR7JfbsuyNv__IC9wKqxQAWoWRDeKPcdw,53214 +numpy/f2py/symbolic.pyi,sha256=piZrats8SXrOD1qEADo-mbsc5NZOIaZ27Fl3d3cydTc,6083 +numpy/f2py/tests/__init__.py,sha256=pdPbCTRwpCJamlyvIi9HZTlqAvK5HPbGu3oMA0cu2Rs,329 +numpy/f2py/tests/src/abstract_interface/foo.f90,sha256=JFU2w98cB_XNwfrqNtI0yDTmpEdxYO_UEl2pgI_rnt8,658 +numpy/f2py/tests/src/abstract_interface/gh18403_mod.f90,sha256=gvQJIzNtvacWE0dhysxn30-iUeI65Hpq7DiE9oRauz8,105 +numpy/f2py/tests/src/array_from_pyobj/wrapmodule.c,sha256=s6XLwujiCr6Xi8yBkvLPBXRmo2WsGVohU7K9ALnKUng,7478 +numpy/f2py/tests/src/assumed_shape/.f2py_f2cmap,sha256=But9r9m4iL7EGq_haMW8IiQ4VivH0TgUozxX4pPvdpE,29 +numpy/f2py/tests/src/assumed_shape/foo_free.f90,sha256=oBwbGSlbr9MkFyhVO2aldjc01dr9GHrMrSiRQek8U64,460 +numpy/f2py/tests/src/assumed_shape/foo_mod.f90,sha256=rfzw3QdI-eaDSl-hslCgGpd5tHftJOVhXvb21Y9Gf6M,499 +numpy/f2py/tests/src/assumed_shape/foo_use.f90,sha256=rmT9k4jP9Ru1PLcGqepw9Jc6P9XNXM0axY7o4hi9lUw,269 +numpy/f2py/tests/src/assumed_shape/precision.f90,sha256=r08JeTVmTTExA-hYZ6HzaxVwBn1GMbPAuuwBhBDtJUk,130 +numpy/f2py/tests/src/block_docstring/foo.f,sha256=y7lPCPu7_Fhs_Tf2hfdpDQo1bhtvNSKRaZAOpM_l3dg,97 +numpy/f2py/tests/src/callback/foo.f,sha256=C1hjfpRCQWiOVVzIHqnsYcnLrqQcixrnHCn8hd9GhVk,1254 +numpy/f2py/tests/src/callback/gh17797.f90,sha256=_Nrl0a2HgUbtymGU0twaJ--7rMa1Uco2A3swbWvHoMo,148 +numpy/f2py/tests/src/callback/gh18335.f90,sha256=NraOyKIXyvv_Y-3xGnmTjtNjW2Znsnlk8AViI8zfovc,506 +numpy/f2py/tests/src/callback/gh25211.f,sha256=a2sxlQhtDVbYn8KOKHUYqwc-aCFt7sDPSnJsXFG35uI,179 +numpy/f2py/tests/src/callback/gh25211.pyf,sha256=FWxo0JWQlw519BpZV8PoYeI_FZ_K6C-3Wk6gLrfBPlw,447 +numpy/f2py/tests/src/callback/gh26681.f90,sha256=-cD69x7omk5wvVsfMHlXiZ-pTcaxs2Bl5G9GHA4UJ2M,566 +numpy/f2py/tests/src/cli/gh_22819.pyf,sha256=5rvOfCv-wSosB354LC9pExJmMoSHnbGZGl_rtA2fogA,142 +numpy/f2py/tests/src/cli/hi77.f,sha256=ttyI6vAP3qLnDqy82V04XmoqrXNM6uhMvvLri2p0dq0,71 +numpy/f2py/tests/src/cli/hiworld.f90,sha256=QWOLPrTxYQu1yrEtyQMbM0fE9M2RmXe7c185KnD5x3o,51 +numpy/f2py/tests/src/common/block.f,sha256=GQ0Pd-VMX3H3a-__f2SuosSdwNXHpBqoGnQDjf8aG9g,224 +numpy/f2py/tests/src/common/gh19161.f90,sha256=BUejyhqpNVfHZHQ-QC7o7ZSo7lQ6YHyX08lSmQqs6YM,193 +numpy/f2py/tests/src/crackfortran/accesstype.f90,sha256=-5Din7YlY1TU7tUHD2p-_DSTxGBpDsWYNeT9WOwGhno,208 +numpy/f2py/tests/src/crackfortran/common_with_division.f,sha256=2LfRa26JEB07_ti-WDmIveq991PxRlL_K6ss28rZDkk,494 +numpy/f2py/tests/src/crackfortran/data_common.f,sha256=ZSUAh3uhn9CCF-cYqK5TNmosBGPfsuHBIEfudgysun4,193 +numpy/f2py/tests/src/crackfortran/data_multiplier.f,sha256=jYrJKZWF_59JF9EMOSALUjn0UupWvp1teuGpcL5s1Sc,197 +numpy/f2py/tests/src/crackfortran/data_stmts.f90,sha256=19YO7OGj0IksyBlmMLZGRBQLjoE3erfkR4tFvhznvvE,693 +numpy/f2py/tests/src/crackfortran/data_with_comments.f,sha256=hoyXw330VHh8duMVmAQZjr1lgLVF4zFCIuEaUIrupv0,175 +numpy/f2py/tests/src/crackfortran/foo_deps.f90,sha256=CaH7mnWTG7FcnJe2vXN_0zDbMadw6NCqK-JJ2HmDjK8,128 +numpy/f2py/tests/src/crackfortran/gh15035.f,sha256=jJly1AzF5L9VxbVQ0vr-sf4LaUo4eQzJguhuemFxnvg,375 +numpy/f2py/tests/src/crackfortran/gh17859.f,sha256=7K5dtOXGuBDAENPNCt-tAGJqTfNKz5OsqVSk16_e7Es,340 +numpy/f2py/tests/src/crackfortran/gh22648.pyf,sha256=qZHPRNQljIeYNwbqPLxREnOrSdVV14f3fnaHqB1M7c0,241 +numpy/f2py/tests/src/crackfortran/gh23533.f,sha256=w3tr_KcY3s7oSWGDmjfMHv5h0RYVGUpyXquNdNFOJQg,126 +numpy/f2py/tests/src/crackfortran/gh23598.f90,sha256=41W6Ire-5wjJTTg6oAo7O1WZfd1Ug9vvNtNgHS5MhEU,101 +numpy/f2py/tests/src/crackfortran/gh23598Warn.f90,sha256=1v-hMCT_K7prhhamoM20nMU9zILam84Hr-imck_dYYk,205 +numpy/f2py/tests/src/crackfortran/gh23879.f90,sha256=LWDJTYR3t9h1IsrKC8dVXZlBfWX7clLeU006X6Ow8oI,332 +numpy/f2py/tests/src/crackfortran/gh27697.f90,sha256=bbnKpDsOuCWluoNodxzCspUQnu169zKTsn4fLTkhwpM,364 +numpy/f2py/tests/src/crackfortran/gh2848.f90,sha256=gPNasx98SIf7Z9ibk_DHiGKCvl7ERtsfoGXiFDT7FbM,282 +numpy/f2py/tests/src/crackfortran/operators.f90,sha256=-Fc-qjW1wBr3Dkvdd5dMTrt0hnjnV-1AYo-NFWcwFSo,1184 +numpy/f2py/tests/src/crackfortran/privatemod.f90,sha256=7bubZGMIn7iD31wDkjF1TlXCUM7naCIK69M9d0e3y-U,174 +numpy/f2py/tests/src/crackfortran/publicmod.f90,sha256=Pnwyf56Qd6W3FUH-ZMgnXEYkb7gn18ptNTdwmGan0Jo,167 +numpy/f2py/tests/src/crackfortran/pubprivmod.f90,sha256=eYpJwBYLKGOxVbKgEqfny1znib-b7uYhxcRXIf7uwXg,165 +numpy/f2py/tests/src/crackfortran/unicode_comment.f90,sha256=aINLh6GlfTwFewxvDoqnMqwuCNb4XAqi5Nj5vXguXYs,98 +numpy/f2py/tests/src/f2cmap/.f2py_f2cmap,sha256=iUOtfHd3OuT1Rz2-yiSgt4uPKGvCt5AzQ1iygJt_yjg,82 +numpy/f2py/tests/src/f2cmap/isoFortranEnvMap.f90,sha256=iJCD8a8MUTmuPuedbcmxW54Nr4alYuLhksBe1sHS4K0,298 +numpy/f2py/tests/src/isocintrin/isoCtests.f90,sha256=jcw-fzrFh0w5U66uJYfeUW4gv94L5MnWQ_NpsV9y0oI,998 +numpy/f2py/tests/src/kind/foo.f90,sha256=zIHpw1KdkWbTzbXb73hPbCg4N2Htj3XL8DIwM7seXpo,347 +numpy/f2py/tests/src/mixed/foo.f,sha256=90zmbSHloY1XQYcPb8B5d9bv9mCZx8Z8AMTtgDwJDz8,85 +numpy/f2py/tests/src/mixed/foo_fixed.f90,sha256=pxKuPzxF3Kn5khyFq9ayCsQiolxB3SaNtcWaK5j6Rv4,179 +numpy/f2py/tests/src/mixed/foo_free.f90,sha256=fIQ71wrBc00JUAVUj_r3QF9SdeNniBiMw6Ly7CGgPWU,139 +numpy/f2py/tests/src/modules/gh25337/data.f90,sha256=9Uz8CHB9i3_mjC3cTOmkTgPAF5tWSwYacG3MUrU-SY0,180 +numpy/f2py/tests/src/modules/gh25337/use_data.f90,sha256=WATiDGAoCKnGgMzm_iMgmfVU0UKOQlk5Fm0iXCmPAkE,179 +numpy/f2py/tests/src/modules/gh26920/two_mods_with_no_public_entities.f90,sha256=c7VU4SbK3yWn-6wksP3tDx_Hxh5u_g8UnlDpjU_-tBg,402 +numpy/f2py/tests/src/modules/gh26920/two_mods_with_one_public_routine.f90,sha256=eEU7RgFPh-TnNXEuJFdtJmTF-wPnpbHLQhG4fEeJnag,403 +numpy/f2py/tests/src/modules/module_data_docstring.f90,sha256=tDZ3fUlazLL8ThJm3VwNGJ75QIlLcW70NnMFv-JA4W0,224 +numpy/f2py/tests/src/modules/use_modules.f90,sha256=UsFfx0B2gu_tS-H-BpLWed_yoMDl1kbydMIOz8fvXWA,398 +numpy/f2py/tests/src/negative_bounds/issue_20853.f90,sha256=fdOPhRi7ipygwYCXcda7p_dlrws5Hd2GlpF9EZ-qnck,157 +numpy/f2py/tests/src/parameter/constant_array.f90,sha256=KRg7Gmq_r3B7t3IEgRkP1FT8ve8AuUFWT0WcTlXoN5U,1468 +numpy/f2py/tests/src/parameter/constant_both.f90,sha256=-bBf2eqHb-uFxgo6Q7iAtVUUQzrGFqzhHDNaxwSICfQ,1939 +numpy/f2py/tests/src/parameter/constant_compound.f90,sha256=re7pfzcuaquiOia53UT7qNNrTYu2euGKOF4IhoLmT6g,469 +numpy/f2py/tests/src/parameter/constant_integer.f90,sha256=nEmMLitKoSAG7gBBEQLWumogN-KS3DBZOAZJWcSDnFw,612 +numpy/f2py/tests/src/parameter/constant_non_compound.f90,sha256=IcxESVLKJUZ1k9uYKoSb8Hfm9-O_4rVnlkiUU2diy8Q,609 +numpy/f2py/tests/src/parameter/constant_real.f90,sha256=quNbDsM1Ts2rN4WtPO67S9Xi_8l2cXabWRO00CPQSSQ,610 +numpy/f2py/tests/src/quoted_character/foo.f,sha256=WjC9D9171fe2f7rkUAZUvik9bkIf9adByfRGzh6V0cM,482 +numpy/f2py/tests/src/regression/AB.inc,sha256=cSNxitwrjTKMiJzhY2AI5FaXJ5y9zDgA27x79jyoI6s,16 +numpy/f2py/tests/src/regression/assignOnlyModule.f90,sha256=c9RvUP1pQ201O_zOXgV0xp_aJF_8llxuA8Uot9z5tr0,608 +numpy/f2py/tests/src/regression/datonly.f90,sha256=9cVvl8zlAuGiqbSHMFzFn6aNWXj2v7sHJdd9A1Oc0qg,392 +numpy/f2py/tests/src/regression/f77comments.f,sha256=bqTsmO8WuSLVFsViIV7Nj7wQbJoZ7IAA3d2tpRDKsnA,626 +numpy/f2py/tests/src/regression/f77fixedform.f95,sha256=hcLZbdozMJ3V9pByVRp3RoeUvZgLMRLFctpZvxK2hTI,139 +numpy/f2py/tests/src/regression/f90continuation.f90,sha256=_W1fj0wXLqT91Q14qpBnM3F7rJKaiSR8upe0mR6_OIE,276 +numpy/f2py/tests/src/regression/incfile.f90,sha256=i7Y1zgMXR9bSxnjeYWSDGeCfsS5jiyn7BLb-wbwjz2U,92 +numpy/f2py/tests/src/regression/inout.f90,sha256=CpHpgMrf0bqA1W3Ozo3vInDz0RP904S7LkpdAH6ODck,277 +numpy/f2py/tests/src/regression/lower_f2py_fortran.f90,sha256=CMQL5RWf9LKnnUDiS-IYa9xc9DGanCYraNq0vGmunOE,100 +numpy/f2py/tests/src/regression/mod_derived_types.f90,sha256=565plqPwWDgnkpSb4-cfZbf3wTM85F2Gocklx5wpGWA,567 +numpy/f2py/tests/src/return_character/foo77.f,sha256=WzDNF3d_hUDSSZjtxd3DtE-bSx1ilOMEviGyYHbcFgM,980 +numpy/f2py/tests/src/return_character/foo90.f90,sha256=ULcETDEt7gXHRzmsMhPsGG4o3lGrcx-FEFaJsPGFKyA,1248 +numpy/f2py/tests/src/return_complex/foo77.f,sha256=8ECRJkfX82oFvGWKbIrCvKjf5QQQClx4sSEvsbkB6A8,973 +numpy/f2py/tests/src/return_complex/foo90.f90,sha256=c1BnrtWwL2dkrTr7wvlEqNDg59SeNMo3gyJuGdRwcDw,1238 +numpy/f2py/tests/src/return_integer/foo77.f,sha256=_8k1evlzBwvgZ047ofpdcbwKdF8Bm3eQ7VYl2Y8b5kA,1178 +numpy/f2py/tests/src/return_integer/foo90.f90,sha256=bzxbYtofivGRYH35Ang9ScnbNsVERN8-6ub5-eI-LGQ,1531 +numpy/f2py/tests/src/return_logical/foo77.f,sha256=FxiF_X0HkyXHzJM2rLyTubZJu4JB-ObLnVqfZwAQFl8,1188 +numpy/f2py/tests/src/return_logical/foo90.f90,sha256=9KmCe7yJYpi4ftkKOM3BCDnPOdBPTbUNrKxY3p37O14,1531 +numpy/f2py/tests/src/return_real/foo77.f,sha256=ZTrzb6oDrIDPlrVWP3Bmtkbz3ffHaaSQoXkfTGtCuFE,933 +numpy/f2py/tests/src/return_real/foo90.f90,sha256=gZuH5lj2lG6gqHlH766KQ3J4-Ero-G4WpOOo2MG3ohU,1194 +numpy/f2py/tests/src/routines/funcfortranname.f,sha256=oGPnHo0zL7kjFnuHw41mWUSXauoeRVPXnYXBb2qljio,123 +numpy/f2py/tests/src/routines/funcfortranname.pyf,sha256=coD8AdLyPK4_cGvQJgE2WJW_jH8EAulZCsMeb-Q1gOk,440 +numpy/f2py/tests/src/routines/subrout.f,sha256=RTexoH7RApv_mhu-RcVwyNiU-DXMTUP8LJAMSn2wQjk,90 +numpy/f2py/tests/src/routines/subrout.pyf,sha256=c9qv4XtIh4wA9avdkDJuXNwojK-VBPldrNhxlh446Ic,322 +numpy/f2py/tests/src/size/foo.f90,sha256=IlFAQazwBRr3zyT7v36-tV0-fXtB1d7WFp6S1JVMstg,815 +numpy/f2py/tests/src/string/char.f90,sha256=ihr_BH9lY7eXcQpHHDQhFoKcbu7VMOX5QP2Tlr7xlaM,618 +numpy/f2py/tests/src/string/fixed_string.f90,sha256=5n6IkuASFKgYICXY9foCVoqndfAY0AQZFEK8L8ARBGM,695 +numpy/f2py/tests/src/string/gh24008.f,sha256=UA8Pr-_yplfOFmc6m4v9ryFQ8W9OulaglulefkFWD68,217 +numpy/f2py/tests/src/string/gh24662.f90,sha256=-Tp9Kd1avvM7AIr8ZukFA9RVr-wusziAnE8AvG9QQI4,197 +numpy/f2py/tests/src/string/gh25286.f90,sha256=2EpxvC-0_dA58MBfGQcLyHzpZgKcMf_W9c73C_Mqnok,304 +numpy/f2py/tests/src/string/gh25286.pyf,sha256=GjgWKh1fHNdPGRiX5ek60i1XSeZsfFalydWqjISPVV8,381 +numpy/f2py/tests/src/string/gh25286_bc.pyf,sha256=6Y9zU66NfcGhTXlFOdFjCSMSwKXpq5ZfAe3FwpkAsm4,384 +numpy/f2py/tests/src/string/scalar_string.f90,sha256=ACxV2i6iPDk-a6L_Bs4jryVKYJMEGUTitEIYTjbJes4,176 +numpy/f2py/tests/src/string/string.f,sha256=shr3fLVZaa6SyUJFYIF1OZuhff8v5lCwsVNBU2B-3pk,248 +numpy/f2py/tests/src/value_attrspec/gh21665.f90,sha256=JC0FfVXsnB2lZHb-nGbySnxv_9VHAyD0mKaLDowczFU,190 +numpy/f2py/tests/test_abstract_interface.py,sha256=PXNQB0DZdmdZyysJkB8f9GY0_hA3hGkmha8aQBXc1Sk,811 +numpy/f2py/tests/test_array_from_pyobj.py,sha256=N1RJ0yFcLs6cFmdxSjizjfLRTEhdKRhrO9Vx8bcG0GU,23696 +numpy/f2py/tests/test_assumed_shape.py,sha256=8kPoQWn6IfMWNMba0al7a5XopKb3JnvZP3V3P6O2F8o,1467 +numpy/f2py/tests/test_block_docstring.py,sha256=P3K0QqnY0UfUQPc3vDrlP_WlZ6gNJ7iokG-D-ZG9tXQ,584 +numpy/f2py/tests/test_callback.py,sha256=P_5qM1xWOYfjeDgd70cIVpV1h0_tA1AP3kxRZDAeqII,7099 +numpy/f2py/tests/test_character.py,sha256=R6FhfIi85E6L1qwlJtsnTCvNgFRriE3kSXefTwIVgLk,21931 +numpy/f2py/tests/test_common.py,sha256=gr4MF659JBWvSY4eQAqgHnOrVbEpq0ZhGM5Cdbye1L4,644 +numpy/f2py/tests/test_crackfortran.py,sha256=x_E4KmEfBX5SFsNkO_-mUi4W_WuzB-ZFsLOfUdHjLVE,16413 +numpy/f2py/tests/test_data.py,sha256=tete-xcIZHZi5VFjy_pyTjr5AjhQzoyJvLsT9QLYU1M,2895 +numpy/f2py/tests/test_docs.py,sha256=wGsRmCJugExEAvj25pANoLr45S6fkpG4kf47dnfg9Ew,1855 +numpy/f2py/tests/test_f2cmap.py,sha256=zM8lksGAoH-cRvEVRkzciZ4oqH28obd-vvMVUObVjt0,387 +numpy/f2py/tests/test_f2py2e.py,sha256=aGZnZH5USd8FJpG5F1L6bWfUzuUqP954lit5-TDPbeE,27834 +numpy/f2py/tests/test_isoc.py,sha256=g5PLyJuAYwF0obaZ55j_e-CNOODJcADsYFSfxcCl5LM,1434 +numpy/f2py/tests/test_kind.py,sha256=ovQVxbtbbnb-Keo8Dh2LpDyPLbIA1uxiZOzMLo5KMX0,1825 +numpy/f2py/tests/test_mixed.py,sha256=DZcTCCle0o4aopFmGi58KtxzP6NFFci4N-pL3_HLb90,862 +numpy/f2py/tests/test_modules.py,sha256=GaOwxLf8KLdNkWIl9fveT9xg_wvCFdDsel9QiFweCAE,2301 +numpy/f2py/tests/test_parameter.py,sha256=P8hDezlxKN_Cm06oWGkS0pwlJvQz5QYwBsyTEA_Y1PQ,4634 +numpy/f2py/tests/test_pyf_src.py,sha256=xV81hRiGeytFFKeVnp-6O2OrGVdzJyecMEalCQSoDoI,1134 +numpy/f2py/tests/test_quoted_character.py,sha256=x19FhD6ZA7JkDuLuiXi48sGd8b42SPRuwwEY8CVRb24,477 +numpy/f2py/tests/test_regression.py,sha256=APQz3e38jz-AbGEBN5n-P1Wuegx4Da1ze7D7nLLpUL8,6197 +numpy/f2py/tests/test_return_character.py,sha256=t8cxO8LatnBXf2EU-HkfmdxvdHMYDk9DLx3kNUTArC4,1534 +numpy/f2py/tests/test_return_complex.py,sha256=_uWrnSh-IDL8men8X__5srP4wM0RkICr4WVJgoNgrzY,2440 +numpy/f2py/tests/test_return_integer.py,sha256=ng_cpFX4nStcpSFoYdD9GiUdCJSXPU0On2MLOA4uOpQ,1813 +numpy/f2py/tests/test_return_logical.py,sha256=OrS11uAw_asDamL7inRKf-S-7SBG0GTS8Vrqlexrkm0,2048 +numpy/f2py/tests/test_return_real.py,sha256=ynInWwkcRfUe981kGJnrkkZeKK7QFlvkiODoIJj6Jg0,3273 +numpy/f2py/tests/test_routines.py,sha256=f9pR8FNJgKuBWtzCjlfniWVHJecpW6gSNkGDb2t693c,795 +numpy/f2py/tests/test_semicolon_split.py,sha256=akc4xJiHI6xOCfpCEtFYPMz8qy2K5jODEPyJHYQvLdE,1627 +numpy/f2py/tests/test_size.py,sha256=SjES727lNcCJFePDnh7uBhncOXWOcqHqVPbZPvBO5js,1155 +numpy/f2py/tests/test_string.py,sha256=47wYPuO1NkjhXSbbyS8vBKsNCju5dA9uMjNhGPx-BGg,2938 +numpy/f2py/tests/test_symbolic.py,sha256=dmuYLhhcv-rT-ux_aVrWaJj_Yxmznezl6Enu8-ediK0,18342 +numpy/f2py/tests/test_value_attrspec.py,sha256=4wY9qPXl0JoPGCG7GyyuMDKLfsHAV8KRWGdEk9-ZZT8,330 +numpy/f2py/tests/util.py,sha256=KIDsCW5uZXe6jSdWpY9Ozlqs5-v-eeDsW3P5TDWKDzo,12112 +numpy/f2py/use_rules.py,sha256=emZhSLPbNDyBHnsfKKXDnGz4P_gwrgL0dfCZcD3n9D4,3376 +numpy/f2py/use_rules.pyi,sha256=gIAAemWfcidclVYZUpa6RRmSdUEDw4FDnGPaCNo93Zw,424 +numpy/fft/__init__.py,sha256=OWE0m6H_blyv1wtqQpiXU5kqxF6O2UxxcV5t11U05RE,8291 +numpy/fft/__init__.pyi,sha256=6XgAsd9coqJ3jBOPD3vn1-8AcbMLhjxzQd21xjeqmlA,514 +numpy/fft/_helper.py,sha256=hIn2ZyEYG4fLB3MGvCPvpSrLXFfh-xO4zGKljk_TQjY,6787 +numpy/fft/_helper.pyi,sha256=1A1kitc5k62ER6X1XLF7PIQL5FiVxxRKu_iCqiQ1kIU,1394 +numpy/fft/_pocketfft.py,sha256=CfpApR9R0SOucql9gp9vXadm_y5cBM-Xnj5trDpvFSE,62598 +numpy/fft/_pocketfft.pyi,sha256=_RIRwdhtixjN4qszZk-xeYn2jmcW_NNAMEJHeETigv0,3174 +numpy/fft/_pocketfft_umath.cpython-312-x86_64-linux-gnu.so,sha256=6EnciecXsqMVlI3QEneGCYNyx6IgbP0TD6gYtz5pFk0,539072 +numpy/fft/helper.py,sha256=RoEADsOnoCgSTL1gE5n-36llz8iwxGzn52af3L-9KEY,611 +numpy/fft/helper.pyi,sha256=KsF45bVyZ4_eJbBFpkER9L8MCWmg7dJuhLqY_7uFNZs,891 +numpy/fft/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +numpy/fft/tests/test_helper.py,sha256=LeVDCCdHzFhmCQ5ByMtVyA22GphgTQS5dupuxrLE8X0,6154 +numpy/fft/tests/test_pocketfft.py,sha256=PCF833rSWsXOMWN8wCluhq0aYHU24_tHbuMl1PuO6dE,24446 +numpy/lib/__init__.py,sha256=zYGuqEfPqq7LDbidpxYs8GgCNAmoJ4xQgFvF3XKJ5Rg,3004 +numpy/lib/__init__.pyi,sha256=Z7OsQAZGURd4cI3xnEF37unbOUqtknwEkT8yQTF-AF8,1651 +numpy/lib/_array_utils_impl.py,sha256=GYWiyNqLQ7DGUSBXz0bbR6AAqZStDIwUe7tsbZ__15M,1697 +numpy/lib/_array_utils_impl.pyi,sha256=AktSeZcFe_XUQ6utYHQyJKG8l8bhM8tQL2Kttj1DjcQ,820 +numpy/lib/_arraypad_impl.py,sha256=z5--XT80TcnDZezHVrdxauJSY3yC4vMDdd7JlO-h3zw,32296 +numpy/lib/_arraypad_impl.pyi,sha256=W98XPsguuf8B924KVVxs6l_EOBM9JKzwTmHL98CKbs0,1837 +numpy/lib/_arraysetops_impl.py,sha256=VFdgpFZJcyJhYFPcTk_LQD_SrqX6poy_shsLKvZigy0,41275 +numpy/lib/_arraysetops_impl.pyi,sha256=Yh-w9l43w6vMBLfwzIKQlxHcE6gFqOsfu5gyKpMgc_s,13403 +numpy/lib/_arrayterator_impl.py,sha256=HtOADIHuG9ADbbMTgmh4P_muke1V-8E-FNEO3bVOGPA,7218 +numpy/lib/_arrayterator_impl.pyi,sha256=8u0nb5NPpWNib-FlWaXlp6BXBPgTv5__NF30FD_1qmM,1876 +numpy/lib/_datasource.py,sha256=zk-Vbn4JlDHEVa3De6A3NgjnnizSJi-HF0ZvvA6YIo4,22731 +numpy/lib/_datasource.pyi,sha256=135RvD3p-3mHdNp_sZV4aN9brwEFvEM49VE1eHlFEfs,996 +numpy/lib/_format_impl.py,sha256=zcQ3xXxPf7epktsYrcdBbIPuOCh9OPV1g3gB6ghf4rE,36865 +numpy/lib/_format_impl.pyi,sha256=_0lEht2hKbTevv0eGChmYMBTAg-2jAfvrfU9p326VHs,869 +numpy/lib/_function_base_impl.py,sha256=AZGyN29Ecw4LRuU1TNUcPC7cVHO9ye4bJ9FQI7n_Gwc,196425 +numpy/lib/_function_base_impl.pyi,sha256=HY21gmJcUIvTsYL2UUZ8l2MYj34cp_7Mdsmck-FjeEE,24116 +numpy/lib/_histograms_impl.py,sha256=Utu7aAQc7ZpsHn_04ogUnZq1ZcdHfipcq9eRq817oVU,38432 +numpy/lib/_histograms_impl.pyi,sha256=QouOxW0sa_LMJ2hDv5WEO9k95mTMjEvbP2-7swNJxzI,1093 +numpy/lib/_index_tricks_impl.py,sha256=g7Np4E8AG9sgyi9HTUgvOM08pIlAj_cvXw4cc7NrU5I,32186 +numpy/lib/_index_tricks_impl.pyi,sha256=gQwY1mj_Sxk2eo9BXcqJ68F88XvzQB81o0nNUkQ9w9o,6325 +numpy/lib/_iotools.py,sha256=0jtpvpl5L-_1ODI21F-1i19t1e3L-6wJxRd1CSLewL0,30876 +numpy/lib/_iotools.pyi,sha256=69hfBI89W2UP6ozHiSByt-GxTupni-gBRPihFbXSh6Q,3393 +numpy/lib/_nanfunctions_impl.py,sha256=cdOT7dYwjvUpI9iEHTrwzbbtKhP9ZZgOCMirTBeYPUk,71949 +numpy/lib/_nanfunctions_impl.pyi,sha256=j5dyJz_c-SQDxXrL9N2ouKC-DsP_EVDZyLedGXqCpMI,833 +numpy/lib/_npyio_impl.py,sha256=kucazwCufh4mNwECyZxEerxsqa_GxQMz1kYuZURDI8s,99277 +numpy/lib/_npyio_impl.pyi,sha256=WWlGxbobwLgEiD-k58g_Q9K1HW1vDk--AYrBSjjqALE,9388 +numpy/lib/_polynomial_impl.py,sha256=TWiqlG3WDa97tayxQCEltZD9TNhUyFprzL_Umd7Lxso,44134 +numpy/lib/_polynomial_impl.pyi,sha256=9PvnPmeCk45ldiJ8xHwsIVdX9DrjPhY9H7CEFbVJMLQ,6999 +numpy/lib/_scimath_impl.py,sha256=QAU4uM_INzVqCTs-ATEyy1JhREl_wDJn_ygU75YtfgE,15692 +numpy/lib/_scimath_impl.pyi,sha256=pXBZjHPB_FbeBfe9M3N8TjrET_oclGuafWjTHC-xjUs,2774 +numpy/lib/_shape_base_impl.py,sha256=5vkU9rPOwKvSc7TzxdfWtM08uV0m15iHPTxbqcY47Oc,39479 +numpy/lib/_shape_base_impl.pyi,sha256=36gmgbFd1cUmSUfUihFtb1brc2gKLYi8NXDAEzLyBmQ,5412 +numpy/lib/_stride_tricks_impl.py,sha256=y3Uxp3jFzDwmIQ137N2zap7-vW_jONUQmXnbfqrs60A,18025 +numpy/lib/_stride_tricks_impl.pyi,sha256=6rR7IO04w1FPCKUM920r9Kf_A_hpZbIABo6Rcl34tFI,1815 +numpy/lib/_twodim_base_impl.py,sha256=3nOLvCD6cfM6MD3o381F48GB8poqsUGDCDOQlOBQXmY,33925 +numpy/lib/_twodim_base_impl.pyi,sha256=nBRqOTSD21ioBkUw6vtzy1-ZyczJcvybkvG3-hvSIkY,11193 +numpy/lib/_type_check_impl.py,sha256=WeVfWz_0Klvb2K_6l0x4nHwHBwPYgfcxeZinV_dp_mw,19221 +numpy/lib/_type_check_impl.pyi,sha256=xpZV5LStVGHbEDAcJUbD7iZFE0onwCPZZuwb01P4o_Q,9713 +numpy/lib/_ufunclike_impl.py,sha256=0eemf_EYlLmSa4inNr3iuJ1eoTMqLyIR0n6dQymga3Y,6309 +numpy/lib/_ufunclike_impl.pyi,sha256=SJ7wbjWFI6WL_rp3CNqbZoKoza4Ou4uDwXvpt4iekys,1288 +numpy/lib/_user_array_impl.py,sha256=t3nnrFuvbBizFV1K3C9NNyIM80LU5spA88MlrYJzEok,7697 +numpy/lib/_user_array_impl.pyi,sha256=AZpI9fHHYpLxyYL9ud5YDHcZhxLl-YpfB23i9f154BQ,9110 +numpy/lib/_utils_impl.py,sha256=7BSreRcHNIsUeMj3U1GbqzVjJYKvyuEWHdG_C4TM46Q,23346 +numpy/lib/_utils_impl.pyi,sha256=ckxdUjdGEaa3JAKVQZHYgZ1R3glZZg-ssh90vkV7dJg,371 +numpy/lib/_version.py,sha256=4dUrc9Js0KPEQ5adoYKR5dnP4ffjCDtJUKPqcMauwY4,4851 +numpy/lib/_version.pyi,sha256=vysY5Vl_nh4si6GkMXEoB6pUDl-jJ5g0LpSDa40F124,641 +numpy/lib/array_utils.py,sha256=XbcyhJ9S0IlNnP9Ny6yygLMEACWWUPNOU8vevj1TEpI,144 +numpy/lib/array_utils.pyi,sha256=LfY_fzfTdtjoLIi5FSCDsC5weYrmAHFh7fxFfniupbg,296 +numpy/lib/format.py,sha256=npJ0eJhT7uKNK5a0lCMGfiJv-R4jyNhiIPeZbJcNXBs,477 +numpy/lib/format.pyi,sha256=fh-5SN4MORvjLliV8LwOb3VqG8tFvOaMeG4Vn5CBusA,1482 +numpy/lib/introspect.py,sha256=u-wgfMuYt8GI3AnRNdXs4j4w9eNTsazlqrazS-P7gKA,2749 +numpy/lib/introspect.pyi,sha256=AWVX6b9mzdwsxizOY0LydWKBEpGatHaeeXGc2txYJEM,152 +numpy/lib/mixins.py,sha256=Kff76ScpgWV3cruicI9A7a4zfBnGVmXtwQzMzu5xDEo,7200 +numpy/lib/mixins.pyi,sha256=I3iXqrcHpV4jwsgBGJKT2Ero2SlTSEZZDmfcx3DJ7Cc,3131 +numpy/lib/npyio.py,sha256=eaPvfHGSzUE70TJHHLOCPIX9G5ihMuBEexy6_PNhJ9Q,68 +numpy/lib/npyio.pyi,sha256=qX68dlgy7M2MtAgNSabTV8rWOTXOXCE1_72XcdJq10Y,192 +numpy/lib/recfunctions.py,sha256=T4aa5xXav9ntfw5YmzPiq_YUkh12wGk40XyBLQPCEzU,59539 +numpy/lib/recfunctions.pyi,sha256=NTf4FyM2Kinx56nNHcyGjKUz_RBSJQr-qtZsLKeIYvQ,13216 +numpy/lib/scimath.py,sha256=qjFaQeq0zEIl7gKqOhaj_vmCC_KaFdyTmHdLUUkSp5I,169 +numpy/lib/scimath.pyi,sha256=Fe7sfleFSY0uCGUj5gATxjEoMnva1nJ53YyP1wP11Nk,512 +numpy/lib/stride_tricks.py,sha256=x0_BfwlycBAlR3BvpxTndeP96dHBT_fASbkTTTzBYgI,88 +numpy/lib/stride_tricks.pyi,sha256=FLo0b8NlLPsS58VzjFFchivpBOjjE_meU0EhWEFPQNY,170 +numpy/lib/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +numpy/lib/tests/data/py2-np0-objarr.npy,sha256=ZLoI7K3iQpXDkuoDF1Ymyc6Jbw4JngbQKC9grauVRsk,258 +numpy/lib/tests/data/py2-objarr.npy,sha256=F4cyUC-_TB9QSFLAo2c7c44rC6NUYIgrfGx9PqWPSKk,258 +numpy/lib/tests/data/py2-objarr.npz,sha256=xo13HBT0FbFZ2qvZz0LWGDb3SuQASSaXh7rKfVcJjx4,366 +numpy/lib/tests/data/py3-objarr.npy,sha256=7mtikKlHXp4unZhM8eBot8Cknlx1BofJdd73Np2PW8o,325 +numpy/lib/tests/data/py3-objarr.npz,sha256=vVRl9_NZ7_q-hjduUr8YWnzRy8ESNlmvMPlaSSC69fk,453 +numpy/lib/tests/data/python3.npy,sha256=X0ad3hAaLGXig9LtSHAo-BgOvLlFfPYMnZuVIxRmj-0,96 +numpy/lib/tests/data/win64python2.npy,sha256=agOcgHVYFJrV-nrRJDbGnUnF4ZTPYXuSeF-Mtg7GMpc,96 +numpy/lib/tests/test__datasource.py,sha256=0vL8l30yb53Wwnt0YbdqvOl2xQf9fc0S-0pRTMAdaYc,10581 +numpy/lib/tests/test__iotools.py,sha256=LTODsFclDQnIbKQb98hEysgVhQ6cs230aj45pA1QYFc,13765 +numpy/lib/tests/test__version.py,sha256=SwXoEqMap603c2jd7ONod0ZOVQeX6T-zArMf03OCHbw,1999 +numpy/lib/tests/test_array_utils.py,sha256=hPXtCjoBKe6MP91sg_04EBpRYg7MITVlCAgD1AScjx8,1118 +numpy/lib/tests/test_arraypad.py,sha256=GzqMIQ0Y8XLYmP5osXzl5W1Pcywy_OK-39STKoCWJc4,56155 +numpy/lib/tests/test_arraysetops.py,sha256=GKotFUbKgEfHybghYP1zIM0RWMqW1pa4cdYlML1seXQ,40445 +numpy/lib/tests/test_arrayterator.py,sha256=1LZmgQQJpndfwh3X2mL4JpaWvKQl9a0WAnQdSpXimhM,1301 +numpy/lib/tests/test_format.py,sha256=BTKd2lUodd8gNznWkh_Hl3mG8Mu8SOFADEqGd5kCw64,41956 +numpy/lib/tests/test_function_base.py,sha256=z2SkeGd9qQjXmaxk6bhoi06qlfxdrDzJEqRsDxIuEoM,171119 +numpy/lib/tests/test_histograms.py,sha256=QkcA46lJ1Y-T3f4-Qn7kn6J9bIid3RLK7NKMrUI3Rpw,33966 +numpy/lib/tests/test_index_tricks.py,sha256=bp4GFjqQ3s_taGDVsCOgs5YU7qtDMhQuPGwvcCxj2sk,20477 +numpy/lib/tests/test_io.py,sha256=8StHTe3-XsyPNBy4IveftRY1Zba2JTW3ALOHg_bEfRw,110989 +numpy/lib/tests/test_loadtxt.py,sha256=1R_xoumDPtPGQYoWh_WWCFKeb3-9WfLIoMHCYQQ0CtQ,40557 +numpy/lib/tests/test_mixins.py,sha256=9r6tgP4Wb6vCDn590PkHmHl-GBAoAL6_-mwp2wbiaO0,7009 +numpy/lib/tests/test_nanfunctions.py,sha256=1GGtPUD8bS5v2FxLr8e0BUgx9k6Iu-8WLZisawPY4Yw,54098 +numpy/lib/tests/test_packbits.py,sha256=REkoSXh9FVVTizyyHWkLqXFLIjt0rynXeixhK8-gBgk,17543 +numpy/lib/tests/test_polynomial.py,sha256=3Z7x5gf2cSb5pN5e0Sb_hZetF3mI5GrTLv-OaN7v0m0,12312 +numpy/lib/tests/test_recfunctions.py,sha256=xYsC_t_tpIpWJvS1pRU2HNxZTO1cJ3QZ1OnXt4ajm0s,43928 +numpy/lib/tests/test_regression.py,sha256=UURtmtwfrxMDF3UY1ZMNbgIJOa38jUzYKCmpYYD8e3Q,7716 +numpy/lib/tests/test_shape_base.py,sha256=ZWHeWCs9x0sD-L03h6kTmUdRHvxHVC-8KOu8KomhyKQ,27406 +numpy/lib/tests/test_stride_tricks.py,sha256=tBErppWSp8jAckkx_zN5ZbAhfKxZJ99cOQxDI9B_xh0,23030 +numpy/lib/tests/test_twodim_base.py,sha256=-djv2iP3W2sB4rAgj9Orl8alGwDFfPvcVu6CNvlKIcg,18925 +numpy/lib/tests/test_type_check.py,sha256=2M6uyLSI-CP13CAylnBn3kbT6nrK6wYWW-Scw13vsAQ,14796 +numpy/lib/tests/test_ufunclike.py,sha256=5a65WfziLpjPJ_yE8zg-A-q08xlyiU8_S1JH8kb-Uyw,3015 +numpy/lib/tests/test_utils.py,sha256=HRZxH8Rs-PxCpMAhgbNOrTfBrsA8B2eTOKypY0Udczw,2374 +numpy/lib/user_array.py,sha256=zs6u6TAXoAySGAZc1qE6fKD4AN-t6urZCaiZaKmHiso,63 +numpy/lib/user_array.pyi,sha256=8C-aTekEYA0bVU7F3turaw1w0j8FfFvDp9xKa9Pfe94,53 +numpy/linalg/__init__.py,sha256=7pVvFwOJFKOArGeUs6MNj3MNqqsx7xx0vt2_7avNAg4,2124 +numpy/linalg/__init__.pyi,sha256=C3fZHKPSa4wpfRqfTjw3DpzE5p-Czjus48OuMLsDckQ,1060 +numpy/linalg/_linalg.py,sha256=6rC77pyHWNOHk03DKEnwHezrUCYdAuItQfA61v8lYsw,115106 +numpy/linalg/_linalg.pyi,sha256=inijXDOFEzZayOL37HNKOqyH8wCLQaU0r__pO4do7Ag,11141 +numpy/linalg/_umath_linalg.cpython-312-x86_64-linux-gnu.so,sha256=46UEl6W4XZFSu4ZGDC_RgVSBzAMg4Wi45u-FXiZARdk,231833 +numpy/linalg/_umath_linalg.pyi,sha256=awvRP1FGuomyfeaR0wzHvrXURAI8tUF3u2RRZ24hkXw,1409 +numpy/linalg/lapack_lite.cpython-312-x86_64-linux-gnu.so,sha256=yKClyTWZEb-1IENKoh6zVgluRTPO5foLIRXC2LMJd9c,30001 +numpy/linalg/lapack_lite.pyi,sha256=QjaS8R4uu6MiJDcCFNE5EOAYGnFCcrNz873gs2OUXEM,2672 +numpy/linalg/linalg.py,sha256=6NimP68tYa0qBRglWH87_tOh2scshtDpcwfvBvmd6Po,585 +numpy/linalg/linalg.pyi,sha256=8E5sbKeM5Ors7r143mM7A4ui8kFZM0SF7NfUGW1eN-4,932 +numpy/linalg/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +numpy/linalg/tests/test_deprecations.py,sha256=9p_SRmtxj2zc1doY9Ie3dyy5JzWy-tCQWFoajcAJUmM,640 +numpy/linalg/tests/test_linalg.py,sha256=VEvQHtAe0o3iVPogcCv9Frx-0HOyNH7WsQHcbkVfgaQ,84998 +numpy/linalg/tests/test_regression.py,sha256=9a96oyeEGQMUxfw_-GUjNWqn51iu4Cf7kllJ0bKp9ws,6704 +numpy/ma/API_CHANGES.txt,sha256=F_4jW8X5cYBbzpcwteymkonTmvzgKKY2kGrHF1AtnrI,3405 +numpy/ma/LICENSE,sha256=BfO4g1GYjs-tEKvpLAxQ5YdcZFLVAJoAhMwpFVH_zKY,1593 +numpy/ma/README.rst,sha256=krf2cvVK_zNQf1d3yVYwg0uDHzTiR4vHbr91zwaAyoI,9874 +numpy/ma/__init__.py,sha256=XpDWYXwauDc49-INsk455D03Uw4p6xFdsdWOn2rt87U,1406 +numpy/ma/__init__.pyi,sha256=QV7F1eN7GQLA2V2vI_bYXC_XhoZl-2IqXHWIqJtXLKU,6946 +numpy/ma/core.py,sha256=Te0RIWw8JyG2iJJjeSiG_t1ahKAICDdr7_pl4G6Q1Yc,288881 +numpy/ma/core.pyi,sha256=RxL-vzdzpBB97UqNesAkHjvFxQUom1ARUvKurQgz58I,40459 +numpy/ma/extras.py,sha256=f8qf6t_x9k34OKmHiNIft9PFCyLYMeBSGhiYjhUuIpc,70680 +numpy/ma/extras.pyi,sha256=4nJDP_0yoEtchWJgczd0ubXba76TsGPBOuCRexQgVbE,3794 +numpy/ma/mrecords.py,sha256=00gzzy_xxC408pVZIRUSRhbwqc1UHcyhE-tO2FYM8IE,27073 +numpy/ma/mrecords.pyi,sha256=YW81zL9LDzi-L-2WI7135-HxBzj12n4YgARHh2qZ6Bs,1973 +numpy/ma/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +numpy/ma/tests/test_arrayobject.py,sha256=MSvEcxlsVt4YZ7mVXU8q_hkwM0I7xsxWejEqnUQx6hE,1099 +numpy/ma/tests/test_core.py,sha256=novMpyqUqf9O7970aVB2HUqTBSiUqMQINMas3PbTgjM,219717 +numpy/ma/tests/test_deprecations.py,sha256=Hye4FMqAdPOOCVnihbs4R8ntLvYJy6WF3LA29876urI,2569 +numpy/ma/tests/test_extras.py,sha256=BnFaTx33kNdLDuLJ74Dt1f7gGsD_noYFmBGA8UelUqI,78435 +numpy/ma/tests/test_mrecords.py,sha256=ZDEv-LbPlx4Qf9NQs8unNXgrdXupRv4IQljf4_vCr34,19894 +numpy/ma/tests/test_old_ma.py,sha256=PMA26SyXJxN0o-pPvyEhl_YF2zRcxuPRMPAXztKCphA,33018 +numpy/ma/tests/test_regression.py,sha256=_eskYMrmSHe-_iODK6mvRD5gN_w6NpAl5agsyIGRRUo,3303 +numpy/ma/tests/test_subclassing.py,sha256=_TQZ4WM2VG-yuITIXeRZbAZrWDHpxtQoLzDKbGRmuHM,16936 +numpy/ma/testutils.py,sha256=vNG1ay689zOktrm-33tyz0bsCLxkJHK6j--2JtHRPq4,10235 +numpy/matlib.py,sha256=_S9N8S2NNsHGQUcloxrxABtJDejHiUyMdMJO7SayPkA,10638 +numpy/matlib.pyi,sha256=d9Tw-ThrWNUgXKGTiQvCjqrkWQSWqHcXUXAxvYENtYk,9602 +numpy/matrixlib/__init__.py,sha256=Ut6IqfjuA-kwwo6HBOYAgFjXXC_h7YV_3HyDsKM72dk,243 +numpy/matrixlib/__init__.pyi,sha256=e9xC6kWhIYoPqa3-tmtxdaq8RLjXrBjpyXLqV-pV9UY,106 +numpy/matrixlib/defmatrix.py,sha256=wpw6lZU9X6qp8wAJokDXt2RBrL1eXqlmBt-ojIwYzlU,30875 +numpy/matrixlib/defmatrix.pyi,sha256=ReQicwbCq4EFGM6paj5KoTeFK3fyiBMC4fJLJcP0SI4,478 +numpy/matrixlib/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +numpy/matrixlib/tests/test_defmatrix.py,sha256=G9v4-cGuAbHVVDCJ2rCUnQrSTUChOih_6ZMV-ZlYsNA,14977 +numpy/matrixlib/tests/test_interaction.py,sha256=BMpaAIeGOJ5EEHWuozBifN8l3Av5RO6jGoaPgdzTiqQ,11874 +numpy/matrixlib/tests/test_masked_matrix.py,sha256=UN212xE5e3G9OuwdOWvRMFT5-z3zIfjQQIIpY26a52k,8787 +numpy/matrixlib/tests/test_matrix_linalg.py,sha256=33UxWKz2NwI2Wt3pP0AyaooZ5tCFpbOePWek3XT0a4U,2149 +numpy/matrixlib/tests/test_multiarray.py,sha256=S5kjzsQR2YgT0qIGrNO1lUDl3o-h0EIdg_g3U3CnuRc,555 +numpy/matrixlib/tests/test_numeric.py,sha256=hZ-r921WDG8Ck8KmT6ulgykjHU1QaGY6gprC2OPo-vg,447 +numpy/matrixlib/tests/test_regression.py,sha256=XnfZ4RoTS49XMUyUlHVMc6wcWImNRja7DT1wTdEk428,934 +numpy/polynomial/__init__.py,sha256=gGSwLNpPCpXfPgiJSsgVoVsJ0AS1c-_MWlGOeiG55sI,6726 +numpy/polynomial/__init__.pyi,sha256=tVWqA3_ZzcTyfp5yIr4ca87Tgx4YtY4660UQi3JhfJI,688 +numpy/polynomial/_polybase.py,sha256=b0kCiTgUm8D5QC_LWSm6yNvwC79npDAeksK0vQPciCQ,39358 +numpy/polynomial/_polybase.pyi,sha256=mKbxu6z3iC6NnDNXHPrMm6Vo6RQvrvtCel7S5Mi3Q3Q,8187 +numpy/polynomial/_polytypes.pyi,sha256=e-uO5HmbYsWffZtOKCDgrxEqvUm-YKTqQKXj83m8j6s,22382 +numpy/polynomial/chebyshev.py,sha256=T0vrDsOrO8Ntxbzf_-0dv_lPyF5c45OjDoVJDzeGBAI,62322 +numpy/polynomial/chebyshev.pyi,sha256=jn21NMBsc4FYvC_5BM4kOfnYEaUSINdq3RyooS-5rjU,4787 +numpy/polynomial/hermite.py,sha256=IguwJittKDh3y0rF1M9lLuIptFXgq-PhaHNTjfE3CnA,54603 +numpy/polynomial/hermite.pyi,sha256=bNrlxTVHTskFUOKDbyrISXbOsmPxxhnAGmZmOF1mLpc,2463 +numpy/polynomial/hermite_e.py,sha256=fhuui2jLc0I5WEEsRDcyw8FKSFxOl9jr8b4yRIxEZqQ,52305 +numpy/polynomial/hermite_e.pyi,sha256=OyjRyzP7tz5sDP-90D4dpn82zJ4zPUCIzhpXaOCpkCY,2555 +numpy/polynomial/laguerre.py,sha256=XJ5dNqWuZNhqwARb_QW4nfrRHyJv1JMCgsP2W4-KE9M,52474 +numpy/polynomial/laguerre.pyi,sha256=_72JssagROc-vwt8W1i3aOo8s5l2v2G2NzMUm14vZnw,2191 +numpy/polynomial/legendre.py,sha256=sMJTmGdewNhccrK9CyfNIIFRgzmY-AJHhgo6zxtGYvo,51129 +numpy/polynomial/legendre.pyi,sha256=dPizRI4HLqAQ8Jms8Ta_HtsUyHV49fk3hFCZNOid1fo,2191 +numpy/polynomial/polynomial.py,sha256=-IICosb2j8ClsIfXPDWgXqLx6WuhU6olocU4JkxN7kI,52196 +numpy/polynomial/polynomial.pyi,sha256=A3oK3wKteiRkUcNEkzgvZQ11HIqloIRoxG2X9rPVZBE,2021 +numpy/polynomial/polyutils.py,sha256=mQEa3oCz9X-d1HaNdXkpBJzXWGzgY42WDMjJOn988O8,22657 +numpy/polynomial/polyutils.pyi,sha256=gnB7TQZclbMGneVVFE1z5LX5Qgs3GCidRTWL97rja-4,10235 +numpy/polynomial/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +numpy/polynomial/tests/test_chebyshev.py,sha256=gcK5jVv1vG3O-VVMkZKpmweR6_4HQAXtzvbJ_ib0-B8,20650 +numpy/polynomial/tests/test_classes.py,sha256=nBsVHcubheo1s7t-jUXY984ptC2x-aWDPkWED1cUZt4,18552 +numpy/polynomial/tests/test_hermite.py,sha256=sexvJUDmac1JKL8qOeQr70KJTD1KdoJ10LKosFfBqm0,18687 +numpy/polynomial/tests/test_hermite_e.py,sha256=r3QQOUVoBBVgZzCjE3qzIl-wMcl_kI1Nuc-KGNy7rIw,19026 +numpy/polynomial/tests/test_laguerre.py,sha256=8c2h7Lj3F2DtuVuOPlS8ZL-dq_IoFxPrzREbuI5iZqQ,17637 +numpy/polynomial/tests/test_legendre.py,sha256=hMdOs_RzkGihUzg7gDmeM1FxkIT2UIgqkDWanucfMHg,18805 +numpy/polynomial/tests/test_polynomial.py,sha256=Pi_X6ThfxgVbgzyAnu3FcyTIUvpL9ENxRSanyUjgon8,22911 +numpy/polynomial/tests/test_polyutils.py,sha256=gO7B1oPBRRClF7WeXFsLjGwqUl9kjVIv7aAoHlhqVsk,3780 +numpy/polynomial/tests/test_printing.py,sha256=qk76AKCvHHqbsDnHIVf5fxIEH9Va4U9jwJkJ1b67k1o,21403 +numpy/polynomial/tests/test_symbol.py,sha256=ShBdNg9cvYy31fQnrn4gprZUSD0shz5r8zlG8CEq7gs,5375 +numpy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +numpy/random/LICENSE.md,sha256=EDFmtiuARDr7nrNIjgUuoGvgz_VmuQjxmeVh_eSa8Z8,3511 +numpy/random/__init__.pxd,sha256=9JbnX540aJNSothGs-7e23ozhilG6U8tINOUEp08M_k,431 +numpy/random/__init__.py,sha256=WFzntztUVNaiXCpQln8twyL8HSFNS7XAWJlJsQXgbqk,7480 +numpy/random/__init__.pyi,sha256=5X5UqSDkeruZafGWv9EnYb0RrjRs49r-TlzV3PPQOjs,2109 +numpy/random/_bounded_integers.cpython-312-x86_64-linux-gnu.so,sha256=L4ZvVgABToQOllsfbzYZWSS7YX_lAQy0lDLecAl5tPQ,323168 +numpy/random/_bounded_integers.pxd,sha256=SH_FwJDigFEInhdliSaNH2H2ZIZoX02xYhNQA81g2-g,1678 +numpy/random/_bounded_integers.pyi,sha256=juqd9PbXs4yg45zMJ7BHAOPQjb7sgEbWE9InBtGZhfo,24 +numpy/random/_common.cpython-312-x86_64-linux-gnu.so,sha256=3CWDfhtTpkNvsgJa13OYYCEMwR6sx5jlDp5f_tvr9hc,258912 +numpy/random/_common.pxd,sha256=7kGArYkBcemrxJcSttwvtDGbimLszdQnZdNvPMgN5xQ,4982 +numpy/random/_common.pyi,sha256=02dQDSAflunmZQFWThDLG3650py_DNqCmxjmkv5_XpA,421 +numpy/random/_examples/cffi/extending.py,sha256=jpIL1njMhf0nehmlMHkgZkIxns2JC9GEDYgAChX87G8,884 +numpy/random/_examples/cffi/parse.py,sha256=PK9vdUxwmvdnFvH3rOpgnnpISwnid7ri5XOmBrMWpJw,1750 +numpy/random/_examples/cython/extending.pyx,sha256=ePnHDNfMQcTUzAqgFiEqrTFr9BoDmbqgjxzrDLvV8fE,2267 +numpy/random/_examples/cython/extending_distributions.pyx,sha256=ahvbdSuRj35DKJRaNFP5JDuPqveBBp-M9mFfF3Wd_M4,3866 +numpy/random/_examples/cython/meson.build,sha256=GxZZT_Lu3nZsgcqo_7sTR_IdMJaHA1fxyjwrQTcodPs,1694 +numpy/random/_examples/numba/extending.py,sha256=Z7Z_Xp7HPE4K5BZ7AwpZ29qvuftFAkvhMtNX53tlMMw,1959 +numpy/random/_examples/numba/extending_distributions.py,sha256=fdePXeUj46yXK0MK1cszxUHQiOTiNuNsrbZqPw4AdGs,2036 +numpy/random/_generator.cpython-312-x86_64-linux-gnu.so,sha256=uTLfyCVXI_zU_LKEaTNhjWZx_2eqTWJpaC5gsy6uSU4,993240 +numpy/random/_generator.pyi,sha256=aFPqfOxIpOIOmdY1xBcUpllMCv20iTq4PN7Ad_gd7HY,24009 +numpy/random/_mt19937.cpython-312-x86_64-linux-gnu.so,sha256=drlMZ3K1OmM6C82Is9cE7_SM-aX3jXxDpsGlA-p7drU,137960 +numpy/random/_mt19937.pyi,sha256=ZjOCfOQb1KLDywy8ZHy8pQb1C-DZvStqYK3OOB6rETo,775 +numpy/random/_pcg64.cpython-312-x86_64-linux-gnu.so,sha256=1XcPLj1ve8oR2oftDtkteLUYQ1wbe7xTaK5GbYfxg4U,148272 +numpy/random/_pcg64.pyi,sha256=bIlGJyN2X3gtKEzh6qwDdyXX88al_2vVmCzGNpbNifs,1142 +numpy/random/_philox.cpython-312-x86_64-linux-gnu.so,sha256=lN4_kLuxiAFBkV5qWDkkMN0bcjNpdtimbDDDdWG-fXg,120808 +numpy/random/_philox.pyi,sha256=xFogUASfSHdviqexIf4bGgkzbryir7Tik7z0XQR9xx4,1005 +numpy/random/_pickle.py,sha256=Lt47ma_vnnJHdnQlc5jZ_DqBHsdKi0QiUNaIkMf95qA,2742 +numpy/random/_pickle.pyi,sha256=5obQY7CZRLMDjOgRtNgzV_Bg5O9E8DK_G74j7J7q6qo,1608 +numpy/random/_sfc64.cpython-312-x86_64-linux-gnu.so,sha256=72W67_gd0OXh2WB3ReFF8oUA0uetpMwsK-rEcbiJsjA,89648 +numpy/random/_sfc64.pyi,sha256=wRrbkEGLNhjXa7-LyGNtO5El9c8B_hNRQqF0Kmv6hQM,682 +numpy/random/bit_generator.cpython-312-x86_64-linux-gnu.so,sha256=YodrCvuRmoDRD083dQb-o-yz6H1gbFMPByouT66Dszc,239072 +numpy/random/bit_generator.pxd,sha256=lArpIXSgTwVnJMYc4XX0NGxegXq3h_QsUDK6qeZKbNc,1007 +numpy/random/bit_generator.pyi,sha256=tX5lVJDp6J5bNzflo-1rNylceD30oDBYtbiYVA1cWOY,3604 +numpy/random/c_distributions.pxd,sha256=UCtqx0Nf-vHuJVaqPlLFURWnaI1vH-vJRE01BZDTL9o,6335 +numpy/random/lib/libnpyrandom.a,sha256=0hYJRONXaBW-JS5wptgs-kvXtPhgwBlh7nhh3JVFxho,71702 +numpy/random/mtrand.cpython-312-x86_64-linux-gnu.so,sha256=RBeIO7oWpwHHpJc18NBxPEBn9iQBMXgefhhAy2R88vU,785752 +numpy/random/mtrand.pyi,sha256=Ds2d-DloxUUE2wNNMA1w6oqqPsgBilkaRMCLioBTiJA,22687 +numpy/random/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +numpy/random/tests/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +numpy/random/tests/data/generator_pcg64_np121.pkl.gz,sha256=EfQ-X70KkHgBAFX2pIPcCUl4MNP1ZNROaXOU75vdiqM,203 +numpy/random/tests/data/generator_pcg64_np126.pkl.gz,sha256=fN8deNVxX-HELA1eIZ32kdtYvc4hwKya6wv00GJeH0Y,208 +numpy/random/tests/data/mt19937-testset-1.csv,sha256=Xkef402AVB-eZgYQkVtoxERHkxffCA9Jyt_oMbtJGwY,15844 +numpy/random/tests/data/mt19937-testset-2.csv,sha256=nsBEQNnff-aFjHYK4thjvUK4xSXDSfv5aTbcE59pOkE,15825 +numpy/random/tests/data/pcg64-testset-1.csv,sha256=xB00DpknGUTTCxDr9L6aNo9Hs-sfzEMbUSS4t11TTfE,23839 +numpy/random/tests/data/pcg64-testset-2.csv,sha256=NTdzTKvG2U7_WyU_IoQUtMzU3kEvDH39CgnR6VzhTkw,23845 +numpy/random/tests/data/pcg64dxsm-testset-1.csv,sha256=vNSUT-gXS_oEw_awR3O30ziVO4seNPUv1UIZ01SfVnI,23833 +numpy/random/tests/data/pcg64dxsm-testset-2.csv,sha256=uylS8PU2AIKZ185OC04RBr_OePweGRtvn-dE4YN0yYA,23839 +numpy/random/tests/data/philox-testset-1.csv,sha256=SedRaIy5zFadmk71nKrGxCFZ6BwKz8g1A9-OZp3IkkY,23852 +numpy/random/tests/data/philox-testset-2.csv,sha256=dWECt-sbfvaSiK8-Ygp5AqyjoN5i26VEOrXqg01rk3g,23838 +numpy/random/tests/data/sfc64-testset-1.csv,sha256=iHs6iX6KR8bxGwKk-3tedAdMPz6ZW8slDSUECkAqC8Q,23840 +numpy/random/tests/data/sfc64-testset-2.csv,sha256=FIDIDFCaPZfWUSxsJMAe58hPNmMrU27kCd9FhCEYt_k,23833 +numpy/random/tests/data/sfc64_np126.pkl.gz,sha256=MVa1ylFy7DUPgUBK-oIeKSdVl4UYEiN3AZ7G3sdzzaw,290 +numpy/random/tests/test_direct.py,sha256=-ugW0cpuYhFSGVDtAbpEy_uFk-cG0JKFpPpQMDyFJh4,19919 +numpy/random/tests/test_extending.py,sha256=8KgkOAbxrgU9_cj9Qm0F8r9qVEVy438Q-Usp7_HpSLQ,4532 +numpy/random/tests/test_generator_mt19937.py,sha256=X0AEzi3xy6FzyTpTZNT_lXyXS_LWOWFYc9nZ6QtkILQ,117812 +numpy/random/tests/test_generator_mt19937_regressions.py,sha256=QZVFTSN9gnJXN-ye89JfUoov1Cu65r4e32FMmCYje5U,8107 +numpy/random/tests/test_random.py,sha256=YSlHTwu6t7BAjDLZrBz4e8-ynSuV6eOHP9NwxDoZBvU,70298 +numpy/random/tests/test_randomstate.py,sha256=WbZBpZplBlgmhWKXNsj7d0Zw0BHJ2nxEerMRnuwyYnE,85749 +numpy/random/tests/test_randomstate_regression.py,sha256=1NgkJ60dVg8-UZ-ApepKlZGotqgenW_vZ3jqofMOSlw,8010 +numpy/random/tests/test_regression.py,sha256=DqqLLE3_MW04ltPhSXy44oFx_daO9b4I7NgI-WoMc-s,5471 +numpy/random/tests/test_seed_sequence.py,sha256=0lb4LRofbt_wHO-Cs_d1hwp1WcWjOmxH-OePkXST5bc,3310 +numpy/random/tests/test_smoke.py,sha256=epkUF47HanaSZVz9NVUt6xUmKZhJNolPIB-z4MN67Qw,28141 +numpy/rec/__init__.py,sha256=kNAYYoSAA0izpUVRb-18sJw-iKtFq2Rl2U5SOH3pHRM,83 +numpy/rec/__init__.pyi,sha256=1ZL2SbvFSaoXwOK-378QQ0g0XldOjskx2E2uIerEGUI,347 +numpy/strings/__init__.py,sha256=o27wHW8jGaUfbDopSyEmYD6Rjeo33AzkGBBTgWrlGH4,83 +numpy/strings/__init__.pyi,sha256=JP8YQR3xZ_mPMdQax7QSR2cZ-N-V7ZDqvOcWIIUP_54,1319 +numpy/testing/__init__.py,sha256=Eqe-Ox-3JSqk6QRnnPPFLCW9Ikqv9OuJDhnm2uGM3zc,581 +numpy/testing/__init__.pyi,sha256=1jr2Gj9BmCdtK4bqNGkwUAuqwC4n2JPOy6lqczK7xpA,2045 +numpy/testing/_private/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +numpy/testing/_private/__init__.pyi,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +numpy/testing/_private/extbuild.py,sha256=Lg1sqA94Q74Ki5u-sx0PEj7urr3YP470-BCiyvJwExQ,7716 +numpy/testing/_private/extbuild.pyi,sha256=aNH6UnAhh4Zny81W45GrAcScB12b6_84y8M0Vdtpm2I,626 +numpy/testing/_private/utils.py,sha256=PZFbAxTSOPFQ_VXMaDCgPFBSEk2kcEGh8GRiBJy_yJg,95707 +numpy/testing/_private/utils.pyi,sha256=9xlm7AQwi1yqOZN_t22jI_G9Ov-0tzX5H0ITHVz0UEE,12943 +numpy/testing/overrides.py,sha256=B8Y8PlpvK71IcSuoubXWj4L5NVmLVSn7WMg1L7xZO8k,2134 +numpy/testing/overrides.pyi,sha256=IQvQLxD-dHcbTQOZEO5bnCtCp8Uv3vj51dl0dZ0htjg,397 +numpy/testing/print_coercion_tables.py,sha256=SboNmCLc5FyV-UR8gKjJc2PIojN1XQTvH0WzDq75M2M,6286 +numpy/testing/print_coercion_tables.pyi,sha256=FRNibMYi0OyLIzKD4RUASZyhlsTY8elN0Q3jcBPEdgE,821 +numpy/testing/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +numpy/testing/tests/test_utils.py,sha256=yb2RpPDZvVagXiwQPFhV2IhwslZRkC-d-Vtb5wbJbbo,69575 +numpy/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +numpy/tests/test__all__.py,sha256=AXbT9VmRSTYq9beba4d1Eom_V9SVXXEtpkBdEW2XCqU,222 +numpy/tests/test_configtool.py,sha256=aVO9XZPUq-T0LXFx-sQbtsOcnwKIFnpKtfuWWlnWDFs,1749 +numpy/tests/test_ctypeslib.py,sha256=RNTHi3cYOEPQno5zZQ_WyekW5E_0bVuwmn1AFgkDzY8,12375 +numpy/tests/test_lazyloading.py,sha256=mMbie5VOu7S4uQBu66RNA2ipSsAY4C0lyoJXeHclAvk,1160 +numpy/tests/test_matlib.py,sha256=RMduSGHBJuVFmk__Ug_hVeGD4-Y3f28G0tlDt8F7k7c,1854 +numpy/tests/test_numpy_config.py,sha256=y4U3wnNW0Ags4W_ejhQ4CRCPnBc9p-4-B9OFDcLq9fg,1235 +numpy/tests/test_numpy_version.py,sha256=6PIeISx9_Hglpxc3y6KugeAgB4eBkuZC-DFlXt4LocA,1744 +numpy/tests/test_public_api.py,sha256=KqMtjIjq0_lp2ag4FTtulzypCqyZ43kuUlXgzd_Vkxc,27851 +numpy/tests/test_reloading.py,sha256=T0NTsxAZFPY0LuAzbsy0wV_vSIZON7dwWSNjz_yzpDg,2367 +numpy/tests/test_scripts.py,sha256=QpjsWc0vgi-IFLdMr81horvHAnjRI7RhYyO-edHxzcU,1665 +numpy/tests/test_warnings.py,sha256=ynGuW4FOgjLcwdyi5AYCGCrmAu7jZlIQWPNK-0Yr800,2328 +numpy/typing/__init__.py,sha256=FdaIH47j8uGEA5luTu-DnrOOTFw-3ne2JVHe-yn_7bA,6048 +numpy/typing/mypy_plugin.py,sha256=1pcfLxJaYFdCPKQJVwHvdYbZSVdZ7RSIcg1QXHR7nqM,6541 +numpy/typing/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +numpy/typing/tests/data/fail/arithmetic.pyi,sha256=B1iRvZyWH_beZWBFsUtF6aHtF99VBjWpHQya6IyY1o8,3690 +numpy/typing/tests/data/fail/array_constructors.pyi,sha256=2917vcb59EaV406oGtA9lSQ8n_KDDyfvMrxj1Na6rPM,1200 +numpy/typing/tests/data/fail/array_like.pyi,sha256=klBpaBcONODcPC1LztdVZ3UuIPwXN8kUK_e9s_QnZoo,496 +numpy/typing/tests/data/fail/array_pad.pyi,sha256=mt-nhrs6f4YP7xgXizti7k_AwFAJ50yK97fMrMAAEds,137 +numpy/typing/tests/data/fail/arrayprint.pyi,sha256=i-0R4ExF_gtfXML5qirbsQLmDwJyg6_37HDYsk6g6tI,616 +numpy/typing/tests/data/fail/arrayterator.pyi,sha256=9Y8aD2lkDO7UcLo9ySR0pnVz0p2ofl2Lq4XTHgoMXxA,463 +numpy/typing/tests/data/fail/bitwise_ops.pyi,sha256=3k-IDummVmwkdm3IVIPWCFp4e48TSTyZJKv1lDoPpvs,399 +numpy/typing/tests/data/fail/char.pyi,sha256=IrAk7rxT3ixBVwNWHzCWEsW9rF_oCXTOtwIG-q_Vx2A,2800 +numpy/typing/tests/data/fail/chararray.pyi,sha256=aTqYSgwQUGUVUDkOly_Dy5SdOiHdKKTEbo6jD68KaH0,2356 +numpy/typing/tests/data/fail/comparisons.pyi,sha256=zscovvsL89W8wrL43k4z8z1DLrjXmxBbu6lAOpiyhp0,750 +numpy/typing/tests/data/fail/constants.pyi,sha256=OHaBJo6GYW94BlUWKQDat5jiit5ZAy_h-5bb1WUJnLU,78 +numpy/typing/tests/data/fail/datasource.pyi,sha256=7om31_WCptsSn_z7E5p-pKFlswZItyZm9GQ-L5fXWqM,419 +numpy/typing/tests/data/fail/dtype.pyi,sha256=crqAVZmBYLYV9N-ihiOnKCY1QK4iTxX7J4Tviac2Vq4,305 +numpy/typing/tests/data/fail/einsumfunc.pyi,sha256=4QWkwE4sr5bKz5-OCjPzeUcr4V-wpaMsqee2PXDwyjw,458 +numpy/typing/tests/data/fail/flatiter.pyi,sha256=3WblzrQewUBZo1mjTRWzwda_rQ0HSVamtz2XwH2CgCc,715 +numpy/typing/tests/data/fail/fromnumeric.pyi,sha256=eWCbs1dcoJraAA3b5qME09sWWvsSdlDO912_OwQ_M7k,5685 +numpy/typing/tests/data/fail/histograms.pyi,sha256=wgI2CG-P0jbDw4KohR_nbJxqa34PT1e6nmnLi9KbPQM,376 +numpy/typing/tests/data/fail/index_tricks.pyi,sha256=RNZLHeMOpSX594Eem4WyJrM_QouqndGRVj2YQakJN-E,517 +numpy/typing/tests/data/fail/lib_function_base.pyi,sha256=JdvdZlgNUNzlOuY74T6Lt_hNOpveU6U1jhFGB9Iu6ZA,2817 +numpy/typing/tests/data/fail/lib_polynomial.pyi,sha256=Y3jlwigvtr5tFEHvr3SgguMVsYZe8cvsdgKcavgfucs,937 +numpy/typing/tests/data/fail/lib_utils.pyi,sha256=6Oc_wYI0mv0l74p4pEiVtKjkWFNg4WmXjGW7_2zEKS4,98 +numpy/typing/tests/data/fail/lib_version.pyi,sha256=BvABs2aeC6ZHUGkrsowu80Ks22pbxUMwSPJ8c5i7H14,154 +numpy/typing/tests/data/fail/linalg.pyi,sha256=h9bcCeP0ARGONB3iYGkdX-BFPsNI-pZq3C-nfKgbbBU,1381 +numpy/typing/tests/data/fail/ma.pyi,sha256=inPaC4jP7hGPqQJn-rBspeJZnxJz7m1nVDYQxuMI8SE,6364 +numpy/typing/tests/data/fail/memmap.pyi,sha256=uXPLcVx726Bbw93E5kdIc7K0ssvLIZoJfNTULMtAa_8,169 +numpy/typing/tests/data/fail/modules.pyi,sha256=mEBLIY6vZAPIf2BuyJcMAR-FarSkT55FRlusrsR0qCo,603 +numpy/typing/tests/data/fail/multiarray.pyi,sha256=lSV5JiLNz-CxHUlNbF1Bq3x7mOftfr1kiiG2DgtXilE,1656 +numpy/typing/tests/data/fail/ndarray.pyi,sha256=65IDiOprlv-sg375SBmmh6_hYOzlucTVLe42GymniGM,381 +numpy/typing/tests/data/fail/ndarray_misc.pyi,sha256=hSdxKyxweyxAH32DGa_ZnZIXqPNh6CafBK90rjbi8cs,1061 +numpy/typing/tests/data/fail/nditer.pyi,sha256=nRbQ66HcoKXDKIacbWD9pTq-523GJOqxzJ3r278lDsc,319 +numpy/typing/tests/data/fail/nested_sequence.pyi,sha256=jGjoQhCLr8dbTCPvWkilJKAW0RRMbrY-iEHf24Happo,463 +numpy/typing/tests/data/fail/npyio.pyi,sha256=vPYmFaPCFbr5V2AC3074w8hTCBUYxpSF4fi1sbbfopw,646 +numpy/typing/tests/data/fail/numerictypes.pyi,sha256=NJxviXTJIaDoz7q56dBrHCBNNG-doTu-oIryzwURxHQ,124 +numpy/typing/tests/data/fail/random.pyi,sha256=IvKXQuxZhuK6M0u2x3Y4PhXvLoC8OFnUdoeneaqDiIE,2903 +numpy/typing/tests/data/fail/rec.pyi,sha256=eeFXVvVg4DherRMA1T8KERtTiRN1ZIbarw4Yokb8WrU,741 +numpy/typing/tests/data/fail/scalars.pyi,sha256=EQy8ovBZX49a7AgtRyI8uHgauQoVzAmjE3NALe97tEw,2849 +numpy/typing/tests/data/fail/shape.pyi,sha256=VNucLx9ittp1a0AOyVPd6XKfERm0kq_ND1lOr-LXQ_s,131 +numpy/typing/tests/data/fail/shape_base.pyi,sha256=8366-8mCNii1D0W6YrOhCNxo8rrfqQThO-dIVWNRHvA,157 +numpy/typing/tests/data/fail/stride_tricks.pyi,sha256=g7-DY8Zc8pzTDyOBA-8t6yIFj1FZI9XpvVdbybQN2i0,330 +numpy/typing/tests/data/fail/strings.pyi,sha256=wX9ROrRNhpH9g_ewNGjWuTKU-He4xaNxrtz2Dm3iPo8,2333 +numpy/typing/tests/data/fail/testing.pyi,sha256=m8d2OZZ1DtsHfmnTwvdMRETUfo0lwRzaOXjuyNi08PQ,1399 +numpy/typing/tests/data/fail/twodim_base.pyi,sha256=ROt5iqOp9ENbXlMEG8dzUZxHD3N4lwcbyCffuJ4BLZE,936 +numpy/typing/tests/data/fail/type_check.pyi,sha256=hRXyE4Ywx6zjtSgiHwKRs4k47M4hnPjj7yjVhi91IaU,397 +numpy/typing/tests/data/fail/ufunc_config.pyi,sha256=v5rd68Y2TzLplIOaOXM4h66HqSv8XbapR0b3xaoUOdQ,589 +numpy/typing/tests/data/fail/ufunclike.pyi,sha256=ejCb6kb7mmxPH0QrDsYfdFSLLPFKx0IZ9xSLs3YXOzg,649 +numpy/typing/tests/data/fail/ufuncs.pyi,sha256=XBoxO597ponBkFcCfwCS3s-jKfcnDzC_K5n2uBPrD6E,505 +numpy/typing/tests/data/fail/warnings_and_errors.pyi,sha256=SoFIznFd_xDifIsS0pv0aqS2BvhZaT6xsOA0zJrRJkA,200 +numpy/typing/tests/data/misc/extended_precision.pyi,sha256=n1nzRzRa_oKDdNExxB0qRIQr8MeDIosbLU6Vpgi6ZYo,322 +numpy/typing/tests/data/mypy.ini,sha256=rfUCMP01SsfRLJ-MRGEicI9XW-HJDoTJ_ncaACuKJ0s,245 +numpy/typing/tests/data/pass/arithmetic.py,sha256=t4UK-TROh0uYPlUNn5CZHdTysECmDZa04uUOCZO58cY,7762 +numpy/typing/tests/data/pass/array_constructors.py,sha256=rfJ8SRB4raElxRjsHBCsZIkZAfqZMie0VE8sSKMgkHg,2447 +numpy/typing/tests/data/pass/array_like.py,sha256=-wTiw2o_rLw1aeT7FSh60RKguhvxKyr_Vv5XNXTYeS4,1032 +numpy/typing/tests/data/pass/arrayprint.py,sha256=y_KkuLz1uM7pv53qfq7GQOuud4LoXE3apK1wtARdVyM,766 +numpy/typing/tests/data/pass/arrayterator.py,sha256=FqcpKdUQBQ0FazHFxr9MsLEZG-jnJVGKWZX2owRr4DQ,393 +numpy/typing/tests/data/pass/bitwise_ops.py,sha256=FmEs_sKaU9ox-5f0NU3_TRIv0XxLQVEZ8rou9VNehb4,964 +numpy/typing/tests/data/pass/comparisons.py,sha256=5aGrNl3D7Yd1m9WVkHrjJtqi7SricTxrEMtmIV9x0aE,3298 +numpy/typing/tests/data/pass/dtype.py,sha256=YDuYAb0oKoJc9eOnKJuoPfLbIKOgEdE04_CYxRS4U5I,1070 +numpy/typing/tests/data/pass/einsumfunc.py,sha256=eXj5L5MWPtQHgrHPsJ36qqrmBHqct9UoujjJCvHnF1k,1370 +numpy/typing/tests/data/pass/flatiter.py,sha256=tpKL_EAjkJoCZ5C0iuIX0dNCwQ9wUq1XlBMP-n2rjM4,203 +numpy/typing/tests/data/pass/fromnumeric.py,sha256=d_hVLyrVDFPVx33aqLIyAGYYQ8XAJFIzrAsE8QCoof4,3991 +numpy/typing/tests/data/pass/index_tricks.py,sha256=dmonWJMUKsXg23zD_mibEEtd4b5ys-sEfT9Fnnq08x8,1402 +numpy/typing/tests/data/pass/lib_user_array.py,sha256=Za_n84msWtV8dqQZhMhvh7lzu5WZvO8ixTPkEqO2Hms,590 +numpy/typing/tests/data/pass/lib_utils.py,sha256=bj1sEA4gsmezqbYdqKnVtKzY_fb64w7PEoZwNvaaUdA,317 +numpy/typing/tests/data/pass/lib_version.py,sha256=HnuGOx7tQA_bcxFIJ3dRoMAR0fockxg4lGqQ4g7LGIw,299 +numpy/typing/tests/data/pass/literal.py,sha256=HSG-2Gf7J5ax3mjTOeh0pAYUrVOqboTkrt2m6ssfqVY,1508 +numpy/typing/tests/data/pass/ma.py,sha256=ZIi85AwntBX7M1LIvl4yEGixAauHAS2GINBR42Ri4Hw,3362 +numpy/typing/tests/data/pass/mod.py,sha256=owFL1fys3LPTWpAlsjS-IzW4sSu98ncp2BnsIetLSrA,1576 +numpy/typing/tests/data/pass/modules.py,sha256=g9PhyLO6rflYHZtmryx1VWTubphN4TAPUSfoiYriTqE,625 +numpy/typing/tests/data/pass/multiarray.py,sha256=MxHax6l94yqlTVZleAqG77ILEbW6wU5osPcHzxJ85ns,1331 +numpy/typing/tests/data/pass/ndarray_conversion.py,sha256=d7cFNUrofdLXh9T_9RG3Esz1XOihWWQNlz5Lb0yt6dM,1525 +numpy/typing/tests/data/pass/ndarray_misc.py,sha256=wBbQDHcpiIlMl-z5ToVOrFpoxrqXQMBq1dFSWfwGJNE,3699 +numpy/typing/tests/data/pass/ndarray_shape_manipulation.py,sha256=37eYwMNqMLwanIW9-63hrokacnSz2K_qtPUlkdpsTjo,640 +numpy/typing/tests/data/pass/nditer.py,sha256=nYO45Lw3ZNbQq75Vht86zzLZ4cWzP3ml0rxDPlYt8_8,63 +numpy/typing/tests/data/pass/numeric.py,sha256=pOwxnmZmdCtDKh9ih0h5GFIUPJwsi97NBs1y5ZAGyUM,1622 +numpy/typing/tests/data/pass/numerictypes.py,sha256=6x6eN9-5NsSQUSc6rf3fYieS2poYEY0t_ujbwgF9S5Q,331 +numpy/typing/tests/data/pass/random.py,sha256=UJF6epKYGfGq9QlrR9YuA7EK_mI8AQ2osdA4Uhsh1ms,61824 +numpy/typing/tests/data/pass/recfunctions.py,sha256=GwDirrHsL3upfIsAEZakPt95-RLY7BpXqU_KXxi4HhQ,5003 +numpy/typing/tests/data/pass/scalars.py,sha256=pzV3Y20dd6xB9NRsJ0YSdkcvI5XcD8cEWtEo1KTL1SU,3724 +numpy/typing/tests/data/pass/shape.py,sha256=L2iugxTnbm8kmBpaJVYpURKJEAnI7TH2KtuYeqNR9co,445 +numpy/typing/tests/data/pass/simple.py,sha256=lPj620zkTA8Sg893eu2mGuj-Xq2BGZ_1dcmfsVDkz8g,2751 +numpy/typing/tests/data/pass/simple_py3.py,sha256=HuLrc5aphThQkLjU2_19KgGFaXwKOfSzXe0p2xMm8ZI,96 +numpy/typing/tests/data/pass/ufunc_config.py,sha256=uzXOhCl9N4LPV9hV2Iqg_skgkKMbBPBF0GXPU9EMeuE,1205 +numpy/typing/tests/data/pass/ufunclike.py,sha256=U4Aay11VALvm22bWEX0eDWuN5qxJlg_hH5IpOL62M3I,1125 +numpy/typing/tests/data/pass/ufuncs.py,sha256=1Rem_geEm4qyD3XaRA1NAPKwr3YjRq68zbIlC_Xhi9M,422 +numpy/typing/tests/data/pass/warnings_and_errors.py,sha256=ETLZkDTGpZspvwjVYAZlnA1gH4PJ4bSY5PkWyxTjusU,161 +numpy/typing/tests/data/reveal/arithmetic.pyi,sha256=phWM8Fz30fe--KkKI8S9voIbDNHbxIKSzLwRWwvJ7yU,27424 +numpy/typing/tests/data/reveal/array_api_info.pyi,sha256=oWKW0yGS9xKcLZnH2QeeixMBcI74dNIcwZr0bwGmDVM,3017 +numpy/typing/tests/data/reveal/array_constructors.pyi,sha256=fJZwsHVQS-_sEMo6qsLKyKyxuQoGvCeW8TF3xUzv_rw,13041 +numpy/typing/tests/data/reveal/arraypad.pyi,sha256=Dg5ss1cDS_QiNT4YEheHXMa2beM4qBTUb1mq-REkh6A,653 +numpy/typing/tests/data/reveal/arrayprint.pyi,sha256=iUHzZaUrYFGC9QBCxhiEAIJODeqGwG7VCv875il-9gY,777 +numpy/typing/tests/data/reveal/arraysetops.pyi,sha256=Hhe49rLgj0P8SXElncNvLeCv1OqdI-iryB_673w7vL4,4411 +numpy/typing/tests/data/reveal/arrayterator.pyi,sha256=QPRyZzHFmti4HlrJ315dgzBjaet8LqM9il-8uc9e2P8,1039 +numpy/typing/tests/data/reveal/bitwise_ops.pyi,sha256=TjW0vMyXqUy-WoEIMA3AMN_u4IGw5RosOWK_qHMNjes,4911 +numpy/typing/tests/data/reveal/char.pyi,sha256=9QbiMbkKycnZl4f4eKBoF_rAxIUIv3vBcOQyksHJCug,11470 +numpy/typing/tests/data/reveal/chararray.pyi,sha256=4oqRNZt7jIdfbNVgcsWPDVVFrrEYhqjAExaNzPya_lY,5199 +numpy/typing/tests/data/reveal/comparisons.pyi,sha256=mXRfm3ZUsk8YbSPg9ugPSWLGRwzUVy4BEVN7q4K56tc,7195 +numpy/typing/tests/data/reveal/constants.pyi,sha256=AazwlvF--Te1dt35f8lkDLNuo3jQXqmGvddDQ37jAE0,333 +numpy/typing/tests/data/reveal/ctypeslib.pyi,sha256=U9ZO5GnGHxVyv-OWRYWHSXctH7LGHPWDdyNVl_saQEQ,4134 +numpy/typing/tests/data/reveal/datasource.pyi,sha256=B9nCoOPE4fJvBIeInAgUCg5pIsr8IYOu_iToqt6n-Nc,583 +numpy/typing/tests/data/reveal/dtype.pyi,sha256=IdxNE3NIE0YKpVw4yI9lS-wWPmeFyfGCW2V0oyor4zk,5080 +numpy/typing/tests/data/reveal/einsumfunc.pyi,sha256=qPYk5W3lardDdgsQIGyu356iIGDnb0P38UKQDXWQlrk,1926 +numpy/typing/tests/data/reveal/emath.pyi,sha256=fcf0-GftYRByfJFuZC-MvzHlQU4A-f9-kPnxzQt48E0,2125 +numpy/typing/tests/data/reveal/fft.pyi,sha256=uZOJ0ljmmnejfPEwMsfUGDb52NOuTh7Npl7ONwx-Y2k,1601 +numpy/typing/tests/data/reveal/flatiter.pyi,sha256=ZxgdgbRWYXlyxlPOXJzZSHvALqGsK3aV4lf9RePghdA,1347 +numpy/typing/tests/data/reveal/fromnumeric.pyi,sha256=xweKmm6uKVgJF4-AwtM6hGEI_YHosu-8jXnd8yjSfJ4,15066 +numpy/typing/tests/data/reveal/getlimits.pyi,sha256=mH0kk94VBu-O5ZzA1nki80jttDK_EBGOsLQOZo3Rq18,1547 +numpy/typing/tests/data/reveal/histograms.pyi,sha256=Mr7P7JYMWF9jM6w5othyzh8CN3ygd2A-WRoB4jImnzk,1257 +numpy/typing/tests/data/reveal/index_tricks.pyi,sha256=4dvG8RXY5ktKXo1uC_pfPHXBDd7tatTbjCs8xr8M2os,3241 +numpy/typing/tests/data/reveal/lib_function_base.pyi,sha256=LMCyduuUjX1E7ruBI-B_cEJQ_rUt9ZO21ck22_OLa_c,10112 +numpy/typing/tests/data/reveal/lib_polynomial.pyi,sha256=CrG0zxbY-HddD7D93q5Cow6c_3mx3nVb1ZCcAq5mC4U,5660 +numpy/typing/tests/data/reveal/lib_utils.pyi,sha256=oQCay2NF8pYHD5jNgRZKNjn8uJW4TJqUPIlytOwDSi0,436 +numpy/typing/tests/data/reveal/lib_version.pyi,sha256=y4ZJSLEeS273Zd6fqaE2XNdczTS0-cwIJ2Yn_4Otm44,572 +numpy/typing/tests/data/reveal/linalg.pyi,sha256=w8RdvwTSt40PMQDvlt_tnky4Cu9LnTUXAmdFhZORPpc,5933 +numpy/typing/tests/data/reveal/ma.pyi,sha256=5FCR2aqUpKOtoQcazro_5C-NE2MrywouDrMHirVyHF0,16223 +numpy/typing/tests/data/reveal/matrix.pyi,sha256=ntknd4qkGbaBMMzPlkTeahyg_H8_TDBJQDbd36a_QfY,3040 +numpy/typing/tests/data/reveal/memmap.pyi,sha256=OCcEhR5mvvXk4UhF6lRqmkxU2NcAqJ4nqAuBpcroQ1g,719 +numpy/typing/tests/data/reveal/mod.pyi,sha256=9nJnn1rA_4mbk2JSYyDmQ5pMWWQ9vPDDzWqijlFAG4I,7599 +numpy/typing/tests/data/reveal/modules.pyi,sha256=_Gvxgql5KbJFL1Mj5gFAphzyGC44AkuNZLnYkv-3LRA,1858 +numpy/typing/tests/data/reveal/multiarray.pyi,sha256=oz81sV4JUBbd6memodStUpT11TARzqRXWUs4H0cU-YA,7779 +numpy/typing/tests/data/reveal/nbit_base_example.pyi,sha256=9OqWKUGRGCIt-mywzDmZExTOsM7l3JGw0YAPB9rs_8k,687 +numpy/typing/tests/data/reveal/ndarray_assignability.pyi,sha256=KOl5ActvtUx6h1oTQT3c0EiU5eCDbMD1okQVfxpc4j0,2668 +numpy/typing/tests/data/reveal/ndarray_conversion.pyi,sha256=SAI9kxMNl66L8n7kO3jn7-EL_3Ygn46behqD_dVa5Hw,3309 +numpy/typing/tests/data/reveal/ndarray_misc.pyi,sha256=8jwi9O-iGcojU0xSF_GUYMFRpkRdol5hQza0hkziNXc,8663 +numpy/typing/tests/data/reveal/ndarray_shape_manipulation.pyi,sha256=z8SRTWdl6fSj_ENNF-M5jZnujUl1180WaFMAanXqCVw,1394 +numpy/typing/tests/data/reveal/nditer.pyi,sha256=yih7UE0OynR7GuVCGgwhzjTjARwOXikDe6Dr4ymRC2g,1898 +numpy/typing/tests/data/reveal/nested_sequence.pyi,sha256=Z2vwweUjoqxR0zUUldOUXsg6mkDfDP1BMyFV2hje5Z8,612 +numpy/typing/tests/data/reveal/npyio.pyi,sha256=p6jJFmcwXuQhshYC70zhg_itI1kLiDu9saUCNwYpFNo,3493 +numpy/typing/tests/data/reveal/numeric.pyi,sha256=0hvPN803QJoO38lYY68of7M-1KGXqdgHy9RdqcHwO-M,5869 +numpy/typing/tests/data/reveal/numerictypes.pyi,sha256=4lnZQTgVtig8UuDwuETyQ6jRFxsYv6tnni2ZJaDyMM0,1331 +numpy/typing/tests/data/reveal/polynomial_polybase.pyi,sha256=V7ulOvXuAcduWTD_7Jg1yPCLvROq8E-10GobfNlKXD8,7925 +numpy/typing/tests/data/reveal/polynomial_polyutils.pyi,sha256=I_4waxJEeUsp5pjnbBN55kqZ2kycK8akD_XvhsgsCGY,10642 +numpy/typing/tests/data/reveal/polynomial_series.pyi,sha256=YowKiIaDd2Je0PjEmXDINUXe4il0r4KDkpzDbYpwG38,6853 +numpy/typing/tests/data/reveal/random.pyi,sha256=xXJobSp5nVBelmrBO_OTvV8XQnbnZjbAyJfrRwlJshg,104296 +numpy/typing/tests/data/reveal/rec.pyi,sha256=E8lxkOQ4qSwwX20Y4d438s5g-kTnNARsZc4f-Y8OhZo,3378 +numpy/typing/tests/data/reveal/scalars.pyi,sha256=5s5Xm1HoA6bwwqK4gfEWqoNk45dAQvxAZLZc2zUhe3A,6378 +numpy/typing/tests/data/reveal/shape.pyi,sha256=ZT6e5LW4nU90tA-Av5NLiyoaPW9NIX_XkWJ-LOOzh84,262 +numpy/typing/tests/data/reveal/shape_base.pyi,sha256=xbnt0jps1djVxVMn4Lj8bxGl-mGvbhqSKFVWYcFApLg,2006 +numpy/typing/tests/data/reveal/stride_tricks.pyi,sha256=Cm9P_F7promu0zGZmo957SOFCZ6Np8wSv5ecR_hB668,1315 +numpy/typing/tests/data/reveal/strings.pyi,sha256=WvSd8xHIdxQdah3Q0ZJUva79jfVngB3UD9yb6awDW8w,9547 +numpy/typing/tests/data/reveal/testing.pyi,sha256=vP3uEWEdFHrfv_Q4OaJ0Oo5gUqUxkkIRVjvJMsqiHs8,8443 +numpy/typing/tests/data/reveal/twodim_base.pyi,sha256=TiBbWXI0xRCgk0bE-Bd4ZryWaLeJIQ5I-6KBjIVoMuE,4237 +numpy/typing/tests/data/reveal/type_check.pyi,sha256=W7rJUEf_iwI0D1FIVjhCEfzIjw_T04qcBYFxuPwnXAo,2392 +numpy/typing/tests/data/reveal/ufunc_config.pyi,sha256=XoD9fxaMVCGgyMncWKIJssFBO0SmndHsDs0hDXS04A8,1162 +numpy/typing/tests/data/reveal/ufunclike.pyi,sha256=0jwIYSgXn0usVGkzyZz0ttO5tSYfWMYu_U2ByqrzuRQ,1183 +numpy/typing/tests/data/reveal/ufuncs.pyi,sha256=2IYvfPlLCuqgoyNKzbcv3mr-Dva2cyUSWtBWuM77sDk,4789 +numpy/typing/tests/data/reveal/warnings_and_errors.pyi,sha256=5qqRFzPOon1GhU_i5CHDxQLPKVcO2EMhbc851V8Gusc,449 +numpy/typing/tests/test_isfile.py,sha256=yaRIX3JLmwY1cgD-xxKvJjMVVBRmv9QNSXx9kQSoVAc,878 +numpy/typing/tests/test_runtime.py,sha256=YHS0Hgv1v3cip7C14UcsJWLGI37m18MqXrwLmb88Ctc,2919 +numpy/typing/tests/test_typing.py,sha256=VERPf6NJ6gRLoKk0ki-s1wvDS4E--InjNUaj63_Q-00,6289 +numpy/version.py,sha256=AjMIRnThSIMGkfWDWXTf4QreFJAZR5RGTsjucRoRXx8,293 +numpy/version.pyi,sha256=x3oCrDqM_gQhitdDgfgMhJ-UPabIXk5etqBq8HUwUok,358 diff --git a/.venv/lib/python3.12/site-packages/numpy-2.3.3.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/numpy-2.3.3.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/numpy-2.3.3.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/numpy-2.3.3.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..227a38cf01899aeccdf106d40cea71a6c95d30af --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy-2.3.3.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: meson +Root-Is-Purelib: false +Tag: cp312-cp312-manylinux_2_27_x86_64 +Tag: cp312-cp312-manylinux_2_28_x86_64 + diff --git a/.venv/lib/python3.12/site-packages/numpy-2.3.3.dist-info/entry_points.txt b/.venv/lib/python3.12/site-packages/numpy-2.3.3.dist-info/entry_points.txt new file mode 100644 index 0000000000000000000000000000000000000000..48c4f6435df3f089c2ffd33f4c90eb1705d34958 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy-2.3.3.dist-info/entry_points.txt @@ -0,0 +1,13 @@ +[pkg_config] +numpy = numpy._core.lib.pkgconfig + +[array_api] +numpy = numpy + +[pyinstaller40] +hook-dirs = numpy:_pyinstaller_hooks_dir + +[console_scripts] +f2py = numpy.f2py.f2py2e:main +numpy-config = numpy._configtool:main + diff --git a/.venv/lib/python3.12/site-packages/numpy/__config__.py b/.venv/lib/python3.12/site-packages/numpy/__config__.py new file mode 100644 index 0000000000000000000000000000000000000000..cbe4a4e23ee56e62d5af506bb010ba7332d7cfff --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/__config__.py @@ -0,0 +1,170 @@ +# This file is generated by numpy's build process +# It contains system_info results at the time of building this package. +from enum import Enum +from numpy._core._multiarray_umath import ( + __cpu_features__, + __cpu_baseline__, + __cpu_dispatch__, +) + +__all__ = ["show_config"] +_built_with_meson = True + + +class DisplayModes(Enum): + stdout = "stdout" + dicts = "dicts" + + +def _cleanup(d): + """ + Removes empty values in a `dict` recursively + This ensures we remove values that Meson could not provide to CONFIG + """ + if isinstance(d, dict): + return {k: _cleanup(v) for k, v in d.items() if v and _cleanup(v)} + else: + return d + + +CONFIG = _cleanup( + { + "Compilers": { + "c": { + "name": "gcc", + "linker": r"ld.bfd", + "version": "14.2.1", + "commands": r"cc", + "args": r"", + "linker args": r"", + }, + "cython": { + "name": "cython", + "linker": r"cython", + "version": "3.1.3", + "commands": r"cython", + "args": r"", + "linker args": r"", + }, + "c++": { + "name": "gcc", + "linker": r"ld.bfd", + "version": "14.2.1", + "commands": r"c++", + "args": r"", + "linker args": r"", + }, + }, + "Machine Information": { + "host": { + "cpu": "x86_64", + "family": "x86_64", + "endian": "little", + "system": "linux", + }, + "build": { + "cpu": "x86_64", + "family": "x86_64", + "endian": "little", + "system": "linux", + }, + "cross-compiled": bool("False".lower().replace("false", "")), + }, + "Build Dependencies": { + "blas": { + "name": "scipy-openblas", + "found": bool("True".lower().replace("false", "")), + "version": "0.3.30", + "detection method": "pkgconfig", + "include directory": r"/opt/_internal/cpython-3.12.11/lib/python3.12/site-packages/scipy_openblas64/include", + "lib directory": r"/opt/_internal/cpython-3.12.11/lib/python3.12/site-packages/scipy_openblas64/lib", + "openblas configuration": r"OpenBLAS 0.3.30 USE64BITINT DYNAMIC_ARCH NO_AFFINITY Haswell MAX_THREADS=64", + "pc file directory": r"/project/.openblas", + }, + "lapack": { + "name": "scipy-openblas", + "found": bool("True".lower().replace("false", "")), + "version": "0.3.30", + "detection method": "pkgconfig", + "include directory": r"/opt/_internal/cpython-3.12.11/lib/python3.12/site-packages/scipy_openblas64/include", + "lib directory": r"/opt/_internal/cpython-3.12.11/lib/python3.12/site-packages/scipy_openblas64/lib", + "openblas configuration": r"OpenBLAS 0.3.30 USE64BITINT DYNAMIC_ARCH NO_AFFINITY Haswell MAX_THREADS=64", + "pc file directory": r"/project/.openblas", + }, + }, + "Python Information": { + "path": r"/tmp/build-env-w4xgk5bb/bin/python", + "version": "3.12", + }, + "SIMD Extensions": { + "baseline": __cpu_baseline__, + "found": [ + feature for feature in __cpu_dispatch__ if __cpu_features__[feature] + ], + "not found": [ + feature for feature in __cpu_dispatch__ if not __cpu_features__[feature] + ], + }, + } +) + + +def _check_pyyaml(): + import yaml + + return yaml + + +def show(mode=DisplayModes.stdout.value): + """ + Show libraries and system information on which NumPy was built + and is being used + + Parameters + ---------- + mode : {`'stdout'`, `'dicts'`}, optional. + Indicates how to display the config information. + `'stdout'` prints to console, `'dicts'` returns a dictionary + of the configuration. + + Returns + ------- + out : {`dict`, `None`} + If mode is `'dicts'`, a dict is returned, else None + + See Also + -------- + get_include : Returns the directory containing NumPy C + header files. + + Notes + ----- + 1. The `'stdout'` mode will give more readable + output if ``pyyaml`` is installed + + """ + if mode == DisplayModes.stdout.value: + try: # Non-standard library, check import + yaml = _check_pyyaml() + + print(yaml.dump(CONFIG)) + except ModuleNotFoundError: + import warnings + import json + + warnings.warn("Install `pyyaml` for better output", stacklevel=1) + print(json.dumps(CONFIG, indent=2)) + elif mode == DisplayModes.dicts.value: + return CONFIG + else: + raise AttributeError( + f"Invalid `mode`, use one of: {', '.join([e.value for e in DisplayModes])}" + ) + + +def show_config(mode=DisplayModes.stdout.value): + return show(mode) + + +show_config.__doc__ = show.__doc__ +show_config.__module__ = "numpy" diff --git a/.venv/lib/python3.12/site-packages/numpy/__config__.pyi b/.venv/lib/python3.12/site-packages/numpy/__config__.pyi new file mode 100644 index 0000000000000000000000000000000000000000..b59bdcd252b633a4bf07245f9b32f23d7283ba69 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/__config__.pyi @@ -0,0 +1,102 @@ +from enum import Enum +from types import ModuleType +from typing import Final, NotRequired, TypedDict, overload, type_check_only +from typing import Literal as L + +_CompilerConfigDictValue = TypedDict( + "_CompilerConfigDictValue", + { + "name": str, + "linker": str, + "version": str, + "commands": str, + "args": str, + "linker args": str, + }, +) +_CompilerConfigDict = TypedDict( + "_CompilerConfigDict", + { + "c": _CompilerConfigDictValue, + "cython": _CompilerConfigDictValue, + "c++": _CompilerConfigDictValue, + }, +) +_MachineInformationDict = TypedDict( + "_MachineInformationDict", + { + "host": _MachineInformationDictValue, + "build": _MachineInformationDictValue, + "cross-compiled": NotRequired[L[True]], + }, +) + +@type_check_only +class _MachineInformationDictValue(TypedDict): + cpu: str + family: str + endian: L["little", "big"] + system: str + +_BuildDependenciesDictValue = TypedDict( + "_BuildDependenciesDictValue", + { + "name": str, + "found": NotRequired[L[True]], + "version": str, + "include directory": str, + "lib directory": str, + "openblas configuration": str, + "pc file directory": str, + }, +) + +class _BuildDependenciesDict(TypedDict): + blas: _BuildDependenciesDictValue + lapack: _BuildDependenciesDictValue + +class _PythonInformationDict(TypedDict): + path: str + version: str + +_SIMDExtensionsDict = TypedDict( + "_SIMDExtensionsDict", + { + "baseline": list[str], + "found": list[str], + "not found": list[str], + }, +) + +_ConfigDict = TypedDict( + "_ConfigDict", + { + "Compilers": _CompilerConfigDict, + "Machine Information": _MachineInformationDict, + "Build Dependencies": _BuildDependenciesDict, + "Python Information": _PythonInformationDict, + "SIMD Extensions": _SIMDExtensionsDict, + }, +) + +### + +__all__ = ["show_config"] + +CONFIG: Final[_ConfigDict] = ... + +class DisplayModes(Enum): + stdout = "stdout" + dicts = "dicts" + +def _check_pyyaml() -> ModuleType: ... + +@overload +def show(mode: L["stdout"] = "stdout") -> None: ... +@overload +def show(mode: L["dicts"]) -> _ConfigDict: ... + +@overload +def show_config(mode: L["stdout"] = "stdout") -> None: ... +@overload +def show_config(mode: L["dicts"]) -> _ConfigDict: ... diff --git a/.venv/lib/python3.12/site-packages/numpy/__init__.cython-30.pxd b/.venv/lib/python3.12/site-packages/numpy/__init__.cython-30.pxd new file mode 100644 index 0000000000000000000000000000000000000000..86c91cf617a50ac8c635bf54a9a7c82a1578c9a6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/__init__.cython-30.pxd @@ -0,0 +1,1241 @@ +# NumPy static imports for Cython >= 3.0 +# +# If any of the PyArray_* functions are called, import_array must be +# called first. This is done automatically by Cython 3.0+ if a call +# is not detected inside of the module. +# +# Author: Dag Sverre Seljebotn +# + +from cpython.ref cimport Py_INCREF +from cpython.object cimport PyObject, PyTypeObject, PyObject_TypeCheck +cimport libc.stdio as stdio + + +cdef extern from *: + # Leave a marker that the NumPy declarations came from NumPy itself and not from Cython. + # See https://github.com/cython/cython/issues/3573 + """ + /* Using NumPy API declarations from "numpy/__init__.cython-30.pxd" */ + """ + + +cdef extern from "numpy/arrayobject.h": + # It would be nice to use size_t and ssize_t, but ssize_t has special + # implicit conversion rules, so just use "long". + # Note: The actual type only matters for Cython promotion, so long + # is closer than int, but could lead to incorrect promotion. + # (Not to worrying, and always the status-quo.) + ctypedef signed long npy_intp + ctypedef unsigned long npy_uintp + + ctypedef unsigned char npy_bool + + ctypedef signed char npy_byte + ctypedef signed short npy_short + ctypedef signed int npy_int + ctypedef signed long npy_long + ctypedef signed long long npy_longlong + + ctypedef unsigned char npy_ubyte + ctypedef unsigned short npy_ushort + ctypedef unsigned int npy_uint + ctypedef unsigned long npy_ulong + ctypedef unsigned long long npy_ulonglong + + ctypedef float npy_float + ctypedef double npy_double + ctypedef long double npy_longdouble + + ctypedef signed char npy_int8 + ctypedef signed short npy_int16 + ctypedef signed int npy_int32 + ctypedef signed long long npy_int64 + + ctypedef unsigned char npy_uint8 + ctypedef unsigned short npy_uint16 + ctypedef unsigned int npy_uint32 + ctypedef unsigned long long npy_uint64 + + ctypedef float npy_float32 + ctypedef double npy_float64 + ctypedef long double npy_float80 + ctypedef long double npy_float96 + ctypedef long double npy_float128 + + ctypedef struct npy_cfloat: + pass + + ctypedef struct npy_cdouble: + pass + + ctypedef struct npy_clongdouble: + pass + + ctypedef struct npy_complex64: + pass + + ctypedef struct npy_complex128: + pass + + ctypedef struct npy_complex160: + pass + + ctypedef struct npy_complex192: + pass + + ctypedef struct npy_complex256: + pass + + ctypedef struct PyArray_Dims: + npy_intp *ptr + int len + + + cdef enum NPY_TYPES: + NPY_BOOL + NPY_BYTE + NPY_UBYTE + NPY_SHORT + NPY_USHORT + NPY_INT + NPY_UINT + NPY_LONG + NPY_ULONG + NPY_LONGLONG + NPY_ULONGLONG + NPY_FLOAT + NPY_DOUBLE + NPY_LONGDOUBLE + NPY_CFLOAT + NPY_CDOUBLE + NPY_CLONGDOUBLE + NPY_OBJECT + NPY_STRING + NPY_UNICODE + NPY_VSTRING + NPY_VOID + NPY_DATETIME + NPY_TIMEDELTA + NPY_NTYPES_LEGACY + NPY_NOTYPE + + NPY_INT8 + NPY_INT16 + NPY_INT32 + NPY_INT64 + NPY_UINT8 + NPY_UINT16 + NPY_UINT32 + NPY_UINT64 + NPY_FLOAT16 + NPY_FLOAT32 + NPY_FLOAT64 + NPY_FLOAT80 + NPY_FLOAT96 + NPY_FLOAT128 + NPY_COMPLEX64 + NPY_COMPLEX128 + NPY_COMPLEX160 + NPY_COMPLEX192 + NPY_COMPLEX256 + + NPY_INTP + NPY_UINTP + NPY_DEFAULT_INT # Not a compile time constant (normally)! + + ctypedef enum NPY_ORDER: + NPY_ANYORDER + NPY_CORDER + NPY_FORTRANORDER + NPY_KEEPORDER + + ctypedef enum NPY_CASTING: + NPY_NO_CASTING + NPY_EQUIV_CASTING + NPY_SAFE_CASTING + NPY_SAME_KIND_CASTING + NPY_UNSAFE_CASTING + + ctypedef enum NPY_CLIPMODE: + NPY_CLIP + NPY_WRAP + NPY_RAISE + + ctypedef enum NPY_SCALARKIND: + NPY_NOSCALAR, + NPY_BOOL_SCALAR, + NPY_INTPOS_SCALAR, + NPY_INTNEG_SCALAR, + NPY_FLOAT_SCALAR, + NPY_COMPLEX_SCALAR, + NPY_OBJECT_SCALAR + + ctypedef enum NPY_SORTKIND: + NPY_QUICKSORT + NPY_HEAPSORT + NPY_MERGESORT + + ctypedef enum NPY_SEARCHSIDE: + NPY_SEARCHLEFT + NPY_SEARCHRIGHT + + enum: + NPY_ARRAY_C_CONTIGUOUS + NPY_ARRAY_F_CONTIGUOUS + NPY_ARRAY_OWNDATA + NPY_ARRAY_FORCECAST + NPY_ARRAY_ENSURECOPY + NPY_ARRAY_ENSUREARRAY + NPY_ARRAY_ELEMENTSTRIDES + NPY_ARRAY_ALIGNED + NPY_ARRAY_NOTSWAPPED + NPY_ARRAY_WRITEABLE + NPY_ARRAY_WRITEBACKIFCOPY + + NPY_ARRAY_BEHAVED + NPY_ARRAY_BEHAVED_NS + NPY_ARRAY_CARRAY + NPY_ARRAY_CARRAY_RO + NPY_ARRAY_FARRAY + NPY_ARRAY_FARRAY_RO + NPY_ARRAY_DEFAULT + + NPY_ARRAY_IN_ARRAY + NPY_ARRAY_OUT_ARRAY + NPY_ARRAY_INOUT_ARRAY + NPY_ARRAY_IN_FARRAY + NPY_ARRAY_OUT_FARRAY + NPY_ARRAY_INOUT_FARRAY + + NPY_ARRAY_UPDATE_ALL + + cdef enum: + NPY_MAXDIMS # 64 on NumPy 2.x and 32 on NumPy 1.x + NPY_RAVEL_AXIS # Used for functions like PyArray_Mean + + ctypedef void (*PyArray_VectorUnaryFunc)(void *, void *, npy_intp, void *, void *) + + ctypedef struct PyArray_ArrayDescr: + # shape is a tuple, but Cython doesn't support "tuple shape" + # inside a non-PyObject declaration, so we have to declare it + # as just a PyObject*. + PyObject* shape + + ctypedef struct PyArray_Descr: + pass + + ctypedef class numpy.dtype [object PyArray_Descr, check_size ignore]: + # Use PyDataType_* macros when possible, however there are no macros + # for accessing some of the fields, so some are defined. + cdef PyTypeObject* typeobj + cdef char kind + cdef char type + # Numpy sometimes mutates this without warning (e.g. it'll + # sometimes change "|" to "<" in shared dtype objects on + # little-endian machines). If this matters to you, use + # PyArray_IsNativeByteOrder(dtype.byteorder) instead of + # directly accessing this field. + cdef char byteorder + cdef int type_num + + @property + cdef inline npy_intp itemsize(self) noexcept nogil: + return PyDataType_ELSIZE(self) + + @property + cdef inline npy_intp alignment(self) noexcept nogil: + return PyDataType_ALIGNMENT(self) + + # Use fields/names with care as they may be NULL. You must check + # for this using PyDataType_HASFIELDS. + @property + cdef inline object fields(self): + return PyDataType_FIELDS(self) + + @property + cdef inline tuple names(self): + return PyDataType_NAMES(self) + + # Use PyDataType_HASSUBARRAY to test whether this field is + # valid (the pointer can be NULL). Most users should access + # this field via the inline helper method PyDataType_SHAPE. + @property + cdef inline PyArray_ArrayDescr* subarray(self) noexcept nogil: + return PyDataType_SUBARRAY(self) + + @property + cdef inline npy_uint64 flags(self) noexcept nogil: + """The data types flags.""" + return PyDataType_FLAGS(self) + + + ctypedef class numpy.flatiter [object PyArrayIterObject, check_size ignore]: + # Use through macros + pass + + ctypedef class numpy.broadcast [object PyArrayMultiIterObject, check_size ignore]: + + @property + cdef inline int numiter(self) noexcept nogil: + """The number of arrays that need to be broadcast to the same shape.""" + return PyArray_MultiIter_NUMITER(self) + + @property + cdef inline npy_intp size(self) noexcept nogil: + """The total broadcasted size.""" + return PyArray_MultiIter_SIZE(self) + + @property + cdef inline npy_intp index(self) noexcept nogil: + """The current (1-d) index into the broadcasted result.""" + return PyArray_MultiIter_INDEX(self) + + @property + cdef inline int nd(self) noexcept nogil: + """The number of dimensions in the broadcasted result.""" + return PyArray_MultiIter_NDIM(self) + + @property + cdef inline npy_intp* dimensions(self) noexcept nogil: + """The shape of the broadcasted result.""" + return PyArray_MultiIter_DIMS(self) + + @property + cdef inline void** iters(self) noexcept nogil: + """An array of iterator objects that holds the iterators for the arrays to be broadcast together. + On return, the iterators are adjusted for broadcasting.""" + return PyArray_MultiIter_ITERS(self) + + + ctypedef struct PyArrayObject: + # For use in situations where ndarray can't replace PyArrayObject*, + # like PyArrayObject**. + pass + + ctypedef class numpy.ndarray [object PyArrayObject, check_size ignore]: + cdef __cythonbufferdefaults__ = {"mode": "strided"} + + # NOTE: no field declarations since direct access is deprecated since NumPy 1.7 + # Instead, we use properties that map to the corresponding C-API functions. + + @property + cdef inline PyObject* base(self) noexcept nogil: + """Returns a borrowed reference to the object owning the data/memory. + """ + return PyArray_BASE(self) + + @property + cdef inline dtype descr(self): + """Returns an owned reference to the dtype of the array. + """ + return PyArray_DESCR(self) + + @property + cdef inline int ndim(self) noexcept nogil: + """Returns the number of dimensions in the array. + """ + return PyArray_NDIM(self) + + @property + cdef inline npy_intp *shape(self) noexcept nogil: + """Returns a pointer to the dimensions/shape of the array. + The number of elements matches the number of dimensions of the array (ndim). + Can return NULL for 0-dimensional arrays. + """ + return PyArray_DIMS(self) + + @property + cdef inline npy_intp *strides(self) noexcept nogil: + """Returns a pointer to the strides of the array. + The number of elements matches the number of dimensions of the array (ndim). + """ + return PyArray_STRIDES(self) + + @property + cdef inline npy_intp size(self) noexcept nogil: + """Returns the total size (in number of elements) of the array. + """ + return PyArray_SIZE(self) + + @property + cdef inline char* data(self) noexcept nogil: + """The pointer to the data buffer as a char*. + This is provided for legacy reasons to avoid direct struct field access. + For new code that needs this access, you probably want to cast the result + of `PyArray_DATA()` instead, which returns a 'void*'. + """ + return PyArray_BYTES(self) + + + int _import_array() except -1 + # A second definition so _import_array isn't marked as used when we use it here. + # Do not use - subject to change any time. + int __pyx_import_array "_import_array"() except -1 + + # + # Macros from ndarrayobject.h + # + bint PyArray_CHKFLAGS(ndarray m, int flags) nogil + bint PyArray_IS_C_CONTIGUOUS(ndarray arr) nogil + bint PyArray_IS_F_CONTIGUOUS(ndarray arr) nogil + bint PyArray_ISCONTIGUOUS(ndarray m) nogil + bint PyArray_ISWRITEABLE(ndarray m) nogil + bint PyArray_ISALIGNED(ndarray m) nogil + + int PyArray_NDIM(ndarray) nogil + bint PyArray_ISONESEGMENT(ndarray) nogil + bint PyArray_ISFORTRAN(ndarray) nogil + int PyArray_FORTRANIF(ndarray) nogil + + void* PyArray_DATA(ndarray) nogil + char* PyArray_BYTES(ndarray) nogil + + npy_intp* PyArray_DIMS(ndarray) nogil + npy_intp* PyArray_STRIDES(ndarray) nogil + npy_intp PyArray_DIM(ndarray, size_t) nogil + npy_intp PyArray_STRIDE(ndarray, size_t) nogil + + PyObject *PyArray_BASE(ndarray) nogil # returns borrowed reference! + PyArray_Descr *PyArray_DESCR(ndarray) nogil # returns borrowed reference to dtype! + PyArray_Descr *PyArray_DTYPE(ndarray) nogil # returns borrowed reference to dtype! NP 1.7+ alias for descr. + int PyArray_FLAGS(ndarray) nogil + void PyArray_CLEARFLAGS(ndarray, int flags) nogil # Added in NumPy 1.7 + void PyArray_ENABLEFLAGS(ndarray, int flags) nogil # Added in NumPy 1.7 + npy_intp PyArray_ITEMSIZE(ndarray) nogil + int PyArray_TYPE(ndarray arr) nogil + + object PyArray_GETITEM(ndarray arr, void *itemptr) + int PyArray_SETITEM(ndarray arr, void *itemptr, object obj) except -1 + + bint PyTypeNum_ISBOOL(int) nogil + bint PyTypeNum_ISUNSIGNED(int) nogil + bint PyTypeNum_ISSIGNED(int) nogil + bint PyTypeNum_ISINTEGER(int) nogil + bint PyTypeNum_ISFLOAT(int) nogil + bint PyTypeNum_ISNUMBER(int) nogil + bint PyTypeNum_ISSTRING(int) nogil + bint PyTypeNum_ISCOMPLEX(int) nogil + bint PyTypeNum_ISFLEXIBLE(int) nogil + bint PyTypeNum_ISUSERDEF(int) nogil + bint PyTypeNum_ISEXTENDED(int) nogil + bint PyTypeNum_ISOBJECT(int) nogil + + npy_intp PyDataType_ELSIZE(dtype) nogil + npy_intp PyDataType_ALIGNMENT(dtype) nogil + PyObject* PyDataType_METADATA(dtype) nogil + PyArray_ArrayDescr* PyDataType_SUBARRAY(dtype) nogil + PyObject* PyDataType_NAMES(dtype) nogil + PyObject* PyDataType_FIELDS(dtype) nogil + + bint PyDataType_ISBOOL(dtype) nogil + bint PyDataType_ISUNSIGNED(dtype) nogil + bint PyDataType_ISSIGNED(dtype) nogil + bint PyDataType_ISINTEGER(dtype) nogil + bint PyDataType_ISFLOAT(dtype) nogil + bint PyDataType_ISNUMBER(dtype) nogil + bint PyDataType_ISSTRING(dtype) nogil + bint PyDataType_ISCOMPLEX(dtype) nogil + bint PyDataType_ISFLEXIBLE(dtype) nogil + bint PyDataType_ISUSERDEF(dtype) nogil + bint PyDataType_ISEXTENDED(dtype) nogil + bint PyDataType_ISOBJECT(dtype) nogil + bint PyDataType_HASFIELDS(dtype) nogil + bint PyDataType_HASSUBARRAY(dtype) nogil + npy_uint64 PyDataType_FLAGS(dtype) nogil + + bint PyArray_ISBOOL(ndarray) nogil + bint PyArray_ISUNSIGNED(ndarray) nogil + bint PyArray_ISSIGNED(ndarray) nogil + bint PyArray_ISINTEGER(ndarray) nogil + bint PyArray_ISFLOAT(ndarray) nogil + bint PyArray_ISNUMBER(ndarray) nogil + bint PyArray_ISSTRING(ndarray) nogil + bint PyArray_ISCOMPLEX(ndarray) nogil + bint PyArray_ISFLEXIBLE(ndarray) nogil + bint PyArray_ISUSERDEF(ndarray) nogil + bint PyArray_ISEXTENDED(ndarray) nogil + bint PyArray_ISOBJECT(ndarray) nogil + bint PyArray_HASFIELDS(ndarray) nogil + + bint PyArray_ISVARIABLE(ndarray) nogil + + bint PyArray_SAFEALIGNEDCOPY(ndarray) nogil + bint PyArray_ISNBO(char) nogil # works on ndarray.byteorder + bint PyArray_IsNativeByteOrder(char) nogil # works on ndarray.byteorder + bint PyArray_ISNOTSWAPPED(ndarray) nogil + bint PyArray_ISBYTESWAPPED(ndarray) nogil + + bint PyArray_FLAGSWAP(ndarray, int) nogil + + bint PyArray_ISCARRAY(ndarray) nogil + bint PyArray_ISCARRAY_RO(ndarray) nogil + bint PyArray_ISFARRAY(ndarray) nogil + bint PyArray_ISFARRAY_RO(ndarray) nogil + bint PyArray_ISBEHAVED(ndarray) nogil + bint PyArray_ISBEHAVED_RO(ndarray) nogil + + + bint PyDataType_ISNOTSWAPPED(dtype) nogil + bint PyDataType_ISBYTESWAPPED(dtype) nogil + + bint PyArray_DescrCheck(object) + + bint PyArray_Check(object) + bint PyArray_CheckExact(object) + + # Cannot be supported due to out arg: + # bint PyArray_HasArrayInterfaceType(object, dtype, object, object&) + # bint PyArray_HasArrayInterface(op, out) + + + bint PyArray_IsZeroDim(object) + # Cannot be supported due to ## ## in macro: + # bint PyArray_IsScalar(object, verbatim work) + bint PyArray_CheckScalar(object) + bint PyArray_IsPythonNumber(object) + bint PyArray_IsPythonScalar(object) + bint PyArray_IsAnyScalar(object) + bint PyArray_CheckAnyScalar(object) + + ndarray PyArray_GETCONTIGUOUS(ndarray) + bint PyArray_SAMESHAPE(ndarray, ndarray) nogil + npy_intp PyArray_SIZE(ndarray) nogil + npy_intp PyArray_NBYTES(ndarray) nogil + + object PyArray_FROM_O(object) + object PyArray_FROM_OF(object m, int flags) + object PyArray_FROM_OT(object m, int type) + object PyArray_FROM_OTF(object m, int type, int flags) + object PyArray_FROMANY(object m, int type, int min, int max, int flags) + object PyArray_ZEROS(int nd, npy_intp* dims, int type, int fortran) + object PyArray_EMPTY(int nd, npy_intp* dims, int type, int fortran) + void PyArray_FILLWBYTE(ndarray, int val) + object PyArray_ContiguousFromAny(op, int, int min_depth, int max_depth) + unsigned char PyArray_EquivArrTypes(ndarray a1, ndarray a2) + bint PyArray_EquivByteorders(int b1, int b2) nogil + object PyArray_SimpleNew(int nd, npy_intp* dims, int typenum) + object PyArray_SimpleNewFromData(int nd, npy_intp* dims, int typenum, void* data) + #object PyArray_SimpleNewFromDescr(int nd, npy_intp* dims, dtype descr) + object PyArray_ToScalar(void* data, ndarray arr) + + void* PyArray_GETPTR1(ndarray m, npy_intp i) nogil + void* PyArray_GETPTR2(ndarray m, npy_intp i, npy_intp j) nogil + void* PyArray_GETPTR3(ndarray m, npy_intp i, npy_intp j, npy_intp k) nogil + void* PyArray_GETPTR4(ndarray m, npy_intp i, npy_intp j, npy_intp k, npy_intp l) nogil + + # Cannot be supported due to out arg + # void PyArray_DESCR_REPLACE(descr) + + + object PyArray_Copy(ndarray) + object PyArray_FromObject(object op, int type, int min_depth, int max_depth) + object PyArray_ContiguousFromObject(object op, int type, int min_depth, int max_depth) + object PyArray_CopyFromObject(object op, int type, int min_depth, int max_depth) + + object PyArray_Cast(ndarray mp, int type_num) + object PyArray_Take(ndarray ap, object items, int axis) + object PyArray_Put(ndarray ap, object items, object values) + + void PyArray_ITER_RESET(flatiter it) nogil + void PyArray_ITER_NEXT(flatiter it) nogil + void PyArray_ITER_GOTO(flatiter it, npy_intp* destination) nogil + void PyArray_ITER_GOTO1D(flatiter it, npy_intp ind) nogil + void* PyArray_ITER_DATA(flatiter it) nogil + bint PyArray_ITER_NOTDONE(flatiter it) nogil + + void PyArray_MultiIter_RESET(broadcast multi) nogil + void PyArray_MultiIter_NEXT(broadcast multi) nogil + void PyArray_MultiIter_GOTO(broadcast multi, npy_intp dest) nogil + void PyArray_MultiIter_GOTO1D(broadcast multi, npy_intp ind) nogil + void* PyArray_MultiIter_DATA(broadcast multi, npy_intp i) nogil + void PyArray_MultiIter_NEXTi(broadcast multi, npy_intp i) nogil + bint PyArray_MultiIter_NOTDONE(broadcast multi) nogil + npy_intp PyArray_MultiIter_SIZE(broadcast multi) nogil + int PyArray_MultiIter_NDIM(broadcast multi) nogil + npy_intp PyArray_MultiIter_INDEX(broadcast multi) nogil + int PyArray_MultiIter_NUMITER(broadcast multi) nogil + npy_intp* PyArray_MultiIter_DIMS(broadcast multi) nogil + void** PyArray_MultiIter_ITERS(broadcast multi) nogil + + # Functions from __multiarray_api.h + + # Functions taking dtype and returning object/ndarray are disabled + # for now as they steal dtype references. I'm conservative and disable + # more than is probably needed until it can be checked further. + int PyArray_INCREF (ndarray) except * # uses PyArray_Item_INCREF... + int PyArray_XDECREF (ndarray) except * # uses PyArray_Item_DECREF... + dtype PyArray_DescrFromType (int) + object PyArray_TypeObjectFromType (int) + char * PyArray_Zero (ndarray) + char * PyArray_One (ndarray) + #object PyArray_CastToType (ndarray, dtype, int) + int PyArray_CanCastSafely (int, int) # writes errors + npy_bool PyArray_CanCastTo (dtype, dtype) # writes errors + int PyArray_ObjectType (object, int) except 0 + dtype PyArray_DescrFromObject (object, dtype) + #ndarray* PyArray_ConvertToCommonType (object, int *) + dtype PyArray_DescrFromScalar (object) + dtype PyArray_DescrFromTypeObject (object) + npy_intp PyArray_Size (object) + #object PyArray_Scalar (void *, dtype, object) + #object PyArray_FromScalar (object, dtype) + void PyArray_ScalarAsCtype (object, void *) + #int PyArray_CastScalarToCtype (object, void *, dtype) + #int PyArray_CastScalarDirect (object, dtype, void *, int) + #PyArray_VectorUnaryFunc * PyArray_GetCastFunc (dtype, int) + #object PyArray_FromAny (object, dtype, int, int, int, object) + object PyArray_EnsureArray (object) + object PyArray_EnsureAnyArray (object) + #object PyArray_FromFile (stdio.FILE *, dtype, npy_intp, char *) + #object PyArray_FromString (char *, npy_intp, dtype, npy_intp, char *) + #object PyArray_FromBuffer (object, dtype, npy_intp, npy_intp) + #object PyArray_FromIter (object, dtype, npy_intp) + object PyArray_Return (ndarray) + #object PyArray_GetField (ndarray, dtype, int) + #int PyArray_SetField (ndarray, dtype, int, object) except -1 + object PyArray_Byteswap (ndarray, npy_bool) + object PyArray_Resize (ndarray, PyArray_Dims *, int, NPY_ORDER) + int PyArray_CopyInto (ndarray, ndarray) except -1 + int PyArray_CopyAnyInto (ndarray, ndarray) except -1 + int PyArray_CopyObject (ndarray, object) except -1 + object PyArray_NewCopy (ndarray, NPY_ORDER) + object PyArray_ToList (ndarray) + object PyArray_ToString (ndarray, NPY_ORDER) + int PyArray_ToFile (ndarray, stdio.FILE *, char *, char *) except -1 + int PyArray_Dump (object, object, int) except -1 + object PyArray_Dumps (object, int) + int PyArray_ValidType (int) # Cannot error + void PyArray_UpdateFlags (ndarray, int) + object PyArray_New (type, int, npy_intp *, int, npy_intp *, void *, int, int, object) + #object PyArray_NewFromDescr (type, dtype, int, npy_intp *, npy_intp *, void *, int, object) + #dtype PyArray_DescrNew (dtype) + dtype PyArray_DescrNewFromType (int) + double PyArray_GetPriority (object, double) # clears errors as of 1.25 + object PyArray_IterNew (object) + object PyArray_MultiIterNew (int, ...) + + int PyArray_PyIntAsInt (object) except? -1 + npy_intp PyArray_PyIntAsIntp (object) + int PyArray_Broadcast (broadcast) except -1 + int PyArray_FillWithScalar (ndarray, object) except -1 + npy_bool PyArray_CheckStrides (int, int, npy_intp, npy_intp, npy_intp *, npy_intp *) + dtype PyArray_DescrNewByteorder (dtype, char) + object PyArray_IterAllButAxis (object, int *) + #object PyArray_CheckFromAny (object, dtype, int, int, int, object) + #object PyArray_FromArray (ndarray, dtype, int) + object PyArray_FromInterface (object) + object PyArray_FromStructInterface (object) + #object PyArray_FromArrayAttr (object, dtype, object) + #NPY_SCALARKIND PyArray_ScalarKind (int, ndarray*) + int PyArray_CanCoerceScalar (int, int, NPY_SCALARKIND) + npy_bool PyArray_CanCastScalar (type, type) + int PyArray_RemoveSmallest (broadcast) except -1 + int PyArray_ElementStrides (object) + void PyArray_Item_INCREF (char *, dtype) except * + void PyArray_Item_XDECREF (char *, dtype) except * + object PyArray_Transpose (ndarray, PyArray_Dims *) + object PyArray_TakeFrom (ndarray, object, int, ndarray, NPY_CLIPMODE) + object PyArray_PutTo (ndarray, object, object, NPY_CLIPMODE) + object PyArray_PutMask (ndarray, object, object) + object PyArray_Repeat (ndarray, object, int) + object PyArray_Choose (ndarray, object, ndarray, NPY_CLIPMODE) + int PyArray_Sort (ndarray, int, NPY_SORTKIND) except -1 + object PyArray_ArgSort (ndarray, int, NPY_SORTKIND) + object PyArray_SearchSorted (ndarray, object, NPY_SEARCHSIDE, PyObject *) + object PyArray_ArgMax (ndarray, int, ndarray) + object PyArray_ArgMin (ndarray, int, ndarray) + object PyArray_Reshape (ndarray, object) + object PyArray_Newshape (ndarray, PyArray_Dims *, NPY_ORDER) + object PyArray_Squeeze (ndarray) + #object PyArray_View (ndarray, dtype, type) + object PyArray_SwapAxes (ndarray, int, int) + object PyArray_Max (ndarray, int, ndarray) + object PyArray_Min (ndarray, int, ndarray) + object PyArray_Ptp (ndarray, int, ndarray) + object PyArray_Mean (ndarray, int, int, ndarray) + object PyArray_Trace (ndarray, int, int, int, int, ndarray) + object PyArray_Diagonal (ndarray, int, int, int) + object PyArray_Clip (ndarray, object, object, ndarray) + object PyArray_Conjugate (ndarray, ndarray) + object PyArray_Nonzero (ndarray) + object PyArray_Std (ndarray, int, int, ndarray, int) + object PyArray_Sum (ndarray, int, int, ndarray) + object PyArray_CumSum (ndarray, int, int, ndarray) + object PyArray_Prod (ndarray, int, int, ndarray) + object PyArray_CumProd (ndarray, int, int, ndarray) + object PyArray_All (ndarray, int, ndarray) + object PyArray_Any (ndarray, int, ndarray) + object PyArray_Compress (ndarray, object, int, ndarray) + object PyArray_Flatten (ndarray, NPY_ORDER) + object PyArray_Ravel (ndarray, NPY_ORDER) + npy_intp PyArray_MultiplyList (npy_intp *, int) + int PyArray_MultiplyIntList (int *, int) + void * PyArray_GetPtr (ndarray, npy_intp*) + int PyArray_CompareLists (npy_intp *, npy_intp *, int) + #int PyArray_AsCArray (object*, void *, npy_intp *, int, dtype) + int PyArray_Free (object, void *) + #int PyArray_Converter (object, object*) + int PyArray_IntpFromSequence (object, npy_intp *, int) except -1 + object PyArray_Concatenate (object, int) + object PyArray_InnerProduct (object, object) + object PyArray_MatrixProduct (object, object) + object PyArray_Correlate (object, object, int) + #int PyArray_DescrConverter (object, dtype*) except 0 + #int PyArray_DescrConverter2 (object, dtype*) except 0 + int PyArray_IntpConverter (object, PyArray_Dims *) except 0 + #int PyArray_BufferConverter (object, chunk) except 0 + int PyArray_AxisConverter (object, int *) except 0 + int PyArray_BoolConverter (object, npy_bool *) except 0 + int PyArray_ByteorderConverter (object, char *) except 0 + int PyArray_OrderConverter (object, NPY_ORDER *) except 0 + unsigned char PyArray_EquivTypes (dtype, dtype) # clears errors + #object PyArray_Zeros (int, npy_intp *, dtype, int) + #object PyArray_Empty (int, npy_intp *, dtype, int) + object PyArray_Where (object, object, object) + object PyArray_Arange (double, double, double, int) + #object PyArray_ArangeObj (object, object, object, dtype) + int PyArray_SortkindConverter (object, NPY_SORTKIND *) except 0 + object PyArray_LexSort (object, int) + object PyArray_Round (ndarray, int, ndarray) + unsigned char PyArray_EquivTypenums (int, int) + int PyArray_RegisterDataType (dtype) except -1 + int PyArray_RegisterCastFunc (dtype, int, PyArray_VectorUnaryFunc *) except -1 + int PyArray_RegisterCanCast (dtype, int, NPY_SCALARKIND) except -1 + #void PyArray_InitArrFuncs (PyArray_ArrFuncs *) + object PyArray_IntTupleFromIntp (int, npy_intp *) + int PyArray_ClipmodeConverter (object, NPY_CLIPMODE *) except 0 + #int PyArray_OutputConverter (object, ndarray*) except 0 + object PyArray_BroadcastToShape (object, npy_intp *, int) + #int PyArray_DescrAlignConverter (object, dtype*) except 0 + #int PyArray_DescrAlignConverter2 (object, dtype*) except 0 + int PyArray_SearchsideConverter (object, void *) except 0 + object PyArray_CheckAxis (ndarray, int *, int) + npy_intp PyArray_OverflowMultiplyList (npy_intp *, int) + int PyArray_SetBaseObject(ndarray, base) except -1 # NOTE: steals a reference to base! Use "set_array_base()" instead. + + # The memory handler functions require the NumPy 1.22 API + # and may require defining NPY_TARGET_VERSION + ctypedef struct PyDataMemAllocator: + void *ctx + void* (*malloc) (void *ctx, size_t size) + void* (*calloc) (void *ctx, size_t nelem, size_t elsize) + void* (*realloc) (void *ctx, void *ptr, size_t new_size) + void (*free) (void *ctx, void *ptr, size_t size) + + ctypedef struct PyDataMem_Handler: + char* name + npy_uint8 version + PyDataMemAllocator allocator + + object PyDataMem_SetHandler(object handler) + object PyDataMem_GetHandler() + + # additional datetime related functions are defined below + + +# Typedefs that matches the runtime dtype objects in +# the numpy module. + +# The ones that are commented out needs an IFDEF function +# in Cython to enable them only on the right systems. + +ctypedef npy_int8 int8_t +ctypedef npy_int16 int16_t +ctypedef npy_int32 int32_t +ctypedef npy_int64 int64_t + +ctypedef npy_uint8 uint8_t +ctypedef npy_uint16 uint16_t +ctypedef npy_uint32 uint32_t +ctypedef npy_uint64 uint64_t + +ctypedef npy_float32 float32_t +ctypedef npy_float64 float64_t +#ctypedef npy_float80 float80_t +#ctypedef npy_float128 float128_t + +ctypedef float complex complex64_t +ctypedef double complex complex128_t + +ctypedef npy_longlong longlong_t +ctypedef npy_ulonglong ulonglong_t + +ctypedef npy_intp intp_t +ctypedef npy_uintp uintp_t + +ctypedef npy_double float_t +ctypedef npy_double double_t +ctypedef npy_longdouble longdouble_t + +ctypedef float complex cfloat_t +ctypedef double complex cdouble_t +ctypedef double complex complex_t +ctypedef long double complex clongdouble_t + +cdef inline object PyArray_MultiIterNew1(a): + return PyArray_MultiIterNew(1, a) + +cdef inline object PyArray_MultiIterNew2(a, b): + return PyArray_MultiIterNew(2, a, b) + +cdef inline object PyArray_MultiIterNew3(a, b, c): + return PyArray_MultiIterNew(3, a, b, c) + +cdef inline object PyArray_MultiIterNew4(a, b, c, d): + return PyArray_MultiIterNew(4, a, b, c, d) + +cdef inline object PyArray_MultiIterNew5(a, b, c, d, e): + return PyArray_MultiIterNew(5, a, b, c, d, e) + +cdef inline tuple PyDataType_SHAPE(dtype d): + if PyDataType_HASSUBARRAY(d): + return d.subarray.shape + else: + return () + + +cdef extern from "numpy/ndarrayobject.h": + PyTypeObject PyTimedeltaArrType_Type + PyTypeObject PyDatetimeArrType_Type + ctypedef int64_t npy_timedelta + ctypedef int64_t npy_datetime + +cdef extern from "numpy/ndarraytypes.h": + ctypedef struct PyArray_DatetimeMetaData: + NPY_DATETIMEUNIT base + int64_t num + + ctypedef struct npy_datetimestruct: + int64_t year + int32_t month, day, hour, min, sec, us, ps, as + + # Iterator API added in v1.6 + # + # These don't match the definition in the C API because Cython can't wrap + # function pointers that return functions. + # https://github.com/cython/cython/issues/6720 + ctypedef int (*NpyIter_IterNextFunc "NpyIter_IterNextFunc *")(NpyIter* it) noexcept nogil + ctypedef void (*NpyIter_GetMultiIndexFunc "NpyIter_GetMultiIndexFunc *")(NpyIter* it, npy_intp* outcoords) noexcept nogil + + +cdef extern from "numpy/arrayscalars.h": + + # abstract types + ctypedef class numpy.generic [object PyObject]: + pass + ctypedef class numpy.number [object PyObject]: + pass + ctypedef class numpy.integer [object PyObject]: + pass + ctypedef class numpy.signedinteger [object PyObject]: + pass + ctypedef class numpy.unsignedinteger [object PyObject]: + pass + ctypedef class numpy.inexact [object PyObject]: + pass + ctypedef class numpy.floating [object PyObject]: + pass + ctypedef class numpy.complexfloating [object PyObject]: + pass + ctypedef class numpy.flexible [object PyObject]: + pass + ctypedef class numpy.character [object PyObject]: + pass + + ctypedef struct PyDatetimeScalarObject: + # PyObject_HEAD + npy_datetime obval + PyArray_DatetimeMetaData obmeta + + ctypedef struct PyTimedeltaScalarObject: + # PyObject_HEAD + npy_timedelta obval + PyArray_DatetimeMetaData obmeta + + ctypedef enum NPY_DATETIMEUNIT: + NPY_FR_Y + NPY_FR_M + NPY_FR_W + NPY_FR_D + NPY_FR_B + NPY_FR_h + NPY_FR_m + NPY_FR_s + NPY_FR_ms + NPY_FR_us + NPY_FR_ns + NPY_FR_ps + NPY_FR_fs + NPY_FR_as + NPY_FR_GENERIC + + +cdef extern from "numpy/arrayobject.h": + # These are part of the C-API defined in `__multiarray_api.h` + + # NumPy internal definitions in datetime_strings.c: + int get_datetime_iso_8601_strlen "NpyDatetime_GetDatetimeISO8601StrLen" ( + int local, NPY_DATETIMEUNIT base) + int make_iso_8601_datetime "NpyDatetime_MakeISO8601Datetime" ( + npy_datetimestruct *dts, char *outstr, npy_intp outlen, + int local, int utc, NPY_DATETIMEUNIT base, int tzoffset, + NPY_CASTING casting) except -1 + + # NumPy internal definition in datetime.c: + # May return 1 to indicate that object does not appear to be a datetime + # (returns 0 on success). + int convert_pydatetime_to_datetimestruct "NpyDatetime_ConvertPyDateTimeToDatetimeStruct" ( + PyObject *obj, npy_datetimestruct *out, + NPY_DATETIMEUNIT *out_bestunit, int apply_tzinfo) except -1 + int convert_datetime64_to_datetimestruct "NpyDatetime_ConvertDatetime64ToDatetimeStruct" ( + PyArray_DatetimeMetaData *meta, npy_datetime dt, + npy_datetimestruct *out) except -1 + int convert_datetimestruct_to_datetime64 "NpyDatetime_ConvertDatetimeStructToDatetime64"( + PyArray_DatetimeMetaData *meta, const npy_datetimestruct *dts, + npy_datetime *out) except -1 + + +# +# ufunc API +# + +cdef extern from "numpy/ufuncobject.h": + + ctypedef void (*PyUFuncGenericFunction) (char **, npy_intp *, npy_intp *, void *) + + ctypedef class numpy.ufunc [object PyUFuncObject, check_size ignore]: + cdef: + int nin, nout, nargs + int identity + PyUFuncGenericFunction *functions + void **data + int ntypes + int check_return + char *name + char *types + char *doc + void *ptr + PyObject *obj + PyObject *userloops + + cdef enum: + PyUFunc_Zero + PyUFunc_One + PyUFunc_None + # deprecated + UFUNC_FPE_DIVIDEBYZERO + UFUNC_FPE_OVERFLOW + UFUNC_FPE_UNDERFLOW + UFUNC_FPE_INVALID + # use these instead + NPY_FPE_DIVIDEBYZERO + NPY_FPE_OVERFLOW + NPY_FPE_UNDERFLOW + NPY_FPE_INVALID + + + object PyUFunc_FromFuncAndData(PyUFuncGenericFunction *, + void **, char *, int, int, int, int, char *, char *, int) + int PyUFunc_RegisterLoopForType(ufunc, int, + PyUFuncGenericFunction, int *, void *) except -1 + void PyUFunc_f_f_As_d_d \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_d_d \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_f_f \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_g_g \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_F_F_As_D_D \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_F_F \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_D_D \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_G_G \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_O_O \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_ff_f_As_dd_d \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_ff_f \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_dd_d \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_gg_g \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_FF_F_As_DD_D \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_DD_D \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_FF_F \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_GG_G \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_OO_O \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_O_O_method \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_OO_O_method \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_On_Om \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_clearfperr() + int PyUFunc_getfperr() + int PyUFunc_ReplaceLoopBySignature \ + (ufunc, PyUFuncGenericFunction, int *, PyUFuncGenericFunction *) + object PyUFunc_FromFuncAndDataAndSignature \ + (PyUFuncGenericFunction *, void **, char *, int, int, int, + int, char *, char *, int, char *) + + int _import_umath() except -1 + +cdef inline void set_array_base(ndarray arr, object base) except *: + Py_INCREF(base) # important to do this before stealing the reference below! + PyArray_SetBaseObject(arr, base) + +cdef inline object get_array_base(ndarray arr): + base = PyArray_BASE(arr) + if base is NULL: + return None + return base + +# Versions of the import_* functions which are more suitable for +# Cython code. +cdef inline int import_array() except -1: + try: + __pyx_import_array() + except Exception: + raise ImportError("numpy._core.multiarray failed to import") + +cdef inline int import_umath() except -1: + try: + _import_umath() + except Exception: + raise ImportError("numpy._core.umath failed to import") + +cdef inline int import_ufunc() except -1: + try: + _import_umath() + except Exception: + raise ImportError("numpy._core.umath failed to import") + + +cdef inline bint is_timedelta64_object(object obj) noexcept: + """ + Cython equivalent of `isinstance(obj, np.timedelta64)` + + Parameters + ---------- + obj : object + + Returns + ------- + bool + """ + return PyObject_TypeCheck(obj, &PyTimedeltaArrType_Type) + + +cdef inline bint is_datetime64_object(object obj) noexcept: + """ + Cython equivalent of `isinstance(obj, np.datetime64)` + + Parameters + ---------- + obj : object + + Returns + ------- + bool + """ + return PyObject_TypeCheck(obj, &PyDatetimeArrType_Type) + + +cdef inline npy_datetime get_datetime64_value(object obj) noexcept nogil: + """ + returns the int64 value underlying scalar numpy datetime64 object + + Note that to interpret this as a datetime, the corresponding unit is + also needed. That can be found using `get_datetime64_unit`. + """ + return (obj).obval + + +cdef inline npy_timedelta get_timedelta64_value(object obj) noexcept nogil: + """ + returns the int64 value underlying scalar numpy timedelta64 object + """ + return (obj).obval + + +cdef inline NPY_DATETIMEUNIT get_datetime64_unit(object obj) noexcept nogil: + """ + returns the unit part of the dtype for a numpy datetime64 object. + """ + return (obj).obmeta.base + + +cdef extern from "numpy/arrayobject.h": + + ctypedef struct NpyIter: + pass + + cdef enum: + NPY_FAIL + NPY_SUCCEED + + cdef enum: + # Track an index representing C order + NPY_ITER_C_INDEX + # Track an index representing Fortran order + NPY_ITER_F_INDEX + # Track a multi-index + NPY_ITER_MULTI_INDEX + # User code external to the iterator does the 1-dimensional innermost loop + NPY_ITER_EXTERNAL_LOOP + # Convert all the operands to a common data type + NPY_ITER_COMMON_DTYPE + # Operands may hold references, requiring API access during iteration + NPY_ITER_REFS_OK + # Zero-sized operands should be permitted, iteration checks IterSize for 0 + NPY_ITER_ZEROSIZE_OK + # Permits reductions (size-0 stride with dimension size > 1) + NPY_ITER_REDUCE_OK + # Enables sub-range iteration + NPY_ITER_RANGED + # Enables buffering + NPY_ITER_BUFFERED + # When buffering is enabled, grows the inner loop if possible + NPY_ITER_GROWINNER + # Delay allocation of buffers until first Reset* call + NPY_ITER_DELAY_BUFALLOC + # When NPY_KEEPORDER is specified, disable reversing negative-stride axes + NPY_ITER_DONT_NEGATE_STRIDES + NPY_ITER_COPY_IF_OVERLAP + # The operand will be read from and written to + NPY_ITER_READWRITE + # The operand will only be read from + NPY_ITER_READONLY + # The operand will only be written to + NPY_ITER_WRITEONLY + # The operand's data must be in native byte order + NPY_ITER_NBO + # The operand's data must be aligned + NPY_ITER_ALIGNED + # The operand's data must be contiguous (within the inner loop) + NPY_ITER_CONTIG + # The operand may be copied to satisfy requirements + NPY_ITER_COPY + # The operand may be copied with WRITEBACKIFCOPY to satisfy requirements + NPY_ITER_UPDATEIFCOPY + # Allocate the operand if it is NULL + NPY_ITER_ALLOCATE + # If an operand is allocated, don't use any subtype + NPY_ITER_NO_SUBTYPE + # This is a virtual array slot, operand is NULL but temporary data is there + NPY_ITER_VIRTUAL + # Require that the dimension match the iterator dimensions exactly + NPY_ITER_NO_BROADCAST + # A mask is being used on this array, affects buffer -> array copy + NPY_ITER_WRITEMASKED + # This array is the mask for all WRITEMASKED operands + NPY_ITER_ARRAYMASK + # Assume iterator order data access for COPY_IF_OVERLAP + NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE + + # construction and destruction functions + NpyIter* NpyIter_New(ndarray arr, npy_uint32 flags, NPY_ORDER order, + NPY_CASTING casting, dtype datatype) except NULL + NpyIter* NpyIter_MultiNew(npy_intp nop, PyArrayObject** op, npy_uint32 flags, + NPY_ORDER order, NPY_CASTING casting, npy_uint32* + op_flags, PyArray_Descr** op_dtypes) except NULL + NpyIter* NpyIter_AdvancedNew(npy_intp nop, PyArrayObject** op, + npy_uint32 flags, NPY_ORDER order, + NPY_CASTING casting, npy_uint32* op_flags, + PyArray_Descr** op_dtypes, int oa_ndim, + int** op_axes, const npy_intp* itershape, + npy_intp buffersize) except NULL + NpyIter* NpyIter_Copy(NpyIter* it) except NULL + int NpyIter_RemoveAxis(NpyIter* it, int axis) except NPY_FAIL + int NpyIter_RemoveMultiIndex(NpyIter* it) except NPY_FAIL + int NpyIter_EnableExternalLoop(NpyIter* it) except NPY_FAIL + int NpyIter_Deallocate(NpyIter* it) except NPY_FAIL + int NpyIter_Reset(NpyIter* it, char** errmsg) except NPY_FAIL + int NpyIter_ResetToIterIndexRange(NpyIter* it, npy_intp istart, + npy_intp iend, char** errmsg) except NPY_FAIL + int NpyIter_ResetBasePointers(NpyIter* it, char** baseptrs, char** errmsg) except NPY_FAIL + int NpyIter_GotoMultiIndex(NpyIter* it, const npy_intp* multi_index) except NPY_FAIL + int NpyIter_GotoIndex(NpyIter* it, npy_intp index) except NPY_FAIL + npy_intp NpyIter_GetIterSize(NpyIter* it) nogil + npy_intp NpyIter_GetIterIndex(NpyIter* it) nogil + void NpyIter_GetIterIndexRange(NpyIter* it, npy_intp* istart, + npy_intp* iend) nogil + int NpyIter_GotoIterIndex(NpyIter* it, npy_intp iterindex) except NPY_FAIL + npy_bool NpyIter_HasDelayedBufAlloc(NpyIter* it) nogil + npy_bool NpyIter_HasExternalLoop(NpyIter* it) nogil + npy_bool NpyIter_HasMultiIndex(NpyIter* it) nogil + npy_bool NpyIter_HasIndex(NpyIter* it) nogil + npy_bool NpyIter_RequiresBuffering(NpyIter* it) nogil + npy_bool NpyIter_IsBuffered(NpyIter* it) nogil + npy_bool NpyIter_IsGrowInner(NpyIter* it) nogil + npy_intp NpyIter_GetBufferSize(NpyIter* it) nogil + int NpyIter_GetNDim(NpyIter* it) nogil + int NpyIter_GetNOp(NpyIter* it) nogil + npy_intp* NpyIter_GetAxisStrideArray(NpyIter* it, int axis) except NULL + int NpyIter_GetShape(NpyIter* it, npy_intp* outshape) nogil + PyArray_Descr** NpyIter_GetDescrArray(NpyIter* it) + PyArrayObject** NpyIter_GetOperandArray(NpyIter* it) + ndarray NpyIter_GetIterView(NpyIter* it, npy_intp i) + void NpyIter_GetReadFlags(NpyIter* it, char* outreadflags) + void NpyIter_GetWriteFlags(NpyIter* it, char* outwriteflags) + int NpyIter_CreateCompatibleStrides(NpyIter* it, npy_intp itemsize, + npy_intp* outstrides) except NPY_FAIL + npy_bool NpyIter_IsFirstVisit(NpyIter* it, int iop) nogil + # functions for iterating an NpyIter object + # + # These don't match the definition in the C API because Cython can't wrap + # function pointers that return functions. + NpyIter_IterNextFunc NpyIter_GetIterNext(NpyIter* it, char** errmsg) except NULL + NpyIter_GetMultiIndexFunc NpyIter_GetGetMultiIndex(NpyIter* it, + char** errmsg) except NULL + char** NpyIter_GetDataPtrArray(NpyIter* it) nogil + char** NpyIter_GetInitialDataPtrArray(NpyIter* it) nogil + npy_intp* NpyIter_GetIndexPtr(NpyIter* it) + npy_intp* NpyIter_GetInnerStrideArray(NpyIter* it) nogil + npy_intp* NpyIter_GetInnerLoopSizePtr(NpyIter* it) nogil + void NpyIter_GetInnerFixedStrideArray(NpyIter* it, npy_intp* outstrides) nogil + npy_bool NpyIter_IterationNeedsAPI(NpyIter* it) nogil + void NpyIter_DebugPrint(NpyIter* it) + +# NpyString API +cdef extern from "numpy/ndarraytypes.h": + ctypedef struct npy_string_allocator: + pass + + ctypedef struct npy_packed_static_string: + pass + + ctypedef struct npy_static_string: + size_t size + const char *buf + + ctypedef struct PyArray_StringDTypeObject: + PyArray_Descr base + PyObject *na_object + char coerce + char has_nan_na + char has_string_na + char array_owned + npy_static_string default_string + npy_static_string na_name + npy_string_allocator *allocator + +cdef extern from "numpy/arrayobject.h": + npy_string_allocator *NpyString_acquire_allocator(const PyArray_StringDTypeObject *descr) + void NpyString_acquire_allocators(size_t n_descriptors, PyArray_Descr *const descrs[], npy_string_allocator *allocators[]) + void NpyString_release_allocator(npy_string_allocator *allocator) + void NpyString_release_allocators(size_t length, npy_string_allocator *allocators[]) + int NpyString_load(npy_string_allocator *allocator, const npy_packed_static_string *packed_string, npy_static_string *unpacked_string) + int NpyString_pack_null(npy_string_allocator *allocator, npy_packed_static_string *packed_string) + int NpyString_pack(npy_string_allocator *allocator, npy_packed_static_string *packed_string, const char *buf, size_t size) diff --git a/.venv/lib/python3.12/site-packages/numpy/__init__.pxd b/.venv/lib/python3.12/site-packages/numpy/__init__.pxd new file mode 100644 index 0000000000000000000000000000000000000000..eb0764126116dd0791abc214db9c27299d72a67f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/__init__.pxd @@ -0,0 +1,1154 @@ +# NumPy static imports for Cython < 3.0 +# +# If any of the PyArray_* functions are called, import_array must be +# called first. +# +# Author: Dag Sverre Seljebotn +# + +DEF _buffer_format_string_len = 255 + +cimport cpython.buffer as pybuf +from cpython.ref cimport Py_INCREF +from cpython.mem cimport PyObject_Malloc, PyObject_Free +from cpython.object cimport PyObject, PyTypeObject +from cpython.buffer cimport PyObject_GetBuffer +from cpython.type cimport type +cimport libc.stdio as stdio + + +cdef extern from *: + # Leave a marker that the NumPy declarations came from NumPy itself and not from Cython. + # See https://github.com/cython/cython/issues/3573 + """ + /* Using NumPy API declarations from "numpy/__init__.pxd" */ + """ + + +cdef extern from "Python.h": + ctypedef int Py_intptr_t + bint PyObject_TypeCheck(object obj, PyTypeObject* type) + +cdef extern from "numpy/arrayobject.h": + # It would be nice to use size_t and ssize_t, but ssize_t has special + # implicit conversion rules, so just use "long". + # Note: The actual type only matters for Cython promotion, so long + # is closer than int, but could lead to incorrect promotion. + # (Not to worrying, and always the status-quo.) + ctypedef signed long npy_intp + ctypedef unsigned long npy_uintp + + ctypedef unsigned char npy_bool + + ctypedef signed char npy_byte + ctypedef signed short npy_short + ctypedef signed int npy_int + ctypedef signed long npy_long + ctypedef signed long long npy_longlong + + ctypedef unsigned char npy_ubyte + ctypedef unsigned short npy_ushort + ctypedef unsigned int npy_uint + ctypedef unsigned long npy_ulong + ctypedef unsigned long long npy_ulonglong + + ctypedef float npy_float + ctypedef double npy_double + ctypedef long double npy_longdouble + + ctypedef signed char npy_int8 + ctypedef signed short npy_int16 + ctypedef signed int npy_int32 + ctypedef signed long long npy_int64 + + ctypedef unsigned char npy_uint8 + ctypedef unsigned short npy_uint16 + ctypedef unsigned int npy_uint32 + ctypedef unsigned long long npy_uint64 + + ctypedef float npy_float32 + ctypedef double npy_float64 + ctypedef long double npy_float80 + ctypedef long double npy_float96 + ctypedef long double npy_float128 + + ctypedef struct npy_cfloat: + pass + + ctypedef struct npy_cdouble: + pass + + ctypedef struct npy_clongdouble: + pass + + ctypedef struct npy_complex64: + pass + + ctypedef struct npy_complex128: + pass + + ctypedef struct npy_complex160: + pass + + ctypedef struct npy_complex192: + pass + + ctypedef struct npy_complex256: + pass + + ctypedef struct PyArray_Dims: + npy_intp *ptr + int len + + + cdef enum NPY_TYPES: + NPY_BOOL + NPY_BYTE + NPY_UBYTE + NPY_SHORT + NPY_USHORT + NPY_INT + NPY_UINT + NPY_LONG + NPY_ULONG + NPY_LONGLONG + NPY_ULONGLONG + NPY_FLOAT + NPY_DOUBLE + NPY_LONGDOUBLE + NPY_CFLOAT + NPY_CDOUBLE + NPY_CLONGDOUBLE + NPY_OBJECT + NPY_STRING + NPY_UNICODE + NPY_VSTRING + NPY_VOID + NPY_DATETIME + NPY_TIMEDELTA + NPY_NTYPES_LEGACY + NPY_NOTYPE + + NPY_INT8 + NPY_INT16 + NPY_INT32 + NPY_INT64 + NPY_UINT8 + NPY_UINT16 + NPY_UINT32 + NPY_UINT64 + NPY_FLOAT16 + NPY_FLOAT32 + NPY_FLOAT64 + NPY_FLOAT80 + NPY_FLOAT96 + NPY_FLOAT128 + NPY_COMPLEX64 + NPY_COMPLEX128 + NPY_COMPLEX160 + NPY_COMPLEX192 + NPY_COMPLEX256 + + NPY_INTP + NPY_UINTP + NPY_DEFAULT_INT # Not a compile time constant (normally)! + + ctypedef enum NPY_ORDER: + NPY_ANYORDER + NPY_CORDER + NPY_FORTRANORDER + NPY_KEEPORDER + + ctypedef enum NPY_CASTING: + NPY_NO_CASTING + NPY_EQUIV_CASTING + NPY_SAFE_CASTING + NPY_SAME_KIND_CASTING + NPY_UNSAFE_CASTING + + ctypedef enum NPY_CLIPMODE: + NPY_CLIP + NPY_WRAP + NPY_RAISE + + ctypedef enum NPY_SCALARKIND: + NPY_NOSCALAR, + NPY_BOOL_SCALAR, + NPY_INTPOS_SCALAR, + NPY_INTNEG_SCALAR, + NPY_FLOAT_SCALAR, + NPY_COMPLEX_SCALAR, + NPY_OBJECT_SCALAR + + ctypedef enum NPY_SORTKIND: + NPY_QUICKSORT + NPY_HEAPSORT + NPY_MERGESORT + + ctypedef enum NPY_SEARCHSIDE: + NPY_SEARCHLEFT + NPY_SEARCHRIGHT + + enum: + NPY_ARRAY_C_CONTIGUOUS + NPY_ARRAY_F_CONTIGUOUS + NPY_ARRAY_OWNDATA + NPY_ARRAY_FORCECAST + NPY_ARRAY_ENSURECOPY + NPY_ARRAY_ENSUREARRAY + NPY_ARRAY_ELEMENTSTRIDES + NPY_ARRAY_ALIGNED + NPY_ARRAY_NOTSWAPPED + NPY_ARRAY_WRITEABLE + NPY_ARRAY_WRITEBACKIFCOPY + + NPY_ARRAY_BEHAVED + NPY_ARRAY_BEHAVED_NS + NPY_ARRAY_CARRAY + NPY_ARRAY_CARRAY_RO + NPY_ARRAY_FARRAY + NPY_ARRAY_FARRAY_RO + NPY_ARRAY_DEFAULT + + NPY_ARRAY_IN_ARRAY + NPY_ARRAY_OUT_ARRAY + NPY_ARRAY_INOUT_ARRAY + NPY_ARRAY_IN_FARRAY + NPY_ARRAY_OUT_FARRAY + NPY_ARRAY_INOUT_FARRAY + + NPY_ARRAY_UPDATE_ALL + + cdef enum: + NPY_MAXDIMS # 64 on NumPy 2.x and 32 on NumPy 1.x + NPY_RAVEL_AXIS # Used for functions like PyArray_Mean + + ctypedef void (*PyArray_VectorUnaryFunc)(void *, void *, npy_intp, void *, void *) + + ctypedef struct PyArray_ArrayDescr: + # shape is a tuple, but Cython doesn't support "tuple shape" + # inside a non-PyObject declaration, so we have to declare it + # as just a PyObject*. + PyObject* shape + + ctypedef struct PyArray_Descr: + pass + + ctypedef class numpy.dtype [object PyArray_Descr, check_size ignore]: + # Use PyDataType_* macros when possible, however there are no macros + # for accessing some of the fields, so some are defined. + cdef PyTypeObject* typeobj + cdef char kind + cdef char type + # Numpy sometimes mutates this without warning (e.g. it'll + # sometimes change "|" to "<" in shared dtype objects on + # little-endian machines). If this matters to you, use + # PyArray_IsNativeByteOrder(dtype.byteorder) instead of + # directly accessing this field. + cdef char byteorder + # Flags are not directly accessible on Cython <3. Use PyDataType_FLAGS. + # cdef char flags + cdef int type_num + # itemsize/elsize, alignment, fields, names, and subarray must + # use the `PyDataType_*` accessor macros. With Cython 3 you can + # still use getter attributes `dtype.itemsize` + + ctypedef class numpy.flatiter [object PyArrayIterObject, check_size ignore]: + # Use through macros + pass + + ctypedef class numpy.broadcast [object PyArrayMultiIterObject, check_size ignore]: + cdef int numiter + cdef npy_intp size, index + cdef int nd + cdef npy_intp *dimensions + cdef void **iters + + ctypedef struct PyArrayObject: + # For use in situations where ndarray can't replace PyArrayObject*, + # like PyArrayObject**. + pass + + ctypedef class numpy.ndarray [object PyArrayObject, check_size ignore]: + cdef __cythonbufferdefaults__ = {"mode": "strided"} + + cdef: + # Only taking a few of the most commonly used and stable fields. + # One should use PyArray_* macros instead to access the C fields. + char *data + int ndim "nd" + npy_intp *shape "dimensions" + npy_intp *strides + dtype descr # deprecated since NumPy 1.7 ! + PyObject* base # NOT PUBLIC, DO NOT USE ! + + + int _import_array() except -1 + # A second definition so _import_array isn't marked as used when we use it here. + # Do not use - subject to change any time. + int __pyx_import_array "_import_array"() except -1 + + # + # Macros from ndarrayobject.h + # + bint PyArray_CHKFLAGS(ndarray m, int flags) nogil + bint PyArray_IS_C_CONTIGUOUS(ndarray arr) nogil + bint PyArray_IS_F_CONTIGUOUS(ndarray arr) nogil + bint PyArray_ISCONTIGUOUS(ndarray m) nogil + bint PyArray_ISWRITEABLE(ndarray m) nogil + bint PyArray_ISALIGNED(ndarray m) nogil + + int PyArray_NDIM(ndarray) nogil + bint PyArray_ISONESEGMENT(ndarray) nogil + bint PyArray_ISFORTRAN(ndarray) nogil + int PyArray_FORTRANIF(ndarray) nogil + + void* PyArray_DATA(ndarray) nogil + char* PyArray_BYTES(ndarray) nogil + + npy_intp* PyArray_DIMS(ndarray) nogil + npy_intp* PyArray_STRIDES(ndarray) nogil + npy_intp PyArray_DIM(ndarray, size_t) nogil + npy_intp PyArray_STRIDE(ndarray, size_t) nogil + + PyObject *PyArray_BASE(ndarray) nogil # returns borrowed reference! + PyArray_Descr *PyArray_DESCR(ndarray) nogil # returns borrowed reference to dtype! + PyArray_Descr *PyArray_DTYPE(ndarray) nogil # returns borrowed reference to dtype! NP 1.7+ alias for descr. + int PyArray_FLAGS(ndarray) nogil + void PyArray_CLEARFLAGS(ndarray, int flags) nogil # Added in NumPy 1.7 + void PyArray_ENABLEFLAGS(ndarray, int flags) nogil # Added in NumPy 1.7 + npy_intp PyArray_ITEMSIZE(ndarray) nogil + int PyArray_TYPE(ndarray arr) nogil + + object PyArray_GETITEM(ndarray arr, void *itemptr) + int PyArray_SETITEM(ndarray arr, void *itemptr, object obj) except -1 + + bint PyTypeNum_ISBOOL(int) nogil + bint PyTypeNum_ISUNSIGNED(int) nogil + bint PyTypeNum_ISSIGNED(int) nogil + bint PyTypeNum_ISINTEGER(int) nogil + bint PyTypeNum_ISFLOAT(int) nogil + bint PyTypeNum_ISNUMBER(int) nogil + bint PyTypeNum_ISSTRING(int) nogil + bint PyTypeNum_ISCOMPLEX(int) nogil + bint PyTypeNum_ISFLEXIBLE(int) nogil + bint PyTypeNum_ISUSERDEF(int) nogil + bint PyTypeNum_ISEXTENDED(int) nogil + bint PyTypeNum_ISOBJECT(int) nogil + + npy_intp PyDataType_ELSIZE(dtype) nogil + npy_intp PyDataType_ALIGNMENT(dtype) nogil + PyObject* PyDataType_METADATA(dtype) nogil + PyArray_ArrayDescr* PyDataType_SUBARRAY(dtype) nogil + PyObject* PyDataType_NAMES(dtype) nogil + PyObject* PyDataType_FIELDS(dtype) nogil + + bint PyDataType_ISBOOL(dtype) nogil + bint PyDataType_ISUNSIGNED(dtype) nogil + bint PyDataType_ISSIGNED(dtype) nogil + bint PyDataType_ISINTEGER(dtype) nogil + bint PyDataType_ISFLOAT(dtype) nogil + bint PyDataType_ISNUMBER(dtype) nogil + bint PyDataType_ISSTRING(dtype) nogil + bint PyDataType_ISCOMPLEX(dtype) nogil + bint PyDataType_ISFLEXIBLE(dtype) nogil + bint PyDataType_ISUSERDEF(dtype) nogil + bint PyDataType_ISEXTENDED(dtype) nogil + bint PyDataType_ISOBJECT(dtype) nogil + bint PyDataType_HASFIELDS(dtype) nogil + bint PyDataType_HASSUBARRAY(dtype) nogil + npy_uint64 PyDataType_FLAGS(dtype) nogil + + bint PyArray_ISBOOL(ndarray) nogil + bint PyArray_ISUNSIGNED(ndarray) nogil + bint PyArray_ISSIGNED(ndarray) nogil + bint PyArray_ISINTEGER(ndarray) nogil + bint PyArray_ISFLOAT(ndarray) nogil + bint PyArray_ISNUMBER(ndarray) nogil + bint PyArray_ISSTRING(ndarray) nogil + bint PyArray_ISCOMPLEX(ndarray) nogil + bint PyArray_ISFLEXIBLE(ndarray) nogil + bint PyArray_ISUSERDEF(ndarray) nogil + bint PyArray_ISEXTENDED(ndarray) nogil + bint PyArray_ISOBJECT(ndarray) nogil + bint PyArray_HASFIELDS(ndarray) nogil + + bint PyArray_ISVARIABLE(ndarray) nogil + + bint PyArray_SAFEALIGNEDCOPY(ndarray) nogil + bint PyArray_ISNBO(char) nogil # works on ndarray.byteorder + bint PyArray_IsNativeByteOrder(char) nogil # works on ndarray.byteorder + bint PyArray_ISNOTSWAPPED(ndarray) nogil + bint PyArray_ISBYTESWAPPED(ndarray) nogil + + bint PyArray_FLAGSWAP(ndarray, int) nogil + + bint PyArray_ISCARRAY(ndarray) nogil + bint PyArray_ISCARRAY_RO(ndarray) nogil + bint PyArray_ISFARRAY(ndarray) nogil + bint PyArray_ISFARRAY_RO(ndarray) nogil + bint PyArray_ISBEHAVED(ndarray) nogil + bint PyArray_ISBEHAVED_RO(ndarray) nogil + + + bint PyDataType_ISNOTSWAPPED(dtype) nogil + bint PyDataType_ISBYTESWAPPED(dtype) nogil + + bint PyArray_DescrCheck(object) + + bint PyArray_Check(object) + bint PyArray_CheckExact(object) + + # Cannot be supported due to out arg: + # bint PyArray_HasArrayInterfaceType(object, dtype, object, object&) + # bint PyArray_HasArrayInterface(op, out) + + + bint PyArray_IsZeroDim(object) + # Cannot be supported due to ## ## in macro: + # bint PyArray_IsScalar(object, verbatim work) + bint PyArray_CheckScalar(object) + bint PyArray_IsPythonNumber(object) + bint PyArray_IsPythonScalar(object) + bint PyArray_IsAnyScalar(object) + bint PyArray_CheckAnyScalar(object) + + ndarray PyArray_GETCONTIGUOUS(ndarray) + bint PyArray_SAMESHAPE(ndarray, ndarray) nogil + npy_intp PyArray_SIZE(ndarray) nogil + npy_intp PyArray_NBYTES(ndarray) nogil + + object PyArray_FROM_O(object) + object PyArray_FROM_OF(object m, int flags) + object PyArray_FROM_OT(object m, int type) + object PyArray_FROM_OTF(object m, int type, int flags) + object PyArray_FROMANY(object m, int type, int min, int max, int flags) + object PyArray_ZEROS(int nd, npy_intp* dims, int type, int fortran) + object PyArray_EMPTY(int nd, npy_intp* dims, int type, int fortran) + void PyArray_FILLWBYTE(ndarray, int val) + object PyArray_ContiguousFromAny(op, int, int min_depth, int max_depth) + unsigned char PyArray_EquivArrTypes(ndarray a1, ndarray a2) + bint PyArray_EquivByteorders(int b1, int b2) nogil + object PyArray_SimpleNew(int nd, npy_intp* dims, int typenum) + object PyArray_SimpleNewFromData(int nd, npy_intp* dims, int typenum, void* data) + #object PyArray_SimpleNewFromDescr(int nd, npy_intp* dims, dtype descr) + object PyArray_ToScalar(void* data, ndarray arr) + + void* PyArray_GETPTR1(ndarray m, npy_intp i) nogil + void* PyArray_GETPTR2(ndarray m, npy_intp i, npy_intp j) nogil + void* PyArray_GETPTR3(ndarray m, npy_intp i, npy_intp j, npy_intp k) nogil + void* PyArray_GETPTR4(ndarray m, npy_intp i, npy_intp j, npy_intp k, npy_intp l) nogil + + # Cannot be supported due to out arg + # void PyArray_DESCR_REPLACE(descr) + + + object PyArray_Copy(ndarray) + object PyArray_FromObject(object op, int type, int min_depth, int max_depth) + object PyArray_ContiguousFromObject(object op, int type, int min_depth, int max_depth) + object PyArray_CopyFromObject(object op, int type, int min_depth, int max_depth) + + object PyArray_Cast(ndarray mp, int type_num) + object PyArray_Take(ndarray ap, object items, int axis) + object PyArray_Put(ndarray ap, object items, object values) + + void PyArray_ITER_RESET(flatiter it) nogil + void PyArray_ITER_NEXT(flatiter it) nogil + void PyArray_ITER_GOTO(flatiter it, npy_intp* destination) nogil + void PyArray_ITER_GOTO1D(flatiter it, npy_intp ind) nogil + void* PyArray_ITER_DATA(flatiter it) nogil + bint PyArray_ITER_NOTDONE(flatiter it) nogil + + void PyArray_MultiIter_RESET(broadcast multi) nogil + void PyArray_MultiIter_NEXT(broadcast multi) nogil + void PyArray_MultiIter_GOTO(broadcast multi, npy_intp dest) nogil + void PyArray_MultiIter_GOTO1D(broadcast multi, npy_intp ind) nogil + void* PyArray_MultiIter_DATA(broadcast multi, npy_intp i) nogil + void PyArray_MultiIter_NEXTi(broadcast multi, npy_intp i) nogil + bint PyArray_MultiIter_NOTDONE(broadcast multi) nogil + npy_intp PyArray_MultiIter_SIZE(broadcast multi) nogil + int PyArray_MultiIter_NDIM(broadcast multi) nogil + npy_intp PyArray_MultiIter_INDEX(broadcast multi) nogil + int PyArray_MultiIter_NUMITER(broadcast multi) nogil + npy_intp* PyArray_MultiIter_DIMS(broadcast multi) nogil + void** PyArray_MultiIter_ITERS(broadcast multi) nogil + + # Functions from __multiarray_api.h + + # Functions taking dtype and returning object/ndarray are disabled + # for now as they steal dtype references. I'm conservative and disable + # more than is probably needed until it can be checked further. + int PyArray_INCREF (ndarray) except * # uses PyArray_Item_INCREF... + int PyArray_XDECREF (ndarray) except * # uses PyArray_Item_DECREF... + dtype PyArray_DescrFromType (int) + object PyArray_TypeObjectFromType (int) + char * PyArray_Zero (ndarray) + char * PyArray_One (ndarray) + #object PyArray_CastToType (ndarray, dtype, int) + int PyArray_CanCastSafely (int, int) # writes errors + npy_bool PyArray_CanCastTo (dtype, dtype) # writes errors + int PyArray_ObjectType (object, int) except 0 + dtype PyArray_DescrFromObject (object, dtype) + #ndarray* PyArray_ConvertToCommonType (object, int *) + dtype PyArray_DescrFromScalar (object) + dtype PyArray_DescrFromTypeObject (object) + npy_intp PyArray_Size (object) + #object PyArray_Scalar (void *, dtype, object) + #object PyArray_FromScalar (object, dtype) + void PyArray_ScalarAsCtype (object, void *) + #int PyArray_CastScalarToCtype (object, void *, dtype) + #int PyArray_CastScalarDirect (object, dtype, void *, int) + #PyArray_VectorUnaryFunc * PyArray_GetCastFunc (dtype, int) + #object PyArray_FromAny (object, dtype, int, int, int, object) + object PyArray_EnsureArray (object) + object PyArray_EnsureAnyArray (object) + #object PyArray_FromFile (stdio.FILE *, dtype, npy_intp, char *) + #object PyArray_FromString (char *, npy_intp, dtype, npy_intp, char *) + #object PyArray_FromBuffer (object, dtype, npy_intp, npy_intp) + #object PyArray_FromIter (object, dtype, npy_intp) + object PyArray_Return (ndarray) + #object PyArray_GetField (ndarray, dtype, int) + #int PyArray_SetField (ndarray, dtype, int, object) except -1 + object PyArray_Byteswap (ndarray, npy_bool) + object PyArray_Resize (ndarray, PyArray_Dims *, int, NPY_ORDER) + int PyArray_CopyInto (ndarray, ndarray) except -1 + int PyArray_CopyAnyInto (ndarray, ndarray) except -1 + int PyArray_CopyObject (ndarray, object) except -1 + object PyArray_NewCopy (ndarray, NPY_ORDER) + object PyArray_ToList (ndarray) + object PyArray_ToString (ndarray, NPY_ORDER) + int PyArray_ToFile (ndarray, stdio.FILE *, char *, char *) except -1 + int PyArray_Dump (object, object, int) except -1 + object PyArray_Dumps (object, int) + int PyArray_ValidType (int) # Cannot error + void PyArray_UpdateFlags (ndarray, int) + object PyArray_New (type, int, npy_intp *, int, npy_intp *, void *, int, int, object) + #object PyArray_NewFromDescr (type, dtype, int, npy_intp *, npy_intp *, void *, int, object) + #dtype PyArray_DescrNew (dtype) + dtype PyArray_DescrNewFromType (int) + double PyArray_GetPriority (object, double) # clears errors as of 1.25 + object PyArray_IterNew (object) + object PyArray_MultiIterNew (int, ...) + + int PyArray_PyIntAsInt (object) except? -1 + npy_intp PyArray_PyIntAsIntp (object) + int PyArray_Broadcast (broadcast) except -1 + int PyArray_FillWithScalar (ndarray, object) except -1 + npy_bool PyArray_CheckStrides (int, int, npy_intp, npy_intp, npy_intp *, npy_intp *) + dtype PyArray_DescrNewByteorder (dtype, char) + object PyArray_IterAllButAxis (object, int *) + #object PyArray_CheckFromAny (object, dtype, int, int, int, object) + #object PyArray_FromArray (ndarray, dtype, int) + object PyArray_FromInterface (object) + object PyArray_FromStructInterface (object) + #object PyArray_FromArrayAttr (object, dtype, object) + #NPY_SCALARKIND PyArray_ScalarKind (int, ndarray*) + int PyArray_CanCoerceScalar (int, int, NPY_SCALARKIND) + npy_bool PyArray_CanCastScalar (type, type) + int PyArray_RemoveSmallest (broadcast) except -1 + int PyArray_ElementStrides (object) + void PyArray_Item_INCREF (char *, dtype) except * + void PyArray_Item_XDECREF (char *, dtype) except * + object PyArray_Transpose (ndarray, PyArray_Dims *) + object PyArray_TakeFrom (ndarray, object, int, ndarray, NPY_CLIPMODE) + object PyArray_PutTo (ndarray, object, object, NPY_CLIPMODE) + object PyArray_PutMask (ndarray, object, object) + object PyArray_Repeat (ndarray, object, int) + object PyArray_Choose (ndarray, object, ndarray, NPY_CLIPMODE) + int PyArray_Sort (ndarray, int, NPY_SORTKIND) except -1 + object PyArray_ArgSort (ndarray, int, NPY_SORTKIND) + object PyArray_SearchSorted (ndarray, object, NPY_SEARCHSIDE, PyObject *) + object PyArray_ArgMax (ndarray, int, ndarray) + object PyArray_ArgMin (ndarray, int, ndarray) + object PyArray_Reshape (ndarray, object) + object PyArray_Newshape (ndarray, PyArray_Dims *, NPY_ORDER) + object PyArray_Squeeze (ndarray) + #object PyArray_View (ndarray, dtype, type) + object PyArray_SwapAxes (ndarray, int, int) + object PyArray_Max (ndarray, int, ndarray) + object PyArray_Min (ndarray, int, ndarray) + object PyArray_Ptp (ndarray, int, ndarray) + object PyArray_Mean (ndarray, int, int, ndarray) + object PyArray_Trace (ndarray, int, int, int, int, ndarray) + object PyArray_Diagonal (ndarray, int, int, int) + object PyArray_Clip (ndarray, object, object, ndarray) + object PyArray_Conjugate (ndarray, ndarray) + object PyArray_Nonzero (ndarray) + object PyArray_Std (ndarray, int, int, ndarray, int) + object PyArray_Sum (ndarray, int, int, ndarray) + object PyArray_CumSum (ndarray, int, int, ndarray) + object PyArray_Prod (ndarray, int, int, ndarray) + object PyArray_CumProd (ndarray, int, int, ndarray) + object PyArray_All (ndarray, int, ndarray) + object PyArray_Any (ndarray, int, ndarray) + object PyArray_Compress (ndarray, object, int, ndarray) + object PyArray_Flatten (ndarray, NPY_ORDER) + object PyArray_Ravel (ndarray, NPY_ORDER) + npy_intp PyArray_MultiplyList (npy_intp *, int) + int PyArray_MultiplyIntList (int *, int) + void * PyArray_GetPtr (ndarray, npy_intp*) + int PyArray_CompareLists (npy_intp *, npy_intp *, int) + #int PyArray_AsCArray (object*, void *, npy_intp *, int, dtype) + int PyArray_Free (object, void *) + #int PyArray_Converter (object, object*) + int PyArray_IntpFromSequence (object, npy_intp *, int) except -1 + object PyArray_Concatenate (object, int) + object PyArray_InnerProduct (object, object) + object PyArray_MatrixProduct (object, object) + object PyArray_Correlate (object, object, int) + #int PyArray_DescrConverter (object, dtype*) except 0 + #int PyArray_DescrConverter2 (object, dtype*) except 0 + int PyArray_IntpConverter (object, PyArray_Dims *) except 0 + #int PyArray_BufferConverter (object, chunk) except 0 + int PyArray_AxisConverter (object, int *) except 0 + int PyArray_BoolConverter (object, npy_bool *) except 0 + int PyArray_ByteorderConverter (object, char *) except 0 + int PyArray_OrderConverter (object, NPY_ORDER *) except 0 + unsigned char PyArray_EquivTypes (dtype, dtype) # clears errors + #object PyArray_Zeros (int, npy_intp *, dtype, int) + #object PyArray_Empty (int, npy_intp *, dtype, int) + object PyArray_Where (object, object, object) + object PyArray_Arange (double, double, double, int) + #object PyArray_ArangeObj (object, object, object, dtype) + int PyArray_SortkindConverter (object, NPY_SORTKIND *) except 0 + object PyArray_LexSort (object, int) + object PyArray_Round (ndarray, int, ndarray) + unsigned char PyArray_EquivTypenums (int, int) + int PyArray_RegisterDataType (dtype) except -1 + int PyArray_RegisterCastFunc (dtype, int, PyArray_VectorUnaryFunc *) except -1 + int PyArray_RegisterCanCast (dtype, int, NPY_SCALARKIND) except -1 + #void PyArray_InitArrFuncs (PyArray_ArrFuncs *) + object PyArray_IntTupleFromIntp (int, npy_intp *) + int PyArray_ClipmodeConverter (object, NPY_CLIPMODE *) except 0 + #int PyArray_OutputConverter (object, ndarray*) except 0 + object PyArray_BroadcastToShape (object, npy_intp *, int) + #int PyArray_DescrAlignConverter (object, dtype*) except 0 + #int PyArray_DescrAlignConverter2 (object, dtype*) except 0 + int PyArray_SearchsideConverter (object, void *) except 0 + object PyArray_CheckAxis (ndarray, int *, int) + npy_intp PyArray_OverflowMultiplyList (npy_intp *, int) + int PyArray_SetBaseObject(ndarray, base) except -1 # NOTE: steals a reference to base! Use "set_array_base()" instead. + + # The memory handler functions require the NumPy 1.22 API + # and may require defining NPY_TARGET_VERSION + ctypedef struct PyDataMemAllocator: + void *ctx + void* (*malloc) (void *ctx, size_t size) + void* (*calloc) (void *ctx, size_t nelem, size_t elsize) + void* (*realloc) (void *ctx, void *ptr, size_t new_size) + void (*free) (void *ctx, void *ptr, size_t size) + + ctypedef struct PyDataMem_Handler: + char* name + npy_uint8 version + PyDataMemAllocator allocator + + object PyDataMem_SetHandler(object handler) + object PyDataMem_GetHandler() + + # additional datetime related functions are defined below + + +# Typedefs that matches the runtime dtype objects in +# the numpy module. + +# The ones that are commented out needs an IFDEF function +# in Cython to enable them only on the right systems. + +ctypedef npy_int8 int8_t +ctypedef npy_int16 int16_t +ctypedef npy_int32 int32_t +ctypedef npy_int64 int64_t + +ctypedef npy_uint8 uint8_t +ctypedef npy_uint16 uint16_t +ctypedef npy_uint32 uint32_t +ctypedef npy_uint64 uint64_t + +ctypedef npy_float32 float32_t +ctypedef npy_float64 float64_t +#ctypedef npy_float80 float80_t +#ctypedef npy_float128 float128_t + +ctypedef float complex complex64_t +ctypedef double complex complex128_t + +ctypedef npy_longlong longlong_t +ctypedef npy_ulonglong ulonglong_t + +ctypedef npy_intp intp_t +ctypedef npy_uintp uintp_t + +ctypedef npy_double float_t +ctypedef npy_double double_t +ctypedef npy_longdouble longdouble_t + +ctypedef float complex cfloat_t +ctypedef double complex cdouble_t +ctypedef double complex complex_t +ctypedef long double complex clongdouble_t + +cdef inline object PyArray_MultiIterNew1(a): + return PyArray_MultiIterNew(1, a) + +cdef inline object PyArray_MultiIterNew2(a, b): + return PyArray_MultiIterNew(2, a, b) + +cdef inline object PyArray_MultiIterNew3(a, b, c): + return PyArray_MultiIterNew(3, a, b, c) + +cdef inline object PyArray_MultiIterNew4(a, b, c, d): + return PyArray_MultiIterNew(4, a, b, c, d) + +cdef inline object PyArray_MultiIterNew5(a, b, c, d, e): + return PyArray_MultiIterNew(5, a, b, c, d, e) + +cdef inline tuple PyDataType_SHAPE(dtype d): + if PyDataType_HASSUBARRAY(d): + return d.subarray.shape + else: + return () + + +cdef extern from "numpy/ndarrayobject.h": + PyTypeObject PyTimedeltaArrType_Type + PyTypeObject PyDatetimeArrType_Type + ctypedef int64_t npy_timedelta + ctypedef int64_t npy_datetime + +cdef extern from "numpy/ndarraytypes.h": + ctypedef struct PyArray_DatetimeMetaData: + NPY_DATETIMEUNIT base + int64_t num + + ctypedef struct npy_datetimestruct: + int64_t year + int32_t month, day, hour, min, sec, us, ps, as + + # Iterator API added in v1.6 + # + # These don't match the definition in the C API because Cython can't wrap + # function pointers that return functions. + # https://github.com/cython/cython/issues/6720 + ctypedef int (*NpyIter_IterNextFunc "NpyIter_IterNextFunc *")(NpyIter* it) noexcept nogil + ctypedef void (*NpyIter_GetMultiIndexFunc "NpyIter_GetMultiIndexFunc *")(NpyIter* it, npy_intp* outcoords) noexcept nogil + +cdef extern from "numpy/arrayscalars.h": + + # abstract types + ctypedef class numpy.generic [object PyObject]: + pass + ctypedef class numpy.number [object PyObject]: + pass + ctypedef class numpy.integer [object PyObject]: + pass + ctypedef class numpy.signedinteger [object PyObject]: + pass + ctypedef class numpy.unsignedinteger [object PyObject]: + pass + ctypedef class numpy.inexact [object PyObject]: + pass + ctypedef class numpy.floating [object PyObject]: + pass + ctypedef class numpy.complexfloating [object PyObject]: + pass + ctypedef class numpy.flexible [object PyObject]: + pass + ctypedef class numpy.character [object PyObject]: + pass + + ctypedef struct PyDatetimeScalarObject: + # PyObject_HEAD + npy_datetime obval + PyArray_DatetimeMetaData obmeta + + ctypedef struct PyTimedeltaScalarObject: + # PyObject_HEAD + npy_timedelta obval + PyArray_DatetimeMetaData obmeta + + ctypedef enum NPY_DATETIMEUNIT: + NPY_FR_Y + NPY_FR_M + NPY_FR_W + NPY_FR_D + NPY_FR_B + NPY_FR_h + NPY_FR_m + NPY_FR_s + NPY_FR_ms + NPY_FR_us + NPY_FR_ns + NPY_FR_ps + NPY_FR_fs + NPY_FR_as + NPY_FR_GENERIC + + +cdef extern from "numpy/arrayobject.h": + # These are part of the C-API defined in `__multiarray_api.h` + + # NumPy internal definitions in datetime_strings.c: + int get_datetime_iso_8601_strlen "NpyDatetime_GetDatetimeISO8601StrLen" ( + int local, NPY_DATETIMEUNIT base) + int make_iso_8601_datetime "NpyDatetime_MakeISO8601Datetime" ( + npy_datetimestruct *dts, char *outstr, npy_intp outlen, + int local, int utc, NPY_DATETIMEUNIT base, int tzoffset, + NPY_CASTING casting) except -1 + + # NumPy internal definition in datetime.c: + # May return 1 to indicate that object does not appear to be a datetime + # (returns 0 on success). + int convert_pydatetime_to_datetimestruct "NpyDatetime_ConvertPyDateTimeToDatetimeStruct" ( + PyObject *obj, npy_datetimestruct *out, + NPY_DATETIMEUNIT *out_bestunit, int apply_tzinfo) except -1 + int convert_datetime64_to_datetimestruct "NpyDatetime_ConvertDatetime64ToDatetimeStruct" ( + PyArray_DatetimeMetaData *meta, npy_datetime dt, + npy_datetimestruct *out) except -1 + int convert_datetimestruct_to_datetime64 "NpyDatetime_ConvertDatetimeStructToDatetime64"( + PyArray_DatetimeMetaData *meta, const npy_datetimestruct *dts, + npy_datetime *out) except -1 + + +# +# ufunc API +# + +cdef extern from "numpy/ufuncobject.h": + + ctypedef void (*PyUFuncGenericFunction) (char **, npy_intp *, npy_intp *, void *) + + ctypedef class numpy.ufunc [object PyUFuncObject, check_size ignore]: + cdef: + int nin, nout, nargs + int identity + PyUFuncGenericFunction *functions + void **data + int ntypes + int check_return + char *name + char *types + char *doc + void *ptr + PyObject *obj + PyObject *userloops + + cdef enum: + PyUFunc_Zero + PyUFunc_One + PyUFunc_None + # deprecated + UFUNC_FPE_DIVIDEBYZERO + UFUNC_FPE_OVERFLOW + UFUNC_FPE_UNDERFLOW + UFUNC_FPE_INVALID + # use these instead + NPY_FPE_DIVIDEBYZERO + NPY_FPE_OVERFLOW + NPY_FPE_UNDERFLOW + NPY_FPE_INVALID + + object PyUFunc_FromFuncAndData(PyUFuncGenericFunction *, + void **, char *, int, int, int, int, char *, char *, int) + int PyUFunc_RegisterLoopForType(ufunc, int, + PyUFuncGenericFunction, int *, void *) except -1 + void PyUFunc_f_f_As_d_d \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_d_d \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_f_f \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_g_g \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_F_F_As_D_D \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_F_F \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_D_D \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_G_G \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_O_O \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_ff_f_As_dd_d \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_ff_f \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_dd_d \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_gg_g \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_FF_F_As_DD_D \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_DD_D \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_FF_F \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_GG_G \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_OO_O \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_O_O_method \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_OO_O_method \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_On_Om \ + (char **, npy_intp *, npy_intp *, void *) + void PyUFunc_clearfperr() + int PyUFunc_getfperr() + int PyUFunc_ReplaceLoopBySignature \ + (ufunc, PyUFuncGenericFunction, int *, PyUFuncGenericFunction *) + object PyUFunc_FromFuncAndDataAndSignature \ + (PyUFuncGenericFunction *, void **, char *, int, int, int, + int, char *, char *, int, char *) + + int _import_umath() except -1 + +cdef inline void set_array_base(ndarray arr, object base): + Py_INCREF(base) # important to do this before stealing the reference below! + PyArray_SetBaseObject(arr, base) + +cdef inline object get_array_base(ndarray arr): + base = PyArray_BASE(arr) + if base is NULL: + return None + return base + +# Versions of the import_* functions which are more suitable for +# Cython code. +cdef inline int import_array() except -1: + try: + __pyx_import_array() + except Exception: + raise ImportError("numpy._core.multiarray failed to import") + +cdef inline int import_umath() except -1: + try: + _import_umath() + except Exception: + raise ImportError("numpy._core.umath failed to import") + +cdef inline int import_ufunc() except -1: + try: + _import_umath() + except Exception: + raise ImportError("numpy._core.umath failed to import") + + +cdef inline bint is_timedelta64_object(object obj): + """ + Cython equivalent of `isinstance(obj, np.timedelta64)` + + Parameters + ---------- + obj : object + + Returns + ------- + bool + """ + return PyObject_TypeCheck(obj, &PyTimedeltaArrType_Type) + + +cdef inline bint is_datetime64_object(object obj): + """ + Cython equivalent of `isinstance(obj, np.datetime64)` + + Parameters + ---------- + obj : object + + Returns + ------- + bool + """ + return PyObject_TypeCheck(obj, &PyDatetimeArrType_Type) + + +cdef inline npy_datetime get_datetime64_value(object obj) nogil: + """ + returns the int64 value underlying scalar numpy datetime64 object + + Note that to interpret this as a datetime, the corresponding unit is + also needed. That can be found using `get_datetime64_unit`. + """ + return (obj).obval + + +cdef inline npy_timedelta get_timedelta64_value(object obj) nogil: + """ + returns the int64 value underlying scalar numpy timedelta64 object + """ + return (obj).obval + + +cdef inline NPY_DATETIMEUNIT get_datetime64_unit(object obj) nogil: + """ + returns the unit part of the dtype for a numpy datetime64 object. + """ + return (obj).obmeta.base + + +cdef extern from "numpy/arrayobject.h": + + ctypedef struct NpyIter: + pass + + cdef enum: + NPY_FAIL + NPY_SUCCEED + + cdef enum: + # Track an index representing C order + NPY_ITER_C_INDEX + # Track an index representing Fortran order + NPY_ITER_F_INDEX + # Track a multi-index + NPY_ITER_MULTI_INDEX + # User code external to the iterator does the 1-dimensional innermost loop + NPY_ITER_EXTERNAL_LOOP + # Convert all the operands to a common data type + NPY_ITER_COMMON_DTYPE + # Operands may hold references, requiring API access during iteration + NPY_ITER_REFS_OK + # Zero-sized operands should be permitted, iteration checks IterSize for 0 + NPY_ITER_ZEROSIZE_OK + # Permits reductions (size-0 stride with dimension size > 1) + NPY_ITER_REDUCE_OK + # Enables sub-range iteration + NPY_ITER_RANGED + # Enables buffering + NPY_ITER_BUFFERED + # When buffering is enabled, grows the inner loop if possible + NPY_ITER_GROWINNER + # Delay allocation of buffers until first Reset* call + NPY_ITER_DELAY_BUFALLOC + # When NPY_KEEPORDER is specified, disable reversing negative-stride axes + NPY_ITER_DONT_NEGATE_STRIDES + NPY_ITER_COPY_IF_OVERLAP + # The operand will be read from and written to + NPY_ITER_READWRITE + # The operand will only be read from + NPY_ITER_READONLY + # The operand will only be written to + NPY_ITER_WRITEONLY + # The operand's data must be in native byte order + NPY_ITER_NBO + # The operand's data must be aligned + NPY_ITER_ALIGNED + # The operand's data must be contiguous (within the inner loop) + NPY_ITER_CONTIG + # The operand may be copied to satisfy requirements + NPY_ITER_COPY + # The operand may be copied with WRITEBACKIFCOPY to satisfy requirements + NPY_ITER_UPDATEIFCOPY + # Allocate the operand if it is NULL + NPY_ITER_ALLOCATE + # If an operand is allocated, don't use any subtype + NPY_ITER_NO_SUBTYPE + # This is a virtual array slot, operand is NULL but temporary data is there + NPY_ITER_VIRTUAL + # Require that the dimension match the iterator dimensions exactly + NPY_ITER_NO_BROADCAST + # A mask is being used on this array, affects buffer -> array copy + NPY_ITER_WRITEMASKED + # This array is the mask for all WRITEMASKED operands + NPY_ITER_ARRAYMASK + # Assume iterator order data access for COPY_IF_OVERLAP + NPY_ITER_OVERLAP_ASSUME_ELEMENTWISE + + # construction and destruction functions + NpyIter* NpyIter_New(ndarray arr, npy_uint32 flags, NPY_ORDER order, + NPY_CASTING casting, dtype datatype) except NULL + NpyIter* NpyIter_MultiNew(npy_intp nop, PyArrayObject** op, npy_uint32 flags, + NPY_ORDER order, NPY_CASTING casting, npy_uint32* + op_flags, PyArray_Descr** op_dtypes) except NULL + NpyIter* NpyIter_AdvancedNew(npy_intp nop, PyArrayObject** op, + npy_uint32 flags, NPY_ORDER order, + NPY_CASTING casting, npy_uint32* op_flags, + PyArray_Descr** op_dtypes, int oa_ndim, + int** op_axes, const npy_intp* itershape, + npy_intp buffersize) except NULL + NpyIter* NpyIter_Copy(NpyIter* it) except NULL + int NpyIter_RemoveAxis(NpyIter* it, int axis) except NPY_FAIL + int NpyIter_RemoveMultiIndex(NpyIter* it) except NPY_FAIL + int NpyIter_EnableExternalLoop(NpyIter* it) except NPY_FAIL + int NpyIter_Deallocate(NpyIter* it) except NPY_FAIL + int NpyIter_Reset(NpyIter* it, char** errmsg) except NPY_FAIL + int NpyIter_ResetToIterIndexRange(NpyIter* it, npy_intp istart, + npy_intp iend, char** errmsg) except NPY_FAIL + int NpyIter_ResetBasePointers(NpyIter* it, char** baseptrs, char** errmsg) except NPY_FAIL + int NpyIter_GotoMultiIndex(NpyIter* it, const npy_intp* multi_index) except NPY_FAIL + int NpyIter_GotoIndex(NpyIter* it, npy_intp index) except NPY_FAIL + npy_intp NpyIter_GetIterSize(NpyIter* it) nogil + npy_intp NpyIter_GetIterIndex(NpyIter* it) nogil + void NpyIter_GetIterIndexRange(NpyIter* it, npy_intp* istart, + npy_intp* iend) nogil + int NpyIter_GotoIterIndex(NpyIter* it, npy_intp iterindex) except NPY_FAIL + npy_bool NpyIter_HasDelayedBufAlloc(NpyIter* it) nogil + npy_bool NpyIter_HasExternalLoop(NpyIter* it) nogil + npy_bool NpyIter_HasMultiIndex(NpyIter* it) nogil + npy_bool NpyIter_HasIndex(NpyIter* it) nogil + npy_bool NpyIter_RequiresBuffering(NpyIter* it) nogil + npy_bool NpyIter_IsBuffered(NpyIter* it) nogil + npy_bool NpyIter_IsGrowInner(NpyIter* it) nogil + npy_intp NpyIter_GetBufferSize(NpyIter* it) nogil + int NpyIter_GetNDim(NpyIter* it) nogil + int NpyIter_GetNOp(NpyIter* it) nogil + npy_intp* NpyIter_GetAxisStrideArray(NpyIter* it, int axis) except NULL + int NpyIter_GetShape(NpyIter* it, npy_intp* outshape) nogil + PyArray_Descr** NpyIter_GetDescrArray(NpyIter* it) + PyArrayObject** NpyIter_GetOperandArray(NpyIter* it) + ndarray NpyIter_GetIterView(NpyIter* it, npy_intp i) + void NpyIter_GetReadFlags(NpyIter* it, char* outreadflags) + void NpyIter_GetWriteFlags(NpyIter* it, char* outwriteflags) + int NpyIter_CreateCompatibleStrides(NpyIter* it, npy_intp itemsize, + npy_intp* outstrides) except NPY_FAIL + npy_bool NpyIter_IsFirstVisit(NpyIter* it, int iop) nogil + # functions for iterating an NpyIter object + # + # These don't match the definition in the C API because Cython can't wrap + # function pointers that return functions. + NpyIter_IterNextFunc* NpyIter_GetIterNext(NpyIter* it, char** errmsg) except NULL + NpyIter_GetMultiIndexFunc* NpyIter_GetGetMultiIndex(NpyIter* it, + char** errmsg) except NULL + char** NpyIter_GetDataPtrArray(NpyIter* it) nogil + char** NpyIter_GetInitialDataPtrArray(NpyIter* it) nogil + npy_intp* NpyIter_GetIndexPtr(NpyIter* it) + npy_intp* NpyIter_GetInnerStrideArray(NpyIter* it) nogil + npy_intp* NpyIter_GetInnerLoopSizePtr(NpyIter* it) nogil + void NpyIter_GetInnerFixedStrideArray(NpyIter* it, npy_intp* outstrides) nogil + npy_bool NpyIter_IterationNeedsAPI(NpyIter* it) nogil + void NpyIter_DebugPrint(NpyIter* it) + +# NpyString API +cdef extern from "numpy/ndarraytypes.h": + ctypedef struct npy_string_allocator: + pass + + ctypedef struct npy_packed_static_string: + pass + + ctypedef struct npy_static_string: + size_t size + const char *buf + + ctypedef struct PyArray_StringDTypeObject: + PyArray_Descr base + PyObject *na_object + char coerce + char has_nan_na + char has_string_na + char array_owned + npy_static_string default_string + npy_static_string na_name + npy_string_allocator *allocator + +cdef extern from "numpy/arrayobject.h": + npy_string_allocator *NpyString_acquire_allocator(const PyArray_StringDTypeObject *descr) + void NpyString_acquire_allocators(size_t n_descriptors, PyArray_Descr *const descrs[], npy_string_allocator *allocators[]) + void NpyString_release_allocator(npy_string_allocator *allocator) + void NpyString_release_allocators(size_t length, npy_string_allocator *allocators[]) + int NpyString_load(npy_string_allocator *allocator, const npy_packed_static_string *packed_string, npy_static_string *unpacked_string) + int NpyString_pack_null(npy_string_allocator *allocator, npy_packed_static_string *packed_string) + int NpyString_pack(npy_string_allocator *allocator, npy_packed_static_string *packed_string, const char *buf, size_t size) diff --git a/.venv/lib/python3.12/site-packages/numpy/__init__.py b/.venv/lib/python3.12/site-packages/numpy/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8fb2e742dfc4ee0d6a7410e31231ae1f9b45e863 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/__init__.py @@ -0,0 +1,928 @@ +""" +NumPy +===== + +Provides + 1. An array object of arbitrary homogeneous items + 2. Fast mathematical operations over arrays + 3. Linear Algebra, Fourier Transforms, Random Number Generation + +How to use the documentation +---------------------------- +Documentation is available in two forms: docstrings provided +with the code, and a loose standing reference guide, available from +`the NumPy homepage `_. + +We recommend exploring the docstrings using +`IPython `_, an advanced Python shell with +TAB-completion and introspection capabilities. See below for further +instructions. + +The docstring examples assume that `numpy` has been imported as ``np``:: + + >>> import numpy as np + +Code snippets are indicated by three greater-than signs:: + + >>> x = 42 + >>> x = x + 1 + +Use the built-in ``help`` function to view a function's docstring:: + + >>> help(np.sort) + ... # doctest: +SKIP + +For some objects, ``np.info(obj)`` may provide additional help. This is +particularly true if you see the line "Help on ufunc object:" at the top +of the help() page. Ufuncs are implemented in C, not Python, for speed. +The native Python help() does not know how to view their help, but our +np.info() function does. + +Available subpackages +--------------------- +lib + Basic functions used by several sub-packages. +random + Core Random Tools +linalg + Core Linear Algebra Tools +fft + Core FFT routines +polynomial + Polynomial tools +testing + NumPy testing tools +distutils + Enhancements to distutils with support for + Fortran compilers support and more (for Python <= 3.11) + +Utilities +--------- +test + Run numpy unittests +show_config + Show numpy build configuration +__version__ + NumPy version string + +Viewing documentation using IPython +----------------------------------- + +Start IPython and import `numpy` usually under the alias ``np``: `import +numpy as np`. Then, directly past or use the ``%cpaste`` magic to paste +examples into the shell. To see which functions are available in `numpy`, +type ``np.`` (where ```` refers to the TAB key), or use +``np.*cos*?`` (where ```` refers to the ENTER key) to narrow +down the list. To view the docstring for a function, use +``np.cos?`` (to view the docstring) and ``np.cos??`` (to view +the source code). + +Copies vs. in-place operation +----------------------------- +Most of the functions in `numpy` return a copy of the array argument +(e.g., `np.sort`). In-place versions of these functions are often +available as array methods, i.e. ``x = np.array([1,2,3]); x.sort()``. +Exceptions to this rule are documented. + +""" +import os +import sys +import warnings + +# If a version with git hash was stored, use that instead +from . import version +from ._expired_attrs_2_0 import __expired_attributes__ +from ._globals import _CopyMode, _NoValue +from .version import __version__ + +# We first need to detect if we're being called as part of the numpy setup +# procedure itself in a reliable manner. +try: + __NUMPY_SETUP__ # noqa: B018 +except NameError: + __NUMPY_SETUP__ = False + +if __NUMPY_SETUP__: + sys.stderr.write('Running from numpy source directory.\n') +else: + # Allow distributors to run custom init code before importing numpy._core + from . import _distributor_init + + try: + from numpy.__config__ import show_config + except ImportError as e: + msg = """Error importing numpy: you should not try to import numpy from + its source directory; please exit the numpy source tree, and relaunch + your python interpreter from there.""" + raise ImportError(msg) from e + + from . import _core + from ._core import ( + False_, + ScalarType, + True_, + abs, + absolute, + acos, + acosh, + add, + all, + allclose, + amax, + amin, + any, + arange, + arccos, + arccosh, + arcsin, + arcsinh, + arctan, + arctan2, + arctanh, + argmax, + argmin, + argpartition, + argsort, + argwhere, + around, + array, + array2string, + array_equal, + array_equiv, + array_repr, + array_str, + asanyarray, + asarray, + ascontiguousarray, + asfortranarray, + asin, + asinh, + astype, + atan, + atan2, + atanh, + atleast_1d, + atleast_2d, + atleast_3d, + base_repr, + binary_repr, + bitwise_and, + bitwise_count, + bitwise_invert, + bitwise_left_shift, + bitwise_not, + bitwise_or, + bitwise_right_shift, + bitwise_xor, + block, + bool, + bool_, + broadcast, + busday_count, + busday_offset, + busdaycalendar, + byte, + bytes_, + can_cast, + cbrt, + cdouble, + ceil, + character, + choose, + clip, + clongdouble, + complex64, + complex128, + complexfloating, + compress, + concat, + concatenate, + conj, + conjugate, + convolve, + copysign, + copyto, + correlate, + cos, + cosh, + count_nonzero, + cross, + csingle, + cumprod, + cumsum, + cumulative_prod, + cumulative_sum, + datetime64, + datetime_as_string, + datetime_data, + deg2rad, + degrees, + diagonal, + divide, + divmod, + dot, + double, + dtype, + e, + einsum, + einsum_path, + empty, + empty_like, + equal, + errstate, + euler_gamma, + exp, + exp2, + expm1, + fabs, + finfo, + flatiter, + flatnonzero, + flexible, + float16, + float32, + float64, + float_power, + floating, + floor, + floor_divide, + fmax, + fmin, + fmod, + format_float_positional, + format_float_scientific, + frexp, + from_dlpack, + frombuffer, + fromfile, + fromfunction, + fromiter, + frompyfunc, + fromstring, + full, + full_like, + gcd, + generic, + geomspace, + get_printoptions, + getbufsize, + geterr, + geterrcall, + greater, + greater_equal, + half, + heaviside, + hstack, + hypot, + identity, + iinfo, + indices, + inexact, + inf, + inner, + int8, + int16, + int32, + int64, + int_, + intc, + integer, + intp, + invert, + is_busday, + isclose, + isdtype, + isfinite, + isfortran, + isinf, + isnan, + isnat, + isscalar, + issubdtype, + lcm, + ldexp, + left_shift, + less, + less_equal, + lexsort, + linspace, + little_endian, + log, + log1p, + log2, + log10, + logaddexp, + logaddexp2, + logical_and, + logical_not, + logical_or, + logical_xor, + logspace, + long, + longdouble, + longlong, + matmul, + matrix_transpose, + matvec, + max, + maximum, + may_share_memory, + mean, + memmap, + min, + min_scalar_type, + minimum, + mod, + modf, + moveaxis, + multiply, + nan, + ndarray, + ndim, + nditer, + negative, + nested_iters, + newaxis, + nextafter, + nonzero, + not_equal, + number, + object_, + ones, + ones_like, + outer, + partition, + permute_dims, + pi, + positive, + pow, + power, + printoptions, + prod, + promote_types, + ptp, + put, + putmask, + rad2deg, + radians, + ravel, + recarray, + reciprocal, + record, + remainder, + repeat, + require, + reshape, + resize, + result_type, + right_shift, + rint, + roll, + rollaxis, + round, + sctypeDict, + searchsorted, + set_printoptions, + setbufsize, + seterr, + seterrcall, + shape, + shares_memory, + short, + sign, + signbit, + signedinteger, + sin, + single, + sinh, + size, + sort, + spacing, + sqrt, + square, + squeeze, + stack, + std, + str_, + subtract, + sum, + swapaxes, + take, + tan, + tanh, + tensordot, + timedelta64, + trace, + transpose, + true_divide, + trunc, + typecodes, + ubyte, + ufunc, + uint, + uint8, + uint16, + uint32, + uint64, + uintc, + uintp, + ulong, + ulonglong, + unsignedinteger, + unstack, + ushort, + var, + vdot, + vecdot, + vecmat, + void, + vstack, + where, + zeros, + zeros_like, + ) + + # NOTE: It's still under discussion whether these aliases + # should be removed. + for ta in ["float96", "float128", "complex192", "complex256"]: + try: + globals()[ta] = getattr(_core, ta) + except AttributeError: + pass + del ta + + from . import lib + from . import matrixlib as _mat + from .lib import scimath as emath + from .lib._arraypad_impl import pad + from .lib._arraysetops_impl import ( + ediff1d, + in1d, + intersect1d, + isin, + setdiff1d, + setxor1d, + union1d, + unique, + unique_all, + unique_counts, + unique_inverse, + unique_values, + ) + from .lib._function_base_impl import ( + angle, + append, + asarray_chkfinite, + average, + bartlett, + bincount, + blackman, + copy, + corrcoef, + cov, + delete, + diff, + digitize, + extract, + flip, + gradient, + hamming, + hanning, + i0, + insert, + interp, + iterable, + kaiser, + median, + meshgrid, + percentile, + piecewise, + place, + quantile, + rot90, + select, + sinc, + sort_complex, + trapezoid, + trapz, + trim_zeros, + unwrap, + vectorize, + ) + from .lib._histograms_impl import histogram, histogram_bin_edges, histogramdd + from .lib._index_tricks_impl import ( + c_, + diag_indices, + diag_indices_from, + fill_diagonal, + index_exp, + ix_, + mgrid, + ndenumerate, + ndindex, + ogrid, + r_, + ravel_multi_index, + s_, + unravel_index, + ) + from .lib._nanfunctions_impl import ( + nanargmax, + nanargmin, + nancumprod, + nancumsum, + nanmax, + nanmean, + nanmedian, + nanmin, + nanpercentile, + nanprod, + nanquantile, + nanstd, + nansum, + nanvar, + ) + from .lib._npyio_impl import ( + fromregex, + genfromtxt, + load, + loadtxt, + packbits, + save, + savetxt, + savez, + savez_compressed, + unpackbits, + ) + from .lib._polynomial_impl import ( + poly, + poly1d, + polyadd, + polyder, + polydiv, + polyfit, + polyint, + polymul, + polysub, + polyval, + roots, + ) + from .lib._shape_base_impl import ( + apply_along_axis, + apply_over_axes, + array_split, + column_stack, + dsplit, + dstack, + expand_dims, + hsplit, + kron, + put_along_axis, + row_stack, + split, + take_along_axis, + tile, + vsplit, + ) + from .lib._stride_tricks_impl import ( + broadcast_arrays, + broadcast_shapes, + broadcast_to, + ) + from .lib._twodim_base_impl import ( + diag, + diagflat, + eye, + fliplr, + flipud, + histogram2d, + mask_indices, + tri, + tril, + tril_indices, + tril_indices_from, + triu, + triu_indices, + triu_indices_from, + vander, + ) + from .lib._type_check_impl import ( + common_type, + imag, + iscomplex, + iscomplexobj, + isreal, + isrealobj, + mintypecode, + nan_to_num, + real, + real_if_close, + typename, + ) + from .lib._ufunclike_impl import fix, isneginf, isposinf + from .lib._utils_impl import get_include, info, show_runtime + from .matrixlib import asmatrix, bmat, matrix + + # public submodules are imported lazily, therefore are accessible from + # __getattr__. Note that `distutils` (deprecated) and `array_api` + # (experimental label) are not added here, because `from numpy import *` + # must not raise any warnings - that's too disruptive. + __numpy_submodules__ = { + "linalg", "fft", "dtypes", "random", "polynomial", "ma", + "exceptions", "lib", "ctypeslib", "testing", "typing", + "f2py", "test", "rec", "char", "core", "strings", + } + + # We build warning messages for former attributes + _msg = ( + "module 'numpy' has no attribute '{n}'.\n" + "`np.{n}` was a deprecated alias for the builtin `{n}`. " + "To avoid this error in existing code, use `{n}` by itself. " + "Doing this will not modify any behavior and is safe. {extended_msg}\n" + "The aliases was originally deprecated in NumPy 1.20; for more " + "details and guidance see the original release note at:\n" + " https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations") + + _specific_msg = ( + "If you specifically wanted the numpy scalar type, use `np.{}` here.") + + _int_extended_msg = ( + "When replacing `np.{}`, you may wish to use e.g. `np.int64` " + "or `np.int32` to specify the precision. If you wish to review " + "your current use, check the release note link for " + "additional information.") + + _type_info = [ + ("object", ""), # The NumPy scalar only exists by name. + ("float", _specific_msg.format("float64")), + ("complex", _specific_msg.format("complex128")), + ("str", _specific_msg.format("str_")), + ("int", _int_extended_msg.format("int"))] + + __former_attrs__ = { + n: _msg.format(n=n, extended_msg=extended_msg) + for n, extended_msg in _type_info + } + + # Some of these could be defined right away, but most were aliases to + # the Python objects and only removed in NumPy 1.24. Defining them should + # probably wait for NumPy 1.26 or 2.0. + # When defined, these should possibly not be added to `__all__` to avoid + # import with `from numpy import *`. + __future_scalars__ = {"str", "bytes", "object"} + + __array_api_version__ = "2024.12" + + from ._array_api_info import __array_namespace_info__ + + # now that numpy core module is imported, can initialize limits + _core.getlimits._register_known_types() + + __all__ = list( + __numpy_submodules__ | + set(_core.__all__) | + set(_mat.__all__) | + set(lib._histograms_impl.__all__) | + set(lib._nanfunctions_impl.__all__) | + set(lib._function_base_impl.__all__) | + set(lib._twodim_base_impl.__all__) | + set(lib._shape_base_impl.__all__) | + set(lib._type_check_impl.__all__) | + set(lib._arraysetops_impl.__all__) | + set(lib._ufunclike_impl.__all__) | + set(lib._arraypad_impl.__all__) | + set(lib._utils_impl.__all__) | + set(lib._stride_tricks_impl.__all__) | + set(lib._polynomial_impl.__all__) | + set(lib._npyio_impl.__all__) | + set(lib._index_tricks_impl.__all__) | + {"emath", "show_config", "__version__", "__array_namespace_info__"} + ) + + # Filter out Cython harmless warnings + warnings.filterwarnings("ignore", message="numpy.dtype size changed") + warnings.filterwarnings("ignore", message="numpy.ufunc size changed") + warnings.filterwarnings("ignore", message="numpy.ndarray size changed") + + def __getattr__(attr): + # Warn for expired attributes + import warnings + + if attr == "linalg": + import numpy.linalg as linalg + return linalg + elif attr == "fft": + import numpy.fft as fft + return fft + elif attr == "dtypes": + import numpy.dtypes as dtypes + return dtypes + elif attr == "random": + import numpy.random as random + return random + elif attr == "polynomial": + import numpy.polynomial as polynomial + return polynomial + elif attr == "ma": + import numpy.ma as ma + return ma + elif attr == "ctypeslib": + import numpy.ctypeslib as ctypeslib + return ctypeslib + elif attr == "exceptions": + import numpy.exceptions as exceptions + return exceptions + elif attr == "testing": + import numpy.testing as testing + return testing + elif attr == "matlib": + import numpy.matlib as matlib + return matlib + elif attr == "f2py": + import numpy.f2py as f2py + return f2py + elif attr == "typing": + import numpy.typing as typing + return typing + elif attr == "rec": + import numpy.rec as rec + return rec + elif attr == "char": + import numpy.char as char + return char + elif attr == "array_api": + raise AttributeError("`numpy.array_api` is not available from " + "numpy 2.0 onwards", name=None) + elif attr == "core": + import numpy.core as core + return core + elif attr == "strings": + import numpy.strings as strings + return strings + elif attr == "distutils": + if 'distutils' in __numpy_submodules__: + import numpy.distutils as distutils + return distutils + else: + raise AttributeError("`numpy.distutils` is not available from " + "Python 3.12 onwards", name=None) + + if attr in __future_scalars__: + # And future warnings for those that will change, but also give + # the AttributeError + warnings.warn( + f"In the future `np.{attr}` will be defined as the " + "corresponding NumPy scalar.", FutureWarning, stacklevel=2) + + if attr in __former_attrs__: + raise AttributeError(__former_attrs__[attr], name=None) + + if attr in __expired_attributes__: + raise AttributeError( + f"`np.{attr}` was removed in the NumPy 2.0 release. " + f"{__expired_attributes__[attr]}", + name=None + ) + + if attr == "chararray": + warnings.warn( + "`np.chararray` is deprecated and will be removed from " + "the main namespace in the future. Use an array with a string " + "or bytes dtype instead.", DeprecationWarning, stacklevel=2) + import numpy.char as char + return char.chararray + + raise AttributeError(f"module {__name__!r} has no attribute {attr!r}") + + def __dir__(): + public_symbols = ( + globals().keys() | __numpy_submodules__ + ) + public_symbols -= { + "matrixlib", "matlib", "tests", "conftest", "version", + "distutils", "array_api" + } + return list(public_symbols) + + # Pytest testing + from numpy._pytesttester import PytestTester + test = PytestTester(__name__) + del PytestTester + + def _sanity_check(): + """ + Quick sanity checks for common bugs caused by environment. + There are some cases e.g. with wrong BLAS ABI that cause wrong + results under specific runtime conditions that are not necessarily + achieved during test suite runs, and it is useful to catch those early. + + See https://github.com/numpy/numpy/issues/8577 and other + similar bug reports. + + """ + try: + x = ones(2, dtype=float32) + if not abs(x.dot(x) - float32(2.0)) < 1e-5: + raise AssertionError + except AssertionError: + msg = ("The current Numpy installation ({!r}) fails to " + "pass simple sanity checks. This can be caused for example " + "by incorrect BLAS library being linked in, or by mixing " + "package managers (pip, conda, apt, ...). Search closed " + "numpy issues for similar problems.") + raise RuntimeError(msg.format(__file__)) from None + + _sanity_check() + del _sanity_check + + def _mac_os_check(): + """ + Quick Sanity check for Mac OS look for accelerate build bugs. + Testing numpy polyfit calls init_dgelsd(LAPACK) + """ + try: + c = array([3., 2., 1.]) + x = linspace(0, 2, 5) + y = polyval(c, x) + _ = polyfit(x, y, 2, cov=True) + except ValueError: + pass + + if sys.platform == "darwin": + from . import exceptions + with warnings.catch_warnings(record=True) as w: + _mac_os_check() + # Throw runtime error, if the test failed + # Check for warning and report the error_message + if len(w) > 0: + for _wn in w: + if _wn.category is exceptions.RankWarning: + # Ignore other warnings, they may not be relevant (see gh-25433) + error_message = ( + f"{_wn.category.__name__}: {_wn.message}" + ) + msg = ( + "Polyfit sanity test emitted a warning, most likely due " + "to using a buggy Accelerate backend." + "\nIf you compiled yourself, more information is available at:" # noqa: E501 + "\nhttps://numpy.org/devdocs/building/index.html" + "\nOtherwise report this to the vendor " + f"that provided NumPy.\n\n{error_message}\n") + raise RuntimeError(msg) + del _wn + del w + del _mac_os_check + + def hugepage_setup(): + """ + We usually use madvise hugepages support, but on some old kernels it + is slow and thus better avoided. Specifically kernel version 4.6 + had a bug fix which probably fixed this: + https://github.com/torvalds/linux/commit/7cf91a98e607c2f935dbcc177d70011e95b8faff + """ + use_hugepage = os.environ.get("NUMPY_MADVISE_HUGEPAGE", None) + if sys.platform == "linux" and use_hugepage is None: + # If there is an issue with parsing the kernel version, + # set use_hugepage to 0. Usage of LooseVersion will handle + # the kernel version parsing better, but avoided since it + # will increase the import time. + # See: #16679 for related discussion. + try: + use_hugepage = 1 + kernel_version = os.uname().release.split(".")[:2] + kernel_version = tuple(int(v) for v in kernel_version) + if kernel_version < (4, 6): + use_hugepage = 0 + except ValueError: + use_hugepage = 0 + elif use_hugepage is None: + # This is not Linux, so it should not matter, just enable anyway + use_hugepage = 1 + else: + use_hugepage = int(use_hugepage) + return use_hugepage + + # Note that this will currently only make a difference on Linux + _core.multiarray._set_madvise_hugepage(hugepage_setup()) + del hugepage_setup + + # Give a warning if NumPy is reloaded or imported on a sub-interpreter + # We do this from python, since the C-module may not be reloaded and + # it is tidier organized. + _core.multiarray._multiarray_umath._reload_guard() + + # TODO: Remove the environment variable entirely now that it is "weak" + if (os.environ.get("NPY_PROMOTION_STATE", "weak") != "weak"): + warnings.warn( + "NPY_PROMOTION_STATE was a temporary feature for NumPy 2.0 " + "transition and is ignored after NumPy 2.2.", + UserWarning, stacklevel=2) + + # Tell PyInstaller where to find hook-numpy.py + def _pyinstaller_hooks_dir(): + from pathlib import Path + return [str(Path(__file__).with_name("_pyinstaller").resolve())] + + +# Remove symbols imported for internal use +del os, sys, warnings diff --git a/.venv/lib/python3.12/site-packages/numpy/__init__.pyi b/.venv/lib/python3.12/site-packages/numpy/__init__.pyi new file mode 100644 index 0000000000000000000000000000000000000000..25397572881f9b66f53fde071f1b13244ff3a2f7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/__init__.pyi @@ -0,0 +1,5547 @@ +# ruff: noqa: I001 +import builtins +import sys +import mmap +import ctypes as ct +import array as _array +import datetime as dt +from abc import abstractmethod +from types import EllipsisType, ModuleType, TracebackType, MappingProxyType, GenericAlias +from decimal import Decimal +from fractions import Fraction +from uuid import UUID + +import numpy as np +from numpy.__config__ import show as show_config +from numpy._pytesttester import PytestTester +from numpy._core._internal import _ctypes + +from numpy._typing import ( + # Arrays + ArrayLike, + NDArray, + _SupportsArray, + _NestedSequence, + _ArrayLike, + _ArrayLikeBool_co, + _ArrayLikeUInt_co, + _ArrayLikeInt, + _ArrayLikeInt_co, + _ArrayLikeFloat64_co, + _ArrayLikeFloat_co, + _ArrayLikeComplex128_co, + _ArrayLikeComplex_co, + _ArrayLikeNumber_co, + _ArrayLikeObject_co, + _ArrayLikeBytes_co, + _ArrayLikeStr_co, + _ArrayLikeString_co, + _ArrayLikeTD64_co, + _ArrayLikeDT64_co, + # DTypes + DTypeLike, + _DTypeLike, + _DTypeLikeVoid, + _VoidDTypeLike, + # Shapes + _AnyShape, + _Shape, + _ShapeLike, + # Scalars + _CharLike_co, + _IntLike_co, + _FloatLike_co, + _TD64Like_co, + _NumberLike_co, + _ScalarLike_co, + # `number` precision + NBitBase, + # NOTE: Do not remove the extended precision bit-types even if seemingly unused; + # they're used by the mypy plugin + _128Bit, + _96Bit, + _64Bit, + _32Bit, + _16Bit, + _8Bit, + _NBitByte, + _NBitShort, + _NBitIntC, + _NBitIntP, + _NBitLong, + _NBitLongLong, + _NBitHalf, + _NBitSingle, + _NBitDouble, + _NBitLongDouble, + # Character codes + _BoolCodes, + _UInt8Codes, + _UInt16Codes, + _UInt32Codes, + _UInt64Codes, + _Int8Codes, + _Int16Codes, + _Int32Codes, + _Int64Codes, + _Float16Codes, + _Float32Codes, + _Float64Codes, + _Complex64Codes, + _Complex128Codes, + _ByteCodes, + _ShortCodes, + _IntCCodes, + _IntPCodes, + _LongCodes, + _LongLongCodes, + _UByteCodes, + _UShortCodes, + _UIntCCodes, + _UIntPCodes, + _ULongCodes, + _ULongLongCodes, + _HalfCodes, + _SingleCodes, + _DoubleCodes, + _LongDoubleCodes, + _CSingleCodes, + _CDoubleCodes, + _CLongDoubleCodes, + _DT64Codes, + _TD64Codes, + _StrCodes, + _BytesCodes, + _VoidCodes, + _ObjectCodes, + _StringCodes, + _UnsignedIntegerCodes, + _SignedIntegerCodes, + _IntegerCodes, + _FloatingCodes, + _ComplexFloatingCodes, + _InexactCodes, + _NumberCodes, + _CharacterCodes, + _FlexibleCodes, + _GenericCodes, + # Ufuncs + _UFunc_Nin1_Nout1, + _UFunc_Nin2_Nout1, + _UFunc_Nin1_Nout2, + _UFunc_Nin2_Nout2, + _GUFunc_Nin2_Nout1, +) + +from numpy._typing._callable import ( + _IntTrueDiv, + _UnsignedIntOp, + _UnsignedIntBitOp, + _UnsignedIntMod, + _UnsignedIntDivMod, + _SignedIntOp, + _SignedIntBitOp, + _SignedIntMod, + _SignedIntDivMod, + _FloatOp, + _FloatMod, + _FloatDivMod, + _NumberOp, + _ComparisonOpLT, + _ComparisonOpLE, + _ComparisonOpGT, + _ComparisonOpGE, +) + +# NOTE: Numpy's mypy plugin is used for removing the types unavailable to the specific platform +from numpy._typing._extended_precision import ( + float96, + float128, + complex192, + complex256, +) + +from numpy._array_api_info import __array_namespace_info__ + +from collections.abc import ( + Callable, + Iterable, + Iterator, + Mapping, + Sequence, +) + +if sys.version_info >= (3, 12): + from collections.abc import Buffer as _SupportsBuffer +else: + _SupportsBuffer: TypeAlias = ( + bytes + | bytearray + | memoryview + | _array.array[Any] + | mmap.mmap + | NDArray[Any] + | generic + ) + +from typing import ( + Any, + ClassVar, + Final, + Generic, + Literal as L, + LiteralString, + Never, + NoReturn, + Protocol, + Self, + SupportsComplex, + SupportsFloat, + SupportsInt, + SupportsIndex, + TypeAlias, + TypedDict, + final, + overload, + type_check_only, +) + +# NOTE: `typing_extensions` and `_typeshed` are always available in `.pyi` stubs, even +# if not available at runtime. This is because the `typeshed` stubs for the standard +# library include `typing_extensions` stubs: +# https://github.com/python/typeshed/blob/main/stdlib/typing_extensions.pyi +from _typeshed import Incomplete, StrOrBytesPath, SupportsFlush, SupportsLenAndGetItem, SupportsWrite +from typing_extensions import CapsuleType, TypeVar + +from numpy import ( + char, + core, + ctypeslib, + dtypes, + exceptions, + f2py, + fft, + lib, + linalg, + ma, + polynomial, + random, + rec, + strings, + testing, + typing, +) + +# available through `__getattr__`, but not in `__all__` or `__dir__` +from numpy import ( + __config__ as __config__, + matlib as matlib, + matrixlib as matrixlib, + version as version, +) +if sys.version_info < (3, 12): + from numpy import distutils as distutils + +from numpy._core.records import ( + record, + recarray, +) + +from numpy._core.function_base import ( + linspace, + logspace, + geomspace, +) + +from numpy._core.fromnumeric import ( + take, + reshape, + choose, + repeat, + put, + swapaxes, + transpose, + matrix_transpose, + partition, + argpartition, + sort, + argsort, + argmax, + argmin, + searchsorted, + resize, + squeeze, + diagonal, + trace, + ravel, + nonzero, + shape, + compress, + clip, + sum, + all, + any, + cumsum, + cumulative_sum, + ptp, + max, + min, + amax, + amin, + prod, + cumprod, + cumulative_prod, + ndim, + size, + around, + round, + mean, + std, + var, +) + +from numpy._core._asarray import ( + require, +) + +from numpy._core._type_aliases import ( + sctypeDict, +) + +from numpy._core._ufunc_config import ( + seterr, + geterr, + setbufsize, + getbufsize, + seterrcall, + geterrcall, + _ErrKind, + _ErrCall, +) + +from numpy._core.arrayprint import ( + set_printoptions, + get_printoptions, + array2string, + format_float_scientific, + format_float_positional, + array_repr, + array_str, + printoptions, +) + +from numpy._core.einsumfunc import ( + einsum, + einsum_path, +) + +from numpy._core.multiarray import ( + array, + empty_like, + empty, + zeros, + concatenate, + inner, + where, + lexsort, + can_cast, + min_scalar_type, + result_type, + dot, + vdot, + bincount, + copyto, + putmask, + packbits, + unpackbits, + shares_memory, + may_share_memory, + asarray, + asanyarray, + ascontiguousarray, + asfortranarray, + arange, + busday_count, + busday_offset, + datetime_as_string, + datetime_data, + frombuffer, + fromfile, + fromiter, + is_busday, + promote_types, + fromstring, + frompyfunc, + nested_iters, + flagsobj, +) + +from numpy._core.numeric import ( + zeros_like, + ones, + ones_like, + full, + full_like, + count_nonzero, + isfortran, + argwhere, + flatnonzero, + correlate, + convolve, + outer, + tensordot, + roll, + rollaxis, + moveaxis, + cross, + indices, + fromfunction, + isscalar, + binary_repr, + base_repr, + identity, + allclose, + isclose, + array_equal, + array_equiv, + astype, +) + +from numpy._core.numerictypes import ( + isdtype, + issubdtype, + ScalarType, + typecodes, +) + +from numpy._core.shape_base import ( + atleast_1d, + atleast_2d, + atleast_3d, + block, + hstack, + stack, + vstack, + unstack, +) + +from ._expired_attrs_2_0 import __expired_attributes__ as __expired_attributes__ + +from numpy.lib import ( + scimath as emath, +) + +from numpy.lib._arraypad_impl import ( + pad, +) + +from numpy.lib._arraysetops_impl import ( + ediff1d, + in1d, + intersect1d, + isin, + setdiff1d, + setxor1d, + union1d, + unique, + unique_all, + unique_counts, + unique_inverse, + unique_values, +) + +from numpy.lib._function_base_impl import ( + select, + piecewise, + trim_zeros, + copy, + iterable, + percentile, + diff, + gradient, + angle, + unwrap, + sort_complex, + flip, + rot90, + extract, + place, + asarray_chkfinite, + average, + digitize, + cov, + corrcoef, + median, + sinc, + hamming, + hanning, + bartlett, + blackman, + kaiser, + trapezoid, + trapz, + i0, + meshgrid, + delete, + insert, + append, + interp, + quantile, +) + +from numpy._globals import _CopyMode + +from numpy.lib._histograms_impl import ( + histogram_bin_edges, + histogram, + histogramdd, +) + +from numpy.lib._index_tricks_impl import ( + ndenumerate, + ndindex, + ravel_multi_index, + unravel_index, + mgrid, + ogrid, + r_, + c_, + s_, + index_exp, + ix_, + fill_diagonal, + diag_indices, + diag_indices_from, +) + +from numpy.lib._nanfunctions_impl import ( + nansum, + nanmax, + nanmin, + nanargmax, + nanargmin, + nanmean, + nanmedian, + nanpercentile, + nanvar, + nanstd, + nanprod, + nancumsum, + nancumprod, + nanquantile, +) + +from numpy.lib._npyio_impl import ( + savetxt, + loadtxt, + genfromtxt, + load, + save, + savez, + savez_compressed, + fromregex, +) + +from numpy.lib._polynomial_impl import ( + poly, + roots, + polyint, + polyder, + polyadd, + polysub, + polymul, + polydiv, + polyval, + polyfit, +) + +from numpy.lib._shape_base_impl import ( + column_stack, + row_stack, + dstack, + array_split, + split, + hsplit, + vsplit, + dsplit, + apply_over_axes, + expand_dims, + apply_along_axis, + kron, + tile, + take_along_axis, + put_along_axis, +) + +from numpy.lib._stride_tricks_impl import ( + broadcast_to, + broadcast_arrays, + broadcast_shapes, +) + +from numpy.lib._twodim_base_impl import ( + diag, + diagflat, + eye, + fliplr, + flipud, + tri, + triu, + tril, + vander, + histogram2d, + mask_indices, + tril_indices, + tril_indices_from, + triu_indices, + triu_indices_from, +) + +from numpy.lib._type_check_impl import ( + mintypecode, + real, + imag, + iscomplex, + isreal, + iscomplexobj, + isrealobj, + nan_to_num, + real_if_close, + typename, + common_type, +) + +from numpy.lib._ufunclike_impl import ( + fix, + isposinf, + isneginf, +) + +from numpy.lib._utils_impl import ( + get_include, + info, + show_runtime, +) + +from numpy.matrixlib import ( + asmatrix, + bmat, +) + +__all__ = [ # noqa: RUF022 + # __numpy_submodules__ + "char", "core", "ctypeslib", "dtypes", "exceptions", "f2py", "fft", "lib", "linalg", + "ma", "polynomial", "random", "rec", "strings", "test", "testing", "typing", + + # _core.__all__ + "abs", "acos", "acosh", "asin", "asinh", "atan", "atanh", "atan2", "bitwise_invert", + "bitwise_left_shift", "bitwise_right_shift", "concat", "pow", "permute_dims", + "memmap", "sctypeDict", "record", "recarray", + + # _core.numeric.__all__ + "newaxis", "ndarray", "flatiter", "nditer", "nested_iters", "ufunc", "arange", + "array", "asarray", "asanyarray", "ascontiguousarray", "asfortranarray", "zeros", + "count_nonzero", "empty", "broadcast", "dtype", "fromstring", "fromfile", + "frombuffer", "from_dlpack", "where", "argwhere", "copyto", "concatenate", + "lexsort", "astype", "can_cast", "promote_types", "min_scalar_type", "result_type", + "isfortran", "empty_like", "zeros_like", "ones_like", "correlate", "convolve", + "inner", "dot", "outer", "vdot", "roll", "rollaxis", "moveaxis", "cross", + "tensordot", "little_endian", "fromiter", "array_equal", "array_equiv", "indices", + "fromfunction", "isclose", "isscalar", "binary_repr", "base_repr", "ones", + "identity", "allclose", "putmask", "flatnonzero", "inf", "nan", "False_", "True_", + "bitwise_not", "full", "full_like", "matmul", "vecdot", "vecmat", + "shares_memory", "may_share_memory", + "all", "amax", "amin", "any", "argmax", "argmin", "argpartition", "argsort", + "around", "choose", "clip", "compress", "cumprod", "cumsum", "cumulative_prod", + "cumulative_sum", "diagonal", "mean", "max", "min", "matrix_transpose", "ndim", + "nonzero", "partition", "prod", "ptp", "put", "ravel", "repeat", "reshape", + "resize", "round", "searchsorted", "shape", "size", "sort", "squeeze", "std", "sum", + "swapaxes", "take", "trace", "transpose", "var", + "absolute", "add", "arccos", "arccosh", "arcsin", "arcsinh", "arctan", "arctan2", + "arctanh", "bitwise_and", "bitwise_or", "bitwise_xor", "cbrt", "ceil", "conj", + "conjugate", "copysign", "cos", "cosh", "bitwise_count", "deg2rad", "degrees", + "divide", "divmod", "e", "equal", "euler_gamma", "exp", "exp2", "expm1", "fabs", + "floor", "floor_divide", "float_power", "fmax", "fmin", "fmod", "frexp", + "frompyfunc", "gcd", "greater", "greater_equal", "heaviside", "hypot", "invert", + "isfinite", "isinf", "isnan", "isnat", "lcm", "ldexp", "left_shift", "less", + "less_equal", "log", "log10", "log1p", "log2", "logaddexp", "logaddexp2", + "logical_and", "logical_not", "logical_or", "logical_xor", "matvec", "maximum", "minimum", + "mod", "modf", "multiply", "negative", "nextafter", "not_equal", "pi", "positive", + "power", "rad2deg", "radians", "reciprocal", "remainder", "right_shift", "rint", + "sign", "signbit", "sin", "sinh", "spacing", "sqrt", "square", "subtract", "tan", + "tanh", "true_divide", "trunc", "ScalarType", "typecodes", "issubdtype", + "datetime_data", "datetime_as_string", "busday_offset", "busday_count", "is_busday", + "busdaycalendar", "isdtype", + "complexfloating", "character", "unsignedinteger", "inexact", "generic", "floating", + "integer", "signedinteger", "number", "flexible", "bool", "float16", "float32", + "float64", "longdouble", "complex64", "complex128", "clongdouble", + "bytes_", "str_", "void", "object_", "datetime64", "timedelta64", "int8", "byte", + "uint8", "ubyte", "int16", "short", "uint16", "ushort", "int32", "intc", "uint32", + "uintc", "int64", "long", "uint64", "ulong", "longlong", "ulonglong", "intp", + "uintp", "double", "cdouble", "single", "csingle", "half", "bool_", "int_", "uint", + "float96", "float128", "complex192", "complex256", + "array2string", "array_str", "array_repr", "set_printoptions", "get_printoptions", + "printoptions", "format_float_positional", "format_float_scientific", "require", + "seterr", "geterr", "setbufsize", "getbufsize", "seterrcall", "geterrcall", + "errstate", + # _core.function_base.__all__ + "logspace", "linspace", "geomspace", + # _core.getlimits.__all__ + "finfo", "iinfo", + # _core.shape_base.__all__ + "atleast_1d", "atleast_2d", "atleast_3d", "block", "hstack", "stack", "unstack", + "vstack", + # _core.einsumfunc.__all__ + "einsum", "einsum_path", + # matrixlib.__all__ + "matrix", "bmat", "asmatrix", + # lib._histograms_impl.__all__ + "histogram", "histogramdd", "histogram_bin_edges", + # lib._nanfunctions_impl.__all__ + "nansum", "nanmax", "nanmin", "nanargmax", "nanargmin", "nanmean", "nanmedian", + "nanpercentile", "nanvar", "nanstd", "nanprod", "nancumsum", "nancumprod", + "nanquantile", + # lib._function_base_impl.__all__ + "select", "piecewise", "trim_zeros", "copy", "iterable", "percentile", "diff", + "gradient", "angle", "unwrap", "sort_complex", "flip", "rot90", "extract", "place", + "vectorize", "asarray_chkfinite", "average", "bincount", "digitize", "cov", + "corrcoef", "median", "sinc", "hamming", "hanning", "bartlett", "blackman", + "kaiser", "trapezoid", "trapz", "i0", "meshgrid", "delete", "insert", "append", + "interp", "quantile", + # lib._twodim_base_impl.__all__ + "diag", "diagflat", "eye", "fliplr", "flipud", "tri", "triu", "tril", "vander", + "histogram2d", "mask_indices", "tril_indices", "tril_indices_from", "triu_indices", + "triu_indices_from", + # lib._shape_base_impl.__all__ + "column_stack", "dstack", "array_split", "split", "hsplit", "vsplit", "dsplit", + "apply_over_axes", "expand_dims", "apply_along_axis", "kron", "tile", + "take_along_axis", "put_along_axis", "row_stack", + # lib._type_check_impl.__all__ + "iscomplexobj", "isrealobj", "imag", "iscomplex", "isreal", "nan_to_num", "real", + "real_if_close", "typename", "mintypecode", "common_type", + # lib._arraysetops_impl.__all__ + "ediff1d", "in1d", "intersect1d", "isin", "setdiff1d", "setxor1d", "union1d", + "unique", "unique_all", "unique_counts", "unique_inverse", "unique_values", + # lib._ufunclike_impl.__all__ + "fix", "isneginf", "isposinf", + # lib._arraypad_impl.__all__ + "pad", + # lib._utils_impl.__all__ + "get_include", "info", "show_runtime", + # lib._stride_tricks_impl.__all__ + "broadcast_to", "broadcast_arrays", "broadcast_shapes", + # lib._polynomial_impl.__all__ + "poly", "roots", "polyint", "polyder", "polyadd", "polysub", "polymul", "polydiv", + "polyval", "poly1d", "polyfit", + # lib._npyio_impl.__all__ + "savetxt", "loadtxt", "genfromtxt", "load", "save", "savez", "savez_compressed", + "packbits", "unpackbits", "fromregex", + # lib._index_tricks_impl.__all__ + "ravel_multi_index", "unravel_index", "mgrid", "ogrid", "r_", "c_", "s_", + "index_exp", "ix_", "ndenumerate", "ndindex", "fill_diagonal", "diag_indices", + "diag_indices_from", + + # __init__.__all__ + "emath", "show_config", "__version__", "__array_namespace_info__", +] # fmt: skip + +### Constrained types (for internal use only) +# Only use these for functions; never as generic type parameter. + +_AnyStr = TypeVar("_AnyStr", LiteralString, str, bytes) +_AnyShapeT = TypeVar( + "_AnyShapeT", + tuple[()], # 0-d + tuple[int], # 1-d + tuple[int, int], # 2-d + tuple[int, int, int], # 3-d + tuple[int, int, int, int], # 4-d + tuple[int, int, int, int, int], # 5-d + tuple[int, int, int, int, int, int], # 6-d + tuple[int, int, int, int, int, int, int], # 7-d + tuple[int, int, int, int, int, int, int, int], # 8-d + tuple[int, ...], # N-d +) +_AnyTD64Item = TypeVar("_AnyTD64Item", dt.timedelta, int, None, dt.timedelta | int | None) +_AnyDT64Arg = TypeVar("_AnyDT64Arg", dt.datetime, dt.date, None) +_AnyDT64Item = TypeVar("_AnyDT64Item", dt.datetime, dt.date, int, None, dt.date, int | None) +_AnyDate = TypeVar("_AnyDate", dt.date, dt.datetime) +_AnyDateOrTime = TypeVar("_AnyDateOrTime", dt.date, dt.datetime, dt.timedelta) + +### Type parameters (for internal use only) + +_T = TypeVar("_T") +_T_co = TypeVar("_T_co", covariant=True) +_T_contra = TypeVar("_T_contra", contravariant=True) +_RealT_co = TypeVar("_RealT_co", covariant=True) +_ImagT_co = TypeVar("_ImagT_co", covariant=True) + +_CallableT = TypeVar("_CallableT", bound=Callable[..., object]) + +_DTypeT = TypeVar("_DTypeT", bound=dtype) +_DTypeT_co = TypeVar("_DTypeT_co", bound=dtype, default=dtype, covariant=True) +_FlexDTypeT = TypeVar("_FlexDTypeT", bound=dtype[flexible]) + +_ArrayT = TypeVar("_ArrayT", bound=ndarray) +_ArrayT_co = TypeVar("_ArrayT_co", bound=ndarray, default=ndarray, covariant=True) +_IntegralArrayT = TypeVar("_IntegralArrayT", bound=NDArray[integer | np.bool | object_]) +_RealArrayT = TypeVar("_RealArrayT", bound=NDArray[floating | integer | timedelta64 | np.bool | object_]) +_NumericArrayT = TypeVar("_NumericArrayT", bound=NDArray[number | timedelta64 | object_]) + +_ShapeT = TypeVar("_ShapeT", bound=_Shape) +_ShapeT_co = TypeVar("_ShapeT_co", bound=_Shape, default=_AnyShape, covariant=True) +_1DShapeT = TypeVar("_1DShapeT", bound=_1D) +_2DShapeT_co = TypeVar("_2DShapeT_co", bound=_2D, default=_2D, covariant=True) +_1NShapeT = TypeVar("_1NShapeT", bound=tuple[L[1], *tuple[L[1], ...]]) # (1,) | (1, 1) | (1, 1, 1) | ... + +_ScalarT = TypeVar("_ScalarT", bound=generic) +_ScalarT_co = TypeVar("_ScalarT_co", bound=generic, default=Any, covariant=True) +_NumberT = TypeVar("_NumberT", bound=number) +_InexactT = TypeVar("_InexactT", bound=inexact) +_RealNumberT = TypeVar("_RealNumberT", bound=floating | integer) +_FloatingT_co = TypeVar("_FloatingT_co", bound=floating, default=floating, covariant=True) +_IntegerT = TypeVar("_IntegerT", bound=integer) +_IntegerT_co = TypeVar("_IntegerT_co", bound=integer, default=integer, covariant=True) +_NonObjectScalarT = TypeVar("_NonObjectScalarT", bound=np.bool | number | flexible | datetime64 | timedelta64) + +_NBit = TypeVar("_NBit", bound=NBitBase, default=Any) # pyright: ignore[reportDeprecated] +_NBit1 = TypeVar("_NBit1", bound=NBitBase, default=Any) # pyright: ignore[reportDeprecated] +_NBit2 = TypeVar("_NBit2", bound=NBitBase, default=_NBit1) # pyright: ignore[reportDeprecated] + +_ItemT_co = TypeVar("_ItemT_co", default=Any, covariant=True) +_BoolItemT = TypeVar("_BoolItemT", bound=builtins.bool) +_BoolItemT_co = TypeVar("_BoolItemT_co", bound=builtins.bool, default=builtins.bool, covariant=True) +_NumberItemT_co = TypeVar("_NumberItemT_co", bound=complex, default=int | float | complex, covariant=True) +_InexactItemT_co = TypeVar("_InexactItemT_co", bound=complex, default=float | complex, covariant=True) +_FlexibleItemT_co = TypeVar( + "_FlexibleItemT_co", + bound=_CharLike_co | tuple[Any, ...], + default=_CharLike_co | tuple[Any, ...], + covariant=True, +) +_CharacterItemT_co = TypeVar("_CharacterItemT_co", bound=_CharLike_co, default=_CharLike_co, covariant=True) +_TD64ItemT_co = TypeVar("_TD64ItemT_co", bound=dt.timedelta | int | None, default=dt.timedelta | int | None, covariant=True) +_DT64ItemT_co = TypeVar("_DT64ItemT_co", bound=dt.date | int | None, default=dt.date | int | None, covariant=True) +_TD64UnitT = TypeVar("_TD64UnitT", bound=_TD64Unit, default=_TD64Unit) +_BoolOrIntArrayT = TypeVar("_BoolOrIntArrayT", bound=NDArray[integer | np.bool]) + +### Type Aliases (for internal use only) + +_Falsy: TypeAlias = L[False, 0] | np.bool[L[False]] +_Truthy: TypeAlias = L[True, 1] | np.bool[L[True]] + +_1D: TypeAlias = tuple[int] +_2D: TypeAlias = tuple[int, int] +_2Tuple: TypeAlias = tuple[_T, _T] + +_ArrayUInt_co: TypeAlias = NDArray[unsignedinteger | np.bool] +_ArrayInt_co: TypeAlias = NDArray[integer | np.bool] +_ArrayFloat64_co: TypeAlias = NDArray[floating[_64Bit] | float32 | float16 | integer | np.bool] +_ArrayFloat_co: TypeAlias = NDArray[floating | integer | np.bool] +_ArrayComplex128_co: TypeAlias = NDArray[number[_64Bit] | number[_32Bit] | float16 | integer | np.bool] +_ArrayComplex_co: TypeAlias = NDArray[inexact | integer | np.bool] +_ArrayNumber_co: TypeAlias = NDArray[number | np.bool] +_ArrayTD64_co: TypeAlias = NDArray[timedelta64 | integer | np.bool] + +_Float64_co: TypeAlias = float | floating[_64Bit] | float32 | float16 | integer | np.bool +_Complex64_co: TypeAlias = number[_32Bit] | number[_16Bit] | number[_8Bit] | builtins.bool | np.bool +_Complex128_co: TypeAlias = complex | number[_64Bit] | _Complex64_co + +_ToIndex: TypeAlias = SupportsIndex | slice | EllipsisType | _ArrayLikeInt_co | None +_ToIndices: TypeAlias = _ToIndex | tuple[_ToIndex, ...] + +_UnsignedIntegerCType: TypeAlias = type[ + ct.c_uint8 | ct.c_uint16 | ct.c_uint32 | ct.c_uint64 + | ct.c_ushort | ct.c_uint | ct.c_ulong | ct.c_ulonglong + | ct.c_size_t | ct.c_void_p +] # fmt: skip +_SignedIntegerCType: TypeAlias = type[ + ct.c_int8 | ct.c_int16 | ct.c_int32 | ct.c_int64 + | ct.c_short | ct.c_int | ct.c_long | ct.c_longlong + | ct.c_ssize_t +] # fmt: skip +_FloatingCType: TypeAlias = type[ct.c_float | ct.c_double | ct.c_longdouble] +_IntegerCType: TypeAlias = _UnsignedIntegerCType | _SignedIntegerCType +_NumberCType: TypeAlias = _IntegerCType +_GenericCType: TypeAlias = _NumberCType | type[ct.c_bool | ct.c_char | ct.py_object[Any]] + +# some commonly used builtin types that are known to result in a +# `dtype[object_]`, when their *type* is passed to the `dtype` constructor +# NOTE: `builtins.object` should not be included here +_BuiltinObjectLike: TypeAlias = ( + slice | Decimal | Fraction | UUID + | dt.date | dt.time | dt.timedelta | dt.tzinfo + | tuple[Any, ...] | list[Any] | set[Any] | frozenset[Any] | dict[Any, Any] +) # fmt: skip + +# Introduce an alias for `dtype` to avoid naming conflicts. +_dtype: TypeAlias = dtype[_ScalarT] + +_ByteOrderChar: TypeAlias = L["<", ">", "=", "|"] +# can be anything, is case-insensitive, and only the first character matters +_ByteOrder: TypeAlias = L[ + "S", # swap the current order (default) + "<", "L", "little", # little-endian + ">", "B", "big", # big endian + "=", "N", "native", # native order + "|", "I", # ignore +] # fmt: skip +_DTypeKind: TypeAlias = L[ + "b", # boolean + "i", # signed integer + "u", # unsigned integer + "f", # floating-point + "c", # complex floating-point + "m", # timedelta64 + "M", # datetime64 + "O", # python object + "S", # byte-string (fixed-width) + "U", # unicode-string (fixed-width) + "V", # void + "T", # unicode-string (variable-width) +] +_DTypeChar: TypeAlias = L[ + "?", # bool + "b", # byte + "B", # ubyte + "h", # short + "H", # ushort + "i", # intc + "I", # uintc + "l", # long + "L", # ulong + "q", # longlong + "Q", # ulonglong + "e", # half + "f", # single + "d", # double + "g", # longdouble + "F", # csingle + "D", # cdouble + "G", # clongdouble + "O", # object + "S", # bytes_ (S0) + "a", # bytes_ (deprecated) + "U", # str_ + "V", # void + "M", # datetime64 + "m", # timedelta64 + "c", # bytes_ (S1) + "T", # StringDType +] +_DTypeNum: TypeAlias = L[ + 0, # bool + 1, # byte + 2, # ubyte + 3, # short + 4, # ushort + 5, # intc + 6, # uintc + 7, # long + 8, # ulong + 9, # longlong + 10, # ulonglong + 23, # half + 11, # single + 12, # double + 13, # longdouble + 14, # csingle + 15, # cdouble + 16, # clongdouble + 17, # object + 18, # bytes_ + 19, # str_ + 20, # void + 21, # datetime64 + 22, # timedelta64 + 25, # no type + 256, # user-defined + 2056, # StringDType +] +_DTypeBuiltinKind: TypeAlias = L[0, 1, 2] + +_ArrayAPIVersion: TypeAlias = L["2021.12", "2022.12", "2023.12", "2024.12"] + +_CastingKind: TypeAlias = L["no", "equiv", "safe", "same_kind", "unsafe"] + +_OrderKACF: TypeAlias = L["K", "A", "C", "F"] | None +_OrderACF: TypeAlias = L["A", "C", "F"] | None +_OrderCF: TypeAlias = L["C", "F"] | None + +_ModeKind: TypeAlias = L["raise", "wrap", "clip"] +_PartitionKind: TypeAlias = L["introselect"] +# in practice, only the first case-insensitive character is considered (so e.g. +# "QuantumSort3000" will be interpreted as quicksort). +_SortKind: TypeAlias = L[ + "Q", "quick", "quicksort", + "M", "merge", "mergesort", + "H", "heap", "heapsort", + "S", "stable", "stablesort", +] +_SortSide: TypeAlias = L["left", "right"] + +_ConvertibleToInt: TypeAlias = SupportsInt | SupportsIndex | _CharLike_co +_ConvertibleToFloat: TypeAlias = SupportsFloat | SupportsIndex | _CharLike_co +_ConvertibleToComplex: TypeAlias = SupportsComplex | SupportsFloat | SupportsIndex | _CharLike_co +_ConvertibleToTD64: TypeAlias = dt.timedelta | int | _CharLike_co | character | number | timedelta64 | np.bool | None +_ConvertibleToDT64: TypeAlias = dt.date | int | _CharLike_co | character | number | datetime64 | np.bool | None + +_NDIterFlagsKind: TypeAlias = L[ + "buffered", + "c_index", + "copy_if_overlap", + "common_dtype", + "delay_bufalloc", + "external_loop", + "f_index", + "grow_inner", "growinner", + "multi_index", + "ranged", + "refs_ok", + "reduce_ok", + "zerosize_ok", +] +_NDIterFlagsOp: TypeAlias = L[ + "aligned", + "allocate", + "arraymask", + "copy", + "config", + "nbo", + "no_subtype", + "no_broadcast", + "overlap_assume_elementwise", + "readonly", + "readwrite", + "updateifcopy", + "virtual", + "writeonly", + "writemasked" +] + +_MemMapModeKind: TypeAlias = L[ + "readonly", "r", + "copyonwrite", "c", + "readwrite", "r+", + "write", "w+", +] + +_DT64Date: TypeAlias = _HasDateAttributes | L["TODAY", "today", b"TODAY", b"today"] +_DT64Now: TypeAlias = L["NOW", "now", b"NOW", b"now"] +_NaTValue: TypeAlias = L["NAT", "NaT", "nat", b"NAT", b"NaT", b"nat"] + +_MonthUnit: TypeAlias = L["Y", "M", b"Y", b"M"] +_DayUnit: TypeAlias = L["W", "D", b"W", b"D"] +_DateUnit: TypeAlias = L[_MonthUnit, _DayUnit] +_NativeTimeUnit: TypeAlias = L["h", "m", "s", "ms", "us", "μs", b"h", b"m", b"s", b"ms", b"us"] +_IntTimeUnit: TypeAlias = L["ns", "ps", "fs", "as", b"ns", b"ps", b"fs", b"as"] +_TimeUnit: TypeAlias = L[_NativeTimeUnit, _IntTimeUnit] +_NativeTD64Unit: TypeAlias = L[_DayUnit, _NativeTimeUnit] +_IntTD64Unit: TypeAlias = L[_MonthUnit, _IntTimeUnit] +_TD64Unit: TypeAlias = L[_DateUnit, _TimeUnit] +_TimeUnitSpec: TypeAlias = _TD64UnitT | tuple[_TD64UnitT, SupportsIndex] + +### TypedDict's (for internal use only) + +@type_check_only +class _FormerAttrsDict(TypedDict): + object: LiteralString + float: LiteralString + complex: LiteralString + str: LiteralString + int: LiteralString + +### Protocols (for internal use only) + +@type_check_only +class _SupportsFileMethods(SupportsFlush, Protocol): + # Protocol for representing file-like-objects accepted by `ndarray.tofile` and `fromfile` + def fileno(self) -> SupportsIndex: ... + def tell(self) -> SupportsIndex: ... + def seek(self, offset: int, whence: int, /) -> object: ... + +@type_check_only +class _SupportsFileMethodsRW(SupportsWrite[bytes], _SupportsFileMethods, Protocol): ... + +@type_check_only +class _SupportsItem(Protocol[_T_co]): + def item(self, /) -> _T_co: ... + +@type_check_only +class _SupportsDLPack(Protocol[_T_contra]): + def __dlpack__(self, /, *, stream: _T_contra | None = None) -> CapsuleType: ... + +@type_check_only +class _HasDType(Protocol[_T_co]): + @property + def dtype(self, /) -> _T_co: ... + +@type_check_only +class _HasRealAndImag(Protocol[_RealT_co, _ImagT_co]): + @property + def real(self, /) -> _RealT_co: ... + @property + def imag(self, /) -> _ImagT_co: ... + +@type_check_only +class _HasTypeWithRealAndImag(Protocol[_RealT_co, _ImagT_co]): + @property + def type(self, /) -> type[_HasRealAndImag[_RealT_co, _ImagT_co]]: ... + +@type_check_only +class _HasDTypeWithRealAndImag(Protocol[_RealT_co, _ImagT_co]): + @property + def dtype(self, /) -> _HasTypeWithRealAndImag[_RealT_co, _ImagT_co]: ... + +@type_check_only +class _HasDateAttributes(Protocol): + # The `datetime64` constructors requires an object with the three attributes below, + # and thus supports datetime duck typing + @property + def day(self) -> int: ... + @property + def month(self) -> int: ... + @property + def year(self) -> int: ... + +### Mixins (for internal use only) + +@type_check_only +class _RealMixin: + @property + def real(self) -> Self: ... + @property + def imag(self) -> Self: ... + +@type_check_only +class _RoundMixin: + @overload + def __round__(self, /, ndigits: None = None) -> int: ... + @overload + def __round__(self, /, ndigits: SupportsIndex) -> Self: ... + +@type_check_only +class _IntegralMixin(_RealMixin): + @property + def numerator(self) -> Self: ... + @property + def denominator(self) -> L[1]: ... + + def is_integer(self, /) -> L[True]: ... + +### Public API + +__version__: Final[LiteralString] = ... + +e: Final[float] = ... +euler_gamma: Final[float] = ... +pi: Final[float] = ... +inf: Final[float] = ... +nan: Final[float] = ... +little_endian: Final[builtins.bool] = ... +False_: Final[np.bool[L[False]]] = ... +True_: Final[np.bool[L[True]]] = ... +newaxis: Final[None] = None + +# not in __all__ +__NUMPY_SETUP__: Final[L[False]] = False +__numpy_submodules__: Final[set[LiteralString]] = ... +__former_attrs__: Final[_FormerAttrsDict] = ... +__future_scalars__: Final[set[L["bytes", "str", "object"]]] = ... +__array_api_version__: Final[L["2024.12"]] = "2024.12" +test: Final[PytestTester] = ... + +@type_check_only +class _DTypeMeta(type): + @property + def type(cls, /) -> type[generic] | None: ... + @property + def _abstract(cls, /) -> bool: ... + @property + def _is_numeric(cls, /) -> bool: ... + @property + def _parametric(cls, /) -> bool: ... + @property + def _legacy(cls, /) -> bool: ... + +@final +class dtype(Generic[_ScalarT_co], metaclass=_DTypeMeta): + names: tuple[builtins.str, ...] | None + def __hash__(self) -> int: ... + + # `None` results in the default dtype + @overload + def __new__( + cls, + dtype: type[float64] | None, + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[builtins.str, Any] = ... + ) -> dtype[float64]: ... + + # Overload for `dtype` instances, scalar types, and instances that have a + # `dtype: dtype[_ScalarT]` attribute + @overload + def __new__( + cls, + dtype: _DTypeLike[_ScalarT], + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[builtins.str, Any] = ..., + ) -> dtype[_ScalarT]: ... + + # Builtin types + # + # NOTE: Typecheckers act as if `bool <: int <: float <: complex <: object`, + # even though at runtime `int`, `float`, and `complex` aren't subtypes.. + # This makes it impossible to express e.g. "a float that isn't an int", + # since type checkers treat `_: float` like `_: float | int`. + # + # For more details, see: + # - https://github.com/numpy/numpy/issues/27032#issuecomment-2278958251 + # - https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex + @overload + def __new__( + cls, + dtype: type[builtins.bool | np.bool], + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[str, Any] = ..., + ) -> dtype[np.bool]: ... + # NOTE: `_: type[int]` also accepts `type[int | bool]` + @overload + def __new__( + cls, + dtype: type[int | int_ | np.bool], + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[str, Any] = ..., + ) -> dtype[int_ | np.bool]: ... + # NOTE: `_: type[float]` also accepts `type[float | int | bool]` + # NOTE: `float64` inherits from `float` at runtime; but this isn't + # reflected in these stubs. So an explicit `float64` is required here. + @overload + def __new__( + cls, + dtype: type[float | float64 | int_ | np.bool] | None, + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[str, Any] = ..., + ) -> dtype[float64 | int_ | np.bool]: ... + # NOTE: `_: type[complex]` also accepts `type[complex | float | int | bool]` + @overload + def __new__( + cls, + dtype: type[complex | complex128 | float64 | int_ | np.bool], + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[str, Any] = ..., + ) -> dtype[complex128 | float64 | int_ | np.bool]: ... + @overload + def __new__( + cls, + dtype: type[bytes], # also includes `type[bytes_]` + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[str, Any] = ..., + ) -> dtype[bytes_]: ... + @overload + def __new__( + cls, + dtype: type[str], # also includes `type[str_]` + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[str, Any] = ..., + ) -> dtype[str_]: ... + # NOTE: These `memoryview` overloads assume PEP 688, which requires mypy to + # be run with the (undocumented) `--disable-memoryview-promotion` flag, + # This will be the default in a future mypy release, see: + # https://github.com/python/mypy/issues/15313 + # Pyright / Pylance requires setting `disableBytesTypePromotions=true`, + # which is the default in strict mode + @overload + def __new__( + cls, + dtype: type[memoryview | void], + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[str, Any] = ..., + ) -> dtype[void]: ... + # NOTE: `_: type[object]` would also accept e.g. `type[object | complex]`, + # and is therefore not included here + @overload + def __new__( + cls, + dtype: type[_BuiltinObjectLike | object_], + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[str, Any] = ..., + ) -> dtype[object_]: ... + + # Unions of builtins. + @overload + def __new__( + cls, + dtype: type[bytes | str], + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[str, Any] = ..., + ) -> dtype[character]: ... + @overload + def __new__( + cls, + dtype: type[bytes | str | memoryview], + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[str, Any] = ..., + ) -> dtype[flexible]: ... + @overload + def __new__( + cls, + dtype: type[complex | bytes | str | memoryview | _BuiltinObjectLike], + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[str, Any] = ..., + ) -> dtype[np.bool | int_ | float64 | complex128 | flexible | object_]: ... + + # `unsignedinteger` string-based representations and ctypes + @overload + def __new__(cls, dtype: _UInt8Codes | type[ct.c_uint8], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[uint8]: ... + @overload + def __new__(cls, dtype: _UInt16Codes | type[ct.c_uint16], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[uint16]: ... + @overload + def __new__(cls, dtype: _UInt32Codes | type[ct.c_uint32], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[uint32]: ... + @overload + def __new__(cls, dtype: _UInt64Codes | type[ct.c_uint64], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[uint64]: ... + @overload + def __new__(cls, dtype: _UByteCodes | type[ct.c_ubyte], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[ubyte]: ... + @overload + def __new__(cls, dtype: _UShortCodes | type[ct.c_ushort], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[ushort]: ... + @overload + def __new__(cls, dtype: _UIntCCodes | type[ct.c_uint], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[uintc]: ... + # NOTE: We're assuming here that `uint_ptr_t == size_t`, + # an assumption that does not hold in rare cases (same for `ssize_t`) + @overload + def __new__(cls, dtype: _UIntPCodes | type[ct.c_void_p] | type[ct.c_size_t], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[uintp]: ... + @overload + def __new__(cls, dtype: _ULongCodes | type[ct.c_ulong], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[ulong]: ... + @overload + def __new__(cls, dtype: _ULongLongCodes | type[ct.c_ulonglong], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[ulonglong]: ... + + # `signedinteger` string-based representations and ctypes + @overload + def __new__(cls, dtype: _Int8Codes | type[ct.c_int8], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[int8]: ... + @overload + def __new__(cls, dtype: _Int16Codes | type[ct.c_int16], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[int16]: ... + @overload + def __new__(cls, dtype: _Int32Codes | type[ct.c_int32], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[int32]: ... + @overload + def __new__(cls, dtype: _Int64Codes | type[ct.c_int64], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[int64]: ... + @overload + def __new__(cls, dtype: _ByteCodes | type[ct.c_byte], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[byte]: ... + @overload + def __new__(cls, dtype: _ShortCodes | type[ct.c_short], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[short]: ... + @overload + def __new__(cls, dtype: _IntCCodes | type[ct.c_int], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[intc]: ... + @overload + def __new__(cls, dtype: _IntPCodes | type[ct.c_ssize_t], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[intp]: ... + @overload + def __new__(cls, dtype: _LongCodes | type[ct.c_long], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[long]: ... + @overload + def __new__(cls, dtype: _LongLongCodes | type[ct.c_longlong], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[longlong]: ... + + # `floating` string-based representations and ctypes + @overload + def __new__(cls, dtype: _Float16Codes, align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[float16]: ... + @overload + def __new__(cls, dtype: _Float32Codes, align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[float32]: ... + @overload + def __new__(cls, dtype: _Float64Codes, align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[float64]: ... + @overload + def __new__(cls, dtype: _HalfCodes, align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[half]: ... + @overload + def __new__(cls, dtype: _SingleCodes | type[ct.c_float], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[single]: ... + @overload + def __new__(cls, dtype: _DoubleCodes | type[ct.c_double], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[double]: ... + @overload + def __new__(cls, dtype: _LongDoubleCodes | type[ct.c_longdouble], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[longdouble]: ... + + # `complexfloating` string-based representations + @overload + def __new__(cls, dtype: _Complex64Codes, align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[complex64]: ... + @overload + def __new__(cls, dtype: _Complex128Codes, align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[complex128]: ... + @overload + def __new__(cls, dtype: _CSingleCodes, align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[csingle]: ... + @overload + def __new__(cls, dtype: _CDoubleCodes, align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[cdouble]: ... + @overload + def __new__(cls, dtype: _CLongDoubleCodes, align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[clongdouble]: ... + + # Miscellaneous string-based representations and ctypes + @overload + def __new__(cls, dtype: _BoolCodes | type[ct.c_bool], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[np.bool]: ... + @overload + def __new__(cls, dtype: _TD64Codes, align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[timedelta64]: ... + @overload + def __new__(cls, dtype: _DT64Codes, align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[datetime64]: ... + @overload + def __new__(cls, dtype: _StrCodes, align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[str_]: ... + @overload + def __new__(cls, dtype: _BytesCodes | type[ct.c_char], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[bytes_]: ... + @overload + def __new__(cls, dtype: _VoidCodes | _VoidDTypeLike, align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[void]: ... + @overload + def __new__(cls, dtype: _ObjectCodes | type[ct.py_object[Any]], align: builtins.bool = ..., copy: builtins.bool = ..., metadata: dict[builtins.str, Any] = ...) -> dtype[object_]: ... + + # `StringDType` requires special treatment because it has no scalar type + @overload + def __new__( + cls, + dtype: dtypes.StringDType | _StringCodes, + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[builtins.str, Any] = ... + ) -> dtypes.StringDType: ... + + # Combined char-codes and ctypes, analogous to the scalar-type hierarchy + @overload + def __new__( + cls, + dtype: _UnsignedIntegerCodes | _UnsignedIntegerCType, + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[builtins.str, Any] = ..., + ) -> dtype[unsignedinteger]: ... + @overload + def __new__( + cls, + dtype: _SignedIntegerCodes | _SignedIntegerCType, + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[builtins.str, Any] = ..., + ) -> dtype[signedinteger]: ... + @overload + def __new__( + cls, + dtype: _IntegerCodes | _IntegerCType, + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[builtins.str, Any] = ..., + ) -> dtype[integer]: ... + @overload + def __new__( + cls, + dtype: _FloatingCodes | _FloatingCType, + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[builtins.str, Any] = ..., + ) -> dtype[floating]: ... + @overload + def __new__( + cls, + dtype: _ComplexFloatingCodes, + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[builtins.str, Any] = ..., + ) -> dtype[complexfloating]: ... + @overload + def __new__( + cls, + dtype: _InexactCodes | _FloatingCType, + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[builtins.str, Any] = ..., + ) -> dtype[inexact]: ... + @overload + def __new__( + cls, + dtype: _NumberCodes | _NumberCType, + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[builtins.str, Any] = ..., + ) -> dtype[number]: ... + @overload + def __new__( + cls, + dtype: _CharacterCodes | type[ct.c_char], + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[builtins.str, Any] = ..., + ) -> dtype[character]: ... + @overload + def __new__( + cls, + dtype: _FlexibleCodes | type[ct.c_char], + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[builtins.str, Any] = ..., + ) -> dtype[flexible]: ... + @overload + def __new__( + cls, + dtype: _GenericCodes | _GenericCType, + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[builtins.str, Any] = ..., + ) -> dtype[generic]: ... + + # Handle strings that can't be expressed as literals; i.e. "S1", "S2", ... + @overload + def __new__( + cls, + dtype: builtins.str, + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[builtins.str, Any] = ..., + ) -> dtype: ... + + # Catch-all overload for object-likes + # NOTE: `object_ | Any` is *not* equivalent to `Any` -- it describes some + # (static) type `T` s.t. `object_ <: T <: builtins.object` (`<:` denotes + # the subtyping relation, the (gradual) typing analogue of `issubclass()`). + # https://typing.readthedocs.io/en/latest/spec/concepts.html#union-types + @overload + def __new__( + cls, + dtype: type[object], + align: builtins.bool = ..., + copy: builtins.bool = ..., + metadata: dict[builtins.str, Any] = ..., + ) -> dtype[object_ | Any]: ... + + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + + @overload + def __getitem__(self: dtype[void], key: list[builtins.str], /) -> dtype[void]: ... + @overload + def __getitem__(self: dtype[void], key: builtins.str | SupportsIndex, /) -> dtype: ... + + # NOTE: In the future 1-based multiplications will also yield `flexible` dtypes + @overload + def __mul__(self: _DTypeT, value: L[1], /) -> _DTypeT: ... + @overload + def __mul__(self: _FlexDTypeT, value: SupportsIndex, /) -> _FlexDTypeT: ... + @overload + def __mul__(self, value: SupportsIndex, /) -> dtype[void]: ... + + # NOTE: `__rmul__` seems to be broken when used in combination with + # literals as of mypy 0.902. Set the return-type to `dtype` for + # now for non-flexible dtypes. + @overload + def __rmul__(self: _FlexDTypeT, value: SupportsIndex, /) -> _FlexDTypeT: ... + @overload + def __rmul__(self, value: SupportsIndex, /) -> dtype: ... + + def __gt__(self, other: DTypeLike, /) -> builtins.bool: ... + def __ge__(self, other: DTypeLike, /) -> builtins.bool: ... + def __lt__(self, other: DTypeLike, /) -> builtins.bool: ... + def __le__(self, other: DTypeLike, /) -> builtins.bool: ... + + # Explicitly defined `__eq__` and `__ne__` to get around mypy's + # `strict_equality` option; even though their signatures are + # identical to their `object`-based counterpart + def __eq__(self, other: Any, /) -> builtins.bool: ... + def __ne__(self, other: Any, /) -> builtins.bool: ... + + @property + def alignment(self) -> int: ... + @property + def base(self) -> dtype: ... + @property + def byteorder(self) -> _ByteOrderChar: ... + @property + def char(self) -> _DTypeChar: ... + @property + def descr(self) -> list[tuple[LiteralString, LiteralString] | tuple[LiteralString, LiteralString, _Shape]]: ... + @property + def fields(self,) -> MappingProxyType[LiteralString, tuple[dtype, int] | tuple[dtype, int, Any]] | None: ... + @property + def flags(self) -> int: ... + @property + def hasobject(self) -> builtins.bool: ... + @property + def isbuiltin(self) -> _DTypeBuiltinKind: ... + @property + def isnative(self) -> builtins.bool: ... + @property + def isalignedstruct(self) -> builtins.bool: ... + @property + def itemsize(self) -> int: ... + @property + def kind(self) -> _DTypeKind: ... + @property + def metadata(self) -> MappingProxyType[builtins.str, Any] | None: ... + @property + def name(self) -> LiteralString: ... + @property + def num(self) -> _DTypeNum: ... + @property + def shape(self) -> _AnyShape: ... + @property + def ndim(self) -> int: ... + @property + def subdtype(self) -> tuple[dtype, _AnyShape] | None: ... + def newbyteorder(self, new_order: _ByteOrder = ..., /) -> Self: ... + @property + def str(self) -> LiteralString: ... + @property + def type(self) -> type[_ScalarT_co]: ... + +@final +class flatiter(Generic[_ArrayT_co]): + __hash__: ClassVar[None] + @property + def base(self) -> _ArrayT_co: ... + @property + def coords(self) -> _Shape: ... + @property + def index(self) -> int: ... + def copy(self) -> _ArrayT_co: ... + def __iter__(self) -> Self: ... + def __next__(self: flatiter[NDArray[_ScalarT]]) -> _ScalarT: ... + def __len__(self) -> int: ... + @overload + def __getitem__( + self: flatiter[NDArray[_ScalarT]], + key: int | integer | tuple[int | integer], + ) -> _ScalarT: ... + @overload + def __getitem__( + self, + key: _ArrayLikeInt | slice | EllipsisType | tuple[_ArrayLikeInt | slice | EllipsisType], + ) -> _ArrayT_co: ... + # TODO: `__setitem__` operates via `unsafe` casting rules, and can + # thus accept any type accepted by the relevant underlying `np.generic` + # constructor. + # This means that `value` must in reality be a supertype of `npt.ArrayLike`. + def __setitem__( + self, + key: _ArrayLikeInt | slice | EllipsisType | tuple[_ArrayLikeInt | slice | EllipsisType], + value: Any, + ) -> None: ... + @overload + def __array__(self: flatiter[ndarray[_1DShapeT, _DTypeT]], dtype: None = ..., /) -> ndarray[_1DShapeT, _DTypeT]: ... + @overload + def __array__(self: flatiter[ndarray[_1DShapeT, Any]], dtype: _DTypeT, /) -> ndarray[_1DShapeT, _DTypeT]: ... + @overload + def __array__(self: flatiter[ndarray[Any, _DTypeT]], dtype: None = ..., /) -> ndarray[_AnyShape, _DTypeT]: ... + @overload + def __array__(self, dtype: _DTypeT, /) -> ndarray[_AnyShape, _DTypeT]: ... + +@type_check_only +class _ArrayOrScalarCommon: + @property + def real(self, /) -> Any: ... + @property + def imag(self, /) -> Any: ... + @property + def T(self) -> Self: ... + @property + def mT(self) -> Self: ... + @property + def data(self) -> memoryview: ... + @property + def flags(self) -> flagsobj: ... + @property + def itemsize(self) -> int: ... + @property + def nbytes(self) -> int: ... + @property + def device(self) -> L["cpu"]: ... + + def __bool__(self, /) -> builtins.bool: ... + def __int__(self, /) -> int: ... + def __float__(self, /) -> float: ... + def __copy__(self) -> Self: ... + def __deepcopy__(self, memo: dict[int, Any] | None, /) -> Self: ... + + # TODO: How to deal with the non-commutative nature of `==` and `!=`? + # xref numpy/numpy#17368 + def __eq__(self, other: Any, /) -> Any: ... + def __ne__(self, other: Any, /) -> Any: ... + + def copy(self, order: _OrderKACF = ...) -> Self: ... + def dump(self, file: StrOrBytesPath | SupportsWrite[bytes]) -> None: ... + def dumps(self) -> bytes: ... + def tobytes(self, order: _OrderKACF = ...) -> bytes: ... + def tofile(self, fid: StrOrBytesPath | _SupportsFileMethods, sep: str = ..., format: str = ...) -> None: ... + # generics and 0d arrays return builtin scalars + def tolist(self) -> Any: ... + def to_device(self, device: L["cpu"], /, *, stream: int | Any | None = ...) -> Self: ... + + @property + def __array_interface__(self) -> dict[str, Any]: ... + @property + def __array_priority__(self) -> float: ... + @property + def __array_struct__(self) -> CapsuleType: ... # builtins.PyCapsule + def __array_namespace__(self, /, *, api_version: _ArrayAPIVersion | None = None) -> ModuleType: ... + def __setstate__(self, state: tuple[ + SupportsIndex, # version + _ShapeLike, # Shape + _DTypeT_co, # DType + np.bool, # F-continuous + bytes | list[Any], # Data + ], /) -> None: ... + + def conj(self) -> Self: ... + def conjugate(self) -> Self: ... + + def argsort( + self, + axis: SupportsIndex | None = ..., + kind: _SortKind | None = ..., + order: str | Sequence[str] | None = ..., + *, + stable: bool | None = ..., + ) -> NDArray[Any]: ... + + @overload # axis=None (default), out=None (default), keepdims=False (default) + def argmax(self, /, axis: None = None, out: None = None, *, keepdims: L[False] = False) -> intp: ... + @overload # axis=index, out=None (default) + def argmax(self, /, axis: SupportsIndex, out: None = None, *, keepdims: builtins.bool = False) -> Any: ... + @overload # axis=index, out=ndarray + def argmax(self, /, axis: SupportsIndex | None, out: _BoolOrIntArrayT, *, keepdims: builtins.bool = False) -> _BoolOrIntArrayT: ... + @overload + def argmax(self, /, axis: SupportsIndex | None = None, *, out: _BoolOrIntArrayT, keepdims: builtins.bool = False) -> _BoolOrIntArrayT: ... + + @overload # axis=None (default), out=None (default), keepdims=False (default) + def argmin(self, /, axis: None = None, out: None = None, *, keepdims: L[False] = False) -> intp: ... + @overload # axis=index, out=None (default) + def argmin(self, /, axis: SupportsIndex, out: None = None, *, keepdims: builtins.bool = False) -> Any: ... + @overload # axis=index, out=ndarray + def argmin(self, /, axis: SupportsIndex | None, out: _BoolOrIntArrayT, *, keepdims: builtins.bool = False) -> _BoolOrIntArrayT: ... + @overload + def argmin(self, /, axis: SupportsIndex | None = None, *, out: _BoolOrIntArrayT, keepdims: builtins.bool = False) -> _BoolOrIntArrayT: ... + + @overload # out=None (default) + def round(self, /, decimals: SupportsIndex = 0, out: None = None) -> Self: ... + @overload # out=ndarray + def round(self, /, decimals: SupportsIndex, out: _ArrayT) -> _ArrayT: ... + @overload + def round(self, /, decimals: SupportsIndex = 0, *, out: _ArrayT) -> _ArrayT: ... + + @overload # out=None (default) + def choose(self, /, choices: ArrayLike, out: None = None, mode: _ModeKind = "raise") -> NDArray[Any]: ... + @overload # out=ndarray + def choose(self, /, choices: ArrayLike, out: _ArrayT, mode: _ModeKind = "raise") -> _ArrayT: ... + + # TODO: Annotate kwargs with an unpacked `TypedDict` + @overload # out: None (default) + def clip(self, /, min: ArrayLike, max: ArrayLike | None = None, out: None = None, **kwargs: Any) -> NDArray[Any]: ... + @overload + def clip(self, /, min: None, max: ArrayLike, out: None = None, **kwargs: Any) -> NDArray[Any]: ... + @overload + def clip(self, /, min: None = None, *, max: ArrayLike, out: None = None, **kwargs: Any) -> NDArray[Any]: ... + @overload # out: ndarray + def clip(self, /, min: ArrayLike, max: ArrayLike | None, out: _ArrayT, **kwargs: Any) -> _ArrayT: ... + @overload + def clip(self, /, min: ArrayLike, max: ArrayLike | None = None, *, out: _ArrayT, **kwargs: Any) -> _ArrayT: ... + @overload + def clip(self, /, min: None, max: ArrayLike, out: _ArrayT, **kwargs: Any) -> _ArrayT: ... + @overload + def clip(self, /, min: None = None, *, max: ArrayLike, out: _ArrayT, **kwargs: Any) -> _ArrayT: ... + + @overload + def compress(self, /, condition: _ArrayLikeInt_co, axis: SupportsIndex | None = None, out: None = None) -> NDArray[Any]: ... + @overload + def compress(self, /, condition: _ArrayLikeInt_co, axis: SupportsIndex | None, out: _ArrayT) -> _ArrayT: ... + @overload + def compress(self, /, condition: _ArrayLikeInt_co, axis: SupportsIndex | None = None, *, out: _ArrayT) -> _ArrayT: ... + + @overload # out: None (default) + def cumprod(self, /, axis: SupportsIndex | None = None, dtype: DTypeLike | None = None, out: None = None) -> NDArray[Any]: ... + @overload # out: ndarray + def cumprod(self, /, axis: SupportsIndex | None, dtype: DTypeLike | None, out: _ArrayT) -> _ArrayT: ... + @overload + def cumprod(self, /, axis: SupportsIndex | None = None, dtype: DTypeLike | None = None, *, out: _ArrayT) -> _ArrayT: ... + + @overload # out: None (default) + def cumsum(self, /, axis: SupportsIndex | None = None, dtype: DTypeLike | None = None, out: None = None) -> NDArray[Any]: ... + @overload # out: ndarray + def cumsum(self, /, axis: SupportsIndex | None, dtype: DTypeLike | None, out: _ArrayT) -> _ArrayT: ... + @overload + def cumsum(self, /, axis: SupportsIndex | None = None, dtype: DTypeLike | None = None, *, out: _ArrayT) -> _ArrayT: ... + + @overload + def max( + self, + /, + axis: _ShapeLike | None = None, + out: None = None, + keepdims: builtins.bool = False, + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = True, + ) -> Any: ... + @overload + def max( + self, + /, + axis: _ShapeLike | None, + out: _ArrayT, + keepdims: builtins.bool = False, + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = True, + ) -> _ArrayT: ... + @overload + def max( + self, + /, + axis: _ShapeLike | None = None, + *, + out: _ArrayT, + keepdims: builtins.bool = False, + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = True, + ) -> _ArrayT: ... + + @overload + def min( + self, + /, + axis: _ShapeLike | None = None, + out: None = None, + keepdims: builtins.bool = False, + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = True, + ) -> Any: ... + @overload + def min( + self, + /, + axis: _ShapeLike | None, + out: _ArrayT, + keepdims: builtins.bool = False, + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = True, + ) -> _ArrayT: ... + @overload + def min( + self, + /, + axis: _ShapeLike | None = None, + *, + out: _ArrayT, + keepdims: builtins.bool = False, + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = True, + ) -> _ArrayT: ... + + @overload + def sum( + self, + /, + axis: _ShapeLike | None = None, + dtype: DTypeLike | None = None, + out: None = None, + keepdims: builtins.bool = False, + initial: _NumberLike_co = 0, + where: _ArrayLikeBool_co = True, + ) -> Any: ... + @overload + def sum( + self, + /, + axis: _ShapeLike | None, + dtype: DTypeLike | None, + out: _ArrayT, + keepdims: builtins.bool = False, + initial: _NumberLike_co = 0, + where: _ArrayLikeBool_co = True, + ) -> _ArrayT: ... + @overload + def sum( + self, + /, + axis: _ShapeLike | None = None, + dtype: DTypeLike | None = None, + *, + out: _ArrayT, + keepdims: builtins.bool = False, + initial: _NumberLike_co = 0, + where: _ArrayLikeBool_co = True, + ) -> _ArrayT: ... + + @overload + def prod( + self, + /, + axis: _ShapeLike | None = None, + dtype: DTypeLike | None = None, + out: None = None, + keepdims: builtins.bool = False, + initial: _NumberLike_co = 1, + where: _ArrayLikeBool_co = True, + ) -> Any: ... + @overload + def prod( + self, + /, + axis: _ShapeLike | None, + dtype: DTypeLike | None, + out: _ArrayT, + keepdims: builtins.bool = False, + initial: _NumberLike_co = 1, + where: _ArrayLikeBool_co = True, + ) -> _ArrayT: ... + @overload + def prod( + self, + /, + axis: _ShapeLike | None = None, + dtype: DTypeLike | None = None, + *, + out: _ArrayT, + keepdims: builtins.bool = False, + initial: _NumberLike_co = 1, + where: _ArrayLikeBool_co = True, + ) -> _ArrayT: ... + + @overload + def mean( + self, + axis: _ShapeLike | None = None, + dtype: DTypeLike | None = None, + out: None = None, + keepdims: builtins.bool = False, + *, + where: _ArrayLikeBool_co = True, + ) -> Any: ... + @overload + def mean( + self, + /, + axis: _ShapeLike | None, + dtype: DTypeLike | None, + out: _ArrayT, + keepdims: builtins.bool = False, + *, + where: _ArrayLikeBool_co = True, + ) -> _ArrayT: ... + @overload + def mean( + self, + /, + axis: _ShapeLike | None = None, + dtype: DTypeLike | None = None, + *, + out: _ArrayT, + keepdims: builtins.bool = False, + where: _ArrayLikeBool_co = True, + ) -> _ArrayT: ... + + @overload + def std( + self, + axis: _ShapeLike | None = None, + dtype: DTypeLike | None = None, + out: None = None, + ddof: float = 0, + keepdims: builtins.bool = False, + *, + where: _ArrayLikeBool_co = True, + mean: _ArrayLikeNumber_co = ..., + correction: float = ..., + ) -> Any: ... + @overload + def std( + self, + axis: _ShapeLike | None, + dtype: DTypeLike | None, + out: _ArrayT, + ddof: float = 0, + keepdims: builtins.bool = False, + *, + where: _ArrayLikeBool_co = True, + mean: _ArrayLikeNumber_co = ..., + correction: float = ..., + ) -> _ArrayT: ... + @overload + def std( + self, + axis: _ShapeLike | None = None, + dtype: DTypeLike | None = None, + *, + out: _ArrayT, + ddof: float = 0, + keepdims: builtins.bool = False, + where: _ArrayLikeBool_co = True, + mean: _ArrayLikeNumber_co = ..., + correction: float = ..., + ) -> _ArrayT: ... + + @overload + def var( + self, + axis: _ShapeLike | None = None, + dtype: DTypeLike | None = None, + out: None = None, + ddof: float = 0, + keepdims: builtins.bool = False, + *, + where: _ArrayLikeBool_co = True, + mean: _ArrayLikeNumber_co = ..., + correction: float = ..., + ) -> Any: ... + @overload + def var( + self, + axis: _ShapeLike | None, + dtype: DTypeLike | None, + out: _ArrayT, + ddof: float = 0, + keepdims: builtins.bool = False, + *, + where: _ArrayLikeBool_co = True, + mean: _ArrayLikeNumber_co = ..., + correction: float = ..., + ) -> _ArrayT: ... + @overload + def var( + self, + axis: _ShapeLike | None = None, + dtype: DTypeLike | None = None, + *, + out: _ArrayT, + ddof: float = 0, + keepdims: builtins.bool = False, + where: _ArrayLikeBool_co = True, + mean: _ArrayLikeNumber_co = ..., + correction: float = ..., + ) -> _ArrayT: ... + +class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DTypeT_co]): + __hash__: ClassVar[None] # type: ignore[assignment] # pyright: ignore[reportIncompatibleMethodOverride] + @property + def base(self) -> NDArray[Any] | None: ... + @property + def ndim(self) -> int: ... + @property + def size(self) -> int: ... + @property + def real(self: _HasDTypeWithRealAndImag[_ScalarT, object], /) -> ndarray[_ShapeT_co, dtype[_ScalarT]]: ... + @real.setter + def real(self, value: ArrayLike, /) -> None: ... + @property + def imag(self: _HasDTypeWithRealAndImag[object, _ScalarT], /) -> ndarray[_ShapeT_co, dtype[_ScalarT]]: ... + @imag.setter + def imag(self, value: ArrayLike, /) -> None: ... + + def __new__( + cls, + shape: _ShapeLike, + dtype: DTypeLike = ..., + buffer: _SupportsBuffer | None = ..., + offset: SupportsIndex = ..., + strides: _ShapeLike | None = ..., + order: _OrderKACF = ..., + ) -> Self: ... + + if sys.version_info >= (3, 12): + def __buffer__(self, flags: int, /) -> memoryview: ... + + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + + @overload + def __array__(self, dtype: None = None, /, *, copy: builtins.bool | None = None) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __array__(self, dtype: _DTypeT, /, *, copy: builtins.bool | None = None) -> ndarray[_ShapeT_co, _DTypeT]: ... + + def __array_ufunc__( + self, + ufunc: ufunc, + method: L["__call__", "reduce", "reduceat", "accumulate", "outer", "at"], + *inputs: Any, + **kwargs: Any, + ) -> Any: ... + + def __array_function__( + self, + func: Callable[..., Any], + types: Iterable[type], + args: Iterable[Any], + kwargs: Mapping[str, Any], + ) -> Any: ... + + # NOTE: In practice any object is accepted by `obj`, but as `__array_finalize__` + # is a pseudo-abstract method the type has been narrowed down in order to + # grant subclasses a bit more flexibility + def __array_finalize__(self, obj: NDArray[Any] | None, /) -> None: ... + + def __array_wrap__( + self, + array: ndarray[_ShapeT, _DTypeT], + context: tuple[ufunc, tuple[Any, ...], int] | None = ..., + return_scalar: builtins.bool = ..., + /, + ) -> ndarray[_ShapeT, _DTypeT]: ... + + @overload + def __getitem__(self, key: _ArrayInt_co | tuple[_ArrayInt_co, ...], /) -> ndarray[_AnyShape, _DTypeT_co]: ... + @overload + def __getitem__(self, key: SupportsIndex | tuple[SupportsIndex, ...], /) -> Any: ... + @overload + def __getitem__(self, key: _ToIndices, /) -> ndarray[_AnyShape, _DTypeT_co]: ... + @overload + def __getitem__(self: NDArray[void], key: str, /) -> ndarray[_ShapeT_co, np.dtype]: ... + @overload + def __getitem__(self: NDArray[void], key: list[str], /) -> ndarray[_ShapeT_co, _dtype[void]]: ... + + @overload # flexible | object_ | bool + def __setitem__( + self: ndarray[Any, dtype[flexible | object_ | np.bool] | dtypes.StringDType], + key: _ToIndices, + value: object, + /, + ) -> None: ... + @overload # integer + def __setitem__( + self: NDArray[integer], + key: _ToIndices, + value: _ConvertibleToInt | _NestedSequence[_ConvertibleToInt] | _ArrayLikeInt_co, + /, + ) -> None: ... + @overload # floating + def __setitem__( + self: NDArray[floating], + key: _ToIndices, + value: _ConvertibleToFloat | _NestedSequence[_ConvertibleToFloat | None] | _ArrayLikeFloat_co | None, + /, + ) -> None: ... + @overload # complexfloating + def __setitem__( + self: NDArray[complexfloating], + key: _ToIndices, + value: _ConvertibleToComplex | _NestedSequence[_ConvertibleToComplex | None] | _ArrayLikeNumber_co | None, + /, + ) -> None: ... + @overload # timedelta64 + def __setitem__( + self: NDArray[timedelta64], + key: _ToIndices, + value: _ConvertibleToTD64 | _NestedSequence[_ConvertibleToTD64], + /, + ) -> None: ... + @overload # datetime64 + def __setitem__( + self: NDArray[datetime64], + key: _ToIndices, + value: _ConvertibleToDT64 | _NestedSequence[_ConvertibleToDT64], + /, + ) -> None: ... + @overload # void + def __setitem__(self: NDArray[void], key: str | list[str], value: object, /) -> None: ... + @overload # catch-all + def __setitem__(self, key: _ToIndices, value: ArrayLike, /) -> None: ... + + @property + def ctypes(self) -> _ctypes[int]: ... + @property + def shape(self) -> _ShapeT_co: ... + @shape.setter + def shape(self, value: _ShapeLike) -> None: ... + @property + def strides(self) -> _Shape: ... + @strides.setter + def strides(self, value: _ShapeLike) -> None: ... + def byteswap(self, inplace: builtins.bool = ...) -> Self: ... + def fill(self, value: Any, /) -> None: ... + @property + def flat(self) -> flatiter[Self]: ... + + @overload # use the same output type as that of the underlying `generic` + def item(self: NDArray[generic[_T]], i0: SupportsIndex | tuple[SupportsIndex, ...] = ..., /, *args: SupportsIndex) -> _T: ... + @overload # special casing for `StringDType`, which has no scalar type + def item( + self: ndarray[Any, dtypes.StringDType], + arg0: SupportsIndex | tuple[SupportsIndex, ...] = ..., + /, + *args: SupportsIndex, + ) -> str: ... + + @overload # this first overload prevents mypy from over-eagerly selecting `tuple[()]` in case of `_AnyShape` + def tolist(self: ndarray[tuple[Never], dtype[generic[_T]]], /) -> Any: ... + @overload + def tolist(self: ndarray[tuple[()], dtype[generic[_T]]], /) -> _T: ... + @overload + def tolist(self: ndarray[tuple[int], dtype[generic[_T]]], /) -> list[_T]: ... + @overload + def tolist(self: ndarray[tuple[int, int], dtype[generic[_T]]], /) -> list[list[_T]]: ... + @overload + def tolist(self: ndarray[tuple[int, int, int], dtype[generic[_T]]], /) -> list[list[list[_T]]]: ... + @overload + def tolist(self, /) -> Any: ... + + @overload + def resize(self, new_shape: _ShapeLike, /, *, refcheck: builtins.bool = ...) -> None: ... + @overload + def resize(self, /, *new_shape: SupportsIndex, refcheck: builtins.bool = ...) -> None: ... + + def setflags(self, write: builtins.bool = ..., align: builtins.bool = ..., uic: builtins.bool = ...) -> None: ... + + def squeeze( + self, + axis: SupportsIndex | tuple[SupportsIndex, ...] | None = ..., + ) -> ndarray[_AnyShape, _DTypeT_co]: ... + + def swapaxes( + self, + axis1: SupportsIndex, + axis2: SupportsIndex, + ) -> ndarray[_AnyShape, _DTypeT_co]: ... + + @overload + def transpose(self, axes: _ShapeLike | None, /) -> Self: ... + @overload + def transpose(self, *axes: SupportsIndex) -> Self: ... + + @overload + def all( + self, + axis: None = None, + out: None = None, + keepdims: L[False, 0] = False, + *, + where: _ArrayLikeBool_co = True + ) -> np.bool: ... + @overload + def all( + self, + axis: int | tuple[int, ...] | None = None, + out: None = None, + keepdims: SupportsIndex = False, + *, + where: _ArrayLikeBool_co = True, + ) -> np.bool | NDArray[np.bool]: ... + @overload + def all( + self, + axis: int | tuple[int, ...] | None, + out: _ArrayT, + keepdims: SupportsIndex = False, + *, + where: _ArrayLikeBool_co = True, + ) -> _ArrayT: ... + @overload + def all( + self, + axis: int | tuple[int, ...] | None = None, + *, + out: _ArrayT, + keepdims: SupportsIndex = False, + where: _ArrayLikeBool_co = True, + ) -> _ArrayT: ... + + @overload + def any( + self, + axis: None = None, + out: None = None, + keepdims: L[False, 0] = False, + *, + where: _ArrayLikeBool_co = True + ) -> np.bool: ... + @overload + def any( + self, + axis: int | tuple[int, ...] | None = None, + out: None = None, + keepdims: SupportsIndex = False, + *, + where: _ArrayLikeBool_co = True, + ) -> np.bool | NDArray[np.bool]: ... + @overload + def any( + self, + axis: int | tuple[int, ...] | None, + out: _ArrayT, + keepdims: SupportsIndex = False, + *, + where: _ArrayLikeBool_co = True, + ) -> _ArrayT: ... + @overload + def any( + self, + axis: int | tuple[int, ...] | None = None, + *, + out: _ArrayT, + keepdims: SupportsIndex = False, + where: _ArrayLikeBool_co = True, + ) -> _ArrayT: ... + + # + @overload + def partition( + self, + /, + kth: _ArrayLikeInt, + axis: SupportsIndex = -1, + kind: _PartitionKind = "introselect", + order: None = None, + ) -> None: ... + @overload + def partition( + self: NDArray[void], + /, + kth: _ArrayLikeInt, + axis: SupportsIndex = -1, + kind: _PartitionKind = "introselect", + order: str | Sequence[str] | None = None, + ) -> None: ... + + # + @overload + def argpartition( + self, + /, + kth: _ArrayLikeInt, + axis: SupportsIndex | None = -1, + kind: _PartitionKind = "introselect", + order: None = None, + ) -> NDArray[intp]: ... + @overload + def argpartition( + self: NDArray[void], + /, + kth: _ArrayLikeInt, + axis: SupportsIndex | None = -1, + kind: _PartitionKind = "introselect", + order: str | Sequence[str] | None = None, + ) -> NDArray[intp]: ... + + # + def diagonal( + self, + offset: SupportsIndex = ..., + axis1: SupportsIndex = ..., + axis2: SupportsIndex = ..., + ) -> ndarray[_AnyShape, _DTypeT_co]: ... + + # 1D + 1D returns a scalar; + # all other with at least 1 non-0D array return an ndarray. + @overload + def dot(self, b: _ScalarLike_co, out: None = ...) -> NDArray[Any]: ... + @overload + def dot(self, b: ArrayLike, out: None = ...) -> Any: ... # type: ignore[misc] + @overload + def dot(self, b: ArrayLike, out: _ArrayT) -> _ArrayT: ... + + # `nonzero()` is deprecated for 0d arrays/generics + def nonzero(self) -> tuple[NDArray[intp], ...]: ... + + # `put` is technically available to `generic`, + # but is pointless as `generic`s are immutable + def put(self, /, indices: _ArrayLikeInt_co, values: ArrayLike, mode: _ModeKind = "raise") -> None: ... + + @overload + def searchsorted( # type: ignore[misc] + self, # >= 1D array + v: _ScalarLike_co, # 0D array-like + side: _SortSide = ..., + sorter: _ArrayLikeInt_co | None = ..., + ) -> intp: ... + @overload + def searchsorted( + self, # >= 1D array + v: ArrayLike, + side: _SortSide = ..., + sorter: _ArrayLikeInt_co | None = ..., + ) -> NDArray[intp]: ... + + def sort( + self, + axis: SupportsIndex = ..., + kind: _SortKind | None = ..., + order: str | Sequence[str] | None = ..., + *, + stable: bool | None = ..., + ) -> None: ... + + @overload + def trace( + self, # >= 2D array + offset: SupportsIndex = ..., + axis1: SupportsIndex = ..., + axis2: SupportsIndex = ..., + dtype: DTypeLike = ..., + out: None = ..., + ) -> Any: ... + @overload + def trace( + self, # >= 2D array + offset: SupportsIndex = ..., + axis1: SupportsIndex = ..., + axis2: SupportsIndex = ..., + dtype: DTypeLike = ..., + out: _ArrayT = ..., + ) -> _ArrayT: ... + + @overload + def take( # type: ignore[misc] + self: NDArray[_ScalarT], + indices: _IntLike_co, + axis: SupportsIndex | None = ..., + out: None = ..., + mode: _ModeKind = ..., + ) -> _ScalarT: ... + @overload + def take( # type: ignore[misc] + self, + indices: _ArrayLikeInt_co, + axis: SupportsIndex | None = ..., + out: None = ..., + mode: _ModeKind = ..., + ) -> ndarray[_AnyShape, _DTypeT_co]: ... + @overload + def take( + self, + indices: _ArrayLikeInt_co, + axis: SupportsIndex | None = ..., + out: _ArrayT = ..., + mode: _ModeKind = ..., + ) -> _ArrayT: ... + + @overload + def repeat( + self, + repeats: _ArrayLikeInt_co, + axis: None = None, + ) -> ndarray[tuple[int], _DTypeT_co]: ... + @overload + def repeat( + self, + repeats: _ArrayLikeInt_co, + axis: SupportsIndex, + ) -> ndarray[_AnyShape, _DTypeT_co]: ... + + def flatten(self, /, order: _OrderKACF = "C") -> ndarray[tuple[int], _DTypeT_co]: ... + def ravel(self, /, order: _OrderKACF = "C") -> ndarray[tuple[int], _DTypeT_co]: ... + + # NOTE: reshape also accepts negative integers, so we can't use integer literals + @overload # (None) + def reshape(self, shape: None, /, *, order: _OrderACF = "C", copy: builtins.bool | None = None) -> Self: ... + @overload # (empty_sequence) + def reshape( # type: ignore[overload-overlap] # mypy false positive + self, + shape: Sequence[Never], + /, + *, + order: _OrderACF = "C", + copy: builtins.bool | None = None, + ) -> ndarray[tuple[()], _DTypeT_co]: ... + @overload # (() | (int) | (int, int) | ....) # up to 8-d + def reshape( + self, + shape: _AnyShapeT, + /, + *, + order: _OrderACF = "C", + copy: builtins.bool | None = None, + ) -> ndarray[_AnyShapeT, _DTypeT_co]: ... + @overload # (index) + def reshape( + self, + size1: SupportsIndex, + /, + *, + order: _OrderACF = "C", + copy: builtins.bool | None = None, + ) -> ndarray[tuple[int], _DTypeT_co]: ... + @overload # (index, index) + def reshape( + self, + size1: SupportsIndex, + size2: SupportsIndex, + /, + *, + order: _OrderACF = "C", + copy: builtins.bool | None = None, + ) -> ndarray[tuple[int, int], _DTypeT_co]: ... + @overload # (index, index, index) + def reshape( + self, + size1: SupportsIndex, + size2: SupportsIndex, + size3: SupportsIndex, + /, + *, + order: _OrderACF = "C", + copy: builtins.bool | None = None, + ) -> ndarray[tuple[int, int, int], _DTypeT_co]: ... + @overload # (index, index, index, index) + def reshape( + self, + size1: SupportsIndex, + size2: SupportsIndex, + size3: SupportsIndex, + size4: SupportsIndex, + /, + *, + order: _OrderACF = "C", + copy: builtins.bool | None = None, + ) -> ndarray[tuple[int, int, int, int], _DTypeT_co]: ... + @overload # (int, *(index, ...)) + def reshape( + self, + size0: SupportsIndex, + /, + *shape: SupportsIndex, + order: _OrderACF = "C", + copy: builtins.bool | None = None, + ) -> ndarray[_AnyShape, _DTypeT_co]: ... + @overload # (sequence[index]) + def reshape( + self, + shape: Sequence[SupportsIndex], + /, + *, + order: _OrderACF = "C", + copy: builtins.bool | None = None, + ) -> ndarray[_AnyShape, _DTypeT_co]: ... + + @overload + def astype( + self, + dtype: _DTypeLike[_ScalarT], + order: _OrderKACF = ..., + casting: _CastingKind = ..., + subok: builtins.bool = ..., + copy: builtins.bool | _CopyMode = ..., + ) -> ndarray[_ShapeT_co, dtype[_ScalarT]]: ... + @overload + def astype( + self, + dtype: DTypeLike, + order: _OrderKACF = ..., + casting: _CastingKind = ..., + subok: builtins.bool = ..., + copy: builtins.bool | _CopyMode = ..., + ) -> ndarray[_ShapeT_co, dtype]: ... + + # + @overload # () + def view(self, /) -> Self: ... + @overload # (dtype: T) + def view(self, /, dtype: _DTypeT | _HasDType[_DTypeT]) -> ndarray[_ShapeT_co, _DTypeT]: ... + @overload # (dtype: dtype[T]) + def view(self, /, dtype: _DTypeLike[_ScalarT]) -> NDArray[_ScalarT]: ... + @overload # (type: T) + def view(self, /, *, type: type[_ArrayT]) -> _ArrayT: ... + @overload # (_: T) + def view(self, /, dtype: type[_ArrayT]) -> _ArrayT: ... + @overload # (dtype: ?) + def view(self, /, dtype: DTypeLike) -> ndarray[_ShapeT_co, dtype]: ... + @overload # (dtype: ?, type: type[T]) + def view(self, /, dtype: DTypeLike, type: type[_ArrayT]) -> _ArrayT: ... + + def setfield(self, /, val: ArrayLike, dtype: DTypeLike, offset: SupportsIndex = 0) -> None: ... + @overload + def getfield(self, dtype: _DTypeLike[_ScalarT], offset: SupportsIndex = 0) -> NDArray[_ScalarT]: ... + @overload + def getfield(self, dtype: DTypeLike, offset: SupportsIndex = 0) -> NDArray[Any]: ... + + def __index__(self: NDArray[integer], /) -> int: ... + def __complex__(self: NDArray[number | np.bool | object_], /) -> complex: ... + + def __len__(self) -> int: ... + def __contains__(self, value: object, /) -> builtins.bool: ... + + # NOTE: This weird `Never` tuple works around a strange mypy issue where it assigns + # `tuple[int]` to `tuple[Never]` or `tuple[int, int]` to `tuple[Never, Never]`. + # This way the bug only occurs for 9-D arrays, which are probably not very common. + @overload + def __iter__(self: ndarray[tuple[Never, Never, Never, Never, Never, Never, Never, Never, Never]], /) -> Iterator[Any]: ... + @overload # == 1-d & dtype[T \ object_] + def __iter__(self: ndarray[tuple[int], dtype[_NonObjectScalarT]], /) -> Iterator[_NonObjectScalarT]: ... + @overload # >= 2-d + def __iter__(self: ndarray[tuple[int, int, *tuple[int, ...]], dtype[_ScalarT]], /) -> Iterator[NDArray[_ScalarT]]: ... + @overload # ?-d + def __iter__(self, /) -> Iterator[Any]: ... + + # + @overload + def __lt__(self: _ArrayNumber_co, other: _ArrayLikeNumber_co, /) -> NDArray[np.bool]: ... + @overload + def __lt__(self: _ArrayTD64_co, other: _ArrayLikeTD64_co, /) -> NDArray[np.bool]: ... + @overload + def __lt__(self: NDArray[datetime64], other: _ArrayLikeDT64_co, /) -> NDArray[np.bool]: ... + @overload + def __lt__(self: NDArray[bytes_], other: _ArrayLikeBytes_co, /) -> NDArray[np.bool]: ... + @overload + def __lt__( + self: ndarray[Any, dtype[str_] | dtypes.StringDType], other: _ArrayLikeStr_co | _ArrayLikeString_co, / + ) -> NDArray[np.bool]: ... + @overload + def __lt__(self: NDArray[object_], other: object, /) -> NDArray[np.bool]: ... + @overload + def __lt__(self, other: _ArrayLikeObject_co, /) -> NDArray[np.bool]: ... + + # + @overload + def __le__(self: _ArrayNumber_co, other: _ArrayLikeNumber_co, /) -> NDArray[np.bool]: ... + @overload + def __le__(self: _ArrayTD64_co, other: _ArrayLikeTD64_co, /) -> NDArray[np.bool]: ... + @overload + def __le__(self: NDArray[datetime64], other: _ArrayLikeDT64_co, /) -> NDArray[np.bool]: ... + @overload + def __le__(self: NDArray[bytes_], other: _ArrayLikeBytes_co, /) -> NDArray[np.bool]: ... + @overload + def __le__( + self: ndarray[Any, dtype[str_] | dtypes.StringDType], other: _ArrayLikeStr_co | _ArrayLikeString_co, / + ) -> NDArray[np.bool]: ... + @overload + def __le__(self: NDArray[object_], other: object, /) -> NDArray[np.bool]: ... + @overload + def __le__(self, other: _ArrayLikeObject_co, /) -> NDArray[np.bool]: ... + + # + @overload + def __gt__(self: _ArrayNumber_co, other: _ArrayLikeNumber_co, /) -> NDArray[np.bool]: ... + @overload + def __gt__(self: _ArrayTD64_co, other: _ArrayLikeTD64_co, /) -> NDArray[np.bool]: ... + @overload + def __gt__(self: NDArray[datetime64], other: _ArrayLikeDT64_co, /) -> NDArray[np.bool]: ... + @overload + def __gt__(self: NDArray[bytes_], other: _ArrayLikeBytes_co, /) -> NDArray[np.bool]: ... + @overload + def __gt__( + self: ndarray[Any, dtype[str_] | dtypes.StringDType], other: _ArrayLikeStr_co | _ArrayLikeString_co, / + ) -> NDArray[np.bool]: ... + @overload + def __gt__(self: NDArray[object_], other: object, /) -> NDArray[np.bool]: ... + @overload + def __gt__(self, other: _ArrayLikeObject_co, /) -> NDArray[np.bool]: ... + + # + @overload + def __ge__(self: _ArrayNumber_co, other: _ArrayLikeNumber_co, /) -> NDArray[np.bool]: ... + @overload + def __ge__(self: _ArrayTD64_co, other: _ArrayLikeTD64_co, /) -> NDArray[np.bool]: ... + @overload + def __ge__(self: NDArray[datetime64], other: _ArrayLikeDT64_co, /) -> NDArray[np.bool]: ... + @overload + def __ge__(self: NDArray[bytes_], other: _ArrayLikeBytes_co, /) -> NDArray[np.bool]: ... + @overload + def __ge__( + self: ndarray[Any, dtype[str_] | dtypes.StringDType], other: _ArrayLikeStr_co | _ArrayLikeString_co, / + ) -> NDArray[np.bool]: ... + @overload + def __ge__(self: NDArray[object_], other: object, /) -> NDArray[np.bool]: ... + @overload + def __ge__(self, other: _ArrayLikeObject_co, /) -> NDArray[np.bool]: ... + + # Unary ops + + # TODO: Uncomment once https://github.com/python/mypy/issues/14070 is fixed + # @overload + # def __abs__(self: ndarray[_ShapeT, dtypes.Complex64DType], /) -> ndarray[_ShapeT, dtypes.Float32DType]: ... + # @overload + # def __abs__(self: ndarray[_ShapeT, dtypes.Complex128DType], /) -> ndarray[_ShapeT, dtypes.Float64DType]: ... + # @overload + # def __abs__(self: ndarray[_ShapeT, dtypes.CLongDoubleDType], /) -> ndarray[_ShapeT, dtypes.LongDoubleDType]: ... + # @overload + # def __abs__(self: ndarray[_ShapeT, dtype[complex128]], /) -> ndarray[_ShapeT, dtype[float64]]: ... + @overload + def __abs__(self: ndarray[_ShapeT, dtype[complexfloating[_NBit]]], /) -> ndarray[_ShapeT, dtype[floating[_NBit]]]: ... + @overload + def __abs__(self: _RealArrayT, /) -> _RealArrayT: ... + + def __invert__(self: _IntegralArrayT, /) -> _IntegralArrayT: ... # noqa: PYI019 + def __neg__(self: _NumericArrayT, /) -> _NumericArrayT: ... # noqa: PYI019 + def __pos__(self: _NumericArrayT, /) -> _NumericArrayT: ... # noqa: PYI019 + + # Binary ops + + # TODO: Support the "1d @ 1d -> scalar" case + @overload + def __matmul__(self: NDArray[_NumberT], other: _ArrayLikeBool_co, /) -> NDArray[_NumberT]: ... + @overload + def __matmul__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> NDArray[np.bool]: ... # type: ignore[overload-overlap] + @overload + def __matmul__(self: NDArray[np.bool], other: _ArrayLike[_NumberT], /) -> NDArray[_NumberT]: ... # type: ignore[overload-overlap] + @overload + def __matmul__(self: NDArray[floating[_64Bit]], other: _ArrayLikeFloat64_co, /) -> NDArray[float64]: ... + @overload + def __matmul__(self: _ArrayFloat64_co, other: _ArrayLike[floating[_64Bit]], /) -> NDArray[float64]: ... + @overload + def __matmul__(self: NDArray[complexfloating[_64Bit]], other: _ArrayLikeComplex128_co, /) -> NDArray[complex128]: ... + @overload + def __matmul__(self: _ArrayComplex128_co, other: _ArrayLike[complexfloating[_64Bit]], /) -> NDArray[complex128]: ... + @overload + def __matmul__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, /) -> NDArray[unsignedinteger]: ... # type: ignore[overload-overlap] + @overload + def __matmul__(self: _ArrayInt_co, other: _ArrayLikeInt_co, /) -> NDArray[signedinteger]: ... # type: ignore[overload-overlap] + @overload + def __matmul__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co, /) -> NDArray[floating]: ... # type: ignore[overload-overlap] + @overload + def __matmul__(self: _ArrayComplex_co, other: _ArrayLikeComplex_co, /) -> NDArray[complexfloating]: ... + @overload + def __matmul__(self: NDArray[number], other: _ArrayLikeNumber_co, /) -> NDArray[number]: ... + @overload + def __matmul__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __matmul__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload # signature equivalent to __matmul__ + def __rmatmul__(self: NDArray[_NumberT], other: _ArrayLikeBool_co, /) -> NDArray[_NumberT]: ... + @overload + def __rmatmul__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> NDArray[np.bool]: ... # type: ignore[overload-overlap] + @overload + def __rmatmul__(self: NDArray[np.bool], other: _ArrayLike[_NumberT], /) -> NDArray[_NumberT]: ... # type: ignore[overload-overlap] + @overload + def __rmatmul__(self: NDArray[floating[_64Bit]], other: _ArrayLikeFloat64_co, /) -> NDArray[float64]: ... + @overload + def __rmatmul__(self: _ArrayFloat64_co, other: _ArrayLike[floating[_64Bit]], /) -> NDArray[float64]: ... + @overload + def __rmatmul__(self: NDArray[complexfloating[_64Bit]], other: _ArrayLikeComplex128_co, /) -> NDArray[complex128]: ... + @overload + def __rmatmul__(self: _ArrayComplex128_co, other: _ArrayLike[complexfloating[_64Bit]], /) -> NDArray[complex128]: ... + @overload + def __rmatmul__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, /) -> NDArray[unsignedinteger]: ... # type: ignore[overload-overlap] + @overload + def __rmatmul__(self: _ArrayInt_co, other: _ArrayLikeInt_co, /) -> NDArray[signedinteger]: ... # type: ignore[overload-overlap] + @overload + def __rmatmul__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co, /) -> NDArray[floating]: ... # type: ignore[overload-overlap] + @overload + def __rmatmul__(self: _ArrayComplex_co, other: _ArrayLikeComplex_co, /) -> NDArray[complexfloating]: ... + @overload + def __rmatmul__(self: NDArray[number], other: _ArrayLikeNumber_co, /) -> NDArray[number]: ... + @overload + def __rmatmul__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __rmatmul__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload + def __mod__(self: NDArray[_RealNumberT], other: int | np.bool, /) -> ndarray[_ShapeT_co, dtype[_RealNumberT]]: ... + @overload + def __mod__(self: NDArray[_RealNumberT], other: _ArrayLikeBool_co, /) -> NDArray[_RealNumberT]: ... # type: ignore[overload-overlap] + @overload + def __mod__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> NDArray[int8]: ... # type: ignore[overload-overlap] + @overload + def __mod__(self: NDArray[np.bool], other: _ArrayLike[_RealNumberT], /) -> NDArray[_RealNumberT]: ... # type: ignore[overload-overlap] + @overload + def __mod__(self: NDArray[float64], other: _ArrayLikeFloat64_co, /) -> NDArray[float64]: ... + @overload + def __mod__(self: _ArrayFloat64_co, other: _ArrayLike[floating[_64Bit]], /) -> NDArray[float64]: ... + @overload + def __mod__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, /) -> NDArray[unsignedinteger]: ... # type: ignore[overload-overlap] + @overload + def __mod__(self: _ArrayInt_co, other: _ArrayLikeInt_co, /) -> NDArray[signedinteger]: ... # type: ignore[overload-overlap] + @overload + def __mod__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co, /) -> NDArray[floating]: ... + @overload + def __mod__(self: NDArray[timedelta64], other: _ArrayLike[timedelta64], /) -> NDArray[timedelta64]: ... + @overload + def __mod__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __mod__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload # signature equivalent to __mod__ + def __rmod__(self: NDArray[_RealNumberT], other: int | np.bool, /) -> ndarray[_ShapeT_co, dtype[_RealNumberT]]: ... + @overload + def __rmod__(self: NDArray[_RealNumberT], other: _ArrayLikeBool_co, /) -> NDArray[_RealNumberT]: ... # type: ignore[overload-overlap] + @overload + def __rmod__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> NDArray[int8]: ... # type: ignore[overload-overlap] + @overload + def __rmod__(self: NDArray[np.bool], other: _ArrayLike[_RealNumberT], /) -> NDArray[_RealNumberT]: ... # type: ignore[overload-overlap] + @overload + def __rmod__(self: NDArray[float64], other: _ArrayLikeFloat64_co, /) -> NDArray[float64]: ... + @overload + def __rmod__(self: _ArrayFloat64_co, other: _ArrayLike[floating[_64Bit]], /) -> NDArray[float64]: ... + @overload + def __rmod__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, /) -> NDArray[unsignedinteger]: ... # type: ignore[overload-overlap] + @overload + def __rmod__(self: _ArrayInt_co, other: _ArrayLikeInt_co, /) -> NDArray[signedinteger]: ... # type: ignore[overload-overlap] + @overload + def __rmod__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co, /) -> NDArray[floating]: ... + @overload + def __rmod__(self: NDArray[timedelta64], other: _ArrayLike[timedelta64], /) -> NDArray[timedelta64]: ... + @overload + def __rmod__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __rmod__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload + def __divmod__(self: NDArray[_RealNumberT], rhs: int | np.bool, /) -> _2Tuple[ndarray[_ShapeT_co, dtype[_RealNumberT]]]: ... + @overload + def __divmod__(self: NDArray[_RealNumberT], rhs: _ArrayLikeBool_co, /) -> _2Tuple[NDArray[_RealNumberT]]: ... # type: ignore[overload-overlap] + @overload + def __divmod__(self: NDArray[np.bool], rhs: _ArrayLikeBool_co, /) -> _2Tuple[NDArray[int8]]: ... # type: ignore[overload-overlap] + @overload + def __divmod__(self: NDArray[np.bool], rhs: _ArrayLike[_RealNumberT], /) -> _2Tuple[NDArray[_RealNumberT]]: ... # type: ignore[overload-overlap] + @overload + def __divmod__(self: NDArray[float64], rhs: _ArrayLikeFloat64_co, /) -> _2Tuple[NDArray[float64]]: ... + @overload + def __divmod__(self: _ArrayFloat64_co, rhs: _ArrayLike[floating[_64Bit]], /) -> _2Tuple[NDArray[float64]]: ... + @overload + def __divmod__(self: _ArrayUInt_co, rhs: _ArrayLikeUInt_co, /) -> _2Tuple[NDArray[unsignedinteger]]: ... # type: ignore[overload-overlap] + @overload + def __divmod__(self: _ArrayInt_co, rhs: _ArrayLikeInt_co, /) -> _2Tuple[NDArray[signedinteger]]: ... # type: ignore[overload-overlap] + @overload + def __divmod__(self: _ArrayFloat_co, rhs: _ArrayLikeFloat_co, /) -> _2Tuple[NDArray[floating]]: ... + @overload + def __divmod__(self: NDArray[timedelta64], rhs: _ArrayLike[timedelta64], /) -> tuple[NDArray[int64], NDArray[timedelta64]]: ... + + @overload # signature equivalent to __divmod__ + def __rdivmod__(self: NDArray[_RealNumberT], lhs: int | np.bool, /) -> _2Tuple[ndarray[_ShapeT_co, dtype[_RealNumberT]]]: ... + @overload + def __rdivmod__(self: NDArray[_RealNumberT], lhs: _ArrayLikeBool_co, /) -> _2Tuple[NDArray[_RealNumberT]]: ... # type: ignore[overload-overlap] + @overload + def __rdivmod__(self: NDArray[np.bool], lhs: _ArrayLikeBool_co, /) -> _2Tuple[NDArray[int8]]: ... # type: ignore[overload-overlap] + @overload + def __rdivmod__(self: NDArray[np.bool], lhs: _ArrayLike[_RealNumberT], /) -> _2Tuple[NDArray[_RealNumberT]]: ... # type: ignore[overload-overlap] + @overload + def __rdivmod__(self: NDArray[float64], lhs: _ArrayLikeFloat64_co, /) -> _2Tuple[NDArray[float64]]: ... + @overload + def __rdivmod__(self: _ArrayFloat64_co, lhs: _ArrayLike[floating[_64Bit]], /) -> _2Tuple[NDArray[float64]]: ... + @overload + def __rdivmod__(self: _ArrayUInt_co, lhs: _ArrayLikeUInt_co, /) -> _2Tuple[NDArray[unsignedinteger]]: ... # type: ignore[overload-overlap] + @overload + def __rdivmod__(self: _ArrayInt_co, lhs: _ArrayLikeInt_co, /) -> _2Tuple[NDArray[signedinteger]]: ... # type: ignore[overload-overlap] + @overload + def __rdivmod__(self: _ArrayFloat_co, lhs: _ArrayLikeFloat_co, /) -> _2Tuple[NDArray[floating]]: ... + @overload + def __rdivmod__(self: NDArray[timedelta64], lhs: _ArrayLike[timedelta64], /) -> tuple[NDArray[int64], NDArray[timedelta64]]: ... + + @overload + def __add__(self: NDArray[_NumberT], other: int | np.bool, /) -> ndarray[_ShapeT_co, dtype[_NumberT]]: ... + @overload + def __add__(self: NDArray[_NumberT], other: _ArrayLikeBool_co, /) -> NDArray[_NumberT]: ... # type: ignore[overload-overlap] + @overload + def __add__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> NDArray[np.bool]: ... # type: ignore[overload-overlap] + @overload + def __add__(self: NDArray[np.bool], other: _ArrayLike[_NumberT], /) -> NDArray[_NumberT]: ... # type: ignore[overload-overlap] + @overload + def __add__(self: NDArray[float64], other: _ArrayLikeFloat64_co, /) -> NDArray[float64]: ... + @overload + def __add__(self: _ArrayFloat64_co, other: _ArrayLike[floating[_64Bit]], /) -> NDArray[float64]: ... + @overload + def __add__(self: NDArray[complex128], other: _ArrayLikeComplex128_co, /) -> NDArray[complex128]: ... + @overload + def __add__(self: _ArrayComplex128_co, other: _ArrayLike[complexfloating[_64Bit]], /) -> NDArray[complex128]: ... + @overload + def __add__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, /) -> NDArray[unsignedinteger]: ... # type: ignore[overload-overlap] + @overload + def __add__(self: _ArrayInt_co, other: _ArrayLikeInt_co, /) -> NDArray[signedinteger]: ... # type: ignore[overload-overlap] + @overload + def __add__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co, /) -> NDArray[floating]: ... # type: ignore[overload-overlap] + @overload + def __add__(self: _ArrayComplex_co, other: _ArrayLikeComplex_co, /) -> NDArray[complexfloating]: ... # type: ignore[overload-overlap] + @overload + def __add__(self: NDArray[number], other: _ArrayLikeNumber_co, /) -> NDArray[number]: ... # type: ignore[overload-overlap] + @overload + def __add__(self: _ArrayTD64_co, other: _ArrayLikeTD64_co, /) -> NDArray[timedelta64]: ... + @overload + def __add__(self: _ArrayTD64_co, other: _ArrayLikeDT64_co, /) -> NDArray[datetime64]: ... + @overload + def __add__(self: NDArray[datetime64], other: _ArrayLikeTD64_co, /) -> NDArray[datetime64]: ... + @overload + def __add__(self: NDArray[bytes_], other: _ArrayLikeBytes_co, /) -> NDArray[bytes_]: ... + @overload + def __add__(self: NDArray[str_], other: _ArrayLikeStr_co, /) -> NDArray[str_]: ... + @overload + def __add__( + self: ndarray[Any, dtypes.StringDType], + other: _ArrayLikeStr_co | _ArrayLikeString_co, + /, + ) -> ndarray[tuple[Any, ...], dtypes.StringDType]: ... + @overload + def __add__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __add__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload # signature equivalent to __add__ + def __radd__(self: NDArray[_NumberT], other: int | np.bool, /) -> ndarray[_ShapeT_co, dtype[_NumberT]]: ... + @overload + def __radd__(self: NDArray[_NumberT], other: _ArrayLikeBool_co, /) -> NDArray[_NumberT]: ... # type: ignore[overload-overlap] + @overload + def __radd__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> NDArray[np.bool]: ... # type: ignore[overload-overlap] + @overload + def __radd__(self: NDArray[np.bool], other: _ArrayLike[_NumberT], /) -> NDArray[_NumberT]: ... # type: ignore[overload-overlap] + @overload + def __radd__(self: NDArray[float64], other: _ArrayLikeFloat64_co, /) -> NDArray[float64]: ... + @overload + def __radd__(self: _ArrayFloat64_co, other: _ArrayLike[floating[_64Bit]], /) -> NDArray[float64]: ... + @overload + def __radd__(self: NDArray[complex128], other: _ArrayLikeComplex128_co, /) -> NDArray[complex128]: ... + @overload + def __radd__(self: _ArrayComplex128_co, other: _ArrayLike[complexfloating[_64Bit]], /) -> NDArray[complex128]: ... + @overload + def __radd__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, /) -> NDArray[unsignedinteger]: ... # type: ignore[overload-overlap] + @overload + def __radd__(self: _ArrayInt_co, other: _ArrayLikeInt_co, /) -> NDArray[signedinteger]: ... # type: ignore[overload-overlap] + @overload + def __radd__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co, /) -> NDArray[floating]: ... # type: ignore[overload-overlap] + @overload + def __radd__(self: _ArrayComplex_co, other: _ArrayLikeComplex_co, /) -> NDArray[complexfloating]: ... # type: ignore[overload-overlap] + @overload + def __radd__(self: NDArray[number], other: _ArrayLikeNumber_co, /) -> NDArray[number]: ... # type: ignore[overload-overlap] + @overload + def __radd__(self: _ArrayTD64_co, other: _ArrayLikeTD64_co, /) -> NDArray[timedelta64]: ... + @overload + def __radd__(self: _ArrayTD64_co, other: _ArrayLikeDT64_co, /) -> NDArray[datetime64]: ... + @overload + def __radd__(self: NDArray[datetime64], other: _ArrayLikeTD64_co, /) -> NDArray[datetime64]: ... + @overload + def __radd__(self: NDArray[bytes_], other: _ArrayLikeBytes_co, /) -> NDArray[bytes_]: ... + @overload + def __radd__(self: NDArray[str_], other: _ArrayLikeStr_co, /) -> NDArray[str_]: ... + @overload + def __radd__( + self: ndarray[Any, dtypes.StringDType], + other: _ArrayLikeStr_co | _ArrayLikeString_co, + /, + ) -> ndarray[tuple[Any, ...], dtypes.StringDType]: ... + @overload + def __radd__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __radd__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload + def __sub__(self: NDArray[_NumberT], other: int | np.bool, /) -> ndarray[_ShapeT_co, dtype[_NumberT]]: ... + @overload + def __sub__(self: NDArray[_NumberT], other: _ArrayLikeBool_co, /) -> NDArray[_NumberT]: ... # type: ignore[overload-overlap] + @overload + def __sub__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> NoReturn: ... + @overload + def __sub__(self: NDArray[np.bool], other: _ArrayLike[_NumberT], /) -> NDArray[_NumberT]: ... # type: ignore[overload-overlap] + @overload + def __sub__(self: NDArray[float64], other: _ArrayLikeFloat64_co, /) -> NDArray[float64]: ... + @overload + def __sub__(self: _ArrayFloat64_co, other: _ArrayLike[floating[_64Bit]], /) -> NDArray[float64]: ... + @overload + def __sub__(self: NDArray[complex128], other: _ArrayLikeComplex128_co, /) -> NDArray[complex128]: ... + @overload + def __sub__(self: _ArrayComplex128_co, other: _ArrayLike[complexfloating[_64Bit]], /) -> NDArray[complex128]: ... + @overload + def __sub__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, /) -> NDArray[unsignedinteger]: ... # type: ignore[overload-overlap] + @overload + def __sub__(self: _ArrayInt_co, other: _ArrayLikeInt_co, /) -> NDArray[signedinteger]: ... # type: ignore[overload-overlap] + @overload + def __sub__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co, /) -> NDArray[floating]: ... # type: ignore[overload-overlap] + @overload + def __sub__(self: _ArrayComplex_co, other: _ArrayLikeComplex_co, /) -> NDArray[complexfloating]: ... # type: ignore[overload-overlap] + @overload + def __sub__(self: NDArray[number], other: _ArrayLikeNumber_co, /) -> NDArray[number]: ... # type: ignore[overload-overlap] + @overload + def __sub__(self: _ArrayTD64_co, other: _ArrayLikeTD64_co, /) -> NDArray[timedelta64]: ... + @overload + def __sub__(self: NDArray[datetime64], other: _ArrayLikeTD64_co, /) -> NDArray[datetime64]: ... + @overload + def __sub__(self: NDArray[datetime64], other: _ArrayLikeDT64_co, /) -> NDArray[timedelta64]: ... + @overload + def __sub__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __sub__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload + def __rsub__(self: NDArray[_NumberT], other: int | np.bool, /) -> ndarray[_ShapeT_co, dtype[_NumberT]]: ... + @overload + def __rsub__(self: NDArray[_NumberT], other: _ArrayLikeBool_co, /) -> NDArray[_NumberT]: ... # type: ignore[overload-overlap] + @overload + def __rsub__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> NoReturn: ... + @overload + def __rsub__(self: NDArray[np.bool], other: _ArrayLike[_NumberT], /) -> NDArray[_NumberT]: ... # type: ignore[overload-overlap] + @overload + def __rsub__(self: NDArray[float64], other: _ArrayLikeFloat64_co, /) -> NDArray[float64]: ... + @overload + def __rsub__(self: _ArrayFloat64_co, other: _ArrayLike[floating[_64Bit]], /) -> NDArray[float64]: ... + @overload + def __rsub__(self: NDArray[complex128], other: _ArrayLikeComplex128_co, /) -> NDArray[complex128]: ... + @overload + def __rsub__(self: _ArrayComplex128_co, other: _ArrayLike[complexfloating[_64Bit]], /) -> NDArray[complex128]: ... + @overload + def __rsub__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, /) -> NDArray[unsignedinteger]: ... # type: ignore[overload-overlap] + @overload + def __rsub__(self: _ArrayInt_co, other: _ArrayLikeInt_co, /) -> NDArray[signedinteger]: ... # type: ignore[overload-overlap] + @overload + def __rsub__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co, /) -> NDArray[floating]: ... # type: ignore[overload-overlap] + @overload + def __rsub__(self: _ArrayComplex_co, other: _ArrayLikeComplex_co, /) -> NDArray[complexfloating]: ... # type: ignore[overload-overlap] + @overload + def __rsub__(self: NDArray[number], other: _ArrayLikeNumber_co, /) -> NDArray[number]: ... # type: ignore[overload-overlap] + @overload + def __rsub__(self: _ArrayTD64_co, other: _ArrayLikeTD64_co, /) -> NDArray[timedelta64]: ... + @overload + def __rsub__(self: _ArrayTD64_co, other: _ArrayLikeDT64_co, /) -> NDArray[datetime64]: ... + @overload + def __rsub__(self: NDArray[datetime64], other: _ArrayLikeDT64_co, /) -> NDArray[timedelta64]: ... + @overload + def __rsub__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __rsub__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload + def __mul__(self: NDArray[_NumberT], other: int | np.bool, /) -> ndarray[_ShapeT_co, dtype[_NumberT]]: ... + @overload + def __mul__(self: NDArray[_NumberT], other: _ArrayLikeBool_co, /) -> NDArray[_NumberT]: ... # type: ignore[overload-overlap] + @overload + def __mul__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> NDArray[np.bool]: ... # type: ignore[overload-overlap] + @overload + def __mul__(self: NDArray[np.bool], other: _ArrayLike[_NumberT], /) -> NDArray[_NumberT]: ... # type: ignore[overload-overlap] + @overload + def __mul__(self: NDArray[float64], other: _ArrayLikeFloat64_co, /) -> NDArray[float64]: ... + @overload + def __mul__(self: _ArrayFloat64_co, other: _ArrayLike[floating[_64Bit]], /) -> NDArray[float64]: ... + @overload + def __mul__(self: NDArray[complex128], other: _ArrayLikeComplex128_co, /) -> NDArray[complex128]: ... + @overload + def __mul__(self: _ArrayComplex128_co, other: _ArrayLike[complexfloating[_64Bit]], /) -> NDArray[complex128]: ... + @overload + def __mul__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, /) -> NDArray[unsignedinteger]: ... # type: ignore[overload-overlap] + @overload + def __mul__(self: _ArrayInt_co, other: _ArrayLikeInt_co, /) -> NDArray[signedinteger]: ... # type: ignore[overload-overlap] + @overload + def __mul__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co, /) -> NDArray[floating]: ... # type: ignore[overload-overlap] + @overload + def __mul__(self: _ArrayComplex_co, other: _ArrayLikeComplex_co, /) -> NDArray[complexfloating]: ... # type: ignore[overload-overlap] + @overload + def __mul__(self: NDArray[number], other: _ArrayLikeNumber_co, /) -> NDArray[number]: ... + @overload + def __mul__(self: NDArray[timedelta64], other: _ArrayLikeFloat_co, /) -> NDArray[timedelta64]: ... + @overload + def __mul__(self: _ArrayFloat_co, other: _ArrayLike[timedelta64], /) -> NDArray[timedelta64]: ... + @overload + def __mul__( + self: ndarray[Any, dtype[character] | dtypes.StringDType], + other: _ArrayLikeInt, + /, + ) -> ndarray[tuple[Any, ...], _DTypeT_co]: ... + @overload + def __mul__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __mul__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload # signature equivalent to __mul__ + def __rmul__(self: NDArray[_NumberT], other: int | np.bool, /) -> ndarray[_ShapeT_co, dtype[_NumberT]]: ... + @overload + def __rmul__(self: NDArray[_NumberT], other: _ArrayLikeBool_co, /) -> NDArray[_NumberT]: ... # type: ignore[overload-overlap] + @overload + def __rmul__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> NDArray[np.bool]: ... # type: ignore[overload-overlap] + @overload + def __rmul__(self: NDArray[np.bool], other: _ArrayLike[_NumberT], /) -> NDArray[_NumberT]: ... # type: ignore[overload-overlap] + @overload + def __rmul__(self: NDArray[float64], other: _ArrayLikeFloat64_co, /) -> NDArray[float64]: ... + @overload + def __rmul__(self: _ArrayFloat64_co, other: _ArrayLike[floating[_64Bit]], /) -> NDArray[float64]: ... + @overload + def __rmul__(self: NDArray[complex128], other: _ArrayLikeComplex128_co, /) -> NDArray[complex128]: ... + @overload + def __rmul__(self: _ArrayComplex128_co, other: _ArrayLike[complexfloating[_64Bit]], /) -> NDArray[complex128]: ... + @overload + def __rmul__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, /) -> NDArray[unsignedinteger]: ... # type: ignore[overload-overlap] + @overload + def __rmul__(self: _ArrayInt_co, other: _ArrayLikeInt_co, /) -> NDArray[signedinteger]: ... # type: ignore[overload-overlap] + @overload + def __rmul__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co, /) -> NDArray[floating]: ... # type: ignore[overload-overlap] + @overload + def __rmul__(self: _ArrayComplex_co, other: _ArrayLikeComplex_co, /) -> NDArray[complexfloating]: ... # type: ignore[overload-overlap] + @overload + def __rmul__(self: NDArray[number], other: _ArrayLikeNumber_co, /) -> NDArray[number]: ... + @overload + def __rmul__(self: NDArray[timedelta64], other: _ArrayLikeFloat_co, /) -> NDArray[timedelta64]: ... + @overload + def __rmul__(self: _ArrayFloat_co, other: _ArrayLike[timedelta64], /) -> NDArray[timedelta64]: ... + @overload + def __rmul__( + self: ndarray[Any, dtype[character] | dtypes.StringDType], + other: _ArrayLikeInt, + /, + ) -> ndarray[tuple[Any, ...], _DTypeT_co]: ... + @overload + def __rmul__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __rmul__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload + def __truediv__(self: _ArrayInt_co | NDArray[float64], other: _ArrayLikeFloat64_co, /) -> NDArray[float64]: ... + @overload + def __truediv__(self: _ArrayFloat64_co, other: _ArrayLikeInt_co | _ArrayLike[floating[_64Bit]], /) -> NDArray[float64]: ... + @overload + def __truediv__(self: NDArray[complex128], other: _ArrayLikeComplex128_co, /) -> NDArray[complex128]: ... + @overload + def __truediv__(self: _ArrayComplex128_co, other: _ArrayLike[complexfloating[_64Bit]], /) -> NDArray[complex128]: ... + @overload + def __truediv__(self: NDArray[floating], other: _ArrayLikeFloat_co, /) -> NDArray[floating]: ... + @overload + def __truediv__(self: _ArrayFloat_co, other: _ArrayLike[floating], /) -> NDArray[floating]: ... + @overload + def __truediv__(self: NDArray[complexfloating], other: _ArrayLikeNumber_co, /) -> NDArray[complexfloating]: ... + @overload + def __truediv__(self: _ArrayNumber_co, other: _ArrayLike[complexfloating], /) -> NDArray[complexfloating]: ... + @overload + def __truediv__(self: NDArray[inexact], other: _ArrayLikeNumber_co, /) -> NDArray[inexact]: ... + @overload + def __truediv__(self: NDArray[number], other: _ArrayLikeNumber_co, /) -> NDArray[number]: ... + @overload + def __truediv__(self: NDArray[timedelta64], other: _ArrayLike[timedelta64], /) -> NDArray[float64]: ... + @overload + def __truediv__(self: NDArray[timedelta64], other: _ArrayLikeBool_co, /) -> NoReturn: ... + @overload + def __truediv__(self: NDArray[timedelta64], other: _ArrayLikeFloat_co, /) -> NDArray[timedelta64]: ... + @overload + def __truediv__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __truediv__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload + def __rtruediv__(self: _ArrayInt_co | NDArray[float64], other: _ArrayLikeFloat64_co, /) -> NDArray[float64]: ... + @overload + def __rtruediv__(self: _ArrayFloat64_co, other: _ArrayLikeInt_co | _ArrayLike[floating[_64Bit]], /) -> NDArray[float64]: ... + @overload + def __rtruediv__(self: NDArray[complex128], other: _ArrayLikeComplex128_co, /) -> NDArray[complex128]: ... + @overload + def __rtruediv__(self: _ArrayComplex128_co, other: _ArrayLike[complexfloating[_64Bit]], /) -> NDArray[complex128]: ... + @overload + def __rtruediv__(self: NDArray[floating], other: _ArrayLikeFloat_co, /) -> NDArray[floating]: ... + @overload + def __rtruediv__(self: _ArrayFloat_co, other: _ArrayLike[floating], /) -> NDArray[floating]: ... + @overload + def __rtruediv__(self: NDArray[complexfloating], other: _ArrayLikeNumber_co, /) -> NDArray[complexfloating]: ... + @overload + def __rtruediv__(self: _ArrayNumber_co, other: _ArrayLike[complexfloating], /) -> NDArray[complexfloating]: ... + @overload + def __rtruediv__(self: NDArray[inexact], other: _ArrayLikeNumber_co, /) -> NDArray[inexact]: ... + @overload + def __rtruediv__(self: NDArray[number], other: _ArrayLikeNumber_co, /) -> NDArray[number]: ... + @overload + def __rtruediv__(self: NDArray[timedelta64], other: _ArrayLike[timedelta64], /) -> NDArray[float64]: ... + @overload + def __rtruediv__(self: NDArray[integer | floating], other: _ArrayLike[timedelta64], /) -> NDArray[timedelta64]: ... + @overload + def __rtruediv__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __rtruediv__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload + def __floordiv__(self: NDArray[_RealNumberT], other: int | np.bool, /) -> ndarray[_ShapeT_co, dtype[_RealNumberT]]: ... + @overload + def __floordiv__(self: NDArray[_RealNumberT], other: _ArrayLikeBool_co, /) -> NDArray[_RealNumberT]: ... # type: ignore[overload-overlap] + @overload + def __floordiv__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> NDArray[int8]: ... # type: ignore[overload-overlap] + @overload + def __floordiv__(self: NDArray[np.bool], other: _ArrayLike[_RealNumberT], /) -> NDArray[_RealNumberT]: ... # type: ignore[overload-overlap] + @overload + def __floordiv__(self: NDArray[float64], other: _ArrayLikeFloat64_co, /) -> NDArray[float64]: ... + @overload + def __floordiv__(self: _ArrayFloat64_co, other: _ArrayLike[floating[_64Bit]], /) -> NDArray[float64]: ... + @overload + def __floordiv__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, /) -> NDArray[unsignedinteger]: ... # type: ignore[overload-overlap] + @overload + def __floordiv__(self: _ArrayInt_co, other: _ArrayLikeInt_co, /) -> NDArray[signedinteger]: ... # type: ignore[overload-overlap] + @overload + def __floordiv__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co, /) -> NDArray[floating]: ... + @overload + def __floordiv__(self: NDArray[timedelta64], other: _ArrayLike[timedelta64], /) -> NDArray[int64]: ... + @overload + def __floordiv__(self: NDArray[timedelta64], other: _ArrayLikeBool_co, /) -> NoReturn: ... + @overload + def __floordiv__(self: NDArray[timedelta64], other: _ArrayLikeFloat_co, /) -> NDArray[timedelta64]: ... + @overload + def __floordiv__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __floordiv__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload + def __rfloordiv__(self: NDArray[_RealNumberT], other: int | np.bool, /) -> ndarray[_ShapeT_co, dtype[_RealNumberT]]: ... + @overload + def __rfloordiv__(self: NDArray[_RealNumberT], other: _ArrayLikeBool_co, /) -> NDArray[_RealNumberT]: ... # type: ignore[overload-overlap] + @overload + def __rfloordiv__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> NDArray[int8]: ... # type: ignore[overload-overlap] + @overload + def __rfloordiv__(self: NDArray[np.bool], other: _ArrayLike[_RealNumberT], /) -> NDArray[_RealNumberT]: ... # type: ignore[overload-overlap] + @overload + def __rfloordiv__(self: NDArray[float64], other: _ArrayLikeFloat64_co, /) -> NDArray[float64]: ... + @overload + def __rfloordiv__(self: _ArrayFloat64_co, other: _ArrayLike[floating[_64Bit]], /) -> NDArray[float64]: ... + @overload + def __rfloordiv__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, /) -> NDArray[unsignedinteger]: ... # type: ignore[overload-overlap] + @overload + def __rfloordiv__(self: _ArrayInt_co, other: _ArrayLikeInt_co, /) -> NDArray[signedinteger]: ... # type: ignore[overload-overlap] + @overload + def __rfloordiv__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co, /) -> NDArray[floating]: ... # type: ignore[overload-overlap] + @overload + def __rfloordiv__(self: NDArray[timedelta64], other: _ArrayLike[timedelta64], /) -> NDArray[int64]: ... + @overload + def __rfloordiv__(self: NDArray[floating | integer], other: _ArrayLike[timedelta64], /) -> NDArray[timedelta64]: ... + @overload + def __rfloordiv__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __rfloordiv__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload + def __pow__(self: NDArray[_NumberT], other: int | np.bool, mod: None = None, /) -> ndarray[_ShapeT_co, dtype[_NumberT]]: ... + @overload + def __pow__(self: NDArray[_NumberT], other: _ArrayLikeBool_co, mod: None = None, /) -> NDArray[_NumberT]: ... # type: ignore[overload-overlap] + @overload + def __pow__(self: NDArray[np.bool], other: _ArrayLikeBool_co, mod: None = None, /) -> NDArray[int8]: ... # type: ignore[overload-overlap] + @overload + def __pow__(self: NDArray[np.bool], other: _ArrayLike[_NumberT], mod: None = None, /) -> NDArray[_NumberT]: ... # type: ignore[overload-overlap] + @overload + def __pow__(self: NDArray[float64], other: _ArrayLikeFloat64_co, mod: None = None, /) -> NDArray[float64]: ... + @overload + def __pow__(self: _ArrayFloat64_co, other: _ArrayLike[floating[_64Bit]], mod: None = None, /) -> NDArray[float64]: ... + @overload + def __pow__(self: NDArray[complex128], other: _ArrayLikeComplex128_co, mod: None = None, /) -> NDArray[complex128]: ... + @overload + def __pow__( + self: _ArrayComplex128_co, other: _ArrayLike[complexfloating[_64Bit]], mod: None = None, / + ) -> NDArray[complex128]: ... + @overload + def __pow__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, mod: None = None, /) -> NDArray[unsignedinteger]: ... # type: ignore[overload-overlap] + @overload + def __pow__(self: _ArrayInt_co, other: _ArrayLikeInt_co, mod: None = None, /) -> NDArray[signedinteger]: ... # type: ignore[overload-overlap] + @overload + def __pow__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co, mod: None = None, /) -> NDArray[floating]: ... # type: ignore[overload-overlap] + @overload + def __pow__(self: _ArrayComplex_co, other: _ArrayLikeComplex_co, mod: None = None, /) -> NDArray[complexfloating]: ... + @overload + def __pow__(self: NDArray[number], other: _ArrayLikeNumber_co, mod: None = None, /) -> NDArray[number]: ... + @overload + def __pow__(self: NDArray[object_], other: Any, mod: None = None, /) -> Any: ... + @overload + def __pow__(self: NDArray[Any], other: _ArrayLikeObject_co, mod: None = None, /) -> Any: ... + + @overload + def __rpow__(self: NDArray[_NumberT], other: int | np.bool, mod: None = None, /) -> ndarray[_ShapeT_co, dtype[_NumberT]]: ... + @overload + def __rpow__(self: NDArray[_NumberT], other: _ArrayLikeBool_co, mod: None = None, /) -> NDArray[_NumberT]: ... # type: ignore[overload-overlap] + @overload + def __rpow__(self: NDArray[np.bool], other: _ArrayLikeBool_co, mod: None = None, /) -> NDArray[int8]: ... # type: ignore[overload-overlap] + @overload + def __rpow__(self: NDArray[np.bool], other: _ArrayLike[_NumberT], mod: None = None, /) -> NDArray[_NumberT]: ... # type: ignore[overload-overlap] + @overload + def __rpow__(self: NDArray[float64], other: _ArrayLikeFloat64_co, mod: None = None, /) -> NDArray[float64]: ... + @overload + def __rpow__(self: _ArrayFloat64_co, other: _ArrayLike[floating[_64Bit]], mod: None = None, /) -> NDArray[float64]: ... + @overload + def __rpow__(self: NDArray[complex128], other: _ArrayLikeComplex128_co, mod: None = None, /) -> NDArray[complex128]: ... + @overload + def __rpow__( + self: _ArrayComplex128_co, other: _ArrayLike[complexfloating[_64Bit]], mod: None = None, / + ) -> NDArray[complex128]: ... + @overload + def __rpow__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, mod: None = None, /) -> NDArray[unsignedinteger]: ... # type: ignore[overload-overlap] + @overload + def __rpow__(self: _ArrayInt_co, other: _ArrayLikeInt_co, mod: None = None, /) -> NDArray[signedinteger]: ... # type: ignore[overload-overlap] + @overload + def __rpow__(self: _ArrayFloat_co, other: _ArrayLikeFloat_co, mod: None = None, /) -> NDArray[floating]: ... # type: ignore[overload-overlap] + @overload + def __rpow__(self: _ArrayComplex_co, other: _ArrayLikeComplex_co, mod: None = None, /) -> NDArray[complexfloating]: ... + @overload + def __rpow__(self: NDArray[number], other: _ArrayLikeNumber_co, mod: None = None, /) -> NDArray[number]: ... + @overload + def __rpow__(self: NDArray[object_], other: Any, mod: None = None, /) -> Any: ... + @overload + def __rpow__(self: NDArray[Any], other: _ArrayLikeObject_co, mod: None = None, /) -> Any: ... + + @overload + def __lshift__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> NDArray[int8]: ... # type: ignore[misc] + @overload + def __lshift__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, /) -> NDArray[unsignedinteger]: ... # type: ignore[misc] + @overload + def __lshift__(self: _ArrayInt_co, other: _ArrayLikeInt_co, /) -> NDArray[signedinteger]: ... + @overload + def __lshift__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __lshift__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload + def __rlshift__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> NDArray[int8]: ... # type: ignore[misc] + @overload + def __rlshift__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, /) -> NDArray[unsignedinteger]: ... # type: ignore[misc] + @overload + def __rlshift__(self: _ArrayInt_co, other: _ArrayLikeInt_co, /) -> NDArray[signedinteger]: ... + @overload + def __rlshift__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __rlshift__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload + def __rshift__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> NDArray[int8]: ... # type: ignore[misc] + @overload + def __rshift__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, /) -> NDArray[unsignedinteger]: ... # type: ignore[misc] + @overload + def __rshift__(self: _ArrayInt_co, other: _ArrayLikeInt_co, /) -> NDArray[signedinteger]: ... + @overload + def __rshift__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __rshift__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload + def __rrshift__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> NDArray[int8]: ... # type: ignore[misc] + @overload + def __rrshift__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, /) -> NDArray[unsignedinteger]: ... # type: ignore[misc] + @overload + def __rrshift__(self: _ArrayInt_co, other: _ArrayLikeInt_co, /) -> NDArray[signedinteger]: ... + @overload + def __rrshift__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __rrshift__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload + def __and__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> NDArray[np.bool]: ... # type: ignore[misc] + @overload + def __and__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, /) -> NDArray[unsignedinteger]: ... # type: ignore[misc] + @overload + def __and__(self: _ArrayInt_co, other: _ArrayLikeInt_co, /) -> NDArray[signedinteger]: ... + @overload + def __and__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __and__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload + def __rand__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> NDArray[np.bool]: ... # type: ignore[misc] + @overload + def __rand__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, /) -> NDArray[unsignedinteger]: ... # type: ignore[misc] + @overload + def __rand__(self: _ArrayInt_co, other: _ArrayLikeInt_co, /) -> NDArray[signedinteger]: ... + @overload + def __rand__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __rand__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload + def __xor__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> NDArray[np.bool]: ... # type: ignore[misc] + @overload + def __xor__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, /) -> NDArray[unsignedinteger]: ... # type: ignore[misc] + @overload + def __xor__(self: _ArrayInt_co, other: _ArrayLikeInt_co, /) -> NDArray[signedinteger]: ... + @overload + def __xor__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __xor__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload + def __rxor__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> NDArray[np.bool]: ... # type: ignore[misc] + @overload + def __rxor__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, /) -> NDArray[unsignedinteger]: ... # type: ignore[misc] + @overload + def __rxor__(self: _ArrayInt_co, other: _ArrayLikeInt_co, /) -> NDArray[signedinteger]: ... + @overload + def __rxor__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __rxor__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload + def __or__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> NDArray[np.bool]: ... # type: ignore[misc] + @overload + def __or__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, /) -> NDArray[unsignedinteger]: ... # type: ignore[misc] + @overload + def __or__(self: _ArrayInt_co, other: _ArrayLikeInt_co, /) -> NDArray[signedinteger]: ... + @overload + def __or__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __or__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + @overload + def __ror__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> NDArray[np.bool]: ... # type: ignore[misc] + @overload + def __ror__(self: _ArrayUInt_co, other: _ArrayLikeUInt_co, /) -> NDArray[unsignedinteger]: ... # type: ignore[misc] + @overload + def __ror__(self: _ArrayInt_co, other: _ArrayLikeInt_co, /) -> NDArray[signedinteger]: ... + @overload + def __ror__(self: NDArray[object_], other: Any, /) -> Any: ... + @overload + def __ror__(self: NDArray[Any], other: _ArrayLikeObject_co, /) -> Any: ... + + # `np.generic` does not support inplace operations + + # NOTE: Inplace ops generally use "same_kind" casting w.r.t. to the left + # operand. An exception to this rule are unsigned integers though, which + # also accepts a signed integer for the right operand as long it is a 0D + # object and its value is >= 0 + # NOTE: Due to a mypy bug, overloading on e.g. `self: NDArray[SCT_floating]` won't + # work, as this will lead to `false negatives` when using these inplace ops. + @overload + def __iadd__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __iadd__(self: NDArray[integer], other: _ArrayLikeInt_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __iadd__(self: NDArray[floating], other: _ArrayLikeFloat_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __iadd__(self: NDArray[complexfloating], other: _ArrayLikeComplex_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __iadd__(self: NDArray[timedelta64 | datetime64], other: _ArrayLikeTD64_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __iadd__(self: NDArray[bytes_], other: _ArrayLikeBytes_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __iadd__( + self: ndarray[Any, dtype[str_] | dtypes.StringDType], + other: _ArrayLikeStr_co | _ArrayLikeString_co, + /, + ) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __iadd__(self: NDArray[object_], other: Any, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + + # + @overload + def __isub__(self: NDArray[integer], other: _ArrayLikeInt_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __isub__(self: NDArray[floating], other: _ArrayLikeFloat_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __isub__(self: NDArray[complexfloating], other: _ArrayLikeComplex_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __isub__(self: NDArray[timedelta64 | datetime64], other: _ArrayLikeTD64_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __isub__(self: NDArray[object_], other: Any, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + + # + @overload + def __imul__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __imul__( + self: ndarray[Any, dtype[integer | character] | dtypes.StringDType], other: _ArrayLikeInt_co, / + ) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __imul__(self: NDArray[floating | timedelta64], other: _ArrayLikeFloat_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __imul__(self: NDArray[complexfloating], other: _ArrayLikeComplex_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __imul__(self: NDArray[object_], other: Any, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + + @overload + def __ipow__(self: NDArray[integer], other: _ArrayLikeInt_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __ipow__(self: NDArray[floating], other: _ArrayLikeFloat_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __ipow__(self: NDArray[complexfloating], other: _ArrayLikeComplex_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __ipow__(self: NDArray[object_], other: Any, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + + # + @overload + def __itruediv__(self: NDArray[floating | timedelta64], other: _ArrayLikeFloat_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __itruediv__(self: NDArray[complexfloating], other: _ArrayLikeComplex_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __itruediv__(self: NDArray[object_], other: Any, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + + # keep in sync with `__imod__` + @overload + def __ifloordiv__(self: NDArray[integer], other: _ArrayLikeInt_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __ifloordiv__(self: NDArray[floating | timedelta64], other: _ArrayLikeFloat_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __ifloordiv__(self: NDArray[object_], other: Any, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + + # keep in sync with `__ifloordiv__` + @overload + def __imod__(self: NDArray[integer], other: _ArrayLikeInt_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __imod__(self: NDArray[floating], other: _ArrayLikeFloat_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __imod__( + self: NDArray[timedelta64], + other: _SupportsArray[_dtype[timedelta64]] | _NestedSequence[_SupportsArray[_dtype[timedelta64]]], + /, + ) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __imod__(self: NDArray[object_], other: Any, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + + # keep in sync with `__irshift__` + @overload + def __ilshift__(self: NDArray[integer], other: _ArrayLikeInt_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __ilshift__(self: NDArray[object_], other: Any, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + + # keep in sync with `__ilshift__` + @overload + def __irshift__(self: NDArray[integer], other: _ArrayLikeInt_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __irshift__(self: NDArray[object_], other: Any, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + + # keep in sync with `__ixor__` and `__ior__` + @overload + def __iand__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __iand__(self: NDArray[integer], other: _ArrayLikeInt_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __iand__(self: NDArray[object_], other: Any, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + + # keep in sync with `__iand__` and `__ior__` + @overload + def __ixor__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __ixor__(self: NDArray[integer], other: _ArrayLikeInt_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __ixor__(self: NDArray[object_], other: Any, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + + # keep in sync with `__iand__` and `__ixor__` + @overload + def __ior__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __ior__(self: NDArray[integer], other: _ArrayLikeInt_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __ior__(self: NDArray[object_], other: Any, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + + # + @overload + def __imatmul__(self: NDArray[np.bool], other: _ArrayLikeBool_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __imatmul__(self: NDArray[integer], other: _ArrayLikeInt_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __imatmul__(self: NDArray[floating], other: _ArrayLikeFloat_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __imatmul__(self: NDArray[complexfloating], other: _ArrayLikeComplex_co, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + @overload + def __imatmul__(self: NDArray[object_], other: Any, /) -> ndarray[_ShapeT_co, _DTypeT_co]: ... + + # + def __dlpack__( + self: NDArray[number], + /, + *, + stream: int | Any | None = None, + max_version: tuple[int, int] | None = None, + dl_device: tuple[int, int] | None = None, + copy: builtins.bool | None = None, + ) -> CapsuleType: ... + def __dlpack_device__(self, /) -> tuple[L[1], L[0]]: ... + + # Keep `dtype` at the bottom to avoid name conflicts with `np.dtype` + @property + def dtype(self) -> _DTypeT_co: ... + +# NOTE: while `np.generic` is not technically an instance of `ABCMeta`, +# the `@abstractmethod` decorator is herein used to (forcefully) deny +# the creation of `np.generic` instances. +# The `# type: ignore` comments are necessary to silence mypy errors regarding +# the missing `ABCMeta` metaclass. +# See https://github.com/numpy/numpy-stubs/pull/80 for more details. +class generic(_ArrayOrScalarCommon, Generic[_ItemT_co]): + @abstractmethod + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + def __hash__(self) -> int: ... + @overload + def __array__(self, dtype: None = None, /) -> ndarray[tuple[()], dtype[Self]]: ... + @overload + def __array__(self, dtype: _DTypeT, /) -> ndarray[tuple[()], _DTypeT]: ... + if sys.version_info >= (3, 12): + def __buffer__(self, flags: int, /) -> memoryview: ... + + @property + def base(self) -> None: ... + @property + def ndim(self) -> L[0]: ... + @property + def size(self) -> L[1]: ... + @property + def shape(self) -> tuple[()]: ... + @property + def strides(self) -> tuple[()]: ... + @property + def flat(self) -> flatiter[ndarray[tuple[int], dtype[Self]]]: ... + + @overload + def item(self, /) -> _ItemT_co: ... + @overload + def item(self, arg0: L[0, -1] | tuple[L[0, -1]] | tuple[()] = ..., /) -> _ItemT_co: ... + def tolist(self, /) -> _ItemT_co: ... + + def byteswap(self, inplace: L[False] = ...) -> Self: ... + + @overload + def astype( + self, + dtype: _DTypeLike[_ScalarT], + order: _OrderKACF = ..., + casting: _CastingKind = ..., + subok: builtins.bool = ..., + copy: builtins.bool | _CopyMode = ..., + ) -> _ScalarT: ... + @overload + def astype( + self, + dtype: DTypeLike, + order: _OrderKACF = ..., + casting: _CastingKind = ..., + subok: builtins.bool = ..., + copy: builtins.bool | _CopyMode = ..., + ) -> Any: ... + + # NOTE: `view` will perform a 0D->scalar cast, + # thus the array `type` is irrelevant to the output type + @overload + def view(self, type: type[NDArray[Any]] = ...) -> Self: ... + @overload + def view( + self, + dtype: _DTypeLike[_ScalarT], + type: type[NDArray[Any]] = ..., + ) -> _ScalarT: ... + @overload + def view( + self, + dtype: DTypeLike, + type: type[NDArray[Any]] = ..., + ) -> Any: ... + + @overload + def getfield( + self, + dtype: _DTypeLike[_ScalarT], + offset: SupportsIndex = ... + ) -> _ScalarT: ... + @overload + def getfield( + self, + dtype: DTypeLike, + offset: SupportsIndex = ... + ) -> Any: ... + + @overload + def take( # type: ignore[misc] + self, + indices: _IntLike_co, + axis: SupportsIndex | None = ..., + out: None = ..., + mode: _ModeKind = ..., + ) -> Self: ... + @overload + def take( # type: ignore[misc] + self, + indices: _ArrayLikeInt_co, + axis: SupportsIndex | None = ..., + out: None = ..., + mode: _ModeKind = ..., + ) -> NDArray[Self]: ... + @overload + def take( + self, + indices: _ArrayLikeInt_co, + axis: SupportsIndex | None = ..., + out: _ArrayT = ..., + mode: _ModeKind = ..., + ) -> _ArrayT: ... + + def repeat(self, repeats: _ArrayLikeInt_co, axis: SupportsIndex | None = None) -> ndarray[tuple[int], dtype[Self]]: ... + def flatten(self, /, order: _OrderKACF = "C") -> ndarray[tuple[int], dtype[Self]]: ... + def ravel(self, /, order: _OrderKACF = "C") -> ndarray[tuple[int], dtype[Self]]: ... + + @overload # (() | []) + def reshape( + self, + shape: tuple[()] | list[Never], + /, + *, + order: _OrderACF = "C", + copy: builtins.bool | None = None, + ) -> Self: ... + @overload # ((1, *(1, ...))@_ShapeT) + def reshape( + self, + shape: _1NShapeT, + /, + *, + order: _OrderACF = "C", + copy: builtins.bool | None = None, + ) -> ndarray[_1NShapeT, dtype[Self]]: ... + @overload # (Sequence[index, ...]) # not recommended + def reshape( + self, + shape: Sequence[SupportsIndex], + /, + *, + order: _OrderACF = "C", + copy: builtins.bool | None = None, + ) -> Self | ndarray[tuple[L[1], ...], dtype[Self]]: ... + @overload # _(index) + def reshape( + self, + size1: SupportsIndex, + /, + *, + order: _OrderACF = "C", + copy: builtins.bool | None = None, + ) -> ndarray[tuple[L[1]], dtype[Self]]: ... + @overload # _(index, index) + def reshape( + self, + size1: SupportsIndex, + size2: SupportsIndex, + /, + *, + order: _OrderACF = "C", + copy: builtins.bool | None = None, + ) -> ndarray[tuple[L[1], L[1]], dtype[Self]]: ... + @overload # _(index, index, index) + def reshape( + self, + size1: SupportsIndex, + size2: SupportsIndex, + size3: SupportsIndex, + /, + *, + order: _OrderACF = "C", + copy: builtins.bool | None = None, + ) -> ndarray[tuple[L[1], L[1], L[1]], dtype[Self]]: ... + @overload # _(index, index, index, index) + def reshape( + self, + size1: SupportsIndex, + size2: SupportsIndex, + size3: SupportsIndex, + size4: SupportsIndex, + /, + *, + order: _OrderACF = "C", + copy: builtins.bool | None = None, + ) -> ndarray[tuple[L[1], L[1], L[1], L[1]], dtype[Self]]: ... + @overload # _(index, index, index, index, index, *index) # ndim >= 5 + def reshape( + self, + size1: SupportsIndex, + size2: SupportsIndex, + size3: SupportsIndex, + size4: SupportsIndex, + size5: SupportsIndex, + /, + *sizes6_: SupportsIndex, + order: _OrderACF = "C", + copy: builtins.bool | None = None, + ) -> ndarray[tuple[L[1], L[1], L[1], L[1], L[1], *tuple[L[1], ...]], dtype[Self]]: ... + + def squeeze(self, axis: L[0] | tuple[()] | None = ...) -> Self: ... + def transpose(self, axes: tuple[()] | None = ..., /) -> Self: ... + + @overload + def all( + self, + /, + axis: L[0, -1] | tuple[()] | None = None, + out: None = None, + keepdims: SupportsIndex = False, + *, + where: builtins.bool | np.bool | ndarray[tuple[()], dtype[np.bool]] = True + ) -> np.bool: ... + @overload + def all( + self, + /, + axis: L[0, -1] | tuple[()] | None, + out: ndarray[tuple[()], dtype[_ScalarT]], + keepdims: SupportsIndex = False, + *, + where: builtins.bool | np.bool | ndarray[tuple[()], dtype[np.bool]] = True, + ) -> _ScalarT: ... + @overload + def all( + self, + /, + axis: L[0, -1] | tuple[()] | None = None, + *, + out: ndarray[tuple[()], dtype[_ScalarT]], + keepdims: SupportsIndex = False, + where: builtins.bool | np.bool | ndarray[tuple[()], dtype[np.bool]] = True, + ) -> _ScalarT: ... + + @overload + def any( + self, + /, + axis: L[0, -1] | tuple[()] | None = None, + out: None = None, + keepdims: SupportsIndex = False, + *, + where: builtins.bool | np.bool | ndarray[tuple[()], dtype[np.bool]] = True + ) -> np.bool: ... + @overload + def any( + self, + /, + axis: L[0, -1] | tuple[()] | None, + out: ndarray[tuple[()], dtype[_ScalarT]], + keepdims: SupportsIndex = False, + *, + where: builtins.bool | np.bool | ndarray[tuple[()], dtype[np.bool]] = True, + ) -> _ScalarT: ... + @overload + def any( + self, + /, + axis: L[0, -1] | tuple[()] | None = None, + *, + out: ndarray[tuple[()], dtype[_ScalarT]], + keepdims: SupportsIndex = False, + where: builtins.bool | np.bool | ndarray[tuple[()], dtype[np.bool]] = True, + ) -> _ScalarT: ... + + # Keep `dtype` at the bottom to avoid name conflicts with `np.dtype` + @property + def dtype(self) -> _dtype[Self]: ... + +class number(generic[_NumberItemT_co], Generic[_NBit, _NumberItemT_co]): + @abstractmethod + def __init__(self, value: _NumberItemT_co, /) -> None: ... + def __class_getitem__(cls, item: Any, /) -> GenericAlias: ... + + def __neg__(self) -> Self: ... + def __pos__(self) -> Self: ... + def __abs__(self) -> Self: ... + + __add__: _NumberOp + __radd__: _NumberOp + __sub__: _NumberOp + __rsub__: _NumberOp + __mul__: _NumberOp + __rmul__: _NumberOp + __floordiv__: _NumberOp + __rfloordiv__: _NumberOp + __pow__: _NumberOp + __rpow__: _NumberOp + __truediv__: _NumberOp + __rtruediv__: _NumberOp + + __lt__: _ComparisonOpLT[_NumberLike_co, _ArrayLikeNumber_co] + __le__: _ComparisonOpLE[_NumberLike_co, _ArrayLikeNumber_co] + __gt__: _ComparisonOpGT[_NumberLike_co, _ArrayLikeNumber_co] + __ge__: _ComparisonOpGE[_NumberLike_co, _ArrayLikeNumber_co] + +class bool(generic[_BoolItemT_co], Generic[_BoolItemT_co]): + @property + def itemsize(self) -> L[1]: ... + @property + def nbytes(self) -> L[1]: ... + @property + def real(self) -> Self: ... + @property + def imag(self) -> np.bool[L[False]]: ... + + @overload # mypy bug workaround: https://github.com/numpy/numpy/issues/29245 + def __init__(self: np.bool[builtins.bool], value: Never, /) -> None: ... + @overload + def __init__(self: np.bool[L[False]], value: _Falsy = ..., /) -> None: ... + @overload + def __init__(self: np.bool[L[True]], value: _Truthy, /) -> None: ... + @overload + def __init__(self: np.bool[builtins.bool], value: object, /) -> None: ... + + def __bool__(self, /) -> _BoolItemT_co: ... + + @overload + def __int__(self: np.bool[L[False]], /) -> L[0]: ... + @overload + def __int__(self: np.bool[L[True]], /) -> L[1]: ... + @overload + def __int__(self, /) -> L[0, 1]: ... + + def __abs__(self) -> Self: ... + + @overload + def __invert__(self: np.bool[L[False]], /) -> np.bool[L[True]]: ... + @overload + def __invert__(self: np.bool[L[True]], /) -> np.bool[L[False]]: ... + @overload + def __invert__(self, /) -> np.bool: ... + + @overload + def __add__(self, other: _NumberT, /) -> _NumberT: ... + @overload + def __add__(self, other: builtins.bool | bool_, /) -> bool_: ... + @overload + def __add__(self, other: int, /) -> int_: ... + @overload + def __add__(self, other: float, /) -> float64: ... + @overload + def __add__(self, other: complex, /) -> complex128: ... + + @overload + def __radd__(self, other: _NumberT, /) -> _NumberT: ... + @overload + def __radd__(self, other: builtins.bool, /) -> bool_: ... + @overload + def __radd__(self, other: int, /) -> int_: ... + @overload + def __radd__(self, other: float, /) -> float64: ... + @overload + def __radd__(self, other: complex, /) -> complex128: ... + + @overload + def __sub__(self, other: _NumberT, /) -> _NumberT: ... + @overload + def __sub__(self, other: int, /) -> int_: ... + @overload + def __sub__(self, other: float, /) -> float64: ... + @overload + def __sub__(self, other: complex, /) -> complex128: ... + + @overload + def __rsub__(self, other: _NumberT, /) -> _NumberT: ... + @overload + def __rsub__(self, other: int, /) -> int_: ... + @overload + def __rsub__(self, other: float, /) -> float64: ... + @overload + def __rsub__(self, other: complex, /) -> complex128: ... + + @overload + def __mul__(self, other: _NumberT, /) -> _NumberT: ... + @overload + def __mul__(self, other: builtins.bool | bool_, /) -> bool_: ... + @overload + def __mul__(self, other: int, /) -> int_: ... + @overload + def __mul__(self, other: float, /) -> float64: ... + @overload + def __mul__(self, other: complex, /) -> complex128: ... + + @overload + def __rmul__(self, other: _NumberT, /) -> _NumberT: ... + @overload + def __rmul__(self, other: builtins.bool, /) -> bool_: ... + @overload + def __rmul__(self, other: int, /) -> int_: ... + @overload + def __rmul__(self, other: float, /) -> float64: ... + @overload + def __rmul__(self, other: complex, /) -> complex128: ... + + @overload + def __pow__(self, other: _NumberT, mod: None = None, /) -> _NumberT: ... + @overload + def __pow__(self, other: builtins.bool | bool_, mod: None = None, /) -> int8: ... + @overload + def __pow__(self, other: int, mod: None = None, /) -> int_: ... + @overload + def __pow__(self, other: float, mod: None = None, /) -> float64: ... + @overload + def __pow__(self, other: complex, mod: None = None, /) -> complex128: ... + + @overload + def __rpow__(self, other: _NumberT, mod: None = None, /) -> _NumberT: ... + @overload + def __rpow__(self, other: builtins.bool, mod: None = None, /) -> int8: ... + @overload + def __rpow__(self, other: int, mod: None = None, /) -> int_: ... + @overload + def __rpow__(self, other: float, mod: None = None, /) -> float64: ... + @overload + def __rpow__(self, other: complex, mod: None = None, /) -> complex128: ... + + @overload + def __truediv__(self, other: _InexactT, /) -> _InexactT: ... + @overload + def __truediv__(self, other: float | integer | bool_, /) -> float64: ... + @overload + def __truediv__(self, other: complex, /) -> complex128: ... + + @overload + def __rtruediv__(self, other: _InexactT, /) -> _InexactT: ... + @overload + def __rtruediv__(self, other: float | integer, /) -> float64: ... + @overload + def __rtruediv__(self, other: complex, /) -> complex128: ... + + @overload + def __floordiv__(self, other: _RealNumberT, /) -> _RealNumberT: ... + @overload + def __floordiv__(self, other: builtins.bool | bool_, /) -> int8: ... + @overload + def __floordiv__(self, other: int, /) -> int_: ... + @overload + def __floordiv__(self, other: float, /) -> float64: ... + + @overload + def __rfloordiv__(self, other: _RealNumberT, /) -> _RealNumberT: ... + @overload + def __rfloordiv__(self, other: builtins.bool, /) -> int8: ... + @overload + def __rfloordiv__(self, other: int, /) -> int_: ... + @overload + def __rfloordiv__(self, other: float, /) -> float64: ... + + # keep in sync with __floordiv__ + @overload + def __mod__(self, other: _RealNumberT, /) -> _RealNumberT: ... + @overload + def __mod__(self, other: builtins.bool | bool_, /) -> int8: ... + @overload + def __mod__(self, other: int, /) -> int_: ... + @overload + def __mod__(self, other: float, /) -> float64: ... + + # keep in sync with __rfloordiv__ + @overload + def __rmod__(self, other: _RealNumberT, /) -> _RealNumberT: ... + @overload + def __rmod__(self, other: builtins.bool, /) -> int8: ... + @overload + def __rmod__(self, other: int, /) -> int_: ... + @overload + def __rmod__(self, other: float, /) -> float64: ... + + # keep in sync with __mod__ + @overload + def __divmod__(self, other: _RealNumberT, /) -> _2Tuple[_RealNumberT]: ... + @overload + def __divmod__(self, other: builtins.bool | bool_, /) -> _2Tuple[int8]: ... + @overload + def __divmod__(self, other: int, /) -> _2Tuple[int_]: ... + @overload + def __divmod__(self, other: float, /) -> _2Tuple[float64]: ... + + # keep in sync with __rmod__ + @overload + def __rdivmod__(self, other: _RealNumberT, /) -> _2Tuple[_RealNumberT]: ... + @overload + def __rdivmod__(self, other: builtins.bool, /) -> _2Tuple[int8]: ... + @overload + def __rdivmod__(self, other: int, /) -> _2Tuple[int_]: ... + @overload + def __rdivmod__(self, other: float, /) -> _2Tuple[float64]: ... + + @overload + def __lshift__(self, other: _IntegerT, /) -> _IntegerT: ... + @overload + def __lshift__(self, other: builtins.bool | bool_, /) -> int8: ... + @overload + def __lshift__(self, other: int, /) -> int_: ... + + @overload + def __rlshift__(self, other: _IntegerT, /) -> _IntegerT: ... + @overload + def __rlshift__(self, other: builtins.bool, /) -> int8: ... + @overload + def __rlshift__(self, other: int, /) -> int_: ... + + # keep in sync with __lshift__ + @overload + def __rshift__(self, other: _IntegerT, /) -> _IntegerT: ... + @overload + def __rshift__(self, other: builtins.bool | bool_, /) -> int8: ... + @overload + def __rshift__(self, other: int, /) -> int_: ... + + # keep in sync with __rlshift__ + @overload + def __rrshift__(self, other: _IntegerT, /) -> _IntegerT: ... + @overload + def __rrshift__(self, other: builtins.bool, /) -> int8: ... + @overload + def __rrshift__(self, other: int, /) -> int_: ... + + @overload + def __and__(self: np.bool[L[False]], other: builtins.bool | np.bool, /) -> np.bool[L[False]]: ... + @overload + def __and__(self, other: L[False] | np.bool[L[False]], /) -> np.bool[L[False]]: ... + @overload + def __and__(self, other: L[True] | np.bool[L[True]], /) -> Self: ... + @overload + def __and__(self, other: builtins.bool | np.bool, /) -> np.bool: ... + @overload + def __and__(self, other: _IntegerT, /) -> _IntegerT: ... + @overload + def __and__(self, other: int, /) -> np.bool | intp: ... + __rand__ = __and__ + + @overload + def __xor__(self: np.bool[L[False]], other: _BoolItemT | np.bool[_BoolItemT], /) -> np.bool[_BoolItemT]: ... + @overload + def __xor__(self: np.bool[L[True]], other: L[True] | np.bool[L[True]], /) -> np.bool[L[False]]: ... + @overload + def __xor__(self, other: L[False] | np.bool[L[False]], /) -> Self: ... + @overload + def __xor__(self, other: builtins.bool | np.bool, /) -> np.bool: ... + @overload + def __xor__(self, other: _IntegerT, /) -> _IntegerT: ... + @overload + def __xor__(self, other: int, /) -> np.bool | intp: ... + __rxor__ = __xor__ + + @overload + def __or__(self: np.bool[L[True]], other: builtins.bool | np.bool, /) -> np.bool[L[True]]: ... + @overload + def __or__(self, other: L[False] | np.bool[L[False]], /) -> Self: ... + @overload + def __or__(self, other: L[True] | np.bool[L[True]], /) -> np.bool[L[True]]: ... + @overload + def __or__(self, other: builtins.bool | np.bool, /) -> np.bool: ... + @overload + def __or__(self, other: _IntegerT, /) -> _IntegerT: ... + @overload + def __or__(self, other: int, /) -> np.bool | intp: ... + __ror__ = __or__ + + __lt__: _ComparisonOpLT[_NumberLike_co, _ArrayLikeNumber_co] + __le__: _ComparisonOpLE[_NumberLike_co, _ArrayLikeNumber_co] + __gt__: _ComparisonOpGT[_NumberLike_co, _ArrayLikeNumber_co] + __ge__: _ComparisonOpGE[_NumberLike_co, _ArrayLikeNumber_co] + +# NOTE: This should _not_ be `Final` or a `TypeAlias` +bool_ = bool + +# NOTE: The `object_` constructor returns the passed object, so instances with type +# `object_` cannot exists (at runtime). +# NOTE: Because mypy has some long-standing bugs related to `__new__`, `object_` can't +# be made generic. +@final +class object_(_RealMixin, generic): + @overload + def __new__(cls, nothing_to_see_here: None = None, /) -> None: ... # type: ignore[misc] + @overload + def __new__(cls, stringy: _AnyStr, /) -> _AnyStr: ... # type: ignore[misc] + @overload + def __new__(cls, array: ndarray[_ShapeT, Any], /) -> ndarray[_ShapeT, dtype[Self]]: ... # type: ignore[misc] + @overload + def __new__(cls, sequence: SupportsLenAndGetItem[object], /) -> NDArray[Self]: ... # type: ignore[misc] + @overload + def __new__(cls, value: _T, /) -> _T: ... # type: ignore[misc] + @overload # catch-all + def __new__(cls, value: Any = ..., /) -> object | NDArray[Self]: ... # type: ignore[misc] + def __init__(self, value: object = ..., /) -> None: ... + def __hash__(self, /) -> int: ... + def __abs__(self, /) -> object_: ... # this affects NDArray[object_].__abs__ + def __call__(self, /, *args: object, **kwargs: object) -> Any: ... + + if sys.version_info >= (3, 12): + def __release_buffer__(self, buffer: memoryview, /) -> None: ... + +class integer(_IntegralMixin, _RoundMixin, number[_NBit, int]): + @abstractmethod + def __init__(self, value: _ConvertibleToInt = ..., /) -> None: ... + + # NOTE: `bit_count` and `__index__` are technically defined in the concrete subtypes + def bit_count(self, /) -> int: ... + def __index__(self, /) -> int: ... + def __invert__(self, /) -> Self: ... + + __truediv__: _IntTrueDiv[_NBit] + __rtruediv__: _IntTrueDiv[_NBit] + def __mod__(self, value: _IntLike_co, /) -> integer: ... + def __rmod__(self, value: _IntLike_co, /) -> integer: ... + # Ensure that objects annotated as `integer` support bit-wise operations + def __lshift__(self, other: _IntLike_co, /) -> integer: ... + def __rlshift__(self, other: _IntLike_co, /) -> integer: ... + def __rshift__(self, other: _IntLike_co, /) -> integer: ... + def __rrshift__(self, other: _IntLike_co, /) -> integer: ... + def __and__(self, other: _IntLike_co, /) -> integer: ... + def __rand__(self, other: _IntLike_co, /) -> integer: ... + def __or__(self, other: _IntLike_co, /) -> integer: ... + def __ror__(self, other: _IntLike_co, /) -> integer: ... + def __xor__(self, other: _IntLike_co, /) -> integer: ... + def __rxor__(self, other: _IntLike_co, /) -> integer: ... + +class signedinteger(integer[_NBit1]): + def __init__(self, value: _ConvertibleToInt = ..., /) -> None: ... + + __add__: _SignedIntOp[_NBit1] + __radd__: _SignedIntOp[_NBit1] + __sub__: _SignedIntOp[_NBit1] + __rsub__: _SignedIntOp[_NBit1] + __mul__: _SignedIntOp[_NBit1] + __rmul__: _SignedIntOp[_NBit1] + __floordiv__: _SignedIntOp[_NBit1] + __rfloordiv__: _SignedIntOp[_NBit1] + __pow__: _SignedIntOp[_NBit1] + __rpow__: _SignedIntOp[_NBit1] + __lshift__: _SignedIntBitOp[_NBit1] + __rlshift__: _SignedIntBitOp[_NBit1] + __rshift__: _SignedIntBitOp[_NBit1] + __rrshift__: _SignedIntBitOp[_NBit1] + __and__: _SignedIntBitOp[_NBit1] + __rand__: _SignedIntBitOp[_NBit1] + __xor__: _SignedIntBitOp[_NBit1] + __rxor__: _SignedIntBitOp[_NBit1] + __or__: _SignedIntBitOp[_NBit1] + __ror__: _SignedIntBitOp[_NBit1] + __mod__: _SignedIntMod[_NBit1] + __rmod__: _SignedIntMod[_NBit1] + __divmod__: _SignedIntDivMod[_NBit1] + __rdivmod__: _SignedIntDivMod[_NBit1] + +int8 = signedinteger[_8Bit] +int16 = signedinteger[_16Bit] +int32 = signedinteger[_32Bit] +int64 = signedinteger[_64Bit] + +byte = signedinteger[_NBitByte] +short = signedinteger[_NBitShort] +intc = signedinteger[_NBitIntC] +intp = signedinteger[_NBitIntP] +int_ = intp +long = signedinteger[_NBitLong] +longlong = signedinteger[_NBitLongLong] + +class unsignedinteger(integer[_NBit1]): + # NOTE: `uint64 + signedinteger -> float64` + def __init__(self, value: _ConvertibleToInt = ..., /) -> None: ... + + __add__: _UnsignedIntOp[_NBit1] + __radd__: _UnsignedIntOp[_NBit1] + __sub__: _UnsignedIntOp[_NBit1] + __rsub__: _UnsignedIntOp[_NBit1] + __mul__: _UnsignedIntOp[_NBit1] + __rmul__: _UnsignedIntOp[_NBit1] + __floordiv__: _UnsignedIntOp[_NBit1] + __rfloordiv__: _UnsignedIntOp[_NBit1] + __pow__: _UnsignedIntOp[_NBit1] + __rpow__: _UnsignedIntOp[_NBit1] + __lshift__: _UnsignedIntBitOp[_NBit1] + __rlshift__: _UnsignedIntBitOp[_NBit1] + __rshift__: _UnsignedIntBitOp[_NBit1] + __rrshift__: _UnsignedIntBitOp[_NBit1] + __and__: _UnsignedIntBitOp[_NBit1] + __rand__: _UnsignedIntBitOp[_NBit1] + __xor__: _UnsignedIntBitOp[_NBit1] + __rxor__: _UnsignedIntBitOp[_NBit1] + __or__: _UnsignedIntBitOp[_NBit1] + __ror__: _UnsignedIntBitOp[_NBit1] + __mod__: _UnsignedIntMod[_NBit1] + __rmod__: _UnsignedIntMod[_NBit1] + __divmod__: _UnsignedIntDivMod[_NBit1] + __rdivmod__: _UnsignedIntDivMod[_NBit1] + +uint8: TypeAlias = unsignedinteger[_8Bit] +uint16: TypeAlias = unsignedinteger[_16Bit] +uint32: TypeAlias = unsignedinteger[_32Bit] +uint64: TypeAlias = unsignedinteger[_64Bit] + +ubyte: TypeAlias = unsignedinteger[_NBitByte] +ushort: TypeAlias = unsignedinteger[_NBitShort] +uintc: TypeAlias = unsignedinteger[_NBitIntC] +uintp: TypeAlias = unsignedinteger[_NBitIntP] +uint: TypeAlias = uintp +ulong: TypeAlias = unsignedinteger[_NBitLong] +ulonglong: TypeAlias = unsignedinteger[_NBitLongLong] + +class inexact(number[_NBit, _InexactItemT_co], Generic[_NBit, _InexactItemT_co]): + @abstractmethod + def __init__(self, value: _InexactItemT_co | None = ..., /) -> None: ... + +class floating(_RealMixin, _RoundMixin, inexact[_NBit1, float]): + def __init__(self, value: _ConvertibleToFloat | None = ..., /) -> None: ... + + __add__: _FloatOp[_NBit1] + __radd__: _FloatOp[_NBit1] + __sub__: _FloatOp[_NBit1] + __rsub__: _FloatOp[_NBit1] + __mul__: _FloatOp[_NBit1] + __rmul__: _FloatOp[_NBit1] + __truediv__: _FloatOp[_NBit1] + __rtruediv__: _FloatOp[_NBit1] + __floordiv__: _FloatOp[_NBit1] + __rfloordiv__: _FloatOp[_NBit1] + __pow__: _FloatOp[_NBit1] + __rpow__: _FloatOp[_NBit1] + __mod__: _FloatMod[_NBit1] + __rmod__: _FloatMod[_NBit1] + __divmod__: _FloatDivMod[_NBit1] + __rdivmod__: _FloatDivMod[_NBit1] + + # NOTE: `is_integer` and `as_integer_ratio` are technically defined in the concrete subtypes + def is_integer(self, /) -> builtins.bool: ... + def as_integer_ratio(self, /) -> tuple[int, int]: ... + +float16: TypeAlias = floating[_16Bit] +float32: TypeAlias = floating[_32Bit] + +# either a C `double`, `float`, or `longdouble` +class float64(floating[_64Bit], float): # type: ignore[misc] + def __new__(cls, x: _ConvertibleToFloat | None = ..., /) -> Self: ... + + # + @property + def itemsize(self) -> L[8]: ... + @property + def nbytes(self) -> L[8]: ... + + # overrides for `floating` and `builtins.float` compatibility (`_RealMixin` doesn't work) + @property + def real(self) -> Self: ... + @property + def imag(self) -> Self: ... + def conjugate(self) -> Self: ... + def __getformat__(self, typestr: L["double", "float"], /) -> str: ... + def __getnewargs__(self, /) -> tuple[float]: ... + + # float64-specific operator overrides + @overload + def __add__(self, other: _Float64_co, /) -> float64: ... + @overload + def __add__(self, other: complexfloating[_64Bit, _64Bit], /) -> complex128: ... + @overload + def __add__(self, other: complexfloating[_NBit1, _NBit2], /) -> complexfloating[_NBit1 | _64Bit, _NBit2 | _64Bit]: ... + @overload + def __add__(self, other: complex, /) -> float64 | complex128: ... + @overload + def __radd__(self, other: _Float64_co, /) -> float64: ... + @overload + def __radd__(self, other: complexfloating[_64Bit, _64Bit], /) -> complex128: ... + @overload + def __radd__(self, other: complexfloating[_NBit1, _NBit2], /) -> complexfloating[_NBit1 | _64Bit, _NBit2 | _64Bit]: ... + @overload + def __radd__(self, other: complex, /) -> float64 | complex128: ... + + @overload + def __sub__(self, other: _Float64_co, /) -> float64: ... + @overload + def __sub__(self, other: complexfloating[_64Bit, _64Bit], /) -> complex128: ... + @overload + def __sub__(self, other: complexfloating[_NBit1, _NBit2], /) -> complexfloating[_NBit1 | _64Bit, _NBit2 | _64Bit]: ... + @overload + def __sub__(self, other: complex, /) -> float64 | complex128: ... + @overload + def __rsub__(self, other: _Float64_co, /) -> float64: ... + @overload + def __rsub__(self, other: complexfloating[_64Bit, _64Bit], /) -> complex128: ... + @overload + def __rsub__(self, other: complexfloating[_NBit1, _NBit2], /) -> complexfloating[_NBit1 | _64Bit, _NBit2 | _64Bit]: ... + @overload + def __rsub__(self, other: complex, /) -> float64 | complex128: ... + + @overload + def __mul__(self, other: _Float64_co, /) -> float64: ... + @overload + def __mul__(self, other: complexfloating[_64Bit, _64Bit], /) -> complex128: ... + @overload + def __mul__(self, other: complexfloating[_NBit1, _NBit2], /) -> complexfloating[_NBit1 | _64Bit, _NBit2 | _64Bit]: ... + @overload + def __mul__(self, other: complex, /) -> float64 | complex128: ... + @overload + def __rmul__(self, other: _Float64_co, /) -> float64: ... + @overload + def __rmul__(self, other: complexfloating[_64Bit, _64Bit], /) -> complex128: ... + @overload + def __rmul__(self, other: complexfloating[_NBit1, _NBit2], /) -> complexfloating[_NBit1 | _64Bit, _NBit2 | _64Bit]: ... + @overload + def __rmul__(self, other: complex, /) -> float64 | complex128: ... + + @overload + def __truediv__(self, other: _Float64_co, /) -> float64: ... + @overload + def __truediv__(self, other: complexfloating[_64Bit, _64Bit], /) -> complex128: ... + @overload + def __truediv__(self, other: complexfloating[_NBit1, _NBit2], /) -> complexfloating[_NBit1 | _64Bit, _NBit2 | _64Bit]: ... + @overload + def __truediv__(self, other: complex, /) -> float64 | complex128: ... + @overload + def __rtruediv__(self, other: _Float64_co, /) -> float64: ... + @overload + def __rtruediv__(self, other: complexfloating[_64Bit, _64Bit], /) -> complex128: ... + @overload + def __rtruediv__(self, other: complexfloating[_NBit1, _NBit2], /) -> complexfloating[_NBit1 | _64Bit, _NBit2 | _64Bit]: ... + @overload + def __rtruediv__(self, other: complex, /) -> float64 | complex128: ... + + @overload + def __floordiv__(self, other: _Float64_co, /) -> float64: ... + @overload + def __floordiv__(self, other: complexfloating[_64Bit, _64Bit], /) -> complex128: ... + @overload + def __floordiv__(self, other: complexfloating[_NBit1, _NBit2], /) -> complexfloating[_NBit1 | _64Bit, _NBit2 | _64Bit]: ... + @overload + def __floordiv__(self, other: complex, /) -> float64 | complex128: ... + @overload + def __rfloordiv__(self, other: _Float64_co, /) -> float64: ... + @overload + def __rfloordiv__(self, other: complexfloating[_64Bit, _64Bit], /) -> complex128: ... + @overload + def __rfloordiv__(self, other: complexfloating[_NBit1, _NBit2], /) -> complexfloating[_NBit1 | _64Bit, _NBit2 | _64Bit]: ... + @overload + def __rfloordiv__(self, other: complex, /) -> float64 | complex128: ... + + @overload + def __pow__(self, other: _Float64_co, mod: None = None, /) -> float64: ... + @overload + def __pow__(self, other: complexfloating[_64Bit, _64Bit], mod: None = None, /) -> complex128: ... + @overload + def __pow__( + self, other: complexfloating[_NBit1, _NBit2], mod: None = None, / + ) -> complexfloating[_NBit1 | _64Bit, _NBit2 | _64Bit]: ... + @overload + def __pow__(self, other: complex, mod: None = None, /) -> float64 | complex128: ... + @overload + def __rpow__(self, other: _Float64_co, mod: None = None, /) -> float64: ... + @overload + def __rpow__(self, other: complexfloating[_64Bit, _64Bit], mod: None = None, /) -> complex128: ... + @overload + def __rpow__( + self, other: complexfloating[_NBit1, _NBit2], mod: None = None, / + ) -> complexfloating[_NBit1 | _64Bit, _NBit2 | _64Bit]: ... + @overload + def __rpow__(self, other: complex, mod: None = None, /) -> float64 | complex128: ... + + def __mod__(self, other: _Float64_co, /) -> float64: ... # type: ignore[override] + def __rmod__(self, other: _Float64_co, /) -> float64: ... # type: ignore[override] + + def __divmod__(self, other: _Float64_co, /) -> _2Tuple[float64]: ... # type: ignore[override] + def __rdivmod__(self, other: _Float64_co, /) -> _2Tuple[float64]: ... # type: ignore[override] + +half: TypeAlias = floating[_NBitHalf] +single: TypeAlias = floating[_NBitSingle] +double: TypeAlias = floating[_NBitDouble] +longdouble: TypeAlias = floating[_NBitLongDouble] + +# The main reason for `complexfloating` having two typevars is cosmetic. +# It is used to clarify why `complex128`s precision is `_64Bit`, the latter +# describing the two 64 bit floats representing its real and imaginary component + +class complexfloating(inexact[_NBit1, complex], Generic[_NBit1, _NBit2]): + @overload + def __init__( + self, + real: complex | SupportsComplex | SupportsFloat | SupportsIndex = ..., + imag: complex | SupportsFloat | SupportsIndex = ..., + /, + ) -> None: ... + @overload + def __init__(self, real: _ConvertibleToComplex | None = ..., /) -> None: ... + + @property + def real(self) -> floating[_NBit1]: ... # type: ignore[override] + @property + def imag(self) -> floating[_NBit2]: ... # type: ignore[override] + + # NOTE: `__complex__` is technically defined in the concrete subtypes + def __complex__(self, /) -> complex: ... + def __abs__(self, /) -> floating[_NBit1 | _NBit2]: ... # type: ignore[override] + + @overload + def __add__(self, other: _Complex64_co, /) -> complexfloating[_NBit1, _NBit2]: ... + @overload + def __add__(self, other: complex | float64 | complex128, /) -> complexfloating[_NBit1, _NBit2] | complex128: ... + @overload + def __add__(self, other: number[_NBit], /) -> complexfloating[_NBit1, _NBit2] | complexfloating[_NBit, _NBit]: ... + @overload + def __radd__(self, other: _Complex64_co, /) -> complexfloating[_NBit1, _NBit2]: ... + @overload + def __radd__(self, other: complex, /) -> complexfloating[_NBit1, _NBit2] | complex128: ... + @overload + def __radd__(self, other: number[_NBit], /) -> complexfloating[_NBit1, _NBit2] | complexfloating[_NBit, _NBit]: ... + + @overload + def __sub__(self, other: _Complex64_co, /) -> complexfloating[_NBit1, _NBit2]: ... + @overload + def __sub__(self, other: complex | float64 | complex128, /) -> complexfloating[_NBit1, _NBit2] | complex128: ... + @overload + def __sub__(self, other: number[_NBit], /) -> complexfloating[_NBit1, _NBit2] | complexfloating[_NBit, _NBit]: ... + @overload + def __rsub__(self, other: _Complex64_co, /) -> complexfloating[_NBit1, _NBit2]: ... + @overload + def __rsub__(self, other: complex, /) -> complexfloating[_NBit1, _NBit2] | complex128: ... + @overload + def __rsub__(self, other: number[_NBit], /) -> complexfloating[_NBit1, _NBit2] | complexfloating[_NBit, _NBit]: ... + + @overload + def __mul__(self, other: _Complex64_co, /) -> complexfloating[_NBit1, _NBit2]: ... + @overload + def __mul__(self, other: complex | float64 | complex128, /) -> complexfloating[_NBit1, _NBit2] | complex128: ... + @overload + def __mul__(self, other: number[_NBit], /) -> complexfloating[_NBit1, _NBit2] | complexfloating[_NBit, _NBit]: ... + @overload + def __rmul__(self, other: _Complex64_co, /) -> complexfloating[_NBit1, _NBit2]: ... + @overload + def __rmul__(self, other: complex, /) -> complexfloating[_NBit1, _NBit2] | complex128: ... + @overload + def __rmul__(self, other: number[_NBit], /) -> complexfloating[_NBit1, _NBit2] | complexfloating[_NBit, _NBit]: ... + + @overload + def __truediv__(self, other: _Complex64_co, /) -> complexfloating[_NBit1, _NBit2]: ... + @overload + def __truediv__(self, other: complex | float64 | complex128, /) -> complexfloating[_NBit1, _NBit2] | complex128: ... + @overload + def __truediv__(self, other: number[_NBit], /) -> complexfloating[_NBit1, _NBit2] | complexfloating[_NBit, _NBit]: ... + @overload + def __rtruediv__(self, other: _Complex64_co, /) -> complexfloating[_NBit1, _NBit2]: ... + @overload + def __rtruediv__(self, other: complex, /) -> complexfloating[_NBit1, _NBit2] | complex128: ... + @overload + def __rtruediv__(self, other: number[_NBit], /) -> complexfloating[_NBit1, _NBit2] | complexfloating[_NBit, _NBit]: ... + + @overload + def __pow__(self, other: _Complex64_co, mod: None = None, /) -> complexfloating[_NBit1, _NBit2]: ... + @overload + def __pow__( + self, other: complex | float64 | complex128, mod: None = None, / + ) -> complexfloating[_NBit1, _NBit2] | complex128: ... + @overload + def __pow__( + self, other: number[_NBit], mod: None = None, / + ) -> complexfloating[_NBit1, _NBit2] | complexfloating[_NBit, _NBit]: ... + @overload + def __rpow__(self, other: _Complex64_co, mod: None = None, /) -> complexfloating[_NBit1, _NBit2]: ... + @overload + def __rpow__(self, other: complex, mod: None = None, /) -> complexfloating[_NBit1, _NBit2] | complex128: ... + @overload + def __rpow__( + self, other: number[_NBit], mod: None = None, / + ) -> complexfloating[_NBit1, _NBit2] | complexfloating[_NBit, _NBit]: ... + +complex64: TypeAlias = complexfloating[_32Bit, _32Bit] + +class complex128(complexfloating[_64Bit, _64Bit], complex): # type: ignore[misc] + @overload + def __new__( + cls, + real: complex | SupportsComplex | SupportsFloat | SupportsIndex = ..., + imag: complex | SupportsFloat | SupportsIndex = ..., + /, + ) -> Self: ... + @overload + def __new__(cls, real: _ConvertibleToComplex | None = ..., /) -> Self: ... + + # + @property + def itemsize(self) -> L[16]: ... + @property + def nbytes(self) -> L[16]: ... + + # overrides for `floating` and `builtins.float` compatibility + @property + def real(self) -> float64: ... + @property + def imag(self) -> float64: ... + def conjugate(self) -> Self: ... + def __abs__(self) -> float64: ... # type: ignore[override] + def __getnewargs__(self, /) -> tuple[float, float]: ... + + # complex128-specific operator overrides + @overload + def __add__(self, other: _Complex128_co, /) -> complex128: ... + @overload + def __add__(self, other: complexfloating[_NBit1, _NBit2], /) -> complexfloating[_NBit1 | _64Bit, _NBit2 | _64Bit]: ... + def __radd__(self, other: _Complex128_co, /) -> complex128: ... + + @overload + def __sub__(self, other: _Complex128_co, /) -> complex128: ... + @overload + def __sub__(self, other: complexfloating[_NBit1, _NBit2], /) -> complexfloating[_NBit1 | _64Bit, _NBit2 | _64Bit]: ... + def __rsub__(self, other: _Complex128_co, /) -> complex128: ... + + @overload + def __mul__(self, other: _Complex128_co, /) -> complex128: ... + @overload + def __mul__(self, other: complexfloating[_NBit1, _NBit2], /) -> complexfloating[_NBit1 | _64Bit, _NBit2 | _64Bit]: ... + def __rmul__(self, other: _Complex128_co, /) -> complex128: ... + + @overload + def __truediv__(self, other: _Complex128_co, /) -> complex128: ... + @overload + def __truediv__(self, other: complexfloating[_NBit1, _NBit2], /) -> complexfloating[_NBit1 | _64Bit, _NBit2 | _64Bit]: ... + def __rtruediv__(self, other: _Complex128_co, /) -> complex128: ... + + @overload + def __pow__(self, other: _Complex128_co, mod: None = None, /) -> complex128: ... + @overload + def __pow__( + self, other: complexfloating[_NBit1, _NBit2], mod: None = None, / + ) -> complexfloating[_NBit1 | _64Bit, _NBit2 | _64Bit]: ... + def __rpow__(self, other: _Complex128_co, mod: None = None, /) -> complex128: ... + +csingle: TypeAlias = complexfloating[_NBitSingle, _NBitSingle] +cdouble: TypeAlias = complexfloating[_NBitDouble, _NBitDouble] +clongdouble: TypeAlias = complexfloating[_NBitLongDouble, _NBitLongDouble] + +class timedelta64(_IntegralMixin, generic[_TD64ItemT_co], Generic[_TD64ItemT_co]): + @property + def itemsize(self) -> L[8]: ... + @property + def nbytes(self) -> L[8]: ... + + @overload + def __init__(self, value: _TD64ItemT_co | timedelta64[_TD64ItemT_co], /) -> None: ... + @overload + def __init__(self: timedelta64[L[0]], /) -> None: ... + @overload + def __init__(self: timedelta64[None], value: _NaTValue | None, format: _TimeUnitSpec, /) -> None: ... + @overload + def __init__(self: timedelta64[L[0]], value: L[0], format: _TimeUnitSpec[_IntTD64Unit] = ..., /) -> None: ... + @overload + def __init__(self: timedelta64[int], value: _IntLike_co, format: _TimeUnitSpec[_IntTD64Unit] = ..., /) -> None: ... + @overload + def __init__(self: timedelta64[int], value: dt.timedelta, format: _TimeUnitSpec[_IntTimeUnit], /) -> None: ... + @overload + def __init__( + self: timedelta64[dt.timedelta], + value: dt.timedelta | _IntLike_co, + format: _TimeUnitSpec[_NativeTD64Unit] = ..., + /, + ) -> None: ... + @overload + def __init__(self, value: _ConvertibleToTD64, format: _TimeUnitSpec = ..., /) -> None: ... + + # inherited at runtime from `signedinteger` + def __class_getitem__(cls, type_arg: type | object, /) -> GenericAlias: ... + + # NOTE: Only a limited number of units support conversion + # to builtin scalar types: `Y`, `M`, `ns`, `ps`, `fs`, `as` + def __int__(self: timedelta64[int], /) -> int: ... + def __float__(self: timedelta64[int], /) -> float: ... + + def __neg__(self, /) -> Self: ... + def __pos__(self, /) -> Self: ... + def __abs__(self, /) -> Self: ... + + @overload + def __add__(self: timedelta64[None], x: _TD64Like_co, /) -> timedelta64[None]: ... + @overload + def __add__(self: timedelta64[int], x: timedelta64[int | dt.timedelta], /) -> timedelta64[int]: ... + @overload + def __add__(self: timedelta64[int], x: timedelta64, /) -> timedelta64[int | None]: ... + @overload + def __add__(self: timedelta64[dt.timedelta], x: _AnyDateOrTime, /) -> _AnyDateOrTime: ... + @overload + def __add__(self: timedelta64[_AnyTD64Item], x: timedelta64[_AnyTD64Item] | _IntLike_co, /) -> timedelta64[_AnyTD64Item]: ... + @overload + def __add__(self, x: timedelta64[None], /) -> timedelta64[None]: ... + __radd__ = __add__ + + @overload + def __mul__(self: timedelta64[_AnyTD64Item], x: int | np.integer | np.bool, /) -> timedelta64[_AnyTD64Item]: ... + @overload + def __mul__(self: timedelta64[_AnyTD64Item], x: float | np.floating, /) -> timedelta64[_AnyTD64Item | None]: ... + @overload + def __mul__(self, x: float | np.floating | np.integer | np.bool, /) -> timedelta64: ... + __rmul__ = __mul__ + + @overload + def __mod__(self, x: timedelta64[L[0] | None], /) -> timedelta64[None]: ... + @overload + def __mod__(self: timedelta64[None], x: timedelta64, /) -> timedelta64[None]: ... + @overload + def __mod__(self: timedelta64[int], x: timedelta64[int | dt.timedelta], /) -> timedelta64[int | None]: ... + @overload + def __mod__(self: timedelta64[dt.timedelta], x: timedelta64[_AnyTD64Item], /) -> timedelta64[_AnyTD64Item | None]: ... + @overload + def __mod__(self: timedelta64[dt.timedelta], x: dt.timedelta, /) -> dt.timedelta: ... + @overload + def __mod__(self, x: timedelta64[int], /) -> timedelta64[int | None]: ... + @overload + def __mod__(self, x: timedelta64, /) -> timedelta64: ... + + # the L[0] makes __mod__ non-commutative, which the first two overloads reflect + @overload + def __rmod__(self, x: timedelta64[None], /) -> timedelta64[None]: ... + @overload + def __rmod__(self: timedelta64[L[0] | None], x: timedelta64, /) -> timedelta64[None]: ... + @overload + def __rmod__(self: timedelta64[int], x: timedelta64[int | dt.timedelta], /) -> timedelta64[int | None]: ... + @overload + def __rmod__(self: timedelta64[dt.timedelta], x: timedelta64[_AnyTD64Item], /) -> timedelta64[_AnyTD64Item | None]: ... + @overload + def __rmod__(self: timedelta64[dt.timedelta], x: dt.timedelta, /) -> dt.timedelta: ... + @overload + def __rmod__(self, x: timedelta64[int], /) -> timedelta64[int | None]: ... + @overload + def __rmod__(self, x: timedelta64, /) -> timedelta64: ... + + # keep in sync with __mod__ + @overload + def __divmod__(self, x: timedelta64[L[0] | None], /) -> tuple[int64, timedelta64[None]]: ... + @overload + def __divmod__(self: timedelta64[None], x: timedelta64, /) -> tuple[int64, timedelta64[None]]: ... + @overload + def __divmod__(self: timedelta64[int], x: timedelta64[int | dt.timedelta], /) -> tuple[int64, timedelta64[int | None]]: ... + @overload + def __divmod__(self: timedelta64[dt.timedelta], x: timedelta64[_AnyTD64Item], /) -> tuple[int64, timedelta64[_AnyTD64Item | None]]: ... + @overload + def __divmod__(self: timedelta64[dt.timedelta], x: dt.timedelta, /) -> tuple[int, dt.timedelta]: ... + @overload + def __divmod__(self, x: timedelta64[int], /) -> tuple[int64, timedelta64[int | None]]: ... + @overload + def __divmod__(self, x: timedelta64, /) -> tuple[int64, timedelta64]: ... + + # keep in sync with __rmod__ + @overload + def __rdivmod__(self, x: timedelta64[None], /) -> tuple[int64, timedelta64[None]]: ... + @overload + def __rdivmod__(self: timedelta64[L[0] | None], x: timedelta64, /) -> tuple[int64, timedelta64[None]]: ... + @overload + def __rdivmod__(self: timedelta64[int], x: timedelta64[int | dt.timedelta], /) -> tuple[int64, timedelta64[int | None]]: ... + @overload + def __rdivmod__(self: timedelta64[dt.timedelta], x: timedelta64[_AnyTD64Item], /) -> tuple[int64, timedelta64[_AnyTD64Item | None]]: ... + @overload + def __rdivmod__(self: timedelta64[dt.timedelta], x: dt.timedelta, /) -> tuple[int, dt.timedelta]: ... + @overload + def __rdivmod__(self, x: timedelta64[int], /) -> tuple[int64, timedelta64[int | None]]: ... + @overload + def __rdivmod__(self, x: timedelta64, /) -> tuple[int64, timedelta64]: ... + + @overload + def __sub__(self: timedelta64[None], b: _TD64Like_co, /) -> timedelta64[None]: ... + @overload + def __sub__(self: timedelta64[int], b: timedelta64[int | dt.timedelta], /) -> timedelta64[int]: ... + @overload + def __sub__(self: timedelta64[int], b: timedelta64, /) -> timedelta64[int | None]: ... + @overload + def __sub__(self: timedelta64[dt.timedelta], b: dt.timedelta, /) -> dt.timedelta: ... + @overload + def __sub__(self: timedelta64[_AnyTD64Item], b: timedelta64[_AnyTD64Item] | _IntLike_co, /) -> timedelta64[_AnyTD64Item]: ... + @overload + def __sub__(self, b: timedelta64[None], /) -> timedelta64[None]: ... + + @overload + def __rsub__(self: timedelta64[None], a: _TD64Like_co, /) -> timedelta64[None]: ... + @overload + def __rsub__(self: timedelta64[dt.timedelta], a: _AnyDateOrTime, /) -> _AnyDateOrTime: ... + @overload + def __rsub__(self: timedelta64[dt.timedelta], a: timedelta64[_AnyTD64Item], /) -> timedelta64[_AnyTD64Item]: ... + @overload + def __rsub__(self: timedelta64[_AnyTD64Item], a: timedelta64[_AnyTD64Item] | _IntLike_co, /) -> timedelta64[_AnyTD64Item]: ... + @overload + def __rsub__(self, a: timedelta64[None], /) -> timedelta64[None]: ... + @overload + def __rsub__(self, a: datetime64[None], /) -> datetime64[None]: ... + + @overload + def __truediv__(self: timedelta64[dt.timedelta], b: dt.timedelta, /) -> float: ... + @overload + def __truediv__(self, b: timedelta64, /) -> float64: ... + @overload + def __truediv__(self: timedelta64[_AnyTD64Item], b: int | integer, /) -> timedelta64[_AnyTD64Item]: ... + @overload + def __truediv__(self: timedelta64[_AnyTD64Item], b: float | floating, /) -> timedelta64[_AnyTD64Item | None]: ... + @overload + def __truediv__(self, b: float | floating | integer, /) -> timedelta64: ... + @overload + def __rtruediv__(self: timedelta64[dt.timedelta], a: dt.timedelta, /) -> float: ... + @overload + def __rtruediv__(self, a: timedelta64, /) -> float64: ... + + @overload + def __floordiv__(self: timedelta64[dt.timedelta], b: dt.timedelta, /) -> int: ... + @overload + def __floordiv__(self, b: timedelta64, /) -> int64: ... + @overload + def __floordiv__(self: timedelta64[_AnyTD64Item], b: int | integer, /) -> timedelta64[_AnyTD64Item]: ... + @overload + def __floordiv__(self: timedelta64[_AnyTD64Item], b: float | floating, /) -> timedelta64[_AnyTD64Item | None]: ... + @overload + def __rfloordiv__(self: timedelta64[dt.timedelta], a: dt.timedelta, /) -> int: ... + @overload + def __rfloordiv__(self, a: timedelta64, /) -> int64: ... + + __lt__: _ComparisonOpLT[_TD64Like_co, _ArrayLikeTD64_co] + __le__: _ComparisonOpLE[_TD64Like_co, _ArrayLikeTD64_co] + __gt__: _ComparisonOpGT[_TD64Like_co, _ArrayLikeTD64_co] + __ge__: _ComparisonOpGE[_TD64Like_co, _ArrayLikeTD64_co] + +class datetime64(_RealMixin, generic[_DT64ItemT_co], Generic[_DT64ItemT_co]): + @property + def itemsize(self) -> L[8]: ... + @property + def nbytes(self) -> L[8]: ... + + @overload + def __init__(self, value: datetime64[_DT64ItemT_co], /) -> None: ... + @overload + def __init__(self: datetime64[_AnyDT64Arg], value: _AnyDT64Arg, /) -> None: ... + @overload + def __init__(self: datetime64[None], value: _NaTValue | None = ..., format: _TimeUnitSpec = ..., /) -> None: ... + @overload + def __init__(self: datetime64[dt.datetime], value: _DT64Now, format: _TimeUnitSpec[_NativeTimeUnit] = ..., /) -> None: ... + @overload + def __init__(self: datetime64[dt.date], value: _DT64Date, format: _TimeUnitSpec[_DateUnit] = ..., /) -> None: ... + @overload + def __init__(self: datetime64[int], value: int | bytes | str | dt.date, format: _TimeUnitSpec[_IntTimeUnit], /) -> None: ... + @overload + def __init__( + self: datetime64[dt.datetime], value: int | bytes | str | dt.date, format: _TimeUnitSpec[_NativeTimeUnit], / + ) -> None: ... + @overload + def __init__(self: datetime64[dt.date], value: int | bytes | str | dt.date, format: _TimeUnitSpec[_DateUnit], /) -> None: ... + @overload + def __init__(self, value: bytes | str | dt.date | None, format: _TimeUnitSpec = ..., /) -> None: ... + + @overload + def __add__(self: datetime64[_AnyDT64Item], x: int | integer | np.bool, /) -> datetime64[_AnyDT64Item]: ... + @overload + def __add__(self: datetime64[None], x: _TD64Like_co, /) -> datetime64[None]: ... + @overload + def __add__(self: datetime64[int], x: timedelta64[int | dt.timedelta], /) -> datetime64[int]: ... + @overload + def __add__(self: datetime64[dt.datetime], x: timedelta64[dt.timedelta], /) -> datetime64[dt.datetime]: ... + @overload + def __add__(self: datetime64[dt.date], x: timedelta64[dt.timedelta], /) -> datetime64[dt.date]: ... + @overload + def __add__(self: datetime64[dt.date], x: timedelta64[int], /) -> datetime64[int]: ... + @overload + def __add__(self, x: datetime64[None], /) -> datetime64[None]: ... + @overload + def __add__(self, x: _TD64Like_co, /) -> datetime64: ... + __radd__ = __add__ + + @overload + def __sub__(self: datetime64[_AnyDT64Item], x: int | integer | np.bool, /) -> datetime64[_AnyDT64Item]: ... + @overload + def __sub__(self: datetime64[_AnyDate], x: _AnyDate, /) -> dt.timedelta: ... + @overload + def __sub__(self: datetime64[None], x: timedelta64, /) -> datetime64[None]: ... + @overload + def __sub__(self: datetime64[None], x: datetime64, /) -> timedelta64[None]: ... + @overload + def __sub__(self: datetime64[int], x: timedelta64, /) -> datetime64[int]: ... + @overload + def __sub__(self: datetime64[int], x: datetime64, /) -> timedelta64[int]: ... + @overload + def __sub__(self: datetime64[dt.datetime], x: timedelta64[int], /) -> datetime64[int]: ... + @overload + def __sub__(self: datetime64[dt.datetime], x: timedelta64[dt.timedelta], /) -> datetime64[dt.datetime]: ... + @overload + def __sub__(self: datetime64[dt.datetime], x: datetime64[int], /) -> timedelta64[int]: ... + @overload + def __sub__(self: datetime64[dt.date], x: timedelta64[int], /) -> datetime64[dt.date | int]: ... + @overload + def __sub__(self: datetime64[dt.date], x: timedelta64[dt.timedelta], /) -> datetime64[dt.date]: ... + @overload + def __sub__(self: datetime64[dt.date], x: datetime64[dt.date], /) -> timedelta64[dt.timedelta]: ... + @overload + def __sub__(self, x: timedelta64[None], /) -> datetime64[None]: ... + @overload + def __sub__(self, x: datetime64[None], /) -> timedelta64[None]: ... + @overload + def __sub__(self, x: _TD64Like_co, /) -> datetime64: ... + @overload + def __sub__(self, x: datetime64, /) -> timedelta64: ... + + @overload + def __rsub__(self: datetime64[_AnyDT64Item], x: int | integer | np.bool, /) -> datetime64[_AnyDT64Item]: ... + @overload + def __rsub__(self: datetime64[_AnyDate], x: _AnyDate, /) -> dt.timedelta: ... + @overload + def __rsub__(self: datetime64[None], x: datetime64, /) -> timedelta64[None]: ... + @overload + def __rsub__(self: datetime64[int], x: datetime64, /) -> timedelta64[int]: ... + @overload + def __rsub__(self: datetime64[dt.datetime], x: datetime64[int], /) -> timedelta64[int]: ... + @overload + def __rsub__(self: datetime64[dt.datetime], x: datetime64[dt.date], /) -> timedelta64[dt.timedelta]: ... + @overload + def __rsub__(self, x: datetime64[None], /) -> timedelta64[None]: ... + @overload + def __rsub__(self, x: datetime64, /) -> timedelta64: ... + + __lt__: _ComparisonOpLT[datetime64, _ArrayLikeDT64_co] + __le__: _ComparisonOpLE[datetime64, _ArrayLikeDT64_co] + __gt__: _ComparisonOpGT[datetime64, _ArrayLikeDT64_co] + __ge__: _ComparisonOpGE[datetime64, _ArrayLikeDT64_co] + +class flexible(_RealMixin, generic[_FlexibleItemT_co], Generic[_FlexibleItemT_co]): ... + +class void(flexible[bytes | tuple[Any, ...]]): + @overload + def __init__(self, value: _IntLike_co | bytes, /, dtype: None = None) -> None: ... + @overload + def __init__(self, value: Any, /, dtype: _DTypeLikeVoid) -> None: ... + + @overload + def __getitem__(self, key: str | SupportsIndex, /) -> Any: ... + @overload + def __getitem__(self, key: list[str], /) -> void: ... + def __setitem__(self, key: str | list[str] | SupportsIndex, value: ArrayLike, /) -> None: ... + + def setfield(self, val: ArrayLike, dtype: DTypeLike, offset: int = ...) -> None: ... + +class character(flexible[_CharacterItemT_co], Generic[_CharacterItemT_co]): + @abstractmethod + def __init__(self, value: _CharacterItemT_co = ..., /) -> None: ... + +# NOTE: Most `np.bytes_` / `np.str_` methods return their builtin `bytes` / `str` counterpart + +class bytes_(character[bytes], bytes): + @overload + def __new__(cls, o: object = ..., /) -> Self: ... + @overload + def __new__(cls, s: str, /, encoding: str, errors: str = ...) -> Self: ... + + # + @overload + def __init__(self, o: object = ..., /) -> None: ... + @overload + def __init__(self, s: str, /, encoding: str, errors: str = ...) -> None: ... + + # + def __bytes__(self, /) -> bytes: ... + +class str_(character[str], str): + @overload + def __new__(cls, value: object = ..., /) -> Self: ... + @overload + def __new__(cls, value: bytes, /, encoding: str = ..., errors: str = ...) -> Self: ... + + # + @overload + def __init__(self, value: object = ..., /) -> None: ... + @overload + def __init__(self, value: bytes, /, encoding: str = ..., errors: str = ...) -> None: ... + +# See `numpy._typing._ufunc` for more concrete nin-/nout-specific stubs +@final +class ufunc: + @property + def __name__(self) -> LiteralString: ... + @property + def __qualname__(self) -> LiteralString: ... + @property + def __doc__(self) -> str: ... + @property + def nin(self) -> int: ... + @property + def nout(self) -> int: ... + @property + def nargs(self) -> int: ... + @property + def ntypes(self) -> int: ... + @property + def types(self) -> list[LiteralString]: ... + # Broad return type because it has to encompass things like + # + # >>> np.logical_and.identity is True + # True + # >>> np.add.identity is 0 + # True + # >>> np.sin.identity is None + # True + # + # and any user-defined ufuncs. + @property + def identity(self) -> Any: ... + # This is None for ufuncs and a string for gufuncs. + @property + def signature(self) -> LiteralString | None: ... + + def __call__(self, *args: Any, **kwargs: Any) -> Any: ... + # The next four methods will always exist, but they will just + # raise a ValueError ufuncs with that don't accept two input + # arguments and return one output argument. Because of that we + # can't type them very precisely. + def reduce(self, /, *args: Any, **kwargs: Any) -> Any: ... + def accumulate(self, /, *args: Any, **kwargs: Any) -> NDArray[Any]: ... + def reduceat(self, /, *args: Any, **kwargs: Any) -> NDArray[Any]: ... + def outer(self, *args: Any, **kwargs: Any) -> Any: ... + # Similarly at won't be defined for ufuncs that return multiple + # outputs, so we can't type it very precisely. + def at(self, /, *args: Any, **kwargs: Any) -> None: ... + + # + def resolve_dtypes( + self, + /, + dtypes: tuple[dtype | type | None, ...], + *, + signature: tuple[dtype | None, ...] | None = None, + casting: _CastingKind | None = None, + reduction: builtins.bool = False, + ) -> tuple[dtype, ...]: ... + +# Parameters: `__name__`, `ntypes` and `identity` +absolute: _UFunc_Nin1_Nout1[L['absolute'], L[20], None] +add: _UFunc_Nin2_Nout1[L['add'], L[22], L[0]] +arccos: _UFunc_Nin1_Nout1[L['arccos'], L[8], None] +arccosh: _UFunc_Nin1_Nout1[L['arccosh'], L[8], None] +arcsin: _UFunc_Nin1_Nout1[L['arcsin'], L[8], None] +arcsinh: _UFunc_Nin1_Nout1[L['arcsinh'], L[8], None] +arctan2: _UFunc_Nin2_Nout1[L['arctan2'], L[5], None] +arctan: _UFunc_Nin1_Nout1[L['arctan'], L[8], None] +arctanh: _UFunc_Nin1_Nout1[L['arctanh'], L[8], None] +bitwise_and: _UFunc_Nin2_Nout1[L['bitwise_and'], L[12], L[-1]] +bitwise_count: _UFunc_Nin1_Nout1[L['bitwise_count'], L[11], None] +bitwise_not: _UFunc_Nin1_Nout1[L['invert'], L[12], None] +bitwise_or: _UFunc_Nin2_Nout1[L['bitwise_or'], L[12], L[0]] +bitwise_xor: _UFunc_Nin2_Nout1[L['bitwise_xor'], L[12], L[0]] +cbrt: _UFunc_Nin1_Nout1[L['cbrt'], L[5], None] +ceil: _UFunc_Nin1_Nout1[L['ceil'], L[7], None] +conj: _UFunc_Nin1_Nout1[L['conjugate'], L[18], None] +conjugate: _UFunc_Nin1_Nout1[L['conjugate'], L[18], None] +copysign: _UFunc_Nin2_Nout1[L['copysign'], L[4], None] +cos: _UFunc_Nin1_Nout1[L['cos'], L[9], None] +cosh: _UFunc_Nin1_Nout1[L['cosh'], L[8], None] +deg2rad: _UFunc_Nin1_Nout1[L['deg2rad'], L[5], None] +degrees: _UFunc_Nin1_Nout1[L['degrees'], L[5], None] +divide: _UFunc_Nin2_Nout1[L['true_divide'], L[11], None] +divmod: _UFunc_Nin2_Nout2[L['divmod'], L[15], None] +equal: _UFunc_Nin2_Nout1[L['equal'], L[23], None] +exp2: _UFunc_Nin1_Nout1[L['exp2'], L[8], None] +exp: _UFunc_Nin1_Nout1[L['exp'], L[10], None] +expm1: _UFunc_Nin1_Nout1[L['expm1'], L[8], None] +fabs: _UFunc_Nin1_Nout1[L['fabs'], L[5], None] +float_power: _UFunc_Nin2_Nout1[L['float_power'], L[4], None] +floor: _UFunc_Nin1_Nout1[L['floor'], L[7], None] +floor_divide: _UFunc_Nin2_Nout1[L['floor_divide'], L[21], None] +fmax: _UFunc_Nin2_Nout1[L['fmax'], L[21], None] +fmin: _UFunc_Nin2_Nout1[L['fmin'], L[21], None] +fmod: _UFunc_Nin2_Nout1[L['fmod'], L[15], None] +frexp: _UFunc_Nin1_Nout2[L['frexp'], L[4], None] +gcd: _UFunc_Nin2_Nout1[L['gcd'], L[11], L[0]] +greater: _UFunc_Nin2_Nout1[L['greater'], L[23], None] +greater_equal: _UFunc_Nin2_Nout1[L['greater_equal'], L[23], None] +heaviside: _UFunc_Nin2_Nout1[L['heaviside'], L[4], None] +hypot: _UFunc_Nin2_Nout1[L['hypot'], L[5], L[0]] +invert: _UFunc_Nin1_Nout1[L['invert'], L[12], None] +isfinite: _UFunc_Nin1_Nout1[L['isfinite'], L[20], None] +isinf: _UFunc_Nin1_Nout1[L['isinf'], L[20], None] +isnan: _UFunc_Nin1_Nout1[L['isnan'], L[20], None] +isnat: _UFunc_Nin1_Nout1[L['isnat'], L[2], None] +lcm: _UFunc_Nin2_Nout1[L['lcm'], L[11], None] +ldexp: _UFunc_Nin2_Nout1[L['ldexp'], L[8], None] +left_shift: _UFunc_Nin2_Nout1[L['left_shift'], L[11], None] +less: _UFunc_Nin2_Nout1[L['less'], L[23], None] +less_equal: _UFunc_Nin2_Nout1[L['less_equal'], L[23], None] +log10: _UFunc_Nin1_Nout1[L['log10'], L[8], None] +log1p: _UFunc_Nin1_Nout1[L['log1p'], L[8], None] +log2: _UFunc_Nin1_Nout1[L['log2'], L[8], None] +log: _UFunc_Nin1_Nout1[L['log'], L[10], None] +logaddexp2: _UFunc_Nin2_Nout1[L['logaddexp2'], L[4], float] +logaddexp: _UFunc_Nin2_Nout1[L['logaddexp'], L[4], float] +logical_and: _UFunc_Nin2_Nout1[L['logical_and'], L[20], L[True]] +logical_not: _UFunc_Nin1_Nout1[L['logical_not'], L[20], None] +logical_or: _UFunc_Nin2_Nout1[L['logical_or'], L[20], L[False]] +logical_xor: _UFunc_Nin2_Nout1[L['logical_xor'], L[19], L[False]] +matmul: _GUFunc_Nin2_Nout1[L['matmul'], L[19], None, L["(n?,k),(k,m?)->(n?,m?)"]] +matvec: _GUFunc_Nin2_Nout1[L['matvec'], L[19], None, L["(m,n),(n)->(m)"]] +maximum: _UFunc_Nin2_Nout1[L['maximum'], L[21], None] +minimum: _UFunc_Nin2_Nout1[L['minimum'], L[21], None] +mod: _UFunc_Nin2_Nout1[L['remainder'], L[16], None] +modf: _UFunc_Nin1_Nout2[L['modf'], L[4], None] +multiply: _UFunc_Nin2_Nout1[L['multiply'], L[23], L[1]] +negative: _UFunc_Nin1_Nout1[L['negative'], L[19], None] +nextafter: _UFunc_Nin2_Nout1[L['nextafter'], L[4], None] +not_equal: _UFunc_Nin2_Nout1[L['not_equal'], L[23], None] +positive: _UFunc_Nin1_Nout1[L['positive'], L[19], None] +power: _UFunc_Nin2_Nout1[L['power'], L[18], None] +rad2deg: _UFunc_Nin1_Nout1[L['rad2deg'], L[5], None] +radians: _UFunc_Nin1_Nout1[L['radians'], L[5], None] +reciprocal: _UFunc_Nin1_Nout1[L['reciprocal'], L[18], None] +remainder: _UFunc_Nin2_Nout1[L['remainder'], L[16], None] +right_shift: _UFunc_Nin2_Nout1[L['right_shift'], L[11], None] +rint: _UFunc_Nin1_Nout1[L['rint'], L[10], None] +sign: _UFunc_Nin1_Nout1[L['sign'], L[19], None] +signbit: _UFunc_Nin1_Nout1[L['signbit'], L[4], None] +sin: _UFunc_Nin1_Nout1[L['sin'], L[9], None] +sinh: _UFunc_Nin1_Nout1[L['sinh'], L[8], None] +spacing: _UFunc_Nin1_Nout1[L['spacing'], L[4], None] +sqrt: _UFunc_Nin1_Nout1[L['sqrt'], L[10], None] +square: _UFunc_Nin1_Nout1[L['square'], L[18], None] +subtract: _UFunc_Nin2_Nout1[L['subtract'], L[21], None] +tan: _UFunc_Nin1_Nout1[L['tan'], L[8], None] +tanh: _UFunc_Nin1_Nout1[L['tanh'], L[8], None] +true_divide: _UFunc_Nin2_Nout1[L['true_divide'], L[11], None] +trunc: _UFunc_Nin1_Nout1[L['trunc'], L[7], None] +vecdot: _GUFunc_Nin2_Nout1[L['vecdot'], L[19], None, L["(n),(n)->()"]] +vecmat: _GUFunc_Nin2_Nout1[L['vecmat'], L[19], None, L["(n),(n,m)->(m)"]] + +abs = absolute +acos = arccos +acosh = arccosh +asin = arcsin +asinh = arcsinh +atan = arctan +atanh = arctanh +atan2 = arctan2 +concat = concatenate +bitwise_left_shift = left_shift +bitwise_invert = invert +bitwise_right_shift = right_shift +permute_dims = transpose +pow = power + +class errstate: + def __init__( + self, + *, + call: _ErrCall = ..., + all: _ErrKind | None = ..., + divide: _ErrKind | None = ..., + over: _ErrKind | None = ..., + under: _ErrKind | None = ..., + invalid: _ErrKind | None = ..., + ) -> None: ... + def __enter__(self) -> None: ... + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + /, + ) -> None: ... + def __call__(self, func: _CallableT) -> _CallableT: ... + +# TODO: The type of each `__next__` and `iters` return-type depends +# on the length and dtype of `args`; we can't describe this behavior yet +# as we lack variadics (PEP 646). +@final +class broadcast: + def __new__(cls, *args: ArrayLike) -> broadcast: ... + @property + def index(self) -> int: ... + @property + def iters(self) -> tuple[flatiter[Any], ...]: ... + @property + def nd(self) -> int: ... + @property + def ndim(self) -> int: ... + @property + def numiter(self) -> int: ... + @property + def shape(self) -> _AnyShape: ... + @property + def size(self) -> int: ... + def __next__(self) -> tuple[Any, ...]: ... + def __iter__(self) -> Self: ... + def reset(self) -> None: ... + +@final +class busdaycalendar: + def __new__( + cls, + weekmask: ArrayLike = ..., + holidays: ArrayLike | dt.date | _NestedSequence[dt.date] = ..., + ) -> busdaycalendar: ... + @property + def weekmask(self) -> NDArray[np.bool]: ... + @property + def holidays(self) -> NDArray[datetime64]: ... + +class finfo(Generic[_FloatingT_co]): + dtype: Final[dtype[_FloatingT_co]] + bits: Final[int] + eps: Final[_FloatingT_co] + epsneg: Final[_FloatingT_co] + iexp: Final[int] + machep: Final[int] + max: Final[_FloatingT_co] + maxexp: Final[int] + min: Final[_FloatingT_co] + minexp: Final[int] + negep: Final[int] + nexp: Final[int] + nmant: Final[int] + precision: Final[int] + resolution: Final[_FloatingT_co] + smallest_subnormal: Final[_FloatingT_co] + @property + def smallest_normal(self) -> _FloatingT_co: ... + @property + def tiny(self) -> _FloatingT_co: ... + @overload + def __new__(cls, dtype: inexact[_NBit1] | _DTypeLike[inexact[_NBit1]]) -> finfo[floating[_NBit1]]: ... + @overload + def __new__(cls, dtype: complex | type[complex]) -> finfo[float64]: ... + @overload + def __new__(cls, dtype: str) -> finfo[floating]: ... + +class iinfo(Generic[_IntegerT_co]): + dtype: Final[dtype[_IntegerT_co]] + kind: Final[LiteralString] + bits: Final[int] + key: Final[LiteralString] + @property + def min(self) -> int: ... + @property + def max(self) -> int: ... + + @overload + def __new__( + cls, dtype: _IntegerT_co | _DTypeLike[_IntegerT_co] + ) -> iinfo[_IntegerT_co]: ... + @overload + def __new__(cls, dtype: int | type[int]) -> iinfo[int_]: ... + @overload + def __new__(cls, dtype: str) -> iinfo[Any]: ... + +@final +class nditer: + def __new__( + cls, + op: ArrayLike | Sequence[ArrayLike | None], + flags: Sequence[_NDIterFlagsKind] | None = ..., + op_flags: Sequence[Sequence[_NDIterFlagsOp]] | None = ..., + op_dtypes: DTypeLike | Sequence[DTypeLike] = ..., + order: _OrderKACF = ..., + casting: _CastingKind = ..., + op_axes: Sequence[Sequence[SupportsIndex]] | None = ..., + itershape: _ShapeLike | None = ..., + buffersize: SupportsIndex = ..., + ) -> nditer: ... + def __enter__(self) -> nditer: ... + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: ... + def __iter__(self) -> nditer: ... + def __next__(self) -> tuple[NDArray[Any], ...]: ... + def __len__(self) -> int: ... + def __copy__(self) -> nditer: ... + @overload + def __getitem__(self, index: SupportsIndex) -> NDArray[Any]: ... + @overload + def __getitem__(self, index: slice) -> tuple[NDArray[Any], ...]: ... + def __setitem__(self, index: slice | SupportsIndex, value: ArrayLike) -> None: ... + def close(self) -> None: ... + def copy(self) -> nditer: ... + def debug_print(self) -> None: ... + def enable_external_loop(self) -> None: ... + def iternext(self) -> builtins.bool: ... + def remove_axis(self, i: SupportsIndex, /) -> None: ... + def remove_multi_index(self) -> None: ... + def reset(self) -> None: ... + @property + def dtypes(self) -> tuple[dtype, ...]: ... + @property + def finished(self) -> builtins.bool: ... + @property + def has_delayed_bufalloc(self) -> builtins.bool: ... + @property + def has_index(self) -> builtins.bool: ... + @property + def has_multi_index(self) -> builtins.bool: ... + @property + def index(self) -> int: ... + @property + def iterationneedsapi(self) -> builtins.bool: ... + @property + def iterindex(self) -> int: ... + @property + def iterrange(self) -> tuple[int, ...]: ... + @property + def itersize(self) -> int: ... + @property + def itviews(self) -> tuple[NDArray[Any], ...]: ... + @property + def multi_index(self) -> tuple[int, ...]: ... + @property + def ndim(self) -> int: ... + @property + def nop(self) -> int: ... + @property + def operands(self) -> tuple[NDArray[Any], ...]: ... + @property + def shape(self) -> tuple[int, ...]: ... + @property + def value(self) -> tuple[NDArray[Any], ...]: ... + +class memmap(ndarray[_ShapeT_co, _DTypeT_co]): + __array_priority__: ClassVar[float] + filename: str | None + offset: int + mode: str + @overload + def __new__( + subtype, + filename: StrOrBytesPath | _SupportsFileMethodsRW, + dtype: type[uint8] = ..., + mode: _MemMapModeKind = ..., + offset: int = ..., + shape: int | tuple[int, ...] | None = ..., + order: _OrderKACF = ..., + ) -> memmap[Any, dtype[uint8]]: ... + @overload + def __new__( + subtype, + filename: StrOrBytesPath | _SupportsFileMethodsRW, + dtype: _DTypeLike[_ScalarT], + mode: _MemMapModeKind = ..., + offset: int = ..., + shape: int | tuple[int, ...] | None = ..., + order: _OrderKACF = ..., + ) -> memmap[Any, dtype[_ScalarT]]: ... + @overload + def __new__( + subtype, + filename: StrOrBytesPath | _SupportsFileMethodsRW, + dtype: DTypeLike, + mode: _MemMapModeKind = ..., + offset: int = ..., + shape: int | tuple[int, ...] | None = ..., + order: _OrderKACF = ..., + ) -> memmap[Any, dtype]: ... + def __array_finalize__(self, obj: object) -> None: ... + def __array_wrap__( + self, + array: memmap[_ShapeT_co, _DTypeT_co], + context: tuple[ufunc, tuple[Any, ...], int] | None = ..., + return_scalar: builtins.bool = ..., + ) -> Any: ... + def flush(self) -> None: ... + +# TODO: Add a mypy plugin for managing functions whose output type is dependent +# on the literal value of some sort of signature (e.g. `einsum` and `vectorize`) +class vectorize: + pyfunc: Callable[..., Any] + cache: builtins.bool + signature: LiteralString | None + otypes: LiteralString | None + excluded: set[int | str] + __doc__: str | None + def __init__( + self, + pyfunc: Callable[..., Any], + otypes: str | Iterable[DTypeLike] | None = ..., + doc: str | None = ..., + excluded: Iterable[int | str] | None = ..., + cache: builtins.bool = ..., + signature: str | None = ..., + ) -> None: ... + def __call__(self, *args: Any, **kwargs: Any) -> Any: ... + +class poly1d: + @property + def variable(self) -> LiteralString: ... + @property + def order(self) -> int: ... + @property + def o(self) -> int: ... + @property + def roots(self) -> NDArray[Any]: ... + @property + def r(self) -> NDArray[Any]: ... + + @property + def coeffs(self) -> NDArray[Any]: ... + @coeffs.setter + def coeffs(self, value: NDArray[Any]) -> None: ... + + @property + def c(self) -> NDArray[Any]: ... + @c.setter + def c(self, value: NDArray[Any]) -> None: ... + + @property + def coef(self) -> NDArray[Any]: ... + @coef.setter + def coef(self, value: NDArray[Any]) -> None: ... + + @property + def coefficients(self) -> NDArray[Any]: ... + @coefficients.setter + def coefficients(self, value: NDArray[Any]) -> None: ... + + __hash__: ClassVar[None] # type: ignore[assignment] # pyright: ignore[reportIncompatibleMethodOverride] + + @overload + def __array__(self, /, t: None = None, copy: builtins.bool | None = None) -> ndarray[tuple[int], dtype]: ... + @overload + def __array__(self, /, t: _DTypeT, copy: builtins.bool | None = None) -> ndarray[tuple[int], _DTypeT]: ... + + @overload + def __call__(self, val: _ScalarLike_co) -> Any: ... + @overload + def __call__(self, val: poly1d) -> poly1d: ... + @overload + def __call__(self, val: ArrayLike) -> NDArray[Any]: ... + + def __init__( + self, + c_or_r: ArrayLike, + r: builtins.bool = ..., + variable: str | None = ..., + ) -> None: ... + def __len__(self) -> int: ... + def __neg__(self) -> poly1d: ... + def __pos__(self) -> poly1d: ... + def __mul__(self, other: ArrayLike, /) -> poly1d: ... + def __rmul__(self, other: ArrayLike, /) -> poly1d: ... + def __add__(self, other: ArrayLike, /) -> poly1d: ... + def __radd__(self, other: ArrayLike, /) -> poly1d: ... + def __pow__(self, val: _FloatLike_co, /) -> poly1d: ... # Integral floats are accepted + def __sub__(self, other: ArrayLike, /) -> poly1d: ... + def __rsub__(self, other: ArrayLike, /) -> poly1d: ... + def __truediv__(self, other: ArrayLike, /) -> poly1d: ... + def __rtruediv__(self, other: ArrayLike, /) -> poly1d: ... + def __getitem__(self, val: int, /) -> Any: ... + def __setitem__(self, key: int, val: Any, /) -> None: ... + def __iter__(self) -> Iterator[Any]: ... + def deriv(self, m: SupportsInt | SupportsIndex = ...) -> poly1d: ... + def integ( + self, + m: SupportsInt | SupportsIndex = ..., + k: _ArrayLikeComplex_co | _ArrayLikeObject_co | None = ..., + ) -> poly1d: ... + +class matrix(ndarray[_2DShapeT_co, _DTypeT_co]): + __array_priority__: ClassVar[float] = 10.0 # pyright: ignore[reportIncompatibleMethodOverride] + + def __new__( + subtype, # pyright: ignore[reportSelfClsParameterName] + data: ArrayLike, + dtype: DTypeLike = ..., + copy: builtins.bool = ..., + ) -> matrix[_2D, Incomplete]: ... + def __array_finalize__(self, obj: object) -> None: ... + + @overload # type: ignore[override] + def __getitem__( + self, key: SupportsIndex | _ArrayLikeInt_co | tuple[SupportsIndex | _ArrayLikeInt_co, ...], / + ) -> Incomplete: ... + @overload + def __getitem__(self, key: _ToIndices, /) -> matrix[_2D, _DTypeT_co]: ... + @overload + def __getitem__(self: matrix[Any, dtype[void]], key: str, /) -> matrix[_2D, dtype]: ... + @overload + def __getitem__(self: matrix[Any, dtype[void]], key: list[str], /) -> matrix[_2DShapeT_co, _DTypeT_co]: ... # pyright: ignore[reportIncompatibleMethodOverride] + + # + def __mul__(self, other: ArrayLike, /) -> matrix[_2D, Incomplete]: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] + def __rmul__(self, other: ArrayLike, /) -> matrix[_2D, Incomplete]: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] + def __imul__(self, other: ArrayLike, /) -> Self: ... + + # + def __pow__(self, other: ArrayLike, mod: None = None, /) -> matrix[_2D, Incomplete]: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] + def __rpow__(self, other: ArrayLike, mod: None = None, /) -> matrix[_2D, Incomplete]: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] + def __ipow__(self, other: ArrayLike, /) -> Self: ... # type: ignore[misc, override] + + # keep in sync with `prod` and `mean` + @overload # type: ignore[override] + def sum(self, axis: None = None, dtype: DTypeLike | None = None, out: None = None) -> Incomplete: ... + @overload + def sum(self, axis: _ShapeLike, dtype: DTypeLike | None = None, out: None = None) -> matrix[_2D, Incomplete]: ... + @overload + def sum(self, axis: _ShapeLike | None, dtype: DTypeLike | None, out: _ArrayT) -> _ArrayT: ... + @overload + def sum(self, axis: _ShapeLike | None = None, dtype: DTypeLike | None = None, *, out: _ArrayT) -> _ArrayT: ... # pyright: ignore[reportIncompatibleMethodOverride] + + # keep in sync with `sum` and `mean` + @overload # type: ignore[override] + def prod(self, axis: None = None, dtype: DTypeLike | None = None, out: None = None) -> Incomplete: ... + @overload + def prod(self, axis: _ShapeLike, dtype: DTypeLike | None = None, out: None = None) -> matrix[_2D, Incomplete]: ... + @overload + def prod(self, axis: _ShapeLike | None, dtype: DTypeLike | None, out: _ArrayT) -> _ArrayT: ... + @overload + def prod(self, axis: _ShapeLike | None = None, dtype: DTypeLike | None = None, *, out: _ArrayT) -> _ArrayT: ... # pyright: ignore[reportIncompatibleMethodOverride] + + # keep in sync with `sum` and `prod` + @overload # type: ignore[override] + def mean(self, axis: None = None, dtype: DTypeLike | None = None, out: None = None) -> Incomplete: ... + @overload + def mean(self, axis: _ShapeLike, dtype: DTypeLike | None = None, out: None = None) -> matrix[_2D, Incomplete]: ... + @overload + def mean(self, axis: _ShapeLike | None, dtype: DTypeLike | None, out: _ArrayT) -> _ArrayT: ... + @overload + def mean(self, axis: _ShapeLike | None = None, dtype: DTypeLike | None = None, *, out: _ArrayT) -> _ArrayT: ... # pyright: ignore[reportIncompatibleMethodOverride] + + # keep in sync with `var` + @overload # type: ignore[override] + def std(self, axis: None = None, dtype: DTypeLike | None = None, out: None = None, ddof: float = 0) -> Incomplete: ... + @overload + def std( + self, axis: _ShapeLike, dtype: DTypeLike | None = None, out: None = None, ddof: float = 0 + ) -> matrix[_2D, Incomplete]: ... + @overload + def std(self, axis: _ShapeLike | None, dtype: DTypeLike | None, out: _ArrayT, ddof: float = 0) -> _ArrayT: ... + @overload + def std( # pyright: ignore[reportIncompatibleMethodOverride] + self, axis: _ShapeLike | None = None, dtype: DTypeLike | None = None, *, out: _ArrayT, ddof: float = 0 + ) -> _ArrayT: ... + + # keep in sync with `std` + @overload # type: ignore[override] + def var(self, axis: None = None, dtype: DTypeLike | None = None, out: None = None, ddof: float = 0) -> Incomplete: ... + @overload + def var( + self, axis: _ShapeLike, dtype: DTypeLike | None = None, out: None = None, ddof: float = 0 + ) -> matrix[_2D, Incomplete]: ... + @overload + def var(self, axis: _ShapeLike | None, dtype: DTypeLike | None, out: _ArrayT, ddof: float = 0) -> _ArrayT: ... + @overload + def var( # pyright: ignore[reportIncompatibleMethodOverride] + self, axis: _ShapeLike | None = None, dtype: DTypeLike | None = None, *, out: _ArrayT, ddof: float = 0 + ) -> _ArrayT: ... + + # keep in sync with `all` + @overload # type: ignore[override] + def any(self, axis: None = None, out: None = None) -> np.bool: ... + @overload + def any(self, axis: _ShapeLike, out: None = None) -> matrix[_2D, dtype[np.bool]]: ... + @overload + def any(self, axis: _ShapeLike | None, out: _ArrayT) -> _ArrayT: ... + @overload + def any(self, axis: _ShapeLike | None = None, *, out: _ArrayT) -> _ArrayT: ... # pyright: ignore[reportIncompatibleMethodOverride] + + # keep in sync with `any` + @overload # type: ignore[override] + def all(self, axis: None = None, out: None = None) -> np.bool: ... + @overload + def all(self, axis: _ShapeLike, out: None = None) -> matrix[_2D, dtype[np.bool]]: ... + @overload + def all(self, axis: _ShapeLike | None, out: _ArrayT) -> _ArrayT: ... + @overload + def all(self, axis: _ShapeLike | None = None, *, out: _ArrayT) -> _ArrayT: ... # pyright: ignore[reportIncompatibleMethodOverride] + + # keep in sync with `min` and `ptp` + @overload # type: ignore[override] + def max(self: NDArray[_ScalarT], axis: None = None, out: None = None) -> _ScalarT: ... + @overload + def max(self, axis: _ShapeLike, out: None = None) -> matrix[_2D, _DTypeT_co]: ... + @overload + def max(self, axis: _ShapeLike | None, out: _ArrayT) -> _ArrayT: ... + @overload + def max(self, axis: _ShapeLike | None = None, *, out: _ArrayT) -> _ArrayT: ... # pyright: ignore[reportIncompatibleMethodOverride] + + # keep in sync with `max` and `ptp` + @overload # type: ignore[override] + def min(self: NDArray[_ScalarT], axis: None = None, out: None = None) -> _ScalarT: ... + @overload + def min(self, axis: _ShapeLike, out: None = None) -> matrix[_2D, _DTypeT_co]: ... + @overload + def min(self, axis: _ShapeLike | None, out: _ArrayT) -> _ArrayT: ... + @overload + def min(self, axis: _ShapeLike | None = None, *, out: _ArrayT) -> _ArrayT: ... # pyright: ignore[reportIncompatibleMethodOverride] + + # keep in sync with `max` and `min` + @overload + def ptp(self: NDArray[_ScalarT], axis: None = None, out: None = None) -> _ScalarT: ... + @overload + def ptp(self, axis: _ShapeLike, out: None = None) -> matrix[_2D, _DTypeT_co]: ... + @overload + def ptp(self, axis: _ShapeLike | None, out: _ArrayT) -> _ArrayT: ... + @overload + def ptp(self, axis: _ShapeLike | None = None, *, out: _ArrayT) -> _ArrayT: ... # pyright: ignore[reportIncompatibleMethodOverride] + + # keep in sync with `argmin` + @overload # type: ignore[override] + def argmax(self: NDArray[_ScalarT], axis: None = None, out: None = None) -> intp: ... + @overload + def argmax(self, axis: _ShapeLike, out: None = None) -> matrix[_2D, dtype[intp]]: ... + @overload + def argmax(self, axis: _ShapeLike | None, out: _BoolOrIntArrayT) -> _BoolOrIntArrayT: ... + @overload + def argmax(self, axis: _ShapeLike | None = None, *, out: _BoolOrIntArrayT) -> _BoolOrIntArrayT: ... # pyright: ignore[reportIncompatibleMethodOverride] + + # keep in sync with `argmax` + @overload # type: ignore[override] + def argmin(self: NDArray[_ScalarT], axis: None = None, out: None = None) -> intp: ... + @overload + def argmin(self, axis: _ShapeLike, out: None = None) -> matrix[_2D, dtype[intp]]: ... + @overload + def argmin(self, axis: _ShapeLike | None, out: _BoolOrIntArrayT) -> _BoolOrIntArrayT: ... + @overload + def argmin(self, axis: _ShapeLike | None = None, *, out: _BoolOrIntArrayT) -> _BoolOrIntArrayT: ... # pyright: ignore[reportIncompatibleMethodOverride] + + #the second overload handles the (rare) case that the matrix is not 2-d + @overload + def tolist(self: matrix[_2D, dtype[generic[_T]]]) -> list[list[_T]]: ... # pyright: ignore[reportIncompatibleMethodOverride] + @overload + def tolist(self) -> Incomplete: ... # pyright: ignore[reportIncompatibleMethodOverride] + + # these three methods will at least return a `2-d` array of shape (1, n) + def squeeze(self, axis: _ShapeLike | None = None) -> matrix[_2D, _DTypeT_co]: ... + def ravel(self, /, order: _OrderKACF = "C") -> matrix[_2D, _DTypeT_co]: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] + def flatten(self, /, order: _OrderKACF = "C") -> matrix[_2D, _DTypeT_co]: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] + + # matrix.T is inherited from _ScalarOrArrayCommon + def getT(self) -> Self: ... + @property + def I(self) -> matrix[_2D, Incomplete]: ... # noqa: E743 + def getI(self) -> matrix[_2D, Incomplete]: ... + @property + def A(self) -> ndarray[_2DShapeT_co, _DTypeT_co]: ... + def getA(self) -> ndarray[_2DShapeT_co, _DTypeT_co]: ... + @property + def A1(self) -> ndarray[_AnyShape, _DTypeT_co]: ... + def getA1(self) -> ndarray[_AnyShape, _DTypeT_co]: ... + @property + def H(self) -> matrix[_2D, _DTypeT_co]: ... + def getH(self) -> matrix[_2D, _DTypeT_co]: ... + +def from_dlpack( + x: _SupportsDLPack[None], + /, + *, + device: L["cpu"] | None = None, + copy: builtins.bool | None = None, +) -> NDArray[number | np.bool]: ... diff --git a/.venv/lib/python3.12/site-packages/numpy/_array_api_info.py b/.venv/lib/python3.12/site-packages/numpy/_array_api_info.py new file mode 100644 index 0000000000000000000000000000000000000000..067e38798718e37383c9550688c4080ef73c1539 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/_array_api_info.py @@ -0,0 +1,346 @@ +""" +Array API Inspection namespace + +This is the namespace for inspection functions as defined by the array API +standard. See +https://data-apis.org/array-api/latest/API_specification/inspection.html for +more details. + +""" +from numpy._core import ( + bool, + complex64, + complex128, + dtype, + float32, + float64, + int8, + int16, + int32, + int64, + intp, + uint8, + uint16, + uint32, + uint64, +) + + +class __array_namespace_info__: + """ + Get the array API inspection namespace for NumPy. + + The array API inspection namespace defines the following functions: + + - capabilities() + - default_device() + - default_dtypes() + - dtypes() + - devices() + + See + https://data-apis.org/array-api/latest/API_specification/inspection.html + for more details. + + Returns + ------- + info : ModuleType + The array API inspection namespace for NumPy. + + Examples + -------- + >>> info = np.__array_namespace_info__() + >>> info.default_dtypes() + {'real floating': numpy.float64, + 'complex floating': numpy.complex128, + 'integral': numpy.int64, + 'indexing': numpy.int64} + + """ + + __module__ = 'numpy' + + def capabilities(self): + """ + Return a dictionary of array API library capabilities. + + The resulting dictionary has the following keys: + + - **"boolean indexing"**: boolean indicating whether an array library + supports boolean indexing. Always ``True`` for NumPy. + + - **"data-dependent shapes"**: boolean indicating whether an array + library supports data-dependent output shapes. Always ``True`` for + NumPy. + + See + https://data-apis.org/array-api/latest/API_specification/generated/array_api.info.capabilities.html + for more details. + + See Also + -------- + __array_namespace_info__.default_device, + __array_namespace_info__.default_dtypes, + __array_namespace_info__.dtypes, + __array_namespace_info__.devices + + Returns + ------- + capabilities : dict + A dictionary of array API library capabilities. + + Examples + -------- + >>> info = np.__array_namespace_info__() + >>> info.capabilities() + {'boolean indexing': True, + 'data-dependent shapes': True, + 'max dimensions': 64} + + """ + return { + "boolean indexing": True, + "data-dependent shapes": True, + "max dimensions": 64, + } + + def default_device(self): + """ + The default device used for new NumPy arrays. + + For NumPy, this always returns ``'cpu'``. + + See Also + -------- + __array_namespace_info__.capabilities, + __array_namespace_info__.default_dtypes, + __array_namespace_info__.dtypes, + __array_namespace_info__.devices + + Returns + ------- + device : str + The default device used for new NumPy arrays. + + Examples + -------- + >>> info = np.__array_namespace_info__() + >>> info.default_device() + 'cpu' + + """ + return "cpu" + + def default_dtypes(self, *, device=None): + """ + The default data types used for new NumPy arrays. + + For NumPy, this always returns the following dictionary: + + - **"real floating"**: ``numpy.float64`` + - **"complex floating"**: ``numpy.complex128`` + - **"integral"**: ``numpy.intp`` + - **"indexing"**: ``numpy.intp`` + + Parameters + ---------- + device : str, optional + The device to get the default data types for. For NumPy, only + ``'cpu'`` is allowed. + + Returns + ------- + dtypes : dict + A dictionary describing the default data types used for new NumPy + arrays. + + See Also + -------- + __array_namespace_info__.capabilities, + __array_namespace_info__.default_device, + __array_namespace_info__.dtypes, + __array_namespace_info__.devices + + Examples + -------- + >>> info = np.__array_namespace_info__() + >>> info.default_dtypes() + {'real floating': numpy.float64, + 'complex floating': numpy.complex128, + 'integral': numpy.int64, + 'indexing': numpy.int64} + + """ + if device not in ["cpu", None]: + raise ValueError( + 'Device not understood. Only "cpu" is allowed, but received:' + f' {device}' + ) + return { + "real floating": dtype(float64), + "complex floating": dtype(complex128), + "integral": dtype(intp), + "indexing": dtype(intp), + } + + def dtypes(self, *, device=None, kind=None): + """ + The array API data types supported by NumPy. + + Note that this function only returns data types that are defined by + the array API. + + Parameters + ---------- + device : str, optional + The device to get the data types for. For NumPy, only ``'cpu'`` is + allowed. + kind : str or tuple of str, optional + The kind of data types to return. If ``None``, all data types are + returned. If a string, only data types of that kind are returned. + If a tuple, a dictionary containing the union of the given kinds + is returned. The following kinds are supported: + + - ``'bool'``: boolean data types (i.e., ``bool``). + - ``'signed integer'``: signed integer data types (i.e., ``int8``, + ``int16``, ``int32``, ``int64``). + - ``'unsigned integer'``: unsigned integer data types (i.e., + ``uint8``, ``uint16``, ``uint32``, ``uint64``). + - ``'integral'``: integer data types. Shorthand for ``('signed + integer', 'unsigned integer')``. + - ``'real floating'``: real-valued floating-point data types + (i.e., ``float32``, ``float64``). + - ``'complex floating'``: complex floating-point data types (i.e., + ``complex64``, ``complex128``). + - ``'numeric'``: numeric data types. Shorthand for ``('integral', + 'real floating', 'complex floating')``. + + Returns + ------- + dtypes : dict + A dictionary mapping the names of data types to the corresponding + NumPy data types. + + See Also + -------- + __array_namespace_info__.capabilities, + __array_namespace_info__.default_device, + __array_namespace_info__.default_dtypes, + __array_namespace_info__.devices + + Examples + -------- + >>> info = np.__array_namespace_info__() + >>> info.dtypes(kind='signed integer') + {'int8': numpy.int8, + 'int16': numpy.int16, + 'int32': numpy.int32, + 'int64': numpy.int64} + + """ + if device not in ["cpu", None]: + raise ValueError( + 'Device not understood. Only "cpu" is allowed, but received:' + f' {device}' + ) + if kind is None: + return { + "bool": dtype(bool), + "int8": dtype(int8), + "int16": dtype(int16), + "int32": dtype(int32), + "int64": dtype(int64), + "uint8": dtype(uint8), + "uint16": dtype(uint16), + "uint32": dtype(uint32), + "uint64": dtype(uint64), + "float32": dtype(float32), + "float64": dtype(float64), + "complex64": dtype(complex64), + "complex128": dtype(complex128), + } + if kind == "bool": + return {"bool": bool} + if kind == "signed integer": + return { + "int8": dtype(int8), + "int16": dtype(int16), + "int32": dtype(int32), + "int64": dtype(int64), + } + if kind == "unsigned integer": + return { + "uint8": dtype(uint8), + "uint16": dtype(uint16), + "uint32": dtype(uint32), + "uint64": dtype(uint64), + } + if kind == "integral": + return { + "int8": dtype(int8), + "int16": dtype(int16), + "int32": dtype(int32), + "int64": dtype(int64), + "uint8": dtype(uint8), + "uint16": dtype(uint16), + "uint32": dtype(uint32), + "uint64": dtype(uint64), + } + if kind == "real floating": + return { + "float32": dtype(float32), + "float64": dtype(float64), + } + if kind == "complex floating": + return { + "complex64": dtype(complex64), + "complex128": dtype(complex128), + } + if kind == "numeric": + return { + "int8": dtype(int8), + "int16": dtype(int16), + "int32": dtype(int32), + "int64": dtype(int64), + "uint8": dtype(uint8), + "uint16": dtype(uint16), + "uint32": dtype(uint32), + "uint64": dtype(uint64), + "float32": dtype(float32), + "float64": dtype(float64), + "complex64": dtype(complex64), + "complex128": dtype(complex128), + } + if isinstance(kind, tuple): + res = {} + for k in kind: + res.update(self.dtypes(kind=k)) + return res + raise ValueError(f"unsupported kind: {kind!r}") + + def devices(self): + """ + The devices supported by NumPy. + + For NumPy, this always returns ``['cpu']``. + + Returns + ------- + devices : list of str + The devices supported by NumPy. + + See Also + -------- + __array_namespace_info__.capabilities, + __array_namespace_info__.default_device, + __array_namespace_info__.default_dtypes, + __array_namespace_info__.dtypes + + Examples + -------- + >>> info = np.__array_namespace_info__() + >>> info.devices() + ['cpu'] + + """ + return ["cpu"] diff --git a/.venv/lib/python3.12/site-packages/numpy/_array_api_info.pyi b/.venv/lib/python3.12/site-packages/numpy/_array_api_info.pyi new file mode 100644 index 0000000000000000000000000000000000000000..ee9f8a5660c3a72cda2266b0238a5a50c36de8fd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/_array_api_info.pyi @@ -0,0 +1,207 @@ +from typing import ( + ClassVar, + Literal, + Never, + TypeAlias, + TypedDict, + TypeVar, + final, + overload, + type_check_only, +) + +import numpy as np + +_Device: TypeAlias = Literal["cpu"] +_DeviceLike: TypeAlias = _Device | None + +_Capabilities = TypedDict( + "_Capabilities", + { + "boolean indexing": Literal[True], + "data-dependent shapes": Literal[True], + }, +) + +_DefaultDTypes = TypedDict( + "_DefaultDTypes", + { + "real floating": np.dtype[np.float64], + "complex floating": np.dtype[np.complex128], + "integral": np.dtype[np.intp], + "indexing": np.dtype[np.intp], + }, +) + +_KindBool: TypeAlias = Literal["bool"] +_KindInt: TypeAlias = Literal["signed integer"] +_KindUInt: TypeAlias = Literal["unsigned integer"] +_KindInteger: TypeAlias = Literal["integral"] +_KindFloat: TypeAlias = Literal["real floating"] +_KindComplex: TypeAlias = Literal["complex floating"] +_KindNumber: TypeAlias = Literal["numeric"] +_Kind: TypeAlias = ( + _KindBool + | _KindInt + | _KindUInt + | _KindInteger + | _KindFloat + | _KindComplex + | _KindNumber +) + +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") +_T3 = TypeVar("_T3") +_Permute1: TypeAlias = _T1 | tuple[_T1] +_Permute2: TypeAlias = tuple[_T1, _T2] | tuple[_T2, _T1] +_Permute3: TypeAlias = ( + tuple[_T1, _T2, _T3] | tuple[_T1, _T3, _T2] + | tuple[_T2, _T1, _T3] | tuple[_T2, _T3, _T1] + | tuple[_T3, _T1, _T2] | tuple[_T3, _T2, _T1] +) + +@type_check_only +class _DTypesBool(TypedDict): + bool: np.dtype[np.bool] + +@type_check_only +class _DTypesInt(TypedDict): + int8: np.dtype[np.int8] + int16: np.dtype[np.int16] + int32: np.dtype[np.int32] + int64: np.dtype[np.int64] + +@type_check_only +class _DTypesUInt(TypedDict): + uint8: np.dtype[np.uint8] + uint16: np.dtype[np.uint16] + uint32: np.dtype[np.uint32] + uint64: np.dtype[np.uint64] + +@type_check_only +class _DTypesInteger(_DTypesInt, _DTypesUInt): ... + +@type_check_only +class _DTypesFloat(TypedDict): + float32: np.dtype[np.float32] + float64: np.dtype[np.float64] + +@type_check_only +class _DTypesComplex(TypedDict): + complex64: np.dtype[np.complex64] + complex128: np.dtype[np.complex128] + +@type_check_only +class _DTypesNumber(_DTypesInteger, _DTypesFloat, _DTypesComplex): ... + +@type_check_only +class _DTypes(_DTypesBool, _DTypesNumber): ... + +@type_check_only +class _DTypesUnion(TypedDict, total=False): + bool: np.dtype[np.bool] + int8: np.dtype[np.int8] + int16: np.dtype[np.int16] + int32: np.dtype[np.int32] + int64: np.dtype[np.int64] + uint8: np.dtype[np.uint8] + uint16: np.dtype[np.uint16] + uint32: np.dtype[np.uint32] + uint64: np.dtype[np.uint64] + float32: np.dtype[np.float32] + float64: np.dtype[np.float64] + complex64: np.dtype[np.complex64] + complex128: np.dtype[np.complex128] + +_EmptyDict: TypeAlias = dict[Never, Never] + +@final +class __array_namespace_info__: + __module__: ClassVar[Literal['numpy']] + + def capabilities(self) -> _Capabilities: ... + def default_device(self) -> _Device: ... + def default_dtypes( + self, + *, + device: _DeviceLike = ..., + ) -> _DefaultDTypes: ... + def devices(self) -> list[_Device]: ... + + @overload + def dtypes( + self, + *, + device: _DeviceLike = ..., + kind: None = ..., + ) -> _DTypes: ... + @overload + def dtypes( + self, + *, + device: _DeviceLike = ..., + kind: _Permute1[_KindBool], + ) -> _DTypesBool: ... + @overload + def dtypes( + self, + *, + device: _DeviceLike = ..., + kind: _Permute1[_KindInt], + ) -> _DTypesInt: ... + @overload + def dtypes( + self, + *, + device: _DeviceLike = ..., + kind: _Permute1[_KindUInt], + ) -> _DTypesUInt: ... + @overload + def dtypes( + self, + *, + device: _DeviceLike = ..., + kind: _Permute1[_KindFloat], + ) -> _DTypesFloat: ... + @overload + def dtypes( + self, + *, + device: _DeviceLike = ..., + kind: _Permute1[_KindComplex], + ) -> _DTypesComplex: ... + @overload + def dtypes( + self, + *, + device: _DeviceLike = ..., + kind: ( + _Permute1[_KindInteger] + | _Permute2[_KindInt, _KindUInt] + ), + ) -> _DTypesInteger: ... + @overload + def dtypes( + self, + *, + device: _DeviceLike = ..., + kind: ( + _Permute1[_KindNumber] + | _Permute3[_KindInteger, _KindFloat, _KindComplex] + ), + ) -> _DTypesNumber: ... + @overload + def dtypes( + self, + *, + device: _DeviceLike = ..., + kind: tuple[()], + ) -> _EmptyDict: ... + @overload + def dtypes( + self, + *, + device: _DeviceLike = ..., + kind: tuple[_Kind, ...], + ) -> _DTypesUnion: ... diff --git a/.venv/lib/python3.12/site-packages/numpy/_configtool.py b/.venv/lib/python3.12/site-packages/numpy/_configtool.py new file mode 100644 index 0000000000000000000000000000000000000000..db7831c339518ba3a2a939eaa0006eded43ad129 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/_configtool.py @@ -0,0 +1,39 @@ +import argparse +import sys +from pathlib import Path + +from .lib._utils_impl import get_include +from .version import __version__ + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument( + "--version", + action="version", + version=__version__, + help="Print the version and exit.", + ) + parser.add_argument( + "--cflags", + action="store_true", + help="Compile flag needed when using the NumPy headers.", + ) + parser.add_argument( + "--pkgconfigdir", + action="store_true", + help=("Print the pkgconfig directory in which `numpy.pc` is stored " + "(useful for setting $PKG_CONFIG_PATH)."), + ) + args = parser.parse_args() + if not sys.argv[1:]: + parser.print_help() + if args.cflags: + print("-I" + get_include()) + if args.pkgconfigdir: + _path = Path(get_include()) / '..' / 'lib' / 'pkgconfig' + print(_path.resolve()) + + +if __name__ == "__main__": + main() diff --git a/.venv/lib/python3.12/site-packages/numpy/_configtool.pyi b/.venv/lib/python3.12/site-packages/numpy/_configtool.pyi new file mode 100644 index 0000000000000000000000000000000000000000..7e7363e797f3f5a33f66efd0349814c562e349e6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/_configtool.pyi @@ -0,0 +1 @@ +def main() -> None: ... diff --git a/.venv/lib/python3.12/site-packages/numpy/_distributor_init.py b/.venv/lib/python3.12/site-packages/numpy/_distributor_init.py new file mode 100644 index 0000000000000000000000000000000000000000..f608036a2405528c0ddc4874b25b223dbe22c385 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/_distributor_init.py @@ -0,0 +1,15 @@ +""" Distributor init file + +Distributors: you can add custom code here to support particular distributions +of numpy. + +For example, this is a good place to put any BLAS/LAPACK initialization code. + +The numpy standard source distribution will not put code in this file, so you +can safely replace this file with your own version. +""" + +try: + from . import _distributor_init_local # noqa: F401 +except ImportError: + pass diff --git a/.venv/lib/python3.12/site-packages/numpy/_distributor_init.pyi b/.venv/lib/python3.12/site-packages/numpy/_distributor_init.pyi new file mode 100644 index 0000000000000000000000000000000000000000..94456aba2bcfaf1166eeb81199dff4515c8b9474 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/_distributor_init.pyi @@ -0,0 +1 @@ +# intentionally left blank diff --git a/.venv/lib/python3.12/site-packages/numpy/_expired_attrs_2_0.py b/.venv/lib/python3.12/site-packages/numpy/_expired_attrs_2_0.py new file mode 100644 index 0000000000000000000000000000000000000000..1397134e3f8cacb8dfb32683f8ddbcdcbd6d21f4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/_expired_attrs_2_0.py @@ -0,0 +1,79 @@ +""" +Dict of expired attributes that are discontinued since 2.0 release. +Each item is associated with a migration note. +""" + +__expired_attributes__ = { + "geterrobj": "Use the np.errstate context manager instead.", + "seterrobj": "Use the np.errstate context manager instead.", + "cast": "Use `np.asarray(arr, dtype=dtype)` instead.", + "source": "Use `inspect.getsource` instead.", + "lookfor": "Search NumPy's documentation directly.", + "who": "Use an IDE variable explorer or `locals()` instead.", + "fastCopyAndTranspose": "Use `arr.T.copy()` instead.", + "set_numeric_ops": + "For the general case, use `PyUFunc_ReplaceLoopBySignature`. " + "For ndarray subclasses, define the ``__array_ufunc__`` method " + "and override the relevant ufunc.", + "NINF": "Use `-np.inf` instead.", + "PINF": "Use `np.inf` instead.", + "NZERO": "Use `-0.0` instead.", + "PZERO": "Use `0.0` instead.", + "add_newdoc": + "It's still available as `np.lib.add_newdoc`.", + "add_docstring": + "It's still available as `np.lib.add_docstring`.", + "add_newdoc_ufunc": + "It's an internal function and doesn't have a replacement.", + "safe_eval": "Use `ast.literal_eval` instead.", + "float_": "Use `np.float64` instead.", + "complex_": "Use `np.complex128` instead.", + "longfloat": "Use `np.longdouble` instead.", + "singlecomplex": "Use `np.complex64` instead.", + "cfloat": "Use `np.complex128` instead.", + "longcomplex": "Use `np.clongdouble` instead.", + "clongfloat": "Use `np.clongdouble` instead.", + "string_": "Use `np.bytes_` instead.", + "unicode_": "Use `np.str_` instead.", + "Inf": "Use `np.inf` instead.", + "Infinity": "Use `np.inf` instead.", + "NaN": "Use `np.nan` instead.", + "infty": "Use `np.inf` instead.", + "issctype": "Use `issubclass(rep, np.generic)` instead.", + "maximum_sctype": + "Use a specific dtype instead. You should avoid relying " + "on any implicit mechanism and select the largest dtype of " + "a kind explicitly in the code.", + "obj2sctype": "Use `np.dtype(obj).type` instead.", + "sctype2char": "Use `np.dtype(obj).char` instead.", + "sctypes": "Access dtypes explicitly instead.", + "issubsctype": "Use `np.issubdtype` instead.", + "set_string_function": + "Use `np.set_printoptions` instead with a formatter for " + "custom printing of NumPy objects.", + "asfarray": "Use `np.asarray` with a proper dtype instead.", + "issubclass_": "Use `issubclass` builtin instead.", + "tracemalloc_domain": "It's now available from `np.lib`.", + "mat": "Use `np.asmatrix` instead.", + "recfromcsv": "Use `np.genfromtxt` with comma delimiter instead.", + "recfromtxt": "Use `np.genfromtxt` instead.", + "deprecate": "Emit `DeprecationWarning` with `warnings.warn` directly, " + "or use `typing.deprecated`.", + "deprecate_with_doc": "Emit `DeprecationWarning` with `warnings.warn` " + "directly, or use `typing.deprecated`.", + "disp": "Use your own printing function instead.", + "find_common_type": + "Use `numpy.promote_types` or `numpy.result_type` instead. " + "To achieve semantics for the `scalar_types` argument, use " + "`numpy.result_type` and pass the Python values `0`, `0.0`, or `0j`.", + "round_": "Use `np.round` instead.", + "get_array_wrap": "", + "DataSource": "It's still available as `np.lib.npyio.DataSource`.", + "nbytes": "Use `np.dtype().itemsize` instead.", + "byte_bounds": "Now it's available under `np.lib.array_utils.byte_bounds`", + "compare_chararrays": + "It's still available as `np.char.compare_chararrays`.", + "format_parser": "It's still available as `np.rec.format_parser`.", + "alltrue": "Use `np.all` instead.", + "sometrue": "Use `np.any` instead.", +} diff --git a/.venv/lib/python3.12/site-packages/numpy/_expired_attrs_2_0.pyi b/.venv/lib/python3.12/site-packages/numpy/_expired_attrs_2_0.pyi new file mode 100644 index 0000000000000000000000000000000000000000..14524689c1c582601b4e1a6158001f65ac884fb3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/_expired_attrs_2_0.pyi @@ -0,0 +1,62 @@ +from typing import Final, TypedDict, final, type_check_only + +@final +@type_check_only +class _ExpiredAttributesType(TypedDict): + geterrobj: str + seterrobj: str + cast: str + source: str + lookfor: str + who: str + fastCopyAndTranspose: str + set_numeric_ops: str + NINF: str + PINF: str + NZERO: str + PZERO: str + add_newdoc: str + add_docstring: str + add_newdoc_ufunc: str + safe_eval: str + float_: str + complex_: str + longfloat: str + singlecomplex: str + cfloat: str + longcomplex: str + clongfloat: str + string_: str + unicode_: str + Inf: str + Infinity: str + NaN: str + infty: str + issctype: str + maximum_sctype: str + obj2sctype: str + sctype2char: str + sctypes: str + issubsctype: str + set_string_function: str + asfarray: str + issubclass_: str + tracemalloc_domain: str + mat: str + recfromcsv: str + recfromtxt: str + deprecate: str + deprecate_with_doc: str + disp: str + find_common_type: str + round_: str + get_array_wrap: str + DataSource: str + nbytes: str + byte_bounds: str + compare_chararrays: str + format_parser: str + alltrue: str + sometrue: str + +__expired_attributes__: Final[_ExpiredAttributesType] = ... diff --git a/.venv/lib/python3.12/site-packages/numpy/_globals.py b/.venv/lib/python3.12/site-packages/numpy/_globals.py new file mode 100644 index 0000000000000000000000000000000000000000..5f838ba91544cfdadaf30287b39093a0484aa194 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/_globals.py @@ -0,0 +1,96 @@ +""" +Module defining global singleton classes. + +This module raises a RuntimeError if an attempt to reload it is made. In that +way the identities of the classes defined here are fixed and will remain so +even if numpy itself is reloaded. In particular, a function like the following +will still work correctly after numpy is reloaded:: + + def foo(arg=np._NoValue): + if arg is np._NoValue: + ... + +That was not the case when the singleton classes were defined in the numpy +``__init__.py`` file. See gh-7844 for a discussion of the reload problem that +motivated this module. + +""" +import enum + +from ._utils import set_module as _set_module + +__all__ = ['_NoValue', '_CopyMode'] + + +# Disallow reloading this module so as to preserve the identities of the +# classes defined here. +if '_is_loaded' in globals(): + raise RuntimeError('Reloading numpy._globals is not allowed') +_is_loaded = True + + +class _NoValueType: + """Special keyword value. + + The instance of this class may be used as the default value assigned to a + keyword if no other obvious default (e.g., `None`) is suitable, + + Common reasons for using this keyword are: + + - A new keyword is added to a function, and that function forwards its + inputs to another function or method which can be defined outside of + NumPy. For example, ``np.std(x)`` calls ``x.std``, so when a ``keepdims`` + keyword was added that could only be forwarded if the user explicitly + specified ``keepdims``; downstream array libraries may not have added + the same keyword, so adding ``x.std(..., keepdims=keepdims)`` + unconditionally could have broken previously working code. + - A keyword is being deprecated, and a deprecation warning must only be + emitted when the keyword is used. + + """ + __instance = None + + def __new__(cls): + # ensure that only one instance exists + if not cls.__instance: + cls.__instance = super().__new__(cls) + return cls.__instance + + def __repr__(self): + return "" + + +_NoValue = _NoValueType() + + +@_set_module("numpy") +class _CopyMode(enum.Enum): + """ + An enumeration for the copy modes supported + by numpy.copy() and numpy.array(). The following three modes are supported, + + - ALWAYS: This means that a deep copy of the input + array will always be taken. + - IF_NEEDED: This means that a deep copy of the input + array will be taken only if necessary. + - NEVER: This means that the deep copy will never be taken. + If a copy cannot be avoided then a `ValueError` will be + raised. + + Note that the buffer-protocol could in theory do copies. NumPy currently + assumes an object exporting the buffer protocol will never do this. + """ + + ALWAYS = True + NEVER = False + IF_NEEDED = 2 + + def __bool__(self): + # For backwards compatibility + if self == _CopyMode.ALWAYS: + return True + + if self == _CopyMode.NEVER: + return False + + raise ValueError(f"{self} is neither True nor False.") diff --git a/.venv/lib/python3.12/site-packages/numpy/_globals.pyi b/.venv/lib/python3.12/site-packages/numpy/_globals.pyi new file mode 100644 index 0000000000000000000000000000000000000000..b2231a9636b0863be24555734d66df6da3464ac4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/_globals.pyi @@ -0,0 +1,17 @@ +__all__ = ["_CopyMode", "_NoValue"] + +import enum +from typing import Final, final + +@final +class _CopyMode(enum.Enum): + ALWAYS = True + NEVER = False + IF_NEEDED = 2 + + def __bool__(self, /) -> bool: ... + +@final +class _NoValueType: ... + +_NoValue: Final[_NoValueType] = ... diff --git a/.venv/lib/python3.12/site-packages/numpy/_pytesttester.py b/.venv/lib/python3.12/site-packages/numpy/_pytesttester.py new file mode 100644 index 0000000000000000000000000000000000000000..77342e44aea0251c513dd0ed7a8fb67b074e298f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/_pytesttester.py @@ -0,0 +1,201 @@ +""" +Pytest test running. + +This module implements the ``test()`` function for NumPy modules. The usual +boiler plate for doing that is to put the following in the module +``__init__.py`` file:: + + from numpy._pytesttester import PytestTester + test = PytestTester(__name__) + del PytestTester + + +Warnings filtering and other runtime settings should be dealt with in the +``pytest.ini`` file in the numpy repo root. The behavior of the test depends on +whether or not that file is found as follows: + +* ``pytest.ini`` is present (develop mode) + All warnings except those explicitly filtered out are raised as error. +* ``pytest.ini`` is absent (release mode) + DeprecationWarnings and PendingDeprecationWarnings are ignored, other + warnings are passed through. + +In practice, tests run from the numpy repo are run in development mode with +``spin``, through the standard ``spin test`` invocation or from an inplace +build with ``pytest numpy``. + +This module is imported by every numpy subpackage, so lies at the top level to +simplify circular import issues. For the same reason, it contains no numpy +imports at module scope, instead importing numpy within function calls. +""" +import os +import sys + +__all__ = ['PytestTester'] + + +def _show_numpy_info(): + import numpy as np + + print(f"NumPy version {np.__version__}") + info = np.lib._utils_impl._opt_info() + print("NumPy CPU features: ", (info or 'nothing enabled')) + + +class PytestTester: + """ + Pytest test runner. + + A test function is typically added to a package's __init__.py like so:: + + from numpy._pytesttester import PytestTester + test = PytestTester(__name__).test + del PytestTester + + Calling this test function finds and runs all tests associated with the + module and all its sub-modules. + + Attributes + ---------- + module_name : str + Full path to the package to test. + + Parameters + ---------- + module_name : module name + The name of the module to test. + + Notes + ----- + Unlike the previous ``nose``-based implementation, this class is not + publicly exposed as it performs some ``numpy``-specific warning + suppression. + + """ + def __init__(self, module_name): + self.module_name = module_name + self.__module__ = module_name + + def __call__(self, label='fast', verbose=1, extra_argv=None, + doctests=False, coverage=False, durations=-1, tests=None): + """ + Run tests for module using pytest. + + Parameters + ---------- + label : {'fast', 'full'}, optional + Identifies the tests to run. When set to 'fast', tests decorated + with `pytest.mark.slow` are skipped, when 'full', the slow marker + is ignored. + verbose : int, optional + Verbosity value for test outputs, in the range 1-3. Default is 1. + extra_argv : list, optional + List with any extra arguments to pass to pytests. + doctests : bool, optional + .. note:: Not supported + coverage : bool, optional + If True, report coverage of NumPy code. Default is False. + Requires installation of (pip) pytest-cov. + durations : int, optional + If < 0, do nothing, If 0, report time of all tests, if > 0, + report the time of the slowest `timer` tests. Default is -1. + tests : test or list of tests + Tests to be executed with pytest '--pyargs' + + Returns + ------- + result : bool + Return True on success, false otherwise. + + Notes + ----- + Each NumPy module exposes `test` in its namespace to run all tests for + it. For example, to run all tests for numpy.lib: + + >>> np.lib.test() #doctest: +SKIP + + Examples + -------- + >>> result = np.lib.test() #doctest: +SKIP + ... + 1023 passed, 2 skipped, 6 deselected, 1 xfailed in 10.39 seconds + >>> result + True + + """ + import warnings + + import pytest + + module = sys.modules[self.module_name] + module_path = os.path.abspath(module.__path__[0]) + + # setup the pytest arguments + pytest_args = ["-l"] + + # offset verbosity. The "-q" cancels a "-v". + pytest_args += ["-q"] + + if sys.version_info < (3, 12): + with warnings.catch_warnings(): + warnings.simplefilter("always") + # Filter out distutils cpu warnings (could be localized to + # distutils tests). ASV has problems with top level import, + # so fetch module for suppression here. + from numpy.distutils import cpuinfo # noqa: F401 + + # Filter out annoying import messages. Want these in both develop and + # release mode. + pytest_args += [ + "-W ignore:Not importing directory", + "-W ignore:numpy.dtype size changed", + "-W ignore:numpy.ufunc size changed", + "-W ignore::UserWarning:cpuinfo", + ] + + # When testing matrices, ignore their PendingDeprecationWarnings + pytest_args += [ + "-W ignore:the matrix subclass is not", + "-W ignore:Importing from numpy.matlib is", + ] + + if doctests: + pytest_args += ["--doctest-modules"] + + if extra_argv: + pytest_args += list(extra_argv) + + if verbose > 1: + pytest_args += ["-" + "v" * (verbose - 1)] + + if coverage: + pytest_args += ["--cov=" + module_path] + + if label == "fast": + # not importing at the top level to avoid circular import of module + from numpy.testing import IS_PYPY + if IS_PYPY: + pytest_args += ["-m", "not slow and not slow_pypy"] + else: + pytest_args += ["-m", "not slow"] + + elif label != "full": + pytest_args += ["-m", label] + + if durations >= 0: + pytest_args += [f"--durations={durations}"] + + if tests is None: + tests = [self.module_name] + + pytest_args += ["--pyargs"] + list(tests) + + # run tests. + _show_numpy_info() + + try: + code = pytest.main(pytest_args) + except SystemExit as exc: + code = exc.code + + return code == 0 diff --git a/.venv/lib/python3.12/site-packages/numpy/_pytesttester.pyi b/.venv/lib/python3.12/site-packages/numpy/_pytesttester.pyi new file mode 100644 index 0000000000000000000000000000000000000000..a12abb1c1a105b624c9fbb580f2da6a10209bcac --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/_pytesttester.pyi @@ -0,0 +1,18 @@ +from collections.abc import Iterable +from typing import Literal as L + +__all__ = ["PytestTester"] + +class PytestTester: + module_name: str + def __init__(self, module_name: str) -> None: ... + def __call__( + self, + label: L["fast", "full"] = ..., + verbose: int = ..., + extra_argv: Iterable[str] | None = ..., + doctests: L[False] = ..., + coverage: bool = ..., + durations: int = ..., + tests: Iterable[str] | None = ..., + ) -> bool: ... diff --git a/.venv/lib/python3.12/site-packages/numpy/conftest.py b/.venv/lib/python3.12/site-packages/numpy/conftest.py new file mode 100644 index 0000000000000000000000000000000000000000..fde4defc926dd344b23221b10c536e6612108715 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/conftest.py @@ -0,0 +1,258 @@ +""" +Pytest configuration and fixtures for the Numpy test suite. +""" +import os +import string +import sys +import tempfile +import warnings +from contextlib import contextmanager + +import hypothesis +import pytest + +import numpy +import numpy as np +from numpy._core._multiarray_tests import get_fpu_mode +from numpy._core.tests._natype import get_stringdtype_dtype, pd_NA +from numpy.testing._private.utils import NOGIL_BUILD + +try: + from scipy_doctest.conftest import dt_config + HAVE_SCPDT = True +except ModuleNotFoundError: + HAVE_SCPDT = False + + +_old_fpu_mode = None +_collect_results = {} + +# Use a known and persistent tmpdir for hypothesis' caches, which +# can be automatically cleared by the OS or user. +hypothesis.configuration.set_hypothesis_home_dir( + os.path.join(tempfile.gettempdir(), ".hypothesis") +) + +# We register two custom profiles for Numpy - for details see +# https://hypothesis.readthedocs.io/en/latest/settings.html +# The first is designed for our own CI runs; the latter also +# forces determinism and is designed for use via np.test() +hypothesis.settings.register_profile( + name="numpy-profile", deadline=None, print_blob=True, +) +hypothesis.settings.register_profile( + name="np.test() profile", + deadline=None, print_blob=True, database=None, derandomize=True, + suppress_health_check=list(hypothesis.HealthCheck), +) +# Note that the default profile is chosen based on the presence +# of pytest.ini, but can be overridden by passing the +# --hypothesis-profile=NAME argument to pytest. +_pytest_ini = os.path.join(os.path.dirname(__file__), "..", "pytest.ini") +hypothesis.settings.load_profile( + "numpy-profile" if os.path.isfile(_pytest_ini) else "np.test() profile" +) + +# The experimentalAPI is used in _umath_tests +os.environ["NUMPY_EXPERIMENTAL_DTYPE_API"] = "1" + +def pytest_configure(config): + config.addinivalue_line("markers", + "valgrind_error: Tests that are known to error under valgrind.") + config.addinivalue_line("markers", + "leaks_references: Tests that are known to leak references.") + config.addinivalue_line("markers", + "slow: Tests that are very slow.") + config.addinivalue_line("markers", + "slow_pypy: Tests that are very slow on pypy.") + + +def pytest_addoption(parser): + parser.addoption("--available-memory", action="store", default=None, + help=("Set amount of memory available for running the " + "test suite. This can result to tests requiring " + "especially large amounts of memory to be skipped. " + "Equivalent to setting environment variable " + "NPY_AVAILABLE_MEM. Default: determined" + "automatically.")) + + +gil_enabled_at_start = True +if NOGIL_BUILD: + gil_enabled_at_start = sys._is_gil_enabled() + + +def pytest_sessionstart(session): + available_mem = session.config.getoption('available_memory') + if available_mem is not None: + os.environ['NPY_AVAILABLE_MEM'] = available_mem + + +def pytest_terminal_summary(terminalreporter, exitstatus, config): + if NOGIL_BUILD and not gil_enabled_at_start and sys._is_gil_enabled(): + tr = terminalreporter + tr.ensure_newline() + tr.section("GIL re-enabled", sep="=", red=True, bold=True) + tr.line("The GIL was re-enabled at runtime during the tests.") + tr.line("This can happen with no test failures if the RuntimeWarning") + tr.line("raised by Python when this happens is filtered by a test.") + tr.line("") + tr.line("Please ensure all new C modules declare support for running") + tr.line("without the GIL. Any new tests that intentionally imports ") + tr.line("code that re-enables the GIL should do so in a subprocess.") + pytest.exit("GIL re-enabled during tests", returncode=1) + +# FIXME when yield tests are gone. +@pytest.hookimpl() +def pytest_itemcollected(item): + """ + Check FPU precision mode was not changed during test collection. + + The clumsy way we do it here is mainly necessary because numpy + still uses yield tests, which can execute code at test collection + time. + """ + global _old_fpu_mode + + mode = get_fpu_mode() + + if _old_fpu_mode is None: + _old_fpu_mode = mode + elif mode != _old_fpu_mode: + _collect_results[item] = (_old_fpu_mode, mode) + _old_fpu_mode = mode + + +@pytest.fixture(scope="function", autouse=True) +def check_fpu_mode(request): + """ + Check FPU precision mode was not changed during the test. + """ + old_mode = get_fpu_mode() + yield + new_mode = get_fpu_mode() + + if old_mode != new_mode: + raise AssertionError(f"FPU precision mode changed from {old_mode:#x} to " + f"{new_mode:#x} during the test") + + collect_result = _collect_results.get(request.node) + if collect_result is not None: + old_mode, new_mode = collect_result + raise AssertionError(f"FPU precision mode changed from {old_mode:#x} to " + f"{new_mode:#x} when collecting the test") + + +@pytest.fixture(autouse=True) +def add_np(doctest_namespace): + doctest_namespace['np'] = numpy + +@pytest.fixture(autouse=True) +def env_setup(monkeypatch): + monkeypatch.setenv('PYTHONHASHSEED', '0') + + +if HAVE_SCPDT: + + @contextmanager + def warnings_errors_and_rng(test=None): + """Filter out the wall of DeprecationWarnings. + """ + msgs = ["The numpy.linalg.linalg", + "The numpy.fft.helper", + "dep_util", + "pkg_resources", + "numpy.core.umath", + "msvccompiler", + "Deprecated call", + "numpy.core", + "Importing from numpy.matlib", + "This function is deprecated.", # random_integers + "Data type alias 'a'", # numpy.rec.fromfile + "Arrays of 2-dimensional vectors", # matlib.cross + "`in1d` is deprecated", ] + msg = "|".join(msgs) + + msgs_r = [ + "invalid value encountered", + "divide by zero encountered" + ] + msg_r = "|".join(msgs_r) + + with warnings.catch_warnings(): + warnings.filterwarnings( + 'ignore', category=DeprecationWarning, message=msg + ) + warnings.filterwarnings( + 'ignore', category=RuntimeWarning, message=msg_r + ) + yield + + # find and check doctests under this context manager + dt_config.user_context_mgr = warnings_errors_and_rng + + # numpy specific tweaks from refguide-check + dt_config.rndm_markers.add('#uninitialized') + dt_config.rndm_markers.add('# uninitialized') + + # make the checker pick on mismatched dtypes + dt_config.strict_check = True + + import doctest + dt_config.optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS + + # recognize the StringDType repr + dt_config.check_namespace['StringDType'] = numpy.dtypes.StringDType + + # temporary skips + dt_config.skiplist = { + 'numpy.savez', # unclosed file + 'numpy.matlib.savez', + 'numpy.__array_namespace_info__', + 'numpy.matlib.__array_namespace_info__', + } + + # xfail problematic tutorials + dt_config.pytest_extra_xfail = { + 'how-to-verify-bug.rst': '', + 'c-info.ufunc-tutorial.rst': '', + 'basics.interoperability.rst': 'needs pandas', + 'basics.dispatch.rst': 'errors out in /testing/overrides.py', + 'basics.subclassing.rst': '.. testcode:: admonitions not understood', + 'misc.rst': 'manipulates warnings', + } + + # ignores are for things fail doctest collection (optionals etc) + dt_config.pytest_extra_ignore = [ + 'numpy/distutils', + 'numpy/_core/cversions.py', + 'numpy/_pyinstaller', + 'numpy/random/_examples', + 'numpy/f2py/_backends/_distutils.py', + ] + + +@pytest.fixture +def random_string_list(): + chars = list(string.ascii_letters + string.digits) + chars = np.array(chars, dtype="U1") + ret = np.random.choice(chars, size=100 * 10, replace=True) + return ret.view("U100") + + +@pytest.fixture(params=[True, False]) +def coerce(request): + return request.param + + +@pytest.fixture( + params=["unset", None, pd_NA, np.nan, float("nan"), "__nan__"], + ids=["unset", "None", "pandas.NA", "np.nan", "float('nan')", "string nan"], +) +def na_object(request): + return request.param + + +@pytest.fixture() +def dtype(na_object, coerce): + return get_stringdtype_dtype(na_object, coerce) diff --git a/.venv/lib/python3.12/site-packages/numpy/dtypes.py b/.venv/lib/python3.12/site-packages/numpy/dtypes.py new file mode 100644 index 0000000000000000000000000000000000000000..550a29e18f292e65600108804636b833c75d1be4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/dtypes.py @@ -0,0 +1,41 @@ +""" +This module is home to specific dtypes related functionality and their classes. +For more general information about dtypes, also see `numpy.dtype` and +:ref:`arrays.dtypes`. + +Similar to the builtin ``types`` module, this submodule defines types (classes) +that are not widely used directly. + +.. versionadded:: NumPy 1.25 + + The dtypes module is new in NumPy 1.25. Previously DType classes were + only accessible indirectly. + + +DType classes +------------- + +The following are the classes of the corresponding NumPy dtype instances and +NumPy scalar types. The classes can be used in ``isinstance`` checks and can +also be instantiated or used directly. Direct use of these classes is not +typical, since their scalar counterparts (e.g. ``np.float64``) or strings +like ``"float64"`` can be used. +""" + +# See doc/source/reference/routines.dtypes.rst for module-level docs + +__all__ = [] + + +def _add_dtype_helper(DType, alias): + # Function to add DTypes a bit more conveniently without channeling them + # through `numpy._core._multiarray_umath` namespace or similar. + from numpy import dtypes + + setattr(dtypes, DType.__name__, DType) + __all__.append(DType.__name__) + + if alias: + alias = alias.removeprefix("numpy.dtypes.") + setattr(dtypes, alias, DType) + __all__.append(alias) diff --git a/.venv/lib/python3.12/site-packages/numpy/dtypes.pyi b/.venv/lib/python3.12/site-packages/numpy/dtypes.pyi new file mode 100644 index 0000000000000000000000000000000000000000..007dc643c0e308a43e6085a4e4cc0ded24f49fba --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/dtypes.pyi @@ -0,0 +1,631 @@ +# ruff: noqa: ANN401 +from typing import ( + Any, + Generic, + LiteralString, + Never, + NoReturn, + Self, + TypeAlias, + final, + overload, + type_check_only, +) +from typing import Literal as L + +from typing_extensions import TypeVar + +import numpy as np + +__all__ = [ # noqa: RUF022 + 'BoolDType', + 'Int8DType', + 'ByteDType', + 'UInt8DType', + 'UByteDType', + 'Int16DType', + 'ShortDType', + 'UInt16DType', + 'UShortDType', + 'Int32DType', + 'IntDType', + 'UInt32DType', + 'UIntDType', + 'Int64DType', + 'LongDType', + 'UInt64DType', + 'ULongDType', + 'LongLongDType', + 'ULongLongDType', + 'Float16DType', + 'Float32DType', + 'Float64DType', + 'LongDoubleDType', + 'Complex64DType', + 'Complex128DType', + 'CLongDoubleDType', + 'ObjectDType', + 'BytesDType', + 'StrDType', + 'VoidDType', + 'DateTime64DType', + 'TimeDelta64DType', + 'StringDType', +] + +# Helper base classes (typing-only) + +_ScalarT_co = TypeVar("_ScalarT_co", bound=np.generic, covariant=True) + +@type_check_only +class _SimpleDType(np.dtype[_ScalarT_co], Generic[_ScalarT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + names: None # pyright: ignore[reportIncompatibleVariableOverride] + def __new__(cls, /) -> Self: ... + def __getitem__(self, key: Any, /) -> NoReturn: ... + @property + def base(self) -> np.dtype[_ScalarT_co]: ... + @property + def fields(self) -> None: ... + @property + def isalignedstruct(self) -> L[False]: ... + @property + def isnative(self) -> L[True]: ... + @property + def ndim(self) -> L[0]: ... + @property + def shape(self) -> tuple[()]: ... + @property + def subdtype(self) -> None: ... + +@type_check_only +class _LiteralDType(_SimpleDType[_ScalarT_co], Generic[_ScalarT_co]): # type: ignore[misc] + @property + def flags(self) -> L[0]: ... + @property + def hasobject(self) -> L[False]: ... + +# Helper mixins (typing-only): + +_KindT_co = TypeVar("_KindT_co", bound=LiteralString, covariant=True) +_CharT_co = TypeVar("_CharT_co", bound=LiteralString, covariant=True) +_NumT_co = TypeVar("_NumT_co", bound=int, covariant=True) + +@type_check_only +class _TypeCodes(Generic[_KindT_co, _CharT_co, _NumT_co]): + @final + @property + def kind(self) -> _KindT_co: ... + @final + @property + def char(self) -> _CharT_co: ... + @final + @property + def num(self) -> _NumT_co: ... + +@type_check_only +class _NoOrder: + @final + @property + def byteorder(self) -> L["|"]: ... + +@type_check_only +class _NativeOrder: + @final + @property + def byteorder(self) -> L["="]: ... + +_DataSize_co = TypeVar("_DataSize_co", bound=int, covariant=True) +_ItemSize_co = TypeVar("_ItemSize_co", bound=int, covariant=True, default=int) + +@type_check_only +class _NBit(Generic[_DataSize_co, _ItemSize_co]): + @final + @property + def alignment(self) -> _DataSize_co: ... + @final + @property + def itemsize(self) -> _ItemSize_co: ... + +@type_check_only +class _8Bit(_NoOrder, _NBit[L[1], L[1]]): ... + +# Boolean: + +@final +class BoolDType( # type: ignore[misc] + _TypeCodes[L["b"], L["?"], L[0]], + _8Bit, + _LiteralDType[np.bool], +): + @property + def name(self) -> L["bool"]: ... + @property + def str(self) -> L["|b1"]: ... + +# Sized integers: + +@final +class Int8DType( # type: ignore[misc] + _TypeCodes[L["i"], L["b"], L[1]], + _8Bit, + _LiteralDType[np.int8], +): + @property + def name(self) -> L["int8"]: ... + @property + def str(self) -> L["|i1"]: ... + +@final +class UInt8DType( # type: ignore[misc] + _TypeCodes[L["u"], L["B"], L[2]], + _8Bit, + _LiteralDType[np.uint8], +): + @property + def name(self) -> L["uint8"]: ... + @property + def str(self) -> L["|u1"]: ... + +@final +class Int16DType( # type: ignore[misc] + _TypeCodes[L["i"], L["h"], L[3]], + _NativeOrder, + _NBit[L[2], L[2]], + _LiteralDType[np.int16], +): + @property + def name(self) -> L["int16"]: ... + @property + def str(self) -> L["i2"]: ... + +@final +class UInt16DType( # type: ignore[misc] + _TypeCodes[L["u"], L["H"], L[4]], + _NativeOrder, + _NBit[L[2], L[2]], + _LiteralDType[np.uint16], +): + @property + def name(self) -> L["uint16"]: ... + @property + def str(self) -> L["u2"]: ... + +@final +class Int32DType( # type: ignore[misc] + _TypeCodes[L["i"], L["i", "l"], L[5, 7]], + _NativeOrder, + _NBit[L[4], L[4]], + _LiteralDType[np.int32], +): + @property + def name(self) -> L["int32"]: ... + @property + def str(self) -> L["i4"]: ... + +@final +class UInt32DType( # type: ignore[misc] + _TypeCodes[L["u"], L["I", "L"], L[6, 8]], + _NativeOrder, + _NBit[L[4], L[4]], + _LiteralDType[np.uint32], +): + @property + def name(self) -> L["uint32"]: ... + @property + def str(self) -> L["u4"]: ... + +@final +class Int64DType( # type: ignore[misc] + _TypeCodes[L["i"], L["l", "q"], L[7, 9]], + _NativeOrder, + _NBit[L[8], L[8]], + _LiteralDType[np.int64], +): + @property + def name(self) -> L["int64"]: ... + @property + def str(self) -> L["i8"]: ... + +@final +class UInt64DType( # type: ignore[misc] + _TypeCodes[L["u"], L["L", "Q"], L[8, 10]], + _NativeOrder, + _NBit[L[8], L[8]], + _LiteralDType[np.uint64], +): + @property + def name(self) -> L["uint64"]: ... + @property + def str(self) -> L["u8"]: ... + +# Standard C-named version/alias: +# NOTE: Don't make these `Final`: it will break stubtest +ByteDType = Int8DType +UByteDType = UInt8DType +ShortDType = Int16DType +UShortDType = UInt16DType + +@final +class IntDType( # type: ignore[misc] + _TypeCodes[L["i"], L["i"], L[5]], + _NativeOrder, + _NBit[L[4], L[4]], + _LiteralDType[np.intc], +): + @property + def name(self) -> L["int32"]: ... + @property + def str(self) -> L["i4"]: ... + +@final +class UIntDType( # type: ignore[misc] + _TypeCodes[L["u"], L["I"], L[6]], + _NativeOrder, + _NBit[L[4], L[4]], + _LiteralDType[np.uintc], +): + @property + def name(self) -> L["uint32"]: ... + @property + def str(self) -> L["u4"]: ... + +@final +class LongDType( # type: ignore[misc] + _TypeCodes[L["i"], L["l"], L[7]], + _NativeOrder, + _NBit[L[4, 8], L[4, 8]], + _LiteralDType[np.long], +): + @property + def name(self) -> L["int32", "int64"]: ... + @property + def str(self) -> L["i4", "i8"]: ... + +@final +class ULongDType( # type: ignore[misc] + _TypeCodes[L["u"], L["L"], L[8]], + _NativeOrder, + _NBit[L[4, 8], L[4, 8]], + _LiteralDType[np.ulong], +): + @property + def name(self) -> L["uint32", "uint64"]: ... + @property + def str(self) -> L["u4", "u8"]: ... + +@final +class LongLongDType( # type: ignore[misc] + _TypeCodes[L["i"], L["q"], L[9]], + _NativeOrder, + _NBit[L[8], L[8]], + _LiteralDType[np.longlong], +): + @property + def name(self) -> L["int64"]: ... + @property + def str(self) -> L["i8"]: ... + +@final +class ULongLongDType( # type: ignore[misc] + _TypeCodes[L["u"], L["Q"], L[10]], + _NativeOrder, + _NBit[L[8], L[8]], + _LiteralDType[np.ulonglong], +): + @property + def name(self) -> L["uint64"]: ... + @property + def str(self) -> L["u8"]: ... + +# Floats: + +@final +class Float16DType( # type: ignore[misc] + _TypeCodes[L["f"], L["e"], L[23]], + _NativeOrder, + _NBit[L[2], L[2]], + _LiteralDType[np.float16], +): + @property + def name(self) -> L["float16"]: ... + @property + def str(self) -> L["f2"]: ... + +@final +class Float32DType( # type: ignore[misc] + _TypeCodes[L["f"], L["f"], L[11]], + _NativeOrder, + _NBit[L[4], L[4]], + _LiteralDType[np.float32], +): + @property + def name(self) -> L["float32"]: ... + @property + def str(self) -> L["f4"]: ... + +@final +class Float64DType( # type: ignore[misc] + _TypeCodes[L["f"], L["d"], L[12]], + _NativeOrder, + _NBit[L[8], L[8]], + _LiteralDType[np.float64], +): + @property + def name(self) -> L["float64"]: ... + @property + def str(self) -> L["f8"]: ... + +@final +class LongDoubleDType( # type: ignore[misc] + _TypeCodes[L["f"], L["g"], L[13]], + _NativeOrder, + _NBit[L[8, 12, 16], L[8, 12, 16]], + _LiteralDType[np.longdouble], +): + @property + def name(self) -> L["float64", "float96", "float128"]: ... + @property + def str(self) -> L["f8", "f12", "f16"]: ... + +# Complex: + +@final +class Complex64DType( # type: ignore[misc] + _TypeCodes[L["c"], L["F"], L[14]], + _NativeOrder, + _NBit[L[4], L[8]], + _LiteralDType[np.complex64], +): + @property + def name(self) -> L["complex64"]: ... + @property + def str(self) -> L["c8"]: ... + +@final +class Complex128DType( # type: ignore[misc] + _TypeCodes[L["c"], L["D"], L[15]], + _NativeOrder, + _NBit[L[8], L[16]], + _LiteralDType[np.complex128], +): + @property + def name(self) -> L["complex128"]: ... + @property + def str(self) -> L["c16"]: ... + +@final +class CLongDoubleDType( # type: ignore[misc] + _TypeCodes[L["c"], L["G"], L[16]], + _NativeOrder, + _NBit[L[8, 12, 16], L[16, 24, 32]], + _LiteralDType[np.clongdouble], +): + @property + def name(self) -> L["complex128", "complex192", "complex256"]: ... + @property + def str(self) -> L["c16", "c24", "c32"]: ... + +# Python objects: + +@final +class ObjectDType( # type: ignore[misc] + _TypeCodes[L["O"], L["O"], L[17]], + _NoOrder, + _NBit[L[8], L[8]], + _SimpleDType[np.object_], +): + @property + def hasobject(self) -> L[True]: ... + @property + def name(self) -> L["object"]: ... + @property + def str(self) -> L["|O"]: ... + +# Flexible: + +@final +class BytesDType( # type: ignore[misc] + _TypeCodes[L["S"], L["S"], L[18]], + _NoOrder, + _NBit[L[1], _ItemSize_co], + _SimpleDType[np.bytes_], + Generic[_ItemSize_co], +): + def __new__(cls, size: _ItemSize_co, /) -> BytesDType[_ItemSize_co]: ... + @property + def hasobject(self) -> L[False]: ... + @property + def name(self) -> LiteralString: ... + @property + def str(self) -> LiteralString: ... + +@final +class StrDType( # type: ignore[misc] + _TypeCodes[L["U"], L["U"], L[19]], + _NativeOrder, + _NBit[L[4], _ItemSize_co], + _SimpleDType[np.str_], + Generic[_ItemSize_co], +): + def __new__(cls, size: _ItemSize_co, /) -> StrDType[_ItemSize_co]: ... + @property + def hasobject(self) -> L[False]: ... + @property + def name(self) -> LiteralString: ... + @property + def str(self) -> LiteralString: ... + +@final +class VoidDType( # type: ignore[misc] + _TypeCodes[L["V"], L["V"], L[20]], + _NoOrder, + _NBit[L[1], _ItemSize_co], + np.dtype[np.void], # pyright: ignore[reportGeneralTypeIssues] + Generic[_ItemSize_co], +): + # NOTE: `VoidDType(...)` raises a `TypeError` at the moment + def __new__(cls, length: _ItemSize_co, /) -> NoReturn: ... + @property + def base(self) -> Self: ... + @property + def isalignedstruct(self) -> L[False]: ... + @property + def isnative(self) -> L[True]: ... + @property + def ndim(self) -> L[0]: ... + @property + def shape(self) -> tuple[()]: ... + @property + def subdtype(self) -> None: ... + @property + def name(self) -> LiteralString: ... + @property + def str(self) -> LiteralString: ... + +# Other: + +_DateUnit: TypeAlias = L["Y", "M", "W", "D"] +_TimeUnit: TypeAlias = L["h", "m", "s", "ms", "us", "ns", "ps", "fs", "as"] +_DateTimeUnit: TypeAlias = _DateUnit | _TimeUnit + +@final +class DateTime64DType( # type: ignore[misc] + _TypeCodes[L["M"], L["M"], L[21]], + _NativeOrder, + _NBit[L[8], L[8]], + _LiteralDType[np.datetime64], +): + # NOTE: `DateTime64DType(...)` raises a `TypeError` at the moment + # TODO: Once implemented, don't forget the`unit: L["μs"]` overload. + def __new__(cls, unit: _DateTimeUnit, /) -> NoReturn: ... + @property + def name(self) -> L[ + "datetime64", + "datetime64[Y]", + "datetime64[M]", + "datetime64[W]", + "datetime64[D]", + "datetime64[h]", + "datetime64[m]", + "datetime64[s]", + "datetime64[ms]", + "datetime64[us]", + "datetime64[ns]", + "datetime64[ps]", + "datetime64[fs]", + "datetime64[as]", + ]: ... + @property + def str(self) -> L[ + "M8", + "M8[Y]", + "M8[M]", + "M8[W]", + "M8[D]", + "M8[h]", + "M8[m]", + "M8[s]", + "M8[ms]", + "M8[us]", + "M8[ns]", + "M8[ps]", + "M8[fs]", + "M8[as]", + ]: ... + +@final +class TimeDelta64DType( # type: ignore[misc] + _TypeCodes[L["m"], L["m"], L[22]], + _NativeOrder, + _NBit[L[8], L[8]], + _LiteralDType[np.timedelta64], +): + # NOTE: `TimeDelta64DType(...)` raises a `TypeError` at the moment + # TODO: Once implemented, don't forget to overload on `unit: L["μs"]`. + def __new__(cls, unit: _DateTimeUnit, /) -> NoReturn: ... + @property + def name(self) -> L[ + "timedelta64", + "timedelta64[Y]", + "timedelta64[M]", + "timedelta64[W]", + "timedelta64[D]", + "timedelta64[h]", + "timedelta64[m]", + "timedelta64[s]", + "timedelta64[ms]", + "timedelta64[us]", + "timedelta64[ns]", + "timedelta64[ps]", + "timedelta64[fs]", + "timedelta64[as]", + ]: ... + @property + def str(self) -> L[ + "m8", + "m8[Y]", + "m8[M]", + "m8[W]", + "m8[D]", + "m8[h]", + "m8[m]", + "m8[s]", + "m8[ms]", + "m8[us]", + "m8[ns]", + "m8[ps]", + "m8[fs]", + "m8[as]", + ]: ... + +_NaObjectT_co = TypeVar("_NaObjectT_co", default=Never, covariant=True) + +@final +class StringDType( # type: ignore[misc] + _TypeCodes[L["T"], L["T"], L[2056]], + _NativeOrder, + _NBit[L[8], L[16]], + # TODO(jorenham): change once we have a string scalar type: + # https://github.com/numpy/numpy/issues/28165 + np.dtype[str], # type: ignore[type-var] # pyright: ignore[reportGeneralTypeIssues, reportInvalidTypeArguments] + Generic[_NaObjectT_co], +): + @property + def na_object(self) -> _NaObjectT_co: ... + @property + def coerce(self) -> L[True]: ... + + # + @overload + def __new__(cls, /, *, coerce: bool = True) -> Self: ... + @overload + def __new__(cls, /, *, na_object: _NaObjectT_co, coerce: bool = True) -> Self: ... + + # + def __getitem__(self, key: Never, /) -> NoReturn: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] + @property + def fields(self) -> None: ... + @property + def base(self) -> Self: ... + @property + def ndim(self) -> L[0]: ... + @property + def shape(self) -> tuple[()]: ... + + # + @property + def name(self) -> L["StringDType64", "StringDType128"]: ... + @property + def subdtype(self) -> None: ... + @property + def type(self) -> type[str]: ... + @property + def str(self) -> L["|T8", "|T16"]: ... + + # + @property + def hasobject(self) -> L[True]: ... + @property + def isalignedstruct(self) -> L[False]: ... + @property + def isnative(self) -> L[True]: ... diff --git a/.venv/lib/python3.12/site-packages/numpy/exceptions.py b/.venv/lib/python3.12/site-packages/numpy/exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..0e8688ae9eba0ff3f623e631c47e07c988679ca8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/exceptions.py @@ -0,0 +1,247 @@ +""" +Exceptions and Warnings +======================= + +General exceptions used by NumPy. Note that some exceptions may be module +specific, such as linear algebra errors. + +.. versionadded:: NumPy 1.25 + + The exceptions module is new in NumPy 1.25. Older exceptions remain + available through the main NumPy namespace for compatibility. + +.. currentmodule:: numpy.exceptions + +Warnings +-------- +.. autosummary:: + :toctree: generated/ + + ComplexWarning Given when converting complex to real. + VisibleDeprecationWarning Same as a DeprecationWarning, but more visible. + RankWarning Issued when the design matrix is rank deficient. + +Exceptions +---------- +.. autosummary:: + :toctree: generated/ + + AxisError Given when an axis was invalid. + DTypePromotionError Given when no common dtype could be found. + TooHardError Error specific to `numpy.shares_memory`. + +""" + + +__all__ = [ + "ComplexWarning", "VisibleDeprecationWarning", "ModuleDeprecationWarning", + "TooHardError", "AxisError", "DTypePromotionError"] + + +# Disallow reloading this module so as to preserve the identities of the +# classes defined here. +if '_is_loaded' in globals(): + raise RuntimeError('Reloading numpy._globals is not allowed') +_is_loaded = True + + +class ComplexWarning(RuntimeWarning): + """ + The warning raised when casting a complex dtype to a real dtype. + + As implemented, casting a complex number to a real discards its imaginary + part, but this behavior may not be what the user actually wants. + + """ + pass + + +class ModuleDeprecationWarning(DeprecationWarning): + """Module deprecation warning. + + .. warning:: + + This warning should not be used, since nose testing is not relevant + anymore. + + The nose tester turns ordinary Deprecation warnings into test failures. + That makes it hard to deprecate whole modules, because they get + imported by default. So this is a special Deprecation warning that the + nose tester will let pass without making tests fail. + + """ + pass + + +class VisibleDeprecationWarning(UserWarning): + """Visible deprecation warning. + + By default, python will not show deprecation warnings, so this class + can be used when a very visible warning is helpful, for example because + the usage is most likely a user bug. + + """ + pass + + +class RankWarning(RuntimeWarning): + """Matrix rank warning. + + Issued by polynomial functions when the design matrix is rank deficient. + + """ + pass + + +# Exception used in shares_memory() +class TooHardError(RuntimeError): + """``max_work`` was exceeded. + + This is raised whenever the maximum number of candidate solutions + to consider specified by the ``max_work`` parameter is exceeded. + Assigning a finite number to ``max_work`` may have caused the operation + to fail. + + """ + pass + + +class AxisError(ValueError, IndexError): + """Axis supplied was invalid. + + This is raised whenever an ``axis`` parameter is specified that is larger + than the number of array dimensions. + For compatibility with code written against older numpy versions, which + raised a mixture of :exc:`ValueError` and :exc:`IndexError` for this + situation, this exception subclasses both to ensure that + ``except ValueError`` and ``except IndexError`` statements continue + to catch ``AxisError``. + + Parameters + ---------- + axis : int or str + The out of bounds axis or a custom exception message. + If an axis is provided, then `ndim` should be specified as well. + ndim : int, optional + The number of array dimensions. + msg_prefix : str, optional + A prefix for the exception message. + + Attributes + ---------- + axis : int, optional + The out of bounds axis or ``None`` if a custom exception + message was provided. This should be the axis as passed by + the user, before any normalization to resolve negative indices. + + .. versionadded:: 1.22 + ndim : int, optional + The number of array dimensions or ``None`` if a custom exception + message was provided. + + .. versionadded:: 1.22 + + + Examples + -------- + >>> import numpy as np + >>> array_1d = np.arange(10) + >>> np.cumsum(array_1d, axis=1) + Traceback (most recent call last): + ... + numpy.exceptions.AxisError: axis 1 is out of bounds for array of dimension 1 + + Negative axes are preserved: + + >>> np.cumsum(array_1d, axis=-2) + Traceback (most recent call last): + ... + numpy.exceptions.AxisError: axis -2 is out of bounds for array of dimension 1 + + The class constructor generally takes the axis and arrays' + dimensionality as arguments: + + >>> print(np.exceptions.AxisError(2, 1, msg_prefix='error')) + error: axis 2 is out of bounds for array of dimension 1 + + Alternatively, a custom exception message can be passed: + + >>> print(np.exceptions.AxisError('Custom error message')) + Custom error message + + """ + + __slots__ = ("_msg", "axis", "ndim") + + def __init__(self, axis, ndim=None, msg_prefix=None): + if ndim is msg_prefix is None: + # single-argument form: directly set the error message + self._msg = axis + self.axis = None + self.ndim = None + else: + self._msg = msg_prefix + self.axis = axis + self.ndim = ndim + + def __str__(self): + axis = self.axis + ndim = self.ndim + + if axis is ndim is None: + return self._msg + else: + msg = f"axis {axis} is out of bounds for array of dimension {ndim}" + if self._msg is not None: + msg = f"{self._msg}: {msg}" + return msg + + +class DTypePromotionError(TypeError): + """Multiple DTypes could not be converted to a common one. + + This exception derives from ``TypeError`` and is raised whenever dtypes + cannot be converted to a single common one. This can be because they + are of a different category/class or incompatible instances of the same + one (see Examples). + + Notes + ----- + Many functions will use promotion to find the correct result and + implementation. For these functions the error will typically be chained + with a more specific error indicating that no implementation was found + for the input dtypes. + + Typically promotion should be considered "invalid" between the dtypes of + two arrays when `arr1 == arr2` can safely return all ``False`` because the + dtypes are fundamentally different. + + Examples + -------- + Datetimes and complex numbers are incompatible classes and cannot be + promoted: + + >>> import numpy as np + >>> np.result_type(np.dtype("M8[s]"), np.complex128) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + DTypePromotionError: The DType could not + be promoted by . This means that no common + DType exists for the given inputs. For example they cannot be stored in a + single array unless the dtype is `object`. The full list of DTypes is: + (, ) + + For example for structured dtypes, the structure can mismatch and the + same ``DTypePromotionError`` is given when two structured dtypes with + a mismatch in their number of fields is given: + + >>> dtype1 = np.dtype([("field1", np.float64), ("field2", np.int64)]) + >>> dtype2 = np.dtype([("field1", np.float64)]) + >>> np.promote_types(dtype1, dtype2) # doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ... + DTypePromotionError: field names `('field1', 'field2')` and `('field1',)` + mismatch. + + """ # noqa: E501 + pass diff --git a/.venv/lib/python3.12/site-packages/numpy/exceptions.pyi b/.venv/lib/python3.12/site-packages/numpy/exceptions.pyi new file mode 100644 index 0000000000000000000000000000000000000000..9ed50927d0702e63135c4bba42b5deaf50cbc532 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/exceptions.pyi @@ -0,0 +1,25 @@ +from typing import overload + +__all__ = [ + "ComplexWarning", + "VisibleDeprecationWarning", + "ModuleDeprecationWarning", + "TooHardError", + "AxisError", + "DTypePromotionError", +] + +class ComplexWarning(RuntimeWarning): ... +class ModuleDeprecationWarning(DeprecationWarning): ... +class VisibleDeprecationWarning(UserWarning): ... +class RankWarning(RuntimeWarning): ... +class TooHardError(RuntimeError): ... +class DTypePromotionError(TypeError): ... + +class AxisError(ValueError, IndexError): + axis: int | None + ndim: int | None + @overload + def __init__(self, axis: str, ndim: None = ..., msg_prefix: None = ...) -> None: ... + @overload + def __init__(self, axis: int, ndim: int, msg_prefix: str | None = ...) -> None: ... diff --git a/.venv/lib/python3.12/site-packages/numpy/matlib.py b/.venv/lib/python3.12/site-packages/numpy/matlib.py new file mode 100644 index 0000000000000000000000000000000000000000..f27d503cdbca80c745b14b8854afecc56125df9a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/matlib.py @@ -0,0 +1,380 @@ +import warnings + +# 2018-05-29, PendingDeprecationWarning added to matrix.__new__ +# 2020-01-23, numpy 1.19.0 PendingDeprecatonWarning +warnings.warn("Importing from numpy.matlib is deprecated since 1.19.0. " + "The matrix subclass is not the recommended way to represent " + "matrices or deal with linear algebra (see " + "https://docs.scipy.org/doc/numpy/user/numpy-for-matlab-users.html). " + "Please adjust your code to use regular ndarray. ", + PendingDeprecationWarning, stacklevel=2) + +import numpy as np + +# Matlib.py contains all functions in the numpy namespace with a few +# replacements. See doc/source/reference/routines.matlib.rst for details. +# Need * as we're copying the numpy namespace. +from numpy import * # noqa: F403 +from numpy.matrixlib.defmatrix import asmatrix, matrix + +__version__ = np.__version__ + +__all__ = ['rand', 'randn', 'repmat'] +__all__ += np.__all__ + +def empty(shape, dtype=None, order='C'): + """Return a new matrix of given shape and type, without initializing entries. + + Parameters + ---------- + shape : int or tuple of int + Shape of the empty matrix. + dtype : data-type, optional + Desired output data-type. + order : {'C', 'F'}, optional + Whether to store multi-dimensional data in row-major + (C-style) or column-major (Fortran-style) order in + memory. + + See Also + -------- + numpy.empty : Equivalent array function. + matlib.zeros : Return a matrix of zeros. + matlib.ones : Return a matrix of ones. + + Notes + ----- + Unlike other matrix creation functions (e.g. `matlib.zeros`, + `matlib.ones`), `matlib.empty` does not initialize the values of the + matrix, and may therefore be marginally faster. However, the values + stored in the newly allocated matrix are arbitrary. For reproducible + behavior, be sure to set each element of the matrix before reading. + + Examples + -------- + >>> import numpy.matlib + >>> np.matlib.empty((2, 2)) # filled with random data + matrix([[ 6.76425276e-320, 9.79033856e-307], # random + [ 7.39337286e-309, 3.22135945e-309]]) + >>> np.matlib.empty((2, 2), dtype=int) + matrix([[ 6600475, 0], # random + [ 6586976, 22740995]]) + + """ + return ndarray.__new__(matrix, shape, dtype, order=order) + +def ones(shape, dtype=None, order='C'): + """ + Matrix of ones. + + Return a matrix of given shape and type, filled with ones. + + Parameters + ---------- + shape : {sequence of ints, int} + Shape of the matrix + dtype : data-type, optional + The desired data-type for the matrix, default is np.float64. + order : {'C', 'F'}, optional + Whether to store matrix in C- or Fortran-contiguous order, + default is 'C'. + + Returns + ------- + out : matrix + Matrix of ones of given shape, dtype, and order. + + See Also + -------- + ones : Array of ones. + matlib.zeros : Zero matrix. + + Notes + ----- + If `shape` has length one i.e. ``(N,)``, or is a scalar ``N``, + `out` becomes a single row matrix of shape ``(1,N)``. + + Examples + -------- + >>> np.matlib.ones((2,3)) + matrix([[1., 1., 1.], + [1., 1., 1.]]) + + >>> np.matlib.ones(2) + matrix([[1., 1.]]) + + """ + a = ndarray.__new__(matrix, shape, dtype, order=order) + a.fill(1) + return a + +def zeros(shape, dtype=None, order='C'): + """ + Return a matrix of given shape and type, filled with zeros. + + Parameters + ---------- + shape : int or sequence of ints + Shape of the matrix + dtype : data-type, optional + The desired data-type for the matrix, default is float. + order : {'C', 'F'}, optional + Whether to store the result in C- or Fortran-contiguous order, + default is 'C'. + + Returns + ------- + out : matrix + Zero matrix of given shape, dtype, and order. + + See Also + -------- + numpy.zeros : Equivalent array function. + matlib.ones : Return a matrix of ones. + + Notes + ----- + If `shape` has length one i.e. ``(N,)``, or is a scalar ``N``, + `out` becomes a single row matrix of shape ``(1,N)``. + + Examples + -------- + >>> import numpy.matlib + >>> np.matlib.zeros((2, 3)) + matrix([[0., 0., 0.], + [0., 0., 0.]]) + + >>> np.matlib.zeros(2) + matrix([[0., 0.]]) + + """ + a = ndarray.__new__(matrix, shape, dtype, order=order) + a.fill(0) + return a + +def identity(n, dtype=None): + """ + Returns the square identity matrix of given size. + + Parameters + ---------- + n : int + Size of the returned identity matrix. + dtype : data-type, optional + Data-type of the output. Defaults to ``float``. + + Returns + ------- + out : matrix + `n` x `n` matrix with its main diagonal set to one, + and all other elements zero. + + See Also + -------- + numpy.identity : Equivalent array function. + matlib.eye : More general matrix identity function. + + Examples + -------- + >>> import numpy.matlib + >>> np.matlib.identity(3, dtype=int) + matrix([[1, 0, 0], + [0, 1, 0], + [0, 0, 1]]) + + """ + a = array([1] + n * [0], dtype=dtype) + b = empty((n, n), dtype=dtype) + b.flat = a + return b + +def eye(n, M=None, k=0, dtype=float, order='C'): + """ + Return a matrix with ones on the diagonal and zeros elsewhere. + + Parameters + ---------- + n : int + Number of rows in the output. + M : int, optional + Number of columns in the output, defaults to `n`. + k : int, optional + Index of the diagonal: 0 refers to the main diagonal, + a positive value refers to an upper diagonal, + and a negative value to a lower diagonal. + dtype : dtype, optional + Data-type of the returned matrix. + order : {'C', 'F'}, optional + Whether the output should be stored in row-major (C-style) or + column-major (Fortran-style) order in memory. + + Returns + ------- + I : matrix + A `n` x `M` matrix where all elements are equal to zero, + except for the `k`-th diagonal, whose values are equal to one. + + See Also + -------- + numpy.eye : Equivalent array function. + identity : Square identity matrix. + + Examples + -------- + >>> import numpy.matlib + >>> np.matlib.eye(3, k=1, dtype=float) + matrix([[0., 1., 0.], + [0., 0., 1.], + [0., 0., 0.]]) + + """ + return asmatrix(np.eye(n, M=M, k=k, dtype=dtype, order=order)) + +def rand(*args): + """ + Return a matrix of random values with given shape. + + Create a matrix of the given shape and propagate it with + random samples from a uniform distribution over ``[0, 1)``. + + Parameters + ---------- + \\*args : Arguments + Shape of the output. + If given as N integers, each integer specifies the size of one + dimension. + If given as a tuple, this tuple gives the complete shape. + + Returns + ------- + out : ndarray + The matrix of random values with shape given by `\\*args`. + + See Also + -------- + randn, numpy.random.RandomState.rand + + Examples + -------- + >>> np.random.seed(123) + >>> import numpy.matlib + >>> np.matlib.rand(2, 3) + matrix([[0.69646919, 0.28613933, 0.22685145], + [0.55131477, 0.71946897, 0.42310646]]) + >>> np.matlib.rand((2, 3)) + matrix([[0.9807642 , 0.68482974, 0.4809319 ], + [0.39211752, 0.34317802, 0.72904971]]) + + If the first argument is a tuple, other arguments are ignored: + + >>> np.matlib.rand((2, 3), 4) + matrix([[0.43857224, 0.0596779 , 0.39804426], + [0.73799541, 0.18249173, 0.17545176]]) + + """ + if isinstance(args[0], tuple): + args = args[0] + return asmatrix(np.random.rand(*args)) + +def randn(*args): + """ + Return a random matrix with data from the "standard normal" distribution. + + `randn` generates a matrix filled with random floats sampled from a + univariate "normal" (Gaussian) distribution of mean 0 and variance 1. + + Parameters + ---------- + \\*args : Arguments + Shape of the output. + If given as N integers, each integer specifies the size of one + dimension. If given as a tuple, this tuple gives the complete shape. + + Returns + ------- + Z : matrix of floats + A matrix of floating-point samples drawn from the standard normal + distribution. + + See Also + -------- + rand, numpy.random.RandomState.randn + + Notes + ----- + For random samples from the normal distribution with mean ``mu`` and + standard deviation ``sigma``, use:: + + sigma * np.matlib.randn(...) + mu + + Examples + -------- + >>> np.random.seed(123) + >>> import numpy.matlib + >>> np.matlib.randn(1) + matrix([[-1.0856306]]) + >>> np.matlib.randn(1, 2, 3) + matrix([[ 0.99734545, 0.2829785 , -1.50629471], + [-0.57860025, 1.65143654, -2.42667924]]) + + Two-by-four matrix of samples from the normal distribution with + mean 3 and standard deviation 2.5: + + >>> 2.5 * np.matlib.randn((2, 4)) + 3 + matrix([[1.92771843, 6.16484065, 0.83314899, 1.30278462], + [2.76322758, 6.72847407, 1.40274501, 1.8900451 ]]) + + """ + if isinstance(args[0], tuple): + args = args[0] + return asmatrix(np.random.randn(*args)) + +def repmat(a, m, n): + """ + Repeat a 0-D to 2-D array or matrix MxN times. + + Parameters + ---------- + a : array_like + The array or matrix to be repeated. + m, n : int + The number of times `a` is repeated along the first and second axes. + + Returns + ------- + out : ndarray + The result of repeating `a`. + + Examples + -------- + >>> import numpy.matlib + >>> a0 = np.array(1) + >>> np.matlib.repmat(a0, 2, 3) + array([[1, 1, 1], + [1, 1, 1]]) + + >>> a1 = np.arange(4) + >>> np.matlib.repmat(a1, 2, 2) + array([[0, 1, 2, 3, 0, 1, 2, 3], + [0, 1, 2, 3, 0, 1, 2, 3]]) + + >>> a2 = np.asmatrix(np.arange(6).reshape(2, 3)) + >>> np.matlib.repmat(a2, 2, 3) + matrix([[0, 1, 2, 0, 1, 2, 0, 1, 2], + [3, 4, 5, 3, 4, 5, 3, 4, 5], + [0, 1, 2, 0, 1, 2, 0, 1, 2], + [3, 4, 5, 3, 4, 5, 3, 4, 5]]) + + """ + a = asanyarray(a) + ndim = a.ndim + if ndim == 0: + origrows, origcols = (1, 1) + elif ndim == 1: + origrows, origcols = (1, a.shape[0]) + else: + origrows, origcols = a.shape + rows = origrows * m + cols = origcols * n + c = a.reshape(1, a.size).repeat(m, 0).reshape(rows, origcols).repeat(n, 0) + return c.reshape(rows, cols) diff --git a/.venv/lib/python3.12/site-packages/numpy/matlib.pyi b/.venv/lib/python3.12/site-packages/numpy/matlib.pyi new file mode 100644 index 0000000000000000000000000000000000000000..baeadc078028ebf79e2a2bf8c25216f84fddf0fc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/matlib.pyi @@ -0,0 +1,582 @@ +from typing import Any, Literal, TypeAlias, TypeVar, overload + +import numpy as np +import numpy.typing as npt +from numpy import ( # noqa: F401 + False_, + ScalarType, + True_, + __array_namespace_info__, + __version__, + abs, + absolute, + acos, + acosh, + add, + all, + allclose, + amax, + amin, + angle, + any, + append, + apply_along_axis, + apply_over_axes, + arange, + arccos, + arccosh, + arcsin, + arcsinh, + arctan, + arctan2, + arctanh, + argmax, + argmin, + argpartition, + argsort, + argwhere, + around, + array, + array2string, + array_equal, + array_equiv, + array_repr, + array_split, + array_str, + asanyarray, + asarray, + asarray_chkfinite, + ascontiguousarray, + asfortranarray, + asin, + asinh, + asmatrix, + astype, + atan, + atan2, + atanh, + atleast_1d, + atleast_2d, + atleast_3d, + average, + bartlett, + base_repr, + binary_repr, + bincount, + bitwise_and, + bitwise_count, + bitwise_invert, + bitwise_left_shift, + bitwise_not, + bitwise_or, + bitwise_right_shift, + bitwise_xor, + blackman, + block, + bmat, + bool, + bool_, + broadcast, + broadcast_arrays, + broadcast_shapes, + broadcast_to, + busday_count, + busday_offset, + busdaycalendar, + byte, + bytes_, + c_, + can_cast, + cbrt, + cdouble, + ceil, + char, + character, + choose, + clip, + clongdouble, + column_stack, + common_type, + complex64, + complex128, + complex256, + complexfloating, + compress, + concat, + concatenate, + conj, + conjugate, + convolve, + copy, + copysign, + copyto, + core, + corrcoef, + correlate, + cos, + cosh, + count_nonzero, + cov, + cross, + csingle, + ctypeslib, + cumprod, + cumsum, + cumulative_prod, + cumulative_sum, + datetime64, + datetime_as_string, + datetime_data, + deg2rad, + degrees, + delete, + diag, + diag_indices, + diag_indices_from, + diagflat, + diagonal, + diff, + digitize, + divide, + divmod, + dot, + double, + dsplit, + dstack, + dtype, + dtypes, + e, + ediff1d, + einsum, + einsum_path, + emath, + empty_like, + equal, + errstate, + euler_gamma, + exceptions, + exp, + exp2, + expand_dims, + expm1, + extract, + f2py, + fabs, + fft, + fill_diagonal, + finfo, + fix, + flatiter, + flatnonzero, + flexible, + flip, + fliplr, + flipud, + float16, + float32, + float64, + float128, + float_power, + floating, + floor, + floor_divide, + fmax, + fmin, + fmod, + format_float_positional, + format_float_scientific, + frexp, + from_dlpack, + frombuffer, + fromfile, + fromfunction, + fromiter, + frompyfunc, + fromregex, + fromstring, + full, + full_like, + gcd, + generic, + genfromtxt, + geomspace, + get_include, + get_printoptions, + getbufsize, + geterr, + geterrcall, + gradient, + greater, + greater_equal, + half, + hamming, + hanning, + heaviside, + histogram, + histogram2d, + histogram_bin_edges, + histogramdd, + hsplit, + hstack, + hypot, + i0, + iinfo, + imag, + in1d, + index_exp, + indices, + inexact, + inf, + info, + inner, + insert, + int8, + int16, + int32, + int64, + int_, + intc, + integer, + interp, + intersect1d, + intp, + invert, + is_busday, + isclose, + iscomplex, + iscomplexobj, + isdtype, + isfinite, + isfortran, + isin, + isinf, + isnan, + isnat, + isneginf, + isposinf, + isreal, + isrealobj, + isscalar, + issubdtype, + iterable, + ix_, + kaiser, + kron, + lcm, + ldexp, + left_shift, + less, + less_equal, + lexsort, + lib, + linalg, + linspace, + little_endian, + load, + loadtxt, + log, + log1p, + log2, + log10, + logaddexp, + logaddexp2, + logical_and, + logical_not, + logical_or, + logical_xor, + logspace, + long, + longdouble, + longlong, + ma, + mask_indices, + matmul, + matrix, + matrix_transpose, + matvec, + max, + maximum, + may_share_memory, + mean, + median, + memmap, + meshgrid, + mgrid, + min, + min_scalar_type, + minimum, + mintypecode, + mod, + modf, + moveaxis, + multiply, + nan, + nan_to_num, + nanargmax, + nanargmin, + nancumprod, + nancumsum, + nanmax, + nanmean, + nanmedian, + nanmin, + nanpercentile, + nanprod, + nanquantile, + nanstd, + nansum, + nanvar, + ndarray, + ndenumerate, + ndim, + ndindex, + nditer, + negative, + nested_iters, + newaxis, + nextafter, + nonzero, + not_equal, + number, + object_, + ogrid, + ones_like, + outer, + packbits, + pad, + partition, + percentile, + permute_dims, + pi, + piecewise, + place, + poly, + poly1d, + polyadd, + polyder, + polydiv, + polyfit, + polyint, + polymul, + polynomial, + polysub, + polyval, + positive, + pow, + power, + printoptions, + prod, + promote_types, + ptp, + put, + put_along_axis, + putmask, + quantile, + r_, + rad2deg, + radians, + random, + ravel, + ravel_multi_index, + real, + real_if_close, + rec, + recarray, + reciprocal, + record, + remainder, + repeat, + require, + reshape, + resize, + result_type, + right_shift, + rint, + roll, + rollaxis, + roots, + rot90, + round, + row_stack, + s_, + save, + savetxt, + savez, + savez_compressed, + sctypeDict, + searchsorted, + select, + set_printoptions, + setbufsize, + setdiff1d, + seterr, + seterrcall, + setxor1d, + shape, + shares_memory, + short, + show_config, + show_runtime, + sign, + signbit, + signedinteger, + sin, + sinc, + single, + sinh, + size, + sort, + sort_complex, + spacing, + split, + sqrt, + square, + squeeze, + stack, + std, + str_, + strings, + subtract, + sum, + swapaxes, + take, + take_along_axis, + tan, + tanh, + tensordot, + test, + testing, + tile, + timedelta64, + trace, + transpose, + trapezoid, + trapz, + tri, + tril, + tril_indices, + tril_indices_from, + trim_zeros, + triu, + triu_indices, + triu_indices_from, + true_divide, + trunc, + typecodes, + typename, + typing, + ubyte, + ufunc, + uint, + uint8, + uint16, + uint32, + uint64, + uintc, + uintp, + ulong, + ulonglong, + union1d, + unique, + unique_all, + unique_counts, + unique_inverse, + unique_values, + unpackbits, + unravel_index, + unsignedinteger, + unstack, + unwrap, + ushort, + vander, + var, + vdot, + vecdot, + vecmat, + vectorize, + void, + vsplit, + vstack, + where, + zeros_like, +) +from numpy._typing import _ArrayLike, _DTypeLike + +__all__ = ["rand", "randn", "repmat"] +__all__ += np.__all__ + +### + +_T = TypeVar("_T", bound=np.generic) +_Matrix: TypeAlias = np.matrix[tuple[int, int], np.dtype[_T]] +_Order: TypeAlias = Literal["C", "F"] + +### + +# +@overload +def empty(shape: int | tuple[int, int], dtype: None = None, order: _Order = "C") -> _Matrix[np.float64]: ... +@overload +def empty(shape: int | tuple[int, int], dtype: _DTypeLike[_T], order: _Order = "C") -> _Matrix[_T]: ... +@overload +def empty(shape: int | tuple[int, int], dtype: npt.DTypeLike, order: _Order = "C") -> _Matrix[Any]: ... + +# +@overload +def ones(shape: int | tuple[int, int], dtype: None = None, order: _Order = "C") -> _Matrix[np.float64]: ... +@overload +def ones(shape: int | tuple[int, int], dtype: _DTypeLike[_T], order: _Order = "C") -> _Matrix[_T]: ... +@overload +def ones(shape: int | tuple[int, int], dtype: npt.DTypeLike, order: _Order = "C") -> _Matrix[Any]: ... + +# +@overload +def zeros(shape: int | tuple[int, int], dtype: None = None, order: _Order = "C") -> _Matrix[np.float64]: ... +@overload +def zeros(shape: int | tuple[int, int], dtype: _DTypeLike[_T], order: _Order = "C") -> _Matrix[_T]: ... +@overload +def zeros(shape: int | tuple[int, int], dtype: npt.DTypeLike, order: _Order = "C") -> _Matrix[Any]: ... + +# +@overload +def identity(n: int, dtype: None = None) -> _Matrix[np.float64]: ... +@overload +def identity(n: int, dtype: _DTypeLike[_T]) -> _Matrix[_T]: ... +@overload +def identity(n: int, dtype: npt.DTypeLike | None = None) -> _Matrix[Any]: ... + +# +@overload +def eye( + n: int, + M: int | None = None, + k: int = 0, + dtype: type[np.float64] | None = ..., + order: _Order = "C", +) -> _Matrix[np.float64]: ... +@overload +def eye(n: int, M: int | None, k: int, dtype: _DTypeLike[_T], order: _Order = "C") -> _Matrix[_T]: ... +@overload +def eye(n: int, M: int | None = None, k: int = 0, *, dtype: _DTypeLike[_T], order: _Order = "C") -> _Matrix[_T]: ... +@overload +def eye(n: int, M: int | None = None, k: int = 0, dtype: npt.DTypeLike = ..., order: _Order = "C") -> _Matrix[Any]: ... + +# +@overload +def rand(arg: int | tuple[()] | tuple[int] | tuple[int, int], /) -> _Matrix[np.float64]: ... +@overload +def rand(arg: int, /, *args: int) -> _Matrix[np.float64]: ... + +# +@overload +def randn(arg: int | tuple[()] | tuple[int] | tuple[int, int], /) -> _Matrix[np.float64]: ... +@overload +def randn(arg: int, /, *args: int) -> _Matrix[np.float64]: ... + +# +@overload +def repmat(a: _Matrix[_T], m: int, n: int) -> _Matrix[_T]: ... +@overload +def repmat(a: _ArrayLike[_T], m: int, n: int) -> npt.NDArray[_T]: ... +@overload +def repmat(a: npt.ArrayLike, m: int, n: int) -> npt.NDArray[Any]: ... diff --git a/.venv/lib/python3.12/site-packages/numpy/py.typed b/.venv/lib/python3.12/site-packages/numpy/py.typed new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/numpy/version.py b/.venv/lib/python3.12/site-packages/numpy/version.py new file mode 100644 index 0000000000000000000000000000000000000000..ea75d5a869c55a23fe59dd67dcac692baf8ff9b0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/version.py @@ -0,0 +1,11 @@ + +""" +Module to expose more detailed version info for the installed `numpy` +""" +version = "2.3.3" +__version__ = version +full_version = version + +git_revision = "f2a77a76e08719556527e0819182073fe9b5f1c3" +release = 'dev' not in version and '+' not in version +short_version = version.split("+")[0] diff --git a/.venv/lib/python3.12/site-packages/numpy/version.pyi b/.venv/lib/python3.12/site-packages/numpy/version.pyi new file mode 100644 index 0000000000000000000000000000000000000000..113cde3f56212b5efdc27c33c731f66ffbc895d2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/numpy/version.pyi @@ -0,0 +1,18 @@ +from typing import Final, LiteralString + +__all__ = ( + '__version__', + 'full_version', + 'git_revision', + 'release', + 'short_version', + 'version', +) + +version: Final[LiteralString] +__version__: Final[LiteralString] +full_version: Final[LiteralString] + +git_revision: Final[LiteralString] +release: Final[bool] +short_version: Final[LiteralString] diff --git a/.venv/lib/python3.12/site-packages/nvidia_cublas_cu12-12.8.4.1.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/nvidia_cublas_cu12-12.8.4.1.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cublas_cu12-12.8.4.1.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/nvidia_cublas_cu12-12.8.4.1.dist-info/License.txt b/.venv/lib/python3.12/site-packages/nvidia_cublas_cu12-12.8.4.1.dist-info/License.txt new file mode 100644 index 0000000000000000000000000000000000000000..b491c70e0aef319022ded661e111ddbd45b8a17f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cublas_cu12-12.8.4.1.dist-info/License.txt @@ -0,0 +1,1568 @@ +End User License Agreement +-------------------------- + + +Preface +------- + +The Software License Agreement in Chapter 1 and the Supplement +in Chapter 2 contain license terms and conditions that govern +the use of NVIDIA software. By accepting this agreement, you +agree to comply with all the terms and conditions applicable +to the product(s) included herein. + + +NVIDIA Driver + + +Description + +This package contains the operating system driver and +fundamental system software components for NVIDIA GPUs. + + +NVIDIA CUDA Toolkit + + +Description + +The NVIDIA CUDA Toolkit provides command-line and graphical +tools for building, debugging and optimizing the performance +of applications accelerated by NVIDIA GPUs, runtime and math +libraries, and documentation including programming guides, +user manuals, and API references. + + +Default Install Location of CUDA Toolkit + +Windows platform: + +%ProgramFiles%\NVIDIA GPU Computing Toolkit\CUDA\v#.# + +Linux platform: + +/usr/local/cuda-#.# + +Mac platform: + +/Developer/NVIDIA/CUDA-#.# + + +NVIDIA CUDA Samples + + +Description + +This package includes over 100+ CUDA examples that demonstrate +various CUDA programming principles, and efficient CUDA +implementation of algorithms in specific application domains. + + +Default Install Location of CUDA Samples + +Windows platform: + +%ProgramData%\NVIDIA Corporation\CUDA Samples\v#.# + +Linux platform: + +/usr/local/cuda-#.#/samples + +and + +$HOME/NVIDIA_CUDA-#.#_Samples + +Mac platform: + +/Developer/NVIDIA/CUDA-#.#/samples + + +NVIDIA Nsight Visual Studio Edition (Windows only) + + +Description + +NVIDIA Nsight Development Platform, Visual Studio Edition is a +development environment integrated into Microsoft Visual +Studio that provides tools for debugging, profiling, analyzing +and optimizing your GPU computing and graphics applications. + + +Default Install Location of Nsight Visual Studio Edition + +Windows platform: + +%ProgramFiles(x86)%\NVIDIA Corporation\Nsight Visual Studio Edition #.# + + +1. License Agreement for NVIDIA Software Development Kits +--------------------------------------------------------- + + +Release Date: July 26, 2018 +--------------------------- + + +Important NoticeRead before downloading, installing, +copying or using the licensed software: +------------------------------------------------------- + +This license agreement, including exhibits attached +("Agreement”) is a legal agreement between you and NVIDIA +Corporation ("NVIDIA") and governs your use of a NVIDIA +software development kit (“SDK”). + +Each SDK has its own set of software and materials, but here +is a description of the types of items that may be included in +a SDK: source code, header files, APIs, data sets and assets +(examples include images, textures, models, scenes, videos, +native API input/output files), binary software, sample code, +libraries, utility programs, programming code and +documentation. + +This Agreement can be accepted only by an adult of legal age +of majority in the country in which the SDK is used. + +If you are entering into this Agreement on behalf of a company +or other legal entity, you represent that you have the legal +authority to bind the entity to this Agreement, in which case +“you” will mean the entity you represent. + +If you don’t have the required age or authority to accept +this Agreement, or if you don’t accept all the terms and +conditions of this Agreement, do not download, install or use +the SDK. + +You agree to use the SDK only for purposes that are permitted +by (a) this Agreement, and (b) any applicable law, regulation +or generally accepted practices or guidelines in the relevant +jurisdictions. + + +1.1. License + + +1.1.1. License Grant + +Subject to the terms of this Agreement, NVIDIA hereby grants +you a non-exclusive, non-transferable license, without the +right to sublicense (except as expressly provided in this +Agreement) to: + + 1. Install and use the SDK, + + 2. Modify and create derivative works of sample source code + delivered in the SDK, and + + 3. Distribute those portions of the SDK that are identified + in this Agreement as distributable, as incorporated in + object code format into a software application that meets + the distribution requirements indicated in this Agreement. + + +1.1.2. Distribution Requirements + +These are the distribution requirements for you to exercise +the distribution grant: + + 1. Your application must have material additional + functionality, beyond the included portions of the SDK. + + 2. The distributable portions of the SDK shall only be + accessed by your application. + + 3. The following notice shall be included in modifications + and derivative works of sample source code distributed: + “This software contains source code provided by NVIDIA + Corporation.” + + 4. Unless a developer tool is identified in this Agreement + as distributable, it is delivered for your internal use + only. + + 5. The terms under which you distribute your application + must be consistent with the terms of this Agreement, + including (without limitation) terms relating to the + license grant and license restrictions and protection of + NVIDIA’s intellectual property rights. Additionally, you + agree that you will protect the privacy, security and + legal rights of your application users. + + 6. You agree to notify NVIDIA in writing of any known or + suspected distribution or use of the SDK not in compliance + with the requirements of this Agreement, and to enforce + the terms of your agreements with respect to distributed + SDK. + + +1.1.3. Authorized Users + +You may allow employees and contractors of your entity or of +your subsidiary(ies) to access and use the SDK from your +secure network to perform work on your behalf. + +If you are an academic institution you may allow users +enrolled or employed by the academic institution to access and +use the SDK from your secure network. + +You are responsible for the compliance with the terms of this +Agreement by your authorized users. If you become aware that +your authorized users didn’t follow the terms of this +Agreement, you agree to take reasonable steps to resolve the +non-compliance and prevent new occurrences. + + +1.1.4. Pre-Release SDK + +The SDK versions identified as alpha, beta, preview or +otherwise as pre-release, may not be fully functional, may +contain errors or design flaws, and may have reduced or +different security, privacy, accessibility, availability, and +reliability standards relative to commercial versions of +NVIDIA software and materials. Use of a pre-release SDK may +result in unexpected results, loss of data, project delays or +other unpredictable damage or loss. + +You may use a pre-release SDK at your own risk, understanding +that pre-release SDKs are not intended for use in production +or business-critical systems. + +NVIDIA may choose not to make available a commercial version +of any pre-release SDK. NVIDIA may also choose to abandon +development and terminate the availability of a pre-release +SDK at any time without liability. + + +1.1.5. Updates + +NVIDIA may, at its option, make available patches, workarounds +or other updates to this SDK. Unless the updates are provided +with their separate governing terms, they are deemed part of +the SDK licensed to you as provided in this Agreement. You +agree that the form and content of the SDK that NVIDIA +provides may change without prior notice to you. While NVIDIA +generally maintains compatibility between versions, NVIDIA may +in some cases make changes that introduce incompatibilities in +future versions of the SDK. + + +1.1.6. Third Party Licenses + +The SDK may come bundled with, or otherwise include or be +distributed with, third party software licensed by a NVIDIA +supplier and/or open source software provided under an open +source license. Use of third party software is subject to the +third-party license terms, or in the absence of third party +terms, the terms of this Agreement. Copyright to third party +software is held by the copyright holders indicated in the +third-party software or license. + + +1.1.7. Reservation of Rights + +NVIDIA reserves all rights, title, and interest in and to the +SDK, not expressly granted to you under this Agreement. + + +1.2. Limitations + +The following license limitations apply to your use of the +SDK: + + 1. You may not reverse engineer, decompile or disassemble, + or remove copyright or other proprietary notices from any + portion of the SDK or copies of the SDK. + + 2. Except as expressly provided in this Agreement, you may + not copy, sell, rent, sublicense, transfer, distribute, + modify, or create derivative works of any portion of the + SDK. For clarity, you may not distribute or sublicense the + SDK as a stand-alone product. + + 3. Unless you have an agreement with NVIDIA for this + purpose, you may not indicate that an application created + with the SDK is sponsored or endorsed by NVIDIA. + + 4. You may not bypass, disable, or circumvent any + encryption, security, digital rights management or + authentication mechanism in the SDK. + + 5. You may not use the SDK in any manner that would cause it + to become subject to an open source software license. As + examples, licenses that require as a condition of use, + modification, and/or distribution that the SDK be: + + a. Disclosed or distributed in source code form; + + b. Licensed for the purpose of making derivative works; + or + + c. Redistributable at no charge. + + 6. Unless you have an agreement with NVIDIA for this + purpose, you may not use the SDK with any system or + application where the use or failure of the system or + application can reasonably be expected to threaten or + result in personal injury, death, or catastrophic loss. + Examples include use in avionics, navigation, military, + medical, life support or other life critical applications. + NVIDIA does not design, test or manufacture the SDK for + these critical uses and NVIDIA shall not be liable to you + or any third party, in whole or in part, for any claims or + damages arising from such uses. + + 7. You agree to defend, indemnify and hold harmless NVIDIA + and its affiliates, and their respective employees, + contractors, agents, officers and directors, from and + against any and all claims, damages, obligations, losses, + liabilities, costs or debt, fines, restitutions and + expenses (including but not limited to attorney’s fees + and costs incident to establishing the right of + indemnification) arising out of or related to your use of + the SDK outside of the scope of this Agreement, or not in + compliance with its terms. + + +1.3. Ownership + + 1. NVIDIA or its licensors hold all rights, title and + interest in and to the SDK and its modifications and + derivative works, including their respective intellectual + property rights, subject to your rights described in this + section. This SDK may include software and materials from + NVIDIA’s licensors, and these licensors are intended + third party beneficiaries that may enforce this Agreement + with respect to their intellectual property rights. + + 2. You hold all rights, title and interest in and to your + applications and your derivative works of the sample + source code delivered in the SDK, including their + respective intellectual property rights, subject to + NVIDIA’s rights described in this section. + + 3. You may, but don’t have to, provide to NVIDIA + suggestions, feature requests or other feedback regarding + the SDK, including possible enhancements or modifications + to the SDK. For any feedback that you voluntarily provide, + you hereby grant NVIDIA and its affiliates a perpetual, + non-exclusive, worldwide, irrevocable license to use, + reproduce, modify, license, sublicense (through multiple + tiers of sublicensees), and distribute (through multiple + tiers of distributors) it without the payment of any + royalties or fees to you. NVIDIA will use feedback at its + choice. NVIDIA is constantly looking for ways to improve + its products, so you may send feedback to NVIDIA through + the developer portal at https://developer.nvidia.com. + + +1.4. No Warranties + +THE SDK IS PROVIDED BY NVIDIA “AS IS” AND “WITH ALL +FAULTS.” TO THE MAXIMUM EXTENT PERMITTED BY LAW, NVIDIA AND +ITS AFFILIATES EXPRESSLY DISCLAIM ALL WARRANTIES OF ANY KIND +OR NATURE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, +BUT NOT LIMITED TO, ANY WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE, TITLE, NON-INFRINGEMENT, OR THE +ABSENCE OF ANY DEFECTS THEREIN, WHETHER LATENT OR PATENT. NO +WARRANTY IS MADE ON THE BASIS OF TRADE USAGE, COURSE OF +DEALING OR COURSE OF TRADE. + + +1.5. Limitation of Liability + +TO THE MAXIMUM EXTENT PERMITTED BY LAW, NVIDIA AND ITS +AFFILIATES SHALL NOT BE LIABLE FOR ANY SPECIAL, INCIDENTAL, +PUNITIVE OR CONSEQUENTIAL DAMAGES, OR ANY LOST PROFITS, LOSS +OF USE, LOSS OF DATA OR LOSS OF GOODWILL, OR THE COSTS OF +PROCURING SUBSTITUTE PRODUCTS, ARISING OUT OF OR IN CONNECTION +WITH THIS AGREEMENT OR THE USE OR PERFORMANCE OF THE SDK, +WHETHER SUCH LIABILITY ARISES FROM ANY CLAIM BASED UPON BREACH +OF CONTRACT, BREACH OF WARRANTY, TORT (INCLUDING NEGLIGENCE), +PRODUCT LIABILITY OR ANY OTHER CAUSE OF ACTION OR THEORY OF +LIABILITY. IN NO EVENT WILL NVIDIA’S AND ITS AFFILIATES +TOTAL CUMULATIVE LIABILITY UNDER OR ARISING OUT OF THIS +AGREEMENT EXCEED US$10.00. THE NATURE OF THE LIABILITY OR THE +NUMBER OF CLAIMS OR SUITS SHALL NOT ENLARGE OR EXTEND THIS +LIMIT. + +These exclusions and limitations of liability shall apply +regardless if NVIDIA or its affiliates have been advised of +the possibility of such damages, and regardless of whether a +remedy fails its essential purpose. These exclusions and +limitations of liability form an essential basis of the +bargain between the parties, and, absent any of these +exclusions or limitations of liability, the provisions of this +Agreement, including, without limitation, the economic terms, +would be substantially different. + + +1.6. Termination + + 1. This Agreement will continue to apply until terminated by + either you or NVIDIA as described below. + + 2. If you want to terminate this Agreement, you may do so by + stopping to use the SDK. + + 3. NVIDIA may, at any time, terminate this Agreement if: + + a. (i) you fail to comply with any term of this + Agreement and the non-compliance is not fixed within + thirty (30) days following notice from NVIDIA (or + immediately if you violate NVIDIA’s intellectual + property rights); + + b. (ii) you commence or participate in any legal + proceeding against NVIDIA with respect to the SDK; or + + c. (iii) NVIDIA decides to no longer provide the SDK in + a country or, in NVIDIA’s sole discretion, the + continued use of it is no longer commercially viable. + + 4. Upon any termination of this Agreement, you agree to + promptly discontinue use of the SDK and destroy all copies + in your possession or control. Your prior distributions in + accordance with this Agreement are not affected by the + termination of this Agreement. Upon written request, you + will certify in writing that you have complied with your + commitments under this section. Upon any termination of + this Agreement all provisions survive except for the + license grant provisions. + + +1.7. General + +If you wish to assign this Agreement or your rights and +obligations, including by merger, consolidation, dissolution +or operation of law, contact NVIDIA to ask for permission. Any +attempted assignment not approved by NVIDIA in writing shall +be void and of no effect. NVIDIA may assign, delegate or +transfer this Agreement and its rights and obligations, and if +to a non-affiliate you will be notified. + +You agree to cooperate with NVIDIA and provide reasonably +requested information to verify your compliance with this +Agreement. + +This Agreement will be governed in all respects by the laws of +the United States and of the State of Delaware as those laws +are applied to contracts entered into and performed entirely +within Delaware by Delaware residents, without regard to the +conflicts of laws principles. The United Nations Convention on +Contracts for the International Sale of Goods is specifically +disclaimed. You agree to all terms of this Agreement in the +English language. + +The state or federal courts residing in Santa Clara County, +California shall have exclusive jurisdiction over any dispute +or claim arising out of this Agreement. Notwithstanding this, +you agree that NVIDIA shall still be allowed to apply for +injunctive remedies or an equivalent type of urgent legal +relief in any jurisdiction. + +If any court of competent jurisdiction determines that any +provision of this Agreement is illegal, invalid or +unenforceable, such provision will be construed as limited to +the extent necessary to be consistent with and fully +enforceable under the law and the remaining provisions will +remain in full force and effect. Unless otherwise specified, +remedies are cumulative. + +Each party acknowledges and agrees that the other is an +independent contractor in the performance of this Agreement. + +The SDK has been developed entirely at private expense and is +“commercial items” consisting of “commercial computer +software” and “commercial computer software +documentation” provided with RESTRICTED RIGHTS. Use, +duplication or disclosure by the U.S. Government or a U.S. +Government subcontractor is subject to the restrictions in +this Agreement pursuant to DFARS 227.7202-3(a) or as set forth +in subparagraphs (c)(1) and (2) of the Commercial Computer +Software - Restricted Rights clause at FAR 52.227-19, as +applicable. Contractor/manufacturer is NVIDIA, 2788 San Tomas +Expressway, Santa Clara, CA 95051. + +The SDK is subject to United States export laws and +regulations. You agree that you will not ship, transfer or +export the SDK into any country, or use the SDK in any manner, +prohibited by the United States Bureau of Industry and +Security or economic sanctions regulations administered by the +U.S. Department of Treasury’s Office of Foreign Assets +Control (OFAC), or any applicable export laws, restrictions or +regulations. These laws include restrictions on destinations, +end users and end use. By accepting this Agreement, you +confirm that you are not a resident or citizen of any country +currently embargoed by the U.S. and that you are not otherwise +prohibited from receiving the SDK. + +Any notice delivered by NVIDIA to you under this Agreement +will be delivered via mail, email or fax. You agree that any +notices that NVIDIA sends you electronically will satisfy any +legal communication requirements. Please direct your legal +notices or other correspondence to NVIDIA Corporation, 2788 +San Tomas Expressway, Santa Clara, California 95051, United +States of America, Attention: Legal Department. + +This Agreement and any exhibits incorporated into this +Agreement constitute the entire agreement of the parties with +respect to the subject matter of this Agreement and supersede +all prior negotiations or documentation exchanged between the +parties relating to this SDK license. Any additional and/or +conflicting terms on documents issued by you are null, void, +and invalid. Any amendment or waiver under this Agreement +shall be in writing and signed by representatives of both +parties. + + +2. CUDA Toolkit Supplement to Software License Agreement for +NVIDIA Software Development Kits +------------------------------------------------------------ + + +Release date: August 16, 2018 +----------------------------- + +The terms in this supplement govern your use of the NVIDIA +CUDA Toolkit SDK under the terms of your license agreement +(“Agreement”) as modified by this supplement. Capitalized +terms used but not defined below have the meaning assigned to +them in the Agreement. + +This supplement is an exhibit to the Agreement and is +incorporated as an integral part of the Agreement. In the +event of conflict between the terms in this supplement and the +terms in the Agreement, the terms in this supplement govern. + + +2.1. License Scope + +The SDK is licensed for you to develop applications only for +use in systems with NVIDIA GPUs. + + +2.2. Distribution + +The portions of the SDK that are distributable under the +Agreement are listed in Attachment A. + + +2.3. Operating Systems + +Those portions of the SDK designed exclusively for use on the +Linux or FreeBSD operating systems, or other operating systems +derived from the source code to these operating systems, may +be copied and redistributed for use in accordance with this +Agreement, provided that the object code files are not +modified in any way (except for unzipping of compressed +files). + + +2.4. Audio and Video Encoders and Decoders + +You acknowledge and agree that it is your sole responsibility +to obtain any additional third-party licenses required to +make, have made, use, have used, sell, import, and offer for +sale your products or services that include or incorporate any +third-party software and content relating to audio and/or +video encoders and decoders from, including but not limited +to, Microsoft, Thomson, Fraunhofer IIS, Sisvel S.p.A., +MPEG-LA, and Coding Technologies. NVIDIA does not grant to you +under this Agreement any necessary patent or other rights with +respect to any audio and/or video encoders and decoders. + + +2.5. Licensing + +If the distribution terms in this Agreement are not suitable +for your organization, or for any questions regarding this +Agreement, please contact NVIDIA at +nvidia-compute-license-questions@nvidia.com. + + +2.6. Attachment A + +The following portions of the SDK are distributable under the +Agreement: + +Component + +CUDA Runtime + +Windows + +cudart.dll, cudart_static.lib, cudadevrt.lib + +Mac OSX + +libcudart.dylib, libcudart_static.a, libcudadevrt.a + +Linux + +libcudart.so, libcudart_static.a, libcudadevrt.a + +Android + +libcudart.so, libcudart_static.a, libcudadevrt.a + +Component + +CUDA FFT Library + +Windows + +cufft.dll, cufftw.dll, cufft.lib, cufftw.lib + +Mac OSX + +libcufft.dylib, libcufft_static.a, libcufftw.dylib, +libcufftw_static.a + +Linux + +libcufft.so, libcufft_static.a, libcufftw.so, +libcufftw_static.a + +Android + +libcufft.so, libcufft_static.a, libcufftw.so, +libcufftw_static.a + +Component + +CUDA BLAS Library + +Windows + +cublas.dll, cublasLt.dll + +Mac OSX + +libcublas.dylib, libcublasLt.dylib, libcublas_static.a, +libcublasLt_static.a + +Linux + +libcublas.so, libcublasLt.so, libcublas_static.a, +libcublasLt_static.a + +Android + +libcublas.so, libcublasLt.so, libcublas_static.a, +libcublasLt_static.a + +Component + +NVIDIA "Drop-in" BLAS Library + +Windows + +nvblas.dll + +Mac OSX + +libnvblas.dylib + +Linux + +libnvblas.so + +Component + +CUDA Sparse Matrix Library + +Windows + +cusparse.dll, cusparse.lib + +Mac OSX + +libcusparse.dylib, libcusparse_static.a + +Linux + +libcusparse.so, libcusparse_static.a + +Android + +libcusparse.so, libcusparse_static.a + +Component + +CUDA Linear Solver Library + +Windows + +cusolver.dll, cusolver.lib + +Mac OSX + +libcusolver.dylib, libcusolver_static.a + +Linux + +libcusolver.so, libcusolver_static.a + +Android + +libcusolver.so, libcusolver_static.a + +Component + +CUDA Random Number Generation Library + +Windows + +curand.dll, curand.lib + +Mac OSX + +libcurand.dylib, libcurand_static.a + +Linux + +libcurand.so, libcurand_static.a + +Android + +libcurand.so, libcurand_static.a + +Component + +CUDA Accelerated Graph Library + +Component + +NVIDIA Performance Primitives Library + +Windows + +nppc.dll, nppc.lib, nppial.dll, nppial.lib, nppicc.dll, +nppicc.lib, nppicom.dll, nppicom.lib, nppidei.dll, +nppidei.lib, nppif.dll, nppif.lib, nppig.dll, nppig.lib, +nppim.dll, nppim.lib, nppist.dll, nppist.lib, nppisu.dll, +nppisu.lib, nppitc.dll, nppitc.lib, npps.dll, npps.lib + +Mac OSX + +libnppc.dylib, libnppc_static.a, libnppial.dylib, +libnppial_static.a, libnppicc.dylib, libnppicc_static.a, +libnppicom.dylib, libnppicom_static.a, libnppidei.dylib, +libnppidei_static.a, libnppif.dylib, libnppif_static.a, +libnppig.dylib, libnppig_static.a, libnppim.dylib, +libnppisu_static.a, libnppitc.dylib, libnppitc_static.a, +libnpps.dylib, libnpps_static.a + +Linux + +libnppc.so, libnppc_static.a, libnppial.so, +libnppial_static.a, libnppicc.so, libnppicc_static.a, +libnppicom.so, libnppicom_static.a, libnppidei.so, +libnppidei_static.a, libnppif.so, libnppif_static.a +libnppig.so, libnppig_static.a, libnppim.so, +libnppim_static.a, libnppist.so, libnppist_static.a, +libnppisu.so, libnppisu_static.a, libnppitc.so +libnppitc_static.a, libnpps.so, libnpps_static.a + +Android + +libnppc.so, libnppc_static.a, libnppial.so, +libnppial_static.a, libnppicc.so, libnppicc_static.a, +libnppicom.so, libnppicom_static.a, libnppidei.so, +libnppidei_static.a, libnppif.so, libnppif_static.a +libnppig.so, libnppig_static.a, libnppim.so, +libnppim_static.a, libnppist.so, libnppist_static.a, +libnppisu.so, libnppisu_static.a, libnppitc.so +libnppitc_static.a, libnpps.so, libnpps_static.a + +Component + +NVIDIA JPEG Library + +Linux + +libnvjpeg.so, libnvjpeg_static.a + +Component + +Internal common library required for statically linking to +cuBLAS, cuSPARSE, cuFFT, cuRAND, nvJPEG and NPP + +Mac OSX + +libculibos.a + +Linux + +libculibos.a + +Component + +NVIDIA Runtime Compilation Library and Header + +All + +nvrtc.h + +Windows + +nvrtc.dll, nvrtc-builtins.dll + +Mac OSX + +libnvrtc.dylib, libnvrtc-builtins.dylib + +Linux + +libnvrtc.so, libnvrtc-builtins.so + +Component + +NVIDIA Optimizing Compiler Library + +Windows + +nvvm.dll + +Mac OSX + +libnvvm.dylib + +Linux + +libnvvm.so + +Component + +NVIDIA Common Device Math Functions Library + +Windows + +libdevice.10.bc + +Mac OSX + +libdevice.10.bc + +Linux + +libdevice.10.bc + +Component + +CUDA Occupancy Calculation Header Library + +All + +cuda_occupancy.h + +Component + +CUDA Half Precision Headers + +All + +cuda_fp16.h, cuda_fp16.hpp + +Component + +CUDA Profiling Tools Interface (CUPTI) Library + +Windows + +cupti.dll + +Mac OSX + +libcupti.dylib + +Linux + +libcupti.so + +Component + +NVIDIA Tools Extension Library + +Windows + +nvToolsExt.dll, nvToolsExt.lib + +Mac OSX + +libnvToolsExt.dylib + +Linux + +libnvToolsExt.so + +Component + +NVIDIA CUDA Driver Libraries + +Linux + +libcuda.so, libnvidia-fatbinaryloader.so, +libnvidia-ptxjitcompiler.so + +The NVIDIA CUDA Driver Libraries are only distributable in +applications that meet this criteria: + + 1. The application was developed starting from a NVIDIA CUDA + container obtained from Docker Hub or the NVIDIA GPU + Cloud, and + + 2. The resulting application is packaged as a Docker + container and distributed to users on Docker Hub or the + NVIDIA GPU Cloud only. + + +2.7. Attachment B + + +Additional Licensing Obligations + +The following third party components included in the SOFTWARE +are licensed to Licensee pursuant to the following terms and +conditions: + + 1. Licensee's use of the GDB third party component is + subject to the terms and conditions of GNU GPL v3: + + This product includes copyrighted third-party software licensed + under the terms of the GNU General Public License v3 ("GPL v3"). + All third-party software packages are copyright by their respective + authors. GPL v3 terms and conditions are hereby incorporated into + the Agreement by this reference: http://www.gnu.org/licenses/gpl.txt + + Consistent with these licensing requirements, the software + listed below is provided under the terms of the specified + open source software licenses. To obtain source code for + software provided under licenses that require + redistribution of source code, including the GNU General + Public License (GPL) and GNU Lesser General Public License + (LGPL), contact oss-requests@nvidia.com. This offer is + valid for a period of three (3) years from the date of the + distribution of this product by NVIDIA CORPORATION. + + Component License + CUDA-GDB GPL v3 + + 2. Licensee represents and warrants that any and all third + party licensing and/or royalty payment obligations in + connection with Licensee's use of the H.264 video codecs + are solely the responsibility of Licensee. + + 3. Licensee's use of the Thrust library is subject to the + terms and conditions of the Apache License Version 2.0. + All third-party software packages are copyright by their + respective authors. Apache License Version 2.0 terms and + conditions are hereby incorporated into the Agreement by + this reference. + http://www.apache.org/licenses/LICENSE-2.0.html + + In addition, Licensee acknowledges the following notice: + Thrust includes source code from the Boost Iterator, + Tuple, System, and Random Number libraries. + + Boost Software License - Version 1.0 - August 17th, 2003 + . . . . + + Permission is hereby granted, free of charge, to any person or + organization obtaining a copy of the software and accompanying + documentation covered by this license (the "Software") to use, + reproduce, display, distribute, execute, and transmit the Software, + and to prepare derivative works of the Software, and to permit + third-parties to whom the Software is furnished to do so, all + subject to the following: + + The copyright notices in the Software and this entire statement, + including the above license grant, this restriction and the following + disclaimer, must be included in all copies of the Software, in whole + or in part, and all derivative works of the Software, unless such + copies or derivative works are solely in the form of machine-executable + object code generated by a source language processor. + + 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, TITLE AND + NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR + ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR + OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + 4. Licensee's use of the LLVM third party component is + subject to the following terms and conditions: + + ====================================================== + LLVM Release License + ====================================================== + University of Illinois/NCSA + Open Source License + + Copyright (c) 2003-2010 University of Illinois at Urbana-Champaign. + All rights reserved. + + Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal with 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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at Urbana- + Champaign, nor the names of its contributors may be used to endorse or + promote products derived from this Software without specific prior + written permission. + + 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 CONTRIBUTORS 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 WITH THE SOFTWARE. + + 5. Licensee's use (e.g. nvprof) of the PCRE third party + component is subject to the following terms and + conditions: + + ------------ + PCRE LICENCE + ------------ + PCRE is a library of functions to support regular expressions whose syntax + and semantics are as close as possible to those of the Perl 5 language. + Release 8 of PCRE is distributed under the terms of the "BSD" licence, as + specified below. The documentation for PCRE, supplied in the "doc" + directory, is distributed under the same terms as the software itself. The + basic library functions are written in C and are freestanding. Also + included in the distribution is a set of C++ wrapper functions, and a just- + in-time compiler that can be used to optimize pattern matching. These are + both optional features that can be omitted when the library is built. + + THE BASIC LIBRARY FUNCTIONS + --------------------------- + Written by: Philip Hazel + Email local part: ph10 + Email domain: cam.ac.uk + University of Cambridge Computing Service, + Cambridge, England. + Copyright (c) 1997-2012 University of Cambridge + All rights reserved. + + PCRE JUST-IN-TIME COMPILATION SUPPORT + ------------------------------------- + Written by: Zoltan Herczeg + Email local part: hzmester + Emain domain: freemail.hu + Copyright(c) 2010-2012 Zoltan Herczeg + All rights reserved. + + STACK-LESS JUST-IN-TIME COMPILER + -------------------------------- + Written by: Zoltan Herczeg + Email local part: hzmester + Emain domain: freemail.hu + Copyright(c) 2009-2012 Zoltan Herczeg + All rights reserved. + + THE C++ WRAPPER FUNCTIONS + ------------------------- + Contributed by: Google Inc. + Copyright (c) 2007-2012, Google Inc. + All rights reserved. + + THE "BSD" LICENCE + ----------------- + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the name of Google + Inc. nor the names of their 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. + + 6. Some of the cuBLAS library routines were written by or + derived from code written by Vasily Volkov and are subject + to the Modified Berkeley Software Distribution License as + follows: + + Copyright (c) 2007-2009, Regents of the University of California + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the University of California, Berkeley 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 AUTHOR "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 AUTHOR 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. + + 7. Some of the cuBLAS library routines were written by or + derived from code written by Davide Barbieri and are + subject to the Modified Berkeley Software Distribution + License as follows: + + Copyright (c) 2008-2009 Davide Barbieri @ University of Rome Tor Vergata. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * The name of the author may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "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 AUTHOR 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. + + 8. Some of the cuBLAS library routines were derived from + code developed by the University of Tennessee and are + subject to the Modified Berkeley Software Distribution + License as follows: + + Copyright (c) 2010 The University of Tennessee. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer listed in this license in the documentation and/or + other materials provided with the distribution. + * Neither the name of the copyright holders 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. + + 9. Some of the cuBLAS library routines were written by or + derived from code written by Jonathan Hogg and are subject + to the Modified Berkeley Software Distribution License as + follows: + + Copyright (c) 2012, The Science and Technology Facilities Council (STFC). + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the STFC 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 STFC 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. + + 10. Some of the cuBLAS library routines were written by or + derived from code written by Ahmad M. Abdelfattah, David + Keyes, and Hatem Ltaief, and are subject to the Apache + License, Version 2.0, as follows: + + -- (C) Copyright 2013 King Abdullah University of Science and Technology + Authors: + Ahmad Abdelfattah (ahmad.ahmad@kaust.edu.sa) + David Keyes (david.keyes@kaust.edu.sa) + Hatem Ltaief (hatem.ltaief@kaust.edu.sa) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the King Abdullah University of Science and + Technology 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 + HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + + 11. Some of the cuSPARSE library routines were written by or + derived from code written by Li-Wen Chang and are subject + to the NCSA Open Source License as follows: + + Copyright (c) 2012, University of Illinois. + + All rights reserved. + + Developed by: IMPACT Group, University of Illinois, http://impact.crhc.illinois.edu + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal with 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: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimers in the documentation and/or other materials provided + with the distribution. + * Neither the names of IMPACT Group, University of Illinois, nor + the names of its contributors may be used to endorse or promote + products derived from this Software without specific prior + written permission. + + 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 CONTRIBUTORS 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 WITH THE + SOFTWARE. + + 12. Some of the cuRAND library routines were written by or + derived from code written by Mutsuo Saito and Makoto + Matsumoto and are subject to the following license: + + Copyright (c) 2009, 2010 Mutsuo Saito, Makoto Matsumoto and Hiroshima + University. All rights reserved. + + Copyright (c) 2011 Mutsuo Saito, Makoto Matsumoto, Hiroshima + University and University of Tokyo. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the Hiroshima University 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. + + 13. Some of the cuRAND library routines were derived from + code developed by D. E. Shaw Research and are subject to + the following license: + + Copyright 2010-2011, D. E. Shaw Research. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions, and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of D. E. Shaw Research 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. + + 14. Some of the Math library routines were written by or + derived from code developed by Norbert Juffa and are + subject to the following license: + + Copyright (c) 2015-2017, Norbert Juffa + 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. + + 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. + + 15. Licensee's use of the lz4 third party component is + subject to the following terms and conditions: + + Copyright (C) 2011-2013, Yann Collet. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + 16. The NPP library uses code from the Boost Math Toolkit, + and is subject to the following license: + + Boost Software License - Version 1.0 - August 17th, 2003 + . . . . + + Permission is hereby granted, free of charge, to any person or + organization obtaining a copy of the software and accompanying + documentation covered by this license (the "Software") to use, + reproduce, display, distribute, execute, and transmit the Software, + and to prepare derivative works of the Software, and to permit + third-parties to whom the Software is furnished to do so, all + subject to the following: + + The copyright notices in the Software and this entire statement, + including the above license grant, this restriction and the following + disclaimer, must be included in all copies of the Software, in whole + or in part, and all derivative works of the Software, unless such + copies or derivative works are solely in the form of machine-executable + object code generated by a source language processor. + + 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, TITLE AND + NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR + ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR + OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + 17. Portions of the Nsight Eclipse Edition is subject to the + following license: + + The Eclipse Foundation makes available all content in this plug-in + ("Content"). Unless otherwise indicated below, the Content is provided + to you under the terms and conditions of the Eclipse Public License + Version 1.0 ("EPL"). A copy of the EPL is available at http:// + www.eclipse.org/legal/epl-v10.html. For purposes of the EPL, "Program" + will mean the Content. + + If you did not receive this Content directly from the Eclipse + Foundation, the Content is being redistributed by another party + ("Redistributor") and different terms and conditions may apply to your + use of any object code in the Content. Check the Redistributor's + license that was provided with the Content. If no such license exists, + contact the Redistributor. Unless otherwise indicated below, the terms + and conditions of the EPL still apply to any source code in the + Content and such source code may be obtained at http://www.eclipse.org. + + 18. Some of the cuBLAS library routines uses code from + OpenAI, which is subject to the following license: + + License URL + https://github.com/openai/openai-gemm/blob/master/LICENSE + + License Text + The MIT License + + Copyright (c) 2016 OpenAI (http://openai.com), 2016 Google Inc. + + 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. + + 19. Licensee's use of the Visual Studio Setup Configuration + Samples is subject to the following license: + + The MIT License (MIT) + Copyright (C) Microsoft Corporation. All rights reserved. + + 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. + + 20. Licensee's use of linmath.h header for CPU functions for + GL vector/matrix operations from lunarG is subject to the + Apache License Version 2.0. + + 21. The DX12-CUDA sample uses the d3dx12.h header, which is + subject to the MIT license . + +----------------- diff --git a/.venv/lib/python3.12/site-packages/nvidia_cublas_cu12-12.8.4.1.dist-info/METADATA b/.venv/lib/python3.12/site-packages/nvidia_cublas_cu12-12.8.4.1.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..cb533db38cb400ef0b0e71cea45e7ce506422bfd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cublas_cu12-12.8.4.1.dist-info/METADATA @@ -0,0 +1,44 @@ +Metadata-Version: 2.2 +Name: nvidia-cublas-cu12 +Version: 12.8.4.1 +Summary: CUBLAS native runtime libraries +Home-page: https://developer.nvidia.com/cuda-zone +Author: Nvidia CUDA Installer Team +Author-email: compute_installer@nvidia.com +License: NVIDIA Proprietary Software +Keywords: cuda,nvidia,runtime,machine learning,deep learning +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Education +Classifier: Intended Audience :: Science/Research +Classifier: License :: Other/Proprietary License +Classifier: Natural Language :: English +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Topic :: Scientific/Engineering +Classifier: Topic :: Scientific/Engineering :: Mathematics +Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence +Classifier: Topic :: Software Development +Classifier: Topic :: Software Development :: Libraries +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX :: Linux +Requires-Python: >=3 +License-File: License.txt +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: home-page +Dynamic: keywords +Dynamic: license +Dynamic: requires-python +Dynamic: summary + +CUBLAS native runtime libraries diff --git a/.venv/lib/python3.12/site-packages/nvidia_cublas_cu12-12.8.4.1.dist-info/RECORD b/.venv/lib/python3.12/site-packages/nvidia_cublas_cu12-12.8.4.1.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..7c57f441b091530a4183e9e5151ea9b43cb34893 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cublas_cu12-12.8.4.1.dist-info/RECORD @@ -0,0 +1,20 @@ +nvidia/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/cublas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/cublas/include/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/cublas/include/cublas.h,sha256=a0lLqy-k47NuwyDjuueC3W0Mpc908MTU7o5sMJqE-1w,41246 +nvidia/cublas/include/cublasLt.h,sha256=oH9TR01H5CWfGPIjJk6-Ljg8eQOu0gho7TNt83gCmwg,102451 +nvidia/cublas/include/cublasXt.h,sha256=CW9dyXYGSUW1wEXrVVyhU6OxBK1PUvMoYdVGlQT7L9A,37380 +nvidia/cublas/include/cublas_api.h,sha256=A4Jvv9elvoJIDw8ReUP7qBnSxEvdslc-ghW_ycq_qAU,374363 +nvidia/cublas/include/cublas_v2.h,sha256=qxMdB5jb97luEfw61LEAB-Wlr8A9DLBvO4rRypDCNKw,15460 +nvidia/cublas/include/nvblas.h,sha256=dXCLR-2oUiJFzLsDtIAK09m42ct4G0HWdYzBUuDPXpc,23341 +nvidia/cublas/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/cublas/lib/libcublas.so.12,sha256=Axzmwsv7uUaPBAUnyrXFmQac5WCec-KPh1A4gQY-rCE,116388640 +nvidia/cublas/lib/libcublasLt.so.12,sha256=ELXmYxz4EVxmHriV7RUzgmMItY95VkZvU9I2pAybYiw,751771728 +nvidia/cublas/lib/libnvblas.so.12,sha256=nSRIINMpZvCvR6_mye5vNLIw-v6H6gFB3HEkGl0rNAw,753824 +nvidia_cublas_cu12-12.8.4.1.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +nvidia_cublas_cu12-12.8.4.1.dist-info/License.txt,sha256=rW9YU_ugyg0VnQ9Y1JrkmDDC-Mk_epJki5zpCttMbM0,59262 +nvidia_cublas_cu12-12.8.4.1.dist-info/METADATA,sha256=MQVW8cLDHsUX1LBJ6vEjdAgdHFLI702Ozqsqrx7f8t8,1683 +nvidia_cublas_cu12-12.8.4.1.dist-info/RECORD,, +nvidia_cublas_cu12-12.8.4.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia_cublas_cu12-12.8.4.1.dist-info/WHEEL,sha256=wwQXGCXJQEUF59fKjBnVVzOkzi78E6rE_-QuUR4ZT4w,109 +nvidia_cublas_cu12-12.8.4.1.dist-info/top_level.txt,sha256=fTkAtiFuL16nUrB9ytDDtpytz2t0B4NvYTnRzwAhO14,7 diff --git a/.venv/lib/python3.12/site-packages/nvidia_cublas_cu12-12.8.4.1.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/nvidia_cublas_cu12-12.8.4.1.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/nvidia_cublas_cu12-12.8.4.1.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/nvidia_cublas_cu12-12.8.4.1.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..6a0d9dc1b21a540a280fda3e78767d22a3b7ed6e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cublas_cu12-12.8.4.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.8.1) +Root-Is-Purelib: true +Tag: py3-none-manylinux_2_27_x86_64 + diff --git a/.venv/lib/python3.12/site-packages/nvidia_cublas_cu12-12.8.4.1.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/nvidia_cublas_cu12-12.8.4.1.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..862f7abf232cdfbb928609856247292e81c9decb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cublas_cu12-12.8.4.1.dist-info/top_level.txt @@ -0,0 +1 @@ +nvidia diff --git a/.venv/lib/python3.12/site-packages/nvidia_cudnn_cu12-9.10.2.21.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/nvidia_cudnn_cu12-9.10.2.21.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cudnn_cu12-9.10.2.21.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/nvidia_cudnn_cu12-9.10.2.21.dist-info/METADATA b/.venv/lib/python3.12/site-packages/nvidia_cudnn_cu12-9.10.2.21.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..c0be1e140b0ba64bff1dcf5cda95da9d6abcccf5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cudnn_cu12-9.10.2.21.dist-info/METADATA @@ -0,0 +1,47 @@ +Metadata-Version: 2.4 +Name: nvidia-cudnn-cu12 +Version: 9.10.2.21 +Summary: cuDNN runtime libraries +Home-page: https://developer.nvidia.com/cuda-zone +Author: Nvidia CUDA Installer Team +Author-email: compute_installer@nvidia.com +License: LicenseRef-NVIDIA-Proprietary +Keywords: cuda,nvidia,runtime,machine learning,deep learning +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Education +Classifier: Intended Audience :: Science/Research +Classifier: License :: Other/Proprietary License +Classifier: Natural Language :: English +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Topic :: Scientific/Engineering +Classifier: Topic :: Scientific/Engineering :: Mathematics +Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence +Classifier: Topic :: Software Development +Classifier: Topic :: Software Development :: Libraries +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX :: Linux +Requires-Python: >=3 +License-File: License.txt +Requires-Dist: nvidia-cublas-cu12 +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: home-page +Dynamic: keywords +Dynamic: license +Dynamic: license-file +Dynamic: requires-dist +Dynamic: requires-python +Dynamic: summary + +cuDNN runtime libraries containing primitives for deep neural networks. diff --git a/.venv/lib/python3.12/site-packages/nvidia_cudnn_cu12-9.10.2.21.dist-info/RECORD b/.venv/lib/python3.12/site-packages/nvidia_cudnn_cu12-9.10.2.21.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..60595b0404e22df7ad06913f96f5f585708100ad --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cudnn_cu12-9.10.2.21.dist-info/RECORD @@ -0,0 +1,29 @@ +nvidia/cudnn/include/cudnn.h,sha256=EILVHTtWS6zo72_G7jNbZosr-lF_V8Bu_UKCY-XCGFU,2841 +nvidia/cudnn/include/cudnn_adv.h,sha256=9As4tvtmhXmC9agNuLfBtSAmtQFhKMMevFS5xigJ6QQ,30855 +nvidia/cudnn/include/cudnn_adv_v9.h,sha256=9As4tvtmhXmC9agNuLfBtSAmtQFhKMMevFS5xigJ6QQ,30855 +nvidia/cudnn/include/cudnn_backend.h,sha256=rdTyTGHQftsrhWeJS6bQwZDQCzrdyAFhoj0gbRkn1F8,2751 +nvidia/cudnn/include/cudnn_backend_v9.h,sha256=rdTyTGHQftsrhWeJS6bQwZDQCzrdyAFhoj0gbRkn1F8,2751 +nvidia/cudnn/include/cudnn_cnn.h,sha256=D3O_KAruI5VroyOXwWgrbtz25hBowP5StWQX_1UGMNM,36700 +nvidia/cudnn/include/cudnn_cnn_v9.h,sha256=D3O_KAruI5VroyOXwWgrbtz25hBowP5StWQX_1UGMNM,36700 +nvidia/cudnn/include/cudnn_graph.h,sha256=abi06zpTOSkpM13noetprjMtDCp8wMcPdRLJiPcq96g,44680 +nvidia/cudnn/include/cudnn_graph_v9.h,sha256=abi06zpTOSkpM13noetprjMtDCp8wMcPdRLJiPcq96g,44680 +nvidia/cudnn/include/cudnn_ops.h,sha256=UPOi5sb3zRn1N6SKveu576RwVIRFYbLAKngBM_IMSLg,63635 +nvidia/cudnn/include/cudnn_ops_v9.h,sha256=UPOi5sb3zRn1N6SKveu576RwVIRFYbLAKngBM_IMSLg,63635 +nvidia/cudnn/include/cudnn_v9.h,sha256=EILVHTtWS6zo72_G7jNbZosr-lF_V8Bu_UKCY-XCGFU,2841 +nvidia/cudnn/include/cudnn_version.h,sha256=SR1hDk5hvh1eVzx29ihJo_C8YIpqY6sbS9kSnJ0oW10,3114 +nvidia/cudnn/include/cudnn_version_v9.h,sha256=SR1hDk5hvh1eVzx29ihJo_C8YIpqY6sbS9kSnJ0oW10,3114 +nvidia/cudnn/lib/libcudnn.so.9,sha256=O2jqaJvmR7zmPLPC2e21ia3UFUBafp8pbWnKApyuC4s,125136 +nvidia/cudnn/lib/libcudnn_adv.so.9,sha256=mBTWTgT84fbL0W0CzO_WYRVBs8YXNvtFDjNLSCY1ygc,285226600 +nvidia/cudnn/lib/libcudnn_cnn.so.9,sha256=ZVwtJklGbHO2tMGXBftpBv2V4zGh-CGDFLSfN918r-0,6301096 +nvidia/cudnn/lib/libcudnn_engines_precompiled.so.9,sha256=9QR0XTRlQmCfl13lEicIBkfTZ16sV26tgsOWuBJPjyg,547383096 +nvidia/cudnn/lib/libcudnn_engines_runtime_compiled.so.9,sha256=giJnJ0PE8uOf6xw2XW9rbED1YCNHFQb0mUSRCEfx7go,23094328 +nvidia/cudnn/lib/libcudnn_graph.so.9,sha256=eHyVXc5JCR6thQ5FNmZllAleqfkqigiHnY3a1GZnRlc,4439912 +nvidia/cudnn/lib/libcudnn_heuristic.so.9,sha256=KATu5fb8EdApm6B2h-2gqXTTskNjV8Y5cGxOKCzCzAU,58726712 +nvidia/cudnn/lib/libcudnn_ops.so.9,sha256=NPVtZ_PfEIlJ0eXgNUMGTgVeIX0PXlPcbU98lFTYUvg,127936080 +nvidia_cudnn_cu12-9.10.2.21.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +nvidia_cudnn_cu12-9.10.2.21.dist-info/METADATA,sha256=fcfqNUERnd6hXtmmjp8B5KFgsWRgerrSL53sVOKTcmY,1796 +nvidia_cudnn_cu12-9.10.2.21.dist-info/RECORD,, +nvidia_cudnn_cu12-9.10.2.21.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia_cudnn_cu12-9.10.2.21.dist-info/WHEEL,sha256=pQkYcE_zO8O3IUE-PXGJ2qj02_9aLe-AqqPPB3Nn1Xg,109 +nvidia_cudnn_cu12-9.10.2.21.dist-info/licenses/License.txt,sha256=Sc95vbNXNLUv5iAwE7O9dZ-B6ZjNMqosZcUduaiMYdI,18174 +nvidia_cudnn_cu12-9.10.2.21.dist-info/top_level.txt,sha256=fTkAtiFuL16nUrB9ytDDtpytz2t0B4NvYTnRzwAhO14,7 diff --git a/.venv/lib/python3.12/site-packages/nvidia_cudnn_cu12-9.10.2.21.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/nvidia_cudnn_cu12-9.10.2.21.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/nvidia_cudnn_cu12-9.10.2.21.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/nvidia_cudnn_cu12-9.10.2.21.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..08a681d359db9e13e5679534af805b7d41f40794 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cudnn_cu12-9.10.2.21.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: true +Tag: py3-none-manylinux_2_27_x86_64 + diff --git a/.venv/lib/python3.12/site-packages/nvidia_cudnn_cu12-9.10.2.21.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/nvidia_cudnn_cu12-9.10.2.21.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..862f7abf232cdfbb928609856247292e81c9decb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cudnn_cu12-9.10.2.21.dist-info/top_level.txt @@ -0,0 +1 @@ +nvidia diff --git a/.venv/lib/python3.12/site-packages/nvidia_cufft_cu12-11.3.3.83.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/nvidia_cufft_cu12-11.3.3.83.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cufft_cu12-11.3.3.83.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/nvidia_cufft_cu12-11.3.3.83.dist-info/License.txt b/.venv/lib/python3.12/site-packages/nvidia_cufft_cu12-11.3.3.83.dist-info/License.txt new file mode 100644 index 0000000000000000000000000000000000000000..b491c70e0aef319022ded661e111ddbd45b8a17f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cufft_cu12-11.3.3.83.dist-info/License.txt @@ -0,0 +1,1568 @@ +End User License Agreement +-------------------------- + + +Preface +------- + +The Software License Agreement in Chapter 1 and the Supplement +in Chapter 2 contain license terms and conditions that govern +the use of NVIDIA software. By accepting this agreement, you +agree to comply with all the terms and conditions applicable +to the product(s) included herein. + + +NVIDIA Driver + + +Description + +This package contains the operating system driver and +fundamental system software components for NVIDIA GPUs. + + +NVIDIA CUDA Toolkit + + +Description + +The NVIDIA CUDA Toolkit provides command-line and graphical +tools for building, debugging and optimizing the performance +of applications accelerated by NVIDIA GPUs, runtime and math +libraries, and documentation including programming guides, +user manuals, and API references. + + +Default Install Location of CUDA Toolkit + +Windows platform: + +%ProgramFiles%\NVIDIA GPU Computing Toolkit\CUDA\v#.# + +Linux platform: + +/usr/local/cuda-#.# + +Mac platform: + +/Developer/NVIDIA/CUDA-#.# + + +NVIDIA CUDA Samples + + +Description + +This package includes over 100+ CUDA examples that demonstrate +various CUDA programming principles, and efficient CUDA +implementation of algorithms in specific application domains. + + +Default Install Location of CUDA Samples + +Windows platform: + +%ProgramData%\NVIDIA Corporation\CUDA Samples\v#.# + +Linux platform: + +/usr/local/cuda-#.#/samples + +and + +$HOME/NVIDIA_CUDA-#.#_Samples + +Mac platform: + +/Developer/NVIDIA/CUDA-#.#/samples + + +NVIDIA Nsight Visual Studio Edition (Windows only) + + +Description + +NVIDIA Nsight Development Platform, Visual Studio Edition is a +development environment integrated into Microsoft Visual +Studio that provides tools for debugging, profiling, analyzing +and optimizing your GPU computing and graphics applications. + + +Default Install Location of Nsight Visual Studio Edition + +Windows platform: + +%ProgramFiles(x86)%\NVIDIA Corporation\Nsight Visual Studio Edition #.# + + +1. License Agreement for NVIDIA Software Development Kits +--------------------------------------------------------- + + +Release Date: July 26, 2018 +--------------------------- + + +Important NoticeRead before downloading, installing, +copying or using the licensed software: +------------------------------------------------------- + +This license agreement, including exhibits attached +("Agreement”) is a legal agreement between you and NVIDIA +Corporation ("NVIDIA") and governs your use of a NVIDIA +software development kit (“SDK”). + +Each SDK has its own set of software and materials, but here +is a description of the types of items that may be included in +a SDK: source code, header files, APIs, data sets and assets +(examples include images, textures, models, scenes, videos, +native API input/output files), binary software, sample code, +libraries, utility programs, programming code and +documentation. + +This Agreement can be accepted only by an adult of legal age +of majority in the country in which the SDK is used. + +If you are entering into this Agreement on behalf of a company +or other legal entity, you represent that you have the legal +authority to bind the entity to this Agreement, in which case +“you” will mean the entity you represent. + +If you don’t have the required age or authority to accept +this Agreement, or if you don’t accept all the terms and +conditions of this Agreement, do not download, install or use +the SDK. + +You agree to use the SDK only for purposes that are permitted +by (a) this Agreement, and (b) any applicable law, regulation +or generally accepted practices or guidelines in the relevant +jurisdictions. + + +1.1. License + + +1.1.1. License Grant + +Subject to the terms of this Agreement, NVIDIA hereby grants +you a non-exclusive, non-transferable license, without the +right to sublicense (except as expressly provided in this +Agreement) to: + + 1. Install and use the SDK, + + 2. Modify and create derivative works of sample source code + delivered in the SDK, and + + 3. Distribute those portions of the SDK that are identified + in this Agreement as distributable, as incorporated in + object code format into a software application that meets + the distribution requirements indicated in this Agreement. + + +1.1.2. Distribution Requirements + +These are the distribution requirements for you to exercise +the distribution grant: + + 1. Your application must have material additional + functionality, beyond the included portions of the SDK. + + 2. The distributable portions of the SDK shall only be + accessed by your application. + + 3. The following notice shall be included in modifications + and derivative works of sample source code distributed: + “This software contains source code provided by NVIDIA + Corporation.” + + 4. Unless a developer tool is identified in this Agreement + as distributable, it is delivered for your internal use + only. + + 5. The terms under which you distribute your application + must be consistent with the terms of this Agreement, + including (without limitation) terms relating to the + license grant and license restrictions and protection of + NVIDIA’s intellectual property rights. Additionally, you + agree that you will protect the privacy, security and + legal rights of your application users. + + 6. You agree to notify NVIDIA in writing of any known or + suspected distribution or use of the SDK not in compliance + with the requirements of this Agreement, and to enforce + the terms of your agreements with respect to distributed + SDK. + + +1.1.3. Authorized Users + +You may allow employees and contractors of your entity or of +your subsidiary(ies) to access and use the SDK from your +secure network to perform work on your behalf. + +If you are an academic institution you may allow users +enrolled or employed by the academic institution to access and +use the SDK from your secure network. + +You are responsible for the compliance with the terms of this +Agreement by your authorized users. If you become aware that +your authorized users didn’t follow the terms of this +Agreement, you agree to take reasonable steps to resolve the +non-compliance and prevent new occurrences. + + +1.1.4. Pre-Release SDK + +The SDK versions identified as alpha, beta, preview or +otherwise as pre-release, may not be fully functional, may +contain errors or design flaws, and may have reduced or +different security, privacy, accessibility, availability, and +reliability standards relative to commercial versions of +NVIDIA software and materials. Use of a pre-release SDK may +result in unexpected results, loss of data, project delays or +other unpredictable damage or loss. + +You may use a pre-release SDK at your own risk, understanding +that pre-release SDKs are not intended for use in production +or business-critical systems. + +NVIDIA may choose not to make available a commercial version +of any pre-release SDK. NVIDIA may also choose to abandon +development and terminate the availability of a pre-release +SDK at any time without liability. + + +1.1.5. Updates + +NVIDIA may, at its option, make available patches, workarounds +or other updates to this SDK. Unless the updates are provided +with their separate governing terms, they are deemed part of +the SDK licensed to you as provided in this Agreement. You +agree that the form and content of the SDK that NVIDIA +provides may change without prior notice to you. While NVIDIA +generally maintains compatibility between versions, NVIDIA may +in some cases make changes that introduce incompatibilities in +future versions of the SDK. + + +1.1.6. Third Party Licenses + +The SDK may come bundled with, or otherwise include or be +distributed with, third party software licensed by a NVIDIA +supplier and/or open source software provided under an open +source license. Use of third party software is subject to the +third-party license terms, or in the absence of third party +terms, the terms of this Agreement. Copyright to third party +software is held by the copyright holders indicated in the +third-party software or license. + + +1.1.7. Reservation of Rights + +NVIDIA reserves all rights, title, and interest in and to the +SDK, not expressly granted to you under this Agreement. + + +1.2. Limitations + +The following license limitations apply to your use of the +SDK: + + 1. You may not reverse engineer, decompile or disassemble, + or remove copyright or other proprietary notices from any + portion of the SDK or copies of the SDK. + + 2. Except as expressly provided in this Agreement, you may + not copy, sell, rent, sublicense, transfer, distribute, + modify, or create derivative works of any portion of the + SDK. For clarity, you may not distribute or sublicense the + SDK as a stand-alone product. + + 3. Unless you have an agreement with NVIDIA for this + purpose, you may not indicate that an application created + with the SDK is sponsored or endorsed by NVIDIA. + + 4. You may not bypass, disable, or circumvent any + encryption, security, digital rights management or + authentication mechanism in the SDK. + + 5. You may not use the SDK in any manner that would cause it + to become subject to an open source software license. As + examples, licenses that require as a condition of use, + modification, and/or distribution that the SDK be: + + a. Disclosed or distributed in source code form; + + b. Licensed for the purpose of making derivative works; + or + + c. Redistributable at no charge. + + 6. Unless you have an agreement with NVIDIA for this + purpose, you may not use the SDK with any system or + application where the use or failure of the system or + application can reasonably be expected to threaten or + result in personal injury, death, or catastrophic loss. + Examples include use in avionics, navigation, military, + medical, life support or other life critical applications. + NVIDIA does not design, test or manufacture the SDK for + these critical uses and NVIDIA shall not be liable to you + or any third party, in whole or in part, for any claims or + damages arising from such uses. + + 7. You agree to defend, indemnify and hold harmless NVIDIA + and its affiliates, and their respective employees, + contractors, agents, officers and directors, from and + against any and all claims, damages, obligations, losses, + liabilities, costs or debt, fines, restitutions and + expenses (including but not limited to attorney’s fees + and costs incident to establishing the right of + indemnification) arising out of or related to your use of + the SDK outside of the scope of this Agreement, or not in + compliance with its terms. + + +1.3. Ownership + + 1. NVIDIA or its licensors hold all rights, title and + interest in and to the SDK and its modifications and + derivative works, including their respective intellectual + property rights, subject to your rights described in this + section. This SDK may include software and materials from + NVIDIA’s licensors, and these licensors are intended + third party beneficiaries that may enforce this Agreement + with respect to their intellectual property rights. + + 2. You hold all rights, title and interest in and to your + applications and your derivative works of the sample + source code delivered in the SDK, including their + respective intellectual property rights, subject to + NVIDIA’s rights described in this section. + + 3. You may, but don’t have to, provide to NVIDIA + suggestions, feature requests or other feedback regarding + the SDK, including possible enhancements or modifications + to the SDK. For any feedback that you voluntarily provide, + you hereby grant NVIDIA and its affiliates a perpetual, + non-exclusive, worldwide, irrevocable license to use, + reproduce, modify, license, sublicense (through multiple + tiers of sublicensees), and distribute (through multiple + tiers of distributors) it without the payment of any + royalties or fees to you. NVIDIA will use feedback at its + choice. NVIDIA is constantly looking for ways to improve + its products, so you may send feedback to NVIDIA through + the developer portal at https://developer.nvidia.com. + + +1.4. No Warranties + +THE SDK IS PROVIDED BY NVIDIA “AS IS” AND “WITH ALL +FAULTS.” TO THE MAXIMUM EXTENT PERMITTED BY LAW, NVIDIA AND +ITS AFFILIATES EXPRESSLY DISCLAIM ALL WARRANTIES OF ANY KIND +OR NATURE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, +BUT NOT LIMITED TO, ANY WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE, TITLE, NON-INFRINGEMENT, OR THE +ABSENCE OF ANY DEFECTS THEREIN, WHETHER LATENT OR PATENT. NO +WARRANTY IS MADE ON THE BASIS OF TRADE USAGE, COURSE OF +DEALING OR COURSE OF TRADE. + + +1.5. Limitation of Liability + +TO THE MAXIMUM EXTENT PERMITTED BY LAW, NVIDIA AND ITS +AFFILIATES SHALL NOT BE LIABLE FOR ANY SPECIAL, INCIDENTAL, +PUNITIVE OR CONSEQUENTIAL DAMAGES, OR ANY LOST PROFITS, LOSS +OF USE, LOSS OF DATA OR LOSS OF GOODWILL, OR THE COSTS OF +PROCURING SUBSTITUTE PRODUCTS, ARISING OUT OF OR IN CONNECTION +WITH THIS AGREEMENT OR THE USE OR PERFORMANCE OF THE SDK, +WHETHER SUCH LIABILITY ARISES FROM ANY CLAIM BASED UPON BREACH +OF CONTRACT, BREACH OF WARRANTY, TORT (INCLUDING NEGLIGENCE), +PRODUCT LIABILITY OR ANY OTHER CAUSE OF ACTION OR THEORY OF +LIABILITY. IN NO EVENT WILL NVIDIA’S AND ITS AFFILIATES +TOTAL CUMULATIVE LIABILITY UNDER OR ARISING OUT OF THIS +AGREEMENT EXCEED US$10.00. THE NATURE OF THE LIABILITY OR THE +NUMBER OF CLAIMS OR SUITS SHALL NOT ENLARGE OR EXTEND THIS +LIMIT. + +These exclusions and limitations of liability shall apply +regardless if NVIDIA or its affiliates have been advised of +the possibility of such damages, and regardless of whether a +remedy fails its essential purpose. These exclusions and +limitations of liability form an essential basis of the +bargain between the parties, and, absent any of these +exclusions or limitations of liability, the provisions of this +Agreement, including, without limitation, the economic terms, +would be substantially different. + + +1.6. Termination + + 1. This Agreement will continue to apply until terminated by + either you or NVIDIA as described below. + + 2. If you want to terminate this Agreement, you may do so by + stopping to use the SDK. + + 3. NVIDIA may, at any time, terminate this Agreement if: + + a. (i) you fail to comply with any term of this + Agreement and the non-compliance is not fixed within + thirty (30) days following notice from NVIDIA (or + immediately if you violate NVIDIA’s intellectual + property rights); + + b. (ii) you commence or participate in any legal + proceeding against NVIDIA with respect to the SDK; or + + c. (iii) NVIDIA decides to no longer provide the SDK in + a country or, in NVIDIA’s sole discretion, the + continued use of it is no longer commercially viable. + + 4. Upon any termination of this Agreement, you agree to + promptly discontinue use of the SDK and destroy all copies + in your possession or control. Your prior distributions in + accordance with this Agreement are not affected by the + termination of this Agreement. Upon written request, you + will certify in writing that you have complied with your + commitments under this section. Upon any termination of + this Agreement all provisions survive except for the + license grant provisions. + + +1.7. General + +If you wish to assign this Agreement or your rights and +obligations, including by merger, consolidation, dissolution +or operation of law, contact NVIDIA to ask for permission. Any +attempted assignment not approved by NVIDIA in writing shall +be void and of no effect. NVIDIA may assign, delegate or +transfer this Agreement and its rights and obligations, and if +to a non-affiliate you will be notified. + +You agree to cooperate with NVIDIA and provide reasonably +requested information to verify your compliance with this +Agreement. + +This Agreement will be governed in all respects by the laws of +the United States and of the State of Delaware as those laws +are applied to contracts entered into and performed entirely +within Delaware by Delaware residents, without regard to the +conflicts of laws principles. The United Nations Convention on +Contracts for the International Sale of Goods is specifically +disclaimed. You agree to all terms of this Agreement in the +English language. + +The state or federal courts residing in Santa Clara County, +California shall have exclusive jurisdiction over any dispute +or claim arising out of this Agreement. Notwithstanding this, +you agree that NVIDIA shall still be allowed to apply for +injunctive remedies or an equivalent type of urgent legal +relief in any jurisdiction. + +If any court of competent jurisdiction determines that any +provision of this Agreement is illegal, invalid or +unenforceable, such provision will be construed as limited to +the extent necessary to be consistent with and fully +enforceable under the law and the remaining provisions will +remain in full force and effect. Unless otherwise specified, +remedies are cumulative. + +Each party acknowledges and agrees that the other is an +independent contractor in the performance of this Agreement. + +The SDK has been developed entirely at private expense and is +“commercial items” consisting of “commercial computer +software” and “commercial computer software +documentation” provided with RESTRICTED RIGHTS. Use, +duplication or disclosure by the U.S. Government or a U.S. +Government subcontractor is subject to the restrictions in +this Agreement pursuant to DFARS 227.7202-3(a) or as set forth +in subparagraphs (c)(1) and (2) of the Commercial Computer +Software - Restricted Rights clause at FAR 52.227-19, as +applicable. Contractor/manufacturer is NVIDIA, 2788 San Tomas +Expressway, Santa Clara, CA 95051. + +The SDK is subject to United States export laws and +regulations. You agree that you will not ship, transfer or +export the SDK into any country, or use the SDK in any manner, +prohibited by the United States Bureau of Industry and +Security or economic sanctions regulations administered by the +U.S. Department of Treasury’s Office of Foreign Assets +Control (OFAC), or any applicable export laws, restrictions or +regulations. These laws include restrictions on destinations, +end users and end use. By accepting this Agreement, you +confirm that you are not a resident or citizen of any country +currently embargoed by the U.S. and that you are not otherwise +prohibited from receiving the SDK. + +Any notice delivered by NVIDIA to you under this Agreement +will be delivered via mail, email or fax. You agree that any +notices that NVIDIA sends you electronically will satisfy any +legal communication requirements. Please direct your legal +notices or other correspondence to NVIDIA Corporation, 2788 +San Tomas Expressway, Santa Clara, California 95051, United +States of America, Attention: Legal Department. + +This Agreement and any exhibits incorporated into this +Agreement constitute the entire agreement of the parties with +respect to the subject matter of this Agreement and supersede +all prior negotiations or documentation exchanged between the +parties relating to this SDK license. Any additional and/or +conflicting terms on documents issued by you are null, void, +and invalid. Any amendment or waiver under this Agreement +shall be in writing and signed by representatives of both +parties. + + +2. CUDA Toolkit Supplement to Software License Agreement for +NVIDIA Software Development Kits +------------------------------------------------------------ + + +Release date: August 16, 2018 +----------------------------- + +The terms in this supplement govern your use of the NVIDIA +CUDA Toolkit SDK under the terms of your license agreement +(“Agreement”) as modified by this supplement. Capitalized +terms used but not defined below have the meaning assigned to +them in the Agreement. + +This supplement is an exhibit to the Agreement and is +incorporated as an integral part of the Agreement. In the +event of conflict between the terms in this supplement and the +terms in the Agreement, the terms in this supplement govern. + + +2.1. License Scope + +The SDK is licensed for you to develop applications only for +use in systems with NVIDIA GPUs. + + +2.2. Distribution + +The portions of the SDK that are distributable under the +Agreement are listed in Attachment A. + + +2.3. Operating Systems + +Those portions of the SDK designed exclusively for use on the +Linux or FreeBSD operating systems, or other operating systems +derived from the source code to these operating systems, may +be copied and redistributed for use in accordance with this +Agreement, provided that the object code files are not +modified in any way (except for unzipping of compressed +files). + + +2.4. Audio and Video Encoders and Decoders + +You acknowledge and agree that it is your sole responsibility +to obtain any additional third-party licenses required to +make, have made, use, have used, sell, import, and offer for +sale your products or services that include or incorporate any +third-party software and content relating to audio and/or +video encoders and decoders from, including but not limited +to, Microsoft, Thomson, Fraunhofer IIS, Sisvel S.p.A., +MPEG-LA, and Coding Technologies. NVIDIA does not grant to you +under this Agreement any necessary patent or other rights with +respect to any audio and/or video encoders and decoders. + + +2.5. Licensing + +If the distribution terms in this Agreement are not suitable +for your organization, or for any questions regarding this +Agreement, please contact NVIDIA at +nvidia-compute-license-questions@nvidia.com. + + +2.6. Attachment A + +The following portions of the SDK are distributable under the +Agreement: + +Component + +CUDA Runtime + +Windows + +cudart.dll, cudart_static.lib, cudadevrt.lib + +Mac OSX + +libcudart.dylib, libcudart_static.a, libcudadevrt.a + +Linux + +libcudart.so, libcudart_static.a, libcudadevrt.a + +Android + +libcudart.so, libcudart_static.a, libcudadevrt.a + +Component + +CUDA FFT Library + +Windows + +cufft.dll, cufftw.dll, cufft.lib, cufftw.lib + +Mac OSX + +libcufft.dylib, libcufft_static.a, libcufftw.dylib, +libcufftw_static.a + +Linux + +libcufft.so, libcufft_static.a, libcufftw.so, +libcufftw_static.a + +Android + +libcufft.so, libcufft_static.a, libcufftw.so, +libcufftw_static.a + +Component + +CUDA BLAS Library + +Windows + +cublas.dll, cublasLt.dll + +Mac OSX + +libcublas.dylib, libcublasLt.dylib, libcublas_static.a, +libcublasLt_static.a + +Linux + +libcublas.so, libcublasLt.so, libcublas_static.a, +libcublasLt_static.a + +Android + +libcublas.so, libcublasLt.so, libcublas_static.a, +libcublasLt_static.a + +Component + +NVIDIA "Drop-in" BLAS Library + +Windows + +nvblas.dll + +Mac OSX + +libnvblas.dylib + +Linux + +libnvblas.so + +Component + +CUDA Sparse Matrix Library + +Windows + +cusparse.dll, cusparse.lib + +Mac OSX + +libcusparse.dylib, libcusparse_static.a + +Linux + +libcusparse.so, libcusparse_static.a + +Android + +libcusparse.so, libcusparse_static.a + +Component + +CUDA Linear Solver Library + +Windows + +cusolver.dll, cusolver.lib + +Mac OSX + +libcusolver.dylib, libcusolver_static.a + +Linux + +libcusolver.so, libcusolver_static.a + +Android + +libcusolver.so, libcusolver_static.a + +Component + +CUDA Random Number Generation Library + +Windows + +curand.dll, curand.lib + +Mac OSX + +libcurand.dylib, libcurand_static.a + +Linux + +libcurand.so, libcurand_static.a + +Android + +libcurand.so, libcurand_static.a + +Component + +CUDA Accelerated Graph Library + +Component + +NVIDIA Performance Primitives Library + +Windows + +nppc.dll, nppc.lib, nppial.dll, nppial.lib, nppicc.dll, +nppicc.lib, nppicom.dll, nppicom.lib, nppidei.dll, +nppidei.lib, nppif.dll, nppif.lib, nppig.dll, nppig.lib, +nppim.dll, nppim.lib, nppist.dll, nppist.lib, nppisu.dll, +nppisu.lib, nppitc.dll, nppitc.lib, npps.dll, npps.lib + +Mac OSX + +libnppc.dylib, libnppc_static.a, libnppial.dylib, +libnppial_static.a, libnppicc.dylib, libnppicc_static.a, +libnppicom.dylib, libnppicom_static.a, libnppidei.dylib, +libnppidei_static.a, libnppif.dylib, libnppif_static.a, +libnppig.dylib, libnppig_static.a, libnppim.dylib, +libnppisu_static.a, libnppitc.dylib, libnppitc_static.a, +libnpps.dylib, libnpps_static.a + +Linux + +libnppc.so, libnppc_static.a, libnppial.so, +libnppial_static.a, libnppicc.so, libnppicc_static.a, +libnppicom.so, libnppicom_static.a, libnppidei.so, +libnppidei_static.a, libnppif.so, libnppif_static.a +libnppig.so, libnppig_static.a, libnppim.so, +libnppim_static.a, libnppist.so, libnppist_static.a, +libnppisu.so, libnppisu_static.a, libnppitc.so +libnppitc_static.a, libnpps.so, libnpps_static.a + +Android + +libnppc.so, libnppc_static.a, libnppial.so, +libnppial_static.a, libnppicc.so, libnppicc_static.a, +libnppicom.so, libnppicom_static.a, libnppidei.so, +libnppidei_static.a, libnppif.so, libnppif_static.a +libnppig.so, libnppig_static.a, libnppim.so, +libnppim_static.a, libnppist.so, libnppist_static.a, +libnppisu.so, libnppisu_static.a, libnppitc.so +libnppitc_static.a, libnpps.so, libnpps_static.a + +Component + +NVIDIA JPEG Library + +Linux + +libnvjpeg.so, libnvjpeg_static.a + +Component + +Internal common library required for statically linking to +cuBLAS, cuSPARSE, cuFFT, cuRAND, nvJPEG and NPP + +Mac OSX + +libculibos.a + +Linux + +libculibos.a + +Component + +NVIDIA Runtime Compilation Library and Header + +All + +nvrtc.h + +Windows + +nvrtc.dll, nvrtc-builtins.dll + +Mac OSX + +libnvrtc.dylib, libnvrtc-builtins.dylib + +Linux + +libnvrtc.so, libnvrtc-builtins.so + +Component + +NVIDIA Optimizing Compiler Library + +Windows + +nvvm.dll + +Mac OSX + +libnvvm.dylib + +Linux + +libnvvm.so + +Component + +NVIDIA Common Device Math Functions Library + +Windows + +libdevice.10.bc + +Mac OSX + +libdevice.10.bc + +Linux + +libdevice.10.bc + +Component + +CUDA Occupancy Calculation Header Library + +All + +cuda_occupancy.h + +Component + +CUDA Half Precision Headers + +All + +cuda_fp16.h, cuda_fp16.hpp + +Component + +CUDA Profiling Tools Interface (CUPTI) Library + +Windows + +cupti.dll + +Mac OSX + +libcupti.dylib + +Linux + +libcupti.so + +Component + +NVIDIA Tools Extension Library + +Windows + +nvToolsExt.dll, nvToolsExt.lib + +Mac OSX + +libnvToolsExt.dylib + +Linux + +libnvToolsExt.so + +Component + +NVIDIA CUDA Driver Libraries + +Linux + +libcuda.so, libnvidia-fatbinaryloader.so, +libnvidia-ptxjitcompiler.so + +The NVIDIA CUDA Driver Libraries are only distributable in +applications that meet this criteria: + + 1. The application was developed starting from a NVIDIA CUDA + container obtained from Docker Hub or the NVIDIA GPU + Cloud, and + + 2. The resulting application is packaged as a Docker + container and distributed to users on Docker Hub or the + NVIDIA GPU Cloud only. + + +2.7. Attachment B + + +Additional Licensing Obligations + +The following third party components included in the SOFTWARE +are licensed to Licensee pursuant to the following terms and +conditions: + + 1. Licensee's use of the GDB third party component is + subject to the terms and conditions of GNU GPL v3: + + This product includes copyrighted third-party software licensed + under the terms of the GNU General Public License v3 ("GPL v3"). + All third-party software packages are copyright by their respective + authors. GPL v3 terms and conditions are hereby incorporated into + the Agreement by this reference: http://www.gnu.org/licenses/gpl.txt + + Consistent with these licensing requirements, the software + listed below is provided under the terms of the specified + open source software licenses. To obtain source code for + software provided under licenses that require + redistribution of source code, including the GNU General + Public License (GPL) and GNU Lesser General Public License + (LGPL), contact oss-requests@nvidia.com. This offer is + valid for a period of three (3) years from the date of the + distribution of this product by NVIDIA CORPORATION. + + Component License + CUDA-GDB GPL v3 + + 2. Licensee represents and warrants that any and all third + party licensing and/or royalty payment obligations in + connection with Licensee's use of the H.264 video codecs + are solely the responsibility of Licensee. + + 3. Licensee's use of the Thrust library is subject to the + terms and conditions of the Apache License Version 2.0. + All third-party software packages are copyright by their + respective authors. Apache License Version 2.0 terms and + conditions are hereby incorporated into the Agreement by + this reference. + http://www.apache.org/licenses/LICENSE-2.0.html + + In addition, Licensee acknowledges the following notice: + Thrust includes source code from the Boost Iterator, + Tuple, System, and Random Number libraries. + + Boost Software License - Version 1.0 - August 17th, 2003 + . . . . + + Permission is hereby granted, free of charge, to any person or + organization obtaining a copy of the software and accompanying + documentation covered by this license (the "Software") to use, + reproduce, display, distribute, execute, and transmit the Software, + and to prepare derivative works of the Software, and to permit + third-parties to whom the Software is furnished to do so, all + subject to the following: + + The copyright notices in the Software and this entire statement, + including the above license grant, this restriction and the following + disclaimer, must be included in all copies of the Software, in whole + or in part, and all derivative works of the Software, unless such + copies or derivative works are solely in the form of machine-executable + object code generated by a source language processor. + + 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, TITLE AND + NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR + ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR + OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + 4. Licensee's use of the LLVM third party component is + subject to the following terms and conditions: + + ====================================================== + LLVM Release License + ====================================================== + University of Illinois/NCSA + Open Source License + + Copyright (c) 2003-2010 University of Illinois at Urbana-Champaign. + All rights reserved. + + Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal with 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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at Urbana- + Champaign, nor the names of its contributors may be used to endorse or + promote products derived from this Software without specific prior + written permission. + + 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 CONTRIBUTORS 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 WITH THE SOFTWARE. + + 5. Licensee's use (e.g. nvprof) of the PCRE third party + component is subject to the following terms and + conditions: + + ------------ + PCRE LICENCE + ------------ + PCRE is a library of functions to support regular expressions whose syntax + and semantics are as close as possible to those of the Perl 5 language. + Release 8 of PCRE is distributed under the terms of the "BSD" licence, as + specified below. The documentation for PCRE, supplied in the "doc" + directory, is distributed under the same terms as the software itself. The + basic library functions are written in C and are freestanding. Also + included in the distribution is a set of C++ wrapper functions, and a just- + in-time compiler that can be used to optimize pattern matching. These are + both optional features that can be omitted when the library is built. + + THE BASIC LIBRARY FUNCTIONS + --------------------------- + Written by: Philip Hazel + Email local part: ph10 + Email domain: cam.ac.uk + University of Cambridge Computing Service, + Cambridge, England. + Copyright (c) 1997-2012 University of Cambridge + All rights reserved. + + PCRE JUST-IN-TIME COMPILATION SUPPORT + ------------------------------------- + Written by: Zoltan Herczeg + Email local part: hzmester + Emain domain: freemail.hu + Copyright(c) 2010-2012 Zoltan Herczeg + All rights reserved. + + STACK-LESS JUST-IN-TIME COMPILER + -------------------------------- + Written by: Zoltan Herczeg + Email local part: hzmester + Emain domain: freemail.hu + Copyright(c) 2009-2012 Zoltan Herczeg + All rights reserved. + + THE C++ WRAPPER FUNCTIONS + ------------------------- + Contributed by: Google Inc. + Copyright (c) 2007-2012, Google Inc. + All rights reserved. + + THE "BSD" LICENCE + ----------------- + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the name of Google + Inc. nor the names of their 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. + + 6. Some of the cuBLAS library routines were written by or + derived from code written by Vasily Volkov and are subject + to the Modified Berkeley Software Distribution License as + follows: + + Copyright (c) 2007-2009, Regents of the University of California + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the University of California, Berkeley 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 AUTHOR "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 AUTHOR 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. + + 7. Some of the cuBLAS library routines were written by or + derived from code written by Davide Barbieri and are + subject to the Modified Berkeley Software Distribution + License as follows: + + Copyright (c) 2008-2009 Davide Barbieri @ University of Rome Tor Vergata. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * The name of the author may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "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 AUTHOR 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. + + 8. Some of the cuBLAS library routines were derived from + code developed by the University of Tennessee and are + subject to the Modified Berkeley Software Distribution + License as follows: + + Copyright (c) 2010 The University of Tennessee. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer listed in this license in the documentation and/or + other materials provided with the distribution. + * Neither the name of the copyright holders 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. + + 9. Some of the cuBLAS library routines were written by or + derived from code written by Jonathan Hogg and are subject + to the Modified Berkeley Software Distribution License as + follows: + + Copyright (c) 2012, The Science and Technology Facilities Council (STFC). + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the STFC 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 STFC 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. + + 10. Some of the cuBLAS library routines were written by or + derived from code written by Ahmad M. Abdelfattah, David + Keyes, and Hatem Ltaief, and are subject to the Apache + License, Version 2.0, as follows: + + -- (C) Copyright 2013 King Abdullah University of Science and Technology + Authors: + Ahmad Abdelfattah (ahmad.ahmad@kaust.edu.sa) + David Keyes (david.keyes@kaust.edu.sa) + Hatem Ltaief (hatem.ltaief@kaust.edu.sa) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the King Abdullah University of Science and + Technology 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 + HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + + 11. Some of the cuSPARSE library routines were written by or + derived from code written by Li-Wen Chang and are subject + to the NCSA Open Source License as follows: + + Copyright (c) 2012, University of Illinois. + + All rights reserved. + + Developed by: IMPACT Group, University of Illinois, http://impact.crhc.illinois.edu + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal with 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: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimers in the documentation and/or other materials provided + with the distribution. + * Neither the names of IMPACT Group, University of Illinois, nor + the names of its contributors may be used to endorse or promote + products derived from this Software without specific prior + written permission. + + 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 CONTRIBUTORS 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 WITH THE + SOFTWARE. + + 12. Some of the cuRAND library routines were written by or + derived from code written by Mutsuo Saito and Makoto + Matsumoto and are subject to the following license: + + Copyright (c) 2009, 2010 Mutsuo Saito, Makoto Matsumoto and Hiroshima + University. All rights reserved. + + Copyright (c) 2011 Mutsuo Saito, Makoto Matsumoto, Hiroshima + University and University of Tokyo. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the Hiroshima University 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. + + 13. Some of the cuRAND library routines were derived from + code developed by D. E. Shaw Research and are subject to + the following license: + + Copyright 2010-2011, D. E. Shaw Research. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions, and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of D. E. Shaw Research 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. + + 14. Some of the Math library routines were written by or + derived from code developed by Norbert Juffa and are + subject to the following license: + + Copyright (c) 2015-2017, Norbert Juffa + 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. + + 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. + + 15. Licensee's use of the lz4 third party component is + subject to the following terms and conditions: + + Copyright (C) 2011-2013, Yann Collet. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + 16. The NPP library uses code from the Boost Math Toolkit, + and is subject to the following license: + + Boost Software License - Version 1.0 - August 17th, 2003 + . . . . + + Permission is hereby granted, free of charge, to any person or + organization obtaining a copy of the software and accompanying + documentation covered by this license (the "Software") to use, + reproduce, display, distribute, execute, and transmit the Software, + and to prepare derivative works of the Software, and to permit + third-parties to whom the Software is furnished to do so, all + subject to the following: + + The copyright notices in the Software and this entire statement, + including the above license grant, this restriction and the following + disclaimer, must be included in all copies of the Software, in whole + or in part, and all derivative works of the Software, unless such + copies or derivative works are solely in the form of machine-executable + object code generated by a source language processor. + + 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, TITLE AND + NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR + ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR + OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + 17. Portions of the Nsight Eclipse Edition is subject to the + following license: + + The Eclipse Foundation makes available all content in this plug-in + ("Content"). Unless otherwise indicated below, the Content is provided + to you under the terms and conditions of the Eclipse Public License + Version 1.0 ("EPL"). A copy of the EPL is available at http:// + www.eclipse.org/legal/epl-v10.html. For purposes of the EPL, "Program" + will mean the Content. + + If you did not receive this Content directly from the Eclipse + Foundation, the Content is being redistributed by another party + ("Redistributor") and different terms and conditions may apply to your + use of any object code in the Content. Check the Redistributor's + license that was provided with the Content. If no such license exists, + contact the Redistributor. Unless otherwise indicated below, the terms + and conditions of the EPL still apply to any source code in the + Content and such source code may be obtained at http://www.eclipse.org. + + 18. Some of the cuBLAS library routines uses code from + OpenAI, which is subject to the following license: + + License URL + https://github.com/openai/openai-gemm/blob/master/LICENSE + + License Text + The MIT License + + Copyright (c) 2016 OpenAI (http://openai.com), 2016 Google Inc. + + 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. + + 19. Licensee's use of the Visual Studio Setup Configuration + Samples is subject to the following license: + + The MIT License (MIT) + Copyright (C) Microsoft Corporation. All rights reserved. + + 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. + + 20. Licensee's use of linmath.h header for CPU functions for + GL vector/matrix operations from lunarG is subject to the + Apache License Version 2.0. + + 21. The DX12-CUDA sample uses the d3dx12.h header, which is + subject to the MIT license . + +----------------- diff --git a/.venv/lib/python3.12/site-packages/nvidia_cufft_cu12-11.3.3.83.dist-info/METADATA b/.venv/lib/python3.12/site-packages/nvidia_cufft_cu12-11.3.3.83.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..82f85353688d5a5d26114a8ee58082974d0a0a6c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cufft_cu12-11.3.3.83.dist-info/METADATA @@ -0,0 +1,46 @@ +Metadata-Version: 2.2 +Name: nvidia-cufft-cu12 +Version: 11.3.3.83 +Summary: CUFFT native runtime libraries +Home-page: https://developer.nvidia.com/cuda-zone +Author: Nvidia CUDA Installer Team +Author-email: compute_installer@nvidia.com +License: NVIDIA Proprietary Software +Keywords: cuda,nvidia,runtime,machine learning,deep learning +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Education +Classifier: Intended Audience :: Science/Research +Classifier: License :: Other/Proprietary License +Classifier: Natural Language :: English +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Topic :: Scientific/Engineering +Classifier: Topic :: Scientific/Engineering :: Mathematics +Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence +Classifier: Topic :: Software Development +Classifier: Topic :: Software Development :: Libraries +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX :: Linux +Requires-Python: >=3 +License-File: License.txt +Requires-Dist: nvidia-nvjitlink-cu12 +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: home-page +Dynamic: keywords +Dynamic: license +Dynamic: requires-dist +Dynamic: requires-python +Dynamic: summary + +CUFFT native runtime libraries diff --git a/.venv/lib/python3.12/site-packages/nvidia_cufft_cu12-11.3.3.83.dist-info/RECORD b/.venv/lib/python3.12/site-packages/nvidia_cufft_cu12-11.3.3.83.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..e261a10553eca4dd6b64a7c545891d0283709903 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cufft_cu12-11.3.3.83.dist-info/RECORD @@ -0,0 +1,17 @@ +nvidia/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/cufft/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/cufft/include/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/cufft/include/cudalibxt.h,sha256=9GDuRiOzJuO61zRDhIpWpF7XHp8FXSOIlHJNoIMwOZQ,4105 +nvidia/cufft/include/cufft.h,sha256=KX0MfP_vIrghXh0nxaLf1Pxg2giCmX8sJaCkDbhtWoo,13169 +nvidia/cufft/include/cufftXt.h,sha256=R3MUFHMNqMae71HwFKPPcShI1HTJbHJYdrr-cRvqXe8,12945 +nvidia/cufft/include/cufftw.h,sha256=Uzfj1IVMlLQU_G50u84hXYX1K95HLXIwOcjQoAg5pGE,20051 +nvidia/cufft/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/cufft/lib/libcufft.so.11,sha256=XJEhRkSWFPnXPr0aXLYEJC2muBnZS6W0qZJyoGSfN2E,278925016 +nvidia/cufft/lib/libcufftw.so.11,sha256=D-TbBTPDF-Ie9zqmUp7F-vxu3qgKr8_xA-tY8AUSuOo,2101472 +nvidia_cufft_cu12-11.3.3.83.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +nvidia_cufft_cu12-11.3.3.83.dist-info/License.txt,sha256=rW9YU_ugyg0VnQ9Y1JrkmDDC-Mk_epJki5zpCttMbM0,59262 +nvidia_cufft_cu12-11.3.3.83.dist-info/METADATA,sha256=7RHSJzuY44l9yHroj816BmG4pMllKbk6XSJ5n9SJjeU,1741 +nvidia_cufft_cu12-11.3.3.83.dist-info/RECORD,, +nvidia_cufft_cu12-11.3.3.83.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia_cufft_cu12-11.3.3.83.dist-info/WHEEL,sha256=C-9JuWX7zPIcETbq0g2dQ83ne4_xEDQIPmC-cSMWixI,144 +nvidia_cufft_cu12-11.3.3.83.dist-info/top_level.txt,sha256=fTkAtiFuL16nUrB9ytDDtpytz2t0B4NvYTnRzwAhO14,7 diff --git a/.venv/lib/python3.12/site-packages/nvidia_cufft_cu12-11.3.3.83.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/nvidia_cufft_cu12-11.3.3.83.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/nvidia_cufft_cu12-11.3.3.83.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/nvidia_cufft_cu12-11.3.3.83.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..04b8de3f7d6f51022dab208980b75eabd347388a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cufft_cu12-11.3.3.83.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.8.2) +Root-Is-Purelib: true +Tag: py3-none-manylinux2014_x86_64 +Tag: py3-none-manylinux_2_17_x86_64 + diff --git a/.venv/lib/python3.12/site-packages/nvidia_cufft_cu12-11.3.3.83.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/nvidia_cufft_cu12-11.3.3.83.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..862f7abf232cdfbb928609856247292e81c9decb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cufft_cu12-11.3.3.83.dist-info/top_level.txt @@ -0,0 +1 @@ +nvidia diff --git a/.venv/lib/python3.12/site-packages/nvidia_cufile_cu12-1.13.1.3.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/nvidia_cufile_cu12-1.13.1.3.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cufile_cu12-1.13.1.3.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/nvidia_cufile_cu12-1.13.1.3.dist-info/License.txt b/.venv/lib/python3.12/site-packages/nvidia_cufile_cu12-1.13.1.3.dist-info/License.txt new file mode 100644 index 0000000000000000000000000000000000000000..b491c70e0aef319022ded661e111ddbd45b8a17f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cufile_cu12-1.13.1.3.dist-info/License.txt @@ -0,0 +1,1568 @@ +End User License Agreement +-------------------------- + + +Preface +------- + +The Software License Agreement in Chapter 1 and the Supplement +in Chapter 2 contain license terms and conditions that govern +the use of NVIDIA software. By accepting this agreement, you +agree to comply with all the terms and conditions applicable +to the product(s) included herein. + + +NVIDIA Driver + + +Description + +This package contains the operating system driver and +fundamental system software components for NVIDIA GPUs. + + +NVIDIA CUDA Toolkit + + +Description + +The NVIDIA CUDA Toolkit provides command-line and graphical +tools for building, debugging and optimizing the performance +of applications accelerated by NVIDIA GPUs, runtime and math +libraries, and documentation including programming guides, +user manuals, and API references. + + +Default Install Location of CUDA Toolkit + +Windows platform: + +%ProgramFiles%\NVIDIA GPU Computing Toolkit\CUDA\v#.# + +Linux platform: + +/usr/local/cuda-#.# + +Mac platform: + +/Developer/NVIDIA/CUDA-#.# + + +NVIDIA CUDA Samples + + +Description + +This package includes over 100+ CUDA examples that demonstrate +various CUDA programming principles, and efficient CUDA +implementation of algorithms in specific application domains. + + +Default Install Location of CUDA Samples + +Windows platform: + +%ProgramData%\NVIDIA Corporation\CUDA Samples\v#.# + +Linux platform: + +/usr/local/cuda-#.#/samples + +and + +$HOME/NVIDIA_CUDA-#.#_Samples + +Mac platform: + +/Developer/NVIDIA/CUDA-#.#/samples + + +NVIDIA Nsight Visual Studio Edition (Windows only) + + +Description + +NVIDIA Nsight Development Platform, Visual Studio Edition is a +development environment integrated into Microsoft Visual +Studio that provides tools for debugging, profiling, analyzing +and optimizing your GPU computing and graphics applications. + + +Default Install Location of Nsight Visual Studio Edition + +Windows platform: + +%ProgramFiles(x86)%\NVIDIA Corporation\Nsight Visual Studio Edition #.# + + +1. License Agreement for NVIDIA Software Development Kits +--------------------------------------------------------- + + +Release Date: July 26, 2018 +--------------------------- + + +Important NoticeRead before downloading, installing, +copying or using the licensed software: +------------------------------------------------------- + +This license agreement, including exhibits attached +("Agreement”) is a legal agreement between you and NVIDIA +Corporation ("NVIDIA") and governs your use of a NVIDIA +software development kit (“SDK”). + +Each SDK has its own set of software and materials, but here +is a description of the types of items that may be included in +a SDK: source code, header files, APIs, data sets and assets +(examples include images, textures, models, scenes, videos, +native API input/output files), binary software, sample code, +libraries, utility programs, programming code and +documentation. + +This Agreement can be accepted only by an adult of legal age +of majority in the country in which the SDK is used. + +If you are entering into this Agreement on behalf of a company +or other legal entity, you represent that you have the legal +authority to bind the entity to this Agreement, in which case +“you” will mean the entity you represent. + +If you don’t have the required age or authority to accept +this Agreement, or if you don’t accept all the terms and +conditions of this Agreement, do not download, install or use +the SDK. + +You agree to use the SDK only for purposes that are permitted +by (a) this Agreement, and (b) any applicable law, regulation +or generally accepted practices or guidelines in the relevant +jurisdictions. + + +1.1. License + + +1.1.1. License Grant + +Subject to the terms of this Agreement, NVIDIA hereby grants +you a non-exclusive, non-transferable license, without the +right to sublicense (except as expressly provided in this +Agreement) to: + + 1. Install and use the SDK, + + 2. Modify and create derivative works of sample source code + delivered in the SDK, and + + 3. Distribute those portions of the SDK that are identified + in this Agreement as distributable, as incorporated in + object code format into a software application that meets + the distribution requirements indicated in this Agreement. + + +1.1.2. Distribution Requirements + +These are the distribution requirements for you to exercise +the distribution grant: + + 1. Your application must have material additional + functionality, beyond the included portions of the SDK. + + 2. The distributable portions of the SDK shall only be + accessed by your application. + + 3. The following notice shall be included in modifications + and derivative works of sample source code distributed: + “This software contains source code provided by NVIDIA + Corporation.” + + 4. Unless a developer tool is identified in this Agreement + as distributable, it is delivered for your internal use + only. + + 5. The terms under which you distribute your application + must be consistent with the terms of this Agreement, + including (without limitation) terms relating to the + license grant and license restrictions and protection of + NVIDIA’s intellectual property rights. Additionally, you + agree that you will protect the privacy, security and + legal rights of your application users. + + 6. You agree to notify NVIDIA in writing of any known or + suspected distribution or use of the SDK not in compliance + with the requirements of this Agreement, and to enforce + the terms of your agreements with respect to distributed + SDK. + + +1.1.3. Authorized Users + +You may allow employees and contractors of your entity or of +your subsidiary(ies) to access and use the SDK from your +secure network to perform work on your behalf. + +If you are an academic institution you may allow users +enrolled or employed by the academic institution to access and +use the SDK from your secure network. + +You are responsible for the compliance with the terms of this +Agreement by your authorized users. If you become aware that +your authorized users didn’t follow the terms of this +Agreement, you agree to take reasonable steps to resolve the +non-compliance and prevent new occurrences. + + +1.1.4. Pre-Release SDK + +The SDK versions identified as alpha, beta, preview or +otherwise as pre-release, may not be fully functional, may +contain errors or design flaws, and may have reduced or +different security, privacy, accessibility, availability, and +reliability standards relative to commercial versions of +NVIDIA software and materials. Use of a pre-release SDK may +result in unexpected results, loss of data, project delays or +other unpredictable damage or loss. + +You may use a pre-release SDK at your own risk, understanding +that pre-release SDKs are not intended for use in production +or business-critical systems. + +NVIDIA may choose not to make available a commercial version +of any pre-release SDK. NVIDIA may also choose to abandon +development and terminate the availability of a pre-release +SDK at any time without liability. + + +1.1.5. Updates + +NVIDIA may, at its option, make available patches, workarounds +or other updates to this SDK. Unless the updates are provided +with their separate governing terms, they are deemed part of +the SDK licensed to you as provided in this Agreement. You +agree that the form and content of the SDK that NVIDIA +provides may change without prior notice to you. While NVIDIA +generally maintains compatibility between versions, NVIDIA may +in some cases make changes that introduce incompatibilities in +future versions of the SDK. + + +1.1.6. Third Party Licenses + +The SDK may come bundled with, or otherwise include or be +distributed with, third party software licensed by a NVIDIA +supplier and/or open source software provided under an open +source license. Use of third party software is subject to the +third-party license terms, or in the absence of third party +terms, the terms of this Agreement. Copyright to third party +software is held by the copyright holders indicated in the +third-party software or license. + + +1.1.7. Reservation of Rights + +NVIDIA reserves all rights, title, and interest in and to the +SDK, not expressly granted to you under this Agreement. + + +1.2. Limitations + +The following license limitations apply to your use of the +SDK: + + 1. You may not reverse engineer, decompile or disassemble, + or remove copyright or other proprietary notices from any + portion of the SDK or copies of the SDK. + + 2. Except as expressly provided in this Agreement, you may + not copy, sell, rent, sublicense, transfer, distribute, + modify, or create derivative works of any portion of the + SDK. For clarity, you may not distribute or sublicense the + SDK as a stand-alone product. + + 3. Unless you have an agreement with NVIDIA for this + purpose, you may not indicate that an application created + with the SDK is sponsored or endorsed by NVIDIA. + + 4. You may not bypass, disable, or circumvent any + encryption, security, digital rights management or + authentication mechanism in the SDK. + + 5. You may not use the SDK in any manner that would cause it + to become subject to an open source software license. As + examples, licenses that require as a condition of use, + modification, and/or distribution that the SDK be: + + a. Disclosed or distributed in source code form; + + b. Licensed for the purpose of making derivative works; + or + + c. Redistributable at no charge. + + 6. Unless you have an agreement with NVIDIA for this + purpose, you may not use the SDK with any system or + application where the use or failure of the system or + application can reasonably be expected to threaten or + result in personal injury, death, or catastrophic loss. + Examples include use in avionics, navigation, military, + medical, life support or other life critical applications. + NVIDIA does not design, test or manufacture the SDK for + these critical uses and NVIDIA shall not be liable to you + or any third party, in whole or in part, for any claims or + damages arising from such uses. + + 7. You agree to defend, indemnify and hold harmless NVIDIA + and its affiliates, and their respective employees, + contractors, agents, officers and directors, from and + against any and all claims, damages, obligations, losses, + liabilities, costs or debt, fines, restitutions and + expenses (including but not limited to attorney’s fees + and costs incident to establishing the right of + indemnification) arising out of or related to your use of + the SDK outside of the scope of this Agreement, or not in + compliance with its terms. + + +1.3. Ownership + + 1. NVIDIA or its licensors hold all rights, title and + interest in and to the SDK and its modifications and + derivative works, including their respective intellectual + property rights, subject to your rights described in this + section. This SDK may include software and materials from + NVIDIA’s licensors, and these licensors are intended + third party beneficiaries that may enforce this Agreement + with respect to their intellectual property rights. + + 2. You hold all rights, title and interest in and to your + applications and your derivative works of the sample + source code delivered in the SDK, including their + respective intellectual property rights, subject to + NVIDIA’s rights described in this section. + + 3. You may, but don’t have to, provide to NVIDIA + suggestions, feature requests or other feedback regarding + the SDK, including possible enhancements or modifications + to the SDK. For any feedback that you voluntarily provide, + you hereby grant NVIDIA and its affiliates a perpetual, + non-exclusive, worldwide, irrevocable license to use, + reproduce, modify, license, sublicense (through multiple + tiers of sublicensees), and distribute (through multiple + tiers of distributors) it without the payment of any + royalties or fees to you. NVIDIA will use feedback at its + choice. NVIDIA is constantly looking for ways to improve + its products, so you may send feedback to NVIDIA through + the developer portal at https://developer.nvidia.com. + + +1.4. No Warranties + +THE SDK IS PROVIDED BY NVIDIA “AS IS” AND “WITH ALL +FAULTS.” TO THE MAXIMUM EXTENT PERMITTED BY LAW, NVIDIA AND +ITS AFFILIATES EXPRESSLY DISCLAIM ALL WARRANTIES OF ANY KIND +OR NATURE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, +BUT NOT LIMITED TO, ANY WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE, TITLE, NON-INFRINGEMENT, OR THE +ABSENCE OF ANY DEFECTS THEREIN, WHETHER LATENT OR PATENT. NO +WARRANTY IS MADE ON THE BASIS OF TRADE USAGE, COURSE OF +DEALING OR COURSE OF TRADE. + + +1.5. Limitation of Liability + +TO THE MAXIMUM EXTENT PERMITTED BY LAW, NVIDIA AND ITS +AFFILIATES SHALL NOT BE LIABLE FOR ANY SPECIAL, INCIDENTAL, +PUNITIVE OR CONSEQUENTIAL DAMAGES, OR ANY LOST PROFITS, LOSS +OF USE, LOSS OF DATA OR LOSS OF GOODWILL, OR THE COSTS OF +PROCURING SUBSTITUTE PRODUCTS, ARISING OUT OF OR IN CONNECTION +WITH THIS AGREEMENT OR THE USE OR PERFORMANCE OF THE SDK, +WHETHER SUCH LIABILITY ARISES FROM ANY CLAIM BASED UPON BREACH +OF CONTRACT, BREACH OF WARRANTY, TORT (INCLUDING NEGLIGENCE), +PRODUCT LIABILITY OR ANY OTHER CAUSE OF ACTION OR THEORY OF +LIABILITY. IN NO EVENT WILL NVIDIA’S AND ITS AFFILIATES +TOTAL CUMULATIVE LIABILITY UNDER OR ARISING OUT OF THIS +AGREEMENT EXCEED US$10.00. THE NATURE OF THE LIABILITY OR THE +NUMBER OF CLAIMS OR SUITS SHALL NOT ENLARGE OR EXTEND THIS +LIMIT. + +These exclusions and limitations of liability shall apply +regardless if NVIDIA or its affiliates have been advised of +the possibility of such damages, and regardless of whether a +remedy fails its essential purpose. These exclusions and +limitations of liability form an essential basis of the +bargain between the parties, and, absent any of these +exclusions or limitations of liability, the provisions of this +Agreement, including, without limitation, the economic terms, +would be substantially different. + + +1.6. Termination + + 1. This Agreement will continue to apply until terminated by + either you or NVIDIA as described below. + + 2. If you want to terminate this Agreement, you may do so by + stopping to use the SDK. + + 3. NVIDIA may, at any time, terminate this Agreement if: + + a. (i) you fail to comply with any term of this + Agreement and the non-compliance is not fixed within + thirty (30) days following notice from NVIDIA (or + immediately if you violate NVIDIA’s intellectual + property rights); + + b. (ii) you commence or participate in any legal + proceeding against NVIDIA with respect to the SDK; or + + c. (iii) NVIDIA decides to no longer provide the SDK in + a country or, in NVIDIA’s sole discretion, the + continued use of it is no longer commercially viable. + + 4. Upon any termination of this Agreement, you agree to + promptly discontinue use of the SDK and destroy all copies + in your possession or control. Your prior distributions in + accordance with this Agreement are not affected by the + termination of this Agreement. Upon written request, you + will certify in writing that you have complied with your + commitments under this section. Upon any termination of + this Agreement all provisions survive except for the + license grant provisions. + + +1.7. General + +If you wish to assign this Agreement or your rights and +obligations, including by merger, consolidation, dissolution +or operation of law, contact NVIDIA to ask for permission. Any +attempted assignment not approved by NVIDIA in writing shall +be void and of no effect. NVIDIA may assign, delegate or +transfer this Agreement and its rights and obligations, and if +to a non-affiliate you will be notified. + +You agree to cooperate with NVIDIA and provide reasonably +requested information to verify your compliance with this +Agreement. + +This Agreement will be governed in all respects by the laws of +the United States and of the State of Delaware as those laws +are applied to contracts entered into and performed entirely +within Delaware by Delaware residents, without regard to the +conflicts of laws principles. The United Nations Convention on +Contracts for the International Sale of Goods is specifically +disclaimed. You agree to all terms of this Agreement in the +English language. + +The state or federal courts residing in Santa Clara County, +California shall have exclusive jurisdiction over any dispute +or claim arising out of this Agreement. Notwithstanding this, +you agree that NVIDIA shall still be allowed to apply for +injunctive remedies or an equivalent type of urgent legal +relief in any jurisdiction. + +If any court of competent jurisdiction determines that any +provision of this Agreement is illegal, invalid or +unenforceable, such provision will be construed as limited to +the extent necessary to be consistent with and fully +enforceable under the law and the remaining provisions will +remain in full force and effect. Unless otherwise specified, +remedies are cumulative. + +Each party acknowledges and agrees that the other is an +independent contractor in the performance of this Agreement. + +The SDK has been developed entirely at private expense and is +“commercial items” consisting of “commercial computer +software” and “commercial computer software +documentation” provided with RESTRICTED RIGHTS. Use, +duplication or disclosure by the U.S. Government or a U.S. +Government subcontractor is subject to the restrictions in +this Agreement pursuant to DFARS 227.7202-3(a) or as set forth +in subparagraphs (c)(1) and (2) of the Commercial Computer +Software - Restricted Rights clause at FAR 52.227-19, as +applicable. Contractor/manufacturer is NVIDIA, 2788 San Tomas +Expressway, Santa Clara, CA 95051. + +The SDK is subject to United States export laws and +regulations. You agree that you will not ship, transfer or +export the SDK into any country, or use the SDK in any manner, +prohibited by the United States Bureau of Industry and +Security or economic sanctions regulations administered by the +U.S. Department of Treasury’s Office of Foreign Assets +Control (OFAC), or any applicable export laws, restrictions or +regulations. These laws include restrictions on destinations, +end users and end use. By accepting this Agreement, you +confirm that you are not a resident or citizen of any country +currently embargoed by the U.S. and that you are not otherwise +prohibited from receiving the SDK. + +Any notice delivered by NVIDIA to you under this Agreement +will be delivered via mail, email or fax. You agree that any +notices that NVIDIA sends you electronically will satisfy any +legal communication requirements. Please direct your legal +notices or other correspondence to NVIDIA Corporation, 2788 +San Tomas Expressway, Santa Clara, California 95051, United +States of America, Attention: Legal Department. + +This Agreement and any exhibits incorporated into this +Agreement constitute the entire agreement of the parties with +respect to the subject matter of this Agreement and supersede +all prior negotiations or documentation exchanged between the +parties relating to this SDK license. Any additional and/or +conflicting terms on documents issued by you are null, void, +and invalid. Any amendment or waiver under this Agreement +shall be in writing and signed by representatives of both +parties. + + +2. CUDA Toolkit Supplement to Software License Agreement for +NVIDIA Software Development Kits +------------------------------------------------------------ + + +Release date: August 16, 2018 +----------------------------- + +The terms in this supplement govern your use of the NVIDIA +CUDA Toolkit SDK under the terms of your license agreement +(“Agreement”) as modified by this supplement. Capitalized +terms used but not defined below have the meaning assigned to +them in the Agreement. + +This supplement is an exhibit to the Agreement and is +incorporated as an integral part of the Agreement. In the +event of conflict between the terms in this supplement and the +terms in the Agreement, the terms in this supplement govern. + + +2.1. License Scope + +The SDK is licensed for you to develop applications only for +use in systems with NVIDIA GPUs. + + +2.2. Distribution + +The portions of the SDK that are distributable under the +Agreement are listed in Attachment A. + + +2.3. Operating Systems + +Those portions of the SDK designed exclusively for use on the +Linux or FreeBSD operating systems, or other operating systems +derived from the source code to these operating systems, may +be copied and redistributed for use in accordance with this +Agreement, provided that the object code files are not +modified in any way (except for unzipping of compressed +files). + + +2.4. Audio and Video Encoders and Decoders + +You acknowledge and agree that it is your sole responsibility +to obtain any additional third-party licenses required to +make, have made, use, have used, sell, import, and offer for +sale your products or services that include or incorporate any +third-party software and content relating to audio and/or +video encoders and decoders from, including but not limited +to, Microsoft, Thomson, Fraunhofer IIS, Sisvel S.p.A., +MPEG-LA, and Coding Technologies. NVIDIA does not grant to you +under this Agreement any necessary patent or other rights with +respect to any audio and/or video encoders and decoders. + + +2.5. Licensing + +If the distribution terms in this Agreement are not suitable +for your organization, or for any questions regarding this +Agreement, please contact NVIDIA at +nvidia-compute-license-questions@nvidia.com. + + +2.6. Attachment A + +The following portions of the SDK are distributable under the +Agreement: + +Component + +CUDA Runtime + +Windows + +cudart.dll, cudart_static.lib, cudadevrt.lib + +Mac OSX + +libcudart.dylib, libcudart_static.a, libcudadevrt.a + +Linux + +libcudart.so, libcudart_static.a, libcudadevrt.a + +Android + +libcudart.so, libcudart_static.a, libcudadevrt.a + +Component + +CUDA FFT Library + +Windows + +cufft.dll, cufftw.dll, cufft.lib, cufftw.lib + +Mac OSX + +libcufft.dylib, libcufft_static.a, libcufftw.dylib, +libcufftw_static.a + +Linux + +libcufft.so, libcufft_static.a, libcufftw.so, +libcufftw_static.a + +Android + +libcufft.so, libcufft_static.a, libcufftw.so, +libcufftw_static.a + +Component + +CUDA BLAS Library + +Windows + +cublas.dll, cublasLt.dll + +Mac OSX + +libcublas.dylib, libcublasLt.dylib, libcublas_static.a, +libcublasLt_static.a + +Linux + +libcublas.so, libcublasLt.so, libcublas_static.a, +libcublasLt_static.a + +Android + +libcublas.so, libcublasLt.so, libcublas_static.a, +libcublasLt_static.a + +Component + +NVIDIA "Drop-in" BLAS Library + +Windows + +nvblas.dll + +Mac OSX + +libnvblas.dylib + +Linux + +libnvblas.so + +Component + +CUDA Sparse Matrix Library + +Windows + +cusparse.dll, cusparse.lib + +Mac OSX + +libcusparse.dylib, libcusparse_static.a + +Linux + +libcusparse.so, libcusparse_static.a + +Android + +libcusparse.so, libcusparse_static.a + +Component + +CUDA Linear Solver Library + +Windows + +cusolver.dll, cusolver.lib + +Mac OSX + +libcusolver.dylib, libcusolver_static.a + +Linux + +libcusolver.so, libcusolver_static.a + +Android + +libcusolver.so, libcusolver_static.a + +Component + +CUDA Random Number Generation Library + +Windows + +curand.dll, curand.lib + +Mac OSX + +libcurand.dylib, libcurand_static.a + +Linux + +libcurand.so, libcurand_static.a + +Android + +libcurand.so, libcurand_static.a + +Component + +CUDA Accelerated Graph Library + +Component + +NVIDIA Performance Primitives Library + +Windows + +nppc.dll, nppc.lib, nppial.dll, nppial.lib, nppicc.dll, +nppicc.lib, nppicom.dll, nppicom.lib, nppidei.dll, +nppidei.lib, nppif.dll, nppif.lib, nppig.dll, nppig.lib, +nppim.dll, nppim.lib, nppist.dll, nppist.lib, nppisu.dll, +nppisu.lib, nppitc.dll, nppitc.lib, npps.dll, npps.lib + +Mac OSX + +libnppc.dylib, libnppc_static.a, libnppial.dylib, +libnppial_static.a, libnppicc.dylib, libnppicc_static.a, +libnppicom.dylib, libnppicom_static.a, libnppidei.dylib, +libnppidei_static.a, libnppif.dylib, libnppif_static.a, +libnppig.dylib, libnppig_static.a, libnppim.dylib, +libnppisu_static.a, libnppitc.dylib, libnppitc_static.a, +libnpps.dylib, libnpps_static.a + +Linux + +libnppc.so, libnppc_static.a, libnppial.so, +libnppial_static.a, libnppicc.so, libnppicc_static.a, +libnppicom.so, libnppicom_static.a, libnppidei.so, +libnppidei_static.a, libnppif.so, libnppif_static.a +libnppig.so, libnppig_static.a, libnppim.so, +libnppim_static.a, libnppist.so, libnppist_static.a, +libnppisu.so, libnppisu_static.a, libnppitc.so +libnppitc_static.a, libnpps.so, libnpps_static.a + +Android + +libnppc.so, libnppc_static.a, libnppial.so, +libnppial_static.a, libnppicc.so, libnppicc_static.a, +libnppicom.so, libnppicom_static.a, libnppidei.so, +libnppidei_static.a, libnppif.so, libnppif_static.a +libnppig.so, libnppig_static.a, libnppim.so, +libnppim_static.a, libnppist.so, libnppist_static.a, +libnppisu.so, libnppisu_static.a, libnppitc.so +libnppitc_static.a, libnpps.so, libnpps_static.a + +Component + +NVIDIA JPEG Library + +Linux + +libnvjpeg.so, libnvjpeg_static.a + +Component + +Internal common library required for statically linking to +cuBLAS, cuSPARSE, cuFFT, cuRAND, nvJPEG and NPP + +Mac OSX + +libculibos.a + +Linux + +libculibos.a + +Component + +NVIDIA Runtime Compilation Library and Header + +All + +nvrtc.h + +Windows + +nvrtc.dll, nvrtc-builtins.dll + +Mac OSX + +libnvrtc.dylib, libnvrtc-builtins.dylib + +Linux + +libnvrtc.so, libnvrtc-builtins.so + +Component + +NVIDIA Optimizing Compiler Library + +Windows + +nvvm.dll + +Mac OSX + +libnvvm.dylib + +Linux + +libnvvm.so + +Component + +NVIDIA Common Device Math Functions Library + +Windows + +libdevice.10.bc + +Mac OSX + +libdevice.10.bc + +Linux + +libdevice.10.bc + +Component + +CUDA Occupancy Calculation Header Library + +All + +cuda_occupancy.h + +Component + +CUDA Half Precision Headers + +All + +cuda_fp16.h, cuda_fp16.hpp + +Component + +CUDA Profiling Tools Interface (CUPTI) Library + +Windows + +cupti.dll + +Mac OSX + +libcupti.dylib + +Linux + +libcupti.so + +Component + +NVIDIA Tools Extension Library + +Windows + +nvToolsExt.dll, nvToolsExt.lib + +Mac OSX + +libnvToolsExt.dylib + +Linux + +libnvToolsExt.so + +Component + +NVIDIA CUDA Driver Libraries + +Linux + +libcuda.so, libnvidia-fatbinaryloader.so, +libnvidia-ptxjitcompiler.so + +The NVIDIA CUDA Driver Libraries are only distributable in +applications that meet this criteria: + + 1. The application was developed starting from a NVIDIA CUDA + container obtained from Docker Hub or the NVIDIA GPU + Cloud, and + + 2. The resulting application is packaged as a Docker + container and distributed to users on Docker Hub or the + NVIDIA GPU Cloud only. + + +2.7. Attachment B + + +Additional Licensing Obligations + +The following third party components included in the SOFTWARE +are licensed to Licensee pursuant to the following terms and +conditions: + + 1. Licensee's use of the GDB third party component is + subject to the terms and conditions of GNU GPL v3: + + This product includes copyrighted third-party software licensed + under the terms of the GNU General Public License v3 ("GPL v3"). + All third-party software packages are copyright by their respective + authors. GPL v3 terms and conditions are hereby incorporated into + the Agreement by this reference: http://www.gnu.org/licenses/gpl.txt + + Consistent with these licensing requirements, the software + listed below is provided under the terms of the specified + open source software licenses. To obtain source code for + software provided under licenses that require + redistribution of source code, including the GNU General + Public License (GPL) and GNU Lesser General Public License + (LGPL), contact oss-requests@nvidia.com. This offer is + valid for a period of three (3) years from the date of the + distribution of this product by NVIDIA CORPORATION. + + Component License + CUDA-GDB GPL v3 + + 2. Licensee represents and warrants that any and all third + party licensing and/or royalty payment obligations in + connection with Licensee's use of the H.264 video codecs + are solely the responsibility of Licensee. + + 3. Licensee's use of the Thrust library is subject to the + terms and conditions of the Apache License Version 2.0. + All third-party software packages are copyright by their + respective authors. Apache License Version 2.0 terms and + conditions are hereby incorporated into the Agreement by + this reference. + http://www.apache.org/licenses/LICENSE-2.0.html + + In addition, Licensee acknowledges the following notice: + Thrust includes source code from the Boost Iterator, + Tuple, System, and Random Number libraries. + + Boost Software License - Version 1.0 - August 17th, 2003 + . . . . + + Permission is hereby granted, free of charge, to any person or + organization obtaining a copy of the software and accompanying + documentation covered by this license (the "Software") to use, + reproduce, display, distribute, execute, and transmit the Software, + and to prepare derivative works of the Software, and to permit + third-parties to whom the Software is furnished to do so, all + subject to the following: + + The copyright notices in the Software and this entire statement, + including the above license grant, this restriction and the following + disclaimer, must be included in all copies of the Software, in whole + or in part, and all derivative works of the Software, unless such + copies or derivative works are solely in the form of machine-executable + object code generated by a source language processor. + + 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, TITLE AND + NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR + ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR + OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + 4. Licensee's use of the LLVM third party component is + subject to the following terms and conditions: + + ====================================================== + LLVM Release License + ====================================================== + University of Illinois/NCSA + Open Source License + + Copyright (c) 2003-2010 University of Illinois at Urbana-Champaign. + All rights reserved. + + Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal with 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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at Urbana- + Champaign, nor the names of its contributors may be used to endorse or + promote products derived from this Software without specific prior + written permission. + + 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 CONTRIBUTORS 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 WITH THE SOFTWARE. + + 5. Licensee's use (e.g. nvprof) of the PCRE third party + component is subject to the following terms and + conditions: + + ------------ + PCRE LICENCE + ------------ + PCRE is a library of functions to support regular expressions whose syntax + and semantics are as close as possible to those of the Perl 5 language. + Release 8 of PCRE is distributed under the terms of the "BSD" licence, as + specified below. The documentation for PCRE, supplied in the "doc" + directory, is distributed under the same terms as the software itself. The + basic library functions are written in C and are freestanding. Also + included in the distribution is a set of C++ wrapper functions, and a just- + in-time compiler that can be used to optimize pattern matching. These are + both optional features that can be omitted when the library is built. + + THE BASIC LIBRARY FUNCTIONS + --------------------------- + Written by: Philip Hazel + Email local part: ph10 + Email domain: cam.ac.uk + University of Cambridge Computing Service, + Cambridge, England. + Copyright (c) 1997-2012 University of Cambridge + All rights reserved. + + PCRE JUST-IN-TIME COMPILATION SUPPORT + ------------------------------------- + Written by: Zoltan Herczeg + Email local part: hzmester + Emain domain: freemail.hu + Copyright(c) 2010-2012 Zoltan Herczeg + All rights reserved. + + STACK-LESS JUST-IN-TIME COMPILER + -------------------------------- + Written by: Zoltan Herczeg + Email local part: hzmester + Emain domain: freemail.hu + Copyright(c) 2009-2012 Zoltan Herczeg + All rights reserved. + + THE C++ WRAPPER FUNCTIONS + ------------------------- + Contributed by: Google Inc. + Copyright (c) 2007-2012, Google Inc. + All rights reserved. + + THE "BSD" LICENCE + ----------------- + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the name of Google + Inc. nor the names of their 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. + + 6. Some of the cuBLAS library routines were written by or + derived from code written by Vasily Volkov and are subject + to the Modified Berkeley Software Distribution License as + follows: + + Copyright (c) 2007-2009, Regents of the University of California + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the University of California, Berkeley 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 AUTHOR "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 AUTHOR 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. + + 7. Some of the cuBLAS library routines were written by or + derived from code written by Davide Barbieri and are + subject to the Modified Berkeley Software Distribution + License as follows: + + Copyright (c) 2008-2009 Davide Barbieri @ University of Rome Tor Vergata. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * The name of the author may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "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 AUTHOR 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. + + 8. Some of the cuBLAS library routines were derived from + code developed by the University of Tennessee and are + subject to the Modified Berkeley Software Distribution + License as follows: + + Copyright (c) 2010 The University of Tennessee. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer listed in this license in the documentation and/or + other materials provided with the distribution. + * Neither the name of the copyright holders 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. + + 9. Some of the cuBLAS library routines were written by or + derived from code written by Jonathan Hogg and are subject + to the Modified Berkeley Software Distribution License as + follows: + + Copyright (c) 2012, The Science and Technology Facilities Council (STFC). + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the STFC 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 STFC 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. + + 10. Some of the cuBLAS library routines were written by or + derived from code written by Ahmad M. Abdelfattah, David + Keyes, and Hatem Ltaief, and are subject to the Apache + License, Version 2.0, as follows: + + -- (C) Copyright 2013 King Abdullah University of Science and Technology + Authors: + Ahmad Abdelfattah (ahmad.ahmad@kaust.edu.sa) + David Keyes (david.keyes@kaust.edu.sa) + Hatem Ltaief (hatem.ltaief@kaust.edu.sa) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the King Abdullah University of Science and + Technology 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 + HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + + 11. Some of the cuSPARSE library routines were written by or + derived from code written by Li-Wen Chang and are subject + to the NCSA Open Source License as follows: + + Copyright (c) 2012, University of Illinois. + + All rights reserved. + + Developed by: IMPACT Group, University of Illinois, http://impact.crhc.illinois.edu + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal with 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: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimers in the documentation and/or other materials provided + with the distribution. + * Neither the names of IMPACT Group, University of Illinois, nor + the names of its contributors may be used to endorse or promote + products derived from this Software without specific prior + written permission. + + 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 CONTRIBUTORS 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 WITH THE + SOFTWARE. + + 12. Some of the cuRAND library routines were written by or + derived from code written by Mutsuo Saito and Makoto + Matsumoto and are subject to the following license: + + Copyright (c) 2009, 2010 Mutsuo Saito, Makoto Matsumoto and Hiroshima + University. All rights reserved. + + Copyright (c) 2011 Mutsuo Saito, Makoto Matsumoto, Hiroshima + University and University of Tokyo. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the Hiroshima University 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. + + 13. Some of the cuRAND library routines were derived from + code developed by D. E. Shaw Research and are subject to + the following license: + + Copyright 2010-2011, D. E. Shaw Research. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions, and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of D. E. Shaw Research 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. + + 14. Some of the Math library routines were written by or + derived from code developed by Norbert Juffa and are + subject to the following license: + + Copyright (c) 2015-2017, Norbert Juffa + 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. + + 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. + + 15. Licensee's use of the lz4 third party component is + subject to the following terms and conditions: + + Copyright (C) 2011-2013, Yann Collet. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + 16. The NPP library uses code from the Boost Math Toolkit, + and is subject to the following license: + + Boost Software License - Version 1.0 - August 17th, 2003 + . . . . + + Permission is hereby granted, free of charge, to any person or + organization obtaining a copy of the software and accompanying + documentation covered by this license (the "Software") to use, + reproduce, display, distribute, execute, and transmit the Software, + and to prepare derivative works of the Software, and to permit + third-parties to whom the Software is furnished to do so, all + subject to the following: + + The copyright notices in the Software and this entire statement, + including the above license grant, this restriction and the following + disclaimer, must be included in all copies of the Software, in whole + or in part, and all derivative works of the Software, unless such + copies or derivative works are solely in the form of machine-executable + object code generated by a source language processor. + + 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, TITLE AND + NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR + ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR + OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + 17. Portions of the Nsight Eclipse Edition is subject to the + following license: + + The Eclipse Foundation makes available all content in this plug-in + ("Content"). Unless otherwise indicated below, the Content is provided + to you under the terms and conditions of the Eclipse Public License + Version 1.0 ("EPL"). A copy of the EPL is available at http:// + www.eclipse.org/legal/epl-v10.html. For purposes of the EPL, "Program" + will mean the Content. + + If you did not receive this Content directly from the Eclipse + Foundation, the Content is being redistributed by another party + ("Redistributor") and different terms and conditions may apply to your + use of any object code in the Content. Check the Redistributor's + license that was provided with the Content. If no such license exists, + contact the Redistributor. Unless otherwise indicated below, the terms + and conditions of the EPL still apply to any source code in the + Content and such source code may be obtained at http://www.eclipse.org. + + 18. Some of the cuBLAS library routines uses code from + OpenAI, which is subject to the following license: + + License URL + https://github.com/openai/openai-gemm/blob/master/LICENSE + + License Text + The MIT License + + Copyright (c) 2016 OpenAI (http://openai.com), 2016 Google Inc. + + 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. + + 19. Licensee's use of the Visual Studio Setup Configuration + Samples is subject to the following license: + + The MIT License (MIT) + Copyright (C) Microsoft Corporation. All rights reserved. + + 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. + + 20. Licensee's use of linmath.h header for CPU functions for + GL vector/matrix operations from lunarG is subject to the + Apache License Version 2.0. + + 21. The DX12-CUDA sample uses the d3dx12.h header, which is + subject to the MIT license . + +----------------- diff --git a/.venv/lib/python3.12/site-packages/nvidia_cufile_cu12-1.13.1.3.dist-info/METADATA b/.venv/lib/python3.12/site-packages/nvidia_cufile_cu12-1.13.1.3.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..4d22d2b0a860e67abb882c6ec18c0a5dc468a39f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cufile_cu12-1.13.1.3.dist-info/METADATA @@ -0,0 +1,44 @@ +Metadata-Version: 2.2 +Name: nvidia-cufile-cu12 +Version: 1.13.1.3 +Summary: cuFile GPUDirect libraries +Home-page: https://developer.nvidia.com/cuda-zone +Author: Nvidia CUDA Installer Team +Author-email: compute_installer@nvidia.com +License: NVIDIA Proprietary Software +Keywords: cuda,nvidia,runtime,machine learning,deep learning +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Education +Classifier: Intended Audience :: Science/Research +Classifier: License :: Other/Proprietary License +Classifier: Natural Language :: English +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Topic :: Scientific/Engineering +Classifier: Topic :: Scientific/Engineering :: Mathematics +Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence +Classifier: Topic :: Software Development +Classifier: Topic :: Software Development :: Libraries +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX :: Linux +Requires-Python: >=3 +License-File: License.txt +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: home-page +Dynamic: keywords +Dynamic: license +Dynamic: requires-python +Dynamic: summary + +cuFile GPUDirect libraries diff --git a/.venv/lib/python3.12/site-packages/nvidia_cufile_cu12-1.13.1.3.dist-info/RECORD b/.venv/lib/python3.12/site-packages/nvidia_cufile_cu12-1.13.1.3.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..e245e7238220c620d3e04c825fa117d4a85a5ab1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cufile_cu12-1.13.1.3.dist-info/RECORD @@ -0,0 +1,14 @@ +nvidia/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/cufile/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/cufile/include/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/cufile/include/cufile.h,sha256=5bNDkZZNOF_qHbDBkN04OfwF09BEcrAxxPW61HJSzX0,29530 +nvidia/cufile/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/cufile/lib/libcufile.so.0,sha256=ovSFQXf2ZsGoxxm0huTLERnr_dhY66pjSctjxVSdJ-g,3209496 +nvidia/cufile/lib/libcufile_rdma.so.1,sha256=mpEoGEkKqSa07RW1ACaa4GTXKHgDFBjOYLjIu773fM8,46528 +nvidia_cufile_cu12-1.13.1.3.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +nvidia_cufile_cu12-1.13.1.3.dist-info/License.txt,sha256=rW9YU_ugyg0VnQ9Y1JrkmDDC-Mk_epJki5zpCttMbM0,59262 +nvidia_cufile_cu12-1.13.1.3.dist-info/METADATA,sha256=33E6JjiagFLRf-48FSFWPgYLJKA3mDs4if_1T5Bu8og,1673 +nvidia_cufile_cu12-1.13.1.3.dist-info/RECORD,, +nvidia_cufile_cu12-1.13.1.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia_cufile_cu12-1.13.1.3.dist-info/WHEEL,sha256=ygM8qpYgOvrn5C-8vbfzPi-0iFPECh71lLWqkqrTjYw,144 +nvidia_cufile_cu12-1.13.1.3.dist-info/top_level.txt,sha256=fTkAtiFuL16nUrB9ytDDtpytz2t0B4NvYTnRzwAhO14,7 diff --git a/.venv/lib/python3.12/site-packages/nvidia_cufile_cu12-1.13.1.3.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/nvidia_cufile_cu12-1.13.1.3.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/nvidia_cufile_cu12-1.13.1.3.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/nvidia_cufile_cu12-1.13.1.3.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..287a9d7e1a3d4435e9542cc8216b8c5eaf2c0ed2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cufile_cu12-1.13.1.3.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.8.0) +Root-Is-Purelib: true +Tag: py3-none-manylinux2014_x86_64 +Tag: py3-none-manylinux_2_17_x86_64 + diff --git a/.venv/lib/python3.12/site-packages/nvidia_cufile_cu12-1.13.1.3.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/nvidia_cufile_cu12-1.13.1.3.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..862f7abf232cdfbb928609856247292e81c9decb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cufile_cu12-1.13.1.3.dist-info/top_level.txt @@ -0,0 +1 @@ +nvidia diff --git a/.venv/lib/python3.12/site-packages/nvidia_curand_cu12-10.3.9.90.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/nvidia_curand_cu12-10.3.9.90.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_curand_cu12-10.3.9.90.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/nvidia_curand_cu12-10.3.9.90.dist-info/License.txt b/.venv/lib/python3.12/site-packages/nvidia_curand_cu12-10.3.9.90.dist-info/License.txt new file mode 100644 index 0000000000000000000000000000000000000000..b491c70e0aef319022ded661e111ddbd45b8a17f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_curand_cu12-10.3.9.90.dist-info/License.txt @@ -0,0 +1,1568 @@ +End User License Agreement +-------------------------- + + +Preface +------- + +The Software License Agreement in Chapter 1 and the Supplement +in Chapter 2 contain license terms and conditions that govern +the use of NVIDIA software. By accepting this agreement, you +agree to comply with all the terms and conditions applicable +to the product(s) included herein. + + +NVIDIA Driver + + +Description + +This package contains the operating system driver and +fundamental system software components for NVIDIA GPUs. + + +NVIDIA CUDA Toolkit + + +Description + +The NVIDIA CUDA Toolkit provides command-line and graphical +tools for building, debugging and optimizing the performance +of applications accelerated by NVIDIA GPUs, runtime and math +libraries, and documentation including programming guides, +user manuals, and API references. + + +Default Install Location of CUDA Toolkit + +Windows platform: + +%ProgramFiles%\NVIDIA GPU Computing Toolkit\CUDA\v#.# + +Linux platform: + +/usr/local/cuda-#.# + +Mac platform: + +/Developer/NVIDIA/CUDA-#.# + + +NVIDIA CUDA Samples + + +Description + +This package includes over 100+ CUDA examples that demonstrate +various CUDA programming principles, and efficient CUDA +implementation of algorithms in specific application domains. + + +Default Install Location of CUDA Samples + +Windows platform: + +%ProgramData%\NVIDIA Corporation\CUDA Samples\v#.# + +Linux platform: + +/usr/local/cuda-#.#/samples + +and + +$HOME/NVIDIA_CUDA-#.#_Samples + +Mac platform: + +/Developer/NVIDIA/CUDA-#.#/samples + + +NVIDIA Nsight Visual Studio Edition (Windows only) + + +Description + +NVIDIA Nsight Development Platform, Visual Studio Edition is a +development environment integrated into Microsoft Visual +Studio that provides tools for debugging, profiling, analyzing +and optimizing your GPU computing and graphics applications. + + +Default Install Location of Nsight Visual Studio Edition + +Windows platform: + +%ProgramFiles(x86)%\NVIDIA Corporation\Nsight Visual Studio Edition #.# + + +1. License Agreement for NVIDIA Software Development Kits +--------------------------------------------------------- + + +Release Date: July 26, 2018 +--------------------------- + + +Important NoticeRead before downloading, installing, +copying or using the licensed software: +------------------------------------------------------- + +This license agreement, including exhibits attached +("Agreement”) is a legal agreement between you and NVIDIA +Corporation ("NVIDIA") and governs your use of a NVIDIA +software development kit (“SDK”). + +Each SDK has its own set of software and materials, but here +is a description of the types of items that may be included in +a SDK: source code, header files, APIs, data sets and assets +(examples include images, textures, models, scenes, videos, +native API input/output files), binary software, sample code, +libraries, utility programs, programming code and +documentation. + +This Agreement can be accepted only by an adult of legal age +of majority in the country in which the SDK is used. + +If you are entering into this Agreement on behalf of a company +or other legal entity, you represent that you have the legal +authority to bind the entity to this Agreement, in which case +“you” will mean the entity you represent. + +If you don’t have the required age or authority to accept +this Agreement, or if you don’t accept all the terms and +conditions of this Agreement, do not download, install or use +the SDK. + +You agree to use the SDK only for purposes that are permitted +by (a) this Agreement, and (b) any applicable law, regulation +or generally accepted practices or guidelines in the relevant +jurisdictions. + + +1.1. License + + +1.1.1. License Grant + +Subject to the terms of this Agreement, NVIDIA hereby grants +you a non-exclusive, non-transferable license, without the +right to sublicense (except as expressly provided in this +Agreement) to: + + 1. Install and use the SDK, + + 2. Modify and create derivative works of sample source code + delivered in the SDK, and + + 3. Distribute those portions of the SDK that are identified + in this Agreement as distributable, as incorporated in + object code format into a software application that meets + the distribution requirements indicated in this Agreement. + + +1.1.2. Distribution Requirements + +These are the distribution requirements for you to exercise +the distribution grant: + + 1. Your application must have material additional + functionality, beyond the included portions of the SDK. + + 2. The distributable portions of the SDK shall only be + accessed by your application. + + 3. The following notice shall be included in modifications + and derivative works of sample source code distributed: + “This software contains source code provided by NVIDIA + Corporation.” + + 4. Unless a developer tool is identified in this Agreement + as distributable, it is delivered for your internal use + only. + + 5. The terms under which you distribute your application + must be consistent with the terms of this Agreement, + including (without limitation) terms relating to the + license grant and license restrictions and protection of + NVIDIA’s intellectual property rights. Additionally, you + agree that you will protect the privacy, security and + legal rights of your application users. + + 6. You agree to notify NVIDIA in writing of any known or + suspected distribution or use of the SDK not in compliance + with the requirements of this Agreement, and to enforce + the terms of your agreements with respect to distributed + SDK. + + +1.1.3. Authorized Users + +You may allow employees and contractors of your entity or of +your subsidiary(ies) to access and use the SDK from your +secure network to perform work on your behalf. + +If you are an academic institution you may allow users +enrolled or employed by the academic institution to access and +use the SDK from your secure network. + +You are responsible for the compliance with the terms of this +Agreement by your authorized users. If you become aware that +your authorized users didn’t follow the terms of this +Agreement, you agree to take reasonable steps to resolve the +non-compliance and prevent new occurrences. + + +1.1.4. Pre-Release SDK + +The SDK versions identified as alpha, beta, preview or +otherwise as pre-release, may not be fully functional, may +contain errors or design flaws, and may have reduced or +different security, privacy, accessibility, availability, and +reliability standards relative to commercial versions of +NVIDIA software and materials. Use of a pre-release SDK may +result in unexpected results, loss of data, project delays or +other unpredictable damage or loss. + +You may use a pre-release SDK at your own risk, understanding +that pre-release SDKs are not intended for use in production +or business-critical systems. + +NVIDIA may choose not to make available a commercial version +of any pre-release SDK. NVIDIA may also choose to abandon +development and terminate the availability of a pre-release +SDK at any time without liability. + + +1.1.5. Updates + +NVIDIA may, at its option, make available patches, workarounds +or other updates to this SDK. Unless the updates are provided +with their separate governing terms, they are deemed part of +the SDK licensed to you as provided in this Agreement. You +agree that the form and content of the SDK that NVIDIA +provides may change without prior notice to you. While NVIDIA +generally maintains compatibility between versions, NVIDIA may +in some cases make changes that introduce incompatibilities in +future versions of the SDK. + + +1.1.6. Third Party Licenses + +The SDK may come bundled with, or otherwise include or be +distributed with, third party software licensed by a NVIDIA +supplier and/or open source software provided under an open +source license. Use of third party software is subject to the +third-party license terms, or in the absence of third party +terms, the terms of this Agreement. Copyright to third party +software is held by the copyright holders indicated in the +third-party software or license. + + +1.1.7. Reservation of Rights + +NVIDIA reserves all rights, title, and interest in and to the +SDK, not expressly granted to you under this Agreement. + + +1.2. Limitations + +The following license limitations apply to your use of the +SDK: + + 1. You may not reverse engineer, decompile or disassemble, + or remove copyright or other proprietary notices from any + portion of the SDK or copies of the SDK. + + 2. Except as expressly provided in this Agreement, you may + not copy, sell, rent, sublicense, transfer, distribute, + modify, or create derivative works of any portion of the + SDK. For clarity, you may not distribute or sublicense the + SDK as a stand-alone product. + + 3. Unless you have an agreement with NVIDIA for this + purpose, you may not indicate that an application created + with the SDK is sponsored or endorsed by NVIDIA. + + 4. You may not bypass, disable, or circumvent any + encryption, security, digital rights management or + authentication mechanism in the SDK. + + 5. You may not use the SDK in any manner that would cause it + to become subject to an open source software license. As + examples, licenses that require as a condition of use, + modification, and/or distribution that the SDK be: + + a. Disclosed or distributed in source code form; + + b. Licensed for the purpose of making derivative works; + or + + c. Redistributable at no charge. + + 6. Unless you have an agreement with NVIDIA for this + purpose, you may not use the SDK with any system or + application where the use or failure of the system or + application can reasonably be expected to threaten or + result in personal injury, death, or catastrophic loss. + Examples include use in avionics, navigation, military, + medical, life support or other life critical applications. + NVIDIA does not design, test or manufacture the SDK for + these critical uses and NVIDIA shall not be liable to you + or any third party, in whole or in part, for any claims or + damages arising from such uses. + + 7. You agree to defend, indemnify and hold harmless NVIDIA + and its affiliates, and their respective employees, + contractors, agents, officers and directors, from and + against any and all claims, damages, obligations, losses, + liabilities, costs or debt, fines, restitutions and + expenses (including but not limited to attorney’s fees + and costs incident to establishing the right of + indemnification) arising out of or related to your use of + the SDK outside of the scope of this Agreement, or not in + compliance with its terms. + + +1.3. Ownership + + 1. NVIDIA or its licensors hold all rights, title and + interest in and to the SDK and its modifications and + derivative works, including their respective intellectual + property rights, subject to your rights described in this + section. This SDK may include software and materials from + NVIDIA’s licensors, and these licensors are intended + third party beneficiaries that may enforce this Agreement + with respect to their intellectual property rights. + + 2. You hold all rights, title and interest in and to your + applications and your derivative works of the sample + source code delivered in the SDK, including their + respective intellectual property rights, subject to + NVIDIA’s rights described in this section. + + 3. You may, but don’t have to, provide to NVIDIA + suggestions, feature requests or other feedback regarding + the SDK, including possible enhancements or modifications + to the SDK. For any feedback that you voluntarily provide, + you hereby grant NVIDIA and its affiliates a perpetual, + non-exclusive, worldwide, irrevocable license to use, + reproduce, modify, license, sublicense (through multiple + tiers of sublicensees), and distribute (through multiple + tiers of distributors) it without the payment of any + royalties or fees to you. NVIDIA will use feedback at its + choice. NVIDIA is constantly looking for ways to improve + its products, so you may send feedback to NVIDIA through + the developer portal at https://developer.nvidia.com. + + +1.4. No Warranties + +THE SDK IS PROVIDED BY NVIDIA “AS IS” AND “WITH ALL +FAULTS.” TO THE MAXIMUM EXTENT PERMITTED BY LAW, NVIDIA AND +ITS AFFILIATES EXPRESSLY DISCLAIM ALL WARRANTIES OF ANY KIND +OR NATURE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, +BUT NOT LIMITED TO, ANY WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE, TITLE, NON-INFRINGEMENT, OR THE +ABSENCE OF ANY DEFECTS THEREIN, WHETHER LATENT OR PATENT. NO +WARRANTY IS MADE ON THE BASIS OF TRADE USAGE, COURSE OF +DEALING OR COURSE OF TRADE. + + +1.5. Limitation of Liability + +TO THE MAXIMUM EXTENT PERMITTED BY LAW, NVIDIA AND ITS +AFFILIATES SHALL NOT BE LIABLE FOR ANY SPECIAL, INCIDENTAL, +PUNITIVE OR CONSEQUENTIAL DAMAGES, OR ANY LOST PROFITS, LOSS +OF USE, LOSS OF DATA OR LOSS OF GOODWILL, OR THE COSTS OF +PROCURING SUBSTITUTE PRODUCTS, ARISING OUT OF OR IN CONNECTION +WITH THIS AGREEMENT OR THE USE OR PERFORMANCE OF THE SDK, +WHETHER SUCH LIABILITY ARISES FROM ANY CLAIM BASED UPON BREACH +OF CONTRACT, BREACH OF WARRANTY, TORT (INCLUDING NEGLIGENCE), +PRODUCT LIABILITY OR ANY OTHER CAUSE OF ACTION OR THEORY OF +LIABILITY. IN NO EVENT WILL NVIDIA’S AND ITS AFFILIATES +TOTAL CUMULATIVE LIABILITY UNDER OR ARISING OUT OF THIS +AGREEMENT EXCEED US$10.00. THE NATURE OF THE LIABILITY OR THE +NUMBER OF CLAIMS OR SUITS SHALL NOT ENLARGE OR EXTEND THIS +LIMIT. + +These exclusions and limitations of liability shall apply +regardless if NVIDIA or its affiliates have been advised of +the possibility of such damages, and regardless of whether a +remedy fails its essential purpose. These exclusions and +limitations of liability form an essential basis of the +bargain between the parties, and, absent any of these +exclusions or limitations of liability, the provisions of this +Agreement, including, without limitation, the economic terms, +would be substantially different. + + +1.6. Termination + + 1. This Agreement will continue to apply until terminated by + either you or NVIDIA as described below. + + 2. If you want to terminate this Agreement, you may do so by + stopping to use the SDK. + + 3. NVIDIA may, at any time, terminate this Agreement if: + + a. (i) you fail to comply with any term of this + Agreement and the non-compliance is not fixed within + thirty (30) days following notice from NVIDIA (or + immediately if you violate NVIDIA’s intellectual + property rights); + + b. (ii) you commence or participate in any legal + proceeding against NVIDIA with respect to the SDK; or + + c. (iii) NVIDIA decides to no longer provide the SDK in + a country or, in NVIDIA’s sole discretion, the + continued use of it is no longer commercially viable. + + 4. Upon any termination of this Agreement, you agree to + promptly discontinue use of the SDK and destroy all copies + in your possession or control. Your prior distributions in + accordance with this Agreement are not affected by the + termination of this Agreement. Upon written request, you + will certify in writing that you have complied with your + commitments under this section. Upon any termination of + this Agreement all provisions survive except for the + license grant provisions. + + +1.7. General + +If you wish to assign this Agreement or your rights and +obligations, including by merger, consolidation, dissolution +or operation of law, contact NVIDIA to ask for permission. Any +attempted assignment not approved by NVIDIA in writing shall +be void and of no effect. NVIDIA may assign, delegate or +transfer this Agreement and its rights and obligations, and if +to a non-affiliate you will be notified. + +You agree to cooperate with NVIDIA and provide reasonably +requested information to verify your compliance with this +Agreement. + +This Agreement will be governed in all respects by the laws of +the United States and of the State of Delaware as those laws +are applied to contracts entered into and performed entirely +within Delaware by Delaware residents, without regard to the +conflicts of laws principles. The United Nations Convention on +Contracts for the International Sale of Goods is specifically +disclaimed. You agree to all terms of this Agreement in the +English language. + +The state or federal courts residing in Santa Clara County, +California shall have exclusive jurisdiction over any dispute +or claim arising out of this Agreement. Notwithstanding this, +you agree that NVIDIA shall still be allowed to apply for +injunctive remedies or an equivalent type of urgent legal +relief in any jurisdiction. + +If any court of competent jurisdiction determines that any +provision of this Agreement is illegal, invalid or +unenforceable, such provision will be construed as limited to +the extent necessary to be consistent with and fully +enforceable under the law and the remaining provisions will +remain in full force and effect. Unless otherwise specified, +remedies are cumulative. + +Each party acknowledges and agrees that the other is an +independent contractor in the performance of this Agreement. + +The SDK has been developed entirely at private expense and is +“commercial items” consisting of “commercial computer +software” and “commercial computer software +documentation” provided with RESTRICTED RIGHTS. Use, +duplication or disclosure by the U.S. Government or a U.S. +Government subcontractor is subject to the restrictions in +this Agreement pursuant to DFARS 227.7202-3(a) or as set forth +in subparagraphs (c)(1) and (2) of the Commercial Computer +Software - Restricted Rights clause at FAR 52.227-19, as +applicable. Contractor/manufacturer is NVIDIA, 2788 San Tomas +Expressway, Santa Clara, CA 95051. + +The SDK is subject to United States export laws and +regulations. You agree that you will not ship, transfer or +export the SDK into any country, or use the SDK in any manner, +prohibited by the United States Bureau of Industry and +Security or economic sanctions regulations administered by the +U.S. Department of Treasury’s Office of Foreign Assets +Control (OFAC), or any applicable export laws, restrictions or +regulations. These laws include restrictions on destinations, +end users and end use. By accepting this Agreement, you +confirm that you are not a resident or citizen of any country +currently embargoed by the U.S. and that you are not otherwise +prohibited from receiving the SDK. + +Any notice delivered by NVIDIA to you under this Agreement +will be delivered via mail, email or fax. You agree that any +notices that NVIDIA sends you electronically will satisfy any +legal communication requirements. Please direct your legal +notices or other correspondence to NVIDIA Corporation, 2788 +San Tomas Expressway, Santa Clara, California 95051, United +States of America, Attention: Legal Department. + +This Agreement and any exhibits incorporated into this +Agreement constitute the entire agreement of the parties with +respect to the subject matter of this Agreement and supersede +all prior negotiations or documentation exchanged between the +parties relating to this SDK license. Any additional and/or +conflicting terms on documents issued by you are null, void, +and invalid. Any amendment or waiver under this Agreement +shall be in writing and signed by representatives of both +parties. + + +2. CUDA Toolkit Supplement to Software License Agreement for +NVIDIA Software Development Kits +------------------------------------------------------------ + + +Release date: August 16, 2018 +----------------------------- + +The terms in this supplement govern your use of the NVIDIA +CUDA Toolkit SDK under the terms of your license agreement +(“Agreement”) as modified by this supplement. Capitalized +terms used but not defined below have the meaning assigned to +them in the Agreement. + +This supplement is an exhibit to the Agreement and is +incorporated as an integral part of the Agreement. In the +event of conflict between the terms in this supplement and the +terms in the Agreement, the terms in this supplement govern. + + +2.1. License Scope + +The SDK is licensed for you to develop applications only for +use in systems with NVIDIA GPUs. + + +2.2. Distribution + +The portions of the SDK that are distributable under the +Agreement are listed in Attachment A. + + +2.3. Operating Systems + +Those portions of the SDK designed exclusively for use on the +Linux or FreeBSD operating systems, or other operating systems +derived from the source code to these operating systems, may +be copied and redistributed for use in accordance with this +Agreement, provided that the object code files are not +modified in any way (except for unzipping of compressed +files). + + +2.4. Audio and Video Encoders and Decoders + +You acknowledge and agree that it is your sole responsibility +to obtain any additional third-party licenses required to +make, have made, use, have used, sell, import, and offer for +sale your products or services that include or incorporate any +third-party software and content relating to audio and/or +video encoders and decoders from, including but not limited +to, Microsoft, Thomson, Fraunhofer IIS, Sisvel S.p.A., +MPEG-LA, and Coding Technologies. NVIDIA does not grant to you +under this Agreement any necessary patent or other rights with +respect to any audio and/or video encoders and decoders. + + +2.5. Licensing + +If the distribution terms in this Agreement are not suitable +for your organization, or for any questions regarding this +Agreement, please contact NVIDIA at +nvidia-compute-license-questions@nvidia.com. + + +2.6. Attachment A + +The following portions of the SDK are distributable under the +Agreement: + +Component + +CUDA Runtime + +Windows + +cudart.dll, cudart_static.lib, cudadevrt.lib + +Mac OSX + +libcudart.dylib, libcudart_static.a, libcudadevrt.a + +Linux + +libcudart.so, libcudart_static.a, libcudadevrt.a + +Android + +libcudart.so, libcudart_static.a, libcudadevrt.a + +Component + +CUDA FFT Library + +Windows + +cufft.dll, cufftw.dll, cufft.lib, cufftw.lib + +Mac OSX + +libcufft.dylib, libcufft_static.a, libcufftw.dylib, +libcufftw_static.a + +Linux + +libcufft.so, libcufft_static.a, libcufftw.so, +libcufftw_static.a + +Android + +libcufft.so, libcufft_static.a, libcufftw.so, +libcufftw_static.a + +Component + +CUDA BLAS Library + +Windows + +cublas.dll, cublasLt.dll + +Mac OSX + +libcublas.dylib, libcublasLt.dylib, libcublas_static.a, +libcublasLt_static.a + +Linux + +libcublas.so, libcublasLt.so, libcublas_static.a, +libcublasLt_static.a + +Android + +libcublas.so, libcublasLt.so, libcublas_static.a, +libcublasLt_static.a + +Component + +NVIDIA "Drop-in" BLAS Library + +Windows + +nvblas.dll + +Mac OSX + +libnvblas.dylib + +Linux + +libnvblas.so + +Component + +CUDA Sparse Matrix Library + +Windows + +cusparse.dll, cusparse.lib + +Mac OSX + +libcusparse.dylib, libcusparse_static.a + +Linux + +libcusparse.so, libcusparse_static.a + +Android + +libcusparse.so, libcusparse_static.a + +Component + +CUDA Linear Solver Library + +Windows + +cusolver.dll, cusolver.lib + +Mac OSX + +libcusolver.dylib, libcusolver_static.a + +Linux + +libcusolver.so, libcusolver_static.a + +Android + +libcusolver.so, libcusolver_static.a + +Component + +CUDA Random Number Generation Library + +Windows + +curand.dll, curand.lib + +Mac OSX + +libcurand.dylib, libcurand_static.a + +Linux + +libcurand.so, libcurand_static.a + +Android + +libcurand.so, libcurand_static.a + +Component + +CUDA Accelerated Graph Library + +Component + +NVIDIA Performance Primitives Library + +Windows + +nppc.dll, nppc.lib, nppial.dll, nppial.lib, nppicc.dll, +nppicc.lib, nppicom.dll, nppicom.lib, nppidei.dll, +nppidei.lib, nppif.dll, nppif.lib, nppig.dll, nppig.lib, +nppim.dll, nppim.lib, nppist.dll, nppist.lib, nppisu.dll, +nppisu.lib, nppitc.dll, nppitc.lib, npps.dll, npps.lib + +Mac OSX + +libnppc.dylib, libnppc_static.a, libnppial.dylib, +libnppial_static.a, libnppicc.dylib, libnppicc_static.a, +libnppicom.dylib, libnppicom_static.a, libnppidei.dylib, +libnppidei_static.a, libnppif.dylib, libnppif_static.a, +libnppig.dylib, libnppig_static.a, libnppim.dylib, +libnppisu_static.a, libnppitc.dylib, libnppitc_static.a, +libnpps.dylib, libnpps_static.a + +Linux + +libnppc.so, libnppc_static.a, libnppial.so, +libnppial_static.a, libnppicc.so, libnppicc_static.a, +libnppicom.so, libnppicom_static.a, libnppidei.so, +libnppidei_static.a, libnppif.so, libnppif_static.a +libnppig.so, libnppig_static.a, libnppim.so, +libnppim_static.a, libnppist.so, libnppist_static.a, +libnppisu.so, libnppisu_static.a, libnppitc.so +libnppitc_static.a, libnpps.so, libnpps_static.a + +Android + +libnppc.so, libnppc_static.a, libnppial.so, +libnppial_static.a, libnppicc.so, libnppicc_static.a, +libnppicom.so, libnppicom_static.a, libnppidei.so, +libnppidei_static.a, libnppif.so, libnppif_static.a +libnppig.so, libnppig_static.a, libnppim.so, +libnppim_static.a, libnppist.so, libnppist_static.a, +libnppisu.so, libnppisu_static.a, libnppitc.so +libnppitc_static.a, libnpps.so, libnpps_static.a + +Component + +NVIDIA JPEG Library + +Linux + +libnvjpeg.so, libnvjpeg_static.a + +Component + +Internal common library required for statically linking to +cuBLAS, cuSPARSE, cuFFT, cuRAND, nvJPEG and NPP + +Mac OSX + +libculibos.a + +Linux + +libculibos.a + +Component + +NVIDIA Runtime Compilation Library and Header + +All + +nvrtc.h + +Windows + +nvrtc.dll, nvrtc-builtins.dll + +Mac OSX + +libnvrtc.dylib, libnvrtc-builtins.dylib + +Linux + +libnvrtc.so, libnvrtc-builtins.so + +Component + +NVIDIA Optimizing Compiler Library + +Windows + +nvvm.dll + +Mac OSX + +libnvvm.dylib + +Linux + +libnvvm.so + +Component + +NVIDIA Common Device Math Functions Library + +Windows + +libdevice.10.bc + +Mac OSX + +libdevice.10.bc + +Linux + +libdevice.10.bc + +Component + +CUDA Occupancy Calculation Header Library + +All + +cuda_occupancy.h + +Component + +CUDA Half Precision Headers + +All + +cuda_fp16.h, cuda_fp16.hpp + +Component + +CUDA Profiling Tools Interface (CUPTI) Library + +Windows + +cupti.dll + +Mac OSX + +libcupti.dylib + +Linux + +libcupti.so + +Component + +NVIDIA Tools Extension Library + +Windows + +nvToolsExt.dll, nvToolsExt.lib + +Mac OSX + +libnvToolsExt.dylib + +Linux + +libnvToolsExt.so + +Component + +NVIDIA CUDA Driver Libraries + +Linux + +libcuda.so, libnvidia-fatbinaryloader.so, +libnvidia-ptxjitcompiler.so + +The NVIDIA CUDA Driver Libraries are only distributable in +applications that meet this criteria: + + 1. The application was developed starting from a NVIDIA CUDA + container obtained from Docker Hub or the NVIDIA GPU + Cloud, and + + 2. The resulting application is packaged as a Docker + container and distributed to users on Docker Hub or the + NVIDIA GPU Cloud only. + + +2.7. Attachment B + + +Additional Licensing Obligations + +The following third party components included in the SOFTWARE +are licensed to Licensee pursuant to the following terms and +conditions: + + 1. Licensee's use of the GDB third party component is + subject to the terms and conditions of GNU GPL v3: + + This product includes copyrighted third-party software licensed + under the terms of the GNU General Public License v3 ("GPL v3"). + All third-party software packages are copyright by their respective + authors. GPL v3 terms and conditions are hereby incorporated into + the Agreement by this reference: http://www.gnu.org/licenses/gpl.txt + + Consistent with these licensing requirements, the software + listed below is provided under the terms of the specified + open source software licenses. To obtain source code for + software provided under licenses that require + redistribution of source code, including the GNU General + Public License (GPL) and GNU Lesser General Public License + (LGPL), contact oss-requests@nvidia.com. This offer is + valid for a period of three (3) years from the date of the + distribution of this product by NVIDIA CORPORATION. + + Component License + CUDA-GDB GPL v3 + + 2. Licensee represents and warrants that any and all third + party licensing and/or royalty payment obligations in + connection with Licensee's use of the H.264 video codecs + are solely the responsibility of Licensee. + + 3. Licensee's use of the Thrust library is subject to the + terms and conditions of the Apache License Version 2.0. + All third-party software packages are copyright by their + respective authors. Apache License Version 2.0 terms and + conditions are hereby incorporated into the Agreement by + this reference. + http://www.apache.org/licenses/LICENSE-2.0.html + + In addition, Licensee acknowledges the following notice: + Thrust includes source code from the Boost Iterator, + Tuple, System, and Random Number libraries. + + Boost Software License - Version 1.0 - August 17th, 2003 + . . . . + + Permission is hereby granted, free of charge, to any person or + organization obtaining a copy of the software and accompanying + documentation covered by this license (the "Software") to use, + reproduce, display, distribute, execute, and transmit the Software, + and to prepare derivative works of the Software, and to permit + third-parties to whom the Software is furnished to do so, all + subject to the following: + + The copyright notices in the Software and this entire statement, + including the above license grant, this restriction and the following + disclaimer, must be included in all copies of the Software, in whole + or in part, and all derivative works of the Software, unless such + copies or derivative works are solely in the form of machine-executable + object code generated by a source language processor. + + 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, TITLE AND + NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR + ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR + OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + 4. Licensee's use of the LLVM third party component is + subject to the following terms and conditions: + + ====================================================== + LLVM Release License + ====================================================== + University of Illinois/NCSA + Open Source License + + Copyright (c) 2003-2010 University of Illinois at Urbana-Champaign. + All rights reserved. + + Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal with 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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at Urbana- + Champaign, nor the names of its contributors may be used to endorse or + promote products derived from this Software without specific prior + written permission. + + 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 CONTRIBUTORS 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 WITH THE SOFTWARE. + + 5. Licensee's use (e.g. nvprof) of the PCRE third party + component is subject to the following terms and + conditions: + + ------------ + PCRE LICENCE + ------------ + PCRE is a library of functions to support regular expressions whose syntax + and semantics are as close as possible to those of the Perl 5 language. + Release 8 of PCRE is distributed under the terms of the "BSD" licence, as + specified below. The documentation for PCRE, supplied in the "doc" + directory, is distributed under the same terms as the software itself. The + basic library functions are written in C and are freestanding. Also + included in the distribution is a set of C++ wrapper functions, and a just- + in-time compiler that can be used to optimize pattern matching. These are + both optional features that can be omitted when the library is built. + + THE BASIC LIBRARY FUNCTIONS + --------------------------- + Written by: Philip Hazel + Email local part: ph10 + Email domain: cam.ac.uk + University of Cambridge Computing Service, + Cambridge, England. + Copyright (c) 1997-2012 University of Cambridge + All rights reserved. + + PCRE JUST-IN-TIME COMPILATION SUPPORT + ------------------------------------- + Written by: Zoltan Herczeg + Email local part: hzmester + Emain domain: freemail.hu + Copyright(c) 2010-2012 Zoltan Herczeg + All rights reserved. + + STACK-LESS JUST-IN-TIME COMPILER + -------------------------------- + Written by: Zoltan Herczeg + Email local part: hzmester + Emain domain: freemail.hu + Copyright(c) 2009-2012 Zoltan Herczeg + All rights reserved. + + THE C++ WRAPPER FUNCTIONS + ------------------------- + Contributed by: Google Inc. + Copyright (c) 2007-2012, Google Inc. + All rights reserved. + + THE "BSD" LICENCE + ----------------- + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the name of Google + Inc. nor the names of their 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. + + 6. Some of the cuBLAS library routines were written by or + derived from code written by Vasily Volkov and are subject + to the Modified Berkeley Software Distribution License as + follows: + + Copyright (c) 2007-2009, Regents of the University of California + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the University of California, Berkeley 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 AUTHOR "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 AUTHOR 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. + + 7. Some of the cuBLAS library routines were written by or + derived from code written by Davide Barbieri and are + subject to the Modified Berkeley Software Distribution + License as follows: + + Copyright (c) 2008-2009 Davide Barbieri @ University of Rome Tor Vergata. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * The name of the author may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "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 AUTHOR 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. + + 8. Some of the cuBLAS library routines were derived from + code developed by the University of Tennessee and are + subject to the Modified Berkeley Software Distribution + License as follows: + + Copyright (c) 2010 The University of Tennessee. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer listed in this license in the documentation and/or + other materials provided with the distribution. + * Neither the name of the copyright holders 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. + + 9. Some of the cuBLAS library routines were written by or + derived from code written by Jonathan Hogg and are subject + to the Modified Berkeley Software Distribution License as + follows: + + Copyright (c) 2012, The Science and Technology Facilities Council (STFC). + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the STFC 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 STFC 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. + + 10. Some of the cuBLAS library routines were written by or + derived from code written by Ahmad M. Abdelfattah, David + Keyes, and Hatem Ltaief, and are subject to the Apache + License, Version 2.0, as follows: + + -- (C) Copyright 2013 King Abdullah University of Science and Technology + Authors: + Ahmad Abdelfattah (ahmad.ahmad@kaust.edu.sa) + David Keyes (david.keyes@kaust.edu.sa) + Hatem Ltaief (hatem.ltaief@kaust.edu.sa) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the King Abdullah University of Science and + Technology 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 + HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + + 11. Some of the cuSPARSE library routines were written by or + derived from code written by Li-Wen Chang and are subject + to the NCSA Open Source License as follows: + + Copyright (c) 2012, University of Illinois. + + All rights reserved. + + Developed by: IMPACT Group, University of Illinois, http://impact.crhc.illinois.edu + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal with 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: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimers in the documentation and/or other materials provided + with the distribution. + * Neither the names of IMPACT Group, University of Illinois, nor + the names of its contributors may be used to endorse or promote + products derived from this Software without specific prior + written permission. + + 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 CONTRIBUTORS 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 WITH THE + SOFTWARE. + + 12. Some of the cuRAND library routines were written by or + derived from code written by Mutsuo Saito and Makoto + Matsumoto and are subject to the following license: + + Copyright (c) 2009, 2010 Mutsuo Saito, Makoto Matsumoto and Hiroshima + University. All rights reserved. + + Copyright (c) 2011 Mutsuo Saito, Makoto Matsumoto, Hiroshima + University and University of Tokyo. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the Hiroshima University 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. + + 13. Some of the cuRAND library routines were derived from + code developed by D. E. Shaw Research and are subject to + the following license: + + Copyright 2010-2011, D. E. Shaw Research. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions, and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of D. E. Shaw Research 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. + + 14. Some of the Math library routines were written by or + derived from code developed by Norbert Juffa and are + subject to the following license: + + Copyright (c) 2015-2017, Norbert Juffa + 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. + + 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. + + 15. Licensee's use of the lz4 third party component is + subject to the following terms and conditions: + + Copyright (C) 2011-2013, Yann Collet. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + 16. The NPP library uses code from the Boost Math Toolkit, + and is subject to the following license: + + Boost Software License - Version 1.0 - August 17th, 2003 + . . . . + + Permission is hereby granted, free of charge, to any person or + organization obtaining a copy of the software and accompanying + documentation covered by this license (the "Software") to use, + reproduce, display, distribute, execute, and transmit the Software, + and to prepare derivative works of the Software, and to permit + third-parties to whom the Software is furnished to do so, all + subject to the following: + + The copyright notices in the Software and this entire statement, + including the above license grant, this restriction and the following + disclaimer, must be included in all copies of the Software, in whole + or in part, and all derivative works of the Software, unless such + copies or derivative works are solely in the form of machine-executable + object code generated by a source language processor. + + 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, TITLE AND + NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR + ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR + OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + 17. Portions of the Nsight Eclipse Edition is subject to the + following license: + + The Eclipse Foundation makes available all content in this plug-in + ("Content"). Unless otherwise indicated below, the Content is provided + to you under the terms and conditions of the Eclipse Public License + Version 1.0 ("EPL"). A copy of the EPL is available at http:// + www.eclipse.org/legal/epl-v10.html. For purposes of the EPL, "Program" + will mean the Content. + + If you did not receive this Content directly from the Eclipse + Foundation, the Content is being redistributed by another party + ("Redistributor") and different terms and conditions may apply to your + use of any object code in the Content. Check the Redistributor's + license that was provided with the Content. If no such license exists, + contact the Redistributor. Unless otherwise indicated below, the terms + and conditions of the EPL still apply to any source code in the + Content and such source code may be obtained at http://www.eclipse.org. + + 18. Some of the cuBLAS library routines uses code from + OpenAI, which is subject to the following license: + + License URL + https://github.com/openai/openai-gemm/blob/master/LICENSE + + License Text + The MIT License + + Copyright (c) 2016 OpenAI (http://openai.com), 2016 Google Inc. + + 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. + + 19. Licensee's use of the Visual Studio Setup Configuration + Samples is subject to the following license: + + The MIT License (MIT) + Copyright (C) Microsoft Corporation. All rights reserved. + + 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. + + 20. Licensee's use of linmath.h header for CPU functions for + GL vector/matrix operations from lunarG is subject to the + Apache License Version 2.0. + + 21. The DX12-CUDA sample uses the d3dx12.h header, which is + subject to the MIT license . + +----------------- diff --git a/.venv/lib/python3.12/site-packages/nvidia_curand_cu12-10.3.9.90.dist-info/METADATA b/.venv/lib/python3.12/site-packages/nvidia_curand_cu12-10.3.9.90.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..7617880958e7c1f9e79db788e5ab17cad840dca2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_curand_cu12-10.3.9.90.dist-info/METADATA @@ -0,0 +1,44 @@ +Metadata-Version: 2.2 +Name: nvidia-curand-cu12 +Version: 10.3.9.90 +Summary: CURAND native runtime libraries +Home-page: https://developer.nvidia.com/cuda-zone +Author: Nvidia CUDA Installer Team +Author-email: compute_installer@nvidia.com +License: NVIDIA Proprietary Software +Keywords: cuda,nvidia,runtime,machine learning,deep learning +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Education +Classifier: Intended Audience :: Science/Research +Classifier: License :: Other/Proprietary License +Classifier: Natural Language :: English +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Topic :: Scientific/Engineering +Classifier: Topic :: Scientific/Engineering :: Mathematics +Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence +Classifier: Topic :: Software Development +Classifier: Topic :: Software Development :: Libraries +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX :: Linux +Requires-Python: >=3 +License-File: License.txt +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: home-page +Dynamic: keywords +Dynamic: license +Dynamic: requires-python +Dynamic: summary + +CURAND native runtime libraries diff --git a/.venv/lib/python3.12/site-packages/nvidia_curand_cu12-10.3.9.90.dist-info/RECORD b/.venv/lib/python3.12/site-packages/nvidia_curand_cu12-10.3.9.90.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..51a75b681bc5f13415ba843f990de6a6cd83abc2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_curand_cu12-10.3.9.90.dist-info/RECORD @@ -0,0 +1,29 @@ +nvidia/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/curand/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/curand/include/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/curand/include/curand.h,sha256=strQ9idlRTQoBJy_hAbAT4pgkW6BKYg8p_nUjbb8BVw,44075 +nvidia/curand/include/curand_discrete.h,sha256=2qD3BkI622XEu0444wVP7HeYkKAx0Rjr2HDhqU4SA7E,3486 +nvidia/curand/include/curand_discrete2.h,sha256=ZrQTO5R9x83AMX88uq7M8M94DLSC5VEz0PAkfcwtQeg,10883 +nvidia/curand/include/curand_globals.h,sha256=bES1Kx0NrATXk1DReMMkqWrB062nOnaAp39y22wViXU,3717 +nvidia/curand/include/curand_kernel.h,sha256=SjfAeh13ybXIxiekcgczzua02kIAqETopJKRhYvCat8,53133 +nvidia/curand/include/curand_lognormal.h,sha256=-X-iNkJSzWpAYYjogm689EJTZfzore9sxU7ObddljLk,28142 +nvidia/curand/include/curand_mrg32k3a.h,sha256=ZVVREjGNsJQJ-3IzZZ_LKGtGteslicb8E0Aly49BKPs,170296 +nvidia/curand/include/curand_mtgp32.h,sha256=Qhrmx0pHWF-P2Uu5bKwYE9ymEWq3c7qBzCITVMaKMfI,7845 +nvidia/curand/include/curand_mtgp32_host.h,sha256=SXqzmSQkzTLSRJ4pojTg_TNCC3T-G89HdBK-boSDqr4,18274 +nvidia/curand/include/curand_mtgp32_kernel.h,sha256=ajZnXr5ZXnQExElf6LPpigrrKPTmMIZbRyTEnJ-BDhw,13731 +nvidia/curand/include/curand_mtgp32dc_p_11213.h,sha256=7_gGYUH47UugIAEt60vYH5nFa-QUwTpDwSEgLg9cZts,276889 +nvidia/curand/include/curand_normal.h,sha256=lnmYVk2fn0oEVWOytdKhXrHL36GLCjMnB8OnZeCaYcA,26953 +nvidia/curand/include/curand_normal_static.h,sha256=5K4iTC9AuSWCe1LVxuj_0y3BVjtp0bxO6hndv2rbmiw,4727 +nvidia/curand/include/curand_philox4x32_x.h,sha256=T21IP-Rdg3_tSVU9Je4dLKuwEqE4ovfwi7r1hOY92Dw,7166 +nvidia/curand/include/curand_poisson.h,sha256=KrhXOmO_D7aclnj8geIyHqdpSQwWHurS9V_pVtgzodM,25461 +nvidia/curand/include/curand_precalc.h,sha256=I6NZdgT42fMm9qSCtP-rlOAqt4Zsqgal0ajktcPmEak,1392393 +nvidia/curand/include/curand_uniform.h,sha256=gpmRgQu5r6ppgLTg60NXoDdVJS6wMUy6jC5bh8l04e8,17472 +nvidia/curand/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/curand/lib/libcurand.so.10,sha256=-b6gOKJwO3IVcf1FopmomBQf2MsmSlkSY1yVEW9ZYP4,136749240 +nvidia_curand_cu12-10.3.9.90.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +nvidia_curand_cu12-10.3.9.90.dist-info/License.txt,sha256=rW9YU_ugyg0VnQ9Y1JrkmDDC-Mk_epJki5zpCttMbM0,59262 +nvidia_curand_cu12-10.3.9.90.dist-info/METADATA,sha256=fU3xSITD3i7JIsVG2ZXO5i-aDlIls-ry2JUVICEsv28,1684 +nvidia_curand_cu12-10.3.9.90.dist-info/RECORD,, +nvidia_curand_cu12-10.3.9.90.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia_curand_cu12-10.3.9.90.dist-info/WHEEL,sha256=VtFLEVB-VX8niQT4kQ5pcQOOqiKvUvqfZe5V14HmU88,109 +nvidia_curand_cu12-10.3.9.90.dist-info/top_level.txt,sha256=fTkAtiFuL16nUrB9ytDDtpytz2t0B4NvYTnRzwAhO14,7 diff --git a/.venv/lib/python3.12/site-packages/nvidia_curand_cu12-10.3.9.90.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/nvidia_curand_cu12-10.3.9.90.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/nvidia_curand_cu12-10.3.9.90.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/nvidia_curand_cu12-10.3.9.90.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..476a64f798fcad2101388098d22bc98258e64990 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_curand_cu12-10.3.9.90.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.8.0) +Root-Is-Purelib: true +Tag: py3-none-manylinux_2_27_x86_64 + diff --git a/.venv/lib/python3.12/site-packages/nvidia_curand_cu12-10.3.9.90.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/nvidia_curand_cu12-10.3.9.90.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..862f7abf232cdfbb928609856247292e81c9decb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_curand_cu12-10.3.9.90.dist-info/top_level.txt @@ -0,0 +1 @@ +nvidia diff --git a/.venv/lib/python3.12/site-packages/nvidia_cusparse_cu12-12.5.8.93.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/nvidia_cusparse_cu12-12.5.8.93.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cusparse_cu12-12.5.8.93.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/nvidia_cusparse_cu12-12.5.8.93.dist-info/License.txt b/.venv/lib/python3.12/site-packages/nvidia_cusparse_cu12-12.5.8.93.dist-info/License.txt new file mode 100644 index 0000000000000000000000000000000000000000..b491c70e0aef319022ded661e111ddbd45b8a17f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cusparse_cu12-12.5.8.93.dist-info/License.txt @@ -0,0 +1,1568 @@ +End User License Agreement +-------------------------- + + +Preface +------- + +The Software License Agreement in Chapter 1 and the Supplement +in Chapter 2 contain license terms and conditions that govern +the use of NVIDIA software. By accepting this agreement, you +agree to comply with all the terms and conditions applicable +to the product(s) included herein. + + +NVIDIA Driver + + +Description + +This package contains the operating system driver and +fundamental system software components for NVIDIA GPUs. + + +NVIDIA CUDA Toolkit + + +Description + +The NVIDIA CUDA Toolkit provides command-line and graphical +tools for building, debugging and optimizing the performance +of applications accelerated by NVIDIA GPUs, runtime and math +libraries, and documentation including programming guides, +user manuals, and API references. + + +Default Install Location of CUDA Toolkit + +Windows platform: + +%ProgramFiles%\NVIDIA GPU Computing Toolkit\CUDA\v#.# + +Linux platform: + +/usr/local/cuda-#.# + +Mac platform: + +/Developer/NVIDIA/CUDA-#.# + + +NVIDIA CUDA Samples + + +Description + +This package includes over 100+ CUDA examples that demonstrate +various CUDA programming principles, and efficient CUDA +implementation of algorithms in specific application domains. + + +Default Install Location of CUDA Samples + +Windows platform: + +%ProgramData%\NVIDIA Corporation\CUDA Samples\v#.# + +Linux platform: + +/usr/local/cuda-#.#/samples + +and + +$HOME/NVIDIA_CUDA-#.#_Samples + +Mac platform: + +/Developer/NVIDIA/CUDA-#.#/samples + + +NVIDIA Nsight Visual Studio Edition (Windows only) + + +Description + +NVIDIA Nsight Development Platform, Visual Studio Edition is a +development environment integrated into Microsoft Visual +Studio that provides tools for debugging, profiling, analyzing +and optimizing your GPU computing and graphics applications. + + +Default Install Location of Nsight Visual Studio Edition + +Windows platform: + +%ProgramFiles(x86)%\NVIDIA Corporation\Nsight Visual Studio Edition #.# + + +1. License Agreement for NVIDIA Software Development Kits +--------------------------------------------------------- + + +Release Date: July 26, 2018 +--------------------------- + + +Important NoticeRead before downloading, installing, +copying or using the licensed software: +------------------------------------------------------- + +This license agreement, including exhibits attached +("Agreement”) is a legal agreement between you and NVIDIA +Corporation ("NVIDIA") and governs your use of a NVIDIA +software development kit (“SDK”). + +Each SDK has its own set of software and materials, but here +is a description of the types of items that may be included in +a SDK: source code, header files, APIs, data sets and assets +(examples include images, textures, models, scenes, videos, +native API input/output files), binary software, sample code, +libraries, utility programs, programming code and +documentation. + +This Agreement can be accepted only by an adult of legal age +of majority in the country in which the SDK is used. + +If you are entering into this Agreement on behalf of a company +or other legal entity, you represent that you have the legal +authority to bind the entity to this Agreement, in which case +“you” will mean the entity you represent. + +If you don’t have the required age or authority to accept +this Agreement, or if you don’t accept all the terms and +conditions of this Agreement, do not download, install or use +the SDK. + +You agree to use the SDK only for purposes that are permitted +by (a) this Agreement, and (b) any applicable law, regulation +or generally accepted practices or guidelines in the relevant +jurisdictions. + + +1.1. License + + +1.1.1. License Grant + +Subject to the terms of this Agreement, NVIDIA hereby grants +you a non-exclusive, non-transferable license, without the +right to sublicense (except as expressly provided in this +Agreement) to: + + 1. Install and use the SDK, + + 2. Modify and create derivative works of sample source code + delivered in the SDK, and + + 3. Distribute those portions of the SDK that are identified + in this Agreement as distributable, as incorporated in + object code format into a software application that meets + the distribution requirements indicated in this Agreement. + + +1.1.2. Distribution Requirements + +These are the distribution requirements for you to exercise +the distribution grant: + + 1. Your application must have material additional + functionality, beyond the included portions of the SDK. + + 2. The distributable portions of the SDK shall only be + accessed by your application. + + 3. The following notice shall be included in modifications + and derivative works of sample source code distributed: + “This software contains source code provided by NVIDIA + Corporation.” + + 4. Unless a developer tool is identified in this Agreement + as distributable, it is delivered for your internal use + only. + + 5. The terms under which you distribute your application + must be consistent with the terms of this Agreement, + including (without limitation) terms relating to the + license grant and license restrictions and protection of + NVIDIA’s intellectual property rights. Additionally, you + agree that you will protect the privacy, security and + legal rights of your application users. + + 6. You agree to notify NVIDIA in writing of any known or + suspected distribution or use of the SDK not in compliance + with the requirements of this Agreement, and to enforce + the terms of your agreements with respect to distributed + SDK. + + +1.1.3. Authorized Users + +You may allow employees and contractors of your entity or of +your subsidiary(ies) to access and use the SDK from your +secure network to perform work on your behalf. + +If you are an academic institution you may allow users +enrolled or employed by the academic institution to access and +use the SDK from your secure network. + +You are responsible for the compliance with the terms of this +Agreement by your authorized users. If you become aware that +your authorized users didn’t follow the terms of this +Agreement, you agree to take reasonable steps to resolve the +non-compliance and prevent new occurrences. + + +1.1.4. Pre-Release SDK + +The SDK versions identified as alpha, beta, preview or +otherwise as pre-release, may not be fully functional, may +contain errors or design flaws, and may have reduced or +different security, privacy, accessibility, availability, and +reliability standards relative to commercial versions of +NVIDIA software and materials. Use of a pre-release SDK may +result in unexpected results, loss of data, project delays or +other unpredictable damage or loss. + +You may use a pre-release SDK at your own risk, understanding +that pre-release SDKs are not intended for use in production +or business-critical systems. + +NVIDIA may choose not to make available a commercial version +of any pre-release SDK. NVIDIA may also choose to abandon +development and terminate the availability of a pre-release +SDK at any time without liability. + + +1.1.5. Updates + +NVIDIA may, at its option, make available patches, workarounds +or other updates to this SDK. Unless the updates are provided +with their separate governing terms, they are deemed part of +the SDK licensed to you as provided in this Agreement. You +agree that the form and content of the SDK that NVIDIA +provides may change without prior notice to you. While NVIDIA +generally maintains compatibility between versions, NVIDIA may +in some cases make changes that introduce incompatibilities in +future versions of the SDK. + + +1.1.6. Third Party Licenses + +The SDK may come bundled with, or otherwise include or be +distributed with, third party software licensed by a NVIDIA +supplier and/or open source software provided under an open +source license. Use of third party software is subject to the +third-party license terms, or in the absence of third party +terms, the terms of this Agreement. Copyright to third party +software is held by the copyright holders indicated in the +third-party software or license. + + +1.1.7. Reservation of Rights + +NVIDIA reserves all rights, title, and interest in and to the +SDK, not expressly granted to you under this Agreement. + + +1.2. Limitations + +The following license limitations apply to your use of the +SDK: + + 1. You may not reverse engineer, decompile or disassemble, + or remove copyright or other proprietary notices from any + portion of the SDK or copies of the SDK. + + 2. Except as expressly provided in this Agreement, you may + not copy, sell, rent, sublicense, transfer, distribute, + modify, or create derivative works of any portion of the + SDK. For clarity, you may not distribute or sublicense the + SDK as a stand-alone product. + + 3. Unless you have an agreement with NVIDIA for this + purpose, you may not indicate that an application created + with the SDK is sponsored or endorsed by NVIDIA. + + 4. You may not bypass, disable, or circumvent any + encryption, security, digital rights management or + authentication mechanism in the SDK. + + 5. You may not use the SDK in any manner that would cause it + to become subject to an open source software license. As + examples, licenses that require as a condition of use, + modification, and/or distribution that the SDK be: + + a. Disclosed or distributed in source code form; + + b. Licensed for the purpose of making derivative works; + or + + c. Redistributable at no charge. + + 6. Unless you have an agreement with NVIDIA for this + purpose, you may not use the SDK with any system or + application where the use or failure of the system or + application can reasonably be expected to threaten or + result in personal injury, death, or catastrophic loss. + Examples include use in avionics, navigation, military, + medical, life support or other life critical applications. + NVIDIA does not design, test or manufacture the SDK for + these critical uses and NVIDIA shall not be liable to you + or any third party, in whole or in part, for any claims or + damages arising from such uses. + + 7. You agree to defend, indemnify and hold harmless NVIDIA + and its affiliates, and their respective employees, + contractors, agents, officers and directors, from and + against any and all claims, damages, obligations, losses, + liabilities, costs or debt, fines, restitutions and + expenses (including but not limited to attorney’s fees + and costs incident to establishing the right of + indemnification) arising out of or related to your use of + the SDK outside of the scope of this Agreement, or not in + compliance with its terms. + + +1.3. Ownership + + 1. NVIDIA or its licensors hold all rights, title and + interest in and to the SDK and its modifications and + derivative works, including their respective intellectual + property rights, subject to your rights described in this + section. This SDK may include software and materials from + NVIDIA’s licensors, and these licensors are intended + third party beneficiaries that may enforce this Agreement + with respect to their intellectual property rights. + + 2. You hold all rights, title and interest in and to your + applications and your derivative works of the sample + source code delivered in the SDK, including their + respective intellectual property rights, subject to + NVIDIA’s rights described in this section. + + 3. You may, but don’t have to, provide to NVIDIA + suggestions, feature requests or other feedback regarding + the SDK, including possible enhancements or modifications + to the SDK. For any feedback that you voluntarily provide, + you hereby grant NVIDIA and its affiliates a perpetual, + non-exclusive, worldwide, irrevocable license to use, + reproduce, modify, license, sublicense (through multiple + tiers of sublicensees), and distribute (through multiple + tiers of distributors) it without the payment of any + royalties or fees to you. NVIDIA will use feedback at its + choice. NVIDIA is constantly looking for ways to improve + its products, so you may send feedback to NVIDIA through + the developer portal at https://developer.nvidia.com. + + +1.4. No Warranties + +THE SDK IS PROVIDED BY NVIDIA “AS IS” AND “WITH ALL +FAULTS.” TO THE MAXIMUM EXTENT PERMITTED BY LAW, NVIDIA AND +ITS AFFILIATES EXPRESSLY DISCLAIM ALL WARRANTIES OF ANY KIND +OR NATURE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, +BUT NOT LIMITED TO, ANY WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE, TITLE, NON-INFRINGEMENT, OR THE +ABSENCE OF ANY DEFECTS THEREIN, WHETHER LATENT OR PATENT. NO +WARRANTY IS MADE ON THE BASIS OF TRADE USAGE, COURSE OF +DEALING OR COURSE OF TRADE. + + +1.5. Limitation of Liability + +TO THE MAXIMUM EXTENT PERMITTED BY LAW, NVIDIA AND ITS +AFFILIATES SHALL NOT BE LIABLE FOR ANY SPECIAL, INCIDENTAL, +PUNITIVE OR CONSEQUENTIAL DAMAGES, OR ANY LOST PROFITS, LOSS +OF USE, LOSS OF DATA OR LOSS OF GOODWILL, OR THE COSTS OF +PROCURING SUBSTITUTE PRODUCTS, ARISING OUT OF OR IN CONNECTION +WITH THIS AGREEMENT OR THE USE OR PERFORMANCE OF THE SDK, +WHETHER SUCH LIABILITY ARISES FROM ANY CLAIM BASED UPON BREACH +OF CONTRACT, BREACH OF WARRANTY, TORT (INCLUDING NEGLIGENCE), +PRODUCT LIABILITY OR ANY OTHER CAUSE OF ACTION OR THEORY OF +LIABILITY. IN NO EVENT WILL NVIDIA’S AND ITS AFFILIATES +TOTAL CUMULATIVE LIABILITY UNDER OR ARISING OUT OF THIS +AGREEMENT EXCEED US$10.00. THE NATURE OF THE LIABILITY OR THE +NUMBER OF CLAIMS OR SUITS SHALL NOT ENLARGE OR EXTEND THIS +LIMIT. + +These exclusions and limitations of liability shall apply +regardless if NVIDIA or its affiliates have been advised of +the possibility of such damages, and regardless of whether a +remedy fails its essential purpose. These exclusions and +limitations of liability form an essential basis of the +bargain between the parties, and, absent any of these +exclusions or limitations of liability, the provisions of this +Agreement, including, without limitation, the economic terms, +would be substantially different. + + +1.6. Termination + + 1. This Agreement will continue to apply until terminated by + either you or NVIDIA as described below. + + 2. If you want to terminate this Agreement, you may do so by + stopping to use the SDK. + + 3. NVIDIA may, at any time, terminate this Agreement if: + + a. (i) you fail to comply with any term of this + Agreement and the non-compliance is not fixed within + thirty (30) days following notice from NVIDIA (or + immediately if you violate NVIDIA’s intellectual + property rights); + + b. (ii) you commence or participate in any legal + proceeding against NVIDIA with respect to the SDK; or + + c. (iii) NVIDIA decides to no longer provide the SDK in + a country or, in NVIDIA’s sole discretion, the + continued use of it is no longer commercially viable. + + 4. Upon any termination of this Agreement, you agree to + promptly discontinue use of the SDK and destroy all copies + in your possession or control. Your prior distributions in + accordance with this Agreement are not affected by the + termination of this Agreement. Upon written request, you + will certify in writing that you have complied with your + commitments under this section. Upon any termination of + this Agreement all provisions survive except for the + license grant provisions. + + +1.7. General + +If you wish to assign this Agreement or your rights and +obligations, including by merger, consolidation, dissolution +or operation of law, contact NVIDIA to ask for permission. Any +attempted assignment not approved by NVIDIA in writing shall +be void and of no effect. NVIDIA may assign, delegate or +transfer this Agreement and its rights and obligations, and if +to a non-affiliate you will be notified. + +You agree to cooperate with NVIDIA and provide reasonably +requested information to verify your compliance with this +Agreement. + +This Agreement will be governed in all respects by the laws of +the United States and of the State of Delaware as those laws +are applied to contracts entered into and performed entirely +within Delaware by Delaware residents, without regard to the +conflicts of laws principles. The United Nations Convention on +Contracts for the International Sale of Goods is specifically +disclaimed. You agree to all terms of this Agreement in the +English language. + +The state or federal courts residing in Santa Clara County, +California shall have exclusive jurisdiction over any dispute +or claim arising out of this Agreement. Notwithstanding this, +you agree that NVIDIA shall still be allowed to apply for +injunctive remedies or an equivalent type of urgent legal +relief in any jurisdiction. + +If any court of competent jurisdiction determines that any +provision of this Agreement is illegal, invalid or +unenforceable, such provision will be construed as limited to +the extent necessary to be consistent with and fully +enforceable under the law and the remaining provisions will +remain in full force and effect. Unless otherwise specified, +remedies are cumulative. + +Each party acknowledges and agrees that the other is an +independent contractor in the performance of this Agreement. + +The SDK has been developed entirely at private expense and is +“commercial items” consisting of “commercial computer +software” and “commercial computer software +documentation” provided with RESTRICTED RIGHTS. Use, +duplication or disclosure by the U.S. Government or a U.S. +Government subcontractor is subject to the restrictions in +this Agreement pursuant to DFARS 227.7202-3(a) or as set forth +in subparagraphs (c)(1) and (2) of the Commercial Computer +Software - Restricted Rights clause at FAR 52.227-19, as +applicable. Contractor/manufacturer is NVIDIA, 2788 San Tomas +Expressway, Santa Clara, CA 95051. + +The SDK is subject to United States export laws and +regulations. You agree that you will not ship, transfer or +export the SDK into any country, or use the SDK in any manner, +prohibited by the United States Bureau of Industry and +Security or economic sanctions regulations administered by the +U.S. Department of Treasury’s Office of Foreign Assets +Control (OFAC), or any applicable export laws, restrictions or +regulations. These laws include restrictions on destinations, +end users and end use. By accepting this Agreement, you +confirm that you are not a resident or citizen of any country +currently embargoed by the U.S. and that you are not otherwise +prohibited from receiving the SDK. + +Any notice delivered by NVIDIA to you under this Agreement +will be delivered via mail, email or fax. You agree that any +notices that NVIDIA sends you electronically will satisfy any +legal communication requirements. Please direct your legal +notices or other correspondence to NVIDIA Corporation, 2788 +San Tomas Expressway, Santa Clara, California 95051, United +States of America, Attention: Legal Department. + +This Agreement and any exhibits incorporated into this +Agreement constitute the entire agreement of the parties with +respect to the subject matter of this Agreement and supersede +all prior negotiations or documentation exchanged between the +parties relating to this SDK license. Any additional and/or +conflicting terms on documents issued by you are null, void, +and invalid. Any amendment or waiver under this Agreement +shall be in writing and signed by representatives of both +parties. + + +2. CUDA Toolkit Supplement to Software License Agreement for +NVIDIA Software Development Kits +------------------------------------------------------------ + + +Release date: August 16, 2018 +----------------------------- + +The terms in this supplement govern your use of the NVIDIA +CUDA Toolkit SDK under the terms of your license agreement +(“Agreement”) as modified by this supplement. Capitalized +terms used but not defined below have the meaning assigned to +them in the Agreement. + +This supplement is an exhibit to the Agreement and is +incorporated as an integral part of the Agreement. In the +event of conflict between the terms in this supplement and the +terms in the Agreement, the terms in this supplement govern. + + +2.1. License Scope + +The SDK is licensed for you to develop applications only for +use in systems with NVIDIA GPUs. + + +2.2. Distribution + +The portions of the SDK that are distributable under the +Agreement are listed in Attachment A. + + +2.3. Operating Systems + +Those portions of the SDK designed exclusively for use on the +Linux or FreeBSD operating systems, or other operating systems +derived from the source code to these operating systems, may +be copied and redistributed for use in accordance with this +Agreement, provided that the object code files are not +modified in any way (except for unzipping of compressed +files). + + +2.4. Audio and Video Encoders and Decoders + +You acknowledge and agree that it is your sole responsibility +to obtain any additional third-party licenses required to +make, have made, use, have used, sell, import, and offer for +sale your products or services that include or incorporate any +third-party software and content relating to audio and/or +video encoders and decoders from, including but not limited +to, Microsoft, Thomson, Fraunhofer IIS, Sisvel S.p.A., +MPEG-LA, and Coding Technologies. NVIDIA does not grant to you +under this Agreement any necessary patent or other rights with +respect to any audio and/or video encoders and decoders. + + +2.5. Licensing + +If the distribution terms in this Agreement are not suitable +for your organization, or for any questions regarding this +Agreement, please contact NVIDIA at +nvidia-compute-license-questions@nvidia.com. + + +2.6. Attachment A + +The following portions of the SDK are distributable under the +Agreement: + +Component + +CUDA Runtime + +Windows + +cudart.dll, cudart_static.lib, cudadevrt.lib + +Mac OSX + +libcudart.dylib, libcudart_static.a, libcudadevrt.a + +Linux + +libcudart.so, libcudart_static.a, libcudadevrt.a + +Android + +libcudart.so, libcudart_static.a, libcudadevrt.a + +Component + +CUDA FFT Library + +Windows + +cufft.dll, cufftw.dll, cufft.lib, cufftw.lib + +Mac OSX + +libcufft.dylib, libcufft_static.a, libcufftw.dylib, +libcufftw_static.a + +Linux + +libcufft.so, libcufft_static.a, libcufftw.so, +libcufftw_static.a + +Android + +libcufft.so, libcufft_static.a, libcufftw.so, +libcufftw_static.a + +Component + +CUDA BLAS Library + +Windows + +cublas.dll, cublasLt.dll + +Mac OSX + +libcublas.dylib, libcublasLt.dylib, libcublas_static.a, +libcublasLt_static.a + +Linux + +libcublas.so, libcublasLt.so, libcublas_static.a, +libcublasLt_static.a + +Android + +libcublas.so, libcublasLt.so, libcublas_static.a, +libcublasLt_static.a + +Component + +NVIDIA "Drop-in" BLAS Library + +Windows + +nvblas.dll + +Mac OSX + +libnvblas.dylib + +Linux + +libnvblas.so + +Component + +CUDA Sparse Matrix Library + +Windows + +cusparse.dll, cusparse.lib + +Mac OSX + +libcusparse.dylib, libcusparse_static.a + +Linux + +libcusparse.so, libcusparse_static.a + +Android + +libcusparse.so, libcusparse_static.a + +Component + +CUDA Linear Solver Library + +Windows + +cusolver.dll, cusolver.lib + +Mac OSX + +libcusolver.dylib, libcusolver_static.a + +Linux + +libcusolver.so, libcusolver_static.a + +Android + +libcusolver.so, libcusolver_static.a + +Component + +CUDA Random Number Generation Library + +Windows + +curand.dll, curand.lib + +Mac OSX + +libcurand.dylib, libcurand_static.a + +Linux + +libcurand.so, libcurand_static.a + +Android + +libcurand.so, libcurand_static.a + +Component + +CUDA Accelerated Graph Library + +Component + +NVIDIA Performance Primitives Library + +Windows + +nppc.dll, nppc.lib, nppial.dll, nppial.lib, nppicc.dll, +nppicc.lib, nppicom.dll, nppicom.lib, nppidei.dll, +nppidei.lib, nppif.dll, nppif.lib, nppig.dll, nppig.lib, +nppim.dll, nppim.lib, nppist.dll, nppist.lib, nppisu.dll, +nppisu.lib, nppitc.dll, nppitc.lib, npps.dll, npps.lib + +Mac OSX + +libnppc.dylib, libnppc_static.a, libnppial.dylib, +libnppial_static.a, libnppicc.dylib, libnppicc_static.a, +libnppicom.dylib, libnppicom_static.a, libnppidei.dylib, +libnppidei_static.a, libnppif.dylib, libnppif_static.a, +libnppig.dylib, libnppig_static.a, libnppim.dylib, +libnppisu_static.a, libnppitc.dylib, libnppitc_static.a, +libnpps.dylib, libnpps_static.a + +Linux + +libnppc.so, libnppc_static.a, libnppial.so, +libnppial_static.a, libnppicc.so, libnppicc_static.a, +libnppicom.so, libnppicom_static.a, libnppidei.so, +libnppidei_static.a, libnppif.so, libnppif_static.a +libnppig.so, libnppig_static.a, libnppim.so, +libnppim_static.a, libnppist.so, libnppist_static.a, +libnppisu.so, libnppisu_static.a, libnppitc.so +libnppitc_static.a, libnpps.so, libnpps_static.a + +Android + +libnppc.so, libnppc_static.a, libnppial.so, +libnppial_static.a, libnppicc.so, libnppicc_static.a, +libnppicom.so, libnppicom_static.a, libnppidei.so, +libnppidei_static.a, libnppif.so, libnppif_static.a +libnppig.so, libnppig_static.a, libnppim.so, +libnppim_static.a, libnppist.so, libnppist_static.a, +libnppisu.so, libnppisu_static.a, libnppitc.so +libnppitc_static.a, libnpps.so, libnpps_static.a + +Component + +NVIDIA JPEG Library + +Linux + +libnvjpeg.so, libnvjpeg_static.a + +Component + +Internal common library required for statically linking to +cuBLAS, cuSPARSE, cuFFT, cuRAND, nvJPEG and NPP + +Mac OSX + +libculibos.a + +Linux + +libculibos.a + +Component + +NVIDIA Runtime Compilation Library and Header + +All + +nvrtc.h + +Windows + +nvrtc.dll, nvrtc-builtins.dll + +Mac OSX + +libnvrtc.dylib, libnvrtc-builtins.dylib + +Linux + +libnvrtc.so, libnvrtc-builtins.so + +Component + +NVIDIA Optimizing Compiler Library + +Windows + +nvvm.dll + +Mac OSX + +libnvvm.dylib + +Linux + +libnvvm.so + +Component + +NVIDIA Common Device Math Functions Library + +Windows + +libdevice.10.bc + +Mac OSX + +libdevice.10.bc + +Linux + +libdevice.10.bc + +Component + +CUDA Occupancy Calculation Header Library + +All + +cuda_occupancy.h + +Component + +CUDA Half Precision Headers + +All + +cuda_fp16.h, cuda_fp16.hpp + +Component + +CUDA Profiling Tools Interface (CUPTI) Library + +Windows + +cupti.dll + +Mac OSX + +libcupti.dylib + +Linux + +libcupti.so + +Component + +NVIDIA Tools Extension Library + +Windows + +nvToolsExt.dll, nvToolsExt.lib + +Mac OSX + +libnvToolsExt.dylib + +Linux + +libnvToolsExt.so + +Component + +NVIDIA CUDA Driver Libraries + +Linux + +libcuda.so, libnvidia-fatbinaryloader.so, +libnvidia-ptxjitcompiler.so + +The NVIDIA CUDA Driver Libraries are only distributable in +applications that meet this criteria: + + 1. The application was developed starting from a NVIDIA CUDA + container obtained from Docker Hub or the NVIDIA GPU + Cloud, and + + 2. The resulting application is packaged as a Docker + container and distributed to users on Docker Hub or the + NVIDIA GPU Cloud only. + + +2.7. Attachment B + + +Additional Licensing Obligations + +The following third party components included in the SOFTWARE +are licensed to Licensee pursuant to the following terms and +conditions: + + 1. Licensee's use of the GDB third party component is + subject to the terms and conditions of GNU GPL v3: + + This product includes copyrighted third-party software licensed + under the terms of the GNU General Public License v3 ("GPL v3"). + All third-party software packages are copyright by their respective + authors. GPL v3 terms and conditions are hereby incorporated into + the Agreement by this reference: http://www.gnu.org/licenses/gpl.txt + + Consistent with these licensing requirements, the software + listed below is provided under the terms of the specified + open source software licenses. To obtain source code for + software provided under licenses that require + redistribution of source code, including the GNU General + Public License (GPL) and GNU Lesser General Public License + (LGPL), contact oss-requests@nvidia.com. This offer is + valid for a period of three (3) years from the date of the + distribution of this product by NVIDIA CORPORATION. + + Component License + CUDA-GDB GPL v3 + + 2. Licensee represents and warrants that any and all third + party licensing and/or royalty payment obligations in + connection with Licensee's use of the H.264 video codecs + are solely the responsibility of Licensee. + + 3. Licensee's use of the Thrust library is subject to the + terms and conditions of the Apache License Version 2.0. + All third-party software packages are copyright by their + respective authors. Apache License Version 2.0 terms and + conditions are hereby incorporated into the Agreement by + this reference. + http://www.apache.org/licenses/LICENSE-2.0.html + + In addition, Licensee acknowledges the following notice: + Thrust includes source code from the Boost Iterator, + Tuple, System, and Random Number libraries. + + Boost Software License - Version 1.0 - August 17th, 2003 + . . . . + + Permission is hereby granted, free of charge, to any person or + organization obtaining a copy of the software and accompanying + documentation covered by this license (the "Software") to use, + reproduce, display, distribute, execute, and transmit the Software, + and to prepare derivative works of the Software, and to permit + third-parties to whom the Software is furnished to do so, all + subject to the following: + + The copyright notices in the Software and this entire statement, + including the above license grant, this restriction and the following + disclaimer, must be included in all copies of the Software, in whole + or in part, and all derivative works of the Software, unless such + copies or derivative works are solely in the form of machine-executable + object code generated by a source language processor. + + 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, TITLE AND + NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR + ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR + OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + 4. Licensee's use of the LLVM third party component is + subject to the following terms and conditions: + + ====================================================== + LLVM Release License + ====================================================== + University of Illinois/NCSA + Open Source License + + Copyright (c) 2003-2010 University of Illinois at Urbana-Champaign. + All rights reserved. + + Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal with 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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at Urbana- + Champaign, nor the names of its contributors may be used to endorse or + promote products derived from this Software without specific prior + written permission. + + 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 CONTRIBUTORS 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 WITH THE SOFTWARE. + + 5. Licensee's use (e.g. nvprof) of the PCRE third party + component is subject to the following terms and + conditions: + + ------------ + PCRE LICENCE + ------------ + PCRE is a library of functions to support regular expressions whose syntax + and semantics are as close as possible to those of the Perl 5 language. + Release 8 of PCRE is distributed under the terms of the "BSD" licence, as + specified below. The documentation for PCRE, supplied in the "doc" + directory, is distributed under the same terms as the software itself. The + basic library functions are written in C and are freestanding. Also + included in the distribution is a set of C++ wrapper functions, and a just- + in-time compiler that can be used to optimize pattern matching. These are + both optional features that can be omitted when the library is built. + + THE BASIC LIBRARY FUNCTIONS + --------------------------- + Written by: Philip Hazel + Email local part: ph10 + Email domain: cam.ac.uk + University of Cambridge Computing Service, + Cambridge, England. + Copyright (c) 1997-2012 University of Cambridge + All rights reserved. + + PCRE JUST-IN-TIME COMPILATION SUPPORT + ------------------------------------- + Written by: Zoltan Herczeg + Email local part: hzmester + Emain domain: freemail.hu + Copyright(c) 2010-2012 Zoltan Herczeg + All rights reserved. + + STACK-LESS JUST-IN-TIME COMPILER + -------------------------------- + Written by: Zoltan Herczeg + Email local part: hzmester + Emain domain: freemail.hu + Copyright(c) 2009-2012 Zoltan Herczeg + All rights reserved. + + THE C++ WRAPPER FUNCTIONS + ------------------------- + Contributed by: Google Inc. + Copyright (c) 2007-2012, Google Inc. + All rights reserved. + + THE "BSD" LICENCE + ----------------- + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the name of Google + Inc. nor the names of their 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. + + 6. Some of the cuBLAS library routines were written by or + derived from code written by Vasily Volkov and are subject + to the Modified Berkeley Software Distribution License as + follows: + + Copyright (c) 2007-2009, Regents of the University of California + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the University of California, Berkeley 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 AUTHOR "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 AUTHOR 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. + + 7. Some of the cuBLAS library routines were written by or + derived from code written by Davide Barbieri and are + subject to the Modified Berkeley Software Distribution + License as follows: + + Copyright (c) 2008-2009 Davide Barbieri @ University of Rome Tor Vergata. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * The name of the author may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "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 AUTHOR 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. + + 8. Some of the cuBLAS library routines were derived from + code developed by the University of Tennessee and are + subject to the Modified Berkeley Software Distribution + License as follows: + + Copyright (c) 2010 The University of Tennessee. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer listed in this license in the documentation and/or + other materials provided with the distribution. + * Neither the name of the copyright holders 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. + + 9. Some of the cuBLAS library routines were written by or + derived from code written by Jonathan Hogg and are subject + to the Modified Berkeley Software Distribution License as + follows: + + Copyright (c) 2012, The Science and Technology Facilities Council (STFC). + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the STFC 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 STFC 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. + + 10. Some of the cuBLAS library routines were written by or + derived from code written by Ahmad M. Abdelfattah, David + Keyes, and Hatem Ltaief, and are subject to the Apache + License, Version 2.0, as follows: + + -- (C) Copyright 2013 King Abdullah University of Science and Technology + Authors: + Ahmad Abdelfattah (ahmad.ahmad@kaust.edu.sa) + David Keyes (david.keyes@kaust.edu.sa) + Hatem Ltaief (hatem.ltaief@kaust.edu.sa) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the King Abdullah University of Science and + Technology 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 + HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE + + 11. Some of the cuSPARSE library routines were written by or + derived from code written by Li-Wen Chang and are subject + to the NCSA Open Source License as follows: + + Copyright (c) 2012, University of Illinois. + + All rights reserved. + + Developed by: IMPACT Group, University of Illinois, http://impact.crhc.illinois.edu + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal with 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: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimers in the documentation and/or other materials provided + with the distribution. + * Neither the names of IMPACT Group, University of Illinois, nor + the names of its contributors may be used to endorse or promote + products derived from this Software without specific prior + written permission. + + 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 CONTRIBUTORS 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 WITH THE + SOFTWARE. + + 12. Some of the cuRAND library routines were written by or + derived from code written by Mutsuo Saito and Makoto + Matsumoto and are subject to the following license: + + Copyright (c) 2009, 2010 Mutsuo Saito, Makoto Matsumoto and Hiroshima + University. All rights reserved. + + Copyright (c) 2011 Mutsuo Saito, Makoto Matsumoto, Hiroshima + University and University of Tokyo. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of the Hiroshima University 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. + + 13. Some of the cuRAND library routines were derived from + code developed by D. E. Shaw Research and are subject to + the following license: + + Copyright 2010-2011, D. E. Shaw Research. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions, and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions, and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of D. E. Shaw Research 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. + + 14. Some of the Math library routines were written by or + derived from code developed by Norbert Juffa and are + subject to the following license: + + Copyright (c) 2015-2017, Norbert Juffa + 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. + + 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. + + 15. Licensee's use of the lz4 third party component is + subject to the following terms and conditions: + + Copyright (C) 2011-2013, Yann Collet. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + 16. The NPP library uses code from the Boost Math Toolkit, + and is subject to the following license: + + Boost Software License - Version 1.0 - August 17th, 2003 + . . . . + + Permission is hereby granted, free of charge, to any person or + organization obtaining a copy of the software and accompanying + documentation covered by this license (the "Software") to use, + reproduce, display, distribute, execute, and transmit the Software, + and to prepare derivative works of the Software, and to permit + third-parties to whom the Software is furnished to do so, all + subject to the following: + + The copyright notices in the Software and this entire statement, + including the above license grant, this restriction and the following + disclaimer, must be included in all copies of the Software, in whole + or in part, and all derivative works of the Software, unless such + copies or derivative works are solely in the form of machine-executable + object code generated by a source language processor. + + 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, TITLE AND + NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR + ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR + OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + 17. Portions of the Nsight Eclipse Edition is subject to the + following license: + + The Eclipse Foundation makes available all content in this plug-in + ("Content"). Unless otherwise indicated below, the Content is provided + to you under the terms and conditions of the Eclipse Public License + Version 1.0 ("EPL"). A copy of the EPL is available at http:// + www.eclipse.org/legal/epl-v10.html. For purposes of the EPL, "Program" + will mean the Content. + + If you did not receive this Content directly from the Eclipse + Foundation, the Content is being redistributed by another party + ("Redistributor") and different terms and conditions may apply to your + use of any object code in the Content. Check the Redistributor's + license that was provided with the Content. If no such license exists, + contact the Redistributor. Unless otherwise indicated below, the terms + and conditions of the EPL still apply to any source code in the + Content and such source code may be obtained at http://www.eclipse.org. + + 18. Some of the cuBLAS library routines uses code from + OpenAI, which is subject to the following license: + + License URL + https://github.com/openai/openai-gemm/blob/master/LICENSE + + License Text + The MIT License + + Copyright (c) 2016 OpenAI (http://openai.com), 2016 Google Inc. + + 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. + + 19. Licensee's use of the Visual Studio Setup Configuration + Samples is subject to the following license: + + The MIT License (MIT) + Copyright (C) Microsoft Corporation. All rights reserved. + + 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. + + 20. Licensee's use of linmath.h header for CPU functions for + GL vector/matrix operations from lunarG is subject to the + Apache License Version 2.0. + + 21. The DX12-CUDA sample uses the d3dx12.h header, which is + subject to the MIT license . + +----------------- diff --git a/.venv/lib/python3.12/site-packages/nvidia_cusparse_cu12-12.5.8.93.dist-info/METADATA b/.venv/lib/python3.12/site-packages/nvidia_cusparse_cu12-12.5.8.93.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..6f49c69886f619189dab562063548d38dab861af --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cusparse_cu12-12.5.8.93.dist-info/METADATA @@ -0,0 +1,46 @@ +Metadata-Version: 2.2 +Name: nvidia-cusparse-cu12 +Version: 12.5.8.93 +Summary: CUSPARSE native runtime libraries +Home-page: https://developer.nvidia.com/cuda-zone +Author: Nvidia CUDA Installer Team +Author-email: compute_installer@nvidia.com +License: NVIDIA Proprietary Software +Keywords: cuda,nvidia,runtime,machine learning,deep learning +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Education +Classifier: Intended Audience :: Science/Research +Classifier: License :: Other/Proprietary License +Classifier: Natural Language :: English +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Topic :: Scientific/Engineering +Classifier: Topic :: Scientific/Engineering :: Mathematics +Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence +Classifier: Topic :: Software Development +Classifier: Topic :: Software Development :: Libraries +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX :: Linux +Requires-Python: >=3 +License-File: License.txt +Requires-Dist: nvidia-nvjitlink-cu12 +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: home-page +Dynamic: keywords +Dynamic: license +Dynamic: requires-dist +Dynamic: requires-python +Dynamic: summary + +CUSPARSE native runtime libraries diff --git a/.venv/lib/python3.12/site-packages/nvidia_cusparse_cu12-12.5.8.93.dist-info/RECORD b/.venv/lib/python3.12/site-packages/nvidia_cusparse_cu12-12.5.8.93.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..f48f532819231b6de4fdfb32ff3534af2048673b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cusparse_cu12-12.5.8.93.dist-info/RECORD @@ -0,0 +1,14 @@ +nvidia/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/cusparse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/cusparse/include/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/cusparse/include/cusparse.h,sha256=8MDDKz-6d4rFquyjD-g-yWZbEJCD97Qq4_Cv5gh7fuk,296787 +nvidia/cusparse/include/cusparse_v2.h,sha256=jkH2A9hYc-TEF0vuQ_SurbhPNEHkYGUIRuxKXhFAqnw,2587 +nvidia/cusparse/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia/cusparse/lib/libcusparse.so.12,sha256=Q3ZR0exdDlsbTQe4xJLxU8tIJSIv4tdjIypAf2Z9m7I,387990224 +nvidia_cusparse_cu12-12.5.8.93.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +nvidia_cusparse_cu12-12.5.8.93.dist-info/License.txt,sha256=rW9YU_ugyg0VnQ9Y1JrkmDDC-Mk_epJki5zpCttMbM0,59262 +nvidia_cusparse_cu12-12.5.8.93.dist-info/METADATA,sha256=6Ds7-B5qLB3E1N3Nc7KDqI5vf50gOF0oDsAw2_U4TyI,1750 +nvidia_cusparse_cu12-12.5.8.93.dist-info/RECORD,, +nvidia_cusparse_cu12-12.5.8.93.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +nvidia_cusparse_cu12-12.5.8.93.dist-info/WHEEL,sha256=W1f9TbdN8VZ2pk2vr0-t6xdwRCfMeEgaW-gJqtHx6Vs,144 +nvidia_cusparse_cu12-12.5.8.93.dist-info/top_level.txt,sha256=fTkAtiFuL16nUrB9ytDDtpytz2t0B4NvYTnRzwAhO14,7 diff --git a/.venv/lib/python3.12/site-packages/nvidia_cusparse_cu12-12.5.8.93.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/nvidia_cusparse_cu12-12.5.8.93.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/nvidia_cusparse_cu12-12.5.8.93.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/nvidia_cusparse_cu12-12.5.8.93.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..711dd0f79fe712cec60be7868740cd7d73ccb637 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cusparse_cu12-12.5.8.93.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: setuptools (75.8.1) +Root-Is-Purelib: true +Tag: py3-none-manylinux2014_x86_64 +Tag: py3-none-manylinux_2_17_x86_64 + diff --git a/.venv/lib/python3.12/site-packages/nvidia_cusparse_cu12-12.5.8.93.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/nvidia_cusparse_cu12-12.5.8.93.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..862f7abf232cdfbb928609856247292e81c9decb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/nvidia_cusparse_cu12-12.5.8.93.dist-info/top_level.txt @@ -0,0 +1 @@ +nvidia diff --git a/.venv/lib/python3.12/site-packages/package_readme.md b/.venv/lib/python3.12/site-packages/package_readme.md new file mode 100644 index 0000000000000000000000000000000000000000..2e935d793b15fa6cb2043cbd326a44e3fb539adb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/package_readme.md @@ -0,0 +1,97 @@ +
+

+
+ +# Weights and Biases [![PyPI](https://img.shields.io/pypi/v/wandb)](https://pypi.python.org/pypi/wandb) [![Conda (channel only)](https://img.shields.io/conda/vn/conda-forge/wandb)](https://anaconda.org/conda-forge/wandb) [![CircleCI](https://img.shields.io/circleci/build/github/wandb/wandb/main)](https://circleci.com/gh/wandb/wandb) [![Codecov](https://img.shields.io/codecov/c/gh/wandb/wandb)](https://codecov.io/gh/wandb/wandb) + +Use W&B to build better models faster. Track and visualize all the pieces of your machine learning pipeline, from datasets to production machine learning models. Get started with W&B today, [sign up for an account!](https://wandb.com?utm_source=github&utm_medium=code&utm_campaign=wandb&utm_content=readme) + + + +See the [W&B Developer Guide](https://docs.wandb.ai/?utm_source=github&utm_medium=code&utm_campaign=wandb&utm_content=documentation) and [API Reference Guide](https://docs.wandb.ai/ref?utm_source=github&utm_medium=code&utm_campaign=wandb&utm_content=documentation) for a full technical description of the W&B platform. + +  + +# Quickstart + +Get started with W&B in four steps: + +1. First, sign up for a [W&B account](https://wandb.ai/login?utm_source=github&utm_medium=code&utm_campaign=wandb&utm_content=quickstart). + +2. Second, install the W&B SDK with [pip](https://pip.pypa.io/en/stable/). Navigate to your terminal and type the following command: + +```shell +pip install wandb +``` + +3. Third, log into W&B: + +```python +wandb.login() +``` + +4. Use the example code snippet below as a template to integrate W&B to your Python script: + +```python +import wandb + +# Start a W&B Run with wandb.init +run = wandb.init(project="my_first_project") + +# Save model inputs and hyperparameters in a wandb.config object +config = run.config +config.learning_rate = 0.01 + +# Model training code here ... + +# Log metrics over time to visualize performance with wandb.log +for i in range(10): + run.log({"loss": ...}) + +# Mark the run as finished, and finish uploading all data +run.finish() +``` + +For example, if the preceding code was stored in a script called train.py: + +```shell +python train.py +``` + +You will see a URL in your terminal logs when your script starts and finishes. Data is staged locally in a directory named _wandb_ relative to your script. Navigate to the W&B App to view a dashboard of your first W&B Experiment. Use the W&B App to compare multiple experiments in a unified place, dive into the results of a single run, and much more! + +  + +# Integrations + +Use your favorite framework with W&B. W&B integrations make it fast and easy to set up experiment tracking and data versioning inside existing projects. For more information on how to integrate W&B with the framework of your choice, see [W&B Integrations](https://docs.wandb.ai/guides/integrations) in the W&B Developer Guide. + +  + +# Python Version Support + +We are committed to supporting our minimum required Python version for *at least* six months after its official end-of-life (EOL) date, as defined by the Python Software Foundation. You can find a list of Python EOL dates [here](https://devguide.python.org/versions/). + +When we discontinue support for a Python version, we will increment the library’s minor version number to reflect this change. + +  + +# Contribution guidelines +Weights & Biases ❤️ open source, and we welcome contributions from the community! See the [Contribution guide](https://github.com/wandb/wandb/blob/main/CONTRIBUTING.md) for more information on the development workflow and the internals of the wandb library. For wandb bugs and feature requests, visit [GitHub Issues](https://github.com/wandb/wandb/issues) or contact support@wandb.com. + +  + +# Academic Researchers +Reach out to W&B Support at support@wandb.com to get a [free academic license](https://www.wandb.com/academic) for you and your research group. + +  + +# W&B Community + +Be a part of the growing W&B Community and interact with the W&B team in our [Discord](https://wandb.me/discord). Stay connected with the latest ML updates and tutorials with [W&B Fully Connected](https://wandb.ai/fully-connected). + +  + +# License + +[MIT License](https://github.com/wandb/wandb/blob/main/LICENSE) diff --git a/.venv/lib/python3.12/site-packages/packaging-25.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/packaging-25.0.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/packaging-25.0.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/packaging-25.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/packaging-25.0.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..10b290a6cd1770506ae281a5fa469f6c110c364c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/packaging-25.0.dist-info/METADATA @@ -0,0 +1,105 @@ +Metadata-Version: 2.4 +Name: packaging +Version: 25.0 +Summary: Core utilities for Python packages +Author-email: Donald Stufft +Requires-Python: >=3.8 +Description-Content-Type: text/x-rst +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: License :: OSI Approved :: BSD License +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 :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Typing :: Typed +License-File: LICENSE +License-File: LICENSE.APACHE +License-File: LICENSE.BSD +Project-URL: Documentation, https://packaging.pypa.io/ +Project-URL: Source, https://github.com/pypa/packaging + +packaging +========= + +.. start-intro + +Reusable core utilities for various Python Packaging +`interoperability specifications `_. + +This library provides utilities that implement the interoperability +specifications which have clearly one correct behaviour (eg: :pep:`440`) +or benefit greatly from having a single shared implementation (eg: :pep:`425`). + +.. end-intro + +The ``packaging`` project includes the following: version handling, specifiers, +markers, requirements, tags, utilities. + +Documentation +------------- + +The `documentation`_ provides information and the API for the following: + +- Version Handling +- Specifiers +- Markers +- Requirements +- Tags +- Utilities + +Installation +------------ + +Use ``pip`` to install these utilities:: + + pip install packaging + +The ``packaging`` library uses calendar-based versioning (``YY.N``). + +Discussion +---------- + +If you run into bugs, you can file them in our `issue tracker`_. + +You can also join ``#pypa`` on Freenode to ask questions or get involved. + + +.. _`documentation`: https://packaging.pypa.io/ +.. _`issue tracker`: https://github.com/pypa/packaging/issues + + +Code of Conduct +--------------- + +Everyone interacting in the packaging project's codebases, issue trackers, chat +rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_. + +.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md + +Contributing +------------ + +The ``CONTRIBUTING.rst`` file outlines how to contribute to this project as +well as how to report a potential security issue. The documentation for this +project also covers information about `project development`_ and `security`_. + +.. _`project development`: https://packaging.pypa.io/en/latest/development/ +.. _`security`: https://packaging.pypa.io/en/latest/security/ + +Project History +--------------- + +Please review the ``CHANGELOG.rst`` file or the `Changelog documentation`_ for +recent changes and project history. + +.. _`Changelog documentation`: https://packaging.pypa.io/en/latest/changelog/ + diff --git a/.venv/lib/python3.12/site-packages/packaging-25.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/packaging-25.0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..08a804a1afe26f889b18e09de0bb62cfc3ad36f1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/packaging-25.0.dist-info/RECORD @@ -0,0 +1,25 @@ +packaging-25.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +packaging-25.0.dist-info/METADATA,sha256=W2EaYJw4_vw9YWv0XSCuyY-31T8kXayp4sMPyFx6woI,3281 +packaging-25.0.dist-info/RECORD,, +packaging-25.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +packaging-25.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82 +packaging-25.0.dist-info/licenses/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197 +packaging-25.0.dist-info/licenses/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174 +packaging-25.0.dist-info/licenses/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344 +packaging/__init__.py,sha256=_0cDiPVf2S-bNfVmZguxxzmrIYWlyASxpqph4qsJWUc,494 +packaging/_elffile.py,sha256=UkrbDtW7aeq3qqoAfU16ojyHZ1xsTvGke_WqMTKAKd0,3286 +packaging/_manylinux.py,sha256=t4y_-dTOcfr36gLY-ztiOpxxJFGO2ikC11HgfysGxiM,9596 +packaging/_musllinux.py,sha256=p9ZqNYiOItGee8KcZFeHF_YcdhVwGHdK6r-8lgixvGQ,2694 +packaging/_parser.py,sha256=gYfnj0pRHflVc4RHZit13KNTyN9iiVcU2RUCGi22BwM,10221 +packaging/_structures.py,sha256=q3eVNmbWJGG_S0Dit_S3Ao8qQqz_5PYTXFAKBZe5yr4,1431 +packaging/_tokenizer.py,sha256=OYzt7qKxylOAJ-q0XyK1qAycyPRYLfMPdGQKRXkZWyI,5310 +packaging/licenses/__init__.py,sha256=VsK4o27CJXWfTi8r2ybJmsBoCdhpnBWuNrskaCVKP7U,5715 +packaging/licenses/_spdx.py,sha256=oAm1ztPFwlsmCKe7lAAsv_OIOfS1cWDu9bNBkeu-2ns,48398 +packaging/markers.py,sha256=P0we27jm1xUzgGMJxBjtUFCIWeBxTsMeJTOJ6chZmAY,12049 +packaging/metadata.py,sha256=8IZErqQQnNm53dZZuYq4FGU4_dpyinMeH1QFBIWIkfE,34739 +packaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +packaging/requirements.py,sha256=gYyRSAdbrIyKDY66ugIDUQjRMvxkH2ALioTmX3tnL6o,2947 +packaging/specifiers.py,sha256=gtPu5DTc-F9baLq3FTGEK6dPhHGCuwwZetaY0PSV2gs,40055 +packaging/tags.py,sha256=41s97W9Zatrq2Ed7Rc3qeBDaHe8pKKvYq2mGjwahfXk,22745 +packaging/utils.py,sha256=0F3Hh9OFuRgrhTgGZUl5K22Fv1YP2tZl1z_2gO6kJiA,5050 +packaging/version.py,sha256=olfyuk_DPbflNkJ4wBWetXQ17c74x3DB501degUv7DY,16676 diff --git a/.venv/lib/python3.12/site-packages/packaging-25.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/packaging-25.0.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/packaging-25.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/packaging-25.0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..d8b9936dad9ab2513fa6979f411560d3b6b57e37 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/packaging-25.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.12.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/peft-0.17.1.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/peft-0.17.1.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/peft-0.17.1.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/peft-0.17.1.dist-info/METADATA b/.venv/lib/python3.12/site-packages/peft-0.17.1.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..34ef7db3d6ee52c7e56484b2c74c2c94b9daf394 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/peft-0.17.1.dist-info/METADATA @@ -0,0 +1,265 @@ +Metadata-Version: 2.4 +Name: peft +Version: 0.17.1 +Summary: Parameter-Efficient Fine-Tuning (PEFT) +Home-page: https://github.com/huggingface/peft +Author: The HuggingFace team +Author-email: benjamin@huggingface.co +License: Apache +Keywords: deep learning +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Education +Classifier: Intended Audience :: Science/Research +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +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 :: Scientific/Engineering :: Artificial Intelligence +Requires-Python: >=3.9.0 +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: numpy>=1.17 +Requires-Dist: packaging>=20.0 +Requires-Dist: psutil +Requires-Dist: pyyaml +Requires-Dist: torch>=1.13.0 +Requires-Dist: transformers +Requires-Dist: tqdm +Requires-Dist: accelerate>=0.21.0 +Requires-Dist: safetensors +Requires-Dist: huggingface_hub>=0.25.0 +Provides-Extra: quality +Requires-Dist: black; extra == "quality" +Requires-Dist: hf-doc-builder; extra == "quality" +Requires-Dist: ruff~=0.9.2; extra == "quality" +Provides-Extra: docs-specific +Requires-Dist: black; extra == "docs-specific" +Requires-Dist: hf-doc-builder; extra == "docs-specific" +Provides-Extra: dev +Requires-Dist: black; extra == "dev" +Requires-Dist: hf-doc-builder; extra == "dev" +Requires-Dist: ruff~=0.9.2; extra == "dev" +Requires-Dist: black; extra == "dev" +Requires-Dist: hf-doc-builder; extra == "dev" +Provides-Extra: test +Requires-Dist: black; extra == "test" +Requires-Dist: hf-doc-builder; extra == "test" +Requires-Dist: ruff~=0.9.2; extra == "test" +Requires-Dist: black; extra == "test" +Requires-Dist: hf-doc-builder; extra == "test" +Requires-Dist: pytest; extra == "test" +Requires-Dist: pytest-cov; extra == "test" +Requires-Dist: pytest-xdist; extra == "test" +Requires-Dist: parameterized; extra == "test" +Requires-Dist: datasets; extra == "test" +Requires-Dist: diffusers; extra == "test" +Requires-Dist: scipy; extra == "test" +Requires-Dist: protobuf; extra == "test" +Requires-Dist: sentencepiece; extra == "test" +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: description-content-type +Dynamic: home-page +Dynamic: keywords +Dynamic: license +Dynamic: license-file +Dynamic: provides-extra +Dynamic: requires-dist +Dynamic: requires-python +Dynamic: summary + + + +

🤗 PEFT

+

+

State-of-the-art Parameter-Efficient Fine-Tuning (PEFT) methods

+

+ +Fine-tuning large pretrained models is often prohibitively costly due to their scale. Parameter-Efficient Fine-Tuning (PEFT) methods enable efficient adaptation of large pretrained models to various downstream applications by only fine-tuning a small number of (extra) model parameters instead of all the model's parameters. This significantly decreases the computational and storage costs. Recent state-of-the-art PEFT techniques achieve performance comparable to fully fine-tuned models. + +PEFT is integrated with Transformers for easy model training and inference, Diffusers for conveniently managing different adapters, and Accelerate for distributed training and inference for really big models. + +> [!TIP] +> Visit the [PEFT](https://huggingface.co/PEFT) organization to read about the PEFT methods implemented in the library and to see notebooks demonstrating how to apply these methods to a variety of downstream tasks. Click the "Watch repos" button on the organization page to be notified of newly implemented methods and notebooks! + +Check the PEFT Adapters API Reference section for a list of supported PEFT methods, and read the [Adapters](https://huggingface.co/docs/peft/en/conceptual_guides/adapter), [Soft prompts](https://huggingface.co/docs/peft/en/conceptual_guides/prompting), and [IA3](https://huggingface.co/docs/peft/en/conceptual_guides/ia3) conceptual guides to learn more about how these methods work. + +## Quickstart + +Install PEFT from pip: + +```bash +pip install peft +``` + +Prepare a model for training with a PEFT method such as LoRA by wrapping the base model and PEFT configuration with `get_peft_model`. For the bigscience/mt0-large model, you're only training 0.19% of the parameters! + +```python +from transformers import AutoModelForCausalLM +from peft import LoraConfig, TaskType, get_peft_model + +device = "cuda" +model_id = "Qwen/Qwen2.5-3B-Instruct" +model = AutoModelForCausalLM.from_pretrained(model_id, device_map=device) +peft_config = LoraConfig( + r=16, + lora_alpha=32, + task_type=TaskType.CAUSAL_LM, + # target_modules=["q_proj", "v_proj", ...] # optionally indicate target modules +) +model = get_peft_model(model, peft_config) +model.print_trainable_parameters() +# prints: trainable params: 3,686,400 || all params: 3,089,625,088 || trainable%: 0.1193 + +# now perform training on your dataset, e.g. using transformers Trainer, then save the model +model.save_pretrained("qwen2.5-3b-lora") +``` + +To load a PEFT model for inference: + +```python +from transformers import AutoModelForCausalLM, AutoTokenizer +from peft import PeftModel + +device = "cuda" +model_id = "Qwen/Qwen2.5-3B-Instruct" +tokenizer = AutoTokenizer.from_pretrained(model_id) +model = AutoModelForCausalLM.from_pretrained(model_id, device_map=device) +model = PeftModel.from_pretrained(model, "qwen2.5-3b-lora") + +inputs = tokenizer("Preheat the oven to 350 degrees and place the cookie dough", return_tensors="pt") +outputs = model.generate(**inputs.to(device), max_new_tokens=50) +print(tokenizer.decode(outputs[0], skip_special_tokens=True)) + +# prints something like: Preheat the oven to 350 degrees and place the cookie dough in a baking dish [...] +``` + +## Why you should use PEFT + +There are many benefits of using PEFT but the main one is the huge savings in compute and storage, making PEFT applicable to many different use cases. + +### High performance on consumer hardware + +Consider the memory requirements for training the following models on the [ought/raft/twitter_complaints](https://huggingface.co/datasets/ought/raft/viewer/twitter_complaints) dataset with an A100 80GB GPU with more than 64GB of CPU RAM. + +| Model | Full Finetuning | PEFT-LoRA PyTorch | PEFT-LoRA DeepSpeed with CPU Offloading | +| --------- | ---- | ---- | ---- | +| bigscience/T0_3B (3B params) | 47.14GB GPU / 2.96GB CPU | 14.4GB GPU / 2.96GB CPU | 9.8GB GPU / 17.8GB CPU | +| bigscience/mt0-xxl (12B params) | OOM GPU | 56GB GPU / 3GB CPU | 22GB GPU / 52GB CPU | +| bigscience/bloomz-7b1 (7B params) | OOM GPU | 32GB GPU / 3.8GB CPU | 18.1GB GPU / 35GB CPU | + +With LoRA you can fully finetune a 12B parameter model that would've otherwise run out of memory on the 80GB GPU, and comfortably fit and train a 3B parameter model. When you look at the 3B parameter model's performance, it is comparable to a fully finetuned model at a fraction of the GPU memory. + +| Submission Name | Accuracy | +| --------- | ---- | +| Human baseline (crowdsourced) | 0.897 | +| Flan-T5 | 0.892 | +| lora-t0-3b | 0.863 | + +> [!TIP] +> The bigscience/T0_3B model performance isn't optimized in the table above. You can squeeze even more performance out of it by playing around with the input instruction templates, LoRA hyperparameters, and other training related hyperparameters. The final checkpoint size of this model is just 19MB compared to 11GB of the full bigscience/T0_3B model. Learn more about the advantages of finetuning with PEFT in this [blog post](https://www.philschmid.de/fine-tune-flan-t5-peft). + +### Quantization + +Quantization is another method for reducing the memory requirements of a model by representing the data in a lower precision. It can be combined with PEFT methods to make it even easier to train and load LLMs for inference. + +* Learn how to finetune [meta-llama/Llama-2-7b-hf](https://huggingface.co/meta-llama/Llama-2-7b-hf) with QLoRA and the [TRL](https://huggingface.co/docs/trl/index) library on a 16GB GPU in the [Finetune LLMs on your own consumer hardware using tools from PyTorch and Hugging Face ecosystem](https://pytorch.org/blog/finetune-llms/) blog post. +* Learn how to finetune a [openai/whisper-large-v2](https://huggingface.co/openai/whisper-large-v2) model for multilingual automatic speech recognition with LoRA and 8-bit quantization in this [notebook](https://colab.research.google.com/drive/1DOkD_5OUjFa0r5Ik3SgywJLJtEo2qLxO?usp=sharing) (see this [notebook](https://colab.research.google.com/drive/1vhF8yueFqha3Y3CpTHN6q9EVcII9EYzs?usp=sharing) instead for an example of streaming a dataset). + +### Save compute and storage + +PEFT can help you save storage by avoiding full finetuning of models on each of downstream task or dataset. In many cases, you're only finetuning a very small fraction of a model's parameters and each checkpoint is only a few MBs in size (instead of GBs). These smaller PEFT adapters demonstrate performance comparable to a fully finetuned model. If you have many datasets, you can save a lot of storage with a PEFT model and not have to worry about catastrophic forgetting or overfitting the backbone or base model. + +## PEFT integrations + +PEFT is widely supported across the Hugging Face ecosystem because of the massive efficiency it brings to training and inference. + +### Diffusers + +The iterative diffusion process consumes a lot of memory which can make it difficult to train. PEFT can help reduce the memory requirements and reduce the storage size of the final model checkpoint. For example, consider the memory required for training a Stable Diffusion model with LoRA on an A100 80GB GPU with more than 64GB of CPU RAM. The final model checkpoint size is only 8.8MB! + +| Model | Full Finetuning | PEFT-LoRA | PEFT-LoRA with Gradient Checkpointing | +| --------- | ---- | ---- | ---- | +| CompVis/stable-diffusion-v1-4 | 27.5GB GPU / 3.97GB CPU | 15.5GB GPU / 3.84GB CPU | 8.12GB GPU / 3.77GB CPU | + +> [!TIP] +> Take a look at the [examples/lora_dreambooth/train_dreambooth.py](examples/lora_dreambooth/train_dreambooth.py) training script to try training your own Stable Diffusion model with LoRA, and play around with the [smangrul/peft-lora-sd-dreambooth](https://huggingface.co/spaces/smangrul/peft-lora-sd-dreambooth) Space which is running on a T4 instance. Learn more about the PEFT integration in Diffusers in this [tutorial](https://huggingface.co/docs/peft/main/en/tutorial/peft_integrations#diffusers). + +### Transformers + +PEFT is directly integrated with [Transformers](https://huggingface.co/docs/transformers/main/en/peft). After loading a model, call `add_adapter` to add a new PEFT adapter to the model: + +```python +from peft import LoraConfig +model = ... # transformers model +peft_config = LoraConfig(...) +model.add_adapter(lora_config, adapter_name="lora_1") +``` + +To load a trained PEFT adapter, call `load_adapter`: + +```python +model = ... # transformers model +model.load_adapter(, adapter_name="lora_1") +``` + +And to switch between different adapters, call `set_adapter`: + +```python +model.set_adapter("lora_2") +``` + +The Transformers integration doesn't include all the functionalities offered in PEFT, such as methods for merging the adapter into the base model. + +### Accelerate + +[Accelerate](https://huggingface.co/docs/accelerate/index) is a library for distributed training and inference on various training setups and hardware (GPUs, TPUs, Apple Silicon, etc.). PEFT models work with Accelerate out of the box, making it really convenient to train really large models or use them for inference on consumer hardware with limited resources. + +### TRL + +PEFT can also be applied to training LLMs with RLHF components such as the ranker and policy. Get started by reading: + +* [Fine-tune a Mistral-7b model with Direct Preference Optimization](https://towardsdatascience.com/fine-tune-a-mistral-7b-model-with-direct-preference-optimization-708042745aac) with PEFT and the [TRL](https://huggingface.co/docs/trl/index) library to learn more about the Direct Preference Optimization (DPO) method and how to apply it to a LLM. +* [Fine-tuning 20B LLMs with RLHF on a 24GB consumer GPU](https://huggingface.co/blog/trl-peft) with PEFT and the [TRL](https://huggingface.co/docs/trl/index) library, and then try out the [gpt2-sentiment_peft.ipynb](https://github.com/huggingface/trl/blob/main/examples/notebooks/gpt2-sentiment.ipynb) notebook to optimize GPT2 to generate positive movie reviews. +* [StackLLaMA: A hands-on guide to train LLaMA with RLHF](https://huggingface.co/blog/stackllama) with PEFT, and then try out the [stack_llama/scripts](https://github.com/huggingface/trl/tree/main/examples/research_projects/stack_llama/scripts) for supervised finetuning, reward modeling, and RL finetuning. + +## Model support + +Use this [Space](https://stevhliu-peft-methods.hf.space) or check out the [docs](https://huggingface.co/docs/peft/main/en/index) to find which models officially support a PEFT method out of the box. Even if you don't see a model listed below, you can manually configure the model config to enable PEFT for a model. Read the [New transformers architecture](https://huggingface.co/docs/peft/main/en/developer_guides/custom_models#new-transformers-architectures) guide to learn how. + +## Contribute + +If you would like to contribute to PEFT, please check out our [contribution guide](https://huggingface.co/docs/peft/developer_guides/contributing). + +## Citing 🤗 PEFT + +To use 🤗 PEFT in your publication, please cite it by using the following BibTeX entry. + +```bibtex +@Misc{peft, + title = {{PEFT}: State-of-the-art Parameter-Efficient Fine-Tuning methods}, + author = {Sourab Mangrulkar and Sylvain Gugger and Lysandre Debut and Younes Belkada and Sayak Paul and Benjamin Bossan}, + howpublished = {\url{https://github.com/huggingface/peft}}, + year = {2022} +} +``` diff --git a/.venv/lib/python3.12/site-packages/peft-0.17.1.dist-info/RECORD b/.venv/lib/python3.12/site-packages/peft-0.17.1.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..0320f5176077ae5bd76ed970ffd3241476ea0b72 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/peft-0.17.1.dist-info/RECORD @@ -0,0 +1,168 @@ +peft-0.17.1.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +peft-0.17.1.dist-info/METADATA,sha256=RIGiuGSAOspn0a8VpCpXW0th8GSI8Aet0K3U-Trh1ik,14695 +peft-0.17.1.dist-info/RECORD,, +peft-0.17.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +peft-0.17.1.dist-info/WHEEL,sha256=lTU6B6eIfYoiQJTZNc-fyaR6BpL6ehTzU3xGYxn2n8k,91 +peft-0.17.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357 +peft-0.17.1.dist-info/top_level.txt,sha256=DOKoqHe6fr-A3g26PPWvf5bHLy8fHKhflUO5xzJJEUY,5 +peft/__init__.py,sha256=76vDtE1zJu7lnAeRDkJvo47gxkGbdz6Pda-HrmHT-jI,5523 +peft/auto.py,sha256=MAxPviiRqTiPwRLsSIXd3o4TPzS_8SkJu1L_uxPbD5s,7269 +peft/config.py,sha256=OY-CoT6EY-nSPeSQ9tEZUXFekgKyfdzK4NRPViQImVE,14889 +peft/helpers.py,sha256=ZzY4zLxabjYSu1OMnjthg2MianHK-ORTSrB-iYZZLHo,9790 +peft/import_utils.py,sha256=PJVcQVlbdB7UgotT0IkNyKQ_XmYv1CJ81FIscibWVNA,5867 +peft/mapping.py,sha256=aEux7M8FkdzcTm37tVa0DH_qlLThfy8w_zyH7JnIZYQ,3890 +peft/mapping_func.py,sha256=wwUzywCepg5Fo11WQoW-Zcl3jAxB4MKB3C36zWnCMV0,6064 +peft/mixed_model.py,sha256=zX8J_EjygpDN_3iNF6xMrC2v6k-pz_ug3qlydfAJZnU,20305 +peft/optimizers/__init__.py,sha256=U9b6PPoAZ7XaOvEaP8JNSQsCGNHxygrt_8srMxZBdyE,760 +peft/optimizers/lorafa.py,sha256=wJOfV6ULSczOHM4EjSKXw-opBWdtlc32KOwXej0R04c,11408 +peft/optimizers/loraplus.py,sha256=_yyE145kThOfAzdvyLrSc1KIrs28NG1BTy_bGspif8o,4736 +peft/peft_model.py,sha256=Iwfr6xAbr_U7Gc5aDxCYhdmbZKsUfWfLoZcZtgl8PhA,156704 +peft/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +peft/tuners/__init__.py,sha256=skxqM5WDqqPpAz00xjauNtozlHbNftAlCxy51zLThv0,3416 +peft/tuners/_buffer_dict.py,sha256=bFeG7cRBIgVNnQSV4rqmeRnaJpAIWB5JnMb4jAhmxis,5464 +peft/tuners/adalora/__init__.py,sha256=UZn9KKpzni8W1va5aGMS5nfz0GYY0lWbrjSV4V64fiQ,1479 +peft/tuners/adalora/bnb.py,sha256=eBUdGJtt5XMcumWz7CeINuJvUO_f3PJ5M58Bmf3K1RQ,5524 +peft/tuners/adalora/config.py,sha256=0MHuhMmNo_sjraeXCIWFbQBymhFwrA_YN_Q-fNOjx68,5689 +peft/tuners/adalora/gptq.py,sha256=BXjI79oGzIbxyIS6GK38BAveZxpvF9J3d9SyMVXXKBc,2701 +peft/tuners/adalora/layer.py,sha256=SG2QBJ2na4QRrQbJFwCri22lA-rhB1aj3X-bOsprFNU,14827 +peft/tuners/adalora/model.py,sha256=Cfy9L0eqZ55N9n2Pu4l5MQaElSfWWvaxJ1mLONi7RK8,16360 +peft/tuners/adaption_prompt/__init__.py,sha256=C3vrdUyV7042lan7Y3jrRer7-EElxHNvd9LsvwSWHCc,949 +peft/tuners/adaption_prompt/config.py,sha256=OC9R0jtc8ZCkEvM4U9DDEzr9dBY1znsFUWLaSsC55tM,3157 +peft/tuners/adaption_prompt/layer.py,sha256=dx5d7b3swtEnPqjUJFGC246kYRbLc2Jn8QIxDYqkYGE,10400 +peft/tuners/adaption_prompt/model.py,sha256=yCdHcTtAOoZWRJYWx6juvMegWCGzRTotG2wDn_uIeSg,7900 +peft/tuners/adaption_prompt/utils.py,sha256=vb4OYH6fHwr9RR9o-2ZeIr3vH1XoKnluYawq7s_EhPg,7391 +peft/tuners/boft/__init__.py,sha256=M5RzpzRMchgBuglsGS6rAgcc1cOTrZiRn-cD3ic9sXQ,865 +peft/tuners/boft/config.py,sha256=fMllyQI2rzIKxNsMT2ITbX-IWjBhO7ZN2b14u2epIck,8370 +peft/tuners/boft/fbd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +peft/tuners/boft/fbd/fbd_cuda.cpp,sha256=w9Pny1nGyyERI6Vmjnxh-LhbP769aL0XKDZIc_nAy48,815 +peft/tuners/boft/fbd/fbd_cuda_kernel.cu,sha256=SgFrZw-WjJrOxq-Q3qugwLAoI4gBWDqJGlnfoPgpQPs,3076 +peft/tuners/boft/layer.py,sha256=QGHS3f4z_UXTyDKjRphqxtbPNLHuUpWvb5Mr2YCvM08,42361 +peft/tuners/boft/model.py,sha256=9q1rktzRBZVul5WXUi9p8Rad9iaTHVFG3o_eP2fTr6A,14583 +peft/tuners/bone/__init__.py,sha256=_VWZt8MAwxPrkb2vx4syCC53y-0W2mx1qr7mclzlB1Y,891 +peft/tuners/bone/config.py,sha256=-9Ah4OOzrIUwAhMN6ehwvVpD0-QwG5xF1QcPuZJNVjM,6632 +peft/tuners/bone/layer.py,sha256=mKRR2_AHbs4odHgOLYrIKu3fYSUXiOQb9-jHuHCKPl0,14925 +peft/tuners/bone/model.py,sha256=5wYpsOmIt0TAuJ7ZQBnnp0oJWmG7t4h0zyCqMh7koFk,13269 +peft/tuners/c3a/__init__.py,sha256=fvrOSHHtbJ2YbLc5B3NRXTj9KXcYAz6oZqpkgGD8EZk,879 +peft/tuners/c3a/config.py,sha256=lR0AaEHF2L1dRFFNvQQSup1sb8sLiv8hwGt8XINIK7o,6939 +peft/tuners/c3a/layer.py,sha256=koLk8rb_3nDhu5wis_mrNz7n0GbHA2rdAjpKkLG05RI,8513 +peft/tuners/c3a/model.py,sha256=IWvqswA7zRB2lr5J0OPBtNhEU7yO-4_BCZss6IyRY4A,11524 +peft/tuners/c3a/utils.py,sha256=N3HPTyDqhUel3uMbQACzHLLln733h6qfw5RPpWae4H8,1767 +peft/tuners/cpt/__init__.py,sha256=eRetdUVUtxHm7Ic_YkGUvCrlmkGjHEZOQW48wlT6lDU,829 +peft/tuners/cpt/config.py,sha256=659uslUyNNAuLP3uRQ5A6lDe1TvYiPdPVR13W5WASOY,4465 +peft/tuners/cpt/model.py,sha256=HzJhDaXFDIIcNaF_plylvYyOKpsg3USm4dB3JPbkBvQ,8405 +peft/tuners/fourierft/__init__.py,sha256=mM9_QZx9OX6iGv6bdUX3OA717S4J12XHB17olneyoFA,946 +peft/tuners/fourierft/config.py,sha256=esSaz3xSotrHHX-6VDdSupVIQDO1xAhwwZfHLekafKI,12167 +peft/tuners/fourierft/layer.py,sha256=rYuXMCsDwznC1Bqn8jMm2frfO2zbEPmDQkwUA1r-CSg,8459 +peft/tuners/fourierft/model.py,sha256=d86fFAueRR-gXZnw5STl3iCj6K35Q-aQTr-OdcjveaA,14367 +peft/tuners/hra/__init__.py,sha256=YAhlcshKSkU5RB57ggsi4Q57zeGPIgKQwL_FbCd8SKg,904 +peft/tuners/hra/config.py,sha256=8dO7PNWCBOh4BnD1S2frpj14aAP8R2DH-5e8PENpK_k,6946 +peft/tuners/hra/layer.py,sha256=3mi6yuaGpH61xkpkm2hO0ctLj_5LYgUn11Vy0J68pHg,19046 +peft/tuners/hra/model.py,sha256=1fjFHVhiSZgYtudU_mg2Wp3G_MQS88gWVFtLKFAXsu0,13505 +peft/tuners/ia3/__init__.py,sha256=kuH_j1DLRgh8tXv4zqpfTedXpV7cHkUwSX8Q_KOwzDg,1349 +peft/tuners/ia3/bnb.py,sha256=GvoVKQS0t4u5Xnq-isEMLi8m_VgZdBZPc6LrVEZdHkM,4668 +peft/tuners/ia3/config.py,sha256=aldnE7rwHyX0GdiYDHwxg2bhce9WG84PrFotNyrE3bk,5924 +peft/tuners/ia3/layer.py,sha256=tt-dkxSxkTfLrrj-ta3qgvAcEI61_rDxxxcmcAs4qXo,14911 +peft/tuners/ia3/model.py,sha256=S4MzUHkGvFRP3tN-kMIblDbw13tB1DqCM2x1EeTRMXk,21089 +peft/tuners/ln_tuning/__init__.py,sha256=eSQwNTe2K73a10v8Va35OgaUfvIwIYdDt4Xxw-W8DNM,852 +peft/tuners/ln_tuning/config.py,sha256=Lfa6Bo1jMO0v-KQrDo8lbtCsg9BQiB5BARimKY837WE,3405 +peft/tuners/ln_tuning/layer.py,sha256=cA0y_UrnXy2EFbkk0nPJhH054rjCGA1CPX_u2HXyTR4,4422 +peft/tuners/ln_tuning/model.py,sha256=erh8MUSKBYaE3c2BX5Opoj_CvIQCVR69SFFB2qZU6Jg,8097 +peft/tuners/loha/__init__.py,sha256=ONprqmQIy45oSa9FLtn-XksHwHyLmPQ_reZ5OhwMBy8,943 +peft/tuners/loha/config.py,sha256=YioHpL3V0pa1mnN5qhVyvrW-RwkAQB91SURTuz3FcZA,7446 +peft/tuners/loha/layer.py,sha256=CzVwI5ZPo8gI_771LfafkM4evkvPxhnb-fsa93sCbpI,15429 +peft/tuners/loha/model.py,sha256=T4vjY9ETEQ1vu9cS2diN1baIgCx8mMgJ1IKVtM37ims,4771 +peft/tuners/lokr/__init__.py,sha256=j8J43bJg8uaCH-LgGXtJ1B8xdtmKtdIu8k2Fr6Zoscw,927 +peft/tuners/lokr/config.py,sha256=PQqv_TggTj8hFyLT5JIRi5EZ37xOADA0cJ0A9TZyyu0,8240 +peft/tuners/lokr/layer.py,sha256=DwXpEVAKGYd2u9iZKNqjVgvqzItrdfWWa8CooJWn0U8,16426 +peft/tuners/lokr/model.py,sha256=5DOiwPdO425Z0pQZywdyn1JYYwIT_pzC3N-5kI7gJ30,4898 +peft/tuners/lora/__init__.py,sha256=atLdxtlSLcxCA-NMmLslOP-7T0wCelZ-CcS-jKIForY,1883 +peft/tuners/lora/aqlm.py,sha256=Z5IucgOYb43_sXxwTIgefxJ3-l_h0dhvyOjfl7aPvGU,3737 +peft/tuners/lora/awq.py,sha256=e3hhACuWxeD4ZCRCdGJ8w7rpg2jDzYF7KHM8sojwH18,4133 +peft/tuners/lora/bnb.py,sha256=qYwVJWQLzM85GL_y3-oLsY4H7HluFq4k9OLDrtB-3lM,23522 +peft/tuners/lora/config.py,sha256=haR4vN_UL5OY0jLV4N20llC9EZkUhQP4ZINP2-yjrrs,42385 +peft/tuners/lora/corda.py,sha256=qufOaAVo3ZW-apRZVGVxFNtP_MGbWcnB3YmoHIo3kUg,15311 +peft/tuners/lora/dora.py,sha256=B9UU_QV9iqnse4m-w_P70EhilRwaj_cxU-gzCWSB7PU,8480 +peft/tuners/lora/eetq.py,sha256=_dbbJ_dur9lcAv7vCN8TNFkjQ2cDHPe_jnIim-f1dQk,4137 +peft/tuners/lora/eva.py,sha256=893lEyOju6qNDK_AzGYJZUhp1l5Ln_Rx8uj8TXpgs80,34252 +peft/tuners/lora/gptq.py,sha256=a_4Sw49mRg9yf6Ri6xQIXZaT1FNrYmt4T7LjXVjCSpo,5325 +peft/tuners/lora/hqq.py,sha256=iAsw18fWKIfwgY202rUxqZsowGNXJfpTOt6Nbmr1IQM,10397 +peft/tuners/lora/inc.py,sha256=WAq7TGBKF43spZXu0Z94c6frnKQsIDo1EFwd4yjp8GQ,2963 +peft/tuners/lora/layer.py,sha256=OeL2kIyfP69NLeK27P30TJbSyuZ7gVXtG1G8Z_sltzQ,97115 +peft/tuners/lora/model.py,sha256=nuw5blBhNOUK-kH_IbJgijMH-PByB_RgUG0toz17wvc,45130 +peft/tuners/lora/torchao.py,sha256=o9bKUGYuyNhu0qy5m7LmBOrqDc9fdPDa71IaG_mNFW0,6001 +peft/tuners/lora/tp_layer.py,sha256=pneYwRpVPQrAXN_JwJx9SBK79sVs5WzRyyN1yuPlIZU,14185 +peft/tuners/lora/variants.py,sha256=xHepDWjKotPS_6IiZvMq06sXZ_AdhCUOUkLx_CnyAsI,18921 +peft/tuners/lycoris_utils.py,sha256=b5jmZp_mYcqWGCmQ3efN5B2C6RnDzUjmWQD_5Dqxay4,17407 +peft/tuners/miss/__init__.py,sha256=0k0uGeggR84kDE11na2-A3XRsWfX9s4N4h5aoLq2Nu0,891 +peft/tuners/miss/config.py,sha256=6ucl5RwsnIOBb3iN62Ie0Ff26Gq58E2RpJ6b14oDMRg,7682 +peft/tuners/miss/layer.py,sha256=G5D684lD8oI3TkJh1PCugHT-k2R-JCaD4NipDDaVjYs,17053 +peft/tuners/miss/model.py,sha256=Yn_SgyjF85TZGhg5mD1Wl4Y4xqSc61Yg0o0PcPzkDdY,13463 +peft/tuners/mixed/__init__.py,sha256=see7CbOiJJ-E8W1QSSBtIK4oQfFnkJOUVU5xckCYyzw,706 +peft/tuners/mixed/model.py,sha256=F9Qfwt0I-vpD5_568TZ-TnZb0vTDbUbSmMeUfbphxEk,15637 +peft/tuners/multitask_prompt_tuning/__init__.py,sha256=YRacKPnRhZ63bW8EmTzUirZjhMxU0sivhsLtj3NiCTU,1000 +peft/tuners/multitask_prompt_tuning/config.py,sha256=WMitUfJdO-u7GAeSQMkV643upKCU5iOlUsHb5O-SzDY,2478 +peft/tuners/multitask_prompt_tuning/model.py,sha256=zDfMF8lCosOKP14qrPe-5GffdHOFvgoeLkRxXfNK5gI,4915 +peft/tuners/oft/__init__.py,sha256=WeqJeH3Ho_jaWI_BMbxHXSIeCi8HntaEwpJL_5jBo8Y,1529 +peft/tuners/oft/aqlm.py,sha256=OJa0Po1Sv8MA5OyWmhuhgjli9FoPffMOUyqqujO0-zg,3291 +peft/tuners/oft/awq.py,sha256=DMDdJVUOod6RQltM6UyccgnaSDYv1vkTODCMz7QWOSY,4066 +peft/tuners/oft/bnb.py,sha256=HooSmYNDmk6aQ5tr5negP7glbflhk9plFdFbkoBfE0U,15881 +peft/tuners/oft/config.py,sha256=l7fkTRdQ5ciSW-WoNYLFo3iF3MZJzlU6oR9QwLozUFE,10118 +peft/tuners/oft/eetq.py,sha256=nzvLoK96rxn-YjV-UFU_K-DMN4KLubGTsz7z1YP7WBg,4001 +peft/tuners/oft/gptq.py,sha256=z2zH4CTVqzDETSj8FkWHU0R0B-N4GiF3MU9OBcLACls,3971 +peft/tuners/oft/hqq.py,sha256=1FgcujKqpDVEHNwvooy6DVvROYIh2Is1FaZsf4xApts,7322 +peft/tuners/oft/inc.py,sha256=neKIK1Z0NbZJE5V5NKU41o76k5jRGG1p9FXUVuRFeFw,2959 +peft/tuners/oft/layer.py,sha256=tXL0hQtraRPJaJNaY_kWNQODVu_Tr1XwfDlO_MvvP8w,36343 +peft/tuners/oft/model.py,sha256=o7waOCaNJyNjFZPp81XZmXpvwPgAPtGSVUSmAZWAcds,17427 +peft/tuners/p_tuning/__init__.py,sha256=EtRO5mb2JSJk55PIDamQMHwwLmusvYksmcs1OiDOLHU,942 +peft/tuners/p_tuning/config.py,sha256=MzgKKOPQ1uit5eDw5wN5TjkJHRnQ2mPXIzR8zIfV-ac,2142 +peft/tuners/p_tuning/model.py,sha256=rv2mbmPOArAefeqhJ09Oz7XNGaScWGRT1hgKlrfhfAw,5575 +peft/tuners/poly/__init__.py,sha256=tqQ06eCqWv5unMpmBYW9xtcUHiUa8z91h7tmSWVZWo4,883 +peft/tuners/poly/config.py,sha256=p9hpa17p16OcLcui7_yQtuP2Eg0tvIoQeXlSlJsbnNI,4569 +peft/tuners/poly/layer.py,sha256=v_EHqPDPHC8oCLLxM49eqVdI8bhKH0lFzl9U59WB5zY,6593 +peft/tuners/poly/model.py,sha256=3wTFEGgZaCvSrfO6nriPPvSFB7KQef32b1gNLCZluLQ,6794 +peft/tuners/poly/router.py,sha256=o0Q3h6yM9FxJWNGhwNOMqaNeuqbV2hcOxFxUDRumc5A,2784 +peft/tuners/prefix_tuning/__init__.py,sha256=iBQHtZgrv5FU_mz4RWT0IdDBfc5C-tX-qDYxnwRL8Wc,868 +peft/tuners/prefix_tuning/config.py,sha256=V7Cxl9bWlhGknPk5Zo-EH629umCNNiqMrgLMh3-XAzA,1418 +peft/tuners/prefix_tuning/model.py,sha256=FyE_EBtvvA9bZDX-GRWN6s0MQtcUe57PLD4qRT2ACww,3007 +peft/tuners/prompt_tuning/__init__.py,sha256=CeZkm_qTIucS_KE60RPGdf_ecUMWtXvtWLupMQVAdTw,912 +peft/tuners/prompt_tuning/config.py,sha256=65V0yvV6gthPYygxsshzvR0yLUMExMw2QMq0E26UPjQ,3504 +peft/tuners/prompt_tuning/model.py,sha256=D6YPZUgFkF3q4EPa_0AvLJHJDbE0DqIjUXUSRFtefKI,3747 +peft/tuners/randlora/__init__.py,sha256=fvo7F6284VmDqCHXcUF5py2vK5bfRwiJ3x3o6eImI0M,1353 +peft/tuners/randlora/bnb.py,sha256=EtXm7pwynvAANxiAjCBRZmTwvS2zGSXjVOPxzqg8b_U,19585 +peft/tuners/randlora/config.py,sha256=MLFBjIANFmW9tUt7ne4iUZQd_OnseHH9zR_-V1Uya_Q,9945 +peft/tuners/randlora/layer.py,sha256=owlLZiPiO6qlmuD_g3yYkuogp9ncDMvMw-_48eDBtrs,15314 +peft/tuners/randlora/model.py,sha256=iw4N59jsy8buTwrzrNW9gQjrV8m1ebgbT9tmvctrK8M,24149 +peft/tuners/shira/__init__.py,sha256=128Mt8OZfVNlJyHI-9mgHCuIk6mg6PP0oop-HTWAjZQ,942 +peft/tuners/shira/config.py,sha256=XEPT_LLADulAvim1HVv3P4z3i0A0VA1z00-3nav6fwY,6598 +peft/tuners/shira/layer.py,sha256=SJi5A3Us-MU9isQZtbeJx-KAP2dUJg14Nnqdkdw6q24,9151 +peft/tuners/shira/mask_functions.py,sha256=t9QCZFVICYPkSdU5jtk4nV8jgnlTT71WpsR_yYzCKwg,2985 +peft/tuners/shira/model.py,sha256=NalpFfrS52oguxpU7SOBA-IBGV6dUd-RXIxBoKOjC1g,12503 +peft/tuners/trainable_tokens/__init__.py,sha256=necElnvofPCAK6LNgN01FswZ2h-Zvde8W59bxULEtoU,1026 +peft/tuners/trainable_tokens/config.py,sha256=ZxxM71ZQbWpa4h2xHiRMCtJ0Z-Emohgu3UavrPjOGu0,4345 +peft/tuners/trainable_tokens/layer.py,sha256=xxI4Lh3kkphEJYgyy8DRgUHxTyM2mJYDCYAVg0RZvGg,10772 +peft/tuners/trainable_tokens/model.py,sha256=yCpsGFoIKXr274An-iqzY3az6BTnec28KXNWYYUbUrM,11755 +peft/tuners/tuners_utils.py,sha256=1bapLY8LUyWVFRnoHS7Z3ry6as4QKOiHlY3V5RiL5mo,67995 +peft/tuners/vblora/__init__.py,sha256=8vps9lYLOY5u82MTjhkv_6bXvkjeRPOfPsXc0lQYXn8,901 +peft/tuners/vblora/config.py,sha256=uRZcB4eAzW2gPorupyDyNGJRG8JmK6J-UkVmqhBEsfg,10572 +peft/tuners/vblora/layer.py,sha256=QV_bVxLE_4RFOv43IJYO1q64ENr3vbdAk1g9P_Z_Nnc,10929 +peft/tuners/vblora/model.py,sha256=oH_q_baAGEOeaVyjXDQIcWWygQ0B-ANExGQViyXlYB8,18457 +peft/tuners/vera/__init__.py,sha256=Wa_lRLZxdDSIbq7twa6hfh36BF5X7y5e6i4h4GSxfug,1320 +peft/tuners/vera/bnb.py,sha256=dcCSOX9mesyuMmQN99YbygsvxJEsH_PypioC6ABe_K0,16373 +peft/tuners/vera/config.py,sha256=5wB8jf5ydlpD0m5CX5kuTnfvgxeiY2qMUuADpVpc9p8,8093 +peft/tuners/vera/layer.py,sha256=oc6vGLLqZMz3tzNLsk1Av1V_xvTYKZbZw9Q3enLljkM,12136 +peft/tuners/vera/model.py,sha256=Bwy1aFRU4RVo1LPt4ABjfFtEUYgsGzYPGdIjx3o9v4I,20482 +peft/tuners/xlora/__init__.py,sha256=N7_cYjTq-KIaNMrHDksLz0vMczgKx9y6_3byEhDoxgQ,830 +peft/tuners/xlora/classifier.py,sha256=D2oOfxWLdqh2kluj9shfiUoJv1TdSM70jsZDkc7uJy0,7386 +peft/tuners/xlora/config.py,sha256=Erf64V-fe2aIQIBZwtYuc9JWjIepb-78o6IqVJMwzy8,4633 +peft/tuners/xlora/layer.py,sha256=5FM63-SrbP3rmch5Mzc01VUyVnimeONGyGI3FQsHCXk,9063 +peft/tuners/xlora/model.py,sha256=4Y-iPpDPiRVkjnNVdDXGGDZZEuzTxaiHpSnIlJjU1Go,20572 +peft/utils/__init__.py,sha256=uD6bWodXWkdhMfVmP78iG2pMFlMiDAafUVoSdESMgvc,4180 +peft/utils/constants.py,sha256=NyAbeO6wCfhiKl7rZm7MIODCqGpXYQU13jghYDFOmOI,15695 +peft/utils/hotswap.py,sha256=WF0ZzJkciyu5w1T7B_gkqgNFnNMLAiD1v1JzqV25N2k,26497 +peft/utils/incremental_pca.py,sha256=C6uNBqOfbc4eR_i2HXLjk8sxsqQ_bxh42pGXKRt_Xfs,14026 +peft/utils/integrations.py,sha256=95XbD8_lAv7FD47yJnAVYe0N0F4N7b-Lin4OhDAnnRE,10977 +peft/utils/loftq_utils.py,sha256=tvbEJuHYd_lJeosdAVMIifOsmzDg43IHJD3eDYX5VhQ,17528 +peft/utils/merge_utils.py,sha256=lnDh0aHvuPdIwq_c8jWY7aQAapPZVNFcaJgzxGZzCHg,9899 +peft/utils/other.py,sha256=mwzfPGB9H_xnnxevLouXcwezaFe4QQuwbORL_3GeC90,58867 +peft/utils/peft_types.py,sha256=LuIoVaQ2pJyCBhrcVz_nW70EDwjHloXD__l6kuPuJwM,5420 +peft/utils/save_and_load.py,sha256=hJKC8B5ptwVcxJnmfyec3j9IlCGuohL1n3AfRXVd4ms,34607 diff --git a/.venv/lib/python3.12/site-packages/peft-0.17.1.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/peft-0.17.1.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/peft-0.17.1.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/peft-0.17.1.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..370ec5f1aa3524bfd31a19fd21e907335f48a7f6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/peft-0.17.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (78.1.1) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.12/site-packages/peft-0.17.1.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/peft-0.17.1.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..dc89ba063e639dfa24fd7f53340bc368adbebb7a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/peft-0.17.1.dist-info/top_level.txt @@ -0,0 +1 @@ +peft diff --git a/.venv/lib/python3.12/site-packages/peft/__init__.py b/.venv/lib/python3.12/site-packages/peft/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9c6202c91d929161efa2a50359118bdf3de32f3b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/peft/__init__.py @@ -0,0 +1,222 @@ +# Copyright 2023-present the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +__version__ = "0.17.1" + +from .auto import ( + MODEL_TYPE_TO_PEFT_MODEL_MAPPING, + AutoPeftModel, + AutoPeftModelForCausalLM, + AutoPeftModelForFeatureExtraction, + AutoPeftModelForQuestionAnswering, + AutoPeftModelForSeq2SeqLM, + AutoPeftModelForSequenceClassification, + AutoPeftModelForTokenClassification, +) +from .config import PeftConfig, PromptLearningConfig +from .mapping import ( + PEFT_TYPE_TO_CONFIG_MAPPING, + PEFT_TYPE_TO_MIXED_MODEL_MAPPING, + PEFT_TYPE_TO_TUNER_MAPPING, + get_peft_config, + inject_adapter_in_model, +) +from .mapping_func import get_peft_model +from .mixed_model import PeftMixedModel +from .peft_model import ( + PeftModel, + PeftModelForCausalLM, + PeftModelForFeatureExtraction, + PeftModelForQuestionAnswering, + PeftModelForSeq2SeqLM, + PeftModelForSequenceClassification, + PeftModelForTokenClassification, + get_layer_status, + get_model_status, +) +from .tuners import ( + AdaLoraConfig, + AdaLoraModel, + AdaptionPromptConfig, + AdaptionPromptModel, + BOFTConfig, + BOFTModel, + BoneConfig, + BoneModel, + C3AConfig, + C3AModel, + CPTConfig, + CPTEmbedding, + EvaConfig, + FourierFTConfig, + FourierFTModel, + HRAConfig, + HRAModel, + IA3Config, + IA3Model, + LNTuningConfig, + LNTuningModel, + LoftQConfig, + LoHaConfig, + LoHaModel, + LoKrConfig, + LoKrModel, + LoraConfig, + LoraModel, + LoraRuntimeConfig, + MissConfig, + MissModel, + MultitaskPromptTuningConfig, + MultitaskPromptTuningInit, + OFTConfig, + OFTModel, + PolyConfig, + PolyModel, + PrefixEncoder, + PrefixTuningConfig, + PromptEmbedding, + PromptEncoder, + PromptEncoderConfig, + PromptEncoderReparameterizationType, + PromptTuningConfig, + PromptTuningInit, + RandLoraConfig, + RandLoraModel, + ShiraConfig, + ShiraModel, + TrainableTokensConfig, + TrainableTokensModel, + VBLoRAConfig, + VBLoRAModel, + VeraConfig, + VeraModel, + XLoraConfig, + XLoraModel, + get_eva_state_dict, + initialize_lora_eva_weights, +) +from .utils import ( + TRANSFORMERS_MODELS_TO_PREFIX_TUNING_POSTPROCESS_MAPPING, + PeftType, + TaskType, + bloom_model_postprocess_past_key_value, + cast_mixed_precision_params, + get_peft_model_state_dict, + load_peft_weights, + prepare_model_for_kbit_training, + replace_lora_weights_loftq, + set_peft_model_state_dict, + shift_tokens_right, +) + + +__all__ = [ + "MODEL_TYPE_TO_PEFT_MODEL_MAPPING", + "PEFT_TYPE_TO_CONFIG_MAPPING", + "PEFT_TYPE_TO_MIXED_MODEL_MAPPING", + "PEFT_TYPE_TO_TUNER_MAPPING", + "TRANSFORMERS_MODELS_TO_PREFIX_TUNING_POSTPROCESS_MAPPING", + "AdaLoraConfig", + "AdaLoraModel", + "AdaptionPromptConfig", + "AdaptionPromptModel", + "AutoPeftModel", + "AutoPeftModelForCausalLM", + "AutoPeftModelForFeatureExtraction", + "AutoPeftModelForQuestionAnswering", + "AutoPeftModelForSeq2SeqLM", + "AutoPeftModelForSequenceClassification", + "AutoPeftModelForTokenClassification", + "BOFTConfig", + "BOFTModel", + "BoneConfig", + "BoneModel", + "C3AConfig", + "C3AModel", + "CPTConfig", + "CPTEmbedding", + "EvaConfig", + "FourierFTConfig", + "FourierFTModel", + "HRAConfig", + "HRAModel", + "IA3Config", + "IA3Model", + "LNTuningConfig", + "LNTuningModel", + "LoHaConfig", + "LoHaModel", + "LoKrConfig", + "LoKrModel", + "LoftQConfig", + "LoraConfig", + "LoraModel", + "LoraRuntimeConfig", + "MissConfig", + "MissModel", + "MultitaskPromptTuningConfig", + "MultitaskPromptTuningInit", + "OFTConfig", + "OFTModel", + "PeftConfig", + "PeftMixedModel", + "PeftModel", + "PeftModelForCausalLM", + "PeftModelForFeatureExtraction", + "PeftModelForQuestionAnswering", + "PeftModelForSeq2SeqLM", + "PeftModelForSequenceClassification", + "PeftModelForTokenClassification", + "PeftType", + "PolyConfig", + "PolyModel", + "PrefixEncoder", + "PrefixTuningConfig", + "PromptEmbedding", + "PromptEncoder", + "PromptEncoderConfig", + "PromptEncoderReparameterizationType", + "PromptLearningConfig", + "PromptTuningConfig", + "PromptTuningInit", + "RandLoraConfig", + "RandLoraModel", + "ShiraConfig", + "ShiraModel", + "TaskType", + "TrainableTokensConfig", + "TrainableTokensModel", + "VBLoRAConfig", + "VBLoRAConfig", + "VBLoRAModel", + "VeraConfig", + "VeraModel", + "XLoraConfig", + "XLoraModel", + "bloom_model_postprocess_past_key_value", + "cast_mixed_precision_params", + "get_eva_state_dict", + "get_layer_status", + "get_model_status", + "get_peft_config", + "get_peft_model", + "get_peft_model_state_dict", + "initialize_lora_eva_weights", + "inject_adapter_in_model", + "load_peft_weights", + "prepare_model_for_kbit_training", + "replace_lora_weights_loftq", + "set_peft_model_state_dict", + "shift_tokens_right", +] diff --git a/.venv/lib/python3.12/site-packages/peft/auto.py b/.venv/lib/python3.12/site-packages/peft/auto.py new file mode 100644 index 0000000000000000000000000000000000000000..613f67c707e344eab1a5281565fb0fdb3d827d01 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/peft/auto.py @@ -0,0 +1,184 @@ +# Copyright 2023-present the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import importlib +import os +from typing import Optional + +from transformers import ( + AutoModel, + AutoModelForCausalLM, + AutoModelForQuestionAnswering, + AutoModelForSeq2SeqLM, + AutoModelForSequenceClassification, + AutoModelForTokenClassification, + AutoTokenizer, +) + +from .config import PeftConfig +from .peft_model import ( + PeftModel, + PeftModelForCausalLM, + PeftModelForFeatureExtraction, + PeftModelForQuestionAnswering, + PeftModelForSeq2SeqLM, + PeftModelForSequenceClassification, + PeftModelForTokenClassification, +) +from .utils.constants import TOKENIZER_CONFIG_NAME +from .utils.other import check_file_exists_on_hf_hub + + +MODEL_TYPE_TO_PEFT_MODEL_MAPPING: dict[str, type[PeftModel]] = { + "SEQ_CLS": PeftModelForSequenceClassification, + "SEQ_2_SEQ_LM": PeftModelForSeq2SeqLM, + "CAUSAL_LM": PeftModelForCausalLM, + "TOKEN_CLS": PeftModelForTokenClassification, + "QUESTION_ANS": PeftModelForQuestionAnswering, + "FEATURE_EXTRACTION": PeftModelForFeatureExtraction, +} + + +class _BaseAutoPeftModel: + _target_class = None + _target_peft_class = None + + def __init__(self, *args, **kwargs): + # For consistency with transformers: https://github.com/huggingface/transformers/blob/91d7df58b6537d385e90578dac40204cb550f706/src/transformers/models/auto/auto_factory.py#L400 + raise EnvironmentError( # noqa: UP024 + f"{self.__class__.__name__} is designed to be instantiated " + f"using the `{self.__class__.__name__}.from_pretrained(pretrained_model_name_or_path)` or " + f"`{self.__class__.__name__}.from_config(config)` methods." + ) + + @classmethod + def from_pretrained( + cls, + pretrained_model_name_or_path, + adapter_name: str = "default", + is_trainable: bool = False, + config: Optional[PeftConfig] = None, + revision: Optional[str] = None, + **kwargs, + ): + r""" + A wrapper around all the preprocessing steps a user needs to perform in order to load a PEFT model. The kwargs + are passed along to `PeftConfig` that automatically takes care of filtering the kwargs of the Hub methods and + the config object init. + """ + peft_config = PeftConfig.from_pretrained(pretrained_model_name_or_path, revision=revision, **kwargs) + base_model_path = peft_config.base_model_name_or_path + base_model_revision = peft_config.revision + + task_type = getattr(peft_config, "task_type", None) + + if cls._target_class is not None: + target_class = cls._target_class + elif cls._target_class is None and task_type is not None: + # this is only in the case where we use `AutoPeftModel` + raise ValueError( + "Cannot use `AutoPeftModel` with a task type, please use a specific class for your task type. (e.g. `AutoPeftModelForCausalLM` for `task_type='CAUSAL_LM'`)" + ) + + if task_type is not None: + expected_target_class = MODEL_TYPE_TO_PEFT_MODEL_MAPPING[task_type] + if cls._target_peft_class.__name__ != expected_target_class.__name__: + raise ValueError( + f"Expected target PEFT class: {expected_target_class.__name__}, but you have asked for: {cls._target_peft_class.__name__}" + " make sure that you are loading the correct model for your task type." + ) + elif task_type is None and getattr(peft_config, "auto_mapping", None) is not None: + auto_mapping = getattr(peft_config, "auto_mapping", None) + base_model_class = auto_mapping["base_model_class"] + parent_library_name = auto_mapping["parent_library"] + + parent_library = importlib.import_module(parent_library_name) + target_class = getattr(parent_library, base_model_class) + else: + raise ValueError( + "Cannot infer the auto class from the config, please make sure that you are loading the correct model for your task type." + ) + + base_model = target_class.from_pretrained(base_model_path, revision=base_model_revision, **kwargs) + + tokenizer_exists = False + if os.path.exists(os.path.join(pretrained_model_name_or_path, TOKENIZER_CONFIG_NAME)): + tokenizer_exists = True + else: + token = kwargs.get("token", None) + if token is None: + token = kwargs.get("use_auth_token", None) + + tokenizer_exists = check_file_exists_on_hf_hub( + repo_id=pretrained_model_name_or_path, + filename=TOKENIZER_CONFIG_NAME, + revision=revision, + repo_type=kwargs.get("repo_type", None), + token=token, + ) + + if tokenizer_exists and hasattr(base_model, "get_input_embeddings"): + tokenizer = AutoTokenizer.from_pretrained( + pretrained_model_name_or_path, trust_remote_code=kwargs.get("trust_remote_code", False) + ) + embedding_size = base_model.get_input_embeddings().weight.shape[0] + if len(tokenizer) > embedding_size: + # only resize if the tokenizer has a larger vocab size than there are embeddings + base_model.resize_token_embeddings(len(tokenizer)) + + return cls._target_peft_class.from_pretrained( + base_model, + pretrained_model_name_or_path, + adapter_name=adapter_name, + is_trainable=is_trainable, + config=config, + **kwargs, + ) + + +class AutoPeftModel(_BaseAutoPeftModel): + _target_class = None + _target_peft_class = PeftModel + + +class AutoPeftModelForCausalLM(_BaseAutoPeftModel): + _target_class = AutoModelForCausalLM + _target_peft_class = PeftModelForCausalLM + + +class AutoPeftModelForSeq2SeqLM(_BaseAutoPeftModel): + _target_class = AutoModelForSeq2SeqLM + _target_peft_class = PeftModelForSeq2SeqLM + + +class AutoPeftModelForSequenceClassification(_BaseAutoPeftModel): + _target_class = AutoModelForSequenceClassification + _target_peft_class = PeftModelForSequenceClassification + + +class AutoPeftModelForTokenClassification(_BaseAutoPeftModel): + _target_class = AutoModelForTokenClassification + _target_peft_class = PeftModelForTokenClassification + + +class AutoPeftModelForQuestionAnswering(_BaseAutoPeftModel): + _target_class = AutoModelForQuestionAnswering + _target_peft_class = PeftModelForQuestionAnswering + + +class AutoPeftModelForFeatureExtraction(_BaseAutoPeftModel): + _target_class = AutoModel + _target_peft_class = PeftModelForFeatureExtraction diff --git a/.venv/lib/python3.12/site-packages/peft/config.py b/.venv/lib/python3.12/site-packages/peft/config.py new file mode 100644 index 0000000000000000000000000000000000000000..094ee709400e659468cf5c87b682f16aae2197e7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/peft/config.py @@ -0,0 +1,353 @@ +# Copyright 2023-present the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import inspect +import json +import os +import warnings +from dataclasses import asdict, dataclass, field +from typing import Optional, Union + +from huggingface_hub import hf_hub_download +from transformers.utils import PushToHubMixin, http_user_agent + +from .utils import CONFIG_NAME, PeftType, TaskType + + +# we expect at least these keys to be present in a PEFT adapter_config.json +MIN_EXPECTED_CONFIG_KEYS = {"peft_type"} + + +def _check_and_remove_unused_kwargs(cls, kwargs): + """Make PEFT configs forward-compatible by removing unused kwargs that were added in later PEFT versions. + + This assumes that removing the unused kwargs will not affect the default behavior. + + Returns the filtered kwargs and the set of removed keys. + """ + # it's not pretty but eh + signature_parameters = inspect.signature(cls.__init__).parameters + unexpected_kwargs = set(kwargs.keys()) - set(signature_parameters.keys()) + for key in unexpected_kwargs: + del kwargs[key] + return kwargs, unexpected_kwargs + + +@dataclass +class PeftConfigMixin(PushToHubMixin): + r""" + This is the base configuration class for PEFT adapter models. It contains all the methods that are common to all + PEFT adapter models. This class inherits from [`~transformers.utils.PushToHubMixin`] which contains the methods to + push your model to the Hub. The method `save_pretrained` will save the configuration of your adapter model in a + directory. The method `from_pretrained` will load the configuration of your adapter model from a directory. + + Args: + peft_type (Union[[`~peft.utils.config.PeftType`], `str`]): The type of Peft method to use. + """ + + task_type: Optional[TaskType] = field(default=None, metadata={"help": "The type of task."}) + peft_type: Optional[PeftType] = field(default=None, metadata={"help": "The type of PEFT model."}) + auto_mapping: Optional[dict] = field( + default=None, metadata={"help": "An auto mapping dict to help retrieve the base model class if needed."} + ) + + def __post_init__(self): + # check for invalid task type + if (self.task_type is not None) and (self.task_type not in list(TaskType)): + raise ValueError( + f"Invalid task type: '{self.task_type}'. Must be one of the following task types: {', '.join(TaskType)}." + ) + + def to_dict(self) -> dict: + r""" + Returns the configuration for your adapter model as a dictionary. + """ + return asdict(self) + + def save_pretrained(self, save_directory: str, **kwargs) -> None: + r""" + This method saves the configuration of your adapter model in a directory. + + Args: + save_directory (`str`): + The directory where the configuration will be saved. + kwargs (additional keyword arguments, *optional*): + Additional keyword arguments passed along to the [`~transformers.utils.PushToHubMixin.push_to_hub`] + method. + """ + if os.path.isfile(save_directory): + raise AssertionError(f"Provided path ({save_directory}) should be a directory, not a file") + + os.makedirs(save_directory, exist_ok=True) + auto_mapping_dict = kwargs.pop("auto_mapping_dict", None) + + output_dict = self.to_dict() + # converting set type to list + for key, value in output_dict.items(): + if isinstance(value, set): + output_dict[key] = list(value) + + output_path = os.path.join(save_directory, CONFIG_NAME) + + # Add auto mapping details for custom models. + if auto_mapping_dict is not None: + output_dict["auto_mapping"] = auto_mapping_dict + + # save it + with open(output_path, "w") as writer: + writer.write(json.dumps(output_dict, indent=2, sort_keys=True)) + + @classmethod + def from_peft_type(cls, **kwargs): + r""" + This method loads the configuration of your adapter model from a set of kwargs. + + The appropriate configuration type is determined by the `peft_type` argument. If `peft_type` is not provided, + the calling class type is instantiated. + + Args: + kwargs (configuration keyword arguments): + Keyword arguments passed along to the configuration initialization. + """ + # Avoid circular dependency .. TODO: fix this with a larger refactor + from peft.mapping import PEFT_TYPE_TO_CONFIG_MAPPING + + # TODO: this hack is needed to fix the following issue (on commit 702f937): + # if someone saves a default config and loads it back with `PeftConfig` class it yields to + # not loading the correct config class. + # + # from peft import AdaLoraConfig, PeftConfig + # peft_config = AdaLoraConfig() + # print(peft_config) + # >>> AdaLoraConfig(peft_type=, auto_mapping=None, base_model_name_or_path=None, + # revision=None, task_type=None, inference_mode=False, r=8, target_modules=None, lora_alpha=8, lora_dropout=0.0, ... + # + # peft_config.save_pretrained("./test_config") + # peft_config = PeftConfig.from_pretrained("./test_config") + # print(peft_config) + # >>> PeftConfig(peft_type='ADALORA', auto_mapping=None, base_model_name_or_path=None, revision=None, task_type=None, inference_mode=False) + + if "peft_type" in kwargs: + peft_type = kwargs["peft_type"] + config_cls = PEFT_TYPE_TO_CONFIG_MAPPING[peft_type] + else: + config_cls = cls + + try: + config = config_cls(**kwargs) + except TypeError as exc: + # Here we potentially handle forward compatibility. Sometimes new keywords are added to configs, which makes + # new configs incompatible with older PEFT versions. We catch these and remove them to allow the program to + # continue, but warn the user about it. + + # First check if the error is due to unexpected keyword arguments, we don't want to accidentally catch + # other TypeErrors. + if "got an unexpected keyword argument" not in str(exc): + raise exc + + filtered_kwargs, unexpected_kwargs = _check_and_remove_unused_kwargs(config_cls, kwargs) + if not MIN_EXPECTED_CONFIG_KEYS.issubset(set(filtered_kwargs.keys())): + raise TypeError( + f"The {cls.__name__} config that is trying to be loaded is missing required keys: " + f"{MIN_EXPECTED_CONFIG_KEYS}." + ) + + warnings.warn( + f"Unexpected keyword arguments {sorted(unexpected_kwargs)} for class {config_cls.__name__}, these are " + "ignored. This probably means that you're loading a configuration file that was saved using a " + "higher version of the library and additional parameters have been introduced since. It is " + "highly recommended to upgrade the PEFT version before continuing (e.g. by running `pip install " + "-U peft`)." + ) + config = config_cls.from_peft_type(**filtered_kwargs) + return config + + @classmethod + def from_pretrained(cls, pretrained_model_name_or_path: str, subfolder: Optional[str] = None, **kwargs): + r""" + This method loads the configuration of your adapter model from a directory. + + Args: + pretrained_model_name_or_path (`str`): + The directory or the Hub repository id where the configuration is saved. + kwargs (additional keyword arguments, *optional*): + Additional keyword arguments passed along to the child class initialization. + """ + path = ( + os.path.join(pretrained_model_name_or_path, subfolder) + if subfolder is not None + else pretrained_model_name_or_path + ) + + hf_hub_download_kwargs, class_kwargs, _ = cls._split_kwargs(kwargs) + if "user_agent" not in hf_hub_download_kwargs: + hf_hub_download_kwargs["user_agent"] = http_user_agent() + + if os.path.isfile(os.path.join(path, CONFIG_NAME)): + config_file = os.path.join(path, CONFIG_NAME) + else: + try: + config_file = hf_hub_download( + pretrained_model_name_or_path, CONFIG_NAME, subfolder=subfolder, **hf_hub_download_kwargs + ) + except Exception as exc: + raise ValueError(f"Can't find '{CONFIG_NAME}' at '{pretrained_model_name_or_path}'") from exc + + loaded_attributes = cls.from_json_file(config_file) + kwargs = {**class_kwargs, **loaded_attributes} + kwargs = cls.check_kwargs(**kwargs) + return cls.from_peft_type(**kwargs) + + @classmethod + def from_json_file(cls, path_json_file: str, **kwargs): + r""" + Loads a configuration file from a json file. + + Args: + path_json_file (`str`): + The path to the json file. + """ + with open(path_json_file) as file: + json_object = json.load(file) + + # Sanity check that config does not contain a runtime_config + if "runtime_config" in json_object: + warnings.warn( + "The configuration file contains a `runtime_config` key. This is ignored. Runtime configurations are only valid at runtime." + ) + del json_object["runtime_config"] + + return json_object + + @classmethod + def _split_kwargs(cls, kwargs): + hf_hub_download_kwargs = {} + class_kwargs = {} + other_kwargs = {} + + for key, value in kwargs.items(): + if key in inspect.signature(hf_hub_download).parameters: + hf_hub_download_kwargs[key] = value + elif key in list(cls.__annotations__): + class_kwargs[key] = value + else: + other_kwargs[key] = value + + return hf_hub_download_kwargs, class_kwargs, other_kwargs + + @classmethod + def _get_peft_type( + cls, + model_id: str, + **hf_hub_download_kwargs, + ): + subfolder = hf_hub_download_kwargs.get("subfolder", None) + + path = os.path.join(model_id, subfolder) if subfolder is not None else model_id + + if os.path.isfile(os.path.join(path, CONFIG_NAME)): + config_file = os.path.join(path, CONFIG_NAME) + else: + try: + config_file = hf_hub_download( + model_id, + CONFIG_NAME, + **hf_hub_download_kwargs, + ) + except Exception: + raise ValueError(f"Can't find '{CONFIG_NAME}' at '{model_id}'") + + loaded_attributes = cls.from_json_file(config_file) + return loaded_attributes["peft_type"] + + @classmethod + def check_kwargs(cls, **kwargs): + """Check kwargs before initializing the config instance. + + Subclasses can override this method to add specific checks. + + """ + return kwargs + + @property + def is_prompt_learning(self) -> bool: + r""" + Utility method to check if the configuration is for prompt learning. + """ + return False + + @property + def is_adaption_prompt(self) -> bool: + """Return True if this is an adaption prompt config.""" + return False + + +@dataclass +class PeftConfig(PeftConfigMixin): + """ + This is the base configuration class to store the configuration of a [`PeftModel`]. + + Args: + peft_type (Union[[`~peft.utils.config.PeftType`], `str`]): The type of Peft method to use. + task_type (Union[[`~peft.utils.config.TaskType`], `str`]): The type of task to perform. + inference_mode (`bool`, defaults to `False`): Whether to use the Peft model in inference mode. + """ + + base_model_name_or_path: Optional[str] = field( + default=None, metadata={"help": "The name of the base model to use."} + ) + revision: Optional[str] = field(default=None, metadata={"help": "The specific base model version to use."}) + peft_type: Optional[Union[str, PeftType]] = field(default=None, metadata={"help": "Peft type"}) + task_type: Optional[Union[str, TaskType]] = field(default=None, metadata={"help": "Task type"}) + inference_mode: bool = field(default=False, metadata={"help": "Whether to use inference mode"}) + + +@dataclass +class PromptLearningConfig(PeftConfig): + """ + This is the base configuration class to store the configuration of [`PrefixTuning`], [`PromptEncoder`], or + [`PromptTuning`]. + + Args: + num_virtual_tokens (`int`): The number of virtual tokens to use. + token_dim (`int`): The hidden embedding dimension of the base transformer model. + num_transformer_submodules (`int`): The number of transformer submodules in the base transformer model. + num_attention_heads (`int`): The number of attention heads in the base transformer model. + num_layers (`int`): The number of layers in the base transformer model. + """ + + num_virtual_tokens: int = field(default=None, metadata={"help": "Number of virtual tokens"}) + token_dim: int = field( + default=None, metadata={"help": "The hidden embedding dimension of the base transformer model"} + ) + num_transformer_submodules: Optional[int] = field( + default=None, metadata={"help": "Number of transformer submodules"} + ) + num_attention_heads: Optional[int] = field(default=None, metadata={"help": "Number of attention heads"}) + num_layers: Optional[int] = field(default=None, metadata={"help": "Number of transformer layers"}) + modules_to_save: Optional[list[str]] = field( + default=None, + metadata={ + "help": "List of extra modules to be set as trainable and saved in the final checkpoint. " + "For example, in Sequence Classification or Token Classification tasks, " + "the final layer `classifier/score` are randomly initialized and as such need to be trainable and saved. " + "The module(s) will be fully fine-tuned." + }, + ) + + @property + def is_prompt_learning(self) -> bool: + r""" + Utility method to check if the configuration is for prompt learning. + """ + return True diff --git a/.venv/lib/python3.12/site-packages/peft/helpers.py b/.venv/lib/python3.12/site-packages/peft/helpers.py new file mode 100644 index 0000000000000000000000000000000000000000..d748c62e696d57034e4e9fd6458b27febbd9c90c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/peft/helpers.py @@ -0,0 +1,251 @@ +# Copyright 2023-present the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import inspect +from contextlib import contextmanager +from copy import deepcopy +from functools import update_wrapper +from types import MethodType + +from torch import nn + +from .peft_model import PeftConfig, PeftModel +from .tuners.lora import LoraLayer +from .tuners.tuners_utils import BaseTunerLayer + + +def update_forward_signature(model: PeftModel) -> None: + """ + Updates the forward signature of the PeftModel to include parents class signature + model (`PeftModel`): Peft model to update the forward signature + + Example: + + ```python + >>> from transformers import WhisperForConditionalGeneration + >>> from peft import get_peft_model, LoraConfig, update_forward_signature + + >>> model = WhisperForConditionalGeneration.from_pretrained("openai/whisper-tiny.en") + >>> peft_config = LoraConfig(r=8, lora_alpha=32, lora_dropout=0.1, target_modules=["q_proj", "v_proj"]) + + >>> peft_model = get_peft_model(model, peft_config) + >>> update_forward_signature(peft_model) + ``` + """ + + # Only update signature when the current forward signature only has *args and **kwargs + current_signature = inspect.signature(model.forward) + if ( + len(current_signature.parameters) == 2 + and "args" in current_signature.parameters + and "kwargs" in current_signature.parameters + ): + forward = deepcopy(model.forward.__func__) + update_wrapper( + forward, type(model.get_base_model()).forward, assigned=("__doc__", "__name__", "__annotations__") + ) + model.forward = MethodType(forward, model) + + +def update_generate_signature(model: PeftModel) -> None: + """ + Updates the generate signature of a PeftModel with overriding generate to include parents class signature + model (`PeftModel`): Peft model to update the generate signature + + Example: + + ```python + >>> from transformers import AutoModelForSeq2SeqLM, AutoTokenizer + >>> from peft import get_peft_model, LoraConfig, TaskType, update_generate_signature + + >>> model_name_or_path = "bigscience/mt0-large" + >>> tokenizer = AutoTokenizer.from_pretrained(model_name_or_path) + >>> model = AutoModelForSeq2SeqLM.from_pretrained(model_name_or_path) + + >>> peft_config = LoraConfig( + ... task_type=TaskType.SEQ_2_SEQ_LM, inference_mode=False, r=8, lora_alpha=32, lora_dropout=0.1 + ... ) + >>> peft_model = get_peft_model(model, peft_config) + >>> update_generate_signature(peft_model) + >>> help(peft_model.generate) + ``` + """ + if not hasattr(model, "generate"): + return + current_signature = inspect.signature(model.generate) + if ( + len(current_signature.parameters) == 2 + and "args" in current_signature.parameters + and "kwargs" in current_signature.parameters + ) or (len(current_signature.parameters) == 1 and "kwargs" in current_signature.parameters): + generate = deepcopy(model.generate.__func__) + update_wrapper( + generate, + type(model.get_base_model()).generate, + assigned=("__doc__", "__name__", "__annotations__"), + ) + model.generate = MethodType(generate, model) + + +def update_signature(model: PeftModel, method: str = "all") -> None: + """ + Updates the signature of a PeftModel include parents class signature for forward or generate method + model (`PeftModel`): Peft model to update generate or forward signature method (`str`): method to update + signature choose one of "forward", "generate", "all" + + Example: + ```python + >>> from transformers import AutoModelForSeq2SeqLM, AutoTokenizer + >>> from peft import get_peft_model, LoraConfig, TaskType, update_signature + + >>> model_name_or_path = "bigscience/mt0-large" + >>> tokenizer = AutoTokenizer.from_pretrained(model_name_or_path) + >>> model = AutoModelForSeq2SeqLM.from_pretrained(model_name_or_path) + + >>> peft_config = LoraConfig( + ... task_type=TaskType.SEQ_2_SEQ_LM, inference_mode=False, r=8, lora_alpha=32, lora_dropout=0.1 + ... ) + >>> peft_model = get_peft_model(model, peft_config) + >>> update_signature(peft_model) + >>> help(peft_model.generate) + ``` + """ + if method == "forward": + update_forward_signature(model) + elif method == "generate": + update_generate_signature(model) + elif method == "all": + update_forward_signature(model) + update_generate_signature(model) + else: + raise ValueError(f"method {method} is not supported please choose one of ['forward', 'generate', 'all']") + + +def check_if_peft_model(model_name_or_path: str) -> bool: + """ + Check if the model is a PEFT model. + + Args: + model_name_or_path (`str`): + Model id to check, can be local or on the Hugging Face Hub. + + Returns: + `bool`: True if the model is a PEFT model, False otherwise. + """ + is_peft_model = True + try: + PeftConfig.from_pretrained(model_name_or_path) + except Exception: + # allow broad exceptions so that this works even if new exceptions are added on HF Hub side + is_peft_model = False + + return is_peft_model + + +@contextmanager +def rescale_adapter_scale(model, multiplier): + """ + Context manager to temporarily rescale the scaling of the LoRA adapter in a model. + + The original scaling values are restored when the context manager exits. This context manager works with the + transformers and diffusers models that have directly loaded LoRA adapters. + + For LoRA, applying this context manager with multiplier in [0, 1] is strictly equivalent to applying + [wise-ft](https://huggingface.co/papers/2109.01903) (see [#1940](https://github.com/huggingface/peft/issues/1940) + for details). It can improve the performances of the model if there is a distribution shiftbetween the training + data used for fine-tuning, and the test data used during inference. + + Warning: It has been reported that when using Apple's MPS backend for PyTorch, it is necessary to add a short sleep + time after exiting the context before the scales are fully restored. + + Args: + model: The model containing `LoraLayer` modules whose scaling is to be adjusted. + multiplier (float or int): + The multiplier that rescales the `scaling` attribute. Must be of type float or int. + + Raises: + ValueError: If the model does not contain any `LoraLayer` + instances, indicating that the model does not support scaling. + + Example: + + ```python + >>> model = ModelWithLoraLayer() + >>> multiplier = 0.5 + >>> with rescale_adapter_scale(model, multiplier): + ... outputs = model(**inputs) # Perform operations with the scaled model + >>> outputs = model(**inputs) # The original scaling values are restored here + ``` + """ + # check if multiplier has a valid data type + if not isinstance(multiplier, (float, int)): + raise TypeError(f"Argument multiplier should be of type float, got {type(multiplier)}") + + # iterate on the model's modules and grab the original scaling attribute + # from the lora layers if present + original_scaling = {} + for module in model.modules(): + if isinstance(module, LoraLayer): + original_scaling[module] = module.scaling.copy() + module.scaling = {k: v * multiplier for k, v in module.scaling.items()} + + # check whether scaling is prohibited on model + # the original scaling dictionary should be empty + # if there were no lora layers + if not original_scaling: + raise ValueError("scaling is only supported for models with `LoraLayer`s") + try: + yield + + finally: + # restore original scaling values after exiting the context + for module, scaling in original_scaling.items(): + module.scaling = scaling + + +@contextmanager +def disable_input_dtype_casting(model: nn.Module, active: bool = True): + """ + Context manager disables input dtype casting to the dtype of the weight. + + Parameters: + model (nn.Module): + The model containing PEFT modules whose input dtype casting is to be adjusted. + active (bool): + Whether the context manager is active (default) or inactive. + + """ + # Additional info: Normally, the dtype of the weight and input need to match, which is why the dtype is cast. + # However, in certain circumustances, this is handled by forward hooks, e.g. when using layerwise casting in + # diffusers. In that case, PEFT casting the dtype interferes with the layerwise casting, which is why the option to + # disable it is given. + if not active: + yield + return + + original_values = {} + for name, module in model.named_modules(): + if not isinstance(module, BaseTunerLayer): + continue + original_values[name] = module.cast_input_dtype_enabled + module.cast_input_dtype_enabled = False + + try: + yield + finally: + for name, module in model.named_modules(): + if not isinstance(module, BaseTunerLayer): + continue + if name in original_values: + module.cast_input_dtype_enabled = original_values[name] diff --git a/.venv/lib/python3.12/site-packages/peft/import_utils.py b/.venv/lib/python3.12/site-packages/peft/import_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..6aa69a85190bc91bb8bd0649fa80806d7209c584 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/peft/import_utils.py @@ -0,0 +1,172 @@ +# Copyright 2023-present the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import importlib +import importlib.metadata as importlib_metadata +import platform +from functools import lru_cache + +import packaging.version +import torch + + +@lru_cache +def is_bnb_available() -> bool: + return importlib.util.find_spec("bitsandbytes") is not None + + +@lru_cache +def is_bnb_4bit_available() -> bool: + if not is_bnb_available(): + return False + + import bitsandbytes as bnb + + return hasattr(bnb.nn, "Linear4bit") + + +@lru_cache +def is_auto_gptq_available(): + if importlib.util.find_spec("auto_gptq") is not None: + AUTOGPTQ_MINIMUM_VERSION = packaging.version.parse("0.5.0") + version_autogptq = packaging.version.parse(importlib_metadata.version("auto_gptq")) + if AUTOGPTQ_MINIMUM_VERSION <= version_autogptq: + return True + else: + raise ImportError( + f"Found an incompatible version of auto-gptq. Found version {version_autogptq}, " + f"but only versions above {AUTOGPTQ_MINIMUM_VERSION} are supported" + ) + + +@lru_cache +def is_gptqmodel_available(): + if importlib.util.find_spec("gptqmodel") is not None: + GPTQMODEL_MINIMUM_VERSION = packaging.version.parse("2.0.0") + OPTIMUM_MINIMUM_VERSION = packaging.version.parse("1.24.0") + version_gptqmodel = packaging.version.parse(importlib_metadata.version("gptqmodel")) + if GPTQMODEL_MINIMUM_VERSION <= version_gptqmodel: + if is_optimum_available(): + version_optimum = packaging.version.parse(importlib_metadata.version("optimum")) + if OPTIMUM_MINIMUM_VERSION <= version_optimum: + return True + else: + raise ImportError( + f"gptqmodel requires optimum version `{OPTIMUM_MINIMUM_VERSION}` or higher. Found version `{version_optimum}`, " + f"but only versions above `{OPTIMUM_MINIMUM_VERSION}` are supported" + ) + else: + raise ImportError( + f"gptqmodel requires optimum version `{OPTIMUM_MINIMUM_VERSION}` or higher to be installed." + ) + else: + raise ImportError( + f"Found an incompatible version of gptqmodel. Found version `{version_gptqmodel}`, " + f"but only versions above `{GPTQMODEL_MINIMUM_VERSION}` are supported" + ) + + +@lru_cache +def is_optimum_available() -> bool: + return importlib.util.find_spec("optimum") is not None + + +@lru_cache +def is_torch_tpu_available(check_device=True): + "Checks if `torch_xla` is installed and potentially if a TPU is in the environment" + if importlib.util.find_spec("torch_xla") is not None: + if check_device: + # We need to check if `xla_device` can be found, will raise a RuntimeError if not + try: + import torch_xla.core.xla_model as xm + + _ = xm.xla_device() + return True + except RuntimeError: + return False + return True + return False + + +@lru_cache +def is_aqlm_available(): + return importlib.util.find_spec("aqlm") is not None + + +@lru_cache +def is_auto_awq_available(): + return importlib.util.find_spec("awq") is not None + + +@lru_cache +def is_eetq_available(): + return importlib.util.find_spec("eetq") is not None + + +@lru_cache +def is_hqq_available(): + return importlib.util.find_spec("hqq") is not None + + +@lru_cache +def is_inc_available(): + return importlib.util.find_spec("neural_compressor") is not None + + +@lru_cache +def is_torchao_available(): + if importlib.util.find_spec("torchao") is None: + return False + + TORCHAO_MINIMUM_VERSION = packaging.version.parse("0.4.0") + try: + torchao_version = packaging.version.parse(importlib_metadata.version("torchao")) + except importlib_metadata.PackageNotFoundError: + # Same idea as in diffusers: + # https://github.com/huggingface/diffusers/blob/9f06a0d1a4a998ac6a463c5be728c892f95320a8/src/diffusers/utils/import_utils.py#L351-L357 + # It's not clear under what circumstances `importlib_metadata.version("torchao")` can raise an error even + # though `importlib.util.find_spec("torchao") is not None` but it has been observed, so adding this for + # precaution. + return False + + if torchao_version < TORCHAO_MINIMUM_VERSION: + raise ImportError( + f"Found an incompatible version of torchao. Found version {torchao_version}, " + f"but only versions above {TORCHAO_MINIMUM_VERSION} are supported" + ) + return True + + +@lru_cache +def is_xpu_available(check_device=False): + """ + Checks if XPU acceleration is available and potentially if a XPU is in the environment + """ + + system = platform.system() + if system == "Darwin": + return False + else: + if check_device: + try: + # Will raise a RuntimeError if no XPU is found + _ = torch.xpu.device_count() + return torch.xpu.is_available() + except RuntimeError: + return False + return hasattr(torch, "xpu") and torch.xpu.is_available() + + +@lru_cache +def is_diffusers_available(): + return importlib.util.find_spec("diffusers") is not None diff --git a/.venv/lib/python3.12/site-packages/peft/mapping.py b/.venv/lib/python3.12/site-packages/peft/mapping.py new file mode 100644 index 0000000000000000000000000000000000000000..70db29df5a8c7ae1de3962f572061fdd540e2dcf --- /dev/null +++ b/.venv/lib/python3.12/site-packages/peft/mapping.py @@ -0,0 +1,89 @@ +# Copyright 2023-present the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Optional + +import torch + +from .utils import PeftType + + +if TYPE_CHECKING: + from .config import PeftConfig + from .tuners.tuners_utils import BaseTuner + + +# these will be filled by the register_peft_method function +PEFT_TYPE_TO_CONFIG_MAPPING: dict[PeftType, type[PeftConfig]] = {} +PEFT_TYPE_TO_TUNER_MAPPING: dict[PeftType, type[BaseTuner]] = {} +PEFT_TYPE_TO_MIXED_MODEL_MAPPING: dict[PeftType, type[BaseTuner]] = {} +PEFT_TYPE_TO_PREFIX_MAPPING: dict[PeftType, str] = {} + + +def get_peft_config(config_dict: dict[str, Any]) -> PeftConfig: + """ + Returns a Peft config object from a dictionary. + + Args: + config_dict (`Dict[str, Any]`): Dictionary containing the configuration parameters. + """ + + return PEFT_TYPE_TO_CONFIG_MAPPING[config_dict["peft_type"]](**config_dict) + + +def inject_adapter_in_model( + peft_config: PeftConfig, + model: torch.nn.Module, + adapter_name: str = "default", + low_cpu_mem_usage: bool = False, + state_dict: Optional[dict[str, torch.Tensor]] = None, +) -> torch.nn.Module: + r""" + A simple API to create and inject adapter in-place into a model. Currently the API does not support prompt learning + methods and adaption prompt. Make sure to have the correct `target_names` set in the `peft_config` object. The API + calls `get_peft_model` under the hood but would be restricted only to non-prompt learning methods. + + Args: + peft_config (`PeftConfig`): + Configuration object containing the parameters of the Peft model. + model (`torch.nn.Module`): + The input model where the adapter will be injected. + adapter_name (`str`, `optional`, defaults to `"default"`): + The name of the adapter to be injected, if not provided, the default adapter name is used ("default"). + low_cpu_mem_usage (`bool`, `optional`, defaults to `False`): + Create empty adapter weights on meta device. Useful to speed up the loading process. + state_dict (`dict`, *optional*, defaults to `None`) + If a state_dict is passed here, the adapters will be injected based on the entries of the state_dict. This + can be useful when the exact `target_modules` of the PEFT method is unknown, for instance because the + checkpoint was created without meta data. Note that the values from the state_dict are not used, only the + keys are used to determine the correct layers that should be adapted. + """ + if peft_config.is_prompt_learning or peft_config.is_adaption_prompt: + raise ValueError("`create_and_replace` does not support prompt learning and adaption prompt yet.") + + if peft_config.peft_type not in PEFT_TYPE_TO_TUNER_MAPPING.keys(): + raise ValueError( + f"`inject_adapter_in_model` does not support {peft_config.peft_type} yet. Please use `get_peft_model`." + ) + + tuner_cls = PEFT_TYPE_TO_TUNER_MAPPING[peft_config.peft_type] + + # By instantiating a peft model we are injecting randomly initialized LoRA layers into the model's modules. + peft_model = tuner_cls( + model, peft_config, adapter_name=adapter_name, low_cpu_mem_usage=low_cpu_mem_usage, state_dict=state_dict + ) + + return peft_model.model diff --git a/.venv/lib/python3.12/site-packages/peft/mapping_func.py b/.venv/lib/python3.12/site-packages/peft/mapping_func.py new file mode 100644 index 0000000000000000000000000000000000000000..f958de7f4b0f071c3eaff60f7047e5ae24109b75 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/peft/mapping_func.py @@ -0,0 +1,131 @@ +# Copyright 2024-present the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import warnings +from typing import Optional + +from transformers import PreTrainedModel + +from .auto import MODEL_TYPE_TO_PEFT_MODEL_MAPPING +from .config import PeftConfig +from .mapping import PEFT_TYPE_TO_CONFIG_MAPPING, PEFT_TYPE_TO_PREFIX_MAPPING +from .mixed_model import PeftMixedModel +from .peft_model import PeftModel +from .tuners.tuners_utils import BaseTuner, BaseTunerLayer +from .utils import _prepare_prompt_learning_config + + +def get_peft_model( + model: PreTrainedModel, + peft_config: PeftConfig, + adapter_name: str = "default", + mixed: bool = False, + autocast_adapter_dtype: bool = True, + revision: Optional[str] = None, + low_cpu_mem_usage: bool = False, +) -> PeftModel | PeftMixedModel: + """ + Returns a Peft model object from a model and a config, where the model will be modified in-place. + + Args: + model ([`transformers.PreTrainedModel`]): + Model to be wrapped. + peft_config ([`PeftConfig`]): + Configuration object containing the parameters of the Peft model. + adapter_name (`str`, `optional`, defaults to `"default"`): + The name of the adapter to be injected, if not provided, the default adapter name is used ("default"). + mixed (`bool`, `optional`, defaults to `False`): + Whether to allow mixing different (compatible) adapter types. + autocast_adapter_dtype (`bool`, *optional*): + Whether to autocast the adapter dtype. Defaults to `True`. Right now, this will only cast adapter weights + using float16 or bfloat16 to float32, as this is typically required for stable training, and only affect + select PEFT tuners. + revision (`str`, `optional`, defaults to `main`): + The revision of the base model. If this isn't set, the saved peft model will load the `main` revision for + the base model + low_cpu_mem_usage (`bool`, `optional`, defaults to `False`): + Create empty adapter weights on meta device. Useful to speed up the loading process. Leave this setting as + False if you intend on training the model, unless the adapter weights will be replaced by different weights + before training starts. + """ + model_config = BaseTuner.get_model_config(model) + old_name = peft_config.base_model_name_or_path + new_name = model.__dict__.get("name_or_path", None) + peft_config.base_model_name_or_path = new_name + + # Especially in notebook environments there could be a case that a user wants to experiment with different + # configuration values. However, it is likely that there won't be any changes for new configs on an already + # initialized PEFT model. The best we can do is warn the user about it. + if any(isinstance(module, BaseTunerLayer) for module in model.modules()): + warnings.warn( + "You are trying to modify a model with PEFT for a second time. If you want to reload the model with a " + "different config, make sure to call `.unload()` before." + ) + + if (old_name is not None) and (old_name != new_name): + warnings.warn( + f"The PEFT config's `base_model_name_or_path` was renamed from '{old_name}' to '{new_name}'. " + "Please ensure that the correct base model is loaded when loading this checkpoint." + ) + + if revision is not None: + if peft_config.revision is not None and peft_config.revision != revision: + warnings.warn( + f"peft config has already set base model revision to {peft_config.revision}, overwriting with revision {revision}" + ) + peft_config.revision = revision + + if ( + (isinstance(peft_config, PEFT_TYPE_TO_CONFIG_MAPPING["LORA"])) + and (peft_config.init_lora_weights == "eva") + and not low_cpu_mem_usage + ): + warnings.warn( + "lora with eva initialization used with low_cpu_mem_usage=False. " + "Setting low_cpu_mem_usage=True can improve the maximum batch size possible for eva initialization." + ) + + prefix = PEFT_TYPE_TO_PREFIX_MAPPING.get(peft_config.peft_type) + if prefix and adapter_name in prefix: + warnings.warn( + f"Adapter name {adapter_name} should not be contained in the prefix {prefix}." + "This may lead to reinitialization of the adapter weights during loading." + ) + + if mixed: + # note: PeftMixedModel does not support autocast_adapter_dtype, so don't pass it + return PeftMixedModel(model, peft_config, adapter_name=adapter_name) + + # We explicitly exclude prompt learning here since prompt learning is specific to the task and needs special + # handling in the PEFT model's forward method. + if peft_config.task_type not in MODEL_TYPE_TO_PEFT_MODEL_MAPPING.keys() and not peft_config.is_prompt_learning: + return PeftModel( + model, + peft_config, + adapter_name=adapter_name, + autocast_adapter_dtype=autocast_adapter_dtype, + low_cpu_mem_usage=low_cpu_mem_usage, + ) + + if peft_config.is_prompt_learning: + peft_config = _prepare_prompt_learning_config(peft_config, model_config) + return MODEL_TYPE_TO_PEFT_MODEL_MAPPING[peft_config.task_type]( + model, + peft_config, + adapter_name=adapter_name, + autocast_adapter_dtype=autocast_adapter_dtype, + low_cpu_mem_usage=low_cpu_mem_usage, + ) diff --git a/.venv/lib/python3.12/site-packages/peft/mixed_model.py b/.venv/lib/python3.12/site-packages/peft/mixed_model.py new file mode 100644 index 0000000000000000000000000000000000000000..815b5c2e82563d11f694e5ade160f0ac6c994fb6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/peft/mixed_model.py @@ -0,0 +1,465 @@ +# Copyright 2023-present the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import os +from contextlib import contextmanager +from typing import Any, Optional, Union + +import torch +from accelerate.hooks import remove_hook_from_submodules +from torch import nn +from transformers.utils import PushToHubMixin + +from peft.utils.constants import DUMMY_MODEL_CONFIG + +from .config import PeftConfig +from .peft_model import PeftModel +from .tuners import MixedModel +from .utils import _set_adapter, _set_trainable + + +def _prepare_model_for_gradient_checkpointing(model: nn.Module) -> None: + r""" + Prepares the model for gradient checkpointing if necessary + """ + # Note: same as PeftModel._prepare_model_for_gradient_checkpointing + if not getattr(model, "is_gradient_checkpointing", True): + return model + + if not ( + getattr(model, "is_loaded_in_8bit", False) + or getattr(model, "is_loaded_in_4bit", False) + or getattr(model, "is_quantized", False) + ): + if hasattr(model, "enable_input_require_grads"): + model.enable_input_require_grads() + elif hasattr(model, "get_input_embeddings"): + + def make_inputs_require_grad(module, input, output): + output.requires_grad_(True) + + model.get_input_embeddings().register_forward_hook(make_inputs_require_grad) + + +def _check_config_compatible(peft_config: PeftConfig) -> None: + from .tuners.mixed import COMPATIBLE_TUNER_TYPES + + if peft_config.peft_type not in COMPATIBLE_TUNER_TYPES: + raise ValueError( + f"The provided `peft_type` '{peft_config.peft_type.value}' is not compatible with the `PeftMixedModel`. " + f"Compatible types are: {COMPATIBLE_TUNER_TYPES}" + ) + + +class PeftMixedModel(PushToHubMixin, torch.nn.Module): + """ + PeftMixedModel for loading mixing different types of adapters for inference. + + This class does not support loading/saving, and it shouldn't usually be initialized directly. Instead, use + `get_peft_model` with the argument `mixed=True`. + + + + Read the [Mixed adapter types](https://huggingface.co/docs/peft/en/developer_guides/mixed_models) guide to learn + more about using different adapter types. + + + + Example: + + ```py + >>> base_model = ... # load the base model, e.g. from transformers + >>> peft_model = PeftMixedModel.from_pretrained(base_model, path_to_adapter1, "adapter1").eval() + >>> peft_model.load_adapter(path_to_adapter2, "adapter2") + >>> peft_model.set_adapter(["adapter1", "adapter2"]) # activate both adapters + >>> peft_model(data) # forward pass using both adapters + ``` + + Args: + model (`torch.nn.Module`): + The model to be tuned. + config (`PeftConfig`): + The config of the model to be tuned. The adapter type must be compatible. + adapter_name (`str`, `optional`, defaults to `"default"`): + The name of the first adapter. + low_cpu_mem_usage (`bool`, `optional`, defaults to `False`): + Create empty adapter weights on meta device. Useful to speed up the loading process. + """ + + def __init__(self, model: nn.Module, peft_config: PeftConfig, adapter_name: str = "default") -> None: + super().__init__() + _check_config_compatible(peft_config) + _prepare_model_for_gradient_checkpointing(model) + self.modules_to_save = None + self.base_model = MixedModel(model, {adapter_name: peft_config}, adapter_name) + self.set_modules_to_save(peft_config, adapter_name) + + self.config = getattr(model, "config", DUMMY_MODEL_CONFIG) + + # the `pretraining_tp` is set for some models to simulate Tensor Parallelism during inference to avoid + # numerical differences, https://github.com/pytorch/pytorch/issues/76232 - to avoid any unexpected + # behavior we disable that in this line. + if hasattr(self.base_model, "config") and hasattr(self.base_model.config, "pretraining_tp"): + self.base_model.config.pretraining_tp = 1 + + @property + def peft_config(self) -> dict[str, PeftConfig]: + return self.base_model.peft_config + + @property + def active_adapter(self) -> str: + return self.base_model.active_adapter + + @property + def active_adapters(self) -> list[str]: + return self.base_model.active_adapters + + def get_nb_trainable_parameters(self): + r""" + Returns the number of trainable parameters and number of all parameters in the model. + """ + # note: same as PeftModel.get_nb_trainable_parameters + trainable_params = 0 + all_param = 0 + for _, param in self.named_parameters(): + num_params = param.numel() + # if using DS Zero 3 and the weights are initialized empty + if num_params == 0 and hasattr(param, "ds_numel"): + num_params = param.ds_numel + + # Due to the design of 4bit linear layers from bitsandbytes + # one needs to multiply the number of parameters by 2 to get + # the correct number of parameters + if param.__class__.__name__ == "Params4bit": + num_params = num_params * 2 + + all_param += num_params + if param.requires_grad: + trainable_params += num_params + + return trainable_params, all_param + + def print_trainable_parameters(self): + """ + Prints the number of trainable parameters in the model. + + Note: print_trainable_parameters() uses get_nb_trainable_parameters() which is different from + num_parameters(only_trainable=True) from huggingface/transformers. get_nb_trainable_parameters() returns + (trainable parameters, all parameters) of the Peft Model which includes modified backbone transformer model. + For techniques like LoRA, the backbone transformer model is modified in place with LoRA modules. However, for + prompt tuning, the backbone transformer model is unmodified. num_parameters(only_trainable=True) returns number + of trainable parameters of the backbone transformer model which can be different. + """ + # note: same as PeftModel.print_trainable_parameters + trainable_params, all_param = self.get_nb_trainable_parameters() + + print( + f"trainable params: {trainable_params:,d} || " + f"all params: {all_param:,d} || " + f"trainable%: {100 * trainable_params / all_param:.4f}" + ) + + def __getattr__(self, name: str): + """Forward missing attributes to the wrapped module.""" + try: + return super().__getattr__(name) # defer to nn.Module's logic + except AttributeError: + if name == "base_model": # see #1892: prevent infinite recursion if class is not initialized + raise + return getattr(self.base_model, name) + + def forward(self, *args: Any, **kwargs: Any): + """ + Forward pass of the model. + """ + return self.base_model(*args, **kwargs) + + def generate(self, *args: Any, **kwargs: Any): + """ + Generate output. + """ + return self.base_model.generate(*args, **kwargs) + + @contextmanager + def disable_adapter(self): + """ + Disables the adapter module. + """ + try: + self.base_model.disable_adapter_layers() + yield + finally: + self.base_model.enable_adapter_layers() + + def add_adapter(self, adapter_name: str, peft_config: PeftConfig, low_cpu_mem_usage: bool = False) -> None: + """ + Add an adapter to the model based on the passed configuration. + + This adapter is not trained. To load a trained adapter, check out [`PeftModel.load_adapter`]. + + The name for the new adapter should be unique. + + The new adapter is not automatically set as the active adapter. Use [`PeftModel.set_adapter`] to set the active + adapter. + + Args: + adapter_name (`str`): + The name of the adapter to be added. + peft_config ([`PeftConfig`]): + The configuration of the adapter to be added. + low_cpu_mem_usage (`bool`, `optional`, defaults to `False`): + Create empty adapter weights on meta device. Useful to speed up the process when loading saved + adapters. + + + + Don't use `low_cpu_mem_usage=True` when creating a new PEFT adapter for training (training is untested + and discouraged for PeftMixedModel in general). + + + """ + _check_config_compatible(peft_config) + + try: + self.peft_config[adapter_name] = peft_config + self.base_model.inject_adapter(self, adapter_name, low_cpu_mem_usage=low_cpu_mem_usage) + except Exception: # something went wrong, roll back + if adapter_name in self.peft_config: + del self.peft_config[adapter_name] + raise + + self.set_modules_to_save(peft_config, adapter_name) + + def set_modules_to_save(self, peft_config: PeftConfig, adapter_name: str) -> None: + if (modules_to_save := getattr(peft_config, "modules_to_save", None)) is None: + return + + if self.modules_to_save is None: + self.modules_to_save = set(modules_to_save) + else: + self.modules_to_save.update(modules_to_save) + _set_trainable(self, adapter_name, module_names=getattr(peft_config, "modules_to_save", None)) + + def set_adapter(self, adapter_name: Union[str, list[str]]) -> None: + """ + Sets the active adapter(s) for the model. + + Note that the order in which the adapters are applied during the forward pass may not be the same as the order + in which they are passed to this function. Instead, the order during the forward pass is determined by the + order in which the adapters were loaded into the model. The active adapters only determine which adapters are + active during the forward pass, but not the order in which they are applied. + + Additionally, this function will set the specified adapters to trainable (i.e., requires_grad=True). If this is + not desired, use the following code. + + ```py + >>> for name, param in model_peft.named_parameters(): + ... if ...: # some check on name (ex. if 'lora' in name) + ... param.requires_grad = False + ``` + + Args: + adapter_name (`str` or `List[str]`): + The name of the adapter(s) to be activated. + """ + if isinstance(adapter_name, str): + adapter_name = [adapter_name] + + mismatched = set(adapter_name) - set(self.peft_config.keys()) + if mismatched: + raise ValueError( + f"Adapter(s) {sorted(mismatched)} not found, available adapters: {sorted(self.peft_config.keys())}" + ) + + self.base_model.set_adapter(adapter_name) + _set_adapter(self, adapter_name) + + def delete_adapter(self, adapter_name: Union[str, list[str]]) -> None: + if isinstance(adapter_name, str): + adapter_name = [adapter_name] + + mismatched = set(adapter_name) - set(self.peft_config.keys()) + if mismatched: + raise ValueError( + f"Adapter(s) {sorted(mismatched)} not found, available adapters: {sorted(self.peft_config.keys())}" + ) + + self.base_model.delete_adapter(adapter_name) + + def merge_and_unload(self, *args: Any, **kwargs: Any): + r""" + This method merges the adapter layers into the base model. This is needed if someone wants to use the base + model as a standalone model. + + Args: + progressbar (`bool`): + whether to show a progressbar indicating the unload and merge process + safe_merge (`bool`): + whether to activate the safe merging check to check if there is any potential Nan in the adapter + weights + adapter_names (`List[str]`, *optional*): + The list of adapter names that should be merged. If None, all active adapters will be merged. Defaults + to `None`. + """ + return self.base_model.merge_and_unload(*args, **kwargs) + + def unload(self, *args: Any, **kwargs: Any): + """ + Gets back the base model by removing all the adapter modules without merging. This gives back the original base + model. + """ + return self.base_model.unload(*args, **kwargs) + + def get_layer_status(self): + raise TypeError(f"get_layer_status is not supported for {self.__class__.__name__}.") + + def get_model_status(self): + raise TypeError(f"get_model_status is not supported for {self.__class__.__name__}.") + + @classmethod + def _split_kwargs(cls, kwargs: dict[str, Any]): + return PeftModel._split_kwargs(kwargs) + + def _check_new_adapter_config(self, peft_config: PeftConfig, is_trainable: bool) -> None: + return PeftModel._check_new_adapter_config(self, peft_config, is_trainable=is_trainable) + + def load_adapter(self, model_id: str, adapter_name: str, *args: Any, **kwargs: Any): + """ + Load a trained adapter into the model. + + The name for the new adapter should be unique. + + The new adapter is not automatically set as the active adapter. Use [`PeftModel.set_adapter`] to set the active + adapter. + + Args: + adapter_name (`str`): + The name of the adapter to be added. + peft_config ([`PeftConfig`]): + The configuration of the adapter to be added. + is_trainable (`bool`, *optional*, defaults to `False`): + Whether the adapter should be trainable or not. If `False`, the adapter will be frozen and can only be + used for inference. + torch_device (`str`, *optional*, defaults to None): + The device to load the adapter on. If `None`, the device will be inferred. + autocast_adapter_dtype (`bool`, *optional*, defaults to `True`): + Whether to autocast the adapter dtype. Defaults to `True`. Right now, this will only cast adapter + weights using float16 and bfloat16 to float32, as this is typically required for stable training, and + only affect select PEFT tuners. + ephemeral_gpu_offload (`bool`, *optional*, defaults to `False`): + Whether to use ephemeral GPU offloading for partially loaded modules. Defaults to `False`. + low_cpu_mem_usage (`bool`, `optional`, defaults to `False`): + Create empty adapter weights on meta device before loading the saved weights. Useful to speed up the + process. + kwargs: (`optional`): + Additional arguments to modify the way the adapter is loaded, e.g. the token for Hugging Face Hub. + """ + # the low_cpu_mem_usage option is handled through kwargs + output = PeftModel.load_adapter(self, model_id, adapter_name, *args, **kwargs) + # TODO: not quite clear why this is necessary but tests fail without it + self.set_adapter(self.active_adapters) + return output + + def create_or_update_model_card(self, output_dir: str): + raise NotImplementedError(f"Model card creation is not supported for {self.__class__.__name__} (yet).") + + def save_pretrained( + self, + save_directory: str, + safe_serialization: bool = False, + selected_adapters: Optional[list[str]] = None, + **kwargs: Any, + ): + raise NotImplementedError(f"Saving is not supported for {self.__class__.__name__} (yet).") + + @classmethod + def from_pretrained( + cls, + model: nn.Module, + model_id: str | os.PathLike, + adapter_name: str = "default", + is_trainable: bool = False, + config: Optional[PeftConfig] = None, + **kwargs: Any, + ): + r""" + Instantiate a PEFT mixed model from a pretrained model and loaded PEFT weights. + + Note that the passed `model` may be modified inplace. + + Args: + model (`nn.Module`): + The model to be adapted. + model_id (`str` or `os.PathLike`): + The name of the PEFT configuration to use. Can be either: + - A string, the `model id` of a PEFT configuration hosted inside a model repo on the Hugging Face + Hub. + - A path to a directory containing a PEFT configuration file saved using the `save_pretrained` + method (`./my_peft_config_directory/`). + adapter_name (`str`, *optional*, defaults to `"default"`): + The name of the adapter to be loaded. This is useful for loading multiple adapters. + is_trainable (`bool`, *optional*, defaults to `False`): + Whether the adapter should be trainable or not. If `False`, the adapter will be frozen and use for + inference + config ([`~peft.PeftConfig`], *optional*): + The configuration object to use instead of an automatically loaded configuration. This configuration + object is mutually exclusive with `model_id` and `kwargs`. This is useful when configuration is already + loaded before calling `from_pretrained`. + low_cpu_mem_usage (`bool`, `optional`, defaults to `False`): + Create empty adapter weights on meta device before loading the saved weights. Useful to speed up the + process. + kwargs: (`optional`): + Additional keyword arguments passed along to the specific PEFT configuration class. + """ + # note: adapted from PeftModel.from_pretrained + from .mapping import PEFT_TYPE_TO_CONFIG_MAPPING, PEFT_TYPE_TO_MIXED_MODEL_MAPPING + + # load the config + if config is None: + config = PEFT_TYPE_TO_CONFIG_MAPPING[ + PeftConfig._get_peft_type( + model_id, + subfolder=kwargs.get("subfolder", None), + revision=kwargs.get("revision", None), + cache_dir=kwargs.get("cache_dir", None), + use_auth_token=kwargs.get("use_auth_token", None), + ) + ].from_pretrained(model_id, **kwargs) + elif isinstance(config, PeftConfig): + config.inference_mode = not is_trainable + else: + raise ValueError(f"The input config must be a PeftConfig, got {config.__class__}") + + # note: this is different from PeftModel.from_pretrained + if config.peft_type not in PEFT_TYPE_TO_MIXED_MODEL_MAPPING: + raise ValueError(f"Adapter of type {config.peft_type} is not supported for mixed models.") + + if (getattr(model, "hf_device_map", None) is not None) and len( + set(model.hf_device_map.values()).intersection({"cpu", "disk"}) + ) > 0: + remove_hook_from_submodules(model) + + if config.is_prompt_learning and is_trainable: + # note: should not be possible to reach, but just in case + raise ValueError("Cannot set a prompt learning adapter to trainable when loading pretrained adapter.") + else: + config.inference_mode = not is_trainable + + # note: this is different from PeftModel.from_pretrained, we always return a PeftMixedModel + model = cls(model, config, adapter_name) + # the low_cpu_mem_usage option is handled through kwargs + model.load_adapter(model_id, adapter_name, is_trainable=is_trainable, **kwargs) + return model diff --git a/.venv/lib/python3.12/site-packages/peft/peft_model.py b/.venv/lib/python3.12/site-packages/peft/peft_model.py new file mode 100644 index 0000000000000000000000000000000000000000..c4b1d9e033403b78e922b78d51abedf61e5c248f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/peft/peft_model.py @@ -0,0 +1,3238 @@ +# Copyright 2023-present the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import collections +import copy +import inspect +import os +import warnings +from contextlib import contextmanager, nullcontext +from copy import deepcopy +from dataclasses import dataclass +from typing import Any, Literal, Optional, Union + +import packaging.version +import torch +import transformers +from accelerate import dispatch_model, infer_auto_device_map +from accelerate.hooks import AlignDevicesHook, add_hook_to_module, remove_hook_from_submodules +from accelerate.utils import get_balanced_memory, named_module_tensors +from huggingface_hub import HfFileSystem, ModelCard, ModelCardData, hf_hub_download +from safetensors import safe_open +from safetensors.torch import save_file as safe_save_file +from torch.nn import BCEWithLogitsLoss, CrossEntropyLoss, MSELoss +from transformers import Cache, DynamicCache, EncoderDecoderCache, HybridCache, PreTrainedModel +from transformers.modeling_outputs import QuestionAnsweringModelOutput, SequenceClassifierOutput, TokenClassifierOutput +from transformers.utils import PushToHubMixin + +from peft.tuners.tuners_utils import BaseTuner, BaseTunerLayer +from peft.utils.constants import DUMMY_MODEL_CONFIG +from peft.utils.integrations import init_empty_weights +from peft.utils.other import create_attention_mask, set_additional_trainable_modules + +from . import __version__ +from .config import PeftConfig +from .mapping import PEFT_TYPE_TO_CONFIG_MAPPING, PEFT_TYPE_TO_PREFIX_MAPPING, PEFT_TYPE_TO_TUNER_MAPPING +from .utils import ( + SAFETENSORS_WEIGHTS_NAME, + TRANSFORMERS_MODELS_TO_PREFIX_TUNING_POSTPROCESS_MAPPING, + WEIGHTS_NAME, + PeftType, + TaskType, + _get_batch_size, + _prepare_prompt_learning_config, + _set_adapter, + _set_trainable, + get_peft_model_state_dict, + id_tensor_storage, + infer_device, + load_peft_weights, + map_cache_to_layer_device_map, + set_peft_model_state_dict, + shift_tokens_right, +) + + +class PeftModel(PushToHubMixin, torch.nn.Module): + """ + Base model encompassing various Peft methods. + + Args: + model ([`~transformers.PreTrainedModel`]): The base transformer model used for Peft. + peft_config ([`PeftConfig`]): The configuration of the Peft model. + adapter_name (`str`, *optional*): The name of the adapter, defaults to `"default"`. + autocast_adapter_dtype (`bool`, *optional*): + Whether to autocast the adapter dtype. Defaults to `True`. Right now, this will only cast adapter weights + using float16 and bfloat16 to float32, as this is typically required for stable training, and only affect + select PEFT tuners. + low_cpu_mem_usage (`bool`, `optional`, defaults to `False`): + Create empty adapter weights on meta device. Useful to speed up the loading loading process. + + + + Don't use `low_cpu_mem_usage=True` when creating a new PEFT adapter for training. + + + + **Attributes**: + - **base_model** ([`torch.nn.Module`]) -- The base transformer model used for Peft. + - **peft_config** ([`PeftConfig`]) -- The configuration of the Peft model. + - **modules_to_save** (`list` of `str`) -- The list of sub-module names to save when + saving the model. + - **prompt_encoder** ([`PromptEncoder`]) -- The prompt encoder used for Peft if + using [`PromptLearningConfig`]. + - **prompt_tokens** (`torch.Tensor`) -- The virtual prompt tokens used for Peft if + using [`PromptLearningConfig`]. + - **transformer_backbone_name** (`str`) -- The name of the transformer + backbone in the base model if using [`PromptLearningConfig`]. + - **word_embeddings** (`torch.nn.Embedding`) -- The word embeddings of the transformer backbone + in the base model if using [`PromptLearningConfig`]. + """ + + def __init__( + self, + model: PreTrainedModel, + peft_config: PeftConfig, + adapter_name: str = "default", + autocast_adapter_dtype: bool = True, + low_cpu_mem_usage: bool = False, + ) -> None: + super().__init__() + self.active_adapter = adapter_name + self.peft_type = peft_config.peft_type + # These args are special PEFT arguments that users can pass. They need to be removed before passing them to + # forward. + self.special_peft_forward_args = {"adapter_names"} + + self._is_prompt_learning = peft_config.is_prompt_learning + if self._is_prompt_learning: + self._peft_config = {adapter_name: peft_config} + self.base_model = model + self.add_adapter(adapter_name, peft_config, low_cpu_mem_usage=low_cpu_mem_usage) + else: + self._peft_config = None + cls = PEFT_TYPE_TO_TUNER_MAPPING[peft_config.peft_type] + ctx = init_empty_weights if low_cpu_mem_usage else nullcontext + with ctx(): + self.base_model = cls(model, {adapter_name: peft_config}, adapter_name) + + if hasattr(self.base_model, "_cast_adapter_dtype"): + self.base_model._cast_adapter_dtype( + adapter_name=adapter_name, autocast_adapter_dtype=autocast_adapter_dtype + ) + + if getattr(model, "is_gradient_checkpointing", True): + model = self.prepare_model_for_gradient_checkpointing(model) + + # the `pretraining_tp` is set for some models to simulate Tensor Parallelism during inference to avoid + # numerical differences, https://github.com/pytorch/pytorch/issues/76232 - to avoid any unexpected + # behavior we disable that in this line. + if hasattr(self.base_model, "config") and hasattr(self.base_model.config, "pretraining_tp"): + self.base_model.config.pretraining_tp = 1 + + @property + def peft_config(self) -> dict[str, PeftConfig]: + if self._is_prompt_learning: + return self._peft_config + return self.base_model.peft_config + + @property + def active_adapters(self) -> list[str]: + try: + adapters = self.base_model.active_adapters + if not isinstance(adapters, list): + # Base model is probably a transformers model, see: + # https://github.com/huggingface/transformers/pull/30790#issuecomment-2253808249 + # Unfortunately, transformers models also have an active_adapters method but it's 1) not a property and + # 2) calling it fails because the base model (usually) has no loaded adapter. The base model can be a + # transformers model for prompt learning, where the base model is not wrapped in a LoraModel or similar. + adapters = self.active_adapter + if isinstance(adapters, str): + adapters = [adapters] + except AttributeError: + adapters = self.active_adapter + if isinstance(adapters, str): + adapters = [adapters] + return adapters + + @peft_config.setter + def peft_config(self, value: dict[str, PeftConfig]): + if self._is_prompt_learning: + self._peft_config = value + else: + self.base_model.peft_config = value + + def save_pretrained( + self, + save_directory: str, + safe_serialization: bool = True, + selected_adapters: Optional[list[str]] = None, + save_embedding_layers: Union[str, bool] = "auto", + is_main_process: bool = True, + path_initial_model_for_weight_conversion: Optional[str] = None, + **kwargs: Any, + ) -> None: + r""" + This function saves the adapter model and the adapter configuration files to a directory, so that it can be + reloaded using the [`PeftModel.from_pretrained`] class method, and also used by the [`PeftModel.push_to_hub`] + method. + + Args: + save_directory (`str`): + Directory where the adapter model and configuration files will be saved (will be created if it does not + exist). + safe_serialization (`bool`, *optional*): + Whether to save the adapter files in safetensors format, defaults to `True`. + selected_adapters (`List[str]`, *optional*): + A list of adapters to be saved. If `None`, will default to all adapters. + save_embedding_layers (`Union[bool, str]`, *optional*, defaults to `"auto"`): + If `True`, save the embedding layers in addition to adapter weights. If `auto`, checks the common + embedding layers `peft.utils.other.EMBEDDING_LAYER_NAMES` in config's `target_modules` when available. + and automatically sets the boolean flag. This only works for 🤗 transformers models. + is_main_process (`bool`, *optional*): + Whether the process calling this is the main process or not. Will default to `True`. Will not save the + checkpoint if not on the main process, which is important for multi device setups (e.g. DDP). + path_initial_model_for_weight_conversion (`str, *optional*`): + The path to the initialized adapter, which is obtained after initializing the model with + PiSSA/CorDA/OLoRA and before performing any training. When `path_initial_model_for_weight_conversion` + is not None, the difference in adapter before and after fine-tuning is calculated. This difference can + be represented as the parameters of a standard LoRA adapter. Using this converted adapter does not + require changes to the base model, thus conveniently allowing the use of multiple PiSSA/CorDA/OLoRA + adapters with LoRA adapters, and the activation or deactivation of any adapters. Note that this + conversion is not supported if `rslora` is used in combination with `rank_pattern` or `alpha_pattern`. + kwargs (additional keyword arguments, *optional*): + Additional keyword arguments passed along to the `push_to_hub` method. + + """ + if os.path.isfile(save_directory): + raise ValueError(f"Provided path ({save_directory}) should be a directory, not a file") + + if selected_adapters is None: + selected_adapters = list(self.peft_config.keys()) + else: + if any( + selected_adapter_name not in list(self.peft_config.keys()) + for selected_adapter_name in selected_adapters + ): + raise ValueError( + f"You passed an invalid `selected_adapters` arguments, current supported adapter names are" + f" {list(self.peft_config.keys())} - got {selected_adapters}." + ) + + def save_mutated_as_lora(peft_config, path_initial_model_for_weight_conversion, output_state_dict, kwargs): + if peft_config.use_rslora and (peft_config.rank_pattern or peft_config.alpha_pattern): + msg = ( + "Passing `path_initial_model_for_weight_conversion` to `save_pretrained` is not supported when " + "using `rank_pattern` or `alpha_pattern` at the same time as `use_rslora=True`." + ) + raise ValueError(msg) + + if not any( + str(peft_config.init_lora_weights).lower().startswith(prefix) + for prefix in ["pissa", "corda", "olora", "true"] + ): + warnings.warn( + "`path_initial_model_for_weight_conversion` only works for converting a PiSSA/CorDA/OLoRA adapter to " + "a LoRA adapter" + ) + initial_adapter_name = os.path.basename(path_initial_model_for_weight_conversion) + try: + self.load_adapter( + os.path.dirname(path_initial_model_for_weight_conversion), + subfolder=initial_adapter_name, + adapter_name=initial_adapter_name, + ) + is_pissa = str(self.peft_config[initial_adapter_name].init_lora_weights).lower().startswith("pissa") + is_corda = str(self.peft_config[initial_adapter_name].init_lora_weights).lower() == "corda" + is_olora = str(self.peft_config[initial_adapter_name].init_lora_weights).lower() == "olora" + if is_pissa or is_corda or is_olora: + raise ValueError( + "The `init_lora_weights` parameter of the initial adapter should be set to `True`. " + "Otherwise, `self.load_adapter` will subtract the decomposed values again based on the " + "residual model." + ) + output_state_dict = self.base_model.subtract_mutated_init( + output_state_dict, initial_adapter_name, kwargs + ) + finally: + self.delete_adapter(initial_adapter_name) + return output_state_dict + + if is_main_process: + os.makedirs(save_directory, exist_ok=True) + self.create_or_update_model_card(save_directory) + + for adapter_name in selected_adapters: + peft_config = self.peft_config[adapter_name] + # save only the trainable weights + output_state_dict = get_peft_model_state_dict( + self, + state_dict=kwargs.get("state_dict", None), + adapter_name=adapter_name, + save_embedding_layers=save_embedding_layers, + ) + output_dir = os.path.join(save_directory, adapter_name) if adapter_name != "default" else save_directory + os.makedirs(output_dir, exist_ok=True) + + if is_main_process and safe_serialization: + # Section copied from: https://github.com/huggingface/transformers/blob/main/src/transformers/modeling_utils.py#L2111-L2134 + # Safetensors does not allow tensor aliasing. + # We're going to remove aliases before saving + ptrs = collections.defaultdict(list) + for name, tensor in output_state_dict.items(): + # Sometimes in the state_dict we have non-tensor objects. + # e.g. in bitsandbytes we have some `str` objects in the state_dict + if isinstance(tensor, torch.Tensor): + ptrs[id_tensor_storage(tensor)].append(name) + else: + # In the non-tensor case, fall back to the pointer of the object itself + ptrs[id(tensor)].append(name) + + # These are all the pointers of shared tensors. + shared_ptrs = {ptr: names for ptr, names in ptrs.items() if len(names) > 1} + + for _, names in shared_ptrs.items(): + # Here we just clone the shared tensors to avoid tensor aliasing which is + # not supported in safetensors. + for shared_tensor_name in names[1:]: + output_state_dict[shared_tensor_name] = output_state_dict[shared_tensor_name].clone() + if path_initial_model_for_weight_conversion is not None: + peft_config = copy.deepcopy(peft_config) + peft_config.init_lora_weights = True + peft_config.save_pretrained(path_initial_model_for_weight_conversion) + output_state_dict = save_mutated_as_lora( + peft_config, path_initial_model_for_weight_conversion, output_state_dict, kwargs + ) + safe_save_file( + output_state_dict, + os.path.join(output_dir, SAFETENSORS_WEIGHTS_NAME), + metadata={"format": "pt"}, + ) + elif is_main_process: + if path_initial_model_for_weight_conversion is not None: + peft_config = copy.deepcopy(peft_config) + peft_config.init_lora_weights = True + peft_config.save_pretrained(path_initial_model_for_weight_conversion) + output_state_dict = save_mutated_as_lora( + peft_config, path_initial_model_for_weight_conversion, output_state_dict, kwargs + ) + torch.save(output_state_dict, os.path.join(output_dir, WEIGHTS_NAME)) + + # save the config and change the inference mode to `True` + if peft_config.base_model_name_or_path is None: + peft_config.base_model_name_or_path = ( + self.base_model.__dict__.get("name_or_path", None) + if peft_config.is_prompt_learning + else self.base_model.model.__dict__.get("name_or_path", None) + ) + inference_mode = peft_config.inference_mode + peft_config.inference_mode = True + + if peft_config.task_type is None: + # deal with auto mapping + base_model_class = self._get_base_model_class( + is_prompt_tuning=peft_config.is_prompt_learning, + ) + parent_library = base_model_class.__module__ + + auto_mapping_dict = { + "base_model_class": base_model_class.__name__, + "parent_library": parent_library, + } + else: + auto_mapping_dict = None + + if is_main_process: + if path_initial_model_for_weight_conversion is not None: + peft_config.init_lora_weights = True + peft_config.r *= 2 + if not peft_config.use_rslora: + peft_config.lora_alpha *= 2 + else: + # with rslora, we have scaling = alpha / sqrt(r), we thus adjust alpha to keep the same scaling + peft_config.lora_alpha *= 2**0.5 + + if peft_config.rank_pattern: + peft_config.rank_pattern = {key: 2 * val for key, val in peft_config.rank_pattern.items()} + if peft_config.alpha_pattern: + peft_config.alpha_pattern = {key: 2 * val for key, val in peft_config.alpha_pattern.items()} + + peft_config.save_pretrained(output_dir, auto_mapping_dict=auto_mapping_dict) + peft_config.inference_mode = inference_mode + + @classmethod + def from_pretrained( + cls, + model: torch.nn.Module, + model_id: Union[str, os.PathLike], + adapter_name: str = "default", + is_trainable: bool = False, + config: Optional[PeftConfig] = None, + autocast_adapter_dtype: bool = True, + ephemeral_gpu_offload: bool = False, + low_cpu_mem_usage: bool = False, + key_mapping: Optional[dict[str, str]] = None, + **kwargs: Any, + ) -> PeftModel: + r""" + Instantiate a PEFT model from a pretrained model and loaded PEFT weights. + + Note that the passed `model` may be modified inplace. + + Args: + model ([`torch.nn.Module`]): + The model to be adapted. For 🤗 Transformers models, the model should be initialized with the + [`~transformers.PreTrainedModel.from_pretrained`]. + model_id (`str` or `os.PathLike`): + The name of the PEFT configuration to use. Can be either: + - A string, the `model id` of a PEFT configuration hosted inside a model repo on the Hugging Face + Hub. + - A path to a directory containing a PEFT configuration file saved using the `save_pretrained` + method (`./my_peft_config_directory/`). + adapter_name (`str`, *optional*, defaults to `"default"`): + The name of the adapter to be loaded. This is useful for loading multiple adapters. + is_trainable (`bool`, *optional*, defaults to `False`): + Whether the adapter should be trainable or not. If `False`, the adapter will be frozen and can only be + used for inference. + config ([`~peft.PeftConfig`], *optional*): + The configuration object to use instead of an automatically loaded configuration. This configuration + object is mutually exclusive with `model_id` and `kwargs`. This is useful when configuration is already + loaded before calling `from_pretrained`. + autocast_adapter_dtype (`bool`, *optional*): + Whether to autocast the adapter dtype. Defaults to `True`. Only relevant for specific adapter types. + ephemeral_gpu_offload (`bool`, *optional*): + Whether to use ephemeral GPU offloading for partially loaded modules. Defaults to `False`. This is + useful when parts of the model and/or components (such as adapters) are kept in CPU memory until they + are needed. Rather than perform expensive operations on small data, the data is transferred to the GPU + on-demand, the operation(s) performed, and the results moved back to CPU memory. This brings a slight + momentary VRAM overhead but gives orders of magnitude speedup in certain cases. + low_cpu_mem_usage (`bool`, `optional`, defaults to `False`): + Create empty adapter weights on meta device before loading the saved weights. Useful to speed up the + process. + torch_device (`str`, *optional*, defaults to None): + The device to load the adapter on. If `None`, the device will be inferred. + key_mapping (dict, *optional*, defaults to None) + Extra mapping of PEFT `state_dict` keys applied before loading the `state_dict`. When this mapping is + applied, the PEFT-specific `"base_model.model"` prefix is removed beforehand and the adapter name (e.g. + `"default"`) is not inserted yet. Only pass this argument if you know what you're doing. + kwargs: (`optional`): + Additional keyword arguments passed along to the specific PEFT configuration class. + """ + from .auto import MODEL_TYPE_TO_PEFT_MODEL_MAPPING + from .tuners import XLoraConfig, XLoraModel + + # load the config + if config is None: + config = PEFT_TYPE_TO_CONFIG_MAPPING[ + PeftConfig._get_peft_type( + model_id, + subfolder=kwargs.get("subfolder", None), + revision=kwargs.get("revision", None), + cache_dir=kwargs.get("cache_dir", None), + use_auth_token=kwargs.get("use_auth_token", None), + token=kwargs.get("token", None), + ) + ].from_pretrained(model_id, **kwargs) + elif isinstance(config, PeftConfig): + config.inference_mode = not is_trainable + else: + raise ValueError(f"The input config must be a PeftConfig, got {config.__class__}") + + # See discussion in https://github.com/huggingface/transformers/pull/38627 + # Some transformers models can have a _checkpoint_conversion_mapping dict that is used to map state_dicts + # stemming from updated model architectures so that they still correspond to the initial architecture. When + # loading a PEFT state_dict created with the initial architecture on a model with the new architecture, we need + # to map it too according to the same rules. Note that we skip prompt learning methods. This is because they + # don't have the "base_model.model." prefix, which we need to remove before mapping. Instead just using + # "base_model.". This could be fine, we could only remove "base_model.", However, the subsequent sub-module + # could also be called "model", resulting in what looks like "base_model.model.". To avoid this confusion, we + # skip prompt learning. Since it applies itself directly to the pre-trained model (unlike LoRA et al that target + # sub-modules), skipping should be fine. + if (key_mapping is None) and (not config.is_prompt_learning): + key_mapping = getattr(model, "_checkpoint_conversion_mapping", {}) + + # Runtime configuration, if supported + if hasattr(config, "runtime_config"): + config.runtime_config.ephemeral_gpu_offload = ephemeral_gpu_offload + else: + if ephemeral_gpu_offload: + warnings.warn("Ephemeral GPU offloading is not supported for this model. Ignoring.") + + if hasattr(model, "hf_device_map"): + weight_map = dict(named_module_tensors(model, recurse=True)) + + # recreate the offload_index for disk-offloaded modules: we need to know the location in storage of each weight + # before the offload hook is removed from the model + disk_modules = set() + index = None + for name, module in model.named_modules(): + if hasattr(module, "_hf_hook") and hasattr(module._hf_hook, "original_devices"): + if hasattr(module._hf_hook.weights_map, "dataset"): + index = module._hf_hook.weights_map.dataset.index + for key in module._hf_hook.original_devices.keys(): + if module._hf_hook.original_devices[key] == torch.device("meta"): + disk_modules.add(str(name) + "." + str(key)) + + if disk_modules and not kwargs.get("use_safetensors", True): + raise ValueError("Disk offloading currently only supported for safetensors") + + if index: + offload_index = { + p: { + "safetensors_file": index[p]["safetensors_file"], + "weight_name": p, + "dtype": str(weight_map[p].dtype).replace("torch.", ""), + } + for p in weight_map.keys() + if p in disk_modules + } + kwargs["offload_index"] = offload_index + + if (getattr(model, "hf_device_map", None) is not None) and len( + set(model.hf_device_map.values()).intersection({"cpu", "disk"}) + ) > 0: + remove_hook_from_submodules(model) + + if config.is_prompt_learning and is_trainable: + raise ValueError("Cannot set a prompt learning adapter to trainable when loading pretrained adapter.") + else: + config.inference_mode = not is_trainable + if isinstance(getattr(model, "base_model", None), XLoraModel): + if not isinstance(config, XLoraConfig): + raise TypeError(f"Expected 'XLoraConfig', got '{type(config)}' instead.") + if "adapters" in kwargs: + config.adapters = kwargs["adapters"] + else: + # If the path is on HF hub, then we get the adapter names to create a subfolders list which tells + # `load_adapter` where the adapters are. + if not os.path.exists(model_id): + s = HfFileSystem() + + # The names of the adapters which must be in folders + adapter_names = [ + file["name"][len(model_id) + 1 :] for file in s.ls(model_id) if file["type"] == "directory" + ] + # Prepare a dict of adapter paths, which really just point to the hf id; we will use the subfolders + adapter_paths = {} + for adapter_name in adapter_names: + adapter_paths[adapter_name] = os.path.join(model_id, model_id) + config.adapters = adapter_paths + config._subfolders = adapter_names + else: + if "adapters" not in kwargs: + raise ValueError("If model_id is a local path, then `adapters` must be passed in kwargs.") + + if config.task_type not in MODEL_TYPE_TO_PEFT_MODEL_MAPPING.keys(): + model = cls( + model, + config, + adapter_name, + autocast_adapter_dtype=autocast_adapter_dtype, + low_cpu_mem_usage=low_cpu_mem_usage, + ) + else: + model = MODEL_TYPE_TO_PEFT_MODEL_MAPPING[config.task_type]( + model, + config, + adapter_name, + autocast_adapter_dtype=autocast_adapter_dtype, + low_cpu_mem_usage=low_cpu_mem_usage, + ) + + load_result = model.load_adapter( + model_id, + adapter_name, + is_trainable=is_trainable, + autocast_adapter_dtype=autocast_adapter_dtype, + low_cpu_mem_usage=low_cpu_mem_usage, + key_mapping=key_mapping, + **kwargs, + ) + + # 1. Remove VB-LoRA vector bank, since it's a shared parameter set via the VBLoRAModel + # 2. Remove the prompt encoder, as it does not need to be part of the checkpoint + missing_keys = [ + k for k in load_result.missing_keys if "vblora_vector_bank" not in k and "prompt_encoder" not in k + ] + if missing_keys: + # Let's warn here since (in contrast to load_adapter) we don't return the load result, so it could be quite + # difficult for users to even notice that something might have gone wrong here. As we filter out non PEFT + # keys from the missing keys, this gives no false positives. + + # careful: if the wording of the warning is changed, adjust the unit tests accordingly! + warn_message = f"Found missing adapter keys while loading the checkpoint: {missing_keys}." + + prefix = PEFT_TYPE_TO_PREFIX_MAPPING.get(config.peft_type) + if prefix and adapter_name in prefix: + warn_message += ( + f"Adapter name {adapter_name} should not be contained in the prefix {prefix}." + "This could be the potential reason for missing adapter keys." + ) + + warnings.warn(warn_message) + + return model + + def _setup_prompt_encoder(self, adapter_name: str): + config = self.peft_config[adapter_name] + if not hasattr(self, "prompt_encoder"): + self.prompt_encoder = torch.nn.ModuleDict({}) + self.prompt_tokens = {} + transformer_backbone = None + for name, module in self.base_model.named_children(): + for param in module.parameters(): + param.requires_grad = False + if isinstance(module, PreTrainedModel): + # Make sure to freeze Tranformers model + if transformer_backbone is None: + transformer_backbone = module + self.transformer_backbone_name = name + if transformer_backbone is None: + transformer_backbone = self.base_model + + if config.num_transformer_submodules is None: + config.num_transformer_submodules = 2 if config.task_type == TaskType.SEQ_2_SEQ_LM else 1 + + # determine the word embeddings + word_embeddings = None + try: + # First try to find the word embeddings based on the module name, this should work for models like Bert, + # Roberta, Deberta, etc. + word_embeddings = self.base_model.get_submodule("embeddings.word_embeddings") + except AttributeError: + pass + + if word_embeddings is None: + # Word embeddings could not be determined. Next try to guess them by checking which parameter has the size + # of the vocab. + for named_param, value in list(transformer_backbone.named_parameters()): + # for ZeRO-3, the tensor is sharded across accelerators and deepspeed modifies it to a tensor with shape + # [0] the actual unsharded shape is stored in "ds_shape" attribute special handling is needed in case + # the model is initialized in deepspeed.zero.Init() context or HfDeepSpeedConfig has been called before + # For reference refer to issue: https://github.com/huggingface/peft/issues/996 + deepspeed_distributed_tensor_shape = getattr(value, "ds_shape", None) + + # Handle VLM case with separate text and vision configs + if hasattr(self.base_model.config, "get_text_config"): + vocab_size = self.base_model.config.get_text_config().vocab_size + # below: for older transformers versions before get_text_config was added + elif "text_config" in self.base_model.config: + vocab_size = self.base_model.config.text_config.vocab_size + else: + vocab_size = self.base_model.config.vocab_size + + if value.shape[0] == vocab_size or ( + deepspeed_distributed_tensor_shape is not None + and deepspeed_distributed_tensor_shape[0] == vocab_size + ): + word_embeddings = transformer_backbone.get_submodule(named_param.replace(".weight", "")) + break + + self.word_embeddings = word_embeddings + model_cls = PEFT_TYPE_TO_TUNER_MAPPING[config.peft_type] + + if config.peft_type in (PeftType.PROMPT_TUNING, PeftType.MULTITASK_PROMPT_TUNING, PeftType.CPT): + prompt_encoder = model_cls(config, self.word_embeddings) + elif config.peft_type == PeftType.P_TUNING: + prompt_encoder = model_cls(config) + elif config.peft_type == PeftType.PREFIX_TUNING: + # prefix tuning now uses Cache but that won't work with gradient checkpointing + if any(getattr(module, "gradient_checkpointing", False) for module in self.get_base_model().modules()): + raise ValueError("Prefix tuning does not work with gradient checkpointing.") + prompt_encoder = model_cls(config) + else: + raise ValueError("Not supported") + + prompt_encoder = prompt_encoder.to(self.device) + self.prompt_encoder.update(torch.nn.ModuleDict({adapter_name: prompt_encoder})) + self.prompt_tokens[adapter_name] = torch.arange( + config.num_virtual_tokens * config.num_transformer_submodules + ).long() + + def prepare_model_for_gradient_checkpointing(self, model: PreTrainedModel): + r""" + Prepares the model for gradient checkpointing if necessary + """ + self._prepare_model_for_gradient_checkpointing(model) + + def _prepare_model_for_gradient_checkpointing(self, model: PreTrainedModel): + if not ( + getattr(model, "is_loaded_in_8bit", False) + or getattr(model, "is_loaded_in_4bit", False) + or getattr(model, "is_quantized", False) + ): + if hasattr(model, "enable_input_require_grads"): + model.enable_input_require_grads() + elif hasattr(model, "get_input_embeddings"): + + def make_inputs_require_grad(module, input, output): + output.requires_grad_(True) + + model.get_input_embeddings().register_forward_hook(make_inputs_require_grad) + return model + + def get_prompt_embedding_to_save(self, adapter_name: str) -> torch.Tensor: + """ + Returns the prompt embedding to save when saving the model. Only applicable when using a prompt learning + method. + """ + prompt_encoder = self.prompt_encoder[adapter_name] + prompt_tokens = ( + self.prompt_tokens[adapter_name].unsqueeze(0).expand(1, -1).to(prompt_encoder.embedding.weight.device) + ) + peft_type = self.peft_config[adapter_name].peft_type + if self.peft_config[adapter_name].peft_type == PeftType.PREFIX_TUNING: + prompt_tokens = prompt_tokens[:, : self.peft_config[adapter_name].num_virtual_tokens] + + if self.peft_config[adapter_name].peft_type == PeftType.MULTITASK_PROMPT_TUNING: + prompt_embedding_cls = PEFT_TYPE_TO_TUNER_MAPPING[peft_type] + prompt_embeddings = super(prompt_embedding_cls, prompt_encoder).forward(prompt_tokens) + else: + prompt_embeddings = prompt_encoder(prompt_tokens) + + return prompt_embeddings[0].detach().cpu() + + def get_prompt( + self, batch_size: int, task_ids: Optional[torch.Tensor] = None, max_cache_len: Optional[int] = None + ) -> torch.Tensor: + """ + Returns the virtual prompts to use for Peft. Only applicable when using a prompt learning method. + """ + peft_config = self.active_peft_config + prompt_encoder = self.prompt_encoder[self.active_adapter] + prompt_tokens = ( + self.prompt_tokens[self.active_adapter] + .unsqueeze(0) + .expand(batch_size, -1) + .to(prompt_encoder.embedding.weight.device) + ) + if peft_config.peft_type == PeftType.PREFIX_TUNING: + prompt_tokens = prompt_tokens[:, : peft_config.num_virtual_tokens] + if peft_config.inference_mode: + past_key_values = prompt_encoder.embedding.weight.repeat(batch_size, 1, 1) + else: + past_key_values = prompt_encoder(prompt_tokens) + if self.base_model_torch_dtype is not None: + past_key_values = past_key_values.to(self.base_model_torch_dtype) + past_key_values = past_key_values.view( + batch_size, + peft_config.num_virtual_tokens, + peft_config.num_layers * 2, + peft_config.num_attention_heads, + peft_config.token_dim // peft_config.num_attention_heads, + ) + if peft_config.num_transformer_submodules == 2: + past_key_values = torch.cat([past_key_values, past_key_values], dim=2) + + # Transpose: 2 x [num_layers, batch_size, num_heads, num_virtual_tokens, head_dim] + past_key_values = past_key_values.permute([2, 0, 3, 1, 4]).split( + peft_config.num_transformer_submodules * 2 + ) + + base_model = self.get_base_model() + model_config = getattr(base_model, "config", None) + model_type = getattr(model_config, "model_type", "") + if TRANSFORMERS_MODELS_TO_PREFIX_TUNING_POSTPROCESS_MAPPING.get(self.config.model_type, None) is not None: + post_process_fn = TRANSFORMERS_MODELS_TO_PREFIX_TUNING_POSTPROCESS_MAPPING[self.config.model_type] + past_key_values = post_process_fn(past_key_values) + elif ("gemma2" in model_type) or ("gemma3_text" in model_type): + # Gemma2 and Gemma3 only support HybridCache (which does not have the from_legacy_cache method) + if max_cache_len is None: + raise ValueError( + "max_cache_len is None but it should have been passed. Something went wrong, please open an " + "issue on GitHub with a reproducer: https://github.com/huggingface/peft/issues" + ) + base_config = base_model.config + if hasattr(base_config, "get_text_config"): + base_config = base_config.get_text_config() + new_cache = HybridCache( + base_config, + max_batch_size=batch_size, + max_cache_len=max_cache_len, + dtype=past_key_values[0].dtype, + device=past_key_values[0].device, + ) + cache_position = torch.arange(peft_config.num_virtual_tokens, device=past_key_values[0].device) + for layer_idx in range(peft_config.num_layers): + key_states, value_states = past_key_values[0][layer_idx], past_key_values[1][layer_idx] + new_cache.update( + key_states, value_states, layer_idx, cache_kwargs={"cache_position": cache_position} + ) + past_key_values = new_cache + elif peft_config.num_transformer_submodules == 1: + # Dont' apply this to encoder-decoder models and not to models requiring special processing. + # local import in case users use a very old transformers version + past_key_values = DynamicCache.from_legacy_cache(past_key_values) + elif (peft_config.num_transformer_submodules == 2) and getattr( + self.base_model, "_supports_cache_class", True + ): + # Dont' apply this to encoder-decoder models that don't support new Cachc format yet + # If we don't apply this, prefix-tuning fails to update cross-attn cache + # TODO: remove check for _supports_cache_class once transformers 4.53 is no longer supported + past_key_values = EncoderDecoderCache.from_legacy_cache(past_key_values) + past_key_values.cross_attention_cache = DynamicCache() + past_key_values.is_updated = { + layer_idx: False for layer_idx in range(len(past_key_values.cross_attention_cache.key_cache)) + } + map_cache_to_layer_device_map(self.get_base_model(), past_key_values) # no-op if not a Cache instance + return past_key_values + else: + if peft_config.peft_type == PeftType.MULTITASK_PROMPT_TUNING: + prompts = prompt_encoder(prompt_tokens, task_ids) + else: + if peft_config.inference_mode: + prompts = prompt_encoder.embedding.weight + else: + # Take only one prompt token sample and expand the output instead of expanding the input, see: + # https://github.com/huggingface/peft/issues/2043#issuecomment-2321522577 + prompt_tokens = prompt_tokens[:1] + prompts = prompt_encoder(prompt_tokens) + prompts = prompts.repeat(batch_size, 1, 1) + return prompts + + def get_nb_trainable_parameters(self) -> tuple[int, int]: + r""" + Returns the number of trainable parameters and the number of all parameters in the model. + """ + trainable_params = 0 + all_param = 0 + for _, param in self.named_parameters(): + num_params = param.numel() + # if using DS Zero 3 and the weights are initialized empty + if num_params == 0 and hasattr(param, "ds_numel"): + num_params = param.ds_numel + + # Due to the design of 4bit linear layers from bitsandbytes + # one needs to multiply the number of parameters by 2 to get + # the correct number of parameters + if param.__class__.__name__ == "Params4bit": + if hasattr(param, "element_size"): + num_bytes = param.element_size() + elif not hasattr(param, "quant_storage"): + num_bytes = 1 + else: + num_bytes = param.quant_storage.itemsize + num_params = num_params * 2 * num_bytes + + all_param += num_params + if param.requires_grad: + trainable_params += num_params + + return trainable_params, all_param + + def print_trainable_parameters(self) -> None: + """ + Prints the number of trainable parameters in the model. + + Note: print_trainable_parameters() uses get_nb_trainable_parameters() which is different from + num_parameters(only_trainable=True) from huggingface/transformers. get_nb_trainable_parameters() returns + (trainable parameters, all parameters) of the Peft Model which includes modified backbone transformer model. + For techniques like LoRA, the backbone transformer model is modified in place with LoRA modules. However, for + prompt tuning, the backbone transformer model is unmodified. num_parameters(only_trainable=True) returns number + of trainable parameters of the backbone transformer model which can be different. + """ + trainable_params, all_param = self.get_nb_trainable_parameters() + + print( + f"trainable params: {trainable_params:,d} || all params: {all_param:,d} || trainable%: {100 * trainable_params / all_param:.4f}" + ) + + def __getattr__(self, name: str): + """Forward missing attributes to the wrapped module.""" + try: + return super().__getattr__(name) # defer to nn.Module's logic + except AttributeError: + if name == "base_model": # see #1892: prevent infinite recursion if class is not initialized + raise + return getattr(self.base_model, name) + + @contextmanager + def _enable_peft_forward_hooks(self, *args, **kwargs): + # If the base model has a method called _enable_peft_forward_hooks, it is invoked as a context. Otherwise, this + # runs without any changes + if hasattr(self.base_model, "_enable_peft_forward_hooks"): + with self.base_model._enable_peft_forward_hooks(*args, **kwargs): + yield + return + else: + # nothing to enable + yield + return + + def forward(self, *args: Any, **kwargs: Any): + """ + Forward pass of the model. + """ + with self._enable_peft_forward_hooks(*args, **kwargs): + kwargs = {k: v for k, v in kwargs.items() if k not in self.special_peft_forward_args} + return self.get_base_model()(*args, **kwargs) + + def generate(self, *args, **kwargs): + with self._enable_peft_forward_hooks(*args, **kwargs): + kwargs = {k: v for k, v in kwargs.items() if k not in self.special_peft_forward_args} + return self.get_base_model().generate(*args, **kwargs) + + def _get_base_model_class(self, is_prompt_tuning=False): + """ + Returns the base model class. + """ + if not is_prompt_tuning: + return self.base_model.model.__class__ + return self.base_model.__class__ + + @contextmanager + def disable_adapter(self): + """ + Context manager that disables the adapter module. Use this to run inference on the base model. + + Example: + + ```py + >>> with model.disable_adapter(): + ... model(inputs) + ``` + """ + if self.peft_config[self.active_adapter].is_prompt_learning: + try: + # TODO: consider replacing this patching of methods with a more robust mechanism: setting a flag and + # letting the underlying methods deal with it, same as how LoRA does it. + old_forward = self.forward + self.forward = self.base_model.forward + old_prepare_inputs_for_generation = self.prepare_inputs_for_generation + self.prepare_inputs_for_generation = self.base_model.prepare_inputs_for_generation + yield + finally: + self.forward = old_forward + self.prepare_inputs_for_generation = old_prepare_inputs_for_generation + + elif self.peft_config[self.active_adapter].is_adaption_prompt: + try: + self.base_model.disable_adapter_layers() + yield + finally: + self.base_model.enable_adapter_layers() + + else: # LoRA, LoHa, etc. + model_status = self.get_model_status() + if model_status.enabled == "irregular": + warnings.warn( + "The model contains some adapter layers that are enabled and others that are disabled. " + "This is most likely unintentional. After exiting the disable_adapter context, all adapters " + "will be enabled" + ) + try: + self.base_model.disable_adapter_layers() + yield + finally: + if model_status.enabled is not False: + # model_status.enabled is `True` or `"irregular"` + self.base_model.enable_adapter_layers() + + def get_base_model(self) -> torch.nn.Module: + """ + Returns the base model. + """ + return ( + self.base_model + if (self.active_peft_config.is_prompt_learning or self.peft_type == PeftType.POLY) + else self.base_model.model + ) + + def add_adapter(self, adapter_name: str, peft_config: PeftConfig, low_cpu_mem_usage: bool = False) -> None: + """ + Add an adapter to the model based on the passed configuration. + + This adapter is not trained. To load a trained adapter, check out [`PeftModel.load_adapter`]. + + The name for the new adapter should be unique. + + The new adapter is not automatically set as the active adapter. Use [`PeftModel.set_adapter`] to set the active + adapter. + + Args: + adapter_name (`str`): + The name of the adapter to be added. + peft_config ([`PeftConfig`]): + The configuration of the adapter to be added. + low_cpu_mem_usage (`bool`, `optional`, defaults to `False`): + Create empty adapter weights on meta device. Useful to speed up the process when loading saved + adapters. Don't use this option when creating a new PEFT adapter for training. + + """ + prefix = PEFT_TYPE_TO_PREFIX_MAPPING.get(peft_config.peft_type) + if prefix and adapter_name in prefix: + warnings.warn( + f"Adapter name {adapter_name} should not be contained in the prefix {prefix}." + "This may lead to reinitialization of the adapter weights during loading." + ) + + if peft_config.peft_type != self.peft_type: + raise ValueError( + f"Cannot combine adapters with different peft types. " + f"Found {self.peft_type} and {peft_config.peft_type}." + ) + + try: + if peft_config.is_prompt_learning: + self.peft_config[adapter_name] = peft_config + if hasattr(self.config, "to_dict"): + dict_config = self.config.to_dict() + else: + dict_config = self.config + + peft_config = _prepare_prompt_learning_config(peft_config, dict_config) + self._setup_prompt_encoder(adapter_name) + set_additional_trainable_modules( + model=self.base_model, + peft_config=peft_config, + model_config=BaseTuner.get_model_config(self), + adapter_name=adapter_name, + ) + elif peft_config.is_adaption_prompt: + self.base_model.add_adapter(adapter_name, peft_config) + set_additional_trainable_modules( + model=self.base_model, + peft_config=peft_config, + model_config=BaseTuner.get_model_config(self), + adapter_name=adapter_name, + ) + else: + self.peft_config[adapter_name] = peft_config + self.base_model.inject_adapter( + self.base_model.model, adapter_name, low_cpu_mem_usage=low_cpu_mem_usage + ) + except Exception: # something went wrong, roll back + if adapter_name in self.peft_config: + del self.peft_config[adapter_name] + raise + + def delete_adapter(self, adapter_name: str) -> None: + """ + Deletes an existing adapter. + + Args: + adapter_name (str): Name of the adapter to be deleted. + """ + if adapter_name not in self.peft_config: + raise ValueError(f"Adapter {adapter_name} does not exist") + + self.base_model.delete_adapter(adapter_name=adapter_name) + new_active_adapters = self.active_adapters + num_adapters = len(new_active_adapters) + # Note: PeftModel assumes that there is exactly one active adapter, so we should theoretically raise if + # num_adapters != 1. However, we have allowed this in the past (maybe inadvertently), so we let it slip and + # don't introduce a backwards incompatibility by raising an error. + if num_adapters == 1: + self.active_adapter = new_active_adapters[0] + + @property + def modules_to_save(self) -> Optional[set[str]]: + modules: set[str] = set() + for config in self.peft_config.values(): + if getattr(config, "modules_to_save", None) is not None: + # modules_to_save can only be a sequence of str, not a str + modules.update(config.modules_to_save) + + if not modules: + # for backwards compatibility, as modules_to_save was initialized as None + return None + return modules + + def get_layer_status(self) -> list[TunerLayerStatus]: + """Get the status of each adapter layer in the model. + + This method returns a list of `TunerLayerStatus` dataclass instances, each of which contains the following + attributes: + + - `name` (`str`): + The name of the adapter layer, e.g. `model.encoder.block.0.layer.0.SelfAttention.q`. + - `module_type` (`str`): + The type of the adapter layer, e.g. `lora.Linear`. + - `enabled` (`bool`): + Whether the adapter layer is enabled. + - `active_adapters` (`list[str]`): + The names of the active adapters, if any, e.g. `["default"]`. + - `merged_adapters` (`list[str]`): + The names of the merged adapters, if any, e.g. `["default"]`. + - `available_adapters` (`list[str]`): + The names of the available adapters, e.g. `["default"]`. + + Args: + model ([`~PeftModel`]): + The model to get the adapter layer status from. + + Returns: + list[`peft.peft_model.TunerLayerStatus`]: + A list of dataclasses, each containing the status of the corresponding adapter layer. + + """ + return get_layer_status(self) + + def get_model_status(self) -> TunerModelStatus: + """Get the status of tuners of the model. + + This method returns a `TunerModelStatus` dataclass instance, which contains the following attributes: + + - `base_model_type` (`str`): + The type of the base model, e.g. `T5Model`. + - `adapter_model_type` (`str`): + The type of the adapter model, e.g. `LoraModel`. + - `peft_types` (`dict[str, str]`): + The mapping of adapter name to adapter type, e.g. `{"default": "LORA"}`. + - `trainable_params` (`int`): + The number of trainable parameters in the model. + - `total_params` (`int`): + The total number of parameters in the model. + - `num_adapter_layers` (`int`): + The number of adapter layers in the model. + - `enabled` (`bool`, `Literal["irregular"]`): + Whether all adapter layers are enabled. If some are enabled and some are not, this will be `"irregular"`. + This means that your model is in an inconsistent state and might not work as expected. + - `active_adapters` (`list[str]`, `Literal["irregular"]`): + The names of the active adapters. If the active adapters are not consistent across all layers, this will be + `"irregular"`, which means that your model is in an inconsistent state and might not work as expected. + - `merged_adapters` (`list[str]`, `Literal["irregular"]`): + The names of the merged adapters. If the merged adapters are not consistent across all layers, this will be + `"irregular"`, which means that your model is in an inconsistent state and might not work as expected. + - `available_adapters` (`list[str]`): + The names of the available adapters, e.g. `["default"]`. + + Args: + model ([`~PeftModel`]): + The model to get the adapter layer status from. + + Returns: + `peft.peft_model.TunerModelStatus`: + A dataclass containing the status of the model. + + """ + return get_model_status(self) + + @classmethod + def _split_kwargs(cls, kwargs: dict[str, Any]): + _kwargs_not_in_hf_hub_download_signature = ("use_auth_token",) + hf_hub_download_kwargs = {} + other_kwargs = {} + + for key, value in kwargs.items(): + if key in inspect.signature(hf_hub_download).parameters or key in _kwargs_not_in_hf_hub_download_signature: + hf_hub_download_kwargs[key] = value + else: + other_kwargs[key] = value + + return hf_hub_download_kwargs, other_kwargs + + def _update_offload(self, offload_index: dict[str, dict[str, str]], adapters_weights: dict[str, torch.tensor]): + """ + Update the offload_index and safetensors files for loading and mergine PeftModels with disk-offloaded modules. + + Args: + offload_index (Dict[str: str]): + Dictionary of disk-offloaded modules with their metadata and safetensors filenames + adapters_weights (Dict[str: torch.tensor]): + Dictionary of Peft adapter module names and weights + """ + + if not offload_index: + return offload_index + + prefix = "base_model.model." + # rename offload index weight and model names + adapter_names = list(self.peft_config.keys()) + for adapter_name in adapter_names: + keys = list(offload_index.keys()) + block_id = keys[0].split(".")[0] + "." # for writing safetensors key, + + # replace original offload index keys with PeftModel keys + for key in keys: + suffix_pos = key.rfind(".") + extended_prefix = prefix + key[:suffix_pos] + module = dict(self.named_modules())[extended_prefix] + if isinstance(module, BaseTunerLayer): + new_key = prefix + key[:suffix_pos] + ".base_layer" + key[suffix_pos:] + else: + new_key = prefix + key + offload_index[key]["weight_name"] = new_key + offload_index[new_key] = offload_index[key] + del offload_index[key] + + files_seen = set() + # rename safetensors for dispatch + for new_key in list(offload_index.keys()): + fname = offload_index[new_key]["safetensors_file"] + + # make a new file name + new_fname_list = list(fname.split(os.sep)) + for i, name in enumerate(new_fname_list): + if "--" in name: + new_fname_list[i] += "-peft" + break + new_fname = os.path.join(*new_fname_list) + + if fname in files_seen: + continue + safe_dict = {} + with safe_open(fname, framework="pt") as f: + for safe_key in f.keys(): + safe_tensor = f.get_tensor(safe_key) + metadata = f.metadata() + suffix_pos = safe_key.rfind(".") + extended_prefix = prefix + block_id + safe_key[:suffix_pos] + safe_module = dict(self.named_modules())[extended_prefix] + if isinstance(safe_module, BaseTunerLayer): + final_key = extended_prefix + ".base_layer" + safe_key[suffix_pos:] + lora_dict = {key: val for key, val in adapters_weights.items() if extended_prefix in key} + + # add LoRA keys and values to disk offload + for lora_key, lora_val in lora_dict.items(): + divide = lora_key.rfind(".") + new_key = lora_key[:divide] + f".{adapter_name}" + lora_key[divide:] + safe_dict[new_key] = lora_val + else: + final_key = prefix + block_id + safe_key + safe_dict[final_key] = safe_tensor + files_seen.add(new_fname) + + # avoid overwriting original safetensors + for key in safe_dict.keys(): + offload_index[key] = {"safetensors_file": new_fname, "weight_name": key} + + base_name = os.path.dirname(new_fname) + if not os.path.exists(base_name): + os.makedirs(base_name) + safe_save_file(safe_dict, new_fname, metadata=metadata) + + def _check_new_adapter_config(self, peft_config: PeftConfig, is_trainable: bool) -> None: + """Perform checks on newly added PEFT configs to ensure integrity.""" + if peft_config.is_prompt_learning and is_trainable: + raise ValueError("Cannot set a prompt learning adapter to trainable when loading pretrained adapter.") + + # Since PiSSA/CorDA/OLoRA modifies the base weights, it should not be combined with other adapters. + all_configs = [peft_config] + list(self.peft_config.values()) + if len(all_configs) > 1: + if any(getattr(config, "init_lora_weights", None) == "pissa" for config in all_configs): + msg = ( + "PiSSA changes the base weights of the model and should thus not be used with other adapters. " + "Consider converting the PiSSA adapter into a normal LoRA adapter: " + "https://github.com/huggingface/peft/tree/main/examples/pissa_finetuning#convert-pissa-to-lora" + ) + warnings.warn(msg) + elif any(getattr(config, "init_lora_weights", None) == "corda" for config in all_configs): + msg = ( + "CorDA changes the base weights of the model and should thus not be used with other adapters. " + "Consider converting the CorDA adapter into a normal LoRA adapter: " + "https://github.com/huggingface/peft/tree/main/examples/corda_finetuning#convert-corda-to-lora" + ) + warnings.warn(msg) + elif any(getattr(config, "init_lora_weights", None) == "olora" for config in all_configs): + msg = ( + "OLoRA changes the base weights of the model and should thus not be used with other adapters. " + "Consider converting the OLoRA adapter into a normal LoRA adapter: " + "https://github.com/huggingface/peft/tree/main/examples/olora_finetuning#olora-and-lora" + ) + warnings.warn(msg) + + def load_adapter( + self, + model_id: Union[str, os.PathLike], + adapter_name: str, + is_trainable: bool = False, + torch_device: Optional[str] = None, + autocast_adapter_dtype: bool = True, + ephemeral_gpu_offload: bool = False, + low_cpu_mem_usage: bool = False, + key_mapping: Optional[dict[str, str]] = None, + **kwargs: Any, + ): + """ + Load a trained adapter into the model. + + The name for the new adapter should be unique. + + The new adapter is not automatically set as the active adapter. Use [`PeftModel.set_adapter`] to set the active + adapter. + + Args: + model_id (`str` or `os.PathLike`): + The name of the PEFT configuration to use. Can be either: + - A string, the `model id` of a PEFT configuration hosted inside a model repo on the Hugging Face + Hub. + - A path to a directory containing a PEFT configuration file saved using the `save_pretrained` + method (`./my_peft_config_directory/`). + adapter_name (`str`): + The name of the adapter to be added. + is_trainable (`bool`, *optional*, defaults to `False`): + Whether the adapter should be trainable or not. If `False`, the adapter will be frozen and can only be + used for inference. + torch_device (`str`, *optional*, defaults to None): + The device to load the adapter on. If `None`, the device will be inferred. + autocast_adapter_dtype (`bool`, *optional*, defaults to `True`): + Whether to autocast the adapter dtype. Defaults to `True`. Right now, this will only cast adapter + weights using float16 and bfloat16 to float32, as this is typically required for stable training, and + only affect select PEFT tuners. + ephemeral_gpu_offload (`bool`, *optional*, defaults to `False`): + Whether to use ephemeral GPU offloading for partially loaded modules. Defaults to `False`. + low_cpu_mem_usage (`bool`, `optional`, defaults to `False`): + Create empty adapter weights on meta device before loading the saved weights. Useful to speed up the + process. + key_mapping (dict, *optional*, defaults to None) + Extra mapping of PEFT `state_dict` keys applied before loading the `state_dict`. When this mapping is + applied, the PEFT-specific `"base_model.model"` prefix is removed beforehand and the adapter name (e.g. + `"default"`) is not inserted yet. Only pass this argument if you know what you're doing. + kwargs: (`optional`): + Additional arguments to modify the way the adapter is loaded, e.g. the token for Hugging Face Hub. + """ + from .mapping import PEFT_TYPE_TO_CONFIG_MAPPING + + hf_hub_download_kwargs, kwargs = self._split_kwargs(kwargs) + if torch_device is None: + torch_device = infer_device() + + if adapter_name not in self.peft_config: + # load the config + peft_config = PEFT_TYPE_TO_CONFIG_MAPPING[ + PeftConfig._get_peft_type( + model_id, + **hf_hub_download_kwargs, + ) + ].from_pretrained( + model_id, + ephemeral_gpu_offload=ephemeral_gpu_offload, + **hf_hub_download_kwargs, + ) + self._check_new_adapter_config(peft_config, is_trainable=is_trainable) + peft_config.inference_mode = not is_trainable + self.add_adapter(adapter_name, peft_config, low_cpu_mem_usage=low_cpu_mem_usage) + + adapters_weights = load_peft_weights( + model_id, device=torch_device, key_mapping=key_mapping, **hf_hub_download_kwargs + ) + + # load the weights into the model + ignore_mismatched_sizes = kwargs.get("ignore_mismatched_sizes", False) + load_result = set_peft_model_state_dict( + self, + adapters_weights, + adapter_name=adapter_name, + ignore_mismatched_sizes=ignore_mismatched_sizes, + low_cpu_mem_usage=low_cpu_mem_usage, + ) + + tuner = self.peft_config[adapter_name].peft_type + tuner_prefix = PEFT_TYPE_TO_PREFIX_MAPPING.get(tuner, "") + adapter_missing_keys = [] + + # Filter missing keys specific to the current adapter and tuner prefix. + for key in load_result.missing_keys: + if tuner_prefix in key and adapter_name in key: + adapter_missing_keys.append(key) + + load_result.missing_keys.clear() + load_result.missing_keys.extend(adapter_missing_keys) + + if ( + (getattr(self, "hf_device_map", None) is not None) + and (len(set(self.hf_device_map.values()).intersection({"cpu", "disk"})) > 0) + and len(self.peft_config) == 1 + ): + device_map = kwargs.get("device_map", "auto") + max_memory = kwargs.get("max_memory", None) + offload_folder = kwargs.get("offload_folder", None) + offload_dir = kwargs.get("offload_dir", None) + offload_index = kwargs.get("offload_index", None) + + if offload_dir is not None and offload_folder is not None: + # see https://github.com/huggingface/peft/issues/2541 + raise ValueError("Cannot use `offload_folder` when `offload_dir` is specified.") + elif offload_dir is None: + # to keep backwards compatibility + offload_dir = offload_folder + + dispatch_model_kwargs = {} + # Safety checker for previous `accelerate` versions + # `offload_index` was introduced in https://github.com/huggingface/accelerate/pull/873/ + if "offload_index" in inspect.signature(dispatch_model).parameters: + dispatch_model_kwargs["offload_index"] = offload_index + + no_split_module_classes = self._no_split_modules + + if device_map != "sequential": + max_memory = get_balanced_memory( + self, + max_memory=max_memory, + no_split_module_classes=no_split_module_classes, + low_zero=(device_map == "balanced_low_0"), + ) + + if isinstance(device_map, str): + device_map = infer_auto_device_map( + self, max_memory=max_memory, no_split_module_classes=no_split_module_classes + ) + + self._update_offload(offload_index, adapters_weights) + dispatch_model_kwargs["offload_index"] = offload_index + + dispatch_model( + self, + device_map=device_map, + offload_dir=offload_dir, + **dispatch_model_kwargs, + ) + + hook = AlignDevicesHook(io_same_device=True) + if self.peft_config[adapter_name].is_prompt_learning: + remove_hook_from_submodules(self.prompt_encoder) + add_hook_to_module(self.get_base_model(), hook) + + if hasattr(self.base_model, "_cast_adapter_dtype"): + self.base_model._cast_adapter_dtype( + adapter_name=adapter_name, autocast_adapter_dtype=autocast_adapter_dtype + ) + + # Set model in evaluation mode to deactivate Dropout modules by default + if not is_trainable: + self.eval() + return load_result + + def set_adapter(self, adapter_name: str) -> None: + """ + Sets the active adapter. + + Only one adapter can be active at a time. + + Additionally, this function will set the specified adapter to trainable (i.e., requires_grad=True). If this is + not desired, use the following code. + + ```py + >>> for name, param in model_peft.named_parameters(): + ... if ...: # some check on name (ex. if 'lora' in name) + ... param.requires_grad = False + ``` + + Args: + adapter_name (`str`): + The name of the adapter to be set as active. The adapter must be loaded first. + """ + if adapter_name not in self.peft_config: + raise ValueError(f"Adapter {adapter_name} not found.") + self.active_adapter = adapter_name + if not self.peft_config[adapter_name].is_prompt_learning: + self.base_model.set_adapter(adapter_name) + _set_adapter(self, adapter_name) + + @property + def base_model_torch_dtype(self): + return getattr(self.base_model, "dtype", None) + + @property + def active_peft_config(self): + return self.peft_config[self.active_adapter] + + def _get_peft_specific_model_tags(self): + """Derive tags for the model card from the adapter's config. For example, setting the + base model is important for enabling support for HF inference providers but it also makes models more + searchable on the HF hub. + """ + peft_method = self.active_peft_config.peft_type + if not isinstance(peft_method, str): + peft_method = peft_method.value + + tags = [] + + if hasattr(self.base_model, "model") and isinstance(self.base_model.model, transformers.PreTrainedModel): + tags.append("transformers") + + if peft_method == "LORA": + tags.append("lora") + + if hasattr(self.base_model, "name_or_path"): + tags.append(f"base_model:adapter:{self.base_model.name_or_path}") + + return tags + + def create_or_update_model_card(self, output_dir: str): + """ + Updates or create model card to include information about peft: + 1. Adds `peft` library tag + 2. Adds peft version + 3. Adds base model info + 4. Adds quantization information if it was used + """ + + filename = os.path.join(output_dir, "README.md") + + card = ModelCard.load(filename) if os.path.exists(filename) else ModelCard.from_template(ModelCardData()) + + card.data["library_name"] = "peft" + + tags = set() + base_model = self.get_base_model() + if hasattr(base_model, "model_tags"): + tags = tags.union(base_model.model_tags or []) + + tags = tags.union(self._get_peft_specific_model_tags()) + if tags: + card.data["tags"] = sorted(tags) + + # One of the rare moments where we can select the pipeline tag with certainty, so let's do that. + # Makes it easier to deploy an adapter with auto inference since the user doesn't have to add any tags. + if not card.data.pipeline_tag and isinstance(self, PeftModelForCausalLM): + card.data.pipeline_tag = "text-generation" + + model_config = BaseTuner.get_model_config(self) + model_config = None if model_config == DUMMY_MODEL_CONFIG else model_config + if model_config is not None and "_name_or_path" in model_config: + card.data["base_model"] = model_config["_name_or_path"] + + lines = card.text.splitlines() + + quantization_config = None + if hasattr(model_config, "quantization_config"): + quantization_config = self.config.quantization_config.to_dict() + training_config_text = "" + quantization_prefix = "The following `bitsandbytes` quantization config was used during training:" + # Adds quantization information if it was used + if quantization_config is not None: + training_config_text += f"\n{quantization_prefix}\n" + training_config_text += "\n".join([f"- {name}: {value}" for name, value in quantization_config.items()]) + training_config_text += "\n" + + training_procedure_heading = "## Training procedure" + if quantization_prefix not in lines and bool(training_config_text): + if training_procedure_heading in lines: + lines.insert(lines.index(training_procedure_heading) + 2, training_config_text) + else: + lines.append(f"{training_procedure_heading}\n{training_config_text}") + + # Adds peft version + framework_block_heading = "### Framework versions" + if f"- PEFT {__version__}" not in lines: + if framework_block_heading in lines: + lines.insert(lines.index(framework_block_heading) + 2, f"- PEFT {__version__}") + else: + lines.append(f"{framework_block_heading}\n\n- PEFT {__version__}") + + card.text = "\n".join(lines) + card.save(filename) + + +class PeftModelForSequenceClassification(PeftModel): + """ + Peft model for sequence classification tasks. + + Args: + model ([`~transformers.PreTrainedModel`]): Base transformer model. + peft_config ([`PeftConfig`]): Peft config. + adapter_name (`str`, *optional*): The name of the adapter, defaults to `"default"`. + autocast_adapter_dtype (`bool`, *optional*): + Whether to autocast the adapter dtype. Defaults to `True`. Right now, this will only cast adapter weights + using float16 and bfloat16 to float32, as this is typically required for stable training, and only affect + select PEFT tuners. + + **Attributes**: + - **config** ([`~transformers.PretrainedConfig`]) -- The configuration object of the base model. + - **cls_layer_name** (`str`) -- The name of the classification layer. + + Example: + + ```py + >>> from transformers import AutoModelForSequenceClassification + >>> from peft import PeftModelForSequenceClassification, get_peft_config + + >>> config = { + ... "peft_type": "PREFIX_TUNING", + ... "task_type": "SEQ_CLS", + ... "inference_mode": False, + ... "num_virtual_tokens": 20, + ... "token_dim": 768, + ... "num_transformer_submodules": 1, + ... "num_attention_heads": 12, + ... "num_layers": 12, + ... "encoder_hidden_size": 768, + ... "prefix_projection": False, + ... "postprocess_past_key_value_function": None, + ... } + + >>> peft_config = get_peft_config(config) + >>> model = AutoModelForSequenceClassification.from_pretrained("bert-base-cased") + >>> peft_model = PeftModelForSequenceClassification(model, peft_config) + >>> peft_model.print_trainable_parameters() + trainable params: 370178 || all params: 108680450 || trainable%: 0.3406113979101117 + ``` + """ + + def __init__( + self, model: torch.nn.Module, peft_config: PeftConfig, adapter_name: str = "default", **kwargs + ) -> None: + classifier_module_names = ["classifier", "score"] + + if hasattr(peft_config, "modules_to_save"): + if peft_config.modules_to_save is None: + peft_config.modules_to_save = classifier_module_names[:] + else: + peft_config.modules_to_save.extend(classifier_module_names) + + # The modification of peft_config must happen before the init call as the `modules_to_save` information + # will be used to guard the target layer matching against matching `modules_to_save` layers. Only the + # config is relevant for this, the `modules_to_save` attribute can follow later. + super().__init__(model, peft_config, adapter_name, **kwargs) + + if hasattr(peft_config, "modules_to_save"): + for name, _ in self.base_model.named_children(): + if any(module_name in name for module_name in self.modules_to_save): + self.cls_layer_name = name + break + + # to make sure classifier layer is trainable; this may add a new ModulesToSaveWrapper + _set_trainable(self, adapter_name, module_names=getattr(peft_config, "modules_to_save", None)) + + def add_adapter(self, adapter_name: str, peft_config: PeftConfig, low_cpu_mem_usage: bool = False) -> None: + """ + Add an adapter to the model based on the passed configuration. + + This adapter is not trained. To load a trained adapter, check out [`PeftModel.load_adapter`]. + + The name for the new adapter should be unique. + + The new adapter is not automatically set as the active adapter. Use [`PeftModel.set_adapter`] to set the active + adapter. + + Args: + adapter_name (`str`): + The name of the adapter to be added. + peft_config ([`PeftConfig`]): + The configuration of the adapter to be added. + low_cpu_mem_usage (`bool`, `optional`, defaults to `False`): + Create empty adapter weights on meta device. Useful to speed up the process when loading saved + adapters. Don't use this option when creating a new PEFT adapter for training. + + """ + # ensure that additional adapters also add the classifier layer to modules_to_save + if hasattr(peft_config, "modules_to_save"): + classifier_module_names = ["classifier", "score"] + if peft_config.modules_to_save is None: + peft_config.modules_to_save = classifier_module_names[:] + else: + peft_config.modules_to_save.extend(classifier_module_names) + + return super().add_adapter(adapter_name, peft_config, low_cpu_mem_usage=low_cpu_mem_usage) + + def forward( + self, + input_ids=None, + attention_mask=None, + inputs_embeds=None, + labels=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + task_ids=None, + **kwargs, + ): + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + peft_config = self.active_peft_config + if not peft_config.is_prompt_learning: + with self._enable_peft_forward_hooks(**kwargs): + kwargs = {k: v for k, v in kwargs.items() if k not in self.special_peft_forward_args} + if peft_config.peft_type == PeftType.POLY: + kwargs["task_ids"] = task_ids + return self.base_model( + input_ids=input_ids, + attention_mask=attention_mask, + inputs_embeds=inputs_embeds, + labels=labels, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + **kwargs, + ) + + batch_size = _get_batch_size(input_ids, inputs_embeds) + if attention_mask is not None: + # concat prompt attention mask + prefix_attention_mask = torch.ones(batch_size, peft_config.num_virtual_tokens).to(attention_mask.device) + attention_mask = torch.cat((prefix_attention_mask, attention_mask), dim=1) + if kwargs.get("position_ids", None) is not None: + warnings.warn("Position ids are not supported for parameter efficient tuning. Ignoring position ids.") + kwargs["position_ids"] = None + kwargs.update( + { + "attention_mask": attention_mask, + "labels": labels, + "output_attentions": output_attentions, + "output_hidden_states": output_hidden_states, + "return_dict": return_dict, + } + ) + + if peft_config.peft_type == PeftType.PREFIX_TUNING: + return self._prefix_tuning_forward(input_ids=input_ids, **kwargs) + else: + if kwargs.get("token_type_ids", None) is not None: + kwargs["token_type_ids"] = torch.cat( + ( + torch.zeros(batch_size, peft_config.num_virtual_tokens).to(self.word_embeddings.weight.device), + kwargs["token_type_ids"], + ), + dim=1, + ).long() + if inputs_embeds is None: + inputs_embeds = self.word_embeddings(input_ids) + prompts = self.get_prompt(batch_size=batch_size, task_ids=task_ids) + prompts = prompts.to(inputs_embeds.dtype) + inputs_embeds = torch.cat((prompts, inputs_embeds), dim=1) + return self.base_model(inputs_embeds=inputs_embeds, **kwargs) + + def _prefix_tuning_forward( + self, + input_ids=None, + attention_mask=None, + inputs_embeds=None, + labels=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + **kwargs, + ): + batch_size = _get_batch_size(input_ids, inputs_embeds) + past_key_values = self.get_prompt(batch_size) + fwd_params = list(inspect.signature(self.base_model.forward).parameters.keys()) + kwargs.update( + { + "input_ids": input_ids, + "attention_mask": attention_mask, + "inputs_embeds": inputs_embeds, + "output_attentions": output_attentions, + "output_hidden_states": output_hidden_states, + "return_dict": return_dict, + "past_key_values": past_key_values, + } + ) + if "past_key_values" in fwd_params: + return self.base_model(labels=labels, **kwargs) + else: + transformer_backbone_name = self.base_model.get_submodule(self.transformer_backbone_name) + fwd_params = list(inspect.signature(transformer_backbone_name.forward).parameters.keys()) + if "past_key_values" not in fwd_params: + raise ValueError("Model does not support past key values which are required for prefix tuning.") + outputs = transformer_backbone_name(**kwargs) + pooled_output = outputs[1] if len(outputs) > 1 else outputs[0] + if "dropout" in [name for name, _ in list(self.base_model.named_children())]: + pooled_output = self.base_model.dropout(pooled_output) + logits = self.base_model.get_submodule(self.cls_layer_name)(pooled_output) + + loss = None + if labels is not None: + if self.config.problem_type is None: + if self.base_model.num_labels == 1: + self.config.problem_type = "regression" + elif self.base_model.num_labels > 1 and (labels.dtype == torch.long or labels.dtype == torch.int): + self.config.problem_type = "single_label_classification" + else: + self.config.problem_type = "multi_label_classification" + + if self.config.problem_type == "regression": + loss_fct = MSELoss() + if self.base_model.num_labels == 1: + loss = loss_fct(logits.squeeze(), labels.squeeze()) + else: + loss = loss_fct(logits, labels) + elif self.config.problem_type == "single_label_classification": + loss_fct = CrossEntropyLoss() + loss = loss_fct(logits.view(-1, self.base_model.num_labels), labels.view(-1)) + elif self.config.problem_type == "multi_label_classification": + loss_fct = BCEWithLogitsLoss() + loss = loss_fct(logits, labels) + if not return_dict: + output = (logits,) + outputs[2:] + return ((loss,) + output) if loss is not None else output + + return SequenceClassifierOutput( + loss=loss, + logits=logits, + hidden_states=outputs.hidden_states, + attentions=outputs.attentions, + ) + + +class PeftModelForCausalLM(PeftModel): + """ + Peft model for causal language modeling. + + Args: + model ([`~transformers.PreTrainedModel`]): Base transformer model. + peft_config ([`PeftConfig`]): Peft config. + adapter_name (`str`, *optional*): The name of the adapter, defaults to `"default"`. + autocast_adapter_dtype (`bool`, *optional*): + Whether to autocast the adapter dtype. Defaults to `True`. Right now, this will only cast adapter weights + using float16 and bfloat16 to float32, as this is typically required for stable training, and only affect + select PEFT tuners. + + Example: + + ```py + >>> from transformers import AutoModelForCausalLM + >>> from peft import PeftModelForCausalLM, get_peft_config + + >>> config = { + ... "peft_type": "PREFIX_TUNING", + ... "task_type": "CAUSAL_LM", + ... "inference_mode": False, + ... "num_virtual_tokens": 20, + ... "token_dim": 1280, + ... "num_transformer_submodules": 1, + ... "num_attention_heads": 20, + ... "num_layers": 36, + ... "encoder_hidden_size": 1280, + ... "prefix_projection": False, + ... "postprocess_past_key_value_function": None, + ... } + + >>> peft_config = get_peft_config(config) + >>> model = AutoModelForCausalLM.from_pretrained("gpt2-large") + >>> peft_model = PeftModelForCausalLM(model, peft_config) + >>> peft_model.print_trainable_parameters() + trainable params: 1843200 || all params: 775873280 || trainable%: 0.23756456724479544 + ``` + """ + + def __init__( + self, model: torch.nn.Module, peft_config: PeftConfig, adapter_name: str = "default", **kwargs + ) -> None: + super().__init__(model, peft_config, adapter_name, **kwargs) + self.base_model_prepare_inputs_for_generation = self.base_model.prepare_inputs_for_generation + + def forward( + self, + input_ids=None, + attention_mask=None, + inputs_embeds=None, + labels=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + task_ids=None, + **kwargs, + ): + peft_config = self.active_peft_config + if not peft_config.is_prompt_learning: + if self.base_model.config.model_type == "mpt": + if inputs_embeds is not None: + raise AssertionError("forward in MPTForCausalLM does not support inputs_embeds") + return self.base_model( + input_ids=input_ids, + attention_mask=attention_mask, + labels=labels, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + **kwargs, + ) + + if peft_config.peft_type == PeftType.POLY: + kwargs["task_ids"] = task_ids + + with self._enable_peft_forward_hooks(**kwargs): + kwargs = {k: v for k, v in kwargs.items() if k not in self.special_peft_forward_args} + return self.base_model( + input_ids=input_ids, + attention_mask=attention_mask, + inputs_embeds=inputs_embeds, + labels=labels, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + **kwargs, + ) + + batch_size = _get_batch_size(input_ids, inputs_embeds) + if attention_mask is not None: + # concat prompt attention mask + prefix_attention_mask = torch.ones(batch_size, peft_config.num_virtual_tokens).to(attention_mask.device) + attention_mask = torch.cat((prefix_attention_mask, attention_mask), dim=1) + + if kwargs.get("position_ids", None) is not None: + warnings.warn("Position ids are not supported for parameter efficient tuning. Ignoring position ids.") + kwargs["position_ids"] = None + if kwargs.get("token_type_ids", None) is not None: + warnings.warn("Token type ids are not supported for parameter efficient tuning. Ignoring token type ids") + kwargs["token_type_ids"] = None + kwargs.update( + { + "attention_mask": attention_mask, + "labels": labels, + "output_attentions": output_attentions, + "output_hidden_states": output_hidden_states, + "return_dict": return_dict, + } + ) + + if peft_config.peft_type == PeftType.PREFIX_TUNING: + # overwrite past_kv in kwargs + # some archs require max_cache_len to re-initialize the cache + if input_ids is not None: + max_cache_len = input_ids.shape[1] + peft_config.num_virtual_tokens + else: + max_cache_len = inputs_embeds.shape[1] + peft_config.num_virtual_tokens + kwargs["past_key_values"] = self.get_prompt(batch_size, max_cache_len=max_cache_len) + return self.base_model(input_ids=input_ids, inputs_embeds=inputs_embeds, **kwargs) + elif peft_config.peft_type == PeftType.CPT: + return self._cpt_forward(input_ids, inputs_embeds, peft_config, task_ids, batch_size, **kwargs) + else: + if inputs_embeds is None: + inputs_embeds = self.word_embeddings(input_ids) + # concat prompt labels + if labels is not None: + prefix_labels = torch.full((batch_size, peft_config.num_virtual_tokens), -100).to(labels.device) + kwargs["labels"] = torch.cat((prefix_labels, labels), dim=1) + prompts = self.get_prompt(batch_size=batch_size, task_ids=task_ids) + prompts = prompts.to(inputs_embeds.dtype) + inputs_embeds = torch.cat((prompts, inputs_embeds), dim=1) + return self.base_model(inputs_embeds=inputs_embeds, **kwargs) + + def _cpt_forward(self, input_ids, inputs_embeds, peft_config, task_ids, batch_size, **kwargs): + # Extract labels from kwargs + labels = kwargs.pop("labels") + device = [i.device for i in [input_ids, inputs_embeds, labels] if i is not None][0] + # Extract input_type_mask from kwargs and move it to the same device as labels + if "input_type_mask" in kwargs.keys(): + input_type_mask = kwargs.pop("input_type_mask").to(device) + else: + if input_ids is None: + N_tokens = inputs_embeds.shape[1] + else: + N_tokens = input_ids.shape[1] + input_type_mask = torch.ones((batch_size, N_tokens)).to(device) * 4 + + cpt_token_ids = peft_config.cpt_token_ids + cpt_tokens_type_mask = peft_config.cpt_tokens_type_mask + + # Generate embeddings if not provided + if inputs_embeds is None: + inputs_embeds = self.word_embeddings(input_ids) + # Get prompt and concatenate with input embeddings + prompts = self.get_prompt(batch_size=batch_size, task_ids=task_ids) + prompts = prompts.to(inputs_embeds.dtype) + inputs_embeds = torch.cat((prompts, inputs_embeds), dim=1) + # If labels are provided, generate prefix labels and type mask + cpt_labels = None + if labels is not None: + # Generate prefix labels and concatenate with the input labels + prefix_labels = torch.Tensor(cpt_token_ids).long().view(1, -1) + prefix_labels = prefix_labels.repeat(batch_size, 1).to(labels.device) + cpt_labels = torch.cat((prefix_labels, labels), dim=1) + # Generate prefix type mask and shift input type mask values to avoid conflicts + prefix_type_mask = torch.Tensor(cpt_tokens_type_mask).long().view(1, -1) + prefix_type_mask = prefix_type_mask.repeat(batch_size, 1).to(labels.device) + adjusted_input_type_mask = input_type_mask + adjusted_input_type_mask[adjusted_input_type_mask > 0] += prefix_type_mask.max() + # Concatenate prefix and shifted input type masks + cpt_type_mask = torch.cat((prefix_type_mask, adjusted_input_type_mask), dim=1) + # Identify valid label positions and mask invalid ones with -100 + labels_idx = (cpt_type_mask > 0) & (cpt_type_mask % 4 == 0) + cpt_labels[~labels_idx] = -100 + # Update kwargs with the modified labels + + kwargs["labels"] = cpt_labels + # Pass the modified inputs to the base model + base_model_output = self.base_model(inputs_embeds=inputs_embeds, **kwargs) + if labels is None: + return base_model_output + else: + # Calculate the loss using the custom CPT loss function + cpt_embedding = PEFT_TYPE_TO_TUNER_MAPPING[peft_config.peft_type] + base_model_output = cpt_embedding.calculate_loss( + base_model_output, cpt_labels, cpt_type_mask, self.peft_config["default"] + ) + return base_model_output + + def generate(self, *args, **kwargs): + peft_config = self.active_peft_config + self.base_model.prepare_inputs_for_generation = self.prepare_inputs_for_generation + if hasattr(self.base_model, "model"): + self.base_model.model.generation_config = self.generation_config + else: + self.base_model.generation_config = self.generation_config + try: + if not peft_config.is_prompt_learning: + with self._enable_peft_forward_hooks(*args, **kwargs): + kwargs = {k: v for k, v in kwargs.items() if k not in self.special_peft_forward_args} + outputs = self.base_model.generate(*args, **kwargs) + else: + outputs = self.base_model.generate(**kwargs) + except: + self.base_model.prepare_inputs_for_generation = self.base_model_prepare_inputs_for_generation + raise + else: + self.base_model.prepare_inputs_for_generation = self.base_model_prepare_inputs_for_generation + return outputs + + def prepare_inputs_for_generation(self, *args, task_ids: Optional[torch.Tensor] = None, **kwargs): + peft_config = self.active_peft_config + model_kwargs = self.base_model_prepare_inputs_for_generation(*args, **kwargs) + + # https://github.com/huggingface/transformers/pull/26681/ introduced new cache format + # for some architectures which requires a special fix for prompt tuning etc. + # TODO: starting with transformers 4.38, all architectures should support caching. + uses_transformers_4_38 = packaging.version.parse(transformers.__version__) >= packaging.version.parse("4.38.0") + uses_transformers_4_36 = packaging.version.parse(transformers.__version__) >= packaging.version.parse("4.36.0") + transformers_new_cache_archs = ["llama", "mistral", "persimmon", "phi"] + if packaging.version.parse(transformers.__version__) > packaging.version.parse("4.43.3"): + # https://github.com/huggingface/transformers/pull/31445 + transformers_new_cache_archs.append("bloom") + + uses_cache = uses_transformers_4_38 or ( + uses_transformers_4_36 and self.base_model.config.model_type in transformers_new_cache_archs + ) + + # heuristic to determine if we're in 'prefill stage' (when the KV cache is filled with the values from the + # initial input) + is_prefill = (model_kwargs.get("cache_position") is not None) and (model_kwargs["cache_position"][0] == 0) + + if peft_config.peft_type == PeftType.POLY: + model_kwargs["task_ids"] = task_ids + if peft_config.is_prompt_learning: + if uses_cache and (model_kwargs.get("past_key_values", None) is not None): + # change in the logic of `prepare_inputs_for_generation` makes the below code necessary + # In prompt learning methods, past key values are longer when compared to the `input_ids`. + # As such only consider the last input ids in the autogressive generation phase. + past_key_values = model_kwargs["past_key_values"] + if isinstance(past_key_values, (tuple, list)): + seq_len = past_key_values[0][0].shape[-2] + else: # using transformers kv cache + seq_len = past_key_values.get_seq_length() + if seq_len >= model_kwargs["input_ids"].shape[1]: + model_kwargs["input_ids"] = model_kwargs["input_ids"][:, -1:] + + if (attention_mask := model_kwargs.get("attention_mask", None)) is not None: + if isinstance(attention_mask, dict): + # see: https://github.com/huggingface/transformers/pull/37866 + # For now, just deal with the case of a single attention mask + if len(attention_mask) != 1: + raise ValueError( + f"Expected a single attention mask, got {len(attention_mask)} instead, please open an " + "issue (https://github.com/huggingface/peft/issues) and report the error." + ) + attention_mask = list(attention_mask.values())[0] + + size = model_kwargs["input_ids"].shape[0], peft_config.num_virtual_tokens + prefix_attention_mask = torch.ones(size).to(model_kwargs["input_ids"].device) + if attention_mask.dim() == 4: + # Transform the 4d attention mask to 2d, leave it up to the model to deal with it instead of trying + # to create a 4d attention mask here. + # from [batch_size, heads, input_ids_length, total_sequence_length] + # to [batch_size, total_sequence_length] + bs = attention_mask.shape[0] + total_seq_len = prefix_attention_mask.shape[1] + attention_mask.shape[2] + attention_mask_2d = torch.ones((bs, total_seq_len), dtype=attention_mask.dtype) + + if is_prefill and (peft_config.peft_type != PeftType.PREFIX_TUNING): + # if in prefill stage, for prompt learning methods that are not prefix tuning, new tokens + # (embeddings) are inserted, thus set cache_position to correspond to these tokens + cache_position_ = torch.arange(total_seq_len, device=model_kwargs["input_ids"].device) + else: + # prefix tuning acts directly on the cache, no need to upate cache_position + cache_position_ = model_kwargs["cache_position"] + + attention_mask_new = create_attention_mask( + self.get_base_model(), + model_input=None, + attention_mask=attention_mask_2d, + past_key_values=model_kwargs.get("past_key_values"), + cache_position=cache_position_, + batch_size=bs, + sequence_length=total_seq_len, + position_ids=model_kwargs.get("position_ids", None), + ) + model_kwargs["attention_mask"] = attention_mask_new + else: + # 2d attention mask + model_kwargs["attention_mask"] = torch.cat((prefix_attention_mask, attention_mask), dim=1) + + if model_kwargs.get("position_ids", None) is not None: + warnings.warn("Position ids are not supported for parameter efficient tuning. Ignoring position ids.") + model_kwargs["position_ids"] = None + + if kwargs.get("token_type_ids", None) is not None: + warnings.warn( + "Token type ids are not supported for parameter efficient tuning. Ignoring token type ids" + ) + kwargs["token_type_ids"] = None + + # no past_key_values or past_key_values empty cache + requires_prompt_injection = (model_kwargs.get("past_key_values", None) is None) or ( + isinstance(model_kwargs["past_key_values"], transformers.Cache) + and not model_kwargs["past_key_values"].get_seq_length() + ) + + if requires_prompt_injection and peft_config.peft_type == PeftType.PREFIX_TUNING: + # some archs require max_cache_len to re-initialize the cache + max_cache_len = getattr(model_kwargs.get("past_key_values", None), "max_cache_len", None) + new_past_key_values = self.get_prompt( + batch_size=model_kwargs["input_ids"].shape[0], + max_cache_len=max_cache_len, + ) + model_kwargs["past_key_values"] = new_past_key_values + elif requires_prompt_injection: + inputs_embeds = self.word_embeddings(model_kwargs["input_ids"]) + prompts = self.get_prompt(batch_size=model_kwargs["input_ids"].shape[0], task_ids=task_ids) + prompts = prompts.to(inputs_embeds.dtype) + model_kwargs["inputs_embeds"] = torch.cat((prompts, inputs_embeds), dim=1) + model_kwargs["input_ids"] = None + + # if we're in the prefill stage + if is_prefill and (peft_config.peft_type == PeftType.PREFIX_TUNING): + # for prefix tuning, the past_key_values have been prefilled + model_kwargs["cache_position"] += peft_config.num_virtual_tokens + elif peft_config.peft_type != PeftType.PREFIX_TUNING: # prefix tuning needs cache_position + # For transformers>=4.38.0 - for some architectures such as Llama, `cache_position` is passed in the forward + # pass to keep track of the position ids of the cache. We have to pop that from `model_kwargs` as + # `cache_position` is properly created by the model, using the passed `inputs_embeds`: + # https://github.com/huggingface/transformers/blob/593230f0a1150ea9c0477b9d859f25daf73c8c33/src/transformers/models/llama/modeling_llama.py#L956 + _ = model_kwargs.pop("cache_position", None) + + return model_kwargs + + +class PeftModelForSeq2SeqLM(PeftModel): + """ + Peft model for sequence-to-sequence language modeling. + + Args: + model ([`~transformers.PreTrainedModel`]): Base transformer model. + peft_config ([`PeftConfig`]): Peft config. + adapter_name (`str`, *optional*): The name of the adapter, defaults to `"default"`. + autocast_adapter_dtype (`bool`, *optional*): + Whether to autocast the adapter dtype. Defaults to `True`. Right now, this will only cast adapter weights + using float16 and bfloat16 to float32, as this is typically required for stable training, and only affect + select PEFT tuners. + + Example: + + ```py + >>> from transformers import AutoModelForSeq2SeqLM + >>> from peft import PeftModelForSeq2SeqLM, get_peft_config + + >>> config = { + ... "peft_type": "LORA", + ... "task_type": "SEQ_2_SEQ_LM", + ... "inference_mode": False, + ... "r": 8, + ... "target_modules": ["q", "v"], + ... "lora_alpha": 32, + ... "lora_dropout": 0.1, + ... "fan_in_fan_out": False, + ... "enable_lora": None, + ... "bias": "none", + ... } + + >>> peft_config = get_peft_config(config) + >>> model = AutoModelForSeq2SeqLM.from_pretrained("t5-base") + >>> peft_model = PeftModelForSeq2SeqLM(model, peft_config) + >>> peft_model.print_trainable_parameters() + trainable params: 884736 || all params: 223843584 || trainable%: 0.3952474242013566 + ``` + """ + + def __init__( + self, model: torch.nn.Module, peft_config: PeftConfig, adapter_name: str = "default", **kwargs + ) -> None: + super().__init__(model, peft_config, adapter_name, **kwargs) + self.base_model_prepare_inputs_for_generation = self.base_model.prepare_inputs_for_generation + self.base_model_prepare_encoder_decoder_kwargs_for_generation = ( + self.base_model._prepare_encoder_decoder_kwargs_for_generation + ) + + def forward( + self, + input_ids=None, + attention_mask=None, + inputs_embeds=None, + decoder_input_ids=None, + decoder_attention_mask=None, + decoder_inputs_embeds=None, + labels=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + task_ids=None, + **kwargs, + ): + peft_config = self.active_peft_config + if not peft_config.is_prompt_learning: + if peft_config.peft_type == PeftType.POLY: + kwargs["task_ids"] = task_ids + + with self._enable_peft_forward_hooks(**kwargs): + kwargs = {k: v for k, v in kwargs.items() if k not in self.special_peft_forward_args} + return self.base_model( + input_ids=input_ids, + attention_mask=attention_mask, + inputs_embeds=inputs_embeds, + decoder_input_ids=decoder_input_ids, + decoder_attention_mask=decoder_attention_mask, + decoder_inputs_embeds=decoder_inputs_embeds, + labels=labels, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + **kwargs, + ) + + batch_size = _get_batch_size(input_ids, inputs_embeds) + if decoder_attention_mask is not None: + # concat prompt attention mask + prefix_attention_mask = torch.ones(batch_size, peft_config.num_virtual_tokens).to( + decoder_attention_mask.device + ) + if peft_config.peft_type not in [PeftType.PROMPT_TUNING, PeftType.P_TUNING]: + decoder_attention_mask = torch.cat((prefix_attention_mask, decoder_attention_mask), dim=1) + + if kwargs.get("position_ids", None) is not None: + warnings.warn("Position ids are not supported for parameter efficient tuning. Ignoring position ids.") + kwargs["position_ids"] = None + if kwargs.get("token_type_ids", None) is not None: + warnings.warn("Token type ids are not supported for parameter efficient tuning. Ignoring token type ids") + kwargs["token_type_ids"] = None + kwargs.update( + { + "attention_mask": attention_mask, + "decoder_attention_mask": decoder_attention_mask, + "labels": labels, + "output_attentions": output_attentions, + "output_hidden_states": output_hidden_states, + "return_dict": return_dict, + } + ) + + if peft_config.peft_type == PeftType.PREFIX_TUNING: + # overwrite past_kv in kwargs + kwargs["past_key_values"] = self.get_prompt(batch_size) + return self.base_model( + input_ids=input_ids, + decoder_input_ids=decoder_input_ids, + decoder_inputs_embeds=decoder_inputs_embeds, + **kwargs, + ) + elif peft_config.peft_type in [PeftType.PROMPT_TUNING, PeftType.P_TUNING]: + if inputs_embeds is None: + inputs_embeds = self.word_embeddings(input_ids) + + if attention_mask is not None: + # concat prompt attention mask + prefix_attention_mask = torch.ones(batch_size, peft_config.num_virtual_tokens).to( + attention_mask.device + ) + kwargs["attention_mask"] = torch.cat((prefix_attention_mask, attention_mask), dim=1) + + prompts = self.get_prompt(batch_size=batch_size) + prompts = prompts.to(inputs_embeds.dtype) + inputs_embeds = torch.cat((prompts[:, : peft_config.num_virtual_tokens], inputs_embeds), dim=1) + + return self.base_model( + inputs_embeds=inputs_embeds, + decoder_input_ids=decoder_input_ids, + decoder_inputs_embeds=decoder_inputs_embeds, + **kwargs, + ) + else: + if inputs_embeds is None: + inputs_embeds = self.word_embeddings(input_ids) + if decoder_inputs_embeds is None and decoder_input_ids is None: + decoder_input_ids = shift_tokens_right( + labels, self.config.pad_token_id, self.config.decoder_start_token_id + ) + decoder_inputs_embeds = self.word_embeddings(decoder_input_ids) + + if attention_mask is not None: + # concat prompt attention mask + prefix_attention_mask = torch.ones(batch_size, peft_config.num_virtual_tokens).to( + attention_mask.device + ) + kwargs["attention_mask"] = torch.cat((prefix_attention_mask, attention_mask), dim=1) + # concat prompt labels + if labels is not None: + if peft_config.num_transformer_submodules == 1: + kwargs["labels"] = labels + elif peft_config.num_transformer_submodules == 2: + prefix_labels = torch.full((batch_size, peft_config.num_virtual_tokens), -100).to(labels.device) + kwargs["labels"] = torch.cat((prefix_labels, labels), dim=1) + prompts = self.get_prompt(batch_size=batch_size, task_ids=task_ids) + prompts = prompts.to(inputs_embeds.dtype) + inputs_embeds = torch.cat((prompts[:, : peft_config.num_virtual_tokens], inputs_embeds), dim=1) + if peft_config.num_transformer_submodules == 1: + return self.base_model(inputs_embeds=inputs_embeds, **kwargs) + elif peft_config.num_transformer_submodules == 2: + decoder_inputs_embeds = torch.cat( + (prompts[:, peft_config.num_virtual_tokens :], decoder_inputs_embeds), dim=1 + ) + return self.base_model( + inputs_embeds=inputs_embeds, decoder_inputs_embeds=decoder_inputs_embeds, **kwargs + ) + + def generate(self, **kwargs): + peft_config = self.active_peft_config + self.base_model.prepare_inputs_for_generation = self.prepare_inputs_for_generation + self.base_model._prepare_encoder_decoder_kwargs_for_generation = ( + self._prepare_encoder_decoder_kwargs_for_generation + ) + try: + if not peft_config.is_prompt_learning: + with self._enable_peft_forward_hooks(**kwargs): + kwargs = {k: v for k, v in kwargs.items() if k not in self.special_peft_forward_args} + outputs = self.base_model.generate(**kwargs) + else: + if "input_ids" not in kwargs: + raise ValueError("input_ids must be provided for Peft model generation") + if kwargs.get("position_ids", None) is not None: + warnings.warn( + "Position ids are not supported for parameter efficient tuning. Ignoring position ids." + ) + kwargs["position_ids"] = None + if kwargs.get("token_type_ids", None) is not None: + warnings.warn( + "Token type ids are not supported for parameter efficient tuning. Ignoring token type ids" + ) + kwargs["token_type_ids"] = None + + if peft_config.peft_type == PeftType.PREFIX_TUNING: + outputs = self.base_model.generate(**kwargs) + elif peft_config.peft_type in [ + PeftType.PROMPT_TUNING, + PeftType.P_TUNING, + PeftType.MULTITASK_PROMPT_TUNING, + ]: + kwargs = deepcopy(kwargs) + + if "encoder_outputs" in kwargs: + del kwargs["encoder_outputs"] + warnings.warn( + "`encoder_outputs` should not be passed to `generate` when using prompt tuning. Ignoring it." + ) + + input_ids = kwargs.pop("input_ids") + inputs_embeds = self.word_embeddings(input_ids) + batch_size = inputs_embeds.shape[0] + prompts = self.get_prompt(batch_size=batch_size, task_ids=kwargs.pop("task_ids", None)) + prompts = prompts.to(inputs_embeds.dtype) + + inputs_embeds = torch.cat((prompts[:, : peft_config.num_virtual_tokens], inputs_embeds), dim=1) + kwargs["inputs_embeds"] = inputs_embeds + + if "attention_mask" in kwargs: + prefix_attention_mask = torch.ones(batch_size, peft_config.num_virtual_tokens).to( + kwargs["attention_mask"].device + ) + kwargs["attention_mask"] = torch.cat((prefix_attention_mask, kwargs["attention_mask"]), dim=1) + + return self.base_model.generate(**kwargs) + else: + raise NotImplementedError + except: + self.base_model.prepare_inputs_for_generation = self.base_model_prepare_inputs_for_generation + self.base_model._prepare_encoder_decoder_kwargs_for_generation = ( + self.base_model_prepare_encoder_decoder_kwargs_for_generation + ) + raise + else: + self.base_model.prepare_inputs_for_generation = self.base_model_prepare_inputs_for_generation + self.base_model._prepare_encoder_decoder_kwargs_for_generation = ( + self.base_model_prepare_encoder_decoder_kwargs_for_generation + ) + return outputs + + def prepare_inputs_for_generation(self, *args, task_ids: torch.Tensor = None, **kwargs): + peft_config = self.active_peft_config + model_kwargs = self.base_model_prepare_inputs_for_generation(*args, **kwargs) + if peft_config.peft_type == PeftType.POLY: + model_kwargs["task_ids"] = task_ids + elif peft_config.peft_type == PeftType.PREFIX_TUNING: + past_key_values = model_kwargs.get("past_key_values", None) + cache_position = model_kwargs.get("cache_position", [None]) + # check prefill stage + is_prefill_stage = ( + # old cache implementation + (past_key_values is None) + # new cache implementation + or (isinstance(past_key_values, Cache) and (cache_position[0] == 0)) + ) + if is_prefill_stage: + batch_size = model_kwargs["decoder_input_ids"].shape[0] + new_past_key_values = self.get_prompt(batch_size) + model_kwargs["past_key_values"] = new_past_key_values + + return model_kwargs + + +class PeftModelForTokenClassification(PeftModel): + """ + Peft model for token classification tasks. + + Args: + model ([`~transformers.PreTrainedModel`]): Base transformer model. + peft_config ([`PeftConfig`]): Peft config. + adapter_name (`str`, *optional*): The name of the adapter, defaults to `"default"`. + autocast_adapter_dtype (`bool`, *optional*): + Whether to autocast the adapter dtype. Defaults to `True`. Right now, this will only cast adapter weights + using float16 and bfloat16 to float32, as this is typically required for stable training, and only affect + select PEFT tuners. + + **Attributes**: + - **config** ([`~transformers.PretrainedConfig`]) -- The configuration object of the base model. + - **cls_layer_name** (`str`) -- The name of the classification layer. + + Example: + + ```py + >>> from transformers import AutoModelForSequenceClassification + >>> from peft import PeftModelForTokenClassification, get_peft_config + + >>> config = { + ... "peft_type": "PREFIX_TUNING", + ... "task_type": "TOKEN_CLS", + ... "inference_mode": False, + ... "num_virtual_tokens": 20, + ... "token_dim": 768, + ... "num_transformer_submodules": 1, + ... "num_attention_heads": 12, + ... "num_layers": 12, + ... "encoder_hidden_size": 768, + ... "prefix_projection": False, + ... "postprocess_past_key_value_function": None, + ... } + + >>> peft_config = get_peft_config(config) + >>> model = AutoModelForTokenClassification.from_pretrained("bert-base-cased") + >>> peft_model = PeftModelForTokenClassification(model, peft_config) + >>> peft_model.print_trainable_parameters() + trainable params: 370178 || all params: 108680450 || trainable%: 0.3406113979101117 + ``` + """ + + def __init__( + self, model: torch.nn.Module, peft_config: PeftConfig = None, adapter_name: str = "default", **kwargs + ) -> None: + super().__init__(model, peft_config, adapter_name, **kwargs) + + classifier_module_names = ["classifier", "score"] + if hasattr(peft_config, "modules_to_save"): + if peft_config.modules_to_save is None: + peft_config.modules_to_save = classifier_module_names[:] + else: + peft_config.modules_to_save.extend(classifier_module_names) + + for name, _ in self.base_model.named_children(): + if any(module_name in name for module_name in self.modules_to_save): + self.cls_layer_name = name + break + + # to make sure classifier layer is trainable; this may add a new ModulesToSaveWrapper + _set_trainable(self, adapter_name, module_names=getattr(peft_config, "modules_to_save", None)) + + def add_adapter(self, adapter_name: str, peft_config: PeftConfig, low_cpu_mem_usage: bool = False) -> None: + """ + Add an adapter to the model based on the passed configuration. + + This adapter is not trained. To load a trained adapter, check out [`PeftModel.load_adapter`]. + + The name for the new adapter should be unique. + + The new adapter is not automatically set as the active adapter. Use [`PeftModel.set_adapter`] to set the active + adapter. + + Args: + adapter_name (`str`): + The name of the adapter to be added. + peft_config ([`PeftConfig`]): + The configuration of the adapter to be added. + low_cpu_mem_usage (`bool`, `optional`, defaults to `False`): + Create empty adapter weights on meta device. Useful to speed up the process when loading saved + adapters. Don't use this option when creating a new PEFT adapter for training. + + """ + # ensure that additional adapters also add the classifier layer to modules_to_save + if hasattr(peft_config, "modules_to_save"): + classifier_module_names = ["classifier", "score"] + if peft_config.modules_to_save is None: + peft_config.modules_to_save = classifier_module_names[:] + else: + peft_config.modules_to_save.extend(classifier_module_names) + + return super().add_adapter(adapter_name, peft_config, low_cpu_mem_usage=low_cpu_mem_usage) + + def forward( + self, + input_ids=None, + attention_mask=None, + inputs_embeds=None, + labels=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + task_ids=None, + **kwargs, + ): + peft_config = self.active_peft_config + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + + if not peft_config.is_prompt_learning: + with self._enable_peft_forward_hooks(**kwargs): + kwargs = {k: v for k, v in kwargs.items() if k not in self.special_peft_forward_args} + if peft_config.peft_type == PeftType.POLY: + kwargs["task_ids"] = task_ids + return self.base_model( + input_ids=input_ids, + attention_mask=attention_mask, + inputs_embeds=inputs_embeds, + labels=labels, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + **kwargs, + ) + + batch_size = _get_batch_size(input_ids, inputs_embeds) + if attention_mask is not None: + # concat prompt attention mask + prefix_attention_mask = torch.ones(batch_size, peft_config.num_virtual_tokens).to(attention_mask.device) + attention_mask = torch.cat((prefix_attention_mask, attention_mask), dim=1) + if kwargs.get("position_ids", None) is not None: + warnings.warn("Position ids are not supported for parameter efficient tuning. Ignoring position ids.") + kwargs["position_ids"] = None + kwargs.update( + { + "attention_mask": attention_mask, + "labels": labels, + "output_attentions": output_attentions, + "output_hidden_states": output_hidden_states, + "return_dict": return_dict, + } + ) + + if peft_config.peft_type == PeftType.PREFIX_TUNING: + return self._prefix_tuning_forward(input_ids=input_ids, **kwargs) + else: + if kwargs.get("token_type_ids", None) is not None: + kwargs["token_type_ids"] = torch.cat( + ( + torch.zeros(batch_size, peft_config.num_virtual_tokens).to(self.word_embeddings.weight.device), + kwargs["token_type_ids"], + ), + dim=1, + ).long() + if inputs_embeds is None: + inputs_embeds = self.word_embeddings(input_ids) + prompts = self.get_prompt(batch_size=batch_size, task_ids=task_ids) + prompts = prompts.to(inputs_embeds.dtype) + inputs_embeds = torch.cat((prompts, inputs_embeds), dim=1) + return self.base_model(inputs_embeds=inputs_embeds, **kwargs) + + def _prefix_tuning_forward( + self, + input_ids=None, + attention_mask=None, + inputs_embeds=None, + labels=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + **kwargs, + ): + batch_size = _get_batch_size(input_ids, inputs_embeds) + past_key_values = self.get_prompt(batch_size) + fwd_params = list(inspect.signature(self.base_model.forward).parameters.keys()) + kwargs.update( + { + "input_ids": input_ids, + "attention_mask": attention_mask, + "inputs_embeds": inputs_embeds, + "output_attentions": output_attentions, + "output_hidden_states": output_hidden_states, + "return_dict": return_dict, + "past_key_values": past_key_values, + } + ) + if "past_key_values" in fwd_params: + return self.base_model(labels=labels, **kwargs) + else: + transformer_backbone_name = self.base_model.get_submodule(self.transformer_backbone_name) + fwd_params = list(inspect.signature(transformer_backbone_name.forward).parameters.keys()) + if "past_key_values" not in fwd_params: + raise ValueError("Model does not support past key values which are required for prefix tuning.") + outputs = transformer_backbone_name(**kwargs) + sequence_output = outputs[0] + if "dropout" in [name for name, _ in list(self.base_model.named_children())]: + sequence_output = self.base_model.dropout(sequence_output) + logits = self.base_model.get_submodule(self.cls_layer_name)(sequence_output) + + loss = None + if labels is not None: + loss_fct = CrossEntropyLoss() + loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1)) + + if not return_dict: + output = (logits,) + outputs[2:] + return ((loss,) + output) if loss is not None else output + + return TokenClassifierOutput( + loss=loss, + logits=logits, + hidden_states=outputs.hidden_states, + attentions=outputs.attentions, + ) + + +class PeftModelForQuestionAnswering(PeftModel): + """ + Peft model for extractive question answering. + + Args: + model ([`~transformers.PreTrainedModel`]): Base transformer model. + peft_config ([`PeftConfig`]): Peft config. + adapter_name (`str`, *optional*): The name of the adapter, defaults to `"default"`. + autocast_adapter_dtype (`bool`, *optional*): + Whether to autocast the adapter dtype. Defaults to `True`. Right now, this will only cast adapter weights + using float16 and bfloat16 to float32, as this is typically required for stable training, and only affect + select PEFT tuners. + + **Attributes**: + - **config** ([`~transformers.PretrainedConfig`]) -- The configuration object of the base model. + - **cls_layer_name** (`str`) -- The name of the classification layer. + + Example: + + ```py + >>> from transformers import AutoModelForQuestionAnswering + >>> from peft import PeftModelForQuestionAnswering, get_peft_config + + >>> config = { + ... "peft_type": "LORA", + ... "task_type": "QUESTION_ANS", + ... "inference_mode": False, + ... "r": 16, + ... "target_modules": ["query", "value"], + ... "lora_alpha": 32, + ... "lora_dropout": 0.05, + ... "fan_in_fan_out": False, + ... "bias": "none", + ... } + + >>> peft_config = get_peft_config(config) + >>> model = AutoModelForQuestionAnswering.from_pretrained("bert-base-cased") + >>> peft_model = PeftModelForQuestionAnswering(model, peft_config) + >>> peft_model.print_trainable_parameters() + trainable params: 592900 || all params: 108312580 || trainable%: 0.5473971721475013 + ``` + """ + + def __init__( + self, model: torch.nn.Module, peft_config: PeftConfig, adapter_name: str = "default", **kwargs + ) -> None: + super().__init__(model, peft_config, adapter_name, **kwargs) + + qa_module_names = ["qa_outputs"] + if hasattr(peft_config, "modules_to_save"): + if peft_config.modules_to_save is None: + peft_config.modules_to_save = qa_module_names[:] + else: + peft_config.modules_to_save.extend(qa_module_names) + + for name, _ in self.base_model.named_children(): + if any(module_name in name for module_name in self.modules_to_save): + self.cls_layer_name = name + break + + # to make sure classifier layer is trainable; this may add a new ModulesToSaveWrapper + _set_trainable(self, adapter_name, module_names=getattr(peft_config, "modules_to_save", None)) + + def add_adapter(self, adapter_name: str, peft_config: PeftConfig, low_cpu_mem_usage: bool = False) -> None: + """ + Add an adapter to the model based on the passed configuration. + + This adapter is not trained. To load a trained adapter, check out [`PeftModel.load_adapter`]. + + The name for the new adapter should be unique. + + The new adapter is not automatically set as the active adapter. Use [`PeftModel.set_adapter`] to set the active + adapter. + + Args: + adapter_name (`str`): + The name of the adapter to be added. + peft_config ([`PeftConfig`]): + The configuration of the adapter to be added. + low_cpu_mem_usage (`bool`, `optional`, defaults to `False`): + Create empty adapter weights on meta device. Useful to speed up the process when loading saved + adapters. Don't use this option when creating a new PEFT adapter for training. + + """ + # ensure that additional adapters also add the classifier layer to modules_to_save + if hasattr(peft_config, "modules_to_save"): + qa_module_names = ["qa_outputs"] + if peft_config.modules_to_save is None: + peft_config.modules_to_save = qa_module_names[:] + else: + peft_config.modules_to_save.extend(qa_module_names) + + return super().add_adapter(adapter_name, peft_config, low_cpu_mem_usage=low_cpu_mem_usage) + + def forward( + self, + input_ids=None, + attention_mask=None, + token_type_ids=None, + position_ids=None, + inputs_embeds=None, + start_positions=None, + end_positions=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + task_ids=None, + **kwargs, + ): + peft_config = self.active_peft_config + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + + if not peft_config.is_prompt_learning: + if peft_config.peft_type == PeftType.POLY: + kwargs["task_ids"] = task_ids + + with self._enable_peft_forward_hooks(**kwargs): + kwargs = {k: v for k, v in kwargs.items() if k not in self.special_peft_forward_args} + return self.base_model( + input_ids=input_ids, + attention_mask=attention_mask, + inputs_embeds=inputs_embeds, + start_positions=start_positions, + end_positions=end_positions, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + **kwargs, + ) + + batch_size = _get_batch_size(input_ids, inputs_embeds) + if attention_mask is not None: + # concat prompt attention mask + prefix_attention_mask = torch.ones(batch_size, peft_config.num_virtual_tokens).to(attention_mask.device) + attention_mask = torch.cat((prefix_attention_mask, attention_mask), dim=1) + if kwargs.get("position_ids", None) is not None: + warnings.warn("Position ids are not supported for parameter efficient tuning. Ignoring position ids.") + kwargs["position_ids"] = None + kwargs.update( + { + "attention_mask": attention_mask, + "start_positions": start_positions, + "end_positions": end_positions, + "output_attentions": output_attentions, + "output_hidden_states": output_hidden_states, + "return_dict": return_dict, + } + ) + + if peft_config.peft_type == PeftType.PREFIX_TUNING: + return self._prefix_tuning_forward(input_ids=input_ids, **kwargs) + else: + if kwargs.get("token_type_ids", None) is not None: + kwargs["token_type_ids"] = torch.cat( + ( + torch.zeros(batch_size, peft_config.num_virtual_tokens).to(self.word_embeddings.weight.device), + kwargs["token_type_ids"], + ), + dim=1, + ).long() + if inputs_embeds is None: + inputs_embeds = self.word_embeddings(input_ids) + prompts = self.get_prompt(batch_size=batch_size) + prompts = prompts.to(inputs_embeds.dtype) + inputs_embeds = torch.cat((prompts, inputs_embeds), dim=1) + return self.base_model(inputs_embeds=inputs_embeds, **kwargs) + + def _prefix_tuning_forward( + self, + input_ids=None, + attention_mask=None, + inputs_embeds=None, + start_positions=None, + end_positions=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + **kwargs, + ): + batch_size = _get_batch_size(input_ids, inputs_embeds) + past_key_values = self.get_prompt(batch_size) + fwd_params = list(inspect.signature(self.base_model.forward).parameters.keys()) + kwargs.update( + { + "input_ids": input_ids, + "attention_mask": attention_mask, + "inputs_embeds": inputs_embeds, + "output_attentions": output_attentions, + "output_hidden_states": output_hidden_states, + "return_dict": return_dict, + "past_key_values": past_key_values, + } + ) + if "past_key_values" in fwd_params: + return self.base_model(start_positions=start_positions, end_positions=end_positions, **kwargs) + else: + transformer_backbone_name = self.base_model.get_submodule(self.transformer_backbone_name) + fwd_params = list(inspect.signature(transformer_backbone_name.forward).parameters.keys()) + if "past_key_values" not in fwd_params: + raise ValueError("Model does not support past key values which are required for prefix tuning.") + outputs = transformer_backbone_name(**kwargs) + sequence_output = outputs[0] + if "dropout" in [name for name, _ in list(self.base_model.named_children())]: + sequence_output = self.base_model.dropout(sequence_output) + logits = self.base_model.get_submodule(self.cls_layer_name)(sequence_output) + start_logits, end_logits = logits.split(1, dim=-1) + start_logits = start_logits.squeeze(-1).contiguous() + end_logits = end_logits.squeeze(-1).contiguous() + + total_loss = None + if start_positions is not None and end_positions is not None: + # If we are on multi-GPU, split add a dimension + if len(start_positions.size()) > 1: + start_positions = start_positions.squeeze(-1) + if len(end_positions.size()) > 1: + end_positions = end_positions.squeeze(-1) + # sometimes the start/end positions are outside our model inputs, we ignore these terms + ignored_index = start_logits.size(1) + start_positions = start_positions.clamp(0, ignored_index) + end_positions = end_positions.clamp(0, ignored_index) + + loss_fct = CrossEntropyLoss(ignore_index=ignored_index) + start_loss = loss_fct(start_logits, start_positions) + end_loss = loss_fct(end_logits, end_positions) + total_loss = (start_loss + end_loss) / 2 + + if not return_dict: + output = (start_logits, end_logits) + outputs[2:] + return ((total_loss,) + output) if total_loss is not None else output + + return QuestionAnsweringModelOutput( + loss=total_loss, + start_logits=start_logits, + end_logits=end_logits, + hidden_states=outputs.hidden_states, + attentions=outputs.attentions, + ) + + +class PeftModelForFeatureExtraction(PeftModel): + """ + Peft model for extracting features/embeddings from transformer models + + Args: + model ([`~transformers.PreTrainedModel`]): Base transformer model. + peft_config ([`PeftConfig`]): Peft config. + adapter_name (`str`, *optional*): The name of the adapter, defaults to `"default"`. + autocast_adapter_dtype (`bool`, *optional*): + Whether to autocast the adapter dtype. Defaults to `True`. Right now, this will only cast adapter weights + using float16 and bfloat16 to float32, as this is typically required for stable training, and only affect + select PEFT tuners. + + **Attributes**: + - **config** ([`~transformers.PretrainedConfig`]) -- The configuration object of the base model. + + Example: + + ```py + >>> from transformers import AutoModel + >>> from peft import PeftModelForFeatureExtraction, get_peft_config + + >>> config = { + ... "peft_type": "LORA", + ... "task_type": "FEATURE_EXTRACTION", + ... "inference_mode": False, + ... "r": 16, + ... "target_modules": ["query", "value"], + ... "lora_alpha": 32, + ... "lora_dropout": 0.05, + ... "fan_in_fan_out": False, + ... "bias": "none", + ... } + >>> peft_config = get_peft_config(config) + >>> model = AutoModel.from_pretrained("bert-base-cased") + >>> peft_model = PeftModelForFeatureExtraction(model, peft_config) + >>> peft_model.print_trainable_parameters() + ``` + """ + + def __init__(self, model: torch.nn.Module, peft_config: PeftConfig, adapter_name: str = "default", **kwargs): + super().__init__(model, peft_config, adapter_name, **kwargs) + + def forward( + self, + input_ids=None, + attention_mask=None, + inputs_embeds=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + task_ids=None, + **kwargs, + ): + peft_config = self.active_peft_config + if not peft_config.is_prompt_learning: + if peft_config.peft_type == PeftType.POLY: + kwargs["task_ids"] = task_ids + + with self._enable_peft_forward_hooks(**kwargs): + kwargs = {k: v for k, v in kwargs.items() if k not in self.special_peft_forward_args} + return self.base_model( + input_ids=input_ids, + attention_mask=attention_mask, + inputs_embeds=inputs_embeds, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + **kwargs, + ) + + batch_size = _get_batch_size(input_ids, inputs_embeds) + if attention_mask is not None: + # concat prompt attention mask + prefix_attention_mask = torch.ones(batch_size, peft_config.num_virtual_tokens).to(attention_mask.device) + attention_mask = torch.cat((prefix_attention_mask, attention_mask), dim=1) + + if kwargs.get("position_ids", None) is not None: + warnings.warn("Position ids are not supported for parameter efficient tuning. Ignoring position ids.") + kwargs["position_ids"] = None + if kwargs.get("token_type_ids", None) is not None: + warnings.warn("Token type ids are not supported for parameter efficient tuning. Ignoring token type ids") + kwargs["token_type_ids"] = None + kwargs.update( + { + "attention_mask": attention_mask, + "output_attentions": output_attentions, + "output_hidden_states": output_hidden_states, + "return_dict": return_dict, + } + ) + + if peft_config.peft_type == PeftType.PREFIX_TUNING: + # overwrite past_kv in kwargs + kwargs["past_key_values"] = self.get_prompt(batch_size) + return self.base_model(input_ids=input_ids, **kwargs) + else: + if inputs_embeds is None: + inputs_embeds = self.word_embeddings(input_ids) + prompts = self.get_prompt(batch_size=batch_size) + prompts = prompts.to(inputs_embeds.dtype) + inputs_embeds = torch.cat((prompts, inputs_embeds), dim=1) + return self.base_model(inputs_embeds=inputs_embeds, **kwargs) + + +@dataclass +class TunerLayerStatus: + name: str + module_type: str + enabled: bool + active_adapters: list[str] + merged_adapters: list[str] + requires_grad: dict[str, bool | Literal["irregular"]] + available_adapters: list[str] + devices: dict[str, list[str]] + + +def get_layer_status(model: torch.nn.Module) -> list[TunerLayerStatus]: + """Get the status of each adapter layer in the model. + + This function returns a list of `TunerLayerStatus` dataclass instances, each of which contains the following + attributes: + + - `name` (`str`): + The name of the adapter layer, e.g. `model.encoder.block.0.layer.0.SelfAttention.q`. + - `module_type` (`str`): + The type of the adapter layer, e.g. `lora.Linear`. + - `enabled` (`bool`): + Whether the adapter layer is enabled. + - `active_adapters` (`list[str]`): + The names of the active adapters, if any, e.g. `["default"]`. + - `merged_adapters` (`list[str]`): + The names of the merged adapters, if any, e.g. `["default"]`. + - requires_grad : dict[str, bool | Literal["irregular"]] + The requires_grad status of the parameters for each adapter module. Ideally, it should be either `True` or + `False`. If the requires_grad status is not consistent across all parameters, the value will be set to + `"irregular"`. + - `available_adapters` (`list[str]`): + The names of the available adapters, e.g. `["default"]`. + - `devices` (`dict[str, list[str]]`): + The devices where the parameters of the given adapter are stored, e.g. `["cuda"]`. + + Args: + model ([Union[`~PeftModel`, `~transformers.PreTrainedModel`, `nn.Module`]]): + The model to get the adapter layer status from. + + Returns: + list[`peft.peft_model.TunerLayerStatus`]: + A list of dataclasses, each containing the status of the corresponding adapter layer. + + """ + if isinstance(model, PeftModel): + base_model = model.base_model + if not isinstance(base_model, BaseTuner): + raise TypeError( + "get_layer_status() got an invalid PeftModel instance; prefix tuning and adaption prompt are not " + "supported." + ) + else: + base_model = model + + layer_status: list[TunerLayerStatus] = [] + for name, module in base_model.named_modules(): + if not isinstance(module, BaseTunerLayer): + continue + + # determine if all submodules/parameters if this module require grad or not + mapping_requires_grad_list: dict[str, list[bool]] = collections.defaultdict(list) + for adapter_module_name in module.adapter_layer_names: + adapter_module = getattr(module, adapter_module_name) + if isinstance(adapter_module, torch.nn.ModuleDict): + for key, submodule in adapter_module.items(): + for param in submodule.parameters(): + mapping_requires_grad_list[key].append(param.requires_grad) + elif isinstance(adapter_module, torch.nn.ParameterDict): + for key, param in adapter_module.items(): + mapping_requires_grad_list[key].append(param.requires_grad) + else: + # strange, we don't know how to handle this, ignore for now + pass + + def check_irrgular(vals: list[bool]) -> bool | Literal["irregular"]: + if all(vals): + return True + if not any(vals): + return False + return "irregular" + + requires_grad = {key: check_irrgular(vals) for key, vals in mapping_requires_grad_list.items()} + + devices_dd = collections.defaultdict(list) + for adapter_module_name in module.adapter_layer_names + module.other_param_names: + adapter_module = getattr(module, adapter_module_name) + if isinstance(adapter_module, torch.nn.ModuleDict): + for key, submodule in adapter_module.items(): + devices_dd[key].extend([param.device.type for param in submodule.parameters()]) + elif isinstance(adapter_module, torch.nn.ParameterDict) or ( + adapter_module.__class__.__name__ == "BufferDict" + ): # VeRA + for key, param in adapter_module.items(): + devices_dd[key].append(param.device.type) + devices = {key: sorted(set(val)) for key, val in devices_dd.items()} + + status = TunerLayerStatus( + name=name, + module_type=repr(module).partition("(")[0], + enabled=not module.disable_adapters, + active_adapters=module.active_adapters, + merged_adapters=module.merged_adapters, + requires_grad=requires_grad, + available_adapters=sorted(module._get_available_adapters()), + devices=devices, + ) + layer_status.append(status) + + if not layer_status: + raise ValueError( + "No adapter layers found in the model, please ensure that it's a PEFT model or that you have PEFT adapters " + "injected in the model." + ) + + return layer_status + + +@dataclass +class TunerModelStatus: + base_model_type: str + adapter_model_type: str + peft_types: dict[str, str] + trainable_params: int + total_params: int + num_adapter_layers: int + enabled: bool | Literal["irregular"] + active_adapters: list[str] | Literal["irregular"] + merged_adapters: list[str] | Literal["irregular"] + requires_grad: dict[str, bool | Literal["irregular"]] + available_adapters: list[str] + devices: dict[str, list[str]] + + +def get_model_status(model: torch.nn.Module) -> TunerModelStatus: + """Get the status of tuners of the model. + + This function returns a `TunerModelStatus` dataclass instance, which contains the following attributes: + + - `base_model_type` (`str`): + The type of the base model, e.g. `T5Model`. + - `adapter_model_type` (`str`): + The type of the adapter model, e.g. `LoraModel`. + - `peft_types` (`dict[str, str]`): + The mapping of adapter name to adapter type, e.g. `{"default": "LORA"}`. + - `trainable_params` (`int`): + The number of trainable parameters in the model. + - `total_params` (`int`): + The total number of parameters in the model. + - `num_adapter_layers` (`int`): + The number of adapter layers in the model. + - `enabled` (`bool`, `Literal["irregular"]`): + Whether all adapter layers are enabled. If some are enabled and some are not, this will be `"irregular"`. This + means that your model is in an inconsistent state and might not work as expected. + - `active_adapters` (`list[str]`, `Literal["irregular"]`): + The names of the active adapters. If the active adapters are not consistent across all layers, this will be + `"irregular"`, which means that your model is in an inconsistent state and might not work as expected. + - `merged_adapters` (`list[str]`, `Literal["irregular"]`): + The names of the merged adapters. If the merged adapters are not consistent across all layers, this will be + `"irregular"`, which means that your model is in an inconsistent state and might not work as expected. + - `requires_grad` (`dict[str, bool | Literal["irregular"]]`): + Whether for the given adapter, all adapter layers have `requires_grad` set to `True` or `False`. If there is a + mix, this will be set to `"irregular"`, which means that your model is in an inconsistent state and might not + work as expected. + - `available_adapters` (`list[str]`): + The names of the available adapters, e.g. `["default"]`. + - `devices` (`dict[str, list[str]]`): + The devices where the parameters of the given adapter are stored, e.g. `["cuda"]`. + + Args: + model ([Union[`~PeftModel`, `~transformers.PreTrainedModel`, `nn.Module`]]): + The model to get the adapter layer status from. + + Returns: + `peft.peft_model.TunerModelStatus`: + A dataclass containing the status of the model. + + """ + if isinstance(model, PeftModel): + if not isinstance(model.base_model, BaseTuner): + raise TypeError( + "get_model_status() got an invalid PeftModel instance; prefix tuning and adaption prompt are not " + "supported." + ) + base_model_type = model.get_base_model().__class__.__name__ + trainable_params, total_params = model.get_nb_trainable_parameters() + base_model = model.base_model + peft_types = {key: str(config.peft_type).partition(".")[-1] for key, config in base_model.peft_config.items()} + adapter_model_type = base_model.__class__.__name__ + elif isinstance(model, PreTrainedModel): + base_model_type = model.__class__.__name__ + trainable_params, total_params = PeftModel.get_nb_trainable_parameters(model) + base_model = model + peft_types = {} + adapter_model_type = "None" + else: + base_model_type = "other" + trainable_params, total_params = PeftModel.get_nb_trainable_parameters(model) + base_model = model + peft_types = {} + adapter_model_type = "None" + + layer_status = get_layer_status(model) + num_adapter_layers = len(layer_status) + + enabled_set: set[bool] = {status.enabled for status in layer_status} # must be {True}, {False}, or {True, False} + enabled: bool | Literal["irregular"] + if len(enabled_set) == 1: + enabled = enabled_set.pop() + else: + enabled = "irregular" + + available_adapters: list[str] = sorted(set().union(*(status.available_adapters for status in layer_status))) + + # ideally, active adapters should be consistent across all layers of the model, but we cannot guarantee it + all_active_adapters: set[tuple[str, ...]] = {tuple(status.active_adapters) for status in layer_status} + active_adapters: list[str] | Literal["irregular"] + if not all_active_adapters: + active_adapters = [] + elif len(all_active_adapters) == 1: + active_adapters = list(all_active_adapters.pop()) + else: + active_adapters = "irregular" + + # Here we determine what adapters are merged. This is not trivial because multiple adapters can be merged or not at + # the same time. Some layers may only have adapter A, some only adapter B, so it's not as easy as just checking + # which adapters are merged on each layer. + + # First, determine all adapters that are merged on at least on module. + merged_all: set[str] = set() + for status in layer_status: + merged_all.update(status.merged_adapters) + + # Next, check if on any layer, on of these adapters is not merged. + merged_adapters: list[str] | Literal["irregular"] = sorted(merged_all) + for status in layer_status: + unmerged = set(status.available_adapters) - set(status.merged_adapters) + if unmerged & merged_all: + # there is overlap between unmerged adapters and adapters that should be merged + merged_adapters = "irregular" + break + + # check status of requires_grad + # first, merge the values for all layers + requires_grad_all: dict[str, list[bool | Literal["irregular"]]] = collections.defaultdict(list) + for status in layer_status: + for key, val in status.requires_grad.items(): + requires_grad_all[key].append(val) + + # then, check if the values are consistent + def check_irrgular(vals: list[bool | Literal["irregular"]]) -> bool | Literal["irregular"]: + if all(val is True for val in vals): + return True + if all(val is False for val in vals): + return False + return "irregular" + + requires_grad = {key: check_irrgular(vals) for key, vals in requires_grad_all.items()} + + devices_dd = collections.defaultdict(list) + for status in layer_status: + for key, val in status.devices.items(): + devices_dd[key].extend(val) + devices = {key: sorted(set(val)) for key, val in devices_dd.items()} + + adapter_model_status = TunerModelStatus( + base_model_type=base_model_type, + adapter_model_type=adapter_model_type, + peft_types=peft_types, + trainable_params=trainable_params, + total_params=total_params, + num_adapter_layers=num_adapter_layers, + enabled=enabled, + active_adapters=active_adapters, + merged_adapters=merged_adapters, + requires_grad=requires_grad, + available_adapters=available_adapters, + devices=devices, + ) + return adapter_model_status + + +def __getattr__(name): + if name == "PEFT_TYPE_TO_MODEL_MAPPING": + # This is for backwards compatibility: In #2282, PEFT_TYPE_TO_MODEL_MAPPING was removed as it was redundant with + # PEFT_TYPE_TO_TUNER_MAPPING. However, third party code could still use this mapping, e.g.: + # https://github.com/AutoGPTQ/AutoGPTQ/blob/6689349625de973b9ee3016c28c11f32acf7f02c/auto_gptq/utils/peft_utils.py#L8 + # TODO: Remove after 2026-01 + msg = ( + "PEFT_TYPE_TO_MODEL_MAPPING is deprecated, please use `from peft import PEFT_TYPE_TO_TUNER_MAPPING` instead. " + "The deprecated variable will be removed in 2026." + ) + warnings.warn(msg, category=DeprecationWarning) + return PEFT_TYPE_TO_TUNER_MAPPING + + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") diff --git a/.venv/lib/python3.12/site-packages/peft/py.typed b/.venv/lib/python3.12/site-packages/peft/py.typed new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/pillow-11.3.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/pillow-11.3.0.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pillow-11.3.0.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/pillow-11.3.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/pillow-11.3.0.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..ea8c3fd5005b0470c94be125ad04c6eb0a6739fc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pillow-11.3.0.dist-info/METADATA @@ -0,0 +1,177 @@ +Metadata-Version: 2.4 +Name: pillow +Version: 11.3.0 +Summary: Python Imaging Library (Fork) +Author-email: "Jeffrey A. Clark" +License-Expression: MIT-CMU +Project-URL: Changelog, https://github.com/python-pillow/Pillow/releases +Project-URL: Documentation, https://pillow.readthedocs.io +Project-URL: Funding, https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=pypi +Project-URL: Homepage, https://python-pillow.github.io +Project-URL: Mastodon, https://fosstodon.org/@pillow +Project-URL: Release notes, https://pillow.readthedocs.io/en/stable/releasenotes/index.html +Project-URL: Source, https://github.com/python-pillow/Pillow +Keywords: Imaging +Classifier: Development Status :: 6 - Mature +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 :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Multimedia :: Graphics +Classifier: Topic :: Multimedia :: Graphics :: Capture :: Digital Camera +Classifier: Topic :: Multimedia :: Graphics :: Capture :: Screen Capture +Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion +Classifier: Topic :: Multimedia :: Graphics :: Viewers +Classifier: Typing :: Typed +Requires-Python: >=3.9 +Description-Content-Type: text/markdown +License-File: LICENSE +Provides-Extra: docs +Requires-Dist: furo; extra == "docs" +Requires-Dist: olefile; extra == "docs" +Requires-Dist: sphinx>=8.2; extra == "docs" +Requires-Dist: sphinx-autobuild; extra == "docs" +Requires-Dist: sphinx-copybutton; extra == "docs" +Requires-Dist: sphinx-inline-tabs; extra == "docs" +Requires-Dist: sphinxext-opengraph; extra == "docs" +Provides-Extra: fpx +Requires-Dist: olefile; extra == "fpx" +Provides-Extra: mic +Requires-Dist: olefile; extra == "mic" +Provides-Extra: test-arrow +Requires-Dist: pyarrow; extra == "test-arrow" +Provides-Extra: tests +Requires-Dist: check-manifest; extra == "tests" +Requires-Dist: coverage>=7.4.2; extra == "tests" +Requires-Dist: defusedxml; extra == "tests" +Requires-Dist: markdown2; extra == "tests" +Requires-Dist: olefile; extra == "tests" +Requires-Dist: packaging; extra == "tests" +Requires-Dist: pyroma; extra == "tests" +Requires-Dist: pytest; extra == "tests" +Requires-Dist: pytest-cov; extra == "tests" +Requires-Dist: pytest-timeout; extra == "tests" +Requires-Dist: pytest-xdist; extra == "tests" +Requires-Dist: trove-classifiers>=2024.10.12; extra == "tests" +Provides-Extra: typing +Requires-Dist: typing-extensions; python_version < "3.10" and extra == "typing" +Provides-Extra: xmp +Requires-Dist: defusedxml; extra == "xmp" +Dynamic: license-file + +

+ Pillow logo +

+ +# Pillow + +## Python Imaging Library (Fork) + +Pillow is the friendly PIL fork by [Jeffrey A. Clark and +contributors](https://github.com/python-pillow/Pillow/graphs/contributors). +PIL is the Python Imaging Library by Fredrik Lundh and contributors. +As of 2019, Pillow development is +[supported by Tidelift](https://tidelift.com/subscription/pkg/pypi-pillow?utm_source=pypi-pillow&utm_medium=readme&utm_campaign=enterprise). + + + + + + + + + + + + + + + + + + +
docs + Documentation Status +
tests + GitHub Actions build status (Lint) + GitHub Actions build status (Test Linux and macOS) + GitHub Actions build status (Test Windows) + GitHub Actions build status (Test MinGW) + GitHub Actions build status (Test Cygwin) + GitHub Actions build status (Test Docker) + GitHub Actions build status (Wheels) + Code coverage + Fuzzing Status +
package + Zenodo + Tidelift + Newest PyPI version + Number of PyPI downloads + OpenSSF Best Practices +
social + Join the chat at https://gitter.im/python-pillow/Pillow + Follow on https://fosstodon.org/@pillow +
+ +## Overview + +The Python Imaging Library adds image processing capabilities to your Python interpreter. + +This library provides extensive file format support, an efficient internal representation, and fairly powerful image processing capabilities. + +The core image library is designed for fast access to data stored in a few basic pixel formats. It should provide a solid foundation for a general image processing tool. + +## More information + +- [Documentation](https://pillow.readthedocs.io/) + - [Installation](https://pillow.readthedocs.io/en/latest/installation/basic-installation.html) + - [Handbook](https://pillow.readthedocs.io/en/latest/handbook/index.html) +- [Contribute](https://github.com/python-pillow/Pillow/blob/main/.github/CONTRIBUTING.md) + - [Issues](https://github.com/python-pillow/Pillow/issues) + - [Pull requests](https://github.com/python-pillow/Pillow/pulls) +- [Release notes](https://pillow.readthedocs.io/en/stable/releasenotes/index.html) +- [Changelog](https://github.com/python-pillow/Pillow/releases) + - [Pre-fork](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst#pre-fork) + +## Report a vulnerability + +To report a security vulnerability, please follow the procedure described in the [Tidelift security policy](https://tidelift.com/docs/security). diff --git a/.venv/lib/python3.12/site-packages/pillow-11.3.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/pillow-11.3.0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..2889132165097029a29e12c0410f6b93c6c85e57 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pillow-11.3.0.dist-info/RECORD @@ -0,0 +1,138 @@ +PIL/AvifImagePlugin.py,sha256=5IiDMvMZQXLnS3t25XJjlwgNWmeVSNaGfReWAp-V5lo,8994 +PIL/BdfFontFile.py,sha256=PhlZfIRmEfmorbhZZeSM5eebGo1Ei7fL-lR9XlfTZZA,3285 +PIL/BlpImagePlugin.py,sha256=Ub4vVKBEniiNBEgNizxScEpO1VKbC1w6iecWUU7T-Vs,16533 +PIL/BmpImagePlugin.py,sha256=-SNdj2godmaKYAc08dEng6z3mRPbYYHezjveIR5e-tU,19855 +PIL/BufrStubImagePlugin.py,sha256=JSqDhkPNPnFw0Qcz-gQJl-D_iSCFdtcLvPynshKJ4WM,1730 +PIL/ContainerIO.py,sha256=wkBqL2GDAb5fh3wrtfTGUfqioJipCl-lg2GxbjQrTZw,4604 +PIL/CurImagePlugin.py,sha256=bICiwXZrzSONWBu4bKtshxZSNFj8su0lbDojYntEUYs,1797 +PIL/DcxImagePlugin.py,sha256=DhqsmW7MjmnUSTGZ-Skv9hz1XeX3XoQQoAl9GWLAEEY,2145 +PIL/DdsImagePlugin.py,sha256=fjdfZK_eQtUp_-bjoRmt-5wgOT5GTmvg6aI-itch4mo,18906 +PIL/EpsImagePlugin.py,sha256=ROWwCv08bC_B41eMf2AFe8UW6ZH4_XQ18x12KB_aQLM,16389 +PIL/ExifTags.py,sha256=zW6kVikCosiyoCo7J7R62evD3hoxjKPchnVh8po7CZc,9931 +PIL/FitsImagePlugin.py,sha256=-oDJnAH113CK5qPvwz9lL81fkV1gla_tNfqLcq8zKgo,4644 +PIL/FliImagePlugin.py,sha256=DaWuH8f-9GihS0VVZqF1bT3uDv1Vb0VBl0chnNd82Ow,4786 +PIL/FontFile.py,sha256=St7MxO5Q-oakCLWn3ZrgrtaT3wSsmAarxm8AU-G8Moc,3577 +PIL/FpxImagePlugin.py,sha256=aXfg0YdvNeJhxqh-f-f22D1NobQ8tSVCj-tpLE2PKfE,7293 +PIL/FtexImagePlugin.py,sha256=v2I5YkdfNA3iW35JzKnWry9v6Rgvr0oezGVOuArREac,3535 +PIL/GbrImagePlugin.py,sha256=5t0UfLubTPQcuDDbafwC78OLR7IsD5hjpvhUZ5g8z4A,3006 +PIL/GdImageFile.py,sha256=LP4Uxv3Y2ivGZIyOVuGJarDDVS7zK6F1Q6SNl4wyGuQ,2788 +PIL/GifImagePlugin.py,sha256=SkXboZwxTolq0uteXYX0ncrZiUxyASywqAurOcVAi3U,42201 +PIL/GimpGradientFile.py,sha256=Z_4TUYMdPyUsiP40KSIpMJ5yLGMnBaIKOAkHyiQGEWE,3906 +PIL/GimpPaletteFile.py,sha256=YHEhKThsEVlXVjFQUnGvhDgNsJcfFqUAN0O0ucG9G-Q,1815 +PIL/GribStubImagePlugin.py,sha256=degHg344X3JXL8u-x8NWn08BsmM9wRh-Jg08HHrvfOc,1738 +PIL/Hdf5StubImagePlugin.py,sha256=OuEQijGqVwTTSG4dB2vAyQzmN-NYT22tiuZHFH0Q0Sw,1741 +PIL/IcnsImagePlugin.py,sha256=qvi-OP0g8CRlNlJE--5_rPlfyxLFLlSOil66Fw4TMwU,12949 +PIL/IcoImagePlugin.py,sha256=QCo29Toh08UX8vEcdCAaIeuidSolbPiZlCnQ4rUu2SQ,12491 +PIL/ImImagePlugin.py,sha256=wo5OL2PAcQW2MwRkJnS-N16toZzXWL95jx9FBM7l9ok,11567 +PIL/Image.py,sha256=95Jefi2QFIfZYOyfHNBRTwBtwrnNZsn5oCsLQsBLdK8,148332 +PIL/ImageChops.py,sha256=GEjlymcoDtA5OOeIxQVIX96BD-s6AXhb7TmSLYn2tUg,7946 +PIL/ImageCms.py,sha256=A5ZVaTjjxR6AeDNNvK-hmu0QqKOMTscou6BUBTLob0g,41934 +PIL/ImageColor.py,sha256=IGA9C2umeED_EzS2Cvj6KsU0VutC9RstWIYPe8uDsVk,9441 +PIL/ImageDraw.py,sha256=Enr0ctBHKBnSHVBDlqcIbIAyHgVj5ZbLL-swVb8s8Vo,42845 +PIL/ImageDraw2.py,sha256=pdVMW7bVw3KwhXvRZh28Md4y-2xFfuo5fHcDnaYqVK4,7227 +PIL/ImageEnhance.py,sha256=4Elhz_lyyxLmx0GkSHrwOAmNJ2TkqVQPHejzGihZUMI,3627 +PIL/ImageFile.py,sha256=HLgKqn6K9J4HlnyiPFZUTAfcqxXYjE06fZeKO6V-haw,29334 +PIL/ImageFilter.py,sha256=MiTowY9micg1dSfwZkExXSBNPr2b_11kDCGreP6W8x4,18671 +PIL/ImageFont.py,sha256=rVQm3zwnTFZ1HSp4OeA5THKjTezhE8HMrnOhHzmqfEM,64292 +PIL/ImageGrab.py,sha256=I9PHpsQf2VyNX4T8QL-8awFNotyAzB1mGxTt_I5FbTE,6471 +PIL/ImageMath.py,sha256=XasMsgjaD9p2OZa7naOdpEACq3yJl-Q2RGTf4xo7CgM,11919 +PIL/ImageMode.py,sha256=5yOxODAZ7jG03DsUFrt7eQayTtIpWPgvfyhlXDWwcv8,2681 +PIL/ImageMorph.py,sha256=TowXnk1Q2wX9AXVBDWRRQhCfAbFOUWGMo00vq4yn-fU,8563 +PIL/ImageOps.py,sha256=A69qjt-mxDI99387z_4cHI-wtH85SLL_ENTI9EeOQGI,25525 +PIL/ImagePalette.py,sha256=M5tYUgadWR7mxUEByyVl7IV9QFFzAGiKKmAhCZtdG0w,9009 +PIL/ImagePath.py,sha256=5yUG5XCUil1KKTTA_8PgGhcmg-mnue-GK0FwTBlhjw4,371 +PIL/ImageQt.py,sha256=dQbadF2Lg59OJVjiNVcbz3wvymqEpL-uEZG32b8E-bg,6841 +PIL/ImageSequence.py,sha256=gx2EvywPBEjxNJujCqdpbfAm2BpyNV2_f1IaO3niubw,2200 +PIL/ImageShow.py,sha256=Ju0_Db2B4_n3yKJV9sDsF7_HAgciEdXlq6I1Eiw1YTo,10106 +PIL/ImageStat.py,sha256=S43FZ89r_u4hKCj59lVuWpyVJfhbUy3igXkp9DwaMgM,5325 +PIL/ImageTk.py,sha256=b5SntckGXs0ECsI2MmdJg3CSX6AtELsWh0Ohxu41u_k,8132 +PIL/ImageTransform.py,sha256=-qek7P3lzLddcXt9cWt5w_L11JGp2yY3AJtOfmJAkDc,3916 +PIL/ImageWin.py,sha256=LT05w8_vTfRrC3n9S9pM0TNbXrzZLEJHlCJil7Xv80k,8085 +PIL/ImtImagePlugin.py,sha256=SL5IrsHcblltxtX4v_HVFhYnR6haJ0AOd2NHhZKMImY,2665 +PIL/IptcImagePlugin.py,sha256=3BVI_oEbFEJC-yn6zmp5Joqf8edCJLKH9N5FQanyaV8,6719 +PIL/Jpeg2KImagePlugin.py,sha256=k9UoU7-Hq8vAWi9ZoosA4bfufNJsctBd4ttM1RFxwnk,13865 +PIL/JpegImagePlugin.py,sha256=WaCZTpdmzuCM5mi44bNyN4p1EXOsnKz63qv4XEbm8Ns,31786 +PIL/JpegPresets.py,sha256=lnqWHo4DLIHIulcdHp0NJ7CWexHt8T3w51kIKlLfkIA,12379 +PIL/McIdasImagePlugin.py,sha256=baOIkD-CIIeCgBFTf8kos928PKBuCUqYYa38u3WES_8,1877 +PIL/MicImagePlugin.py,sha256=aoIwkWVyr_X-dPvB6ldZOJF3a9kd_OeuEW3say5Y0QM,2564 +PIL/MpegImagePlugin.py,sha256=g7BZd93kWpFi41SG_wKFoi0yEPsioI4kj45b2F-3Vrw,2010 +PIL/MpoImagePlugin.py,sha256=S45qt7OcY7rBjYlwEk0nUmEj5IOu5z8KVLo066V1RBE,6722 +PIL/MspImagePlugin.py,sha256=oxk_MLUDvzJ4JDuOZCHkmqOPXniG42PHOyNGwe60slY,5892 +PIL/PSDraw.py,sha256=KMBGj3vXaFpblaIcA9KjFFTpdal41AQggY-UgzqoMkQ,6918 +PIL/PaletteFile.py,sha256=suDdAL6VMljXw4oEn1vhTt4DQ4vbpIHGd3A4oxOgE6s,1216 +PIL/PalmImagePlugin.py,sha256=WJ1b8I1xTSAXYDJhIpkVFCLu2LlpbiBD5d1Hr-m2l08,8748 +PIL/PcdImagePlugin.py,sha256=VweZ108HBHeNEfsoE26EOR4ktxqNGSOWOnd58DhS8Fo,1601 +PIL/PcfFontFile.py,sha256=NPZQ0XkbGB8uTlGqgmIPGkwuLMYBdykDeVuvFgIC7JU,7147 +PIL/PcxImagePlugin.py,sha256=2dqnjRjSLbjm8Opub4sZRhOIdYLdn3y7Q_ETV8EmiOQ,6224 +PIL/PdfImagePlugin.py,sha256=AbJA2f4qzH8G1olfmk18SzQlcx3WsipUYDc5bcR8Wvk,9349 +PIL/PdfParser.py,sha256=LnmX0Cm7ZQwGkB1uYP4rvXZUkERmURzmYo78zjeq6VI,37987 +PIL/PixarImagePlugin.py,sha256=l_4GwBd0mATnIXYJbwmmODU2vP7wewLu6BRviHCB2EI,1758 +PIL/PngImagePlugin.py,sha256=jPBNqZ50txFHWIsDikcdkeeBfLNY1PxT5wzcPMcmcmQ,51117 +PIL/PpmImagePlugin.py,sha256=QJM-V-odV7w-prA7B5bLRQcykdC4d7OJ5BBbCvPPIzY,12370 +PIL/PsdImagePlugin.py,sha256=ImnNRG4VANs2GATXVEB5Q-yy1Jskc6XRVRtZYi2fALg,8685 +PIL/QoiImagePlugin.py,sha256=RPO63QsgHAsyPpcxh7ymeMYlnjVu5gT5ELolkvJt0vc,8572 +PIL/SgiImagePlugin.py,sha256=3Ql89s8vycNWjcxJwMw28iksV9Yj2xWoKBQ6c5DHXBg,6389 +PIL/SpiderImagePlugin.py,sha256=Bsg6pfZMctas1xYx__oL-ZZseUReZdnLy5a-aKEJhpE,10249 +PIL/SunImagePlugin.py,sha256=Hdxkhk0pxpBGxYhPJfCDLwsYcO1KjxjtplNMFYibIvk,4589 +PIL/TarIO.py,sha256=BqYUChCBb9F7Sh-uZ86iz1Dtoy2D0obNwGm65z1rdc0,1442 +PIL/TgaImagePlugin.py,sha256=2vDsFTcBUBHw1V80wpVv4tgpLDbPr6yVHi6Fvaqf0HY,6980 +PIL/TiffImagePlugin.py,sha256=IK7Ur131NNyJET-wk50tzLkSyd7TI1lwSES4N_txy5w,85029 +PIL/TiffTags.py,sha256=-gbXLZ5rlHD6crwtY6TkafDm2tamlc5v8e7FjS8PcIg,17082 +PIL/WalImageFile.py,sha256=Lfuq_WZ_V_onwucfUc6GWfvY7z_K4s-5EdaQGu_2DD4,5704 +PIL/WebPImagePlugin.py,sha256=YFWo6_FYBSrzAf6XMbmrF4YRtR4x7tYecCWF7EA13WQ,10010 +PIL/WmfImagePlugin.py,sha256=Z1hzGuHGt08tBLsxgBV7ZVOLdQPykDMYd4RGkw1J8rw,5243 +PIL/XVThumbImagePlugin.py,sha256=cJSapkBasFt11O6XYXxqcyA-njxA5BD3wHhNj6VC7Fk,2115 +PIL/XbmImagePlugin.py,sha256=Fd6GVDEo73nyFICA3Z3w4LjkwoZWvhHB6rKCm5yVrho,2669 +PIL/XpmImagePlugin.py,sha256=jtUKavJCYwIAsJaJwSx8vJsx1oTbCywfDxePENmA93w,4400 +PIL/__init__.py,sha256=Q4KOEpR7S_Xsj30fvOsvR94xEpX4KUsVeUwaVP1fU80,2031 +PIL/__main__.py,sha256=Lpj4vef8mI7jA1sRCUAoVYaeePD_Uc898xF5c7XLx1A,133 +PIL/_avif.cpython-312-x86_64-linux-gnu.so,sha256=Zk8Qj3O7vkQl-EZsbz4EkFV4s9e97jRvBOf4irBArC8,87889 +PIL/_avif.pyi,sha256=3fBxcSppJr6EOEcUojvflG3Eegg7lv2Qp0dNQQILrP4,63 +PIL/_binary.py,sha256=pcM6AL04GxgmGeLfcH1V1BZHENwIrQH0uxhJ7r0HIL0,2550 +PIL/_deprecate.py,sha256=JYJfJgemvedcdHMH6_RFTDBLNp4vSJqd-o32e3WzNlM,2034 +PIL/_imaging.cpython-312-x86_64-linux-gnu.so,sha256=In1u4FUyxkYbXW3gVbj-7qricWqAW_g-pcoeHOwcw6c,3382089 +PIL/_imaging.pyi,sha256=StMbXUZS32AegATP1sUHfs5P05A3TD_BiQKsDHQBW40,868 +PIL/_imagingcms.cpython-312-x86_64-linux-gnu.so,sha256=epzZshmZGIbBoI_I5EPPp9pBdVLGEOAUKhLju2A_-7Q,137273 +PIL/_imagingcms.pyi,sha256=brpjxRoiY_2ItyfTrjhKeGEsExe4GPG-25q9AQP8Jp8,4389 +PIL/_imagingft.cpython-312-x86_64-linux-gnu.so,sha256=V_QhgVhpTtOWp0xZ07RdiLFU9izGbjKmb5uEfbr3IjI,310601 +PIL/_imagingft.pyi,sha256=IYdFGfApwsqYiJVoD5AVOvgMvnO1eP1J3cMA6L0YZJ0,1806 +PIL/_imagingmath.cpython-312-x86_64-linux-gnu.so,sha256=5H2EkypJS7uu1_imLKKhzFU_xpo7APN4WVdLOhgnTsM,167000 +PIL/_imagingmath.pyi,sha256=3fBxcSppJr6EOEcUojvflG3Eegg7lv2Qp0dNQQILrP4,63 +PIL/_imagingmorph.cpython-312-x86_64-linux-gnu.so,sha256=4JvsHNFkQIHJoC628iF679Ule_dAcQDNz1w8LLkQhG0,36504 +PIL/_imagingmorph.pyi,sha256=3fBxcSppJr6EOEcUojvflG3Eegg7lv2Qp0dNQQILrP4,63 +PIL/_imagingtk.cpython-312-x86_64-linux-gnu.so,sha256=N-X12JuasRC0yQl897nRxKAGtjn5Dvm857ViDH0eJr4,46688 +PIL/_imagingtk.pyi,sha256=3fBxcSppJr6EOEcUojvflG3Eegg7lv2Qp0dNQQILrP4,63 +PIL/_tkinter_finder.py,sha256=GIZ4stmFhUosmHKSrdxcjStiocDNfyJn7RBie2SWxU0,538 +PIL/_typing.py,sha256=1NAWJ7Z59TP98cFv9qGpBMgSHbyR4CAByLjMRRbSZxY,1251 +PIL/_util.py,sha256=E76J1WLAe6Xg5yNWYztQwYzxUT_sR_VQxFJu7IZ3S3k,635 +PIL/_version.py,sha256=Zwv2LKWt6v32STL5K9uN7PdcJmZhDlokKTLkDA7Ky1w,87 +PIL/_webp.cpython-312-x86_64-linux-gnu.so,sha256=lMw83m0hd2a2ND6BfneEgTkEc9nhzW0Xo9N1tSe9HwY,84193 +PIL/_webp.pyi,sha256=3fBxcSppJr6EOEcUojvflG3Eegg7lv2Qp0dNQQILrP4,63 +PIL/features.py,sha256=FfyYObVJbzYQUXf8KuRuqY6kvA8md2LorE81k3EuQrw,11479 +PIL/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +PIL/report.py,sha256=4JY6-IU7sH1RKuRbOvy1fUt0dAoi79FX4tYJN3p1DT0,100 +pillow-11.3.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +pillow-11.3.0.dist-info/METADATA,sha256=1T1NePio7-GCOWcR73aEA4bSukzNrUIfWMwAw7NAH3M,9023 +pillow-11.3.0.dist-info/RECORD,, +pillow-11.3.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pillow-11.3.0.dist-info/WHEEL,sha256=1crAxrAH5rUbvWUY1UR0ly3o7KnT1jo0_98V8RY5-FM,152 +pillow-11.3.0.dist-info/licenses/LICENSE,sha256=LECn2IlBJv96mTp1JQW5FWlKr-emIIie82isv1S4cUQ,66498 +pillow-11.3.0.dist-info/top_level.txt,sha256=riZqrk-hyZqh5f1Z0Zwii3dKfxEsByhu9cU9IODF-NY,4 +pillow-11.3.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 +pillow.libs/libXau-154567c4.so.6.0.0,sha256=BUhNJL94y47QMWnxywZyBNgpy3ryHeiCBADSnRFeQyA,22081 +pillow.libs/libavif-01e67780.so.16.3.0,sha256=xCXlA8_rggzsCA_EsWKFzgqPBmT1ihpZCo3iMyvD1P0,5142057 +pillow.libs/libbrotlicommon-c55a5f7a.so.1.1.0,sha256=HaLbMm3YehX759wgF7ZU0kVwhdgX4ukfvQNKytoarw8,144425 +pillow.libs/libbrotlidec-2ced2f3a.so.1.1.0,sha256=BOwekVTiRipkYusnBXmzGGhiPsBwBI6DDWPLkvxAbRE,62337 +pillow.libs/libfreetype-083ff72c.so.6.20.2,sha256=nR7Qj8vHmYmFqPKudV_uWIjG3NoY3ZEIGqySuB3Fxi0,1430825 +pillow.libs/libharfbuzz-fe5b8f8d.so.0.61121.0,sha256=7BYYYjM0M0rNxEo1d1yqqSoOGLCcbs-Jqi1KWqxvomM,908081 +pillow.libs/libjpeg-8a13c6e0.so.62.4.0,sha256=L9LNO3lhekD_kggV6DrgsleI_gXfl3EfWwApiRwxy9k,832177 +pillow.libs/liblcms2-cc10e42f.so.2.0.17,sha256=5JMjEDVKMwxcinhbQl6qhRLaezAiQFYEPSz-KultHe0,519073 +pillow.libs/liblzma-64b7ab39.so.5.8.1,sha256=hN2B2RPEM6wOgvER_g43fNjbNQ_SsrenX2wlAfHW-nA,266369 +pillow.libs/libopenjp2-56811f71.so.2.5.3,sha256=aG9iy-0yE3nj44vnrpTFZBHUNFvejVTlV7QEctmnaTo,585849 +pillow.libs/libpng16-d00bd151.so.16.49.0,sha256=VYiCiAoDB6dnWllbcSpOu9DrHKQ3Th4tepiWdhdg_mw,278001 +pillow.libs/libsharpyuv-60a7c00b.so.0.1.1,sha256=jySHi3I26NspkZW9eAaQsPCDwB8_D_Guh7g5d-ROCAQ,46113 +pillow.libs/libtiff-13a02c81.so.6.1.0,sha256=AH827ZKX4f_-DpYv66BELczkhkVdbNmLLFile8vRVgU,746233 +pillow.libs/libwebp-5f0275c0.so.7.1.10,sha256=s6N3iSclHhrkhSaM_cFqk7IAZrWe1pgGmwJk-wzhSYk,731185 +pillow.libs/libwebpdemux-efaed568.so.2.0.16,sha256=q8bcLAXcqize_gg5VszKAHNQ-nBcDU7VVyFKytD1J68,30217 +pillow.libs/libwebpmux-6f2b1ad9.so.3.1.1,sha256=Amxlm7OxjsJWAu9Q-bI59RnoZ9GiZS6FKb2bGccAPUw,58617 +pillow.libs/libxcb-64009ff3.so.1.1.0,sha256=t0N-0WuuesRJgEn9FOENG9HD59FdDl6rHS6tQqg6SdE,251425 diff --git a/.venv/lib/python3.12/site-packages/pillow-11.3.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/pillow-11.3.0.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/pillow-11.3.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/pillow-11.3.0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..895655c2dd100ef008b7c91dd73244160a062179 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pillow-11.3.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: false +Tag: cp312-cp312-manylinux_2_27_x86_64 +Tag: cp312-cp312-manylinux_2_28_x86_64 + diff --git a/.venv/lib/python3.12/site-packages/pillow-11.3.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/pillow-11.3.0.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..b338169ce0c740c335bfe82912227ae8637bd492 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pillow-11.3.0.dist-info/top_level.txt @@ -0,0 +1 @@ +PIL diff --git a/.venv/lib/python3.12/site-packages/pillow-11.3.0.dist-info/zip-safe b/.venv/lib/python3.12/site-packages/pillow-11.3.0.dist-info/zip-safe new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pillow-11.3.0.dist-info/zip-safe @@ -0,0 +1 @@ + diff --git a/.venv/lib/python3.12/site-packages/pkg_resources/__init__.py b/.venv/lib/python3.12/site-packages/pkg_resources/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..926765b8870704128577a990c7826f2cc4cc2f18 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pkg_resources/__init__.py @@ -0,0 +1,3713 @@ +""" +Package resource API +-------------------- + +A resource is a logical file contained within a package, or a logical +subdirectory thereof. The package resource API expects resource names +to have their path parts separated with ``/``, *not* whatever the local +path separator is. Do not use os.path operations to manipulate resource +names being passed into the API. + +The package resource API is designed to work with normal filesystem packages, +.egg files, and unpacked .egg files. It can also work in a limited way with +.zip files and with custom PEP 302 loaders that support the ``get_data()`` +method. + +This module is deprecated. Users are directed to :mod:`importlib.resources`, +:mod:`importlib.metadata` and :pypi:`packaging` instead. +""" + +from __future__ import annotations + +import sys + +if sys.version_info < (3, 9): # noqa: UP036 # Check for unsupported versions + raise RuntimeError("Python 3.9 or later is required") + +import _imp +import collections +import email.parser +import errno +import functools +import importlib +import importlib.abc +import importlib.machinery +import inspect +import io +import ntpath +import operator +import os +import pkgutil +import platform +import plistlib +import posixpath +import re +import stat +import tempfile +import textwrap +import time +import types +import warnings +import zipfile +import zipimport +from collections.abc import Iterable, Iterator, Mapping, MutableSequence +from pkgutil import get_importer +from typing import ( + TYPE_CHECKING, + Any, + BinaryIO, + Callable, + Literal, + NamedTuple, + NoReturn, + Protocol, + TypeVar, + Union, + overload, +) + +sys.path.extend(((vendor_path := os.path.join(os.path.dirname(os.path.dirname(__file__)), 'setuptools', '_vendor')) not in sys.path) * [vendor_path]) # fmt: skip +# workaround for #4476 +sys.modules.pop('backports', None) + +# capture these to bypass sandboxing +from os import open as os_open, utime # isort: skip +from os.path import isdir, split # isort: skip + +try: + from os import mkdir, rename, unlink + + WRITE_SUPPORT = True +except ImportError: + # no write support, probably under GAE + WRITE_SUPPORT = False + +import packaging.markers +import packaging.requirements +import packaging.specifiers +import packaging.utils +import packaging.version +from jaraco.text import drop_comment, join_continuation, yield_lines +from platformdirs import user_cache_dir as _user_cache_dir + +if TYPE_CHECKING: + from _typeshed import BytesPath, StrOrBytesPath, StrPath + from _typeshed.importlib import LoaderProtocol + from typing_extensions import Self, TypeAlias + +warnings.warn( + "pkg_resources is deprecated as an API. " + "See https://setuptools.pypa.io/en/latest/pkg_resources.html. " + "The pkg_resources package is slated for removal as early as " + "2025-11-30. Refrain from using this package or pin to " + "Setuptools<81.", + UserWarning, + stacklevel=2, +) + +_T = TypeVar("_T") +_DistributionT = TypeVar("_DistributionT", bound="Distribution") +# Type aliases +_NestedStr: TypeAlias = Union[str, Iterable[Union[str, Iterable["_NestedStr"]]]] +_StrictInstallerType: TypeAlias = Callable[["Requirement"], "_DistributionT"] +_InstallerType: TypeAlias = Callable[["Requirement"], Union["Distribution", None]] +_PkgReqType: TypeAlias = Union[str, "Requirement"] +_EPDistType: TypeAlias = Union["Distribution", _PkgReqType] +_MetadataType: TypeAlias = Union["IResourceProvider", None] +_ResolvedEntryPoint: TypeAlias = Any # Can be any attribute in the module +_ResourceStream: TypeAlias = Any # TODO / Incomplete: A readable file-like object +# Any object works, but let's indicate we expect something like a module (optionally has __loader__ or __file__) +_ModuleLike: TypeAlias = Union[object, types.ModuleType] +# Any: Should be _ModuleLike but we end up with issues where _ModuleLike doesn't have _ZipLoaderModule's __loader__ +_ProviderFactoryType: TypeAlias = Callable[[Any], "IResourceProvider"] +_DistFinderType: TypeAlias = Callable[[_T, str, bool], Iterable["Distribution"]] +_NSHandlerType: TypeAlias = Callable[[_T, str, str, types.ModuleType], Union[str, None]] +_AdapterT = TypeVar( + "_AdapterT", _DistFinderType[Any], _ProviderFactoryType, _NSHandlerType[Any] +) + + +class _ZipLoaderModule(Protocol): + __loader__: zipimport.zipimporter + + +_PEP440_FALLBACK = re.compile(r"^v?(?P(?:[0-9]+!)?[0-9]+(?:\.[0-9]+)*)", re.I) + + +class PEP440Warning(RuntimeWarning): + """ + Used when there is an issue with a version or specifier not complying with + PEP 440. + """ + + +parse_version = packaging.version.Version + +_state_vars: dict[str, str] = {} + + +def _declare_state(vartype: str, varname: str, initial_value: _T) -> _T: + _state_vars[varname] = vartype + return initial_value + + +def __getstate__() -> dict[str, Any]: + state = {} + g = globals() + for k, v in _state_vars.items(): + state[k] = g['_sget_' + v](g[k]) + return state + + +def __setstate__(state: dict[str, Any]) -> dict[str, Any]: + g = globals() + for k, v in state.items(): + g['_sset_' + _state_vars[k]](k, g[k], v) + return state + + +def _sget_dict(val): + return val.copy() + + +def _sset_dict(key, ob, state) -> None: + ob.clear() + ob.update(state) + + +def _sget_object(val): + return val.__getstate__() + + +def _sset_object(key, ob, state) -> None: + ob.__setstate__(state) + + +_sget_none = _sset_none = lambda *args: None + + +def get_supported_platform(): + """Return this platform's maximum compatible version. + + distutils.util.get_platform() normally reports the minimum version + of macOS that would be required to *use* extensions produced by + distutils. But what we want when checking compatibility is to know the + version of macOS that we are *running*. To allow usage of packages that + explicitly require a newer version of macOS, we must also know the + current version of the OS. + + If this condition occurs for any other platform with a version in its + platform strings, this function should be extended accordingly. + """ + plat = get_build_platform() + m = macosVersionString.match(plat) + if m is not None and sys.platform == "darwin": + try: + major_minor = '.'.join(_macos_vers()[:2]) + build = m.group(3) + plat = f'macosx-{major_minor}-{build}' + except ValueError: + # not macOS + pass + return plat + + +__all__ = [ + # Basic resource access and distribution/entry point discovery + 'require', + 'run_script', + 'get_provider', + 'get_distribution', + 'load_entry_point', + 'get_entry_map', + 'get_entry_info', + 'iter_entry_points', + 'resource_string', + 'resource_stream', + 'resource_filename', + 'resource_listdir', + 'resource_exists', + 'resource_isdir', + # Environmental control + 'declare_namespace', + 'working_set', + 'add_activation_listener', + 'find_distributions', + 'set_extraction_path', + 'cleanup_resources', + 'get_default_cache', + # Primary implementation classes + 'Environment', + 'WorkingSet', + 'ResourceManager', + 'Distribution', + 'Requirement', + 'EntryPoint', + # Exceptions + 'ResolutionError', + 'VersionConflict', + 'DistributionNotFound', + 'UnknownExtra', + 'ExtractionError', + # Warnings + 'PEP440Warning', + # Parsing functions and string utilities + 'parse_requirements', + 'parse_version', + 'safe_name', + 'safe_version', + 'get_platform', + 'compatible_platforms', + 'yield_lines', + 'split_sections', + 'safe_extra', + 'to_filename', + 'invalid_marker', + 'evaluate_marker', + # filesystem utilities + 'ensure_directory', + 'normalize_path', + # Distribution "precedence" constants + 'EGG_DIST', + 'BINARY_DIST', + 'SOURCE_DIST', + 'CHECKOUT_DIST', + 'DEVELOP_DIST', + # "Provider" interfaces, implementations, and registration/lookup APIs + 'IMetadataProvider', + 'IResourceProvider', + 'FileMetadata', + 'PathMetadata', + 'EggMetadata', + 'EmptyProvider', + 'empty_provider', + 'NullProvider', + 'EggProvider', + 'DefaultProvider', + 'ZipProvider', + 'register_finder', + 'register_namespace_handler', + 'register_loader_type', + 'fixup_namespace_packages', + 'get_importer', + # Warnings + 'PkgResourcesDeprecationWarning', + # Deprecated/backward compatibility only + 'run_main', + 'AvailableDistributions', +] + + +class ResolutionError(Exception): + """Abstract base for dependency resolution errors""" + + def __repr__(self) -> str: + return self.__class__.__name__ + repr(self.args) + + +class VersionConflict(ResolutionError): + """ + An already-installed version conflicts with the requested version. + + Should be initialized with the installed Distribution and the requested + Requirement. + """ + + _template = "{self.dist} is installed but {self.req} is required" + + @property + def dist(self) -> Distribution: + return self.args[0] + + @property + def req(self) -> Requirement: + return self.args[1] + + def report(self): + return self._template.format(**locals()) + + def with_context( + self, required_by: set[Distribution | str] + ) -> Self | ContextualVersionConflict: + """ + If required_by is non-empty, return a version of self that is a + ContextualVersionConflict. + """ + if not required_by: + return self + args = self.args + (required_by,) + return ContextualVersionConflict(*args) + + +class ContextualVersionConflict(VersionConflict): + """ + A VersionConflict that accepts a third parameter, the set of the + requirements that required the installed Distribution. + """ + + _template = VersionConflict._template + ' by {self.required_by}' + + @property + def required_by(self) -> set[str]: + return self.args[2] + + +class DistributionNotFound(ResolutionError): + """A requested distribution was not found""" + + _template = ( + "The '{self.req}' distribution was not found " + "and is required by {self.requirers_str}" + ) + + @property + def req(self) -> Requirement: + return self.args[0] + + @property + def requirers(self) -> set[str] | None: + return self.args[1] + + @property + def requirers_str(self): + if not self.requirers: + return 'the application' + return ', '.join(self.requirers) + + def report(self): + return self._template.format(**locals()) + + def __str__(self) -> str: + return self.report() + + +class UnknownExtra(ResolutionError): + """Distribution doesn't have an "extra feature" of the given name""" + + +_provider_factories: dict[type[_ModuleLike], _ProviderFactoryType] = {} + +PY_MAJOR = f'{sys.version_info.major}.{sys.version_info.minor}' +EGG_DIST = 3 +BINARY_DIST = 2 +SOURCE_DIST = 1 +CHECKOUT_DIST = 0 +DEVELOP_DIST = -1 + + +def register_loader_type( + loader_type: type[_ModuleLike], provider_factory: _ProviderFactoryType +) -> None: + """Register `provider_factory` to make providers for `loader_type` + + `loader_type` is the type or class of a PEP 302 ``module.__loader__``, + and `provider_factory` is a function that, passed a *module* object, + returns an ``IResourceProvider`` for that module. + """ + _provider_factories[loader_type] = provider_factory + + +@overload +def get_provider(moduleOrReq: str) -> IResourceProvider: ... +@overload +def get_provider(moduleOrReq: Requirement) -> Distribution: ... +def get_provider(moduleOrReq: str | Requirement) -> IResourceProvider | Distribution: + """Return an IResourceProvider for the named module or requirement""" + if isinstance(moduleOrReq, Requirement): + return working_set.find(moduleOrReq) or require(str(moduleOrReq))[0] + try: + module = sys.modules[moduleOrReq] + except KeyError: + __import__(moduleOrReq) + module = sys.modules[moduleOrReq] + loader = getattr(module, '__loader__', None) + return _find_adapter(_provider_factories, loader)(module) + + +@functools.cache +def _macos_vers(): + version = platform.mac_ver()[0] + # fallback for MacPorts + if version == '': + plist = '/System/Library/CoreServices/SystemVersion.plist' + if os.path.exists(plist): + with open(plist, 'rb') as fh: + plist_content = plistlib.load(fh) + if 'ProductVersion' in plist_content: + version = plist_content['ProductVersion'] + return version.split('.') + + +def _macos_arch(machine): + return {'PowerPC': 'ppc', 'Power_Macintosh': 'ppc'}.get(machine, machine) + + +def get_build_platform(): + """Return this platform's string for platform-specific distributions""" + from sysconfig import get_platform + + plat = get_platform() + if sys.platform == "darwin" and not plat.startswith('macosx-'): + try: + version = _macos_vers() + machine = _macos_arch(os.uname()[4].replace(" ", "_")) + return f"macosx-{version[0]}.{version[1]}-{machine}" + except ValueError: + # if someone is running a non-Mac darwin system, this will fall + # through to the default implementation + pass + return plat + + +macosVersionString = re.compile(r"macosx-(\d+)\.(\d+)-(.*)") +darwinVersionString = re.compile(r"darwin-(\d+)\.(\d+)\.(\d+)-(.*)") +# XXX backward compat +get_platform = get_build_platform + + +def compatible_platforms(provided: str | None, required: str | None) -> bool: + """Can code for the `provided` platform run on the `required` platform? + + Returns true if either platform is ``None``, or the platforms are equal. + + XXX Needs compatibility checks for Linux and other unixy OSes. + """ + if provided is None or required is None or provided == required: + # easy case + return True + + # macOS special cases + reqMac = macosVersionString.match(required) + if reqMac: + provMac = macosVersionString.match(provided) + + # is this a Mac package? + if not provMac: + # this is backwards compatibility for packages built before + # setuptools 0.6. All packages built after this point will + # use the new macOS designation. + provDarwin = darwinVersionString.match(provided) + if provDarwin: + dversion = int(provDarwin.group(1)) + macosversion = f"{reqMac.group(1)}.{reqMac.group(2)}" + if ( + dversion == 7 + and macosversion >= "10.3" + or dversion == 8 + and macosversion >= "10.4" + ): + return True + # egg isn't macOS or legacy darwin + return False + + # are they the same major version and machine type? + if provMac.group(1) != reqMac.group(1) or provMac.group(3) != reqMac.group(3): + return False + + # is the required OS major update >= the provided one? + if int(provMac.group(2)) > int(reqMac.group(2)): + return False + + return True + + # XXX Linux and other platforms' special cases should go here + return False + + +@overload +def get_distribution(dist: _DistributionT) -> _DistributionT: ... +@overload +def get_distribution(dist: _PkgReqType) -> Distribution: ... +def get_distribution(dist: Distribution | _PkgReqType) -> Distribution: + """Return a current distribution object for a Requirement or string""" + if isinstance(dist, str): + dist = Requirement.parse(dist) + if isinstance(dist, Requirement): + dist = get_provider(dist) + if not isinstance(dist, Distribution): + raise TypeError("Expected str, Requirement, or Distribution", dist) + return dist + + +def load_entry_point(dist: _EPDistType, group: str, name: str) -> _ResolvedEntryPoint: + """Return `name` entry point of `group` for `dist` or raise ImportError""" + return get_distribution(dist).load_entry_point(group, name) + + +@overload +def get_entry_map( + dist: _EPDistType, group: None = None +) -> dict[str, dict[str, EntryPoint]]: ... +@overload +def get_entry_map(dist: _EPDistType, group: str) -> dict[str, EntryPoint]: ... +def get_entry_map(dist: _EPDistType, group: str | None = None): + """Return the entry point map for `group`, or the full entry map""" + return get_distribution(dist).get_entry_map(group) + + +def get_entry_info(dist: _EPDistType, group: str, name: str) -> EntryPoint | None: + """Return the EntryPoint object for `group`+`name`, or ``None``""" + return get_distribution(dist).get_entry_info(group, name) + + +class IMetadataProvider(Protocol): + def has_metadata(self, name: str) -> bool: + """Does the package's distribution contain the named metadata?""" + ... + + def get_metadata(self, name: str) -> str: + """The named metadata resource as a string""" + ... + + def get_metadata_lines(self, name: str) -> Iterator[str]: + """Yield named metadata resource as list of non-blank non-comment lines + + Leading and trailing whitespace is stripped from each line, and lines + with ``#`` as the first non-blank character are omitted.""" + ... + + def metadata_isdir(self, name: str) -> bool: + """Is the named metadata a directory? (like ``os.path.isdir()``)""" + ... + + def metadata_listdir(self, name: str) -> list[str]: + """List of metadata names in the directory (like ``os.listdir()``)""" + ... + + def run_script(self, script_name: str, namespace: dict[str, Any]) -> None: + """Execute the named script in the supplied namespace dictionary""" + ... + + +class IResourceProvider(IMetadataProvider, Protocol): + """An object that provides access to package resources""" + + def get_resource_filename( + self, manager: ResourceManager, resource_name: str + ) -> str: + """Return a true filesystem path for `resource_name` + + `manager` must be a ``ResourceManager``""" + ... + + def get_resource_stream( + self, manager: ResourceManager, resource_name: str + ) -> _ResourceStream: + """Return a readable file-like object for `resource_name` + + `manager` must be a ``ResourceManager``""" + ... + + def get_resource_string( + self, manager: ResourceManager, resource_name: str + ) -> bytes: + """Return the contents of `resource_name` as :obj:`bytes` + + `manager` must be a ``ResourceManager``""" + ... + + def has_resource(self, resource_name: str) -> bool: + """Does the package contain the named resource?""" + ... + + def resource_isdir(self, resource_name: str) -> bool: + """Is the named resource a directory? (like ``os.path.isdir()``)""" + ... + + def resource_listdir(self, resource_name: str) -> list[str]: + """List of resource names in the directory (like ``os.listdir()``)""" + ... + + +class WorkingSet: + """A collection of active distributions on sys.path (or a similar list)""" + + def __init__(self, entries: Iterable[str] | None = None) -> None: + """Create working set from list of path entries (default=sys.path)""" + self.entries: list[str] = [] + self.entry_keys: dict[str | None, list[str]] = {} + self.by_key: dict[str, Distribution] = {} + self.normalized_to_canonical_keys: dict[str, str] = {} + self.callbacks: list[Callable[[Distribution], object]] = [] + + if entries is None: + entries = sys.path + + for entry in entries: + self.add_entry(entry) + + @classmethod + def _build_master(cls): + """ + Prepare the master working set. + """ + ws = cls() + try: + from __main__ import __requires__ + except ImportError: + # The main program does not list any requirements + return ws + + # ensure the requirements are met + try: + ws.require(__requires__) + except VersionConflict: + return cls._build_from_requirements(__requires__) + + return ws + + @classmethod + def _build_from_requirements(cls, req_spec): + """ + Build a working set from a requirement spec. Rewrites sys.path. + """ + # try it without defaults already on sys.path + # by starting with an empty path + ws = cls([]) + reqs = parse_requirements(req_spec) + dists = ws.resolve(reqs, Environment()) + for dist in dists: + ws.add(dist) + + # add any missing entries from sys.path + for entry in sys.path: + if entry not in ws.entries: + ws.add_entry(entry) + + # then copy back to sys.path + sys.path[:] = ws.entries + return ws + + def add_entry(self, entry: str) -> None: + """Add a path item to ``.entries``, finding any distributions on it + + ``find_distributions(entry, True)`` is used to find distributions + corresponding to the path entry, and they are added. `entry` is + always appended to ``.entries``, even if it is already present. + (This is because ``sys.path`` can contain the same value more than + once, and the ``.entries`` of the ``sys.path`` WorkingSet should always + equal ``sys.path``.) + """ + self.entry_keys.setdefault(entry, []) + self.entries.append(entry) + for dist in find_distributions(entry, True): + self.add(dist, entry, False) + + def __contains__(self, dist: Distribution) -> bool: + """True if `dist` is the active distribution for its project""" + return self.by_key.get(dist.key) == dist + + def find(self, req: Requirement) -> Distribution | None: + """Find a distribution matching requirement `req` + + If there is an active distribution for the requested project, this + returns it as long as it meets the version requirement specified by + `req`. But, if there is an active distribution for the project and it + does *not* meet the `req` requirement, ``VersionConflict`` is raised. + If there is no active distribution for the requested project, ``None`` + is returned. + """ + dist: Distribution | None = None + + candidates = ( + req.key, + self.normalized_to_canonical_keys.get(req.key), + safe_name(req.key).replace(".", "-"), + ) + + for candidate in filter(None, candidates): + dist = self.by_key.get(candidate) + if dist: + req.key = candidate + break + + if dist is not None and dist not in req: + # XXX add more info + raise VersionConflict(dist, req) + return dist + + def iter_entry_points( + self, group: str, name: str | None = None + ) -> Iterator[EntryPoint]: + """Yield entry point objects from `group` matching `name` + + If `name` is None, yields all entry points in `group` from all + distributions in the working set, otherwise only ones matching + both `group` and `name` are yielded (in distribution order). + """ + return ( + entry + for dist in self + for entry in dist.get_entry_map(group).values() + if name is None or name == entry.name + ) + + def run_script(self, requires: str, script_name: str) -> None: + """Locate distribution for `requires` and run `script_name` script""" + ns = sys._getframe(1).f_globals + name = ns['__name__'] + ns.clear() + ns['__name__'] = name + self.require(requires)[0].run_script(script_name, ns) + + def __iter__(self) -> Iterator[Distribution]: + """Yield distributions for non-duplicate projects in the working set + + The yield order is the order in which the items' path entries were + added to the working set. + """ + seen = set() + for item in self.entries: + if item not in self.entry_keys: + # workaround a cache issue + continue + + for key in self.entry_keys[item]: + if key not in seen: + seen.add(key) + yield self.by_key[key] + + def add( + self, + dist: Distribution, + entry: str | None = None, + insert: bool = True, + replace: bool = False, + ) -> None: + """Add `dist` to working set, associated with `entry` + + If `entry` is unspecified, it defaults to the ``.location`` of `dist`. + On exit from this routine, `entry` is added to the end of the working + set's ``.entries`` (if it wasn't already present). + + `dist` is only added to the working set if it's for a project that + doesn't already have a distribution in the set, unless `replace=True`. + If it's added, any callbacks registered with the ``subscribe()`` method + will be called. + """ + if insert: + dist.insert_on(self.entries, entry, replace=replace) + + if entry is None: + entry = dist.location + keys = self.entry_keys.setdefault(entry, []) + keys2 = self.entry_keys.setdefault(dist.location, []) + if not replace and dist.key in self.by_key: + # ignore hidden distros + return + + self.by_key[dist.key] = dist + normalized_name = packaging.utils.canonicalize_name(dist.key) + self.normalized_to_canonical_keys[normalized_name] = dist.key + if dist.key not in keys: + keys.append(dist.key) + if dist.key not in keys2: + keys2.append(dist.key) + self._added_new(dist) + + @overload + def resolve( + self, + requirements: Iterable[Requirement], + env: Environment | None, + installer: _StrictInstallerType[_DistributionT], + replace_conflicting: bool = False, + extras: tuple[str, ...] | None = None, + ) -> list[_DistributionT]: ... + @overload + def resolve( + self, + requirements: Iterable[Requirement], + env: Environment | None = None, + *, + installer: _StrictInstallerType[_DistributionT], + replace_conflicting: bool = False, + extras: tuple[str, ...] | None = None, + ) -> list[_DistributionT]: ... + @overload + def resolve( + self, + requirements: Iterable[Requirement], + env: Environment | None = None, + installer: _InstallerType | None = None, + replace_conflicting: bool = False, + extras: tuple[str, ...] | None = None, + ) -> list[Distribution]: ... + def resolve( + self, + requirements: Iterable[Requirement], + env: Environment | None = None, + installer: _InstallerType | None | _StrictInstallerType[_DistributionT] = None, + replace_conflicting: bool = False, + extras: tuple[str, ...] | None = None, + ) -> list[Distribution] | list[_DistributionT]: + """List all distributions needed to (recursively) meet `requirements` + + `requirements` must be a sequence of ``Requirement`` objects. `env`, + if supplied, should be an ``Environment`` instance. If + not supplied, it defaults to all distributions available within any + entry or distribution in the working set. `installer`, if supplied, + will be invoked with each requirement that cannot be met by an + already-installed distribution; it should return a ``Distribution`` or + ``None``. + + Unless `replace_conflicting=True`, raises a VersionConflict exception + if + any requirements are found on the path that have the correct name but + the wrong version. Otherwise, if an `installer` is supplied it will be + invoked to obtain the correct version of the requirement and activate + it. + + `extras` is a list of the extras to be used with these requirements. + This is important because extra requirements may look like `my_req; + extra = "my_extra"`, which would otherwise be interpreted as a purely + optional requirement. Instead, we want to be able to assert that these + requirements are truly required. + """ + + # set up the stack + requirements = list(requirements)[::-1] + # set of processed requirements + processed = set() + # key -> dist + best: dict[str, Distribution] = {} + to_activate: list[Distribution] = [] + + req_extras = _ReqExtras() + + # Mapping of requirement to set of distributions that required it; + # useful for reporting info about conflicts. + required_by = collections.defaultdict[Requirement, set[str]](set) + + while requirements: + # process dependencies breadth-first + req = requirements.pop(0) + if req in processed: + # Ignore cyclic or redundant dependencies + continue + + if not req_extras.markers_pass(req, extras): + continue + + dist = self._resolve_dist( + req, best, replace_conflicting, env, installer, required_by, to_activate + ) + + # push the new requirements onto the stack + new_requirements = dist.requires(req.extras)[::-1] + requirements.extend(new_requirements) + + # Register the new requirements needed by req + for new_requirement in new_requirements: + required_by[new_requirement].add(req.project_name) + req_extras[new_requirement] = req.extras + + processed.add(req) + + # return list of distros to activate + return to_activate + + def _resolve_dist( + self, req, best, replace_conflicting, env, installer, required_by, to_activate + ) -> Distribution: + dist = best.get(req.key) + if dist is None: + # Find the best distribution and add it to the map + dist = self.by_key.get(req.key) + if dist is None or (dist not in req and replace_conflicting): + ws = self + if env is None: + if dist is None: + env = Environment(self.entries) + else: + # Use an empty environment and workingset to avoid + # any further conflicts with the conflicting + # distribution + env = Environment([]) + ws = WorkingSet([]) + dist = best[req.key] = env.best_match( + req, ws, installer, replace_conflicting=replace_conflicting + ) + if dist is None: + requirers = required_by.get(req, None) + raise DistributionNotFound(req, requirers) + to_activate.append(dist) + if dist not in req: + # Oops, the "best" so far conflicts with a dependency + dependent_req = required_by[req] + raise VersionConflict(dist, req).with_context(dependent_req) + return dist + + @overload + def find_plugins( + self, + plugin_env: Environment, + full_env: Environment | None, + installer: _StrictInstallerType[_DistributionT], + fallback: bool = True, + ) -> tuple[list[_DistributionT], dict[Distribution, Exception]]: ... + @overload + def find_plugins( + self, + plugin_env: Environment, + full_env: Environment | None = None, + *, + installer: _StrictInstallerType[_DistributionT], + fallback: bool = True, + ) -> tuple[list[_DistributionT], dict[Distribution, Exception]]: ... + @overload + def find_plugins( + self, + plugin_env: Environment, + full_env: Environment | None = None, + installer: _InstallerType | None = None, + fallback: bool = True, + ) -> tuple[list[Distribution], dict[Distribution, Exception]]: ... + def find_plugins( + self, + plugin_env: Environment, + full_env: Environment | None = None, + installer: _InstallerType | None | _StrictInstallerType[_DistributionT] = None, + fallback: bool = True, + ) -> tuple[ + list[Distribution] | list[_DistributionT], + dict[Distribution, Exception], + ]: + """Find all activatable distributions in `plugin_env` + + Example usage:: + + distributions, errors = working_set.find_plugins( + Environment(plugin_dirlist) + ) + # add plugins+libs to sys.path + map(working_set.add, distributions) + # display errors + print('Could not load', errors) + + The `plugin_env` should be an ``Environment`` instance that contains + only distributions that are in the project's "plugin directory" or + directories. The `full_env`, if supplied, should be an ``Environment`` + contains all currently-available distributions. If `full_env` is not + supplied, one is created automatically from the ``WorkingSet`` this + method is called on, which will typically mean that every directory on + ``sys.path`` will be scanned for distributions. + + `installer` is a standard installer callback as used by the + ``resolve()`` method. The `fallback` flag indicates whether we should + attempt to resolve older versions of a plugin if the newest version + cannot be resolved. + + This method returns a 2-tuple: (`distributions`, `error_info`), where + `distributions` is a list of the distributions found in `plugin_env` + that were loadable, along with any other distributions that are needed + to resolve their dependencies. `error_info` is a dictionary mapping + unloadable plugin distributions to an exception instance describing the + error that occurred. Usually this will be a ``DistributionNotFound`` or + ``VersionConflict`` instance. + """ + + plugin_projects = list(plugin_env) + # scan project names in alphabetic order + plugin_projects.sort() + + error_info: dict[Distribution, Exception] = {} + distributions: dict[Distribution, Exception | None] = {} + + if full_env is None: + env = Environment(self.entries) + env += plugin_env + else: + env = full_env + plugin_env + + shadow_set = self.__class__([]) + # put all our entries in shadow_set + list(map(shadow_set.add, self)) + + for project_name in plugin_projects: + for dist in plugin_env[project_name]: + req = [dist.as_requirement()] + + try: + resolvees = shadow_set.resolve(req, env, installer) + + except ResolutionError as v: + # save error info + error_info[dist] = v + if fallback: + # try the next older version of project + continue + else: + # give up on this project, keep going + break + + else: + list(map(shadow_set.add, resolvees)) + distributions.update(dict.fromkeys(resolvees)) + + # success, no need to try any more versions of this project + break + + sorted_distributions = list(distributions) + sorted_distributions.sort() + + return sorted_distributions, error_info + + def require(self, *requirements: _NestedStr) -> list[Distribution]: + """Ensure that distributions matching `requirements` are activated + + `requirements` must be a string or a (possibly-nested) sequence + thereof, specifying the distributions and versions required. The + return value is a sequence of the distributions that needed to be + activated to fulfill the requirements; all relevant distributions are + included, even if they were already activated in this working set. + """ + needed = self.resolve(parse_requirements(requirements)) + + for dist in needed: + self.add(dist) + + return needed + + def subscribe( + self, callback: Callable[[Distribution], object], existing: bool = True + ) -> None: + """Invoke `callback` for all distributions + + If `existing=True` (default), + call on all existing ones, as well. + """ + if callback in self.callbacks: + return + self.callbacks.append(callback) + if not existing: + return + for dist in self: + callback(dist) + + def _added_new(self, dist) -> None: + for callback in self.callbacks: + callback(dist) + + def __getstate__( + self, + ) -> tuple[ + list[str], + dict[str | None, list[str]], + dict[str, Distribution], + dict[str, str], + list[Callable[[Distribution], object]], + ]: + return ( + self.entries[:], + self.entry_keys.copy(), + self.by_key.copy(), + self.normalized_to_canonical_keys.copy(), + self.callbacks[:], + ) + + def __setstate__(self, e_k_b_n_c) -> None: + entries, keys, by_key, normalized_to_canonical_keys, callbacks = e_k_b_n_c + self.entries = entries[:] + self.entry_keys = keys.copy() + self.by_key = by_key.copy() + self.normalized_to_canonical_keys = normalized_to_canonical_keys.copy() + self.callbacks = callbacks[:] + + +class _ReqExtras(dict["Requirement", tuple[str, ...]]): + """ + Map each requirement to the extras that demanded it. + """ + + def markers_pass(self, req: Requirement, extras: tuple[str, ...] | None = None): + """ + Evaluate markers for req against each extra that + demanded it. + + Return False if the req has a marker and fails + evaluation. Otherwise, return True. + """ + return not req.marker or any( + req.marker.evaluate({'extra': extra}) + for extra in self.get(req, ()) + (extras or ("",)) + ) + + +class Environment: + """Searchable snapshot of distributions on a search path""" + + def __init__( + self, + search_path: Iterable[str] | None = None, + platform: str | None = get_supported_platform(), + python: str | None = PY_MAJOR, + ) -> None: + """Snapshot distributions available on a search path + + Any distributions found on `search_path` are added to the environment. + `search_path` should be a sequence of ``sys.path`` items. If not + supplied, ``sys.path`` is used. + + `platform` is an optional string specifying the name of the platform + that platform-specific distributions must be compatible with. If + unspecified, it defaults to the current platform. `python` is an + optional string naming the desired version of Python (e.g. ``'3.6'``); + it defaults to the current version. + + You may explicitly set `platform` (and/or `python`) to ``None`` if you + wish to map *all* distributions, not just those compatible with the + running platform or Python version. + """ + self._distmap: dict[str, list[Distribution]] = {} + self.platform = platform + self.python = python + self.scan(search_path) + + def can_add(self, dist: Distribution) -> bool: + """Is distribution `dist` acceptable for this environment? + + The distribution must match the platform and python version + requirements specified when this environment was created, or False + is returned. + """ + py_compat = ( + self.python is None + or dist.py_version is None + or dist.py_version == self.python + ) + return py_compat and compatible_platforms(dist.platform, self.platform) + + def remove(self, dist: Distribution) -> None: + """Remove `dist` from the environment""" + self._distmap[dist.key].remove(dist) + + def scan(self, search_path: Iterable[str] | None = None) -> None: + """Scan `search_path` for distributions usable in this environment + + Any distributions found are added to the environment. + `search_path` should be a sequence of ``sys.path`` items. If not + supplied, ``sys.path`` is used. Only distributions conforming to + the platform/python version defined at initialization are added. + """ + if search_path is None: + search_path = sys.path + + for item in search_path: + for dist in find_distributions(item): + self.add(dist) + + def __getitem__(self, project_name: str) -> list[Distribution]: + """Return a newest-to-oldest list of distributions for `project_name` + + Uses case-insensitive `project_name` comparison, assuming all the + project's distributions use their project's name converted to all + lowercase as their key. + + """ + distribution_key = project_name.lower() + return self._distmap.get(distribution_key, []) + + def add(self, dist: Distribution) -> None: + """Add `dist` if we ``can_add()`` it and it has not already been added""" + if self.can_add(dist) and dist.has_version(): + dists = self._distmap.setdefault(dist.key, []) + if dist not in dists: + dists.append(dist) + dists.sort(key=operator.attrgetter('hashcmp'), reverse=True) + + @overload + def best_match( + self, + req: Requirement, + working_set: WorkingSet, + installer: _StrictInstallerType[_DistributionT], + replace_conflicting: bool = False, + ) -> _DistributionT: ... + @overload + def best_match( + self, + req: Requirement, + working_set: WorkingSet, + installer: _InstallerType | None = None, + replace_conflicting: bool = False, + ) -> Distribution | None: ... + def best_match( + self, + req: Requirement, + working_set: WorkingSet, + installer: _InstallerType | None | _StrictInstallerType[_DistributionT] = None, + replace_conflicting: bool = False, + ) -> Distribution | None: + """Find distribution best matching `req` and usable on `working_set` + + This calls the ``find(req)`` method of the `working_set` to see if a + suitable distribution is already active. (This may raise + ``VersionConflict`` if an unsuitable version of the project is already + active in the specified `working_set`.) If a suitable distribution + isn't active, this method returns the newest distribution in the + environment that meets the ``Requirement`` in `req`. If no suitable + distribution is found, and `installer` is supplied, then the result of + calling the environment's ``obtain(req, installer)`` method will be + returned. + """ + try: + dist = working_set.find(req) + except VersionConflict: + if not replace_conflicting: + raise + dist = None + if dist is not None: + return dist + for dist in self[req.key]: + if dist in req: + return dist + # try to download/install + return self.obtain(req, installer) + + @overload + def obtain( + self, + requirement: Requirement, + installer: _StrictInstallerType[_DistributionT], + ) -> _DistributionT: ... + @overload + def obtain( + self, + requirement: Requirement, + installer: Callable[[Requirement], None] | None = None, + ) -> None: ... + @overload + def obtain( + self, + requirement: Requirement, + installer: _InstallerType | None = None, + ) -> Distribution | None: ... + def obtain( + self, + requirement: Requirement, + installer: Callable[[Requirement], None] + | _InstallerType + | None + | _StrictInstallerType[_DistributionT] = None, + ) -> Distribution | None: + """Obtain a distribution matching `requirement` (e.g. via download) + + Obtain a distro that matches requirement (e.g. via download). In the + base ``Environment`` class, this routine just returns + ``installer(requirement)``, unless `installer` is None, in which case + None is returned instead. This method is a hook that allows subclasses + to attempt other ways of obtaining a distribution before falling back + to the `installer` argument.""" + return installer(requirement) if installer else None + + def __iter__(self) -> Iterator[str]: + """Yield the unique project names of the available distributions""" + for key in self._distmap.keys(): + if self[key]: + yield key + + def __iadd__(self, other: Distribution | Environment) -> Self: + """In-place addition of a distribution or environment""" + if isinstance(other, Distribution): + self.add(other) + elif isinstance(other, Environment): + for project in other: + for dist in other[project]: + self.add(dist) + else: + raise TypeError(f"Can't add {other!r} to environment") + return self + + def __add__(self, other: Distribution | Environment) -> Self: + """Add an environment or distribution to an environment""" + new = self.__class__([], platform=None, python=None) + for env in self, other: + new += env + return new + + +# XXX backward compatibility +AvailableDistributions = Environment + + +class ExtractionError(RuntimeError): + """An error occurred extracting a resource + + The following attributes are available from instances of this exception: + + manager + The resource manager that raised this exception + + cache_path + The base directory for resource extraction + + original_error + The exception instance that caused extraction to fail + """ + + manager: ResourceManager + cache_path: str + original_error: BaseException | None + + +class ResourceManager: + """Manage resource extraction and packages""" + + extraction_path: str | None = None + + def __init__(self) -> None: + # acts like a set + self.cached_files: dict[str, Literal[True]] = {} + + def resource_exists( + self, package_or_requirement: _PkgReqType, resource_name: str + ) -> bool: + """Does the named resource exist?""" + return get_provider(package_or_requirement).has_resource(resource_name) + + def resource_isdir( + self, package_or_requirement: _PkgReqType, resource_name: str + ) -> bool: + """Is the named resource an existing directory?""" + return get_provider(package_or_requirement).resource_isdir(resource_name) + + def resource_filename( + self, package_or_requirement: _PkgReqType, resource_name: str + ) -> str: + """Return a true filesystem path for specified resource""" + return get_provider(package_or_requirement).get_resource_filename( + self, resource_name + ) + + def resource_stream( + self, package_or_requirement: _PkgReqType, resource_name: str + ) -> _ResourceStream: + """Return a readable file-like object for specified resource""" + return get_provider(package_or_requirement).get_resource_stream( + self, resource_name + ) + + def resource_string( + self, package_or_requirement: _PkgReqType, resource_name: str + ) -> bytes: + """Return specified resource as :obj:`bytes`""" + return get_provider(package_or_requirement).get_resource_string( + self, resource_name + ) + + def resource_listdir( + self, package_or_requirement: _PkgReqType, resource_name: str + ) -> list[str]: + """List the contents of the named resource directory""" + return get_provider(package_or_requirement).resource_listdir(resource_name) + + def extraction_error(self) -> NoReturn: + """Give an error message for problems extracting file(s)""" + + old_exc = sys.exc_info()[1] + cache_path = self.extraction_path or get_default_cache() + + tmpl = textwrap.dedent( + """ + Can't extract file(s) to egg cache + + The following error occurred while trying to extract file(s) + to the Python egg cache: + + {old_exc} + + The Python egg cache directory is currently set to: + + {cache_path} + + Perhaps your account does not have write access to this directory? + You can change the cache directory by setting the PYTHON_EGG_CACHE + environment variable to point to an accessible directory. + """ + ).lstrip() + err = ExtractionError(tmpl.format(**locals())) + err.manager = self + err.cache_path = cache_path + err.original_error = old_exc + raise err + + def get_cache_path(self, archive_name: str, names: Iterable[StrPath] = ()) -> str: + """Return absolute location in cache for `archive_name` and `names` + + The parent directory of the resulting path will be created if it does + not already exist. `archive_name` should be the base filename of the + enclosing egg (which may not be the name of the enclosing zipfile!), + including its ".egg" extension. `names`, if provided, should be a + sequence of path name parts "under" the egg's extraction location. + + This method should only be called by resource providers that need to + obtain an extraction location, and only for names they intend to + extract, as it tracks the generated names for possible cleanup later. + """ + extract_path = self.extraction_path or get_default_cache() + target_path = os.path.join(extract_path, archive_name + '-tmp', *names) + try: + _bypass_ensure_directory(target_path) + except Exception: + self.extraction_error() + + self._warn_unsafe_extraction_path(extract_path) + + self.cached_files[target_path] = True + return target_path + + @staticmethod + def _warn_unsafe_extraction_path(path) -> None: + """ + If the default extraction path is overridden and set to an insecure + location, such as /tmp, it opens up an opportunity for an attacker to + replace an extracted file with an unauthorized payload. Warn the user + if a known insecure location is used. + + See Distribute #375 for more details. + """ + if os.name == 'nt' and not path.startswith(os.environ['windir']): + # On Windows, permissions are generally restrictive by default + # and temp directories are not writable by other users, so + # bypass the warning. + return + mode = os.stat(path).st_mode + if mode & stat.S_IWOTH or mode & stat.S_IWGRP: + msg = ( + "Extraction path is writable by group/others " + "and vulnerable to attack when " + "used with get_resource_filename ({path}). " + "Consider a more secure " + "location (set with .set_extraction_path or the " + "PYTHON_EGG_CACHE environment variable)." + ).format(**locals()) + warnings.warn(msg, UserWarning) + + def postprocess(self, tempname: StrOrBytesPath, filename: StrOrBytesPath) -> None: + """Perform any platform-specific postprocessing of `tempname` + + This is where Mac header rewrites should be done; other platforms don't + have anything special they should do. + + Resource providers should call this method ONLY after successfully + extracting a compressed resource. They must NOT call it on resources + that are already in the filesystem. + + `tempname` is the current (temporary) name of the file, and `filename` + is the name it will be renamed to by the caller after this routine + returns. + """ + + if os.name == 'posix': + # Make the resource executable + mode = ((os.stat(tempname).st_mode) | 0o555) & 0o7777 + os.chmod(tempname, mode) + + def set_extraction_path(self, path: str) -> None: + """Set the base path where resources will be extracted to, if needed. + + If you do not call this routine before any extractions take place, the + path defaults to the return value of ``get_default_cache()``. (Which + is based on the ``PYTHON_EGG_CACHE`` environment variable, with various + platform-specific fallbacks. See that routine's documentation for more + details.) + + Resources are extracted to subdirectories of this path based upon + information given by the ``IResourceProvider``. You may set this to a + temporary directory, but then you must call ``cleanup_resources()`` to + delete the extracted files when done. There is no guarantee that + ``cleanup_resources()`` will be able to remove all extracted files. + + (Note: you may not change the extraction path for a given resource + manager once resources have been extracted, unless you first call + ``cleanup_resources()``.) + """ + if self.cached_files: + raise ValueError("Can't change extraction path, files already extracted") + + self.extraction_path = path + + def cleanup_resources(self, force: bool = False) -> list[str]: + """ + Delete all extracted resource files and directories, returning a list + of the file and directory names that could not be successfully removed. + This function does not have any concurrency protection, so it should + generally only be called when the extraction path is a temporary + directory exclusive to a single process. This method is not + automatically called; you must call it explicitly or register it as an + ``atexit`` function if you wish to ensure cleanup of a temporary + directory used for extractions. + """ + # XXX + return [] + + +def get_default_cache() -> str: + """ + Return the ``PYTHON_EGG_CACHE`` environment variable + or a platform-relevant user cache dir for an app + named "Python-Eggs". + """ + return os.environ.get('PYTHON_EGG_CACHE') or _user_cache_dir(appname='Python-Eggs') + + +def safe_name(name: str) -> str: + """Convert an arbitrary string to a standard distribution name + + Any runs of non-alphanumeric/. characters are replaced with a single '-'. + """ + return re.sub('[^A-Za-z0-9.]+', '-', name) + + +def safe_version(version: str) -> str: + """ + Convert an arbitrary string to a standard version string + """ + try: + # normalize the version + return str(packaging.version.Version(version)) + except packaging.version.InvalidVersion: + version = version.replace(' ', '.') + return re.sub('[^A-Za-z0-9.]+', '-', version) + + +def _forgiving_version(version) -> str: + """Fallback when ``safe_version`` is not safe enough + >>> parse_version(_forgiving_version('0.23ubuntu1')) + + >>> parse_version(_forgiving_version('0.23-')) + + >>> parse_version(_forgiving_version('0.-_')) + + >>> parse_version(_forgiving_version('42.+?1')) + + >>> parse_version(_forgiving_version('hello world')) + + """ + version = version.replace(' ', '.') + match = _PEP440_FALLBACK.search(version) + if match: + safe = match["safe"] + rest = version[len(safe) :] + else: + safe = "0" + rest = version + local = f"sanitized.{_safe_segment(rest)}".strip(".") + return f"{safe}.dev0+{local}" + + +def _safe_segment(segment): + """Convert an arbitrary string into a safe segment""" + segment = re.sub('[^A-Za-z0-9.]+', '-', segment) + segment = re.sub('-[^A-Za-z0-9]+', '-', segment) + return re.sub(r'\.[^A-Za-z0-9]+', '.', segment).strip(".-") + + +def safe_extra(extra: str) -> str: + """Convert an arbitrary string to a standard 'extra' name + + Any runs of non-alphanumeric characters are replaced with a single '_', + and the result is always lowercased. + """ + return re.sub('[^A-Za-z0-9.-]+', '_', extra).lower() + + +def to_filename(name: str) -> str: + """Convert a project or version name to its filename-escaped form + + Any '-' characters are currently replaced with '_'. + """ + return name.replace('-', '_') + + +def invalid_marker(text: str) -> SyntaxError | Literal[False]: + """ + Validate text as a PEP 508 environment marker; return an exception + if invalid or False otherwise. + """ + try: + evaluate_marker(text) + except SyntaxError as e: + e.filename = None + e.lineno = None + return e + return False + + +def evaluate_marker(text: str, extra: str | None = None) -> bool: + """ + Evaluate a PEP 508 environment marker. + Return a boolean indicating the marker result in this environment. + Raise SyntaxError if marker is invalid. + + This implementation uses the 'pyparsing' module. + """ + try: + marker = packaging.markers.Marker(text) + return marker.evaluate() + except packaging.markers.InvalidMarker as e: + raise SyntaxError(e) from e + + +class NullProvider: + """Try to implement resources and metadata for arbitrary PEP 302 loaders""" + + egg_name: str | None = None + egg_info: str | None = None + loader: LoaderProtocol | None = None + + def __init__(self, module: _ModuleLike) -> None: + self.loader = getattr(module, '__loader__', None) + self.module_path = os.path.dirname(getattr(module, '__file__', '')) + + def get_resource_filename( + self, manager: ResourceManager, resource_name: str + ) -> str: + return self._fn(self.module_path, resource_name) + + def get_resource_stream( + self, manager: ResourceManager, resource_name: str + ) -> BinaryIO: + return io.BytesIO(self.get_resource_string(manager, resource_name)) + + def get_resource_string( + self, manager: ResourceManager, resource_name: str + ) -> bytes: + return self._get(self._fn(self.module_path, resource_name)) + + def has_resource(self, resource_name: str) -> bool: + return self._has(self._fn(self.module_path, resource_name)) + + def _get_metadata_path(self, name): + return self._fn(self.egg_info, name) + + def has_metadata(self, name: str) -> bool: + if not self.egg_info: + return False + + path = self._get_metadata_path(name) + return self._has(path) + + def get_metadata(self, name: str) -> str: + if not self.egg_info: + return "" + path = self._get_metadata_path(name) + value = self._get(path) + try: + return value.decode('utf-8') + except UnicodeDecodeError as exc: + # Include the path in the error message to simplify + # troubleshooting, and without changing the exception type. + exc.reason += f' in {name} file at path: {path}' + raise + + def get_metadata_lines(self, name: str) -> Iterator[str]: + return yield_lines(self.get_metadata(name)) + + def resource_isdir(self, resource_name: str) -> bool: + return self._isdir(self._fn(self.module_path, resource_name)) + + def metadata_isdir(self, name: str) -> bool: + return bool(self.egg_info and self._isdir(self._fn(self.egg_info, name))) + + def resource_listdir(self, resource_name: str) -> list[str]: + return self._listdir(self._fn(self.module_path, resource_name)) + + def metadata_listdir(self, name: str) -> list[str]: + if self.egg_info: + return self._listdir(self._fn(self.egg_info, name)) + return [] + + def run_script(self, script_name: str, namespace: dict[str, Any]) -> None: + script = 'scripts/' + script_name + if not self.has_metadata(script): + raise ResolutionError( + "Script {script!r} not found in metadata at {self.egg_info!r}".format( + **locals() + ), + ) + + script_text = self.get_metadata(script).replace('\r\n', '\n') + script_text = script_text.replace('\r', '\n') + script_filename = self._fn(self.egg_info, script) + namespace['__file__'] = script_filename + if os.path.exists(script_filename): + source = _read_utf8_with_fallback(script_filename) + code = compile(source, script_filename, 'exec') + exec(code, namespace, namespace) + else: + from linecache import cache + + cache[script_filename] = ( + len(script_text), + 0, + script_text.split('\n'), + script_filename, + ) + script_code = compile(script_text, script_filename, 'exec') + exec(script_code, namespace, namespace) + + def _has(self, path) -> bool: + raise NotImplementedError( + "Can't perform this operation for unregistered loader type" + ) + + def _isdir(self, path) -> bool: + raise NotImplementedError( + "Can't perform this operation for unregistered loader type" + ) + + def _listdir(self, path) -> list[str]: + raise NotImplementedError( + "Can't perform this operation for unregistered loader type" + ) + + def _fn(self, base: str | None, resource_name: str): + if base is None: + raise TypeError( + "`base` parameter in `_fn` is `None`. Either override this method or check the parameter first." + ) + self._validate_resource_path(resource_name) + if resource_name: + return os.path.join(base, *resource_name.split('/')) + return base + + @staticmethod + def _validate_resource_path(path) -> None: + """ + Validate the resource paths according to the docs. + https://setuptools.pypa.io/en/latest/pkg_resources.html#basic-resource-access + + >>> warned = getfixture('recwarn') + >>> warnings.simplefilter('always') + >>> vrp = NullProvider._validate_resource_path + >>> vrp('foo/bar.txt') + >>> bool(warned) + False + >>> vrp('../foo/bar.txt') + >>> bool(warned) + True + >>> warned.clear() + >>> vrp('/foo/bar.txt') + >>> bool(warned) + True + >>> vrp('foo/../../bar.txt') + >>> bool(warned) + True + >>> warned.clear() + >>> vrp('foo/f../bar.txt') + >>> bool(warned) + False + + Windows path separators are straight-up disallowed. + >>> vrp(r'\\foo/bar.txt') + Traceback (most recent call last): + ... + ValueError: Use of .. or absolute path in a resource path \ +is not allowed. + + >>> vrp(r'C:\\foo/bar.txt') + Traceback (most recent call last): + ... + ValueError: Use of .. or absolute path in a resource path \ +is not allowed. + + Blank values are allowed + + >>> vrp('') + >>> bool(warned) + False + + Non-string values are not. + + >>> vrp(None) + Traceback (most recent call last): + ... + AttributeError: ... + """ + invalid = ( + os.path.pardir in path.split(posixpath.sep) + or posixpath.isabs(path) + or ntpath.isabs(path) + or path.startswith("\\") + ) + if not invalid: + return + + msg = "Use of .. or absolute path in a resource path is not allowed." + + # Aggressively disallow Windows absolute paths + if (path.startswith("\\") or ntpath.isabs(path)) and not posixpath.isabs(path): + raise ValueError(msg) + + # for compatibility, warn; in future + # raise ValueError(msg) + issue_warning( + msg[:-1] + " and will raise exceptions in a future release.", + DeprecationWarning, + ) + + def _get(self, path) -> bytes: + if hasattr(self.loader, 'get_data') and self.loader: + # Already checked get_data exists + return self.loader.get_data(path) # type: ignore[attr-defined] + raise NotImplementedError( + "Can't perform this operation for loaders without 'get_data()'" + ) + + +register_loader_type(object, NullProvider) + + +def _parents(path): + """ + yield all parents of path including path + """ + last = None + while path != last: + yield path + last = path + path, _ = os.path.split(path) + + +class EggProvider(NullProvider): + """Provider based on a virtual filesystem""" + + def __init__(self, module: _ModuleLike) -> None: + super().__init__(module) + self._setup_prefix() + + def _setup_prefix(self): + # Assume that metadata may be nested inside a "basket" + # of multiple eggs and use module_path instead of .archive. + eggs = filter(_is_egg_path, _parents(self.module_path)) + egg = next(eggs, None) + egg and self._set_egg(egg) + + def _set_egg(self, path: str) -> None: + self.egg_name = os.path.basename(path) + self.egg_info = os.path.join(path, 'EGG-INFO') + self.egg_root = path + + +class DefaultProvider(EggProvider): + """Provides access to package resources in the filesystem""" + + def _has(self, path) -> bool: + return os.path.exists(path) + + def _isdir(self, path) -> bool: + return os.path.isdir(path) + + def _listdir(self, path): + return os.listdir(path) + + def get_resource_stream( + self, manager: object, resource_name: str + ) -> io.BufferedReader: + return open(self._fn(self.module_path, resource_name), 'rb') + + def _get(self, path) -> bytes: + with open(path, 'rb') as stream: + return stream.read() + + @classmethod + def _register(cls) -> None: + loader_names = ( + 'SourceFileLoader', + 'SourcelessFileLoader', + ) + for name in loader_names: + loader_cls = getattr(importlib.machinery, name, type(None)) + register_loader_type(loader_cls, cls) + + +DefaultProvider._register() + + +class EmptyProvider(NullProvider): + """Provider that returns nothing for all requests""" + + # A special case, we don't want all Providers inheriting from NullProvider to have a potentially None module_path + module_path: str | None = None # type: ignore[assignment] + + _isdir = _has = lambda self, path: False + + def _get(self, path) -> bytes: + return b'' + + def _listdir(self, path): + return [] + + def __init__(self) -> None: + pass + + +empty_provider = EmptyProvider() + + +class ZipManifests(dict[str, "MemoizedZipManifests.manifest_mod"]): + """ + zip manifest builder + """ + + # `path` could be `StrPath | IO[bytes]` but that violates the LSP for `MemoizedZipManifests.load` + @classmethod + def build(cls, path: str) -> dict[str, zipfile.ZipInfo]: + """ + Build a dictionary similar to the zipimport directory + caches, except instead of tuples, store ZipInfo objects. + + Use a platform-specific path separator (os.sep) for the path keys + for compatibility with pypy on Windows. + """ + with zipfile.ZipFile(path) as zfile: + items = ( + ( + name.replace('/', os.sep), + zfile.getinfo(name), + ) + for name in zfile.namelist() + ) + return dict(items) + + load = build + + +class MemoizedZipManifests(ZipManifests): + """ + Memoized zipfile manifests. + """ + + class manifest_mod(NamedTuple): + manifest: dict[str, zipfile.ZipInfo] + mtime: float + + def load(self, path: str) -> dict[str, zipfile.ZipInfo]: # type: ignore[override] # ZipManifests.load is a classmethod + """ + Load a manifest at path or return a suitable manifest already loaded. + """ + path = os.path.normpath(path) + mtime = os.stat(path).st_mtime + + if path not in self or self[path].mtime != mtime: + manifest = self.build(path) + self[path] = self.manifest_mod(manifest, mtime) + + return self[path].manifest + + +class ZipProvider(EggProvider): + """Resource support for zips and eggs""" + + eagers: list[str] | None = None + _zip_manifests = MemoizedZipManifests() + # ZipProvider's loader should always be a zipimporter or equivalent + loader: zipimport.zipimporter + + def __init__(self, module: _ZipLoaderModule) -> None: + super().__init__(module) + self.zip_pre = self.loader.archive + os.sep + + def _zipinfo_name(self, fspath): + # Convert a virtual filename (full path to file) into a zipfile subpath + # usable with the zipimport directory cache for our target archive + fspath = fspath.rstrip(os.sep) + if fspath == self.loader.archive: + return '' + if fspath.startswith(self.zip_pre): + return fspath[len(self.zip_pre) :] + raise AssertionError(f"{fspath} is not a subpath of {self.zip_pre}") + + def _parts(self, zip_path): + # Convert a zipfile subpath into an egg-relative path part list. + # pseudo-fs path + fspath = self.zip_pre + zip_path + if fspath.startswith(self.egg_root + os.sep): + return fspath[len(self.egg_root) + 1 :].split(os.sep) + raise AssertionError(f"{fspath} is not a subpath of {self.egg_root}") + + @property + def zipinfo(self): + return self._zip_manifests.load(self.loader.archive) + + def get_resource_filename( + self, manager: ResourceManager, resource_name: str + ) -> str: + if not self.egg_name: + raise NotImplementedError( + "resource_filename() only supported for .egg, not .zip" + ) + # no need to lock for extraction, since we use temp names + zip_path = self._resource_to_zip(resource_name) + eagers = self._get_eager_resources() + if '/'.join(self._parts(zip_path)) in eagers: + for name in eagers: + self._extract_resource(manager, self._eager_to_zip(name)) + return self._extract_resource(manager, zip_path) + + @staticmethod + def _get_date_and_size(zip_stat): + size = zip_stat.file_size + # ymdhms+wday, yday, dst + date_time = zip_stat.date_time + (0, 0, -1) + # 1980 offset already done + timestamp = time.mktime(date_time) + return timestamp, size + + # FIXME: 'ZipProvider._extract_resource' is too complex (12) + def _extract_resource(self, manager: ResourceManager, zip_path) -> str: # noqa: C901 + if zip_path in self._index(): + for name in self._index()[zip_path]: + last = self._extract_resource(manager, os.path.join(zip_path, name)) + # return the extracted directory name + return os.path.dirname(last) + + timestamp, _size = self._get_date_and_size(self.zipinfo[zip_path]) + + if not WRITE_SUPPORT: + raise OSError( + '"os.rename" and "os.unlink" are not supported on this platform' + ) + try: + if not self.egg_name: + raise OSError( + '"egg_name" is empty. This likely means no egg could be found from the "module_path".' + ) + real_path = manager.get_cache_path(self.egg_name, self._parts(zip_path)) + + if self._is_current(real_path, zip_path): + return real_path + + outf, tmpnam = _mkstemp( + ".$extract", + dir=os.path.dirname(real_path), + ) + os.write(outf, self.loader.get_data(zip_path)) + os.close(outf) + utime(tmpnam, (timestamp, timestamp)) + manager.postprocess(tmpnam, real_path) + + try: + rename(tmpnam, real_path) + + except OSError: + if os.path.isfile(real_path): + if self._is_current(real_path, zip_path): + # the file became current since it was checked above, + # so proceed. + return real_path + # Windows, del old file and retry + elif os.name == 'nt': + unlink(real_path) + rename(tmpnam, real_path) + return real_path + raise + + except OSError: + # report a user-friendly error + manager.extraction_error() + + return real_path + + def _is_current(self, file_path, zip_path): + """ + Return True if the file_path is current for this zip_path + """ + timestamp, size = self._get_date_and_size(self.zipinfo[zip_path]) + if not os.path.isfile(file_path): + return False + stat = os.stat(file_path) + if stat.st_size != size or stat.st_mtime != timestamp: + return False + # check that the contents match + zip_contents = self.loader.get_data(zip_path) + with open(file_path, 'rb') as f: + file_contents = f.read() + return zip_contents == file_contents + + def _get_eager_resources(self): + if self.eagers is None: + eagers = [] + for name in ('native_libs.txt', 'eager_resources.txt'): + if self.has_metadata(name): + eagers.extend(self.get_metadata_lines(name)) + self.eagers = eagers + return self.eagers + + def _index(self): + try: + return self._dirindex + except AttributeError: + ind = {} + for path in self.zipinfo: + parts = path.split(os.sep) + while parts: + parent = os.sep.join(parts[:-1]) + if parent in ind: + ind[parent].append(parts[-1]) + break + else: + ind[parent] = [parts.pop()] + self._dirindex = ind + return ind + + def _has(self, fspath) -> bool: + zip_path = self._zipinfo_name(fspath) + return zip_path in self.zipinfo or zip_path in self._index() + + def _isdir(self, fspath) -> bool: + return self._zipinfo_name(fspath) in self._index() + + def _listdir(self, fspath): + return list(self._index().get(self._zipinfo_name(fspath), ())) + + def _eager_to_zip(self, resource_name: str): + return self._zipinfo_name(self._fn(self.egg_root, resource_name)) + + def _resource_to_zip(self, resource_name: str): + return self._zipinfo_name(self._fn(self.module_path, resource_name)) + + +register_loader_type(zipimport.zipimporter, ZipProvider) + + +class FileMetadata(EmptyProvider): + """Metadata handler for standalone PKG-INFO files + + Usage:: + + metadata = FileMetadata("/path/to/PKG-INFO") + + This provider rejects all data and metadata requests except for PKG-INFO, + which is treated as existing, and will be the contents of the file at + the provided location. + """ + + def __init__(self, path: StrPath) -> None: + self.path = path + + def _get_metadata_path(self, name): + return self.path + + def has_metadata(self, name: str) -> bool: + return name == 'PKG-INFO' and os.path.isfile(self.path) + + def get_metadata(self, name: str) -> str: + if name != 'PKG-INFO': + raise KeyError("No metadata except PKG-INFO is available") + + with open(self.path, encoding='utf-8', errors="replace") as f: + metadata = f.read() + self._warn_on_replacement(metadata) + return metadata + + def _warn_on_replacement(self, metadata) -> None: + replacement_char = '�' + if replacement_char in metadata: + tmpl = "{self.path} could not be properly decoded in UTF-8" + msg = tmpl.format(**locals()) + warnings.warn(msg) + + def get_metadata_lines(self, name: str) -> Iterator[str]: + return yield_lines(self.get_metadata(name)) + + +class PathMetadata(DefaultProvider): + """Metadata provider for egg directories + + Usage:: + + # Development eggs: + + egg_info = "/path/to/PackageName.egg-info" + base_dir = os.path.dirname(egg_info) + metadata = PathMetadata(base_dir, egg_info) + dist_name = os.path.splitext(os.path.basename(egg_info))[0] + dist = Distribution(basedir, project_name=dist_name, metadata=metadata) + + # Unpacked egg directories: + + egg_path = "/path/to/PackageName-ver-pyver-etc.egg" + metadata = PathMetadata(egg_path, os.path.join(egg_path,'EGG-INFO')) + dist = Distribution.from_filename(egg_path, metadata=metadata) + """ + + def __init__(self, path: str, egg_info: str) -> None: + self.module_path = path + self.egg_info = egg_info + + +class EggMetadata(ZipProvider): + """Metadata provider for .egg files""" + + def __init__(self, importer: zipimport.zipimporter) -> None: + """Create a metadata provider from a zipimporter""" + + self.zip_pre = importer.archive + os.sep + self.loader = importer + if importer.prefix: + self.module_path = os.path.join(importer.archive, importer.prefix) + else: + self.module_path = importer.archive + self._setup_prefix() + + +_distribution_finders: dict[type, _DistFinderType[Any]] = _declare_state( + 'dict', '_distribution_finders', {} +) + + +def register_finder( + importer_type: type[_T], distribution_finder: _DistFinderType[_T] +) -> None: + """Register `distribution_finder` to find distributions in sys.path items + + `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item + handler), and `distribution_finder` is a callable that, passed a path + item and the importer instance, yields ``Distribution`` instances found on + that path item. See ``pkg_resources.find_on_path`` for an example.""" + _distribution_finders[importer_type] = distribution_finder + + +def find_distributions(path_item: str, only: bool = False) -> Iterable[Distribution]: + """Yield distributions accessible via `path_item`""" + importer = get_importer(path_item) + finder = _find_adapter(_distribution_finders, importer) + return finder(importer, path_item, only) + + +def find_eggs_in_zip( + importer: zipimport.zipimporter, path_item: str, only: bool = False +) -> Iterator[Distribution]: + """ + Find eggs in zip files; possibly multiple nested eggs. + """ + if importer.archive.endswith('.whl'): + # wheels are not supported with this finder + # they don't have PKG-INFO metadata, and won't ever contain eggs + return + metadata = EggMetadata(importer) + if metadata.has_metadata('PKG-INFO'): + yield Distribution.from_filename(path_item, metadata=metadata) + if only: + # don't yield nested distros + return + for subitem in metadata.resource_listdir(''): + if _is_egg_path(subitem): + subpath = os.path.join(path_item, subitem) + dists = find_eggs_in_zip(zipimport.zipimporter(subpath), subpath) + yield from dists + elif subitem.lower().endswith(('.dist-info', '.egg-info')): + subpath = os.path.join(path_item, subitem) + submeta = EggMetadata(zipimport.zipimporter(subpath)) + submeta.egg_info = subpath + yield Distribution.from_location(path_item, subitem, submeta) + + +register_finder(zipimport.zipimporter, find_eggs_in_zip) + + +def find_nothing( + importer: object | None, path_item: str | None, only: bool | None = False +): + return () + + +register_finder(object, find_nothing) + + +def find_on_path(importer: object | None, path_item, only=False): + """Yield distributions accessible on a sys.path directory""" + path_item = _normalize_cached(path_item) + + if _is_unpacked_egg(path_item): + yield Distribution.from_filename( + path_item, + metadata=PathMetadata(path_item, os.path.join(path_item, 'EGG-INFO')), + ) + return + + entries = (os.path.join(path_item, child) for child in safe_listdir(path_item)) + + # scan for .egg and .egg-info in directory + for entry in sorted(entries): + fullpath = os.path.join(path_item, entry) + factory = dist_factory(path_item, entry, only) + yield from factory(fullpath) + + +def dist_factory(path_item, entry, only): + """Return a dist_factory for the given entry.""" + lower = entry.lower() + is_egg_info = lower.endswith('.egg-info') + is_dist_info = lower.endswith('.dist-info') and os.path.isdir( + os.path.join(path_item, entry) + ) + is_meta = is_egg_info or is_dist_info + return ( + distributions_from_metadata + if is_meta + else find_distributions + if not only and _is_egg_path(entry) + else resolve_egg_link + if not only and lower.endswith('.egg-link') + else NoDists() + ) + + +class NoDists: + """ + >>> bool(NoDists()) + False + + >>> list(NoDists()('anything')) + [] + """ + + def __bool__(self) -> Literal[False]: + return False + + def __call__(self, fullpath: object): + return iter(()) + + +def safe_listdir(path: StrOrBytesPath): + """ + Attempt to list contents of path, but suppress some exceptions. + """ + try: + return os.listdir(path) + except (PermissionError, NotADirectoryError): + pass + except OSError as e: + # Ignore the directory if does not exist, not a directory or + # permission denied + if e.errno not in (errno.ENOTDIR, errno.EACCES, errno.ENOENT): + raise + return () + + +def distributions_from_metadata(path: str): + root = os.path.dirname(path) + if os.path.isdir(path): + if len(os.listdir(path)) == 0: + # empty metadata dir; skip + return + metadata: _MetadataType = PathMetadata(root, path) + else: + metadata = FileMetadata(path) + entry = os.path.basename(path) + yield Distribution.from_location( + root, + entry, + metadata, + precedence=DEVELOP_DIST, + ) + + +def non_empty_lines(path): + """ + Yield non-empty lines from file at path + """ + for line in _read_utf8_with_fallback(path).splitlines(): + line = line.strip() + if line: + yield line + + +def resolve_egg_link(path): + """ + Given a path to an .egg-link, resolve distributions + present in the referenced path. + """ + referenced_paths = non_empty_lines(path) + resolved_paths = ( + os.path.join(os.path.dirname(path), ref) for ref in referenced_paths + ) + dist_groups = map(find_distributions, resolved_paths) + return next(dist_groups, ()) + + +if hasattr(pkgutil, 'ImpImporter'): + register_finder(pkgutil.ImpImporter, find_on_path) + +register_finder(importlib.machinery.FileFinder, find_on_path) + +_namespace_handlers: dict[type, _NSHandlerType[Any]] = _declare_state( + 'dict', '_namespace_handlers', {} +) +_namespace_packages: dict[str | None, list[str]] = _declare_state( + 'dict', '_namespace_packages', {} +) + + +def register_namespace_handler( + importer_type: type[_T], namespace_handler: _NSHandlerType[_T] +) -> None: + """Register `namespace_handler` to declare namespace packages + + `importer_type` is the type or class of a PEP 302 "Importer" (sys.path item + handler), and `namespace_handler` is a callable like this:: + + def namespace_handler(importer, path_entry, moduleName, module): + # return a path_entry to use for child packages + + Namespace handlers are only called if the importer object has already + agreed that it can handle the relevant path item, and they should only + return a subpath if the module __path__ does not already contain an + equivalent subpath. For an example namespace handler, see + ``pkg_resources.file_ns_handler``. + """ + _namespace_handlers[importer_type] = namespace_handler + + +def _handle_ns(packageName, path_item): + """Ensure that named package includes a subpath of path_item (if needed)""" + + importer = get_importer(path_item) + if importer is None: + return None + + # use find_spec (PEP 451) and fall-back to find_module (PEP 302) + try: + spec = importer.find_spec(packageName) + except AttributeError: + # capture warnings due to #1111 + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + loader = importer.find_module(packageName) + else: + loader = spec.loader if spec else None + + if loader is None: + return None + module = sys.modules.get(packageName) + if module is None: + module = sys.modules[packageName] = types.ModuleType(packageName) + module.__path__ = [] + _set_parent_ns(packageName) + elif not hasattr(module, '__path__'): + raise TypeError("Not a package:", packageName) + handler = _find_adapter(_namespace_handlers, importer) + subpath = handler(importer, path_item, packageName, module) + if subpath is not None: + path = module.__path__ + path.append(subpath) + importlib.import_module(packageName) + _rebuild_mod_path(path, packageName, module) + return subpath + + +def _rebuild_mod_path(orig_path, package_name, module: types.ModuleType) -> None: + """ + Rebuild module.__path__ ensuring that all entries are ordered + corresponding to their sys.path order + """ + sys_path = [_normalize_cached(p) for p in sys.path] + + def safe_sys_path_index(entry): + """ + Workaround for #520 and #513. + """ + try: + return sys_path.index(entry) + except ValueError: + return float('inf') + + def position_in_sys_path(path): + """ + Return the ordinal of the path based on its position in sys.path + """ + path_parts = path.split(os.sep) + module_parts = package_name.count('.') + 1 + parts = path_parts[:-module_parts] + return safe_sys_path_index(_normalize_cached(os.sep.join(parts))) + + new_path = sorted(orig_path, key=position_in_sys_path) + new_path = [_normalize_cached(p) for p in new_path] + + if isinstance(module.__path__, list): + module.__path__[:] = new_path + else: + module.__path__ = new_path + + +def declare_namespace(packageName: str) -> None: + """Declare that package 'packageName' is a namespace package""" + + msg = ( + f"Deprecated call to `pkg_resources.declare_namespace({packageName!r})`.\n" + "Implementing implicit namespace packages (as specified in PEP 420) " + "is preferred to `pkg_resources.declare_namespace`. " + "See https://setuptools.pypa.io/en/latest/references/" + "keywords.html#keyword-namespace-packages" + ) + warnings.warn(msg, DeprecationWarning, stacklevel=2) + + _imp.acquire_lock() + try: + if packageName in _namespace_packages: + return + + path: MutableSequence[str] = sys.path + parent, _, _ = packageName.rpartition('.') + + if parent: + declare_namespace(parent) + if parent not in _namespace_packages: + __import__(parent) + try: + path = sys.modules[parent].__path__ + except AttributeError as e: + raise TypeError("Not a package:", parent) from e + + # Track what packages are namespaces, so when new path items are added, + # they can be updated + _namespace_packages.setdefault(parent or None, []).append(packageName) + _namespace_packages.setdefault(packageName, []) + + for path_item in path: + # Ensure all the parent's path items are reflected in the child, + # if they apply + _handle_ns(packageName, path_item) + + finally: + _imp.release_lock() + + +def fixup_namespace_packages(path_item: str, parent: str | None = None) -> None: + """Ensure that previously-declared namespace packages include path_item""" + _imp.acquire_lock() + try: + for package in _namespace_packages.get(parent, ()): + subpath = _handle_ns(package, path_item) + if subpath: + fixup_namespace_packages(subpath, package) + finally: + _imp.release_lock() + + +def file_ns_handler( + importer: object, + path_item: StrPath, + packageName: str, + module: types.ModuleType, +): + """Compute an ns-package subpath for a filesystem or zipfile importer""" + + subpath = os.path.join(path_item, packageName.split('.')[-1]) + normalized = _normalize_cached(subpath) + for item in module.__path__: + if _normalize_cached(item) == normalized: + break + else: + # Only return the path if it's not already there + return subpath + + +if hasattr(pkgutil, 'ImpImporter'): + register_namespace_handler(pkgutil.ImpImporter, file_ns_handler) + +register_namespace_handler(zipimport.zipimporter, file_ns_handler) +register_namespace_handler(importlib.machinery.FileFinder, file_ns_handler) + + +def null_ns_handler( + importer: object, + path_item: str | None, + packageName: str | None, + module: _ModuleLike | None, +) -> None: + return None + + +register_namespace_handler(object, null_ns_handler) + + +@overload +def normalize_path(filename: StrPath) -> str: ... +@overload +def normalize_path(filename: BytesPath) -> bytes: ... +def normalize_path(filename: StrOrBytesPath) -> str | bytes: + """Normalize a file/dir name for comparison purposes""" + return os.path.normcase(os.path.realpath(os.path.normpath(_cygwin_patch(filename)))) + + +def _cygwin_patch(filename: StrOrBytesPath): # pragma: nocover + """ + Contrary to POSIX 2008, on Cygwin, getcwd (3) contains + symlink components. Using + os.path.abspath() works around this limitation. A fix in os.getcwd() + would probably better, in Cygwin even more so, except + that this seems to be by design... + """ + return os.path.abspath(filename) if sys.platform == 'cygwin' else filename + + +if TYPE_CHECKING: + # https://github.com/python/mypy/issues/16261 + # https://github.com/python/typeshed/issues/6347 + @overload + def _normalize_cached(filename: StrPath) -> str: ... + @overload + def _normalize_cached(filename: BytesPath) -> bytes: ... + def _normalize_cached(filename: StrOrBytesPath) -> str | bytes: ... + +else: + + @functools.cache + def _normalize_cached(filename): + return normalize_path(filename) + + +def _is_egg_path(path): + """ + Determine if given path appears to be an egg. + """ + return _is_zip_egg(path) or _is_unpacked_egg(path) + + +def _is_zip_egg(path): + return ( + path.lower().endswith('.egg') + and os.path.isfile(path) + and zipfile.is_zipfile(path) + ) + + +def _is_unpacked_egg(path): + """ + Determine if given path appears to be an unpacked egg. + """ + return path.lower().endswith('.egg') and os.path.isfile( + os.path.join(path, 'EGG-INFO', 'PKG-INFO') + ) + + +def _set_parent_ns(packageName) -> None: + parts = packageName.split('.') + name = parts.pop() + if parts: + parent = '.'.join(parts) + setattr(sys.modules[parent], name, sys.modules[packageName]) + + +MODULE = re.compile(r"\w+(\.\w+)*$").match +EGG_NAME = re.compile( + r""" + (?P[^-]+) ( + -(?P[^-]+) ( + -py(?P[^-]+) ( + -(?P.+) + )? + )? + )? + """, + re.VERBOSE | re.IGNORECASE, +).match + + +class EntryPoint: + """Object representing an advertised importable object""" + + def __init__( + self, + name: str, + module_name: str, + attrs: Iterable[str] = (), + extras: Iterable[str] = (), + dist: Distribution | None = None, + ) -> None: + if not MODULE(module_name): + raise ValueError("Invalid module name", module_name) + self.name = name + self.module_name = module_name + self.attrs = tuple(attrs) + self.extras = tuple(extras) + self.dist = dist + + def __str__(self) -> str: + s = f"{self.name} = {self.module_name}" + if self.attrs: + s += ':' + '.'.join(self.attrs) + if self.extras: + extras = ','.join(self.extras) + s += f' [{extras}]' + return s + + def __repr__(self) -> str: + return f"EntryPoint.parse({str(self)!r})" + + @overload + def load( + self, + require: Literal[True] = True, + env: Environment | None = None, + installer: _InstallerType | None = None, + ) -> _ResolvedEntryPoint: ... + @overload + def load( + self, + require: Literal[False], + *args: Any, + **kwargs: Any, + ) -> _ResolvedEntryPoint: ... + def load( + self, + require: bool = True, + *args: Environment | _InstallerType | None, + **kwargs: Environment | _InstallerType | None, + ) -> _ResolvedEntryPoint: + """ + Require packages for this EntryPoint, then resolve it. + """ + if not require or args or kwargs: + warnings.warn( + "Parameters to load are deprecated. Call .resolve and " + ".require separately.", + PkgResourcesDeprecationWarning, + stacklevel=2, + ) + if require: + # We could pass `env` and `installer` directly, + # but keeping `*args` and `**kwargs` for backwards compatibility + self.require(*args, **kwargs) # type: ignore[arg-type] + return self.resolve() + + def resolve(self) -> _ResolvedEntryPoint: + """ + Resolve the entry point from its module and attrs. + """ + module = __import__(self.module_name, fromlist=['__name__'], level=0) + try: + return functools.reduce(getattr, self.attrs, module) + except AttributeError as exc: + raise ImportError(str(exc)) from exc + + def require( + self, + env: Environment | None = None, + installer: _InstallerType | None = None, + ) -> None: + if not self.dist: + error_cls = UnknownExtra if self.extras else AttributeError + raise error_cls("Can't require() without a distribution", self) + + # Get the requirements for this entry point with all its extras and + # then resolve them. We have to pass `extras` along when resolving so + # that the working set knows what extras we want. Otherwise, for + # dist-info distributions, the working set will assume that the + # requirements for that extra are purely optional and skip over them. + reqs = self.dist.requires(self.extras) + items = working_set.resolve(reqs, env, installer, extras=self.extras) + list(map(working_set.add, items)) + + pattern = re.compile( + r'\s*' + r'(?P.+?)\s*' + r'=\s*' + r'(?P[\w.]+)\s*' + r'(:\s*(?P[\w.]+))?\s*' + r'(?P\[.*\])?\s*$' + ) + + @classmethod + def parse(cls, src: str, dist: Distribution | None = None) -> Self: + """Parse a single entry point from string `src` + + Entry point syntax follows the form:: + + name = some.module:some.attr [extra1, extra2] + + The entry name and module name are required, but the ``:attrs`` and + ``[extras]`` parts are optional + """ + m = cls.pattern.match(src) + if not m: + msg = "EntryPoint must be in 'name=module:attrs [extras]' format" + raise ValueError(msg, src) + res = m.groupdict() + extras = cls._parse_extras(res['extras']) + attrs = res['attr'].split('.') if res['attr'] else () + return cls(res['name'], res['module'], attrs, extras, dist) + + @classmethod + def _parse_extras(cls, extras_spec): + if not extras_spec: + return () + req = Requirement.parse('x' + extras_spec) + if req.specs: + raise ValueError + return req.extras + + @classmethod + def parse_group( + cls, + group: str, + lines: _NestedStr, + dist: Distribution | None = None, + ) -> dict[str, Self]: + """Parse an entry point group""" + if not MODULE(group): + raise ValueError("Invalid group name", group) + this: dict[str, Self] = {} + for line in yield_lines(lines): + ep = cls.parse(line, dist) + if ep.name in this: + raise ValueError("Duplicate entry point", group, ep.name) + this[ep.name] = ep + return this + + @classmethod + def parse_map( + cls, + data: str | Iterable[str] | dict[str, str | Iterable[str]], + dist: Distribution | None = None, + ) -> dict[str, dict[str, Self]]: + """Parse a map of entry point groups""" + _data: Iterable[tuple[str | None, str | Iterable[str]]] + if isinstance(data, dict): + _data = data.items() + else: + _data = split_sections(data) + maps: dict[str, dict[str, Self]] = {} + for group, lines in _data: + if group is None: + if not lines: + continue + raise ValueError("Entry points must be listed in groups") + group = group.strip() + if group in maps: + raise ValueError("Duplicate group name", group) + maps[group] = cls.parse_group(group, lines, dist) + return maps + + +def _version_from_file(lines): + """ + Given an iterable of lines from a Metadata file, return + the value of the Version field, if present, or None otherwise. + """ + + def is_version_line(line): + return line.lower().startswith('version:') + + version_lines = filter(is_version_line, lines) + line = next(iter(version_lines), '') + _, _, value = line.partition(':') + return safe_version(value.strip()) or None + + +class Distribution: + """Wrap an actual or potential sys.path entry w/metadata""" + + PKG_INFO = 'PKG-INFO' + + def __init__( + self, + location: str | None = None, + metadata: _MetadataType = None, + project_name: str | None = None, + version: str | None = None, + py_version: str | None = PY_MAJOR, + platform: str | None = None, + precedence: int = EGG_DIST, + ) -> None: + self.project_name = safe_name(project_name or 'Unknown') + if version is not None: + self._version = safe_version(version) + self.py_version = py_version + self.platform = platform + self.location = location + self.precedence = precedence + self._provider = metadata or empty_provider + + @classmethod + def from_location( + cls, + location: str, + basename: StrPath, + metadata: _MetadataType = None, + **kw: int, # We could set `precedence` explicitly, but keeping this as `**kw` for full backwards and subclassing compatibility + ) -> Distribution: + project_name, version, py_version, platform = [None] * 4 + basename, ext = os.path.splitext(basename) + if ext.lower() in _distributionImpl: + cls = _distributionImpl[ext.lower()] + + match = EGG_NAME(basename) + if match: + project_name, version, py_version, platform = match.group( + 'name', 'ver', 'pyver', 'plat' + ) + return cls( + location, + metadata, + project_name=project_name, + version=version, + py_version=py_version, + platform=platform, + **kw, + )._reload_version() + + def _reload_version(self): + return self + + @property + def hashcmp(self): + return ( + self._forgiving_parsed_version, + self.precedence, + self.key, + self.location, + self.py_version or '', + self.platform or '', + ) + + def __hash__(self) -> int: + return hash(self.hashcmp) + + def __lt__(self, other: Distribution) -> bool: + return self.hashcmp < other.hashcmp + + def __le__(self, other: Distribution) -> bool: + return self.hashcmp <= other.hashcmp + + def __gt__(self, other: Distribution) -> bool: + return self.hashcmp > other.hashcmp + + def __ge__(self, other: Distribution) -> bool: + return self.hashcmp >= other.hashcmp + + def __eq__(self, other: object) -> bool: + if not isinstance(other, self.__class__): + # It's not a Distribution, so they are not equal + return False + return self.hashcmp == other.hashcmp + + def __ne__(self, other: object) -> bool: + return not self == other + + # These properties have to be lazy so that we don't have to load any + # metadata until/unless it's actually needed. (i.e., some distributions + # may not know their name or version without loading PKG-INFO) + + @property + def key(self): + try: + return self._key + except AttributeError: + self._key = key = self.project_name.lower() + return key + + @property + def parsed_version(self): + if not hasattr(self, "_parsed_version"): + try: + self._parsed_version = parse_version(self.version) + except packaging.version.InvalidVersion as ex: + info = f"(package: {self.project_name})" + if hasattr(ex, "add_note"): + ex.add_note(info) # PEP 678 + raise + raise packaging.version.InvalidVersion(f"{str(ex)} {info}") from None + + return self._parsed_version + + @property + def _forgiving_parsed_version(self): + try: + return self.parsed_version + except packaging.version.InvalidVersion as ex: + self._parsed_version = parse_version(_forgiving_version(self.version)) + + notes = "\n".join(getattr(ex, "__notes__", [])) # PEP 678 + msg = f"""!!\n\n + ************************************************************************* + {str(ex)}\n{notes} + + This is a long overdue deprecation. + For the time being, `pkg_resources` will use `{self._parsed_version}` + as a replacement to avoid breaking existing environments, + but no future compatibility is guaranteed. + + If you maintain package {self.project_name} you should implement + the relevant changes to adequate the project to PEP 440 immediately. + ************************************************************************* + \n\n!! + """ + warnings.warn(msg, DeprecationWarning) + + return self._parsed_version + + @property + def version(self): + try: + return self._version + except AttributeError as e: + version = self._get_version() + if version is None: + path = self._get_metadata_path_for_display(self.PKG_INFO) + msg = f"Missing 'Version:' header and/or {self.PKG_INFO} file at path: {path}" + raise ValueError(msg, self) from e + + return version + + @property + def _dep_map(self): + """ + A map of extra to its list of (direct) requirements + for this distribution, including the null extra. + """ + try: + return self.__dep_map + except AttributeError: + self.__dep_map = self._filter_extras(self._build_dep_map()) + return self.__dep_map + + @staticmethod + def _filter_extras( + dm: dict[str | None, list[Requirement]], + ) -> dict[str | None, list[Requirement]]: + """ + Given a mapping of extras to dependencies, strip off + environment markers and filter out any dependencies + not matching the markers. + """ + for extra in list(filter(None, dm)): + new_extra: str | None = extra + reqs = dm.pop(extra) + new_extra, _, marker = extra.partition(':') + fails_marker = marker and ( + invalid_marker(marker) or not evaluate_marker(marker) + ) + if fails_marker: + reqs = [] + new_extra = safe_extra(new_extra) or None + + dm.setdefault(new_extra, []).extend(reqs) + return dm + + def _build_dep_map(self): + dm = {} + for name in 'requires.txt', 'depends.txt': + for extra, reqs in split_sections(self._get_metadata(name)): + dm.setdefault(extra, []).extend(parse_requirements(reqs)) + return dm + + def requires(self, extras: Iterable[str] = ()) -> list[Requirement]: + """List of Requirements needed for this distro if `extras` are used""" + dm = self._dep_map + deps: list[Requirement] = [] + deps.extend(dm.get(None, ())) + for ext in extras: + try: + deps.extend(dm[safe_extra(ext)]) + except KeyError as e: + raise UnknownExtra(f"{self} has no such extra feature {ext!r}") from e + return deps + + def _get_metadata_path_for_display(self, name): + """ + Return the path to the given metadata file, if available. + """ + try: + # We need to access _get_metadata_path() on the provider object + # directly rather than through this class's __getattr__() + # since _get_metadata_path() is marked private. + path = self._provider._get_metadata_path(name) + + # Handle exceptions e.g. in case the distribution's metadata + # provider doesn't support _get_metadata_path(). + except Exception: + return '[could not detect]' + + return path + + def _get_metadata(self, name): + if self.has_metadata(name): + yield from self.get_metadata_lines(name) + + def _get_version(self): + lines = self._get_metadata(self.PKG_INFO) + return _version_from_file(lines) + + def activate(self, path: list[str] | None = None, replace: bool = False) -> None: + """Ensure distribution is importable on `path` (default=sys.path)""" + if path is None: + path = sys.path + self.insert_on(path, replace=replace) + if path is sys.path and self.location is not None: + fixup_namespace_packages(self.location) + for pkg in self._get_metadata('namespace_packages.txt'): + if pkg in sys.modules: + declare_namespace(pkg) + + def egg_name(self): + """Return what this distribution's standard .egg filename should be""" + filename = f"{to_filename(self.project_name)}-{to_filename(self.version)}-py{self.py_version or PY_MAJOR}" + + if self.platform: + filename += '-' + self.platform + return filename + + def __repr__(self) -> str: + if self.location: + return f"{self} ({self.location})" + else: + return str(self) + + def __str__(self) -> str: + try: + version = getattr(self, 'version', None) + except ValueError: + version = None + version = version or "[unknown version]" + return f"{self.project_name} {version}" + + def __getattr__(self, attr: str): + """Delegate all unrecognized public attributes to .metadata provider""" + if attr.startswith('_'): + raise AttributeError(attr) + return getattr(self._provider, attr) + + def __dir__(self): + return list( + set(super().__dir__()) + | set(attr for attr in self._provider.__dir__() if not attr.startswith('_')) + ) + + @classmethod + def from_filename( + cls, + filename: StrPath, + metadata: _MetadataType = None, + **kw: int, # We could set `precedence` explicitly, but keeping this as `**kw` for full backwards and subclassing compatibility + ) -> Distribution: + return cls.from_location( + _normalize_cached(filename), os.path.basename(filename), metadata, **kw + ) + + def as_requirement(self): + """Return a ``Requirement`` that matches this distribution exactly""" + if isinstance(self.parsed_version, packaging.version.Version): + spec = f"{self.project_name}=={self.parsed_version}" + else: + spec = f"{self.project_name}==={self.parsed_version}" + + return Requirement.parse(spec) + + def load_entry_point(self, group: str, name: str) -> _ResolvedEntryPoint: + """Return the `name` entry point of `group` or raise ImportError""" + ep = self.get_entry_info(group, name) + if ep is None: + raise ImportError(f"Entry point {(group, name)!r} not found") + return ep.load() + + @overload + def get_entry_map(self, group: None = None) -> dict[str, dict[str, EntryPoint]]: ... + @overload + def get_entry_map(self, group: str) -> dict[str, EntryPoint]: ... + def get_entry_map(self, group: str | None = None): + """Return the entry point map for `group`, or the full entry map""" + if not hasattr(self, "_ep_map"): + self._ep_map = EntryPoint.parse_map( + self._get_metadata('entry_points.txt'), self + ) + if group is not None: + return self._ep_map.get(group, {}) + return self._ep_map + + def get_entry_info(self, group: str, name: str) -> EntryPoint | None: + """Return the EntryPoint object for `group`+`name`, or ``None``""" + return self.get_entry_map(group).get(name) + + # FIXME: 'Distribution.insert_on' is too complex (13) + def insert_on( # noqa: C901 + self, + path: list[str], + loc=None, + replace: bool = False, + ) -> None: + """Ensure self.location is on path + + If replace=False (default): + - If location is already in path anywhere, do nothing. + - Else: + - If it's an egg and its parent directory is on path, + insert just ahead of the parent. + - Else: add to the end of path. + If replace=True: + - If location is already on path anywhere (not eggs) + or higher priority than its parent (eggs) + do nothing. + - Else: + - If it's an egg and its parent directory is on path, + insert just ahead of the parent, + removing any lower-priority entries. + - Else: add it to the front of path. + """ + + loc = loc or self.location + if not loc: + return + + nloc = _normalize_cached(loc) + bdir = os.path.dirname(nloc) + npath = [(p and _normalize_cached(p) or p) for p in path] + + for p, item in enumerate(npath): + if item == nloc: + if replace: + break + else: + # don't modify path (even removing duplicates) if + # found and not replace + return + elif item == bdir and self.precedence == EGG_DIST: + # if it's an .egg, give it precedence over its directory + # UNLESS it's already been added to sys.path and replace=False + if (not replace) and nloc in npath[p:]: + return + if path is sys.path: + self.check_version_conflict() + path.insert(p, loc) + npath.insert(p, nloc) + break + else: + if path is sys.path: + self.check_version_conflict() + if replace: + path.insert(0, loc) + else: + path.append(loc) + return + + # p is the spot where we found or inserted loc; now remove duplicates + while True: + try: + np = npath.index(nloc, p + 1) + except ValueError: + break + else: + del npath[np], path[np] + # ha! + p = np + + return + + def check_version_conflict(self): + if self.key == 'setuptools': + # ignore the inevitable setuptools self-conflicts :( + return + + nsp = dict.fromkeys(self._get_metadata('namespace_packages.txt')) + loc = normalize_path(self.location) + for modname in self._get_metadata('top_level.txt'): + if ( + modname not in sys.modules + or modname in nsp + or modname in _namespace_packages + ): + continue + if modname in ('pkg_resources', 'setuptools', 'site'): + continue + fn = getattr(sys.modules[modname], '__file__', None) + if fn and ( + normalize_path(fn).startswith(loc) or fn.startswith(self.location) + ): + continue + issue_warning( + f"Module {modname} was already imported from {fn}, " + f"but {self.location} is being added to sys.path", + ) + + def has_version(self) -> bool: + try: + self.version + except ValueError: + issue_warning("Unbuilt egg for " + repr(self)) + return False + except SystemError: + # TODO: remove this except clause when python/cpython#103632 is fixed. + return False + return True + + def clone(self, **kw: str | int | IResourceProvider | None) -> Self: + """Copy this distribution, substituting in any changed keyword args""" + names = 'project_name version py_version platform location precedence' + for attr in names.split(): + kw.setdefault(attr, getattr(self, attr, None)) + kw.setdefault('metadata', self._provider) + # Unsafely unpacking. But keeping **kw for backwards and subclassing compatibility + return self.__class__(**kw) # type:ignore[arg-type] + + @property + def extras(self): + return [dep for dep in self._dep_map if dep] + + +class EggInfoDistribution(Distribution): + def _reload_version(self): + """ + Packages installed by distutils (e.g. numpy or scipy), + which uses an old safe_version, and so + their version numbers can get mangled when + converted to filenames (e.g., 1.11.0.dev0+2329eae to + 1.11.0.dev0_2329eae). These distributions will not be + parsed properly + downstream by Distribution and safe_version, so + take an extra step and try to get the version number from + the metadata file itself instead of the filename. + """ + md_version = self._get_version() + if md_version: + self._version = md_version + return self + + +class DistInfoDistribution(Distribution): + """ + Wrap an actual or potential sys.path entry + w/metadata, .dist-info style. + """ + + PKG_INFO = 'METADATA' + EQEQ = re.compile(r"([\(,])\s*(\d.*?)\s*([,\)])") + + @property + def _parsed_pkg_info(self): + """Parse and cache metadata""" + try: + return self._pkg_info + except AttributeError: + metadata = self.get_metadata(self.PKG_INFO) + self._pkg_info = email.parser.Parser().parsestr(metadata) + return self._pkg_info + + @property + def _dep_map(self): + try: + return self.__dep_map + except AttributeError: + self.__dep_map = self._compute_dependencies() + return self.__dep_map + + def _compute_dependencies(self) -> dict[str | None, list[Requirement]]: + """Recompute this distribution's dependencies.""" + self.__dep_map: dict[str | None, list[Requirement]] = {None: []} + + reqs: list[Requirement] = [] + # Including any condition expressions + for req in self._parsed_pkg_info.get_all('Requires-Dist') or []: + reqs.extend(parse_requirements(req)) + + def reqs_for_extra(extra): + for req in reqs: + if not req.marker or req.marker.evaluate({'extra': extra}): + yield req + + common = types.MappingProxyType(dict.fromkeys(reqs_for_extra(None))) + self.__dep_map[None].extend(common) + + for extra in self._parsed_pkg_info.get_all('Provides-Extra') or []: + s_extra = safe_extra(extra.strip()) + self.__dep_map[s_extra] = [ + r for r in reqs_for_extra(extra) if r not in common + ] + + return self.__dep_map + + +_distributionImpl = { + '.egg': Distribution, + '.egg-info': EggInfoDistribution, + '.dist-info': DistInfoDistribution, +} + + +def issue_warning(*args, **kw): + level = 1 + g = globals() + try: + # find the first stack frame that is *not* code in + # the pkg_resources module, to use for the warning + while sys._getframe(level).f_globals is g: + level += 1 + except ValueError: + pass + warnings.warn(stacklevel=level + 1, *args, **kw) + + +def parse_requirements(strs: _NestedStr) -> map[Requirement]: + """ + Yield ``Requirement`` objects for each specification in `strs`. + + `strs` must be a string, or a (possibly-nested) iterable thereof. + """ + return map(Requirement, join_continuation(map(drop_comment, yield_lines(strs)))) + + +class RequirementParseError(packaging.requirements.InvalidRequirement): + "Compatibility wrapper for InvalidRequirement" + + +class Requirement(packaging.requirements.Requirement): + # prefer variable length tuple to set (as found in + # packaging.requirements.Requirement) + extras: tuple[str, ...] # type: ignore[assignment] + + def __init__(self, requirement_string: str) -> None: + """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!""" + super().__init__(requirement_string) + self.unsafe_name = self.name + project_name = safe_name(self.name) + self.project_name, self.key = project_name, project_name.lower() + self.specs = [(spec.operator, spec.version) for spec in self.specifier] + self.extras = tuple(map(safe_extra, self.extras)) + self.hashCmp = ( + self.key, + self.url, + self.specifier, + frozenset(self.extras), + str(self.marker) if self.marker else None, + ) + self.__hash = hash(self.hashCmp) + + def __eq__(self, other: object) -> bool: + return isinstance(other, Requirement) and self.hashCmp == other.hashCmp + + def __ne__(self, other: object) -> bool: + return not self == other + + def __contains__( + self, item: Distribution | packaging.specifiers.UnparsedVersion + ) -> bool: + if isinstance(item, Distribution): + if item.key != self.key: + return False + + version = item.version + else: + version = item + + # Allow prereleases always in order to match the previous behavior of + # this method. In the future this should be smarter and follow PEP 440 + # more accurately. + return self.specifier.contains( + version, + prereleases=True, + ) + + def __hash__(self) -> int: + return self.__hash + + def __repr__(self) -> str: + return f"Requirement.parse({str(self)!r})" + + @staticmethod + def parse(s: str | Iterable[str]) -> Requirement: + (req,) = parse_requirements(s) + return req + + +def _always_object(classes): + """ + Ensure object appears in the mro even + for old-style classes. + """ + if object not in classes: + return classes + (object,) + return classes + + +def _find_adapter(registry: Mapping[type, _AdapterT], ob: object) -> _AdapterT: + """Return an adapter factory for `ob` from `registry`""" + types = _always_object(inspect.getmro(getattr(ob, '__class__', type(ob)))) + for t in types: + if t in registry: + return registry[t] + # _find_adapter would previously return None, and immediately be called. + # So we're raising a TypeError to keep backward compatibility if anyone depended on that behaviour. + raise TypeError(f"Could not find adapter for {registry} and {ob}") + + +def ensure_directory(path: StrOrBytesPath) -> None: + """Ensure that the parent directory of `path` exists""" + dirname = os.path.dirname(path) + os.makedirs(dirname, exist_ok=True) + + +def _bypass_ensure_directory(path) -> None: + """Sandbox-bypassing version of ensure_directory()""" + if not WRITE_SUPPORT: + raise OSError('"os.mkdir" not supported on this platform.') + dirname, filename = split(path) + if dirname and filename and not isdir(dirname): + _bypass_ensure_directory(dirname) + try: + mkdir(dirname, 0o755) + except FileExistsError: + pass + + +def split_sections(s: _NestedStr) -> Iterator[tuple[str | None, list[str]]]: + """Split a string or iterable thereof into (section, content) pairs + + Each ``section`` is a stripped version of the section header ("[section]") + and each ``content`` is a list of stripped lines excluding blank lines and + comment-only lines. If there are any such lines before the first section + header, they're returned in a first ``section`` of ``None``. + """ + section = None + content: list[str] = [] + for line in yield_lines(s): + if line.startswith("["): + if line.endswith("]"): + if section or content: + yield section, content + section = line[1:-1].strip() + content = [] + else: + raise ValueError("Invalid section heading", line) + else: + content.append(line) + + # wrap up last segment + yield section, content + + +def _mkstemp(*args, **kw): + old_open = os.open + try: + # temporarily bypass sandboxing + os.open = os_open + return tempfile.mkstemp(*args, **kw) + finally: + # and then put it back + os.open = old_open + + +# Silence the PEP440Warning by default, so that end users don't get hit by it +# randomly just because they use pkg_resources. We want to append the rule +# because we want earlier uses of filterwarnings to take precedence over this +# one. +warnings.filterwarnings("ignore", category=PEP440Warning, append=True) + + +class PkgResourcesDeprecationWarning(Warning): + """ + Base class for warning about deprecations in ``pkg_resources`` + + This class is not derived from ``DeprecationWarning``, and as such is + visible by default. + """ + + +# Ported from ``setuptools`` to avoid introducing an import inter-dependency: +_LOCALE_ENCODING = "locale" if sys.version_info >= (3, 10) else None + + +# This must go before calls to `_call_aside`. See https://github.com/pypa/setuptools/pull/4422 +def _read_utf8_with_fallback(file: str, fallback_encoding=_LOCALE_ENCODING) -> str: + """See setuptools.unicode_utils._read_utf8_with_fallback""" + try: + with open(file, "r", encoding="utf-8") as f: + return f.read() + except UnicodeDecodeError: # pragma: no cover + msg = f"""\ + ******************************************************************************** + `encoding="utf-8"` fails with {file!r}, trying `encoding={fallback_encoding!r}`. + + This fallback behaviour is considered **deprecated** and future versions of + `setuptools/pkg_resources` may not implement it. + + Please encode {file!r} with "utf-8" to ensure future builds will succeed. + + If this file was produced by `setuptools` itself, cleaning up the cached files + and re-building/re-installing the package with a newer version of `setuptools` + (e.g. by updating `build-system.requires` in its `pyproject.toml`) + might solve the problem. + ******************************************************************************** + """ + # TODO: Add a deadline? + # See comment in setuptools.unicode_utils._Utf8EncodingNeeded + warnings.warn(msg, PkgResourcesDeprecationWarning, stacklevel=2) + with open(file, "r", encoding=fallback_encoding) as f: + return f.read() + + +# from jaraco.functools 1.3 +def _call_aside(f, *args, **kwargs): + f(*args, **kwargs) + return f + + +@_call_aside +def _initialize(g=globals()) -> None: + "Set up global resource manager (deliberately not state-saved)" + manager = ResourceManager() + g['_manager'] = manager + g.update( + (name, getattr(manager, name)) + for name in dir(manager) + if not name.startswith('_') + ) + + +@_call_aside +def _initialize_master_working_set() -> None: + """ + Prepare the master working set and make the ``require()`` + API available. + + This function has explicit effects on the global state + of pkg_resources. It is intended to be invoked once at + the initialization of this module. + + Invocation by other packages is unsupported and done + at their own risk. + """ + working_set = _declare_state('object', 'working_set', WorkingSet._build_master()) + + require = working_set.require + iter_entry_points = working_set.iter_entry_points + add_activation_listener = working_set.subscribe + run_script = working_set.run_script + # backward compatibility + run_main = run_script + # Activate all distributions already on sys.path with replace=False and + # ensure that all distributions added to the working set in the future + # (e.g. by calling ``require()``) will get activated as well, + # with higher priority (replace=True). + tuple(dist.activate(replace=False) for dist in working_set) + add_activation_listener( + lambda dist: dist.activate(replace=True), + existing=False, + ) + working_set.entries = [] + # match order + list(map(working_set.add_entry, sys.path)) + globals().update(locals()) + + +if TYPE_CHECKING: + # All of these are set by the @_call_aside methods above + __resource_manager = ResourceManager() # Won't exist at runtime + resource_exists = __resource_manager.resource_exists + resource_isdir = __resource_manager.resource_isdir + resource_filename = __resource_manager.resource_filename + resource_stream = __resource_manager.resource_stream + resource_string = __resource_manager.resource_string + resource_listdir = __resource_manager.resource_listdir + set_extraction_path = __resource_manager.set_extraction_path + cleanup_resources = __resource_manager.cleanup_resources + + working_set = WorkingSet() + require = working_set.require + iter_entry_points = working_set.iter_entry_points + add_activation_listener = working_set.subscribe + run_script = working_set.run_script + run_main = run_script diff --git a/.venv/lib/python3.12/site-packages/pkg_resources/api_tests.txt b/.venv/lib/python3.12/site-packages/pkg_resources/api_tests.txt new file mode 100644 index 0000000000000000000000000000000000000000..d72b85aa375b3a19cb3d36ccb1dd8d205c425938 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pkg_resources/api_tests.txt @@ -0,0 +1,424 @@ +Pluggable Distributions of Python Software +========================================== + +Distributions +------------- + +A "Distribution" is a collection of files that represent a "Release" of a +"Project" as of a particular point in time, denoted by a +"Version":: + + >>> import sys, pkg_resources + >>> from pkg_resources import Distribution + >>> Distribution(project_name="Foo", version="1.2") + Foo 1.2 + +Distributions have a location, which can be a filename, URL, or really anything +else you care to use:: + + >>> dist = Distribution( + ... location="http://example.com/something", + ... project_name="Bar", version="0.9" + ... ) + + >>> dist + Bar 0.9 (http://example.com/something) + + +Distributions have various introspectable attributes:: + + >>> dist.location + 'http://example.com/something' + + >>> dist.project_name + 'Bar' + + >>> dist.version + '0.9' + + >>> dist.py_version == '{}.{}'.format(*sys.version_info) + True + + >>> print(dist.platform) + None + +Including various computed attributes:: + + >>> from pkg_resources import parse_version + >>> dist.parsed_version == parse_version(dist.version) + True + + >>> dist.key # case-insensitive form of the project name + 'bar' + +Distributions are compared (and hashed) by version first:: + + >>> Distribution(version='1.0') == Distribution(version='1.0') + True + >>> Distribution(version='1.0') == Distribution(version='1.1') + False + >>> Distribution(version='1.0') < Distribution(version='1.1') + True + +but also by project name (case-insensitive), platform, Python version, +location, etc.:: + + >>> Distribution(project_name="Foo",version="1.0") == \ + ... Distribution(project_name="Foo",version="1.0") + True + + >>> Distribution(project_name="Foo",version="1.0") == \ + ... Distribution(project_name="foo",version="1.0") + True + + >>> Distribution(project_name="Foo",version="1.0") == \ + ... Distribution(project_name="Foo",version="1.1") + False + + >>> Distribution(project_name="Foo",py_version="2.3",version="1.0") == \ + ... Distribution(project_name="Foo",py_version="2.4",version="1.0") + False + + >>> Distribution(location="spam",version="1.0") == \ + ... Distribution(location="spam",version="1.0") + True + + >>> Distribution(location="spam",version="1.0") == \ + ... Distribution(location="baz",version="1.0") + False + + + +Hash and compare distribution by prio/plat + +Get version from metadata +provider capabilities +egg_name() +as_requirement() +from_location, from_filename (w/path normalization) + +Releases may have zero or more "Requirements", which indicate +what releases of another project the release requires in order to +function. A Requirement names the other project, expresses some criteria +as to what releases of that project are acceptable, and lists any "Extras" +that the requiring release may need from that project. (An Extra is an +optional feature of a Release, that can only be used if its additional +Requirements are satisfied.) + + + +The Working Set +--------------- + +A collection of active distributions is called a Working Set. Note that a +Working Set can contain any importable distribution, not just pluggable ones. +For example, the Python standard library is an importable distribution that +will usually be part of the Working Set, even though it is not pluggable. +Similarly, when you are doing development work on a project, the files you are +editing are also a Distribution. (And, with a little attention to the +directory names used, and including some additional metadata, such a +"development distribution" can be made pluggable as well.) + + >>> from pkg_resources import WorkingSet + +A working set's entries are the sys.path entries that correspond to the active +distributions. By default, the working set's entries are the items on +``sys.path``:: + + >>> ws = WorkingSet() + >>> ws.entries == sys.path + True + +But you can also create an empty working set explicitly, and add distributions +to it:: + + >>> ws = WorkingSet([]) + >>> ws.add(dist) + >>> ws.entries + ['http://example.com/something'] + >>> dist in ws + True + >>> Distribution('foo',version="") in ws + False + +And you can iterate over its distributions:: + + >>> list(ws) + [Bar 0.9 (http://example.com/something)] + +Adding the same distribution more than once is a no-op:: + + >>> ws.add(dist) + >>> list(ws) + [Bar 0.9 (http://example.com/something)] + +For that matter, adding multiple distributions for the same project also does +nothing, because a working set can only hold one active distribution per +project -- the first one added to it:: + + >>> ws.add( + ... Distribution( + ... 'http://example.com/something', project_name="Bar", + ... version="7.2" + ... ) + ... ) + >>> list(ws) + [Bar 0.9 (http://example.com/something)] + +You can append a path entry to a working set using ``add_entry()``:: + + >>> ws.entries + ['http://example.com/something'] + >>> ws.add_entry(pkg_resources.__file__) + >>> ws.entries + ['http://example.com/something', '...pkg_resources...'] + +Multiple additions result in multiple entries, even if the entry is already in +the working set (because ``sys.path`` can contain the same entry more than +once):: + + >>> ws.add_entry(pkg_resources.__file__) + >>> ws.entries + ['...example.com...', '...pkg_resources...', '...pkg_resources...'] + +And you can specify the path entry a distribution was found under, using the +optional second parameter to ``add()``:: + + >>> ws = WorkingSet([]) + >>> ws.add(dist,"foo") + >>> ws.entries + ['foo'] + +But even if a distribution is found under multiple path entries, it still only +shows up once when iterating the working set: + + >>> ws.add_entry(ws.entries[0]) + >>> list(ws) + [Bar 0.9 (http://example.com/something)] + +You can ask a WorkingSet to ``find()`` a distribution matching a requirement:: + + >>> from pkg_resources import Requirement + >>> print(ws.find(Requirement.parse("Foo==1.0"))) # no match, return None + None + + >>> ws.find(Requirement.parse("Bar==0.9")) # match, return distribution + Bar 0.9 (http://example.com/something) + +Note that asking for a conflicting version of a distribution already in a +working set triggers a ``pkg_resources.VersionConflict`` error: + + >>> try: + ... ws.find(Requirement.parse("Bar==1.0")) + ... except pkg_resources.VersionConflict as exc: + ... print(str(exc)) + ... else: + ... raise AssertionError("VersionConflict was not raised") + (Bar 0.9 (http://example.com/something), Requirement.parse('Bar==1.0')) + +You can subscribe a callback function to receive notifications whenever a new +distribution is added to a working set. The callback is immediately invoked +once for each existing distribution in the working set, and then is called +again for new distributions added thereafter:: + + >>> def added(dist): print("Added %s" % dist) + >>> ws.subscribe(added) + Added Bar 0.9 + >>> foo12 = Distribution(project_name="Foo", version="1.2", location="f12") + >>> ws.add(foo12) + Added Foo 1.2 + +Note, however, that only the first distribution added for a given project name +will trigger a callback, even during the initial ``subscribe()`` callback:: + + >>> foo14 = Distribution(project_name="Foo", version="1.4", location="f14") + >>> ws.add(foo14) # no callback, because Foo 1.2 is already active + + >>> ws = WorkingSet([]) + >>> ws.add(foo12) + >>> ws.add(foo14) + >>> ws.subscribe(added) + Added Foo 1.2 + +And adding a callback more than once has no effect, either:: + + >>> ws.subscribe(added) # no callbacks + + # and no double-callbacks on subsequent additions, either + >>> just_a_test = Distribution(project_name="JustATest", version="0.99") + >>> ws.add(just_a_test) + Added JustATest 0.99 + + +Finding Plugins +--------------- + +``WorkingSet`` objects can be used to figure out what plugins in an +``Environment`` can be loaded without any resolution errors:: + + >>> from pkg_resources import Environment + + >>> plugins = Environment([]) # normally, a list of plugin directories + >>> plugins.add(foo12) + >>> plugins.add(foo14) + >>> plugins.add(just_a_test) + +In the simplest case, we just get the newest version of each distribution in +the plugin environment:: + + >>> ws = WorkingSet([]) + >>> ws.find_plugins(plugins) + ([JustATest 0.99, Foo 1.4 (f14)], {}) + +But if there's a problem with a version conflict or missing requirements, the +method falls back to older versions, and the error info dict will contain an +exception instance for each unloadable plugin:: + + >>> ws.add(foo12) # this will conflict with Foo 1.4 + >>> ws.find_plugins(plugins) + ([JustATest 0.99, Foo 1.2 (f12)], {Foo 1.4 (f14): VersionConflict(...)}) + +But if you disallow fallbacks, the failed plugin will be skipped instead of +trying older versions:: + + >>> ws.find_plugins(plugins, fallback=False) + ([JustATest 0.99], {Foo 1.4 (f14): VersionConflict(...)}) + + + +Platform Compatibility Rules +---------------------------- + +On the Mac, there are potential compatibility issues for modules compiled +on newer versions of macOS than what the user is running. Additionally, +macOS will soon have two platforms to contend with: Intel and PowerPC. + +Basic equality works as on other platforms:: + + >>> from pkg_resources import compatible_platforms as cp + >>> reqd = 'macosx-10.4-ppc' + >>> cp(reqd, reqd) + True + >>> cp("win32", reqd) + False + +Distributions made on other machine types are not compatible:: + + >>> cp("macosx-10.4-i386", reqd) + False + +Distributions made on earlier versions of the OS are compatible, as +long as they are from the same top-level version. The patchlevel version +number does not matter:: + + >>> cp("macosx-10.4-ppc", reqd) + True + >>> cp("macosx-10.3-ppc", reqd) + True + >>> cp("macosx-10.5-ppc", reqd) + False + >>> cp("macosx-9.5-ppc", reqd) + False + +Backwards compatibility for packages made via earlier versions of +setuptools is provided as well:: + + >>> cp("darwin-8.2.0-Power_Macintosh", reqd) + True + >>> cp("darwin-7.2.0-Power_Macintosh", reqd) + True + >>> cp("darwin-8.2.0-Power_Macintosh", "macosx-10.3-ppc") + False + + +Environment Markers +------------------- + + >>> from pkg_resources import invalid_marker as im, evaluate_marker as em + >>> import os + + >>> print(im("sys_platform")) + Expected marker operator, one of <=, <, !=, ==, >=, >, ~=, ===, in, not in + sys_platform + ^ + + >>> print(im("sys_platform==")) + Expected a marker variable or quoted string + sys_platform== + ^ + + >>> print(im("sys_platform=='win32'")) + False + + >>> print(im("sys=='x'")) + Expected a marker variable or quoted string + sys=='x' + ^ + + >>> print(im("(extra)")) + Expected marker operator, one of <=, <, !=, ==, >=, >, ~=, ===, in, not in + (extra) + ^ + + >>> print(im("(extra")) + Expected marker operator, one of <=, <, !=, ==, >=, >, ~=, ===, in, not in + (extra + ^ + + >>> print(im("os.open('foo')=='y'")) + Expected a marker variable or quoted string + os.open('foo')=='y' + ^ + + >>> print(im("'x'=='y' and os.open('foo')=='y'")) # no short-circuit! + Expected a marker variable or quoted string + 'x'=='y' and os.open('foo')=='y' + ^ + + >>> print(im("'x'=='x' or os.open('foo')=='y'")) # no short-circuit! + Expected a marker variable or quoted string + 'x'=='x' or os.open('foo')=='y' + ^ + + >>> print(im("r'x'=='x'")) + Expected a marker variable or quoted string + r'x'=='x' + ^ + + >>> print(im("'''x'''=='x'")) + Expected marker operator, one of <=, <, !=, ==, >=, >, ~=, ===, in, not in + '''x'''=='x' + ^ + + >>> print(im('"""x"""=="x"')) + Expected marker operator, one of <=, <, !=, ==, >=, >, ~=, ===, in, not in + """x"""=="x" + ^ + + >>> print(im(r"x\n=='x'")) + Expected a marker variable or quoted string + x\n=='x' + ^ + + >>> print(im("os.open=='y'")) + Expected a marker variable or quoted string + os.open=='y' + ^ + + >>> em("sys_platform=='win32'") == (sys.platform=='win32') + True + + >>> em("python_version >= '2.7'") + True + + >>> em("python_version > '2.6'") + True + + >>> im("implementation_name=='cpython'") + False + + >>> im("platform_python_implementation=='CPython'") + False + + >>> im("implementation_version=='3.5.1'") + False diff --git a/.venv/lib/python3.12/site-packages/pkg_resources/py.typed b/.venv/lib/python3.12/site-packages/pkg_resources/py.typed new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/platformdirs-4.5.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/platformdirs-4.5.0.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/platformdirs-4.5.0.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/platformdirs-4.5.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/platformdirs-4.5.0.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..f39386dd1c34e037d0a4db5ca252b71cb16468a5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/platformdirs-4.5.0.dist-info/METADATA @@ -0,0 +1,350 @@ +Metadata-Version: 2.4 +Name: platformdirs +Version: 4.5.0 +Summary: A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`. +Project-URL: Changelog, https://github.com/tox-dev/platformdirs/releases +Project-URL: Documentation, https://platformdirs.readthedocs.io +Project-URL: Homepage, https://github.com/tox-dev/platformdirs +Project-URL: Source, https://github.com/tox-dev/platformdirs +Project-URL: Tracker, https://github.com/tox-dev/platformdirs/issues +Maintainer-email: Bernát Gábor , Julian Berman , Ofek Lev , Ronny Pfannschmidt +License-Expression: MIT +License-File: LICENSE +Keywords: appdirs,application,cache,directory,log,user +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 :: Only +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 :: Libraries :: Python Modules +Requires-Python: >=3.10 +Provides-Extra: docs +Requires-Dist: furo>=2025.9.25; extra == 'docs' +Requires-Dist: proselint>=0.14; extra == 'docs' +Requires-Dist: sphinx-autodoc-typehints>=3.2; extra == 'docs' +Requires-Dist: sphinx>=8.2.3; extra == 'docs' +Provides-Extra: test +Requires-Dist: appdirs==1.4.4; extra == 'test' +Requires-Dist: covdefaults>=2.3; extra == 'test' +Requires-Dist: pytest-cov>=7; extra == 'test' +Requires-Dist: pytest-mock>=3.15.1; extra == 'test' +Requires-Dist: pytest>=8.4.2; extra == 'test' +Provides-Extra: type +Requires-Dist: mypy>=1.18.2; extra == 'type' +Description-Content-Type: text/x-rst + +The problem +=========== + +.. image:: https://badge.fury.io/py/platformdirs.svg + :target: https://badge.fury.io/py/platformdirs +.. image:: https://img.shields.io/pypi/pyversions/platformdirs.svg + :target: https://pypi.python.org/pypi/platformdirs/ +.. image:: https://github.com/tox-dev/platformdirs/actions/workflows/check.yaml/badge.svg + :target: https://github.com/platformdirs/platformdirs/actions +.. image:: https://static.pepy.tech/badge/platformdirs/month + :target: https://pepy.tech/project/platformdirs + +When writing desktop application, finding the right location to store user data +and configuration varies per platform. Even for single-platform apps, there +may by plenty of nuances in figuring out the right location. + +For example, if running on macOS, you should use:: + + ~/Library/Application Support/ + +If on Windows (at least English Win) that should be:: + + C:\Users\\Application Data\Local Settings\\ + +or possibly:: + + C:\Users\\Application Data\\ + +for `roaming profiles `_ but that is another story. + +On Linux (and other Unices), according to the `XDG Basedir Spec`_, it should be:: + + ~/.local/share/ + +.. _XDG Basedir Spec: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html + +``platformdirs`` to the rescue +============================== + +This kind of thing is what the ``platformdirs`` package is for. +``platformdirs`` will help you choose an appropriate: + +- user data dir (``user_data_dir``) +- user config dir (``user_config_dir``) +- user cache dir (``user_cache_dir``) +- site data dir (``site_data_dir``) +- site config dir (``site_config_dir``) +- user log dir (``user_log_dir``) +- user documents dir (``user_documents_dir``) +- user downloads dir (``user_downloads_dir``) +- user pictures dir (``user_pictures_dir``) +- user videos dir (``user_videos_dir``) +- user music dir (``user_music_dir``) +- user desktop dir (``user_desktop_dir``) +- user runtime dir (``user_runtime_dir``) + +And also: + +- Is slightly opinionated on the directory names used. Look for "OPINION" in + documentation and code for when an opinion is being applied. + +Example output +============== + +On macOS: + +.. code-block:: pycon + + >>> from platformdirs import * + >>> appname = "SuperApp" + >>> appauthor = "Acme" + >>> user_data_dir(appname, appauthor) + '/Users/trentm/Library/Application Support/SuperApp' + >>> user_config_dir(appname, appauthor) + '/Users/trentm/Library/Application Support/SuperApp' + >>> user_cache_dir(appname, appauthor) + '/Users/trentm/Library/Caches/SuperApp' + >>> site_data_dir(appname, appauthor) + '/Library/Application Support/SuperApp' + >>> site_config_dir(appname, appauthor) + '/Library/Application Support/SuperApp' + >>> user_log_dir(appname, appauthor) + '/Users/trentm/Library/Logs/SuperApp' + >>> user_documents_dir() + '/Users/trentm/Documents' + >>> user_downloads_dir() + '/Users/trentm/Downloads' + >>> user_pictures_dir() + '/Users/trentm/Pictures' + >>> user_videos_dir() + '/Users/trentm/Movies' + >>> user_music_dir() + '/Users/trentm/Music' + >>> user_desktop_dir() + '/Users/trentm/Desktop' + >>> user_runtime_dir(appname, appauthor) + '/Users/trentm/Library/Caches/TemporaryItems/SuperApp' + +On Windows: + +.. code-block:: pycon + + >>> from platformdirs import * + >>> appname = "SuperApp" + >>> appauthor = "Acme" + >>> user_data_dir(appname, appauthor) + 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp' + >>> user_data_dir(appname, appauthor, roaming=True) + 'C:\\Users\\trentm\\AppData\\Roaming\\Acme\\SuperApp' + >>> user_config_dir(appname, appauthor) + 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp' + >>> user_cache_dir(appname, appauthor) + 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Cache' + >>> site_data_dir(appname, appauthor) + 'C:\\ProgramData\\Acme\\SuperApp' + >>> site_config_dir(appname, appauthor) + 'C:\\ProgramData\\Acme\\SuperApp' + >>> user_log_dir(appname, appauthor) + 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Logs' + >>> user_documents_dir() + 'C:\\Users\\trentm\\Documents' + >>> user_downloads_dir() + 'C:\\Users\\trentm\\Downloads' + >>> user_pictures_dir() + 'C:\\Users\\trentm\\Pictures' + >>> user_videos_dir() + 'C:\\Users\\trentm\\Videos' + >>> user_music_dir() + 'C:\\Users\\trentm\\Music' + >>> user_desktop_dir() + 'C:\\Users\\trentm\\Desktop' + >>> user_runtime_dir(appname, appauthor) + 'C:\\Users\\trentm\\AppData\\Local\\Temp\\Acme\\SuperApp' + +On Linux: + +.. code-block:: pycon + + >>> from platformdirs import * + >>> appname = "SuperApp" + >>> appauthor = "Acme" + >>> user_data_dir(appname, appauthor) + '/home/trentm/.local/share/SuperApp' + >>> user_config_dir(appname) + '/home/trentm/.config/SuperApp' + >>> user_cache_dir(appname, appauthor) + '/home/trentm/.cache/SuperApp' + >>> site_data_dir(appname, appauthor) + '/usr/local/share/SuperApp' + >>> site_data_dir(appname, appauthor, multipath=True) + '/usr/local/share/SuperApp:/usr/share/SuperApp' + >>> site_config_dir(appname) + '/etc/xdg/SuperApp' + >>> os.environ["XDG_CONFIG_DIRS"] = "/etc:/usr/local/etc" + >>> site_config_dir(appname, multipath=True) + '/etc/SuperApp:/usr/local/etc/SuperApp' + >>> user_log_dir(appname, appauthor) + '/home/trentm/.local/state/SuperApp/log' + >>> user_documents_dir() + '/home/trentm/Documents' + >>> user_downloads_dir() + '/home/trentm/Downloads' + >>> user_pictures_dir() + '/home/trentm/Pictures' + >>> user_videos_dir() + '/home/trentm/Videos' + >>> user_music_dir() + '/home/trentm/Music' + >>> user_desktop_dir() + '/home/trentm/Desktop' + >>> user_runtime_dir(appname, appauthor) + '/run/user/{os.getuid()}/SuperApp' + +On Android:: + + >>> from platformdirs import * + >>> appname = "SuperApp" + >>> appauthor = "Acme" + >>> user_data_dir(appname, appauthor) + '/data/data/com.myApp/files/SuperApp' + >>> user_config_dir(appname) + '/data/data/com.myApp/shared_prefs/SuperApp' + >>> user_cache_dir(appname, appauthor) + '/data/data/com.myApp/cache/SuperApp' + >>> site_data_dir(appname, appauthor) + '/data/data/com.myApp/files/SuperApp' + >>> site_config_dir(appname) + '/data/data/com.myApp/shared_prefs/SuperApp' + >>> user_log_dir(appname, appauthor) + '/data/data/com.myApp/cache/SuperApp/log' + >>> user_documents_dir() + '/storage/emulated/0/Documents' + >>> user_downloads_dir() + '/storage/emulated/0/Downloads' + >>> user_pictures_dir() + '/storage/emulated/0/Pictures' + >>> user_videos_dir() + '/storage/emulated/0/DCIM/Camera' + >>> user_music_dir() + '/storage/emulated/0/Music' + >>> user_desktop_dir() + '/storage/emulated/0/Desktop' + >>> user_runtime_dir(appname, appauthor) + '/data/data/com.myApp/cache/SuperApp/tmp' + +Note: Some android apps like Termux and Pydroid are used as shells. These +apps are used by the end user to emulate Linux environment. Presence of +``SHELL`` environment variable is used by Platformdirs to differentiate +between general android apps and android apps used as shells. Shell android +apps also support ``XDG_*`` environment variables. + + +``PlatformDirs`` for convenience +================================ + +.. code-block:: pycon + + >>> from platformdirs import PlatformDirs + >>> dirs = PlatformDirs("SuperApp", "Acme") + >>> dirs.user_data_dir + '/Users/trentm/Library/Application Support/SuperApp' + >>> dirs.user_config_dir + '/Users/trentm/Library/Application Support/SuperApp' + >>> dirs.user_cache_dir + '/Users/trentm/Library/Caches/SuperApp' + >>> dirs.site_data_dir + '/Library/Application Support/SuperApp' + >>> dirs.site_config_dir + '/Library/Application Support/SuperApp' + >>> dirs.user_cache_dir + '/Users/trentm/Library/Caches/SuperApp' + >>> dirs.user_log_dir + '/Users/trentm/Library/Logs/SuperApp' + >>> dirs.user_documents_dir + '/Users/trentm/Documents' + >>> dirs.user_downloads_dir + '/Users/trentm/Downloads' + >>> dirs.user_pictures_dir + '/Users/trentm/Pictures' + >>> dirs.user_videos_dir + '/Users/trentm/Movies' + >>> dirs.user_music_dir + '/Users/trentm/Music' + >>> dirs.user_desktop_dir + '/Users/trentm/Desktop' + >>> dirs.user_runtime_dir + '/Users/trentm/Library/Caches/TemporaryItems/SuperApp' + +Per-version isolation +===================== + +If you have multiple versions of your app in use that you want to be +able to run side-by-side, then you may want version-isolation for these +dirs:: + + >>> from platformdirs import PlatformDirs + >>> dirs = PlatformDirs("SuperApp", "Acme", version="1.0") + >>> dirs.user_data_dir + '/Users/trentm/Library/Application Support/SuperApp/1.0' + >>> dirs.user_config_dir + '/Users/trentm/Library/Application Support/SuperApp/1.0' + >>> dirs.user_cache_dir + '/Users/trentm/Library/Caches/SuperApp/1.0' + >>> dirs.site_data_dir + '/Library/Application Support/SuperApp/1.0' + >>> dirs.site_config_dir + '/Library/Application Support/SuperApp/1.0' + >>> dirs.user_log_dir + '/Users/trentm/Library/Logs/SuperApp/1.0' + >>> dirs.user_documents_dir + '/Users/trentm/Documents' + >>> dirs.user_downloads_dir + '/Users/trentm/Downloads' + >>> dirs.user_pictures_dir + '/Users/trentm/Pictures' + >>> dirs.user_videos_dir + '/Users/trentm/Movies' + >>> dirs.user_music_dir + '/Users/trentm/Music' + >>> dirs.user_desktop_dir + '/Users/trentm/Desktop' + >>> dirs.user_runtime_dir + '/Users/trentm/Library/Caches/TemporaryItems/SuperApp/1.0' + +Be wary of using this for configuration files though; you'll need to handle +migrating configuration files manually. + +Why this Fork? +============== + +This repository is a friendly fork of the wonderful work started by +`ActiveState `_ who created +``appdirs``, this package's ancestor. + +Maintaining an open source project is no easy task, particularly +from within an organization, and the Python community is indebted +to ``appdirs`` (and to Trent Mick and Jeff Rouse in particular) for +creating an incredibly useful simple module, as evidenced by the wide +number of users it has attracted over the years. + +Nonetheless, given the number of long-standing open issues +and pull requests, and no clear path towards `ensuring +that maintenance of the package would continue or grow +`_, this fork was +created. + +Contributions are most welcome. diff --git a/.venv/lib/python3.12/site-packages/platformdirs-4.5.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/platformdirs-4.5.0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..5d49a8b7c13d7d30b690ca0db74826eae42d6bae --- /dev/null +++ b/.venv/lib/python3.12/site-packages/platformdirs-4.5.0.dist-info/RECORD @@ -0,0 +1,15 @@ +platformdirs-4.5.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +platformdirs-4.5.0.dist-info/METADATA,sha256=mFxZl6Q-fO2nCdWWCJT4WOr4p7U12jZX4lk26MqGy1o,12804 +platformdirs-4.5.0.dist-info/RECORD,, +platformdirs-4.5.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +platformdirs-4.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87 +platformdirs-4.5.0.dist-info/licenses/LICENSE,sha256=KeD9YukphQ6G6yjD_czwzv30-pSHkBHP-z0NS-1tTbY,1089 +platformdirs/__init__.py,sha256=iORRy6_lZ9tXLvO0W6fJPn8QV7F532ivl-f2WGmabBc,22284 +platformdirs/__main__.py,sha256=HnsUQHpiBaiTxwcmwVw-nFaPdVNZtQIdi1eWDtI-MzI,1493 +platformdirs/android.py,sha256=r0DshVBf-RO1jXJGX8C4Til7F1XWt-bkdWMgmvEiaYg,9013 +platformdirs/api.py,sha256=wPHOlwOsfz2oqQZ6A2FcCu5kEAj-JondzoNOHYFQ0h8,9281 +platformdirs/macos.py,sha256=0XoOgin1NK7Qki7iskD-oS8xKxw6bXgoKEgdqpCRAFQ,6322 +platformdirs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +platformdirs/unix.py,sha256=WZmkUA--L3JNRGmz32s35YfoD3ica6xKIPdCV_HhLcs,10458 +platformdirs/version.py,sha256=sved76l3nstESjZInsYGzPryR4cPIaf3QHTJuTDYXNM,704 +platformdirs/windows.py,sha256=IFpiohUBwxPtCzlyKwNtxyW4Jk8haa6W8o59mfrDXVo,10125 diff --git a/.venv/lib/python3.12/site-packages/platformdirs-4.5.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/platformdirs-4.5.0.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/platformdirs-4.5.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/platformdirs-4.5.0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..12228d414b6cfed7c39d3781c85c63256a1d7fb5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/platformdirs-4.5.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.27.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/platformdirs/__init__.py b/.venv/lib/python3.12/site-packages/platformdirs/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..02daa5914a82da08293dc1675e75c11ed3fb2bd8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/platformdirs/__init__.py @@ -0,0 +1,631 @@ +""" +Utilities for determining application-specific dirs. + +See for details and usage. + +""" + +from __future__ import annotations + +import os +import sys +from typing import TYPE_CHECKING + +from .api import PlatformDirsABC +from .version import __version__ +from .version import __version_tuple__ as __version_info__ + +if TYPE_CHECKING: + from pathlib import Path + from typing import Literal + +if sys.platform == "win32": + from platformdirs.windows import Windows as _Result +elif sys.platform == "darwin": + from platformdirs.macos import MacOS as _Result +else: + from platformdirs.unix import Unix as _Result + + +def _set_platform_dir_class() -> type[PlatformDirsABC]: + if os.getenv("ANDROID_DATA") == "/data" and os.getenv("ANDROID_ROOT") == "/system": + if os.getenv("SHELL") or os.getenv("PREFIX"): + return _Result + + from platformdirs.android import _android_folder # noqa: PLC0415 + + if _android_folder() is not None: + from platformdirs.android import Android # noqa: PLC0415 + + return Android # return to avoid redefinition of a result + + return _Result + + +if TYPE_CHECKING: + # Work around mypy issue: https://github.com/python/mypy/issues/10962 + PlatformDirs = _Result +else: + PlatformDirs = _set_platform_dir_class() #: Currently active platform +AppDirs = PlatformDirs #: Backwards compatibility with appdirs + + +def user_data_dir( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + roaming: bool = False, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param roaming: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: data directory tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + roaming=roaming, + ensure_exists=ensure_exists, + ).user_data_dir + + +def site_data_dir( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + multipath: bool = False, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param multipath: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: data directory shared by users + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + multipath=multipath, + ensure_exists=ensure_exists, + ).site_data_dir + + +def user_config_dir( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + roaming: bool = False, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param roaming: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: config directory tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + roaming=roaming, + ensure_exists=ensure_exists, + ).user_config_dir + + +def site_config_dir( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + multipath: bool = False, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param multipath: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: config directory shared by the users + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + multipath=multipath, + ensure_exists=ensure_exists, + ).site_config_dir + + +def user_cache_dir( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + opinion: bool = True, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: cache directory tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + opinion=opinion, + ensure_exists=ensure_exists, + ).user_cache_dir + + +def site_cache_dir( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + opinion: bool = True, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `opinion `. + :param ensure_exists: See `ensure_exists `. + :returns: cache directory tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + opinion=opinion, + ensure_exists=ensure_exists, + ).site_cache_dir + + +def user_state_dir( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + roaming: bool = False, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param roaming: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: state directory tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + roaming=roaming, + ensure_exists=ensure_exists, + ).user_state_dir + + +def user_log_dir( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + opinion: bool = True, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: log directory tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + opinion=opinion, + ensure_exists=ensure_exists, + ).user_log_dir + + +def user_documents_dir() -> str: + """:returns: documents directory tied to the user""" + return PlatformDirs().user_documents_dir + + +def user_downloads_dir() -> str: + """:returns: downloads directory tied to the user""" + return PlatformDirs().user_downloads_dir + + +def user_pictures_dir() -> str: + """:returns: pictures directory tied to the user""" + return PlatformDirs().user_pictures_dir + + +def user_videos_dir() -> str: + """:returns: videos directory tied to the user""" + return PlatformDirs().user_videos_dir + + +def user_music_dir() -> str: + """:returns: music directory tied to the user""" + return PlatformDirs().user_music_dir + + +def user_desktop_dir() -> str: + """:returns: desktop directory tied to the user""" + return PlatformDirs().user_desktop_dir + + +def user_runtime_dir( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + opinion: bool = True, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `opinion `. + :param ensure_exists: See `ensure_exists `. + :returns: runtime directory tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + opinion=opinion, + ensure_exists=ensure_exists, + ).user_runtime_dir + + +def site_runtime_dir( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + opinion: bool = True, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> str: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `opinion `. + :param ensure_exists: See `ensure_exists `. + :returns: runtime directory shared by users + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + opinion=opinion, + ensure_exists=ensure_exists, + ).site_runtime_dir + + +def user_data_path( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + roaming: bool = False, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param roaming: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: data path tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + roaming=roaming, + ensure_exists=ensure_exists, + ).user_data_path + + +def site_data_path( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + multipath: bool = False, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param multipath: See `multipath `. + :param ensure_exists: See `ensure_exists `. + :returns: data path shared by users + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + multipath=multipath, + ensure_exists=ensure_exists, + ).site_data_path + + +def user_config_path( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + roaming: bool = False, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param roaming: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: config path tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + roaming=roaming, + ensure_exists=ensure_exists, + ).user_config_path + + +def site_config_path( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + multipath: bool = False, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param multipath: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: config path shared by the users + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + multipath=multipath, + ensure_exists=ensure_exists, + ).site_config_path + + +def site_cache_path( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + opinion: bool = True, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `opinion `. + :param ensure_exists: See `ensure_exists `. + :returns: cache directory tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + opinion=opinion, + ensure_exists=ensure_exists, + ).site_cache_path + + +def user_cache_path( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + opinion: bool = True, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: cache path tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + opinion=opinion, + ensure_exists=ensure_exists, + ).user_cache_path + + +def user_state_path( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + roaming: bool = False, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param roaming: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: state path tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + roaming=roaming, + ensure_exists=ensure_exists, + ).user_state_path + + +def user_log_path( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + opinion: bool = True, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `roaming `. + :param ensure_exists: See `ensure_exists `. + :returns: log path tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + opinion=opinion, + ensure_exists=ensure_exists, + ).user_log_path + + +def user_documents_path() -> Path: + """:returns: documents a path tied to the user""" + return PlatformDirs().user_documents_path + + +def user_downloads_path() -> Path: + """:returns: downloads path tied to the user""" + return PlatformDirs().user_downloads_path + + +def user_pictures_path() -> Path: + """:returns: pictures path tied to the user""" + return PlatformDirs().user_pictures_path + + +def user_videos_path() -> Path: + """:returns: videos path tied to the user""" + return PlatformDirs().user_videos_path + + +def user_music_path() -> Path: + """:returns: music path tied to the user""" + return PlatformDirs().user_music_path + + +def user_desktop_path() -> Path: + """:returns: desktop path tied to the user""" + return PlatformDirs().user_desktop_path + + +def user_runtime_path( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + opinion: bool = True, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `opinion `. + :param ensure_exists: See `ensure_exists `. + :returns: runtime path tied to the user + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + opinion=opinion, + ensure_exists=ensure_exists, + ).user_runtime_path + + +def site_runtime_path( + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + opinion: bool = True, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 +) -> Path: + """ + :param appname: See `appname `. + :param appauthor: See `appauthor `. + :param version: See `version `. + :param opinion: See `opinion `. + :param ensure_exists: See `ensure_exists `. + :returns: runtime path shared by users + """ + return PlatformDirs( + appname=appname, + appauthor=appauthor, + version=version, + opinion=opinion, + ensure_exists=ensure_exists, + ).site_runtime_path + + +__all__ = [ + "AppDirs", + "PlatformDirs", + "PlatformDirsABC", + "__version__", + "__version_info__", + "site_cache_dir", + "site_cache_path", + "site_config_dir", + "site_config_path", + "site_data_dir", + "site_data_path", + "site_runtime_dir", + "site_runtime_path", + "user_cache_dir", + "user_cache_path", + "user_config_dir", + "user_config_path", + "user_data_dir", + "user_data_path", + "user_desktop_dir", + "user_desktop_path", + "user_documents_dir", + "user_documents_path", + "user_downloads_dir", + "user_downloads_path", + "user_log_dir", + "user_log_path", + "user_music_dir", + "user_music_path", + "user_pictures_dir", + "user_pictures_path", + "user_runtime_dir", + "user_runtime_path", + "user_state_dir", + "user_state_path", + "user_videos_dir", + "user_videos_path", +] diff --git a/.venv/lib/python3.12/site-packages/platformdirs/__main__.py b/.venv/lib/python3.12/site-packages/platformdirs/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..922c521358e349470ec48d6372b8f8ee8641128a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/platformdirs/__main__.py @@ -0,0 +1,55 @@ +"""Main entry point.""" + +from __future__ import annotations + +from platformdirs import PlatformDirs, __version__ + +PROPS = ( + "user_data_dir", + "user_config_dir", + "user_cache_dir", + "user_state_dir", + "user_log_dir", + "user_documents_dir", + "user_downloads_dir", + "user_pictures_dir", + "user_videos_dir", + "user_music_dir", + "user_runtime_dir", + "site_data_dir", + "site_config_dir", + "site_cache_dir", + "site_runtime_dir", +) + + +def main() -> None: + """Run the main entry point.""" + app_name = "MyApp" + app_author = "MyCompany" + + print(f"-- platformdirs {__version__} --") # noqa: T201 + + print("-- app dirs (with optional 'version')") # noqa: T201 + dirs = PlatformDirs(app_name, app_author, version="1.0") + for prop in PROPS: + print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201 + + print("\n-- app dirs (without optional 'version')") # noqa: T201 + dirs = PlatformDirs(app_name, app_author) + for prop in PROPS: + print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201 + + print("\n-- app dirs (without optional 'appauthor')") # noqa: T201 + dirs = PlatformDirs(app_name) + for prop in PROPS: + print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201 + + print("\n-- app dirs (with disabled 'appauthor')") # noqa: T201 + dirs = PlatformDirs(app_name, appauthor=False) + for prop in PROPS: + print(f"{prop}: {getattr(dirs, prop)}") # noqa: T201 + + +if __name__ == "__main__": + main() diff --git a/.venv/lib/python3.12/site-packages/platformdirs/android.py b/.venv/lib/python3.12/site-packages/platformdirs/android.py new file mode 100644 index 0000000000000000000000000000000000000000..92efc852d3818b70a708ab61ba2b291eb5a6ee67 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/platformdirs/android.py @@ -0,0 +1,249 @@ +"""Android.""" + +from __future__ import annotations + +import os +import re +import sys +from functools import lru_cache +from typing import TYPE_CHECKING, cast + +from .api import PlatformDirsABC + + +class Android(PlatformDirsABC): + """ + Follows the guidance `from here `_. + + Makes use of the `appname `, `version + `, `ensure_exists `. + + """ + + @property + def user_data_dir(self) -> str: + """:return: data directory tied to the user, e.g. ``/data/user///files/``""" + return self._append_app_name_and_version(cast("str", _android_folder()), "files") + + @property + def site_data_dir(self) -> str: + """:return: data directory shared by users, same as `user_data_dir`""" + return self.user_data_dir + + @property + def user_config_dir(self) -> str: + """ + :return: config directory tied to the user, e.g. \ + ``/data/user///shared_prefs/`` + """ + return self._append_app_name_and_version(cast("str", _android_folder()), "shared_prefs") + + @property + def site_config_dir(self) -> str: + """:return: config directory shared by the users, same as `user_config_dir`""" + return self.user_config_dir + + @property + def user_cache_dir(self) -> str: + """:return: cache directory tied to the user, e.g.,``/data/user///cache/``""" + return self._append_app_name_and_version(cast("str", _android_folder()), "cache") + + @property + def site_cache_dir(self) -> str: + """:return: cache directory shared by users, same as `user_cache_dir`""" + return self.user_cache_dir + + @property + def user_state_dir(self) -> str: + """:return: state directory tied to the user, same as `user_data_dir`""" + return self.user_data_dir + + @property + def user_log_dir(self) -> str: + """ + :return: log directory tied to the user, same as `user_cache_dir` if not opinionated else ``log`` in it, + e.g. ``/data/user///cache//log`` + """ + path = self.user_cache_dir + if self.opinion: + path = os.path.join(path, "log") # noqa: PTH118 + return path + + @property + def user_documents_dir(self) -> str: + """:return: documents directory tied to the user e.g. ``/storage/emulated/0/Documents``""" + return _android_documents_folder() + + @property + def user_downloads_dir(self) -> str: + """:return: downloads directory tied to the user e.g. ``/storage/emulated/0/Downloads``""" + return _android_downloads_folder() + + @property + def user_pictures_dir(self) -> str: + """:return: pictures directory tied to the user e.g. ``/storage/emulated/0/Pictures``""" + return _android_pictures_folder() + + @property + def user_videos_dir(self) -> str: + """:return: videos directory tied to the user e.g. ``/storage/emulated/0/DCIM/Camera``""" + return _android_videos_folder() + + @property + def user_music_dir(self) -> str: + """:return: music directory tied to the user e.g. ``/storage/emulated/0/Music``""" + return _android_music_folder() + + @property + def user_desktop_dir(self) -> str: + """:return: desktop directory tied to the user e.g. ``/storage/emulated/0/Desktop``""" + return "/storage/emulated/0/Desktop" + + @property + def user_runtime_dir(self) -> str: + """ + :return: runtime directory tied to the user, same as `user_cache_dir` if not opinionated else ``tmp`` in it, + e.g. ``/data/user///cache//tmp`` + """ + path = self.user_cache_dir + if self.opinion: + path = os.path.join(path, "tmp") # noqa: PTH118 + return path + + @property + def site_runtime_dir(self) -> str: + """:return: runtime directory shared by users, same as `user_runtime_dir`""" + return self.user_runtime_dir + + +@lru_cache(maxsize=1) +def _android_folder() -> str | None: # noqa: C901 + """:return: base folder for the Android OS or None if it cannot be found""" + result: str | None = None + # type checker isn't happy with our "import android", just don't do this when type checking see + # https://stackoverflow.com/a/61394121 + if not TYPE_CHECKING: + try: + # First try to get a path to android app using python4android (if available)... + from android import mActivity # noqa: PLC0415 + + context = cast("android.content.Context", mActivity.getApplicationContext()) # noqa: F821 + result = context.getFilesDir().getParentFile().getAbsolutePath() + except Exception: # noqa: BLE001 + result = None + if result is None: + try: + # ...and fall back to using plain pyjnius, if python4android isn't available or doesn't deliver any useful + # result... + from jnius import autoclass # noqa: PLC0415 + + context = autoclass("android.content.Context") + result = context.getFilesDir().getParentFile().getAbsolutePath() + except Exception: # noqa: BLE001 + result = None + if result is None: + # and if that fails, too, find an android folder looking at path on the sys.path + # warning: only works for apps installed under /data, not adopted storage etc. + pattern = re.compile(r"/data/(data|user/\d+)/(.+)/files") + for path in sys.path: + if pattern.match(path): + result = path.split("/files")[0] + break + else: + result = None + if result is None: + # one last try: find an android folder looking at path on the sys.path taking adopted storage paths into + # account + pattern = re.compile(r"/mnt/expand/[a-fA-F0-9-]{36}/(data|user/\d+)/(.+)/files") + for path in sys.path: + if pattern.match(path): + result = path.split("/files")[0] + break + else: + result = None + return result + + +@lru_cache(maxsize=1) +def _android_documents_folder() -> str: + """:return: documents folder for the Android OS""" + # Get directories with pyjnius + try: + from jnius import autoclass # noqa: PLC0415 + + context = autoclass("android.content.Context") + environment = autoclass("android.os.Environment") + documents_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DOCUMENTS).getAbsolutePath() + except Exception: # noqa: BLE001 + documents_dir = "/storage/emulated/0/Documents" + + return documents_dir + + +@lru_cache(maxsize=1) +def _android_downloads_folder() -> str: + """:return: downloads folder for the Android OS""" + # Get directories with pyjnius + try: + from jnius import autoclass # noqa: PLC0415 + + context = autoclass("android.content.Context") + environment = autoclass("android.os.Environment") + downloads_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DOWNLOADS).getAbsolutePath() + except Exception: # noqa: BLE001 + downloads_dir = "/storage/emulated/0/Downloads" + + return downloads_dir + + +@lru_cache(maxsize=1) +def _android_pictures_folder() -> str: + """:return: pictures folder for the Android OS""" + # Get directories with pyjnius + try: + from jnius import autoclass # noqa: PLC0415 + + context = autoclass("android.content.Context") + environment = autoclass("android.os.Environment") + pictures_dir: str = context.getExternalFilesDir(environment.DIRECTORY_PICTURES).getAbsolutePath() + except Exception: # noqa: BLE001 + pictures_dir = "/storage/emulated/0/Pictures" + + return pictures_dir + + +@lru_cache(maxsize=1) +def _android_videos_folder() -> str: + """:return: videos folder for the Android OS""" + # Get directories with pyjnius + try: + from jnius import autoclass # noqa: PLC0415 + + context = autoclass("android.content.Context") + environment = autoclass("android.os.Environment") + videos_dir: str = context.getExternalFilesDir(environment.DIRECTORY_DCIM).getAbsolutePath() + except Exception: # noqa: BLE001 + videos_dir = "/storage/emulated/0/DCIM/Camera" + + return videos_dir + + +@lru_cache(maxsize=1) +def _android_music_folder() -> str: + """:return: music folder for the Android OS""" + # Get directories with pyjnius + try: + from jnius import autoclass # noqa: PLC0415 + + context = autoclass("android.content.Context") + environment = autoclass("android.os.Environment") + music_dir: str = context.getExternalFilesDir(environment.DIRECTORY_MUSIC).getAbsolutePath() + except Exception: # noqa: BLE001 + music_dir = "/storage/emulated/0/Music" + + return music_dir + + +__all__ = [ + "Android", +] diff --git a/.venv/lib/python3.12/site-packages/platformdirs/api.py b/.venv/lib/python3.12/site-packages/platformdirs/api.py new file mode 100644 index 0000000000000000000000000000000000000000..251600e6d1b6f7d38f6fee0b91de34d31b2124d0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/platformdirs/api.py @@ -0,0 +1,299 @@ +"""Base API.""" + +from __future__ import annotations + +import os +from abc import ABC, abstractmethod +from pathlib import Path +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Iterator + from typing import Literal + + +class PlatformDirsABC(ABC): # noqa: PLR0904 + """Abstract base class for platform directories.""" + + def __init__( # noqa: PLR0913, PLR0917 + self, + appname: str | None = None, + appauthor: str | Literal[False] | None = None, + version: str | None = None, + roaming: bool = False, # noqa: FBT001, FBT002 + multipath: bool = False, # noqa: FBT001, FBT002 + opinion: bool = True, # noqa: FBT001, FBT002 + ensure_exists: bool = False, # noqa: FBT001, FBT002 + ) -> None: + """ + Create a new platform directory. + + :param appname: See `appname`. + :param appauthor: See `appauthor`. + :param version: See `version`. + :param roaming: See `roaming`. + :param multipath: See `multipath`. + :param opinion: See `opinion`. + :param ensure_exists: See `ensure_exists`. + + """ + self.appname = appname #: The name of application. + self.appauthor = appauthor + """ + The name of the app author or distributing body for this application. + + Typically, it is the owning company name. Defaults to `appname`. You may pass ``False`` to disable it. + + """ + self.version = version + """ + An optional version path element to append to the path. + + You might want to use this if you want multiple versions of your app to be able to run independently. If used, + this would typically be ``.``. + + """ + self.roaming = roaming + """ + Whether to use the roaming appdata directory on Windows. + + That means that for users on a Windows network setup for roaming profiles, this user data will be synced on + login (see + `here `_). + + """ + self.multipath = multipath + """ + An optional parameter which indicates that the entire list of data dirs should be returned. + + By default, the first item would only be returned. + + """ + self.opinion = opinion #: A flag to indicating to use opinionated values. + self.ensure_exists = ensure_exists + """ + Optionally create the directory (and any missing parents) upon access if it does not exist. + + By default, no directories are created. + + """ + + def _append_app_name_and_version(self, *base: str) -> str: + params = list(base[1:]) + if self.appname: + params.append(self.appname) + if self.version: + params.append(self.version) + path = os.path.join(base[0], *params) # noqa: PTH118 + self._optionally_create_directory(path) + return path + + def _optionally_create_directory(self, path: str) -> None: + if self.ensure_exists: + Path(path).mkdir(parents=True, exist_ok=True) + + def _first_item_as_path_if_multipath(self, directory: str) -> Path: + if self.multipath: + # If multipath is True, the first path is returned. + directory = directory.partition(os.pathsep)[0] + return Path(directory) + + @property + @abstractmethod + def user_data_dir(self) -> str: + """:return: data directory tied to the user""" + + @property + @abstractmethod + def site_data_dir(self) -> str: + """:return: data directory shared by users""" + + @property + @abstractmethod + def user_config_dir(self) -> str: + """:return: config directory tied to the user""" + + @property + @abstractmethod + def site_config_dir(self) -> str: + """:return: config directory shared by the users""" + + @property + @abstractmethod + def user_cache_dir(self) -> str: + """:return: cache directory tied to the user""" + + @property + @abstractmethod + def site_cache_dir(self) -> str: + """:return: cache directory shared by users""" + + @property + @abstractmethod + def user_state_dir(self) -> str: + """:return: state directory tied to the user""" + + @property + @abstractmethod + def user_log_dir(self) -> str: + """:return: log directory tied to the user""" + + @property + @abstractmethod + def user_documents_dir(self) -> str: + """:return: documents directory tied to the user""" + + @property + @abstractmethod + def user_downloads_dir(self) -> str: + """:return: downloads directory tied to the user""" + + @property + @abstractmethod + def user_pictures_dir(self) -> str: + """:return: pictures directory tied to the user""" + + @property + @abstractmethod + def user_videos_dir(self) -> str: + """:return: videos directory tied to the user""" + + @property + @abstractmethod + def user_music_dir(self) -> str: + """:return: music directory tied to the user""" + + @property + @abstractmethod + def user_desktop_dir(self) -> str: + """:return: desktop directory tied to the user""" + + @property + @abstractmethod + def user_runtime_dir(self) -> str: + """:return: runtime directory tied to the user""" + + @property + @abstractmethod + def site_runtime_dir(self) -> str: + """:return: runtime directory shared by users""" + + @property + def user_data_path(self) -> Path: + """:return: data path tied to the user""" + return Path(self.user_data_dir) + + @property + def site_data_path(self) -> Path: + """:return: data path shared by users""" + return Path(self.site_data_dir) + + @property + def user_config_path(self) -> Path: + """:return: config path tied to the user""" + return Path(self.user_config_dir) + + @property + def site_config_path(self) -> Path: + """:return: config path shared by the users""" + return Path(self.site_config_dir) + + @property + def user_cache_path(self) -> Path: + """:return: cache path tied to the user""" + return Path(self.user_cache_dir) + + @property + def site_cache_path(self) -> Path: + """:return: cache path shared by users""" + return Path(self.site_cache_dir) + + @property + def user_state_path(self) -> Path: + """:return: state path tied to the user""" + return Path(self.user_state_dir) + + @property + def user_log_path(self) -> Path: + """:return: log path tied to the user""" + return Path(self.user_log_dir) + + @property + def user_documents_path(self) -> Path: + """:return: documents a path tied to the user""" + return Path(self.user_documents_dir) + + @property + def user_downloads_path(self) -> Path: + """:return: downloads path tied to the user""" + return Path(self.user_downloads_dir) + + @property + def user_pictures_path(self) -> Path: + """:return: pictures path tied to the user""" + return Path(self.user_pictures_dir) + + @property + def user_videos_path(self) -> Path: + """:return: videos path tied to the user""" + return Path(self.user_videos_dir) + + @property + def user_music_path(self) -> Path: + """:return: music path tied to the user""" + return Path(self.user_music_dir) + + @property + def user_desktop_path(self) -> Path: + """:return: desktop path tied to the user""" + return Path(self.user_desktop_dir) + + @property + def user_runtime_path(self) -> Path: + """:return: runtime path tied to the user""" + return Path(self.user_runtime_dir) + + @property + def site_runtime_path(self) -> Path: + """:return: runtime path shared by users""" + return Path(self.site_runtime_dir) + + def iter_config_dirs(self) -> Iterator[str]: + """:yield: all user and site configuration directories.""" + yield self.user_config_dir + yield self.site_config_dir + + def iter_data_dirs(self) -> Iterator[str]: + """:yield: all user and site data directories.""" + yield self.user_data_dir + yield self.site_data_dir + + def iter_cache_dirs(self) -> Iterator[str]: + """:yield: all user and site cache directories.""" + yield self.user_cache_dir + yield self.site_cache_dir + + def iter_runtime_dirs(self) -> Iterator[str]: + """:yield: all user and site runtime directories.""" + yield self.user_runtime_dir + yield self.site_runtime_dir + + def iter_config_paths(self) -> Iterator[Path]: + """:yield: all user and site configuration paths.""" + for path in self.iter_config_dirs(): + yield Path(path) + + def iter_data_paths(self) -> Iterator[Path]: + """:yield: all user and site data paths.""" + for path in self.iter_data_dirs(): + yield Path(path) + + def iter_cache_paths(self) -> Iterator[Path]: + """:yield: all user and site cache paths.""" + for path in self.iter_cache_dirs(): + yield Path(path) + + def iter_runtime_paths(self) -> Iterator[Path]: + """:yield: all user and site runtime paths.""" + for path in self.iter_runtime_dirs(): + yield Path(path) diff --git a/.venv/lib/python3.12/site-packages/platformdirs/macos.py b/.venv/lib/python3.12/site-packages/platformdirs/macos.py new file mode 100644 index 0000000000000000000000000000000000000000..30ab368913061aa4b7935e65f5696f7d8cffcf4f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/platformdirs/macos.py @@ -0,0 +1,146 @@ +"""macOS.""" + +from __future__ import annotations + +import os.path +import sys +from typing import TYPE_CHECKING + +from .api import PlatformDirsABC + +if TYPE_CHECKING: + from pathlib import Path + + +class MacOS(PlatformDirsABC): + """ + Platform directories for the macOS operating system. + + Follows the guidance from + `Apple documentation `_. + Makes use of the `appname `, + `version `, + `ensure_exists `. + + """ + + @property + def user_data_dir(self) -> str: + """:return: data directory tied to the user, e.g. ``~/Library/Application Support/$appname/$version``""" + return self._append_app_name_and_version(os.path.expanduser("~/Library/Application Support")) # noqa: PTH111 + + @property + def site_data_dir(self) -> str: + """ + :return: data directory shared by users, e.g. ``/Library/Application Support/$appname/$version``. + If we're using a Python binary managed by `Homebrew `_, the directory + will be under the Homebrew prefix, e.g. ``$homebrew_prefix/share/$appname/$version``. + If `multipath ` is enabled, and we're in Homebrew, + the response is a multi-path string separated by ":", e.g. + ``$homebrew_prefix/share/$appname/$version:/Library/Application Support/$appname/$version`` + """ + is_homebrew = "/opt/python" in sys.prefix + homebrew_prefix = sys.prefix.split("/opt/python")[0] if is_homebrew else "" + path_list = [self._append_app_name_and_version(f"{homebrew_prefix}/share")] if is_homebrew else [] + path_list.append(self._append_app_name_and_version("/Library/Application Support")) + if self.multipath: + return os.pathsep.join(path_list) + return path_list[0] + + @property + def site_data_path(self) -> Path: + """:return: data path shared by users. Only return the first item, even if ``multipath`` is set to ``True``""" + return self._first_item_as_path_if_multipath(self.site_data_dir) + + @property + def user_config_dir(self) -> str: + """:return: config directory tied to the user, same as `user_data_dir`""" + return self.user_data_dir + + @property + def site_config_dir(self) -> str: + """:return: config directory shared by the users, same as `site_data_dir`""" + return self.site_data_dir + + @property + def user_cache_dir(self) -> str: + """:return: cache directory tied to the user, e.g. ``~/Library/Caches/$appname/$version``""" + return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches")) # noqa: PTH111 + + @property + def site_cache_dir(self) -> str: + """ + :return: cache directory shared by users, e.g. ``/Library/Caches/$appname/$version``. + If we're using a Python binary managed by `Homebrew `_, the directory + will be under the Homebrew prefix, e.g. ``$homebrew_prefix/var/cache/$appname/$version``. + If `multipath ` is enabled, and we're in Homebrew, + the response is a multi-path string separated by ":", e.g. + ``$homebrew_prefix/var/cache/$appname/$version:/Library/Caches/$appname/$version`` + """ + is_homebrew = "/opt/python" in sys.prefix + homebrew_prefix = sys.prefix.split("/opt/python")[0] if is_homebrew else "" + path_list = [self._append_app_name_and_version(f"{homebrew_prefix}/var/cache")] if is_homebrew else [] + path_list.append(self._append_app_name_and_version("/Library/Caches")) + if self.multipath: + return os.pathsep.join(path_list) + return path_list[0] + + @property + def site_cache_path(self) -> Path: + """:return: cache path shared by users. Only return the first item, even if ``multipath`` is set to ``True``""" + return self._first_item_as_path_if_multipath(self.site_cache_dir) + + @property + def user_state_dir(self) -> str: + """:return: state directory tied to the user, same as `user_data_dir`""" + return self.user_data_dir + + @property + def user_log_dir(self) -> str: + """:return: log directory tied to the user, e.g. ``~/Library/Logs/$appname/$version``""" + return self._append_app_name_and_version(os.path.expanduser("~/Library/Logs")) # noqa: PTH111 + + @property + def user_documents_dir(self) -> str: + """:return: documents directory tied to the user, e.g. ``~/Documents``""" + return os.path.expanduser("~/Documents") # noqa: PTH111 + + @property + def user_downloads_dir(self) -> str: + """:return: downloads directory tied to the user, e.g. ``~/Downloads``""" + return os.path.expanduser("~/Downloads") # noqa: PTH111 + + @property + def user_pictures_dir(self) -> str: + """:return: pictures directory tied to the user, e.g. ``~/Pictures``""" + return os.path.expanduser("~/Pictures") # noqa: PTH111 + + @property + def user_videos_dir(self) -> str: + """:return: videos directory tied to the user, e.g. ``~/Movies``""" + return os.path.expanduser("~/Movies") # noqa: PTH111 + + @property + def user_music_dir(self) -> str: + """:return: music directory tied to the user, e.g. ``~/Music``""" + return os.path.expanduser("~/Music") # noqa: PTH111 + + @property + def user_desktop_dir(self) -> str: + """:return: desktop directory tied to the user, e.g. ``~/Desktop``""" + return os.path.expanduser("~/Desktop") # noqa: PTH111 + + @property + def user_runtime_dir(self) -> str: + """:return: runtime directory tied to the user, e.g. ``~/Library/Caches/TemporaryItems/$appname/$version``""" + return self._append_app_name_and_version(os.path.expanduser("~/Library/Caches/TemporaryItems")) # noqa: PTH111 + + @property + def site_runtime_dir(self) -> str: + """:return: runtime directory shared by users, same as `user_runtime_dir`""" + return self.user_runtime_dir + + +__all__ = [ + "MacOS", +] diff --git a/.venv/lib/python3.12/site-packages/platformdirs/py.typed b/.venv/lib/python3.12/site-packages/platformdirs/py.typed new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/platformdirs/unix.py b/.venv/lib/python3.12/site-packages/platformdirs/unix.py new file mode 100644 index 0000000000000000000000000000000000000000..fc75d8d0747b0d5ce84b96cfc37877a126d1a9b6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/platformdirs/unix.py @@ -0,0 +1,272 @@ +"""Unix.""" + +from __future__ import annotations + +import os +import sys +from configparser import ConfigParser +from pathlib import Path +from typing import TYPE_CHECKING, NoReturn + +from .api import PlatformDirsABC + +if TYPE_CHECKING: + from collections.abc import Iterator + +if sys.platform == "win32": + + def getuid() -> NoReturn: + msg = "should only be used on Unix" + raise RuntimeError(msg) + +else: + from os import getuid + + +class Unix(PlatformDirsABC): # noqa: PLR0904 + """ + On Unix/Linux, we follow the `XDG Basedir Spec `_. + + The spec allows overriding directories with environment variables. The examples shown are the default values, + alongside the name of the environment variable that overrides them. Makes use of the `appname + `, `version `, `multipath + `, `opinion `, `ensure_exists + `. + + """ + + @property + def user_data_dir(self) -> str: + """ + :return: data directory tied to the user, e.g. ``~/.local/share/$appname/$version`` or + ``$XDG_DATA_HOME/$appname/$version`` + """ + path = os.environ.get("XDG_DATA_HOME", "") + if not path.strip(): + path = os.path.expanduser("~/.local/share") # noqa: PTH111 + return self._append_app_name_and_version(path) + + @property + def _site_data_dirs(self) -> list[str]: + path = os.environ.get("XDG_DATA_DIRS", "") + if not path.strip(): + path = f"/usr/local/share{os.pathsep}/usr/share" + return [self._append_app_name_and_version(p) for p in path.split(os.pathsep)] + + @property + def site_data_dir(self) -> str: + """ + :return: data directories shared by users (if `multipath ` is + enabled and ``XDG_DATA_DIRS`` is set and a multi path the response is also a multi path separated by the + OS path separator), e.g. ``/usr/local/share/$appname/$version`` or ``/usr/share/$appname/$version`` + """ + # XDG default for $XDG_DATA_DIRS; only first, if multipath is False + dirs = self._site_data_dirs + if not self.multipath: + return dirs[0] + return os.pathsep.join(dirs) + + @property + def user_config_dir(self) -> str: + """ + :return: config directory tied to the user, e.g. ``~/.config/$appname/$version`` or + ``$XDG_CONFIG_HOME/$appname/$version`` + """ + path = os.environ.get("XDG_CONFIG_HOME", "") + if not path.strip(): + path = os.path.expanduser("~/.config") # noqa: PTH111 + return self._append_app_name_and_version(path) + + @property + def _site_config_dirs(self) -> list[str]: + path = os.environ.get("XDG_CONFIG_DIRS", "") + if not path.strip(): + path = "/etc/xdg" + return [self._append_app_name_and_version(p) for p in path.split(os.pathsep)] + + @property + def site_config_dir(self) -> str: + """ + :return: config directories shared by users (if `multipath ` + is enabled and ``XDG_CONFIG_DIRS`` is set and a multi path the response is also a multi path separated by + the OS path separator), e.g. ``/etc/xdg/$appname/$version`` + """ + # XDG default for $XDG_CONFIG_DIRS only first, if multipath is False + dirs = self._site_config_dirs + if not self.multipath: + return dirs[0] + return os.pathsep.join(dirs) + + @property + def user_cache_dir(self) -> str: + """ + :return: cache directory tied to the user, e.g. ``~/.cache/$appname/$version`` or + ``~/$XDG_CACHE_HOME/$appname/$version`` + """ + path = os.environ.get("XDG_CACHE_HOME", "") + if not path.strip(): + path = os.path.expanduser("~/.cache") # noqa: PTH111 + return self._append_app_name_and_version(path) + + @property + def site_cache_dir(self) -> str: + """:return: cache directory shared by users, e.g. ``/var/cache/$appname/$version``""" + return self._append_app_name_and_version("/var/cache") + + @property + def user_state_dir(self) -> str: + """ + :return: state directory tied to the user, e.g. ``~/.local/state/$appname/$version`` or + ``$XDG_STATE_HOME/$appname/$version`` + """ + path = os.environ.get("XDG_STATE_HOME", "") + if not path.strip(): + path = os.path.expanduser("~/.local/state") # noqa: PTH111 + return self._append_app_name_and_version(path) + + @property + def user_log_dir(self) -> str: + """:return: log directory tied to the user, same as `user_state_dir` if not opinionated else ``log`` in it""" + path = self.user_state_dir + if self.opinion: + path = os.path.join(path, "log") # noqa: PTH118 + self._optionally_create_directory(path) + return path + + @property + def user_documents_dir(self) -> str: + """:return: documents directory tied to the user, e.g. ``~/Documents``""" + return _get_user_media_dir("XDG_DOCUMENTS_DIR", "~/Documents") + + @property + def user_downloads_dir(self) -> str: + """:return: downloads directory tied to the user, e.g. ``~/Downloads``""" + return _get_user_media_dir("XDG_DOWNLOAD_DIR", "~/Downloads") + + @property + def user_pictures_dir(self) -> str: + """:return: pictures directory tied to the user, e.g. ``~/Pictures``""" + return _get_user_media_dir("XDG_PICTURES_DIR", "~/Pictures") + + @property + def user_videos_dir(self) -> str: + """:return: videos directory tied to the user, e.g. ``~/Videos``""" + return _get_user_media_dir("XDG_VIDEOS_DIR", "~/Videos") + + @property + def user_music_dir(self) -> str: + """:return: music directory tied to the user, e.g. ``~/Music``""" + return _get_user_media_dir("XDG_MUSIC_DIR", "~/Music") + + @property + def user_desktop_dir(self) -> str: + """:return: desktop directory tied to the user, e.g. ``~/Desktop``""" + return _get_user_media_dir("XDG_DESKTOP_DIR", "~/Desktop") + + @property + def user_runtime_dir(self) -> str: + """ + :return: runtime directory tied to the user, e.g. ``/run/user/$(id -u)/$appname/$version`` or + ``$XDG_RUNTIME_DIR/$appname/$version``. + + For FreeBSD/OpenBSD/NetBSD, it would return ``/var/run/user/$(id -u)/$appname/$version`` if + exists, otherwise ``/tmp/runtime-$(id -u)/$appname/$version``, if``$XDG_RUNTIME_DIR`` + is not set. + """ + path = os.environ.get("XDG_RUNTIME_DIR", "") + if not path.strip(): + if sys.platform.startswith(("freebsd", "openbsd", "netbsd")): + path = f"/var/run/user/{getuid()}" + if not Path(path).exists(): + path = f"/tmp/runtime-{getuid()}" # noqa: S108 + else: + path = f"/run/user/{getuid()}" + return self._append_app_name_and_version(path) + + @property + def site_runtime_dir(self) -> str: + """ + :return: runtime directory shared by users, e.g. ``/run/$appname/$version`` or \ + ``$XDG_RUNTIME_DIR/$appname/$version``. + + Note that this behaves almost exactly like `user_runtime_dir` if ``$XDG_RUNTIME_DIR`` is set, but will + fall back to paths associated to the root user instead of a regular logged-in user if it's not set. + + If you wish to ensure that a logged-in root user path is returned e.g. ``/run/user/0``, use `user_runtime_dir` + instead. + + For FreeBSD/OpenBSD/NetBSD, it would return ``/var/run/$appname/$version`` if ``$XDG_RUNTIME_DIR`` is not set. + """ + path = os.environ.get("XDG_RUNTIME_DIR", "") + if not path.strip(): + if sys.platform.startswith(("freebsd", "openbsd", "netbsd")): + path = "/var/run" + else: + path = "/run" + return self._append_app_name_and_version(path) + + @property + def site_data_path(self) -> Path: + """:return: data path shared by users. Only return the first item, even if ``multipath`` is set to ``True``""" + return self._first_item_as_path_if_multipath(self.site_data_dir) + + @property + def site_config_path(self) -> Path: + """:return: config path shared by the users, returns the first item, even if ``multipath`` is set to ``True``""" + return self._first_item_as_path_if_multipath(self.site_config_dir) + + @property + def site_cache_path(self) -> Path: + """:return: cache path shared by users. Only return the first item, even if ``multipath`` is set to ``True``""" + return self._first_item_as_path_if_multipath(self.site_cache_dir) + + def iter_config_dirs(self) -> Iterator[str]: + """:yield: all user and site configuration directories.""" + yield self.user_config_dir + yield from self._site_config_dirs + + def iter_data_dirs(self) -> Iterator[str]: + """:yield: all user and site data directories.""" + yield self.user_data_dir + yield from self._site_data_dirs + + +def _get_user_media_dir(env_var: str, fallback_tilde_path: str) -> str: + media_dir = _get_user_dirs_folder(env_var) + if media_dir is None: + media_dir = os.environ.get(env_var, "").strip() + if not media_dir: + media_dir = os.path.expanduser(fallback_tilde_path) # noqa: PTH111 + + return media_dir + + +def _get_user_dirs_folder(key: str) -> str | None: + """ + Return directory from user-dirs.dirs config file. + + See https://freedesktop.org/wiki/Software/xdg-user-dirs/. + + """ + user_dirs_config_path = Path(Unix().user_config_dir) / "user-dirs.dirs" + if user_dirs_config_path.exists(): + parser = ConfigParser() + + with user_dirs_config_path.open() as stream: + # Add fake section header, so ConfigParser doesn't complain + parser.read_string(f"[top]\n{stream.read()}") + + if key not in parser["top"]: + return None + + path = parser["top"][key].strip('"') + # Handle relative home paths + return path.replace("$HOME", os.path.expanduser("~")) # noqa: PTH111 + + return None + + +__all__ = [ + "Unix", +] diff --git a/.venv/lib/python3.12/site-packages/platformdirs/version.py b/.venv/lib/python3.12/site-packages/platformdirs/version.py new file mode 100644 index 0000000000000000000000000000000000000000..357528256b63664e5a35a5dd3c7ddecf734d167f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/platformdirs/version.py @@ -0,0 +1,34 @@ +# file generated by setuptools-scm +# don't change, don't track in version control + +__all__ = [ + "__version__", + "__version_tuple__", + "version", + "version_tuple", + "__commit_id__", + "commit_id", +] + +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import Tuple + from typing import Union + + VERSION_TUPLE = Tuple[Union[int, str], ...] + COMMIT_ID = Union[str, None] +else: + VERSION_TUPLE = object + COMMIT_ID = object + +version: str +__version__: str +__version_tuple__: VERSION_TUPLE +version_tuple: VERSION_TUPLE +commit_id: COMMIT_ID +__commit_id__: COMMIT_ID + +__version__ = version = '4.5.0' +__version_tuple__ = version_tuple = (4, 5, 0) + +__commit_id__ = commit_id = None diff --git a/.venv/lib/python3.12/site-packages/platformdirs/windows.py b/.venv/lib/python3.12/site-packages/platformdirs/windows.py new file mode 100644 index 0000000000000000000000000000000000000000..d7bc96091a2b1cd078a0847519cb5dd50a5d8898 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/platformdirs/windows.py @@ -0,0 +1,272 @@ +"""Windows.""" + +from __future__ import annotations + +import os +import sys +from functools import lru_cache +from typing import TYPE_CHECKING + +from .api import PlatformDirsABC + +if TYPE_CHECKING: + from collections.abc import Callable + + +class Windows(PlatformDirsABC): + """ + `MSDN on where to store app data files `_. + + Makes use of the `appname `, `appauthor + `, `version `, `roaming + `, `opinion `, `ensure_exists + `. + + """ + + @property + def user_data_dir(self) -> str: + """ + :return: data directory tied to the user, e.g. + ``%USERPROFILE%\\AppData\\Local\\$appauthor\\$appname`` (not roaming) or + ``%USERPROFILE%\\AppData\\Roaming\\$appauthor\\$appname`` (roaming) + """ + const = "CSIDL_APPDATA" if self.roaming else "CSIDL_LOCAL_APPDATA" + path = os.path.normpath(get_win_folder(const)) + return self._append_parts(path) + + def _append_parts(self, path: str, *, opinion_value: str | None = None) -> str: + params = [] + if self.appname: + if self.appauthor is not False: + author = self.appauthor or self.appname + params.append(author) + params.append(self.appname) + if opinion_value is not None and self.opinion: + params.append(opinion_value) + if self.version: + params.append(self.version) + path = os.path.join(path, *params) # noqa: PTH118 + self._optionally_create_directory(path) + return path + + @property + def site_data_dir(self) -> str: + """:return: data directory shared by users, e.g. ``C:\\ProgramData\\$appauthor\\$appname``""" + path = os.path.normpath(get_win_folder("CSIDL_COMMON_APPDATA")) + return self._append_parts(path) + + @property + def user_config_dir(self) -> str: + """:return: config directory tied to the user, same as `user_data_dir`""" + return self.user_data_dir + + @property + def site_config_dir(self) -> str: + """:return: config directory shared by the users, same as `site_data_dir`""" + return self.site_data_dir + + @property + def user_cache_dir(self) -> str: + """ + :return: cache directory tied to the user (if opinionated with ``Cache`` folder within ``$appname``) e.g. + ``%USERPROFILE%\\AppData\\Local\\$appauthor\\$appname\\Cache\\$version`` + """ + path = os.path.normpath(get_win_folder("CSIDL_LOCAL_APPDATA")) + return self._append_parts(path, opinion_value="Cache") + + @property + def site_cache_dir(self) -> str: + """:return: cache directory shared by users, e.g. ``C:\\ProgramData\\$appauthor\\$appname\\Cache\\$version``""" + path = os.path.normpath(get_win_folder("CSIDL_COMMON_APPDATA")) + return self._append_parts(path, opinion_value="Cache") + + @property + def user_state_dir(self) -> str: + """:return: state directory tied to the user, same as `user_data_dir`""" + return self.user_data_dir + + @property + def user_log_dir(self) -> str: + """:return: log directory tied to the user, same as `user_data_dir` if not opinionated else ``Logs`` in it""" + path = self.user_data_dir + if self.opinion: + path = os.path.join(path, "Logs") # noqa: PTH118 + self._optionally_create_directory(path) + return path + + @property + def user_documents_dir(self) -> str: + """:return: documents directory tied to the user e.g. ``%USERPROFILE%\\Documents``""" + return os.path.normpath(get_win_folder("CSIDL_PERSONAL")) + + @property + def user_downloads_dir(self) -> str: + """:return: downloads directory tied to the user e.g. ``%USERPROFILE%\\Downloads``""" + return os.path.normpath(get_win_folder("CSIDL_DOWNLOADS")) + + @property + def user_pictures_dir(self) -> str: + """:return: pictures directory tied to the user e.g. ``%USERPROFILE%\\Pictures``""" + return os.path.normpath(get_win_folder("CSIDL_MYPICTURES")) + + @property + def user_videos_dir(self) -> str: + """:return: videos directory tied to the user e.g. ``%USERPROFILE%\\Videos``""" + return os.path.normpath(get_win_folder("CSIDL_MYVIDEO")) + + @property + def user_music_dir(self) -> str: + """:return: music directory tied to the user e.g. ``%USERPROFILE%\\Music``""" + return os.path.normpath(get_win_folder("CSIDL_MYMUSIC")) + + @property + def user_desktop_dir(self) -> str: + """:return: desktop directory tied to the user, e.g. ``%USERPROFILE%\\Desktop``""" + return os.path.normpath(get_win_folder("CSIDL_DESKTOPDIRECTORY")) + + @property + def user_runtime_dir(self) -> str: + """ + :return: runtime directory tied to the user, e.g. + ``%USERPROFILE%\\AppData\\Local\\Temp\\$appauthor\\$appname`` + """ + path = os.path.normpath(os.path.join(get_win_folder("CSIDL_LOCAL_APPDATA"), "Temp")) # noqa: PTH118 + return self._append_parts(path) + + @property + def site_runtime_dir(self) -> str: + """:return: runtime directory shared by users, same as `user_runtime_dir`""" + return self.user_runtime_dir + + +def get_win_folder_from_env_vars(csidl_name: str) -> str: + """Get folder from environment variables.""" + result = get_win_folder_if_csidl_name_not_env_var(csidl_name) + if result is not None: + return result + + env_var_name = { + "CSIDL_APPDATA": "APPDATA", + "CSIDL_COMMON_APPDATA": "ALLUSERSPROFILE", + "CSIDL_LOCAL_APPDATA": "LOCALAPPDATA", + }.get(csidl_name) + if env_var_name is None: + msg = f"Unknown CSIDL name: {csidl_name}" + raise ValueError(msg) + result = os.environ.get(env_var_name) + if result is None: + msg = f"Unset environment variable: {env_var_name}" + raise ValueError(msg) + return result + + +def get_win_folder_if_csidl_name_not_env_var(csidl_name: str) -> str | None: + """Get a folder for a CSIDL name that does not exist as an environment variable.""" + if csidl_name == "CSIDL_PERSONAL": + return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Documents") # noqa: PTH118 + + if csidl_name == "CSIDL_DOWNLOADS": + return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Downloads") # noqa: PTH118 + + if csidl_name == "CSIDL_MYPICTURES": + return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Pictures") # noqa: PTH118 + + if csidl_name == "CSIDL_MYVIDEO": + return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Videos") # noqa: PTH118 + + if csidl_name == "CSIDL_MYMUSIC": + return os.path.join(os.path.normpath(os.environ["USERPROFILE"]), "Music") # noqa: PTH118 + return None + + +def get_win_folder_from_registry(csidl_name: str) -> str: + """ + Get folder from the registry. + + This is a fallback technique at best. I'm not sure if using the registry for these guarantees us the correct answer + for all CSIDL_* names. + + """ + shell_folder_name = { + "CSIDL_APPDATA": "AppData", + "CSIDL_COMMON_APPDATA": "Common AppData", + "CSIDL_LOCAL_APPDATA": "Local AppData", + "CSIDL_PERSONAL": "Personal", + "CSIDL_DOWNLOADS": "{374DE290-123F-4565-9164-39C4925E467B}", + "CSIDL_MYPICTURES": "My Pictures", + "CSIDL_MYVIDEO": "My Video", + "CSIDL_MYMUSIC": "My Music", + }.get(csidl_name) + if shell_folder_name is None: + msg = f"Unknown CSIDL name: {csidl_name}" + raise ValueError(msg) + if sys.platform != "win32": # only needed for mypy type checker to know that this code runs only on Windows + raise NotImplementedError + import winreg # noqa: PLC0415 + + key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders") + directory, _ = winreg.QueryValueEx(key, shell_folder_name) + return str(directory) + + +def get_win_folder_via_ctypes(csidl_name: str) -> str: + """Get folder with ctypes.""" + # There is no 'CSIDL_DOWNLOADS'. + # Use 'CSIDL_PROFILE' (40) and append the default folder 'Downloads' instead. + # https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid + + import ctypes # noqa: PLC0415 + + csidl_const = { + "CSIDL_APPDATA": 26, + "CSIDL_COMMON_APPDATA": 35, + "CSIDL_LOCAL_APPDATA": 28, + "CSIDL_PERSONAL": 5, + "CSIDL_MYPICTURES": 39, + "CSIDL_MYVIDEO": 14, + "CSIDL_MYMUSIC": 13, + "CSIDL_DOWNLOADS": 40, + "CSIDL_DESKTOPDIRECTORY": 16, + }.get(csidl_name) + if csidl_const is None: + msg = f"Unknown CSIDL name: {csidl_name}" + raise ValueError(msg) + + buf = ctypes.create_unicode_buffer(1024) + windll = getattr(ctypes, "windll") # noqa: B009 # using getattr to avoid false positive with mypy type checker + windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf) + + # Downgrade to short path name if it has high-bit chars. + if any(ord(c) > 255 for c in buf): # noqa: PLR2004 + buf2 = ctypes.create_unicode_buffer(1024) + if windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024): + buf = buf2 + + if csidl_name == "CSIDL_DOWNLOADS": + return os.path.join(buf.value, "Downloads") # noqa: PTH118 + + return buf.value + + +def _pick_get_win_folder() -> Callable[[str], str]: + try: + import ctypes # noqa: PLC0415 + except ImportError: + pass + else: + if hasattr(ctypes, "windll"): + return get_win_folder_via_ctypes + try: + import winreg # noqa: PLC0415, F401 + except ImportError: + return get_win_folder_from_env_vars + else: + return get_win_folder_from_registry + + +get_win_folder = lru_cache(maxsize=None)(_pick_get_win_folder()) + +__all__ = [ + "Windows", +] diff --git a/.venv/lib/python3.12/site-packages/pydantic/__init__.py b/.venv/lib/python3.12/site-packages/pydantic/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ce645b06bee0f6e64b6fbcdbc7d42f2503252960 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/__init__.py @@ -0,0 +1,453 @@ +from importlib import import_module +from typing import TYPE_CHECKING +from warnings import warn + +from ._migration import getattr_migration +from .version import VERSION, _ensure_pydantic_core_version + +_ensure_pydantic_core_version() +del _ensure_pydantic_core_version + +if TYPE_CHECKING: + # import of virtually everything is supported via `__getattr__` below, + # but we need them here for type checking and IDE support + import pydantic_core + from pydantic_core.core_schema import ( + FieldSerializationInfo, + SerializationInfo, + SerializerFunctionWrapHandler, + ValidationInfo, + ValidatorFunctionWrapHandler, + ) + + from . import dataclasses + from .aliases import AliasChoices, AliasGenerator, AliasPath + from .annotated_handlers import GetCoreSchemaHandler, GetJsonSchemaHandler + from .config import ConfigDict, with_config + from .errors import * + from .fields import Field, PrivateAttr, computed_field + from .functional_serializers import ( + PlainSerializer, + SerializeAsAny, + WrapSerializer, + field_serializer, + model_serializer, + ) + from .functional_validators import ( + AfterValidator, + BeforeValidator, + InstanceOf, + ModelWrapValidatorHandler, + PlainValidator, + SkipValidation, + ValidateAs, + WrapValidator, + field_validator, + model_validator, + ) + from .json_schema import WithJsonSchema + from .main import * + from .networks import * + from .type_adapter import TypeAdapter + from .types import * + from .validate_call_decorator import validate_call + from .warnings import ( + PydanticDeprecatedSince20, + PydanticDeprecatedSince26, + PydanticDeprecatedSince29, + PydanticDeprecatedSince210, + PydanticDeprecatedSince211, + PydanticDeprecationWarning, + PydanticExperimentalWarning, + ) + + # this encourages pycharm to import `ValidationError` from here, not pydantic_core + ValidationError = pydantic_core.ValidationError + from .deprecated.class_validators import root_validator, validator + from .deprecated.config import BaseConfig, Extra + from .deprecated.tools import * + from .root_model import RootModel + +__version__ = VERSION +__all__ = ( + # dataclasses + 'dataclasses', + # functional validators + 'field_validator', + 'model_validator', + 'AfterValidator', + 'BeforeValidator', + 'PlainValidator', + 'WrapValidator', + 'SkipValidation', + 'ValidateAs', + 'InstanceOf', + 'ModelWrapValidatorHandler', + # JSON Schema + 'WithJsonSchema', + # deprecated V1 functional validators, these are imported via `__getattr__` below + 'root_validator', + 'validator', + # functional serializers + 'field_serializer', + 'model_serializer', + 'PlainSerializer', + 'SerializeAsAny', + 'WrapSerializer', + # config + 'ConfigDict', + 'with_config', + # deprecated V1 config, these are imported via `__getattr__` below + 'BaseConfig', + 'Extra', + # validate_call + 'validate_call', + # errors + 'PydanticErrorCodes', + 'PydanticUserError', + 'PydanticSchemaGenerationError', + 'PydanticImportError', + 'PydanticUndefinedAnnotation', + 'PydanticInvalidForJsonSchema', + 'PydanticForbiddenQualifier', + # fields + 'Field', + 'computed_field', + 'PrivateAttr', + # alias + 'AliasChoices', + 'AliasGenerator', + 'AliasPath', + # main + 'BaseModel', + 'create_model', + # network + 'AnyUrl', + 'AnyHttpUrl', + 'FileUrl', + 'HttpUrl', + 'FtpUrl', + 'WebsocketUrl', + 'AnyWebsocketUrl', + 'UrlConstraints', + 'EmailStr', + 'NameEmail', + 'IPvAnyAddress', + 'IPvAnyInterface', + 'IPvAnyNetwork', + 'PostgresDsn', + 'CockroachDsn', + 'AmqpDsn', + 'RedisDsn', + 'MongoDsn', + 'KafkaDsn', + 'NatsDsn', + 'MySQLDsn', + 'MariaDBDsn', + 'ClickHouseDsn', + 'SnowflakeDsn', + 'validate_email', + # root_model + 'RootModel', + # deprecated tools, these are imported via `__getattr__` below + 'parse_obj_as', + 'schema_of', + 'schema_json_of', + # types + 'Strict', + 'StrictStr', + 'conbytes', + 'conlist', + 'conset', + 'confrozenset', + 'constr', + 'StringConstraints', + 'ImportString', + 'conint', + 'PositiveInt', + 'NegativeInt', + 'NonNegativeInt', + 'NonPositiveInt', + 'confloat', + 'PositiveFloat', + 'NegativeFloat', + 'NonNegativeFloat', + 'NonPositiveFloat', + 'FiniteFloat', + 'condecimal', + 'condate', + 'UUID1', + 'UUID3', + 'UUID4', + 'UUID5', + 'UUID6', + 'UUID7', + 'UUID8', + 'FilePath', + 'DirectoryPath', + 'NewPath', + 'Json', + 'Secret', + 'SecretStr', + 'SecretBytes', + 'SocketPath', + 'StrictBool', + 'StrictBytes', + 'StrictInt', + 'StrictFloat', + 'PaymentCardNumber', + 'ByteSize', + 'PastDate', + 'FutureDate', + 'PastDatetime', + 'FutureDatetime', + 'AwareDatetime', + 'NaiveDatetime', + 'AllowInfNan', + 'EncoderProtocol', + 'EncodedBytes', + 'EncodedStr', + 'Base64Encoder', + 'Base64Bytes', + 'Base64Str', + 'Base64UrlBytes', + 'Base64UrlStr', + 'GetPydanticSchema', + 'Tag', + 'Discriminator', + 'JsonValue', + 'FailFast', + # type_adapter + 'TypeAdapter', + # version + '__version__', + 'VERSION', + # warnings + 'PydanticDeprecatedSince20', + 'PydanticDeprecatedSince26', + 'PydanticDeprecatedSince29', + 'PydanticDeprecatedSince210', + 'PydanticDeprecatedSince211', + 'PydanticDeprecationWarning', + 'PydanticExperimentalWarning', + # annotated handlers + 'GetCoreSchemaHandler', + 'GetJsonSchemaHandler', + # pydantic_core + 'ValidationError', + 'ValidationInfo', + 'SerializationInfo', + 'ValidatorFunctionWrapHandler', + 'FieldSerializationInfo', + 'SerializerFunctionWrapHandler', + 'OnErrorOmit', +) + +# A mapping of {: (package, )} defining dynamic imports +_dynamic_imports: 'dict[str, tuple[str, str]]' = { + 'dataclasses': (__spec__.parent, '__module__'), + # functional validators + 'field_validator': (__spec__.parent, '.functional_validators'), + 'model_validator': (__spec__.parent, '.functional_validators'), + 'AfterValidator': (__spec__.parent, '.functional_validators'), + 'BeforeValidator': (__spec__.parent, '.functional_validators'), + 'PlainValidator': (__spec__.parent, '.functional_validators'), + 'WrapValidator': (__spec__.parent, '.functional_validators'), + 'SkipValidation': (__spec__.parent, '.functional_validators'), + 'InstanceOf': (__spec__.parent, '.functional_validators'), + 'ValidateAs': (__spec__.parent, '.functional_validators'), + 'ModelWrapValidatorHandler': (__spec__.parent, '.functional_validators'), + # JSON Schema + 'WithJsonSchema': (__spec__.parent, '.json_schema'), + # functional serializers + 'field_serializer': (__spec__.parent, '.functional_serializers'), + 'model_serializer': (__spec__.parent, '.functional_serializers'), + 'PlainSerializer': (__spec__.parent, '.functional_serializers'), + 'SerializeAsAny': (__spec__.parent, '.functional_serializers'), + 'WrapSerializer': (__spec__.parent, '.functional_serializers'), + # config + 'ConfigDict': (__spec__.parent, '.config'), + 'with_config': (__spec__.parent, '.config'), + # validate call + 'validate_call': (__spec__.parent, '.validate_call_decorator'), + # errors + 'PydanticErrorCodes': (__spec__.parent, '.errors'), + 'PydanticUserError': (__spec__.parent, '.errors'), + 'PydanticSchemaGenerationError': (__spec__.parent, '.errors'), + 'PydanticImportError': (__spec__.parent, '.errors'), + 'PydanticUndefinedAnnotation': (__spec__.parent, '.errors'), + 'PydanticInvalidForJsonSchema': (__spec__.parent, '.errors'), + 'PydanticForbiddenQualifier': (__spec__.parent, '.errors'), + # fields + 'Field': (__spec__.parent, '.fields'), + 'computed_field': (__spec__.parent, '.fields'), + 'PrivateAttr': (__spec__.parent, '.fields'), + # alias + 'AliasChoices': (__spec__.parent, '.aliases'), + 'AliasGenerator': (__spec__.parent, '.aliases'), + 'AliasPath': (__spec__.parent, '.aliases'), + # main + 'BaseModel': (__spec__.parent, '.main'), + 'create_model': (__spec__.parent, '.main'), + # network + 'AnyUrl': (__spec__.parent, '.networks'), + 'AnyHttpUrl': (__spec__.parent, '.networks'), + 'FileUrl': (__spec__.parent, '.networks'), + 'HttpUrl': (__spec__.parent, '.networks'), + 'FtpUrl': (__spec__.parent, '.networks'), + 'WebsocketUrl': (__spec__.parent, '.networks'), + 'AnyWebsocketUrl': (__spec__.parent, '.networks'), + 'UrlConstraints': (__spec__.parent, '.networks'), + 'EmailStr': (__spec__.parent, '.networks'), + 'NameEmail': (__spec__.parent, '.networks'), + 'IPvAnyAddress': (__spec__.parent, '.networks'), + 'IPvAnyInterface': (__spec__.parent, '.networks'), + 'IPvAnyNetwork': (__spec__.parent, '.networks'), + 'PostgresDsn': (__spec__.parent, '.networks'), + 'CockroachDsn': (__spec__.parent, '.networks'), + 'AmqpDsn': (__spec__.parent, '.networks'), + 'RedisDsn': (__spec__.parent, '.networks'), + 'MongoDsn': (__spec__.parent, '.networks'), + 'KafkaDsn': (__spec__.parent, '.networks'), + 'NatsDsn': (__spec__.parent, '.networks'), + 'MySQLDsn': (__spec__.parent, '.networks'), + 'MariaDBDsn': (__spec__.parent, '.networks'), + 'ClickHouseDsn': (__spec__.parent, '.networks'), + 'SnowflakeDsn': (__spec__.parent, '.networks'), + 'validate_email': (__spec__.parent, '.networks'), + # root_model + 'RootModel': (__spec__.parent, '.root_model'), + # types + 'Strict': (__spec__.parent, '.types'), + 'StrictStr': (__spec__.parent, '.types'), + 'conbytes': (__spec__.parent, '.types'), + 'conlist': (__spec__.parent, '.types'), + 'conset': (__spec__.parent, '.types'), + 'confrozenset': (__spec__.parent, '.types'), + 'constr': (__spec__.parent, '.types'), + 'StringConstraints': (__spec__.parent, '.types'), + 'ImportString': (__spec__.parent, '.types'), + 'conint': (__spec__.parent, '.types'), + 'PositiveInt': (__spec__.parent, '.types'), + 'NegativeInt': (__spec__.parent, '.types'), + 'NonNegativeInt': (__spec__.parent, '.types'), + 'NonPositiveInt': (__spec__.parent, '.types'), + 'confloat': (__spec__.parent, '.types'), + 'PositiveFloat': (__spec__.parent, '.types'), + 'NegativeFloat': (__spec__.parent, '.types'), + 'NonNegativeFloat': (__spec__.parent, '.types'), + 'NonPositiveFloat': (__spec__.parent, '.types'), + 'FiniteFloat': (__spec__.parent, '.types'), + 'condecimal': (__spec__.parent, '.types'), + 'condate': (__spec__.parent, '.types'), + 'UUID1': (__spec__.parent, '.types'), + 'UUID3': (__spec__.parent, '.types'), + 'UUID4': (__spec__.parent, '.types'), + 'UUID5': (__spec__.parent, '.types'), + 'UUID6': (__spec__.parent, '.types'), + 'UUID7': (__spec__.parent, '.types'), + 'UUID8': (__spec__.parent, '.types'), + 'FilePath': (__spec__.parent, '.types'), + 'DirectoryPath': (__spec__.parent, '.types'), + 'NewPath': (__spec__.parent, '.types'), + 'Json': (__spec__.parent, '.types'), + 'Secret': (__spec__.parent, '.types'), + 'SecretStr': (__spec__.parent, '.types'), + 'SecretBytes': (__spec__.parent, '.types'), + 'StrictBool': (__spec__.parent, '.types'), + 'StrictBytes': (__spec__.parent, '.types'), + 'StrictInt': (__spec__.parent, '.types'), + 'StrictFloat': (__spec__.parent, '.types'), + 'PaymentCardNumber': (__spec__.parent, '.types'), + 'ByteSize': (__spec__.parent, '.types'), + 'PastDate': (__spec__.parent, '.types'), + 'SocketPath': (__spec__.parent, '.types'), + 'FutureDate': (__spec__.parent, '.types'), + 'PastDatetime': (__spec__.parent, '.types'), + 'FutureDatetime': (__spec__.parent, '.types'), + 'AwareDatetime': (__spec__.parent, '.types'), + 'NaiveDatetime': (__spec__.parent, '.types'), + 'AllowInfNan': (__spec__.parent, '.types'), + 'EncoderProtocol': (__spec__.parent, '.types'), + 'EncodedBytes': (__spec__.parent, '.types'), + 'EncodedStr': (__spec__.parent, '.types'), + 'Base64Encoder': (__spec__.parent, '.types'), + 'Base64Bytes': (__spec__.parent, '.types'), + 'Base64Str': (__spec__.parent, '.types'), + 'Base64UrlBytes': (__spec__.parent, '.types'), + 'Base64UrlStr': (__spec__.parent, '.types'), + 'GetPydanticSchema': (__spec__.parent, '.types'), + 'Tag': (__spec__.parent, '.types'), + 'Discriminator': (__spec__.parent, '.types'), + 'JsonValue': (__spec__.parent, '.types'), + 'OnErrorOmit': (__spec__.parent, '.types'), + 'FailFast': (__spec__.parent, '.types'), + # type_adapter + 'TypeAdapter': (__spec__.parent, '.type_adapter'), + # warnings + 'PydanticDeprecatedSince20': (__spec__.parent, '.warnings'), + 'PydanticDeprecatedSince26': (__spec__.parent, '.warnings'), + 'PydanticDeprecatedSince29': (__spec__.parent, '.warnings'), + 'PydanticDeprecatedSince210': (__spec__.parent, '.warnings'), + 'PydanticDeprecatedSince211': (__spec__.parent, '.warnings'), + 'PydanticDeprecationWarning': (__spec__.parent, '.warnings'), + 'PydanticExperimentalWarning': (__spec__.parent, '.warnings'), + # annotated handlers + 'GetCoreSchemaHandler': (__spec__.parent, '.annotated_handlers'), + 'GetJsonSchemaHandler': (__spec__.parent, '.annotated_handlers'), + # pydantic_core stuff + 'ValidationError': ('pydantic_core', '.'), + 'ValidationInfo': ('pydantic_core', '.core_schema'), + 'SerializationInfo': ('pydantic_core', '.core_schema'), + 'ValidatorFunctionWrapHandler': ('pydantic_core', '.core_schema'), + 'FieldSerializationInfo': ('pydantic_core', '.core_schema'), + 'SerializerFunctionWrapHandler': ('pydantic_core', '.core_schema'), + # deprecated, mostly not included in __all__ + 'root_validator': (__spec__.parent, '.deprecated.class_validators'), + 'validator': (__spec__.parent, '.deprecated.class_validators'), + 'BaseConfig': (__spec__.parent, '.deprecated.config'), + 'Extra': (__spec__.parent, '.deprecated.config'), + 'parse_obj_as': (__spec__.parent, '.deprecated.tools'), + 'schema_of': (__spec__.parent, '.deprecated.tools'), + 'schema_json_of': (__spec__.parent, '.deprecated.tools'), + # deprecated dynamic imports + 'FieldValidationInfo': ('pydantic_core', '.core_schema'), + 'GenerateSchema': (__spec__.parent, '._internal._generate_schema'), +} +_deprecated_dynamic_imports = {'FieldValidationInfo', 'GenerateSchema'} + +_getattr_migration = getattr_migration(__name__) + + +def __getattr__(attr_name: str) -> object: + if attr_name in _deprecated_dynamic_imports: + from pydantic.warnings import PydanticDeprecatedSince20 + + warn( + f'Importing {attr_name} from `pydantic` is deprecated. This feature is either no longer supported, or is not public.', + PydanticDeprecatedSince20, + stacklevel=2, + ) + + dynamic_attr = _dynamic_imports.get(attr_name) + if dynamic_attr is None: + return _getattr_migration(attr_name) + + package, module_name = dynamic_attr + + if module_name == '__module__': + result = import_module(f'.{attr_name}', package=package) + globals()[attr_name] = result + return result + else: + module = import_module(module_name, package=package) + result = getattr(module, attr_name) + g = globals() + for k, (_, v_module_name) in _dynamic_imports.items(): + if v_module_name == module_name and k not in _deprecated_dynamic_imports: + g[k] = getattr(module, k) + return result + + +def __dir__() -> list[str]: + return list(__all__) diff --git a/.venv/lib/python3.12/site-packages/pydantic/_migration.py b/.venv/lib/python3.12/site-packages/pydantic/_migration.py new file mode 100644 index 0000000000000000000000000000000000000000..b4ecd283a0ac53c55ff75e9a03669d03b2d0ea30 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/_migration.py @@ -0,0 +1,316 @@ +import sys +from typing import Any, Callable + +from pydantic.warnings import PydanticDeprecatedSince20 + +from .version import version_short + +MOVED_IN_V2 = { + 'pydantic.utils:version_info': 'pydantic.version:version_info', + 'pydantic.error_wrappers:ValidationError': 'pydantic:ValidationError', + 'pydantic.utils:to_camel': 'pydantic.alias_generators:to_pascal', + 'pydantic.utils:to_lower_camel': 'pydantic.alias_generators:to_camel', + 'pydantic:PyObject': 'pydantic.types:ImportString', + 'pydantic.types:PyObject': 'pydantic.types:ImportString', + 'pydantic.generics:GenericModel': 'pydantic.BaseModel', +} + +DEPRECATED_MOVED_IN_V2 = { + 'pydantic.tools:schema_of': 'pydantic.deprecated.tools:schema_of', + 'pydantic.tools:parse_obj_as': 'pydantic.deprecated.tools:parse_obj_as', + 'pydantic.tools:schema_json_of': 'pydantic.deprecated.tools:schema_json_of', + 'pydantic.json:pydantic_encoder': 'pydantic.deprecated.json:pydantic_encoder', + 'pydantic:validate_arguments': 'pydantic.deprecated.decorator:validate_arguments', + 'pydantic.json:custom_pydantic_encoder': 'pydantic.deprecated.json:custom_pydantic_encoder', + 'pydantic.json:timedelta_isoformat': 'pydantic.deprecated.json:timedelta_isoformat', + 'pydantic.decorator:validate_arguments': 'pydantic.deprecated.decorator:validate_arguments', + 'pydantic.class_validators:validator': 'pydantic.deprecated.class_validators:validator', + 'pydantic.class_validators:root_validator': 'pydantic.deprecated.class_validators:root_validator', + 'pydantic.config:BaseConfig': 'pydantic.deprecated.config:BaseConfig', + 'pydantic.config:Extra': 'pydantic.deprecated.config:Extra', +} + +REDIRECT_TO_V1 = { + f'pydantic.utils:{obj}': f'pydantic.v1.utils:{obj}' + for obj in ( + 'deep_update', + 'GetterDict', + 'lenient_issubclass', + 'lenient_isinstance', + 'is_valid_field', + 'update_not_none', + 'import_string', + 'Representation', + 'ROOT_KEY', + 'smart_deepcopy', + 'sequence_like', + ) +} + + +REMOVED_IN_V2 = { + 'pydantic:ConstrainedBytes', + 'pydantic:ConstrainedDate', + 'pydantic:ConstrainedDecimal', + 'pydantic:ConstrainedFloat', + 'pydantic:ConstrainedFrozenSet', + 'pydantic:ConstrainedInt', + 'pydantic:ConstrainedList', + 'pydantic:ConstrainedSet', + 'pydantic:ConstrainedStr', + 'pydantic:JsonWrapper', + 'pydantic:NoneBytes', + 'pydantic:NoneStr', + 'pydantic:NoneStrBytes', + 'pydantic:Protocol', + 'pydantic:Required', + 'pydantic:StrBytes', + 'pydantic:compiled', + 'pydantic.config:get_config', + 'pydantic.config:inherit_config', + 'pydantic.config:prepare_config', + 'pydantic:create_model_from_namedtuple', + 'pydantic:create_model_from_typeddict', + 'pydantic.dataclasses:create_pydantic_model_from_dataclass', + 'pydantic.dataclasses:make_dataclass_validator', + 'pydantic.dataclasses:set_validation', + 'pydantic.datetime_parse:parse_date', + 'pydantic.datetime_parse:parse_time', + 'pydantic.datetime_parse:parse_datetime', + 'pydantic.datetime_parse:parse_duration', + 'pydantic.error_wrappers:ErrorWrapper', + 'pydantic.errors:AnyStrMaxLengthError', + 'pydantic.errors:AnyStrMinLengthError', + 'pydantic.errors:ArbitraryTypeError', + 'pydantic.errors:BoolError', + 'pydantic.errors:BytesError', + 'pydantic.errors:CallableError', + 'pydantic.errors:ClassError', + 'pydantic.errors:ColorError', + 'pydantic.errors:ConfigError', + 'pydantic.errors:DataclassTypeError', + 'pydantic.errors:DateError', + 'pydantic.errors:DateNotInTheFutureError', + 'pydantic.errors:DateNotInThePastError', + 'pydantic.errors:DateTimeError', + 'pydantic.errors:DecimalError', + 'pydantic.errors:DecimalIsNotFiniteError', + 'pydantic.errors:DecimalMaxDigitsError', + 'pydantic.errors:DecimalMaxPlacesError', + 'pydantic.errors:DecimalWholeDigitsError', + 'pydantic.errors:DictError', + 'pydantic.errors:DurationError', + 'pydantic.errors:EmailError', + 'pydantic.errors:EnumError', + 'pydantic.errors:EnumMemberError', + 'pydantic.errors:ExtraError', + 'pydantic.errors:FloatError', + 'pydantic.errors:FrozenSetError', + 'pydantic.errors:FrozenSetMaxLengthError', + 'pydantic.errors:FrozenSetMinLengthError', + 'pydantic.errors:HashableError', + 'pydantic.errors:IPv4AddressError', + 'pydantic.errors:IPv4InterfaceError', + 'pydantic.errors:IPv4NetworkError', + 'pydantic.errors:IPv6AddressError', + 'pydantic.errors:IPv6InterfaceError', + 'pydantic.errors:IPv6NetworkError', + 'pydantic.errors:IPvAnyAddressError', + 'pydantic.errors:IPvAnyInterfaceError', + 'pydantic.errors:IPvAnyNetworkError', + 'pydantic.errors:IntEnumError', + 'pydantic.errors:IntegerError', + 'pydantic.errors:InvalidByteSize', + 'pydantic.errors:InvalidByteSizeUnit', + 'pydantic.errors:InvalidDiscriminator', + 'pydantic.errors:InvalidLengthForBrand', + 'pydantic.errors:JsonError', + 'pydantic.errors:JsonTypeError', + 'pydantic.errors:ListError', + 'pydantic.errors:ListMaxLengthError', + 'pydantic.errors:ListMinLengthError', + 'pydantic.errors:ListUniqueItemsError', + 'pydantic.errors:LuhnValidationError', + 'pydantic.errors:MissingDiscriminator', + 'pydantic.errors:MissingError', + 'pydantic.errors:NoneIsAllowedError', + 'pydantic.errors:NoneIsNotAllowedError', + 'pydantic.errors:NotDigitError', + 'pydantic.errors:NotNoneError', + 'pydantic.errors:NumberNotGeError', + 'pydantic.errors:NumberNotGtError', + 'pydantic.errors:NumberNotLeError', + 'pydantic.errors:NumberNotLtError', + 'pydantic.errors:NumberNotMultipleError', + 'pydantic.errors:PathError', + 'pydantic.errors:PathNotADirectoryError', + 'pydantic.errors:PathNotAFileError', + 'pydantic.errors:PathNotExistsError', + 'pydantic.errors:PatternError', + 'pydantic.errors:PyObjectError', + 'pydantic.errors:PydanticTypeError', + 'pydantic.errors:PydanticValueError', + 'pydantic.errors:SequenceError', + 'pydantic.errors:SetError', + 'pydantic.errors:SetMaxLengthError', + 'pydantic.errors:SetMinLengthError', + 'pydantic.errors:StrError', + 'pydantic.errors:StrRegexError', + 'pydantic.errors:StrictBoolError', + 'pydantic.errors:SubclassError', + 'pydantic.errors:TimeError', + 'pydantic.errors:TupleError', + 'pydantic.errors:TupleLengthError', + 'pydantic.errors:UUIDError', + 'pydantic.errors:UUIDVersionError', + 'pydantic.errors:UrlError', + 'pydantic.errors:UrlExtraError', + 'pydantic.errors:UrlHostError', + 'pydantic.errors:UrlHostTldError', + 'pydantic.errors:UrlPortError', + 'pydantic.errors:UrlSchemeError', + 'pydantic.errors:UrlSchemePermittedError', + 'pydantic.errors:UrlUserInfoError', + 'pydantic.errors:WrongConstantError', + 'pydantic.main:validate_model', + 'pydantic.networks:stricturl', + 'pydantic:parse_file_as', + 'pydantic:parse_raw_as', + 'pydantic:stricturl', + 'pydantic.tools:parse_file_as', + 'pydantic.tools:parse_raw_as', + 'pydantic.types:ConstrainedBytes', + 'pydantic.types:ConstrainedDate', + 'pydantic.types:ConstrainedDecimal', + 'pydantic.types:ConstrainedFloat', + 'pydantic.types:ConstrainedFrozenSet', + 'pydantic.types:ConstrainedInt', + 'pydantic.types:ConstrainedList', + 'pydantic.types:ConstrainedSet', + 'pydantic.types:ConstrainedStr', + 'pydantic.types:JsonWrapper', + 'pydantic.types:NoneBytes', + 'pydantic.types:NoneStr', + 'pydantic.types:NoneStrBytes', + 'pydantic.types:StrBytes', + 'pydantic.typing:evaluate_forwardref', + 'pydantic.typing:AbstractSetIntStr', + 'pydantic.typing:AnyCallable', + 'pydantic.typing:AnyClassMethod', + 'pydantic.typing:CallableGenerator', + 'pydantic.typing:DictAny', + 'pydantic.typing:DictIntStrAny', + 'pydantic.typing:DictStrAny', + 'pydantic.typing:IntStr', + 'pydantic.typing:ListStr', + 'pydantic.typing:MappingIntStrAny', + 'pydantic.typing:NoArgAnyCallable', + 'pydantic.typing:NoneType', + 'pydantic.typing:ReprArgs', + 'pydantic.typing:SetStr', + 'pydantic.typing:StrPath', + 'pydantic.typing:TupleGenerator', + 'pydantic.typing:WithArgsTypes', + 'pydantic.typing:all_literal_values', + 'pydantic.typing:display_as_type', + 'pydantic.typing:get_all_type_hints', + 'pydantic.typing:get_args', + 'pydantic.typing:get_origin', + 'pydantic.typing:get_sub_types', + 'pydantic.typing:is_callable_type', + 'pydantic.typing:is_classvar', + 'pydantic.typing:is_finalvar', + 'pydantic.typing:is_literal_type', + 'pydantic.typing:is_namedtuple', + 'pydantic.typing:is_new_type', + 'pydantic.typing:is_none_type', + 'pydantic.typing:is_typeddict', + 'pydantic.typing:is_typeddict_special', + 'pydantic.typing:is_union', + 'pydantic.typing:new_type_supertype', + 'pydantic.typing:resolve_annotations', + 'pydantic.typing:typing_base', + 'pydantic.typing:update_field_forward_refs', + 'pydantic.typing:update_model_forward_refs', + 'pydantic.utils:ClassAttribute', + 'pydantic.utils:DUNDER_ATTRIBUTES', + 'pydantic.utils:PyObjectStr', + 'pydantic.utils:ValueItems', + 'pydantic.utils:almost_equal_floats', + 'pydantic.utils:get_discriminator_alias_and_values', + 'pydantic.utils:get_model', + 'pydantic.utils:get_unique_discriminator_alias', + 'pydantic.utils:in_ipython', + 'pydantic.utils:is_valid_identifier', + 'pydantic.utils:path_type', + 'pydantic.utils:validate_field_name', + 'pydantic:validate_model', +} + + +def getattr_migration(module: str) -> Callable[[str], Any]: + """Implement PEP 562 for objects that were either moved or removed on the migration + to V2. + + Args: + module: The module name. + + Returns: + A callable that will raise an error if the object is not found. + """ + # This avoids circular import with errors.py. + from .errors import PydanticImportError + + def wrapper(name: str) -> object: + """Raise an error if the object is not found, or warn if it was moved. + + In case it was moved, it still returns the object. + + Args: + name: The object name. + + Returns: + The object. + """ + if name == '__path__': + raise AttributeError(f'module {module!r} has no attribute {name!r}') + + import warnings + + from ._internal._validators import import_string + + import_path = f'{module}:{name}' + if import_path in MOVED_IN_V2.keys(): + new_location = MOVED_IN_V2[import_path] + warnings.warn( + f'`{import_path}` has been moved to `{new_location}`.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + return import_string(MOVED_IN_V2[import_path]) + if import_path in DEPRECATED_MOVED_IN_V2: + # skip the warning here because a deprecation warning will be raised elsewhere + return import_string(DEPRECATED_MOVED_IN_V2[import_path]) + if import_path in REDIRECT_TO_V1: + new_location = REDIRECT_TO_V1[import_path] + warnings.warn( + f'`{import_path}` has been removed. We are importing from `{new_location}` instead.' + 'See the migration guide for more details: https://docs.pydantic.dev/latest/migration/', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + return import_string(REDIRECT_TO_V1[import_path]) + if import_path == 'pydantic:BaseSettings': + raise PydanticImportError( + '`BaseSettings` has been moved to the `pydantic-settings` package. ' + f'See https://docs.pydantic.dev/{version_short()}/migration/#basesettings-has-moved-to-pydantic-settings ' + 'for more details.' + ) + if import_path in REMOVED_IN_V2: + raise PydanticImportError(f'`{import_path}` has been removed in V2.') + globals: dict[str, Any] = sys.modules[module].__dict__ + if name in globals: + return globals[name] + raise AttributeError(f'module {module!r} has no attribute {name!r}') + + return wrapper diff --git a/.venv/lib/python3.12/site-packages/pydantic/alias_generators.py b/.venv/lib/python3.12/site-packages/pydantic/alias_generators.py new file mode 100644 index 0000000000000000000000000000000000000000..0b7653f5825979edcd446d698d320bdb980b8e9e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/alias_generators.py @@ -0,0 +1,62 @@ +"""Alias generators for converting between different capitalization conventions.""" + +import re + +__all__ = ('to_pascal', 'to_camel', 'to_snake') + +# TODO: in V3, change the argument names to be more descriptive +# Generally, don't only convert from snake_case, or name the functions +# more specifically like snake_to_camel. + + +def to_pascal(snake: str) -> str: + """Convert a snake_case string to PascalCase. + + Args: + snake: The string to convert. + + Returns: + The PascalCase string. + """ + camel = snake.title() + return re.sub('([0-9A-Za-z])_(?=[0-9A-Z])', lambda m: m.group(1), camel) + + +def to_camel(snake: str) -> str: + """Convert a snake_case string to camelCase. + + Args: + snake: The string to convert. + + Returns: + The converted camelCase string. + """ + # If the string is already in camelCase and does not contain a digit followed + # by a lowercase letter, return it as it is + if re.match('^[a-z]+[A-Za-z0-9]*$', snake) and not re.search(r'\d[a-z]', snake): + return snake + + camel = to_pascal(snake) + return re.sub('(^_*[A-Z])', lambda m: m.group(1).lower(), camel) + + +def to_snake(camel: str) -> str: + """Convert a PascalCase, camelCase, or kebab-case string to snake_case. + + Args: + camel: The string to convert. + + Returns: + The converted string in snake_case. + """ + # Handle the sequence of uppercase letters followed by a lowercase letter + snake = re.sub(r'([A-Z]+)([A-Z][a-z])', lambda m: f'{m.group(1)}_{m.group(2)}', camel) + # Insert an underscore between a lowercase letter and an uppercase letter + snake = re.sub(r'([a-z])([A-Z])', lambda m: f'{m.group(1)}_{m.group(2)}', snake) + # Insert an underscore between a digit and an uppercase letter + snake = re.sub(r'([0-9])([A-Z])', lambda m: f'{m.group(1)}_{m.group(2)}', snake) + # Insert an underscore between a lowercase letter and a digit + snake = re.sub(r'([a-z])([0-9])', lambda m: f'{m.group(1)}_{m.group(2)}', snake) + # Replace hyphens with underscores to handle kebab-case + snake = snake.replace('-', '_') + return snake.lower() diff --git a/.venv/lib/python3.12/site-packages/pydantic/aliases.py b/.venv/lib/python3.12/site-packages/pydantic/aliases.py new file mode 100644 index 0000000000000000000000000000000000000000..ac2273701b5a0ae7cb0d0171cd08a7bbf77a9178 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/aliases.py @@ -0,0 +1,135 @@ +"""Support for alias configurations.""" + +from __future__ import annotations + +import dataclasses +from typing import Any, Callable, Literal + +from pydantic_core import PydanticUndefined + +from ._internal import _internal_dataclass + +__all__ = ('AliasGenerator', 'AliasPath', 'AliasChoices') + + +@dataclasses.dataclass(**_internal_dataclass.slots_true) +class AliasPath: + """!!! abstract "Usage Documentation" + [`AliasPath` and `AliasChoices`](../concepts/alias.md#aliaspath-and-aliaschoices) + + A data class used by `validation_alias` as a convenience to create aliases. + + Attributes: + path: A list of string or integer aliases. + """ + + path: list[int | str] + + def __init__(self, first_arg: str, *args: str | int) -> None: + self.path = [first_arg] + list(args) + + def convert_to_aliases(self) -> list[str | int]: + """Converts arguments to a list of string or integer aliases. + + Returns: + The list of aliases. + """ + return self.path + + def search_dict_for_path(self, d: dict) -> Any: + """Searches a dictionary for the path specified by the alias. + + Returns: + The value at the specified path, or `PydanticUndefined` if the path is not found. + """ + v = d + for k in self.path: + if isinstance(v, str): + # disallow indexing into a str, like for AliasPath('x', 0) and x='abc' + return PydanticUndefined + try: + v = v[k] + except (KeyError, IndexError, TypeError): + return PydanticUndefined + return v + + +@dataclasses.dataclass(**_internal_dataclass.slots_true) +class AliasChoices: + """!!! abstract "Usage Documentation" + [`AliasPath` and `AliasChoices`](../concepts/alias.md#aliaspath-and-aliaschoices) + + A data class used by `validation_alias` as a convenience to create aliases. + + Attributes: + choices: A list containing a string or `AliasPath`. + """ + + choices: list[str | AliasPath] + + def __init__(self, first_choice: str | AliasPath, *choices: str | AliasPath) -> None: + self.choices = [first_choice] + list(choices) + + def convert_to_aliases(self) -> list[list[str | int]]: + """Converts arguments to a list of lists containing string or integer aliases. + + Returns: + The list of aliases. + """ + aliases: list[list[str | int]] = [] + for c in self.choices: + if isinstance(c, AliasPath): + aliases.append(c.convert_to_aliases()) + else: + aliases.append([c]) + return aliases + + +@dataclasses.dataclass(**_internal_dataclass.slots_true) +class AliasGenerator: + """!!! abstract "Usage Documentation" + [Using an `AliasGenerator`](../concepts/alias.md#using-an-aliasgenerator) + + A data class used by `alias_generator` as a convenience to create various aliases. + + Attributes: + alias: A callable that takes a field name and returns an alias for it. + validation_alias: A callable that takes a field name and returns a validation alias for it. + serialization_alias: A callable that takes a field name and returns a serialization alias for it. + """ + + alias: Callable[[str], str] | None = None + validation_alias: Callable[[str], str | AliasPath | AliasChoices] | None = None + serialization_alias: Callable[[str], str] | None = None + + def _generate_alias( + self, + alias_kind: Literal['alias', 'validation_alias', 'serialization_alias'], + allowed_types: tuple[type[str] | type[AliasPath] | type[AliasChoices], ...], + field_name: str, + ) -> str | AliasPath | AliasChoices | None: + """Generate an alias of the specified kind. Returns None if the alias generator is None. + + Raises: + TypeError: If the alias generator produces an invalid type. + """ + alias = None + if alias_generator := getattr(self, alias_kind): + alias = alias_generator(field_name) + if alias and not isinstance(alias, allowed_types): + raise TypeError( + f'Invalid `{alias_kind}` type. `{alias_kind}` generator must produce one of `{allowed_types}`' + ) + return alias + + def generate_aliases(self, field_name: str) -> tuple[str | None, str | AliasPath | AliasChoices | None, str | None]: + """Generate `alias`, `validation_alias`, and `serialization_alias` for a field. + + Returns: + A tuple of three aliases - validation, alias, and serialization. + """ + alias = self._generate_alias('alias', (str,), field_name) + validation_alias = self._generate_alias('validation_alias', (str, AliasChoices, AliasPath), field_name) + serialization_alias = self._generate_alias('serialization_alias', (str,), field_name) + + return alias, validation_alias, serialization_alias # type: ignore diff --git a/.venv/lib/python3.12/site-packages/pydantic/annotated_handlers.py b/.venv/lib/python3.12/site-packages/pydantic/annotated_handlers.py new file mode 100644 index 0000000000000000000000000000000000000000..d0cb5d3d4616f938aea2aae42985b4c166b500cc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/annotated_handlers.py @@ -0,0 +1,122 @@ +"""Type annotations to use with `__get_pydantic_core_schema__` and `__get_pydantic_json_schema__`.""" + +from __future__ import annotations as _annotations + +from typing import TYPE_CHECKING, Any, Union + +from pydantic_core import core_schema + +if TYPE_CHECKING: + from ._internal._namespace_utils import NamespacesTuple + from .json_schema import JsonSchemaMode, JsonSchemaValue + + CoreSchemaOrField = Union[ + core_schema.CoreSchema, + core_schema.ModelField, + core_schema.DataclassField, + core_schema.TypedDictField, + core_schema.ComputedField, + ] + +__all__ = 'GetJsonSchemaHandler', 'GetCoreSchemaHandler' + + +class GetJsonSchemaHandler: + """Handler to call into the next JSON schema generation function. + + Attributes: + mode: Json schema mode, can be `validation` or `serialization`. + """ + + mode: JsonSchemaMode + + def __call__(self, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue: + """Call the inner handler and get the JsonSchemaValue it returns. + This will call the next JSON schema modifying function up until it calls + into `pydantic.json_schema.GenerateJsonSchema`, which will raise a + `pydantic.errors.PydanticInvalidForJsonSchema` error if it cannot generate + a JSON schema. + + Args: + core_schema: A `pydantic_core.core_schema.CoreSchema`. + + Returns: + JsonSchemaValue: The JSON schema generated by the inner JSON schema modify + functions. + """ + raise NotImplementedError + + def resolve_ref_schema(self, maybe_ref_json_schema: JsonSchemaValue, /) -> JsonSchemaValue: + """Get the real schema for a `{"$ref": ...}` schema. + If the schema given is not a `$ref` schema, it will be returned as is. + This means you don't have to check before calling this function. + + Args: + maybe_ref_json_schema: A JsonSchemaValue which may be a `$ref` schema. + + Raises: + LookupError: If the ref is not found. + + Returns: + JsonSchemaValue: A JsonSchemaValue that has no `$ref`. + """ + raise NotImplementedError + + +class GetCoreSchemaHandler: + """Handler to call into the next CoreSchema schema generation function.""" + + def __call__(self, source_type: Any, /) -> core_schema.CoreSchema: + """Call the inner handler and get the CoreSchema it returns. + This will call the next CoreSchema modifying function up until it calls + into Pydantic's internal schema generation machinery, which will raise a + `pydantic.errors.PydanticSchemaGenerationError` error if it cannot generate + a CoreSchema for the given source type. + + Args: + source_type: The input type. + + Returns: + CoreSchema: The `pydantic-core` CoreSchema generated. + """ + raise NotImplementedError + + def generate_schema(self, source_type: Any, /) -> core_schema.CoreSchema: + """Generate a schema unrelated to the current context. + Use this function if e.g. you are handling schema generation for a sequence + and want to generate a schema for its items. + Otherwise, you may end up doing something like applying a `min_length` constraint + that was intended for the sequence itself to its items! + + Args: + source_type: The input type. + + Returns: + CoreSchema: The `pydantic-core` CoreSchema generated. + """ + raise NotImplementedError + + def resolve_ref_schema(self, maybe_ref_schema: core_schema.CoreSchema, /) -> core_schema.CoreSchema: + """Get the real schema for a `definition-ref` schema. + If the schema given is not a `definition-ref` schema, it will be returned as is. + This means you don't have to check before calling this function. + + Args: + maybe_ref_schema: A `CoreSchema`, `ref`-based or not. + + Raises: + LookupError: If the `ref` is not found. + + Returns: + A concrete `CoreSchema`. + """ + raise NotImplementedError + + @property + def field_name(self) -> str | None: + """Get the name of the closest field to this validator.""" + raise NotImplementedError + + def _get_types_namespace(self) -> NamespacesTuple: + """Internal method used during type resolution for serializer annotations.""" + raise NotImplementedError diff --git a/.venv/lib/python3.12/site-packages/pydantic/class_validators.py b/.venv/lib/python3.12/site-packages/pydantic/class_validators.py new file mode 100644 index 0000000000000000000000000000000000000000..9977150c92fcb083fcdfa632c9de3b5fa92470cb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/class_validators.py @@ -0,0 +1,5 @@ +"""`class_validators` module is a backport module from V1.""" + +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.venv/lib/python3.12/site-packages/pydantic/color.py b/.venv/lib/python3.12/site-packages/pydantic/color.py new file mode 100644 index 0000000000000000000000000000000000000000..9a42d586fd71a786ba63a4d5ce55a0ed93d866b0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/color.py @@ -0,0 +1,604 @@ +"""Color definitions are used as per the CSS3 +[CSS Color Module Level 3](http://www.w3.org/TR/css3-color/#svg-color) specification. + +A few colors have multiple names referring to the sames colors, eg. `grey` and `gray` or `aqua` and `cyan`. + +In these cases the _last_ color when sorted alphabetically takes preferences, +eg. `Color((0, 255, 255)).as_named() == 'cyan'` because "cyan" comes after "aqua". + +Warning: Deprecated + The `Color` class is deprecated, use `pydantic_extra_types` instead. + See [`pydantic-extra-types.Color`](../usage/types/extra_types/color_types.md) + for more information. +""" + +import math +import re +from colorsys import hls_to_rgb, rgb_to_hls +from typing import Any, Callable, Optional, Union, cast + +from pydantic_core import CoreSchema, PydanticCustomError, core_schema +from typing_extensions import deprecated + +from ._internal import _repr +from ._internal._schema_generation_shared import GetJsonSchemaHandler as _GetJsonSchemaHandler +from .json_schema import JsonSchemaValue +from .warnings import PydanticDeprecatedSince20 + +ColorTuple = Union[tuple[int, int, int], tuple[int, int, int, float]] +ColorType = Union[ColorTuple, str] +HslColorTuple = Union[tuple[float, float, float], tuple[float, float, float, float]] + + +class RGBA: + """Internal use only as a representation of a color.""" + + __slots__ = 'r', 'g', 'b', 'alpha', '_tuple' + + def __init__(self, r: float, g: float, b: float, alpha: Optional[float]): + self.r = r + self.g = g + self.b = b + self.alpha = alpha + + self._tuple: tuple[float, float, float, Optional[float]] = (r, g, b, alpha) + + def __getitem__(self, item: Any) -> Any: + return self._tuple[item] + + +# these are not compiled here to avoid import slowdown, they'll be compiled the first time they're used, then cached +_r_255 = r'(\d{1,3}(?:\.\d+)?)' +_r_comma = r'\s*,\s*' +_r_alpha = r'(\d(?:\.\d+)?|\.\d+|\d{1,2}%)' +_r_h = r'(-?\d+(?:\.\d+)?|-?\.\d+)(deg|rad|turn)?' +_r_sl = r'(\d{1,3}(?:\.\d+)?)%' +r_hex_short = r'\s*(?:#|0x)?([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])?\s*' +r_hex_long = r'\s*(?:#|0x)?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?\s*' +# CSS3 RGB examples: rgb(0, 0, 0), rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 50%) +r_rgb = rf'\s*rgba?\(\s*{_r_255}{_r_comma}{_r_255}{_r_comma}{_r_255}(?:{_r_comma}{_r_alpha})?\s*\)\s*' +# CSS3 HSL examples: hsl(270, 60%, 50%), hsla(270, 60%, 50%, 0.5), hsla(270, 60%, 50%, 50%) +r_hsl = rf'\s*hsla?\(\s*{_r_h}{_r_comma}{_r_sl}{_r_comma}{_r_sl}(?:{_r_comma}{_r_alpha})?\s*\)\s*' +# CSS4 RGB examples: rgb(0 0 0), rgb(0 0 0 / 0.5), rgb(0 0 0 / 50%), rgba(0 0 0 / 50%) +r_rgb_v4_style = rf'\s*rgba?\(\s*{_r_255}\s+{_r_255}\s+{_r_255}(?:\s*/\s*{_r_alpha})?\s*\)\s*' +# CSS4 HSL examples: hsl(270 60% 50%), hsl(270 60% 50% / 0.5), hsl(270 60% 50% / 50%), hsla(270 60% 50% / 50%) +r_hsl_v4_style = rf'\s*hsla?\(\s*{_r_h}\s+{_r_sl}\s+{_r_sl}(?:\s*/\s*{_r_alpha})?\s*\)\s*' + +# colors where the two hex characters are the same, if all colors match this the short version of hex colors can be used +repeat_colors = {int(c * 2, 16) for c in '0123456789abcdef'} +rads = 2 * math.pi + + +@deprecated( + 'The `Color` class is deprecated, use `pydantic_extra_types` instead. ' + 'See https://docs.pydantic.dev/latest/api/pydantic_extra_types_color/.', + category=PydanticDeprecatedSince20, +) +class Color(_repr.Representation): + """Represents a color.""" + + __slots__ = '_original', '_rgba' + + def __init__(self, value: ColorType) -> None: + self._rgba: RGBA + self._original: ColorType + if isinstance(value, (tuple, list)): + self._rgba = parse_tuple(value) + elif isinstance(value, str): + self._rgba = parse_str(value) + elif isinstance(value, Color): + self._rgba = value._rgba + value = value._original + else: + raise PydanticCustomError( + 'color_error', 'value is not a valid color: value must be a tuple, list or string' + ) + + # if we've got here value must be a valid color + self._original = value + + @classmethod + def __get_pydantic_json_schema__( + cls, core_schema: core_schema.CoreSchema, handler: _GetJsonSchemaHandler + ) -> JsonSchemaValue: + field_schema = {} + field_schema.update(type='string', format='color') + return field_schema + + def original(self) -> ColorType: + """Original value passed to `Color`.""" + return self._original + + def as_named(self, *, fallback: bool = False) -> str: + """Returns the name of the color if it can be found in `COLORS_BY_VALUE` dictionary, + otherwise returns the hexadecimal representation of the color or raises `ValueError`. + + Args: + fallback: If True, falls back to returning the hexadecimal representation of + the color instead of raising a ValueError when no named color is found. + + Returns: + The name of the color, or the hexadecimal representation of the color. + + Raises: + ValueError: When no named color is found and fallback is `False`. + """ + if self._rgba.alpha is None: + rgb = cast(tuple[int, int, int], self.as_rgb_tuple()) + try: + return COLORS_BY_VALUE[rgb] + except KeyError as e: + if fallback: + return self.as_hex() + else: + raise ValueError('no named color found, use fallback=True, as_hex() or as_rgb()') from e + else: + return self.as_hex() + + def as_hex(self) -> str: + """Returns the hexadecimal representation of the color. + + Hex string representing the color can be 3, 4, 6, or 8 characters depending on whether the string + a "short" representation of the color is possible and whether there's an alpha channel. + + Returns: + The hexadecimal representation of the color. + """ + values = [float_to_255(c) for c in self._rgba[:3]] + if self._rgba.alpha is not None: + values.append(float_to_255(self._rgba.alpha)) + + as_hex = ''.join(f'{v:02x}' for v in values) + if all(c in repeat_colors for c in values): + as_hex = ''.join(as_hex[c] for c in range(0, len(as_hex), 2)) + return '#' + as_hex + + def as_rgb(self) -> str: + """Color as an `rgb(, , )` or `rgba(, , , )` string.""" + if self._rgba.alpha is None: + return f'rgb({float_to_255(self._rgba.r)}, {float_to_255(self._rgba.g)}, {float_to_255(self._rgba.b)})' + else: + return ( + f'rgba({float_to_255(self._rgba.r)}, {float_to_255(self._rgba.g)}, {float_to_255(self._rgba.b)}, ' + f'{round(self._alpha_float(), 2)})' + ) + + def as_rgb_tuple(self, *, alpha: Optional[bool] = None) -> ColorTuple: + """Returns the color as an RGB or RGBA tuple. + + Args: + alpha: Whether to include the alpha channel. There are three options for this input: + + - `None` (default): Include alpha only if it's set. (e.g. not `None`) + - `True`: Always include alpha. + - `False`: Always omit alpha. + + Returns: + A tuple that contains the values of the red, green, and blue channels in the range 0 to 255. + If alpha is included, it is in the range 0 to 1. + """ + r, g, b = (float_to_255(c) for c in self._rgba[:3]) + if alpha is None: + if self._rgba.alpha is None: + return r, g, b + else: + return r, g, b, self._alpha_float() + elif alpha: + return r, g, b, self._alpha_float() + else: + # alpha is False + return r, g, b + + def as_hsl(self) -> str: + """Color as an `hsl(, , )` or `hsl(, , , )` string.""" + if self._rgba.alpha is None: + h, s, li = self.as_hsl_tuple(alpha=False) # type: ignore + return f'hsl({h * 360:0.0f}, {s:0.0%}, {li:0.0%})' + else: + h, s, li, a = self.as_hsl_tuple(alpha=True) # type: ignore + return f'hsl({h * 360:0.0f}, {s:0.0%}, {li:0.0%}, {round(a, 2)})' + + def as_hsl_tuple(self, *, alpha: Optional[bool] = None) -> HslColorTuple: + """Returns the color as an HSL or HSLA tuple. + + Args: + alpha: Whether to include the alpha channel. + + - `None` (default): Include the alpha channel only if it's set (e.g. not `None`). + - `True`: Always include alpha. + - `False`: Always omit alpha. + + Returns: + The color as a tuple of hue, saturation, lightness, and alpha (if included). + All elements are in the range 0 to 1. + + Note: + This is HSL as used in HTML and most other places, not HLS as used in Python's `colorsys`. + """ + h, l, s = rgb_to_hls(self._rgba.r, self._rgba.g, self._rgba.b) # noqa: E741 + if alpha is None: + if self._rgba.alpha is None: + return h, s, l + else: + return h, s, l, self._alpha_float() + if alpha: + return h, s, l, self._alpha_float() + else: + # alpha is False + return h, s, l + + def _alpha_float(self) -> float: + return 1 if self._rgba.alpha is None else self._rgba.alpha + + @classmethod + def __get_pydantic_core_schema__( + cls, source: type[Any], handler: Callable[[Any], CoreSchema] + ) -> core_schema.CoreSchema: + return core_schema.with_info_plain_validator_function( + cls._validate, serialization=core_schema.to_string_ser_schema() + ) + + @classmethod + def _validate(cls, __input_value: Any, _: Any) -> 'Color': + return cls(__input_value) + + def __str__(self) -> str: + return self.as_named(fallback=True) + + def __repr_args__(self) -> '_repr.ReprArgs': + return [(None, self.as_named(fallback=True))] + [('rgb', self.as_rgb_tuple())] + + def __eq__(self, other: Any) -> bool: + return isinstance(other, Color) and self.as_rgb_tuple() == other.as_rgb_tuple() + + def __hash__(self) -> int: + return hash(self.as_rgb_tuple()) + + +def parse_tuple(value: tuple[Any, ...]) -> RGBA: + """Parse a tuple or list to get RGBA values. + + Args: + value: A tuple or list. + + Returns: + An `RGBA` tuple parsed from the input tuple. + + Raises: + PydanticCustomError: If tuple is not valid. + """ + if len(value) == 3: + r, g, b = (parse_color_value(v) for v in value) + return RGBA(r, g, b, None) + elif len(value) == 4: + r, g, b = (parse_color_value(v) for v in value[:3]) + return RGBA(r, g, b, parse_float_alpha(value[3])) + else: + raise PydanticCustomError('color_error', 'value is not a valid color: tuples must have length 3 or 4') + + +def parse_str(value: str) -> RGBA: + """Parse a string representing a color to an RGBA tuple. + + Possible formats for the input string include: + + * named color, see `COLORS_BY_NAME` + * hex short eg. `fff` (prefix can be `#`, `0x` or nothing) + * hex long eg. `ffffff` (prefix can be `#`, `0x` or nothing) + * `rgb(, , )` + * `rgba(, , , )` + + Args: + value: A string representing a color. + + Returns: + An `RGBA` tuple parsed from the input string. + + Raises: + ValueError: If the input string cannot be parsed to an RGBA tuple. + """ + value_lower = value.lower() + try: + r, g, b = COLORS_BY_NAME[value_lower] + except KeyError: + pass + else: + return ints_to_rgba(r, g, b, None) + + m = re.fullmatch(r_hex_short, value_lower) + if m: + *rgb, a = m.groups() + r, g, b = (int(v * 2, 16) for v in rgb) + if a: + alpha: Optional[float] = int(a * 2, 16) / 255 + else: + alpha = None + return ints_to_rgba(r, g, b, alpha) + + m = re.fullmatch(r_hex_long, value_lower) + if m: + *rgb, a = m.groups() + r, g, b = (int(v, 16) for v in rgb) + if a: + alpha = int(a, 16) / 255 + else: + alpha = None + return ints_to_rgba(r, g, b, alpha) + + m = re.fullmatch(r_rgb, value_lower) or re.fullmatch(r_rgb_v4_style, value_lower) + if m: + return ints_to_rgba(*m.groups()) # type: ignore + + m = re.fullmatch(r_hsl, value_lower) or re.fullmatch(r_hsl_v4_style, value_lower) + if m: + return parse_hsl(*m.groups()) # type: ignore + + raise PydanticCustomError('color_error', 'value is not a valid color: string not recognised as a valid color') + + +def ints_to_rgba(r: Union[int, str], g: Union[int, str], b: Union[int, str], alpha: Optional[float] = None) -> RGBA: + """Converts integer or string values for RGB color and an optional alpha value to an `RGBA` object. + + Args: + r: An integer or string representing the red color value. + g: An integer or string representing the green color value. + b: An integer or string representing the blue color value. + alpha: A float representing the alpha value. Defaults to None. + + Returns: + An instance of the `RGBA` class with the corresponding color and alpha values. + """ + return RGBA(parse_color_value(r), parse_color_value(g), parse_color_value(b), parse_float_alpha(alpha)) + + +def parse_color_value(value: Union[int, str], max_val: int = 255) -> float: + """Parse the color value provided and return a number between 0 and 1. + + Args: + value: An integer or string color value. + max_val: Maximum range value. Defaults to 255. + + Raises: + PydanticCustomError: If the value is not a valid color. + + Returns: + A number between 0 and 1. + """ + try: + color = float(value) + except ValueError: + raise PydanticCustomError('color_error', 'value is not a valid color: color values must be a valid number') + if 0 <= color <= max_val: + return color / max_val + else: + raise PydanticCustomError( + 'color_error', + 'value is not a valid color: color values must be in the range 0 to {max_val}', + {'max_val': max_val}, + ) + + +def parse_float_alpha(value: Union[None, str, float, int]) -> Optional[float]: + """Parse an alpha value checking it's a valid float in the range 0 to 1. + + Args: + value: The input value to parse. + + Returns: + The parsed value as a float, or `None` if the value was None or equal 1. + + Raises: + PydanticCustomError: If the input value cannot be successfully parsed as a float in the expected range. + """ + if value is None: + return None + try: + if isinstance(value, str) and value.endswith('%'): + alpha = float(value[:-1]) / 100 + else: + alpha = float(value) + except ValueError: + raise PydanticCustomError('color_error', 'value is not a valid color: alpha values must be a valid float') + + if math.isclose(alpha, 1): + return None + elif 0 <= alpha <= 1: + return alpha + else: + raise PydanticCustomError('color_error', 'value is not a valid color: alpha values must be in the range 0 to 1') + + +def parse_hsl(h: str, h_units: str, sat: str, light: str, alpha: Optional[float] = None) -> RGBA: + """Parse raw hue, saturation, lightness, and alpha values and convert to RGBA. + + Args: + h: The hue value. + h_units: The unit for hue value. + sat: The saturation value. + light: The lightness value. + alpha: Alpha value. + + Returns: + An instance of `RGBA`. + """ + s_value, l_value = parse_color_value(sat, 100), parse_color_value(light, 100) + + h_value = float(h) + if h_units in {None, 'deg'}: + h_value = h_value % 360 / 360 + elif h_units == 'rad': + h_value = h_value % rads / rads + else: + # turns + h_value = h_value % 1 + + r, g, b = hls_to_rgb(h_value, l_value, s_value) + return RGBA(r, g, b, parse_float_alpha(alpha)) + + +def float_to_255(c: float) -> int: + """Converts a float value between 0 and 1 (inclusive) to an integer between 0 and 255 (inclusive). + + Args: + c: The float value to be converted. Must be between 0 and 1 (inclusive). + + Returns: + The integer equivalent of the given float value rounded to the nearest whole number. + + Raises: + ValueError: If the given float value is outside the acceptable range of 0 to 1 (inclusive). + """ + return int(round(c * 255)) + + +COLORS_BY_NAME = { + 'aliceblue': (240, 248, 255), + 'antiquewhite': (250, 235, 215), + 'aqua': (0, 255, 255), + 'aquamarine': (127, 255, 212), + 'azure': (240, 255, 255), + 'beige': (245, 245, 220), + 'bisque': (255, 228, 196), + 'black': (0, 0, 0), + 'blanchedalmond': (255, 235, 205), + 'blue': (0, 0, 255), + 'blueviolet': (138, 43, 226), + 'brown': (165, 42, 42), + 'burlywood': (222, 184, 135), + 'cadetblue': (95, 158, 160), + 'chartreuse': (127, 255, 0), + 'chocolate': (210, 105, 30), + 'coral': (255, 127, 80), + 'cornflowerblue': (100, 149, 237), + 'cornsilk': (255, 248, 220), + 'crimson': (220, 20, 60), + 'cyan': (0, 255, 255), + 'darkblue': (0, 0, 139), + 'darkcyan': (0, 139, 139), + 'darkgoldenrod': (184, 134, 11), + 'darkgray': (169, 169, 169), + 'darkgreen': (0, 100, 0), + 'darkgrey': (169, 169, 169), + 'darkkhaki': (189, 183, 107), + 'darkmagenta': (139, 0, 139), + 'darkolivegreen': (85, 107, 47), + 'darkorange': (255, 140, 0), + 'darkorchid': (153, 50, 204), + 'darkred': (139, 0, 0), + 'darksalmon': (233, 150, 122), + 'darkseagreen': (143, 188, 143), + 'darkslateblue': (72, 61, 139), + 'darkslategray': (47, 79, 79), + 'darkslategrey': (47, 79, 79), + 'darkturquoise': (0, 206, 209), + 'darkviolet': (148, 0, 211), + 'deeppink': (255, 20, 147), + 'deepskyblue': (0, 191, 255), + 'dimgray': (105, 105, 105), + 'dimgrey': (105, 105, 105), + 'dodgerblue': (30, 144, 255), + 'firebrick': (178, 34, 34), + 'floralwhite': (255, 250, 240), + 'forestgreen': (34, 139, 34), + 'fuchsia': (255, 0, 255), + 'gainsboro': (220, 220, 220), + 'ghostwhite': (248, 248, 255), + 'gold': (255, 215, 0), + 'goldenrod': (218, 165, 32), + 'gray': (128, 128, 128), + 'green': (0, 128, 0), + 'greenyellow': (173, 255, 47), + 'grey': (128, 128, 128), + 'honeydew': (240, 255, 240), + 'hotpink': (255, 105, 180), + 'indianred': (205, 92, 92), + 'indigo': (75, 0, 130), + 'ivory': (255, 255, 240), + 'khaki': (240, 230, 140), + 'lavender': (230, 230, 250), + 'lavenderblush': (255, 240, 245), + 'lawngreen': (124, 252, 0), + 'lemonchiffon': (255, 250, 205), + 'lightblue': (173, 216, 230), + 'lightcoral': (240, 128, 128), + 'lightcyan': (224, 255, 255), + 'lightgoldenrodyellow': (250, 250, 210), + 'lightgray': (211, 211, 211), + 'lightgreen': (144, 238, 144), + 'lightgrey': (211, 211, 211), + 'lightpink': (255, 182, 193), + 'lightsalmon': (255, 160, 122), + 'lightseagreen': (32, 178, 170), + 'lightskyblue': (135, 206, 250), + 'lightslategray': (119, 136, 153), + 'lightslategrey': (119, 136, 153), + 'lightsteelblue': (176, 196, 222), + 'lightyellow': (255, 255, 224), + 'lime': (0, 255, 0), + 'limegreen': (50, 205, 50), + 'linen': (250, 240, 230), + 'magenta': (255, 0, 255), + 'maroon': (128, 0, 0), + 'mediumaquamarine': (102, 205, 170), + 'mediumblue': (0, 0, 205), + 'mediumorchid': (186, 85, 211), + 'mediumpurple': (147, 112, 219), + 'mediumseagreen': (60, 179, 113), + 'mediumslateblue': (123, 104, 238), + 'mediumspringgreen': (0, 250, 154), + 'mediumturquoise': (72, 209, 204), + 'mediumvioletred': (199, 21, 133), + 'midnightblue': (25, 25, 112), + 'mintcream': (245, 255, 250), + 'mistyrose': (255, 228, 225), + 'moccasin': (255, 228, 181), + 'navajowhite': (255, 222, 173), + 'navy': (0, 0, 128), + 'oldlace': (253, 245, 230), + 'olive': (128, 128, 0), + 'olivedrab': (107, 142, 35), + 'orange': (255, 165, 0), + 'orangered': (255, 69, 0), + 'orchid': (218, 112, 214), + 'palegoldenrod': (238, 232, 170), + 'palegreen': (152, 251, 152), + 'paleturquoise': (175, 238, 238), + 'palevioletred': (219, 112, 147), + 'papayawhip': (255, 239, 213), + 'peachpuff': (255, 218, 185), + 'peru': (205, 133, 63), + 'pink': (255, 192, 203), + 'plum': (221, 160, 221), + 'powderblue': (176, 224, 230), + 'purple': (128, 0, 128), + 'red': (255, 0, 0), + 'rosybrown': (188, 143, 143), + 'royalblue': (65, 105, 225), + 'saddlebrown': (139, 69, 19), + 'salmon': (250, 128, 114), + 'sandybrown': (244, 164, 96), + 'seagreen': (46, 139, 87), + 'seashell': (255, 245, 238), + 'sienna': (160, 82, 45), + 'silver': (192, 192, 192), + 'skyblue': (135, 206, 235), + 'slateblue': (106, 90, 205), + 'slategray': (112, 128, 144), + 'slategrey': (112, 128, 144), + 'snow': (255, 250, 250), + 'springgreen': (0, 255, 127), + 'steelblue': (70, 130, 180), + 'tan': (210, 180, 140), + 'teal': (0, 128, 128), + 'thistle': (216, 191, 216), + 'tomato': (255, 99, 71), + 'turquoise': (64, 224, 208), + 'violet': (238, 130, 238), + 'wheat': (245, 222, 179), + 'white': (255, 255, 255), + 'whitesmoke': (245, 245, 245), + 'yellow': (255, 255, 0), + 'yellowgreen': (154, 205, 50), +} + +COLORS_BY_VALUE = {v: k for k, v in COLORS_BY_NAME.items()} diff --git a/.venv/lib/python3.12/site-packages/pydantic/config.py b/.venv/lib/python3.12/site-packages/pydantic/config.py new file mode 100644 index 0000000000000000000000000000000000000000..ba10d1673c1428ae66fb486dd0de532ab8cffba8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/config.py @@ -0,0 +1,1294 @@ +"""Configuration for Pydantic models.""" + +from __future__ import annotations as _annotations + +import warnings +from re import Pattern +from typing import TYPE_CHECKING, Any, Callable, Literal, TypeVar, Union, cast, overload + +from typing_extensions import TypeAlias, TypedDict, Unpack, deprecated + +from ._migration import getattr_migration +from .aliases import AliasGenerator +from .errors import PydanticUserError +from .warnings import PydanticDeprecatedSince211 + +if TYPE_CHECKING: + from ._internal._generate_schema import GenerateSchema as _GenerateSchema + from .fields import ComputedFieldInfo, FieldInfo + +__all__ = ('ConfigDict', 'with_config') + + +JsonValue: TypeAlias = Union[int, float, str, bool, None, list['JsonValue'], 'JsonDict'] +JsonDict: TypeAlias = dict[str, JsonValue] + +JsonEncoder = Callable[[Any], Any] + +JsonSchemaExtraCallable: TypeAlias = Union[ + Callable[[JsonDict], None], + Callable[[JsonDict, type[Any]], None], +] + +ExtraValues = Literal['allow', 'ignore', 'forbid'] + + +class ConfigDict(TypedDict, total=False): + """A TypedDict for configuring Pydantic behaviour.""" + + title: str | None + """The title for the generated JSON schema, defaults to the model's name""" + + model_title_generator: Callable[[type], str] | None + """A callable that takes a model class and returns the title for it. Defaults to `None`.""" + + field_title_generator: Callable[[str, FieldInfo | ComputedFieldInfo], str] | None + """A callable that takes a field's name and info and returns title for it. Defaults to `None`.""" + + str_to_lower: bool + """Whether to convert all characters to lowercase for str types. Defaults to `False`.""" + + str_to_upper: bool + """Whether to convert all characters to uppercase for str types. Defaults to `False`.""" + + str_strip_whitespace: bool + """Whether to strip leading and trailing whitespace for str types.""" + + str_min_length: int + """The minimum length for str types. Defaults to `None`.""" + + str_max_length: int | None + """The maximum length for str types. Defaults to `None`.""" + + extra: ExtraValues | None + ''' + Whether to ignore, allow, or forbid extra data during model initialization. Defaults to `'ignore'`. + + Three configuration values are available: + + - `'ignore'`: Providing extra data is ignored (the default): + ```python + from pydantic import BaseModel, ConfigDict + + class User(BaseModel): + model_config = ConfigDict(extra='ignore') # (1)! + + name: str + + user = User(name='John Doe', age=20) # (2)! + print(user) + #> name='John Doe' + ``` + + 1. This is the default behaviour. + 2. The `age` argument is ignored. + + - `'forbid'`: Providing extra data is not permitted, and a [`ValidationError`][pydantic_core.ValidationError] + will be raised if this is the case: + ```python + from pydantic import BaseModel, ConfigDict, ValidationError + + + class Model(BaseModel): + x: int + + model_config = ConfigDict(extra='forbid') + + + try: + Model(x=1, y='a') + except ValidationError as exc: + print(exc) + """ + 1 validation error for Model + y + Extra inputs are not permitted [type=extra_forbidden, input_value='a', input_type=str] + """ + ``` + + - `'allow'`: Providing extra data is allowed and stored in the `__pydantic_extra__` dictionary attribute: + ```python + from pydantic import BaseModel, ConfigDict + + + class Model(BaseModel): + x: int + + model_config = ConfigDict(extra='allow') + + + m = Model(x=1, y='a') + assert m.__pydantic_extra__ == {'y': 'a'} + ``` + By default, no validation will be applied to these extra items, but you can set a type for the values by overriding + the type annotation for `__pydantic_extra__`: + ```python + from pydantic import BaseModel, ConfigDict, Field, ValidationError + + + class Model(BaseModel): + __pydantic_extra__: dict[str, int] = Field(init=False) # (1)! + + x: int + + model_config = ConfigDict(extra='allow') + + + try: + Model(x=1, y='a') + except ValidationError as exc: + print(exc) + """ + 1 validation error for Model + y + Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str] + """ + + m = Model(x=1, y='2') + assert m.x == 1 + assert m.y == 2 + assert m.model_dump() == {'x': 1, 'y': 2} + assert m.__pydantic_extra__ == {'y': 2} + ``` + + 1. The `= Field(init=False)` does not have any effect at runtime, but prevents the `__pydantic_extra__` field from + being included as a parameter to the model's `__init__` method by type checkers. + + As well as specifying an `extra` configuration value on the model, you can also provide it as an argument to the validation methods. + This will override any `extra` configuration value set on the model: + ```python + from pydantic import BaseModel, ConfigDict, ValidationError + + class Model(BaseModel): + x: int + model_config = ConfigDict(extra="allow") + + try: + # Override model config and forbid extra fields just this time + Model.model_validate({"x": 1, "y": 2}, extra="forbid") + except ValidationError as exc: + print(exc) + """ + 1 validation error for Model + y + Extra inputs are not permitted [type=extra_forbidden, input_value=2, input_type=int] + """ + ``` + ''' + + frozen: bool + """ + Whether models are faux-immutable, i.e. whether `__setattr__` is allowed, and also generates + a `__hash__()` method for the model. This makes instances of the model potentially hashable if all the + attributes are hashable. Defaults to `False`. + + Note: + On V1, the inverse of this setting was called `allow_mutation`, and was `True` by default. + """ + + populate_by_name: bool + """ + Whether an aliased field may be populated by its name as given by the model + attribute, as well as the alias. Defaults to `False`. + + !!! warning + `populate_by_name` usage is not recommended in v2.11+ and will be deprecated in v3. + Instead, you should use the [`validate_by_name`][pydantic.config.ConfigDict.validate_by_name] configuration setting. + + When `validate_by_name=True` and `validate_by_alias=True`, this is strictly equivalent to the + previous behavior of `populate_by_name=True`. + + In v2.11, we also introduced a [`validate_by_alias`][pydantic.config.ConfigDict.validate_by_alias] setting that introduces more fine grained + control for validation behavior. + + Here's how you might go about using the new settings to achieve the same behavior: + + ```python + from pydantic import BaseModel, ConfigDict, Field + + class Model(BaseModel): + model_config = ConfigDict(validate_by_name=True, validate_by_alias=True) + + my_field: str = Field(alias='my_alias') # (1)! + + m = Model(my_alias='foo') # (2)! + print(m) + #> my_field='foo' + + m = Model(my_field='foo') # (3)! + print(m) + #> my_field='foo' + ``` + + 1. The field `'my_field'` has an alias `'my_alias'`. + 2. The model is populated by the alias `'my_alias'`. + 3. The model is populated by the attribute name `'my_field'`. + """ + + use_enum_values: bool + """ + Whether to populate models with the `value` property of enums, rather than the raw enum. + This may be useful if you want to serialize `model.model_dump()` later. Defaults to `False`. + + !!! note + If you have an `Optional[Enum]` value that you set a default for, you need to use `validate_default=True` + for said Field to ensure that the `use_enum_values` flag takes effect on the default, as extracting an + enum's value occurs during validation, not serialization. + + ```python + from enum import Enum + from typing import Optional + + from pydantic import BaseModel, ConfigDict, Field + + class SomeEnum(Enum): + FOO = 'foo' + BAR = 'bar' + BAZ = 'baz' + + class SomeModel(BaseModel): + model_config = ConfigDict(use_enum_values=True) + + some_enum: SomeEnum + another_enum: Optional[SomeEnum] = Field( + default=SomeEnum.FOO, validate_default=True + ) + + model1 = SomeModel(some_enum=SomeEnum.BAR) + print(model1.model_dump()) + #> {'some_enum': 'bar', 'another_enum': 'foo'} + + model2 = SomeModel(some_enum=SomeEnum.BAR, another_enum=SomeEnum.BAZ) + print(model2.model_dump()) + #> {'some_enum': 'bar', 'another_enum': 'baz'} + ``` + """ + + validate_assignment: bool + """ + Whether to validate the data when the model is changed. Defaults to `False`. + + The default behavior of Pydantic is to validate the data when the model is created. + + In case the user changes the data after the model is created, the model is _not_ revalidated. + + ```python + from pydantic import BaseModel + + class User(BaseModel): + name: str + + user = User(name='John Doe') # (1)! + print(user) + #> name='John Doe' + user.name = 123 # (1)! + print(user) + #> name=123 + ``` + + 1. The validation happens only when the model is created. + 2. The validation does not happen when the data is changed. + + In case you want to revalidate the model when the data is changed, you can use `validate_assignment=True`: + + ```python + from pydantic import BaseModel, ValidationError + + class User(BaseModel, validate_assignment=True): # (1)! + name: str + + user = User(name='John Doe') # (2)! + print(user) + #> name='John Doe' + try: + user.name = 123 # (3)! + except ValidationError as e: + print(e) + ''' + 1 validation error for User + name + Input should be a valid string [type=string_type, input_value=123, input_type=int] + ''' + ``` + + 1. You can either use class keyword arguments, or `model_config` to set `validate_assignment=True`. + 2. The validation happens when the model is created. + 3. The validation _also_ happens when the data is changed. + """ + + arbitrary_types_allowed: bool + """ + Whether arbitrary types are allowed for field types. Defaults to `False`. + + ```python + from pydantic import BaseModel, ConfigDict, ValidationError + + # This is not a pydantic model, it's an arbitrary class + class Pet: + def __init__(self, name: str): + self.name = name + + class Model(BaseModel): + model_config = ConfigDict(arbitrary_types_allowed=True) + + pet: Pet + owner: str + + pet = Pet(name='Hedwig') + # A simple check of instance type is used to validate the data + model = Model(owner='Harry', pet=pet) + print(model) + #> pet=<__main__.Pet object at 0x0123456789ab> owner='Harry' + print(model.pet) + #> <__main__.Pet object at 0x0123456789ab> + print(model.pet.name) + #> Hedwig + print(type(model.pet)) + #> + try: + # If the value is not an instance of the type, it's invalid + Model(owner='Harry', pet='Hedwig') + except ValidationError as e: + print(e) + ''' + 1 validation error for Model + pet + Input should be an instance of Pet [type=is_instance_of, input_value='Hedwig', input_type=str] + ''' + + # Nothing in the instance of the arbitrary type is checked + # Here name probably should have been a str, but it's not validated + pet2 = Pet(name=42) + model2 = Model(owner='Harry', pet=pet2) + print(model2) + #> pet=<__main__.Pet object at 0x0123456789ab> owner='Harry' + print(model2.pet) + #> <__main__.Pet object at 0x0123456789ab> + print(model2.pet.name) + #> 42 + print(type(model2.pet)) + #> + ``` + """ + + from_attributes: bool + """ + Whether to build models and look up discriminators of tagged unions using python object attributes. + """ + + loc_by_alias: bool + """Whether to use the actual key provided in the data (e.g. alias) for error `loc`s rather than the field's name. Defaults to `True`.""" + + alias_generator: Callable[[str], str] | AliasGenerator | None + """ + A callable that takes a field name and returns an alias for it + or an instance of [`AliasGenerator`][pydantic.aliases.AliasGenerator]. Defaults to `None`. + + When using a callable, the alias generator is used for both validation and serialization. + If you want to use different alias generators for validation and serialization, you can use + [`AliasGenerator`][pydantic.aliases.AliasGenerator] instead. + + If data source field names do not match your code style (e.g. CamelCase fields), + you can automatically generate aliases using `alias_generator`. Here's an example with + a basic callable: + + ```python + from pydantic import BaseModel, ConfigDict + from pydantic.alias_generators import to_pascal + + class Voice(BaseModel): + model_config = ConfigDict(alias_generator=to_pascal) + + name: str + language_code: str + + voice = Voice(Name='Filiz', LanguageCode='tr-TR') + print(voice.language_code) + #> tr-TR + print(voice.model_dump(by_alias=True)) + #> {'Name': 'Filiz', 'LanguageCode': 'tr-TR'} + ``` + + If you want to use different alias generators for validation and serialization, you can use + [`AliasGenerator`][pydantic.aliases.AliasGenerator]. + + ```python + from pydantic import AliasGenerator, BaseModel, ConfigDict + from pydantic.alias_generators import to_camel, to_pascal + + class Athlete(BaseModel): + first_name: str + last_name: str + sport: str + + model_config = ConfigDict( + alias_generator=AliasGenerator( + validation_alias=to_camel, + serialization_alias=to_pascal, + ) + ) + + athlete = Athlete(firstName='John', lastName='Doe', sport='track') + print(athlete.model_dump(by_alias=True)) + #> {'FirstName': 'John', 'LastName': 'Doe', 'Sport': 'track'} + ``` + + Note: + Pydantic offers three built-in alias generators: [`to_pascal`][pydantic.alias_generators.to_pascal], + [`to_camel`][pydantic.alias_generators.to_camel], and [`to_snake`][pydantic.alias_generators.to_snake]. + """ + + ignored_types: tuple[type, ...] + """A tuple of types that may occur as values of class attributes without annotations. This is + typically used for custom descriptors (classes that behave like `property`). If an attribute is set on a + class without an annotation and has a type that is not in this tuple (or otherwise recognized by + _pydantic_), an error will be raised. Defaults to `()`. + """ + + allow_inf_nan: bool + """Whether to allow infinity (`+inf` an `-inf`) and NaN values to float and decimal fields. Defaults to `True`.""" + + json_schema_extra: JsonDict | JsonSchemaExtraCallable | None + """A dict or callable to provide extra JSON schema properties. Defaults to `None`.""" + + json_encoders: dict[type[object], JsonEncoder] | None + """ + A `dict` of custom JSON encoders for specific types. Defaults to `None`. + + !!! warning "Deprecated" + This config option is a carryover from v1. + We originally planned to remove it in v2 but didn't have a 1:1 replacement so we are keeping it for now. + It is still deprecated and will likely be removed in the future. + """ + + # new in V2 + strict: bool + """ + _(new in V2)_ If `True`, strict validation is applied to all fields on the model. + + By default, Pydantic attempts to coerce values to the correct type, when possible. + + There are situations in which you may want to disable this behavior, and instead raise an error if a value's type + does not match the field's type annotation. + + To configure strict mode for all fields on a model, you can set `strict=True` on the model. + + ```python + from pydantic import BaseModel, ConfigDict + + class Model(BaseModel): + model_config = ConfigDict(strict=True) + + name: str + age: int + ``` + + See [Strict Mode](../concepts/strict_mode.md) for more details. + + See the [Conversion Table](../concepts/conversion_table.md) for more details on how Pydantic converts data in both + strict and lax modes. + """ + # whether instances of models and dataclasses (including subclass instances) should re-validate, default 'never' + revalidate_instances: Literal['always', 'never', 'subclass-instances'] + """ + When and how to revalidate models and dataclasses during validation. Accepts the string + values of `'never'`, `'always'` and `'subclass-instances'`. Defaults to `'never'`. + + - `'never'` will not revalidate models and dataclasses during validation + - `'always'` will revalidate models and dataclasses during validation + - `'subclass-instances'` will revalidate models and dataclasses during validation if the instance is a + subclass of the model or dataclass + + By default, model and dataclass instances are not revalidated during validation. + + ```python + from pydantic import BaseModel + + class User(BaseModel, revalidate_instances='never'): # (1)! + hobbies: list[str] + + class SubUser(User): + sins: list[str] + + class Transaction(BaseModel): + user: User + + my_user = User(hobbies=['reading']) + t = Transaction(user=my_user) + print(t) + #> user=User(hobbies=['reading']) + + my_user.hobbies = [1] # (2)! + t = Transaction(user=my_user) # (3)! + print(t) + #> user=User(hobbies=[1]) + + my_sub_user = SubUser(hobbies=['scuba diving'], sins=['lying']) + t = Transaction(user=my_sub_user) + print(t) + #> user=SubUser(hobbies=['scuba diving'], sins=['lying']) + ``` + + 1. `revalidate_instances` is set to `'never'` by **default. + 2. The assignment is not validated, unless you set `validate_assignment` to `True` in the model's config. + 3. Since `revalidate_instances` is set to `never`, this is not revalidated. + + If you want to revalidate instances during validation, you can set `revalidate_instances` to `'always'` + in the model's config. + + ```python + from pydantic import BaseModel, ValidationError + + class User(BaseModel, revalidate_instances='always'): # (1)! + hobbies: list[str] + + class SubUser(User): + sins: list[str] + + class Transaction(BaseModel): + user: User + + my_user = User(hobbies=['reading']) + t = Transaction(user=my_user) + print(t) + #> user=User(hobbies=['reading']) + + my_user.hobbies = [1] + try: + t = Transaction(user=my_user) # (2)! + except ValidationError as e: + print(e) + ''' + 1 validation error for Transaction + user.hobbies.0 + Input should be a valid string [type=string_type, input_value=1, input_type=int] + ''' + + my_sub_user = SubUser(hobbies=['scuba diving'], sins=['lying']) + t = Transaction(user=my_sub_user) + print(t) # (3)! + #> user=User(hobbies=['scuba diving']) + ``` + + 1. `revalidate_instances` is set to `'always'`. + 2. The model is revalidated, since `revalidate_instances` is set to `'always'`. + 3. Using `'never'` we would have gotten `user=SubUser(hobbies=['scuba diving'], sins=['lying'])`. + + It's also possible to set `revalidate_instances` to `'subclass-instances'` to only revalidate instances + of subclasses of the model. + + ```python + from pydantic import BaseModel + + class User(BaseModel, revalidate_instances='subclass-instances'): # (1)! + hobbies: list[str] + + class SubUser(User): + sins: list[str] + + class Transaction(BaseModel): + user: User + + my_user = User(hobbies=['reading']) + t = Transaction(user=my_user) + print(t) + #> user=User(hobbies=['reading']) + + my_user.hobbies = [1] + t = Transaction(user=my_user) # (2)! + print(t) + #> user=User(hobbies=[1]) + + my_sub_user = SubUser(hobbies=['scuba diving'], sins=['lying']) + t = Transaction(user=my_sub_user) + print(t) # (3)! + #> user=User(hobbies=['scuba diving']) + ``` + + 1. `revalidate_instances` is set to `'subclass-instances'`. + 2. This is not revalidated, since `my_user` is not a subclass of `User`. + 3. Using `'never'` we would have gotten `user=SubUser(hobbies=['scuba diving'], sins=['lying'])`. + """ + + ser_json_timedelta: Literal['iso8601', 'float'] + """ + The format of JSON serialized timedeltas. Accepts the string values of `'iso8601'` and + `'float'`. Defaults to `'iso8601'`. + + - `'iso8601'` will serialize timedeltas to [ISO 8601 text format](https://en.wikipedia.org/wiki/ISO_8601#Durations). + - `'float'` will serialize timedeltas to the total number of seconds. + + !!! warning + Starting in v2.12, it is recommended to use the [`ser_json_temporal`][pydantic.config.ConfigDict.ser_json_temporal] + setting instead of `ser_json_timedelta`. This setting will be deprecated in v3. + """ + + ser_json_temporal: Literal['iso8601', 'seconds', 'milliseconds'] + """ + The format of JSON serialized temporal types from the [`datetime`][] module. This includes: + + - [`datetime.datetime`][] + - [`datetime.date`][] + - [`datetime.time`][] + - [`datetime.timedelta`][] + + Can be one of: + + - `'iso8601'` will serialize date-like types to [ISO 8601 text format](https://en.wikipedia.org/wiki/ISO_8601#Durations). + - `'milliseconds'` will serialize date-like types to a floating point number of milliseconds since the epoch. + - `'seconds'` will serialize date-like types to a floating point number of seconds since the epoch. + + Defaults to `'iso8601'`. + + !!! note + This setting was introduced in v2.12. It overlaps with the [`ser_json_timedelta`][pydantic.config.ConfigDict.ser_json_timedelta] + setting which will be deprecated in v3. It also adds more configurability for + the other temporal types. + """ + + val_temporal_unit: Literal['seconds', 'milliseconds', 'infer'] + """ + The unit to assume for validating numeric input for datetime-like types ([`datetime.datetime`][] and [`datetime.date`][]). Can be one of: + + - `'seconds'` will validate date or time numeric inputs as seconds since the [epoch]. + - `'milliseconds'` will validate date or time numeric inputs as milliseconds since the [epoch]. + - `'infer'` will infer the unit from the string numeric input on unix time as: + + * seconds since the [epoch] if $-2^{10} <= v <= 2^{10}$ + * milliseconds since the [epoch] (if $v < -2^{10}$ or $v > 2^{10}$). + + Defaults to `'infer'`. + + [epoch]: https://en.wikipedia.org/wiki/Unix_time + """ + + ser_json_bytes: Literal['utf8', 'base64', 'hex'] + """ + The encoding of JSON serialized bytes. Defaults to `'utf8'`. + Set equal to `val_json_bytes` to get back an equal value after serialization round trip. + + - `'utf8'` will serialize bytes to UTF-8 strings. + - `'base64'` will serialize bytes to URL safe base64 strings. + - `'hex'` will serialize bytes to hexadecimal strings. + """ + + val_json_bytes: Literal['utf8', 'base64', 'hex'] + """ + The encoding of JSON serialized bytes to decode. Defaults to `'utf8'`. + Set equal to `ser_json_bytes` to get back an equal value after serialization round trip. + + - `'utf8'` will deserialize UTF-8 strings to bytes. + - `'base64'` will deserialize URL safe base64 strings to bytes. + - `'hex'` will deserialize hexadecimal strings to bytes. + """ + + ser_json_inf_nan: Literal['null', 'constants', 'strings'] + """ + The encoding of JSON serialized infinity and NaN float values. Defaults to `'null'`. + + - `'null'` will serialize infinity and NaN values as `null`. + - `'constants'` will serialize infinity and NaN values as `Infinity` and `NaN`. + - `'strings'` will serialize infinity as string `"Infinity"` and NaN as string `"NaN"`. + """ + + # whether to validate default values during validation, default False + validate_default: bool + """Whether to validate default values during validation. Defaults to `False`.""" + + validate_return: bool + """Whether to validate the return value from call validators. Defaults to `False`.""" + + protected_namespaces: tuple[str | Pattern[str], ...] + """ + A `tuple` of strings and/or patterns that prevent models from having fields with names that conflict with them. + For strings, we match on a prefix basis. Ex, if 'dog' is in the protected namespace, 'dog_name' will be protected. + For patterns, we match on the entire field name. Ex, if `re.compile(r'^dog$')` is in the protected namespace, 'dog' will be protected, but 'dog_name' will not be. + Defaults to `('model_validate', 'model_dump',)`. + + The reason we've selected these is to prevent collisions with other validation / dumping formats + in the future - ex, `model_validate_{some_newly_supported_format}`. + + Before v2.10, Pydantic used `('model_',)` as the default value for this setting to + prevent collisions between model attributes and `BaseModel`'s own methods. This was changed + in v2.10 given feedback that this restriction was limiting in AI and data science contexts, + where it is common to have fields with names like `model_id`, `model_input`, `model_output`, etc. + + For more details, see https://github.com/pydantic/pydantic/issues/10315. + + ```python + import warnings + + from pydantic import BaseModel + + warnings.filterwarnings('error') # Raise warnings as errors + + try: + + class Model(BaseModel): + model_dump_something: str + + except UserWarning as e: + print(e) + ''' + Field 'model_dump_something' in 'Model' conflicts with protected namespace 'model_dump'. + + You may be able to solve this by setting the 'protected_namespaces' configuration to ('model_validate',). + ''' + ``` + + You can customize this behavior using the `protected_namespaces` setting: + + ```python {test="skip"} + import re + import warnings + + from pydantic import BaseModel, ConfigDict + + with warnings.catch_warnings(record=True) as caught_warnings: + warnings.simplefilter('always') # Catch all warnings + + class Model(BaseModel): + safe_field: str + also_protect_field: str + protect_this: str + + model_config = ConfigDict( + protected_namespaces=( + 'protect_me_', + 'also_protect_', + re.compile('^protect_this$'), + ) + ) + + for warning in caught_warnings: + print(f'{warning.message}') + ''' + Field 'also_protect_field' in 'Model' conflicts with protected namespace 'also_protect_'. + You may be able to solve this by setting the 'protected_namespaces' configuration to ('protect_me_', re.compile('^protect_this$'))`. + + Field 'protect_this' in 'Model' conflicts with protected namespace 're.compile('^protect_this$')'. + You may be able to solve this by setting the 'protected_namespaces' configuration to ('protect_me_', 'also_protect_')`. + ''' + ``` + + While Pydantic will only emit a warning when an item is in a protected namespace but does not actually have a collision, + an error _is_ raised if there is an actual collision with an existing attribute: + + ```python + from pydantic import BaseModel, ConfigDict + + try: + + class Model(BaseModel): + model_validate: str + + model_config = ConfigDict(protected_namespaces=('model_',)) + + except ValueError as e: + print(e) + ''' + Field 'model_validate' conflicts with member > of protected namespace 'model_'. + ''' + ``` + """ + + hide_input_in_errors: bool + """ + Whether to hide inputs when printing errors. Defaults to `False`. + + Pydantic shows the input value and type when it raises `ValidationError` during the validation. + + ```python + from pydantic import BaseModel, ValidationError + + class Model(BaseModel): + a: str + + try: + Model(a=123) + except ValidationError as e: + print(e) + ''' + 1 validation error for Model + a + Input should be a valid string [type=string_type, input_value=123, input_type=int] + ''' + ``` + + You can hide the input value and type by setting the `hide_input_in_errors` config to `True`. + + ```python + from pydantic import BaseModel, ConfigDict, ValidationError + + class Model(BaseModel): + a: str + model_config = ConfigDict(hide_input_in_errors=True) + + try: + Model(a=123) + except ValidationError as e: + print(e) + ''' + 1 validation error for Model + a + Input should be a valid string [type=string_type] + ''' + ``` + """ + + defer_build: bool + """ + Whether to defer model validator and serializer construction until the first model validation. Defaults to False. + + This can be useful to avoid the overhead of building models which are only + used nested within other models, or when you want to manually define type namespace via + [`Model.model_rebuild(_types_namespace=...)`][pydantic.BaseModel.model_rebuild]. + + Since v2.10, this setting also applies to pydantic dataclasses and TypeAdapter instances. + """ + + plugin_settings: dict[str, object] | None + """A `dict` of settings for plugins. Defaults to `None`.""" + + schema_generator: type[_GenerateSchema] | None + """ + !!! warning + `schema_generator` is deprecated in v2.10. + + Prior to v2.10, this setting was advertised as highly subject to change. + It's possible that this interface may once again become public once the internal core schema generation + API is more stable, but that will likely come after significant performance improvements have been made. + """ + + json_schema_serialization_defaults_required: bool + """ + Whether fields with default values should be marked as required in the serialization schema. Defaults to `False`. + + This ensures that the serialization schema will reflect the fact a field with a default will always be present + when serializing the model, even though it is not required for validation. + + However, there are scenarios where this may be undesirable — in particular, if you want to share the schema + between validation and serialization, and don't mind fields with defaults being marked as not required during + serialization. See [#7209](https://github.com/pydantic/pydantic/issues/7209) for more details. + + ```python + from pydantic import BaseModel, ConfigDict + + class Model(BaseModel): + a: str = 'a' + + model_config = ConfigDict(json_schema_serialization_defaults_required=True) + + print(Model.model_json_schema(mode='validation')) + ''' + { + 'properties': {'a': {'default': 'a', 'title': 'A', 'type': 'string'}}, + 'title': 'Model', + 'type': 'object', + } + ''' + print(Model.model_json_schema(mode='serialization')) + ''' + { + 'properties': {'a': {'default': 'a', 'title': 'A', 'type': 'string'}}, + 'required': ['a'], + 'title': 'Model', + 'type': 'object', + } + ''' + ``` + """ + + json_schema_mode_override: Literal['validation', 'serialization', None] + """ + If not `None`, the specified mode will be used to generate the JSON schema regardless of what `mode` was passed to + the function call. Defaults to `None`. + + This provides a way to force the JSON schema generation to reflect a specific mode, e.g., to always use the + validation schema. + + It can be useful when using frameworks (such as FastAPI) that may generate different schemas for validation + and serialization that must both be referenced from the same schema; when this happens, we automatically append + `-Input` to the definition reference for the validation schema and `-Output` to the definition reference for the + serialization schema. By specifying a `json_schema_mode_override` though, this prevents the conflict between + the validation and serialization schemas (since both will use the specified schema), and so prevents the suffixes + from being added to the definition references. + + ```python + from pydantic import BaseModel, ConfigDict, Json + + class Model(BaseModel): + a: Json[int] # requires a string to validate, but will dump an int + + print(Model.model_json_schema(mode='serialization')) + ''' + { + 'properties': {'a': {'title': 'A', 'type': 'integer'}}, + 'required': ['a'], + 'title': 'Model', + 'type': 'object', + } + ''' + + class ForceInputModel(Model): + # the following ensures that even with mode='serialization', we + # will get the schema that would be generated for validation. + model_config = ConfigDict(json_schema_mode_override='validation') + + print(ForceInputModel.model_json_schema(mode='serialization')) + ''' + { + 'properties': { + 'a': { + 'contentMediaType': 'application/json', + 'contentSchema': {'type': 'integer'}, + 'title': 'A', + 'type': 'string', + } + }, + 'required': ['a'], + 'title': 'ForceInputModel', + 'type': 'object', + } + ''' + ``` + """ + + coerce_numbers_to_str: bool + """ + If `True`, enables automatic coercion of any `Number` type to `str` in "lax" (non-strict) mode. Defaults to `False`. + + Pydantic doesn't allow number types (`int`, `float`, `Decimal`) to be coerced as type `str` by default. + + ```python + from decimal import Decimal + + from pydantic import BaseModel, ConfigDict, ValidationError + + class Model(BaseModel): + value: str + + try: + print(Model(value=42)) + except ValidationError as e: + print(e) + ''' + 1 validation error for Model + value + Input should be a valid string [type=string_type, input_value=42, input_type=int] + ''' + + class Model(BaseModel): + model_config = ConfigDict(coerce_numbers_to_str=True) + + value: str + + repr(Model(value=42).value) + #> "42" + repr(Model(value=42.13).value) + #> "42.13" + repr(Model(value=Decimal('42.13')).value) + #> "42.13" + ``` + """ + + regex_engine: Literal['rust-regex', 'python-re'] + """ + The regex engine to be used for pattern validation. + Defaults to `'rust-regex'`. + + - `'rust-regex'` uses the [`regex`](https://docs.rs/regex) Rust crate, + which is non-backtracking and therefore more DDoS resistant, but does not support all regex features. + - `'python-re'` use the [`re`][] module, which supports all regex features, but may be slower. + + !!! note + If you use a compiled regex pattern, the `'python-re'` engine will be used regardless of this setting. + This is so that flags such as [`re.IGNORECASE`][] are respected. + + ```python + from pydantic import BaseModel, ConfigDict, Field, ValidationError + + class Model(BaseModel): + model_config = ConfigDict(regex_engine='python-re') + + value: str = Field(pattern=r'^abc(?=def)') + + print(Model(value='abcdef').value) + #> abcdef + + try: + print(Model(value='abxyzcdef')) + except ValidationError as e: + print(e) + ''' + 1 validation error for Model + value + String should match pattern '^abc(?=def)' [type=string_pattern_mismatch, input_value='abxyzcdef', input_type=str] + ''' + ``` + """ + + validation_error_cause: bool + """ + If `True`, Python exceptions that were part of a validation failure will be shown as an exception group as a cause. Can be useful for debugging. Defaults to `False`. + + Note: + Python 3.10 and older don't support exception groups natively. <=3.10, backport must be installed: `pip install exceptiongroup`. + + Note: + The structure of validation errors are likely to change in future Pydantic versions. Pydantic offers no guarantees about their structure. Should be used for visual traceback debugging only. + """ + + use_attribute_docstrings: bool + ''' + Whether docstrings of attributes (bare string literals immediately following the attribute declaration) + should be used for field descriptions. Defaults to `False`. + + Available in Pydantic v2.7+. + + ```python + from pydantic import BaseModel, ConfigDict, Field + + + class Model(BaseModel): + model_config = ConfigDict(use_attribute_docstrings=True) + + x: str + """ + Example of an attribute docstring + """ + + y: int = Field(description="Description in Field") + """ + Description in Field overrides attribute docstring + """ + + + print(Model.model_fields["x"].description) + # > Example of an attribute docstring + print(Model.model_fields["y"].description) + # > Description in Field + ``` + This requires the source code of the class to be available at runtime. + + !!! warning "Usage with `TypedDict` and stdlib dataclasses" + Due to current limitations, attribute docstrings detection may not work as expected when using + [`TypedDict`][typing.TypedDict] and stdlib dataclasses, in particular when: + + - inheritance is being used. + - multiple classes have the same name in the same source file (unless Python 3.13 or greater is used). + ''' + + cache_strings: bool | Literal['all', 'keys', 'none'] + """ + Whether to cache strings to avoid constructing new Python objects. Defaults to True. + + Enabling this setting should significantly improve validation performance while increasing memory usage slightly. + + - `True` or `'all'` (the default): cache all strings + - `'keys'`: cache only dictionary keys + - `False` or `'none'`: no caching + + !!! note + `True` or `'all'` is required to cache strings during general validation because + validators don't know if they're in a key or a value. + + !!! tip + If repeated strings are rare, it's recommended to use `'keys'` or `'none'` to reduce memory usage, + as the performance difference is minimal if repeated strings are rare. + """ + + validate_by_alias: bool + """ + Whether an aliased field may be populated by its alias. Defaults to `True`. + + !!! note + In v2.11, `validate_by_alias` was introduced in conjunction with [`validate_by_name`][pydantic.ConfigDict.validate_by_name] + to empower users with more fine grained validation control. In my_field='foo' + ``` + + 1. The field `'my_field'` has an alias `'my_alias'`. + 2. The model can only be populated by the attribute name `'my_field'`. + + !!! warning + You cannot set both `validate_by_alias` and `validate_by_name` to `False`. + This would make it impossible to populate an attribute. + + See [usage errors](../errors/usage_errors.md#validate-by-alias-and-name-false) for an example. + + If you set `validate_by_alias` to `False`, under the hood, Pydantic dynamically sets + `validate_by_name` to `True` to ensure that validation can still occur. + """ + + validate_by_name: bool + """ + Whether an aliased field may be populated by its name as given by the model + attribute. Defaults to `False`. + + !!! note + In v2.0-v2.10, the `populate_by_name` configuration setting was used to specify + whether or not a field could be populated by its name **and** alias. + + In v2.11, `validate_by_name` was introduced in conjunction with [`validate_by_alias`][pydantic.ConfigDict.validate_by_alias] + to empower users with more fine grained validation behavior control. + + ```python + from pydantic import BaseModel, ConfigDict, Field + + class Model(BaseModel): + model_config = ConfigDict(validate_by_name=True, validate_by_alias=True) + + my_field: str = Field(validation_alias='my_alias') # (1)! + + m = Model(my_alias='foo') # (2)! + print(m) + #> my_field='foo' + + m = Model(my_field='foo') # (3)! + print(m) + #> my_field='foo' + ``` + + 1. The field `'my_field'` has an alias `'my_alias'`. + 2. The model is populated by the alias `'my_alias'`. + 3. The model is populated by the attribute name `'my_field'`. + + !!! warning + You cannot set both `validate_by_alias` and `validate_by_name` to `False`. + This would make it impossible to populate an attribute. + + See [usage errors](../errors/usage_errors.md#validate-by-alias-and-name-false) for an example. + """ + + serialize_by_alias: bool + """ + Whether an aliased field should be serialized by its alias. Defaults to `False`. + + Note: In v2.11, `serialize_by_alias` was introduced to address the + [popular request](https://github.com/pydantic/pydantic/issues/8379) + for consistency with alias behavior for validation and serialization settings. + In v3, the default value is expected to change to `True` for consistency with the validation default. + + ```python + from pydantic import BaseModel, ConfigDict, Field + + class Model(BaseModel): + model_config = ConfigDict(serialize_by_alias=True) + + my_field: str = Field(serialization_alias='my_alias') # (1)! + + m = Model(my_field='foo') + print(m.model_dump()) # (2)! + #> {'my_alias': 'foo'} + ``` + + 1. The field `'my_field'` has an alias `'my_alias'`. + 2. The model is serialized using the alias `'my_alias'` for the `'my_field'` attribute. + """ + + url_preserve_empty_path: bool + """ + Whether to preserve empty URL paths when validating values for a URL type. Defaults to `False`. + + ```python + from pydantic import AnyUrl, BaseModel, ConfigDict + + class Model(BaseModel): + model_config = ConfigDict(url_preserve_empty_path=True) + + url: AnyUrl + + m = Model(url='http://example.com') + print(m.url) + #> http://example.com + ``` + """ + + +_TypeT = TypeVar('_TypeT', bound=type) + + +@overload +@deprecated('Passing `config` as a keyword argument is deprecated. Pass `config` as a positional argument instead.') +def with_config(*, config: ConfigDict) -> Callable[[_TypeT], _TypeT]: ... + + +@overload +def with_config(config: ConfigDict, /) -> Callable[[_TypeT], _TypeT]: ... + + +@overload +def with_config(**config: Unpack[ConfigDict]) -> Callable[[_TypeT], _TypeT]: ... + + +def with_config(config: ConfigDict | None = None, /, **kwargs: Any) -> Callable[[_TypeT], _TypeT]: + """!!! abstract "Usage Documentation" + [Configuration with other types](../concepts/config.md#configuration-on-other-supported-types) + + A convenience decorator to set a [Pydantic configuration](config.md) on a `TypedDict` or a `dataclass` from the standard library. + + Although the configuration can be set using the `__pydantic_config__` attribute, it does not play well with type checkers, + especially with `TypedDict`. + + !!! example "Usage" + + ```python + from typing_extensions import TypedDict + + from pydantic import ConfigDict, TypeAdapter, with_config + + @with_config(ConfigDict(str_to_lower=True)) + class TD(TypedDict): + x: str + + ta = TypeAdapter(TD) + + print(ta.validate_python({'x': 'ABC'})) + #> {'x': 'abc'} + ``` + """ + if config is not None and kwargs: + raise ValueError('Cannot specify both `config` and keyword arguments') + + if len(kwargs) == 1 and (kwargs_conf := kwargs.get('config')) is not None: + warnings.warn( + 'Passing `config` as a keyword argument is deprecated. Pass `config` as a positional argument instead', + category=PydanticDeprecatedSince211, + stacklevel=2, + ) + final_config = cast(ConfigDict, kwargs_conf) + else: + final_config = config if config is not None else cast(ConfigDict, kwargs) + + def inner(class_: _TypeT, /) -> _TypeT: + # Ideally, we would check for `class_` to either be a `TypedDict` or a stdlib dataclass. + # However, the `@with_config` decorator can be applied *after* `@dataclass`. To avoid + # common mistakes, we at least check for `class_` to not be a Pydantic model. + from ._internal._utils import is_model_class + + if is_model_class(class_): + raise PydanticUserError( + f'Cannot use `with_config` on {class_.__name__} as it is a Pydantic model', + code='with-config-on-model', + ) + class_.__pydantic_config__ = final_config + return class_ + + return inner + + +__getattr__ = getattr_migration(__name__) diff --git a/.venv/lib/python3.12/site-packages/pydantic/dataclasses.py b/.venv/lib/python3.12/site-packages/pydantic/dataclasses.py new file mode 100644 index 0000000000000000000000000000000000000000..e5537ff4d8f3496f6f707fee6af4feb76de711c8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/dataclasses.py @@ -0,0 +1,413 @@ +"""Provide an enhanced dataclass that performs validation.""" + +from __future__ import annotations as _annotations + +import dataclasses +import functools +import sys +import types +from typing import TYPE_CHECKING, Any, Callable, Generic, Literal, NoReturn, TypeVar, overload +from warnings import warn + +from typing_extensions import TypeGuard, dataclass_transform + +from ._internal import _config, _decorators, _namespace_utils, _typing_extra +from ._internal import _dataclasses as _pydantic_dataclasses +from ._migration import getattr_migration +from .config import ConfigDict +from .errors import PydanticUserError +from .fields import Field, FieldInfo, PrivateAttr + +if TYPE_CHECKING: + from ._internal._dataclasses import PydanticDataclass + from ._internal._namespace_utils import MappingNamespace + +__all__ = 'dataclass', 'rebuild_dataclass' + +_T = TypeVar('_T') + +if sys.version_info >= (3, 10): + + @dataclass_transform(field_specifiers=(dataclasses.field, Field, PrivateAttr)) + @overload + def dataclass( + *, + init: Literal[False] = False, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool = False, + config: ConfigDict | type[object] | None = None, + validate_on_init: bool | None = None, + kw_only: bool = ..., + slots: bool = ..., + ) -> Callable[[type[_T]], type[PydanticDataclass]]: # type: ignore + ... + + @dataclass_transform(field_specifiers=(dataclasses.field, Field, PrivateAttr)) + @overload + def dataclass( + _cls: type[_T], # type: ignore + *, + init: Literal[False] = False, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool | None = None, + config: ConfigDict | type[object] | None = None, + validate_on_init: bool | None = None, + kw_only: bool = ..., + slots: bool = ..., + ) -> type[PydanticDataclass]: ... + +else: + + @dataclass_transform(field_specifiers=(dataclasses.field, Field, PrivateAttr)) + @overload + def dataclass( + *, + init: Literal[False] = False, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool | None = None, + config: ConfigDict | type[object] | None = None, + validate_on_init: bool | None = None, + ) -> Callable[[type[_T]], type[PydanticDataclass]]: # type: ignore + ... + + @dataclass_transform(field_specifiers=(dataclasses.field, Field, PrivateAttr)) + @overload + def dataclass( + _cls: type[_T], # type: ignore + *, + init: Literal[False] = False, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool | None = None, + config: ConfigDict | type[object] | None = None, + validate_on_init: bool | None = None, + ) -> type[PydanticDataclass]: ... + + +@dataclass_transform(field_specifiers=(dataclasses.field, Field, PrivateAttr)) +def dataclass( + _cls: type[_T] | None = None, + *, + init: Literal[False] = False, + repr: bool = True, + eq: bool = True, + order: bool = False, + unsafe_hash: bool = False, + frozen: bool | None = None, + config: ConfigDict | type[object] | None = None, + validate_on_init: bool | None = None, + kw_only: bool = False, + slots: bool = False, +) -> Callable[[type[_T]], type[PydanticDataclass]] | type[PydanticDataclass]: + """!!! abstract "Usage Documentation" + [`dataclasses`](../concepts/dataclasses.md) + + A decorator used to create a Pydantic-enhanced dataclass, similar to the standard Python `dataclass`, + but with added validation. + + This function should be used similarly to `dataclasses.dataclass`. + + Args: + _cls: The target `dataclass`. + init: Included for signature compatibility with `dataclasses.dataclass`, and is passed through to + `dataclasses.dataclass` when appropriate. If specified, must be set to `False`, as pydantic inserts its + own `__init__` function. + repr: A boolean indicating whether to include the field in the `__repr__` output. + eq: Determines if a `__eq__` method should be generated for the class. + order: Determines if comparison magic methods should be generated, such as `__lt__`, but not `__eq__`. + unsafe_hash: Determines if a `__hash__` method should be included in the class, as in `dataclasses.dataclass`. + frozen: Determines if the generated class should be a 'frozen' `dataclass`, which does not allow its + attributes to be modified after it has been initialized. If not set, the value from the provided `config` argument will be used (and will default to `False` otherwise). + config: The Pydantic config to use for the `dataclass`. + validate_on_init: A deprecated parameter included for backwards compatibility; in V2, all Pydantic dataclasses + are validated on init. + kw_only: Determines if `__init__` method parameters must be specified by keyword only. Defaults to `False`. + slots: Determines if the generated class should be a 'slots' `dataclass`, which does not allow the addition of + new attributes after instantiation. + + Returns: + A decorator that accepts a class as its argument and returns a Pydantic `dataclass`. + + Raises: + AssertionError: Raised if `init` is not `False` or `validate_on_init` is `False`. + """ + assert init is False, 'pydantic.dataclasses.dataclass only supports init=False' + assert validate_on_init is not False, 'validate_on_init=False is no longer supported' + + if sys.version_info >= (3, 10): + kwargs = {'kw_only': kw_only, 'slots': slots} + else: + kwargs = {} + + def create_dataclass(cls: type[Any]) -> type[PydanticDataclass]: + """Create a Pydantic dataclass from a regular dataclass. + + Args: + cls: The class to create the Pydantic dataclass from. + + Returns: + A Pydantic dataclass. + """ + from ._internal._utils import is_model_class + + if is_model_class(cls): + raise PydanticUserError( + f'Cannot create a Pydantic dataclass from {cls.__name__} as it is already a Pydantic model', + code='dataclass-on-model', + ) + + original_cls = cls + + # we warn on conflicting config specifications, but only if the class doesn't have a dataclass base + # because a dataclass base might provide a __pydantic_config__ attribute that we don't want to warn about + has_dataclass_base = any(dataclasses.is_dataclass(base) for base in cls.__bases__) + if not has_dataclass_base and config is not None and hasattr(cls, '__pydantic_config__'): + warn( + f'`config` is set via both the `dataclass` decorator and `__pydantic_config__` for dataclass {cls.__name__}. ' + f'The `config` specification from `dataclass` decorator will take priority.', + category=UserWarning, + stacklevel=2, + ) + + # if config is not explicitly provided, try to read it from the type + config_dict = config if config is not None else getattr(cls, '__pydantic_config__', None) + config_wrapper = _config.ConfigWrapper(config_dict) + decorators = _decorators.DecoratorInfos.build(cls) + decorators.update_from_config(config_wrapper) + + # Keep track of the original __doc__ so that we can restore it after applying the dataclasses decorator + # Otherwise, classes with no __doc__ will have their signature added into the JSON schema description, + # since dataclasses.dataclass will set this as the __doc__ + original_doc = cls.__doc__ + + if _pydantic_dataclasses.is_stdlib_dataclass(cls): + # Vanilla dataclasses include a default docstring (representing the class signature), + # which we don't want to preserve. + original_doc = None + + # We don't want to add validation to the existing std lib dataclass, so we will subclass it + # If the class is generic, we need to make sure the subclass also inherits from Generic + # with all the same parameters. + bases = (cls,) + if issubclass(cls, Generic): + generic_base = Generic[cls.__parameters__] # type: ignore + bases = bases + (generic_base,) + cls = types.new_class(cls.__name__, bases) + + # Respect frozen setting from dataclass constructor and fallback to config setting if not provided + if frozen is not None: + frozen_ = frozen + if config_wrapper.frozen: + # It's not recommended to define both, as the setting from the dataclass decorator will take priority. + warn( + f'`frozen` is set via both the `dataclass` decorator and `config` for dataclass {cls.__name__!r}.' + 'This is not recommended. The `frozen` specification on `dataclass` will take priority.', + category=UserWarning, + stacklevel=2, + ) + else: + frozen_ = config_wrapper.frozen or False + + # Make Pydantic's `Field()` function compatible with stdlib dataclasses. As we'll decorate + # `cls` with the stdlib `@dataclass` decorator first, there are two attributes, `kw_only` and + # `repr` that need to be understood *during* the stdlib creation. We do so in two steps: + + # 1. On the decorated class, wrap `Field()` assignment with `dataclass.field()`, with the + # two attributes set (done in `as_dataclass_field()`) + cls_anns = _typing_extra.safe_get_annotations(cls) + for field_name in cls_anns: + # We should look for assignments in `__dict__` instead, but for now we follow + # the same behavior as stdlib dataclasses (see https://github.com/python/cpython/issues/88609) + field_value = getattr(cls, field_name, None) + if isinstance(field_value, FieldInfo): + setattr(cls, field_name, _pydantic_dataclasses.as_dataclass_field(field_value)) + + # 2. For bases of `cls` that are stdlib dataclasses, we temporarily patch their fields + # (see the docstring of the context manager): + with _pydantic_dataclasses.patch_base_fields(cls): + cls = dataclasses.dataclass( # pyright: ignore[reportCallIssue] + cls, + # the value of init here doesn't affect anything except that it makes it easier to generate a signature + init=True, + repr=repr, + eq=eq, + order=order, + unsafe_hash=unsafe_hash, + frozen=frozen_, + **kwargs, + ) + + if config_wrapper.validate_assignment: + original_setattr = cls.__setattr__ + + @functools.wraps(cls.__setattr__) + def validated_setattr(instance: PydanticDataclass, name: str, value: Any, /) -> None: + if frozen_: + return original_setattr(instance, name, value) # pyright: ignore[reportCallIssue] + inst_cls = type(instance) + attr = getattr(inst_cls, name, None) + + if isinstance(attr, property): + attr.__set__(instance, value) + elif isinstance(attr, functools.cached_property): + instance.__dict__.__setitem__(name, value) + else: + inst_cls.__pydantic_validator__.validate_assignment(instance, name, value) + + cls.__setattr__ = validated_setattr.__get__(None, cls) # type: ignore + + if slots and not hasattr(cls, '__setstate__'): + # If slots is set, `pickle` (relied on by `copy.copy()`) will use + # `__setattr__()` to reconstruct the dataclass. However, the custom + # `__setattr__()` set above relies on `validate_assignment()`, which + # in turn expects all the field values to be already present on the + # instance, resulting in attribute errors. + # As such, we make use of `object.__setattr__()` instead. + # Note that we do so only if `__setstate__()` isn't already set (this is the + # case if on top of `slots`, `frozen` is used). + + # Taken from `dataclasses._dataclass_get/setstate()`: + def _dataclass_getstate(self: Any) -> list[Any]: + return [getattr(self, f.name) for f in dataclasses.fields(self)] + + def _dataclass_setstate(self: Any, state: list[Any]) -> None: + for field, value in zip(dataclasses.fields(self), state): + object.__setattr__(self, field.name, value) + + cls.__getstate__ = _dataclass_getstate # pyright: ignore[reportAttributeAccessIssue] + cls.__setstate__ = _dataclass_setstate # pyright: ignore[reportAttributeAccessIssue] + + # This is an undocumented attribute to distinguish stdlib/Pydantic dataclasses. + # It should be set as early as possible: + cls.__is_pydantic_dataclass__ = True + cls.__pydantic_decorators__ = decorators # type: ignore + cls.__doc__ = original_doc + # Can be non-existent for dynamically created classes: + firstlineno = getattr(original_cls, '__firstlineno__', None) + cls.__module__ = original_cls.__module__ + if sys.version_info >= (3, 13) and firstlineno is not None: + # As per https://docs.python.org/3/reference/datamodel.html#type.__firstlineno__: + # Setting the `__module__` attribute removes the `__firstlineno__` item from the type’s dictionary. + original_cls.__firstlineno__ = firstlineno + cls.__firstlineno__ = firstlineno + cls.__qualname__ = original_cls.__qualname__ + cls.__pydantic_fields_complete__ = classmethod(_pydantic_fields_complete) + cls.__pydantic_complete__ = False # `complete_dataclass` will set it to `True` if successful. + # TODO `parent_namespace` is currently None, but we could do the same thing as Pydantic models: + # fetch the parent ns using `parent_frame_namespace` (if the dataclass was defined in a function), + # and possibly cache it (see the `__pydantic_parent_namespace__` logic for models). + _pydantic_dataclasses.complete_dataclass(cls, config_wrapper, raise_errors=False) + return cls + + return create_dataclass if _cls is None else create_dataclass(_cls) + + +def _pydantic_fields_complete(cls: type[PydanticDataclass]) -> bool: + """Return whether the fields where successfully collected (i.e. type hints were successfully resolves). + + This is a private property, not meant to be used outside Pydantic. + """ + return all(field_info._complete for field_info in cls.__pydantic_fields__.values()) + + +__getattr__ = getattr_migration(__name__) + +if sys.version_info < (3, 11): + # Monkeypatch dataclasses.InitVar so that typing doesn't error if it occurs as a type when evaluating type hints + # Starting in 3.11, typing.get_type_hints will not raise an error if the retrieved type hints are not callable. + + def _call_initvar(*args: Any, **kwargs: Any) -> NoReturn: + """This function does nothing but raise an error that is as similar as possible to what you'd get + if you were to try calling `InitVar[int]()` without this monkeypatch. The whole purpose is just + to ensure typing._type_check does not error if the type hint evaluates to `InitVar[]`. + """ + raise TypeError("'InitVar' object is not callable") + + dataclasses.InitVar.__call__ = _call_initvar + + +def rebuild_dataclass( + cls: type[PydanticDataclass], + *, + force: bool = False, + raise_errors: bool = True, + _parent_namespace_depth: int = 2, + _types_namespace: MappingNamespace | None = None, +) -> bool | None: + """Try to rebuild the pydantic-core schema for the dataclass. + + This may be necessary when one of the annotations is a ForwardRef which could not be resolved during + the initial attempt to build the schema, and automatic rebuilding fails. + + This is analogous to `BaseModel.model_rebuild`. + + Args: + cls: The class to rebuild the pydantic-core schema for. + force: Whether to force the rebuilding of the schema, defaults to `False`. + raise_errors: Whether to raise errors, defaults to `True`. + _parent_namespace_depth: The depth level of the parent namespace, defaults to 2. + _types_namespace: The types namespace, defaults to `None`. + + Returns: + Returns `None` if the schema is already "complete" and rebuilding was not required. + If rebuilding _was_ required, returns `True` if rebuilding was successful, otherwise `False`. + """ + if not force and cls.__pydantic_complete__: + return None + + for attr in ('__pydantic_core_schema__', '__pydantic_validator__', '__pydantic_serializer__'): + if attr in cls.__dict__: + # Deleting the validator/serializer is necessary as otherwise they can get reused in + # pydantic-core. Same applies for the core schema that can be reused in schema generation. + delattr(cls, attr) + + cls.__pydantic_complete__ = False + + if _types_namespace is not None: + rebuild_ns = _types_namespace + elif _parent_namespace_depth > 0: + rebuild_ns = _typing_extra.parent_frame_namespace(parent_depth=_parent_namespace_depth, force=True) or {} + else: + rebuild_ns = {} + + ns_resolver = _namespace_utils.NsResolver( + parent_namespace=rebuild_ns, + ) + + return _pydantic_dataclasses.complete_dataclass( + cls, + _config.ConfigWrapper(cls.__pydantic_config__, check=False), + raise_errors=raise_errors, + ns_resolver=ns_resolver, + # We could provide a different config instead (with `'defer_build'` set to `True`) + # of this explicit `_force_build` argument, but because config can come from the + # decorator parameter or the `__pydantic_config__` attribute, `complete_dataclass` + # will overwrite `__pydantic_config__` with the provided config above: + _force_build=True, + ) + + +def is_pydantic_dataclass(class_: type[Any], /) -> TypeGuard[type[PydanticDataclass]]: + """Whether a class is a pydantic dataclass. + + Args: + class_: The class. + + Returns: + `True` if the class is a pydantic dataclass, `False` otherwise. + """ + try: + return '__is_pydantic_dataclass__' in class_.__dict__ and dataclasses.is_dataclass(class_) + except AttributeError: + return False diff --git a/.venv/lib/python3.12/site-packages/pydantic/datetime_parse.py b/.venv/lib/python3.12/site-packages/pydantic/datetime_parse.py new file mode 100644 index 0000000000000000000000000000000000000000..53d52649e7620965ed194240a3becaa3ce3e3448 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/datetime_parse.py @@ -0,0 +1,5 @@ +"""The `datetime_parse` module is a backport module from V1.""" + +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.venv/lib/python3.12/site-packages/pydantic/decorator.py b/.venv/lib/python3.12/site-packages/pydantic/decorator.py new file mode 100644 index 0000000000000000000000000000000000000000..0d97560c1b791956726b04fd66740a947647aabe --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/decorator.py @@ -0,0 +1,5 @@ +"""The `decorator` module is a backport module from V1.""" + +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.venv/lib/python3.12/site-packages/pydantic/env_settings.py b/.venv/lib/python3.12/site-packages/pydantic/env_settings.py new file mode 100644 index 0000000000000000000000000000000000000000..cd0b04e6a43c1bb3767dd136a19a9873fa547514 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/env_settings.py @@ -0,0 +1,5 @@ +"""The `env_settings` module is a backport module from V1.""" + +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.venv/lib/python3.12/site-packages/pydantic/error_wrappers.py b/.venv/lib/python3.12/site-packages/pydantic/error_wrappers.py new file mode 100644 index 0000000000000000000000000000000000000000..2985419abf23f5e529af43883f1d365452e4190a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/error_wrappers.py @@ -0,0 +1,5 @@ +"""The `error_wrappers` module is a backport module from V1.""" + +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.venv/lib/python3.12/site-packages/pydantic/errors.py b/.venv/lib/python3.12/site-packages/pydantic/errors.py new file mode 100644 index 0000000000000000000000000000000000000000..f22706822f3fa542f69810f8e497857d5be36a65 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/errors.py @@ -0,0 +1,189 @@ +"""Pydantic-specific errors.""" + +from __future__ import annotations as _annotations + +import re +from typing import Any, ClassVar, Literal + +from typing_extensions import Self +from typing_inspection.introspection import Qualifier + +from pydantic._internal import _repr + +from ._migration import getattr_migration +from .version import version_short + +__all__ = ( + 'PydanticUserError', + 'PydanticUndefinedAnnotation', + 'PydanticImportError', + 'PydanticSchemaGenerationError', + 'PydanticInvalidForJsonSchema', + 'PydanticForbiddenQualifier', + 'PydanticErrorCodes', +) + +# We use this URL to allow for future flexibility about how we host the docs, while allowing for Pydantic +# code in the while with "old" URLs to still work. +# 'u' refers to "user errors" - e.g. errors caused by developers using pydantic, as opposed to validation errors. +DEV_ERROR_DOCS_URL = f'https://errors.pydantic.dev/{version_short()}/u/' +PydanticErrorCodes = Literal[ + 'class-not-fully-defined', + 'custom-json-schema', + 'decorator-missing-field', + 'discriminator-no-field', + 'discriminator-alias-type', + 'discriminator-needs-literal', + 'discriminator-alias', + 'discriminator-validator', + 'callable-discriminator-no-tag', + 'typed-dict-version', + 'model-field-overridden', + 'model-field-missing-annotation', + 'config-both', + 'removed-kwargs', + 'circular-reference-schema', + 'invalid-for-json-schema', + 'json-schema-already-used', + 'base-model-instantiated', + 'undefined-annotation', + 'schema-for-unknown-type', + 'import-error', + 'create-model-field-definitions', + 'validator-no-fields', + 'validator-invalid-fields', + 'validator-instance-method', + 'validator-input-type', + 'root-validator-pre-skip', + 'model-serializer-instance-method', + 'validator-field-config-info', + 'validator-v1-signature', + 'validator-signature', + 'field-serializer-signature', + 'model-serializer-signature', + 'multiple-field-serializers', + 'invalid-annotated-type', + 'type-adapter-config-unused', + 'root-model-extra', + 'unevaluable-type-annotation', + 'dataclass-init-false-extra-allow', + 'clashing-init-and-init-var', + 'model-config-invalid-field-name', + 'with-config-on-model', + 'dataclass-on-model', + 'validate-call-type', + 'unpack-typed-dict', + 'overlapping-unpack-typed-dict', + 'invalid-self-type', + 'validate-by-alias-and-name-false', +] + + +class PydanticErrorMixin: + """A mixin class for common functionality shared by all Pydantic-specific errors. + + Attributes: + message: A message describing the error. + code: An optional error code from PydanticErrorCodes enum. + """ + + def __init__(self, message: str, *, code: PydanticErrorCodes | None) -> None: + self.message = message + self.code = code + + def __str__(self) -> str: + if self.code is None: + return self.message + else: + return f'{self.message}\n\nFor further information visit {DEV_ERROR_DOCS_URL}{self.code}' + + +class PydanticUserError(PydanticErrorMixin, TypeError): + """An error raised due to incorrect use of Pydantic.""" + + +class PydanticUndefinedAnnotation(PydanticErrorMixin, NameError): + """A subclass of `NameError` raised when handling undefined annotations during `CoreSchema` generation. + + Attributes: + name: Name of the error. + message: Description of the error. + """ + + def __init__(self, name: str, message: str) -> None: + self.name = name + super().__init__(message=message, code='undefined-annotation') + + @classmethod + def from_name_error(cls, name_error: NameError) -> Self: + """Convert a `NameError` to a `PydanticUndefinedAnnotation` error. + + Args: + name_error: `NameError` to be converted. + + Returns: + Converted `PydanticUndefinedAnnotation` error. + """ + try: + name = name_error.name # type: ignore # python > 3.10 + except AttributeError: + name = re.search(r".*'(.+?)'", str(name_error)).group(1) # type: ignore[union-attr] + return cls(name=name, message=str(name_error)) + + +class PydanticImportError(PydanticErrorMixin, ImportError): + """An error raised when an import fails due to module changes between V1 and V2. + + Attributes: + message: Description of the error. + """ + + def __init__(self, message: str) -> None: + super().__init__(message, code='import-error') + + +class PydanticSchemaGenerationError(PydanticUserError): + """An error raised during failures to generate a `CoreSchema` for some type. + + Attributes: + message: Description of the error. + """ + + def __init__(self, message: str) -> None: + super().__init__(message, code='schema-for-unknown-type') + + +class PydanticInvalidForJsonSchema(PydanticUserError): + """An error raised during failures to generate a JSON schema for some `CoreSchema`. + + Attributes: + message: Description of the error. + """ + + def __init__(self, message: str) -> None: + super().__init__(message, code='invalid-for-json-schema') + + +class PydanticForbiddenQualifier(PydanticUserError): + """An error raised if a forbidden type qualifier is found in a type annotation.""" + + _qualifier_repr_map: ClassVar[dict[Qualifier, str]] = { + 'required': 'typing.Required', + 'not_required': 'typing.NotRequired', + 'read_only': 'typing.ReadOnly', + 'class_var': 'typing.ClassVar', + 'init_var': 'dataclasses.InitVar', + 'final': 'typing.Final', + } + + def __init__(self, qualifier: Qualifier, annotation: Any) -> None: + super().__init__( + message=( + f'The annotation {_repr.display_as_type(annotation)!r} contains the {self._qualifier_repr_map[qualifier]!r} ' + f'type qualifier, which is invalid in the context it is defined.' + ), + code=None, + ) + + +__getattr__ = getattr_migration(__name__) diff --git a/.venv/lib/python3.12/site-packages/pydantic/fields.py b/.venv/lib/python3.12/site-packages/pydantic/fields.py new file mode 100644 index 0000000000000000000000000000000000000000..5848533e9bba3332d5521f0f3725c2a0e236495c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/fields.py @@ -0,0 +1,1767 @@ +"""Defining fields on models.""" + +from __future__ import annotations as _annotations + +import dataclasses +import inspect +import re +import sys +from collections.abc import Callable, Mapping +from copy import copy +from dataclasses import Field as DataclassField +from functools import cached_property +from typing import TYPE_CHECKING, Annotated, Any, ClassVar, Literal, TypedDict, TypeVar, cast, final, overload +from warnings import warn + +import annotated_types +import typing_extensions +from pydantic_core import MISSING, PydanticUndefined +from typing_extensions import Self, TypeAlias, Unpack, deprecated +from typing_inspection import typing_objects +from typing_inspection.introspection import UNKNOWN, AnnotationSource, ForbiddenQualifier, Qualifier, inspect_annotation + +from . import types +from ._internal import _decorators, _fields, _generics, _internal_dataclass, _repr, _typing_extra, _utils +from ._internal._namespace_utils import GlobalsNamespace, MappingNamespace +from .aliases import AliasChoices, AliasGenerator, AliasPath +from .config import JsonDict +from .errors import PydanticForbiddenQualifier, PydanticUserError +from .json_schema import PydanticJsonSchemaWarning +from .warnings import PydanticDeprecatedSince20 + +if TYPE_CHECKING: + from ._internal._config import ConfigWrapper + from ._internal._repr import ReprArgs + + +__all__ = 'Field', 'FieldInfo', 'PrivateAttr', 'computed_field' + + +_Unset: Any = PydanticUndefined + +if sys.version_info >= (3, 13): + import warnings + + Deprecated: TypeAlias = warnings.deprecated | deprecated +else: + Deprecated: TypeAlias = deprecated + + +class _FromFieldInfoInputs(TypedDict, total=False): + """This class exists solely to add type checking for the `**kwargs` in `FieldInfo.from_field`.""" + + # TODO PEP 747: use TypeForm: + annotation: type[Any] | None + default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any] | None + alias: str | None + alias_priority: int | None + validation_alias: str | AliasPath | AliasChoices | None + serialization_alias: str | None + title: str | None + field_title_generator: Callable[[str, FieldInfo], str] | None + description: str | None + examples: list[Any] | None + exclude: bool | None + exclude_if: Callable[[Any], bool] | None + gt: annotated_types.SupportsGt | None + ge: annotated_types.SupportsGe | None + lt: annotated_types.SupportsLt | None + le: annotated_types.SupportsLe | None + multiple_of: float | None + strict: bool | None + min_length: int | None + max_length: int | None + pattern: str | re.Pattern[str] | None + allow_inf_nan: bool | None + max_digits: int | None + decimal_places: int | None + union_mode: Literal['smart', 'left_to_right'] | None + discriminator: str | types.Discriminator | None + deprecated: Deprecated | str | bool | None + json_schema_extra: JsonDict | Callable[[JsonDict], None] | None + frozen: bool | None + validate_default: bool | None + repr: bool + init: bool | None + init_var: bool | None + kw_only: bool | None + coerce_numbers_to_str: bool | None + fail_fast: bool | None + + +class _FieldInfoInputs(_FromFieldInfoInputs, total=False): + """This class exists solely to add type checking for the `**kwargs` in `FieldInfo.__init__`.""" + + default: Any + + +@final +class FieldInfo(_repr.Representation): + """This class holds information about a field. + + `FieldInfo` is used for any field definition regardless of whether the [`Field()`][pydantic.fields.Field] + function is explicitly used. + + !!! warning + You generally shouldn't be creating `FieldInfo` directly, you'll only need to use it when accessing + [`BaseModel`][pydantic.main.BaseModel] `.model_fields` internals. + + Attributes: + annotation: The type annotation of the field. + default: The default value of the field. + default_factory: A callable to generate the default value. The callable can either take 0 arguments + (in which case it is called as is) or a single argument containing the already validated data. + alias: The alias name of the field. + alias_priority: The priority of the field's alias. + validation_alias: The validation alias of the field. + serialization_alias: The serialization alias of the field. + title: The title of the field. + field_title_generator: A callable that takes a field name and returns title for it. + description: The description of the field. + examples: List of examples of the field. + exclude: Whether to exclude the field from the model serialization. + exclude_if: A callable that determines whether to exclude a field during serialization based on its value. + discriminator: Field name or Discriminator for discriminating the type in a tagged union. + deprecated: A deprecation message, an instance of `warnings.deprecated` or the `typing_extensions.deprecated` backport, + or a boolean. If `True`, a default deprecation message will be emitted when accessing the field. + json_schema_extra: A dict or callable to provide extra JSON schema properties. + frozen: Whether the field is frozen. + validate_default: Whether to validate the default value of the field. + repr: Whether to include the field in representation of the model. + init: Whether the field should be included in the constructor of the dataclass. + init_var: Whether the field should _only_ be included in the constructor of the dataclass, and not stored. + kw_only: Whether the field should be a keyword-only argument in the constructor of the dataclass. + metadata: List of metadata constraints. + """ + + annotation: type[Any] | None + default: Any + default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any] | None + alias: str | None + alias_priority: int | None + validation_alias: str | AliasPath | AliasChoices | None + serialization_alias: str | None + title: str | None + field_title_generator: Callable[[str, FieldInfo], str] | None + description: str | None + examples: list[Any] | None + exclude: bool | None + exclude_if: Callable[[Any], bool] | None + discriminator: str | types.Discriminator | None + deprecated: Deprecated | str | bool | None + json_schema_extra: JsonDict | Callable[[JsonDict], None] | None + frozen: bool | None + validate_default: bool | None + repr: bool + init: bool | None + init_var: bool | None + kw_only: bool | None + metadata: list[Any] + + __slots__ = ( + 'annotation', + 'default', + 'default_factory', + 'alias', + 'alias_priority', + 'validation_alias', + 'serialization_alias', + 'title', + 'field_title_generator', + 'description', + 'examples', + 'exclude', + 'exclude_if', + 'discriminator', + 'deprecated', + 'json_schema_extra', + 'frozen', + 'validate_default', + 'repr', + 'init', + 'init_var', + 'kw_only', + 'metadata', + '_attributes_set', + '_qualifiers', + '_complete', + '_original_assignment', + '_original_annotation', + ) + + # used to convert kwargs to metadata/constraints, + # None has a special meaning - these items are collected into a `PydanticGeneralMetadata` + metadata_lookup: ClassVar[dict[str, Callable[[Any], Any] | None]] = { + 'strict': types.Strict, + 'gt': annotated_types.Gt, + 'ge': annotated_types.Ge, + 'lt': annotated_types.Lt, + 'le': annotated_types.Le, + 'multiple_of': annotated_types.MultipleOf, + 'min_length': annotated_types.MinLen, + 'max_length': annotated_types.MaxLen, + 'pattern': None, + 'allow_inf_nan': None, + 'max_digits': None, + 'decimal_places': None, + 'union_mode': None, + 'coerce_numbers_to_str': None, + 'fail_fast': types.FailFast, + } + + def __init__(self, **kwargs: Unpack[_FieldInfoInputs]) -> None: + """This class should generally not be initialized directly; instead, use the `pydantic.fields.Field` function + or one of the constructor classmethods. + + See the signature of `pydantic.fields.Field` for more details about the expected arguments. + """ + self._attributes_set = {k: v for k, v in kwargs.items() if v is not _Unset and k not in self.metadata_lookup} + kwargs = {k: _DefaultValues.get(k) if v is _Unset else v for k, v in kwargs.items()} # type: ignore + self.annotation = kwargs.get('annotation') + + default = kwargs.pop('default', PydanticUndefined) + if default is Ellipsis: + self.default = PydanticUndefined + self._attributes_set.pop('default', None) + else: + self.default = default + + self.default_factory = kwargs.pop('default_factory', None) + + if self.default is not PydanticUndefined and self.default_factory is not None: + raise TypeError('cannot specify both default and default_factory') + + self.alias = kwargs.pop('alias', None) + self.validation_alias = kwargs.pop('validation_alias', None) + self.serialization_alias = kwargs.pop('serialization_alias', None) + alias_is_set = any(alias is not None for alias in (self.alias, self.validation_alias, self.serialization_alias)) + self.alias_priority = kwargs.pop('alias_priority', None) or 2 if alias_is_set else None + self.title = kwargs.pop('title', None) + self.field_title_generator = kwargs.pop('field_title_generator', None) + self.description = kwargs.pop('description', None) + self.examples = kwargs.pop('examples', None) + self.exclude = kwargs.pop('exclude', None) + self.exclude_if = kwargs.pop('exclude_if', None) + self.discriminator = kwargs.pop('discriminator', None) + # For compatibility with FastAPI<=0.110.0, we preserve the existing value if it is not overridden + self.deprecated = kwargs.pop('deprecated', getattr(self, 'deprecated', None)) + self.repr = kwargs.pop('repr', True) + self.json_schema_extra = kwargs.pop('json_schema_extra', None) + self.validate_default = kwargs.pop('validate_default', None) + self.frozen = kwargs.pop('frozen', None) + # currently only used on dataclasses + self.init = kwargs.pop('init', None) + self.init_var = kwargs.pop('init_var', None) + self.kw_only = kwargs.pop('kw_only', None) + + self.metadata = self._collect_metadata(kwargs) # type: ignore + + # Private attributes: + self._qualifiers: set[Qualifier] = set() + # Used to rebuild FieldInfo instances: + self._complete = True + self._original_annotation: Any = PydanticUndefined + self._original_assignment: Any = PydanticUndefined + + @staticmethod + def from_field(default: Any = PydanticUndefined, **kwargs: Unpack[_FromFieldInfoInputs]) -> FieldInfo: + """Create a new `FieldInfo` object with the `Field` function. + + Args: + default: The default value for the field. Defaults to Undefined. + **kwargs: Additional arguments dictionary. + + Raises: + TypeError: If 'annotation' is passed as a keyword argument. + + Returns: + A new FieldInfo object with the given parameters. + + Example: + This is how you can create a field with default value like this: + + ```python + import pydantic + + class MyModel(pydantic.BaseModel): + foo: int = pydantic.Field(4) + ``` + """ + if 'annotation' in kwargs: + raise TypeError('"annotation" is not permitted as a Field keyword argument') + return FieldInfo(default=default, **kwargs) + + @staticmethod + def from_annotation(annotation: type[Any], *, _source: AnnotationSource = AnnotationSource.ANY) -> FieldInfo: + """Creates a `FieldInfo` instance from a bare annotation. + + This function is used internally to create a `FieldInfo` from a bare annotation like this: + + ```python + import pydantic + + class MyModel(pydantic.BaseModel): + foo: int # <-- like this + ``` + + We also account for the case where the annotation can be an instance of `Annotated` and where + one of the (not first) arguments in `Annotated` is an instance of `FieldInfo`, e.g.: + + ```python + from typing import Annotated + + import annotated_types + + import pydantic + + class MyModel(pydantic.BaseModel): + foo: Annotated[int, annotated_types.Gt(42)] + bar: Annotated[int, pydantic.Field(gt=42)] + ``` + + Args: + annotation: An annotation object. + + Returns: + An instance of the field metadata. + """ + try: + inspected_ann = inspect_annotation( + annotation, + annotation_source=_source, + unpack_type_aliases='skip', + ) + except ForbiddenQualifier as e: + raise PydanticForbiddenQualifier(e.qualifier, annotation) + + # TODO check for classvar and error? + + # No assigned value, this happens when using a bare `Final` qualifier (also for other + # qualifiers, but they shouldn't appear here). In this case we infer the type as `Any` + # because we don't have any assigned value. + type_expr: Any = Any if inspected_ann.type is UNKNOWN else inspected_ann.type + final = 'final' in inspected_ann.qualifiers + metadata = inspected_ann.metadata + + attr_overrides = {'annotation': type_expr} + if final: + attr_overrides['frozen'] = True + field_info = FieldInfo._construct(metadata, **attr_overrides) + field_info._qualifiers = inspected_ann.qualifiers + return field_info + + @staticmethod + def from_annotated_attribute( + annotation: type[Any], default: Any, *, _source: AnnotationSource = AnnotationSource.ANY + ) -> FieldInfo: + """Create `FieldInfo` from an annotation with a default value. + + This is used in cases like the following: + + ```python + from typing import Annotated + + import annotated_types + + import pydantic + + class MyModel(pydantic.BaseModel): + foo: int = 4 # <-- like this + bar: Annotated[int, annotated_types.Gt(4)] = 4 # <-- or this + spam: Annotated[int, pydantic.Field(gt=4)] = 4 # <-- or this + ``` + + Args: + annotation: The type annotation of the field. + default: The default value of the field. + + Returns: + A field object with the passed values. + """ + if annotation is not MISSING and annotation is default: + raise PydanticUserError( + 'Error when building FieldInfo from annotated attribute. ' + "Make sure you don't have any field name clashing with a type annotation.", + code='unevaluable-type-annotation', + ) + + try: + inspected_ann = inspect_annotation( + annotation, + annotation_source=_source, + unpack_type_aliases='skip', + ) + except ForbiddenQualifier as e: + raise PydanticForbiddenQualifier(e.qualifier, annotation) + + # TODO check for classvar and error? + + # TODO infer from the default, this can be done in v3 once we treat final fields with + # a default as proper fields and not class variables: + type_expr: Any = Any if inspected_ann.type is UNKNOWN else inspected_ann.type + final = 'final' in inspected_ann.qualifiers + metadata = inspected_ann.metadata + + # HACK 1: the order in which the metadata is merged is inconsistent; we need to prepend + # metadata from the assignment at the beginning of the metadata. Changing this is only + # possible in v3 (at least). See https://github.com/pydantic/pydantic/issues/10507 + prepend_metadata: list[Any] | None = None + attr_overrides = {'annotation': type_expr} + if final: + attr_overrides['frozen'] = True + + # HACK 2: FastAPI is subclassing `FieldInfo` and historically expected the actual + # instance's type to be preserved when constructing new models with its subclasses as assignments. + # This code is never reached by Pydantic itself, and in an ideal world this shouldn't be necessary. + if not metadata and isinstance(default, FieldInfo) and type(default) is not FieldInfo: + field_info = default._copy() + field_info._attributes_set.update(attr_overrides) + for k, v in attr_overrides.items(): + setattr(field_info, k, v) + return field_info + + if isinstance(default, FieldInfo): + default_copy = default._copy() # Copy unnecessary when we remove HACK 1. + prepend_metadata = default_copy.metadata + default_copy.metadata = [] + metadata = metadata + [default_copy] + elif isinstance(default, dataclasses.Field): + from_field = FieldInfo._from_dataclass_field(default) + prepend_metadata = from_field.metadata # Unnecessary when we remove HACK 1. + from_field.metadata = [] + metadata = metadata + [from_field] + if 'init_var' in inspected_ann.qualifiers: + attr_overrides['init_var'] = True + if (init := getattr(default, 'init', None)) is not None: + attr_overrides['init'] = init + if (kw_only := getattr(default, 'kw_only', None)) is not None: + attr_overrides['kw_only'] = kw_only + else: + # `default` is the actual default value + attr_overrides['default'] = default + + field_info = FieldInfo._construct( + prepend_metadata + metadata if prepend_metadata is not None else metadata, **attr_overrides + ) + field_info._qualifiers = inspected_ann.qualifiers + return field_info + + @classmethod + def _construct(cls, metadata: list[Any], **attr_overrides: Any) -> Self: + """Construct the final `FieldInfo` instance, by merging the possibly existing `FieldInfo` instances from the metadata. + + With the following example: + + ```python {test="skip" lint="skip"} + class Model(BaseModel): + f: Annotated[int, Gt(1), Field(description='desc', lt=2)] + ``` + + `metadata` refers to the metadata elements of the `Annotated` form. This metadata is iterated over from left to right: + + - If the element is a `Field()` function (which is itself a `FieldInfo` instance), the field attributes (such as + `description`) are saved to be set on the final `FieldInfo` instance. + On the other hand, some kwargs (such as `lt`) are stored as `metadata` (see `FieldInfo.__init__()`, calling + `FieldInfo._collect_metadata()`). In this case, the final metadata list is extended with the one from this instance. + - Else, the element is considered as a single metadata object, and is appended to the final metadata list. + + Args: + metadata: The list of metadata elements to merge together. If the `FieldInfo` instance to be constructed is for + a field with an assigned `Field()`, this `Field()` assignment should be added as the last element of the + provided metadata. + **attr_overrides: Extra attributes that should be set on the final merged `FieldInfo` instance. + + Returns: + The final merged `FieldInfo` instance. + """ + merged_metadata: list[Any] = [] + merged_kwargs: dict[str, Any] = {} + + for meta in metadata: + if isinstance(meta, FieldInfo): + merged_metadata.extend(meta.metadata) + + new_js_extra: JsonDict | None = None + current_js_extra = meta.json_schema_extra + if current_js_extra is not None and 'json_schema_extra' in merged_kwargs: + # We need to merge `json_schema_extra`'s: + existing_js_extra = merged_kwargs['json_schema_extra'] + if isinstance(existing_js_extra, dict): + if isinstance(current_js_extra, dict): + new_js_extra = { + **existing_js_extra, + **current_js_extra, + } + elif callable(current_js_extra): + warn( + 'Composing `dict` and `callable` type `json_schema_extra` is not supported. ' + 'The `callable` type is being ignored. ' + "If you'd like support for this behavior, please open an issue on pydantic.", + UserWarning, + ) + elif callable(existing_js_extra) and isinstance(current_js_extra, dict): + warn( + 'Composing `dict` and `callable` type `json_schema_extra` is not supported. ' + 'The `callable` type is being ignored. ' + "If you'd like support for this behavior, please open an issue on pydantic.", + UserWarning, + ) + + merged_kwargs.update(meta._attributes_set) + if new_js_extra is not None: + merged_kwargs['json_schema_extra'] = new_js_extra + elif typing_objects.is_deprecated(meta): + merged_kwargs['deprecated'] = meta + else: + merged_metadata.append(meta) + + merged_kwargs.update(attr_overrides) + merged_field_info = cls(**merged_kwargs) + merged_field_info.metadata = merged_metadata + return merged_field_info + + @staticmethod + @typing_extensions.deprecated( + "The 'merge_field_infos()' method is deprecated and will be removed in a future version. " + 'If you relied on this method, please open an issue in the Pydantic issue tracker.', + category=None, + ) + def merge_field_infos(*field_infos: FieldInfo, **overrides: Any) -> FieldInfo: + """Merge `FieldInfo` instances keeping only explicitly set attributes. + + Later `FieldInfo` instances override earlier ones. + + Returns: + FieldInfo: A merged FieldInfo instance. + """ + if len(field_infos) == 1: + # No merging necessary, but we still need to make a copy and apply the overrides + field_info = field_infos[0]._copy() + field_info._attributes_set.update(overrides) + + default_override = overrides.pop('default', PydanticUndefined) + if default_override is Ellipsis: + default_override = PydanticUndefined + if default_override is not PydanticUndefined: + field_info.default = default_override + + for k, v in overrides.items(): + setattr(field_info, k, v) + return field_info # type: ignore + + merged_field_info_kwargs: dict[str, Any] = {} + metadata = {} + for field_info in field_infos: + attributes_set = field_info._attributes_set.copy() + + try: + json_schema_extra = attributes_set.pop('json_schema_extra') + existing_json_schema_extra = merged_field_info_kwargs.get('json_schema_extra') + + if existing_json_schema_extra is None: + merged_field_info_kwargs['json_schema_extra'] = json_schema_extra + if isinstance(existing_json_schema_extra, dict): + if isinstance(json_schema_extra, dict): + merged_field_info_kwargs['json_schema_extra'] = { + **existing_json_schema_extra, + **json_schema_extra, + } + if callable(json_schema_extra): + warn( + 'Composing `dict` and `callable` type `json_schema_extra` is not supported.' + 'The `callable` type is being ignored.' + "If you'd like support for this behavior, please open an issue on pydantic.", + PydanticJsonSchemaWarning, + ) + elif callable(json_schema_extra): + # if ever there's a case of a callable, we'll just keep the last json schema extra spec + merged_field_info_kwargs['json_schema_extra'] = json_schema_extra + except KeyError: + pass + + # later FieldInfo instances override everything except json_schema_extra from earlier FieldInfo instances + merged_field_info_kwargs.update(attributes_set) + + for x in field_info.metadata: + if not isinstance(x, FieldInfo): + metadata[type(x)] = x + + merged_field_info_kwargs.update(overrides) + field_info = FieldInfo(**merged_field_info_kwargs) + field_info.metadata = list(metadata.values()) + return field_info + + @staticmethod + def _from_dataclass_field(dc_field: DataclassField[Any]) -> FieldInfo: + """Return a new `FieldInfo` instance from a `dataclasses.Field` instance. + + Args: + dc_field: The `dataclasses.Field` instance to convert. + + Returns: + The corresponding `FieldInfo` instance. + + Raises: + TypeError: If any of the `FieldInfo` kwargs does not match the `dataclass.Field` kwargs. + """ + default = dc_field.default + if default is dataclasses.MISSING: + default = _Unset + + if dc_field.default_factory is dataclasses.MISSING: + default_factory = _Unset + else: + default_factory = dc_field.default_factory + + # use the `Field` function so in correct kwargs raise the correct `TypeError` + dc_field_metadata = {k: v for k, v in dc_field.metadata.items() if k in _FIELD_ARG_NAMES} + if sys.version_info >= (3, 14) and dc_field.doc is not None: + dc_field_metadata['description'] = dc_field.doc + return Field(default=default, default_factory=default_factory, repr=dc_field.repr, **dc_field_metadata) # pyright: ignore[reportCallIssue] + + @staticmethod + def _collect_metadata(kwargs: dict[str, Any]) -> list[Any]: + """Collect annotations from kwargs. + + Args: + kwargs: Keyword arguments passed to the function. + + Returns: + A list of metadata objects - a combination of `annotated_types.BaseMetadata` and + `PydanticMetadata`. + """ + metadata: list[Any] = [] + general_metadata = {} + for key, value in list(kwargs.items()): + try: + marker = FieldInfo.metadata_lookup[key] + except KeyError: + continue + + del kwargs[key] + if value is not None: + if marker is None: + general_metadata[key] = value + else: + metadata.append(marker(value)) + if general_metadata: + metadata.append(_fields.pydantic_general_metadata(**general_metadata)) + return metadata + + @property + def deprecation_message(self) -> str | None: + """The deprecation message to be emitted, or `None` if not set.""" + if self.deprecated is None: + return None + if isinstance(self.deprecated, bool): + return 'deprecated' if self.deprecated else None + return self.deprecated if isinstance(self.deprecated, str) else self.deprecated.message + + @property + def default_factory_takes_validated_data(self) -> bool | None: + """Whether the provided default factory callable has a validated data parameter. + + Returns `None` if no default factory is set. + """ + if self.default_factory is not None: + return _fields.takes_validated_data_argument(self.default_factory) + + @overload + def get_default( + self, *, call_default_factory: Literal[True], validated_data: dict[str, Any] | None = None + ) -> Any: ... + + @overload + def get_default(self, *, call_default_factory: Literal[False] = ...) -> Any: ... + + def get_default(self, *, call_default_factory: bool = False, validated_data: dict[str, Any] | None = None) -> Any: + """Get the default value. + + We expose an option for whether to call the default_factory (if present), as calling it may + result in side effects that we want to avoid. However, there are times when it really should + be called (namely, when instantiating a model via `model_construct`). + + Args: + call_default_factory: Whether to call the default factory or not. + validated_data: The already validated data to be passed to the default factory. + + Returns: + The default value, calling the default factory if requested or `None` if not set. + """ + if self.default_factory is None: + return _utils.smart_deepcopy(self.default) + elif call_default_factory: + if self.default_factory_takes_validated_data: + fac = cast('Callable[[dict[str, Any]], Any]', self.default_factory) + if validated_data is None: + raise ValueError( + "The default factory requires the 'validated_data' argument, which was not provided when calling 'get_default'." + ) + return fac(validated_data) + else: + fac = cast('Callable[[], Any]', self.default_factory) + return fac() + else: + return None + + def is_required(self) -> bool: + """Check if the field is required (i.e., does not have a default value or factory). + + Returns: + `True` if the field is required, `False` otherwise. + """ + return self.default is PydanticUndefined and self.default_factory is None + + def rebuild_annotation(self) -> Any: + """Attempts to rebuild the original annotation for use in function signatures. + + If metadata is present, it adds it to the original annotation using + `Annotated`. Otherwise, it returns the original annotation as-is. + + Note that because the metadata has been flattened, the original annotation + may not be reconstructed exactly as originally provided, e.g. if the original + type had unrecognized annotations, or was annotated with a call to `pydantic.Field`. + + Returns: + The rebuilt annotation. + """ + if not self.metadata: + return self.annotation + else: + # Annotated arguments must be a tuple + return Annotated[(self.annotation, *self.metadata)] # type: ignore + + def apply_typevars_map( + self, + typevars_map: Mapping[TypeVar, Any] | None, + globalns: GlobalsNamespace | None = None, + localns: MappingNamespace | None = None, + ) -> None: + """Apply a `typevars_map` to the annotation. + + This method is used when analyzing parametrized generic types to replace typevars with their concrete types. + + This method applies the `typevars_map` to the annotation in place. + + Args: + typevars_map: A dictionary mapping type variables to their concrete types. + globalns: The globals namespace to use during type annotation evaluation. + localns: The locals namespace to use during type annotation evaluation. + + See Also: + pydantic._internal._generics.replace_types is used for replacing the typevars with + their concrete types. + """ + annotation = _generics.replace_types(self.annotation, typevars_map) + annotation, evaluated = _typing_extra.try_eval_type(annotation, globalns, localns) + self.annotation = annotation + if not evaluated: + self._complete = False + self._original_annotation = self.annotation + + def _copy(self) -> Self: + """Return a copy of the `FieldInfo` instance.""" + # Note: we can't define a custom `__copy__()`, as `FieldInfo` is being subclassed + # by some third-party libraries with extra attributes defined (and as `FieldInfo` + # is slotted, we can't make a copy of the `__dict__`). + copied = copy(self) + for attr_name in ('metadata', '_attributes_set', '_qualifiers'): + # Apply "deep-copy" behavior on collections attributes: + value = getattr(copied, attr_name).copy() + setattr(copied, attr_name, value) + + return copied + + def __repr_args__(self) -> ReprArgs: + yield 'annotation', _repr.PlainRepr(_repr.display_as_type(self.annotation)) + yield 'required', self.is_required() + + for s in self.__slots__: + # TODO: properly make use of the protocol (https://rich.readthedocs.io/en/stable/pretty.html#rich-repr-protocol) + # By yielding a three-tuple: + if s in ( + 'annotation', + '_attributes_set', + '_qualifiers', + '_complete', + '_original_assignment', + '_original_annotation', + ): + continue + elif s == 'metadata' and not self.metadata: + continue + elif s == 'repr' and self.repr is True: + continue + if s == 'frozen' and self.frozen is False: + continue + if s == 'validation_alias' and self.validation_alias == self.alias: + continue + if s == 'serialization_alias' and self.serialization_alias == self.alias: + continue + if s == 'default' and self.default is not PydanticUndefined: + yield 'default', self.default + elif s == 'default_factory' and self.default_factory is not None: + yield 'default_factory', _repr.PlainRepr(_repr.display_as_type(self.default_factory)) + else: + value = getattr(self, s) + if value is not None and value is not PydanticUndefined: + yield s, value + + +class _EmptyKwargs(TypedDict): + """This class exists solely to ensure that type checking warns about passing `**extra` in `Field`.""" + + +_DefaultValues = { + 'default': ..., + 'default_factory': None, + 'alias': None, + 'alias_priority': None, + 'validation_alias': None, + 'serialization_alias': None, + 'title': None, + 'description': None, + 'examples': None, + 'exclude': None, + 'exclude_if': None, + 'discriminator': None, + 'json_schema_extra': None, + 'frozen': None, + 'validate_default': None, + 'repr': True, + 'init': None, + 'init_var': None, + 'kw_only': None, + 'pattern': None, + 'strict': None, + 'gt': None, + 'ge': None, + 'lt': None, + 'le': None, + 'multiple_of': None, + 'allow_inf_nan': None, + 'max_digits': None, + 'decimal_places': None, + 'min_length': None, + 'max_length': None, + 'coerce_numbers_to_str': None, +} + + +_T = TypeVar('_T') + + +# NOTE: Actual return type is 'FieldInfo', but we want to help type checkers +# to understand the magic that happens at runtime with the following overloads: +@overload # type hint the return value as `Any` to avoid type checking regressions when using `...`. +def Field( + default: ellipsis, # noqa: F821 # TODO: use `_typing_extra.EllipsisType` when we drop Py3.9 + *, + alias: str | None = _Unset, + alias_priority: int | None = _Unset, + validation_alias: str | AliasPath | AliasChoices | None = _Unset, + serialization_alias: str | None = _Unset, + title: str | None = _Unset, + field_title_generator: Callable[[str, FieldInfo], str] | None = _Unset, + description: str | None = _Unset, + examples: list[Any] | None = _Unset, + exclude: bool | None = _Unset, + exclude_if: Callable[[Any], bool] | None = _Unset, + discriminator: str | types.Discriminator | None = _Unset, + deprecated: Deprecated | str | bool | None = _Unset, + json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = _Unset, + frozen: bool | None = _Unset, + validate_default: bool | None = _Unset, + repr: bool = _Unset, + init: bool | None = _Unset, + init_var: bool | None = _Unset, + kw_only: bool | None = _Unset, + pattern: str | re.Pattern[str] | None = _Unset, + strict: bool | None = _Unset, + coerce_numbers_to_str: bool | None = _Unset, + gt: annotated_types.SupportsGt | None = _Unset, + ge: annotated_types.SupportsGe | None = _Unset, + lt: annotated_types.SupportsLt | None = _Unset, + le: annotated_types.SupportsLe | None = _Unset, + multiple_of: float | None = _Unset, + allow_inf_nan: bool | None = _Unset, + max_digits: int | None = _Unset, + decimal_places: int | None = _Unset, + min_length: int | None = _Unset, + max_length: int | None = _Unset, + union_mode: Literal['smart', 'left_to_right'] = _Unset, + fail_fast: bool | None = _Unset, + **extra: Unpack[_EmptyKwargs], +) -> Any: ... +@overload # `default` argument set, validate_default=True (no type checking on the default value) +def Field( + default: Any, + *, + alias: str | None = _Unset, + alias_priority: int | None = _Unset, + validation_alias: str | AliasPath | AliasChoices | None = _Unset, + serialization_alias: str | None = _Unset, + title: str | None = _Unset, + field_title_generator: Callable[[str, FieldInfo], str] | None = _Unset, + description: str | None = _Unset, + examples: list[Any] | None = _Unset, + exclude: bool | None = _Unset, + exclude_if: Callable[[Any], bool] | None = _Unset, + discriminator: str | types.Discriminator | None = _Unset, + deprecated: Deprecated | str | bool | None = _Unset, + json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = _Unset, + frozen: bool | None = _Unset, + validate_default: Literal[True], + repr: bool = _Unset, + init: bool | None = _Unset, + init_var: bool | None = _Unset, + kw_only: bool | None = _Unset, + pattern: str | re.Pattern[str] | None = _Unset, + strict: bool | None = _Unset, + coerce_numbers_to_str: bool | None = _Unset, + gt: annotated_types.SupportsGt | None = _Unset, + ge: annotated_types.SupportsGe | None = _Unset, + lt: annotated_types.SupportsLt | None = _Unset, + le: annotated_types.SupportsLe | None = _Unset, + multiple_of: float | None = _Unset, + allow_inf_nan: bool | None = _Unset, + max_digits: int | None = _Unset, + decimal_places: int | None = _Unset, + min_length: int | None = _Unset, + max_length: int | None = _Unset, + union_mode: Literal['smart', 'left_to_right'] = _Unset, + fail_fast: bool | None = _Unset, + **extra: Unpack[_EmptyKwargs], +) -> Any: ... +@overload # `default` argument set, validate_default=False or unset +def Field( + default: _T, + *, + alias: str | None = _Unset, + alias_priority: int | None = _Unset, + validation_alias: str | AliasPath | AliasChoices | None = _Unset, + serialization_alias: str | None = _Unset, + title: str | None = _Unset, + field_title_generator: Callable[[str, FieldInfo], str] | None = _Unset, + description: str | None = _Unset, + examples: list[Any] | None = _Unset, + exclude: bool | None = _Unset, + # NOTE: to get proper type checking on `exclude_if`'s argument, we could use `_T` instead of `Any`. However, + # this requires (at least for pyright) adding an additional overload where `exclude_if` is required (otherwise + # `a: int = Field(default_factory=str)` results in a false negative). + exclude_if: Callable[[Any], bool] | None = _Unset, + discriminator: str | types.Discriminator | None = _Unset, + deprecated: Deprecated | str | bool | None = _Unset, + json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = _Unset, + frozen: bool | None = _Unset, + validate_default: Literal[False] = ..., + repr: bool = _Unset, + init: bool | None = _Unset, + init_var: bool | None = _Unset, + kw_only: bool | None = _Unset, + pattern: str | re.Pattern[str] | None = _Unset, + strict: bool | None = _Unset, + coerce_numbers_to_str: bool | None = _Unset, + gt: annotated_types.SupportsGt | None = _Unset, + ge: annotated_types.SupportsGe | None = _Unset, + lt: annotated_types.SupportsLt | None = _Unset, + le: annotated_types.SupportsLe | None = _Unset, + multiple_of: float | None = _Unset, + allow_inf_nan: bool | None = _Unset, + max_digits: int | None = _Unset, + decimal_places: int | None = _Unset, + min_length: int | None = _Unset, + max_length: int | None = _Unset, + union_mode: Literal['smart', 'left_to_right'] = _Unset, + fail_fast: bool | None = _Unset, + **extra: Unpack[_EmptyKwargs], +) -> _T: ... +@overload # `default_factory` argument set, validate_default=True (no type checking on the default value) +def Field( # pyright: ignore[reportOverlappingOverload] + *, + default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any], + alias: str | None = _Unset, + alias_priority: int | None = _Unset, + validation_alias: str | AliasPath | AliasChoices | None = _Unset, + serialization_alias: str | None = _Unset, + title: str | None = _Unset, + field_title_generator: Callable[[str, FieldInfo], str] | None = _Unset, + description: str | None = _Unset, + examples: list[Any] | None = _Unset, + exclude: bool | None = _Unset, + exclude_if: Callable[[Any], bool] | None = _Unset, + discriminator: str | types.Discriminator | None = _Unset, + deprecated: Deprecated | str | bool | None = _Unset, + json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = _Unset, + frozen: bool | None = _Unset, + validate_default: Literal[True], + repr: bool = _Unset, + init: bool | None = _Unset, + init_var: bool | None = _Unset, + kw_only: bool | None = _Unset, + pattern: str | re.Pattern[str] | None = _Unset, + strict: bool | None = _Unset, + coerce_numbers_to_str: bool | None = _Unset, + gt: annotated_types.SupportsGt | None = _Unset, + ge: annotated_types.SupportsGe | None = _Unset, + lt: annotated_types.SupportsLt | None = _Unset, + le: annotated_types.SupportsLe | None = _Unset, + multiple_of: float | None = _Unset, + allow_inf_nan: bool | None = _Unset, + max_digits: int | None = _Unset, + decimal_places: int | None = _Unset, + min_length: int | None = _Unset, + max_length: int | None = _Unset, + union_mode: Literal['smart', 'left_to_right'] = _Unset, + fail_fast: bool | None = _Unset, + **extra: Unpack[_EmptyKwargs], +) -> Any: ... +@overload # `default_factory` argument set, validate_default=False or unset +def Field( + *, + default_factory: Callable[[], _T] | Callable[[dict[str, Any]], _T], + alias: str | None = _Unset, + alias_priority: int | None = _Unset, + validation_alias: str | AliasPath | AliasChoices | None = _Unset, + serialization_alias: str | None = _Unset, + title: str | None = _Unset, + field_title_generator: Callable[[str, FieldInfo], str] | None = _Unset, + description: str | None = _Unset, + examples: list[Any] | None = _Unset, + exclude: bool | None = _Unset, + # NOTE: to get proper type checking on `exclude_if`'s argument, we could use `_T` instead of `Any`. However, + # this requires (at least for pyright) adding an additional overload where `exclude_if` is required (otherwise + # `a: int = Field(default_factory=str)` results in a false negative). + exclude_if: Callable[[Any], bool] | None = _Unset, + discriminator: str | types.Discriminator | None = _Unset, + deprecated: Deprecated | str | bool | None = _Unset, + json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = _Unset, + frozen: bool | None = _Unset, + validate_default: Literal[False] | None = _Unset, + repr: bool = _Unset, + init: bool | None = _Unset, + init_var: bool | None = _Unset, + kw_only: bool | None = _Unset, + pattern: str | re.Pattern[str] | None = _Unset, + strict: bool | None = _Unset, + coerce_numbers_to_str: bool | None = _Unset, + gt: annotated_types.SupportsGt | None = _Unset, + ge: annotated_types.SupportsGe | None = _Unset, + lt: annotated_types.SupportsLt | None = _Unset, + le: annotated_types.SupportsLe | None = _Unset, + multiple_of: float | None = _Unset, + allow_inf_nan: bool | None = _Unset, + max_digits: int | None = _Unset, + decimal_places: int | None = _Unset, + min_length: int | None = _Unset, + max_length: int | None = _Unset, + union_mode: Literal['smart', 'left_to_right'] = _Unset, + fail_fast: bool | None = _Unset, + **extra: Unpack[_EmptyKwargs], +) -> _T: ... +@overload +def Field( # No default set + *, + alias: str | None = _Unset, + alias_priority: int | None = _Unset, + validation_alias: str | AliasPath | AliasChoices | None = _Unset, + serialization_alias: str | None = _Unset, + title: str | None = _Unset, + field_title_generator: Callable[[str, FieldInfo], str] | None = _Unset, + description: str | None = _Unset, + examples: list[Any] | None = _Unset, + exclude: bool | None = _Unset, + exclude_if: Callable[[Any], bool] | None = _Unset, + discriminator: str | types.Discriminator | None = _Unset, + deprecated: Deprecated | str | bool | None = _Unset, + json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = _Unset, + frozen: bool | None = _Unset, + validate_default: bool | None = _Unset, + repr: bool = _Unset, + init: bool | None = _Unset, + init_var: bool | None = _Unset, + kw_only: bool | None = _Unset, + pattern: str | re.Pattern[str] | None = _Unset, + strict: bool | None = _Unset, + coerce_numbers_to_str: bool | None = _Unset, + gt: annotated_types.SupportsGt | None = _Unset, + ge: annotated_types.SupportsGe | None = _Unset, + lt: annotated_types.SupportsLt | None = _Unset, + le: annotated_types.SupportsLe | None = _Unset, + multiple_of: float | None = _Unset, + allow_inf_nan: bool | None = _Unset, + max_digits: int | None = _Unset, + decimal_places: int | None = _Unset, + min_length: int | None = _Unset, + max_length: int | None = _Unset, + union_mode: Literal['smart', 'left_to_right'] = _Unset, + fail_fast: bool | None = _Unset, + **extra: Unpack[_EmptyKwargs], +) -> Any: ... +def Field( # noqa: C901 + default: Any = PydanticUndefined, + *, + default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any] | None = _Unset, + alias: str | None = _Unset, + alias_priority: int | None = _Unset, + validation_alias: str | AliasPath | AliasChoices | None = _Unset, + serialization_alias: str | None = _Unset, + title: str | None = _Unset, + field_title_generator: Callable[[str, FieldInfo], str] | None = _Unset, + description: str | None = _Unset, + examples: list[Any] | None = _Unset, + exclude: bool | None = _Unset, + exclude_if: Callable[[Any], bool] | None = _Unset, + discriminator: str | types.Discriminator | None = _Unset, + deprecated: Deprecated | str | bool | None = _Unset, + json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = _Unset, + frozen: bool | None = _Unset, + validate_default: bool | None = _Unset, + repr: bool = _Unset, + init: bool | None = _Unset, + init_var: bool | None = _Unset, + kw_only: bool | None = _Unset, + pattern: str | re.Pattern[str] | None = _Unset, + strict: bool | None = _Unset, + coerce_numbers_to_str: bool | None = _Unset, + gt: annotated_types.SupportsGt | None = _Unset, + ge: annotated_types.SupportsGe | None = _Unset, + lt: annotated_types.SupportsLt | None = _Unset, + le: annotated_types.SupportsLe | None = _Unset, + multiple_of: float | None = _Unset, + allow_inf_nan: bool | None = _Unset, + max_digits: int | None = _Unset, + decimal_places: int | None = _Unset, + min_length: int | None = _Unset, + max_length: int | None = _Unset, + union_mode: Literal['smart', 'left_to_right'] = _Unset, + fail_fast: bool | None = _Unset, + **extra: Unpack[_EmptyKwargs], +) -> Any: + """!!! abstract "Usage Documentation" + [Fields](../concepts/fields.md) + + Create a field for objects that can be configured. + + Used to provide extra information about a field, either for the model schema or complex validation. Some arguments + apply only to number fields (`int`, `float`, `Decimal`) and some apply only to `str`. + + Note: + - Any `_Unset` objects will be replaced by the corresponding value defined in the `_DefaultValues` dictionary. If a key for the `_Unset` object is not found in the `_DefaultValues` dictionary, it will default to `None` + + Args: + default: Default value if the field is not set. + default_factory: A callable to generate the default value. The callable can either take 0 arguments + (in which case it is called as is) or a single argument containing the already validated data. + alias: The name to use for the attribute when validating or serializing by alias. + This is often used for things like converting between snake and camel case. + alias_priority: Priority of the alias. This affects whether an alias generator is used. + validation_alias: Like `alias`, but only affects validation, not serialization. + serialization_alias: Like `alias`, but only affects serialization, not validation. + title: Human-readable title. + field_title_generator: A callable that takes a field name and returns title for it. + description: Human-readable description. + examples: Example values for this field. + exclude: Whether to exclude the field from the model serialization. + exclude_if: A callable that determines whether to exclude a field during serialization based on its value. + discriminator: Field name or Discriminator for discriminating the type in a tagged union. + deprecated: A deprecation message, an instance of `warnings.deprecated` or the `typing_extensions.deprecated` backport, + or a boolean. If `True`, a default deprecation message will be emitted when accessing the field. + json_schema_extra: A dict or callable to provide extra JSON schema properties. + frozen: Whether the field is frozen. If true, attempts to change the value on an instance will raise an error. + validate_default: If `True`, apply validation to the default value every time you create an instance. + Otherwise, for performance reasons, the default value of the field is trusted and not validated. + repr: A boolean indicating whether to include the field in the `__repr__` output. + init: Whether the field should be included in the constructor of the dataclass. + (Only applies to dataclasses.) + init_var: Whether the field should _only_ be included in the constructor of the dataclass. + (Only applies to dataclasses.) + kw_only: Whether the field should be a keyword-only argument in the constructor of the dataclass. + (Only applies to dataclasses.) + coerce_numbers_to_str: Whether to enable coercion of any `Number` type to `str` (not applicable in `strict` mode). + strict: If `True`, strict validation is applied to the field. + See [Strict Mode](../concepts/strict_mode.md) for details. + gt: Greater than. If set, value must be greater than this. Only applicable to numbers. + ge: Greater than or equal. If set, value must be greater than or equal to this. Only applicable to numbers. + lt: Less than. If set, value must be less than this. Only applicable to numbers. + le: Less than or equal. If set, value must be less than or equal to this. Only applicable to numbers. + multiple_of: Value must be a multiple of this. Only applicable to numbers. + min_length: Minimum length for iterables. + max_length: Maximum length for iterables. + pattern: Pattern for strings (a regular expression). + allow_inf_nan: Allow `inf`, `-inf`, `nan`. Only applicable to float and [`Decimal`][decimal.Decimal] numbers. + max_digits: Maximum number of allow digits for strings. + decimal_places: Maximum number of decimal places allowed for numbers. + union_mode: The strategy to apply when validating a union. Can be `smart` (the default), or `left_to_right`. + See [Union Mode](../concepts/unions.md#union-modes) for details. + fail_fast: If `True`, validation will stop on the first error. If `False`, all validation errors will be collected. + This option can be applied only to iterable types (list, tuple, set, and frozenset). + extra: (Deprecated) Extra fields that will be included in the JSON schema. + + !!! warning Deprecated + The `extra` kwargs is deprecated. Use `json_schema_extra` instead. + + Returns: + A new [`FieldInfo`][pydantic.fields.FieldInfo]. The return annotation is `Any` so `Field` can be used on + type-annotated fields without causing a type error. + """ + # Check deprecated and removed params from V1. This logic should eventually be removed. + const = extra.pop('const', None) # type: ignore + if const is not None: + raise PydanticUserError('`const` is removed, use `Literal` instead', code='removed-kwargs') + + min_items = extra.pop('min_items', None) # type: ignore + if min_items is not None: + warn( + '`min_items` is deprecated and will be removed, use `min_length` instead', + PydanticDeprecatedSince20, + stacklevel=2, + ) + if min_length in (None, _Unset): + min_length = min_items # type: ignore + + max_items = extra.pop('max_items', None) # type: ignore + if max_items is not None: + warn( + '`max_items` is deprecated and will be removed, use `max_length` instead', + PydanticDeprecatedSince20, + stacklevel=2, + ) + if max_length in (None, _Unset): + max_length = max_items # type: ignore + + unique_items = extra.pop('unique_items', None) # type: ignore + if unique_items is not None: + raise PydanticUserError( + ( + '`unique_items` is removed, use `Set` instead' + '(this feature is discussed in https://github.com/pydantic/pydantic-core/issues/296)' + ), + code='removed-kwargs', + ) + + allow_mutation = extra.pop('allow_mutation', None) # type: ignore + if allow_mutation is not None: + warn( + '`allow_mutation` is deprecated and will be removed. use `frozen` instead', + PydanticDeprecatedSince20, + stacklevel=2, + ) + if allow_mutation is False: + frozen = True + + regex = extra.pop('regex', None) # type: ignore + if regex is not None: + raise PydanticUserError('`regex` is removed. use `pattern` instead', code='removed-kwargs') + + if extra: + warn( + 'Using extra keyword arguments on `Field` is deprecated and will be removed.' + ' Use `json_schema_extra` instead.' + f' (Extra keys: {", ".join(k.__repr__() for k in extra.keys())})', + PydanticDeprecatedSince20, + stacklevel=2, + ) + if not json_schema_extra or json_schema_extra is _Unset: + json_schema_extra = extra # type: ignore + + if ( + validation_alias + and validation_alias is not _Unset + and not isinstance(validation_alias, (str, AliasChoices, AliasPath)) + ): + raise TypeError('Invalid `validation_alias` type. it should be `str`, `AliasChoices`, or `AliasPath`') + + if serialization_alias in (_Unset, None) and isinstance(alias, str): + serialization_alias = alias + + if validation_alias in (_Unset, None): + validation_alias = alias + + include = extra.pop('include', None) # type: ignore + if include is not None: + warn( + '`include` is deprecated and does nothing. It will be removed, use `exclude` instead', + PydanticDeprecatedSince20, + stacklevel=2, + ) + + return FieldInfo.from_field( + default, + default_factory=default_factory, + alias=alias, + alias_priority=alias_priority, + validation_alias=validation_alias, + serialization_alias=serialization_alias, + title=title, + field_title_generator=field_title_generator, + description=description, + examples=examples, + exclude=exclude, + exclude_if=exclude_if, + discriminator=discriminator, + deprecated=deprecated, + json_schema_extra=json_schema_extra, + frozen=frozen, + pattern=pattern, + validate_default=validate_default, + repr=repr, + init=init, + init_var=init_var, + kw_only=kw_only, + coerce_numbers_to_str=coerce_numbers_to_str, + strict=strict, + gt=gt, + ge=ge, + lt=lt, + le=le, + multiple_of=multiple_of, + min_length=min_length, + max_length=max_length, + allow_inf_nan=allow_inf_nan, + max_digits=max_digits, + decimal_places=decimal_places, + union_mode=union_mode, + fail_fast=fail_fast, + ) + + +_FIELD_ARG_NAMES = set(inspect.signature(Field).parameters) +_FIELD_ARG_NAMES.remove('extra') # do not include the varkwargs parameter + + +class ModelPrivateAttr(_repr.Representation): + """A descriptor for private attributes in class models. + + !!! warning + You generally shouldn't be creating `ModelPrivateAttr` instances directly, instead use + `pydantic.fields.PrivateAttr`. (This is similar to `FieldInfo` vs. `Field`.) + + Attributes: + default: The default value of the attribute if not provided. + default_factory: A callable function that generates the default value of the + attribute if not provided. + """ + + __slots__ = ('default', 'default_factory') + + def __init__(self, default: Any = PydanticUndefined, *, default_factory: Callable[[], Any] | None = None) -> None: + if default is Ellipsis: + self.default = PydanticUndefined + else: + self.default = default + self.default_factory = default_factory + + if not TYPE_CHECKING: + # We put `__getattr__` in a non-TYPE_CHECKING block because otherwise, mypy allows arbitrary attribute access + + def __getattr__(self, item: str) -> Any: + """This function improves compatibility with custom descriptors by ensuring delegation happens + as expected when the default value of a private attribute is a descriptor. + """ + if item in {'__get__', '__set__', '__delete__'}: + if hasattr(self.default, item): + return getattr(self.default, item) + raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}') + + def __set_name__(self, cls: type[Any], name: str) -> None: + """Preserve `__set_name__` protocol defined in https://peps.python.org/pep-0487.""" + default = self.default + if default is PydanticUndefined: + return + set_name = getattr(default, '__set_name__', None) + if callable(set_name): + set_name(cls, name) + + def get_default(self) -> Any: + """Retrieve the default value of the object. + + If `self.default_factory` is `None`, the method will return a deep copy of the `self.default` object. + + If `self.default_factory` is not `None`, it will call `self.default_factory` and return the value returned. + + Returns: + The default value of the object. + """ + return _utils.smart_deepcopy(self.default) if self.default_factory is None else self.default_factory() + + def __eq__(self, other: Any) -> bool: + return isinstance(other, self.__class__) and (self.default, self.default_factory) == ( + other.default, + other.default_factory, + ) + + +# NOTE: Actual return type is 'ModelPrivateAttr', but we want to help type checkers +# to understand the magic that happens at runtime. +@overload # `default` argument set +def PrivateAttr( + default: _T, + *, + init: Literal[False] = False, +) -> _T: ... +@overload # `default_factory` argument set +def PrivateAttr( + *, + default_factory: Callable[[], _T], + init: Literal[False] = False, +) -> _T: ... +@overload # No default set +def PrivateAttr( + *, + init: Literal[False] = False, +) -> Any: ... +def PrivateAttr( + default: Any = PydanticUndefined, + *, + default_factory: Callable[[], Any] | None = None, + init: Literal[False] = False, +) -> Any: + """!!! abstract "Usage Documentation" + [Private Model Attributes](../concepts/models.md#private-model-attributes) + + Indicates that an attribute is intended for private use and not handled during normal validation/serialization. + + Private attributes are not validated by Pydantic, so it's up to you to ensure they are used in a type-safe manner. + + Private attributes are stored in `__private_attributes__` on the model. + + Args: + default: The attribute's default value. Defaults to Undefined. + default_factory: Callable that will be + called when a default value is needed for this attribute. + If both `default` and `default_factory` are set, an error will be raised. + init: Whether the attribute should be included in the constructor of the dataclass. Always `False`. + + Returns: + An instance of [`ModelPrivateAttr`][pydantic.fields.ModelPrivateAttr] class. + + Raises: + ValueError: If both `default` and `default_factory` are set. + """ + if default is not PydanticUndefined and default_factory is not None: + raise TypeError('cannot specify both default and default_factory') + + return ModelPrivateAttr( + default, + default_factory=default_factory, + ) + + +@dataclasses.dataclass(**_internal_dataclass.slots_true) +class ComputedFieldInfo: + """A container for data from `@computed_field` so that we can access it while building the pydantic-core schema. + + Attributes: + decorator_repr: A class variable representing the decorator string, '@computed_field'. + wrapped_property: The wrapped computed field property. + return_type: The type of the computed field property's return value. + alias: The alias of the property to be used during serialization. + alias_priority: The priority of the alias. This affects whether an alias generator is used. + title: Title of the computed field to include in the serialization JSON schema. + field_title_generator: A callable that takes a field name and returns title for it. + description: Description of the computed field to include in the serialization JSON schema. + deprecated: A deprecation message, an instance of `warnings.deprecated` or the `typing_extensions.deprecated` backport, + or a boolean. If `True`, a default deprecation message will be emitted when accessing the field. + examples: Example values of the computed field to include in the serialization JSON schema. + json_schema_extra: A dict or callable to provide extra JSON schema properties. + repr: A boolean indicating whether to include the field in the __repr__ output. + """ + + decorator_repr: ClassVar[str] = '@computed_field' + wrapped_property: property + return_type: Any + alias: str | None + alias_priority: int | None + title: str | None + field_title_generator: Callable[[str, ComputedFieldInfo], str] | None + description: str | None + deprecated: Deprecated | str | bool | None + examples: list[Any] | None + json_schema_extra: JsonDict | Callable[[JsonDict], None] | None + repr: bool + + @property + def deprecation_message(self) -> str | None: + """The deprecation message to be emitted, or `None` if not set.""" + if self.deprecated is None: + return None + if isinstance(self.deprecated, bool): + return 'deprecated' if self.deprecated else None + return self.deprecated if isinstance(self.deprecated, str) else self.deprecated.message + + def _update_from_config(self, config_wrapper: ConfigWrapper, name: str) -> None: + """Update the instance from the configuration set on the class this computed field belongs to.""" + title_generator = self.field_title_generator or config_wrapper.field_title_generator + if title_generator is not None and self.title is None: + self.title = title_generator(name, self) + if config_wrapper.alias_generator is not None: + self._apply_alias_generator(config_wrapper.alias_generator, name) + + def _apply_alias_generator(self, alias_generator: Callable[[str], str] | AliasGenerator, name: str) -> None: + """Apply an alias generator to aliases if appropriate. + + Args: + alias_generator: A callable that takes a string and returns a string, or an `AliasGenerator` instance. + name: The name of the computed field from which to generate the alias. + """ + # Apply an alias_generator if + # 1. An alias is not specified + # 2. An alias is specified, but the priority is <= 1 + + if self.alias_priority is None or self.alias_priority <= 1 or self.alias is None: + alias, _, serialization_alias = None, None, None + + if isinstance(alias_generator, AliasGenerator): + alias, _, serialization_alias = alias_generator.generate_aliases(name) + elif callable(alias_generator): + alias = alias_generator(name) + + # if priority is not set, we set to 1 + # which supports the case where the alias_generator from a child class is used + # to generate an alias for a field in a parent class + if self.alias_priority is None or self.alias_priority <= 1: + self.alias_priority = 1 + + # if the priority is 1, then we set the aliases to the generated alias + # note that we use the serialization_alias with priority over alias, as computed_field + # aliases are used for serialization only (not validation) + if self.alias_priority == 1: + self.alias = _utils.get_first_not_none(serialization_alias, alias) + + +def _wrapped_property_is_private(property_: cached_property | property) -> bool: # type: ignore + """Returns true if provided property is private, False otherwise.""" + wrapped_name: str = '' + + if isinstance(property_, property): + wrapped_name = getattr(property_.fget, '__name__', '') + elif isinstance(property_, cached_property): # type: ignore + wrapped_name = getattr(property_.func, '__name__', '') # type: ignore + + return wrapped_name.startswith('_') and not wrapped_name.startswith('__') + + +# this should really be `property[T], cached_property[T]` but property is not generic unlike cached_property +# See https://github.com/python/typing/issues/985 and linked issues +PropertyT = TypeVar('PropertyT') + + +@overload +def computed_field(func: PropertyT, /) -> PropertyT: ... + + +@overload +def computed_field( + *, + alias: str | None = None, + alias_priority: int | None = None, + title: str | None = None, + field_title_generator: Callable[[str, ComputedFieldInfo], str] | None = None, + description: str | None = None, + deprecated: Deprecated | str | bool | None = None, + examples: list[Any] | None = None, + json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = None, + repr: bool = True, + return_type: Any = PydanticUndefined, +) -> Callable[[PropertyT], PropertyT]: ... + + +def computed_field( + func: PropertyT | None = None, + /, + *, + alias: str | None = None, + alias_priority: int | None = None, + title: str | None = None, + field_title_generator: Callable[[str, ComputedFieldInfo], str] | None = None, + description: str | None = None, + deprecated: Deprecated | str | bool | None = None, + examples: list[Any] | None = None, + json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = None, + repr: bool | None = None, + return_type: Any = PydanticUndefined, +) -> PropertyT | Callable[[PropertyT], PropertyT]: + """!!! abstract "Usage Documentation" + [The `computed_field` decorator](../concepts/fields.md#the-computed_field-decorator) + + Decorator to include `property` and `cached_property` when serializing models or dataclasses. + + This is useful for fields that are computed from other fields, or for fields that are expensive to compute and should be cached. + + ```python + from pydantic import BaseModel, computed_field + + class Rectangle(BaseModel): + width: int + length: int + + @computed_field + @property + def area(self) -> int: + return self.width * self.length + + print(Rectangle(width=3, length=2).model_dump()) + #> {'width': 3, 'length': 2, 'area': 6} + ``` + + If applied to functions not yet decorated with `@property` or `@cached_property`, the function is + automatically wrapped with `property`. Although this is more concise, you will lose IntelliSense in your IDE, + and confuse static type checkers, thus explicit use of `@property` is recommended. + + !!! warning "Mypy Warning" + Even with the `@property` or `@cached_property` applied to your function before `@computed_field`, + mypy may throw a `Decorated property not supported` error. + See [mypy issue #1362](https://github.com/python/mypy/issues/1362), for more information. + To avoid this error message, add `# type: ignore[prop-decorator]` to the `@computed_field` line. + + [pyright](https://github.com/microsoft/pyright) supports `@computed_field` without error. + + ```python + import random + + from pydantic import BaseModel, computed_field + + class Square(BaseModel): + width: float + + @computed_field + def area(self) -> float: # converted to a `property` by `computed_field` + return round(self.width**2, 2) + + @area.setter + def area(self, new_area: float) -> None: + self.width = new_area**0.5 + + @computed_field(alias='the magic number', repr=False) + def random_number(self) -> int: + return random.randint(0, 1_000) + + square = Square(width=1.3) + + # `random_number` does not appear in representation + print(repr(square)) + #> Square(width=1.3, area=1.69) + + print(square.random_number) + #> 3 + + square.area = 4 + + print(square.model_dump_json(by_alias=True)) + #> {"width":2.0,"area":4.0,"the magic number":3} + ``` + + !!! warning "Overriding with `computed_field`" + You can't override a field from a parent class with a `computed_field` in the child class. + `mypy` complains about this behavior if allowed, and `dataclasses` doesn't allow this pattern either. + See the example below: + + ```python + from pydantic import BaseModel, computed_field + + class Parent(BaseModel): + a: str + + try: + + class Child(Parent): + @computed_field + @property + def a(self) -> str: + return 'new a' + + except TypeError as e: + print(e) + ''' + Field 'a' of class 'Child' overrides symbol of same name in a parent class. This override with a computed_field is incompatible. + ''' + ``` + + Private properties decorated with `@computed_field` have `repr=False` by default. + + ```python + from functools import cached_property + + from pydantic import BaseModel, computed_field + + class Model(BaseModel): + foo: int + + @computed_field + @cached_property + def _private_cached_property(self) -> int: + return -self.foo + + @computed_field + @property + def _private_property(self) -> int: + return -self.foo + + m = Model(foo=1) + print(repr(m)) + #> Model(foo=1) + ``` + + Args: + func: the function to wrap. + alias: alias to use when serializing this computed field, only used when `by_alias=True` + alias_priority: priority of the alias. This affects whether an alias generator is used + title: Title to use when including this computed field in JSON Schema + field_title_generator: A callable that takes a field name and returns title for it. + description: Description to use when including this computed field in JSON Schema, defaults to the function's + docstring + deprecated: A deprecation message (or an instance of `warnings.deprecated` or the `typing_extensions.deprecated` backport). + to be emitted when accessing the field. Or a boolean. This will automatically be set if the property is decorated with the + `deprecated` decorator. + examples: Example values to use when including this computed field in JSON Schema + json_schema_extra: A dict or callable to provide extra JSON schema properties. + repr: whether to include this computed field in model repr. + Default is `False` for private properties and `True` for public properties. + return_type: optional return for serialization logic to expect when serializing to JSON, if included + this must be correct, otherwise a `TypeError` is raised. + If you don't include a return type Any is used, which does runtime introspection to handle arbitrary + objects. + + Returns: + A proxy wrapper for the property. + """ + + def dec(f: Any) -> Any: + nonlocal description, deprecated, return_type, alias_priority + unwrapped = _decorators.unwrap_wrapped_function(f) + + if description is None and unwrapped.__doc__: + description = inspect.cleandoc(unwrapped.__doc__) + + if deprecated is None and hasattr(unwrapped, '__deprecated__'): + deprecated = unwrapped.__deprecated__ + + # if the function isn't already decorated with `@property` (or another descriptor), then we wrap it now + f = _decorators.ensure_property(f) + alias_priority = (alias_priority or 2) if alias is not None else None + + if repr is None: + repr_: bool = not _wrapped_property_is_private(property_=f) + else: + repr_ = repr + + dec_info = ComputedFieldInfo( + f, + return_type, + alias, + alias_priority, + title, + field_title_generator, + description, + deprecated, + examples, + json_schema_extra, + repr_, + ) + return _decorators.PydanticDescriptorProxy(f, dec_info) + + if func is None: + return dec + else: + return dec(func) diff --git a/.venv/lib/python3.12/site-packages/pydantic/functional_serializers.py b/.venv/lib/python3.12/site-packages/pydantic/functional_serializers.py new file mode 100644 index 0000000000000000000000000000000000000000..0c1522f1bba25bc66aaa3c02a6785203ada8552f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/functional_serializers.py @@ -0,0 +1,451 @@ +"""This module contains related classes and functions for serialization.""" + +from __future__ import annotations + +import dataclasses +from functools import partial, partialmethod +from typing import TYPE_CHECKING, Annotated, Any, Callable, Literal, TypeVar, overload + +from pydantic_core import PydanticUndefined, core_schema +from pydantic_core.core_schema import SerializationInfo, SerializerFunctionWrapHandler, WhenUsed +from typing_extensions import TypeAlias + +from . import PydanticUndefinedAnnotation +from ._internal import _decorators, _internal_dataclass +from .annotated_handlers import GetCoreSchemaHandler + + +@dataclasses.dataclass(**_internal_dataclass.slots_true, frozen=True) +class PlainSerializer: + """Plain serializers use a function to modify the output of serialization. + + This is particularly helpful when you want to customize the serialization for annotated types. + Consider an input of `list`, which will be serialized into a space-delimited string. + + ```python + from typing import Annotated + + from pydantic import BaseModel, PlainSerializer + + CustomStr = Annotated[ + list, PlainSerializer(lambda x: ' '.join(x), return_type=str) + ] + + class StudentModel(BaseModel): + courses: CustomStr + + student = StudentModel(courses=['Math', 'Chemistry', 'English']) + print(student.model_dump()) + #> {'courses': 'Math Chemistry English'} + ``` + + Attributes: + func: The serializer function. + return_type: The return type for the function. If omitted it will be inferred from the type annotation. + when_used: Determines when this serializer should be used. Accepts a string with values `'always'`, + `'unless-none'`, `'json'`, and `'json-unless-none'`. Defaults to 'always'. + """ + + func: core_schema.SerializerFunction + return_type: Any = PydanticUndefined + when_used: WhenUsed = 'always' + + def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + """Gets the Pydantic core schema. + + Args: + source_type: The source type. + handler: The `GetCoreSchemaHandler` instance. + + Returns: + The Pydantic core schema. + """ + schema = handler(source_type) + if self.return_type is not PydanticUndefined: + return_type = self.return_type + else: + try: + # Do not pass in globals as the function could be defined in a different module. + # Instead, let `get_callable_return_type` infer the globals to use, but still pass + # in locals that may contain a parent/rebuild namespace: + return_type = _decorators.get_callable_return_type( + self.func, + localns=handler._get_types_namespace().locals, + ) + except NameError as e: + raise PydanticUndefinedAnnotation.from_name_error(e) from e + + return_schema = None if return_type is PydanticUndefined else handler.generate_schema(return_type) + schema['serialization'] = core_schema.plain_serializer_function_ser_schema( + function=self.func, + info_arg=_decorators.inspect_annotated_serializer(self.func, 'plain'), + return_schema=return_schema, + when_used=self.when_used, + ) + return schema + + +@dataclasses.dataclass(**_internal_dataclass.slots_true, frozen=True) +class WrapSerializer: + """Wrap serializers receive the raw inputs along with a handler function that applies the standard serialization + logic, and can modify the resulting value before returning it as the final output of serialization. + + For example, here's a scenario in which a wrap serializer transforms timezones to UTC **and** utilizes the existing `datetime` serialization logic. + + ```python + from datetime import datetime, timezone + from typing import Annotated, Any + + from pydantic import BaseModel, WrapSerializer + + class EventDatetime(BaseModel): + start: datetime + end: datetime + + def convert_to_utc(value: Any, handler, info) -> dict[str, datetime]: + # Note that `handler` can actually help serialize the `value` for + # further custom serialization in case it's a subclass. + partial_result = handler(value, info) + if info.mode == 'json': + return { + k: datetime.fromisoformat(v).astimezone(timezone.utc) + for k, v in partial_result.items() + } + return {k: v.astimezone(timezone.utc) for k, v in partial_result.items()} + + UTCEventDatetime = Annotated[EventDatetime, WrapSerializer(convert_to_utc)] + + class EventModel(BaseModel): + event_datetime: UTCEventDatetime + + dt = EventDatetime( + start='2024-01-01T07:00:00-08:00', end='2024-01-03T20:00:00+06:00' + ) + event = EventModel(event_datetime=dt) + print(event.model_dump()) + ''' + { + 'event_datetime': { + 'start': datetime.datetime( + 2024, 1, 1, 15, 0, tzinfo=datetime.timezone.utc + ), + 'end': datetime.datetime( + 2024, 1, 3, 14, 0, tzinfo=datetime.timezone.utc + ), + } + } + ''' + + print(event.model_dump_json()) + ''' + {"event_datetime":{"start":"2024-01-01T15:00:00Z","end":"2024-01-03T14:00:00Z"}} + ''' + ``` + + Attributes: + func: The serializer function to be wrapped. + return_type: The return type for the function. If omitted it will be inferred from the type annotation. + when_used: Determines when this serializer should be used. Accepts a string with values `'always'`, + `'unless-none'`, `'json'`, and `'json-unless-none'`. Defaults to 'always'. + """ + + func: core_schema.WrapSerializerFunction + return_type: Any = PydanticUndefined + when_used: WhenUsed = 'always' + + def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + """This method is used to get the Pydantic core schema of the class. + + Args: + source_type: Source type. + handler: Core schema handler. + + Returns: + The generated core schema of the class. + """ + schema = handler(source_type) + if self.return_type is not PydanticUndefined: + return_type = self.return_type + else: + try: + # Do not pass in globals as the function could be defined in a different module. + # Instead, let `get_callable_return_type` infer the globals to use, but still pass + # in locals that may contain a parent/rebuild namespace: + return_type = _decorators.get_callable_return_type( + self.func, + localns=handler._get_types_namespace().locals, + ) + except NameError as e: + raise PydanticUndefinedAnnotation.from_name_error(e) from e + + return_schema = None if return_type is PydanticUndefined else handler.generate_schema(return_type) + schema['serialization'] = core_schema.wrap_serializer_function_ser_schema( + function=self.func, + info_arg=_decorators.inspect_annotated_serializer(self.func, 'wrap'), + return_schema=return_schema, + when_used=self.when_used, + ) + return schema + + +if TYPE_CHECKING: + _Partial: TypeAlias = 'partial[Any] | partialmethod[Any]' + + FieldPlainSerializer: TypeAlias = 'core_schema.SerializerFunction | _Partial' + """A field serializer method or function in `plain` mode.""" + + FieldWrapSerializer: TypeAlias = 'core_schema.WrapSerializerFunction | _Partial' + """A field serializer method or function in `wrap` mode.""" + + FieldSerializer: TypeAlias = 'FieldPlainSerializer | FieldWrapSerializer' + """A field serializer method or function.""" + + _FieldPlainSerializerT = TypeVar('_FieldPlainSerializerT', bound=FieldPlainSerializer) + _FieldWrapSerializerT = TypeVar('_FieldWrapSerializerT', bound=FieldWrapSerializer) + + +@overload +def field_serializer( + field: str, + /, + *fields: str, + mode: Literal['wrap'], + return_type: Any = ..., + when_used: WhenUsed = ..., + check_fields: bool | None = ..., +) -> Callable[[_FieldWrapSerializerT], _FieldWrapSerializerT]: ... + + +@overload +def field_serializer( + field: str, + /, + *fields: str, + mode: Literal['plain'] = ..., + return_type: Any = ..., + when_used: WhenUsed = ..., + check_fields: bool | None = ..., +) -> Callable[[_FieldPlainSerializerT], _FieldPlainSerializerT]: ... + + +def field_serializer( + *fields: str, + mode: Literal['plain', 'wrap'] = 'plain', + # TODO PEP 747 (grep for 'return_type' on the whole code base): + return_type: Any = PydanticUndefined, + when_used: WhenUsed = 'always', + check_fields: bool | None = None, +) -> ( + Callable[[_FieldWrapSerializerT], _FieldWrapSerializerT] + | Callable[[_FieldPlainSerializerT], _FieldPlainSerializerT] +): + """Decorator that enables custom field serialization. + + In the below example, a field of type `set` is used to mitigate duplication. A `field_serializer` is used to serialize the data as a sorted list. + + ```python + from pydantic import BaseModel, field_serializer + + class StudentModel(BaseModel): + name: str = 'Jane' + courses: set[str] + + @field_serializer('courses', when_used='json') + def serialize_courses_in_order(self, courses: set[str]): + return sorted(courses) + + student = StudentModel(courses={'Math', 'Chemistry', 'English'}) + print(student.model_dump_json()) + #> {"name":"Jane","courses":["Chemistry","English","Math"]} + ``` + + See [the usage documentation](../concepts/serialization.md#serializers) for more information. + + Four signatures are supported: + + - `(self, value: Any, info: FieldSerializationInfo)` + - `(self, value: Any, nxt: SerializerFunctionWrapHandler, info: FieldSerializationInfo)` + - `(value: Any, info: SerializationInfo)` + - `(value: Any, nxt: SerializerFunctionWrapHandler, info: SerializationInfo)` + + Args: + fields: Which field(s) the method should be called on. + mode: The serialization mode. + + - `plain` means the function will be called instead of the default serialization logic, + - `wrap` means the function will be called with an argument to optionally call the + default serialization logic. + return_type: Optional return type for the function, if omitted it will be inferred from the type annotation. + when_used: Determines the serializer will be used for serialization. + check_fields: Whether to check that the fields actually exist on the model. + + Returns: + The decorator function. + """ + + def dec(f: FieldSerializer) -> _decorators.PydanticDescriptorProxy[Any]: + dec_info = _decorators.FieldSerializerDecoratorInfo( + fields=fields, + mode=mode, + return_type=return_type, + when_used=when_used, + check_fields=check_fields, + ) + return _decorators.PydanticDescriptorProxy(f, dec_info) # pyright: ignore[reportArgumentType] + + return dec # pyright: ignore[reportReturnType] + + +if TYPE_CHECKING: + # The first argument in the following callables represent the `self` type: + + ModelPlainSerializerWithInfo: TypeAlias = Callable[[Any, SerializationInfo[Any]], Any] + """A model serializer method with the `info` argument, in `plain` mode.""" + + ModelPlainSerializerWithoutInfo: TypeAlias = Callable[[Any], Any] + """A model serializer method without the `info` argument, in `plain` mode.""" + + ModelPlainSerializer: TypeAlias = 'ModelPlainSerializerWithInfo | ModelPlainSerializerWithoutInfo' + """A model serializer method in `plain` mode.""" + + ModelWrapSerializerWithInfo: TypeAlias = Callable[[Any, SerializerFunctionWrapHandler, SerializationInfo[Any]], Any] + """A model serializer method with the `info` argument, in `wrap` mode.""" + + ModelWrapSerializerWithoutInfo: TypeAlias = Callable[[Any, SerializerFunctionWrapHandler], Any] + """A model serializer method without the `info` argument, in `wrap` mode.""" + + ModelWrapSerializer: TypeAlias = 'ModelWrapSerializerWithInfo | ModelWrapSerializerWithoutInfo' + """A model serializer method in `wrap` mode.""" + + ModelSerializer: TypeAlias = 'ModelPlainSerializer | ModelWrapSerializer' + + _ModelPlainSerializerT = TypeVar('_ModelPlainSerializerT', bound=ModelPlainSerializer) + _ModelWrapSerializerT = TypeVar('_ModelWrapSerializerT', bound=ModelWrapSerializer) + + +@overload +def model_serializer(f: _ModelPlainSerializerT, /) -> _ModelPlainSerializerT: ... + + +@overload +def model_serializer( + *, mode: Literal['wrap'], when_used: WhenUsed = 'always', return_type: Any = ... +) -> Callable[[_ModelWrapSerializerT], _ModelWrapSerializerT]: ... + + +@overload +def model_serializer( + *, + mode: Literal['plain'] = ..., + when_used: WhenUsed = 'always', + return_type: Any = ..., +) -> Callable[[_ModelPlainSerializerT], _ModelPlainSerializerT]: ... + + +def model_serializer( + f: _ModelPlainSerializerT | _ModelWrapSerializerT | None = None, + /, + *, + mode: Literal['plain', 'wrap'] = 'plain', + when_used: WhenUsed = 'always', + return_type: Any = PydanticUndefined, +) -> ( + _ModelPlainSerializerT + | Callable[[_ModelWrapSerializerT], _ModelWrapSerializerT] + | Callable[[_ModelPlainSerializerT], _ModelPlainSerializerT] +): + """Decorator that enables custom model serialization. + + This is useful when a model need to be serialized in a customized manner, allowing for flexibility beyond just specific fields. + + An example would be to serialize temperature to the same temperature scale, such as degrees Celsius. + + ```python + from typing import Literal + + from pydantic import BaseModel, model_serializer + + class TemperatureModel(BaseModel): + unit: Literal['C', 'F'] + value: int + + @model_serializer() + def serialize_model(self): + if self.unit == 'F': + return {'unit': 'C', 'value': int((self.value - 32) / 1.8)} + return {'unit': self.unit, 'value': self.value} + + temperature = TemperatureModel(unit='F', value=212) + print(temperature.model_dump()) + #> {'unit': 'C', 'value': 100} + ``` + + Two signatures are supported for `mode='plain'`, which is the default: + + - `(self)` + - `(self, info: SerializationInfo)` + + And two other signatures for `mode='wrap'`: + + - `(self, nxt: SerializerFunctionWrapHandler)` + - `(self, nxt: SerializerFunctionWrapHandler, info: SerializationInfo)` + + See [the usage documentation](../concepts/serialization.md#serializers) for more information. + + Args: + f: The function to be decorated. + mode: The serialization mode. + + - `'plain'` means the function will be called instead of the default serialization logic + - `'wrap'` means the function will be called with an argument to optionally call the default + serialization logic. + when_used: Determines when this serializer should be used. + return_type: The return type for the function. If omitted it will be inferred from the type annotation. + + Returns: + The decorator function. + """ + + def dec(f: ModelSerializer) -> _decorators.PydanticDescriptorProxy[Any]: + dec_info = _decorators.ModelSerializerDecoratorInfo(mode=mode, return_type=return_type, when_used=when_used) + return _decorators.PydanticDescriptorProxy(f, dec_info) + + if f is None: + return dec # pyright: ignore[reportReturnType] + else: + return dec(f) # pyright: ignore[reportReturnType] + + +AnyType = TypeVar('AnyType') + + +if TYPE_CHECKING: + SerializeAsAny = Annotated[AnyType, ...] # SerializeAsAny[list[str]] will be treated by type checkers as list[str] + """Annotation used to mark a type as having duck-typing serialization behavior. + + See [usage documentation](../concepts/serialization.md#serializing-with-duck-typing) for more details. + """ +else: + + @dataclasses.dataclass(**_internal_dataclass.slots_true) + class SerializeAsAny: + """Annotation used to mark a type as having duck-typing serialization behavior. + + See [usage documentation](../concepts/serialization.md#serializing-with-duck-typing) for more details. + """ + + def __class_getitem__(cls, item: Any) -> Any: + return Annotated[item, SerializeAsAny()] + + def __get_pydantic_core_schema__( + self, source_type: Any, handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + schema = handler(source_type) + schema_to_update = schema + while schema_to_update['type'] == 'definitions': + schema_to_update = schema_to_update.copy() + schema_to_update = schema_to_update['schema'] + schema_to_update['serialization'] = core_schema.simple_ser_schema('any') + return schema + + __hash__ = object.__hash__ diff --git a/.venv/lib/python3.12/site-packages/pydantic/functional_validators.py b/.venv/lib/python3.12/site-packages/pydantic/functional_validators.py new file mode 100644 index 0000000000000000000000000000000000000000..ce1671434fe4ed795c05220e5e9da6975e4756a4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/functional_validators.py @@ -0,0 +1,883 @@ +"""This module contains related classes and functions for validation.""" + +from __future__ import annotations as _annotations + +import dataclasses +import sys +import warnings +from functools import partialmethod +from types import FunctionType +from typing import TYPE_CHECKING, Annotated, Any, Callable, Literal, TypeVar, Union, cast, overload + +from pydantic_core import PydanticUndefined, core_schema +from typing_extensions import Self, TypeAlias + +from ._internal import _decorators, _generics, _internal_dataclass +from .annotated_handlers import GetCoreSchemaHandler +from .errors import PydanticUserError +from .warnings import ArbitraryTypeWarning + +if sys.version_info < (3, 11): + from typing_extensions import Protocol +else: + from typing import Protocol + +_inspect_validator = _decorators.inspect_validator + + +@dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true) +class AfterValidator: + """!!! abstract "Usage Documentation" + [field *after* validators](../concepts/validators.md#field-after-validator) + + A metadata class that indicates that a validation should be applied **after** the inner validation logic. + + Attributes: + func: The validator function. + + Example: + ```python + from typing import Annotated + + from pydantic import AfterValidator, BaseModel, ValidationError + + MyInt = Annotated[int, AfterValidator(lambda v: v + 1)] + + class Model(BaseModel): + a: MyInt + + print(Model(a=1).a) + #> 2 + + try: + Model(a='a') + except ValidationError as e: + print(e.json(indent=2)) + ''' + [ + { + "type": "int_parsing", + "loc": [ + "a" + ], + "msg": "Input should be a valid integer, unable to parse string as an integer", + "input": "a", + "url": "https://errors.pydantic.dev/2/v/int_parsing" + } + ] + ''' + ``` + """ + + func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction + + def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + schema = handler(source_type) + info_arg = _inspect_validator(self.func, mode='after', type='field') + if info_arg: + func = cast(core_schema.WithInfoValidatorFunction, self.func) + return core_schema.with_info_after_validator_function(func, schema=schema) + else: + func = cast(core_schema.NoInfoValidatorFunction, self.func) + return core_schema.no_info_after_validator_function(func, schema=schema) + + @classmethod + def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self: + return cls(func=decorator.func) + + +@dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true) +class BeforeValidator: + """!!! abstract "Usage Documentation" + [field *before* validators](../concepts/validators.md#field-before-validator) + + A metadata class that indicates that a validation should be applied **before** the inner validation logic. + + Attributes: + func: The validator function. + json_schema_input_type: The input type used to generate the appropriate + JSON Schema (in validation mode). The actual input type is `Any`. + + Example: + ```python + from typing import Annotated + + from pydantic import BaseModel, BeforeValidator + + MyInt = Annotated[int, BeforeValidator(lambda v: v + 1)] + + class Model(BaseModel): + a: MyInt + + print(Model(a=1).a) + #> 2 + + try: + Model(a='a') + except TypeError as e: + print(e) + #> can only concatenate str (not "int") to str + ``` + """ + + func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction + json_schema_input_type: Any = PydanticUndefined + + def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + schema = handler(source_type) + input_schema = ( + None + if self.json_schema_input_type is PydanticUndefined + else handler.generate_schema(self.json_schema_input_type) + ) + + info_arg = _inspect_validator(self.func, mode='before', type='field') + if info_arg: + func = cast(core_schema.WithInfoValidatorFunction, self.func) + return core_schema.with_info_before_validator_function( + func, + schema=schema, + json_schema_input_schema=input_schema, + ) + else: + func = cast(core_schema.NoInfoValidatorFunction, self.func) + return core_schema.no_info_before_validator_function( + func, schema=schema, json_schema_input_schema=input_schema + ) + + @classmethod + def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self: + return cls( + func=decorator.func, + json_schema_input_type=decorator.info.json_schema_input_type, + ) + + +@dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true) +class PlainValidator: + """!!! abstract "Usage Documentation" + [field *plain* validators](../concepts/validators.md#field-plain-validator) + + A metadata class that indicates that a validation should be applied **instead** of the inner validation logic. + + !!! note + Before v2.9, `PlainValidator` wasn't always compatible with JSON Schema generation for `mode='validation'`. + You can now use the `json_schema_input_type` argument to specify the input type of the function + to be used in the JSON schema when `mode='validation'` (the default). See the example below for more details. + + Attributes: + func: The validator function. + json_schema_input_type: The input type used to generate the appropriate + JSON Schema (in validation mode). The actual input type is `Any`. + + Example: + ```python + from typing import Annotated, Union + + from pydantic import BaseModel, PlainValidator + + def validate(v: object) -> int: + if not isinstance(v, (int, str)): + raise ValueError(f'Expected int or str, go {type(v)}') + + return int(v) + 1 + + MyInt = Annotated[ + int, + PlainValidator(validate, json_schema_input_type=Union[str, int]), # (1)! + ] + + class Model(BaseModel): + a: MyInt + + print(Model(a='1').a) + #> 2 + + print(Model(a=1).a) + #> 2 + ``` + + 1. In this example, we've specified the `json_schema_input_type` as `Union[str, int]` which indicates to the JSON schema + generator that in validation mode, the input type for the `a` field can be either a [`str`][] or an [`int`][]. + """ + + func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction + json_schema_input_type: Any = Any + + def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + # Note that for some valid uses of PlainValidator, it is not possible to generate a core schema for the + # source_type, so calling `handler(source_type)` will error, which prevents us from generating a proper + # serialization schema. To work around this for use cases that will not involve serialization, we simply + # catch any PydanticSchemaGenerationError that may be raised while attempting to build the serialization schema + # and abort any attempts to handle special serialization. + from pydantic import PydanticSchemaGenerationError + + try: + schema = handler(source_type) + # TODO if `schema['serialization']` is one of `'include-exclude-dict/sequence', + # schema validation will fail. That's why we use 'type ignore' comments below. + serialization = schema.get( + 'serialization', + core_schema.wrap_serializer_function_ser_schema( + function=lambda v, h: h(v), + schema=schema, + return_schema=handler.generate_schema(source_type), + ), + ) + except PydanticSchemaGenerationError: + serialization = None + + input_schema = handler.generate_schema(self.json_schema_input_type) + + info_arg = _inspect_validator(self.func, mode='plain', type='field') + if info_arg: + func = cast(core_schema.WithInfoValidatorFunction, self.func) + return core_schema.with_info_plain_validator_function( + func, + serialization=serialization, # pyright: ignore[reportArgumentType] + json_schema_input_schema=input_schema, + ) + else: + func = cast(core_schema.NoInfoValidatorFunction, self.func) + return core_schema.no_info_plain_validator_function( + func, + serialization=serialization, # pyright: ignore[reportArgumentType] + json_schema_input_schema=input_schema, + ) + + @classmethod + def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self: + return cls( + func=decorator.func, + json_schema_input_type=decorator.info.json_schema_input_type, + ) + + +@dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true) +class WrapValidator: + """!!! abstract "Usage Documentation" + [field *wrap* validators](../concepts/validators.md#field-wrap-validator) + + A metadata class that indicates that a validation should be applied **around** the inner validation logic. + + Attributes: + func: The validator function. + json_schema_input_type: The input type used to generate the appropriate + JSON Schema (in validation mode). The actual input type is `Any`. + + ```python + from datetime import datetime + from typing import Annotated + + from pydantic import BaseModel, ValidationError, WrapValidator + + def validate_timestamp(v, handler): + if v == 'now': + # we don't want to bother with further validation, just return the new value + return datetime.now() + try: + return handler(v) + except ValidationError: + # validation failed, in this case we want to return a default value + return datetime(2000, 1, 1) + + MyTimestamp = Annotated[datetime, WrapValidator(validate_timestamp)] + + class Model(BaseModel): + a: MyTimestamp + + print(Model(a='now').a) + #> 2032-01-02 03:04:05.000006 + print(Model(a='invalid').a) + #> 2000-01-01 00:00:00 + ``` + """ + + func: core_schema.NoInfoWrapValidatorFunction | core_schema.WithInfoWrapValidatorFunction + json_schema_input_type: Any = PydanticUndefined + + def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + schema = handler(source_type) + input_schema = ( + None + if self.json_schema_input_type is PydanticUndefined + else handler.generate_schema(self.json_schema_input_type) + ) + + info_arg = _inspect_validator(self.func, mode='wrap', type='field') + if info_arg: + func = cast(core_schema.WithInfoWrapValidatorFunction, self.func) + return core_schema.with_info_wrap_validator_function( + func, + schema=schema, + json_schema_input_schema=input_schema, + ) + else: + func = cast(core_schema.NoInfoWrapValidatorFunction, self.func) + return core_schema.no_info_wrap_validator_function( + func, + schema=schema, + json_schema_input_schema=input_schema, + ) + + @classmethod + def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self: + return cls( + func=decorator.func, + json_schema_input_type=decorator.info.json_schema_input_type, + ) + + +if TYPE_CHECKING: + + class _OnlyValueValidatorClsMethod(Protocol): + def __call__(self, cls: Any, value: Any, /) -> Any: ... + + class _V2ValidatorClsMethod(Protocol): + def __call__(self, cls: Any, value: Any, info: core_schema.ValidationInfo[Any], /) -> Any: ... + + class _OnlyValueWrapValidatorClsMethod(Protocol): + def __call__(self, cls: Any, value: Any, handler: core_schema.ValidatorFunctionWrapHandler, /) -> Any: ... + + class _V2WrapValidatorClsMethod(Protocol): + def __call__( + self, + cls: Any, + value: Any, + handler: core_schema.ValidatorFunctionWrapHandler, + info: core_schema.ValidationInfo[Any], + /, + ) -> Any: ... + + _V2Validator = Union[ + _V2ValidatorClsMethod, + core_schema.WithInfoValidatorFunction, + _OnlyValueValidatorClsMethod, + core_schema.NoInfoValidatorFunction, + ] + + _V2WrapValidator = Union[ + _V2WrapValidatorClsMethod, + core_schema.WithInfoWrapValidatorFunction, + _OnlyValueWrapValidatorClsMethod, + core_schema.NoInfoWrapValidatorFunction, + ] + + _PartialClsOrStaticMethod: TypeAlias = Union[classmethod[Any, Any, Any], staticmethod[Any, Any], partialmethod[Any]] + + _V2BeforeAfterOrPlainValidatorType = TypeVar( + '_V2BeforeAfterOrPlainValidatorType', + bound=Union[_V2Validator, _PartialClsOrStaticMethod], + ) + _V2WrapValidatorType = TypeVar('_V2WrapValidatorType', bound=Union[_V2WrapValidator, _PartialClsOrStaticMethod]) + +FieldValidatorModes: TypeAlias = Literal['before', 'after', 'wrap', 'plain'] + + +@overload +def field_validator( + field: str, + /, + *fields: str, + mode: Literal['wrap'], + check_fields: bool | None = ..., + json_schema_input_type: Any = ..., +) -> Callable[[_V2WrapValidatorType], _V2WrapValidatorType]: ... + + +@overload +def field_validator( + field: str, + /, + *fields: str, + mode: Literal['before', 'plain'], + check_fields: bool | None = ..., + json_schema_input_type: Any = ..., +) -> Callable[[_V2BeforeAfterOrPlainValidatorType], _V2BeforeAfterOrPlainValidatorType]: ... + + +@overload +def field_validator( + field: str, + /, + *fields: str, + mode: Literal['after'] = ..., + check_fields: bool | None = ..., +) -> Callable[[_V2BeforeAfterOrPlainValidatorType], _V2BeforeAfterOrPlainValidatorType]: ... + + +def field_validator( + field: str, + /, + *fields: str, + mode: FieldValidatorModes = 'after', + check_fields: bool | None = None, + json_schema_input_type: Any = PydanticUndefined, +) -> Callable[[Any], Any]: + """!!! abstract "Usage Documentation" + [field validators](../concepts/validators.md#field-validators) + + Decorate methods on the class indicating that they should be used to validate fields. + + Example usage: + ```python + from typing import Any + + from pydantic import ( + BaseModel, + ValidationError, + field_validator, + ) + + class Model(BaseModel): + a: str + + @field_validator('a') + @classmethod + def ensure_foobar(cls, v: Any): + if 'foobar' not in v: + raise ValueError('"foobar" not found in a') + return v + + print(repr(Model(a='this is foobar good'))) + #> Model(a='this is foobar good') + + try: + Model(a='snap') + except ValidationError as exc_info: + print(exc_info) + ''' + 1 validation error for Model + a + Value error, "foobar" not found in a [type=value_error, input_value='snap', input_type=str] + ''' + ``` + + For more in depth examples, see [Field Validators](../concepts/validators.md#field-validators). + + Args: + field: The first field the `field_validator` should be called on; this is separate + from `fields` to ensure an error is raised if you don't pass at least one. + *fields: Additional field(s) the `field_validator` should be called on. + mode: Specifies whether to validate the fields before or after validation. + check_fields: Whether to check that the fields actually exist on the model. + json_schema_input_type: The input type of the function. This is only used to generate + the appropriate JSON Schema (in validation mode) and can only specified + when `mode` is either `'before'`, `'plain'` or `'wrap'`. + + Returns: + A decorator that can be used to decorate a function to be used as a field_validator. + + Raises: + PydanticUserError: + - If `@field_validator` is used bare (with no fields). + - If the args passed to `@field_validator` as fields are not strings. + - If `@field_validator` applied to instance methods. + """ + if isinstance(field, FunctionType): + raise PydanticUserError( + '`@field_validator` should be used with fields and keyword arguments, not bare. ' + "E.g. usage should be `@validator('', ...)`", + code='validator-no-fields', + ) + + if mode not in ('before', 'plain', 'wrap') and json_schema_input_type is not PydanticUndefined: + raise PydanticUserError( + f"`json_schema_input_type` can't be used when mode is set to {mode!r}", + code='validator-input-type', + ) + + if json_schema_input_type is PydanticUndefined and mode == 'plain': + json_schema_input_type = Any + + fields = field, *fields + if not all(isinstance(field, str) for field in fields): + raise PydanticUserError( + '`@field_validator` fields should be passed as separate string args. ' + "E.g. usage should be `@validator('', '', ...)`", + code='validator-invalid-fields', + ) + + def dec( + f: Callable[..., Any] | staticmethod[Any, Any] | classmethod[Any, Any, Any], + ) -> _decorators.PydanticDescriptorProxy[Any]: + if _decorators.is_instance_method_from_sig(f): + raise PydanticUserError( + '`@field_validator` cannot be applied to instance methods', code='validator-instance-method' + ) + + # auto apply the @classmethod decorator + f = _decorators.ensure_classmethod_based_on_signature(f) + + dec_info = _decorators.FieldValidatorDecoratorInfo( + fields=fields, mode=mode, check_fields=check_fields, json_schema_input_type=json_schema_input_type + ) + return _decorators.PydanticDescriptorProxy(f, dec_info) + + return dec + + +_ModelType = TypeVar('_ModelType') +_ModelTypeCo = TypeVar('_ModelTypeCo', covariant=True) + + +class ModelWrapValidatorHandler(core_schema.ValidatorFunctionWrapHandler, Protocol[_ModelTypeCo]): + """`@model_validator` decorated function handler argument type. This is used when `mode='wrap'`.""" + + def __call__( # noqa: D102 + self, + value: Any, + outer_location: str | int | None = None, + /, + ) -> _ModelTypeCo: # pragma: no cover + ... + + +class ModelWrapValidatorWithoutInfo(Protocol[_ModelType]): + """A `@model_validator` decorated function signature. + This is used when `mode='wrap'` and the function does not have info argument. + """ + + def __call__( # noqa: D102 + self, + cls: type[_ModelType], + # this can be a dict, a model instance + # or anything else that gets passed to validate_python + # thus validators _must_ handle all cases + value: Any, + handler: ModelWrapValidatorHandler[_ModelType], + /, + ) -> _ModelType: ... + + +class ModelWrapValidator(Protocol[_ModelType]): + """A `@model_validator` decorated function signature. This is used when `mode='wrap'`.""" + + def __call__( # noqa: D102 + self, + cls: type[_ModelType], + # this can be a dict, a model instance + # or anything else that gets passed to validate_python + # thus validators _must_ handle all cases + value: Any, + handler: ModelWrapValidatorHandler[_ModelType], + info: core_schema.ValidationInfo, + /, + ) -> _ModelType: ... + + +class FreeModelBeforeValidatorWithoutInfo(Protocol): + """A `@model_validator` decorated function signature. + This is used when `mode='before'` and the function does not have info argument. + """ + + def __call__( # noqa: D102 + self, + # this can be a dict, a model instance + # or anything else that gets passed to validate_python + # thus validators _must_ handle all cases + value: Any, + /, + ) -> Any: ... + + +class ModelBeforeValidatorWithoutInfo(Protocol): + """A `@model_validator` decorated function signature. + This is used when `mode='before'` and the function does not have info argument. + """ + + def __call__( # noqa: D102 + self, + cls: Any, + # this can be a dict, a model instance + # or anything else that gets passed to validate_python + # thus validators _must_ handle all cases + value: Any, + /, + ) -> Any: ... + + +class FreeModelBeforeValidator(Protocol): + """A `@model_validator` decorated function signature. This is used when `mode='before'`.""" + + def __call__( # noqa: D102 + self, + # this can be a dict, a model instance + # or anything else that gets passed to validate_python + # thus validators _must_ handle all cases + value: Any, + info: core_schema.ValidationInfo[Any], + /, + ) -> Any: ... + + +class ModelBeforeValidator(Protocol): + """A `@model_validator` decorated function signature. This is used when `mode='before'`.""" + + def __call__( # noqa: D102 + self, + cls: Any, + # this can be a dict, a model instance + # or anything else that gets passed to validate_python + # thus validators _must_ handle all cases + value: Any, + info: core_schema.ValidationInfo[Any], + /, + ) -> Any: ... + + +ModelAfterValidatorWithoutInfo = Callable[[_ModelType], _ModelType] +"""A `@model_validator` decorated function signature. This is used when `mode='after'` and the function does not +have info argument. +""" + +ModelAfterValidator = Callable[[_ModelType, core_schema.ValidationInfo[Any]], _ModelType] +"""A `@model_validator` decorated function signature. This is used when `mode='after'`.""" + +_AnyModelWrapValidator = Union[ModelWrapValidator[_ModelType], ModelWrapValidatorWithoutInfo[_ModelType]] +_AnyModelBeforeValidator = Union[ + FreeModelBeforeValidator, ModelBeforeValidator, FreeModelBeforeValidatorWithoutInfo, ModelBeforeValidatorWithoutInfo +] +_AnyModelAfterValidator = Union[ModelAfterValidator[_ModelType], ModelAfterValidatorWithoutInfo[_ModelType]] + + +@overload +def model_validator( + *, + mode: Literal['wrap'], +) -> Callable[ + [_AnyModelWrapValidator[_ModelType]], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo] +]: ... + + +@overload +def model_validator( + *, + mode: Literal['before'], +) -> Callable[ + [_AnyModelBeforeValidator], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo] +]: ... + + +@overload +def model_validator( + *, + mode: Literal['after'], +) -> Callable[ + [_AnyModelAfterValidator[_ModelType]], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo] +]: ... + + +def model_validator( + *, + mode: Literal['wrap', 'before', 'after'], +) -> Any: + """!!! abstract "Usage Documentation" + [Model Validators](../concepts/validators.md#model-validators) + + Decorate model methods for validation purposes. + + Example usage: + ```python + from typing_extensions import Self + + from pydantic import BaseModel, ValidationError, model_validator + + class Square(BaseModel): + width: float + height: float + + @model_validator(mode='after') + def verify_square(self) -> Self: + if self.width != self.height: + raise ValueError('width and height do not match') + return self + + s = Square(width=1, height=1) + print(repr(s)) + #> Square(width=1.0, height=1.0) + + try: + Square(width=1, height=2) + except ValidationError as e: + print(e) + ''' + 1 validation error for Square + Value error, width and height do not match [type=value_error, input_value={'width': 1, 'height': 2}, input_type=dict] + ''' + ``` + + For more in depth examples, see [Model Validators](../concepts/validators.md#model-validators). + + Args: + mode: A required string literal that specifies the validation mode. + It can be one of the following: 'wrap', 'before', or 'after'. + + Returns: + A decorator that can be used to decorate a function to be used as a model validator. + """ + + def dec(f: Any) -> _decorators.PydanticDescriptorProxy[Any]: + # auto apply the @classmethod decorator (except for *after* validators, which should be instance methods): + if mode != 'after': + f = _decorators.ensure_classmethod_based_on_signature(f) + dec_info = _decorators.ModelValidatorDecoratorInfo(mode=mode) + return _decorators.PydanticDescriptorProxy(f, dec_info) + + return dec + + +AnyType = TypeVar('AnyType') + + +if TYPE_CHECKING: + # If we add configurable attributes to IsInstance, we'd probably need to stop hiding it from type checkers like this + InstanceOf = Annotated[AnyType, ...] # `IsInstance[Sequence]` will be recognized by type checkers as `Sequence` + +else: + + @dataclasses.dataclass(**_internal_dataclass.slots_true) + class InstanceOf: + '''Generic type for annotating a type that is an instance of a given class. + + Example: + ```python + from pydantic import BaseModel, InstanceOf + + class Foo: + ... + + class Bar(BaseModel): + foo: InstanceOf[Foo] + + Bar(foo=Foo()) + try: + Bar(foo=42) + except ValidationError as e: + print(e) + """ + [ + │ { + │ │ 'type': 'is_instance_of', + │ │ 'loc': ('foo',), + │ │ 'msg': 'Input should be an instance of Foo', + │ │ 'input': 42, + │ │ 'ctx': {'class': 'Foo'}, + │ │ 'url': 'https://errors.pydantic.dev/0.38.0/v/is_instance_of' + │ } + ] + """ + ``` + ''' + + @classmethod + def __class_getitem__(cls, item: AnyType) -> AnyType: + return Annotated[item, cls()] + + @classmethod + def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + from pydantic import PydanticSchemaGenerationError + + # use the generic _origin_ as the second argument to isinstance when appropriate + instance_of_schema = core_schema.is_instance_schema(_generics.get_origin(source) or source) + + try: + # Try to generate the "standard" schema, which will be used when loading from JSON + original_schema = handler(source) + except PydanticSchemaGenerationError: + # If that fails, just produce a schema that can validate from python + return instance_of_schema + else: + # Use the "original" approach to serialization + instance_of_schema['serialization'] = core_schema.wrap_serializer_function_ser_schema( + function=lambda v, h: h(v), schema=original_schema + ) + return core_schema.json_or_python_schema(python_schema=instance_of_schema, json_schema=original_schema) + + __hash__ = object.__hash__ + + +if TYPE_CHECKING: + SkipValidation = Annotated[AnyType, ...] # SkipValidation[list[str]] will be treated by type checkers as list[str] +else: + + @dataclasses.dataclass(**_internal_dataclass.slots_true) + class SkipValidation: + """If this is applied as an annotation (e.g., via `x: Annotated[int, SkipValidation]`), validation will be + skipped. You can also use `SkipValidation[int]` as a shorthand for `Annotated[int, SkipValidation]`. + + This can be useful if you want to use a type annotation for documentation/IDE/type-checking purposes, + and know that it is safe to skip validation for one or more of the fields. + + Because this converts the validation schema to `any_schema`, subsequent annotation-applied transformations + may not have the expected effects. Therefore, when used, this annotation should generally be the final + annotation applied to a type. + """ + + def __class_getitem__(cls, item: Any) -> Any: + return Annotated[item, SkipValidation()] + + @classmethod + def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + with warnings.catch_warnings(): + warnings.simplefilter('ignore', ArbitraryTypeWarning) + original_schema = handler(source) + metadata = {'pydantic_js_annotation_functions': [lambda _c, h: h(original_schema)]} + return core_schema.any_schema( + metadata=metadata, + serialization=core_schema.wrap_serializer_function_ser_schema( + function=lambda v, h: h(v), schema=original_schema + ), + ) + + __hash__ = object.__hash__ + + +_FromTypeT = TypeVar('_FromTypeT') + + +class ValidateAs: + """A helper class to validate a custom type from a type that is natively supported by Pydantic. + + Args: + from_type: The type natively supported by Pydantic to use to perform validation. + instantiation_hook: A callable taking the validated type as an argument, and returning + the populated custom type. + + Example: + ```python {lint="skip"} + from typing import Annotated + + from pydantic import BaseModel, TypeAdapter, ValidateAs + + class MyCls: + def __init__(self, a: int) -> None: + self.a = a + + def __repr__(self) -> str: + return f"MyCls(a={self.a})" + + class Model(BaseModel): + a: int + + + ta = TypeAdapter( + Annotated[MyCls, ValidateAs(Model, lambda v: MyCls(a=v.a))] + ) + + print(ta.validate_python({'a': 1})) + #> MyCls(a=1) + ``` + """ + + # TODO: make use of PEP 747 + def __init__(self, from_type: type[_FromTypeT], /, instantiation_hook: Callable[[_FromTypeT], Any]) -> None: + self.from_type = from_type + self.instantiation_hook = instantiation_hook + + def __get_pydantic_core_schema__(self, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + schema = handler(self.from_type) + return core_schema.no_info_after_validator_function( + self.instantiation_hook, + schema=schema, + ) diff --git a/.venv/lib/python3.12/site-packages/pydantic/generics.py b/.venv/lib/python3.12/site-packages/pydantic/generics.py new file mode 100644 index 0000000000000000000000000000000000000000..3f1070d08f3ac5bd554794401734eb349373ab8e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/generics.py @@ -0,0 +1,5 @@ +"""The `generics` module is a backport module from V1.""" + +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.venv/lib/python3.12/site-packages/pydantic/json.py b/.venv/lib/python3.12/site-packages/pydantic/json.py new file mode 100644 index 0000000000000000000000000000000000000000..bcaff9f57958b8c0938255fd8f937293c5850cbd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/json.py @@ -0,0 +1,5 @@ +"""The `json` module is a backport module from V1.""" + +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.venv/lib/python3.12/site-packages/pydantic/json_schema.py b/.venv/lib/python3.12/site-packages/pydantic/json_schema.py new file mode 100644 index 0000000000000000000000000000000000000000..eb25c080a516a9f0b3d607f3c2ea13fd45c9ebd8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/json_schema.py @@ -0,0 +1,2848 @@ +"""!!! abstract "Usage Documentation" + [JSON Schema](../concepts/json_schema.md) + +The `json_schema` module contains classes and functions to allow the way [JSON Schema](https://json-schema.org/) +is generated to be customized. + +In general you shouldn't need to use this module directly; instead, you can use +[`BaseModel.model_json_schema`][pydantic.BaseModel.model_json_schema] and +[`TypeAdapter.json_schema`][pydantic.TypeAdapter.json_schema]. +""" + +from __future__ import annotations as _annotations + +import dataclasses +import inspect +import math +import os +import re +import warnings +from collections import Counter, defaultdict +from collections.abc import Hashable, Iterable, Sequence +from copy import deepcopy +from enum import Enum +from re import Pattern +from typing import ( + TYPE_CHECKING, + Annotated, + Any, + Callable, + Literal, + NewType, + TypeVar, + Union, + cast, + overload, +) + +import pydantic_core +from pydantic_core import MISSING, CoreSchema, PydanticOmit, core_schema, to_jsonable_python +from pydantic_core.core_schema import ComputedField +from typing_extensions import TypeAlias, assert_never, deprecated, final +from typing_inspection.introspection import get_literal_values + +from pydantic.warnings import PydanticDeprecatedSince26, PydanticDeprecatedSince29 + +from ._internal import ( + _config, + _core_metadata, + _core_utils, + _decorators, + _internal_dataclass, + _mock_val_ser, + _schema_generation_shared, +) +from .annotated_handlers import GetJsonSchemaHandler +from .config import JsonDict, JsonValue +from .errors import PydanticInvalidForJsonSchema, PydanticSchemaGenerationError, PydanticUserError + +if TYPE_CHECKING: + from . import ConfigDict + from ._internal._core_utils import CoreSchemaField, CoreSchemaOrField + from ._internal._dataclasses import PydanticDataclass + from ._internal._schema_generation_shared import GetJsonSchemaFunction + from .main import BaseModel + + +CoreSchemaOrFieldType = Literal[core_schema.CoreSchemaType, core_schema.CoreSchemaFieldType] +""" +A type alias for defined schema types that represents a union of +`core_schema.CoreSchemaType` and +`core_schema.CoreSchemaFieldType`. +""" + +JsonSchemaValue = dict[str, Any] +""" +A type alias for a JSON schema value. This is a dictionary of string keys to arbitrary JSON values. +""" + +JsonSchemaMode = Literal['validation', 'serialization'] +""" +A type alias that represents the mode of a JSON schema; either 'validation' or 'serialization'. + +For some types, the inputs to validation differ from the outputs of serialization. For example, +computed fields will only be present when serializing, and should not be provided when +validating. This flag provides a way to indicate whether you want the JSON schema required +for validation inputs, or that will be matched by serialization outputs. +""" + +_MODE_TITLE_MAPPING: dict[JsonSchemaMode, str] = {'validation': 'Input', 'serialization': 'Output'} + + +JsonSchemaWarningKind = Literal['skipped-choice', 'non-serializable-default', 'skipped-discriminator'] +""" +A type alias representing the kinds of warnings that can be emitted during JSON schema generation. + +See [`GenerateJsonSchema.render_warning_message`][pydantic.json_schema.GenerateJsonSchema.render_warning_message] +for more details. +""" + + +class PydanticJsonSchemaWarning(UserWarning): + """This class is used to emit warnings produced during JSON schema generation. + See the [`GenerateJsonSchema.emit_warning`][pydantic.json_schema.GenerateJsonSchema.emit_warning] and + [`GenerateJsonSchema.render_warning_message`][pydantic.json_schema.GenerateJsonSchema.render_warning_message] + methods for more details; these can be overridden to control warning behavior. + """ + + +NoDefault = object() +"""A sentinel value used to indicate that no default value should be used when generating a JSON Schema +for a core schema with a default value. +""" + + +# ##### JSON Schema Generation ##### +DEFAULT_REF_TEMPLATE = '#/$defs/{model}' +"""The default format string used to generate reference names.""" + +# There are three types of references relevant to building JSON schemas: +# 1. core_schema "ref" values; these are not exposed as part of the JSON schema +# * these might look like the fully qualified path of a model, its id, or something similar +CoreRef = NewType('CoreRef', str) +# 2. keys of the "definitions" object that will eventually go into the JSON schema +# * by default, these look like "MyModel", though may change in the presence of collisions +# * eventually, we may want to make it easier to modify the way these names are generated +DefsRef = NewType('DefsRef', str) +# 3. the values corresponding to the "$ref" key in the schema +# * By default, these look like "#/$defs/MyModel", as in {"$ref": "#/$defs/MyModel"} +JsonRef = NewType('JsonRef', str) + +CoreModeRef = tuple[CoreRef, JsonSchemaMode] +JsonSchemaKeyT = TypeVar('JsonSchemaKeyT', bound=Hashable) + +_PRIMITIVE_JSON_SCHEMA_TYPES = ('string', 'boolean', 'null', 'integer', 'number') + + +@dataclasses.dataclass(**_internal_dataclass.slots_true) +class _DefinitionsRemapping: + defs_remapping: dict[DefsRef, DefsRef] + json_remapping: dict[JsonRef, JsonRef] + + @staticmethod + def from_prioritized_choices( + prioritized_choices: dict[DefsRef, list[DefsRef]], + defs_to_json: dict[DefsRef, JsonRef], + definitions: dict[DefsRef, JsonSchemaValue], + ) -> _DefinitionsRemapping: + """ + This function should produce a remapping that replaces complex DefsRef with the simpler ones from the + prioritized_choices such that applying the name remapping would result in an equivalent JSON schema. + """ + # We need to iteratively simplify the definitions until we reach a fixed point. + # The reason for this is that outer definitions may reference inner definitions that get simplified + # into an equivalent reference, and the outer definitions won't be equivalent until we've simplified + # the inner definitions. + copied_definitions = deepcopy(definitions) + definitions_schema = {'$defs': copied_definitions} + for _iter in range(100): # prevent an infinite loop in the case of a bug, 100 iterations should be enough + # For every possible remapped DefsRef, collect all schemas that that DefsRef might be used for: + schemas_for_alternatives: dict[DefsRef, list[JsonSchemaValue]] = defaultdict(list) + for defs_ref in copied_definitions: + alternatives = prioritized_choices[defs_ref] + for alternative in alternatives: + schemas_for_alternatives[alternative].append(copied_definitions[defs_ref]) + + # Deduplicate the schemas for each alternative; the idea is that we only want to remap to a new DefsRef + # if it introduces no ambiguity, i.e., there is only one distinct schema for that DefsRef. + for defs_ref in schemas_for_alternatives: + schemas_for_alternatives[defs_ref] = _deduplicate_schemas(schemas_for_alternatives[defs_ref]) + + # Build the remapping + defs_remapping: dict[DefsRef, DefsRef] = {} + json_remapping: dict[JsonRef, JsonRef] = {} + for original_defs_ref in definitions: + alternatives = prioritized_choices[original_defs_ref] + # Pick the first alternative that has only one schema, since that means there is no collision + remapped_defs_ref = next(x for x in alternatives if len(schemas_for_alternatives[x]) == 1) + defs_remapping[original_defs_ref] = remapped_defs_ref + json_remapping[defs_to_json[original_defs_ref]] = defs_to_json[remapped_defs_ref] + remapping = _DefinitionsRemapping(defs_remapping, json_remapping) + new_definitions_schema = remapping.remap_json_schema({'$defs': copied_definitions}) + if definitions_schema == new_definitions_schema: + # We've reached the fixed point + return remapping + definitions_schema = new_definitions_schema + + raise PydanticInvalidForJsonSchema('Failed to simplify the JSON schema definitions') + + def remap_defs_ref(self, ref: DefsRef) -> DefsRef: + return self.defs_remapping.get(ref, ref) + + def remap_json_ref(self, ref: JsonRef) -> JsonRef: + return self.json_remapping.get(ref, ref) + + def remap_json_schema(self, schema: Any) -> Any: + """ + Recursively update the JSON schema replacing all $refs + """ + if isinstance(schema, str): + # Note: this may not really be a JsonRef; we rely on having no collisions between JsonRefs and other strings + return self.remap_json_ref(JsonRef(schema)) + elif isinstance(schema, list): + return [self.remap_json_schema(item) for item in schema] + elif isinstance(schema, dict): + for key, value in schema.items(): + if key == '$ref' and isinstance(value, str): + schema['$ref'] = self.remap_json_ref(JsonRef(value)) + elif key == '$defs': + schema['$defs'] = { + self.remap_defs_ref(DefsRef(key)): self.remap_json_schema(value) + for key, value in schema['$defs'].items() + } + else: + schema[key] = self.remap_json_schema(value) + return schema + + +class GenerateJsonSchema: + """!!! abstract "Usage Documentation" + [Customizing the JSON Schema Generation Process](../concepts/json_schema.md#customizing-the-json-schema-generation-process) + + A class for generating JSON schemas. + + This class generates JSON schemas based on configured parameters. The default schema dialect + is [https://json-schema.org/draft/2020-12/schema](https://json-schema.org/draft/2020-12/schema). + The class uses `by_alias` to configure how fields with + multiple names are handled and `ref_template` to format reference names. + + Attributes: + schema_dialect: The JSON schema dialect used to generate the schema. See + [Declaring a Dialect](https://json-schema.org/understanding-json-schema/reference/schema.html#id4) + in the JSON Schema documentation for more information about dialects. + ignored_warning_kinds: Warnings to ignore when generating the schema. `self.render_warning_message` will + do nothing if its argument `kind` is in `ignored_warning_kinds`; + this value can be modified on subclasses to easily control which warnings are emitted. + by_alias: Whether to use field aliases when generating the schema. + ref_template: The format string used when generating reference names. + core_to_json_refs: A mapping of core refs to JSON refs. + core_to_defs_refs: A mapping of core refs to definition refs. + defs_to_core_refs: A mapping of definition refs to core refs. + json_to_defs_refs: A mapping of JSON refs to definition refs. + definitions: Definitions in the schema. + + Args: + by_alias: Whether to use field aliases in the generated schemas. + ref_template: The format string to use when generating reference names. + union_format: The format to use when combining schemas from unions together. Can be one of: + + - `'any_of'`: Use the [`anyOf`](https://json-schema.org/understanding-json-schema/reference/combining#anyOf) + keyword to combine schemas (the default). + - `'primitive_type_array'`: Use the [`type`](https://json-schema.org/understanding-json-schema/reference/type) + keyword as an array of strings, containing each type of the combination. If any of the schemas is not a primitive + type (`string`, `boolean`, `null`, `integer` or `number`) or contains constraints/metadata, falls back to + `any_of`. + + Raises: + JsonSchemaError: If the instance of the class is inadvertently reused after generating a schema. + """ + + schema_dialect = 'https://json-schema.org/draft/2020-12/schema' + + # `self.render_warning_message` will do nothing if its argument `kind` is in `ignored_warning_kinds`; + # this value can be modified on subclasses to easily control which warnings are emitted + ignored_warning_kinds: set[JsonSchemaWarningKind] = {'skipped-choice'} + + def __init__( + self, + by_alias: bool = True, + ref_template: str = DEFAULT_REF_TEMPLATE, + union_format: Literal['any_of', 'primitive_type_array'] = 'any_of', + ) -> None: + self.by_alias = by_alias + self.ref_template = ref_template + self.union_format: Literal['any_of', 'primitive_type_array'] = union_format + + self.core_to_json_refs: dict[CoreModeRef, JsonRef] = {} + self.core_to_defs_refs: dict[CoreModeRef, DefsRef] = {} + self.defs_to_core_refs: dict[DefsRef, CoreModeRef] = {} + self.json_to_defs_refs: dict[JsonRef, DefsRef] = {} + + self.definitions: dict[DefsRef, JsonSchemaValue] = {} + self._config_wrapper_stack = _config.ConfigWrapperStack(_config.ConfigWrapper({})) + + self._mode: JsonSchemaMode = 'validation' + + # The following includes a mapping of a fully-unique defs ref choice to a list of preferred + # alternatives, which are generally simpler, such as only including the class name. + # At the end of schema generation, we use these to produce a JSON schema with more human-readable + # definitions, which would also work better in a generated OpenAPI client, etc. + self._prioritized_defsref_choices: dict[DefsRef, list[DefsRef]] = {} + self._collision_counter: dict[str, int] = defaultdict(int) + self._collision_index: dict[str, int] = {} + + self._schema_type_to_method = self.build_schema_type_to_method() + + # When we encounter definitions we need to try to build them immediately + # so that they are available schemas that reference them + # But it's possible that CoreSchema was never going to be used + # (e.g. because the CoreSchema that references short circuits is JSON schema generation without needing + # the reference) so instead of failing altogether if we can't build a definition we + # store the error raised and re-throw it if we end up needing that def + self._core_defs_invalid_for_json_schema: dict[DefsRef, PydanticInvalidForJsonSchema] = {} + + # This changes to True after generating a schema, to prevent issues caused by accidental reuse + # of a single instance of a schema generator + self._used = False + + @property + def _config(self) -> _config.ConfigWrapper: + return self._config_wrapper_stack.tail + + @property + def mode(self) -> JsonSchemaMode: + if self._config.json_schema_mode_override is not None: + return self._config.json_schema_mode_override + else: + return self._mode + + def build_schema_type_to_method( + self, + ) -> dict[CoreSchemaOrFieldType, Callable[[CoreSchemaOrField], JsonSchemaValue]]: + """Builds a dictionary mapping fields to methods for generating JSON schemas. + + Returns: + A dictionary containing the mapping of `CoreSchemaOrFieldType` to a handler method. + + Raises: + TypeError: If no method has been defined for generating a JSON schema for a given pydantic core schema type. + """ + mapping: dict[CoreSchemaOrFieldType, Callable[[CoreSchemaOrField], JsonSchemaValue]] = {} + core_schema_types: list[CoreSchemaOrFieldType] = list(get_literal_values(CoreSchemaOrFieldType)) + for key in core_schema_types: + method_name = f'{key.replace("-", "_")}_schema' + try: + mapping[key] = getattr(self, method_name) + except AttributeError as e: # pragma: no cover + if os.getenv('PYDANTIC_PRIVATE_ALLOW_UNHANDLED_SCHEMA_TYPES'): + continue + raise TypeError( + f'No method for generating JsonSchema for core_schema.type={key!r} ' + f'(expected: {type(self).__name__}.{method_name})' + ) from e + return mapping + + def generate_definitions( + self, inputs: Sequence[tuple[JsonSchemaKeyT, JsonSchemaMode, core_schema.CoreSchema]] + ) -> tuple[dict[tuple[JsonSchemaKeyT, JsonSchemaMode], JsonSchemaValue], dict[DefsRef, JsonSchemaValue]]: + """Generates JSON schema definitions from a list of core schemas, pairing the generated definitions with a + mapping that links the input keys to the definition references. + + Args: + inputs: A sequence of tuples, where: + + - The first element is a JSON schema key type. + - The second element is the JSON mode: either 'validation' or 'serialization'. + - The third element is a core schema. + + Returns: + A tuple where: + + - The first element is a dictionary whose keys are tuples of JSON schema key type and JSON mode, and + whose values are the JSON schema corresponding to that pair of inputs. (These schemas may have + JsonRef references to definitions that are defined in the second returned element.) + - The second element is a dictionary whose keys are definition references for the JSON schemas + from the first returned element, and whose values are the actual JSON schema definitions. + + Raises: + PydanticUserError: Raised if the JSON schema generator has already been used to generate a JSON schema. + """ + if self._used: + raise PydanticUserError( + 'This JSON schema generator has already been used to generate a JSON schema. ' + f'You must create a new instance of {type(self).__name__} to generate a new JSON schema.', + code='json-schema-already-used', + ) + + for _, mode, schema in inputs: + self._mode = mode + self.generate_inner(schema) + + definitions_remapping = self._build_definitions_remapping() + + json_schemas_map: dict[tuple[JsonSchemaKeyT, JsonSchemaMode], DefsRef] = {} + for key, mode, schema in inputs: + self._mode = mode + json_schema = self.generate_inner(schema) + json_schemas_map[(key, mode)] = definitions_remapping.remap_json_schema(json_schema) + + json_schema = {'$defs': self.definitions} + json_schema = definitions_remapping.remap_json_schema(json_schema) + self._used = True + return json_schemas_map, self.sort(json_schema['$defs']) # type: ignore + + def generate(self, schema: CoreSchema, mode: JsonSchemaMode = 'validation') -> JsonSchemaValue: + """Generates a JSON schema for a specified schema in a specified mode. + + Args: + schema: A Pydantic model. + mode: The mode in which to generate the schema. Defaults to 'validation'. + + Returns: + A JSON schema representing the specified schema. + + Raises: + PydanticUserError: If the JSON schema generator has already been used to generate a JSON schema. + """ + self._mode = mode + if self._used: + raise PydanticUserError( + 'This JSON schema generator has already been used to generate a JSON schema. ' + f'You must create a new instance of {type(self).__name__} to generate a new JSON schema.', + code='json-schema-already-used', + ) + + json_schema: JsonSchemaValue = self.generate_inner(schema) + json_ref_counts = self.get_json_ref_counts(json_schema) + + ref = cast(JsonRef, json_schema.get('$ref')) + while ref is not None: # may need to unpack multiple levels + ref_json_schema = self.get_schema_from_definitions(ref) + if json_ref_counts[ref] == 1 and ref_json_schema is not None and len(json_schema) == 1: + # "Unpack" the ref since this is the only reference and there are no sibling keys + json_schema = ref_json_schema.copy() # copy to prevent recursive dict reference + json_ref_counts[ref] -= 1 + ref = cast(JsonRef, json_schema.get('$ref')) + ref = None + + self._garbage_collect_definitions(json_schema) + definitions_remapping = self._build_definitions_remapping() + + if self.definitions: + json_schema['$defs'] = self.definitions + + json_schema = definitions_remapping.remap_json_schema(json_schema) + + # For now, we will not set the $schema key. However, if desired, this can be easily added by overriding + # this method and adding the following line after a call to super().generate(schema): + # json_schema['$schema'] = self.schema_dialect + + self._used = True + return self.sort(json_schema) + + def generate_inner(self, schema: CoreSchemaOrField) -> JsonSchemaValue: # noqa: C901 + """Generates a JSON schema for a given core schema. + + Args: + schema: The given core schema. + + Returns: + The generated JSON schema. + + TODO: the nested function definitions here seem like bad practice, I'd like to unpack these + in a future PR. It'd be great if we could shorten the call stack a bit for JSON schema generation, + and I think there's potential for that here. + """ + # If a schema with the same CoreRef has been handled, just return a reference to it + # Note that this assumes that it will _never_ be the case that the same CoreRef is used + # on types that should have different JSON schemas + if 'ref' in schema: + core_ref = CoreRef(schema['ref']) # type: ignore[typeddict-item] + core_mode_ref = (core_ref, self.mode) + if core_mode_ref in self.core_to_defs_refs and self.core_to_defs_refs[core_mode_ref] in self.definitions: + return {'$ref': self.core_to_json_refs[core_mode_ref]} + + def populate_defs(core_schema: CoreSchema, json_schema: JsonSchemaValue) -> JsonSchemaValue: + if 'ref' in core_schema: + core_ref = CoreRef(core_schema['ref']) # type: ignore[typeddict-item] + defs_ref, ref_json_schema = self.get_cache_defs_ref_schema(core_ref) + json_ref = JsonRef(ref_json_schema['$ref']) + # Replace the schema if it's not a reference to itself + # What we want to avoid is having the def be just a ref to itself + # which is what would happen if we blindly assigned any + if json_schema.get('$ref', None) != json_ref: + self.definitions[defs_ref] = json_schema + self._core_defs_invalid_for_json_schema.pop(defs_ref, None) + json_schema = ref_json_schema + return json_schema + + def handler_func(schema_or_field: CoreSchemaOrField) -> JsonSchemaValue: + """Generate a JSON schema based on the input schema. + + Args: + schema_or_field: The core schema to generate a JSON schema from. + + Returns: + The generated JSON schema. + + Raises: + TypeError: If an unexpected schema type is encountered. + """ + # Generate the core-schema-type-specific bits of the schema generation: + json_schema: JsonSchemaValue | None = None + if self.mode == 'serialization' and 'serialization' in schema_or_field: + # In this case, we skip the JSON Schema generation of the schema + # and use the `'serialization'` schema instead (canonical example: + # `Annotated[int, PlainSerializer(str)]`). + ser_schema = schema_or_field['serialization'] # type: ignore + json_schema = self.ser_schema(ser_schema) + + # It might be that the 'serialization'` is skipped depending on `when_used`. + # This is only relevant for `nullable` schemas though, so we special case here. + if ( + json_schema is not None + and ser_schema.get('when_used') in ('unless-none', 'json-unless-none') + and schema_or_field['type'] == 'nullable' + ): + json_schema = self.get_union_of_schemas([{'type': 'null'}, json_schema]) + if json_schema is None: + if _core_utils.is_core_schema(schema_or_field) or _core_utils.is_core_schema_field(schema_or_field): + generate_for_schema_type = self._schema_type_to_method[schema_or_field['type']] + json_schema = generate_for_schema_type(schema_or_field) + else: + raise TypeError(f'Unexpected schema type: schema={schema_or_field}') + return json_schema + + current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, handler_func) + + metadata = cast(_core_metadata.CoreMetadata, schema.get('metadata', {})) + + # TODO: I dislike that we have to wrap these basic dict updates in callables, is there any way around this? + + if js_updates := metadata.get('pydantic_js_updates'): + + def js_updates_handler_func( + schema_or_field: CoreSchemaOrField, + current_handler: GetJsonSchemaHandler = current_handler, + ) -> JsonSchemaValue: + json_schema = {**current_handler(schema_or_field), **js_updates} + return json_schema + + current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, js_updates_handler_func) + + if js_extra := metadata.get('pydantic_js_extra'): + + def js_extra_handler_func( + schema_or_field: CoreSchemaOrField, + current_handler: GetJsonSchemaHandler = current_handler, + ) -> JsonSchemaValue: + json_schema = current_handler(schema_or_field) + if isinstance(js_extra, dict): + json_schema.update(to_jsonable_python(js_extra)) + elif callable(js_extra): + # similar to typing issue in _update_class_schema when we're working with callable js extra + js_extra(json_schema) # type: ignore + return json_schema + + current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, js_extra_handler_func) + + for js_modify_function in metadata.get('pydantic_js_functions', ()): + + def new_handler_func( + schema_or_field: CoreSchemaOrField, + current_handler: GetJsonSchemaHandler = current_handler, + js_modify_function: GetJsonSchemaFunction = js_modify_function, + ) -> JsonSchemaValue: + json_schema = js_modify_function(schema_or_field, current_handler) + if _core_utils.is_core_schema(schema_or_field): + json_schema = populate_defs(schema_or_field, json_schema) + original_schema = current_handler.resolve_ref_schema(json_schema) + ref = json_schema.pop('$ref', None) + if ref and json_schema: + original_schema.update(json_schema) + return original_schema + + current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, new_handler_func) + + for js_modify_function in metadata.get('pydantic_js_annotation_functions', ()): + + def new_handler_func( + schema_or_field: CoreSchemaOrField, + current_handler: GetJsonSchemaHandler = current_handler, + js_modify_function: GetJsonSchemaFunction = js_modify_function, + ) -> JsonSchemaValue: + return js_modify_function(schema_or_field, current_handler) + + current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, new_handler_func) + + json_schema = current_handler(schema) + if _core_utils.is_core_schema(schema): + json_schema = populate_defs(schema, json_schema) + return json_schema + + def sort(self, value: JsonSchemaValue, parent_key: str | None = None) -> JsonSchemaValue: + """Override this method to customize the sorting of the JSON schema (e.g., don't sort at all, sort all keys unconditionally, etc.) + + By default, alphabetically sort the keys in the JSON schema, skipping the 'properties' and 'default' keys to preserve field definition order. + This sort is recursive, so it will sort all nested dictionaries as well. + """ + sorted_dict: dict[str, JsonSchemaValue] = {} + keys = value.keys() + if parent_key not in ('properties', 'default'): + keys = sorted(keys) + for key in keys: + sorted_dict[key] = self._sort_recursive(value[key], parent_key=key) + return sorted_dict + + def _sort_recursive(self, value: Any, parent_key: str | None = None) -> Any: + """Recursively sort a JSON schema value.""" + if isinstance(value, dict): + sorted_dict: dict[str, JsonSchemaValue] = {} + keys = value.keys() + if parent_key not in ('properties', 'default'): + keys = sorted(keys) + for key in keys: + sorted_dict[key] = self._sort_recursive(value[key], parent_key=key) + return sorted_dict + elif isinstance(value, list): + sorted_list: list[JsonSchemaValue] = [self._sort_recursive(item, parent_key) for item in value] + return sorted_list + else: + return value + + # ### Schema generation methods + + def invalid_schema(self, schema: core_schema.InvalidSchema) -> JsonSchemaValue: + """Placeholder - should never be called.""" + + raise RuntimeError('Cannot generate schema for invalid_schema. This is a bug! Please report it.') + + def any_schema(self, schema: core_schema.AnySchema) -> JsonSchemaValue: + """Generates a JSON schema that matches any value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return {} + + def none_schema(self, schema: core_schema.NoneSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches `None`. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return {'type': 'null'} + + def bool_schema(self, schema: core_schema.BoolSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a bool value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return {'type': 'boolean'} + + def int_schema(self, schema: core_schema.IntSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches an int value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + json_schema: dict[str, Any] = {'type': 'integer'} + self.update_with_validations(json_schema, schema, self.ValidationsMapping.numeric) + json_schema = {k: v for k, v in json_schema.items() if v not in {math.inf, -math.inf}} + return json_schema + + def float_schema(self, schema: core_schema.FloatSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a float value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + json_schema: dict[str, Any] = {'type': 'number'} + self.update_with_validations(json_schema, schema, self.ValidationsMapping.numeric) + json_schema = {k: v for k, v in json_schema.items() if v not in {math.inf, -math.inf}} + return json_schema + + def decimal_schema(self, schema: core_schema.DecimalSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a decimal value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + + def get_decimal_pattern(schema: core_schema.DecimalSchema) -> str: + max_digits = schema.get('max_digits') + decimal_places = schema.get('decimal_places') + + pattern = ( + r'^(?!^[-+.]*$)[+-]?0*' # check it is not empty string and not one or sequence of ".+-" characters. + ) + + # Case 1: Both max_digits and decimal_places are set + if max_digits is not None and decimal_places is not None: + integer_places = max(0, max_digits - decimal_places) + pattern += ( + rf'(?:' + rf'\d{{0,{integer_places}}}' + rf'|' + rf'(?=[\d.]{{1,{max_digits + 1}}}0*$)' + rf'\d{{0,{integer_places}}}\.\d{{0,{decimal_places}}}0*$' + rf')' + ) + + # Case 2: Only max_digits is set + elif max_digits is not None and decimal_places is None: + pattern += ( + rf'(?:' + rf'\d{{0,{max_digits}}}' + rf'|' + rf'(?=[\d.]{{1,{max_digits + 1}}}0*$)' + rf'\d*\.\d*0*$' + rf')' + ) + + # Case 3: Only decimal_places is set + elif max_digits is None and decimal_places is not None: + pattern += rf'\d*\.?\d{{0,{decimal_places}}}0*$' + + # Case 4: Both are None (no restrictions) + else: + pattern += r'\d*\.?\d*$' # look for arbitrary integer or decimal + + return pattern + + json_schema = self.str_schema(core_schema.str_schema(pattern=get_decimal_pattern(schema))) + if self.mode == 'validation': + multiple_of = schema.get('multiple_of') + le = schema.get('le') + ge = schema.get('ge') + lt = schema.get('lt') + gt = schema.get('gt') + json_schema = { + 'anyOf': [ + self.float_schema( + core_schema.float_schema( + allow_inf_nan=schema.get('allow_inf_nan'), + multiple_of=None if multiple_of is None else float(multiple_of), + le=None if le is None else float(le), + ge=None if ge is None else float(ge), + lt=None if lt is None else float(lt), + gt=None if gt is None else float(gt), + ) + ), + json_schema, + ], + } + return json_schema + + def str_schema(self, schema: core_schema.StringSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a string value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + json_schema = {'type': 'string'} + self.update_with_validations(json_schema, schema, self.ValidationsMapping.string) + if isinstance(json_schema.get('pattern'), Pattern): + # TODO: should we add regex flags to the pattern? + json_schema['pattern'] = json_schema.get('pattern').pattern # type: ignore + return json_schema + + def bytes_schema(self, schema: core_schema.BytesSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a bytes value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + json_schema = {'type': 'string', 'format': 'base64url' if self._config.ser_json_bytes == 'base64' else 'binary'} + self.update_with_validations(json_schema, schema, self.ValidationsMapping.bytes) + return json_schema + + def date_schema(self, schema: core_schema.DateSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a date value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return {'type': 'string', 'format': 'date'} + + def time_schema(self, schema: core_schema.TimeSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a time value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return {'type': 'string', 'format': 'time'} + + def datetime_schema(self, schema: core_schema.DatetimeSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a datetime value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return {'type': 'string', 'format': 'date-time'} + + def timedelta_schema(self, schema: core_schema.TimedeltaSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a timedelta value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + if self._config.ser_json_timedelta == 'float': + return {'type': 'number'} + return {'type': 'string', 'format': 'duration'} + + def literal_schema(self, schema: core_schema.LiteralSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a literal value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + expected = [to_jsonable_python(v.value if isinstance(v, Enum) else v) for v in schema['expected']] + + result: dict[str, Any] = {} + if len(expected) == 1: + result['const'] = expected[0] + else: + result['enum'] = expected + + types = {type(e) for e in expected} + if types == {str}: + result['type'] = 'string' + elif types == {int}: + result['type'] = 'integer' + elif types == {float}: + result['type'] = 'number' + elif types == {bool}: + result['type'] = 'boolean' + elif types == {list}: + result['type'] = 'array' + elif types == {type(None)}: + result['type'] = 'null' + return result + + def missing_sentinel_schema(self, schema: core_schema.MissingSentinelSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches the `MISSING` sentinel value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + raise PydanticOmit + + def enum_schema(self, schema: core_schema.EnumSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches an Enum value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + enum_type = schema['cls'] + description = None if not enum_type.__doc__ else inspect.cleandoc(enum_type.__doc__) + if ( + description == 'An enumeration.' + ): # This is the default value provided by enum.EnumMeta.__new__; don't use it + description = None + result: dict[str, Any] = {'title': enum_type.__name__, 'description': description} + result = {k: v for k, v in result.items() if v is not None} + + expected = [to_jsonable_python(v.value) for v in schema['members']] + + result['enum'] = expected + + types = {type(e) for e in expected} + if isinstance(enum_type, str) or types == {str}: + result['type'] = 'string' + elif isinstance(enum_type, int) or types == {int}: + result['type'] = 'integer' + elif isinstance(enum_type, float) or types == {float}: + result['type'] = 'number' + elif types == {bool}: + result['type'] = 'boolean' + elif types == {list}: + result['type'] = 'array' + + return result + + def is_instance_schema(self, schema: core_schema.IsInstanceSchema) -> JsonSchemaValue: + """Handles JSON schema generation for a core schema that checks if a value is an instance of a class. + + Unless overridden in a subclass, this raises an error. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self.handle_invalid_for_json_schema(schema, f'core_schema.IsInstanceSchema ({schema["cls"]})') + + def is_subclass_schema(self, schema: core_schema.IsSubclassSchema) -> JsonSchemaValue: + """Handles JSON schema generation for a core schema that checks if a value is a subclass of a class. + + For backwards compatibility with v1, this does not raise an error, but can be overridden to change this. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + # Note: This is for compatibility with V1; you can override if you want different behavior. + return {} + + def callable_schema(self, schema: core_schema.CallableSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a callable value. + + Unless overridden in a subclass, this raises an error. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self.handle_invalid_for_json_schema(schema, 'core_schema.CallableSchema') + + def list_schema(self, schema: core_schema.ListSchema) -> JsonSchemaValue: + """Returns a schema that matches a list schema. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema']) + json_schema = {'type': 'array', 'items': items_schema} + self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) + return json_schema + + @deprecated('`tuple_positional_schema` is deprecated. Use `tuple_schema` instead.', category=None) + @final + def tuple_positional_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue: + """Replaced by `tuple_schema`.""" + warnings.warn( + '`tuple_positional_schema` is deprecated. Use `tuple_schema` instead.', + PydanticDeprecatedSince26, + stacklevel=2, + ) + return self.tuple_schema(schema) + + @deprecated('`tuple_variable_schema` is deprecated. Use `tuple_schema` instead.', category=None) + @final + def tuple_variable_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue: + """Replaced by `tuple_schema`.""" + warnings.warn( + '`tuple_variable_schema` is deprecated. Use `tuple_schema` instead.', + PydanticDeprecatedSince26, + stacklevel=2, + ) + return self.tuple_schema(schema) + + def tuple_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a tuple schema e.g. `tuple[int, + str, bool]` or `tuple[int, ...]`. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + json_schema: JsonSchemaValue = {'type': 'array'} + if 'variadic_item_index' in schema: + variadic_item_index = schema['variadic_item_index'] + if variadic_item_index > 0: + json_schema['minItems'] = variadic_item_index + json_schema['prefixItems'] = [ + self.generate_inner(item) for item in schema['items_schema'][:variadic_item_index] + ] + if variadic_item_index + 1 == len(schema['items_schema']): + # if the variadic item is the last item, then represent it faithfully + json_schema['items'] = self.generate_inner(schema['items_schema'][variadic_item_index]) + else: + # otherwise, 'items' represents the schema for the variadic + # item plus the suffix, so just allow anything for simplicity + # for now + json_schema['items'] = True + else: + prefixItems = [self.generate_inner(item) for item in schema['items_schema']] + if prefixItems: + json_schema['prefixItems'] = prefixItems + json_schema['minItems'] = len(prefixItems) + json_schema['maxItems'] = len(prefixItems) + self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) + return json_schema + + def set_schema(self, schema: core_schema.SetSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a set schema. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self._common_set_schema(schema) + + def frozenset_schema(self, schema: core_schema.FrozenSetSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a frozenset schema. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self._common_set_schema(schema) + + def _common_set_schema(self, schema: core_schema.SetSchema | core_schema.FrozenSetSchema) -> JsonSchemaValue: + items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema']) + json_schema = {'type': 'array', 'uniqueItems': True, 'items': items_schema} + self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) + return json_schema + + def generator_schema(self, schema: core_schema.GeneratorSchema) -> JsonSchemaValue: + """Returns a JSON schema that represents the provided GeneratorSchema. + + Args: + schema: The schema. + + Returns: + The generated JSON schema. + """ + items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema']) + json_schema = {'type': 'array', 'items': items_schema} + self.update_with_validations(json_schema, schema, self.ValidationsMapping.array) + return json_schema + + def dict_schema(self, schema: core_schema.DictSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a dict schema. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + json_schema: JsonSchemaValue = {'type': 'object'} + + keys_schema = self.generate_inner(schema['keys_schema']).copy() if 'keys_schema' in schema else {} + if '$ref' not in keys_schema: + keys_pattern = keys_schema.pop('pattern', None) + # Don't give a title to patternProperties/propertyNames: + keys_schema.pop('title', None) + else: + # Here, we assume that if the keys schema is a definition reference, + # it can't be a simple string core schema (and thus no pattern can exist). + # However, this is only in practice (in theory, a definition reference core + # schema could be generated for a simple string schema). + # Note that we avoid calling `self.resolve_ref_schema`, as it might not exist yet. + keys_pattern = None + + values_schema = self.generate_inner(schema['values_schema']).copy() if 'values_schema' in schema else {} + # don't give a title to additionalProperties: + values_schema.pop('title', None) + + if values_schema or keys_pattern is not None: + if keys_pattern is None: + json_schema['additionalProperties'] = values_schema + else: + json_schema['patternProperties'] = {keys_pattern: values_schema} + else: # for `dict[str, Any]`, we allow any key and any value, since `str` is the default key type + json_schema['additionalProperties'] = True + + if ( + # The len check indicates that constraints are probably present: + (keys_schema.get('type') == 'string' and len(keys_schema) > 1) + # If this is a definition reference schema, it most likely has constraints: + or '$ref' in keys_schema + ): + keys_schema.pop('type', None) + json_schema['propertyNames'] = keys_schema + + self.update_with_validations(json_schema, schema, self.ValidationsMapping.object) + return json_schema + + def function_before_schema(self, schema: core_schema.BeforeValidatorFunctionSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a function-before schema. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + if self.mode == 'validation' and (input_schema := schema.get('json_schema_input_schema')): + return self.generate_inner(input_schema) + + return self.generate_inner(schema['schema']) + + def function_after_schema(self, schema: core_schema.AfterValidatorFunctionSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a function-after schema. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self.generate_inner(schema['schema']) + + def function_plain_schema(self, schema: core_schema.PlainValidatorFunctionSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a function-plain schema. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + if self.mode == 'validation' and (input_schema := schema.get('json_schema_input_schema')): + return self.generate_inner(input_schema) + + return self.handle_invalid_for_json_schema( + schema, f'core_schema.PlainValidatorFunctionSchema ({schema["function"]})' + ) + + def function_wrap_schema(self, schema: core_schema.WrapValidatorFunctionSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a function-wrap schema. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + if self.mode == 'validation' and (input_schema := schema.get('json_schema_input_schema')): + return self.generate_inner(input_schema) + + return self.generate_inner(schema['schema']) + + def default_schema(self, schema: core_schema.WithDefaultSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema with a default value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + json_schema = self.generate_inner(schema['schema']) + + default = self.get_default_value(schema) + if default is NoDefault or default is MISSING: + return json_schema + + # we reflect the application of custom plain, no-info serializers to defaults for + # JSON Schemas viewed in serialization mode: + # TODO: improvements along with https://github.com/pydantic/pydantic/issues/8208 + if self.mode == 'serialization': + # `_get_ser_schema_for_default_value()` is used to unpack potentially nested validator schemas: + ser_schema = _get_ser_schema_for_default_value(schema['schema']) + if ( + ser_schema is not None + and (ser_func := ser_schema.get('function')) + and not (default is None and ser_schema.get('when_used') in ('unless-none', 'json-unless-none')) + ): + try: + default = ser_func(default) # type: ignore + except Exception: + # It might be that the provided default needs to be validated (read: parsed) first + # (assuming `validate_default` is enabled). However, we can't perform + # such validation during JSON Schema generation so we don't support + # this pattern for now. + # (One example is when using `foo: ByteSize = '1MB'`, which validates and + # serializes as an int. In this case, `ser_func` is `int` and `int('1MB')` fails). + self.emit_warning( + 'non-serializable-default', + f'Unable to serialize value {default!r} with the plain serializer; excluding default from JSON schema', + ) + return json_schema + + try: + encoded_default = self.encode_default(default) + except pydantic_core.PydanticSerializationError: + self.emit_warning( + 'non-serializable-default', + f'Default value {default} is not JSON serializable; excluding default from JSON schema', + ) + # Return the inner schema, as though there was no default + return json_schema + + json_schema['default'] = encoded_default + return json_schema + + def get_default_value(self, schema: core_schema.WithDefaultSchema) -> Any: + """Get the default value to be used when generating a JSON Schema for a core schema with a default. + + The default implementation is to use the statically defined default value. This method can be overridden + if you want to make use of the default factory. + + Args: + schema: The `'with-default'` core schema. + + Returns: + The default value to use, or [`NoDefault`][pydantic.json_schema.NoDefault] if no default + value is available. + """ + return schema.get('default', NoDefault) + + def nullable_schema(self, schema: core_schema.NullableSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that allows null values. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + null_schema = {'type': 'null'} + inner_json_schema = self.generate_inner(schema['schema']) + + if inner_json_schema == null_schema: + return null_schema + else: + return self.get_union_of_schemas([inner_json_schema, null_schema]) + + def union_schema(self, schema: core_schema.UnionSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that allows values matching any of the given schemas. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + generated: list[JsonSchemaValue] = [] + + choices = schema['choices'] + for choice in choices: + # choice will be a tuple if an explicit label was provided + choice_schema = choice[0] if isinstance(choice, tuple) else choice + try: + generated.append(self.generate_inner(choice_schema)) + except PydanticOmit: + continue + except PydanticInvalidForJsonSchema as exc: + self.emit_warning('skipped-choice', exc.message) + if len(generated) == 1: + return generated[0] + return self.get_union_of_schemas(generated) + + def get_union_of_schemas(self, schemas: list[JsonSchemaValue]) -> JsonSchemaValue: + """Returns the JSON Schema representation for the union of the provided JSON Schemas. + + The result depends on the configured `'union_format'`. + + Args: + schemas: The list of JSON Schemas to be included in the union. + + Returns: + The JSON Schema representing the union of schemas. + """ + if self.union_format == 'primitive_type_array': + types: list[str] = [] + for schema in schemas: + schema_types: list[str] | str | None = schema.get('type') + if schema_types is None: + # No type, meaning it can be a ref or an empty schema. + break + if not isinstance(schema_types, list): + schema_types = [schema_types] + if not all(t in _PRIMITIVE_JSON_SCHEMA_TYPES for t in schema_types): + break + if len(schema) != 1: + # We only want to include types that don't have any constraints. For instance, + # if `schemas = [{'type': 'string', 'maxLength': 3}, {'type': 'string', 'minLength': 5}]`, + # we don't want to produce `{'type': 'string', 'maxLength': 3, 'minLength': 5}`. + # Same if we have some metadata (e.g. `title`) on a specific union member, we want to preserve it. + break + + types.extend(schema_types) + else: + # If we got there, all the schemas where valid to be used with the `'primitive_type_array` format + return {'type': list(dict.fromkeys(types))} + + return self.get_flattened_anyof(schemas) + + def tagged_union_schema(self, schema: core_schema.TaggedUnionSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that allows values matching any of the given schemas, where + the schemas are tagged with a discriminator field that indicates which schema should be used to validate + the value. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + generated: dict[str, JsonSchemaValue] = {} + for k, v in schema['choices'].items(): + if isinstance(k, Enum): + k = k.value + try: + # Use str(k) since keys must be strings for json; while not technically correct, + # it's the closest that can be represented in valid JSON + generated[str(k)] = self.generate_inner(v).copy() + except PydanticOmit: + continue + except PydanticInvalidForJsonSchema as exc: + self.emit_warning('skipped-choice', exc.message) + + one_of_choices = _deduplicate_schemas(generated.values()) + json_schema: JsonSchemaValue = {'oneOf': one_of_choices} + + # This reflects the v1 behavior; TODO: we should make it possible to exclude OpenAPI stuff from the JSON schema + openapi_discriminator = self._extract_discriminator(schema, one_of_choices) + if openapi_discriminator is not None: + json_schema['discriminator'] = { + 'propertyName': openapi_discriminator, + 'mapping': {k: v.get('$ref', v) for k, v in generated.items()}, + } + + return json_schema + + def _extract_discriminator( + self, schema: core_schema.TaggedUnionSchema, one_of_choices: list[JsonDict] + ) -> str | None: + """Extract a compatible OpenAPI discriminator from the schema and one_of choices that end up in the final + schema.""" + openapi_discriminator: str | None = None + + if isinstance(schema['discriminator'], str): + return schema['discriminator'] + + if isinstance(schema['discriminator'], list): + # If the discriminator is a single item list containing a string, that is equivalent to the string case + if len(schema['discriminator']) == 1 and isinstance(schema['discriminator'][0], str): + return schema['discriminator'][0] + # When an alias is used that is different from the field name, the discriminator will be a list of single + # str lists, one for the attribute and one for the actual alias. The logic here will work even if there is + # more than one possible attribute, and looks for whether a single alias choice is present as a documented + # property on all choices. If so, that property will be used as the OpenAPI discriminator. + for alias_path in schema['discriminator']: + if not isinstance(alias_path, list): + break # this means that the discriminator is not a list of alias paths + if len(alias_path) != 1: + continue # this means that the "alias" does not represent a single field + alias = alias_path[0] + if not isinstance(alias, str): + continue # this means that the "alias" does not represent a field + alias_is_present_on_all_choices = True + for choice in one_of_choices: + try: + choice = self.resolve_ref_schema(choice) + except RuntimeError as exc: + # TODO: fixme - this is a workaround for the fact that we can't always resolve refs + # for tagged union choices at this point in the schema gen process, we might need to do + # another pass at the end like we do for core schemas + self.emit_warning('skipped-discriminator', str(exc)) + choice = {} + properties = choice.get('properties', {}) + if not isinstance(properties, dict) or alias not in properties: + alias_is_present_on_all_choices = False + break + if alias_is_present_on_all_choices: + openapi_discriminator = alias + break + return openapi_discriminator + + def chain_schema(self, schema: core_schema.ChainSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a core_schema.ChainSchema. + + When generating a schema for validation, we return the validation JSON schema for the first step in the chain. + For serialization, we return the serialization JSON schema for the last step in the chain. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + step_index = 0 if self.mode == 'validation' else -1 # use first step for validation, last for serialization + return self.generate_inner(schema['steps'][step_index]) + + def lax_or_strict_schema(self, schema: core_schema.LaxOrStrictSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that allows values matching either the lax schema or the + strict schema. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + # TODO: Need to read the default value off of model config or whatever + use_strict = schema.get('strict', False) # TODO: replace this default False + # If your JSON schema fails to generate it is probably + # because one of the following two branches failed. + if use_strict: + return self.generate_inner(schema['strict_schema']) + else: + return self.generate_inner(schema['lax_schema']) + + def json_or_python_schema(self, schema: core_schema.JsonOrPythonSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that allows values matching either the JSON schema or the + Python schema. + + The JSON schema is used instead of the Python schema. If you want to use the Python schema, you should override + this method. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self.generate_inner(schema['json_schema']) + + def typed_dict_schema(self, schema: core_schema.TypedDictSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a typed dict. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + total = schema.get('total', True) + named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [ + (name, self.field_is_required(field, total), field) + for name, field in schema['fields'].items() + if self.field_is_present(field) + ] + if self.mode == 'serialization': + named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', []))) + cls = schema.get('cls') + config = _get_typed_dict_config(cls) + with self._config_wrapper_stack.push(config): + json_schema = self._named_required_fields_schema(named_required_fields) + + # There's some duplication between `extra_behavior` and + # the config's `extra`/core config's `extra_fields_behavior`. + # However, it is common to manually create TypedDictSchemas, + # where you don't necessarily have a class. + # At runtime, `extra_behavior` takes priority over the config + # for validation, so follow the same for the JSON Schema: + if schema.get('extra_behavior') == 'forbid': + json_schema['additionalProperties'] = False + elif schema.get('extra_behavior') == 'allow': + if 'extras_schema' in schema and schema['extras_schema'] != {'type': 'any'}: + json_schema['additionalProperties'] = self.generate_inner(schema['extras_schema']) + else: + json_schema['additionalProperties'] = True + + if cls is not None: + # `_update_class_schema()` will not override + # `additionalProperties` if already present: + self._update_class_schema(json_schema, cls, config) + elif 'additionalProperties' not in json_schema: + extra = schema.get('config', {}).get('extra_fields_behavior') + if extra == 'forbid': + json_schema['additionalProperties'] = False + elif extra == 'allow': + json_schema['additionalProperties'] = True + + return json_schema + + @staticmethod + def _name_required_computed_fields( + computed_fields: list[ComputedField], + ) -> list[tuple[str, bool, core_schema.ComputedField]]: + return [(field['property_name'], True, field) for field in computed_fields] + + def _named_required_fields_schema( + self, named_required_fields: Sequence[tuple[str, bool, CoreSchemaField]] + ) -> JsonSchemaValue: + properties: dict[str, JsonSchemaValue] = {} + required_fields: list[str] = [] + for name, required, field in named_required_fields: + if self.by_alias: + name = self._get_alias_name(field, name) + try: + field_json_schema = self.generate_inner(field).copy() + except PydanticOmit: + continue + if 'title' not in field_json_schema and self.field_title_should_be_set(field): + title = self.get_title_from_name(name) + field_json_schema['title'] = title + field_json_schema = self.handle_ref_overrides(field_json_schema) + properties[name] = field_json_schema + if required: + required_fields.append(name) + + json_schema = {'type': 'object', 'properties': properties} + if required_fields: + json_schema['required'] = required_fields + return json_schema + + def _get_alias_name(self, field: CoreSchemaField, name: str) -> str: + if field['type'] == 'computed-field': + alias: Any = field.get('alias', name) + elif self.mode == 'validation': + alias = field.get('validation_alias', name) + else: + alias = field.get('serialization_alias', name) + if isinstance(alias, str): + name = alias + elif isinstance(alias, list): + alias = cast('list[str] | str', alias) + for path in alias: + if isinstance(path, list) and len(path) == 1 and isinstance(path[0], str): + # Use the first valid single-item string path; the code that constructs the alias array + # should ensure the first such item is what belongs in the JSON schema + name = path[0] + break + else: + assert_never(alias) + return name + + def typed_dict_field_schema(self, schema: core_schema.TypedDictField) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a typed dict field. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self.generate_inner(schema['schema']) + + def dataclass_field_schema(self, schema: core_schema.DataclassField) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a dataclass field. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self.generate_inner(schema['schema']) + + def model_field_schema(self, schema: core_schema.ModelField) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a model field. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self.generate_inner(schema['schema']) + + def computed_field_schema(self, schema: core_schema.ComputedField) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a computed field. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self.generate_inner(schema['return_schema']) + + def model_schema(self, schema: core_schema.ModelSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a model. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + # We do not use schema['model'].model_json_schema() here + # because it could lead to inconsistent refs handling, etc. + cls = cast('type[BaseModel]', schema['cls']) + config = cls.model_config + + with self._config_wrapper_stack.push(config): + json_schema = self.generate_inner(schema['schema']) + + self._update_class_schema(json_schema, cls, config) + + return json_schema + + def _update_class_schema(self, json_schema: JsonSchemaValue, cls: type[Any], config: ConfigDict) -> None: + """Update json_schema with the following, extracted from `config` and `cls`: + + * title + * description + * additional properties + * json_schema_extra + * deprecated + + Done in place, hence there's no return value as the original json_schema is mutated. + No ref resolving is involved here, as that's not appropriate for simple updates. + """ + from .main import BaseModel + from .root_model import RootModel + + if (config_title := config.get('title')) is not None: + json_schema.setdefault('title', config_title) + elif model_title_generator := config.get('model_title_generator'): + title = model_title_generator(cls) + if not isinstance(title, str): + raise TypeError(f'model_title_generator {model_title_generator} must return str, not {title.__class__}') + json_schema.setdefault('title', title) + if 'title' not in json_schema: + json_schema['title'] = cls.__name__ + + # BaseModel and dataclasses; don't use cls.__doc__ as it will contain the verbose class signature by default + docstring = None if cls is BaseModel or dataclasses.is_dataclass(cls) else cls.__doc__ + + if docstring: + json_schema.setdefault('description', inspect.cleandoc(docstring)) + elif issubclass(cls, RootModel) and (root_description := cls.__pydantic_fields__['root'].description): + json_schema.setdefault('description', root_description) + + extra = config.get('extra') + if 'additionalProperties' not in json_schema: # This check is particularly important for `typed_dict_schema()` + if extra == 'allow': + json_schema['additionalProperties'] = True + elif extra == 'forbid': + json_schema['additionalProperties'] = False + + json_schema_extra = config.get('json_schema_extra') + if issubclass(cls, BaseModel) and cls.__pydantic_root_model__: + root_json_schema_extra = cls.model_fields['root'].json_schema_extra + if json_schema_extra and root_json_schema_extra: + raise ValueError( + '"model_config[\'json_schema_extra\']" and "Field.json_schema_extra" on "RootModel.root"' + ' field must not be set simultaneously' + ) + if root_json_schema_extra: + json_schema_extra = root_json_schema_extra + + if isinstance(json_schema_extra, (staticmethod, classmethod)): + # In older versions of python, this is necessary to ensure staticmethod/classmethods are callable + json_schema_extra = json_schema_extra.__get__(cls) + + if isinstance(json_schema_extra, dict): + json_schema.update(json_schema_extra) + elif callable(json_schema_extra): + # FIXME: why are there type ignores here? We support two signatures for json_schema_extra callables... + if len(inspect.signature(json_schema_extra).parameters) > 1: + json_schema_extra(json_schema, cls) # type: ignore + else: + json_schema_extra(json_schema) # type: ignore + elif json_schema_extra is not None: + raise ValueError( + f"model_config['json_schema_extra']={json_schema_extra} should be a dict, callable, or None" + ) + + if hasattr(cls, '__deprecated__'): + json_schema['deprecated'] = True + + def resolve_ref_schema(self, json_schema: JsonSchemaValue) -> JsonSchemaValue: + """Resolve a JsonSchemaValue to the non-ref schema if it is a $ref schema. + + Args: + json_schema: The schema to resolve. + + Returns: + The resolved schema. + + Raises: + RuntimeError: If the schema reference can't be found in definitions. + """ + while '$ref' in json_schema: + ref = json_schema['$ref'] + schema_to_update = self.get_schema_from_definitions(JsonRef(ref)) + if schema_to_update is None: + raise RuntimeError(f'Cannot update undefined schema for $ref={ref}') + json_schema = schema_to_update + return json_schema + + def model_fields_schema(self, schema: core_schema.ModelFieldsSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a model's fields. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [ + (name, self.field_is_required(field, total=True), field) + for name, field in schema['fields'].items() + if self.field_is_present(field) + ] + if self.mode == 'serialization': + named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', []))) + json_schema = self._named_required_fields_schema(named_required_fields) + extras_schema = schema.get('extras_schema', None) + if extras_schema is not None: + schema_to_update = self.resolve_ref_schema(json_schema) + schema_to_update['additionalProperties'] = self.generate_inner(extras_schema) + return json_schema + + def field_is_present(self, field: CoreSchemaField) -> bool: + """Whether the field should be included in the generated JSON schema. + + Args: + field: The schema for the field itself. + + Returns: + `True` if the field should be included in the generated JSON schema, `False` otherwise. + """ + if self.mode == 'serialization': + # If you still want to include the field in the generated JSON schema, + # override this method and return True + return not field.get('serialization_exclude') + elif self.mode == 'validation': + return True + else: + assert_never(self.mode) + + def field_is_required( + self, + field: core_schema.ModelField | core_schema.DataclassField | core_schema.TypedDictField, + total: bool, + ) -> bool: + """Whether the field should be marked as required in the generated JSON schema. + (Note that this is irrelevant if the field is not present in the JSON schema.). + + Args: + field: The schema for the field itself. + total: Only applies to `TypedDictField`s. + Indicates if the `TypedDict` this field belongs to is total, in which case any fields that don't + explicitly specify `required=False` are required. + + Returns: + `True` if the field should be marked as required in the generated JSON schema, `False` otherwise. + """ + if self.mode == 'serialization' and self._config.json_schema_serialization_defaults_required: + return not field.get('serialization_exclude') + else: + if field['type'] == 'typed-dict-field': + return field.get('required', total) + else: + return field['schema']['type'] != 'default' + + def dataclass_args_schema(self, schema: core_schema.DataclassArgsSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a dataclass's constructor arguments. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [ + (field['name'], self.field_is_required(field, total=True), field) + for field in schema['fields'] + if self.field_is_present(field) + ] + if self.mode == 'serialization': + named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', []))) + return self._named_required_fields_schema(named_required_fields) + + def dataclass_schema(self, schema: core_schema.DataclassSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a dataclass. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + from ._internal._dataclasses import is_stdlib_dataclass + + cls = schema['cls'] + config: ConfigDict = getattr(cls, '__pydantic_config__', cast('ConfigDict', {})) + + with self._config_wrapper_stack.push(config): + json_schema = self.generate_inner(schema['schema']).copy() + + self._update_class_schema(json_schema, cls, config) + + # Dataclass-specific handling of description + if is_stdlib_dataclass(cls): + # vanilla dataclass; don't use cls.__doc__ as it will contain the class signature by default + description = None + else: + description = None if cls.__doc__ is None else inspect.cleandoc(cls.__doc__) + if description: + json_schema['description'] = description + + return json_schema + + def arguments_schema(self, schema: core_schema.ArgumentsSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a function's arguments. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + prefer_positional = schema.get('metadata', {}).get('pydantic_js_prefer_positional_arguments') + + arguments = schema['arguments_schema'] + kw_only_arguments = [a for a in arguments if a.get('mode') == 'keyword_only'] + kw_or_p_arguments = [a for a in arguments if a.get('mode') in {'positional_or_keyword', None}] + p_only_arguments = [a for a in arguments if a.get('mode') == 'positional_only'] + var_args_schema = schema.get('var_args_schema') + var_kwargs_schema = schema.get('var_kwargs_schema') + + if prefer_positional: + positional_possible = not kw_only_arguments and not var_kwargs_schema + if positional_possible: + return self.p_arguments_schema(p_only_arguments + kw_or_p_arguments, var_args_schema) + + keyword_possible = not p_only_arguments and not var_args_schema + if keyword_possible: + return self.kw_arguments_schema(kw_or_p_arguments + kw_only_arguments, var_kwargs_schema) + + if not prefer_positional: + positional_possible = not kw_only_arguments and not var_kwargs_schema + if positional_possible: + return self.p_arguments_schema(p_only_arguments + kw_or_p_arguments, var_args_schema) + + raise PydanticInvalidForJsonSchema( + 'Unable to generate JSON schema for arguments validator with positional-only and keyword-only arguments' + ) + + def kw_arguments_schema( + self, arguments: list[core_schema.ArgumentsParameter], var_kwargs_schema: CoreSchema | None + ) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a function's keyword arguments. + + Args: + arguments: The core schema. + + Returns: + The generated JSON schema. + """ + properties: dict[str, JsonSchemaValue] = {} + required: list[str] = [] + for argument in arguments: + name = self.get_argument_name(argument) + argument_schema = self.generate_inner(argument['schema']).copy() + if 'title' not in argument_schema and self.field_title_should_be_set(argument['schema']): + argument_schema['title'] = self.get_title_from_name(name) + properties[name] = argument_schema + + if argument['schema']['type'] != 'default': + # This assumes that if the argument has a default value, + # the inner schema must be of type WithDefaultSchema. + # I believe this is true, but I am not 100% sure + required.append(name) + + json_schema: JsonSchemaValue = {'type': 'object', 'properties': properties} + if required: + json_schema['required'] = required + + if var_kwargs_schema: + additional_properties_schema = self.generate_inner(var_kwargs_schema) + if additional_properties_schema: + json_schema['additionalProperties'] = additional_properties_schema + else: + json_schema['additionalProperties'] = False + return json_schema + + def p_arguments_schema( + self, arguments: list[core_schema.ArgumentsParameter], var_args_schema: CoreSchema | None + ) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a function's positional arguments. + + Args: + arguments: The core schema. + + Returns: + The generated JSON schema. + """ + prefix_items: list[JsonSchemaValue] = [] + min_items = 0 + + for argument in arguments: + name = self.get_argument_name(argument) + + argument_schema = self.generate_inner(argument['schema']).copy() + if 'title' not in argument_schema and self.field_title_should_be_set(argument['schema']): + argument_schema['title'] = self.get_title_from_name(name) + prefix_items.append(argument_schema) + + if argument['schema']['type'] != 'default': + # This assumes that if the argument has a default value, + # the inner schema must be of type WithDefaultSchema. + # I believe this is true, but I am not 100% sure + min_items += 1 + + json_schema: JsonSchemaValue = {'type': 'array'} + if prefix_items: + json_schema['prefixItems'] = prefix_items + if min_items: + json_schema['minItems'] = min_items + + if var_args_schema: + items_schema = self.generate_inner(var_args_schema) + if items_schema: + json_schema['items'] = items_schema + else: + json_schema['maxItems'] = len(prefix_items) + + return json_schema + + def get_argument_name(self, argument: core_schema.ArgumentsParameter | core_schema.ArgumentsV3Parameter) -> str: + """Retrieves the name of an argument. + + Args: + argument: The core schema. + + Returns: + The name of the argument. + """ + name = argument['name'] + if self.by_alias: + alias = argument.get('alias') + if isinstance(alias, str): + name = alias + else: + pass # might want to do something else? + return name + + def arguments_v3_schema(self, schema: core_schema.ArgumentsV3Schema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a function's arguments. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + arguments = schema['arguments_schema'] + properties: dict[str, JsonSchemaValue] = {} + required: list[str] = [] + for argument in arguments: + mode = argument.get('mode', 'positional_or_keyword') + name = self.get_argument_name(argument) + argument_schema = self.generate_inner(argument['schema']).copy() + if mode == 'var_args': + argument_schema = {'type': 'array', 'items': argument_schema} + elif mode == 'var_kwargs_uniform': + argument_schema = {'type': 'object', 'additionalProperties': argument_schema} + + argument_schema.setdefault('title', self.get_title_from_name(name)) + properties[name] = argument_schema + + if ( + (mode == 'var_kwargs_unpacked_typed_dict' and 'required' in argument_schema) + or mode not in {'var_args', 'var_kwargs_uniform', 'var_kwargs_unpacked_typed_dict'} + and argument['schema']['type'] != 'default' + ): + # This assumes that if the argument has a default value, + # the inner schema must be of type WithDefaultSchema. + # I believe this is true, but I am not 100% sure + required.append(name) + + json_schema: JsonSchemaValue = {'type': 'object', 'properties': properties} + if required: + json_schema['required'] = required + return json_schema + + def call_schema(self, schema: core_schema.CallSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a function call. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self.generate_inner(schema['arguments_schema']) + + def custom_error_schema(self, schema: core_schema.CustomErrorSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a custom error. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return self.generate_inner(schema['schema']) + + def json_schema(self, schema: core_schema.JsonSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a JSON object. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + content_core_schema = schema.get('schema') or core_schema.any_schema() + content_json_schema = self.generate_inner(content_core_schema) + if self.mode == 'validation': + return {'type': 'string', 'contentMediaType': 'application/json', 'contentSchema': content_json_schema} + else: + # self.mode == 'serialization' + return content_json_schema + + def url_schema(self, schema: core_schema.UrlSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a URL. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + json_schema = {'type': 'string', 'format': 'uri', 'minLength': 1} + self.update_with_validations(json_schema, schema, self.ValidationsMapping.string) + return json_schema + + def multi_host_url_schema(self, schema: core_schema.MultiHostUrlSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a URL that can be used with multiple hosts. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + # Note: 'multi-host-uri' is a custom/pydantic-specific format, not part of the JSON Schema spec + json_schema = {'type': 'string', 'format': 'multi-host-uri', 'minLength': 1} + self.update_with_validations(json_schema, schema, self.ValidationsMapping.string) + return json_schema + + def uuid_schema(self, schema: core_schema.UuidSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a UUID. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return {'type': 'string', 'format': 'uuid'} + + def definitions_schema(self, schema: core_schema.DefinitionsSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that defines a JSON object with definitions. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + for definition in schema['definitions']: + try: + self.generate_inner(definition) + except PydanticInvalidForJsonSchema as e: # noqa: PERF203 + core_ref: CoreRef = CoreRef(definition['ref']) # type: ignore + self._core_defs_invalid_for_json_schema[self.get_defs_ref((core_ref, self.mode))] = e + continue + return self.generate_inner(schema['schema']) + + def definition_ref_schema(self, schema: core_schema.DefinitionReferenceSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a schema that references a definition. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + core_ref = CoreRef(schema['schema_ref']) + _, ref_json_schema = self.get_cache_defs_ref_schema(core_ref) + return ref_json_schema + + def ser_schema( + self, schema: core_schema.SerSchema | core_schema.IncExSeqSerSchema | core_schema.IncExDictSerSchema + ) -> JsonSchemaValue | None: + """Generates a JSON schema that matches a schema that defines a serialized object. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + schema_type = schema['type'] + if schema_type == 'function-plain' or schema_type == 'function-wrap': + # PlainSerializerFunctionSerSchema or WrapSerializerFunctionSerSchema + return_schema = schema.get('return_schema') + if return_schema is not None: + return self.generate_inner(return_schema) + elif schema_type == 'format' or schema_type == 'to-string': + # FormatSerSchema or ToStringSerSchema + return self.str_schema(core_schema.str_schema()) + elif schema['type'] == 'model': + # ModelSerSchema + return self.generate_inner(schema['schema']) + return None + + def complex_schema(self, schema: core_schema.ComplexSchema) -> JsonSchemaValue: + """Generates a JSON schema that matches a complex number. + + JSON has no standard way to represent complex numbers. Complex number is not a numeric + type. Here we represent complex number as strings following the rule defined by Python. + For instance, '1+2j' is an accepted complex string. Details can be found in + [Python's `complex` documentation][complex]. + + Args: + schema: The core schema. + + Returns: + The generated JSON schema. + """ + return {'type': 'string'} + + # ### Utility methods + + def get_title_from_name(self, name: str) -> str: + """Retrieves a title from a name. + + Args: + name: The name to retrieve a title from. + + Returns: + The title. + """ + return name.title().replace('_', ' ').strip() + + def field_title_should_be_set(self, schema: CoreSchemaOrField) -> bool: + """Returns true if a field with the given schema should have a title set based on the field name. + + Intuitively, we want this to return true for schemas that wouldn't otherwise provide their own title + (e.g., int, float, str), and false for those that would (e.g., BaseModel subclasses). + + Args: + schema: The schema to check. + + Returns: + `True` if the field should have a title set, `False` otherwise. + """ + if _core_utils.is_core_schema_field(schema): + if schema['type'] == 'computed-field': + field_schema = schema['return_schema'] + else: + field_schema = schema['schema'] + return self.field_title_should_be_set(field_schema) + + elif _core_utils.is_core_schema(schema): + if schema.get('ref'): # things with refs, such as models and enums, should not have titles set + return False + if schema['type'] in {'default', 'nullable', 'definitions'}: + return self.field_title_should_be_set(schema['schema']) # type: ignore[typeddict-item] + if _core_utils.is_function_with_inner_schema(schema): + return self.field_title_should_be_set(schema['schema']) + if schema['type'] == 'definition-ref': + # Referenced schemas should not have titles set for the same reason + # schemas with refs should not + return False + return True # anything else should have title set + + else: + raise PydanticInvalidForJsonSchema(f'Unexpected schema type: schema={schema}') # pragma: no cover + + def normalize_name(self, name: str) -> str: + """Normalizes a name to be used as a key in a dictionary. + + Args: + name: The name to normalize. + + Returns: + The normalized name. + """ + return re.sub(r'[^a-zA-Z0-9.\-_]', '_', name).replace('.', '__') + + def get_defs_ref(self, core_mode_ref: CoreModeRef) -> DefsRef: + """Override this method to change the way that definitions keys are generated from a core reference. + + Args: + core_mode_ref: The core reference. + + Returns: + The definitions key. + """ + # Split the core ref into "components"; generic origins and arguments are each separate components + core_ref, mode = core_mode_ref + components = re.split(r'([\][,])', core_ref) + # Remove IDs from each component + components = [x.rsplit(':', 1)[0] for x in components] + core_ref_no_id = ''.join(components) + # Remove everything before the last period from each "component" + components = [re.sub(r'(?:[^.[\]]+\.)+((?:[^.[\]]+))', r'\1', x) for x in components] + short_ref = ''.join(components) + + mode_title = _MODE_TITLE_MAPPING[mode] + + # It is important that the generated defs_ref values be such that at least one choice will not + # be generated for any other core_ref. Currently, this should be the case because we include + # the id of the source type in the core_ref + name = DefsRef(self.normalize_name(short_ref)) + name_mode = DefsRef(self.normalize_name(short_ref) + f'-{mode_title}') + module_qualname = DefsRef(self.normalize_name(core_ref_no_id)) + module_qualname_mode = DefsRef(f'{module_qualname}-{mode_title}') + module_qualname_id = DefsRef(self.normalize_name(core_ref)) + occurrence_index = self._collision_index.get(module_qualname_id) + if occurrence_index is None: + self._collision_counter[module_qualname] += 1 + occurrence_index = self._collision_index[module_qualname_id] = self._collision_counter[module_qualname] + + module_qualname_occurrence = DefsRef(f'{module_qualname}__{occurrence_index}') + module_qualname_occurrence_mode = DefsRef(f'{module_qualname_mode}__{occurrence_index}') + + self._prioritized_defsref_choices[module_qualname_occurrence_mode] = [ + name, + name_mode, + module_qualname, + module_qualname_mode, + module_qualname_occurrence, + module_qualname_occurrence_mode, + ] + + return module_qualname_occurrence_mode + + def get_cache_defs_ref_schema(self, core_ref: CoreRef) -> tuple[DefsRef, JsonSchemaValue]: + """This method wraps the get_defs_ref method with some cache-lookup/population logic, + and returns both the produced defs_ref and the JSON schema that will refer to the right definition. + + Args: + core_ref: The core reference to get the definitions reference for. + + Returns: + A tuple of the definitions reference and the JSON schema that will refer to it. + """ + core_mode_ref = (core_ref, self.mode) + maybe_defs_ref = self.core_to_defs_refs.get(core_mode_ref) + if maybe_defs_ref is not None: + json_ref = self.core_to_json_refs[core_mode_ref] + return maybe_defs_ref, {'$ref': json_ref} + + defs_ref = self.get_defs_ref(core_mode_ref) + + # populate the ref translation mappings + self.core_to_defs_refs[core_mode_ref] = defs_ref + self.defs_to_core_refs[defs_ref] = core_mode_ref + + json_ref = JsonRef(self.ref_template.format(model=defs_ref)) + self.core_to_json_refs[core_mode_ref] = json_ref + self.json_to_defs_refs[json_ref] = defs_ref + ref_json_schema = {'$ref': json_ref} + return defs_ref, ref_json_schema + + def handle_ref_overrides(self, json_schema: JsonSchemaValue) -> JsonSchemaValue: + """Remove any sibling keys that are redundant with the referenced schema. + + Args: + json_schema: The schema to remove redundant sibling keys from. + + Returns: + The schema with redundant sibling keys removed. + """ + if '$ref' in json_schema: + # prevent modifications to the input; this copy may be safe to drop if there is significant overhead + json_schema = json_schema.copy() + + referenced_json_schema = self.get_schema_from_definitions(JsonRef(json_schema['$ref'])) + if referenced_json_schema is None: + # This can happen when building schemas for models with not-yet-defined references. + # It may be a good idea to do a recursive pass at the end of the generation to remove + # any redundant override keys. + return json_schema + for k, v in list(json_schema.items()): + if k == '$ref': + continue + if k in referenced_json_schema and referenced_json_schema[k] == v: + del json_schema[k] # redundant key + + return json_schema + + def get_schema_from_definitions(self, json_ref: JsonRef) -> JsonSchemaValue | None: + try: + def_ref = self.json_to_defs_refs[json_ref] + if def_ref in self._core_defs_invalid_for_json_schema: + raise self._core_defs_invalid_for_json_schema[def_ref] + return self.definitions.get(def_ref, None) + except KeyError: + if json_ref.startswith(('http://', 'https://')): + return None + raise + + def encode_default(self, dft: Any) -> Any: + """Encode a default value to a JSON-serializable value. + + This is used to encode default values for fields in the generated JSON schema. + + Args: + dft: The default value to encode. + + Returns: + The encoded default value. + """ + from .type_adapter import TypeAdapter, _type_has_config + + config = self._config + try: + default = ( + dft + if _type_has_config(type(dft)) + else TypeAdapter(type(dft), config=config.config_dict).dump_python( + dft, by_alias=self.by_alias, mode='json' + ) + ) + except PydanticSchemaGenerationError: + raise pydantic_core.PydanticSerializationError(f'Unable to encode default value {dft}') + + return pydantic_core.to_jsonable_python( + default, timedelta_mode=config.ser_json_timedelta, bytes_mode=config.ser_json_bytes, by_alias=self.by_alias + ) + + def update_with_validations( + self, json_schema: JsonSchemaValue, core_schema: CoreSchema, mapping: dict[str, str] + ) -> None: + """Update the json_schema with the corresponding validations specified in the core_schema, + using the provided mapping to translate keys in core_schema to the appropriate keys for a JSON schema. + + Args: + json_schema: The JSON schema to update. + core_schema: The core schema to get the validations from. + mapping: A mapping from core_schema attribute names to the corresponding JSON schema attribute names. + """ + for core_key, json_schema_key in mapping.items(): + if core_key in core_schema: + json_schema[json_schema_key] = core_schema[core_key] + + class ValidationsMapping: + """This class just contains mappings from core_schema attribute names to the corresponding + JSON schema attribute names. While I suspect it is unlikely to be necessary, you can in + principle override this class in a subclass of GenerateJsonSchema (by inheriting from + GenerateJsonSchema.ValidationsMapping) to change these mappings. + """ + + numeric = { + 'multiple_of': 'multipleOf', + 'le': 'maximum', + 'ge': 'minimum', + 'lt': 'exclusiveMaximum', + 'gt': 'exclusiveMinimum', + } + bytes = { + 'min_length': 'minLength', + 'max_length': 'maxLength', + } + string = { + 'min_length': 'minLength', + 'max_length': 'maxLength', + 'pattern': 'pattern', + } + array = { + 'min_length': 'minItems', + 'max_length': 'maxItems', + } + object = { + 'min_length': 'minProperties', + 'max_length': 'maxProperties', + } + + def get_flattened_anyof(self, schemas: list[JsonSchemaValue]) -> JsonSchemaValue: + members = [] + for schema in schemas: + if len(schema) == 1 and 'anyOf' in schema: + members.extend(schema['anyOf']) + else: + members.append(schema) + members = _deduplicate_schemas(members) + if len(members) == 1: + return members[0] + return {'anyOf': members} + + def get_json_ref_counts(self, json_schema: JsonSchemaValue) -> dict[JsonRef, int]: + """Get all values corresponding to the key '$ref' anywhere in the json_schema.""" + json_refs: dict[JsonRef, int] = Counter() + + def _add_json_refs(schema: Any) -> None: + if isinstance(schema, dict): + if '$ref' in schema: + json_ref = JsonRef(schema['$ref']) + if not isinstance(json_ref, str): + return # in this case, '$ref' might have been the name of a property + already_visited = json_ref in json_refs + json_refs[json_ref] += 1 + if already_visited: + return # prevent recursion on a definition that was already visited + try: + defs_ref = self.json_to_defs_refs[json_ref] + if defs_ref in self._core_defs_invalid_for_json_schema: + raise self._core_defs_invalid_for_json_schema[defs_ref] + _add_json_refs(self.definitions[defs_ref]) + except KeyError: + if not json_ref.startswith(('http://', 'https://')): + raise + + for k, v in schema.items(): + if k == 'examples' and isinstance(v, list): + # Skip examples that may contain arbitrary values and references + # (see the comment in `_get_all_json_refs` for more details). + continue + _add_json_refs(v) + elif isinstance(schema, list): + for v in schema: + _add_json_refs(v) + + _add_json_refs(json_schema) + return json_refs + + def handle_invalid_for_json_schema(self, schema: CoreSchemaOrField, error_info: str) -> JsonSchemaValue: + raise PydanticInvalidForJsonSchema(f'Cannot generate a JsonSchema for {error_info}') + + def emit_warning(self, kind: JsonSchemaWarningKind, detail: str) -> None: + """This method simply emits PydanticJsonSchemaWarnings based on handling in the `warning_message` method.""" + message = self.render_warning_message(kind, detail) + if message is not None: + warnings.warn(message, PydanticJsonSchemaWarning) + + def render_warning_message(self, kind: JsonSchemaWarningKind, detail: str) -> str | None: + """This method is responsible for ignoring warnings as desired, and for formatting the warning messages. + + You can override the value of `ignored_warning_kinds` in a subclass of GenerateJsonSchema + to modify what warnings are generated. If you want more control, you can override this method; + just return None in situations where you don't want warnings to be emitted. + + Args: + kind: The kind of warning to render. It can be one of the following: + + - 'skipped-choice': A choice field was skipped because it had no valid choices. + - 'non-serializable-default': A default value was skipped because it was not JSON-serializable. + detail: A string with additional details about the warning. + + Returns: + The formatted warning message, or `None` if no warning should be emitted. + """ + if kind in self.ignored_warning_kinds: + return None + return f'{detail} [{kind}]' + + def _build_definitions_remapping(self) -> _DefinitionsRemapping: + defs_to_json: dict[DefsRef, JsonRef] = {} + for defs_refs in self._prioritized_defsref_choices.values(): + for defs_ref in defs_refs: + json_ref = JsonRef(self.ref_template.format(model=defs_ref)) + defs_to_json[defs_ref] = json_ref + + return _DefinitionsRemapping.from_prioritized_choices( + self._prioritized_defsref_choices, defs_to_json, self.definitions + ) + + def _garbage_collect_definitions(self, schema: JsonSchemaValue) -> None: + visited_defs_refs: set[DefsRef] = set() + unvisited_json_refs = _get_all_json_refs(schema) + while unvisited_json_refs: + next_json_ref = unvisited_json_refs.pop() + try: + next_defs_ref = self.json_to_defs_refs[next_json_ref] + if next_defs_ref in visited_defs_refs: + continue + visited_defs_refs.add(next_defs_ref) + unvisited_json_refs.update(_get_all_json_refs(self.definitions[next_defs_ref])) + except KeyError: + if not next_json_ref.startswith(('http://', 'https://')): + raise + + self.definitions = {k: v for k, v in self.definitions.items() if k in visited_defs_refs} + + +# ##### Start JSON Schema Generation Functions ##### + + +def model_json_schema( + cls: type[BaseModel] | type[PydanticDataclass], + by_alias: bool = True, + ref_template: str = DEFAULT_REF_TEMPLATE, + union_format: Literal['any_of', 'primitive_type_array'] = 'any_of', + schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema, + mode: JsonSchemaMode = 'validation', +) -> dict[str, Any]: + """Utility function to generate a JSON Schema for a model. + + Args: + cls: The model class to generate a JSON Schema for. + by_alias: If `True` (the default), fields will be serialized according to their alias. + If `False`, fields will be serialized according to their attribute name. + ref_template: The template to use for generating JSON Schema references. + union_format: The format to use when combining schemas from unions together. Can be one of: + + - `'any_of'`: Use the [`anyOf`](https://json-schema.org/understanding-json-schema/reference/combining#anyOf) + keyword to combine schemas (the default). + - `'primitive_type_array'`: Use the [`type`](https://json-schema.org/understanding-json-schema/reference/type) + keyword as an array of strings, containing each type of the combination. If any of the schemas is not a primitive + type (`string`, `boolean`, `null`, `integer` or `number`) or contains constraints/metadata, falls back to + `any_of`. + schema_generator: The class to use for generating the JSON Schema. + mode: The mode to use for generating the JSON Schema. It can be one of the following: + + - 'validation': Generate a JSON Schema for validating data. + - 'serialization': Generate a JSON Schema for serializing data. + + Returns: + The generated JSON Schema. + """ + from .main import BaseModel + + schema_generator_instance = schema_generator( + by_alias=by_alias, ref_template=ref_template, union_format=union_format + ) + + if isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema): + cls.__pydantic_core_schema__.rebuild() + + if cls is BaseModel: + raise AttributeError('model_json_schema() must be called on a subclass of BaseModel, not BaseModel itself.') + + assert not isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema), 'this is a bug! please report it' + return schema_generator_instance.generate(cls.__pydantic_core_schema__, mode=mode) + + +def models_json_schema( + models: Sequence[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode]], + *, + by_alias: bool = True, + title: str | None = None, + description: str | None = None, + ref_template: str = DEFAULT_REF_TEMPLATE, + union_format: Literal['any_of', 'primitive_type_array'] = 'any_of', + schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema, +) -> tuple[dict[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode], JsonSchemaValue], JsonSchemaValue]: + """Utility function to generate a JSON Schema for multiple models. + + Args: + models: A sequence of tuples of the form (model, mode). + by_alias: Whether field aliases should be used as keys in the generated JSON Schema. + title: The title of the generated JSON Schema. + description: The description of the generated JSON Schema. + ref_template: The reference template to use for generating JSON Schema references. + union_format: The format to use when combining schemas from unions together. Can be one of: + + - `'any_of'`: Use the [`anyOf`](https://json-schema.org/understanding-json-schema/reference/combining#anyOf) + keyword to combine schemas (the default). + - `'primitive_type_array'`: Use the [`type`](https://json-schema.org/understanding-json-schema/reference/type) + keyword as an array of strings, containing each type of the combination. If any of the schemas is not a primitive + type (`string`, `boolean`, `null`, `integer` or `number`) or contains constraints/metadata, falls back to + `any_of`. + schema_generator: The schema generator to use for generating the JSON Schema. + + Returns: + A tuple where: + - The first element is a dictionary whose keys are tuples of JSON schema key type and JSON mode, and + whose values are the JSON schema corresponding to that pair of inputs. (These schemas may have + JsonRef references to definitions that are defined in the second returned element.) + - The second element is a JSON schema containing all definitions referenced in the first returned + element, along with the optional title and description keys. + """ + for cls, _ in models: + if isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema): + cls.__pydantic_core_schema__.rebuild() + + instance = schema_generator(by_alias=by_alias, ref_template=ref_template, union_format=union_format) + inputs: list[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode, CoreSchema]] = [ + (m, mode, m.__pydantic_core_schema__) for m, mode in models + ] + json_schemas_map, definitions = instance.generate_definitions(inputs) + + json_schema: dict[str, Any] = {} + if definitions: + json_schema['$defs'] = definitions + if title: + json_schema['title'] = title + if description: + json_schema['description'] = description + + return json_schemas_map, json_schema + + +# ##### End JSON Schema Generation Functions ##### + + +_HashableJsonValue: TypeAlias = Union[ + int, float, str, bool, None, tuple['_HashableJsonValue', ...], tuple[tuple[str, '_HashableJsonValue'], ...] +] + + +def _deduplicate_schemas(schemas: Iterable[JsonDict]) -> list[JsonDict]: + return list({_make_json_hashable(schema): schema for schema in schemas}.values()) + + +def _make_json_hashable(value: JsonValue) -> _HashableJsonValue: + if isinstance(value, dict): + return tuple(sorted((k, _make_json_hashable(v)) for k, v in value.items())) + elif isinstance(value, list): + return tuple(_make_json_hashable(v) for v in value) + else: + return value + + +@dataclasses.dataclass(**_internal_dataclass.slots_true) +class WithJsonSchema: + """!!! abstract "Usage Documentation" + [`WithJsonSchema` Annotation](../concepts/json_schema.md#withjsonschema-annotation) + + Add this as an annotation on a field to override the (base) JSON schema that would be generated for that field. + This provides a way to set a JSON schema for types that would otherwise raise errors when producing a JSON schema, + such as Callable, or types that have an is-instance core schema, without needing to go so far as creating a + custom subclass of pydantic.json_schema.GenerateJsonSchema. + Note that any _modifications_ to the schema that would normally be made (such as setting the title for model fields) + will still be performed. + + If `mode` is set this will only apply to that schema generation mode, allowing you + to set different json schemas for validation and serialization. + """ + + json_schema: JsonSchemaValue | None + mode: Literal['validation', 'serialization'] | None = None + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> JsonSchemaValue: + mode = self.mode or handler.mode + if mode != handler.mode: + return handler(core_schema) + if self.json_schema is None: + # This exception is handled in pydantic.json_schema.GenerateJsonSchema._named_required_fields_schema + raise PydanticOmit + else: + return self.json_schema.copy() + + def __hash__(self) -> int: + return hash(type(self.mode)) + + +class Examples: + """Add examples to a JSON schema. + + If the JSON Schema already contains examples, the provided examples + will be appended. + + If `mode` is set this will only apply to that schema generation mode, + allowing you to add different examples for validation and serialization. + """ + + @overload + @deprecated('Using a dict for `examples` is deprecated since v2.9 and will be removed in v3.0. Use a list instead.') + def __init__( + self, examples: dict[str, Any], mode: Literal['validation', 'serialization'] | None = None + ) -> None: ... + + @overload + def __init__(self, examples: list[Any], mode: Literal['validation', 'serialization'] | None = None) -> None: ... + + def __init__( + self, examples: dict[str, Any] | list[Any], mode: Literal['validation', 'serialization'] | None = None + ) -> None: + if isinstance(examples, dict): + warnings.warn( + 'Using a dict for `examples` is deprecated, use a list instead.', + PydanticDeprecatedSince29, + stacklevel=2, + ) + self.examples = examples + self.mode = mode + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> JsonSchemaValue: + mode = self.mode or handler.mode + json_schema = handler(core_schema) + if mode != handler.mode: + return json_schema + examples = json_schema.get('examples') + if examples is None: + json_schema['examples'] = to_jsonable_python(self.examples) + if isinstance(examples, dict): + if isinstance(self.examples, list): + warnings.warn( + 'Updating existing JSON Schema examples of type dict with examples of type list. ' + 'Only the existing examples values will be retained. Note that dict support for ' + 'examples is deprecated and will be removed in v3.0.', + UserWarning, + ) + json_schema['examples'] = to_jsonable_python( + [ex for value in examples.values() for ex in value] + self.examples + ) + else: + json_schema['examples'] = to_jsonable_python({**examples, **self.examples}) + if isinstance(examples, list): + if isinstance(self.examples, list): + json_schema['examples'] = to_jsonable_python(examples + self.examples) + elif isinstance(self.examples, dict): + warnings.warn( + 'Updating existing JSON Schema examples of type list with examples of type dict. ' + 'Only the examples values will be retained. Note that dict support for ' + 'examples is deprecated and will be removed in v3.0.', + UserWarning, + ) + json_schema['examples'] = to_jsonable_python( + examples + [ex for value in self.examples.values() for ex in value] + ) + + return json_schema + + def __hash__(self) -> int: + return hash(type(self.mode)) + + +def _get_all_json_refs(item: Any) -> set[JsonRef]: + """Get all the definitions references from a JSON schema.""" + refs: set[JsonRef] = set() + stack = [item] + + while stack: + current = stack.pop() + if isinstance(current, dict): + for key, value in current.items(): + if key == 'examples' and isinstance(value, list): + # Skip examples that may contain arbitrary values and references + # (e.g. `{"examples": [{"$ref": "..."}]}`). Note: checking for value + # of type list is necessary to avoid skipping valid portions of the schema, + # for instance when "examples" is used as a property key. A more robust solution + # could be found, but would require more advanced JSON Schema parsing logic. + continue + if key == '$ref' and isinstance(value, str): + refs.add(JsonRef(value)) + elif isinstance(value, dict): + stack.append(value) + elif isinstance(value, list): + stack.extend(value) + elif isinstance(current, list): + stack.extend(current) + + return refs + + +AnyType = TypeVar('AnyType') + +if TYPE_CHECKING: + SkipJsonSchema = Annotated[AnyType, ...] +else: + + @dataclasses.dataclass(**_internal_dataclass.slots_true) + class SkipJsonSchema: + """!!! abstract "Usage Documentation" + [`SkipJsonSchema` Annotation](../concepts/json_schema.md#skipjsonschema-annotation) + + Add this as an annotation on a field to skip generating a JSON schema for that field. + + Example: + ```python + from pprint import pprint + from typing import Union + + from pydantic import BaseModel + from pydantic.json_schema import SkipJsonSchema + + class Model(BaseModel): + a: Union[int, None] = None # (1)! + b: Union[int, SkipJsonSchema[None]] = None # (2)! + c: SkipJsonSchema[Union[int, None]] = None # (3)! + + pprint(Model.model_json_schema()) + ''' + { + 'properties': { + 'a': { + 'anyOf': [ + {'type': 'integer'}, + {'type': 'null'} + ], + 'default': None, + 'title': 'A' + }, + 'b': { + 'default': None, + 'title': 'B', + 'type': 'integer' + } + }, + 'title': 'Model', + 'type': 'object' + } + ''' + ``` + + 1. The integer and null types are both included in the schema for `a`. + 2. The integer type is the only type included in the schema for `b`. + 3. The entirety of the `c` field is omitted from the schema. + """ + + def __class_getitem__(cls, item: AnyType) -> AnyType: + return Annotated[item, cls()] + + def __get_pydantic_json_schema__( + self, core_schema: CoreSchema, handler: GetJsonSchemaHandler + ) -> JsonSchemaValue: + raise PydanticOmit + + def __hash__(self) -> int: + return hash(type(self)) + + +def _get_typed_dict_config(cls: type[Any] | None) -> ConfigDict: + if cls is not None: + try: + return _decorators.get_attribute_from_bases(cls, '__pydantic_config__') + except AttributeError: + pass + return {} + + +def _get_ser_schema_for_default_value(schema: CoreSchema) -> core_schema.PlainSerializerFunctionSerSchema | None: + """Get a `'function-plain'` serialization schema that can be used to serialize a default value. + + This takes into account having the serialization schema nested under validation schema(s). + """ + if ( + (ser_schema := schema.get('serialization')) + and ser_schema['type'] == 'function-plain' + and not ser_schema.get('info_arg') + ): + return ser_schema + if _core_utils.is_function_with_inner_schema(schema): + return _get_ser_schema_for_default_value(schema['schema']) diff --git a/.venv/lib/python3.12/site-packages/pydantic/main.py b/.venv/lib/python3.12/site-packages/pydantic/main.py new file mode 100644 index 0000000000000000000000000000000000000000..1cc4c24f9ef396b36244e7a1f00eed968c744975 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/main.py @@ -0,0 +1,1814 @@ +"""Logic for creating models.""" + +# Because `dict` is in the local namespace of the `BaseModel` class, we use `Dict` for annotations. +# TODO v3 fallback to `dict` when the deprecated `dict` method gets removed. +# ruff: noqa: UP035 + +from __future__ import annotations as _annotations + +import operator +import sys +import types +import warnings +from collections.abc import Generator, Mapping +from copy import copy, deepcopy +from functools import cached_property +from typing import ( + TYPE_CHECKING, + Any, + Callable, + ClassVar, + Dict, + Generic, + Literal, + TypeVar, + Union, + cast, + overload, +) + +import pydantic_core +import typing_extensions +from pydantic_core import PydanticUndefined, ValidationError +from typing_extensions import Self, TypeAlias, Unpack + +from . import PydanticDeprecatedSince20, PydanticDeprecatedSince211 +from ._internal import ( + _config, + _decorators, + _fields, + _forward_ref, + _generics, + _mock_val_ser, + _model_construction, + _namespace_utils, + _repr, + _typing_extra, + _utils, +) +from ._migration import getattr_migration +from .aliases import AliasChoices, AliasPath +from .annotated_handlers import GetCoreSchemaHandler, GetJsonSchemaHandler +from .config import ConfigDict, ExtraValues +from .errors import PydanticUndefinedAnnotation, PydanticUserError +from .json_schema import DEFAULT_REF_TEMPLATE, GenerateJsonSchema, JsonSchemaMode, JsonSchemaValue, model_json_schema +from .plugin._schema_validator import PluggableSchemaValidator + +if TYPE_CHECKING: + from inspect import Signature + from pathlib import Path + + from pydantic_core import CoreSchema, SchemaSerializer, SchemaValidator + + from ._internal._namespace_utils import MappingNamespace + from ._internal._utils import AbstractSetIntStr, MappingIntStrAny + from .deprecated.parse import Protocol as DeprecatedParseProtocol + from .fields import ComputedFieldInfo, FieldInfo, ModelPrivateAttr + + +__all__ = 'BaseModel', 'create_model' + +# Keep these type aliases available at runtime: +TupleGenerator: TypeAlias = Generator[tuple[str, Any], None, None] +# NOTE: In reality, `bool` should be replaced by `Literal[True]` but mypy fails to correctly apply bidirectional +# type inference (e.g. when using `{'a': {'b': True}}`): +# NOTE: Keep this type alias in sync with the stub definition in `pydantic-core`: +IncEx: TypeAlias = Union[set[int], set[str], Mapping[int, Union['IncEx', bool]], Mapping[str, Union['IncEx', bool]]] + +_object_setattr = _model_construction.object_setattr + + +def _check_frozen(model_cls: type[BaseModel], name: str, value: Any) -> None: + if model_cls.model_config.get('frozen'): + error_type = 'frozen_instance' + elif getattr(model_cls.__pydantic_fields__.get(name), 'frozen', False): + error_type = 'frozen_field' + else: + return + + raise ValidationError.from_exception_data( + model_cls.__name__, [{'type': error_type, 'loc': (name,), 'input': value}] + ) + + +def _model_field_setattr_handler(model: BaseModel, name: str, val: Any) -> None: + model.__dict__[name] = val + model.__pydantic_fields_set__.add(name) + + +def _private_setattr_handler(model: BaseModel, name: str, val: Any) -> None: + if getattr(model, '__pydantic_private__', None) is None: + # While the attribute should be present at this point, this may not be the case if + # users do unusual stuff with `model_post_init()` (which is where the `__pydantic_private__` + # is initialized, by wrapping the user-defined `model_post_init()`), e.g. if they mock + # the `model_post_init()` call. Ideally we should find a better way to init private attrs. + object.__setattr__(model, '__pydantic_private__', {}) + model.__pydantic_private__[name] = val # pyright: ignore[reportOptionalSubscript] + + +_SIMPLE_SETATTR_HANDLERS: Mapping[str, Callable[[BaseModel, str, Any], None]] = { + 'model_field': _model_field_setattr_handler, + 'validate_assignment': lambda model, name, val: model.__pydantic_validator__.validate_assignment(model, name, val), # pyright: ignore[reportAssignmentType] + 'private': _private_setattr_handler, + 'cached_property': lambda model, name, val: model.__dict__.__setitem__(name, val), + 'extra_known': lambda model, name, val: _object_setattr(model, name, val), +} + + +class BaseModel(metaclass=_model_construction.ModelMetaclass): + """!!! abstract "Usage Documentation" + [Models](../concepts/models.md) + + A base class for creating Pydantic models. + + Attributes: + __class_vars__: The names of the class variables defined on the model. + __private_attributes__: Metadata about the private attributes of the model. + __signature__: The synthesized `__init__` [`Signature`][inspect.Signature] of the model. + + __pydantic_complete__: Whether model building is completed, or if there are still undefined fields. + __pydantic_core_schema__: The core schema of the model. + __pydantic_custom_init__: Whether the model has a custom `__init__` function. + __pydantic_decorators__: Metadata containing the decorators defined on the model. + This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1. + __pydantic_generic_metadata__: Metadata for generic models; contains data used for a similar purpose to + __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these. + __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models. + __pydantic_post_init__: The name of the post-init method for the model, if defined. + __pydantic_root_model__: Whether the model is a [`RootModel`][pydantic.root_model.RootModel]. + __pydantic_serializer__: The `pydantic-core` `SchemaSerializer` used to dump instances of the model. + __pydantic_validator__: The `pydantic-core` `SchemaValidator` used to validate instances of the model. + + __pydantic_fields__: A dictionary of field names and their corresponding [`FieldInfo`][pydantic.fields.FieldInfo] objects. + __pydantic_computed_fields__: A dictionary of computed field names and their corresponding [`ComputedFieldInfo`][pydantic.fields.ComputedFieldInfo] objects. + + __pydantic_extra__: A dictionary containing extra values, if [`extra`][pydantic.config.ConfigDict.extra] + is set to `'allow'`. + __pydantic_fields_set__: The names of fields explicitly set during instantiation. + __pydantic_private__: Values of private attributes set on the model instance. + """ + + # Note: Many of the below class vars are defined in the metaclass, but we define them here for type checking purposes. + + model_config: ClassVar[ConfigDict] = ConfigDict() + """ + Configuration for the model, should be a dictionary conforming to [`ConfigDict`][pydantic.config.ConfigDict]. + """ + + __class_vars__: ClassVar[set[str]] + """The names of the class variables defined on the model.""" + + __private_attributes__: ClassVar[Dict[str, ModelPrivateAttr]] # noqa: UP006 + """Metadata about the private attributes of the model.""" + + __signature__: ClassVar[Signature] + """The synthesized `__init__` [`Signature`][inspect.Signature] of the model.""" + + __pydantic_complete__: ClassVar[bool] = False + """Whether model building is completed, or if there are still undefined fields.""" + + __pydantic_core_schema__: ClassVar[CoreSchema] + """The core schema of the model.""" + + __pydantic_custom_init__: ClassVar[bool] + """Whether the model has a custom `__init__` method.""" + + # Must be set for `GenerateSchema.model_schema` to work for a plain `BaseModel` annotation. + __pydantic_decorators__: ClassVar[_decorators.DecoratorInfos] = _decorators.DecoratorInfos() + """Metadata containing the decorators defined on the model. + This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.""" + + __pydantic_generic_metadata__: ClassVar[_generics.PydanticGenericMetadata] + """Metadata for generic models; contains data used for a similar purpose to + __args__, __origin__, __parameters__ in typing-module generics. May eventually be replaced by these.""" + + __pydantic_parent_namespace__: ClassVar[Dict[str, Any] | None] = None # noqa: UP006 + """Parent namespace of the model, used for automatic rebuilding of models.""" + + __pydantic_post_init__: ClassVar[None | Literal['model_post_init']] + """The name of the post-init method for the model, if defined.""" + + __pydantic_root_model__: ClassVar[bool] = False + """Whether the model is a [`RootModel`][pydantic.root_model.RootModel].""" + + __pydantic_serializer__: ClassVar[SchemaSerializer] + """The `pydantic-core` `SchemaSerializer` used to dump instances of the model.""" + + __pydantic_validator__: ClassVar[SchemaValidator | PluggableSchemaValidator] + """The `pydantic-core` `SchemaValidator` used to validate instances of the model.""" + + __pydantic_fields__: ClassVar[Dict[str, FieldInfo]] # noqa: UP006 + """A dictionary of field names and their corresponding [`FieldInfo`][pydantic.fields.FieldInfo] objects. + This replaces `Model.__fields__` from Pydantic V1. + """ + + __pydantic_setattr_handlers__: ClassVar[Dict[str, Callable[[BaseModel, str, Any], None]]] # noqa: UP006 + """`__setattr__` handlers. Memoizing the handlers leads to a dramatic performance improvement in `__setattr__`""" + + __pydantic_computed_fields__: ClassVar[Dict[str, ComputedFieldInfo]] # noqa: UP006 + """A dictionary of computed field names and their corresponding [`ComputedFieldInfo`][pydantic.fields.ComputedFieldInfo] objects.""" + + __pydantic_extra__: Dict[str, Any] | None = _model_construction.NoInitField(init=False) # noqa: UP006 + """A dictionary containing extra values, if [`extra`][pydantic.config.ConfigDict.extra] is set to `'allow'`.""" + + __pydantic_fields_set__: set[str] = _model_construction.NoInitField(init=False) + """The names of fields explicitly set during instantiation.""" + + __pydantic_private__: Dict[str, Any] | None = _model_construction.NoInitField(init=False) # noqa: UP006 + """Values of private attributes set on the model instance.""" + + if not TYPE_CHECKING: + # Prevent `BaseModel` from being instantiated directly + # (defined in an `if not TYPE_CHECKING` block for clarity and to avoid type checking errors): + __pydantic_core_schema__ = _mock_val_ser.MockCoreSchema( + 'Pydantic models should inherit from BaseModel, BaseModel cannot be instantiated directly', + code='base-model-instantiated', + ) + __pydantic_validator__ = _mock_val_ser.MockValSer( + 'Pydantic models should inherit from BaseModel, BaseModel cannot be instantiated directly', + val_or_ser='validator', + code='base-model-instantiated', + ) + __pydantic_serializer__ = _mock_val_ser.MockValSer( + 'Pydantic models should inherit from BaseModel, BaseModel cannot be instantiated directly', + val_or_ser='serializer', + code='base-model-instantiated', + ) + + __slots__ = '__dict__', '__pydantic_fields_set__', '__pydantic_extra__', '__pydantic_private__' + + def __init__(self, /, **data: Any) -> None: + """Create a new model by parsing and validating input data from keyword arguments. + + Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be + validated to form a valid model. + + `self` is explicitly positional-only to allow `self` as a field name. + """ + # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks + __tracebackhide__ = True + validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self) + if self is not validated_self: + warnings.warn( + 'A custom validator is returning a value other than `self`.\n' + "Returning anything other than `self` from a top level model validator isn't supported when validating via `__init__`.\n" + 'See the `model_validator` docs (https://docs.pydantic.dev/latest/concepts/validators/#model-validators) for more details.', + stacklevel=2, + ) + + # The following line sets a flag that we use to determine when `__init__` gets overridden by the user + __init__.__pydantic_base_init__ = True # pyright: ignore[reportFunctionMemberAccess] + + @_utils.deprecated_instance_property + @classmethod + def model_fields(cls) -> dict[str, FieldInfo]: + """A mapping of field names to their respective [`FieldInfo`][pydantic.fields.FieldInfo] instances. + + !!! warning + Accessing this attribute from a model instance is deprecated, and will not work in Pydantic V3. + Instead, you should access this attribute from the model class. + """ + return getattr(cls, '__pydantic_fields__', {}) + + @_utils.deprecated_instance_property + @classmethod + def model_computed_fields(cls) -> dict[str, ComputedFieldInfo]: + """A mapping of computed field names to their respective [`ComputedFieldInfo`][pydantic.fields.ComputedFieldInfo] instances. + + !!! warning + Accessing this attribute from a model instance is deprecated, and will not work in Pydantic V3. + Instead, you should access this attribute from the model class. + """ + return getattr(cls, '__pydantic_computed_fields__', {}) + + @property + def model_extra(self) -> dict[str, Any] | None: + """Get extra fields set during validation. + + Returns: + A dictionary of extra fields, or `None` if `config.extra` is not set to `"allow"`. + """ + return self.__pydantic_extra__ + + @property + def model_fields_set(self) -> set[str]: + """Returns the set of fields that have been explicitly set on this model instance. + + Returns: + A set of strings representing the fields that have been set, + i.e. that were not filled from defaults. + """ + return self.__pydantic_fields_set__ + + @classmethod + def model_construct(cls, _fields_set: set[str] | None = None, **values: Any) -> Self: # noqa: C901 + """Creates a new instance of the `Model` class with validated data. + + Creates a new model setting `__dict__` and `__pydantic_fields_set__` from trusted or pre-validated data. + Default values are respected, but no other validation is performed. + + !!! note + `model_construct()` generally respects the `model_config.extra` setting on the provided model. + That is, if `model_config.extra == 'allow'`, then all extra passed values are added to the model instance's `__dict__` + and `__pydantic_extra__` fields. If `model_config.extra == 'ignore'` (the default), then all extra passed values are ignored. + Because no validation is performed with a call to `model_construct()`, having `model_config.extra == 'forbid'` does not result in + an error if extra values are passed, but they will be ignored. + + Args: + _fields_set: A set of field names that were originally explicitly set during instantiation. If provided, + this is directly used for the [`model_fields_set`][pydantic.BaseModel.model_fields_set] attribute. + Otherwise, the field names from the `values` argument will be used. + values: Trusted or pre-validated data dictionary. + + Returns: + A new instance of the `Model` class with validated data. + """ + m = cls.__new__(cls) + fields_values: dict[str, Any] = {} + fields_set = set() + + for name, field in cls.__pydantic_fields__.items(): + if field.alias is not None and field.alias in values: + fields_values[name] = values.pop(field.alias) + fields_set.add(name) + + if (name not in fields_set) and (field.validation_alias is not None): + validation_aliases: list[str | AliasPath] = ( + field.validation_alias.choices + if isinstance(field.validation_alias, AliasChoices) + else [field.validation_alias] + ) + + for alias in validation_aliases: + if isinstance(alias, str) and alias in values: + fields_values[name] = values.pop(alias) + fields_set.add(name) + break + elif isinstance(alias, AliasPath): + value = alias.search_dict_for_path(values) + if value is not PydanticUndefined: + fields_values[name] = value + fields_set.add(name) + break + + if name not in fields_set: + if name in values: + fields_values[name] = values.pop(name) + fields_set.add(name) + elif not field.is_required(): + fields_values[name] = field.get_default(call_default_factory=True, validated_data=fields_values) + if _fields_set is None: + _fields_set = fields_set + + _extra: dict[str, Any] | None = values if cls.model_config.get('extra') == 'allow' else None + _object_setattr(m, '__dict__', fields_values) + _object_setattr(m, '__pydantic_fields_set__', _fields_set) + if not cls.__pydantic_root_model__: + _object_setattr(m, '__pydantic_extra__', _extra) + + if cls.__pydantic_post_init__: + m.model_post_init(None) + # update private attributes with values set + if hasattr(m, '__pydantic_private__') and m.__pydantic_private__ is not None: + for k, v in values.items(): + if k in m.__private_attributes__: + m.__pydantic_private__[k] = v + + elif not cls.__pydantic_root_model__: + # Note: if there are any private attributes, cls.__pydantic_post_init__ would exist + # Since it doesn't, that means that `__pydantic_private__` should be set to None + _object_setattr(m, '__pydantic_private__', None) + + return m + + def model_copy(self, *, update: Mapping[str, Any] | None = None, deep: bool = False) -> Self: + """!!! abstract "Usage Documentation" + [`model_copy`](../concepts/models.md#model-copy) + + Returns a copy of the model. + + !!! note + The underlying instance's [`__dict__`][object.__dict__] attribute is copied. This + might have unexpected side effects if you store anything in it, on top of the model + fields (e.g. the value of [cached properties][functools.cached_property]). + + Args: + update: Values to change/add in the new model. Note: the data is not validated + before creating the new model. You should trust this data. + deep: Set to `True` to make a deep copy of the model. + + Returns: + New model instance. + """ + copied = self.__deepcopy__() if deep else self.__copy__() + if update: + if self.model_config.get('extra') == 'allow': + for k, v in update.items(): + if k in self.__pydantic_fields__: + copied.__dict__[k] = v + else: + if copied.__pydantic_extra__ is None: + copied.__pydantic_extra__ = {} + copied.__pydantic_extra__[k] = v + else: + copied.__dict__.update(update) + copied.__pydantic_fields_set__.update(update.keys()) + return copied + + 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]: + """!!! abstract "Usage Documentation" + [`model_dump`](../concepts/serialization.md#python-mode) + + 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. + """ + return self.__pydantic_serializer__.to_python( + self, + mode=mode, + by_alias=by_alias, + include=include, + exclude=exclude, + context=context, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + exclude_computed_fields=exclude_computed_fields, + round_trip=round_trip, + warnings=warnings, + fallback=fallback, + serialize_as_any=serialize_as_any, + ) + + 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: + """!!! abstract "Usage Documentation" + [`model_dump_json`](../concepts/serialization.md#json-mode) + + 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. + ensure_ascii: If `True`, the output is guaranteed to have all incoming non-ASCII characters escaped. + If `False` (the default), these characters will be output as-is. + include: Field(s) to include in the JSON output. + exclude: Field(s) to exclude from the JSON output. + context: Additional context to pass to the serializer. + 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 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 JSON string representation of the model. + """ + return self.__pydantic_serializer__.to_json( + self, + indent=indent, + ensure_ascii=ensure_ascii, + include=include, + exclude=exclude, + context=context, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + exclude_computed_fields=exclude_computed_fields, + round_trip=round_trip, + warnings=warnings, + fallback=fallback, + serialize_as_any=serialize_as_any, + ).decode() + + @classmethod + def model_json_schema( + cls, + by_alias: bool = True, + ref_template: str = DEFAULT_REF_TEMPLATE, + schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema, + mode: JsonSchemaMode = 'validation', + *, + union_format: Literal['any_of', 'primitive_type_array'] = 'any_of', + ) -> dict[str, Any]: + """Generates a JSON schema for a model class. + + Args: + by_alias: Whether to use attribute aliases or not. + ref_template: The reference template. + union_format: The format to use when combining schemas from unions together. Can be one of: + + - `'any_of'`: Use the [`anyOf`](https://json-schema.org/understanding-json-schema/reference/combining#anyOf) + keyword to combine schemas (the default). + - `'primitive_type_array'`: Use the [`type`](https://json-schema.org/understanding-json-schema/reference/type) + keyword as an array of strings, containing each type of the combination. If any of the schemas is not a primitive + type (`string`, `boolean`, `null`, `integer` or `number`) or contains constraints/metadata, falls back to + `any_of`. + schema_generator: To override the logic used to generate the JSON schema, as a subclass of + `GenerateJsonSchema` with your desired modifications + mode: The mode in which to generate the schema. + + Returns: + The JSON schema for the given model class. + """ + return model_json_schema( + cls, + by_alias=by_alias, + ref_template=ref_template, + union_format=union_format, + schema_generator=schema_generator, + mode=mode, + ) + + @classmethod + def model_parametrized_name(cls, params: tuple[type[Any], ...]) -> str: + """Compute the class name for parametrizations of generic classes. + + This method can be overridden to achieve a custom naming scheme for generic BaseModels. + + Args: + params: Tuple of types of the class. Given a generic class + `Model` with 2 type variables and a concrete model `Model[str, int]`, + the value `(str, int)` would be passed to `params`. + + Returns: + String representing the new class where `params` are passed to `cls` as type variables. + + Raises: + TypeError: Raised when trying to generate concrete names for non-generic models. + """ + if not issubclass(cls, Generic): + raise TypeError('Concrete names should only be generated for generic models.') + + # Any strings received should represent forward references, so we handle them specially below. + # If we eventually move toward wrapping them in a ForwardRef in __class_getitem__ in the future, + # we may be able to remove this special case. + param_names = [param if isinstance(param, str) else _repr.display_as_type(param) for param in params] + params_component = ', '.join(param_names) + return f'{cls.__name__}[{params_component}]' + + def model_post_init(self, context: Any, /) -> None: + """Override this method to perform additional initialization after `__init__` and `model_construct`. + This is useful if you want to do some validation that requires the entire model to be initialized. + """ + + @classmethod + def model_rebuild( + cls, + *, + force: bool = False, + raise_errors: bool = True, + _parent_namespace_depth: int = 2, + _types_namespace: MappingNamespace | None = None, + ) -> bool | None: + """Try to rebuild the pydantic-core schema for the model. + + This may be necessary when one of the annotations is a ForwardRef which could not be resolved during + the initial attempt to build the schema, and automatic rebuilding fails. + + Args: + force: Whether to force the rebuilding of the model schema, defaults to `False`. + raise_errors: Whether to raise errors, defaults to `True`. + _parent_namespace_depth: The depth level of the parent namespace, defaults to 2. + _types_namespace: The types namespace, defaults to `None`. + + Returns: + Returns `None` if the schema is already "complete" and rebuilding was not required. + If rebuilding _was_ required, returns `True` if rebuilding was successful, otherwise `False`. + """ + already_complete = cls.__pydantic_complete__ + if already_complete and not force: + return None + + cls.__pydantic_complete__ = False + + for attr in ('__pydantic_core_schema__', '__pydantic_validator__', '__pydantic_serializer__'): + if attr in cls.__dict__ and not isinstance(getattr(cls, attr), _mock_val_ser.MockValSer): + # Deleting the validator/serializer is necessary as otherwise they can get reused in + # pydantic-core. We do so only if they aren't mock instances, otherwise — as `model_rebuild()` + # isn't thread-safe — concurrent model instantiations can lead to the parent validator being used. + # Same applies for the core schema that can be reused in schema generation. + delattr(cls, attr) + + if _types_namespace is not None: + rebuild_ns = _types_namespace + elif _parent_namespace_depth > 0: + rebuild_ns = _typing_extra.parent_frame_namespace(parent_depth=_parent_namespace_depth, force=True) or {} + else: + rebuild_ns = {} + + parent_ns = _model_construction.unpack_lenient_weakvaluedict(cls.__pydantic_parent_namespace__) or {} + + ns_resolver = _namespace_utils.NsResolver( + parent_namespace={**rebuild_ns, **parent_ns}, + ) + + return _model_construction.complete_model_class( + cls, + _config.ConfigWrapper(cls.model_config, check=False), + ns_resolver, + raise_errors=raise_errors, + # If the model was already complete, we don't need to call the hook again. + call_on_complete_hook=not already_complete, + ) + + @classmethod + def model_validate( + cls, + obj: Any, + *, + strict: bool | None = None, + extra: ExtraValues | None = None, + from_attributes: bool | None = None, + context: Any | None = None, + by_alias: bool | None = None, + by_name: bool | None = None, + ) -> Self: + """Validate a pydantic model instance. + + Args: + obj: The object to validate. + strict: Whether to enforce types strictly. + extra: Whether to ignore, allow, or forbid extra data during model validation. + See the [`extra` configuration value][pydantic.ConfigDict.extra] for details. + from_attributes: Whether to extract data from object attributes. + context: Additional context to pass to the validator. + by_alias: Whether to use the field's alias when validating against the provided input data. + by_name: Whether to use the field's name when validating against the provided input data. + + Raises: + ValidationError: If the object could not be validated. + + Returns: + The validated model instance. + """ + # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks + __tracebackhide__ = True + + if by_alias is False and by_name is not True: + raise PydanticUserError( + 'At least one of `by_alias` or `by_name` must be set to True.', + code='validate-by-alias-and-name-false', + ) + + return cls.__pydantic_validator__.validate_python( + obj, + strict=strict, + extra=extra, + from_attributes=from_attributes, + context=context, + by_alias=by_alias, + by_name=by_name, + ) + + @classmethod + def model_validate_json( + cls, + json_data: str | bytes | bytearray, + *, + strict: bool | None = None, + extra: ExtraValues | None = None, + context: Any | None = None, + by_alias: bool | None = None, + by_name: bool | None = None, + ) -> Self: + """!!! abstract "Usage Documentation" + [JSON Parsing](../concepts/json.md#json-parsing) + + Validate the given JSON data against the Pydantic model. + + Args: + json_data: The JSON data to validate. + strict: Whether to enforce types strictly. + extra: Whether to ignore, allow, or forbid extra data during model validation. + See the [`extra` configuration value][pydantic.ConfigDict.extra] for details. + context: Extra variables to pass to the validator. + by_alias: Whether to use the field's alias when validating against the provided input data. + by_name: Whether to use the field's name when validating against the provided input data. + + Returns: + The validated Pydantic model. + + Raises: + ValidationError: If `json_data` is not a JSON string or the object could not be validated. + """ + # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks + __tracebackhide__ = True + + if by_alias is False and by_name is not True: + raise PydanticUserError( + 'At least one of `by_alias` or `by_name` must be set to True.', + code='validate-by-alias-and-name-false', + ) + + return cls.__pydantic_validator__.validate_json( + json_data, strict=strict, extra=extra, context=context, by_alias=by_alias, by_name=by_name + ) + + @classmethod + def model_validate_strings( + cls, + obj: Any, + *, + strict: bool | None = None, + extra: ExtraValues | None = None, + context: Any | None = None, + by_alias: bool | None = None, + by_name: bool | None = None, + ) -> Self: + """Validate the given object with string data against the Pydantic model. + + Args: + obj: The object containing string data to validate. + strict: Whether to enforce types strictly. + extra: Whether to ignore, allow, or forbid extra data during model validation. + See the [`extra` configuration value][pydantic.ConfigDict.extra] for details. + context: Extra variables to pass to the validator. + by_alias: Whether to use the field's alias when validating against the provided input data. + by_name: Whether to use the field's name when validating against the provided input data. + + Returns: + The validated Pydantic model. + """ + # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks + __tracebackhide__ = True + + if by_alias is False and by_name is not True: + raise PydanticUserError( + 'At least one of `by_alias` or `by_name` must be set to True.', + code='validate-by-alias-and-name-false', + ) + + return cls.__pydantic_validator__.validate_strings( + obj, strict=strict, extra=extra, context=context, by_alias=by_alias, by_name=by_name + ) + + @classmethod + def __get_pydantic_core_schema__(cls, source: type[BaseModel], handler: GetCoreSchemaHandler, /) -> CoreSchema: + # This warning is only emitted when calling `super().__get_pydantic_core_schema__` from a model subclass. + # In the generate schema logic, this method (`BaseModel.__get_pydantic_core_schema__`) is special cased to + # *not* be called if not overridden. + warnings.warn( + 'The `__get_pydantic_core_schema__` method of the `BaseModel` class is deprecated. If you are calling ' + '`super().__get_pydantic_core_schema__` when overriding the method on a Pydantic model, consider using ' + '`handler(source)` instead. However, note that overriding this method on models can lead to unexpected ' + 'side effects.', + PydanticDeprecatedSince211, + stacklevel=2, + ) + # Logic copied over from `GenerateSchema._model_schema`: + schema = cls.__dict__.get('__pydantic_core_schema__') + if schema is not None and not isinstance(schema, _mock_val_ser.MockCoreSchema): + return cls.__pydantic_core_schema__ + + return handler(source) + + @classmethod + def __get_pydantic_json_schema__( + cls, + core_schema: CoreSchema, + handler: GetJsonSchemaHandler, + /, + ) -> JsonSchemaValue: + """Hook into generating the model's JSON schema. + + Args: + core_schema: A `pydantic-core` CoreSchema. + You can ignore this argument and call the handler with a new CoreSchema, + wrap this CoreSchema (`{'type': 'nullable', 'schema': current_schema}`), + or just call the handler with the original schema. + handler: Call into Pydantic's internal JSON schema generation. + This will raise a `pydantic.errors.PydanticInvalidForJsonSchema` if JSON schema + generation fails. + Since this gets called by `BaseModel.model_json_schema` you can override the + `schema_generator` argument to that function to change JSON schema generation globally + for a type. + + Returns: + A JSON schema, as a Python object. + """ + return handler(core_schema) + + @classmethod + def __pydantic_init_subclass__(cls, **kwargs: Any) -> None: + """This is intended to behave just like `__init_subclass__`, but is called by `ModelMetaclass` + only after basic class initialization is complete. In particular, attributes like `model_fields` will + be present when this is called, but forward annotations are not guaranteed to be resolved yet, + meaning that creating an instance of the class may fail. + + This is necessary because `__init_subclass__` will always be called by `type.__new__`, + and it would require a prohibitively large refactor to the `ModelMetaclass` to ensure that + `type.__new__` was called in such a manner that the class would already be sufficiently initialized. + + This will receive the same `kwargs` that would be passed to the standard `__init_subclass__`, namely, + any kwargs passed to the class definition that aren't used internally by Pydantic. + + Args: + **kwargs: Any keyword arguments passed to the class definition that aren't used internally + by Pydantic. + + Note: + You may want to override [`__pydantic_on_complete__()`][pydantic.main.BaseModel.__pydantic_on_complete__] + instead, which is called once the class and its fields are fully initialized and ready for validation. + """ + + @classmethod + def __pydantic_on_complete__(cls) -> None: + """This is called once the class and its fields are fully initialized and ready to be used. + + This typically happens when the class is created (just before + [`__pydantic_init_subclass__()`][pydantic.main.BaseModel.__pydantic_init_subclass__] is called on the superclass), + except when forward annotations are used that could not immediately be resolved. + In that case, it will be called later, when the model is rebuilt automatically or explicitly using + [`model_rebuild()`][pydantic.main.BaseModel.model_rebuild]. + """ + + def __class_getitem__( + cls, typevar_values: type[Any] | tuple[type[Any], ...] + ) -> type[BaseModel] | _forward_ref.PydanticRecursiveRef: + cached = _generics.get_cached_generic_type_early(cls, typevar_values) + if cached is not None: + return cached + + if cls is BaseModel: + raise TypeError('Type parameters should be placed on typing.Generic, not BaseModel') + if not hasattr(cls, '__parameters__'): + raise TypeError(f'{cls} cannot be parametrized because it does not inherit from typing.Generic') + if not cls.__pydantic_generic_metadata__['parameters'] and Generic not in cls.__bases__: + raise TypeError(f'{cls} is not a generic class') + + if not isinstance(typevar_values, tuple): + typevar_values = (typevar_values,) + + # For a model `class Model[T, U, V = int](BaseModel): ...` parametrized with `(str, bool)`, + # this gives us `{T: str, U: bool, V: int}`: + typevars_map = _generics.map_generic_model_arguments(cls, typevar_values) + # We also update the provided args to use defaults values (`(str, bool)` becomes `(str, bool, int)`): + typevar_values = tuple(v for v in typevars_map.values()) + + if _utils.all_identical(typevars_map.keys(), typevars_map.values()) and typevars_map: + submodel = cls # if arguments are equal to parameters it's the same object + _generics.set_cached_generic_type(cls, typevar_values, submodel) + else: + parent_args = cls.__pydantic_generic_metadata__['args'] + if not parent_args: + args = typevar_values + else: + args = tuple(_generics.replace_types(arg, typevars_map) for arg in parent_args) + + origin = cls.__pydantic_generic_metadata__['origin'] or cls + model_name = origin.model_parametrized_name(args) + params = tuple( + dict.fromkeys(_generics.iter_contained_typevars(typevars_map.values())) + ) # use dict as ordered set + + with _generics.generic_recursion_self_type(origin, args) as maybe_self_type: + cached = _generics.get_cached_generic_type_late(cls, typevar_values, origin, args) + if cached is not None: + return cached + + if maybe_self_type is not None: + return maybe_self_type + + # Attempt to rebuild the origin in case new types have been defined + try: + # depth 2 gets you above this __class_getitem__ call. + # Note that we explicitly provide the parent ns, otherwise + # `model_rebuild` will use the parent ns no matter if it is the ns of a module. + # We don't want this here, as this has unexpected effects when a model + # is being parametrized during a forward annotation evaluation. + parent_ns = _typing_extra.parent_frame_namespace(parent_depth=2) or {} + origin.model_rebuild(_types_namespace=parent_ns) + except PydanticUndefinedAnnotation: + # It's okay if it fails, it just means there are still undefined types + # that could be evaluated later. + pass + + submodel = _generics.create_generic_submodel(model_name, origin, args, params) + + _generics.set_cached_generic_type(cls, typevar_values, submodel, origin, args) + + return submodel + + def __copy__(self) -> Self: + """Returns a shallow copy of the model.""" + cls = type(self) + m = cls.__new__(cls) + _object_setattr(m, '__dict__', copy(self.__dict__)) + _object_setattr(m, '__pydantic_extra__', copy(self.__pydantic_extra__)) + _object_setattr(m, '__pydantic_fields_set__', copy(self.__pydantic_fields_set__)) + + if not hasattr(self, '__pydantic_private__') or self.__pydantic_private__ is None: + _object_setattr(m, '__pydantic_private__', None) + else: + _object_setattr( + m, + '__pydantic_private__', + {k: v for k, v in self.__pydantic_private__.items() if v is not PydanticUndefined}, + ) + + return m + + def __deepcopy__(self, memo: dict[int, Any] | None = None) -> Self: + """Returns a deep copy of the model.""" + cls = type(self) + m = cls.__new__(cls) + _object_setattr(m, '__dict__', deepcopy(self.__dict__, memo=memo)) + _object_setattr(m, '__pydantic_extra__', deepcopy(self.__pydantic_extra__, memo=memo)) + # This next line doesn't need a deepcopy because __pydantic_fields_set__ is a set[str], + # and attempting a deepcopy would be marginally slower. + _object_setattr(m, '__pydantic_fields_set__', copy(self.__pydantic_fields_set__)) + + if not hasattr(self, '__pydantic_private__') or self.__pydantic_private__ is None: + _object_setattr(m, '__pydantic_private__', None) + else: + _object_setattr( + m, + '__pydantic_private__', + deepcopy({k: v for k, v in self.__pydantic_private__.items() if v is not PydanticUndefined}, memo=memo), + ) + + return m + + if not TYPE_CHECKING: + # We put `__getattr__` in a non-TYPE_CHECKING block because otherwise, mypy allows arbitrary attribute access + # The same goes for __setattr__ and __delattr__, see: https://github.com/pydantic/pydantic/issues/8643 + + def __getattr__(self, item: str) -> Any: + private_attributes = object.__getattribute__(self, '__private_attributes__') + if item in private_attributes: + attribute = private_attributes[item] + if hasattr(attribute, '__get__'): + return attribute.__get__(self, type(self)) # type: ignore + + try: + # Note: self.__pydantic_private__ cannot be None if self.__private_attributes__ has items + return self.__pydantic_private__[item] # type: ignore + except KeyError as exc: + raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}') from exc + else: + # `__pydantic_extra__` can fail to be set if the model is not yet fully initialized. + # See `BaseModel.__repr_args__` for more details + try: + pydantic_extra = object.__getattribute__(self, '__pydantic_extra__') + except AttributeError: + pydantic_extra = None + + if pydantic_extra and item in pydantic_extra: + return pydantic_extra[item] + else: + if hasattr(self.__class__, item): + return super().__getattribute__(item) # Raises AttributeError if appropriate + else: + # this is the current error + raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}') + + def __setattr__(self, name: str, value: Any) -> None: + if (setattr_handler := self.__pydantic_setattr_handlers__.get(name)) is not None: + setattr_handler(self, name, value) + # if None is returned from _setattr_handler, the attribute was set directly + elif (setattr_handler := self._setattr_handler(name, value)) is not None: + setattr_handler(self, name, value) # call here to not memo on possibly unknown fields + self.__pydantic_setattr_handlers__[name] = setattr_handler # memoize the handler for faster access + + def _setattr_handler(self, name: str, value: Any) -> Callable[[BaseModel, str, Any], None] | None: + """Get a handler for setting an attribute on the model instance. + + Returns: + A handler for setting an attribute on the model instance. Used for memoization of the handler. + Memoizing the handlers leads to a dramatic performance improvement in `__setattr__` + Returns `None` when memoization is not safe, then the attribute is set directly. + """ + cls = self.__class__ + if name in cls.__class_vars__: + raise AttributeError( + f'{name!r} is a ClassVar of `{cls.__name__}` and cannot be set on an instance. ' + f'If you want to set a value on the class, use `{cls.__name__}.{name} = value`.' + ) + elif not _fields.is_valid_field_name(name): + if (attribute := cls.__private_attributes__.get(name)) is not None: + if hasattr(attribute, '__set__'): + return lambda model, _name, val: attribute.__set__(model, val) + else: + return _SIMPLE_SETATTR_HANDLERS['private'] + else: + _object_setattr(self, name, value) + return None # Can not return memoized handler with possibly freeform attr names + + attr = getattr(cls, name, None) + # NOTE: We currently special case properties and `cached_property`, but we might need + # to generalize this to all data/non-data descriptors at some point. For non-data descriptors + # (such as `cached_property`), it isn't obvious though. `cached_property` caches the value + # to the instance's `__dict__`, but other non-data descriptors might do things differently. + if isinstance(attr, cached_property): + return _SIMPLE_SETATTR_HANDLERS['cached_property'] + + _check_frozen(cls, name, value) + + # We allow properties to be set only on non frozen models for now (to match dataclasses). + # This can be changed if it ever gets requested. + if isinstance(attr, property): + return lambda model, _name, val: attr.__set__(model, val) + elif cls.model_config.get('validate_assignment'): + return _SIMPLE_SETATTR_HANDLERS['validate_assignment'] + elif name not in cls.__pydantic_fields__: + if cls.model_config.get('extra') != 'allow': + # TODO - matching error + raise ValueError(f'"{cls.__name__}" object has no field "{name}"') + elif attr is None: + # attribute does not exist, so put it in extra + self.__pydantic_extra__[name] = value + return None # Can not return memoized handler with possibly freeform attr names + else: + # attribute _does_ exist, and was not in extra, so update it + return _SIMPLE_SETATTR_HANDLERS['extra_known'] + else: + return _SIMPLE_SETATTR_HANDLERS['model_field'] + + def __delattr__(self, item: str) -> Any: + cls = self.__class__ + + if item in self.__private_attributes__: + attribute = self.__private_attributes__[item] + if hasattr(attribute, '__delete__'): + attribute.__delete__(self) # type: ignore + return + + try: + # Note: self.__pydantic_private__ cannot be None if self.__private_attributes__ has items + del self.__pydantic_private__[item] # type: ignore + return + except KeyError as exc: + raise AttributeError(f'{cls.__name__!r} object has no attribute {item!r}') from exc + + # Allow cached properties to be deleted (even if the class is frozen): + attr = getattr(cls, item, None) + if isinstance(attr, cached_property): + return object.__delattr__(self, item) + + _check_frozen(cls, name=item, value=None) + + if item in self.__pydantic_fields__: + object.__delattr__(self, item) + elif self.__pydantic_extra__ is not None and item in self.__pydantic_extra__: + del self.__pydantic_extra__[item] + else: + try: + object.__delattr__(self, item) + except AttributeError: + raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}') + + # Because we make use of `@dataclass_transform()`, `__replace__` is already synthesized by + # type checkers, so we define the implementation in this `if not TYPE_CHECKING:` block: + def __replace__(self, **changes: Any) -> Self: + return self.model_copy(update=changes) + + def __getstate__(self) -> dict[Any, Any]: + private = self.__pydantic_private__ + if private: + private = {k: v for k, v in private.items() if v is not PydanticUndefined} + return { + '__dict__': self.__dict__, + '__pydantic_extra__': self.__pydantic_extra__, + '__pydantic_fields_set__': self.__pydantic_fields_set__, + '__pydantic_private__': private, + } + + def __setstate__(self, state: dict[Any, Any]) -> None: + _object_setattr(self, '__pydantic_fields_set__', state.get('__pydantic_fields_set__', {})) + _object_setattr(self, '__pydantic_extra__', state.get('__pydantic_extra__', {})) + _object_setattr(self, '__pydantic_private__', state.get('__pydantic_private__', {})) + _object_setattr(self, '__dict__', state.get('__dict__', {})) + + if not TYPE_CHECKING: + + def __eq__(self, other: Any) -> bool: + if isinstance(other, BaseModel): + # When comparing instances of generic types for equality, as long as all field values are equal, + # only require their generic origin types to be equal, rather than exact type equality. + # This prevents headaches like MyGeneric(x=1) != MyGeneric[Any](x=1). + self_type = self.__pydantic_generic_metadata__['origin'] or self.__class__ + other_type = other.__pydantic_generic_metadata__['origin'] or other.__class__ + + # Perform common checks first + if not ( + self_type == other_type + and getattr(self, '__pydantic_private__', None) == getattr(other, '__pydantic_private__', None) + and self.__pydantic_extra__ == other.__pydantic_extra__ + ): + return False + + # We only want to compare pydantic fields but ignoring fields is costly. + # We'll perform a fast check first, and fallback only when needed + # See GH-7444 and GH-7825 for rationale and a performance benchmark + + # First, do the fast (and sometimes faulty) __dict__ comparison + if self.__dict__ == other.__dict__: + # If the check above passes, then pydantic fields are equal, we can return early + return True + + # We don't want to trigger unnecessary costly filtering of __dict__ on all unequal objects, so we return + # early if there are no keys to ignore (we would just return False later on anyway) + model_fields = type(self).__pydantic_fields__.keys() + if self.__dict__.keys() <= model_fields and other.__dict__.keys() <= model_fields: + return False + + # If we reach here, there are non-pydantic-fields keys, mapped to unequal values, that we need to ignore + # Resort to costly filtering of the __dict__ objects + # We use operator.itemgetter because it is much faster than dict comprehensions + # NOTE: Contrary to standard python class and instances, when the Model class has a default value for an + # attribute and the model instance doesn't have a corresponding attribute, accessing the missing attribute + # raises an error in BaseModel.__getattr__ instead of returning the class attribute + # So we can use operator.itemgetter() instead of operator.attrgetter() + getter = operator.itemgetter(*model_fields) if model_fields else lambda _: _utils._SENTINEL + try: + return getter(self.__dict__) == getter(other.__dict__) + except KeyError: + # In rare cases (such as when using the deprecated BaseModel.copy() method), + # the __dict__ may not contain all model fields, which is how we can get here. + # getter(self.__dict__) is much faster than any 'safe' method that accounts + # for missing keys, and wrapping it in a `try` doesn't slow things down much + # in the common case. + self_fields_proxy = _utils.SafeGetItemProxy(self.__dict__) + other_fields_proxy = _utils.SafeGetItemProxy(other.__dict__) + return getter(self_fields_proxy) == getter(other_fields_proxy) + + # other instance is not a BaseModel + else: + return NotImplemented # delegate to the other item in the comparison + + if TYPE_CHECKING: + # We put `__init_subclass__` in a TYPE_CHECKING block because, even though we want the type-checking benefits + # described in the signature of `__init_subclass__` below, we don't want to modify the default behavior of + # subclass initialization. + + def __init_subclass__(cls, **kwargs: Unpack[ConfigDict]): + """This signature is included purely to help type-checkers check arguments to class declaration, which + provides a way to conveniently set model_config key/value pairs. + + ```python + from pydantic import BaseModel + + class MyModel(BaseModel, extra='allow'): ... + ``` + + However, this may be deceiving, since the _actual_ calls to `__init_subclass__` will not receive any + of the config arguments, and will only receive any keyword arguments passed during class initialization + that are _not_ expected keys in ConfigDict. (This is due to the way `ModelMetaclass.__new__` works.) + + Args: + **kwargs: Keyword arguments passed to the class definition, which set model_config + + Note: + You may want to override `__pydantic_init_subclass__` instead, which behaves similarly but is called + *after* the class is fully initialized. + """ + + def __iter__(self) -> TupleGenerator: + """So `dict(model)` works.""" + yield from [(k, v) for (k, v) in self.__dict__.items() if not k.startswith('_')] + extra = self.__pydantic_extra__ + if extra: + yield from extra.items() + + def __repr__(self) -> str: + return f'{self.__repr_name__()}({self.__repr_str__(", ")})' + + def __repr_args__(self) -> _repr.ReprArgs: + # Eagerly create the repr of computed fields, as this may trigger access of cached properties and as such + # modify the instance's `__dict__`. If we don't do it now, it could happen when iterating over the `__dict__` + # below if the instance happens to be referenced in a field, and would modify the `__dict__` size *during* iteration. + computed_fields_repr_args = [ + (k, getattr(self, k)) for k, v in self.__pydantic_computed_fields__.items() if v.repr + ] + + for k, v in self.__dict__.items(): + field = self.__pydantic_fields__.get(k) + if field and field.repr: + if v is not self: + yield k, v + else: + yield k, self.__repr_recursion__(v) + # `__pydantic_extra__` can fail to be set if the model is not yet fully initialized. + # This can happen if a `ValidationError` is raised during initialization and the instance's + # repr is generated as part of the exception handling. Therefore, we use `getattr` here + # with a fallback, even though the type hints indicate the attribute will always be present. + try: + pydantic_extra = object.__getattribute__(self, '__pydantic_extra__') + except AttributeError: + pydantic_extra = None + + if pydantic_extra is not None: + yield from ((k, v) for k, v in pydantic_extra.items()) + yield from computed_fields_repr_args + + # take logic from `_repr.Representation` without the side effects of inheritance, see #5740 + __repr_name__ = _repr.Representation.__repr_name__ + __repr_recursion__ = _repr.Representation.__repr_recursion__ + __repr_str__ = _repr.Representation.__repr_str__ + __pretty__ = _repr.Representation.__pretty__ + __rich_repr__ = _repr.Representation.__rich_repr__ + + def __str__(self) -> str: + return self.__repr_str__(' ') + + # ##### Deprecated methods from v1 ##### + @property + @typing_extensions.deprecated( + 'The `__fields__` attribute is deprecated, use the `model_fields` class property instead.', category=None + ) + def __fields__(self) -> dict[str, FieldInfo]: + warnings.warn( + 'The `__fields__` attribute is deprecated, use the `model_fields` class property instead.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + return getattr(type(self), '__pydantic_fields__', {}) + + @property + @typing_extensions.deprecated( + 'The `__fields_set__` attribute is deprecated, use `model_fields_set` instead.', + category=None, + ) + def __fields_set__(self) -> set[str]: + warnings.warn( + 'The `__fields_set__` attribute is deprecated, use `model_fields_set` instead.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + return self.__pydantic_fields_set__ + + @typing_extensions.deprecated('The `dict` method is deprecated; use `model_dump` instead.', category=None) + def dict( # noqa: D102 + self, + *, + include: IncEx | None = None, + exclude: IncEx | None = None, + by_alias: bool = False, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + ) -> Dict[str, Any]: # noqa UP006 + warnings.warn( + 'The `dict` method is deprecated; use `model_dump` instead.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + return self.model_dump( + include=include, + exclude=exclude, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + + @typing_extensions.deprecated('The `json` method is deprecated; use `model_dump_json` instead.', category=None) + def json( # noqa: D102 + self, + *, + include: IncEx | None = None, + exclude: IncEx | None = None, + by_alias: bool = False, + exclude_unset: bool = False, + exclude_defaults: bool = False, + exclude_none: bool = False, + encoder: Callable[[Any], Any] | None = PydanticUndefined, # type: ignore[assignment] + models_as_dict: bool = PydanticUndefined, # type: ignore[assignment] + **dumps_kwargs: Any, + ) -> str: + warnings.warn( + 'The `json` method is deprecated; use `model_dump_json` instead.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + if encoder is not PydanticUndefined: + raise TypeError('The `encoder` argument is no longer supported; use field serializers instead.') + if models_as_dict is not PydanticUndefined: + raise TypeError('The `models_as_dict` argument is no longer supported; use a model serializer instead.') + if dumps_kwargs: + raise TypeError('`dumps_kwargs` keyword arguments are no longer supported.') + return self.model_dump_json( + include=include, + exclude=exclude, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + ) + + @classmethod + @typing_extensions.deprecated('The `parse_obj` method is deprecated; use `model_validate` instead.', category=None) + def parse_obj(cls, obj: Any) -> Self: # noqa: D102 + warnings.warn( + 'The `parse_obj` method is deprecated; use `model_validate` instead.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + return cls.model_validate(obj) + + @classmethod + @typing_extensions.deprecated( + 'The `parse_raw` method is deprecated; if your data is JSON use `model_validate_json`, ' + 'otherwise load the data then use `model_validate` instead.', + category=None, + ) + def parse_raw( # noqa: D102 + cls, + b: str | bytes, + *, + content_type: str | None = None, + encoding: str = 'utf8', + proto: DeprecatedParseProtocol | None = None, + allow_pickle: bool = False, + ) -> Self: # pragma: no cover + warnings.warn( + 'The `parse_raw` method is deprecated; if your data is JSON use `model_validate_json`, ' + 'otherwise load the data then use `model_validate` instead.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + from .deprecated import parse + + try: + obj = parse.load_str_bytes( + b, + proto=proto, + content_type=content_type, + encoding=encoding, + allow_pickle=allow_pickle, + ) + except (ValueError, TypeError) as exc: + import json + + # try to match V1 + if isinstance(exc, UnicodeDecodeError): + type_str = 'value_error.unicodedecode' + elif isinstance(exc, json.JSONDecodeError): + type_str = 'value_error.jsondecode' + elif isinstance(exc, ValueError): + type_str = 'value_error' + else: + type_str = 'type_error' + + # ctx is missing here, but since we've added `input` to the error, we're not pretending it's the same + error: pydantic_core.InitErrorDetails = { + # The type: ignore on the next line is to ignore the requirement of LiteralString + 'type': pydantic_core.PydanticCustomError(type_str, str(exc)), # type: ignore + 'loc': ('__root__',), + 'input': b, + } + raise pydantic_core.ValidationError.from_exception_data(cls.__name__, [error]) + return cls.model_validate(obj) + + @classmethod + @typing_extensions.deprecated( + 'The `parse_file` method is deprecated; load the data from file, then if your data is JSON ' + 'use `model_validate_json`, otherwise `model_validate` instead.', + category=None, + ) + def parse_file( # noqa: D102 + cls, + path: str | Path, + *, + content_type: str | None = None, + encoding: str = 'utf8', + proto: DeprecatedParseProtocol | None = None, + allow_pickle: bool = False, + ) -> Self: + warnings.warn( + 'The `parse_file` method is deprecated; load the data from file, then if your data is JSON ' + 'use `model_validate_json`, otherwise `model_validate` instead.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + from .deprecated import parse + + obj = parse.load_file( + path, + proto=proto, + content_type=content_type, + encoding=encoding, + allow_pickle=allow_pickle, + ) + return cls.parse_obj(obj) + + @classmethod + @typing_extensions.deprecated( + 'The `from_orm` method is deprecated; set ' + "`model_config['from_attributes']=True` and use `model_validate` instead.", + category=None, + ) + def from_orm(cls, obj: Any) -> Self: # noqa: D102 + warnings.warn( + 'The `from_orm` method is deprecated; set ' + "`model_config['from_attributes']=True` and use `model_validate` instead.", + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + if not cls.model_config.get('from_attributes', None): + raise PydanticUserError( + 'You must set the config attribute `from_attributes=True` to use from_orm', code=None + ) + return cls.model_validate(obj) + + @classmethod + @typing_extensions.deprecated('The `construct` method is deprecated; use `model_construct` instead.', category=None) + def construct(cls, _fields_set: set[str] | None = None, **values: Any) -> Self: # noqa: D102 + warnings.warn( + 'The `construct` method is deprecated; use `model_construct` instead.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + return cls.model_construct(_fields_set=_fields_set, **values) + + @typing_extensions.deprecated( + 'The `copy` method is deprecated; use `model_copy` instead. ' + 'See the docstring of `BaseModel.copy` for details about how to handle `include` and `exclude`.', + category=None, + ) + def copy( + self, + *, + include: AbstractSetIntStr | MappingIntStrAny | None = None, + exclude: AbstractSetIntStr | MappingIntStrAny | None = None, + update: Dict[str, Any] | None = None, # noqa UP006 + deep: bool = False, + ) -> Self: # pragma: no cover + """Returns a copy of the model. + + !!! warning "Deprecated" + This method is now deprecated; use `model_copy` instead. + + If you need `include` or `exclude`, use: + + ```python {test="skip" lint="skip"} + data = self.model_dump(include=include, exclude=exclude, round_trip=True) + data = {**data, **(update or {})} + copied = self.model_validate(data) + ``` + + Args: + include: Optional set or mapping specifying which fields to include in the copied model. + exclude: Optional set or mapping specifying which fields to exclude in the copied model. + update: Optional dictionary of field-value pairs to override field values in the copied model. + deep: If True, the values of fields that are Pydantic models will be deep-copied. + + Returns: + A copy of the model with included, excluded and updated fields as specified. + """ + warnings.warn( + 'The `copy` method is deprecated; use `model_copy` instead. ' + 'See the docstring of `BaseModel.copy` for details about how to handle `include` and `exclude`.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + from .deprecated import copy_internals + + values = dict( + copy_internals._iter( + self, to_dict=False, by_alias=False, include=include, exclude=exclude, exclude_unset=False + ), + **(update or {}), + ) + if self.__pydantic_private__ is None: + private = None + else: + private = {k: v for k, v in self.__pydantic_private__.items() if v is not PydanticUndefined} + + if self.__pydantic_extra__ is None: + extra: dict[str, Any] | None = None + else: + extra = self.__pydantic_extra__.copy() + for k in list(self.__pydantic_extra__): + if k not in values: # k was in the exclude + extra.pop(k) + for k in list(values): + if k in self.__pydantic_extra__: # k must have come from extra + extra[k] = values.pop(k) + + # new `__pydantic_fields_set__` can have unset optional fields with a set value in `update` kwarg + if update: + fields_set = self.__pydantic_fields_set__ | update.keys() + else: + fields_set = set(self.__pydantic_fields_set__) + + # removing excluded fields from `__pydantic_fields_set__` + if exclude: + fields_set -= set(exclude) + + return copy_internals._copy_and_set_values(self, values, fields_set, extra, private, deep=deep) + + @classmethod + @typing_extensions.deprecated('The `schema` method is deprecated; use `model_json_schema` instead.', category=None) + def schema( # noqa: D102 + cls, by_alias: bool = True, ref_template: str = DEFAULT_REF_TEMPLATE + ) -> Dict[str, Any]: # noqa UP006 + warnings.warn( + 'The `schema` method is deprecated; use `model_json_schema` instead.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + return cls.model_json_schema(by_alias=by_alias, ref_template=ref_template) + + @classmethod + @typing_extensions.deprecated( + 'The `schema_json` method is deprecated; use `model_json_schema` and json.dumps instead.', + category=None, + ) + def schema_json( # noqa: D102 + cls, *, by_alias: bool = True, ref_template: str = DEFAULT_REF_TEMPLATE, **dumps_kwargs: Any + ) -> str: # pragma: no cover + warnings.warn( + 'The `schema_json` method is deprecated; use `model_json_schema` and json.dumps instead.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + import json + + from .deprecated.json import pydantic_encoder + + return json.dumps( + cls.model_json_schema(by_alias=by_alias, ref_template=ref_template), + default=pydantic_encoder, + **dumps_kwargs, + ) + + @classmethod + @typing_extensions.deprecated('The `validate` method is deprecated; use `model_validate` instead.', category=None) + def validate(cls, value: Any) -> Self: # noqa: D102 + warnings.warn( + 'The `validate` method is deprecated; use `model_validate` instead.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + return cls.model_validate(value) + + @classmethod + @typing_extensions.deprecated( + 'The `update_forward_refs` method is deprecated; use `model_rebuild` instead.', + category=None, + ) + def update_forward_refs(cls, **localns: Any) -> None: # noqa: D102 + warnings.warn( + 'The `update_forward_refs` method is deprecated; use `model_rebuild` instead.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + if localns: # pragma: no cover + raise TypeError('`localns` arguments are not longer accepted.') + cls.model_rebuild(force=True) + + @typing_extensions.deprecated( + 'The private method `_iter` will be removed and should no longer be used.', category=None + ) + def _iter(self, *args: Any, **kwargs: Any) -> Any: + warnings.warn( + 'The private method `_iter` will be removed and should no longer be used.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + from .deprecated import copy_internals + + return copy_internals._iter(self, *args, **kwargs) + + @typing_extensions.deprecated( + 'The private method `_copy_and_set_values` will be removed and should no longer be used.', + category=None, + ) + def _copy_and_set_values(self, *args: Any, **kwargs: Any) -> Any: + warnings.warn( + 'The private method `_copy_and_set_values` will be removed and should no longer be used.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + from .deprecated import copy_internals + + return copy_internals._copy_and_set_values(self, *args, **kwargs) + + @classmethod + @typing_extensions.deprecated( + 'The private method `_get_value` will be removed and should no longer be used.', + category=None, + ) + def _get_value(cls, *args: Any, **kwargs: Any) -> Any: + warnings.warn( + 'The private method `_get_value` will be removed and should no longer be used.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + from .deprecated import copy_internals + + return copy_internals._get_value(cls, *args, **kwargs) + + @typing_extensions.deprecated( + 'The private method `_calculate_keys` will be removed and should no longer be used.', + category=None, + ) + def _calculate_keys(self, *args: Any, **kwargs: Any) -> Any: + warnings.warn( + 'The private method `_calculate_keys` will be removed and should no longer be used.', + category=PydanticDeprecatedSince20, + stacklevel=2, + ) + from .deprecated import copy_internals + + return copy_internals._calculate_keys(self, *args, **kwargs) + + +ModelT = TypeVar('ModelT', bound=BaseModel) + + +@overload +def create_model( + model_name: str, + /, + *, + __config__: ConfigDict | None = None, + __doc__: str | None = None, + __base__: None = None, + __module__: str = __name__, + __validators__: dict[str, Callable[..., Any]] | None = None, + __cls_kwargs__: dict[str, Any] | None = None, + __qualname__: str | None = None, + **field_definitions: Any | tuple[str, Any], +) -> type[BaseModel]: ... + + +@overload +def create_model( + model_name: str, + /, + *, + __config__: ConfigDict | None = None, + __doc__: str | None = None, + __base__: type[ModelT] | tuple[type[ModelT], ...], + __module__: str = __name__, + __validators__: dict[str, Callable[..., Any]] | None = None, + __cls_kwargs__: dict[str, Any] | None = None, + __qualname__: str | None = None, + **field_definitions: Any | tuple[str, Any], +) -> type[ModelT]: ... + + +def create_model( # noqa: C901 + model_name: str, + /, + *, + __config__: ConfigDict | None = None, + __doc__: str | None = None, + __base__: type[ModelT] | tuple[type[ModelT], ...] | None = None, + __module__: str | None = None, + __validators__: dict[str, Callable[..., Any]] | None = None, + __cls_kwargs__: dict[str, Any] | None = None, + __qualname__: str | None = None, + # TODO PEP 747: replace `Any` by the TypeForm: + **field_definitions: Any | tuple[str, Any], +) -> type[ModelT]: + """!!! abstract "Usage Documentation" + [Dynamic Model Creation](../concepts/models.md#dynamic-model-creation) + + Dynamically creates and returns a new Pydantic model, in other words, `create_model` dynamically creates a + subclass of [`BaseModel`][pydantic.BaseModel]. + + Args: + model_name: The name of the newly created model. + __config__: The configuration of the new model. + __doc__: The docstring of the new model. + __base__: The base class or classes for the new model. + __module__: The name of the module that the model belongs to; + if `None`, the value is taken from `sys._getframe(1)` + __validators__: A dictionary of methods that validate fields. The keys are the names of the validation methods to + be added to the model, and the values are the validation methods themselves. You can read more about functional + validators [here](https://docs.pydantic.dev/2.9/concepts/validators/#field-validators). + __cls_kwargs__: A dictionary of keyword arguments for class creation, such as `metaclass`. + __qualname__: The qualified name of the newly created model. + **field_definitions: Field definitions of the new model. Either: + + - a single element, representing the type annotation of the field. + - a two-tuple, the first element being the type and the second element the assigned value + (either a default or the [`Field()`][pydantic.Field] function). + + Returns: + The new [model][pydantic.BaseModel]. + + Raises: + PydanticUserError: If `__base__` and `__config__` are both passed. + """ + if __base__ is None: + __base__ = (cast('type[ModelT]', BaseModel),) + elif not isinstance(__base__, tuple): + __base__ = (__base__,) + + __cls_kwargs__ = __cls_kwargs__ or {} + + fields: dict[str, Any] = {} + annotations: dict[str, Any] = {} + + for f_name, f_def in field_definitions.items(): + if isinstance(f_def, tuple): + if len(f_def) != 2: + raise PydanticUserError( + f'Field definition for {f_name!r} should a single element representing the type or a two-tuple, the first element ' + 'being the type and the second element the assigned value (either a default or the `Field()` function).', + code='create-model-field-definitions', + ) + + annotations[f_name] = f_def[0] + fields[f_name] = f_def[1] + else: + annotations[f_name] = f_def + + if __module__ is None: + f = sys._getframe(1) + __module__ = f.f_globals['__name__'] + + namespace: dict[str, Any] = {'__annotations__': annotations, '__module__': __module__} + if __doc__: + namespace['__doc__'] = __doc__ + if __qualname__ is not None: + namespace['__qualname__'] = __qualname__ + if __validators__: + namespace.update(__validators__) + namespace.update(fields) + if __config__: + namespace['model_config'] = __config__ + resolved_bases = types.resolve_bases(__base__) + meta, ns, kwds = types.prepare_class(model_name, resolved_bases, kwds=__cls_kwargs__) + if resolved_bases is not __base__: + ns['__orig_bases__'] = __base__ + namespace.update(ns) + + return meta( + model_name, + resolved_bases, + namespace, + __pydantic_reset_parent_namespace__=False, + _create_model_module=__module__, + **kwds, + ) + + +__getattr__ = getattr_migration(__name__) diff --git a/.venv/lib/python3.12/site-packages/pydantic/mypy.py b/.venv/lib/python3.12/site-packages/pydantic/mypy.py new file mode 100644 index 0000000000000000000000000000000000000000..6e8228ef97e0943b8772c31edf54122668e331e3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/mypy.py @@ -0,0 +1,1374 @@ +"""This module includes classes and functions designed specifically for use with the mypy plugin.""" + +from __future__ import annotations + +import sys +from collections.abc import Iterator +from configparser import ConfigParser +from typing import Any, Callable + +from mypy.errorcodes import ErrorCode +from mypy.expandtype import expand_type, expand_type_by_instance +from mypy.nodes import ( + ARG_NAMED, + ARG_NAMED_OPT, + ARG_OPT, + ARG_POS, + ARG_STAR2, + INVARIANT, + MDEF, + Argument, + AssignmentStmt, + Block, + CallExpr, + ClassDef, + Context, + Decorator, + DictExpr, + EllipsisExpr, + Expression, + FuncDef, + IfStmt, + JsonDict, + MemberExpr, + NameExpr, + PassStmt, + PlaceholderNode, + RefExpr, + Statement, + StrExpr, + SymbolTableNode, + TempNode, + TypeAlias, + TypeInfo, + Var, +) +from mypy.options import Options +from mypy.plugin import ( + CheckerPluginInterface, + ClassDefContext, + MethodContext, + Plugin, + ReportConfigContext, + SemanticAnalyzerPluginInterface, +) +from mypy.plugins.common import ( + deserialize_and_fixup_type, +) +from mypy.semanal import set_callable_name +from mypy.server.trigger import make_wildcard_trigger +from mypy.state import state +from mypy.type_visitor import TypeTranslator +from mypy.typeops import map_type_from_supertype +from mypy.types import ( + AnyType, + CallableType, + Instance, + NoneType, + Type, + TypeOfAny, + TypeType, + TypeVarType, + UnionType, + get_proper_type, +) +from mypy.typevars import fill_typevars +from mypy.util import get_unique_redefinition_name +from mypy.version import __version__ as mypy_version + +from pydantic._internal import _fields +from pydantic.version import parse_mypy_version + +CONFIGFILE_KEY = 'pydantic-mypy' +METADATA_KEY = 'pydantic-mypy-metadata' +BASEMODEL_FULLNAME = 'pydantic.main.BaseModel' +BASESETTINGS_FULLNAME = 'pydantic_settings.main.BaseSettings' +ROOT_MODEL_FULLNAME = 'pydantic.root_model.RootModel' +MODEL_METACLASS_FULLNAME = 'pydantic._internal._model_construction.ModelMetaclass' +FIELD_FULLNAME = 'pydantic.fields.Field' +DATACLASS_FULLNAME = 'pydantic.dataclasses.dataclass' +MODEL_VALIDATOR_FULLNAME = 'pydantic.functional_validators.model_validator' +DECORATOR_FULLNAMES = { + 'pydantic.functional_validators.field_validator', + 'pydantic.functional_validators.model_validator', + 'pydantic.functional_serializers.serializer', + 'pydantic.functional_serializers.model_serializer', + 'pydantic.deprecated.class_validators.validator', + 'pydantic.deprecated.class_validators.root_validator', +} +IMPLICIT_CLASSMETHOD_DECORATOR_FULLNAMES = DECORATOR_FULLNAMES - {'pydantic.functional_serializers.model_serializer'} + + +MYPY_VERSION_TUPLE = parse_mypy_version(mypy_version) +BUILTINS_NAME = 'builtins' + +# Increment version if plugin changes and mypy caches should be invalidated +__version__ = 2 + + +def plugin(version: str) -> type[Plugin]: + """`version` is the mypy version string. + + We might want to use this to print a warning if the mypy version being used is + newer, or especially older, than we expect (or need). + + Args: + version: The mypy version string. + + Return: + The Pydantic mypy plugin type. + """ + return PydanticPlugin + + +class PydanticPlugin(Plugin): + """The Pydantic mypy plugin.""" + + def __init__(self, options: Options) -> None: + self.plugin_config = PydanticPluginConfig(options) + self._plugin_data = self.plugin_config.to_data() + super().__init__(options) + + def get_base_class_hook(self, fullname: str) -> Callable[[ClassDefContext], None] | None: + """Update Pydantic model class.""" + sym = self.lookup_fully_qualified(fullname) + if sym and isinstance(sym.node, TypeInfo): # pragma: no branch + # No branching may occur if the mypy cache has not been cleared + if sym.node.has_base(BASEMODEL_FULLNAME): + return self._pydantic_model_class_maker_callback + return None + + def get_metaclass_hook(self, fullname: str) -> Callable[[ClassDefContext], None] | None: + """Update Pydantic `ModelMetaclass` definition.""" + if fullname == MODEL_METACLASS_FULLNAME: + return self._pydantic_model_metaclass_marker_callback + return None + + def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | None: + """Adjust return type of `from_orm` method call.""" + if fullname.endswith('.from_orm'): + return from_attributes_callback + return None + + def report_config_data(self, ctx: ReportConfigContext) -> dict[str, Any]: + """Return all plugin config data. + + Used by mypy to determine if cache needs to be discarded. + """ + return self._plugin_data + + def _pydantic_model_class_maker_callback(self, ctx: ClassDefContext) -> None: + transformer = PydanticModelTransformer(ctx.cls, ctx.reason, ctx.api, self.plugin_config) + transformer.transform() + + def _pydantic_model_metaclass_marker_callback(self, ctx: ClassDefContext) -> None: + """Reset dataclass_transform_spec attribute of ModelMetaclass. + + Let the plugin handle it. This behavior can be disabled + if 'debug_dataclass_transform' is set to True', for testing purposes. + """ + if self.plugin_config.debug_dataclass_transform: + return + info_metaclass = ctx.cls.info.declared_metaclass + assert info_metaclass, "callback not passed from 'get_metaclass_hook'" + if getattr(info_metaclass.type, 'dataclass_transform_spec', None): + info_metaclass.type.dataclass_transform_spec = None + + +class PydanticPluginConfig: + """A Pydantic mypy plugin config holder. + + Attributes: + init_forbid_extra: Whether to add a `**kwargs` at the end of the generated `__init__` signature. + init_typed: Whether to annotate fields in the generated `__init__`. + warn_required_dynamic_aliases: Whether to raise required dynamic aliases error. + debug_dataclass_transform: Whether to not reset `dataclass_transform_spec` attribute + of `ModelMetaclass` for testing purposes. + """ + + __slots__ = ( + 'init_forbid_extra', + 'init_typed', + 'warn_required_dynamic_aliases', + 'debug_dataclass_transform', + ) + init_forbid_extra: bool + init_typed: bool + warn_required_dynamic_aliases: bool + debug_dataclass_transform: bool # undocumented + + def __init__(self, options: Options) -> None: + if options.config_file is None: # pragma: no cover + return + + toml_config = parse_toml(options.config_file) + if toml_config is not None: + config = toml_config.get('tool', {}).get('pydantic-mypy', {}) + for key in self.__slots__: + setting = config.get(key, False) + if not isinstance(setting, bool): + raise ValueError(f'Configuration value must be a boolean for key: {key}') + setattr(self, key, setting) + else: + plugin_config = ConfigParser() + plugin_config.read(options.config_file) + for key in self.__slots__: + setting = plugin_config.getboolean(CONFIGFILE_KEY, key, fallback=False) + setattr(self, key, setting) + + def to_data(self) -> dict[str, Any]: + """Returns a dict of config names to their values.""" + return {key: getattr(self, key) for key in self.__slots__} + + +def from_attributes_callback(ctx: MethodContext) -> Type: + """Raise an error if from_attributes is not enabled.""" + model_type: Instance + ctx_type = ctx.type + if isinstance(ctx_type, TypeType): + ctx_type = ctx_type.item + if isinstance(ctx_type, CallableType) and isinstance(ctx_type.ret_type, Instance): + model_type = ctx_type.ret_type # called on the class + elif isinstance(ctx_type, Instance): + model_type = ctx_type # called on an instance (unusual, but still valid) + else: # pragma: no cover + detail = f'ctx.type: {ctx_type} (of type {ctx_type.__class__.__name__})' + error_unexpected_behavior(detail, ctx.api, ctx.context) + return ctx.default_return_type + pydantic_metadata = model_type.type.metadata.get(METADATA_KEY) + if pydantic_metadata is None: + return ctx.default_return_type + if not model_type.type.has_base(BASEMODEL_FULLNAME): + # not a Pydantic v2 model + return ctx.default_return_type + from_attributes = pydantic_metadata.get('config', {}).get('from_attributes') + if from_attributes is not True: + error_from_attributes(model_type.type.name, ctx.api, ctx.context) + return ctx.default_return_type + + +class PydanticModelField: + """Based on mypy.plugins.dataclasses.DataclassAttribute.""" + + def __init__( + self, + name: str, + alias: str | None, + is_frozen: bool, + has_dynamic_alias: bool, + has_default: bool, + strict: bool | None, + line: int, + column: int, + type: Type | None, + info: TypeInfo, + ): + self.name = name + self.alias = alias + self.is_frozen = is_frozen + self.has_dynamic_alias = has_dynamic_alias + self.has_default = has_default + self.strict = strict + self.line = line + self.column = column + self.type = type + self.info = info + + def to_argument( + self, + current_info: TypeInfo, + typed: bool, + model_strict: bool, + force_optional: bool, + use_alias: bool, + api: SemanticAnalyzerPluginInterface, + force_typevars_invariant: bool, + is_root_model_root: bool, + ) -> Argument: + """Based on mypy.plugins.dataclasses.DataclassAttribute.to_argument.""" + variable = self.to_var(current_info, api, use_alias, force_typevars_invariant) + + strict = model_strict if self.strict is None else self.strict + if typed or strict: + type_annotation = self.expand_type(current_info, api, include_root_type=True) + else: + type_annotation = AnyType(TypeOfAny.explicit) + + return Argument( + variable=variable, + type_annotation=type_annotation, + initializer=None, + kind=ARG_OPT + if is_root_model_root + else (ARG_NAMED_OPT if force_optional or self.has_default else ARG_NAMED), + ) + + def expand_type( + self, + current_info: TypeInfo, + api: SemanticAnalyzerPluginInterface, + force_typevars_invariant: bool = False, + include_root_type: bool = False, + ) -> Type | None: + """Based on mypy.plugins.dataclasses.DataclassAttribute.expand_type.""" + if force_typevars_invariant: + # In some cases, mypy will emit an error "Cannot use a covariant type variable as a parameter" + # To prevent that, we add an option to replace typevars with invariant ones while building certain + # method signatures (in particular, `__init__`). There may be a better way to do this, if this causes + # us problems in the future, we should look into why the dataclasses plugin doesn't have this issue. + if isinstance(self.type, TypeVarType): + modified_type = self.type.copy_modified() + modified_type.variance = INVARIANT + self.type = modified_type + + if self.type is not None and self.info.self_type is not None: + # In general, it is not safe to call `expand_type()` during semantic analysis, + # however this plugin is called very late, so all types should be fully ready. + # Also, it is tricky to avoid eager expansion of Self types here (e.g. because + # we serialize attributes). + with state.strict_optional_set(api.options.strict_optional): + filled_with_typevars = fill_typevars(current_info) + # Cannot be TupleType as current_info represents a Pydantic model: + assert isinstance(filled_with_typevars, Instance) + if force_typevars_invariant: + for arg in filled_with_typevars.args: + if isinstance(arg, TypeVarType): + arg.variance = INVARIANT + + expanded_type = expand_type(self.type, {self.info.self_type.id: filled_with_typevars}) + if include_root_type and isinstance(expanded_type, Instance) and is_root_model(expanded_type.type): + # When a root model is used as a field, Pydantic allows both an instance of the root model + # as well as instances of the `root` field type: + root_type = expanded_type.type['root'].type + if root_type is None: + # Happens if the hint for 'root' has unsolved forward references + return expanded_type + expanded_root_type = expand_type_by_instance(root_type, expanded_type) + expanded_type = UnionType([expanded_type, expanded_root_type]) + return expanded_type + return self.type + + def to_var( + self, + current_info: TypeInfo, + api: SemanticAnalyzerPluginInterface, + use_alias: bool, + force_typevars_invariant: bool = False, + ) -> Var: + """Based on mypy.plugins.dataclasses.DataclassAttribute.to_var.""" + if use_alias and self.alias is not None: + name = self.alias + else: + name = self.name + + return Var(name, self.expand_type(current_info, api, force_typevars_invariant)) + + def serialize(self) -> JsonDict: + """Based on mypy.plugins.dataclasses.DataclassAttribute.serialize.""" + assert self.type + return { + 'name': self.name, + 'alias': self.alias, + 'is_frozen': self.is_frozen, + 'has_dynamic_alias': self.has_dynamic_alias, + 'has_default': self.has_default, + 'strict': self.strict, + 'line': self.line, + 'column': self.column, + 'type': self.type.serialize(), + } + + @classmethod + def deserialize(cls, info: TypeInfo, data: JsonDict, api: SemanticAnalyzerPluginInterface) -> PydanticModelField: + """Based on mypy.plugins.dataclasses.DataclassAttribute.deserialize.""" + data = data.copy() + typ = deserialize_and_fixup_type(data.pop('type'), api) + return cls(type=typ, info=info, **data) + + def expand_typevar_from_subtype(self, sub_type: TypeInfo, api: SemanticAnalyzerPluginInterface) -> None: + """Expands type vars in the context of a subtype when an attribute is inherited + from a generic super type. + """ + if self.type is not None: + with state.strict_optional_set(api.options.strict_optional): + self.type = map_type_from_supertype(self.type, sub_type, self.info) + + +class PydanticModelClassVar: + """Based on mypy.plugins.dataclasses.DataclassAttribute. + + ClassVars are ignored by subclasses. + + Attributes: + name: the ClassVar name + """ + + def __init__(self, name): + self.name = name + + @classmethod + def deserialize(cls, data: JsonDict) -> PydanticModelClassVar: + """Based on mypy.plugins.dataclasses.DataclassAttribute.deserialize.""" + data = data.copy() + return cls(**data) + + def serialize(self) -> JsonDict: + """Based on mypy.plugins.dataclasses.DataclassAttribute.serialize.""" + return { + 'name': self.name, + } + + +class PydanticModelTransformer: + """Transform the BaseModel subclass according to the plugin settings. + + Attributes: + tracked_config_fields: A set of field configs that the plugin has to track their value. + """ + + tracked_config_fields: set[str] = { + 'extra', + 'frozen', + 'from_attributes', + 'populate_by_name', + 'validate_by_alias', + 'validate_by_name', + 'alias_generator', + 'strict', + } + + def __init__( + self, + cls: ClassDef, + reason: Expression | Statement, + api: SemanticAnalyzerPluginInterface, + plugin_config: PydanticPluginConfig, + ) -> None: + self._cls = cls + self._reason = reason + self._api = api + + self.plugin_config = plugin_config + + def transform(self) -> bool: + """Configures the BaseModel subclass according to the plugin settings. + + In particular: + + * determines the model config and fields, + * adds a fields-aware signature for the initializer and construct methods + * freezes the class if frozen = True + * stores the fields, config, and if the class is settings in the mypy metadata for access by subclasses + """ + info = self._cls.info + is_a_root_model = is_root_model(info) + config = self.collect_config() + fields, class_vars = self.collect_fields_and_class_vars(config, is_a_root_model) + if fields is None or class_vars is None: + # Some definitions are not ready. We need another pass. + return False + for field in fields: + if field.type is None: + return False + + is_settings = info.has_base(BASESETTINGS_FULLNAME) + self.add_initializer(fields, config, is_settings, is_a_root_model) + self.add_model_construct_method(fields, config, is_settings, is_a_root_model) + self.set_frozen(fields, self._api, frozen=config.frozen is True) + + self.adjust_decorator_signatures() + + info.metadata[METADATA_KEY] = { + 'fields': {field.name: field.serialize() for field in fields}, + 'class_vars': {class_var.name: class_var.serialize() for class_var in class_vars}, + 'config': config.get_values_dict(), + } + + return True + + def adjust_decorator_signatures(self) -> None: + """When we decorate a function `f` with `pydantic.validator(...)`, `pydantic.field_validator` + or `pydantic.serializer(...)`, mypy sees `f` as a regular method taking a `self` instance, + even though pydantic internally wraps `f` with `classmethod` if necessary. + + Teach mypy this by marking any function whose outermost decorator is a `validator()`, + `field_validator()` or `serializer()` call as a `classmethod`. + """ + for sym in self._cls.info.names.values(): + if isinstance(sym.node, Decorator): + first_dec = sym.node.original_decorators[0] + if ( + isinstance(first_dec, CallExpr) + and isinstance(first_dec.callee, NameExpr) + and first_dec.callee.fullname in IMPLICIT_CLASSMETHOD_DECORATOR_FULLNAMES + # @model_validator(mode="after") is an exception, it expects a regular method + and not ( + first_dec.callee.fullname == MODEL_VALIDATOR_FULLNAME + and any( + first_dec.arg_names[i] == 'mode' and isinstance(arg, StrExpr) and arg.value == 'after' + for i, arg in enumerate(first_dec.args) + ) + ) + ): + # TODO: Only do this if the first argument of the decorated function is `cls` + sym.node.func.is_class = True + + def collect_config(self) -> ModelConfigData: # noqa: C901 (ignore complexity) + """Collects the values of the config attributes that are used by the plugin, accounting for parent classes.""" + cls = self._cls + config = ModelConfigData() + + has_config_kwargs = False + has_config_from_namespace = False + + # Handle `class MyModel(BaseModel, =, ...):` + for name, expr in cls.keywords.items(): + config_data = self.get_config_update(name, expr) + if config_data: + has_config_kwargs = True + config.update(config_data) + + # Handle `model_config` + stmt: Statement | None = None + for stmt in cls.defs.body: + if not isinstance(stmt, (AssignmentStmt, ClassDef)): + continue + + if isinstance(stmt, AssignmentStmt): + lhs = stmt.lvalues[0] + if not isinstance(lhs, NameExpr) or lhs.name != 'model_config': + continue + + if isinstance(stmt.rvalue, CallExpr): # calls to `dict` or `ConfigDict` + for arg_name, arg in zip(stmt.rvalue.arg_names, stmt.rvalue.args): + if arg_name is None: + continue + config.update(self.get_config_update(arg_name, arg, lax_extra=True)) + elif isinstance(stmt.rvalue, DictExpr): # dict literals + for key_expr, value_expr in stmt.rvalue.items: + if not isinstance(key_expr, StrExpr): + continue + config.update(self.get_config_update(key_expr.value, value_expr)) + + elif isinstance(stmt, ClassDef): + if stmt.name != 'Config': # 'deprecated' Config-class + continue + for substmt in stmt.defs.body: + if not isinstance(substmt, AssignmentStmt): + continue + lhs = substmt.lvalues[0] + if not isinstance(lhs, NameExpr): + continue + config.update(self.get_config_update(lhs.name, substmt.rvalue)) + + if has_config_kwargs: + self._api.fail( + 'Specifying config in two places is ambiguous, use either Config attribute or class kwargs', + cls, + ) + break + + has_config_from_namespace = True + + if has_config_kwargs or has_config_from_namespace: + if ( + stmt + and config.has_alias_generator + and not (config.validate_by_name or config.populate_by_name) + and self.plugin_config.warn_required_dynamic_aliases + ): + error_required_dynamic_aliases(self._api, stmt) + + for info in cls.info.mro[1:]: # 0 is the current class + if METADATA_KEY not in info.metadata: + continue + + # Each class depends on the set of fields in its ancestors + self._api.add_plugin_dependency(make_wildcard_trigger(info.fullname)) + for name, value in info.metadata[METADATA_KEY]['config'].items(): + config.setdefault(name, value) + return config + + def collect_fields_and_class_vars( + self, model_config: ModelConfigData, is_root_model: bool + ) -> tuple[list[PydanticModelField] | None, list[PydanticModelClassVar] | None]: + """Collects the fields for the model, accounting for parent classes.""" + cls = self._cls + + # First, collect fields and ClassVars belonging to any class in the MRO, ignoring duplicates. + # + # We iterate through the MRO in reverse because attrs defined in the parent must appear + # earlier in the attributes list than attrs defined in the child. See: + # https://docs.python.org/3/library/dataclasses.html#inheritance + # + # However, we also want fields defined in the subtype to override ones defined + # in the parent. We can implement this via a dict without disrupting the attr order + # because dicts preserve insertion order in Python 3.7+. + found_fields: dict[str, PydanticModelField] = {} + found_class_vars: dict[str, PydanticModelClassVar] = {} + for info in reversed(cls.info.mro[1:-1]): # 0 is the current class, -2 is BaseModel, -1 is object + # if BASEMODEL_METADATA_TAG_KEY in info.metadata and BASEMODEL_METADATA_KEY not in info.metadata: + # # We haven't processed the base class yet. Need another pass. + # return None, None + if METADATA_KEY not in info.metadata: + continue + + # Each class depends on the set of attributes in its dataclass ancestors. + self._api.add_plugin_dependency(make_wildcard_trigger(info.fullname)) + + for name, data in info.metadata[METADATA_KEY]['fields'].items(): + field = PydanticModelField.deserialize(info, data, self._api) + # (The following comment comes directly from the dataclasses plugin) + # TODO: We shouldn't be performing type operations during the main + # semantic analysis pass, since some TypeInfo attributes might + # still be in flux. This should be performed in a later phase. + field.expand_typevar_from_subtype(cls.info, self._api) + found_fields[name] = field + + sym_node = cls.info.names.get(name) + if sym_node and sym_node.node and not isinstance(sym_node.node, Var): + self._api.fail( + 'BaseModel field may only be overridden by another field', + sym_node.node, + ) + # Collect ClassVars + for name, data in info.metadata[METADATA_KEY]['class_vars'].items(): + found_class_vars[name] = PydanticModelClassVar.deserialize(data) + + # Second, collect fields and ClassVars belonging to the current class. + current_field_names: set[str] = set() + current_class_vars_names: set[str] = set() + for stmt in self._get_assignment_statements_from_block(cls.defs): + maybe_field = self.collect_field_or_class_var_from_stmt(stmt, model_config, found_class_vars) + if maybe_field is None: + continue + + lhs = stmt.lvalues[0] + assert isinstance(lhs, NameExpr) # collect_field_or_class_var_from_stmt guarantees this + if isinstance(maybe_field, PydanticModelField): + if is_root_model and lhs.name != 'root': + error_extra_fields_on_root_model(self._api, stmt) + else: + current_field_names.add(lhs.name) + found_fields[lhs.name] = maybe_field + elif isinstance(maybe_field, PydanticModelClassVar): + current_class_vars_names.add(lhs.name) + found_class_vars[lhs.name] = maybe_field + + return list(found_fields.values()), list(found_class_vars.values()) + + def _get_assignment_statements_from_if_statement(self, stmt: IfStmt) -> Iterator[AssignmentStmt]: + for body in stmt.body: + if not body.is_unreachable: + yield from self._get_assignment_statements_from_block(body) + if stmt.else_body is not None and not stmt.else_body.is_unreachable: + yield from self._get_assignment_statements_from_block(stmt.else_body) + + def _get_assignment_statements_from_block(self, block: Block) -> Iterator[AssignmentStmt]: + for stmt in block.body: + if isinstance(stmt, AssignmentStmt): + yield stmt + elif isinstance(stmt, IfStmt): + yield from self._get_assignment_statements_from_if_statement(stmt) + + def collect_field_or_class_var_from_stmt( # noqa C901 + self, stmt: AssignmentStmt, model_config: ModelConfigData, class_vars: dict[str, PydanticModelClassVar] + ) -> PydanticModelField | PydanticModelClassVar | None: + """Get pydantic model field from statement. + + Args: + stmt: The statement. + model_config: Configuration settings for the model. + class_vars: ClassVars already known to be defined on the model. + + Returns: + A pydantic model field if it could find the field in statement. Otherwise, `None`. + """ + cls = self._cls + + lhs = stmt.lvalues[0] + if not isinstance(lhs, NameExpr) or not _fields.is_valid_field_name(lhs.name) or lhs.name == 'model_config': + return None + + if not stmt.new_syntax: + if ( + isinstance(stmt.rvalue, CallExpr) + and isinstance(stmt.rvalue.callee, CallExpr) + and isinstance(stmt.rvalue.callee.callee, NameExpr) + and stmt.rvalue.callee.callee.fullname in DECORATOR_FULLNAMES + ): + # This is a (possibly-reused) validator or serializer, not a field + # In particular, it looks something like: my_validator = validator('my_field')(f) + # Eventually, we may want to attempt to respect model_config['ignored_types'] + return None + + if lhs.name in class_vars: + # Class vars are not fields and are not required to be annotated + return None + + # The assignment does not have an annotation, and it's not anything else we recognize + error_untyped_fields(self._api, stmt) + return None + + lhs = stmt.lvalues[0] + if not isinstance(lhs, NameExpr): + return None + + if not _fields.is_valid_field_name(lhs.name) or lhs.name == 'model_config': + return None + + sym = cls.info.names.get(lhs.name) + if sym is None: # pragma: no cover + # This is likely due to a star import (see the dataclasses plugin for a more detailed explanation) + # This is the same logic used in the dataclasses plugin + return None + + node = sym.node + if isinstance(node, PlaceholderNode): # pragma: no cover + # See the PlaceholderNode docstring for more detail about how this can occur + # Basically, it is an edge case when dealing with complex import logic + + # The dataclasses plugin now asserts this cannot happen, but I'd rather not error if it does.. + return None + + if isinstance(node, TypeAlias): + self._api.fail( + 'Type aliases inside BaseModel definitions are not supported at runtime', + node, + ) + # Skip processing this node. This doesn't match the runtime behaviour, + # but the only alternative would be to modify the SymbolTable, + # and it's a little hairy to do that in a plugin. + return None + + if not isinstance(node, Var): # pragma: no cover + # Don't know if this edge case still happens with the `is_valid_field` check above + # but better safe than sorry + + # The dataclasses plugin now asserts this cannot happen, but I'd rather not error if it does.. + return None + + # x: ClassVar[int] is not a field + if node.is_classvar: + return PydanticModelClassVar(lhs.name) + + # x: InitVar[int] is not supported in BaseModel + node_type = get_proper_type(node.type) + if isinstance(node_type, Instance) and node_type.type.fullname == 'dataclasses.InitVar': + self._api.fail( + 'InitVar is not supported in BaseModel', + node, + ) + + has_default = self.get_has_default(stmt) + strict = self.get_strict(stmt) + + if sym.type is None and node.is_final and node.is_inferred: + # This follows the logic from the dataclasses plugin. The following comment is taken verbatim: + # + # This is a special case, assignment like x: Final = 42 is classified + # annotated above, but mypy strips the `Final` turning it into x = 42. + # We do not support inferred types in dataclasses, so we can try inferring + # type for simple literals, and otherwise require an explicit type + # argument for Final[...]. + typ = self._api.analyze_simple_literal_type(stmt.rvalue, is_final=True) + if typ: + node.type = typ + else: + self._api.fail( + 'Need type argument for Final[...] with non-literal default in BaseModel', + stmt, + ) + node.type = AnyType(TypeOfAny.from_error) + + if node.is_final and has_default: + # TODO this path should be removed (see https://github.com/pydantic/pydantic/issues/11119) + return PydanticModelClassVar(lhs.name) + + alias, has_dynamic_alias = self.get_alias_info(stmt) + if ( + has_dynamic_alias + and not (model_config.validate_by_name or model_config.populate_by_name) + and self.plugin_config.warn_required_dynamic_aliases + ): + error_required_dynamic_aliases(self._api, stmt) + is_frozen = self.is_field_frozen(stmt) + + init_type = self._infer_dataclass_attr_init_type(sym, lhs.name, stmt) + return PydanticModelField( + name=lhs.name, + has_dynamic_alias=has_dynamic_alias, + has_default=has_default, + strict=strict, + alias=alias, + is_frozen=is_frozen, + line=stmt.line, + column=stmt.column, + type=init_type, + info=cls.info, + ) + + def _infer_dataclass_attr_init_type(self, sym: SymbolTableNode, name: str, context: Context) -> Type | None: + """Infer __init__ argument type for an attribute. + + In particular, possibly use the signature of __set__. + """ + default = sym.type + if sym.implicit: + return default + t = get_proper_type(sym.type) + + # Perform a simple-minded inference from the signature of __set__, if present. + # We can't use mypy.checkmember here, since this plugin runs before type checking. + # We only support some basic scanerios here, which is hopefully sufficient for + # the vast majority of use cases. + if not isinstance(t, Instance): + return default + setter = t.type.get('__set__') + if setter: + if isinstance(setter.node, FuncDef): + super_info = t.type.get_containing_type_info('__set__') + assert super_info + if setter.type: + setter_type = get_proper_type(map_type_from_supertype(setter.type, t.type, super_info)) + else: + return AnyType(TypeOfAny.unannotated) + if isinstance(setter_type, CallableType) and setter_type.arg_kinds == [ + ARG_POS, + ARG_POS, + ARG_POS, + ]: + return expand_type_by_instance(setter_type.arg_types[2], t) + else: + self._api.fail(f'Unsupported signature for "__set__" in "{t.type.name}"', context) + else: + self._api.fail(f'Unsupported "__set__" in "{t.type.name}"', context) + + return default + + def add_initializer( + self, fields: list[PydanticModelField], config: ModelConfigData, is_settings: bool, is_root_model: bool + ) -> None: + """Adds a fields-aware `__init__` method to the class. + + The added `__init__` will be annotated with types vs. all `Any` depending on the plugin settings. + """ + if '__init__' in self._cls.info.names and not self._cls.info.names['__init__'].plugin_generated: + return # Don't generate an __init__ if one already exists + + typed = self.plugin_config.init_typed + model_strict = bool(config.strict) + use_alias = not (config.validate_by_name or config.populate_by_name) and config.validate_by_alias is not False + requires_dynamic_aliases = bool(config.has_alias_generator and not config.validate_by_name) + args = self.get_field_arguments( + fields, + typed=typed, + model_strict=model_strict, + requires_dynamic_aliases=requires_dynamic_aliases, + use_alias=use_alias, + is_settings=is_settings, + is_root_model=is_root_model, + force_typevars_invariant=True, + ) + + if is_settings: + base_settings_node = self._api.lookup_fully_qualified(BASESETTINGS_FULLNAME).node + assert isinstance(base_settings_node, TypeInfo) + if '__init__' in base_settings_node.names: + base_settings_init_node = base_settings_node.names['__init__'].node + assert isinstance(base_settings_init_node, FuncDef) + if base_settings_init_node is not None and base_settings_init_node.type is not None: + func_type = base_settings_init_node.type + assert isinstance(func_type, CallableType) + for arg_idx, arg_name in enumerate(func_type.arg_names): + if arg_name is None or arg_name.startswith('__') or not arg_name.startswith('_'): + continue + analyzed_variable_type = self._api.anal_type(func_type.arg_types[arg_idx]) + if analyzed_variable_type is not None and arg_name == '_cli_settings_source': + # _cli_settings_source is defined as CliSettingsSource[Any], and as such + # the Any causes issues with --disallow-any-explicit. As a workaround, change + # the Any type (as if CliSettingsSource was left unparameterized): + analyzed_variable_type = analyzed_variable_type.accept( + ChangeExplicitTypeOfAny(TypeOfAny.from_omitted_generics) + ) + variable = Var(arg_name, analyzed_variable_type) + args.append(Argument(variable, analyzed_variable_type, None, ARG_OPT)) + + if not self.should_init_forbid_extra(fields, config): + var = Var('kwargs') + args.append(Argument(var, AnyType(TypeOfAny.explicit), None, ARG_STAR2)) + + add_method(self._api, self._cls, '__init__', args=args, return_type=NoneType()) + + def add_model_construct_method( + self, + fields: list[PydanticModelField], + config: ModelConfigData, + is_settings: bool, + is_root_model: bool, + ) -> None: + """Adds a fully typed `model_construct` classmethod to the class. + + Similar to the fields-aware __init__ method, but always uses the field names (not aliases), + and does not treat settings fields as optional. + """ + set_str = self._api.named_type(f'{BUILTINS_NAME}.set', [self._api.named_type(f'{BUILTINS_NAME}.str')]) + optional_set_str = UnionType([set_str, NoneType()]) + fields_set_argument = Argument(Var('_fields_set', optional_set_str), optional_set_str, None, ARG_OPT) + with state.strict_optional_set(self._api.options.strict_optional): + args = self.get_field_arguments( + fields, + typed=True, + model_strict=bool(config.strict), + requires_dynamic_aliases=False, + use_alias=False, + is_settings=is_settings, + is_root_model=is_root_model, + ) + if not self.should_init_forbid_extra(fields, config): + var = Var('kwargs') + args.append(Argument(var, AnyType(TypeOfAny.explicit), None, ARG_STAR2)) + + args = args + [fields_set_argument] if is_root_model else [fields_set_argument] + args + + add_method( + self._api, + self._cls, + 'model_construct', + args=args, + return_type=fill_typevars(self._cls.info), + is_classmethod=True, + ) + + def set_frozen(self, fields: list[PydanticModelField], api: SemanticAnalyzerPluginInterface, frozen: bool) -> None: + """Marks all fields as properties so that attempts to set them trigger mypy errors. + + This is the same approach used by the attrs and dataclasses plugins. + """ + info = self._cls.info + for field in fields: + sym_node = info.names.get(field.name) + if sym_node is not None: + var = sym_node.node + if isinstance(var, Var): + var.is_property = frozen or field.is_frozen + elif isinstance(var, PlaceholderNode) and not self._api.final_iteration: + # See https://github.com/pydantic/pydantic/issues/5191 to hit this branch for test coverage + self._api.defer() + # `var` can also be a FuncDef or Decorator node (e.g. when overriding a field with a function or property). + # In that case, we don't want to do anything. Mypy will already raise an error that a field was not properly + # overridden. + else: + var = field.to_var(info, api, use_alias=False) + var.info = info + var.is_property = frozen + var._fullname = info.fullname + '.' + var.name + info.names[var.name] = SymbolTableNode(MDEF, var) + + def get_config_update(self, name: str, arg: Expression, lax_extra: bool = False) -> ModelConfigData | None: + """Determines the config update due to a single kwarg in the ConfigDict definition. + + Warns if a tracked config attribute is set to a value the plugin doesn't know how to interpret (e.g., an int) + """ + if name not in self.tracked_config_fields: + return None + if name == 'extra': + if isinstance(arg, StrExpr): + forbid_extra = arg.value == 'forbid' + elif isinstance(arg, MemberExpr): + forbid_extra = arg.name == 'forbid' + else: + if not lax_extra: + # Only emit an error for other types of `arg` (e.g., `NameExpr`, `ConditionalExpr`, etc.) when + # reading from a config class, etc. If a ConfigDict is used, then we don't want to emit an error + # because you'll get type checking from the ConfigDict itself. + # + # It would be nice if we could introspect the types better otherwise, but I don't know what the API + # is to evaluate an expr into its type and then check if that type is compatible with the expected + # type. Note that you can still get proper type checking via: `model_config = ConfigDict(...)`, just + # if you don't use an explicit string, the plugin won't be able to infer whether extra is forbidden. + error_invalid_config_value(name, self._api, arg) + return None + return ModelConfigData(forbid_extra=forbid_extra) + if name == 'alias_generator': + has_alias_generator = True + if isinstance(arg, NameExpr) and arg.fullname == 'builtins.None': + has_alias_generator = False + return ModelConfigData(has_alias_generator=has_alias_generator) + if isinstance(arg, NameExpr) and arg.fullname in ('builtins.True', 'builtins.False'): + return ModelConfigData(**{name: arg.fullname == 'builtins.True'}) + error_invalid_config_value(name, self._api, arg) + return None + + @staticmethod + def get_has_default(stmt: AssignmentStmt) -> bool: + """Returns a boolean indicating whether the field defined in `stmt` is a required field.""" + expr = stmt.rvalue + if isinstance(expr, TempNode): + # TempNode means annotation-only, so has no default + return False + if isinstance(expr, CallExpr) and isinstance(expr.callee, RefExpr) and expr.callee.fullname == FIELD_FULLNAME: + # The "default value" is a call to `Field`; at this point, the field has a default if and only if: + # * there is a positional argument that is not `...` + # * there is a keyword argument named "default" that is not `...` + # * there is a "default_factory" that is not `None` + for arg, name in zip(expr.args, expr.arg_names): + # If name is None, then this arg is the default because it is the only positional argument. + if name is None or name == 'default': + return arg.__class__ is not EllipsisExpr + if name == 'default_factory': + return not (isinstance(arg, NameExpr) and arg.fullname == 'builtins.None') + return False + # Has no default if the "default value" is Ellipsis (i.e., `field_name: Annotation = ...`) + return not isinstance(expr, EllipsisExpr) + + @staticmethod + def get_strict(stmt: AssignmentStmt) -> bool | None: + """Returns a the `strict` value of a field if defined, otherwise `None`.""" + expr = stmt.rvalue + if isinstance(expr, CallExpr) and isinstance(expr.callee, RefExpr) and expr.callee.fullname == FIELD_FULLNAME: + for arg, name in zip(expr.args, expr.arg_names): + if name != 'strict': + continue + if isinstance(arg, NameExpr): + if arg.fullname == 'builtins.True': + return True + elif arg.fullname == 'builtins.False': + return False + return None + return None + + @staticmethod + def get_alias_info(stmt: AssignmentStmt) -> tuple[str | None, bool]: + """Returns a pair (alias, has_dynamic_alias), extracted from the declaration of the field defined in `stmt`. + + `has_dynamic_alias` is True if and only if an alias is provided, but not as a string literal. + If `has_dynamic_alias` is True, `alias` will be None. + """ + expr = stmt.rvalue + if isinstance(expr, TempNode): + # TempNode means annotation-only + return None, False + + if not ( + isinstance(expr, CallExpr) and isinstance(expr.callee, RefExpr) and expr.callee.fullname == FIELD_FULLNAME + ): + # Assigned value is not a call to pydantic.fields.Field + return None, False + + if 'validation_alias' in expr.arg_names: + arg = expr.args[expr.arg_names.index('validation_alias')] + elif 'alias' in expr.arg_names: + arg = expr.args[expr.arg_names.index('alias')] + else: + return None, False + + if isinstance(arg, StrExpr): + return arg.value, False + else: + return None, True + + @staticmethod + def is_field_frozen(stmt: AssignmentStmt) -> bool: + """Returns whether the field is frozen, extracted from the declaration of the field defined in `stmt`. + + Note that this is only whether the field was declared to be frozen in a ` = Field(frozen=True)` + sense; this does not determine whether the field is frozen because the entire model is frozen; that is + handled separately. + """ + expr = stmt.rvalue + if isinstance(expr, TempNode): + # TempNode means annotation-only + return False + + if not ( + isinstance(expr, CallExpr) and isinstance(expr.callee, RefExpr) and expr.callee.fullname == FIELD_FULLNAME + ): + # Assigned value is not a call to pydantic.fields.Field + return False + + for i, arg_name in enumerate(expr.arg_names): + if arg_name == 'frozen': + arg = expr.args[i] + return isinstance(arg, NameExpr) and arg.fullname == 'builtins.True' + return False + + def get_field_arguments( + self, + fields: list[PydanticModelField], + typed: bool, + model_strict: bool, + use_alias: bool, + requires_dynamic_aliases: bool, + is_settings: bool, + is_root_model: bool, + force_typevars_invariant: bool = False, + ) -> list[Argument]: + """Helper function used during the construction of the `__init__` and `model_construct` method signatures. + + Returns a list of mypy Argument instances for use in the generated signatures. + """ + info = self._cls.info + arguments = [ + field.to_argument( + info, + typed=typed, + model_strict=model_strict, + force_optional=requires_dynamic_aliases or is_settings, + use_alias=use_alias, + api=self._api, + force_typevars_invariant=force_typevars_invariant, + is_root_model_root=is_root_model and field.name == 'root', + ) + for field in fields + if not (use_alias and field.has_dynamic_alias) + ] + return arguments + + def should_init_forbid_extra(self, fields: list[PydanticModelField], config: ModelConfigData) -> bool: + """Indicates whether the generated `__init__` should get a `**kwargs` at the end of its signature. + + We disallow arbitrary kwargs if the extra config setting is "forbid", or if the plugin config says to, + *unless* a required dynamic alias is present (since then we can't determine a valid signature). + """ + if not (config.validate_by_name or config.populate_by_name): + if self.is_dynamic_alias_present(fields, bool(config.has_alias_generator)): + return False + if config.forbid_extra: + return True + return self.plugin_config.init_forbid_extra + + @staticmethod + def is_dynamic_alias_present(fields: list[PydanticModelField], has_alias_generator: bool) -> bool: + """Returns whether any fields on the model have a "dynamic alias", i.e., an alias that cannot be + determined during static analysis. + """ + for field in fields: + if field.has_dynamic_alias: + return True + if has_alias_generator: + for field in fields: + if field.alias is None: + return True + return False + + +class ChangeExplicitTypeOfAny(TypeTranslator): + """A type translator used to change type of Any's, if explicit.""" + + def __init__(self, type_of_any: int) -> None: + self._type_of_any = type_of_any + super().__init__() + + def visit_any(self, t: AnyType) -> Type: # noqa: D102 + if t.type_of_any == TypeOfAny.explicit: + return t.copy_modified(type_of_any=self._type_of_any) + else: + return t + + +class ModelConfigData: + """Pydantic mypy plugin model config class.""" + + def __init__( + self, + forbid_extra: bool | None = None, + frozen: bool | None = None, + from_attributes: bool | None = None, + populate_by_name: bool | None = None, + validate_by_alias: bool | None = None, + validate_by_name: bool | None = None, + has_alias_generator: bool | None = None, + strict: bool | None = None, + ): + self.forbid_extra = forbid_extra + self.frozen = frozen + self.from_attributes = from_attributes + self.populate_by_name = populate_by_name + self.validate_by_alias = validate_by_alias + self.validate_by_name = validate_by_name + self.has_alias_generator = has_alias_generator + self.strict = strict + + def get_values_dict(self) -> dict[str, Any]: + """Returns a dict of Pydantic model config names to their values. + + It includes the config if config value is not `None`. + """ + return {k: v for k, v in self.__dict__.items() if v is not None} + + def update(self, config: ModelConfigData | None) -> None: + """Update Pydantic model config values.""" + if config is None: + return + for k, v in config.get_values_dict().items(): + setattr(self, k, v) + + def setdefault(self, key: str, value: Any) -> None: + """Set default value for Pydantic model config if config value is `None`.""" + if getattr(self, key) is None: + setattr(self, key, value) + + +def is_root_model(info: TypeInfo) -> bool: + """Return whether the type info is a root model subclass (or the `RootModel` class itself).""" + return info.has_base(ROOT_MODEL_FULLNAME) + + +ERROR_ORM = ErrorCode('pydantic-orm', 'Invalid from_attributes call', 'Pydantic') +ERROR_CONFIG = ErrorCode('pydantic-config', 'Invalid config value', 'Pydantic') +ERROR_ALIAS = ErrorCode('pydantic-alias', 'Dynamic alias disallowed', 'Pydantic') +ERROR_UNEXPECTED = ErrorCode('pydantic-unexpected', 'Unexpected behavior', 'Pydantic') +ERROR_UNTYPED = ErrorCode('pydantic-field', 'Untyped field disallowed', 'Pydantic') +ERROR_FIELD_DEFAULTS = ErrorCode('pydantic-field', 'Invalid Field defaults', 'Pydantic') +ERROR_EXTRA_FIELD_ROOT_MODEL = ErrorCode('pydantic-field', 'Extra field on RootModel subclass', 'Pydantic') + + +def error_from_attributes(model_name: str, api: CheckerPluginInterface, context: Context) -> None: + """Emits an error when the model does not have `from_attributes=True`.""" + api.fail(f'"{model_name}" does not have from_attributes=True', context, code=ERROR_ORM) + + +def error_invalid_config_value(name: str, api: SemanticAnalyzerPluginInterface, context: Context) -> None: + """Emits an error when the config value is invalid.""" + api.fail(f'Invalid value for "Config.{name}"', context, code=ERROR_CONFIG) + + +def error_required_dynamic_aliases(api: SemanticAnalyzerPluginInterface, context: Context) -> None: + """Emits required dynamic aliases error. + + This will be called when `warn_required_dynamic_aliases=True`. + """ + api.fail('Required dynamic aliases disallowed', context, code=ERROR_ALIAS) + + +def error_unexpected_behavior( + detail: str, api: CheckerPluginInterface | SemanticAnalyzerPluginInterface, context: Context +) -> None: # pragma: no cover + """Emits unexpected behavior error.""" + # Can't think of a good way to test this, but I confirmed it renders as desired by adding to a non-error path + link = 'https://github.com/pydantic/pydantic/issues/new/choose' + full_message = f'The pydantic mypy plugin ran into unexpected behavior: {detail}\n' + full_message += f'Please consider reporting this bug at {link} so we can try to fix it!' + api.fail(full_message, context, code=ERROR_UNEXPECTED) + + +def error_untyped_fields(api: SemanticAnalyzerPluginInterface, context: Context) -> None: + """Emits an error when there is an untyped field in the model.""" + api.fail('Untyped fields disallowed', context, code=ERROR_UNTYPED) + + +def error_extra_fields_on_root_model(api: CheckerPluginInterface, context: Context) -> None: + """Emits an error when there is more than just a root field defined for a subclass of RootModel.""" + api.fail('Only `root` is allowed as a field of a `RootModel`', context, code=ERROR_EXTRA_FIELD_ROOT_MODEL) + + +def add_method( + api: SemanticAnalyzerPluginInterface | CheckerPluginInterface, + cls: ClassDef, + name: str, + args: list[Argument], + return_type: Type, + self_type: Type | None = None, + tvar_def: TypeVarType | None = None, + is_classmethod: bool = False, +) -> None: + """Very closely related to `mypy.plugins.common.add_method_to_class`, with a few pydantic-specific changes.""" + info = cls.info + + # First remove any previously generated methods with the same name + # to avoid clashes and problems in the semantic analyzer. + if name in info.names: + sym = info.names[name] + if sym.plugin_generated and isinstance(sym.node, FuncDef): + cls.defs.body.remove(sym.node) # pragma: no cover + + if isinstance(api, SemanticAnalyzerPluginInterface): + function_type = api.named_type('builtins.function') + else: + function_type = api.named_generic_type('builtins.function', []) + + if is_classmethod: + self_type = self_type or TypeType(fill_typevars(info)) + first = [Argument(Var('_cls'), self_type, None, ARG_POS, True)] + else: + self_type = self_type or fill_typevars(info) + # `self` is positional *ONLY* here, but this can't be expressed + # fully in the mypy internal API. ARG_POS is the closest we can get. + # Using ARG_POS will, however, give mypy errors if a `self` field + # is present on a model: + # + # Name "self" already defined (possibly by an import) [no-redef] + # + # As a workaround, we give this argument a name that will + # never conflict. By its positional nature, this name will not + # be used or exposed to users. + first = [Argument(Var('__pydantic_self__'), self_type, None, ARG_POS)] + args = first + args + + arg_types, arg_names, arg_kinds = [], [], [] + for arg in args: + assert arg.type_annotation, 'All arguments must be fully typed.' + arg_types.append(arg.type_annotation) + arg_names.append(arg.variable.name) + arg_kinds.append(arg.kind) + + signature = CallableType( + arg_types, arg_kinds, arg_names, return_type, function_type, variables=[tvar_def] if tvar_def else None + ) + + func = FuncDef(name, args, Block([PassStmt()])) + func.info = info + func.type = set_callable_name(signature, func) + func.is_class = is_classmethod + func._fullname = info.fullname + '.' + name + func.line = info.line + + # NOTE: we would like the plugin generated node to dominate, but we still + # need to keep any existing definitions so they get semantically analyzed. + if name in info.names: + # Get a nice unique name instead. + r_name = get_unique_redefinition_name(name, info.names) + info.names[r_name] = info.names[name] + + # Add decorator for is_classmethod + # The dataclasses plugin claims this is unnecessary for classmethods, but not including it results in a + # signature incompatible with the superclass, which causes mypy errors to occur for every subclass of BaseModel. + if is_classmethod: + func.is_decorated = True + v = Var(name, func.type) + v.info = info + v._fullname = func._fullname + v.is_classmethod = True + dec = Decorator(func, [NameExpr('classmethod')], v) + dec.line = info.line + sym = SymbolTableNode(MDEF, dec) + else: + sym = SymbolTableNode(MDEF, func) + sym.plugin_generated = True + info.names[name] = sym + + info.defn.defs.body.append(func) + + +def parse_toml(config_file: str) -> dict[str, Any] | None: + """Returns a dict of config keys to values. + + It reads configs from toml file and returns `None` if the file is not a toml file. + """ + if not config_file.endswith('.toml'): + return None + + if sys.version_info >= (3, 11): + import tomllib as toml_ + else: + try: + import tomli as toml_ + except ImportError: # pragma: no cover + import warnings + + warnings.warn('No TOML parser installed, cannot read configuration from `pyproject.toml`.', stacklevel=2) + return None + + with open(config_file, 'rb') as rf: + return toml_.load(rf) diff --git a/.venv/lib/python3.12/site-packages/pydantic/networks.py b/.venv/lib/python3.12/site-packages/pydantic/networks.py new file mode 100644 index 0000000000000000000000000000000000000000..c5660825358aacacd158cec350332e9ae98790de --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/networks.py @@ -0,0 +1,1315 @@ +"""The networks module contains types for common network-related fields.""" + +from __future__ import annotations as _annotations + +import dataclasses as _dataclasses +import re +from dataclasses import fields +from functools import lru_cache +from importlib.metadata import version +from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network +from typing import TYPE_CHECKING, Annotated, Any, ClassVar + +from pydantic_core import ( + MultiHostHost, + PydanticCustomError, + PydanticSerializationUnexpectedValue, + SchemaSerializer, + core_schema, +) +from pydantic_core import MultiHostUrl as _CoreMultiHostUrl +from pydantic_core import Url as _CoreUrl +from typing_extensions import Self, TypeAlias + +from pydantic.errors import PydanticUserError + +from ._internal import _repr, _schema_generation_shared +from ._migration import getattr_migration +from .annotated_handlers import GetCoreSchemaHandler +from .json_schema import JsonSchemaValue +from .type_adapter import TypeAdapter + +if TYPE_CHECKING: + import email_validator + + NetworkType: TypeAlias = 'str | bytes | int | tuple[str | bytes | int, str | int]' + +else: + email_validator = None + + +__all__ = [ + 'AnyUrl', + 'AnyHttpUrl', + 'FileUrl', + 'FtpUrl', + 'HttpUrl', + 'WebsocketUrl', + 'AnyWebsocketUrl', + 'UrlConstraints', + 'EmailStr', + 'NameEmail', + 'IPvAnyAddress', + 'IPvAnyInterface', + 'IPvAnyNetwork', + 'PostgresDsn', + 'CockroachDsn', + 'AmqpDsn', + 'RedisDsn', + 'MongoDsn', + 'KafkaDsn', + 'NatsDsn', + 'validate_email', + 'MySQLDsn', + 'MariaDBDsn', + 'ClickHouseDsn', + 'SnowflakeDsn', +] + + +@_dataclasses.dataclass +class UrlConstraints: + """Url constraints. + + Attributes: + max_length: The maximum length of the url. Defaults to `None`. + allowed_schemes: The allowed schemes. Defaults to `None`. + host_required: Whether the host is required. Defaults to `None`. + default_host: The default host. Defaults to `None`. + default_port: The default port. Defaults to `None`. + default_path: The default path. Defaults to `None`. + preserve_empty_path: Whether to preserve empty URL paths. Defaults to `None`. + """ + + max_length: int | None = None + allowed_schemes: list[str] | None = None + host_required: bool | None = None + default_host: str | None = None + default_port: int | None = None + default_path: str | None = None + preserve_empty_path: bool | None = None + + def __hash__(self) -> int: + return hash( + ( + self.max_length, + tuple(self.allowed_schemes) if self.allowed_schemes is not None else None, + self.host_required, + self.default_host, + self.default_port, + self.default_path, + self.preserve_empty_path, + ) + ) + + @property + def defined_constraints(self) -> dict[str, Any]: + """Fetch a key / value mapping of constraints to values that are not None. Used for core schema updates.""" + return {field.name: value for field in fields(self) if (value := getattr(self, field.name)) is not None} + + def __get_pydantic_core_schema__(self, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + schema = handler(source) + + # for function-wrap schemas, url constraints is applied to the inner schema + # because when we generate schemas for urls, we wrap a core_schema.url_schema() with a function-wrap schema + # that helps with validation on initialization, see _BaseUrl and _BaseMultiHostUrl below. + schema_to_mutate = schema['schema'] if schema['type'] == 'function-wrap' else schema + if annotated_type := schema_to_mutate['type'] not in ('url', 'multi-host-url'): + raise PydanticUserError( + f"'UrlConstraints' cannot annotate '{annotated_type}'.", code='invalid-annotated-type' + ) + for constraint_key, constraint_value in self.defined_constraints.items(): + schema_to_mutate[constraint_key] = constraint_value + return schema + + +class _BaseUrl: + _constraints: ClassVar[UrlConstraints] = UrlConstraints() + _url: _CoreUrl + + def __init__(self, url: str | _CoreUrl | _BaseUrl) -> None: + self._url = _build_type_adapter(self.__class__).validate_python(url)._url + + @property + def scheme(self) -> str: + """The scheme part of the URL. + + e.g. `https` in `https://user:pass@host:port/path?query#fragment` + """ + return self._url.scheme + + @property + def username(self) -> str | None: + """The username part of the URL, or `None`. + + e.g. `user` in `https://user:pass@host:port/path?query#fragment` + """ + return self._url.username + + @property + def password(self) -> str | None: + """The password part of the URL, or `None`. + + e.g. `pass` in `https://user:pass@host:port/path?query#fragment` + """ + return self._url.password + + @property + def host(self) -> str | None: + """The host part of the URL, or `None`. + + If the URL must be punycode encoded, this is the encoded host, e.g if the input URL is `https://£££.com`, + `host` will be `xn--9aaa.com` + """ + return self._url.host + + def unicode_host(self) -> str | None: + """The host part of the URL as a unicode string, or `None`. + + e.g. `host` in `https://user:pass@host:port/path?query#fragment` + + If the URL must be punycode encoded, this is the decoded host, e.g if the input URL is `https://£££.com`, + `unicode_host()` will be `£££.com` + """ + return self._url.unicode_host() + + @property + def port(self) -> int | None: + """The port part of the URL, or `None`. + + e.g. `port` in `https://user:pass@host:port/path?query#fragment` + """ + return self._url.port + + @property + def path(self) -> str | None: + """The path part of the URL, or `None`. + + e.g. `/path` in `https://user:pass@host:port/path?query#fragment` + """ + return self._url.path + + @property + def query(self) -> str | None: + """The query part of the URL, or `None`. + + e.g. `query` in `https://user:pass@host:port/path?query#fragment` + """ + return self._url.query + + def query_params(self) -> list[tuple[str, str]]: + """The query part of the URL as a list of key-value pairs. + + e.g. `[('foo', 'bar')]` in `https://user:pass@host:port/path?foo=bar#fragment` + """ + return self._url.query_params() + + @property + def fragment(self) -> str | None: + """The fragment part of the URL, or `None`. + + e.g. `fragment` in `https://user:pass@host:port/path?query#fragment` + """ + return self._url.fragment + + def unicode_string(self) -> str: + """The URL as a unicode string, unlike `__str__()` this will not punycode encode the host. + + If the URL must be punycode encoded, this is the decoded string, e.g if the input URL is `https://£££.com`, + `unicode_string()` will be `https://£££.com` + """ + return self._url.unicode_string() + + def encoded_string(self) -> str: + """The URL's encoded string representation via __str__(). + + This returns the punycode-encoded host version of the URL as a string. + """ + return str(self) + + def __str__(self) -> str: + """The URL as a string, this will punycode encode the host if required.""" + return str(self._url) + + def __repr__(self) -> str: + return f'{self.__class__.__name__}({str(self._url)!r})' + + def __deepcopy__(self, memo: dict) -> Self: + return self.__class__(self._url) + + def __eq__(self, other: Any) -> bool: + return self.__class__ is other.__class__ and self._url == other._url + + def __lt__(self, other: Any) -> bool: + return self.__class__ is other.__class__ and self._url < other._url + + def __gt__(self, other: Any) -> bool: + return self.__class__ is other.__class__ and self._url > other._url + + def __le__(self, other: Any) -> bool: + return self.__class__ is other.__class__ and self._url <= other._url + + def __ge__(self, other: Any) -> bool: + return self.__class__ is other.__class__ and self._url >= other._url + + def __hash__(self) -> int: + return hash(self._url) + + def __len__(self) -> int: + return len(str(self._url)) + + @classmethod + def build( + cls, + *, + scheme: str, + username: str | None = None, + password: str | None = None, + host: str, + port: int | None = None, + path: str | None = None, + query: str | None = None, + fragment: str | None = None, + ) -> Self: + """Build a new `Url` instance from its component parts. + + Args: + scheme: The scheme part of the URL. + username: The username part of the URL, or omit for no username. + password: The password part of the URL, or omit for no password. + host: The host part of the URL. + port: The port part of the URL, or omit for no port. + path: The path part of the URL, or omit for no path. + query: The query part of the URL, or omit for no query. + fragment: The fragment part of the URL, or omit for no fragment. + + Returns: + An instance of URL + """ + return cls( + _CoreUrl.build( + scheme=scheme, + username=username, + password=password, + host=host, + port=port, + path=path, + query=query, + fragment=fragment, + ) + ) + + @classmethod + def serialize_url(cls, url: Any, info: core_schema.SerializationInfo) -> str | Self: + if not isinstance(url, cls): + raise PydanticSerializationUnexpectedValue( + f"Expected `{cls}` but got `{type(url)}` with value `'{url}'` - serialized value may not be as expected." + ) + if info.mode == 'json': + return str(url) + return url + + @classmethod + def __get_pydantic_core_schema__( + cls, source: type[_BaseUrl], handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + def wrap_val(v, h): + if isinstance(v, source): + return v + if isinstance(v, _BaseUrl): + v = str(v) + core_url = h(v) + instance = source.__new__(source) + instance._url = core_url + return instance + + return core_schema.no_info_wrap_validator_function( + wrap_val, + schema=core_schema.url_schema(**cls._constraints.defined_constraints), + serialization=core_schema.plain_serializer_function_ser_schema( + cls.serialize_url, info_arg=True, when_used='always' + ), + ) + + @classmethod + def __get_pydantic_json_schema__( + cls, core_schema: core_schema.CoreSchema, handler: _schema_generation_shared.GetJsonSchemaHandler + ) -> JsonSchemaValue: + # we use the url schema for json schema generation, but we might have to extract it from + # the function-wrap schema we use as a tool for validation on initialization + inner_schema = core_schema['schema'] if core_schema['type'] == 'function-wrap' else core_schema + return handler(inner_schema) + + __pydantic_serializer__ = SchemaSerializer(core_schema.any_schema(serialization=core_schema.to_string_ser_schema())) + + +class _BaseMultiHostUrl: + _constraints: ClassVar[UrlConstraints] = UrlConstraints() + _url: _CoreMultiHostUrl + + def __init__(self, url: str | _CoreMultiHostUrl | _BaseMultiHostUrl) -> None: + self._url = _build_type_adapter(self.__class__).validate_python(url)._url + + @property + def scheme(self) -> str: + """The scheme part of the URL. + + e.g. `https` in `https://foo.com,bar.com/path?query#fragment` + """ + return self._url.scheme + + @property + def path(self) -> str | None: + """The path part of the URL, or `None`. + + e.g. `/path` in `https://foo.com,bar.com/path?query#fragment` + """ + return self._url.path + + @property + def query(self) -> str | None: + """The query part of the URL, or `None`. + + e.g. `query` in `https://foo.com,bar.com/path?query#fragment` + """ + return self._url.query + + def query_params(self) -> list[tuple[str, str]]: + """The query part of the URL as a list of key-value pairs. + + e.g. `[('foo', 'bar')]` in `https://foo.com,bar.com/path?foo=bar#fragment` + """ + return self._url.query_params() + + @property + def fragment(self) -> str | None: + """The fragment part of the URL, or `None`. + + e.g. `fragment` in `https://foo.com,bar.com/path?query#fragment` + """ + return self._url.fragment + + def hosts(self) -> list[MultiHostHost]: + '''The hosts of the `MultiHostUrl` as [`MultiHostHost`][pydantic_core.MultiHostHost] typed dicts. + + ```python + from pydantic_core import MultiHostUrl + + mhu = MultiHostUrl('https://foo.com:123,foo:bar@bar.com/path') + print(mhu.hosts()) + """ + [ + {'username': None, 'password': None, 'host': 'foo.com', 'port': 123}, + {'username': 'foo', 'password': 'bar', 'host': 'bar.com', 'port': 443} + ] + ``` + Returns: + A list of dicts, each representing a host. + ''' + return self._url.hosts() + + def encoded_string(self) -> str: + """The URL's encoded string representation via __str__(). + + This returns the punycode-encoded host version of the URL as a string. + """ + return str(self) + + def unicode_string(self) -> str: + """The URL as a unicode string, unlike `__str__()` this will not punycode encode the hosts.""" + return self._url.unicode_string() + + def __str__(self) -> str: + """The URL as a string, this will punycode encode the host if required.""" + return str(self._url) + + def __repr__(self) -> str: + return f'{self.__class__.__name__}({str(self._url)!r})' + + def __deepcopy__(self, memo: dict) -> Self: + return self.__class__(self._url) + + def __eq__(self, other: Any) -> bool: + return self.__class__ is other.__class__ and self._url == other._url + + def __hash__(self) -> int: + return hash(self._url) + + def __len__(self) -> int: + return len(str(self._url)) + + @classmethod + def build( + cls, + *, + scheme: str, + hosts: list[MultiHostHost] | None = None, + username: str | None = None, + password: str | None = None, + host: str | None = None, + port: int | None = None, + path: str | None = None, + query: str | None = None, + fragment: str | None = None, + ) -> Self: + """Build a new `MultiHostUrl` instance from its component parts. + + This method takes either `hosts` - a list of `MultiHostHost` typed dicts, or the individual components + `username`, `password`, `host` and `port`. + + Args: + scheme: The scheme part of the URL. + hosts: Multiple hosts to build the URL from. + username: The username part of the URL. + password: The password part of the URL. + host: The host part of the URL. + port: The port part of the URL. + path: The path part of the URL. + query: The query part of the URL, or omit for no query. + fragment: The fragment part of the URL, or omit for no fragment. + + Returns: + An instance of `MultiHostUrl` + """ + return cls( + _CoreMultiHostUrl.build( + scheme=scheme, + hosts=hosts, + username=username, + password=password, + host=host, + port=port, + path=path, + query=query, + fragment=fragment, + ) + ) + + @classmethod + def serialize_url(cls, url: Any, info: core_schema.SerializationInfo) -> str | Self: + if not isinstance(url, cls): + raise PydanticSerializationUnexpectedValue( + f"Expected `{cls}` but got `{type(url)}` with value `'{url}'` - serialized value may not be as expected." + ) + if info.mode == 'json': + return str(url) + return url + + @classmethod + def __get_pydantic_core_schema__( + cls, source: type[_BaseMultiHostUrl], handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + def wrap_val(v, h): + if isinstance(v, source): + return v + if isinstance(v, _BaseMultiHostUrl): + v = str(v) + core_url = h(v) + instance = source.__new__(source) + instance._url = core_url + return instance + + return core_schema.no_info_wrap_validator_function( + wrap_val, + schema=core_schema.multi_host_url_schema(**cls._constraints.defined_constraints), + serialization=core_schema.plain_serializer_function_ser_schema( + cls.serialize_url, info_arg=True, when_used='always' + ), + ) + + @classmethod + def __get_pydantic_json_schema__( + cls, core_schema: core_schema.CoreSchema, handler: _schema_generation_shared.GetJsonSchemaHandler + ) -> JsonSchemaValue: + # we use the url schema for json schema generation, but we might have to extract it from + # the function-wrap schema we use as a tool for validation on initialization + inner_schema = core_schema['schema'] if core_schema['type'] == 'function-wrap' else core_schema + return handler(inner_schema) + + __pydantic_serializer__ = SchemaSerializer(core_schema.any_schema(serialization=core_schema.to_string_ser_schema())) + + +@lru_cache +def _build_type_adapter(cls: type[_BaseUrl | _BaseMultiHostUrl]) -> TypeAdapter: + return TypeAdapter(cls) + + +class AnyUrl(_BaseUrl): + """Base type for all URLs. + + * Any scheme allowed + * Top-level domain (TLD) not required + * Host not required + + Assuming an input URL of `http://samuel:pass@example.com:8000/the/path/?query=here#fragment=is;this=bit`, + the types export the following properties: + + - `scheme`: the URL scheme (`http`), always set. + - `host`: the URL host (`example.com`). + - `username`: optional username if included (`samuel`). + - `password`: optional password if included (`pass`). + - `port`: optional port (`8000`). + - `path`: optional path (`/the/path/`). + - `query`: optional URL query (for example, `GET` arguments or "search string", such as `query=here`). + - `fragment`: optional fragment (`fragment=is;this=bit`). + """ + + +# Note: all single host urls inherit from `AnyUrl` to preserve compatibility with pre-v2.10 code +# Where urls were annotated variants of `AnyUrl`, which was an alias to `pydantic_core.Url` + + +class AnyHttpUrl(AnyUrl): + """A type that will accept any http or https URL. + + * TLD not required + * Host not required + """ + + _constraints = UrlConstraints(allowed_schemes=['http', 'https']) + + +class HttpUrl(AnyUrl): + """A type that will accept any http or https URL. + + * TLD not required + * Host not required + * Max length 2083 + + ```python + from pydantic import BaseModel, HttpUrl, ValidationError + + class MyModel(BaseModel): + url: HttpUrl + + m = MyModel(url='http://www.example.com') # (1)! + print(m.url) + #> http://www.example.com/ + + try: + MyModel(url='ftp://invalid.url') + except ValidationError as e: + print(e) + ''' + 1 validation error for MyModel + url + URL scheme should be 'http' or 'https' [type=url_scheme, input_value='ftp://invalid.url', input_type=str] + ''' + + try: + MyModel(url='not a url') + except ValidationError as e: + print(e) + ''' + 1 validation error for MyModel + url + Input should be a valid URL, relative URL without a base [type=url_parsing, input_value='not a url', input_type=str] + ''' + ``` + + 1. Note: mypy would prefer `m = MyModel(url=HttpUrl('http://www.example.com'))`, but Pydantic will convert the string to an HttpUrl instance anyway. + + "International domains" (e.g. a URL where the host or TLD includes non-ascii characters) will be encoded via + [punycode](https://en.wikipedia.org/wiki/Punycode) (see + [this article](https://www.xudongz.com/blog/2017/idn-phishing/) for a good description of why this is important): + + ```python + from pydantic import BaseModel, HttpUrl + + class MyModel(BaseModel): + url: HttpUrl + + m1 = MyModel(url='http://puny£code.com') + print(m1.url) + #> http://xn--punycode-eja.com/ + m2 = MyModel(url='https://www.аррӏе.com/') + print(m2.url) + #> https://www.xn--80ak6aa92e.com/ + m3 = MyModel(url='https://www.example.珠宝/') + print(m3.url) + #> https://www.example.xn--pbt977c/ + ``` + + + !!! warning "Underscores in Hostnames" + In Pydantic, underscores are allowed in all parts of a domain except the TLD. + Technically this might be wrong - in theory the hostname cannot have underscores, but subdomains can. + + To explain this; consider the following two cases: + + - `exam_ple.co.uk`: the hostname is `exam_ple`, which should not be allowed since it contains an underscore. + - `foo_bar.example.com` the hostname is `example`, which should be allowed since the underscore is in the subdomain. + + Without having an exhaustive list of TLDs, it would be impossible to differentiate between these two. Therefore + underscores are allowed, but you can always do further validation in a validator if desired. + + Also, Chrome, Firefox, and Safari all currently accept `http://exam_ple.com` as a URL, so we're in good + (or at least big) company. + """ + + _constraints = UrlConstraints(max_length=2083, allowed_schemes=['http', 'https']) + + +class AnyWebsocketUrl(AnyUrl): + """A type that will accept any ws or wss URL. + + * TLD not required + * Host not required + """ + + _constraints = UrlConstraints(allowed_schemes=['ws', 'wss']) + + +class WebsocketUrl(AnyUrl): + """A type that will accept any ws or wss URL. + + * TLD not required + * Host not required + * Max length 2083 + """ + + _constraints = UrlConstraints(max_length=2083, allowed_schemes=['ws', 'wss']) + + +class FileUrl(AnyUrl): + """A type that will accept any file URL. + + * Host not required + """ + + _constraints = UrlConstraints(allowed_schemes=['file']) + + +class FtpUrl(AnyUrl): + """A type that will accept ftp URL. + + * TLD not required + * Host not required + """ + + _constraints = UrlConstraints(allowed_schemes=['ftp']) + + +class PostgresDsn(_BaseMultiHostUrl): + """A type that will accept any Postgres DSN. + + * User info required + * TLD not required + * Host required + * Supports multiple hosts + + If further validation is required, these properties can be used by validators to enforce specific behaviour: + + ```python + from pydantic import ( + BaseModel, + HttpUrl, + PostgresDsn, + ValidationError, + field_validator, + ) + + class MyModel(BaseModel): + url: HttpUrl + + m = MyModel(url='http://www.example.com') + + # the repr() method for a url will display all properties of the url + print(repr(m.url)) + #> HttpUrl('http://www.example.com/') + print(m.url.scheme) + #> http + print(m.url.host) + #> www.example.com + print(m.url.port) + #> 80 + + class MyDatabaseModel(BaseModel): + db: PostgresDsn + + @field_validator('db') + def check_db_name(cls, v): + assert v.path and len(v.path) > 1, 'database must be provided' + return v + + m = MyDatabaseModel(db='postgres://user:pass@localhost:5432/foobar') + print(m.db) + #> postgres://user:pass@localhost:5432/foobar + + try: + MyDatabaseModel(db='postgres://user:pass@localhost:5432') + except ValidationError as e: + print(e) + ''' + 1 validation error for MyDatabaseModel + db + Assertion failed, database must be provided + assert (None) + + where None = PostgresDsn('postgres://user:pass@localhost:5432').path [type=assertion_error, input_value='postgres://user:pass@localhost:5432', input_type=str] + ''' + ``` + """ + + _constraints = UrlConstraints( + host_required=True, + allowed_schemes=[ + 'postgres', + 'postgresql', + 'postgresql+asyncpg', + 'postgresql+pg8000', + 'postgresql+psycopg', + 'postgresql+psycopg2', + 'postgresql+psycopg2cffi', + 'postgresql+py-postgresql', + 'postgresql+pygresql', + ], + ) + + @property + def host(self) -> str: + """The required URL host.""" + return self._url.host # pyright: ignore[reportAttributeAccessIssue] + + +class CockroachDsn(AnyUrl): + """A type that will accept any Cockroach DSN. + + * User info required + * TLD not required + * Host required + """ + + _constraints = UrlConstraints( + host_required=True, + allowed_schemes=[ + 'cockroachdb', + 'cockroachdb+psycopg2', + 'cockroachdb+asyncpg', + ], + ) + + @property + def host(self) -> str: + """The required URL host.""" + return self._url.host # pyright: ignore[reportReturnType] + + +class AmqpDsn(AnyUrl): + """A type that will accept any AMQP DSN. + + * User info required + * TLD not required + * Host not required + """ + + _constraints = UrlConstraints(allowed_schemes=['amqp', 'amqps']) + + +class RedisDsn(AnyUrl): + """A type that will accept any Redis DSN. + + * User info required + * TLD not required + * Host required (e.g., `rediss://:pass@localhost`) + """ + + _constraints = UrlConstraints( + allowed_schemes=['redis', 'rediss'], + default_host='localhost', + default_port=6379, + default_path='/0', + host_required=True, + ) + + @property + def host(self) -> str: + """The required URL host.""" + return self._url.host # pyright: ignore[reportReturnType] + + +class MongoDsn(_BaseMultiHostUrl): + """A type that will accept any MongoDB DSN. + + * User info not required + * Database name not required + * Port not required + * User info may be passed without user part (e.g., `mongodb://mongodb0.example.com:27017`). + """ + + _constraints = UrlConstraints(allowed_schemes=['mongodb', 'mongodb+srv'], default_port=27017) + + +class KafkaDsn(AnyUrl): + """A type that will accept any Kafka DSN. + + * User info required + * TLD not required + * Host not required + """ + + _constraints = UrlConstraints(allowed_schemes=['kafka'], default_host='localhost', default_port=9092) + + +class NatsDsn(_BaseMultiHostUrl): + """A type that will accept any NATS DSN. + + NATS is a connective technology built for the ever increasingly hyper-connected world. + It is a single technology that enables applications to securely communicate across + any combination of cloud vendors, on-premise, edge, web and mobile, and devices. + More: https://nats.io + """ + + _constraints = UrlConstraints( + allowed_schemes=['nats', 'tls', 'ws', 'wss'], default_host='localhost', default_port=4222 + ) + + +class MySQLDsn(AnyUrl): + """A type that will accept any MySQL DSN. + + * User info required + * TLD not required + * Host not required + """ + + _constraints = UrlConstraints( + allowed_schemes=[ + 'mysql', + 'mysql+mysqlconnector', + 'mysql+aiomysql', + 'mysql+asyncmy', + 'mysql+mysqldb', + 'mysql+pymysql', + 'mysql+cymysql', + 'mysql+pyodbc', + ], + default_port=3306, + host_required=True, + ) + + +class MariaDBDsn(AnyUrl): + """A type that will accept any MariaDB DSN. + + * User info required + * TLD not required + * Host not required + """ + + _constraints = UrlConstraints( + allowed_schemes=['mariadb', 'mariadb+mariadbconnector', 'mariadb+pymysql'], + default_port=3306, + ) + + +class ClickHouseDsn(AnyUrl): + """A type that will accept any ClickHouse DSN. + + * User info required + * TLD not required + * Host not required + """ + + _constraints = UrlConstraints( + allowed_schemes=[ + 'clickhouse+native', + 'clickhouse+asynch', + 'clickhouse+http', + 'clickhouse', + 'clickhouses', + 'clickhousedb', + ], + default_host='localhost', + default_port=9000, + ) + + +class SnowflakeDsn(AnyUrl): + """A type that will accept any Snowflake DSN. + + * User info required + * TLD not required + * Host required + """ + + _constraints = UrlConstraints( + allowed_schemes=['snowflake'], + host_required=True, + ) + + @property + def host(self) -> str: + """The required URL host.""" + return self._url.host # pyright: ignore[reportReturnType] + + +def import_email_validator() -> None: + global email_validator + try: + import email_validator + except ImportError as e: + raise ImportError("email-validator is not installed, run `pip install 'pydantic[email]'`") from e + if not version('email-validator').partition('.')[0] == '2': + raise ImportError('email-validator version >= 2.0 required, run pip install -U email-validator') + + +if TYPE_CHECKING: + EmailStr = Annotated[str, ...] +else: + + class EmailStr: + """ + Info: + To use this type, you need to install the optional + [`email-validator`](https://github.com/JoshData/python-email-validator) package: + + ```bash + pip install email-validator + ``` + + Validate email addresses. + + ```python + from pydantic import BaseModel, EmailStr + + class Model(BaseModel): + email: EmailStr + + print(Model(email='contact@mail.com')) + #> email='contact@mail.com' + ``` + """ # noqa: D212 + + @classmethod + def __get_pydantic_core_schema__( + cls, + _source: type[Any], + _handler: GetCoreSchemaHandler, + ) -> core_schema.CoreSchema: + import_email_validator() + return core_schema.no_info_after_validator_function(cls._validate, core_schema.str_schema()) + + @classmethod + def __get_pydantic_json_schema__( + cls, core_schema: core_schema.CoreSchema, handler: _schema_generation_shared.GetJsonSchemaHandler + ) -> JsonSchemaValue: + field_schema = handler(core_schema) + field_schema.update(type='string', format='email') + return field_schema + + @classmethod + def _validate(cls, input_value: str, /) -> str: + return validate_email(input_value)[1] + + +class NameEmail(_repr.Representation): + """ + Info: + To use this type, you need to install the optional + [`email-validator`](https://github.com/JoshData/python-email-validator) package: + + ```bash + pip install email-validator + ``` + + Validate a name and email address combination, as specified by + [RFC 5322](https://datatracker.ietf.org/doc/html/rfc5322#section-3.4). + + The `NameEmail` has two properties: `name` and `email`. + In case the `name` is not provided, it's inferred from the email address. + + ```python + from pydantic import BaseModel, NameEmail + + class User(BaseModel): + email: NameEmail + + user = User(email='Fred Bloggs ') + print(user.email) + #> Fred Bloggs + print(user.email.name) + #> Fred Bloggs + + user = User(email='fred.bloggs@example.com') + print(user.email) + #> fred.bloggs + print(user.email.name) + #> fred.bloggs + ``` + """ # noqa: D212 + + __slots__ = 'name', 'email' + + def __init__(self, name: str, email: str): + self.name = name + self.email = email + + def __eq__(self, other: Any) -> bool: + return isinstance(other, NameEmail) and (self.name, self.email) == (other.name, other.email) + + @classmethod + def __get_pydantic_json_schema__( + cls, core_schema: core_schema.CoreSchema, handler: _schema_generation_shared.GetJsonSchemaHandler + ) -> JsonSchemaValue: + field_schema = handler(core_schema) + field_schema.update(type='string', format='name-email') + return field_schema + + @classmethod + def __get_pydantic_core_schema__( + cls, + _source: type[Any], + _handler: GetCoreSchemaHandler, + ) -> core_schema.CoreSchema: + import_email_validator() + + return core_schema.no_info_after_validator_function( + cls._validate, + core_schema.json_or_python_schema( + json_schema=core_schema.str_schema(), + python_schema=core_schema.union_schema( + [core_schema.is_instance_schema(cls), core_schema.str_schema()], + custom_error_type='name_email_type', + custom_error_message='Input is not a valid NameEmail', + ), + serialization=core_schema.to_string_ser_schema(), + ), + ) + + @classmethod + def _validate(cls, input_value: Self | str, /) -> Self: + if isinstance(input_value, str): + name, email = validate_email(input_value) + return cls(name, email) + else: + return input_value + + def __str__(self) -> str: + if '@' in self.name: + return f'"{self.name}" <{self.email}>' + + return f'{self.name} <{self.email}>' + + +IPvAnyAddressType: TypeAlias = 'IPv4Address | IPv6Address' +IPvAnyInterfaceType: TypeAlias = 'IPv4Interface | IPv6Interface' +IPvAnyNetworkType: TypeAlias = 'IPv4Network | IPv6Network' + +if TYPE_CHECKING: + IPvAnyAddress = IPvAnyAddressType + IPvAnyInterface = IPvAnyInterfaceType + IPvAnyNetwork = IPvAnyNetworkType +else: + + class IPvAnyAddress: + """Validate an IPv4 or IPv6 address. + + ```python + from pydantic import BaseModel + from pydantic.networks import IPvAnyAddress + + class IpModel(BaseModel): + ip: IPvAnyAddress + + print(IpModel(ip='127.0.0.1')) + #> ip=IPv4Address('127.0.0.1') + + try: + IpModel(ip='http://www.example.com') + except ValueError as e: + print(e.errors()) + ''' + [ + { + 'type': 'ip_any_address', + 'loc': ('ip',), + 'msg': 'value is not a valid IPv4 or IPv6 address', + 'input': 'http://www.example.com', + } + ] + ''' + ``` + """ + + __slots__ = () + + def __new__(cls, value: Any) -> IPvAnyAddressType: + """Validate an IPv4 or IPv6 address.""" + try: + return IPv4Address(value) + except ValueError: + pass + + try: + return IPv6Address(value) + except ValueError: + raise PydanticCustomError('ip_any_address', 'value is not a valid IPv4 or IPv6 address') + + @classmethod + def __get_pydantic_json_schema__( + cls, core_schema: core_schema.CoreSchema, handler: _schema_generation_shared.GetJsonSchemaHandler + ) -> JsonSchemaValue: + field_schema = {} + field_schema.update(type='string', format='ipvanyaddress') + return field_schema + + @classmethod + def __get_pydantic_core_schema__( + cls, + _source: type[Any], + _handler: GetCoreSchemaHandler, + ) -> core_schema.CoreSchema: + return core_schema.no_info_plain_validator_function( + cls._validate, serialization=core_schema.to_string_ser_schema() + ) + + @classmethod + def _validate(cls, input_value: Any, /) -> IPvAnyAddressType: + return cls(input_value) # type: ignore[return-value] + + class IPvAnyInterface: + """Validate an IPv4 or IPv6 interface.""" + + __slots__ = () + + def __new__(cls, value: NetworkType) -> IPvAnyInterfaceType: + """Validate an IPv4 or IPv6 interface.""" + try: + return IPv4Interface(value) + except ValueError: + pass + + try: + return IPv6Interface(value) + except ValueError: + raise PydanticCustomError('ip_any_interface', 'value is not a valid IPv4 or IPv6 interface') + + @classmethod + def __get_pydantic_json_schema__( + cls, core_schema: core_schema.CoreSchema, handler: _schema_generation_shared.GetJsonSchemaHandler + ) -> JsonSchemaValue: + field_schema = {} + field_schema.update(type='string', format='ipvanyinterface') + return field_schema + + @classmethod + def __get_pydantic_core_schema__( + cls, + _source: type[Any], + _handler: GetCoreSchemaHandler, + ) -> core_schema.CoreSchema: + return core_schema.no_info_plain_validator_function( + cls._validate, serialization=core_schema.to_string_ser_schema() + ) + + @classmethod + def _validate(cls, input_value: NetworkType, /) -> IPvAnyInterfaceType: + return cls(input_value) # type: ignore[return-value] + + class IPvAnyNetwork: + """Validate an IPv4 or IPv6 network.""" + + __slots__ = () + + def __new__(cls, value: NetworkType) -> IPvAnyNetworkType: + """Validate an IPv4 or IPv6 network.""" + # Assume IP Network is defined with a default value for `strict` argument. + # Define your own class if you want to specify network address check strictness. + try: + return IPv4Network(value) + except ValueError: + pass + + try: + return IPv6Network(value) + except ValueError: + raise PydanticCustomError('ip_any_network', 'value is not a valid IPv4 or IPv6 network') + + @classmethod + def __get_pydantic_json_schema__( + cls, core_schema: core_schema.CoreSchema, handler: _schema_generation_shared.GetJsonSchemaHandler + ) -> JsonSchemaValue: + field_schema = {} + field_schema.update(type='string', format='ipvanynetwork') + return field_schema + + @classmethod + def __get_pydantic_core_schema__( + cls, + _source: type[Any], + _handler: GetCoreSchemaHandler, + ) -> core_schema.CoreSchema: + return core_schema.no_info_plain_validator_function( + cls._validate, serialization=core_schema.to_string_ser_schema() + ) + + @classmethod + def _validate(cls, input_value: NetworkType, /) -> IPvAnyNetworkType: + return cls(input_value) # type: ignore[return-value] + + +def _build_pretty_email_regex() -> re.Pattern[str]: + name_chars = r'[\w!#$%&\'*+\-/=?^_`{|}~]' + unquoted_name_group = rf'((?:{name_chars}+\s+)*{name_chars}+)' + quoted_name_group = r'"((?:[^"]|\")+)"' + email_group = r'<(.+)>' + return re.compile(rf'\s*(?:{unquoted_name_group}|{quoted_name_group})?\s*{email_group}\s*') + + +pretty_email_regex = _build_pretty_email_regex() + +MAX_EMAIL_LENGTH = 2048 +"""Maximum length for an email. +A somewhat arbitrary but very generous number compared to what is allowed by most implementations. +""" + + +def validate_email(value: str) -> tuple[str, str]: + """Email address validation using [email-validator](https://pypi.org/project/email-validator/). + + Returns: + A tuple containing the local part of the email (or the name for "pretty" email addresses) + and the normalized email. + + Raises: + PydanticCustomError: If the email is invalid. + + Note: + Note that: + + * Raw IP address (literal) domain parts are not allowed. + * `"John Doe "` style "pretty" email addresses are processed. + * Spaces are striped from the beginning and end of addresses, but no error is raised. + """ + if email_validator is None: + import_email_validator() + + if len(value) > MAX_EMAIL_LENGTH: + raise PydanticCustomError( + 'value_error', + 'value is not a valid email address: {reason}', + {'reason': f'Length must not exceed {MAX_EMAIL_LENGTH} characters'}, + ) + + m = pretty_email_regex.fullmatch(value) + name: str | None = None + if m: + unquoted_name, quoted_name, value = m.groups() + name = unquoted_name or quoted_name + + email = value.strip() + + try: + parts = email_validator.validate_email(email, check_deliverability=False) + except email_validator.EmailNotValidError as e: + raise PydanticCustomError( + 'value_error', 'value is not a valid email address: {reason}', {'reason': str(e.args[0])} + ) from e + + email = parts.normalized + assert email is not None + name = name or parts.local_part + return name, email + + +__getattr__ = getattr_migration(__name__) diff --git a/.venv/lib/python3.12/site-packages/pydantic/parse.py b/.venv/lib/python3.12/site-packages/pydantic/parse.py new file mode 100644 index 0000000000000000000000000000000000000000..68b7f0464ece32edfb955eec0cb24655752716d6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/parse.py @@ -0,0 +1,5 @@ +"""The `parse` module is a backport module from V1.""" + +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.venv/lib/python3.12/site-packages/pydantic/py.typed b/.venv/lib/python3.12/site-packages/pydantic/py.typed new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/pydantic/root_model.py b/.venv/lib/python3.12/site-packages/pydantic/root_model.py new file mode 100644 index 0000000000000000000000000000000000000000..80a54201c427800ab28540296c31d9701e5093c3 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/root_model.py @@ -0,0 +1,155 @@ +"""RootModel class and type definitions.""" + +from __future__ import annotations as _annotations + +from copy import copy, deepcopy +from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar + +from pydantic_core import PydanticUndefined +from typing_extensions import Self, dataclass_transform + +from . import PydanticUserError +from ._internal import _model_construction, _repr +from .main import BaseModel, _object_setattr + +if TYPE_CHECKING: + from .fields import Field as PydanticModelField + from .fields import PrivateAttr as PydanticModelPrivateAttr + + # dataclass_transform could be applied to RootModel directly, but `ModelMetaclass`'s dataclass_transform + # takes priority (at least with pyright). We trick type checkers into thinking we apply dataclass_transform + # on a new metaclass. + @dataclass_transform(kw_only_default=False, field_specifiers=(PydanticModelField, PydanticModelPrivateAttr)) + class _RootModelMetaclass(_model_construction.ModelMetaclass): ... +else: + _RootModelMetaclass = _model_construction.ModelMetaclass + +__all__ = ('RootModel',) + +RootModelRootType = TypeVar('RootModelRootType') + + +class RootModel(BaseModel, Generic[RootModelRootType], metaclass=_RootModelMetaclass): + """!!! abstract "Usage Documentation" + [`RootModel` and Custom Root Types](../concepts/models.md#rootmodel-and-custom-root-types) + + A Pydantic `BaseModel` for the root object of the model. + + Attributes: + root: The root object of the model. + __pydantic_root_model__: Whether the model is a RootModel. + __pydantic_private__: Private fields in the model. + __pydantic_extra__: Extra fields in the model. + + """ + + __pydantic_root_model__ = True + __pydantic_private__ = None + __pydantic_extra__ = None + + root: RootModelRootType + + def __init_subclass__(cls, **kwargs): + extra = cls.model_config.get('extra') + if extra is not None: + raise PydanticUserError( + "`RootModel` does not support setting `model_config['extra']`", code='root-model-extra' + ) + super().__init_subclass__(**kwargs) + + def __init__(self, /, root: RootModelRootType = PydanticUndefined, **data) -> None: # type: ignore + __tracebackhide__ = True + if data: + if root is not PydanticUndefined: + raise ValueError( + '"RootModel.__init__" accepts either a single positional argument or arbitrary keyword arguments' + ) + root = data # type: ignore + self.__pydantic_validator__.validate_python(root, self_instance=self) + + __init__.__pydantic_base_init__ = True # pyright: ignore[reportFunctionMemberAccess] + + @classmethod + def model_construct(cls, root: RootModelRootType, _fields_set: set[str] | None = None) -> Self: # type: ignore + """Create a new model using the provided root object and update fields set. + + Args: + root: The root object of the model. + _fields_set: The set of fields to be updated. + + Returns: + The new model. + + Raises: + NotImplemented: If the model is not a subclass of `RootModel`. + """ + return super().model_construct(root=root, _fields_set=_fields_set) + + def __getstate__(self) -> dict[Any, Any]: + return { + '__dict__': self.__dict__, + '__pydantic_fields_set__': self.__pydantic_fields_set__, + } + + def __setstate__(self, state: dict[Any, Any]) -> None: + _object_setattr(self, '__pydantic_fields_set__', state['__pydantic_fields_set__']) + _object_setattr(self, '__dict__', state['__dict__']) + + def __copy__(self) -> Self: + """Returns a shallow copy of the model.""" + cls = type(self) + m = cls.__new__(cls) + _object_setattr(m, '__dict__', copy(self.__dict__)) + _object_setattr(m, '__pydantic_fields_set__', copy(self.__pydantic_fields_set__)) + return m + + def __deepcopy__(self, memo: dict[int, Any] | None = None) -> Self: + """Returns a deep copy of the model.""" + cls = type(self) + m = cls.__new__(cls) + _object_setattr(m, '__dict__', deepcopy(self.__dict__, memo=memo)) + # This next line doesn't need a deepcopy because __pydantic_fields_set__ is a set[str], + # and attempting a deepcopy would be marginally slower. + _object_setattr(m, '__pydantic_fields_set__', copy(self.__pydantic_fields_set__)) + return m + + if TYPE_CHECKING: + + def model_dump( # type: ignore + self, + *, + mode: Literal['json', 'python'] | str = 'python', + include: Any = None, + exclude: Any = None, + context: dict[str, 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, + serialize_as_any: bool = False, + ) -> Any: + """This method is included just to get a more accurate return type for type checkers. + It is included in this `if TYPE_CHECKING:` block since no override is actually necessary. + + See the documentation of `BaseModel.model_dump` for more details about the arguments. + + Generally, this method will have a return type of `RootModelRootType`, assuming that `RootModelRootType` is + not a `BaseModel` subclass. If `RootModelRootType` is a `BaseModel` subclass, then the return + type will likely be `dict[str, Any]`, as `model_dump` calls are recursive. The return type could + even be something different, in the case of a custom serializer. + Thus, `Any` is used here to catch all of these cases. + """ + ... + + def __eq__(self, other: Any) -> bool: + if not isinstance(other, RootModel): + return NotImplemented + return self.__pydantic_fields__['root'].annotation == other.__pydantic_fields__[ + 'root' + ].annotation and super().__eq__(other) + + def __repr_args__(self) -> _repr.ReprArgs: + yield 'root', self.root diff --git a/.venv/lib/python3.12/site-packages/pydantic/schema.py b/.venv/lib/python3.12/site-packages/pydantic/schema.py new file mode 100644 index 0000000000000000000000000000000000000000..a3245a61afd0f59143f38ee809ee2d784d9198eb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/schema.py @@ -0,0 +1,5 @@ +"""The `schema` module is a backport module from V1.""" + +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.venv/lib/python3.12/site-packages/pydantic/tools.py b/.venv/lib/python3.12/site-packages/pydantic/tools.py new file mode 100644 index 0000000000000000000000000000000000000000..fdc68c4f4a35e8d8200b3aafcda63d43839da375 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/tools.py @@ -0,0 +1,5 @@ +"""The `tools` module is a backport module from V1.""" + +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.venv/lib/python3.12/site-packages/pydantic/type_adapter.py b/.venv/lib/python3.12/site-packages/pydantic/type_adapter.py new file mode 100644 index 0000000000000000000000000000000000000000..6f1a082eab9906b8938f35901a45ca9ca41991fb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/type_adapter.py @@ -0,0 +1,795 @@ +"""Type adapter specification.""" + +from __future__ import annotations as _annotations + +import sys +import types +from collections.abc import Callable, Iterable +from dataclasses import is_dataclass +from types import FrameType +from typing import ( + Any, + Generic, + Literal, + TypeVar, + cast, + final, + overload, +) + +from pydantic_core import CoreSchema, SchemaSerializer, SchemaValidator, Some +from typing_extensions import ParamSpec, is_typeddict + +from pydantic.errors import PydanticUserError +from pydantic.main import BaseModel, IncEx + +from ._internal import _config, _generate_schema, _mock_val_ser, _namespace_utils, _repr, _typing_extra, _utils +from .config import ConfigDict, ExtraValues +from .errors import PydanticUndefinedAnnotation +from .json_schema import ( + DEFAULT_REF_TEMPLATE, + GenerateJsonSchema, + JsonSchemaKeyT, + JsonSchemaMode, + JsonSchemaValue, +) +from .plugin._schema_validator import PluggableSchemaValidator, create_schema_validator + +T = TypeVar('T') +R = TypeVar('R') +P = ParamSpec('P') +TypeAdapterT = TypeVar('TypeAdapterT', bound='TypeAdapter') + + +def _getattr_no_parents(obj: Any, attribute: str) -> Any: + """Returns the attribute value without attempting to look up attributes from parent types.""" + if hasattr(obj, '__dict__'): + try: + return obj.__dict__[attribute] + except KeyError: + pass + + slots = getattr(obj, '__slots__', None) + if slots is not None and attribute in slots: + return getattr(obj, attribute) + else: + raise AttributeError(attribute) + + +def _type_has_config(type_: Any) -> bool: + """Returns whether the type has config.""" + type_ = _typing_extra.annotated_type(type_) or type_ + try: + return issubclass(type_, BaseModel) or is_dataclass(type_) or is_typeddict(type_) + except TypeError: + # type is not a class + return False + + +@final +class TypeAdapter(Generic[T]): + """!!! abstract "Usage Documentation" + [`TypeAdapter`](../concepts/type_adapter.md) + + Type adapters provide a flexible way to perform validation and serialization based on a Python type. + + A `TypeAdapter` instance exposes some of the functionality from `BaseModel` instance methods + for types that do not have such methods (such as dataclasses, primitive types, and more). + + **Note:** `TypeAdapter` instances are not types, and cannot be used as type annotations for fields. + + Args: + type: The type associated with the `TypeAdapter`. + config: Configuration for the `TypeAdapter`, should be a dictionary conforming to + [`ConfigDict`][pydantic.config.ConfigDict]. + + !!! note + You cannot provide a configuration when instantiating a `TypeAdapter` if the type you're using + has its own config that cannot be overridden (ex: `BaseModel`, `TypedDict`, and `dataclass`). A + [`type-adapter-config-unused`](../errors/usage_errors.md#type-adapter-config-unused) error will + be raised in this case. + _parent_depth: Depth at which to search for the [parent frame][frame-objects]. This frame is used when + resolving forward annotations during schema building, by looking for the globals and locals of this + frame. Defaults to 2, which will result in the frame where the `TypeAdapter` was instantiated. + + !!! note + This parameter is named with an underscore to suggest its private nature and discourage use. + It may be deprecated in a minor version, so we only recommend using it if you're comfortable + with potential change in behavior/support. It's default value is 2 because internally, + the `TypeAdapter` class makes another call to fetch the frame. + module: The module that passes to plugin if provided. + + Attributes: + core_schema: The core schema for the type. + validator: The schema validator for the type. + serializer: The schema serializer for the type. + pydantic_complete: Whether the core schema for the type is successfully built. + + ??? tip "Compatibility with `mypy`" + Depending on the type used, `mypy` might raise an error when instantiating a `TypeAdapter`. As a workaround, you can explicitly + annotate your variable: + + ```py + from typing import Union + + from pydantic import TypeAdapter + + ta: TypeAdapter[Union[str, int]] = TypeAdapter(Union[str, int]) # type: ignore[arg-type] + ``` + + ??? info "Namespace management nuances and implementation details" + + Here, we collect some notes on namespace management, and subtle differences from `BaseModel`: + + `BaseModel` uses its own `__module__` to find out where it was defined + and then looks for symbols to resolve forward references in those globals. + On the other hand, `TypeAdapter` can be initialized with arbitrary objects, + which may not be types and thus do not have a `__module__` available. + So instead we look at the globals in our parent stack frame. + + It is expected that the `ns_resolver` passed to this function will have the correct + namespace for the type we're adapting. See the source code for `TypeAdapter.__init__` + and `TypeAdapter.rebuild` for various ways to construct this namespace. + + This works for the case where this function is called in a module that + has the target of forward references in its scope, but + does not always work for more complex cases. + + For example, take the following: + + ```python {title="a.py"} + IntList = list[int] + OuterDict = dict[str, 'IntList'] + ``` + + ```python {test="skip" title="b.py"} + from a import OuterDict + + from pydantic import TypeAdapter + + IntList = int # replaces the symbol the forward reference is looking for + v = TypeAdapter(OuterDict) + v({'x': 1}) # should fail but doesn't + ``` + + If `OuterDict` were a `BaseModel`, this would work because it would resolve + the forward reference within the `a.py` namespace. + But `TypeAdapter(OuterDict)` can't determine what module `OuterDict` came from. + + In other words, the assumption that _all_ forward references exist in the + module we are being called from is not technically always true. + Although most of the time it is and it works fine for recursive models and such, + `BaseModel`'s behavior isn't perfect either and _can_ break in similar ways, + so there is no right or wrong between the two. + + But at the very least this behavior is _subtly_ different from `BaseModel`'s. + """ + + core_schema: CoreSchema + validator: SchemaValidator | PluggableSchemaValidator + serializer: SchemaSerializer + pydantic_complete: bool + + @overload + def __init__( + self, + type: type[T], + *, + config: ConfigDict | None = ..., + _parent_depth: int = ..., + module: str | None = ..., + ) -> None: ... + + # This second overload is for unsupported special forms (such as Annotated, Union, etc.) + # Currently there is no way to type this correctly + # See https://github.com/python/typing/pull/1618 + @overload + def __init__( + self, + type: Any, + *, + config: ConfigDict | None = ..., + _parent_depth: int = ..., + module: str | None = ..., + ) -> None: ... + + def __init__( + self, + type: Any, + *, + config: ConfigDict | None = None, + _parent_depth: int = 2, + module: str | None = None, + ) -> None: + if _type_has_config(type) and config is not None: + raise PydanticUserError( + 'Cannot use `config` when the type is a BaseModel, dataclass or TypedDict.' + ' These types can have their own config and setting the config via the `config`' + ' parameter to TypeAdapter will not override it, thus the `config` you passed to' + ' TypeAdapter becomes meaningless, which is probably not what you want.', + code='type-adapter-config-unused', + ) + + self._type = type + self._config = config + self._parent_depth = _parent_depth + self.pydantic_complete = False + + parent_frame = self._fetch_parent_frame() + if isinstance(type, types.FunctionType): + # Special case functions, which are *not* pushed to the `NsResolver` stack and without this special case + # would only have access to the parent namespace where the `TypeAdapter` was instantiated (if the function is defined + # in another module, we need to look at that module's globals). + if parent_frame is not None: + # `f_locals` is the namespace where the type adapter was instantiated (~ to `f_globals` if at the module level): + parent_ns = parent_frame.f_locals + else: # pragma: no cover + parent_ns = None + globalns, localns = _namespace_utils.ns_for_function( + type, + parent_namespace=parent_ns, + ) + parent_namespace = None + else: + if parent_frame is not None: + globalns = parent_frame.f_globals + # Do not provide a local ns if the type adapter happens to be instantiated at the module level: + localns = parent_frame.f_locals if parent_frame.f_locals is not globalns else {} + else: # pragma: no cover + globalns = {} + localns = {} + parent_namespace = localns + + self._module_name = module or cast(str, globalns.get('__name__', '')) + self._init_core_attrs( + ns_resolver=_namespace_utils.NsResolver( + namespaces_tuple=_namespace_utils.NamespacesTuple(locals=localns, globals=globalns), + parent_namespace=parent_namespace, + ), + force=False, + ) + + def _fetch_parent_frame(self) -> FrameType | None: + frame = sys._getframe(self._parent_depth) + if frame.f_globals.get('__name__') == 'typing': + # Because `TypeAdapter` is generic, explicitly parametrizing the class results + # in a `typing._GenericAlias` instance, which proxies instantiation calls to the + # "real" `TypeAdapter` class and thus adding an extra frame to the call. To avoid + # pulling anything from the `typing` module, use the correct frame (the one before): + return frame.f_back + + return frame + + def _init_core_attrs( + self, ns_resolver: _namespace_utils.NsResolver, force: bool, raise_errors: bool = False + ) -> bool: + """Initialize the core schema, validator, and serializer for the type. + + Args: + ns_resolver: The namespace resolver to use when building the core schema for the adapted type. + force: Whether to force the construction of the core schema, validator, and serializer. + If `force` is set to `False` and `_defer_build` is `True`, the core schema, validator, and serializer will be set to mocks. + raise_errors: Whether to raise errors if initializing any of the core attrs fails. + + Returns: + `True` if the core schema, validator, and serializer were successfully initialized, otherwise `False`. + + Raises: + PydanticUndefinedAnnotation: If `PydanticUndefinedAnnotation` occurs in`__get_pydantic_core_schema__` + and `raise_errors=True`. + """ + if not force and self._defer_build: + _mock_val_ser.set_type_adapter_mocks(self) + self.pydantic_complete = False + return False + + try: + self.core_schema = _getattr_no_parents(self._type, '__pydantic_core_schema__') + self.validator = _getattr_no_parents(self._type, '__pydantic_validator__') + self.serializer = _getattr_no_parents(self._type, '__pydantic_serializer__') + + # TODO: we don't go through the rebuild logic here directly because we don't want + # to repeat all of the namespace fetching logic that we've already done + # so we simply skip to the block below that does the actual schema generation + if ( + isinstance(self.core_schema, _mock_val_ser.MockCoreSchema) + or isinstance(self.validator, _mock_val_ser.MockValSer) + or isinstance(self.serializer, _mock_val_ser.MockValSer) + ): + raise AttributeError() + except AttributeError: + config_wrapper = _config.ConfigWrapper(self._config) + + schema_generator = _generate_schema.GenerateSchema(config_wrapper, ns_resolver=ns_resolver) + + try: + core_schema = schema_generator.generate_schema(self._type) + except PydanticUndefinedAnnotation: + if raise_errors: + raise + _mock_val_ser.set_type_adapter_mocks(self) + return False + + try: + self.core_schema = schema_generator.clean_schema(core_schema) + except _generate_schema.InvalidSchemaError: + _mock_val_ser.set_type_adapter_mocks(self) + return False + + core_config = config_wrapper.core_config(None) + + self.validator = create_schema_validator( + schema=self.core_schema, + schema_type=self._type, + schema_type_module=self._module_name, + schema_type_name=str(self._type), + schema_kind='TypeAdapter', + config=core_config, + plugin_settings=config_wrapper.plugin_settings, + ) + self.serializer = SchemaSerializer(self.core_schema, core_config) + + self.pydantic_complete = True + return True + + @property + def _defer_build(self) -> bool: + config = self._config if self._config is not None else self._model_config + if config: + return config.get('defer_build') is True + return False + + @property + def _model_config(self) -> ConfigDict | None: + type_: Any = _typing_extra.annotated_type(self._type) or self._type # Eg FastAPI heavily uses Annotated + if _utils.lenient_issubclass(type_, BaseModel): + return type_.model_config + return getattr(type_, '__pydantic_config__', None) + + def __repr__(self) -> str: + return f'TypeAdapter({_repr.display_as_type(self._type)})' + + def rebuild( + self, + *, + force: bool = False, + raise_errors: bool = True, + _parent_namespace_depth: int = 2, + _types_namespace: _namespace_utils.MappingNamespace | None = None, + ) -> bool | None: + """Try to rebuild the pydantic-core schema for the adapter's type. + + This may be necessary when one of the annotations is a ForwardRef which could not be resolved during + the initial attempt to build the schema, and automatic rebuilding fails. + + Args: + force: Whether to force the rebuilding of the type adapter's schema, defaults to `False`. + raise_errors: Whether to raise errors, defaults to `True`. + _parent_namespace_depth: Depth at which to search for the [parent frame][frame-objects]. This + frame is used when resolving forward annotations during schema rebuilding, by looking for + the locals of this frame. Defaults to 2, which will result in the frame where the method + was called. + _types_namespace: An explicit types namespace to use, instead of using the local namespace + from the parent frame. Defaults to `None`. + + Returns: + Returns `None` if the schema is already "complete" and rebuilding was not required. + If rebuilding _was_ required, returns `True` if rebuilding was successful, otherwise `False`. + """ + if not force and self.pydantic_complete: + return None + + if _types_namespace is not None: + rebuild_ns = _types_namespace + elif _parent_namespace_depth > 0: + rebuild_ns = _typing_extra.parent_frame_namespace(parent_depth=_parent_namespace_depth, force=True) or {} + else: + rebuild_ns = {} + + # we have to manually fetch globals here because there's no type on the stack of the NsResolver + # and so we skip the globalns = get_module_ns_of(typ) call that would normally happen + globalns = sys._getframe(max(_parent_namespace_depth - 1, 1)).f_globals + ns_resolver = _namespace_utils.NsResolver( + namespaces_tuple=_namespace_utils.NamespacesTuple(locals=rebuild_ns, globals=globalns), + parent_namespace=rebuild_ns, + ) + return self._init_core_attrs(ns_resolver=ns_resolver, force=True, raise_errors=raise_errors) + + def validate_python( + self, + object: Any, + /, + *, + strict: bool | None = None, + extra: ExtraValues | None = None, + from_attributes: bool | None = None, + context: Any | None = None, + experimental_allow_partial: bool | Literal['off', 'on', 'trailing-strings'] = False, + by_alias: bool | None = None, + by_name: bool | None = None, + ) -> T: + """Validate a Python object against the model. + + Args: + object: The Python object to validate against the model. + strict: Whether to strictly check types. + extra: Whether to ignore, allow, or forbid extra data during model validation. + See the [`extra` configuration value][pydantic.ConfigDict.extra] for details. + from_attributes: Whether to extract data from object attributes. + context: Additional context to pass to the validator. + experimental_allow_partial: **Experimental** whether to enable + [partial validation](../concepts/experimental.md#partial-validation), e.g. to process streams. + * False / 'off': Default behavior, no partial validation. + * True / 'on': Enable partial validation. + * 'trailing-strings': Enable partial validation and allow trailing strings in the input. + by_alias: Whether to use the field's alias when validating against the provided input data. + by_name: Whether to use the field's name when validating against the provided input data. + + !!! note + When using `TypeAdapter` with a Pydantic `dataclass`, the use of the `from_attributes` + argument is not supported. + + Returns: + The validated object. + """ + if by_alias is False and by_name is not True: + raise PydanticUserError( + 'At least one of `by_alias` or `by_name` must be set to True.', + code='validate-by-alias-and-name-false', + ) + + return self.validator.validate_python( + object, + strict=strict, + extra=extra, + from_attributes=from_attributes, + context=context, + allow_partial=experimental_allow_partial, + by_alias=by_alias, + by_name=by_name, + ) + + def validate_json( + self, + data: str | bytes | bytearray, + /, + *, + strict: bool | None = None, + extra: ExtraValues | None = None, + context: Any | None = None, + experimental_allow_partial: bool | Literal['off', 'on', 'trailing-strings'] = False, + by_alias: bool | None = None, + by_name: bool | None = None, + ) -> T: + """!!! abstract "Usage Documentation" + [JSON Parsing](../concepts/json.md#json-parsing) + + Validate a JSON string or bytes against the model. + + Args: + data: The JSON data to validate against the model. + strict: Whether to strictly check types. + extra: Whether to ignore, allow, or forbid extra data during model validation. + See the [`extra` configuration value][pydantic.ConfigDict.extra] for details. + context: Additional context to use during validation. + experimental_allow_partial: **Experimental** whether to enable + [partial validation](../concepts/experimental.md#partial-validation), e.g. to process streams. + * False / 'off': Default behavior, no partial validation. + * True / 'on': Enable partial validation. + * 'trailing-strings': Enable partial validation and allow trailing strings in the input. + by_alias: Whether to use the field's alias when validating against the provided input data. + by_name: Whether to use the field's name when validating against the provided input data. + + Returns: + The validated object. + """ + if by_alias is False and by_name is not True: + raise PydanticUserError( + 'At least one of `by_alias` or `by_name` must be set to True.', + code='validate-by-alias-and-name-false', + ) + + return self.validator.validate_json( + data, + strict=strict, + extra=extra, + context=context, + allow_partial=experimental_allow_partial, + by_alias=by_alias, + by_name=by_name, + ) + + def validate_strings( + self, + obj: Any, + /, + *, + strict: bool | None = None, + extra: ExtraValues | None = None, + context: Any | None = None, + experimental_allow_partial: bool | Literal['off', 'on', 'trailing-strings'] = False, + by_alias: bool | None = None, + by_name: bool | None = None, + ) -> T: + """Validate object contains string data against the model. + + Args: + obj: The object contains string data to validate. + strict: Whether to strictly check types. + extra: Whether to ignore, allow, or forbid extra data during model validation. + See the [`extra` configuration value][pydantic.ConfigDict.extra] for details. + context: Additional context to use during validation. + experimental_allow_partial: **Experimental** whether to enable + [partial validation](../concepts/experimental.md#partial-validation), e.g. to process streams. + * False / 'off': Default behavior, no partial validation. + * True / 'on': Enable partial validation. + * 'trailing-strings': Enable partial validation and allow trailing strings in the input. + by_alias: Whether to use the field's alias when validating against the provided input data. + by_name: Whether to use the field's name when validating against the provided input data. + + Returns: + The validated object. + """ + if by_alias is False and by_name is not True: + raise PydanticUserError( + 'At least one of `by_alias` or `by_name` must be set to True.', + code='validate-by-alias-and-name-false', + ) + + return self.validator.validate_strings( + obj, + strict=strict, + extra=extra, + context=context, + allow_partial=experimental_allow_partial, + by_alias=by_alias, + by_name=by_name, + ) + + def get_default_value(self, *, strict: bool | None = None, context: Any | None = None) -> Some[T] | None: + """Get the default value for the wrapped type. + + Args: + strict: Whether to strictly check types. + context: Additional context to pass to the validator. + + Returns: + The default value wrapped in a `Some` if there is one or None if not. + """ + return self.validator.get_default_value(strict=strict, context=context) + + def dump_python( + self, + instance: T, + /, + *, + mode: Literal['json', 'python'] = 'python', + include: IncEx | None = None, + exclude: IncEx | 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, + context: Any | None = None, + ) -> Any: + """Dump an instance of the adapted type to a Python object. + + Args: + instance: The Python object to serialize. + mode: The output format. + include: Fields to include in the output. + exclude: Fields to exclude from the output. + by_alias: Whether to use alias names for field names. + exclude_unset: Whether to exclude unset fields. + exclude_defaults: Whether to exclude fields with default values. + exclude_none: Whether to exclude fields with None values. + 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: Whether to output the serialized data in a way that is compatible with deserialization. + 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. + context: Additional context to pass to the serializer. + + Returns: + The serialized object. + """ + return self.serializer.to_python( + instance, + mode=mode, + by_alias=by_alias, + include=include, + exclude=exclude, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + exclude_computed_fields=exclude_computed_fields, + round_trip=round_trip, + warnings=warnings, + fallback=fallback, + serialize_as_any=serialize_as_any, + context=context, + ) + + def dump_json( + self, + instance: T, + /, + *, + indent: int | None = None, + ensure_ascii: bool = False, + include: IncEx | None = None, + exclude: IncEx | 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, + context: Any | None = None, + ) -> bytes: + """!!! abstract "Usage Documentation" + [JSON Serialization](../concepts/json.md#json-serialization) + + Serialize an instance of the adapted type to JSON. + + Args: + instance: The instance to be serialized. + indent: Number of spaces for JSON indentation. + ensure_ascii: If `True`, the output is guaranteed to have all incoming non-ASCII characters escaped. + If `False` (the default), these characters will be output as-is. + include: Fields to include. + exclude: Fields to exclude. + by_alias: Whether to use alias names for field names. + exclude_unset: Whether to exclude unset fields. + exclude_defaults: Whether to exclude fields with default values. + exclude_none: Whether to exclude fields with 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: Whether to serialize and deserialize the instance to ensure round-tripping. + 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. + context: Additional context to pass to the serializer. + + Returns: + The JSON representation of the given instance as bytes. + """ + return self.serializer.to_json( + instance, + indent=indent, + ensure_ascii=ensure_ascii, + include=include, + exclude=exclude, + by_alias=by_alias, + exclude_unset=exclude_unset, + exclude_defaults=exclude_defaults, + exclude_none=exclude_none, + exclude_computed_fields=exclude_computed_fields, + round_trip=round_trip, + warnings=warnings, + fallback=fallback, + serialize_as_any=serialize_as_any, + context=context, + ) + + def json_schema( + self, + *, + by_alias: bool = True, + ref_template: str = DEFAULT_REF_TEMPLATE, + union_format: Literal['any_of', 'primitive_type_array'] = 'any_of', + schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema, + mode: JsonSchemaMode = 'validation', + ) -> dict[str, Any]: + """Generate a JSON schema for the adapted type. + + Args: + by_alias: Whether to use alias names for field names. + ref_template: The format string used for generating $ref strings. + union_format: The format to use when combining schemas from unions together. Can be one of: + + - `'any_of'`: Use the [`anyOf`](https://json-schema.org/understanding-json-schema/reference/combining#anyOf) + keyword to combine schemas (the default). + - `'primitive_type_array'`: Use the [`type`](https://json-schema.org/understanding-json-schema/reference/type) + keyword as an array of strings, containing each type of the combination. If any of the schemas is not a primitive + type (`string`, `boolean`, `null`, `integer` or `number`) or contains constraints/metadata, falls back to + `any_of`. + schema_generator: To override the logic used to generate the JSON schema, as a subclass of + `GenerateJsonSchema` with your desired modifications + mode: The mode in which to generate the schema. + schema_generator: The generator class used for creating the schema. + mode: The mode to use for schema generation. + + Returns: + The JSON schema for the model as a dictionary. + """ + schema_generator_instance = schema_generator( + by_alias=by_alias, ref_template=ref_template, union_format=union_format + ) + if isinstance(self.core_schema, _mock_val_ser.MockCoreSchema): + self.core_schema.rebuild() + assert not isinstance(self.core_schema, _mock_val_ser.MockCoreSchema), 'this is a bug! please report it' + return schema_generator_instance.generate(self.core_schema, mode=mode) + + @staticmethod + def json_schemas( + inputs: Iterable[tuple[JsonSchemaKeyT, JsonSchemaMode, TypeAdapter[Any]]], + /, + *, + by_alias: bool = True, + title: str | None = None, + description: str | None = None, + ref_template: str = DEFAULT_REF_TEMPLATE, + union_format: Literal['any_of', 'primitive_type_array'] = 'any_of', + schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema, + ) -> tuple[dict[tuple[JsonSchemaKeyT, JsonSchemaMode], JsonSchemaValue], JsonSchemaValue]: + """Generate a JSON schema including definitions from multiple type adapters. + + Args: + inputs: Inputs to schema generation. The first two items will form the keys of the (first) + output mapping; the type adapters will provide the core schemas that get converted into + definitions in the output JSON schema. + by_alias: Whether to use alias names. + title: The title for the schema. + description: The description for the schema. + ref_template: The format string used for generating $ref strings. + union_format: The format to use when combining schemas from unions together. Can be one of: + + - `'any_of'`: Use the [`anyOf`](https://json-schema.org/understanding-json-schema/reference/combining#anyOf) + keyword to combine schemas (the default). + - `'primitive_type_array'`: Use the [`type`](https://json-schema.org/understanding-json-schema/reference/type) + keyword as an array of strings, containing each type of the combination. If any of the schemas is not a primitive + type (`string`, `boolean`, `null`, `integer` or `number`) or contains constraints/metadata, falls back to + `any_of`. + schema_generator: The generator class used for creating the schema. + + Returns: + A tuple where: + + - The first element is a dictionary whose keys are tuples of JSON schema key type and JSON mode, and + whose values are the JSON schema corresponding to that pair of inputs. (These schemas may have + JsonRef references to definitions that are defined in the second returned element.) + - The second element is a JSON schema containing all definitions referenced in the first returned + element, along with the optional title and description keys. + + """ + schema_generator_instance = schema_generator( + by_alias=by_alias, ref_template=ref_template, union_format=union_format + ) + + inputs_ = [] + for key, mode, adapter in inputs: + # This is the same pattern we follow for model json schemas - we attempt a core schema rebuild if we detect a mock + if isinstance(adapter.core_schema, _mock_val_ser.MockCoreSchema): + adapter.core_schema.rebuild() + assert not isinstance(adapter.core_schema, _mock_val_ser.MockCoreSchema), ( + 'this is a bug! please report it' + ) + inputs_.append((key, mode, adapter.core_schema)) + + json_schemas_map, definitions = schema_generator_instance.generate_definitions(inputs_) + + json_schema: dict[str, Any] = {} + if definitions: + json_schema['$defs'] = definitions + if title: + json_schema['title'] = title + if description: + json_schema['description'] = description + + return json_schemas_map, json_schema diff --git a/.venv/lib/python3.12/site-packages/pydantic/types.py b/.venv/lib/python3.12/site-packages/pydantic/types.py new file mode 100644 index 0000000000000000000000000000000000000000..59160ab72b54d1f100fbc5da0a85f3fe7e4b7190 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/types.py @@ -0,0 +1,3295 @@ +"""The types module contains custom types used by pydantic.""" + +from __future__ import annotations as _annotations + +import base64 +import dataclasses as _dataclasses +import re +from collections.abc import Hashable, Iterator +from datetime import date, datetime +from decimal import Decimal +from enum import Enum +from pathlib import Path +from re import Pattern +from types import ModuleType +from typing import ( + TYPE_CHECKING, + Annotated, + Any, + Callable, + ClassVar, + Generic, + Literal, + TypeVar, + Union, + cast, +) +from uuid import UUID + +import annotated_types +from annotated_types import BaseMetadata, MaxLen, MinLen +from pydantic_core import CoreSchema, PydanticCustomError, SchemaSerializer, core_schema +from typing_extensions import Protocol, TypeAlias, TypeAliasType, deprecated, get_args, get_origin +from typing_inspection.introspection import is_union_origin + +from ._internal import _fields, _internal_dataclass, _utils, _validators +from ._migration import getattr_migration +from .annotated_handlers import GetCoreSchemaHandler, GetJsonSchemaHandler +from .errors import PydanticUserError +from .json_schema import JsonSchemaValue +from .warnings import PydanticDeprecatedSince20 + +if TYPE_CHECKING: + from ._internal._core_metadata import CoreMetadata + +__all__ = ( + 'Strict', + 'StrictStr', + 'SocketPath', + 'conbytes', + 'conlist', + 'conset', + 'confrozenset', + 'constr', + 'ImportString', + 'conint', + 'PositiveInt', + 'NegativeInt', + 'NonNegativeInt', + 'NonPositiveInt', + 'confloat', + 'PositiveFloat', + 'NegativeFloat', + 'NonNegativeFloat', + 'NonPositiveFloat', + 'FiniteFloat', + 'condecimal', + 'UUID1', + 'UUID3', + 'UUID4', + 'UUID5', + 'UUID6', + 'UUID7', + 'UUID8', + 'FilePath', + 'DirectoryPath', + 'NewPath', + 'Json', + 'Secret', + 'SecretStr', + 'SecretBytes', + 'StrictBool', + 'StrictBytes', + 'StrictInt', + 'StrictFloat', + 'PaymentCardNumber', + 'ByteSize', + 'PastDate', + 'FutureDate', + 'PastDatetime', + 'FutureDatetime', + 'condate', + 'AwareDatetime', + 'NaiveDatetime', + 'AllowInfNan', + 'EncoderProtocol', + 'EncodedBytes', + 'EncodedStr', + 'Base64Encoder', + 'Base64Bytes', + 'Base64Str', + 'Base64UrlBytes', + 'Base64UrlStr', + 'GetPydanticSchema', + 'StringConstraints', + 'Tag', + 'Discriminator', + 'JsonValue', + 'OnErrorOmit', + 'FailFast', +) + + +T = TypeVar('T') + + +@_dataclasses.dataclass +class Strict(_fields.PydanticMetadata, BaseMetadata): + """!!! abstract "Usage Documentation" + [Strict Mode with `Annotated` `Strict`](../concepts/strict_mode.md#strict-mode-with-annotated-strict) + + A field metadata class to indicate that a field should be validated in strict mode. + Use this class as an annotation via [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated), as seen below. + + Attributes: + strict: Whether to validate the field in strict mode. + + Example: + ```python + from typing import Annotated + + from pydantic.types import Strict + + StrictBool = Annotated[bool, Strict()] + ``` + """ + + strict: bool = True + + def __hash__(self) -> int: + return hash(self.strict) + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BOOLEAN TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +StrictBool = Annotated[bool, Strict()] +"""A boolean that must be either ``True`` or ``False``.""" + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ INTEGER TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +def conint( + *, + strict: bool | None = None, + gt: int | None = None, + ge: int | None = None, + lt: int | None = None, + le: int | None = None, + multiple_of: int | None = None, +) -> type[int]: + """ + !!! warning "Discouraged" + This function is **discouraged** in favor of using + [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated) with + [`Field`][pydantic.fields.Field] instead. + + This function will be **deprecated** in Pydantic 3.0. + + The reason is that `conint` returns a type, which doesn't play well with static analysis tools. + + === ":x: Don't do this" + ```python + from pydantic import BaseModel, conint + + class Foo(BaseModel): + bar: conint(strict=True, gt=0) + ``` + + === ":white_check_mark: Do this" + ```python + from typing import Annotated + + from pydantic import BaseModel, Field + + class Foo(BaseModel): + bar: Annotated[int, Field(strict=True, gt=0)] + ``` + + A wrapper around `int` that allows for additional constraints. + + Args: + strict: Whether to validate the integer in strict mode. Defaults to `None`. + gt: The value must be greater than this. + ge: The value must be greater than or equal to this. + lt: The value must be less than this. + le: The value must be less than or equal to this. + multiple_of: The value must be a multiple of this. + + Returns: + The wrapped integer type. + + ```python + from pydantic import BaseModel, ValidationError, conint + + class ConstrainedExample(BaseModel): + constrained_int: conint(gt=1) + + m = ConstrainedExample(constrained_int=2) + print(repr(m)) + #> ConstrainedExample(constrained_int=2) + + try: + ConstrainedExample(constrained_int=0) + except ValidationError as e: + print(e.errors()) + ''' + [ + { + 'type': 'greater_than', + 'loc': ('constrained_int',), + 'msg': 'Input should be greater than 1', + 'input': 0, + 'ctx': {'gt': 1}, + 'url': 'https://errors.pydantic.dev/2/v/greater_than', + } + ] + ''' + ``` + + """ # noqa: D212 + return Annotated[ # pyright: ignore[reportReturnType] + int, + Strict(strict) if strict is not None else None, + annotated_types.Interval(gt=gt, ge=ge, lt=lt, le=le), + annotated_types.MultipleOf(multiple_of) if multiple_of is not None else None, + ] + + +PositiveInt = Annotated[int, annotated_types.Gt(0)] +"""An integer that must be greater than zero. + +```python +from pydantic import BaseModel, PositiveInt, ValidationError + +class Model(BaseModel): + positive_int: PositiveInt + +m = Model(positive_int=1) +print(repr(m)) +#> Model(positive_int=1) + +try: + Model(positive_int=-1) +except ValidationError as e: + print(e.errors()) + ''' + [ + { + 'type': 'greater_than', + 'loc': ('positive_int',), + 'msg': 'Input should be greater than 0', + 'input': -1, + 'ctx': {'gt': 0}, + 'url': 'https://errors.pydantic.dev/2/v/greater_than', + } + ] + ''' +``` +""" +NegativeInt = Annotated[int, annotated_types.Lt(0)] +"""An integer that must be less than zero. + +```python +from pydantic import BaseModel, NegativeInt, ValidationError + +class Model(BaseModel): + negative_int: NegativeInt + +m = Model(negative_int=-1) +print(repr(m)) +#> Model(negative_int=-1) + +try: + Model(negative_int=1) +except ValidationError as e: + print(e.errors()) + ''' + [ + { + 'type': 'less_than', + 'loc': ('negative_int',), + 'msg': 'Input should be less than 0', + 'input': 1, + 'ctx': {'lt': 0}, + 'url': 'https://errors.pydantic.dev/2/v/less_than', + } + ] + ''' +``` +""" +NonPositiveInt = Annotated[int, annotated_types.Le(0)] +"""An integer that must be less than or equal to zero. + +```python +from pydantic import BaseModel, NonPositiveInt, ValidationError + +class Model(BaseModel): + non_positive_int: NonPositiveInt + +m = Model(non_positive_int=0) +print(repr(m)) +#> Model(non_positive_int=0) + +try: + Model(non_positive_int=1) +except ValidationError as e: + print(e.errors()) + ''' + [ + { + 'type': 'less_than_equal', + 'loc': ('non_positive_int',), + 'msg': 'Input should be less than or equal to 0', + 'input': 1, + 'ctx': {'le': 0}, + 'url': 'https://errors.pydantic.dev/2/v/less_than_equal', + } + ] + ''' +``` +""" +NonNegativeInt = Annotated[int, annotated_types.Ge(0)] +"""An integer that must be greater than or equal to zero. + +```python +from pydantic import BaseModel, NonNegativeInt, ValidationError + +class Model(BaseModel): + non_negative_int: NonNegativeInt + +m = Model(non_negative_int=0) +print(repr(m)) +#> Model(non_negative_int=0) + +try: + Model(non_negative_int=-1) +except ValidationError as e: + print(e.errors()) + ''' + [ + { + 'type': 'greater_than_equal', + 'loc': ('non_negative_int',), + 'msg': 'Input should be greater than or equal to 0', + 'input': -1, + 'ctx': {'ge': 0}, + 'url': 'https://errors.pydantic.dev/2/v/greater_than_equal', + } + ] + ''' +``` +""" +StrictInt = Annotated[int, Strict()] +"""An integer that must be validated in strict mode. + +```python +from pydantic import BaseModel, StrictInt, ValidationError + +class StrictIntModel(BaseModel): + strict_int: StrictInt + +try: + StrictIntModel(strict_int=3.14159) +except ValidationError as e: + print(e) + ''' + 1 validation error for StrictIntModel + strict_int + Input should be a valid integer [type=int_type, input_value=3.14159, input_type=float] + ''' +``` +""" + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ FLOAT TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +@_dataclasses.dataclass +class AllowInfNan(_fields.PydanticMetadata): + """A field metadata class to indicate that a field should allow `-inf`, `inf`, and `nan`. + + Use this class as an annotation via [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated), as seen below. + + Attributes: + allow_inf_nan: Whether to allow `-inf`, `inf`, and `nan`. Defaults to `True`. + + Example: + ```python + from typing import Annotated + + from pydantic.types import AllowInfNan + + LaxFloat = Annotated[float, AllowInfNan()] + ``` + """ + + allow_inf_nan: bool = True + + def __hash__(self) -> int: + return hash(self.allow_inf_nan) + + +def confloat( + *, + strict: bool | None = None, + gt: float | None = None, + ge: float | None = None, + lt: float | None = None, + le: float | None = None, + multiple_of: float | None = None, + allow_inf_nan: bool | None = None, +) -> type[float]: + """ + !!! warning "Discouraged" + This function is **discouraged** in favor of using + [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated) with + [`Field`][pydantic.fields.Field] instead. + + This function will be **deprecated** in Pydantic 3.0. + + The reason is that `confloat` returns a type, which doesn't play well with static analysis tools. + + === ":x: Don't do this" + ```python + from pydantic import BaseModel, confloat + + class Foo(BaseModel): + bar: confloat(strict=True, gt=0) + ``` + + === ":white_check_mark: Do this" + ```python + from typing import Annotated + + from pydantic import BaseModel, Field + + class Foo(BaseModel): + bar: Annotated[float, Field(strict=True, gt=0)] + ``` + + A wrapper around `float` that allows for additional constraints. + + Args: + strict: Whether to validate the float in strict mode. + gt: The value must be greater than this. + ge: The value must be greater than or equal to this. + lt: The value must be less than this. + le: The value must be less than or equal to this. + multiple_of: The value must be a multiple of this. + allow_inf_nan: Whether to allow `-inf`, `inf`, and `nan`. + + Returns: + The wrapped float type. + + ```python + from pydantic import BaseModel, ValidationError, confloat + + class ConstrainedExample(BaseModel): + constrained_float: confloat(gt=1.0) + + m = ConstrainedExample(constrained_float=1.1) + print(repr(m)) + #> ConstrainedExample(constrained_float=1.1) + + try: + ConstrainedExample(constrained_float=0.9) + except ValidationError as e: + print(e.errors()) + ''' + [ + { + 'type': 'greater_than', + 'loc': ('constrained_float',), + 'msg': 'Input should be greater than 1', + 'input': 0.9, + 'ctx': {'gt': 1.0}, + 'url': 'https://errors.pydantic.dev/2/v/greater_than', + } + ] + ''' + ``` + """ # noqa: D212 + return Annotated[ # pyright: ignore[reportReturnType] + float, + Strict(strict) if strict is not None else None, + annotated_types.Interval(gt=gt, ge=ge, lt=lt, le=le), + annotated_types.MultipleOf(multiple_of) if multiple_of is not None else None, + AllowInfNan(allow_inf_nan) if allow_inf_nan is not None else None, + ] + + +PositiveFloat = Annotated[float, annotated_types.Gt(0)] +"""A float that must be greater than zero. + +```python +from pydantic import BaseModel, PositiveFloat, ValidationError + +class Model(BaseModel): + positive_float: PositiveFloat + +m = Model(positive_float=1.0) +print(repr(m)) +#> Model(positive_float=1.0) + +try: + Model(positive_float=-1.0) +except ValidationError as e: + print(e.errors()) + ''' + [ + { + 'type': 'greater_than', + 'loc': ('positive_float',), + 'msg': 'Input should be greater than 0', + 'input': -1.0, + 'ctx': {'gt': 0.0}, + 'url': 'https://errors.pydantic.dev/2/v/greater_than', + } + ] + ''' +``` +""" +NegativeFloat = Annotated[float, annotated_types.Lt(0)] +"""A float that must be less than zero. + +```python +from pydantic import BaseModel, NegativeFloat, ValidationError + +class Model(BaseModel): + negative_float: NegativeFloat + +m = Model(negative_float=-1.0) +print(repr(m)) +#> Model(negative_float=-1.0) + +try: + Model(negative_float=1.0) +except ValidationError as e: + print(e.errors()) + ''' + [ + { + 'type': 'less_than', + 'loc': ('negative_float',), + 'msg': 'Input should be less than 0', + 'input': 1.0, + 'ctx': {'lt': 0.0}, + 'url': 'https://errors.pydantic.dev/2/v/less_than', + } + ] + ''' +``` +""" +NonPositiveFloat = Annotated[float, annotated_types.Le(0)] +"""A float that must be less than or equal to zero. + +```python +from pydantic import BaseModel, NonPositiveFloat, ValidationError + +class Model(BaseModel): + non_positive_float: NonPositiveFloat + +m = Model(non_positive_float=0.0) +print(repr(m)) +#> Model(non_positive_float=0.0) + +try: + Model(non_positive_float=1.0) +except ValidationError as e: + print(e.errors()) + ''' + [ + { + 'type': 'less_than_equal', + 'loc': ('non_positive_float',), + 'msg': 'Input should be less than or equal to 0', + 'input': 1.0, + 'ctx': {'le': 0.0}, + 'url': 'https://errors.pydantic.dev/2/v/less_than_equal', + } + ] + ''' +``` +""" +NonNegativeFloat = Annotated[float, annotated_types.Ge(0)] +"""A float that must be greater than or equal to zero. + +```python +from pydantic import BaseModel, NonNegativeFloat, ValidationError + +class Model(BaseModel): + non_negative_float: NonNegativeFloat + +m = Model(non_negative_float=0.0) +print(repr(m)) +#> Model(non_negative_float=0.0) + +try: + Model(non_negative_float=-1.0) +except ValidationError as e: + print(e.errors()) + ''' + [ + { + 'type': 'greater_than_equal', + 'loc': ('non_negative_float',), + 'msg': 'Input should be greater than or equal to 0', + 'input': -1.0, + 'ctx': {'ge': 0.0}, + 'url': 'https://errors.pydantic.dev/2/v/greater_than_equal', + } + ] + ''' +``` +""" +StrictFloat = Annotated[float, Strict(True)] +"""A float that must be validated in strict mode. + +```python +from pydantic import BaseModel, StrictFloat, ValidationError + +class StrictFloatModel(BaseModel): + strict_float: StrictFloat + +try: + StrictFloatModel(strict_float='1.0') +except ValidationError as e: + print(e) + ''' + 1 validation error for StrictFloatModel + strict_float + Input should be a valid number [type=float_type, input_value='1.0', input_type=str] + ''' +``` +""" +FiniteFloat = Annotated[float, AllowInfNan(False)] +"""A float that must be finite (not ``-inf``, ``inf``, or ``nan``). + +```python +from pydantic import BaseModel, FiniteFloat + +class Model(BaseModel): + finite: FiniteFloat + +m = Model(finite=1.0) +print(m) +#> finite=1.0 +``` +""" + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BYTES TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +def conbytes( + *, + min_length: int | None = None, + max_length: int | None = None, + strict: bool | None = None, +) -> type[bytes]: + """A wrapper around `bytes` that allows for additional constraints. + + Args: + min_length: The minimum length of the bytes. + max_length: The maximum length of the bytes. + strict: Whether to validate the bytes in strict mode. + + Returns: + The wrapped bytes type. + """ + return Annotated[ # pyright: ignore[reportReturnType] + bytes, + Strict(strict) if strict is not None else None, + annotated_types.Len(min_length or 0, max_length), + ] + + +StrictBytes = Annotated[bytes, Strict()] +"""A bytes that must be validated in strict mode.""" + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ STRING TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +@_dataclasses.dataclass(frozen=True) +class StringConstraints(annotated_types.GroupedMetadata): + """!!! abstract "Usage Documentation" + [String types](./standard_library_types.md#strings) + + A field metadata class to apply constraints to `str` types. + Use this class as an annotation via [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated), as seen below. + + Attributes: + strip_whitespace: Whether to remove leading and trailing whitespace. + to_upper: Whether to convert the string to uppercase. + to_lower: Whether to convert the string to lowercase. + strict: Whether to validate the string in strict mode. + min_length: The minimum length of the string. + max_length: The maximum length of the string. + pattern: A regex pattern that the string must match. + + Example: + ```python + from typing import Annotated + + from pydantic.types import StringConstraints + + ConstrainedStr = Annotated[str, StringConstraints(min_length=1, max_length=10)] + ``` + """ + + strip_whitespace: bool | None = None + to_upper: bool | None = None + to_lower: bool | None = None + strict: bool | None = None + min_length: int | None = None + max_length: int | None = None + pattern: str | Pattern[str] | None = None + + def __iter__(self) -> Iterator[BaseMetadata]: + if self.min_length is not None: + yield MinLen(self.min_length) + if self.max_length is not None: + yield MaxLen(self.max_length) + if self.strict is not None: + yield Strict(self.strict) + if ( + self.strip_whitespace is not None + or self.pattern is not None + or self.to_lower is not None + or self.to_upper is not None + ): + yield _fields.pydantic_general_metadata( + strip_whitespace=self.strip_whitespace, + to_upper=self.to_upper, + to_lower=self.to_lower, + pattern=self.pattern, + ) + + +def constr( + *, + strip_whitespace: bool | None = None, + to_upper: bool | None = None, + to_lower: bool | None = None, + strict: bool | None = None, + min_length: int | None = None, + max_length: int | None = None, + pattern: str | Pattern[str] | None = None, +) -> type[str]: + """ + !!! warning "Discouraged" + This function is **discouraged** in favor of using + [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated) with + [`StringConstraints`][pydantic.types.StringConstraints] instead. + + This function will be **deprecated** in Pydantic 3.0. + + The reason is that `constr` returns a type, which doesn't play well with static analysis tools. + + === ":x: Don't do this" + ```python + from pydantic import BaseModel, constr + + class Foo(BaseModel): + bar: constr(strip_whitespace=True, to_upper=True, pattern=r'^[A-Z]+$') + ``` + + === ":white_check_mark: Do this" + ```python + from typing import Annotated + + from pydantic import BaseModel, StringConstraints + + class Foo(BaseModel): + bar: Annotated[ + str, + StringConstraints( + strip_whitespace=True, to_upper=True, pattern=r'^[A-Z]+$' + ), + ] + ``` + + A wrapper around `str` that allows for additional constraints. + + ```python + from pydantic import BaseModel, constr + + class Foo(BaseModel): + bar: constr(strip_whitespace=True, to_upper=True) + + foo = Foo(bar=' hello ') + print(foo) + #> bar='HELLO' + ``` + + Args: + strip_whitespace: Whether to remove leading and trailing whitespace. + to_upper: Whether to turn all characters to uppercase. + to_lower: Whether to turn all characters to lowercase. + strict: Whether to validate the string in strict mode. + min_length: The minimum length of the string. + max_length: The maximum length of the string. + pattern: A regex pattern to validate the string against. + + Returns: + The wrapped string type. + """ # noqa: D212 + return Annotated[ # pyright: ignore[reportReturnType] + str, + StringConstraints( + strip_whitespace=strip_whitespace, + to_upper=to_upper, + to_lower=to_lower, + strict=strict, + min_length=min_length, + max_length=max_length, + pattern=pattern, + ), + ] + + +StrictStr = Annotated[str, Strict()] +"""A string that must be validated in strict mode.""" + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~ COLLECTION TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +HashableItemType = TypeVar('HashableItemType', bound=Hashable) + + +def conset( + item_type: type[HashableItemType], *, min_length: int | None = None, max_length: int | None = None +) -> type[set[HashableItemType]]: + """A wrapper around `typing.Set` that allows for additional constraints. + + Args: + item_type: The type of the items in the set. + min_length: The minimum length of the set. + max_length: The maximum length of the set. + + Returns: + The wrapped set type. + """ + return Annotated[set[item_type], annotated_types.Len(min_length or 0, max_length)] # pyright: ignore[reportReturnType] + + +def confrozenset( + item_type: type[HashableItemType], *, min_length: int | None = None, max_length: int | None = None +) -> type[frozenset[HashableItemType]]: + """A wrapper around `typing.FrozenSet` that allows for additional constraints. + + Args: + item_type: The type of the items in the frozenset. + min_length: The minimum length of the frozenset. + max_length: The maximum length of the frozenset. + + Returns: + The wrapped frozenset type. + """ + return Annotated[frozenset[item_type], annotated_types.Len(min_length or 0, max_length)] # pyright: ignore[reportReturnType] + + +AnyItemType = TypeVar('AnyItemType') + + +def conlist( + item_type: type[AnyItemType], + *, + min_length: int | None = None, + max_length: int | None = None, + unique_items: bool | None = None, +) -> type[list[AnyItemType]]: + """A wrapper around [`list`][] that adds validation. + + Args: + item_type: The type of the items in the list. + min_length: The minimum length of the list. Defaults to None. + max_length: The maximum length of the list. Defaults to None. + unique_items: Whether the items in the list must be unique. Defaults to None. + !!! warning Deprecated + The `unique_items` parameter is deprecated, use `Set` instead. + See [this issue](https://github.com/pydantic/pydantic-core/issues/296) for more details. + + Returns: + The wrapped list type. + """ + if unique_items is not None: + raise PydanticUserError( + ( + '`unique_items` is removed, use `Set` instead' + '(this feature is discussed in https://github.com/pydantic/pydantic-core/issues/296)' + ), + code='removed-kwargs', + ) + return Annotated[list[item_type], annotated_types.Len(min_length or 0, max_length)] # pyright: ignore[reportReturnType] + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~ IMPORT STRING TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +AnyType = TypeVar('AnyType') +if TYPE_CHECKING: + ImportString = Annotated[AnyType, ...] +else: + + class ImportString: + """A type that can be used to import a Python object from a string. + + `ImportString` expects a string and loads the Python object importable at that dotted path. + Attributes of modules may be separated from the module by `:` or `.`, e.g. if `'math:cos'` is provided, + the resulting field value would be the function `cos`. If a `.` is used and both an attribute and submodule + are present at the same path, the module will be preferred. + + On model instantiation, pointers will be evaluated and imported. There is + some nuance to this behavior, demonstrated in the examples below. + + ```python + import math + + from pydantic import BaseModel, Field, ImportString, ValidationError + + class ImportThings(BaseModel): + obj: ImportString + + # A string value will cause an automatic import + my_cos = ImportThings(obj='math.cos') + + # You can use the imported function as you would expect + cos_of_0 = my_cos.obj(0) + assert cos_of_0 == 1 + + # A string whose value cannot be imported will raise an error + try: + ImportThings(obj='foo.bar') + except ValidationError as e: + print(e) + ''' + 1 validation error for ImportThings + obj + Invalid python path: No module named 'foo.bar' [type=import_error, input_value='foo.bar', input_type=str] + ''' + + # Actual python objects can be assigned as well + my_cos = ImportThings(obj=math.cos) + my_cos_2 = ImportThings(obj='math.cos') + my_cos_3 = ImportThings(obj='math:cos') + assert my_cos == my_cos_2 == my_cos_3 + + # You can set default field value either as Python object: + class ImportThingsDefaultPyObj(BaseModel): + obj: ImportString = math.cos + + # or as a string value (but only if used with `validate_default=True`) + class ImportThingsDefaultString(BaseModel): + obj: ImportString = Field(default='math.cos', validate_default=True) + + my_cos_default1 = ImportThingsDefaultPyObj() + my_cos_default2 = ImportThingsDefaultString() + assert my_cos_default1.obj == my_cos_default2.obj == math.cos + + # note: this will not work! + class ImportThingsMissingValidateDefault(BaseModel): + obj: ImportString = 'math.cos' + + my_cos_default3 = ImportThingsMissingValidateDefault() + assert my_cos_default3.obj == 'math.cos' # just string, not evaluated + ``` + + Serializing an `ImportString` type to json is also possible. + + ```python + from pydantic import BaseModel, ImportString + + class ImportThings(BaseModel): + obj: ImportString + + # Create an instance + m = ImportThings(obj='math.cos') + print(m) + #> obj= + print(m.model_dump_json()) + #> {"obj":"math.cos"} + ``` + """ + + @classmethod + def __class_getitem__(cls, item: AnyType) -> AnyType: + return Annotated[item, cls()] + + @classmethod + def __get_pydantic_core_schema__( + cls, source: type[Any], handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + serializer = core_schema.plain_serializer_function_ser_schema(cls._serialize, when_used='json') + if cls is source: + # Treat bare usage of ImportString (`schema is None`) as the same as ImportString[Any] + return core_schema.no_info_plain_validator_function( + function=_validators.import_string, serialization=serializer + ) + else: + return core_schema.no_info_before_validator_function( + function=_validators.import_string, schema=handler(source), serialization=serializer + ) + + @classmethod + def __get_pydantic_json_schema__(cls, cs: CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue: + return handler(core_schema.str_schema()) + + @staticmethod + def _serialize(v: Any) -> str: + if isinstance(v, ModuleType): + return v.__name__ + elif hasattr(v, '__module__') and hasattr(v, '__name__'): + return f'{v.__module__}.{v.__name__}' + # Handle special cases for sys.XXX streams + # if we see more of these, we should consider a more general solution + elif hasattr(v, 'name'): + if v.name == '': + return 'sys.stdout' + elif v.name == '': + return 'sys.stdin' + elif v.name == '': + return 'sys.stderr' + return v + + def __repr__(self) -> str: + return 'ImportString' + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DECIMAL TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +def condecimal( + *, + strict: bool | None = None, + gt: int | Decimal | None = None, + ge: int | Decimal | None = None, + lt: int | Decimal | None = None, + le: int | Decimal | None = None, + multiple_of: int | Decimal | None = None, + max_digits: int | None = None, + decimal_places: int | None = None, + allow_inf_nan: bool | None = None, +) -> type[Decimal]: + """ + !!! warning "Discouraged" + This function is **discouraged** in favor of using + [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated) with + [`Field`][pydantic.fields.Field] instead. + + This function will be **deprecated** in Pydantic 3.0. + + The reason is that `condecimal` returns a type, which doesn't play well with static analysis tools. + + === ":x: Don't do this" + ```python + from pydantic import BaseModel, condecimal + + class Foo(BaseModel): + bar: condecimal(strict=True, allow_inf_nan=True) + ``` + + === ":white_check_mark: Do this" + ```python + from decimal import Decimal + from typing import Annotated + + from pydantic import BaseModel, Field + + class Foo(BaseModel): + bar: Annotated[Decimal, Field(strict=True, allow_inf_nan=True)] + ``` + + A wrapper around Decimal that adds validation. + + Args: + strict: Whether to validate the value in strict mode. Defaults to `None`. + gt: The value must be greater than this. Defaults to `None`. + ge: The value must be greater than or equal to this. Defaults to `None`. + lt: The value must be less than this. Defaults to `None`. + le: The value must be less than or equal to this. Defaults to `None`. + multiple_of: The value must be a multiple of this. Defaults to `None`. + max_digits: The maximum number of digits. Defaults to `None`. + decimal_places: The number of decimal places. Defaults to `None`. + allow_inf_nan: Whether to allow infinity and NaN. Defaults to `None`. + + ```python + from decimal import Decimal + + from pydantic import BaseModel, ValidationError, condecimal + + class ConstrainedExample(BaseModel): + constrained_decimal: condecimal(gt=Decimal('1.0')) + + m = ConstrainedExample(constrained_decimal=Decimal('1.1')) + print(repr(m)) + #> ConstrainedExample(constrained_decimal=Decimal('1.1')) + + try: + ConstrainedExample(constrained_decimal=Decimal('0.9')) + except ValidationError as e: + print(e.errors()) + ''' + [ + { + 'type': 'greater_than', + 'loc': ('constrained_decimal',), + 'msg': 'Input should be greater than 1.0', + 'input': Decimal('0.9'), + 'ctx': {'gt': Decimal('1.0')}, + 'url': 'https://errors.pydantic.dev/2/v/greater_than', + } + ] + ''' + ``` + """ # noqa: D212 + return Annotated[ # pyright: ignore[reportReturnType] + Decimal, + Strict(strict) if strict is not None else None, + annotated_types.Interval(gt=gt, ge=ge, lt=lt, le=le), + annotated_types.MultipleOf(multiple_of) if multiple_of is not None else None, + _fields.pydantic_general_metadata(max_digits=max_digits, decimal_places=decimal_places), + AllowInfNan(allow_inf_nan) if allow_inf_nan is not None else None, + ] + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UUID TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +@_dataclasses.dataclass(**_internal_dataclass.slots_true) +class UuidVersion: + """A field metadata class to indicate a [UUID](https://docs.python.org/3/library/uuid.html) version. + + Use this class as an annotation via [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated), as seen below. + + Attributes: + uuid_version: The version of the UUID. Must be one of 1, 3, 4, 5, 6, 7 or 8. + + Example: + ```python + from typing import Annotated + from uuid import UUID + + from pydantic.types import UuidVersion + + UUID1 = Annotated[UUID, UuidVersion(1)] + ``` + """ + + uuid_version: Literal[1, 3, 4, 5, 6, 7, 8] + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> JsonSchemaValue: + field_schema = handler(core_schema) + field_schema.pop('anyOf', None) # remove the bytes/str union + field_schema.update(type='string', format=f'uuid{self.uuid_version}') + return field_schema + + def __get_pydantic_core_schema__(self, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + schema = handler(source) + _check_annotated_type(schema['type'], 'uuid', self.__class__.__name__) + schema['version'] = self.uuid_version # type: ignore + return schema + + def __hash__(self) -> int: + return hash(type(self.uuid_version)) + + +UUID1 = Annotated[UUID, UuidVersion(1)] +"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 1. + +```python +import uuid + +from pydantic import UUID1, BaseModel + +class Model(BaseModel): + uuid1: UUID1 + +Model(uuid1=uuid.uuid1()) +``` +""" +UUID3 = Annotated[UUID, UuidVersion(3)] +"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 3. + +```python +import uuid + +from pydantic import UUID3, BaseModel + +class Model(BaseModel): + uuid3: UUID3 + +Model(uuid3=uuid.uuid3(uuid.NAMESPACE_DNS, 'pydantic.org')) +``` +""" +UUID4 = Annotated[UUID, UuidVersion(4)] +"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 4. + +```python +import uuid + +from pydantic import UUID4, BaseModel + +class Model(BaseModel): + uuid4: UUID4 + +Model(uuid4=uuid.uuid4()) +``` +""" +UUID5 = Annotated[UUID, UuidVersion(5)] +"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 5. + +```python +import uuid + +from pydantic import UUID5, BaseModel + +class Model(BaseModel): + uuid5: UUID5 + +Model(uuid5=uuid.uuid5(uuid.NAMESPACE_DNS, 'pydantic.org')) +``` +""" +UUID6 = Annotated[UUID, UuidVersion(6)] +"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 6. + +```python +import uuid + +from pydantic import UUID6, BaseModel + +class Model(BaseModel): + uuid6: UUID6 + +Model(uuid6=uuid.UUID('1efea953-c2d6-6790-aa0a-69db8c87df97')) +``` +""" +UUID7 = Annotated[UUID, UuidVersion(7)] +"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 7. + +```python +import uuid + +from pydantic import UUID7, BaseModel + +class Model(BaseModel): + uuid7: UUID7 + +Model(uuid7=uuid.UUID('0194fdcb-1c47-7a09-b52c-561154de0b4a')) +``` +""" +UUID8 = Annotated[UUID, UuidVersion(8)] +"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 8. + +```python +import uuid + +from pydantic import UUID8, BaseModel + +class Model(BaseModel): + uuid8: UUID8 + +Model(uuid8=uuid.UUID('81a0b92e-6078-8551-9c81-8ccb666bdab8')) +``` +""" + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PATH TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +@_dataclasses.dataclass +class PathType: + path_type: Literal['file', 'dir', 'new', 'socket'] + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> JsonSchemaValue: + field_schema = handler(core_schema) + format_conversion = {'file': 'file-path', 'dir': 'directory-path'} + field_schema.update(format=format_conversion.get(self.path_type, 'path'), type='string') + return field_schema + + def __get_pydantic_core_schema__(self, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + function_lookup = { + 'file': cast(core_schema.WithInfoValidatorFunction, self.validate_file), + 'dir': cast(core_schema.WithInfoValidatorFunction, self.validate_directory), + 'new': cast(core_schema.WithInfoValidatorFunction, self.validate_new), + 'socket': cast(core_schema.WithInfoValidatorFunction, self.validate_socket), + } + + return core_schema.with_info_after_validator_function( + function_lookup[self.path_type], + handler(source), + ) + + @staticmethod + def validate_file(path: Path, _: core_schema.ValidationInfo) -> Path: + if path.is_file(): + return path + else: + raise PydanticCustomError('path_not_file', 'Path does not point to a file') + + @staticmethod + def validate_socket(path: Path, _: core_schema.ValidationInfo) -> Path: + if path.is_socket(): + return path + else: + raise PydanticCustomError('path_not_socket', 'Path does not point to a socket') + + @staticmethod + def validate_directory(path: Path, _: core_schema.ValidationInfo) -> Path: + if path.is_dir(): + return path + else: + raise PydanticCustomError('path_not_directory', 'Path does not point to a directory') + + @staticmethod + def validate_new(path: Path, _: core_schema.ValidationInfo) -> Path: + if path.exists(): + raise PydanticCustomError('path_exists', 'Path already exists') + elif not path.parent.exists(): + raise PydanticCustomError('parent_does_not_exist', 'Parent directory does not exist') + else: + return path + + def __hash__(self) -> int: + return hash(type(self.path_type)) + + +FilePath = Annotated[Path, PathType('file')] +"""A path that must point to a file. + +```python +from pathlib import Path + +from pydantic import BaseModel, FilePath, ValidationError + +class Model(BaseModel): + f: FilePath + +path = Path('text.txt') +path.touch() +m = Model(f='text.txt') +print(m.model_dump()) +#> {'f': PosixPath('text.txt')} +path.unlink() + +path = Path('directory') +path.mkdir(exist_ok=True) +try: + Model(f='directory') # directory +except ValidationError as e: + print(e) + ''' + 1 validation error for Model + f + Path does not point to a file [type=path_not_file, input_value='directory', input_type=str] + ''' +path.rmdir() + +try: + Model(f='not-exists-file') +except ValidationError as e: + print(e) + ''' + 1 validation error for Model + f + Path does not point to a file [type=path_not_file, input_value='not-exists-file', input_type=str] + ''' +``` +""" +DirectoryPath = Annotated[Path, PathType('dir')] +"""A path that must point to a directory. + +```python +from pathlib import Path + +from pydantic import BaseModel, DirectoryPath, ValidationError + +class Model(BaseModel): + f: DirectoryPath + +path = Path('directory/') +path.mkdir() +m = Model(f='directory/') +print(m.model_dump()) +#> {'f': PosixPath('directory')} +path.rmdir() + +path = Path('file.txt') +path.touch() +try: + Model(f='file.txt') # file +except ValidationError as e: + print(e) + ''' + 1 validation error for Model + f + Path does not point to a directory [type=path_not_directory, input_value='file.txt', input_type=str] + ''' +path.unlink() + +try: + Model(f='not-exists-directory') +except ValidationError as e: + print(e) + ''' + 1 validation error for Model + f + Path does not point to a directory [type=path_not_directory, input_value='not-exists-directory', input_type=str] + ''' +``` +""" +NewPath = Annotated[Path, PathType('new')] +"""A path for a new file or directory that must not already exist. The parent directory must already exist.""" + +SocketPath = Annotated[Path, PathType('socket')] +"""A path to an existing socket file""" + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +if TYPE_CHECKING: + # Json[list[str]] will be recognized by type checkers as list[str] + Json = Annotated[AnyType, ...] + +else: + + class Json: + """A special type wrapper which loads JSON before parsing. + + You can use the `Json` data type to make Pydantic first load a raw JSON string before + validating the loaded data into the parametrized type: + + ```python + from typing import Any + + from pydantic import BaseModel, Json, ValidationError + + class AnyJsonModel(BaseModel): + json_obj: Json[Any] + + class ConstrainedJsonModel(BaseModel): + json_obj: Json[list[int]] + + print(AnyJsonModel(json_obj='{"b": 1}')) + #> json_obj={'b': 1} + print(ConstrainedJsonModel(json_obj='[1, 2, 3]')) + #> json_obj=[1, 2, 3] + + try: + ConstrainedJsonModel(json_obj=12) + except ValidationError as e: + print(e) + ''' + 1 validation error for ConstrainedJsonModel + json_obj + JSON input should be string, bytes or bytearray [type=json_type, input_value=12, input_type=int] + ''' + + try: + ConstrainedJsonModel(json_obj='[a, b]') + except ValidationError as e: + print(e) + ''' + 1 validation error for ConstrainedJsonModel + json_obj + Invalid JSON: expected value at line 1 column 2 [type=json_invalid, input_value='[a, b]', input_type=str] + ''' + + try: + ConstrainedJsonModel(json_obj='["a", "b"]') + except ValidationError as e: + print(e) + ''' + 2 validation errors for ConstrainedJsonModel + json_obj.0 + Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str] + json_obj.1 + Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='b', input_type=str] + ''' + ``` + + When you dump the model using `model_dump` or `model_dump_json`, the dumped value will be the result of validation, + not the original JSON string. However, you can use the argument `round_trip=True` to get the original JSON string back: + + ```python + from pydantic import BaseModel, Json + + class ConstrainedJsonModel(BaseModel): + json_obj: Json[list[int]] + + print(ConstrainedJsonModel(json_obj='[1, 2, 3]').model_dump_json()) + #> {"json_obj":[1,2,3]} + print( + ConstrainedJsonModel(json_obj='[1, 2, 3]').model_dump_json(round_trip=True) + ) + #> {"json_obj":"[1,2,3]"} + ``` + """ + + @classmethod + def __class_getitem__(cls, item: AnyType) -> AnyType: + return Annotated[item, cls()] + + @classmethod + def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + if cls is source: + return core_schema.json_schema(None) + else: + return core_schema.json_schema(handler(source)) + + def __repr__(self) -> str: + return 'Json' + + def __hash__(self) -> int: + return hash(type(self)) + + def __eq__(self, other: Any) -> bool: + return type(other) is type(self) + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SECRET TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +# The `Secret` class being conceptually immutable, make the type variable covariant: +SecretType = TypeVar('SecretType', covariant=True) + + +class _SecretBase(Generic[SecretType]): + def __init__(self, secret_value: SecretType) -> None: + self._secret_value: SecretType = secret_value + + def get_secret_value(self) -> SecretType: + """Get the secret value. + + Returns: + The secret value. + """ + return self._secret_value + + def __eq__(self, other: Any) -> bool: + return isinstance(other, self.__class__) and self.get_secret_value() == other.get_secret_value() + + def __hash__(self) -> int: + return hash(self.get_secret_value()) + + def __str__(self) -> str: + return str(self._display()) + + def __repr__(self) -> str: + return f'{self.__class__.__name__}({self._display()!r})' + + def _display(self) -> str | bytes: + raise NotImplementedError + + +def _serialize_secret(value: Secret[SecretType], info: core_schema.SerializationInfo) -> str | Secret[SecretType]: + if info.mode == 'json': + return str(value) + else: + return value + + +class Secret(_SecretBase[SecretType]): + """A generic base class used for defining a field with sensitive information that you do not want to be visible in logging or tracebacks. + + You may either directly parametrize `Secret` with a type, or subclass from `Secret` with a parametrized type. The benefit of subclassing + is that you can define a custom `_display` method, which will be used for `repr()` and `str()` methods. The examples below demonstrate both + ways of using `Secret` to create a new secret type. + + 1. Directly parametrizing `Secret` with a type: + + ```python + from pydantic import BaseModel, Secret + + SecretBool = Secret[bool] + + class Model(BaseModel): + secret_bool: SecretBool + + m = Model(secret_bool=True) + print(m.model_dump()) + #> {'secret_bool': Secret('**********')} + + print(m.model_dump_json()) + #> {"secret_bool":"**********"} + + print(m.secret_bool.get_secret_value()) + #> True + ``` + + 2. Subclassing from parametrized `Secret`: + + ```python + from datetime import date + + from pydantic import BaseModel, Secret + + class SecretDate(Secret[date]): + def _display(self) -> str: + return '****/**/**' + + class Model(BaseModel): + secret_date: SecretDate + + m = Model(secret_date=date(2022, 1, 1)) + print(m.model_dump()) + #> {'secret_date': SecretDate('****/**/**')} + + print(m.model_dump_json()) + #> {"secret_date":"****/**/**"} + + print(m.secret_date.get_secret_value()) + #> 2022-01-01 + ``` + + The value returned by the `_display` method will be used for `repr()` and `str()`. + + You can enforce constraints on the underlying type through annotations: + For example: + + ```python + from typing import Annotated + + from pydantic import BaseModel, Field, Secret, ValidationError + + SecretPosInt = Secret[Annotated[int, Field(gt=0, strict=True)]] + + class Model(BaseModel): + sensitive_int: SecretPosInt + + m = Model(sensitive_int=42) + print(m.model_dump()) + #> {'sensitive_int': Secret('**********')} + + try: + m = Model(sensitive_int=-42) # (1)! + except ValidationError as exc_info: + print(exc_info.errors(include_url=False, include_input=False)) + ''' + [ + { + 'type': 'greater_than', + 'loc': ('sensitive_int',), + 'msg': 'Input should be greater than 0', + 'ctx': {'gt': 0}, + } + ] + ''' + + try: + m = Model(sensitive_int='42') # (2)! + except ValidationError as exc_info: + print(exc_info.errors(include_url=False, include_input=False)) + ''' + [ + { + 'type': 'int_type', + 'loc': ('sensitive_int',), + 'msg': 'Input should be a valid integer', + } + ] + ''' + ``` + + 1. The input value is not greater than 0, so it raises a validation error. + 2. The input value is not an integer, so it raises a validation error because the `SecretPosInt` type has strict mode enabled. + """ + + def _display(self) -> str | bytes: + return '**********' if self.get_secret_value() else '' + + @classmethod + def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + inner_type = None + # if origin_type is Secret, then cls is a GenericAlias, and we can extract the inner type directly + origin_type = get_origin(source) + if origin_type is not None: + inner_type = get_args(source)[0] + # otherwise, we need to get the inner type from the base class + else: + bases = getattr(cls, '__orig_bases__', getattr(cls, '__bases__', [])) + for base in bases: + if get_origin(base) is Secret: + inner_type = get_args(base)[0] + if bases == [] or inner_type is None: + raise TypeError( + f"Can't get secret type from {cls.__name__}. " + 'Please use Secret[], or subclass from Secret[] instead.' + ) + + inner_schema = handler.generate_schema(inner_type) # type: ignore + + def validate_secret_value(value, handler) -> Secret[SecretType]: + if isinstance(value, Secret): + value = value.get_secret_value() + validated_inner = handler(value) + return cls(validated_inner) + + return core_schema.json_or_python_schema( + python_schema=core_schema.no_info_wrap_validator_function( + validate_secret_value, + inner_schema, + ), + json_schema=core_schema.no_info_after_validator_function(lambda x: cls(x), inner_schema), + serialization=core_schema.plain_serializer_function_ser_schema( + _serialize_secret, + info_arg=True, + when_used='always', + ), + ) + + __pydantic_serializer__ = SchemaSerializer( + core_schema.any_schema( + serialization=core_schema.plain_serializer_function_ser_schema( + _serialize_secret, + info_arg=True, + when_used='always', + ) + ) + ) + + +def _secret_display(value: SecretType) -> str: # type: ignore + return '**********' if value else '' + + +def _serialize_secret_field( + value: _SecretField[SecretType], info: core_schema.SerializationInfo +) -> str | _SecretField[SecretType]: + if info.mode == 'json': + # we want the output to always be string without the `b'` prefix for bytes, + # hence we just use `secret_display` + return _secret_display(value.get_secret_value()) + else: + return value + + +class _SecretField(_SecretBase[SecretType]): + _inner_schema: ClassVar[CoreSchema] + _error_kind: ClassVar[str] + + @classmethod + def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + def get_json_schema(_core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue: + json_schema = handler(cls._inner_schema) + _utils.update_not_none( + json_schema, + type='string', + writeOnly=True, + format='password', + ) + return json_schema + + def get_secret_schema(strict: bool) -> CoreSchema: + inner_schema = {**cls._inner_schema, 'strict': strict} + json_schema = core_schema.no_info_after_validator_function( + source, # construct the type + inner_schema, # pyright: ignore[reportArgumentType] + ) + return core_schema.json_or_python_schema( + python_schema=core_schema.union_schema( + [ + core_schema.is_instance_schema(source), + json_schema, + ], + custom_error_type=cls._error_kind, + ), + json_schema=json_schema, + serialization=core_schema.plain_serializer_function_ser_schema( + _serialize_secret_field, + info_arg=True, + when_used='always', + ), + ) + + return core_schema.lax_or_strict_schema( + lax_schema=get_secret_schema(strict=False), + strict_schema=get_secret_schema(strict=True), + metadata={'pydantic_js_functions': [get_json_schema]}, + ) + + __pydantic_serializer__ = SchemaSerializer( + core_schema.any_schema( + serialization=core_schema.plain_serializer_function_ser_schema( + _serialize_secret_field, + info_arg=True, + when_used='always', + ) + ) + ) + + +class SecretStr(_SecretField[str]): + """A string used for storing sensitive information that you do not want to be visible in logging or tracebacks. + + When the secret value is nonempty, it is displayed as `'**********'` instead of the underlying value in + calls to `repr()` and `str()`. If the value _is_ empty, it is displayed as `''`. + + ```python + from pydantic import BaseModel, SecretStr + + class User(BaseModel): + username: str + password: SecretStr + + user = User(username='scolvin', password='password1') + + print(user) + #> username='scolvin' password=SecretStr('**********') + print(user.password.get_secret_value()) + #> password1 + print((SecretStr('password'), SecretStr(''))) + #> (SecretStr('**********'), SecretStr('')) + ``` + + As seen above, by default, [`SecretStr`][pydantic.types.SecretStr] (and [`SecretBytes`][pydantic.types.SecretBytes]) + will be serialized as `**********` when serializing to json. + + You can use the [`field_serializer`][pydantic.functional_serializers.field_serializer] to dump the + secret as plain-text when serializing to json. + + ```python + from pydantic import BaseModel, SecretBytes, SecretStr, field_serializer + + class Model(BaseModel): + password: SecretStr + password_bytes: SecretBytes + + @field_serializer('password', 'password_bytes', when_used='json') + def dump_secret(self, v): + return v.get_secret_value() + + model = Model(password='IAmSensitive', password_bytes=b'IAmSensitiveBytes') + print(model) + #> password=SecretStr('**********') password_bytes=SecretBytes(b'**********') + print(model.password) + #> ********** + print(model.model_dump()) + ''' + { + 'password': SecretStr('**********'), + 'password_bytes': SecretBytes(b'**********'), + } + ''' + print(model.model_dump_json()) + #> {"password":"IAmSensitive","password_bytes":"IAmSensitiveBytes"} + ``` + """ + + _inner_schema: ClassVar[CoreSchema] = core_schema.str_schema() + _error_kind: ClassVar[str] = 'string_type' + + def __len__(self) -> int: + return len(self._secret_value) + + def _display(self) -> str: + return _secret_display(self._secret_value) + + +class SecretBytes(_SecretField[bytes]): + """A bytes used for storing sensitive information that you do not want to be visible in logging or tracebacks. + + It displays `b'**********'` instead of the string value on `repr()` and `str()` calls. + When the secret value is nonempty, it is displayed as `b'**********'` instead of the underlying value in + calls to `repr()` and `str()`. If the value _is_ empty, it is displayed as `b''`. + + ```python + from pydantic import BaseModel, SecretBytes + + class User(BaseModel): + username: str + password: SecretBytes + + user = User(username='scolvin', password=b'password1') + #> username='scolvin' password=SecretBytes(b'**********') + print(user.password.get_secret_value()) + #> b'password1' + print((SecretBytes(b'password'), SecretBytes(b''))) + #> (SecretBytes(b'**********'), SecretBytes(b'')) + ``` + """ + + _inner_schema: ClassVar[CoreSchema] = core_schema.bytes_schema() + _error_kind: ClassVar[str] = 'bytes_type' + + def __len__(self) -> int: + return len(self._secret_value) + + def _display(self) -> bytes: + return _secret_display(self._secret_value).encode() + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PAYMENT CARD TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +class PaymentCardBrand(str, Enum): + amex = 'American Express' + mastercard = 'Mastercard' + visa = 'Visa' + other = 'other' + + def __str__(self) -> str: + return self.value + + +@deprecated( + 'The `PaymentCardNumber` class is deprecated, use `pydantic_extra_types` instead. ' + 'See https://docs.pydantic.dev/latest/api/pydantic_extra_types_payment/#pydantic_extra_types.payment.PaymentCardNumber.', + category=PydanticDeprecatedSince20, +) +class PaymentCardNumber(str): + """Based on: https://en.wikipedia.org/wiki/Payment_card_number.""" + + strip_whitespace: ClassVar[bool] = True + min_length: ClassVar[int] = 12 + max_length: ClassVar[int] = 19 + bin: str + last4: str + brand: PaymentCardBrand + + def __init__(self, card_number: str): + self.validate_digits(card_number) + + card_number = self.validate_luhn_check_digit(card_number) + + self.bin = card_number[:6] + self.last4 = card_number[-4:] + self.brand = self.validate_brand(card_number) + + @classmethod + def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + return core_schema.with_info_after_validator_function( + cls.validate, + core_schema.str_schema( + min_length=cls.min_length, max_length=cls.max_length, strip_whitespace=cls.strip_whitespace + ), + ) + + @classmethod + def validate(cls, input_value: str, /, _: core_schema.ValidationInfo) -> PaymentCardNumber: + """Validate the card number and return a `PaymentCardNumber` instance.""" + return cls(input_value) + + @property + def masked(self) -> str: + """Mask all but the last 4 digits of the card number. + + Returns: + A masked card number string. + """ + num_masked = len(self) - 10 # len(bin) + len(last4) == 10 + return f'{self.bin}{"*" * num_masked}{self.last4}' + + @classmethod + def validate_digits(cls, card_number: str) -> None: + """Validate that the card number is all digits.""" + if not card_number.isdigit(): + raise PydanticCustomError('payment_card_number_digits', 'Card number is not all digits') + + @classmethod + def validate_luhn_check_digit(cls, card_number: str) -> str: + """Based on: https://en.wikipedia.org/wiki/Luhn_algorithm.""" + sum_ = int(card_number[-1]) + length = len(card_number) + parity = length % 2 + for i in range(length - 1): + digit = int(card_number[i]) + if i % 2 == parity: + digit *= 2 + if digit > 9: + digit -= 9 + sum_ += digit + valid = sum_ % 10 == 0 + if not valid: + raise PydanticCustomError('payment_card_number_luhn', 'Card number is not luhn valid') + return card_number + + @staticmethod + def validate_brand(card_number: str) -> PaymentCardBrand: + """Validate length based on BIN for major brands: + https://en.wikipedia.org/wiki/Payment_card_number#Issuer_identification_number_(IIN). + """ + if card_number[0] == '4': + brand = PaymentCardBrand.visa + elif 51 <= int(card_number[:2]) <= 55: + brand = PaymentCardBrand.mastercard + elif card_number[:2] in {'34', '37'}: + brand = PaymentCardBrand.amex + else: + brand = PaymentCardBrand.other + + required_length: None | int | str = None + if brand in PaymentCardBrand.mastercard: + required_length = 16 + valid = len(card_number) == required_length + elif brand == PaymentCardBrand.visa: + required_length = '13, 16 or 19' + valid = len(card_number) in {13, 16, 19} + elif brand == PaymentCardBrand.amex: + required_length = 15 + valid = len(card_number) == required_length + else: + valid = True + + if not valid: + raise PydanticCustomError( + 'payment_card_number_brand', + 'Length for a {brand} card must be {required_length}', + {'brand': brand, 'required_length': required_length}, + ) + return brand + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BYTE SIZE TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +class ByteSize(int): + """Converts a string representing a number of bytes with units (such as `'1KB'` or `'11.5MiB'`) into an integer. + + You can use the `ByteSize` data type to (case-insensitively) convert a string representation of a number of bytes into + an integer, and also to print out human-readable strings representing a number of bytes. + + In conformance with [IEC 80000-13 Standard](https://en.wikipedia.org/wiki/ISO/IEC_80000) we interpret `'1KB'` to mean 1000 bytes, + and `'1KiB'` to mean 1024 bytes. In general, including a middle `'i'` will cause the unit to be interpreted as a power of 2, + rather than a power of 10 (so, for example, `'1 MB'` is treated as `1_000_000` bytes, whereas `'1 MiB'` is treated as `1_048_576` bytes). + + !!! info + Note that `1b` will be parsed as "1 byte" and not "1 bit". + + ```python + from pydantic import BaseModel, ByteSize + + class MyModel(BaseModel): + size: ByteSize + + print(MyModel(size=52000).size) + #> 52000 + print(MyModel(size='3000 KiB').size) + #> 3072000 + + m = MyModel(size='50 PB') + print(m.size.human_readable()) + #> 44.4PiB + print(m.size.human_readable(decimal=True)) + #> 50.0PB + print(m.size.human_readable(separator=' ')) + #> 44.4 PiB + + print(m.size.to('TiB')) + #> 45474.73508864641 + ``` + """ + + byte_sizes = { + 'b': 1, + 'kb': 10**3, + 'mb': 10**6, + 'gb': 10**9, + 'tb': 10**12, + 'pb': 10**15, + 'eb': 10**18, + 'kib': 2**10, + 'mib': 2**20, + 'gib': 2**30, + 'tib': 2**40, + 'pib': 2**50, + 'eib': 2**60, + 'bit': 1 / 8, + 'kbit': 10**3 / 8, + 'mbit': 10**6 / 8, + 'gbit': 10**9 / 8, + 'tbit': 10**12 / 8, + 'pbit': 10**15 / 8, + 'ebit': 10**18 / 8, + 'kibit': 2**10 / 8, + 'mibit': 2**20 / 8, + 'gibit': 2**30 / 8, + 'tibit': 2**40 / 8, + 'pibit': 2**50 / 8, + 'eibit': 2**60 / 8, + } + byte_sizes.update({k.lower()[0]: v for k, v in byte_sizes.items() if 'i' not in k}) + + byte_string_pattern = r'^\s*(\d*\.?\d+)\s*(\w+)?' + byte_string_re = re.compile(byte_string_pattern, re.IGNORECASE) + + @classmethod + def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + return core_schema.with_info_after_validator_function( + function=cls._validate, + schema=core_schema.union_schema( + [ + core_schema.str_schema(pattern=cls.byte_string_pattern), + core_schema.int_schema(ge=0), + ], + custom_error_type='byte_size', + custom_error_message='could not parse value and unit from byte string', + ), + serialization=core_schema.plain_serializer_function_ser_schema( + int, return_schema=core_schema.int_schema(ge=0) + ), + ) + + @classmethod + def _validate(cls, input_value: Any, /, _: core_schema.ValidationInfo) -> ByteSize: + try: + return cls(int(input_value)) + except ValueError: + pass + + str_match = cls.byte_string_re.match(str(input_value)) + if str_match is None: + raise PydanticCustomError('byte_size', 'could not parse value and unit from byte string') + + scalar, unit = str_match.groups() + if unit is None: + unit = 'b' + + try: + unit_mult = cls.byte_sizes[unit.lower()] + except KeyError: + raise PydanticCustomError('byte_size_unit', 'could not interpret byte unit: {unit}', {'unit': unit}) + + return cls(int(float(scalar) * unit_mult)) + + def human_readable(self, decimal: bool = False, separator: str = '') -> str: + """Converts a byte size to a human readable string. + + Args: + decimal: If True, use decimal units (e.g. 1000 bytes per KB). If False, use binary units + (e.g. 1024 bytes per KiB). + separator: A string used to split the value and unit. Defaults to an empty string (''). + + Returns: + A human readable string representation of the byte size. + """ + if decimal: + divisor = 1000 + units = 'B', 'KB', 'MB', 'GB', 'TB', 'PB' + final_unit = 'EB' + else: + divisor = 1024 + units = 'B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB' + final_unit = 'EiB' + + num = float(self) + for unit in units: + if abs(num) < divisor: + if unit == 'B': + return f'{num:0.0f}{separator}{unit}' + else: + return f'{num:0.1f}{separator}{unit}' + num /= divisor + + return f'{num:0.1f}{separator}{final_unit}' + + def to(self, unit: str) -> float: + """Converts a byte size to another unit, including both byte and bit units. + + Args: + unit: The unit to convert to. Must be one of the following: B, KB, MB, GB, TB, PB, EB, + KiB, MiB, GiB, TiB, PiB, EiB (byte units) and + bit, kbit, mbit, gbit, tbit, pbit, ebit, + kibit, mibit, gibit, tibit, pibit, eibit (bit units). + + Returns: + The byte size in the new unit. + """ + try: + unit_div = self.byte_sizes[unit.lower()] + except KeyError: + raise PydanticCustomError('byte_size_unit', 'Could not interpret byte unit: {unit}', {'unit': unit}) + + return self / unit_div + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DATE TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +def _check_annotated_type(annotated_type: str, expected_type: str, annotation: str) -> None: + if annotated_type != expected_type: + raise PydanticUserError(f"'{annotation}' cannot annotate '{annotated_type}'.", code='invalid-annotated-type') + + +if TYPE_CHECKING: + PastDate = Annotated[date, ...] + FutureDate = Annotated[date, ...] +else: + + class PastDate: + """A date in the past.""" + + @classmethod + def __get_pydantic_core_schema__( + cls, source: type[Any], handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + if cls is source: + # used directly as a type + return core_schema.date_schema(now_op='past') + else: + schema = handler(source) + _check_annotated_type(schema['type'], 'date', cls.__name__) + schema['now_op'] = 'past' + return schema + + def __repr__(self) -> str: + return 'PastDate' + + class FutureDate: + """A date in the future.""" + + @classmethod + def __get_pydantic_core_schema__( + cls, source: type[Any], handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + if cls is source: + # used directly as a type + return core_schema.date_schema(now_op='future') + else: + schema = handler(source) + _check_annotated_type(schema['type'], 'date', cls.__name__) + schema['now_op'] = 'future' + return schema + + def __repr__(self) -> str: + return 'FutureDate' + + +def condate( + *, + strict: bool | None = None, + gt: date | None = None, + ge: date | None = None, + lt: date | None = None, + le: date | None = None, +) -> type[date]: + """A wrapper for date that adds constraints. + + Args: + strict: Whether to validate the date value in strict mode. Defaults to `None`. + gt: The value must be greater than this. Defaults to `None`. + ge: The value must be greater than or equal to this. Defaults to `None`. + lt: The value must be less than this. Defaults to `None`. + le: The value must be less than or equal to this. Defaults to `None`. + + Returns: + A date type with the specified constraints. + """ + return Annotated[ # pyright: ignore[reportReturnType] + date, + Strict(strict) if strict is not None else None, + annotated_types.Interval(gt=gt, ge=ge, lt=lt, le=le), + ] + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DATETIME TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +if TYPE_CHECKING: + AwareDatetime = Annotated[datetime, ...] + NaiveDatetime = Annotated[datetime, ...] + PastDatetime = Annotated[datetime, ...] + FutureDatetime = Annotated[datetime, ...] + +else: + + class AwareDatetime: + """A datetime that requires timezone info.""" + + @classmethod + def __get_pydantic_core_schema__( + cls, source: type[Any], handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + if cls is source: + # used directly as a type + return core_schema.datetime_schema(tz_constraint='aware') + else: + schema = handler(source) + _check_annotated_type(schema['type'], 'datetime', cls.__name__) + schema['tz_constraint'] = 'aware' + return schema + + def __repr__(self) -> str: + return 'AwareDatetime' + + class NaiveDatetime: + """A datetime that doesn't require timezone info.""" + + @classmethod + def __get_pydantic_core_schema__( + cls, source: type[Any], handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + if cls is source: + # used directly as a type + return core_schema.datetime_schema(tz_constraint='naive') + else: + schema = handler(source) + _check_annotated_type(schema['type'], 'datetime', cls.__name__) + schema['tz_constraint'] = 'naive' + return schema + + def __repr__(self) -> str: + return 'NaiveDatetime' + + class PastDatetime: + """A datetime that must be in the past.""" + + @classmethod + def __get_pydantic_core_schema__( + cls, source: type[Any], handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + if cls is source: + # used directly as a type + return core_schema.datetime_schema(now_op='past') + else: + schema = handler(source) + _check_annotated_type(schema['type'], 'datetime', cls.__name__) + schema['now_op'] = 'past' + return schema + + def __repr__(self) -> str: + return 'PastDatetime' + + class FutureDatetime: + """A datetime that must be in the future.""" + + @classmethod + def __get_pydantic_core_schema__( + cls, source: type[Any], handler: GetCoreSchemaHandler + ) -> core_schema.CoreSchema: + if cls is source: + # used directly as a type + return core_schema.datetime_schema(now_op='future') + else: + schema = handler(source) + _check_annotated_type(schema['type'], 'datetime', cls.__name__) + schema['now_op'] = 'future' + return schema + + def __repr__(self) -> str: + return 'FutureDatetime' + + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Encoded TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +class EncoderProtocol(Protocol): + """Protocol for encoding and decoding data to and from bytes.""" + + @classmethod + def decode(cls, data: bytes) -> bytes: + """Decode the data using the encoder. + + Args: + data: The data to decode. + + Returns: + The decoded data. + """ + ... + + @classmethod + def encode(cls, value: bytes) -> bytes: + """Encode the data using the encoder. + + Args: + value: The data to encode. + + Returns: + The encoded data. + """ + ... + + @classmethod + def get_json_format(cls) -> str: + """Get the JSON format for the encoded data. + + Returns: + The JSON format for the encoded data. + """ + ... + + +class Base64Encoder(EncoderProtocol): + """Standard (non-URL-safe) Base64 encoder.""" + + @classmethod + def decode(cls, data: bytes) -> bytes: + """Decode the data from base64 encoded bytes to original bytes data. + + Args: + data: The data to decode. + + Returns: + The decoded data. + """ + try: + return base64.b64decode(data) + except ValueError as e: + raise PydanticCustomError('base64_decode', "Base64 decoding error: '{error}'", {'error': str(e)}) + + @classmethod + def encode(cls, value: bytes) -> bytes: + """Encode the data from bytes to a base64 encoded bytes. + + Args: + value: The data to encode. + + Returns: + The encoded data. + """ + return base64.b64encode(value) + + @classmethod + def get_json_format(cls) -> Literal['base64']: + """Get the JSON format for the encoded data. + + Returns: + The JSON format for the encoded data. + """ + return 'base64' + + +class Base64UrlEncoder(EncoderProtocol): + """URL-safe Base64 encoder.""" + + @classmethod + def decode(cls, data: bytes) -> bytes: + """Decode the data from base64 encoded bytes to original bytes data. + + Args: + data: The data to decode. + + Returns: + The decoded data. + """ + try: + return base64.urlsafe_b64decode(data) + except ValueError as e: + raise PydanticCustomError('base64_decode', "Base64 decoding error: '{error}'", {'error': str(e)}) + + @classmethod + def encode(cls, value: bytes) -> bytes: + """Encode the data from bytes to a base64 encoded bytes. + + Args: + value: The data to encode. + + Returns: + The encoded data. + """ + return base64.urlsafe_b64encode(value) + + @classmethod + def get_json_format(cls) -> Literal['base64url']: + """Get the JSON format for the encoded data. + + Returns: + The JSON format for the encoded data. + """ + return 'base64url' + + +@_dataclasses.dataclass(**_internal_dataclass.slots_true) +class EncodedBytes: + """A bytes type that is encoded and decoded using the specified encoder. + + `EncodedBytes` needs an encoder that implements `EncoderProtocol` to operate. + + ```python + from typing import Annotated + + from pydantic import BaseModel, EncodedBytes, EncoderProtocol, ValidationError + + class MyEncoder(EncoderProtocol): + @classmethod + def decode(cls, data: bytes) -> bytes: + if data == b'**undecodable**': + raise ValueError('Cannot decode data') + return data[13:] + + @classmethod + def encode(cls, value: bytes) -> bytes: + return b'**encoded**: ' + value + + @classmethod + def get_json_format(cls) -> str: + return 'my-encoder' + + MyEncodedBytes = Annotated[bytes, EncodedBytes(encoder=MyEncoder)] + + class Model(BaseModel): + my_encoded_bytes: MyEncodedBytes + + # Initialize the model with encoded data + m = Model(my_encoded_bytes=b'**encoded**: some bytes') + + # Access decoded value + print(m.my_encoded_bytes) + #> b'some bytes' + + # Serialize into the encoded form + print(m.model_dump()) + #> {'my_encoded_bytes': b'**encoded**: some bytes'} + + # Validate encoded data + try: + Model(my_encoded_bytes=b'**undecodable**') + except ValidationError as e: + print(e) + ''' + 1 validation error for Model + my_encoded_bytes + Value error, Cannot decode data [type=value_error, input_value=b'**undecodable**', input_type=bytes] + ''' + ``` + """ + + encoder: type[EncoderProtocol] + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> JsonSchemaValue: + field_schema = handler(core_schema) + field_schema.update(type='string', format=self.encoder.get_json_format()) + return field_schema + + def __get_pydantic_core_schema__(self, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + schema = handler(source) + _check_annotated_type(schema['type'], 'bytes', self.__class__.__name__) + return core_schema.with_info_after_validator_function( + function=self.decode, + schema=schema, + serialization=core_schema.plain_serializer_function_ser_schema(function=self.encode), + ) + + def decode(self, data: bytes, _: core_schema.ValidationInfo) -> bytes: + """Decode the data using the specified encoder. + + Args: + data: The data to decode. + + Returns: + The decoded data. + """ + return self.encoder.decode(data) + + def encode(self, value: bytes) -> bytes: + """Encode the data using the specified encoder. + + Args: + value: The data to encode. + + Returns: + The encoded data. + """ + return self.encoder.encode(value) + + def __hash__(self) -> int: + return hash(self.encoder) + + +@_dataclasses.dataclass(**_internal_dataclass.slots_true) +class EncodedStr: + """A str type that is encoded and decoded using the specified encoder. + + `EncodedStr` needs an encoder that implements `EncoderProtocol` to operate. + + ```python + from typing import Annotated + + from pydantic import BaseModel, EncodedStr, EncoderProtocol, ValidationError + + class MyEncoder(EncoderProtocol): + @classmethod + def decode(cls, data: bytes) -> bytes: + if data == b'**undecodable**': + raise ValueError('Cannot decode data') + return data[13:] + + @classmethod + def encode(cls, value: bytes) -> bytes: + return b'**encoded**: ' + value + + @classmethod + def get_json_format(cls) -> str: + return 'my-encoder' + + MyEncodedStr = Annotated[str, EncodedStr(encoder=MyEncoder)] + + class Model(BaseModel): + my_encoded_str: MyEncodedStr + + # Initialize the model with encoded data + m = Model(my_encoded_str='**encoded**: some str') + + # Access decoded value + print(m.my_encoded_str) + #> some str + + # Serialize into the encoded form + print(m.model_dump()) + #> {'my_encoded_str': '**encoded**: some str'} + + # Validate encoded data + try: + Model(my_encoded_str='**undecodable**') + except ValidationError as e: + print(e) + ''' + 1 validation error for Model + my_encoded_str + Value error, Cannot decode data [type=value_error, input_value='**undecodable**', input_type=str] + ''' + ``` + """ + + encoder: type[EncoderProtocol] + + def __get_pydantic_json_schema__( + self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> JsonSchemaValue: + field_schema = handler(core_schema) + field_schema.update(type='string', format=self.encoder.get_json_format()) + return field_schema + + def __get_pydantic_core_schema__(self, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema: + schema = handler(source) + _check_annotated_type(schema['type'], 'str', self.__class__.__name__) + return core_schema.with_info_after_validator_function( + function=self.decode_str, + schema=schema, + serialization=core_schema.plain_serializer_function_ser_schema(function=self.encode_str), + ) + + def decode_str(self, data: str, _: core_schema.ValidationInfo) -> str: + """Decode the data using the specified encoder. + + Args: + data: The data to decode. + + Returns: + The decoded data. + """ + return self.encoder.decode(data.encode()).decode() + + def encode_str(self, value: str) -> str: + """Encode the data using the specified encoder. + + Args: + value: The data to encode. + + Returns: + The encoded data. + """ + return self.encoder.encode(value.encode()).decode() # noqa: UP008 + + def __hash__(self) -> int: + return hash(self.encoder) + + +Base64Bytes = Annotated[bytes, EncodedBytes(encoder=Base64Encoder)] +"""A bytes type that is encoded and decoded using the standard (non-URL-safe) base64 encoder. + +Note: + Under the hood, `Base64Bytes` uses the standard library `base64.b64encode` and `base64.b64decode` functions. + + As a result, attempting to decode url-safe base64 data using the `Base64Bytes` type may fail or produce an incorrect + decoding. + +Warning: + In versions of Pydantic prior to v2.10, `Base64Bytes` used [`base64.encodebytes`][base64.encodebytes] + and [`base64.decodebytes`][base64.decodebytes] functions. According to the [base64 documentation](https://docs.python.org/3/library/base64.html), + these methods are considered legacy implementation, and thus, Pydantic v2.10+ now uses the modern + [`base64.b64encode`][base64.b64encode] and [`base64.b64decode`][base64.b64decode] functions. + + If you'd still like to use these legacy encoders / decoders, you can achieve this by creating a custom annotated type, + like follows: + + ```python + import base64 + from typing import Annotated, Literal + + from pydantic_core import PydanticCustomError + + from pydantic import EncodedBytes, EncoderProtocol + + class LegacyBase64Encoder(EncoderProtocol): + @classmethod + def decode(cls, data: bytes) -> bytes: + try: + return base64.decodebytes(data) + except ValueError as e: + raise PydanticCustomError( + 'base64_decode', + "Base64 decoding error: '{error}'", + {'error': str(e)}, + ) + + @classmethod + def encode(cls, value: bytes) -> bytes: + return base64.encodebytes(value) + + @classmethod + def get_json_format(cls) -> Literal['base64']: + return 'base64' + + LegacyBase64Bytes = Annotated[bytes, EncodedBytes(encoder=LegacyBase64Encoder)] + ``` + +```python +from pydantic import Base64Bytes, BaseModel, ValidationError + +class Model(BaseModel): + base64_bytes: Base64Bytes + +# Initialize the model with base64 data +m = Model(base64_bytes=b'VGhpcyBpcyB0aGUgd2F5') + +# Access decoded value +print(m.base64_bytes) +#> b'This is the way' + +# Serialize into the base64 form +print(m.model_dump()) +#> {'base64_bytes': b'VGhpcyBpcyB0aGUgd2F5'} + +# Validate base64 data +try: + print(Model(base64_bytes=b'undecodable').base64_bytes) +except ValidationError as e: + print(e) + ''' + 1 validation error for Model + base64_bytes + Base64 decoding error: 'Incorrect padding' [type=base64_decode, input_value=b'undecodable', input_type=bytes] + ''' +``` +""" +Base64Str = Annotated[str, EncodedStr(encoder=Base64Encoder)] +"""A str type that is encoded and decoded using the standard (non-URL-safe) base64 encoder. + +Note: + Under the hood, `Base64Str` uses the standard library `base64.b64encode` and `base64.b64decode` functions. + + As a result, attempting to decode url-safe base64 data using the `Base64Str` type may fail or produce an incorrect + decoding. + +Warning: + In versions of Pydantic prior to v2.10, `Base64Str` used [`base64.encodebytes`][base64.encodebytes] + and [`base64.decodebytes`][base64.decodebytes] functions. According to the [base64 documentation](https://docs.python.org/3/library/base64.html), + these methods are considered legacy implementation, and thus, Pydantic v2.10+ now uses the modern + [`base64.b64encode`][base64.b64encode] and [`base64.b64decode`][base64.b64decode] functions. + + See the [`Base64Bytes`][pydantic.types.Base64Bytes] type for more information on how to + replicate the old behavior with the legacy encoders / decoders. + +```python +from pydantic import Base64Str, BaseModel, ValidationError + +class Model(BaseModel): + base64_str: Base64Str + +# Initialize the model with base64 data +m = Model(base64_str='VGhlc2UgYXJlbid0IHRoZSBkcm9pZHMgeW91J3JlIGxvb2tpbmcgZm9y') + +# Access decoded value +print(m.base64_str) +#> These aren't the droids you're looking for + +# Serialize into the base64 form +print(m.model_dump()) +#> {'base64_str': 'VGhlc2UgYXJlbid0IHRoZSBkcm9pZHMgeW91J3JlIGxvb2tpbmcgZm9y'} + +# Validate base64 data +try: + print(Model(base64_str='undecodable').base64_str) +except ValidationError as e: + print(e) + ''' + 1 validation error for Model + base64_str + Base64 decoding error: 'Incorrect padding' [type=base64_decode, input_value='undecodable', input_type=str] + ''' +``` +""" +Base64UrlBytes = Annotated[bytes, EncodedBytes(encoder=Base64UrlEncoder)] +"""A bytes type that is encoded and decoded using the URL-safe base64 encoder. + +Note: + Under the hood, `Base64UrlBytes` use standard library `base64.urlsafe_b64encode` and `base64.urlsafe_b64decode` + functions. + + As a result, the `Base64UrlBytes` type can be used to faithfully decode "vanilla" base64 data + (using `'+'` and `'/'`). + +```python +from pydantic import Base64UrlBytes, BaseModel + +class Model(BaseModel): + base64url_bytes: Base64UrlBytes + +# Initialize the model with base64 data +m = Model(base64url_bytes=b'SHc_dHc-TXc==') +print(m) +#> base64url_bytes=b'Hw?tw>Mw' +``` +""" +Base64UrlStr = Annotated[str, EncodedStr(encoder=Base64UrlEncoder)] +"""A str type that is encoded and decoded using the URL-safe base64 encoder. + +Note: + Under the hood, `Base64UrlStr` use standard library `base64.urlsafe_b64encode` and `base64.urlsafe_b64decode` + functions. + + As a result, the `Base64UrlStr` type can be used to faithfully decode "vanilla" base64 data (using `'+'` and `'/'`). + +```python +from pydantic import Base64UrlStr, BaseModel + +class Model(BaseModel): + base64url_str: Base64UrlStr + +# Initialize the model with base64 data +m = Model(base64url_str='SHc_dHc-TXc==') +print(m) +#> base64url_str='Hw?tw>Mw' +``` +""" + + +__getattr__ = getattr_migration(__name__) + + +@_dataclasses.dataclass(**_internal_dataclass.slots_true) +class GetPydanticSchema: + """!!! abstract "Usage Documentation" + [Using `GetPydanticSchema` to Reduce Boilerplate](../concepts/types.md#using-getpydanticschema-to-reduce-boilerplate) + + A convenience class for creating an annotation that provides pydantic custom type hooks. + + This class is intended to eliminate the need to create a custom "marker" which defines the + `__get_pydantic_core_schema__` and `__get_pydantic_json_schema__` custom hook methods. + + For example, to have a field treated by type checkers as `int`, but by pydantic as `Any`, you can do: + ```python + from typing import Annotated, Any + + from pydantic import BaseModel, GetPydanticSchema + + HandleAsAny = GetPydanticSchema(lambda _s, h: h(Any)) + + class Model(BaseModel): + x: Annotated[int, HandleAsAny] # pydantic sees `x: Any` + + print(repr(Model(x='abc').x)) + #> 'abc' + ``` + """ + + get_pydantic_core_schema: Callable[[Any, GetCoreSchemaHandler], CoreSchema] | None = None + get_pydantic_json_schema: Callable[[Any, GetJsonSchemaHandler], JsonSchemaValue] | None = None + + # Note: we may want to consider adding a convenience staticmethod `def for_type(type_: Any) -> GetPydanticSchema:` + # which returns `GetPydanticSchema(lambda _s, h: h(type_))` + + if not TYPE_CHECKING: + # We put `__getattr__` in a non-TYPE_CHECKING block because otherwise, mypy allows arbitrary attribute access + + def __getattr__(self, item: str) -> Any: + """Use this rather than defining `__get_pydantic_core_schema__` etc. to reduce the number of nested calls.""" + if item == '__get_pydantic_core_schema__' and self.get_pydantic_core_schema: + return self.get_pydantic_core_schema + elif item == '__get_pydantic_json_schema__' and self.get_pydantic_json_schema: + return self.get_pydantic_json_schema + else: + return object.__getattribute__(self, item) + + __hash__ = object.__hash__ + + +@_dataclasses.dataclass(**_internal_dataclass.slots_true, frozen=True) +class Tag: + """Provides a way to specify the expected tag to use for a case of a (callable) discriminated union. + + Also provides a way to label a union case in error messages. + + When using a callable `Discriminator`, attach a `Tag` to each case in the `Union` to specify the tag that + should be used to identify that case. For example, in the below example, the `Tag` is used to specify that + if `get_discriminator_value` returns `'apple'`, the input should be validated as an `ApplePie`, and if it + returns `'pumpkin'`, the input should be validated as a `PumpkinPie`. + + The primary role of the `Tag` here is to map the return value from the callable `Discriminator` function to + the appropriate member of the `Union` in question. + + ```python + from typing import Annotated, Any, Literal, Union + + from pydantic import BaseModel, Discriminator, Tag + + class Pie(BaseModel): + time_to_cook: int + num_ingredients: int + + class ApplePie(Pie): + fruit: Literal['apple'] = 'apple' + + class PumpkinPie(Pie): + filling: Literal['pumpkin'] = 'pumpkin' + + def get_discriminator_value(v: Any) -> str: + if isinstance(v, dict): + return v.get('fruit', v.get('filling')) + return getattr(v, 'fruit', getattr(v, 'filling', None)) + + class ThanksgivingDinner(BaseModel): + dessert: Annotated[ + Union[ + Annotated[ApplePie, Tag('apple')], + Annotated[PumpkinPie, Tag('pumpkin')], + ], + Discriminator(get_discriminator_value), + ] + + apple_variation = ThanksgivingDinner.model_validate( + {'dessert': {'fruit': 'apple', 'time_to_cook': 60, 'num_ingredients': 8}} + ) + print(repr(apple_variation)) + ''' + ThanksgivingDinner(dessert=ApplePie(time_to_cook=60, num_ingredients=8, fruit='apple')) + ''' + + pumpkin_variation = ThanksgivingDinner.model_validate( + { + 'dessert': { + 'filling': 'pumpkin', + 'time_to_cook': 40, + 'num_ingredients': 6, + } + } + ) + print(repr(pumpkin_variation)) + ''' + ThanksgivingDinner(dessert=PumpkinPie(time_to_cook=40, num_ingredients=6, filling='pumpkin')) + ''' + ``` + + !!! note + You must specify a `Tag` for every case in a `Tag` that is associated with a + callable `Discriminator`. Failing to do so will result in a `PydanticUserError` with code + [`callable-discriminator-no-tag`](../errors/usage_errors.md#callable-discriminator-no-tag). + + See the [Discriminated Unions] concepts docs for more details on how to use `Tag`s. + + [Discriminated Unions]: ../concepts/unions.md#discriminated-unions + """ + + tag: str + + def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: + schema = handler(source_type) + metadata = cast('CoreMetadata', schema.setdefault('metadata', {})) + metadata['pydantic_internal_union_tag_key'] = self.tag + return schema + + +@_dataclasses.dataclass(**_internal_dataclass.slots_true, frozen=True) +class Discriminator: + """!!! abstract "Usage Documentation" + [Discriminated Unions with `Callable` `Discriminator`](../concepts/unions.md#discriminated-unions-with-callable-discriminator) + + Provides a way to use a custom callable as the way to extract the value of a union discriminator. + + This allows you to get validation behavior like you'd get from `Field(discriminator=)`, + but without needing to have a single shared field across all the union choices. This also makes it + possible to handle unions of models and primitive types with discriminated-union-style validation errors. + Finally, this allows you to use a custom callable as the way to identify which member of a union a value + belongs to, while still seeing all the performance benefits of a discriminated union. + + Consider this example, which is much more performant with the use of `Discriminator` and thus a `TaggedUnion` + than it would be as a normal `Union`. + + ```python + from typing import Annotated, Any, Literal, Union + + from pydantic import BaseModel, Discriminator, Tag + + class Pie(BaseModel): + time_to_cook: int + num_ingredients: int + + class ApplePie(Pie): + fruit: Literal['apple'] = 'apple' + + class PumpkinPie(Pie): + filling: Literal['pumpkin'] = 'pumpkin' + + def get_discriminator_value(v: Any) -> str: + if isinstance(v, dict): + return v.get('fruit', v.get('filling')) + return getattr(v, 'fruit', getattr(v, 'filling', None)) + + class ThanksgivingDinner(BaseModel): + dessert: Annotated[ + Union[ + Annotated[ApplePie, Tag('apple')], + Annotated[PumpkinPie, Tag('pumpkin')], + ], + Discriminator(get_discriminator_value), + ] + + apple_variation = ThanksgivingDinner.model_validate( + {'dessert': {'fruit': 'apple', 'time_to_cook': 60, 'num_ingredients': 8}} + ) + print(repr(apple_variation)) + ''' + ThanksgivingDinner(dessert=ApplePie(time_to_cook=60, num_ingredients=8, fruit='apple')) + ''' + + pumpkin_variation = ThanksgivingDinner.model_validate( + { + 'dessert': { + 'filling': 'pumpkin', + 'time_to_cook': 40, + 'num_ingredients': 6, + } + } + ) + print(repr(pumpkin_variation)) + ''' + ThanksgivingDinner(dessert=PumpkinPie(time_to_cook=40, num_ingredients=6, filling='pumpkin')) + ''' + ``` + + See the [Discriminated Unions] concepts docs for more details on how to use `Discriminator`s. + + [Discriminated Unions]: ../concepts/unions.md#discriminated-unions + """ + + discriminator: str | Callable[[Any], Hashable] + """The callable or field name for discriminating the type in a tagged union. + + A `Callable` discriminator must extract the value of the discriminator from the input. + A `str` discriminator must be the name of a field to discriminate against. + """ + custom_error_type: str | None = None + """Type to use in [custom errors](../errors/errors.md) replacing the standard discriminated union + validation errors. + """ + custom_error_message: str | None = None + """Message to use in custom errors.""" + custom_error_context: dict[str, int | str | float] | None = None + """Context to use in custom errors.""" + + def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: + if not is_union_origin(get_origin(source_type)): + raise TypeError(f'{type(self).__name__} must be used with a Union type, not {source_type}') + + if isinstance(self.discriminator, str): + from pydantic import Field + + return handler(Annotated[source_type, Field(discriminator=self.discriminator)]) + else: + original_schema = handler(source_type) + return self._convert_schema(original_schema, handler) + + def _convert_schema( + self, original_schema: core_schema.CoreSchema, handler: GetCoreSchemaHandler | None = None + ) -> core_schema.TaggedUnionSchema: + if original_schema['type'] != 'union': + # This likely indicates that the schema was a single-item union that was simplified. + # In this case, we do the same thing we do in + # `pydantic._internal._discriminated_union._ApplyInferredDiscriminator._apply_to_root`, namely, + # package the generated schema back into a single-item union. + original_schema = core_schema.union_schema([original_schema]) + + tagged_union_choices = {} + for choice in original_schema['choices']: + tag = None + if isinstance(choice, tuple): + choice, tag = choice + metadata = cast('CoreMetadata | None', choice.get('metadata')) + if metadata is not None: + tag = metadata.get('pydantic_internal_union_tag_key') or tag + if tag is None: + # `handler` is None when this method is called from `apply_discriminator()` (deferred discriminators) + if handler is not None and choice['type'] == 'definition-ref': + # If choice was built from a PEP 695 type alias, try to resolve the def: + try: + choice = handler.resolve_ref_schema(choice) + except LookupError: + pass + else: + metadata = cast('CoreMetadata | None', choice.get('metadata')) + if metadata is not None: + tag = metadata.get('pydantic_internal_union_tag_key') + + if tag is None: + raise PydanticUserError( + f'`Tag` not provided for choice {choice} used with `Discriminator`', + code='callable-discriminator-no-tag', + ) + tagged_union_choices[tag] = choice + + # Have to do these verbose checks to ensure falsy values ('' and {}) don't get ignored + custom_error_type = self.custom_error_type + if custom_error_type is None: + custom_error_type = original_schema.get('custom_error_type') + + custom_error_message = self.custom_error_message + if custom_error_message is None: + custom_error_message = original_schema.get('custom_error_message') + + custom_error_context = self.custom_error_context + if custom_error_context is None: + custom_error_context = original_schema.get('custom_error_context') + + custom_error_type = original_schema.get('custom_error_type') if custom_error_type is None else custom_error_type + return core_schema.tagged_union_schema( + tagged_union_choices, + self.discriminator, + custom_error_type=custom_error_type, + custom_error_message=custom_error_message, + custom_error_context=custom_error_context, + strict=original_schema.get('strict'), + ref=original_schema.get('ref'), + metadata=original_schema.get('metadata'), + serialization=original_schema.get('serialization'), + ) + + +_JSON_TYPES = {int, float, str, bool, list, dict, type(None)} + + +def _get_type_name(x: Any) -> str: + type_ = type(x) + if type_ in _JSON_TYPES: + return type_.__name__ + + # Handle proper subclasses; note we don't need to handle None or bool here + if isinstance(x, int): + return 'int' + if isinstance(x, float): + return 'float' + if isinstance(x, str): + return 'str' + if isinstance(x, list): + return 'list' + if isinstance(x, dict): + return 'dict' + + # Fail by returning the type's actual name + return getattr(type_, '__name__', '') + + +class _AllowAnyJson: + @classmethod + def __get_pydantic_core_schema__(cls, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: + python_schema = handler(source_type) + return core_schema.json_or_python_schema(json_schema=core_schema.any_schema(), python_schema=python_schema) + + +if TYPE_CHECKING: + # This seems to only be necessary for mypy + JsonValue: TypeAlias = Union[ + list['JsonValue'], + dict[str, 'JsonValue'], + str, + bool, + int, + float, + None, + ] + """A `JsonValue` is used to represent a value that can be serialized to JSON. + + It may be one of: + + * `list['JsonValue']` + * `dict[str, 'JsonValue']` + * `str` + * `bool` + * `int` + * `float` + * `None` + + The following example demonstrates how to use `JsonValue` to validate JSON data, + and what kind of errors to expect when input data is not json serializable. + + ```python + import json + + from pydantic import BaseModel, JsonValue, ValidationError + + class Model(BaseModel): + j: JsonValue + + valid_json_data = {'j': {'a': {'b': {'c': 1, 'd': [2, None]}}}} + invalid_json_data = {'j': {'a': {'b': ...}}} + + print(repr(Model.model_validate(valid_json_data))) + #> Model(j={'a': {'b': {'c': 1, 'd': [2, None]}}}) + print(repr(Model.model_validate_json(json.dumps(valid_json_data)))) + #> Model(j={'a': {'b': {'c': 1, 'd': [2, None]}}}) + + try: + Model.model_validate(invalid_json_data) + except ValidationError as e: + print(e) + ''' + 1 validation error for Model + j.dict.a.dict.b + input was not a valid JSON value [type=invalid-json-value, input_value=Ellipsis, input_type=ellipsis] + ''' + ``` + """ + +else: + JsonValue = TypeAliasType( + 'JsonValue', + Annotated[ + Union[ + Annotated[list['JsonValue'], Tag('list')], + Annotated[dict[str, 'JsonValue'], Tag('dict')], + Annotated[str, Tag('str')], + Annotated[bool, Tag('bool')], + Annotated[int, Tag('int')], + Annotated[float, Tag('float')], + Annotated[None, Tag('NoneType')], + ], + Discriminator( + _get_type_name, + custom_error_type='invalid-json-value', + custom_error_message='input was not a valid JSON value', + ), + _AllowAnyJson, + ], + ) + + +class _OnErrorOmit: + @classmethod + def __get_pydantic_core_schema__(cls, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema: + # there is no actual default value here but we use with_default_schema since it already has the on_error + # behavior implemented and it would be no more efficient to implement it on every other validator + # or as a standalone validator + return core_schema.with_default_schema(schema=handler(source_type), on_error='omit') + + +OnErrorOmit = Annotated[T, _OnErrorOmit] +""" +When used as an item in a list, the key type in a dict, optional values of a TypedDict, etc. +this annotation omits the item from the iteration if there is any error validating it. +That is, instead of a [`ValidationError`][pydantic_core.ValidationError] being propagated up and the entire iterable being discarded +any invalid items are discarded and the valid ones are returned. +""" + + +@_dataclasses.dataclass +class FailFast(_fields.PydanticMetadata, BaseMetadata): + """A `FailFast` annotation can be used to specify that validation should stop at the first error. + + This can be useful when you want to validate a large amount of data and you only need to know if it's valid or not. + + You might want to enable this setting if you want to validate your data faster (basically, if you use this, + validation will be more performant with the caveat that you get less information). + + ```python + from typing import Annotated + + from pydantic import BaseModel, FailFast, ValidationError + + class Model(BaseModel): + x: Annotated[list[int], FailFast()] + + # This will raise a single error for the first invalid value and stop validation + try: + obj = Model(x=[1, 2, 'a', 4, 5, 'b', 7, 8, 9, 'c']) + except ValidationError as e: + print(e) + ''' + 1 validation error for Model + x.2 + Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str] + ''' + ``` + """ + + fail_fast: bool = True diff --git a/.venv/lib/python3.12/site-packages/pydantic/typing.py b/.venv/lib/python3.12/site-packages/pydantic/typing.py new file mode 100644 index 0000000000000000000000000000000000000000..0bda22d02ffc59f306ded475805343aef2855f49 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/typing.py @@ -0,0 +1,5 @@ +"""`typing` module is a backport module from V1.""" + +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.venv/lib/python3.12/site-packages/pydantic/utils.py b/.venv/lib/python3.12/site-packages/pydantic/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..8d1e2a81c5f0d1bd858bf12ccf15b1461008cf99 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/utils.py @@ -0,0 +1,5 @@ +"""The `utils` module is a backport module from V1.""" + +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.venv/lib/python3.12/site-packages/pydantic/validate_call_decorator.py b/.venv/lib/python3.12/site-packages/pydantic/validate_call_decorator.py new file mode 100644 index 0000000000000000000000000000000000000000..fe4d9c9b6c5ef3a98e4f2770d853d92d8a0124b0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/validate_call_decorator.py @@ -0,0 +1,116 @@ +"""Decorator for validating function calls.""" + +from __future__ import annotations as _annotations + +import inspect +from functools import partial +from types import BuiltinFunctionType +from typing import TYPE_CHECKING, Any, Callable, TypeVar, cast, overload + +from ._internal import _generate_schema, _typing_extra, _validate_call +from .errors import PydanticUserError + +__all__ = ('validate_call',) + +if TYPE_CHECKING: + from .config import ConfigDict + + AnyCallableT = TypeVar('AnyCallableT', bound=Callable[..., Any]) + + +_INVALID_TYPE_ERROR_CODE = 'validate-call-type' + + +def _check_function_type(function: object) -> None: + """Check if the input function is a supported type for `validate_call`.""" + if isinstance(function, _generate_schema.VALIDATE_CALL_SUPPORTED_TYPES): + try: + inspect.signature(cast(_generate_schema.ValidateCallSupportedTypes, function)) + except ValueError: + raise PydanticUserError( + f"Input function `{function}` doesn't have a valid signature", code=_INVALID_TYPE_ERROR_CODE + ) + + if isinstance(function, partial): + try: + assert not isinstance(partial.func, partial), 'Partial of partial' + _check_function_type(function.func) + except PydanticUserError as e: + raise PydanticUserError( + f'Partial of `{function.func}` is invalid because the type of `{function.func}` is not supported by `validate_call`', + code=_INVALID_TYPE_ERROR_CODE, + ) from e + + return + + if isinstance(function, BuiltinFunctionType): + raise PydanticUserError(f'Input built-in function `{function}` is not supported', code=_INVALID_TYPE_ERROR_CODE) + if isinstance(function, (classmethod, staticmethod, property)): + name = type(function).__name__ + raise PydanticUserError( + f'The `@{name}` decorator should be applied after `@validate_call` (put `@{name}` on top)', + code=_INVALID_TYPE_ERROR_CODE, + ) + + if inspect.isclass(function): + raise PydanticUserError( + f'Unable to validate {function}: `validate_call` should be applied to functions, not classes (put `@validate_call` on top of `__init__` or `__new__` instead)', + code=_INVALID_TYPE_ERROR_CODE, + ) + if callable(function): + raise PydanticUserError( + f'Unable to validate {function}: `validate_call` should be applied to functions, not instances or other callables. Use `validate_call` explicitly on `__call__` instead.', + code=_INVALID_TYPE_ERROR_CODE, + ) + + raise PydanticUserError( + f'Unable to validate {function}: `validate_call` should be applied to one of the following: function, method, partial, or lambda', + code=_INVALID_TYPE_ERROR_CODE, + ) + + +@overload +def validate_call( + *, config: ConfigDict | None = None, validate_return: bool = False +) -> Callable[[AnyCallableT], AnyCallableT]: ... + + +@overload +def validate_call(func: AnyCallableT, /) -> AnyCallableT: ... + + +def validate_call( + func: AnyCallableT | None = None, + /, + *, + config: ConfigDict | None = None, + validate_return: bool = False, +) -> AnyCallableT | Callable[[AnyCallableT], AnyCallableT]: + """!!! abstract "Usage Documentation" + [Validation Decorator](../concepts/validation_decorator.md) + + Returns a decorated wrapper around the function that validates the arguments and, optionally, the return value. + + Usage may be either as a plain decorator `@validate_call` or with arguments `@validate_call(...)`. + + Args: + func: The function to be decorated. + config: The configuration dictionary. + validate_return: Whether to validate the return value. + + Returns: + The decorated function. + """ + parent_namespace = _typing_extra.parent_frame_namespace() + + def validate(function: AnyCallableT) -> AnyCallableT: + _check_function_type(function) + validate_call_wrapper = _validate_call.ValidateCallWrapper( + cast(_generate_schema.ValidateCallSupportedTypes, function), config, validate_return, parent_namespace + ) + return _validate_call.update_wrapper_attributes(function, validate_call_wrapper.__call__) # type: ignore + + if func is not None: + return validate(func) + else: + return validate diff --git a/.venv/lib/python3.12/site-packages/pydantic/validators.py b/.venv/lib/python3.12/site-packages/pydantic/validators.py new file mode 100644 index 0000000000000000000000000000000000000000..7921b04f05d5ef0ad20365e7372a48d86724dc51 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/validators.py @@ -0,0 +1,5 @@ +"""The `validators` module is a backport module from V1.""" + +from ._migration import getattr_migration + +__getattr__ = getattr_migration(__name__) diff --git a/.venv/lib/python3.12/site-packages/pydantic/version.py b/.venv/lib/python3.12/site-packages/pydantic/version.py new file mode 100644 index 0000000000000000000000000000000000000000..9a4322debf876de027b94b491f9f755eb10c6938 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/version.py @@ -0,0 +1,113 @@ +"""The `version` module holds the version information for Pydantic.""" + +from __future__ import annotations as _annotations + +import sys + +from pydantic_core import __version__ as __pydantic_core_version__ + +__all__ = 'VERSION', 'version_info' + +VERSION = '2.12.2' +"""The version of Pydantic. + +This version specifier is guaranteed to be compliant with the [specification], +introduced by [PEP 440]. + +[specification]: https://packaging.python.org/en/latest/specifications/version-specifiers/ +[PEP 440]: https://peps.python.org/pep-0440/ +""" + +# Keep this in sync with the version constraint in the `pyproject.toml` dependencies: +_COMPATIBLE_PYDANTIC_CORE_VERSION = '2.41.4' + + +def version_short() -> str: + """Return the `major.minor` part of Pydantic version. + + It returns '2.1' if Pydantic version is '2.1.1'. + """ + return '.'.join(VERSION.split('.')[:2]) + + +def version_info() -> str: + """Return complete version information for Pydantic and its dependencies.""" + import importlib.metadata + import platform + from pathlib import Path + + import pydantic_core._pydantic_core as pdc + + from ._internal import _git as git + + # get data about packages that are closely related to pydantic, use pydantic or often conflict with pydantic + package_names = { + 'email-validator', + 'fastapi', + 'mypy', + 'pydantic-extra-types', + 'pydantic-settings', + 'pyright', + 'typing_extensions', + } + related_packages = [] + + for dist in importlib.metadata.distributions(): + name = dist.metadata['Name'] + if name in package_names: + related_packages.append(f'{name}-{dist.version}') + + pydantic_dir = Path(__file__).parents[1].resolve() + most_recent_commit = ( + git.git_revision(pydantic_dir) if git.is_git_repo(pydantic_dir) and git.have_git() else 'unknown' + ) + + info = { + 'pydantic version': VERSION, + 'pydantic-core version': __pydantic_core_version__, + 'pydantic-core build': getattr(pdc, 'build_info', None) or pdc.build_profile, # pyright: ignore[reportPrivateImportUsage] + 'python version': sys.version, + 'platform': platform.platform(), + 'related packages': ' '.join(related_packages), + 'commit': most_recent_commit, + } + return '\n'.join('{:>30} {}'.format(k + ':', str(v).replace('\n', ' ')) for k, v in info.items()) + + +def check_pydantic_core_version() -> bool: + """Check that the installed `pydantic-core` dependency is compatible.""" + return __pydantic_core_version__ == _COMPATIBLE_PYDANTIC_CORE_VERSION + + +def _ensure_pydantic_core_version() -> None: # pragma: no cover + if not check_pydantic_core_version(): + raise_error = True + # Do not raise the error if pydantic is installed in editable mode (i.e. in development): + if sys.version_info >= (3, 13): # origin property added in 3.13 + from importlib.metadata import distribution + + dist = distribution('pydantic') + if getattr(getattr(dist.origin, 'dir_info', None), 'editable', False): + raise_error = False + + if raise_error: + raise SystemError( + f'The installed pydantic-core version ({__pydantic_core_version__}) is incompatible ' + f'with the current pydantic version, which requires {_COMPATIBLE_PYDANTIC_CORE_VERSION}. ' + "If you encounter this error, make sure that you haven't upgraded pydantic-core manually." + ) + + +def parse_mypy_version(version: str) -> tuple[int, int, int]: + """Parse `mypy` string version to a 3-tuple of ints. + + It parses normal version like `1.11.0` and extra info followed by a `+` sign + like `1.11.0+dev.d6d9d8cd4f27c52edac1f537e236ec48a01e54cb.dirty`. + + Args: + version: The mypy version string. + + Returns: + A triple of ints, e.g. `(1, 11, 0)`. + """ + return tuple(map(int, version.partition('+')[0].split('.'))) # pyright: ignore[reportReturnType] diff --git a/.venv/lib/python3.12/site-packages/pydantic/warnings.py b/.venv/lib/python3.12/site-packages/pydantic/warnings.py new file mode 100644 index 0000000000000000000000000000000000000000..4b8578c8ba8e2a49132fa9feb5e6f603ce8b83ee --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic/warnings.py @@ -0,0 +1,114 @@ +"""Pydantic-specific warnings.""" + +from __future__ import annotations as _annotations + +from .version import version_short + +__all__ = ( + 'PydanticDeprecatedSince20', + 'PydanticDeprecatedSince26', + 'PydanticDeprecatedSince29', + 'PydanticDeprecatedSince210', + 'PydanticDeprecatedSince211', + 'PydanticDeprecationWarning', + 'PydanticExperimentalWarning', + 'ArbitraryTypeWarning', + 'UnsupportedFieldAttributeWarning', + 'TypedDictExtraConfigWarning', +) + + +class PydanticDeprecationWarning(DeprecationWarning): + """A Pydantic specific deprecation warning. + + This warning is raised when using deprecated functionality in Pydantic. It provides information on when the + deprecation was introduced and the expected version in which the corresponding functionality will be removed. + + Attributes: + message: Description of the warning. + since: Pydantic version in what the deprecation was introduced. + expected_removal: Pydantic version in what the corresponding functionality expected to be removed. + """ + + message: str + since: tuple[int, int] + expected_removal: tuple[int, int] + + def __init__( + self, message: str, *args: object, since: tuple[int, int], expected_removal: tuple[int, int] | None = None + ) -> None: + super().__init__(message, *args) + self.message = message.rstrip('.') + self.since = since + self.expected_removal = expected_removal if expected_removal is not None else (since[0] + 1, 0) + + def __str__(self) -> str: + message = ( + f'{self.message}. Deprecated in Pydantic V{self.since[0]}.{self.since[1]}' + f' to be removed in V{self.expected_removal[0]}.{self.expected_removal[1]}.' + ) + if self.since == (2, 0): + message += f' See Pydantic V2 Migration Guide at https://errors.pydantic.dev/{version_short()}/migration/' + return message + + +class PydanticDeprecatedSince20(PydanticDeprecationWarning): + """A specific `PydanticDeprecationWarning` subclass defining functionality deprecated since Pydantic 2.0.""" + + def __init__(self, message: str, *args: object) -> None: + super().__init__(message, *args, since=(2, 0), expected_removal=(3, 0)) + + +class PydanticDeprecatedSince26(PydanticDeprecationWarning): + """A specific `PydanticDeprecationWarning` subclass defining functionality deprecated since Pydantic 2.6.""" + + def __init__(self, message: str, *args: object) -> None: + super().__init__(message, *args, since=(2, 6), expected_removal=(3, 0)) + + +class PydanticDeprecatedSince29(PydanticDeprecationWarning): + """A specific `PydanticDeprecationWarning` subclass defining functionality deprecated since Pydantic 2.9.""" + + def __init__(self, message: str, *args: object) -> None: + super().__init__(message, *args, since=(2, 9), expected_removal=(3, 0)) + + +class PydanticDeprecatedSince210(PydanticDeprecationWarning): + """A specific `PydanticDeprecationWarning` subclass defining functionality deprecated since Pydantic 2.10.""" + + def __init__(self, message: str, *args: object) -> None: + super().__init__(message, *args, since=(2, 10), expected_removal=(3, 0)) + + +class PydanticDeprecatedSince211(PydanticDeprecationWarning): + """A specific `PydanticDeprecationWarning` subclass defining functionality deprecated since Pydantic 2.11.""" + + def __init__(self, message: str, *args: object) -> None: + super().__init__(message, *args, since=(2, 11), expected_removal=(3, 0)) + + +class GenericBeforeBaseModelWarning(Warning): + pass + + +class PydanticExperimentalWarning(Warning): + """A Pydantic specific experimental functionality warning. + + It is raised to warn users that the functionality may change or be removed in future versions of Pydantic. + """ + + +class CoreSchemaGenerationWarning(UserWarning): + """A warning raised during core schema generation.""" + + +class ArbitraryTypeWarning(CoreSchemaGenerationWarning): + """A warning raised when Pydantic fails to generate a core schema for an arbitrary type.""" + + +class UnsupportedFieldAttributeWarning(CoreSchemaGenerationWarning): + """A warning raised when a `Field()` attribute isn't supported in the context it is used.""" + + +class TypedDictExtraConfigWarning(CoreSchemaGenerationWarning): + """A warning raised when the [`extra`][pydantic.ConfigDict.extra] configuration is incompatible with the `closed` or `extra_items` specification.""" diff --git a/.venv/lib/python3.12/site-packages/pydantic_core-2.41.4.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/pydantic_core-2.41.4.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic_core-2.41.4.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/pydantic_core-2.41.4.dist-info/METADATA b/.venv/lib/python3.12/site-packages/pydantic_core-2.41.4.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..7f09ec888b5b09b8fb1bd854a317c11fc581e40b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic_core-2.41.4.dist-info/METADATA @@ -0,0 +1,180 @@ +Metadata-Version: 2.4 +Name: pydantic_core +Version: 2.41.4 +Classifier: Development Status :: 3 - Alpha +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 :: Implementation :: GraalPy +Classifier: Programming Language :: Rust +Classifier: Framework :: Pydantic +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Information Technology +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: MacOS +Classifier: Typing :: Typed +Requires-Dist: typing-extensions>=4.14.1 +License-File: LICENSE +Summary: Core functionality for Pydantic validation and serialization +Home-Page: https://github.com/pydantic/pydantic-core +Author-email: Samuel Colvin , Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com>, David Montague , David Hewitt , Sydney Runkle , Victorien Plot +License-Expression: MIT +Requires-Python: >=3.9 +Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM +Project-URL: Homepage, https://github.com/pydantic/pydantic-core +Project-URL: Funding, https://github.com/sponsors/samuelcolvin +Project-URL: Source, https://github.com/pydantic/pydantic-core + +# pydantic-core + +[![CI](https://github.com/pydantic/pydantic-core/workflows/ci/badge.svg?event=push)](https://github.com/pydantic/pydantic-core/actions?query=event%3Apush+branch%3Amain+workflow%3Aci) +[![Coverage](https://codecov.io/gh/pydantic/pydantic-core/branch/main/graph/badge.svg)](https://codecov.io/gh/pydantic/pydantic-core) +[![pypi](https://img.shields.io/pypi/v/pydantic-core.svg)](https://pypi.python.org/pypi/pydantic-core) +[![versions](https://img.shields.io/pypi/pyversions/pydantic-core.svg)](https://github.com/pydantic/pydantic-core) +[![license](https://img.shields.io/github/license/pydantic/pydantic-core.svg)](https://github.com/pydantic/pydantic-core/blob/main/LICENSE) + +This package provides the core functionality for [pydantic](https://docs.pydantic.dev) validation and serialization. + +Pydantic-core is currently around 17x faster than pydantic V1. +See [`tests/benchmarks/`](./tests/benchmarks/) for details. + +## Example of direct usage + +_NOTE: You should not need to use pydantic-core directly; instead, use pydantic, which in turn uses pydantic-core._ + +```py +from pydantic_core import SchemaValidator, ValidationError + + +v = SchemaValidator( + { + 'type': 'typed-dict', + 'fields': { + 'name': { + 'type': 'typed-dict-field', + 'schema': { + 'type': 'str', + }, + }, + 'age': { + 'type': 'typed-dict-field', + 'schema': { + 'type': 'int', + 'ge': 18, + }, + }, + 'is_developer': { + 'type': 'typed-dict-field', + 'schema': { + 'type': 'default', + 'schema': {'type': 'bool'}, + 'default': True, + }, + }, + }, + } +) + +r1 = v.validate_python({'name': 'Samuel', 'age': 35}) +assert r1 == {'name': 'Samuel', 'age': 35, 'is_developer': True} + +# pydantic-core can also validate JSON directly +r2 = v.validate_json('{"name": "Samuel", "age": 35}') +assert r1 == r2 + +try: + v.validate_python({'name': 'Samuel', 'age': 11}) +except ValidationError as e: + print(e) + """ + 1 validation error for model + age + Input should be greater than or equal to 18 + [type=greater_than_equal, context={ge: 18}, input_value=11, input_type=int] + """ +``` + +## Getting Started + +### Prerequisites + +You'll need: +1. **[Rust](https://rustup.rs/)** - Rust stable (or nightly for coverage) +2. **[uv](https://docs.astral.sh/uv/getting-started/installation/)** - Fast Python package manager (will install Python 3.9+ automatically) +3. **[git](https://git-scm.com/)** - For version control +4. **[make](https://www.gnu.org/software/make/)** - For running development commands (or use `nmake` on Windows) + +### Quick Start + +```bash +# Clone the repository (or from your fork) +git clone git@github.com:pydantic/pydantic-core.git +cd pydantic-core + +# Install all dependencies using uv, setup pre-commit hooks, and build the development version +make install +``` + +Verify your installation by running: + +```bash +make +``` + +This runs a full development cycle: formatting, building, linting, and testing + +### Development Commands + +Run `make help` to see all available commands, or use these common ones: + +```bash +make build-dev # to build the package during development +make build-prod # to perform an optimised build for benchmarking +make test # to run the tests +make testcov # to run the tests and generate a coverage report +make lint # to run the linter +make format # to format python and rust code +make all # to run to run build-dev + format + lint + test +``` + +### Useful Resources + +* [`python/pydantic_core/_pydantic_core.pyi`](./python/pydantic_core/_pydantic_core.pyi) - Python API types +* [`python/pydantic_core/core_schema.py`](./python/pydantic_core/core_schema.py) - Core schema definitions +* [`tests/`](./tests) - Comprehensive usage examples + +## Profiling + +It's possible to profile the code using the [`flamegraph` utility from `flamegraph-rs`](https://github.com/flamegraph-rs/flamegraph). (Tested on Linux.) You can install this with `cargo install flamegraph`. + +Run `make build-profiling` to install a release build with debugging symbols included (needed for profiling). + +Once that is built, you can profile pytest benchmarks with (e.g.): + +```bash +flamegraph -- pytest tests/benchmarks/test_micro_benchmarks.py -k test_list_of_ints_core_py --benchmark-enable +``` +The `flamegraph` command will produce an interactive SVG at `flamegraph.svg`. + +## Releasing + +1. Bump package version locally. Do not just edit `Cargo.toml` on Github, you need both `Cargo.toml` and `Cargo.lock` to be updated. +2. Make a PR for the version bump and merge it. +3. Go to https://github.com/pydantic/pydantic-core/releases and click "Draft a new release" +4. In the "Choose a tag" dropdown enter the new tag `v` and select "Create new tag on publish" when the option appears. +5. Enter the release title in the form "v " +6. Click Generate release notes button +7. Click Publish release +8. Go to https://github.com/pydantic/pydantic-core/actions and ensure that all build for release are done successfully. +9. Go to https://pypi.org/project/pydantic-core/ and ensure that the latest release is published. +10. Done 🎉 + diff --git a/.venv/lib/python3.12/site-packages/pydantic_core-2.41.4.dist-info/RECORD b/.venv/lib/python3.12/site-packages/pydantic_core-2.41.4.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..fcb16452b3ddd5e2c71d8cce9e929fedfcb50d58 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic_core-2.41.4.dist-info/RECORD @@ -0,0 +1,11 @@ +pydantic_core-2.41.4.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +pydantic_core-2.41.4.dist-info/METADATA,sha256=NDT7iG_iVO1BRUHzBahuQ_9XUJP49tFcib0RFE7Y4TE,7277 +pydantic_core-2.41.4.dist-info/RECORD,, +pydantic_core-2.41.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pydantic_core-2.41.4.dist-info/WHEEL,sha256=AUS7tHOBvWg1bDsPcHg1j3P_rKxqebEdeR--lIGHkyI,129 +pydantic_core-2.41.4.dist-info/licenses/LICENSE,sha256=Kv3TDVS01itvSIprzBVG6E7FBh8T9CCcA9ASNIeDeVo,1080 +pydantic_core/__init__.py,sha256=nK1ikrdSVK9gapcKrpv_blrp8LCAic1jrK-jkbYHlNI,5115 +pydantic_core/_pydantic_core.cpython-312-x86_64-linux-gnu.so,sha256=3jd8b0nseILu016kLatli4tsXzYZ17CggG3MC-4fOGs,4894624 +pydantic_core/_pydantic_core.pyi,sha256=PqHb1BgvCM-TQfJLPFz323egWzU1_-niNSUSejYXoR8,44927 +pydantic_core/core_schema.py,sha256=u9yFC3LWhRM6DiUP7SY7M2kdzfOBNJLzwOMQAePUYAU,154730 +pydantic_core/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/.venv/lib/python3.12/site-packages/pydantic_core-2.41.4.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/pydantic_core-2.41.4.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/pydantic_core-2.41.4.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/pydantic_core-2.41.4.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..ecc076195c46d7bd21f36f202ceb5977d3100be8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pydantic_core-2.41.4.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: maturin (1.9.6) +Root-Is-Purelib: false +Tag: cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64 diff --git a/.venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/METADATA b/.venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..702ee6da5865b50b297705b077ea7d5904fead58 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/METADATA @@ -0,0 +1,59 @@ +Metadata-Version: 2.4 +Name: PyYAML +Version: 6.0.3 +Summary: YAML parser and emitter for Python +Home-page: https://pyyaml.org/ +Download-URL: https://pypi.org/project/PyYAML/ +Author: Kirill Simonov +Author-email: xi@resolvent.net +License: MIT +Project-URL: Bug Tracker, https://github.com/yaml/pyyaml/issues +Project-URL: CI, https://github.com/yaml/pyyaml/actions +Project-URL: Documentation, https://pyyaml.org/wiki/PyYAMLDocumentation +Project-URL: Mailing lists, http://lists.sourceforge.net/lists/listinfo/yaml-core +Project-URL: Source Code, https://github.com/yaml/pyyaml +Platform: Any +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Cython +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Processing :: Markup +Requires-Python: >=3.8 +License-File: LICENSE +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: download-url +Dynamic: home-page +Dynamic: license +Dynamic: license-file +Dynamic: platform +Dynamic: project-url +Dynamic: requires-python +Dynamic: summary + +YAML is a data serialization format designed for human readability +and interaction with scripting languages. PyYAML is a YAML parser +and emitter for Python. + +PyYAML features a complete YAML 1.1 parser, Unicode support, pickle +support, capable extension API, and sensible error messages. PyYAML +supports standard YAML tags and provides Python-specific tags that +allow to represent an arbitrary Python object. + +PyYAML is applicable for a broad range of tasks from complex +configuration files to object serialization and persistence. diff --git a/.venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/RECORD b/.venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..92fec2f47d09e6397e8b8b3d47826c4fddd4ac9a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/RECORD @@ -0,0 +1,26 @@ +_yaml/__init__.py,sha256=04Ae_5osxahpJHa3XBZUAf4wi6XX32gR8D6X6p64GEA,1402 +pyyaml-6.0.3.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +pyyaml-6.0.3.dist-info/METADATA,sha256=A8O0Fe040J-u3Ek2DpMHabQMWPaFhebeAOLkkWqFjTQ,2351 +pyyaml-6.0.3.dist-info/RECORD,, +pyyaml-6.0.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +pyyaml-6.0.3.dist-info/WHEEL,sha256=DxRnWQz-Kp9-4a4hdDHsSv0KUC3H7sN9Nbef3-8RjXU,190 +pyyaml-6.0.3.dist-info/licenses/LICENSE,sha256=jTko-dxEkP1jVwfLiOsmvXZBAqcoKVQwfT5RZ6V36KQ,1101 +pyyaml-6.0.3.dist-info/top_level.txt,sha256=rpj0IVMTisAjh_1vG3Ccf9v5jpCQwAz6cD1IVU5ZdhQ,11 +yaml/__init__.py,sha256=sZ38wzPWp139cwc5ARZFByUvJxtB07X32FUQAzoFR6c,12311 +yaml/_yaml.cpython-312-x86_64-linux-gnu.so,sha256=lXoJmkUhwfdmkwb7b3nKY_pLmgtMRj98rIM6ZaXFwMs,2679264 +yaml/composer.py,sha256=_Ko30Wr6eDWUeUpauUGT3Lcg9QPBnOPVlTnIMRGJ9FM,4883 +yaml/constructor.py,sha256=kNgkfaeLUkwQYY_Q6Ff1Tz2XVw_pG1xVE9Ak7z-viLA,28639 +yaml/cyaml.py,sha256=6ZrAG9fAYvdVe2FK_w0hmXoG7ZYsoYUwapG8CiC72H0,3851 +yaml/dumper.py,sha256=PLctZlYwZLp7XmeUdwRuv4nYOZ2UBnDIUy8-lKfLF-o,2837 +yaml/emitter.py,sha256=jghtaU7eFwg31bG0B7RZea_29Adi9CKmXq_QjgQpCkQ,43006 +yaml/error.py,sha256=Ah9z-toHJUbE9j-M8YpxgSRM5CgLCcwVzJgLLRF2Fxo,2533 +yaml/events.py,sha256=50_TksgQiE4up-lKo_V-nBy-tAIxkIPQxY5qDhKCeHw,2445 +yaml/loader.py,sha256=UVa-zIqmkFSCIYq_PgSGm4NSJttHY2Rf_zQ4_b1fHN0,2061 +yaml/nodes.py,sha256=gPKNj8pKCdh2d4gr3gIYINnPOaOxGhJAUiYhGRnPE84,1440 +yaml/parser.py,sha256=ilWp5vvgoHFGzvOZDItFoGjD6D42nhlZrZyjAwa0oJo,25495 +yaml/reader.py,sha256=0dmzirOiDG4Xo41RnuQS7K9rkY3xjHiVasfDMNTqCNw,6794 +yaml/representer.py,sha256=IuWP-cAW9sHKEnS0gCqSa894k1Bg4cgTxaDwIcbRQ-Y,14190 +yaml/resolver.py,sha256=9L-VYfm4mWHxUD1Vg4X7rjDRK_7VZd6b92wzq7Y2IKY,9004 +yaml/scanner.py,sha256=YEM3iLZSaQwXcQRg2l2R4MdT0zGP2F9eHkKGKnHyWQY,51279 +yaml/serializer.py,sha256=ChuFgmhU01hj4xgI8GaKv6vfM2Bujwa9i7d2FAHj7cA,4165 +yaml/tokens.py,sha256=lTQIzSVw8Mg9wv459-TjiOQe6wVziqaRlqX2_89rp54,2573 diff --git a/.venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..f3e8a970f16adf4526f1722547053522d94bf860 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/WHEEL @@ -0,0 +1,7 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: false +Tag: cp312-cp312-manylinux_2_17_x86_64 +Tag: cp312-cp312-manylinux2014_x86_64 +Tag: cp312-cp312-manylinux_2_28_x86_64 + diff --git a/.venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..e6475e911f628412049bc4090d86f23ac403adde --- /dev/null +++ b/.venv/lib/python3.12/site-packages/pyyaml-6.0.3.dist-info/top_level.txt @@ -0,0 +1,2 @@ +_yaml +yaml diff --git a/.venv/lib/python3.12/site-packages/regex-2025.9.18.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/regex-2025.9.18.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/regex-2025.9.18.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/regex-2025.9.18.dist-info/METADATA b/.venv/lib/python3.12/site-packages/regex-2025.9.18.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..7ad71d924bd8e066ce750084bc7dce5c5a129dd8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/regex-2025.9.18.dist-info/METADATA @@ -0,0 +1,1059 @@ +Metadata-Version: 2.4 +Name: regex +Version: 2025.9.18 +Summary: Alternative regular expression module, to replace re. +Author-email: Matthew Barnett +License-Expression: Apache-2.0 AND CNRI-Python +Project-URL: Homepage, https://github.com/mrabarnett/mrab-regex +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +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 :: Scientific/Engineering :: Information Analysis +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: Text Processing +Classifier: Topic :: Text Processing :: General +Requires-Python: >=3.9 +Description-Content-Type: text/x-rst +License-File: LICENSE.txt +Dynamic: license-file + +Introduction +------------ + +This regex implementation is backwards-compatible with the standard 're' module, but offers additional functionality. + +Python 2 +-------- + +Python 2 is no longer supported. The last release that supported Python 2 was 2021.11.10. + +PyPy +---- + +This module is targeted at CPython. It expects that all codepoints are the same width, so it won't behave properly with PyPy outside U+0000..U+007F because PyPy stores strings as UTF-8. + +Multithreading +-------------- + +The regex module releases the GIL during matching on instances of the built-in (immutable) string classes, enabling other Python threads to run concurrently. It is also possible to force the regex module to release the GIL during matching by calling the matching methods with the keyword argument ``concurrent=True``. The behaviour is undefined if the string changes during matching, so use it *only* when it is guaranteed that that won't happen. + +Unicode +------- + +This module supports Unicode 16.0.0. Full Unicode case-folding is supported. + +Flags +----- + +There are 2 kinds of flag: scoped and global. Scoped flags can apply to only part of a pattern and can be turned on or off; global flags apply to the entire pattern and can only be turned on. + +The scoped flags are: ``ASCII (?a)``, ``FULLCASE (?f)``, ``IGNORECASE (?i)``, ``LOCALE (?L)``, ``MULTILINE (?m)``, ``DOTALL (?s)``, ``UNICODE (?u)``, ``VERBOSE (?x)``, ``WORD (?w)``. + +The global flags are: ``BESTMATCH (?b)``, ``ENHANCEMATCH (?e)``, ``POSIX (?p)``, ``REVERSE (?r)``, ``VERSION0 (?V0)``, ``VERSION1 (?V1)``. + +If neither the ``ASCII``, ``LOCALE`` nor ``UNICODE`` flag is specified, it will default to ``UNICODE`` if the regex pattern is a Unicode string and ``ASCII`` if it's a bytestring. + +The ``ENHANCEMATCH`` flag makes fuzzy matching attempt to improve the fit of the next match that it finds. + +The ``BESTMATCH`` flag makes fuzzy matching search for the best match instead of the next match. + +Old vs new behaviour +-------------------- + +In order to be compatible with the re module, this module has 2 behaviours: + +* **Version 0** behaviour (old behaviour, compatible with the re module): + + Please note that the re module's behaviour may change over time, and I'll endeavour to match that behaviour in version 0. + + * Indicated by the ``VERSION0`` flag. + + * Zero-width matches are not handled correctly in the re module before Python 3.7. The behaviour in those earlier versions is: + + * ``.split`` won't split a string at a zero-width match. + + * ``.sub`` will advance by one character after a zero-width match. + + * Inline flags apply to the entire pattern, and they can't be turned off. + + * Only simple sets are supported. + + * Case-insensitive matches in Unicode use simple case-folding by default. + +* **Version 1** behaviour (new behaviour, possibly different from the re module): + + * Indicated by the ``VERSION1`` flag. + + * Zero-width matches are handled correctly. + + * Inline flags apply to the end of the group or pattern, and they can be turned off. + + * Nested sets and set operations are supported. + + * Case-insensitive matches in Unicode use full case-folding by default. + +If no version is specified, the regex module will default to ``regex.DEFAULT_VERSION``. + +Case-insensitive matches in Unicode +----------------------------------- + +The regex module supports both simple and full case-folding for case-insensitive matches in Unicode. Use of full case-folding can be turned on using the ``FULLCASE`` flag. Please note that this flag affects how the ``IGNORECASE`` flag works; the ``FULLCASE`` flag itself does not turn on case-insensitive matching. + +Version 0 behaviour: the flag is off by default. + +Version 1 behaviour: the flag is on by default. + +Nested sets and set operations +------------------------------ + +It's not possible to support both simple sets, as used in the re module, and nested sets at the same time because of a difference in the meaning of an unescaped ``"["`` in a set. + +For example, the pattern ``[[a-z]--[aeiou]]`` is treated in the version 0 behaviour (simple sets, compatible with the re module) as: + +* Set containing "[" and the letters "a" to "z" + +* Literal "--" + +* Set containing letters "a", "e", "i", "o", "u" + +* Literal "]" + +but in the version 1 behaviour (nested sets, enhanced behaviour) as: + +* Set which is: + + * Set containing the letters "a" to "z" + +* but excluding: + + * Set containing the letters "a", "e", "i", "o", "u" + +Version 0 behaviour: only simple sets are supported. + +Version 1 behaviour: nested sets and set operations are supported. + +Notes on named groups +--------------------- + +All groups have a group number, starting from 1. + +Groups with the same group name will have the same group number, and groups with a different group name will have a different group number. + +The same name can be used by more than one group, with later captures 'overwriting' earlier captures. All the captures of the group will be available from the ``captures`` method of the match object. + +Group numbers will be reused across different branches of a branch reset, eg. ``(?|(first)|(second))`` has only group 1. If groups have different group names then they will, of course, have different group numbers, eg. ``(?|(?Pfirst)|(?Psecond))`` has group 1 ("foo") and group 2 ("bar"). + +In the regex ``(\s+)(?|(?P[A-Z]+)|(\w+) (?P[0-9]+)`` there are 2 groups: + +* ``(\s+)`` is group 1. + +* ``(?P[A-Z]+)`` is group 2, also called "foo". + +* ``(\w+)`` is group 2 because of the branch reset. + +* ``(?P[0-9]+)`` is group 2 because it's called "foo". + +If you want to prevent ``(\w+)`` from being group 2, you need to name it (different name, different group number). + +Additional features +------------------- + +The issue numbers relate to the Python bug tracker, except where listed otherwise. + +Added ``\p{Horiz_Space}`` and ``\p{Vert_Space}`` (`GitHub issue 477 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``\p{Horiz_Space}`` or ``\p{H}`` matches horizontal whitespace and ``\p{Vert_Space}`` or ``\p{V}`` matches vertical whitespace. + +Added support for lookaround in conditional pattern (`Hg issue 163 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The test of a conditional pattern can be a lookaround. + +.. sourcecode:: python + + >>> regex.match(r'(?(?=\d)\d+|\w+)', '123abc') + + >>> regex.match(r'(?(?=\d)\d+|\w+)', 'abc123') + + +This is not quite the same as putting a lookaround in the first branch of a pair of alternatives. + +.. sourcecode:: python + + >>> print(regex.match(r'(?:(?=\d)\d+\b|\w+)', '123abc')) + + >>> print(regex.match(r'(?(?=\d)\d+\b|\w+)', '123abc')) + None + +In the first example, the lookaround matched, but the remainder of the first branch failed to match, and so the second branch was attempted, whereas in the second example, the lookaround matched, and the first branch failed to match, but the second branch was **not** attempted. + +Added POSIX matching (leftmost longest) (`Hg issue 150 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The POSIX standard for regex is to return the leftmost longest match. This can be turned on using the ``POSIX`` flag. + +.. sourcecode:: python + + >>> # Normal matching. + >>> regex.search(r'Mr|Mrs', 'Mrs') + + >>> regex.search(r'one(self)?(selfsufficient)?', 'oneselfsufficient') + + >>> # POSIX matching. + >>> regex.search(r'(?p)Mr|Mrs', 'Mrs') + + >>> regex.search(r'(?p)one(self)?(selfsufficient)?', 'oneselfsufficient') + + +Note that it will take longer to find matches because when it finds a match at a certain position, it won't return that immediately, but will keep looking to see if there's another longer match there. + +Added ``(?(DEFINE)...)`` (`Hg issue 152 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If there's no group called "DEFINE", then ... will be ignored except that any groups defined within it can be called and that the normal rules for numbering groups still apply. + +.. sourcecode:: python + + >>> regex.search(r'(?(DEFINE)(?P\d+)(?P\w+))(?&quant) (?&item)', '5 elephants') + + +Added ``(*PRUNE)``, ``(*SKIP)`` and ``(*FAIL)`` (`Hg issue 153 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``(*PRUNE)`` discards the backtracking info up to that point. When used in an atomic group or a lookaround, it won't affect the enclosing pattern. + +``(*SKIP)`` is similar to ``(*PRUNE)``, except that it also sets where in the text the next attempt to match will start. When used in an atomic group or a lookaround, it won't affect the enclosing pattern. + +``(*FAIL)`` causes immediate backtracking. ``(*F)`` is a permitted abbreviation. + +Added ``\K`` (`Hg issue 151 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Keeps the part of the entire match after the position where ``\K`` occurred; the part before it is discarded. + +It does not affect what groups return. + +.. sourcecode:: python + + >>> m = regex.search(r'(\w\w\K\w\w\w)', 'abcdef') + >>> m[0] + 'cde' + >>> m[1] + 'abcde' + >>> + >>> m = regex.search(r'(?r)(\w\w\K\w\w\w)', 'abcdef') + >>> m[0] + 'bc' + >>> m[1] + 'bcdef' + +Added capture subscripting for ``expandf`` and ``subf``/``subfn`` (`Hg issue 133 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +You can use subscripting to get the captures of a repeated group. + +.. sourcecode:: python + + >>> m = regex.match(r"(\w)+", "abc") + >>> m.expandf("{1}") + 'c' + >>> m.expandf("{1[0]} {1[1]} {1[2]}") + 'a b c' + >>> m.expandf("{1[-1]} {1[-2]} {1[-3]}") + 'c b a' + >>> + >>> m = regex.match(r"(?P\w)+", "abc") + >>> m.expandf("{letter}") + 'c' + >>> m.expandf("{letter[0]} {letter[1]} {letter[2]}") + 'a b c' + >>> m.expandf("{letter[-1]} {letter[-2]} {letter[-3]}") + 'c b a' + +Added support for referring to a group by number using ``(?P=...)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This is in addition to the existing ``\g<...>``. + +Fixed the handling of locale-sensitive regexes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``LOCALE`` flag is intended for legacy code and has limited support. You're still recommended to use Unicode instead. + +Added partial matches (`Hg issue 102 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A partial match is one that matches up to the end of string, but that string has been truncated and you want to know whether a complete match could be possible if the string had not been truncated. + +Partial matches are supported by ``match``, ``search``, ``fullmatch`` and ``finditer`` with the ``partial`` keyword argument. + +Match objects have a ``partial`` attribute, which is ``True`` if it's a partial match. + +For example, if you wanted a user to enter a 4-digit number and check it character by character as it was being entered: + +.. sourcecode:: python + + >>> pattern = regex.compile(r'\d{4}') + + >>> # Initially, nothing has been entered: + >>> print(pattern.fullmatch('', partial=True)) + + + >>> # An empty string is OK, but it's only a partial match. + >>> # The user enters a letter: + >>> print(pattern.fullmatch('a', partial=True)) + None + >>> # It'll never match. + + >>> # The user deletes that and enters a digit: + >>> print(pattern.fullmatch('1', partial=True)) + + >>> # It matches this far, but it's only a partial match. + + >>> # The user enters 2 more digits: + >>> print(pattern.fullmatch('123', partial=True)) + + >>> # It matches this far, but it's only a partial match. + + >>> # The user enters another digit: + >>> print(pattern.fullmatch('1234', partial=True)) + + >>> # It's a complete match. + + >>> # If the user enters another digit: + >>> print(pattern.fullmatch('12345', partial=True)) + None + >>> # It's no longer a match. + + >>> # This is a partial match: + >>> pattern.match('123', partial=True).partial + True + + >>> # This is a complete match: + >>> pattern.match('1233', partial=True).partial + False + +``*`` operator not working correctly with sub() (`Hg issue 106 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sometimes it's not clear how zero-width matches should be handled. For example, should ``.*`` match 0 characters directly after matching >0 characters? + +.. sourcecode:: python + + >>> regex.sub('.*', 'x', 'test') + 'xx' + >>> regex.sub('.*?', '|', 'test') + '|||||||||' + +Added ``capturesdict`` (`Hg issue 86 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``capturesdict`` is a combination of ``groupdict`` and ``captures``: + +``groupdict`` returns a dict of the named groups and the last capture of those groups. + +``captures`` returns a list of all the captures of a group + +``capturesdict`` returns a dict of the named groups and lists of all the captures of those groups. + +.. sourcecode:: python + + >>> m = regex.match(r"(?:(?P\w+) (?P\d+)\n)+", "one 1\ntwo 2\nthree 3\n") + >>> m.groupdict() + {'word': 'three', 'digits': '3'} + >>> m.captures("word") + ['one', 'two', 'three'] + >>> m.captures("digits") + ['1', '2', '3'] + >>> m.capturesdict() + {'word': ['one', 'two', 'three'], 'digits': ['1', '2', '3']} + +Added ``allcaptures`` and ``allspans`` (`Git issue 474 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``allcaptures`` returns a list of all the captures of all the groups. + +``allspans`` returns a list of all the spans of the all captures of all the groups. + +.. sourcecode:: python + + >>> m = regex.match(r"(?:(?P\w+) (?P\d+)\n)+", "one 1\ntwo 2\nthree 3\n") + >>> m.allcaptures() + (['one 1\ntwo 2\nthree 3\n'], ['one', 'two', 'three'], ['1', '2', '3']) + >>> m.allspans() + ([(0, 20)], [(0, 3), (6, 9), (12, 17)], [(4, 5), (10, 11), (18, 19)]) + +Allow duplicate names of groups (`Hg issue 87 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Group names can be duplicated. + +.. sourcecode:: python + + >>> # With optional groups: + >>> + >>> # Both groups capture, the second capture 'overwriting' the first. + >>> m = regex.match(r"(?P\w+)? or (?P\w+)?", "first or second") + >>> m.group("item") + 'second' + >>> m.captures("item") + ['first', 'second'] + >>> # Only the second group captures. + >>> m = regex.match(r"(?P\w+)? or (?P\w+)?", " or second") + >>> m.group("item") + 'second' + >>> m.captures("item") + ['second'] + >>> # Only the first group captures. + >>> m = regex.match(r"(?P\w+)? or (?P\w+)?", "first or ") + >>> m.group("item") + 'first' + >>> m.captures("item") + ['first'] + >>> + >>> # With mandatory groups: + >>> + >>> # Both groups capture, the second capture 'overwriting' the first. + >>> m = regex.match(r"(?P\w*) or (?P\w*)?", "first or second") + >>> m.group("item") + 'second' + >>> m.captures("item") + ['first', 'second'] + >>> # Again, both groups capture, the second capture 'overwriting' the first. + >>> m = regex.match(r"(?P\w*) or (?P\w*)", " or second") + >>> m.group("item") + 'second' + >>> m.captures("item") + ['', 'second'] + >>> # And yet again, both groups capture, the second capture 'overwriting' the first. + >>> m = regex.match(r"(?P\w*) or (?P\w*)", "first or ") + >>> m.group("item") + '' + >>> m.captures("item") + ['first', ''] + +Added ``fullmatch`` (`issue #16203 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``fullmatch`` behaves like ``match``, except that it must match all of the string. + +.. sourcecode:: python + + >>> print(regex.fullmatch(r"abc", "abc").span()) + (0, 3) + >>> print(regex.fullmatch(r"abc", "abcx")) + None + >>> print(regex.fullmatch(r"abc", "abcx", endpos=3).span()) + (0, 3) + >>> print(regex.fullmatch(r"abc", "xabcy", pos=1, endpos=4).span()) + (1, 4) + >>> + >>> regex.match(r"a.*?", "abcd").group(0) + 'a' + >>> regex.fullmatch(r"a.*?", "abcd").group(0) + 'abcd' + +Added ``subf`` and ``subfn`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``subf`` and ``subfn`` are alternatives to ``sub`` and ``subn`` respectively. When passed a replacement string, they treat it as a format string. + +.. sourcecode:: python + + >>> regex.subf(r"(\w+) (\w+)", "{0} => {2} {1}", "foo bar") + 'foo bar => bar foo' + >>> regex.subf(r"(?P\w+) (?P\w+)", "{word2} {word1}", "foo bar") + 'bar foo' + +Added ``expandf`` to match object +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``expandf`` is an alternative to ``expand``. When passed a replacement string, it treats it as a format string. + +.. sourcecode:: python + + >>> m = regex.match(r"(\w+) (\w+)", "foo bar") + >>> m.expandf("{0} => {2} {1}") + 'foo bar => bar foo' + >>> + >>> m = regex.match(r"(?P\w+) (?P\w+)", "foo bar") + >>> m.expandf("{word2} {word1}") + 'bar foo' + +Detach searched string +^^^^^^^^^^^^^^^^^^^^^^ + +A match object contains a reference to the string that was searched, via its ``string`` attribute. The ``detach_string`` method will 'detach' that string, making it available for garbage collection, which might save valuable memory if that string is very large. + +.. sourcecode:: python + + >>> m = regex.search(r"\w+", "Hello world") + >>> print(m.group()) + Hello + >>> print(m.string) + Hello world + >>> m.detach_string() + >>> print(m.group()) + Hello + >>> print(m.string) + None + +Recursive patterns (`Hg issue 27 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Recursive and repeated patterns are supported. + +``(?R)`` or ``(?0)`` tries to match the entire regex recursively. ``(?1)``, ``(?2)``, etc, try to match the relevant group. + +``(?&name)`` tries to match the named group. + +.. sourcecode:: python + + >>> regex.match(r"(Tarzan|Jane) loves (?1)", "Tarzan loves Jane").groups() + ('Tarzan',) + >>> regex.match(r"(Tarzan|Jane) loves (?1)", "Jane loves Tarzan").groups() + ('Jane',) + + >>> m = regex.search(r"(\w)(?:(?R)|(\w?))\1", "kayak") + >>> m.group(0, 1, 2) + ('kayak', 'k', None) + +The first two examples show how the subpattern within the group is reused, but is _not_ itself a group. In other words, ``"(Tarzan|Jane) loves (?1)"`` is equivalent to ``"(Tarzan|Jane) loves (?:Tarzan|Jane)"``. + +It's possible to backtrack into a recursed or repeated group. + +You can't call a group if there is more than one group with that group name or group number (``"ambiguous group reference"``). + +The alternative forms ``(?P>name)`` and ``(?P&name)`` are also supported. + +Full Unicode case-folding is supported +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In version 1 behaviour, the regex module uses full case-folding when performing case-insensitive matches in Unicode. + +.. sourcecode:: python + + >>> regex.match(r"(?iV1)strasse", "stra\N{LATIN SMALL LETTER SHARP S}e").span() + (0, 6) + >>> regex.match(r"(?iV1)stra\N{LATIN SMALL LETTER SHARP S}e", "STRASSE").span() + (0, 7) + +In version 0 behaviour, it uses simple case-folding for backward compatibility with the re module. + +Approximate "fuzzy" matching (`Hg issue 12 `_, `Hg issue 41 `_, `Hg issue 109 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Regex usually attempts an exact match, but sometimes an approximate, or "fuzzy", match is needed, for those cases where the text being searched may contain errors in the form of inserted, deleted or substituted characters. + +A fuzzy regex specifies which types of errors are permitted, and, optionally, either the minimum and maximum or only the maximum permitted number of each type. (You cannot specify only a minimum.) + +The 3 types of error are: + +* Insertion, indicated by "i" + +* Deletion, indicated by "d" + +* Substitution, indicated by "s" + +In addition, "e" indicates any type of error. + +The fuzziness of a regex item is specified between "{" and "}" after the item. + +Examples: + +* ``foo`` match "foo" exactly + +* ``(?:foo){i}`` match "foo", permitting insertions + +* ``(?:foo){d}`` match "foo", permitting deletions + +* ``(?:foo){s}`` match "foo", permitting substitutions + +* ``(?:foo){i,s}`` match "foo", permitting insertions and substitutions + +* ``(?:foo){e}`` match "foo", permitting errors + +If a certain type of error is specified, then any type not specified will **not** be permitted. + +In the following examples I'll omit the item and write only the fuzziness: + +* ``{d<=3}`` permit at most 3 deletions, but no other types + +* ``{i<=1,s<=2}`` permit at most 1 insertion and at most 2 substitutions, but no deletions + +* ``{1<=e<=3}`` permit at least 1 and at most 3 errors + +* ``{i<=2,d<=2,e<=3}`` permit at most 2 insertions, at most 2 deletions, at most 3 errors in total, but no substitutions + +It's also possible to state the costs of each type of error and the maximum permitted total cost. + +Examples: + +* ``{2i+2d+1s<=4}`` each insertion costs 2, each deletion costs 2, each substitution costs 1, the total cost must not exceed 4 + +* ``{i<=1,d<=1,s<=1,2i+2d+1s<=4}`` at most 1 insertion, at most 1 deletion, at most 1 substitution; each insertion costs 2, each deletion costs 2, each substitution costs 1, the total cost must not exceed 4 + +You can also use "<" instead of "<=" if you want an exclusive minimum or maximum. + +You can add a test to perform on a character that's substituted or inserted. + +Examples: + +* ``{s<=2:[a-z]}`` at most 2 substitutions, which must be in the character set ``[a-z]``. + +* ``{s<=2,i<=3:\d}`` at most 2 substitutions, at most 3 insertions, which must be digits. + +By default, fuzzy matching searches for the first match that meets the given constraints. The ``ENHANCEMATCH`` flag will cause it to attempt to improve the fit (i.e. reduce the number of errors) of the match that it has found. + +The ``BESTMATCH`` flag will make it search for the best match instead. + +Further examples to note: + +* ``regex.search("(dog){e}", "cat and dog")[1]`` returns ``"cat"`` because that matches ``"dog"`` with 3 errors (an unlimited number of errors is permitted). + +* ``regex.search("(dog){e<=1}", "cat and dog")[1]`` returns ``" dog"`` (with a leading space) because that matches ``"dog"`` with 1 error, which is within the limit. + +* ``regex.search("(?e)(dog){e<=1}", "cat and dog")[1]`` returns ``"dog"`` (without a leading space) because the fuzzy search matches ``" dog"`` with 1 error, which is within the limit, and the ``(?e)`` then it attempts a better fit. + +In the first two examples there are perfect matches later in the string, but in neither case is it the first possible match. + +The match object has an attribute ``fuzzy_counts`` which gives the total number of substitutions, insertions and deletions. + +.. sourcecode:: python + + >>> # A 'raw' fuzzy match: + >>> regex.fullmatch(r"(?:cats|cat){e<=1}", "cat").fuzzy_counts + (0, 0, 1) + >>> # 0 substitutions, 0 insertions, 1 deletion. + + >>> # A better match might be possible if the ENHANCEMATCH flag used: + >>> regex.fullmatch(r"(?e)(?:cats|cat){e<=1}", "cat").fuzzy_counts + (0, 0, 0) + >>> # 0 substitutions, 0 insertions, 0 deletions. + +The match object also has an attribute ``fuzzy_changes`` which gives a tuple of the positions of the substitutions, insertions and deletions. + +.. sourcecode:: python + + >>> m = regex.search('(fuu){i<=2,d<=2,e<=5}', 'anaconda foo bar') + >>> m + + >>> m.fuzzy_changes + ([], [7, 8], [10, 11]) + +What this means is that if the matched part of the string had been: + +.. sourcecode:: python + + 'anacondfuuoo bar' + +it would've been an exact match. + +However, there were insertions at positions 7 and 8: + +.. sourcecode:: python + + 'anaconda fuuoo bar' + ^^ + +and deletions at positions 10 and 11: + +.. sourcecode:: python + + 'anaconda f~~oo bar' + ^^ + +So the actual string was: + +.. sourcecode:: python + + 'anaconda foo bar' + +Named lists ``\L`` (`Hg issue 11 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +There are occasions where you may want to include a list (actually, a set) of options in a regex. + +One way is to build the pattern like this: + +.. sourcecode:: python + + >>> p = regex.compile(r"first|second|third|fourth|fifth") + +but if the list is large, parsing the resulting regex can take considerable time, and care must also be taken that the strings are properly escaped and properly ordered, for example, "cats" before "cat". + +The new alternative is to use a named list: + +.. sourcecode:: python + + >>> option_set = ["first", "second", "third", "fourth", "fifth"] + >>> p = regex.compile(r"\L", options=option_set) + +The order of the items is irrelevant, they are treated as a set. The named lists are available as the ``.named_lists`` attribute of the pattern object : + +.. sourcecode:: python + + >>> print(p.named_lists) + {'options': frozenset({'third', 'first', 'fifth', 'fourth', 'second'})} + +If there are any unused keyword arguments, ``ValueError`` will be raised unless you tell it otherwise: + +.. sourcecode:: python + + >>> option_set = ["first", "second", "third", "fourth", "fifth"] + >>> p = regex.compile(r"\L", options=option_set, other_options=[]) + Traceback (most recent call last): + File "", line 1, in + File "C:\Python310\lib\site-packages\regex\regex.py", line 353, in compile + return _compile(pattern, flags, ignore_unused, kwargs, cache_pattern) + File "C:\Python310\lib\site-packages\regex\regex.py", line 500, in _compile + complain_unused_args() + File "C:\Python310\lib\site-packages\regex\regex.py", line 483, in complain_unused_args + raise ValueError('unused keyword argument {!a}'.format(any_one)) + ValueError: unused keyword argument 'other_options' + >>> p = regex.compile(r"\L", options=option_set, other_options=[], ignore_unused=True) + >>> p = regex.compile(r"\L", options=option_set, other_options=[], ignore_unused=False) + Traceback (most recent call last): + File "", line 1, in + File "C:\Python310\lib\site-packages\regex\regex.py", line 353, in compile + return _compile(pattern, flags, ignore_unused, kwargs, cache_pattern) + File "C:\Python310\lib\site-packages\regex\regex.py", line 500, in _compile + complain_unused_args() + File "C:\Python310\lib\site-packages\regex\regex.py", line 483, in complain_unused_args + raise ValueError('unused keyword argument {!a}'.format(any_one)) + ValueError: unused keyword argument 'other_options' + >>> + +Start and end of word +^^^^^^^^^^^^^^^^^^^^^ + +``\m`` matches at the start of a word. + +``\M`` matches at the end of a word. + +Compare with ``\b``, which matches at the start or end of a word. + +Unicode line separators +^^^^^^^^^^^^^^^^^^^^^^^ + +Normally the only line separator is ``\n`` (``\x0A``), but if the ``WORD`` flag is turned on then the line separators are ``\x0D\x0A``, ``\x0A``, ``\x0B``, ``\x0C`` and ``\x0D``, plus ``\x85``, ``\u2028`` and ``\u2029`` when working with Unicode. + +This affects the regex dot ``"."``, which, with the ``DOTALL`` flag turned off, matches any character except a line separator. It also affects the line anchors ``^`` and ``$`` (in multiline mode). + +Set operators +^^^^^^^^^^^^^ + +**Version 1 behaviour only** + +Set operators have been added, and a set ``[...]`` can include nested sets. + +The operators, in order of increasing precedence, are: + +* ``||`` for union ("x||y" means "x or y") + +* ``~~`` (double tilde) for symmetric difference ("x~~y" means "x or y, but not both") + +* ``&&`` for intersection ("x&&y" means "x and y") + +* ``--`` (double dash) for difference ("x--y" means "x but not y") + +Implicit union, ie, simple juxtaposition like in ``[ab]``, has the highest precedence. Thus, ``[ab&&cd]`` is the same as ``[[a||b]&&[c||d]]``. + +Examples: + +* ``[ab]`` # Set containing 'a' and 'b' + +* ``[a-z]`` # Set containing 'a' .. 'z' + +* ``[[a-z]--[qw]]`` # Set containing 'a' .. 'z', but not 'q' or 'w' + +* ``[a-z--qw]`` # Same as above + +* ``[\p{L}--QW]`` # Set containing all letters except 'Q' and 'W' + +* ``[\p{N}--[0-9]]`` # Set containing all numbers except '0' .. '9' + +* ``[\p{ASCII}&&\p{Letter}]`` # Set containing all characters which are ASCII and letter + +regex.escape (`issue #2650 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +regex.escape has an additional keyword parameter ``special_only``. When True, only 'special' regex characters, such as '?', are escaped. + +.. sourcecode:: python + + >>> regex.escape("foo!?", special_only=False) + 'foo\\!\\?' + >>> regex.escape("foo!?", special_only=True) + 'foo!\\?' + +regex.escape (`Hg issue 249 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +regex.escape has an additional keyword parameter ``literal_spaces``. When True, spaces are not escaped. + +.. sourcecode:: python + + >>> regex.escape("foo bar!?", literal_spaces=False) + 'foo\\ bar!\\?' + >>> regex.escape("foo bar!?", literal_spaces=True) + 'foo bar!\\?' + +Repeated captures (`issue #7132 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A match object has additional methods which return information on all the successful matches of a repeated group. These methods are: + +* ``matchobject.captures([group1, ...])`` + + * Returns a list of the strings matched in a group or groups. Compare with ``matchobject.group([group1, ...])``. + +* ``matchobject.starts([group])`` + + * Returns a list of the start positions. Compare with ``matchobject.start([group])``. + +* ``matchobject.ends([group])`` + + * Returns a list of the end positions. Compare with ``matchobject.end([group])``. + +* ``matchobject.spans([group])`` + + * Returns a list of the spans. Compare with ``matchobject.span([group])``. + +.. sourcecode:: python + + >>> m = regex.search(r"(\w{3})+", "123456789") + >>> m.group(1) + '789' + >>> m.captures(1) + ['123', '456', '789'] + >>> m.start(1) + 6 + >>> m.starts(1) + [0, 3, 6] + >>> m.end(1) + 9 + >>> m.ends(1) + [3, 6, 9] + >>> m.span(1) + (6, 9) + >>> m.spans(1) + [(0, 3), (3, 6), (6, 9)] + +Atomic grouping ``(?>...)`` (`issue #433030 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If the following pattern subsequently fails, then the subpattern as a whole will fail. + +Possessive quantifiers +^^^^^^^^^^^^^^^^^^^^^^ + +``(?:...)?+`` ; ``(?:...)*+`` ; ``(?:...)++`` ; ``(?:...){min,max}+`` + +The subpattern is matched up to 'max' times. If the following pattern subsequently fails, then all the repeated subpatterns will fail as a whole. For example, ``(?:...)++`` is equivalent to ``(?>(?:...)+)``. + +Scoped flags (`issue #433028 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``(?flags-flags:...)`` + +The flags will apply only to the subpattern. Flags can be turned on or off. + +Definition of 'word' character (`issue #1693050 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The definition of a 'word' character has been expanded for Unicode. It conforms to the Unicode specification at ``http://www.unicode.org/reports/tr29/``. + +Variable-length lookbehind +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A lookbehind can match a variable-length string. + +Flags argument for regex.split, regex.sub and regex.subn (`issue #3482 `_) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``regex.split``, ``regex.sub`` and ``regex.subn`` support a 'flags' argument. + +Pos and endpos arguments for regex.sub and regex.subn +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``regex.sub`` and ``regex.subn`` support 'pos' and 'endpos' arguments. + +'Overlapped' argument for regex.findall and regex.finditer +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``regex.findall`` and ``regex.finditer`` support an 'overlapped' flag which permits overlapped matches. + +Splititer +^^^^^^^^^ + +``regex.splititer`` has been added. It's a generator equivalent of ``regex.split``. + +Subscripting match objects for groups +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A match object accepts access to the groups via subscripting and slicing: + +.. sourcecode:: python + + >>> m = regex.search(r"(?P.*?)(?P\d+)(?P.*)", "pqr123stu") + >>> print(m["before"]) + pqr + >>> print(len(m)) + 4 + >>> print(m[:]) + ('pqr123stu', 'pqr', '123', 'stu') + +Named groups +^^^^^^^^^^^^ + +Groups can be named with ``(?...)`` as well as the existing ``(?P...)``. + +Group references +^^^^^^^^^^^^^^^^ + +Groups can be referenced within a pattern with ``\g``. This also allows there to be more than 99 groups. + +Named characters ``\N{name}`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Named characters are supported. Note that only those known by Python's Unicode database will be recognised. + +Unicode codepoint properties, including scripts and blocks +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``\p{property=value}``; ``\P{property=value}``; ``\p{value}`` ; ``\P{value}`` + +Many Unicode properties are supported, including blocks and scripts. ``\p{property=value}`` or ``\p{property:value}`` matches a character whose property ``property`` has value ``value``. The inverse of ``\p{property=value}`` is ``\P{property=value}`` or ``\p{^property=value}``. + +If the short form ``\p{value}`` is used, the properties are checked in the order: ``General_Category``, ``Script``, ``Block``, binary property: + +* ``Latin``, the 'Latin' script (``Script=Latin``). + +* ``BasicLatin``, the 'BasicLatin' block (``Block=BasicLatin``). + +* ``Alphabetic``, the 'Alphabetic' binary property (``Alphabetic=Yes``). + +A short form starting with ``Is`` indicates a script or binary property: + +* ``IsLatin``, the 'Latin' script (``Script=Latin``). + +* ``IsAlphabetic``, the 'Alphabetic' binary property (``Alphabetic=Yes``). + +A short form starting with ``In`` indicates a block property: + +* ``InBasicLatin``, the 'BasicLatin' block (``Block=BasicLatin``). + +POSIX character classes +^^^^^^^^^^^^^^^^^^^^^^^ + +``[[:alpha:]]``; ``[[:^alpha:]]`` + +POSIX character classes are supported. These are normally treated as an alternative form of ``\p{...}``. + +The exceptions are ``alnum``, ``digit``, ``punct`` and ``xdigit``, whose definitions are different from those of Unicode. + +``[[:alnum:]]`` is equivalent to ``\p{posix_alnum}``. + +``[[:digit:]]`` is equivalent to ``\p{posix_digit}``. + +``[[:punct:]]`` is equivalent to ``\p{posix_punct}``. + +``[[:xdigit:]]`` is equivalent to ``\p{posix_xdigit}``. + +Search anchor ``\G`` +^^^^^^^^^^^^^^^^^^^^ + +A search anchor has been added. It matches at the position where each search started/continued and can be used for contiguous matches or in negative variable-length lookbehinds to limit how far back the lookbehind goes: + +.. sourcecode:: python + + >>> regex.findall(r"\w{2}", "abcd ef") + ['ab', 'cd', 'ef'] + >>> regex.findall(r"\G\w{2}", "abcd ef") + ['ab', 'cd'] + +* The search starts at position 0 and matches 'ab'. + +* The search continues at position 2 and matches 'cd'. + +* The search continues at position 4 and fails to match any letters. + +* The anchor stops the search start position from being advanced, so there are no more results. + +Reverse searching +^^^^^^^^^^^^^^^^^ + +Searches can also work backwards: + +.. sourcecode:: python + + >>> regex.findall(r".", "abc") + ['a', 'b', 'c'] + >>> regex.findall(r"(?r).", "abc") + ['c', 'b', 'a'] + +Note that the result of a reverse search is not necessarily the reverse of a forward search: + +.. sourcecode:: python + + >>> regex.findall(r"..", "abcde") + ['ab', 'cd'] + >>> regex.findall(r"(?r)..", "abcde") + ['de', 'bc'] + +Matching a single grapheme ``\X`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The grapheme matcher is supported. It conforms to the Unicode specification at ``http://www.unicode.org/reports/tr29/``. + +Branch reset ``(?|...|...)`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Group numbers will be reused across the alternatives, but groups with different names will have different group numbers. + +.. sourcecode:: python + + >>> regex.match(r"(?|(first)|(second))", "first").groups() + ('first',) + >>> regex.match(r"(?|(first)|(second))", "second").groups() + ('second',) + +Note that there is only one group. + +Default Unicode word boundary +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``WORD`` flag changes the definition of a 'word boundary' to that of a default Unicode word boundary. This applies to ``\b`` and ``\B``. + +Timeout +^^^^^^^ + +The matching methods and functions support timeouts. The timeout (in seconds) applies to the entire operation: + +.. sourcecode:: python + + >>> from time import sleep + >>> + >>> def fast_replace(m): + ... return 'X' + ... + >>> def slow_replace(m): + ... sleep(0.5) + ... return 'X' + ... + >>> regex.sub(r'[a-z]', fast_replace, 'abcde', timeout=2) + 'XXXXX' + >>> regex.sub(r'[a-z]', slow_replace, 'abcde', timeout=2) + Traceback (most recent call last): + File "", line 1, in + File "C:\Python310\lib\site-packages\regex\regex.py", line 278, in sub + return pat.sub(repl, string, count, pos, endpos, concurrent, timeout) + TimeoutError: regex timed out diff --git a/.venv/lib/python3.12/site-packages/regex-2025.9.18.dist-info/RECORD b/.venv/lib/python3.12/site-packages/regex-2025.9.18.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..9d00c885d6bae50622f6f657daea89c6611bc093 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/regex-2025.9.18.dist-info/RECORD @@ -0,0 +1,12 @@ +regex-2025.9.18.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +regex-2025.9.18.dist-info/METADATA,sha256=ppVSsHk1elKol6eLDEfsSIrvQKptKmwqmvcfEAXoBNI,40467 +regex-2025.9.18.dist-info/RECORD,, +regex-2025.9.18.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +regex-2025.9.18.dist-info/WHEEL,sha256=DxRnWQz-Kp9-4a4hdDHsSv0KUC3H7sN9Nbef3-8RjXU,190 +regex-2025.9.18.dist-info/licenses/LICENSE.txt,sha256=v_Ve9M3MjBTOJZ-OirYOJkQYRA1jNfTcE4Jz-9UGFE0,11584 +regex-2025.9.18.dist-info/top_level.txt,sha256=aQmiDMhNTF26cCK4_7D-qaVvhbxClG0wyCTnEhkzYBs,6 +regex/__init__.py,sha256=9slNQEb4SCZ9LncNzcQvqmkyxXlcOAF7QwAwigxWjsw,65 +regex/_regex.cpython-312-x86_64-linux-gnu.so,sha256=D73Z701QlklLA53REKxIkTyjFwn7AZsVcSvxOzRyIXg,2576520 +regex/_regex_core.py,sha256=Sc72uTUn02segfjUvAN8fqWhlidiVArL-ODNOzC0vKQ,144661 +regex/regex.py,sha256=UjJthKd24f34PfvGNkSqgnHgtbSmT3mjPhAdZedf_D8,32683 +regex/test_regex.py,sha256=2GH_zmbgL8s2QYTlK5Py5C6IHwMGYDHeZGM1e-_Xe7Y,225810 diff --git a/.venv/lib/python3.12/site-packages/regex-2025.9.18.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/regex-2025.9.18.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/regex-2025.9.18.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/regex-2025.9.18.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..f3e8a970f16adf4526f1722547053522d94bf860 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/regex-2025.9.18.dist-info/WHEEL @@ -0,0 +1,7 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: false +Tag: cp312-cp312-manylinux_2_17_x86_64 +Tag: cp312-cp312-manylinux2014_x86_64 +Tag: cp312-cp312-manylinux_2_28_x86_64 + diff --git a/.venv/lib/python3.12/site-packages/regex-2025.9.18.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/regex-2025.9.18.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..4f9256d62325c75de027d1cd48f1ff520117413e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/regex-2025.9.18.dist-info/top_level.txt @@ -0,0 +1 @@ +regex diff --git a/.venv/lib/python3.12/site-packages/requests-2.32.5.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/requests-2.32.5.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/requests-2.32.5.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/requests-2.32.5.dist-info/METADATA b/.venv/lib/python3.12/site-packages/requests-2.32.5.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..b31773e3ea2d0e0e16e77d33fefd8b8f8ff3fc85 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/requests-2.32.5.dist-info/METADATA @@ -0,0 +1,133 @@ +Metadata-Version: 2.4 +Name: requests +Version: 2.32.5 +Summary: Python HTTP for Humans. +Home-page: https://requests.readthedocs.io +Author: Kenneth Reitz +Author-email: me@kennethreitz.org +License: Apache-2.0 +Project-URL: Documentation, https://requests.readthedocs.io +Project-URL: Source, https://github.com/psf/requests +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Natural Language :: English +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +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 :: 3 :: Only +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: Software Development :: Libraries +Requires-Python: >=3.9 +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: charset_normalizer<4,>=2 +Requires-Dist: idna<4,>=2.5 +Requires-Dist: urllib3<3,>=1.21.1 +Requires-Dist: certifi>=2017.4.17 +Provides-Extra: security +Provides-Extra: socks +Requires-Dist: PySocks!=1.5.7,>=1.5.6; extra == "socks" +Provides-Extra: use-chardet-on-py3 +Requires-Dist: chardet<6,>=3.0.2; extra == "use-chardet-on-py3" +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: description-content-type +Dynamic: home-page +Dynamic: license +Dynamic: license-file +Dynamic: project-url +Dynamic: provides-extra +Dynamic: requires-dist +Dynamic: requires-python +Dynamic: summary + +# Requests + +**Requests** is a simple, yet elegant, HTTP library. + +```python +>>> import requests +>>> r = requests.get('https://httpbin.org/basic-auth/user/pass', auth=('user', 'pass')) +>>> r.status_code +200 +>>> r.headers['content-type'] +'application/json; charset=utf8' +>>> r.encoding +'utf-8' +>>> r.text +'{"authenticated": true, ...' +>>> r.json() +{'authenticated': True, ...} +``` + +Requests allows you to send HTTP/1.1 requests extremely easily. There’s no need to manually add query strings to your URLs, or to form-encode your `PUT` & `POST` data — but nowadays, just use the `json` method! + +Requests is one of the most downloaded Python packages today, pulling in around `30M downloads / week`— according to GitHub, Requests is currently [depended upon](https://github.com/psf/requests/network/dependents?package_id=UGFja2FnZS01NzA4OTExNg%3D%3D) by `1,000,000+` repositories. You may certainly put your trust in this code. + +[![Downloads](https://static.pepy.tech/badge/requests/month)](https://pepy.tech/project/requests) +[![Supported Versions](https://img.shields.io/pypi/pyversions/requests.svg)](https://pypi.org/project/requests) +[![Contributors](https://img.shields.io/github/contributors/psf/requests.svg)](https://github.com/psf/requests/graphs/contributors) + +## Installing Requests and Supported Versions + +Requests is available on PyPI: + +```console +$ python -m pip install requests +``` + +Requests officially supports Python 3.9+. + +## Supported Features & Best–Practices + +Requests is ready for the demands of building robust and reliable HTTP–speaking applications, for the needs of today. + +- Keep-Alive & Connection Pooling +- International Domains and URLs +- Sessions with Cookie Persistence +- Browser-style TLS/SSL Verification +- Basic & Digest Authentication +- Familiar `dict`–like Cookies +- Automatic Content Decompression and Decoding +- Multi-part File Uploads +- SOCKS Proxy Support +- Connection Timeouts +- Streaming Downloads +- Automatic honoring of `.netrc` +- Chunked HTTP Requests + +## API Reference and User Guide available on [Read the Docs](https://requests.readthedocs.io) + +[![Read the Docs](https://raw.githubusercontent.com/psf/requests/main/ext/ss.png)](https://requests.readthedocs.io) + +## Cloning the repository + +When cloning the Requests repository, you may need to add the `-c +fetch.fsck.badTimezone=ignore` flag to avoid an error about a bad commit timestamp (see +[this issue](https://github.com/psf/requests/issues/2690) for more background): + +```shell +git clone -c fetch.fsck.badTimezone=ignore https://github.com/psf/requests.git +``` + +You can also apply this setting to your global Git config: + +```shell +git config --global fetch.fsck.badTimezone ignore +``` + +--- + +[![Kenneth Reitz](https://raw.githubusercontent.com/psf/requests/main/ext/kr.png)](https://kennethreitz.org) [![Python Software Foundation](https://raw.githubusercontent.com/psf/requests/main/ext/psf.png)](https://www.python.org/psf) diff --git a/.venv/lib/python3.12/site-packages/requests-2.32.5.dist-info/RECORD b/.venv/lib/python3.12/site-packages/requests-2.32.5.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..5fd79a6be91e1c2c5ff19760558afe0d4d8d8fb6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/requests-2.32.5.dist-info/RECORD @@ -0,0 +1,25 @@ +requests-2.32.5.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +requests-2.32.5.dist-info/METADATA,sha256=ZbWgjagfSRVRPnYJZf8Ut1GPZbe7Pv4NqzZLvMTUDLA,4945 +requests-2.32.5.dist-info/RECORD,, +requests-2.32.5.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +requests-2.32.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91 +requests-2.32.5.dist-info/licenses/LICENSE,sha256=CeipvOyAZxBGUsFoaFqwkx54aPnIKEtm9a5u2uXxEws,10142 +requests-2.32.5.dist-info/top_level.txt,sha256=fMSVmHfb5rbGOo6xv-O_tUX6j-WyixssE-SnwcDRxNQ,9 +requests/__init__.py,sha256=4xaAERmPDIBPsa2PsjpU9r06yooK-2mZKHTZAhWRWts,5072 +requests/__version__.py,sha256=QKDceK8K_ujqwDDc3oYrR0odOBYgKVOQQ5vFap_G_cg,435 +requests/_internal_utils.py,sha256=nMQymr4hs32TqVo5AbCrmcJEhvPUh7xXlluyqwslLiQ,1495 +requests/adapters.py,sha256=8nX113gbb123aUtx2ETkAN_6IsYX-M2fRoLGluTEcRk,26285 +requests/api.py,sha256=_Zb9Oa7tzVIizTKwFrPjDEY9ejtm_OnSRERnADxGsQs,6449 +requests/auth.py,sha256=kF75tqnLctZ9Mf_hm9TZIj4cQWnN5uxRz8oWsx5wmR0,10186 +requests/certs.py,sha256=Z9Sb410Anv6jUFTyss0jFFhU6xst8ctELqfy8Ev23gw,429 +requests/compat.py,sha256=J7sIjR6XoDGp5JTVzOxkK5fSoUVUa_Pjc7iRZhAWGmI,2142 +requests/cookies.py,sha256=bNi-iqEj4NPZ00-ob-rHvzkvObzN3lEpgw3g6paS3Xw,18590 +requests/exceptions.py,sha256=jJPS1UWATs86ShVUaLorTiJb1SaGuoNEWgICJep-VkY,4260 +requests/help.py,sha256=gPX5d_H7Xd88aDABejhqGgl9B1VFRTt5BmiYvL3PzIQ,3875 +requests/hooks.py,sha256=CiuysiHA39V5UfcCBXFIx83IrDpuwfN9RcTUgv28ftQ,733 +requests/models.py,sha256=MjZdZ4k7tnw-1nz5PKShjmPmqyk0L6DciwnFngb_Vk4,35510 +requests/packages.py,sha256=_g0gZ681UyAlKHRjH6kanbaoxx2eAb6qzcXiODyTIoc,904 +requests/sessions.py,sha256=Cl1dpEnOfwrzzPbku-emepNeN4Rt_0_58Iy2x-JGTm8,30503 +requests/status_codes.py,sha256=iJUAeA25baTdw-6PfD0eF4qhpINDJRJI-yaMqxs4LEI,4322 +requests/structures.py,sha256=-IbmhVz06S-5aPSZuUthZ6-6D9XOjRuTXHOabY041XM,2912 +requests/utils.py,sha256=WqU86rZ3wvhC-tQjWcjtH_HEKZwWB3iWCZV6SW5DEdQ,33213 diff --git a/.venv/lib/python3.12/site-packages/requests-2.32.5.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/requests-2.32.5.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/requests-2.32.5.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/requests-2.32.5.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..e7fa31b6f3f78deb1022c1f7927f07d4d16da822 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/requests-2.32.5.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.12/site-packages/requests-2.32.5.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/requests-2.32.5.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..f2293605cf1b01dca72aad0a15c45b72ed5429a2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/requests-2.32.5.dist-info/top_level.txt @@ -0,0 +1 @@ +requests diff --git a/.venv/lib/python3.12/site-packages/scikit_learn-1.7.2.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/scikit_learn-1.7.2.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/scikit_learn-1.7.2.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/scikit_learn-1.7.2.dist-info/METADATA b/.venv/lib/python3.12/site-packages/scikit_learn-1.7.2.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..6d5a4ea1497ee18f89a3c6137696573bbe10055b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/scikit_learn-1.7.2.dist-info/METADATA @@ -0,0 +1,307 @@ +Metadata-Version: 2.4 +Name: scikit-learn +Version: 1.7.2 +Summary: A set of python modules for machine learning and data mining +Maintainer-Email: scikit-learn developers +License-Expression: BSD-3-Clause +License-File: COPYING +Classifier: Intended Audience :: Science/Research +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: C +Classifier: Programming Language :: Python +Classifier: Topic :: Software Development +Classifier: Topic :: Scientific/Engineering +Classifier: Development Status :: 5 - Production/Stable +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX +Classifier: Operating System :: Unix +Classifier: Operating System :: MacOS +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 :: Implementation :: CPython +Project-URL: homepage, https://scikit-learn.org +Project-URL: source, https://github.com/scikit-learn/scikit-learn +Project-URL: download, https://pypi.org/project/scikit-learn/#files +Project-URL: tracker, https://github.com/scikit-learn/scikit-learn/issues +Project-URL: release notes, https://scikit-learn.org/stable/whats_new +Requires-Python: >=3.10 +Requires-Dist: numpy>=1.22.0 +Requires-Dist: scipy>=1.8.0 +Requires-Dist: joblib>=1.2.0 +Requires-Dist: threadpoolctl>=3.1.0 +Provides-Extra: build +Requires-Dist: numpy>=1.22.0; extra == "build" +Requires-Dist: scipy>=1.8.0; extra == "build" +Requires-Dist: cython>=3.0.10; extra == "build" +Requires-Dist: meson-python>=0.17.1; extra == "build" +Provides-Extra: install +Requires-Dist: numpy>=1.22.0; extra == "install" +Requires-Dist: scipy>=1.8.0; extra == "install" +Requires-Dist: joblib>=1.2.0; extra == "install" +Requires-Dist: threadpoolctl>=3.1.0; extra == "install" +Provides-Extra: benchmark +Requires-Dist: matplotlib>=3.5.0; extra == "benchmark" +Requires-Dist: pandas>=1.4.0; extra == "benchmark" +Requires-Dist: memory_profiler>=0.57.0; extra == "benchmark" +Provides-Extra: docs +Requires-Dist: matplotlib>=3.5.0; extra == "docs" +Requires-Dist: scikit-image>=0.19.0; extra == "docs" +Requires-Dist: pandas>=1.4.0; extra == "docs" +Requires-Dist: seaborn>=0.9.0; extra == "docs" +Requires-Dist: memory_profiler>=0.57.0; extra == "docs" +Requires-Dist: sphinx>=7.3.7; extra == "docs" +Requires-Dist: sphinx-copybutton>=0.5.2; extra == "docs" +Requires-Dist: sphinx-gallery>=0.17.1; extra == "docs" +Requires-Dist: numpydoc>=1.2.0; extra == "docs" +Requires-Dist: Pillow>=8.4.0; extra == "docs" +Requires-Dist: pooch>=1.6.0; extra == "docs" +Requires-Dist: sphinx-prompt>=1.4.0; extra == "docs" +Requires-Dist: sphinxext-opengraph>=0.9.1; extra == "docs" +Requires-Dist: plotly>=5.14.0; extra == "docs" +Requires-Dist: polars>=0.20.30; extra == "docs" +Requires-Dist: sphinx-design>=0.5.0; extra == "docs" +Requires-Dist: sphinx-design>=0.6.0; extra == "docs" +Requires-Dist: sphinxcontrib-sass>=0.3.4; extra == "docs" +Requires-Dist: pydata-sphinx-theme>=0.15.3; extra == "docs" +Requires-Dist: sphinx-remove-toctrees>=1.0.0.post1; extra == "docs" +Requires-Dist: towncrier>=24.8.0; extra == "docs" +Provides-Extra: examples +Requires-Dist: matplotlib>=3.5.0; extra == "examples" +Requires-Dist: scikit-image>=0.19.0; extra == "examples" +Requires-Dist: pandas>=1.4.0; extra == "examples" +Requires-Dist: seaborn>=0.9.0; extra == "examples" +Requires-Dist: pooch>=1.6.0; extra == "examples" +Requires-Dist: plotly>=5.14.0; extra == "examples" +Provides-Extra: tests +Requires-Dist: matplotlib>=3.5.0; extra == "tests" +Requires-Dist: scikit-image>=0.19.0; extra == "tests" +Requires-Dist: pandas>=1.4.0; extra == "tests" +Requires-Dist: pytest>=7.1.2; extra == "tests" +Requires-Dist: pytest-cov>=2.9.0; extra == "tests" +Requires-Dist: ruff>=0.11.7; extra == "tests" +Requires-Dist: mypy>=1.15; extra == "tests" +Requires-Dist: pyamg>=4.2.1; extra == "tests" +Requires-Dist: polars>=0.20.30; extra == "tests" +Requires-Dist: pyarrow>=12.0.0; extra == "tests" +Requires-Dist: numpydoc>=1.2.0; extra == "tests" +Requires-Dist: pooch>=1.6.0; extra == "tests" +Provides-Extra: maintenance +Requires-Dist: conda-lock==3.0.1; extra == "maintenance" +Description-Content-Type: text/x-rst + +.. -*- mode: rst -*- + +|Azure| |Codecov| |CircleCI| |Nightly wheels| |Ruff| |PythonVersion| |PyPi| |DOI| |Benchmark| + +.. |Azure| image:: https://dev.azure.com/scikit-learn/scikit-learn/_apis/build/status/scikit-learn.scikit-learn?branchName=main + :target: https://dev.azure.com/scikit-learn/scikit-learn/_build/latest?definitionId=1&branchName=main + +.. |CircleCI| image:: https://circleci.com/gh/scikit-learn/scikit-learn/tree/main.svg?style=shield + :target: https://circleci.com/gh/scikit-learn/scikit-learn + +.. |Codecov| image:: https://codecov.io/gh/scikit-learn/scikit-learn/branch/main/graph/badge.svg?token=Pk8G9gg3y9 + :target: https://codecov.io/gh/scikit-learn/scikit-learn + +.. |Nightly wheels| image:: https://github.com/scikit-learn/scikit-learn/actions/workflows/wheels.yml/badge.svg?event=schedule + :target: https://github.com/scikit-learn/scikit-learn/actions?query=workflow%3A%22Wheel+builder%22+event%3Aschedule + +.. |Ruff| image:: https://img.shields.io/badge/code%20style-ruff-000000.svg + :target: https://github.com/astral-sh/ruff + +.. |PythonVersion| image:: https://img.shields.io/pypi/pyversions/scikit-learn.svg + :target: https://pypi.org/project/scikit-learn/ + +.. |PyPi| image:: https://img.shields.io/pypi/v/scikit-learn + :target: https://pypi.org/project/scikit-learn + +.. |DOI| image:: https://zenodo.org/badge/21369/scikit-learn/scikit-learn.svg + :target: https://zenodo.org/badge/latestdoi/21369/scikit-learn/scikit-learn + +.. |Benchmark| image:: https://img.shields.io/badge/Benchmarked%20by-asv-blue + :target: https://scikit-learn.org/scikit-learn-benchmarks + +.. |PythonMinVersion| replace:: 3.10 +.. |NumPyMinVersion| replace:: 1.22.0 +.. |SciPyMinVersion| replace:: 1.8.0 +.. |JoblibMinVersion| replace:: 1.2.0 +.. |ThreadpoolctlMinVersion| replace:: 3.1.0 +.. |MatplotlibMinVersion| replace:: 3.5.0 +.. |Scikit-ImageMinVersion| replace:: 0.19.0 +.. |PandasMinVersion| replace:: 1.4.0 +.. |SeabornMinVersion| replace:: 0.9.0 +.. |PytestMinVersion| replace:: 7.1.2 +.. |PlotlyMinVersion| replace:: 5.14.0 + +.. image:: https://raw.githubusercontent.com/scikit-learn/scikit-learn/main/doc/logos/scikit-learn-logo.png + :target: https://scikit-learn.org/ + +**scikit-learn** is a Python module for machine learning built on top of +SciPy and is distributed under the 3-Clause BSD license. + +The project was started in 2007 by David Cournapeau as a Google Summer +of Code project, and since then many volunteers have contributed. See +the `About us `__ page +for a list of core contributors. + +It is currently maintained by a team of volunteers. + +Website: https://scikit-learn.org + +Installation +------------ + +Dependencies +~~~~~~~~~~~~ + +scikit-learn requires: + +- Python (>= |PythonMinVersion|) +- NumPy (>= |NumPyMinVersion|) +- SciPy (>= |SciPyMinVersion|) +- joblib (>= |JoblibMinVersion|) +- threadpoolctl (>= |ThreadpoolctlMinVersion|) + +======= + +Scikit-learn plotting capabilities (i.e., functions start with ``plot_`` and +classes end with ``Display``) require Matplotlib (>= |MatplotlibMinVersion|). +For running the examples Matplotlib >= |MatplotlibMinVersion| is required. +A few examples require scikit-image >= |Scikit-ImageMinVersion|, a few examples +require pandas >= |PandasMinVersion|, some examples require seaborn >= +|SeabornMinVersion| and plotly >= |PlotlyMinVersion|. + +User installation +~~~~~~~~~~~~~~~~~ + +If you already have a working installation of NumPy and SciPy, +the easiest way to install scikit-learn is using ``pip``:: + + pip install -U scikit-learn + +or ``conda``:: + + conda install -c conda-forge scikit-learn + +The documentation includes more detailed `installation instructions `_. + + +Changelog +--------- + +See the `changelog `__ +for a history of notable changes to scikit-learn. + +Development +----------- + +We welcome new contributors of all experience levels. The scikit-learn +community goals are to be helpful, welcoming, and effective. The +`Development Guide `_ +has detailed information about contributing code, documentation, tests, and +more. We've included some basic information in this README. + +Important links +~~~~~~~~~~~~~~~ + +- Official source code repo: https://github.com/scikit-learn/scikit-learn +- Download releases: https://pypi.org/project/scikit-learn/ +- Issue tracker: https://github.com/scikit-learn/scikit-learn/issues + +Source code +~~~~~~~~~~~ + +You can check the latest sources with the command:: + + git clone https://github.com/scikit-learn/scikit-learn.git + +Contributing +~~~~~~~~~~~~ + +To learn more about making a contribution to scikit-learn, please see our +`Contributing guide +`_. + +Testing +~~~~~~~ + +After installation, you can launch the test suite from outside the source +directory (you will need to have ``pytest`` >= |PyTestMinVersion| installed):: + + pytest sklearn + +See the web page https://scikit-learn.org/dev/developers/contributing.html#testing-and-improving-test-coverage +for more information. + + Random number generation can be controlled during testing by setting + the ``SKLEARN_SEED`` environment variable. + +Submitting a Pull Request +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Before opening a Pull Request, have a look at the +full Contributing page to make sure your code complies +with our guidelines: https://scikit-learn.org/stable/developers/index.html + +Project History +--------------- + +The project was started in 2007 by David Cournapeau as a Google Summer +of Code project, and since then many volunteers have contributed. See +the `About us `__ page +for a list of core contributors. + +The project is currently maintained by a team of volunteers. + +**Note**: `scikit-learn` was previously referred to as `scikits.learn`. + +Help and Support +---------------- + +Documentation +~~~~~~~~~~~~~ + +- HTML documentation (stable release): https://scikit-learn.org +- HTML documentation (development version): https://scikit-learn.org/dev/ +- FAQ: https://scikit-learn.org/stable/faq.html + +Communication +~~~~~~~~~~~~~ + +Main Channels +^^^^^^^^^^^^^ + +- **Website**: https://scikit-learn.org +- **Blog**: https://blog.scikit-learn.org +- **Mailing list**: https://mail.python.org/mailman/listinfo/scikit-learn + +Developer & Support +^^^^^^^^^^^^^^^^^^^^^^ + +- **GitHub Discussions**: https://github.com/scikit-learn/scikit-learn/discussions +- **Stack Overflow**: https://stackoverflow.com/questions/tagged/scikit-learn +- **Discord**: https://discord.gg/h9qyrK8Jc8 + +Social Media Platforms +^^^^^^^^^^^^^^^^^^^^^^ + +- **LinkedIn**: https://www.linkedin.com/company/scikit-learn +- **YouTube**: https://www.youtube.com/channel/UCJosFjYm0ZYVUARxuOZqnnw/playlists +- **Facebook**: https://www.facebook.com/scikitlearnofficial/ +- **Instagram**: https://www.instagram.com/scikitlearnofficial/ +- **TikTok**: https://www.tiktok.com/@scikit.learn +- **Bluesky**: https://bsky.app/profile/scikit-learn.org +- **Mastodon**: https://mastodon.social/@sklearn@fosstodon.org + +Resources +^^^^^^^^^ + +- **Calendar**: https://blog.scikit-learn.org/calendar/ +- **Logos & Branding**: https://github.com/scikit-learn/scikit-learn/tree/main/doc/logos + +Citation +~~~~~~~~ + +If you use scikit-learn in a scientific publication, we would appreciate citations: https://scikit-learn.org/stable/about.html#citing-scikit-learn diff --git a/.venv/lib/python3.12/site-packages/scikit_learn-1.7.2.dist-info/RECORD b/.venv/lib/python3.12/site-packages/scikit_learn-1.7.2.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..3da0295ce9824e8905c31b9d452c64278abd9c8a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/scikit_learn-1.7.2.dist-info/RECORD @@ -0,0 +1,958 @@ +scikit_learn-1.7.2.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +scikit_learn-1.7.2.dist-info/METADATA,sha256=owF3DYFuXvXiomKIYRXx5gAmNzJPO_5z-SN4iX3qjGM,11784 +scikit_learn-1.7.2.dist-info/RECORD,, +scikit_learn-1.7.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +scikit_learn-1.7.2.dist-info/WHEEL,sha256=3qIDcXCk577AXiK3pDifO-gE9U_MYWYGgtD78gLa2_U,137 +scikit_learn-1.7.2.dist-info/licenses/COPYING,sha256=_ebNwKhsZahFrxcIb5ZPejjZNEZ7fzYgOJSvMOzudkA,5078 +scikit_learn.libs/libgomp-a34b3233.so.1.0.0,sha256=On6uznIxkRvi-7Gz58tMtcLg-E4MK7c3OUcrWh_uyME,168193 +sklearn/__check_build/__init__.py,sha256=qOmiYYd8XWCN-knP2AdJLoNrN7E-Jn48vx1iZpYRugY,1843 +sklearn/__check_build/_check_build.cpython-312-x86_64-linux-gnu.so,sha256=lzcY4zLoubG55siScnN8buQt6IQ20Uy1hWbL0SwmCxg,45304 +sklearn/__check_build/_check_build.pyx,sha256=8uo0MEvoqggJXyJug6X1iOtrHEjEuRHEy8XK9EEEsVE,30 +sklearn/__check_build/meson.build,sha256=kYUehV7zeGx_ckXUuJZoUHqzFr_QjTkEQFpzUdCmUeM,135 +sklearn/__init__.py,sha256=fd3KRzTRQlLjWNsK6L-gtU2k3nofZrpMkrObdRuzY7Q,4640 +sklearn/_build_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/_build_utils/tempita.py,sha256=D-5VlYirbKymB12g0lRet-BHq40YXbViGh51Ngr_yi8,1684 +sklearn/_build_utils/version.py,sha256=MXulZf33cp8otqGocAwKzSBIM6MUerYtE8fxqZsAfJA,448 +sklearn/_built_with_meson.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/_config.py,sha256=YzP6N9DtWSRDbPnw2xF3JKGdvAwkl5XDSXl-QqHETZs,14970 +sklearn/_cyutility.cpython-312-x86_64-linux-gnu.so,sha256=5yaNjyY0ZMrZV4wstTg3ae9UIi3NeBrWY9eSxLe84bY,195344 +sklearn/_distributor_init.py,sha256=HJ3OJ8FgzN3a-dNHePdJd_rdMK7_GYsnqU_fe3VuipE,424 +sklearn/_isotonic.cpython-312-x86_64-linux-gnu.so,sha256=NOYjWEPUqtBm2czR5erdxj7UvQQpnDadpDQ1RQuj59g,182280 +sklearn/_isotonic.pyx,sha256=L1JOQOTp5SoiZjzZKTczOtGuxilh_0bAluCk8Q9YM3Y,3733 +sklearn/_loss/__init__.py,sha256=ZGxLoo-OlLqcwI4Za5lYA31dcTayjaZzO54BjuymyBQ,687 +sklearn/_loss/_loss.cpython-312-x86_64-linux-gnu.so,sha256=KlxzjwPJ_nvQKqS7xWFz3VjBA7FMtbDYb-BhPPNdUlA,2952761 +sklearn/_loss/_loss.pxd,sha256=8LvWX3YNUuv3E5KQtl2o68mEqzu3tFFGjk8Qn-9lnk0,4577 +sklearn/_loss/_loss.pyx.tp,sha256=PKBBX3n6ASIt5IuZYp8F4fWee5dK3RTNVyMrJKgfErA,53677 +sklearn/_loss/link.py,sha256=1-PzVdqnGp7eE1Q7UoILBLHwM9TRaYwN1P-jfXa7xp8,8126 +sklearn/_loss/loss.py,sha256=_39Z0lvdVL_hs8Llt_PjdmyoKwtzR-4cvIKFv9v1h1g,41317 +sklearn/_loss/meson.build,sha256=jltTivAK8aZP29ZOeyF6HuUihkj_qVV1UxhIm8Ux7uE,654 +sklearn/_loss/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/_loss/tests/test_link.py,sha256=XMHMLjmPA1RHLrAhexoMzVQRlokcCT9LMhajVVnneS0,3954 +sklearn/_loss/tests/test_loss.py,sha256=hSgF_G5R2cv1P3lrdYloiwdDYpimT-AwfU1jhcZ6FcQ,49712 +sklearn/_min_dependencies.py,sha256=Qvl6tAcvEqabDE6qyuOfRpNxA_tEFOBUTmTwyzHDH-k,2800 +sklearn/base.py,sha256=oq05xf9kkdBMNXOOlChg3vDoDWgArrFpDP1ov0a-jZE,47777 +sklearn/calibration.py,sha256=Tg-mX0eMYow7li9GGBH8UDfYvrniuNMbiiFD-P4ottU,51595 +sklearn/cluster/__init__.py,sha256=DPe0qNOVhLx5mSDInkJBNgxd-22nhkKLjBlRV-6fWYg,1476 +sklearn/cluster/_affinity_propagation.py,sha256=axsTYyWEvxM8Drc7hO9BDWKWwGgFi_wXqmByIgF21AU,20706 +sklearn/cluster/_agglomerative.py,sha256=RipbZwQoduArZZTJQmCfFnZr2JheIbB0NkWd4ruUTKM,49368 +sklearn/cluster/_bicluster.py,sha256=89i_H3m0wrBHvDw11qM2yKQ_dFZqa9-mKGpBIEHLU_w,21975 +sklearn/cluster/_birch.py,sha256=10YX8EiSfbXlnJnpRuTvBpb793HoR5wcLagtpFkRkTM,26834 +sklearn/cluster/_bisect_k_means.py,sha256=Z0WCdf03rpS3m1XkEjyIfBC_r6ch8cNtAJcA632nfzw,19359 +sklearn/cluster/_dbscan.py,sha256=25tD7FhfLbgcDUkU_jxP78lXqjCsqEx65vtH6WCjWMg,18529 +sklearn/cluster/_dbscan_inner.cpython-312-x86_64-linux-gnu.so,sha256=-RfF1ERNt4G5C5-_kYMjK8MxbUaB1xcE1oK7mPeeMPI,78512 +sklearn/cluster/_dbscan_inner.pyx,sha256=JQ2riqW6JizG8wgHc2i_eKZUnNK_clS8dGE60NMCp1U,1318 +sklearn/cluster/_feature_agglomeration.py,sha256=2wo8vtVMqz0pwMb4cYVUTXcswjeGdkFbs3XNlaJMJn4,2426 +sklearn/cluster/_hdbscan/__init__.py,sha256=vKm4xxqJIfK6Tk6By-YU03YcE6pR1X1juFrOsacfZjY,79 +sklearn/cluster/_hdbscan/_linkage.cpython-312-x86_64-linux-gnu.so,sha256=5BgFqocTxG0gehXC8x1IvTjUplt0OI_Sg_u3xLgpoyw,141232 +sklearn/cluster/_hdbscan/_linkage.pyx,sha256=bDCkEDXyZ8M0t8wIp1uUiQWNrNuLVHaGQVw_NDFXpvU,10252 +sklearn/cluster/_hdbscan/_reachability.cpython-312-x86_64-linux-gnu.so,sha256=2ZQUGM4ANo3KLx0DjQn76ObMXdCxVHc0sKCpAc5VEK4,248744 +sklearn/cluster/_hdbscan/_reachability.pyx,sha256=Ap27H1gEE43fLRtR4RqtJ5BnBSWoxeUKYhj4u2OtqHU,7774 +sklearn/cluster/_hdbscan/_tree.cpython-312-x86_64-linux-gnu.so,sha256=ZprUIFg8lxheelZmhgcqwS3KRHfewK8EHktzBBJ3zuU,266096 +sklearn/cluster/_hdbscan/_tree.pxd,sha256=Nm7ghFqifD2vLnyBoCQCn9eFsmoB8ITpEuCMItJZoM4,2150 +sklearn/cluster/_hdbscan/_tree.pyx,sha256=Fs7cI-3EjHEmLqFwDx4JvrO_vuil32llUG9w4-ElaSs,27781 +sklearn/cluster/_hdbscan/hdbscan.py,sha256=oQuEMFAJZWjbfOf34ghlgVEmn5tQBtSepy90scB0HVI,41019 +sklearn/cluster/_hdbscan/meson.build,sha256=7qnGFFfS5OsBpQLS6xdBUVIcUjzd_VZrkG8Sg1WEw0Y,492 +sklearn/cluster/_hdbscan/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/cluster/_hdbscan/tests/test_reachibility.py,sha256=HCzRrBdtYARu83re_Z-Mu-hEZzhVWKNzCDpuZD_M3rM,2065 +sklearn/cluster/_hierarchical_fast.cpython-312-x86_64-linux-gnu.so,sha256=Gi_mPxtWfGdaX7hJzBEJAYWOilFbMgGH2YfbYOJ1aCg,209016 +sklearn/cluster/_hierarchical_fast.pxd,sha256=JlWtArNtEgc2RBeCJRADftNTPwNV_M-OAsAJz7lHqzY,245 +sklearn/cluster/_hierarchical_fast.pyx,sha256=DJe1c-WdbgLaClMwJjucwFwbGePFqnm0vPSdXyGQy2U,15927 +sklearn/cluster/_k_means_common.cpython-312-x86_64-linux-gnu.so,sha256=eq556Lg5ti53fDsqF9POs2R2EVE1mQIhXY8aHZb0YVk,413297 +sklearn/cluster/_k_means_common.pxd,sha256=6QW18TtC1wGpyTd0cdG9PxSYTiP4ZN3hj6ltJWrdaic,887 +sklearn/cluster/_k_means_common.pyx,sha256=w8e0U721_57eE97moyGYtGEULsDA1LhsHzqR6pvrD0s,10206 +sklearn/cluster/_k_means_elkan.cpython-312-x86_64-linux-gnu.so,sha256=eRFpuBM994NcXCNLg0g94zao0Bb8oVnrjKgRiiRTMGU,388937 +sklearn/cluster/_k_means_elkan.pyx,sha256=9qqaR6NCvT994gFfZVVV5nQ7qZOdYsr1UgPdXad_dQs,28164 +sklearn/cluster/_k_means_lloyd.cpython-312-x86_64-linux-gnu.so,sha256=HenECDNT5JkAKk5Cvl5NnfwU2uOGQISijpi-bkEodm0,269953 +sklearn/cluster/_k_means_lloyd.pyx,sha256=Ns8rod9sRad_un-fpePHDOqwM6MB6lT-0_Fivhmm9E4,16472 +sklearn/cluster/_k_means_minibatch.cpython-312-x86_64-linux-gnu.so,sha256=OiFgcLA-NwmrBdbzRPyGwW2DXfgVXA-HNHxvJGiOpeI,203945 +sklearn/cluster/_k_means_minibatch.pyx,sha256=ytlKAPQuIgC54Wc8t8OlzeS8qi6HMALyKcun4lWOjR4,8156 +sklearn/cluster/_kmeans.py,sha256=Lg2oA_QcyHDfRcWaJM0tqTxG0GR4X-8-jVwuSRLZyAM,81743 +sklearn/cluster/_mean_shift.py,sha256=r5TJitv8uVAwAP_15btOVXNzZzhwSzJOqXB4rDp-hwA,20284 +sklearn/cluster/_optics.py,sha256=E7IWBHG9ygbfgzKOumTQo-ft33nStfTDaDzoiuvF8xs,44932 +sklearn/cluster/_spectral.py,sha256=siT1f8-8plaS2L0f36nC1wrq1i1OShU_KPdFjRksuOA,30936 +sklearn/cluster/meson.build,sha256=UBtHRFqB7JvZQ3o6rP4FsLccnPlbs5WYYBXNlO1dfmQ,975 +sklearn/cluster/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/cluster/tests/common.py,sha256=1jmt9fXRXYt9TYCwJFcgDGV10slNNjJW7_2tRCSzJBY,880 +sklearn/cluster/tests/test_affinity_propagation.py,sha256=p-q92owXh0cG1oPD3d5VZOfQoZMwEeDfRlTAS25NTa0,11898 +sklearn/cluster/tests/test_bicluster.py,sha256=JJjahw-5rSvyNcKpz0ZtM1jl07jvLAB5D9zdzcqMXU4,9126 +sklearn/cluster/tests/test_birch.py,sha256=0c5tVBWc7lY4R-7oBwF8cpvI3-qploOHWp5poqF9KaY,8857 +sklearn/cluster/tests/test_bisect_k_means.py,sha256=1hf2vfXJ_0aIncY-bZMgx5TXTzGI49YCfVxChYrsLno,5139 +sklearn/cluster/tests/test_dbscan.py,sha256=8T5QOHsOI7ZnCYcBgcRE1AMT9IUanlFImxxsr3TKi1E,15704 +sklearn/cluster/tests/test_feature_agglomeration.py,sha256=V1apZXrZLF631DBVs72OMsZFGTHmb-ZdhvwXGd1vew0,1965 +sklearn/cluster/tests/test_hdbscan.py,sha256=xLYnVvA0Yo1KLqh_axWs1eQCqWGlJbaSiKVhf2QszpA,19401 +sklearn/cluster/tests/test_hierarchical.py,sha256=70Nqw-utJHu80ixqqOL2NC3nxZFOm-oBDaV2y1VIZtU,32118 +sklearn/cluster/tests/test_k_means.py,sha256=mmTpatBS9EzfckKi84LghrIIX30tbews5dUdYX4irsU,48754 +sklearn/cluster/tests/test_mean_shift.py,sha256=g6nBLNG0dPijUCTeM6ScqUpI9irAOv6tEG3U0n-rh-Y,7081 +sklearn/cluster/tests/test_optics.py,sha256=TYCTdbrzrD7AB9zRPpkhCZSt3OhvcNuvn3pLw-bIDyk,24787 +sklearn/cluster/tests/test_spectral.py,sha256=fDPwNrFgA-3PLF9RFNxhVvu5seE5c8bTDpoZxqHSvUM,11763 +sklearn/compose/__init__.py,sha256=XU4j8dd7SFuy5r0AfTLZ36XsEcIP_IqjQWNGC7Grs0g,631 +sklearn/compose/_column_transformer.py,sha256=4PyzFFEsikh_SN-39S31zafhpbH7wAyRIcLjN7X6rJQ,63786 +sklearn/compose/_target.py,sha256=uFf1Nxnfjpq_JiOKha8bBrGP-7y2yyf0GKwnNh4NGB0,14600 +sklearn/compose/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/compose/tests/test_column_transformer.py,sha256=ZcZd51x2ThfFDIDbkTkhWMj6MW02ReAUCLpgXWkNpV8,95077 +sklearn/compose/tests/test_target.py,sha256=OzwK81_h6he_AqlrK1_7R_pnu03IcaMkYOJfGA2uqKw,14891 +sklearn/conftest.py,sha256=TH7GXOavTSmLTwOGKqaGldetfIOGMEja4RZ8bepC9jo,13083 +sklearn/covariance/__init__.py,sha256=IsRnf4hz1aAODGnrFiF3VaptjqC0NRqrHPW1iiDOj3s,1171 +sklearn/covariance/_elliptic_envelope.py,sha256=z0xSxlDx7IyvjXkmHH9wiYeM3Ub7oz6abuCG913aqJ8,9055 +sklearn/covariance/_empirical_covariance.py,sha256=8nmLvVu9kDRWrAIYPv0-maCUr6b3_HS8zcA4CF4i-wI,12297 +sklearn/covariance/_graph_lasso.py,sha256=ty136Rp5nd73TVUcbQn-0VpS8uzQFbYe4j9JTKhg8Ik,40298 +sklearn/covariance/_robust_covariance.py,sha256=q4Fu19fLfu9MI42icnD4g1Q73Qapvzj30DniREhvJ24,34403 +sklearn/covariance/_shrunk_covariance.py,sha256=4l2H9FOzMbdUUsOVaDLdfWjq3Xoi35epcEguU0uQzJ4,28038 +sklearn/covariance/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/covariance/tests/test_covariance.py,sha256=eAp8bVdc5VYO7-LakTQBEl8Bku-I1xcstkp-wn2sbm8,14038 +sklearn/covariance/tests/test_elliptic_envelope.py,sha256=xCxtRYDNADB22KJURLGRqI2OoWh4LLfazpjgsIWOzH4,1587 +sklearn/covariance/tests/test_graphical_lasso.py,sha256=WqWXj3Hxd_Q9jxFL2Jn3r_5lgYXAz7qESoI49wwEOzg,10972 +sklearn/covariance/tests/test_robust_covariance.py,sha256=IUtakkWbJCbM753mqbIs76536SHWZ4uK6sgXv-9qIUY,6370 +sklearn/cross_decomposition/__init__.py,sha256=o35MjQxe2HkuWiAEgtgMGvy3ui_Otfo8opg0yw2uUh8,244 +sklearn/cross_decomposition/_pls.py,sha256=fPyCpmeF6GKzRAVawwZc4Ffa56R26526DD9OUS2rSfI,36972 +sklearn/cross_decomposition/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/cross_decomposition/tests/test_pls.py,sha256=NK4bLt4YwegJnyDR7F7XuNwPeQ4WR-AqXVve1I5vkEY,23488 +sklearn/datasets/__init__.py,sha256=OIl-zBuJJkFSHzL6ZFJfB1EJ1s-j1adLtEyFaakQxy8,5186 +sklearn/datasets/_arff_parser.py,sha256=tjZDgNyIqQ1I6zPIwkxZyCXcrW1p_QNy9xLSO9_ZMMY,19160 +sklearn/datasets/_base.py,sha256=l8clUavEbBSMCMoOzIhfT5WWSnnFGIjG4G_BPrYYgM0,53388 +sklearn/datasets/_california_housing.py,sha256=W8PzRJpPbAp69aMcsSEGnIJyxe672j_gvW3wmLug34Y,7279 +sklearn/datasets/_covtype.py,sha256=icC_R-02b83gIWJQq53E4_6Q8n8UiAOzFKHzRsSYFYY,8075 +sklearn/datasets/_kddcup99.py,sha256=1f1Ss2pFpnsVmSZOSWGGZw7pvpLIBwR__jPevyfg0Lo,13961 +sklearn/datasets/_lfw.py,sha256=wObR1RrTviwH_K0RAFa_GjOAlZ74a6q2rszhrSq2J4o,22588 +sklearn/datasets/_olivetti_faces.py,sha256=_JgWZdUL7j51hNnquvZw76yvXChFhQnS-wSNBREoDUY,6075 +sklearn/datasets/_openml.py,sha256=5LR7oE2SAkbkwnchwlicHuPu_iBwM4H0asKxs3ioywU,41790 +sklearn/datasets/_rcv1.py,sha256=oBpLrSj4ENcQAmKBpakBYZIm7Ao-7CGqKET7J6HbWzg,11861 +sklearn/datasets/_samples_generator.py,sha256=0tJqRu2coJB9E_LAetgzVK13nc4HE_w3x9aHcepuCDQ,76834 +sklearn/datasets/_species_distributions.py,sha256=ZJjzcktxxA6hHOVb8y9CkiDolZtKlGO4GCUCQAIU1qc,9407 +sklearn/datasets/_svmlight_format_fast.cpython-312-x86_64-linux-gnu.so,sha256=rnqqrpUWYm7ITk7BLrSNMt4S8OgQnhqvaxrdB4Rx6jo,455480 +sklearn/datasets/_svmlight_format_fast.pyx,sha256=9eDLPP_HvkuCzJbFH4hmlrsuAlYcD7CtEujLyET0yz0,7196 +sklearn/datasets/_svmlight_format_io.py,sha256=515Dc6TLQnKu_2HbNQTnZhZK5oHH2CwBHA03Y49EKdY,20839 +sklearn/datasets/_twenty_newsgroups.py,sha256=JOjPJEx34HAMdjcgGfd4r5SwUOL95OoePSUdzcyfTbs,20808 +sklearn/datasets/data/__init__.py,sha256=vKm4xxqJIfK6Tk6By-YU03YcE6pR1X1juFrOsacfZjY,79 +sklearn/datasets/data/breast_cancer.csv,sha256=_tPrctBXXvYZIpP1CTxugBsUdrV30Dhr9EVVBFIhcu0,119913 +sklearn/datasets/data/diabetes_data_raw.csv.gz,sha256=o-lMx86gD4qE-l9jRSA5E6aO-kLfGPh935vq1yG_1QM,7105 +sklearn/datasets/data/diabetes_target.csv.gz,sha256=jlP2XrgR30PCBvNTS7OvDl_tITvDfta6NjEBV9YCOAM,1050 +sklearn/datasets/data/digits.csv.gz,sha256=CfZubeve4s0rWuWeDWq7tz_CsOAYXS4ZV-nrtR4jqiI,57523 +sklearn/datasets/data/iris.csv,sha256=8T_6j91W_Y5sjRbUCBo_vTEUvNCq5CVsQyBRac2dFEk,2734 +sklearn/datasets/data/linnerud_exercise.csv,sha256=y42MJJN2Q_okWWgu-4bF5me81t2TEJ7vgZZNnp8Rv4w,212 +sklearn/datasets/data/linnerud_physiological.csv,sha256=K_fgXBzX0K3w7KHkVpQfYkvtCk_JZpTWDQ_3hT7F_Pc,219 +sklearn/datasets/data/wine_data.csv,sha256=EOioApCLNPhuXajOli88gGaUvJhFChj2GFGvWfMkvt4,11157 +sklearn/datasets/descr/__init__.py,sha256=vKm4xxqJIfK6Tk6By-YU03YcE6pR1X1juFrOsacfZjY,79 +sklearn/datasets/descr/breast_cancer.rst,sha256=PFhVGCpE0SyR8fsnOIdB-3C0uSukD7dC3Km15ATGjxk,4794 +sklearn/datasets/descr/california_housing.rst,sha256=Cr6d8BzCwbHjKZi21qSNLumQppwIzjx4zGIJbxSUEfE,1720 +sklearn/datasets/descr/covtype.rst,sha256=C6DmczitjtnrO-XhCIi8WqNT0uPgYnPWNYtKwJTwcn4,1191 +sklearn/datasets/descr/diabetes.rst,sha256=B9z8E5V6gkhb385Ers_7py55d1lZZtEYuB8WLLgn44E,1455 +sklearn/datasets/descr/digits.rst,sha256=jn5Y1hKVj32bDeGTHtaLIRcD7rI56Ajz2CxfCDfMAiI,2007 +sklearn/datasets/descr/iris.rst,sha256=cfhnSai8Uo0ht9sPlTMuMjDRMjGgXCcg5TeyxaqO9ek,2656 +sklearn/datasets/descr/kddcup99.rst,sha256=qRz2X8XmUh8IZKjzT1OAJd5sj91bBo0xpdcV5rS2Jko,3919 +sklearn/datasets/descr/lfw.rst,sha256=8sj8ApMwZDHabnaksL4S-WHS1V-k-divU7DGPJWP7Aw,4409 +sklearn/datasets/descr/linnerud.rst,sha256=jDI-AIsVeZZTVVWSiUztp5lEL4H2us847bgF3FSGb1s,704 +sklearn/datasets/descr/olivetti_faces.rst,sha256=i8Y7-g4fOPdLvupgJ8i_ze1pA0hGpfDgAoPCGvCPFxI,1834 +sklearn/datasets/descr/rcv1.rst,sha256=mLj4WU7aEVqaJg7hgSSe81oI74L6_pGECR72O8dEMZ4,2455 +sklearn/datasets/descr/species_distributions.rst,sha256=L80eaLcb9ymJOZyFLoQhDykU9dwiouRFRTD-_IrKFsI,1648 +sklearn/datasets/descr/twenty_newsgroups.rst,sha256=Z5efG4-mdET4H4sXgCt37IHL08EUzKbnucB190o_GD8,10923 +sklearn/datasets/descr/wine_data.rst,sha256=R4crlpp_b1Q_B9Jo2-Jq-3djwbQO5qpBTtee9y6t6cY,3355 +sklearn/datasets/images/README.txt,sha256=PH7xWh-iW5mNOMkhMjeGNZVare3B3PPkDmPcAJj2uPc,709 +sklearn/datasets/images/__init__.py,sha256=vKm4xxqJIfK6Tk6By-YU03YcE6pR1X1juFrOsacfZjY,79 +sklearn/datasets/images/china.jpg,sha256=g3gCWtJRnWSdAuMr2YmQ20q1cjV9nwmEHC-_u0_vrSk,196653 +sklearn/datasets/images/flower.jpg,sha256=p39uxB41Ov34vf8uqYGylVU12NgylPjPpJz05CPdVjg,142987 +sklearn/datasets/meson.build,sha256=Vx9GBA1WjNkluNaLNncDqp7NsZ6jTw3Ymw7htBHfy2M,173 +sklearn/datasets/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/datasets/tests/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/datasets/tests/data/openml/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/datasets/tests/data/openml/id_1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/datasets/tests/data/openml/id_1/api-v1-jd-1.json.gz,sha256=hi4IUgokM6SVo7066f2ebHxUCpxjLbKbuCUnhMva13k,1786 +sklearn/datasets/tests/data/openml/id_1/api-v1-jdf-1.json.gz,sha256=qWba1Yz1-8kUo3StVVbAQU9e2WIjftVaN5_pbjCNAN4,889 +sklearn/datasets/tests/data/openml/id_1/api-v1-jdq-1.json.gz,sha256=hKhybSw_i7ynnVTYsZEVh0SxmTFG-PCDsRGo6nhTYFc,145 +sklearn/datasets/tests/data/openml/id_1/data-v1-dl-1.arff.gz,sha256=z-iUW5SXcLDaQtr1jOZ9HF_uJc97T9FFFhg3wqvAlCk,1841 +sklearn/datasets/tests/data/openml/id_1119/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/datasets/tests/data/openml/id_1119/api-v1-jd-1119.json.gz,sha256=xB5fuz5ZzU3oge18j4j5sDp1DVN7pjWByv3mqv13rcE,711 +sklearn/datasets/tests/data/openml/id_1119/api-v1-jdf-1119.json.gz,sha256=gviZ7cWctB_dZxslaiKOXgbfxeJMknEudQBbJRsACGU,1108 +sklearn/datasets/tests/data/openml/id_1119/api-v1-jdl-dn-adult-census-l-2-dv-1.json.gz,sha256=Sl3DbKl1gxOXiyqdecznY8b4TV2V8VrFV7PXSC8i7iE,364 +sklearn/datasets/tests/data/openml/id_1119/api-v1-jdl-dn-adult-census-l-2-s-act-.json.gz,sha256=bsCVV4iRT6gfaY6XpNGv93PXoSXtbnacYnGgtI_EAR0,363 +sklearn/datasets/tests/data/openml/id_1119/api-v1-jdq-1119.json.gz,sha256=73y8tYwu3P6kXAWLdR-vd4PnEEYqkk6arK2NR6fp-Us,1549 +sklearn/datasets/tests/data/openml/id_1119/data-v1-dl-54002.arff.gz,sha256=aTGvJWGV_N0uR92LD57fFvvwOxmOd7cOPf2Yd83wlRU,1190 +sklearn/datasets/tests/data/openml/id_1590/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/datasets/tests/data/openml/id_1590/api-v1-jd-1590.json.gz,sha256=mxBa3-3GtrgvRpXKm_4jI5MDTN95gDUj85em3Fv4JNE,1544 +sklearn/datasets/tests/data/openml/id_1590/api-v1-jdf-1590.json.gz,sha256=BG9eYFZGk_DzuOOCclyAEsPgWGRxOcJGhc7JhOQPzQA,1032 +sklearn/datasets/tests/data/openml/id_1590/api-v1-jdq-1590.json.gz,sha256=RLmw0pCh4zlpWkMUOPhAgAccVjUWHDl33Rf0wnsAo0o,1507 +sklearn/datasets/tests/data/openml/id_1590/data-v1-dl-1595261.arff.gz,sha256=7h3N9Y8vEHL33RtDOIlpxRvGz-d24-lGWuanVuXdsQo,1152 +sklearn/datasets/tests/data/openml/id_2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/datasets/tests/data/openml/id_2/api-v1-jd-2.json.gz,sha256=pnLUNbl6YDPf0dKlyCPSN60YZRAb1eQDzZm1vguk4Ds,1363 +sklearn/datasets/tests/data/openml/id_2/api-v1-jdf-2.json.gz,sha256=wbg4en0IAUocCYB65FjKdmarijxXnL-xieCcbX3okqY,866 +sklearn/datasets/tests/data/openml/id_2/api-v1-jdl-dn-anneal-l-2-dv-1.json.gz,sha256=6QCxkHlSJP9I5GocArEAINTJhroUKIDALIbwtHLe08k,309 +sklearn/datasets/tests/data/openml/id_2/api-v1-jdl-dn-anneal-l-2-s-act-.json.gz,sha256=_2Ily5gmDKTr7AFaGidU8qew2_tNDxfc9nJ1QhVOKhA,346 +sklearn/datasets/tests/data/openml/id_2/api-v1-jdq-2.json.gz,sha256=xG9sXyIdh33mBLkGQDsgy99nTxIlvNuz4VvRiCpppHE,1501 +sklearn/datasets/tests/data/openml/id_2/data-v1-dl-1666876.arff.gz,sha256=1XsrBMrlJjBmcONRaYncoyyIwVV4EyXdrELkPcIyLDA,1855 +sklearn/datasets/tests/data/openml/id_292/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/datasets/tests/data/openml/id_292/api-v1-jd-292.json.gz,sha256=Hmo4152PnlOizhG2i0FTBi1OluwLNo0CsuZPGzPFFpM,551 +sklearn/datasets/tests/data/openml/id_292/api-v1-jd-40981.json.gz,sha256=wm3L4wz7ORYfMFsrPUOptQrcizaNB0lWjEcQbL2yCJc,553 +sklearn/datasets/tests/data/openml/id_292/api-v1-jdf-292.json.gz,sha256=JVwW8z7Sln_hAM2AEafmn3iWA3JLHsLs-R3-tyBnwZA,306 +sklearn/datasets/tests/data/openml/id_292/api-v1-jdf-40981.json.gz,sha256=JVwW8z7Sln_hAM2AEafmn3iWA3JLHsLs-R3-tyBnwZA,306 +sklearn/datasets/tests/data/openml/id_292/api-v1-jdl-dn-australian-l-2-dv-1-s-dact.json.gz,sha256=jvYCVCX9_F9zZVXqOFJSr1vL9iODYV24JIk2bU-WoKc,327 +sklearn/datasets/tests/data/openml/id_292/api-v1-jdl-dn-australian-l-2-dv-1.json.gz,sha256=naCemmAx0GDsQW9jmmvzSYnmyIzmQdEGIeuQa6HYwpM,99 +sklearn/datasets/tests/data/openml/id_292/api-v1-jdl-dn-australian-l-2-s-act-.json.gz,sha256=NYkNCBZcgEUmtIqtRi18zAnoCL15dbpgS9YSuWCHl6w,319 +sklearn/datasets/tests/data/openml/id_292/data-v1-dl-49822.arff.gz,sha256=t-4kravUqu1kGbQ_6dP4bVX89L7g8WmK4h2GwnATFOM,2532 +sklearn/datasets/tests/data/openml/id_3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/datasets/tests/data/openml/id_3/api-v1-jd-3.json.gz,sha256=BmohZnmxl8xRlG4X7pouKCFUJZkbDOt_EJiMFPfz-Gk,2473 +sklearn/datasets/tests/data/openml/id_3/api-v1-jdf-3.json.gz,sha256=7E8ta8TfOIKwi7oBVx4HkqVveeCpItmEiXdzrNKEtCY,535 +sklearn/datasets/tests/data/openml/id_3/api-v1-jdq-3.json.gz,sha256=Ce8Zz60lxd5Ifduu88TQaMowY3d3MKKI39b1CWoMb0Y,1407 +sklearn/datasets/tests/data/openml/id_3/data-v1-dl-3.arff.gz,sha256=xj_fiGF2HxynBQn30tFpp8wFOYjHt8CcCabbYSTiCL4,19485 +sklearn/datasets/tests/data/openml/id_40589/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/datasets/tests/data/openml/id_40589/api-v1-jd-40589.json.gz,sha256=WdGqawLSNYwW-p5Pvv9SOjvRDr04x8NxkR-oM1573L8,598 +sklearn/datasets/tests/data/openml/id_40589/api-v1-jdf-40589.json.gz,sha256=gmurBXo5KfQRibxRr6ChdSaV5jzPIOEoymEp6eMyH8I,856 +sklearn/datasets/tests/data/openml/id_40589/api-v1-jdl-dn-emotions-l-2-dv-3.json.gz,sha256=Geayoqj-xUA8FGZCpNwuB31mo6Gsh-gjm9HdMckoq5w,315 +sklearn/datasets/tests/data/openml/id_40589/api-v1-jdl-dn-emotions-l-2-s-act-.json.gz,sha256=TaY6YBYzQLbhiSKr_n8fKnp9oj2mPCaTJJhdYf-qYHU,318 +sklearn/datasets/tests/data/openml/id_40589/api-v1-jdq-40589.json.gz,sha256=0PeXMZPrNdGemdHYvKPH86i40EEFCK80rVca7o7FqwU,913 +sklearn/datasets/tests/data/openml/id_40589/data-v1-dl-4644182.arff.gz,sha256=LEImVQgnzv81CcZxecRz4UOFzuIGU2Ni5XxeDfx3Ub8,4344 +sklearn/datasets/tests/data/openml/id_40675/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/datasets/tests/data/openml/id_40675/api-v1-jd-40675.json.gz,sha256=p4d3LWD7_MIaDpb9gZBvA1QuC5QtGdzJXa5HSYlTpP0,323 +sklearn/datasets/tests/data/openml/id_40675/api-v1-jdf-40675.json.gz,sha256=1I2WeXida699DTw0bjV211ibZjw2QJQvnB26duNV-qo,307 +sklearn/datasets/tests/data/openml/id_40675/api-v1-jdl-dn-glass2-l-2-dv-1-s-dact.json.gz,sha256=Ie0ezF2HSVbpUak2HyUa-yFlrdqSeYyJyl4vl66A3Y8,317 +sklearn/datasets/tests/data/openml/id_40675/api-v1-jdl-dn-glass2-l-2-dv-1.json.gz,sha256=rQpKVHdgU4D4gZzoQNu5KKPQhCZ8US9stQ1b4vfHa8I,85 +sklearn/datasets/tests/data/openml/id_40675/api-v1-jdl-dn-glass2-l-2-s-act-.json.gz,sha256=FBumMOA56kS7rvkqKI4tlk_Dqi74BalyO0qsc4ompic,88 +sklearn/datasets/tests/data/openml/id_40675/api-v1-jdq-40675.json.gz,sha256=iPzcOm_tVpfzbcJi9pv_-4FHZ84zb_KKId7zqsk3sIw,886 +sklearn/datasets/tests/data/openml/id_40675/data-v1-dl-4965250.arff.gz,sha256=VD0IhzEvQ9n2Wn4dCL54okNjafYy1zgrQTTOu1JaSKM,3000 +sklearn/datasets/tests/data/openml/id_40945/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/datasets/tests/data/openml/id_40945/api-v1-jd-40945.json.gz,sha256=AogsawLE4GjvKxbzfzOuPV6d0XyinQFmLGkk4WQn610,437 +sklearn/datasets/tests/data/openml/id_40945/api-v1-jdf-40945.json.gz,sha256=lfCTjf3xuH0P_E1SbyyR4JfvdolIC2k5cBJtkI8pEDA,320 +sklearn/datasets/tests/data/openml/id_40945/api-v1-jdq-40945.json.gz,sha256=nH5aRlVKtqgSGDLcDNn3pg9QNM7xpafWE0a72RJRa1Q,1042 +sklearn/datasets/tests/data/openml/id_40945/data-v1-dl-16826755.arff.gz,sha256=UW6WH1GYduX4mzOaA2SgjdZBYKw6TXbV7GKVW_1tbOU,32243 +sklearn/datasets/tests/data/openml/id_40966/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/datasets/tests/data/openml/id_40966/api-v1-jd-40966.json.gz,sha256=NsY8OsjJ21mRCsv0x3LNUwQMzQ6sCwRSYR3XrY2lBHQ,1660 +sklearn/datasets/tests/data/openml/id_40966/api-v1-jdf-40966.json.gz,sha256=itrI4vjLy_qWd6zdSSepYUMEZdLJlAGDIWC-RVz6ztg,3690 +sklearn/datasets/tests/data/openml/id_40966/api-v1-jdl-dn-miceprotein-l-2-dv-4.json.gz,sha256=8MIDtGJxdc679SfYGRekmZEa-RX28vRu5ySEKKlI1gM,325 +sklearn/datasets/tests/data/openml/id_40966/api-v1-jdl-dn-miceprotein-l-2-s-act-.json.gz,sha256=MBOWtKQsgUsaFQON38vPXIWQUBIxdH0NwqUAuEsv0N8,328 +sklearn/datasets/tests/data/openml/id_40966/api-v1-jdq-40966.json.gz,sha256=Pe6DmH__qOwg4js8q8ANQr63pGmva9gDkJmYwWh_pjQ,934 +sklearn/datasets/tests/data/openml/id_40966/data-v1-dl-17928620.arff.gz,sha256=HF_ZP_7H3rY6lA_WmFNN1-u32zSfwYOTAEHL8X5g4sw,6471 +sklearn/datasets/tests/data/openml/id_42074/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/datasets/tests/data/openml/id_42074/api-v1-jd-42074.json.gz,sha256=T8shVZW7giMyGUPw31D1pQE0Rb8YGdU9PLW_qQ2eecA,595 +sklearn/datasets/tests/data/openml/id_42074/api-v1-jdf-42074.json.gz,sha256=OLdOfwKmH_Vbz6xNhxA9W__EP-uwwBnZqqFi-PdpMGg,272 +sklearn/datasets/tests/data/openml/id_42074/api-v1-jdq-42074.json.gz,sha256=h0KnS9W8EgrNkYbIqHN8tCDtmwCfreALJOfOUhd5fyw,722 +sklearn/datasets/tests/data/openml/id_42074/data-v1-dl-21552912.arff.gz,sha256=9iPnd8CjaubIL64Qp8IIjLODKY6iRFlb-NyVRJyb5MQ,2326 +sklearn/datasets/tests/data/openml/id_42585/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/datasets/tests/data/openml/id_42585/api-v1-jd-42585.json.gz,sha256=fMvxOOBmOJX5z1ERNrxjlcFT9iOK8urLajZ-huFdGnE,1492 +sklearn/datasets/tests/data/openml/id_42585/api-v1-jdf-42585.json.gz,sha256=CYUEWkVMgYa05pDr77bOoe98EyksmNUKvaRwoP861CU,312 +sklearn/datasets/tests/data/openml/id_42585/api-v1-jdq-42585.json.gz,sha256=Nzbn_retMMaGdcLE5IqfsmLoAwjJCDsQDd0DOdofwoI,348 +sklearn/datasets/tests/data/openml/id_42585/data-v1-dl-21854866.arff.gz,sha256=yNAMZpBXap7Dnhy3cFThMpa-D966sPs1pkoOhie25vM,4519 +sklearn/datasets/tests/data/openml/id_561/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/datasets/tests/data/openml/id_561/api-v1-jd-561.json.gz,sha256=odOP3WAbZ7ucbRYVL1Pd8Wagz8_vT6hkOOiZv-RJImw,1798 +sklearn/datasets/tests/data/openml/id_561/api-v1-jdf-561.json.gz,sha256=QHQk-3nMMLjp_5CQCzvykkSsfzeX8ni1vmAoQ_lZtO4,425 +sklearn/datasets/tests/data/openml/id_561/api-v1-jdl-dn-cpu-l-2-dv-1.json.gz,sha256=BwOwriC5_3UIfcYBZA7ljxwq1naIWOohokUVHam6jkw,301 +sklearn/datasets/tests/data/openml/id_561/api-v1-jdl-dn-cpu-l-2-s-act-.json.gz,sha256=cNRZath5VHhjEJ2oZ1wreJ0H32a1Jtfry86WFsTJuUw,347 +sklearn/datasets/tests/data/openml/id_561/api-v1-jdq-561.json.gz,sha256=h0Oy2T0sYqgvtH4fvAArl-Ja3Ptb8fyya1itC-0VvUg,1074 +sklearn/datasets/tests/data/openml/id_561/data-v1-dl-52739.arff.gz,sha256=6WFCteAN_sJhewwi1xkrNAriwo7D_8OolMW-dGuXClk,3303 +sklearn/datasets/tests/data/openml/id_61/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/datasets/tests/data/openml/id_61/api-v1-jd-61.json.gz,sha256=pcfnmqQe9YCDj7n8GQYoDwdsR74XQf3dUATdtQDrV_4,898 +sklearn/datasets/tests/data/openml/id_61/api-v1-jdf-61.json.gz,sha256=M8vWrpRboElpNwqzVgTpNjyHJWOTSTOCtRGKidWThtY,268 +sklearn/datasets/tests/data/openml/id_61/api-v1-jdl-dn-iris-l-2-dv-1.json.gz,sha256=C84gquf9kDeW2W1bOjZ3twWPvF8_4Jlu6dSR5O4j0TI,293 +sklearn/datasets/tests/data/openml/id_61/api-v1-jdl-dn-iris-l-2-s-act-.json.gz,sha256=qfS5MXmX32PtjSuwc6OQY0TA4L4Bf9OE6uw2zti5S64,330 +sklearn/datasets/tests/data/openml/id_61/api-v1-jdq-61.json.gz,sha256=QkzUfBKlHHu42BafrID7VgHxUr14RoskHUsRW_fSLyA,1121 +sklearn/datasets/tests/data/openml/id_61/data-v1-dl-61.arff.gz,sha256=r-RzaSRgZjiYTlcyNRkQJdQZxUXTHciHTJa3L17F23M,2342 +sklearn/datasets/tests/data/openml/id_62/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/datasets/tests/data/openml/id_62/api-v1-jd-62.json.gz,sha256=fvNVGtR9SAI8Wh8c8HcEeppLlVRLuR1Khgl_i1dPjQc,656 +sklearn/datasets/tests/data/openml/id_62/api-v1-jdf-62.json.gz,sha256=SJsXcSbLfzNcsiBwkjO5RtOgrXHTi7ptSLeRhxRuWFo,817 +sklearn/datasets/tests/data/openml/id_62/api-v1-jdq-62.json.gz,sha256=J4pSpS1WnwfRTGp4d7EEdix32qxCn7H9mBegN41uxjQ,805 +sklearn/datasets/tests/data/openml/id_62/data-v1-dl-52352.arff.gz,sha256=-1gwyCES9ipADIKsHxtethwpwKfMcrpW0q7_D66KYPk,1625 +sklearn/datasets/tests/data/svmlight_classification.txt,sha256=an5ZLlFP2RJkK0iT8V6B5NLpNvZFUEzpTonY-Frcv0o,253 +sklearn/datasets/tests/data/svmlight_invalid.txt,sha256=ueCvdPekdiYpH8FAH_AW9MHiyMd9SulhrkJ8FQm3ol8,54 +sklearn/datasets/tests/data/svmlight_invalid_order.txt,sha256=xSNKVNcM7TuWkTyTZnQSTTcoBdERxUKoM2yz_gFCaHA,23 +sklearn/datasets/tests/data/svmlight_multilabel.txt,sha256=DsT6kKm83Ac7HLhmw6d6P0e2YNSdL7-ES3lgk7BozW4,104 +sklearn/datasets/tests/test_20news.py,sha256=-EdeU6SLVlTPCGtatJRplVBvPrt6AygXgeNz_9JF-8Y,5340 +sklearn/datasets/tests/test_arff_parser.py,sha256=n9WpxiBJ_AvltjDGmH8VLJyX6EXLWzhQQoGKTLYYbEI,8196 +sklearn/datasets/tests/test_base.py,sha256=ARlzPUqsECOclOcFbmglzjEAKIAlKYcEWDcpLEU5ppE,23022 +sklearn/datasets/tests/test_california_housing.py,sha256=-kGKf35jMxfB9PgvNryrL3Xqil_CVhoWFPqRGoCdBoU,1369 +sklearn/datasets/tests/test_common.py,sha256=F2J7ng0CH0Izs6yJ979ZrTfR_LO9stx_WoiE9y-kwgc,4392 +sklearn/datasets/tests/test_covtype.py,sha256=rnS0G-zkPov-roszvXRwiNBG50tciwMKe-D_RKe2OYY,1757 +sklearn/datasets/tests/test_kddcup99.py,sha256=5rw4Pva1EC2CO7imU9NVe0OqTrmTCu_4hElGpvZkUfk,2601 +sklearn/datasets/tests/test_lfw.py,sha256=YWNdfvIMcBbCfBfDSlaKBB1_9Q9qBXGe9VOaUUTFXac,7796 +sklearn/datasets/tests/test_olivetti_faces.py,sha256=d2r43YseviKoA9OyX6JvDyXvY8lFRfV__j5hippkYY0,919 +sklearn/datasets/tests/test_openml.py,sha256=JDq-SFAF-pawcncA8bCqWFEmBdQ8n1j9PHUey4BY_nQ,54649 +sklearn/datasets/tests/test_rcv1.py,sha256=_MI_VuGKrZIIV-WMVxOEKMh94DqzhCrxV7l1E3NGkNM,2343 +sklearn/datasets/tests/test_samples_generator.py,sha256=CBWSP9td7WpU1vi8e2XiuMqauhXzvHHnXYJKZ22-56U,23846 +sklearn/datasets/tests/test_svmlight_format.py,sha256=mqKurK216uySN6hE-DAfHRt-6NHEGm4fBWyBIHpKCx0,20222 +sklearn/decomposition/__init__.py,sha256=joTYvN7TfssMwqycJWm9QjQqMknhLhm4CvpA3Xi3Jgg,1325 +sklearn/decomposition/_base.py,sha256=ghws6Nz8rN3zWFhk1DXbHAe-5oz9gpofEKey9G2Izx4,7147 +sklearn/decomposition/_cdnmf_fast.cpython-312-x86_64-linux-gnu.so,sha256=uFsuDOKix_57ENnJiNx_J-3lmAxgr-rz2wfaXN54xPU,117744 +sklearn/decomposition/_cdnmf_fast.pyx,sha256=ONJUPP9uKUn6uyUJHwHBD3qQeJmtM-7GFjFA8qCniJQ,1128 +sklearn/decomposition/_dict_learning.py,sha256=rIy4gcLaY434hn9jwcYQXTDvm0EB1ALgJ6cBoz8mDQ0,77761 +sklearn/decomposition/_factor_analysis.py,sha256=T-DH7_Wz9l3RJTDwzRTaPfQUCX9trhde3A_Yu3h9ysI,15245 +sklearn/decomposition/_fastica.py,sha256=araRZCVXh0BwHsGv4vp9p07DodDqTSrGPhKz77oFWwU,26553 +sklearn/decomposition/_incremental_pca.py,sha256=6dXXSI5OsmTXy8Ou8KhayQwf6n6wavqDiFa4z8C2fDg,16434 +sklearn/decomposition/_kernel_pca.py,sha256=0k8l6HvxhCZDOkgGQ9__KpvSY4dckQHaSVEYoaYh0nM,22584 +sklearn/decomposition/_lda.py,sha256=p26x3ZNmzm1bQeIkJTGxYrWiURgMdI-t4T2E3_nfXOM,34068 +sklearn/decomposition/_nmf.py,sha256=VY9hCOD73XvG06K934LiaZykUpGEgu_cXp--9xJ0-EA,81455 +sklearn/decomposition/_online_lda_fast.cpython-312-x86_64-linux-gnu.so,sha256=evfxvUgjSmeOWy4F_3T8akKHvjUE3NOX3JF2_nVwQrM,178992 +sklearn/decomposition/_online_lda_fast.pyx,sha256=AMEYftJohmE84AayqSAn0CXbAb2ac_QAL_OSbjOsFJw,2842 +sklearn/decomposition/_pca.py,sha256=VjuYIRlMY-K79harkthjzcgFL7rlkkLOoAwNfECIlZw,34601 +sklearn/decomposition/_sparse_pca.py,sha256=Zjhwze5bnNBOnmU-aBK8KEcSbDj4hRDSZ-B8CC-UfiY,17916 +sklearn/decomposition/_truncated_svd.py,sha256=n-I_HryY_AvycFDZWt8wKWEZ8nGUM8BZYuYBHtb6qj0,11708 +sklearn/decomposition/meson.build,sha256=Ou8NjxEeKMUenExdqgH-7ijrec-bkKI8Q_21ScKCjzM,322 +sklearn/decomposition/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/decomposition/tests/test_dict_learning.py,sha256=ZnG9Zi7tcVkk8t1jwHjlHVqMt2-Zax0-R9Co7zgY3vo,30587 +sklearn/decomposition/tests/test_factor_analysis.py,sha256=WOiKlwnFn4WqelwLa8ERB4dZsUbjF32h3-OTtRgRzZA,4032 +sklearn/decomposition/tests/test_fastica.py,sha256=BFYeGAA9DGiZb4tVFpKi__0QtECEFk47qkPQFZ4xOus,15916 +sklearn/decomposition/tests/test_incremental_pca.py,sha256=Oa2iwpd53fnOFsVX_U6-54AFjRDS8gs84alpmqMKwOY,16897 +sklearn/decomposition/tests/test_kernel_pca.py,sha256=8h17WzyseYxwyMbR1LIweP_yF6CXkoIYEbLJBYto9T8,21021 +sklearn/decomposition/tests/test_nmf.py,sha256=TcuG7v5R864EHgikwlA3LtueImtLhVc0NU0D1YbAqYs,32219 +sklearn/decomposition/tests/test_online_lda.py,sha256=HQz3SUqlQ1BVMwhymTGcx1BdOliU_C1Y0RKrHR6XT4A,16023 +sklearn/decomposition/tests/test_pca.py,sha256=madikr_ZdbbGxMJRoV3mbMxm3IcaLcvXraAf-4uvMFw,41916 +sklearn/decomposition/tests/test_sparse_pca.py,sha256=BZiQPrCsQuk0k1gVt8769hhLZJdoKgrffGW0sxsbJYI,12077 +sklearn/decomposition/tests/test_truncated_svd.py,sha256=ZVJ_Jv-HX-3YM5uDZ4rA_U6SOxC6kRQGCIe-vxAgYj0,7242 +sklearn/discriminant_analysis.py,sha256=StVAOtSw-kEHgW4UunwMyrWx5aTcbDzkfurKH5uxYsI,40512 +sklearn/dummy.py,sha256=BGnZaLCwgpPTHZZy9qjvy2NFSTX4xQZXrsBhQgdVtJk,24507 +sklearn/ensemble/__init__.py,sha256=emYX8q4bOw4SGxORFbVKSzOsRZwhm2H04PqFab1_-oY,1374 +sklearn/ensemble/_bagging.py,sha256=h4FpdoDbNDT_JjCXGbUe0_CGpD_mmrRROKoXNGI589E,52239 +sklearn/ensemble/_base.py,sha256=-CfPYQHpnf0RJ-mU0WZyENQWpIyLHMYPXoJNAAyBpPY,10543 +sklearn/ensemble/_forest.py,sha256=xaQjDmN1PZZGdDY728RxeTQPKEMxMxctzh7O9Oc7buQ,117697 +sklearn/ensemble/_gb.py,sha256=Ao8IAFBxxZmOtu9EFACfs5G8svxz-OGFhPbx0Km0BZ0,87765 +sklearn/ensemble/_gradient_boosting.cpython-312-x86_64-linux-gnu.so,sha256=s8bG5FPNa77ExwoG1yiSfhyHBL3ZQDm2YPkgOjoYagU,130904 +sklearn/ensemble/_gradient_boosting.pyx,sha256=Emsc3f3sNgCb7RgQV5f_mnXfDHPAI0N1gvQe6NaINwQ,8562 +sklearn/ensemble/_hist_gradient_boosting/__init__.py,sha256=CjfoMHKJd5hxBLWAbtW-lN4WAoAjhWpw8RwtcWmuX-s,246 +sklearn/ensemble/_hist_gradient_boosting/_binning.cpython-312-x86_64-linux-gnu.so,sha256=DXuErEVIiekzZCZzQ8NMF_cphbBxpwlA_wTfIhmiOb4,96777 +sklearn/ensemble/_hist_gradient_boosting/_binning.pyx,sha256=iQH5QwuI4ia7LHDw8RclXqhwscEWTc2H7vshtH5usOE,2786 +sklearn/ensemble/_hist_gradient_boosting/_bitset.cpython-312-x86_64-linux-gnu.so,sha256=2w9ydxHRh_e5O7bys69sNoYHua45ZL-oyem7HB2kmtM,84360 +sklearn/ensemble/_hist_gradient_boosting/_bitset.pxd,sha256=_5y92vr1nOs5_KyCfs2-E-hTnpEW5KTGjUTXMwthIQ0,708 +sklearn/ensemble/_hist_gradient_boosting/_bitset.pyx,sha256=Jyt_GO23ad6ZM7XKlEKxQlWV_j-s7cbVn83P44mr6d0,2540 +sklearn/ensemble/_hist_gradient_boosting/_gradient_boosting.cpython-312-x86_64-linux-gnu.so,sha256=palLnP9oest6-Bsfpo5jJyM6Kf4_3Cwvyg5CTyR7UAQ,100977 +sklearn/ensemble/_hist_gradient_boosting/_gradient_boosting.pyx,sha256=-0EYKZFppgamkie2aaQii29psvZjmd6g6rTAkeLOOos,1990 +sklearn/ensemble/_hist_gradient_boosting/_predictor.cpython-312-x86_64-linux-gnu.so,sha256=dbU-qd9Kc5xY1JBGsrKoTd4dys08HMAvkDnOf-vka5w,142105 +sklearn/ensemble/_hist_gradient_boosting/_predictor.pyx,sha256=oBIH9D7SzdCdiv7n0hZA-o54TI4kFFDdkc-GUtN_1f0,9575 +sklearn/ensemble/_hist_gradient_boosting/binning.py,sha256=7ZuQXsKA4FbHw8xI6dm7puW_0xjsCFTzgDdlRC0RwI0,13925 +sklearn/ensemble/_hist_gradient_boosting/common.cpython-312-x86_64-linux-gnu.so,sha256=VAJDShzcbp32hzVhWHzkVOrv0WUfWU8YpmpWR7wwYJQ,43560 +sklearn/ensemble/_hist_gradient_boosting/common.pxd,sha256=MLDp9cP2k6UeUENyhJKBynnwTSoUnfAG-J32TucOZpk,1244 +sklearn/ensemble/_hist_gradient_boosting/common.pyx,sha256=FSvUdsBMiLIAmvk1eke3C0PBo0dcmUIJ1omN7a-B0kY,1747 +sklearn/ensemble/_hist_gradient_boosting/gradient_boosting.py,sha256=-I7BWx-GHWrIUXuOPlNIu0Ui6-MSfF2JEnUCB4tCf9w,97143 +sklearn/ensemble/_hist_gradient_boosting/grower.py,sha256=QM1v8cGeXwdP3pOUnQ3PjKAPJ9NX6g9T-MsuFSBacsk,32674 +sklearn/ensemble/_hist_gradient_boosting/histogram.cpython-312-x86_64-linux-gnu.so,sha256=PQoIUxfFH_vfEbgwWB1DzqZ4xQN_d5dc51-0dWd2qr0,236753 +sklearn/ensemble/_hist_gradient_boosting/histogram.pyx,sha256=BB-f6QgDOPiQ_vUSUKTANZgpeeGON7Elpe6-yz8WwG0,20651 +sklearn/ensemble/_hist_gradient_boosting/meson.build,sha256=aNiFEGgexu7353QjlQ_MgjSL1hQK6wlBgLytMaZEgwE,979 +sklearn/ensemble/_hist_gradient_boosting/predictor.py,sha256=oBStnOotKnJcUp-lJCigLNOeOO4U0KywfrKo4zFBBzE,5029 +sklearn/ensemble/_hist_gradient_boosting/splitting.cpython-312-x86_64-linux-gnu.so,sha256=NWvu7CbbjfLxAIsNCy-rVb9Rld4GaMvlEbjjMhXuHb0,265457 +sklearn/ensemble/_hist_gradient_boosting/splitting.pyx,sha256=edDtSh-xGAJq7X7IWp-_hoTGpc-zaGFfcETr3WH7YXk,52287 +sklearn/ensemble/_hist_gradient_boosting/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/ensemble/_hist_gradient_boosting/tests/test_binning.py,sha256=aNXHw7u7IRAdEfHO2TWdjAmlj9y_SdhJir-w0yQ-fkc,16252 +sklearn/ensemble/_hist_gradient_boosting/tests/test_bitset.py,sha256=5QHny5G3p9tyExBsdsUVV2vFKgPI-vYDt-zvLpMBHXQ,2100 +sklearn/ensemble/_hist_gradient_boosting/tests/test_compare_lightgbm.py,sha256=yaUeaZ8g4F5J3Vrct3mfcR9djCMV2gKvn7ITF4QZtVM,10592 +sklearn/ensemble/_hist_gradient_boosting/tests/test_gradient_boosting.py,sha256=P8jW5nyqUKP9iIpDIL6kooPxgxyv_3DlZbPiErP3_Vk,63145 +sklearn/ensemble/_hist_gradient_boosting/tests/test_grower.py,sha256=mDda3Xp-vF2Kgqdz3bj5UUtC4jUZR--dCesLwmDI50c,23152 +sklearn/ensemble/_hist_gradient_boosting/tests/test_histogram.py,sha256=PBoacgv-6rOI5lTpzCyaafC9eDvyA6tb94RnDw_wLhs,8681 +sklearn/ensemble/_hist_gradient_boosting/tests/test_monotonic_constraints.py,sha256=ucsF7gy_hskZ1oDK6GSD1lr9ypKNqadkEFXRGeaNHfQ,16940 +sklearn/ensemble/_hist_gradient_boosting/tests/test_predictor.py,sha256=wq5vXIMwh7Fr3wDeHGO2F-oNNXEH_hUdyOyS7SIGXpE,6345 +sklearn/ensemble/_hist_gradient_boosting/tests/test_splitting.py,sha256=nkX5rAlTeO6tPR4_K4Gc9bvViPu1HUboA7-vRdiTETo,38639 +sklearn/ensemble/_hist_gradient_boosting/tests/test_warm_start.py,sha256=3Q_3ZhKf94uvmADlNMj0Vpyp7gqjDd1czBzFW8pUuAQ,7933 +sklearn/ensemble/_hist_gradient_boosting/utils.py,sha256=RiXIru1WQYuMxoj7Ko141DeH32WctBmQZeTfKwYdRcA,5523 +sklearn/ensemble/_iforest.py,sha256=o3PaqtullSmCARwGI0MsVukYzMRopDdeVnhvM3VW2Lw,24264 +sklearn/ensemble/_stacking.py,sha256=Z8D7xDNAg2nxEM_B4us7RoYz7_1OcF-p9lj3izrcM6U,43546 +sklearn/ensemble/_voting.py,sha256=WL7_PjWtf8V4fdf9fOpIYB79qAxaTp8U4q1hjfrxsu4,24834 +sklearn/ensemble/_weight_boosting.py,sha256=Cl1PzwH0DG5CGcOH22Eacd_cmkysykABwgIgD99aTYA,41097 +sklearn/ensemble/meson.build,sha256=7nR6oq_djKOBo-7Yxc-UmB6uWVai4w5By9i10tBX4hE,224 +sklearn/ensemble/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/ensemble/tests/test_bagging.py,sha256=YS38i0QjCIZ_XY3YSwstXAo5pa_65v6JHcreb5T2NfQ,33682 +sklearn/ensemble/tests/test_base.py,sha256=dCynI18UuKx7HpGwnUSjfUR2GlTfhGRmO_UA_-kDu6A,3667 +sklearn/ensemble/tests/test_common.py,sha256=kUynrJPb67QHmQZaVC0KPWvJkZAhTEKEF5WFSO8pM2k,9106 +sklearn/ensemble/tests/test_forest.py,sha256=Oko4dDMgB2z6H-UbGaCxKiwG2ezbhqiGKjPnc2QyJAM,62801 +sklearn/ensemble/tests/test_gradient_boosting.py,sha256=zrUVq7La0QRrA34MccQBCqatrPJlOwzHxy34AG-h5YA,58761 +sklearn/ensemble/tests/test_iforest.py,sha256=s2wpk7-N9Hr6hRWYvOAhsbQTkrRqXbu3CYisUNud6nQ,13539 +sklearn/ensemble/tests/test_stacking.py,sha256=Gqiay4pCaaZ68F-jDcTixcEKb7te7ztR-w9W2xqYHEU,33490 +sklearn/ensemble/tests/test_voting.py,sha256=HLY47XeqyoSuHR5jAD25TIcLAvFt4Kjt0MxXNEUVkR8,27499 +sklearn/ensemble/tests/test_weight_boosting.py,sha256=EPyS-E7pWkcs4-bJGzM2gE1rDpTGshTTki4kXAf593U,21928 +sklearn/exceptions.py,sha256=CaaFS4DVbqhqUidiA-cMvTP6DR6qIFmEUzuiMS5HDec,7703 +sklearn/experimental/__init__.py,sha256=0SSV8qXhFfA8-T9zvuWasIT8bNbPXLUX4ZQZp0CoDzk,305 +sklearn/experimental/enable_halving_search_cv.py,sha256=4s1q_AiYCx7jiZGmc7uieges2_MsYh8ykfUi3UC4qMw,1290 +sklearn/experimental/enable_hist_gradient_boosting.py,sha256=0vehofwAKeWQbeQO0B0E0lulIWUk1q4pwBCMFERSm3Q,826 +sklearn/experimental/enable_iterative_imputer.py,sha256=IgDLGeBd6XtbGp-K5xuef6edPfHGaLNakuDMfE_Vj9A,768 +sklearn/experimental/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/experimental/tests/test_enable_hist_gradient_boosting.py,sha256=cAFugPf0tYSd-P2-GlcfvhG7YnKlfMoqE8Pff7yXG-4,672 +sklearn/experimental/tests/test_enable_iterative_imputer.py,sha256=LWtq99MTXXga2dq_ZcB0korId_7ctVxKtZLrFNZvFns,1689 +sklearn/experimental/tests/test_enable_successive_halving.py,sha256=MVt6aApWKiR3VnVRnY7GEoQdI8w-f2M--w60vS0B5vA,1896 +sklearn/externals/README,sha256=GFbJH7vHxxuzJLaVlul1GkfwjREK64RyEXUCWL1NSxk,270 +sklearn/externals/__init__.py,sha256=jo7XxwlsquXvHghwURnScmXn3XraDerjG1fNR_e11-U,42 +sklearn/externals/_arff.py,sha256=YXR8xgF1IxyugQV70YHNjmza2yuz86zhVM1i6AI-RSA,38341 +sklearn/externals/_array_api_compat_vendor.py,sha256=Gb7C65qVPo5gbKKlpq4jHtXWkgsN0wIrTQAaFBbais0,198 +sklearn/externals/_packaging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/externals/_packaging/_structures.py,sha256=Ofe3RryZqacr5auj4s7MsEylGigfeyf8sagFvK-rPv0,2922 +sklearn/externals/_packaging/version.py,sha256=IDbp4Q6S9OZ3mP57YCDerh4Xm0s6AUqSi6CbFJ3eQyI,16134 +sklearn/externals/_scipy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/externals/_scipy/sparse/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/externals/_scipy/sparse/csgraph/__init__.py,sha256=GMAcZXBWt9Dp0QEOeCsQglt8CWB6_stqr7Wf_LfH0tE,34 +sklearn/externals/_scipy/sparse/csgraph/_laplacian.py,sha256=l1bAYnntljvIXc8mwJqSpLS6EBTjzMTb0XrW2_S1A1k,18166 +sklearn/externals/array_api_compat/LICENSE,sha256=T_2Xjj-hjQWNmMZncc_qftY0qvcCPPlhK4tV7umo8P4,1097 +sklearn/externals/array_api_compat/README.md,sha256=YjsmsQ3VNuGPaD7I6a_lvqGBVNBhm-k5ty-yWwIjjRY,67 +sklearn/externals/array_api_compat/__init__.py,sha256=zk6TZdJLBzT7Td3TKbCkYA1KIxKOsa-CKqDn0JCUq2I,992 +sklearn/externals/array_api_compat/_internal.py,sha256=pfbMacXgxBaLmhueWE54mtXrbBdxyLd2Gc7dHrxYtGk,1412 +sklearn/externals/array_api_compat/common/__init__.py,sha256=4IcMWP5rARLYe2_pgXDWEuj2YpM0c1G6Pb5pkbQ0QS8,38 +sklearn/externals/array_api_compat/common/_aliases.py,sha256=xvZcAGCBbbujmjh76EvaYDzgPfQhaHK8QH--CQI906U,19644 +sklearn/externals/array_api_compat/common/_fft.py,sha256=ckCR2uHtz0iaOkcuvqVunhz1khIdxQNKuVU0x1bfrq8,4669 +sklearn/externals/array_api_compat/common/_helpers.py,sha256=zIz2QmS4LEI-aT05xMzXTgZ6Y6aULKbxlZxWa_R-lb4,31586 +sklearn/externals/array_api_compat/common/_linalg.py,sha256=Wdf0FzzxJNEiGhOOsQKg8PnMusM3fVeN5CA4RBItF_Y,6856 +sklearn/externals/array_api_compat/common/_typing.py,sha256=Z5N8fYR_54UorD4IXFdOOigqYRDp6mNa-iA7703PKf4,4358 +sklearn/externals/array_api_compat/cupy/__init__.py,sha256=8KfEs6ULcXuZ4AUKBD_7L3XZfW8TOQayZPerR_YLeSI,390 +sklearn/externals/array_api_compat/cupy/_aliases.py,sha256=OgOoVRk-TI9t0hCsI82VLkebkZRdN7aXjamWMRw0yYQ,4842 +sklearn/externals/array_api_compat/cupy/_info.py,sha256=g3DwO5ps4bSlFU2pc_f4XTaLrkCYuSDlCw0Ql2wuqM8,10125 +sklearn/externals/array_api_compat/cupy/_typing.py,sha256=dkA_sAAgU1Zb1PNopuOsywbLeFK-rLWAY4V4Vj3-x0I,628 +sklearn/externals/array_api_compat/cupy/fft.py,sha256=xCAC42CNAwAyVW7uCREsSoAV23R3rL2dqrT7w877zuE,842 +sklearn/externals/array_api_compat/cupy/linalg.py,sha256=nKOM-_wcOHzHhEeV9KBzcMVNlviJK4nP1nFBUtvnjTM,1444 +sklearn/externals/array_api_compat/dask/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/externals/array_api_compat/dask/array/__init__.py,sha256=OkadrcCZUdp3KsB5q2fhTyAACW12gDXxW_A4ANGcAqY,320 +sklearn/externals/array_api_compat/dask/array/_aliases.py,sha256=ZmoAVGbsj04gcfE7R0V6N_7AXCZrhYSFXXfzJfJ5O4Y,10668 +sklearn/externals/array_api_compat/dask/array/_info.py,sha256=rpfvNrS4ZaZMEcaomlRFxx7Dqb_tohhDFvI6qYoaivI,12618 +sklearn/externals/array_api_compat/dask/array/fft.py,sha256=OZxTcLBCXKgVpbMo7Oqn9NH_7_9ZUHQdB6iP8WSYVfY,589 +sklearn/externals/array_api_compat/dask/array/linalg.py,sha256=AtkHftJ3hufuuSlZhRxR0RH9IureEet387rpn1h38XU,2451 +sklearn/externals/array_api_compat/numpy/__init__.py,sha256=7SOguTm7-yJgJPnFTlbk_4bPTltsgKLbkO59ZmoCODg,853 +sklearn/externals/array_api_compat/numpy/_aliases.py,sha256=SKaCfzc2eY1eAu3Yzm3JVuR3uUqL7PoXf6GyYyXpcw4,5715 +sklearn/externals/array_api_compat/numpy/_info.py,sha256=8KNJ09jKFfMH20wff67GJVPyoZ-e8-OUHF88THx-1Cs,10782 +sklearn/externals/array_api_compat/numpy/_typing.py,sha256=O03YoguInLXMcL5Q0JKHxRXSREgE0DCusVAZKAv-l10,626 +sklearn/externals/array_api_compat/numpy/fft.py,sha256=7oxAzAnFwsAH0J43eXFKRkJ_GKCVEC-7G_lz56pVBz8,779 +sklearn/externals/array_api_compat/numpy/linalg.py,sha256=ORu4MhuN6F5EXOy-lYHxfMHkRpVRx2VEC29rRwB8Bws,4039 +sklearn/externals/array_api_compat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/externals/array_api_compat/torch/__init__.py,sha256=o351abwQmNWcX00GBnGYHrpfM8pFiieFWRaf0NI-KFg,549 +sklearn/externals/array_api_compat/torch/_aliases.py,sha256=w_exCqFcAuB3TXtiqk_NpSHJ8D3ZawulLdjFxTvujQc,30261 +sklearn/externals/array_api_compat/torch/_info.py,sha256=-H2xD9z9SMf3GjIOW0jeRTUOvh4s8E9p9u_4LqawRZM,11889 +sklearn/externals/array_api_compat/torch/_typing.py,sha256=-uCkuTie1g9hb4vwPLK9eEnir9Zp67wAhrfaI_o-35E,108 +sklearn/externals/array_api_compat/torch/fft.py,sha256=9YO23YEbQr49gq_DrfJ7V0G41G7WlJC6rJAeqqOP7dw,1738 +sklearn/externals/array_api_compat/torch/linalg.py,sha256=acbcg80CjamMQ0JDAkrWL7FkyEW5MfmGVzQsrRT00jM,4799 +sklearn/externals/array_api_extra/LICENSE,sha256=WElDmP4Uf9znamiy3s1MCM46HqI3ttZ4UAHBX4IsbtY,1097 +sklearn/externals/array_api_extra/README.md,sha256=hujBWt3i3o5AkT4rUqbVle7qQ3LhbaSwl1VYPT33rig,66 +sklearn/externals/array_api_extra/__init__.py,sha256=Xsj-UtwQSb-PYz6mcJ76Bj0NPKmzOXcTzBeMBZY7EV8,660 +sklearn/externals/array_api_extra/_delegation.py,sha256=1biTXOZj5KyDCG2JEOgCGasKHu6n1UMF_9iuB8YP-wI,6345 +sklearn/externals/array_api_extra/_lib/__init__.py,sha256=GCx2h0v6DbmpkC0XDJkRzbZWcUqwwHEuVDoAeX7FrAI,91 +sklearn/externals/array_api_extra/_lib/_at.py,sha256=-ZPik4faGK6D_WvsCg9V926C0oF_x6Cmw8i3yfjvTyM,14970 +sklearn/externals/array_api_extra/_lib/_backends.py,sha256=MJ4r-NYRF9gSZkLJviYq7DeioyfgIWM9ErIkCavNANU,1754 +sklearn/externals/array_api_extra/_lib/_funcs.py,sha256=5kXrbceGaV7v2PlZiweQo7CXesB1TqXJzYegZib2yrk,28982 +sklearn/externals/array_api_extra/_lib/_lazy.py,sha256=abt1ee49uFMx3Nws8-BKee2j1qLwVUGx2trJfTHusGY,13682 +sklearn/externals/array_api_extra/_lib/_testing.py,sha256=TH7--PHPinrQ6gRZWMiCSyZGgTaZyXZ6AOSbvtPsAYQ,7658 +sklearn/externals/array_api_extra/_lib/_utils/__init__.py,sha256=8ICffM2MprXpWZd8ia0-5ZTnKtDfeZD0gExLveDrXZs,49 +sklearn/externals/array_api_extra/_lib/_utils/_compat.py,sha256=l_4tKMzUsfG3f_ZjczhYpuwhq8G76GtzxkgJ1fEhrzk,1724 +sklearn/externals/array_api_extra/_lib/_utils/_compat.pyi,sha256=M0UcaeFCqLPgGsF8N5mHw7s3bsrlbDfJY2uhLozJ97I,1675 +sklearn/externals/array_api_extra/_lib/_utils/_helpers.py,sha256=7rZIEG-g6xqVKs7erLlt-nLDTOomUmAh45rXP42fVW4,8234 +sklearn/externals/array_api_extra/_lib/_utils/_typing.py,sha256=FCc9Ocs2akiX3Wzwv7gWb737Azt_RRmsbEVT9dc9WU8,213 +sklearn/externals/array_api_extra/_lib/_utils/_typing.pyi,sha256=-XcCOYxOoKjgPeo3w9Pqg1KyYm6JTKm6aO_jD22CGoU,4725 +sklearn/externals/array_api_extra/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/externals/array_api_extra/testing.py,sha256=mw_0y_TzMmTP2wUV_ofAwcih0R_ymhUkQBrHBj9k_gM,11940 +sklearn/externals/conftest.py,sha256=8wfDBd_pWHl3PsD3IOGeZT4z0U-q2895fYvApMzq5gg,312 +sklearn/feature_extraction/__init__.py,sha256=I44s-WIjNSCKkaMvQ6k60KFhHD3Je34kYW5ebV4TYTk,396 +sklearn/feature_extraction/_dict_vectorizer.py,sha256=jjMZ8gPjjPihpP-sOjM1sFI_xt5WeqDem54-ZLQUZxw,16030 +sklearn/feature_extraction/_hash.py,sha256=tiaLWdUw45EF4zJ1QLdeaEvCPZcr-4HcN8y2npGYwlw,7829 +sklearn/feature_extraction/_hashing_fast.cpython-312-x86_64-linux-gnu.so,sha256=JY8l_SiYj_x44-nm2pZpcl5gKu5h7dWgKvoFOWdy8eQ,92488 +sklearn/feature_extraction/_hashing_fast.pyx,sha256=V-PISJDpipnfNlxj6NxYhdq4LsaYwpudjdzSim1OKiw,3027 +sklearn/feature_extraction/_stop_words.py,sha256=ZEfwEZHSNFr0id2pPdBlZq-E9j6VFQM8S86gubzOweo,5725 +sklearn/feature_extraction/image.py,sha256=pAx59y5gZ0WDgExj7SnHstWEHTOvmZD_MyqoHfdX-BY,23563 +sklearn/feature_extraction/meson.build,sha256=5D4WuiUPvXvqJcQj-yqpkmQ2yxJ9yVr6T-_Q5Gk0Tw8,192 +sklearn/feature_extraction/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/feature_extraction/tests/test_dict_vectorizer.py,sha256=sJfcwyId7Xjrs3aakS-HjkPvt0dzuVLqIuCQmpxnN5U,8256 +sklearn/feature_extraction/tests/test_feature_hasher.py,sha256=ar6ZCR5VGSl1sMUqERAHPSYfgvGyNyAQzvz4CFzT3_Q,5538 +sklearn/feature_extraction/tests/test_image.py,sha256=lALrGDEr4LzX0HCMKNtcrhzHrtqrL-j_QDsk8_zLfcU,12303 +sklearn/feature_extraction/tests/test_text.py,sha256=Kpm-pRro8m3bURK3pR-c1HAwgPmRunj6DrRDLUJ548U,52764 +sklearn/feature_extraction/text.py,sha256=h28NM0c193F0gi-r_yh_q1YCzP-Jc6VDDrxwVIkW2mI,77416 +sklearn/feature_selection/__init__.py,sha256=_G69r4hI6pPZ_6Da8uPNMW1MBzdulQfgcrJuinCJ6Mc,1128 +sklearn/feature_selection/_base.py,sha256=lwJe6Qub7zyF6LHHrT3jZ4Y2asC2Eb6oPWC6hh2gwGQ,9426 +sklearn/feature_selection/_from_model.py,sha256=heQ8iO367qKPUYrHf2MgiSwyIXbtoDu_i5yht2hF80M,18651 +sklearn/feature_selection/_mutual_info.py,sha256=e8tJ69GdZu4VgpRW85nlxAXrY3Y4I8xg68xe79y2uDQ,19968 +sklearn/feature_selection/_rfe.py,sha256=Y8w0_KQLZtFAKvu2Q8tHNAGwFCGAN3abiXyjd39HR9o,37652 +sklearn/feature_selection/_sequential.py,sha256=c6dilPIHBKOCAYTGYWSWSg4VlCEsxXrg2MYMO7DfRaQ,13904 +sklearn/feature_selection/_univariate_selection.py,sha256=dnj6zNvGKDlnH7R-iOgco6a0U4OmCBg0zL8TtJSI5M4,40735 +sklearn/feature_selection/_variance_threshold.py,sha256=uxrTWbLzgx_b74XKUGmNwrSWMOjP0LDq7kXUD_mLQxY,4639 +sklearn/feature_selection/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/feature_selection/tests/test_base.py,sha256=ythpS8iRYjFm3VP581ikkgPobz12JrlTAYBTATIKKBU,4832 +sklearn/feature_selection/tests/test_chi2.py,sha256=c6L3cs9DYulMNUTjnZJo7VURucjhUHLYzG2EaRE9N1c,3139 +sklearn/feature_selection/tests/test_feature_select.py,sha256=59hWeQqIEOZJGcE5IL5y3jMnlBwFbpuwH855OKUgpsA,32507 +sklearn/feature_selection/tests/test_from_model.py,sha256=qAQAdvrS7SwnXpNY53qexquuMoWFAZyO_AZQVNdSKUk,23841 +sklearn/feature_selection/tests/test_mutual_info.py,sha256=IyCSjjXPkQez915cjtshElj_9xQVHY84a5aiCJMFP4s,9853 +sklearn/feature_selection/tests/test_rfe.py,sha256=xCDzFtO6UnnoApmEmPMHR61iii_IImfA1AZcwKR2xIo,25270 +sklearn/feature_selection/tests/test_sequential.py,sha256=9Z-naJRDVboKShzMI4xcWekQjwktpUwKT2hmaalAS3Y,10906 +sklearn/feature_selection/tests/test_variance_threshold.py,sha256=tKaSBkRgVBzo3xC0lT6nLNNzKW4M-5t_sAFJgUmr--g,2640 +sklearn/frozen/__init__.py,sha256=7zBEBZHkRwHUBRG1VAn6kPYJeFjFkktqSpLATohnI7o,148 +sklearn/frozen/_frozen.py,sha256=hKGn7cTTiE6Db0cBoehUCyRNbRcOObRPeBM7V0X_XC4,4985 +sklearn/frozen/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/frozen/tests/test_frozen.py,sha256=u7WjplRRlNCjNx77UMkBbVpXhuKFdM6TgVSmvwEzI_4,7069 +sklearn/gaussian_process/__init__.py,sha256=pK0Xi-lrrByscZP7Brgk92RC5Qy4AIDrOtFb71bpQ58,330 +sklearn/gaussian_process/_gpc.py,sha256=iL9epkfnD-q4-n0cpLf5ClLiV_2pWDNDkFqxGRfoxMw,39297 +sklearn/gaussian_process/_gpr.py,sha256=zpKQWpQkjfBtXtwxz3R_SWSGEixKsjKoMkhOCVsAM_U,28314 +sklearn/gaussian_process/kernels.py,sha256=l0uLSlAi-Mrax0cAPSDPZ3N7osIViS2rSQIFr7rrV3o,85106 +sklearn/gaussian_process/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/gaussian_process/tests/_mini_sequence_kernel.py,sha256=YpD-vtJFSVdzVmJxHDmEdFGl6cOQ4J98mLpjFCFThys,1571 +sklearn/gaussian_process/tests/test_gpc.py,sha256=XZIDGXYMKKgjB3Tn53Dnyit4oPrpDOE5GSlC61a-L0Y,11251 +sklearn/gaussian_process/tests/test_gpr.py,sha256=yTJz72nlINDcPygRoAjQTSZ8Mv79DoAhUq7CiqM4lXk,29682 +sklearn/gaussian_process/tests/test_kernels.py,sha256=izel3Fru6VdgNRGHxnwVqmVENxy06sYjDTF03iRI9mQ,14492 +sklearn/impute/__init__.py,sha256=ps33PrOn-LYpyam1EVU7mur1eIlt8huormM1mbDV1UI,1031 +sklearn/impute/_base.py,sha256=81ScvkI1pcPgtfVpU4TcSB7pEPYJjO18MNUIBdBQREc,43434 +sklearn/impute/_iterative.py,sha256=cgIyfyJjaV5HySQ5TxaX97Aywa8VpSkr5UJAtNd1iWI,40184 +sklearn/impute/_knn.py,sha256=1kvnVdpDEHsm-pZBTYhbNWIAtcMzWoDPJkLpIdeJFGo,14905 +sklearn/impute/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/impute/tests/test_base.py,sha256=L-RND6V8s4g40Uy65BIdQG1oEtHgOWBliBH4bUVdVQc,3367 +sklearn/impute/tests/test_common.py,sha256=G7WzU8u9bItkql-tlSTnRdekw0HPeCDfObCYQwcV63w,7616 +sklearn/impute/tests/test_impute.py,sha256=Al2BRqV6UFCIYhc_WfOTeMy5dbie85K0W0vTRctmsBM,66843 +sklearn/impute/tests/test_knn.py,sha256=4FL0dBxzW_FooUpFuzgR6uYDH2Y4l9pLGJ1zkgy9b4Q,17540 +sklearn/inspection/__init__.py,sha256=Sb9g89Bjofq0OCfNUQlC3rfvHyGE__zWiXA2yvPhib8,485 +sklearn/inspection/_partial_dependence.py,sha256=BAR29VAvZyQ6bn6cNoEJmUQSWjF5uie6vjR-5RmxgrM,33439 +sklearn/inspection/_pd_utils.py,sha256=m01ubgd8W-ThbL95ATj7dRWK6nACermQBc0MrEPPQr8,2218 +sklearn/inspection/_permutation_importance.py,sha256=XqjjABRNDScQUAoJos5hhruGuRE1EdLLZTyc15BPblo,11395 +sklearn/inspection/_plot/__init__.py,sha256=vKm4xxqJIfK6Tk6By-YU03YcE6pR1X1juFrOsacfZjY,79 +sklearn/inspection/_plot/decision_boundary.py,sha256=fJgQNeItWQB70nngLb-2_AJeOkL-Gq9P7eXng1_kPBI,22072 +sklearn/inspection/_plot/partial_dependence.py,sha256=kg2frhpo2AMCmhZWirf6aCSIbaD5svA0lqT6ee79e6Y,61426 +sklearn/inspection/_plot/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/inspection/_plot/tests/test_boundary_decision_display.py,sha256=kuwvW1ND7hZwEHz3dooYVl47rjVLy_gjuGqfGWMg_bs,24640 +sklearn/inspection/_plot/tests/test_plot_partial_dependence.py,sha256=KHHwbby3GhtkD_4x6fLlR0ZVU2nokGX2eDrDMW-JA9w,41417 +sklearn/inspection/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/inspection/tests/test_partial_dependence.py,sha256=7Bu-KuVvLuCZtQjBi6ZP-2sDmrN46isUtByOzd-pHLw,40976 +sklearn/inspection/tests/test_pd_utils.py,sha256=t-8K4YbQAbVK4pcI1P9hr8-0iEgc72x_1-868HAhLBg,1640 +sklearn/inspection/tests/test_permutation_importance.py,sha256=wDt75_tkjpDMffkcYn7jz6WeKZrkXgsBhtAO6nAa7WY,19840 +sklearn/isotonic.py,sha256=dIKBLNb4TNGdTKRJbP4iR0PM_MfLy4BlnZ4dF40zkAI,17371 +sklearn/kernel_approximation.py,sha256=AgM5jql4nyxrTD5zlw0DC1pROLRtlL2ttUOSI0B0hqw,39676 +sklearn/kernel_ridge.py,sha256=b9dyensnC3vnJhkIJSzoAtVO5-QDYP4cl7GGD_01IUE,9211 +sklearn/linear_model/__init__.py,sha256=m1s3A4BrvReDX5PliDalY53YhPfvh1s-x4efa1flIaE,2411 +sklearn/linear_model/_base.py,sha256=HS_9ugy0UJ0RmD-oe6QYfURpycGun9kZNGEoGyCzUTM,28901 +sklearn/linear_model/_bayes.py,sha256=5maNoqyc8gZCDHs7h-PVipebFfQW4_e7lxkW6Ef5r-c,29016 +sklearn/linear_model/_cd_fast.cpython-312-x86_64-linux-gnu.so,sha256=k9pqA11D3UYuuObBNZi72mbA1pWMeDcENyNURlismo0,374848 +sklearn/linear_model/_cd_fast.pyx,sha256=ssLdsiFVGST1OFCXtygffSYCs1W-KQXClCY3662q99E,32804 +sklearn/linear_model/_coordinate_descent.py,sha256=3IOmDXrTDKvNPa571XyoYMrgeEbK-Zh_ijCFn-WtGg4,118398 +sklearn/linear_model/_glm/__init__.py,sha256=BmGWcP-GtYkT0WWUcbku9vHCWfCV6V8pniulKsbyrvU,318 +sklearn/linear_model/_glm/_newton_solver.py,sha256=jqsBVeKbi6svdM91DLe9ydsF-mh5yJLklW51xpa0Qc4,25348 +sklearn/linear_model/_glm/glm.py,sha256=6riFlb2eKwjvQUY_o_r4b8a6V5LCHMMqpNIwMAA7y3Y,32206 +sklearn/linear_model/_glm/tests/__init__.py,sha256=vKm4xxqJIfK6Tk6By-YU03YcE6pR1X1juFrOsacfZjY,79 +sklearn/linear_model/_glm/tests/test_glm.py,sha256=OSNL5u1UYyCUnn02iorwIUzkgyAwV55euP3SB67usrw,42223 +sklearn/linear_model/_huber.py,sha256=7Cj6NId-S-iRPZtO6VAiyJDqczWKkAekEo7wqwLgStM,12690 +sklearn/linear_model/_least_angle.py,sha256=4546YB9iYpnryuxiVpb1piQXyTxQmba6Ki7qS90vw-s,82966 +sklearn/linear_model/_linear_loss.py,sha256=qQSX-bxYyAKk8bQ2z33b_pHBMLp1wXVmj5Zz5aCgEzQ,34113 +sklearn/linear_model/_logistic.py,sha256=mB9OEcmm_CsCzZMi8pJoSRQ-SovxgZ46QVmp_OYVoAw,90722 +sklearn/linear_model/_omp.py,sha256=--eDGLDRu_s8p_veALnGprMyzsIGV3AytfSuGXcfHPQ,38267 +sklearn/linear_model/_passive_aggressive.py,sha256=zF7znXaTn5M5cMRpHr6rNYllZoaD4Ohk6IXOE-skNBE,19264 +sklearn/linear_model/_perceptron.py,sha256=dZkROr_kx5MLVdiP9nTaHiIdQX9_q330-7SXrgV3pjk,7564 +sklearn/linear_model/_quantile.py,sha256=lIfK-QCEa0zNqZKed6ayrfU6QdKC9UKePFZPq4MD5aA,10471 +sklearn/linear_model/_ransac.py,sha256=gJgPVGQFGpREoSQSBAz-f9Ar8j-WgRLQ38bf9HitbkI,25733 +sklearn/linear_model/_ridge.py,sha256=oKjF4mYRwbyRqXroXWrdWbmKL6XJL_fCVGfqiL1uTOM,103515 +sklearn/linear_model/_sag.py,sha256=56X90dePIvQEQG6TDDr6PWMnoL6yYUQ10NQF49JiGhU,12286 +sklearn/linear_model/_sag_fast.cpython-312-x86_64-linux-gnu.so,sha256=2dNGzMwfbroQ9U3OA2oBJnDcaV3ETBjYp6fYCQlVp3g,163024 +sklearn/linear_model/_sag_fast.pyx.tp,sha256=FFxDn4DS3e8zt5VfK9ZRIDIn0xusZJwbGWsd7QuX5Ks,24277 +sklearn/linear_model/_sgd_fast.cpython-312-x86_64-linux-gnu.so,sha256=u0-ps84TUKWucKsH-VuSi4_FrF93yOcGQpggSnJeoV0,242144 +sklearn/linear_model/_sgd_fast.pyx.tp,sha256=5ewo_7gSyywxQBqtEBL6V_eBmtJORSadRPURK2ZEFB0,20671 +sklearn/linear_model/_stochastic_gradient.py,sha256=3LoT_LCt_2ftIk78NJiw08zeoZFabk81kEQmt1Qd_TY,90146 +sklearn/linear_model/_theil_sen.py,sha256=krSq8Hr9ilc4EHCdiH5fm3pn_N3PgndHu-LCZUHIlCE,16405 +sklearn/linear_model/meson.build,sha256=UTRL_xsxXtXpbw4pokGzMgLjeuEj0HGgzGoQebqUZ3M,929 +sklearn/linear_model/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/linear_model/tests/test_base.py,sha256=XMSu3aWOFjB6o_0jG6plMG6TWjpuag1TOrQZCODz2Vo,27048 +sklearn/linear_model/tests/test_bayes.py,sha256=YrINPjqB0laIJxpcrVPI3uG7qUWJkE-Mntzp9P6Xf0I,11078 +sklearn/linear_model/tests/test_common.py,sha256=fUPlV7x4PGbNE6YloDEo2VsX2r4dPzc_B84w7MwefC8,7303 +sklearn/linear_model/tests/test_coordinate_descent.py,sha256=CmCoLW0BEYWwA7TqE1l3g-1llmKMldM3p_1korNXodc,63282 +sklearn/linear_model/tests/test_huber.py,sha256=au_AulAuqWt1XACGbWr5_1tw12M_g7Vi2U3LxvyflgM,7615 +sklearn/linear_model/tests/test_least_angle.py,sha256=Du1rm-UVjzDTjztRw_S-_OacyOscAg5yqAmSKwsrMbo,29609 +sklearn/linear_model/tests/test_linear_loss.py,sha256=2zMWRVfYQM_sVuvzTOYeG9Kl4N9XhyxSSbACCU_BUx4,17912 +sklearn/linear_model/tests/test_logistic.py,sha256=SNZyQ-lW4QXQsYzu8omicC7tAVN-rigTpYnnf9-dUmM,86751 +sklearn/linear_model/tests/test_omp.py,sha256=ZG03dTxyJGmeajIo4fA8SN4Kxwz_rzcOTeEVkS6g3HY,9344 +sklearn/linear_model/tests/test_passive_aggressive.py,sha256=oylJ8F5LNg0br4zX2LXZRU8FMUECnhsaVL0r8mxmd1Q,8994 +sklearn/linear_model/tests/test_perceptron.py,sha256=rsNfXmS37bAZeZ04kRNhc2PXr4WjjTWDaxW_gNmMCkI,2608 +sklearn/linear_model/tests/test_quantile.py,sha256=JiOfB1V2NwuWeK-ed6hKmOpHPHj7CNEHp_ZZuGt4CZk,10689 +sklearn/linear_model/tests/test_ransac.py,sha256=bDDkKflBMv5vTMjAZPfjC0qvlA_VNNDhS9bYC6T3g2M,16790 +sklearn/linear_model/tests/test_ridge.py,sha256=Hpk-qE-_QoDKtaYezNqAhgBUxkQjRn4gAkrQU3fzI8U,81605 +sklearn/linear_model/tests/test_sag.py,sha256=ksURaaSDjzvHB203ZH6bRxd1s9fUkPhcAb62XAXjk7o,25807 +sklearn/linear_model/tests/test_sgd.py,sha256=4Lc9pjw9E9x5BA8KmkmKkqALcZzR2GFl0NCMQWQUZbo,69765 +sklearn/linear_model/tests/test_sparse_coordinate_descent.py,sha256=2_IRPgEBCa6eWM_vtHfVHqX8-LDN7pj027WSpFHjWys,12654 +sklearn/linear_model/tests/test_theil_sen.py,sha256=UIfe_oW99MnoSeNZeqf2nfzuMd2zzDq5a6rjxpHRkl4,10135 +sklearn/manifold/__init__.py,sha256=gl4f7rOHDtrpOYrQb3eQeUrRRT2Y5TZpwrgDfG-d0uE,565 +sklearn/manifold/_barnes_hut_tsne.cpython-312-x86_64-linux-gnu.so,sha256=jqMNxKwwPZUyiO45CG1f6BpzmjZQwIbM_PBgYfB2qkI,134169 +sklearn/manifold/_barnes_hut_tsne.pyx,sha256=W2mN6eXTRn8kuBLdAV5S2LVyPxG6WemhA0z0CuLBuU8,11264 +sklearn/manifold/_isomap.py,sha256=h-X6biNZS5G33-1thUSn-qnDsRxg09kqMu5hD49WhV4,15686 +sklearn/manifold/_locally_linear.py,sha256=otP2a25Y6Tgf95IuhQB-rfJnZUzrMPHCYuOFTshu5XE,30541 +sklearn/manifold/_mds.py,sha256=_UaYQVKWUAFs_NuGS3vYL_58AE7gqFvX0oosb1ejSFc,26025 +sklearn/manifold/_spectral_embedding.py,sha256=5arXNyMZyZhEXQddoJgH6uahUiHxIeuw9-t0HZhekWY,29916 +sklearn/manifold/_t_sne.py,sha256=Vce22nRw2xWDeBiELqjA-vM17plPUqbRFl4kKz2pYEE,44265 +sklearn/manifold/_utils.cpython-312-x86_64-linux-gnu.so,sha256=vX3e89_u4FdT2nz2P4lBGV9qpu4TkEXpnNt_ZqIrU3s,93280 +sklearn/manifold/_utils.pyx,sha256=o8U-cGOuCt2W0uJ6GTvTgALOmtPoUMyM4ZXsg0hmou0,3908 +sklearn/manifold/meson.build,sha256=sCySiLhLC1RumNBhRsAFZFM7GyU88lQxCrBVcZhWgtU,314 +sklearn/manifold/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/manifold/tests/test_isomap.py,sha256=Wl4voE-7ZRpK6YS6JKyEpAhccA0ReBlGyLNU-p4hQWc,12074 +sklearn/manifold/tests/test_locally_linear.py,sha256=yxsUuJ7vzm2VxiLi1fuZjzICS_0mXrwIicJHLC79eDM,5772 +sklearn/manifold/tests/test_mds.py,sha256=Q77vGfH1pB_LAiYYoKSAt7jaBGabAUtHyCwvMHmEz0k,7197 +sklearn/manifold/tests/test_spectral_embedding.py,sha256=YvmsFIvLYGZqn2FSXhSxU80P_vmYv4kVv7sR4jDouIQ,17775 +sklearn/manifold/tests/test_t_sne.py,sha256=xZPO7J--r2m3_bqMP4Odnz3_A16mmxu5A2aX_Bx4in4,39057 +sklearn/meson.build,sha256=ihYL4bRJExpefJ5vOIOJPU2Ufm1-jfhVN5Sht_E9nog,9808 +sklearn/metrics/__init__.py,sha256=mQOOWIFYPSJ60bPWNUH8RxXqL0K71gz9NYwj41YW6YM,4633 +sklearn/metrics/_base.py,sha256=ppcQ_yli1Z3SgwSvy7boSIDW4el9bmtp0ZxXr3XlQsw,6987 +sklearn/metrics/_classification.py,sha256=b-iLrbaQ3fNw-MVgqShvYFRKxQhiV2sz-kdXarWNnhQ,139502 +sklearn/metrics/_dist_metrics.cpython-312-x86_64-linux-gnu.so,sha256=uZnWh7E4Ifn9PkfIksQvRAayjo486lqkBLt6fLvWtWI,601504 +sklearn/metrics/_dist_metrics.pxd,sha256=U4vH-mgokzVA5-li0CRbFICY4gyJ8gOjtpQs6bQg7G8,7330 +sklearn/metrics/_dist_metrics.pxd.tp,sha256=YI-GhztvViANTOCY4cjexOnxGJNVdVN1tH2l7yyCV00,4378 +sklearn/metrics/_dist_metrics.pyx.tp,sha256=L9frrbHm0t3l6KXz_T_W9aEg7g0wagXDyHzJmzoMqJA,92197 +sklearn/metrics/_pairwise_distances_reduction/__init__.py,sha256=tUkZS268OxDpX4rYbbw8a0zG8W03xtpxo0lqIvIdZmI,5132 +sklearn/metrics/_pairwise_distances_reduction/_argkmin.cpython-312-x86_64-linux-gnu.so,sha256=87rgzTAa6X9VqcGHMzpyJq7PmRCm5oxIpwushlrAa9g,257657 +sklearn/metrics/_pairwise_distances_reduction/_argkmin.pxd.tp,sha256=eLGvaqpxdaoT1CgTTtzn9_PlCJ7fLMmZ_vqcDsTeBI0,979 +sklearn/metrics/_pairwise_distances_reduction/_argkmin.pyx.tp,sha256=2qtw0fq-UuAkxkz1nKLUOE-wSXosKNHrK8biI6ICxQs,19783 +sklearn/metrics/_pairwise_distances_reduction/_argkmin_classmode.cpython-312-x86_64-linux-gnu.so,sha256=SoR1g_BU0wH-FHIA2U3_xua9kbidX8Z2FEm0nl9sP1c,171217 +sklearn/metrics/_pairwise_distances_reduction/_argkmin_classmode.pyx.tp,sha256=KHXJwDRXq58rYQYnjlgE8k8NUXl0Qz9vrpwKTee8z_M,6432 +sklearn/metrics/_pairwise_distances_reduction/_base.cpython-312-x86_64-linux-gnu.so,sha256=vZyBr8OxuBgYaazSgpy_HwUCNyvHhTnOEdX05M-5R_E,225065 +sklearn/metrics/_pairwise_distances_reduction/_base.pxd.tp,sha256=vIOGH_zE7b8JUZ3DOC0ieX18ea7clFZzd1B2AnrYeek,3563 +sklearn/metrics/_pairwise_distances_reduction/_base.pyx.tp,sha256=h4sPRzksjOO35w6ByoulBx8wtb3zV44flEWYXXyaEAY,18353 +sklearn/metrics/_pairwise_distances_reduction/_classmode.pxd,sha256=DndeCKL21LyIGbp42nlWI9CKoyErDByZyQawUagL1XE,151 +sklearn/metrics/_pairwise_distances_reduction/_datasets_pair.cpython-312-x86_64-linux-gnu.so,sha256=6Ep3TgvPM0vwwchsF5WkAAgV7E75cFtJH3tZPGbPr9I,360464 +sklearn/metrics/_pairwise_distances_reduction/_datasets_pair.pxd.tp,sha256=7BR2LUjE2MELP3fV9OZH9tXakpsw8QQumBFi_CjMU0U,1948 +sklearn/metrics/_pairwise_distances_reduction/_datasets_pair.pyx.tp,sha256=ipbswS5TSNvw9lO_6tN-7E8ruDS5HbMDumfoxr5h0H0,15087 +sklearn/metrics/_pairwise_distances_reduction/_dispatcher.py,sha256=UsK6BZyrKlmIMxJy82Y65HfbEuegPLcODsZ8J5io3eo,29806 +sklearn/metrics/_pairwise_distances_reduction/_middle_term_computer.cpython-312-x86_64-linux-gnu.so,sha256=gML2vnXOQz_L0YyK3mLy3fVKHmJNd_lczqXIMWmGptk,362472 +sklearn/metrics/_pairwise_distances_reduction/_middle_term_computer.pxd.tp,sha256=bsr7Pmqj-09ciVAh5bMfyc6A8KgcQ_3WlPC0dBoWwfI,5925 +sklearn/metrics/_pairwise_distances_reduction/_middle_term_computer.pyx.tp,sha256=2RUfNdjCB6aJU4b42nNYZb2ILAYY74A9SGfu3zzn8Gc,20344 +sklearn/metrics/_pairwise_distances_reduction/_radius_neighbors.cpython-312-x86_64-linux-gnu.so,sha256=cqVR4O5T62XyJ5XCe0gOOvIGAmc2dZuRA7uICc04IEM,284073 +sklearn/metrics/_pairwise_distances_reduction/_radius_neighbors.pxd.tp,sha256=gaUTpGpL4dPmjcwjnIrjlOs7RX4pUe9-T-6QDspl5No,3254 +sklearn/metrics/_pairwise_distances_reduction/_radius_neighbors.pyx.tp,sha256=105e6MGHtvVGqQs3JkpD7BnYFcn8G1TPeQ4VIPGiF_4,19423 +sklearn/metrics/_pairwise_distances_reduction/_radius_neighbors_classmode.cpython-312-x86_64-linux-gnu.so,sha256=_cOTSZ2FkDnrZLWlUmAB-ht1jRqIzKgwL9nkcXWmPN0,200025 +sklearn/metrics/_pairwise_distances_reduction/_radius_neighbors_classmode.pyx.tp,sha256=J-hJcHrpN0bsPMGIMfFu_RYDVFaayvl4M8GMteOHRzA,7353 +sklearn/metrics/_pairwise_distances_reduction/meson.build,sha256=tlbyYpIVeIOkS1_9LoBk5br8jscbwBss7cFIO86uMyw,7540 +sklearn/metrics/_pairwise_fast.cpython-312-x86_64-linux-gnu.so,sha256=ZXFRIuvNoQ_fguezgkWuiBaznYnUXmAfBsbTr9HMBl0,183529 +sklearn/metrics/_pairwise_fast.pyx,sha256=LmzoEGFiL-shm5pOwGHBR8Pue8Qz_cY0rNStYVSWxVQ,3460 +sklearn/metrics/_plot/__init__.py,sha256=vKm4xxqJIfK6Tk6By-YU03YcE6pR1X1juFrOsacfZjY,79 +sklearn/metrics/_plot/confusion_matrix.py,sha256=YfXTo3iYAY0fokkz77iUbRgp75CfrbO8cWU9og6z4Ws,17330 +sklearn/metrics/_plot/det_curve.py,sha256=s7-5iGGsKSUlGxiGwK2UNKJ9C1L9MESU_XKNHHHaahE,12595 +sklearn/metrics/_plot/precision_recall_curve.py,sha256=xnFVrHXNK46OFsIqOMJBmHC4BDd2YRMLSNGk-uxjMQ8,19414 +sklearn/metrics/_plot/regression.py,sha256=_6smop2JeU3aS1LbKCZCNbjIVpQbFF4WR8mlyFnVOLw,14691 +sklearn/metrics/_plot/roc_curve.py,sha256=b3_-RNwVCVbXG7MxRf_8piAQrXwesJvRLl3IejDoR4A,28572 +sklearn/metrics/_plot/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/metrics/_plot/tests/test_common_curve_display.py,sha256=oq5eSPrDGbksf-x7TeNfXtvqZPJQwWG_nY6ixkumx2A,9825 +sklearn/metrics/_plot/tests/test_confusion_matrix_display.py,sha256=E1xT26QRcOHH8VvuLShq8ev5YQ_8bLsJmfkHW7dTiiE,13487 +sklearn/metrics/_plot/tests/test_det_curve_display.py,sha256=j8LJA2Ms-IoZjip1zvymtwORF6uTe4MBx6ldGAaqj_U,3633 +sklearn/metrics/_plot/tests/test_precision_recall_display.py,sha256=d5-4E3HOQXvMshtrKOUt7NIwwqe7-eknw8wouX_Y6TE,13884 +sklearn/metrics/_plot/tests/test_predict_error_display.py,sha256=3PnOYrgBf7bnw1zHCPWm28tVCeuZlR4hIQD2fR-9RfM,6007 +sklearn/metrics/_plot/tests/test_roc_curve_display.py,sha256=YiS8sF4nL9cZZE5iFxBimqO_prRgOgg__WgAN-bVAug,34828 +sklearn/metrics/_ranking.py,sha256=ofwC8LBCo5RcN2ksk3enRoAN0qkM3CJ99SaPIYvqP3Q,78995 +sklearn/metrics/_regression.py,sha256=c5RpBgYy5F6Ck-RdsRxCYIinZpZDKjJyAdJ9RsqeO8s,65002 +sklearn/metrics/_scorer.py,sha256=tvOiC__wKam3Th2I0E0NTtEUcl4Sq04l3QtWim4kEpI,41073 +sklearn/metrics/cluster/__init__.py,sha256=xQxpw9CyuWGDuqYT0Mrl82HTdseiQCGJD8wpEf_e1wQ,1415 +sklearn/metrics/cluster/_bicluster.py,sha256=gb0X2lNFVTCqmdi1YutW3c_W4ZqjiBKmgYWs57lxEUc,3637 +sklearn/metrics/cluster/_expected_mutual_info_fast.cpython-312-x86_64-linux-gnu.so,sha256=A8yzs6L5grEIyf3UF7etLB_txCPz7EVTdykugf_RhVo,110432 +sklearn/metrics/cluster/_expected_mutual_info_fast.pyx,sha256=UWIcBVPgxQ6dD99fmNtP_QdmK4jd-im2zIB4R0gqPMc,2687 +sklearn/metrics/cluster/_supervised.py,sha256=6sdhfvWBGmRz6KINd5Dnr3xK2QDPRDmNcc2PMLCmo34,45333 +sklearn/metrics/cluster/_unsupervised.py,sha256=JT-IKuqoswGQ3_w16yLb0qm773K_WQUDjkl4m8c35FE,17019 +sklearn/metrics/cluster/meson.build,sha256=j4LJu4kH3dRH5UZN-2B62MM4m3mXwq44hEIeBRLgd2w,164 +sklearn/metrics/cluster/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/metrics/cluster/tests/test_bicluster.py,sha256=KecSxviHfRfUMNVZ0g77Ykx96QAuKoax0YUY8paQjFg,1719 +sklearn/metrics/cluster/tests/test_common.py,sha256=sUBhJJbGfo2zPA-JJ73xXDXc7c_VI1870kdBxwkIoEk,8201 +sklearn/metrics/cluster/tests/test_supervised.py,sha256=GWrG7Suowna_Emut6bsGzf3z3ie5SDvh7Szs2OkrXJs,19370 +sklearn/metrics/cluster/tests/test_unsupervised.py,sha256=eAic9M_89S8Xbk1hEX0xyIeBW2GrAwPOTpNuNob3TaU,12269 +sklearn/metrics/meson.build,sha256=OH0IO4OSp5gSFTXVRZkj8zvyidbgtaoLJ2kZmON2PxE,1510 +sklearn/metrics/pairwise.py,sha256=zZsB-LFPhVvNWCG2w7w00Mnmv1Vgx7Z9-oT3hhKAbnI,91691 +sklearn/metrics/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/metrics/tests/test_classification.py,sha256=R4nU1hSRhS82WIhhEjVkJyZd5zuVEWRHOxMZxB5WH5E,120654 +sklearn/metrics/tests/test_common.py,sha256=2Coo4Hhq4TF3Oy4CFg_npBsubJDdFDo82x_ptuWFcTg,77273 +sklearn/metrics/tests/test_dist_metrics.py,sha256=vMpc3Q1sgD6nuWNXkKVQD46eGRkzn1S_w1w72ACLP2I,14995 +sklearn/metrics/tests/test_pairwise.py,sha256=f5VoV6kv-F-pEzPpmSdaM_jfuSts4X4RZDLQFrgPoME,58631 +sklearn/metrics/tests/test_pairwise_distances_reduction.py,sha256=t-ZNZ7RyXOkybyFvCxm7VU2UWgOjY2_roOE8UO89aig,53061 +sklearn/metrics/tests/test_ranking.py,sha256=PJH9La5JdFhTQJCTrgE7YrV_rkEv5zY9dUkZj30VnA0,83993 +sklearn/metrics/tests/test_regression.py,sha256=W8spjLxk7WZO42-gVGwnkapTCbvPVJoHQ1HFzOrIJgA,25924 +sklearn/metrics/tests/test_score_objects.py,sha256=JhAQazrHgDR2hx1mFUO6G2XR_A-tyAmkCktKduuPHY4,59004 +sklearn/mixture/__init__.py,sha256=o0w1PyZ4gXtcQcpvrGrYEn226ugS_Ne3kQ3ZJPGt6-8,276 +sklearn/mixture/_base.py,sha256=JloA758DB-woL_K_MymnLwN7wN6GS7gap5YFN8olobY,19241 +sklearn/mixture/_bayesian_mixture.py,sha256=PmRf4Dyqb0k995eZMi57_cvaIl_mqZ6Cr4zVX4oziCY,33573 +sklearn/mixture/_gaussian_mixture.py,sha256=xJ5DG7DKrD_cx61FepCZaJesbWuumhAXAMjfBrTFxq0,32736 +sklearn/mixture/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/mixture/tests/test_bayesian_mixture.py,sha256=23_HO3xRwo4hMpF60yjCVixeCsx4T4bZcd_OebiqmPA,17040 +sklearn/mixture/tests/test_gaussian_mixture.py,sha256=TJPWI0kChKEMvdF8RYDAa14Co_VhI_wAIIm0x4DJflY,50021 +sklearn/mixture/tests/test_mixture.py,sha256=ar7zjdUa-fsJQlroNmS8-Mj0brARolFyLEZrLdOIrWM,993 +sklearn/model_selection/__init__.py,sha256=EyWSMWF2i6eVm4VtUqG3e9xtniasEDVLt8Am-wUs4Io,2660 +sklearn/model_selection/_classification_threshold.py,sha256=NaKa_yc3fo3rn49nxSmTm0_ZYGzn0XK9AjImz78P2ws,32637 +sklearn/model_selection/_plot.py,sha256=J2LntPSgwlkDTYDkkh1Wn__ZZavYUup-yfqkg7b_MIw,34579 +sklearn/model_selection/_search.py,sha256=4bkqRGyiGWHShE9Y7outfmvbxzLtsmcDm2Yxk_If3TE,79924 +sklearn/model_selection/_search_successive_halving.py,sha256=AuDSR5cEbOvr1rAMOST1f1DvPNibA-PzZyCDYd0XwR4,45154 +sklearn/model_selection/_split.py,sha256=zrfGqOcIwVv6sCFiGVTy0YjCmCa0VWuMtM-P7tkPTcU,109612 +sklearn/model_selection/_validation.py,sha256=gFPz1-o_mkOmFStvMRBjnr42EjATs2XqBiLMgSGyvN4,95908 +sklearn/model_selection/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/model_selection/tests/common.py,sha256=PrR7WoVcn4MdG4DPrOvuZ1jrOIZPFPok20zannr4dwI,641 +sklearn/model_selection/tests/test_classification_threshold.py,sha256=dZ_fWiDJdiz9DFa3ZGBH1u5-cljeXrOY5DYixxw5rP4,23299 +sklearn/model_selection/tests/test_plot.py,sha256=goA_s29K0admCpVCSnWisPzLVf5-XvbTfwxev-DcDZ8,18456 +sklearn/model_selection/tests/test_search.py,sha256=I9KuCvt7N30CMiEdoue3BvMixiQ6JtPSQkJ7X_Yp-TQ,99221 +sklearn/model_selection/tests/test_split.py,sha256=m_eeANTxv3ZqIjLBy8L5ibfzgUMDNtEbsqdeJ8zlTN0,74292 +sklearn/model_selection/tests/test_successive_halving.py,sha256=liuAL9oXGDJM8a63Jpqjp_UmMTehJNnFlHZvjK08gRI,29010 +sklearn/model_selection/tests/test_validation.py,sha256=zY5nRfnG-Nl4Ljhbev2nPaaMNDLDXjwgFnjLtKrRLxk,92511 +sklearn/multiclass.py,sha256=X_8kN37ayDtZOe9k1AeAhz8Ttfvc9YBbr2isVTOabPk,44339 +sklearn/multioutput.py,sha256=cGafTqvPvgTLrZoDXWltAfJ8p_eTvA1xKgPsPJx2Zxs,45541 +sklearn/naive_bayes.py,sha256=7iZcCCF7K0oFQ72YTLEalitNL0yagVAzLoTxNktdN-w,55949 +sklearn/neighbors/__init__.py,sha256=AGlC69XvpiUJ2KeIs96WoogAF5GdQFv48c1BUnOR4tk,1251 +sklearn/neighbors/_ball_tree.cpython-312-x86_64-linux-gnu.so,sha256=1uuatMWy2oODG3mcRGOqPyRxEbMmbq4XGHqgIpUoGFw,636104 +sklearn/neighbors/_ball_tree.pyx.tp,sha256=xqeo6v1L72VcadqIVGX7db8fNX9wI0X5tP3u3uz30UQ,9321 +sklearn/neighbors/_base.py,sha256=Zobunz82rnq5tXKpCTHmiTRejVq2sYeTYfsuoVmQ2eU,52312 +sklearn/neighbors/_binary_tree.pxi.tp,sha256=-TY-H2YOsJbKLfI4Xg9G6yc8tUujBszvEPeKs8UtEt4,100641 +sklearn/neighbors/_classification.py,sha256=TUGUxAjnEvwWrFvg2bqC9Wj8fWbQkvInIz9waKaDIew,35044 +sklearn/neighbors/_graph.py,sha256=SvWpzfkH-5xcG8TjkVT0cVNYbQf_kDtBPB1GC-iNfDw,24611 +sklearn/neighbors/_kd_tree.cpython-312-x86_64-linux-gnu.so,sha256=3wtWoH5lEkLaG40Bd5gVCr7C2DUhYLM84D5DftmIZGQ,637920 +sklearn/neighbors/_kd_tree.pyx.tp,sha256=ZM4_DcS7eUkXbpxBJ4OLXR6exLbkGUB7Kmd_Ou2H4N0,11117 +sklearn/neighbors/_kde.py,sha256=g3Tsl0vWeKT_rE8UYQI8mc8chg2KzJtRQtr0kirgHW4,12272 +sklearn/neighbors/_lof.py,sha256=oFd01Nt9be1BN09osbZ4xfZy0ehFTN3qnr54QapLTmM,19957 +sklearn/neighbors/_nca.py,sha256=rk8JChFYShvGqSwpLVjl9MaXZI_zc6BkIzCmk95wdb4,19864 +sklearn/neighbors/_nearest_centroid.py,sha256=a85jX0t6z64tI6afHzSqjW0rB0uwhABQV9DU10O0LIQ,13048 +sklearn/neighbors/_partition_nodes.cpython-312-x86_64-linux-gnu.so,sha256=vCBwcsfWq8vQfaBnhIWt7T0tJml2hNrzJn-NhcV3LEo,28912 +sklearn/neighbors/_partition_nodes.pxd,sha256=rngZZqkJWPnBW8BRvk0FgM817-lcHgCoBWEd91X0Dbc,288 +sklearn/neighbors/_partition_nodes.pyx,sha256=iJw0PB95n4VgXORPMjDzLr0DJKgdfzoz_PUKyi0MelY,4120 +sklearn/neighbors/_quad_tree.cpython-312-x86_64-linux-gnu.so,sha256=2ELrhTvBgbQb_6u8f_lJYF3kLA_bjH_ZjRRuY4Mj0lo,186944 +sklearn/neighbors/_quad_tree.pxd,sha256=olKQpppK6rZ_HKcXS3swAb7dq_aTyOlcilY8bg_d1mw,4232 +sklearn/neighbors/_quad_tree.pyx,sha256=pztvIhqbZHC6iGre_wMDKY7o3qSZzu-bZDSzgB7ggCc,23664 +sklearn/neighbors/_regression.py,sha256=r5dKgxNNjwjdfuDzsOOeXsQ7S7tv3viPWjEReBjrEqg,18313 +sklearn/neighbors/_unsupervised.py,sha256=OXFrGjh8anfiEEflMk6fmZj0sZ6Ie4J-zfArTEhVQvM,6260 +sklearn/neighbors/meson.build,sha256=hZzIPGmdgKDqcNMm_WajOYj9-2gwXFpTxpAAdNWCzKM,1634 +sklearn/neighbors/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/neighbors/tests/test_ball_tree.py,sha256=hpoJiFpMrGxw0FEhd-KghO8zWtonaqSr1JgbKB7sdN0,7097 +sklearn/neighbors/tests/test_graph.py,sha256=QdJvyK2N138biDPhixx_Z9xbJ7R-aSxz5mhSSvh-HRg,3547 +sklearn/neighbors/tests/test_kd_tree.py,sha256=4cE2XJO0umuWnWPQluOMR9jfeJKDXmFETowsLElwKCI,3898 +sklearn/neighbors/tests/test_kde.py,sha256=kEZsv-8U0oWrkAVuzRidsqL5w1jQZ2b7tK9pFZYnm44,9745 +sklearn/neighbors/tests/test_lof.py,sha256=x0h5dzFQpRqSG91CviJa_cytpemWv2HPSe45leX-p60,13746 +sklearn/neighbors/tests/test_nca.py,sha256=CAT5f0TpDPc8hvpPHobj2y-41xuQDjk3d1dNmdiTeCg,19506 +sklearn/neighbors/tests/test_nearest_centroid.py,sha256=Uo0oebJiyjCnrkSx9mDN89EPbFjhmhV2c8boUEIXRyQ,7572 +sklearn/neighbors/tests/test_neighbors.py,sha256=Yi42k3z52_Qp96nIUnuAHPAbN4hd0yHlHrZM1zqaXH0,86776 +sklearn/neighbors/tests/test_neighbors_pipeline.py,sha256=CwllxS4T9cP2utY-xuui3GhgtjRBkA7759byS4LdQ3U,8147 +sklearn/neighbors/tests/test_neighbors_tree.py,sha256=8OagtxQTE0jNy7-rTbl4L9lEbCgarf6n_jkx1woYlOs,9297 +sklearn/neighbors/tests/test_quad_tree.py,sha256=y_WE4jNxliYos_SiICl_miGIya2IJlu71rXzwvQw2qk,4856 +sklearn/neural_network/__init__.py,sha256=p9-lqKAT-q-6wCIj0R97J1cflbINXL4-0X60SF3hhmY,276 +sklearn/neural_network/_base.py,sha256=bp4Z3TxnFtzH7VinDh9GAuywChqyz3NohImLLymG9jg,7983 +sklearn/neural_network/_multilayer_perceptron.py,sha256=q1Kcv3H4gaQR6so_P77eopnoWGm269xxuCM_LPLCIi0,65995 +sklearn/neural_network/_rbm.py,sha256=Bi37Of-5A-gfCoEBo62QbANnPD9WBdW_MVcYfYsey4s,14968 +sklearn/neural_network/_stochastic_optimizers.py,sha256=ldZWuIL10VpHq4tZ2PzJrTSWzAQjdpbzx7iJEbbFyMw,8838 +sklearn/neural_network/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/neural_network/tests/test_base.py,sha256=jSriY_p7h95ngec0Iggh2IwDpeR67RirQ5-q2RrP9Zc,1566 +sklearn/neural_network/tests/test_mlp.py,sha256=ICYTyg2Bf15s4nPhxBEjq5rUj38d8Dgr5dA2_PARTAM,36232 +sklearn/neural_network/tests/test_rbm.py,sha256=Ucezw6y1X0HU9PEC9lniKrqXplVXjfX5yjWueHIPPkg,8048 +sklearn/neural_network/tests/test_stochastic_optimizers.py,sha256=9JhAPo1Qc0sA735qPORoKtS04bCTts9lQ65P9Qlhtyo,4137 +sklearn/pipeline.py,sha256=BvfMt0oxq508kA2HyBS3zoG4rL2TitdP6cxnQHXweGk,84861 +sklearn/preprocessing/__init__.py,sha256=IW0_AGxFmhh1JCuwnVwBPv43_M_zkvoXO5aorgz82iQ,1503 +sklearn/preprocessing/_csr_polynomial_expansion.cpython-312-x86_64-linux-gnu.so,sha256=RGLETjIp7hU1rFquBYuOMeZhSTNDQd-BW0lHQfsXjOk,363784 +sklearn/preprocessing/_csr_polynomial_expansion.pyx,sha256=vbTDWGOdzC6xb_AxTj-WeYWghXDO0RYqlxKx7V-N3uw,9154 +sklearn/preprocessing/_data.py,sha256=ElUECg65yZ9Yd11VzzT2dc9hU598Cg3Q1qNssfDaHhU,127903 +sklearn/preprocessing/_discretization.py,sha256=wN5OGmOv7ZMIVa_UtqGIbfe7mye3cz100R36IpVTQzk,20951 +sklearn/preprocessing/_encoders.py,sha256=GCrYWMY88owm1rMyJQAGwFKlAuSDdAtbI2o8jnST25c,68416 +sklearn/preprocessing/_function_transformer.py,sha256=uAwQKTIf7T4ii17BOjN6hx9NQF4gFzT82warU5i2r1I,17117 +sklearn/preprocessing/_label.py,sha256=90zXmJLk9cFQSka5K9-QZ6WM4iA2_dxtctBkXML1gD8,31271 +sklearn/preprocessing/_polynomial.py,sha256=JysoQGJRO-QPba9ahIUMvNPotzBGCsnI7iU11bxR8Ys,46303 +sklearn/preprocessing/_target_encoder.py,sha256=bmH3lffWPuc1JhurIGfWC4Y1pG4orwX5HF1Jjdh2yro,20612 +sklearn/preprocessing/_target_encoder_fast.cpython-312-x86_64-linux-gnu.so,sha256=WyeQ1Zq2pSdbNZo4U55bXeDW4xcHYERa1c5o4Sjd5aE,385168 +sklearn/preprocessing/_target_encoder_fast.pyx,sha256=svYh2Yd1T1ursqdyVJmR8CUIKIbVV-AyIFHw9AAHJ4g,5941 +sklearn/preprocessing/meson.build,sha256=D5DfSN_SRseZ2iljBqU9IKnue8HeH-27TnVDTQY1uhU,357 +sklearn/preprocessing/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/preprocessing/tests/test_common.py,sha256=1gLqwBEMTpCJOMsftAwACox0d8wbqfB9z-JtRLlx9NM,6793 +sklearn/preprocessing/tests/test_data.py,sha256=vh5nbrVqCjfgJQNnaUzUanqhzeAZCZadqqQzznqdXhc,98518 +sklearn/preprocessing/tests/test_discretization.py,sha256=B6drMSsu7tEiQ6wwdZ0QxULFvPwu_tQAmVsEu-o_Vx8,21803 +sklearn/preprocessing/tests/test_encoders.py,sha256=pLvymUguHds5nzY_eJY5iOkIDiNhJw0E3deYSVQaXVQ,79549 +sklearn/preprocessing/tests/test_function_transformer.py,sha256=xPDnZiJwQx7WNDTPsaw1p1zXijcGdIvIYUDYY4tCX-I,19272 +sklearn/preprocessing/tests/test_label.py,sha256=7aCITA2EprYNs0yC0RzXfcFlu7wvk3VEZZ6MIqvpyXU,25641 +sklearn/preprocessing/tests/test_polynomial.py,sha256=VwYFsVxt0tzhm-ptXdLAzIh10QNG40R1h3oV1E5BUAw,41236 +sklearn/preprocessing/tests/test_target_encoder.py,sha256=WADxAKbtWNAIIoP30C_uEC-kfTnYR2Hf1hL6qV1YlbE,27802 +sklearn/random_projection.py,sha256=-aCd6ZoTf6iRFkZhws-IEmt_P7OctlVn9gQQyTo6Sfk,28351 +sklearn/semi_supervised/__init__.py,sha256=Qkdt7JhsauqNEyletlbs7aU9RPxnpghyTw_WionXWC4,435 +sklearn/semi_supervised/_label_propagation.py,sha256=NBZSDxTeFPwGbt_j8rCLggiVU35pDZpgZs4kxLdUSpM,21448 +sklearn/semi_supervised/_self_training.py,sha256=wUXp_i3rvYLw1bMSa6M-1WjtvW9uImMn8sM9u1bPjJo,22014 +sklearn/semi_supervised/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/semi_supervised/tests/test_label_propagation.py,sha256=1dxD9IP2hGuUybuPkdMuJRFnoC1Dlz_Fti4_EJRpbxE,8801 +sklearn/semi_supervised/tests/test_self_training.py,sha256=9NxdemICSM3BO9Jw77PzhuY4iQXmv4Jzoa9_9YfEyMo,14428 +sklearn/svm/__init__.py,sha256=UKj8B0uoG71h0SR5mcFPTxPp84peaKEjf1QWQaUwwSA,454 +sklearn/svm/_base.py,sha256=LUZHaaTiHTAQ_3TLO0KQVDpj2j4yyCk4skCQ0-qsWmk,42956 +sklearn/svm/_bounds.py,sha256=_BV2163ys85SYXAFw4SoF80rkCpyERqd0d3L8jI4aW4,3459 +sklearn/svm/_classes.py,sha256=7dpD6qGKXhFiTIw1JvA7m_9ObxbQZUvyroLfy5uh36Y,66217 +sklearn/svm/_liblinear.cpython-312-x86_64-linux-gnu.so,sha256=3j2d9nFpXHR1S3EpCxE5lyZdKo_iY0wAbYADm6AEuBo,190344 +sklearn/svm/_liblinear.pxi,sha256=H5Li48ad7cS3z_jZu1lAJDByXVT9kA78pEVQ-AJCerI,1719 +sklearn/svm/_liblinear.pyx,sha256=_I3KvUevamU1X-7Ev21XNcdlfu8z1Jbd3IOEXcjUOwE,4101 +sklearn/svm/_libsvm.cpython-312-x86_64-linux-gnu.so,sha256=jTaF9svz3GOUo-7OdYJ1a3h-PkczSRmO0UaJJyHanW0,430624 +sklearn/svm/_libsvm.pxi,sha256=cV0nEGKq3yrtKsNxHpioX0MOmwO_4dURv9gR7Ci8TKM,3186 +sklearn/svm/_libsvm.pyx,sha256=xG6wFD9ciyARvXbOliyAm2KJK7WR4dskyq5DwbTMRhg,26669 +sklearn/svm/_libsvm_sparse.cpython-312-x86_64-linux-gnu.so,sha256=UCi7dUaxet6PBED5wCoC7Zv_aeAcRtwlxj-Ump1eIHk,380672 +sklearn/svm/_libsvm_sparse.pyx,sha256=tDSRkgykLtwTg5rZGGMezynJCeJeln950PL-D1zZ4kY,18886 +sklearn/svm/_newrand.cpython-312-x86_64-linux-gnu.so,sha256=Heyr30kfJzaybO0yEbJfhzDjnD5IlFemtdKbrFu-9Y8,56736 +sklearn/svm/_newrand.pyx,sha256=9Wgz24TrfT03OhvSrJ50LOq-6dznY73cXToi_seg0hg,298 +sklearn/svm/meson.build,sha256=yS7MPRrPJ5pxtVemPkGo7wNzNVfIVzivnmP_elfNd3M,1218 +sklearn/svm/src/liblinear/COPYRIGHT,sha256=NvBI21ZR3UUPA-UTAWt3A2zJmkSmay_c7PT2QYZX4OE,1486 +sklearn/svm/src/liblinear/_cython_blas_helpers.h,sha256=x7EL4uLM9u9v0iJmEaQDFJgXEhxM-3lWQ1ax-78gtlE,458 +sklearn/svm/src/liblinear/liblinear_helper.c,sha256=9rtFOnID6rSuKKkkj1kGLhPAqbA01-pYIB_14JtlREw,6380 +sklearn/svm/src/liblinear/linear.cpp,sha256=-eupquURUIdGa-8VKFJpvXNP2Fl-DpC8fhZLOI8t9IM,62634 +sklearn/svm/src/liblinear/linear.h,sha256=w70N_Hu8NaTsmxYTffXfOTgpbK1nbcpzVAiT1OOsiNs,2458 +sklearn/svm/src/liblinear/tron.cpp,sha256=meJe2MJ4b5dOutshAAxU1i9EKZ1lXYp4dXbiL_zgyP4,4940 +sklearn/svm/src/liblinear/tron.h,sha256=rX95I3vubCVFvoPaI8vE6jqdsWTOvq5GHx8FUcOiRFE,768 +sklearn/svm/src/libsvm/LIBSVM_CHANGES,sha256=n5OrHZ65A9CqDFxpGfph5_tWGAuiRhdBI0xAGWoYx9I,769 +sklearn/svm/src/libsvm/_svm_cython_blas_helpers.h,sha256=H25CeF4GM3FQq0B6u3cQp1FZGAiGlbOOhgFqn4RIAFk,217 +sklearn/svm/src/libsvm/libsvm_helper.c,sha256=fVUEDyWrrX65g3pstPpnxWdvWZlIsB4BoD4XCQ5gy-c,11718 +sklearn/svm/src/libsvm/libsvm_sparse_helper.c,sha256=fWKVM9H_TNNUcVhymn678X2PYCM4S1KrD6ArcRbdW1I,13247 +sklearn/svm/src/libsvm/libsvm_template.cpp,sha256=de-H2Nxv6VI2P8KXyAirKS8IAdtJYKfqPoDn3mMaIyM,173 +sklearn/svm/src/libsvm/svm.cpp,sha256=kOPTJGIi9eDqTR9xRZ_lu0KxL9fDW799-6inxngLu88,69105 +sklearn/svm/src/libsvm/svm.h,sha256=Vhf4LRfqLp7dE8swI2LmAKF3lf6ZOjC6L10k1IXJ96I,6262 +sklearn/svm/src/newrand/newrand.h,sha256=VGF__VxEdrYCRWeldvGF2AQfmb6DTH2bwR3QnsAmhQg,1840 +sklearn/svm/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/svm/tests/test_bounds.py,sha256=17Uej-UAlNFcsiVQRJVg7UQQY1mwXTNAS-pqIH2Sh5g,5488 +sklearn/svm/tests/test_sparse.py,sha256=7Uelip6jrKqyDj3BSio9RWeXzoIDR7Qts4bGB4Ljeok,15713 +sklearn/svm/tests/test_svm.py,sha256=i0DQrT_gyJWvtwzJpB3W7nLkiufJja7xROvWTN8otqc,49321 +sklearn/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/tests/metadata_routing_common.py,sha256=SRXRZEbTLYwnN7oEgRBPV7kpjESVwULuIOSj1gt6DcQ,20209 +sklearn/tests/test_base.py,sha256=VrVLt_AaDs8x11DdWZFpAeOWeOUvSEE5Kcx3stUBRIU,33694 +sklearn/tests/test_build.py,sha256=n9OrwEnrrozhLRaYwJNWYHrL65d7UHpmLbTDfVNtpmg,1181 +sklearn/tests/test_calibration.py,sha256=fkdT26HTVN4YMQtsrAUBJu_M0WPI3UKDFc64lnFsHYE,42703 +sklearn/tests/test_check_build.py,sha256=udkTjCgk_hJbkNmyyWxCZaf3-rfGCNudkHY_dH3lXj0,300 +sklearn/tests/test_common.py,sha256=Lc6qj8HJ1vOQ26ku2NbX2sPoaEaFemZApoxMq3z1-Wg,13255 +sklearn/tests/test_config.py,sha256=IOKEMY6L4Aq9Ykp90fjo7cuj9ReTWu7DQuEUeKTh4mI,5787 +sklearn/tests/test_discriminant_analysis.py,sha256=VaWug3CYu_OLTFrJ_AM1IALfWTjldYc03ntlpWzyfxo,22689 +sklearn/tests/test_docstring_parameters.py,sha256=zdFK0a9qLNGSePIx5T3CsF1iAjHDBSogOQpDiMY2iQc,11672 +sklearn/tests/test_docstring_parameters_consistency.py,sha256=MEG4Rut5qEKBgrkImKzRvr-iNEqZUO4iyG1SK9KYSIc,4171 +sklearn/tests/test_docstrings.py,sha256=t1zNwka5FH2Y1r5uweYuZwHR6RuicG5VJfdaK8YerNc,6853 +sklearn/tests/test_dummy.py,sha256=kSSm0v9b-rcoGR5fCETFmd2qYOoBcYSUyNZ1T4nYcdY,22085 +sklearn/tests/test_init.py,sha256=0cET-MRZ2v0MeHqLg9XqubgyWB03xsD3QmE-vBKF73A,476 +sklearn/tests/test_isotonic.py,sha256=YnhVVK8aTb5liVOKnzIy67aXw4Hk9GabGzuFd22zF9Y,22331 +sklearn/tests/test_kernel_approximation.py,sha256=h52dmpdAJyRzf_tVYYIAlu7Ac3gC8jv1_DDYw9E8U7E,16579 +sklearn/tests/test_kernel_ridge.py,sha256=qkwUUjuY5O1uMiXi9gAS-wXOCHa62F5T7VJnNdZdGOE,2888 +sklearn/tests/test_metadata_routing.py,sha256=nWWuf5wDbvi-r9ZyYHXUGLmMVhjm4UXqYeLN2Xbjnzo,40635 +sklearn/tests/test_metaestimators.py,sha256=6DVdtUKP7r5y3VafOX5SlaTimtxuT-OlQKYRLBD-HnE,11471 +sklearn/tests/test_metaestimators_metadata_routing.py,sha256=BKNAA6CAKly7n1CPHKdccZdRHcf2taSEubRtzdHTbls,32009 +sklearn/tests/test_min_dependencies_readme.py,sha256=BprXGDEgArPAoWLvBP8YPefv5vvKPzONzB1bSrRnzaE,4576 +sklearn/tests/test_multiclass.py,sha256=w2LmmymBo9K4ZyfFmMX27nvHIljeoaZlTi6WJUx-Pns,33934 +sklearn/tests/test_multioutput.py,sha256=3enAkYXMhvEFxC2zeyD_nZl5afjhF-eFtMRLwYYo93I,30553 +sklearn/tests/test_naive_bayes.py,sha256=SePTMoTagDqYQznjFrc01tIBTpMiWSK9MKvbdBnL9rg,35184 +sklearn/tests/test_pipeline.py,sha256=LmF4cO-0DjK5FEwgjE-zAyyV4RZZ9fuKJma8mzyGpps,81319 +sklearn/tests/test_public_functions.py,sha256=sCP84pcI2ok33NM2n8kllIDxxinIoDmffbjuj7cohN0,16738 +sklearn/tests/test_random_projection.py,sha256=PHgMtjxt5qvy6IM0YY6eWmNLelxdT2H4kF8BQbUBeRc,19583 +sklearn/tree/__init__.py,sha256=w6EhQ5jlcvPSer8w3GTWY0RPufTiCmp2IaRqhYYdmbY,572 +sklearn/tree/_classes.py,sha256=DygqR1NKHxRMECLn888NTnF7KX21HJ5kg1BwA4tIu9g,77648 +sklearn/tree/_criterion.cpython-312-x86_64-linux-gnu.so,sha256=YWX3Qpq_sBasowXpS3DenmL15EEQasYNgOEMeVWhCmQ,211400 +sklearn/tree/_criterion.pxd,sha256=K_TRUrtxRiX_4Q_AltNDYtkhYLerlREjq9F14hcGJrs,4491 +sklearn/tree/_criterion.pyx,sha256=ujjfJAUnJ2y2rpHiJe5xumhuLC5S1eSqTyFmpyELN28,61626 +sklearn/tree/_export.py,sha256=Bnz5BYL-MXWgX9nYQYEzUQqWazLLFYwT8vRKTB2zWfQ,40733 +sklearn/tree/_partitioner.cpython-312-x86_64-linux-gnu.so,sha256=VsmtD-rhAWULNfskBBlUz7aEzXoH35OFyf6YrZYvXOM,181776 +sklearn/tree/_partitioner.pxd,sha256=Wlf1kIFiFeykzrO1YYXOcGd_RZwnUTO4YXTwNCVfXNA,4939 +sklearn/tree/_partitioner.pyx,sha256=CvptDNtr-F7WkHuOk0PVN8USOAczQ-U9jbmG-oCIOhs,31975 +sklearn/tree/_reingold_tilford.py,sha256=ImilHGv15TI5inwyBar79zEy-V-TxD5A9NUk0sBM91A,5157 +sklearn/tree/_splitter.cpython-312-x86_64-linux-gnu.so,sha256=G2E1I1xtnIqazg976gc1g91GwNZIbrGAtM2buhiIlWo,156664 +sklearn/tree/_splitter.pxd,sha256=Yq0osi__MEU1QIJ_rz6Oq940Eu5srHBUMTQptnnWRUY,4436 +sklearn/tree/_splitter.pyx,sha256=Gj0B-hAU-TBKi90MRwZu9_WXy8v0lQonNoinb1QlauE,33411 +sklearn/tree/_tree.cpython-312-x86_64-linux-gnu.so,sha256=pUVQALpCKxmstV22cZSS_LgeNaJedJ5CrV6ztFDeObI,462088 +sklearn/tree/_tree.pxd,sha256=0z7WppVbOyb-1kOv0eKSO6iBrySonlhScaPjf_YWlsw,5431 +sklearn/tree/_tree.pyx,sha256=gKlP2sGFwwKXm3BS8kHoFXAMPhVczyLp3qFBX9G1rnM,73917 +sklearn/tree/_utils.cpython-312-x86_64-linux-gnu.so,sha256=tpIEHmYW1VlhfbHQogd9w_y8ubLSAY1peZOEoylHE0k,147496 +sklearn/tree/_utils.pxd,sha256=x-vTBBqxgTB-py3mJ8QQ3fqDfEeexkzsLnKbXcgk-Z4,3622 +sklearn/tree/_utils.pyx,sha256=k-viNXwSoiZ8Xe-S9BxyBVIWQW8nFuN6TInVpDJVCDA,16609 +sklearn/tree/meson.build,sha256=h_vVyJ3Uap4rs3v7OnDqMq8gV0bxOmsYYdCZAi_G1tE,899 +sklearn/tree/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/tree/tests/test_export.py,sha256=ClpF28hE3ZKLU90Q1ncPw6mlZO_Gv2BTlgThBo3AzqE,21026 +sklearn/tree/tests/test_monotonic_tree.py,sha256=-UtlxTxsTe-30KfK7KsfniieAN2Iks8Z6l23UTjKoz4,18612 +sklearn/tree/tests/test_reingold_tilford.py,sha256=xRt_Hlm-fGJ2onva4L9eL5mNdcHwWhPEppwNjP4VEJs,1461 +sklearn/tree/tests/test_tree.py,sha256=QIq_89dUB30m99D7XbS2EqhiU48sqDZd1_QUz_36Jh8,99442 +sklearn/utils/__init__.py,sha256=3Nz2hkOhi9RofSV3J-1KDnEfiXXIosfxhCLVlclJ5X0,2134 +sklearn/utils/_arpack.py,sha256=dB4rJYnuwSUXl73JoLISQbYHSXeco10y3gjNKGGEAig,1209 +sklearn/utils/_array_api.py,sha256=hJTLyn5kuMCwGD5-C5peyDEWR2jEY1lZUzNYc2En-6A,34748 +sklearn/utils/_available_if.py,sha256=CUJT-FoWEUiSCJ7BnfBFZ__74shIuMbHSBhQpwbVgnE,2945 +sklearn/utils/_bunch.py,sha256=_QRWzRU0TcO0Suv-mUFfuvuNrvP0Avp-PI0RY7uxdbA,2176 +sklearn/utils/_chunking.py,sha256=fpnjaJDWTLndUv4bHfIlt2gk0YmPYdArtYljwVA0KsM,5438 +sklearn/utils/_cython_blas.cpython-312-x86_64-linux-gnu.so,sha256=wLhaarG3CWGBWF5ts3Fw8PbBSXZks59XuVSQVHR1e7w,345928 +sklearn/utils/_cython_blas.pxd,sha256=Kx-TV-Wy3JD8JAROmcAB3623tmk01WnffCiFLResUZI,1565 +sklearn/utils/_cython_blas.pyx,sha256=c9hEUrULMKXia5j3Ia88YDaJ7Lv4RGsqqxY6HIF9oQY,8282 +sklearn/utils/_encode.py,sha256=bptNb3r5s1VW1eI--TJM0S-feBJ9ozOceq9ju1DioWs,11797 +sklearn/utils/_estimator_html_repr.py,sha256=fgZ19z0W2bb51uWISQtaOUYxD2nvPx-EHgIuc4jHiO0,898 +sklearn/utils/_fast_dict.cpython-312-x86_64-linux-gnu.so,sha256=c2eLAomgZJVQp7WCjv-wsiFPLxwp3r07yt2cq3I7-w8,165216 +sklearn/utils/_fast_dict.pxd,sha256=IyPazoB2nBPCRf-TrfMqGDl9xQSM9QmnNx1nDUcSNCo,516 +sklearn/utils/_fast_dict.pyx,sha256=H4RiRkSLH3syEzlAR54xArEAWURDmp8U4S17Adxbf2s,4652 +sklearn/utils/_heap.cpython-312-x86_64-linux-gnu.so,sha256=MTSzz1qBzXFRGcPeB79aLf2RXiidGo5TsYr7AFY-MEw,23472 +sklearn/utils/_heap.pxd,sha256=FXcpp-JAYxvFGZqLZ6IrJieDZ9_W2hP4sVOLY4fzJAQ,256 +sklearn/utils/_heap.pyx,sha256=ca-rKqGzTbGz7X-HuLF9VzkZ3CfNEiIF2Bh7QjfZQ7s,2253 +sklearn/utils/_indexing.py,sha256=WpNkZhzQf0SQkNGDhZT0bum6BgxpWFmYciTWQQaYLcU,26366 +sklearn/utils/_isfinite.cpython-312-x86_64-linux-gnu.so,sha256=J94lkEZU8CsdFA_km3UwLYBqc16yM6pieJt9-h9h0js,118048 +sklearn/utils/_isfinite.pyx,sha256=PFLLYo0BWaxpfNP6t0O6r0cLY9KXZSzHQmVYQKYbBtI,1414 +sklearn/utils/_mask.py,sha256=QoXi1rB6ZLp5GfOmv5jY47Wv2IS20-NS7bTt1Phz8Wc,4890 +sklearn/utils/_metadata_requests.py,sha256=RLnau3NUdivBZyGHih2yK7-C_Q4-H2g3CILkYLvQ8U0,58254 +sklearn/utils/_missing.py,sha256=SerUx-LWOIZFw3i6uxWQ9KkJX5n3BWZJZFt6lELH1TE,1479 +sklearn/utils/_mocking.py,sha256=J7wTGzJL364cLCYeIfxNxmtPYSvM_gKRAcJ7WqFvRvg,13661 +sklearn/utils/_openmp_helpers.cpython-312-x86_64-linux-gnu.so,sha256=DTCcU6LvrvK-N0YotbeUg1Ie-e-bMKiCuM8kymdXDps,84617 +sklearn/utils/_openmp_helpers.pxd,sha256=ORtNXjPXDBOmoHW6--54wwrMEIZptCAZ6T8CJPCuJ-0,1069 +sklearn/utils/_openmp_helpers.pyx,sha256=6NgzGt7XMaLuzqqigYqJzERWbpvW-pDJ36L8OAVfdKw,3143 +sklearn/utils/_optional_dependencies.py,sha256=ppUWhMBeNGhVcPjZDqrmDOujWZ_qApndumj6OesynOA,1300 +sklearn/utils/_param_validation.py,sha256=H3vhAp9Cbn-7JuTGozS-MwGoOlEcd1fHKmP0oq0Z2UY,28578 +sklearn/utils/_plotting.py,sha256=Tu8t4k0EhWACBvsH5t0TlFOVdeJ4wI9PXub8ZLdyadQ,15370 +sklearn/utils/_pprint.py,sha256=QtAc-rPoco7xOuky6RLmMafpzPKsxud9LEgCGAEh9gg,18520 +sklearn/utils/_random.cpython-312-x86_64-linux-gnu.so,sha256=GQ1Sm8EyfE_2frH9E86UTQe80Bu56SxiRtOXygCcft8,240848 +sklearn/utils/_random.pxd,sha256=_9sOwgmCxQ3rJCvVPplc7FJ-2iJgXZxeU3q8bo2oXXE,1250 +sklearn/utils/_random.pyx,sha256=H1plEnif12DxB2ZKB8H_mkC5WxXrPHpeFRbTLSxZQUI,12589 +sklearn/utils/_repr_html/__init__.py,sha256=vKm4xxqJIfK6Tk6By-YU03YcE6pR1X1juFrOsacfZjY,79 +sklearn/utils/_repr_html/base.py,sha256=ZrLqweuvYizwi5HzCx8261BkyF7ot9iQ9Xf2zJcOx-o,6146 +sklearn/utils/_repr_html/estimator.css,sha256=cypIOeM_ga4IcYEXJfke90HNLJ1bHmhM-uUh52wQER4,11237 +sklearn/utils/_repr_html/estimator.js,sha256=TeUu7jCSDl2Af2v9C8I2qGOMw-LCHbh7ED2EMsyQaEs,1730 +sklearn/utils/_repr_html/estimator.py,sha256=N-c-YguOE554YmjZ5In6k0J6Bsz4HJkZmqyeaFNf844,18069 +sklearn/utils/_repr_html/params.css,sha256=kbxocqXiZSXRJB697838Kl3bTBJ3PiZC3lWU3ZGNXJY,1896 +sklearn/utils/_repr_html/params.py,sha256=pUWHGeI_EWfN3Wxum5HCOlevm_qOMuEdgiIBNbPjhvs,2651 +sklearn/utils/_repr_html/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/utils/_repr_html/tests/test_estimator.py,sha256=EuxDDZhcJDR_Wlawk4vFMsXPXlqS2XUvZI1oWZfdCr4,21420 +sklearn/utils/_repr_html/tests/test_params.py,sha256=qimQsD0wAD3249bS-_9nvMg8m-_u4igjzXeBvO5utw4,2356 +sklearn/utils/_response.py,sha256=_-mr2y7YAVXx_lrZ2Cz2RUj54LVC4BVhzQzL7Qy6fco,12117 +sklearn/utils/_seq_dataset.cpython-312-x86_64-linux-gnu.so,sha256=PbIAokzfxGJBlJf9MtFzJq3nyGDKnZxgJRlVIphphIU,219744 +sklearn/utils/_seq_dataset.pxd.tp,sha256=XWHP_pzN2o5rQkKSgnZWO_VMdASTPOQap35ebvWnRXw,2567 +sklearn/utils/_seq_dataset.pyx.tp,sha256=tkpcGPtSGLrAJxE4GGzXydoMvvn_6jWs4EKLUGEWio4,12252 +sklearn/utils/_set_output.py,sha256=l8xL8wklyS8hBdF-6HYry4m8HlSrRQiGe58JIja758A,14793 +sklearn/utils/_show_versions.py,sha256=GL9Ca3wwOStKVdOYIqTv2vRB5BWCTwiJTBiQSIaYEmI,2548 +sklearn/utils/_sorting.cpython-312-x86_64-linux-gnu.so,sha256=qG2VNri5YzWCv2goe12ZtatC1SFlR0PiNxHvuyIzQbc,28096 +sklearn/utils/_sorting.pxd,sha256=i8Bkh1j07pgP6pIvzFxFIZ7uAlR1fQOCbIHh7v04xw8,161 +sklearn/utils/_sorting.pyx,sha256=Q-F_hwd8KFokcfaVXOswOWXVjdIjiQREoQRLaRxl9dY,3280 +sklearn/utils/_tags.py,sha256=B4xmNHIdYlVdrFI1n5ji1_qZYdVcr_SWcAX6Ck_ZNns,12283 +sklearn/utils/_test_common/__init__.py,sha256=vKm4xxqJIfK6Tk6By-YU03YcE6pR1X1juFrOsacfZjY,79 +sklearn/utils/_test_common/instance_generator.py,sha256=36b7kvFHnuBhRyUgb49OLNYfUaHKR0bhc2aP3Pw55m8,50205 +sklearn/utils/_testing.py,sha256=1m-_LOB2ml38qvcf1QQ1Xd2ArwwmP7RpXR3e0P7tC-c,50818 +sklearn/utils/_typedefs.cpython-312-x86_64-linux-gnu.so,sha256=BzHF5ZYssdvlFsikCQRyt1MCrUVU7kq5Bxhm33KbKIE,152424 +sklearn/utils/_typedefs.pxd,sha256=gew7YuCZWwpo-JWXGDIrwJ2-K_6mB-C4Ghd_Zu9Gd-o,2090 +sklearn/utils/_typedefs.pyx,sha256=rX9ZIRqg-XFgtM4L3Mh0YAsmRHSnccxdg2nEs9_2Zns,428 +sklearn/utils/_unique.py,sha256=IBhmM0fwGmuhcNjtSf_1-OryOx1plPOGwXVE4sndDyM,2972 +sklearn/utils/_user_interface.py,sha256=dzS5H5O6prEkNLholFgBLWOfFp4u0Mw61mDBQFh5KZ4,1485 +sklearn/utils/_vector_sentinel.cpython-312-x86_64-linux-gnu.so,sha256=OzQgbDdhTL4NRc497n93mKSaGRIChPFrLuxwHxhHBv0,163056 +sklearn/utils/_vector_sentinel.pxd,sha256=G_im5dT6DaREJgMAGu2MCd-tj5E-elc5mYX4sulSYW0,296 +sklearn/utils/_vector_sentinel.pyx,sha256=H1GeEQ7qOSSwgo55sNUaiWzVb1vbAqOr03hnfR-B-o8,4458 +sklearn/utils/_weight_vector.cpython-312-x86_64-linux-gnu.so,sha256=SE3a3Y7uAZTA3ZN-3mdprKPYp1W3EPBg4bDdn_DvoGU,93144 +sklearn/utils/_weight_vector.pxd.tp,sha256=VXw0bYJBkzy0rFFI_wfwPFZsAnfdykJz0W_svYGXiKM,1389 +sklearn/utils/_weight_vector.pyx.tp,sha256=8NR10zND_aAQt2iKtOXIGm7nu665cVsXoKmaZioWH1I,6901 +sklearn/utils/arrayfuncs.cpython-312-x86_64-linux-gnu.so,sha256=TEJgFSxU1Lz89EvRUhIQqyqnTjzyz8bAYLUznWK36Ro,188664 +sklearn/utils/arrayfuncs.pyx,sha256=vw-BXUbdkWBH6l5XAGRMPB0Ek8lRdFouVrVPjPD-iBg,2908 +sklearn/utils/class_weight.py,sha256=zcSNeTVb852HxMJlTUj86mbuWJdWA2dSMS-ft3ESYZM,8722 +sklearn/utils/deprecation.py,sha256=wi1BfyMwrwx1vdgSsw42Vser4aeKnKsDdlNh2OIrhY0,4374 +sklearn/utils/discovery.py,sha256=vQzoj_8YHjepxOlGT1YbF2C871mjkUuYbQRpM6Dho4s,8698 +sklearn/utils/estimator_checks.py,sha256=9el1g3Vc_qXy2Dmo6sUGlco4lv9NMHL8qwFx7vZbtm4,193210 +sklearn/utils/extmath.py,sha256=ZfCd1ARG9C2HP5Oss244N9rf7yawYz-hSIBsiPkFXPQ,48516 +sklearn/utils/fixes.py,sha256=6U_XXwI976vOFzT2MkxeuFbtkOnbtWmpNsqv-RvqSgY,15211 +sklearn/utils/graph.py,sha256=Jorg2G33rvJEC9ySKfQ30Siu_KGZ3VLARo8U9xAKxAc,5696 +sklearn/utils/meson.build,sha256=B12-imQSrpXZL81WVtFk7kGtjI6yZPIW4hzqYdH3Djs,2575 +sklearn/utils/metadata_routing.py,sha256=yOqxU3x6s2TAQ35XN9uTR8DLQtAucuXbzjrjlLIyxKY,578 +sklearn/utils/metaestimators.py,sha256=OvXMa6tex9Gog2wzaHZqoMd1DYHEp02-Ubrz2czXWyE,5827 +sklearn/utils/multiclass.py,sha256=pAwNDXiul4QCW6UJP1MJGNLgsM8JBNo7vDtVpidihQE,20391 +sklearn/utils/murmurhash.cpython-312-x86_64-linux-gnu.so,sha256=HUwpyEUvQSyl1nYVkCgWRmIpao8pzkkwEXgApNhaRU0,142464 +sklearn/utils/murmurhash.pxd,sha256=Z8mj3dEhTQN1MdzvlHA7jS9lA6fjkqYFK1YTeVwC10o,876 +sklearn/utils/murmurhash.pyx,sha256=bRyDiVMurmKHJW32MNMqzmEA9Mj-eNR6zBoj5C6M4MU,4530 +sklearn/utils/optimize.py,sha256=GX3H4bzCVBZ1Fv6eUcgvU2d7zx-xBclNMRFCcqLuWn8,12298 +sklearn/utils/parallel.py,sha256=0f3yUjH024aVyf6zauOj1vhOmwlibQs7CFQUJCkoa00,6082 +sklearn/utils/random.py,sha256=8fAjBUbjcuhHypUwQ7X7GB7D-k-Y-RfhhOiZyQ182Sg,3683 +sklearn/utils/sparsefuncs.py,sha256=msAts1ikks1NEGG1EvTZhcp2VfIEsEEh3rkPAYlgLLo,22598 +sklearn/utils/sparsefuncs_fast.cpython-312-x86_64-linux-gnu.so,sha256=lONvfztoL0Byu5NEkxgb4xnrlD7hYWaTUB9KLduamuE,712144 +sklearn/utils/sparsefuncs_fast.pyx,sha256=XcHvxCBHTlxwhn4kZX5FSrOl36ziouVbBJDhKpN5KtA,21795 +sklearn/utils/src/MurmurHash3.cpp,sha256=5BI_ft6ZWDOlbpDI-U1MM-bvqH5G2ssGgfLJWD5bozU,7968 +sklearn/utils/src/MurmurHash3.h,sha256=vX2iW09b4laQOwIwXSiTu14wfdkowndTzKgDAmHQPi4,1155 +sklearn/utils/stats.py,sha256=B7SSDY9D-KSUd9AdmsjIVtr5zWulMypyLmj7m9pLgqs,5035 +sklearn/utils/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sklearn/utils/tests/test_arpack.py,sha256=EL3_6a1iDpl8Q-0A8iv6YrwycX0zBwWsL_6cEm3i6lo,490 +sklearn/utils/tests/test_array_api.py,sha256=X079ez3xRzqPQ-6RhM0nJjVWhm8z-nu6ZtZASHBotGA,21062 +sklearn/utils/tests/test_arrayfuncs.py,sha256=DGbK5ejSO-_ibZDoeM5RlDNY7a8Z8eGScnq3cThQ1Js,1326 +sklearn/utils/tests/test_bunch.py,sha256=QZXKwtgneO2wcnnrbMVM_QNfVlVec8eLw0JYtL_ExMI,813 +sklearn/utils/tests/test_chunking.py,sha256=4ygjiWbrLWxqgYYKPZ2aHKRzJ93MD32kEajbgIO0C6s,2371 +sklearn/utils/tests/test_class_weight.py,sha256=K-vCvXz5GlqOl1h6QdPXRGs-svR82ql2fD6ndzPhsXI,12957 +sklearn/utils/tests/test_cython_blas.py,sha256=COhzY-WHwQLIF5qn_XvBPggWn9OfMw7J2IOLV63MKaw,6709 +sklearn/utils/tests/test_deprecation.py,sha256=BRp5wLhsGtSdDt1cWwF72kqqsqbNDvggGs_kETP0g0U,2294 +sklearn/utils/tests/test_encode.py,sha256=QiiG0ArBGF7ENYrvcgPGwjYgUdn3W6Ch_GE9VEF2DWI,9603 +sklearn/utils/tests/test_estimator_checks.py,sha256=_5OzYdijgENQQfL_zDN8TTntCCjXVq-MLtwqGnvHOhI,58138 +sklearn/utils/tests/test_estimator_html_repr.py,sha256=YeI-j1_CkTR82HpHi4vZflc9zshWQRBx_PT9qiWz_6Y,614 +sklearn/utils/tests/test_extmath.py,sha256=6lQ03gV-8CD0-02tJU7eVz4ORNgi00WfxDuHDhj2D1Q,39056 +sklearn/utils/tests/test_fast_dict.py,sha256=Y4wCGUJ4Wb1SkePK4HJsqQa3iL9rTqsbByU2X3P8KQY,1355 +sklearn/utils/tests/test_fixes.py,sha256=8w_0PiyUlBq1EebvaCMJdttuCFStZykRYGQ7sTCdzPs,5328 +sklearn/utils/tests/test_graph.py,sha256=0FGOXawAnpEg2wYW5PEkJsLmIlz1zVTIgFP5IJqdXpc,3047 +sklearn/utils/tests/test_indexing.py,sha256=P5ulIjFw0gkoHFRGSORlhCFt6_y7EbvcdHTNHOuM-jM,23721 +sklearn/utils/tests/test_mask.py,sha256=eEsLP_o7OqGGFt5Kj9vnobvHw4sNaVFzHCuE4rlyEd4,537 +sklearn/utils/tests/test_metaestimators.py,sha256=x_0agW4puaVCmqPwBrk3FrWIZeK3qgM9eNJWUxYD640,2107 +sklearn/utils/tests/test_missing.py,sha256=3lPgYdyvRkzPH-Bw82N282i_5_aYN7hHK-bkoPBw_Jg,709 +sklearn/utils/tests/test_mocking.py,sha256=S0W07EnpATWo5sy1V-FAoPpyhRT1DHOveb9PyXa7ibQ,5898 +sklearn/utils/tests/test_multiclass.py,sha256=h0GSlMbftD2sVtel14MDHd38u_7tVMGHI9uRJN8kIk4,22059 +sklearn/utils/tests/test_murmurhash.py,sha256=b-WKvPEJmp8XiIjGVDv_c_6mGOL-nz9XOvMFNXPpXeA,2516 +sklearn/utils/tests/test_optimize.py,sha256=FWWUjF2yJ_zIn41UCmik8iTKMeww7mp16Djq1UhPFKs,7603 +sklearn/utils/tests/test_parallel.py,sha256=ldqH6MqBTZCM6EIjHySSzlx3wRwWD0_cdV4DzQwpKcQ,5665 +sklearn/utils/tests/test_param_validation.py,sha256=fApmDQ01VznF_uaA4bKch0IqHHfBMLxAa1VMa_c7su4,24407 +sklearn/utils/tests/test_plotting.py,sha256=UWU43kvMkBbNoYJUAqDdgSHwINeiPxv3dnWc37PGy7E,19938 +sklearn/utils/tests/test_pprint.py,sha256=Bg9Sv8uNPfM3bDtWrIeahnXacdyGSjliIecPyHCqmTc,27858 +sklearn/utils/tests/test_random.py,sha256=wzhfCP5lhSm-5PaCvbcgqvsnuINkvJNVSanQZiRmc0s,7149 +sklearn/utils/tests/test_response.py,sha256=JW7hWzu3l8_c0GNU3AHxbPynknD7zaqXuvbGPLsFHEI,14142 +sklearn/utils/tests/test_seq_dataset.py,sha256=Nr1MGuCWVEM6T7OWPKDUxss7XzAaX1qFZwjN5KC_sFU,5868 +sklearn/utils/tests/test_set_output.py,sha256=sPtTyGoAsIXO4IkT6TYhSgXPeQu_Qt-kZ9gxErWeeos,16131 +sklearn/utils/tests/test_shortest_path.py,sha256=XN1SF7TfMo8tQCC-bUV2wK99jR32hEM7xZOl54NbIoQ,1846 +sklearn/utils/tests/test_show_versions.py,sha256=eMzrmzaMs6TO7JSMSfSokfAVW_daMms-7Xel5XyqKZc,1001 +sklearn/utils/tests/test_sparsefuncs.py,sha256=mnmWRDHuvKHT-rgkHoC48edn-sAguWAYzA-OfplR_4w,34943 +sklearn/utils/tests/test_stats.py,sha256=r1kIVHmemK0scdg_1M93HQXEjFCn3Lv9GkC6xj5-D7M,12576 +sklearn/utils/tests/test_tags.py,sha256=oQf8mu-ooCy2EZUiYsJer6se4BTwS93mx4IepysvU6A,4644 +sklearn/utils/tests/test_testing.py,sha256=LdQzf2249lqmo48vHNcNnFeQCt6PL-V09iLWTiRR1bQ,33119 +sklearn/utils/tests/test_typedefs.py,sha256=gc_bm54uF15dtX5rz0Cmw4OQQhscTHACRhjdkEkMx8o,735 +sklearn/utils/tests/test_unique.py,sha256=UMMRUrDYiTzxcf49N_ddIWWyvSySFBTzrPK7JY94fGU,1820 +sklearn/utils/tests/test_user_interface.py,sha256=Pn0bUwodt-TCy7f2KdYFOXQZ-2c2BI98rhpXTpCW4uE,1772 +sklearn/utils/tests/test_validation.py,sha256=95Bo4XIy3w4fGMLK7obcs73vWMFHwVj8aGYT3Hf0qd0,80550 +sklearn/utils/tests/test_weight_vector.py,sha256=eay4_mfrN7vg2ZGoXmZ06cU9CLQYBJKMR_dK6s2Wyic,665 +sklearn/utils/validation.py,sha256=rjyXLmReLQugBFno-CrFQPCmcPR0zCk4dalfTWPwzMI,108488 diff --git a/.venv/lib/python3.12/site-packages/scikit_learn-1.7.2.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/scikit_learn-1.7.2.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/scikit_learn-1.7.2.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/scikit_learn-1.7.2.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..d98ef534f680b37433e9ab0f8470bdbe56c303d8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/scikit_learn-1.7.2.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: meson +Root-Is-Purelib: false +Tag: cp312-cp312-manylinux_2_17_x86_64 +Tag: cp312-cp312-manylinux2014_x86_64 + diff --git a/.venv/lib/python3.12/site-packages/scipy/__config__.py b/.venv/lib/python3.12/site-packages/scipy/__config__.py new file mode 100644 index 0000000000000000000000000000000000000000..0a3ef9a2e90c3bcde5abbbdfab9ebc4b66f38d23 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/scipy/__config__.py @@ -0,0 +1,161 @@ +# This file is generated by SciPy's build process +# It contains system_info results at the time of building this package. +from enum import Enum + +__all__ = ["show"] +_built_with_meson = True + + +class DisplayModes(Enum): + stdout = "stdout" + dicts = "dicts" + + +def _cleanup(d): + """ + Removes empty values in a `dict` recursively + This ensures we remove values that Meson could not provide to CONFIG + """ + if isinstance(d, dict): + return { k: _cleanup(v) for k, v in d.items() if v != '' and _cleanup(v) != '' } + else: + return d + + +CONFIG = _cleanup( + { + "Compilers": { + "c": { + "name": "gcc", + "linker": r"ld.bfd", + "version": "10.2.1", + "commands": r"cc", + "args": r"", + "linker args": r"", + }, + "cython": { + "name": r"cython", + "linker": r"cython", + "version": r"3.1.3", + "commands": r"cython", + "args": r"", + "linker args": r"", + }, + "c++": { + "name": "gcc", + "linker": r"ld.bfd", + "version": "10.2.1", + "commands": r"c++", + "args": r"", + "linker args": r"", + }, + "fortran": { + "name": "gcc", + "linker": r"ld.bfd", + "version": "10.2.1", + "commands": r"gfortran", + "args": r"", + "linker args": r"", + }, + "pythran": { + "version": r"0.18.0", + "include directory": r"../../tmp/build-env-rkdsvvju/lib/python3.12/site-packages/pythran" + }, + }, + "Machine Information": { + "host": { + "cpu": r"x86_64", + "family": r"x86_64", + "endian": r"little", + "system": r"linux", + }, + "build": { + "cpu": r"x86_64", + "family": r"x86_64", + "endian": r"little", + "system": r"linux", + }, + "cross-compiled": bool("False".lower().replace('false', '')), + }, + "Build Dependencies": { + "blas": { + "name": "scipy-openblas", + "found": bool("True".lower().replace('false', '')), + "version": "0.3.29.dev", + "detection method": "pkgconfig", + "include directory": r"/opt/_internal/cpython-3.12.11/lib/python3.12/site-packages/scipy_openblas32/include", + "lib directory": r"/opt/_internal/cpython-3.12.11/lib/python3.12/site-packages/scipy_openblas32/lib", + "openblas configuration": r"OpenBLAS 0.3.29.dev DYNAMIC_ARCH NO_AFFINITY Haswell MAX_THREADS=64", + "pc file directory": r"/project", + }, + "lapack": { + "name": "scipy-openblas", + "found": bool("True".lower().replace('false', '')), + "version": "0.3.29.dev", + "detection method": "pkgconfig", + "include directory": r"/opt/_internal/cpython-3.12.11/lib/python3.12/site-packages/scipy_openblas32/include", + "lib directory": r"/opt/_internal/cpython-3.12.11/lib/python3.12/site-packages/scipy_openblas32/lib", + "openblas configuration": r"OpenBLAS 0.3.29.dev DYNAMIC_ARCH NO_AFFINITY Haswell MAX_THREADS=64", + "pc file directory": r"/project", + }, + "pybind11": { + "name": "pybind11", + "version": "3.0.1", + "detection method": "config-tool", + "include directory": r"unknown", + }, + }, + "Python Information": { + "path": r"/tmp/build-env-rkdsvvju/bin/python", + "version": "3.12", + }, + } +) + + +def _check_pyyaml(): + import yaml + + return yaml + + +def show(mode=DisplayModes.stdout.value): + """ + Show libraries and system information on which SciPy was built + and is being used + + Parameters + ---------- + mode : {`'stdout'`, `'dicts'`}, optional. + Indicates how to display the config information. + `'stdout'` prints to console, `'dicts'` returns a dictionary + of the configuration. + + Returns + ------- + out : {`dict`, `None`} + If mode is `'dicts'`, a dict is returned, else None + + Notes + ----- + 1. The `'stdout'` mode will give more readable + output if ``pyyaml`` is installed + + """ + if mode == DisplayModes.stdout.value: + try: # Non-standard library, check import + yaml = _check_pyyaml() + + print(yaml.dump(CONFIG)) + except ModuleNotFoundError: + import warnings + import json + + warnings.warn("Install `pyyaml` for better output", stacklevel=1) + print(json.dumps(CONFIG, indent=2)) + elif mode == DisplayModes.dicts.value: + return CONFIG + else: + raise AttributeError( + f"Invalid `mode`, use one of: {', '.join([e.value for e in DisplayModes])}" + ) diff --git a/.venv/lib/python3.12/site-packages/scipy/__init__.py b/.venv/lib/python3.12/site-packages/scipy/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f18952cfb315cc94eb354a7983b4fe5ab38037cf --- /dev/null +++ b/.venv/lib/python3.12/site-packages/scipy/__init__.py @@ -0,0 +1,138 @@ +""" +SciPy: A scientific computing package for Python +================================================ + +Documentation is available in the docstrings and +online at https://docs.scipy.org/doc/scipy/ + +Subpackages +----------- +:: + + cluster --- Vector Quantization / Kmeans + constants --- Physical and mathematical constants and units + datasets --- Dataset methods + differentiate --- Finite difference differentiation tools + fft --- Discrete Fourier transforms + fftpack --- Legacy discrete Fourier transforms + integrate --- Integration routines + interpolate --- Interpolation Tools + io --- Data input and output + linalg --- Linear algebra routines + ndimage --- N-D image package + odr --- Orthogonal Distance Regression + optimize --- Optimization Tools + signal --- Signal Processing Tools + sparse --- Sparse Matrices + spatial --- Spatial data structures and algorithms + special --- Special functions + stats --- Statistical Functions + +Public API in the main SciPy namespace +-------------------------------------- +:: + + __version__ --- SciPy version string + LowLevelCallable --- Low-level callback function + show_config --- Show scipy build configuration + test --- Run scipy unittests + +""" + +import importlib as _importlib + +from numpy import __version__ as __numpy_version__ + + +try: + from scipy.__config__ import show as show_config +except ImportError as e: + msg = """Error importing SciPy: you cannot import SciPy while + being in scipy source directory; please exit the SciPy source + tree first and relaunch your Python interpreter.""" + raise ImportError(msg) from e + + +from scipy.version import version as __version__ + + +# Allow distributors to run custom init code +from . import _distributor_init +del _distributor_init + + +from scipy._lib import _pep440 +# In maintenance branch, change to np_maxversion N+3 if numpy is at N +np_minversion = '1.25.2' +np_maxversion = '2.6.0' +if (_pep440.parse(__numpy_version__) < _pep440.Version(np_minversion) or + _pep440.parse(__numpy_version__) >= _pep440.Version(np_maxversion)): + import warnings + warnings.warn(f"A NumPy version >={np_minversion} and <{np_maxversion}" + f" is required for this version of SciPy (detected " + f"version {__numpy_version__})", + UserWarning, stacklevel=2) +del _pep440 + + +# This is the first import of an extension module within SciPy. If there's +# a general issue with the install, such that extension modules are missing +# or cannot be imported, this is where we'll get a failure - so give an +# informative error message. +try: + from scipy._lib._ccallback import LowLevelCallable +except ImportError as e: + msg = "The `scipy` install you are using seems to be broken, " + \ + "(extension modules cannot be imported), " + \ + "please try reinstalling." + raise ImportError(msg) from e + + +from scipy._lib._testutils import PytestTester +test = PytestTester(__name__) +del PytestTester + + +submodules = [ + 'cluster', + 'constants', + 'datasets', + 'differentiate', + 'fft', + 'fftpack', + 'integrate', + 'interpolate', + 'io', + 'linalg', + 'ndimage', + 'odr', + 'optimize', + 'signal', + 'sparse', + 'spatial', + 'special', + 'stats' +] + +__all__ = submodules + [ + 'LowLevelCallable', + 'test', + 'show_config', + '__version__', +] + + +def __dir__(): + return __all__ + + +def __getattr__(name): + if name in submodules: + return _importlib.import_module(f'scipy.{name}') + else: + try: + return globals()[name] + except KeyError: + raise AttributeError( + f"Module 'scipy' has no attribute '{name}'" + ) diff --git a/.venv/lib/python3.12/site-packages/scipy/_distributor_init.py b/.venv/lib/python3.12/site-packages/scipy/_distributor_init.py new file mode 100644 index 0000000000000000000000000000000000000000..5df134975aa27d31beaff74c3cbfd2d3fb0a55dd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/scipy/_distributor_init.py @@ -0,0 +1,18 @@ +""" Distributor init file + +Distributors: you can replace the contents of this file with your own custom +code to support particular distributions of SciPy. + +For example, this is a good place to put any checks for hardware requirements +or BLAS/LAPACK library initialization. + +The SciPy standard source distribution will not put code in this file beyond +the try-except import of `_distributor_init_local` (which is not part of a +standard source distribution), so you can safely replace this file with your +own version. +""" + +try: + from . import _distributor_init_local # noqa: F401 +except ImportError: + pass diff --git a/.venv/lib/python3.12/site-packages/scipy/conftest.py b/.venv/lib/python3.12/site-packages/scipy/conftest.py new file mode 100644 index 0000000000000000000000000000000000000000..56bc5bd8219e1300acfce5867784b4a471e1e02b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/scipy/conftest.py @@ -0,0 +1,683 @@ +# Pytest customization +import json +import os +import warnings +import tempfile +from contextlib import contextmanager +from typing import Literal + +import numpy as np +import numpy.testing as npt +import pytest +import hypothesis + +from scipy._lib._fpumode import get_fpu_mode +from scipy._lib._array_api import ( + SCIPY_ARRAY_API, SCIPY_DEVICE, array_namespace, default_xp, + is_cupy, is_dask, is_jax, +) +from scipy._lib._testutils import FPUModeChangeWarning +from scipy._lib.array_api_extra.testing import patch_lazy_xp_functions +from scipy._lib import _pep440 + +try: + from scipy_doctest.conftest import dt_config + HAVE_SCPDT = True +except ModuleNotFoundError: + HAVE_SCPDT = False + +try: + import pytest_run_parallel # noqa:F401 + PARALLEL_RUN_AVAILABLE = True +except Exception: + PARALLEL_RUN_AVAILABLE = False + + +def pytest_configure(config): + """ + Add pytest markers to avoid PytestUnknownMarkWarning + + This needs to contain all markers that are SciPy-specific, as well as + dummy fallbacks for markers defined in optional test packages. + + Note that we need both the registration here *and* in `pytest.ini`. + """ + config.addinivalue_line("markers", + "slow: Tests that are very slow.") + config.addinivalue_line("markers", + "xslow: mark test as extremely slow (not run unless explicitly requested)") + config.addinivalue_line("markers", + "xfail_on_32bit: mark test as failing on 32-bit platforms") + config.addinivalue_line("markers", + "array_api_backends: test iterates on all array API backends") + config.addinivalue_line("markers", + ("skip_xp_backends(backends, reason=None, np_only=False, cpu_only=False, " + + "eager_only=False, exceptions=None): mark the desired skip configuration " + + "for the `skip_xp_backends` fixture")) + config.addinivalue_line("markers", + ("xfail_xp_backends(backends, reason=None, np_only=False, cpu_only=False, " + + "eager_only=False, exceptions=None): mark the desired xfail configuration " + + "for the `xfail_xp_backends` fixture")) + + try: + import pytest_timeout # noqa:F401 + except Exception: + config.addinivalue_line( + "markers", 'timeout: mark a test for a non-default timeout') + try: + # This is a more reliable test of whether pytest_fail_slow is installed + # When I uninstalled it, `import pytest_fail_slow` didn't fail! + from pytest_fail_slow import parse_duration # type: ignore[import-not-found] # noqa:F401,E501 + except Exception: + config.addinivalue_line( + "markers", 'fail_slow: mark a test for a non-default timeout failure') + + if not PARALLEL_RUN_AVAILABLE: + config.addinivalue_line( + 'markers', + 'parallel_threads(n): run the given test function in parallel ' + 'using `n` threads.') + config.addinivalue_line( + "markers", + "thread_unsafe: mark the test function as single-threaded", + ) + config.addinivalue_line( + "markers", + "iterations(n): run the given test function `n` times in each thread", + ) + + +def pytest_runtest_setup(item): + mark = item.get_closest_marker("xslow") + if mark is not None: + try: + v = int(os.environ.get('SCIPY_XSLOW', '0')) + except ValueError: + v = False + if not v: + pytest.skip("very slow test; " + "set environment variable SCIPY_XSLOW=1 to run it") + mark = item.get_closest_marker("xfail_on_32bit") + if mark is not None and np.intp(0).itemsize < 8: + pytest.xfail(f'Fails on our 32-bit test platform(s): {mark.args[0]}') + + # Older versions of threadpoolctl have an issue that may lead to this + # warning being emitted, see gh-14441 + with npt.suppress_warnings() as sup: + sup.filter(pytest.PytestUnraisableExceptionWarning) + + try: + from threadpoolctl import threadpool_limits + + HAS_THREADPOOLCTL = True + except Exception: # observed in gh-14441: (ImportError, AttributeError) + # Optional dependency only. All exceptions are caught, for robustness + HAS_THREADPOOLCTL = False + + if HAS_THREADPOOLCTL: + # Set the number of openmp threads based on the number of workers + # xdist is using to prevent oversubscription. Simplified version of what + # sklearn does (it can rely on threadpoolctl and its builtin OpenMP helper + # functions) + try: + xdist_worker_count = int(os.environ['PYTEST_XDIST_WORKER_COUNT']) + except KeyError: + # raises when pytest-xdist is not installed + return + + if not os.getenv('OMP_NUM_THREADS'): + max_openmp_threads = os.cpu_count() // 2 # use nr of physical cores + threads_per_worker = max(max_openmp_threads // xdist_worker_count, 1) + try: + threadpool_limits(threads_per_worker, user_api='blas') + except Exception: + # May raise AttributeError for older versions of OpenBLAS. + # Catch any error for robustness. + return + + +@pytest.fixture(scope="function", autouse=True) +def check_fpu_mode(request): + """ + Check FPU mode was not changed during the test. + """ + old_mode = get_fpu_mode() + yield + new_mode = get_fpu_mode() + + if old_mode != new_mode: + warnings.warn(f"FPU mode changed from {old_mode:#x} to {new_mode:#x} during " + "the test", + category=FPUModeChangeWarning, stacklevel=0) + + +if not PARALLEL_RUN_AVAILABLE: + @pytest.fixture + def num_parallel_threads(): + return 1 + + +# Array API backend handling +xp_known_backends = {'numpy', 'array_api_strict', 'torch', 'cupy', 'jax.numpy', + 'dask.array'} +xp_available_backends = {'numpy': np} +xp_skip_cpu_only_backends = set() +xp_skip_eager_only_backends = set() + +if SCIPY_ARRAY_API: + # fill the dict of backends with available libraries + try: + import array_api_strict + xp_available_backends.update({'array_api_strict': array_api_strict}) + if _pep440.parse(array_api_strict.__version__) < _pep440.Version('2.3'): + raise ImportError("array-api-strict must be >= version 2.3") + array_api_strict.set_array_api_strict_flags( + api_version='2024.12' + ) + except ImportError: + pass + + try: + import torch # type: ignore[import-not-found] + xp_available_backends.update({'torch': torch}) + torch.set_default_device(SCIPY_DEVICE) + if SCIPY_DEVICE != "cpu": + xp_skip_cpu_only_backends.add('torch') + + # default to float64 unless explicitly requested + default = os.getenv('SCIPY_DEFAULT_DTYPE', default='float64') + if default == 'float64': + torch.set_default_dtype(torch.float64) + elif default != "float32": + raise ValueError( + "SCIPY_DEFAULT_DTYPE env var, if set, can only be either 'float64' " + f"or 'float32'. Got '{default}' instead." + ) + except ImportError: + pass + + try: + import cupy # type: ignore[import-not-found] + # Note: cupy disregards SCIPY_DEVICE and always runs on cuda. + # It will fail to import if you don't have CUDA hardware and drivers. + xp_available_backends.update({'cupy': cupy}) + xp_skip_cpu_only_backends.add('cupy') + + # this is annoying in CuPy 13.x + warnings.filterwarnings( + 'ignore', 'cupyx.jit.rawkernel is experimental', category=FutureWarning + ) + from cupyx.scipy import signal + del signal + except ImportError: + pass + + try: + import jax.numpy # type: ignore[import-not-found] + xp_available_backends.update({'jax.numpy': jax.numpy}) + jax.config.update("jax_enable_x64", True) + jax.config.update("jax_default_device", jax.devices(SCIPY_DEVICE)[0]) + if SCIPY_DEVICE != "cpu": + xp_skip_cpu_only_backends.add('jax.numpy') + # JAX can be eager or lazy (when wrapped in jax.jit). However it is + # recommended by upstream devs to assume it's always lazy. + xp_skip_eager_only_backends.add('jax.numpy') + except ImportError: + pass + + try: + import dask.array as da + xp_available_backends.update({'dask.array': da}) + # Dask can wrap around cupy. However, this is untested in scipy + # (and will almost surely not work as delegation will misbehave). + + # Dask, strictly speaking, can be eager, in the sense that + # __array__, __bool__ etc. are implemented and do not raise. + # However, calling them triggers an extra computation of the whole graph + # until that point, which is highly destructive for performance. + xp_skip_eager_only_backends.add('dask.array') + except ImportError: + pass + + # by default, use all available backends + if ( + isinstance(SCIPY_ARRAY_API, str) + and SCIPY_ARRAY_API.lower() not in ("1", "true", "all") + ): + SCIPY_ARRAY_API_ = json.loads(SCIPY_ARRAY_API) + if SCIPY_ARRAY_API_ != ['all']: + # only select a subset of backend by filtering out the dict + try: + xp_available_backends = { + backend: xp_available_backends[backend] + for backend in SCIPY_ARRAY_API_ + } + except KeyError: + msg = ("'--array-api-backend' must be in " + f"{list(xp_available_backends)}; got {SCIPY_ARRAY_API_}") + raise ValueError(msg) + +assert not set(xp_available_backends) - xp_known_backends +xp_skip_np_only_backends = set(xp_available_backends) - {"numpy"} + + +@pytest.fixture(params=[ + pytest.param(v, id=k, marks=pytest.mark.array_api_backends) + for k, v in xp_available_backends.items() +]) +def xp(request, monkeypatch): + """Run the test that uses this fixture on each available array API library. + + You can select all and only the tests that use the `xp` fixture by + passing `-m array_api_backends` to pytest. + + You can select where individual tests run through the `@skip_xp_backends`, + `@xfail_xp_backends`, and `@skip_xp_invalid_arg` pytest markers. + + Please read: https://docs.scipy.org/doc/scipy/dev/api-dev/array_api.html#adding-tests + """ + # Read all @pytest.marks.skip_xp_backends markers that decorate to the test, + # if any, and raise pytest.skip() if the current xp is in the list. + skip_or_xfail_xp_backends(request, "skip") + # Read all @pytest.marks.xfail_xp_backends markers that decorate the test, + # if any, and raise pytest.xfail() if the current xp is in the list. + skip_or_xfail_xp_backends(request, "xfail") + + xp = request.param + # Potentially wrap namespace with array_api_compat + xp = array_namespace(xp.empty(0)) + + if SCIPY_ARRAY_API: + # If request.param==jax.numpy, wrap tested functions in jax.jit + patch_lazy_xp_functions( + xp=request.param, request=request, monkeypatch=monkeypatch + ) + + # Throughout all calls to assert_almost_equal, assert_array_almost_equal, and + # xp_assert_* functions, test that the array namespace is xp in both the + # expected and actual arrays. This is to detect the case where both arrays are + # erroneously just plain numpy while xp is something else. + with default_xp(xp): + yield xp + else: + yield xp + + +skip_xp_invalid_arg = pytest.mark.skipif(SCIPY_ARRAY_API, + reason = ('Test involves masked arrays, object arrays, or other types ' + 'that are not valid input when `SCIPY_ARRAY_API` is used.')) + + +def _backends_kwargs_from_request(request, skip_or_xfail): + """A helper for {skip,xfail}_xp_backends. + + Return dict of {backend to skip/xfail: top reason to skip/xfail it} + """ + markers = list(request.node.iter_markers(f'{skip_or_xfail}_xp_backends')) + reasons = {backend: [] for backend in xp_known_backends} + + for marker in markers: + invalid_kwargs = set(marker.kwargs) - { + "cpu_only", "np_only", "eager_only", "reason", "exceptions"} + if invalid_kwargs: + raise TypeError(f"Invalid kwargs: {invalid_kwargs}") + + exceptions = set(marker.kwargs.get('exceptions', [])) + invalid_exceptions = exceptions - xp_known_backends + if (invalid_exceptions := list(exceptions - xp_known_backends)): + raise ValueError(f"Unknown backend(s): {invalid_exceptions}; " + f"must be a subset of {list(xp_known_backends)}") + + if marker.kwargs.get('np_only', False): + reason = marker.kwargs.get("reason") or "do not run with non-NumPy backends" + for backend in xp_skip_np_only_backends - exceptions: + reasons[backend].append(reason) + + elif marker.kwargs.get('cpu_only', False): + reason = marker.kwargs.get("reason") or ( + "no array-agnostic implementation or delegation available " + "for this backend and device") + for backend in xp_skip_cpu_only_backends - exceptions: + reasons[backend].append(reason) + + elif marker.kwargs.get('eager_only', False): + reason = marker.kwargs.get("reason") or ( + "eager checks not executed on lazy backends") + for backend in xp_skip_eager_only_backends - exceptions: + reasons[backend].append(reason) + + # add backends, if any + if len(marker.args) == 1: + backend = marker.args[0] + if backend not in xp_known_backends: + raise ValueError(f"Unknown backend: {backend}; " + f"must be one of {list(xp_known_backends)}") + reason = marker.kwargs.get("reason") or ( + f"do not run with array API backend: {backend}") + # reason overrides the ones from cpu_only, np_only, and eager_only. + # This is regardless of order of appearence of the markers. + reasons[backend].insert(0, reason) + + for kwarg in ("cpu_only", "np_only", "eager_only", "exceptions"): + if kwarg in marker.kwargs: + raise ValueError(f"{kwarg} is mutually exclusive with {backend}") + + elif len(marker.args) > 1: + raise ValueError( + f"Please specify only one backend per marker: {marker.args}" + ) + + return {backend: backend_reasons[0] + for backend, backend_reasons in reasons.items() + if backend_reasons} + + +def skip_or_xfail_xp_backends(request: pytest.FixtureRequest, + skip_or_xfail: Literal['skip', 'xfail']) -> None: + """ + Helper of the `xp` fixture. + Skip or xfail based on the ``skip_xp_backends`` or ``xfail_xp_backends`` markers. + + See the "Support for the array API standard" docs page for usage examples. + + Usage + ----- + :: + skip_xp_backends = pytest.mark.skip_xp_backends + xfail_xp_backends = pytest.mark.xfail_xp_backends + ... + + @skip_xp_backends(backend, *, reason=None) + @skip_xp_backends(*, cpu_only=True, exceptions=(), reason=None) + @skip_xp_backends(*, eager_only=True, exceptions=(), reason=None) + @skip_xp_backends(*, np_only=True, exceptions=(), reason=None) + + @xfail_xp_backends(backend, *, reason=None) + @xfail_xp_backends(*, cpu_only=True, exceptions=(), reason=None) + @xfail_xp_backends(*, eager_only=True, exceptions=(), reason=None) + @xfail_xp_backends(*, np_only=True, exceptions=(), reason=None) + + Parameters + ---------- + backend : str, optional + Backend to skip/xfail, e.g. ``"torch"``. + Mutually exclusive with ``cpu_only``, ``eager_only``, and ``np_only``. + cpu_only : bool, optional + When ``True``, the test is skipped/xfailed on non-CPU devices, + minus exceptions. Mutually exclusive with ``backend``. + eager_only : bool, optional + When ``True``, the test is skipped/xfailed for lazy backends, e.g. those + with major caveats when invoking ``__array__``, ``__bool__``, ``__float__``, + or ``__complex__``, minus exceptions. Mutually exclusive with ``backend``. + np_only : bool, optional + When ``True``, the test is skipped/xfailed for all backends other + than the default NumPy backend and the exceptions. + Mutually exclusive with ``backend``. Implies ``cpu_only`` and ``eager_only``. + reason : str, optional + A reason for the skip/xfail. If omitted, a default reason is used. + exceptions : list[str], optional + A list of exceptions for use with ``cpu_only``, ``eager_only``, or ``np_only``. + This should be provided when delegation is implemented for some, + but not all, non-CPU/non-NumPy backends. + """ + if f"{skip_or_xfail}_xp_backends" not in request.keywords: + return + + skip_xfail_reasons = _backends_kwargs_from_request( + request, skip_or_xfail=skip_or_xfail + ) + xp = request.param + if xp.__name__ in skip_xfail_reasons: + reason = skip_xfail_reasons[xp.__name__] + assert reason # Default reason applied above + skip_or_xfail = getattr(pytest, skip_or_xfail) + skip_or_xfail(reason=reason) + + +@pytest.fixture +def devices(xp): + """Fixture that returns a list of all devices for the backend, plus None. + Used to test input->output device propagation. + + Usage + ----- + from scipy._lib._array_api import xp_device + + def test_device(xp, devices): + for d in devices: + x = xp.asarray(..., device=d) + y = f(x) + assert xp_device(y) == xp_device(x) + """ + if is_cupy(xp): + # CuPy does not support devices other than the current one + # data-apis/array-api-compat#293 + pytest.xfail(reason="data-apis/array-api-compat#293") + if is_dask(xp): + # Skip dummy DASK_DEVICE from array-api-compat, which does not propagate + return ["cpu", None] + if is_jax(xp): + # The .device attribute is not accessible inside jax.jit; the consequence + # (downstream of array-api-compat hacks) is that a non-default device in + # input is not guaranteed to propagate to the output even if the scipy code + # states `device=xp_device(arg)`` in all array creation functions. + # While this issue is specific to jax.jit, it would be unnecessarily + # verbose to skip the test for each jit-capable function and run it for + # those that only support eager mode. + pytest.xfail(reason="jax-ml/jax#26000") + + return xp.__array_namespace_info__().devices() + [None] + + +# Following the approach of NumPy's conftest.py... +# Use a known and persistent tmpdir for hypothesis' caches, which +# can be automatically cleared by the OS or user. +hypothesis.configuration.set_hypothesis_home_dir( + os.path.join(tempfile.gettempdir(), ".hypothesis") +) + +# We register two custom profiles for SciPy - for details see +# https://hypothesis.readthedocs.io/en/latest/settings.html +# The first is designed for our own CI runs; the latter also +# forces determinism and is designed for use via scipy.test() +hypothesis.settings.register_profile( + name="nondeterministic", deadline=None, print_blob=True, +) +hypothesis.settings.register_profile( + name="deterministic", + deadline=None, print_blob=True, database=None, derandomize=True, + suppress_health_check=list(hypothesis.HealthCheck), +) + +# Profile is currently set by environment variable `SCIPY_HYPOTHESIS_PROFILE` +# In the future, it would be good to work the choice into dev.py. +SCIPY_HYPOTHESIS_PROFILE = os.environ.get("SCIPY_HYPOTHESIS_PROFILE", + "deterministic") +hypothesis.settings.load_profile(SCIPY_HYPOTHESIS_PROFILE) + + +############################################################################ +# doctesting stuff + +if HAVE_SCPDT: + + # FIXME: populate the dict once + @contextmanager + def warnings_errors_and_rng(test=None): + """Temporarily turn (almost) all warnings to errors. + + Filter out known warnings which we allow. + """ + known_warnings = dict() + + # these functions are known to emit "divide by zero" RuntimeWarnings + divide_by_zero = [ + 'scipy.linalg.norm', 'scipy.ndimage.center_of_mass', + ] + for name in divide_by_zero: + known_warnings[name] = dict(category=RuntimeWarning, + message='divide by zero') + + # Deprecated stuff in scipy.signal and elsewhere + deprecated = [ + 'scipy.signal.cwt', 'scipy.signal.morlet', 'scipy.signal.morlet2', + 'scipy.signal.ricker', + 'scipy.integrate.simpson', + 'scipy.interpolate.interp2d', + 'scipy.linalg.kron', + ] + for name in deprecated: + known_warnings[name] = dict(category=DeprecationWarning) + + from scipy import integrate + # the functions are known to emit IntegrationWarnings + integration_w = ['scipy.special.ellip_normal', + 'scipy.special.ellip_harm_2', + ] + for name in integration_w: + known_warnings[name] = dict(category=integrate.IntegrationWarning, + message='The occurrence of roundoff') + + # scipy.stats deliberately emits UserWarnings sometimes + user_w = ['scipy.stats.anderson_ksamp', 'scipy.stats.kurtosistest', + 'scipy.stats.normaltest', 'scipy.sparse.linalg.norm'] + for name in user_w: + known_warnings[name] = dict(category=UserWarning) + + # additional one-off warnings to filter + dct = { + 'scipy.sparse.linalg.norm': + dict(category=UserWarning, message="Exited at iteration"), + # tutorials + 'linalg.rst': + dict(message='the matrix subclass is not', + category=PendingDeprecationWarning), + 'stats.rst': + dict(message='The maximum number of subdivisions', + category=integrate.IntegrationWarning), + } + known_warnings.update(dct) + + # these legitimately emit warnings in examples + legit = set('scipy.signal.normalize') + + # Now, the meat of the matter: filter warnings, + # also control the random seed for each doctest. + + # XXX: this matches the refguide-check behavior, but is a tad strange: + # makes sure that the seed the old-fashioned np.random* methods is + # *NOT* reproducible but the new-style `default_rng()` *IS* repoducible. + # Should these two be either both repro or both not repro? + + from scipy._lib._util import _fixed_default_rng + import numpy as np + with _fixed_default_rng(): + np.random.seed(None) + with warnings.catch_warnings(): + if test and test.name in known_warnings: + warnings.filterwarnings('ignore', + **known_warnings[test.name]) + yield + elif test and test.name in legit: + yield + else: + warnings.simplefilter('error', Warning) + yield + + dt_config.user_context_mgr = warnings_errors_and_rng + dt_config.skiplist = set([ + 'scipy.linalg.LinAlgError', # comes from numpy + 'scipy.fftpack.fftshift', # fftpack stuff is also from numpy + 'scipy.fftpack.ifftshift', + 'scipy.fftpack.fftfreq', + 'scipy.special.sinc', # sinc is from numpy + 'scipy.optimize.show_options', # does not have much to doctest + 'scipy.signal.normalize', # manipulates warnings (XXX temp skip) + 'scipy.sparse.linalg.norm', # XXX temp skip + # these below test things which inherit from np.ndarray + # cross-ref https://github.com/numpy/numpy/issues/28019 + 'scipy.io.matlab.MatlabObject.strides', + 'scipy.io.matlab.MatlabObject.dtype', + 'scipy.io.matlab.MatlabOpaque.dtype', + 'scipy.io.matlab.MatlabOpaque.strides', + 'scipy.io.matlab.MatlabFunction.strides', + 'scipy.io.matlab.MatlabFunction.dtype' + ]) + + # these are affected by NumPy 2.0 scalar repr: rely on string comparison + if np.__version__ < "2": + dt_config.skiplist.update(set([ + 'scipy.io.hb_read', + 'scipy.io.hb_write', + 'scipy.sparse.csgraph.connected_components', + 'scipy.sparse.csgraph.depth_first_order', + 'scipy.sparse.csgraph.shortest_path', + 'scipy.sparse.csgraph.floyd_warshall', + 'scipy.sparse.csgraph.dijkstra', + 'scipy.sparse.csgraph.bellman_ford', + 'scipy.sparse.csgraph.johnson', + 'scipy.sparse.csgraph.yen', + 'scipy.sparse.csgraph.breadth_first_order', + 'scipy.sparse.csgraph.reverse_cuthill_mckee', + 'scipy.sparse.csgraph.structural_rank', + 'scipy.sparse.csgraph.construct_dist_matrix', + 'scipy.sparse.csgraph.reconstruct_path', + 'scipy.ndimage.value_indices', + 'scipy.stats.mstats.describe', + ])) + + # help pytest collection a bit: these names are either private + # (distributions), or just do not need doctesting. + dt_config.pytest_extra_ignore = [ + "scipy.stats.distributions", + "scipy.optimize.cython_optimize", + "scipy.test", + "scipy.show_config", + # equivalent to "pytest --ignore=path/to/file" + "scipy/special/_precompute", + "scipy/interpolate/_interpnd_info.py", + "scipy/interpolate/_rbfinterp_pythran.py", + "scipy/_build_utils/tempita.py", + "scipy/_lib/array_api_compat", + "scipy/_lib/highs", + "scipy/_lib/unuran", + "scipy/_lib/_gcutils.py", + "scipy/_lib/doccer.py", + "scipy/_lib/_uarray", + "scipy/linalg/_cython_signature_generator.py", + "scipy/linalg/_generate_pyx.py", + "scipy/linalg/_linalg_pythran.py", + "scipy/linalg/_matfuncs_sqrtm_triu.py", + "scipy/ndimage/utils/generate_label_testvectors.py", + "scipy/optimize/_group_columns.py", + "scipy/optimize/_max_len_seq_inner.py", + "scipy/signal/_max_len_seq_inner.py", + "scipy/sparse/_generate_sparsetools.py", + "scipy/special/_generate_pyx.py", + "scipy/stats/_stats_pythran.py", + ] + + dt_config.pytest_extra_xfail = { + # name: reason + "ND_regular_grid.rst": "ReST parser limitation", + "extrapolation_examples.rst": "ReST parser limitation", + "sampling_pinv.rst": "__cinit__ unexpected argument", + "sampling_srou.rst": "nan in scalar_power", + "probability_distributions.rst": "integration warning", + } + + # tutorials + dt_config.pseudocode = set(['integrate.nquad(func,']) + dt_config.local_resources = { + 'io.rst': [ + "octave_a.mat", + "octave_cells.mat", + "octave_struct.mat" + ] + } + + dt_config.strict_check = True + + # ignore Matplotlib's `ax.text`: + dt_config.stopwords.add('.text(') +############################################################################ diff --git a/.venv/lib/python3.12/site-packages/scipy/version.py b/.venv/lib/python3.12/site-packages/scipy/version.py new file mode 100644 index 0000000000000000000000000000000000000000..27395924a3d18814ef48c8cb596e7e2e8d7b464c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/scipy/version.py @@ -0,0 +1,12 @@ + +""" +Module to expose more detailed version info for the installed `scipy` +""" +version = "1.16.2" +full_version = version +short_version = version.split('.dev')[0] +git_revision = "b1296b9b4393e251511fe8fdd3e58c22a1124899" +release = 'dev' not in version and '+' not in version + +if not release: + version = full_version diff --git a/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..411c23175893af2cf8f02f3233ee98357fba720e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/METADATA @@ -0,0 +1,328 @@ +Metadata-Version: 2.4 +Name: sentence-transformers +Version: 5.1.0.dev0 +Summary: Embeddings, Retrieval, and Reranking +Author-email: Nils Reimers , Tom Aarsen +Maintainer-email: Tom Aarsen +License: Apache 2.0 +Project-URL: Homepage, https://www.SBERT.net +Project-URL: Repository, https://github.com/UKPLab/sentence-transformers/ +Keywords: Transformer Networks,BERT,XLNet,sentence embedding,PyTorch,NLP,deep learning +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Science/Research +Classifier: License :: OSI Approved :: Apache Software License +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 :: Scientific/Engineering :: Artificial Intelligence +Requires-Python: >=3.9 +Description-Content-Type: text/markdown +License-File: LICENSE +License-File: NOTICE.txt +Requires-Dist: transformers<5.0.0,>=4.41.0 +Requires-Dist: tqdm +Requires-Dist: torch>=1.11.0 +Requires-Dist: scikit-learn +Requires-Dist: scipy +Requires-Dist: huggingface-hub>=0.20.0 +Requires-Dist: Pillow +Requires-Dist: typing_extensions>=4.5.0 +Provides-Extra: train +Requires-Dist: datasets; extra == "train" +Requires-Dist: accelerate>=0.20.3; extra == "train" +Provides-Extra: onnx +Requires-Dist: optimum[onnxruntime]>=1.23.1; extra == "onnx" +Provides-Extra: onnx-gpu +Requires-Dist: optimum[onnxruntime-gpu]>=1.23.1; extra == "onnx-gpu" +Provides-Extra: openvino +Requires-Dist: optimum-intel[openvino]>=1.20.0; extra == "openvino" +Provides-Extra: dev +Requires-Dist: datasets; extra == "dev" +Requires-Dist: accelerate>=0.20.3; extra == "dev" +Requires-Dist: pre-commit; extra == "dev" +Requires-Dist: pytest; extra == "dev" +Requires-Dist: pytest-cov; extra == "dev" +Requires-Dist: peft; extra == "dev" +Dynamic: license-file + + +[![HF Models](https://img.shields.io/badge/%F0%9F%A4%97-models-yellow)](https://huggingface.co/models?library=sentence-transformers) +[![GitHub - License](https://img.shields.io/github/license/UKPLab/sentence-transformers?logo=github&style=flat&color=green)][#github-license] +[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/sentence-transformers?logo=pypi&style=flat&color=blue)][#pypi-package] +[![PyPI - Package Version](https://img.shields.io/pypi/v/sentence-transformers?logo=pypi&style=flat&color=orange)][#pypi-package] +[![Docs - GitHub.io](https://img.shields.io/static/v1?logo=github&style=flat&color=pink&label=docs&message=sentence-transformers)][#docs-package] + + +[#github-license]: https://github.com/UKPLab/sentence-transformers/blob/master/LICENSE +[#pypi-package]: https://pypi.org/project/sentence-transformers/ +[#conda-forge-package]: https://anaconda.org/conda-forge/sentence-transformers +[#docs-package]: https://www.sbert.net/ + + +# Sentence Transformers: Embeddings, Retrieval, and Reranking + +This framework provides an easy method to compute embeddings for accessing, using, and training state-of-the-art embedding and reranker models. It can be used to compute embeddings using Sentence Transformer models ([quickstart](https://sbert.net/docs/quickstart.html#sentence-transformer)), to calculate similarity scores using Cross-Encoder (a.k.a. reranker) models ([quickstart](https://sbert.net/docs/quickstart.html#cross-encoder)) or to generate sparse embeddings using Sparse Encoder models ([quickstart](https://sbert.net/docs/quickstart.html#sparse-encoder)). This unlocks a wide range of applications, including [semantic search](https://sbert.net/examples/applications/semantic-search/README.html), [semantic textual similarity](https://sbert.net/docs/sentence_transformer/usage/semantic_textual_similarity.html), and [paraphrase mining](https://sbert.net/examples/applications/paraphrase-mining/README.html). + +A wide selection of over [15,000 pre-trained Sentence Transformers models](https://huggingface.co/models?library=sentence-transformers) are available for immediate use on 🤗 Hugging Face, including many of the state-of-the-art models from the [Massive Text Embeddings Benchmark (MTEB) leaderboard](https://huggingface.co/spaces/mteb/leaderboard). Additionally, it is easy to train or finetune your own [embedding models](https://sbert.net/docs/sentence_transformer/training_overview.html), [reranker models](https://sbert.net/docs/cross_encoder/training_overview.html) or [sparse encoder models](https://sbert.net/docs/sparse_encoder/training_overview.html) using Sentence Transformers, enabling you to create custom models for your specific use cases. + +For the **full documentation**, see **[www.SBERT.net](https://www.sbert.net)**. + +## Installation + +We recommend **Python 3.9+**, **[PyTorch 1.11.0+](https://pytorch.org/get-started/locally/)**, and **[transformers v4.34.0+](https://github.com/huggingface/transformers)**. + +**Install with pip** + +``` +pip install -U sentence-transformers +``` + +**Install with conda** + +``` +conda install -c conda-forge sentence-transformers +``` + +**Install from sources** + +Alternatively, you can also clone the latest version from the [repository](https://github.com/UKPLab/sentence-transformers) and install it directly from the source code: + +```` +pip install -e . +```` + +**PyTorch with CUDA** + +If you want to use a GPU / CUDA, you must install PyTorch with the matching CUDA Version. Follow +[PyTorch - Get Started](https://pytorch.org/get-started/locally/) for further details how to install PyTorch. + +## Getting Started + +See [Quickstart](https://www.sbert.net/docs/quickstart.html) in our documentation. + +### Embedding Models + +First download a pretrained embedding a.k.a. Sentence Transformer model. + +````python +from sentence_transformers import SentenceTransformer + +model = SentenceTransformer("all-MiniLM-L6-v2") +```` + +Then provide some texts to the model. + +````python +sentences = [ + "The weather is lovely today.", + "It's so sunny outside!", + "He drove to the stadium.", +] +embeddings = model.encode(sentences) +print(embeddings.shape) +# => (3, 384) +```` + +And that's already it. We now have numpy arrays with the embeddings, one for each text. We can use these to compute similarities. + +````python +similarities = model.similarity(embeddings, embeddings) +print(similarities) +# tensor([[1.0000, 0.6660, 0.1046], +# [0.6660, 1.0000, 0.1411], +# [0.1046, 0.1411, 1.0000]]) +```` + +### Reranker Models + +First download a pretrained reranker a.k.a. Cross Encoder model. + +```python +from sentence_transformers import CrossEncoder + +# 1. Load a pretrained CrossEncoder model +model = CrossEncoder("cross-encoder/ms-marco-MiniLM-L6-v2") +``` + +Then provide some texts to the model. + +```python +# The texts for which to predict similarity scores +query = "How many people live in Berlin?" +passages = [ + "Berlin had a population of 3,520,031 registered inhabitants in an area of 891.82 square kilometers.", + "Berlin has a yearly total of about 135 million day visitors, making it one of the most-visited cities in the European Union.", + "In 2013 around 600,000 Berliners were registered in one of the more than 2,300 sport and fitness clubs.", +] + +# 2a. predict scores for pairs of texts +scores = model.predict([(query, passage) for passage in passages]) +print(scores) +# => [8.607139 5.506266 6.352977] +``` + +And we're good to go. You can also use [`model.rank`](https://sbert.net/docs/package_reference/cross_encoder/cross_encoder.html#sentence_transformers.cross_encoder.CrossEncoder.rank) to avoid having to perform the reranking manually: + +```python +# 2b. Rank a list of passages for a query +ranks = model.rank(query, passages, return_documents=True) + +print("Query:", query) +for rank in ranks: + print(f"- #{rank['corpus_id']} ({rank['score']:.2f}): {rank['text']}") +""" +Query: How many people live in Berlin? +- #0 (8.61): Berlin had a population of 3,520,031 registered inhabitants in an area of 891.82 square kilometers. +- #2 (6.35): In 2013 around 600,000 Berliners were registered in one of the more than 2,300 sport and fitness clubs. +- #1 (5.51): Berlin has a yearly total of about 135 million day visitors, making it one of the most-visited cities in the European Union. +""" +``` +### Sparse Encoder Models + +First download a pretrained sparse embedding a.k.a. Sparse Encoder model. + +```python + +from sentence_transformers import SparseEncoder + +# 1. Load a pretrained SparseEncoder model +model = SparseEncoder("naver/splade-cocondenser-ensembledistil") + +# The sentences to encode +sentences = [ + "The weather is lovely today.", + "It's so sunny outside!", + "He drove to the stadium.", +] + +# 2. Calculate sparse embeddings by calling model.encode() +embeddings = model.encode(sentences) +print(embeddings.shape) +# [3, 30522] - sparse representation with vocabulary size dimensions + +# 3. Calculate the embedding similarities +similarities = model.similarity(embeddings, embeddings) +print(similarities) +# tensor([[ 35.629, 9.154, 0.098], +# [ 9.154, 27.478, 0.019], +# [ 0.098, 0.019, 29.553]]) + +# 4. Check sparsity stats +stats = SparseEncoder.sparsity(embeddings) +print(f"Sparsity: {stats['sparsity_ratio']:.2%}") +# Sparsity: 99.84% +``` + +## Pre-Trained Models + +We provide a large list of pretrained models for more than 100 languages. Some models are general purpose models, while others produce embeddings for specific use cases. + +* [Pretrained Sentence Transformer (Embedding) Models](https://sbert.net/docs/sentence_transformer/pretrained_models.html) +* [Pretrained Cross Encoder (Reranker) Models](https://sbert.net/docs/cross_encoder/pretrained_models.html) +* [Pretrained Sparse Encoder (Sparse Embeddings) Models](https://sbert.net/docs/sparse_encoder/pretrained_models.html) + +## Training + +This framework allows you to fine-tune your own sentence embedding methods, so that you get task-specific sentence embeddings. You have various options to choose from in order to get perfect sentence embeddings for your specific task. + +* Embedding Models + * [Sentence Transformer > Training Overview](https://www.sbert.net/docs/sentence_transformer/training_overview.html) + * [Sentence Transformer > Training Examples](https://www.sbert.net/docs/sentence_transformer/training/examples.html) or [training examples on GitHub](https://github.com/UKPLab/sentence-transformers/tree/master/examples/sentence_transformer/training). +* Reranker Models + * [Cross Encoder > Training Overview](https://www.sbert.net/docs/cross_encoder/training_overview.html) + * [Cross Encoder > Training Examples](https://www.sbert.net/docs/cross_encoder/training/examples.html) or [training examples on GitHub](https://github.com/UKPLab/sentence-transformers/tree/master/examples/cross_encoder/training). +* Sparse Embedding Models + * [Sparse Encoder > Training Overview](https://www.sbert.net/docs/sparse_encoder/training_overview.html) + * [Sparse Encoder > Training Examples](https://www.sbert.net/docs/sparse_encoder/training/examples.html) or [training examples on GitHub](https://github.com/UKPLab/sentence-transformers/tree/master/examples/sparse_encoder/training). + +Some highlights across the different types of training are: +- Support of various transformer networks including BERT, RoBERTa, XLM-R, DistilBERT, Electra, BART, ... +- Multi-Lingual and multi-task learning +- Evaluation during training to find optimal model +- [20+ loss functions](https://www.sbert.net/docs/package_reference/sentence_transformer/losses.html) for embedding models, [10+ loss functions](https://www.sbert.net/docs/package_reference/cross_encoder/losses.html) for reranker models and [10+ loss functions](https://www.sbert.net/docs/package_reference/sparse_encoder/losses.html) for sparse embedding models, allowing you to tune models specifically for semantic search, paraphrase mining, semantic similarity comparison, clustering, triplet loss, contrastive loss, etc. + +## Application Examples + +You can use this framework for: + +- **Computing Sentence Embeddings** + - [Dense Embeddings](https://www.sbert.net/examples/sentence_transformer/applications/computing-embeddings/README.html) + - [Sparse Embeddings](https://www.sbert.net/examples/sparse_encoder/applications/computing_embeddings/README.html) + +- **Semantic Textual Similarity** + - [Dense STS](https://www.sbert.net/docs/sentence_transformer/usage/semantic_textual_similarity.html) + - [Sparse STS](https://www.sbert.net/examples/sparse_encoder/applications/semantic_textual_similarity/README.html) + +- **Semantic Search** + - [Dense Search](https://www.sbert.net/examples/sentence_transformer/applications/semantic-search/README.html) + - [Sparse Search](https://www.sbert.net/examples/sparse_encoder/applications/semantic_search/README.html) + +- **Retrieve & Re-Rank** + - [Dense only Retrieval](https://www.sbert.net/examples/sentence_transformer/applications/retrieve_rerank/README.html) + - [Sparse/Dense/Hybrid Retrieval](https://www.sbert.net/examples/sentence_transformer/applications/retrieve_rerank/README.html) + +- [Clustering](https://www.sbert.net/examples/sentence_transformer/applications/clustering/README.html) +- [Paraphrase Mining](https://www.sbert.net/examples/sentence_transformer/applications/paraphrase-mining/README.html) +- [Translated Sentence Mining](https://www.sbert.net/examples/sentence_transformer/applications/parallel-sentence-mining/README.html) +- [Multilingual Image Search, Clustering & Duplicate Detection](https://www.sbert.net/examples/sentence_transformer/applications/image-search/README.html) + +and many more use-cases. + +For all examples, see [examples/sentence_transformer/applications](https://github.com/UKPLab/sentence-transformers/tree/master/examples/sentence_transformer/applications). + +## Development setup + +After cloning the repo (or a fork) to your machine, in a virtual environment, run: + +``` +python -m pip install -e ".[dev]" + +pre-commit install +``` + +To test your changes, run: + +``` +pytest +``` + +## Citing & Authors + +If you find this repository helpful, feel free to cite our publication [Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks](https://arxiv.org/abs/1908.10084): + +```bibtex +@inproceedings{reimers-2019-sentence-bert, + title = "Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks", + author = "Reimers, Nils and Gurevych, Iryna", + booktitle = "Proceedings of the 2019 Conference on Empirical Methods in Natural Language Processing", + month = "11", + year = "2019", + publisher = "Association for Computational Linguistics", + url = "https://arxiv.org/abs/1908.10084", +} +``` + +If you use one of the multilingual models, feel free to cite our publication [Making Monolingual Sentence Embeddings Multilingual using Knowledge Distillation](https://arxiv.org/abs/2004.09813): + +```bibtex +@inproceedings{reimers-2020-multilingual-sentence-bert, + title = "Making Monolingual Sentence Embeddings Multilingual using Knowledge Distillation", + author = "Reimers, Nils and Gurevych, Iryna", + booktitle = "Proceedings of the 2020 Conference on Empirical Methods in Natural Language Processing", + month = "11", + year = "2020", + publisher = "Association for Computational Linguistics", + url = "https://arxiv.org/abs/2004.09813", +} +``` + +Please have a look at [Publications](https://www.sbert.net/docs/publications.html) for our different publications that are integrated into SentenceTransformers. + +Maintainer: [Tom Aarsen](https://github.com/tomaarsen), 🤗 Hugging Face + +https://www.ukp.tu-darmstadt.de/ + +Don't hesitate to open an issue if something is broken (and it shouldn't be) or if you have further questions. + +> This repository contains experimental software and is published for the sole purpose of giving additional background details on the respective publication. diff --git a/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..cc89a1439ad430efa25e992b8bd6068368fa7a05 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/RECORD @@ -0,0 +1,12 @@ +__editable__.sentence_transformers-5.1.0.dev0.pth,sha256=Dhr1zoQuAuD80sqb92JpsfGUu-Yr49O01JIBpW74q9k,123 +__editable___sentence_transformers_5_1_0_dev0_finder.py,sha256=10Kqn6jUl62_gd7PhjBPwk7b9LBbwfS5khbTPV9yflY,3460 +sentence_transformers-5.1.0.dev0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +sentence_transformers-5.1.0.dev0.dist-info/METADATA,sha256=yfE5RU5ahCXBHraO4Ku5cqPiL_kBGBQpDJrt7nOntnU,16006 +sentence_transformers-5.1.0.dev0.dist-info/RECORD,, +sentence_transformers-5.1.0.dev0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sentence_transformers-5.1.0.dev0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91 +sentence_transformers-5.1.0.dev0.dist-info/direct_url.json,sha256=vtzHqqyODnU9zlgw8Y8_VtiUI-P1JAXuNFEXwf_KZp0,100 +sentence_transformers-5.1.0.dev0.dist-info/licenses/LICENSE,sha256=YksDD9Yri5id1QLVu2Miy8DbtSSrnJcI8y7-M9cnVzU,11338 +sentence_transformers-5.1.0.dev0.dist-info/licenses/NOTICE.txt,sha256=b2uTp6MMZfiS6jgdaPfV8ucGvzc2jpzaqOyvOvId9rA,254 +sentence_transformers-5.1.0.dev0.dist-info/top_level.txt,sha256=G9jWBWwTz-uxA1H2fuPmBn8PuLhP2SsPF-RsCYpjJ6E,22 +sentence_transformers-5.1.0.dev0.dist-info/uv_cache.json,sha256=TSop5oGdaj0fQF9pLY5M1Syrwmr8TaSqoAM-KxrDHYU,137 diff --git a/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..e7fa31b6f3f78deb1022c1f7927f07d4d16da822 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/direct_url.json b/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/direct_url.json new file mode 100644 index 0000000000000000000000000000000000000000..2c68d1688bc889c16621d24f8171940822ebbcca --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/direct_url.json @@ -0,0 +1 @@ +{"url":"file:///home/ubuntu/lyl/QwenIllustrious/sentence-transformers","dir_info":{"editable":true}} \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..470db3cc7a0102030df35d52d97a548cbd5bca18 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/top_level.txt @@ -0,0 +1 @@ +sentence_transformers diff --git a/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/uv_cache.json b/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/uv_cache.json new file mode 100644 index 0000000000000000000000000000000000000000..3eca4dbf918fdff282ae43a083cb284528d4c55b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentence_transformers-5.1.0.dev0.dist-info/uv_cache.json @@ -0,0 +1 @@ +{"timestamp":{"secs_since_epoch":1752633702,"nanos_since_epoch":106104850},"commit":null,"tags":null,"env":{},"directories":{"src":null}} \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/sentencepiece-0.2.1.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/sentencepiece-0.2.1.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentencepiece-0.2.1.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/sentencepiece-0.2.1.dist-info/METADATA b/.venv/lib/python3.12/site-packages/sentencepiece-0.2.1.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..de114be88c5daed96d52425ac9e553866395e25c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentencepiece-0.2.1.dist-info/METADATA @@ -0,0 +1,251 @@ +Metadata-Version: 2.4 +Name: sentencepiece +Version: 0.2.1 +Summary: Unsupervised text tokenizer and detokenizer. +Author-email: Taku Kudo +Project-URL: Homepage, https://github.com/google/sentencepiece +Classifier: Programming Language :: Python :: 3 +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +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.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 :: Free Threading :: 2 - Beta +Classifier: Topic :: Text Processing :: Linguistic +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.9 +Description-Content-Type: text/markdown +Provides-Extra: test +Requires-Dist: pytest; extra == "test" +Provides-Extra: testpaths +Requires-Dist: test; extra == "testpaths" + +# SentencePiece Python Wrapper + +Python wrapper for SentencePiece. This API will offer the encoding, decoding and training of Sentencepiece. + +## Build and Install SentencePiece + +For Linux (x64/i686), macOS, and Windows(win32/x64/arm64) environment, you can simply use pip command to install SentencePiece python module. + +``` +% pip install sentencepiece +``` + +Before building SentencePiece from source on Linux, ensure that the following dependencies are installed. + +``` +% sudo apt update +% sudo apt install -y cmake pkg-config libsentencepiece-dev +``` + +To build and install the Python wrapper from source, try the following commands to build and install wheel package. + +``` +% git clone https://github.com/google/sentencepiece.git +% cd sentencepiece +% mkdir build +% cd build +% cmake .. -DSPM_ENABLE_SHARED=OFF -DCMAKE_INSTALL_PREFIX=./root -DSPM_DISABLE_EMBEDDED_DATA=ON +% make install +% cd ../python +% python setup.py bdist_wheel +% pip install dist/sentencepiece*.whl +``` + +If you don’t have write permission to the global site-packages directory or don’t want to install into it, please try: + +``` +% python setup.py install --user +``` + +For Windows users who want to build from source, you can build and install the Python wrapper using Visual Studio. First, you need to install the `pwsh.exe` (Powershell 7). Use `winget install --id Microsoft.Powershell --source winget` to install directly. Then open the `Developer PowerShell for VS 2022`, and execute the following commands. + +``` +git clone https://github.com/google/sentencepiece.git +cd sentencepiece +mkdir build +cd build +cmake .. -DSPM_ENABLE_SHARED=OFF -DCMAKE_INSTALL_PREFIX=".\root" -DSPM_DISABLE_EMBEDDED_DATA=ON +cmake --build . --config Release --target install +cd ../python +pip install wheel +python setup.py bdist_wheel +Get-ChildItem .\dist\sentencepiece*.whl | ForEach-Object { pip install $_.FullName } +``` + +## Usage + +See [this google colab page](https://github.com/google/sentencepiece/blob/master/python/sentencepiece_python_module_example.ipynb) to run sentencepiece interactively. + +### Segmentation + +``` +% python +>>> import sentencepiece as spm +>>> sp = spm.SentencePieceProcessor(model_file='test/test_model.model') + +>>> sp.encode('This is a test') +[284, 47, 11, 4, 15, 400] + +>>> sp.encode(['This is a test', 'Hello world'], out_type=int) +[[284, 47, 11, 4, 15, 400], [151, 88, 21, 887]] + +>>> sp.encode_as_ids(['This is a test', 'Hello world']) +[[284, 47, 11, 4, 15, 400], [151, 88, 21, 887]] + +>>> sp.encode('This is a test', out_type=str) +['▁This', '▁is', '▁a', '▁', 't', 'est'] + +>>> sp.encode(['This is a test', 'Hello world'], out_type=str) +[['▁This', '▁is', '▁a', '▁', 't', 'est'], ['▁He', 'll', 'o', '▁world']] + +>>> sp.encode_as_pieces(['This is a test', 'Hello world']) +[['▁This', '▁is', '▁a', '▁', 't', 'est'], ['▁He', 'll', 'o', '▁world']] + +>>> proto = sp.encode('This is a test', out_type='immutable_proto') +>>> for n in proto.pieces: +... print('piece="{}" surface="{}" id={} begin={} end={}'.format(n.piece, n.surface, n.id, n.begin, n.end)) +... +piece="▁This" surface="This" id=284 begin=0 end=4 +piece="▁is" surface=" is" id=47 begin=4 end=7 +piece="▁a" surface=" a" id=11 begin=7 end=9 +piece="▁" surface=" " id=4 begin=9 end=10 +piece="t" surface="t" id=15 begin=10 end=11 +piece="est" surface="est" id=400 begin=11 end=14 + +>>> [[x.id for x in proto.pieces], [x.piece for x in proto.pieces], [x.begin for x in proto.pieces], [x.end for x in proto.pieces]] +[[284, 47, 11, 4, 15, 400], ['▁This', '▁is', '▁a', '▁', 't', 'est'], [0, 4, 7, 9, 10, 11], [4, 7, 9, 10, 11, 14]] + +>>> proto2 = sp.encode_as_immutable_proto('This is a test') +>>> proto2 == proto +True + +>>> for _ in range(10): +... sp.encode('This is a test', out_type=str, enable_sampling=True, alpha=0.1, nbest_size=-1) +... +['▁', 'This', '▁', 'is', '▁a', '▁', 't', 'e', 'st'] +['▁T', 'h', 'i', 's', '▁is', '▁a', '▁', 'te', 's', 't'] +['▁T', 'h', 'is', '▁', 'is', '▁', 'a', '▁', 't', 'est'] +['▁', 'This', '▁is', '▁', 'a', '▁', 't', 'e', 'st'] +['▁', 'This', '▁', 'is', '▁', 'a', '▁', 't', 'e', 's', 't'] +['▁This', '▁is', '▁a', '▁', 'te', 's', 't'] +['▁This', '▁is', '▁', 'a', '▁', 't', 'e', 'st'] +['▁', 'T', 'h', 'is', '▁', 'is', '▁', 'a', '▁', 'te', 'st'] +['▁', 'This', '▁', 'i', 's', '▁a', '▁', 't', 'e', 'st'] +['▁This', '▁', 'is', '▁a', '▁', 't', 'est'] + +>> sp.nbest_encode('This is a test', nbest_size=5, out_type=str) +[['▁This', '▁is', '▁a', '▁', 't', 'est'], +['▁This', '▁is', '▁a', '▁', 'te', 'st'], +['▁This', '▁is', '▁a', '▁', 'te', 's', 't'], +['▁This', '▁is', '▁a', '▁', 't', 'e', 'st'], +['▁This', '▁is', '▁a', '▁', 't', 'es', 't']] + +>>> sp.sample_encode_and_score('This is a test', num_samples=5, alpha=0.1, out_type=str, wor=True) +[(['▁This', '▁', 'i', 's', '▁a', '▁', 'te', 's', 't'], -3.043105125427246), +(['▁This', '▁', 'i', 's', '▁a', '▁', 'te', 'st'], -2.8475849628448486), +(['▁', 'This', '▁is', '▁', 'a', '▁', 'te', 'st'], -3.043248176574707), +(['▁', 'This', '▁is', '▁a', '▁', 't', 'e', 'st'], -2.87727689743042), +(['▁', 'This', '▁', 'i', 's', '▁', 'a', '▁', 't', 'est'], -3.6284031867980957)] + +>>> sp.decode([284, 47, 11, 4, 15, 400]) +'This is a test' + +>>> sp.decode([[284, 47, 11, 4, 15, 400], [151, 88, 21, 887]]) +['This is a test', 'Hello world'] + +>>> proto = sp.decode([284, 47, 11, 4, 15, 400], out_type='immutable_proto') +>>> proto.text +'This is a test' + +>>> sp.decode(['▁', 'This', '▁', 'is', '▁a', '▁', 't', 'e', 'st']) +'This is a test' + +>>> sp.decode([['▁This', '▁is', '▁a', '▁', 't', 'est'], ['▁He', 'll', 'o', '▁world']]) +['This is a test', 'Hello world'] + +>>> sp.get_piece_size() +1000 + +>>> sp.id_to_piece(2) +'' + +>>> sp.id_to_piece([2, 3, 4]) +['', '\r', '▁'] + +>>> sp.piece_to_id('') +1 + +>>> sp.piece_to_id(['', '\r', '▁']) +[2, 3, 4] + +>>> len(sp) +1000 + +>>> sp[''] +2 +``` + +### Model Training + +Training is performed by passing parameters of [spm_train](https://github.com/google/sentencepiece#train-sentencepiece-model) to SentencePieceTrainer.train() function. + +``` +>>> import sentencepiece as spm +>>> spm.SentencePieceTrainer.train(input='test/botchan.txt', model_prefix='m', vocab_size=1000, user_defined_symbols=['foo', 'bar']) +sentencepiece_trainer.cc(73) LOG(INFO) Starts training with : +trainer_spec { + input: test/botchan.txt + .. snip +unigram_model_trainer.cc(500) LOG(INFO) EM sub_iter=1 size=1188 obj=10.2839 num_tokens=32182 num_tokens/piece=27.0892 +unigram_model_trainer.cc(500) LOG(INFO) EM sub_iter=0 size=1100 obj=10.4269 num_tokens=33001 num_tokens/piece=30.0009 +unigram_model_trainer.cc(500) LOG(INFO) EM sub_iter=1 size=1100 obj=10.4069 num_tokens=33002 num_tokens/piece=30.0018 +trainer_interface.cc(595) LOG(INFO) Saving model: m.model +trainer_interface.cc(619) LOG(INFO) Saving vocabs: m.vocab +>>> +``` + +### Training without local filesystem + +Sentencepiece trainer can receive any iterable object to feed training sentences. You can also pass a file object (instance with write() method) to emit the output model to any devices. These features are useful to run sentencepiece on environment that have limited access to the local file system (e.g., Google colab.) + +``` +import urllib.request +import io +import sentencepiece as spm + +# Loads model from URL as iterator and stores the model to BytesIO. +model = io.BytesIO() +with urllib.request.urlopen( + 'https://raw.githubusercontent.com/google/sentencepiece/master/data/botchan.txt' +) as response: + spm.SentencePieceTrainer.train( + sentence_iterator=response, model_writer=model, vocab_size=1000) + +# Serialize the model as file. +# with open('out.model', 'wb') as f: +# f.write(model.getvalue()) + +# Directly load the model from serialized model. +sp = spm.SentencePieceProcessor(model_proto=model.getvalue()) +print(sp.encode('this is test')) +``` + +### Free Threading support +Experimental support for no-GIL/Free-Threading has been introduced since v0.2.1. For more details, please refer to [this page](https://py-free-threading.github.io.). +This operates similarly to how [NumPy](https://numpy.org/devdocs/reference/thread_safety.html#free-threaded-python) handles it. + +The C++ library's const and static methods, e.g., encode(), decode() and train(), are designed to work in a non-GIL environment. +However, non-const methods, e.g., load(), may have potential data race issues, so please ensure you implement appropriate locks beforehand. + +While this limitation might be removed in the future, please note that it's not a simple fix, as it would require additional shared locks in C++. diff --git a/.venv/lib/python3.12/site-packages/sentencepiece-0.2.1.dist-info/RECORD b/.venv/lib/python3.12/site-packages/sentencepiece-0.2.1.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..223abf848e938fe0b8bb73f856ee236bfaa93b1d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentencepiece-0.2.1.dist-info/RECORD @@ -0,0 +1,17 @@ +sentencepiece-0.2.1.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +sentencepiece-0.2.1.dist-info/METADATA,sha256=zgounFmc0GCnvD3_sYvlz8PV8pNTPj7k6HDq7wpVAP0,10003 +sentencepiece-0.2.1.dist-info/RECORD,, +sentencepiece-0.2.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sentencepiece-0.2.1.dist-info/WHEEL,sha256=1crAxrAH5rUbvWUY1UR0ly3o7KnT1jo0_98V8RY5-FM,152 +sentencepiece-0.2.1.dist-info/top_level.txt,sha256=NIXVKmsq-xdZ5KROMHHV0gkLA3fvbNTb1g1KtgiYFOk,130 +sentencepiece/__init__.py,sha256=NQT0kP3jkjEmRRIyK_jsKCug6L9zQi2vIPTWQin8E2w,49422 +sentencepiece/_sentencepiece.cpython-312-x86_64-linux-gnu.so,sha256=z2sCOJaqHJkCZEXvwrXL07ipfGg53Q_fsrynV_vtlw0,2005912 +sentencepiece/_version.py,sha256=PmcQ2PI2oP8irnLtJLJby2YfW6sBvLAmL-VpABzTqwc,22 +sentencepiece/package_data/nfkc.bin,sha256=UvEQKP-KffPgCdlKi2pU1KihcTLvtMzByaCkHkMr2R4,240008 +sentencepiece/package_data/nfkc_cf.bin,sha256=YIM-wRIBRGZZw1ScGDsY8CTEAHYozGs6TpGuAHaXuCY,247028 +sentencepiece/package_data/nmt_nfkc.bin,sha256=eTcsQTicK5spvBcQF6tUAONS3r1oawJnCkK-xwkBUHQ,240007 +sentencepiece/package_data/nmt_nfkc_cf.bin,sha256=IsKSx29QN5XzDIXXnTCn9XL_9PSeADktLWD0-T6UGh4,247027 +sentencepiece/sentencepiece.i,sha256=Hfv8AHFOJEfDfElVYIhoz29W7rV1VJ0Z13aP7S7ck6M,72647 +sentencepiece/sentencepiece_model_pb2.py,sha256=LawEwmdUiIU1T9HcYu-rNEVTFcwAh9i-qavMMsg9riE,6257 +sentencepiece/sentencepiece_pb2.py,sha256=_ZgnXOkpoScMXbJ-8BMKn2Q97BbMOH9Hz-L7JFMcJro,1753 +sentencepiece/sentencepiece_wrap.cxx,sha256=XlbUFs7s48i3i_nhka9U_b41Xpv_eeSD9U_uxO742Y0,381494 diff --git a/.venv/lib/python3.12/site-packages/sentencepiece-0.2.1.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/sentencepiece-0.2.1.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/sentencepiece-0.2.1.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/sentencepiece-0.2.1.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..895655c2dd100ef008b7c91dd73244160a062179 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentencepiece-0.2.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: false +Tag: cp312-cp312-manylinux_2_27_x86_64 +Tag: cp312-cp312-manylinux_2_28_x86_64 + diff --git a/.venv/lib/python3.12/site-packages/sentencepiece-0.2.1.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/sentencepiece-0.2.1.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..6a66f9441436eaa6d7b2d87d1af197d980e727b0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentencepiece-0.2.1.dist-info/top_level.txt @@ -0,0 +1,5 @@ +sentencepiece +sentencepiece/__init__ +sentencepiece/_version +sentencepiece/sentencepiece_model_pb2 +sentencepiece/sentencepiece_pb2 diff --git a/.venv/lib/python3.12/site-packages/sentencepiece/__init__.py b/.venv/lib/python3.12/site-packages/sentencepiece/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..02379dd36dad021a82d4b3a85ad1598f01184758 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentencepiece/__init__.py @@ -0,0 +1,1230 @@ +# This file was automatically generated by SWIG (https://www.swig.org). +# Version 4.3.0 +# +# Do not make changes to this file unless you know what you are doing - modify +# the SWIG interface file instead. + +from sys import version_info as _swig_python_version_info +# Import the low-level C/C++ module +if __package__ or "." in __name__: + from . import _sentencepiece +else: + import _sentencepiece + +try: + import builtins as __builtin__ +except ImportError: + import __builtin__ + +def _swig_repr(self): + try: + strthis = "proxy of " + self.this.__repr__() + except __builtin__.Exception: + strthis = "" + return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) + + +def _swig_setattr_nondynamic_instance_variable(set): + def set_instance_attr(self, name, value): + if name == "this": + set(self, name, value) + elif name == "thisown": + self.this.own(value) + elif hasattr(self, name) and isinstance(getattr(type(self), name), property): + set(self, name, value) + else: + raise AttributeError("You cannot add instance attributes to %s" % self) + return set_instance_attr + + +def _swig_setattr_nondynamic_class_variable(set): + def set_class_attr(cls, name, value): + if hasattr(cls, name) and not isinstance(getattr(cls, name), property): + set(cls, name, value) + else: + raise AttributeError("You cannot add class attributes to %s" % cls) + return set_class_attr + + +def _swig_add_metaclass(metaclass): + """Class decorator for adding a metaclass to a SWIG wrapped class - a slimmed down version of six.add_metaclass""" + def wrapper(cls): + return metaclass(cls.__name__, cls.__bases__, cls.__dict__.copy()) + return wrapper + + +class _SwigNonDynamicMeta(type): + """Meta class to enforce nondynamic attributes (no new attributes) for a class""" + __setattr__ = _swig_setattr_nondynamic_class_variable(type.__setattr__) + + +class ImmutableSentencePieceText_ImmutableSentencePiece(object): + thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag") + __repr__ = _swig_repr + + def __init__(self): + _sentencepiece.ImmutableSentencePieceText_ImmutableSentencePiece_swiginit(self, _sentencepiece.new_ImmutableSentencePieceText_ImmutableSentencePiece()) + __swig_destroy__ = _sentencepiece.delete_ImmutableSentencePieceText_ImmutableSentencePiece + + def _piece(self): + return _sentencepiece.ImmutableSentencePieceText_ImmutableSentencePiece__piece(self) + + def _surface(self): + return _sentencepiece.ImmutableSentencePieceText_ImmutableSentencePiece__surface(self) + + def _id(self): + return _sentencepiece.ImmutableSentencePieceText_ImmutableSentencePiece__id(self) + + def _begin(self): + return _sentencepiece.ImmutableSentencePieceText_ImmutableSentencePiece__begin(self) + + def _end(self): + return _sentencepiece.ImmutableSentencePieceText_ImmutableSentencePiece__end(self) + + def _surface_as_bytes(self): + return _sentencepiece.ImmutableSentencePieceText_ImmutableSentencePiece__surface_as_bytes(self) + + def _piece_as_bytes(self): + return _sentencepiece.ImmutableSentencePieceText_ImmutableSentencePiece__piece_as_bytes(self) + + piece = property(_piece) + piece_as_bytes = property(_piece_as_bytes) + surface = property(_surface) + surface_as_bytes = property(_surface_as_bytes) + id = property(_id) + begin = property(_begin) + end = property(_end) + + def __str__(self): + return ('piece: \"{}\"\n' + 'id: {}\n' + 'surface: \"{}\"\n' + 'begin: {}\n' + 'end: {}\n').format(self.piece, self.id, self.surface, + self.begin, self.end) + + def __eq__(self, other): + return self.piece == other.piece and self.id == other.id and self.surface == other.surface and self.begin == other.begin and self.end == other.end + + def __hash__(self): + return hash(str(self)) + + __repr__ = __str__ + + +# Register ImmutableSentencePieceText_ImmutableSentencePiece in _sentencepiece: +_sentencepiece.ImmutableSentencePieceText_ImmutableSentencePiece_swigregister(ImmutableSentencePieceText_ImmutableSentencePiece) +class ImmutableSentencePieceText(object): + thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag") + __repr__ = _swig_repr + + def __init__(self): + _sentencepiece.ImmutableSentencePieceText_swiginit(self, _sentencepiece.new_ImmutableSentencePieceText()) + __swig_destroy__ = _sentencepiece.delete_ImmutableSentencePieceText + + def _pieces_size(self): + return _sentencepiece.ImmutableSentencePieceText__pieces_size(self) + + def _pieces(self, index): + return _sentencepiece.ImmutableSentencePieceText__pieces(self, index) + + def _text(self): + return _sentencepiece.ImmutableSentencePieceText__text(self) + + def _score(self): + return _sentencepiece.ImmutableSentencePieceText__score(self) + + def SerializeAsString(self): + return _sentencepiece.ImmutableSentencePieceText_SerializeAsString(self) + + def _text_as_bytes(self): + return _sentencepiece.ImmutableSentencePieceText__text_as_bytes(self) + + text = property(_text) + text_as_bytes = property(_text_as_bytes) + score = property(_score) + + class ImmutableSentencePieceIterator: + def __init__(self, proto): + self.proto = proto + self.len = self.proto._pieces_size() + + def __len__(self): + return self.len + + def __getitem__(self, index): + if isinstance(index, slice): + return [self.proto._pieces(i) for i in range(self.len)][index.start:index.stop:index.step] + if index < 0: + index = index + self.len + if index < 0 or index >= self.len: + raise IndexError('piece index is out of range') + return self.proto._pieces(index) + + def __str__(self): + return '\n'.join(['pieces {{\n{}}}'.format(str(x)) for x in self]) + + __repr__ = __str__ + + @property + def pieces(self): + return ImmutableSentencePieceText.ImmutableSentencePieceIterator(self) + + def __eq__(self, other): + return self.SerializeAsString() == other.SerializeAsString() + + def __hash__(self): + return hash(self.SerializeAsString()) + + def __str__(self): + return ('text: \"{}\"\n' + 'score: {}\n' + '{}').format(self.text, self.score, + '\n'.join(['pieces {{\n{}}}'.format(str(x)) for x in self.pieces])) + + __repr__ = __str__ + + +# Register ImmutableSentencePieceText in _sentencepiece: +_sentencepiece.ImmutableSentencePieceText_swigregister(ImmutableSentencePieceText) +class ImmutableNBestSentencePieceText(object): + thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag") + __repr__ = _swig_repr + + def __init__(self): + _sentencepiece.ImmutableNBestSentencePieceText_swiginit(self, _sentencepiece.new_ImmutableNBestSentencePieceText()) + __swig_destroy__ = _sentencepiece.delete_ImmutableNBestSentencePieceText + + def _nbests_size(self): + return _sentencepiece.ImmutableNBestSentencePieceText__nbests_size(self) + + def _nbests(self, index): + return _sentencepiece.ImmutableNBestSentencePieceText__nbests(self, index) + + def SerializeAsString(self): + return _sentencepiece.ImmutableNBestSentencePieceText_SerializeAsString(self) + + class ImmutableSentencePieceTextIterator: + def __init__(self, proto): + self.proto = proto + self.len = self.proto._nbests_size() + + def __len__(self): + return self.len + + def __getitem__(self, index): + if isinstance(index, slice): + return [self.proto._nbests(i) for i in range(self.len)][index.start:index.stop:index.step] + if index < 0: + index = index + self.len + if index < 0 or index >= self.len: + raise IndexError('nbests index is out of range') + return self.proto._nbests(index) + + def __str__(self): + return '\n'.join(['nbests {{\n{}}}'.format(str(x)) for x in self]) + + __repr__ = __str__ + + @property + def nbests(self): + return ImmutableNBestSentencePieceText.ImmutableSentencePieceTextIterator(self) + + def __eq__(self, other): + return self.SerializeAsString() == other.SerializeAsString() + + def __hash__(self): + return hash(self.SerializeAsString()) + + def __str__(self): + return '\n'.join(['nbests {{\n{}}}'.format(str(x)) for x in self.nbests]) + + __repr__ = __str__ + + +# Register ImmutableNBestSentencePieceText in _sentencepiece: +_sentencepiece.ImmutableNBestSentencePieceText_swigregister(ImmutableNBestSentencePieceText) +class SentencePieceProcessor(object): + thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag") + __repr__ = _swig_repr + + def __init__(self): + _sentencepiece.SentencePieceProcessor_swiginit(self, _sentencepiece.new_SentencePieceProcessor()) + __swig_destroy__ = _sentencepiece.delete_SentencePieceProcessor + + def LoadFromSerializedProto(self, serialized): + return _sentencepiece.SentencePieceProcessor_LoadFromSerializedProto(self, serialized) + + def SetEncodeExtraOptions(self, extra_option): + return _sentencepiece.SentencePieceProcessor_SetEncodeExtraOptions(self, extra_option) + + def SetDecodeExtraOptions(self, extra_option): + return _sentencepiece.SentencePieceProcessor_SetDecodeExtraOptions(self, extra_option) + + def SetVocabulary(self, valid_vocab): + return _sentencepiece.SentencePieceProcessor_SetVocabulary(self, valid_vocab) + + def ResetVocabulary(self): + return _sentencepiece.SentencePieceProcessor_ResetVocabulary(self) + + def LoadVocabulary(self, filename, threshold): + return _sentencepiece.SentencePieceProcessor_LoadVocabulary(self, filename, threshold) + + def CalculateEntropy(self, *args): + return _sentencepiece.SentencePieceProcessor_CalculateEntropy(self, *args) + + def GetPieceSize(self): + return _sentencepiece.SentencePieceProcessor_GetPieceSize(self) + + def PieceToId(self, piece): + return _sentencepiece.SentencePieceProcessor_PieceToId(self, piece) + + def IdToPiece(self, id): + return _sentencepiece.SentencePieceProcessor_IdToPiece(self, id) + + def GetScore(self, id): + return _sentencepiece.SentencePieceProcessor_GetScore(self, id) + + def IsUnknown(self, id): + return _sentencepiece.SentencePieceProcessor_IsUnknown(self, id) + + def IsControl(self, id): + return _sentencepiece.SentencePieceProcessor_IsControl(self, id) + + def IsUnused(self, id): + return _sentencepiece.SentencePieceProcessor_IsUnused(self, id) + + def IsByte(self, id): + return _sentencepiece.SentencePieceProcessor_IsByte(self, id) + + def unk_id(self): + return _sentencepiece.SentencePieceProcessor_unk_id(self) + + def bos_id(self): + return _sentencepiece.SentencePieceProcessor_bos_id(self) + + def eos_id(self): + return _sentencepiece.SentencePieceProcessor_eos_id(self) + + def pad_id(self): + return _sentencepiece.SentencePieceProcessor_pad_id(self) + + def serialized_model_proto(self): + return _sentencepiece.SentencePieceProcessor_serialized_model_proto(self) + + def LoadFromFile(self, arg): + return _sentencepiece.SentencePieceProcessor_LoadFromFile(self, arg) + + def _EncodeAsIds(self, text, enable_sampling, nbest_size, alpha, add_bos, add_eos, reverse, emit_unk_piece): + return _sentencepiece.SentencePieceProcessor__EncodeAsIds(self, text, enable_sampling, nbest_size, alpha, add_bos, add_eos, reverse, emit_unk_piece) + + def _EncodeAsPieces(self, text, enable_sampling, nbest_size, alpha, add_bos, add_eos, reverse, emit_unk_piece): + return _sentencepiece.SentencePieceProcessor__EncodeAsPieces(self, text, enable_sampling, nbest_size, alpha, add_bos, add_eos, reverse, emit_unk_piece) + + def _EncodeAsSerializedProto(self, text, enable_sampling, nbest_size, alpha, add_bos, add_eos, reverse, emit_unk_piece): + return _sentencepiece.SentencePieceProcessor__EncodeAsSerializedProto(self, text, enable_sampling, nbest_size, alpha, add_bos, add_eos, reverse, emit_unk_piece) + + def _EncodeAsImmutableProto(self, text, enable_sampling, nbest_size, alpha, add_bos, add_eos, reverse, emit_unk_piece): + return _sentencepiece.SentencePieceProcessor__EncodeAsImmutableProto(self, text, enable_sampling, nbest_size, alpha, add_bos, add_eos, reverse, emit_unk_piece) + + def _EncodeAsIdsBatch(self, ins, num_threads, enable_sampling, nbest_size, alpha, add_bos, add_eos, reverse, emit_unk_piece): + return _sentencepiece.SentencePieceProcessor__EncodeAsIdsBatch(self, ins, num_threads, enable_sampling, nbest_size, alpha, add_bos, add_eos, reverse, emit_unk_piece) + + def _EncodeAsPiecesBatch(self, ins, num_threads, enable_sampling, nbest_size, alpha, add_bos, add_eos, reverse, emit_unk_piece): + return _sentencepiece.SentencePieceProcessor__EncodeAsPiecesBatch(self, ins, num_threads, enable_sampling, nbest_size, alpha, add_bos, add_eos, reverse, emit_unk_piece) + + def _EncodeAsSerializedProtoBatch(self, ins, num_threads, enable_sampling, nbest_size, alpha, add_bos, add_eos, reverse, emit_unk_piece): + return _sentencepiece.SentencePieceProcessor__EncodeAsSerializedProtoBatch(self, ins, num_threads, enable_sampling, nbest_size, alpha, add_bos, add_eos, reverse, emit_unk_piece) + + def _EncodeAsImmutableProtoBatch(self, ins, num_threads, enable_sampling, nbest_size, alpha, add_bos, add_eos, reverse, emit_unk_piece): + return _sentencepiece.SentencePieceProcessor__EncodeAsImmutableProtoBatch(self, ins, num_threads, enable_sampling, nbest_size, alpha, add_bos, add_eos, reverse, emit_unk_piece) + + def _DecodeIds(self, ids): + return _sentencepiece.SentencePieceProcessor__DecodeIds(self, ids) + + def _DecodeIdsAsBytes(self, ids): + return _sentencepiece.SentencePieceProcessor__DecodeIdsAsBytes(self, ids) + + def _DecodePieces(self, pieces): + return _sentencepiece.SentencePieceProcessor__DecodePieces(self, pieces) + + def _DecodeIdsAsSerializedProto(self, ids): + return _sentencepiece.SentencePieceProcessor__DecodeIdsAsSerializedProto(self, ids) + + def _DecodePiecesAsSerializedProto(self, pieces): + return _sentencepiece.SentencePieceProcessor__DecodePiecesAsSerializedProto(self, pieces) + + def _DecodeIdsAsImmutableProto(self, ids): + return _sentencepiece.SentencePieceProcessor__DecodeIdsAsImmutableProto(self, ids) + + def _DecodePiecesAsImmutableProto(self, pieces): + return _sentencepiece.SentencePieceProcessor__DecodePiecesAsImmutableProto(self, pieces) + + def _DecodeIdsBatch(self, ins, num_threads): + return _sentencepiece.SentencePieceProcessor__DecodeIdsBatch(self, ins, num_threads) + + def _DecodeIdsAsBytesBatch(self, ins, num_threads): + return _sentencepiece.SentencePieceProcessor__DecodeIdsAsBytesBatch(self, ins, num_threads) + + def _DecodeIdsAsSerializedProtoBatch(self, ins, num_threads): + return _sentencepiece.SentencePieceProcessor__DecodeIdsAsSerializedProtoBatch(self, ins, num_threads) + + def _DecodeIdsAsImmutableProtoBatch(self, ins, num_threads): + return _sentencepiece.SentencePieceProcessor__DecodeIdsAsImmutableProtoBatch(self, ins, num_threads) + + def _DecodePiecesBatch(self, ins, num_threads): + return _sentencepiece.SentencePieceProcessor__DecodePiecesBatch(self, ins, num_threads) + + def _DecodePiecesAsSerializedProtoBatch(self, ins, num_threads): + return _sentencepiece.SentencePieceProcessor__DecodePiecesAsSerializedProtoBatch(self, ins, num_threads) + + def _DecodePiecesAsImmutableProtoBatch(self, ins, num_threads): + return _sentencepiece.SentencePieceProcessor__DecodePiecesAsImmutableProtoBatch(self, ins, num_threads) + + def _NBestEncodeAsIds(self, text, nbest_size, add_bos, add_eos, reverse, emit_unk_piece): + return _sentencepiece.SentencePieceProcessor__NBestEncodeAsIds(self, text, nbest_size, add_bos, add_eos, reverse, emit_unk_piece) + + def _NBestEncodeAsPieces(self, text, nbest_size, add_bos, add_eos, reverse, emit_unk_piece): + return _sentencepiece.SentencePieceProcessor__NBestEncodeAsPieces(self, text, nbest_size, add_bos, add_eos, reverse, emit_unk_piece) + + def _NBestEncodeAsSerializedProto(self, text, nbest_size, add_bos, add_eos, reverse, emit_unk_piece): + return _sentencepiece.SentencePieceProcessor__NBestEncodeAsSerializedProto(self, text, nbest_size, add_bos, add_eos, reverse, emit_unk_piece) + + def _NBestEncodeAsImmutableProto(self, text, nbest_size, add_bos, add_eos, reverse, emit_unk_piece): + return _sentencepiece.SentencePieceProcessor__NBestEncodeAsImmutableProto(self, text, nbest_size, add_bos, add_eos, reverse, emit_unk_piece) + + def _SampleEncodeAndScoreAsIds(self, text, num_samples, alpha, wor, include_best, add_bos, add_eos, reverse, emit_unk_piece): + return _sentencepiece.SentencePieceProcessor__SampleEncodeAndScoreAsIds(self, text, num_samples, alpha, wor, include_best, add_bos, add_eos, reverse, emit_unk_piece) + + def _SampleEncodeAndScoreAsPieces(self, text, num_samples, alpha, wor, include_best, add_bos, add_eos, reverse, emit_unk_piece): + return _sentencepiece.SentencePieceProcessor__SampleEncodeAndScoreAsPieces(self, text, num_samples, alpha, wor, include_best, add_bos, add_eos, reverse, emit_unk_piece) + + def _SampleEncodeAndScoreAsSerializedProto(self, text, num_samples, alpha, wor, include_best, add_bos, add_eos, reverse, emit_unk_piece): + return _sentencepiece.SentencePieceProcessor__SampleEncodeAndScoreAsSerializedProto(self, text, num_samples, alpha, wor, include_best, add_bos, add_eos, reverse, emit_unk_piece) + + def _SampleEncodeAndScoreAsImmutableProto(self, text, num_samples, alpha, wor, include_best, add_bos, add_eos, reverse, emit_unk_piece): + return _sentencepiece.SentencePieceProcessor__SampleEncodeAndScoreAsImmutableProto(self, text, num_samples, alpha, wor, include_best, add_bos, add_eos, reverse, emit_unk_piece) + + def _Normalize(self, text): + return _sentencepiece.SentencePieceProcessor__Normalize(self, text) + + def _NormalizeWithOffsets(self, text): + return _sentencepiece.SentencePieceProcessor__NormalizeWithOffsets(self, text) + + def _CalculateEntropy(self, text, alpha): + return _sentencepiece.SentencePieceProcessor__CalculateEntropy(self, text, alpha) + + def _CalculateEntropyBatch(self, ins, alpha, num_threads): + return _sentencepiece.SentencePieceProcessor__CalculateEntropyBatch(self, ins, alpha, num_threads) + + def _OverrideNormalizerSpec(self, args): + return _sentencepiece.SentencePieceProcessor__OverrideNormalizerSpec(self, args) + + def Init(self, + model_file=None, + model_proto=None, + out_type=int, + add_bos=False, + add_eos=False, + reverse=False, + emit_unk_piece=False, + enable_sampling=False, + nbest_size=-1, + alpha=0.1, + num_threads=-1): + """Initialzie sentencepieceProcessor. + + Args: + model_file: The sentencepiece model file path. + model_proto: The sentencepiece model serialized proto. + out_type: output type. int or str. + add_bos: Add to the result (Default = false) + add_eos: Add to the result (Default = false) / is added after + reversing (if enabled). + reverse: Reverses the tokenized sequence (Default = false) + emit_unk_piece: Emits the unk literal string (Default = false) + nbest_size: sampling parameters for unigram. Invalid in BPE-Dropout. + nbest_size = {0,1}: No sampling is performed. + nbest_size > 1: samples from the nbest_size results. + nbest_size < 0: assuming that nbest_size is infinite and samples + from the all hypothesis (lattice) using + forward-filtering-and-backward-sampling algorithm. + alpha: Soothing parameter for unigram sampling, and dropout probability of + merge operations for BPE-dropout. + num_threads: number of threads in batch processing (Default = -1, auto-detected) + """ + + _sentencepiece_processor_init_native(self) + self._out_type = out_type + self._add_bos = add_bos + self._add_eos = add_eos + self._reverse = reverse + self._emit_unk_piece = emit_unk_piece + self._enable_sampling = enable_sampling + self._nbest_size = nbest_size + self._alpha = alpha + self._num_threads = num_threads + if model_file or model_proto: + self.Load(model_file=model_file, model_proto=model_proto) + + + def Encode(self, + input, + out_type=None, + add_bos=None, + add_eos=None, + reverse=None, + emit_unk_piece=None, + enable_sampling=None, + nbest_size=None, + alpha=None, + num_threads=None): + """Encode text input to segmented ids or tokens. + + Args: + input: input string. accepsts list of string. + out_type: output type. int or str. + add_bos: Add to the result (Default = false) + add_eos: Add to the result (Default = false) / is added after + reversing (if enabled). + reverse: Reverses the tokenized sequence (Default = false) + emit_unk_piece: Emits the unk literal string (Default = false) + nbest_size: sampling parameters for unigram. Invalid in BPE-Dropout. + nbest_size = {0,1}: No sampling is performed. + nbest_size > 1: samples from the nbest_size results. + nbest_size < 0: assuming that nbest_size is infinite and samples + from the all hypothesis (lattice) using + forward-filtering-and-backward-sampling algorithm. + alpha: Soothing parameter for unigram sampling, and merge probability for + BPE-dropout (probablity 'p' in BPE-dropout paper). + num_threads: the number of threads used in the batch processing (Default = -1). + """ + + if out_type is None: + out_type = self._out_type + if add_bos is None: + add_bos = self._add_bos + if add_eos is None: + add_eos = self._add_eos + if reverse is None: + reverse = self._reverse + if emit_unk_piece is None: + emit_unk_piece = self._emit_unk_piece + if enable_sampling is None: + enable_sampling = self._enable_sampling + if nbest_size is None: + nbest_size = self._nbest_size + if alpha is None: + alpha = self._alpha + if num_threads is None: + num_threads = self._num_threads + + if enable_sampling == True and (nbest_size is None or nbest_size == 0 or + nbest_size == 1 or alpha is None): + raise RuntimeError( + 'When enable_sampling is True, We must specify "nbest_size > 1" or "nbest_size = -1", ' + 'and "alpha". "nbest_size" is enabled only on unigram mode ignored in BPE-dropout. ' + 'when "nbest_size = -1" , this method samples from all candidates on the lattice ' + 'instead of nbest segmentations.' + ) + + if num_threads is None or type(num_threads) is not int: + raise RuntimeError('num_threads must be int') + + if type(input) is list: + if out_type is int: + return self._EncodeAsIdsBatch(input, num_threads, enable_sampling, nbest_size, + alpha, add_bos, add_eos, reverse, emit_unk_piece) + if out_type is str: + return self._EncodeAsPiecesBatch(input, num_threads, enable_sampling, nbest_size, + alpha, add_bos, add_eos, reverse, emit_unk_piece) + if out_type == 'serialized_proto' or out_type == 'proto': + return self._EncodeAsSerializedProtoBatch(input, num_threads, enable_sampling, nbest_size, + alpha, add_bos, add_eos, reverse, emit_unk_piece) + if out_type == 'immutable_proto': + return self._EncodeAsImmutableProtoBatch(input, num_threads, enable_sampling, nbest_size, + alpha, add_bos, add_eos, reverse, emit_unk_piece) + + if out_type is int: + return self._EncodeAsIds(input, enable_sampling, nbest_size, + alpha, add_bos, add_eos, reverse, emit_unk_piece) + if out_type is str: + return self._EncodeAsPieces(input, enable_sampling, nbest_size, + alpha, add_bos, add_eos, reverse, emit_unk_piece) + if out_type == 'serialized_proto' or out_type == 'proto': + return self._EncodeAsSerializedProto(input, enable_sampling, nbest_size, + alpha, add_bos, add_eos, reverse, emit_unk_piece) + if out_type == 'immutable_proto': + return self._EncodeAsImmutableProto(input, enable_sampling, nbest_size, + alpha, add_bos, add_eos, reverse, emit_unk_piece) + + raise RuntimeError('unknown out_type={}'.format(out_type)) + return None + + + def EncodeAsPieces(self, input, **kwargs): + return self.Encode(input=input, out_type=str, **kwargs) + + + def EncodeAsIds(self, input, **kwargs): + return self.Encode(input=input, out_type=int, **kwargs) + + + def EncodeAsSerializedProto(self, input, **kwargs): + return self.Encode(input=input, out_type='serialized_proto', **kwargs) + + + def EncodeAsImmutableProto(self, input, **kwargs): + return self.Encode(input=input, out_type='immutable_proto', **kwargs) + + + def SampleEncodeAsPieces(self, input, nbest_size=None, alpha=None, **kwargs): + return self.Encode(input=input, nbest_size=nbest_size, alpha=alpha, + out_type=str, enable_sampling=True, **kwargs) + + + def SampleEncodeAsIds(self, input, nbest_size=None, alpha=None,**kwargs): + return self.Encode(input=input, nbest_size=nbest_size, alpha=alpha, + out_type=int, enable_sampling=True, **kwargs) + + + def SampleEncodeAsSerializedProto(self, input, nbest_size=None, alpha=None, **kwargs): + return self.Encode(input=input, nbest_size=nbest_size, alpha=alpha, + out_type='serialized_proto', enable_sampling=True, **kwargs) + + + def SampleEncodeAsImmutableProto(self, input, nbest_size=None, alpha=None, **kwargs): + return self.Encode(input=input, nbest_size=nbest_size, alpha=alpha, + out_type='immutable_proto', enable_sampling=True, **kwargs) + + + def NBestEncode(self, + input, + out_type=None, + add_bos=None, + add_eos=None, + reverse=None, + emit_unk_piece=None, + nbest_size=None): + """NBestEncode text input to segmented ids or tokens. + + Args: + input: input string. accepsts list of string. + out_type: output type. int or str. + add_bos: Add to the result (Default = false) + add_eos: Add to the result (Default = false) / is added after reversing (if enabled). + reverse: Reverses the tokenized sequence (Default = false) + emit_unk_piece: Emits the unk literal string (Default = false) + nbest_size: nbest size + """ + + if out_type is None: + out_type = self._out_type + if add_bos is None: + add_bos = self._add_bos + if add_eos is None: + add_eos = self._add_eos + if reverse is None: + reverse = self._reverse + if emit_unk_piece is None: + emit_unk_piece = self._emit_unk_piece + if nbest_size is None: + nbest_size = self._nbest_size + + if nbest_size <= 0: + nbest_size=1 + + def _encode(text): + if out_type is int: + return self._NBestEncodeAsIds(text, nbest_size, + add_bos, add_eos, reverse, emit_unk_piece) + if out_type is str: + return self._NBestEncodeAsPieces(text, nbest_size, + add_bos, add_eos, reverse, emit_unk_piece) + if out_type == 'serialized_proto' or out_type == 'proto': + return self._NBestEncodeAsSerializedProto(text, nbest_size, + add_bos, add_eos, reverse, emit_unk_piece) + if out_type == 'immutable_proto': + return self._NBestEncodeAsImmutableProto(text, nbest_size, + add_bos, add_eos, reverse, emit_unk_piece) + + raise RuntimeError('unknown out_type') + + if type(input) is list: + return [_encode(n) for n in input] + + return _encode(input) + + + def NBestEncodeAsPieces(self, input, nbest_size=None, **kwargs): + return self.NBestEncode(input=input, nbest_size=nbest_size, + out_type=str, **kwargs) + + + def NBestEncodeAsIds(self, input, nbest_size=None, **kwargs): + return self.NBestEncode(input=input, nbest_size=nbest_size, + out_type=int, **kwargs) + + + def NBestEncodeAsSerializedProto(self, input, nbest_size=None, **kwargs): + return self.NBestEncode(input=input, nbest_size=nbest_size, + out_type='serialized_proto', **kwargs) + + + def NBestEncodeAsImmutableProto(self, input, nbest_size=None, **kwargs): + return self.NBestEncode(input=input, nbest_size=nbest_size, + out_type='immutable_proto', **kwargs) + + + def SampleEncodeAndScore(self, + input, + out_type=None, + add_bos=None, + add_eos=None, + reverse=None, + emit_unk_piece=None, + num_samples=None, + alpha=None, + wor=None, + include_best=None): + """SampleEncodeAndScore text input to segmented ids or tokens. + + Args: + input: input string. accepsts list of string. + out_type: output type. int or str or 'serialized_proto' or 'immutable_proto' + add_bos: Add to the result (Default = false) + add_eos: Add to the result (Default = false) / is added after reversing (if enabled). + reverse: Reverses the tokenized sequence (Default = false) + emit_unk_piece: Emits the unk literal string (Default = false) + num_samples: How many samples to return (Default = 1) + alpha: inverse temperature for sampling + wor: whether to sample without replacement (Default = false) + include_best: whether to include the best tokenization, requires wor=True (Default = false) + """ + + if out_type is None: + out_type = self._out_type + if add_bos is None: + add_bos = self._add_bos + if add_eos is None: + add_eos = self._add_eos + if reverse is None: + reverse = self._reverse + if emit_unk_piece is None: + emit_unk_piece = self._emit_unk_piece + if num_samples is None: + num_samples = 1 + if alpha is None: + alpha = 1. + if wor is None: + wor = False + if include_best is None: + include_best = False + + if num_samples <= 0: + raise RuntimeError('num_examples must be positive') + + if include_best and not wor: + raise RuntimeError('When include_best is True, We must specify "wor = True".') + + + def _encode(text): + if out_type is int: + return self._SampleEncodeAndScoreAsIds(text, num_samples, alpha, wor, include_best, + add_bos, add_eos, reverse, emit_unk_piece) + if out_type is str: + return self._SampleEncodeAndScoreAsPieces(text, num_samples, alpha, wor, include_best, + add_bos, add_eos, reverse, emit_unk_piece) + + if out_type == 'serialized_proto' or out_type == 'proto': + return self._SampleEncodeAndScoreAsSerializedProto(text, num_samples, alpha, wor, include_best, + add_bos, add_eos, reverse, emit_unk_piece) + + if out_type == 'immutable_proto': + return self._SampleEncodeAndScoreAsImmutableProto(text, num_samples, alpha, wor, include_best, + add_bos, add_eos, reverse, emit_unk_piece) + + raise RuntimeError('unknown output type') + + + if type(input) is list: + return [_encode(n) for n in input] + + return _encode(input) + + + def SampleEncodeAndScoreAsPieces(self, input, num_samples=None, alpha=None, **kwargs): + return self.SampleEncodeAndScore(input=input, num_samples=num_samples, alpha=alpha, + out_type=str, **kwargs) + + + def SampleEncodeAndScoreAsIds(self, input, num_samples=None, alpha=None, **kwargs): + return self.SampleEncodeAndScore(input=input, num_samples=num_samples, alpha=alpha, + out_type=int, **kwargs) + + + def SampleEncodeAndScoreAsSerializedProto(self, input, num_samples=None, alpha=None, **kwargs): + return self.SampleEncodeAndScore(input=input, num_samples=num_samples, alpha=alpha, + out_type='serialized_proto', **kwargs) + + + def SampleEncodeAndScoreAsImmutableProto(self, input, num_samples=None, alpha=None, **kwargs): + return self.SampleEncodeAndScore(input=input, num_samples=num_samples, alpha=alpha, + out_type='immutable_proto', **kwargs) + + + def Decode(self, input, out_type=str, num_threads=None): + """Decode processed id or token sequences. + + Args: + out_type: output type. str, bytes or 'serialized_proto' or 'immutable_proto' (Default = str) + num_threads: the number of threads used in the batch processing (Default = -1). + """ + + if num_threads is None: + num_threads = self._num_threads + + if num_threads is None or type(num_threads) is not int: + raise RuntimeError('num_threads must be int') + + if not input: + return '' + + if out_type is str: + if type(input) is int: + return self._DecodeIds([input]) + if type(input) is str: + return self._DecodePieces([input]) + + if type(input) is list: + if len(input) == 0 or type(input[0]) is int: + return self._DecodeIds(input) + if type(input[0]) is str: + return self._DecodePieces(input) + + if type(input[0]) is list: + if len(input[0]) == 0 or type(input[0][0]) is int: + return self._DecodeIdsBatch(input, num_threads) + if type(input[0][0]) is str: + return self._DecodePiecesBatch(input, num_threads) + + if out_type is bytes: + if type(input) is int: + return self._DecodeIdsAsBytes([input]) + if type(input) is str: + return self._DecodePieces([input]) + + if type(input) is list: + if len(input) == 0 or type(input[0]) is int: + return self._DecodeIdsAsBytes(input) + if type(input[0]) is str: + return self._DecodePieces(input) + + if type(input[0]) is list: + if len(input[0]) == 0 or type(input[0][0]) is int: + return self._DecodeIdsAsBytesBatch(input, num_threads) + if type(input[0][0]) is str: + return self._DecodePiecesBatch(input, num_threads) + + if out_type == 'serialized_proto': + if type(input) is int: + return self._DecodeIdsAsSerializedProto([input]) + if type(input) is str: + return self._DecodePiecesAsSerializedProto([input]) + + if type(input) is list: + if len(input) == 0 or type(input[0]) is int: + return self._DecodeIdsAsSerializedProto(input) + if type(input[0]) is str: + return self._DecodePiecesAsSerializedProto(input) + + if type(input[0]) is list: + if len(input[0]) == 0 or type(input[0][0]) is int: + return self._DecodeIdsAsSerializedProtoBatch(input, num_threads) + if type(input[0][0]) is str: + return self._DecodePiecesAsSerializedProtoBatch(input, num_threads) + + + if out_type == 'immutable_proto': + if type(input) is int: + return self._DecodeIdsAsImmutableProto([input]) + if type(input) is str: + return self._DecodePiecesAsImmutableProto([input]) + + if type(input) is list: + if len(input) == 0 or type(input[0]) is int: + return self._DecodeIdsAsImmutableProto(input) + if type(input[0]) is str: + return self._DecodePiecesAsImmutableProto(input) + + if type(input[0]) is list: + if len(input[0]) == 0 or type(input[0][0]) is int: + return self._DecodeIdsAsImmutableProtoBatch(input, num_threads) + if type(input[0][0]) is str: + return self._DecodePiecesAsImmutableProtoBatch(input, num_threads) + + + raise RuntimeError('unknown output or input type') + return None + + + def DecodePieces(self, input, out_type=str, **kwargs): + return self.Decode(input=input, out_type=out_type, **kwargs) + + + def DecodeIds(self, input, out_type=str, **kwargs): + return self.Decode(input=input, out_type=out_type, **kwargs) + + + def DecodePiecesAsSerializedProto(self, input, out_type='serialized_proto', **kwargs): + return self.Decode(input=input, out_type=out_type, **kwargs) + + + def DecodeIdsAsSerializedProto(self, input, out_type='serialized_proto', **kwargs): + return self.Decode(input=input, out_type=out_type, **kwargs) + + + def DecodePiecesAsImmutableProto(self, input, out_type='immutable_proto', **kwargs): + return self.Decode(input=input, out_type=out_type, **kwargs) + + + def DecodeIdsAsImmutableProto(self, input, out_type='immutable_proto', **kwargs): + return self.Decode(input=input, out_type=out_type, **kwargs) + + + def CalculateEntropy(self, input, alpha, num_threads=None): + """Calculate sentence entropy""" + if type(input) is list: + if num_threads is None: + num_threads = self._num_threads + if num_threads is None or type(num_threads) is not int: + raise RuntimeError('num_threads must be int') + return self._CalculateEntropyBatch(input, alpha, num_threads) + + return self._CalculateEntropy(input, alpha) + + + def Normalize(self, input, with_offsets=None): + def _normalize(text): + if with_offsets: + return self._NormalizeWithOffsets(text) + return self._Normalize(text) + + if type(input) is list: + return [_normalize(x) for x in input] + return _normalize(input) + + def OverrideNormalizerSpec(self, **kwargs): + new_kwargs = {} + for key, value in kwargs.items(): + new_kwargs[key] = str(value) + return self._OverrideNormalizerSpec(new_kwargs) + + + def piece_size(self): + return self.GetPieceSize() + + + def vocab_size(self): + return self.GetPieceSize() + + + def __getstate__(self): + return self.serialized_model_proto() + + + def __setstate__(self, serialized_model_proto): + self.__init__() + self.LoadFromSerializedProto(serialized_model_proto) + + + def __len__(self): + return self.GetPieceSize() + + + def __getitem__(self, piece): + return self.PieceToId(piece) + + + def Load(self, model_file=None, model_proto=None): + """Overwride SentencePieceProcessor.Load to support both model_file and model_proto. + + Args: + model_file: The sentencepiece model file path. + model_proto: The sentencepiece model serialized proto. Either `model_file` + or `model_proto` must be set. + """ + if model_file and model_proto: + raise RuntimeError('model_file and model_proto must be exclusive.') + if model_proto: + return self.LoadFromSerializedProto(model_proto) + return self.LoadFromFile(model_file) + + +# Register SentencePieceProcessor in _sentencepiece: +_sentencepiece.SentencePieceProcessor_swigregister(SentencePieceProcessor) + +def SetRandomGeneratorSeed(seed): + return _sentencepiece.SetRandomGeneratorSeed(seed) + +def SetMinLogLevel(v): + return _sentencepiece.SetMinLogLevel(v) +class SentencePieceTrainer(object): + thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag") + + def __init__(self, *args, **kwargs): + raise AttributeError("No constructor defined") + __repr__ = _swig_repr + + @staticmethod + def _TrainFromString(arg): + return _sentencepiece.SentencePieceTrainer__TrainFromString(arg) + + @staticmethod + def _TrainFromMap(args): + return _sentencepiece.SentencePieceTrainer__TrainFromMap(args) + + @staticmethod + def _TrainFromMap2(args, iter): + return _sentencepiece.SentencePieceTrainer__TrainFromMap2(args, iter) + + @staticmethod + def _TrainFromMap3(args): + return _sentencepiece.SentencePieceTrainer__TrainFromMap3(args) + + @staticmethod + def _TrainFromMap4(args, iter): + return _sentencepiece.SentencePieceTrainer__TrainFromMap4(args, iter) + + @staticmethod + def _Train(arg=None, **kwargs): + """Train Sentencepiece model. Accept both kwargs and legacy string arg.""" + if arg is not None and type(arg) is str: + return SentencePieceTrainer._TrainFromString(arg) + + def _encode(value): + """Encode value to CSV..""" + if type(value) is list: + if sys.version_info[0] == 3: + f = StringIO() + else: + f = BytesIO() + writer = csv.writer(f, lineterminator='') + writer.writerow([str(v) for v in value]) + return f.getvalue() + else: + return str(value) + + sentence_iterator = None + model_writer = None + new_kwargs = {} + for key, value in kwargs.items(): + if key in ['sentence_iterator', 'sentence_reader']: + sentence_iterator = value + elif key in ['model_writer']: + model_writer = value + else: + new_kwargs[key] = _encode(value) + + if model_writer: + if sentence_iterator: + model_proto = SentencePieceTrainer._TrainFromMap4(new_kwargs, + sentence_iterator) + else: + model_proto = SentencePieceTrainer._TrainFromMap3(new_kwargs) + model_writer.write(model_proto) + else: + if sentence_iterator: + return SentencePieceTrainer._TrainFromMap2(new_kwargs, sentence_iterator) + else: + return SentencePieceTrainer._TrainFromMap(new_kwargs) + + return None + + @staticmethod + def Train(arg=None, logstream=None, **kwargs): + with _LogStream(ostream=logstream): + SentencePieceTrainer._Train(arg=arg, **kwargs) + + +# Register SentencePieceTrainer in _sentencepiece: +_sentencepiece.SentencePieceTrainer_swigregister(SentencePieceTrainer) +class SentencePieceNormalizer(object): + thisown = property(lambda x: x.this.own(), lambda x, v: x.this.own(v), doc="The membership flag") + __repr__ = _swig_repr + + def __init__(self): + _sentencepiece.SentencePieceNormalizer_swiginit(self, _sentencepiece.new_SentencePieceNormalizer()) + __swig_destroy__ = _sentencepiece.delete_SentencePieceNormalizer + + def LoadFromSerializedProto(self, serialized): + return _sentencepiece.SentencePieceNormalizer_LoadFromSerializedProto(self, serialized) + + def LoadFromRuleTSV(self, filename): + return _sentencepiece.SentencePieceNormalizer_LoadFromRuleTSV(self, filename) + + def LoadFromRuleName(self, name): + return _sentencepiece.SentencePieceNormalizer_LoadFromRuleName(self, name) + + def serialized_model_proto(self): + return _sentencepiece.SentencePieceNormalizer_serialized_model_proto(self) + + def LoadFromFile(self, arg): + return _sentencepiece.SentencePieceNormalizer_LoadFromFile(self, arg) + + def _Normalize(self, text): + return _sentencepiece.SentencePieceNormalizer__Normalize(self, text) + + def _NormalizeWithOffsets(self, text): + return _sentencepiece.SentencePieceNormalizer__NormalizeWithOffsets(self, text) + + def _SetProtoField(self, name, value): + return _sentencepiece.SentencePieceNormalizer__SetProtoField(self, name, value) + + def Init(self, + model_file=None, + model_proto=None, + rule_tsv=None, + rule_name=None, + add_dummy_prefix=False, + escape_whitespaces=False, + remove_extra_whitespaces=False): + """Initialzie sentencePieceNormalizer. + + Args: + model_file: The sentencepiece model file path. + model_proto: The sentencepiece model serialized proto. + rule_tsv: The normalization rule file in TSV format. + rule_name: Pre-defined normalization name. + add_dummy_prefix: add dummy prefix. + escape_whitespaces: escape whitespaces. + remove_extra_whitespaces: remove extra whitespaces. + """ + + _sentencepiece_normalizer_init_native(self) + + if model_file: + status = self.LoadFromFile(model_file) + elif model_proto: + status = self.LoadFromSerializedProto(model_proto) + elif rule_tsv: + status = self.LoadFromRuleTSV(rule_tsv) + elif rule_name: + status = self.LoadFromRuleName(rule_name) + else: + raise RuntimeError('no model is specified') + + if status: + self._SetProtoField('add_dummy_prefix', add_dummy_prefix) + self._SetProtoField('escape_whitespaces', escape_whitespaces) + self._SetProtoField('remove_extra_whitespaces', remove_extra_whitespaces) + + def Normalize(self, input, with_offsets=None): + def _normalize(text): + if with_offsets: + return self._NormalizeWithOffsets(text) + return self._Normalize(text) + + if type(input) is list: + return [_normalize(x) for x in input] + return _normalize(input) + + + def __getstate__(self): + return self.serialized_model_proto() + + + def __setstate__(self, serialized_model_proto): + self.__init__() + self.LoadFromSerializedProto(serialized_model_proto) + + +# Register SentencePieceNormalizer in _sentencepiece: +_sentencepiece.SentencePieceNormalizer_swigregister(SentencePieceNormalizer) + +def SetDataDir(data_dir): + return _sentencepiece.SetDataDir(data_dir) + + +import re +import csv +import sys +import os +import importlib.resources +from io import StringIO +from io import BytesIO + + +def _add_snake_case(classname): + """Added snake_cased method from CammelCased method.""" + + snake_map = {} + for k, v in classname.__dict__.items(): + if re.match(r'^[A-Z]+', k): + snake = re.sub(r'(?= v.piece_size()): + raise IndexError('piece id is out of range.') + return func(v, n) + + def _batched_func(self, arg): + if type(arg) is list: + return [_func(self, n) for n in arg] + else: + return _func(self, arg) + + setattr(classname, name, _batched_func) + + +_sentencepiece_processor_init_native = SentencePieceProcessor.__init__ +_sentencepiece_normalizer_init_native = SentencePieceNormalizer.__init__ +setattr(SentencePieceProcessor, '__init__', SentencePieceProcessor.Init) +setattr(SentencePieceNormalizer, '__init__', SentencePieceNormalizer.Init) + +SentencePieceProcessor.Tokenize = SentencePieceProcessor.Encode +SentencePieceProcessor.Detokenize = SentencePieceProcessor.Decode + +for m in [ + 'PieceToId', 'IdToPiece', 'GetScore', 'IsUnknown', 'IsControl', 'IsUnused', + 'IsByte' +]: + _batchnize(SentencePieceProcessor, m) + +_add_snake_case(SentencePieceProcessor) +_add_snake_case(SentencePieceTrainer) +_add_snake_case(SentencePieceNormalizer) +set_random_generator_seed = SetRandomGeneratorSeed +set_min_log_level = SetMinLogLevel + +from ._version import __version__ + +SetDataDir(os.path.join(str(importlib.resources.files('sentencepiece')), 'package_data')) + +class _LogStream(object): + def __init__(self, ostream=None): + self.ostream = ostream + if self.ostream is not None: + self.orig_stream_fileno = sys.stderr.fileno() + + def __enter__(self): + if self.ostream is not None: + self.orig_stream_dup = os.dup(self.orig_stream_fileno) + os.dup2(self.ostream.fileno(), self.orig_stream_fileno) + + def __exit__(self, type, value, traceback): + if self.ostream is not None: + os.close(self.orig_stream_fileno) + os.dup2(self.orig_stream_dup, self.orig_stream_fileno) + os.close(self.orig_stream_dup) + self.ostream.close() + + diff --git a/.venv/lib/python3.12/site-packages/sentencepiece/_version.py b/.venv/lib/python3.12/site-packages/sentencepiece/_version.py new file mode 100644 index 0000000000000000000000000000000000000000..fc79d63d5430b972ac6ec1c4bfea9af80922da4d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentencepiece/_version.py @@ -0,0 +1 @@ +__version__ = '0.2.1' diff --git a/.venv/lib/python3.12/site-packages/sentencepiece/sentencepiece.i b/.venv/lib/python3.12/site-packages/sentencepiece/sentencepiece.i new file mode 100644 index 0000000000000000000000000000000000000000..20944ea1ea2ade6d4a1b2debf4e90aa8e16d91d6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentencepiece/sentencepiece.i @@ -0,0 +1,2013 @@ +%module sentencepiece +%include exception.i + +%{ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { +PyObject* kUnicodeInput = reinterpret_cast(0x1); +PyObject* kByteInput = reinterpret_cast(0x2); + +using BytesArray = std::vector; + +inline void ReleaseResultObject(PyObject *obj) { + if (obj != nullptr && obj != kUnicodeInput && obj != kByteInput) { + Py_XDECREF(obj); + } +} + +class PyInputString { + public: + explicit PyInputString(PyObject* obj) { + if (PyUnicode_Check(obj)) { + str_ = const_cast(PyUnicode_AsUTF8AndSize(obj, &size_)); + input_type_ = kUnicodeInput; + } else if (PyBytes_Check(obj)) { + PyBytes_AsStringAndSize(obj, &str_, &size_); + input_type_ = kByteInput; + } else { + str_ = nullptr; + } + } + absl::string_view str() const { return absl::string_view(data(), size()); } + const char* data() const { return str_; } + Py_ssize_t size() const { return size_; } + bool IsAvalable() const { return str_ != nullptr; } + PyObject *input_type() const { return input_type_; } + + static bool IsUnicode(PyObject *resultobj) { + return (resultobj == nullptr || resultobj == kUnicodeInput); + } + + private: + PyObject* input_type_ = nullptr; + char* str_ = nullptr; + Py_ssize_t size_ = 0; +}; + +PyObject* MakePyOutputString(const std::string& output, + PyObject *resultobj) { + if (PyInputString::IsUnicode(resultobj)) { + return PyUnicode_FromStringAndSize(output.data(), output.size()); + } + return PyBytes_FromStringAndSize(output.data(), output.size()); +} + +PyObject* MakePyOutputBytes(const sentencepiece::util::bytes& output) { + return PyBytes_FromStringAndSize(output.data(), output.size()); +} + +int ToSwigError(sentencepiece::util::StatusCode code) { + switch (code) { + case sentencepiece::util::StatusCode::kNotFound: + return SWIG_IOError; + case sentencepiece::util::StatusCode::kOutOfRange: + return SWIG_IndexError; + case sentencepiece::util::StatusCode::kInvalidArgument: + return SWIG_SyntaxError; + default: + return SWIG_RuntimeError; + } + return SWIG_RuntimeError; +} + +class PySentenceIterator : public sentencepiece::SentenceIterator { + public: + PySentenceIterator(PyObject *iter) : iter_(iter) { + item_ = PyIter_Next(iter_); + CopyValue(); + } + + ~PySentenceIterator() { + // Py_XDECREF(iter_); + } + + bool done() const override { + return item_ == nullptr; + } + + void Next() override { + item_ = PyIter_Next(iter_); + CopyValue(); + } + + const std::string &value() const override { + return value_; + } + + sentencepiece::util::Status status() const override { + return status_; + } + + private: + void CopyValue() { + if (item_ == nullptr) return; + const PyInputString ustring(item_); + if (ustring.IsAvalable()) { + const char *data = ustring.data(); + size_t size = ustring.size(); + while (size > 0) { + if (data[size - 1] == '\r' || data[size - 1] == '\n') + --size; + else + break; + } + value_.assign(data, size); + } else { + status_ = sentencepiece::util::Status(sentencepiece::util::StatusCode::kInternal, + "Not a string."); + } + Py_XDECREF(item_); + } + PyObject *iter_ = nullptr; + PyObject *item_ = nullptr; + std::string value_; + sentencepiece::util::Status status_; +}; + +inline void RewriteIds(const sentencepiece::SentencePieceProcessor &sp, + std::vector *ids, + bool add_bos, bool add_eos, bool reverse, bool emit_unk_piece) { + if (!add_bos && !add_eos && !reverse) return; + if (reverse) std::reverse(ids->begin(), ids->end()); + if (add_bos) ids->insert(ids->begin(), sp.bos_id()); + if (add_eos) ids->push_back(sp.eos_id()); +} + +inline void RewriteIds(const sentencepiece::SentencePieceProcessor &sp, + std::vector *pieces, + bool add_bos, bool add_eos, bool reverse, bool emit_unk_piece) { + if (!add_bos && !add_eos && !reverse && !emit_unk_piece) return; + if (reverse) std::reverse(pieces->begin(), pieces->end()); + if (add_bos) pieces->insert(pieces->begin(), sp.IdToPiece(sp.bos_id())); + if (add_eos) pieces->push_back(sp.IdToPiece(sp.eos_id())); + if (emit_unk_piece) { + const auto &unk = sp.IdToPiece(sp.unk_id()); + for (auto &piece : *pieces) { + const int id = sp.PieceToId(piece); + if (id == sp.unk_id()) { + piece = unk; + } + } + } +} + +inline void RewriteIds(const sentencepiece::SentencePieceProcessor &sp, + sentencepiece::util::bytes *proto, + bool add_bos, bool add_eos, bool reverse, bool emit_unk_piece) { + if (add_bos || add_eos || reverse || emit_unk_piece) { + throw sentencepiece::util::Status( + sentencepiece::util::StatusCode::kUnimplemented, + "add_bos, add_eos, reverse, and emit_unk_piece is not supported in proto API"); + } +} + +inline void RewriteIds(const sentencepiece::SentencePieceProcessor &sp, + sentencepiece::ImmutableSentencePieceText *proto, + bool add_bos, bool add_eos, bool reverse, bool emit_unk_piece) { + if (add_bos || add_eos || reverse || emit_unk_piece) { + throw sentencepiece::util::Status( + sentencepiece::util::StatusCode::kUnimplemented, + "add_bos, add_eos, reverse, and emit_unk_piece is not supported in proto API"); + } +} + +inline void CheckIds(const std::vector &ids, int num_pieces) { + for (int id : ids) { + if (id < 0 || id >= num_pieces) { + throw sentencepiece::util::Status( + sentencepiece::util::StatusCode::kOutOfRange, + "piece id is out of range."); + } + } +} + +inline void CheckIds(const std::vector &ids, int num_pieces) {} + +inline void CheckIdsBatch(const std::vector> &ids, int num_pieces) { + for (const auto &v : ids) CheckIds(v, num_pieces); +} + +template +inline void ConvertToUnicodeSpans(T *proto) {} + +template <> +inline void ConvertToUnicodeSpans(sentencepiece::ImmutableSentencePieceText *proto) { + proto->ConvertToUnicodeSpans(); +} + +template <> +inline void ConvertToUnicodeSpans(sentencepiece::ImmutableNBestSentencePieceText *proto) { + proto->ConvertToUnicodeSpans(); +} + +class ThreadPool { + public: + explicit ThreadPool(size_t request_size) : + request_size_(request_size) {} + + virtual ~ThreadPool() { + for (auto &task : tasks_) { + task.join(); + } + } + + void Schedule(std::function closure) { + static constexpr size_t kMinThreadSize = 2; + if (request_size_ < kMinThreadSize) { + closure(); + } else { + tasks_.emplace_back(closure); + } + } + + private: + size_t request_size_ = 0; + std::vector tasks_; +}; + +template +inline void InitNumThreads(const std::vector &ins, int *num_threads) { + if (*num_threads < 0) { + *num_threads = std::thread::hardware_concurrency(); + } + *num_threads = std::max(1, + std::min({*num_threads, + static_cast(ins.size()), 256})); +} + +#define DEFINE_ENCODE_BATCH_FUNC_IMPL(FuncName, InType, OutType) \ + std::vector outs(ins.size()); \ + InitNumThreads(ins, &num_threads); \ + { \ + ThreadPool pool(ins.size()); \ + std::atomic index = 0; \ + for (int n = 0; n < num_threads; ++n) { \ + pool.Schedule([&]() { \ + size_t i = 0; \ + while ((i = std::atomic_fetch_add(&index, 1)) < outs.size()) { \ + auto out = enable_sampling ? \ + self->Sample##FuncName(ins[i], \ + nbest_size, alpha) : \ + self->FuncName(ins[i]); \ + RewriteIds(*self, &out, add_bos, add_eos, reverse, \ + emit_unk_piece); \ + ConvertToUnicodeSpans(&out); \ + outs[i] = std::move(out); \ + } \ + }); \ + } \ + } \ + return outs; + +#define DEFINE_DECODE_BATCH_FUNC_IMPL(FuncName, InType, OutType) \ + std::vector outs(ins.size()); \ + InitNumThreads(ins, &num_threads); \ + { \ + std::atomic index = 0; \ + ThreadPool pool(ins.size()); \ + for (int n = 0; n < num_threads; ++n) { \ + pool.Schedule([&]() { \ + size_t i = 0; \ + while ((i = std::atomic_fetch_add(&index, 1)) < outs.size()) { \ + auto out = self->FuncName(ins[i]); \ + ConvertToUnicodeSpans(&out); \ + outs[i] = std::move(out); \ + } \ + }); \ + } \ + } \ + return outs; + +} // namespace +%} + +%init %{ +#ifdef Py_GIL_DISABLED + PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); +#endif +%} + +%exception { + try { + $action + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } +} + +%apply unsigned int { uint32_t } + +%ignore sentencepiece::util::Status; +%ignore sentencepiece::util::StatusCode; +%ignore absl::string_view; +%ignore std::string_view; +%ignore sentencepiece::SentencePieceText; +%ignore sentencepiece::NormalizerSpec; +%ignore sentencepiece::TrainerSpec; +%ignore sentencepiece::SentencePieceProcessor::status; +%ignore sentencepiece::ImmutableSentencePieceText::mutable_proto; +%ignore sentencepiece::ImmutableSentencePieceText::pieces() const; +%ignore sentencepiece::ImmutableSentencePieceText::ConvertToUnicodeSpans; +%ignore sentencepiece::ImmutableNBestSentencePieceText::mutable_proto; +%ignore sentencepiece::ImmutableNBestSentencePieceText::nbests() const; +%ignore sentencepiece::ImmutableNBestSentencePieceText::ConvertToUnicodeSpans; + +%ignore sentencepiece::SentencePieceProcessor::Encode; +%ignore sentencepiece::SentencePieceProcessor::SampleEncode; +%ignore sentencepiece::SentencePieceProcessor::NBestEncode; +%ignore sentencepiece::SentencePieceProcessor::SampleEncodeAndScore; +%ignore sentencepiece::SentencePieceProcessor::Decode; + +%ignore sentencepiece::SentencePieceProcessor::EncodeAsPieces; +%ignore sentencepiece::SentencePieceProcessor::EncodeAsIds; +%ignore sentencepiece::SentencePieceProcessor::SampleEncodeAsIds; +%ignore sentencepiece::SentencePieceProcessor::SampleEncodeAsPieces; +%ignore sentencepiece::SentencePieceProcessor::NBestEncodeAsIds; +%ignore sentencepiece::SentencePieceProcessor::NBestEncodeAsPieces; +%ignore sentencepiece::SentencePieceProcessor::SampleEncodeAndScoreAsIds; +%ignore sentencepiece::SentencePieceProcessor::SampleEncodeAndScoreAsPieces; +%ignore sentencepiece::SentencePieceProcessor::DecodeIds; +%ignore sentencepiece::SentencePieceProcessor::DecodePieces; + +%ignore sentencepiece::SentencePieceProcessor::EncodeAsSerializedProto; +%ignore sentencepiece::SentencePieceProcessor::SampleEncodeAsSerializedProto; +%ignore sentencepiece::SentencePieceProcessor::NBestEncodeAsSerializedProto; +%ignore sentencepiece::SentencePieceProcessor::SampleEncodeAndScoreAsSerializedProto; +%ignore sentencepiece::SentencePieceProcessor::DecodePiecesAsSerializedProto; +%ignore sentencepiece::SentencePieceProcessor::DecodeIdsAsSerializedProto; + +%ignore sentencepiece::SentencePieceProcessor::EncodeAsImmutableProto; +%ignore sentencepiece::SentencePieceProcessor::SampleEncodeAsImmutableProto; +%ignore sentencepiece::SentencePieceProcessor::NBestEncodeAsImmutableProto; +%ignore sentencepiece::SentencePieceProcessor::SampleEncodeAndScoreAsImmutableProto; +%ignore sentencepiece::SentencePieceProcessor::DecodePiecesAsImmutableProto; +%ignore sentencepiece::SentencePieceProcessor::DecodeIdsAsImmutableProto; + +%ignore sentencepiece::SentencePieceProcessor::Normalize; +%ignore sentencepiece::SentencePieceProcessor::NormalizeWithOffsets; + +%ignore sentencepiece::SentencePieceProcessor::model_proto; +%ignore sentencepiece::SentencePieceProcessor::mutable_normalizer_spec; +%ignore sentencepiece::SentencePieceProcessor::Load; +%ignore sentencepiece::SentencePieceProcessor::LoadOrDie; +%ignore sentencepiece::SentencePieceProcessor::SetModel; +%ignore sentencepiece::SentencePieceProcessor::SetNormalizer; +%ignore sentencepiece::pretokenizer::PretokenizerForTrainingInterface; +%ignore sentencepiece::SentenceIterator; +%ignore sentencepiece::ConvertToUnicodeSpans; +%ignore sentencepiece::SentencePieceTrainer::Train; +%ignore sentencepiece::SentencePieceTrainer::GetNormalizerSpec; +%ignore sentencepiece::SentencePieceTrainer::PopulateNormalizerSpec; +%ignore sentencepiece::SentencePieceTrainer::MergeSpecsFromArgs; +%ignore sentencepiece::SentencePieceTrainer::SetProtoField; +%ignore sentencepiece::SentencePieceTrainer::PopulateModelTypeFromString; +%ignore sentencepiece::SentencePieceTrainer::PieceProcecssor; +%ignore sentencepiece::SentencePieceTrainer::SetPretokenizerForTraining; +%ignore sentencepiece::SentencePieceTrainer::GetPretokenizerForTraining; +%ignore sentencepiece::SentencePieceTrainer::SetDataDir; +%ignore sentencepiece::ConvertToUnicodeAlignment; + +%ignore sentencepiece::SentencePieceNormalizer::Load; +%ignore sentencepiece::SentencePieceNormalizer::Normalize; +%ignore sentencepiece::SentencePieceNormalizer::mutable_normalizer_spec; + +%ignore sentencepiece::io::LoadModelProto; +%ignore sentencepiece::io::SaveModelProto; + +%extend sentencepiece::SentencePieceProcessor { + sentencepiece::util::Status LoadFromFile(absl::string_view arg) { + return $self->Load(arg); + } + + ///////////////////////////////////////////////////////////////////////////// + // EncodeAs* (Single request) + std::vector _EncodeAsIds(absl::string_view text, + bool enable_sampling, + int nbest_size, float alpha, + bool add_bos, bool add_eos, bool reverse, + bool emit_unk_piece) const { + auto ids = enable_sampling ? + $self->SampleEncodeAsIds(text, nbest_size, alpha) : + $self->EncodeAsIds(text); + RewriteIds(*$self, &ids, add_bos, add_eos, reverse, emit_unk_piece); + return ids; + } + + std::vector _EncodeAsPieces(absl::string_view text, + bool enable_sampling, + int nbest_size, float alpha, + bool add_bos, bool add_eos, bool reverse, + bool emit_unk_piece) const { + auto pieces = enable_sampling ? + $self->SampleEncodeAsPieces(text, nbest_size, alpha) : + $self->EncodeAsPieces(text); + RewriteIds(*$self, &pieces, add_bos, add_eos, reverse, emit_unk_piece); + return pieces; + } + + sentencepiece::util::bytes _EncodeAsSerializedProto(absl::string_view text, + bool enable_sampling, + int nbest_size, float alpha, + bool add_bos, bool add_eos, bool reverse, + bool emit_unk_piece) const { + auto proto = enable_sampling ? + $self->SampleEncodeAsSerializedProto(text, nbest_size, alpha) : + $self->EncodeAsSerializedProto(text); + RewriteIds(*$self, &proto, add_bos, add_eos, reverse, emit_unk_piece); + return proto; + } + + sentencepiece::ImmutableSentencePieceText + _EncodeAsImmutableProto(absl::string_view text, + bool enable_sampling, + int nbest_size, float alpha, + bool add_bos, bool add_eos, bool reverse, + bool emit_unk_piece) const { + auto proto = enable_sampling ? + $self->SampleEncodeAsImmutableProto(text, nbest_size, alpha) : + $self->EncodeAsImmutableProto(text); + proto.ConvertToUnicodeSpans(); + RewriteIds(*$self, &proto, add_bos, add_eos, reverse, emit_unk_piece); + return proto; + } + + ///////////////////////////////////////////////////////////////////////////// + // EncodeAs* (Batch request) + std::vector> _EncodeAsIdsBatch( + const std::vector &ins, int num_threads, + bool enable_sampling, int nbest_size, float alpha, + bool add_bos, bool add_eos, bool reverse, + bool emit_unk_piece) const { + DEFINE_ENCODE_BATCH_FUNC_IMPL(EncodeAsIds, + absl::string_view, std::vector); + } + + std::vector> _EncodeAsPiecesBatch( + const std::vector &ins, int num_threads, + bool enable_sampling, int nbest_size, float alpha, + bool add_bos, bool add_eos, bool reverse, + bool emit_unk_piece) const { + DEFINE_ENCODE_BATCH_FUNC_IMPL(EncodeAsPieces, + absl::string_view, std::vector); + } + + BytesArray _EncodeAsSerializedProtoBatch( + const std::vector &ins, int num_threads, + bool enable_sampling, int nbest_size, float alpha, + bool add_bos, bool add_eos, bool reverse, + bool emit_unk_piece) const { + DEFINE_ENCODE_BATCH_FUNC_IMPL(EncodeAsSerializedProto, + absl::string_view, + sentencepiece::util::bytes); + } + + std::vector + _EncodeAsImmutableProtoBatch( + const std::vector &ins, int num_threads, + bool enable_sampling, int nbest_size, float alpha, + bool add_bos, bool add_eos, bool reverse, + bool emit_unk_piece) const { + DEFINE_ENCODE_BATCH_FUNC_IMPL(EncodeAsImmutableProto, + absl::string_view, + sentencepiece::ImmutableSentencePieceText); + } + + ///////////////////////////////////////////////////////////////////////////// + // DecodeAs* (Single request) + std::string _DecodeIds(const std::vector &ids) const { + CheckIds(ids, $self->GetPieceSize()); + return $self->DecodeIds(ids); + } + + sentencepiece::util::bytes _DecodeIdsAsBytes(const std::vector &ids) const { + CheckIds(ids, $self->GetPieceSize()); + return $self->DecodeIds(ids); + } + + std::string _DecodePieces(const std::vector &pieces) const { + return $self->DecodePieces(pieces); + } + + sentencepiece::util::bytes _DecodeIdsAsSerializedProto( + const std::vector &ids) const { + CheckIds(ids, $self->GetPieceSize()); + return $self->DecodeIdsAsSerializedProto(ids); + } + + sentencepiece::util::bytes _DecodePiecesAsSerializedProto( + const std::vector &pieces) const { + CheckIds(pieces, $self->GetPieceSize()); + return $self->DecodePiecesAsSerializedProto(pieces); + } + + sentencepiece::ImmutableSentencePieceText _DecodeIdsAsImmutableProto( + const std::vector &ids) const { + CheckIds(ids, $self->GetPieceSize()); + auto proto = $self->DecodeIdsAsImmutableProto(ids); + proto.ConvertToUnicodeSpans(); + return proto; + } + + sentencepiece::ImmutableSentencePieceText _DecodePiecesAsImmutableProto( + const std::vector &pieces) const { + CheckIds(pieces, $self->GetPieceSize()); + auto proto= $self->DecodePiecesAsImmutableProto(pieces); + proto.ConvertToUnicodeSpans(); + return proto; + } + + ///////////////////////////////////////////////////////////////////////////// + // DecodeAs* (Batch request) + std::vector _DecodeIdsBatch( + const std::vector> &ins, int num_threads) const { + CheckIdsBatch(ins, $self->GetPieceSize()); + DEFINE_DECODE_BATCH_FUNC_IMPL(DecodeIds, int, std::string); + } + + BytesArray _DecodeIdsAsBytesBatch( + const std::vector> &ins, int num_threads) const { + CheckIdsBatch(ins, $self->GetPieceSize()); + DEFINE_DECODE_BATCH_FUNC_IMPL(DecodeIds, int, std::string); + } + + BytesArray _DecodeIdsAsSerializedProtoBatch( + const std::vector> &ins, int num_threads) const { + CheckIdsBatch(ins, $self->GetPieceSize()); + DEFINE_DECODE_BATCH_FUNC_IMPL(DecodeIdsAsSerializedProto, int, + sentencepiece::util::bytes); + } + + std::vector + _DecodeIdsAsImmutableProtoBatch( + const std::vector> &ins, int num_threads) const { + CheckIdsBatch(ins, $self->GetPieceSize()); + DEFINE_DECODE_BATCH_FUNC_IMPL(DecodeIdsAsImmutableProto, int, + sentencepiece::ImmutableSentencePieceText); + } + + std::vector _DecodePiecesBatch( + const std::vector> &ins, int num_threads) const { + DEFINE_DECODE_BATCH_FUNC_IMPL(DecodePieces, std::string, std::string); + } + + BytesArray _DecodePiecesAsSerializedProtoBatch( + const std::vector> &ins, int num_threads) const { + DEFINE_DECODE_BATCH_FUNC_IMPL(DecodePiecesAsSerializedProto, std::string, + sentencepiece::util::bytes); + } + + std::vector + _DecodePiecesAsImmutableProtoBatch( + const std::vector> &ins, int num_threads) const { + DEFINE_DECODE_BATCH_FUNC_IMPL(DecodePiecesAsImmutableProto, std::string, + sentencepiece::ImmutableSentencePieceText); + } + + //////////////////////////////////////////////////////////////////////////// + // NBestEncodeAs* (Single request) + std::vector> + _NBestEncodeAsIds(absl::string_view text, + int nbest_size, + bool add_bos, bool add_eos, bool reverse, + bool emit_unk_piece) const { + auto idss = $self->NBestEncodeAsIds(text, nbest_size); + for (auto &ids : idss) { + RewriteIds(*$self, &ids, add_bos, add_eos, reverse, emit_unk_piece); + } + return idss; + } + + std::vector> + _NBestEncodeAsPieces(absl::string_view text, + int nbest_size, + bool add_bos, bool add_eos, bool reverse, + bool emit_unk_piece) const { + auto piecess = $self->NBestEncodeAsPieces(text, nbest_size); + for (auto &pieces : piecess) { + RewriteIds(*$self, &pieces, add_bos, add_eos, reverse, emit_unk_piece); + } + return piecess; + } + + sentencepiece::util::bytes + _NBestEncodeAsSerializedProto(absl::string_view text, + int nbest_size, + bool add_bos, bool add_eos, bool reverse, + bool emit_unk_piece) const { + RewriteIds(*$self, static_cast(nullptr), + add_bos, add_eos, reverse, emit_unk_piece); + return $self->NBestEncodeAsSerializedProto(text, nbest_size); + } + + sentencepiece::ImmutableNBestSentencePieceText + _NBestEncodeAsImmutableProto(absl::string_view text, + int nbest_size, + bool add_bos, bool add_eos, bool reverse, + bool emit_unk_piece) const { + RewriteIds(*$self, static_cast(nullptr), + add_bos, add_eos, reverse, emit_unk_piece); + auto proto = $self->NBestEncodeAsImmutableProto(text, nbest_size); + proto.ConvertToUnicodeSpans(); + return proto; + } + + + ///////////////////////////////////////////////////////////////////////////// + // SampleEncodeAndScoreAs* (Single request) + std::vector, float>> + _SampleEncodeAndScoreAsIds(absl::string_view text, + int num_samples, float alpha, bool wor, + bool include_best, + bool add_bos, bool add_eos, bool reverse, + bool emit_unk_piece) const { + auto idss = $self->SampleEncodeAndScoreAsIds(text, num_samples, + alpha, wor, include_best); + for (auto &ids : idss) { + RewriteIds(*$self, &ids.first, add_bos, add_eos, reverse, emit_unk_piece); + } + return idss; + } + + std::vector, float>> + _SampleEncodeAndScoreAsPieces(absl::string_view text, + int num_samples, float alpha, bool wor, + bool include_best, + bool add_bos, bool add_eos, bool reverse, + bool emit_unk_piece) const { + auto piecess = $self->SampleEncodeAndScoreAsPieces(text, num_samples, + alpha, wor, include_best); + for (auto &pieces : piecess) { + RewriteIds(*$self, &pieces.first, add_bos, add_eos, reverse, emit_unk_piece); + } + return piecess; + } + + sentencepiece::util::bytes + _SampleEncodeAndScoreAsSerializedProto(absl::string_view text, + int num_samples, float alpha, bool wor, + bool include_best, + bool add_bos, bool add_eos, bool reverse, + bool emit_unk_piece) const { + RewriteIds(*$self, static_cast(nullptr), + add_bos, add_eos, reverse, emit_unk_piece); + return $self->SampleEncodeAndScoreAsSerializedProto(text, num_samples, + alpha, wor, include_best); + } + + sentencepiece::ImmutableNBestSentencePieceText + _SampleEncodeAndScoreAsImmutableProto(absl::string_view text, + int num_samples, float alpha, bool wor, + bool include_best, + bool add_bos, bool add_eos, bool reverse, + bool emit_unk_piece) const { + RewriteIds(*$self, static_cast(nullptr), + add_bos, add_eos, reverse, emit_unk_piece); + auto proto = $self->SampleEncodeAndScoreAsImmutableProto(text, num_samples, + alpha, wor, include_best); + proto.ConvertToUnicodeSpans(); + return proto; + } + + // Normalize + std::string _Normalize(absl::string_view text) { + return $self->Normalize(text); + } + + std::pair> _NormalizeWithOffsets(absl::string_view text) { + std::pair> result; + $self->Normalize(text, &result.first, &result.second).IgnoreError(); + return result; + } + + // Calculate Entropy + float _CalculateEntropy(absl::string_view text, float alpha) { + return $self->CalculateEntropy(text, alpha); + } + + std::vector _CalculateEntropyBatch(const std::vector &ins, + float alpha, int num_threads) { + std::vector outs(ins.size()); + InitNumThreads(ins, &num_threads); + { + ThreadPool pool(ins.size()); + std::atomic index = 0; + for (int n = 0; n < num_threads; ++n) { + pool.Schedule([&]() { + size_t i = 0; + while ((i = std::atomic_fetch_add(&index, 1)) < outs.size()) { + outs[i] = self->CalculateEntropy(ins[i], alpha); + } + }); + } + } + return outs; + } + + // override normalizer_spec + sentencepiece::util::Status _OverrideNormalizerSpec( + const std::unordered_map &args) { + sentencepiece::util::Status status; + for (const auto &[key, value] : args) { + status = sentencepiece::SentencePieceTrainer::SetProtoField( + key, value, + $self->mutable_normalizer_spec()); + if (!status.ok()) return status; + } + return status; + } + +%pythoncode { + def Init(self, + model_file=None, + model_proto=None, + out_type=int, + add_bos=False, + add_eos=False, + reverse=False, + emit_unk_piece=False, + enable_sampling=False, + nbest_size=-1, + alpha=0.1, + num_threads=-1): + """Initialzie sentencepieceProcessor. + + Args: + model_file: The sentencepiece model file path. + model_proto: The sentencepiece model serialized proto. + out_type: output type. int or str. + add_bos: Add to the result (Default = false) + add_eos: Add to the result (Default = false) / is added after + reversing (if enabled). + reverse: Reverses the tokenized sequence (Default = false) + emit_unk_piece: Emits the unk literal string (Default = false) + nbest_size: sampling parameters for unigram. Invalid in BPE-Dropout. + nbest_size = {0,1}: No sampling is performed. + nbest_size > 1: samples from the nbest_size results. + nbest_size < 0: assuming that nbest_size is infinite and samples + from the all hypothesis (lattice) using + forward-filtering-and-backward-sampling algorithm. + alpha: Soothing parameter for unigram sampling, and dropout probability of + merge operations for BPE-dropout. + num_threads: number of threads in batch processing (Default = -1, auto-detected) + """ + + _sentencepiece_processor_init_native(self) + self._out_type = out_type + self._add_bos = add_bos + self._add_eos = add_eos + self._reverse = reverse + self._emit_unk_piece = emit_unk_piece + self._enable_sampling = enable_sampling + self._nbest_size = nbest_size + self._alpha = alpha + self._num_threads = num_threads + if model_file or model_proto: + self.Load(model_file=model_file, model_proto=model_proto) + + + def Encode(self, + input, + out_type=None, + add_bos=None, + add_eos=None, + reverse=None, + emit_unk_piece=None, + enable_sampling=None, + nbest_size=None, + alpha=None, + num_threads=None): + """Encode text input to segmented ids or tokens. + + Args: + input: input string. accepsts list of string. + out_type: output type. int or str. + add_bos: Add to the result (Default = false) + add_eos: Add to the result (Default = false) / is added after + reversing (if enabled). + reverse: Reverses the tokenized sequence (Default = false) + emit_unk_piece: Emits the unk literal string (Default = false) + nbest_size: sampling parameters for unigram. Invalid in BPE-Dropout. + nbest_size = {0,1}: No sampling is performed. + nbest_size > 1: samples from the nbest_size results. + nbest_size < 0: assuming that nbest_size is infinite and samples + from the all hypothesis (lattice) using + forward-filtering-and-backward-sampling algorithm. + alpha: Soothing parameter for unigram sampling, and merge probability for + BPE-dropout (probablity 'p' in BPE-dropout paper). + num_threads: the number of threads used in the batch processing (Default = -1). + """ + + if out_type is None: + out_type = self._out_type + if add_bos is None: + add_bos = self._add_bos + if add_eos is None: + add_eos = self._add_eos + if reverse is None: + reverse = self._reverse + if emit_unk_piece is None: + emit_unk_piece = self._emit_unk_piece + if enable_sampling is None: + enable_sampling = self._enable_sampling + if nbest_size is None: + nbest_size = self._nbest_size + if alpha is None: + alpha = self._alpha + if num_threads is None: + num_threads = self._num_threads + + if enable_sampling == True and (nbest_size is None or nbest_size == 0 or + nbest_size == 1 or alpha is None): + raise RuntimeError( + 'When enable_sampling is True, We must specify "nbest_size > 1" or "nbest_size = -1", ' + 'and "alpha". "nbest_size" is enabled only on unigram mode ignored in BPE-dropout. ' + 'when "nbest_size = -1" , this method samples from all candidates on the lattice ' + 'instead of nbest segmentations.' + ) + + if num_threads is None or type(num_threads) is not int: + raise RuntimeError('num_threads must be int') + + if type(input) is list: + if out_type is int: + return self._EncodeAsIdsBatch(input, num_threads, enable_sampling, nbest_size, + alpha, add_bos, add_eos, reverse, emit_unk_piece) + if out_type is str: + return self._EncodeAsPiecesBatch(input, num_threads, enable_sampling, nbest_size, + alpha, add_bos, add_eos, reverse, emit_unk_piece) + if out_type == 'serialized_proto' or out_type == 'proto': + return self._EncodeAsSerializedProtoBatch(input, num_threads, enable_sampling, nbest_size, + alpha, add_bos, add_eos, reverse, emit_unk_piece) + if out_type == 'immutable_proto': + return self._EncodeAsImmutableProtoBatch(input, num_threads, enable_sampling, nbest_size, + alpha, add_bos, add_eos, reverse, emit_unk_piece) + + if out_type is int: + return self._EncodeAsIds(input, enable_sampling, nbest_size, + alpha, add_bos, add_eos, reverse, emit_unk_piece) + if out_type is str: + return self._EncodeAsPieces(input, enable_sampling, nbest_size, + alpha, add_bos, add_eos, reverse, emit_unk_piece) + if out_type == 'serialized_proto' or out_type == 'proto': + return self._EncodeAsSerializedProto(input, enable_sampling, nbest_size, + alpha, add_bos, add_eos, reverse, emit_unk_piece) + if out_type == 'immutable_proto': + return self._EncodeAsImmutableProto(input, enable_sampling, nbest_size, + alpha, add_bos, add_eos, reverse, emit_unk_piece) + + raise RuntimeError('unknown out_type={}'.format(out_type)) + return None + + + def EncodeAsPieces(self, input, **kwargs): + return self.Encode(input=input, out_type=str, **kwargs) + + + def EncodeAsIds(self, input, **kwargs): + return self.Encode(input=input, out_type=int, **kwargs) + + + def EncodeAsSerializedProto(self, input, **kwargs): + return self.Encode(input=input, out_type='serialized_proto', **kwargs) + + + def EncodeAsImmutableProto(self, input, **kwargs): + return self.Encode(input=input, out_type='immutable_proto', **kwargs) + + + def SampleEncodeAsPieces(self, input, nbest_size=None, alpha=None, **kwargs): + return self.Encode(input=input, nbest_size=nbest_size, alpha=alpha, + out_type=str, enable_sampling=True, **kwargs) + + + def SampleEncodeAsIds(self, input, nbest_size=None, alpha=None,**kwargs): + return self.Encode(input=input, nbest_size=nbest_size, alpha=alpha, + out_type=int, enable_sampling=True, **kwargs) + + + def SampleEncodeAsSerializedProto(self, input, nbest_size=None, alpha=None, **kwargs): + return self.Encode(input=input, nbest_size=nbest_size, alpha=alpha, + out_type='serialized_proto', enable_sampling=True, **kwargs) + + + def SampleEncodeAsImmutableProto(self, input, nbest_size=None, alpha=None, **kwargs): + return self.Encode(input=input, nbest_size=nbest_size, alpha=alpha, + out_type='immutable_proto', enable_sampling=True, **kwargs) + + + def NBestEncode(self, + input, + out_type=None, + add_bos=None, + add_eos=None, + reverse=None, + emit_unk_piece=None, + nbest_size=None): + """NBestEncode text input to segmented ids or tokens. + + Args: + input: input string. accepsts list of string. + out_type: output type. int or str. + add_bos: Add to the result (Default = false) + add_eos: Add to the result (Default = false) / is added after reversing (if enabled). + reverse: Reverses the tokenized sequence (Default = false) + emit_unk_piece: Emits the unk literal string (Default = false) + nbest_size: nbest size + """ + + if out_type is None: + out_type = self._out_type + if add_bos is None: + add_bos = self._add_bos + if add_eos is None: + add_eos = self._add_eos + if reverse is None: + reverse = self._reverse + if emit_unk_piece is None: + emit_unk_piece = self._emit_unk_piece + if nbest_size is None: + nbest_size = self._nbest_size + + if nbest_size <= 0: + nbest_size=1 + + def _encode(text): + if out_type is int: + return self._NBestEncodeAsIds(text, nbest_size, + add_bos, add_eos, reverse, emit_unk_piece) + if out_type is str: + return self._NBestEncodeAsPieces(text, nbest_size, + add_bos, add_eos, reverse, emit_unk_piece) + if out_type == 'serialized_proto' or out_type == 'proto': + return self._NBestEncodeAsSerializedProto(text, nbest_size, + add_bos, add_eos, reverse, emit_unk_piece) + if out_type == 'immutable_proto': + return self._NBestEncodeAsImmutableProto(text, nbest_size, + add_bos, add_eos, reverse, emit_unk_piece) + + raise RuntimeError('unknown out_type') + + if type(input) is list: + return [_encode(n) for n in input] + + return _encode(input) + + + def NBestEncodeAsPieces(self, input, nbest_size=None, **kwargs): + return self.NBestEncode(input=input, nbest_size=nbest_size, + out_type=str, **kwargs) + + + def NBestEncodeAsIds(self, input, nbest_size=None, **kwargs): + return self.NBestEncode(input=input, nbest_size=nbest_size, + out_type=int, **kwargs) + + + def NBestEncodeAsSerializedProto(self, input, nbest_size=None, **kwargs): + return self.NBestEncode(input=input, nbest_size=nbest_size, + out_type='serialized_proto', **kwargs) + + + def NBestEncodeAsImmutableProto(self, input, nbest_size=None, **kwargs): + return self.NBestEncode(input=input, nbest_size=nbest_size, + out_type='immutable_proto', **kwargs) + + + def SampleEncodeAndScore(self, + input, + out_type=None, + add_bos=None, + add_eos=None, + reverse=None, + emit_unk_piece=None, + num_samples=None, + alpha=None, + wor=None, + include_best=None): + """SampleEncodeAndScore text input to segmented ids or tokens. + + Args: + input: input string. accepsts list of string. + out_type: output type. int or str or 'serialized_proto' or 'immutable_proto' + add_bos: Add to the result (Default = false) + add_eos: Add to the result (Default = false) / is added after reversing (if enabled). + reverse: Reverses the tokenized sequence (Default = false) + emit_unk_piece: Emits the unk literal string (Default = false) + num_samples: How many samples to return (Default = 1) + alpha: inverse temperature for sampling + wor: whether to sample without replacement (Default = false) + include_best: whether to include the best tokenization, requires wor=True (Default = false) + """ + + if out_type is None: + out_type = self._out_type + if add_bos is None: + add_bos = self._add_bos + if add_eos is None: + add_eos = self._add_eos + if reverse is None: + reverse = self._reverse + if emit_unk_piece is None: + emit_unk_piece = self._emit_unk_piece + if num_samples is None: + num_samples = 1 + if alpha is None: + alpha = 1. + if wor is None: + wor = False + if include_best is None: + include_best = False + + if num_samples <= 0: + raise RuntimeError('num_examples must be positive') + + if include_best and not wor: + raise RuntimeError('When include_best is True, We must specify "wor = True".') + + + def _encode(text): + if out_type is int: + return self._SampleEncodeAndScoreAsIds(text, num_samples, alpha, wor, include_best, + add_bos, add_eos, reverse, emit_unk_piece) + if out_type is str: + return self._SampleEncodeAndScoreAsPieces(text, num_samples, alpha, wor, include_best, + add_bos, add_eos, reverse, emit_unk_piece) + + if out_type == 'serialized_proto' or out_type == 'proto': + return self._SampleEncodeAndScoreAsSerializedProto(text, num_samples, alpha, wor, include_best, + add_bos, add_eos, reverse, emit_unk_piece) + + if out_type == 'immutable_proto': + return self._SampleEncodeAndScoreAsImmutableProto(text, num_samples, alpha, wor, include_best, + add_bos, add_eos, reverse, emit_unk_piece) + + raise RuntimeError('unknown output type') + + + if type(input) is list: + return [_encode(n) for n in input] + + return _encode(input) + + + def SampleEncodeAndScoreAsPieces(self, input, num_samples=None, alpha=None, **kwargs): + return self.SampleEncodeAndScore(input=input, num_samples=num_samples, alpha=alpha, + out_type=str, **kwargs) + + + def SampleEncodeAndScoreAsIds(self, input, num_samples=None, alpha=None, **kwargs): + return self.SampleEncodeAndScore(input=input, num_samples=num_samples, alpha=alpha, + out_type=int, **kwargs) + + + def SampleEncodeAndScoreAsSerializedProto(self, input, num_samples=None, alpha=None, **kwargs): + return self.SampleEncodeAndScore(input=input, num_samples=num_samples, alpha=alpha, + out_type='serialized_proto', **kwargs) + + + def SampleEncodeAndScoreAsImmutableProto(self, input, num_samples=None, alpha=None, **kwargs): + return self.SampleEncodeAndScore(input=input, num_samples=num_samples, alpha=alpha, + out_type='immutable_proto', **kwargs) + + + def Decode(self, input, out_type=str, num_threads=None): + """Decode processed id or token sequences. + + Args: + out_type: output type. str, bytes or 'serialized_proto' or 'immutable_proto' (Default = str) + num_threads: the number of threads used in the batch processing (Default = -1). + """ + + if num_threads is None: + num_threads = self._num_threads + + if num_threads is None or type(num_threads) is not int: + raise RuntimeError('num_threads must be int') + + if not input: + return '' + + if out_type is str: + if type(input) is int: + return self._DecodeIds([input]) + if type(input) is str: + return self._DecodePieces([input]) + + if type(input) is list: + if len(input) == 0 or type(input[0]) is int: + return self._DecodeIds(input) + if type(input[0]) is str: + return self._DecodePieces(input) + + if type(input[0]) is list: + if len(input[0]) == 0 or type(input[0][0]) is int: + return self._DecodeIdsBatch(input, num_threads) + if type(input[0][0]) is str: + return self._DecodePiecesBatch(input, num_threads) + + if out_type is bytes: + if type(input) is int: + return self._DecodeIdsAsBytes([input]) + if type(input) is str: + return self._DecodePieces([input]) + + if type(input) is list: + if len(input) == 0 or type(input[0]) is int: + return self._DecodeIdsAsBytes(input) + if type(input[0]) is str: + return self._DecodePieces(input) + + if type(input[0]) is list: + if len(input[0]) == 0 or type(input[0][0]) is int: + return self._DecodeIdsAsBytesBatch(input, num_threads) + if type(input[0][0]) is str: + return self._DecodePiecesBatch(input, num_threads) + + if out_type == 'serialized_proto': + if type(input) is int: + return self._DecodeIdsAsSerializedProto([input]) + if type(input) is str: + return self._DecodePiecesAsSerializedProto([input]) + + if type(input) is list: + if len(input) == 0 or type(input[0]) is int: + return self._DecodeIdsAsSerializedProto(input) + if type(input[0]) is str: + return self._DecodePiecesAsSerializedProto(input) + + if type(input[0]) is list: + if len(input[0]) == 0 or type(input[0][0]) is int: + return self._DecodeIdsAsSerializedProtoBatch(input, num_threads) + if type(input[0][0]) is str: + return self._DecodePiecesAsSerializedProtoBatch(input, num_threads) + + + if out_type == 'immutable_proto': + if type(input) is int: + return self._DecodeIdsAsImmutableProto([input]) + if type(input) is str: + return self._DecodePiecesAsImmutableProto([input]) + + if type(input) is list: + if len(input) == 0 or type(input[0]) is int: + return self._DecodeIdsAsImmutableProto(input) + if type(input[0]) is str: + return self._DecodePiecesAsImmutableProto(input) + + if type(input[0]) is list: + if len(input[0]) == 0 or type(input[0][0]) is int: + return self._DecodeIdsAsImmutableProtoBatch(input, num_threads) + if type(input[0][0]) is str: + return self._DecodePiecesAsImmutableProtoBatch(input, num_threads) + + + raise RuntimeError('unknown output or input type') + return None + + + def DecodePieces(self, input, out_type=str, **kwargs): + return self.Decode(input=input, out_type=out_type, **kwargs) + + + def DecodeIds(self, input, out_type=str, **kwargs): + return self.Decode(input=input, out_type=out_type, **kwargs) + + + def DecodePiecesAsSerializedProto(self, input, out_type='serialized_proto', **kwargs): + return self.Decode(input=input, out_type=out_type, **kwargs) + + + def DecodeIdsAsSerializedProto(self, input, out_type='serialized_proto', **kwargs): + return self.Decode(input=input, out_type=out_type, **kwargs) + + + def DecodePiecesAsImmutableProto(self, input, out_type='immutable_proto', **kwargs): + return self.Decode(input=input, out_type=out_type, **kwargs) + + + def DecodeIdsAsImmutableProto(self, input, out_type='immutable_proto', **kwargs): + return self.Decode(input=input, out_type=out_type, **kwargs) + + + def CalculateEntropy(self, input, alpha, num_threads=None): + """Calculate sentence entropy""" + if type(input) is list: + if num_threads is None: + num_threads = self._num_threads + if num_threads is None or type(num_threads) is not int: + raise RuntimeError('num_threads must be int') + return self._CalculateEntropyBatch(input, alpha, num_threads) + + return self._CalculateEntropy(input, alpha) + + + def Normalize(self, input, with_offsets=None): + def _normalize(text): + if with_offsets: + return self._NormalizeWithOffsets(text) + return self._Normalize(text) + + if type(input) is list: + return [_normalize(x) for x in input] + return _normalize(input) + + def OverrideNormalizerSpec(self, **kwargs): + new_kwargs = {} + for key, value in kwargs.items(): + new_kwargs[key] = str(value) + return self._OverrideNormalizerSpec(new_kwargs) + + + def piece_size(self): + return self.GetPieceSize() + + + def vocab_size(self): + return self.GetPieceSize() + + + def __getstate__(self): + return self.serialized_model_proto() + + + def __setstate__(self, serialized_model_proto): + self.__init__() + self.LoadFromSerializedProto(serialized_model_proto) + + + def __len__(self): + return self.GetPieceSize() + + + def __getitem__(self, piece): + return self.PieceToId(piece) + + + def Load(self, model_file=None, model_proto=None): + """Overwride SentencePieceProcessor.Load to support both model_file and model_proto. + + Args: + model_file: The sentencepiece model file path. + model_proto: The sentencepiece model serialized proto. Either `model_file` + or `model_proto` must be set. + """ + if model_file and model_proto: + raise RuntimeError('model_file and model_proto must be exclusive.') + if model_proto: + return self.LoadFromSerializedProto(model_proto) + return self.LoadFromFile(model_file) +} +} + +%extend sentencepiece::SentencePieceTrainer { + static void _TrainFromString(absl::string_view arg) { + const auto _status = sentencepiece::SentencePieceTrainer::Train(arg); + if (!_status.ok()) throw _status; + return; + } + + static void _TrainFromMap(const std::unordered_map &args) { + const auto _status = sentencepiece::SentencePieceTrainer::Train(args); + if (!_status.ok()) throw _status; + return; + } + + static void _TrainFromMap2(const std::unordered_map &args, + SentenceIterator *iter) { + const auto _status = sentencepiece::SentencePieceTrainer::Train(args, iter); + if (!_status.ok()) throw _status; + return; + } + + static sentencepiece::util::bytes _TrainFromMap3(const std::unordered_map &args) { + sentencepiece::util::bytes model_proto; + const auto _status = sentencepiece::SentencePieceTrainer::Train(args, nullptr, &model_proto); + if (!_status.ok()) throw _status; + return model_proto; + } + + static sentencepiece::util::bytes _TrainFromMap4(const std::unordered_map &args, + SentenceIterator *iter) { + sentencepiece::util::bytes model_proto; + const auto _status = sentencepiece::SentencePieceTrainer::Train(args, iter, &model_proto); + if (!_status.ok()) throw _status; + return model_proto; + } + +%pythoncode { + @staticmethod + def _Train(arg=None, **kwargs): + """Train Sentencepiece model. Accept both kwargs and legacy string arg.""" + if arg is not None and type(arg) is str: + return SentencePieceTrainer._TrainFromString(arg) + + def _encode(value): + """Encode value to CSV..""" + if type(value) is list: + if sys.version_info[0] == 3: + f = StringIO() + else: + f = BytesIO() + writer = csv.writer(f, lineterminator='') + writer.writerow([str(v) for v in value]) + return f.getvalue() + else: + return str(value) + + sentence_iterator = None + model_writer = None + new_kwargs = {} + for key, value in kwargs.items(): + if key in ['sentence_iterator', 'sentence_reader']: + sentence_iterator = value + elif key in ['model_writer']: + model_writer = value + else: + new_kwargs[key] = _encode(value) + + if model_writer: + if sentence_iterator: + model_proto = SentencePieceTrainer._TrainFromMap4(new_kwargs, + sentence_iterator) + else: + model_proto = SentencePieceTrainer._TrainFromMap3(new_kwargs) + model_writer.write(model_proto) + else: + if sentence_iterator: + return SentencePieceTrainer._TrainFromMap2(new_kwargs, sentence_iterator) + else: + return SentencePieceTrainer._TrainFromMap(new_kwargs) + + return None + + @staticmethod + def Train(arg=None, logstream=None, **kwargs): + with _LogStream(ostream=logstream): + SentencePieceTrainer._Train(arg=arg, **kwargs) +} +} + +%extend sentencepiece::SentencePieceNormalizer { + sentencepiece::util::Status LoadFromFile(absl::string_view arg) { + return $self->Load(arg); + } + + std::string _Normalize(absl::string_view text) { + std::string result; + const auto _status = $self->Normalize(text, &result); + if (!_status.ok()) throw _status; + return result; + } + + std::pair> _NormalizeWithOffsets(absl::string_view text) { + std::pair> result; + const auto _status = $self->Normalize(text, &result.first, &result.second); + if (!_status.ok()) throw _status; + return result; + } + + void _SetProtoField(absl::string_view name, bool value) { + sentencepiece::SentencePieceTrainer::SetProtoField( + name, + value ? "1" : "0", + $self->mutable_normalizer_spec()).IgnoreError(); + } + +%pythoncode %{ + def Init(self, + model_file=None, + model_proto=None, + rule_tsv=None, + rule_name=None, + add_dummy_prefix=False, + escape_whitespaces=False, + remove_extra_whitespaces=False): + """Initialzie sentencePieceNormalizer. + + Args: + model_file: The sentencepiece model file path. + model_proto: The sentencepiece model serialized proto. + rule_tsv: The normalization rule file in TSV format. + rule_name: Pre-defined normalization name. + add_dummy_prefix: add dummy prefix. + escape_whitespaces: escape whitespaces. + remove_extra_whitespaces: remove extra whitespaces. + """ + + _sentencepiece_normalizer_init_native(self) + + if model_file: + status = self.LoadFromFile(model_file) + elif model_proto: + status = self.LoadFromSerializedProto(model_proto) + elif rule_tsv: + status = self.LoadFromRuleTSV(rule_tsv) + elif rule_name: + status = self.LoadFromRuleName(rule_name) + else: + raise RuntimeError('no model is specified') + + if status: + self._SetProtoField('add_dummy_prefix', add_dummy_prefix) + self._SetProtoField('escape_whitespaces', escape_whitespaces) + self._SetProtoField('remove_extra_whitespaces', remove_extra_whitespaces) + + def Normalize(self, input, with_offsets=None): + def _normalize(text): + if with_offsets: + return self._NormalizeWithOffsets(text) + return self._Normalize(text) + + if type(input) is list: + return [_normalize(x) for x in input] + return _normalize(input) + + + def __getstate__(self): + return self.serialized_model_proto() + + + def __setstate__(self, serialized_model_proto): + self.__init__() + self.LoadFromSerializedProto(serialized_model_proto) +%} +} + +%extend sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece { + const sentencepiece::util::bytes& _surface_as_bytes() const { + return $self->surface(); + } + + const sentencepiece::util::bytes& _piece_as_bytes() const { + return $self->piece(); + } + + %rename(_piece) piece; + %rename(_piece_as_bytes) piece_as_bytes; + %rename(_id) id; + %rename(_surface) surface; + %rename(_surface_as_bytes) surface_as_bytes; + %rename(_begin) begin; + %rename(_end) end; + + %pythoncode %{ + piece = property(_piece) + piece_as_bytes = property(_piece_as_bytes) + surface = property(_surface) + surface_as_bytes = property(_surface_as_bytes) + id = property(_id) + begin = property(_begin) + end = property(_end) + + def __str__(self): + return ('piece: \"{}\"\n' + 'id: {}\n' + 'surface: \"{}\"\n' + 'begin: {}\n' + 'end: {}\n').format(self.piece, self.id, self.surface, + self.begin, self.end) + + def __eq__(self, other): + return self.piece == other.piece and self.id == other.id and self.surface == other.surface and self.begin == other.begin and self.end == other.end + + def __hash__(self): + return hash(str(self)) + + __repr__ = __str__ + %} +} + +%extend sentencepiece::ImmutableSentencePieceText { + const sentencepiece::util::bytes& _text_as_bytes() const { + return $self->text(); + } + + %rename(_text) text; + %rename(_text_as_bytes) text_as_bytes; + %rename(_score) score; + %rename(_pieces) pieces; + %rename(_pieces_size) pieces_size; + + %pythoncode %{ + text = property(_text) + text_as_bytes = property(_text_as_bytes) + score = property(_score) + + class ImmutableSentencePieceIterator: + def __init__(self, proto): + self.proto = proto + self.len = self.proto._pieces_size() + + def __len__(self): + return self.len + + def __getitem__(self, index): + if isinstance(index, slice): + return [self.proto._pieces(i) for i in range(self.len)][index.start:index.stop:index.step] + if index < 0: + index = index + self.len + if index < 0 or index >= self.len: + raise IndexError('piece index is out of range') + return self.proto._pieces(index) + + def __str__(self): + return '\n'.join(['pieces {{\n{}}}'.format(str(x)) for x in self]) + + __repr__ = __str__ + + @property + def pieces(self): + return ImmutableSentencePieceText.ImmutableSentencePieceIterator(self) + + def __eq__(self, other): + return self.SerializeAsString() == other.SerializeAsString() + + def __hash__(self): + return hash(self.SerializeAsString()) + + def __str__(self): + return ('text: \"{}\"\n' + 'score: {}\n' + '{}').format(self.text, self.score, + '\n'.join(['pieces {{\n{}}}'.format(str(x)) for x in self.pieces])) + + __repr__ = __str__ + %} +} + +%extend sentencepiece::ImmutableNBestSentencePieceText { + %rename(_nbests) nbests; + %rename(_nbests_size) nbests_size; + + %pythoncode %{ + class ImmutableSentencePieceTextIterator: + def __init__(self, proto): + self.proto = proto + self.len = self.proto._nbests_size() + + def __len__(self): + return self.len + + def __getitem__(self, index): + if isinstance(index, slice): + return [self.proto._nbests(i) for i in range(self.len)][index.start:index.stop:index.step] + if index < 0: + index = index + self.len + if index < 0 or index >= self.len: + raise IndexError('nbests index is out of range') + return self.proto._nbests(index) + + def __str__(self): + return '\n'.join(['nbests {{\n{}}}'.format(str(x)) for x in self]) + + __repr__ = __str__ + + @property + def nbests(self): + return ImmutableNBestSentencePieceText.ImmutableSentencePieceTextIterator(self) + + def __eq__(self, other): + return self.SerializeAsString() == other.SerializeAsString() + + def __hash__(self): + return hash(self.SerializeAsString()) + + def __str__(self): + return '\n'.join(['nbests {{\n{}}}'.format(str(x)) for x in self.nbests]) + + __repr__ = __str__ + %} +} + +%typemap(out) std::vector { + $result = PyList_New($1.size()); + for (size_t i = 0; i < $1.size(); ++i) { + PyList_SET_ITEM($result, i, PyInt_FromLong(static_cast($1[i]))); + } +} + +%typemap(out) std::vector { + $result = PyList_New($1.size()); + for (size_t i = 0; i < $1.size(); ++i) { + PyList_SET_ITEM($result, i, PyFloat_FromDouble(static_cast($1[i]))); + } +} + +%typemap(out) std::vector> { + $result = PyList_New($1.size()); + for (size_t i = 0; i < $1.size(); ++i) { + PyObject *obj = PyList_New($1[i].size()); + for (size_t j = 0; j < $1[i].size(); ++j) { + PyList_SET_ITEM(obj, j, PyInt_FromLong(static_cast($1[i][j]))); + } + PyList_SET_ITEM($result, i, obj); + } +} + +%typemap(out) std::vector { + PyObject *input_type = resultobj; + $result = PyList_New($1.size()); + for (size_t i = 0; i < $1.size(); ++i) { + PyList_SET_ITEM($result, i, MakePyOutputString($1[i], input_type)); + } +} + +%typemap(out) BytesArray { + $result = PyList_New($1.size()); + for (size_t i = 0; i < $1.size(); ++i) { + PyList_SET_ITEM($result, i, MakePyOutputBytes($1[i])); + } +} + +%typemap(out) std::vector> { + PyObject *input_type = resultobj; + $result = PyList_New($1.size()); + for (size_t i = 0; i < $1.size(); ++i) { + PyObject *obj = PyList_New($1[i].size()); + for (size_t j = 0; j < $1[i].size(); ++j) { + PyList_SET_ITEM(obj, j, MakePyOutputString($1[i][j], input_type)); + } + PyList_SET_ITEM($result, i, obj); + } +} + +%typemap(out) sentencepiece::util::bytes { + $result = MakePyOutputBytes($1); +} + +%typemap(out) const sentencepiece::util::bytes& { + $result = MakePyOutputBytes(*$1); +} + +%typemap(out) std::string { + PyObject *input_type = resultobj; + $result = MakePyOutputString($1, input_type); +} + +%typemap(out) const std::string& { + PyObject *input_type = resultobj; + $result = MakePyOutputString(*$1, input_type); +} + +%typemap(out) sentencepiece::util::Status { + if (!$1.ok()) { + SWIG_exception(ToSwigError($1.code()), $1.ToString().c_str()); + } + $result = SWIG_From_bool($1.ok());} + + +%typemap(in) const std::string & { + const PyInputString ustring($input); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + $1 = new std::string(ustring.data(), ustring.size()); +} + +%typemap(typecheck) absl::string_view = char *; + +%typemap(in) absl::string_view { + const PyInputString ustring($input); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + $1 = ustring.str(); +} + +%typemap(in) const std::vector& { + std::vector *out = nullptr; + if (PyList_Check($input)) { + const size_t size = PyList_Size($input); + out = new std::vector(size); + for (size_t i = 0; i < size; ++i) { + const PyInputString ustring(PyList_GetItem($input, i)); + if (ustring.IsAvalable()) { + (*out)[i] = ustring.str(); + } else { + PyErr_SetString(PyExc_TypeError, "list must contain strings"); + SWIG_fail; + } + resultobj = ustring.input_type(); + } + } else { + PyErr_SetString(PyExc_TypeError, "not a list"); + SWIG_fail; + } + $1 = out; +} + +%typemap(in) const std::vector& { + std::vector *out = nullptr; + if (PyList_Check($input)) { + const size_t size = PyList_Size($input); + out = new std::vector(size); + for (size_t i = 0; i < size; ++i) { + PyObject *o = PyList_GetItem($input, i); + if (PyInt_Check(o)) { + (*out)[i] = static_cast(PyInt_AsLong(o)); + } else { + PyErr_SetString(PyExc_TypeError,"list must contain integers"); + SWIG_fail; + } + } + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + SWIG_fail; + } + $1 = out; +} + +%typemap(in) const std::vector>& { + std::vector> *out = nullptr; + if (PyList_Check($input)) { + const size_t size = PyList_Size($input); + out = new std::vector>(size); + for (size_t i = 0; i < size; ++i) { + PyObject *o = PyList_GetItem($input, i); + if (PyList_Check(o)) { + const size_t size2 = PyList_Size(o); + (*out)[i].resize(size2); + for (size_t j = 0; j < size2; ++j) { + const PyInputString ustring(PyList_GetItem(o, j)); + if (ustring.IsAvalable()) { + (*out)[i][j] = ustring.str(); + } else { + PyErr_SetString(PyExc_TypeError,"list must contain integers"); + SWIG_fail; + } + resultobj = ustring.input_type(); + } + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + SWIG_fail; + } + } + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + SWIG_fail; + } + $1 = out; +} + +%typemap(in) const std::vector>& { + std::vector> *out = nullptr; + if (PyList_Check($input)) { + const size_t size = PyList_Size($input); + out = new std::vector>(size); + for (size_t i = 0; i < size; ++i) { + PyObject *o = PyList_GetItem($input, i); + if (PyList_Check(o)) { + const size_t size2 = PyList_Size(o); + (*out)[i].resize(size2); + for (size_t j = 0; j < size2; ++j) { + PyObject *o2 = PyList_GetItem(o, j); + if (PyInt_Check(o2)) { + (*out)[i][j] = static_cast(PyInt_AsLong(o2)); + } else { + PyErr_SetString(PyExc_TypeError, "list must contain strings"); + SWIG_fail; + } + } + } else { + PyErr_SetString(PyExc_TypeError, "not a list"); + SWIG_fail; + } + } + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + SWIG_fail; + } + $1 = out; +} + +%typemap(in) const std::unordered_map & { + std::unordered_map *out = nullptr; + if (PyDict_Check($input)) { + PyObject *key, *value; + Py_ssize_t pos = 0; + out = new std::unordered_map; + while (PyDict_Next($input, &pos, &key, &value)) { + const PyInputString key_ustring(key); + const PyInputString value_ustring(value); + if (key_ustring.IsAvalable() && value_ustring.IsAvalable()) { + out->emplace(std::string(key_ustring.data(), key_ustring.size()), + std::string(value_ustring.data(), value_ustring.size())); + } else { + PyErr_SetString(PyExc_TypeError, "map must contain strings."); + SWIG_fail; + } + resultobj = key_ustring.input_type(); + } + } else { + PyErr_SetString(PyExc_TypeError, "not a dictionary"); + SWIG_fail; + } + $1 = out; +} + +%typemap(out) std::vector, float>> { + PyObject *input_type = resultobj; + $result = PyList_New($1.size()); + for (size_t i = 0; i < $1.size(); ++i) { + PyObject *obj = PyList_New($1[i].first.size()); + for (size_t j = 0; j < $1[i].first.size(); ++j) { + PyList_SET_ITEM(obj, j, MakePyOutputString($1[i].first[j], input_type)); + } + PyList_SET_ITEM($result, i, PyTuple_Pack(2, obj, PyFloat_FromDouble(static_cast($1[i].second)))); + } +} + +%typemap(out) std::vector, float>> { + $result = PyList_New($1.size()); + for (size_t i = 0; i < $1.size(); ++i) { + PyObject *obj = PyList_New($1[i].first.size()); + for (size_t j = 0; j < $1[i].first.size(); ++j) { + PyList_SET_ITEM(obj, j, PyInt_FromLong(static_cast($1[i].first[j]))); + } + PyList_SET_ITEM($result, i, PyTuple_Pack(2, obj, PyFloat_FromDouble(static_cast($1[i].second)))); + } +} + +%typemap(out) std::vector { + $result = PyList_New($1.size()); + for (size_t i = 0; i < $1.size(); ++i) { + PyObject *obj = SWIG_NewPointerObj(new sentencepiece::ImmutableSentencePieceText($1.at(i)), SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText, SWIG_POINTER_OWN | 0); + PyList_SET_ITEM($result, i, obj); + } +} + +// Types for normalized string and offset +%typemap(out) std::pair> { + PyObject *input_type = resultobj; + if (PyInputString::IsUnicode(input_type)) { + sentencepiece::ConvertToUnicodeAlignment(arg2, $1.first, &$1.second); + } + PyObject *obj = PyList_New($1.second.size()); + for (size_t i = 0; i < $1.second.size(); ++i) { + PyList_SET_ITEM(obj, i, PyInt_FromLong(static_cast($1.second[i]))); + } + $result = PyTuple_Pack(2, MakePyOutputString($1.first, input_type), obj); +} + +%typemap(in) sentencepiece::SentenceIterator * { + sentencepiece::SentenceIterator *out = nullptr; + if (PyIter_Check($input)) { + out = new PySentenceIterator($input); + } else { + PyErr_SetString(PyExc_TypeError, "not a iterator"); + SWIG_fail; + } + $1 = out; +} + +%typemap(freearg) const std::string& { + delete $1; +} + +%typemap(freearg) const std::vector& { + delete $1; +} + +%typemap(freearg) const std::vector& { + delete $1; +} + +%typemap(freearg) const std::vector>& { + delete $1; +} + +%typemap(freearg) const std::vector& { + delete $1; +} + +%typemap(freearg) const std::vector& { + delete $1; +} + +%typemap(freearg) const std::vector>& { + delete $1; +} + +%typemap(freearg) const std::unordered_map & { + delete $1; +} + +%typemap(freearg) sentencepiece::SentenceIterator * { + delete $1; +} + +%typemap(freearg) sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece { + delete $1; +} + +%typemap(freearg) sentencepiece::ImmutableSentencePieceText { + delete $1; +} + +%typemap(freearg) sentencepiece::ImmutableNBestSentencePieceText { + delete $1; +} + +%include +%include + +%pythoncode %{ + +import re +import csv +import sys +import os +import importlib.resources +from io import StringIO +from io import BytesIO + + +def _add_snake_case(classname): + """Added snake_cased method from CammelCased method.""" + + snake_map = {} + for k, v in classname.__dict__.items(): + if re.match(r'^[A-Z]+', k): + snake = re.sub(r'(?= v.piece_size()): + raise IndexError('piece id is out of range.') + return func(v, n) + + def _batched_func(self, arg): + if type(arg) is list: + return [_func(self, n) for n in arg] + else: + return _func(self, arg) + + setattr(classname, name, _batched_func) + + +_sentencepiece_processor_init_native = SentencePieceProcessor.__init__ +_sentencepiece_normalizer_init_native = SentencePieceNormalizer.__init__ +setattr(SentencePieceProcessor, '__init__', SentencePieceProcessor.Init) +setattr(SentencePieceNormalizer, '__init__', SentencePieceNormalizer.Init) + +SentencePieceProcessor.Tokenize = SentencePieceProcessor.Encode +SentencePieceProcessor.Detokenize = SentencePieceProcessor.Decode + +for m in [ + 'PieceToId', 'IdToPiece', 'GetScore', 'IsUnknown', 'IsControl', 'IsUnused', + 'IsByte' +]: + _batchnize(SentencePieceProcessor, m) + +_add_snake_case(SentencePieceProcessor) +_add_snake_case(SentencePieceTrainer) +_add_snake_case(SentencePieceNormalizer) +set_random_generator_seed = SetRandomGeneratorSeed +set_min_log_level = SetMinLogLevel + +from ._version import __version__ + +SetDataDir(os.path.join(str(importlib.resources.files('sentencepiece')), 'package_data')) + +class _LogStream(object): + def __init__(self, ostream=None): + self.ostream = ostream + if self.ostream is not None: + self.orig_stream_fileno = sys.stderr.fileno() + + def __enter__(self): + if self.ostream is not None: + self.orig_stream_dup = os.dup(self.orig_stream_fileno) + os.dup2(self.ostream.fileno(), self.orig_stream_fileno) + + def __exit__(self, type, value, traceback): + if self.ostream is not None: + os.close(self.orig_stream_fileno) + os.dup2(self.orig_stream_dup, self.orig_stream_fileno) + os.close(self.orig_stream_dup) + self.ostream.close() +%} diff --git a/.venv/lib/python3.12/site-packages/sentencepiece/sentencepiece_model_pb2.py b/.venv/lib/python3.12/site-packages/sentencepiece/sentencepiece_model_pb2.py new file mode 100644 index 0000000000000000000000000000000000000000..b07107d69de178e107544a29bb4d0280b5482241 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentencepiece/sentencepiece_model_pb2.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: sentencepiece_model.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19sentencepiece_model.proto\x12\rsentencepiece\"\x80\x0c\n\x0bTrainerSpec\x12\r\n\x05input\x18\x01 \x03(\t\x12\x14\n\x0cinput_format\x18\x07 \x01(\t\x12\x14\n\x0cmodel_prefix\x18\x02 \x01(\t\x12\x41\n\nmodel_type\x18\x03 \x01(\x0e\x32$.sentencepiece.TrainerSpec.ModelType:\x07UNIGRAM\x12\x18\n\nvocab_size\x18\x04 \x01(\x05:\x04\x38\x30\x30\x30\x12\x17\n\x0f\x61\x63\x63\x65pt_language\x18\x05 \x03(\t\x12 \n\x15self_test_sample_size\x18\x06 \x01(\x05:\x01\x30\x12*\n\x1b\x65nable_differential_privacy\x18\x32 \x01(\x08:\x05\x66\x61lse\x12+\n differential_privacy_noise_level\x18\x33 \x01(\x02:\x01\x30\x12\x32\n\'differential_privacy_clipping_threshold\x18\x34 \x01(\x04:\x01\x30\x12\"\n\x12\x63haracter_coverage\x18\n \x01(\x02:\x06\x30.9995\x12\x1e\n\x13input_sentence_size\x18\x0b \x01(\x04:\x01\x30\x12$\n\x16shuffle_input_sentence\x18\x13 \x01(\x08:\x04true\x12 \n\x14mining_sentence_size\x18\x0c \x01(\x05\x42\x02\x18\x01\x12\"\n\x16training_sentence_size\x18\r \x01(\x05\x42\x02\x18\x01\x12(\n\x17seed_sentencepiece_size\x18\x0e \x01(\x05:\x07\x31\x30\x30\x30\x30\x30\x30\x12\x1e\n\x10shrinking_factor\x18\x0f \x01(\x02:\x04\x30.75\x12!\n\x13max_sentence_length\x18\x12 \x01(\x05:\x04\x34\x31\x39\x32\x12\x17\n\x0bnum_threads\x18\x10 \x01(\x05:\x02\x31\x36\x12\x1d\n\x12num_sub_iterations\x18\x11 \x01(\x05:\x01\x32\x12$\n\x18max_sentencepiece_length\x18\x14 \x01(\x05:\x02\x31\x36\x12%\n\x17split_by_unicode_script\x18\x15 \x01(\x08:\x04true\x12\x1d\n\x0fsplit_by_number\x18\x17 \x01(\x08:\x04true\x12!\n\x13split_by_whitespace\x18\x16 \x01(\x08:\x04true\x12)\n\x1atreat_whitespace_as_suffix\x18\x18 \x01(\x08:\x05\x66\x61lse\x12+\n\x1c\x61llow_whitespace_only_pieces\x18\x1a \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x0csplit_digits\x18\x19 \x01(\x08:\x05\x66\x61lse\x12#\n\x19pretokenization_delimiter\x18\x35 \x01(\t:\x00\x12\x17\n\x0f\x63ontrol_symbols\x18\x1e \x03(\t\x12\x1c\n\x14user_defined_symbols\x18\x1f \x03(\t\x12\x16\n\x0erequired_chars\x18$ \x01(\t\x12\x1c\n\rbyte_fallback\x18# \x01(\x08:\x05\x66\x61lse\x12+\n\x1dvocabulary_output_piece_score\x18 \x01(\x08:\x04true\x12\x1e\n\x10hard_vocab_limit\x18! \x01(\x08:\x04true\x12\x1c\n\ruse_all_vocab\x18\" \x01(\x08:\x05\x66\x61lse\x12\x11\n\x06unk_id\x18( \x01(\x05:\x01\x30\x12\x11\n\x06\x62os_id\x18) \x01(\x05:\x01\x31\x12\x11\n\x06\x65os_id\x18* \x01(\x05:\x01\x32\x12\x12\n\x06pad_id\x18+ \x01(\x05:\x02-1\x12\x18\n\tunk_piece\x18- \x01(\t:\x05\x12\x16\n\tbos_piece\x18. \x01(\t:\x03\x12\x17\n\teos_piece\x18/ \x01(\t:\x04\x12\x18\n\tpad_piece\x18\x30 \x01(\t:\x05\x12\x1a\n\x0bunk_surface\x18, \x01(\t:\x05 \xe2\x81\x87 \x12+\n\x1ctrain_extremely_large_corpus\x18\x31 \x01(\x08:\x05\x66\x61lse\"5\n\tModelType\x12\x0b\n\x07UNIGRAM\x10\x01\x12\x07\n\x03\x42PE\x10\x02\x12\x08\n\x04WORD\x10\x03\x12\x08\n\x04\x43HAR\x10\x04*\t\x08\xc8\x01\x10\x80\x80\x80\x80\x02\"\xd1\x01\n\x0eNormalizerSpec\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x1c\n\x14precompiled_charsmap\x18\x02 \x01(\x0c\x12\x1e\n\x10\x61\x64\x64_dummy_prefix\x18\x03 \x01(\x08:\x04true\x12&\n\x18remove_extra_whitespaces\x18\x04 \x01(\x08:\x04true\x12 \n\x12\x65scape_whitespaces\x18\x05 \x01(\x08:\x04true\x12\x1e\n\x16normalization_rule_tsv\x18\x06 \x01(\t*\t\x08\xc8\x01\x10\x80\x80\x80\x80\x02\"y\n\x0cSelfTestData\x12\x33\n\x07samples\x18\x01 \x03(\x0b\x32\".sentencepiece.SelfTestData.Sample\x1a)\n\x06Sample\x12\r\n\x05input\x18\x01 \x01(\t\x12\x10\n\x08\x65xpected\x18\x02 \x01(\t*\t\x08\xc8\x01\x10\x80\x80\x80\x80\x02\"\xfe\x03\n\nModelProto\x12\x37\n\x06pieces\x18\x01 \x03(\x0b\x32\'.sentencepiece.ModelProto.SentencePiece\x12\x30\n\x0ctrainer_spec\x18\x02 \x01(\x0b\x32\x1a.sentencepiece.TrainerSpec\x12\x36\n\x0fnormalizer_spec\x18\x03 \x01(\x0b\x32\x1d.sentencepiece.NormalizerSpec\x12\x33\n\x0eself_test_data\x18\x04 \x01(\x0b\x32\x1b.sentencepiece.SelfTestData\x12\x38\n\x11\x64\x65normalizer_spec\x18\x05 \x01(\x0b\x32\x1d.sentencepiece.NormalizerSpec\x1a\xd2\x01\n\rSentencePiece\x12\r\n\x05piece\x18\x01 \x01(\t\x12\r\n\x05score\x18\x02 \x01(\x02\x12\x42\n\x04type\x18\x03 \x01(\x0e\x32,.sentencepiece.ModelProto.SentencePiece.Type:\x06NORMAL\"T\n\x04Type\x12\n\n\x06NORMAL\x10\x01\x12\x0b\n\x07UNKNOWN\x10\x02\x12\x0b\n\x07\x43ONTROL\x10\x03\x12\x10\n\x0cUSER_DEFINED\x10\x04\x12\x08\n\x04\x42YTE\x10\x06\x12\n\n\x06UNUSED\x10\x05*\t\x08\xc8\x01\x10\x80\x80\x80\x80\x02*\t\x08\xc8\x01\x10\x80\x80\x80\x80\x02\x42\x02H\x03') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'sentencepiece_model_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'H\003' + _TRAINERSPEC.fields_by_name['mining_sentence_size']._options = None + _TRAINERSPEC.fields_by_name['mining_sentence_size']._serialized_options = b'\030\001' + _TRAINERSPEC.fields_by_name['training_sentence_size']._options = None + _TRAINERSPEC.fields_by_name['training_sentence_size']._serialized_options = b'\030\001' + _TRAINERSPEC._serialized_start=45 + _TRAINERSPEC._serialized_end=1581 + _TRAINERSPEC_MODELTYPE._serialized_start=1517 + _TRAINERSPEC_MODELTYPE._serialized_end=1570 + _NORMALIZERSPEC._serialized_start=1584 + _NORMALIZERSPEC._serialized_end=1793 + _SELFTESTDATA._serialized_start=1795 + _SELFTESTDATA._serialized_end=1916 + _SELFTESTDATA_SAMPLE._serialized_start=1864 + _SELFTESTDATA_SAMPLE._serialized_end=1905 + _MODELPROTO._serialized_start=1919 + _MODELPROTO._serialized_end=2429 + _MODELPROTO_SENTENCEPIECE._serialized_start=2208 + _MODELPROTO_SENTENCEPIECE._serialized_end=2418 + _MODELPROTO_SENTENCEPIECE_TYPE._serialized_start=2323 + _MODELPROTO_SENTENCEPIECE_TYPE._serialized_end=2407 +# @@protoc_insertion_point(module_scope) diff --git a/.venv/lib/python3.12/site-packages/sentencepiece/sentencepiece_pb2.py b/.venv/lib/python3.12/site-packages/sentencepiece/sentencepiece_pb2.py new file mode 100644 index 0000000000000000000000000000000000000000..840cfd2143a47d8a972008bd946a8dd271aa0a0f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentencepiece/sentencepiece_pb2.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: sentencepiece.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13sentencepiece.proto\x12\rsentencepiece\"\xdf\x01\n\x11SentencePieceText\x12\x0c\n\x04text\x18\x01 \x01(\t\x12>\n\x06pieces\x18\x02 \x03(\x0b\x32..sentencepiece.SentencePieceText.SentencePiece\x12\r\n\x05score\x18\x03 \x01(\x02\x1a\x62\n\rSentencePiece\x12\r\n\x05piece\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\r\x12\x0f\n\x07surface\x18\x03 \x01(\t\x12\r\n\x05\x62\x65gin\x18\x04 \x01(\r\x12\x0b\n\x03\x65nd\x18\x05 \x01(\r*\t\x08\xc8\x01\x10\x80\x80\x80\x80\x02*\t\x08\xc8\x01\x10\x80\x80\x80\x80\x02\"J\n\x16NBestSentencePieceText\x12\x30\n\x06nbests\x18\x01 \x03(\x0b\x32 .sentencepiece.SentencePieceTextB\x02H\x03') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'sentencepiece_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'H\003' + _SENTENCEPIECETEXT._serialized_start=39 + _SENTENCEPIECETEXT._serialized_end=262 + _SENTENCEPIECETEXT_SENTENCEPIECE._serialized_start=153 + _SENTENCEPIECETEXT_SENTENCEPIECE._serialized_end=251 + _NBESTSENTENCEPIECETEXT._serialized_start=264 + _NBESTSENTENCEPIECETEXT._serialized_end=338 +# @@protoc_insertion_point(module_scope) diff --git a/.venv/lib/python3.12/site-packages/sentencepiece/sentencepiece_wrap.cxx b/.venv/lib/python3.12/site-packages/sentencepiece/sentencepiece_wrap.cxx new file mode 100644 index 0000000000000000000000000000000000000000..e6c3d659469e6cbe9c7bab2d8349fa289cd758b1 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentencepiece/sentencepiece_wrap.cxx @@ -0,0 +1,10628 @@ +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (https://www.swig.org). + * Version 4.3.0 + * + * Do not make changes to this file unless you know what you are doing - modify + * the SWIG interface file instead. + * ----------------------------------------------------------------------------- */ + + +#define SWIG_VERSION 0x040300 +#define SWIGPYTHON +#define SWIG_PYTHON_DIRECTOR_NO_VTABLE + +#define SWIG_name "_sentencepiece" +/* ----------------------------------------------------------------------------- + * This section contains generic SWIG labels for method/variable + * declarations/attributes, and other compiler dependent labels. + * ----------------------------------------------------------------------------- */ + +/* template workaround for compilers that cannot correctly implement the C++ standard */ +#ifndef SWIGTEMPLATEDISAMBIGUATOR +# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560) +# define SWIGTEMPLATEDISAMBIGUATOR template +# elif defined(__HP_aCC) +/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */ +/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */ +# define SWIGTEMPLATEDISAMBIGUATOR template +# else +# define SWIGTEMPLATEDISAMBIGUATOR +# endif +#endif + +/* inline attribute */ +#ifndef SWIGINLINE +# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__)) +# define SWIGINLINE inline +# else +# define SWIGINLINE +# endif +#endif + +/* attribute recognised by some compilers to avoid 'unused' warnings */ +#ifndef SWIGUNUSED +# if defined(__GNUC__) +# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) +# define SWIGUNUSED __attribute__ ((__unused__)) +# else +# define SWIGUNUSED +# endif +# elif defined(__ICC) +# define SWIGUNUSED __attribute__ ((__unused__)) +# else +# define SWIGUNUSED +# endif +#endif + +#ifndef SWIG_MSC_UNSUPPRESS_4505 +# if defined(_MSC_VER) +# pragma warning(disable : 4505) /* unreferenced local function has been removed */ +# endif +#endif + +#ifndef SWIGUNUSEDPARM +# ifdef __cplusplus +# define SWIGUNUSEDPARM(p) +# else +# define SWIGUNUSEDPARM(p) p SWIGUNUSED +# endif +#endif + +/* internal SWIG method */ +#ifndef SWIGINTERN +# define SWIGINTERN static SWIGUNUSED +#endif + +/* internal inline SWIG method */ +#ifndef SWIGINTERNINLINE +# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE +#endif + +/* exporting methods */ +#if defined(__GNUC__) +# if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +# ifndef GCC_HASCLASSVISIBILITY +# define GCC_HASCLASSVISIBILITY +# endif +# endif +#endif + +#ifndef SWIGEXPORT +# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# if defined(STATIC_LINKED) +# define SWIGEXPORT +# else +# define SWIGEXPORT __declspec(dllexport) +# endif +# else +# if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY) +# define SWIGEXPORT __attribute__ ((visibility("default"))) +# else +# define SWIGEXPORT +# endif +# endif +#endif + +/* calling conventions for Windows */ +#ifndef SWIGSTDCALL +# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# define SWIGSTDCALL __stdcall +# else +# define SWIGSTDCALL +# endif +#endif + +/* Deal with Microsoft's attempt at deprecating C standard runtime functions */ +#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) +# define _CRT_SECURE_NO_DEPRECATE +#endif + +/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */ +#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE) +# define _SCL_SECURE_NO_DEPRECATE +#endif + +/* Deal with Apple's deprecated 'AssertMacros.h' from Carbon-framework */ +#if defined(__APPLE__) && !defined(__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES) +# define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0 +#endif + +/* Intel's compiler complains if a variable which was never initialised is + * cast to void, which is a common idiom which we use to indicate that we + * are aware a variable isn't used. So we just silence that warning. + * See: https://github.com/swig/swig/issues/192 for more discussion. + */ +#ifdef __INTEL_COMPILER +# pragma warning disable 592 +#endif + +#if defined(__cplusplus) && __cplusplus >=201103L +# define SWIG_NULLPTR nullptr +#else +# define SWIG_NULLPTR NULL +#endif + +/* ----------------------------------------------------------------------------- + * swigcompat.swg + * + * Macros to provide support compatibility with older C and C++ standards. + * + * Note that SWIG expects __cplusplus to be defined to the appropriate C++ standard. + * MSVC users are urged to check and examine the /Zc:__cplusplus compiler option. + * See https://learn.microsoft.com/en-us/cpp/build/reference/zc-cplusplus. + * ----------------------------------------------------------------------------- */ + +/* C99 and C++11 should provide snprintf, but define SWIG_NO_SNPRINTF + * if you're missing it. + */ +#if ((defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) || \ + (defined __cplusplus && __cplusplus >= 201103L) || \ + defined SWIG_HAVE_SNPRINTF) && \ + !defined SWIG_NO_SNPRINTF +# define SWIG_snprintf(O,S,F,A) snprintf(O,S,F,A) +# define SWIG_snprintf2(O,S,F,A,B) snprintf(O,S,F,A,B) +#else +/* Fallback versions ignore the buffer size, but most of our uses either have a + * fixed maximum possible size or dynamically allocate a buffer that's large + * enough. + */ +# define SWIG_snprintf(O,S,F,A) sprintf(O,F,A) +# define SWIG_snprintf2(O,S,F,A,B) sprintf(O,F,A,B) +#endif + + +#if defined(__GNUC__) && defined(_WIN32) && !defined(SWIG_PYTHON_NO_HYPOT_WORKAROUND) +/* Workaround for '::hypot' has not been declared', see https://bugs.python.org/issue11566 */ +# include +#endif + +#if !defined(PY_SSIZE_T_CLEAN) && !defined(SWIG_NO_PY_SSIZE_T_CLEAN) +#define PY_SSIZE_T_CLEAN +#endif + +#if __GNUC__ >= 7 +#pragma GCC diagnostic push +#if defined(__cplusplus) && __cplusplus >=201703L +#pragma GCC diagnostic ignored "-Wregister" /* For python-2.7 headers that use register */ +#endif +#endif + +#if defined(_DEBUG) && defined(SWIG_PYTHON_INTERPRETER_NO_DEBUG) +/* Use debug wrappers with the Python release dll */ + +#if defined(_MSC_VER) && _MSC_VER >= 1929 +/* Workaround compilation errors when redefining _DEBUG in MSVC 2019 version 16.10 and later + * See https://github.com/swig/swig/issues/2090 */ +# include +#endif + +# undef _DEBUG +# include +# define _DEBUG 1 +#else +# include +#endif + +#if !defined(SWIGPYTHON_BUILTIN) && PY_VERSION_HEX >= 0x03030000 +# define SWIG_HEAPTYPES + +/* Note: Currently this won't activate - it is in place ready for when the + * SWIGPYTHON_BUILTIN condition above gets removed. */ +# if PY_VERSION_HEX < 0x030c0000 && defined(SWIGPYTHON_BUILTIN) +# include +# define Py_READONLY READONLY +# define Py_T_PYSSIZET T_PYSSIZET +# endif +#endif + +#if __GNUC__ >= 7 +#pragma GCC diagnostic pop +#endif + +#include +#include + +/* ----------------------------------------------------------------------------- + * swigrun.swg + * + * This file contains generic C API SWIG runtime support for pointer + * type checking. + * ----------------------------------------------------------------------------- */ + +/* This should only be incremented when either the layout of swig_type_info changes, + or for whatever reason, the runtime changes incompatibly */ +#define SWIG_RUNTIME_VERSION "4" + +/* define SWIG_TYPE_TABLE_NAME as "SWIG_TYPE_TABLE" */ +#ifdef SWIG_TYPE_TABLE +# define SWIG_QUOTE_STRING(x) #x +# define SWIG_EXPAND_AND_QUOTE_STRING(x) SWIG_QUOTE_STRING(x) +# define SWIG_TYPE_TABLE_NAME SWIG_EXPAND_AND_QUOTE_STRING(SWIG_TYPE_TABLE) +#else +# define SWIG_TYPE_TABLE_NAME +#endif + +/* + You can use the SWIGRUNTIME and SWIGRUNTIMEINLINE macros for + creating a static or dynamic library from the SWIG runtime code. + In 99.9% of the cases, SWIG just needs to declare them as 'static'. + + But only do this if strictly necessary, ie, if you have problems + with your compiler or suchlike. +*/ + +#ifndef SWIGRUNTIME +# define SWIGRUNTIME SWIGINTERN +#endif + +#ifndef SWIGRUNTIMEINLINE +# define SWIGRUNTIMEINLINE SWIGRUNTIME SWIGINLINE +#endif + +/* Generic buffer size */ +#ifndef SWIG_BUFFER_SIZE +# define SWIG_BUFFER_SIZE 1024 +#endif + +/* Flags for pointer conversions */ +#define SWIG_POINTER_DISOWN 0x1 +#define SWIG_CAST_NEW_MEMORY 0x2 +#define SWIG_POINTER_NO_NULL 0x4 +#define SWIG_POINTER_CLEAR 0x8 +#define SWIG_POINTER_RELEASE (SWIG_POINTER_CLEAR | SWIG_POINTER_DISOWN) + +/* Flags for new pointer objects */ +#define SWIG_POINTER_OWN 0x1 + + +/* + Flags/methods for returning states. + + The SWIG conversion methods, as ConvertPtr, return an integer + that tells if the conversion was successful or not. And if not, + an error code can be returned (see swigerrors.swg for the codes). + + Use the following macros/flags to set or process the returning + states. + + In old versions of SWIG, code such as the following was usually written: + + if (SWIG_ConvertPtr(obj,vptr,ty.flags) != -1) { + // success code + } else { + //fail code + } + + Now you can be more explicit: + + int res = SWIG_ConvertPtr(obj,vptr,ty.flags); + if (SWIG_IsOK(res)) { + // success code + } else { + // fail code + } + + which is the same really, but now you can also do + + Type *ptr; + int res = SWIG_ConvertPtr(obj,(void **)(&ptr),ty.flags); + if (SWIG_IsOK(res)) { + // success code + if (SWIG_IsNewObj(res) { + ... + delete *ptr; + } else { + ... + } + } else { + // fail code + } + + I.e., now SWIG_ConvertPtr can return new objects and you can + identify the case and take care of the deallocation. Of course that + also requires SWIG_ConvertPtr to return new result values, such as + + int SWIG_ConvertPtr(obj, ptr,...) { + if () { + if () { + *ptr = ; + return SWIG_NEWOBJ; + } else { + *ptr = ; + return SWIG_OLDOBJ; + } + } else { + return SWIG_BADOBJ; + } + } + + Of course, returning the plain '0(success)/-1(fail)' still works, but you can be + more explicit by returning SWIG_BADOBJ, SWIG_ERROR or any of the + SWIG errors code. + + Finally, if the SWIG_CASTRANK_MODE is enabled, the result code + allows returning the 'cast rank', for example, if you have this + + int food(double) + int fooi(int); + + and you call + + food(1) // cast rank '1' (1 -> 1.0) + fooi(1) // cast rank '0' + + just use the SWIG_AddCast()/SWIG_CheckState() +*/ + +#define SWIG_OK (0) +/* Runtime errors are < 0 */ +#define SWIG_ERROR (-1) +/* Errors in range -1 to -99 are in swigerrors.swg (errors for all languages including those not using the runtime) */ +/* Errors in range -100 to -199 are language specific errors defined in *errors.swg */ +/* Errors < -200 are generic runtime specific errors */ +#define SWIG_ERROR_RELEASE_NOT_OWNED (-200) + +#define SWIG_IsOK(r) (r >= 0) +#define SWIG_ArgError(r) ((r != SWIG_ERROR) ? r : SWIG_TypeError) + +/* The CastRankLimit says how many bits are used for the cast rank */ +#define SWIG_CASTRANKLIMIT (1 << 8) +/* The NewMask denotes the object was created (using new/malloc) */ +#define SWIG_NEWOBJMASK (SWIG_CASTRANKLIMIT << 1) +/* The TmpMask is for in/out typemaps that use temporary objects */ +#define SWIG_TMPOBJMASK (SWIG_NEWOBJMASK << 1) +/* Simple returning values */ +#define SWIG_BADOBJ (SWIG_ERROR) +#define SWIG_OLDOBJ (SWIG_OK) +#define SWIG_NEWOBJ (SWIG_OK | SWIG_NEWOBJMASK) +#define SWIG_TMPOBJ (SWIG_OK | SWIG_TMPOBJMASK) +/* Check, add and del object mask methods */ +#define SWIG_AddNewMask(r) (SWIG_IsOK(r) ? (r | SWIG_NEWOBJMASK) : r) +#define SWIG_DelNewMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_NEWOBJMASK) : r) +#define SWIG_IsNewObj(r) (SWIG_IsOK(r) && (r & SWIG_NEWOBJMASK)) +#define SWIG_AddTmpMask(r) (SWIG_IsOK(r) ? (r | SWIG_TMPOBJMASK) : r) +#define SWIG_DelTmpMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_TMPOBJMASK) : r) +#define SWIG_IsTmpObj(r) (SWIG_IsOK(r) && (r & SWIG_TMPOBJMASK)) + +/* Cast-Rank Mode */ +#if defined(SWIG_CASTRANK_MODE) +# ifndef SWIG_TypeRank +# define SWIG_TypeRank unsigned long +# endif +# ifndef SWIG_MAXCASTRANK /* Default cast allowed */ +# define SWIG_MAXCASTRANK (2) +# endif +# define SWIG_CASTRANKMASK ((SWIG_CASTRANKLIMIT) -1) +# define SWIG_CastRank(r) (r & SWIG_CASTRANKMASK) +SWIGINTERNINLINE int SWIG_AddCast(int r) { + return SWIG_IsOK(r) ? ((SWIG_CastRank(r) < SWIG_MAXCASTRANK) ? (r + 1) : SWIG_ERROR) : r; +} +SWIGINTERNINLINE int SWIG_CheckState(int r) { + return SWIG_IsOK(r) ? SWIG_CastRank(r) + 1 : 0; +} +#else /* no cast-rank mode */ +# define SWIG_AddCast(r) (r) +# define SWIG_CheckState(r) (SWIG_IsOK(r) ? 1 : 0) +#endif + + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *(*swig_converter_func)(void *, int *); +typedef struct swig_type_info *(*swig_dycast_func)(void **); + +/* Structure to store information on one type */ +typedef struct swig_type_info { + const char *name; /* mangled name of this type */ + const char *str; /* human readable name of this type */ + swig_dycast_func dcast; /* dynamic cast function down a hierarchy */ + struct swig_cast_info *cast; /* linked list of types that can cast into this type */ + void *clientdata; /* language specific type data */ + int owndata; /* flag if the structure owns the clientdata */ +} swig_type_info; + +/* Structure to store a type and conversion function used for casting */ +typedef struct swig_cast_info { + swig_type_info *type; /* pointer to type that is equivalent to this type */ + swig_converter_func converter; /* function to cast the void pointers */ + struct swig_cast_info *next; /* pointer to next cast in linked list */ + struct swig_cast_info *prev; /* pointer to the previous cast */ +} swig_cast_info; + +/* Structure used to store module information + * Each module generates one structure like this, and the runtime collects + * all of these structures and stores them in a circularly linked list.*/ +typedef struct swig_module_info { + swig_type_info **types; /* Array of pointers to swig_type_info structures that are in this module */ + size_t size; /* Number of types in this module */ + struct swig_module_info *next; /* Pointer to next element in circularly linked list */ + swig_type_info **type_initial; /* Array of initially generated type structures */ + swig_cast_info **cast_initial; /* Array of initially generated casting structures */ + void *clientdata; /* Language specific module data */ +} swig_module_info; + +/* + Compare two type names skipping the space characters, therefore + "char*" == "char *" and "Class" == "Class", etc. + + Return 0 when the two name types are equivalent, as in + strncmp, but skipping ' '. +*/ +SWIGRUNTIME int +SWIG_TypeNameComp(const char *f1, const char *l1, + const char *f2, const char *l2) { + for (;(f1 != l1) && (f2 != l2); ++f1, ++f2) { + while ((*f1 == ' ') && (f1 != l1)) ++f1; + while ((*f2 == ' ') && (f2 != l2)) ++f2; + if (*f1 != *f2) return (*f1 > *f2) ? 1 : -1; + } + return (int)((l1 - f1) - (l2 - f2)); +} + +/* + Check type equivalence in a name list like ||... + Return 0 if equal, -1 if nb < tb, 1 if nb > tb +*/ +SWIGRUNTIME int +SWIG_TypeCmp(const char *nb, const char *tb) { + int equiv = 1; + const char* te = tb + strlen(tb); + const char* ne = nb; + while (equiv != 0 && *ne) { + for (nb = ne; *ne; ++ne) { + if (*ne == '|') break; + } + equiv = SWIG_TypeNameComp(nb, ne, tb, te); + if (*ne) ++ne; + } + return equiv; +} + +/* + Check type equivalence in a name list like ||... + Return 0 if not equal, 1 if equal +*/ +SWIGRUNTIME int +SWIG_TypeEquiv(const char *nb, const char *tb) { + return SWIG_TypeCmp(nb, tb) == 0 ? 1 : 0; +} + +/* + Check the typename +*/ +SWIGRUNTIME swig_cast_info * +SWIG_TypeCheck(const char *c, swig_type_info *ty) { + if (ty) { + swig_cast_info *iter = ty->cast; + while (iter) { + if (strcmp(iter->type->name, c) == 0) { + if (iter == ty->cast) + return iter; + /* Move iter to the top of the linked list */ + iter->prev->next = iter->next; + if (iter->next) + iter->next->prev = iter->prev; + iter->next = ty->cast; + iter->prev = 0; + if (ty->cast) ty->cast->prev = iter; + ty->cast = iter; + return iter; + } + iter = iter->next; + } + } + return 0; +} + +/* + Identical to SWIG_TypeCheck, except strcmp is replaced with a pointer comparison +*/ +SWIGRUNTIME swig_cast_info * +SWIG_TypeCheckStruct(const swig_type_info *from, swig_type_info *ty) { + if (ty) { + swig_cast_info *iter = ty->cast; + while (iter) { + if (iter->type == from) { + if (iter == ty->cast) + return iter; + /* Move iter to the top of the linked list */ + iter->prev->next = iter->next; + if (iter->next) + iter->next->prev = iter->prev; + iter->next = ty->cast; + iter->prev = 0; + if (ty->cast) ty->cast->prev = iter; + ty->cast = iter; + return iter; + } + iter = iter->next; + } + } + return 0; +} + +/* + Cast a pointer up an inheritance hierarchy +*/ +SWIGRUNTIMEINLINE void * +SWIG_TypeCast(swig_cast_info *ty, void *ptr, int *newmemory) { + return ((!ty) || (!ty->converter)) ? ptr : (*ty->converter)(ptr, newmemory); +} + +/* + Dynamic pointer casting. Down an inheritance hierarchy +*/ +SWIGRUNTIME swig_type_info * +SWIG_TypeDynamicCast(swig_type_info *ty, void **ptr) { + swig_type_info *lastty = ty; + if (!ty || !ty->dcast) return ty; + while (ty && (ty->dcast)) { + ty = (*ty->dcast)(ptr); + if (ty) lastty = ty; + } + return lastty; +} + +/* + Return the name associated with this type +*/ +SWIGRUNTIMEINLINE const char * +SWIG_TypeName(const swig_type_info *ty) { + return ty->name; +} + +/* + Return the pretty name associated with this type, + that is an unmangled type name in a form presentable to the user. +*/ +SWIGRUNTIME const char * +SWIG_TypePrettyName(const swig_type_info *type) { + /* The "str" field contains the equivalent pretty names of the + type, separated by vertical-bar characters. Choose the last + name. It should be the most specific; a fully resolved name + but not necessarily with default template parameters expanded. */ + if (!type) return NULL; + if (type->str != NULL) { + const char *last_name = type->str; + const char *s; + for (s = type->str; *s; s++) + if (*s == '|') last_name = s+1; + return last_name; + } + else + return type->name; +} + +/* + Set the clientdata field for a type +*/ +SWIGRUNTIME void +SWIG_TypeClientData(swig_type_info *ti, void *clientdata) { + swig_cast_info *cast = ti->cast; + /* if (ti->clientdata == clientdata) return; */ + ti->clientdata = clientdata; + + while (cast) { + if (!cast->converter) { + swig_type_info *tc = cast->type; + if (!tc->clientdata) { + SWIG_TypeClientData(tc, clientdata); + } + } + cast = cast->next; + } +} +SWIGRUNTIME void +SWIG_TypeNewClientData(swig_type_info *ti, void *clientdata) { + SWIG_TypeClientData(ti, clientdata); + ti->owndata = 1; +} + +/* + Search for a swig_type_info structure only by mangled name + Search is a O(log #types) + + We start searching at module start, and finish searching when start == end. + Note: if start == end at the beginning of the function, we go all the way around + the circular list. +*/ +SWIGRUNTIME swig_type_info * +SWIG_MangledTypeQueryModule(swig_module_info *start, + swig_module_info *end, + const char *name) { + swig_module_info *iter = start; + do { + if (iter->size) { + size_t l = 0; + size_t r = iter->size - 1; + do { + /* since l+r >= 0, we can (>> 1) instead (/ 2) */ + size_t i = (l + r) >> 1; + const char *iname = iter->types[i]->name; + if (iname) { + int compare = strcmp(name, iname); + if (compare == 0) { + return iter->types[i]; + } else if (compare < 0) { + if (i) { + r = i - 1; + } else { + break; + } + } else if (compare > 0) { + l = i + 1; + } + } else { + break; /* should never happen */ + } + } while (l <= r); + } + iter = iter->next; + } while (iter != end); + return 0; +} + +/* + Search for a swig_type_info structure for either a mangled name or a human readable name. + It first searches the mangled names of the types, which is a O(log #types) + If a type is not found it then searches the human readable names, which is O(#types). + + We start searching at module start, and finish searching when start == end. + Note: if start == end at the beginning of the function, we go all the way around + the circular list. +*/ +SWIGRUNTIME swig_type_info * +SWIG_TypeQueryModule(swig_module_info *start, + swig_module_info *end, + const char *name) { + /* STEP 1: Search the name field using binary search */ + swig_type_info *ret = SWIG_MangledTypeQueryModule(start, end, name); + if (ret) { + return ret; + } else { + /* STEP 2: If the type hasn't been found, do a complete search + of the str field (the human readable name) */ + swig_module_info *iter = start; + do { + size_t i = 0; + for (; i < iter->size; ++i) { + if (iter->types[i]->str && (SWIG_TypeEquiv(iter->types[i]->str, name))) + return iter->types[i]; + } + iter = iter->next; + } while (iter != end); + } + + /* neither found a match */ + return 0; +} + +/* + Pack binary data into a string +*/ +SWIGRUNTIME char * +SWIG_PackData(char *c, void *ptr, size_t sz) { + static const char hex[17] = "0123456789abcdef"; + const unsigned char *u = (unsigned char *) ptr; + const unsigned char *eu = u + sz; + for (; u != eu; ++u) { + unsigned char uu = *u; + *(c++) = hex[(uu & 0xf0) >> 4]; + *(c++) = hex[uu & 0xf]; + } + return c; +} + +/* + Unpack binary data from a string +*/ +SWIGRUNTIME const char * +SWIG_UnpackData(const char *c, void *ptr, size_t sz) { + unsigned char *u = (unsigned char *) ptr; + const unsigned char *eu = u + sz; + for (; u != eu; ++u) { + char d = *(c++); + unsigned char uu; + if ((d >= '0') && (d <= '9')) + uu = (unsigned char)((d - '0') << 4); + else if ((d >= 'a') && (d <= 'f')) + uu = (unsigned char)((d - ('a'-10)) << 4); + else + return (char *) 0; + d = *(c++); + if ((d >= '0') && (d <= '9')) + uu |= (unsigned char)(d - '0'); + else if ((d >= 'a') && (d <= 'f')) + uu |= (unsigned char)(d - ('a'-10)); + else + return (char *) 0; + *u = uu; + } + return c; +} + +/* + Pack 'void *' into a string buffer. +*/ +SWIGRUNTIME char * +SWIG_PackVoidPtr(char *buff, void *ptr, const char *name, size_t bsz) { + char *r = buff; + if ((2*sizeof(void *) + 2) > bsz) return 0; + *(r++) = '_'; + r = SWIG_PackData(r,&ptr,sizeof(void *)); + if (strlen(name) + 1 > (bsz - (r - buff))) return 0; + strcpy(r,name); + return buff; +} + +SWIGRUNTIME const char * +SWIG_UnpackVoidPtr(const char *c, void **ptr, const char *name) { + if (*c != '_') { + if (strcmp(c,"NULL") == 0) { + *ptr = (void *) 0; + return name; + } else { + return 0; + } + } + return SWIG_UnpackData(++c,ptr,sizeof(void *)); +} + +SWIGRUNTIME char * +SWIG_PackDataName(char *buff, void *ptr, size_t sz, const char *name, size_t bsz) { + char *r = buff; + size_t lname = (name ? strlen(name) : 0); + if ((2*sz + 2 + lname) > bsz) return 0; + *(r++) = '_'; + r = SWIG_PackData(r,ptr,sz); + if (lname) { + strncpy(r,name,lname+1); + } else { + *r = 0; + } + return buff; +} + +SWIGRUNTIME const char * +SWIG_UnpackDataName(const char *c, void *ptr, size_t sz, const char *name) { + if (*c != '_') { + if (strcmp(c,"NULL") == 0) { + memset(ptr,0,sz); + return name; + } else { + return 0; + } + } + return SWIG_UnpackData(++c,ptr,sz); +} + +#ifdef __cplusplus +} +#endif + +/* SWIG Errors applicable to all language modules, values are reserved from -1 to -99 */ +#define SWIG_UnknownError -1 +#define SWIG_IOError -2 +#define SWIG_RuntimeError -3 +#define SWIG_IndexError -4 +#define SWIG_TypeError -5 +#define SWIG_DivisionByZero -6 +#define SWIG_OverflowError -7 +#define SWIG_SyntaxError -8 +#define SWIG_ValueError -9 +#define SWIG_SystemError -10 +#define SWIG_AttributeError -11 +#define SWIG_MemoryError -12 +#define SWIG_NullReferenceError -13 + + +/* Compatibility macros for Python 3 */ +#if PY_VERSION_HEX >= 0x03000000 + +#define PyClass_Check(obj) PyObject_IsInstance(obj, (PyObject *)&PyType_Type) +#define PyInt_Check(x) PyLong_Check(x) +#define PyInt_AsLong(x) PyLong_AsLong(x) +#define PyInt_FromLong(x) PyLong_FromLong(x) +#define PyInt_FromSize_t(x) PyLong_FromSize_t(x) +#define PyString_Check(name) PyBytes_Check(name) +#define PyString_FromString(x) PyUnicode_FromString(x) +#define PyString_Format(fmt, args) PyUnicode_Format(fmt, args) +#define PyString_AsString(str) PyBytes_AsString(str) +#define PyString_Size(str) PyBytes_Size(str) +#define PyString_InternFromString(key) PyUnicode_InternFromString(key) +#define Py_TPFLAGS_HAVE_CLASS Py_TPFLAGS_BASETYPE +#define _PyLong_FromSsize_t(x) PyLong_FromSsize_t(x) + +#endif + +/* SWIG APIs for compatibility of both Python 2 & 3 */ + +#if PY_VERSION_HEX >= 0x03000000 +# define SWIG_Python_str_FromFormat PyUnicode_FromFormat +#else +# define SWIG_Python_str_FromFormat PyString_FromFormat +#endif + + +/* Wrapper around PyUnicode_AsUTF8AndSize - call Py_XDECREF on the returned pbytes when finished with the returned string */ +SWIGINTERN const char * +SWIG_PyUnicode_AsUTF8AndSize(PyObject *str, Py_ssize_t *psize, PyObject **pbytes) +{ +#if PY_VERSION_HEX >= 0x03030000 +# if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000 + *pbytes = NULL; + return PyUnicode_AsUTF8AndSize(str, psize); +# else + const char *chars; + *pbytes = PyUnicode_AsUTF8String(str); + chars = *pbytes ? PyBytes_AsString(*pbytes) : NULL; + if (chars && psize) + *psize = PyBytes_Size(*pbytes); + return chars; +# endif +#else + char *chars = NULL; + *pbytes = NULL; + PyString_AsStringAndSize(str, &chars, psize); + return chars; +#endif +} + +SWIGINTERN PyObject* +SWIG_Python_str_FromChar(const char *c) +{ +#if PY_VERSION_HEX >= 0x03000000 + return PyUnicode_FromString(c); +#else + return PyString_FromString(c); +#endif +} + +/* SWIGPY_USE_CAPSULE is no longer used within SWIG itself, but some user interface files check for it. */ +# define SWIGPY_USE_CAPSULE +#ifdef SWIGPYTHON_BUILTIN +# define SWIGPY_CAPSULE_ATTR_NAME "type_pointer_capsule_builtin" SWIG_TYPE_TABLE_NAME +#else +# define SWIGPY_CAPSULE_ATTR_NAME "type_pointer_capsule" SWIG_TYPE_TABLE_NAME +#endif +# define SWIGPY_CAPSULE_NAME ("swig_runtime_data" SWIG_RUNTIME_VERSION "." SWIGPY_CAPSULE_ATTR_NAME) + +#if PY_VERSION_HEX < 0x03020000 +#define PyDescr_TYPE(x) (((PyDescrObject *)(x))->d_type) +#define PyDescr_NAME(x) (((PyDescrObject *)(x))->d_name) +#define Py_hash_t long +#endif + +#ifdef Py_LIMITED_API +# define PyTuple_GET_ITEM PyTuple_GetItem +/* Note that PyTuple_SetItem() has different semantics from PyTuple_SET_ITEM as it decref's the original tuple item, so in general they cannot be used + interchangeably. However in SWIG-generated code PyTuple_SET_ITEM is only used with newly initialized tuples without any items and for them this does work. */ +# define PyTuple_SET_ITEM PyTuple_SetItem +# define PyTuple_GET_SIZE PyTuple_Size +# define PyCFunction_GET_FLAGS PyCFunction_GetFlags +# define PyCFunction_GET_FUNCTION PyCFunction_GetFunction +# define PyCFunction_GET_SELF PyCFunction_GetSelf +# define PyList_GET_ITEM PyList_GetItem +# define PyList_SET_ITEM PyList_SetItem +# define PySliceObject PyObject +#endif + +/* Increment and Decrement wrappers - for portability when using the stable abi and for performance otherwise */ +#ifdef Py_LIMITED_API +# define SWIG_Py_INCREF Py_IncRef +# define SWIG_Py_XINCREF Py_IncRef +# define SWIG_Py_DECREF Py_DecRef +# define SWIG_Py_XDECREF Py_DecRef +#else +# define SWIG_Py_INCREF Py_INCREF +# define SWIG_Py_XINCREF Py_XINCREF +# define SWIG_Py_DECREF Py_DECREF +# define SWIG_Py_XDECREF Py_XDECREF +#endif + +/* ----------------------------------------------------------------------------- + * error manipulation + * ----------------------------------------------------------------------------- */ + +SWIGRUNTIME PyObject* +SWIG_Python_ErrorType(int code) { + PyObject* type = 0; + switch(code) { + case SWIG_MemoryError: + type = PyExc_MemoryError; + break; + case SWIG_IOError: + type = PyExc_IOError; + break; + case SWIG_RuntimeError: + type = PyExc_RuntimeError; + break; + case SWIG_IndexError: + type = PyExc_IndexError; + break; + case SWIG_TypeError: + type = PyExc_TypeError; + break; + case SWIG_DivisionByZero: + type = PyExc_ZeroDivisionError; + break; + case SWIG_OverflowError: + type = PyExc_OverflowError; + break; + case SWIG_SyntaxError: + type = PyExc_SyntaxError; + break; + case SWIG_ValueError: + type = PyExc_ValueError; + break; + case SWIG_SystemError: + type = PyExc_SystemError; + break; + case SWIG_AttributeError: + type = PyExc_AttributeError; + break; + case SWIG_NullReferenceError: + type = PyExc_TypeError; + break; + default: + type = PyExc_RuntimeError; + } + return type; +} + + +SWIGRUNTIME void +SWIG_Python_AddErrorMsg(const char* mesg) +{ + PyObject *type = 0; + PyObject *value = 0; + PyObject *traceback = 0; + + if (PyErr_Occurred()) + PyErr_Fetch(&type, &value, &traceback); + if (value) { + PyObject *old_str = PyObject_Str(value); + PyObject *bytes = NULL; + const char *tmp = SWIG_PyUnicode_AsUTF8AndSize(old_str, NULL, &bytes); + PyErr_Clear(); + SWIG_Py_XINCREF(type); + if (tmp) + PyErr_Format(type, "%s %s", tmp, mesg); + else + PyErr_Format(type, "%s", mesg); + SWIG_Py_XDECREF(bytes); + SWIG_Py_DECREF(old_str); + SWIG_Py_DECREF(value); + } else { + PyErr_SetString(PyExc_RuntimeError, mesg); + } +} + +SWIGRUNTIME int +SWIG_Python_TypeErrorOccurred(PyObject *obj) +{ + PyObject *error; + if (obj) + return 0; + error = PyErr_Occurred(); + return error && PyErr_GivenExceptionMatches(error, PyExc_TypeError); +} + +SWIGRUNTIME void +SWIG_Python_RaiseOrModifyTypeError(const char *message) +{ + if (SWIG_Python_TypeErrorOccurred(NULL)) { + /* Use existing TypeError to preserve stacktrace and enhance with given message */ + PyObject *newvalue; + PyObject *type = NULL, *value = NULL, *traceback = NULL; + PyErr_Fetch(&type, &value, &traceback); +#if PY_VERSION_HEX >= 0x03000000 + newvalue = PyUnicode_FromFormat("%S\nAdditional information:\n%s", value, message); +#else + newvalue = PyString_FromFormat("%s\nAdditional information:\n%s", PyString_AsString(value), message); +#endif + if (newvalue) { + SWIG_Py_XDECREF(value); + PyErr_Restore(type, newvalue, traceback); + } else { + PyErr_Restore(type, value, traceback); + } + } else { + /* Raise TypeError using given message */ + PyErr_SetString(PyExc_TypeError, message); + } +} + +#if defined(SWIG_PYTHON_NO_THREADS) +# if defined(SWIG_PYTHON_THREADS) +# undef SWIG_PYTHON_THREADS +# endif +#endif +#if defined(SWIG_PYTHON_THREADS) /* Threading support is enabled */ +# if !defined(SWIG_PYTHON_USE_GIL) && !defined(SWIG_PYTHON_NO_USE_GIL) +# define SWIG_PYTHON_USE_GIL +# endif +# if defined(SWIG_PYTHON_USE_GIL) /* Use PyGILState threads calls */ +# if !defined(SWIG_PYTHON_INITIALIZE_THREADS) +# if PY_VERSION_HEX < 0x03070000 +# define SWIG_PYTHON_INITIALIZE_THREADS PyEval_InitThreads() +# else +# define SWIG_PYTHON_INITIALIZE_THREADS +# endif +# endif +# ifdef __cplusplus /* C++ code */ + class SWIG_Python_Thread_Block { + bool status; + PyGILState_STATE state; + public: + void end() { if (status) { PyGILState_Release(state); status = false;} } + SWIG_Python_Thread_Block() : status(true), state(PyGILState_Ensure()) {} + ~SWIG_Python_Thread_Block() { end(); } + }; + class SWIG_Python_Thread_Allow { + bool status; + PyThreadState *save; + public: + void end() { if (status) { status = false; PyEval_RestoreThread(save); }} + SWIG_Python_Thread_Allow() : status(true), save(PyEval_SaveThread()) {} + ~SWIG_Python_Thread_Allow() { end(); } + }; +# define SWIG_PYTHON_THREAD_BEGIN_BLOCK SWIG_Python_Thread_Block _swig_thread_block +# define SWIG_PYTHON_THREAD_END_BLOCK _swig_thread_block.end() +# define SWIG_PYTHON_THREAD_BEGIN_ALLOW SWIG_Python_Thread_Allow _swig_thread_allow +# define SWIG_PYTHON_THREAD_END_ALLOW _swig_thread_allow.end() +# else /* C code */ +# define SWIG_PYTHON_THREAD_BEGIN_BLOCK PyGILState_STATE _swig_thread_block = PyGILState_Ensure() +# define SWIG_PYTHON_THREAD_END_BLOCK PyGILState_Release(_swig_thread_block) +# define SWIG_PYTHON_THREAD_BEGIN_ALLOW PyThreadState *_swig_thread_allow = PyEval_SaveThread() +# define SWIG_PYTHON_THREAD_END_ALLOW PyEval_RestoreThread(_swig_thread_allow) +# endif +# else /* Old thread way, not implemented, user must provide it */ +# if !defined(SWIG_PYTHON_INITIALIZE_THREADS) +# define SWIG_PYTHON_INITIALIZE_THREADS +# endif +# if !defined(SWIG_PYTHON_THREAD_BEGIN_BLOCK) +# define SWIG_PYTHON_THREAD_BEGIN_BLOCK +# endif +# if !defined(SWIG_PYTHON_THREAD_END_BLOCK) +# define SWIG_PYTHON_THREAD_END_BLOCK +# endif +# if !defined(SWIG_PYTHON_THREAD_BEGIN_ALLOW) +# define SWIG_PYTHON_THREAD_BEGIN_ALLOW +# endif +# if !defined(SWIG_PYTHON_THREAD_END_ALLOW) +# define SWIG_PYTHON_THREAD_END_ALLOW +# endif +# endif +#else /* No thread support */ +# define SWIG_PYTHON_INITIALIZE_THREADS +# define SWIG_PYTHON_THREAD_BEGIN_BLOCK +# define SWIG_PYTHON_THREAD_END_BLOCK +# define SWIG_PYTHON_THREAD_BEGIN_ALLOW +# define SWIG_PYTHON_THREAD_END_ALLOW +#endif + +/* ----------------------------------------------------------------------------- + * Python API portion that goes into the runtime + * ----------------------------------------------------------------------------- */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* ----------------------------------------------------------------------------- + * Constant declarations + * ----------------------------------------------------------------------------- */ + +/* Constant Types */ +#define SWIG_PY_POINTER 4 +#define SWIG_PY_BINARY 5 + +/* Constant information structure */ +typedef struct swig_const_info { + int type; + const char *name; + long lvalue; + double dvalue; + void *pvalue; + swig_type_info **ptype; +} swig_const_info; + +#ifdef __cplusplus +} +#endif + + +/* ----------------------------------------------------------------------------- + * pyrun.swg + * + * This file contains the runtime support for Python modules + * and includes code for managing global variables and pointer + * type checking. + * + * ----------------------------------------------------------------------------- */ + +#if PY_VERSION_HEX < 0x02070000 /* 2.7.0 */ +# error "This version of SWIG only supports Python >= 2.7" +#endif + +#if PY_VERSION_HEX >= 0x03000000 && PY_VERSION_HEX < 0x03030000 +# error "This version of SWIG only supports Python 3 >= 3.3" +#endif + +/* Common SWIG API */ + +/* for raw pointers */ +#define SWIG_Python_ConvertPtr(obj, pptr, type, flags) SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, 0) +#define SWIG_ConvertPtr(obj, pptr, type, flags) SWIG_Python_ConvertPtr(obj, pptr, type, flags) +#define SWIG_ConvertPtrAndOwn(obj,pptr,type,flags,own) SWIG_Python_ConvertPtrAndOwn(obj, pptr, type, flags, own) + +#ifdef SWIGPYTHON_BUILTIN +#define SWIG_NewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(self, ptr, type, flags) +#else +#define SWIG_NewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(NULL, ptr, type, flags) +#endif + +#define SWIG_InternalNewPointerObj(ptr, type, flags) SWIG_Python_NewPointerObj(NULL, ptr, type, flags) + +#define SWIG_CheckImplicit(ty) SWIG_Python_CheckImplicit(ty) +#define SWIG_AcquirePtr(ptr, src) SWIG_Python_AcquirePtr(ptr, src) +#define swig_owntype int + +/* for raw packed data */ +#define SWIG_ConvertPacked(obj, ptr, sz, ty) SWIG_Python_ConvertPacked(obj, ptr, sz, ty) +#define SWIG_NewPackedObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type) + +/* for class or struct pointers */ +#define SWIG_ConvertInstance(obj, pptr, type, flags) SWIG_ConvertPtr(obj, pptr, type, flags) +#define SWIG_NewInstanceObj(ptr, type, flags) SWIG_NewPointerObj(ptr, type, flags) + +/* for C or C++ function pointers */ +#define SWIG_ConvertFunctionPtr(obj, pptr, type) SWIG_Python_ConvertFunctionPtr(obj, pptr, type) +#define SWIG_NewFunctionPtrObj(ptr, type) SWIG_Python_NewPointerObj(NULL, ptr, type, 0) + +/* for C++ member pointers, ie, member methods */ +#define SWIG_ConvertMember(obj, ptr, sz, ty) SWIG_Python_ConvertPacked(obj, ptr, sz, ty) +#define SWIG_NewMemberObj(ptr, sz, type) SWIG_Python_NewPackedObj(ptr, sz, type) + + +/* Runtime API */ + +#define SWIG_GetModule(clientdata) SWIG_Python_GetModule(clientdata) +#define SWIG_SetModule(clientdata, pointer) SWIG_Python_SetModule(pointer) +#define SWIG_NewClientData(obj) SwigPyClientData_New(obj) + +#define SWIG_SetErrorObj SWIG_Python_SetErrorObj +#define SWIG_SetErrorMsg SWIG_Python_SetErrorMsg +#define SWIG_ErrorType(code) SWIG_Python_ErrorType(code) +#define SWIG_Error(code, msg) SWIG_Python_SetErrorMsg(SWIG_ErrorType(code), msg) +#define SWIG_fail goto fail + + +/* Runtime API implementation */ + +/* Error manipulation */ + +SWIGINTERN void +SWIG_Python_SetErrorObj(PyObject *errtype, PyObject *obj) { + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + PyErr_SetObject(errtype, obj); + SWIG_Py_DECREF(obj); + SWIG_PYTHON_THREAD_END_BLOCK; +} + +SWIGINTERN void +SWIG_Python_SetErrorMsg(PyObject *errtype, const char *msg) { + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + PyErr_SetString(errtype, msg); + SWIG_PYTHON_THREAD_END_BLOCK; +} + +#define SWIG_Python_Raise(obj, type, desc) SWIG_Python_SetErrorObj(SWIG_Python_ExceptionType(desc), obj) + +/* Set a constant value */ + +#if defined(SWIGPYTHON_BUILTIN) + +SWIGINTERN void +SwigPyBuiltin_AddPublicSymbol(PyObject *seq, const char *key) { + PyObject *s = PyString_InternFromString(key); + PyList_Append(seq, s); + SWIG_Py_DECREF(s); +} + +SWIGINTERN void +SWIG_Python_SetConstant(PyObject *d, PyObject *public_interface, const char *name, PyObject *obj) { + PyDict_SetItemString(d, name, obj); + SWIG_Py_DECREF(obj); + if (public_interface) + SwigPyBuiltin_AddPublicSymbol(public_interface, name); +} + +#else + +SWIGINTERN void +SWIG_Python_SetConstant(PyObject *d, const char *name, PyObject *obj) { + PyDict_SetItemString(d, name, obj); + SWIG_Py_DECREF(obj); +} + +#endif + +/* Append a value to the result obj */ + +SWIGINTERN PyObject* +SWIG_Python_AppendOutput(PyObject* result, PyObject* obj, int is_void) { + if (!result) { + result = obj; + } else if (result == Py_None && is_void) { + SWIG_Py_DECREF(result); + result = obj; + } else { + if (!PyList_Check(result)) { + PyObject *o2 = result; + result = PyList_New(1); + if (result) { + PyList_SET_ITEM(result, 0, o2); + } else { + SWIG_Py_DECREF(obj); + return o2; + } + } + PyList_Append(result,obj); + SWIG_Py_DECREF(obj); + } + return result; +} + +/* Unpack the argument tuple */ + +SWIGINTERN Py_ssize_t +SWIG_Python_UnpackTuple(PyObject *args, const char *name, Py_ssize_t min, Py_ssize_t max, PyObject **objs) +{ + if (!args) { + if (!min && !max) { + return 1; + } else { + PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got none", + name, (min == max ? "" : "at least "), (int)min); + return 0; + } + } + if (!PyTuple_Check(args)) { + if (min <= 1 && max >= 1) { + Py_ssize_t i; + objs[0] = args; + for (i = 1; i < max; ++i) { + objs[i] = 0; + } + return 2; + } + PyErr_SetString(PyExc_SystemError, "UnpackTuple() argument list is not a tuple"); + return 0; + } else { + Py_ssize_t l = PyTuple_GET_SIZE(args); + if (l < min) { + PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d", + name, (min == max ? "" : "at least "), (int)min, (int)l); + return 0; + } else if (l > max) { + PyErr_Format(PyExc_TypeError, "%s expected %s%d arguments, got %d", + name, (min == max ? "" : "at most "), (int)max, (int)l); + return 0; + } else { + Py_ssize_t i; + for (i = 0; i < l; ++i) { + objs[i] = PyTuple_GET_ITEM(args, i); + } + for (; l < max; ++l) { + objs[l] = 0; + } + return i + 1; + } + } +} + +SWIGINTERN int +SWIG_Python_CheckNoKeywords(PyObject *kwargs, const char *name) { + int no_kwargs = 1; + if (kwargs) { + assert(PyDict_Check(kwargs)); + if (PyDict_Size(kwargs) > 0) { + PyErr_Format(PyExc_TypeError, "%s() does not take keyword arguments", name); + no_kwargs = 0; + } + } + return no_kwargs; +} + +/* A functor is a function object with one single object argument */ +#define SWIG_Python_CallFunctor(functor, obj) PyObject_CallFunctionObjArgs(functor, obj, NULL); + +/* + Helper for static pointer initialization for both C and C++ code, for example + static PyObject *SWIG_STATIC_POINTER(MyVar) = NewSomething(...); +*/ +#ifdef __cplusplus +#define SWIG_STATIC_POINTER(var) var +#else +#define SWIG_STATIC_POINTER(var) var = 0; if (!var) var +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Python-specific SWIG API */ +#define SWIG_newvarlink() SWIG_Python_newvarlink() +#define SWIG_addvarlink(p, name, get_attr, set_attr) SWIG_Python_addvarlink(p, name, get_attr, set_attr) +#define SWIG_InstallConstants(d, constants) SWIG_Python_InstallConstants(d, constants) + +/* ----------------------------------------------------------------------------- + * global variable support code. + * ----------------------------------------------------------------------------- */ + +typedef struct swig_globalvar { + char *name; /* Name of global variable */ + PyObject *(*get_attr)(void); /* Return the current value */ + int (*set_attr)(PyObject *); /* Set the value */ + struct swig_globalvar *next; +} swig_globalvar; + +typedef struct swig_varlinkobject { + PyObject_HEAD + swig_globalvar *vars; +} swig_varlinkobject; + +SWIGINTERN PyObject * +swig_varlink_repr(PyObject *SWIGUNUSEDPARM(v)) { +#if PY_VERSION_HEX >= 0x03000000 + return PyUnicode_InternFromString(""); +#else + return PyString_FromString(""); +#endif +} + +SWIGINTERN PyObject * +swig_varlink_str(PyObject *o) { + swig_varlinkobject *v = (swig_varlinkobject *) o; +#if PY_VERSION_HEX >= 0x03000000 + PyObject *str = PyUnicode_InternFromString("("); + PyObject *tail; + PyObject *joined; + swig_globalvar *var; + for (var = v->vars; var; var=var->next) { + tail = PyUnicode_FromString(var->name); + joined = PyUnicode_Concat(str, tail); + SWIG_Py_DECREF(str); + SWIG_Py_DECREF(tail); + str = joined; + if (var->next) { + tail = PyUnicode_InternFromString(", "); + joined = PyUnicode_Concat(str, tail); + SWIG_Py_DECREF(str); + SWIG_Py_DECREF(tail); + str = joined; + } + } + tail = PyUnicode_InternFromString(")"); + joined = PyUnicode_Concat(str, tail); + SWIG_Py_DECREF(str); + SWIG_Py_DECREF(tail); + str = joined; +#else + PyObject *str = PyString_FromString("("); + swig_globalvar *var; + for (var = v->vars; var; var=var->next) { + PyString_ConcatAndDel(&str,PyString_FromString(var->name)); + if (var->next) PyString_ConcatAndDel(&str,PyString_FromString(", ")); + } + PyString_ConcatAndDel(&str,PyString_FromString(")")); +#endif + return str; +} + +SWIGINTERN void +swig_varlink_dealloc(PyObject *o) { + swig_varlinkobject *v = (swig_varlinkobject *) o; + swig_globalvar *var = v->vars; + while (var) { + swig_globalvar *n = var->next; + free(var->name); + free(var); + var = n; + } +} + +SWIGINTERN PyObject * +swig_varlink_getattr(PyObject *o, char *n) { + swig_varlinkobject *v = (swig_varlinkobject *) o; + PyObject *res = NULL; + swig_globalvar *var = v->vars; + while (var) { + if (strcmp(var->name,n) == 0) { + res = (*var->get_attr)(); + break; + } + var = var->next; + } + if (res == NULL && !PyErr_Occurred()) { + PyErr_Format(PyExc_AttributeError, "Unknown C global variable '%s'", n); + } + return res; +} + +SWIGINTERN int +swig_varlink_setattr(PyObject *o, char *n, PyObject *p) { + swig_varlinkobject *v = (swig_varlinkobject *) o; + int res = 1; + swig_globalvar *var = v->vars; + while (var) { + if (strcmp(var->name,n) == 0) { + res = (*var->set_attr)(p); + break; + } + var = var->next; + } + if (res == 1 && !PyErr_Occurred()) { + PyErr_Format(PyExc_AttributeError, "Unknown C global variable '%s'", n); + } + return res; +} + +SWIGINTERN PyTypeObject* +swig_varlink_type(void) { + static char varlink__doc__[] = "Swig var link object"; +#ifndef SWIG_HEAPTYPES + static PyTypeObject varlink_type; + static int type_init = 0; + if (!type_init) { + const PyTypeObject tmp = { +#if PY_VERSION_HEX >= 0x03000000 + PyVarObject_HEAD_INIT(NULL, 0) +#else + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ +#endif + "swigvarlink", /* tp_name */ + sizeof(swig_varlinkobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor) swig_varlink_dealloc, /* tp_dealloc */ +#if PY_VERSION_HEX < 0x030800b4 + (printfunc)0, /* tp_print */ +#else + (Py_ssize_t)0, /* tp_vectorcall_offset */ +#endif + (getattrfunc) swig_varlink_getattr, /* tp_getattr */ + (setattrfunc) swig_varlink_setattr, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc) swig_varlink_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + (reprfunc) swig_varlink_str, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + 0, /* tp_flags */ + varlink__doc__, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* tp_iter -> tp_weaklist */ + 0, /* tp_del */ + 0, /* tp_version_tag */ +#if PY_VERSION_HEX >= 0x03040000 + 0, /* tp_finalize */ +#endif +#if PY_VERSION_HEX >= 0x03080000 + 0, /* tp_vectorcall */ +#endif +#if (PY_VERSION_HEX >= 0x03080000) && (PY_VERSION_HEX < 0x03090000) + 0, /* tp_print */ +#endif +#if PY_VERSION_HEX >= 0x030c0000 + 0, /* tp_watched */ +#endif +#if PY_VERSION_HEX >= 0x030d00a4 + 0, /* tp_versions_used */ +#endif +#ifdef COUNT_ALLOCS + 0, /* tp_allocs */ + 0, /* tp_frees */ + 0, /* tp_maxalloc */ + 0, /* tp_prev */ + 0 /* tp_next */ +#endif + }; + varlink_type = tmp; + type_init = 1; + if (PyType_Ready(&varlink_type) < 0) + return NULL; + } + return &varlink_type; +#else + PyType_Slot slots[] = { + { Py_tp_dealloc, (void *)swig_varlink_dealloc }, + { Py_tp_repr, (void *)swig_varlink_repr }, + { Py_tp_getattr, (void *)swig_varlink_getattr }, + { Py_tp_setattr, (void *)swig_varlink_setattr }, + { Py_tp_str, (void *)swig_varlink_str }, + { Py_tp_doc, (void *)varlink__doc__ }, + { 0, NULL } + }; + PyType_Spec spec = { + "swigvarlink", + sizeof(swig_varlinkobject), + 0, + Py_TPFLAGS_DEFAULT, + slots + }; + return (PyTypeObject *)PyType_FromSpec(&spec); +#endif +} + +/* Create a variable linking object for use later */ +SWIGINTERN PyObject * +SWIG_Python_newvarlink(void) { + swig_varlinkobject *result = PyObject_New(swig_varlinkobject, swig_varlink_type()); + if (result) { + result->vars = 0; + } + return ((PyObject*) result); +} + +SWIGINTERN void +SWIG_Python_addvarlink(PyObject *p, const char *name, PyObject *(*get_attr)(void), int (*set_attr)(PyObject *p)) { + swig_varlinkobject *v = (swig_varlinkobject *) p; + swig_globalvar *gv = (swig_globalvar *) malloc(sizeof(swig_globalvar)); + if (gv) { + size_t size = strlen(name)+1; + gv->name = (char *)malloc(size); + if (gv->name) { + memcpy(gv->name, name, size); + gv->get_attr = get_attr; + gv->set_attr = set_attr; + gv->next = v->vars; + } + } + v->vars = gv; +} + + +static PyObject *Swig_Globals_global = NULL; + +SWIGINTERN PyObject * +SWIG_globals(void) { + if (Swig_Globals_global == NULL) { + Swig_Globals_global = SWIG_newvarlink(); + } + return Swig_Globals_global; +} + +#ifdef __cplusplus +} +#endif + +/* ----------------------------------------------------------------------------- + * Pointer declarations + * ----------------------------------------------------------------------------- */ + +/* Flags for new pointer objects */ +#define SWIG_POINTER_NOSHADOW (SWIG_POINTER_OWN << 1) +#define SWIG_POINTER_NEW (SWIG_POINTER_NOSHADOW | SWIG_POINTER_OWN) + +#define SWIG_POINTER_IMPLICIT_CONV (SWIG_POINTER_DISOWN << 1) + +#define SWIG_BUILTIN_TP_INIT (SWIG_POINTER_OWN << 2) +#define SWIG_BUILTIN_INIT (SWIG_BUILTIN_TP_INIT | SWIG_POINTER_OWN) + +#ifdef __cplusplus +extern "C" { +#endif + +/* The python void return value */ + +SWIGRUNTIMEINLINE PyObject * +SWIG_Py_Void(void) +{ + PyObject *none = Py_None; + SWIG_Py_INCREF(none); + return none; +} + +/* SwigPyClientData */ + +typedef struct { + PyObject *klass; + PyObject *newraw; + PyObject *newargs; + PyObject *destroy; + int delargs; + int implicitconv; + PyTypeObject *pytype; +} SwigPyClientData; + +SWIGRUNTIMEINLINE int +SWIG_Python_CheckImplicit(swig_type_info *ty) +{ + SwigPyClientData *data = (SwigPyClientData *)ty->clientdata; + int fail = data ? data->implicitconv : 0; + if (fail) + PyErr_SetString(PyExc_TypeError, "Implicit conversion is prohibited for explicit constructors."); + return fail; +} + +SWIGRUNTIMEINLINE PyObject * +SWIG_Python_ExceptionType(swig_type_info *desc) { + SwigPyClientData *data = desc ? (SwigPyClientData *) desc->clientdata : 0; + PyObject *klass = data ? data->klass : 0; + return (klass ? klass : PyExc_RuntimeError); +} + + +SWIGRUNTIME SwigPyClientData * +SwigPyClientData_New(PyObject* obj) +{ + if (!obj) { + return 0; + } else { + SwigPyClientData *data = (SwigPyClientData *)malloc(sizeof(SwigPyClientData)); + /* the klass element */ + data->klass = obj; + SWIG_Py_INCREF(data->klass); + /* the newraw method and newargs arguments used to create a new raw instance */ + if (PyClass_Check(obj)) { + data->newraw = 0; + SWIG_Py_INCREF(obj); + data->newargs = obj; + } else { + data->newraw = PyObject_GetAttrString(data->klass, "__new__"); + if (data->newraw) { + data->newargs = PyTuple_New(1); + if (data->newargs) { + SWIG_Py_INCREF(obj); + PyTuple_SET_ITEM(data->newargs, 0, obj); + } else { + SWIG_Py_DECREF(data->newraw); + SWIG_Py_DECREF(data->klass); + free(data); + return 0; + } + } else { + SWIG_Py_INCREF(obj); + data->newargs = obj; + } + } + /* the destroy method, aka as the C++ delete method */ + data->destroy = PyObject_GetAttrString(data->klass, "__swig_destroy__"); + if (PyErr_Occurred()) { + PyErr_Clear(); + data->destroy = 0; + } + if (data->destroy) { + data->delargs = !(PyCFunction_GET_FLAGS(data->destroy) & METH_O); + } else { + data->delargs = 0; + } + data->implicitconv = 0; + data->pytype = 0; + return data; + } +} + +SWIGRUNTIME void +SwigPyClientData_Del(SwigPyClientData *data) +{ + SWIG_Py_XDECREF(data->klass); + SWIG_Py_XDECREF(data->newraw); + SWIG_Py_XDECREF(data->newargs); + SWIG_Py_XDECREF(data->destroy); + free(data); +} + +/* =============== SwigPyObject =====================*/ + +typedef struct { + PyObject_HEAD + void *ptr; + swig_type_info *ty; + int own; + PyObject *next; +#ifdef SWIGPYTHON_BUILTIN + PyObject *dict; +#endif +} SwigPyObject; + + +#ifdef SWIGPYTHON_BUILTIN + +SWIGRUNTIME PyObject * +SwigPyObject_get___dict__(PyObject *v, PyObject *SWIGUNUSEDPARM(args)) +{ + SwigPyObject *sobj = (SwigPyObject *)v; + + if (!sobj->dict) + sobj->dict = PyDict_New(); + + SWIG_Py_XINCREF(sobj->dict); + return sobj->dict; +} + +#endif + +SWIGRUNTIME PyObject * +SwigPyObject_long(SwigPyObject *v) +{ + return PyLong_FromVoidPtr(v->ptr); +} + +SWIGRUNTIME PyObject * +SwigPyObject_format(const char* fmt, SwigPyObject *v) +{ + PyObject *res = NULL; + PyObject *args = PyTuple_New(1); + if (args) { + PyObject *val = SwigPyObject_long(v); + if (val) { + PyObject *ofmt; + PyTuple_SET_ITEM(args, 0, val); + ofmt = SWIG_Python_str_FromChar(fmt); + if (ofmt) { +#if PY_VERSION_HEX >= 0x03000000 + res = PyUnicode_Format(ofmt,args); +#else + res = PyString_Format(ofmt,args); +#endif + SWIG_Py_DECREF(ofmt); + } + } + SWIG_Py_DECREF(args); + } + return res; +} + +SWIGRUNTIME PyObject * +SwigPyObject_oct(SwigPyObject *v) +{ + return SwigPyObject_format("%o",v); +} + +SWIGRUNTIME PyObject * +SwigPyObject_hex(SwigPyObject *v) +{ + return SwigPyObject_format("%x",v); +} + +SWIGRUNTIME PyObject * +SwigPyObject_repr(SwigPyObject *v) +{ + const char *name = SWIG_TypePrettyName(v->ty); + PyObject *repr = SWIG_Python_str_FromFormat("", (name ? name : "unknown"), (void *)v); + if (repr && v->next) { + PyObject *nrep = SwigPyObject_repr((SwigPyObject *)v->next); + if (nrep) { +# if PY_VERSION_HEX >= 0x03000000 + PyObject *joined = PyUnicode_Concat(repr, nrep); + SWIG_Py_DECREF(repr); + SWIG_Py_DECREF(nrep); + repr = joined; +# else + PyString_ConcatAndDel(&repr,nrep); +# endif + } else { + SWIG_Py_DECREF(repr); + repr = NULL; + } + } + return repr; +} + +/* We need a version taking two PyObject* parameters so it's a valid + * PyCFunction to use in swigobject_methods[]. */ +SWIGRUNTIME PyObject * +SwigPyObject_repr2(PyObject *v, PyObject *SWIGUNUSEDPARM(args)) +{ + return SwigPyObject_repr((SwigPyObject*)v); +} + +SWIGRUNTIME int +SwigPyObject_compare(SwigPyObject *v, SwigPyObject *w) +{ + void *i = v->ptr; + void *j = w->ptr; + return (i < j) ? -1 : ((i > j) ? 1 : 0); +} + +/* Added for Python 3.x, would it also be useful for Python 2.x? */ +SWIGRUNTIME PyObject* +SwigPyObject_richcompare(SwigPyObject *v, SwigPyObject *w, int op) +{ + PyObject* res = NULL; + if (!PyErr_Occurred()) { + if (op != Py_EQ && op != Py_NE) { + SWIG_Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + res = PyBool_FromLong( (SwigPyObject_compare(v, w)==0) == (op == Py_EQ) ? 1 : 0); + } + return res; +} + + +SWIGRUNTIME PyTypeObject* SwigPyObject_TypeOnce(void); + +#ifdef SWIGPYTHON_BUILTIN +static swig_type_info *SwigPyObject_stype = 0; +SWIGRUNTIME PyTypeObject* +SwigPyObject_type(void) { + SwigPyClientData *cd; + assert(SwigPyObject_stype); + cd = (SwigPyClientData*) SwigPyObject_stype->clientdata; + assert(cd); + assert(cd->pytype); + return cd->pytype; +} +#else +SWIGRUNTIME PyTypeObject* +SwigPyObject_type(void) { + static PyTypeObject *SWIG_STATIC_POINTER(type) = SwigPyObject_TypeOnce(); + return type; +} +#endif + +SWIGRUNTIMEINLINE int +SwigPyObject_Check(PyObject *op) { + PyTypeObject *target_tp = SwigPyObject_type(); + PyTypeObject *op_type = Py_TYPE(op); +#ifdef SWIGPYTHON_BUILTIN + if (PyType_IsSubtype(op_type, target_tp)) + return 1; + return (strcmp(op_type->tp_name, "SwigPyObject") == 0); +#else +# ifdef Py_LIMITED_API + int cmp; + PyObject *tp_name; +#endif + if (op_type == target_tp) + return 1; +# ifdef Py_LIMITED_API + tp_name = PyObject_GetAttrString((PyObject *)op_type, "__name__"); + if (!tp_name) + return 0; + cmp = PyUnicode_CompareWithASCIIString(tp_name, "SwigPyObject"); + SWIG_Py_DECREF(tp_name); + return cmp == 0; +# else + return (strcmp(op_type->tp_name, "SwigPyObject") == 0); +# endif +#endif +} + +SWIGRUNTIME PyObject * +SwigPyObject_New(void *ptr, swig_type_info *ty, int own); + +static PyObject* Swig_Capsule_global = NULL; + +SWIGRUNTIME void +SwigPyObject_dealloc(PyObject *v) +{ + SwigPyObject *sobj = (SwigPyObject *) v; + PyObject *next = sobj->next; + if (sobj->own == SWIG_POINTER_OWN) { + swig_type_info *ty = sobj->ty; + SwigPyClientData *data = ty ? (SwigPyClientData *) ty->clientdata : 0; + PyObject *destroy = data ? data->destroy : 0; + if (destroy) { + /* destroy is always a VARARGS method */ + PyObject *res; + + /* PyObject_CallFunction() has the potential to silently drop + the active exception. In cases of unnamed temporary + variable or where we just finished iterating over a generator + StopIteration will be active right now, and this needs to + remain true upon return from SwigPyObject_dealloc. So save + and restore. */ + + PyObject *type = NULL, *value = NULL, *traceback = NULL; + PyErr_Fetch(&type, &value, &traceback); + + if (data->delargs) { + /* we need to create a temporary object to carry the destroy operation */ + PyObject *tmp = SwigPyObject_New(sobj->ptr, ty, 0); + if (tmp) { + res = SWIG_Python_CallFunctor(destroy, tmp); + } else { + res = 0; + } + SWIG_Py_XDECREF(tmp); + } else { + PyCFunction meth = PyCFunction_GET_FUNCTION(destroy); + PyObject *mself = PyCFunction_GET_SELF(destroy); + res = ((*meth)(mself, v)); + } + if (!res) + PyErr_WriteUnraisable(destroy); + + PyErr_Restore(type, value, traceback); + + SWIG_Py_XDECREF(res); + } +#if !defined(SWIG_PYTHON_SILENT_MEMLEAK) + else { + const char *name = SWIG_TypePrettyName(ty); + printf("swig/python detected a memory leak of type '%s', no destructor found.\n", (name ? name : "unknown")); + } +#endif + SWIG_Py_XDECREF(Swig_Capsule_global); + } + SWIG_Py_XDECREF(next); +#ifdef SWIGPYTHON_BUILTIN + SWIG_Py_XDECREF(sobj->dict); +#endif + PyObject_Free(v); +} + +SWIGRUNTIME PyObject* +SwigPyObject_append(PyObject* v, PyObject* next) +{ + SwigPyObject *sobj = (SwigPyObject *) v; + if (!SwigPyObject_Check(next)) { + PyErr_SetString(PyExc_TypeError, "Attempt to append a non SwigPyObject"); + return NULL; + } + ((SwigPyObject *)next)->next = sobj->next; + sobj->next = next; + SWIG_Py_INCREF(next); + return SWIG_Py_Void(); +} + +SWIGRUNTIME PyObject* +SwigPyObject_next(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) +{ + SwigPyObject *sobj = (SwigPyObject *) v; + if (sobj->next) { + SWIG_Py_INCREF(sobj->next); + return sobj->next; + } else { + return SWIG_Py_Void(); + } +} + +SWIGINTERN PyObject* +SwigPyObject_disown(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) +{ + SwigPyObject *sobj = (SwigPyObject *)v; + sobj->own = 0; + return SWIG_Py_Void(); +} + +SWIGINTERN PyObject* +SwigPyObject_acquire(PyObject* v, PyObject *SWIGUNUSEDPARM(args)) +{ + SwigPyObject *sobj = (SwigPyObject *)v; + sobj->own = SWIG_POINTER_OWN; + return SWIG_Py_Void(); +} + +SWIGINTERN PyObject* +SwigPyObject_own(PyObject *v, PyObject *args) +{ + PyObject *val = 0; + if (!PyArg_UnpackTuple(args, "own", 0, 1, &val)) { + return NULL; + } else { + SwigPyObject *sobj = (SwigPyObject *)v; + PyObject *obj = PyBool_FromLong(sobj->own); + if (val) { + if (PyObject_IsTrue(val)) { + SWIG_Py_DECREF(SwigPyObject_acquire(v,args)); + } else { + SWIG_Py_DECREF(SwigPyObject_disown(v,args)); + } + } + return obj; + } +} + +static PyMethodDef +swigobject_methods[] = { + {"disown", SwigPyObject_disown, METH_NOARGS, "releases ownership of the pointer"}, + {"acquire", SwigPyObject_acquire, METH_NOARGS, "acquires ownership of the pointer"}, + {"own", SwigPyObject_own, METH_VARARGS, "returns/sets ownership of the pointer"}, + {"append", SwigPyObject_append, METH_O, "appends another 'this' object"}, + {"next", SwigPyObject_next, METH_NOARGS, "returns the next 'this' object"}, + {"__repr__",SwigPyObject_repr2, METH_NOARGS, "returns object representation"}, + {0, 0, 0, 0} +}; + +SWIGRUNTIME PyTypeObject* +SwigPyObject_TypeOnce(void) { + static char swigobject_doc[] = "Swig object carries a C/C++ instance pointer"; +#ifndef SWIG_HEAPTYPES + static PyNumberMethods SwigPyObject_as_number = { + (binaryfunc)0, /*nb_add*/ + (binaryfunc)0, /*nb_subtract*/ + (binaryfunc)0, /*nb_multiply*/ + /* nb_divide removed in Python 3 */ +#if PY_VERSION_HEX < 0x03000000 + (binaryfunc)0, /*nb_divide*/ +#endif + (binaryfunc)0, /*nb_remainder*/ + (binaryfunc)0, /*nb_divmod*/ + (ternaryfunc)0,/*nb_power*/ + (unaryfunc)0, /*nb_negative*/ + (unaryfunc)0, /*nb_positive*/ + (unaryfunc)0, /*nb_absolute*/ + (inquiry)0, /*nb_nonzero*/ + 0, /*nb_invert*/ + 0, /*nb_lshift*/ + 0, /*nb_rshift*/ + 0, /*nb_and*/ + 0, /*nb_xor*/ + 0, /*nb_or*/ +#if PY_VERSION_HEX < 0x03000000 + 0, /*nb_coerce*/ +#endif + (unaryfunc)SwigPyObject_long, /*nb_int*/ +#if PY_VERSION_HEX < 0x03000000 + (unaryfunc)SwigPyObject_long, /*nb_long*/ +#else + 0, /*nb_reserved*/ +#endif + (unaryfunc)0, /*nb_float*/ +#if PY_VERSION_HEX < 0x03000000 + (unaryfunc)SwigPyObject_oct, /*nb_oct*/ + (unaryfunc)SwigPyObject_hex, /*nb_hex*/ +#endif +#if PY_VERSION_HEX >= 0x03050000 /* 3.5 */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_inplace_matrix_multiply */ +#elif PY_VERSION_HEX >= 0x03000000 /* 3.0 */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_index, nb_inplace_divide removed */ +#else + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 /* nb_inplace_add -> nb_index */ +#endif + }; + + static PyTypeObject swigpyobject_type; + static int type_init = 0; + if (!type_init) { + const PyTypeObject tmp = { +#if PY_VERSION_HEX >= 0x03000000 + PyVarObject_HEAD_INIT(NULL, 0) +#else + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ +#endif + "SwigPyObject", /* tp_name */ + sizeof(SwigPyObject), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)SwigPyObject_dealloc, /* tp_dealloc */ +#if PY_VERSION_HEX < 0x030800b4 + (printfunc)0, /* tp_print */ +#else + (Py_ssize_t)0, /* tp_vectorcall_offset */ +#endif + (getattrfunc)0, /* tp_getattr */ + (setattrfunc)0, /* tp_setattr */ +#if PY_VERSION_HEX >= 0x03000000 + 0, /* tp_reserved in 3.0.1, tp_compare in 3.0.0 but not used */ +#else + (cmpfunc)SwigPyObject_compare, /* tp_compare */ +#endif + (reprfunc)SwigPyObject_repr, /* tp_repr */ + &SwigPyObject_as_number, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + swigobject_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + (richcmpfunc)SwigPyObject_richcompare,/* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + swigobject_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + 0, /* tp_del */ + 0, /* tp_version_tag */ +#if PY_VERSION_HEX >= 0x03040000 + 0, /* tp_finalize */ +#endif +#if PY_VERSION_HEX >= 0x03080000 + 0, /* tp_vectorcall */ +#endif +#if (PY_VERSION_HEX >= 0x03080000) && (PY_VERSION_HEX < 0x03090000) + 0, /* tp_print */ +#endif +#if PY_VERSION_HEX >= 0x030c0000 + 0, /* tp_watched */ +#endif +#if PY_VERSION_HEX >= 0x030d00a4 + 0, /* tp_versions_used */ +#endif +#ifdef COUNT_ALLOCS + 0, /* tp_allocs */ + 0, /* tp_frees */ + 0, /* tp_maxalloc */ + 0, /* tp_prev */ + 0 /* tp_next */ +#endif + }; + swigpyobject_type = tmp; + type_init = 1; + if (PyType_Ready(&swigpyobject_type) != 0) + return NULL; + } + return &swigpyobject_type; +#else + PyType_Slot slots[] = { + { Py_tp_dealloc, (void *)SwigPyObject_dealloc }, + { Py_tp_repr, (void *)SwigPyObject_repr }, + { Py_tp_getattro, (void *)PyObject_GenericGetAttr }, + { Py_tp_doc, (void *)swigobject_doc }, + { Py_tp_richcompare, (void *)SwigPyObject_richcompare }, + { Py_tp_methods, (void *)swigobject_methods }, + { Py_nb_int, (void *)SwigPyObject_long }, + { 0, NULL } + }; + PyType_Spec spec = { + "SwigPyObject", + sizeof(SwigPyObject), + 0, + Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, + slots + }; + return (PyTypeObject *)PyType_FromSpec(&spec); +#endif +} + +SWIGRUNTIME PyObject * +SwigPyObject_New(void *ptr, swig_type_info *ty, int own) +{ + SwigPyObject *sobj = PyObject_New(SwigPyObject, SwigPyObject_type()); + if (sobj) { + sobj->ptr = ptr; + sobj->ty = ty; + sobj->own = own; + sobj->next = 0; +#ifdef SWIGPYTHON_BUILTIN + sobj->dict = 0; +#endif + if (own == SWIG_POINTER_OWN) { + /* Obtain a reference to the Python capsule wrapping the module information, so that the + * module information is correctly destroyed after all SWIG python objects have been freed + * by the GC (and corresponding destructors invoked) */ + SWIG_Py_XINCREF(Swig_Capsule_global); + } + } + return (PyObject *)sobj; +} + +/* ----------------------------------------------------------------------------- + * Implements a simple Swig Packed type, and use it instead of string + * ----------------------------------------------------------------------------- */ + +typedef struct { + PyObject_HEAD + void *pack; + swig_type_info *ty; + size_t size; +} SwigPyPacked; + +SWIGRUNTIME PyObject * +SwigPyPacked_repr(SwigPyPacked *v) +{ + char result[SWIG_BUFFER_SIZE]; + if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))) { + return SWIG_Python_str_FromFormat("", result, v->ty->name); + } else { + return SWIG_Python_str_FromFormat("", v->ty->name); + } +} + +SWIGRUNTIME PyObject * +SwigPyPacked_str(SwigPyPacked *v) +{ + char result[SWIG_BUFFER_SIZE]; + if (SWIG_PackDataName(result, v->pack, v->size, 0, sizeof(result))){ + return SWIG_Python_str_FromFormat("%s%s", result, v->ty->name); + } else { + return SWIG_Python_str_FromChar(v->ty->name); + } +} + +SWIGRUNTIME int +SwigPyPacked_compare(SwigPyPacked *v, SwigPyPacked *w) +{ + size_t i = v->size; + size_t j = w->size; + int s = (i < j) ? -1 : ((i > j) ? 1 : 0); + return s ? s : strncmp((const char *)v->pack, (const char *)w->pack, 2*v->size); +} + +SWIGRUNTIME PyTypeObject* SwigPyPacked_TypeOnce(void); + +SWIGRUNTIME PyTypeObject* +SwigPyPacked_type(void) { + static PyTypeObject *SWIG_STATIC_POINTER(type) = SwigPyPacked_TypeOnce(); + return type; +} + +SWIGRUNTIMEINLINE int +SwigPyPacked_Check(PyObject *op) { +#ifdef Py_LIMITED_API + int cmp; + PyObject *tp_name; +#endif + PyTypeObject* op_type = Py_TYPE(op); + if (op_type == SwigPyPacked_TypeOnce()) + return 1; +#ifdef Py_LIMITED_API + tp_name = PyObject_GetAttrString((PyObject *)op_type, "__name__"); + if (!tp_name) + return 0; + cmp = PyUnicode_CompareWithASCIIString(tp_name, "SwigPyPacked"); + SWIG_Py_DECREF(tp_name); + return cmp == 0; +#else + return (strcmp(op_type->tp_name, "SwigPyPacked") == 0); +#endif +} + +SWIGRUNTIME void +SwigPyPacked_dealloc(PyObject *v) +{ + if (SwigPyPacked_Check(v)) { + SwigPyPacked *sobj = (SwigPyPacked *) v; + free(sobj->pack); + } + PyObject_Free(v); +} + +SWIGRUNTIME PyTypeObject* +SwigPyPacked_TypeOnce(void) { + static char swigpacked_doc[] = "Swig object carries a C/C++ instance pointer"; +#ifndef SWIG_HEAPTYPES + static PyTypeObject swigpypacked_type; + static int type_init = 0; + if (!type_init) { + const PyTypeObject tmp = { +#if PY_VERSION_HEX>=0x03000000 + PyVarObject_HEAD_INIT(NULL, 0) +#else + PyObject_HEAD_INIT(NULL) + 0, /* ob_size */ +#endif + "SwigPyPacked", /* tp_name */ + sizeof(SwigPyPacked), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)SwigPyPacked_dealloc, /* tp_dealloc */ +#if PY_VERSION_HEX < 0x030800b4 + (printfunc)0, /* tp_print */ +#else + (Py_ssize_t)0, /* tp_vectorcall_offset */ +#endif + (getattrfunc)0, /* tp_getattr */ + (setattrfunc)0, /* tp_setattr */ +#if PY_VERSION_HEX>=0x03000000 + 0, /* tp_reserved in 3.0.1 */ +#else + (cmpfunc)SwigPyPacked_compare, /* tp_compare */ +#endif + (reprfunc)SwigPyPacked_repr, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)0, /* tp_hash */ + (ternaryfunc)0, /* tp_call */ + (reprfunc)SwigPyPacked_str, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + swigpacked_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ + 0, /* tp_free */ + 0, /* tp_is_gc */ + 0, /* tp_bases */ + 0, /* tp_mro */ + 0, /* tp_cache */ + 0, /* tp_subclasses */ + 0, /* tp_weaklist */ + 0, /* tp_del */ + 0, /* tp_version_tag */ +#if PY_VERSION_HEX >= 0x03040000 + 0, /* tp_finalize */ +#endif +#if PY_VERSION_HEX >= 0x03080000 + 0, /* tp_vectorcall */ +#endif +#if (PY_VERSION_HEX >= 0x03080000) && (PY_VERSION_HEX < 0x03090000) + 0, /* tp_print */ +#endif +#if PY_VERSION_HEX >= 0x030c0000 + 0, /* tp_watched */ +#endif +#if PY_VERSION_HEX >= 0x030d00a4 + 0, /* tp_versions_used */ +#endif +#ifdef COUNT_ALLOCS + 0, /* tp_allocs */ + 0, /* tp_frees */ + 0, /* tp_maxalloc */ + 0, /* tp_prev */ + 0 /* tp_next */ +#endif + }; + swigpypacked_type = tmp; + type_init = 1; + if (PyType_Ready(&swigpypacked_type) != 0) + return NULL; + } + return &swigpypacked_type; +#else + PyType_Slot slots[] = { + { Py_tp_dealloc, (void *)SwigPyPacked_dealloc }, + { Py_tp_repr, (void *)SwigPyPacked_repr }, + { Py_tp_str, (void *)SwigPyPacked_str }, + { Py_tp_getattro, (void *)PyObject_GenericGetAttr }, + { Py_tp_doc, (void *)swigpacked_doc }, + { 0, NULL } + }; + PyType_Spec spec = { + "SwigPyPacked", + sizeof(SwigPyPacked), + 0, + Py_TPFLAGS_DEFAULT, + slots + }; + return (PyTypeObject *)PyType_FromSpec(&spec); +#endif +} + +SWIGRUNTIME PyObject * +SwigPyPacked_New(void *ptr, size_t size, swig_type_info *ty) +{ + SwigPyPacked *sobj = PyObject_New(SwigPyPacked, SwigPyPacked_type()); + if (sobj) { + void *pack = malloc(size); + if (pack) { + memcpy(pack, ptr, size); + sobj->pack = pack; + sobj->ty = ty; + sobj->size = size; + } else { + PyObject_Free((PyObject *)sobj); + sobj = 0; + } + } + return (PyObject *) sobj; +} + +SWIGRUNTIME swig_type_info * +SwigPyPacked_UnpackData(PyObject *obj, void *ptr, size_t size) +{ + if (SwigPyPacked_Check(obj)) { + SwigPyPacked *sobj = (SwigPyPacked *)obj; + if (sobj->size != size) return 0; + memcpy(ptr, sobj->pack, size); + return sobj->ty; + } else { + return 0; + } +} + +/* ----------------------------------------------------------------------------- + * pointers/data manipulation + * ----------------------------------------------------------------------------- */ + +static PyObject *Swig_This_global = NULL; + +SWIGRUNTIME PyObject * +SWIG_This(void) +{ + if (Swig_This_global == NULL) + Swig_This_global = SWIG_Python_str_FromChar("this"); + return Swig_This_global; +} + +/* #define SWIG_PYTHON_SLOW_GETSET_THIS */ + +/* TODO: I don't know how to implement the fast getset in Python 3 right now */ +#if PY_VERSION_HEX>=0x03000000 +#define SWIG_PYTHON_SLOW_GETSET_THIS +#endif + +SWIGRUNTIME SwigPyObject * +SWIG_Python_GetSwigThis(PyObject *pyobj) +{ + PyObject *obj; + + if (SwigPyObject_Check(pyobj)) + return (SwigPyObject *) pyobj; + +#ifdef SWIGPYTHON_BUILTIN + (void)obj; +# ifdef PyWeakref_CheckProxy + if (PyWeakref_CheckProxy(pyobj)) { +#if PY_VERSION_HEX >= 0x030d0000 + PyWeakref_GetRef(pyobj, &pyobj); + Py_DECREF(pyobj); +#else + pyobj = PyWeakref_GET_OBJECT(pyobj); +#endif + if (pyobj && SwigPyObject_Check(pyobj)) + return (SwigPyObject*) pyobj; + } +# endif + return NULL; +#else + + obj = 0; + +#if !defined(SWIG_PYTHON_SLOW_GETSET_THIS) + if (PyInstance_Check(pyobj)) { + obj = _PyInstance_Lookup(pyobj, SWIG_This()); + } else { + PyObject **dictptr = _PyObject_GetDictPtr(pyobj); + if (dictptr != NULL) { + PyObject *dict = *dictptr; + obj = dict ? PyDict_GetItem(dict, SWIG_This()) : 0; + } else { +#ifdef PyWeakref_CheckProxy + if (PyWeakref_CheckProxy(pyobj)) { + PyObject *wobj = PyWeakref_GET_OBJECT(pyobj); + return wobj ? SWIG_Python_GetSwigThis(wobj) : 0; + } +#endif + obj = PyObject_GetAttr(pyobj,SWIG_This()); + if (obj) { + SWIG_Py_DECREF(obj); + } else { + if (PyErr_Occurred()) PyErr_Clear(); + return 0; + } + } + } +#else + obj = PyObject_GetAttr(pyobj,SWIG_This()); + if (obj) { + SWIG_Py_DECREF(obj); + } else { + if (PyErr_Occurred()) PyErr_Clear(); + return 0; + } +#endif + if (obj && !SwigPyObject_Check(obj)) { + /* a PyObject is called 'this', try to get the 'real this' + SwigPyObject from it */ + return SWIG_Python_GetSwigThis(obj); + } + return (SwigPyObject *)obj; +#endif +} + +/* Acquire a pointer value */ + +SWIGRUNTIME int +SWIG_Python_AcquirePtr(PyObject *obj, int own) { + if (own == SWIG_POINTER_OWN) { + SwigPyObject *sobj = SWIG_Python_GetSwigThis(obj); + if (sobj) { + int oldown = sobj->own; + sobj->own = own; + return oldown; + } + } + return 0; +} + +/* Convert a pointer value */ + +SWIGRUNTIME int +SWIG_Python_ConvertPtrAndOwn(PyObject *obj, void **ptr, swig_type_info *ty, int flags, int *own) { + int res; + SwigPyObject *sobj; + int implicit_conv = (flags & SWIG_POINTER_IMPLICIT_CONV) != 0; + + if (!obj) + return SWIG_ERROR; + if (obj == Py_None && !implicit_conv) { + if (ptr) + *ptr = 0; + return (flags & SWIG_POINTER_NO_NULL) ? SWIG_NullReferenceError : SWIG_OK; + } + + res = SWIG_ERROR; + + sobj = SWIG_Python_GetSwigThis(obj); + if (own) + *own = 0; + while (sobj) { + void *vptr = sobj->ptr; + if (ty) { + swig_type_info *to = sobj->ty; + if (to == ty) { + /* no type cast needed */ + if (ptr) *ptr = vptr; + break; + } else { + swig_cast_info *tc = SWIG_TypeCheck(to->name,ty); + if (!tc) { + sobj = (SwigPyObject *)sobj->next; + } else { + if (ptr) { + int newmemory = 0; + *ptr = SWIG_TypeCast(tc,vptr,&newmemory); + if (newmemory == SWIG_CAST_NEW_MEMORY) { + assert(own); /* badly formed typemap which will lead to a memory leak - it must set and use own to delete *ptr */ + if (own) + *own = *own | SWIG_CAST_NEW_MEMORY; + } + } + break; + } + } + } else { + if (ptr) *ptr = vptr; + break; + } + } + if (sobj) { + if (((flags & SWIG_POINTER_RELEASE) == SWIG_POINTER_RELEASE) && !sobj->own) { + res = SWIG_ERROR_RELEASE_NOT_OWNED; + } else { + if (own) + *own = *own | sobj->own; + if (flags & SWIG_POINTER_DISOWN) { + sobj->own = 0; + } + if (flags & SWIG_POINTER_CLEAR) { + sobj->ptr = 0; + } + res = SWIG_OK; + } + } else { + if (implicit_conv) { + SwigPyClientData *data = ty ? (SwigPyClientData *) ty->clientdata : 0; + if (data && !data->implicitconv) { + PyObject *klass = data->klass; + if (klass) { + PyObject *impconv; + data->implicitconv = 1; /* avoid recursion and call 'explicit' constructors*/ + impconv = SWIG_Python_CallFunctor(klass, obj); + data->implicitconv = 0; + if (PyErr_Occurred()) { + PyErr_Clear(); + impconv = 0; + } + if (impconv) { + SwigPyObject *iobj = SWIG_Python_GetSwigThis(impconv); + if (iobj) { + void *vptr; + res = SWIG_Python_ConvertPtrAndOwn((PyObject*)iobj, &vptr, ty, 0, 0); + if (SWIG_IsOK(res)) { + if (ptr) { + *ptr = vptr; + /* transfer the ownership to 'ptr' */ + iobj->own = 0; + res = SWIG_AddCast(res); + res = SWIG_AddNewMask(res); + } else { + res = SWIG_AddCast(res); + } + } + } + SWIG_Py_DECREF(impconv); + } + } + } + if (!SWIG_IsOK(res) && obj == Py_None) { + if (ptr) + *ptr = 0; + if (PyErr_Occurred()) + PyErr_Clear(); + res = SWIG_OK; + } + } + } + return res; +} + +/* Convert a function ptr value */ + +SWIGRUNTIME int +SWIG_Python_ConvertFunctionPtr(PyObject *obj, void **ptr, swig_type_info *ty) { + if (!PyCFunction_Check(obj)) { + return SWIG_ConvertPtr(obj, ptr, ty, 0); + } else { + void *vptr = 0; + swig_cast_info *tc; + + /* here we get the method pointer for callbacks */ +#ifndef Py_LIMITED_API + const char *doc = (((PyCFunctionObject *)obj) -> m_ml -> ml_doc); +#else + PyObject* pystr_doc = PyObject_GetAttrString(obj, "__doc__"); + PyObject *bytes = NULL; + const char *doc = pystr_doc ? SWIG_PyUnicode_AsUTF8AndSize(pystr_doc, NULL, &bytes) : 0; +#endif + const char *desc = doc ? strstr(doc, "swig_ptr: ") : 0; + if (desc) + desc = ty ? SWIG_UnpackVoidPtr(desc + 10, &vptr, ty->name) : 0; +#ifdef Py_LIMITED_API + SWIG_Py_XDECREF(bytes); + SWIG_Py_XDECREF(pystr_doc); +#endif + if (!desc) + return SWIG_ERROR; + tc = SWIG_TypeCheck(desc,ty); + if (tc) { + int newmemory = 0; + *ptr = SWIG_TypeCast(tc,vptr,&newmemory); + assert(!newmemory); /* newmemory handling not yet implemented */ + } else { + return SWIG_ERROR; + } + return SWIG_OK; + } +} + +/* Convert a packed pointer value */ + +SWIGRUNTIME int +SWIG_Python_ConvertPacked(PyObject *obj, void *ptr, size_t sz, swig_type_info *ty) { + swig_type_info *to = SwigPyPacked_UnpackData(obj, ptr, sz); + if (!to) return SWIG_ERROR; + if (ty) { + if (to != ty) { + /* check type cast? */ + swig_cast_info *tc = SWIG_TypeCheck(to->name,ty); + if (!tc) return SWIG_ERROR; + } + } + return SWIG_OK; +} + +/* ----------------------------------------------------------------------------- + * Create a new pointer object + * ----------------------------------------------------------------------------- */ + +/* + Create a new instance object, without calling __init__, and set the + 'this' attribute. +*/ + +SWIGRUNTIME PyObject* +SWIG_Python_NewShadowInstance(SwigPyClientData *data, PyObject *swig_this) +{ + PyObject *inst = 0; + PyObject *newraw = data->newraw; + if (newraw) { + inst = PyObject_Call(newraw, data->newargs, NULL); + if (inst) { +#if !defined(SWIG_PYTHON_SLOW_GETSET_THIS) + PyObject **dictptr = _PyObject_GetDictPtr(inst); + if (dictptr != NULL) { + PyObject *dict = *dictptr; + if (dict == NULL) { + dict = PyDict_New(); + *dictptr = dict; + } + if (dict) { + PyDict_SetItem(dict, SWIG_This(), swig_this); + } else{ + SWIG_Py_DECREF(inst); + inst = 0; + } + } +#else + if (PyObject_SetAttr(inst, SWIG_This(), swig_this) == -1) { + SWIG_Py_DECREF(inst); + inst = 0; + } +#endif + } + } else { +#if PY_VERSION_HEX >= 0x03000000 + PyObject *empty_args = PyTuple_New(0); + if (empty_args) { + PyObject *empty_kwargs = PyDict_New(); + if (empty_kwargs) { +#ifndef Py_LIMITED_API + newfunc newfn = ((PyTypeObject *)data->newargs)->tp_new; +#else + newfunc newfn = (newfunc)PyType_GetSlot((PyTypeObject *)data->newargs, Py_tp_new); +#endif + inst = newfn((PyTypeObject *)data->newargs, empty_args, empty_kwargs); + SWIG_Py_DECREF(empty_kwargs); + if (inst) { + if (PyObject_SetAttr(inst, SWIG_This(), swig_this) == -1) { + SWIG_Py_DECREF(inst); + inst = 0; + } else { + PyType_Modified(Py_TYPE(inst)); + } + } + } + SWIG_Py_DECREF(empty_args); + } +#else + PyObject *dict = PyDict_New(); + if (dict) { + PyDict_SetItem(dict, SWIG_This(), swig_this); + inst = PyInstance_NewRaw(data->newargs, dict); + SWIG_Py_DECREF(dict); + } +#endif + } + return inst; +} + +SWIGRUNTIME int +SWIG_Python_SetSwigThis(PyObject *inst, PyObject *swig_this) +{ +#if !defined(SWIG_PYTHON_SLOW_GETSET_THIS) + PyObject **dictptr = _PyObject_GetDictPtr(inst); + if (dictptr != NULL) { + PyObject *dict = *dictptr; + if (dict == NULL) { + dict = PyDict_New(); + *dictptr = dict; + } + if (dict) { + return PyDict_SetItem(dict, SWIG_This(), swig_this); + } else{ + return -1; + } + } +#endif + return PyObject_SetAttr(inst, SWIG_This(), swig_this); +} + + +SWIGINTERN PyObject * +SWIG_Python_InitShadowInstance(PyObject *args) { + PyObject *obj[2]; + if (!SWIG_Python_UnpackTuple(args, "swiginit", 2, 2, obj)) { + return NULL; + } else { + SwigPyObject *sthis = SWIG_Python_GetSwigThis(obj[0]); + if (sthis) { + SWIG_Py_DECREF(SwigPyObject_append((PyObject*) sthis, obj[1])); + } else { + if (SWIG_Python_SetSwigThis(obj[0], obj[1]) != 0) + return NULL; + } + return SWIG_Py_Void(); + } +} + +/* Create a new pointer object */ + +SWIGRUNTIME PyObject * +SWIG_Python_NewPointerObj(PyObject *self, void *ptr, swig_type_info *type, int flags) { + SwigPyClientData *clientdata; + PyObject * robj; + int own; + + if (!ptr) + return SWIG_Py_Void(); + + clientdata = type ? (SwigPyClientData *)(type->clientdata) : 0; + own = (flags & SWIG_POINTER_OWN) ? SWIG_POINTER_OWN : 0; + if (clientdata && clientdata->pytype) { + SwigPyObject *newobj; + if (flags & SWIG_BUILTIN_TP_INIT) { + newobj = (SwigPyObject*) self; + if (newobj->ptr) { +#ifndef Py_LIMITED_API + allocfunc alloc = clientdata->pytype->tp_alloc; +#else + allocfunc alloc = (allocfunc)PyType_GetSlot(clientdata->pytype, Py_tp_alloc); +#endif + PyObject *next_self = alloc(clientdata->pytype, 0); + while (newobj->next) + newobj = (SwigPyObject *) newobj->next; + newobj->next = next_self; + newobj = (SwigPyObject *)next_self; +#ifdef SWIGPYTHON_BUILTIN + newobj->dict = 0; +#endif + } + } else { + newobj = PyObject_New(SwigPyObject, clientdata->pytype); +#ifdef SWIGPYTHON_BUILTIN + if (newobj) { + newobj->dict = 0; + } +#endif + } + if (newobj) { + newobj->ptr = ptr; + newobj->ty = type; + newobj->own = own; + newobj->next = 0; + return (PyObject*) newobj; + } + return SWIG_Py_Void(); + } + + assert(!(flags & SWIG_BUILTIN_TP_INIT)); + + robj = SwigPyObject_New(ptr, type, own); + if (robj && clientdata && !(flags & SWIG_POINTER_NOSHADOW)) { + PyObject *inst = SWIG_Python_NewShadowInstance(clientdata, robj); + SWIG_Py_DECREF(robj); + robj = inst; + } + return robj; +} + +/* Create a new packed object */ + +SWIGRUNTIMEINLINE PyObject * +SWIG_Python_NewPackedObj(void *ptr, size_t sz, swig_type_info *type) { + return ptr ? SwigPyPacked_New((void *) ptr, sz, type) : SWIG_Py_Void(); +} + +/* -----------------------------------------------------------------------------* + * Get type list + * -----------------------------------------------------------------------------*/ + +#ifdef SWIG_LINK_RUNTIME +void *SWIG_ReturnGlobalTypeList(void *); +#endif + +static PyObject *Swig_TypeCache_global = NULL; + +/* The python cached type query */ +SWIGRUNTIME PyObject * +SWIG_Python_TypeCache(void) { + if (Swig_TypeCache_global == NULL) { + Swig_TypeCache_global = PyDict_New(); + } + return Swig_TypeCache_global; +} + +SWIGRUNTIME swig_module_info * +SWIG_Python_GetModule(void *SWIGUNUSEDPARM(clientdata)) { +#ifdef SWIG_LINK_RUNTIME + static void *type_pointer = (void *)0; + /* first check if module already created */ + if (!type_pointer) { + type_pointer = SWIG_ReturnGlobalTypeList((void *)0); + } +#else + void *type_pointer = PyCapsule_Import(SWIGPY_CAPSULE_NAME, 0); + if (PyErr_Occurred()) { + PyErr_Clear(); + type_pointer = (void *)0; + } +#endif + return (swig_module_info *) type_pointer; +} + + +static int interpreter_counter = 0; /* how many (sub-)interpreters are using swig_module's types */ + +SWIGRUNTIME void +SWIG_Python_DestroyModule(PyObject *obj) +{ + swig_module_info *swig_module = (swig_module_info *) PyCapsule_GetPointer(obj, SWIGPY_CAPSULE_NAME); + swig_type_info **types = swig_module->types; + size_t i; + if (--interpreter_counter != 0) /* another sub-interpreter may still be using the swig_module's types */ + return; + for (i =0; i < swig_module->size; ++i) { + swig_type_info *ty = types[i]; + if (ty->owndata) { + SwigPyClientData *data = (SwigPyClientData *) ty->clientdata; + ty->clientdata = 0; + if (data) SwigPyClientData_Del(data); + } + } + SWIG_Py_DECREF(SWIG_This()); + Swig_This_global = NULL; + SWIG_Py_DECREF(SWIG_globals()); + Swig_Globals_global = NULL; + SWIG_Py_DECREF(SWIG_Python_TypeCache()); + Swig_TypeCache_global = NULL; + Swig_Capsule_global = NULL; +} + +SWIGRUNTIME void +SWIG_Python_SetModule(swig_module_info *swig_module) { +#if PY_VERSION_HEX >= 0x03000000 + /* Add a dummy module object into sys.modules */ + PyObject *module = PyImport_AddModule("swig_runtime_data" SWIG_RUNTIME_VERSION); +#else + static PyMethodDef swig_empty_runtime_method_table[] = { {NULL, NULL, 0, NULL} }; /* Sentinel */ + PyObject *module = Py_InitModule("swig_runtime_data" SWIG_RUNTIME_VERSION, swig_empty_runtime_method_table); +#endif + PyObject *pointer = PyCapsule_New((void *) swig_module, SWIGPY_CAPSULE_NAME, SWIG_Python_DestroyModule); + if (pointer && module) { + if (PyModule_AddObject(module, SWIGPY_CAPSULE_ATTR_NAME, pointer) == 0) { + ++interpreter_counter; + Swig_Capsule_global = pointer; + } else { + SWIG_Py_DECREF(pointer); + } + } else { + SWIG_Py_XDECREF(pointer); + } +} + +SWIGRUNTIME swig_type_info * +SWIG_Python_TypeQuery(const char *type) +{ + PyObject *cache = SWIG_Python_TypeCache(); + PyObject *key = SWIG_Python_str_FromChar(type); + PyObject *obj = PyDict_GetItem(cache, key); + swig_type_info *descriptor; + if (obj) { + descriptor = (swig_type_info *) PyCapsule_GetPointer(obj, NULL); + } else { + swig_module_info *swig_module = SWIG_GetModule(0); + descriptor = SWIG_TypeQueryModule(swig_module, swig_module, type); + if (descriptor) { + obj = PyCapsule_New((void*) descriptor, NULL, NULL); + if (obj) { + PyDict_SetItem(cache, key, obj); + SWIG_Py_DECREF(obj); + } + } + } + SWIG_Py_DECREF(key); + return descriptor; +} + +/* + For backward compatibility only +*/ +#define SWIG_POINTER_EXCEPTION 0 +#define SWIG_arg_fail(arg) SWIG_Python_ArgFail(arg) +#define SWIG_MustGetPtr(p, type, argnum, flags) SWIG_Python_MustGetPtr(p, type, argnum, flags) + +SWIGRUNTIME int +SWIG_Python_AddErrMesg(const char* mesg, int infront) +{ + if (PyErr_Occurred()) { + PyObject *type = 0; + PyObject *value = 0; + PyObject *traceback = 0; + PyErr_Fetch(&type, &value, &traceback); + if (value) { + PyObject *old_str = PyObject_Str(value); + PyObject *bytes = NULL; + const char *tmp = SWIG_PyUnicode_AsUTF8AndSize(old_str, NULL, &bytes); + const char *errmesg = tmp ? tmp : "Invalid error message"; + SWIG_Py_XINCREF(type); + PyErr_Clear(); + if (infront) { + PyErr_Format(type, "%s %s", mesg, errmesg); + } else { + PyErr_Format(type, "%s %s", errmesg, mesg); + } + SWIG_Py_XDECREF(bytes); + SWIG_Py_DECREF(old_str); + } + return 1; + } else { + return 0; + } +} + +SWIGRUNTIME int +SWIG_Python_ArgFail(int argnum) +{ + if (PyErr_Occurred()) { + /* add information about failing argument */ + char mesg[256]; + PyOS_snprintf(mesg, sizeof(mesg), "argument number %d:", argnum); + return SWIG_Python_AddErrMesg(mesg, 1); + } else { + return 0; + } +} + +SWIGRUNTIMEINLINE const char * +SwigPyObject_GetDesc(PyObject *self) +{ + SwigPyObject *v = (SwigPyObject *)self; + swig_type_info *ty = v ? v->ty : 0; + return ty ? ty->str : ""; +} + +SWIGRUNTIME void +SWIG_Python_TypeError(const char *type, PyObject *obj) +{ + (void) obj; + if (type) { +#if defined(SWIG_COBJECT_TYPES) + if (obj && SwigPyObject_Check(obj)) { + const char *otype = (const char *) SwigPyObject_GetDesc(obj); + if (otype) { + PyErr_Format(PyExc_TypeError, "a '%s' is expected, 'SwigPyObject(%s)' is received", + type, otype); + return; + } + } else +#endif + { +#ifndef Py_LIMITED_API + /* tp_name is not accessible */ + const char *otype = (obj ? obj->ob_type->tp_name : 0); + if (otype) { + PyObject *str = PyObject_Str(obj); + PyObject *bytes = NULL; + const char *cstr = str ? SWIG_PyUnicode_AsUTF8AndSize(str, NULL, &bytes) : 0; + if (cstr) { + PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s(%s)' is received", + type, otype, cstr); + } else { + PyErr_Format(PyExc_TypeError, "a '%s' is expected, '%s' is received", + type, otype); + } + SWIG_Py_XDECREF(bytes); + SWIG_Py_XDECREF(str); + return; + } +#endif + } + PyErr_Format(PyExc_TypeError, "a '%s' is expected", type); + } else { + PyErr_Format(PyExc_TypeError, "unexpected type is received"); + } +} + + +/* Convert a pointer value, signal an exception on a type mismatch */ +SWIGRUNTIME void * +SWIG_Python_MustGetPtr(PyObject *obj, swig_type_info *ty, int SWIGUNUSEDPARM(argnum), int flags) { + void *result; + if (SWIG_Python_ConvertPtr(obj, &result, ty, flags) == -1) { + PyErr_Clear(); + } + return result; +} + +#ifdef SWIGPYTHON_BUILTIN +SWIGRUNTIME int +SWIG_Python_NonDynamicSetAttr(PyObject *obj, PyObject *name, PyObject *value) { + PyTypeObject *tp = obj->ob_type; + PyObject *descr; + PyObject *encoded_name; + descrsetfunc f; + int res = -1; + +# ifdef Py_USING_UNICODE + if (PyString_Check(name)) { + name = PyUnicode_Decode(PyString_AsString(name), PyString_Size(name), NULL, NULL); + if (!name) + return -1; + } else if (!PyUnicode_Check(name)) +# else + if (!PyString_Check(name)) +# endif + { + PyErr_Format(PyExc_TypeError, "attribute name must be string, not '%.200s'", name->ob_type->tp_name); + return -1; + } else { + SWIG_Py_INCREF(name); + } + + if (!tp->tp_dict) { + if (PyType_Ready(tp) != 0) + goto done; + } + + descr = _PyType_Lookup(tp, name); + f = NULL; + if (descr != NULL) + f = descr->ob_type->tp_descr_set; + if (!f) { + if (PyString_Check(name)) { + encoded_name = name; + SWIG_Py_INCREF(name); + } else { + encoded_name = PyUnicode_AsUTF8String(name); + if (!encoded_name) + goto done; + } + PyErr_Format(PyExc_AttributeError, "'%.100s' object has no attribute '%.200s'", tp->tp_name, PyString_AsString(encoded_name)); + SWIG_Py_DECREF(encoded_name); + } else { + res = f(descr, obj, value); + } + + done: + SWIG_Py_DECREF(name); + return res; +} +#endif + + +#ifdef __cplusplus +} +#endif + + + +#define SWIG_exception_fail(code, msg) do { SWIG_Error(code, msg); SWIG_fail; } while(0) + +#define SWIG_contract_assert(expr, msg) do { if (!(expr)) { SWIG_Error(SWIG_RuntimeError, msg); SWIG_fail; } } while (0) + + + + #define SWIG_exception(code, msg) do { SWIG_Error(code, msg); SWIG_fail;; } while(0) + + +/* -------- TYPES TABLE (BEGIN) -------- */ + +#define SWIGTYPE_p_char swig_types[0] +#define SWIGTYPE_p_float swig_types[1] +#define SWIGTYPE_p_sentencepiece__ImmutableNBestSentencePieceText swig_types[2] +#define SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText swig_types[3] +#define SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText_ImmutableSentencePiece swig_types[4] +#define SWIGTYPE_p_sentencepiece__SentenceIterator swig_types[5] +#define SWIGTYPE_p_sentencepiece__SentencePieceNormalizer swig_types[6] +#define SWIGTYPE_p_sentencepiece__SentencePieceProcessor swig_types[7] +#define SWIGTYPE_p_sentencepiece__SentencePieceTrainer swig_types[8] +#define SWIGTYPE_p_std__string swig_types[9] +#define SWIGTYPE_p_std__unordered_mapT_std__string_std__string_t swig_types[10] +#define SWIGTYPE_p_std__vectorT_absl__string_view_t swig_types[11] +#define SWIGTYPE_p_std__vectorT_int_t swig_types[12] +#define SWIGTYPE_p_std__vectorT_std__vectorT_absl__string_view_t_t swig_types[13] +#define SWIGTYPE_p_std__vectorT_std__vectorT_int_t_t swig_types[14] +static swig_type_info *swig_types[16]; +static swig_module_info swig_module = {swig_types, 15, 0, 0, 0, 0}; +#define SWIG_TypeQuery(name) SWIG_TypeQueryModule(&swig_module, &swig_module, name) +#define SWIG_MangledTypeQuery(name) SWIG_MangledTypeQueryModule(&swig_module, &swig_module, name) + +/* -------- TYPES TABLE (END) -------- */ + +#ifdef SWIG_TypeQuery +# undef SWIG_TypeQuery +#endif +#define SWIG_TypeQuery SWIG_Python_TypeQuery + +/*----------------------------------------------- + @(target):= _sentencepiece.so + ------------------------------------------------*/ +#if PY_VERSION_HEX >= 0x03000000 +# define SWIG_init PyInit__sentencepiece + +#else +# define SWIG_init init_sentencepiece + +#endif + +#ifdef __cplusplus +#include +/* SwigValueWrapper is described in swig.swg */ +template class SwigValueWrapper { + struct SwigSmartPointer { + T *ptr; + SwigSmartPointer(T *p) : ptr(p) { } + ~SwigSmartPointer() { delete ptr; } + SwigSmartPointer& operator=(SwigSmartPointer& rhs) { T* oldptr = ptr; ptr = 0; delete oldptr; ptr = rhs.ptr; rhs.ptr = 0; return *this; } + void reset(T *p) { T* oldptr = ptr; ptr = 0; delete oldptr; ptr = p; } + } pointer; + SwigValueWrapper& operator=(const SwigValueWrapper& rhs); + SwigValueWrapper(const SwigValueWrapper& rhs); +public: + SwigValueWrapper() : pointer(0) { } + SwigValueWrapper& operator=(const T& t) { SwigSmartPointer tmp(new T(t)); pointer = tmp; return *this; } +#if __cplusplus >=201103L + SwigValueWrapper& operator=(T&& t) { SwigSmartPointer tmp(new T(std::move(t))); pointer = tmp; return *this; } + operator T&&() const { return std::move(*pointer.ptr); } +#else + operator T&() const { return *pointer.ptr; } +#endif + T *operator&() const { return pointer.ptr; } + static void reset(SwigValueWrapper& t, T *p) { t.pointer.reset(p); } +}; + +/* + * SwigValueInit() is a generic initialisation solution as the following approach: + * + * T c_result = T(); + * + * doesn't compile for all types for example: + * + * unsigned int c_result = unsigned int(); + */ +template T SwigValueInit() { + return T(); +} + +#if __cplusplus >=201103L +# define SWIG_STD_MOVE(OBJ) std::move(OBJ) +#else +# define SWIG_STD_MOVE(OBJ) OBJ +#endif + +#endif + + +#define SWIG_as_voidptr(a) const_cast< void * >(static_cast< const void * >(a)) +#define SWIG_as_voidptrptr(a) ((void)SWIG_as_voidptr(*a),reinterpret_cast< void** >(a)) + + +#include + + +namespace swig { + class SwigPtr_PyObject { + protected: + PyObject *_obj; + + public: + SwigPtr_PyObject() :_obj(0) + { + } + + SwigPtr_PyObject(const SwigPtr_PyObject& item) : _obj(item._obj) + { + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + SWIG_Py_XINCREF(_obj); + SWIG_PYTHON_THREAD_END_BLOCK; + } + + SwigPtr_PyObject(PyObject *obj, bool initial_ref = true) :_obj(obj) + { + if (initial_ref) { + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + SWIG_Py_XINCREF(_obj); + SWIG_PYTHON_THREAD_END_BLOCK; + } + } + + SwigPtr_PyObject & operator=(const SwigPtr_PyObject& item) + { + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + SWIG_Py_XINCREF(item._obj); + SWIG_Py_XDECREF(_obj); + _obj = item._obj; + SWIG_PYTHON_THREAD_END_BLOCK; + return *this; + } + + ~SwigPtr_PyObject() + { + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + SWIG_Py_XDECREF(_obj); + SWIG_PYTHON_THREAD_END_BLOCK; + } + + operator PyObject *() const + { + return _obj; + } + + PyObject *operator->() const + { + return _obj; + } + }; +} + + +namespace swig { + struct SwigVar_PyObject : SwigPtr_PyObject { + SwigVar_PyObject(PyObject* obj = 0) : SwigPtr_PyObject(obj, false) { } + + SwigVar_PyObject & operator = (PyObject* obj) + { + SWIG_Py_XDECREF(_obj); + _obj = obj; + return *this; + } + }; +} + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { +PyObject* kUnicodeInput = reinterpret_cast(0x1); +PyObject* kByteInput = reinterpret_cast(0x2); + +using BytesArray = std::vector; + +inline void ReleaseResultObject(PyObject *obj) { + if (obj != nullptr && obj != kUnicodeInput && obj != kByteInput) { + Py_XDECREF(obj); + } +} + +class PyInputString { + public: + explicit PyInputString(PyObject* obj) { + if (PyUnicode_Check(obj)) { + str_ = const_cast(PyUnicode_AsUTF8AndSize(obj, &size_)); + input_type_ = kUnicodeInput; + } else if (PyBytes_Check(obj)) { + PyBytes_AsStringAndSize(obj, &str_, &size_); + input_type_ = kByteInput; + } else { + str_ = nullptr; + } + } + absl::string_view str() const { return absl::string_view(data(), size()); } + const char* data() const { return str_; } + Py_ssize_t size() const { return size_; } + bool IsAvalable() const { return str_ != nullptr; } + PyObject *input_type() const { return input_type_; } + + static bool IsUnicode(PyObject *resultobj) { + return (resultobj == nullptr || resultobj == kUnicodeInput); + } + + private: + PyObject* input_type_ = nullptr; + char* str_ = nullptr; + Py_ssize_t size_ = 0; +}; + +PyObject* MakePyOutputString(const std::string& output, + PyObject *resultobj) { + if (PyInputString::IsUnicode(resultobj)) { + return PyUnicode_FromStringAndSize(output.data(), output.size()); + } + return PyBytes_FromStringAndSize(output.data(), output.size()); +} + +PyObject* MakePyOutputBytes(const sentencepiece::util::bytes& output) { + return PyBytes_FromStringAndSize(output.data(), output.size()); +} + +int ToSwigError(sentencepiece::util::StatusCode code) { + switch (code) { + case sentencepiece::util::StatusCode::kNotFound: + return SWIG_IOError; + case sentencepiece::util::StatusCode::kOutOfRange: + return SWIG_IndexError; + case sentencepiece::util::StatusCode::kInvalidArgument: + return SWIG_SyntaxError; + default: + return SWIG_RuntimeError; + } + return SWIG_RuntimeError; +} + +class PySentenceIterator : public sentencepiece::SentenceIterator { + public: + PySentenceIterator(PyObject *iter) : iter_(iter) { + item_ = PyIter_Next(iter_); + CopyValue(); + } + + ~PySentenceIterator() { + // Py_XDECREF(iter_); + } + + bool done() const override { + return item_ == nullptr; + } + + void Next() override { + item_ = PyIter_Next(iter_); + CopyValue(); + } + + const std::string &value() const override { + return value_; + } + + sentencepiece::util::Status status() const override { + return status_; + } + + private: + void CopyValue() { + if (item_ == nullptr) return; + const PyInputString ustring(item_); + if (ustring.IsAvalable()) { + const char *data = ustring.data(); + size_t size = ustring.size(); + while (size > 0) { + if (data[size - 1] == '\r' || data[size - 1] == '\n') + --size; + else + break; + } + value_.assign(data, size); + } else { + status_ = sentencepiece::util::Status(sentencepiece::util::StatusCode::kInternal, + "Not a string."); + } + Py_XDECREF(item_); + } + PyObject *iter_ = nullptr; + PyObject *item_ = nullptr; + std::string value_; + sentencepiece::util::Status status_; +}; + +inline void RewriteIds(const sentencepiece::SentencePieceProcessor &sp, + std::vector *ids, + bool add_bos, bool add_eos, bool reverse, bool emit_unk_piece) { + if (!add_bos && !add_eos && !reverse) return; + if (reverse) std::reverse(ids->begin(), ids->end()); + if (add_bos) ids->insert(ids->begin(), sp.bos_id()); + if (add_eos) ids->push_back(sp.eos_id()); +} + +inline void RewriteIds(const sentencepiece::SentencePieceProcessor &sp, + std::vector *pieces, + bool add_bos, bool add_eos, bool reverse, bool emit_unk_piece) { + if (!add_bos && !add_eos && !reverse && !emit_unk_piece) return; + if (reverse) std::reverse(pieces->begin(), pieces->end()); + if (add_bos) pieces->insert(pieces->begin(), sp.IdToPiece(sp.bos_id())); + if (add_eos) pieces->push_back(sp.IdToPiece(sp.eos_id())); + if (emit_unk_piece) { + const auto &unk = sp.IdToPiece(sp.unk_id()); + for (auto &piece : *pieces) { + const int id = sp.PieceToId(piece); + if (id == sp.unk_id()) { + piece = unk; + } + } + } +} + +inline void RewriteIds(const sentencepiece::SentencePieceProcessor &sp, + sentencepiece::util::bytes *proto, + bool add_bos, bool add_eos, bool reverse, bool emit_unk_piece) { + if (add_bos || add_eos || reverse || emit_unk_piece) { + throw sentencepiece::util::Status( + sentencepiece::util::StatusCode::kUnimplemented, + "add_bos, add_eos, reverse, and emit_unk_piece is not supported in proto API"); + } +} + +inline void RewriteIds(const sentencepiece::SentencePieceProcessor &sp, + sentencepiece::ImmutableSentencePieceText *proto, + bool add_bos, bool add_eos, bool reverse, bool emit_unk_piece) { + if (add_bos || add_eos || reverse || emit_unk_piece) { + throw sentencepiece::util::Status( + sentencepiece::util::StatusCode::kUnimplemented, + "add_bos, add_eos, reverse, and emit_unk_piece is not supported in proto API"); + } +} + +inline void CheckIds(const std::vector &ids, int num_pieces) { + for (int id : ids) { + if (id < 0 || id >= num_pieces) { + throw sentencepiece::util::Status( + sentencepiece::util::StatusCode::kOutOfRange, + "piece id is out of range."); + } + } +} + +inline void CheckIds(const std::vector &ids, int num_pieces) {} + +inline void CheckIdsBatch(const std::vector> &ids, int num_pieces) { + for (const auto &v : ids) CheckIds(v, num_pieces); +} + +template +inline void ConvertToUnicodeSpans(T *proto) {} + +template <> +inline void ConvertToUnicodeSpans(sentencepiece::ImmutableSentencePieceText *proto) { + proto->ConvertToUnicodeSpans(); +} + +template <> +inline void ConvertToUnicodeSpans(sentencepiece::ImmutableNBestSentencePieceText *proto) { + proto->ConvertToUnicodeSpans(); +} + +class ThreadPool { + public: + explicit ThreadPool(size_t request_size) : + request_size_(request_size) {} + + virtual ~ThreadPool() { + for (auto &task : tasks_) { + task.join(); + } + } + + void Schedule(std::function closure) { + static constexpr size_t kMinThreadSize = 2; + if (request_size_ < kMinThreadSize) { + closure(); + } else { + tasks_.emplace_back(closure); + } + } + + private: + size_t request_size_ = 0; + std::vector tasks_; +}; + +template +inline void InitNumThreads(const std::vector &ins, int *num_threads) { + if (*num_threads < 0) { + *num_threads = std::thread::hardware_concurrency(); + } + *num_threads = std::max(1, + std::min({*num_threads, + static_cast(ins.size()), 256})); +} + +#define DEFINE_ENCODE_BATCH_FUNC_IMPL(FuncName, InType, OutType) \ + std::vector outs(ins.size()); \ + InitNumThreads(ins, &num_threads); \ + { \ + ThreadPool pool(ins.size()); \ + std::atomic index = 0; \ + for (int n = 0; n < num_threads; ++n) { \ + pool.Schedule([&]() { \ + size_t i = 0; \ + while ((i = std::atomic_fetch_add(&index, 1)) < outs.size()) { \ + auto out = enable_sampling ? \ + self->Sample##FuncName(ins[i], \ + nbest_size, alpha) : \ + self->FuncName(ins[i]); \ + RewriteIds(*self, &out, add_bos, add_eos, reverse, \ + emit_unk_piece); \ + ConvertToUnicodeSpans(&out); \ + outs[i] = std::move(out); \ + } \ + }); \ + } \ + } \ + return outs; + +#define DEFINE_DECODE_BATCH_FUNC_IMPL(FuncName, InType, OutType) \ + std::vector outs(ins.size()); \ + InitNumThreads(ins, &num_threads); \ + { \ + std::atomic index = 0; \ + ThreadPool pool(ins.size()); \ + for (int n = 0; n < num_threads; ++n) { \ + pool.Schedule([&]() { \ + size_t i = 0; \ + while ((i = std::atomic_fetch_add(&index, 1)) < outs.size()) { \ + auto out = self->FuncName(ins[i]); \ + ConvertToUnicodeSpans(&out); \ + outs[i] = std::move(out); \ + } \ + }); \ + } \ + } \ + return outs; + +} // namespace + + +SWIGINTERNINLINE PyObject* + SWIG_From_unsigned_SS_int (unsigned int value) +{ + return PyInt_FromSize_t((size_t) value); +} + +SWIGINTERN sentencepiece::util::bytes const &sentencepiece_ImmutableSentencePieceText_ImmutableSentencePiece__surface_as_bytes(sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece const *self){ + return self->surface(); + } +SWIGINTERN sentencepiece::util::bytes const &sentencepiece_ImmutableSentencePieceText_ImmutableSentencePiece__piece_as_bytes(sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece const *self){ + return self->piece(); + } + + #define SWIG_From_long PyInt_FromLong + + +SWIGINTERNINLINE PyObject* +SWIG_From_unsigned_SS_long (unsigned long value) +{ + return (value > LONG_MAX) ? + PyLong_FromUnsignedLong(value) : PyInt_FromLong(static_cast< long >(value)); +} + + +#include +#if !defined(SWIG_NO_LLONG_MAX) +# if !defined(LLONG_MAX) && defined(__GNUC__) && defined (__LONG_LONG_MAX__) +# define LLONG_MAX __LONG_LONG_MAX__ +# define LLONG_MIN (-LLONG_MAX - 1LL) +# define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL) +# endif +#endif + + +#if defined(LLONG_MAX) && !defined(SWIG_LONG_LONG_AVAILABLE) +# define SWIG_LONG_LONG_AVAILABLE +#endif + + +#ifdef SWIG_LONG_LONG_AVAILABLE +SWIGINTERNINLINE PyObject* +SWIG_From_unsigned_SS_long_SS_long (unsigned long long value) +{ + return (value > LONG_MAX) ? + PyLong_FromUnsignedLongLong(value) : PyInt_FromLong(static_cast< long >(value)); +} +#endif + + +SWIGINTERNINLINE PyObject * +SWIG_From_size_t (size_t value) +{ +#ifdef SWIG_LONG_LONG_AVAILABLE + if (sizeof(size_t) <= sizeof(unsigned long)) { +#endif + return SWIG_From_unsigned_SS_long (static_cast< unsigned long >(value)); +#ifdef SWIG_LONG_LONG_AVAILABLE + } else { + /* assume sizeof(size_t) <= sizeof(unsigned long long) */ + return SWIG_From_unsigned_SS_long_SS_long (static_cast< unsigned long long >(value)); + } +#endif +} + + +SWIGINTERN int +SWIG_AsVal_double (PyObject *obj, double *val) +{ + int res = SWIG_TypeError; + if (PyFloat_Check(obj)) { + if (val) *val = PyFloat_AsDouble(obj); + return SWIG_OK; +#if PY_VERSION_HEX < 0x03000000 + } else if (PyInt_Check(obj)) { + if (val) *val = (double) PyInt_AsLong(obj); + return SWIG_OK; +#endif + } else if (PyLong_Check(obj)) { + double v = PyLong_AsDouble(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_OK; + } else { + PyErr_Clear(); + } + } +#ifdef SWIG_PYTHON_CAST_MODE + { + int dispatch = 0; + double d = PyFloat_AsDouble(obj); + if (!PyErr_Occurred()) { + if (val) *val = d; + return SWIG_AddCast(SWIG_OK); + } else { + PyErr_Clear(); + } + if (!dispatch) { + long v = PyLong_AsLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_AddCast(SWIG_AddCast(SWIG_OK)); + } else { + PyErr_Clear(); + } + } + } +#endif + return res; +} + + +#include + + +#include + + +SWIGINTERNINLINE int +SWIG_CanCastAsInteger(double *d, double min, double max) { + double x = *d; + if ((min <= x && x <= max)) { + double fx, cx, rd; + errno = 0; + fx = floor(x); + cx = ceil(x); + rd = ((x - fx) < 0.5) ? fx : cx; /* simple rint */ + if ((errno == EDOM) || (errno == ERANGE)) { + errno = 0; + } else { + double summ, reps, diff; + if (rd < x) { + diff = x - rd; + } else if (rd > x) { + diff = rd - x; + } else { + return 1; + } + summ = rd + x; + reps = diff/summ; + if (reps < 8*DBL_EPSILON) { + *d = rd; + return 1; + } + } + } + return 0; +} + + +SWIGINTERN int +SWIG_AsVal_long (PyObject *obj, long* val) +{ +#if PY_VERSION_HEX < 0x03000000 + if (PyInt_Check(obj)) { + if (val) *val = PyInt_AsLong(obj); + return SWIG_OK; + } else +#endif + if (PyLong_Check(obj)) { + long v = PyLong_AsLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_OK; + } else { + PyErr_Clear(); + return SWIG_OverflowError; + } + } +#ifdef SWIG_PYTHON_CAST_MODE + { + int dispatch = 0; + long v = PyInt_AsLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_AddCast(SWIG_OK); + } else { + PyErr_Clear(); + } + if (!dispatch) { + double d; + int res = SWIG_AddCast(SWIG_AsVal_double (obj,&d)); + // Largest double not larger than LONG_MAX (not portably calculated easily) + // Note that double(LONG_MAX) is stored in a double rounded up by one (for 64-bit long) + // 0x7ffffffffffffc00LL == (int64_t)std::nextafter(double(__uint128_t(LONG_MAX)+1), double(0)) + const double long_max = sizeof(long) == 8 ? 0x7ffffffffffffc00LL : LONG_MAX; + // No equivalent needed for 64-bit double(LONG_MIN) is exactly LONG_MIN + if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, LONG_MIN, long_max)) { + if (val) *val = (long)(d); + return res; + } + } + } +#endif + return SWIG_TypeError; +} + + +SWIGINTERN int +SWIG_AsVal_int (PyObject * obj, int *val) +{ + long v; + int res = SWIG_AsVal_long (obj, &v); + if (SWIG_IsOK(res)) { + if ((v < INT_MIN || v > INT_MAX)) { + return SWIG_OverflowError; + } else { + if (val) *val = static_cast< int >(v); + } + } + return res; +} + + + #define SWIG_From_double PyFloat_FromDouble + + +SWIGINTERNINLINE PyObject * +SWIG_From_float (float value) +{ + return SWIG_From_double (value); +} + +SWIGINTERN sentencepiece::util::bytes const &sentencepiece_ImmutableSentencePieceText__text_as_bytes(sentencepiece::ImmutableSentencePieceText const *self){ + return self->text(); + } + +SWIGINTERN swig_type_info* +SWIG_pchar_descriptor(void) +{ + static swig_type_info* info = 0; + if (!info) { + info = SWIG_TypeQuery("_p_char"); + } + return info; +} + + +/* Return string from Python obj. NOTE: obj must remain in scope in order + to use the returned cptr (but only when alloc is set to SWIG_OLDOBJ) */ +SWIGINTERN int +SWIG_AsCharPtrAndSize(PyObject *obj, char **cptr, size_t *psize, int *alloc) +{ +#if PY_VERSION_HEX>=0x03000000 +#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR) + if (PyBytes_Check(obj)) +#else + if (PyUnicode_Check(obj)) +#endif +#else + if (PyString_Check(obj)) +#endif + { + char *cstr; Py_ssize_t len; + PyObject *bytes = NULL; + int ret = SWIG_OK; + if (alloc) + *alloc = SWIG_OLDOBJ; +#if PY_VERSION_HEX>=0x03000000 && defined(SWIG_PYTHON_STRICT_BYTE_CHAR) + if (PyBytes_AsStringAndSize(obj, &cstr, &len) == -1) + return SWIG_TypeError; +#else + cstr = (char *)SWIG_PyUnicode_AsUTF8AndSize(obj, &len, &bytes); + if (!cstr) + return SWIG_TypeError; + /* The returned string is only duplicated if the char * returned is not owned and memory managed by obj */ + if (bytes && cptr) { + if (alloc) { + cstr = reinterpret_cast< char* >(memcpy(new char[len + 1], cstr, sizeof(char)*(len + 1))); + *alloc = SWIG_NEWOBJ; + } else { + /* alloc must be set in order to clean up allocated memory */ + return SWIG_RuntimeError; + } + } +#endif + if (cptr) *cptr = cstr; + if (psize) *psize = len + 1; + SWIG_Py_XDECREF(bytes); + return ret; + } else { +#if defined(SWIG_PYTHON_2_UNICODE) +#if defined(SWIG_PYTHON_STRICT_BYTE_CHAR) +#error "Cannot use both SWIG_PYTHON_2_UNICODE and SWIG_PYTHON_STRICT_BYTE_CHAR at once" +#endif +#if PY_VERSION_HEX<0x03000000 + if (PyUnicode_Check(obj)) { + char *cstr; Py_ssize_t len; + if (!alloc && cptr) { + return SWIG_RuntimeError; + } + obj = PyUnicode_AsUTF8String(obj); + if (!obj) + return SWIG_TypeError; + if (PyString_AsStringAndSize(obj, &cstr, &len) != -1) { + if (cptr) { + if (alloc) *alloc = SWIG_NEWOBJ; + *cptr = reinterpret_cast< char* >(memcpy(new char[len + 1], cstr, sizeof(char)*(len + 1))); + } + if (psize) *psize = len + 1; + + SWIG_Py_XDECREF(obj); + return SWIG_OK; + } else { + SWIG_Py_XDECREF(obj); + } + } +#endif +#endif + + swig_type_info* pchar_descriptor = SWIG_pchar_descriptor(); + if (pchar_descriptor) { + void* vptr = 0; + if (SWIG_ConvertPtr(obj, &vptr, pchar_descriptor, 0) == SWIG_OK) { + if (cptr) *cptr = (char *) vptr; + if (psize) *psize = vptr ? (strlen((char *)vptr) + 1) : 0; + if (alloc) *alloc = SWIG_OLDOBJ; + return SWIG_OK; + } + } + } + return SWIG_TypeError; +} + + + + + +/* Getting isfinite working pre C99 across multiple platforms is non-trivial. Users can provide SWIG_isfinite on older platforms. */ +#ifndef SWIG_isfinite +/* isfinite() is a macro for C99 */ +# if defined(isfinite) +# define SWIG_isfinite(X) (isfinite(X)) +# elif defined(__cplusplus) && __cplusplus >= 201103L +/* Use a template so that this works whether isfinite() is std::isfinite() or + * in the global namespace. The reality seems to vary between compiler + * versions. + * + * Make sure namespace std exists to avoid compiler warnings. + * + * extern "C++" is required as this fragment can end up inside an extern "C" { } block + */ +namespace std { } +extern "C++" template +inline int SWIG_isfinite_func(T x) { + using namespace std; + return isfinite(x); +} +# define SWIG_isfinite(X) (SWIG_isfinite_func(X)) +# elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)) +# define SWIG_isfinite(X) (__builtin_isfinite(X)) +# elif defined(_MSC_VER) +# define SWIG_isfinite(X) (_finite(X)) +# elif defined(__sun) && defined(__SVR4) +# include +# define SWIG_isfinite(X) (finite(X)) +# endif +#endif + + +/* Accept infinite as a valid float value unless we are unable to check if a value is finite */ +#ifdef SWIG_isfinite +# define SWIG_Float_Overflow_Check(X) ((X < -FLT_MAX || X > FLT_MAX) && SWIG_isfinite(X)) +#else +# define SWIG_Float_Overflow_Check(X) ((X < -FLT_MAX || X > FLT_MAX)) +#endif + + +SWIGINTERN int +SWIG_AsVal_float (PyObject * obj, float *val) +{ + double v; + int res = SWIG_AsVal_double (obj, &v); + if (SWIG_IsOK(res)) { + if (SWIG_Float_Overflow_Check(v)) { + return SWIG_OverflowError; + } else { + if (val) *val = static_cast< float >(v); + } + } + return res; +} + + +SWIGINTERNINLINE PyObject* + SWIG_From_int (int value) +{ + return PyInt_FromLong((long) value); +} + + +SWIGINTERNINLINE PyObject* + SWIG_From_bool (bool value) +{ + return PyBool_FromLong(value ? 1 : 0); +} + +SWIGINTERN sentencepiece::util::Status sentencepiece_SentencePieceProcessor_LoadFromFile(sentencepiece::SentencePieceProcessor *self,absl::string_view arg){ + return self->Load(arg); + } + +SWIGINTERN int +SWIG_AsVal_bool (PyObject *obj, bool *val) +{ + int r; + if (!PyBool_Check(obj)) + return SWIG_ERROR; + r = PyObject_IsTrue(obj); + if (r == -1) + return SWIG_ERROR; + if (val) *val = r ? true : false; + return SWIG_OK; +} + +SWIGINTERN std::vector< int > sentencepiece_SentencePieceProcessor__EncodeAsIds(sentencepiece::SentencePieceProcessor const *self,absl::string_view text,bool enable_sampling,int nbest_size,float alpha,bool add_bos,bool add_eos,bool reverse,bool emit_unk_piece){ + auto ids = enable_sampling ? + self->SampleEncodeAsIds(text, nbest_size, alpha) : + self->EncodeAsIds(text); + RewriteIds(*self, &ids, add_bos, add_eos, reverse, emit_unk_piece); + return ids; + } +SWIGINTERN std::vector< std::string > sentencepiece_SentencePieceProcessor__EncodeAsPieces(sentencepiece::SentencePieceProcessor const *self,absl::string_view text,bool enable_sampling,int nbest_size,float alpha,bool add_bos,bool add_eos,bool reverse,bool emit_unk_piece){ + auto pieces = enable_sampling ? + self->SampleEncodeAsPieces(text, nbest_size, alpha) : + self->EncodeAsPieces(text); + RewriteIds(*self, &pieces, add_bos, add_eos, reverse, emit_unk_piece); + return pieces; + } +SWIGINTERN sentencepiece::util::bytes sentencepiece_SentencePieceProcessor__EncodeAsSerializedProto(sentencepiece::SentencePieceProcessor const *self,absl::string_view text,bool enable_sampling,int nbest_size,float alpha,bool add_bos,bool add_eos,bool reverse,bool emit_unk_piece){ + auto proto = enable_sampling ? + self->SampleEncodeAsSerializedProto(text, nbest_size, alpha) : + self->EncodeAsSerializedProto(text); + RewriteIds(*self, &proto, add_bos, add_eos, reverse, emit_unk_piece); + return proto; + } +SWIGINTERN sentencepiece::ImmutableSentencePieceText sentencepiece_SentencePieceProcessor__EncodeAsImmutableProto(sentencepiece::SentencePieceProcessor const *self,absl::string_view text,bool enable_sampling,int nbest_size,float alpha,bool add_bos,bool add_eos,bool reverse,bool emit_unk_piece){ + auto proto = enable_sampling ? + self->SampleEncodeAsImmutableProto(text, nbest_size, alpha) : + self->EncodeAsImmutableProto(text); + proto.ConvertToUnicodeSpans(); + RewriteIds(*self, &proto, add_bos, add_eos, reverse, emit_unk_piece); + return proto; + } +SWIGINTERN std::vector< std::vector< int > > sentencepiece_SentencePieceProcessor__EncodeAsIdsBatch(sentencepiece::SentencePieceProcessor const *self,std::vector< absl::string_view > const &ins,int num_threads,bool enable_sampling,int nbest_size,float alpha,bool add_bos,bool add_eos,bool reverse,bool emit_unk_piece){ + DEFINE_ENCODE_BATCH_FUNC_IMPL(EncodeAsIds, + absl::string_view, std::vector); + } +SWIGINTERN std::vector< std::vector< std::string > > sentencepiece_SentencePieceProcessor__EncodeAsPiecesBatch(sentencepiece::SentencePieceProcessor const *self,std::vector< absl::string_view > const &ins,int num_threads,bool enable_sampling,int nbest_size,float alpha,bool add_bos,bool add_eos,bool reverse,bool emit_unk_piece){ + DEFINE_ENCODE_BATCH_FUNC_IMPL(EncodeAsPieces, + absl::string_view, std::vector); + } +SWIGINTERN BytesArray sentencepiece_SentencePieceProcessor__EncodeAsSerializedProtoBatch(sentencepiece::SentencePieceProcessor const *self,std::vector< absl::string_view > const &ins,int num_threads,bool enable_sampling,int nbest_size,float alpha,bool add_bos,bool add_eos,bool reverse,bool emit_unk_piece){ + DEFINE_ENCODE_BATCH_FUNC_IMPL(EncodeAsSerializedProto, + absl::string_view, + sentencepiece::util::bytes); + } +SWIGINTERN std::vector< sentencepiece::ImmutableSentencePieceText > sentencepiece_SentencePieceProcessor__EncodeAsImmutableProtoBatch(sentencepiece::SentencePieceProcessor const *self,std::vector< absl::string_view > const &ins,int num_threads,bool enable_sampling,int nbest_size,float alpha,bool add_bos,bool add_eos,bool reverse,bool emit_unk_piece){ + DEFINE_ENCODE_BATCH_FUNC_IMPL(EncodeAsImmutableProto, + absl::string_view, + sentencepiece::ImmutableSentencePieceText); + } +SWIGINTERN std::string sentencepiece_SentencePieceProcessor__DecodeIds(sentencepiece::SentencePieceProcessor const *self,std::vector< int > const &ids){ + CheckIds(ids, self->GetPieceSize()); + return self->DecodeIds(ids); + } +SWIGINTERN sentencepiece::util::bytes sentencepiece_SentencePieceProcessor__DecodeIdsAsBytes(sentencepiece::SentencePieceProcessor const *self,std::vector< int > const &ids){ + CheckIds(ids, self->GetPieceSize()); + return self->DecodeIds(ids); + } +SWIGINTERN std::string sentencepiece_SentencePieceProcessor__DecodePieces(sentencepiece::SentencePieceProcessor const *self,std::vector< absl::string_view > const &pieces){ + return self->DecodePieces(pieces); + } +SWIGINTERN sentencepiece::util::bytes sentencepiece_SentencePieceProcessor__DecodeIdsAsSerializedProto(sentencepiece::SentencePieceProcessor const *self,std::vector< int > const &ids){ + CheckIds(ids, self->GetPieceSize()); + return self->DecodeIdsAsSerializedProto(ids); + } +SWIGINTERN sentencepiece::util::bytes sentencepiece_SentencePieceProcessor__DecodePiecesAsSerializedProto(sentencepiece::SentencePieceProcessor const *self,std::vector< absl::string_view > const &pieces){ + CheckIds(pieces, self->GetPieceSize()); + return self->DecodePiecesAsSerializedProto(pieces); + } +SWIGINTERN sentencepiece::ImmutableSentencePieceText sentencepiece_SentencePieceProcessor__DecodeIdsAsImmutableProto(sentencepiece::SentencePieceProcessor const *self,std::vector< int > const &ids){ + CheckIds(ids, self->GetPieceSize()); + auto proto = self->DecodeIdsAsImmutableProto(ids); + proto.ConvertToUnicodeSpans(); + return proto; + } +SWIGINTERN sentencepiece::ImmutableSentencePieceText sentencepiece_SentencePieceProcessor__DecodePiecesAsImmutableProto(sentencepiece::SentencePieceProcessor const *self,std::vector< absl::string_view > const &pieces){ + CheckIds(pieces, self->GetPieceSize()); + auto proto= self->DecodePiecesAsImmutableProto(pieces); + proto.ConvertToUnicodeSpans(); + return proto; + } +SWIGINTERN std::vector< std::string > sentencepiece_SentencePieceProcessor__DecodeIdsBatch(sentencepiece::SentencePieceProcessor const *self,std::vector< std::vector< int > > const &ins,int num_threads){ + CheckIdsBatch(ins, self->GetPieceSize()); + DEFINE_DECODE_BATCH_FUNC_IMPL(DecodeIds, int, std::string); + } +SWIGINTERN BytesArray sentencepiece_SentencePieceProcessor__DecodeIdsAsBytesBatch(sentencepiece::SentencePieceProcessor const *self,std::vector< std::vector< int > > const &ins,int num_threads){ + CheckIdsBatch(ins, self->GetPieceSize()); + DEFINE_DECODE_BATCH_FUNC_IMPL(DecodeIds, int, std::string); + } +SWIGINTERN BytesArray sentencepiece_SentencePieceProcessor__DecodeIdsAsSerializedProtoBatch(sentencepiece::SentencePieceProcessor const *self,std::vector< std::vector< int > > const &ins,int num_threads){ + CheckIdsBatch(ins, self->GetPieceSize()); + DEFINE_DECODE_BATCH_FUNC_IMPL(DecodeIdsAsSerializedProto, int, + sentencepiece::util::bytes); + } +SWIGINTERN std::vector< sentencepiece::ImmutableSentencePieceText > sentencepiece_SentencePieceProcessor__DecodeIdsAsImmutableProtoBatch(sentencepiece::SentencePieceProcessor const *self,std::vector< std::vector< int > > const &ins,int num_threads){ + CheckIdsBatch(ins, self->GetPieceSize()); + DEFINE_DECODE_BATCH_FUNC_IMPL(DecodeIdsAsImmutableProto, int, + sentencepiece::ImmutableSentencePieceText); + } +SWIGINTERN std::vector< std::string > sentencepiece_SentencePieceProcessor__DecodePiecesBatch(sentencepiece::SentencePieceProcessor const *self,std::vector< std::vector< absl::string_view > > const &ins,int num_threads){ + DEFINE_DECODE_BATCH_FUNC_IMPL(DecodePieces, std::string, std::string); + } +SWIGINTERN BytesArray sentencepiece_SentencePieceProcessor__DecodePiecesAsSerializedProtoBatch(sentencepiece::SentencePieceProcessor const *self,std::vector< std::vector< absl::string_view > > const &ins,int num_threads){ + DEFINE_DECODE_BATCH_FUNC_IMPL(DecodePiecesAsSerializedProto, std::string, + sentencepiece::util::bytes); + } +SWIGINTERN std::vector< sentencepiece::ImmutableSentencePieceText > sentencepiece_SentencePieceProcessor__DecodePiecesAsImmutableProtoBatch(sentencepiece::SentencePieceProcessor const *self,std::vector< std::vector< absl::string_view > > const &ins,int num_threads){ + DEFINE_DECODE_BATCH_FUNC_IMPL(DecodePiecesAsImmutableProto, std::string, + sentencepiece::ImmutableSentencePieceText); + } +SWIGINTERN std::vector< std::vector< int > > sentencepiece_SentencePieceProcessor__NBestEncodeAsIds(sentencepiece::SentencePieceProcessor const *self,absl::string_view text,int nbest_size,bool add_bos,bool add_eos,bool reverse,bool emit_unk_piece){ + auto idss = self->NBestEncodeAsIds(text, nbest_size); + for (auto &ids : idss) { + RewriteIds(*self, &ids, add_bos, add_eos, reverse, emit_unk_piece); + } + return idss; + } +SWIGINTERN std::vector< std::vector< std::string > > sentencepiece_SentencePieceProcessor__NBestEncodeAsPieces(sentencepiece::SentencePieceProcessor const *self,absl::string_view text,int nbest_size,bool add_bos,bool add_eos,bool reverse,bool emit_unk_piece){ + auto piecess = self->NBestEncodeAsPieces(text, nbest_size); + for (auto &pieces : piecess) { + RewriteIds(*self, &pieces, add_bos, add_eos, reverse, emit_unk_piece); + } + return piecess; + } +SWIGINTERN sentencepiece::util::bytes sentencepiece_SentencePieceProcessor__NBestEncodeAsSerializedProto(sentencepiece::SentencePieceProcessor const *self,absl::string_view text,int nbest_size,bool add_bos,bool add_eos,bool reverse,bool emit_unk_piece){ + RewriteIds(*self, static_cast(nullptr), + add_bos, add_eos, reverse, emit_unk_piece); + return self->NBestEncodeAsSerializedProto(text, nbest_size); + } +SWIGINTERN sentencepiece::ImmutableNBestSentencePieceText sentencepiece_SentencePieceProcessor__NBestEncodeAsImmutableProto(sentencepiece::SentencePieceProcessor const *self,absl::string_view text,int nbest_size,bool add_bos,bool add_eos,bool reverse,bool emit_unk_piece){ + RewriteIds(*self, static_cast(nullptr), + add_bos, add_eos, reverse, emit_unk_piece); + auto proto = self->NBestEncodeAsImmutableProto(text, nbest_size); + proto.ConvertToUnicodeSpans(); + return proto; + } +SWIGINTERN std::vector< std::pair< std::vector< int >,float > > sentencepiece_SentencePieceProcessor__SampleEncodeAndScoreAsIds(sentencepiece::SentencePieceProcessor const *self,absl::string_view text,int num_samples,float alpha,bool wor,bool include_best,bool add_bos,bool add_eos,bool reverse,bool emit_unk_piece){ + auto idss = self->SampleEncodeAndScoreAsIds(text, num_samples, + alpha, wor, include_best); + for (auto &ids : idss) { + RewriteIds(*self, &ids.first, add_bos, add_eos, reverse, emit_unk_piece); + } + return idss; + } +SWIGINTERN std::vector< std::pair< std::vector< std::string >,float > > sentencepiece_SentencePieceProcessor__SampleEncodeAndScoreAsPieces(sentencepiece::SentencePieceProcessor const *self,absl::string_view text,int num_samples,float alpha,bool wor,bool include_best,bool add_bos,bool add_eos,bool reverse,bool emit_unk_piece){ + auto piecess = self->SampleEncodeAndScoreAsPieces(text, num_samples, + alpha, wor, include_best); + for (auto &pieces : piecess) { + RewriteIds(*self, &pieces.first, add_bos, add_eos, reverse, emit_unk_piece); + } + return piecess; + } +SWIGINTERN sentencepiece::util::bytes sentencepiece_SentencePieceProcessor__SampleEncodeAndScoreAsSerializedProto(sentencepiece::SentencePieceProcessor const *self,absl::string_view text,int num_samples,float alpha,bool wor,bool include_best,bool add_bos,bool add_eos,bool reverse,bool emit_unk_piece){ + RewriteIds(*self, static_cast(nullptr), + add_bos, add_eos, reverse, emit_unk_piece); + return self->SampleEncodeAndScoreAsSerializedProto(text, num_samples, + alpha, wor, include_best); + } +SWIGINTERN sentencepiece::ImmutableNBestSentencePieceText sentencepiece_SentencePieceProcessor__SampleEncodeAndScoreAsImmutableProto(sentencepiece::SentencePieceProcessor const *self,absl::string_view text,int num_samples,float alpha,bool wor,bool include_best,bool add_bos,bool add_eos,bool reverse,bool emit_unk_piece){ + RewriteIds(*self, static_cast(nullptr), + add_bos, add_eos, reverse, emit_unk_piece); + auto proto = self->SampleEncodeAndScoreAsImmutableProto(text, num_samples, + alpha, wor, include_best); + proto.ConvertToUnicodeSpans(); + return proto; + } +SWIGINTERN std::string sentencepiece_SentencePieceProcessor__Normalize(sentencepiece::SentencePieceProcessor *self,absl::string_view text){ + return self->Normalize(text); + } +SWIGINTERN std::pair< std::string,std::vector< size_t > > sentencepiece_SentencePieceProcessor__NormalizeWithOffsets(sentencepiece::SentencePieceProcessor *self,absl::string_view text){ + std::pair> result; + self->Normalize(text, &result.first, &result.second).IgnoreError(); + return result; + } +SWIGINTERN float sentencepiece_SentencePieceProcessor__CalculateEntropy(sentencepiece::SentencePieceProcessor *self,absl::string_view text,float alpha){ + return self->CalculateEntropy(text, alpha); + } +SWIGINTERN std::vector< float > sentencepiece_SentencePieceProcessor__CalculateEntropyBatch(sentencepiece::SentencePieceProcessor *self,std::vector< absl::string_view > const &ins,float alpha,int num_threads){ + std::vector outs(ins.size()); + InitNumThreads(ins, &num_threads); + { + ThreadPool pool(ins.size()); + std::atomic index = 0; + for (int n = 0; n < num_threads; ++n) { + pool.Schedule([&]() { + size_t i = 0; + while ((i = std::atomic_fetch_add(&index, 1)) < outs.size()) { + outs[i] = self->CalculateEntropy(ins[i], alpha); + } + }); + } + } + return outs; + } +SWIGINTERN sentencepiece::util::Status sentencepiece_SentencePieceProcessor__OverrideNormalizerSpec(sentencepiece::SentencePieceProcessor *self,std::unordered_map< std::string,std::string > const &args){ + sentencepiece::util::Status status; + for (const auto &[key, value] : args) { + status = sentencepiece::SentencePieceTrainer::SetProtoField( + key, value, + self->mutable_normalizer_spec()); + if (!status.ok()) return status; + } + return status; + } + +SWIGINTERN int +SWIG_AsVal_unsigned_SS_long (PyObject *obj, unsigned long *val) +{ +#if PY_VERSION_HEX < 0x03000000 + if (PyInt_Check(obj)) { + long v = PyInt_AsLong(obj); + if (v >= 0) { + if (val) *val = v; + return SWIG_OK; + } else { + return SWIG_OverflowError; + } + } else +#endif + if (PyLong_Check(obj)) { + unsigned long v = PyLong_AsUnsignedLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_OK; + } else { + PyErr_Clear(); + return SWIG_OverflowError; + } + } +#ifdef SWIG_PYTHON_CAST_MODE + { + int dispatch = 0; + unsigned long v = PyLong_AsUnsignedLong(obj); + if (!PyErr_Occurred()) { + if (val) *val = v; + return SWIG_AddCast(SWIG_OK); + } else { + PyErr_Clear(); + } + if (!dispatch) { + double d; + int res = SWIG_AddCast(SWIG_AsVal_double (obj,&d)); + // Largest double not larger than ULONG_MAX (not portably calculated easily) + // Note that double(ULONG_MAX) is stored in a double rounded up by one (for 64-bit unsigned long) + // 0xfffffffffffff800ULL == (uint64_t)std::nextafter(double(__uint128_t(ULONG_MAX)+1), double(0)) + const double ulong_max = sizeof(unsigned long) == 8 ? 0xfffffffffffff800ULL : ULONG_MAX; + if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, 0, ulong_max)) { + if (val) *val = (unsigned long)(d); + return res; + } + } + } +#endif + return SWIG_TypeError; +} + + +SWIGINTERN int +SWIG_AsVal_unsigned_SS_int (PyObject * obj, unsigned int *val) +{ + unsigned long v; + int res = SWIG_AsVal_unsigned_SS_long (obj, &v); + if (SWIG_IsOK(res)) { + if ((v > UINT_MAX)) { + return SWIG_OverflowError; + } else { + if (val) *val = static_cast< unsigned int >(v); + } + } + return res; +} + +SWIGINTERN void sentencepiece_SentencePieceTrainer__TrainFromString(absl::string_view arg){ + const auto _status = sentencepiece::SentencePieceTrainer::Train(arg); + if (!_status.ok()) throw _status; + return; + } +SWIGINTERN void sentencepiece_SentencePieceTrainer__TrainFromMap(std::unordered_map< std::string,std::string > const &args){ + const auto _status = sentencepiece::SentencePieceTrainer::Train(args); + if (!_status.ok()) throw _status; + return; + } +SWIGINTERN void sentencepiece_SentencePieceTrainer__TrainFromMap2(std::unordered_map< std::string,std::string > const &args,sentencepiece::SentenceIterator *iter){ + const auto _status = sentencepiece::SentencePieceTrainer::Train(args, iter); + if (!_status.ok()) throw _status; + return; + } +SWIGINTERN sentencepiece::util::bytes sentencepiece_SentencePieceTrainer__TrainFromMap3(std::unordered_map< std::string,std::string > const &args){ + sentencepiece::util::bytes model_proto; + const auto _status = sentencepiece::SentencePieceTrainer::Train(args, nullptr, &model_proto); + if (!_status.ok()) throw _status; + return model_proto; + } +SWIGINTERN sentencepiece::util::bytes sentencepiece_SentencePieceTrainer__TrainFromMap4(std::unordered_map< std::string,std::string > const &args,sentencepiece::SentenceIterator *iter){ + sentencepiece::util::bytes model_proto; + const auto _status = sentencepiece::SentencePieceTrainer::Train(args, iter, &model_proto); + if (!_status.ok()) throw _status; + return model_proto; + } +SWIGINTERN sentencepiece::util::Status sentencepiece_SentencePieceNormalizer_LoadFromFile(sentencepiece::SentencePieceNormalizer *self,absl::string_view arg){ + return self->Load(arg); + } +SWIGINTERN std::string sentencepiece_SentencePieceNormalizer__Normalize(sentencepiece::SentencePieceNormalizer *self,absl::string_view text){ + std::string result; + const auto _status = self->Normalize(text, &result); + if (!_status.ok()) throw _status; + return result; + } +SWIGINTERN std::pair< std::string,std::vector< size_t > > sentencepiece_SentencePieceNormalizer__NormalizeWithOffsets(sentencepiece::SentencePieceNormalizer *self,absl::string_view text){ + std::pair> result; + const auto _status = self->Normalize(text, &result.first, &result.second); + if (!_status.ok()) throw _status; + return result; + } +SWIGINTERN void sentencepiece_SentencePieceNormalizer__SetProtoField(sentencepiece::SentencePieceNormalizer *self,absl::string_view name,bool value){ + sentencepiece::SentencePieceTrainer::SetProtoField( + name, + value ? "1" : "0", + self->mutable_normalizer_spec()).IgnoreError(); + } +#ifdef __cplusplus +extern "C" { +#endif +SWIGINTERN PyObject *_wrap_new_ImmutableSentencePieceText_ImmutableSentencePiece(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece *result = 0 ; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "new_ImmutableSentencePieceText_ImmutableSentencePiece", 0, 0, 0)) SWIG_fail; + { + try { + result = (sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece *)new sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText_ImmutableSentencePiece, SWIG_POINTER_NEW | 0 ); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_delete_ImmutableSentencePieceText_ImmutableSentencePiece(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece *arg1 = (sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText_ImmutableSentencePiece, SWIG_POINTER_DISOWN | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "delete_ImmutableSentencePieceText_ImmutableSentencePiece" "', argument " "1"" of type '" "sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece *""'"); + } + arg1 = reinterpret_cast< sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece * >(argp1); + { + try { + delete arg1; + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_Py_Void(); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_ImmutableSentencePieceText_ImmutableSentencePiece__piece(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece *arg1 = (sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + std::string *result = 0 ; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText_ImmutableSentencePiece, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ImmutableSentencePieceText_ImmutableSentencePiece__piece" "', argument " "1"" of type '" "sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece * >(argp1); + { + try { + result = (std::string *) &((sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece const *)arg1)->piece(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + PyObject *input_type = resultobj; + resultobj = MakePyOutputString(*result, input_type); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_ImmutableSentencePieceText_ImmutableSentencePiece__surface(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece *arg1 = (sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + std::string *result = 0 ; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText_ImmutableSentencePiece, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ImmutableSentencePieceText_ImmutableSentencePiece__surface" "', argument " "1"" of type '" "sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece * >(argp1); + { + try { + result = (std::string *) &((sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece const *)arg1)->surface(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + PyObject *input_type = resultobj; + resultobj = MakePyOutputString(*result, input_type); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_ImmutableSentencePieceText_ImmutableSentencePiece__id(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece *arg1 = (sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + uint32_t result; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText_ImmutableSentencePiece, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ImmutableSentencePieceText_ImmutableSentencePiece__id" "', argument " "1"" of type '" "sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece * >(argp1); + { + try { + result = ((sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece const *)arg1)->id(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_From_unsigned_SS_int(static_cast< unsigned int >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_ImmutableSentencePieceText_ImmutableSentencePiece__begin(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece *arg1 = (sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + uint32_t result; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText_ImmutableSentencePiece, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ImmutableSentencePieceText_ImmutableSentencePiece__begin" "', argument " "1"" of type '" "sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece * >(argp1); + { + try { + result = ((sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece const *)arg1)->begin(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_From_unsigned_SS_int(static_cast< unsigned int >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_ImmutableSentencePieceText_ImmutableSentencePiece__end(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece *arg1 = (sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + uint32_t result; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText_ImmutableSentencePiece, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ImmutableSentencePieceText_ImmutableSentencePiece__end" "', argument " "1"" of type '" "sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece * >(argp1); + { + try { + result = ((sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece const *)arg1)->end(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_From_unsigned_SS_int(static_cast< unsigned int >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_ImmutableSentencePieceText_ImmutableSentencePiece__surface_as_bytes(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece *arg1 = (sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + sentencepiece::util::bytes *result = 0 ; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText_ImmutableSentencePiece, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ImmutableSentencePieceText_ImmutableSentencePiece__surface_as_bytes" "', argument " "1"" of type '" "sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece * >(argp1); + { + try { + result = (sentencepiece::util::bytes *) &sentencepiece_ImmutableSentencePieceText_ImmutableSentencePiece__surface_as_bytes((sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece const *)arg1); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = MakePyOutputBytes(*result); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_ImmutableSentencePieceText_ImmutableSentencePiece__piece_as_bytes(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece *arg1 = (sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + sentencepiece::util::bytes *result = 0 ; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText_ImmutableSentencePiece, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ImmutableSentencePieceText_ImmutableSentencePiece__piece_as_bytes" "', argument " "1"" of type '" "sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece * >(argp1); + { + try { + result = (sentencepiece::util::bytes *) &sentencepiece_ImmutableSentencePieceText_ImmutableSentencePiece__piece_as_bytes((sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece const *)arg1); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = MakePyOutputBytes(*result); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *ImmutableSentencePieceText_ImmutableSentencePiece_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *obj = NULL; + if (!SWIG_Python_UnpackTuple(args, "swigregister", 1, 1, &obj)) return NULL; + SWIG_TypeNewClientData(SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText_ImmutableSentencePiece, SWIG_NewClientData(obj)); + return SWIG_Py_Void(); +} + +SWIGINTERN PyObject *ImmutableSentencePieceText_ImmutableSentencePiece_swiginit(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + return SWIG_Python_InitShadowInstance(args); +} + +SWIGINTERN PyObject *_wrap_new_ImmutableSentencePieceText(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::ImmutableSentencePieceText *result = 0 ; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "new_ImmutableSentencePieceText", 0, 0, 0)) SWIG_fail; + { + try { + result = (sentencepiece::ImmutableSentencePieceText *)new sentencepiece::ImmutableSentencePieceText(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText, SWIG_POINTER_NEW | 0 ); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_delete_ImmutableSentencePieceText(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::ImmutableSentencePieceText *arg1 = (sentencepiece::ImmutableSentencePieceText *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText, SWIG_POINTER_DISOWN | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "delete_ImmutableSentencePieceText" "', argument " "1"" of type '" "sentencepiece::ImmutableSentencePieceText *""'"); + } + arg1 = reinterpret_cast< sentencepiece::ImmutableSentencePieceText * >(argp1); + { + try { + delete arg1; + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_Py_Void(); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_ImmutableSentencePieceText__pieces_size(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::ImmutableSentencePieceText *arg1 = (sentencepiece::ImmutableSentencePieceText *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + size_t result; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ImmutableSentencePieceText__pieces_size" "', argument " "1"" of type '" "sentencepiece::ImmutableSentencePieceText const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::ImmutableSentencePieceText * >(argp1); + { + try { + result = ((sentencepiece::ImmutableSentencePieceText const *)arg1)->pieces_size(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_From_size_t(static_cast< size_t >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_ImmutableSentencePieceText__pieces(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::ImmutableSentencePieceText *arg1 = (sentencepiece::ImmutableSentencePieceText *) 0 ; + int arg2 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val2 ; + int ecode2 = 0 ; + PyObject *swig_obj[2] ; + sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "ImmutableSentencePieceText__pieces", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ImmutableSentencePieceText__pieces" "', argument " "1"" of type '" "sentencepiece::ImmutableSentencePieceText const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::ImmutableSentencePieceText * >(argp1); + ecode2 = SWIG_AsVal_int(swig_obj[1], &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "ImmutableSentencePieceText__pieces" "', argument " "2"" of type '" "int""'"); + } + arg2 = static_cast< int >(val2); + { + try { + result = ((sentencepiece::ImmutableSentencePieceText const *)arg1)->pieces(arg2); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_NewPointerObj((new sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece(result)), SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText_ImmutableSentencePiece, SWIG_POINTER_OWN | 0 ); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_ImmutableSentencePieceText__text(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::ImmutableSentencePieceText *arg1 = (sentencepiece::ImmutableSentencePieceText *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + std::string *result = 0 ; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ImmutableSentencePieceText__text" "', argument " "1"" of type '" "sentencepiece::ImmutableSentencePieceText const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::ImmutableSentencePieceText * >(argp1); + { + try { + result = (std::string *) &((sentencepiece::ImmutableSentencePieceText const *)arg1)->text(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + PyObject *input_type = resultobj; + resultobj = MakePyOutputString(*result, input_type); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_ImmutableSentencePieceText__score(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::ImmutableSentencePieceText *arg1 = (sentencepiece::ImmutableSentencePieceText *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + float result; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ImmutableSentencePieceText__score" "', argument " "1"" of type '" "sentencepiece::ImmutableSentencePieceText const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::ImmutableSentencePieceText * >(argp1); + { + try { + result = (float)((sentencepiece::ImmutableSentencePieceText const *)arg1)->score(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_From_float(static_cast< float >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_ImmutableSentencePieceText_SerializeAsString(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::ImmutableSentencePieceText *arg1 = (sentencepiece::ImmutableSentencePieceText *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + sentencepiece::util::bytes result; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ImmutableSentencePieceText_SerializeAsString" "', argument " "1"" of type '" "sentencepiece::ImmutableSentencePieceText const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::ImmutableSentencePieceText * >(argp1); + { + try { + result = ((sentencepiece::ImmutableSentencePieceText const *)arg1)->SerializeAsString(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = MakePyOutputBytes(result); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_ImmutableSentencePieceText__text_as_bytes(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::ImmutableSentencePieceText *arg1 = (sentencepiece::ImmutableSentencePieceText *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + sentencepiece::util::bytes *result = 0 ; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ImmutableSentencePieceText__text_as_bytes" "', argument " "1"" of type '" "sentencepiece::ImmutableSentencePieceText const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::ImmutableSentencePieceText * >(argp1); + { + try { + result = (sentencepiece::util::bytes *) &sentencepiece_ImmutableSentencePieceText__text_as_bytes((sentencepiece::ImmutableSentencePieceText const *)arg1); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = MakePyOutputBytes(*result); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *ImmutableSentencePieceText_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *obj = NULL; + if (!SWIG_Python_UnpackTuple(args, "swigregister", 1, 1, &obj)) return NULL; + SWIG_TypeNewClientData(SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText, SWIG_NewClientData(obj)); + return SWIG_Py_Void(); +} + +SWIGINTERN PyObject *ImmutableSentencePieceText_swiginit(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + return SWIG_Python_InitShadowInstance(args); +} + +SWIGINTERN PyObject *_wrap_new_ImmutableNBestSentencePieceText(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::ImmutableNBestSentencePieceText *result = 0 ; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "new_ImmutableNBestSentencePieceText", 0, 0, 0)) SWIG_fail; + { + try { + result = (sentencepiece::ImmutableNBestSentencePieceText *)new sentencepiece::ImmutableNBestSentencePieceText(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_sentencepiece__ImmutableNBestSentencePieceText, SWIG_POINTER_NEW | 0 ); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_delete_ImmutableNBestSentencePieceText(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::ImmutableNBestSentencePieceText *arg1 = (sentencepiece::ImmutableNBestSentencePieceText *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__ImmutableNBestSentencePieceText, SWIG_POINTER_DISOWN | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "delete_ImmutableNBestSentencePieceText" "', argument " "1"" of type '" "sentencepiece::ImmutableNBestSentencePieceText *""'"); + } + arg1 = reinterpret_cast< sentencepiece::ImmutableNBestSentencePieceText * >(argp1); + { + try { + delete arg1; + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_Py_Void(); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_ImmutableNBestSentencePieceText__nbests_size(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::ImmutableNBestSentencePieceText *arg1 = (sentencepiece::ImmutableNBestSentencePieceText *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + size_t result; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__ImmutableNBestSentencePieceText, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ImmutableNBestSentencePieceText__nbests_size" "', argument " "1"" of type '" "sentencepiece::ImmutableNBestSentencePieceText const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::ImmutableNBestSentencePieceText * >(argp1); + { + try { + result = ((sentencepiece::ImmutableNBestSentencePieceText const *)arg1)->nbests_size(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_From_size_t(static_cast< size_t >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_ImmutableNBestSentencePieceText__nbests(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::ImmutableNBestSentencePieceText *arg1 = (sentencepiece::ImmutableNBestSentencePieceText *) 0 ; + int arg2 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val2 ; + int ecode2 = 0 ; + PyObject *swig_obj[2] ; + sentencepiece::ImmutableSentencePieceText result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "ImmutableNBestSentencePieceText__nbests", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__ImmutableNBestSentencePieceText, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ImmutableNBestSentencePieceText__nbests" "', argument " "1"" of type '" "sentencepiece::ImmutableNBestSentencePieceText const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::ImmutableNBestSentencePieceText * >(argp1); + ecode2 = SWIG_AsVal_int(swig_obj[1], &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "ImmutableNBestSentencePieceText__nbests" "', argument " "2"" of type '" "int""'"); + } + arg2 = static_cast< int >(val2); + { + try { + result = ((sentencepiece::ImmutableNBestSentencePieceText const *)arg1)->nbests(arg2); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_NewPointerObj((new sentencepiece::ImmutableSentencePieceText(result)), SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText, SWIG_POINTER_OWN | 0 ); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_ImmutableNBestSentencePieceText_SerializeAsString(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::ImmutableNBestSentencePieceText *arg1 = (sentencepiece::ImmutableNBestSentencePieceText *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + sentencepiece::util::bytes result; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__ImmutableNBestSentencePieceText, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ImmutableNBestSentencePieceText_SerializeAsString" "', argument " "1"" of type '" "sentencepiece::ImmutableNBestSentencePieceText const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::ImmutableNBestSentencePieceText * >(argp1); + { + try { + result = ((sentencepiece::ImmutableNBestSentencePieceText const *)arg1)->SerializeAsString(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = MakePyOutputBytes(result); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *ImmutableNBestSentencePieceText_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *obj = NULL; + if (!SWIG_Python_UnpackTuple(args, "swigregister", 1, 1, &obj)) return NULL; + SWIG_TypeNewClientData(SWIGTYPE_p_sentencepiece__ImmutableNBestSentencePieceText, SWIG_NewClientData(obj)); + return SWIG_Py_Void(); +} + +SWIGINTERN PyObject *ImmutableNBestSentencePieceText_swiginit(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + return SWIG_Python_InitShadowInstance(args); +} + +SWIGINTERN PyObject *_wrap_new_SentencePieceProcessor(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *result = 0 ; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "new_SentencePieceProcessor", 0, 0, 0)) SWIG_fail; + { + try { + result = (sentencepiece::SentencePieceProcessor *)new sentencepiece::SentencePieceProcessor(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_sentencepiece__SentencePieceProcessor, SWIG_POINTER_NEW | 0 ); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_delete_SentencePieceProcessor(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, SWIG_POINTER_DISOWN | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "delete_SentencePieceProcessor" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + try { + delete arg1; + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_Py_Void(); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_LoadFromSerializedProto(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + sentencepiece::util::Status result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor_LoadFromSerializedProto", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor_LoadFromSerializedProto" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + { + try { + result = (arg1)->LoadFromSerializedProto(SWIG_STD_MOVE(arg2)); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + if (!(&result)->ok()) { + SWIG_exception(ToSwigError((&result)->code()), (&result)->ToString().c_str()); + } + resultobj = SWIG_From_bool((&result)->ok()); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_SetEncodeExtraOptions(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + sentencepiece::util::Status result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor_SetEncodeExtraOptions", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor_SetEncodeExtraOptions" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + { + try { + result = (arg1)->SetEncodeExtraOptions(SWIG_STD_MOVE(arg2)); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + if (!(&result)->ok()) { + SWIG_exception(ToSwigError((&result)->code()), (&result)->ToString().c_str()); + } + resultobj = SWIG_From_bool((&result)->ok()); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_SetDecodeExtraOptions(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + sentencepiece::util::Status result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor_SetDecodeExtraOptions", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor_SetDecodeExtraOptions" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + { + try { + result = (arg1)->SetDecodeExtraOptions(SWIG_STD_MOVE(arg2)); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + if (!(&result)->ok()) { + SWIG_exception(ToSwigError((&result)->code()), (&result)->ToString().c_str()); + } + resultobj = SWIG_From_bool((&result)->ok()); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_SetVocabulary(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + std::vector< absl::string_view > *arg2 = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + sentencepiece::util::Status result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor_SetVocabulary", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor_SetVocabulary" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + std::vector *out = nullptr; + if (PyList_Check(swig_obj[1])) { + const size_t size = PyList_Size(swig_obj[1]); + out = new std::vector(size); + for (size_t i = 0; i < size; ++i) { + const PyInputString ustring(PyList_GetItem(swig_obj[1], i)); + if (ustring.IsAvalable()) { + (*out)[i] = ustring.str(); + } else { + PyErr_SetString(PyExc_TypeError, "list must contain strings"); + SWIG_fail; + } + resultobj = ustring.input_type(); + } + } else { + PyErr_SetString(PyExc_TypeError, "not a list"); + SWIG_fail; + } + arg2 = out; + } + { + try { + result = (arg1)->SetVocabulary((std::vector< absl::string_view > const &)*arg2); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + if (!(&result)->ok()) { + SWIG_exception(ToSwigError((&result)->code()), (&result)->ToString().c_str()); + } + resultobj = SWIG_From_bool((&result)->ok()); + } + { + delete arg2; + } + return resultobj; +fail: + { + delete arg2; + } + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_ResetVocabulary(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + sentencepiece::util::Status result; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor_ResetVocabulary" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + try { + result = (arg1)->ResetVocabulary(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + if (!(&result)->ok()) { + SWIG_exception(ToSwigError((&result)->code()), (&result)->ToString().c_str()); + } + resultobj = SWIG_From_bool((&result)->ok()); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_LoadVocabulary(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + int arg3 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val3 ; + int ecode3 = 0 ; + PyObject *swig_obj[3] ; + sentencepiece::util::Status result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor_LoadVocabulary", 3, 3, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor_LoadVocabulary" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor_LoadVocabulary" "', argument " "3"" of type '" "int""'"); + } + arg3 = static_cast< int >(val3); + { + try { + result = (arg1)->LoadVocabulary(SWIG_STD_MOVE(arg2),arg3); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + if (!(&result)->ok()) { + SWIG_exception(ToSwigError((&result)->code()), (&result)->ToString().c_str()); + } + resultobj = SWIG_From_bool((&result)->ok()); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_CalculateEntropy__SWIG_0(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + float arg3 ; + float *arg4 = (float *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + float val3 ; + int ecode3 = 0 ; + void *argp4 = 0 ; + int res4 = 0 ; + sentencepiece::util::Status result; + + (void)self; + if ((nobjs < 4) || (nobjs > 4)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor_CalculateEntropy" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + ecode3 = SWIG_AsVal_float(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor_CalculateEntropy" "', argument " "3"" of type '" "float""'"); + } + arg3 = static_cast< float >(val3); + res4 = SWIG_ConvertPtr(swig_obj[3], &argp4,SWIGTYPE_p_float, 0 | 0 ); + if (!SWIG_IsOK(res4)) { + SWIG_exception_fail(SWIG_ArgError(res4), "in method '" "SentencePieceProcessor_CalculateEntropy" "', argument " "4"" of type '" "float *""'"); + } + arg4 = reinterpret_cast< float * >(argp4); + { + try { + result = ((sentencepiece::SentencePieceProcessor const *)arg1)->CalculateEntropy(SWIG_STD_MOVE(arg2),arg3,arg4); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + if (!(&result)->ok()) { + SWIG_exception(ToSwigError((&result)->code()), (&result)->ToString().c_str()); + } + resultobj = SWIG_From_bool((&result)->ok()); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_CalculateEntropy__SWIG_1(PyObject *self, Py_ssize_t nobjs, PyObject **swig_obj) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + float arg3 ; + void *argp1 = 0 ; + int res1 = 0 ; + float val3 ; + int ecode3 = 0 ; + float result; + + (void)self; + if ((nobjs < 3) || (nobjs > 3)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor_CalculateEntropy" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + ecode3 = SWIG_AsVal_float(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor_CalculateEntropy" "', argument " "3"" of type '" "float""'"); + } + arg3 = static_cast< float >(val3); + { + try { + result = (float)((sentencepiece::SentencePieceProcessor const *)arg1)->CalculateEntropy(SWIG_STD_MOVE(arg2),arg3); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_From_float(static_cast< float >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_CalculateEntropy(PyObject *self, PyObject *args) { + Py_ssize_t argc; + PyObject *argv[5] = { + 0 + }; + + if (!(argc = SWIG_Python_UnpackTuple(args, "SentencePieceProcessor_CalculateEntropy", 0, 4, argv))) SWIG_fail; + --argc; + if (argc == 3) { + int _v = 0; + void *vptr = 0; + int res = SWIG_ConvertPtr(argv[0], &vptr, SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0); + _v = SWIG_CheckState(res); + if (_v) { + int res = SWIG_AsCharPtrAndSize(argv[1], 0, NULL, 0); + _v = SWIG_CheckState(res); + if (_v) { + { + int res = SWIG_AsVal_float(argv[2], NULL); + _v = SWIG_CheckState(res); + } + if (_v) { + return _wrap_SentencePieceProcessor_CalculateEntropy__SWIG_1(self, argc, argv); + } + } + } + } + if (argc == 4) { + int _v = 0; + void *vptr = 0; + int res = SWIG_ConvertPtr(argv[0], &vptr, SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0); + _v = SWIG_CheckState(res); + if (_v) { + int res = SWIG_AsCharPtrAndSize(argv[1], 0, NULL, 0); + _v = SWIG_CheckState(res); + if (_v) { + { + int res = SWIG_AsVal_float(argv[2], NULL); + _v = SWIG_CheckState(res); + } + if (_v) { + void *vptr = 0; + int res = SWIG_ConvertPtr(argv[3], &vptr, SWIGTYPE_p_float, 0); + _v = SWIG_CheckState(res); + if (_v) { + return _wrap_SentencePieceProcessor_CalculateEntropy__SWIG_0(self, argc, argv); + } + } + } + } + } + +fail: + SWIG_Python_RaiseOrModifyTypeError("Wrong number or type of arguments for overloaded function 'SentencePieceProcessor_CalculateEntropy'.\n" + " Possible C/C++ prototypes are:\n" + " sentencepiece::SentencePieceProcessor::CalculateEntropy(absl::string_view,float,float *) const\n" + " sentencepiece::SentencePieceProcessor::CalculateEntropy(absl::string_view,float) const\n"); + return 0; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_GetPieceSize(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + int result; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor_GetPieceSize" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + try { + result = (int)((sentencepiece::SentencePieceProcessor const *)arg1)->GetPieceSize(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_From_int(static_cast< int >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_PieceToId(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + int result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor_PieceToId", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor_PieceToId" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + { + try { + result = (int)((sentencepiece::SentencePieceProcessor const *)arg1)->PieceToId(SWIG_STD_MOVE(arg2)); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_From_int(static_cast< int >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_IdToPiece(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + int arg2 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val2 ; + int ecode2 = 0 ; + PyObject *swig_obj[2] ; + std::string *result = 0 ; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor_IdToPiece", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor_IdToPiece" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + ecode2 = SWIG_AsVal_int(swig_obj[1], &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "SentencePieceProcessor_IdToPiece" "', argument " "2"" of type '" "int""'"); + } + arg2 = static_cast< int >(val2); + { + try { + result = (std::string *) &((sentencepiece::SentencePieceProcessor const *)arg1)->IdToPiece(arg2); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + PyObject *input_type = resultobj; + resultobj = MakePyOutputString(*result, input_type); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_GetScore(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + int arg2 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val2 ; + int ecode2 = 0 ; + PyObject *swig_obj[2] ; + float result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor_GetScore", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor_GetScore" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + ecode2 = SWIG_AsVal_int(swig_obj[1], &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "SentencePieceProcessor_GetScore" "', argument " "2"" of type '" "int""'"); + } + arg2 = static_cast< int >(val2); + { + try { + result = (float)((sentencepiece::SentencePieceProcessor const *)arg1)->GetScore(arg2); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_From_float(static_cast< float >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_IsUnknown(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + int arg2 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val2 ; + int ecode2 = 0 ; + PyObject *swig_obj[2] ; + bool result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor_IsUnknown", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor_IsUnknown" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + ecode2 = SWIG_AsVal_int(swig_obj[1], &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "SentencePieceProcessor_IsUnknown" "', argument " "2"" of type '" "int""'"); + } + arg2 = static_cast< int >(val2); + { + try { + result = (bool)((sentencepiece::SentencePieceProcessor const *)arg1)->IsUnknown(arg2); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_From_bool(static_cast< bool >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_IsControl(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + int arg2 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val2 ; + int ecode2 = 0 ; + PyObject *swig_obj[2] ; + bool result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor_IsControl", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor_IsControl" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + ecode2 = SWIG_AsVal_int(swig_obj[1], &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "SentencePieceProcessor_IsControl" "', argument " "2"" of type '" "int""'"); + } + arg2 = static_cast< int >(val2); + { + try { + result = (bool)((sentencepiece::SentencePieceProcessor const *)arg1)->IsControl(arg2); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_From_bool(static_cast< bool >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_IsUnused(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + int arg2 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val2 ; + int ecode2 = 0 ; + PyObject *swig_obj[2] ; + bool result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor_IsUnused", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor_IsUnused" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + ecode2 = SWIG_AsVal_int(swig_obj[1], &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "SentencePieceProcessor_IsUnused" "', argument " "2"" of type '" "int""'"); + } + arg2 = static_cast< int >(val2); + { + try { + result = (bool)((sentencepiece::SentencePieceProcessor const *)arg1)->IsUnused(arg2); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_From_bool(static_cast< bool >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_IsByte(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + int arg2 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val2 ; + int ecode2 = 0 ; + PyObject *swig_obj[2] ; + bool result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor_IsByte", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor_IsByte" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + ecode2 = SWIG_AsVal_int(swig_obj[1], &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "SentencePieceProcessor_IsByte" "', argument " "2"" of type '" "int""'"); + } + arg2 = static_cast< int >(val2); + { + try { + result = (bool)((sentencepiece::SentencePieceProcessor const *)arg1)->IsByte(arg2); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_From_bool(static_cast< bool >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_unk_id(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + int result; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor_unk_id" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + try { + result = (int)((sentencepiece::SentencePieceProcessor const *)arg1)->unk_id(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_From_int(static_cast< int >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_bos_id(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + int result; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor_bos_id" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + try { + result = (int)((sentencepiece::SentencePieceProcessor const *)arg1)->bos_id(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_From_int(static_cast< int >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_eos_id(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + int result; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor_eos_id" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + try { + result = (int)((sentencepiece::SentencePieceProcessor const *)arg1)->eos_id(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_From_int(static_cast< int >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_pad_id(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + int result; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor_pad_id" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + try { + result = (int)((sentencepiece::SentencePieceProcessor const *)arg1)->pad_id(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_From_int(static_cast< int >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_serialized_model_proto(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + sentencepiece::util::bytes result; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor_serialized_model_proto" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + try { + result = ((sentencepiece::SentencePieceProcessor const *)arg1)->serialized_model_proto(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = MakePyOutputBytes(result); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor_LoadFromFile(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + sentencepiece::util::Status result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor_LoadFromFile", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor_LoadFromFile" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + { + try { + result = sentencepiece_SentencePieceProcessor_LoadFromFile(arg1,SWIG_STD_MOVE(arg2)); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + if (!(&result)->ok()) { + SWIG_exception(ToSwigError((&result)->code()), (&result)->ToString().c_str()); + } + resultobj = SWIG_From_bool((&result)->ok()); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__EncodeAsIds(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + bool arg3 ; + int arg4 ; + float arg5 ; + bool arg6 ; + bool arg7 ; + bool arg8 ; + bool arg9 ; + void *argp1 = 0 ; + int res1 = 0 ; + bool val3 ; + int ecode3 = 0 ; + int val4 ; + int ecode4 = 0 ; + float val5 ; + int ecode5 = 0 ; + bool val6 ; + int ecode6 = 0 ; + bool val7 ; + int ecode7 = 0 ; + bool val8 ; + int ecode8 = 0 ; + bool val9 ; + int ecode9 = 0 ; + PyObject *swig_obj[9] ; + std::vector< int > result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__EncodeAsIds", 9, 9, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__EncodeAsIds" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + ecode3 = SWIG_AsVal_bool(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__EncodeAsIds" "', argument " "3"" of type '" "bool""'"); + } + arg3 = static_cast< bool >(val3); + ecode4 = SWIG_AsVal_int(swig_obj[3], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "SentencePieceProcessor__EncodeAsIds" "', argument " "4"" of type '" "int""'"); + } + arg4 = static_cast< int >(val4); + ecode5 = SWIG_AsVal_float(swig_obj[4], &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "SentencePieceProcessor__EncodeAsIds" "', argument " "5"" of type '" "float""'"); + } + arg5 = static_cast< float >(val5); + ecode6 = SWIG_AsVal_bool(swig_obj[5], &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "SentencePieceProcessor__EncodeAsIds" "', argument " "6"" of type '" "bool""'"); + } + arg6 = static_cast< bool >(val6); + ecode7 = SWIG_AsVal_bool(swig_obj[6], &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "SentencePieceProcessor__EncodeAsIds" "', argument " "7"" of type '" "bool""'"); + } + arg7 = static_cast< bool >(val7); + ecode8 = SWIG_AsVal_bool(swig_obj[7], &val8); + if (!SWIG_IsOK(ecode8)) { + SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "SentencePieceProcessor__EncodeAsIds" "', argument " "8"" of type '" "bool""'"); + } + arg8 = static_cast< bool >(val8); + ecode9 = SWIG_AsVal_bool(swig_obj[8], &val9); + if (!SWIG_IsOK(ecode9)) { + SWIG_exception_fail(SWIG_ArgError(ecode9), "in method '" "SentencePieceProcessor__EncodeAsIds" "', argument " "9"" of type '" "bool""'"); + } + arg9 = static_cast< bool >(val9); + { + try { + result = sentencepiece_SentencePieceProcessor__EncodeAsIds((sentencepiece::SentencePieceProcessor const *)arg1,SWIG_STD_MOVE(arg2),arg3,arg4,arg5,arg6,arg7,arg8,arg9); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = PyList_New((&result)->size()); + for (size_t i = 0; i < (&result)->size(); ++i) { + PyList_SET_ITEM(resultobj, i, PyInt_FromLong(static_cast(result[i]))); + } + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__EncodeAsPieces(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + bool arg3 ; + int arg4 ; + float arg5 ; + bool arg6 ; + bool arg7 ; + bool arg8 ; + bool arg9 ; + void *argp1 = 0 ; + int res1 = 0 ; + bool val3 ; + int ecode3 = 0 ; + int val4 ; + int ecode4 = 0 ; + float val5 ; + int ecode5 = 0 ; + bool val6 ; + int ecode6 = 0 ; + bool val7 ; + int ecode7 = 0 ; + bool val8 ; + int ecode8 = 0 ; + bool val9 ; + int ecode9 = 0 ; + PyObject *swig_obj[9] ; + std::vector< std::string > result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__EncodeAsPieces", 9, 9, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__EncodeAsPieces" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + ecode3 = SWIG_AsVal_bool(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__EncodeAsPieces" "', argument " "3"" of type '" "bool""'"); + } + arg3 = static_cast< bool >(val3); + ecode4 = SWIG_AsVal_int(swig_obj[3], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "SentencePieceProcessor__EncodeAsPieces" "', argument " "4"" of type '" "int""'"); + } + arg4 = static_cast< int >(val4); + ecode5 = SWIG_AsVal_float(swig_obj[4], &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "SentencePieceProcessor__EncodeAsPieces" "', argument " "5"" of type '" "float""'"); + } + arg5 = static_cast< float >(val5); + ecode6 = SWIG_AsVal_bool(swig_obj[5], &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "SentencePieceProcessor__EncodeAsPieces" "', argument " "6"" of type '" "bool""'"); + } + arg6 = static_cast< bool >(val6); + ecode7 = SWIG_AsVal_bool(swig_obj[6], &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "SentencePieceProcessor__EncodeAsPieces" "', argument " "7"" of type '" "bool""'"); + } + arg7 = static_cast< bool >(val7); + ecode8 = SWIG_AsVal_bool(swig_obj[7], &val8); + if (!SWIG_IsOK(ecode8)) { + SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "SentencePieceProcessor__EncodeAsPieces" "', argument " "8"" of type '" "bool""'"); + } + arg8 = static_cast< bool >(val8); + ecode9 = SWIG_AsVal_bool(swig_obj[8], &val9); + if (!SWIG_IsOK(ecode9)) { + SWIG_exception_fail(SWIG_ArgError(ecode9), "in method '" "SentencePieceProcessor__EncodeAsPieces" "', argument " "9"" of type '" "bool""'"); + } + arg9 = static_cast< bool >(val9); + { + try { + result = sentencepiece_SentencePieceProcessor__EncodeAsPieces((sentencepiece::SentencePieceProcessor const *)arg1,SWIG_STD_MOVE(arg2),arg3,arg4,arg5,arg6,arg7,arg8,arg9); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + PyObject *input_type = resultobj; + resultobj = PyList_New((&result)->size()); + for (size_t i = 0; i < (&result)->size(); ++i) { + PyList_SET_ITEM(resultobj, i, MakePyOutputString(result[i], input_type)); + } + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__EncodeAsSerializedProto(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + bool arg3 ; + int arg4 ; + float arg5 ; + bool arg6 ; + bool arg7 ; + bool arg8 ; + bool arg9 ; + void *argp1 = 0 ; + int res1 = 0 ; + bool val3 ; + int ecode3 = 0 ; + int val4 ; + int ecode4 = 0 ; + float val5 ; + int ecode5 = 0 ; + bool val6 ; + int ecode6 = 0 ; + bool val7 ; + int ecode7 = 0 ; + bool val8 ; + int ecode8 = 0 ; + bool val9 ; + int ecode9 = 0 ; + PyObject *swig_obj[9] ; + sentencepiece::util::bytes result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__EncodeAsSerializedProto", 9, 9, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__EncodeAsSerializedProto" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + ecode3 = SWIG_AsVal_bool(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__EncodeAsSerializedProto" "', argument " "3"" of type '" "bool""'"); + } + arg3 = static_cast< bool >(val3); + ecode4 = SWIG_AsVal_int(swig_obj[3], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "SentencePieceProcessor__EncodeAsSerializedProto" "', argument " "4"" of type '" "int""'"); + } + arg4 = static_cast< int >(val4); + ecode5 = SWIG_AsVal_float(swig_obj[4], &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "SentencePieceProcessor__EncodeAsSerializedProto" "', argument " "5"" of type '" "float""'"); + } + arg5 = static_cast< float >(val5); + ecode6 = SWIG_AsVal_bool(swig_obj[5], &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "SentencePieceProcessor__EncodeAsSerializedProto" "', argument " "6"" of type '" "bool""'"); + } + arg6 = static_cast< bool >(val6); + ecode7 = SWIG_AsVal_bool(swig_obj[6], &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "SentencePieceProcessor__EncodeAsSerializedProto" "', argument " "7"" of type '" "bool""'"); + } + arg7 = static_cast< bool >(val7); + ecode8 = SWIG_AsVal_bool(swig_obj[7], &val8); + if (!SWIG_IsOK(ecode8)) { + SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "SentencePieceProcessor__EncodeAsSerializedProto" "', argument " "8"" of type '" "bool""'"); + } + arg8 = static_cast< bool >(val8); + ecode9 = SWIG_AsVal_bool(swig_obj[8], &val9); + if (!SWIG_IsOK(ecode9)) { + SWIG_exception_fail(SWIG_ArgError(ecode9), "in method '" "SentencePieceProcessor__EncodeAsSerializedProto" "', argument " "9"" of type '" "bool""'"); + } + arg9 = static_cast< bool >(val9); + { + try { + result = sentencepiece_SentencePieceProcessor__EncodeAsSerializedProto((sentencepiece::SentencePieceProcessor const *)arg1,SWIG_STD_MOVE(arg2),arg3,arg4,arg5,arg6,arg7,arg8,arg9); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = MakePyOutputBytes(result); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__EncodeAsImmutableProto(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + bool arg3 ; + int arg4 ; + float arg5 ; + bool arg6 ; + bool arg7 ; + bool arg8 ; + bool arg9 ; + void *argp1 = 0 ; + int res1 = 0 ; + bool val3 ; + int ecode3 = 0 ; + int val4 ; + int ecode4 = 0 ; + float val5 ; + int ecode5 = 0 ; + bool val6 ; + int ecode6 = 0 ; + bool val7 ; + int ecode7 = 0 ; + bool val8 ; + int ecode8 = 0 ; + bool val9 ; + int ecode9 = 0 ; + PyObject *swig_obj[9] ; + sentencepiece::ImmutableSentencePieceText result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__EncodeAsImmutableProto", 9, 9, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__EncodeAsImmutableProto" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + ecode3 = SWIG_AsVal_bool(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__EncodeAsImmutableProto" "', argument " "3"" of type '" "bool""'"); + } + arg3 = static_cast< bool >(val3); + ecode4 = SWIG_AsVal_int(swig_obj[3], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "SentencePieceProcessor__EncodeAsImmutableProto" "', argument " "4"" of type '" "int""'"); + } + arg4 = static_cast< int >(val4); + ecode5 = SWIG_AsVal_float(swig_obj[4], &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "SentencePieceProcessor__EncodeAsImmutableProto" "', argument " "5"" of type '" "float""'"); + } + arg5 = static_cast< float >(val5); + ecode6 = SWIG_AsVal_bool(swig_obj[5], &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "SentencePieceProcessor__EncodeAsImmutableProto" "', argument " "6"" of type '" "bool""'"); + } + arg6 = static_cast< bool >(val6); + ecode7 = SWIG_AsVal_bool(swig_obj[6], &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "SentencePieceProcessor__EncodeAsImmutableProto" "', argument " "7"" of type '" "bool""'"); + } + arg7 = static_cast< bool >(val7); + ecode8 = SWIG_AsVal_bool(swig_obj[7], &val8); + if (!SWIG_IsOK(ecode8)) { + SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "SentencePieceProcessor__EncodeAsImmutableProto" "', argument " "8"" of type '" "bool""'"); + } + arg8 = static_cast< bool >(val8); + ecode9 = SWIG_AsVal_bool(swig_obj[8], &val9); + if (!SWIG_IsOK(ecode9)) { + SWIG_exception_fail(SWIG_ArgError(ecode9), "in method '" "SentencePieceProcessor__EncodeAsImmutableProto" "', argument " "9"" of type '" "bool""'"); + } + arg9 = static_cast< bool >(val9); + { + try { + result = sentencepiece_SentencePieceProcessor__EncodeAsImmutableProto((sentencepiece::SentencePieceProcessor const *)arg1,SWIG_STD_MOVE(arg2),arg3,arg4,arg5,arg6,arg7,arg8,arg9); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_NewPointerObj((new sentencepiece::ImmutableSentencePieceText(result)), SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText, SWIG_POINTER_OWN | 0 ); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__EncodeAsIdsBatch(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + std::vector< absl::string_view > *arg2 = 0 ; + int arg3 ; + bool arg4 ; + int arg5 ; + float arg6 ; + bool arg7 ; + bool arg8 ; + bool arg9 ; + bool arg10 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val3 ; + int ecode3 = 0 ; + bool val4 ; + int ecode4 = 0 ; + int val5 ; + int ecode5 = 0 ; + float val6 ; + int ecode6 = 0 ; + bool val7 ; + int ecode7 = 0 ; + bool val8 ; + int ecode8 = 0 ; + bool val9 ; + int ecode9 = 0 ; + bool val10 ; + int ecode10 = 0 ; + PyObject *swig_obj[10] ; + std::vector< std::vector< int > > result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__EncodeAsIdsBatch", 10, 10, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__EncodeAsIdsBatch" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + std::vector *out = nullptr; + if (PyList_Check(swig_obj[1])) { + const size_t size = PyList_Size(swig_obj[1]); + out = new std::vector(size); + for (size_t i = 0; i < size; ++i) { + const PyInputString ustring(PyList_GetItem(swig_obj[1], i)); + if (ustring.IsAvalable()) { + (*out)[i] = ustring.str(); + } else { + PyErr_SetString(PyExc_TypeError, "list must contain strings"); + SWIG_fail; + } + resultobj = ustring.input_type(); + } + } else { + PyErr_SetString(PyExc_TypeError, "not a list"); + SWIG_fail; + } + arg2 = out; + } + ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__EncodeAsIdsBatch" "', argument " "3"" of type '" "int""'"); + } + arg3 = static_cast< int >(val3); + ecode4 = SWIG_AsVal_bool(swig_obj[3], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "SentencePieceProcessor__EncodeAsIdsBatch" "', argument " "4"" of type '" "bool""'"); + } + arg4 = static_cast< bool >(val4); + ecode5 = SWIG_AsVal_int(swig_obj[4], &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "SentencePieceProcessor__EncodeAsIdsBatch" "', argument " "5"" of type '" "int""'"); + } + arg5 = static_cast< int >(val5); + ecode6 = SWIG_AsVal_float(swig_obj[5], &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "SentencePieceProcessor__EncodeAsIdsBatch" "', argument " "6"" of type '" "float""'"); + } + arg6 = static_cast< float >(val6); + ecode7 = SWIG_AsVal_bool(swig_obj[6], &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "SentencePieceProcessor__EncodeAsIdsBatch" "', argument " "7"" of type '" "bool""'"); + } + arg7 = static_cast< bool >(val7); + ecode8 = SWIG_AsVal_bool(swig_obj[7], &val8); + if (!SWIG_IsOK(ecode8)) { + SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "SentencePieceProcessor__EncodeAsIdsBatch" "', argument " "8"" of type '" "bool""'"); + } + arg8 = static_cast< bool >(val8); + ecode9 = SWIG_AsVal_bool(swig_obj[8], &val9); + if (!SWIG_IsOK(ecode9)) { + SWIG_exception_fail(SWIG_ArgError(ecode9), "in method '" "SentencePieceProcessor__EncodeAsIdsBatch" "', argument " "9"" of type '" "bool""'"); + } + arg9 = static_cast< bool >(val9); + ecode10 = SWIG_AsVal_bool(swig_obj[9], &val10); + if (!SWIG_IsOK(ecode10)) { + SWIG_exception_fail(SWIG_ArgError(ecode10), "in method '" "SentencePieceProcessor__EncodeAsIdsBatch" "', argument " "10"" of type '" "bool""'"); + } + arg10 = static_cast< bool >(val10); + { + try { + result = sentencepiece_SentencePieceProcessor__EncodeAsIdsBatch((sentencepiece::SentencePieceProcessor const *)arg1,(std::vector< absl::string_view > const &)*arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = PyList_New((&result)->size()); + for (size_t i = 0; i < (&result)->size(); ++i) { + PyObject *obj = PyList_New(result[i].size()); + for (size_t j = 0; j < result[i].size(); ++j) { + PyList_SET_ITEM(obj, j, PyInt_FromLong(static_cast(result[i][j]))); + } + PyList_SET_ITEM(resultobj, i, obj); + } + } + { + delete arg2; + } + return resultobj; +fail: + { + delete arg2; + } + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__EncodeAsPiecesBatch(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + std::vector< absl::string_view > *arg2 = 0 ; + int arg3 ; + bool arg4 ; + int arg5 ; + float arg6 ; + bool arg7 ; + bool arg8 ; + bool arg9 ; + bool arg10 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val3 ; + int ecode3 = 0 ; + bool val4 ; + int ecode4 = 0 ; + int val5 ; + int ecode5 = 0 ; + float val6 ; + int ecode6 = 0 ; + bool val7 ; + int ecode7 = 0 ; + bool val8 ; + int ecode8 = 0 ; + bool val9 ; + int ecode9 = 0 ; + bool val10 ; + int ecode10 = 0 ; + PyObject *swig_obj[10] ; + std::vector< std::vector< std::string > > result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__EncodeAsPiecesBatch", 10, 10, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__EncodeAsPiecesBatch" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + std::vector *out = nullptr; + if (PyList_Check(swig_obj[1])) { + const size_t size = PyList_Size(swig_obj[1]); + out = new std::vector(size); + for (size_t i = 0; i < size; ++i) { + const PyInputString ustring(PyList_GetItem(swig_obj[1], i)); + if (ustring.IsAvalable()) { + (*out)[i] = ustring.str(); + } else { + PyErr_SetString(PyExc_TypeError, "list must contain strings"); + SWIG_fail; + } + resultobj = ustring.input_type(); + } + } else { + PyErr_SetString(PyExc_TypeError, "not a list"); + SWIG_fail; + } + arg2 = out; + } + ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__EncodeAsPiecesBatch" "', argument " "3"" of type '" "int""'"); + } + arg3 = static_cast< int >(val3); + ecode4 = SWIG_AsVal_bool(swig_obj[3], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "SentencePieceProcessor__EncodeAsPiecesBatch" "', argument " "4"" of type '" "bool""'"); + } + arg4 = static_cast< bool >(val4); + ecode5 = SWIG_AsVal_int(swig_obj[4], &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "SentencePieceProcessor__EncodeAsPiecesBatch" "', argument " "5"" of type '" "int""'"); + } + arg5 = static_cast< int >(val5); + ecode6 = SWIG_AsVal_float(swig_obj[5], &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "SentencePieceProcessor__EncodeAsPiecesBatch" "', argument " "6"" of type '" "float""'"); + } + arg6 = static_cast< float >(val6); + ecode7 = SWIG_AsVal_bool(swig_obj[6], &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "SentencePieceProcessor__EncodeAsPiecesBatch" "', argument " "7"" of type '" "bool""'"); + } + arg7 = static_cast< bool >(val7); + ecode8 = SWIG_AsVal_bool(swig_obj[7], &val8); + if (!SWIG_IsOK(ecode8)) { + SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "SentencePieceProcessor__EncodeAsPiecesBatch" "', argument " "8"" of type '" "bool""'"); + } + arg8 = static_cast< bool >(val8); + ecode9 = SWIG_AsVal_bool(swig_obj[8], &val9); + if (!SWIG_IsOK(ecode9)) { + SWIG_exception_fail(SWIG_ArgError(ecode9), "in method '" "SentencePieceProcessor__EncodeAsPiecesBatch" "', argument " "9"" of type '" "bool""'"); + } + arg9 = static_cast< bool >(val9); + ecode10 = SWIG_AsVal_bool(swig_obj[9], &val10); + if (!SWIG_IsOK(ecode10)) { + SWIG_exception_fail(SWIG_ArgError(ecode10), "in method '" "SentencePieceProcessor__EncodeAsPiecesBatch" "', argument " "10"" of type '" "bool""'"); + } + arg10 = static_cast< bool >(val10); + { + try { + result = sentencepiece_SentencePieceProcessor__EncodeAsPiecesBatch((sentencepiece::SentencePieceProcessor const *)arg1,(std::vector< absl::string_view > const &)*arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + PyObject *input_type = resultobj; + resultobj = PyList_New((&result)->size()); + for (size_t i = 0; i < (&result)->size(); ++i) { + PyObject *obj = PyList_New(result[i].size()); + for (size_t j = 0; j < result[i].size(); ++j) { + PyList_SET_ITEM(obj, j, MakePyOutputString(result[i][j], input_type)); + } + PyList_SET_ITEM(resultobj, i, obj); + } + } + { + delete arg2; + } + return resultobj; +fail: + { + delete arg2; + } + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__EncodeAsSerializedProtoBatch(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + std::vector< absl::string_view > *arg2 = 0 ; + int arg3 ; + bool arg4 ; + int arg5 ; + float arg6 ; + bool arg7 ; + bool arg8 ; + bool arg9 ; + bool arg10 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val3 ; + int ecode3 = 0 ; + bool val4 ; + int ecode4 = 0 ; + int val5 ; + int ecode5 = 0 ; + float val6 ; + int ecode6 = 0 ; + bool val7 ; + int ecode7 = 0 ; + bool val8 ; + int ecode8 = 0 ; + bool val9 ; + int ecode9 = 0 ; + bool val10 ; + int ecode10 = 0 ; + PyObject *swig_obj[10] ; + BytesArray result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__EncodeAsSerializedProtoBatch", 10, 10, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__EncodeAsSerializedProtoBatch" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + std::vector *out = nullptr; + if (PyList_Check(swig_obj[1])) { + const size_t size = PyList_Size(swig_obj[1]); + out = new std::vector(size); + for (size_t i = 0; i < size; ++i) { + const PyInputString ustring(PyList_GetItem(swig_obj[1], i)); + if (ustring.IsAvalable()) { + (*out)[i] = ustring.str(); + } else { + PyErr_SetString(PyExc_TypeError, "list must contain strings"); + SWIG_fail; + } + resultobj = ustring.input_type(); + } + } else { + PyErr_SetString(PyExc_TypeError, "not a list"); + SWIG_fail; + } + arg2 = out; + } + ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__EncodeAsSerializedProtoBatch" "', argument " "3"" of type '" "int""'"); + } + arg3 = static_cast< int >(val3); + ecode4 = SWIG_AsVal_bool(swig_obj[3], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "SentencePieceProcessor__EncodeAsSerializedProtoBatch" "', argument " "4"" of type '" "bool""'"); + } + arg4 = static_cast< bool >(val4); + ecode5 = SWIG_AsVal_int(swig_obj[4], &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "SentencePieceProcessor__EncodeAsSerializedProtoBatch" "', argument " "5"" of type '" "int""'"); + } + arg5 = static_cast< int >(val5); + ecode6 = SWIG_AsVal_float(swig_obj[5], &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "SentencePieceProcessor__EncodeAsSerializedProtoBatch" "', argument " "6"" of type '" "float""'"); + } + arg6 = static_cast< float >(val6); + ecode7 = SWIG_AsVal_bool(swig_obj[6], &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "SentencePieceProcessor__EncodeAsSerializedProtoBatch" "', argument " "7"" of type '" "bool""'"); + } + arg7 = static_cast< bool >(val7); + ecode8 = SWIG_AsVal_bool(swig_obj[7], &val8); + if (!SWIG_IsOK(ecode8)) { + SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "SentencePieceProcessor__EncodeAsSerializedProtoBatch" "', argument " "8"" of type '" "bool""'"); + } + arg8 = static_cast< bool >(val8); + ecode9 = SWIG_AsVal_bool(swig_obj[8], &val9); + if (!SWIG_IsOK(ecode9)) { + SWIG_exception_fail(SWIG_ArgError(ecode9), "in method '" "SentencePieceProcessor__EncodeAsSerializedProtoBatch" "', argument " "9"" of type '" "bool""'"); + } + arg9 = static_cast< bool >(val9); + ecode10 = SWIG_AsVal_bool(swig_obj[9], &val10); + if (!SWIG_IsOK(ecode10)) { + SWIG_exception_fail(SWIG_ArgError(ecode10), "in method '" "SentencePieceProcessor__EncodeAsSerializedProtoBatch" "', argument " "10"" of type '" "bool""'"); + } + arg10 = static_cast< bool >(val10); + { + try { + result = sentencepiece_SentencePieceProcessor__EncodeAsSerializedProtoBatch((sentencepiece::SentencePieceProcessor const *)arg1,(std::vector< absl::string_view > const &)*arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = PyList_New((&result)->size()); + for (size_t i = 0; i < (&result)->size(); ++i) { + PyList_SET_ITEM(resultobj, i, MakePyOutputBytes(result[i])); + } + } + { + delete arg2; + } + return resultobj; +fail: + { + delete arg2; + } + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__EncodeAsImmutableProtoBatch(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + std::vector< absl::string_view > *arg2 = 0 ; + int arg3 ; + bool arg4 ; + int arg5 ; + float arg6 ; + bool arg7 ; + bool arg8 ; + bool arg9 ; + bool arg10 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val3 ; + int ecode3 = 0 ; + bool val4 ; + int ecode4 = 0 ; + int val5 ; + int ecode5 = 0 ; + float val6 ; + int ecode6 = 0 ; + bool val7 ; + int ecode7 = 0 ; + bool val8 ; + int ecode8 = 0 ; + bool val9 ; + int ecode9 = 0 ; + bool val10 ; + int ecode10 = 0 ; + PyObject *swig_obj[10] ; + SwigValueWrapper< std::vector< sentencepiece::ImmutableSentencePieceText > > result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__EncodeAsImmutableProtoBatch", 10, 10, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__EncodeAsImmutableProtoBatch" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + std::vector *out = nullptr; + if (PyList_Check(swig_obj[1])) { + const size_t size = PyList_Size(swig_obj[1]); + out = new std::vector(size); + for (size_t i = 0; i < size; ++i) { + const PyInputString ustring(PyList_GetItem(swig_obj[1], i)); + if (ustring.IsAvalable()) { + (*out)[i] = ustring.str(); + } else { + PyErr_SetString(PyExc_TypeError, "list must contain strings"); + SWIG_fail; + } + resultobj = ustring.input_type(); + } + } else { + PyErr_SetString(PyExc_TypeError, "not a list"); + SWIG_fail; + } + arg2 = out; + } + ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__EncodeAsImmutableProtoBatch" "', argument " "3"" of type '" "int""'"); + } + arg3 = static_cast< int >(val3); + ecode4 = SWIG_AsVal_bool(swig_obj[3], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "SentencePieceProcessor__EncodeAsImmutableProtoBatch" "', argument " "4"" of type '" "bool""'"); + } + arg4 = static_cast< bool >(val4); + ecode5 = SWIG_AsVal_int(swig_obj[4], &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "SentencePieceProcessor__EncodeAsImmutableProtoBatch" "', argument " "5"" of type '" "int""'"); + } + arg5 = static_cast< int >(val5); + ecode6 = SWIG_AsVal_float(swig_obj[5], &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "SentencePieceProcessor__EncodeAsImmutableProtoBatch" "', argument " "6"" of type '" "float""'"); + } + arg6 = static_cast< float >(val6); + ecode7 = SWIG_AsVal_bool(swig_obj[6], &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "SentencePieceProcessor__EncodeAsImmutableProtoBatch" "', argument " "7"" of type '" "bool""'"); + } + arg7 = static_cast< bool >(val7); + ecode8 = SWIG_AsVal_bool(swig_obj[7], &val8); + if (!SWIG_IsOK(ecode8)) { + SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "SentencePieceProcessor__EncodeAsImmutableProtoBatch" "', argument " "8"" of type '" "bool""'"); + } + arg8 = static_cast< bool >(val8); + ecode9 = SWIG_AsVal_bool(swig_obj[8], &val9); + if (!SWIG_IsOK(ecode9)) { + SWIG_exception_fail(SWIG_ArgError(ecode9), "in method '" "SentencePieceProcessor__EncodeAsImmutableProtoBatch" "', argument " "9"" of type '" "bool""'"); + } + arg9 = static_cast< bool >(val9); + ecode10 = SWIG_AsVal_bool(swig_obj[9], &val10); + if (!SWIG_IsOK(ecode10)) { + SWIG_exception_fail(SWIG_ArgError(ecode10), "in method '" "SentencePieceProcessor__EncodeAsImmutableProtoBatch" "', argument " "10"" of type '" "bool""'"); + } + arg10 = static_cast< bool >(val10); + { + try { + result = sentencepiece_SentencePieceProcessor__EncodeAsImmutableProtoBatch((sentencepiece::SentencePieceProcessor const *)arg1,(std::vector< absl::string_view > const &)*arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = PyList_New((&result)->size()); + for (size_t i = 0; i < (&result)->size(); ++i) { + PyObject *obj = SWIG_NewPointerObj(new sentencepiece::ImmutableSentencePieceText((&result)->at(i)), SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText, SWIG_POINTER_OWN | 0); + PyList_SET_ITEM(resultobj, i, obj); + } + } + { + delete arg2; + } + return resultobj; +fail: + { + delete arg2; + } + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__DecodeIds(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + std::vector< int > *arg2 = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + std::string result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__DecodeIds", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__DecodeIds" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + std::vector *out = nullptr; + if (PyList_Check(swig_obj[1])) { + const size_t size = PyList_Size(swig_obj[1]); + out = new std::vector(size); + for (size_t i = 0; i < size; ++i) { + PyObject *o = PyList_GetItem(swig_obj[1], i); + if (PyInt_Check(o)) { + (*out)[i] = static_cast(PyInt_AsLong(o)); + } else { + PyErr_SetString(PyExc_TypeError,"list must contain integers"); + SWIG_fail; + } + } + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + SWIG_fail; + } + arg2 = out; + } + { + try { + result = sentencepiece_SentencePieceProcessor__DecodeIds((sentencepiece::SentencePieceProcessor const *)arg1,(std::vector< int > const &)*arg2); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + PyObject *input_type = resultobj; + resultobj = MakePyOutputString(result, input_type); + } + { + delete arg2; + } + return resultobj; +fail: + { + delete arg2; + } + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__DecodeIdsAsBytes(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + std::vector< int > *arg2 = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + sentencepiece::util::bytes result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__DecodeIdsAsBytes", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__DecodeIdsAsBytes" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + std::vector *out = nullptr; + if (PyList_Check(swig_obj[1])) { + const size_t size = PyList_Size(swig_obj[1]); + out = new std::vector(size); + for (size_t i = 0; i < size; ++i) { + PyObject *o = PyList_GetItem(swig_obj[1], i); + if (PyInt_Check(o)) { + (*out)[i] = static_cast(PyInt_AsLong(o)); + } else { + PyErr_SetString(PyExc_TypeError,"list must contain integers"); + SWIG_fail; + } + } + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + SWIG_fail; + } + arg2 = out; + } + { + try { + result = sentencepiece_SentencePieceProcessor__DecodeIdsAsBytes((sentencepiece::SentencePieceProcessor const *)arg1,(std::vector< int > const &)*arg2); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = MakePyOutputBytes(result); + } + { + delete arg2; + } + return resultobj; +fail: + { + delete arg2; + } + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__DecodePieces(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + std::vector< absl::string_view > *arg2 = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + std::string result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__DecodePieces", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__DecodePieces" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + std::vector *out = nullptr; + if (PyList_Check(swig_obj[1])) { + const size_t size = PyList_Size(swig_obj[1]); + out = new std::vector(size); + for (size_t i = 0; i < size; ++i) { + const PyInputString ustring(PyList_GetItem(swig_obj[1], i)); + if (ustring.IsAvalable()) { + (*out)[i] = ustring.str(); + } else { + PyErr_SetString(PyExc_TypeError, "list must contain strings"); + SWIG_fail; + } + resultobj = ustring.input_type(); + } + } else { + PyErr_SetString(PyExc_TypeError, "not a list"); + SWIG_fail; + } + arg2 = out; + } + { + try { + result = sentencepiece_SentencePieceProcessor__DecodePieces((sentencepiece::SentencePieceProcessor const *)arg1,(std::vector< absl::string_view > const &)*arg2); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + PyObject *input_type = resultobj; + resultobj = MakePyOutputString(result, input_type); + } + { + delete arg2; + } + return resultobj; +fail: + { + delete arg2; + } + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__DecodeIdsAsSerializedProto(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + std::vector< int > *arg2 = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + sentencepiece::util::bytes result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__DecodeIdsAsSerializedProto", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__DecodeIdsAsSerializedProto" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + std::vector *out = nullptr; + if (PyList_Check(swig_obj[1])) { + const size_t size = PyList_Size(swig_obj[1]); + out = new std::vector(size); + for (size_t i = 0; i < size; ++i) { + PyObject *o = PyList_GetItem(swig_obj[1], i); + if (PyInt_Check(o)) { + (*out)[i] = static_cast(PyInt_AsLong(o)); + } else { + PyErr_SetString(PyExc_TypeError,"list must contain integers"); + SWIG_fail; + } + } + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + SWIG_fail; + } + arg2 = out; + } + { + try { + result = sentencepiece_SentencePieceProcessor__DecodeIdsAsSerializedProto((sentencepiece::SentencePieceProcessor const *)arg1,(std::vector< int > const &)*arg2); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = MakePyOutputBytes(result); + } + { + delete arg2; + } + return resultobj; +fail: + { + delete arg2; + } + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__DecodePiecesAsSerializedProto(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + std::vector< absl::string_view > *arg2 = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + sentencepiece::util::bytes result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__DecodePiecesAsSerializedProto", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__DecodePiecesAsSerializedProto" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + std::vector *out = nullptr; + if (PyList_Check(swig_obj[1])) { + const size_t size = PyList_Size(swig_obj[1]); + out = new std::vector(size); + for (size_t i = 0; i < size; ++i) { + const PyInputString ustring(PyList_GetItem(swig_obj[1], i)); + if (ustring.IsAvalable()) { + (*out)[i] = ustring.str(); + } else { + PyErr_SetString(PyExc_TypeError, "list must contain strings"); + SWIG_fail; + } + resultobj = ustring.input_type(); + } + } else { + PyErr_SetString(PyExc_TypeError, "not a list"); + SWIG_fail; + } + arg2 = out; + } + { + try { + result = sentencepiece_SentencePieceProcessor__DecodePiecesAsSerializedProto((sentencepiece::SentencePieceProcessor const *)arg1,(std::vector< absl::string_view > const &)*arg2); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = MakePyOutputBytes(result); + } + { + delete arg2; + } + return resultobj; +fail: + { + delete arg2; + } + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__DecodeIdsAsImmutableProto(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + std::vector< int > *arg2 = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + sentencepiece::ImmutableSentencePieceText result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__DecodeIdsAsImmutableProto", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__DecodeIdsAsImmutableProto" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + std::vector *out = nullptr; + if (PyList_Check(swig_obj[1])) { + const size_t size = PyList_Size(swig_obj[1]); + out = new std::vector(size); + for (size_t i = 0; i < size; ++i) { + PyObject *o = PyList_GetItem(swig_obj[1], i); + if (PyInt_Check(o)) { + (*out)[i] = static_cast(PyInt_AsLong(o)); + } else { + PyErr_SetString(PyExc_TypeError,"list must contain integers"); + SWIG_fail; + } + } + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + SWIG_fail; + } + arg2 = out; + } + { + try { + result = sentencepiece_SentencePieceProcessor__DecodeIdsAsImmutableProto((sentencepiece::SentencePieceProcessor const *)arg1,(std::vector< int > const &)*arg2); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_NewPointerObj((new sentencepiece::ImmutableSentencePieceText(result)), SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText, SWIG_POINTER_OWN | 0 ); + { + delete arg2; + } + return resultobj; +fail: + { + delete arg2; + } + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__DecodePiecesAsImmutableProto(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + std::vector< absl::string_view > *arg2 = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + sentencepiece::ImmutableSentencePieceText result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__DecodePiecesAsImmutableProto", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__DecodePiecesAsImmutableProto" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + std::vector *out = nullptr; + if (PyList_Check(swig_obj[1])) { + const size_t size = PyList_Size(swig_obj[1]); + out = new std::vector(size); + for (size_t i = 0; i < size; ++i) { + const PyInputString ustring(PyList_GetItem(swig_obj[1], i)); + if (ustring.IsAvalable()) { + (*out)[i] = ustring.str(); + } else { + PyErr_SetString(PyExc_TypeError, "list must contain strings"); + SWIG_fail; + } + resultobj = ustring.input_type(); + } + } else { + PyErr_SetString(PyExc_TypeError, "not a list"); + SWIG_fail; + } + arg2 = out; + } + { + try { + result = sentencepiece_SentencePieceProcessor__DecodePiecesAsImmutableProto((sentencepiece::SentencePieceProcessor const *)arg1,(std::vector< absl::string_view > const &)*arg2); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_NewPointerObj((new sentencepiece::ImmutableSentencePieceText(result)), SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText, SWIG_POINTER_OWN | 0 ); + { + delete arg2; + } + return resultobj; +fail: + { + delete arg2; + } + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__DecodeIdsBatch(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + std::vector< std::vector< int > > *arg2 = 0 ; + int arg3 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val3 ; + int ecode3 = 0 ; + PyObject *swig_obj[3] ; + std::vector< std::string > result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__DecodeIdsBatch", 3, 3, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__DecodeIdsBatch" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + std::vector> *out = nullptr; + if (PyList_Check(swig_obj[1])) { + const size_t size = PyList_Size(swig_obj[1]); + out = new std::vector>(size); + for (size_t i = 0; i < size; ++i) { + PyObject *o = PyList_GetItem(swig_obj[1], i); + if (PyList_Check(o)) { + const size_t size2 = PyList_Size(o); + (*out)[i].resize(size2); + for (size_t j = 0; j < size2; ++j) { + PyObject *o2 = PyList_GetItem(o, j); + if (PyInt_Check(o2)) { + (*out)[i][j] = static_cast(PyInt_AsLong(o2)); + } else { + PyErr_SetString(PyExc_TypeError, "list must contain strings"); + SWIG_fail; + } + } + } else { + PyErr_SetString(PyExc_TypeError, "not a list"); + SWIG_fail; + } + } + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + SWIG_fail; + } + arg2 = out; + } + ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__DecodeIdsBatch" "', argument " "3"" of type '" "int""'"); + } + arg3 = static_cast< int >(val3); + { + try { + result = sentencepiece_SentencePieceProcessor__DecodeIdsBatch((sentencepiece::SentencePieceProcessor const *)arg1,(std::vector< std::vector< int > > const &)*arg2,arg3); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + PyObject *input_type = resultobj; + resultobj = PyList_New((&result)->size()); + for (size_t i = 0; i < (&result)->size(); ++i) { + PyList_SET_ITEM(resultobj, i, MakePyOutputString(result[i], input_type)); + } + } + { + delete arg2; + } + return resultobj; +fail: + { + delete arg2; + } + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__DecodeIdsAsBytesBatch(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + std::vector< std::vector< int > > *arg2 = 0 ; + int arg3 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val3 ; + int ecode3 = 0 ; + PyObject *swig_obj[3] ; + BytesArray result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__DecodeIdsAsBytesBatch", 3, 3, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__DecodeIdsAsBytesBatch" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + std::vector> *out = nullptr; + if (PyList_Check(swig_obj[1])) { + const size_t size = PyList_Size(swig_obj[1]); + out = new std::vector>(size); + for (size_t i = 0; i < size; ++i) { + PyObject *o = PyList_GetItem(swig_obj[1], i); + if (PyList_Check(o)) { + const size_t size2 = PyList_Size(o); + (*out)[i].resize(size2); + for (size_t j = 0; j < size2; ++j) { + PyObject *o2 = PyList_GetItem(o, j); + if (PyInt_Check(o2)) { + (*out)[i][j] = static_cast(PyInt_AsLong(o2)); + } else { + PyErr_SetString(PyExc_TypeError, "list must contain strings"); + SWIG_fail; + } + } + } else { + PyErr_SetString(PyExc_TypeError, "not a list"); + SWIG_fail; + } + } + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + SWIG_fail; + } + arg2 = out; + } + ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__DecodeIdsAsBytesBatch" "', argument " "3"" of type '" "int""'"); + } + arg3 = static_cast< int >(val3); + { + try { + result = sentencepiece_SentencePieceProcessor__DecodeIdsAsBytesBatch((sentencepiece::SentencePieceProcessor const *)arg1,(std::vector< std::vector< int > > const &)*arg2,arg3); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = PyList_New((&result)->size()); + for (size_t i = 0; i < (&result)->size(); ++i) { + PyList_SET_ITEM(resultobj, i, MakePyOutputBytes(result[i])); + } + } + { + delete arg2; + } + return resultobj; +fail: + { + delete arg2; + } + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__DecodeIdsAsSerializedProtoBatch(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + std::vector< std::vector< int > > *arg2 = 0 ; + int arg3 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val3 ; + int ecode3 = 0 ; + PyObject *swig_obj[3] ; + BytesArray result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__DecodeIdsAsSerializedProtoBatch", 3, 3, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__DecodeIdsAsSerializedProtoBatch" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + std::vector> *out = nullptr; + if (PyList_Check(swig_obj[1])) { + const size_t size = PyList_Size(swig_obj[1]); + out = new std::vector>(size); + for (size_t i = 0; i < size; ++i) { + PyObject *o = PyList_GetItem(swig_obj[1], i); + if (PyList_Check(o)) { + const size_t size2 = PyList_Size(o); + (*out)[i].resize(size2); + for (size_t j = 0; j < size2; ++j) { + PyObject *o2 = PyList_GetItem(o, j); + if (PyInt_Check(o2)) { + (*out)[i][j] = static_cast(PyInt_AsLong(o2)); + } else { + PyErr_SetString(PyExc_TypeError, "list must contain strings"); + SWIG_fail; + } + } + } else { + PyErr_SetString(PyExc_TypeError, "not a list"); + SWIG_fail; + } + } + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + SWIG_fail; + } + arg2 = out; + } + ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__DecodeIdsAsSerializedProtoBatch" "', argument " "3"" of type '" "int""'"); + } + arg3 = static_cast< int >(val3); + { + try { + result = sentencepiece_SentencePieceProcessor__DecodeIdsAsSerializedProtoBatch((sentencepiece::SentencePieceProcessor const *)arg1,(std::vector< std::vector< int > > const &)*arg2,arg3); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = PyList_New((&result)->size()); + for (size_t i = 0; i < (&result)->size(); ++i) { + PyList_SET_ITEM(resultobj, i, MakePyOutputBytes(result[i])); + } + } + { + delete arg2; + } + return resultobj; +fail: + { + delete arg2; + } + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__DecodeIdsAsImmutableProtoBatch(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + std::vector< std::vector< int > > *arg2 = 0 ; + int arg3 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val3 ; + int ecode3 = 0 ; + PyObject *swig_obj[3] ; + SwigValueWrapper< std::vector< sentencepiece::ImmutableSentencePieceText > > result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__DecodeIdsAsImmutableProtoBatch", 3, 3, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__DecodeIdsAsImmutableProtoBatch" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + std::vector> *out = nullptr; + if (PyList_Check(swig_obj[1])) { + const size_t size = PyList_Size(swig_obj[1]); + out = new std::vector>(size); + for (size_t i = 0; i < size; ++i) { + PyObject *o = PyList_GetItem(swig_obj[1], i); + if (PyList_Check(o)) { + const size_t size2 = PyList_Size(o); + (*out)[i].resize(size2); + for (size_t j = 0; j < size2; ++j) { + PyObject *o2 = PyList_GetItem(o, j); + if (PyInt_Check(o2)) { + (*out)[i][j] = static_cast(PyInt_AsLong(o2)); + } else { + PyErr_SetString(PyExc_TypeError, "list must contain strings"); + SWIG_fail; + } + } + } else { + PyErr_SetString(PyExc_TypeError, "not a list"); + SWIG_fail; + } + } + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + SWIG_fail; + } + arg2 = out; + } + ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__DecodeIdsAsImmutableProtoBatch" "', argument " "3"" of type '" "int""'"); + } + arg3 = static_cast< int >(val3); + { + try { + result = sentencepiece_SentencePieceProcessor__DecodeIdsAsImmutableProtoBatch((sentencepiece::SentencePieceProcessor const *)arg1,(std::vector< std::vector< int > > const &)*arg2,arg3); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = PyList_New((&result)->size()); + for (size_t i = 0; i < (&result)->size(); ++i) { + PyObject *obj = SWIG_NewPointerObj(new sentencepiece::ImmutableSentencePieceText((&result)->at(i)), SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText, SWIG_POINTER_OWN | 0); + PyList_SET_ITEM(resultobj, i, obj); + } + } + { + delete arg2; + } + return resultobj; +fail: + { + delete arg2; + } + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__DecodePiecesBatch(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + std::vector< std::vector< absl::string_view > > *arg2 = 0 ; + int arg3 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val3 ; + int ecode3 = 0 ; + PyObject *swig_obj[3] ; + std::vector< std::string > result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__DecodePiecesBatch", 3, 3, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__DecodePiecesBatch" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + std::vector> *out = nullptr; + if (PyList_Check(swig_obj[1])) { + const size_t size = PyList_Size(swig_obj[1]); + out = new std::vector>(size); + for (size_t i = 0; i < size; ++i) { + PyObject *o = PyList_GetItem(swig_obj[1], i); + if (PyList_Check(o)) { + const size_t size2 = PyList_Size(o); + (*out)[i].resize(size2); + for (size_t j = 0; j < size2; ++j) { + const PyInputString ustring(PyList_GetItem(o, j)); + if (ustring.IsAvalable()) { + (*out)[i][j] = ustring.str(); + } else { + PyErr_SetString(PyExc_TypeError,"list must contain integers"); + SWIG_fail; + } + resultobj = ustring.input_type(); + } + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + SWIG_fail; + } + } + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + SWIG_fail; + } + arg2 = out; + } + ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__DecodePiecesBatch" "', argument " "3"" of type '" "int""'"); + } + arg3 = static_cast< int >(val3); + { + try { + result = sentencepiece_SentencePieceProcessor__DecodePiecesBatch((sentencepiece::SentencePieceProcessor const *)arg1,(std::vector< std::vector< absl::string_view > > const &)*arg2,arg3); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + PyObject *input_type = resultobj; + resultobj = PyList_New((&result)->size()); + for (size_t i = 0; i < (&result)->size(); ++i) { + PyList_SET_ITEM(resultobj, i, MakePyOutputString(result[i], input_type)); + } + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__DecodePiecesAsSerializedProtoBatch(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + std::vector< std::vector< absl::string_view > > *arg2 = 0 ; + int arg3 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val3 ; + int ecode3 = 0 ; + PyObject *swig_obj[3] ; + BytesArray result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__DecodePiecesAsSerializedProtoBatch", 3, 3, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__DecodePiecesAsSerializedProtoBatch" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + std::vector> *out = nullptr; + if (PyList_Check(swig_obj[1])) { + const size_t size = PyList_Size(swig_obj[1]); + out = new std::vector>(size); + for (size_t i = 0; i < size; ++i) { + PyObject *o = PyList_GetItem(swig_obj[1], i); + if (PyList_Check(o)) { + const size_t size2 = PyList_Size(o); + (*out)[i].resize(size2); + for (size_t j = 0; j < size2; ++j) { + const PyInputString ustring(PyList_GetItem(o, j)); + if (ustring.IsAvalable()) { + (*out)[i][j] = ustring.str(); + } else { + PyErr_SetString(PyExc_TypeError,"list must contain integers"); + SWIG_fail; + } + resultobj = ustring.input_type(); + } + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + SWIG_fail; + } + } + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + SWIG_fail; + } + arg2 = out; + } + ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__DecodePiecesAsSerializedProtoBatch" "', argument " "3"" of type '" "int""'"); + } + arg3 = static_cast< int >(val3); + { + try { + result = sentencepiece_SentencePieceProcessor__DecodePiecesAsSerializedProtoBatch((sentencepiece::SentencePieceProcessor const *)arg1,(std::vector< std::vector< absl::string_view > > const &)*arg2,arg3); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = PyList_New((&result)->size()); + for (size_t i = 0; i < (&result)->size(); ++i) { + PyList_SET_ITEM(resultobj, i, MakePyOutputBytes(result[i])); + } + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__DecodePiecesAsImmutableProtoBatch(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + std::vector< std::vector< absl::string_view > > *arg2 = 0 ; + int arg3 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val3 ; + int ecode3 = 0 ; + PyObject *swig_obj[3] ; + SwigValueWrapper< std::vector< sentencepiece::ImmutableSentencePieceText > > result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__DecodePiecesAsImmutableProtoBatch", 3, 3, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__DecodePiecesAsImmutableProtoBatch" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + std::vector> *out = nullptr; + if (PyList_Check(swig_obj[1])) { + const size_t size = PyList_Size(swig_obj[1]); + out = new std::vector>(size); + for (size_t i = 0; i < size; ++i) { + PyObject *o = PyList_GetItem(swig_obj[1], i); + if (PyList_Check(o)) { + const size_t size2 = PyList_Size(o); + (*out)[i].resize(size2); + for (size_t j = 0; j < size2; ++j) { + const PyInputString ustring(PyList_GetItem(o, j)); + if (ustring.IsAvalable()) { + (*out)[i][j] = ustring.str(); + } else { + PyErr_SetString(PyExc_TypeError,"list must contain integers"); + SWIG_fail; + } + resultobj = ustring.input_type(); + } + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + SWIG_fail; + } + } + } else { + PyErr_SetString(PyExc_TypeError,"not a list"); + SWIG_fail; + } + arg2 = out; + } + ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__DecodePiecesAsImmutableProtoBatch" "', argument " "3"" of type '" "int""'"); + } + arg3 = static_cast< int >(val3); + { + try { + result = sentencepiece_SentencePieceProcessor__DecodePiecesAsImmutableProtoBatch((sentencepiece::SentencePieceProcessor const *)arg1,(std::vector< std::vector< absl::string_view > > const &)*arg2,arg3); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = PyList_New((&result)->size()); + for (size_t i = 0; i < (&result)->size(); ++i) { + PyObject *obj = SWIG_NewPointerObj(new sentencepiece::ImmutableSentencePieceText((&result)->at(i)), SWIGTYPE_p_sentencepiece__ImmutableSentencePieceText, SWIG_POINTER_OWN | 0); + PyList_SET_ITEM(resultobj, i, obj); + } + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__NBestEncodeAsIds(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + int arg3 ; + bool arg4 ; + bool arg5 ; + bool arg6 ; + bool arg7 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val3 ; + int ecode3 = 0 ; + bool val4 ; + int ecode4 = 0 ; + bool val5 ; + int ecode5 = 0 ; + bool val6 ; + int ecode6 = 0 ; + bool val7 ; + int ecode7 = 0 ; + PyObject *swig_obj[7] ; + std::vector< std::vector< int > > result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__NBestEncodeAsIds", 7, 7, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__NBestEncodeAsIds" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__NBestEncodeAsIds" "', argument " "3"" of type '" "int""'"); + } + arg3 = static_cast< int >(val3); + ecode4 = SWIG_AsVal_bool(swig_obj[3], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "SentencePieceProcessor__NBestEncodeAsIds" "', argument " "4"" of type '" "bool""'"); + } + arg4 = static_cast< bool >(val4); + ecode5 = SWIG_AsVal_bool(swig_obj[4], &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "SentencePieceProcessor__NBestEncodeAsIds" "', argument " "5"" of type '" "bool""'"); + } + arg5 = static_cast< bool >(val5); + ecode6 = SWIG_AsVal_bool(swig_obj[5], &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "SentencePieceProcessor__NBestEncodeAsIds" "', argument " "6"" of type '" "bool""'"); + } + arg6 = static_cast< bool >(val6); + ecode7 = SWIG_AsVal_bool(swig_obj[6], &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "SentencePieceProcessor__NBestEncodeAsIds" "', argument " "7"" of type '" "bool""'"); + } + arg7 = static_cast< bool >(val7); + { + try { + result = sentencepiece_SentencePieceProcessor__NBestEncodeAsIds((sentencepiece::SentencePieceProcessor const *)arg1,SWIG_STD_MOVE(arg2),arg3,arg4,arg5,arg6,arg7); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = PyList_New((&result)->size()); + for (size_t i = 0; i < (&result)->size(); ++i) { + PyObject *obj = PyList_New(result[i].size()); + for (size_t j = 0; j < result[i].size(); ++j) { + PyList_SET_ITEM(obj, j, PyInt_FromLong(static_cast(result[i][j]))); + } + PyList_SET_ITEM(resultobj, i, obj); + } + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__NBestEncodeAsPieces(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + int arg3 ; + bool arg4 ; + bool arg5 ; + bool arg6 ; + bool arg7 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val3 ; + int ecode3 = 0 ; + bool val4 ; + int ecode4 = 0 ; + bool val5 ; + int ecode5 = 0 ; + bool val6 ; + int ecode6 = 0 ; + bool val7 ; + int ecode7 = 0 ; + PyObject *swig_obj[7] ; + std::vector< std::vector< std::string > > result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__NBestEncodeAsPieces", 7, 7, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__NBestEncodeAsPieces" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__NBestEncodeAsPieces" "', argument " "3"" of type '" "int""'"); + } + arg3 = static_cast< int >(val3); + ecode4 = SWIG_AsVal_bool(swig_obj[3], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "SentencePieceProcessor__NBestEncodeAsPieces" "', argument " "4"" of type '" "bool""'"); + } + arg4 = static_cast< bool >(val4); + ecode5 = SWIG_AsVal_bool(swig_obj[4], &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "SentencePieceProcessor__NBestEncodeAsPieces" "', argument " "5"" of type '" "bool""'"); + } + arg5 = static_cast< bool >(val5); + ecode6 = SWIG_AsVal_bool(swig_obj[5], &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "SentencePieceProcessor__NBestEncodeAsPieces" "', argument " "6"" of type '" "bool""'"); + } + arg6 = static_cast< bool >(val6); + ecode7 = SWIG_AsVal_bool(swig_obj[6], &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "SentencePieceProcessor__NBestEncodeAsPieces" "', argument " "7"" of type '" "bool""'"); + } + arg7 = static_cast< bool >(val7); + { + try { + result = sentencepiece_SentencePieceProcessor__NBestEncodeAsPieces((sentencepiece::SentencePieceProcessor const *)arg1,SWIG_STD_MOVE(arg2),arg3,arg4,arg5,arg6,arg7); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + PyObject *input_type = resultobj; + resultobj = PyList_New((&result)->size()); + for (size_t i = 0; i < (&result)->size(); ++i) { + PyObject *obj = PyList_New(result[i].size()); + for (size_t j = 0; j < result[i].size(); ++j) { + PyList_SET_ITEM(obj, j, MakePyOutputString(result[i][j], input_type)); + } + PyList_SET_ITEM(resultobj, i, obj); + } + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__NBestEncodeAsSerializedProto(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + int arg3 ; + bool arg4 ; + bool arg5 ; + bool arg6 ; + bool arg7 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val3 ; + int ecode3 = 0 ; + bool val4 ; + int ecode4 = 0 ; + bool val5 ; + int ecode5 = 0 ; + bool val6 ; + int ecode6 = 0 ; + bool val7 ; + int ecode7 = 0 ; + PyObject *swig_obj[7] ; + sentencepiece::util::bytes result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__NBestEncodeAsSerializedProto", 7, 7, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__NBestEncodeAsSerializedProto" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__NBestEncodeAsSerializedProto" "', argument " "3"" of type '" "int""'"); + } + arg3 = static_cast< int >(val3); + ecode4 = SWIG_AsVal_bool(swig_obj[3], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "SentencePieceProcessor__NBestEncodeAsSerializedProto" "', argument " "4"" of type '" "bool""'"); + } + arg4 = static_cast< bool >(val4); + ecode5 = SWIG_AsVal_bool(swig_obj[4], &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "SentencePieceProcessor__NBestEncodeAsSerializedProto" "', argument " "5"" of type '" "bool""'"); + } + arg5 = static_cast< bool >(val5); + ecode6 = SWIG_AsVal_bool(swig_obj[5], &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "SentencePieceProcessor__NBestEncodeAsSerializedProto" "', argument " "6"" of type '" "bool""'"); + } + arg6 = static_cast< bool >(val6); + ecode7 = SWIG_AsVal_bool(swig_obj[6], &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "SentencePieceProcessor__NBestEncodeAsSerializedProto" "', argument " "7"" of type '" "bool""'"); + } + arg7 = static_cast< bool >(val7); + { + try { + result = sentencepiece_SentencePieceProcessor__NBestEncodeAsSerializedProto((sentencepiece::SentencePieceProcessor const *)arg1,SWIG_STD_MOVE(arg2),arg3,arg4,arg5,arg6,arg7); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = MakePyOutputBytes(result); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__NBestEncodeAsImmutableProto(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + int arg3 ; + bool arg4 ; + bool arg5 ; + bool arg6 ; + bool arg7 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val3 ; + int ecode3 = 0 ; + bool val4 ; + int ecode4 = 0 ; + bool val5 ; + int ecode5 = 0 ; + bool val6 ; + int ecode6 = 0 ; + bool val7 ; + int ecode7 = 0 ; + PyObject *swig_obj[7] ; + sentencepiece::ImmutableNBestSentencePieceText result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__NBestEncodeAsImmutableProto", 7, 7, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__NBestEncodeAsImmutableProto" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__NBestEncodeAsImmutableProto" "', argument " "3"" of type '" "int""'"); + } + arg3 = static_cast< int >(val3); + ecode4 = SWIG_AsVal_bool(swig_obj[3], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "SentencePieceProcessor__NBestEncodeAsImmutableProto" "', argument " "4"" of type '" "bool""'"); + } + arg4 = static_cast< bool >(val4); + ecode5 = SWIG_AsVal_bool(swig_obj[4], &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "SentencePieceProcessor__NBestEncodeAsImmutableProto" "', argument " "5"" of type '" "bool""'"); + } + arg5 = static_cast< bool >(val5); + ecode6 = SWIG_AsVal_bool(swig_obj[5], &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "SentencePieceProcessor__NBestEncodeAsImmutableProto" "', argument " "6"" of type '" "bool""'"); + } + arg6 = static_cast< bool >(val6); + ecode7 = SWIG_AsVal_bool(swig_obj[6], &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "SentencePieceProcessor__NBestEncodeAsImmutableProto" "', argument " "7"" of type '" "bool""'"); + } + arg7 = static_cast< bool >(val7); + { + try { + result = sentencepiece_SentencePieceProcessor__NBestEncodeAsImmutableProto((sentencepiece::SentencePieceProcessor const *)arg1,SWIG_STD_MOVE(arg2),arg3,arg4,arg5,arg6,arg7); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_NewPointerObj((new sentencepiece::ImmutableNBestSentencePieceText(result)), SWIGTYPE_p_sentencepiece__ImmutableNBestSentencePieceText, SWIG_POINTER_OWN | 0 ); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__SampleEncodeAndScoreAsIds(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + int arg3 ; + float arg4 ; + bool arg5 ; + bool arg6 ; + bool arg7 ; + bool arg8 ; + bool arg9 ; + bool arg10 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val3 ; + int ecode3 = 0 ; + float val4 ; + int ecode4 = 0 ; + bool val5 ; + int ecode5 = 0 ; + bool val6 ; + int ecode6 = 0 ; + bool val7 ; + int ecode7 = 0 ; + bool val8 ; + int ecode8 = 0 ; + bool val9 ; + int ecode9 = 0 ; + bool val10 ; + int ecode10 = 0 ; + PyObject *swig_obj[10] ; + std::vector< std::pair< std::vector< int >,float > > result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__SampleEncodeAndScoreAsIds", 10, 10, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsIds" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsIds" "', argument " "3"" of type '" "int""'"); + } + arg3 = static_cast< int >(val3); + ecode4 = SWIG_AsVal_float(swig_obj[3], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsIds" "', argument " "4"" of type '" "float""'"); + } + arg4 = static_cast< float >(val4); + ecode5 = SWIG_AsVal_bool(swig_obj[4], &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsIds" "', argument " "5"" of type '" "bool""'"); + } + arg5 = static_cast< bool >(val5); + ecode6 = SWIG_AsVal_bool(swig_obj[5], &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsIds" "', argument " "6"" of type '" "bool""'"); + } + arg6 = static_cast< bool >(val6); + ecode7 = SWIG_AsVal_bool(swig_obj[6], &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsIds" "', argument " "7"" of type '" "bool""'"); + } + arg7 = static_cast< bool >(val7); + ecode8 = SWIG_AsVal_bool(swig_obj[7], &val8); + if (!SWIG_IsOK(ecode8)) { + SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsIds" "', argument " "8"" of type '" "bool""'"); + } + arg8 = static_cast< bool >(val8); + ecode9 = SWIG_AsVal_bool(swig_obj[8], &val9); + if (!SWIG_IsOK(ecode9)) { + SWIG_exception_fail(SWIG_ArgError(ecode9), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsIds" "', argument " "9"" of type '" "bool""'"); + } + arg9 = static_cast< bool >(val9); + ecode10 = SWIG_AsVal_bool(swig_obj[9], &val10); + if (!SWIG_IsOK(ecode10)) { + SWIG_exception_fail(SWIG_ArgError(ecode10), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsIds" "', argument " "10"" of type '" "bool""'"); + } + arg10 = static_cast< bool >(val10); + { + try { + result = sentencepiece_SentencePieceProcessor__SampleEncodeAndScoreAsIds((sentencepiece::SentencePieceProcessor const *)arg1,SWIG_STD_MOVE(arg2),arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = PyList_New((&result)->size()); + for (size_t i = 0; i < (&result)->size(); ++i) { + PyObject *obj = PyList_New(result[i].first.size()); + for (size_t j = 0; j < result[i].first.size(); ++j) { + PyList_SET_ITEM(obj, j, PyInt_FromLong(static_cast(result[i].first[j]))); + } + PyList_SET_ITEM(resultobj, i, PyTuple_Pack(2, obj, PyFloat_FromDouble(static_cast(result[i].second)))); + } + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__SampleEncodeAndScoreAsPieces(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + int arg3 ; + float arg4 ; + bool arg5 ; + bool arg6 ; + bool arg7 ; + bool arg8 ; + bool arg9 ; + bool arg10 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val3 ; + int ecode3 = 0 ; + float val4 ; + int ecode4 = 0 ; + bool val5 ; + int ecode5 = 0 ; + bool val6 ; + int ecode6 = 0 ; + bool val7 ; + int ecode7 = 0 ; + bool val8 ; + int ecode8 = 0 ; + bool val9 ; + int ecode9 = 0 ; + bool val10 ; + int ecode10 = 0 ; + PyObject *swig_obj[10] ; + std::vector< std::pair< std::vector< std::string >,float > > result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__SampleEncodeAndScoreAsPieces", 10, 10, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsPieces" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsPieces" "', argument " "3"" of type '" "int""'"); + } + arg3 = static_cast< int >(val3); + ecode4 = SWIG_AsVal_float(swig_obj[3], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsPieces" "', argument " "4"" of type '" "float""'"); + } + arg4 = static_cast< float >(val4); + ecode5 = SWIG_AsVal_bool(swig_obj[4], &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsPieces" "', argument " "5"" of type '" "bool""'"); + } + arg5 = static_cast< bool >(val5); + ecode6 = SWIG_AsVal_bool(swig_obj[5], &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsPieces" "', argument " "6"" of type '" "bool""'"); + } + arg6 = static_cast< bool >(val6); + ecode7 = SWIG_AsVal_bool(swig_obj[6], &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsPieces" "', argument " "7"" of type '" "bool""'"); + } + arg7 = static_cast< bool >(val7); + ecode8 = SWIG_AsVal_bool(swig_obj[7], &val8); + if (!SWIG_IsOK(ecode8)) { + SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsPieces" "', argument " "8"" of type '" "bool""'"); + } + arg8 = static_cast< bool >(val8); + ecode9 = SWIG_AsVal_bool(swig_obj[8], &val9); + if (!SWIG_IsOK(ecode9)) { + SWIG_exception_fail(SWIG_ArgError(ecode9), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsPieces" "', argument " "9"" of type '" "bool""'"); + } + arg9 = static_cast< bool >(val9); + ecode10 = SWIG_AsVal_bool(swig_obj[9], &val10); + if (!SWIG_IsOK(ecode10)) { + SWIG_exception_fail(SWIG_ArgError(ecode10), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsPieces" "', argument " "10"" of type '" "bool""'"); + } + arg10 = static_cast< bool >(val10); + { + try { + result = sentencepiece_SentencePieceProcessor__SampleEncodeAndScoreAsPieces((sentencepiece::SentencePieceProcessor const *)arg1,SWIG_STD_MOVE(arg2),arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + PyObject *input_type = resultobj; + resultobj = PyList_New((&result)->size()); + for (size_t i = 0; i < (&result)->size(); ++i) { + PyObject *obj = PyList_New(result[i].first.size()); + for (size_t j = 0; j < result[i].first.size(); ++j) { + PyList_SET_ITEM(obj, j, MakePyOutputString(result[i].first[j], input_type)); + } + PyList_SET_ITEM(resultobj, i, PyTuple_Pack(2, obj, PyFloat_FromDouble(static_cast(result[i].second)))); + } + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__SampleEncodeAndScoreAsSerializedProto(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + int arg3 ; + float arg4 ; + bool arg5 ; + bool arg6 ; + bool arg7 ; + bool arg8 ; + bool arg9 ; + bool arg10 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val3 ; + int ecode3 = 0 ; + float val4 ; + int ecode4 = 0 ; + bool val5 ; + int ecode5 = 0 ; + bool val6 ; + int ecode6 = 0 ; + bool val7 ; + int ecode7 = 0 ; + bool val8 ; + int ecode8 = 0 ; + bool val9 ; + int ecode9 = 0 ; + bool val10 ; + int ecode10 = 0 ; + PyObject *swig_obj[10] ; + sentencepiece::util::bytes result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__SampleEncodeAndScoreAsSerializedProto", 10, 10, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsSerializedProto" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsSerializedProto" "', argument " "3"" of type '" "int""'"); + } + arg3 = static_cast< int >(val3); + ecode4 = SWIG_AsVal_float(swig_obj[3], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsSerializedProto" "', argument " "4"" of type '" "float""'"); + } + arg4 = static_cast< float >(val4); + ecode5 = SWIG_AsVal_bool(swig_obj[4], &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsSerializedProto" "', argument " "5"" of type '" "bool""'"); + } + arg5 = static_cast< bool >(val5); + ecode6 = SWIG_AsVal_bool(swig_obj[5], &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsSerializedProto" "', argument " "6"" of type '" "bool""'"); + } + arg6 = static_cast< bool >(val6); + ecode7 = SWIG_AsVal_bool(swig_obj[6], &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsSerializedProto" "', argument " "7"" of type '" "bool""'"); + } + arg7 = static_cast< bool >(val7); + ecode8 = SWIG_AsVal_bool(swig_obj[7], &val8); + if (!SWIG_IsOK(ecode8)) { + SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsSerializedProto" "', argument " "8"" of type '" "bool""'"); + } + arg8 = static_cast< bool >(val8); + ecode9 = SWIG_AsVal_bool(swig_obj[8], &val9); + if (!SWIG_IsOK(ecode9)) { + SWIG_exception_fail(SWIG_ArgError(ecode9), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsSerializedProto" "', argument " "9"" of type '" "bool""'"); + } + arg9 = static_cast< bool >(val9); + ecode10 = SWIG_AsVal_bool(swig_obj[9], &val10); + if (!SWIG_IsOK(ecode10)) { + SWIG_exception_fail(SWIG_ArgError(ecode10), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsSerializedProto" "', argument " "10"" of type '" "bool""'"); + } + arg10 = static_cast< bool >(val10); + { + try { + result = sentencepiece_SentencePieceProcessor__SampleEncodeAndScoreAsSerializedProto((sentencepiece::SentencePieceProcessor const *)arg1,SWIG_STD_MOVE(arg2),arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = MakePyOutputBytes(result); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__SampleEncodeAndScoreAsImmutableProto(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + int arg3 ; + float arg4 ; + bool arg5 ; + bool arg6 ; + bool arg7 ; + bool arg8 ; + bool arg9 ; + bool arg10 ; + void *argp1 = 0 ; + int res1 = 0 ; + int val3 ; + int ecode3 = 0 ; + float val4 ; + int ecode4 = 0 ; + bool val5 ; + int ecode5 = 0 ; + bool val6 ; + int ecode6 = 0 ; + bool val7 ; + int ecode7 = 0 ; + bool val8 ; + int ecode8 = 0 ; + bool val9 ; + int ecode9 = 0 ; + bool val10 ; + int ecode10 = 0 ; + PyObject *swig_obj[10] ; + sentencepiece::ImmutableNBestSentencePieceText result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__SampleEncodeAndScoreAsImmutableProto", 10, 10, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsImmutableProto" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + ecode3 = SWIG_AsVal_int(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsImmutableProto" "', argument " "3"" of type '" "int""'"); + } + arg3 = static_cast< int >(val3); + ecode4 = SWIG_AsVal_float(swig_obj[3], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsImmutableProto" "', argument " "4"" of type '" "float""'"); + } + arg4 = static_cast< float >(val4); + ecode5 = SWIG_AsVal_bool(swig_obj[4], &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsImmutableProto" "', argument " "5"" of type '" "bool""'"); + } + arg5 = static_cast< bool >(val5); + ecode6 = SWIG_AsVal_bool(swig_obj[5], &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsImmutableProto" "', argument " "6"" of type '" "bool""'"); + } + arg6 = static_cast< bool >(val6); + ecode7 = SWIG_AsVal_bool(swig_obj[6], &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsImmutableProto" "', argument " "7"" of type '" "bool""'"); + } + arg7 = static_cast< bool >(val7); + ecode8 = SWIG_AsVal_bool(swig_obj[7], &val8); + if (!SWIG_IsOK(ecode8)) { + SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsImmutableProto" "', argument " "8"" of type '" "bool""'"); + } + arg8 = static_cast< bool >(val8); + ecode9 = SWIG_AsVal_bool(swig_obj[8], &val9); + if (!SWIG_IsOK(ecode9)) { + SWIG_exception_fail(SWIG_ArgError(ecode9), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsImmutableProto" "', argument " "9"" of type '" "bool""'"); + } + arg9 = static_cast< bool >(val9); + ecode10 = SWIG_AsVal_bool(swig_obj[9], &val10); + if (!SWIG_IsOK(ecode10)) { + SWIG_exception_fail(SWIG_ArgError(ecode10), "in method '" "SentencePieceProcessor__SampleEncodeAndScoreAsImmutableProto" "', argument " "10"" of type '" "bool""'"); + } + arg10 = static_cast< bool >(val10); + { + try { + result = sentencepiece_SentencePieceProcessor__SampleEncodeAndScoreAsImmutableProto((sentencepiece::SentencePieceProcessor const *)arg1,SWIG_STD_MOVE(arg2),arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_NewPointerObj((new sentencepiece::ImmutableNBestSentencePieceText(result)), SWIGTYPE_p_sentencepiece__ImmutableNBestSentencePieceText, SWIG_POINTER_OWN | 0 ); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__Normalize(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + std::string result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__Normalize", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__Normalize" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + { + try { + result = sentencepiece_SentencePieceProcessor__Normalize(arg1,SWIG_STD_MOVE(arg2)); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + PyObject *input_type = resultobj; + resultobj = MakePyOutputString(result, input_type); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__NormalizeWithOffsets(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + std::pair< std::string,std::vector< size_t > > result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__NormalizeWithOffsets", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__NormalizeWithOffsets" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + { + try { + result = sentencepiece_SentencePieceProcessor__NormalizeWithOffsets(arg1,SWIG_STD_MOVE(arg2)); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + PyObject *input_type = resultobj; + if (PyInputString::IsUnicode(input_type)) { + sentencepiece::ConvertToUnicodeAlignment(arg2, (&result)->first, &(&result)->second); + } + PyObject *obj = PyList_New((&result)->second.size()); + for (size_t i = 0; i < (&result)->second.size(); ++i) { + PyList_SET_ITEM(obj, i, PyInt_FromLong(static_cast((&result)->second[i]))); + } + resultobj = PyTuple_Pack(2, MakePyOutputString((&result)->first, input_type), obj); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__CalculateEntropy(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + absl::string_view arg2 ; + float arg3 ; + void *argp1 = 0 ; + int res1 = 0 ; + float val3 ; + int ecode3 = 0 ; + PyObject *swig_obj[3] ; + float result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__CalculateEntropy", 3, 3, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__CalculateEntropy" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + ecode3 = SWIG_AsVal_float(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__CalculateEntropy" "', argument " "3"" of type '" "float""'"); + } + arg3 = static_cast< float >(val3); + { + try { + result = (float)sentencepiece_SentencePieceProcessor__CalculateEntropy(arg1,SWIG_STD_MOVE(arg2),arg3); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_From_float(static_cast< float >(result)); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__CalculateEntropyBatch(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + std::vector< absl::string_view > *arg2 = 0 ; + float arg3 ; + int arg4 ; + void *argp1 = 0 ; + int res1 = 0 ; + float val3 ; + int ecode3 = 0 ; + int val4 ; + int ecode4 = 0 ; + PyObject *swig_obj[4] ; + std::vector< float > result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__CalculateEntropyBatch", 4, 4, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__CalculateEntropyBatch" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + std::vector *out = nullptr; + if (PyList_Check(swig_obj[1])) { + const size_t size = PyList_Size(swig_obj[1]); + out = new std::vector(size); + for (size_t i = 0; i < size; ++i) { + const PyInputString ustring(PyList_GetItem(swig_obj[1], i)); + if (ustring.IsAvalable()) { + (*out)[i] = ustring.str(); + } else { + PyErr_SetString(PyExc_TypeError, "list must contain strings"); + SWIG_fail; + } + resultobj = ustring.input_type(); + } + } else { + PyErr_SetString(PyExc_TypeError, "not a list"); + SWIG_fail; + } + arg2 = out; + } + ecode3 = SWIG_AsVal_float(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceProcessor__CalculateEntropyBatch" "', argument " "3"" of type '" "float""'"); + } + arg3 = static_cast< float >(val3); + ecode4 = SWIG_AsVal_int(swig_obj[3], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "SentencePieceProcessor__CalculateEntropyBatch" "', argument " "4"" of type '" "int""'"); + } + arg4 = static_cast< int >(val4); + { + try { + result = sentencepiece_SentencePieceProcessor__CalculateEntropyBatch(arg1,(std::vector< absl::string_view > const &)*arg2,arg3,arg4); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = PyList_New((&result)->size()); + for (size_t i = 0; i < (&result)->size(); ++i) { + PyList_SET_ITEM(resultobj, i, PyFloat_FromDouble(static_cast(result[i]))); + } + } + { + delete arg2; + } + return resultobj; +fail: + { + delete arg2; + } + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceProcessor__OverrideNormalizerSpec(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceProcessor *arg1 = (sentencepiece::SentencePieceProcessor *) 0 ; + std::unordered_map< std::string,std::string > *arg2 = 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + sentencepiece::util::Status result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceProcessor__OverrideNormalizerSpec", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceProcessor, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceProcessor__OverrideNormalizerSpec" "', argument " "1"" of type '" "sentencepiece::SentencePieceProcessor *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceProcessor * >(argp1); + { + std::unordered_map *out = nullptr; + if (PyDict_Check(swig_obj[1])) { + PyObject *key, *value; + Py_ssize_t pos = 0; + out = new std::unordered_map; + while (PyDict_Next(swig_obj[1], &pos, &key, &value)) { + const PyInputString key_ustring(key); + const PyInputString value_ustring(value); + if (key_ustring.IsAvalable() && value_ustring.IsAvalable()) { + out->emplace(std::string(key_ustring.data(), key_ustring.size()), + std::string(value_ustring.data(), value_ustring.size())); + } else { + PyErr_SetString(PyExc_TypeError, "map must contain strings."); + SWIG_fail; + } + resultobj = key_ustring.input_type(); + } + } else { + PyErr_SetString(PyExc_TypeError, "not a dictionary"); + SWIG_fail; + } + arg2 = out; + } + { + try { + result = sentencepiece_SentencePieceProcessor__OverrideNormalizerSpec(arg1,(std::unordered_map< std::string,std::string > const &)*arg2); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + if (!(&result)->ok()) { + SWIG_exception(ToSwigError((&result)->code()), (&result)->ToString().c_str()); + } + resultobj = SWIG_From_bool((&result)->ok()); + } + { + delete arg2; + } + return resultobj; +fail: + { + delete arg2; + } + return NULL; +} + + +SWIGINTERN PyObject *SentencePieceProcessor_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *obj = NULL; + if (!SWIG_Python_UnpackTuple(args, "swigregister", 1, 1, &obj)) return NULL; + SWIG_TypeNewClientData(SWIGTYPE_p_sentencepiece__SentencePieceProcessor, SWIG_NewClientData(obj)); + return SWIG_Py_Void(); +} + +SWIGINTERN PyObject *SentencePieceProcessor_swiginit(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + return SWIG_Python_InitShadowInstance(args); +} + +SWIGINTERN PyObject *_wrap_SetRandomGeneratorSeed(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + unsigned int arg1 ; + unsigned int val1 ; + int ecode1 = 0 ; + PyObject *swig_obj[1] ; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + ecode1 = SWIG_AsVal_unsigned_SS_int(swig_obj[0], &val1); + if (!SWIG_IsOK(ecode1)) { + SWIG_exception_fail(SWIG_ArgError(ecode1), "in method '" "SetRandomGeneratorSeed" "', argument " "1"" of type '" "unsigned int""'"); + } + arg1 = static_cast< unsigned int >(val1); + { + try { + sentencepiece::SetRandomGeneratorSeed(arg1); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_Py_Void(); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SetMinLogLevel(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + int arg1 ; + int val1 ; + int ecode1 = 0 ; + PyObject *swig_obj[1] ; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + ecode1 = SWIG_AsVal_int(swig_obj[0], &val1); + if (!SWIG_IsOK(ecode1)) { + SWIG_exception_fail(SWIG_ArgError(ecode1), "in method '" "SetMinLogLevel" "', argument " "1"" of type '" "int""'"); + } + arg1 = static_cast< int >(val1); + { + try { + sentencepiece::SetMinLogLevel(arg1); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_Py_Void(); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceTrainer__TrainFromString(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + absl::string_view arg1 ; + PyObject *swig_obj[1] ; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + { + const PyInputString ustring(swig_obj[0]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg1 = ustring.str(); + } + { + try { + sentencepiece_SentencePieceTrainer__TrainFromString(SWIG_STD_MOVE(arg1)); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_Py_Void(); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceTrainer__TrainFromMap(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + std::unordered_map< std::string,std::string > *arg1 = 0 ; + PyObject *swig_obj[1] ; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + { + std::unordered_map *out = nullptr; + if (PyDict_Check(swig_obj[0])) { + PyObject *key, *value; + Py_ssize_t pos = 0; + out = new std::unordered_map; + while (PyDict_Next(swig_obj[0], &pos, &key, &value)) { + const PyInputString key_ustring(key); + const PyInputString value_ustring(value); + if (key_ustring.IsAvalable() && value_ustring.IsAvalable()) { + out->emplace(std::string(key_ustring.data(), key_ustring.size()), + std::string(value_ustring.data(), value_ustring.size())); + } else { + PyErr_SetString(PyExc_TypeError, "map must contain strings."); + SWIG_fail; + } + resultobj = key_ustring.input_type(); + } + } else { + PyErr_SetString(PyExc_TypeError, "not a dictionary"); + SWIG_fail; + } + arg1 = out; + } + { + try { + sentencepiece_SentencePieceTrainer__TrainFromMap((std::unordered_map< std::string,std::string > const &)*arg1); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_Py_Void(); + { + delete arg1; + } + return resultobj; +fail: + { + delete arg1; + } + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceTrainer__TrainFromMap2(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + std::unordered_map< std::string,std::string > *arg1 = 0 ; + sentencepiece::SentenceIterator *arg2 = (sentencepiece::SentenceIterator *) 0 ; + PyObject *swig_obj[2] ; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceTrainer__TrainFromMap2", 2, 2, swig_obj)) SWIG_fail; + { + std::unordered_map *out = nullptr; + if (PyDict_Check(swig_obj[0])) { + PyObject *key, *value; + Py_ssize_t pos = 0; + out = new std::unordered_map; + while (PyDict_Next(swig_obj[0], &pos, &key, &value)) { + const PyInputString key_ustring(key); + const PyInputString value_ustring(value); + if (key_ustring.IsAvalable() && value_ustring.IsAvalable()) { + out->emplace(std::string(key_ustring.data(), key_ustring.size()), + std::string(value_ustring.data(), value_ustring.size())); + } else { + PyErr_SetString(PyExc_TypeError, "map must contain strings."); + SWIG_fail; + } + resultobj = key_ustring.input_type(); + } + } else { + PyErr_SetString(PyExc_TypeError, "not a dictionary"); + SWIG_fail; + } + arg1 = out; + } + { + sentencepiece::SentenceIterator *out = nullptr; + if (PyIter_Check(swig_obj[1])) { + out = new PySentenceIterator(swig_obj[1]); + } else { + PyErr_SetString(PyExc_TypeError, "not a iterator"); + SWIG_fail; + } + arg2 = out; + } + { + try { + sentencepiece_SentencePieceTrainer__TrainFromMap2((std::unordered_map< std::string,std::string > const &)*arg1,arg2); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_Py_Void(); + { + delete arg1; + } + { + delete arg2; + } + return resultobj; +fail: + { + delete arg1; + } + { + delete arg2; + } + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceTrainer__TrainFromMap3(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + std::unordered_map< std::string,std::string > *arg1 = 0 ; + PyObject *swig_obj[1] ; + sentencepiece::util::bytes result; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + { + std::unordered_map *out = nullptr; + if (PyDict_Check(swig_obj[0])) { + PyObject *key, *value; + Py_ssize_t pos = 0; + out = new std::unordered_map; + while (PyDict_Next(swig_obj[0], &pos, &key, &value)) { + const PyInputString key_ustring(key); + const PyInputString value_ustring(value); + if (key_ustring.IsAvalable() && value_ustring.IsAvalable()) { + out->emplace(std::string(key_ustring.data(), key_ustring.size()), + std::string(value_ustring.data(), value_ustring.size())); + } else { + PyErr_SetString(PyExc_TypeError, "map must contain strings."); + SWIG_fail; + } + resultobj = key_ustring.input_type(); + } + } else { + PyErr_SetString(PyExc_TypeError, "not a dictionary"); + SWIG_fail; + } + arg1 = out; + } + { + try { + result = sentencepiece_SentencePieceTrainer__TrainFromMap3((std::unordered_map< std::string,std::string > const &)*arg1); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = MakePyOutputBytes(result); + } + { + delete arg1; + } + return resultobj; +fail: + { + delete arg1; + } + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceTrainer__TrainFromMap4(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + std::unordered_map< std::string,std::string > *arg1 = 0 ; + sentencepiece::SentenceIterator *arg2 = (sentencepiece::SentenceIterator *) 0 ; + PyObject *swig_obj[2] ; + sentencepiece::util::bytes result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceTrainer__TrainFromMap4", 2, 2, swig_obj)) SWIG_fail; + { + std::unordered_map *out = nullptr; + if (PyDict_Check(swig_obj[0])) { + PyObject *key, *value; + Py_ssize_t pos = 0; + out = new std::unordered_map; + while (PyDict_Next(swig_obj[0], &pos, &key, &value)) { + const PyInputString key_ustring(key); + const PyInputString value_ustring(value); + if (key_ustring.IsAvalable() && value_ustring.IsAvalable()) { + out->emplace(std::string(key_ustring.data(), key_ustring.size()), + std::string(value_ustring.data(), value_ustring.size())); + } else { + PyErr_SetString(PyExc_TypeError, "map must contain strings."); + SWIG_fail; + } + resultobj = key_ustring.input_type(); + } + } else { + PyErr_SetString(PyExc_TypeError, "not a dictionary"); + SWIG_fail; + } + arg1 = out; + } + { + sentencepiece::SentenceIterator *out = nullptr; + if (PyIter_Check(swig_obj[1])) { + out = new PySentenceIterator(swig_obj[1]); + } else { + PyErr_SetString(PyExc_TypeError, "not a iterator"); + SWIG_fail; + } + arg2 = out; + } + { + try { + result = sentencepiece_SentencePieceTrainer__TrainFromMap4((std::unordered_map< std::string,std::string > const &)*arg1,arg2); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + resultobj = MakePyOutputBytes(result); + } + { + delete arg1; + } + { + delete arg2; + } + return resultobj; +fail: + { + delete arg1; + } + { + delete arg2; + } + return NULL; +} + + +SWIGINTERN PyObject *SentencePieceTrainer_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *obj = NULL; + if (!SWIG_Python_UnpackTuple(args, "swigregister", 1, 1, &obj)) return NULL; + SWIG_TypeNewClientData(SWIGTYPE_p_sentencepiece__SentencePieceTrainer, SWIG_NewClientData(obj)); + return SWIG_Py_Void(); +} + +SWIGINTERN PyObject *_wrap_new_SentencePieceNormalizer(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceNormalizer *result = 0 ; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "new_SentencePieceNormalizer", 0, 0, 0)) SWIG_fail; + { + try { + result = (sentencepiece::SentencePieceNormalizer *)new sentencepiece::SentencePieceNormalizer(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_sentencepiece__SentencePieceNormalizer, SWIG_POINTER_NEW | 0 ); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_delete_SentencePieceNormalizer(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceNormalizer *arg1 = (sentencepiece::SentencePieceNormalizer *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceNormalizer, SWIG_POINTER_DISOWN | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "delete_SentencePieceNormalizer" "', argument " "1"" of type '" "sentencepiece::SentencePieceNormalizer *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceNormalizer * >(argp1); + { + try { + delete arg1; + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_Py_Void(); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceNormalizer_LoadFromSerializedProto(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceNormalizer *arg1 = (sentencepiece::SentencePieceNormalizer *) 0 ; + absl::string_view arg2 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + sentencepiece::util::Status result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceNormalizer_LoadFromSerializedProto", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceNormalizer, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceNormalizer_LoadFromSerializedProto" "', argument " "1"" of type '" "sentencepiece::SentencePieceNormalizer *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceNormalizer * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + { + try { + result = (arg1)->LoadFromSerializedProto(SWIG_STD_MOVE(arg2)); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + if (!(&result)->ok()) { + SWIG_exception(ToSwigError((&result)->code()), (&result)->ToString().c_str()); + } + resultobj = SWIG_From_bool((&result)->ok()); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceNormalizer_LoadFromRuleTSV(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceNormalizer *arg1 = (sentencepiece::SentencePieceNormalizer *) 0 ; + absl::string_view arg2 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + sentencepiece::util::Status result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceNormalizer_LoadFromRuleTSV", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceNormalizer, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceNormalizer_LoadFromRuleTSV" "', argument " "1"" of type '" "sentencepiece::SentencePieceNormalizer *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceNormalizer * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + { + try { + result = (arg1)->LoadFromRuleTSV(SWIG_STD_MOVE(arg2)); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + if (!(&result)->ok()) { + SWIG_exception(ToSwigError((&result)->code()), (&result)->ToString().c_str()); + } + resultobj = SWIG_From_bool((&result)->ok()); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceNormalizer_LoadFromRuleName(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceNormalizer *arg1 = (sentencepiece::SentencePieceNormalizer *) 0 ; + absl::string_view arg2 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + sentencepiece::util::Status result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceNormalizer_LoadFromRuleName", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceNormalizer, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceNormalizer_LoadFromRuleName" "', argument " "1"" of type '" "sentencepiece::SentencePieceNormalizer *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceNormalizer * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + { + try { + result = (arg1)->LoadFromRuleName(SWIG_STD_MOVE(arg2)); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + if (!(&result)->ok()) { + SWIG_exception(ToSwigError((&result)->code()), (&result)->ToString().c_str()); + } + resultobj = SWIG_From_bool((&result)->ok()); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceNormalizer_serialized_model_proto(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceNormalizer *arg1 = (sentencepiece::SentencePieceNormalizer *) 0 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[1] ; + std::string result; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceNormalizer, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceNormalizer_serialized_model_proto" "', argument " "1"" of type '" "sentencepiece::SentencePieceNormalizer const *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceNormalizer * >(argp1); + { + try { + result = ((sentencepiece::SentencePieceNormalizer const *)arg1)->serialized_model_proto(); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + PyObject *input_type = resultobj; + resultobj = MakePyOutputString(result, input_type); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceNormalizer_LoadFromFile(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceNormalizer *arg1 = (sentencepiece::SentencePieceNormalizer *) 0 ; + absl::string_view arg2 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + sentencepiece::util::Status result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceNormalizer_LoadFromFile", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceNormalizer, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceNormalizer_LoadFromFile" "', argument " "1"" of type '" "sentencepiece::SentencePieceNormalizer *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceNormalizer * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + { + try { + result = sentencepiece_SentencePieceNormalizer_LoadFromFile(arg1,SWIG_STD_MOVE(arg2)); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + if (!(&result)->ok()) { + SWIG_exception(ToSwigError((&result)->code()), (&result)->ToString().c_str()); + } + resultobj = SWIG_From_bool((&result)->ok()); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceNormalizer__Normalize(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceNormalizer *arg1 = (sentencepiece::SentencePieceNormalizer *) 0 ; + absl::string_view arg2 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + std::string result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceNormalizer__Normalize", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceNormalizer, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceNormalizer__Normalize" "', argument " "1"" of type '" "sentencepiece::SentencePieceNormalizer *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceNormalizer * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + { + try { + result = sentencepiece_SentencePieceNormalizer__Normalize(arg1,SWIG_STD_MOVE(arg2)); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + PyObject *input_type = resultobj; + resultobj = MakePyOutputString(result, input_type); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceNormalizer__NormalizeWithOffsets(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceNormalizer *arg1 = (sentencepiece::SentencePieceNormalizer *) 0 ; + absl::string_view arg2 ; + void *argp1 = 0 ; + int res1 = 0 ; + PyObject *swig_obj[2] ; + std::pair< std::string,std::vector< size_t > > result; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceNormalizer__NormalizeWithOffsets", 2, 2, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceNormalizer, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceNormalizer__NormalizeWithOffsets" "', argument " "1"" of type '" "sentencepiece::SentencePieceNormalizer *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceNormalizer * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + { + try { + result = sentencepiece_SentencePieceNormalizer__NormalizeWithOffsets(arg1,SWIG_STD_MOVE(arg2)); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + { + PyObject *input_type = resultobj; + if (PyInputString::IsUnicode(input_type)) { + sentencepiece::ConvertToUnicodeAlignment(arg2, (&result)->first, &(&result)->second); + } + PyObject *obj = PyList_New((&result)->second.size()); + for (size_t i = 0; i < (&result)->second.size(); ++i) { + PyList_SET_ITEM(obj, i, PyInt_FromLong(static_cast((&result)->second[i]))); + } + resultobj = PyTuple_Pack(2, MakePyOutputString((&result)->first, input_type), obj); + } + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *_wrap_SentencePieceNormalizer__SetProtoField(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + sentencepiece::SentencePieceNormalizer *arg1 = (sentencepiece::SentencePieceNormalizer *) 0 ; + absl::string_view arg2 ; + bool arg3 ; + void *argp1 = 0 ; + int res1 = 0 ; + bool val3 ; + int ecode3 = 0 ; + PyObject *swig_obj[3] ; + + (void)self; + if (!SWIG_Python_UnpackTuple(args, "SentencePieceNormalizer__SetProtoField", 3, 3, swig_obj)) SWIG_fail; + res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_sentencepiece__SentencePieceNormalizer, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SentencePieceNormalizer__SetProtoField" "', argument " "1"" of type '" "sentencepiece::SentencePieceNormalizer *""'"); + } + arg1 = reinterpret_cast< sentencepiece::SentencePieceNormalizer * >(argp1); + { + const PyInputString ustring(swig_obj[1]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg2 = ustring.str(); + } + ecode3 = SWIG_AsVal_bool(swig_obj[2], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "SentencePieceNormalizer__SetProtoField" "', argument " "3"" of type '" "bool""'"); + } + arg3 = static_cast< bool >(val3); + { + try { + sentencepiece_SentencePieceNormalizer__SetProtoField(arg1,SWIG_STD_MOVE(arg2),arg3); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_Py_Void(); + return resultobj; +fail: + return NULL; +} + + +SWIGINTERN PyObject *SentencePieceNormalizer_swigregister(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *obj = NULL; + if (!SWIG_Python_UnpackTuple(args, "swigregister", 1, 1, &obj)) return NULL; + SWIG_TypeNewClientData(SWIGTYPE_p_sentencepiece__SentencePieceNormalizer, SWIG_NewClientData(obj)); + return SWIG_Py_Void(); +} + +SWIGINTERN PyObject *SentencePieceNormalizer_swiginit(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + return SWIG_Python_InitShadowInstance(args); +} + +SWIGINTERN PyObject *_wrap_SetDataDir(PyObject *self, PyObject *args) { + PyObject *resultobj = 0; + absl::string_view arg1 ; + PyObject *swig_obj[1] ; + + (void)self; + if (!args) SWIG_fail; + swig_obj[0] = args; + { + const PyInputString ustring(swig_obj[0]); + if (!ustring.IsAvalable()) { + PyErr_SetString(PyExc_TypeError, "not a string"); + SWIG_fail; + } + resultobj = ustring.input_type(); + arg1 = ustring.str(); + } + { + try { + sentencepiece::SetDataDir(SWIG_STD_MOVE(arg1)); + ReleaseResultObject(resultobj); + } + catch (const sentencepiece::util::Status &status) { + SWIG_exception(ToSwigError(status.code()), status.ToString().c_str()); + } + } + resultobj = SWIG_Py_Void(); + return resultobj; +fail: + return NULL; +} + + +static PyMethodDef SwigMethods[] = { + { "new_ImmutableSentencePieceText_ImmutableSentencePiece", _wrap_new_ImmutableSentencePieceText_ImmutableSentencePiece, METH_NOARGS, NULL}, + { "delete_ImmutableSentencePieceText_ImmutableSentencePiece", _wrap_delete_ImmutableSentencePieceText_ImmutableSentencePiece, METH_O, NULL}, + { "ImmutableSentencePieceText_ImmutableSentencePiece__piece", _wrap_ImmutableSentencePieceText_ImmutableSentencePiece__piece, METH_O, NULL}, + { "ImmutableSentencePieceText_ImmutableSentencePiece__surface", _wrap_ImmutableSentencePieceText_ImmutableSentencePiece__surface, METH_O, NULL}, + { "ImmutableSentencePieceText_ImmutableSentencePiece__id", _wrap_ImmutableSentencePieceText_ImmutableSentencePiece__id, METH_O, NULL}, + { "ImmutableSentencePieceText_ImmutableSentencePiece__begin", _wrap_ImmutableSentencePieceText_ImmutableSentencePiece__begin, METH_O, NULL}, + { "ImmutableSentencePieceText_ImmutableSentencePiece__end", _wrap_ImmutableSentencePieceText_ImmutableSentencePiece__end, METH_O, NULL}, + { "ImmutableSentencePieceText_ImmutableSentencePiece__surface_as_bytes", _wrap_ImmutableSentencePieceText_ImmutableSentencePiece__surface_as_bytes, METH_O, NULL}, + { "ImmutableSentencePieceText_ImmutableSentencePiece__piece_as_bytes", _wrap_ImmutableSentencePieceText_ImmutableSentencePiece__piece_as_bytes, METH_O, NULL}, + { "ImmutableSentencePieceText_ImmutableSentencePiece_swigregister", ImmutableSentencePieceText_ImmutableSentencePiece_swigregister, METH_O, NULL}, + { "ImmutableSentencePieceText_ImmutableSentencePiece_swiginit", ImmutableSentencePieceText_ImmutableSentencePiece_swiginit, METH_VARARGS, NULL}, + { "new_ImmutableSentencePieceText", _wrap_new_ImmutableSentencePieceText, METH_NOARGS, NULL}, + { "delete_ImmutableSentencePieceText", _wrap_delete_ImmutableSentencePieceText, METH_O, NULL}, + { "ImmutableSentencePieceText__pieces_size", _wrap_ImmutableSentencePieceText__pieces_size, METH_O, NULL}, + { "ImmutableSentencePieceText__pieces", _wrap_ImmutableSentencePieceText__pieces, METH_VARARGS, NULL}, + { "ImmutableSentencePieceText__text", _wrap_ImmutableSentencePieceText__text, METH_O, NULL}, + { "ImmutableSentencePieceText__score", _wrap_ImmutableSentencePieceText__score, METH_O, NULL}, + { "ImmutableSentencePieceText_SerializeAsString", _wrap_ImmutableSentencePieceText_SerializeAsString, METH_O, NULL}, + { "ImmutableSentencePieceText__text_as_bytes", _wrap_ImmutableSentencePieceText__text_as_bytes, METH_O, NULL}, + { "ImmutableSentencePieceText_swigregister", ImmutableSentencePieceText_swigregister, METH_O, NULL}, + { "ImmutableSentencePieceText_swiginit", ImmutableSentencePieceText_swiginit, METH_VARARGS, NULL}, + { "new_ImmutableNBestSentencePieceText", _wrap_new_ImmutableNBestSentencePieceText, METH_NOARGS, NULL}, + { "delete_ImmutableNBestSentencePieceText", _wrap_delete_ImmutableNBestSentencePieceText, METH_O, NULL}, + { "ImmutableNBestSentencePieceText__nbests_size", _wrap_ImmutableNBestSentencePieceText__nbests_size, METH_O, NULL}, + { "ImmutableNBestSentencePieceText__nbests", _wrap_ImmutableNBestSentencePieceText__nbests, METH_VARARGS, NULL}, + { "ImmutableNBestSentencePieceText_SerializeAsString", _wrap_ImmutableNBestSentencePieceText_SerializeAsString, METH_O, NULL}, + { "ImmutableNBestSentencePieceText_swigregister", ImmutableNBestSentencePieceText_swigregister, METH_O, NULL}, + { "ImmutableNBestSentencePieceText_swiginit", ImmutableNBestSentencePieceText_swiginit, METH_VARARGS, NULL}, + { "new_SentencePieceProcessor", _wrap_new_SentencePieceProcessor, METH_NOARGS, NULL}, + { "delete_SentencePieceProcessor", _wrap_delete_SentencePieceProcessor, METH_O, NULL}, + { "SentencePieceProcessor_LoadFromSerializedProto", _wrap_SentencePieceProcessor_LoadFromSerializedProto, METH_VARARGS, NULL}, + { "SentencePieceProcessor_SetEncodeExtraOptions", _wrap_SentencePieceProcessor_SetEncodeExtraOptions, METH_VARARGS, NULL}, + { "SentencePieceProcessor_SetDecodeExtraOptions", _wrap_SentencePieceProcessor_SetDecodeExtraOptions, METH_VARARGS, NULL}, + { "SentencePieceProcessor_SetVocabulary", _wrap_SentencePieceProcessor_SetVocabulary, METH_VARARGS, NULL}, + { "SentencePieceProcessor_ResetVocabulary", _wrap_SentencePieceProcessor_ResetVocabulary, METH_O, NULL}, + { "SentencePieceProcessor_LoadVocabulary", _wrap_SentencePieceProcessor_LoadVocabulary, METH_VARARGS, NULL}, + { "SentencePieceProcessor_CalculateEntropy", _wrap_SentencePieceProcessor_CalculateEntropy, METH_VARARGS, NULL}, + { "SentencePieceProcessor_GetPieceSize", _wrap_SentencePieceProcessor_GetPieceSize, METH_O, NULL}, + { "SentencePieceProcessor_PieceToId", _wrap_SentencePieceProcessor_PieceToId, METH_VARARGS, NULL}, + { "SentencePieceProcessor_IdToPiece", _wrap_SentencePieceProcessor_IdToPiece, METH_VARARGS, NULL}, + { "SentencePieceProcessor_GetScore", _wrap_SentencePieceProcessor_GetScore, METH_VARARGS, NULL}, + { "SentencePieceProcessor_IsUnknown", _wrap_SentencePieceProcessor_IsUnknown, METH_VARARGS, NULL}, + { "SentencePieceProcessor_IsControl", _wrap_SentencePieceProcessor_IsControl, METH_VARARGS, NULL}, + { "SentencePieceProcessor_IsUnused", _wrap_SentencePieceProcessor_IsUnused, METH_VARARGS, NULL}, + { "SentencePieceProcessor_IsByte", _wrap_SentencePieceProcessor_IsByte, METH_VARARGS, NULL}, + { "SentencePieceProcessor_unk_id", _wrap_SentencePieceProcessor_unk_id, METH_O, NULL}, + { "SentencePieceProcessor_bos_id", _wrap_SentencePieceProcessor_bos_id, METH_O, NULL}, + { "SentencePieceProcessor_eos_id", _wrap_SentencePieceProcessor_eos_id, METH_O, NULL}, + { "SentencePieceProcessor_pad_id", _wrap_SentencePieceProcessor_pad_id, METH_O, NULL}, + { "SentencePieceProcessor_serialized_model_proto", _wrap_SentencePieceProcessor_serialized_model_proto, METH_O, NULL}, + { "SentencePieceProcessor_LoadFromFile", _wrap_SentencePieceProcessor_LoadFromFile, METH_VARARGS, NULL}, + { "SentencePieceProcessor__EncodeAsIds", _wrap_SentencePieceProcessor__EncodeAsIds, METH_VARARGS, NULL}, + { "SentencePieceProcessor__EncodeAsPieces", _wrap_SentencePieceProcessor__EncodeAsPieces, METH_VARARGS, NULL}, + { "SentencePieceProcessor__EncodeAsSerializedProto", _wrap_SentencePieceProcessor__EncodeAsSerializedProto, METH_VARARGS, NULL}, + { "SentencePieceProcessor__EncodeAsImmutableProto", _wrap_SentencePieceProcessor__EncodeAsImmutableProto, METH_VARARGS, NULL}, + { "SentencePieceProcessor__EncodeAsIdsBatch", _wrap_SentencePieceProcessor__EncodeAsIdsBatch, METH_VARARGS, NULL}, + { "SentencePieceProcessor__EncodeAsPiecesBatch", _wrap_SentencePieceProcessor__EncodeAsPiecesBatch, METH_VARARGS, NULL}, + { "SentencePieceProcessor__EncodeAsSerializedProtoBatch", _wrap_SentencePieceProcessor__EncodeAsSerializedProtoBatch, METH_VARARGS, NULL}, + { "SentencePieceProcessor__EncodeAsImmutableProtoBatch", _wrap_SentencePieceProcessor__EncodeAsImmutableProtoBatch, METH_VARARGS, NULL}, + { "SentencePieceProcessor__DecodeIds", _wrap_SentencePieceProcessor__DecodeIds, METH_VARARGS, NULL}, + { "SentencePieceProcessor__DecodeIdsAsBytes", _wrap_SentencePieceProcessor__DecodeIdsAsBytes, METH_VARARGS, NULL}, + { "SentencePieceProcessor__DecodePieces", _wrap_SentencePieceProcessor__DecodePieces, METH_VARARGS, NULL}, + { "SentencePieceProcessor__DecodeIdsAsSerializedProto", _wrap_SentencePieceProcessor__DecodeIdsAsSerializedProto, METH_VARARGS, NULL}, + { "SentencePieceProcessor__DecodePiecesAsSerializedProto", _wrap_SentencePieceProcessor__DecodePiecesAsSerializedProto, METH_VARARGS, NULL}, + { "SentencePieceProcessor__DecodeIdsAsImmutableProto", _wrap_SentencePieceProcessor__DecodeIdsAsImmutableProto, METH_VARARGS, NULL}, + { "SentencePieceProcessor__DecodePiecesAsImmutableProto", _wrap_SentencePieceProcessor__DecodePiecesAsImmutableProto, METH_VARARGS, NULL}, + { "SentencePieceProcessor__DecodeIdsBatch", _wrap_SentencePieceProcessor__DecodeIdsBatch, METH_VARARGS, NULL}, + { "SentencePieceProcessor__DecodeIdsAsBytesBatch", _wrap_SentencePieceProcessor__DecodeIdsAsBytesBatch, METH_VARARGS, NULL}, + { "SentencePieceProcessor__DecodeIdsAsSerializedProtoBatch", _wrap_SentencePieceProcessor__DecodeIdsAsSerializedProtoBatch, METH_VARARGS, NULL}, + { "SentencePieceProcessor__DecodeIdsAsImmutableProtoBatch", _wrap_SentencePieceProcessor__DecodeIdsAsImmutableProtoBatch, METH_VARARGS, NULL}, + { "SentencePieceProcessor__DecodePiecesBatch", _wrap_SentencePieceProcessor__DecodePiecesBatch, METH_VARARGS, NULL}, + { "SentencePieceProcessor__DecodePiecesAsSerializedProtoBatch", _wrap_SentencePieceProcessor__DecodePiecesAsSerializedProtoBatch, METH_VARARGS, NULL}, + { "SentencePieceProcessor__DecodePiecesAsImmutableProtoBatch", _wrap_SentencePieceProcessor__DecodePiecesAsImmutableProtoBatch, METH_VARARGS, NULL}, + { "SentencePieceProcessor__NBestEncodeAsIds", _wrap_SentencePieceProcessor__NBestEncodeAsIds, METH_VARARGS, NULL}, + { "SentencePieceProcessor__NBestEncodeAsPieces", _wrap_SentencePieceProcessor__NBestEncodeAsPieces, METH_VARARGS, NULL}, + { "SentencePieceProcessor__NBestEncodeAsSerializedProto", _wrap_SentencePieceProcessor__NBestEncodeAsSerializedProto, METH_VARARGS, NULL}, + { "SentencePieceProcessor__NBestEncodeAsImmutableProto", _wrap_SentencePieceProcessor__NBestEncodeAsImmutableProto, METH_VARARGS, NULL}, + { "SentencePieceProcessor__SampleEncodeAndScoreAsIds", _wrap_SentencePieceProcessor__SampleEncodeAndScoreAsIds, METH_VARARGS, NULL}, + { "SentencePieceProcessor__SampleEncodeAndScoreAsPieces", _wrap_SentencePieceProcessor__SampleEncodeAndScoreAsPieces, METH_VARARGS, NULL}, + { "SentencePieceProcessor__SampleEncodeAndScoreAsSerializedProto", _wrap_SentencePieceProcessor__SampleEncodeAndScoreAsSerializedProto, METH_VARARGS, NULL}, + { "SentencePieceProcessor__SampleEncodeAndScoreAsImmutableProto", _wrap_SentencePieceProcessor__SampleEncodeAndScoreAsImmutableProto, METH_VARARGS, NULL}, + { "SentencePieceProcessor__Normalize", _wrap_SentencePieceProcessor__Normalize, METH_VARARGS, NULL}, + { "SentencePieceProcessor__NormalizeWithOffsets", _wrap_SentencePieceProcessor__NormalizeWithOffsets, METH_VARARGS, NULL}, + { "SentencePieceProcessor__CalculateEntropy", _wrap_SentencePieceProcessor__CalculateEntropy, METH_VARARGS, NULL}, + { "SentencePieceProcessor__CalculateEntropyBatch", _wrap_SentencePieceProcessor__CalculateEntropyBatch, METH_VARARGS, NULL}, + { "SentencePieceProcessor__OverrideNormalizerSpec", _wrap_SentencePieceProcessor__OverrideNormalizerSpec, METH_VARARGS, NULL}, + { "SentencePieceProcessor_swigregister", SentencePieceProcessor_swigregister, METH_O, NULL}, + { "SentencePieceProcessor_swiginit", SentencePieceProcessor_swiginit, METH_VARARGS, NULL}, + { "SetRandomGeneratorSeed", _wrap_SetRandomGeneratorSeed, METH_O, NULL}, + { "SetMinLogLevel", _wrap_SetMinLogLevel, METH_O, NULL}, + { "SentencePieceTrainer__TrainFromString", _wrap_SentencePieceTrainer__TrainFromString, METH_O, NULL}, + { "SentencePieceTrainer__TrainFromMap", _wrap_SentencePieceTrainer__TrainFromMap, METH_O, NULL}, + { "SentencePieceTrainer__TrainFromMap2", _wrap_SentencePieceTrainer__TrainFromMap2, METH_VARARGS, NULL}, + { "SentencePieceTrainer__TrainFromMap3", _wrap_SentencePieceTrainer__TrainFromMap3, METH_O, NULL}, + { "SentencePieceTrainer__TrainFromMap4", _wrap_SentencePieceTrainer__TrainFromMap4, METH_VARARGS, NULL}, + { "SentencePieceTrainer_swigregister", SentencePieceTrainer_swigregister, METH_O, NULL}, + { "new_SentencePieceNormalizer", _wrap_new_SentencePieceNormalizer, METH_NOARGS, NULL}, + { "delete_SentencePieceNormalizer", _wrap_delete_SentencePieceNormalizer, METH_O, NULL}, + { "SentencePieceNormalizer_LoadFromSerializedProto", _wrap_SentencePieceNormalizer_LoadFromSerializedProto, METH_VARARGS, NULL}, + { "SentencePieceNormalizer_LoadFromRuleTSV", _wrap_SentencePieceNormalizer_LoadFromRuleTSV, METH_VARARGS, NULL}, + { "SentencePieceNormalizer_LoadFromRuleName", _wrap_SentencePieceNormalizer_LoadFromRuleName, METH_VARARGS, NULL}, + { "SentencePieceNormalizer_serialized_model_proto", _wrap_SentencePieceNormalizer_serialized_model_proto, METH_O, NULL}, + { "SentencePieceNormalizer_LoadFromFile", _wrap_SentencePieceNormalizer_LoadFromFile, METH_VARARGS, NULL}, + { "SentencePieceNormalizer__Normalize", _wrap_SentencePieceNormalizer__Normalize, METH_VARARGS, NULL}, + { "SentencePieceNormalizer__NormalizeWithOffsets", _wrap_SentencePieceNormalizer__NormalizeWithOffsets, METH_VARARGS, NULL}, + { "SentencePieceNormalizer__SetProtoField", _wrap_SentencePieceNormalizer__SetProtoField, METH_VARARGS, NULL}, + { "SentencePieceNormalizer_swigregister", SentencePieceNormalizer_swigregister, METH_O, NULL}, + { "SentencePieceNormalizer_swiginit", SentencePieceNormalizer_swiginit, METH_VARARGS, NULL}, + { "SetDataDir", _wrap_SetDataDir, METH_O, NULL}, + { NULL, NULL, 0, NULL } +}; + + +/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (BEGIN) -------- */ + +static swig_type_info _swigt__p_char = {"_p_char", "char *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_float = {"_p_float", "float *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_sentencepiece__ImmutableNBestSentencePieceText = {"_p_sentencepiece__ImmutableNBestSentencePieceText", "sentencepiece::ImmutableNBestSentencePieceText *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_sentencepiece__ImmutableSentencePieceText = {"_p_sentencepiece__ImmutableSentencePieceText", "sentencepiece::ImmutableSentencePieceText *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_sentencepiece__ImmutableSentencePieceText_ImmutableSentencePiece = {"_p_sentencepiece__ImmutableSentencePieceText_ImmutableSentencePiece", "sentencepiece::ImmutableSentencePieceText_ImmutableSentencePiece *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_sentencepiece__SentenceIterator = {"_p_sentencepiece__SentenceIterator", "sentencepiece::SentenceIterator *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_sentencepiece__SentencePieceNormalizer = {"_p_sentencepiece__SentencePieceNormalizer", "sentencepiece::SentencePieceNormalizer *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_sentencepiece__SentencePieceProcessor = {"_p_sentencepiece__SentencePieceProcessor", "sentencepiece::SentencePieceProcessor *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_sentencepiece__SentencePieceTrainer = {"_p_sentencepiece__SentencePieceTrainer", "sentencepiece::SentencePieceTrainer *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_std__string = {"_p_std__string", "sentencepiece::util::bytes *|std::string *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_std__unordered_mapT_std__string_std__string_t = {"_p_std__unordered_mapT_std__string_std__string_t", "std::unordered_map< std::string,std::string > *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_std__vectorT_absl__string_view_t = {"_p_std__vectorT_absl__string_view_t", "std::vector< absl::string_view > *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_std__vectorT_int_t = {"_p_std__vectorT_int_t", "std::vector< int > *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_std__vectorT_std__vectorT_absl__string_view_t_t = {"_p_std__vectorT_std__vectorT_absl__string_view_t_t", "std::vector< std::vector< absl::string_view > > *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_std__vectorT_std__vectorT_int_t_t = {"_p_std__vectorT_std__vectorT_int_t_t", "std::vector< std::vector< int > > *", 0, 0, (void*)0, 0}; + +static swig_type_info *swig_type_initial[] = { + &_swigt__p_char, + &_swigt__p_float, + &_swigt__p_sentencepiece__ImmutableNBestSentencePieceText, + &_swigt__p_sentencepiece__ImmutableSentencePieceText, + &_swigt__p_sentencepiece__ImmutableSentencePieceText_ImmutableSentencePiece, + &_swigt__p_sentencepiece__SentenceIterator, + &_swigt__p_sentencepiece__SentencePieceNormalizer, + &_swigt__p_sentencepiece__SentencePieceProcessor, + &_swigt__p_sentencepiece__SentencePieceTrainer, + &_swigt__p_std__string, + &_swigt__p_std__unordered_mapT_std__string_std__string_t, + &_swigt__p_std__vectorT_absl__string_view_t, + &_swigt__p_std__vectorT_int_t, + &_swigt__p_std__vectorT_std__vectorT_absl__string_view_t_t, + &_swigt__p_std__vectorT_std__vectorT_int_t_t, +}; + +static swig_cast_info _swigc__p_char[] = { {&_swigt__p_char, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_float[] = { {&_swigt__p_float, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_sentencepiece__ImmutableNBestSentencePieceText[] = { {&_swigt__p_sentencepiece__ImmutableNBestSentencePieceText, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_sentencepiece__ImmutableSentencePieceText[] = { {&_swigt__p_sentencepiece__ImmutableSentencePieceText, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_sentencepiece__ImmutableSentencePieceText_ImmutableSentencePiece[] = { {&_swigt__p_sentencepiece__ImmutableSentencePieceText_ImmutableSentencePiece, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_sentencepiece__SentenceIterator[] = { {&_swigt__p_sentencepiece__SentenceIterator, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_sentencepiece__SentencePieceNormalizer[] = { {&_swigt__p_sentencepiece__SentencePieceNormalizer, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_sentencepiece__SentencePieceProcessor[] = { {&_swigt__p_sentencepiece__SentencePieceProcessor, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_sentencepiece__SentencePieceTrainer[] = { {&_swigt__p_sentencepiece__SentencePieceTrainer, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_std__string[] = { {&_swigt__p_std__string, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_std__unordered_mapT_std__string_std__string_t[] = { {&_swigt__p_std__unordered_mapT_std__string_std__string_t, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_std__vectorT_absl__string_view_t[] = { {&_swigt__p_std__vectorT_absl__string_view_t, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_std__vectorT_int_t[] = { {&_swigt__p_std__vectorT_int_t, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_std__vectorT_std__vectorT_absl__string_view_t_t[] = { {&_swigt__p_std__vectorT_std__vectorT_absl__string_view_t_t, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_std__vectorT_std__vectorT_int_t_t[] = { {&_swigt__p_std__vectorT_std__vectorT_int_t_t, 0, 0, 0},{0, 0, 0, 0}}; + +static swig_cast_info *swig_cast_initial[] = { + _swigc__p_char, + _swigc__p_float, + _swigc__p_sentencepiece__ImmutableNBestSentencePieceText, + _swigc__p_sentencepiece__ImmutableSentencePieceText, + _swigc__p_sentencepiece__ImmutableSentencePieceText_ImmutableSentencePiece, + _swigc__p_sentencepiece__SentenceIterator, + _swigc__p_sentencepiece__SentencePieceNormalizer, + _swigc__p_sentencepiece__SentencePieceProcessor, + _swigc__p_sentencepiece__SentencePieceTrainer, + _swigc__p_std__string, + _swigc__p_std__unordered_mapT_std__string_std__string_t, + _swigc__p_std__vectorT_absl__string_view_t, + _swigc__p_std__vectorT_int_t, + _swigc__p_std__vectorT_std__vectorT_absl__string_view_t_t, + _swigc__p_std__vectorT_std__vectorT_int_t_t, +}; + + +/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (END) -------- */ + +static swig_const_info swig_const_table[] = { +{0, 0, 0, 0.0, 0, 0}}; + +#ifdef __cplusplus +} +#endif +/* ----------------------------------------------------------------------------- + * Type initialization: + * This problem is tough by the requirement that no dynamic + * memory is used. Also, since swig_type_info structures store pointers to + * swig_cast_info structures and swig_cast_info structures store pointers back + * to swig_type_info structures, we need some lookup code at initialization. + * The idea is that swig generates all the structures that are needed. + * The runtime then collects these partially filled structures. + * The SWIG_InitializeModule function takes these initial arrays out of + * swig_module, and does all the lookup, filling in the swig_module.types + * array with the correct data and linking the correct swig_cast_info + * structures together. + * + * The generated swig_type_info structures are assigned statically to an initial + * array. We just loop through that array, and handle each type individually. + * First we lookup if this type has been already loaded, and if so, use the + * loaded structure instead of the generated one. Then we have to fill in the + * cast linked list. The cast data is initially stored in something like a + * two-dimensional array. Each row corresponds to a type (there are the same + * number of rows as there are in the swig_type_initial array). Each entry in + * a column is one of the swig_cast_info structures for that type. + * The cast_initial array is actually an array of arrays, because each row has + * a variable number of columns. So to actually build the cast linked list, + * we find the array of casts associated with the type, and loop through it + * adding the casts to the list. The one last trick we need to do is making + * sure the type pointer in the swig_cast_info struct is correct. + * + * First off, we lookup the cast->type name to see if it is already loaded. + * There are three cases to handle: + * 1) If the cast->type has already been loaded AND the type we are adding + * casting info to has not been loaded (it is in this module), THEN we + * replace the cast->type pointer with the type pointer that has already + * been loaded. + * 2) If BOTH types (the one we are adding casting info to, and the + * cast->type) are loaded, THEN the cast info has already been loaded by + * the previous module so we just ignore it. + * 3) Finally, if cast->type has not already been loaded, then we add that + * swig_cast_info to the linked list (because the cast->type) pointer will + * be correct. + * ----------------------------------------------------------------------------- */ + +#ifdef __cplusplus +extern "C" { +#if 0 +} /* c-mode */ +#endif +#endif + +#if 0 +#define SWIGRUNTIME_DEBUG +#endif + +#ifndef SWIG_INIT_CLIENT_DATA_TYPE +#define SWIG_INIT_CLIENT_DATA_TYPE void * +#endif + +SWIGRUNTIME void +SWIG_InitializeModule(SWIG_INIT_CLIENT_DATA_TYPE clientdata) { + size_t i; + swig_module_info *module_head, *iter; + int init; + + /* check to see if the circular list has been setup, if not, set it up */ + if (swig_module.next==0) { + /* Initialize the swig_module */ + swig_module.type_initial = swig_type_initial; + swig_module.cast_initial = swig_cast_initial; + swig_module.next = &swig_module; + init = 1; + } else { + init = 0; + } + + /* Try and load any already created modules */ + module_head = SWIG_GetModule(clientdata); + if (!module_head) { + /* This is the first module loaded for this interpreter */ + /* so set the swig module into the interpreter */ + SWIG_SetModule(clientdata, &swig_module); + } else { + /* the interpreter has loaded a SWIG module, but has it loaded this one? */ + iter=module_head; + do { + if (iter==&swig_module) { + /* Our module is already in the list, so there's nothing more to do. */ + return; + } + iter=iter->next; + } while (iter!= module_head); + + /* otherwise we must add our module into the list */ + swig_module.next = module_head->next; + module_head->next = &swig_module; + } + + /* When multiple interpreters are used, a module could have already been initialized in + a different interpreter, but not yet have a pointer in this interpreter. + In this case, we do not want to continue adding types... everything should be + set up already */ + if (init == 0) return; + + /* Now work on filling in swig_module.types */ +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: size %lu\n", (unsigned long)swig_module.size); +#endif + for (i = 0; i < swig_module.size; ++i) { + swig_type_info *type = 0; + swig_type_info *ret; + swig_cast_info *cast; + +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: type %lu %s\n", (unsigned long)i, swig_module.type_initial[i]->name); +#endif + + /* if there is another module already loaded */ + if (swig_module.next != &swig_module) { + type = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, swig_module.type_initial[i]->name); + } + if (type) { + /* Overwrite clientdata field */ +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: found type %s\n", type->name); +#endif + if (swig_module.type_initial[i]->clientdata) { + type->clientdata = swig_module.type_initial[i]->clientdata; +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: found and overwrite type %s \n", type->name); +#endif + } + } else { + type = swig_module.type_initial[i]; + } + + /* Insert casting types */ + cast = swig_module.cast_initial[i]; + while (cast->type) { + /* Don't need to add information already in the list */ + ret = 0; +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: look cast %s\n", cast->type->name); +#endif + if (swig_module.next != &swig_module) { + ret = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, cast->type->name); +#ifdef SWIGRUNTIME_DEBUG + if (ret) printf("SWIG_InitializeModule: found cast %s\n", ret->name); +#endif + } + if (ret) { + if (type == swig_module.type_initial[i]) { +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: skip old type %s\n", ret->name); +#endif + cast->type = ret; + ret = 0; + } else { + /* Check for casting already in the list */ + swig_cast_info *ocast = SWIG_TypeCheck(ret->name, type); +#ifdef SWIGRUNTIME_DEBUG + if (ocast) printf("SWIG_InitializeModule: skip old cast %s\n", ret->name); +#endif + if (!ocast) ret = 0; + } + } + + if (!ret) { +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: adding cast %s\n", cast->type->name); +#endif + if (type->cast) { + type->cast->prev = cast; + cast->next = type->cast; + } + type->cast = cast; + } + cast++; + } + /* Set entry in modules->types array equal to the type */ + swig_module.types[i] = type; + } + swig_module.types[i] = 0; + +#ifdef SWIGRUNTIME_DEBUG + printf("**** SWIG_InitializeModule: Cast List ******\n"); + for (i = 0; i < swig_module.size; ++i) { + int j = 0; + swig_cast_info *cast = swig_module.cast_initial[i]; + printf("SWIG_InitializeModule: type %lu %s\n", (unsigned long)i, swig_module.type_initial[i]->name); + while (cast->type) { + printf("SWIG_InitializeModule: cast type %s\n", cast->type->name); + cast++; + ++j; + } + printf("---- Total casts: %d\n",j); + } + printf("**** SWIG_InitializeModule: Cast List ******\n"); +#endif +} + +/* This function will propagate the clientdata field of type to +* any new swig_type_info structures that have been added into the list +* of equivalent types. It is like calling +* SWIG_TypeClientData(type, clientdata) a second time. +*/ +SWIGRUNTIME void +SWIG_PropagateClientData(void) { + size_t i; + swig_cast_info *equiv; + static int init_run = 0; + + if (init_run) return; + init_run = 1; + + for (i = 0; i < swig_module.size; i++) { + if (swig_module.types[i]->clientdata) { + equiv = swig_module.types[i]->cast; + while (equiv) { + if (!equiv->converter) { + if (equiv->type && !equiv->type->clientdata) + SWIG_TypeClientData(equiv->type, swig_module.types[i]->clientdata); + } + equiv = equiv->next; + } + } + } +} + +#ifdef __cplusplus +#if 0 +{ + /* c-mode */ +#endif +} +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif + + /* ----------------------------------------------------------------------------- + * constants/methods manipulation + * ----------------------------------------------------------------------------- */ + + /* Install Constants */ + SWIGINTERN void + SWIG_Python_InstallConstants(PyObject *d, swig_const_info constants[]) { + PyObject *obj = 0; + size_t i; + for (i = 0; constants[i].type; ++i) { + switch(constants[i].type) { + case SWIG_PY_POINTER: + obj = SWIG_InternalNewPointerObj(constants[i].pvalue, *(constants[i]).ptype,0); + break; + case SWIG_PY_BINARY: + obj = SWIG_NewPackedObj(constants[i].pvalue, constants[i].lvalue, *(constants[i].ptype)); + break; + default: + obj = 0; + break; + } + if (obj) { + PyDict_SetItemString(d, constants[i].name, obj); + SWIG_Py_DECREF(obj); + } + } + } + + /* ----------------------------------------------------------------------------- + * Patch %callback methods' docstrings to hold the callback ptrs + * -----------------------------------------------------------------------------*/ + + SWIGINTERN void + SWIG_Python_FixMethods(PyMethodDef *methods, const swig_const_info *const_table, swig_type_info **types, swig_type_info **types_initial) { + size_t i; + for (i = 0; methods[i].ml_name; ++i) { + const char *c = methods[i].ml_doc; + if (!c) continue; + c = strstr(c, "swig_ptr: "); + if (c) { + int j; + const swig_const_info *ci = 0; + const char *name = c + 10; + for (j = 0; const_table[j].type; ++j) { + if (strncmp(const_table[j].name, name, + strlen(const_table[j].name)) == 0) { + ci = &(const_table[j]); + break; + } + } + if (ci) { + void *ptr = (ci->type == SWIG_PY_POINTER) ? ci->pvalue : 0; + if (ptr) { + size_t shift = (ci->ptype) - types; + swig_type_info *ty = types_initial[shift]; + size_t ldoc = (c - methods[i].ml_doc); + size_t lptr = strlen(ty->name)+2*sizeof(void*)+2; + char *ndoc = (char*)malloc(ldoc + lptr + 10); + if (ndoc) { + char *buff = ndoc; + memcpy(buff, methods[i].ml_doc, ldoc); + buff += ldoc; + memcpy(buff, "swig_ptr: ", 10); + buff += 10; + SWIG_PackVoidPtr(buff, ptr, ty->name, lptr); + methods[i].ml_doc = ndoc; + } + } + } + } + } + } + +#ifdef __cplusplus +} +#endif + + + + +/* -----------------------------------------------------------------------------* + * Partial Init method + * -----------------------------------------------------------------------------*/ + +#ifdef __cplusplus +extern "C" +#endif + +SWIGEXPORT +#if PY_VERSION_HEX >= 0x03000000 +PyObject* +#else +void +#endif +SWIG_init(void) { + PyObject *m, *d, *md, *globals; + +#if PY_VERSION_HEX >= 0x03000000 + static struct PyModuleDef SWIG_module = { + PyModuleDef_HEAD_INIT, + SWIG_name, + NULL, + -1, + SwigMethods, + NULL, + NULL, + NULL, + NULL + }; +#endif + +#if defined(SWIGPYTHON_BUILTIN) + static SwigPyClientData SwigPyObject_clientdata = { + 0, 0, 0, 0, 0, 0, 0 + }; + static PyGetSetDef this_getset_def = { + (char *)"this", &SwigPyBuiltin_ThisClosure, NULL, NULL, NULL + }; + static SwigPyGetSet thisown_getset_closure = { + SwigPyObject_own, + SwigPyObject_own + }; + static PyGetSetDef thisown_getset_def = { + (char *)"thisown", SwigPyBuiltin_GetterClosure, SwigPyBuiltin_SetterClosure, NULL, &thisown_getset_closure + }; + PyTypeObject *builtin_pytype; + int builtin_base_count; + swig_type_info *builtin_basetype; + PyObject *tuple; + PyGetSetDescrObject *static_getset; + PyTypeObject *metatype; + PyTypeObject *swigpyobject; + SwigPyClientData *cd; + PyObject *public_interface, *public_symbol; + PyObject *this_descr; + PyObject *thisown_descr; + PyObject *self = 0; + int i; + + (void)builtin_pytype; + (void)builtin_base_count; + (void)builtin_basetype; + (void)tuple; + (void)static_getset; + (void)self; + + /* Metaclass is used to implement static member variables */ + metatype = SwigPyObjectType(); + assert(metatype); +#endif + + (void)globals; + + /* Create singletons now to avoid potential deadlocks with multi-threaded usage after module initialization */ + SWIG_This(); + SWIG_Python_TypeCache(); + SwigPyPacked_type(); +#ifndef SWIGPYTHON_BUILTIN + SwigPyObject_type(); +#endif + + /* Fix SwigMethods to carry the callback ptrs when needed */ + SWIG_Python_FixMethods(SwigMethods, swig_const_table, swig_types, swig_type_initial); + +#if PY_VERSION_HEX >= 0x03000000 + m = PyModule_Create(&SWIG_module); +#else + m = Py_InitModule(SWIG_name, SwigMethods); +#endif + + md = d = PyModule_GetDict(m); + (void)md; + + SWIG_InitializeModule(0); + +#ifdef SWIGPYTHON_BUILTIN + swigpyobject = SwigPyObject_TypeOnce(); + + SwigPyObject_stype = SWIG_MangledTypeQuery("_p_SwigPyObject"); + assert(SwigPyObject_stype); + cd = (SwigPyClientData*) SwigPyObject_stype->clientdata; + if (!cd) { + SwigPyObject_stype->clientdata = &SwigPyObject_clientdata; + SwigPyObject_clientdata.pytype = swigpyobject; + } else if (swigpyobject->tp_basicsize != cd->pytype->tp_basicsize) { + PyErr_SetString(PyExc_RuntimeError, "Import error: attempted to load two incompatible swig-generated modules."); +# if PY_VERSION_HEX >= 0x03000000 + return NULL; +# else + return; +# endif + } + + /* All objects have a 'this' attribute */ + this_descr = PyDescr_NewGetSet(SwigPyObject_type(), &this_getset_def); + (void)this_descr; + + /* All objects have a 'thisown' attribute */ + thisown_descr = PyDescr_NewGetSet(SwigPyObject_type(), &thisown_getset_def); + (void)thisown_descr; + + public_interface = PyList_New(0); + public_symbol = 0; + (void)public_symbol; + + PyDict_SetItemString(md, "__all__", public_interface); + SWIG_Py_DECREF(public_interface); + for (i = 0; SwigMethods[i].ml_name != NULL; ++i) + SwigPyBuiltin_AddPublicSymbol(public_interface, SwigMethods[i].ml_name); + for (i = 0; swig_const_table[i].name != 0; ++i) + SwigPyBuiltin_AddPublicSymbol(public_interface, swig_const_table[i].name); +#endif + + SWIG_InstallConstants(d,swig_const_table); + + +#ifdef Py_GIL_DISABLED + PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); +#endif + +#if PY_VERSION_HEX >= 0x03000000 + return m; +#else + return; +#endif +} + diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk-2.42.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/sentry_sdk-2.42.0.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk-2.42.0.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk-2.42.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/sentry_sdk-2.42.0.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..c9fda0207b1f2eea4c6d12139cee68efa781c4ac --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk-2.42.0.dist-info/METADATA @@ -0,0 +1,261 @@ +Metadata-Version: 2.4 +Name: sentry-sdk +Version: 2.42.0 +Summary: Python client for Sentry (https://sentry.io) +Home-page: https://github.com/getsentry/sentry-python +Author: Sentry Team and Contributors +Author-email: hello@sentry.io +License: MIT +Project-URL: Documentation, https://docs.sentry.io/platforms/python/ +Project-URL: Changelog, https://github.com/getsentry/sentry-python/blob/master/CHANGELOG.md +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.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=3.6 +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: urllib3>=1.26.11 +Requires-Dist: certifi +Provides-Extra: aiohttp +Requires-Dist: aiohttp>=3.5; extra == "aiohttp" +Provides-Extra: anthropic +Requires-Dist: anthropic>=0.16; extra == "anthropic" +Provides-Extra: arq +Requires-Dist: arq>=0.23; extra == "arq" +Provides-Extra: asyncpg +Requires-Dist: asyncpg>=0.23; extra == "asyncpg" +Provides-Extra: beam +Requires-Dist: apache-beam>=2.12; extra == "beam" +Provides-Extra: bottle +Requires-Dist: bottle>=0.12.13; extra == "bottle" +Provides-Extra: celery +Requires-Dist: celery>=3; extra == "celery" +Provides-Extra: celery-redbeat +Requires-Dist: celery-redbeat>=2; extra == "celery-redbeat" +Provides-Extra: chalice +Requires-Dist: chalice>=1.16.0; extra == "chalice" +Provides-Extra: clickhouse-driver +Requires-Dist: clickhouse-driver>=0.2.0; extra == "clickhouse-driver" +Provides-Extra: django +Requires-Dist: django>=1.8; extra == "django" +Provides-Extra: falcon +Requires-Dist: falcon>=1.4; extra == "falcon" +Provides-Extra: fastapi +Requires-Dist: fastapi>=0.79.0; extra == "fastapi" +Provides-Extra: flask +Requires-Dist: flask>=0.11; extra == "flask" +Requires-Dist: blinker>=1.1; extra == "flask" +Requires-Dist: markupsafe; extra == "flask" +Provides-Extra: grpcio +Requires-Dist: grpcio>=1.21.1; extra == "grpcio" +Requires-Dist: protobuf>=3.8.0; extra == "grpcio" +Provides-Extra: http2 +Requires-Dist: httpcore[http2]==1.*; extra == "http2" +Provides-Extra: httpx +Requires-Dist: httpx>=0.16.0; extra == "httpx" +Provides-Extra: huey +Requires-Dist: huey>=2; extra == "huey" +Provides-Extra: huggingface-hub +Requires-Dist: huggingface_hub>=0.22; extra == "huggingface-hub" +Provides-Extra: langchain +Requires-Dist: langchain>=0.0.210; extra == "langchain" +Provides-Extra: langgraph +Requires-Dist: langgraph>=0.6.6; extra == "langgraph" +Provides-Extra: launchdarkly +Requires-Dist: launchdarkly-server-sdk>=9.8.0; extra == "launchdarkly" +Provides-Extra: litellm +Requires-Dist: litellm>=1.77.5; extra == "litellm" +Provides-Extra: litestar +Requires-Dist: litestar>=2.0.0; extra == "litestar" +Provides-Extra: loguru +Requires-Dist: loguru>=0.5; extra == "loguru" +Provides-Extra: openai +Requires-Dist: openai>=1.0.0; extra == "openai" +Requires-Dist: tiktoken>=0.3.0; extra == "openai" +Provides-Extra: openfeature +Requires-Dist: openfeature-sdk>=0.7.1; extra == "openfeature" +Provides-Extra: opentelemetry +Requires-Dist: opentelemetry-distro>=0.35b0; extra == "opentelemetry" +Provides-Extra: opentelemetry-experimental +Requires-Dist: opentelemetry-distro; extra == "opentelemetry-experimental" +Provides-Extra: pure-eval +Requires-Dist: pure_eval; extra == "pure-eval" +Requires-Dist: executing; extra == "pure-eval" +Requires-Dist: asttokens; extra == "pure-eval" +Provides-Extra: pymongo +Requires-Dist: pymongo>=3.1; extra == "pymongo" +Provides-Extra: pyspark +Requires-Dist: pyspark>=2.4.4; extra == "pyspark" +Provides-Extra: quart +Requires-Dist: quart>=0.16.1; extra == "quart" +Requires-Dist: blinker>=1.1; extra == "quart" +Provides-Extra: rq +Requires-Dist: rq>=0.6; extra == "rq" +Provides-Extra: sanic +Requires-Dist: sanic>=0.8; extra == "sanic" +Provides-Extra: sqlalchemy +Requires-Dist: sqlalchemy>=1.2; extra == "sqlalchemy" +Provides-Extra: starlette +Requires-Dist: starlette>=0.19.1; extra == "starlette" +Provides-Extra: starlite +Requires-Dist: starlite>=1.48; extra == "starlite" +Provides-Extra: statsig +Requires-Dist: statsig>=0.55.3; extra == "statsig" +Provides-Extra: tornado +Requires-Dist: tornado>=6; extra == "tornado" +Provides-Extra: unleash +Requires-Dist: UnleashClient>=6.0.1; extra == "unleash" +Provides-Extra: google-genai +Requires-Dist: google-genai>=1.29.0; extra == "google-genai" +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: description-content-type +Dynamic: home-page +Dynamic: license +Dynamic: license-file +Dynamic: project-url +Dynamic: provides-extra +Dynamic: requires-dist +Dynamic: requires-python +Dynamic: summary + + + Sentry for Python + +
+ +_Bad software is everywhere, and we're tired of it. Sentry is on a mission to help developers write better software faster, so we can get back to enjoying technology. If you want to join us +[**Check out our open positions**](https://sentry.io/careers/)_. + +[![Discord](https://img.shields.io/discord/621778831602221064?logo=discord&labelColor=%20%235462eb&logoColor=%20%23f5f5f5&color=%20%235462eb)](https://discord.com/invite/Ww9hbqr) +[![Twitter Follow](https://img.shields.io/twitter/follow/getsentry?label=@getsentry&style=social)](https://twitter.com/intent/follow?screen_name=getsentry) +[![PyPi page link -- version](https://img.shields.io/pypi/v/sentry-sdk.svg)](https://pypi.python.org/pypi/sentry-sdk) +python +[![Build Status](https://github.com/getsentry/sentry-python/actions/workflows/ci.yml/badge.svg)](https://github.com/getsentry/sentry-python/actions/workflows/ci.yml) + +
+ +
+ + +# Official Sentry SDK for Python + +Welcome to the official Python SDK for **[Sentry](http://sentry.io/)**. + + +## 📦 Getting Started + +### Prerequisites + +You need a Sentry [account](https://sentry.io/signup/) and [project](https://docs.sentry.io/product/projects/). + +### Installation + +Getting Sentry into your project is straightforward. Just run this command in your terminal: + +```bash +pip install --upgrade sentry-sdk +``` + +### Basic Configuration + +Here's a quick configuration example to get Sentry up and running: + +```python +import sentry_sdk + +sentry_sdk.init( + "https://12927b5f211046b575ee51fd8b1ac34f@o1.ingest.sentry.io/1", # Your DSN here + + # Set traces_sample_rate to 1.0 to capture 100% + # of traces for performance monitoring. + traces_sample_rate=1.0, +) +``` + +With this configuration, Sentry will monitor for exceptions and performance issues. + +### Quick Usage Example + +To generate some events that will show up in Sentry, you can log messages or capture errors: + +```python +import sentry_sdk +sentry_sdk.init(...) # same as above + +sentry_sdk.capture_message("Hello Sentry!") # You'll see this in your Sentry dashboard. + +raise ValueError("Oops, something went wrong!") # This will create an error event in Sentry. +``` + + +## 📚 Documentation + +For more details on advanced usage, integrations, and customization, check out the full documentation on [https://docs.sentry.io](https://docs.sentry.io/). + + +## 🧩 Integrations + +Sentry integrates with a ton of popular Python libraries and frameworks, including [FastAPI](https://docs.sentry.io/platforms/python/integrations/fastapi/), [Django](https://docs.sentry.io/platforms/python/integrations/django/), [Celery](https://docs.sentry.io/platforms/python/integrations/celery/), [OpenAI](https://docs.sentry.io/platforms/python/integrations/openai/) and many, many more. Check out the [full list of integrations](https://docs.sentry.io/platforms/python/integrations/) to get the full picture. + + +## 🚧 Migrating Between Versions? + +### From `1.x` to `2.x` + +If you're using the older `1.x` version of the SDK, now's the time to upgrade to `2.x`. It includes significant upgrades and new features. Check our [migration guide](https://docs.sentry.io/platforms/python/migration/1.x-to-2.x) for assistance. + +### From `raven-python` + +Using the legacy `raven-python` client? It's now in maintenance mode, and we recommend migrating to the new SDK for an improved experience. Get all the details in our [migration guide](https://docs.sentry.io/platforms/python/migration/raven-to-sentry-sdk/). + + +## 🙌 Want to Contribute? + +We'd love your help in improving the Sentry SDK! Whether it's fixing bugs, adding features, writing new integrations, or enhancing documentation, every contribution is valuable. + +For details on how to contribute, please read our [contribution guide](CONTRIBUTING.md) and explore the [open issues](https://github.com/getsentry/sentry-python/issues). + + +## 🛟 Need Help? + +If you encounter issues or need help setting up or configuring the SDK, don't hesitate to reach out to the [Sentry Community on Discord](https://discord.com/invite/Ww9hbqr). There is a ton of great people there ready to help! + + +## 🔗 Resources + +Here are all resources to help you make the most of Sentry: + +- [Documentation](https://docs.sentry.io/platforms/python/) - Official documentation to get started. +- [Discord](https://discord.com/invite/Ww9hbqr) - Join our Discord community. +- [X/Twitter](https://twitter.com/intent/follow?screen_name=getsentry) - Follow us on X (Twitter) for updates. +- [Stack Overflow](https://stackoverflow.com/questions/tagged/sentry) - Questions and answers related to Sentry. + + +## 📃 License + +The SDK is open-source and available under the MIT license. Check out the [LICENSE](LICENSE) file for more information. + + +## 😘 Contributors + +Thanks to everyone who has helped improve the SDK! + + + + diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk-2.42.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/sentry_sdk-2.42.0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..3152251d3055e4a2721a8e097f411c2cda1f352c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk-2.42.0.dist-info/RECORD @@ -0,0 +1,176 @@ +sentry_sdk-2.42.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +sentry_sdk-2.42.0.dist-info/METADATA,sha256=rsHFGPELP7QwfoPHI1KKqVwK_wBxppb2w3Zd6-S4E68,10523 +sentry_sdk-2.42.0.dist-info/RECORD,, +sentry_sdk-2.42.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sentry_sdk-2.42.0.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109 +sentry_sdk-2.42.0.dist-info/entry_points.txt,sha256=qacZEz40UspQZD1IukCXykx0JtImqGDOctS5KfOLTko,91 +sentry_sdk-2.42.0.dist-info/licenses/LICENSE,sha256=KhQNZg9GKBL6KQvHQNBGMxJsXsRdhLebVp4Sew7t3Qs,1093 +sentry_sdk-2.42.0.dist-info/top_level.txt,sha256=XrQz30XE9FKXSY_yGLrd9bsv2Rk390GTDJOSujYaMxI,11 +sentry_sdk/__init__.py,sha256=-jRAO-EG4LBj5L20sK01fdMlbtvGf_idy54Eb46EiKQ,1364 +sentry_sdk/_compat.py,sha256=Pxcg6cUYPiOoXIFfLI_H3ATb7SfrcXOeZdzpeWv3umI,3116 +sentry_sdk/_init_implementation.py,sha256=WL54d8nggjRunBm3XlG-sWSx4yS5lpYYggd7YBWpuVk,2559 +sentry_sdk/_log_batcher.py,sha256=bBpspIlf1ejxlbudo17bZOSir226LGAdjDe_3kHkOro,5085 +sentry_sdk/_lru_cache.py,sha256=phZMBm9EKU1m67OOApnKCffnlWAlVz9bYjig7CglQuk,1229 +sentry_sdk/_metrics.py,sha256=ov1aCqPvcmnDba43HHjWT2flqNPfA5Fa0O0iopcnZpI,2044 +sentry_sdk/_metrics_batcher.py,sha256=1W7nmijIsiFAsNfg2jdw6Lm4mwlAFFSnx_Oc2zQmASc,4612 +sentry_sdk/_queue.py,sha256=UUzbmliDYmdVYiDA32NMYkX369ElWMFNSj5kodqVQZE,11250 +sentry_sdk/_types.py,sha256=ld5Y0yMsLxd6P6tPifw3IqUg8bpFE0hgxPDUmn6B9Xg,10422 +sentry_sdk/_werkzeug.py,sha256=m3GPf-jHd8v3eVOfBHaKw5f0uHoLkXrSO1EcY-8EisY,3734 +sentry_sdk/ai/__init__.py,sha256=I7GRMQEYTV0bXzEIE0iAJjAn6Te-dPM5uGyJ9KS-6hI,193 +sentry_sdk/ai/monitoring.py,sha256=bS_KneWCAL9ehml5XiyficoPVx4DUUG6acbH3cjP3I8,5057 +sentry_sdk/ai/utils.py,sha256=StHk8b34APp6mHGp9oPD7aPRROKXJv5KqM8h8c4Hv9s,3270 +sentry_sdk/api.py,sha256=OkwQ2tA5YASJ77wLOteUdv_woPF4wL_JTOAMxe9z8k4,15282 +sentry_sdk/attachments.py,sha256=0Dylhm065O6hNFjB40fWCd5Hg4qWSXndmi1TPWglZkI,3109 +sentry_sdk/client.py,sha256=ilR4V9_m7tknWlMK9Czq9lHn7ccuU7lfu6B4l_oRTFk,40520 +sentry_sdk/consts.py,sha256=q1vCuHWbIGLWmwemLk6cVXqM4WZFnKOGRNfvY2IXE2g,51058 +sentry_sdk/crons/__init__.py,sha256=3Zt6g1-pZZ12uRKKsC8QLm3XgJ4K1VYxgVpNNUygOZY,221 +sentry_sdk/crons/api.py,sha256=mk-UB8Im2LU2rJFdE-TV302EaKnf8kAjwEL0bIV0Hzc,1767 +sentry_sdk/crons/consts.py,sha256=dXqJk5meBSu5rjlGpqAOlkpACnuUi7svQnAFoy1ZNUU,87 +sentry_sdk/crons/decorator.py,sha256=UrjeIqBCbvsuKrfjGkKJbbLBvjw2TQvDWcTO7WwAmrI,3913 +sentry_sdk/debug.py,sha256=ddBehQlAuQC1sg1XO-N4N3diZ0x0iT5RWJwFdrtcsjw,1019 +sentry_sdk/envelope.py,sha256=1nqp_DMw66MYtrszRiiCuodyi3JKcOiQodEMkD6uZ_c,10473 +sentry_sdk/feature_flags.py,sha256=savtmWAHjAvgw2m_KWW8mUagjLhAXCm3jjscmBlfIJ4,2232 +sentry_sdk/hub.py,sha256=jg7UqYiJFJ1dknVCNT4_E5lIBfrCFQdWwnJrhgScVNg,25748 +sentry_sdk/integrations/__init__.py,sha256=c-0q-kzVEKt_2-bk2X4f4LLXnykTmldcjo98dEQmPms,10442 +sentry_sdk/integrations/_asgi_common.py,sha256=Ypg7IctB3iPPY60ebVlzChzgT8GeGpZ0YH8VvJNDlEY,3187 +sentry_sdk/integrations/_wsgi_common.py,sha256=A1-X7l1pZCcrbUhRHkmdKiK_EemEZjn7xToJIvlEuFM,7558 +sentry_sdk/integrations/aiohttp.py,sha256=FynazdaPWCanC91KKVba8yl0UwWnVJcJxWNPzSu64x0,13007 +sentry_sdk/integrations/anthropic.py,sha256=AeGNc8WGXhqtSk9oZcxcEAp1lRvQT16i6HOKUGfat2M,13935 +sentry_sdk/integrations/argv.py,sha256=GIY7TBFETF8Z0fDzqTXEJldt5XXCDdFNZxpGxP7EPaU,911 +sentry_sdk/integrations/ariadne.py,sha256=C-zKlOrU7jvTWmQHZx0M0tAZNkPPo7Z5-5jXDD92LiU,5834 +sentry_sdk/integrations/arq.py,sha256=yDPdWJa3ZgnGLwFzavIylIafEVN0qqSSgL4kUHxQF70,7881 +sentry_sdk/integrations/asgi.py,sha256=b3zE8Om_yP7SDxcD8MMCdDthYhLLgOB3LC3ZZCf94hw,12800 +sentry_sdk/integrations/asyncio.py,sha256=DEoXAwk8oVl_1Sbmm2TthpruaLO7p4WZBTh9K-mch_g,4136 +sentry_sdk/integrations/asyncpg.py,sha256=fbBTi5bEERK3c9o43LBLtS5wPaSVq_qIl3Y50NPmr5Y,6521 +sentry_sdk/integrations/atexit.py,sha256=sY46N2hEvtGuT1DBQhirUXHbjgXjXAm7R_sgiectVKw,1652 +sentry_sdk/integrations/aws_lambda.py,sha256=WveHWnB_nBsnfLTbaUxih79Ra3Qjv4Jjh-7m2v-gSJs,17954 +sentry_sdk/integrations/beam.py,sha256=qt35UmkA0ng4VNzmwqH9oz7SESU-is9IjFbTJ21ad4U,5182 +sentry_sdk/integrations/boto3.py,sha256=1ItKUX7EL9MHXS1H8VSn6IfZSFLeqaUqeWg-OKBm_Ik,4411 +sentry_sdk/integrations/bottle.py,sha256=aC5OsitlsRUEWBlpkNjxvH0m6UEG3OfAJ9jFyPCbzqQ,6615 +sentry_sdk/integrations/celery/__init__.py,sha256=FNmrLL0Cs95kv6yUCpJGu9X8Cpw20mMYGmnkBC4IL4Y,18699 +sentry_sdk/integrations/celery/beat.py,sha256=WHEdKetrDJgtZGNp1VUMa6BG1q-MhsLZMefUmVrPu3w,8953 +sentry_sdk/integrations/celery/utils.py,sha256=CMWQOpg9yniEkm3WlXe7YakJfVnLwaY0-jyeo2GX-ZI,1208 +sentry_sdk/integrations/chalice.py,sha256=A4K_9FmNUu131El0ctkTmjtyYd184I4hQTlidZcEC54,4699 +sentry_sdk/integrations/clickhouse_driver.py,sha256=2qpRznwSNuRSzrCA1R5bmpgiehDmzbG7yZe6hN-61Wg,6098 +sentry_sdk/integrations/cloud_resource_context.py,sha256=_gFldMeVHs5pxP5sm8uP7ZKmm6s_5hw3UsnXek9Iw8A,7780 +sentry_sdk/integrations/cohere.py,sha256=VI5mLukp_CQDt-OFVNgtVqpbY-j-d5UOfD7iPBKu0FU,9401 +sentry_sdk/integrations/dedupe.py,sha256=FMdGQOlL86r4AjiSf4MfzyfEIjkI5b_v3Ko2gOZKkBE,1975 +sentry_sdk/integrations/django/__init__.py,sha256=KqAgBKkuyJGw0lqNZBj0otqZGy_YHqPsisgPZLCN8mQ,25247 +sentry_sdk/integrations/django/asgi.py,sha256=R5wQYS6HAaSM9rmO5bnTHNt6pClthM6LsrgSioTSZSM,8349 +sentry_sdk/integrations/django/caching.py,sha256=UvYaiI7xrN08Se59vMgJWrSO2BuowOyx3jmXmZoxQJo,6427 +sentry_sdk/integrations/django/middleware.py,sha256=UVKq134w_TyOVPV7WwBW0QjHY-ziDipcZBIDQmjqceE,6009 +sentry_sdk/integrations/django/signals_handlers.py,sha256=iudWetTlzNr5-kx_ew1YwW_vZ0yDChoonwPZB7AYGPo,3098 +sentry_sdk/integrations/django/templates.py,sha256=k3PQrNICGS4wqmFxK3o8KwOlqip7rSIryyc4oa1Wexc,5725 +sentry_sdk/integrations/django/transactions.py,sha256=Axyh3l4UvM96R3go2anVhew3JbrEZ4FSYd1r3UXEcw4,4951 +sentry_sdk/integrations/django/views.py,sha256=bjHwt6TVfYY7yfGUa2Rat9yowkUbQ2bYCcJaXJxP2Ik,3137 +sentry_sdk/integrations/dramatiq.py,sha256=P0j732DU4pkUQi_CRtY4DxvhcalXN8jQVX6gdHl6yPw,7455 +sentry_sdk/integrations/excepthook.py,sha256=tfwpSQuo1b_OmJbNKPPRh90EUjD_OSE4DqqgYY9PVQI,2408 +sentry_sdk/integrations/executing.py,sha256=5lxBAxO5FypY-zTV03AHncGmolmaHd327-3Vrjzskcc,1994 +sentry_sdk/integrations/falcon.py,sha256=uhjqFPKa8bWRQr0za4pVXGYaPr-LRdICw2rUO-laKCo,9501 +sentry_sdk/integrations/fastapi.py,sha256=kicdigHM3MG-GlpLUN6wwH8jOVu4dTuoQD6RBFrlXgo,4589 +sentry_sdk/integrations/flask.py,sha256=t7q73JoJT46RWDtrNImtIloGyDg7CnsNFKpS4gOuBIw,8740 +sentry_sdk/integrations/gcp.py,sha256=u1rSi3nK2ISUQqkRnmKFG23Ks-SefshTf5PV0Dwp3_4,8274 +sentry_sdk/integrations/gnu_backtrace.py,sha256=FL7WkRfHT6idYCSLIrtFQ2G5ZTGoYudCKvBcjR5hqsI,2812 +sentry_sdk/integrations/google_genai/__init__.py,sha256=BeE4uK8CjwU1qC3_CDEahSVZNpxMDu78o2IjUCbNDIQ,11208 +sentry_sdk/integrations/google_genai/consts.py,sha256=nqHKKSyGixrSoozA06BGVBFaUCsvZlvGoubUZGI1kB8,559 +sentry_sdk/integrations/google_genai/streaming.py,sha256=cRRbVD2Xv3ncoS2ICYhoPGVpHaOK_HHjXhCIij9Kos0,5191 +sentry_sdk/integrations/google_genai/utils.py,sha256=6CB4DI0qXjyH2QXnzss19DhAv4HSc9fpS5d-63MWskk,19679 +sentry_sdk/integrations/gql.py,sha256=lN5LJNZwUHs_1BQcIrR0adIkgb9YiOh6UtoUG8vCO_Y,4801 +sentry_sdk/integrations/graphene.py,sha256=I6ZJ8Apd9dO9XPVvZY7I46-v1eXOW1C1rAkWwasF3gU,5042 +sentry_sdk/integrations/grpc/__init__.py,sha256=zukyRYtaxRGcDuQSXBbVcpa7ZMAYdLQ-laRQqqHsHgc,5620 +sentry_sdk/integrations/grpc/aio/__init__.py,sha256=2rgrliowpPfLLw40_2YU6ixSzIu_3f8NN3TRplzc8S8,141 +sentry_sdk/integrations/grpc/aio/client.py,sha256=3zfF3XkpzR717BpY1ehxi72jDUvT8Xntx8vkD78kCXc,3327 +sentry_sdk/integrations/grpc/aio/server.py,sha256=SCkdikPZRdWyrlnZewsSGpPk4v6AsdSApVAbO-lf_Lk,4019 +sentry_sdk/integrations/grpc/client.py,sha256=4MCY24tqZAU6OzNC_0pphyCLnR_SrfBC-xh8Kb-i8LU,3373 +sentry_sdk/integrations/grpc/consts.py,sha256=NpsN5gKWDmtGtVK_L5HscgFZBHqjOpmLJLGKyh8GZBA,31 +sentry_sdk/integrations/grpc/server.py,sha256=oo79zjfGaJtCSwtxaJeCFRA6UWoH1PDzjR6SDMtt398,2474 +sentry_sdk/integrations/httpx.py,sha256=HK0Nbxc4TAFesTz6gegz6tAHcIXKlOckFWrBHMBB0VM,6086 +sentry_sdk/integrations/huey.py,sha256=wlyxjeWqqJp1X5S3neD5FiZjXcyznm1dl8_u1wIo76U,5443 +sentry_sdk/integrations/huggingface_hub.py,sha256=B5z0--bC2tEDtWl5V7xAqM4234yhY_RYbnkZGgqC8PA,14952 +sentry_sdk/integrations/langchain.py,sha256=WeqF7F0HZKR8Fra4RAivgxtxt8BsgMxua6r4n-sLX84,30302 +sentry_sdk/integrations/langgraph.py,sha256=3wzDDwHVmuxF1vEiVoJqyc7r7hqK9VJoOmdgKNwbcE0,11238 +sentry_sdk/integrations/launchdarkly.py,sha256=L5yE9NBRon8JPYzO6XT-dA4YkvNcrUfK4nD5fycSXM0,1934 +sentry_sdk/integrations/litellm.py,sha256=WCwjsIZBxJ2Rk2yAWJBUxyNehgO6B37a_X2JJcvM4OY,8858 +sentry_sdk/integrations/litestar.py,sha256=0BkfynHkxERshbxycwHDnpjzGx31c5ipYvBYqprAoHY,11830 +sentry_sdk/integrations/logging.py,sha256=L1f3dej3Zdn9wyB5_mzvzyk4bF-HvFFmhGegfBfMPnA,13892 +sentry_sdk/integrations/loguru.py,sha256=JmIiVnkjbEzb8dRsFln4T7Ft_GULyXEt7w5t-p5Zdt4,6202 +sentry_sdk/integrations/modules.py,sha256=vzLx3Erg77Vl4mnUvAgTg_3teAuWy7zylFpAidBI9I0,820 +sentry_sdk/integrations/openai.py,sha256=zvsW4-ypH_n4B2EIYYQ3gheJDzNOxZRgr5ApPrzCOE8,24160 +sentry_sdk/integrations/openai_agents/__init__.py,sha256=-ydqG0sFIrvJlT9JHO58EZpCAzyy9J59Av8dxn0fHuw,1424 +sentry_sdk/integrations/openai_agents/consts.py,sha256=PTb3vlqkuMPktu21ALK72o5WMIX4-cewTEiTRdHKFdQ,38 +sentry_sdk/integrations/openai_agents/patches/__init__.py,sha256=I7C9JZ70Mf8PV3wPdFsxTqvcYl4TYUgSZYfNU2Spb7Y,231 +sentry_sdk/integrations/openai_agents/patches/agent_run.py,sha256=GPBV-j8YnHOrJAhdhu_tphe14z7G0-riFVmjFNDgy0s,5773 +sentry_sdk/integrations/openai_agents/patches/models.py,sha256=DtwqCmSsYFlhRZquKM2jiTOnnAg97eyCTtJYZkWqdww,1405 +sentry_sdk/integrations/openai_agents/patches/runner.py,sha256=Fr5tflgadu3fnEThSZauAhrT7BbvemuZelDVGZjleqA,1483 +sentry_sdk/integrations/openai_agents/patches/tools.py,sha256=uAx1GgsiDJBP7jpYW8r_kOImdgzXlwYqK-uhkyP3icI,3255 +sentry_sdk/integrations/openai_agents/spans/__init__.py,sha256=RlVi781zGsvCJBciDO_EbBbwkakwbP9DoFQBbo4VAEE,353 +sentry_sdk/integrations/openai_agents/spans/agent_workflow.py,sha256=fdRSThD31TcoMXFg-2vmqK2YcSws8Yhd0oC6fxOnysM,469 +sentry_sdk/integrations/openai_agents/spans/ai_client.py,sha256=gCZrl1vpBmf8vzDTSLCvoZg-x_TvMUFTLOxh4Vy_4OY,1292 +sentry_sdk/integrations/openai_agents/spans/execute_tool.py,sha256=tqtDIzaxhxJUE-XEvm2dSyJV65UXJ7Mr23C6NTt6ZJE,1411 +sentry_sdk/integrations/openai_agents/spans/handoff.py,sha256=MBhzy7MpvPGwQTPT5TFcOnmSPiSH_uadQ5wvksueIik,525 +sentry_sdk/integrations/openai_agents/spans/invoke_agent.py,sha256=AXiXuJjcvgj_M6NWxHg-OVWld7-nb8VQe3Iq1IEIxOk,2493 +sentry_sdk/integrations/openai_agents/utils.py,sha256=fa3r6iHLjTtrU2dHM_7D_0lDQAHR3CUSutIa6Wf7efg,6808 +sentry_sdk/integrations/openfeature.py,sha256=-vvdrN4fK0Xhu2ip41bkPIPEqdzv8xzmLu9wRlI2xPA,1131 +sentry_sdk/integrations/opentelemetry/__init__.py,sha256=emNL5aAq_NhK0PZmfX_g4GIdvBS6nHqGrjrIgrdC5m8,229 +sentry_sdk/integrations/opentelemetry/consts.py,sha256=fYL6FIAEfnGZGBhFn5X7aRyHxihSPqAKKqMLhf5Gniw,143 +sentry_sdk/integrations/opentelemetry/integration.py,sha256=CWp6hFFMqoR7wcuwTRbRO-1iVch4A6oOB3RuHWeX9GQ,1791 +sentry_sdk/integrations/opentelemetry/propagator.py,sha256=NpCgv2Ibq1LUrv8-URayZaPGSzz0f1tJsf7aaxAZ5pc,3720 +sentry_sdk/integrations/opentelemetry/span_processor.py,sha256=IBF75ld9zJLNF1-4EYnNBoAS00_XTXjPio86zPX9DLQ,13276 +sentry_sdk/integrations/pure_eval.py,sha256=R2UuFCtQ_ShTIZJKPn9RUD06lbc6mug4Mv8S1_mo1j0,4605 +sentry_sdk/integrations/pymongo.py,sha256=cPpMGEbXHlV6HTHgmIDL1F-x3w7ZMROXVb4eUhLs3bw,6380 +sentry_sdk/integrations/pyramid.py,sha256=IDonzoZvLrH18JL-i_Qpbztc4T3iZNQhWFFv6SAXac8,7364 +sentry_sdk/integrations/quart.py,sha256=7h4BuGNWzZabVIIOKm194gMKDlIvS-dmWFW4iZXsmF4,7413 +sentry_sdk/integrations/ray.py,sha256=gzyjDqc9THINu0R5FCYa-f8YPA5cS6XE0JFR-G5RwpQ,5323 +sentry_sdk/integrations/redis/__init__.py,sha256=As5XhbOue-9Sy9d8Vr8cZagbO_Bc0uG8n2G3YNMP7TU,1332 +sentry_sdk/integrations/redis/_async_common.py,sha256=A-23KY7JkkZ8g6FufnGo6IHK7Ln-jtZmopVH5WhqdkE,4056 +sentry_sdk/integrations/redis/_sync_common.py,sha256=MS5Bc94cqispn4ZM-WSH02GrgnB6chvrnf0JBabTNMU,3796 +sentry_sdk/integrations/redis/consts.py,sha256=jYhloX935YQ1AR9c8giCVo1FpIuGXkGR_Tfn4LOulNU,480 +sentry_sdk/integrations/redis/modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sentry_sdk/integrations/redis/modules/caches.py,sha256=eY8XY4Nk3QsMM0T26OOYdcNr4bN0Sp9325HkH-hO8cg,4063 +sentry_sdk/integrations/redis/modules/queries.py,sha256=0GxZ98wyjqcc4CwPG3xJ4bSGIGW8wPXChSk5Fxm6kYg,2035 +sentry_sdk/integrations/redis/rb.py,sha256=paykO7EE_DAdiZzCpIqW1MqtBE7mE5UG0JnauFejuzE,806 +sentry_sdk/integrations/redis/redis.py,sha256=1K6seuP6ttEdscKLFtEYEu9vkDRuANCsxWVeDISsGsg,1702 +sentry_sdk/integrations/redis/redis_cluster.py,sha256=a5F5PglAm87b-aW08RUv41zYGYliWZgcM3wrGB_mF-s,3554 +sentry_sdk/integrations/redis/redis_py_cluster_legacy.py,sha256=pz5pg0AxdHPZWt0jMQRDPH_9jdh0i3KoDPbNUyavIro,1585 +sentry_sdk/integrations/redis/utils.py,sha256=j1yBJyogaxoLxBq8nLkRAqzSt-EbdtHoO-9zZTW_WXw,3970 +sentry_sdk/integrations/rq.py,sha256=2Cidur0yL_JtdpOtBup6D6aYyN4T9mgshebEc-kvp-E,5307 +sentry_sdk/integrations/rust_tracing.py,sha256=fQ0eG09w3IPZe8ecgeUoQTPoGFThkkarUyGC8KJj35o,9078 +sentry_sdk/integrations/sanic.py,sha256=Z7orxkX9YhU9YSX4Oidsi3n46J0qlVG7Ajog-fnUreo,12960 +sentry_sdk/integrations/serverless.py,sha256=npiKJuIy_sEkWT_x0Eu2xSEMiMh_aySqGYlnvIROsYk,1804 +sentry_sdk/integrations/socket.py,sha256=hlJDYlspzOy3UNjsd7qXPUoqJl5s1ShF3iijTRWpVaU,3169 +sentry_sdk/integrations/spark/__init__.py,sha256=oOewMErnZk2rzNvIlZO6URxQexu9bUJuSLM2m_zECy8,208 +sentry_sdk/integrations/spark/spark_driver.py,sha256=hInLM2dO5yPxQT9Wb5gvHIKkbbA1i84LBsx416Dv-6c,9474 +sentry_sdk/integrations/spark/spark_worker.py,sha256=FGT4yRU2X_iQCC46aasMmvJfYOKmBip8KbDF_wnhvEY,3706 +sentry_sdk/integrations/sqlalchemy.py,sha256=rzOK3yFLrRE3V7q-wVcAUhq5iSTrqGPW5ytbGU9lXkk,4344 +sentry_sdk/integrations/starlette.py,sha256=oHuzJXRWnCgD22Q9_JHfuD2OSW7gIt_wwA-TTwJ_2ng,26235 +sentry_sdk/integrations/starlite.py,sha256=hSiVB6KZr8pxsQVRSGGP-9UQBLsBl-3DmrK_5CPebB8,10559 +sentry_sdk/integrations/statsig.py,sha256=-e57hxHfHo1S13YQKObV65q_UvREyxbR56fnf7bkC9o,1227 +sentry_sdk/integrations/stdlib.py,sha256=4EeLQeU3yjPyjPORX6K0B5N8D5ZXT3eDAFIwjDckUj8,8968 +sentry_sdk/integrations/strawberry.py,sha256=u7Lk4u3sNEycdSmY1nQBzYKmqI-mO8BWKAAJkCSuTRA,14126 +sentry_sdk/integrations/sys_exit.py,sha256=AwShgGBWPdiY25aOWDLRAs2RBUKm5T3CrL-Q-zAk0l4,2493 +sentry_sdk/integrations/threading.py,sha256=0lNxcMLN7Z5DwLg9d1Of7lgGcMOggsM76bU35hIOGXA,7109 +sentry_sdk/integrations/tornado.py,sha256=Qcft8FZxdVICnaa1AhsDB262sInEQZPf-pvgI-Agjmc,7206 +sentry_sdk/integrations/trytond.py,sha256=BaLCNqQeRWDbHHDEelS5tmj-p_CrbmtGEHIn6JfzEFE,1651 +sentry_sdk/integrations/typer.py,sha256=FQrFgpR9t6yQWF-oWCI9KJLFioEnA2c_1BEtYV-mPAs,1815 +sentry_sdk/integrations/unleash.py,sha256=6JshqyuAY_kbu9Nr20tMOhtgx-ryqPHCrq_EQIzeqm4,1058 +sentry_sdk/integrations/unraisablehook.py,sha256=8IW8Ia9t2hKgPLh8WS8WkmeAsyjJ6Al4qi8sM6vGrpU,1753 +sentry_sdk/integrations/wsgi.py,sha256=M3lExlLqQ_J-vEZPmv5HI7vRf8T7DvPicbNZiypUvmE,10809 +sentry_sdk/logger.py,sha256=6tD1sQq3NKAIRgTjRSJyxEvDZMeShj5aUfMhY9hLYIE,2458 +sentry_sdk/monitor.py,sha256=52CG1m2e8okFDVoTpbqfm9zeeaLa0ciC_r9x2RiXuDg,3639 +sentry_sdk/profiler/__init__.py,sha256=3PI3bHk9RSkkOXZKN84DDedk_7M65EiqqaIGo-DYs0E,1291 +sentry_sdk/profiler/continuous_profiler.py,sha256=7Qb75TaKLNYxMA97wO-qEpDVqxPQWOLUi2rnUm6_Ci0,23066 +sentry_sdk/profiler/transaction_profiler.py,sha256=e3MsUqs-YIp6-nmzpmBYGoWWIF7RyuSGu24Dj-8GXAU,27970 +sentry_sdk/profiler/utils.py,sha256=80MF0wiguKe47O-uWQfl-81G1caiVa8HgcFHWBzFpuM,6492 +sentry_sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sentry_sdk/scope.py,sha256=gTdGB0eUvjS1TMKvRHckB5AJnBGFGpwps8uPh--KI8k,63934 +sentry_sdk/scrubber.py,sha256=rENmQ35buugDl269bRZuIAtgr27B9SzisJYUF-691pc,6064 +sentry_sdk/serializer.py,sha256=0-WdtKYwmGEM7nVxmOeRAXjC0DVdIHCymQpRuIHDBX0,13534 +sentry_sdk/session.py,sha256=BXWHf5Opg9yx7jKe-_iHxF6LDJw9Jnu7NfHxo3UQRpw,5589 +sentry_sdk/sessions.py,sha256=e7Jv8McW3QZp3H1GuI_CA_ezq_G0ZWY6nK0ZLqJRdNI,9172 +sentry_sdk/spotlight.py,sha256=93kdd8KxdLfcPaxFnFuqHgYAAL4FCfpK1hiiPoD7Ac4,8678 +sentry_sdk/tracing.py,sha256=lJG5TmA7mz7-RfJEr34ydgBf-lebRegejHkhdNsHH08,51747 +sentry_sdk/tracing_utils.py,sha256=Qq4zPU1wEudCgQyb0nnS4AaTBmCchz6ovghGZEjc91w,40541 +sentry_sdk/transport.py,sha256=NzlBS5liRSh0Fm7Zi7sPdZG82uECw9myECs_JrClqkg,31878 +sentry_sdk/types.py,sha256=A92AqvfrGQZ9KY6FaUjKfL9F1HK7Ui3heQilVzfzYCs,1269 +sentry_sdk/utils.py,sha256=ytlUxHsGpnUQgnFBmDoEiFLq0iZ3cxJbPQi88HMniiA,62176 +sentry_sdk/worker.py,sha256=VSMaigRMbInVyupSFpBC42bft2oIViea-0C_d9ThnIo,4464 diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk-2.42.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/sentry_sdk-2.42.0.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk-2.42.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/sentry_sdk-2.42.0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..5f133dbb5cfac001f2e84cda817210c03ce6484e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk-2.42.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk-2.42.0.dist-info/entry_points.txt b/.venv/lib/python3.12/site-packages/sentry_sdk-2.42.0.dist-info/entry_points.txt new file mode 100644 index 0000000000000000000000000000000000000000..4b128a843cbaf61581876070bb4bbe709ce1c0b5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk-2.42.0.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[opentelemetry_propagator] +sentry = sentry_sdk.integrations.opentelemetry:SentryPropagator diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk-2.42.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/sentry_sdk-2.42.0.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..5051901ecb46b7905ec9fcb8dfa5c2711d494b8d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk-2.42.0.dist-info/top_level.txt @@ -0,0 +1 @@ +sentry_sdk diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/__init__.py b/.venv/lib/python3.12/site-packages/sentry_sdk/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1939be0510c7590b0e03bdd9447c0343e7160352 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/__init__.py @@ -0,0 +1,65 @@ +from sentry_sdk import profiler +from sentry_sdk.scope import Scope +from sentry_sdk.transport import Transport, HttpTransport +from sentry_sdk.client import Client + +from sentry_sdk.api import * # noqa +from sentry_sdk.consts import VERSION + +__all__ = [ # noqa + "Hub", + "Scope", + "Client", + "Transport", + "HttpTransport", + "VERSION", + "integrations", + # From sentry_sdk.api + "init", + "add_attachment", + "add_breadcrumb", + "capture_event", + "capture_exception", + "capture_message", + "configure_scope", + "continue_trace", + "flush", + "get_baggage", + "get_client", + "get_global_scope", + "get_isolation_scope", + "get_current_scope", + "get_current_span", + "get_traceparent", + "is_initialized", + "isolation_scope", + "last_event_id", + "new_scope", + "push_scope", + "set_context", + "set_extra", + "set_level", + "set_measurement", + "set_tag", + "set_tags", + "set_user", + "start_span", + "start_transaction", + "trace", + "monitor", + "logger", + "profiler", + "start_session", + "end_session", + "set_transaction_name", + "update_current_span", +] + +# Initialize the debug support after everything is loaded +from sentry_sdk.debug import init_debug_support + +init_debug_support() +del init_debug_support + +# circular imports +from sentry_sdk.hub import Hub diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/_compat.py b/.venv/lib/python3.12/site-packages/sentry_sdk/_compat.py new file mode 100644 index 0000000000000000000000000000000000000000..a811cf212063b6cabe3081eccbb8ce34107c4a83 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/_compat.py @@ -0,0 +1,98 @@ +import sys + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing import Any + from typing import TypeVar + + T = TypeVar("T") + + +PY37 = sys.version_info[0] == 3 and sys.version_info[1] >= 7 +PY38 = sys.version_info[0] == 3 and sys.version_info[1] >= 8 +PY310 = sys.version_info[0] == 3 and sys.version_info[1] >= 10 +PY311 = sys.version_info[0] == 3 and sys.version_info[1] >= 11 + + +def with_metaclass(meta, *bases): + # type: (Any, *Any) -> Any + class MetaClass(type): + def __new__(metacls, name, this_bases, d): + # type: (Any, Any, Any, Any) -> Any + return meta(name, bases, d) + + return type.__new__(MetaClass, "temporary_class", (), {}) + + +def check_uwsgi_thread_support(): + # type: () -> bool + # We check two things here: + # + # 1. uWSGI doesn't run in threaded mode by default -- issue a warning if + # that's the case. + # + # 2. Additionally, if uWSGI is running in preforking mode (default), it needs + # the --py-call-uwsgi-fork-hooks option for the SDK to work properly. This + # is because any background threads spawned before the main process is + # forked are NOT CLEANED UP IN THE CHILDREN BY DEFAULT even if + # --enable-threads is on. One has to explicitly provide + # --py-call-uwsgi-fork-hooks to force uWSGI to run regular cpython + # after-fork hooks that take care of cleaning up stale thread data. + try: + from uwsgi import opt # type: ignore + except ImportError: + return True + + from sentry_sdk.consts import FALSE_VALUES + + def enabled(option): + # type: (str) -> bool + value = opt.get(option, False) + if isinstance(value, bool): + return value + + if isinstance(value, bytes): + try: + value = value.decode() + except Exception: + pass + + return value and str(value).lower() not in FALSE_VALUES + + # When `threads` is passed in as a uwsgi option, + # `enable-threads` is implied on. + threads_enabled = "threads" in opt or enabled("enable-threads") + fork_hooks_on = enabled("py-call-uwsgi-fork-hooks") + lazy_mode = enabled("lazy-apps") or enabled("lazy") + + if lazy_mode and not threads_enabled: + from warnings import warn + + warn( + Warning( + "IMPORTANT: " + "We detected the use of uWSGI without thread support. " + "This might lead to unexpected issues. " + 'Please run uWSGI with "--enable-threads" for full support.' + ) + ) + + return False + + elif not lazy_mode and (not threads_enabled or not fork_hooks_on): + from warnings import warn + + warn( + Warning( + "IMPORTANT: " + "We detected the use of uWSGI in preforking mode without " + "thread support. This might lead to crashing workers. " + 'Please run uWSGI with both "--enable-threads" and ' + '"--py-call-uwsgi-fork-hooks" for full support.' + ) + ) + + return False + + return True diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/_init_implementation.py b/.venv/lib/python3.12/site-packages/sentry_sdk/_init_implementation.py new file mode 100644 index 0000000000000000000000000000000000000000..eb02b3d11e9fa53e6aaa9bdf1c7755936e637c35 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/_init_implementation.py @@ -0,0 +1,84 @@ +import warnings + +from typing import TYPE_CHECKING + +import sentry_sdk + +if TYPE_CHECKING: + from typing import Any, ContextManager, Optional + + import sentry_sdk.consts + + +class _InitGuard: + _CONTEXT_MANAGER_DEPRECATION_WARNING_MESSAGE = ( + "Using the return value of sentry_sdk.init as a context manager " + "and manually calling the __enter__ and __exit__ methods on the " + "return value are deprecated. We are no longer maintaining this " + "functionality, and we will remove it in the next major release." + ) + + def __init__(self, client): + # type: (sentry_sdk.Client) -> None + self._client = client + + def __enter__(self): + # type: () -> _InitGuard + warnings.warn( + self._CONTEXT_MANAGER_DEPRECATION_WARNING_MESSAGE, + stacklevel=2, + category=DeprecationWarning, + ) + + return self + + def __exit__(self, exc_type, exc_value, tb): + # type: (Any, Any, Any) -> None + warnings.warn( + self._CONTEXT_MANAGER_DEPRECATION_WARNING_MESSAGE, + stacklevel=2, + category=DeprecationWarning, + ) + + c = self._client + if c is not None: + c.close() + + +def _check_python_deprecations(): + # type: () -> None + # Since we're likely to deprecate Python versions in the future, I'm keeping + # this handy function around. Use this to detect the Python version used and + # to output logger.warning()s if it's deprecated. + pass + + +def _init(*args, **kwargs): + # type: (*Optional[str], **Any) -> ContextManager[Any] + """Initializes the SDK and optionally integrations. + + This takes the same arguments as the client constructor. + """ + client = sentry_sdk.Client(*args, **kwargs) + sentry_sdk.get_global_scope().set_client(client) + _check_python_deprecations() + rv = _InitGuard(client) + return rv + + +if TYPE_CHECKING: + # Make mypy, PyCharm and other static analyzers think `init` is a type to + # have nicer autocompletion for params. + # + # Use `ClientConstructor` to define the argument types of `init` and + # `ContextManager[Any]` to tell static analyzers about the return type. + + class init(sentry_sdk.consts.ClientConstructor, _InitGuard): # noqa: N801 + pass + +else: + # Alias `init` for actual usage. Go through the lambda indirection to throw + # PyCharm off of the weakly typed signature (it would otherwise discover + # both the weakly typed signature of `_init` and our faked `init` type). + + init = (lambda: _init)() diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/_log_batcher.py b/.venv/lib/python3.12/site-packages/sentry_sdk/_log_batcher.py new file mode 100644 index 0000000000000000000000000000000000000000..87bebdb2265a9551382d39e99d00fccaf1498976 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/_log_batcher.py @@ -0,0 +1,161 @@ +import os +import random +import threading +from datetime import datetime, timezone +from typing import Optional, List, Callable, TYPE_CHECKING, Any + +from sentry_sdk.utils import format_timestamp, safe_repr +from sentry_sdk.envelope import Envelope, Item, PayloadRef + +if TYPE_CHECKING: + from sentry_sdk._types import Log + + +class LogBatcher: + MAX_LOGS_BEFORE_FLUSH = 100 + FLUSH_WAIT_TIME = 5.0 + + def __init__( + self, + capture_func, # type: Callable[[Envelope], None] + ): + # type: (...) -> None + self._log_buffer = [] # type: List[Log] + self._capture_func = capture_func + self._running = True + self._lock = threading.Lock() + + self._flush_event = threading.Event() # type: threading.Event + + self._flusher = None # type: Optional[threading.Thread] + self._flusher_pid = None # type: Optional[int] + + def _ensure_thread(self): + # type: (...) -> bool + """For forking processes we might need to restart this thread. + This ensures that our process actually has that thread running. + """ + if not self._running: + return False + + pid = os.getpid() + if self._flusher_pid == pid: + return True + + with self._lock: + # Recheck to make sure another thread didn't get here and start the + # the flusher in the meantime + if self._flusher_pid == pid: + return True + + self._flusher_pid = pid + + self._flusher = threading.Thread(target=self._flush_loop) + self._flusher.daemon = True + + try: + self._flusher.start() + except RuntimeError: + # Unfortunately at this point the interpreter is in a state that no + # longer allows us to spawn a thread and we have to bail. + self._running = False + return False + + return True + + def _flush_loop(self): + # type: (...) -> None + while self._running: + self._flush_event.wait(self.FLUSH_WAIT_TIME + random.random()) + self._flush_event.clear() + self._flush() + + def add( + self, + log, # type: Log + ): + # type: (...) -> None + if not self._ensure_thread() or self._flusher is None: + return None + + with self._lock: + self._log_buffer.append(log) + if len(self._log_buffer) >= self.MAX_LOGS_BEFORE_FLUSH: + self._flush_event.set() + + def kill(self): + # type: (...) -> None + if self._flusher is None: + return + + self._running = False + self._flush_event.set() + self._flusher = None + + def flush(self): + # type: (...) -> None + self._flush() + + @staticmethod + def _log_to_transport_format(log): + # type: (Log) -> Any + def format_attribute(val): + # type: (int | float | str | bool) -> Any + if isinstance(val, bool): + return {"value": val, "type": "boolean"} + if isinstance(val, int): + return {"value": val, "type": "integer"} + if isinstance(val, float): + return {"value": val, "type": "double"} + if isinstance(val, str): + return {"value": val, "type": "string"} + return {"value": safe_repr(val), "type": "string"} + + if "sentry.severity_number" not in log["attributes"]: + log["attributes"]["sentry.severity_number"] = log["severity_number"] + if "sentry.severity_text" not in log["attributes"]: + log["attributes"]["sentry.severity_text"] = log["severity_text"] + + res = { + "timestamp": int(log["time_unix_nano"]) / 1.0e9, + "trace_id": log.get("trace_id", "00000000-0000-0000-0000-000000000000"), + "level": str(log["severity_text"]), + "body": str(log["body"]), + "attributes": { + k: format_attribute(v) for (k, v) in log["attributes"].items() + }, + } + + return res + + def _flush(self): + # type: (...) -> Optional[Envelope] + + envelope = Envelope( + headers={"sent_at": format_timestamp(datetime.now(timezone.utc))} + ) + with self._lock: + if len(self._log_buffer) == 0: + return None + + envelope.add_item( + Item( + type="log", + content_type="application/vnd.sentry.items.log+json", + headers={ + "item_count": len(self._log_buffer), + }, + payload=PayloadRef( + json={ + "items": [ + self._log_to_transport_format(log) + for log in self._log_buffer + ] + } + ), + ) + ) + self._log_buffer.clear() + + self._capture_func(envelope) + return envelope diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/_lru_cache.py b/.venv/lib/python3.12/site-packages/sentry_sdk/_lru_cache.py new file mode 100644 index 0000000000000000000000000000000000000000..cbadd9723b6fc545675f0176e9e2d767fd39bb38 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/_lru_cache.py @@ -0,0 +1,47 @@ +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing import Any + + +_SENTINEL = object() + + +class LRUCache: + def __init__(self, max_size): + # type: (int) -> None + if max_size <= 0: + raise AssertionError(f"invalid max_size: {max_size}") + self.max_size = max_size + self._data = {} # type: dict[Any, Any] + self.hits = self.misses = 0 + self.full = False + + def set(self, key, value): + # type: (Any, Any) -> None + current = self._data.pop(key, _SENTINEL) + if current is not _SENTINEL: + self._data[key] = value + elif self.full: + self._data.pop(next(iter(self._data))) + self._data[key] = value + else: + self._data[key] = value + self.full = len(self._data) >= self.max_size + + def get(self, key, default=None): + # type: (Any, Any) -> Any + try: + ret = self._data.pop(key) + except KeyError: + self.misses += 1 + ret = default + else: + self.hits += 1 + self._data[key] = ret + + return ret + + def get_all(self): + # type: () -> list[tuple[Any, Any]] + return list(self._data.items()) diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/_metrics.py b/.venv/lib/python3.12/site-packages/sentry_sdk/_metrics.py new file mode 100644 index 0000000000000000000000000000000000000000..03bde137bd026974f38ad34a8779b17473bc8329 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/_metrics.py @@ -0,0 +1,81 @@ +""" +NOTE: This file contains experimental code that may be changed or removed at any +time without prior notice. +""" + +import time +from typing import Any, Optional, TYPE_CHECKING, Union + +import sentry_sdk +from sentry_sdk.utils import safe_repr + +if TYPE_CHECKING: + from sentry_sdk._types import Metric, MetricType + + +def _capture_metric( + name, # type: str + metric_type, # type: MetricType + value, # type: float + unit=None, # type: Optional[str] + attributes=None, # type: Optional[dict[str, Any]] +): + # type: (...) -> None + client = sentry_sdk.get_client() + + attrs = {} # type: dict[str, Union[str, bool, float, int]] + if attributes: + for k, v in attributes.items(): + attrs[k] = ( + v + if ( + isinstance(v, str) + or isinstance(v, int) + or isinstance(v, bool) + or isinstance(v, float) + ) + else safe_repr(v) + ) + + metric = { + "timestamp": time.time(), + "trace_id": None, + "span_id": None, + "name": name, + "type": metric_type, + "value": float(value), + "unit": unit, + "attributes": attrs, + } # type: Metric + + client._capture_metric(metric) + + +def count( + name, # type: str + value, # type: float + unit=None, # type: Optional[str] + attributes=None, # type: Optional[dict[str, Any]] +): + # type: (...) -> None + _capture_metric(name, "counter", value, unit, attributes) + + +def gauge( + name, # type: str + value, # type: float + unit=None, # type: Optional[str] + attributes=None, # type: Optional[dict[str, Any]] +): + # type: (...) -> None + _capture_metric(name, "gauge", value, unit, attributes) + + +def distribution( + name, # type: str + value, # type: float + unit=None, # type: Optional[str] + attributes=None, # type: Optional[dict[str, Any]] +): + # type: (...) -> None + _capture_metric(name, "distribution", value, unit, attributes) diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/_metrics_batcher.py b/.venv/lib/python3.12/site-packages/sentry_sdk/_metrics_batcher.py new file mode 100644 index 0000000000000000000000000000000000000000..fd9a5d732bac33682ee676a1aa0b3a8ce33779bf --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/_metrics_batcher.py @@ -0,0 +1,156 @@ +import os +import random +import threading +from datetime import datetime, timezone +from typing import Optional, List, Callable, TYPE_CHECKING, Any, Union + +from sentry_sdk.utils import format_timestamp, safe_repr +from sentry_sdk.envelope import Envelope, Item, PayloadRef + +if TYPE_CHECKING: + from sentry_sdk._types import Metric + + +class MetricsBatcher: + MAX_METRICS_BEFORE_FLUSH = 100 + FLUSH_WAIT_TIME = 5.0 + + def __init__( + self, + capture_func, # type: Callable[[Envelope], None] + ): + # type: (...) -> None + self._metric_buffer = [] # type: List[Metric] + self._capture_func = capture_func + self._running = True + self._lock = threading.Lock() + + self._flush_event = threading.Event() # type: threading.Event + + self._flusher = None # type: Optional[threading.Thread] + self._flusher_pid = None # type: Optional[int] + + def _ensure_thread(self): + # type: (...) -> bool + if not self._running: + return False + + pid = os.getpid() + if self._flusher_pid == pid: + return True + + with self._lock: + if self._flusher_pid == pid: + return True + + self._flusher_pid = pid + + self._flusher = threading.Thread(target=self._flush_loop) + self._flusher.daemon = True + + try: + self._flusher.start() + except RuntimeError: + self._running = False + return False + + return True + + def _flush_loop(self): + # type: (...) -> None + while self._running: + self._flush_event.wait(self.FLUSH_WAIT_TIME + random.random()) + self._flush_event.clear() + self._flush() + + def add( + self, + metric, # type: Metric + ): + # type: (...) -> None + if not self._ensure_thread() or self._flusher is None: + return None + + with self._lock: + self._metric_buffer.append(metric) + if len(self._metric_buffer) >= self.MAX_METRICS_BEFORE_FLUSH: + self._flush_event.set() + + def kill(self): + # type: (...) -> None + if self._flusher is None: + return + + self._running = False + self._flush_event.set() + self._flusher = None + + def flush(self): + # type: (...) -> None + self._flush() + + @staticmethod + def _metric_to_transport_format(metric): + # type: (Metric) -> Any + def format_attribute(val): + # type: (Union[int, float, str, bool]) -> Any + if isinstance(val, bool): + return {"value": val, "type": "boolean"} + if isinstance(val, int): + return {"value": val, "type": "integer"} + if isinstance(val, float): + return {"value": val, "type": "double"} + if isinstance(val, str): + return {"value": val, "type": "string"} + return {"value": safe_repr(val), "type": "string"} + + res = { + "timestamp": metric["timestamp"], + "trace_id": metric["trace_id"], + "name": metric["name"], + "type": metric["type"], + "value": metric["value"], + "attributes": { + k: format_attribute(v) for (k, v) in metric["attributes"].items() + }, + } + + if metric.get("span_id") is not None: + res["span_id"] = metric["span_id"] + + if metric.get("unit") is not None: + res["unit"] = metric["unit"] + + return res + + def _flush(self): + # type: (...) -> Optional[Envelope] + + envelope = Envelope( + headers={"sent_at": format_timestamp(datetime.now(timezone.utc))} + ) + with self._lock: + if len(self._metric_buffer) == 0: + return None + + envelope.add_item( + Item( + type="trace_metric", + content_type="application/vnd.sentry.items.trace-metric+json", + headers={ + "item_count": len(self._metric_buffer), + }, + payload=PayloadRef( + json={ + "items": [ + self._metric_to_transport_format(metric) + for metric in self._metric_buffer + ] + } + ), + ) + ) + self._metric_buffer.clear() + + self._capture_func(envelope) + return envelope diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/_queue.py b/.venv/lib/python3.12/site-packages/sentry_sdk/_queue.py new file mode 100644 index 0000000000000000000000000000000000000000..a21c86ec0aeb7f6316106c6876a094245c58277d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/_queue.py @@ -0,0 +1,289 @@ +""" +A fork of Python 3.6's stdlib queue (found in Pythons 'cpython/Lib/queue.py') +with Lock swapped out for RLock to avoid a deadlock while garbage collecting. + +https://github.com/python/cpython/blob/v3.6.12/Lib/queue.py + + +See also +https://codewithoutrules.com/2017/08/16/concurrency-python/ +https://bugs.python.org/issue14976 +https://github.com/sqlalchemy/sqlalchemy/blob/4eb747b61f0c1b1c25bdee3856d7195d10a0c227/lib/sqlalchemy/queue.py#L1 + +We also vendor the code to evade eventlet's broken monkeypatching, see +https://github.com/getsentry/sentry-python/pull/484 + + +Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Python Software Foundation; + +All Rights Reserved + + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby +grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +analyze, test, perform and/or display publicly, prepare derivative works, +distribute, and otherwise use Python alone or in any derivative version, +provided, however, that PSF's License Agreement and PSF's notice of copyright, +i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Python Software Foundation; +All Rights Reserved" are retained in Python alone or in any derivative version +prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + +""" + +import threading + +from collections import deque +from time import time + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing import Any + +__all__ = ["EmptyError", "FullError", "Queue"] + + +class EmptyError(Exception): + "Exception raised by Queue.get(block=0)/get_nowait()." + + pass + + +class FullError(Exception): + "Exception raised by Queue.put(block=0)/put_nowait()." + + pass + + +class Queue: + """Create a queue object with a given maximum size. + + If maxsize is <= 0, the queue size is infinite. + """ + + def __init__(self, maxsize=0): + self.maxsize = maxsize + self._init(maxsize) + + # mutex must be held whenever the queue is mutating. All methods + # that acquire mutex must release it before returning. mutex + # is shared between the three conditions, so acquiring and + # releasing the conditions also acquires and releases mutex. + self.mutex = threading.RLock() + + # Notify not_empty whenever an item is added to the queue; a + # thread waiting to get is notified then. + self.not_empty = threading.Condition(self.mutex) + + # Notify not_full whenever an item is removed from the queue; + # a thread waiting to put is notified then. + self.not_full = threading.Condition(self.mutex) + + # Notify all_tasks_done whenever the number of unfinished tasks + # drops to zero; thread waiting to join() is notified to resume + self.all_tasks_done = threading.Condition(self.mutex) + self.unfinished_tasks = 0 + + def task_done(self): + """Indicate that a formerly enqueued task is complete. + + Used by Queue consumer threads. For each get() used to fetch a task, + a subsequent call to task_done() tells the queue that the processing + on the task is complete. + + If a join() is currently blocking, it will resume when all items + have been processed (meaning that a task_done() call was received + for every item that had been put() into the queue). + + Raises a ValueError if called more times than there were items + placed in the queue. + """ + with self.all_tasks_done: + unfinished = self.unfinished_tasks - 1 + if unfinished <= 0: + if unfinished < 0: + raise ValueError("task_done() called too many times") + self.all_tasks_done.notify_all() + self.unfinished_tasks = unfinished + + def join(self): + """Blocks until all items in the Queue have been gotten and processed. + + The count of unfinished tasks goes up whenever an item is added to the + queue. The count goes down whenever a consumer thread calls task_done() + to indicate the item was retrieved and all work on it is complete. + + When the count of unfinished tasks drops to zero, join() unblocks. + """ + with self.all_tasks_done: + while self.unfinished_tasks: + self.all_tasks_done.wait() + + def qsize(self): + """Return the approximate size of the queue (not reliable!).""" + with self.mutex: + return self._qsize() + + def empty(self): + """Return True if the queue is empty, False otherwise (not reliable!). + + This method is likely to be removed at some point. Use qsize() == 0 + as a direct substitute, but be aware that either approach risks a race + condition where a queue can grow before the result of empty() or + qsize() can be used. + + To create code that needs to wait for all queued tasks to be + completed, the preferred technique is to use the join() method. + """ + with self.mutex: + return not self._qsize() + + def full(self): + """Return True if the queue is full, False otherwise (not reliable!). + + This method is likely to be removed at some point. Use qsize() >= n + as a direct substitute, but be aware that either approach risks a race + condition where a queue can shrink before the result of full() or + qsize() can be used. + """ + with self.mutex: + return 0 < self.maxsize <= self._qsize() + + def put(self, item, block=True, timeout=None): + """Put an item into the queue. + + If optional args 'block' is true and 'timeout' is None (the default), + block if necessary until a free slot is available. If 'timeout' is + a non-negative number, it blocks at most 'timeout' seconds and raises + the FullError exception if no free slot was available within that time. + Otherwise ('block' is false), put an item on the queue if a free slot + is immediately available, else raise the FullError exception ('timeout' + is ignored in that case). + """ + with self.not_full: + if self.maxsize > 0: + if not block: + if self._qsize() >= self.maxsize: + raise FullError() + elif timeout is None: + while self._qsize() >= self.maxsize: + self.not_full.wait() + elif timeout < 0: + raise ValueError("'timeout' must be a non-negative number") + else: + endtime = time() + timeout + while self._qsize() >= self.maxsize: + remaining = endtime - time() + if remaining <= 0.0: + raise FullError() + self.not_full.wait(remaining) + self._put(item) + self.unfinished_tasks += 1 + self.not_empty.notify() + + def get(self, block=True, timeout=None): + """Remove and return an item from the queue. + + If optional args 'block' is true and 'timeout' is None (the default), + block if necessary until an item is available. If 'timeout' is + a non-negative number, it blocks at most 'timeout' seconds and raises + the EmptyError exception if no item was available within that time. + Otherwise ('block' is false), return an item if one is immediately + available, else raise the EmptyError exception ('timeout' is ignored + in that case). + """ + with self.not_empty: + if not block: + if not self._qsize(): + raise EmptyError() + elif timeout is None: + while not self._qsize(): + self.not_empty.wait() + elif timeout < 0: + raise ValueError("'timeout' must be a non-negative number") + else: + endtime = time() + timeout + while not self._qsize(): + remaining = endtime - time() + if remaining <= 0.0: + raise EmptyError() + self.not_empty.wait(remaining) + item = self._get() + self.not_full.notify() + return item + + def put_nowait(self, item): + """Put an item into the queue without blocking. + + Only enqueue the item if a free slot is immediately available. + Otherwise raise the FullError exception. + """ + return self.put(item, block=False) + + def get_nowait(self): + """Remove and return an item from the queue without blocking. + + Only get an item if one is immediately available. Otherwise + raise the EmptyError exception. + """ + return self.get(block=False) + + # Override these methods to implement other queue organizations + # (e.g. stack or priority queue). + # These will only be called with appropriate locks held + + # Initialize the queue representation + def _init(self, maxsize): + self.queue = deque() # type: Any + + def _qsize(self): + return len(self.queue) + + # Put a new item in the queue + def _put(self, item): + self.queue.append(item) + + # Get an item from the queue + def _get(self): + return self.queue.popleft() diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/_types.py b/.venv/lib/python3.12/site-packages/sentry_sdk/_types.py new file mode 100644 index 0000000000000000000000000000000000000000..66ed7df4f7bb0988944635c0bfb69de2241617dc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/_types.py @@ -0,0 +1,338 @@ +from typing import TYPE_CHECKING, TypeVar, Union + + +# Re-exported for compat, since code out there in the wild might use this variable. +MYPY = TYPE_CHECKING + + +SENSITIVE_DATA_SUBSTITUTE = "[Filtered]" + + +class AnnotatedValue: + """ + Meta information for a data field in the event payload. + This is to tell Relay that we have tampered with the fields value. + See: + https://github.com/getsentry/relay/blob/be12cd49a0f06ea932ed9b9f93a655de5d6ad6d1/relay-general/src/types/meta.rs#L407-L423 + """ + + __slots__ = ("value", "metadata") + + def __init__(self, value, metadata): + # type: (Optional[Any], Dict[str, Any]) -> None + self.value = value + self.metadata = metadata + + def __eq__(self, other): + # type: (Any) -> bool + if not isinstance(other, AnnotatedValue): + return False + + return self.value == other.value and self.metadata == other.metadata + + def __str__(self): + # type: (AnnotatedValue) -> str + return str({"value": str(self.value), "metadata": str(self.metadata)}) + + def __len__(self): + # type: (AnnotatedValue) -> int + if self.value is not None: + return len(self.value) + else: + return 0 + + @classmethod + def removed_because_raw_data(cls): + # type: () -> AnnotatedValue + """The value was removed because it could not be parsed. This is done for request body values that are not json nor a form.""" + return AnnotatedValue( + value="", + metadata={ + "rem": [ # Remark + [ + "!raw", # Unparsable raw data + "x", # The fields original value was removed + ] + ] + }, + ) + + @classmethod + def removed_because_over_size_limit(cls, value=""): + # type: (Any) -> AnnotatedValue + """ + The actual value was removed because the size of the field exceeded the configured maximum size, + for example specified with the max_request_body_size sdk option. + """ + return AnnotatedValue( + value=value, + metadata={ + "rem": [ # Remark + [ + "!config", # Because of configured maximum size + "x", # The fields original value was removed + ] + ] + }, + ) + + @classmethod + def substituted_because_contains_sensitive_data(cls): + # type: () -> AnnotatedValue + """The actual value was removed because it contained sensitive information.""" + return AnnotatedValue( + value=SENSITIVE_DATA_SUBSTITUTE, + metadata={ + "rem": [ # Remark + [ + "!config", # Because of SDK configuration (in this case the config is the hard coded removal of certain django cookies) + "s", # The fields original value was substituted + ] + ] + }, + ) + + +T = TypeVar("T") +Annotated = Union[AnnotatedValue, T] + + +if TYPE_CHECKING: + from collections.abc import Container, MutableMapping, Sequence + + from datetime import datetime + + from types import TracebackType + from typing import Any + from typing import Callable + from typing import Dict + from typing import Mapping + from typing import NotRequired + from typing import Optional + from typing import Tuple + from typing import Type + from typing_extensions import Literal, TypedDict + + class SDKInfo(TypedDict): + name: str + version: str + packages: Sequence[Mapping[str, str]] + + # "critical" is an alias of "fatal" recognized by Relay + LogLevelStr = Literal["fatal", "critical", "error", "warning", "info", "debug"] + + DurationUnit = Literal[ + "nanosecond", + "microsecond", + "millisecond", + "second", + "minute", + "hour", + "day", + "week", + ] + + InformationUnit = Literal[ + "bit", + "byte", + "kilobyte", + "kibibyte", + "megabyte", + "mebibyte", + "gigabyte", + "gibibyte", + "terabyte", + "tebibyte", + "petabyte", + "pebibyte", + "exabyte", + "exbibyte", + ] + + FractionUnit = Literal["ratio", "percent"] + MeasurementUnit = Union[DurationUnit, InformationUnit, FractionUnit, str] + + MeasurementValue = TypedDict( + "MeasurementValue", + { + "value": float, + "unit": NotRequired[Optional[MeasurementUnit]], + }, + ) + + Event = TypedDict( + "Event", + { + "breadcrumbs": Annotated[ + dict[Literal["values"], list[dict[str, Any]]] + ], # TODO: We can expand on this type + "check_in_id": str, + "contexts": dict[str, dict[str, object]], + "dist": str, + "duration": Optional[float], + "environment": Optional[str], + "errors": list[dict[str, Any]], # TODO: We can expand on this type + "event_id": str, + "exception": dict[ + Literal["values"], list[dict[str, Any]] + ], # TODO: We can expand on this type + "extra": MutableMapping[str, object], + "fingerprint": list[str], + "level": LogLevelStr, + "logentry": Mapping[str, object], + "logger": str, + "measurements": dict[str, MeasurementValue], + "message": str, + "modules": dict[str, str], + "monitor_config": Mapping[str, object], + "monitor_slug": Optional[str], + "platform": Literal["python"], + "profile": object, # Should be sentry_sdk.profiler.Profile, but we can't import that here due to circular imports + "release": Optional[str], + "request": dict[str, object], + "sdk": Mapping[str, object], + "server_name": str, + "spans": Annotated[list[dict[str, object]]], + "stacktrace": dict[ + str, object + ], # We access this key in the code, but I am unsure whether we ever set it + "start_timestamp": datetime, + "status": Optional[str], + "tags": MutableMapping[ + str, str + ], # Tags must be less than 200 characters each + "threads": dict[ + Literal["values"], list[dict[str, Any]] + ], # TODO: We can expand on this type + "timestamp": Optional[datetime], # Must be set before sending the event + "transaction": str, + "transaction_info": Mapping[str, Any], # TODO: We can expand on this type + "type": Literal["check_in", "transaction"], + "user": dict[str, object], + "_dropped_spans": int, + }, + total=False, + ) + + ExcInfo = Union[ + tuple[Type[BaseException], BaseException, Optional[TracebackType]], + tuple[None, None, None], + ] + + # TODO: Make a proper type definition for this (PRs welcome!) + Hint = Dict[str, Any] + + Log = TypedDict( + "Log", + { + "severity_text": str, + "severity_number": int, + "body": str, + "attributes": dict[str, str | bool | float | int], + "time_unix_nano": int, + "trace_id": Optional[str], + }, + ) + + MetricType = Literal["counter", "gauge", "distribution"] + + MetricAttributeValue = TypedDict( + "MetricAttributeValue", + { + "value": Union[str, bool, float, int], + "type": Literal["string", "boolean", "double", "integer"], + }, + ) + + Metric = TypedDict( + "Metric", + { + "timestamp": float, + "trace_id": Optional[str], + "span_id": Optional[str], + "name": str, + "type": MetricType, + "value": float, + "unit": Optional[str], + "attributes": dict[str, str | bool | float | int], + }, + ) + + MetricProcessor = Callable[[Metric, Hint], Optional[Metric]] + + # TODO: Make a proper type definition for this (PRs welcome!) + Breadcrumb = Dict[str, Any] + + # TODO: Make a proper type definition for this (PRs welcome!) + BreadcrumbHint = Dict[str, Any] + + # TODO: Make a proper type definition for this (PRs welcome!) + SamplingContext = Dict[str, Any] + + EventProcessor = Callable[[Event, Hint], Optional[Event]] + ErrorProcessor = Callable[[Event, ExcInfo], Optional[Event]] + BreadcrumbProcessor = Callable[[Breadcrumb, BreadcrumbHint], Optional[Breadcrumb]] + TransactionProcessor = Callable[[Event, Hint], Optional[Event]] + LogProcessor = Callable[[Log, Hint], Optional[Log]] + + TracesSampler = Callable[[SamplingContext], Union[float, int, bool]] + + # https://github.com/python/mypy/issues/5710 + NotImplementedType = Any + + EventDataCategory = Literal[ + "default", + "error", + "crash", + "transaction", + "security", + "attachment", + "session", + "internal", + "profile", + "profile_chunk", + "monitor", + "span", + "log_item", + "trace_metric", + ] + SessionStatus = Literal["ok", "exited", "crashed", "abnormal"] + + ContinuousProfilerMode = Literal["thread", "gevent", "unknown"] + ProfilerMode = Union[ContinuousProfilerMode, Literal["sleep"]] + + MonitorConfigScheduleType = Literal["crontab", "interval"] + MonitorConfigScheduleUnit = Literal[ + "year", + "month", + "week", + "day", + "hour", + "minute", + "second", # not supported in Sentry and will result in a warning + ] + + MonitorConfigSchedule = TypedDict( + "MonitorConfigSchedule", + { + "type": MonitorConfigScheduleType, + "value": Union[int, str], + "unit": MonitorConfigScheduleUnit, + }, + total=False, + ) + + MonitorConfig = TypedDict( + "MonitorConfig", + { + "schedule": MonitorConfigSchedule, + "timezone": str, + "checkin_margin": int, + "max_runtime": int, + "failure_issue_threshold": int, + "recovery_threshold": int, + }, + total=False, + ) + + HttpStatusCodeRange = Union[int, Container[int]] diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/_werkzeug.py b/.venv/lib/python3.12/site-packages/sentry_sdk/_werkzeug.py new file mode 100644 index 0000000000000000000000000000000000000000..0fa3d611f154b4eb7ed3f609d33718a70918d01f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/_werkzeug.py @@ -0,0 +1,98 @@ +""" +Copyright (c) 2007 by the Pallets team. + +Some rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* Neither the name of the copyright 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 AND DOCUMENTATION 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 AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. +""" + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing import Dict + from typing import Iterator + from typing import Tuple + + +# +# `get_headers` comes from `werkzeug.datastructures.EnvironHeaders` +# https://github.com/pallets/werkzeug/blob/0.14.1/werkzeug/datastructures.py#L1361 +# +# We need this function because Django does not give us a "pure" http header +# dict. So we might as well use it for all WSGI integrations. +# +def _get_headers(environ): + # type: (Dict[str, str]) -> Iterator[Tuple[str, str]] + """ + Returns only proper HTTP headers. + """ + for key, value in environ.items(): + key = str(key) + if key.startswith("HTTP_") and key not in ( + "HTTP_CONTENT_TYPE", + "HTTP_CONTENT_LENGTH", + ): + yield key[5:].replace("_", "-").title(), value + elif key in ("CONTENT_TYPE", "CONTENT_LENGTH"): + yield key.replace("_", "-").title(), value + + +# +# `get_host` comes from `werkzeug.wsgi.get_host` +# https://github.com/pallets/werkzeug/blob/1.0.1/src/werkzeug/wsgi.py#L145 +# +def get_host(environ, use_x_forwarded_for=False): + # type: (Dict[str, str], bool) -> str + """ + Return the host for the given WSGI environment. + """ + if use_x_forwarded_for and "HTTP_X_FORWARDED_HOST" in environ: + rv = environ["HTTP_X_FORWARDED_HOST"] + if environ["wsgi.url_scheme"] == "http" and rv.endswith(":80"): + rv = rv[:-3] + elif environ["wsgi.url_scheme"] == "https" and rv.endswith(":443"): + rv = rv[:-4] + elif environ.get("HTTP_HOST"): + rv = environ["HTTP_HOST"] + if environ["wsgi.url_scheme"] == "http" and rv.endswith(":80"): + rv = rv[:-3] + elif environ["wsgi.url_scheme"] == "https" and rv.endswith(":443"): + rv = rv[:-4] + elif environ.get("SERVER_NAME"): + rv = environ["SERVER_NAME"] + if (environ["wsgi.url_scheme"], environ["SERVER_PORT"]) not in ( + ("https", "443"), + ("http", "80"), + ): + rv += ":" + environ["SERVER_PORT"] + else: + # In spite of the WSGI spec, SERVER_NAME might not be present. + rv = "unknown" + + return rv diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/api.py b/.venv/lib/python3.12/site-packages/sentry_sdk/api.py new file mode 100644 index 0000000000000000000000000000000000000000..43758b4d786543ee1b9bdb2f6fc9e041abf781e2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/api.py @@ -0,0 +1,555 @@ +import inspect +import warnings +from contextlib import contextmanager + +from sentry_sdk import tracing_utils, Client +from sentry_sdk._init_implementation import init +from sentry_sdk.consts import INSTRUMENTER +from sentry_sdk.scope import Scope, _ScopeManager, new_scope, isolation_scope +from sentry_sdk.tracing import NoOpSpan, Transaction, trace +from sentry_sdk.crons import monitor + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Mapping + + from typing import Any + from typing import Dict + from typing import Generator + from typing import Optional + from typing import overload + from typing import Callable + from typing import TypeVar + from typing import ContextManager + from typing import Union + + from typing_extensions import Unpack + + from sentry_sdk.client import BaseClient + from sentry_sdk._types import ( + Event, + Hint, + Breadcrumb, + BreadcrumbHint, + ExcInfo, + MeasurementUnit, + LogLevelStr, + SamplingContext, + ) + from sentry_sdk.tracing import Span, TransactionKwargs + + T = TypeVar("T") + F = TypeVar("F", bound=Callable[..., Any]) +else: + + def overload(x): + # type: (T) -> T + return x + + +# When changing this, update __all__ in __init__.py too +__all__ = [ + "init", + "add_attachment", + "add_breadcrumb", + "capture_event", + "capture_exception", + "capture_message", + "configure_scope", + "continue_trace", + "flush", + "get_baggage", + "get_client", + "get_global_scope", + "get_isolation_scope", + "get_current_scope", + "get_current_span", + "get_traceparent", + "is_initialized", + "isolation_scope", + "last_event_id", + "new_scope", + "push_scope", + "set_context", + "set_extra", + "set_level", + "set_measurement", + "set_tag", + "set_tags", + "set_user", + "start_span", + "start_transaction", + "trace", + "monitor", + "start_session", + "end_session", + "set_transaction_name", + "update_current_span", +] + + +def scopemethod(f): + # type: (F) -> F + f.__doc__ = "%s\n\n%s" % ( + "Alias for :py:meth:`sentry_sdk.Scope.%s`" % f.__name__, + inspect.getdoc(getattr(Scope, f.__name__)), + ) + return f + + +def clientmethod(f): + # type: (F) -> F + f.__doc__ = "%s\n\n%s" % ( + "Alias for :py:meth:`sentry_sdk.Client.%s`" % f.__name__, + inspect.getdoc(getattr(Client, f.__name__)), + ) + return f + + +@scopemethod +def get_client(): + # type: () -> BaseClient + return Scope.get_client() + + +def is_initialized(): + # type: () -> bool + """ + .. versionadded:: 2.0.0 + + Returns whether Sentry has been initialized or not. + + If a client is available and the client is active + (meaning it is configured to send data) then + Sentry is initialized. + """ + return get_client().is_active() + + +@scopemethod +def get_global_scope(): + # type: () -> Scope + return Scope.get_global_scope() + + +@scopemethod +def get_isolation_scope(): + # type: () -> Scope + return Scope.get_isolation_scope() + + +@scopemethod +def get_current_scope(): + # type: () -> Scope + return Scope.get_current_scope() + + +@scopemethod +def last_event_id(): + # type: () -> Optional[str] + """ + See :py:meth:`sentry_sdk.Scope.last_event_id` documentation regarding + this method's limitations. + """ + return Scope.last_event_id() + + +@scopemethod +def capture_event( + event, # type: Event + hint=None, # type: Optional[Hint] + scope=None, # type: Optional[Any] + **scope_kwargs, # type: Any +): + # type: (...) -> Optional[str] + return get_current_scope().capture_event(event, hint, scope=scope, **scope_kwargs) + + +@scopemethod +def capture_message( + message, # type: str + level=None, # type: Optional[LogLevelStr] + scope=None, # type: Optional[Any] + **scope_kwargs, # type: Any +): + # type: (...) -> Optional[str] + return get_current_scope().capture_message( + message, level, scope=scope, **scope_kwargs + ) + + +@scopemethod +def capture_exception( + error=None, # type: Optional[Union[BaseException, ExcInfo]] + scope=None, # type: Optional[Any] + **scope_kwargs, # type: Any +): + # type: (...) -> Optional[str] + return get_current_scope().capture_exception(error, scope=scope, **scope_kwargs) + + +@scopemethod +def add_attachment( + bytes=None, # type: Union[None, bytes, Callable[[], bytes]] + filename=None, # type: Optional[str] + path=None, # type: Optional[str] + content_type=None, # type: Optional[str] + add_to_transactions=False, # type: bool +): + # type: (...) -> None + return get_isolation_scope().add_attachment( + bytes, filename, path, content_type, add_to_transactions + ) + + +@scopemethod +def add_breadcrumb( + crumb=None, # type: Optional[Breadcrumb] + hint=None, # type: Optional[BreadcrumbHint] + **kwargs, # type: Any +): + # type: (...) -> None + return get_isolation_scope().add_breadcrumb(crumb, hint, **kwargs) + + +@overload +def configure_scope(): + # type: () -> ContextManager[Scope] + pass + + +@overload +def configure_scope( # noqa: F811 + callback, # type: Callable[[Scope], None] +): + # type: (...) -> None + pass + + +def configure_scope( # noqa: F811 + callback=None, # type: Optional[Callable[[Scope], None]] +): + # type: (...) -> Optional[ContextManager[Scope]] + """ + Reconfigures the scope. + + :param callback: If provided, call the callback with the current scope. + + :returns: If no callback is provided, returns a context manager that returns the scope. + """ + warnings.warn( + "sentry_sdk.configure_scope is deprecated and will be removed in the next major version. " + "Please consult our migration guide to learn how to migrate to the new API: " + "https://docs.sentry.io/platforms/python/migration/1.x-to-2.x#scope-configuring", + DeprecationWarning, + stacklevel=2, + ) + + scope = get_isolation_scope() + scope.generate_propagation_context() + + if callback is not None: + # TODO: used to return None when client is None. Check if this changes behavior. + callback(scope) + + return None + + @contextmanager + def inner(): + # type: () -> Generator[Scope, None, None] + yield scope + + return inner() + + +@overload +def push_scope(): + # type: () -> ContextManager[Scope] + pass + + +@overload +def push_scope( # noqa: F811 + callback, # type: Callable[[Scope], None] +): + # type: (...) -> None + pass + + +def push_scope( # noqa: F811 + callback=None, # type: Optional[Callable[[Scope], None]] +): + # type: (...) -> Optional[ContextManager[Scope]] + """ + Pushes a new layer on the scope stack. + + :param callback: If provided, this method pushes a scope, calls + `callback`, and pops the scope again. + + :returns: If no `callback` is provided, a context manager that should + be used to pop the scope again. + """ + warnings.warn( + "sentry_sdk.push_scope is deprecated and will be removed in the next major version. " + "Please consult our migration guide to learn how to migrate to the new API: " + "https://docs.sentry.io/platforms/python/migration/1.x-to-2.x#scope-pushing", + DeprecationWarning, + stacklevel=2, + ) + + if callback is not None: + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + with push_scope() as scope: + callback(scope) + return None + + return _ScopeManager() + + +@scopemethod +def set_tag(key, value): + # type: (str, Any) -> None + return get_isolation_scope().set_tag(key, value) + + +@scopemethod +def set_tags(tags): + # type: (Mapping[str, object]) -> None + return get_isolation_scope().set_tags(tags) + + +@scopemethod +def set_context(key, value): + # type: (str, Dict[str, Any]) -> None + return get_isolation_scope().set_context(key, value) + + +@scopemethod +def set_extra(key, value): + # type: (str, Any) -> None + return get_isolation_scope().set_extra(key, value) + + +@scopemethod +def set_user(value): + # type: (Optional[Dict[str, Any]]) -> None + return get_isolation_scope().set_user(value) + + +@scopemethod +def set_level(value): + # type: (LogLevelStr) -> None + return get_isolation_scope().set_level(value) + + +@clientmethod +def flush( + timeout=None, # type: Optional[float] + callback=None, # type: Optional[Callable[[int, float], None]] +): + # type: (...) -> None + return get_client().flush(timeout=timeout, callback=callback) + + +@scopemethod +def start_span( + **kwargs, # type: Any +): + # type: (...) -> Span + return get_current_scope().start_span(**kwargs) + + +@scopemethod +def start_transaction( + transaction=None, # type: Optional[Transaction] + instrumenter=INSTRUMENTER.SENTRY, # type: str + custom_sampling_context=None, # type: Optional[SamplingContext] + **kwargs, # type: Unpack[TransactionKwargs] +): + # type: (...) -> Union[Transaction, NoOpSpan] + """ + Start and return a transaction on the current scope. + + Start an existing transaction if given, otherwise create and start a new + transaction with kwargs. + + This is the entry point to manual tracing instrumentation. + + A tree structure can be built by adding child spans to the transaction, + and child spans to other spans. To start a new child span within the + transaction or any span, call the respective `.start_child()` method. + + Every child span must be finished before the transaction is finished, + otherwise the unfinished spans are discarded. + + When used as context managers, spans and transactions are automatically + finished at the end of the `with` block. If not using context managers, + call the `.finish()` method. + + When the transaction is finished, it will be sent to Sentry with all its + finished child spans. + + :param transaction: The transaction to start. If omitted, we create and + start a new transaction. + :param instrumenter: This parameter is meant for internal use only. It + will be removed in the next major version. + :param custom_sampling_context: The transaction's custom sampling context. + :param kwargs: Optional keyword arguments to be passed to the Transaction + constructor. See :py:class:`sentry_sdk.tracing.Transaction` for + available arguments. + """ + return get_current_scope().start_transaction( + transaction, instrumenter, custom_sampling_context, **kwargs + ) + + +def set_measurement(name, value, unit=""): + # type: (str, float, MeasurementUnit) -> None + """ + .. deprecated:: 2.28.0 + This function is deprecated and will be removed in the next major release. + """ + transaction = get_current_scope().transaction + if transaction is not None: + transaction.set_measurement(name, value, unit) + + +def get_current_span(scope=None): + # type: (Optional[Scope]) -> Optional[Span] + """ + Returns the currently active span if there is one running, otherwise `None` + """ + return tracing_utils.get_current_span(scope) + + +def get_traceparent(): + # type: () -> Optional[str] + """ + Returns the traceparent either from the active span or from the scope. + """ + return get_current_scope().get_traceparent() + + +def get_baggage(): + # type: () -> Optional[str] + """ + Returns Baggage either from the active span or from the scope. + """ + baggage = get_current_scope().get_baggage() + if baggage is not None: + return baggage.serialize() + + return None + + +def continue_trace( + environ_or_headers, op=None, name=None, source=None, origin="manual" +): + # type: (Dict[str, Any], Optional[str], Optional[str], Optional[str], str) -> Transaction + """ + Sets the propagation context from environment or headers and returns a transaction. + """ + return get_isolation_scope().continue_trace( + environ_or_headers, op, name, source, origin + ) + + +@scopemethod +def start_session( + session_mode="application", # type: str +): + # type: (...) -> None + return get_isolation_scope().start_session(session_mode=session_mode) + + +@scopemethod +def end_session(): + # type: () -> None + return get_isolation_scope().end_session() + + +@scopemethod +def set_transaction_name(name, source=None): + # type: (str, Optional[str]) -> None + return get_current_scope().set_transaction_name(name, source) + + +def update_current_span(op=None, name=None, attributes=None, data=None): + # type: (Optional[str], Optional[str], Optional[dict[str, Union[str, int, float, bool]]], Optional[dict[str, Any]]) -> None + """ + Update the current active span with the provided parameters. + + This function allows you to modify properties of the currently active span. + If no span is currently active, this function will do nothing. + + :param op: The operation name for the span. This is a high-level description + of what the span represents (e.g., "http.client", "db.query"). + You can use predefined constants from :py:class:`sentry_sdk.consts.OP` + or provide your own string. If not provided, the span's operation will + remain unchanged. + :type op: str or None + + :param name: The human-readable name/description for the span. This provides + more specific details about what the span represents (e.g., "GET /api/users", + "SELECT * FROM users"). If not provided, the span's name will remain unchanged. + :type name: str or None + + :param data: A dictionary of key-value pairs to add as data to the span. This + data will be merged with any existing span data. If not provided, + no data will be added. + + .. deprecated:: 2.35.0 + Use ``attributes`` instead. The ``data`` parameter will be removed + in a future version. + :type data: dict[str, Union[str, int, float, bool]] or None + + :param attributes: A dictionary of key-value pairs to add as attributes to the span. + Attribute values must be strings, integers, floats, or booleans. These + attributes will be merged with any existing span data. If not provided, + no attributes will be added. + :type attributes: dict[str, Union[str, int, float, bool]] or None + + :returns: None + + .. versionadded:: 2.35.0 + + Example:: + + import sentry_sdk + from sentry_sdk.consts import OP + + sentry_sdk.update_current_span( + op=OP.FUNCTION, + name="process_user_data", + attributes={"user_id": 123, "batch_size": 50} + ) + """ + current_span = get_current_span() + + if current_span is None: + return + + if op is not None: + current_span.op = op + + if name is not None: + # internally it is still description + current_span.description = name + + if data is not None and attributes is not None: + raise ValueError( + "Cannot provide both `data` and `attributes`. Please use only `attributes`." + ) + + if data is not None: + warnings.warn( + "The `data` parameter is deprecated. Please use `attributes` instead.", + DeprecationWarning, + stacklevel=2, + ) + attributes = data + + if attributes is not None: + current_span.update_data(attributes) diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/attachments.py b/.venv/lib/python3.12/site-packages/sentry_sdk/attachments.py new file mode 100644 index 0000000000000000000000000000000000000000..e5404f8658af45128e4ba81692a1fe966f8c7312 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/attachments.py @@ -0,0 +1,75 @@ +import os +import mimetypes + +from sentry_sdk.envelope import Item, PayloadRef + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing import Optional, Union, Callable + + +class Attachment: + """Additional files/data to send along with an event. + + This class stores attachments that can be sent along with an event. Attachments are files or other data, e.g. + config or log files, that are relevant to an event. Attachments are set on the ``Scope``, and are sent along with + all non-transaction events (or all events including transactions if ``add_to_transactions`` is ``True``) that are + captured within the ``Scope``. + + To add an attachment to a ``Scope``, use :py:meth:`sentry_sdk.Scope.add_attachment`. The parameters for + ``add_attachment`` are the same as the parameters for this class's constructor. + + :param bytes: Raw bytes of the attachment, or a function that returns the raw bytes. Must be provided unless + ``path`` is provided. + :param filename: The filename of the attachment. Must be provided unless ``path`` is provided. + :param path: Path to a file to attach. Must be provided unless ``bytes`` is provided. + :param content_type: The content type of the attachment. If not provided, it will be guessed from the ``filename`` + parameter, if available, or the ``path`` parameter if ``filename`` is ``None``. + :param add_to_transactions: Whether to add this attachment to transactions. Defaults to ``False``. + """ + + def __init__( + self, + bytes=None, # type: Union[None, bytes, Callable[[], bytes]] + filename=None, # type: Optional[str] + path=None, # type: Optional[str] + content_type=None, # type: Optional[str] + add_to_transactions=False, # type: bool + ): + # type: (...) -> None + if bytes is None and path is None: + raise TypeError("path or raw bytes required for attachment") + if filename is None and path is not None: + filename = os.path.basename(path) + if filename is None: + raise TypeError("filename is required for attachment") + if content_type is None: + content_type = mimetypes.guess_type(filename)[0] + self.bytes = bytes + self.filename = filename + self.path = path + self.content_type = content_type + self.add_to_transactions = add_to_transactions + + def to_envelope_item(self): + # type: () -> Item + """Returns an envelope item for this attachment.""" + payload = None # type: Union[None, PayloadRef, bytes] + if self.bytes is not None: + if callable(self.bytes): + payload = self.bytes() + else: + payload = self.bytes + else: + payload = PayloadRef(path=self.path) + return Item( + payload=payload, + type="attachment", + content_type=self.content_type, + filename=self.filename, + ) + + def __repr__(self): + # type: () -> str + return "" % (self.filename,) diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/client.py b/.venv/lib/python3.12/site-packages/sentry_sdk/client.py new file mode 100644 index 0000000000000000000000000000000000000000..d17f922642ad93a2b86078d4538ee005499cc525 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/client.py @@ -0,0 +1,1136 @@ +import os +import uuid +import random +import socket +from collections.abc import Mapping +from datetime import datetime, timezone +from importlib import import_module +from typing import TYPE_CHECKING, List, Dict, cast, overload +import warnings + +import sentry_sdk +from sentry_sdk._compat import PY37, check_uwsgi_thread_support +from sentry_sdk.utils import ( + AnnotatedValue, + ContextVar, + capture_internal_exceptions, + current_stacktrace, + env_to_bool, + format_timestamp, + get_sdk_name, + get_type_name, + get_default_release, + handle_in_app, + is_gevent, + logger, + get_before_send_log, + get_before_send_metric, + has_logs_enabled, + has_metrics_enabled, +) +from sentry_sdk.serializer import serialize +from sentry_sdk.tracing import trace +from sentry_sdk.transport import BaseHttpTransport, make_transport +from sentry_sdk.consts import ( + SPANDATA, + DEFAULT_MAX_VALUE_LENGTH, + DEFAULT_OPTIONS, + INSTRUMENTER, + VERSION, + ClientConstructor, +) +from sentry_sdk.integrations import _DEFAULT_INTEGRATIONS, setup_integrations +from sentry_sdk.integrations.dedupe import DedupeIntegration +from sentry_sdk.sessions import SessionFlusher +from sentry_sdk.envelope import Envelope +from sentry_sdk.profiler.continuous_profiler import setup_continuous_profiler +from sentry_sdk.profiler.transaction_profiler import ( + has_profiling_enabled, + Profile, + setup_profiler, +) +from sentry_sdk.scrubber import EventScrubber +from sentry_sdk.monitor import Monitor + +if TYPE_CHECKING: + from typing import Any + from typing import Callable + from typing import Optional + from typing import Sequence + from typing import Type + from typing import Union + from typing import TypeVar + + from sentry_sdk._types import Event, Hint, SDKInfo, Log, Metric + from sentry_sdk.integrations import Integration + from sentry_sdk.scope import Scope + from sentry_sdk.session import Session + from sentry_sdk.spotlight import SpotlightClient + from sentry_sdk.transport import Transport + from sentry_sdk._log_batcher import LogBatcher + from sentry_sdk._metrics_batcher import MetricsBatcher + + I = TypeVar("I", bound=Integration) # noqa: E741 + +_client_init_debug = ContextVar("client_init_debug") + + +SDK_INFO = { + "name": "sentry.python", # SDK name will be overridden after integrations have been loaded with sentry_sdk.integrations.setup_integrations() + "version": VERSION, + "packages": [{"name": "pypi:sentry-sdk", "version": VERSION}], +} # type: SDKInfo + + +def _get_options(*args, **kwargs): + # type: (*Optional[str], **Any) -> Dict[str, Any] + if args and (isinstance(args[0], (bytes, str)) or args[0] is None): + dsn = args[0] # type: Optional[str] + args = args[1:] + else: + dsn = None + + if len(args) > 1: + raise TypeError("Only single positional argument is expected") + + rv = dict(DEFAULT_OPTIONS) + options = dict(*args, **kwargs) + if dsn is not None and options.get("dsn") is None: + options["dsn"] = dsn + + for key, value in options.items(): + if key not in rv: + raise TypeError("Unknown option %r" % (key,)) + + rv[key] = value + + if rv["dsn"] is None: + rv["dsn"] = os.environ.get("SENTRY_DSN") + + if rv["release"] is None: + rv["release"] = get_default_release() + + if rv["environment"] is None: + rv["environment"] = os.environ.get("SENTRY_ENVIRONMENT") or "production" + + if rv["debug"] is None: + rv["debug"] = env_to_bool(os.environ.get("SENTRY_DEBUG"), strict=True) or False + + if rv["server_name"] is None and hasattr(socket, "gethostname"): + rv["server_name"] = socket.gethostname() + + if rv["instrumenter"] is None: + rv["instrumenter"] = INSTRUMENTER.SENTRY + + if rv["project_root"] is None: + try: + project_root = os.getcwd() + except Exception: + project_root = None + + rv["project_root"] = project_root + + if rv["enable_tracing"] is True and rv["traces_sample_rate"] is None: + rv["traces_sample_rate"] = 1.0 + + if rv["event_scrubber"] is None: + rv["event_scrubber"] = EventScrubber( + send_default_pii=( + False if rv["send_default_pii"] is None else rv["send_default_pii"] + ) + ) + + if rv["socket_options"] and not isinstance(rv["socket_options"], list): + logger.warning( + "Ignoring socket_options because of unexpected format. See urllib3.HTTPConnection.socket_options for the expected format." + ) + rv["socket_options"] = None + + if rv["keep_alive"] is None: + rv["keep_alive"] = ( + env_to_bool(os.environ.get("SENTRY_KEEP_ALIVE"), strict=True) or False + ) + + if rv["enable_tracing"] is not None: + warnings.warn( + "The `enable_tracing` parameter is deprecated. Please use `traces_sample_rate` instead.", + DeprecationWarning, + stacklevel=2, + ) + + return rv + + +try: + # Python 3.6+ + module_not_found_error = ModuleNotFoundError +except Exception: + # Older Python versions + module_not_found_error = ImportError # type: ignore + + +class BaseClient: + """ + .. versionadded:: 2.0.0 + + The basic definition of a client that is used for sending data to Sentry. + """ + + spotlight = None # type: Optional[SpotlightClient] + + def __init__(self, options=None): + # type: (Optional[Dict[str, Any]]) -> None + self.options = options if options is not None else DEFAULT_OPTIONS # type: Dict[str, Any] + + self.transport = None # type: Optional[Transport] + self.monitor = None # type: Optional[Monitor] + self.log_batcher = None # type: Optional[LogBatcher] + self.metrics_batcher = None # type: Optional[MetricsBatcher] + + def __getstate__(self, *args, **kwargs): + # type: (*Any, **Any) -> Any + return {"options": {}} + + def __setstate__(self, *args, **kwargs): + # type: (*Any, **Any) -> None + pass + + @property + def dsn(self): + # type: () -> Optional[str] + return None + + def should_send_default_pii(self): + # type: () -> bool + return False + + def is_active(self): + # type: () -> bool + """ + .. versionadded:: 2.0.0 + + Returns whether the client is active (able to send data to Sentry) + """ + return False + + def capture_event(self, *args, **kwargs): + # type: (*Any, **Any) -> Optional[str] + return None + + def _capture_log(self, log): + # type: (Log) -> None + pass + + def _capture_metric(self, metric): + # type: (Metric) -> None + pass + + def capture_session(self, *args, **kwargs): + # type: (*Any, **Any) -> None + return None + + if TYPE_CHECKING: + + @overload + def get_integration(self, name_or_class): + # type: (str) -> Optional[Integration] + ... + + @overload + def get_integration(self, name_or_class): + # type: (type[I]) -> Optional[I] + ... + + def get_integration(self, name_or_class): + # type: (Union[str, type[Integration]]) -> Optional[Integration] + return None + + def close(self, *args, **kwargs): + # type: (*Any, **Any) -> None + return None + + def flush(self, *args, **kwargs): + # type: (*Any, **Any) -> None + return None + + def __enter__(self): + # type: () -> BaseClient + return self + + def __exit__(self, exc_type, exc_value, tb): + # type: (Any, Any, Any) -> None + return None + + +class NonRecordingClient(BaseClient): + """ + .. versionadded:: 2.0.0 + + A client that does not send any events to Sentry. This is used as a fallback when the Sentry SDK is not yet initialized. + """ + + pass + + +class _Client(BaseClient): + """ + The client is internally responsible for capturing the events and + forwarding them to sentry through the configured transport. It takes + the client options as keyword arguments and optionally the DSN as first + argument. + + Alias of :py:class:`sentry_sdk.Client`. (Was created for better intelisense support) + """ + + def __init__(self, *args, **kwargs): + # type: (*Any, **Any) -> None + super(_Client, self).__init__(options=get_options(*args, **kwargs)) + self._init_impl() + + def __getstate__(self): + # type: () -> Any + return {"options": self.options} + + def __setstate__(self, state): + # type: (Any) -> None + self.options = state["options"] + self._init_impl() + + def _setup_instrumentation(self, functions_to_trace): + # type: (Sequence[Dict[str, str]]) -> None + """ + Instruments the functions given in the list `functions_to_trace` with the `@sentry_sdk.tracing.trace` decorator. + """ + for function in functions_to_trace: + class_name = None + function_qualname = function["qualified_name"] + module_name, function_name = function_qualname.rsplit(".", 1) + + try: + # Try to import module and function + # ex: "mymodule.submodule.funcname" + + module_obj = import_module(module_name) + function_obj = getattr(module_obj, function_name) + setattr(module_obj, function_name, trace(function_obj)) + logger.debug("Enabled tracing for %s", function_qualname) + except module_not_found_error: + try: + # Try to import a class + # ex: "mymodule.submodule.MyClassName.member_function" + + module_name, class_name = module_name.rsplit(".", 1) + module_obj = import_module(module_name) + class_obj = getattr(module_obj, class_name) + function_obj = getattr(class_obj, function_name) + function_type = type(class_obj.__dict__[function_name]) + traced_function = trace(function_obj) + + if function_type in (staticmethod, classmethod): + traced_function = staticmethod(traced_function) + + setattr(class_obj, function_name, traced_function) + setattr(module_obj, class_name, class_obj) + logger.debug("Enabled tracing for %s", function_qualname) + + except Exception as e: + logger.warning( + "Can not enable tracing for '%s'. (%s) Please check your `functions_to_trace` parameter.", + function_qualname, + e, + ) + + except Exception as e: + logger.warning( + "Can not enable tracing for '%s'. (%s) Please check your `functions_to_trace` parameter.", + function_qualname, + e, + ) + + def _init_impl(self): + # type: () -> None + old_debug = _client_init_debug.get(False) + + def _capture_envelope(envelope): + # type: (Envelope) -> None + if self.transport is not None: + self.transport.capture_envelope(envelope) + + try: + _client_init_debug.set(self.options["debug"]) + self.transport = make_transport(self.options) + + self.monitor = None + if self.transport: + if self.options["enable_backpressure_handling"]: + self.monitor = Monitor(self.transport) + + self.session_flusher = SessionFlusher(capture_func=_capture_envelope) + + self.log_batcher = None + + if has_logs_enabled(self.options): + from sentry_sdk._log_batcher import LogBatcher + + self.log_batcher = LogBatcher(capture_func=_capture_envelope) + + self.metrics_batcher = None + + if has_metrics_enabled(self.options): + from sentry_sdk._metrics_batcher import MetricsBatcher + + self.metrics_batcher = MetricsBatcher(capture_func=_capture_envelope) + + max_request_body_size = ("always", "never", "small", "medium") + if self.options["max_request_body_size"] not in max_request_body_size: + raise ValueError( + "Invalid value for max_request_body_size. Must be one of {}".format( + max_request_body_size + ) + ) + + if self.options["_experiments"].get("otel_powered_performance", False): + logger.debug( + "[OTel] Enabling experimental OTel-powered performance monitoring." + ) + self.options["instrumenter"] = INSTRUMENTER.OTEL + if ( + "sentry_sdk.integrations.opentelemetry.integration.OpenTelemetryIntegration" + not in _DEFAULT_INTEGRATIONS + ): + _DEFAULT_INTEGRATIONS.append( + "sentry_sdk.integrations.opentelemetry.integration.OpenTelemetryIntegration", + ) + + self.integrations = setup_integrations( + self.options["integrations"], + with_defaults=self.options["default_integrations"], + with_auto_enabling_integrations=self.options[ + "auto_enabling_integrations" + ], + disabled_integrations=self.options["disabled_integrations"], + ) + + spotlight_config = self.options.get("spotlight") + if spotlight_config is None and "SENTRY_SPOTLIGHT" in os.environ: + spotlight_env_value = os.environ["SENTRY_SPOTLIGHT"] + spotlight_config = env_to_bool(spotlight_env_value, strict=True) + self.options["spotlight"] = ( + spotlight_config + if spotlight_config is not None + else spotlight_env_value + ) + + if self.options.get("spotlight"): + # This is intentionally here to prevent setting up spotlight + # stuff we don't need unless spotlight is explicitly enabled + from sentry_sdk.spotlight import setup_spotlight + + self.spotlight = setup_spotlight(self.options) + if not self.options["dsn"]: + sample_all = lambda *_args, **_kwargs: 1.0 + self.options["send_default_pii"] = True + self.options["error_sampler"] = sample_all + self.options["traces_sampler"] = sample_all + self.options["profiles_sampler"] = sample_all + + sdk_name = get_sdk_name(list(self.integrations.keys())) + SDK_INFO["name"] = sdk_name + logger.debug("Setting SDK name to '%s'", sdk_name) + + if has_profiling_enabled(self.options): + try: + setup_profiler(self.options) + except Exception as e: + logger.debug("Can not set up profiler. (%s)", e) + else: + try: + setup_continuous_profiler( + self.options, + sdk_info=SDK_INFO, + capture_func=_capture_envelope, + ) + except Exception as e: + logger.debug("Can not set up continuous profiler. (%s)", e) + + finally: + _client_init_debug.set(old_debug) + + self._setup_instrumentation(self.options.get("functions_to_trace", [])) + + if ( + self.monitor + or self.log_batcher + or has_profiling_enabled(self.options) + or isinstance(self.transport, BaseHttpTransport) + ): + # If we have anything on that could spawn a background thread, we + # need to check if it's safe to use them. + check_uwsgi_thread_support() + + def is_active(self): + # type: () -> bool + """ + .. versionadded:: 2.0.0 + + Returns whether the client is active (able to send data to Sentry) + """ + return True + + def should_send_default_pii(self): + # type: () -> bool + """ + .. versionadded:: 2.0.0 + + Returns whether the client should send default PII (Personally Identifiable Information) data to Sentry. + """ + return self.options.get("send_default_pii") or False + + @property + def dsn(self): + # type: () -> Optional[str] + """Returns the configured DSN as string.""" + return self.options["dsn"] + + def _prepare_event( + self, + event, # type: Event + hint, # type: Hint + scope, # type: Optional[Scope] + ): + # type: (...) -> Optional[Event] + + previous_total_spans = None # type: Optional[int] + previous_total_breadcrumbs = None # type: Optional[int] + + if event.get("timestamp") is None: + event["timestamp"] = datetime.now(timezone.utc) + + is_transaction = event.get("type") == "transaction" + + if scope is not None: + spans_before = len(cast(List[Dict[str, object]], event.get("spans", []))) + event_ = scope.apply_to_event(event, hint, self.options) + + # one of the event/error processors returned None + if event_ is None: + if self.transport: + self.transport.record_lost_event( + "event_processor", + data_category=("transaction" if is_transaction else "error"), + ) + if is_transaction: + self.transport.record_lost_event( + "event_processor", + data_category="span", + quantity=spans_before + 1, # +1 for the transaction itself + ) + return None + + event = event_ + spans_delta = spans_before - len( + cast(List[Dict[str, object]], event.get("spans", [])) + ) + if is_transaction and spans_delta > 0 and self.transport is not None: + self.transport.record_lost_event( + "event_processor", data_category="span", quantity=spans_delta + ) + + dropped_spans = event.pop("_dropped_spans", 0) + spans_delta # type: int + if dropped_spans > 0: + previous_total_spans = spans_before + dropped_spans + if scope._n_breadcrumbs_truncated > 0: + breadcrumbs = event.get("breadcrumbs", {}) + values = ( + breadcrumbs.get("values", []) + if not isinstance(breadcrumbs, AnnotatedValue) + else [] + ) + previous_total_breadcrumbs = ( + len(values) + scope._n_breadcrumbs_truncated + ) + + if ( + not is_transaction + and self.options["attach_stacktrace"] + and "exception" not in event + and "stacktrace" not in event + and "threads" not in event + ): + with capture_internal_exceptions(): + event["threads"] = { + "values": [ + { + "stacktrace": current_stacktrace( + include_local_variables=self.options.get( + "include_local_variables", True + ), + max_value_length=self.options.get( + "max_value_length", DEFAULT_MAX_VALUE_LENGTH + ), + ), + "crashed": False, + "current": True, + } + ] + } + + for key in "release", "environment", "server_name", "dist": + if event.get(key) is None and self.options[key] is not None: + event[key] = str(self.options[key]).strip() + if event.get("sdk") is None: + sdk_info = dict(SDK_INFO) + sdk_info["integrations"] = sorted(self.integrations.keys()) + event["sdk"] = sdk_info + + if event.get("platform") is None: + event["platform"] = "python" + + event = handle_in_app( + event, + self.options["in_app_exclude"], + self.options["in_app_include"], + self.options["project_root"], + ) + + if event is not None: + event_scrubber = self.options["event_scrubber"] + if event_scrubber: + event_scrubber.scrub_event(event) + + if previous_total_spans is not None: + event["spans"] = AnnotatedValue( + event.get("spans", []), {"len": previous_total_spans} + ) + if previous_total_breadcrumbs is not None: + event["breadcrumbs"] = AnnotatedValue( + event.get("breadcrumbs", []), {"len": previous_total_breadcrumbs} + ) + # Postprocess the event here so that annotated types do + # generally not surface in before_send + if event is not None: + event = cast( + "Event", + serialize( + cast("Dict[str, Any]", event), + max_request_body_size=self.options.get("max_request_body_size"), + max_value_length=self.options.get("max_value_length"), + custom_repr=self.options.get("custom_repr"), + ), + ) + + before_send = self.options["before_send"] + if ( + before_send is not None + and event is not None + and event.get("type") != "transaction" + ): + new_event = None + with capture_internal_exceptions(): + new_event = before_send(event, hint or {}) + if new_event is None: + logger.info("before send dropped event") + if self.transport: + self.transport.record_lost_event( + "before_send", data_category="error" + ) + + # If this is an exception, reset the DedupeIntegration. It still + # remembers the dropped exception as the last exception, meaning + # that if the same exception happens again and is not dropped + # in before_send, it'd get dropped by DedupeIntegration. + if event.get("exception"): + DedupeIntegration.reset_last_seen() + + event = new_event + + before_send_transaction = self.options["before_send_transaction"] + if ( + before_send_transaction is not None + and event is not None + and event.get("type") == "transaction" + ): + new_event = None + spans_before = len(cast(List[Dict[str, object]], event.get("spans", []))) + with capture_internal_exceptions(): + new_event = before_send_transaction(event, hint or {}) + if new_event is None: + logger.info("before send transaction dropped event") + if self.transport: + self.transport.record_lost_event( + reason="before_send", data_category="transaction" + ) + self.transport.record_lost_event( + reason="before_send", + data_category="span", + quantity=spans_before + 1, # +1 for the transaction itself + ) + else: + spans_delta = spans_before - len(new_event.get("spans", [])) + if spans_delta > 0 and self.transport is not None: + self.transport.record_lost_event( + reason="before_send", data_category="span", quantity=spans_delta + ) + + event = new_event + + return event + + def _is_ignored_error(self, event, hint): + # type: (Event, Hint) -> bool + exc_info = hint.get("exc_info") + if exc_info is None: + return False + + error = exc_info[0] + error_type_name = get_type_name(exc_info[0]) + error_full_name = "%s.%s" % (exc_info[0].__module__, error_type_name) + + for ignored_error in self.options["ignore_errors"]: + # String types are matched against the type name in the + # exception only + if isinstance(ignored_error, str): + if ignored_error == error_full_name or ignored_error == error_type_name: + return True + else: + if issubclass(error, ignored_error): + return True + + return False + + def _should_capture( + self, + event, # type: Event + hint, # type: Hint + scope=None, # type: Optional[Scope] + ): + # type: (...) -> bool + # Transactions are sampled independent of error events. + is_transaction = event.get("type") == "transaction" + if is_transaction: + return True + + ignoring_prevents_recursion = scope is not None and not scope._should_capture + if ignoring_prevents_recursion: + return False + + ignored_by_config_option = self._is_ignored_error(event, hint) + if ignored_by_config_option: + return False + + return True + + def _should_sample_error( + self, + event, # type: Event + hint, # type: Hint + ): + # type: (...) -> bool + error_sampler = self.options.get("error_sampler", None) + + if callable(error_sampler): + with capture_internal_exceptions(): + sample_rate = error_sampler(event, hint) + else: + sample_rate = self.options["sample_rate"] + + try: + not_in_sample_rate = sample_rate < 1.0 and random.random() >= sample_rate + except NameError: + logger.warning( + "The provided error_sampler raised an error. Defaulting to sampling the event." + ) + + # If the error_sampler raised an error, we should sample the event, since the default behavior + # (when no sample_rate or error_sampler is provided) is to sample all events. + not_in_sample_rate = False + except TypeError: + parameter, verb = ( + ("error_sampler", "returned") + if callable(error_sampler) + else ("sample_rate", "contains") + ) + logger.warning( + "The provided %s %s an invalid value of %s. The value should be a float or a bool. Defaulting to sampling the event." + % (parameter, verb, repr(sample_rate)) + ) + + # If the sample_rate has an invalid value, we should sample the event, since the default behavior + # (when no sample_rate or error_sampler is provided) is to sample all events. + not_in_sample_rate = False + + if not_in_sample_rate: + # because we will not sample this event, record a "lost event". + if self.transport: + self.transport.record_lost_event("sample_rate", data_category="error") + + return False + + return True + + def _update_session_from_event( + self, + session, # type: Session + event, # type: Event + ): + # type: (...) -> None + + crashed = False + errored = False + user_agent = None + + exceptions = (event.get("exception") or {}).get("values") + if exceptions: + errored = True + for error in exceptions: + if isinstance(error, AnnotatedValue): + error = error.value or {} + mechanism = error.get("mechanism") + if isinstance(mechanism, Mapping) and mechanism.get("handled") is False: + crashed = True + break + + user = event.get("user") + + if session.user_agent is None: + headers = (event.get("request") or {}).get("headers") + headers_dict = headers if isinstance(headers, dict) else {} + for k, v in headers_dict.items(): + if k.lower() == "user-agent": + user_agent = v + break + + session.update( + status="crashed" if crashed else None, + user=user, + user_agent=user_agent, + errors=session.errors + (errored or crashed), + ) + + def capture_event( + self, + event, # type: Event + hint=None, # type: Optional[Hint] + scope=None, # type: Optional[Scope] + ): + # type: (...) -> Optional[str] + """Captures an event. + + :param event: A ready-made event that can be directly sent to Sentry. + + :param hint: Contains metadata about the event that can be read from `before_send`, such as the original exception object or a HTTP request object. + + :param scope: An optional :py:class:`sentry_sdk.Scope` to apply to events. + + :returns: An event ID. May be `None` if there is no DSN set or of if the SDK decided to discard the event for other reasons. In such situations setting `debug=True` on `init()` may help. + """ + hint = dict(hint or ()) # type: Hint + + if not self._should_capture(event, hint, scope): + return None + + profile = event.pop("profile", None) + + event_id = event.get("event_id") + if event_id is None: + event["event_id"] = event_id = uuid.uuid4().hex + event_opt = self._prepare_event(event, hint, scope) + if event_opt is None: + return None + + # whenever we capture an event we also check if the session needs + # to be updated based on that information. + session = scope._session if scope else None + if session: + self._update_session_from_event(session, event) + + is_transaction = event_opt.get("type") == "transaction" + is_checkin = event_opt.get("type") == "check_in" + + if ( + not is_transaction + and not is_checkin + and not self._should_sample_error(event, hint) + ): + return None + + attachments = hint.get("attachments") + + trace_context = event_opt.get("contexts", {}).get("trace") or {} + dynamic_sampling_context = trace_context.pop("dynamic_sampling_context", {}) + + headers = { + "event_id": event_opt["event_id"], + "sent_at": format_timestamp(datetime.now(timezone.utc)), + } # type: dict[str, object] + + if dynamic_sampling_context: + headers["trace"] = dynamic_sampling_context + + envelope = Envelope(headers=headers) + + if is_transaction: + if isinstance(profile, Profile): + envelope.add_profile(profile.to_json(event_opt, self.options)) + envelope.add_transaction(event_opt) + elif is_checkin: + envelope.add_checkin(event_opt) + else: + envelope.add_event(event_opt) + + for attachment in attachments or (): + envelope.add_item(attachment.to_envelope_item()) + + return_value = None + if self.spotlight: + self.spotlight.capture_envelope(envelope) + return_value = event_id + + if self.transport is not None: + self.transport.capture_envelope(envelope) + return_value = event_id + + return return_value + + def _capture_log(self, log): + # type: (Optional[Log]) -> None + if not has_logs_enabled(self.options) or log is None: + return + + current_scope = sentry_sdk.get_current_scope() + isolation_scope = sentry_sdk.get_isolation_scope() + + log["attributes"]["sentry.sdk.name"] = SDK_INFO["name"] + log["attributes"]["sentry.sdk.version"] = SDK_INFO["version"] + + server_name = self.options.get("server_name") + if server_name is not None and SPANDATA.SERVER_ADDRESS not in log["attributes"]: + log["attributes"][SPANDATA.SERVER_ADDRESS] = server_name + + environment = self.options.get("environment") + if environment is not None and "sentry.environment" not in log["attributes"]: + log["attributes"]["sentry.environment"] = environment + + release = self.options.get("release") + if release is not None and "sentry.release" not in log["attributes"]: + log["attributes"]["sentry.release"] = release + + span = current_scope.span + if span is not None and "sentry.trace.parent_span_id" not in log["attributes"]: + log["attributes"]["sentry.trace.parent_span_id"] = span.span_id + + if log.get("trace_id") is None: + transaction = current_scope.transaction + propagation_context = isolation_scope.get_active_propagation_context() + if transaction is not None: + log["trace_id"] = transaction.trace_id + elif propagation_context is not None: + log["trace_id"] = propagation_context.trace_id + + # The user, if present, is always set on the isolation scope. + if isolation_scope._user is not None: + for log_attribute, user_attribute in ( + ("user.id", "id"), + ("user.name", "username"), + ("user.email", "email"), + ): + if ( + user_attribute in isolation_scope._user + and log_attribute not in log["attributes"] + ): + log["attributes"][log_attribute] = isolation_scope._user[ + user_attribute + ] + + # If debug is enabled, log the log to the console + debug = self.options.get("debug", False) + if debug: + logger.debug( + f"[Sentry Logs] [{log.get('severity_text')}] {log.get('body')}" + ) + + before_send_log = get_before_send_log(self.options) + if before_send_log is not None: + log = before_send_log(log, {}) + + if log is None: + return + + if self.log_batcher: + self.log_batcher.add(log) + + def _capture_metric(self, metric): + # type: (Optional[Metric]) -> None + if not has_metrics_enabled(self.options) or metric is None: + return + + isolation_scope = sentry_sdk.get_isolation_scope() + + metric["attributes"]["sentry.sdk.name"] = SDK_INFO["name"] + metric["attributes"]["sentry.sdk.version"] = SDK_INFO["version"] + + environment = self.options.get("environment") + if environment is not None and "sentry.environment" not in metric["attributes"]: + metric["attributes"]["sentry.environment"] = environment + + release = self.options.get("release") + if release is not None and "sentry.release" not in metric["attributes"]: + metric["attributes"]["sentry.release"] = release + + span = sentry_sdk.get_current_span() + metric["trace_id"] = "00000000-0000-0000-0000-000000000000" + + if span: + metric["trace_id"] = span.trace_id + metric["span_id"] = span.span_id + else: + propagation_context = isolation_scope.get_active_propagation_context() + if propagation_context and propagation_context.trace_id: + metric["trace_id"] = propagation_context.trace_id + + if isolation_scope._user is not None: + for metric_attribute, user_attribute in ( + ("user.id", "id"), + ("user.name", "username"), + ("user.email", "email"), + ): + if ( + user_attribute in isolation_scope._user + and metric_attribute not in metric["attributes"] + ): + metric["attributes"][metric_attribute] = isolation_scope._user[ + user_attribute + ] + + debug = self.options.get("debug", False) + if debug: + logger.debug( + f"[Sentry Metrics] [{metric.get('type')}] {metric.get('name')}: {metric.get('value')}" + ) + + before_send_metric = get_before_send_metric(self.options) + if before_send_metric is not None: + metric = before_send_metric(metric, {}) + + if metric is None: + return + + if self.metrics_batcher: + self.metrics_batcher.add(metric) + + def capture_session( + self, + session, # type: Session + ): + # type: (...) -> None + if not session.release: + logger.info("Discarded session update because of missing release") + else: + self.session_flusher.add_session(session) + + if TYPE_CHECKING: + + @overload + def get_integration(self, name_or_class): + # type: (str) -> Optional[Integration] + ... + + @overload + def get_integration(self, name_or_class): + # type: (type[I]) -> Optional[I] + ... + + def get_integration( + self, + name_or_class, # type: Union[str, Type[Integration]] + ): + # type: (...) -> Optional[Integration] + """Returns the integration for this client by name or class. + If the client does not have that integration then `None` is returned. + """ + if isinstance(name_or_class, str): + integration_name = name_or_class + elif name_or_class.identifier is not None: + integration_name = name_or_class.identifier + else: + raise ValueError("Integration has no name") + + return self.integrations.get(integration_name) + + def close( + self, + timeout=None, # type: Optional[float] + callback=None, # type: Optional[Callable[[int, float], None]] + ): + # type: (...) -> None + """ + Close the client and shut down the transport. Arguments have the same + semantics as :py:meth:`Client.flush`. + """ + if self.transport is not None: + self.flush(timeout=timeout, callback=callback) + self.session_flusher.kill() + if self.log_batcher is not None: + self.log_batcher.kill() + if self.metrics_batcher is not None: + self.metrics_batcher.kill() + if self.monitor: + self.monitor.kill() + self.transport.kill() + self.transport = None + + def flush( + self, + timeout=None, # type: Optional[float] + callback=None, # type: Optional[Callable[[int, float], None]] + ): + # type: (...) -> None + """ + Wait for the current events to be sent. + + :param timeout: Wait for at most `timeout` seconds. If no `timeout` is provided, the `shutdown_timeout` option value is used. + + :param callback: Is invoked with the number of pending events and the configured timeout. + """ + if self.transport is not None: + if timeout is None: + timeout = self.options["shutdown_timeout"] + self.session_flusher.flush() + if self.log_batcher is not None: + self.log_batcher.flush() + if self.metrics_batcher is not None: + self.metrics_batcher.flush() + self.transport.flush(timeout=timeout, callback=callback) + + def __enter__(self): + # type: () -> _Client + return self + + def __exit__(self, exc_type, exc_value, tb): + # type: (Any, Any, Any) -> None + self.close() + + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + # Make mypy, PyCharm and other static analyzers think `get_options` is a + # type to have nicer autocompletion for params. + # + # Use `ClientConstructor` to define the argument types of `init` and + # `Dict[str, Any]` to tell static analyzers about the return type. + + class get_options(ClientConstructor, Dict[str, Any]): # noqa: N801 + pass + + class Client(ClientConstructor, _Client): + pass + +else: + # Alias `get_options` for actual usage. Go through the lambda indirection + # to throw PyCharm off of the weakly typed signature (it would otherwise + # discover both the weakly typed signature of `_init` and our faked `init` + # type). + + get_options = (lambda: _get_options)() + Client = (lambda: _Client)() diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/consts.py b/.venv/lib/python3.12/site-packages/sentry_sdk/consts.py new file mode 100644 index 0000000000000000000000000000000000000000..2a3c9411be592c01eb62f50144c3308775ed6001 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/consts.py @@ -0,0 +1,1351 @@ +import itertools +from enum import Enum +from typing import TYPE_CHECKING + +# up top to prevent circular import due to integration import +# This is more or less an arbitrary large-ish value for now, so that we allow +# pretty long strings (like LLM prompts), but still have *some* upper limit +# until we verify that removing the trimming completely is safe. +DEFAULT_MAX_VALUE_LENGTH = 100_000 + +DEFAULT_MAX_STACK_FRAMES = 100 +DEFAULT_ADD_FULL_STACK = False + + +# Also needs to be at the top to prevent circular import +class EndpointType(Enum): + """ + The type of an endpoint. This is an enum, rather than a constant, for historical reasons + (the old /store endpoint). The enum also preserve future compatibility, in case we ever + have a new endpoint. + """ + + ENVELOPE = "envelope" + + +class CompressionAlgo(Enum): + GZIP = "gzip" + BROTLI = "br" + + +if TYPE_CHECKING: + import sentry_sdk + + from typing import Optional + from typing import Callable + from typing import Union + from typing import List + from typing import Type + from typing import Dict + from typing import Any + from typing import Sequence + from typing import Tuple + from typing import AbstractSet + from typing_extensions import Literal + from typing_extensions import TypedDict + + from sentry_sdk._types import ( + BreadcrumbProcessor, + ContinuousProfilerMode, + Event, + EventProcessor, + Hint, + Log, + MeasurementUnit, + Metric, + ProfilerMode, + TracesSampler, + TransactionProcessor, + ) + + # Experiments are feature flags to enable and disable certain unstable SDK + # functionality. Changing them from the defaults (`None`) in production + # code is highly discouraged. They are not subject to any stability + # guarantees such as the ones from semantic versioning. + Experiments = TypedDict( + "Experiments", + { + "max_spans": Optional[int], + "max_flags": Optional[int], + "record_sql_params": Optional[bool], + "continuous_profiling_auto_start": Optional[bool], + "continuous_profiling_mode": Optional[ContinuousProfilerMode], + "otel_powered_performance": Optional[bool], + "transport_zlib_compression_level": Optional[int], + "transport_compression_level": Optional[int], + "transport_compression_algo": Optional[CompressionAlgo], + "transport_num_pools": Optional[int], + "transport_http2": Optional[bool], + "enable_logs": Optional[bool], + "before_send_log": Optional[Callable[[Log, Hint], Optional[Log]]], + "enable_metrics": Optional[bool], + "before_send_metric": Optional[Callable[[Metric, Hint], Optional[Metric]]], + }, + total=False, + ) + +DEFAULT_QUEUE_SIZE = 100 +DEFAULT_MAX_BREADCRUMBS = 100 +MATCH_ALL = r".*" + +FALSE_VALUES = [ + "false", + "no", + "off", + "n", + "0", +] + + +class SPANTEMPLATE(str, Enum): + DEFAULT = "default" + AI_AGENT = "ai_agent" + AI_TOOL = "ai_tool" + AI_CHAT = "ai_chat" + + def __str__(self): + # type: () -> str + return self.value + + +class INSTRUMENTER: + SENTRY = "sentry" + OTEL = "otel" + + +class SPANDATA: + """ + Additional information describing the type of the span. + See: https://develop.sentry.dev/sdk/performance/span-data-conventions/ + """ + + AI_CITATIONS = "ai.citations" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_* attributes instead. + + References or sources cited by the AI model in its response. + Example: ["Smith et al. 2020", "Jones 2019"] + """ + + AI_DOCUMENTS = "ai.documents" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_* attributes instead. + + Documents or content chunks used as context for the AI model. + Example: ["doc1.txt", "doc2.pdf"] + """ + + AI_FINISH_REASON = "ai.finish_reason" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_RESPONSE_FINISH_REASONS instead. + + The reason why the model stopped generating. + Example: "length" + """ + + AI_FREQUENCY_PENALTY = "ai.frequency_penalty" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_REQUEST_FREQUENCY_PENALTY instead. + + Used to reduce repetitiveness of generated tokens. + Example: 0.5 + """ + + AI_FUNCTION_CALL = "ai.function_call" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_RESPONSE_TOOL_CALLS instead. + + For an AI model call, the function that was called. This is deprecated for OpenAI, and replaced by tool_calls + """ + + AI_GENERATION_ID = "ai.generation_id" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_RESPONSE_ID instead. + + Unique identifier for the completion. + Example: "gen_123abc" + """ + + AI_INPUT_MESSAGES = "ai.input_messages" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_REQUEST_MESSAGES instead. + + The input messages to an LLM call. + Example: [{"role": "user", "message": "hello"}] + """ + + AI_LOGIT_BIAS = "ai.logit_bias" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_* attributes instead. + + For an AI model call, the logit bias + """ + + AI_METADATA = "ai.metadata" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_* attributes instead. + + Extra metadata passed to an AI pipeline step. + Example: {"executed_function": "add_integers"} + """ + + AI_MODEL_ID = "ai.model_id" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_REQUEST_MODEL or GEN_AI_RESPONSE_MODEL instead. + + The unique descriptor of the model being executed. + Example: gpt-4 + """ + + AI_PIPELINE_NAME = "ai.pipeline.name" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_PIPELINE_NAME instead. + + Name of the AI pipeline or chain being executed. + Example: "qa-pipeline" + """ + + AI_PREAMBLE = "ai.preamble" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_* attributes instead. + + For an AI model call, the preamble parameter. + Preambles are a part of the prompt used to adjust the model's overall behavior and conversation style. + Example: "You are now a clown." + """ + + AI_PRESENCE_PENALTY = "ai.presence_penalty" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_REQUEST_PRESENCE_PENALTY instead. + + Used to reduce repetitiveness of generated tokens. + Example: 0.5 + """ + + AI_RAW_PROMPTING = "ai.raw_prompting" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_* attributes instead. + + Minimize pre-processing done to the prompt sent to the LLM. + Example: true + """ + + AI_RESPONSE_FORMAT = "ai.response_format" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_* attributes instead. + + For an AI model call, the format of the response + """ + + AI_RESPONSES = "ai.responses" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_RESPONSE_TEXT instead. + + The responses to an AI model call. Always as a list. + Example: ["hello", "world"] + """ + + AI_SEARCH_QUERIES = "ai.search_queries" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_* attributes instead. + + Queries used to search for relevant context or documents. + Example: ["climate change effects", "renewable energy"] + """ + + AI_SEARCH_REQUIRED = "ai.is_search_required" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_* attributes instead. + + Boolean indicating if the model needs to perform a search. + Example: true + """ + + AI_SEARCH_RESULTS = "ai.search_results" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_* attributes instead. + + Results returned from search queries for context. + Example: ["Result 1", "Result 2"] + """ + + AI_SEED = "ai.seed" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_REQUEST_SEED instead. + + The seed, ideally models given the same seed and same other parameters will produce the exact same output. + Example: 123.45 + """ + + AI_STREAMING = "ai.streaming" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_RESPONSE_STREAMING instead. + + Whether or not the AI model call's response was streamed back asynchronously + Example: true + """ + + AI_TAGS = "ai.tags" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_* attributes instead. + + Tags that describe an AI pipeline step. + Example: {"executed_function": "add_integers"} + """ + + AI_TEMPERATURE = "ai.temperature" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_REQUEST_TEMPERATURE instead. + + For an AI model call, the temperature parameter. Temperature essentially means how random the output will be. + Example: 0.5 + """ + + AI_TEXTS = "ai.texts" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_* attributes instead. + + Raw text inputs provided to the model. + Example: ["What is machine learning?"] + """ + + AI_TOP_K = "ai.top_k" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_REQUEST_TOP_K instead. + + For an AI model call, the top_k parameter. Top_k essentially controls how random the output will be. + Example: 35 + """ + + AI_TOP_P = "ai.top_p" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_REQUEST_TOP_P instead. + + For an AI model call, the top_p parameter. Top_p essentially controls how random the output will be. + Example: 0.5 + """ + + AI_TOOL_CALLS = "ai.tool_calls" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_RESPONSE_TOOL_CALLS instead. + + For an AI model call, the function that was called. This is deprecated for OpenAI, and replaced by tool_calls + """ + + AI_TOOLS = "ai.tools" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_REQUEST_AVAILABLE_TOOLS instead. + + For an AI model call, the functions that are available + """ + + AI_WARNINGS = "ai.warnings" + """ + .. deprecated:: + This attribute is deprecated. Use GEN_AI_* attributes instead. + + Warning messages generated during model execution. + Example: ["Token limit exceeded"] + """ + + CACHE_HIT = "cache.hit" + """ + A boolean indicating whether the requested data was found in the cache. + Example: true + """ + + CACHE_ITEM_SIZE = "cache.item_size" + """ + The size of the requested data in bytes. + Example: 58 + """ + + CACHE_KEY = "cache.key" + """ + The key of the requested data. + Example: template.cache.some_item.867da7e2af8e6b2f3aa7213a4080edb3 + """ + + CODE_FILEPATH = "code.filepath" + """ + The source code file name that identifies the code unit as uniquely as possible (preferably an absolute file path). + Example: "/app/myapplication/http/handler/server.py" + """ + + CODE_FUNCTION = "code.function" + """ + The method or function name, or equivalent (usually rightmost part of the code unit's name). + Example: "server_request" + """ + + CODE_LINENO = "code.lineno" + """ + The line number in `code.filepath` best representing the operation. It SHOULD point within the code unit named in `code.function`. + Example: 42 + """ + + CODE_NAMESPACE = "code.namespace" + """ + The "namespace" within which `code.function` is defined. Usually the qualified class or module name, such that `code.namespace` + some separator + `code.function` form a unique identifier for the code unit. + Example: "http.handler" + """ + + DB_MONGODB_COLLECTION = "db.mongodb.collection" + """ + The MongoDB collection being accessed within the database. + See: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/database/mongodb.md#attributes + Example: public.users; customers + """ + + DB_NAME = "db.name" + """ + The name of the database being accessed. For commands that switch the database, this should be set to the target database (even if the command fails). + Example: myDatabase + """ + + DB_OPERATION = "db.operation" + """ + The name of the operation being executed, e.g. the MongoDB command name such as findAndModify, or the SQL keyword. + See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md + Example: findAndModify, HMSET, SELECT + """ + + DB_SYSTEM = "db.system" + """ + An identifier for the database management system (DBMS) product being used. + See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md + Example: postgresql + """ + + DB_USER = "db.user" + """ + The name of the database user used for connecting to the database. + See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md + Example: my_user + """ + + GEN_AI_AGENT_NAME = "gen_ai.agent.name" + """ + The name of the agent being used. + Example: "ResearchAssistant" + """ + + GEN_AI_CHOICE = "gen_ai.choice" + """ + The model's response message. + Example: "The weather in Paris is rainy and overcast, with temperatures around 57°F" + """ + + GEN_AI_OPERATION_NAME = "gen_ai.operation.name" + """ + The name of the operation being performed. + Example: "chat" + """ + + GEN_AI_PIPELINE_NAME = "gen_ai.pipeline.name" + """ + Name of the AI pipeline or chain being executed. + Example: "qa-pipeline" + """ + + GEN_AI_RESPONSE_FINISH_REASONS = "gen_ai.response.finish_reasons" + """ + The reason why the model stopped generating. + Example: "COMPLETE" + """ + + GEN_AI_RESPONSE_ID = "gen_ai.response.id" + """ + Unique identifier for the completion. + Example: "gen_123abc" + """ + + GEN_AI_RESPONSE_MODEL = "gen_ai.response.model" + """ + Exact model identifier used to generate the response + Example: gpt-4o-mini-2024-07-18 + """ + + GEN_AI_RESPONSE_STREAMING = "gen_ai.response.streaming" + """ + Whether or not the AI model call's response was streamed back asynchronously + Example: true + """ + + GEN_AI_RESPONSE_TEXT = "gen_ai.response.text" + """ + The model's response text messages. + Example: ["The weather in Paris is rainy and overcast, with temperatures around 57°F", "The weather in London is sunny and warm, with temperatures around 65°F"] + """ + + GEN_AI_RESPONSE_TOOL_CALLS = "gen_ai.response.tool_calls" + """ + The tool calls in the model's response. + Example: [{"name": "get_weather", "arguments": {"location": "Paris"}}] + """ + + GEN_AI_REQUEST_AVAILABLE_TOOLS = "gen_ai.request.available_tools" + """ + The available tools for the model. + Example: [{"name": "get_weather", "description": "Get the weather for a given location"}, {"name": "get_news", "description": "Get the news for a given topic"}] + """ + + GEN_AI_REQUEST_FREQUENCY_PENALTY = "gen_ai.request.frequency_penalty" + """ + The frequency penalty parameter used to reduce repetitiveness of generated tokens. + Example: 0.1 + """ + + GEN_AI_REQUEST_MAX_TOKENS = "gen_ai.request.max_tokens" + """ + The maximum number of tokens to generate in the response. + Example: 2048 + """ + + GEN_AI_REQUEST_MESSAGES = "gen_ai.request.messages" + """ + The messages passed to the model. The "content" can be a string or an array of objects. + Example: [{role: "system", "content: "Generate a random number."}, {"role": "user", "content": [{"text": "Generate a random number between 0 and 10.", "type": "text"}]}] + """ + + GEN_AI_REQUEST_MODEL = "gen_ai.request.model" + """ + The model identifier being used for the request. + Example: "gpt-4-turbo" + """ + + GEN_AI_REQUEST_PRESENCE_PENALTY = "gen_ai.request.presence_penalty" + """ + The presence penalty parameter used to reduce repetitiveness of generated tokens. + Example: 0.1 + """ + + GEN_AI_REQUEST_SEED = "gen_ai.request.seed" + """ + The seed, ideally models given the same seed and same other parameters will produce the exact same output. + Example: "1234567890" + """ + + GEN_AI_REQUEST_TEMPERATURE = "gen_ai.request.temperature" + """ + The temperature parameter used to control randomness in the output. + Example: 0.7 + """ + + GEN_AI_REQUEST_TOP_K = "gen_ai.request.top_k" + """ + Limits the model to only consider the K most likely next tokens, where K is an integer (e.g., top_k=20 means only the 20 highest probability tokens are considered). + Example: 35 + """ + + GEN_AI_REQUEST_TOP_P = "gen_ai.request.top_p" + """ + The top_p parameter used to control diversity via nucleus sampling. + Example: 1.0 + """ + + GEN_AI_SYSTEM = "gen_ai.system" + """ + The name of the AI system being used. + Example: "openai" + """ + + GEN_AI_TOOL_DESCRIPTION = "gen_ai.tool.description" + """ + The description of the tool being used. + Example: "Searches the web for current information about a topic" + """ + + GEN_AI_TOOL_INPUT = "gen_ai.tool.input" + """ + The input of the tool being used. + Example: {"location": "Paris"} + """ + + GEN_AI_TOOL_NAME = "gen_ai.tool.name" + """ + The name of the tool being used. + Example: "web_search" + """ + + GEN_AI_TOOL_OUTPUT = "gen_ai.tool.output" + """ + The output of the tool being used. + Example: "rainy, 57°F" + """ + + GEN_AI_TOOL_TYPE = "gen_ai.tool.type" + """ + The type of tool being used. + Example: "function" + """ + + GEN_AI_USAGE_INPUT_TOKENS = "gen_ai.usage.input_tokens" + """ + The number of tokens in the input. + Example: 150 + """ + + GEN_AI_USAGE_INPUT_TOKENS_CACHED = "gen_ai.usage.input_tokens.cached" + """ + The number of cached tokens in the input. + Example: 50 + """ + + GEN_AI_USAGE_OUTPUT_TOKENS = "gen_ai.usage.output_tokens" + """ + The number of tokens in the output. + Example: 250 + """ + + GEN_AI_USAGE_OUTPUT_TOKENS_REASONING = "gen_ai.usage.output_tokens.reasoning" + """ + The number of tokens used for reasoning in the output. + Example: 75 + """ + + GEN_AI_USAGE_TOTAL_TOKENS = "gen_ai.usage.total_tokens" + """ + The total number of tokens used (input + output). + Example: 400 + """ + + GEN_AI_USER_MESSAGE = "gen_ai.user.message" + """ + The user message passed to the model. + Example: "What's the weather in Paris?" + """ + + HTTP_FRAGMENT = "http.fragment" + """ + The Fragments present in the URL. + Example: #foo=bar + """ + + HTTP_METHOD = "http.method" + """ + The HTTP method used. + Example: GET + """ + + HTTP_QUERY = "http.query" + """ + The Query string present in the URL. + Example: ?foo=bar&bar=baz + """ + + HTTP_STATUS_CODE = "http.response.status_code" + """ + The HTTP status code as an integer. + Example: 418 + """ + + MESSAGING_DESTINATION_NAME = "messaging.destination.name" + """ + The destination name where the message is being consumed from, + e.g. the queue name or topic. + """ + + MESSAGING_MESSAGE_ID = "messaging.message.id" + """ + The message's identifier. + """ + + MESSAGING_MESSAGE_RECEIVE_LATENCY = "messaging.message.receive.latency" + """ + The latency between when the task was enqueued and when it was started to be processed. + """ + + MESSAGING_MESSAGE_RETRY_COUNT = "messaging.message.retry.count" + """ + Number of retries/attempts to process a message. + """ + + MESSAGING_SYSTEM = "messaging.system" + """ + The messaging system's name, e.g. `kafka`, `aws_sqs` + """ + + NETWORK_PEER_ADDRESS = "network.peer.address" + """ + Peer address of the network connection - IP address or Unix domain socket name. + Example: 10.1.2.80, /tmp/my.sock, localhost + """ + + NETWORK_PEER_PORT = "network.peer.port" + """ + Peer port number of the network connection. + Example: 6379 + """ + + PROFILER_ID = "profiler_id" + """ + Label identifying the profiler id that the span occurred in. This should be a string. + Example: "5249fbada8d5416482c2f6e47e337372" + """ + + SERVER_ADDRESS = "server.address" + """ + Name of the database host. + Example: example.com + """ + + SERVER_PORT = "server.port" + """ + Logical server port number + Example: 80; 8080; 443 + """ + + SERVER_SOCKET_ADDRESS = "server.socket.address" + """ + Physical server IP address or Unix socket address. + Example: 10.5.3.2 + """ + + SERVER_SOCKET_PORT = "server.socket.port" + """ + Physical server port. + Recommended: If different than server.port. + Example: 16456 + """ + + THREAD_ID = "thread.id" + """ + Identifier of a thread from where the span originated. This should be a string. + Example: "7972576320" + """ + + THREAD_NAME = "thread.name" + """ + Label identifying a thread from where the span originated. This should be a string. + Example: "MainThread" + """ + + +class SPANSTATUS: + """ + The status of a Sentry span. + + See: https://develop.sentry.dev/sdk/event-payloads/contexts/#trace-context + """ + + ABORTED = "aborted" + ALREADY_EXISTS = "already_exists" + CANCELLED = "cancelled" + DATA_LOSS = "data_loss" + DEADLINE_EXCEEDED = "deadline_exceeded" + ERROR = "error" # OTel status code: https://opentelemetry.io/docs/concepts/signals/traces/#span-status + FAILED_PRECONDITION = "failed_precondition" + INTERNAL_ERROR = "internal_error" + INVALID_ARGUMENT = "invalid_argument" + NOT_FOUND = "not_found" + OK = "ok" # HTTP 200 and OTel status code: https://opentelemetry.io/docs/concepts/signals/traces/#span-status + OUT_OF_RANGE = "out_of_range" + PERMISSION_DENIED = "permission_denied" + RESOURCE_EXHAUSTED = "resource_exhausted" + UNAUTHENTICATED = "unauthenticated" + UNAVAILABLE = "unavailable" + UNIMPLEMENTED = "unimplemented" + UNKNOWN_ERROR = "unknown_error" + UNSET = "unset" # OTel status code: https://opentelemetry.io/docs/concepts/signals/traces/#span-status + + +class OP: + ANTHROPIC_MESSAGES_CREATE = "ai.messages.create.anthropic" + CACHE_GET = "cache.get" + CACHE_PUT = "cache.put" + COHERE_CHAT_COMPLETIONS_CREATE = "ai.chat_completions.create.cohere" + COHERE_EMBEDDINGS_CREATE = "ai.embeddings.create.cohere" + DB = "db" + DB_REDIS = "db.redis" + EVENT_DJANGO = "event.django" + FUNCTION = "function" + FUNCTION_AWS = "function.aws" + FUNCTION_GCP = "function.gcp" + GEN_AI_CHAT = "gen_ai.chat" + GEN_AI_CREATE_AGENT = "gen_ai.create_agent" + GEN_AI_EMBEDDINGS = "gen_ai.embeddings" + GEN_AI_EXECUTE_TOOL = "gen_ai.execute_tool" + GEN_AI_GENERATE_TEXT = "gen_ai.generate_text" + GEN_AI_HANDOFF = "gen_ai.handoff" + GEN_AI_PIPELINE = "gen_ai.pipeline" + GEN_AI_INVOKE_AGENT = "gen_ai.invoke_agent" + GEN_AI_RESPONSES = "gen_ai.responses" + GRAPHQL_EXECUTE = "graphql.execute" + GRAPHQL_MUTATION = "graphql.mutation" + GRAPHQL_PARSE = "graphql.parse" + GRAPHQL_RESOLVE = "graphql.resolve" + GRAPHQL_SUBSCRIPTION = "graphql.subscription" + GRAPHQL_QUERY = "graphql.query" + GRAPHQL_VALIDATE = "graphql.validate" + GRPC_CLIENT = "grpc.client" + GRPC_SERVER = "grpc.server" + HTTP_CLIENT = "http.client" + HTTP_CLIENT_STREAM = "http.client.stream" + HTTP_SERVER = "http.server" + MIDDLEWARE_DJANGO = "middleware.django" + MIDDLEWARE_LITESTAR = "middleware.litestar" + MIDDLEWARE_LITESTAR_RECEIVE = "middleware.litestar.receive" + MIDDLEWARE_LITESTAR_SEND = "middleware.litestar.send" + MIDDLEWARE_STARLETTE = "middleware.starlette" + MIDDLEWARE_STARLETTE_RECEIVE = "middleware.starlette.receive" + MIDDLEWARE_STARLETTE_SEND = "middleware.starlette.send" + MIDDLEWARE_STARLITE = "middleware.starlite" + MIDDLEWARE_STARLITE_RECEIVE = "middleware.starlite.receive" + MIDDLEWARE_STARLITE_SEND = "middleware.starlite.send" + HUGGINGFACE_HUB_CHAT_COMPLETIONS_CREATE = ( + "ai.chat_completions.create.huggingface_hub" + ) + QUEUE_PROCESS = "queue.process" + QUEUE_PUBLISH = "queue.publish" + QUEUE_SUBMIT_ARQ = "queue.submit.arq" + QUEUE_TASK_ARQ = "queue.task.arq" + QUEUE_SUBMIT_CELERY = "queue.submit.celery" + QUEUE_TASK_CELERY = "queue.task.celery" + QUEUE_TASK_RQ = "queue.task.rq" + QUEUE_SUBMIT_HUEY = "queue.submit.huey" + QUEUE_TASK_HUEY = "queue.task.huey" + QUEUE_SUBMIT_RAY = "queue.submit.ray" + QUEUE_TASK_RAY = "queue.task.ray" + QUEUE_TASK_DRAMATIQ = "queue.task.dramatiq" + SUBPROCESS = "subprocess" + SUBPROCESS_WAIT = "subprocess.wait" + SUBPROCESS_COMMUNICATE = "subprocess.communicate" + TEMPLATE_RENDER = "template.render" + VIEW_RENDER = "view.render" + VIEW_RESPONSE_RENDER = "view.response.render" + WEBSOCKET_SERVER = "websocket.server" + SOCKET_CONNECTION = "socket.connection" + SOCKET_DNS = "socket.dns" + + +# This type exists to trick mypy and PyCharm into thinking `init` and `Client` +# take these arguments (even though they take opaque **kwargs) +class ClientConstructor: + def __init__( + self, + dsn=None, # type: Optional[str] + *, + max_breadcrumbs=DEFAULT_MAX_BREADCRUMBS, # type: int + release=None, # type: Optional[str] + environment=None, # type: Optional[str] + server_name=None, # type: Optional[str] + shutdown_timeout=2, # type: float + integrations=[], # type: Sequence[sentry_sdk.integrations.Integration] # noqa: B006 + in_app_include=[], # type: List[str] # noqa: B006 + in_app_exclude=[], # type: List[str] # noqa: B006 + default_integrations=True, # type: bool + dist=None, # type: Optional[str] + transport=None, # type: Optional[Union[sentry_sdk.transport.Transport, Type[sentry_sdk.transport.Transport], Callable[[Event], None]]] + transport_queue_size=DEFAULT_QUEUE_SIZE, # type: int + sample_rate=1.0, # type: float + send_default_pii=None, # type: Optional[bool] + http_proxy=None, # type: Optional[str] + https_proxy=None, # type: Optional[str] + ignore_errors=[], # type: Sequence[Union[type, str]] # noqa: B006 + max_request_body_size="medium", # type: str + socket_options=None, # type: Optional[List[Tuple[int, int, int | bytes]]] + keep_alive=None, # type: Optional[bool] + before_send=None, # type: Optional[EventProcessor] + before_breadcrumb=None, # type: Optional[BreadcrumbProcessor] + debug=None, # type: Optional[bool] + attach_stacktrace=False, # type: bool + ca_certs=None, # type: Optional[str] + propagate_traces=True, # type: bool + traces_sample_rate=None, # type: Optional[float] + traces_sampler=None, # type: Optional[TracesSampler] + profiles_sample_rate=None, # type: Optional[float] + profiles_sampler=None, # type: Optional[TracesSampler] + profiler_mode=None, # type: Optional[ProfilerMode] + profile_lifecycle="manual", # type: Literal["manual", "trace"] + profile_session_sample_rate=None, # type: Optional[float] + auto_enabling_integrations=True, # type: bool + disabled_integrations=None, # type: Optional[Sequence[sentry_sdk.integrations.Integration]] + auto_session_tracking=True, # type: bool + send_client_reports=True, # type: bool + _experiments={}, # type: Experiments # noqa: B006 + proxy_headers=None, # type: Optional[Dict[str, str]] + instrumenter=INSTRUMENTER.SENTRY, # type: Optional[str] + before_send_transaction=None, # type: Optional[TransactionProcessor] + project_root=None, # type: Optional[str] + enable_tracing=None, # type: Optional[bool] + include_local_variables=True, # type: Optional[bool] + include_source_context=True, # type: Optional[bool] + trace_propagation_targets=[ # noqa: B006 + MATCH_ALL + ], # type: Optional[Sequence[str]] + functions_to_trace=[], # type: Sequence[Dict[str, str]] # noqa: B006 + event_scrubber=None, # type: Optional[sentry_sdk.scrubber.EventScrubber] + max_value_length=DEFAULT_MAX_VALUE_LENGTH, # type: int + enable_backpressure_handling=True, # type: bool + error_sampler=None, # type: Optional[Callable[[Event, Hint], Union[float, bool]]] + enable_db_query_source=True, # type: bool + db_query_source_threshold_ms=100, # type: int + enable_http_request_source=False, # type: bool + http_request_source_threshold_ms=100, # type: int + spotlight=None, # type: Optional[Union[bool, str]] + cert_file=None, # type: Optional[str] + key_file=None, # type: Optional[str] + custom_repr=None, # type: Optional[Callable[..., Optional[str]]] + add_full_stack=DEFAULT_ADD_FULL_STACK, # type: bool + max_stack_frames=DEFAULT_MAX_STACK_FRAMES, # type: Optional[int] + enable_logs=False, # type: bool + before_send_log=None, # type: Optional[Callable[[Log, Hint], Optional[Log]]] + trace_ignore_status_codes=frozenset(), # type: AbstractSet[int] + ): + # type: (...) -> None + """Initialize the Sentry SDK with the given parameters. All parameters described here can be used in a call to `sentry_sdk.init()`. + + :param dsn: The DSN tells the SDK where to send the events. + + If this option is not set, the SDK will just not send any data. + + The `dsn` config option takes precedence over the environment variable. + + Learn more about `DSN utilization `_. + + :param debug: Turns debug mode on or off. + + When `True`, the SDK will attempt to print out debugging information. This can be useful if something goes + wrong with event sending. + + The default is always `False`. It's generally not recommended to turn it on in production because of the + increase in log output. + + The `debug` config option takes precedence over the environment variable. + + :param release: Sets the release. + + If not set, the SDK will try to automatically configure a release out of the box but it's a better idea to + manually set it to guarantee that the release is in sync with your deploy integrations. + + Release names are strings, but some formats are detected by Sentry and might be rendered differently. + + See `the releases documentation `_ to learn how the SDK tries to + automatically configure a release. + + The `release` config option takes precedence over the environment variable. + + Learn more about how to send release data so Sentry can tell you about regressions between releases and + identify the potential source in `the product documentation `_. + + :param environment: Sets the environment. This string is freeform and set to `production` by default. + + A release can be associated with more than one environment to separate them in the UI (think `staging` vs + `production` or similar). + + The `environment` config option takes precedence over the environment variable. + + :param dist: The distribution of the application. + + Distributions are used to disambiguate build or deployment variants of the same release of an application. + + The dist can be for example a build number. + + :param sample_rate: Configures the sample rate for error events, in the range of `0.0` to `1.0`. + + The default is `1.0`, which means that 100% of error events will be sent. If set to `0.1`, only 10% of + error events will be sent. + + Events are picked randomly. + + :param error_sampler: Dynamically configures the sample rate for error events on a per-event basis. + + This configuration option accepts a function, which takes two parameters (the `event` and the `hint`), and + which returns a boolean (indicating whether the event should be sent to Sentry) or a floating-point number + between `0.0` and `1.0`, inclusive. + + The number indicates the probability the event is sent to Sentry; the SDK will randomly decide whether to + send the event with the given probability. + + If this configuration option is specified, the `sample_rate` option is ignored. + + :param ignore_errors: A list of exception class names that shouldn't be sent to Sentry. + + Errors that are an instance of these exceptions or a subclass of them, will be filtered out before they're + sent to Sentry. + + By default, all errors are sent. + + :param max_breadcrumbs: This variable controls the total amount of breadcrumbs that should be captured. + + This defaults to `100`, but you can set this to any number. + + However, you should be aware that Sentry has a `maximum payload size `_ + and any events exceeding that payload size will be dropped. + + :param attach_stacktrace: When enabled, stack traces are automatically attached to all messages logged. + + Stack traces are always attached to exceptions; however, when this option is set, stack traces are also + sent with messages. + + This option means that stack traces appear next to all log messages. + + Grouping in Sentry is different for events with stack traces and without. As a result, you will get new + groups as you enable or disable this flag for certain events. + + :param send_default_pii: If this flag is enabled, `certain personally identifiable information (PII) + `_ is added by active integrations. + + If you enable this option, be sure to manually remove what you don't want to send using our features for + managing `Sensitive Data `_. + + :param event_scrubber: Scrubs the event payload for sensitive information such as cookies, sessions, and + passwords from a `denylist`. + + It can additionally be used to scrub from another `pii_denylist` if `send_default_pii` is disabled. + + See how to `configure the scrubber here `_. + + :param include_source_context: When enabled, source context will be included in events sent to Sentry. + + This source context includes the five lines of code above and below the line of code where an error + happened. + + :param include_local_variables: When enabled, the SDK will capture a snapshot of local variables to send with + the event to help with debugging. + + :param add_full_stack: When capturing errors, Sentry stack traces typically only include frames that start the + moment an error occurs. + + But if the `add_full_stack` option is enabled (set to `True`), all frames from the start of execution will + be included in the stack trace sent to Sentry. + + :param max_stack_frames: This option limits the number of stack frames that will be captured when + `add_full_stack` is enabled. + + :param server_name: This option can be used to supply a server name. + + When provided, the name of the server is sent along and persisted in the event. + + For many integrations, the server name actually corresponds to the device hostname, even in situations + where the machine is not actually a server. + + :param project_root: The full path to the root directory of your application. + + The `project_root` is used to mark frames in a stack trace either as being in your application or outside + of the application. + + :param in_app_include: A list of string prefixes of module names that belong to the app. + + This option takes precedence over `in_app_exclude`. + + Sentry differentiates stack frames that are directly related to your application ("in application") from + stack frames that come from other packages such as the standard library, frameworks, or other dependencies. + + The application package is automatically marked as `inApp`. + + The difference is visible in [sentry.io](https://sentry.io), where only the "in application" frames are + displayed by default. + + :param in_app_exclude: A list of string prefixes of module names that do not belong to the app, but rather to + third-party packages. + + Modules considered not part of the app will be hidden from stack traces by default. + + This option can be overridden using `in_app_include`. + + :param max_request_body_size: This parameter controls whether integrations should capture HTTP request bodies. + It can be set to one of the following values: + + - `never`: Request bodies are never sent. + - `small`: Only small request bodies will be captured. The cutoff for small depends on the SDK (typically + 4KB). + - `medium`: Medium and small requests will be captured (typically 10KB). + - `always`: The SDK will always capture the request body as long as Sentry can make sense of it. + + Please note that the Sentry server [limits HTTP request body size](https://develop.sentry.dev/sdk/ + expected-features/data-handling/#variable-size). The server always enforces its size limit, regardless of + how you configure this option. + + :param max_value_length: The number of characters after which the values containing text in the event payload + will be truncated. + + WARNING: If the value you set for this is exceptionally large, the event may exceed 1 MiB and will be + dropped by Sentry. + + :param ca_certs: A path to an alternative CA bundle file in PEM-format. + + :param send_client_reports: Set this boolean to `False` to disable sending of client reports. + + Client reports allow the client to send status reports about itself to Sentry, such as information about + events that were dropped before being sent. + + :param integrations: List of integrations to enable in addition to `auto-enabling integrations (overview) + `_. + + This setting can be used to override the default config options for a specific auto-enabling integration + or to add an integration that is not auto-enabled. + + :param disabled_integrations: List of integrations that will be disabled. + + This setting can be used to explicitly turn off specific `auto-enabling integrations (list) + `_ or + `default `_ integrations. + + :param auto_enabling_integrations: Configures whether `auto-enabling integrations (configuration) + `_ should be enabled. + + When set to `False`, no auto-enabling integrations will be enabled by default, even if the corresponding + framework/library is detected. + + :param default_integrations: Configures whether `default integrations + `_ should be enabled. + + Setting `default_integrations` to `False` disables all default integrations **as well as all auto-enabling + integrations**, unless they are specifically added in the `integrations` option, described above. + + :param before_send: This function is called with an SDK-specific message or error event object, and can return + a modified event object, or `null` to skip reporting the event. + + This can be used, for instance, for manual PII stripping before sending. + + By the time `before_send` is executed, all scope data has already been applied to the event. Further + modification of the scope won't have any effect. + + :param before_send_transaction: This function is called with an SDK-specific transaction event object, and can + return a modified transaction event object, or `null` to skip reporting the event. + + One way this might be used is for manual PII stripping before sending. + + :param before_breadcrumb: This function is called with an SDK-specific breadcrumb object before the breadcrumb + is added to the scope. + + When nothing is returned from the function, the breadcrumb is dropped. + + To pass the breadcrumb through, return the first argument, which contains the breadcrumb object. + + The callback typically gets a second argument (called a "hint") which contains the original object from + which the breadcrumb was created to further customize what the breadcrumb should look like. + + :param transport: Switches out the transport used to send events. + + How this works depends on the SDK. It can, for instance, be used to capture events for unit-testing or to + send it through some more complex setup that requires proxy authentication. + + :param transport_queue_size: The maximum number of events that will be queued before the transport is forced to + flush. + + :param http_proxy: When set, a proxy can be configured that should be used for outbound requests. + + This is also used for HTTPS requests unless a separate `https_proxy` is configured. However, not all SDKs + support a separate HTTPS proxy. + + SDKs will attempt to default to the system-wide configured proxy, if possible. For instance, on Unix + systems, the `http_proxy` environment variable will be picked up. + + :param https_proxy: Configures a separate proxy for outgoing HTTPS requests. + + This value might not be supported by all SDKs. When not supported the `http-proxy` value is also used for + HTTPS requests at all times. + + :param proxy_headers: A dict containing additional proxy headers (usually for authentication) to be forwarded + to `urllib3`'s `ProxyManager `_. + + :param shutdown_timeout: Controls how many seconds to wait before shutting down. + + Sentry SDKs send events from a background queue. This queue is given a certain amount to drain pending + events. The default is SDK specific but typically around two seconds. + + Setting this value too low may cause problems for sending events from command line applications. + + Setting the value too high will cause the application to block for a long time for users experiencing + network connectivity problems. + + :param keep_alive: Determines whether to keep the connection alive between requests. + + This can be useful in environments where you encounter frequent network issues such as connection resets. + + :param cert_file: Path to the client certificate to use. + + If set, supersedes the `CLIENT_CERT_FILE` environment variable. + + :param key_file: Path to the key file to use. + + If set, supersedes the `CLIENT_KEY_FILE` environment variable. + + :param socket_options: An optional list of socket options to use. + + These provide fine-grained, low-level control over the way the SDK connects to Sentry. + + If provided, the options will override the default `urllib3` `socket options + `_. + + :param traces_sample_rate: A number between `0` and `1`, controlling the percentage chance a given transaction + will be sent to Sentry. + + (`0` represents 0% while `1` represents 100%.) Applies equally to all transactions created in the app. + + Either this or `traces_sampler` must be defined to enable tracing. + + If `traces_sample_rate` is `0`, this means that no new traces will be created. However, if you have + another service (for example a JS frontend) that makes requests to your service that include trace + information, those traces will be continued and thus transactions will be sent to Sentry. + + If you want to disable all tracing you need to set `traces_sample_rate=None`. In this case, no new traces + will be started and no incoming traces will be continued. + + :param traces_sampler: A function responsible for determining the percentage chance a given transaction will be + sent to Sentry. + + It will automatically be passed information about the transaction and the context in which it's being + created, and must return a number between `0` (0% chance of being sent) and `1` (100% chance of being + sent). + + Can also be used for filtering transactions, by returning `0` for those that are unwanted. + + Either this or `traces_sample_rate` must be defined to enable tracing. + + :param trace_propagation_targets: An optional property that controls which downstream services receive tracing + data, in the form of a `sentry-trace` and a `baggage` header attached to any outgoing HTTP requests. + + The option may contain a list of strings or regex against which the URLs of outgoing requests are matched. + + If one of the entries in the list matches the URL of an outgoing request, trace data will be attached to + that request. + + String entries do not have to be full matches, meaning the URL of a request is matched when it _contains_ + a string provided through the option. + + If `trace_propagation_targets` is not provided, trace data is attached to every outgoing request from the + instrumented client. + + :param functions_to_trace: An optional list of functions that should be set up for tracing. + + For each function in the list, a span will be created when the function is executed. + + Functions in the list are represented as strings containing the fully qualified name of the function. + + This is a convenient option, making it possible to have one central place for configuring what functions + to trace, instead of having custom instrumentation scattered all over your code base. + + To learn more, see the `Custom Instrumentation `_ documentation. + + :param enable_backpressure_handling: When enabled, a new monitor thread will be spawned to perform health + checks on the SDK. + + If the system is unhealthy, the SDK will keep halving the `traces_sample_rate` set by you in 10 second + intervals until recovery. + + This down sampling helps ensure that the system stays stable and reduces SDK overhead under high load. + + This option is enabled by default. + + :param enable_db_query_source: When enabled, the source location will be added to database queries. + + :param db_query_source_threshold_ms: The threshold in milliseconds for adding the source location to database + queries. + + The query location will be added to the query for queries slower than the specified threshold. + + :param enable_http_request_source: When enabled, the source location will be added to outgoing HTTP requests. + + :param http_request_source_threshold_ms: The threshold in milliseconds for adding the source location to an + outgoing HTTP request. + + The request location will be added to the request for requests slower than the specified threshold. + + :param custom_repr: A custom `repr `_ function to run + while serializing an object. + + Use this to control how your custom objects and classes are visible in Sentry. + + Return a string for that repr value to be used or `None` to continue serializing how Sentry would have + done it anyway. + + :param profiles_sample_rate: A number between `0` and `1`, controlling the percentage chance a given sampled + transaction will be profiled. + + (`0` represents 0% while `1` represents 100%.) Applies equally to all transactions created in the app. + + This is relative to the tracing sample rate - e.g. `0.5` means 50% of sampled transactions will be + profiled. + + :param profiles_sampler: + + :param profiler_mode: + + :param profile_lifecycle: + + :param profile_session_sample_rate: + + :param enable_tracing: + + :param propagate_traces: + + :param auto_session_tracking: + + :param spotlight: + + :param instrumenter: + + :param enable_logs: Set `enable_logs` to True to enable the SDK to emit + Sentry logs. Defaults to False. + + :param before_send_log: An optional function to modify or filter out logs + before they're sent to Sentry. Any modifications to the log in this + function will be retained. If the function returns None, the log will + not be sent to Sentry. + + :param trace_ignore_status_codes: An optional property that disables tracing for + HTTP requests with certain status codes. + + Requests are not traced if the status code is contained in the provided set. + + If `trace_ignore_status_codes` is not provided, requests with any status code + may be traced. + + :param _experiments: + """ + pass + + +def _get_default_options(): + # type: () -> dict[str, Any] + import inspect + + a = inspect.getfullargspec(ClientConstructor.__init__) + defaults = a.defaults or () + kwonlydefaults = a.kwonlydefaults or {} + + return dict( + itertools.chain( + zip(a.args[-len(defaults) :], defaults), + kwonlydefaults.items(), + ) + ) + + +DEFAULT_OPTIONS = _get_default_options() +del _get_default_options + + +VERSION = "2.42.0" diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/debug.py b/.venv/lib/python3.12/site-packages/sentry_sdk/debug.py new file mode 100644 index 0000000000000000000000000000000000000000..e4c686a3e86ded54d334e1f371aa186e56e75374 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/debug.py @@ -0,0 +1,41 @@ +import sys +import logging +import warnings + +from sentry_sdk import get_client +from sentry_sdk.client import _client_init_debug +from sentry_sdk.utils import logger +from logging import LogRecord + + +class _DebugFilter(logging.Filter): + def filter(self, record): + # type: (LogRecord) -> bool + if _client_init_debug.get(False): + return True + + return get_client().options["debug"] + + +def init_debug_support(): + # type: () -> None + if not logger.handlers: + configure_logger() + + +def configure_logger(): + # type: () -> None + _handler = logging.StreamHandler(sys.stderr) + _handler.setFormatter(logging.Formatter(" [sentry] %(levelname)s: %(message)s")) + logger.addHandler(_handler) + logger.setLevel(logging.DEBUG) + logger.addFilter(_DebugFilter()) + + +def configure_debug_hub(): + # type: () -> None + warnings.warn( + "configure_debug_hub is deprecated. Please remove calls to it, as it is a no-op.", + DeprecationWarning, + stacklevel=2, + ) diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/envelope.py b/.venv/lib/python3.12/site-packages/sentry_sdk/envelope.py new file mode 100644 index 0000000000000000000000000000000000000000..56bb5fde73440b519439e888e16053e7cac77b36 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/envelope.py @@ -0,0 +1,369 @@ +import io +import json +import mimetypes + +from sentry_sdk.session import Session +from sentry_sdk.utils import json_dumps, capture_internal_exceptions + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing import Any + from typing import Optional + from typing import Union + from typing import Dict + from typing import List + from typing import Iterator + + from sentry_sdk._types import Event, EventDataCategory + + +def parse_json(data): + # type: (Union[bytes, str]) -> Any + # on some python 3 versions this needs to be bytes + if isinstance(data, bytes): + data = data.decode("utf-8", "replace") + return json.loads(data) + + +class Envelope: + """ + Represents a Sentry Envelope. The calling code is responsible for adhering to the constraints + documented in the Sentry docs: https://develop.sentry.dev/sdk/envelopes/#data-model. In particular, + each envelope may have at most one Item with type "event" or "transaction" (but not both). + """ + + def __init__( + self, + headers=None, # type: Optional[Dict[str, Any]] + items=None, # type: Optional[List[Item]] + ): + # type: (...) -> None + if headers is not None: + headers = dict(headers) + self.headers = headers or {} + if items is None: + items = [] + else: + items = list(items) + self.items = items + + @property + def description(self): + # type: (...) -> str + return "envelope with %s items (%s)" % ( + len(self.items), + ", ".join(x.data_category for x in self.items), + ) + + def add_event( + self, + event, # type: Event + ): + # type: (...) -> None + self.add_item(Item(payload=PayloadRef(json=event), type="event")) + + def add_transaction( + self, + transaction, # type: Event + ): + # type: (...) -> None + self.add_item(Item(payload=PayloadRef(json=transaction), type="transaction")) + + def add_profile( + self, + profile, # type: Any + ): + # type: (...) -> None + self.add_item(Item(payload=PayloadRef(json=profile), type="profile")) + + def add_profile_chunk( + self, + profile_chunk, # type: Any + ): + # type: (...) -> None + self.add_item( + Item( + payload=PayloadRef(json=profile_chunk), + type="profile_chunk", + headers={"platform": profile_chunk.get("platform", "python")}, + ) + ) + + def add_checkin( + self, + checkin, # type: Any + ): + # type: (...) -> None + self.add_item(Item(payload=PayloadRef(json=checkin), type="check_in")) + + def add_session( + self, + session, # type: Union[Session, Any] + ): + # type: (...) -> None + if isinstance(session, Session): + session = session.to_json() + self.add_item(Item(payload=PayloadRef(json=session), type="session")) + + def add_sessions( + self, + sessions, # type: Any + ): + # type: (...) -> None + self.add_item(Item(payload=PayloadRef(json=sessions), type="sessions")) + + def add_item( + self, + item, # type: Item + ): + # type: (...) -> None + self.items.append(item) + + def get_event(self): + # type: (...) -> Optional[Event] + for items in self.items: + event = items.get_event() + if event is not None: + return event + return None + + def get_transaction_event(self): + # type: (...) -> Optional[Event] + for item in self.items: + event = item.get_transaction_event() + if event is not None: + return event + return None + + def __iter__(self): + # type: (...) -> Iterator[Item] + return iter(self.items) + + def serialize_into( + self, + f, # type: Any + ): + # type: (...) -> None + f.write(json_dumps(self.headers)) + f.write(b"\n") + for item in self.items: + item.serialize_into(f) + + def serialize(self): + # type: (...) -> bytes + out = io.BytesIO() + self.serialize_into(out) + return out.getvalue() + + @classmethod + def deserialize_from( + cls, + f, # type: Any + ): + # type: (...) -> Envelope + headers = parse_json(f.readline()) + items = [] + while 1: + item = Item.deserialize_from(f) + if item is None: + break + items.append(item) + return cls(headers=headers, items=items) + + @classmethod + def deserialize( + cls, + bytes, # type: bytes + ): + # type: (...) -> Envelope + return cls.deserialize_from(io.BytesIO(bytes)) + + def __repr__(self): + # type: (...) -> str + return "" % (self.headers, self.items) + + +class PayloadRef: + def __init__( + self, + bytes=None, # type: Optional[bytes] + path=None, # type: Optional[Union[bytes, str]] + json=None, # type: Optional[Any] + ): + # type: (...) -> None + self.json = json + self.bytes = bytes + self.path = path + + def get_bytes(self): + # type: (...) -> bytes + if self.bytes is None: + if self.path is not None: + with capture_internal_exceptions(): + with open(self.path, "rb") as f: + self.bytes = f.read() + elif self.json is not None: + self.bytes = json_dumps(self.json) + return self.bytes or b"" + + @property + def inferred_content_type(self): + # type: (...) -> str + if self.json is not None: + return "application/json" + elif self.path is not None: + path = self.path + if isinstance(path, bytes): + path = path.decode("utf-8", "replace") + ty = mimetypes.guess_type(path)[0] + if ty: + return ty + return "application/octet-stream" + + def __repr__(self): + # type: (...) -> str + return "" % (self.inferred_content_type,) + + +class Item: + def __init__( + self, + payload, # type: Union[bytes, str, PayloadRef] + headers=None, # type: Optional[Dict[str, Any]] + type=None, # type: Optional[str] + content_type=None, # type: Optional[str] + filename=None, # type: Optional[str] + ): + if headers is not None: + headers = dict(headers) + elif headers is None: + headers = {} + self.headers = headers + if isinstance(payload, bytes): + payload = PayloadRef(bytes=payload) + elif isinstance(payload, str): + payload = PayloadRef(bytes=payload.encode("utf-8")) + else: + payload = payload + + if filename is not None: + headers["filename"] = filename + if type is not None: + headers["type"] = type + if content_type is not None: + headers["content_type"] = content_type + elif "content_type" not in headers: + headers["content_type"] = payload.inferred_content_type + + self.payload = payload + + def __repr__(self): + # type: (...) -> str + return "" % ( + self.headers, + self.payload, + self.data_category, + ) + + @property + def type(self): + # type: (...) -> Optional[str] + return self.headers.get("type") + + @property + def data_category(self): + # type: (...) -> EventDataCategory + ty = self.headers.get("type") + if ty == "session" or ty == "sessions": + return "session" + elif ty == "attachment": + return "attachment" + elif ty == "transaction": + return "transaction" + elif ty == "event": + return "error" + elif ty == "log": + return "log_item" + elif ty == "trace_metric": + return "trace_metric" + elif ty == "client_report": + return "internal" + elif ty == "profile": + return "profile" + elif ty == "profile_chunk": + return "profile_chunk" + elif ty == "check_in": + return "monitor" + else: + return "default" + + def get_bytes(self): + # type: (...) -> bytes + return self.payload.get_bytes() + + def get_event(self): + # type: (...) -> Optional[Event] + """ + Returns an error event if there is one. + """ + if self.type == "event" and self.payload.json is not None: + return self.payload.json + return None + + def get_transaction_event(self): + # type: (...) -> Optional[Event] + if self.type == "transaction" and self.payload.json is not None: + return self.payload.json + return None + + def serialize_into( + self, + f, # type: Any + ): + # type: (...) -> None + headers = dict(self.headers) + bytes = self.get_bytes() + headers["length"] = len(bytes) + f.write(json_dumps(headers)) + f.write(b"\n") + f.write(bytes) + f.write(b"\n") + + def serialize(self): + # type: (...) -> bytes + out = io.BytesIO() + self.serialize_into(out) + return out.getvalue() + + @classmethod + def deserialize_from( + cls, + f, # type: Any + ): + # type: (...) -> Optional[Item] + line = f.readline().rstrip() + if not line: + return None + headers = parse_json(line) + length = headers.get("length") + if length is not None: + payload = f.read(length) + f.readline() + else: + # if no length was specified we need to read up to the end of line + # and remove it (if it is present, i.e. not the very last char in an eof terminated envelope) + payload = f.readline().rstrip(b"\n") + if headers.get("type") in ("event", "transaction"): + rv = cls(headers=headers, payload=PayloadRef(json=parse_json(payload))) + else: + rv = cls(headers=headers, payload=payload) + return rv + + @classmethod + def deserialize( + cls, + bytes, # type: bytes + ): + # type: (...) -> Optional[Item] + return cls.deserialize_from(io.BytesIO(bytes)) diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/feature_flags.py b/.venv/lib/python3.12/site-packages/sentry_sdk/feature_flags.py new file mode 100644 index 0000000000000000000000000000000000000000..03fba9c53c0c390cd30b990744faff7cd8d7bfde --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/feature_flags.py @@ -0,0 +1,71 @@ +import copy +import sentry_sdk +from sentry_sdk._lru_cache import LRUCache +from threading import Lock + +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + from typing import TypedDict + + FlagData = TypedDict("FlagData", {"flag": str, "result": bool}) + + +DEFAULT_FLAG_CAPACITY = 100 + + +class FlagBuffer: + def __init__(self, capacity): + # type: (int) -> None + self.capacity = capacity + self.lock = Lock() + + # Buffer is private. The name is mangled to discourage use. If you use this attribute + # directly you're on your own! + self.__buffer = LRUCache(capacity) + + def clear(self): + # type: () -> None + self.__buffer = LRUCache(self.capacity) + + def __deepcopy__(self, memo): + # type: (dict[int, Any]) -> FlagBuffer + with self.lock: + buffer = FlagBuffer(self.capacity) + buffer.__buffer = copy.deepcopy(self.__buffer, memo) + return buffer + + def get(self): + # type: () -> list[FlagData] + with self.lock: + return [ + {"flag": key, "result": value} for key, value in self.__buffer.get_all() + ] + + def set(self, flag, result): + # type: (str, bool) -> None + if isinstance(result, FlagBuffer): + # If someone were to insert `self` into `self` this would create a circular dependency + # on the lock. This is of course a deadlock. However, this is far outside the expected + # usage of this class. We guard against it here for completeness and to document this + # expected failure mode. + raise ValueError( + "FlagBuffer instances can not be inserted into the dictionary." + ) + + with self.lock: + self.__buffer.set(flag, result) + + +def add_feature_flag(flag, result): + # type: (str, bool) -> None + """ + Records a flag and its value to be sent on subsequent error events. + We recommend you do this on flag evaluations. Flags are buffered per Sentry scope. + """ + flags = sentry_sdk.get_isolation_scope().flags + flags.set(flag, result) + + span = sentry_sdk.get_current_span() + if span: + span.set_flag(f"flag.evaluation.{flag}", result) diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/hub.py b/.venv/lib/python3.12/site-packages/sentry_sdk/hub.py new file mode 100644 index 0000000000000000000000000000000000000000..6f2d1bbf139a9420be47b2d9f919bf7ca0e3ccfb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/hub.py @@ -0,0 +1,747 @@ +import warnings +from contextlib import contextmanager + +from sentry_sdk import ( + get_client, + get_global_scope, + get_isolation_scope, + get_current_scope, +) +from sentry_sdk._compat import with_metaclass +from sentry_sdk.consts import INSTRUMENTER +from sentry_sdk.scope import _ScopeManager +from sentry_sdk.client import Client +from sentry_sdk.tracing import ( + NoOpSpan, + Span, + Transaction, +) + +from sentry_sdk.utils import ( + logger, + ContextVar, +) + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing import Any + from typing import Callable + from typing import ContextManager + from typing import Dict + from typing import Generator + from typing import List + from typing import Optional + from typing import overload + from typing import Tuple + from typing import Type + from typing import TypeVar + from typing import Union + + from typing_extensions import Unpack + + from sentry_sdk.scope import Scope + from sentry_sdk.client import BaseClient + from sentry_sdk.integrations import Integration + from sentry_sdk._types import ( + Event, + Hint, + Breadcrumb, + BreadcrumbHint, + ExcInfo, + LogLevelStr, + SamplingContext, + ) + from sentry_sdk.tracing import TransactionKwargs + + T = TypeVar("T") + +else: + + def overload(x): + # type: (T) -> T + return x + + +class SentryHubDeprecationWarning(DeprecationWarning): + """ + A custom deprecation warning to inform users that the Hub is deprecated. + """ + + _MESSAGE = ( + "`sentry_sdk.Hub` is deprecated and will be removed in a future major release. " + "Please consult our 1.x to 2.x migration guide for details on how to migrate " + "`Hub` usage to the new API: " + "https://docs.sentry.io/platforms/python/migration/1.x-to-2.x" + ) + + def __init__(self, *_): + # type: (*object) -> None + super().__init__(self._MESSAGE) + + +@contextmanager +def _suppress_hub_deprecation_warning(): + # type: () -> Generator[None, None, None] + """Utility function to suppress deprecation warnings for the Hub.""" + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", category=SentryHubDeprecationWarning) + yield + + +_local = ContextVar("sentry_current_hub") + + +class HubMeta(type): + @property + def current(cls): + # type: () -> Hub + """Returns the current instance of the hub.""" + warnings.warn(SentryHubDeprecationWarning(), stacklevel=2) + rv = _local.get(None) + if rv is None: + with _suppress_hub_deprecation_warning(): + # This will raise a deprecation warning; suppress it since we already warned above. + rv = Hub(GLOBAL_HUB) + _local.set(rv) + return rv + + @property + def main(cls): + # type: () -> Hub + """Returns the main instance of the hub.""" + warnings.warn(SentryHubDeprecationWarning(), stacklevel=2) + return GLOBAL_HUB + + +class Hub(with_metaclass(HubMeta)): # type: ignore + """ + .. deprecated:: 2.0.0 + The Hub is deprecated. Its functionality will be merged into :py:class:`sentry_sdk.scope.Scope`. + + The hub wraps the concurrency management of the SDK. Each thread has + its own hub but the hub might transfer with the flow of execution if + context vars are available. + + If the hub is used with a with statement it's temporarily activated. + """ + + _stack = None # type: List[Tuple[Optional[Client], Scope]] + _scope = None # type: Optional[Scope] + + # Mypy doesn't pick up on the metaclass. + + if TYPE_CHECKING: + current = None # type: Hub + main = None # type: Hub + + def __init__( + self, + client_or_hub=None, # type: Optional[Union[Hub, Client]] + scope=None, # type: Optional[Any] + ): + # type: (...) -> None + warnings.warn(SentryHubDeprecationWarning(), stacklevel=2) + + current_scope = None + + if isinstance(client_or_hub, Hub): + client = get_client() + if scope is None: + # hub cloning is going on, we use a fork of the current/isolation scope for context manager + scope = get_isolation_scope().fork() + current_scope = get_current_scope().fork() + else: + client = client_or_hub # type: ignore + get_global_scope().set_client(client) + + if scope is None: # so there is no Hub cloning going on + # just the current isolation scope is used for context manager + scope = get_isolation_scope() + current_scope = get_current_scope() + + if current_scope is None: + # just the current current scope is used for context manager + current_scope = get_current_scope() + + self._stack = [(client, scope)] # type: ignore + self._last_event_id = None # type: Optional[str] + self._old_hubs = [] # type: List[Hub] + + self._old_current_scopes = [] # type: List[Scope] + self._old_isolation_scopes = [] # type: List[Scope] + self._current_scope = current_scope # type: Scope + self._scope = scope # type: Scope + + def __enter__(self): + # type: () -> Hub + self._old_hubs.append(Hub.current) + _local.set(self) + + current_scope = get_current_scope() + self._old_current_scopes.append(current_scope) + scope._current_scope.set(self._current_scope) + + isolation_scope = get_isolation_scope() + self._old_isolation_scopes.append(isolation_scope) + scope._isolation_scope.set(self._scope) + + return self + + def __exit__( + self, + exc_type, # type: Optional[type] + exc_value, # type: Optional[BaseException] + tb, # type: Optional[Any] + ): + # type: (...) -> None + old = self._old_hubs.pop() + _local.set(old) + + old_current_scope = self._old_current_scopes.pop() + scope._current_scope.set(old_current_scope) + + old_isolation_scope = self._old_isolation_scopes.pop() + scope._isolation_scope.set(old_isolation_scope) + + def run( + self, + callback, # type: Callable[[], T] + ): + # type: (...) -> T + """ + .. deprecated:: 2.0.0 + This function is deprecated and will be removed in a future release. + + Runs a callback in the context of the hub. Alternatively the + with statement can be used on the hub directly. + """ + with self: + return callback() + + def get_integration( + self, + name_or_class, # type: Union[str, Type[Integration]] + ): + # type: (...) -> Any + """ + .. deprecated:: 2.0.0 + This function is deprecated and will be removed in a future release. + Please use :py:meth:`sentry_sdk.client._Client.get_integration` instead. + + Returns the integration for this hub by name or class. If there + is no client bound or the client does not have that integration + then `None` is returned. + + If the return value is not `None` the hub is guaranteed to have a + client attached. + """ + return get_client().get_integration(name_or_class) + + @property + def client(self): + # type: () -> Optional[BaseClient] + """ + .. deprecated:: 2.0.0 + This property is deprecated and will be removed in a future release. + Please use :py:func:`sentry_sdk.api.get_client` instead. + + Returns the current client on the hub. + """ + client = get_client() + + if not client.is_active(): + return None + + return client + + @property + def scope(self): + # type: () -> Scope + """ + .. deprecated:: 2.0.0 + This property is deprecated and will be removed in a future release. + Returns the current scope on the hub. + """ + return get_isolation_scope() + + def last_event_id(self): + # type: () -> Optional[str] + """ + Returns the last event ID. + + .. deprecated:: 1.40.5 + This function is deprecated and will be removed in a future release. The functions `capture_event`, `capture_message`, and `capture_exception` return the event ID directly. + """ + logger.warning( + "Deprecated: last_event_id is deprecated. This will be removed in the future. The functions `capture_event`, `capture_message`, and `capture_exception` return the event ID directly." + ) + return self._last_event_id + + def bind_client( + self, + new, # type: Optional[BaseClient] + ): + # type: (...) -> None + """ + .. deprecated:: 2.0.0 + This function is deprecated and will be removed in a future release. + Please use :py:meth:`sentry_sdk.Scope.set_client` instead. + + Binds a new client to the hub. + """ + get_global_scope().set_client(new) + + def capture_event(self, event, hint=None, scope=None, **scope_kwargs): + # type: (Event, Optional[Hint], Optional[Scope], Any) -> Optional[str] + """ + .. deprecated:: 2.0.0 + This function is deprecated and will be removed in a future release. + Please use :py:meth:`sentry_sdk.Scope.capture_event` instead. + + Captures an event. + + Alias of :py:meth:`sentry_sdk.Scope.capture_event`. + + :param event: A ready-made event that can be directly sent to Sentry. + + :param hint: Contains metadata about the event that can be read from `before_send`, such as the original exception object or a HTTP request object. + + :param scope: An optional :py:class:`sentry_sdk.Scope` to apply to events. + The `scope` and `scope_kwargs` parameters are mutually exclusive. + + :param scope_kwargs: Optional data to apply to event. + For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + The `scope` and `scope_kwargs` parameters are mutually exclusive. + """ + last_event_id = get_current_scope().capture_event( + event, hint, scope=scope, **scope_kwargs + ) + + is_transaction = event.get("type") == "transaction" + if last_event_id is not None and not is_transaction: + self._last_event_id = last_event_id + + return last_event_id + + def capture_message(self, message, level=None, scope=None, **scope_kwargs): + # type: (str, Optional[LogLevelStr], Optional[Scope], Any) -> Optional[str] + """ + .. deprecated:: 2.0.0 + This function is deprecated and will be removed in a future release. + Please use :py:meth:`sentry_sdk.Scope.capture_message` instead. + + Captures a message. + + Alias of :py:meth:`sentry_sdk.Scope.capture_message`. + + :param message: The string to send as the message to Sentry. + + :param level: If no level is provided, the default level is `info`. + + :param scope: An optional :py:class:`sentry_sdk.Scope` to apply to events. + The `scope` and `scope_kwargs` parameters are mutually exclusive. + + :param scope_kwargs: Optional data to apply to event. + For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + The `scope` and `scope_kwargs` parameters are mutually exclusive. + + :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.client._Client.capture_event`). + """ + last_event_id = get_current_scope().capture_message( + message, level=level, scope=scope, **scope_kwargs + ) + + if last_event_id is not None: + self._last_event_id = last_event_id + + return last_event_id + + def capture_exception(self, error=None, scope=None, **scope_kwargs): + # type: (Optional[Union[BaseException, ExcInfo]], Optional[Scope], Any) -> Optional[str] + """ + .. deprecated:: 2.0.0 + This function is deprecated and will be removed in a future release. + Please use :py:meth:`sentry_sdk.Scope.capture_exception` instead. + + Captures an exception. + + Alias of :py:meth:`sentry_sdk.Scope.capture_exception`. + + :param error: An exception to capture. If `None`, `sys.exc_info()` will be used. + + :param scope: An optional :py:class:`sentry_sdk.Scope` to apply to events. + The `scope` and `scope_kwargs` parameters are mutually exclusive. + + :param scope_kwargs: Optional data to apply to event. + For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + The `scope` and `scope_kwargs` parameters are mutually exclusive. + + :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.client._Client.capture_event`). + """ + last_event_id = get_current_scope().capture_exception( + error, scope=scope, **scope_kwargs + ) + + if last_event_id is not None: + self._last_event_id = last_event_id + + return last_event_id + + def add_breadcrumb(self, crumb=None, hint=None, **kwargs): + # type: (Optional[Breadcrumb], Optional[BreadcrumbHint], Any) -> None + """ + .. deprecated:: 2.0.0 + This function is deprecated and will be removed in a future release. + Please use :py:meth:`sentry_sdk.Scope.add_breadcrumb` instead. + + Adds a breadcrumb. + + :param crumb: Dictionary with the data as the sentry v7/v8 protocol expects. + + :param hint: An optional value that can be used by `before_breadcrumb` + to customize the breadcrumbs that are emitted. + """ + get_isolation_scope().add_breadcrumb(crumb, hint, **kwargs) + + def start_span(self, instrumenter=INSTRUMENTER.SENTRY, **kwargs): + # type: (str, Any) -> Span + """ + .. deprecated:: 2.0.0 + This function is deprecated and will be removed in a future release. + Please use :py:meth:`sentry_sdk.Scope.start_span` instead. + + Start a span whose parent is the currently active span or transaction, if any. + + The return value is a :py:class:`sentry_sdk.tracing.Span` instance, + typically used as a context manager to start and stop timing in a `with` + block. + + Only spans contained in a transaction are sent to Sentry. Most + integrations start a transaction at the appropriate time, for example + for every incoming HTTP request. Use + :py:meth:`sentry_sdk.start_transaction` to start a new transaction when + one is not already in progress. + + For supported `**kwargs` see :py:class:`sentry_sdk.tracing.Span`. + """ + scope = get_current_scope() + return scope.start_span(instrumenter=instrumenter, **kwargs) + + def start_transaction( + self, + transaction=None, + instrumenter=INSTRUMENTER.SENTRY, + custom_sampling_context=None, + **kwargs, + ): + # type: (Optional[Transaction], str, Optional[SamplingContext], Unpack[TransactionKwargs]) -> Union[Transaction, NoOpSpan] + """ + .. deprecated:: 2.0.0 + This function is deprecated and will be removed in a future release. + Please use :py:meth:`sentry_sdk.Scope.start_transaction` instead. + + Start and return a transaction. + + Start an existing transaction if given, otherwise create and start a new + transaction with kwargs. + + This is the entry point to manual tracing instrumentation. + + A tree structure can be built by adding child spans to the transaction, + and child spans to other spans. To start a new child span within the + transaction or any span, call the respective `.start_child()` method. + + Every child span must be finished before the transaction is finished, + otherwise the unfinished spans are discarded. + + When used as context managers, spans and transactions are automatically + finished at the end of the `with` block. If not using context managers, + call the `.finish()` method. + + When the transaction is finished, it will be sent to Sentry with all its + finished child spans. + + For supported `**kwargs` see :py:class:`sentry_sdk.tracing.Transaction`. + """ + scope = get_current_scope() + + # For backwards compatibility, we allow passing the scope as the hub. + # We need a major release to make this nice. (if someone searches the code: deprecated) + # Type checking disabled for this line because deprecated keys are not allowed in the type signature. + kwargs["hub"] = scope # type: ignore + + return scope.start_transaction( + transaction, instrumenter, custom_sampling_context, **kwargs + ) + + def continue_trace(self, environ_or_headers, op=None, name=None, source=None): + # type: (Dict[str, Any], Optional[str], Optional[str], Optional[str]) -> Transaction + """ + .. deprecated:: 2.0.0 + This function is deprecated and will be removed in a future release. + Please use :py:meth:`sentry_sdk.Scope.continue_trace` instead. + + Sets the propagation context from environment or headers and returns a transaction. + """ + return get_isolation_scope().continue_trace( + environ_or_headers=environ_or_headers, op=op, name=name, source=source + ) + + @overload + def push_scope( + self, + callback=None, # type: Optional[None] + ): + # type: (...) -> ContextManager[Scope] + pass + + @overload + def push_scope( # noqa: F811 + self, + callback, # type: Callable[[Scope], None] + ): + # type: (...) -> None + pass + + def push_scope( # noqa + self, + callback=None, # type: Optional[Callable[[Scope], None]] + continue_trace=True, # type: bool + ): + # type: (...) -> Optional[ContextManager[Scope]] + """ + .. deprecated:: 2.0.0 + This function is deprecated and will be removed in a future release. + + Pushes a new layer on the scope stack. + + :param callback: If provided, this method pushes a scope, calls + `callback`, and pops the scope again. + + :returns: If no `callback` is provided, a context manager that should + be used to pop the scope again. + """ + if callback is not None: + with self.push_scope() as scope: + callback(scope) + return None + + return _ScopeManager(self) + + def pop_scope_unsafe(self): + # type: () -> Tuple[Optional[Client], Scope] + """ + .. deprecated:: 2.0.0 + This function is deprecated and will be removed in a future release. + + Pops a scope layer from the stack. + + Try to use the context manager :py:meth:`push_scope` instead. + """ + rv = self._stack.pop() + assert self._stack, "stack must have at least one layer" + return rv + + @overload + def configure_scope( + self, + callback=None, # type: Optional[None] + ): + # type: (...) -> ContextManager[Scope] + pass + + @overload + def configure_scope( # noqa: F811 + self, + callback, # type: Callable[[Scope], None] + ): + # type: (...) -> None + pass + + def configure_scope( # noqa + self, + callback=None, # type: Optional[Callable[[Scope], None]] + continue_trace=True, # type: bool + ): + # type: (...) -> Optional[ContextManager[Scope]] + """ + .. deprecated:: 2.0.0 + This function is deprecated and will be removed in a future release. + + Reconfigures the scope. + + :param callback: If provided, call the callback with the current scope. + + :returns: If no callback is provided, returns a context manager that returns the scope. + """ + scope = get_isolation_scope() + + if continue_trace: + scope.generate_propagation_context() + + if callback is not None: + # TODO: used to return None when client is None. Check if this changes behavior. + callback(scope) + + return None + + @contextmanager + def inner(): + # type: () -> Generator[Scope, None, None] + yield scope + + return inner() + + def start_session( + self, + session_mode="application", # type: str + ): + # type: (...) -> None + """ + .. deprecated:: 2.0.0 + This function is deprecated and will be removed in a future release. + Please use :py:meth:`sentry_sdk.Scope.start_session` instead. + + Starts a new session. + """ + get_isolation_scope().start_session( + session_mode=session_mode, + ) + + def end_session(self): + # type: (...) -> None + """ + .. deprecated:: 2.0.0 + This function is deprecated and will be removed in a future release. + Please use :py:meth:`sentry_sdk.Scope.end_session` instead. + + Ends the current session if there is one. + """ + get_isolation_scope().end_session() + + def stop_auto_session_tracking(self): + # type: (...) -> None + """ + .. deprecated:: 2.0.0 + This function is deprecated and will be removed in a future release. + Please use :py:meth:`sentry_sdk.Scope.stop_auto_session_tracking` instead. + + Stops automatic session tracking. + + This temporarily session tracking for the current scope when called. + To resume session tracking call `resume_auto_session_tracking`. + """ + get_isolation_scope().stop_auto_session_tracking() + + def resume_auto_session_tracking(self): + # type: (...) -> None + """ + .. deprecated:: 2.0.0 + This function is deprecated and will be removed in a future release. + Please use :py:meth:`sentry_sdk.Scope.resume_auto_session_tracking` instead. + + Resumes automatic session tracking for the current scope if + disabled earlier. This requires that generally automatic session + tracking is enabled. + """ + get_isolation_scope().resume_auto_session_tracking() + + def flush( + self, + timeout=None, # type: Optional[float] + callback=None, # type: Optional[Callable[[int, float], None]] + ): + # type: (...) -> None + """ + .. deprecated:: 2.0.0 + This function is deprecated and will be removed in a future release. + Please use :py:meth:`sentry_sdk.client._Client.flush` instead. + + Alias for :py:meth:`sentry_sdk.client._Client.flush` + """ + return get_client().flush(timeout=timeout, callback=callback) + + def get_traceparent(self): + # type: () -> Optional[str] + """ + .. deprecated:: 2.0.0 + This function is deprecated and will be removed in a future release. + Please use :py:meth:`sentry_sdk.Scope.get_traceparent` instead. + + Returns the traceparent either from the active span or from the scope. + """ + current_scope = get_current_scope() + traceparent = current_scope.get_traceparent() + + if traceparent is None: + isolation_scope = get_isolation_scope() + traceparent = isolation_scope.get_traceparent() + + return traceparent + + def get_baggage(self): + # type: () -> Optional[str] + """ + .. deprecated:: 2.0.0 + This function is deprecated and will be removed in a future release. + Please use :py:meth:`sentry_sdk.Scope.get_baggage` instead. + + Returns Baggage either from the active span or from the scope. + """ + current_scope = get_current_scope() + baggage = current_scope.get_baggage() + + if baggage is None: + isolation_scope = get_isolation_scope() + baggage = isolation_scope.get_baggage() + + if baggage is not None: + return baggage.serialize() + + return None + + def iter_trace_propagation_headers(self, span=None): + # type: (Optional[Span]) -> Generator[Tuple[str, str], None, None] + """ + .. deprecated:: 2.0.0 + This function is deprecated and will be removed in a future release. + Please use :py:meth:`sentry_sdk.Scope.iter_trace_propagation_headers` instead. + + Return HTTP headers which allow propagation of trace data. Data taken + from the span representing the request, if available, or the current + span on the scope if not. + """ + return get_current_scope().iter_trace_propagation_headers( + span=span, + ) + + def trace_propagation_meta(self, span=None): + # type: (Optional[Span]) -> str + """ + .. deprecated:: 2.0.0 + This function is deprecated and will be removed in a future release. + Please use :py:meth:`sentry_sdk.Scope.trace_propagation_meta` instead. + + Return meta tags which should be injected into HTML templates + to allow propagation of trace information. + """ + if span is not None: + logger.warning( + "The parameter `span` in trace_propagation_meta() is deprecated and will be removed in the future." + ) + + return get_current_scope().trace_propagation_meta( + span=span, + ) + + +with _suppress_hub_deprecation_warning(): + # Suppress deprecation warning for the Hub here, since we still always + # import this module. + GLOBAL_HUB = Hub() +_local.set(GLOBAL_HUB) + + +# Circular imports +from sentry_sdk import scope diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/logger.py b/.venv/lib/python3.12/site-packages/sentry_sdk/logger.py new file mode 100644 index 0000000000000000000000000000000000000000..0ea7218e0143e34edb5eaa81aed8f267f2df4e52 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/logger.py @@ -0,0 +1,84 @@ +# NOTE: this is the logger sentry exposes to users, not some generic logger. +import functools +import time +from typing import Any + +from sentry_sdk import get_client +from sentry_sdk.utils import safe_repr + +OTEL_RANGES = [ + # ((severity level range), severity text) + # https://opentelemetry.io/docs/specs/otel/logs/data-model + ((1, 4), "trace"), + ((5, 8), "debug"), + ((9, 12), "info"), + ((13, 16), "warn"), + ((17, 20), "error"), + ((21, 24), "fatal"), +] + + +def _capture_log(severity_text, severity_number, template, **kwargs): + # type: (str, int, str, **Any) -> None + client = get_client() + + attrs = {} # type: dict[str, str | bool | float | int] + if "attributes" in kwargs: + attrs.update(kwargs.pop("attributes")) + for k, v in kwargs.items(): + attrs[f"sentry.message.parameter.{k}"] = v + if kwargs: + # only attach template if there are parameters + attrs["sentry.message.template"] = template + + attrs = { + k: ( + v + if ( + isinstance(v, str) + or isinstance(v, int) + or isinstance(v, bool) + or isinstance(v, float) + ) + else safe_repr(v) + ) + for (k, v) in attrs.items() + } + + # noinspection PyProtectedMember + client._capture_log( + { + "severity_text": severity_text, + "severity_number": severity_number, + "attributes": attrs, + "body": template.format(**kwargs), + "time_unix_nano": time.time_ns(), + "trace_id": None, + }, + ) + + +trace = functools.partial(_capture_log, "trace", 1) +debug = functools.partial(_capture_log, "debug", 5) +info = functools.partial(_capture_log, "info", 9) +warning = functools.partial(_capture_log, "warn", 13) +error = functools.partial(_capture_log, "error", 17) +fatal = functools.partial(_capture_log, "fatal", 21) + + +def _otel_severity_text(otel_severity_number): + # type: (int) -> str + for (lower, upper), severity in OTEL_RANGES: + if lower <= otel_severity_number <= upper: + return severity + + return "default" + + +def _log_level_to_otel(level, mapping): + # type: (int, dict[Any, int]) -> tuple[int, str] + for py_level, otel_severity_number in sorted(mapping.items(), reverse=True): + if level >= py_level: + return otel_severity_number, _otel_severity_text(otel_severity_number) + + return 0, "default" diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/monitor.py b/.venv/lib/python3.12/site-packages/sentry_sdk/monitor.py new file mode 100644 index 0000000000000000000000000000000000000000..b82a528851c67eb6e83e65e1a9c95d39db95aae8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/monitor.py @@ -0,0 +1,120 @@ +import os +import time +from threading import Thread, Lock + +import sentry_sdk +from sentry_sdk.utils import logger + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing import Optional + + +MAX_DOWNSAMPLE_FACTOR = 10 + + +class Monitor: + """ + Performs health checks in a separate thread once every interval seconds + and updates the internal state. Other parts of the SDK only read this state + and act accordingly. + """ + + name = "sentry.monitor" + + def __init__(self, transport, interval=10): + # type: (sentry_sdk.transport.Transport, float) -> None + self.transport = transport # type: sentry_sdk.transport.Transport + self.interval = interval # type: float + + self._healthy = True + self._downsample_factor = 0 # type: int + + self._thread = None # type: Optional[Thread] + self._thread_lock = Lock() + self._thread_for_pid = None # type: Optional[int] + self._running = True + + def _ensure_running(self): + # type: () -> None + """ + Check that the monitor has an active thread to run in, or create one if not. + + Note that this might fail (e.g. in Python 3.12 it's not possible to + spawn new threads at interpreter shutdown). In that case self._running + will be False after running this function. + """ + if self._thread_for_pid == os.getpid() and self._thread is not None: + return None + + with self._thread_lock: + if self._thread_for_pid == os.getpid() and self._thread is not None: + return None + + def _thread(): + # type: (...) -> None + while self._running: + time.sleep(self.interval) + if self._running: + self.run() + + thread = Thread(name=self.name, target=_thread) + thread.daemon = True + try: + thread.start() + except RuntimeError: + # Unfortunately at this point the interpreter is in a state that no + # longer allows us to spawn a thread and we have to bail. + self._running = False + return None + + self._thread = thread + self._thread_for_pid = os.getpid() + + return None + + def run(self): + # type: () -> None + self.check_health() + self.set_downsample_factor() + + def set_downsample_factor(self): + # type: () -> None + if self._healthy: + if self._downsample_factor > 0: + logger.debug( + "[Monitor] health check positive, reverting to normal sampling" + ) + self._downsample_factor = 0 + else: + if self.downsample_factor < MAX_DOWNSAMPLE_FACTOR: + self._downsample_factor += 1 + logger.debug( + "[Monitor] health check negative, downsampling with a factor of %d", + self._downsample_factor, + ) + + def check_health(self): + # type: () -> None + """ + Perform the actual health checks, + currently only checks if the transport is rate-limited. + TODO: augment in the future with more checks. + """ + self._healthy = self.transport.is_healthy() + + def is_healthy(self): + # type: () -> bool + self._ensure_running() + return self._healthy + + @property + def downsample_factor(self): + # type: () -> int + self._ensure_running() + return self._downsample_factor + + def kill(self): + # type: () -> None + self._running = False diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/py.typed b/.venv/lib/python3.12/site-packages/sentry_sdk/py.typed new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/scope.py b/.venv/lib/python3.12/site-packages/sentry_sdk/scope.py new file mode 100644 index 0000000000000000000000000000000000000000..c871e6a467415e393e1d79ca4e94986190c18fe2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/scope.py @@ -0,0 +1,1830 @@ +import os +import sys +import warnings +from copy import copy, deepcopy +from collections import deque +from contextlib import contextmanager +from enum import Enum +from datetime import datetime, timezone +from functools import wraps +from itertools import chain + +from sentry_sdk._types import AnnotatedValue +from sentry_sdk.attachments import Attachment +from sentry_sdk.consts import DEFAULT_MAX_BREADCRUMBS, FALSE_VALUES, INSTRUMENTER +from sentry_sdk.feature_flags import FlagBuffer, DEFAULT_FLAG_CAPACITY +from sentry_sdk.profiler.continuous_profiler import ( + get_profiler_id, + try_autostart_continuous_profiler, + try_profile_lifecycle_trace_start, +) +from sentry_sdk.profiler.transaction_profiler import Profile +from sentry_sdk.session import Session +from sentry_sdk.tracing_utils import ( + Baggage, + has_tracing_enabled, + normalize_incoming_data, + PropagationContext, +) +from sentry_sdk.tracing import ( + BAGGAGE_HEADER_NAME, + SENTRY_TRACE_HEADER_NAME, + NoOpSpan, + Span, + Transaction, +) +from sentry_sdk.utils import ( + capture_internal_exception, + capture_internal_exceptions, + ContextVar, + datetime_from_isoformat, + disable_capture_event, + event_from_exception, + exc_info_from_error, + logger, +) + +import typing +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Mapping + + from typing import Any + from typing import Callable + from typing import Deque + from typing import Dict + from typing import Generator + from typing import Iterator + from typing import List + from typing import Optional + from typing import ParamSpec + from typing import Tuple + from typing import TypeVar + from typing import Union + + from typing_extensions import Unpack + + from sentry_sdk._types import ( + Breadcrumb, + BreadcrumbHint, + ErrorProcessor, + Event, + EventProcessor, + ExcInfo, + Hint, + LogLevelStr, + SamplingContext, + Type, + ) + + from sentry_sdk.tracing import TransactionKwargs + + import sentry_sdk + + P = ParamSpec("P") + R = TypeVar("R") + + F = TypeVar("F", bound=Callable[..., Any]) + T = TypeVar("T") + + +# Holds data that will be added to **all** events sent by this process. +# In case this is a http server (think web framework) with multiple users +# the data will be added to events of all users. +# Typically this is used for process wide data such as the release. +_global_scope = None # type: Optional[Scope] + +# Holds data for the active request. +# This is used to isolate data for different requests or users. +# The isolation scope is usually created by integrations, but may also +# be created manually +_isolation_scope = ContextVar("isolation_scope", default=None) + +# Holds data for the active span. +# This can be used to manually add additional data to a span. +_current_scope = ContextVar("current_scope", default=None) + +global_event_processors = [] # type: List[EventProcessor] + + +class ScopeType(Enum): + CURRENT = "current" + ISOLATION = "isolation" + GLOBAL = "global" + MERGED = "merged" + + +class _ScopeManager: + def __init__(self, hub=None): + # type: (Optional[Any]) -> None + self._old_scopes = [] # type: List[Scope] + + def __enter__(self): + # type: () -> Scope + isolation_scope = Scope.get_isolation_scope() + + self._old_scopes.append(isolation_scope) + + forked_scope = isolation_scope.fork() + _isolation_scope.set(forked_scope) + + return forked_scope + + def __exit__(self, exc_type, exc_value, tb): + # type: (Any, Any, Any) -> None + old_scope = self._old_scopes.pop() + _isolation_scope.set(old_scope) + + +def add_global_event_processor(processor): + # type: (EventProcessor) -> None + global_event_processors.append(processor) + + +def _attr_setter(fn): + # type: (Any) -> Any + return property(fset=fn, doc=fn.__doc__) + + +def _disable_capture(fn): + # type: (F) -> F + @wraps(fn) + def wrapper(self, *args, **kwargs): + # type: (Any, *Dict[str, Any], **Any) -> Any + if not self._should_capture: + return + try: + self._should_capture = False + return fn(self, *args, **kwargs) + finally: + self._should_capture = True + + return wrapper # type: ignore + + +class Scope: + """The scope holds extra information that should be sent with all + events that belong to it. + """ + + # NOTE: Even though it should not happen, the scope needs to not crash when + # accessed by multiple threads. It's fine if it's full of races, but those + # races should never make the user application crash. + # + # The same needs to hold for any accesses of the scope the SDK makes. + + __slots__ = ( + "_level", + "_name", + "_fingerprint", + # note that for legacy reasons, _transaction is the transaction *name*, + # not a Transaction object (the object is stored in _span) + "_transaction", + "_transaction_info", + "_user", + "_tags", + "_contexts", + "_extras", + "_breadcrumbs", + "_n_breadcrumbs_truncated", + "_event_processors", + "_error_processors", + "_should_capture", + "_span", + "_session", + "_attachments", + "_force_auto_session_tracking", + "_profile", + "_propagation_context", + "client", + "_type", + "_last_event_id", + "_flags", + ) + + def __init__(self, ty=None, client=None): + # type: (Optional[ScopeType], Optional[sentry_sdk.Client]) -> None + self._type = ty + + self._event_processors = [] # type: List[EventProcessor] + self._error_processors = [] # type: List[ErrorProcessor] + + self._name = None # type: Optional[str] + self._propagation_context = None # type: Optional[PropagationContext] + self._n_breadcrumbs_truncated = 0 # type: int + + self.client = NonRecordingClient() # type: sentry_sdk.client.BaseClient + + if client is not None: + self.set_client(client) + + self.clear() + + incoming_trace_information = self._load_trace_data_from_env() + self.generate_propagation_context(incoming_data=incoming_trace_information) + + def __copy__(self): + # type: () -> Scope + """ + Returns a copy of this scope. + This also creates a copy of all referenced data structures. + """ + rv = object.__new__(self.__class__) # type: Scope + + rv._type = self._type + rv.client = self.client + rv._level = self._level + rv._name = self._name + rv._fingerprint = self._fingerprint + rv._transaction = self._transaction + rv._transaction_info = self._transaction_info.copy() + rv._user = self._user + + rv._tags = self._tags.copy() + rv._contexts = self._contexts.copy() + rv._extras = self._extras.copy() + + rv._breadcrumbs = copy(self._breadcrumbs) + rv._n_breadcrumbs_truncated = self._n_breadcrumbs_truncated + rv._event_processors = self._event_processors.copy() + rv._error_processors = self._error_processors.copy() + rv._propagation_context = self._propagation_context + + rv._should_capture = self._should_capture + rv._span = self._span + rv._session = self._session + rv._force_auto_session_tracking = self._force_auto_session_tracking + rv._attachments = self._attachments.copy() + + rv._profile = self._profile + + rv._last_event_id = self._last_event_id + + rv._flags = deepcopy(self._flags) + + return rv + + @classmethod + def get_current_scope(cls): + # type: () -> Scope + """ + .. versionadded:: 2.0.0 + + Returns the current scope. + """ + current_scope = _current_scope.get() + if current_scope is None: + current_scope = Scope(ty=ScopeType.CURRENT) + _current_scope.set(current_scope) + + return current_scope + + @classmethod + def set_current_scope(cls, new_current_scope): + # type: (Scope) -> None + """ + .. versionadded:: 2.0.0 + + Sets the given scope as the new current scope overwriting the existing current scope. + :param new_current_scope: The scope to set as the new current scope. + """ + _current_scope.set(new_current_scope) + + @classmethod + def get_isolation_scope(cls): + # type: () -> Scope + """ + .. versionadded:: 2.0.0 + + Returns the isolation scope. + """ + isolation_scope = _isolation_scope.get() + if isolation_scope is None: + isolation_scope = Scope(ty=ScopeType.ISOLATION) + _isolation_scope.set(isolation_scope) + + return isolation_scope + + @classmethod + def set_isolation_scope(cls, new_isolation_scope): + # type: (Scope) -> None + """ + .. versionadded:: 2.0.0 + + Sets the given scope as the new isolation scope overwriting the existing isolation scope. + :param new_isolation_scope: The scope to set as the new isolation scope. + """ + _isolation_scope.set(new_isolation_scope) + + @classmethod + def get_global_scope(cls): + # type: () -> Scope + """ + .. versionadded:: 2.0.0 + + Returns the global scope. + """ + global _global_scope + if _global_scope is None: + _global_scope = Scope(ty=ScopeType.GLOBAL) + + return _global_scope + + @classmethod + def last_event_id(cls): + # type: () -> Optional[str] + """ + .. versionadded:: 2.2.0 + + Returns event ID of the event most recently captured by the isolation scope, or None if no event + has been captured. We do not consider events that are dropped, e.g. by a before_send hook. + Transactions also are not considered events in this context. + + The event corresponding to the returned event ID is NOT guaranteed to actually be sent to Sentry; + whether the event is sent depends on the transport. The event could be sent later or not at all. + Even a sent event could fail to arrive in Sentry due to network issues, exhausted quotas, or + various other reasons. + """ + return cls.get_isolation_scope()._last_event_id + + def _merge_scopes(self, additional_scope=None, additional_scope_kwargs=None): + # type: (Optional[Scope], Optional[Dict[str, Any]]) -> Scope + """ + Merges global, isolation and current scope into a new scope and + adds the given additional scope or additional scope kwargs to it. + """ + if additional_scope and additional_scope_kwargs: + raise TypeError("cannot provide scope and kwargs") + + final_scope = copy(_global_scope) if _global_scope is not None else Scope() + final_scope._type = ScopeType.MERGED + + isolation_scope = _isolation_scope.get() + if isolation_scope is not None: + final_scope.update_from_scope(isolation_scope) + + current_scope = _current_scope.get() + if current_scope is not None: + final_scope.update_from_scope(current_scope) + + if self != current_scope and self != isolation_scope: + final_scope.update_from_scope(self) + + if additional_scope is not None: + if callable(additional_scope): + additional_scope(final_scope) + else: + final_scope.update_from_scope(additional_scope) + + elif additional_scope_kwargs: + final_scope.update_from_kwargs(**additional_scope_kwargs) + + return final_scope + + @classmethod + def get_client(cls): + # type: () -> sentry_sdk.client.BaseClient + """ + .. versionadded:: 2.0.0 + + Returns the currently used :py:class:`sentry_sdk.Client`. + This checks the current scope, the isolation scope and the global scope for a client. + If no client is available a :py:class:`sentry_sdk.client.NonRecordingClient` is returned. + """ + current_scope = _current_scope.get() + try: + client = current_scope.client + except AttributeError: + client = None + + if client is not None and client.is_active(): + return client + + isolation_scope = _isolation_scope.get() + try: + client = isolation_scope.client + except AttributeError: + client = None + + if client is not None and client.is_active(): + return client + + try: + client = _global_scope.client # type: ignore + except AttributeError: + client = None + + if client is not None and client.is_active(): + return client + + return NonRecordingClient() + + def set_client(self, client=None): + # type: (Optional[sentry_sdk.client.BaseClient]) -> None + """ + .. versionadded:: 2.0.0 + + Sets the client for this scope. + + :param client: The client to use in this scope. + If `None` the client of the scope will be replaced by a :py:class:`sentry_sdk.NonRecordingClient`. + + """ + self.client = client if client is not None else NonRecordingClient() + + def fork(self): + # type: () -> Scope + """ + .. versionadded:: 2.0.0 + + Returns a fork of this scope. + """ + forked_scope = copy(self) + return forked_scope + + def _load_trace_data_from_env(self): + # type: () -> Optional[Dict[str, str]] + """ + Load Sentry trace id and baggage from environment variables. + Can be disabled by setting SENTRY_USE_ENVIRONMENT to "false". + """ + incoming_trace_information = None + + sentry_use_environment = ( + os.environ.get("SENTRY_USE_ENVIRONMENT") or "" + ).lower() + use_environment = sentry_use_environment not in FALSE_VALUES + if use_environment: + incoming_trace_information = {} + + if os.environ.get("SENTRY_TRACE"): + incoming_trace_information[SENTRY_TRACE_HEADER_NAME] = ( + os.environ.get("SENTRY_TRACE") or "" + ) + + if os.environ.get("SENTRY_BAGGAGE"): + incoming_trace_information[BAGGAGE_HEADER_NAME] = ( + os.environ.get("SENTRY_BAGGAGE") or "" + ) + + return incoming_trace_information or None + + def set_new_propagation_context(self): + # type: () -> None + """ + Creates a new propagation context and sets it as `_propagation_context`. Overwriting existing one. + """ + self._propagation_context = PropagationContext() + + def generate_propagation_context(self, incoming_data=None): + # type: (Optional[Dict[str, str]]) -> None + """ + Makes sure the propagation context is set on the scope. + If there is `incoming_data` overwrite existing propagation context. + If there is no `incoming_data` create new propagation context, but do NOT overwrite if already existing. + """ + if incoming_data: + propagation_context = PropagationContext.from_incoming_data(incoming_data) + if propagation_context is not None: + self._propagation_context = propagation_context + + if self._type != ScopeType.CURRENT: + if self._propagation_context is None: + self.set_new_propagation_context() + + def get_dynamic_sampling_context(self): + # type: () -> Optional[Dict[str, str]] + """ + Returns the Dynamic Sampling Context from the Propagation Context. + If not existing, creates a new one. + """ + if self._propagation_context is None: + return None + + baggage = self.get_baggage() + if baggage is not None: + self._propagation_context.dynamic_sampling_context = ( + baggage.dynamic_sampling_context() + ) + + return self._propagation_context.dynamic_sampling_context + + def get_traceparent(self, *args, **kwargs): + # type: (Any, Any) -> Optional[str] + """ + Returns the Sentry "sentry-trace" header (aka the traceparent) from the + currently active span or the scopes Propagation Context. + """ + client = self.get_client() + + # If we have an active span, return traceparent from there + if has_tracing_enabled(client.options) and self.span is not None: + return self.span.to_traceparent() + + # If this scope has a propagation context, return traceparent from there + if self._propagation_context is not None: + traceparent = "%s-%s" % ( + self._propagation_context.trace_id, + self._propagation_context.span_id, + ) + return traceparent + + # Fall back to isolation scope's traceparent. It always has one + return self.get_isolation_scope().get_traceparent() + + def get_baggage(self, *args, **kwargs): + # type: (Any, Any) -> Optional[Baggage] + """ + Returns the Sentry "baggage" header containing trace information from the + currently active span or the scopes Propagation Context. + """ + client = self.get_client() + + # If we have an active span, return baggage from there + if has_tracing_enabled(client.options) and self.span is not None: + return self.span.to_baggage() + + # If this scope has a propagation context, return baggage from there + if self._propagation_context is not None: + dynamic_sampling_context = ( + self._propagation_context.dynamic_sampling_context + ) + if dynamic_sampling_context is None: + return Baggage.from_options(self) + else: + return Baggage(dynamic_sampling_context) + + # Fall back to isolation scope's baggage. It always has one + return self.get_isolation_scope().get_baggage() + + def get_trace_context(self): + # type: () -> Any + """ + Returns the Sentry "trace" context from the Propagation Context. + """ + if self._propagation_context is None: + return None + + trace_context = { + "trace_id": self._propagation_context.trace_id, + "span_id": self._propagation_context.span_id, + "parent_span_id": self._propagation_context.parent_span_id, + "dynamic_sampling_context": self.get_dynamic_sampling_context(), + } # type: Dict[str, Any] + + return trace_context + + def trace_propagation_meta(self, *args, **kwargs): + # type: (*Any, **Any) -> str + """ + Return meta tags which should be injected into HTML templates + to allow propagation of trace information. + """ + span = kwargs.pop("span", None) + if span is not None: + logger.warning( + "The parameter `span` in trace_propagation_meta() is deprecated and will be removed in the future." + ) + + meta = "" + + sentry_trace = self.get_traceparent() + if sentry_trace is not None: + meta += '' % ( + SENTRY_TRACE_HEADER_NAME, + sentry_trace, + ) + + baggage = self.get_baggage() + if baggage is not None: + meta += '' % ( + BAGGAGE_HEADER_NAME, + baggage.serialize(), + ) + + return meta + + def iter_headers(self): + # type: () -> Iterator[Tuple[str, str]] + """ + Creates a generator which returns the `sentry-trace` and `baggage` headers from the Propagation Context. + """ + if self._propagation_context is not None: + traceparent = self.get_traceparent() + if traceparent is not None: + yield SENTRY_TRACE_HEADER_NAME, traceparent + + dsc = self.get_dynamic_sampling_context() + if dsc is not None: + baggage = Baggage(dsc).serialize() + yield BAGGAGE_HEADER_NAME, baggage + + def iter_trace_propagation_headers(self, *args, **kwargs): + # type: (Any, Any) -> Generator[Tuple[str, str], None, None] + """ + Return HTTP headers which allow propagation of trace data. + + If a span is given, the trace data will taken from the span. + If no span is given, the trace data is taken from the scope. + """ + client = self.get_client() + if not client.options.get("propagate_traces"): + warnings.warn( + "The `propagate_traces` parameter is deprecated. Please use `trace_propagation_targets` instead.", + DeprecationWarning, + stacklevel=2, + ) + return + + span = kwargs.pop("span", None) + span = span or self.span + + if has_tracing_enabled(client.options) and span is not None: + for header in span.iter_headers(): + yield header + else: + # If this scope has a propagation context, return headers from there + # (it could be that self is not the current scope nor the isolation scope) + if self._propagation_context is not None: + for header in self.iter_headers(): + yield header + else: + # otherwise try headers from current scope + current_scope = self.get_current_scope() + if current_scope._propagation_context is not None: + for header in current_scope.iter_headers(): + yield header + else: + # otherwise fall back to headers from isolation scope + isolation_scope = self.get_isolation_scope() + if isolation_scope._propagation_context is not None: + for header in isolation_scope.iter_headers(): + yield header + + def get_active_propagation_context(self): + # type: () -> Optional[PropagationContext] + if self._propagation_context is not None: + return self._propagation_context + + current_scope = self.get_current_scope() + if current_scope._propagation_context is not None: + return current_scope._propagation_context + + isolation_scope = self.get_isolation_scope() + if isolation_scope._propagation_context is not None: + return isolation_scope._propagation_context + + return None + + def clear(self): + # type: () -> None + """Clears the entire scope.""" + self._level = None # type: Optional[LogLevelStr] + self._fingerprint = None # type: Optional[List[str]] + self._transaction = None # type: Optional[str] + self._transaction_info = {} # type: dict[str, str] + self._user = None # type: Optional[Dict[str, Any]] + + self._tags = {} # type: Dict[str, Any] + self._contexts = {} # type: Dict[str, Dict[str, Any]] + self._extras = {} # type: dict[str, Any] + self._attachments = [] # type: List[Attachment] + + self.clear_breadcrumbs() + self._should_capture = True # type: bool + + self._span = None # type: Optional[Span] + self._session = None # type: Optional[Session] + self._force_auto_session_tracking = None # type: Optional[bool] + + self._profile = None # type: Optional[Profile] + + self._propagation_context = None + + # self._last_event_id is only applicable to isolation scopes + self._last_event_id = None # type: Optional[str] + self._flags = None # type: Optional[FlagBuffer] + + @_attr_setter + def level(self, value): + # type: (LogLevelStr) -> None + """ + When set this overrides the level. + + .. deprecated:: 1.0.0 + Use :func:`set_level` instead. + + :param value: The level to set. + """ + logger.warning( + "Deprecated: use .set_level() instead. This will be removed in the future." + ) + + self._level = value + + def set_level(self, value): + # type: (LogLevelStr) -> None + """ + Sets the level for the scope. + + :param value: The level to set. + """ + self._level = value + + @_attr_setter + def fingerprint(self, value): + # type: (Optional[List[str]]) -> None + """When set this overrides the default fingerprint.""" + self._fingerprint = value + + @property + def transaction(self): + # type: () -> Any + # would be type: () -> Optional[Transaction], see https://github.com/python/mypy/issues/3004 + """Return the transaction (root span) in the scope, if any.""" + + # there is no span/transaction on the scope + if self._span is None: + return None + + # there is an orphan span on the scope + if self._span.containing_transaction is None: + return None + + # there is either a transaction (which is its own containing + # transaction) or a non-orphan span on the scope + return self._span.containing_transaction + + @transaction.setter + def transaction(self, value): + # type: (Any) -> None + # would be type: (Optional[str]) -> None, see https://github.com/python/mypy/issues/3004 + """When set this forces a specific transaction name to be set. + + Deprecated: use set_transaction_name instead.""" + + # XXX: the docstring above is misleading. The implementation of + # apply_to_event prefers an existing value of event.transaction over + # anything set in the scope. + # XXX: note that with the introduction of the Scope.transaction getter, + # there is a semantic and type mismatch between getter and setter. The + # getter returns a Transaction, the setter sets a transaction name. + # Without breaking version compatibility, we could make the setter set a + # transaction name or transaction (self._span) depending on the type of + # the value argument. + + logger.warning( + "Assigning to scope.transaction directly is deprecated: use scope.set_transaction_name() instead." + ) + self._transaction = value + if self._span and self._span.containing_transaction: + self._span.containing_transaction.name = value + + def set_transaction_name(self, name, source=None): + # type: (str, Optional[str]) -> None + """Set the transaction name and optionally the transaction source.""" + self._transaction = name + + if self._span and self._span.containing_transaction: + self._span.containing_transaction.name = name + if source: + self._span.containing_transaction.source = source + + if source: + self._transaction_info["source"] = source + + @_attr_setter + def user(self, value): + # type: (Optional[Dict[str, Any]]) -> None + """When set a specific user is bound to the scope. Deprecated in favor of set_user.""" + warnings.warn( + "The `Scope.user` setter is deprecated in favor of `Scope.set_user()`.", + DeprecationWarning, + stacklevel=2, + ) + self.set_user(value) + + def set_user(self, value): + # type: (Optional[Dict[str, Any]]) -> None + """Sets a user for the scope.""" + self._user = value + session = self.get_isolation_scope()._session + if session is not None: + session.update(user=value) + + @property + def span(self): + # type: () -> Optional[Span] + """Get/set current tracing span or transaction.""" + return self._span + + @span.setter + def span(self, span): + # type: (Optional[Span]) -> None + self._span = span + # XXX: this differs from the implementation in JS, there Scope.setSpan + # does not set Scope._transactionName. + if isinstance(span, Transaction): + transaction = span + if transaction.name: + self._transaction = transaction.name + if transaction.source: + self._transaction_info["source"] = transaction.source + + @property + def profile(self): + # type: () -> Optional[Profile] + return self._profile + + @profile.setter + def profile(self, profile): + # type: (Optional[Profile]) -> None + + self._profile = profile + + def set_tag(self, key, value): + # type: (str, Any) -> None + """ + Sets a tag for a key to a specific value. + + :param key: Key of the tag to set. + + :param value: Value of the tag to set. + """ + self._tags[key] = value + + def set_tags(self, tags): + # type: (Mapping[str, object]) -> None + """Sets multiple tags at once. + + This method updates multiple tags at once. The tags are passed as a dictionary + or other mapping type. + + Calling this method is equivalent to calling `set_tag` on each key-value pair + in the mapping. If a tag key already exists in the scope, its value will be + updated. If the tag key does not exist in the scope, the key-value pair will + be added to the scope. + + This method only modifies tag keys in the `tags` mapping passed to the method. + `scope.set_tags({})` is, therefore, a no-op. + + :param tags: A mapping of tag keys to tag values to set. + """ + self._tags.update(tags) + + def remove_tag(self, key): + # type: (str) -> None + """ + Removes a specific tag. + + :param key: Key of the tag to remove. + """ + self._tags.pop(key, None) + + def set_context( + self, + key, # type: str + value, # type: Dict[str, Any] + ): + # type: (...) -> None + """ + Binds a context at a certain key to a specific value. + """ + self._contexts[key] = value + + def remove_context( + self, + key, # type: str + ): + # type: (...) -> None + """Removes a context.""" + self._contexts.pop(key, None) + + def set_extra( + self, + key, # type: str + value, # type: Any + ): + # type: (...) -> None + """Sets an extra key to a specific value.""" + self._extras[key] = value + + def remove_extra( + self, + key, # type: str + ): + # type: (...) -> None + """Removes a specific extra key.""" + self._extras.pop(key, None) + + def clear_breadcrumbs(self): + # type: () -> None + """Clears breadcrumb buffer.""" + self._breadcrumbs = deque() # type: Deque[Breadcrumb] + self._n_breadcrumbs_truncated = 0 + + def add_attachment( + self, + bytes=None, # type: Union[None, bytes, Callable[[], bytes]] + filename=None, # type: Optional[str] + path=None, # type: Optional[str] + content_type=None, # type: Optional[str] + add_to_transactions=False, # type: bool + ): + # type: (...) -> None + """Adds an attachment to future events sent from this scope. + + The parameters are the same as for the :py:class:`sentry_sdk.attachments.Attachment` constructor. + """ + self._attachments.append( + Attachment( + bytes=bytes, + path=path, + filename=filename, + content_type=content_type, + add_to_transactions=add_to_transactions, + ) + ) + + def add_breadcrumb(self, crumb=None, hint=None, **kwargs): + # type: (Optional[Breadcrumb], Optional[BreadcrumbHint], Any) -> None + """ + Adds a breadcrumb. + + :param crumb: Dictionary with the data as the sentry v7/v8 protocol expects. + + :param hint: An optional value that can be used by `before_breadcrumb` + to customize the breadcrumbs that are emitted. + """ + client = self.get_client() + + if not client.is_active(): + logger.info("Dropped breadcrumb because no client bound") + return + + before_breadcrumb = client.options.get("before_breadcrumb") + max_breadcrumbs = client.options.get("max_breadcrumbs", DEFAULT_MAX_BREADCRUMBS) + + crumb = dict(crumb or ()) # type: Breadcrumb + crumb.update(kwargs) + if not crumb: + return + + hint = dict(hint or ()) # type: Hint + + if crumb.get("timestamp") is None: + crumb["timestamp"] = datetime.now(timezone.utc) + if crumb.get("type") is None: + crumb["type"] = "default" + + if before_breadcrumb is not None: + new_crumb = before_breadcrumb(crumb, hint) + else: + new_crumb = crumb + + if new_crumb is not None: + self._breadcrumbs.append(new_crumb) + else: + logger.info("before breadcrumb dropped breadcrumb (%s)", crumb) + + while len(self._breadcrumbs) > max_breadcrumbs: + self._breadcrumbs.popleft() + self._n_breadcrumbs_truncated += 1 + + def start_transaction( + self, + transaction=None, + instrumenter=INSTRUMENTER.SENTRY, + custom_sampling_context=None, + **kwargs, + ): + # type: (Optional[Transaction], str, Optional[SamplingContext], Unpack[TransactionKwargs]) -> Union[Transaction, NoOpSpan] + """ + Start and return a transaction. + + Start an existing transaction if given, otherwise create and start a new + transaction with kwargs. + + This is the entry point to manual tracing instrumentation. + + A tree structure can be built by adding child spans to the transaction, + and child spans to other spans. To start a new child span within the + transaction or any span, call the respective `.start_child()` method. + + Every child span must be finished before the transaction is finished, + otherwise the unfinished spans are discarded. + + When used as context managers, spans and transactions are automatically + finished at the end of the `with` block. If not using context managers, + call the `.finish()` method. + + When the transaction is finished, it will be sent to Sentry with all its + finished child spans. + + :param transaction: The transaction to start. If omitted, we create and + start a new transaction. + :param instrumenter: This parameter is meant for internal use only. It + will be removed in the next major version. + :param custom_sampling_context: The transaction's custom sampling context. + :param kwargs: Optional keyword arguments to be passed to the Transaction + constructor. See :py:class:`sentry_sdk.tracing.Transaction` for + available arguments. + """ + kwargs.setdefault("scope", self) + + client = self.get_client() + + configuration_instrumenter = client.options["instrumenter"] + + if instrumenter != configuration_instrumenter: + return NoOpSpan() + + try_autostart_continuous_profiler() + + custom_sampling_context = custom_sampling_context or {} + + # kwargs at this point has type TransactionKwargs, since we have removed + # the client and custom_sampling_context from it. + transaction_kwargs = kwargs # type: TransactionKwargs + + # if we haven't been given a transaction, make one + if transaction is None: + transaction = Transaction(**transaction_kwargs) + + # use traces_sample_rate, traces_sampler, and/or inheritance to make a + # sampling decision + sampling_context = { + "transaction_context": transaction.to_json(), + "parent_sampled": transaction.parent_sampled, + } + sampling_context.update(custom_sampling_context) + transaction._set_initial_sampling_decision(sampling_context=sampling_context) + + # update the sample rate in the dsc + if transaction.sample_rate is not None: + propagation_context = self.get_active_propagation_context() + if propagation_context: + dsc = propagation_context.dynamic_sampling_context + if dsc is not None: + dsc["sample_rate"] = str(transaction.sample_rate) + if transaction._baggage: + transaction._baggage.sentry_items["sample_rate"] = str( + transaction.sample_rate + ) + + if transaction.sampled: + profile = Profile( + transaction.sampled, transaction._start_timestamp_monotonic_ns + ) + profile._set_initial_sampling_decision(sampling_context=sampling_context) + + transaction._profile = profile + + transaction._continuous_profile = try_profile_lifecycle_trace_start() + + # Typically, the profiler is set when the transaction is created. But when + # using the auto lifecycle, the profiler isn't running when the first + # transaction is started. So make sure we update the profiler id on it. + if transaction._continuous_profile is not None: + transaction.set_profiler_id(get_profiler_id()) + + # we don't bother to keep spans if we already know we're not going to + # send the transaction + max_spans = (client.options["_experiments"].get("max_spans")) or 1000 + transaction.init_span_recorder(maxlen=max_spans) + + return transaction + + def start_span(self, instrumenter=INSTRUMENTER.SENTRY, **kwargs): + # type: (str, Any) -> Span + """ + Start a span whose parent is the currently active span or transaction, if any. + + The return value is a :py:class:`sentry_sdk.tracing.Span` instance, + typically used as a context manager to start and stop timing in a `with` + block. + + Only spans contained in a transaction are sent to Sentry. Most + integrations start a transaction at the appropriate time, for example + for every incoming HTTP request. Use + :py:meth:`sentry_sdk.start_transaction` to start a new transaction when + one is not already in progress. + + For supported `**kwargs` see :py:class:`sentry_sdk.tracing.Span`. + + The instrumenter parameter is deprecated for user code, and it will + be removed in the next major version. Going forward, it should only + be used by the SDK itself. + """ + if kwargs.get("description") is not None: + warnings.warn( + "The `description` parameter is deprecated. Please use `name` instead.", + DeprecationWarning, + stacklevel=2, + ) + + with new_scope(): + kwargs.setdefault("scope", self) + + client = self.get_client() + + configuration_instrumenter = client.options["instrumenter"] + + if instrumenter != configuration_instrumenter: + return NoOpSpan() + + # get current span or transaction + span = self.span or self.get_isolation_scope().span + + if span is None: + # New spans get the `trace_id` from the scope + if "trace_id" not in kwargs: + propagation_context = self.get_active_propagation_context() + if propagation_context is not None: + kwargs["trace_id"] = propagation_context.trace_id + + span = Span(**kwargs) + else: + # Children take `trace_id`` from the parent span. + span = span.start_child(**kwargs) + + return span + + def continue_trace( + self, environ_or_headers, op=None, name=None, source=None, origin="manual" + ): + # type: (Dict[str, Any], Optional[str], Optional[str], Optional[str], str) -> Transaction + """ + Sets the propagation context from environment or headers and returns a transaction. + """ + self.generate_propagation_context(environ_or_headers) + + # When we generate the propagation context, the sample_rand value is set + # if missing or invalid (we use the original value if it's valid). + # We want the transaction to use the same sample_rand value. Due to duplicated + # propagation logic in the transaction, we pass it in to avoid recomputing it + # in the transaction. + # TYPE SAFETY: self.generate_propagation_context() ensures that self._propagation_context + # is not None. + sample_rand = typing.cast( + PropagationContext, self._propagation_context + )._sample_rand() + + transaction = Transaction.continue_from_headers( + normalize_incoming_data(environ_or_headers), + _sample_rand=sample_rand, + op=op, + origin=origin, + name=name, + source=source, + ) + + return transaction + + def capture_event(self, event, hint=None, scope=None, **scope_kwargs): + # type: (Event, Optional[Hint], Optional[Scope], Any) -> Optional[str] + """ + Captures an event. + + Merges given scope data and calls :py:meth:`sentry_sdk.client._Client.capture_event`. + + :param event: A ready-made event that can be directly sent to Sentry. + + :param hint: Contains metadata about the event that can be read from `before_send`, such as the original exception object or a HTTP request object. + + :param scope: An optional :py:class:`sentry_sdk.Scope` to apply to events. + The `scope` and `scope_kwargs` parameters are mutually exclusive. + + :param scope_kwargs: Optional data to apply to event. + For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + The `scope` and `scope_kwargs` parameters are mutually exclusive. + + :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.client._Client.capture_event`). + """ + if disable_capture_event.get(False): + return None + + scope = self._merge_scopes(scope, scope_kwargs) + + event_id = self.get_client().capture_event(event=event, hint=hint, scope=scope) + + if event_id is not None and event.get("type") != "transaction": + self.get_isolation_scope()._last_event_id = event_id + + return event_id + + def capture_message(self, message, level=None, scope=None, **scope_kwargs): + # type: (str, Optional[LogLevelStr], Optional[Scope], Any) -> Optional[str] + """ + Captures a message. + + :param message: The string to send as the message. + + :param level: If no level is provided, the default level is `info`. + + :param scope: An optional :py:class:`sentry_sdk.Scope` to apply to events. + The `scope` and `scope_kwargs` parameters are mutually exclusive. + + :param scope_kwargs: Optional data to apply to event. + For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + The `scope` and `scope_kwargs` parameters are mutually exclusive. + + :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.client._Client.capture_event`). + """ + if disable_capture_event.get(False): + return None + + if level is None: + level = "info" + + event = { + "message": message, + "level": level, + } # type: Event + + return self.capture_event(event, scope=scope, **scope_kwargs) + + def capture_exception(self, error=None, scope=None, **scope_kwargs): + # type: (Optional[Union[BaseException, ExcInfo]], Optional[Scope], Any) -> Optional[str] + """Captures an exception. + + :param error: An exception to capture. If `None`, `sys.exc_info()` will be used. + + :param scope: An optional :py:class:`sentry_sdk.Scope` to apply to events. + The `scope` and `scope_kwargs` parameters are mutually exclusive. + + :param scope_kwargs: Optional data to apply to event. + For supported `**scope_kwargs` see :py:meth:`sentry_sdk.Scope.update_from_kwargs`. + The `scope` and `scope_kwargs` parameters are mutually exclusive. + + :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.client._Client.capture_event`). + """ + if disable_capture_event.get(False): + return None + + if error is not None: + exc_info = exc_info_from_error(error) + else: + exc_info = sys.exc_info() + + event, hint = event_from_exception( + exc_info, client_options=self.get_client().options + ) + + try: + return self.capture_event(event, hint=hint, scope=scope, **scope_kwargs) + except Exception: + capture_internal_exception(sys.exc_info()) + + return None + + def start_session(self, *args, **kwargs): + # type: (*Any, **Any) -> None + """Starts a new session.""" + session_mode = kwargs.pop("session_mode", "application") + + self.end_session() + + client = self.get_client() + self._session = Session( + release=client.options.get("release"), + environment=client.options.get("environment"), + user=self._user, + session_mode=session_mode, + ) + + def end_session(self, *args, **kwargs): + # type: (*Any, **Any) -> None + """Ends the current session if there is one.""" + session = self._session + self._session = None + + if session is not None: + session.close() + self.get_client().capture_session(session) + + def stop_auto_session_tracking(self, *args, **kwargs): + # type: (*Any, **Any) -> None + """Stops automatic session tracking. + + This temporarily session tracking for the current scope when called. + To resume session tracking call `resume_auto_session_tracking`. + """ + self.end_session() + self._force_auto_session_tracking = False + + def resume_auto_session_tracking(self): + # type: (...) -> None + """Resumes automatic session tracking for the current scope if + disabled earlier. This requires that generally automatic session + tracking is enabled. + """ + self._force_auto_session_tracking = None + + def add_event_processor( + self, + func, # type: EventProcessor + ): + # type: (...) -> None + """Register a scope local event processor on the scope. + + :param func: This function behaves like `before_send.` + """ + if len(self._event_processors) > 20: + logger.warning( + "Too many event processors on scope! Clearing list to free up some memory: %r", + self._event_processors, + ) + del self._event_processors[:] + + self._event_processors.append(func) + + def add_error_processor( + self, + func, # type: ErrorProcessor + cls=None, # type: Optional[Type[BaseException]] + ): + # type: (...) -> None + """Register a scope local error processor on the scope. + + :param func: A callback that works similar to an event processor but is invoked with the original exception info triple as second argument. + + :param cls: Optionally, only process exceptions of this type. + """ + if cls is not None: + cls_ = cls # For mypy. + real_func = func + + def func(event, exc_info): + # type: (Event, ExcInfo) -> Optional[Event] + try: + is_inst = isinstance(exc_info[1], cls_) + except Exception: + is_inst = False + if is_inst: + return real_func(event, exc_info) + return event + + self._error_processors.append(func) + + def _apply_level_to_event(self, event, hint, options): + # type: (Event, Hint, Optional[Dict[str, Any]]) -> None + if self._level is not None: + event["level"] = self._level + + def _apply_breadcrumbs_to_event(self, event, hint, options): + # type: (Event, Hint, Optional[Dict[str, Any]]) -> None + event.setdefault("breadcrumbs", {}) + + # This check is just for mypy - + if not isinstance(event["breadcrumbs"], AnnotatedValue): + event["breadcrumbs"].setdefault("values", []) + event["breadcrumbs"]["values"].extend(self._breadcrumbs) + + # Attempt to sort timestamps + try: + if not isinstance(event["breadcrumbs"], AnnotatedValue): + for crumb in event["breadcrumbs"]["values"]: + if isinstance(crumb["timestamp"], str): + crumb["timestamp"] = datetime_from_isoformat(crumb["timestamp"]) + + event["breadcrumbs"]["values"].sort( + key=lambda crumb: crumb["timestamp"] + ) + except Exception as err: + logger.debug("Error when sorting breadcrumbs", exc_info=err) + pass + + def _apply_user_to_event(self, event, hint, options): + # type: (Event, Hint, Optional[Dict[str, Any]]) -> None + if event.get("user") is None and self._user is not None: + event["user"] = self._user + + def _apply_transaction_name_to_event(self, event, hint, options): + # type: (Event, Hint, Optional[Dict[str, Any]]) -> None + if event.get("transaction") is None and self._transaction is not None: + event["transaction"] = self._transaction + + def _apply_transaction_info_to_event(self, event, hint, options): + # type: (Event, Hint, Optional[Dict[str, Any]]) -> None + if event.get("transaction_info") is None and self._transaction_info is not None: + event["transaction_info"] = self._transaction_info + + def _apply_fingerprint_to_event(self, event, hint, options): + # type: (Event, Hint, Optional[Dict[str, Any]]) -> None + if event.get("fingerprint") is None and self._fingerprint is not None: + event["fingerprint"] = self._fingerprint + + def _apply_extra_to_event(self, event, hint, options): + # type: (Event, Hint, Optional[Dict[str, Any]]) -> None + if self._extras: + event.setdefault("extra", {}).update(self._extras) + + def _apply_tags_to_event(self, event, hint, options): + # type: (Event, Hint, Optional[Dict[str, Any]]) -> None + if self._tags: + event.setdefault("tags", {}).update(self._tags) + + def _apply_contexts_to_event(self, event, hint, options): + # type: (Event, Hint, Optional[Dict[str, Any]]) -> None + if self._contexts: + event.setdefault("contexts", {}).update(self._contexts) + + contexts = event.setdefault("contexts", {}) + + # Add "trace" context + if contexts.get("trace") is None: + if has_tracing_enabled(options) and self._span is not None: + contexts["trace"] = self._span.get_trace_context() + else: + contexts["trace"] = self.get_trace_context() + + def _apply_flags_to_event(self, event, hint, options): + # type: (Event, Hint, Optional[Dict[str, Any]]) -> None + flags = self.flags.get() + if len(flags) > 0: + event.setdefault("contexts", {}).setdefault("flags", {}).update( + {"values": flags} + ) + + def _drop(self, cause, ty): + # type: (Any, str) -> Optional[Any] + logger.info("%s (%s) dropped event", ty, cause) + return None + + def run_error_processors(self, event, hint): + # type: (Event, Hint) -> Optional[Event] + """ + Runs the error processors on the event and returns the modified event. + """ + exc_info = hint.get("exc_info") + if exc_info is not None: + error_processors = chain( + self.get_global_scope()._error_processors, + self.get_isolation_scope()._error_processors, + self.get_current_scope()._error_processors, + ) + + for error_processor in error_processors: + new_event = error_processor(event, exc_info) + if new_event is None: + return self._drop(error_processor, "error processor") + + event = new_event + + return event + + def run_event_processors(self, event, hint): + # type: (Event, Hint) -> Optional[Event] + """ + Runs the event processors on the event and returns the modified event. + """ + ty = event.get("type") + is_check_in = ty == "check_in" + + if not is_check_in: + # Get scopes without creating them to prevent infinite recursion + isolation_scope = _isolation_scope.get() + current_scope = _current_scope.get() + + event_processors = chain( + global_event_processors, + _global_scope and _global_scope._event_processors or [], + isolation_scope and isolation_scope._event_processors or [], + current_scope and current_scope._event_processors or [], + ) + + for event_processor in event_processors: + new_event = event + with capture_internal_exceptions(): + new_event = event_processor(event, hint) + if new_event is None: + return self._drop(event_processor, "event processor") + event = new_event + + return event + + @_disable_capture + def apply_to_event( + self, + event, # type: Event + hint, # type: Hint + options=None, # type: Optional[Dict[str, Any]] + ): + # type: (...) -> Optional[Event] + """Applies the information contained on the scope to the given event.""" + ty = event.get("type") + is_transaction = ty == "transaction" + is_check_in = ty == "check_in" + + # put all attachments into the hint. This lets callbacks play around + # with attachments. We also later pull this out of the hint when we + # create the envelope. + attachments_to_send = hint.get("attachments") or [] + for attachment in self._attachments: + if not is_transaction or attachment.add_to_transactions: + attachments_to_send.append(attachment) + hint["attachments"] = attachments_to_send + + self._apply_contexts_to_event(event, hint, options) + + if is_check_in: + # Check-ins only support the trace context, strip all others + event["contexts"] = { + "trace": event.setdefault("contexts", {}).get("trace", {}) + } + + if not is_check_in: + self._apply_level_to_event(event, hint, options) + self._apply_fingerprint_to_event(event, hint, options) + self._apply_user_to_event(event, hint, options) + self._apply_transaction_name_to_event(event, hint, options) + self._apply_transaction_info_to_event(event, hint, options) + self._apply_tags_to_event(event, hint, options) + self._apply_extra_to_event(event, hint, options) + + if not is_transaction and not is_check_in: + self._apply_breadcrumbs_to_event(event, hint, options) + self._apply_flags_to_event(event, hint, options) + + event = self.run_error_processors(event, hint) + if event is None: + return None + + event = self.run_event_processors(event, hint) + if event is None: + return None + + return event + + def update_from_scope(self, scope): + # type: (Scope) -> None + """Update the scope with another scope's data.""" + if scope._level is not None: + self._level = scope._level + if scope._fingerprint is not None: + self._fingerprint = scope._fingerprint + if scope._transaction is not None: + self._transaction = scope._transaction + if scope._transaction_info is not None: + self._transaction_info.update(scope._transaction_info) + if scope._user is not None: + self._user = scope._user + if scope._tags: + self._tags.update(scope._tags) + if scope._contexts: + self._contexts.update(scope._contexts) + if scope._extras: + self._extras.update(scope._extras) + if scope._breadcrumbs: + self._breadcrumbs.extend(scope._breadcrumbs) + if scope._n_breadcrumbs_truncated: + self._n_breadcrumbs_truncated = ( + self._n_breadcrumbs_truncated + scope._n_breadcrumbs_truncated + ) + if scope._span: + self._span = scope._span + if scope._attachments: + self._attachments.extend(scope._attachments) + if scope._profile: + self._profile = scope._profile + if scope._propagation_context: + self._propagation_context = scope._propagation_context + if scope._session: + self._session = scope._session + if scope._flags: + if not self._flags: + self._flags = deepcopy(scope._flags) + else: + for flag in scope._flags.get(): + self._flags.set(flag["flag"], flag["result"]) + + def update_from_kwargs( + self, + user=None, # type: Optional[Any] + level=None, # type: Optional[LogLevelStr] + extras=None, # type: Optional[Dict[str, Any]] + contexts=None, # type: Optional[Dict[str, Dict[str, Any]]] + tags=None, # type: Optional[Dict[str, str]] + fingerprint=None, # type: Optional[List[str]] + ): + # type: (...) -> None + """Update the scope's attributes.""" + if level is not None: + self._level = level + if user is not None: + self._user = user + if extras is not None: + self._extras.update(extras) + if contexts is not None: + self._contexts.update(contexts) + if tags is not None: + self._tags.update(tags) + if fingerprint is not None: + self._fingerprint = fingerprint + + def __repr__(self): + # type: () -> str + return "<%s id=%s name=%s type=%s>" % ( + self.__class__.__name__, + hex(id(self)), + self._name, + self._type, + ) + + @property + def flags(self): + # type: () -> FlagBuffer + if self._flags is None: + max_flags = ( + self.get_client().options["_experiments"].get("max_flags") + or DEFAULT_FLAG_CAPACITY + ) + self._flags = FlagBuffer(capacity=max_flags) + return self._flags + + +@contextmanager +def new_scope(): + # type: () -> Generator[Scope, None, None] + """ + .. versionadded:: 2.0.0 + + Context manager that forks the current scope and runs the wrapped code in it. + After the wrapped code is executed, the original scope is restored. + + Example Usage: + + .. code-block:: python + + import sentry_sdk + + with sentry_sdk.new_scope() as scope: + scope.set_tag("color", "green") + sentry_sdk.capture_message("hello") # will include `color` tag. + + sentry_sdk.capture_message("hello, again") # will NOT include `color` tag. + + """ + # fork current scope + current_scope = Scope.get_current_scope() + new_scope = current_scope.fork() + token = _current_scope.set(new_scope) + + try: + yield new_scope + + finally: + try: + # restore original scope + _current_scope.reset(token) + except LookupError: + capture_internal_exception(sys.exc_info()) + + +@contextmanager +def use_scope(scope): + # type: (Scope) -> Generator[Scope, None, None] + """ + .. versionadded:: 2.0.0 + + Context manager that uses the given `scope` and runs the wrapped code in it. + After the wrapped code is executed, the original scope is restored. + + Example Usage: + Suppose the variable `scope` contains a `Scope` object, which is not currently + the active scope. + + .. code-block:: python + + import sentry_sdk + + with sentry_sdk.use_scope(scope): + scope.set_tag("color", "green") + sentry_sdk.capture_message("hello") # will include `color` tag. + + sentry_sdk.capture_message("hello, again") # will NOT include `color` tag. + + """ + # set given scope as current scope + token = _current_scope.set(scope) + + try: + yield scope + + finally: + try: + # restore original scope + _current_scope.reset(token) + except LookupError: + capture_internal_exception(sys.exc_info()) + + +@contextmanager +def isolation_scope(): + # type: () -> Generator[Scope, None, None] + """ + .. versionadded:: 2.0.0 + + Context manager that forks the current isolation scope and runs the wrapped code in it. + The current scope is also forked to not bleed data into the existing current scope. + After the wrapped code is executed, the original scopes are restored. + + Example Usage: + + .. code-block:: python + + import sentry_sdk + + with sentry_sdk.isolation_scope() as scope: + scope.set_tag("color", "green") + sentry_sdk.capture_message("hello") # will include `color` tag. + + sentry_sdk.capture_message("hello, again") # will NOT include `color` tag. + + """ + # fork current scope + current_scope = Scope.get_current_scope() + forked_current_scope = current_scope.fork() + current_token = _current_scope.set(forked_current_scope) + + # fork isolation scope + isolation_scope = Scope.get_isolation_scope() + new_isolation_scope = isolation_scope.fork() + isolation_token = _isolation_scope.set(new_isolation_scope) + + try: + yield new_isolation_scope + + finally: + # restore original scopes + try: + _current_scope.reset(current_token) + except LookupError: + capture_internal_exception(sys.exc_info()) + + try: + _isolation_scope.reset(isolation_token) + except LookupError: + capture_internal_exception(sys.exc_info()) + + +@contextmanager +def use_isolation_scope(isolation_scope): + # type: (Scope) -> Generator[Scope, None, None] + """ + .. versionadded:: 2.0.0 + + Context manager that uses the given `isolation_scope` and runs the wrapped code in it. + The current scope is also forked to not bleed data into the existing current scope. + After the wrapped code is executed, the original scopes are restored. + + Example Usage: + + .. code-block:: python + + import sentry_sdk + + with sentry_sdk.isolation_scope() as scope: + scope.set_tag("color", "green") + sentry_sdk.capture_message("hello") # will include `color` tag. + + sentry_sdk.capture_message("hello, again") # will NOT include `color` tag. + + """ + # fork current scope + current_scope = Scope.get_current_scope() + forked_current_scope = current_scope.fork() + current_token = _current_scope.set(forked_current_scope) + + # set given scope as isolation scope + isolation_token = _isolation_scope.set(isolation_scope) + + try: + yield isolation_scope + + finally: + # restore original scopes + try: + _current_scope.reset(current_token) + except LookupError: + capture_internal_exception(sys.exc_info()) + + try: + _isolation_scope.reset(isolation_token) + except LookupError: + capture_internal_exception(sys.exc_info()) + + +def should_send_default_pii(): + # type: () -> bool + """Shortcut for `Scope.get_client().should_send_default_pii()`.""" + return Scope.get_client().should_send_default_pii() + + +# Circular imports +from sentry_sdk.client import NonRecordingClient + +if TYPE_CHECKING: + import sentry_sdk.client diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/scrubber.py b/.venv/lib/python3.12/site-packages/sentry_sdk/scrubber.py new file mode 100644 index 0000000000000000000000000000000000000000..b0576c7e9556e9c63a2b9c1fbb354754142318e8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/scrubber.py @@ -0,0 +1,177 @@ +from sentry_sdk.utils import ( + capture_internal_exceptions, + AnnotatedValue, + iter_event_frames, +) + +from typing import TYPE_CHECKING, cast, List, Dict + +if TYPE_CHECKING: + from sentry_sdk._types import Event + from typing import Optional + + +DEFAULT_DENYLIST = [ + # stolen from relay + "password", + "passwd", + "secret", + "api_key", + "apikey", + "auth", + "credentials", + "mysql_pwd", + "privatekey", + "private_key", + "token", + "session", + # django + "csrftoken", + "sessionid", + # wsgi + "x_csrftoken", + "x_forwarded_for", + "set_cookie", + "cookie", + "authorization", + "x_api_key", + # other common names used in the wild + "aiohttp_session", # aiohttp + "connect.sid", # Express + "csrf_token", # Pyramid + "csrf", # (this is a cookie name used in accepted answers on stack overflow) + "_csrf", # Express + "_csrf_token", # Bottle + "PHPSESSID", # PHP + "_session", # Sanic + "symfony", # Symfony + "user_session", # Vue + "_xsrf", # Tornado + "XSRF-TOKEN", # Angular, Laravel +] + +DEFAULT_PII_DENYLIST = [ + "x_forwarded_for", + "x_real_ip", + "ip_address", + "remote_addr", +] + + +class EventScrubber: + def __init__( + self, denylist=None, recursive=False, send_default_pii=False, pii_denylist=None + ): + # type: (Optional[List[str]], bool, bool, Optional[List[str]]) -> None + """ + A scrubber that goes through the event payload and removes sensitive data configured through denylists. + + :param denylist: A security denylist that is always scrubbed, defaults to DEFAULT_DENYLIST. + :param recursive: Whether to scrub the event payload recursively, default False. + :param send_default_pii: Whether pii is sending is on, pii fields are not scrubbed. + :param pii_denylist: The denylist to use for scrubbing when pii is not sent, defaults to DEFAULT_PII_DENYLIST. + """ + self.denylist = DEFAULT_DENYLIST.copy() if denylist is None else denylist + + if not send_default_pii: + pii_denylist = ( + DEFAULT_PII_DENYLIST.copy() if pii_denylist is None else pii_denylist + ) + self.denylist += pii_denylist + + self.denylist = [x.lower() for x in self.denylist] + self.recursive = recursive + + def scrub_list(self, lst): + # type: (object) -> None + """ + If a list is passed to this method, the method recursively searches the list and any + nested lists for any dictionaries. The method calls scrub_dict on all dictionaries + it finds. + If the parameter passed to this method is not a list, the method does nothing. + """ + if not isinstance(lst, list): + return + + for v in lst: + self.scrub_dict(v) # no-op unless v is a dict + self.scrub_list(v) # no-op unless v is a list + + def scrub_dict(self, d): + # type: (object) -> None + """ + If a dictionary is passed to this method, the method scrubs the dictionary of any + sensitive data. The method calls itself recursively on any nested dictionaries ( + including dictionaries nested in lists) if self.recursive is True. + This method does nothing if the parameter passed to it is not a dictionary. + """ + if not isinstance(d, dict): + return + + for k, v in d.items(): + # The cast is needed because mypy is not smart enough to figure out that k must be a + # string after the isinstance check. + if isinstance(k, str) and k.lower() in self.denylist: + d[k] = AnnotatedValue.substituted_because_contains_sensitive_data() + elif self.recursive: + self.scrub_dict(v) # no-op unless v is a dict + self.scrub_list(v) # no-op unless v is a list + + def scrub_request(self, event): + # type: (Event) -> None + with capture_internal_exceptions(): + if "request" in event: + if "headers" in event["request"]: + self.scrub_dict(event["request"]["headers"]) + if "cookies" in event["request"]: + self.scrub_dict(event["request"]["cookies"]) + if "data" in event["request"]: + self.scrub_dict(event["request"]["data"]) + + def scrub_extra(self, event): + # type: (Event) -> None + with capture_internal_exceptions(): + if "extra" in event: + self.scrub_dict(event["extra"]) + + def scrub_user(self, event): + # type: (Event) -> None + with capture_internal_exceptions(): + if "user" in event: + self.scrub_dict(event["user"]) + + def scrub_breadcrumbs(self, event): + # type: (Event) -> None + with capture_internal_exceptions(): + if "breadcrumbs" in event: + if ( + not isinstance(event["breadcrumbs"], AnnotatedValue) + and "values" in event["breadcrumbs"] + ): + for value in event["breadcrumbs"]["values"]: + if "data" in value: + self.scrub_dict(value["data"]) + + def scrub_frames(self, event): + # type: (Event) -> None + with capture_internal_exceptions(): + for frame in iter_event_frames(event): + if "vars" in frame: + self.scrub_dict(frame["vars"]) + + def scrub_spans(self, event): + # type: (Event) -> None + with capture_internal_exceptions(): + if "spans" in event: + for span in cast(List[Dict[str, object]], event["spans"]): + if "data" in span: + self.scrub_dict(span["data"]) + + def scrub_event(self, event): + # type: (Event) -> None + self.scrub_request(event) + self.scrub_extra(event) + self.scrub_user(event) + self.scrub_breadcrumbs(event) + self.scrub_frames(event) + self.scrub_spans(event) diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/serializer.py b/.venv/lib/python3.12/site-packages/sentry_sdk/serializer.py new file mode 100644 index 0000000000000000000000000000000000000000..1775b1b555927fa658bf5aa3f8ef574be4b9457e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/serializer.py @@ -0,0 +1,405 @@ +import sys +import math +from collections.abc import Mapping, Sequence, Set +from datetime import datetime + +from sentry_sdk.utils import ( + AnnotatedValue, + capture_internal_exception, + disable_capture_event, + format_timestamp, + safe_repr, + strip_string, +) + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from types import TracebackType + + from typing import Any + from typing import Callable + from typing import ContextManager + from typing import Dict + from typing import List + from typing import Optional + from typing import Type + from typing import Union + + from sentry_sdk._types import NotImplementedType + + Span = Dict[str, Any] + + ReprProcessor = Callable[[Any, Dict[str, Any]], Union[NotImplementedType, str]] + Segment = Union[str, int] + + +# Bytes are technically not strings in Python 3, but we can serialize them +serializable_str_types = (str, bytes, bytearray, memoryview) + + +# Maximum length of JSON-serialized event payloads that can be safely sent +# before the server may reject the event due to its size. This is not intended +# to reflect actual values defined server-side, but rather only be an upper +# bound for events sent by the SDK. +# +# Can be overwritten if wanting to send more bytes, e.g. with a custom server. +# When changing this, keep in mind that events may be a little bit larger than +# this value due to attached metadata, so keep the number conservative. +MAX_EVENT_BYTES = 10**6 + +# Maximum depth and breadth of databags. Excess data will be trimmed. If +# max_request_body_size is "always", request bodies won't be trimmed. +MAX_DATABAG_DEPTH = 5 +MAX_DATABAG_BREADTH = 10 +CYCLE_MARKER = "" + + +global_repr_processors = [] # type: List[ReprProcessor] + + +def add_global_repr_processor(processor): + # type: (ReprProcessor) -> None + global_repr_processors.append(processor) + + +sequence_types = [Sequence, Set] # type: List[type] + + +def add_repr_sequence_type(ty): + # type: (type) -> None + sequence_types.append(ty) + + +class Memo: + __slots__ = ("_ids", "_objs") + + def __init__(self): + # type: () -> None + self._ids = {} # type: Dict[int, Any] + self._objs = [] # type: List[Any] + + def memoize(self, obj): + # type: (Any) -> ContextManager[bool] + self._objs.append(obj) + return self + + def __enter__(self): + # type: () -> bool + obj = self._objs[-1] + if id(obj) in self._ids: + return True + else: + self._ids[id(obj)] = obj + return False + + def __exit__( + self, + ty, # type: Optional[Type[BaseException]] + value, # type: Optional[BaseException] + tb, # type: Optional[TracebackType] + ): + # type: (...) -> None + self._ids.pop(id(self._objs.pop()), None) + + +def serialize(event, **kwargs): + # type: (Dict[str, Any], **Any) -> Dict[str, Any] + """ + A very smart serializer that takes a dict and emits a json-friendly dict. + Currently used for serializing the final Event and also prematurely while fetching the stack + local variables for each frame in a stacktrace. + + It works internally with 'databags' which are arbitrary data structures like Mapping, Sequence and Set. + The algorithm itself is a recursive graph walk down the data structures it encounters. + + It has the following responsibilities: + * Trimming databags and keeping them within MAX_DATABAG_BREADTH and MAX_DATABAG_DEPTH. + * Calling safe_repr() on objects appropriately to keep them informative and readable in the final payload. + * Annotating the payload with the _meta field whenever trimming happens. + + :param max_request_body_size: If set to "always", will never trim request bodies. + :param max_value_length: The max length to strip strings to, defaults to sentry_sdk.consts.DEFAULT_MAX_VALUE_LENGTH + :param is_vars: If we're serializing vars early, we want to repr() things that are JSON-serializable to make their type more apparent. For example, it's useful to see the difference between a unicode-string and a bytestring when viewing a stacktrace. + :param custom_repr: A custom repr function that runs before safe_repr on the object to be serialized. If it returns None or throws internally, we will fallback to safe_repr. + + """ + memo = Memo() + path = [] # type: List[Segment] + meta_stack = [] # type: List[Dict[str, Any]] + + keep_request_bodies = kwargs.pop("max_request_body_size", None) == "always" # type: bool + max_value_length = kwargs.pop("max_value_length", None) # type: Optional[int] + is_vars = kwargs.pop("is_vars", False) + custom_repr = kwargs.pop("custom_repr", None) # type: Callable[..., Optional[str]] + + def _safe_repr_wrapper(value): + # type: (Any) -> str + try: + repr_value = None + if custom_repr is not None: + repr_value = custom_repr(value) + return repr_value or safe_repr(value) + except Exception: + return safe_repr(value) + + def _annotate(**meta): + # type: (**Any) -> None + while len(meta_stack) <= len(path): + try: + segment = path[len(meta_stack) - 1] + node = meta_stack[-1].setdefault(str(segment), {}) + except IndexError: + node = {} + + meta_stack.append(node) + + meta_stack[-1].setdefault("", {}).update(meta) + + def _is_databag(): + # type: () -> Optional[bool] + """ + A databag is any value that we need to trim. + True for stuff like vars, request bodies, breadcrumbs and extra. + + :returns: `True` for "yes", `False` for :"no", `None` for "maybe soon". + """ + try: + if is_vars: + return True + + is_request_body = _is_request_body() + if is_request_body in (True, None): + return is_request_body + + p0 = path[0] + if p0 == "breadcrumbs" and path[1] == "values": + path[2] + return True + + if p0 == "extra": + return True + + except IndexError: + return None + + return False + + def _is_span_attribute(): + # type: () -> Optional[bool] + try: + if path[0] == "spans" and path[2] == "data": + return True + except IndexError: + return None + + return False + + def _is_request_body(): + # type: () -> Optional[bool] + try: + if path[0] == "request" and path[1] == "data": + return True + except IndexError: + return None + + return False + + def _serialize_node( + obj, # type: Any + is_databag=None, # type: Optional[bool] + is_request_body=None, # type: Optional[bool] + should_repr_strings=None, # type: Optional[bool] + segment=None, # type: Optional[Segment] + remaining_breadth=None, # type: Optional[Union[int, float]] + remaining_depth=None, # type: Optional[Union[int, float]] + ): + # type: (...) -> Any + if segment is not None: + path.append(segment) + + try: + with memo.memoize(obj) as result: + if result: + return CYCLE_MARKER + + return _serialize_node_impl( + obj, + is_databag=is_databag, + is_request_body=is_request_body, + should_repr_strings=should_repr_strings, + remaining_depth=remaining_depth, + remaining_breadth=remaining_breadth, + ) + except BaseException: + capture_internal_exception(sys.exc_info()) + + if is_databag: + return "" + + return None + finally: + if segment is not None: + path.pop() + del meta_stack[len(path) + 1 :] + + def _flatten_annotated(obj): + # type: (Any) -> Any + if isinstance(obj, AnnotatedValue): + _annotate(**obj.metadata) + obj = obj.value + return obj + + def _serialize_node_impl( + obj, + is_databag, + is_request_body, + should_repr_strings, + remaining_depth, + remaining_breadth, + ): + # type: (Any, Optional[bool], Optional[bool], Optional[bool], Optional[Union[float, int]], Optional[Union[float, int]]) -> Any + if isinstance(obj, AnnotatedValue): + should_repr_strings = False + if should_repr_strings is None: + should_repr_strings = is_vars + + if is_databag is None: + is_databag = _is_databag() + + if is_request_body is None: + is_request_body = _is_request_body() + + if is_databag: + if is_request_body and keep_request_bodies: + remaining_depth = float("inf") + remaining_breadth = float("inf") + else: + if remaining_depth is None: + remaining_depth = MAX_DATABAG_DEPTH + if remaining_breadth is None: + remaining_breadth = MAX_DATABAG_BREADTH + + obj = _flatten_annotated(obj) + + if remaining_depth is not None and remaining_depth <= 0: + _annotate(rem=[["!limit", "x"]]) + if is_databag: + return _flatten_annotated( + strip_string(_safe_repr_wrapper(obj), max_length=max_value_length) + ) + return None + + is_span_attribute = _is_span_attribute() + if (is_databag or is_span_attribute) and global_repr_processors: + hints = {"memo": memo, "remaining_depth": remaining_depth} + for processor in global_repr_processors: + result = processor(obj, hints) + if result is not NotImplemented: + return _flatten_annotated(result) + + sentry_repr = getattr(type(obj), "__sentry_repr__", None) + + if obj is None or isinstance(obj, (bool, int, float)): + if should_repr_strings or ( + isinstance(obj, float) and (math.isinf(obj) or math.isnan(obj)) + ): + return _safe_repr_wrapper(obj) + else: + return obj + + elif callable(sentry_repr): + return sentry_repr(obj) + + elif isinstance(obj, datetime): + return ( + str(format_timestamp(obj)) + if not should_repr_strings + else _safe_repr_wrapper(obj) + ) + + elif isinstance(obj, Mapping): + # Create temporary copy here to avoid calling too much code that + # might mutate our dictionary while we're still iterating over it. + obj = dict(obj.items()) + + rv_dict = {} # type: Dict[str, Any] + i = 0 + + for k, v in obj.items(): + if remaining_breadth is not None and i >= remaining_breadth: + _annotate(len=len(obj)) + break + + str_k = str(k) + v = _serialize_node( + v, + segment=str_k, + should_repr_strings=should_repr_strings, + is_databag=is_databag, + is_request_body=is_request_body, + remaining_depth=( + remaining_depth - 1 if remaining_depth is not None else None + ), + remaining_breadth=remaining_breadth, + ) + rv_dict[str_k] = v + i += 1 + + return rv_dict + + elif not isinstance(obj, serializable_str_types) and isinstance( + obj, tuple(sequence_types) + ): + rv_list = [] + + for i, v in enumerate(obj): + if remaining_breadth is not None and i >= remaining_breadth: + _annotate(len=len(obj)) + break + + rv_list.append( + _serialize_node( + v, + segment=i, + should_repr_strings=should_repr_strings, + is_databag=is_databag, + is_request_body=is_request_body, + remaining_depth=( + remaining_depth - 1 if remaining_depth is not None else None + ), + remaining_breadth=remaining_breadth, + ) + ) + + return rv_list + + if should_repr_strings: + obj = _safe_repr_wrapper(obj) + else: + if isinstance(obj, bytes) or isinstance(obj, bytearray): + obj = obj.decode("utf-8", "replace") + + if not isinstance(obj, str): + obj = _safe_repr_wrapper(obj) + + is_span_description = ( + len(path) == 3 and path[0] == "spans" and path[-1] == "description" + ) + if is_span_description: + return obj + + return _flatten_annotated(strip_string(obj, max_length=max_value_length)) + + # + # Start of serialize() function + # + disable_capture_event.set(True) + try: + serialized_event = _serialize_node(event, **kwargs) + if not is_vars and meta_stack and isinstance(serialized_event, dict): + serialized_event["_meta"] = meta_stack[0] + + return serialized_event + finally: + disable_capture_event.set(False) diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/session.py b/.venv/lib/python3.12/site-packages/sentry_sdk/session.py new file mode 100644 index 0000000000000000000000000000000000000000..af9551c56ef6684ef12390875c33ca48eb35edfc --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/session.py @@ -0,0 +1,177 @@ +import uuid +from datetime import datetime, timezone + +from sentry_sdk.utils import format_timestamp + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing import Optional + from typing import Union + from typing import Any + from typing import Dict + + from sentry_sdk._types import SessionStatus + + +def _minute_trunc(ts): + # type: (datetime) -> datetime + return ts.replace(second=0, microsecond=0) + + +def _make_uuid( + val, # type: Union[str, uuid.UUID] +): + # type: (...) -> uuid.UUID + if isinstance(val, uuid.UUID): + return val + return uuid.UUID(val) + + +class Session: + def __init__( + self, + sid=None, # type: Optional[Union[str, uuid.UUID]] + did=None, # type: Optional[str] + timestamp=None, # type: Optional[datetime] + started=None, # type: Optional[datetime] + duration=None, # type: Optional[float] + status=None, # type: Optional[SessionStatus] + release=None, # type: Optional[str] + environment=None, # type: Optional[str] + user_agent=None, # type: Optional[str] + ip_address=None, # type: Optional[str] + errors=None, # type: Optional[int] + user=None, # type: Optional[Any] + session_mode="application", # type: str + ): + # type: (...) -> None + if sid is None: + sid = uuid.uuid4() + if started is None: + started = datetime.now(timezone.utc) + if status is None: + status = "ok" + self.status = status + self.did = None # type: Optional[str] + self.started = started + self.release = None # type: Optional[str] + self.environment = None # type: Optional[str] + self.duration = None # type: Optional[float] + self.user_agent = None # type: Optional[str] + self.ip_address = None # type: Optional[str] + self.session_mode = session_mode # type: str + self.errors = 0 + + self.update( + sid=sid, + did=did, + timestamp=timestamp, + duration=duration, + release=release, + environment=environment, + user_agent=user_agent, + ip_address=ip_address, + errors=errors, + user=user, + ) + + @property + def truncated_started(self): + # type: (...) -> datetime + return _minute_trunc(self.started) + + def update( + self, + sid=None, # type: Optional[Union[str, uuid.UUID]] + did=None, # type: Optional[str] + timestamp=None, # type: Optional[datetime] + started=None, # type: Optional[datetime] + duration=None, # type: Optional[float] + status=None, # type: Optional[SessionStatus] + release=None, # type: Optional[str] + environment=None, # type: Optional[str] + user_agent=None, # type: Optional[str] + ip_address=None, # type: Optional[str] + errors=None, # type: Optional[int] + user=None, # type: Optional[Any] + ): + # type: (...) -> None + # If a user is supplied we pull some data form it + if user: + if ip_address is None: + ip_address = user.get("ip_address") + if did is None: + did = user.get("id") or user.get("email") or user.get("username") + + if sid is not None: + self.sid = _make_uuid(sid) + if did is not None: + self.did = str(did) + if timestamp is None: + timestamp = datetime.now(timezone.utc) + self.timestamp = timestamp + if started is not None: + self.started = started + if duration is not None: + self.duration = duration + if release is not None: + self.release = release + if environment is not None: + self.environment = environment + if ip_address is not None: + self.ip_address = ip_address + if user_agent is not None: + self.user_agent = user_agent + if errors is not None: + self.errors = errors + + if status is not None: + self.status = status + + def close( + self, + status=None, # type: Optional[SessionStatus] + ): + # type: (...) -> Any + if status is None and self.status == "ok": + status = "exited" + if status is not None: + self.update(status=status) + + def get_json_attrs( + self, + with_user_info=True, # type: Optional[bool] + ): + # type: (...) -> Any + attrs = {} + if self.release is not None: + attrs["release"] = self.release + if self.environment is not None: + attrs["environment"] = self.environment + if with_user_info: + if self.ip_address is not None: + attrs["ip_address"] = self.ip_address + if self.user_agent is not None: + attrs["user_agent"] = self.user_agent + return attrs + + def to_json(self): + # type: (...) -> Any + rv = { + "sid": str(self.sid), + "init": True, + "started": format_timestamp(self.started), + "timestamp": format_timestamp(self.timestamp), + "status": self.status, + } # type: Dict[str, Any] + if self.errors: + rv["errors"] = self.errors + if self.did is not None: + rv["did"] = self.did + if self.duration is not None: + rv["duration"] = self.duration + attrs = self.get_json_attrs() + if attrs: + rv["attrs"] = attrs + return rv diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/sessions.py b/.venv/lib/python3.12/site-packages/sentry_sdk/sessions.py new file mode 100644 index 0000000000000000000000000000000000000000..2bf4ee707af437b1c2386047f7ef0dc62d70bd9c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/sessions.py @@ -0,0 +1,275 @@ +import os +import warnings +from threading import Thread, Lock, Event +from contextlib import contextmanager + +import sentry_sdk +from sentry_sdk.envelope import Envelope +from sentry_sdk.session import Session +from sentry_sdk.utils import format_timestamp + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing import Any + from typing import Callable + from typing import Dict + from typing import Generator + from typing import List + from typing import Optional + from typing import Union + + +def is_auto_session_tracking_enabled(hub=None): + # type: (Optional[sentry_sdk.Hub]) -> Union[Any, bool, None] + """DEPRECATED: Utility function to find out if session tracking is enabled.""" + + # Internal callers should use private _is_auto_session_tracking_enabled, instead. + warnings.warn( + "This function is deprecated and will be removed in the next major release. " + "There is no public API replacement.", + DeprecationWarning, + stacklevel=2, + ) + + if hub is None: + hub = sentry_sdk.Hub.current + + should_track = hub.scope._force_auto_session_tracking + + if should_track is None: + client_options = hub.client.options if hub.client else {} + should_track = client_options.get("auto_session_tracking", False) + + return should_track + + +@contextmanager +def auto_session_tracking(hub=None, session_mode="application"): + # type: (Optional[sentry_sdk.Hub], str) -> Generator[None, None, None] + """DEPRECATED: Use track_session instead + Starts and stops a session automatically around a block. + """ + warnings.warn( + "This function is deprecated and will be removed in the next major release. " + "Use track_session instead.", + DeprecationWarning, + stacklevel=2, + ) + + if hub is None: + hub = sentry_sdk.Hub.current + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + should_track = is_auto_session_tracking_enabled(hub) + if should_track: + hub.start_session(session_mode=session_mode) + try: + yield + finally: + if should_track: + hub.end_session() + + +def is_auto_session_tracking_enabled_scope(scope): + # type: (sentry_sdk.Scope) -> bool + """ + DEPRECATED: Utility function to find out if session tracking is enabled. + """ + + warnings.warn( + "This function is deprecated and will be removed in the next major release. " + "There is no public API replacement.", + DeprecationWarning, + stacklevel=2, + ) + + # Internal callers should use private _is_auto_session_tracking_enabled, instead. + return _is_auto_session_tracking_enabled(scope) + + +def _is_auto_session_tracking_enabled(scope): + # type: (sentry_sdk.Scope) -> bool + """ + Utility function to find out if session tracking is enabled. + """ + + should_track = scope._force_auto_session_tracking + if should_track is None: + client_options = sentry_sdk.get_client().options + should_track = client_options.get("auto_session_tracking", False) + + return should_track + + +@contextmanager +def auto_session_tracking_scope(scope, session_mode="application"): + # type: (sentry_sdk.Scope, str) -> Generator[None, None, None] + """DEPRECATED: This function is a deprecated alias for track_session. + Starts and stops a session automatically around a block. + """ + + warnings.warn( + "This function is a deprecated alias for track_session and will be removed in the next major release.", + DeprecationWarning, + stacklevel=2, + ) + + with track_session(scope, session_mode=session_mode): + yield + + +@contextmanager +def track_session(scope, session_mode="application"): + # type: (sentry_sdk.Scope, str) -> Generator[None, None, None] + """ + Start a new session in the provided scope, assuming session tracking is enabled. + This is a no-op context manager if session tracking is not enabled. + """ + + should_track = _is_auto_session_tracking_enabled(scope) + if should_track: + scope.start_session(session_mode=session_mode) + try: + yield + finally: + if should_track: + scope.end_session() + + +TERMINAL_SESSION_STATES = ("exited", "abnormal", "crashed") +MAX_ENVELOPE_ITEMS = 100 + + +def make_aggregate_envelope(aggregate_states, attrs): + # type: (Any, Any) -> Any + return {"attrs": dict(attrs), "aggregates": list(aggregate_states.values())} + + +class SessionFlusher: + def __init__( + self, + capture_func, # type: Callable[[Envelope], None] + flush_interval=60, # type: int + ): + # type: (...) -> None + self.capture_func = capture_func + self.flush_interval = flush_interval + self.pending_sessions = [] # type: List[Any] + self.pending_aggregates = {} # type: Dict[Any, Any] + self._thread = None # type: Optional[Thread] + self._thread_lock = Lock() + self._aggregate_lock = Lock() + self._thread_for_pid = None # type: Optional[int] + self.__shutdown_requested = Event() + + def flush(self): + # type: (...) -> None + pending_sessions = self.pending_sessions + self.pending_sessions = [] + + with self._aggregate_lock: + pending_aggregates = self.pending_aggregates + self.pending_aggregates = {} + + envelope = Envelope() + for session in pending_sessions: + if len(envelope.items) == MAX_ENVELOPE_ITEMS: + self.capture_func(envelope) + envelope = Envelope() + + envelope.add_session(session) + + for attrs, states in pending_aggregates.items(): + if len(envelope.items) == MAX_ENVELOPE_ITEMS: + self.capture_func(envelope) + envelope = Envelope() + + envelope.add_sessions(make_aggregate_envelope(states, attrs)) + + if len(envelope.items) > 0: + self.capture_func(envelope) + + def _ensure_running(self): + # type: (...) -> None + """ + Check that we have an active thread to run in, or create one if not. + + Note that this might fail (e.g. in Python 3.12 it's not possible to + spawn new threads at interpreter shutdown). In that case self._running + will be False after running this function. + """ + if self._thread_for_pid == os.getpid() and self._thread is not None: + return None + with self._thread_lock: + if self._thread_for_pid == os.getpid() and self._thread is not None: + return None + + def _thread(): + # type: (...) -> None + running = True + while running: + running = not self.__shutdown_requested.wait(self.flush_interval) + self.flush() + + thread = Thread(target=_thread) + thread.daemon = True + try: + thread.start() + except RuntimeError: + # Unfortunately at this point the interpreter is in a state that no + # longer allows us to spawn a thread and we have to bail. + self.__shutdown_requested.set() + return None + + self._thread = thread + self._thread_for_pid = os.getpid() + + return None + + def add_aggregate_session( + self, + session, # type: Session + ): + # type: (...) -> None + # NOTE on `session.did`: + # the protocol can deal with buckets that have a distinct-id, however + # in practice we expect the python SDK to have an extremely high cardinality + # here, effectively making aggregation useless, therefore we do not + # aggregate per-did. + + # For this part we can get away with using the global interpreter lock + with self._aggregate_lock: + attrs = session.get_json_attrs(with_user_info=False) + primary_key = tuple(sorted(attrs.items())) + secondary_key = session.truncated_started # (, session.did) + states = self.pending_aggregates.setdefault(primary_key, {}) + state = states.setdefault(secondary_key, {}) + + if "started" not in state: + state["started"] = format_timestamp(session.truncated_started) + # if session.did is not None: + # state["did"] = session.did + if session.status == "crashed": + state["crashed"] = state.get("crashed", 0) + 1 + elif session.status == "abnormal": + state["abnormal"] = state.get("abnormal", 0) + 1 + elif session.errors > 0: + state["errored"] = state.get("errored", 0) + 1 + else: + state["exited"] = state.get("exited", 0) + 1 + + def add_session( + self, + session, # type: Session + ): + # type: (...) -> None + if session.session_mode == "request": + self.add_aggregate_session(session) + else: + self.pending_sessions.append(session.to_json()) + self._ensure_running() + + def kill(self): + # type: (...) -> None + self.__shutdown_requested.set() diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/spotlight.py b/.venv/lib/python3.12/site-packages/sentry_sdk/spotlight.py new file mode 100644 index 0000000000000000000000000000000000000000..4ac427b9c1f5b8708044e43d0bb80d68b23396a5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/spotlight.py @@ -0,0 +1,242 @@ +import io +import logging +import os +import urllib.parse +import urllib.request +import urllib.error +import urllib3 +import sys + +from itertools import chain, product + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing import Any + from typing import Callable + from typing import Dict + from typing import Optional + from typing import Self + +from sentry_sdk.utils import ( + logger as sentry_logger, + env_to_bool, + capture_internal_exceptions, +) +from sentry_sdk.envelope import Envelope + + +logger = logging.getLogger("spotlight") + + +DEFAULT_SPOTLIGHT_URL = "http://localhost:8969/stream" +DJANGO_SPOTLIGHT_MIDDLEWARE_PATH = "sentry_sdk.spotlight.SpotlightMiddleware" + + +class SpotlightClient: + def __init__(self, url): + # type: (str) -> None + self.url = url + self.http = urllib3.PoolManager() + self.fails = 0 + + def capture_envelope(self, envelope): + # type: (Envelope) -> None + body = io.BytesIO() + envelope.serialize_into(body) + try: + req = self.http.request( + url=self.url, + body=body.getvalue(), + method="POST", + headers={ + "Content-Type": "application/x-sentry-envelope", + }, + ) + req.close() + self.fails = 0 + except Exception as e: + if self.fails < 2: + sentry_logger.warning(str(e)) + self.fails += 1 + elif self.fails == 2: + self.fails += 1 + sentry_logger.warning( + "Looks like Spotlight is not running, will keep trying to send events but will not log errors." + ) + # omitting self.fails += 1 in the `else:` case intentionally + # to avoid overflowing the variable if Spotlight never becomes reachable + + +try: + from django.utils.deprecation import MiddlewareMixin + from django.http import HttpResponseServerError, HttpResponse, HttpRequest + from django.conf import settings + + SPOTLIGHT_JS_ENTRY_PATH = "/assets/main.js" + SPOTLIGHT_JS_SNIPPET_PATTERN = ( + "\n" + '\n' + ) + SPOTLIGHT_ERROR_PAGE_SNIPPET = ( + '\n' + '\n' + ) + CHARSET_PREFIX = "charset=" + BODY_TAG_NAME = "body" + BODY_CLOSE_TAG_POSSIBILITIES = tuple( + "".format("".join(chars)) + for chars in product(*zip(BODY_TAG_NAME.upper(), BODY_TAG_NAME.lower())) + ) + + class SpotlightMiddleware(MiddlewareMixin): # type: ignore[misc] + _spotlight_script = None # type: Optional[str] + _spotlight_url = None # type: Optional[str] + + def __init__(self, get_response): + # type: (Self, Callable[..., HttpResponse]) -> None + super().__init__(get_response) + + import sentry_sdk.api + + self.sentry_sdk = sentry_sdk.api + + spotlight_client = self.sentry_sdk.get_client().spotlight + if spotlight_client is None: + sentry_logger.warning( + "Cannot find Spotlight client from SpotlightMiddleware, disabling the middleware." + ) + return None + # Spotlight URL has a trailing `/stream` part at the end so split it off + self._spotlight_url = urllib.parse.urljoin(spotlight_client.url, "../") + + @property + def spotlight_script(self): + # type: (Self) -> Optional[str] + if self._spotlight_url is not None and self._spotlight_script is None: + try: + spotlight_js_url = urllib.parse.urljoin( + self._spotlight_url, SPOTLIGHT_JS_ENTRY_PATH + ) + req = urllib.request.Request( + spotlight_js_url, + method="HEAD", + ) + urllib.request.urlopen(req) + self._spotlight_script = SPOTLIGHT_JS_SNIPPET_PATTERN.format( + spotlight_url=self._spotlight_url, + spotlight_js_url=spotlight_js_url, + ) + except urllib.error.URLError as err: + sentry_logger.debug( + "Cannot get Spotlight JS to inject at %s. SpotlightMiddleware will not be very useful.", + spotlight_js_url, + exc_info=err, + ) + + return self._spotlight_script + + def process_response(self, _request, response): + # type: (Self, HttpRequest, HttpResponse) -> Optional[HttpResponse] + content_type_header = tuple( + p.strip() + for p in response.headers.get("Content-Type", "").lower().split(";") + ) + content_type = content_type_header[0] + if len(content_type_header) > 1 and content_type_header[1].startswith( + CHARSET_PREFIX + ): + encoding = content_type_header[1][len(CHARSET_PREFIX) :] + else: + encoding = "utf-8" + + if ( + self.spotlight_script is not None + and not response.streaming + and content_type == "text/html" + ): + content_length = len(response.content) + injection = self.spotlight_script.encode(encoding) + injection_site = next( + ( + idx + for idx in ( + response.content.rfind(body_variant.encode(encoding)) + for body_variant in BODY_CLOSE_TAG_POSSIBILITIES + ) + if idx > -1 + ), + content_length, + ) + + # This approach works even when we don't have a `` tag + response.content = ( + response.content[:injection_site] + + injection + + response.content[injection_site:] + ) + + if response.has_header("Content-Length"): + response.headers["Content-Length"] = content_length + len(injection) + + return response + + def process_exception(self, _request, exception): + # type: (Self, HttpRequest, Exception) -> Optional[HttpResponseServerError] + if not settings.DEBUG or not self._spotlight_url: + return None + + try: + spotlight = ( + urllib.request.urlopen(self._spotlight_url).read().decode("utf-8") + ) + except urllib.error.URLError: + return None + else: + event_id = self.sentry_sdk.capture_exception(exception) + return HttpResponseServerError( + spotlight.replace( + "", + SPOTLIGHT_ERROR_PAGE_SNIPPET.format( + spotlight_url=self._spotlight_url, event_id=event_id + ), + ) + ) + +except ImportError: + settings = None + + +def setup_spotlight(options): + # type: (Dict[str, Any]) -> Optional[SpotlightClient] + _handler = logging.StreamHandler(sys.stderr) + _handler.setFormatter(logging.Formatter(" [spotlight] %(levelname)s: %(message)s")) + logger.addHandler(_handler) + logger.setLevel(logging.INFO) + + url = options.get("spotlight") + + if url is True: + url = DEFAULT_SPOTLIGHT_URL + + if not isinstance(url, str): + return None + + with capture_internal_exceptions(): + if ( + settings is not None + and settings.DEBUG + and env_to_bool(os.environ.get("SENTRY_SPOTLIGHT_ON_ERROR", "1")) + and env_to_bool(os.environ.get("SENTRY_SPOTLIGHT_MIDDLEWARE", "1")) + ): + middleware = settings.MIDDLEWARE + if DJANGO_SPOTLIGHT_MIDDLEWARE_PATH not in middleware: + settings.MIDDLEWARE = type(middleware)( + chain(middleware, (DJANGO_SPOTLIGHT_MIDDLEWARE_PATH,)) + ) + logger.info("Enabled Spotlight integration for Django") + + client = SpotlightClient(url) + logger.info("Enabled Spotlight using sidecar at %s", url) + + return client diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/tracing.py b/.venv/lib/python3.12/site-packages/sentry_sdk/tracing.py new file mode 100644 index 0000000000000000000000000000000000000000..0d652e490addb8c4a6e5101070a8b65ad7100cc5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/tracing.py @@ -0,0 +1,1486 @@ +import uuid +import warnings +from datetime import datetime, timedelta, timezone +from enum import Enum + +import sentry_sdk +from sentry_sdk.consts import INSTRUMENTER, SPANSTATUS, SPANDATA, SPANTEMPLATE +from sentry_sdk.profiler.continuous_profiler import get_profiler_id +from sentry_sdk.utils import ( + capture_internal_exceptions, + get_current_thread_meta, + is_valid_sample_rate, + logger, + nanosecond_time, + should_be_treated_as_error, +) + +from typing import TYPE_CHECKING + + +if TYPE_CHECKING: + from collections.abc import Callable, Mapping, MutableMapping + from typing import Any + from typing import Dict + from typing import Iterator + from typing import List + from typing import Optional + from typing import overload + from typing import ParamSpec + from typing import Tuple + from typing import Union + from typing import TypeVar + from typing import Set + + from typing_extensions import TypedDict, Unpack + + P = ParamSpec("P") + R = TypeVar("R") + + from sentry_sdk.profiler.continuous_profiler import ContinuousProfile + from sentry_sdk.profiler.transaction_profiler import Profile + from sentry_sdk._types import ( + Event, + MeasurementUnit, + SamplingContext, + MeasurementValue, + ) + + class SpanKwargs(TypedDict, total=False): + trace_id: str + """ + The trace ID of the root span. If this new span is to be the root span, + omit this parameter, and a new trace ID will be generated. + """ + + span_id: str + """The span ID of this span. If omitted, a new span ID will be generated.""" + + parent_span_id: str + """The span ID of the parent span, if applicable.""" + + same_process_as_parent: bool + """Whether this span is in the same process as the parent span.""" + + sampled: bool + """ + Whether the span should be sampled. Overrides the default sampling decision + for this span when provided. + """ + + op: str + """ + The span's operation. A list of recommended values is available here: + https://develop.sentry.dev/sdk/performance/span-operations/ + """ + + description: str + """A description of what operation is being performed within the span. This argument is DEPRECATED. Please use the `name` parameter, instead.""" + + hub: Optional["sentry_sdk.Hub"] + """The hub to use for this span. This argument is DEPRECATED. Please use the `scope` parameter, instead.""" + + status: str + """The span's status. Possible values are listed at https://develop.sentry.dev/sdk/event-payloads/span/""" + + containing_transaction: Optional["Transaction"] + """The transaction that this span belongs to.""" + + start_timestamp: Optional[Union[datetime, float]] + """ + The timestamp when the span started. If omitted, the current time + will be used. + """ + + scope: "sentry_sdk.Scope" + """The scope to use for this span. If not provided, we use the current scope.""" + + origin: str + """ + The origin of the span. + See https://develop.sentry.dev/sdk/performance/trace-origin/ + Default "manual". + """ + + name: str + """A string describing what operation is being performed within the span/transaction.""" + + class TransactionKwargs(SpanKwargs, total=False): + source: str + """ + A string describing the source of the transaction name. This will be used to determine the transaction's type. + See https://develop.sentry.dev/sdk/event-payloads/transaction/#transaction-annotations for more information. + Default "custom". + """ + + parent_sampled: bool + """Whether the parent transaction was sampled. If True this transaction will be kept, if False it will be discarded.""" + + baggage: "Baggage" + """The W3C baggage header value. (see https://www.w3.org/TR/baggage/)""" + + ProfileContext = TypedDict( + "ProfileContext", + { + "profiler_id": str, + }, + ) + +BAGGAGE_HEADER_NAME = "baggage" +SENTRY_TRACE_HEADER_NAME = "sentry-trace" + + +# Transaction source +# see https://develop.sentry.dev/sdk/event-payloads/transaction/#transaction-annotations +class TransactionSource(str, Enum): + COMPONENT = "component" + CUSTOM = "custom" + ROUTE = "route" + TASK = "task" + URL = "url" + VIEW = "view" + + def __str__(self): + # type: () -> str + return self.value + + +# These are typically high cardinality and the server hates them +LOW_QUALITY_TRANSACTION_SOURCES = [ + TransactionSource.URL, +] + +SOURCE_FOR_STYLE = { + "endpoint": TransactionSource.COMPONENT, + "function_name": TransactionSource.COMPONENT, + "handler_name": TransactionSource.COMPONENT, + "method_and_path_pattern": TransactionSource.ROUTE, + "path": TransactionSource.URL, + "route_name": TransactionSource.COMPONENT, + "route_pattern": TransactionSource.ROUTE, + "uri_template": TransactionSource.ROUTE, + "url": TransactionSource.ROUTE, +} + + +def get_span_status_from_http_code(http_status_code): + # type: (int) -> str + """ + Returns the Sentry status corresponding to the given HTTP status code. + + See: https://develop.sentry.dev/sdk/event-payloads/contexts/#trace-context + """ + if http_status_code < 400: + return SPANSTATUS.OK + + elif 400 <= http_status_code < 500: + if http_status_code == 403: + return SPANSTATUS.PERMISSION_DENIED + elif http_status_code == 404: + return SPANSTATUS.NOT_FOUND + elif http_status_code == 429: + return SPANSTATUS.RESOURCE_EXHAUSTED + elif http_status_code == 413: + return SPANSTATUS.FAILED_PRECONDITION + elif http_status_code == 401: + return SPANSTATUS.UNAUTHENTICATED + elif http_status_code == 409: + return SPANSTATUS.ALREADY_EXISTS + else: + return SPANSTATUS.INVALID_ARGUMENT + + elif 500 <= http_status_code < 600: + if http_status_code == 504: + return SPANSTATUS.DEADLINE_EXCEEDED + elif http_status_code == 501: + return SPANSTATUS.UNIMPLEMENTED + elif http_status_code == 503: + return SPANSTATUS.UNAVAILABLE + else: + return SPANSTATUS.INTERNAL_ERROR + + return SPANSTATUS.UNKNOWN_ERROR + + +class _SpanRecorder: + """Limits the number of spans recorded in a transaction.""" + + __slots__ = ("maxlen", "spans", "dropped_spans") + + def __init__(self, maxlen): + # type: (int) -> None + # FIXME: this is `maxlen - 1` only to preserve historical behavior + # enforced by tests. + # Either this should be changed to `maxlen` or the JS SDK implementation + # should be changed to match a consistent interpretation of what maxlen + # limits: either transaction+spans or only child spans. + self.maxlen = maxlen - 1 + self.spans = [] # type: List[Span] + self.dropped_spans = 0 # type: int + + def add(self, span): + # type: (Span) -> None + if len(self.spans) > self.maxlen: + span._span_recorder = None + self.dropped_spans += 1 + else: + self.spans.append(span) + + +class Span: + """A span holds timing information of a block of code. + Spans can have multiple child spans thus forming a span tree. + + :param trace_id: The trace ID of the root span. If this new span is to be the root span, + omit this parameter, and a new trace ID will be generated. + :param span_id: The span ID of this span. If omitted, a new span ID will be generated. + :param parent_span_id: The span ID of the parent span, if applicable. + :param same_process_as_parent: Whether this span is in the same process as the parent span. + :param sampled: Whether the span should be sampled. Overrides the default sampling decision + for this span when provided. + :param op: The span's operation. A list of recommended values is available here: + https://develop.sentry.dev/sdk/performance/span-operations/ + :param description: A description of what operation is being performed within the span. + + .. deprecated:: 2.15.0 + Please use the `name` parameter, instead. + :param name: A string describing what operation is being performed within the span. + :param hub: The hub to use for this span. + + .. deprecated:: 2.0.0 + Please use the `scope` parameter, instead. + :param status: The span's status. Possible values are listed at + https://develop.sentry.dev/sdk/event-payloads/span/ + :param containing_transaction: The transaction that this span belongs to. + :param start_timestamp: The timestamp when the span started. If omitted, the current time + will be used. + :param scope: The scope to use for this span. If not provided, we use the current scope. + """ + + __slots__ = ( + "_trace_id", + "_span_id", + "parent_span_id", + "same_process_as_parent", + "sampled", + "op", + "description", + "_measurements", + "start_timestamp", + "_start_timestamp_monotonic_ns", + "status", + "timestamp", + "_tags", + "_data", + "_span_recorder", + "hub", + "_context_manager_state", + "_containing_transaction", + "scope", + "origin", + "name", + "_flags", + "_flags_capacity", + ) + + def __init__( + self, + trace_id=None, # type: Optional[str] + span_id=None, # type: Optional[str] + parent_span_id=None, # type: Optional[str] + same_process_as_parent=True, # type: bool + sampled=None, # type: Optional[bool] + op=None, # type: Optional[str] + description=None, # type: Optional[str] + hub=None, # type: Optional[sentry_sdk.Hub] # deprecated + status=None, # type: Optional[str] + containing_transaction=None, # type: Optional[Transaction] + start_timestamp=None, # type: Optional[Union[datetime, float]] + scope=None, # type: Optional[sentry_sdk.Scope] + origin="manual", # type: str + name=None, # type: Optional[str] + ): + # type: (...) -> None + self._trace_id = trace_id + self._span_id = span_id + self.parent_span_id = parent_span_id + self.same_process_as_parent = same_process_as_parent + self.sampled = sampled + self.op = op + self.description = name or description + self.status = status + self.hub = hub # backwards compatibility + self.scope = scope + self.origin = origin + self._measurements = {} # type: Dict[str, MeasurementValue] + self._tags = {} # type: MutableMapping[str, str] + self._data = {} # type: Dict[str, Any] + self._containing_transaction = containing_transaction + self._flags = {} # type: Dict[str, bool] + self._flags_capacity = 10 + + if hub is not None: + warnings.warn( + "The `hub` parameter is deprecated. Please use `scope` instead.", + DeprecationWarning, + stacklevel=2, + ) + + self.scope = self.scope or hub.scope + + if start_timestamp is None: + start_timestamp = datetime.now(timezone.utc) + elif isinstance(start_timestamp, float): + start_timestamp = datetime.fromtimestamp(start_timestamp, timezone.utc) + self.start_timestamp = start_timestamp + try: + # profiling depends on this value and requires that + # it is measured in nanoseconds + self._start_timestamp_monotonic_ns = nanosecond_time() + except AttributeError: + pass + + #: End timestamp of span + self.timestamp = None # type: Optional[datetime] + + self._span_recorder = None # type: Optional[_SpanRecorder] + + self.update_active_thread() + self.set_profiler_id(get_profiler_id()) + + # TODO this should really live on the Transaction class rather than the Span + # class + def init_span_recorder(self, maxlen): + # type: (int) -> None + if self._span_recorder is None: + self._span_recorder = _SpanRecorder(maxlen) + + @property + def trace_id(self): + # type: () -> str + if not self._trace_id: + self._trace_id = uuid.uuid4().hex + + return self._trace_id + + @trace_id.setter + def trace_id(self, value): + # type: (str) -> None + self._trace_id = value + + @property + def span_id(self): + # type: () -> str + if not self._span_id: + self._span_id = uuid.uuid4().hex[16:] + + return self._span_id + + @span_id.setter + def span_id(self, value): + # type: (str) -> None + self._span_id = value + + def __repr__(self): + # type: () -> str + return ( + "<%s(op=%r, description:%r, trace_id=%r, span_id=%r, parent_span_id=%r, sampled=%r, origin=%r)>" + % ( + self.__class__.__name__, + self.op, + self.description, + self.trace_id, + self.span_id, + self.parent_span_id, + self.sampled, + self.origin, + ) + ) + + def __enter__(self): + # type: () -> Span + scope = self.scope or sentry_sdk.get_current_scope() + old_span = scope.span + scope.span = self + self._context_manager_state = (scope, old_span) + return self + + def __exit__(self, ty, value, tb): + # type: (Optional[Any], Optional[Any], Optional[Any]) -> None + if value is not None and should_be_treated_as_error(ty, value): + if self.status != SPANSTATUS.ERROR: + self.set_status(SPANSTATUS.INTERNAL_ERROR) + + with capture_internal_exceptions(): + scope, old_span = self._context_manager_state + del self._context_manager_state + self.finish(scope) + scope.span = old_span + + @property + def containing_transaction(self): + # type: () -> Optional[Transaction] + """The ``Transaction`` that this span belongs to. + The ``Transaction`` is the root of the span tree, + so one could also think of this ``Transaction`` as the "root span".""" + + # this is a getter rather than a regular attribute so that transactions + # can return `self` here instead (as a way to prevent them circularly + # referencing themselves) + return self._containing_transaction + + def start_child(self, instrumenter=INSTRUMENTER.SENTRY, **kwargs): + # type: (str, **Any) -> Span + """ + Start a sub-span from the current span or transaction. + + Takes the same arguments as the initializer of :py:class:`Span`. The + trace id, sampling decision, transaction pointer, and span recorder are + inherited from the current span/transaction. + + The instrumenter parameter is deprecated for user code, and it will + be removed in the next major version. Going forward, it should only + be used by the SDK itself. + """ + if kwargs.get("description") is not None: + warnings.warn( + "The `description` parameter is deprecated. Please use `name` instead.", + DeprecationWarning, + stacklevel=2, + ) + + configuration_instrumenter = sentry_sdk.get_client().options["instrumenter"] + + if instrumenter != configuration_instrumenter: + return NoOpSpan() + + kwargs.setdefault("sampled", self.sampled) + + child = Span( + trace_id=self.trace_id, + parent_span_id=self.span_id, + containing_transaction=self.containing_transaction, + **kwargs, + ) + + span_recorder = ( + self.containing_transaction and self.containing_transaction._span_recorder + ) + if span_recorder: + span_recorder.add(child) + + return child + + @classmethod + def continue_from_environ( + cls, + environ, # type: Mapping[str, str] + **kwargs, # type: Any + ): + # type: (...) -> Transaction + """ + Create a Transaction with the given params, then add in data pulled from + the ``sentry-trace`` and ``baggage`` headers from the environ (if any) + before returning the Transaction. + + This is different from :py:meth:`~sentry_sdk.tracing.Span.continue_from_headers` + in that it assumes header names in the form ``HTTP_HEADER_NAME`` - + such as you would get from a WSGI/ASGI environ - + rather than the form ``header-name``. + + :param environ: The ASGI/WSGI environ to pull information from. + """ + if cls is Span: + logger.warning( + "Deprecated: use Transaction.continue_from_environ " + "instead of Span.continue_from_environ." + ) + return Transaction.continue_from_headers(EnvironHeaders(environ), **kwargs) + + @classmethod + def continue_from_headers( + cls, + headers, # type: Mapping[str, str] + *, + _sample_rand=None, # type: Optional[str] + **kwargs, # type: Any + ): + # type: (...) -> Transaction + """ + Create a transaction with the given params (including any data pulled from + the ``sentry-trace`` and ``baggage`` headers). + + :param headers: The dictionary with the HTTP headers to pull information from. + :param _sample_rand: If provided, we override the sample_rand value from the + incoming headers with this value. (internal use only) + """ + # TODO move this to the Transaction class + if cls is Span: + logger.warning( + "Deprecated: use Transaction.continue_from_headers " + "instead of Span.continue_from_headers." + ) + + # TODO-neel move away from this kwargs stuff, it's confusing and opaque + # make more explicit + baggage = Baggage.from_incoming_header( + headers.get(BAGGAGE_HEADER_NAME), _sample_rand=_sample_rand + ) + kwargs.update({BAGGAGE_HEADER_NAME: baggage}) + + sentrytrace_kwargs = extract_sentrytrace_data( + headers.get(SENTRY_TRACE_HEADER_NAME) + ) + + if sentrytrace_kwargs is not None: + kwargs.update(sentrytrace_kwargs) + + # If there's an incoming sentry-trace but no incoming baggage header, + # for instance in traces coming from older SDKs, + # baggage will be empty and immutable and won't be populated as head SDK. + baggage.freeze() + + transaction = Transaction(**kwargs) + transaction.same_process_as_parent = False + + return transaction + + def iter_headers(self): + # type: () -> Iterator[Tuple[str, str]] + """ + Creates a generator which returns the span's ``sentry-trace`` and ``baggage`` headers. + If the span's containing transaction doesn't yet have a ``baggage`` value, + this will cause one to be generated and stored. + """ + if not self.containing_transaction: + # Do not propagate headers if there is no containing transaction. Otherwise, this + # span ends up being the root span of a new trace, and since it does not get sent + # to Sentry, the trace will be missing a root transaction. The dynamic sampling + # context will also be missing, breaking dynamic sampling & traces. + return + + yield SENTRY_TRACE_HEADER_NAME, self.to_traceparent() + + baggage = self.containing_transaction.get_baggage().serialize() + if baggage: + yield BAGGAGE_HEADER_NAME, baggage + + @classmethod + def from_traceparent( + cls, + traceparent, # type: Optional[str] + **kwargs, # type: Any + ): + # type: (...) -> Optional[Transaction] + """ + DEPRECATED: Use :py:meth:`sentry_sdk.tracing.Span.continue_from_headers`. + + Create a ``Transaction`` with the given params, then add in data pulled from + the given ``sentry-trace`` header value before returning the ``Transaction``. + """ + logger.warning( + "Deprecated: Use Transaction.continue_from_headers(headers, **kwargs) " + "instead of from_traceparent(traceparent, **kwargs)" + ) + + if not traceparent: + return None + + return cls.continue_from_headers( + {SENTRY_TRACE_HEADER_NAME: traceparent}, **kwargs + ) + + def to_traceparent(self): + # type: () -> str + if self.sampled is True: + sampled = "1" + elif self.sampled is False: + sampled = "0" + else: + sampled = None + + traceparent = "%s-%s" % (self.trace_id, self.span_id) + if sampled is not None: + traceparent += "-%s" % (sampled,) + + return traceparent + + def to_baggage(self): + # type: () -> Optional[Baggage] + """Returns the :py:class:`~sentry_sdk.tracing_utils.Baggage` + associated with this ``Span``, if any. (Taken from the root of the span tree.) + """ + if self.containing_transaction: + return self.containing_transaction.get_baggage() + return None + + def set_tag(self, key, value): + # type: (str, Any) -> None + self._tags[key] = value + + def set_data(self, key, value): + # type: (str, Any) -> None + self._data[key] = value + + def update_data(self, data): + # type: (Dict[str, Any]) -> None + self._data.update(data) + + def set_flag(self, flag, result): + # type: (str, bool) -> None + if len(self._flags) < self._flags_capacity: + self._flags[flag] = result + + def set_status(self, value): + # type: (str) -> None + self.status = value + + def set_measurement(self, name, value, unit=""): + # type: (str, float, MeasurementUnit) -> None + """ + .. deprecated:: 2.28.0 + This function is deprecated and will be removed in the next major release. + """ + + warnings.warn( + "`set_measurement()` is deprecated and will be removed in the next major version. Please use `set_data()` instead.", + DeprecationWarning, + stacklevel=2, + ) + self._measurements[name] = {"value": value, "unit": unit} + + def set_thread(self, thread_id, thread_name): + # type: (Optional[int], Optional[str]) -> None + + if thread_id is not None: + self.set_data(SPANDATA.THREAD_ID, str(thread_id)) + + if thread_name is not None: + self.set_data(SPANDATA.THREAD_NAME, thread_name) + + def set_profiler_id(self, profiler_id): + # type: (Optional[str]) -> None + if profiler_id is not None: + self.set_data(SPANDATA.PROFILER_ID, profiler_id) + + def set_http_status(self, http_status): + # type: (int) -> None + self.set_tag( + "http.status_code", str(http_status) + ) # we keep this for backwards compatibility + self.set_data(SPANDATA.HTTP_STATUS_CODE, http_status) + self.set_status(get_span_status_from_http_code(http_status)) + + def is_success(self): + # type: () -> bool + return self.status == "ok" + + def finish(self, scope=None, end_timestamp=None): + # type: (Optional[sentry_sdk.Scope], Optional[Union[float, datetime]]) -> Optional[str] + """ + Sets the end timestamp of the span. + + Additionally it also creates a breadcrumb from the span, + if the span represents a database or HTTP request. + + :param scope: The scope to use for this transaction. + If not provided, the current scope will be used. + :param end_timestamp: Optional timestamp that should + be used as timestamp instead of the current time. + + :return: Always ``None``. The type is ``Optional[str]`` to match + the return value of :py:meth:`sentry_sdk.tracing.Transaction.finish`. + """ + if self.timestamp is not None: + # This span is already finished, ignore. + return None + + try: + if end_timestamp: + if isinstance(end_timestamp, float): + end_timestamp = datetime.fromtimestamp(end_timestamp, timezone.utc) + self.timestamp = end_timestamp + else: + elapsed = nanosecond_time() - self._start_timestamp_monotonic_ns + self.timestamp = self.start_timestamp + timedelta( + microseconds=elapsed / 1000 + ) + except AttributeError: + self.timestamp = datetime.now(timezone.utc) + + scope = scope or sentry_sdk.get_current_scope() + maybe_create_breadcrumbs_from_span(scope, self) + + return None + + def to_json(self): + # type: () -> Dict[str, Any] + """Returns a JSON-compatible representation of the span.""" + + rv = { + "trace_id": self.trace_id, + "span_id": self.span_id, + "parent_span_id": self.parent_span_id, + "same_process_as_parent": self.same_process_as_parent, + "op": self.op, + "description": self.description, + "start_timestamp": self.start_timestamp, + "timestamp": self.timestamp, + "origin": self.origin, + } # type: Dict[str, Any] + + if self.status: + self._tags["status"] = self.status + + if len(self._measurements) > 0: + rv["measurements"] = self._measurements + + tags = self._tags + if tags: + rv["tags"] = tags + + data = {} + data.update(self._flags) + data.update(self._data) + if data: + rv["data"] = data + + return rv + + def get_trace_context(self): + # type: () -> Any + rv = { + "trace_id": self.trace_id, + "span_id": self.span_id, + "parent_span_id": self.parent_span_id, + "op": self.op, + "description": self.description, + "origin": self.origin, + } # type: Dict[str, Any] + if self.status: + rv["status"] = self.status + + if self.containing_transaction: + rv["dynamic_sampling_context"] = ( + self.containing_transaction.get_baggage().dynamic_sampling_context() + ) + + data = {} + + thread_id = self._data.get(SPANDATA.THREAD_ID) + if thread_id is not None: + data["thread.id"] = thread_id + + thread_name = self._data.get(SPANDATA.THREAD_NAME) + if thread_name is not None: + data["thread.name"] = thread_name + + if data: + rv["data"] = data + + return rv + + def get_profile_context(self): + # type: () -> Optional[ProfileContext] + profiler_id = self._data.get(SPANDATA.PROFILER_ID) + if profiler_id is None: + return None + + return { + "profiler_id": profiler_id, + } + + def update_active_thread(self): + # type: () -> None + thread_id, thread_name = get_current_thread_meta() + self.set_thread(thread_id, thread_name) + + +class Transaction(Span): + """The Transaction is the root element that holds all the spans + for Sentry performance instrumentation. + + :param name: Identifier of the transaction. + Will show up in the Sentry UI. + :param parent_sampled: Whether the parent transaction was sampled. + If True this transaction will be kept, if False it will be discarded. + :param baggage: The W3C baggage header value. + (see https://www.w3.org/TR/baggage/) + :param source: A string describing the source of the transaction name. + This will be used to determine the transaction's type. + See https://develop.sentry.dev/sdk/event-payloads/transaction/#transaction-annotations + for more information. Default "custom". + :param kwargs: Additional arguments to be passed to the Span constructor. + See :py:class:`sentry_sdk.tracing.Span` for available arguments. + """ + + __slots__ = ( + "name", + "source", + "parent_sampled", + # used to create baggage value for head SDKs in dynamic sampling + "sample_rate", + "_measurements", + "_contexts", + "_profile", + "_continuous_profile", + "_baggage", + "_sample_rand", + ) + + def __init__( # type: ignore[misc] + self, + name="", # type: str + parent_sampled=None, # type: Optional[bool] + baggage=None, # type: Optional[Baggage] + source=TransactionSource.CUSTOM, # type: str + **kwargs, # type: Unpack[SpanKwargs] + ): + # type: (...) -> None + super().__init__(**kwargs) + + self.name = name + self.source = source + self.sample_rate = None # type: Optional[float] + self.parent_sampled = parent_sampled + self._measurements = {} # type: Dict[str, MeasurementValue] + self._contexts = {} # type: Dict[str, Any] + self._profile = None # type: Optional[Profile] + self._continuous_profile = None # type: Optional[ContinuousProfile] + self._baggage = baggage + + baggage_sample_rand = ( + None if self._baggage is None else self._baggage._sample_rand() + ) + if baggage_sample_rand is not None: + self._sample_rand = baggage_sample_rand + else: + self._sample_rand = _generate_sample_rand(self.trace_id) + + def __repr__(self): + # type: () -> str + return ( + "<%s(name=%r, op=%r, trace_id=%r, span_id=%r, parent_span_id=%r, sampled=%r, source=%r, origin=%r)>" + % ( + self.__class__.__name__, + self.name, + self.op, + self.trace_id, + self.span_id, + self.parent_span_id, + self.sampled, + self.source, + self.origin, + ) + ) + + def _possibly_started(self): + # type: () -> bool + """Returns whether the transaction might have been started. + + If this returns False, we know that the transaction was not started + with sentry_sdk.start_transaction, and therefore the transaction will + be discarded. + """ + + # We must explicitly check self.sampled is False since self.sampled can be None + return self._span_recorder is not None or self.sampled is False + + def __enter__(self): + # type: () -> Transaction + if not self._possibly_started(): + logger.debug( + "Transaction was entered without being started with sentry_sdk.start_transaction." + "The transaction will not be sent to Sentry. To fix, start the transaction by" + "passing it to sentry_sdk.start_transaction." + ) + + super().__enter__() + + if self._profile is not None: + self._profile.__enter__() + + return self + + def __exit__(self, ty, value, tb): + # type: (Optional[Any], Optional[Any], Optional[Any]) -> None + if self._profile is not None: + self._profile.__exit__(ty, value, tb) + + if self._continuous_profile is not None: + self._continuous_profile.stop() + + super().__exit__(ty, value, tb) + + @property + def containing_transaction(self): + # type: () -> Transaction + """The root element of the span tree. + In the case of a transaction it is the transaction itself. + """ + + # Transactions (as spans) belong to themselves (as transactions). This + # is a getter rather than a regular attribute to avoid having a circular + # reference. + return self + + def _get_scope_from_finish_args( + self, + scope_arg, # type: Optional[Union[sentry_sdk.Scope, sentry_sdk.Hub]] + hub_arg, # type: Optional[Union[sentry_sdk.Scope, sentry_sdk.Hub]] + ): + # type: (...) -> Optional[sentry_sdk.Scope] + """ + Logic to get the scope from the arguments passed to finish. This + function exists for backwards compatibility with the old finish. + + TODO: Remove this function in the next major version. + """ + scope_or_hub = scope_arg + if hub_arg is not None: + warnings.warn( + "The `hub` parameter is deprecated. Please use the `scope` parameter, instead.", + DeprecationWarning, + stacklevel=3, + ) + + scope_or_hub = hub_arg + + if isinstance(scope_or_hub, sentry_sdk.Hub): + warnings.warn( + "Passing a Hub to finish is deprecated. Please pass a Scope, instead.", + DeprecationWarning, + stacklevel=3, + ) + + return scope_or_hub.scope + + return scope_or_hub + + def _get_log_representation(self): + # type: () -> str + return "{op}transaction <{name}>".format( + op=("<" + self.op + "> " if self.op else ""), name=self.name + ) + + def finish( + self, + scope=None, # type: Optional[sentry_sdk.Scope] + end_timestamp=None, # type: Optional[Union[float, datetime]] + *, + hub=None, # type: Optional[sentry_sdk.Hub] + ): + # type: (...) -> Optional[str] + """Finishes the transaction and sends it to Sentry. + All finished spans in the transaction will also be sent to Sentry. + + :param scope: The Scope to use for this transaction. + If not provided, the current Scope will be used. + :param end_timestamp: Optional timestamp that should + be used as timestamp instead of the current time. + :param hub: The hub to use for this transaction. + This argument is DEPRECATED. Please use the `scope` + parameter, instead. + + :return: The event ID if the transaction was sent to Sentry, + otherwise None. + """ + if self.timestamp is not None: + # This transaction is already finished, ignore. + return None + + # For backwards compatibility, we must handle the case where `scope` + # or `hub` could both either be a `Scope` or a `Hub`. + scope = self._get_scope_from_finish_args(scope, hub) # type: Optional[sentry_sdk.Scope] + + scope = scope or self.scope or sentry_sdk.get_current_scope() + client = sentry_sdk.get_client() + + if not client.is_active(): + # We have no active client and therefore nowhere to send this transaction. + return None + + if self._span_recorder is None: + # Explicit check against False needed because self.sampled might be None + if self.sampled is False: + logger.debug("Discarding transaction because sampled = False") + else: + logger.debug( + "Discarding transaction because it was not started with sentry_sdk.start_transaction" + ) + + # This is not entirely accurate because discards here are not + # exclusively based on sample rate but also traces sampler, but + # we handle this the same here. + if client.transport and has_tracing_enabled(client.options): + if client.monitor and client.monitor.downsample_factor > 0: + reason = "backpressure" + else: + reason = "sample_rate" + + client.transport.record_lost_event(reason, data_category="transaction") + + # Only one span (the transaction itself) is discarded, since we did not record any spans here. + client.transport.record_lost_event(reason, data_category="span") + return None + + if not self.name: + logger.warning( + "Transaction has no name, falling back to ``." + ) + self.name = "" + + super().finish(scope, end_timestamp) + + status_code = self._data.get(SPANDATA.HTTP_STATUS_CODE) + if ( + status_code is not None + and status_code in client.options["trace_ignore_status_codes"] + ): + logger.debug( + "[Tracing] Discarding {transaction_description} because the HTTP status code {status_code} is matched by trace_ignore_status_codes: {trace_ignore_status_codes}".format( + transaction_description=self._get_log_representation(), + status_code=self._data[SPANDATA.HTTP_STATUS_CODE], + trace_ignore_status_codes=client.options[ + "trace_ignore_status_codes" + ], + ) + ) + if client.transport: + client.transport.record_lost_event( + "event_processor", data_category="transaction" + ) + + num_spans = len(self._span_recorder.spans) + 1 + client.transport.record_lost_event( + "event_processor", data_category="span", quantity=num_spans + ) + + self.sampled = False + + if not self.sampled: + # At this point a `sampled = None` should have already been resolved + # to a concrete decision. + if self.sampled is None: + logger.warning("Discarding transaction without sampling decision.") + + return None + + finished_spans = [ + span.to_json() + for span in self._span_recorder.spans + if span.timestamp is not None + ] + + len_diff = len(self._span_recorder.spans) - len(finished_spans) + dropped_spans = len_diff + self._span_recorder.dropped_spans + + # we do this to break the circular reference of transaction -> span + # recorder -> span -> containing transaction (which is where we started) + # before either the spans or the transaction goes out of scope and has + # to be garbage collected + self._span_recorder = None + + contexts = {} + contexts.update(self._contexts) + contexts.update({"trace": self.get_trace_context()}) + profile_context = self.get_profile_context() + if profile_context is not None: + contexts.update({"profile": profile_context}) + + event = { + "type": "transaction", + "transaction": self.name, + "transaction_info": {"source": self.source}, + "contexts": contexts, + "tags": self._tags, + "timestamp": self.timestamp, + "start_timestamp": self.start_timestamp, + "spans": finished_spans, + } # type: Event + + if dropped_spans > 0: + event["_dropped_spans"] = dropped_spans + + if self._profile is not None and self._profile.valid(): + event["profile"] = self._profile + self._profile = None + + event["measurements"] = self._measurements + + return scope.capture_event(event) + + def set_measurement(self, name, value, unit=""): + # type: (str, float, MeasurementUnit) -> None + """ + .. deprecated:: 2.28.0 + This function is deprecated and will be removed in the next major release. + """ + + warnings.warn( + "`set_measurement()` is deprecated and will be removed in the next major version. Please use `set_data()` instead.", + DeprecationWarning, + stacklevel=2, + ) + self._measurements[name] = {"value": value, "unit": unit} + + def set_context(self, key, value): + # type: (str, dict[str, Any]) -> None + """Sets a context. Transactions can have multiple contexts + and they should follow the format described in the "Contexts Interface" + documentation. + + :param key: The name of the context. + :param value: The information about the context. + """ + self._contexts[key] = value + + def set_http_status(self, http_status): + # type: (int) -> None + """Sets the status of the Transaction according to the given HTTP status. + + :param http_status: The HTTP status code.""" + super().set_http_status(http_status) + self.set_context("response", {"status_code": http_status}) + + def to_json(self): + # type: () -> Dict[str, Any] + """Returns a JSON-compatible representation of the transaction.""" + rv = super().to_json() + + rv["name"] = self.name + rv["source"] = self.source + rv["sampled"] = self.sampled + + return rv + + def get_trace_context(self): + # type: () -> Any + trace_context = super().get_trace_context() + + if self._data: + trace_context["data"] = self._data + + return trace_context + + def get_baggage(self): + # type: () -> Baggage + """Returns the :py:class:`~sentry_sdk.tracing_utils.Baggage` + associated with the Transaction. + + The first time a new baggage with Sentry items is made, + it will be frozen.""" + if not self._baggage or self._baggage.mutable: + self._baggage = Baggage.populate_from_transaction(self) + + return self._baggage + + def _set_initial_sampling_decision(self, sampling_context): + # type: (SamplingContext) -> None + """ + Sets the transaction's sampling decision, according to the following + precedence rules: + + 1. If a sampling decision is passed to `start_transaction` + (`start_transaction(name: "my transaction", sampled: True)`), that + decision will be used, regardless of anything else + + 2. If `traces_sampler` is defined, its decision will be used. It can + choose to keep or ignore any parent sampling decision, or use the + sampling context data to make its own decision or to choose a sample + rate for the transaction. + + 3. If `traces_sampler` is not defined, but there's a parent sampling + decision, the parent sampling decision will be used. + + 4. If `traces_sampler` is not defined and there's no parent sampling + decision, `traces_sample_rate` will be used. + """ + client = sentry_sdk.get_client() + + transaction_description = self._get_log_representation() + + # nothing to do if tracing is disabled + if not has_tracing_enabled(client.options): + self.sampled = False + return + + # if the user has forced a sampling decision by passing a `sampled` + # value when starting the transaction, go with that + if self.sampled is not None: + self.sample_rate = float(self.sampled) + return + + # we would have bailed already if neither `traces_sampler` nor + # `traces_sample_rate` were defined, so one of these should work; prefer + # the hook if so + sample_rate = ( + client.options["traces_sampler"](sampling_context) + if callable(client.options.get("traces_sampler")) + # default inheritance behavior + else ( + sampling_context["parent_sampled"] + if sampling_context["parent_sampled"] is not None + else client.options["traces_sample_rate"] + ) + ) + + # Since this is coming from the user (or from a function provided by the + # user), who knows what we might get. (The only valid values are + # booleans or numbers between 0 and 1.) + if not is_valid_sample_rate(sample_rate, source="Tracing"): + logger.warning( + "[Tracing] Discarding {transaction_description} because of invalid sample rate.".format( + transaction_description=transaction_description, + ) + ) + self.sampled = False + return + + self.sample_rate = float(sample_rate) + + if client.monitor: + self.sample_rate /= 2**client.monitor.downsample_factor + + # if the function returned 0 (or false), or if `traces_sample_rate` is + # 0, it's a sign the transaction should be dropped + if not self.sample_rate: + logger.debug( + "[Tracing] Discarding {transaction_description} because {reason}".format( + transaction_description=transaction_description, + reason=( + "traces_sampler returned 0 or False" + if callable(client.options.get("traces_sampler")) + else "traces_sample_rate is set to 0" + ), + ) + ) + self.sampled = False + return + + # Now we roll the dice. + self.sampled = self._sample_rand < self.sample_rate + + if self.sampled: + logger.debug( + "[Tracing] Starting {transaction_description}".format( + transaction_description=transaction_description, + ) + ) + else: + logger.debug( + "[Tracing] Discarding {transaction_description} because it's not included in the random sample (sampling rate = {sample_rate})".format( + transaction_description=transaction_description, + sample_rate=self.sample_rate, + ) + ) + + +class NoOpSpan(Span): + def __repr__(self): + # type: () -> str + return "<%s>" % self.__class__.__name__ + + @property + def containing_transaction(self): + # type: () -> Optional[Transaction] + return None + + def start_child(self, instrumenter=INSTRUMENTER.SENTRY, **kwargs): + # type: (str, **Any) -> NoOpSpan + return NoOpSpan() + + def to_traceparent(self): + # type: () -> str + return "" + + def to_baggage(self): + # type: () -> Optional[Baggage] + return None + + def get_baggage(self): + # type: () -> Optional[Baggage] + return None + + def iter_headers(self): + # type: () -> Iterator[Tuple[str, str]] + return iter(()) + + def set_tag(self, key, value): + # type: (str, Any) -> None + pass + + def set_data(self, key, value): + # type: (str, Any) -> None + pass + + def update_data(self, data): + # type: (Dict[str, Any]) -> None + pass + + def set_status(self, value): + # type: (str) -> None + pass + + def set_http_status(self, http_status): + # type: (int) -> None + pass + + def is_success(self): + # type: () -> bool + return True + + def to_json(self): + # type: () -> Dict[str, Any] + return {} + + def get_trace_context(self): + # type: () -> Any + return {} + + def get_profile_context(self): + # type: () -> Any + return {} + + def finish( + self, + scope=None, # type: Optional[sentry_sdk.Scope] + end_timestamp=None, # type: Optional[Union[float, datetime]] + *, + hub=None, # type: Optional[sentry_sdk.Hub] + ): + # type: (...) -> Optional[str] + """ + The `hub` parameter is deprecated. Please use the `scope` parameter, instead. + """ + pass + + def set_measurement(self, name, value, unit=""): + # type: (str, float, MeasurementUnit) -> None + pass + + def set_context(self, key, value): + # type: (str, dict[str, Any]) -> None + pass + + def init_span_recorder(self, maxlen): + # type: (int) -> None + pass + + def _set_initial_sampling_decision(self, sampling_context): + # type: (SamplingContext) -> None + pass + + +if TYPE_CHECKING: + + @overload + def trace( + func=None, *, op=None, name=None, attributes=None, template=SPANTEMPLATE.DEFAULT + ): + # type: (None, Optional[str], Optional[str], Optional[dict[str, Any]], SPANTEMPLATE) -> Callable[[Callable[P, R]], Callable[P, R]] + # Handles: @trace() and @trace(op="custom") + pass + + @overload + def trace(func): + # type: (Callable[P, R]) -> Callable[P, R] + # Handles: @trace + pass + + +def trace( + func=None, *, op=None, name=None, attributes=None, template=SPANTEMPLATE.DEFAULT +): + # type: (Optional[Callable[P, R]], Optional[str], Optional[str], Optional[dict[str, Any]], SPANTEMPLATE) -> Union[Callable[P, R], Callable[[Callable[P, R]], Callable[P, R]]] + """ + Decorator to start a child span around a function call. + + This decorator automatically creates a new span when the decorated function + is called, and finishes the span when the function returns or raises an exception. + + :param func: The function to trace. When used as a decorator without parentheses, + this is the function being decorated. When used with parameters (e.g., + ``@trace(op="custom")``, this should be None. + :type func: Callable or None + + :param op: The operation name for the span. This is a high-level description + of what the span represents (e.g., "http.client", "db.query"). + You can use predefined constants from :py:class:`sentry_sdk.consts.OP` + or provide your own string. If not provided, a default operation will + be assigned based on the template. + :type op: str or None + + :param name: The human-readable name/description for the span. If not provided, + defaults to the function name. This provides more specific details about + what the span represents (e.g., "GET /api/users", "process_user_data"). + :type name: str or None + + :param attributes: A dictionary of key-value pairs to add as attributes to the span. + Attribute values must be strings, integers, floats, or booleans. These + attributes provide additional context about the span's execution. + :type attributes: dict[str, Any] or None + + :param template: The type of span to create. This determines what kind of + span instrumentation and data collection will be applied. Use predefined + constants from :py:class:`sentry_sdk.consts.SPANTEMPLATE`. + The default is `SPANTEMPLATE.DEFAULT` which is the right choice for most + use cases. + :type template: :py:class:`sentry_sdk.consts.SPANTEMPLATE` + + :returns: When used as ``@trace``, returns the decorated function. When used as + ``@trace(...)`` with parameters, returns a decorator function. + :rtype: Callable or decorator function + + Example:: + + import sentry_sdk + from sentry_sdk.consts import OP, SPANTEMPLATE + + # Simple usage with default values + @sentry_sdk.trace + def process_data(): + # Function implementation + pass + + # With custom parameters + @sentry_sdk.trace( + op=OP.DB_QUERY, + name="Get user data", + attributes={"postgres": True} + ) + def make_db_query(sql): + # Function implementation + pass + + # With a custom template + @sentry_sdk.trace(template=SPANTEMPLATE.AI_TOOL) + def calculate_interest_rate(amount, rate, years): + # Function implementation + pass + """ + from sentry_sdk.tracing_utils import create_span_decorator + + decorator = create_span_decorator( + op=op, + name=name, + attributes=attributes, + template=template, + ) + + if func: + return decorator(func) + else: + return decorator + + +# Circular imports + +from sentry_sdk.tracing_utils import ( + Baggage, + EnvironHeaders, + extract_sentrytrace_data, + _generate_sample_rand, + has_tracing_enabled, + maybe_create_breadcrumbs_from_span, +) diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/tracing_utils.py b/.venv/lib/python3.12/site-packages/sentry_sdk/tracing_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..587133ad6762755689415411544cc23eabcdb155 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/tracing_utils.py @@ -0,0 +1,1236 @@ +import contextlib +import functools +import inspect +import os +import re +import sys +from collections.abc import Mapping +from datetime import timedelta +from random import Random +from urllib.parse import quote, unquote +import uuid + +import sentry_sdk +from sentry_sdk.consts import OP, SPANDATA, SPANSTATUS, SPANTEMPLATE +from sentry_sdk.utils import ( + capture_internal_exceptions, + filename_for_module, + Dsn, + logger, + match_regex_list, + qualname_from_function, + safe_repr, + to_string, + try_convert, + is_sentry_url, + _is_external_source, + _is_in_project_root, + _module_in_list, +) + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing import Any + from typing import Dict + from typing import Generator + from typing import Optional + from typing import Union + + from types import FrameType + + +SENTRY_TRACE_REGEX = re.compile( + "^[ \t]*" # whitespace + "([0-9a-f]{32})?" # trace_id + "-?([0-9a-f]{16})?" # span_id + "-?([01])?" # sampled + "[ \t]*$" # whitespace +) + + +# This is a normal base64 regex, modified to reflect that fact that we strip the +# trailing = or == off +base64_stripped = ( + # any of the characters in the base64 "alphabet", in multiples of 4 + "([a-zA-Z0-9+/]{4})*" + # either nothing or 2 or 3 base64-alphabet characters (see + # https://en.wikipedia.org/wiki/Base64#Decoding_Base64_without_padding for + # why there's never only 1 extra character) + "([a-zA-Z0-9+/]{2,3})?" +) + + +class EnvironHeaders(Mapping): # type: ignore + def __init__( + self, + environ, # type: Mapping[str, str] + prefix="HTTP_", # type: str + ): + # type: (...) -> None + self.environ = environ + self.prefix = prefix + + def __getitem__(self, key): + # type: (str) -> Optional[Any] + return self.environ[self.prefix + key.replace("-", "_").upper()] + + def __len__(self): + # type: () -> int + return sum(1 for _ in iter(self)) + + def __iter__(self): + # type: () -> Generator[str, None, None] + for k in self.environ: + if not isinstance(k, str): + continue + + k = k.replace("-", "_").upper() + if not k.startswith(self.prefix): + continue + + yield k[len(self.prefix) :] + + +def has_tracing_enabled(options): + # type: (Optional[Dict[str, Any]]) -> bool + """ + Returns True if either traces_sample_rate or traces_sampler is + defined and enable_tracing is set and not false. + """ + if options is None: + return False + + return bool( + options.get("enable_tracing") is not False + and ( + options.get("traces_sample_rate") is not None + or options.get("traces_sampler") is not None + ) + ) + + +@contextlib.contextmanager +def record_sql_queries( + cursor, # type: Any + query, # type: Any + params_list, # type: Any + paramstyle, # type: Optional[str] + executemany, # type: bool + record_cursor_repr=False, # type: bool + span_origin="manual", # type: str +): + # type: (...) -> Generator[sentry_sdk.tracing.Span, None, None] + + # TODO: Bring back capturing of params by default + if sentry_sdk.get_client().options["_experiments"].get("record_sql_params", False): + if not params_list or params_list == [None]: + params_list = None + + if paramstyle == "pyformat": + paramstyle = "format" + else: + params_list = None + paramstyle = None + + query = _format_sql(cursor, query) + + data = {} + if params_list is not None: + data["db.params"] = params_list + if paramstyle is not None: + data["db.paramstyle"] = paramstyle + if executemany: + data["db.executemany"] = True + if record_cursor_repr and cursor is not None: + data["db.cursor"] = cursor + + with capture_internal_exceptions(): + sentry_sdk.add_breadcrumb(message=query, category="query", data=data) + + with sentry_sdk.start_span( + op=OP.DB, + name=query, + origin=span_origin, + ) as span: + for k, v in data.items(): + span.set_data(k, v) + yield span + + +def maybe_create_breadcrumbs_from_span(scope, span): + # type: (sentry_sdk.Scope, sentry_sdk.tracing.Span) -> None + if span.op == OP.DB_REDIS: + scope.add_breadcrumb( + message=span.description, type="redis", category="redis", data=span._tags + ) + + elif span.op == OP.HTTP_CLIENT: + level = None + status_code = span._data.get(SPANDATA.HTTP_STATUS_CODE) + if status_code: + if 500 <= status_code <= 599: + level = "error" + elif 400 <= status_code <= 499: + level = "warning" + + if level: + scope.add_breadcrumb( + type="http", category="httplib", data=span._data, level=level + ) + else: + scope.add_breadcrumb(type="http", category="httplib", data=span._data) + + elif span.op == "subprocess": + scope.add_breadcrumb( + type="subprocess", + category="subprocess", + message=span.description, + data=span._data, + ) + + +def _get_frame_module_abs_path(frame): + # type: (FrameType) -> Optional[str] + try: + return frame.f_code.co_filename + except Exception: + return None + + +def _should_be_included( + is_sentry_sdk_frame, # type: bool + namespace, # type: Optional[str] + in_app_include, # type: Optional[list[str]] + in_app_exclude, # type: Optional[list[str]] + abs_path, # type: Optional[str] + project_root, # type: Optional[str] +): + # type: (...) -> bool + # in_app_include takes precedence over in_app_exclude + should_be_included = _module_in_list(namespace, in_app_include) + should_be_excluded = _is_external_source(abs_path) or _module_in_list( + namespace, in_app_exclude + ) + return not is_sentry_sdk_frame and ( + should_be_included + or (_is_in_project_root(abs_path, project_root) and not should_be_excluded) + ) + + +def add_source(span, project_root, in_app_include, in_app_exclude): + # type: (sentry_sdk.tracing.Span, Optional[str], Optional[list[str]], Optional[list[str]]) -> None + """ + Adds OTel compatible source code information to the span + """ + # Find the correct frame + frame = sys._getframe() # type: Union[FrameType, None] + while frame is not None: + abs_path = _get_frame_module_abs_path(frame) + + try: + namespace = frame.f_globals.get("__name__") # type: Optional[str] + except Exception: + namespace = None + + is_sentry_sdk_frame = namespace is not None and namespace.startswith( + "sentry_sdk." + ) + + should_be_included = _should_be_included( + is_sentry_sdk_frame=is_sentry_sdk_frame, + namespace=namespace, + in_app_include=in_app_include, + in_app_exclude=in_app_exclude, + abs_path=abs_path, + project_root=project_root, + ) + if should_be_included: + break + + frame = frame.f_back + else: + frame = None + + # Set the data + if frame is not None: + try: + lineno = frame.f_lineno + except Exception: + lineno = None + if lineno is not None: + span.set_data(SPANDATA.CODE_LINENO, frame.f_lineno) + + try: + namespace = frame.f_globals.get("__name__") + except Exception: + namespace = None + if namespace is not None: + span.set_data(SPANDATA.CODE_NAMESPACE, namespace) + + filepath = _get_frame_module_abs_path(frame) + if filepath is not None: + if namespace is not None: + in_app_path = filename_for_module(namespace, filepath) + elif project_root is not None and filepath.startswith(project_root): + in_app_path = filepath.replace(project_root, "").lstrip(os.sep) + else: + in_app_path = filepath + span.set_data(SPANDATA.CODE_FILEPATH, in_app_path) + + try: + code_function = frame.f_code.co_name + except Exception: + code_function = None + + if code_function is not None: + span.set_data(SPANDATA.CODE_FUNCTION, frame.f_code.co_name) + + +def add_query_source(span): + # type: (sentry_sdk.tracing.Span) -> None + """ + Adds OTel compatible source code information to a database query span + """ + client = sentry_sdk.get_client() + if not client.is_active(): + return + + if span.timestamp is None or span.start_timestamp is None: + return + + should_add_query_source = client.options.get("enable_db_query_source", True) + if not should_add_query_source: + return + + duration = span.timestamp - span.start_timestamp + threshold = client.options.get("db_query_source_threshold_ms", 0) + slow_query = duration / timedelta(milliseconds=1) > threshold + + if not slow_query: + return + + add_source( + span=span, + project_root=client.options["project_root"], + in_app_include=client.options.get("in_app_include"), + in_app_exclude=client.options.get("in_app_exclude"), + ) + + +def add_http_request_source(span): + # type: (sentry_sdk.tracing.Span) -> None + """ + Adds OTel compatible source code information to a span for an outgoing HTTP request + """ + client = sentry_sdk.get_client() + if not client.is_active(): + return + + if span.timestamp is None or span.start_timestamp is None: + return + + should_add_request_source = client.options.get("enable_http_request_source", False) + if not should_add_request_source: + return + + duration = span.timestamp - span.start_timestamp + threshold = client.options.get("http_request_source_threshold_ms", 0) + slow_query = duration / timedelta(milliseconds=1) > threshold + + if not slow_query: + return + + add_source( + span=span, + project_root=client.options["project_root"], + in_app_include=client.options.get("in_app_include"), + in_app_exclude=client.options.get("in_app_exclude"), + ) + + +def extract_sentrytrace_data(header): + # type: (Optional[str]) -> Optional[Dict[str, Union[str, bool, None]]] + """ + Given a `sentry-trace` header string, return a dictionary of data. + """ + if not header: + return None + + if header.startswith("00-") and header.endswith("-00"): + header = header[3:-3] + + match = SENTRY_TRACE_REGEX.match(header) + if not match: + return None + + trace_id, parent_span_id, sampled_str = match.groups() + parent_sampled = None + + if trace_id: + trace_id = "{:032x}".format(int(trace_id, 16)) + if parent_span_id: + parent_span_id = "{:016x}".format(int(parent_span_id, 16)) + if sampled_str: + parent_sampled = sampled_str != "0" + + return { + "trace_id": trace_id, + "parent_span_id": parent_span_id, + "parent_sampled": parent_sampled, + } + + +def _format_sql(cursor, sql): + # type: (Any, str) -> Optional[str] + + real_sql = None + + # If we're using psycopg2, it could be that we're + # looking at a query that uses Composed objects. Use psycopg2's mogrify + # function to format the query. We lose per-parameter trimming but gain + # accuracy in formatting. + try: + if hasattr(cursor, "mogrify"): + real_sql = cursor.mogrify(sql) + if isinstance(real_sql, bytes): + real_sql = real_sql.decode(cursor.connection.encoding) + except Exception: + real_sql = None + + return real_sql or to_string(sql) + + +class PropagationContext: + """ + The PropagationContext represents the data of a trace in Sentry. + """ + + __slots__ = ( + "_trace_id", + "_span_id", + "parent_span_id", + "parent_sampled", + "dynamic_sampling_context", + ) + + def __init__( + self, + trace_id=None, # type: Optional[str] + span_id=None, # type: Optional[str] + parent_span_id=None, # type: Optional[str] + parent_sampled=None, # type: Optional[bool] + dynamic_sampling_context=None, # type: Optional[Dict[str, str]] + ): + # type: (...) -> None + self._trace_id = trace_id + """The trace id of the Sentry trace.""" + + self._span_id = span_id + """The span id of the currently executing span.""" + + self.parent_span_id = parent_span_id + """The id of the parent span that started this span. + The parent span could also be a span in an upstream service.""" + + self.parent_sampled = parent_sampled + """Boolean indicator if the parent span was sampled. + Important when the parent span originated in an upstream service, + because we want to sample the whole trace, or nothing from the trace.""" + + self.dynamic_sampling_context = dynamic_sampling_context + """Data that is used for dynamic sampling decisions.""" + + @classmethod + def from_incoming_data(cls, incoming_data): + # type: (Dict[str, Any]) -> Optional[PropagationContext] + propagation_context = None + + normalized_data = normalize_incoming_data(incoming_data) + baggage_header = normalized_data.get(BAGGAGE_HEADER_NAME) + if baggage_header: + propagation_context = PropagationContext() + propagation_context.dynamic_sampling_context = Baggage.from_incoming_header( + baggage_header + ).dynamic_sampling_context() + + sentry_trace_header = normalized_data.get(SENTRY_TRACE_HEADER_NAME) + if sentry_trace_header: + sentrytrace_data = extract_sentrytrace_data(sentry_trace_header) + if sentrytrace_data is not None: + if propagation_context is None: + propagation_context = PropagationContext() + propagation_context.update(sentrytrace_data) + + if propagation_context is not None: + propagation_context._fill_sample_rand() + + return propagation_context + + @property + def trace_id(self): + # type: () -> str + """The trace id of the Sentry trace.""" + if not self._trace_id: + # New trace, don't fill in sample_rand + self._trace_id = uuid.uuid4().hex + + return self._trace_id + + @trace_id.setter + def trace_id(self, value): + # type: (str) -> None + self._trace_id = value + + @property + def span_id(self): + # type: () -> str + """The span id of the currently executed span.""" + if not self._span_id: + self._span_id = uuid.uuid4().hex[16:] + + return self._span_id + + @span_id.setter + def span_id(self, value): + # type: (str) -> None + self._span_id = value + + def update(self, other_dict): + # type: (Dict[str, Any]) -> None + """ + Updates the PropagationContext with data from the given dictionary. + """ + for key, value in other_dict.items(): + try: + setattr(self, key, value) + except AttributeError: + pass + + def __repr__(self): + # type: (...) -> str + return "".format( + self._trace_id, + self._span_id, + self.parent_span_id, + self.parent_sampled, + self.dynamic_sampling_context, + ) + + def _fill_sample_rand(self): + # type: () -> None + """ + Ensure that there is a valid sample_rand value in the dynamic_sampling_context. + + If there is a valid sample_rand value in the dynamic_sampling_context, we keep it. + Otherwise, we generate a sample_rand value according to the following: + + - If we have a parent_sampled value and a sample_rate in the DSC, we compute + a sample_rand value randomly in the range: + - [0, sample_rate) if parent_sampled is True, + - or, in the range [sample_rate, 1) if parent_sampled is False. + + - If either parent_sampled or sample_rate is missing, we generate a random + value in the range [0, 1). + + The sample_rand is deterministically generated from the trace_id, if present. + + This function does nothing if there is no dynamic_sampling_context. + """ + if self.dynamic_sampling_context is None: + return + + sample_rand = try_convert( + float, self.dynamic_sampling_context.get("sample_rand") + ) + if sample_rand is not None and 0 <= sample_rand < 1: + # sample_rand is present and valid, so don't overwrite it + return + + # Get the sample rate and compute the transformation that will map the random value + # to the desired range: [0, 1), [0, sample_rate), or [sample_rate, 1). + sample_rate = try_convert( + float, self.dynamic_sampling_context.get("sample_rate") + ) + lower, upper = _sample_rand_range(self.parent_sampled, sample_rate) + + try: + sample_rand = _generate_sample_rand(self.trace_id, interval=(lower, upper)) + except ValueError: + # ValueError is raised if the interval is invalid, i.e. lower >= upper. + # lower >= upper might happen if the incoming trace's sampled flag + # and sample_rate are inconsistent, e.g. sample_rate=0.0 but sampled=True. + # We cannot generate a sensible sample_rand value in this case. + logger.debug( + f"Could not backfill sample_rand, since parent_sampled={self.parent_sampled} " + f"and sample_rate={sample_rate}." + ) + return + + self.dynamic_sampling_context["sample_rand"] = f"{sample_rand:.6f}" # noqa: E231 + + def _sample_rand(self): + # type: () -> Optional[str] + """Convenience method to get the sample_rand value from the dynamic_sampling_context.""" + if self.dynamic_sampling_context is None: + return None + + return self.dynamic_sampling_context.get("sample_rand") + + +class Baggage: + """ + The W3C Baggage header information (see https://www.w3.org/TR/baggage/). + + Before mutating a `Baggage` object, calling code must check that `mutable` is `True`. + Mutating a `Baggage` object that has `mutable` set to `False` is not allowed, but + it is the caller's responsibility to enforce this restriction. + """ + + __slots__ = ("sentry_items", "third_party_items", "mutable") + + SENTRY_PREFIX = "sentry-" + SENTRY_PREFIX_REGEX = re.compile("^sentry-") + + def __init__( + self, + sentry_items, # type: Dict[str, str] + third_party_items="", # type: str + mutable=True, # type: bool + ): + self.sentry_items = sentry_items + self.third_party_items = third_party_items + self.mutable = mutable + + @classmethod + def from_incoming_header( + cls, + header, # type: Optional[str] + *, + _sample_rand=None, # type: Optional[str] + ): + # type: (...) -> Baggage + """ + freeze if incoming header already has sentry baggage + """ + sentry_items = {} + third_party_items = "" + mutable = True + + if header: + for item in header.split(","): + if "=" not in item: + continue + + with capture_internal_exceptions(): + item = item.strip() + key, val = item.split("=") + if Baggage.SENTRY_PREFIX_REGEX.match(key): + baggage_key = unquote(key.split("-")[1]) + sentry_items[baggage_key] = unquote(val) + mutable = False + else: + third_party_items += ("," if third_party_items else "") + item + + if _sample_rand is not None: + sentry_items["sample_rand"] = str(_sample_rand) + mutable = False + + return Baggage(sentry_items, third_party_items, mutable) + + @classmethod + def from_options(cls, scope): + # type: (sentry_sdk.scope.Scope) -> Optional[Baggage] + + sentry_items = {} # type: Dict[str, str] + third_party_items = "" + mutable = False + + client = sentry_sdk.get_client() + + if not client.is_active() or scope._propagation_context is None: + return Baggage(sentry_items) + + options = client.options + propagation_context = scope._propagation_context + + if propagation_context is not None: + sentry_items["trace_id"] = propagation_context.trace_id + + if options.get("environment"): + sentry_items["environment"] = options["environment"] + + if options.get("release"): + sentry_items["release"] = options["release"] + + if options.get("dsn"): + sentry_items["public_key"] = Dsn(options["dsn"]).public_key + + if options.get("traces_sample_rate"): + sentry_items["sample_rate"] = str(options["traces_sample_rate"]) + + return Baggage(sentry_items, third_party_items, mutable) + + @classmethod + def populate_from_transaction(cls, transaction): + # type: (sentry_sdk.tracing.Transaction) -> Baggage + """ + Populate fresh baggage entry with sentry_items and make it immutable + if this is the head SDK which originates traces. + """ + client = sentry_sdk.get_client() + sentry_items = {} # type: Dict[str, str] + + if not client.is_active(): + return Baggage(sentry_items) + + options = client.options or {} + + sentry_items["trace_id"] = transaction.trace_id + sentry_items["sample_rand"] = f"{transaction._sample_rand:.6f}" # noqa: E231 + + if options.get("environment"): + sentry_items["environment"] = options["environment"] + + if options.get("release"): + sentry_items["release"] = options["release"] + + if options.get("dsn"): + sentry_items["public_key"] = Dsn(options["dsn"]).public_key + + if ( + transaction.name + and transaction.source not in LOW_QUALITY_TRANSACTION_SOURCES + ): + sentry_items["transaction"] = transaction.name + + if transaction.sample_rate is not None: + sentry_items["sample_rate"] = str(transaction.sample_rate) + + if transaction.sampled is not None: + sentry_items["sampled"] = "true" if transaction.sampled else "false" + + # there's an existing baggage but it was mutable, + # which is why we are creating this new baggage. + # However, if by chance the user put some sentry items in there, give them precedence. + if transaction._baggage and transaction._baggage.sentry_items: + sentry_items.update(transaction._baggage.sentry_items) + + return Baggage(sentry_items, mutable=False) + + def freeze(self): + # type: () -> None + self.mutable = False + + def dynamic_sampling_context(self): + # type: () -> Dict[str, str] + header = {} + + for key, item in self.sentry_items.items(): + header[key] = item + + return header + + def serialize(self, include_third_party=False): + # type: (bool) -> str + items = [] + + for key, val in self.sentry_items.items(): + with capture_internal_exceptions(): + item = Baggage.SENTRY_PREFIX + quote(key) + "=" + quote(str(val)) + items.append(item) + + if include_third_party: + items.append(self.third_party_items) + + return ",".join(items) + + @staticmethod + def strip_sentry_baggage(header): + # type: (str) -> str + """Remove Sentry baggage from the given header. + + Given a Baggage header, return a new Baggage header with all Sentry baggage items removed. + """ + return ",".join( + ( + item + for item in header.split(",") + if not Baggage.SENTRY_PREFIX_REGEX.match(item.strip()) + ) + ) + + def _sample_rand(self): + # type: () -> Optional[float] + """Convenience method to get the sample_rand value from the sentry_items. + + We validate the value and parse it as a float before returning it. The value is considered + valid if it is a float in the range [0, 1). + """ + sample_rand = try_convert(float, self.sentry_items.get("sample_rand")) + + if sample_rand is not None and 0.0 <= sample_rand < 1.0: + return sample_rand + + return None + + def __repr__(self): + # type: () -> str + return f'' + + +def should_propagate_trace(client, url): + # type: (sentry_sdk.client.BaseClient, str) -> bool + """ + Returns True if url matches trace_propagation_targets configured in the given client. Otherwise, returns False. + """ + trace_propagation_targets = client.options["trace_propagation_targets"] + + if is_sentry_url(client, url): + return False + + return match_regex_list(url, trace_propagation_targets, substring_matching=True) + + +def normalize_incoming_data(incoming_data): + # type: (Dict[str, Any]) -> Dict[str, Any] + """ + Normalizes incoming data so the keys are all lowercase with dashes instead of underscores and stripped from known prefixes. + """ + data = {} + for key, value in incoming_data.items(): + if key.startswith("HTTP_"): + key = key[5:] + + key = key.replace("_", "-").lower() + data[key] = value + + return data + + +def create_span_decorator( + op=None, name=None, attributes=None, template=SPANTEMPLATE.DEFAULT +): + # type: (Optional[Union[str, OP]], Optional[str], Optional[dict[str, Any]], SPANTEMPLATE) -> Any + """ + Create a span decorator that can wrap both sync and async functions. + + :param op: The operation type for the span. + :type op: str or :py:class:`sentry_sdk.consts.OP` or None + :param name: The name of the span. + :type name: str or None + :param attributes: Additional attributes to set on the span. + :type attributes: dict or None + :param template: The type of span to create. This determines what kind of + span instrumentation and data collection will be applied. Use predefined + constants from :py:class:`sentry_sdk.consts.SPANTEMPLATE`. + The default is `SPANTEMPLATE.DEFAULT` which is the right choice for most + use cases. + :type template: :py:class:`sentry_sdk.consts.SPANTEMPLATE` + """ + from sentry_sdk.scope import should_send_default_pii + + def span_decorator(f): + # type: (Any) -> Any + """ + Decorator to create a span for the given function. + """ + + @functools.wraps(f) + async def async_wrapper(*args, **kwargs): + # type: (*Any, **Any) -> Any + current_span = get_current_span() + + if current_span is None: + logger.debug( + "Cannot create a child span for %s. " + "Please start a Sentry transaction before calling this function.", + qualname_from_function(f), + ) + return await f(*args, **kwargs) + + span_op = op or _get_span_op(template) + function_name = name or qualname_from_function(f) or "" + span_name = _get_span_name(template, function_name, kwargs) + send_pii = should_send_default_pii() + + with current_span.start_child( + op=span_op, + name=span_name, + ) as span: + span.update_data(attributes or {}) + _set_input_attributes( + span, template, send_pii, function_name, f, args, kwargs + ) + + result = await f(*args, **kwargs) + + _set_output_attributes(span, template, send_pii, result) + + return result + + try: + async_wrapper.__signature__ = inspect.signature(f) # type: ignore[attr-defined] + except Exception: + pass + + @functools.wraps(f) + def sync_wrapper(*args, **kwargs): + # type: (*Any, **Any) -> Any + current_span = get_current_span() + + if current_span is None: + logger.debug( + "Cannot create a child span for %s. " + "Please start a Sentry transaction before calling this function.", + qualname_from_function(f), + ) + return f(*args, **kwargs) + + span_op = op or _get_span_op(template) + function_name = name or qualname_from_function(f) or "" + span_name = _get_span_name(template, function_name, kwargs) + send_pii = should_send_default_pii() + + with current_span.start_child( + op=span_op, + name=span_name, + ) as span: + span.update_data(attributes or {}) + _set_input_attributes( + span, template, send_pii, function_name, f, args, kwargs + ) + + result = f(*args, **kwargs) + + _set_output_attributes(span, template, send_pii, result) + + return result + + try: + sync_wrapper.__signature__ = inspect.signature(f) # type: ignore[attr-defined] + except Exception: + pass + + if inspect.iscoroutinefunction(f): + return async_wrapper + else: + return sync_wrapper + + return span_decorator + + +def get_current_span(scope=None): + # type: (Optional[sentry_sdk.Scope]) -> Optional[Span] + """ + Returns the currently active span if there is one running, otherwise `None` + """ + scope = scope or sentry_sdk.get_current_scope() + current_span = scope.span + return current_span + + +def set_span_errored(span=None): + # type: (Optional[Span]) -> None + """ + Set the status of the current or given span to ERROR. + Also sets the status of the transaction (root span) to ERROR. + """ + span = span or get_current_span() + if span is not None: + span.set_status(SPANSTATUS.ERROR) + if span.containing_transaction is not None: + span.containing_transaction.set_status(SPANSTATUS.ERROR) + + +def _generate_sample_rand( + trace_id, # type: Optional[str] + *, + interval=(0.0, 1.0), # type: tuple[float, float] +): + # type: (...) -> float + """Generate a sample_rand value from a trace ID. + + The generated value will be pseudorandomly chosen from the provided + interval. Specifically, given (lower, upper) = interval, the generated + value will be in the range [lower, upper). The value has 6-digit precision, + so when printing with .6f, the value will never be rounded up. + + The pseudorandom number generator is seeded with the trace ID. + """ + lower, upper = interval + if not lower < upper: # using `if lower >= upper` would handle NaNs incorrectly + raise ValueError("Invalid interval: lower must be less than upper") + + rng = Random(trace_id) + lower_scaled = int(lower * 1_000_000) + upper_scaled = int(upper * 1_000_000) + try: + sample_rand_scaled = rng.randrange(lower_scaled, upper_scaled) + except ValueError: + # In some corner cases it might happen that the range is too small + # In that case, just take the lower bound + sample_rand_scaled = lower_scaled + + return sample_rand_scaled / 1_000_000 + + +def _sample_rand_range(parent_sampled, sample_rate): + # type: (Optional[bool], Optional[float]) -> tuple[float, float] + """ + Compute the lower (inclusive) and upper (exclusive) bounds of the range of values + that a generated sample_rand value must fall into, given the parent_sampled and + sample_rate values. + """ + if parent_sampled is None or sample_rate is None: + return 0.0, 1.0 + elif parent_sampled is True: + return 0.0, sample_rate + else: # parent_sampled is False + return sample_rate, 1.0 + + +def _get_value(source, key): + # type: (Any, str) -> Optional[Any] + """ + Gets a value from a source object. The source can be a dict or an object. + It is checked for dictionary keys and object attributes. + """ + value = None + if isinstance(source, dict): + value = source.get(key) + else: + if hasattr(source, key): + try: + value = getattr(source, key) + except Exception: + value = None + return value + + +def _get_span_name(template, name, kwargs=None): + # type: (Union[str, SPANTEMPLATE], str, Optional[dict[str, Any]]) -> str + """ + Get the name of the span based on the template and the name. + """ + span_name = name + + if template == SPANTEMPLATE.AI_CHAT: + model = None + if kwargs: + for key in ("model", "model_name"): + if kwargs.get(key) and isinstance(kwargs[key], str): + model = kwargs[key] + break + + span_name = f"chat {model}" if model else "chat" + + elif template == SPANTEMPLATE.AI_AGENT: + span_name = f"invoke_agent {name}" + + elif template == SPANTEMPLATE.AI_TOOL: + span_name = f"execute_tool {name}" + + return span_name + + +def _get_span_op(template): + # type: (Union[str, SPANTEMPLATE]) -> str + """ + Get the operation of the span based on the template. + """ + mapping = { + SPANTEMPLATE.AI_CHAT: OP.GEN_AI_CHAT, + SPANTEMPLATE.AI_AGENT: OP.GEN_AI_INVOKE_AGENT, + SPANTEMPLATE.AI_TOOL: OP.GEN_AI_EXECUTE_TOOL, + } # type: dict[Union[str, SPANTEMPLATE], Union[str, OP]] + op = mapping.get(template, OP.FUNCTION) + + return str(op) + + +def _get_input_attributes(template, send_pii, args, kwargs): + # type: (Union[str, SPANTEMPLATE], bool, tuple[Any, ...], dict[str, Any]) -> dict[str, Any] + """ + Get input attributes for the given span template. + """ + attributes = {} # type: dict[str, Any] + + if template in [SPANTEMPLATE.AI_AGENT, SPANTEMPLATE.AI_TOOL, SPANTEMPLATE.AI_CHAT]: + mapping = { + "model": (SPANDATA.GEN_AI_REQUEST_MODEL, str), + "model_name": (SPANDATA.GEN_AI_REQUEST_MODEL, str), + "agent": (SPANDATA.GEN_AI_AGENT_NAME, str), + "agent_name": (SPANDATA.GEN_AI_AGENT_NAME, str), + "max_tokens": (SPANDATA.GEN_AI_REQUEST_MAX_TOKENS, int), + "frequency_penalty": (SPANDATA.GEN_AI_REQUEST_FREQUENCY_PENALTY, float), + "presence_penalty": (SPANDATA.GEN_AI_REQUEST_PRESENCE_PENALTY, float), + "temperature": (SPANDATA.GEN_AI_REQUEST_TEMPERATURE, float), + "top_p": (SPANDATA.GEN_AI_REQUEST_TOP_P, float), + "top_k": (SPANDATA.GEN_AI_REQUEST_TOP_K, int), + } + + def _set_from_key(key, value): + # type: (str, Any) -> None + if key in mapping: + (attribute, data_type) = mapping[key] + if value is not None and isinstance(value, data_type): + attributes[attribute] = value + + for key, value in list(kwargs.items()): + if key == "prompt" and isinstance(value, str): + attributes.setdefault(SPANDATA.GEN_AI_REQUEST_MESSAGES, []).append( + {"role": "user", "content": value} + ) + continue + + if key == "system_prompt" and isinstance(value, str): + attributes.setdefault(SPANDATA.GEN_AI_REQUEST_MESSAGES, []).append( + {"role": "system", "content": value} + ) + continue + + _set_from_key(key, value) + + if template == SPANTEMPLATE.AI_TOOL and send_pii: + attributes[SPANDATA.GEN_AI_TOOL_INPUT] = safe_repr( + {"args": args, "kwargs": kwargs} + ) + + # Coerce to string + if SPANDATA.GEN_AI_REQUEST_MESSAGES in attributes: + attributes[SPANDATA.GEN_AI_REQUEST_MESSAGES] = safe_repr( + attributes[SPANDATA.GEN_AI_REQUEST_MESSAGES] + ) + + return attributes + + +def _get_usage_attributes(usage): + # type: (Any) -> dict[str, Any] + """ + Get usage attributes. + """ + attributes = {} + + def _set_from_keys(attribute, keys): + # type: (str, tuple[str, ...]) -> None + for key in keys: + value = _get_value(usage, key) + if value is not None and isinstance(value, int): + attributes[attribute] = value + + _set_from_keys( + SPANDATA.GEN_AI_USAGE_INPUT_TOKENS, + ("prompt_tokens", "input_tokens"), + ) + _set_from_keys( + SPANDATA.GEN_AI_USAGE_OUTPUT_TOKENS, + ("completion_tokens", "output_tokens"), + ) + _set_from_keys( + SPANDATA.GEN_AI_USAGE_TOTAL_TOKENS, + ("total_tokens",), + ) + + return attributes + + +def _get_output_attributes(template, send_pii, result): + # type: (Union[str, SPANTEMPLATE], bool, Any) -> dict[str, Any] + """ + Get output attributes for the given span template. + """ + attributes = {} # type: dict[str, Any] + + if template in [SPANTEMPLATE.AI_AGENT, SPANTEMPLATE.AI_TOOL, SPANTEMPLATE.AI_CHAT]: + with capture_internal_exceptions(): + # Usage from result, result.usage, and result.metadata.usage + usage_candidates = [result] + + usage = _get_value(result, "usage") + usage_candidates.append(usage) + + meta = _get_value(result, "metadata") + usage = _get_value(meta, "usage") + usage_candidates.append(usage) + + for usage_candidate in usage_candidates: + if usage_candidate is not None: + attributes.update(_get_usage_attributes(usage_candidate)) + + # Response model + model_name = _get_value(result, "model") + if model_name is not None and isinstance(model_name, str): + attributes[SPANDATA.GEN_AI_RESPONSE_MODEL] = model_name + + model_name = _get_value(result, "model_name") + if model_name is not None and isinstance(model_name, str): + attributes[SPANDATA.GEN_AI_RESPONSE_MODEL] = model_name + + # Tool output + if template == SPANTEMPLATE.AI_TOOL and send_pii: + attributes[SPANDATA.GEN_AI_TOOL_OUTPUT] = safe_repr(result) + + return attributes + + +def _set_input_attributes(span, template, send_pii, name, f, args, kwargs): + # type: (Span, Union[str, SPANTEMPLATE], bool, str, Any, tuple[Any, ...], dict[str, Any]) -> None + """ + Set span input attributes based on the given span template. + + :param span: The span to set attributes on. + :param template: The template to use to set attributes on the span. + :param send_pii: Whether to send PII data. + :param f: The wrapped function. + :param args: The arguments to the wrapped function. + :param kwargs: The keyword arguments to the wrapped function. + """ + attributes = {} # type: dict[str, Any] + + if template == SPANTEMPLATE.AI_AGENT: + attributes = { + SPANDATA.GEN_AI_OPERATION_NAME: "invoke_agent", + SPANDATA.GEN_AI_AGENT_NAME: name, + } + elif template == SPANTEMPLATE.AI_CHAT: + attributes = { + SPANDATA.GEN_AI_OPERATION_NAME: "chat", + } + elif template == SPANTEMPLATE.AI_TOOL: + attributes = { + SPANDATA.GEN_AI_OPERATION_NAME: "execute_tool", + SPANDATA.GEN_AI_TOOL_NAME: name, + } + + docstring = f.__doc__ + if docstring is not None: + attributes[SPANDATA.GEN_AI_TOOL_DESCRIPTION] = docstring + + attributes.update(_get_input_attributes(template, send_pii, args, kwargs)) + span.update_data(attributes or {}) + + +def _set_output_attributes(span, template, send_pii, result): + # type: (Span, Union[str, SPANTEMPLATE], bool, Any) -> None + """ + Set span output attributes based on the given span template. + + :param span: The span to set attributes on. + :param template: The template to use to set attributes on the span. + :param send_pii: Whether to send PII data. + :param result: The result of the wrapped function. + """ + span.update_data(_get_output_attributes(template, send_pii, result) or {}) + + +# Circular imports +from sentry_sdk.tracing import ( + BAGGAGE_HEADER_NAME, + LOW_QUALITY_TRANSACTION_SOURCES, + SENTRY_TRACE_HEADER_NAME, +) + +if TYPE_CHECKING: + from sentry_sdk.tracing import Span diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/transport.py b/.venv/lib/python3.12/site-packages/sentry_sdk/transport.py new file mode 100644 index 0000000000000000000000000000000000000000..645bfead19082b91c70238f37ced7b5e9ecc9d99 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/transport.py @@ -0,0 +1,899 @@ +from abc import ABC, abstractmethod +import io +import os +import gzip +import socket +import ssl +import time +import warnings +from datetime import datetime, timedelta, timezone +from collections import defaultdict +from urllib.request import getproxies + +try: + import brotli # type: ignore +except ImportError: + brotli = None + +import urllib3 +import certifi + +import sentry_sdk +from sentry_sdk.consts import EndpointType +from sentry_sdk.utils import Dsn, logger, capture_internal_exceptions +from sentry_sdk.worker import BackgroundWorker +from sentry_sdk.envelope import Envelope, Item, PayloadRef + +from typing import TYPE_CHECKING, cast, List, Dict + +if TYPE_CHECKING: + from typing import Any + from typing import Callable + from typing import DefaultDict + from typing import Iterable + from typing import Mapping + from typing import Optional + from typing import Self + from typing import Tuple + from typing import Type + from typing import Union + + from urllib3.poolmanager import PoolManager + from urllib3.poolmanager import ProxyManager + + from sentry_sdk._types import Event, EventDataCategory + +KEEP_ALIVE_SOCKET_OPTIONS = [] +for option in [ + (socket.SOL_SOCKET, lambda: getattr(socket, "SO_KEEPALIVE"), 1), # noqa: B009 + (socket.SOL_TCP, lambda: getattr(socket, "TCP_KEEPIDLE"), 45), # noqa: B009 + (socket.SOL_TCP, lambda: getattr(socket, "TCP_KEEPINTVL"), 10), # noqa: B009 + (socket.SOL_TCP, lambda: getattr(socket, "TCP_KEEPCNT"), 6), # noqa: B009 +]: + try: + KEEP_ALIVE_SOCKET_OPTIONS.append((option[0], option[1](), option[2])) + except AttributeError: + # a specific option might not be available on specific systems, + # e.g. TCP_KEEPIDLE doesn't exist on macOS + pass + + +class Transport(ABC): + """Baseclass for all transports. + + A transport is used to send an event to sentry. + """ + + parsed_dsn = None # type: Optional[Dsn] + + def __init__(self, options=None): + # type: (Self, Optional[Dict[str, Any]]) -> None + self.options = options + if options and options["dsn"] is not None and options["dsn"]: + self.parsed_dsn = Dsn(options["dsn"]) + else: + self.parsed_dsn = None + + def capture_event(self, event): + # type: (Self, Event) -> None + """ + DEPRECATED: Please use capture_envelope instead. + + This gets invoked with the event dictionary when an event should + be sent to sentry. + """ + + warnings.warn( + "capture_event is deprecated, please use capture_envelope instead!", + DeprecationWarning, + stacklevel=2, + ) + + envelope = Envelope() + envelope.add_event(event) + self.capture_envelope(envelope) + + @abstractmethod + def capture_envelope(self, envelope): + # type: (Self, Envelope) -> None + """ + Send an envelope to Sentry. + + Envelopes are a data container format that can hold any type of data + submitted to Sentry. We use it to send all event data (including errors, + transactions, crons check-ins, etc.) to Sentry. + """ + pass + + def flush( + self, + timeout, + callback=None, + ): + # type: (Self, float, Optional[Any]) -> None + """ + Wait `timeout` seconds for the current events to be sent out. + + The default implementation is a no-op, since this method may only be relevant to some transports. + Subclasses should override this method if necessary. + """ + return None + + def kill(self): + # type: (Self) -> None + """ + Forcefully kills the transport. + + The default implementation is a no-op, since this method may only be relevant to some transports. + Subclasses should override this method if necessary. + """ + return None + + def record_lost_event( + self, + reason, # type: str + data_category=None, # type: Optional[EventDataCategory] + item=None, # type: Optional[Item] + *, + quantity=1, # type: int + ): + # type: (...) -> None + """This increments a counter for event loss by reason and + data category by the given positive-int quantity (default 1). + + If an item is provided, the data category and quantity are + extracted from the item, and the values passed for + data_category and quantity are ignored. + + When recording a lost transaction via data_category="transaction", + the calling code should also record the lost spans via this method. + When recording lost spans, `quantity` should be set to the number + of contained spans, plus one for the transaction itself. When + passing an Item containing a transaction via the `item` parameter, + this method automatically records the lost spans. + """ + return None + + def is_healthy(self): + # type: (Self) -> bool + return True + + +def _parse_rate_limits(header, now=None): + # type: (str, Optional[datetime]) -> Iterable[Tuple[Optional[EventDataCategory], datetime]] + if now is None: + now = datetime.now(timezone.utc) + + for limit in header.split(","): + try: + parameters = limit.strip().split(":") + retry_after_val, categories = parameters[:2] + + retry_after = now + timedelta(seconds=int(retry_after_val)) + for category in categories and categories.split(";") or (None,): + yield category, retry_after # type: ignore + except (LookupError, ValueError): + continue + + +class BaseHttpTransport(Transport): + """The base HTTP transport.""" + + TIMEOUT = 30 # seconds + + def __init__(self, options): + # type: (Self, Dict[str, Any]) -> None + from sentry_sdk.consts import VERSION + + Transport.__init__(self, options) + assert self.parsed_dsn is not None + self.options = options # type: Dict[str, Any] + self._worker = BackgroundWorker(queue_size=options["transport_queue_size"]) + self._auth = self.parsed_dsn.to_auth("sentry.python/%s" % VERSION) + self._disabled_until = {} # type: Dict[Optional[EventDataCategory], datetime] + # We only use this Retry() class for the `get_retry_after` method it exposes + self._retry = urllib3.util.Retry() + self._discarded_events = defaultdict(int) # type: DefaultDict[Tuple[EventDataCategory, str], int] + self._last_client_report_sent = time.time() + + self._pool = self._make_pool() + + # Backwards compatibility for deprecated `self.hub_class` attribute + self._hub_cls = sentry_sdk.Hub + + experiments = options.get("_experiments", {}) + compression_level = experiments.get( + "transport_compression_level", + experiments.get("transport_zlib_compression_level"), + ) + compression_algo = experiments.get( + "transport_compression_algo", + ( + "gzip" + # if only compression level is set, assume gzip for backwards compatibility + # if we don't have brotli available, fallback to gzip + if compression_level is not None or brotli is None + else "br" + ), + ) + + if compression_algo == "br" and brotli is None: + logger.warning( + "You asked for brotli compression without the Brotli module, falling back to gzip -9" + ) + compression_algo = "gzip" + compression_level = None + + if compression_algo not in ("br", "gzip"): + logger.warning( + "Unknown compression algo %s, disabling compression", compression_algo + ) + self._compression_level = 0 + self._compression_algo = None + else: + self._compression_algo = compression_algo + + if compression_level is not None: + self._compression_level = compression_level + elif self._compression_algo == "gzip": + self._compression_level = 9 + elif self._compression_algo == "br": + self._compression_level = 4 + + def record_lost_event( + self, + reason, # type: str + data_category=None, # type: Optional[EventDataCategory] + item=None, # type: Optional[Item] + *, + quantity=1, # type: int + ): + # type: (...) -> None + if not self.options["send_client_reports"]: + return + + if item is not None: + data_category = item.data_category + quantity = 1 # If an item is provided, we always count it as 1 (except for attachments, handled below). + + if data_category == "transaction": + # Also record the lost spans + event = item.get_transaction_event() or {} + + # +1 for the transaction itself + span_count = ( + len(cast(List[Dict[str, object]], event.get("spans") or [])) + 1 + ) + self.record_lost_event(reason, "span", quantity=span_count) + + elif data_category == "attachment": + # quantity of 0 is actually 1 as we do not want to count + # empty attachments as actually empty. + quantity = len(item.get_bytes()) or 1 + + elif data_category is None: + raise TypeError("data category not provided") + + self._discarded_events[data_category, reason] += quantity + + def _get_header_value(self, response, header): + # type: (Self, Any, str) -> Optional[str] + return response.headers.get(header) + + def _update_rate_limits(self, response): + # type: (Self, Union[urllib3.BaseHTTPResponse, httpcore.Response]) -> None + + # new sentries with more rate limit insights. We honor this header + # no matter of the status code to update our internal rate limits. + header = self._get_header_value(response, "x-sentry-rate-limits") + if header: + logger.warning("Rate-limited via x-sentry-rate-limits") + self._disabled_until.update(_parse_rate_limits(header)) + + # old sentries only communicate global rate limit hits via the + # retry-after header on 429. This header can also be emitted on new + # sentries if a proxy in front wants to globally slow things down. + elif response.status == 429: + logger.warning("Rate-limited via 429") + retry_after_value = self._get_header_value(response, "Retry-After") + retry_after = ( + self._retry.parse_retry_after(retry_after_value) + if retry_after_value is not None + else None + ) or 60 + self._disabled_until[None] = datetime.now(timezone.utc) + timedelta( + seconds=retry_after + ) + + def _send_request( + self, + body, + headers, + endpoint_type=EndpointType.ENVELOPE, + envelope=None, + ): + # type: (Self, bytes, Dict[str, str], EndpointType, Optional[Envelope]) -> None + + def record_loss(reason): + # type: (str) -> None + if envelope is None: + self.record_lost_event(reason, data_category="error") + else: + for item in envelope.items: + self.record_lost_event(reason, item=item) + + headers.update( + { + "User-Agent": str(self._auth.client), + "X-Sentry-Auth": str(self._auth.to_header()), + } + ) + try: + response = self._request( + "POST", + endpoint_type, + body, + headers, + ) + except Exception: + self.on_dropped_event("network") + record_loss("network_error") + raise + + try: + self._update_rate_limits(response) + + if response.status == 429: + # if we hit a 429. Something was rate limited but we already + # acted on this in `self._update_rate_limits`. Note that we + # do not want to record event loss here as we will have recorded + # an outcome in relay already. + self.on_dropped_event("status_429") + pass + + elif response.status >= 300 or response.status < 200: + logger.error( + "Unexpected status code: %s (body: %s)", + response.status, + getattr(response, "data", getattr(response, "content", None)), + ) + self.on_dropped_event("status_{}".format(response.status)) + record_loss("network_error") + finally: + response.close() + + def on_dropped_event(self, _reason): + # type: (Self, str) -> None + return None + + def _fetch_pending_client_report(self, force=False, interval=60): + # type: (Self, bool, int) -> Optional[Item] + if not self.options["send_client_reports"]: + return None + + if not (force or self._last_client_report_sent < time.time() - interval): + return None + + discarded_events = self._discarded_events + self._discarded_events = defaultdict(int) + self._last_client_report_sent = time.time() + + if not discarded_events: + return None + + return Item( + PayloadRef( + json={ + "timestamp": time.time(), + "discarded_events": [ + {"reason": reason, "category": category, "quantity": quantity} + for ( + (category, reason), + quantity, + ) in discarded_events.items() + ], + } + ), + type="client_report", + ) + + def _flush_client_reports(self, force=False): + # type: (Self, bool) -> None + client_report = self._fetch_pending_client_report(force=force, interval=60) + if client_report is not None: + self.capture_envelope(Envelope(items=[client_report])) + + def _check_disabled(self, category): + # type: (str) -> bool + def _disabled(bucket): + # type: (Any) -> bool + ts = self._disabled_until.get(bucket) + return ts is not None and ts > datetime.now(timezone.utc) + + return _disabled(category) or _disabled(None) + + def _is_rate_limited(self): + # type: (Self) -> bool + return any( + ts > datetime.now(timezone.utc) for ts in self._disabled_until.values() + ) + + def _is_worker_full(self): + # type: (Self) -> bool + return self._worker.full() + + def is_healthy(self): + # type: (Self) -> bool + return not (self._is_worker_full() or self._is_rate_limited()) + + def _send_envelope(self, envelope): + # type: (Self, Envelope) -> None + + # remove all items from the envelope which are over quota + new_items = [] + for item in envelope.items: + if self._check_disabled(item.data_category): + if item.data_category in ("transaction", "error", "default", "statsd"): + self.on_dropped_event("self_rate_limits") + self.record_lost_event("ratelimit_backoff", item=item) + else: + new_items.append(item) + + # Since we're modifying the envelope here make a copy so that others + # that hold references do not see their envelope modified. + envelope = Envelope(headers=envelope.headers, items=new_items) + + if not envelope.items: + return None + + # since we're already in the business of sending out an envelope here + # check if we have one pending for the stats session envelopes so we + # can attach it to this enveloped scheduled for sending. This will + # currently typically attach the client report to the most recent + # session update. + client_report_item = self._fetch_pending_client_report(interval=30) + if client_report_item is not None: + envelope.items.append(client_report_item) + + content_encoding, body = self._serialize_envelope(envelope) + + assert self.parsed_dsn is not None + logger.debug( + "Sending envelope [%s] project:%s host:%s", + envelope.description, + self.parsed_dsn.project_id, + self.parsed_dsn.host, + ) + + headers = { + "Content-Type": "application/x-sentry-envelope", + } + if content_encoding: + headers["Content-Encoding"] = content_encoding + + self._send_request( + body.getvalue(), + headers=headers, + endpoint_type=EndpointType.ENVELOPE, + envelope=envelope, + ) + return None + + def _serialize_envelope(self, envelope): + # type: (Self, Envelope) -> tuple[Optional[str], io.BytesIO] + content_encoding = None + body = io.BytesIO() + if self._compression_level == 0 or self._compression_algo is None: + envelope.serialize_into(body) + else: + content_encoding = self._compression_algo + if self._compression_algo == "br" and brotli is not None: + body.write( + brotli.compress( + envelope.serialize(), quality=self._compression_level + ) + ) + else: # assume gzip as we sanitize the algo value in init + with gzip.GzipFile( + fileobj=body, mode="w", compresslevel=self._compression_level + ) as f: + envelope.serialize_into(f) + + return content_encoding, body + + def _get_pool_options(self): + # type: (Self) -> Dict[str, Any] + raise NotImplementedError() + + def _in_no_proxy(self, parsed_dsn): + # type: (Self, Dsn) -> bool + no_proxy = getproxies().get("no") + if not no_proxy: + return False + for host in no_proxy.split(","): + host = host.strip() + if parsed_dsn.host.endswith(host) or parsed_dsn.netloc.endswith(host): + return True + return False + + def _make_pool(self): + # type: (Self) -> Union[PoolManager, ProxyManager, httpcore.SOCKSProxy, httpcore.HTTPProxy, httpcore.ConnectionPool] + raise NotImplementedError() + + def _request( + self, + method, + endpoint_type, + body, + headers, + ): + # type: (Self, str, EndpointType, Any, Mapping[str, str]) -> Union[urllib3.BaseHTTPResponse, httpcore.Response] + raise NotImplementedError() + + def capture_envelope( + self, + envelope, # type: Envelope + ): + # type: (...) -> None + def send_envelope_wrapper(): + # type: () -> None + with capture_internal_exceptions(): + self._send_envelope(envelope) + self._flush_client_reports() + + if not self._worker.submit(send_envelope_wrapper): + self.on_dropped_event("full_queue") + for item in envelope.items: + self.record_lost_event("queue_overflow", item=item) + + def flush( + self, + timeout, + callback=None, + ): + # type: (Self, float, Optional[Callable[[int, float], None]]) -> None + logger.debug("Flushing HTTP transport") + + if timeout > 0: + self._worker.submit(lambda: self._flush_client_reports(force=True)) + self._worker.flush(timeout, callback) + + def kill(self): + # type: (Self) -> None + logger.debug("Killing HTTP transport") + self._worker.kill() + + @staticmethod + def _warn_hub_cls(): + # type: () -> None + """Convenience method to warn users about the deprecation of the `hub_cls` attribute.""" + warnings.warn( + "The `hub_cls` attribute is deprecated and will be removed in a future release.", + DeprecationWarning, + stacklevel=3, + ) + + @property + def hub_cls(self): + # type: (Self) -> type[sentry_sdk.Hub] + """DEPRECATED: This attribute is deprecated and will be removed in a future release.""" + HttpTransport._warn_hub_cls() + return self._hub_cls + + @hub_cls.setter + def hub_cls(self, value): + # type: (Self, type[sentry_sdk.Hub]) -> None + """DEPRECATED: This attribute is deprecated and will be removed in a future release.""" + HttpTransport._warn_hub_cls() + self._hub_cls = value + + +class HttpTransport(BaseHttpTransport): + if TYPE_CHECKING: + _pool: Union[PoolManager, ProxyManager] + + def _get_pool_options(self): + # type: (Self) -> Dict[str, Any] + + num_pools = self.options.get("_experiments", {}).get("transport_num_pools") + options = { + "num_pools": 2 if num_pools is None else int(num_pools), + "cert_reqs": "CERT_REQUIRED", + "timeout": urllib3.Timeout(total=self.TIMEOUT), + } + + socket_options = None # type: Optional[List[Tuple[int, int, int | bytes]]] + + if self.options["socket_options"] is not None: + socket_options = self.options["socket_options"] + + if self.options["keep_alive"]: + if socket_options is None: + socket_options = [] + + used_options = {(o[0], o[1]) for o in socket_options} + for default_option in KEEP_ALIVE_SOCKET_OPTIONS: + if (default_option[0], default_option[1]) not in used_options: + socket_options.append(default_option) + + if socket_options is not None: + options["socket_options"] = socket_options + + options["ca_certs"] = ( + self.options["ca_certs"] # User-provided bundle from the SDK init + or os.environ.get("SSL_CERT_FILE") + or os.environ.get("REQUESTS_CA_BUNDLE") + or certifi.where() + ) + + options["cert_file"] = self.options["cert_file"] or os.environ.get( + "CLIENT_CERT_FILE" + ) + options["key_file"] = self.options["key_file"] or os.environ.get( + "CLIENT_KEY_FILE" + ) + + return options + + def _make_pool(self): + # type: (Self) -> Union[PoolManager, ProxyManager] + if self.parsed_dsn is None: + raise ValueError("Cannot create HTTP-based transport without valid DSN") + + proxy = None + no_proxy = self._in_no_proxy(self.parsed_dsn) + + # try HTTPS first + https_proxy = self.options["https_proxy"] + if self.parsed_dsn.scheme == "https" and (https_proxy != ""): + proxy = https_proxy or (not no_proxy and getproxies().get("https")) + + # maybe fallback to HTTP proxy + http_proxy = self.options["http_proxy"] + if not proxy and (http_proxy != ""): + proxy = http_proxy or (not no_proxy and getproxies().get("http")) + + opts = self._get_pool_options() + + if proxy: + proxy_headers = self.options["proxy_headers"] + if proxy_headers: + opts["proxy_headers"] = proxy_headers + + if proxy.startswith("socks"): + use_socks_proxy = True + try: + # Check if PySocks dependency is available + from urllib3.contrib.socks import SOCKSProxyManager + except ImportError: + use_socks_proxy = False + logger.warning( + "You have configured a SOCKS proxy (%s) but support for SOCKS proxies is not installed. Disabling proxy support. Please add `PySocks` (or `urllib3` with the `[socks]` extra) to your dependencies.", + proxy, + ) + + if use_socks_proxy: + return SOCKSProxyManager(proxy, **opts) + else: + return urllib3.PoolManager(**opts) + else: + return urllib3.ProxyManager(proxy, **opts) + else: + return urllib3.PoolManager(**opts) + + def _request( + self, + method, + endpoint_type, + body, + headers, + ): + # type: (Self, str, EndpointType, Any, Mapping[str, str]) -> urllib3.BaseHTTPResponse + return self._pool.request( + method, + self._auth.get_api_url(endpoint_type), + body=body, + headers=headers, + ) + + +try: + import httpcore + import h2 # noqa: F401 +except ImportError: + # Sorry, no Http2Transport for you + class Http2Transport(HttpTransport): + def __init__(self, options): + # type: (Self, Dict[str, Any]) -> None + super().__init__(options) + logger.warning( + "You tried to use HTTP2Transport but don't have httpcore[http2] installed. Falling back to HTTPTransport." + ) + +else: + + class Http2Transport(BaseHttpTransport): # type: ignore + """The HTTP2 transport based on httpcore.""" + + TIMEOUT = 15 + + if TYPE_CHECKING: + _pool: Union[ + httpcore.SOCKSProxy, httpcore.HTTPProxy, httpcore.ConnectionPool + ] + + def _get_header_value(self, response, header): + # type: (Self, httpcore.Response, str) -> Optional[str] + return next( + ( + val.decode("ascii") + for key, val in response.headers + if key.decode("ascii").lower() == header + ), + None, + ) + + def _request( + self, + method, + endpoint_type, + body, + headers, + ): + # type: (Self, str, EndpointType, Any, Mapping[str, str]) -> httpcore.Response + response = self._pool.request( + method, + self._auth.get_api_url(endpoint_type), + content=body, + headers=headers, # type: ignore + extensions={ + "timeout": { + "pool": self.TIMEOUT, + "connect": self.TIMEOUT, + "write": self.TIMEOUT, + "read": self.TIMEOUT, + } + }, + ) + return response + + def _get_pool_options(self): + # type: (Self) -> Dict[str, Any] + options = { + "http2": self.parsed_dsn is not None + and self.parsed_dsn.scheme == "https", + "retries": 3, + } # type: Dict[str, Any] + + socket_options = ( + self.options["socket_options"] + if self.options["socket_options"] is not None + else [] + ) + + used_options = {(o[0], o[1]) for o in socket_options} + for default_option in KEEP_ALIVE_SOCKET_OPTIONS: + if (default_option[0], default_option[1]) not in used_options: + socket_options.append(default_option) + + options["socket_options"] = socket_options + + ssl_context = ssl.create_default_context() + ssl_context.load_verify_locations( + self.options["ca_certs"] # User-provided bundle from the SDK init + or os.environ.get("SSL_CERT_FILE") + or os.environ.get("REQUESTS_CA_BUNDLE") + or certifi.where() + ) + cert_file = self.options["cert_file"] or os.environ.get("CLIENT_CERT_FILE") + key_file = self.options["key_file"] or os.environ.get("CLIENT_KEY_FILE") + if cert_file is not None: + ssl_context.load_cert_chain(cert_file, key_file) + + options["ssl_context"] = ssl_context + + return options + + def _make_pool(self): + # type: (Self) -> Union[httpcore.SOCKSProxy, httpcore.HTTPProxy, httpcore.ConnectionPool] + if self.parsed_dsn is None: + raise ValueError("Cannot create HTTP-based transport without valid DSN") + proxy = None + no_proxy = self._in_no_proxy(self.parsed_dsn) + + # try HTTPS first + https_proxy = self.options["https_proxy"] + if self.parsed_dsn.scheme == "https" and (https_proxy != ""): + proxy = https_proxy or (not no_proxy and getproxies().get("https")) + + # maybe fallback to HTTP proxy + http_proxy = self.options["http_proxy"] + if not proxy and (http_proxy != ""): + proxy = http_proxy or (not no_proxy and getproxies().get("http")) + + opts = self._get_pool_options() + + if proxy: + proxy_headers = self.options["proxy_headers"] + if proxy_headers: + opts["proxy_headers"] = proxy_headers + + if proxy.startswith("socks"): + try: + if "socket_options" in opts: + socket_options = opts.pop("socket_options") + if socket_options: + logger.warning( + "You have defined socket_options but using a SOCKS proxy which doesn't support these. We'll ignore socket_options." + ) + return httpcore.SOCKSProxy(proxy_url=proxy, **opts) + except RuntimeError: + logger.warning( + "You have configured a SOCKS proxy (%s) but support for SOCKS proxies is not installed. Disabling proxy support.", + proxy, + ) + else: + return httpcore.HTTPProxy(proxy_url=proxy, **opts) + + return httpcore.ConnectionPool(**opts) + + +class _FunctionTransport(Transport): + """ + DEPRECATED: Users wishing to provide a custom transport should subclass + the Transport class, rather than providing a function. + """ + + def __init__( + self, + func, # type: Callable[[Event], None] + ): + # type: (...) -> None + Transport.__init__(self) + self._func = func + + def capture_event( + self, + event, # type: Event + ): + # type: (...) -> None + self._func(event) + return None + + def capture_envelope(self, envelope: Envelope) -> None: + # Since function transports expect to be called with an event, we need + # to iterate over the envelope and call the function for each event, via + # the deprecated capture_event method. + event = envelope.get_event() + if event is not None: + self.capture_event(event) + + +def make_transport(options): + # type: (Dict[str, Any]) -> Optional[Transport] + ref_transport = options["transport"] + + use_http2_transport = options.get("_experiments", {}).get("transport_http2", False) + + # By default, we use the http transport class + transport_cls = Http2Transport if use_http2_transport else HttpTransport # type: Type[Transport] + + if isinstance(ref_transport, Transport): + return ref_transport + elif isinstance(ref_transport, type) and issubclass(ref_transport, Transport): + transport_cls = ref_transport + elif callable(ref_transport): + warnings.warn( + "Function transports are deprecated and will be removed in a future release." + "Please provide a Transport instance or subclass, instead.", + DeprecationWarning, + stacklevel=2, + ) + return _FunctionTransport(ref_transport) + + # if a transport class is given only instantiate it if the dsn is not + # empty or None + if options["dsn"]: + return transport_cls(options) + + return None diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/types.py b/.venv/lib/python3.12/site-packages/sentry_sdk/types.py new file mode 100644 index 0000000000000000000000000000000000000000..8b28166462ba34052438c3ecc2efd977efa8f8c8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/types.py @@ -0,0 +1,52 @@ +""" +This module contains type definitions for the Sentry SDK's public API. +The types are re-exported from the internal module `sentry_sdk._types`. + +Disclaimer: Since types are a form of documentation, type definitions +may change in minor releases. Removing a type would be considered a +breaking change, and so we will only remove type definitions in major +releases. +""" + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + # Re-export types to make them available in the public API + from sentry_sdk._types import ( + Breadcrumb, + BreadcrumbHint, + Event, + EventDataCategory, + Hint, + Log, + MonitorConfig, + SamplingContext, + Metric, + ) +else: + from typing import Any + + # The lines below allow the types to be imported from outside `if TYPE_CHECKING` + # guards. The types in this module are only intended to be used for type hints. + Breadcrumb = Any + BreadcrumbHint = Any + Event = Any + EventDataCategory = Any + Hint = Any + Log = Any + MonitorConfig = Any + SamplingContext = Any + Metric = Any + + +__all__ = ( + "Breadcrumb", + "BreadcrumbHint", + "Event", + "EventDataCategory", + "Hint", + "Log", + "MonitorConfig", + "SamplingContext", + "Metric", +) diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/utils.py b/.venv/lib/python3.12/site-packages/sentry_sdk/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..cd825b29e234359f3efbcf4a7ceb55ec55ba56e5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/sentry_sdk/utils.py @@ -0,0 +1,2031 @@ +import base64 +import json +import linecache +import logging +import math +import os +import random +import re +import subprocess +import sys +import threading +import time +from collections import namedtuple +from datetime import datetime, timezone +from decimal import Decimal +from functools import partial, partialmethod, wraps +from numbers import Real +from urllib.parse import parse_qs, unquote, urlencode, urlsplit, urlunsplit + +try: + # Python 3.11 + from builtins import BaseExceptionGroup +except ImportError: + # Python 3.10 and below + BaseExceptionGroup = None # type: ignore + +import sentry_sdk +from sentry_sdk._compat import PY37 +from sentry_sdk.consts import ( + DEFAULT_ADD_FULL_STACK, + DEFAULT_MAX_STACK_FRAMES, + DEFAULT_MAX_VALUE_LENGTH, + EndpointType, +) +from sentry_sdk._types import Annotated, AnnotatedValue, SENSITIVE_DATA_SUBSTITUTE + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from types import FrameType, TracebackType + from typing import ( + Any, + Callable, + cast, + ContextManager, + Dict, + Iterator, + List, + NoReturn, + Optional, + overload, + ParamSpec, + Set, + Tuple, + Type, + TypeVar, + Union, + ) + + from gevent.hub import Hub + + from sentry_sdk._types import Event, ExcInfo, Log, Hint, Metric + + P = ParamSpec("P") + R = TypeVar("R") + + +epoch = datetime(1970, 1, 1) + +# The logger is created here but initialized in the debug support module +logger = logging.getLogger("sentry_sdk.errors") + +_installed_modules = None + +BASE64_ALPHABET = re.compile(r"^[a-zA-Z0-9/+=]*$") + +FALSY_ENV_VALUES = frozenset(("false", "f", "n", "no", "off", "0")) +TRUTHY_ENV_VALUES = frozenset(("true", "t", "y", "yes", "on", "1")) + +MAX_STACK_FRAMES = 2000 +"""Maximum number of stack frames to send to Sentry. + +If we have more than this number of stack frames, we will stop processing +the stacktrace to avoid getting stuck in a long-lasting loop. This value +exceeds the default sys.getrecursionlimit() of 1000, so users will only +be affected by this limit if they have a custom recursion limit. +""" + + +def env_to_bool(value, *, strict=False): + # type: (Any, Optional[bool]) -> bool | None + """Casts an ENV variable value to boolean using the constants defined above. + In strict mode, it may return None if the value doesn't match any of the predefined values. + """ + normalized = str(value).lower() if value is not None else None + + if normalized in FALSY_ENV_VALUES: + return False + + if normalized in TRUTHY_ENV_VALUES: + return True + + return None if strict else bool(value) + + +def json_dumps(data): + # type: (Any) -> bytes + """Serialize data into a compact JSON representation encoded as UTF-8.""" + return json.dumps(data, allow_nan=False, separators=(",", ":")).encode("utf-8") + + +def get_git_revision(): + # type: () -> Optional[str] + try: + with open(os.path.devnull, "w+") as null: + # prevent command prompt windows from popping up on windows + startupinfo = None + if sys.platform == "win32" or sys.platform == "cygwin": + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + + revision = ( + subprocess.Popen( + ["git", "rev-parse", "HEAD"], + startupinfo=startupinfo, + stdout=subprocess.PIPE, + stderr=null, + stdin=null, + ) + .communicate()[0] + .strip() + .decode("utf-8") + ) + except (OSError, IOError, FileNotFoundError): + return None + + return revision + + +def get_default_release(): + # type: () -> Optional[str] + """Try to guess a default release.""" + release = os.environ.get("SENTRY_RELEASE") + if release: + return release + + release = get_git_revision() + if release: + return release + + for var in ( + "HEROKU_SLUG_COMMIT", + "SOURCE_VERSION", + "CODEBUILD_RESOLVED_SOURCE_VERSION", + "CIRCLE_SHA1", + "GAE_DEPLOYMENT_ID", + ): + release = os.environ.get(var) + if release: + return release + return None + + +def get_sdk_name(installed_integrations): + # type: (List[str]) -> str + """Return the SDK name including the name of the used web framework.""" + + # Note: I can not use for example sentry_sdk.integrations.django.DjangoIntegration.identifier + # here because if django is not installed the integration is not accessible. + framework_integrations = [ + "django", + "flask", + "fastapi", + "bottle", + "falcon", + "quart", + "sanic", + "starlette", + "litestar", + "starlite", + "chalice", + "serverless", + "pyramid", + "tornado", + "aiohttp", + "aws_lambda", + "gcp", + "beam", + "asgi", + "wsgi", + ] + + for integration in framework_integrations: + if integration in installed_integrations: + return "sentry.python.{}".format(integration) + + return "sentry.python" + + +class CaptureInternalException: + __slots__ = () + + def __enter__(self): + # type: () -> ContextManager[Any] + return self + + def __exit__(self, ty, value, tb): + # type: (Optional[Type[BaseException]], Optional[BaseException], Optional[TracebackType]) -> bool + if ty is not None and value is not None: + capture_internal_exception((ty, value, tb)) + + return True + + +_CAPTURE_INTERNAL_EXCEPTION = CaptureInternalException() + + +def capture_internal_exceptions(): + # type: () -> ContextManager[Any] + return _CAPTURE_INTERNAL_EXCEPTION + + +def capture_internal_exception(exc_info): + # type: (ExcInfo) -> None + """ + Capture an exception that is likely caused by a bug in the SDK + itself. + + These exceptions do not end up in Sentry and are just logged instead. + """ + if sentry_sdk.get_client().is_active(): + logger.error("Internal error in sentry_sdk", exc_info=exc_info) + + +def to_timestamp(value): + # type: (datetime) -> float + return (value - epoch).total_seconds() + + +def format_timestamp(value): + # type: (datetime) -> str + """Formats a timestamp in RFC 3339 format. + + Any datetime objects with a non-UTC timezone are converted to UTC, so that all timestamps are formatted in UTC. + """ + utctime = value.astimezone(timezone.utc) + + # We use this custom formatting rather than isoformat for backwards compatibility (we have used this format for + # several years now), and isoformat is slightly different. + return utctime.strftime("%Y-%m-%dT%H:%M:%S.%fZ") + + +ISO_TZ_SEPARATORS = frozenset(("+", "-")) + + +def datetime_from_isoformat(value): + # type: (str) -> datetime + try: + result = datetime.fromisoformat(value) + except (AttributeError, ValueError): + # py 3.6 + timestamp_format = ( + "%Y-%m-%dT%H:%M:%S.%f" if "." in value else "%Y-%m-%dT%H:%M:%S" + ) + if value.endswith("Z"): + value = value[:-1] + "+0000" + + if value[-6] in ISO_TZ_SEPARATORS: + timestamp_format += "%z" + value = value[:-3] + value[-2:] + elif value[-5] in ISO_TZ_SEPARATORS: + timestamp_format += "%z" + + result = datetime.strptime(value, timestamp_format) + return result.astimezone(timezone.utc) + + +def event_hint_with_exc_info(exc_info=None): + # type: (Optional[ExcInfo]) -> Dict[str, Optional[ExcInfo]] + """Creates a hint with the exc info filled in.""" + if exc_info is None: + exc_info = sys.exc_info() + else: + exc_info = exc_info_from_error(exc_info) + if exc_info[0] is None: + exc_info = None + return {"exc_info": exc_info} + + +class BadDsn(ValueError): + """Raised on invalid DSNs.""" + + +class Dsn: + """Represents a DSN.""" + + def __init__(self, value): + # type: (Union[Dsn, str]) -> None + if isinstance(value, Dsn): + self.__dict__ = dict(value.__dict__) + return + parts = urlsplit(str(value)) + + if parts.scheme not in ("http", "https"): + raise BadDsn("Unsupported scheme %r" % parts.scheme) + self.scheme = parts.scheme + + if parts.hostname is None: + raise BadDsn("Missing hostname") + + self.host = parts.hostname + + if parts.port is None: + self.port = self.scheme == "https" and 443 or 80 # type: int + else: + self.port = parts.port + + if not parts.username: + raise BadDsn("Missing public key") + + self.public_key = parts.username + self.secret_key = parts.password + + path = parts.path.rsplit("/", 1) + + try: + self.project_id = str(int(path.pop())) + except (ValueError, TypeError): + raise BadDsn("Invalid project in DSN (%r)" % (parts.path or "")[1:]) + + self.path = "/".join(path) + "/" + + @property + def netloc(self): + # type: () -> str + """The netloc part of a DSN.""" + rv = self.host + if (self.scheme, self.port) not in (("http", 80), ("https", 443)): + rv = "%s:%s" % (rv, self.port) + return rv + + def to_auth(self, client=None): + # type: (Optional[Any]) -> Auth + """Returns the auth info object for this dsn.""" + return Auth( + scheme=self.scheme, + host=self.netloc, + path=self.path, + project_id=self.project_id, + public_key=self.public_key, + secret_key=self.secret_key, + client=client, + ) + + def __str__(self): + # type: () -> str + return "%s://%s%s@%s%s%s" % ( + self.scheme, + self.public_key, + self.secret_key and "@" + self.secret_key or "", + self.netloc, + self.path, + self.project_id, + ) + + +class Auth: + """Helper object that represents the auth info.""" + + def __init__( + self, + scheme, + host, + project_id, + public_key, + secret_key=None, + version=7, + client=None, + path="/", + ): + # type: (str, str, str, str, Optional[str], int, Optional[Any], str) -> None + self.scheme = scheme + self.host = host + self.path = path + self.project_id = project_id + self.public_key = public_key + self.secret_key = secret_key + self.version = version + self.client = client + + def get_api_url( + self, + type=EndpointType.ENVELOPE, # type: EndpointType + ): + # type: (...) -> str + """Returns the API url for storing events.""" + return "%s://%s%sapi/%s/%s/" % ( + self.scheme, + self.host, + self.path, + self.project_id, + type.value, + ) + + def to_header(self): + # type: () -> str + """Returns the auth header a string.""" + rv = [("sentry_key", self.public_key), ("sentry_version", self.version)] + if self.client is not None: + rv.append(("sentry_client", self.client)) + if self.secret_key is not None: + rv.append(("sentry_secret", self.secret_key)) + return "Sentry " + ", ".join("%s=%s" % (key, value) for key, value in rv) + + +def get_type_name(cls): + # type: (Optional[type]) -> Optional[str] + return getattr(cls, "__qualname__", None) or getattr(cls, "__name__", None) + + +def get_type_module(cls): + # type: (Optional[type]) -> Optional[str] + mod = getattr(cls, "__module__", None) + if mod not in (None, "builtins", "__builtins__"): + return mod + return None + + +def should_hide_frame(frame): + # type: (FrameType) -> bool + try: + mod = frame.f_globals["__name__"] + if mod.startswith("sentry_sdk."): + return True + except (AttributeError, KeyError): + pass + + for flag_name in "__traceback_hide__", "__tracebackhide__": + try: + if frame.f_locals[flag_name]: + return True + except Exception: + pass + + return False + + +def iter_stacks(tb): + # type: (Optional[TracebackType]) -> Iterator[TracebackType] + tb_ = tb # type: Optional[TracebackType] + while tb_ is not None: + if not should_hide_frame(tb_.tb_frame): + yield tb_ + tb_ = tb_.tb_next + + +def get_lines_from_file( + filename, # type: str + lineno, # type: int + max_length=None, # type: Optional[int] + loader=None, # type: Optional[Any] + module=None, # type: Optional[str] +): + # type: (...) -> Tuple[List[Annotated[str]], Optional[Annotated[str]], List[Annotated[str]]] + context_lines = 5 + source = None + if loader is not None and hasattr(loader, "get_source"): + try: + source_str = loader.get_source(module) # type: Optional[str] + except (ImportError, IOError): + source_str = None + if source_str is not None: + source = source_str.splitlines() + + if source is None: + try: + source = linecache.getlines(filename) + except (OSError, IOError): + return [], None, [] + + if not source: + return [], None, [] + + lower_bound = max(0, lineno - context_lines) + upper_bound = min(lineno + 1 + context_lines, len(source)) + + try: + pre_context = [ + strip_string(line.strip("\r\n"), max_length=max_length) + for line in source[lower_bound:lineno] + ] + context_line = strip_string(source[lineno].strip("\r\n"), max_length=max_length) + post_context = [ + strip_string(line.strip("\r\n"), max_length=max_length) + for line in source[(lineno + 1) : upper_bound] + ] + return pre_context, context_line, post_context + except IndexError: + # the file may have changed since it was loaded into memory + return [], None, [] + + +def get_source_context( + frame, # type: FrameType + tb_lineno, # type: Optional[int] + max_value_length=None, # type: Optional[int] +): + # type: (...) -> Tuple[List[Annotated[str]], Optional[Annotated[str]], List[Annotated[str]]] + try: + abs_path = frame.f_code.co_filename # type: Optional[str] + except Exception: + abs_path = None + try: + module = frame.f_globals["__name__"] + except Exception: + return [], None, [] + try: + loader = frame.f_globals["__loader__"] + except Exception: + loader = None + + if tb_lineno is not None and abs_path: + lineno = tb_lineno - 1 + return get_lines_from_file( + abs_path, lineno, max_value_length, loader=loader, module=module + ) + + return [], None, [] + + +def safe_str(value): + # type: (Any) -> str + try: + return str(value) + except Exception: + return safe_repr(value) + + +def safe_repr(value): + # type: (Any) -> str + try: + return repr(value) + except Exception: + return "" + + +def filename_for_module(module, abs_path): + # type: (Optional[str], Optional[str]) -> Optional[str] + if not abs_path or not module: + return abs_path + + try: + if abs_path.endswith(".pyc"): + abs_path = abs_path[:-1] + + base_module = module.split(".", 1)[0] + if base_module == module: + return os.path.basename(abs_path) + + base_module_path = sys.modules[base_module].__file__ + if not base_module_path: + return abs_path + + return abs_path.split(base_module_path.rsplit(os.sep, 2)[0], 1)[-1].lstrip( + os.sep + ) + except Exception: + return abs_path + + +def serialize_frame( + frame, + tb_lineno=None, + include_local_variables=True, + include_source_context=True, + max_value_length=None, + custom_repr=None, +): + # type: (FrameType, Optional[int], bool, bool, Optional[int], Optional[Callable[..., Optional[str]]]) -> Dict[str, Any] + f_code = getattr(frame, "f_code", None) + if not f_code: + abs_path = None + function = None + else: + abs_path = frame.f_code.co_filename + function = frame.f_code.co_name + try: + module = frame.f_globals["__name__"] + except Exception: + module = None + + if tb_lineno is None: + tb_lineno = frame.f_lineno + + try: + os_abs_path = os.path.abspath(abs_path) if abs_path else None + except Exception: + os_abs_path = None + + rv = { + "filename": filename_for_module(module, abs_path) or None, + "abs_path": os_abs_path, + "function": function or "", + "module": module, + "lineno": tb_lineno, + } # type: Dict[str, Any] + + if include_source_context: + rv["pre_context"], rv["context_line"], rv["post_context"] = get_source_context( + frame, tb_lineno, max_value_length + ) + + if include_local_variables: + from sentry_sdk.serializer import serialize + + rv["vars"] = serialize( + dict(frame.f_locals), is_vars=True, custom_repr=custom_repr + ) + + return rv + + +def current_stacktrace( + include_local_variables=True, # type: bool + include_source_context=True, # type: bool + max_value_length=None, # type: Optional[int] +): + # type: (...) -> Dict[str, Any] + __tracebackhide__ = True + frames = [] + + f = sys._getframe() # type: Optional[FrameType] + while f is not None: + if not should_hide_frame(f): + frames.append( + serialize_frame( + f, + include_local_variables=include_local_variables, + include_source_context=include_source_context, + max_value_length=max_value_length, + ) + ) + f = f.f_back + + frames.reverse() + + return {"frames": frames} + + +def get_errno(exc_value): + # type: (BaseException) -> Optional[Any] + return getattr(exc_value, "errno", None) + + +def get_error_message(exc_value): + # type: (Optional[BaseException]) -> str + message = ( + getattr(exc_value, "message", "") + or getattr(exc_value, "detail", "") + or safe_str(exc_value) + ) # type: str + + # __notes__ should be a list of strings when notes are added + # via add_note, but can be anything else if __notes__ is set + # directly. We only support strings in __notes__, since that + # is the correct use. + notes = getattr(exc_value, "__notes__", None) # type: object + if isinstance(notes, list) and len(notes) > 0: + message += "\n" + "\n".join(note for note in notes if isinstance(note, str)) + + return message + + +def single_exception_from_error_tuple( + exc_type, # type: Optional[type] + exc_value, # type: Optional[BaseException] + tb, # type: Optional[TracebackType] + client_options=None, # type: Optional[Dict[str, Any]] + mechanism=None, # type: Optional[Dict[str, Any]] + exception_id=None, # type: Optional[int] + parent_id=None, # type: Optional[int] + source=None, # type: Optional[str] + full_stack=None, # type: Optional[list[dict[str, Any]]] +): + # type: (...) -> Dict[str, Any] + """ + Creates a dict that goes into the events `exception.values` list and is ingestible by Sentry. + + See the Exception Interface documentation for more details: + https://develop.sentry.dev/sdk/event-payloads/exception/ + """ + exception_value = {} # type: Dict[str, Any] + exception_value["mechanism"] = ( + mechanism.copy() if mechanism else {"type": "generic", "handled": True} + ) + if exception_id is not None: + exception_value["mechanism"]["exception_id"] = exception_id + + if exc_value is not None: + errno = get_errno(exc_value) + else: + errno = None + + if errno is not None: + exception_value["mechanism"].setdefault("meta", {}).setdefault( + "errno", {} + ).setdefault("number", errno) + + if source is not None: + exception_value["mechanism"]["source"] = source + + is_root_exception = exception_id == 0 + if not is_root_exception and parent_id is not None: + exception_value["mechanism"]["parent_id"] = parent_id + exception_value["mechanism"]["type"] = "chained" + + if is_root_exception and "type" not in exception_value["mechanism"]: + exception_value["mechanism"]["type"] = "generic" + + is_exception_group = BaseExceptionGroup is not None and isinstance( + exc_value, BaseExceptionGroup + ) + if is_exception_group: + exception_value["mechanism"]["is_exception_group"] = True + + exception_value["module"] = get_type_module(exc_type) + exception_value["type"] = get_type_name(exc_type) + exception_value["value"] = get_error_message(exc_value) + + if client_options is None: + include_local_variables = True + include_source_context = True + max_value_length = DEFAULT_MAX_VALUE_LENGTH # fallback + custom_repr = None + else: + include_local_variables = client_options["include_local_variables"] + include_source_context = client_options["include_source_context"] + max_value_length = client_options["max_value_length"] + custom_repr = client_options.get("custom_repr") + + frames = [ + serialize_frame( + tb.tb_frame, + tb_lineno=tb.tb_lineno, + include_local_variables=include_local_variables, + include_source_context=include_source_context, + max_value_length=max_value_length, + custom_repr=custom_repr, + ) + # Process at most MAX_STACK_FRAMES + 1 frames, to avoid hanging on + # processing a super-long stacktrace. + for tb, _ in zip(iter_stacks(tb), range(MAX_STACK_FRAMES + 1)) + ] # type: List[Dict[str, Any]] + + if len(frames) > MAX_STACK_FRAMES: + # If we have more frames than the limit, we remove the stacktrace completely. + # We don't trim the stacktrace here because we have not processed the whole + # thing (see above, we stop at MAX_STACK_FRAMES + 1). Normally, Relay would + # intelligently trim by removing frames in the middle of the stacktrace, but + # since we don't have the whole stacktrace, we can't do that. Instead, we + # drop the entire stacktrace. + exception_value["stacktrace"] = AnnotatedValue.removed_because_over_size_limit( + value=None + ) + + elif frames: + if not full_stack: + new_frames = frames + else: + new_frames = merge_stack_frames(frames, full_stack, client_options) + + exception_value["stacktrace"] = {"frames": new_frames} + + return exception_value + + +HAS_CHAINED_EXCEPTIONS = hasattr(Exception, "__suppress_context__") + +if HAS_CHAINED_EXCEPTIONS: + + def walk_exception_chain(exc_info): + # type: (ExcInfo) -> Iterator[ExcInfo] + exc_type, exc_value, tb = exc_info + + seen_exceptions = [] + seen_exception_ids = set() # type: Set[int] + + while ( + exc_type is not None + and exc_value is not None + and id(exc_value) not in seen_exception_ids + ): + yield exc_type, exc_value, tb + + # Avoid hashing random types we don't know anything + # about. Use the list to keep a ref so that the `id` is + # not used for another object. + seen_exceptions.append(exc_value) + seen_exception_ids.add(id(exc_value)) + + if exc_value.__suppress_context__: + cause = exc_value.__cause__ + else: + cause = exc_value.__context__ + if cause is None: + break + exc_type = type(cause) + exc_value = cause + tb = getattr(cause, "__traceback__", None) + +else: + + def walk_exception_chain(exc_info): + # type: (ExcInfo) -> Iterator[ExcInfo] + yield exc_info + + +def exceptions_from_error( + exc_type, # type: Optional[type] + exc_value, # type: Optional[BaseException] + tb, # type: Optional[TracebackType] + client_options=None, # type: Optional[Dict[str, Any]] + mechanism=None, # type: Optional[Dict[str, Any]] + exception_id=0, # type: int + parent_id=0, # type: int + source=None, # type: Optional[str] + full_stack=None, # type: Optional[list[dict[str, Any]]] +): + # type: (...) -> Tuple[int, List[Dict[str, Any]]] + """ + Creates the list of exceptions. + This can include chained exceptions and exceptions from an ExceptionGroup. + + See the Exception Interface documentation for more details: + https://develop.sentry.dev/sdk/event-payloads/exception/ + """ + + parent = single_exception_from_error_tuple( + exc_type=exc_type, + exc_value=exc_value, + tb=tb, + client_options=client_options, + mechanism=mechanism, + exception_id=exception_id, + parent_id=parent_id, + source=source, + full_stack=full_stack, + ) + exceptions = [parent] + + parent_id = exception_id + exception_id += 1 + + should_supress_context = ( + hasattr(exc_value, "__suppress_context__") and exc_value.__suppress_context__ # type: ignore + ) + if should_supress_context: + # Add direct cause. + # The field `__cause__` is set when raised with the exception (using the `from` keyword). + exception_has_cause = ( + exc_value + and hasattr(exc_value, "__cause__") + and exc_value.__cause__ is not None + ) + if exception_has_cause: + cause = exc_value.__cause__ # type: ignore + (exception_id, child_exceptions) = exceptions_from_error( + exc_type=type(cause), + exc_value=cause, + tb=getattr(cause, "__traceback__", None), + client_options=client_options, + mechanism=mechanism, + exception_id=exception_id, + source="__cause__", + full_stack=full_stack, + ) + exceptions.extend(child_exceptions) + + else: + # Add indirect cause. + # The field `__context__` is assigned if another exception occurs while handling the exception. + exception_has_content = ( + exc_value + and hasattr(exc_value, "__context__") + and exc_value.__context__ is not None + ) + if exception_has_content: + context = exc_value.__context__ # type: ignore + (exception_id, child_exceptions) = exceptions_from_error( + exc_type=type(context), + exc_value=context, + tb=getattr(context, "__traceback__", None), + client_options=client_options, + mechanism=mechanism, + exception_id=exception_id, + source="__context__", + full_stack=full_stack, + ) + exceptions.extend(child_exceptions) + + # Add exceptions from an ExceptionGroup. + is_exception_group = exc_value and hasattr(exc_value, "exceptions") + if is_exception_group: + for idx, e in enumerate(exc_value.exceptions): # type: ignore + (exception_id, child_exceptions) = exceptions_from_error( + exc_type=type(e), + exc_value=e, + tb=getattr(e, "__traceback__", None), + client_options=client_options, + mechanism=mechanism, + exception_id=exception_id, + parent_id=parent_id, + source="exceptions[%s]" % idx, + full_stack=full_stack, + ) + exceptions.extend(child_exceptions) + + return (exception_id, exceptions) + + +def exceptions_from_error_tuple( + exc_info, # type: ExcInfo + client_options=None, # type: Optional[Dict[str, Any]] + mechanism=None, # type: Optional[Dict[str, Any]] + full_stack=None, # type: Optional[list[dict[str, Any]]] +): + # type: (...) -> List[Dict[str, Any]] + exc_type, exc_value, tb = exc_info + + is_exception_group = BaseExceptionGroup is not None and isinstance( + exc_value, BaseExceptionGroup + ) + + if is_exception_group: + (_, exceptions) = exceptions_from_error( + exc_type=exc_type, + exc_value=exc_value, + tb=tb, + client_options=client_options, + mechanism=mechanism, + exception_id=0, + parent_id=0, + full_stack=full_stack, + ) + + else: + exceptions = [] + for exc_type, exc_value, tb in walk_exception_chain(exc_info): + exceptions.append( + single_exception_from_error_tuple( + exc_type=exc_type, + exc_value=exc_value, + tb=tb, + client_options=client_options, + mechanism=mechanism, + full_stack=full_stack, + ) + ) + + exceptions.reverse() + + return exceptions + + +def to_string(value): + # type: (str) -> str + try: + return str(value) + except UnicodeDecodeError: + return repr(value)[1:-1] + + +def iter_event_stacktraces(event): + # type: (Event) -> Iterator[Annotated[Dict[str, Any]]] + if "stacktrace" in event: + yield event["stacktrace"] + if "threads" in event: + for thread in event["threads"].get("values") or (): + if "stacktrace" in thread: + yield thread["stacktrace"] + if "exception" in event: + for exception in event["exception"].get("values") or (): + if isinstance(exception, dict) and "stacktrace" in exception: + yield exception["stacktrace"] + + +def iter_event_frames(event): + # type: (Event) -> Iterator[Dict[str, Any]] + for stacktrace in iter_event_stacktraces(event): + if isinstance(stacktrace, AnnotatedValue): + stacktrace = stacktrace.value or {} + + for frame in stacktrace.get("frames") or (): + yield frame + + +def handle_in_app(event, in_app_exclude=None, in_app_include=None, project_root=None): + # type: (Event, Optional[List[str]], Optional[List[str]], Optional[str]) -> Event + for stacktrace in iter_event_stacktraces(event): + if isinstance(stacktrace, AnnotatedValue): + stacktrace = stacktrace.value or {} + + set_in_app_in_frames( + stacktrace.get("frames"), + in_app_exclude=in_app_exclude, + in_app_include=in_app_include, + project_root=project_root, + ) + + return event + + +def set_in_app_in_frames(frames, in_app_exclude, in_app_include, project_root=None): + # type: (Any, Optional[List[str]], Optional[List[str]], Optional[str]) -> Optional[Any] + if not frames: + return None + + for frame in frames: + # if frame has already been marked as in_app, skip it + current_in_app = frame.get("in_app") + if current_in_app is not None: + continue + + module = frame.get("module") + + # check if module in frame is in the list of modules to include + if _module_in_list(module, in_app_include): + frame["in_app"] = True + continue + + # check if module in frame is in the list of modules to exclude + if _module_in_list(module, in_app_exclude): + frame["in_app"] = False + continue + + # if frame has no abs_path, skip further checks + abs_path = frame.get("abs_path") + if abs_path is None: + continue + + if _is_external_source(abs_path): + frame["in_app"] = False + continue + + if _is_in_project_root(abs_path, project_root): + frame["in_app"] = True + continue + + return frames + + +def exc_info_from_error(error): + # type: (Union[BaseException, ExcInfo]) -> ExcInfo + if isinstance(error, tuple) and len(error) == 3: + exc_type, exc_value, tb = error + elif isinstance(error, BaseException): + tb = getattr(error, "__traceback__", None) + if tb is not None: + exc_type = type(error) + exc_value = error + else: + exc_type, exc_value, tb = sys.exc_info() + if exc_value is not error: + tb = None + exc_value = error + exc_type = type(error) + + else: + raise ValueError("Expected Exception object to report, got %s!" % type(error)) + + exc_info = (exc_type, exc_value, tb) + + if TYPE_CHECKING: + # This cast is safe because exc_type and exc_value are either both + # None or both not None. + exc_info = cast(ExcInfo, exc_info) + + return exc_info + + +def merge_stack_frames(frames, full_stack, client_options): + # type: (List[Dict[str, Any]], List[Dict[str, Any]], Optional[Dict[str, Any]]) -> List[Dict[str, Any]] + """ + Add the missing frames from full_stack to frames and return the merged list. + """ + frame_ids = { + ( + frame["abs_path"], + frame["context_line"], + frame["lineno"], + frame["function"], + ) + for frame in frames + } + + new_frames = [ + stackframe + for stackframe in full_stack + if ( + stackframe["abs_path"], + stackframe["context_line"], + stackframe["lineno"], + stackframe["function"], + ) + not in frame_ids + ] + new_frames.extend(frames) + + # Limit the number of frames + max_stack_frames = ( + client_options.get("max_stack_frames", DEFAULT_MAX_STACK_FRAMES) + if client_options + else None + ) + if max_stack_frames is not None: + new_frames = new_frames[len(new_frames) - max_stack_frames :] + + return new_frames + + +def event_from_exception( + exc_info, # type: Union[BaseException, ExcInfo] + client_options=None, # type: Optional[Dict[str, Any]] + mechanism=None, # type: Optional[Dict[str, Any]] +): + # type: (...) -> Tuple[Event, Dict[str, Any]] + exc_info = exc_info_from_error(exc_info) + hint = event_hint_with_exc_info(exc_info) + + if client_options and client_options.get("add_full_stack", DEFAULT_ADD_FULL_STACK): + full_stack = current_stacktrace( + include_local_variables=client_options["include_local_variables"], + max_value_length=client_options["max_value_length"], + )["frames"] + else: + full_stack = None + + return ( + { + "level": "error", + "exception": { + "values": exceptions_from_error_tuple( + exc_info, client_options, mechanism, full_stack + ) + }, + }, + hint, + ) + + +def _module_in_list(name, items): + # type: (Optional[str], Optional[List[str]]) -> bool + if name is None: + return False + + if not items: + return False + + for item in items: + if item == name or name.startswith(item + "."): + return True + + return False + + +def _is_external_source(abs_path): + # type: (Optional[str]) -> bool + # check if frame is in 'site-packages' or 'dist-packages' + if abs_path is None: + return False + + external_source = ( + re.search(r"[\\/](?:dist|site)-packages[\\/]", abs_path) is not None + ) + return external_source + + +def _is_in_project_root(abs_path, project_root): + # type: (Optional[str], Optional[str]) -> bool + if abs_path is None or project_root is None: + return False + + # check if path is in the project root + if abs_path.startswith(project_root): + return True + + return False + + +def _truncate_by_bytes(string, max_bytes): + # type: (str, int) -> str + """ + Truncate a UTF-8-encodable string to the last full codepoint so that it fits in max_bytes. + """ + truncated = string.encode("utf-8")[: max_bytes - 3].decode("utf-8", errors="ignore") + + return truncated + "..." + + +def _get_size_in_bytes(value): + # type: (str) -> Optional[int] + try: + return len(value.encode("utf-8")) + except (UnicodeEncodeError, UnicodeDecodeError): + return None + + +def strip_string(value, max_length=None): + # type: (str, Optional[int]) -> Union[AnnotatedValue, str] + if not value: + return value + + if max_length is None: + max_length = DEFAULT_MAX_VALUE_LENGTH + + byte_size = _get_size_in_bytes(value) + text_size = len(value) + + if byte_size is not None and byte_size > max_length: + # truncate to max_length bytes, preserving code points + truncated_value = _truncate_by_bytes(value, max_length) + elif text_size is not None and text_size > max_length: + # fallback to truncating by string length + truncated_value = value[: max_length - 3] + "..." + else: + return value + + return AnnotatedValue( + value=truncated_value, + metadata={ + "len": byte_size or text_size, + "rem": [["!limit", "x", max_length - 3, max_length]], + }, + ) + + +def parse_version(version): + # type: (str) -> Optional[Tuple[int, ...]] + """ + Parses a version string into a tuple of integers. + This uses the parsing loging from PEP 440: + https://peps.python.org/pep-0440/#appendix-b-parsing-version-strings-with-regular-expressions + """ + VERSION_PATTERN = r""" # noqa: N806 + v? + (?: + (?:(?P[0-9]+)!)? # epoch + (?P[0-9]+(?:\.[0-9]+)*) # release segment + (?P
                                          # pre-release
+                [-_\.]?
+                (?P(a|b|c|rc|alpha|beta|pre|preview))
+                [-_\.]?
+                (?P[0-9]+)?
+            )?
+            (?P                                         # post release
+                (?:-(?P[0-9]+))
+                |
+                (?:
+                    [-_\.]?
+                    (?Ppost|rev|r)
+                    [-_\.]?
+                    (?P[0-9]+)?
+                )
+            )?
+            (?P                                          # dev release
+                [-_\.]?
+                (?Pdev)
+                [-_\.]?
+                (?P[0-9]+)?
+            )?
+        )
+        (?:\+(?P[a-z0-9]+(?:[-_\.][a-z0-9]+)*))?       # local version
+    """
+
+    pattern = re.compile(
+        r"^\s*" + VERSION_PATTERN + r"\s*$",
+        re.VERBOSE | re.IGNORECASE,
+    )
+
+    try:
+        release = pattern.match(version).groupdict()["release"]  # type: ignore
+        release_tuple = tuple(map(int, release.split(".")[:3]))  # type: Tuple[int, ...]
+    except (TypeError, ValueError, AttributeError):
+        return None
+
+    return release_tuple
+
+
+def _is_contextvars_broken():
+    # type: () -> bool
+    """
+    Returns whether gevent/eventlet have patched the stdlib in a way where thread locals are now more "correct" than contextvars.
+    """
+    try:
+        import gevent
+        from gevent.monkey import is_object_patched
+
+        # Get the MAJOR and MINOR version numbers of Gevent
+        version_tuple = tuple(
+            [int(part) for part in re.split(r"a|b|rc|\.", gevent.__version__)[:2]]
+        )
+        if is_object_patched("threading", "local"):
+            # Gevent 20.9.0 depends on Greenlet 0.4.17 which natively handles switching
+            # context vars when greenlets are switched, so, Gevent 20.9.0+ is all fine.
+            # Ref: https://github.com/gevent/gevent/blob/83c9e2ae5b0834b8f84233760aabe82c3ba065b4/src/gevent/monkey.py#L604-L609
+            # Gevent 20.5, that doesn't depend on Greenlet 0.4.17 with native support
+            # for contextvars, is able to patch both thread locals and contextvars, in
+            # that case, check if contextvars are effectively patched.
+            if (
+                # Gevent 20.9.0+
+                (sys.version_info >= (3, 7) and version_tuple >= (20, 9))
+                # Gevent 20.5.0+ or Python < 3.7
+                or (is_object_patched("contextvars", "ContextVar"))
+            ):
+                return False
+
+            return True
+    except ImportError:
+        pass
+
+    try:
+        import greenlet
+        from eventlet.patcher import is_monkey_patched  # type: ignore
+
+        greenlet_version = parse_version(greenlet.__version__)
+
+        if greenlet_version is None:
+            logger.error(
+                "Internal error in Sentry SDK: Could not parse Greenlet version from greenlet.__version__."
+            )
+            return False
+
+        if is_monkey_patched("thread") and greenlet_version < (0, 5):
+            return True
+    except ImportError:
+        pass
+
+    return False
+
+
+def _make_threadlocal_contextvars(local):
+    # type: (type) -> type
+    class ContextVar:
+        # Super-limited impl of ContextVar
+
+        def __init__(self, name, default=None):
+            # type: (str, Any) -> None
+            self._name = name
+            self._default = default
+            self._local = local()
+            self._original_local = local()
+
+        def get(self, default=None):
+            # type: (Any) -> Any
+            return getattr(self._local, "value", default or self._default)
+
+        def set(self, value):
+            # type: (Any) -> Any
+            token = str(random.getrandbits(64))
+            original_value = self.get()
+            setattr(self._original_local, token, original_value)
+            self._local.value = value
+            return token
+
+        def reset(self, token):
+            # type: (Any) -> None
+            self._local.value = getattr(self._original_local, token)
+            # delete the original value (this way it works in Python 3.6+)
+            del self._original_local.__dict__[token]
+
+    return ContextVar
+
+
+def _get_contextvars():
+    # type: () -> Tuple[bool, type]
+    """
+    Figure out the "right" contextvars installation to use. Returns a
+    `contextvars.ContextVar`-like class with a limited API.
+
+    See https://docs.sentry.io/platforms/python/contextvars/ for more information.
+    """
+    if not _is_contextvars_broken():
+        # aiocontextvars is a PyPI package that ensures that the contextvars
+        # backport (also a PyPI package) works with asyncio under Python 3.6
+        #
+        # Import it if available.
+        if sys.version_info < (3, 7):
+            # `aiocontextvars` is absolutely required for functional
+            # contextvars on Python 3.6.
+            try:
+                from aiocontextvars import ContextVar
+
+                return True, ContextVar
+            except ImportError:
+                pass
+        else:
+            # On Python 3.7 contextvars are functional.
+            try:
+                from contextvars import ContextVar
+
+                return True, ContextVar
+            except ImportError:
+                pass
+
+    # Fall back to basic thread-local usage.
+
+    from threading import local
+
+    return False, _make_threadlocal_contextvars(local)
+
+
+HAS_REAL_CONTEXTVARS, ContextVar = _get_contextvars()
+
+CONTEXTVARS_ERROR_MESSAGE = """
+
+With asyncio/ASGI applications, the Sentry SDK requires a functional
+installation of `contextvars` to avoid leaking scope/context data across
+requests.
+
+Please refer to https://docs.sentry.io/platforms/python/contextvars/ for more information.
+"""
+
+
+def qualname_from_function(func):
+    # type: (Callable[..., Any]) -> Optional[str]
+    """Return the qualified name of func. Works with regular function, lambda, partial and partialmethod."""
+    func_qualname = None  # type: Optional[str]
+
+    # Python 2
+    try:
+        return "%s.%s.%s" % (
+            func.im_class.__module__,  # type: ignore
+            func.im_class.__name__,  # type: ignore
+            func.__name__,
+        )
+    except Exception:
+        pass
+
+    prefix, suffix = "", ""
+
+    if isinstance(func, partial) and hasattr(func.func, "__name__"):
+        prefix, suffix = "partial()"
+        func = func.func
+    else:
+        # The _partialmethod attribute of methods wrapped with partialmethod() was renamed to __partialmethod__ in CPython 3.13:
+        # https://github.com/python/cpython/pull/16600
+        partial_method = getattr(func, "_partialmethod", None) or getattr(
+            func, "__partialmethod__", None
+        )
+        if isinstance(partial_method, partialmethod):
+            prefix, suffix = "partialmethod()"
+            func = partial_method.func
+
+    if hasattr(func, "__qualname__"):
+        func_qualname = func.__qualname__
+    elif hasattr(func, "__name__"):  # Python 2.7 has no __qualname__
+        func_qualname = func.__name__
+
+    # Python 3: methods, functions, classes
+    if func_qualname is not None:
+        if hasattr(func, "__module__") and isinstance(func.__module__, str):
+            func_qualname = func.__module__ + "." + func_qualname
+        func_qualname = prefix + func_qualname + suffix
+
+    return func_qualname
+
+
+def transaction_from_function(func):
+    # type: (Callable[..., Any]) -> Optional[str]
+    return qualname_from_function(func)
+
+
+disable_capture_event = ContextVar("disable_capture_event")
+
+
+class ServerlessTimeoutWarning(Exception):  # noqa: N818
+    """Raised when a serverless method is about to reach its timeout."""
+
+    pass
+
+
+class TimeoutThread(threading.Thread):
+    """Creates a Thread which runs (sleeps) for a time duration equal to
+    waiting_time and raises a custom ServerlessTimeout exception.
+    """
+
+    def __init__(self, waiting_time, configured_timeout):
+        # type: (float, int) -> None
+        threading.Thread.__init__(self)
+        self.waiting_time = waiting_time
+        self.configured_timeout = configured_timeout
+        self._stop_event = threading.Event()
+
+    def stop(self):
+        # type: () -> None
+        self._stop_event.set()
+
+    def run(self):
+        # type: () -> None
+
+        self._stop_event.wait(self.waiting_time)
+
+        if self._stop_event.is_set():
+            return
+
+        integer_configured_timeout = int(self.configured_timeout)
+
+        # Setting up the exact integer value of configured time(in seconds)
+        if integer_configured_timeout < self.configured_timeout:
+            integer_configured_timeout = integer_configured_timeout + 1
+
+        # Raising Exception after timeout duration is reached
+        raise ServerlessTimeoutWarning(
+            "WARNING : Function is expected to get timed out. Configured timeout duration = {} seconds.".format(
+                integer_configured_timeout
+            )
+        )
+
+
+def to_base64(original):
+    # type: (str) -> Optional[str]
+    """
+    Convert a string to base64, via UTF-8. Returns None on invalid input.
+    """
+    base64_string = None
+
+    try:
+        utf8_bytes = original.encode("UTF-8")
+        base64_bytes = base64.b64encode(utf8_bytes)
+        base64_string = base64_bytes.decode("UTF-8")
+    except Exception as err:
+        logger.warning("Unable to encode {orig} to base64:".format(orig=original), err)
+
+    return base64_string
+
+
+def from_base64(base64_string):
+    # type: (str) -> Optional[str]
+    """
+    Convert a string from base64, via UTF-8. Returns None on invalid input.
+    """
+    utf8_string = None
+
+    try:
+        only_valid_chars = BASE64_ALPHABET.match(base64_string)
+        assert only_valid_chars
+
+        base64_bytes = base64_string.encode("UTF-8")
+        utf8_bytes = base64.b64decode(base64_bytes)
+        utf8_string = utf8_bytes.decode("UTF-8")
+    except Exception as err:
+        logger.warning(
+            "Unable to decode {b64} from base64:".format(b64=base64_string), err
+        )
+
+    return utf8_string
+
+
+Components = namedtuple("Components", ["scheme", "netloc", "path", "query", "fragment"])
+
+
+def sanitize_url(url, remove_authority=True, remove_query_values=True, split=False):
+    # type: (str, bool, bool, bool) -> Union[str, Components]
+    """
+    Removes the authority and query parameter values from a given URL.
+    """
+    parsed_url = urlsplit(url)
+    query_params = parse_qs(parsed_url.query, keep_blank_values=True)
+
+    # strip username:password (netloc can be usr:pwd@example.com)
+    if remove_authority:
+        netloc_parts = parsed_url.netloc.split("@")
+        if len(netloc_parts) > 1:
+            netloc = "%s:%s@%s" % (
+                SENSITIVE_DATA_SUBSTITUTE,
+                SENSITIVE_DATA_SUBSTITUTE,
+                netloc_parts[-1],
+            )
+        else:
+            netloc = parsed_url.netloc
+    else:
+        netloc = parsed_url.netloc
+
+    # strip values from query string
+    if remove_query_values:
+        query_string = unquote(
+            urlencode({key: SENSITIVE_DATA_SUBSTITUTE for key in query_params})
+        )
+    else:
+        query_string = parsed_url.query
+
+    components = Components(
+        scheme=parsed_url.scheme,
+        netloc=netloc,
+        query=query_string,
+        path=parsed_url.path,
+        fragment=parsed_url.fragment,
+    )
+
+    if split:
+        return components
+    else:
+        return urlunsplit(components)
+
+
+ParsedUrl = namedtuple("ParsedUrl", ["url", "query", "fragment"])
+
+
+def parse_url(url, sanitize=True):
+    # type: (str, bool) -> ParsedUrl
+    """
+    Splits a URL into a url (including path), query and fragment. If sanitize is True, the query
+    parameters will be sanitized to remove sensitive data. The autority (username and password)
+    in the URL will always be removed.
+    """
+    parsed_url = sanitize_url(
+        url, remove_authority=True, remove_query_values=sanitize, split=True
+    )
+
+    base_url = urlunsplit(
+        Components(
+            scheme=parsed_url.scheme,  # type: ignore
+            netloc=parsed_url.netloc,  # type: ignore
+            query="",
+            path=parsed_url.path,  # type: ignore
+            fragment="",
+        )
+    )
+
+    return ParsedUrl(
+        url=base_url,
+        query=parsed_url.query,  # type: ignore
+        fragment=parsed_url.fragment,  # type: ignore
+    )
+
+
+def is_valid_sample_rate(rate, source):
+    # type: (Any, str) -> bool
+    """
+    Checks the given sample rate to make sure it is valid type and value (a
+    boolean or a number between 0 and 1, inclusive).
+    """
+
+    # both booleans and NaN are instances of Real, so a) checking for Real
+    # checks for the possibility of a boolean also, and b) we have to check
+    # separately for NaN and Decimal does not derive from Real so need to check that too
+    if not isinstance(rate, (Real, Decimal)) or math.isnan(rate):
+        logger.warning(
+            "{source} Given sample rate is invalid. Sample rate must be a boolean or a number between 0 and 1. Got {rate} of type {type}.".format(
+                source=source, rate=rate, type=type(rate)
+            )
+        )
+        return False
+
+    # in case rate is a boolean, it will get cast to 1 if it's True and 0 if it's False
+    rate = float(rate)
+    if rate < 0 or rate > 1:
+        logger.warning(
+            "{source} Given sample rate is invalid. Sample rate must be between 0 and 1. Got {rate}.".format(
+                source=source, rate=rate
+            )
+        )
+        return False
+
+    return True
+
+
+def match_regex_list(item, regex_list=None, substring_matching=False):
+    # type: (str, Optional[List[str]], bool) -> bool
+    if regex_list is None:
+        return False
+
+    for item_matcher in regex_list:
+        if not substring_matching and item_matcher[-1] != "$":
+            item_matcher += "$"
+
+        matched = re.search(item_matcher, item)
+        if matched:
+            return True
+
+    return False
+
+
+def is_sentry_url(client, url):
+    # type: (sentry_sdk.client.BaseClient, str) -> bool
+    """
+    Determines whether the given URL matches the Sentry DSN.
+    """
+    return (
+        client is not None
+        and client.transport is not None
+        and client.transport.parsed_dsn is not None
+        and client.transport.parsed_dsn.netloc in url
+    )
+
+
+def _generate_installed_modules():
+    # type: () -> Iterator[Tuple[str, str]]
+    try:
+        from importlib import metadata
+
+        yielded = set()
+        for dist in metadata.distributions():
+            name = dist.metadata.get("Name", None)  # type: ignore[attr-defined]
+            # `metadata` values may be `None`, see:
+            # https://github.com/python/cpython/issues/91216
+            # and
+            # https://github.com/python/importlib_metadata/issues/371
+            if name is not None:
+                normalized_name = _normalize_module_name(name)
+                if dist.version is not None and normalized_name not in yielded:
+                    yield normalized_name, dist.version
+                    yielded.add(normalized_name)
+
+    except ImportError:
+        # < py3.8
+        try:
+            import pkg_resources
+        except ImportError:
+            return
+
+        for info in pkg_resources.working_set:
+            yield _normalize_module_name(info.key), info.version
+
+
+def _normalize_module_name(name):
+    # type: (str) -> str
+    return name.lower()
+
+
+def _get_installed_modules():
+    # type: () -> Dict[str, str]
+    global _installed_modules
+    if _installed_modules is None:
+        _installed_modules = dict(_generate_installed_modules())
+    return _installed_modules
+
+
+def package_version(package):
+    # type: (str) -> Optional[Tuple[int, ...]]
+    installed_packages = _get_installed_modules()
+    version = installed_packages.get(package)
+    if version is None:
+        return None
+
+    return parse_version(version)
+
+
+def reraise(tp, value, tb=None):
+    # type: (Optional[Type[BaseException]], Optional[BaseException], Optional[Any]) -> NoReturn
+    assert value is not None
+    if value.__traceback__ is not tb:
+        raise value.with_traceback(tb)
+    raise value
+
+
+def _no_op(*_a, **_k):
+    # type: (*Any, **Any) -> None
+    """No-op function for ensure_integration_enabled."""
+    pass
+
+
+if TYPE_CHECKING:
+
+    @overload
+    def ensure_integration_enabled(
+        integration,  # type: type[sentry_sdk.integrations.Integration]
+        original_function,  # type: Callable[P, R]
+    ):
+        # type: (...) -> Callable[[Callable[P, R]], Callable[P, R]]
+        ...
+
+    @overload
+    def ensure_integration_enabled(
+        integration,  # type: type[sentry_sdk.integrations.Integration]
+    ):
+        # type: (...) -> Callable[[Callable[P, None]], Callable[P, None]]
+        ...
+
+
+def ensure_integration_enabled(
+    integration,  # type: type[sentry_sdk.integrations.Integration]
+    original_function=_no_op,  # type: Union[Callable[P, R], Callable[P, None]]
+):
+    # type: (...) -> Callable[[Callable[P, R]], Callable[P, R]]
+    """
+    Ensures a given integration is enabled prior to calling a Sentry-patched function.
+
+    The function takes as its parameters the integration that must be enabled and the original
+    function that the SDK is patching. The function returns a function that takes the
+    decorated (Sentry-patched) function as its parameter, and returns a function that, when
+    called, checks whether the given integration is enabled. If the integration is enabled, the
+    function calls the decorated, Sentry-patched function. If the integration is not enabled,
+    the original function is called.
+
+    The function also takes care of preserving the original function's signature and docstring.
+
+    Example usage:
+
+    ```python
+    @ensure_integration_enabled(MyIntegration, my_function)
+    def patch_my_function():
+        with sentry_sdk.start_transaction(...):
+            return my_function()
+    ```
+    """
+    if TYPE_CHECKING:
+        # Type hint to ensure the default function has the right typing. The overloads
+        # ensure the default _no_op function is only used when R is None.
+        original_function = cast(Callable[P, R], original_function)
+
+    def patcher(sentry_patched_function):
+        # type: (Callable[P, R]) -> Callable[P, R]
+        def runner(*args: "P.args", **kwargs: "P.kwargs"):
+            # type: (...) -> R
+            if sentry_sdk.get_client().get_integration(integration) is None:
+                return original_function(*args, **kwargs)
+
+            return sentry_patched_function(*args, **kwargs)
+
+        if original_function is _no_op:
+            return wraps(sentry_patched_function)(runner)
+
+        return wraps(original_function)(runner)
+
+    return patcher
+
+
+if PY37:
+
+    def nanosecond_time():
+        # type: () -> int
+        return time.perf_counter_ns()
+
+else:
+
+    def nanosecond_time():
+        # type: () -> int
+        return int(time.perf_counter() * 1e9)
+
+
+def now():
+    # type: () -> float
+    return time.perf_counter()
+
+
+try:
+    from gevent import get_hub as get_gevent_hub
+    from gevent.monkey import is_module_patched
+except ImportError:
+    # it's not great that the signatures are different, get_hub can't return None
+    # consider adding an if TYPE_CHECKING to change the signature to Optional[Hub]
+    def get_gevent_hub():  # type: ignore[misc]
+        # type: () -> Optional[Hub]
+        return None
+
+    def is_module_patched(mod_name):
+        # type: (str) -> bool
+        # unable to import from gevent means no modules have been patched
+        return False
+
+
+def is_gevent():
+    # type: () -> bool
+    return is_module_patched("threading") or is_module_patched("_thread")
+
+
+def get_current_thread_meta(thread=None):
+    # type: (Optional[threading.Thread]) -> Tuple[Optional[int], Optional[str]]
+    """
+    Try to get the id of the current thread, with various fall backs.
+    """
+
+    # if a thread is specified, that takes priority
+    if thread is not None:
+        try:
+            thread_id = thread.ident
+            thread_name = thread.name
+            if thread_id is not None:
+                return thread_id, thread_name
+        except AttributeError:
+            pass
+
+    # if the app is using gevent, we should look at the gevent hub first
+    # as the id there differs from what the threading module reports
+    if is_gevent():
+        gevent_hub = get_gevent_hub()
+        if gevent_hub is not None:
+            try:
+                # this is undocumented, so wrap it in try except to be safe
+                return gevent_hub.thread_ident, None
+            except AttributeError:
+                pass
+
+    # use the current thread's id if possible
+    try:
+        thread = threading.current_thread()
+        thread_id = thread.ident
+        thread_name = thread.name
+        if thread_id is not None:
+            return thread_id, thread_name
+    except AttributeError:
+        pass
+
+    # if we can't get the current thread id, fall back to the main thread id
+    try:
+        thread = threading.main_thread()
+        thread_id = thread.ident
+        thread_name = thread.name
+        if thread_id is not None:
+            return thread_id, thread_name
+    except AttributeError:
+        pass
+
+    # we've tried everything, time to give up
+    return None, None
+
+
+def should_be_treated_as_error(ty, value):
+    # type: (Any, Any) -> bool
+    if ty == SystemExit and hasattr(value, "code") and value.code in (0, None):
+        # https://docs.python.org/3/library/exceptions.html#SystemExit
+        return False
+
+    return True
+
+
+if TYPE_CHECKING:
+    T = TypeVar("T")
+
+
+def try_convert(convert_func, value):
+    # type: (Callable[[Any], T], Any) -> Optional[T]
+    """
+    Attempt to convert from an unknown type to a specific type, using the
+    given function. Return None if the conversion fails, i.e. if the function
+    raises an exception.
+    """
+    try:
+        if isinstance(value, convert_func):  # type: ignore
+            return value
+    except TypeError:
+        pass
+
+    try:
+        return convert_func(value)
+    except Exception:
+        return None
+
+
+def safe_serialize(data):
+    # type: (Any) -> str
+    """Safely serialize to a readable string."""
+
+    def serialize_item(item):
+        # type: (Any) -> Union[str, dict[Any, Any], list[Any], tuple[Any, ...]]
+        if callable(item):
+            try:
+                module = getattr(item, "__module__", None)
+                qualname = getattr(item, "__qualname__", None)
+                name = getattr(item, "__name__", "anonymous")
+
+                if module and qualname:
+                    full_path = f"{module}.{qualname}"
+                elif module and name:
+                    full_path = f"{module}.{name}"
+                else:
+                    full_path = name
+
+                return f""
+            except Exception:
+                return f""
+        elif isinstance(item, dict):
+            return {k: serialize_item(v) for k, v in item.items()}
+        elif isinstance(item, (list, tuple)):
+            return [serialize_item(x) for x in item]
+        elif hasattr(item, "__dict__"):
+            try:
+                attrs = {
+                    k: serialize_item(v)
+                    for k, v in vars(item).items()
+                    if not k.startswith("_")
+                }
+                return f"<{type(item).__name__} {attrs}>"
+            except Exception:
+                return repr(item)
+        else:
+            return item
+
+    try:
+        serialized = serialize_item(data)
+        return json.dumps(serialized, default=str)
+    except Exception:
+        return str(data)
+
+
+def has_logs_enabled(options):
+    # type: (Optional[dict[str, Any]]) -> bool
+    if options is None:
+        return False
+
+    return bool(
+        options.get("enable_logs", False)
+        or options["_experiments"].get("enable_logs", False)
+    )
+
+
+def get_before_send_log(options):
+    # type: (Optional[dict[str, Any]]) -> Optional[Callable[[Log, Hint], Optional[Log]]]
+    if options is None:
+        return None
+
+    return options.get("before_send_log") or options["_experiments"].get(
+        "before_send_log"
+    )
+
+
+def has_metrics_enabled(options):
+    # type: (Optional[dict[str, Any]]) -> bool
+    if options is None:
+        return False
+
+    return bool(options["_experiments"].get("enable_metrics", False))
+
+
+def get_before_send_metric(options):
+    # type: (Optional[dict[str, Any]]) -> Optional[Callable[[Metric, Hint], Optional[Metric]]]
+    if options is None:
+        return None
+
+    return options["_experiments"].get("before_send_metric")
diff --git a/.venv/lib/python3.12/site-packages/sentry_sdk/worker.py b/.venv/lib/python3.12/site-packages/sentry_sdk/worker.py
new file mode 100644
index 0000000000000000000000000000000000000000..b04ea582bcc20d73dd6cae43253747c9cd668d1e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sentry_sdk/worker.py
@@ -0,0 +1,141 @@
+import os
+import threading
+
+from time import sleep, time
+from sentry_sdk._queue import Queue, FullError
+from sentry_sdk.utils import logger
+from sentry_sdk.consts import DEFAULT_QUEUE_SIZE
+
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from typing import Any
+    from typing import Optional
+    from typing import Callable
+
+
+_TERMINATOR = object()
+
+
+class BackgroundWorker:
+    def __init__(self, queue_size=DEFAULT_QUEUE_SIZE):
+        # type: (int) -> None
+        self._queue = Queue(queue_size)  # type: Queue
+        self._lock = threading.Lock()
+        self._thread = None  # type: Optional[threading.Thread]
+        self._thread_for_pid = None  # type: Optional[int]
+
+    @property
+    def is_alive(self):
+        # type: () -> bool
+        if self._thread_for_pid != os.getpid():
+            return False
+        if not self._thread:
+            return False
+        return self._thread.is_alive()
+
+    def _ensure_thread(self):
+        # type: () -> None
+        if not self.is_alive:
+            self.start()
+
+    def _timed_queue_join(self, timeout):
+        # type: (float) -> bool
+        deadline = time() + timeout
+        queue = self._queue
+
+        queue.all_tasks_done.acquire()
+
+        try:
+            while queue.unfinished_tasks:
+                delay = deadline - time()
+                if delay <= 0:
+                    return False
+                queue.all_tasks_done.wait(timeout=delay)
+
+            return True
+        finally:
+            queue.all_tasks_done.release()
+
+    def start(self):
+        # type: () -> None
+        with self._lock:
+            if not self.is_alive:
+                self._thread = threading.Thread(
+                    target=self._target, name="sentry-sdk.BackgroundWorker"
+                )
+                self._thread.daemon = True
+                try:
+                    self._thread.start()
+                    self._thread_for_pid = os.getpid()
+                except RuntimeError:
+                    # At this point we can no longer start because the interpreter
+                    # is already shutting down.  Sadly at this point we can no longer
+                    # send out events.
+                    self._thread = None
+
+    def kill(self):
+        # type: () -> None
+        """
+        Kill worker thread. Returns immediately. Not useful for
+        waiting on shutdown for events, use `flush` for that.
+        """
+        logger.debug("background worker got kill request")
+        with self._lock:
+            if self._thread:
+                try:
+                    self._queue.put_nowait(_TERMINATOR)
+                except FullError:
+                    logger.debug("background worker queue full, kill failed")
+
+                self._thread = None
+                self._thread_for_pid = None
+
+    def flush(self, timeout, callback=None):
+        # type: (float, Optional[Any]) -> None
+        logger.debug("background worker got flush request")
+        with self._lock:
+            if self.is_alive and timeout > 0.0:
+                self._wait_flush(timeout, callback)
+        logger.debug("background worker flushed")
+
+    def full(self):
+        # type: () -> bool
+        return self._queue.full()
+
+    def _wait_flush(self, timeout, callback):
+        # type: (float, Optional[Any]) -> None
+        initial_timeout = min(0.1, timeout)
+        if not self._timed_queue_join(initial_timeout):
+            pending = self._queue.qsize() + 1
+            logger.debug("%d event(s) pending on flush", pending)
+            if callback is not None:
+                callback(pending, timeout)
+
+            if not self._timed_queue_join(timeout - initial_timeout):
+                pending = self._queue.qsize() + 1
+                logger.error("flush timed out, dropped %s events", pending)
+
+    def submit(self, callback):
+        # type: (Callable[[], None]) -> bool
+        self._ensure_thread()
+        try:
+            self._queue.put_nowait(callback)
+            return True
+        except FullError:
+            return False
+
+    def _target(self):
+        # type: () -> None
+        while True:
+            callback = self._queue.get()
+            try:
+                if callback is _TERMINATOR:
+                    break
+                try:
+                    callback()
+                except Exception:
+                    logger.error("Failed processing job", exc_info=True)
+            finally:
+                self._queue.task_done()
+            sleep(0)
diff --git a/.venv/lib/python3.12/site-packages/setuptools-80.9.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/setuptools-80.9.0.dist-info/INSTALLER
new file mode 100644
index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools-80.9.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+uv
\ No newline at end of file
diff --git a/.venv/lib/python3.12/site-packages/setuptools-80.9.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/setuptools-80.9.0.dist-info/METADATA
new file mode 100644
index 0000000000000000000000000000000000000000..f125370964ecca2fe6493df1e0afe1d4d0a21f8d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools-80.9.0.dist-info/METADATA
@@ -0,0 +1,141 @@
+Metadata-Version: 2.4
+Name: setuptools
+Version: 80.9.0
+Summary: Easily download, build, install, upgrade, and uninstall Python packages
+Author-email: Python Packaging Authority 
+License-Expression: MIT
+Project-URL: Source, https://github.com/pypa/setuptools
+Project-URL: Documentation, https://setuptools.pypa.io/
+Project-URL: Changelog, https://setuptools.pypa.io/en/stable/history.html
+Keywords: CPAN PyPI distutils eggs package management
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: System :: Archiving :: Packaging
+Classifier: Topic :: System :: Systems Administration
+Classifier: Topic :: Utilities
+Requires-Python: >=3.9
+Description-Content-Type: text/x-rst
+License-File: LICENSE
+Provides-Extra: test
+Requires-Dist: pytest!=8.1.*,>=6; extra == "test"
+Requires-Dist: virtualenv>=13.0.0; extra == "test"
+Requires-Dist: wheel>=0.44.0; extra == "test"
+Requires-Dist: pip>=19.1; extra == "test"
+Requires-Dist: packaging>=24.2; extra == "test"
+Requires-Dist: jaraco.envs>=2.2; extra == "test"
+Requires-Dist: pytest-xdist>=3; extra == "test"
+Requires-Dist: jaraco.path>=3.7.2; extra == "test"
+Requires-Dist: build[virtualenv]>=1.0.3; extra == "test"
+Requires-Dist: filelock>=3.4.0; extra == "test"
+Requires-Dist: ini2toml[lite]>=0.14; extra == "test"
+Requires-Dist: tomli-w>=1.0.0; extra == "test"
+Requires-Dist: pytest-timeout; extra == "test"
+Requires-Dist: pytest-perf; sys_platform != "cygwin" and extra == "test"
+Requires-Dist: jaraco.develop>=7.21; (python_version >= "3.9" and sys_platform != "cygwin") and extra == "test"
+Requires-Dist: pytest-home>=0.5; extra == "test"
+Requires-Dist: pytest-subprocess; extra == "test"
+Requires-Dist: pyproject-hooks!=1.1; extra == "test"
+Requires-Dist: jaraco.test>=5.5; extra == "test"
+Provides-Extra: doc
+Requires-Dist: sphinx>=3.5; extra == "doc"
+Requires-Dist: jaraco.packaging>=9.3; extra == "doc"
+Requires-Dist: rst.linker>=1.9; extra == "doc"
+Requires-Dist: furo; extra == "doc"
+Requires-Dist: sphinx-lint; extra == "doc"
+Requires-Dist: jaraco.tidelift>=1.4; extra == "doc"
+Requires-Dist: pygments-github-lexers==0.0.5; extra == "doc"
+Requires-Dist: sphinx-favicon; extra == "doc"
+Requires-Dist: sphinx-inline-tabs; extra == "doc"
+Requires-Dist: sphinx-reredirects; extra == "doc"
+Requires-Dist: sphinxcontrib-towncrier; extra == "doc"
+Requires-Dist: sphinx-notfound-page<2,>=1; extra == "doc"
+Requires-Dist: pyproject-hooks!=1.1; extra == "doc"
+Requires-Dist: towncrier<24.7; extra == "doc"
+Provides-Extra: ssl
+Provides-Extra: certs
+Provides-Extra: core
+Requires-Dist: packaging>=24.2; extra == "core"
+Requires-Dist: more_itertools>=8.8; extra == "core"
+Requires-Dist: jaraco.text>=3.7; extra == "core"
+Requires-Dist: importlib_metadata>=6; python_version < "3.10" and extra == "core"
+Requires-Dist: tomli>=2.0.1; python_version < "3.11" and extra == "core"
+Requires-Dist: wheel>=0.43.0; extra == "core"
+Requires-Dist: platformdirs>=4.2.2; extra == "core"
+Requires-Dist: jaraco.functools>=4; extra == "core"
+Requires-Dist: more_itertools; extra == "core"
+Provides-Extra: check
+Requires-Dist: pytest-checkdocs>=2.4; extra == "check"
+Requires-Dist: pytest-ruff>=0.2.1; sys_platform != "cygwin" and extra == "check"
+Requires-Dist: ruff>=0.8.0; sys_platform != "cygwin" and extra == "check"
+Provides-Extra: cover
+Requires-Dist: pytest-cov; extra == "cover"
+Provides-Extra: enabler
+Requires-Dist: pytest-enabler>=2.2; extra == "enabler"
+Provides-Extra: type
+Requires-Dist: pytest-mypy; extra == "type"
+Requires-Dist: mypy==1.14.*; extra == "type"
+Requires-Dist: importlib_metadata>=7.0.2; python_version < "3.10" and extra == "type"
+Requires-Dist: jaraco.develop>=7.21; sys_platform != "cygwin" and extra == "type"
+Dynamic: license-file
+
+.. |pypi-version| image:: https://img.shields.io/pypi/v/setuptools.svg
+   :target: https://pypi.org/project/setuptools
+
+.. |py-version| image:: https://img.shields.io/pypi/pyversions/setuptools.svg
+
+.. |test-badge| image:: https://github.com/pypa/setuptools/actions/workflows/main.yml/badge.svg
+   :target: https://github.com/pypa/setuptools/actions?query=workflow%3A%22tests%22
+   :alt: tests
+
+.. |ruff-badge| image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json
+   :target: https://github.com/astral-sh/ruff
+   :alt: Ruff
+
+.. |docs-badge| image:: https://img.shields.io/readthedocs/setuptools/latest.svg
+   :target: https://setuptools.pypa.io
+
+.. |skeleton-badge| image:: https://img.shields.io/badge/skeleton-2025-informational
+   :target: https://blog.jaraco.com/skeleton
+
+.. |codecov-badge| image:: https://img.shields.io/codecov/c/github/pypa/setuptools/master.svg?logo=codecov&logoColor=white
+   :target: https://codecov.io/gh/pypa/setuptools
+
+.. |tidelift-badge| image:: https://tidelift.com/badges/github/pypa/setuptools?style=flat
+   :target: https://tidelift.com/subscription/pkg/pypi-setuptools?utm_source=pypi-setuptools&utm_medium=readme
+
+.. |discord-badge| image:: https://img.shields.io/discord/803025117553754132
+   :target: https://discord.com/channels/803025117553754132/815945031150993468
+   :alt: Discord
+
+|pypi-version| |py-version| |test-badge| |ruff-badge| |docs-badge| |skeleton-badge| |codecov-badge| |discord-badge|
+
+See the `Quickstart `_
+and the `User's Guide `_ for
+instructions on how to use Setuptools.
+
+Questions and comments should be directed to `GitHub Discussions
+`_.
+Bug reports and especially tested patches may be
+submitted directly to the `bug tracker
+`_.
+
+
+Code of Conduct
+===============
+
+Everyone interacting in the setuptools project's codebases, issue trackers,
+chat rooms, and fora is expected to follow the
+`PSF Code of Conduct `_.
+
+
+For Enterprise
+==============
+
+Available as part of the Tidelift Subscription.
+
+Setuptools and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use.
+
+`Learn more `_.
diff --git a/.venv/lib/python3.12/site-packages/setuptools-80.9.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/setuptools-80.9.0.dist-info/RECORD
new file mode 100644
index 0000000000000000000000000000000000000000..ace9bab9ce7a5cf4ae4f0feb0e8e119d0bcb4898
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools-80.9.0.dist-info/RECORD
@@ -0,0 +1,514 @@
+_distutils_hack/__init__.py,sha256=34HmvLo07j45Uvd2VR-2aRQ7lJD91sTK6zJgn5fphbQ,6755
+_distutils_hack/override.py,sha256=Eu_s-NF6VIZ4Cqd0tbbA5wtWky2IZPNd8et6GLt1mzo,44
+distutils-precedence.pth,sha256=JjjOniUA5XKl4N5_rtZmHrVp0baW_LoHsN0iPaX10iQ,151
+pkg_resources/__init__.py,sha256=uxrWmKF3lxsG4q6ojHlu4tB8j8Kw9jqx_SNMyDKP5q4,126219
+pkg_resources/api_tests.txt,sha256=XEdvy4igHHrq2qNHNMHnlfO6XSQKNqOyLHbl6QcpfAI,12595
+pkg_resources/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pkg_resources/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pkg_resources/tests/data/my-test-package-source/setup.cfg,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pkg_resources/tests/data/my-test-package-source/setup.py,sha256=1VobhAZbMb7M9mfhb_NE8PwDsvukoWLs9aUAS0pYhe8,105
+pkg_resources/tests/data/my-test-package-zip/my-test-package.zip,sha256=AYRcQ39GVePPnMT8TknP1gdDHyJnXhthESmpAjnzSCI,1809
+pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/PKG-INFO,sha256=JvWv9Io2PAuYwEEw2fBW4Qc5YvdbkscpKX1kmLzsoHk,187
+pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/SOURCES.txt,sha256=4ClkH8eTovZrdVrJFsVuxdbMEF--lBVSuKonDAPE5Jc,208
+pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/dependency_links.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
+pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/top_level.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
+pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
+pkg_resources/tests/data/my-test-package_zipped-egg/my_test_package-1.0-py3.7.egg,sha256=ZTlMGxjRGiKDNkiA2c75jbQH2TWIteP00irF9gvczbo,843
+pkg_resources/tests/test_find_distributions.py,sha256=U91cov5L1COAIWLNq3Xy4plU7_MnOE1WtXMu6iV2waM,1972
+pkg_resources/tests/test_integration_zope_interface.py,sha256=nzVoK557KZQN0V3DIQ1sVeaCOgt4Kpl-CODAWsO7pmc,1652
+pkg_resources/tests/test_markers.py,sha256=0orKg7UMDf7fnuNQvRMOc-EF9EAP_JTQnk4mtGgbW50,241
+pkg_resources/tests/test_pkg_resources.py,sha256=5Mt4bJQhLCL8j8cC46Uv32Np2Xc1TTxLGawIfET55Fk,17111
+pkg_resources/tests/test_resources.py,sha256=K0LqMAUGpRQ9pUb9K0vyI7GesvtlQvTH074m-E2VQlo,31252
+pkg_resources/tests/test_working_set.py,sha256=lRtGJWIixSwSMSbjHgRxeJEQiLMRXcz3xzJL2qL7eXY,8602
+setuptools-80.9.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+setuptools-80.9.0.dist-info/METADATA,sha256=f4kMqNvBa2O1aMiTEu1qf58KedCyt_PIO_eWrD2TBhU,6572
+setuptools-80.9.0.dist-info/RECORD,,
+setuptools-80.9.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools-80.9.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
+setuptools-80.9.0.dist-info/entry_points.txt,sha256=zkgthpf_Fa9NVE9p6FKT3Xk9DR1faAcRU4coggsV7jA,2449
+setuptools-80.9.0.dist-info/licenses/LICENSE,sha256=htoPAa6uRjSKPD1GUZXcHOzN55956HdppkuNoEsqR0E,1023
+setuptools-80.9.0.dist-info/top_level.txt,sha256=d9yL39v_W7qmKDDSH6sT4bE0j_Ls1M3P161OGgdsm4g,41
+setuptools/__init__.py,sha256=7duacBaImxzrUa0OghIz8lVgaJ1fIw7-uL2v16vY_SE,9004
+setuptools/_core_metadata.py,sha256=T7Tjp-WSoN881adev3R1wzXCPnkDHqbC2MgylN1yjS8,11978
+setuptools/_discovery.py,sha256=HxEpgYQ8zUaLOOSp8JIA4y2Mdvn9ysVxspPT-ppfzM4,836
+setuptools/_distutils/__init__.py,sha256=xGYuhWwLG07J0Q49BVnEjPy6wyDcd6veJMDJX7ljlyM,359
+setuptools/_distutils/_log.py,sha256=i-lNTTcXS8TmWITJ6DODGvtW5z5tMattJQ76h8rZxQU,42
+setuptools/_distutils/_macos_compat.py,sha256=JzUGhF4E5yIITHbUaPobZEWjGHdrrcNV63z86S4RjBc,239
+setuptools/_distutils/_modified.py,sha256=RF1n1CexyDYV3lvGbeXS0s-XCJVboDOIUbA8wEQqYTY,3211
+setuptools/_distutils/_msvccompiler.py,sha256=9PSfSHxvJnHnQL6Sqz4Xcz7iaBIT62p6BheQzGsSlwo,335
+setuptools/_distutils/archive_util.py,sha256=Qw2z-Pt-NV8lNUQrzjs3XDGWCWHMPnqHLyt8TiD2XEA,8884
+setuptools/_distutils/ccompiler.py,sha256=FKVjqzGJ7c-FtouNjhLiaMPm5LKMZHHAruXf8LU216c,524
+setuptools/_distutils/cmd.py,sha256=hXtaRaH7QBnfNOIqEvCt47iwZzD9MVvBdhhdQctHsxM,22186
+setuptools/_distutils/command/__init__.py,sha256=GfFAzbBqk1qxSH4BdaKioKS4hRRnD44BAmwEN85C4u8,386
+setuptools/_distutils/command/_framework_compat.py,sha256=0iZdSJYzGRWCCvzRDKE-R0-_yaAYvFMd1ylXb2eYXug,1609
+setuptools/_distutils/command/bdist.py,sha256=jWtk61R7fWNUUNxJV0thTZzU5n80L3Ay1waSiP9kiLA,5854
+setuptools/_distutils/command/bdist_dumb.py,sha256=Hx1jAqoZNxYIy4N5TLzUp6J5fi8Ls18py7UlLNFhO2E,4631
+setuptools/_distutils/command/bdist_rpm.py,sha256=nxcXXv5a7B-1ntWu4DbGmCtES4EBINrJaBQcRNAYCJI,21785
+setuptools/_distutils/command/build.py,sha256=SpHlagf0iNaKVyIhxDfhPFZ8X1-LAWOCQACy-yt2K0w,5923
+setuptools/_distutils/command/build_clib.py,sha256=aMqZcUfCbOAu_xr-A9iW-Q9YZHzpDGLRTezOgMQJmSQ,7777
+setuptools/_distutils/command/build_ext.py,sha256=zrrsu9HXnzV6bXYbJuZCK4SwVZMjKnl4pG1o3bNcxtc,32710
+setuptools/_distutils/command/build_py.py,sha256=Vfq-INemoMbg6f003BTy_Ufp8bjOZhmFIhpKMcfXLgs,16696
+setuptools/_distutils/command/build_scripts.py,sha256=emMEOONkNLPC-AMjKy45UksUlY1wk06esOGThpwidIM,5135
+setuptools/_distutils/command/check.py,sha256=yoNe2MPY4JcTM7rwoIQdfZ75q5Ri058I2coi-Gq9CjM,4946
+setuptools/_distutils/command/clean.py,sha256=dQAacOabwBXU9JoZ-1GFusq3eFltDaeXJFSYncqGbvE,2644
+setuptools/_distutils/command/config.py,sha256=qrrfz6NEQORmbqiY2XlvCDWYhsbLyxZXJsURKfYN_kw,12724
+setuptools/_distutils/command/install.py,sha256=-JenB-mua4hc2RI_-W8F9PnP_J-OaFO7E0PJGKxLo1o,30072
+setuptools/_distutils/command/install_data.py,sha256=GzBlUWWKubTYJlP-L0auUriq9cL-5RKOcoyHTttKj0Q,2875
+setuptools/_distutils/command/install_egg_info.py,sha256=ffiLoU1ivQJ8q2_WL7ZygZbUcOsgdFLKL7otEIJWWkI,2868
+setuptools/_distutils/command/install_headers.py,sha256=5ciKCj8c3XKsYNKdkdMvnypaUCKcoWCDeeZij3fD-Z4,1272
+setuptools/_distutils/command/install_lib.py,sha256=2s9-m5-b1qKm51F28lB5L39Z6vv_GHMlv9dNBSupok0,8588
+setuptools/_distutils/command/install_scripts.py,sha256=M0pPdiaqB7TGmqTMujpGGeiL0Iq_CTeGjMFtrmDmwzM,2002
+setuptools/_distutils/command/sdist.py,sha256=cRIF6Ht1hJ6ayOOFVycMFBUNxjo94e_rFYPx4Hi8Ahc,19151
+setuptools/_distutils/compat/__init__.py,sha256=J20aXGjJ86Rg41xFLIWlcWCgZ9edMdJ9vvdNEQ87vPQ,522
+setuptools/_distutils/compat/numpy.py,sha256=UFgneZw9w97g4c-yGoAIOyLxUOWQ-fPRIhhfMs7_Ouc,167
+setuptools/_distutils/compat/py39.py,sha256=hOsD6lwZLqZoMnacNJ3P6nUA-LJQhEpVtYTzVH0o96M,1964
+setuptools/_distutils/compilers/C/base.py,sha256=XR1rBCStCquqm7QOYXD41-LfvsFcPpGxrwxeXzJyn_w,54876
+setuptools/_distutils/compilers/C/cygwin.py,sha256=DUlwQSb55aj7OdcmcddrmCmVEjEaxIiJ5hHUO3GBPNs,11844
+setuptools/_distutils/compilers/C/errors.py,sha256=sKOVzJajMUmNdfywo9UM_QQGsKFcclDhtI5TlCiXMLc,573
+setuptools/_distutils/compilers/C/msvc.py,sha256=elzG8v9jN5QytLMwLCdUdSuZ3eZ3R98VUvnm9Y2PBCA,21404
+setuptools/_distutils/compilers/C/tests/test_base.py,sha256=rdhHc56bhXtm5NnN9BSHwr6c69UqzMItZQzlw2AsdMc,2706
+setuptools/_distutils/compilers/C/tests/test_cygwin.py,sha256=UgV2VgUXj3VulcbDc0UBWfEyJDx42tgSwS4LzHix3mY,2701
+setuptools/_distutils/compilers/C/tests/test_mingw.py,sha256=hCmwyywISpRoyOySbFHBL4TprWRV0mUWDKmOLO8XBXE,1900
+setuptools/_distutils/compilers/C/tests/test_msvc.py,sha256=DlGjmZ1mBSMXIgmlu80BKc7V-EJOZuYucwJwFh5dn28,4151
+setuptools/_distutils/compilers/C/tests/test_unix.py,sha256=AyadWw1fR-UeDl2TvIbYBzOJVHkpE_oRRQ3JTJWqaEA,14642
+setuptools/_distutils/compilers/C/unix.py,sha256=YH-y9g_pjBFjaJyHJQkDEBQ7q4D20I2-cWJNdgw-Yho,16531
+setuptools/_distutils/compilers/C/zos.py,sha256=vnNeWLRZkdIkdZ-YyBnL8idTUfcCOn0tLMW5OBJ0ScU,6586
+setuptools/_distutils/core.py,sha256=GEHKaFC48T3o-_SmH4864GvKyx1IgbVC6ISIPVlx7a4,9364
+setuptools/_distutils/cygwinccompiler.py,sha256=mG_cU8SVZ4amD_VtF5vH6BXP0-kghGsDPbDSXrQ963c,594
+setuptools/_distutils/debug.py,sha256=N6MrTAqK6l9SVk6tWweR108PM8Ol7qNlfyV-nHcLhsY,139
+setuptools/_distutils/dep_util.py,sha256=xN75p6ZpHhMiHEc-rpL2XilJQynHnDNiafHteaZ4tjU,349
+setuptools/_distutils/dir_util.py,sha256=DXPUlfVVGsg9B-Jgg4At_j9T7vM60OgwNXkQHqTo7-I,7236
+setuptools/_distutils/dist.py,sha256=gW598UE0WMkzXQQ31Nr-8L7MPw0oIOz5OSSRzYZlwrM,55794
+setuptools/_distutils/errors.py,sha256=PPE2oDRh5y9Q1beKK9rhdvDaCzQhi4HCXs4KcqfqgZY,3092
+setuptools/_distutils/extension.py,sha256=Foyu4gULcPqm1_U9zrYYHxNk4NqglXv1rbsOk_QrSds,11155
+setuptools/_distutils/fancy_getopt.py,sha256=PjdO-bWCW0imV_UN-MGEw9R2GP2OiE8pHjITgmTAY3Q,17895
+setuptools/_distutils/file_util.py,sha256=YFQL_pD3hLuER9II_H6-hDC_YIGEookdd4wedLuiTW0,7978
+setuptools/_distutils/filelist.py,sha256=MBeSRJmPcKmDv8ooZgSU4BiQPZ0Khwv8l_jhD50XycI,15337
+setuptools/_distutils/log.py,sha256=VyBs5j7z4-K6XTEEBThUc9HyMpoPLGtQpERqbz5ylww,1200
+setuptools/_distutils/spawn.py,sha256=zseCh9sEifyp0I5Vg719JNIASlROJ2ehXqQnHlpt89Q,4086
+setuptools/_distutils/sysconfig.py,sha256=KeI8OHbMuEzHJ8Q0cBez9KZny8iRy6Z6Y0AkMz1jlsU,19728
+setuptools/_distutils/tests/__init__.py,sha256=j-IoPZEtQv3EOPuqNTwalr6GLyRjzCC-OOaNvZzmHsI,1485
+setuptools/_distutils/tests/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_distutils/tests/compat/py39.py,sha256=t0GBTM-30jX-9zCfkwlNBFtzzabemx6065mJ0d9_VRw,1026
+setuptools/_distutils/tests/support.py,sha256=tjsYsyxvpTK4NrkCseh2ujvDIGV0Mf_b5SI5fP2T0yM,4099
+setuptools/_distutils/tests/test_archive_util.py,sha256=jozimSwPBF-JoJfN_vDaiVGZp66BNcWZGh34FlW57DQ,11787
+setuptools/_distutils/tests/test_bdist.py,sha256=xNHxUsLlHsZQRwkzLb_iSD24s-9Mk-NX2ffBWwOyPyc,1396
+setuptools/_distutils/tests/test_bdist_dumb.py,sha256=QF05MHNhPOdZyh88Xpw8KsO64s7pRFkl8KL-RoV4XK0,2247
+setuptools/_distutils/tests/test_bdist_rpm.py,sha256=Hdm-pwWgyaoGdGbEcGZa8cRhGU45y8gHK8umOanTjik,3932
+setuptools/_distutils/tests/test_build.py,sha256=JJY5XpOZco25ZY0pstxl-iI8mHsWP0ujf5o8aOtuZYY,1742
+setuptools/_distutils/tests/test_build_clib.py,sha256=Mo1ZFb4C1VXBYOGvnallwN7YCnTtr24akLDO8Zi4CsY,4331
+setuptools/_distutils/tests/test_build_ext.py,sha256=QFO9qYVhWWdJu17HXc4x9RMnLZlhk0lAHi9HVppbuX4,22545
+setuptools/_distutils/tests/test_build_py.py,sha256=NsfmRrojOHBXNMqWR_mp5g4PLTgjhD7iZFUffGZFIdw,6882
+setuptools/_distutils/tests/test_build_scripts.py,sha256=cD-FRy-oX55sXRX5Ez5xQCaeHrWajyKc4Xuwv2fe48w,2880
+setuptools/_distutils/tests/test_check.py,sha256=hHSV07qf7YoSxGsTbbsUQ9tssZz5RRNdbrY1s2SwaFI,6226
+setuptools/_distutils/tests/test_clean.py,sha256=hPH6jfIpGFUrvWbF1txkiNVSNaAxt2wq5XjV499zO4E,1240
+setuptools/_distutils/tests/test_cmd.py,sha256=bgRB79mitoOKR1OiyZHnCogvGxt3pWkxeTqIC04lQWQ,3254
+setuptools/_distutils/tests/test_config_cmd.py,sha256=Zs6WX0IfxDvmuC19XzuVNnYCnTr9Y-hl73TAmDSBN4Y,2664
+setuptools/_distutils/tests/test_core.py,sha256=L7XKVAxa-MGoAZeANopnuK9fRKneYhkSQpgw8XQvcF8,3829
+setuptools/_distutils/tests/test_dir_util.py,sha256=E84lC-k4riVUwURyWaQ0Jqx2ui2-io-0RuJa3M7qkJs,4500
+setuptools/_distutils/tests/test_dist.py,sha256=a6wlc5fQJd5qQ6HOndzcupNhjTxvj6-_JLtpuYvaP1M,18793
+setuptools/_distutils/tests/test_extension.py,sha256=-YejLgZCuycFrOBd64pVH0JvwMc9NwhzHvQxvvjXHqk,3670
+setuptools/_distutils/tests/test_file_util.py,sha256=livjnl3FkilQlrB2rFdFQq9nvjEVZHynNya0bfzv_b4,3522
+setuptools/_distutils/tests/test_filelist.py,sha256=rJwkqCUfkGDgWlD22TozsT8ycbupMHB8DXqThzwT1T4,10766
+setuptools/_distutils/tests/test_install.py,sha256=TfCB0ykhIxydIC2Q4SuTAZzSHvteMHgrBL9whoSgK9Q,8618
+setuptools/_distutils/tests/test_install_data.py,sha256=vKq3K97k0hBAnOg38nmwEdf7cEDVr9rTVyCeJolgb4A,2464
+setuptools/_distutils/tests/test_install_headers.py,sha256=PVAYpo_tYl980Qf64DPOmmSvyefIHdU06f7VsJeZykE,936
+setuptools/_distutils/tests/test_install_lib.py,sha256=qri6Rl-maNTQrNDV8DbeXNl0hjsfRIKiI4rfZLrmWBI,3612
+setuptools/_distutils/tests/test_install_scripts.py,sha256=KE3v0cDkFW-90IOID-OmZZGM2mhy-ZkEuuW7UXS2SHw,1600
+setuptools/_distutils/tests/test_log.py,sha256=isFtOufloCyEdZaQOV7cVUr46GwtdVMj43mGBB5XH7k,323
+setuptools/_distutils/tests/test_modified.py,sha256=h1--bOWmtJo1bpVV6uRhdnS9br71CBiNDM1MDwSGpug,4221
+setuptools/_distutils/tests/test_sdist.py,sha256=cfzUhlCA418-1vH9ta3IBs26c_jUBbkJoFOK5GnAyNk,15062
+setuptools/_distutils/tests/test_spawn.py,sha256=eS8w9D7bTxyFLSyRahJWeuh8Kc1F8RWWiY_dSG5B5Bc,4803
+setuptools/_distutils/tests/test_sysconfig.py,sha256=lxM8LsUi1TomjDV4HoYK8u5nUoBkeNL60Uq8PY1DcwU,11986
+setuptools/_distutils/tests/test_text_file.py,sha256=WQWSB5AfdBDZaMA8BFgipJPnsJb_2SKMfL90fSkRVtw,3460
+setuptools/_distutils/tests/test_util.py,sha256=H9zlZ4z4Vh4TfjNYDBsxP7wguQLpxCfJYyOcm1yZU3c,7988
+setuptools/_distutils/tests/test_version.py,sha256=ahfg_mP8wRy1sgwY-_Px5hrjgf6_upTIpnCgpR4yWRk,2750
+setuptools/_distutils/tests/test_versionpredicate.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_distutils/tests/unix_compat.py,sha256=z-op6C2iVdX1aq5BIBR7cqOxijKE97alNwJqHNdLpoI,386
+setuptools/_distutils/text_file.py,sha256=z4dkOJBr9Bo2LG0TNqm8sD63LEEaKSSP0J0bWBrFG3c,12101
+setuptools/_distutils/unixccompiler.py,sha256=1bXJWH4fiu_A2WfriHzf88xjllQTXnnjUkZdRKs9cWU,212
+setuptools/_distutils/util.py,sha256=Njfnqk60zMdkiAjRnGcTWX3t49-obHapOlbNvyIl02I,18094
+setuptools/_distutils/version.py,sha256=vImT5-ECXkQ21oKL0XYFiTqK6NyM09cpzBNoA_34CQU,12619
+setuptools/_distutils/versionpredicate.py,sha256=qBWQ6wTj12ODytoTmIydefIY2jb4uY1sdbgbuLn-IJM,5205
+setuptools/_distutils/zosccompiler.py,sha256=svdiXZ2kdcwKrJKfhUhib03y8gz7aGZKukXH3I7YkBc,58
+setuptools/_entry_points.py,sha256=10TjbzfGdqWGH06lQuPPGDDci-OnXoIzrfpIWba5AZw,2468
+setuptools/_imp.py,sha256=YY1EjZEN-0zYci1cxO10B_adAEOr7i8eK8JoCc9Ierc,2435
+setuptools/_importlib.py,sha256=aKIjcK0HKXNz2D-XTrxaixGn_juTkONwmu3dcheMOF0,223
+setuptools/_itertools.py,sha256=jWRfsIrpC7myooz3hDURj9GtvpswZeKXg2HakmEhNjo,657
+setuptools/_normalization.py,sha256=-SxdhisW3W1JKzqKYxd3XeHyRjIj3J9EVRkt3aL8nKY,5747
+setuptools/_path.py,sha256=2Bv1q9_Hrd4oizKwcH3pv_05YVR6meovQE6ZtyD45yI,2976
+setuptools/_reqs.py,sha256=RRX-qYsz_fy6K66XchCHcIszK3bSAtU6aO1s3ZaLV14,1380
+setuptools/_scripts.py,sha256=5TrIWDVOuO3cRcfzcZAUBKPRH7K4svQRdQLZKKiD1bQ,11247
+setuptools/_shutil.py,sha256=IQQ1gcPX4X_wPilYGJGxChyMCqG43VOejoQZTIrCTY8,1578
+setuptools/_static.py,sha256=GTR79gESF1_JaK4trLkpDrEuCeEtPlwQW0MRv7VNQX4,4855
+setuptools/_vendor/autocommand-2.2.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+setuptools/_vendor/autocommand-2.2.2.dist-info/LICENSE,sha256=reeNBJgtaZctREqOFKlPh6IzTdOFXMgDSOqOJAqg3y0,7634
+setuptools/_vendor/autocommand-2.2.2.dist-info/METADATA,sha256=OADZuR3O6iBlpu1ieTgzYul6w4uOVrk0P0BO5TGGAJk,15006
+setuptools/_vendor/autocommand-2.2.2.dist-info/RECORD,sha256=giu6ZrQVJvpUcYa4AiH4XaUNZSvuVJPb_l0UCFES8MM,1308
+setuptools/_vendor/autocommand-2.2.2.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
+setuptools/_vendor/autocommand-2.2.2.dist-info/top_level.txt,sha256=AzfhgKKS8EdAwWUTSF8mgeVQbXOY9kokHB6kSqwwqu0,12
+setuptools/_vendor/autocommand/__init__.py,sha256=zko5Rnvolvb-UXjCx_2ArPTGBWwUK5QY4LIQIKYR7As,1037
+setuptools/_vendor/autocommand/autoasync.py,sha256=AMdyrxNS4pqWJfP_xuoOcImOHWD-qT7x06wmKN1Vp-U,5680
+setuptools/_vendor/autocommand/autocommand.py,sha256=hmkEmQ72HtL55gnURVjDOnsfYlGd5lLXbvT4KG496Qw,2505
+setuptools/_vendor/autocommand/automain.py,sha256=A2b8i754Mxc_DjU9WFr6vqYDWlhz0cn8miu8d8EsxV8,2076
+setuptools/_vendor/autocommand/autoparse.py,sha256=WVWmZJPcbzUKXP40raQw_0HD8qPJ2V9VG1eFFmmnFxw,11642
+setuptools/_vendor/autocommand/errors.py,sha256=7aa3roh9Herd6nIKpQHNWEslWE8oq7GiHYVUuRqORnA,886
+setuptools/_vendor/backports.tarfile-1.2.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+setuptools/_vendor/backports.tarfile-1.2.0.dist-info/LICENSE,sha256=htoPAa6uRjSKPD1GUZXcHOzN55956HdppkuNoEsqR0E,1023
+setuptools/_vendor/backports.tarfile-1.2.0.dist-info/METADATA,sha256=ghXFTq132dxaEIolxr3HK1mZqm9iyUmaRANZQSr6WlE,2020
+setuptools/_vendor/backports.tarfile-1.2.0.dist-info/RECORD,sha256=JYofHISeEXUGmlWl1s41ev3QTjTNXeJwk-Ss7HqdLOE,1360
+setuptools/_vendor/backports.tarfile-1.2.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/backports.tarfile-1.2.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
+setuptools/_vendor/backports.tarfile-1.2.0.dist-info/top_level.txt,sha256=cGjaLMOoBR1FK0ApojtzWVmViTtJ7JGIK_HwXiEsvtU,10
+setuptools/_vendor/backports/__init__.py,sha256=iOEMwnlORWezdO8-2vxBIPSR37D7JGjluZ8f55vzxls,81
+setuptools/_vendor/backports/tarfile/__init__.py,sha256=Pwf2qUIfB0SolJPCKcx3vz3UEu_aids4g4sAfxy94qg,108491
+setuptools/_vendor/backports/tarfile/__main__.py,sha256=Yw2oGT1afrz2eBskzdPYL8ReB_3liApmhFkN2EbDmc4,59
+setuptools/_vendor/backports/tarfile/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/backports/tarfile/compat/py38.py,sha256=iYkyt_gvWjLzGUTJD9TuTfMMjOk-ersXZmRlvQYN2qE,568
+setuptools/_vendor/importlib_metadata-8.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+setuptools/_vendor/importlib_metadata-8.0.0.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
+setuptools/_vendor/importlib_metadata-8.0.0.dist-info/METADATA,sha256=anuQ7_7h4J1bSEzfcjIBakPi2cyVQ7y7jklLHsBeH1k,4648
+setuptools/_vendor/importlib_metadata-8.0.0.dist-info/RECORD,sha256=DY08buueu-hsrH1ghhVSQzwynanqUSSLYdAr4uXmQDA,2518
+setuptools/_vendor/importlib_metadata-8.0.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/importlib_metadata-8.0.0.dist-info/WHEEL,sha256=mguMlWGMX-VHnMpKOjjQidIo1ssRlCFu4a4mBpz1s2M,91
+setuptools/_vendor/importlib_metadata-8.0.0.dist-info/top_level.txt,sha256=CO3fD9yylANiXkrMo4qHLV_mqXL2sC5JFKgt1yWAT-A,19
+setuptools/_vendor/importlib_metadata/__init__.py,sha256=tZNB-23h8Bixi9uCrQqj9Yf0aeC--Josdy3IZRIQeB0,33798
+setuptools/_vendor/importlib_metadata/_adapters.py,sha256=rIhWTwBvYA1bV7i-5FfVX38qEXDTXFeS5cb5xJtP3ks,2317
+setuptools/_vendor/importlib_metadata/_collections.py,sha256=CJ0OTCHIjWA0ZIVS4voORAsn2R4R2cQBEtPsZEJpASY,743
+setuptools/_vendor/importlib_metadata/_compat.py,sha256=73QKrN9KNoaZzhbX5yPCCZa-FaALwXe8TPlDR72JgBU,1314
+setuptools/_vendor/importlib_metadata/_functools.py,sha256=PsY2-4rrKX4RVeRC1oGp1lB1pmC9eKN88_f-bD9uOoA,2895
+setuptools/_vendor/importlib_metadata/_itertools.py,sha256=cvr_2v8BRbxcIl5x5ldfqdHjhI8Yi8s8yk50G_nm6jQ,2068
+setuptools/_vendor/importlib_metadata/_meta.py,sha256=nxZ7C8GVlcBFAKWyVOn_dn7ot_twBcbm1NmvjIetBHI,1801
+setuptools/_vendor/importlib_metadata/_text.py,sha256=HCsFksZpJLeTP3NEk_ngrAeXVRRtTrtyh9eOABoRP4A,2166
+setuptools/_vendor/importlib_metadata/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/importlib_metadata/compat/py311.py,sha256=uqm-K-uohyj1042TH4a9Er_I5o7667DvulcD-gC_fSA,608
+setuptools/_vendor/importlib_metadata/compat/py39.py,sha256=cPkMv6-0ilK-0Jw_Tkn0xYbOKJZc4WJKQHow0c2T44w,1102
+setuptools/_vendor/importlib_metadata/diagnose.py,sha256=nkSRMiowlmkhLYhKhvCg9glmt_11Cox-EmLzEbqYTa8,379
+setuptools/_vendor/importlib_metadata/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/inflect-7.3.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+setuptools/_vendor/inflect-7.3.1.dist-info/LICENSE,sha256=htoPAa6uRjSKPD1GUZXcHOzN55956HdppkuNoEsqR0E,1023
+setuptools/_vendor/inflect-7.3.1.dist-info/METADATA,sha256=ZgMNY0WAZRs-U8wZiV2SMfjSKqBrMngXyDMs_CAwMwg,21079
+setuptools/_vendor/inflect-7.3.1.dist-info/RECORD,sha256=XXg0rBuiYSxoAQUP3lenuYsPNqz4jDwtTzdv2JEbMJE,943
+setuptools/_vendor/inflect-7.3.1.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
+setuptools/_vendor/inflect-7.3.1.dist-info/top_level.txt,sha256=m52ujdp10CqT6jh1XQxZT6kEntcnv-7Tl7UiGNTzWZA,8
+setuptools/_vendor/inflect/__init__.py,sha256=Jxy1HJXZiZ85kHeLAhkmvz6EMTdFqBe-duvt34R6IOc,103796
+setuptools/_vendor/inflect/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/inflect/compat/py38.py,sha256=oObVfVnWX9_OpnOuEJn1mFbJxVhwyR5epbiTNXDDaso,160
+setuptools/_vendor/inflect/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/jaraco.collections-5.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+setuptools/_vendor/jaraco.collections-5.1.0.dist-info/LICENSE,sha256=htoPAa6uRjSKPD1GUZXcHOzN55956HdppkuNoEsqR0E,1023
+setuptools/_vendor/jaraco.collections-5.1.0.dist-info/METADATA,sha256=IMUaliNsA5X1Ox9MXUWOagch5R4Wwb_3M7erp29dBtg,3933
+setuptools/_vendor/jaraco.collections-5.1.0.dist-info/RECORD,sha256=HptivXDkpfom6VlMu4CGD_7KPev-6Hc9rvp3TNJZygY,873
+setuptools/_vendor/jaraco.collections-5.1.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/jaraco.collections-5.1.0.dist-info/WHEEL,sha256=Mdi9PDNwEZptOjTlUcAth7XJDFtKrHYaQMPulZeBCiQ,91
+setuptools/_vendor/jaraco.collections-5.1.0.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7
+setuptools/_vendor/jaraco.context-5.3.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+setuptools/_vendor/jaraco.context-5.3.0.dist-info/LICENSE,sha256=htoPAa6uRjSKPD1GUZXcHOzN55956HdppkuNoEsqR0E,1023
+setuptools/_vendor/jaraco.context-5.3.0.dist-info/METADATA,sha256=xDtguJej0tN9iEXCUvxEJh2a7xceIRVBEakBLSr__tY,4020
+setuptools/_vendor/jaraco.context-5.3.0.dist-info/RECORD,sha256=VRl7iKeEQyl7stgnp1uq50CzOJYlHYcoNdS0x17C9X4,641
+setuptools/_vendor/jaraco.context-5.3.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
+setuptools/_vendor/jaraco.context-5.3.0.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7
+setuptools/_vendor/jaraco.functools-4.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+setuptools/_vendor/jaraco.functools-4.0.1.dist-info/LICENSE,sha256=htoPAa6uRjSKPD1GUZXcHOzN55956HdppkuNoEsqR0E,1023
+setuptools/_vendor/jaraco.functools-4.0.1.dist-info/METADATA,sha256=i4aUaQDX-jjdEQK5wevhegyx8JyLfin2HyvaSk3FHso,2891
+setuptools/_vendor/jaraco.functools-4.0.1.dist-info/RECORD,sha256=YyqnwE98S8wBwCevW5vHb-iVj0oYEDW5V6O9MBS6JIs,843
+setuptools/_vendor/jaraco.functools-4.0.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
+setuptools/_vendor/jaraco.functools-4.0.1.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7
+setuptools/_vendor/jaraco.text-3.12.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+setuptools/_vendor/jaraco.text-3.12.1.dist-info/LICENSE,sha256=htoPAa6uRjSKPD1GUZXcHOzN55956HdppkuNoEsqR0E,1023
+setuptools/_vendor/jaraco.text-3.12.1.dist-info/METADATA,sha256=AzWdm6ViMfDOPoQMfLWn2zgBQSGJScyqeN29TcuWXVI,3658
+setuptools/_vendor/jaraco.text-3.12.1.dist-info/RECORD,sha256=gW2UV0HcokYJk4jKPu10_AZnrLqjb3C1WbJJTDl5sfY,1500
+setuptools/_vendor/jaraco.text-3.12.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/jaraco.text-3.12.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
+setuptools/_vendor/jaraco.text-3.12.1.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7
+setuptools/_vendor/jaraco/collections/__init__.py,sha256=Pc1-SqjWm81ad1P0-GttpkwO_LWlnaY6gUq8gcKh2v0,26640
+setuptools/_vendor/jaraco/collections/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/jaraco/context.py,sha256=REoLIxDkO5MfEYowt_WoupNCRoxBS5v7YX2PbW8lIcs,9552
+setuptools/_vendor/jaraco/functools/__init__.py,sha256=hEAJaS2uSZRuF_JY4CxCHIYh79ZpxaPp9OiHyr9EJ1w,16642
+setuptools/_vendor/jaraco/functools/__init__.pyi,sha256=gk3dsgHzo5F_U74HzAvpNivFAPCkPJ1b2-yCd62dfnw,3878
+setuptools/_vendor/jaraco/functools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/jaraco/text/Lorem ipsum.txt,sha256=N_7c_79zxOufBY9HZ3yzMgOkNv-TkOTTio4BydrSjgs,1335
+setuptools/_vendor/jaraco/text/__init__.py,sha256=Y2YUqXR_orUoDaY4SkPRe6ZZhb5HUHB_Ah9RCNsVyho,16250
+setuptools/_vendor/jaraco/text/layouts.py,sha256=HTC8aSTLZ7uXipyOXapRMC158juecjK6RVwitfmZ9_w,643
+setuptools/_vendor/jaraco/text/show-newlines.py,sha256=WGQa65e8lyhb92LUOLqVn6KaCtoeVgVws6WtSRmLk6w,904
+setuptools/_vendor/jaraco/text/strip-prefix.py,sha256=NfVXV8JVNo6nqcuYASfMV7_y4Eo8zMQqlCOGvAnRIVw,412
+setuptools/_vendor/jaraco/text/to-dvorak.py,sha256=1SNcbSsvISpXXg-LnybIHHY-RUFOQr36zcHkY1pWFqw,119
+setuptools/_vendor/jaraco/text/to-qwerty.py,sha256=s4UMQUnPwFn_dB5uZC27BurHOQcYondBfzIpVL5pEzw,119
+setuptools/_vendor/more_itertools-10.3.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+setuptools/_vendor/more_itertools-10.3.0.dist-info/LICENSE,sha256=CfHIyelBrz5YTVlkHqm4fYPAyw_QB-te85Gn4mQ8GkY,1053
+setuptools/_vendor/more_itertools-10.3.0.dist-info/METADATA,sha256=BFO90O-fLNiVQMpj7oIS5ztzgJUUQZ3TA32P5HH3N-A,36293
+setuptools/_vendor/more_itertools-10.3.0.dist-info/RECORD,sha256=d8jnPgGNwP1-ntbICwWkQEVF9kH7CFIgzkKzaLWao9M,1259
+setuptools/_vendor/more_itertools-10.3.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/more_itertools-10.3.0.dist-info/WHEEL,sha256=rSgq_JpHF9fHR1lx53qwg_1-2LypZE_qmcuXbVUq948,81
+setuptools/_vendor/more_itertools/__init__.py,sha256=dtAbGjTDmn_ghiU5YXfhyDy0phAlXVdt5klZA5fUa-Q,149
+setuptools/_vendor/more_itertools/__init__.pyi,sha256=5B3eTzON1BBuOLob1vCflyEb2lSd6usXQQ-Cv-hXkeA,43
+setuptools/_vendor/more_itertools/more.py,sha256=1E5kzFncRKTDw0cYv1yRXMgDdunstLQd1QStcnL6U90,148370
+setuptools/_vendor/more_itertools/more.pyi,sha256=iXXeqt48Nxe8VGmIWpkVXuKpR2FYNuu2DU8nQLWCCu0,21484
+setuptools/_vendor/more_itertools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/more_itertools/recipes.py,sha256=WedhhfhGVgr6zii8fIbGJVmRTw0ZKRiLKnYBDGJv4nY,28591
+setuptools/_vendor/more_itertools/recipes.pyi,sha256=T_mdGpcFdfrP3JSWbwzYP9JyNV-Go-7RPfpxfftAWlA,4617
+setuptools/_vendor/packaging-24.2.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+setuptools/_vendor/packaging-24.2.dist-info/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197
+setuptools/_vendor/packaging-24.2.dist-info/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174
+setuptools/_vendor/packaging-24.2.dist-info/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344
+setuptools/_vendor/packaging-24.2.dist-info/METADATA,sha256=ohH86s6k5mIfQxY2TS0LcSfADeOFa4BiCC-bxZV-pNs,3204
+setuptools/_vendor/packaging-24.2.dist-info/RECORD,sha256=Y4DrXM0KY0ArfzhbAEa1LYFPwW3WEgEeL4iCqXe-A-M,2009
+setuptools/_vendor/packaging-24.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/packaging-24.2.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
+setuptools/_vendor/packaging/__init__.py,sha256=dk4Ta_vmdVJxYHDcfyhvQNw8V3PgSBomKNXqg-D2JDY,494
+setuptools/_vendor/packaging/_elffile.py,sha256=cflAQAkE25tzhYmq_aCi72QfbT_tn891tPzfpbeHOwE,3306
+setuptools/_vendor/packaging/_manylinux.py,sha256=vl5OCoz4kx80H5rwXKeXWjl9WNISGmr4ZgTpTP9lU9c,9612
+setuptools/_vendor/packaging/_musllinux.py,sha256=p9ZqNYiOItGee8KcZFeHF_YcdhVwGHdK6r-8lgixvGQ,2694
+setuptools/_vendor/packaging/_parser.py,sha256=s_TvTvDNK0NrM2QB3VKThdWFM4Nc0P6JnkObkl3MjpM,10236
+setuptools/_vendor/packaging/_structures.py,sha256=q3eVNmbWJGG_S0Dit_S3Ao8qQqz_5PYTXFAKBZe5yr4,1431
+setuptools/_vendor/packaging/_tokenizer.py,sha256=J6v5H7Jzvb-g81xp_2QACKwO7LxHQA6ikryMU7zXwN8,5273
+setuptools/_vendor/packaging/licenses/__init__.py,sha256=1x5M1nEYjcgwEbLt0dXwz2ukjr18DiCzC0sraQqJ-Ww,5715
+setuptools/_vendor/packaging/licenses/_spdx.py,sha256=oAm1ztPFwlsmCKe7lAAsv_OIOfS1cWDu9bNBkeu-2ns,48398
+setuptools/_vendor/packaging/markers.py,sha256=c89TNzB7ZdGYhkovm6PYmqGyHxXlYVaLW591PHUNKD8,10561
+setuptools/_vendor/packaging/metadata.py,sha256=YJibM7GYe4re8-0a3OlXmGS-XDgTEoO4tlBt2q25Bng,34762
+setuptools/_vendor/packaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/packaging/requirements.py,sha256=gYyRSAdbrIyKDY66ugIDUQjRMvxkH2ALioTmX3tnL6o,2947
+setuptools/_vendor/packaging/specifiers.py,sha256=GG1wPNMcL0fMJO68vF53wKMdwnfehDcaI-r9NpTfilA,40074
+setuptools/_vendor/packaging/tags.py,sha256=CFqrJzAzc2XNGexerH__T-Y5Iwq7WbsYXsiLERLWxY0,21014
+setuptools/_vendor/packaging/utils.py,sha256=0F3Hh9OFuRgrhTgGZUl5K22Fv1YP2tZl1z_2gO6kJiA,5050
+setuptools/_vendor/packaging/version.py,sha256=olfyuk_DPbflNkJ4wBWetXQ17c74x3DB501degUv7DY,16676
+setuptools/_vendor/platformdirs-4.2.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+setuptools/_vendor/platformdirs-4.2.2.dist-info/METADATA,sha256=zmsie01G1MtXR0wgIv5XpVeTO7idr0WWvfmxKsKWuGk,11429
+setuptools/_vendor/platformdirs-4.2.2.dist-info/RECORD,sha256=TCEddtQu1A78Os_Mhm2JEqcYr7yit-UYSUQjZtbpn-g,1642
+setuptools/_vendor/platformdirs-4.2.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/platformdirs-4.2.2.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
+setuptools/_vendor/platformdirs-4.2.2.dist-info/licenses/LICENSE,sha256=KeD9YukphQ6G6yjD_czwzv30-pSHkBHP-z0NS-1tTbY,1089
+setuptools/_vendor/platformdirs/__init__.py,sha256=EMGE8qeHRR9CzDFr8kL3tA8hdZZniYjXBVZd0UGTWK0,22225
+setuptools/_vendor/platformdirs/__main__.py,sha256=HnsUQHpiBaiTxwcmwVw-nFaPdVNZtQIdi1eWDtI-MzI,1493
+setuptools/_vendor/platformdirs/android.py,sha256=xZXY9Jd46WOsxT2U6-5HsNtDZ-IQqxcEUrBLl3hYk4o,9016
+setuptools/_vendor/platformdirs/api.py,sha256=QBYdUac2eC521ek_y53uD1Dcq-lJX8IgSRVd4InC6uc,8996
+setuptools/_vendor/platformdirs/macos.py,sha256=wftsbsvq6nZ0WORXSiCrZNkRHz_WKuktl0a6mC7MFkI,5580
+setuptools/_vendor/platformdirs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/platformdirs/unix.py,sha256=Cci9Wqt35dAMsg6HT9nRGHSBW5obb0pR3AE1JJnsCXg,10643
+setuptools/_vendor/platformdirs/version.py,sha256=r7F76tZRjgQKzrpx_I0_ZMQOMU-PS7eGnHD7zEK3KB0,411
+setuptools/_vendor/platformdirs/windows.py,sha256=IFpiohUBwxPtCzlyKwNtxyW4Jk8haa6W8o59mfrDXVo,10125
+setuptools/_vendor/tomli-2.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+setuptools/_vendor/tomli-2.0.1.dist-info/LICENSE,sha256=uAgWsNUwuKzLTCIReDeQmEpuO2GSLCte6S8zcqsnQv4,1072
+setuptools/_vendor/tomli-2.0.1.dist-info/METADATA,sha256=zPDceKmPwJGLWtZykrHixL7WVXWmJGzZ1jyRT5lCoPI,8875
+setuptools/_vendor/tomli-2.0.1.dist-info/RECORD,sha256=DLn5pFGh42WsVLTIhmLh2gy1SnLRalJY-wq_-dPhwCI,999
+setuptools/_vendor/tomli-2.0.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/tomli-2.0.1.dist-info/WHEEL,sha256=jPMR_Dzkc4X4icQtmz81lnNY_kAsfog7ry7qoRvYLXw,81
+setuptools/_vendor/tomli/__init__.py,sha256=JhUwV66DB1g4Hvt1UQCVMdfCu-IgAV8FXmvDU9onxd4,396
+setuptools/_vendor/tomli/_parser.py,sha256=g9-ENaALS-B8dokYpCuzUFalWlog7T-SIYMjLZSWrtM,22633
+setuptools/_vendor/tomli/_re.py,sha256=dbjg5ChZT23Ka9z9DHOXfdtSpPwUfdgMXnj8NOoly-w,2943
+setuptools/_vendor/tomli/_types.py,sha256=-GTG2VUqkpxwMqzmVO4F7ybKddIbAnuAHXfmWQcTi3Q,254
+setuptools/_vendor/tomli/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26
+setuptools/_vendor/typeguard-4.3.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+setuptools/_vendor/typeguard-4.3.0.dist-info/LICENSE,sha256=YWP3mH37ONa8MgzitwsvArhivEESZRbVUu8c1DJH51g,1130
+setuptools/_vendor/typeguard-4.3.0.dist-info/METADATA,sha256=z2dcHAp0TwhYCFU5Deh8x31nazElgujUz9tbuP0pjSE,3717
+setuptools/_vendor/typeguard-4.3.0.dist-info/RECORD,sha256=SKUZWVgkeDUidUKM7s1473fXmsna55bjmi6vJUAoJVI,2402
+setuptools/_vendor/typeguard-4.3.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
+setuptools/_vendor/typeguard-4.3.0.dist-info/entry_points.txt,sha256=qp7NQ1aLtiSgMQqo6gWlfGpy0IIXzoMJmeQTLpzqFZQ,48
+setuptools/_vendor/typeguard-4.3.0.dist-info/top_level.txt,sha256=4z28AhuDodwRS_c1J_l8H51t5QuwfTseskYzlxp6grs,10
+setuptools/_vendor/typeguard/__init__.py,sha256=Onh4w38elPCjtlcU3JY9k3h70NjsxXIkAflmQn-Z0FY,2071
+setuptools/_vendor/typeguard/_checkers.py,sha256=JRrgKicdOEfIBoNEtegYCEIlhpad-a1u1Em7GCj0WCI,31360
+setuptools/_vendor/typeguard/_config.py,sha256=nIz8QwDa-oFO3L9O8_6srzlmd99pSby2wOM4Wb7F_B0,2846
+setuptools/_vendor/typeguard/_decorators.py,sha256=v6dsIeWvPhExGLP_wXF-RmDUyjZf_Ak28g7gBJ_v0-0,9033
+setuptools/_vendor/typeguard/_exceptions.py,sha256=ZIPeiV-FBd5Emw2EaWd2Fvlsrwi4ocwT2fVGBIAtHcQ,1121
+setuptools/_vendor/typeguard/_functions.py,sha256=ibgSAKa5ptIm1eR9ARG0BSozAFJPFNASZqhPVyQeqig,10393
+setuptools/_vendor/typeguard/_importhook.py,sha256=ugjCDvFcdWMU7UugqlJG91IpVNpEIxtRr-99s0h1k7M,6389
+setuptools/_vendor/typeguard/_memo.py,sha256=1juQV_vxnD2JYKbSrebiQuj4oKHz6n67v9pYA-CCISg,1303
+setuptools/_vendor/typeguard/_pytest_plugin.py,sha256=-fcSqkv54rIfIF8pDavY5YQPkj4OX8GMt_lL7CQSD4I,4416
+setuptools/_vendor/typeguard/_suppression.py,sha256=VQfzxcwIbu3if0f7VBkKM7hkYOA7tNFw9a7jMBsmMg4,2266
+setuptools/_vendor/typeguard/_transformer.py,sha256=9Ha7_QhdwoUni_6hvdY-hZbuEergowHrNL2vzHIakFY,44937
+setuptools/_vendor/typeguard/_union_transformer.py,sha256=v_42r7-6HuRX2SoFwnyJ-E5PlxXpVeUJPJR1-HU9qSo,1354
+setuptools/_vendor/typeguard/_utils.py,sha256=5HhO1rPn5f1M6ymkVAEv7Xmlz1cX-j0OnTMlyHqqrR8,5270
+setuptools/_vendor/typeguard/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/typing_extensions-4.12.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+setuptools/_vendor/typing_extensions-4.12.2.dist-info/LICENSE,sha256=Oy-B_iHRgcSZxZolbI4ZaEVdZonSaaqFNzv7avQdo78,13936
+setuptools/_vendor/typing_extensions-4.12.2.dist-info/METADATA,sha256=BeUQIa8cnYbrjWx-N8TOznM9UGW5Gm2DicVpDtRA8W0,3018
+setuptools/_vendor/typing_extensions-4.12.2.dist-info/RECORD,sha256=dxAALYGXHmMqpqL8M9xddKr118quIgQKZdPjFQOwXuk,571
+setuptools/_vendor/typing_extensions-4.12.2.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
+setuptools/_vendor/typing_extensions.py,sha256=gwekpyG9DVG3lxWKX4ni8u7nk3We5slG98mA9F3DJQw,134451
+setuptools/_vendor/wheel-0.45.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+setuptools/_vendor/wheel-0.45.1.dist-info/LICENSE.txt,sha256=MMI2GGeRCPPo6h0qZYx8pBe9_IkcmO8aifpP8MmChlQ,1107
+setuptools/_vendor/wheel-0.45.1.dist-info/METADATA,sha256=mKz84H7m7jsxJyzeIcTVORiTb0NPMV39KvOIYhGgmjA,2313
+setuptools/_vendor/wheel-0.45.1.dist-info/RECORD,sha256=1jnxrHyZPDcVvULyfGFhiba4Z5L9_RsXr9dxcNbhaYQ,4900
+setuptools/_vendor/wheel-0.45.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/wheel-0.45.1.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
+setuptools/_vendor/wheel-0.45.1.dist-info/entry_points.txt,sha256=rTY1BbkPHhkGMm4Q3F0pIzJBzW2kMxoG1oriffvGdA0,104
+setuptools/_vendor/wheel/__init__.py,sha256=mrxMnvdXACur_LWegbUfh5g5ysWZrd63UJn890wvGNk,59
+setuptools/_vendor/wheel/__main__.py,sha256=NkMUnuTCGcOkgY0IBLgBCVC_BGGcWORx2K8jYGS12UE,455
+setuptools/_vendor/wheel/_bdist_wheel.py,sha256=UghCQjSH_pVfcZh6oRjzSw_TQhcf3anSx1OkiLSL82M,21694
+setuptools/_vendor/wheel/_setuptools_logging.py,sha256=-5KC-lne0ilOUWIDfOkqapUWGMFZhuKYDIavIZiB5kM,781
+setuptools/_vendor/wheel/bdist_wheel.py,sha256=tpf9WufiSO1RuEMg5oPhIfSG8DMziCZ_4muCKF69Cqo,1107
+setuptools/_vendor/wheel/cli/__init__.py,sha256=Npq6_jKi03dhIcRnmbuFhwviVJxwO0tYEnEhWMv9cJo,4402
+setuptools/_vendor/wheel/cli/convert.py,sha256=Bi0ntEXb9nTllCxWeTRQ4j-nPs3szWSEKipG_GgnMkQ,12634
+setuptools/_vendor/wheel/cli/pack.py,sha256=CAFcHdBVulvsHYJlndKVO7KMI9JqBTZz5ii0PKxxCOs,3103
+setuptools/_vendor/wheel/cli/tags.py,sha256=lHw-LaWrkS5Jy_qWcw-6pSjeNM6yAjDnqKI3E5JTTCU,4760
+setuptools/_vendor/wheel/cli/unpack.py,sha256=Y_J7ynxPSoFFTT7H0fMgbBlVErwyDGcObgme5MBuz58,1021
+setuptools/_vendor/wheel/macosx_libfile.py,sha256=k1x7CE3LPtOVGqj6NXQ1nTGYVPaeRrhVzUG_KPq3zDs,16572
+setuptools/_vendor/wheel/metadata.py,sha256=JC4p7jlQZu2bUTAQ2fevkqLjg_X6gnNyRhLn6OUO1tc,6171
+setuptools/_vendor/wheel/util.py,sha256=aL7aibHwYUgfc8WlolL5tXdkV4DatbJxZHb1kwHFJAU,423
+setuptools/_vendor/wheel/vendored/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/wheel/vendored/packaging/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197
+setuptools/_vendor/wheel/vendored/packaging/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174
+setuptools/_vendor/wheel/vendored/packaging/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344
+setuptools/_vendor/wheel/vendored/packaging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/wheel/vendored/packaging/_elffile.py,sha256=hbmK8OD6Z7fY6hwinHEUcD1by7czkGiNYu7ShnFEk2k,3266
+setuptools/_vendor/wheel/vendored/packaging/_manylinux.py,sha256=P7sdR5_7XBY09LVYYPhHmydMJIIwPXWsh4olk74Uuj4,9588
+setuptools/_vendor/wheel/vendored/packaging/_musllinux.py,sha256=z1s8To2hQ0vpn_d-O2i5qxGwEK8WmGlLt3d_26V7NeY,2674
+setuptools/_vendor/wheel/vendored/packaging/_parser.py,sha256=4tT4emSl2qTaU7VTQE1Xa9o1jMPCsBezsYBxyNMUN-s,10347
+setuptools/_vendor/wheel/vendored/packaging/_structures.py,sha256=q3eVNmbWJGG_S0Dit_S3Ao8qQqz_5PYTXFAKBZe5yr4,1431
+setuptools/_vendor/wheel/vendored/packaging/_tokenizer.py,sha256=alCtbwXhOFAmFGZ6BQ-wCTSFoRAJ2z-ysIf7__MTJ_k,5292
+setuptools/_vendor/wheel/vendored/packaging/markers.py,sha256=_TSPI1BhJYO7Bp9AzTmHQxIqHEVXaTjmDh9G-w8qzPA,8232
+setuptools/_vendor/wheel/vendored/packaging/requirements.py,sha256=dgoBeVprPu2YE6Q8nGfwOPTjATHbRa_ZGLyXhFEln6Q,2933
+setuptools/_vendor/wheel/vendored/packaging/specifiers.py,sha256=IWSt0SrLSP72heWhAC8UL0eGvas7XIQHjqiViVfmPKE,39778
+setuptools/_vendor/wheel/vendored/packaging/tags.py,sha256=fedHXiOHkBxNZTXotXv8uXPmMFU9ae-TKBujgYHigcA,18950
+setuptools/_vendor/wheel/vendored/packaging/utils.py,sha256=XgdmP3yx9-wQEFjO7OvMj9RjEf5JlR5HFFR69v7SQ9E,5268
+setuptools/_vendor/wheel/vendored/packaging/version.py,sha256=PFJaYZDxBgyxkfYhH3SQw4qfE9ICCWrTmitvq14y3bs,16234
+setuptools/_vendor/wheel/vendored/vendor.txt,sha256=Z2ENjB1i5prfez8CdM1Sdr3c6Zxv2rRRolMpLmBncAE,16
+setuptools/_vendor/wheel/wheelfile.py,sha256=USCttNlJwafxt51YYFFKG7jnxz8dfhbyqAZL6jMTA9s,8411
+setuptools/_vendor/zipp-3.19.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+setuptools/_vendor/zipp-3.19.2.dist-info/LICENSE,sha256=htoPAa6uRjSKPD1GUZXcHOzN55956HdppkuNoEsqR0E,1023
+setuptools/_vendor/zipp-3.19.2.dist-info/METADATA,sha256=UIrk_kMIHGSwsKKChYizqMw0MMZpPRZ2ZiVpQAsN_bE,3575
+setuptools/_vendor/zipp-3.19.2.dist-info/RECORD,sha256=8xby4D_ZrefrvAsVRwaEjiu4_VaLkJNRCfDY484rm_4,1039
+setuptools/_vendor/zipp-3.19.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/zipp-3.19.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
+setuptools/_vendor/zipp-3.19.2.dist-info/top_level.txt,sha256=iAbdoSHfaGqBfVb2XuR9JqSQHCoOsOtG6y9C_LSpqFw,5
+setuptools/_vendor/zipp/__init__.py,sha256=QuI1g00G4fRAcGt-HqbV0oWIkmSgedCGGYsHHYzNa8A,13412
+setuptools/_vendor/zipp/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/_vendor/zipp/compat/py310.py,sha256=eZpkW0zRtunkhEh8jjX3gCGe22emoKCBJw72Zt4RkhA,219
+setuptools/_vendor/zipp/glob.py,sha256=etWpnfEoRyfUvrUsi6sTiGmErvPwe6HzY6pT8jg_lUI,3082
+setuptools/archive_util.py,sha256=Tl_64hSTtc4y8x7xa98rFVUbG24oArpjzLAYGYP2_sI,7356
+setuptools/build_meta.py,sha256=3cHAWucJaLA9DU5OfCbKkkteTDiQ5bB4LokfTRMgJT4,19968
+setuptools/cli-32.exe,sha256=MqzBvFQxFsviz_EMuGd3LfLyVP8mNMhwrvC0bEtpb9s,11776
+setuptools/cli-64.exe,sha256=u7PeVwdinmpgoMI4zUd7KPB_AGaYL9qVP6b87DkHOko,14336
+setuptools/cli-arm64.exe,sha256=uafQjaiA36yLz1SOuksG-1m28JsX0zFIoPZhgyiSbGE,13824
+setuptools/cli.exe,sha256=MqzBvFQxFsviz_EMuGd3LfLyVP8mNMhwrvC0bEtpb9s,11776
+setuptools/command/__init__.py,sha256=wdSrlNR0P6nCz9_oFtCAiAkeFJMsZa1jPcpXT53f0SM,803
+setuptools/command/_requirestxt.py,sha256=ItYMTJGh_i5TlQstX_nFopqEhkC4PJFadBL2Zd3V670,4228
+setuptools/command/alias.py,sha256=rDdrMt32DS6qf3K7tjZZyHD_dMKrm77AXcAtx-nBQ0I,2380
+setuptools/command/bdist_egg.py,sha256=JmtKoIbiwgEHcJBkbc7zyXCZcAF851t6ek18gme-60Q,16948
+setuptools/command/bdist_rpm.py,sha256=LyqI49w48SKk0FmuHsE9MLzX1SuXjL7YMNbZMFZqFII,1435
+setuptools/command/bdist_wheel.py,sha256=SknYPVwhrRPfXudmO_gvqNHHHhzSfU8cEmFtQomQ9xI,22247
+setuptools/command/build.py,sha256=eI7STMERGGZEpzk1tvJN8p9IOjAAXMcGLzljv2mwI3M,6052
+setuptools/command/build_clib.py,sha256=AbgpPIF_3qL8fZr3JIebI-WHTMTBiMfrFkVQz8K40G4,4528
+setuptools/command/build_ext.py,sha256=Wddi6ho4MnVh84qqZUNqTvjKLqeoWe6cbwdJOUitXCc,18465
+setuptools/command/build_py.py,sha256=DCbjvB18kkL-xUK5rvlzm0C6twTeOxNhyvJDxxa7fII,15539
+setuptools/command/develop.py,sha256=1dsb2lkjcPQQAlQNVVlfPIJUBZ9di0l5bs4l83g9-9Y,1610
+setuptools/command/dist_info.py,sha256=HU752iLLmmYMHbsDBgz2ubRjkgJobugOp8H71LzzDys,3450
+setuptools/command/easy_install.py,sha256=XrN5cV51mfzbCDoapZ6iT8nCzaLpumdwJYRKeMHEjCQ,780
+setuptools/command/editable_wheel.py,sha256=MXyQx41gwu3d1raYSZGgzCVp5kkVtlCKftZKvTma3wY,34836
+setuptools/command/egg_info.py,sha256=GXyvq5E8huO4g-FDoNzf3MHjeXfxzPoS0Hkvbta_zc8,25878
+setuptools/command/install.py,sha256=4x2hiNgBGQrFEXKuPBQMrb8ecSwIfYF4TYHZQLjPVAg,5066
+setuptools/command/install_egg_info.py,sha256=3I9IPCH7D59Sh-6aVYz-h6wwyxq-wkxrKwKg3nDdJqs,2075
+setuptools/command/install_lib.py,sha256=9n1_U83eHcERL_a_rv_LhHCkhXlLdqyZ4SdBow-9qcE,4319
+setuptools/command/install_scripts.py,sha256=JmDGngHzCO2Y1j4maFNdHB_ILhGPhk-b5KhxsZwUwiQ,2490
+setuptools/command/launcher manifest.xml,sha256=xlLbjWrB01tKC0-hlVkOKkiSPbzMml2eOPtJ_ucCnbE,628
+setuptools/command/rotate.py,sha256=XNd_BEEOWAJHW1FcLTMUWWl4QB6zAuk7b8VWQg3FHos,2187
+setuptools/command/saveopts.py,sha256=Np0PVb7SD7oTbu9Z9sosS7D-CkkIkU7x4glu5Es1tjA,692
+setuptools/command/sdist.py,sha256=5ZiA8yfdNfl-kLTnfPAht1yKnS1o_HrSFphsJd-9foU,7369
+setuptools/command/setopt.py,sha256=xZF2RCc4ABvE9eHHAzF50-fkQg3au8fcRUVVGd58k3U,5100
+setuptools/command/test.py,sha256=k7xcq7D7bEehgxarbw-dW3AtmGZORqz8HjKR6FGJ3jk,1343
+setuptools/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/compat/py310.py,sha256=JwjQZ3cNTizfpDLNl9GLsUGzBr-tVlMPxmMYVDTlhiI,344
+setuptools/compat/py311.py,sha256=e6tJAFwZEP82hmMBl10HYeSypelo_Ti2wTjKZVKLwOE,790
+setuptools/compat/py312.py,sha256=vYKVtdrdOTsO_R90dJkEXsFwfMJFuIFJflhIgHrjJ-Y,366
+setuptools/compat/py39.py,sha256=BJMtnkfcqyTfccqjYQxfoRtU2nTnWaEESBVkshTiXqY,493
+setuptools/config/NOTICE,sha256=Ld3wiBgpejuJ1D2V_2WdjahXQRCMkTbfo6TYVsBiO9g,493
+setuptools/config/__init__.py,sha256=aiPnL9BJn1O6MfmuNXyn8W2Lp8u9qizRVqwPiOdPIjY,1499
+setuptools/config/_apply_pyprojecttoml.py,sha256=SUyTw7A2btZ1lBuWKN5o42-Diyv95eGTiYJ3rZOnGSc,19120
+setuptools/config/_validate_pyproject/NOTICE,sha256=XTANv6ZDE4sBO3WsnK7uWR-VG4sO4kKIw0zNkmxHgMg,18737
+setuptools/config/_validate_pyproject/__init__.py,sha256=dnp6T7ePP1R5z4OuC7Fd2dkFlIrtIfizUfvpGJP6nz0,1042
+setuptools/config/_validate_pyproject/error_reporting.py,sha256=meldD7nBQdolQhvG-43r1Ue-gU1n7ORAJR86vh3Rrvk,11813
+setuptools/config/_validate_pyproject/extra_validations.py,sha256=-GUG5S--ijY8WfXbdXPoHl6ywGsyEF9dtDpenSoJPHg,2858
+setuptools/config/_validate_pyproject/fastjsonschema_exceptions.py,sha256=w749JgqKi8clBFcObdcbZVqsmF4oJ_QByhZ1SGbUFNw,1612
+setuptools/config/_validate_pyproject/fastjsonschema_validations.py,sha256=FihD5ZcM6p77BPZ04CGqh3BEwVNoPMKJZJAyuJpkAU0,354682
+setuptools/config/_validate_pyproject/formats.py,sha256=TETokJBK9hjl-cVg1olsojkJwLxfP7_chgJQNmzAB98,13564
+setuptools/config/distutils.schema.json,sha256=Tcp32kRnhwORGw_9p6GEi08lj2h15tQRzOYBbzGmcBU,972
+setuptools/config/expand.py,sha256=JNAktRCsyyRB-rQodbPnCucmLWqcYvzCDC8Ebn2Z7xM,16041
+setuptools/config/pyprojecttoml.py,sha256=YMu5PdbJJI5azp6kR_boM1mflf5nqOA-InF4s6LnLgw,18320
+setuptools/config/setupcfg.py,sha256=VZDkwE7DYv45SbadJD8CwKrDtiXvjgllL8PYSvoRCyg,26575
+setuptools/config/setuptools.schema.json,sha256=dZBRuSEnZkatoVlt1kVwG8ocTeRdO7BD0xvOWKH54PY,16071
+setuptools/depends.py,sha256=jKYfjmt_2ZQYVghb8L9bU7LJ6erHJ5ze-K_fKV1BMXk,5965
+setuptools/discovery.py,sha256=-42c3XhwzkfodDKKP50C2YBzr11fncAgmUzBdBRb0-Q,21258
+setuptools/dist.py,sha256=jrLGf-4udZjoZyULuGrXEPzgFDVq1CHCfGsqjTq52Gg,44887
+setuptools/errors.py,sha256=gY2x2PIaIgy01yRANRC-zcCwxDCqCScgJoCOZFe0yio,3024
+setuptools/extension.py,sha256=KCnv9p3tgm0ZVqtgE451fyILsm4hCyvOiUtOu787D-4,6683
+setuptools/glob.py,sha256=AC_B33DY8g-CHELxDsJrtwFrpiucSAZsakPFdSOQzhc,6062
+setuptools/gui-32.exe,sha256=hdrh6V13hF8stZvKw9Sv50u-TJGpvMW_SnHNQxBNvnw,11776
+setuptools/gui-64.exe,sha256=NHG2FA6txkEid9u-_j_vjDRaDxpZd2CGuAo2GMOoPjs,14336
+setuptools/gui-arm64.exe,sha256=5pT0dDQFyLWSb_RX22_n8aEt7HwWqcOGR4TT9OB64Jc,13824
+setuptools/gui.exe,sha256=hdrh6V13hF8stZvKw9Sv50u-TJGpvMW_SnHNQxBNvnw,11776
+setuptools/installer.py,sha256=veio-PDCseWN0J1E_1gjvVLkcIhPpQLlEKpSaA03WWk,5093
+setuptools/launch.py,sha256=IBb5lEv69CyuZ9ewIrmKlXh154kdLmP29LKfTMkximE,820
+setuptools/logging.py,sha256=W16iHJ1HcCXYQ0RxyrEfJ83FT4175tCtoYg-E6uSpVI,1261
+setuptools/modified.py,sha256=ZwbfBfCFP88ltvbv_dJDz-t1LsQjnM-JUpgZnnQZjjM,568
+setuptools/monkey.py,sha256=FwMWl2n1v2bHbeqBy-o9g8yUNaAkYFbszCbXe9d5Za8,3717
+setuptools/msvc.py,sha256=vmM0qL4rIzrtD9pia9ZEwtqZ4LbbrgL0dU0EANVYRm8,41631
+setuptools/namespaces.py,sha256=2GGqYY1BNDEhMtBc1rHTv7klgmNVRdksJeW-L1f--ys,3171
+setuptools/script (dev).tmpl,sha256=RUzQzCQUaXtwdLtYHWYbIQmOaES5Brqq1FvUA_tu-5I,218
+setuptools/script.tmpl,sha256=WGTt5piezO27c-Dbx6l5Q4T3Ff20A5z7872hv3aAhYY,138
+setuptools/tests/__init__.py,sha256=AnBfls2iJbTDQzmMKeLRt-9lxhaOHUVOZEgXv89Uwvs,335
+setuptools/tests/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/tests/compat/py39.py,sha256=eUy7_F-6KRTOIKl-veshUu6I0EdTSdBZMh0EV0lZ1-g,135
+setuptools/tests/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/tests/config/downloads/__init__.py,sha256=9ixnDEdyL_arKbUzfuiJftAj9bGxKz8M9alOFZMjx9Y,1827
+setuptools/tests/config/downloads/preload.py,sha256=sIGGZpY3cmMpMwiJYYYYHG2ifZJkvJgEotRFtiulV1I,450
+setuptools/tests/config/setupcfg_examples.txt,sha256=cAbVvCbkFZuTUL6xRRzRgqyB0rLvJTfvw3D30glo2OE,1912
+setuptools/tests/config/test_apply_pyprojecttoml.py,sha256=l6nE4d8WLU_eSWRic7VSoqeKv9Bi7CZGHcEuB2ehk2w,28807
+setuptools/tests/config/test_expand.py,sha256=S0oT6JvgA_oujR4YS4RUuf5gmOt1CTQV66RQDzV8xd4,8933
+setuptools/tests/config/test_pyprojecttoml.py,sha256=0LefSljUhA6MqtJ5AVzLhomqZcYiFKdu_1ckDeMT1LY,12406
+setuptools/tests/config/test_pyprojecttoml_dynamic_deps.py,sha256=9W73-yLhZJmvCiO4rTiQoBpZT5wNA90Xbd5n2HCshd4,3271
+setuptools/tests/config/test_setupcfg.py,sha256=ZvN-O-2Dgon1adp6oM6il8JWdgT9y196fRvqESU5ELI,33427
+setuptools/tests/contexts.py,sha256=Ozdfc2KydF9x9wODUsdun800myLuP27uxoeT06Gbk7M,3166
+setuptools/tests/environment.py,sha256=95_UtTaRiuvwYC9eXKEHbn02kDtZysvZq3UZJmPUj1I,3102
+setuptools/tests/fixtures.py,sha256=aPewdPHlKHRAsMo9H828c3ZC8l3OJqgyfRYwpLBvgCk,11705
+setuptools/tests/indexes/test_links_priority/external.html,sha256=eL9euOuE93JKZdqlXxBOlHbKwIuNuIdq7GBRpsaPMcU,92
+setuptools/tests/indexes/test_links_priority/simple/foobar/index.html,sha256=DD-TKr7UU4zAjHHz4VexYDNSAzR27levSh1c-k3ZdLE,174
+setuptools/tests/integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+setuptools/tests/integration/helpers.py,sha256=3PHcS9SCA-fwVJmUP2ad5NQOttJAETI5Nnoc_xroO5k,2522
+setuptools/tests/integration/test_pbr.py,sha256=2eKuklFNmpnBgA_eEhYPBr6rLLG2Xm4MY6PlcmzZgGU,432
+setuptools/tests/integration/test_pip_install_sdist.py,sha256=SFbvuYF_hDzt6OtsQ5GjFNnxmoJ_eElfvpYsiyyGJ-g,8256
+setuptools/tests/mod_with_constant.py,sha256=X_Kj80M55w1tmQ4f7uZY91ZTALo4hKVT6EHxgYocUMQ,22
+setuptools/tests/namespaces.py,sha256=HPcI3nR5MCFWXpaADIJ1fwKxymcQgBkuw87Ic5PUSAQ,2774
+setuptools/tests/script-with-bom.py,sha256=hRRgIizEULGiG_ZTNoMY46HhKhxpWfy5FGcD6Qbh5fc,18
+setuptools/tests/test_archive_util.py,sha256=buuKdY8XkW26Pe3IKAoBRGHG0MDumnuNoPg2WsAQzIg,845
+setuptools/tests/test_bdist_deprecations.py,sha256=75Xq3gYn79LIIyusEltbHan0bEgAt2e_CaL7KLS8-KQ,775
+setuptools/tests/test_bdist_egg.py,sha256=6PaYN1F3JDbIh1uK0urv7yJFcx98z5dn9SOJ8Mv91l8,1957
+setuptools/tests/test_bdist_wheel.py,sha256=dZ9a7OT_UyRvLnoCi2KGEIbtzhEQjM3YutYMA6ZCezs,23083
+setuptools/tests/test_build.py,sha256=wJgMz2hwHADcLFg-nXrwRVhus7hjmAeEGgrpIQwCGnA,798
+setuptools/tests/test_build_clib.py,sha256=bX51XRAf4uO7IuHFpjePnoK8mE74N2gsoeEqF-ofgws,3123
+setuptools/tests/test_build_ext.py,sha256=e4ZSxsYPB5zq1KSqGEuATZ0t0PJQzMhjjkKJ-hIjcgc,10099
+setuptools/tests/test_build_meta.py,sha256=kvi0Bn4p9DBVme3zyWQsn3QgB9oPdq8S15agj1m69L0,33289
+setuptools/tests/test_build_py.py,sha256=gobME_Cvzf6Ugxq70iWfXekb_xyyT61khwjFq8zkwfw,14186
+setuptools/tests/test_config_discovery.py,sha256=FqV-lOtkqaI-ayzU2zocSdD5TaRAgCZnixNDilKA6FQ,22580
+setuptools/tests/test_core_metadata.py,sha256=vbVJ5_Lsx_hsO_GdB6nQEXJRjA2ydx6_qSbr5LpheAA,20881
+setuptools/tests/test_depends.py,sha256=yQBXoQbNQlJit6mbRVoz6Bb553f3sNrq02lZimNz5XY,424
+setuptools/tests/test_develop.py,sha256=MHYL_YDqNMU5jhKkjsBUGKMGCkrva8CFR8dRc6kkYKE,3072
+setuptools/tests/test_dist.py,sha256=_IYleHR9YVqzV-nLq_JqSup6DNeUdPzuQ0EXhp009Uk,8893
+setuptools/tests/test_dist_info.py,sha256=F_xTXc5TGhjiujtGukFt6wNstqpTW7sVQtUnL1IX7yo,4988
+setuptools/tests/test_distutils_adoption.py,sha256=_eynrOfyEqXFEmjUJhzpe8GXPyTUPvNSObs4qAAmBy8,5987
+setuptools/tests/test_editable_install.py,sha256=Tg4kunvwYoDLYsfvSkggneUgpKQferUDx3EEvvdfmwE,42619
+setuptools/tests/test_egg_info.py,sha256=R7nT27YhVz9oSuDyimAGerWglkbRWiMSPBs5FzcSnBM,44941
+setuptools/tests/test_extern.py,sha256=rpKU6oCcksumLwf5TeKlDluFQ0TUfbPwTLQbpxcFrCU,296
+setuptools/tests/test_find_packages.py,sha256=CTLAcTzWGWBLCcd2aAsUVkvO3ibrlqexFBdDKOWPoq8,7819
+setuptools/tests/test_find_py_modules.py,sha256=zQjuhIG5TQN2SJPix9ARo4DL_w84Ln8QsHDUjjbrtAQ,2404
+setuptools/tests/test_glob.py,sha256=P3JvpH-kXQ4BZ3zvRF-zKxOgwyWzwIaQIz0WHdxS0kk,887
+setuptools/tests/test_install_scripts.py,sha256=scIrJ6a_ssKqg4vIBNaUjmAKHEYLUUZ9WKnPeKnE6gc,3433
+setuptools/tests/test_logging.py,sha256=zlE5DlldukC7Jc54FNvDV_7ux3ErAkrfrN5CSsnNOUQ,2099
+setuptools/tests/test_manifest.py,sha256=eMg65pIA52DizB6mpktSU-b8CjwaNCS5MSgL_V1LrFI,18562
+setuptools/tests/test_namespaces.py,sha256=Y6utoe5PHHqL_DlgawqB9F8XpsUDPvvw1sQMenK04e0,4515
+setuptools/tests/test_scripts.py,sha256=_ra506yQF7n72ROUDcz2r3CTsGsawO1m-1oqA9EQCfw,379
+setuptools/tests/test_sdist.py,sha256=RYLvPa_nfyC1ZmoinzqMzJynTDG4RtPYC19_0LU6pvs,32872
+setuptools/tests/test_setopt.py,sha256=3VxxM4ATfP-P4AGnDjoWCnHr5-i9CSEQTFYU1-FTnvI,1365
+setuptools/tests/test_setuptools.py,sha256=_eIhqKf45-OtHqxRf20KndOZJlJdS0PuFLXBO3M-LN8,9008
+setuptools/tests/test_shutil_wrapper.py,sha256=g15E11PtZxG-InB2BWNFyH-svObXx2XcMhgMLJPuFnc,641
+setuptools/tests/test_unicode_utils.py,sha256=xWfEEl8jkQCt9othUTXJfFmdyATAFggJs2tTxjbumbw,316
+setuptools/tests/test_virtualenv.py,sha256=g-njC_9JTAs1YVx_1dGJ_Q6RlInO4qKVu9-XAgNb6TY,3730
+setuptools/tests/test_warnings.py,sha256=zwR2zcnCeCeDqILZlJOPAcuyPHoDvGu1OtOVYiLMk74,3347
+setuptools/tests/test_wheel.py,sha256=I709mQO4YCztxI2L8x7bu_rE818vC9SXHR8qtZPwZW8,18752
+setuptools/tests/test_windows_wrappers.py,sha256=wBjXN3iGldkzRGTgKTrx99xqUqwPJ0V-ldyiB1pWD-g,7868
+setuptools/tests/text.py,sha256=a12197pMVTvB6FAWQ0ujT8fIQiLIWJlFAl1UCaDUDfg,123
+setuptools/tests/textwrap.py,sha256=FNNNq_MiaEJx88PnsbJQIRxmj1qmgcAOCXXRsODPJN4,98
+setuptools/unicode_utils.py,sha256=ukMGh8pEAw6F_Ezb-K5D3c-078RgA_GcF0oW6lg4lSs,3189
+setuptools/version.py,sha256=WJCeUuyq74Aok2TeK9-OexZOu8XrlQy7-y0BEuWNovQ,161
+setuptools/warnings.py,sha256=oY0Se5eOqje_FEyjTgonUc0XGwgsrI5cgm1kkwulz_w,3796
+setuptools/wheel.py,sha256=_JuhinWmlQwBHJrdIh1yfhrDS7GFMacCiJiOY3H5gwA,9477
+setuptools/windows_support.py,sha256=wW4IYLM1Bv7Z1MaauP2xmPjyy-wkmQnXdyvXscAf9fw,726
diff --git a/.venv/lib/python3.12/site-packages/setuptools-80.9.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/setuptools-80.9.0.dist-info/REQUESTED
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/.venv/lib/python3.12/site-packages/setuptools-80.9.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/setuptools-80.9.0.dist-info/WHEEL
new file mode 100644
index 0000000000000000000000000000000000000000..e7fa31b6f3f78deb1022c1f7927f07d4d16da822
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools-80.9.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: setuptools (80.9.0)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/.venv/lib/python3.12/site-packages/setuptools-80.9.0.dist-info/entry_points.txt b/.venv/lib/python3.12/site-packages/setuptools-80.9.0.dist-info/entry_points.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0db0a6c8f1b8d9c0ad4a25db6892e29f8988fcf2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools-80.9.0.dist-info/entry_points.txt
@@ -0,0 +1,51 @@
+[distutils.commands]
+alias = setuptools.command.alias:alias
+bdist_egg = setuptools.command.bdist_egg:bdist_egg
+bdist_rpm = setuptools.command.bdist_rpm:bdist_rpm
+bdist_wheel = setuptools.command.bdist_wheel:bdist_wheel
+build = setuptools.command.build:build
+build_clib = setuptools.command.build_clib:build_clib
+build_ext = setuptools.command.build_ext:build_ext
+build_py = setuptools.command.build_py:build_py
+develop = setuptools.command.develop:develop
+dist_info = setuptools.command.dist_info:dist_info
+easy_install = setuptools.command.easy_install:easy_install
+editable_wheel = setuptools.command.editable_wheel:editable_wheel
+egg_info = setuptools.command.egg_info:egg_info
+install = setuptools.command.install:install
+install_egg_info = setuptools.command.install_egg_info:install_egg_info
+install_lib = setuptools.command.install_lib:install_lib
+install_scripts = setuptools.command.install_scripts:install_scripts
+rotate = setuptools.command.rotate:rotate
+saveopts = setuptools.command.saveopts:saveopts
+sdist = setuptools.command.sdist:sdist
+setopt = setuptools.command.setopt:setopt
+
+[distutils.setup_keywords]
+dependency_links = setuptools.dist:assert_string_list
+eager_resources = setuptools.dist:assert_string_list
+entry_points = setuptools.dist:check_entry_points
+exclude_package_data = setuptools.dist:check_package_data
+extras_require = setuptools.dist:check_extras
+include_package_data = setuptools.dist:assert_bool
+install_requires = setuptools.dist:check_requirements
+namespace_packages = setuptools.dist:check_nsp
+package_data = setuptools.dist:check_package_data
+packages = setuptools.dist:check_packages
+python_requires = setuptools.dist:check_specifier
+setup_requires = setuptools.dist:check_requirements
+use_2to3 = setuptools.dist:invalid_unless_false
+zip_safe = setuptools.dist:assert_bool
+
+[egg_info.writers]
+PKG-INFO = setuptools.command.egg_info:write_pkg_info
+dependency_links.txt = setuptools.command.egg_info:overwrite_arg
+eager_resources.txt = setuptools.command.egg_info:overwrite_arg
+entry_points.txt = setuptools.command.egg_info:write_entries
+namespace_packages.txt = setuptools.command.egg_info:overwrite_arg
+requires.txt = setuptools.command.egg_info:write_requirements
+top_level.txt = setuptools.command.egg_info:write_toplevel_names
+
+[setuptools.finalize_distribution_options]
+keywords = setuptools.dist:Distribution._finalize_setup_keywords
+parent_finalize = setuptools.dist:_Distribution.finalize_options
diff --git a/.venv/lib/python3.12/site-packages/setuptools-80.9.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/setuptools-80.9.0.dist-info/top_level.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b5ac1070294b478b7cc2ce677207ee08813bfa37
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools-80.9.0.dist-info/top_level.txt
@@ -0,0 +1,3 @@
+_distutils_hack
+pkg_resources
+setuptools
diff --git a/.venv/lib/python3.12/site-packages/setuptools/__init__.py b/.venv/lib/python3.12/site-packages/setuptools/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..f1b9bfe9b8ae51a90b71014630f01ffafb3f2405
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/__init__.py
@@ -0,0 +1,248 @@
+"""Extensions to the 'distutils' for large or complex distributions"""
+# mypy: disable_error_code=override
+# Command.reinitialize_command has an extra **kw param that distutils doesn't have
+# Can't disable on the exact line because distutils doesn't exists on Python 3.12
+# and mypy isn't aware of distutils_hack, causing distutils.core.Command to be Any,
+# and a [unused-ignore] to be raised on 3.12+
+
+from __future__ import annotations
+
+import functools
+import os
+import sys
+from abc import abstractmethod
+from collections.abc import Mapping
+from typing import TYPE_CHECKING, TypeVar, overload
+
+sys.path.extend(((vendor_path := os.path.join(os.path.dirname(os.path.dirname(__file__)), 'setuptools', '_vendor')) not in sys.path) * [vendor_path])  # fmt: skip
+# workaround for #4476
+sys.modules.pop('backports', None)
+
+import _distutils_hack.override  # noqa: F401
+
+from . import logging, monkey
+from .depends import Require
+from .discovery import PackageFinder, PEP420PackageFinder
+from .dist import Distribution
+from .extension import Extension
+from .version import __version__ as __version__
+from .warnings import SetuptoolsDeprecationWarning
+
+import distutils.core
+
+__all__ = [
+    'setup',
+    'Distribution',
+    'Command',
+    'Extension',
+    'Require',
+    'SetuptoolsDeprecationWarning',
+    'find_packages',
+    'find_namespace_packages',
+]
+
+_CommandT = TypeVar("_CommandT", bound="_Command")
+
+bootstrap_install_from = None
+
+find_packages = PackageFinder.find
+find_namespace_packages = PEP420PackageFinder.find
+
+
+def _install_setup_requires(attrs):
+    # Note: do not use `setuptools.Distribution` directly, as
+    # our PEP 517 backend patch `distutils.core.Distribution`.
+    class MinimalDistribution(distutils.core.Distribution):
+        """
+        A minimal version of a distribution for supporting the
+        fetch_build_eggs interface.
+        """
+
+        def __init__(self, attrs: Mapping[str, object]) -> None:
+            _incl = 'dependency_links', 'setup_requires'
+            filtered = {k: attrs[k] for k in set(_incl) & set(attrs)}
+            super().__init__(filtered)
+            # Prevent accidentally triggering discovery with incomplete set of attrs
+            self.set_defaults._disable()
+
+        def _get_project_config_files(self, filenames=None):
+            """Ignore ``pyproject.toml``, they are not related to setup_requires"""
+            try:
+                cfg, _toml = super()._split_standard_project_metadata(filenames)
+            except Exception:
+                return filenames, ()
+            return cfg, ()
+
+        def finalize_options(self):
+            """
+            Disable finalize_options to avoid building the working set.
+            Ref #2158.
+            """
+
+    dist = MinimalDistribution(attrs)
+
+    # Honor setup.cfg's options.
+    dist.parse_config_files(ignore_option_errors=True)
+    if dist.setup_requires:
+        _fetch_build_eggs(dist)
+
+
+def _fetch_build_eggs(dist: Distribution):
+    try:
+        dist.fetch_build_eggs(dist.setup_requires)
+    except Exception as ex:
+        msg = """
+        It is possible a package already installed in your system
+        contains an version that is invalid according to PEP 440.
+        You can try `pip install --use-pep517` as a workaround for this problem,
+        or rely on a new virtual environment.
+
+        If the problem refers to a package that is not installed yet,
+        please contact that package's maintainers or distributors.
+        """
+        if "InvalidVersion" in ex.__class__.__name__:
+            if hasattr(ex, "add_note"):
+                ex.add_note(msg)  # PEP 678
+            else:
+                dist.announce(f"\n{msg}\n")
+        raise
+
+
+def setup(**attrs):
+    logging.configure()
+    # Make sure we have any requirements needed to interpret 'attrs'.
+    _install_setup_requires(attrs)
+    return distutils.core.setup(**attrs)
+
+
+setup.__doc__ = distutils.core.setup.__doc__
+
+if TYPE_CHECKING:
+    # Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962
+    from distutils.core import Command as _Command
+else:
+    _Command = monkey.get_unpatched(distutils.core.Command)
+
+
+class Command(_Command):
+    """
+    Setuptools internal actions are organized using a *command design pattern*.
+    This means that each action (or group of closely related actions) executed during
+    the build should be implemented as a ``Command`` subclass.
+
+    These commands are abstractions and do not necessarily correspond to a command that
+    can (or should) be executed via a terminal, in a CLI fashion (although historically
+    they would).
+
+    When creating a new command from scratch, custom defined classes **SHOULD** inherit
+    from ``setuptools.Command`` and implement a few mandatory methods.
+    Between these mandatory methods, are listed:
+    :meth:`initialize_options`, :meth:`finalize_options` and :meth:`run`.
+
+    A useful analogy for command classes is to think of them as subroutines with local
+    variables called "options".  The options are "declared" in :meth:`initialize_options`
+    and "defined" (given their final values, aka "finalized") in :meth:`finalize_options`,
+    both of which must be defined by every command class. The "body" of the subroutine,
+    (where it does all the work) is the :meth:`run` method.
+    Between :meth:`initialize_options` and :meth:`finalize_options`, ``setuptools`` may set
+    the values for options/attributes based on user's input (or circumstance),
+    which means that the implementation should be careful to not overwrite values in
+    :meth:`finalize_options` unless necessary.
+
+    Please note that other commands (or other parts of setuptools) may also overwrite
+    the values of the command's options/attributes multiple times during the build
+    process.
+    Therefore it is important to consistently implement :meth:`initialize_options` and
+    :meth:`finalize_options`. For example, all derived attributes (or attributes that
+    depend on the value of other attributes) **SHOULD** be recomputed in
+    :meth:`finalize_options`.
+
+    When overwriting existing commands, custom defined classes **MUST** abide by the
+    same APIs implemented by the original class. They also **SHOULD** inherit from the
+    original class.
+    """
+
+    command_consumes_arguments = False
+    distribution: Distribution  # override distutils.dist.Distribution with setuptools.dist.Distribution
+
+    def __init__(self, dist: Distribution, **kw) -> None:
+        """
+        Construct the command for dist, updating
+        vars(self) with any keyword parameters.
+        """
+        super().__init__(dist)
+        vars(self).update(kw)
+
+    @overload
+    def reinitialize_command(
+        self, command: str, reinit_subcommands: bool = False, **kw
+    ) -> _Command: ...
+    @overload
+    def reinitialize_command(
+        self, command: _CommandT, reinit_subcommands: bool = False, **kw
+    ) -> _CommandT: ...
+    def reinitialize_command(
+        self, command: str | _Command, reinit_subcommands: bool = False, **kw
+    ) -> _Command:
+        cmd = _Command.reinitialize_command(self, command, reinit_subcommands)
+        vars(cmd).update(kw)
+        return cmd  # pyright: ignore[reportReturnType] # pypa/distutils#307
+
+    @abstractmethod
+    def initialize_options(self) -> None:
+        """
+        Set or (reset) all options/attributes/caches used by the command
+        to their default values. Note that these values may be overwritten during
+        the build.
+        """
+        raise NotImplementedError
+
+    @abstractmethod
+    def finalize_options(self) -> None:
+        """
+        Set final values for all options/attributes used by the command.
+        Most of the time, each option/attribute/cache should only be set if it does not
+        have any value yet (e.g. ``if self.attr is None: self.attr = val``).
+        """
+        raise NotImplementedError
+
+    @abstractmethod
+    def run(self) -> None:
+        """
+        Execute the actions intended by the command.
+        (Side effects **SHOULD** only take place when :meth:`run` is executed,
+        for example, creating new files or writing to the terminal output).
+        """
+        raise NotImplementedError
+
+
+def _find_all_simple(path):
+    """
+    Find all files under 'path'
+    """
+    results = (
+        os.path.join(base, file)
+        for base, dirs, files in os.walk(path, followlinks=True)
+        for file in files
+    )
+    return filter(os.path.isfile, results)
+
+
+def findall(dir=os.curdir):
+    """
+    Find all files under 'dir' and return the list of full filenames.
+    Unless dir is '.', return full filenames with dir prepended.
+    """
+    files = _find_all_simple(dir)
+    if dir == os.curdir:
+        make_rel = functools.partial(os.path.relpath, start=dir)
+        files = map(make_rel, files)
+    return list(files)
+
+
+class sic(str):
+    """Treat this string as-is (https://en.wikipedia.org/wiki/Sic)"""
+
+
+# Apply monkey patches
+monkey.patch_all()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_core_metadata.py b/.venv/lib/python3.12/site-packages/setuptools/_core_metadata.py
new file mode 100644
index 0000000000000000000000000000000000000000..a52d5cf755cdd6a502e752c2f7a3afa3b25897d5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_core_metadata.py
@@ -0,0 +1,337 @@
+"""
+Handling of Core Metadata for Python packages (including reading and writing).
+
+See: https://packaging.python.org/en/latest/specifications/core-metadata/
+"""
+
+from __future__ import annotations
+
+import os
+import stat
+import textwrap
+from email import message_from_file
+from email.message import Message
+from tempfile import NamedTemporaryFile
+
+from packaging.markers import Marker
+from packaging.requirements import Requirement
+from packaging.utils import canonicalize_name, canonicalize_version
+from packaging.version import Version
+
+from . import _normalization, _reqs
+from ._static import is_static
+from .warnings import SetuptoolsDeprecationWarning
+
+from distutils.util import rfc822_escape
+
+
+def get_metadata_version(self):
+    mv = getattr(self, 'metadata_version', None)
+    if mv is None:
+        mv = Version('2.4')
+        self.metadata_version = mv
+    return mv
+
+
+def rfc822_unescape(content: str) -> str:
+    """Reverse RFC-822 escaping by removing leading whitespaces from content."""
+    lines = content.splitlines()
+    if len(lines) == 1:
+        return lines[0].lstrip()
+    return '\n'.join((lines[0].lstrip(), textwrap.dedent('\n'.join(lines[1:]))))
+
+
+def _read_field_from_msg(msg: Message, field: str) -> str | None:
+    """Read Message header field."""
+    value = msg[field]
+    if value == 'UNKNOWN':
+        return None
+    return value
+
+
+def _read_field_unescaped_from_msg(msg: Message, field: str) -> str | None:
+    """Read Message header field and apply rfc822_unescape."""
+    value = _read_field_from_msg(msg, field)
+    if value is None:
+        return value
+    return rfc822_unescape(value)
+
+
+def _read_list_from_msg(msg: Message, field: str) -> list[str] | None:
+    """Read Message header field and return all results as list."""
+    values = msg.get_all(field, None)
+    if values == []:
+        return None
+    return values
+
+
+def _read_payload_from_msg(msg: Message) -> str | None:
+    value = str(msg.get_payload()).strip()
+    if value == 'UNKNOWN' or not value:
+        return None
+    return value
+
+
+def read_pkg_file(self, file):
+    """Reads the metadata values from a file object."""
+    msg = message_from_file(file)
+
+    self.metadata_version = Version(msg['metadata-version'])
+    self.name = _read_field_from_msg(msg, 'name')
+    self.version = _read_field_from_msg(msg, 'version')
+    self.description = _read_field_from_msg(msg, 'summary')
+    # we are filling author only.
+    self.author = _read_field_from_msg(msg, 'author')
+    self.maintainer = None
+    self.author_email = _read_field_from_msg(msg, 'author-email')
+    self.maintainer_email = None
+    self.url = _read_field_from_msg(msg, 'home-page')
+    self.download_url = _read_field_from_msg(msg, 'download-url')
+    self.license = _read_field_unescaped_from_msg(msg, 'license')
+    self.license_expression = _read_field_unescaped_from_msg(msg, 'license-expression')
+
+    self.long_description = _read_field_unescaped_from_msg(msg, 'description')
+    if self.long_description is None and self.metadata_version >= Version('2.1'):
+        self.long_description = _read_payload_from_msg(msg)
+    self.description = _read_field_from_msg(msg, 'summary')
+
+    if 'keywords' in msg:
+        self.keywords = _read_field_from_msg(msg, 'keywords').split(',')
+
+    self.platforms = _read_list_from_msg(msg, 'platform')
+    self.classifiers = _read_list_from_msg(msg, 'classifier')
+
+    # PEP 314 - these fields only exist in 1.1
+    if self.metadata_version == Version('1.1'):
+        self.requires = _read_list_from_msg(msg, 'requires')
+        self.provides = _read_list_from_msg(msg, 'provides')
+        self.obsoletes = _read_list_from_msg(msg, 'obsoletes')
+    else:
+        self.requires = None
+        self.provides = None
+        self.obsoletes = None
+
+    self.license_files = _read_list_from_msg(msg, 'license-file')
+
+
+def single_line(val):
+    """
+    Quick and dirty validation for Summary pypa/setuptools#1390.
+    """
+    if '\n' in val:
+        # TODO: Replace with `raise ValueError("newlines not allowed")`
+        # after reviewing #2893.
+        msg = "newlines are not allowed in `summary` and will break in the future"
+        SetuptoolsDeprecationWarning.emit("Invalid config.", msg)
+        # due_date is undefined. Controversial change, there was a lot of push back.
+        val = val.strip().split('\n')[0]
+    return val
+
+
+def write_pkg_info(self, base_dir):
+    """Write the PKG-INFO file into the release tree."""
+    temp = ""
+    final = os.path.join(base_dir, 'PKG-INFO')
+    try:
+        # Use a temporary file while writing to avoid race conditions
+        # (e.g. `importlib.metadata` reading `.egg-info/PKG-INFO`):
+        with NamedTemporaryFile("w", encoding="utf-8", dir=base_dir, delete=False) as f:
+            temp = f.name
+            self.write_pkg_file(f)
+        permissions = stat.S_IMODE(os.lstat(temp).st_mode)
+        os.chmod(temp, permissions | stat.S_IRGRP | stat.S_IROTH)
+        os.replace(temp, final)  # atomic operation.
+    finally:
+        if temp and os.path.exists(temp):
+            os.remove(temp)
+
+
+# Based on Python 3.5 version
+def write_pkg_file(self, file):  # noqa: C901  # is too complex (14)  # FIXME
+    """Write the PKG-INFO format data to a file object."""
+    version = self.get_metadata_version()
+
+    def write_field(key, value):
+        file.write(f"{key}: {value}\n")
+
+    write_field('Metadata-Version', str(version))
+    write_field('Name', self.get_name())
+    write_field('Version', self.get_version())
+
+    summary = self.get_description()
+    if summary:
+        write_field('Summary', single_line(summary))
+
+    optional_fields = (
+        ('Home-page', 'url'),
+        ('Download-URL', 'download_url'),
+        ('Author', 'author'),
+        ('Author-email', 'author_email'),
+        ('Maintainer', 'maintainer'),
+        ('Maintainer-email', 'maintainer_email'),
+    )
+
+    for field, attr in optional_fields:
+        attr_val = getattr(self, attr, None)
+        if attr_val is not None:
+            write_field(field, attr_val)
+
+    if license_expression := self.license_expression:
+        write_field('License-Expression', license_expression)
+    elif license := self.get_license():
+        write_field('License', rfc822_escape(license))
+
+    for label, url in self.project_urls.items():
+        write_field('Project-URL', f'{label}, {url}')
+
+    keywords = ','.join(self.get_keywords())
+    if keywords:
+        write_field('Keywords', keywords)
+
+    platforms = self.get_platforms() or []
+    for platform in platforms:
+        write_field('Platform', platform)
+
+    self._write_list(file, 'Classifier', self.get_classifiers())
+
+    # PEP 314
+    self._write_list(file, 'Requires', self.get_requires())
+    self._write_list(file, 'Provides', self.get_provides())
+    self._write_list(file, 'Obsoletes', self.get_obsoletes())
+
+    # Setuptools specific for PEP 345
+    if hasattr(self, 'python_requires'):
+        write_field('Requires-Python', self.python_requires)
+
+    # PEP 566
+    if self.long_description_content_type:
+        write_field('Description-Content-Type', self.long_description_content_type)
+
+    safe_license_files = map(_safe_license_file, self.license_files or [])
+    self._write_list(file, 'License-File', safe_license_files)
+    _write_requirements(self, file)
+
+    for field, attr in _POSSIBLE_DYNAMIC_FIELDS.items():
+        if (val := getattr(self, attr, None)) and not is_static(val):
+            write_field('Dynamic', field)
+
+    long_description = self.get_long_description()
+    if long_description:
+        file.write(f"\n{long_description}")
+        if not long_description.endswith("\n"):
+            file.write("\n")
+
+
+def _write_requirements(self, file):
+    for req in _reqs.parse(self.install_requires):
+        file.write(f"Requires-Dist: {req}\n")
+
+    processed_extras = {}
+    for augmented_extra, reqs in self.extras_require.items():
+        # Historically, setuptools allows "augmented extras": `:`
+        unsafe_extra, _, condition = augmented_extra.partition(":")
+        unsafe_extra = unsafe_extra.strip()
+        extra = _normalization.safe_extra(unsafe_extra)
+
+        if extra:
+            _write_provides_extra(file, processed_extras, extra, unsafe_extra)
+        for req in _reqs.parse_strings(reqs):
+            r = _include_extra(req, extra, condition.strip())
+            file.write(f"Requires-Dist: {r}\n")
+
+    return processed_extras
+
+
+def _include_extra(req: str, extra: str, condition: str) -> Requirement:
+    r = Requirement(req)  # create a fresh object that can be modified
+    parts = (
+        f"({r.marker})" if r.marker else None,
+        f"({condition})" if condition else None,
+        f"extra == {extra!r}" if extra else None,
+    )
+    r.marker = Marker(" and ".join(x for x in parts if x))
+    return r
+
+
+def _write_provides_extra(file, processed_extras, safe, unsafe):
+    previous = processed_extras.get(safe)
+    if previous == unsafe:
+        SetuptoolsDeprecationWarning.emit(
+            'Ambiguity during "extra" normalization for dependencies.',
+            f"""
+            {previous!r} and {unsafe!r} normalize to the same value:\n
+                {safe!r}\n
+            In future versions, setuptools might halt the build process.
+            """,
+            see_url="https://peps.python.org/pep-0685/",
+        )
+    else:
+        processed_extras[safe] = unsafe
+        file.write(f"Provides-Extra: {safe}\n")
+
+
+# from pypa/distutils#244; needed only until that logic is always available
+def get_fullname(self):
+    return _distribution_fullname(self.get_name(), self.get_version())
+
+
+def _distribution_fullname(name: str, version: str) -> str:
+    """
+    >>> _distribution_fullname('setup.tools', '1.0-2')
+    'setup_tools-1.0.post2'
+    >>> _distribution_fullname('setup-tools', '1.2post2')
+    'setup_tools-1.2.post2'
+    >>> _distribution_fullname('setup-tools', '1.0-r2')
+    'setup_tools-1.0.post2'
+    >>> _distribution_fullname('setup.tools', '1.0.post')
+    'setup_tools-1.0.post0'
+    >>> _distribution_fullname('setup.tools', '1.0+ubuntu-1')
+    'setup_tools-1.0+ubuntu.1'
+    """
+    return "{}-{}".format(
+        canonicalize_name(name).replace('-', '_'),
+        canonicalize_version(version, strip_trailing_zero=False),
+    )
+
+
+def _safe_license_file(file):
+    # XXX: Do we need this after the deprecation discussed in #4892, #4896??
+    normalized = os.path.normpath(file).replace(os.sep, "/")
+    if "../" in normalized:
+        return os.path.basename(normalized)  # Temporarily restore pre PEP639 behaviour
+    return normalized
+
+
+_POSSIBLE_DYNAMIC_FIELDS = {
+    # Core Metadata Field x related Distribution attribute
+    "author": "author",
+    "author-email": "author_email",
+    "classifier": "classifiers",
+    "description": "long_description",
+    "description-content-type": "long_description_content_type",
+    "download-url": "download_url",
+    "home-page": "url",
+    "keywords": "keywords",
+    "license": "license",
+    # XXX: License-File is complicated because the user gives globs that are expanded
+    #      during the build. Without special handling it is likely always
+    #      marked as Dynamic, which is an acceptable outcome according to:
+    #      https://github.com/pypa/setuptools/issues/4629#issuecomment-2331233677
+    "license-file": "license_files",
+    "license-expression": "license_expression",  # PEP 639
+    "maintainer": "maintainer",
+    "maintainer-email": "maintainer_email",
+    "obsoletes": "obsoletes",
+    # "obsoletes-dist": "obsoletes_dist",  # NOT USED
+    "platform": "platforms",
+    "project-url": "project_urls",
+    "provides": "provides",
+    # "provides-dist": "provides_dist",  # NOT USED
+    "provides-extra": "extras_require",
+    "requires": "requires",
+    "requires-dist": "install_requires",
+    # "requires-external": "requires_external",  # NOT USED
+    "requires-python": "python_requires",
+    "summary": "description",
+    # "supported-platform": "supported_platforms",  # NOT USED
+}
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_discovery.py b/.venv/lib/python3.12/site-packages/setuptools/_discovery.py
new file mode 100644
index 0000000000000000000000000000000000000000..d1b4a0ee0351d69de5f0ab7d65bbe6958e398916
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_discovery.py
@@ -0,0 +1,33 @@
+import functools
+import operator
+
+import packaging.requirements
+
+
+# from coherent.build.discovery
+def extras_from_dep(dep):
+    try:
+        markers = packaging.requirements.Requirement(dep).marker._markers
+    except AttributeError:
+        markers = ()
+    return set(
+        marker[2].value
+        for marker in markers
+        if isinstance(marker, tuple) and marker[0].value == 'extra'
+    )
+
+
+def extras_from_deps(deps):
+    """
+    >>> extras_from_deps(['requests'])
+    set()
+    >>> extras_from_deps(['pytest; extra == "test"'])
+    {'test'}
+    >>> sorted(extras_from_deps([
+    ...     'requests',
+    ...     'pytest; extra == "test"',
+    ...     'pytest-cov; extra == "test"',
+    ...     'sphinx; extra=="doc"']))
+    ['doc', 'test']
+    """
+    return functools.reduce(operator.or_, map(extras_from_dep, deps), set())
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_entry_points.py b/.venv/lib/python3.12/site-packages/setuptools/_entry_points.py
new file mode 100644
index 0000000000000000000000000000000000000000..cd5dd2c8ac99783a2edb617278627ba170a9872b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_entry_points.py
@@ -0,0 +1,94 @@
+import functools
+import itertools
+import operator
+
+from jaraco.functools import pass_none
+from jaraco.text import yield_lines
+from more_itertools import consume
+
+from ._importlib import metadata
+from ._itertools import ensure_unique
+from .errors import OptionError
+
+
+def ensure_valid(ep):
+    """
+    Exercise one of the dynamic properties to trigger
+    the pattern match.
+
+    This function is deprecated in favor of importlib_metadata 8.7 and
+    Python 3.14 importlib.metadata, which validates entry points on
+    construction.
+    """
+    try:
+        ep.extras
+    except (AttributeError, AssertionError) as ex:
+        # Why both? See https://github.com/python/importlib_metadata/issues/488
+        msg = (
+            f"Problems to parse {ep}.\nPlease ensure entry-point follows the spec: "
+            "https://packaging.python.org/en/latest/specifications/entry-points/"
+        )
+        raise OptionError(msg) from ex
+
+
+def load_group(value, group):
+    """
+    Given a value of an entry point or series of entry points,
+    return each as an EntryPoint.
+    """
+    # normalize to a single sequence of lines
+    lines = yield_lines(value)
+    text = f'[{group}]\n' + '\n'.join(lines)
+    return metadata.EntryPoints._from_text(text)
+
+
+def by_group_and_name(ep):
+    return ep.group, ep.name
+
+
+def validate(eps: metadata.EntryPoints):
+    """
+    Ensure entry points are unique by group and name and validate each.
+    """
+    consume(map(ensure_valid, ensure_unique(eps, key=by_group_and_name)))
+    return eps
+
+
+@functools.singledispatch
+def load(eps):
+    """
+    Given a Distribution.entry_points, produce EntryPoints.
+    """
+    groups = itertools.chain.from_iterable(
+        load_group(value, group) for group, value in eps.items()
+    )
+    return validate(metadata.EntryPoints(groups))
+
+
+@load.register(str)
+def _(eps):
+    r"""
+    >>> ep, = load('[console_scripts]\nfoo=bar')
+    >>> ep.group
+    'console_scripts'
+    >>> ep.name
+    'foo'
+    >>> ep.value
+    'bar'
+    """
+    return validate(metadata.EntryPoints(metadata.EntryPoints._from_text(eps)))
+
+
+load.register(type(None), lambda x: x)
+
+
+@pass_none
+def render(eps: metadata.EntryPoints):
+    by_group = operator.attrgetter('group')
+    groups = itertools.groupby(sorted(eps, key=by_group), by_group)
+
+    return '\n'.join(f'[{group}]\n{render_items(items)}\n' for group, items in groups)
+
+
+def render_items(eps):
+    return '\n'.join(f'{ep.name} = {ep.value}' for ep in sorted(eps))
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_imp.py b/.venv/lib/python3.12/site-packages/setuptools/_imp.py
new file mode 100644
index 0000000000000000000000000000000000000000..f1d9f29218987d4f830f2d57aca9e3f74d00a095
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_imp.py
@@ -0,0 +1,87 @@
+"""
+Re-implementation of find_module and get_frozen_object
+from the deprecated imp module.
+"""
+
+import importlib.machinery
+import importlib.util
+import os
+import tokenize
+from importlib.util import module_from_spec
+
+PY_SOURCE = 1
+PY_COMPILED = 2
+C_EXTENSION = 3
+C_BUILTIN = 6
+PY_FROZEN = 7
+
+
+def find_spec(module, paths):
+    finder = (
+        importlib.machinery.PathFinder().find_spec
+        if isinstance(paths, list)
+        else importlib.util.find_spec
+    )
+    return finder(module, paths)
+
+
+def find_module(module, paths=None):
+    """Just like 'imp.find_module()', but with package support"""
+    spec = find_spec(module, paths)
+    if spec is None:
+        raise ImportError(f"Can't find {module}")
+    if not spec.has_location and hasattr(spec, 'submodule_search_locations'):
+        spec = importlib.util.spec_from_loader('__init__.py', spec.loader)
+
+    kind = -1
+    file = None
+    static = isinstance(spec.loader, type)
+    if (
+        spec.origin == 'frozen'
+        or static
+        and issubclass(spec.loader, importlib.machinery.FrozenImporter)
+    ):
+        kind = PY_FROZEN
+        path = None  # imp compabilty
+        suffix = mode = ''  # imp compatibility
+    elif (
+        spec.origin == 'built-in'
+        or static
+        and issubclass(spec.loader, importlib.machinery.BuiltinImporter)
+    ):
+        kind = C_BUILTIN
+        path = None  # imp compabilty
+        suffix = mode = ''  # imp compatibility
+    elif spec.has_location:
+        path = spec.origin
+        suffix = os.path.splitext(path)[1]
+        mode = 'r' if suffix in importlib.machinery.SOURCE_SUFFIXES else 'rb'
+
+        if suffix in importlib.machinery.SOURCE_SUFFIXES:
+            kind = PY_SOURCE
+            file = tokenize.open(path)
+        elif suffix in importlib.machinery.BYTECODE_SUFFIXES:
+            kind = PY_COMPILED
+            file = open(path, 'rb')
+        elif suffix in importlib.machinery.EXTENSION_SUFFIXES:
+            kind = C_EXTENSION
+
+    else:
+        path = None
+        suffix = mode = ''
+
+    return file, path, (suffix, mode, kind)
+
+
+def get_frozen_object(module, paths=None):
+    spec = find_spec(module, paths)
+    if not spec:
+        raise ImportError(f"Can't find {module}")
+    return spec.loader.get_code(module)
+
+
+def get_module(module, paths, info):
+    spec = find_spec(module, paths)
+    if not spec:
+        raise ImportError(f"Can't find {module}")
+    return module_from_spec(spec)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_importlib.py b/.venv/lib/python3.12/site-packages/setuptools/_importlib.py
new file mode 100644
index 0000000000000000000000000000000000000000..ce0fd52653b56c9c2cb2b2c7bfb35e3ec3c61408
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_importlib.py
@@ -0,0 +1,9 @@
+import sys
+
+if sys.version_info < (3, 10):
+    import importlib_metadata as metadata  # pragma: no cover
+else:
+    import importlib.metadata as metadata  # noqa: F401
+
+
+import importlib.resources as resources  # noqa: F401
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_itertools.py b/.venv/lib/python3.12/site-packages/setuptools/_itertools.py
new file mode 100644
index 0000000000000000000000000000000000000000..d6ca841353ce39ac4361013f5c8160d69028d0d8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_itertools.py
@@ -0,0 +1,23 @@
+from more_itertools import consume  # noqa: F401
+
+
+# copied from jaraco.itertools 6.1
+def ensure_unique(iterable, key=lambda x: x):
+    """
+    Wrap an iterable to raise a ValueError if non-unique values are encountered.
+
+    >>> list(ensure_unique('abc'))
+    ['a', 'b', 'c']
+    >>> consume(ensure_unique('abca'))
+    Traceback (most recent call last):
+    ...
+    ValueError: Duplicate element 'a' encountered.
+    """
+    seen = set()
+    seen_add = seen.add
+    for element in iterable:
+        k = key(element)
+        if k in seen:
+            raise ValueError(f"Duplicate element {element!r} encountered.")
+        seen_add(k)
+        yield element
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_normalization.py b/.venv/lib/python3.12/site-packages/setuptools/_normalization.py
new file mode 100644
index 0000000000000000000000000000000000000000..fb89323c9db4a27ee0a07cd5b5437303774bab0b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_normalization.py
@@ -0,0 +1,177 @@
+"""
+Helpers for normalization as expected in wheel/sdist/module file names
+and core metadata
+"""
+
+import re
+from typing import TYPE_CHECKING
+
+import packaging
+
+# https://packaging.python.org/en/latest/specifications/core-metadata/#name
+_VALID_NAME = re.compile(r"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$", re.I)
+_UNSAFE_NAME_CHARS = re.compile(r"[^A-Z0-9._-]+", re.I)
+_NON_ALPHANUMERIC = re.compile(r"[^A-Z0-9]+", re.I)
+_PEP440_FALLBACK = re.compile(r"^v?(?P(?:[0-9]+!)?[0-9]+(?:\.[0-9]+)*)", re.I)
+
+
+def safe_identifier(name: str) -> str:
+    """Make a string safe to be used as Python identifier.
+    >>> safe_identifier("12abc")
+    '_12abc'
+    >>> safe_identifier("__editable__.myns.pkg-78.9.3_local")
+    '__editable___myns_pkg_78_9_3_local'
+    """
+    safe = re.sub(r'\W|^(?=\d)', '_', name)
+    assert safe.isidentifier()
+    return safe
+
+
+def safe_name(component: str) -> str:
+    """Escape a component used as a project name according to Core Metadata.
+    >>> safe_name("hello world")
+    'hello-world'
+    >>> safe_name("hello?world")
+    'hello-world'
+    >>> safe_name("hello_world")
+    'hello_world'
+    """
+    return _UNSAFE_NAME_CHARS.sub("-", component)
+
+
+def safe_version(version: str) -> str:
+    """Convert an arbitrary string into a valid version string.
+    Can still raise an ``InvalidVersion`` exception.
+    To avoid exceptions use ``best_effort_version``.
+    >>> safe_version("1988 12 25")
+    '1988.12.25'
+    >>> safe_version("v0.2.1")
+    '0.2.1'
+    >>> safe_version("v0.2?beta")
+    '0.2b0'
+    >>> safe_version("v0.2 beta")
+    '0.2b0'
+    >>> safe_version("ubuntu lts")
+    Traceback (most recent call last):
+    ...
+    packaging.version.InvalidVersion: Invalid version: 'ubuntu.lts'
+    """
+    v = version.replace(' ', '.')
+    try:
+        return str(packaging.version.Version(v))
+    except packaging.version.InvalidVersion:
+        attempt = _UNSAFE_NAME_CHARS.sub("-", v)
+        return str(packaging.version.Version(attempt))
+
+
+def best_effort_version(version: str) -> str:
+    """Convert an arbitrary string into a version-like string.
+    Fallback when ``safe_version`` is not safe enough.
+    >>> best_effort_version("v0.2 beta")
+    '0.2b0'
+    >>> best_effort_version("ubuntu lts")
+    '0.dev0+sanitized.ubuntu.lts'
+    >>> best_effort_version("0.23ubuntu1")
+    '0.23.dev0+sanitized.ubuntu1'
+    >>> best_effort_version("0.23-")
+    '0.23.dev0+sanitized'
+    >>> best_effort_version("0.-_")
+    '0.dev0+sanitized'
+    >>> best_effort_version("42.+?1")
+    '42.dev0+sanitized.1'
+    """
+    try:
+        return safe_version(version)
+    except packaging.version.InvalidVersion:
+        v = version.replace(' ', '.')
+        match = _PEP440_FALLBACK.search(v)
+        if match:
+            safe = match["safe"]
+            rest = v[len(safe) :]
+        else:
+            safe = "0"
+            rest = version
+        safe_rest = _NON_ALPHANUMERIC.sub(".", rest).strip(".")
+        local = f"sanitized.{safe_rest}".strip(".")
+        return safe_version(f"{safe}.dev0+{local}")
+
+
+def safe_extra(extra: str) -> str:
+    """Normalize extra name according to PEP 685
+    >>> safe_extra("_FrIeNdLy-._.-bArD")
+    'friendly-bard'
+    >>> safe_extra("FrIeNdLy-._.-bArD__._-")
+    'friendly-bard'
+    """
+    return _NON_ALPHANUMERIC.sub("-", extra).strip("-").lower()
+
+
+def filename_component(value: str) -> str:
+    """Normalize each component of a filename (e.g. distribution/version part of wheel)
+    Note: ``value`` needs to be already normalized.
+    >>> filename_component("my-pkg")
+    'my_pkg'
+    """
+    return value.replace("-", "_").strip("_")
+
+
+def filename_component_broken(value: str) -> str:
+    """
+    Produce the incorrect filename component for compatibility.
+
+    See pypa/setuptools#4167 for detailed analysis.
+
+    TODO: replace this with filename_component after pip 24 is
+    nearly-ubiquitous.
+
+    >>> filename_component_broken('foo_bar-baz')
+    'foo-bar-baz'
+    """
+    return value.replace('_', '-')
+
+
+def safer_name(value: str) -> str:
+    """Like ``safe_name`` but can be used as filename component for wheel"""
+    # See bdist_wheel.safer_name
+    return (
+        # Per https://packaging.python.org/en/latest/specifications/name-normalization/#name-normalization
+        re.sub(r"[-_.]+", "-", safe_name(value))
+        .lower()
+        # Per https://packaging.python.org/en/latest/specifications/binary-distribution-format/#escaping-and-unicode
+        .replace("-", "_")
+    )
+
+
+def safer_best_effort_version(value: str) -> str:
+    """Like ``best_effort_version`` but can be used as filename component for wheel"""
+    # See bdist_wheel.safer_verion
+    # TODO: Replace with only safe_version in the future (no need for best effort)
+    return filename_component(best_effort_version(value))
+
+
+def _missing_canonicalize_license_expression(expression: str) -> str:
+    """
+    Defer import error to affect only users that actually use it
+    https://github.com/pypa/setuptools/issues/4894
+    >>> _missing_canonicalize_license_expression("a OR b")
+    Traceback (most recent call last):
+    ...
+    ImportError: ...Cannot import `packaging.licenses`...
+    """
+    raise ImportError(
+        "Cannot import `packaging.licenses`."
+        """
+        Setuptools>=77.0.0 requires "packaging>=24.2" to work properly.
+        Please make sure you have a suitable version installed.
+        """
+    )
+
+
+try:
+    from packaging.licenses import (
+        canonicalize_license_expression as _canonicalize_license_expression,
+    )
+except ImportError:  # pragma: nocover
+    if not TYPE_CHECKING:
+        # XXX: pyright is still upset even with # pyright: ignore[reportAssignmentType]
+        _canonicalize_license_expression = _missing_canonicalize_license_expression
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_path.py b/.venv/lib/python3.12/site-packages/setuptools/_path.py
new file mode 100644
index 0000000000000000000000000000000000000000..2b78022934d89599e642a10f861b382947031be9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_path.py
@@ -0,0 +1,93 @@
+from __future__ import annotations
+
+import contextlib
+import os
+import sys
+from typing import TYPE_CHECKING, TypeVar, Union
+
+from more_itertools import unique_everseen
+
+if TYPE_CHECKING:
+    from typing_extensions import TypeAlias
+
+StrPath: TypeAlias = Union[str, os.PathLike[str]]  #  Same as _typeshed.StrPath
+StrPathT = TypeVar("StrPathT", bound=Union[str, os.PathLike[str]])
+
+
+def ensure_directory(path):
+    """Ensure that the parent directory of `path` exists"""
+    dirname = os.path.dirname(path)
+    os.makedirs(dirname, exist_ok=True)
+
+
+def same_path(p1: StrPath, p2: StrPath) -> bool:
+    """Differs from os.path.samefile because it does not require paths to exist.
+    Purely string based (no comparison between i-nodes).
+    >>> same_path("a/b", "./a/b")
+    True
+    >>> same_path("a/b", "a/./b")
+    True
+    >>> same_path("a/b", "././a/b")
+    True
+    >>> same_path("a/b", "./a/b/c/..")
+    True
+    >>> same_path("a/b", "../a/b/c")
+    False
+    >>> same_path("a", "a/b")
+    False
+    """
+    return normpath(p1) == normpath(p2)
+
+
+def _cygwin_patch(filename: StrPath):  # pragma: nocover
+    """
+    Contrary to POSIX 2008, on Cygwin, getcwd (3) contains
+    symlink components. Using
+    os.path.abspath() works around this limitation. A fix in os.getcwd()
+    would probably better, in Cygwin even more so, except
+    that this seems to be by design...
+    """
+    return os.path.abspath(filename) if sys.platform == 'cygwin' else filename
+
+
+def normpath(filename: StrPath) -> str:
+    """Normalize a file/dir name for comparison purposes."""
+    return os.path.normcase(os.path.realpath(os.path.normpath(_cygwin_patch(filename))))
+
+
+@contextlib.contextmanager
+def paths_on_pythonpath(paths):
+    """
+    Add the indicated paths to the head of the PYTHONPATH environment
+    variable so that subprocesses will also see the packages at
+    these paths.
+
+    Do this in a context that restores the value on exit.
+
+    >>> getfixture('monkeypatch').setenv('PYTHONPATH', 'anything')
+    >>> with paths_on_pythonpath(['foo', 'bar']):
+    ...     assert 'foo' in os.environ['PYTHONPATH']
+    ...     assert 'anything' in os.environ['PYTHONPATH']
+    >>> os.environ['PYTHONPATH']
+    'anything'
+
+    >>> getfixture('monkeypatch').delenv('PYTHONPATH')
+    >>> with paths_on_pythonpath(['foo', 'bar']):
+    ...     assert 'foo' in os.environ['PYTHONPATH']
+    >>> os.environ.get('PYTHONPATH')
+    """
+    nothing = object()
+    orig_pythonpath = os.environ.get('PYTHONPATH', nothing)
+    current_pythonpath = os.environ.get('PYTHONPATH', '')
+    try:
+        prefix = os.pathsep.join(unique_everseen(paths))
+        to_join = filter(None, [prefix, current_pythonpath])
+        new_path = os.pathsep.join(to_join)
+        if new_path:
+            os.environ['PYTHONPATH'] = new_path
+        yield
+    finally:
+        if orig_pythonpath is nothing:
+            os.environ.pop('PYTHONPATH', None)
+        else:
+            os.environ['PYTHONPATH'] = orig_pythonpath
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_reqs.py b/.venv/lib/python3.12/site-packages/setuptools/_reqs.py
new file mode 100644
index 0000000000000000000000000000000000000000..7be56cbf35d4a7cbeacdca1c18599433d7fb2f7f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_reqs.py
@@ -0,0 +1,42 @@
+from __future__ import annotations
+
+from collections.abc import Iterable, Iterator
+from functools import lru_cache
+from typing import TYPE_CHECKING, Callable, TypeVar, Union, overload
+
+import jaraco.text as text
+from packaging.requirements import Requirement
+
+if TYPE_CHECKING:
+    from typing_extensions import TypeAlias
+
+_T = TypeVar("_T")
+_StrOrIter: TypeAlias = Union[str, Iterable[str]]
+
+
+parse_req: Callable[[str], Requirement] = lru_cache()(Requirement)
+# Setuptools parses the same requirement many times
+# (e.g. first for validation than for normalisation),
+# so it might be worth to cache.
+
+
+def parse_strings(strs: _StrOrIter) -> Iterator[str]:
+    """
+    Yield requirement strings for each specification in `strs`.
+
+    `strs` must be a string, or a (possibly-nested) iterable thereof.
+    """
+    return text.join_continuation(map(text.drop_comment, text.yield_lines(strs)))
+
+
+# These overloads are only needed because of a mypy false-positive, pyright gets it right
+# https://github.com/python/mypy/issues/3737
+@overload
+def parse(strs: _StrOrIter) -> Iterator[Requirement]: ...
+@overload
+def parse(strs: _StrOrIter, parser: Callable[[str], _T]) -> Iterator[_T]: ...
+def parse(strs: _StrOrIter, parser: Callable[[str], _T] = parse_req) -> Iterator[_T]:  # type: ignore[assignment]
+    """
+    Parse requirements.
+    """
+    return map(parser, parse_strings(strs))
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_scripts.py b/.venv/lib/python3.12/site-packages/setuptools/_scripts.py
new file mode 100644
index 0000000000000000000000000000000000000000..88bf02f927ba7cb47731fc0984f4b4f135fbdd45
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_scripts.py
@@ -0,0 +1,361 @@
+from __future__ import annotations
+
+import os
+import re
+import shlex
+import shutil
+import struct
+import subprocess
+import sys
+import textwrap
+from collections.abc import Iterable
+from typing import TYPE_CHECKING, TypedDict
+
+from ._importlib import metadata, resources
+
+if TYPE_CHECKING:
+    from typing_extensions import Self
+
+from .warnings import SetuptoolsWarning
+
+from distutils.command.build_scripts import first_line_re
+from distutils.util import get_platform
+
+
+class _SplitArgs(TypedDict, total=False):
+    comments: bool
+    posix: bool
+
+
+class CommandSpec(list):
+    """
+    A command spec for a #! header, specified as a list of arguments akin to
+    those passed to Popen.
+    """
+
+    options: list[str] = []
+    split_args = _SplitArgs()
+
+    @classmethod
+    def best(cls):
+        """
+        Choose the best CommandSpec class based on environmental conditions.
+        """
+        return cls
+
+    @classmethod
+    def _sys_executable(cls):
+        _default = os.path.normpath(sys.executable)
+        return os.environ.get('__PYVENV_LAUNCHER__', _default)
+
+    @classmethod
+    def from_param(cls, param: Self | str | Iterable[str] | None) -> Self:
+        """
+        Construct a CommandSpec from a parameter to build_scripts, which may
+        be None.
+        """
+        if isinstance(param, cls):
+            return param
+        if isinstance(param, str):
+            return cls.from_string(param)
+        if isinstance(param, Iterable):
+            return cls(param)
+        if param is None:
+            return cls.from_environment()
+        raise TypeError(f"Argument has an unsupported type {type(param)}")
+
+    @classmethod
+    def from_environment(cls):
+        return cls([cls._sys_executable()])
+
+    @classmethod
+    def from_string(cls, string: str) -> Self:
+        """
+        Construct a command spec from a simple string representing a command
+        line parseable by shlex.split.
+        """
+        items = shlex.split(string, **cls.split_args)
+        return cls(items)
+
+    def install_options(self, script_text: str):
+        self.options = shlex.split(self._extract_options(script_text))
+        cmdline = subprocess.list2cmdline(self)
+        if not isascii(cmdline):
+            self.options[:0] = ['-x']
+
+    @staticmethod
+    def _extract_options(orig_script):
+        """
+        Extract any options from the first line of the script.
+        """
+        first = (orig_script + '\n').splitlines()[0]
+        match = _first_line_re().match(first)
+        options = match.group(1) or '' if match else ''
+        return options.strip()
+
+    def as_header(self):
+        return self._render(self + list(self.options))
+
+    @staticmethod
+    def _strip_quotes(item):
+        _QUOTES = '"\''
+        for q in _QUOTES:
+            if item.startswith(q) and item.endswith(q):
+                return item[1:-1]
+        return item
+
+    @staticmethod
+    def _render(items):
+        cmdline = subprocess.list2cmdline(
+            CommandSpec._strip_quotes(item.strip()) for item in items
+        )
+        return '#!' + cmdline + '\n'
+
+
+class WindowsCommandSpec(CommandSpec):
+    split_args = _SplitArgs(posix=False)
+
+
+class ScriptWriter:
+    """
+    Encapsulates behavior around writing entry point scripts for console and
+    gui apps.
+    """
+
+    template = textwrap.dedent(
+        r"""
+        # EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r
+        import re
+        import sys
+
+        # for compatibility with easy_install; see #2198
+        __requires__ = %(spec)r
+
+        try:
+            from importlib.metadata import distribution
+        except ImportError:
+            try:
+                from importlib_metadata import distribution
+            except ImportError:
+                from pkg_resources import load_entry_point
+
+
+        def importlib_load_entry_point(spec, group, name):
+            dist_name, _, _ = spec.partition('==')
+            matches = (
+                entry_point
+                for entry_point in distribution(dist_name).entry_points
+                if entry_point.group == group and entry_point.name == name
+            )
+            return next(matches).load()
+
+
+        globals().setdefault('load_entry_point', importlib_load_entry_point)
+
+
+        if __name__ == '__main__':
+            sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
+            sys.exit(load_entry_point(%(spec)r, %(group)r, %(name)r)())
+        """
+    ).lstrip()
+
+    command_spec_class = CommandSpec
+
+    @classmethod
+    def get_args(cls, dist, header=None):
+        """
+        Yield write_script() argument tuples for a distribution's
+        console_scripts and gui_scripts entry points.
+        """
+
+        # If distribution is not an importlib.metadata.Distribution, assume
+        # it's a pkg_resources.Distribution and transform it.
+        if not hasattr(dist, 'entry_points'):
+            SetuptoolsWarning.emit("Unsupported distribution encountered.")
+            dist = metadata.Distribution.at(dist.egg_info)
+
+        if header is None:
+            header = cls.get_header()
+        spec = f'{dist.name}=={dist.version}'
+        for type_ in 'console', 'gui':
+            group = f'{type_}_scripts'
+            for ep in dist.entry_points.select(group=group):
+                name = ep.name
+                cls._ensure_safe_name(ep.name)
+                script_text = cls.template % locals()
+                args = cls._get_script_args(type_, ep.name, header, script_text)
+                yield from args
+
+    @staticmethod
+    def _ensure_safe_name(name):
+        """
+        Prevent paths in *_scripts entry point names.
+        """
+        has_path_sep = re.search(r'[\\/]', name)
+        if has_path_sep:
+            raise ValueError("Path separators not allowed in script names")
+
+    @classmethod
+    def best(cls):
+        """
+        Select the best ScriptWriter for this environment.
+        """
+        if sys.platform == 'win32' or (os.name == 'java' and os._name == 'nt'):
+            return WindowsScriptWriter.best()
+        else:
+            return cls
+
+    @classmethod
+    def _get_script_args(cls, type_, name, header, script_text):
+        # Simply write the stub with no extension.
+        yield (name, header + script_text)
+
+    @classmethod
+    def get_header(
+        cls,
+        script_text: str = "",
+        executable: str | CommandSpec | Iterable[str] | None = None,
+    ) -> str:
+        """Create a #! line, getting options (if any) from script_text"""
+        cmd = cls.command_spec_class.best().from_param(executable)
+        cmd.install_options(script_text)
+        return cmd.as_header()
+
+
+class WindowsScriptWriter(ScriptWriter):
+    command_spec_class = WindowsCommandSpec
+
+    @classmethod
+    def best(cls):
+        """
+        Select the best ScriptWriter suitable for Windows
+        """
+        writer_lookup = dict(
+            executable=WindowsExecutableLauncherWriter,
+            natural=cls,
+        )
+        # for compatibility, use the executable launcher by default
+        launcher = os.environ.get('SETUPTOOLS_LAUNCHER', 'executable')
+        return writer_lookup[launcher]
+
+    @classmethod
+    def _get_script_args(cls, type_, name, header, script_text):
+        "For Windows, add a .py extension"
+        ext = dict(console='.pya', gui='.pyw')[type_]
+        if ext not in os.environ['PATHEXT'].lower().split(';'):
+            msg = (
+                "{ext} not listed in PATHEXT; scripts will not be "
+                "recognized as executables."
+            ).format(**locals())
+            SetuptoolsWarning.emit(msg)
+        old = ['.pya', '.py', '-script.py', '.pyc', '.pyo', '.pyw', '.exe']
+        old.remove(ext)
+        header = cls._adjust_header(type_, header)
+        blockers = [name + x for x in old]
+        yield name + ext, header + script_text, 't', blockers
+
+    @classmethod
+    def _adjust_header(cls, type_, orig_header):
+        """
+        Make sure 'pythonw' is used for gui and 'python' is used for
+        console (regardless of what sys.executable is).
+        """
+        pattern = 'pythonw.exe'
+        repl = 'python.exe'
+        if type_ == 'gui':
+            pattern, repl = repl, pattern
+        pattern_ob = re.compile(re.escape(pattern), re.IGNORECASE)
+        new_header = pattern_ob.sub(string=orig_header, repl=repl)
+        return new_header if cls._use_header(new_header) else orig_header
+
+    @staticmethod
+    def _use_header(new_header):
+        """
+        Should _adjust_header use the replaced header?
+
+        On non-windows systems, always use. On
+        Windows systems, only use the replaced header if it resolves
+        to an executable on the system.
+        """
+        clean_header = new_header[2:-1].strip('"')
+        return sys.platform != 'win32' or shutil.which(clean_header)
+
+
+class WindowsExecutableLauncherWriter(WindowsScriptWriter):
+    @classmethod
+    def _get_script_args(cls, type_, name, header, script_text):
+        """
+        For Windows, add a .py extension and an .exe launcher
+        """
+        if type_ == 'gui':
+            launcher_type = 'gui'
+            ext = '-script.pyw'
+            old = ['.pyw']
+        else:
+            launcher_type = 'cli'
+            ext = '-script.py'
+            old = ['.py', '.pyc', '.pyo']
+        hdr = cls._adjust_header(type_, header)
+        blockers = [name + x for x in old]
+        yield (name + ext, hdr + script_text, 't', blockers)
+        yield (
+            name + '.exe',
+            get_win_launcher(launcher_type),
+            'b',  # write in binary mode
+        )
+        if not is_64bit():
+            # install a manifest for the launcher to prevent Windows
+            # from detecting it as an installer (which it will for
+            #  launchers like easy_install.exe). Consider only
+            #  adding a manifest for launchers detected as installers.
+            #  See Distribute #143 for details.
+            m_name = name + '.exe.manifest'
+            yield (m_name, load_launcher_manifest(name), 't')
+
+
+def get_win_launcher(type):
+    """
+    Load the Windows launcher (executable) suitable for launching a script.
+
+    `type` should be either 'cli' or 'gui'
+
+    Returns the executable as a byte string.
+    """
+    launcher_fn = f'{type}.exe'
+    if is_64bit():
+        if get_platform() == "win-arm64":
+            launcher_fn = launcher_fn.replace(".", "-arm64.")
+        else:
+            launcher_fn = launcher_fn.replace(".", "-64.")
+    else:
+        launcher_fn = launcher_fn.replace(".", "-32.")
+    return resources.files('setuptools').joinpath(launcher_fn).read_bytes()
+
+
+def load_launcher_manifest(name):
+    res = resources.files(__name__).joinpath('launcher manifest.xml')
+    return res.read_text(encoding='utf-8') % vars()
+
+
+def _first_line_re():
+    """
+    Return a regular expression based on first_line_re suitable for matching
+    strings.
+    """
+    if isinstance(first_line_re.pattern, str):
+        return first_line_re
+
+    # first_line_re in Python >=3.1.4 and >=3.2.1 is a bytes pattern.
+    return re.compile(first_line_re.pattern.decode())
+
+
+def is_64bit():
+    return struct.calcsize("P") == 8
+
+
+def isascii(s):
+    try:
+        s.encode('ascii')
+    except UnicodeError:
+        return False
+    return True
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_shutil.py b/.venv/lib/python3.12/site-packages/setuptools/_shutil.py
new file mode 100644
index 0000000000000000000000000000000000000000..660459a1102eed0da597ebd7c1a38cb4eee99791
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_shutil.py
@@ -0,0 +1,59 @@
+"""Convenience layer on top of stdlib's shutil and os"""
+
+import os
+import stat
+from typing import Callable, TypeVar
+
+from .compat import py311
+
+from distutils import log
+
+try:
+    from os import chmod  # pyright: ignore[reportAssignmentType]
+    # Losing type-safety w/ pyright, but that's ok
+except ImportError:  # pragma: no cover
+    # Jython compatibility
+    def chmod(*args: object, **kwargs: object) -> None:  # type: ignore[misc] # Mypy reuses the imported definition anyway
+        pass
+
+
+_T = TypeVar("_T")
+
+
+def attempt_chmod_verbose(path, mode):
+    log.debug("changing mode of %s to %o", path, mode)
+    try:
+        chmod(path, mode)
+    except OSError as e:  # pragma: no cover
+        log.debug("chmod failed: %s", e)
+
+
+# Must match shutil._OnExcCallback
+def _auto_chmod(
+    func: Callable[..., _T], arg: str, exc: BaseException
+) -> _T:  # pragma: no cover
+    """shutils onexc callback to automatically call chmod for certain functions."""
+    # Only retry for scenarios known to have an issue
+    if func in [os.unlink, os.remove] and os.name == 'nt':
+        attempt_chmod_verbose(arg, stat.S_IWRITE)
+        return func(arg)
+    raise exc
+
+
+def rmtree(path, ignore_errors=False, onexc=_auto_chmod):
+    """
+    Similar to ``shutil.rmtree`` but automatically executes ``chmod``
+    for well know Windows failure scenarios.
+    """
+    return py311.shutil_rmtree(path, ignore_errors, onexc)
+
+
+def rmdir(path, **opts):
+    if os.path.isdir(path):
+        rmtree(path, **opts)
+
+
+def current_umask():
+    tmp = os.umask(0o022)
+    os.umask(tmp)
+    return tmp
diff --git a/.venv/lib/python3.12/site-packages/setuptools/_static.py b/.venv/lib/python3.12/site-packages/setuptools/_static.py
new file mode 100644
index 0000000000000000000000000000000000000000..af35862cf8b759a0e60110ce9e92bfdb1b49bc5f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/_static.py
@@ -0,0 +1,188 @@
+from functools import wraps
+from typing import TypeVar
+
+import packaging.specifiers
+
+from .warnings import SetuptoolsDeprecationWarning
+
+
+class Static:
+    """
+    Wrapper for built-in object types that are allow setuptools to identify
+    static core metadata (in opposition to ``Dynamic``, as defined :pep:`643`).
+
+    The trick is to mark values with :class:`Static` when they come from
+    ``pyproject.toml`` or ``setup.cfg``, so if any plugin overwrite the value
+    with a built-in, setuptools will be able to recognise the change.
+
+    We inherit from built-in classes, so that we don't need to change the existing
+    code base to deal with the new types.
+    We also should strive for immutability objects to avoid changes after the
+    initial parsing.
+    """
+
+    _mutated_: bool = False  # TODO: Remove after deprecation warning is solved
+
+
+def _prevent_modification(target: type, method: str, copying: str) -> None:
+    """
+    Because setuptools is very flexible we cannot fully prevent
+    plugins and user customizations from modifying static values that were
+    parsed from config files.
+    But we can attempt to block "in-place" mutations and identify when they
+    were done.
+    """
+    fn = getattr(target, method, None)
+    if fn is None:
+        return
+
+    @wraps(fn)
+    def _replacement(self: Static, *args, **kwargs):
+        # TODO: After deprecation period raise NotImplementedError instead of warning
+        #       which obviated the existence and checks of the `_mutated_` attribute.
+        self._mutated_ = True
+        SetuptoolsDeprecationWarning.emit(
+            "Direct modification of value will be disallowed",
+            f"""
+            In an effort to implement PEP 643, direct/in-place changes of static values
+            that come from configuration files are deprecated.
+            If you need to modify this value, please first create a copy with {copying}
+            and make sure conform to all relevant standards when overriding setuptools
+            functionality (https://packaging.python.org/en/latest/specifications/).
+            """,
+            due_date=(2025, 10, 10),  # Initially introduced in 2024-09-06
+        )
+        return fn(self, *args, **kwargs)
+
+    _replacement.__doc__ = ""  # otherwise doctest may fail.
+    setattr(target, method, _replacement)
+
+
+class Str(str, Static):
+    pass
+
+
+class Tuple(tuple, Static):
+    pass
+
+
+class List(list, Static):
+    """
+    :meta private:
+    >>> x = List([1, 2, 3])
+    >>> is_static(x)
+    True
+    >>> x += [0]  # doctest: +IGNORE_EXCEPTION_DETAIL
+    Traceback (most recent call last):
+    SetuptoolsDeprecationWarning: Direct modification ...
+    >>> is_static(x)  # no longer static after modification
+    False
+    >>> y = list(x)
+    >>> y.clear()
+    >>> y
+    []
+    >>> y == x
+    False
+    >>> is_static(List(y))
+    True
+    """
+
+
+# Make `List` immutable-ish
+# (certain places of setuptools/distutils issue a warn if we use tuple instead of list)
+for _method in (
+    '__delitem__',
+    '__iadd__',
+    '__setitem__',
+    'append',
+    'clear',
+    'extend',
+    'insert',
+    'remove',
+    'reverse',
+    'pop',
+):
+    _prevent_modification(List, _method, "`list(value)`")
+
+
+class Dict(dict, Static):
+    """
+    :meta private:
+    >>> x = Dict({'a': 1, 'b': 2})
+    >>> is_static(x)
+    True
+    >>> x['c'] = 0  # doctest: +IGNORE_EXCEPTION_DETAIL
+    Traceback (most recent call last):
+    SetuptoolsDeprecationWarning: Direct modification ...
+    >>> x._mutated_
+    True
+    >>> is_static(x)  # no longer static after modification
+    False
+    >>> y = dict(x)
+    >>> y.popitem()
+    ('b', 2)
+    >>> y == x
+    False
+    >>> is_static(Dict(y))
+    True
+    """
+
+
+# Make `Dict` immutable-ish (we cannot inherit from types.MappingProxyType):
+for _method in (
+    '__delitem__',
+    '__ior__',
+    '__setitem__',
+    'clear',
+    'pop',
+    'popitem',
+    'setdefault',
+    'update',
+):
+    _prevent_modification(Dict, _method, "`dict(value)`")
+
+
+class SpecifierSet(packaging.specifiers.SpecifierSet, Static):
+    """Not exactly a built-in type but useful for ``requires-python``"""
+
+
+T = TypeVar("T")
+
+
+def noop(value: T) -> T:
+    """
+    >>> noop(42)
+    42
+    """
+    return value
+
+
+_CONVERSIONS = {str: Str, tuple: Tuple, list: List, dict: Dict}
+
+
+def attempt_conversion(value: T) -> T:
+    """
+    >>> is_static(attempt_conversion("hello"))
+    True
+    >>> is_static(object())
+    False
+    """
+    return _CONVERSIONS.get(type(value), noop)(value)  # type: ignore[call-overload]
+
+
+def is_static(value: object) -> bool:
+    """
+    >>> is_static(a := Dict({'a': 1}))
+    True
+    >>> is_static(dict(a))
+    False
+    >>> is_static(b := List([1, 2, 3]))
+    True
+    >>> is_static(list(b))
+    False
+    """
+    return isinstance(value, Static) and not value._mutated_
+
+
+EMPTY_LIST = List()
+EMPTY_DICT = Dict()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/archive_util.py b/.venv/lib/python3.12/site-packages/setuptools/archive_util.py
new file mode 100644
index 0000000000000000000000000000000000000000..1a02010bb2af2be0487730d6a32080877b9ac220
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/archive_util.py
@@ -0,0 +1,219 @@
+"""Utilities for extracting common archive formats"""
+
+import contextlib
+import os
+import posixpath
+import shutil
+import tarfile
+import zipfile
+
+from ._path import ensure_directory
+
+from distutils.errors import DistutilsError
+
+__all__ = [
+    "unpack_archive",
+    "unpack_zipfile",
+    "unpack_tarfile",
+    "default_filter",
+    "UnrecognizedFormat",
+    "extraction_drivers",
+    "unpack_directory",
+]
+
+
+class UnrecognizedFormat(DistutilsError):
+    """Couldn't recognize the archive type"""
+
+
+def default_filter(src, dst):
+    """The default progress/filter callback; returns True for all files"""
+    return dst
+
+
+def unpack_archive(
+    filename, extract_dir, progress_filter=default_filter, drivers=None
+) -> None:
+    """Unpack `filename` to `extract_dir`, or raise ``UnrecognizedFormat``
+
+    `progress_filter` is a function taking two arguments: a source path
+    internal to the archive ('/'-separated), and a filesystem path where it
+    will be extracted.  The callback must return the desired extract path
+    (which may be the same as the one passed in), or else ``None`` to skip
+    that file or directory.  The callback can thus be used to report on the
+    progress of the extraction, as well as to filter the items extracted or
+    alter their extraction paths.
+
+    `drivers`, if supplied, must be a non-empty sequence of functions with the
+    same signature as this function (minus the `drivers` argument), that raise
+    ``UnrecognizedFormat`` if they do not support extracting the designated
+    archive type.  The `drivers` are tried in sequence until one is found that
+    does not raise an error, or until all are exhausted (in which case
+    ``UnrecognizedFormat`` is raised).  If you do not supply a sequence of
+    drivers, the module's ``extraction_drivers`` constant will be used, which
+    means that ``unpack_zipfile`` and ``unpack_tarfile`` will be tried, in that
+    order.
+    """
+    for driver in drivers or extraction_drivers:
+        try:
+            driver(filename, extract_dir, progress_filter)
+        except UnrecognizedFormat:
+            continue
+        else:
+            return
+    else:
+        raise UnrecognizedFormat(f"Not a recognized archive type: {filename}")
+
+
+def unpack_directory(filename, extract_dir, progress_filter=default_filter) -> None:
+    """ "Unpack" a directory, using the same interface as for archives
+
+    Raises ``UnrecognizedFormat`` if `filename` is not a directory
+    """
+    if not os.path.isdir(filename):
+        raise UnrecognizedFormat(f"{filename} is not a directory")
+
+    paths = {
+        filename: ('', extract_dir),
+    }
+    for base, dirs, files in os.walk(filename):
+        src, dst = paths[base]
+        for d in dirs:
+            paths[os.path.join(base, d)] = src + d + '/', os.path.join(dst, d)
+        for f in files:
+            target = os.path.join(dst, f)
+            target = progress_filter(src + f, target)
+            if not target:
+                # skip non-files
+                continue
+            ensure_directory(target)
+            f = os.path.join(base, f)
+            shutil.copyfile(f, target)
+            shutil.copystat(f, target)
+
+
+def unpack_zipfile(filename, extract_dir, progress_filter=default_filter) -> None:
+    """Unpack zip `filename` to `extract_dir`
+
+    Raises ``UnrecognizedFormat`` if `filename` is not a zipfile (as determined
+    by ``zipfile.is_zipfile()``).  See ``unpack_archive()`` for an explanation
+    of the `progress_filter` argument.
+    """
+
+    if not zipfile.is_zipfile(filename):
+        raise UnrecognizedFormat(f"{filename} is not a zip file")
+
+    with zipfile.ZipFile(filename) as z:
+        _unpack_zipfile_obj(z, extract_dir, progress_filter)
+
+
+def _unpack_zipfile_obj(zipfile_obj, extract_dir, progress_filter=default_filter):
+    """Internal/private API used by other parts of setuptools.
+    Similar to ``unpack_zipfile``, but receives an already opened :obj:`zipfile.ZipFile`
+    object instead of a filename.
+    """
+    for info in zipfile_obj.infolist():
+        name = info.filename
+
+        # don't extract absolute paths or ones with .. in them
+        if name.startswith('/') or '..' in name.split('/'):
+            continue
+
+        target = os.path.join(extract_dir, *name.split('/'))
+        target = progress_filter(name, target)
+        if not target:
+            continue
+        if name.endswith('/'):
+            # directory
+            ensure_directory(target)
+        else:
+            # file
+            ensure_directory(target)
+            data = zipfile_obj.read(info.filename)
+            with open(target, 'wb') as f:
+                f.write(data)
+        unix_attributes = info.external_attr >> 16
+        if unix_attributes:
+            os.chmod(target, unix_attributes)
+
+
+def _resolve_tar_file_or_dir(tar_obj, tar_member_obj):
+    """Resolve any links and extract link targets as normal files."""
+    while tar_member_obj is not None and (
+        tar_member_obj.islnk() or tar_member_obj.issym()
+    ):
+        linkpath = tar_member_obj.linkname
+        if tar_member_obj.issym():
+            base = posixpath.dirname(tar_member_obj.name)
+            linkpath = posixpath.join(base, linkpath)
+            linkpath = posixpath.normpath(linkpath)
+        tar_member_obj = tar_obj._getmember(linkpath)
+
+    is_file_or_dir = tar_member_obj is not None and (
+        tar_member_obj.isfile() or tar_member_obj.isdir()
+    )
+    if is_file_or_dir:
+        return tar_member_obj
+
+    raise LookupError('Got unknown file type')
+
+
+def _iter_open_tar(tar_obj, extract_dir, progress_filter):
+    """Emit member-destination pairs from a tar archive."""
+    # don't do any chowning!
+    tar_obj.chown = lambda *args: None
+
+    with contextlib.closing(tar_obj):
+        for member in tar_obj:
+            name = member.name
+            # don't extract absolute paths or ones with .. in them
+            if name.startswith('/') or '..' in name.split('/'):
+                continue
+
+            prelim_dst = os.path.join(extract_dir, *name.split('/'))
+
+            try:
+                member = _resolve_tar_file_or_dir(tar_obj, member)
+            except LookupError:
+                continue
+
+            final_dst = progress_filter(name, prelim_dst)
+            if not final_dst:
+                continue
+
+            if final_dst.endswith(os.sep):
+                final_dst = final_dst[:-1]
+
+            yield member, final_dst
+
+
+def unpack_tarfile(filename, extract_dir, progress_filter=default_filter) -> bool:
+    """Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir`
+
+    Raises ``UnrecognizedFormat`` if `filename` is not a tarfile (as determined
+    by ``tarfile.open()``).  See ``unpack_archive()`` for an explanation
+    of the `progress_filter` argument.
+    """
+    try:
+        tarobj = tarfile.open(filename)
+    except tarfile.TarError as e:
+        raise UnrecognizedFormat(
+            f"{filename} is not a compressed or uncompressed tar file"
+        ) from e
+
+    for member, final_dst in _iter_open_tar(
+        tarobj,
+        extract_dir,
+        progress_filter,
+    ):
+        try:
+            # XXX Ugh
+            tarobj._extract_member(member, final_dst)
+        except tarfile.ExtractError:
+            # chown/chmod/mkfifo/mknode/makedev failed
+            pass
+
+    return True
+
+
+extraction_drivers = unpack_directory, unpack_zipfile, unpack_tarfile
diff --git a/.venv/lib/python3.12/site-packages/setuptools/build_meta.py b/.venv/lib/python3.12/site-packages/setuptools/build_meta.py
new file mode 100644
index 0000000000000000000000000000000000000000..8f2e930c73439933b1e15506699ce25ec926c46f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/build_meta.py
@@ -0,0 +1,548 @@
+"""A PEP 517 interface to setuptools
+
+Previously, when a user or a command line tool (let's call it a "frontend")
+needed to make a request of setuptools to take a certain action, for
+example, generating a list of installation requirements, the frontend
+would call "setup.py egg_info" or "setup.py bdist_wheel" on the command line.
+
+PEP 517 defines a different method of interfacing with setuptools. Rather
+than calling "setup.py" directly, the frontend should:
+
+  1. Set the current directory to the directory with a setup.py file
+  2. Import this module into a safe python interpreter (one in which
+     setuptools can potentially set global variables or crash hard).
+  3. Call one of the functions defined in PEP 517.
+
+What each function does is defined in PEP 517. However, here is a "casual"
+definition of the functions (this definition should not be relied on for
+bug reports or API stability):
+
+  - `build_wheel`: build a wheel in the folder and return the basename
+  - `get_requires_for_build_wheel`: get the `setup_requires` to build
+  - `prepare_metadata_for_build_wheel`: get the `install_requires`
+  - `build_sdist`: build an sdist in the folder and return the basename
+  - `get_requires_for_build_sdist`: get the `setup_requires` to build
+
+Again, this is not a formal definition! Just a "taste" of the module.
+"""
+
+from __future__ import annotations
+
+import contextlib
+import io
+import os
+import shlex
+import shutil
+import sys
+import tempfile
+import tokenize
+import warnings
+from collections.abc import Iterable, Iterator, Mapping
+from pathlib import Path
+from typing import TYPE_CHECKING, Union
+
+import setuptools
+
+from . import errors
+from ._path import StrPath, same_path
+from ._reqs import parse_strings
+from .warnings import SetuptoolsDeprecationWarning
+
+import distutils
+from distutils.util import strtobool
+
+if TYPE_CHECKING:
+    from typing_extensions import TypeAlias
+
+__all__ = [
+    'get_requires_for_build_sdist',
+    'get_requires_for_build_wheel',
+    'prepare_metadata_for_build_wheel',
+    'build_wheel',
+    'build_sdist',
+    'get_requires_for_build_editable',
+    'prepare_metadata_for_build_editable',
+    'build_editable',
+    '__legacy__',
+    'SetupRequirementsError',
+]
+
+
+class SetupRequirementsError(BaseException):
+    def __init__(self, specifiers) -> None:
+        self.specifiers = specifiers
+
+
+class Distribution(setuptools.dist.Distribution):
+    def fetch_build_eggs(self, specifiers):
+        specifier_list = list(parse_strings(specifiers))
+
+        raise SetupRequirementsError(specifier_list)
+
+    @classmethod
+    @contextlib.contextmanager
+    def patch(cls):
+        """
+        Replace
+        distutils.dist.Distribution with this class
+        for the duration of this context.
+        """
+        orig = distutils.core.Distribution
+        distutils.core.Distribution = cls  # type: ignore[misc] # monkeypatching
+        try:
+            yield
+        finally:
+            distutils.core.Distribution = orig  # type: ignore[misc] # monkeypatching
+
+
+@contextlib.contextmanager
+def no_install_setup_requires():
+    """Temporarily disable installing setup_requires
+
+    Under PEP 517, the backend reports build dependencies to the frontend,
+    and the frontend is responsible for ensuring they're installed.
+    So setuptools (acting as a backend) should not try to install them.
+    """
+    orig = setuptools._install_setup_requires
+    setuptools._install_setup_requires = lambda attrs: None
+    try:
+        yield
+    finally:
+        setuptools._install_setup_requires = orig
+
+
+def _get_immediate_subdirectories(a_dir):
+    return [
+        name for name in os.listdir(a_dir) if os.path.isdir(os.path.join(a_dir, name))
+    ]
+
+
+def _file_with_extension(directory: StrPath, extension: str | tuple[str, ...]):
+    matching = (f for f in os.listdir(directory) if f.endswith(extension))
+    try:
+        (file,) = matching
+    except ValueError:
+        raise ValueError(
+            'No distribution was found. Ensure that `setup.py` '
+            'is not empty and that it calls `setup()`.'
+        ) from None
+    return file
+
+
+def _open_setup_script(setup_script):
+    if not os.path.exists(setup_script):
+        # Supply a default setup.py
+        return io.StringIO("from setuptools import setup; setup()")
+
+    return tokenize.open(setup_script)
+
+
+@contextlib.contextmanager
+def suppress_known_deprecation():
+    with warnings.catch_warnings():
+        warnings.filterwarnings('ignore', 'setup.py install is deprecated')
+        yield
+
+
+_ConfigSettings: TypeAlias = Union[Mapping[str, Union[str, list[str], None]], None]
+"""
+Currently the user can run::
+
+    pip install -e . --config-settings key=value
+    python -m build -C--key=value -C key=value
+
+- pip will pass both key and value as strings and overwriting repeated keys
+  (pypa/pip#11059).
+- build will accumulate values associated with repeated keys in a list.
+  It will also accept keys with no associated value.
+  This means that an option passed by build can be ``str | list[str] | None``.
+- PEP 517 specifies that ``config_settings`` is an optional dict.
+"""
+
+
+class _ConfigSettingsTranslator:
+    """Translate ``config_settings`` into distutils-style command arguments.
+    Only a limited number of options is currently supported.
+    """
+
+    # See pypa/setuptools#1928 pypa/setuptools#2491
+
+    def _get_config(self, key: str, config_settings: _ConfigSettings) -> list[str]:
+        """
+        Get the value of a specific key in ``config_settings`` as a list of strings.
+
+        >>> fn = _ConfigSettingsTranslator()._get_config
+        >>> fn("--global-option", None)
+        []
+        >>> fn("--global-option", {})
+        []
+        >>> fn("--global-option", {'--global-option': 'foo'})
+        ['foo']
+        >>> fn("--global-option", {'--global-option': ['foo']})
+        ['foo']
+        >>> fn("--global-option", {'--global-option': 'foo'})
+        ['foo']
+        >>> fn("--global-option", {'--global-option': 'foo bar'})
+        ['foo', 'bar']
+        """
+        cfg = config_settings or {}
+        opts = cfg.get(key) or []
+        return shlex.split(opts) if isinstance(opts, str) else opts
+
+    def _global_args(self, config_settings: _ConfigSettings) -> Iterator[str]:
+        """
+        Let the user specify ``verbose`` or ``quiet`` + escape hatch via
+        ``--global-option``.
+        Note: ``-v``, ``-vv``, ``-vvv`` have similar effects in setuptools,
+        so we just have to cover the basic scenario ``-v``.
+
+        >>> fn = _ConfigSettingsTranslator()._global_args
+        >>> list(fn(None))
+        []
+        >>> list(fn({"verbose": "False"}))
+        ['-q']
+        >>> list(fn({"verbose": "1"}))
+        ['-v']
+        >>> list(fn({"--verbose": None}))
+        ['-v']
+        >>> list(fn({"verbose": "true", "--global-option": "-q --no-user-cfg"}))
+        ['-v', '-q', '--no-user-cfg']
+        >>> list(fn({"--quiet": None}))
+        ['-q']
+        """
+        cfg = config_settings or {}
+        falsey = {"false", "no", "0", "off"}
+        if "verbose" in cfg or "--verbose" in cfg:
+            level = str(cfg.get("verbose") or cfg.get("--verbose") or "1")
+            yield ("-q" if level.lower() in falsey else "-v")
+        if "quiet" in cfg or "--quiet" in cfg:
+            level = str(cfg.get("quiet") or cfg.get("--quiet") or "1")
+            yield ("-v" if level.lower() in falsey else "-q")
+
+        yield from self._get_config("--global-option", config_settings)
+
+    def __dist_info_args(self, config_settings: _ConfigSettings) -> Iterator[str]:
+        """
+        The ``dist_info`` command accepts ``tag-date`` and ``tag-build``.
+
+        .. warning::
+           We cannot use this yet as it requires the ``sdist`` and ``bdist_wheel``
+           commands run in ``build_sdist`` and ``build_wheel`` to reuse the egg-info
+           directory created in ``prepare_metadata_for_build_wheel``.
+
+        >>> fn = _ConfigSettingsTranslator()._ConfigSettingsTranslator__dist_info_args
+        >>> list(fn(None))
+        []
+        >>> list(fn({"tag-date": "False"}))
+        ['--no-date']
+        >>> list(fn({"tag-date": None}))
+        ['--no-date']
+        >>> list(fn({"tag-date": "true", "tag-build": ".a"}))
+        ['--tag-date', '--tag-build', '.a']
+        """
+        cfg = config_settings or {}
+        if "tag-date" in cfg:
+            val = strtobool(str(cfg["tag-date"] or "false"))
+            yield ("--tag-date" if val else "--no-date")
+        if "tag-build" in cfg:
+            yield from ["--tag-build", str(cfg["tag-build"])]
+
+    def _editable_args(self, config_settings: _ConfigSettings) -> Iterator[str]:
+        """
+        The ``editable_wheel`` command accepts ``editable-mode=strict``.
+
+        >>> fn = _ConfigSettingsTranslator()._editable_args
+        >>> list(fn(None))
+        []
+        >>> list(fn({"editable-mode": "strict"}))
+        ['--mode', 'strict']
+        """
+        cfg = config_settings or {}
+        mode = cfg.get("editable-mode") or cfg.get("editable_mode")
+        if not mode:
+            return
+        yield from ["--mode", str(mode)]
+
+    def _arbitrary_args(self, config_settings: _ConfigSettings) -> Iterator[str]:
+        """
+        Users may expect to pass arbitrary lists of arguments to a command
+        via "--global-option" (example provided in PEP 517 of a "escape hatch").
+
+        >>> fn = _ConfigSettingsTranslator()._arbitrary_args
+        >>> list(fn(None))
+        []
+        >>> list(fn({}))
+        []
+        >>> list(fn({'--build-option': 'foo'}))
+        ['foo']
+        >>> list(fn({'--build-option': ['foo']}))
+        ['foo']
+        >>> list(fn({'--build-option': 'foo'}))
+        ['foo']
+        >>> list(fn({'--build-option': 'foo bar'}))
+        ['foo', 'bar']
+        >>> list(fn({'--global-option': 'foo'}))
+        []
+        """
+        yield from self._get_config("--build-option", config_settings)
+
+
+class _BuildMetaBackend(_ConfigSettingsTranslator):
+    def _get_build_requires(
+        self, config_settings: _ConfigSettings, requirements: list[str]
+    ):
+        sys.argv = [
+            *sys.argv[:1],
+            *self._global_args(config_settings),
+            "egg_info",
+        ]
+        try:
+            with Distribution.patch():
+                self.run_setup()
+        except SetupRequirementsError as e:
+            requirements += e.specifiers
+
+        return requirements
+
+    def run_setup(self, setup_script: str = 'setup.py'):
+        # Note that we can reuse our build directory between calls
+        # Correctness comes first, then optimization later
+        __file__ = os.path.abspath(setup_script)
+        __name__ = '__main__'
+
+        with _open_setup_script(__file__) as f:
+            code = f.read().replace(r'\r\n', r'\n')
+
+        try:
+            exec(code, locals())
+        except SystemExit as e:
+            if e.code:
+                raise
+            # We ignore exit code indicating success
+            SetuptoolsDeprecationWarning.emit(
+                "Running `setup.py` directly as CLI tool is deprecated.",
+                "Please avoid using `sys.exit(0)` or similar statements "
+                "that don't fit in the paradigm of a configuration file.",
+                see_url="https://blog.ganssle.io/articles/2021/10/"
+                "setup-py-deprecated.html",
+            )
+
+    def get_requires_for_build_wheel(self, config_settings: _ConfigSettings = None):
+        return self._get_build_requires(config_settings, requirements=[])
+
+    def get_requires_for_build_sdist(self, config_settings: _ConfigSettings = None):
+        return self._get_build_requires(config_settings, requirements=[])
+
+    def _bubble_up_info_directory(
+        self, metadata_directory: StrPath, suffix: str
+    ) -> str:
+        """
+        PEP 517 requires that the .dist-info directory be placed in the
+        metadata_directory. To comply, we MUST copy the directory to the root.
+
+        Returns the basename of the info directory, e.g. `proj-0.0.0.dist-info`.
+        """
+        info_dir = self._find_info_directory(metadata_directory, suffix)
+        if not same_path(info_dir.parent, metadata_directory):
+            shutil.move(str(info_dir), metadata_directory)
+            # PEP 517 allow other files and dirs to exist in metadata_directory
+        return info_dir.name
+
+    def _find_info_directory(self, metadata_directory: StrPath, suffix: str) -> Path:
+        for parent, dirs, _ in os.walk(metadata_directory):
+            candidates = [f for f in dirs if f.endswith(suffix)]
+
+            if len(candidates) != 0 or len(dirs) != 1:
+                assert len(candidates) == 1, f"Multiple {suffix} directories found"
+                return Path(parent, candidates[0])
+
+        msg = f"No {suffix} directory found in {metadata_directory}"
+        raise errors.InternalError(msg)
+
+    def prepare_metadata_for_build_wheel(
+        self, metadata_directory: StrPath, config_settings: _ConfigSettings = None
+    ):
+        sys.argv = [
+            *sys.argv[:1],
+            *self._global_args(config_settings),
+            "dist_info",
+            "--output-dir",
+            str(metadata_directory),
+            "--keep-egg-info",
+        ]
+        with no_install_setup_requires():
+            self.run_setup()
+
+        self._bubble_up_info_directory(metadata_directory, ".egg-info")
+        return self._bubble_up_info_directory(metadata_directory, ".dist-info")
+
+    def _build_with_temp_dir(
+        self,
+        setup_command: Iterable[str],
+        result_extension: str | tuple[str, ...],
+        result_directory: StrPath,
+        config_settings: _ConfigSettings,
+        arbitrary_args: Iterable[str] = (),
+    ):
+        result_directory = os.path.abspath(result_directory)
+
+        # Build in a temporary directory, then copy to the target.
+        os.makedirs(result_directory, exist_ok=True)
+
+        with tempfile.TemporaryDirectory(
+            prefix=".tmp-", dir=result_directory
+        ) as tmp_dist_dir:
+            sys.argv = [
+                *sys.argv[:1],
+                *self._global_args(config_settings),
+                *setup_command,
+                "--dist-dir",
+                tmp_dist_dir,
+                *arbitrary_args,
+            ]
+            with no_install_setup_requires():
+                self.run_setup()
+
+            result_basename = _file_with_extension(tmp_dist_dir, result_extension)
+            result_path = os.path.join(result_directory, result_basename)
+            if os.path.exists(result_path):
+                # os.rename will fail overwriting on non-Unix.
+                os.remove(result_path)
+            os.rename(os.path.join(tmp_dist_dir, result_basename), result_path)
+
+        return result_basename
+
+    def build_wheel(
+        self,
+        wheel_directory: StrPath,
+        config_settings: _ConfigSettings = None,
+        metadata_directory: StrPath | None = None,
+    ):
+        def _build(cmd: list[str]):
+            with suppress_known_deprecation():
+                return self._build_with_temp_dir(
+                    cmd,
+                    '.whl',
+                    wheel_directory,
+                    config_settings,
+                    self._arbitrary_args(config_settings),
+                )
+
+        if metadata_directory is None:
+            return _build(['bdist_wheel'])
+
+        try:
+            return _build(['bdist_wheel', '--dist-info-dir', str(metadata_directory)])
+        except SystemExit as ex:  # pragma: nocover
+            # pypa/setuptools#4683
+            if "--dist-info-dir not recognized" not in str(ex):
+                raise
+            _IncompatibleBdistWheel.emit()
+            return _build(['bdist_wheel'])
+
+    def build_sdist(
+        self, sdist_directory: StrPath, config_settings: _ConfigSettings = None
+    ):
+        return self._build_with_temp_dir(
+            ['sdist', '--formats', 'gztar'], '.tar.gz', sdist_directory, config_settings
+        )
+
+    def _get_dist_info_dir(self, metadata_directory: StrPath | None) -> str | None:
+        if not metadata_directory:
+            return None
+        dist_info_candidates = list(Path(metadata_directory).glob("*.dist-info"))
+        assert len(dist_info_candidates) <= 1
+        return str(dist_info_candidates[0]) if dist_info_candidates else None
+
+    def build_editable(
+        self,
+        wheel_directory: StrPath,
+        config_settings: _ConfigSettings = None,
+        metadata_directory: StrPath | None = None,
+    ):
+        # XXX can or should we hide our editable_wheel command normally?
+        info_dir = self._get_dist_info_dir(metadata_directory)
+        opts = ["--dist-info-dir", info_dir] if info_dir else []
+        cmd = ["editable_wheel", *opts, *self._editable_args(config_settings)]
+        with suppress_known_deprecation():
+            return self._build_with_temp_dir(
+                cmd, ".whl", wheel_directory, config_settings
+            )
+
+    def get_requires_for_build_editable(self, config_settings: _ConfigSettings = None):
+        return self.get_requires_for_build_wheel(config_settings)
+
+    def prepare_metadata_for_build_editable(
+        self, metadata_directory: StrPath, config_settings: _ConfigSettings = None
+    ):
+        return self.prepare_metadata_for_build_wheel(
+            metadata_directory, config_settings
+        )
+
+
+class _BuildMetaLegacyBackend(_BuildMetaBackend):
+    """Compatibility backend for setuptools
+
+    This is a version of setuptools.build_meta that endeavors
+    to maintain backwards
+    compatibility with pre-PEP 517 modes of invocation. It
+    exists as a temporary
+    bridge between the old packaging mechanism and the new
+    packaging mechanism,
+    and will eventually be removed.
+    """
+
+    def run_setup(self, setup_script: str = 'setup.py'):
+        # In order to maintain compatibility with scripts assuming that
+        # the setup.py script is in a directory on the PYTHONPATH, inject
+        # '' into sys.path. (pypa/setuptools#1642)
+        sys_path = list(sys.path)  # Save the original path
+
+        script_dir = os.path.dirname(os.path.abspath(setup_script))
+        if script_dir not in sys.path:
+            sys.path.insert(0, script_dir)
+
+        # Some setup.py scripts (e.g. in pygame and numpy) use sys.argv[0] to
+        # get the directory of the source code. They expect it to refer to the
+        # setup.py script.
+        sys_argv_0 = sys.argv[0]
+        sys.argv[0] = setup_script
+
+        try:
+            super().run_setup(setup_script=setup_script)
+        finally:
+            # While PEP 517 frontends should be calling each hook in a fresh
+            # subprocess according to the standard (and thus it should not be
+            # strictly necessary to restore the old sys.path), we'll restore
+            # the original path so that the path manipulation does not persist
+            # within the hook after run_setup is called.
+            sys.path[:] = sys_path
+            sys.argv[0] = sys_argv_0
+
+
+class _IncompatibleBdistWheel(SetuptoolsDeprecationWarning):
+    _SUMMARY = "wheel.bdist_wheel is deprecated, please import it from setuptools"
+    _DETAILS = """
+    Ensure that any custom bdist_wheel implementation is a subclass of
+    setuptools.command.bdist_wheel.bdist_wheel.
+    """
+    _DUE_DATE = (2025, 10, 15)
+    # Initially introduced in 2024/10/15, but maybe too disruptive to be enforced?
+    _SEE_URL = "https://github.com/pypa/wheel/pull/631"
+
+
+# The primary backend
+_BACKEND = _BuildMetaBackend()
+
+get_requires_for_build_wheel = _BACKEND.get_requires_for_build_wheel
+get_requires_for_build_sdist = _BACKEND.get_requires_for_build_sdist
+prepare_metadata_for_build_wheel = _BACKEND.prepare_metadata_for_build_wheel
+build_wheel = _BACKEND.build_wheel
+build_sdist = _BACKEND.build_sdist
+get_requires_for_build_editable = _BACKEND.get_requires_for_build_editable
+prepare_metadata_for_build_editable = _BACKEND.prepare_metadata_for_build_editable
+build_editable = _BACKEND.build_editable
+
+
+# The legacy backend
+__legacy__ = _BuildMetaLegacyBackend()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/cli-32.exe b/.venv/lib/python3.12/site-packages/setuptools/cli-32.exe
new file mode 100644
index 0000000000000000000000000000000000000000..65c3cd99cc7433f271a5b9387abdd1ddb949d1a6
Binary files /dev/null and b/.venv/lib/python3.12/site-packages/setuptools/cli-32.exe differ
diff --git a/.venv/lib/python3.12/site-packages/setuptools/cli-64.exe b/.venv/lib/python3.12/site-packages/setuptools/cli-64.exe
new file mode 100644
index 0000000000000000000000000000000000000000..3ea50eebfe3f0113b231a318cc1ad6e238afd60d
Binary files /dev/null and b/.venv/lib/python3.12/site-packages/setuptools/cli-64.exe differ
diff --git a/.venv/lib/python3.12/site-packages/setuptools/cli-arm64.exe b/.venv/lib/python3.12/site-packages/setuptools/cli-arm64.exe
new file mode 100644
index 0000000000000000000000000000000000000000..da96455a07a0bad4cde5dc5626544325f82c722b
Binary files /dev/null and b/.venv/lib/python3.12/site-packages/setuptools/cli-arm64.exe differ
diff --git a/.venv/lib/python3.12/site-packages/setuptools/cli.exe b/.venv/lib/python3.12/site-packages/setuptools/cli.exe
new file mode 100644
index 0000000000000000000000000000000000000000..65c3cd99cc7433f271a5b9387abdd1ddb949d1a6
Binary files /dev/null and b/.venv/lib/python3.12/site-packages/setuptools/cli.exe differ
diff --git a/.venv/lib/python3.12/site-packages/setuptools/depends.py b/.venv/lib/python3.12/site-packages/setuptools/depends.py
new file mode 100644
index 0000000000000000000000000000000000000000..e5223b79561c36d9b6c45ead78288098e1cb0f1d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/depends.py
@@ -0,0 +1,185 @@
+from __future__ import annotations
+
+import contextlib
+import dis
+import marshal
+import sys
+from types import CodeType
+from typing import Any, Literal, TypeVar
+
+from packaging.version import Version
+
+from . import _imp
+from ._imp import PY_COMPILED, PY_FROZEN, PY_SOURCE, find_module
+
+_T = TypeVar("_T")
+
+__all__ = ['Require', 'find_module']
+
+
+class Require:
+    """A prerequisite to building or installing a distribution"""
+
+    def __init__(
+        self,
+        name,
+        requested_version,
+        module,
+        homepage: str = '',
+        attribute=None,
+        format=None,
+    ) -> None:
+        if format is None and requested_version is not None:
+            format = Version
+
+        if format is not None:
+            requested_version = format(requested_version)
+            if attribute is None:
+                attribute = '__version__'
+
+        self.__dict__.update(locals())
+        del self.self
+
+    def full_name(self):
+        """Return full package/distribution name, w/version"""
+        if self.requested_version is not None:
+            return f'{self.name}-{self.requested_version}'
+        return self.name
+
+    def version_ok(self, version):
+        """Is 'version' sufficiently up-to-date?"""
+        return (
+            self.attribute is None
+            or self.format is None
+            or str(version) != "unknown"
+            and self.format(version) >= self.requested_version
+        )
+
+    def get_version(
+        self, paths=None, default: _T | Literal["unknown"] = "unknown"
+    ) -> _T | Literal["unknown"] | None | Any:
+        """Get version number of installed module, 'None', or 'default'
+
+        Search 'paths' for module.  If not found, return 'None'.  If found,
+        return the extracted version attribute, or 'default' if no version
+        attribute was specified, or the value cannot be determined without
+        importing the module.  The version is formatted according to the
+        requirement's version format (if any), unless it is 'None' or the
+        supplied 'default'.
+        """
+
+        if self.attribute is None:
+            try:
+                f, _p, _i = find_module(self.module, paths)
+            except ImportError:
+                return None
+            if f:
+                f.close()
+            return default
+
+        v = get_module_constant(self.module, self.attribute, default, paths)
+
+        if v is not None and v is not default and self.format is not None:
+            return self.format(v)
+
+        return v
+
+    def is_present(self, paths=None):
+        """Return true if dependency is present on 'paths'"""
+        return self.get_version(paths) is not None
+
+    def is_current(self, paths=None):
+        """Return true if dependency is present and up-to-date on 'paths'"""
+        version = self.get_version(paths)
+        if version is None:
+            return False
+        return self.version_ok(str(version))
+
+
+def maybe_close(f):
+    @contextlib.contextmanager
+    def empty():
+        yield
+        return
+
+    if not f:
+        return empty()
+
+    return contextlib.closing(f)
+
+
+# Some objects are not available on some platforms.
+# XXX it'd be better to test assertions about bytecode instead.
+if not sys.platform.startswith('java') and sys.platform != 'cli':
+
+    def get_module_constant(
+        module, symbol, default: _T | int = -1, paths=None
+    ) -> _T | int | None | Any:
+        """Find 'module' by searching 'paths', and extract 'symbol'
+
+        Return 'None' if 'module' does not exist on 'paths', or it does not define
+        'symbol'.  If the module defines 'symbol' as a constant, return the
+        constant.  Otherwise, return 'default'."""
+
+        try:
+            f, path, (_suffix, _mode, kind) = info = find_module(module, paths)
+        except ImportError:
+            # Module doesn't exist
+            return None
+
+        with maybe_close(f):
+            if kind == PY_COMPILED:
+                f.read(8)  # skip magic & date
+                code = marshal.load(f)
+            elif kind == PY_FROZEN:
+                code = _imp.get_frozen_object(module, paths)
+            elif kind == PY_SOURCE:
+                code = compile(f.read(), path, 'exec')
+            else:
+                # Not something we can parse; we'll have to import it.  :(
+                imported = _imp.get_module(module, paths, info)
+                return getattr(imported, symbol, None)
+
+        return extract_constant(code, symbol, default)
+
+    def extract_constant(
+        code: CodeType, symbol: str, default: _T | int = -1
+    ) -> _T | int | None | Any:
+        """Extract the constant value of 'symbol' from 'code'
+
+        If the name 'symbol' is bound to a constant value by the Python code
+        object 'code', return that value.  If 'symbol' is bound to an expression,
+        return 'default'.  Otherwise, return 'None'.
+
+        Return value is based on the first assignment to 'symbol'.  'symbol' must
+        be a global, or at least a non-"fast" local in the code block.  That is,
+        only 'STORE_NAME' and 'STORE_GLOBAL' opcodes are checked, and 'symbol'
+        must be present in 'code.co_names'.
+        """
+        if symbol not in code.co_names:
+            # name's not there, can't possibly be an assignment
+            return None
+
+        name_idx = list(code.co_names).index(symbol)
+
+        STORE_NAME = dis.opmap['STORE_NAME']
+        STORE_GLOBAL = dis.opmap['STORE_GLOBAL']
+        LOAD_CONST = dis.opmap['LOAD_CONST']
+
+        const = default
+
+        for byte_code in dis.Bytecode(code):
+            op = byte_code.opcode
+            arg = byte_code.arg
+
+            if op == LOAD_CONST:
+                assert arg is not None
+                const = code.co_consts[arg]
+            elif arg == name_idx and (op == STORE_NAME or op == STORE_GLOBAL):
+                return const
+            else:
+                const = default
+
+        return None
+
+    __all__ += ['get_module_constant', 'extract_constant']
diff --git a/.venv/lib/python3.12/site-packages/setuptools/discovery.py b/.venv/lib/python3.12/site-packages/setuptools/discovery.py
new file mode 100644
index 0000000000000000000000000000000000000000..c88839918562bad12f1a2e72309f1eacfe23349c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/discovery.py
@@ -0,0 +1,614 @@
+"""Automatic discovery of Python modules and packages (for inclusion in the
+distribution) and other config values.
+
+For the purposes of this module, the following nomenclature is used:
+
+- "src-layout": a directory representing a Python project that contains a "src"
+  folder. Everything under the "src" folder is meant to be included in the
+  distribution when packaging the project. Example::
+
+    .
+    ├── tox.ini
+    ├── pyproject.toml
+    └── src/
+        └── mypkg/
+            ├── __init__.py
+            ├── mymodule.py
+            └── my_data_file.txt
+
+- "flat-layout": a Python project that does not use "src-layout" but instead
+  have a directory under the project root for each package::
+
+    .
+    ├── tox.ini
+    ├── pyproject.toml
+    └── mypkg/
+        ├── __init__.py
+        ├── mymodule.py
+        └── my_data_file.txt
+
+- "single-module": a project that contains a single Python script direct under
+  the project root (no directory used)::
+
+    .
+    ├── tox.ini
+    ├── pyproject.toml
+    └── mymodule.py
+
+"""
+
+from __future__ import annotations
+
+import itertools
+import os
+from collections.abc import Iterable, Iterator, Mapping
+from fnmatch import fnmatchcase
+from glob import glob
+from pathlib import Path
+from typing import TYPE_CHECKING, ClassVar
+
+import _distutils_hack.override  # noqa: F401
+
+from ._path import StrPath
+
+from distutils import log
+from distutils.util import convert_path
+
+if TYPE_CHECKING:
+    from setuptools import Distribution
+
+chain_iter = itertools.chain.from_iterable
+
+
+def _valid_name(path: StrPath) -> bool:
+    # Ignore invalid names that cannot be imported directly
+    return os.path.basename(path).isidentifier()
+
+
+class _Filter:
+    """
+    Given a list of patterns, create a callable that will be true only if
+    the input matches at least one of the patterns.
+    """
+
+    def __init__(self, *patterns: str) -> None:
+        self._patterns = dict.fromkeys(patterns)
+
+    def __call__(self, item: str) -> bool:
+        return any(fnmatchcase(item, pat) for pat in self._patterns)
+
+    def __contains__(self, item: str) -> bool:
+        return item in self._patterns
+
+
+class _Finder:
+    """Base class that exposes functionality for module/package finders"""
+
+    ALWAYS_EXCLUDE: ClassVar[tuple[str, ...]] = ()
+    DEFAULT_EXCLUDE: ClassVar[tuple[str, ...]] = ()
+
+    @classmethod
+    def find(
+        cls,
+        where: StrPath = '.',
+        exclude: Iterable[str] = (),
+        include: Iterable[str] = ('*',),
+    ) -> list[str]:
+        """Return a list of all Python items (packages or modules, depending on
+        the finder implementation) found within directory 'where'.
+
+        'where' is the root directory which will be searched.
+        It should be supplied as a "cross-platform" (i.e. URL-style) path;
+        it will be converted to the appropriate local path syntax.
+
+        'exclude' is a sequence of names to exclude; '*' can be used
+        as a wildcard in the names.
+        When finding packages, 'foo.*' will exclude all subpackages of 'foo'
+        (but not 'foo' itself).
+
+        'include' is a sequence of names to include.
+        If it's specified, only the named items will be included.
+        If it's not specified, all found items will be included.
+        'include' can contain shell style wildcard patterns just like
+        'exclude'.
+        """
+
+        exclude = exclude or cls.DEFAULT_EXCLUDE
+        return list(
+            cls._find_iter(
+                convert_path(str(where)),
+                _Filter(*cls.ALWAYS_EXCLUDE, *exclude),
+                _Filter(*include),
+            )
+        )
+
+    @classmethod
+    def _find_iter(
+        cls, where: StrPath, exclude: _Filter, include: _Filter
+    ) -> Iterator[str]:
+        raise NotImplementedError
+
+
+class PackageFinder(_Finder):
+    """
+    Generate a list of all Python packages found within a directory
+    """
+
+    ALWAYS_EXCLUDE = ("ez_setup", "*__pycache__")
+
+    @classmethod
+    def _find_iter(
+        cls, where: StrPath, exclude: _Filter, include: _Filter
+    ) -> Iterator[str]:
+        """
+        All the packages found in 'where' that pass the 'include' filter, but
+        not the 'exclude' filter.
+        """
+        for root, dirs, files in os.walk(str(where), followlinks=True):
+            # Copy dirs to iterate over it, then empty dirs.
+            all_dirs = dirs[:]
+            dirs[:] = []
+
+            for dir in all_dirs:
+                full_path = os.path.join(root, dir)
+                rel_path = os.path.relpath(full_path, where)
+                package = rel_path.replace(os.path.sep, '.')
+
+                # Skip directory trees that are not valid packages
+                if '.' in dir or not cls._looks_like_package(full_path, package):
+                    continue
+
+                # Should this package be included?
+                if include(package) and not exclude(package):
+                    yield package
+
+                # Early pruning if there is nothing else to be scanned
+                if f"{package}*" in exclude or f"{package}.*" in exclude:
+                    continue
+
+                # Keep searching subdirectories, as there may be more packages
+                # down there, even if the parent was excluded.
+                dirs.append(dir)
+
+    @staticmethod
+    def _looks_like_package(path: StrPath, _package_name: str) -> bool:
+        """Does a directory look like a package?"""
+        return os.path.isfile(os.path.join(path, '__init__.py'))
+
+
+class PEP420PackageFinder(PackageFinder):
+    @staticmethod
+    def _looks_like_package(_path: StrPath, _package_name: str) -> bool:
+        return True
+
+
+class ModuleFinder(_Finder):
+    """Find isolated Python modules.
+    This function will **not** recurse subdirectories.
+    """
+
+    @classmethod
+    def _find_iter(
+        cls, where: StrPath, exclude: _Filter, include: _Filter
+    ) -> Iterator[str]:
+        for file in glob(os.path.join(where, "*.py")):
+            module, _ext = os.path.splitext(os.path.basename(file))
+
+            if not cls._looks_like_module(module):
+                continue
+
+            if include(module) and not exclude(module):
+                yield module
+
+    _looks_like_module = staticmethod(_valid_name)
+
+
+# We have to be extra careful in the case of flat layout to not include files
+# and directories not meant for distribution (e.g. tool-related)
+
+
+class FlatLayoutPackageFinder(PEP420PackageFinder):
+    _EXCLUDE = (
+        "ci",
+        "bin",
+        "debian",
+        "doc",
+        "docs",
+        "documentation",
+        "manpages",
+        "news",
+        "newsfragments",
+        "changelog",
+        "test",
+        "tests",
+        "unit_test",
+        "unit_tests",
+        "example",
+        "examples",
+        "scripts",
+        "tools",
+        "util",
+        "utils",
+        "python",
+        "build",
+        "dist",
+        "venv",
+        "env",
+        "requirements",
+        # ---- Task runners / Build tools ----
+        "tasks",  # invoke
+        "fabfile",  # fabric
+        "site_scons",  # SCons
+        # ---- Other tools ----
+        "benchmark",
+        "benchmarks",
+        "exercise",
+        "exercises",
+        "htmlcov",  # Coverage.py
+        # ---- Hidden directories/Private packages ----
+        "[._]*",
+    )
+
+    DEFAULT_EXCLUDE = tuple(chain_iter((p, f"{p}.*") for p in _EXCLUDE))
+    """Reserved package names"""
+
+    @staticmethod
+    def _looks_like_package(_path: StrPath, package_name: str) -> bool:
+        names = package_name.split('.')
+        # Consider PEP 561
+        root_pkg_is_valid = names[0].isidentifier() or names[0].endswith("-stubs")
+        return root_pkg_is_valid and all(name.isidentifier() for name in names[1:])
+
+
+class FlatLayoutModuleFinder(ModuleFinder):
+    DEFAULT_EXCLUDE = (
+        "setup",
+        "conftest",
+        "test",
+        "tests",
+        "example",
+        "examples",
+        "build",
+        # ---- Task runners ----
+        "toxfile",
+        "noxfile",
+        "pavement",
+        "dodo",
+        "tasks",
+        "fabfile",
+        # ---- Other tools ----
+        "[Ss][Cc]onstruct",  # SCons
+        "conanfile",  # Connan: C/C++ build tool
+        "manage",  # Django
+        "benchmark",
+        "benchmarks",
+        "exercise",
+        "exercises",
+        # ---- Hidden files/Private modules ----
+        "[._]*",
+    )
+    """Reserved top-level module names"""
+
+
+def _find_packages_within(root_pkg: str, pkg_dir: StrPath) -> list[str]:
+    nested = PEP420PackageFinder.find(pkg_dir)
+    return [root_pkg] + [".".join((root_pkg, n)) for n in nested]
+
+
+class ConfigDiscovery:
+    """Fill-in metadata and options that can be automatically derived
+    (from other metadata/options, the file system or conventions)
+    """
+
+    def __init__(self, distribution: Distribution) -> None:
+        self.dist = distribution
+        self._called = False
+        self._disabled = False
+        self._skip_ext_modules = False
+
+    def _disable(self):
+        """Internal API to disable automatic discovery"""
+        self._disabled = True
+
+    def _ignore_ext_modules(self):
+        """Internal API to disregard ext_modules.
+
+        Normally auto-discovery would not be triggered if ``ext_modules`` are set
+        (this is done for backward compatibility with existing packages relying on
+        ``setup.py`` or ``setup.cfg``). However, ``setuptools`` can call this function
+        to ignore given ``ext_modules`` and proceed with the auto-discovery if
+        ``packages`` and ``py_modules`` are not given (e.g. when using pyproject.toml
+        metadata).
+        """
+        self._skip_ext_modules = True
+
+    @property
+    def _root_dir(self) -> StrPath:
+        # The best is to wait until `src_root` is set in dist, before using _root_dir.
+        return self.dist.src_root or os.curdir
+
+    @property
+    def _package_dir(self) -> dict[str, str]:
+        if self.dist.package_dir is None:
+            return {}
+        return self.dist.package_dir
+
+    def __call__(
+        self, force: bool = False, name: bool = True, ignore_ext_modules: bool = False
+    ):
+        """Automatically discover missing configuration fields
+        and modifies the given ``distribution`` object in-place.
+
+        Note that by default this will only have an effect the first time the
+        ``ConfigDiscovery`` object is called.
+
+        To repeatedly invoke automatic discovery (e.g. when the project
+        directory changes), please use ``force=True`` (or create a new
+        ``ConfigDiscovery`` instance).
+        """
+        if force is False and (self._called or self._disabled):
+            # Avoid overhead of multiple calls
+            return
+
+        self._analyse_package_layout(ignore_ext_modules)
+        if name:
+            self.analyse_name()  # depends on ``packages`` and ``py_modules``
+
+        self._called = True
+
+    def _explicitly_specified(self, ignore_ext_modules: bool) -> bool:
+        """``True`` if the user has specified some form of package/module listing"""
+        ignore_ext_modules = ignore_ext_modules or self._skip_ext_modules
+        ext_modules = not (self.dist.ext_modules is None or ignore_ext_modules)
+        return (
+            self.dist.packages is not None
+            or self.dist.py_modules is not None
+            or ext_modules
+            or hasattr(self.dist, "configuration")
+            and self.dist.configuration
+            # ^ Some projects use numpy.distutils.misc_util.Configuration
+        )
+
+    def _analyse_package_layout(self, ignore_ext_modules: bool) -> bool:
+        if self._explicitly_specified(ignore_ext_modules):
+            # For backward compatibility, just try to find modules/packages
+            # when nothing is given
+            return True
+
+        log.debug(
+            "No `packages` or `py_modules` configuration, performing "
+            "automatic discovery."
+        )
+
+        return (
+            self._analyse_explicit_layout()
+            or self._analyse_src_layout()
+            # flat-layout is the trickiest for discovery so it should be last
+            or self._analyse_flat_layout()
+        )
+
+    def _analyse_explicit_layout(self) -> bool:
+        """The user can explicitly give a package layout via ``package_dir``"""
+        package_dir = self._package_dir.copy()  # don't modify directly
+        package_dir.pop("", None)  # This falls under the "src-layout" umbrella
+        root_dir = self._root_dir
+
+        if not package_dir:
+            return False
+
+        log.debug(f"`explicit-layout` detected -- analysing {package_dir}")
+        pkgs = chain_iter(
+            _find_packages_within(pkg, os.path.join(root_dir, parent_dir))
+            for pkg, parent_dir in package_dir.items()
+        )
+        self.dist.packages = list(pkgs)
+        log.debug(f"discovered packages -- {self.dist.packages}")
+        return True
+
+    def _analyse_src_layout(self) -> bool:
+        """Try to find all packages or modules under the ``src`` directory
+        (or anything pointed by ``package_dir[""]``).
+
+        The "src-layout" is relatively safe for automatic discovery.
+        We assume that everything within is meant to be included in the
+        distribution.
+
+        If ``package_dir[""]`` is not given, but the ``src`` directory exists,
+        this function will set ``package_dir[""] = "src"``.
+        """
+        package_dir = self._package_dir
+        src_dir = os.path.join(self._root_dir, package_dir.get("", "src"))
+        if not os.path.isdir(src_dir):
+            return False
+
+        log.debug(f"`src-layout` detected -- analysing {src_dir}")
+        package_dir.setdefault("", os.path.basename(src_dir))
+        self.dist.package_dir = package_dir  # persist eventual modifications
+        self.dist.packages = PEP420PackageFinder.find(src_dir)
+        self.dist.py_modules = ModuleFinder.find(src_dir)
+        log.debug(f"discovered packages -- {self.dist.packages}")
+        log.debug(f"discovered py_modules -- {self.dist.py_modules}")
+        return True
+
+    def _analyse_flat_layout(self) -> bool:
+        """Try to find all packages and modules under the project root.
+
+        Since the ``flat-layout`` is more dangerous in terms of accidentally including
+        extra files/directories, this function is more conservative and will raise an
+        error if multiple packages or modules are found.
+
+        This assumes that multi-package dists are uncommon and refuse to support that
+        use case in order to be able to prevent unintended errors.
+        """
+        log.debug(f"`flat-layout` detected -- analysing {self._root_dir}")
+        return self._analyse_flat_packages() or self._analyse_flat_modules()
+
+    def _analyse_flat_packages(self) -> bool:
+        self.dist.packages = FlatLayoutPackageFinder.find(self._root_dir)
+        top_level = remove_nested_packages(remove_stubs(self.dist.packages))
+        log.debug(f"discovered packages -- {self.dist.packages}")
+        self._ensure_no_accidental_inclusion(top_level, "packages")
+        return bool(top_level)
+
+    def _analyse_flat_modules(self) -> bool:
+        self.dist.py_modules = FlatLayoutModuleFinder.find(self._root_dir)
+        log.debug(f"discovered py_modules -- {self.dist.py_modules}")
+        self._ensure_no_accidental_inclusion(self.dist.py_modules, "modules")
+        return bool(self.dist.py_modules)
+
+    def _ensure_no_accidental_inclusion(self, detected: list[str], kind: str):
+        if len(detected) > 1:
+            from inspect import cleandoc
+
+            from setuptools.errors import PackageDiscoveryError
+
+            msg = f"""Multiple top-level {kind} discovered in a flat-layout: {detected}.
+
+            To avoid accidental inclusion of unwanted files or directories,
+            setuptools will not proceed with this build.
+
+            If you are trying to create a single distribution with multiple {kind}
+            on purpose, you should not rely on automatic discovery.
+            Instead, consider the following options:
+
+            1. set up custom discovery (`find` directive with `include` or `exclude`)
+            2. use a `src-layout`
+            3. explicitly set `py_modules` or `packages` with a list of names
+
+            To find more information, look for "package discovery" on setuptools docs.
+            """
+            raise PackageDiscoveryError(cleandoc(msg))
+
+    def analyse_name(self) -> None:
+        """The packages/modules are the essential contribution of the author.
+        Therefore the name of the distribution can be derived from them.
+        """
+        if self.dist.metadata.name or self.dist.name:
+            # get_name() is not reliable (can return "UNKNOWN")
+            return
+
+        log.debug("No `name` configuration, performing automatic discovery")
+
+        name = (
+            self._find_name_single_package_or_module()
+            or self._find_name_from_packages()
+        )
+        if name:
+            self.dist.metadata.name = name
+
+    def _find_name_single_package_or_module(self) -> str | None:
+        """Exactly one module or package"""
+        for field in ('packages', 'py_modules'):
+            items = getattr(self.dist, field, None) or []
+            if items and len(items) == 1:
+                log.debug(f"Single module/package detected, name: {items[0]}")
+                return items[0]
+
+        return None
+
+    def _find_name_from_packages(self) -> str | None:
+        """Try to find the root package that is not a PEP 420 namespace"""
+        if not self.dist.packages:
+            return None
+
+        packages = remove_stubs(sorted(self.dist.packages, key=len))
+        package_dir = self.dist.package_dir or {}
+
+        parent_pkg = find_parent_package(packages, package_dir, self._root_dir)
+        if parent_pkg:
+            log.debug(f"Common parent package detected, name: {parent_pkg}")
+            return parent_pkg
+
+        log.warn("No parent package detected, impossible to derive `name`")
+        return None
+
+
+def remove_nested_packages(packages: list[str]) -> list[str]:
+    """Remove nested packages from a list of packages.
+
+    >>> remove_nested_packages(["a", "a.b1", "a.b2", "a.b1.c1"])
+    ['a']
+    >>> remove_nested_packages(["a", "b", "c.d", "c.d.e.f", "g.h", "a.a1"])
+    ['a', 'b', 'c.d', 'g.h']
+    """
+    pkgs = sorted(packages, key=len)
+    top_level = pkgs[:]
+    size = len(pkgs)
+    for i, name in enumerate(reversed(pkgs)):
+        if any(name.startswith(f"{other}.") for other in top_level):
+            top_level.pop(size - i - 1)
+
+    return top_level
+
+
+def remove_stubs(packages: list[str]) -> list[str]:
+    """Remove type stubs (:pep:`561`) from a list of packages.
+
+    >>> remove_stubs(["a", "a.b", "a-stubs", "a-stubs.b.c", "b", "c-stubs"])
+    ['a', 'a.b', 'b']
+    """
+    return [pkg for pkg in packages if not pkg.split(".")[0].endswith("-stubs")]
+
+
+def find_parent_package(
+    packages: list[str], package_dir: Mapping[str, str], root_dir: StrPath
+) -> str | None:
+    """Find the parent package that is not a namespace."""
+    packages = sorted(packages, key=len)
+    common_ancestors = []
+    for i, name in enumerate(packages):
+        if not all(n.startswith(f"{name}.") for n in packages[i + 1 :]):
+            # Since packages are sorted by length, this condition is able
+            # to find a list of all common ancestors.
+            # When there is divergence (e.g. multiple root packages)
+            # the list will be empty
+            break
+        common_ancestors.append(name)
+
+    for name in common_ancestors:
+        pkg_path = find_package_path(name, package_dir, root_dir)
+        init = os.path.join(pkg_path, "__init__.py")
+        if os.path.isfile(init):
+            return name
+
+    return None
+
+
+def find_package_path(
+    name: str, package_dir: Mapping[str, str], root_dir: StrPath
+) -> str:
+    """Given a package name, return the path where it should be found on
+    disk, considering the ``package_dir`` option.
+
+    >>> path = find_package_path("my.pkg", {"": "root/is/nested"}, ".")
+    >>> path.replace(os.sep, "/")
+    './root/is/nested/my/pkg'
+
+    >>> path = find_package_path("my.pkg", {"my": "root/is/nested"}, ".")
+    >>> path.replace(os.sep, "/")
+    './root/is/nested/pkg'
+
+    >>> path = find_package_path("my.pkg", {"my.pkg": "root/is/nested"}, ".")
+    >>> path.replace(os.sep, "/")
+    './root/is/nested'
+
+    >>> path = find_package_path("other.pkg", {"my.pkg": "root/is/nested"}, ".")
+    >>> path.replace(os.sep, "/")
+    './other/pkg'
+    """
+    parts = name.split(".")
+    for i in range(len(parts), 0, -1):
+        # Look backwards, the most specific package_dir first
+        partial_name = ".".join(parts[:i])
+        if partial_name in package_dir:
+            parent = package_dir[partial_name]
+            return os.path.join(root_dir, parent, *parts[i:])
+
+    parent = package_dir.get("") or ""
+    return os.path.join(root_dir, *parent.split("/"), *parts)
+
+
+def construct_package_dir(packages: list[str], package_path: StrPath) -> dict[str, str]:
+    parent_pkgs = remove_nested_packages(packages)
+    prefix = Path(package_path).parts
+    return {pkg: "/".join([*prefix, *pkg.split(".")]) for pkg in parent_pkgs}
diff --git a/.venv/lib/python3.12/site-packages/setuptools/dist.py b/.venv/lib/python3.12/site-packages/setuptools/dist.py
new file mode 100644
index 0000000000000000000000000000000000000000..f06298c8681a637176c7def931f06ddbdb9854dc
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/dist.py
@@ -0,0 +1,1119 @@
+from __future__ import annotations
+
+import functools
+import io
+import itertools
+import numbers
+import os
+import re
+import sys
+from collections.abc import Iterable, Iterator, MutableMapping, Sequence
+from glob import glob
+from pathlib import Path
+from typing import TYPE_CHECKING, Any, Union
+
+from more_itertools import partition, unique_everseen
+from packaging.markers import InvalidMarker, Marker
+from packaging.specifiers import InvalidSpecifier, SpecifierSet
+from packaging.version import Version
+
+from . import (
+    _entry_points,
+    _reqs,
+    _static,
+    command as _,  # noqa: F401 # imported for side-effects
+)
+from ._importlib import metadata
+from ._normalization import _canonicalize_license_expression
+from ._path import StrPath
+from ._reqs import _StrOrIter
+from .config import pyprojecttoml, setupcfg
+from .discovery import ConfigDiscovery
+from .errors import InvalidConfigError
+from .monkey import get_unpatched
+from .warnings import InformationOnly, SetuptoolsDeprecationWarning
+
+import distutils.cmd
+import distutils.command
+import distutils.core
+import distutils.dist
+import distutils.log
+from distutils.debug import DEBUG
+from distutils.errors import DistutilsOptionError, DistutilsSetupError
+from distutils.fancy_getopt import translate_longopt
+from distutils.util import strtobool
+
+if TYPE_CHECKING:
+    from typing_extensions import TypeAlias
+
+
+__all__ = ['Distribution']
+
+_sequence = tuple, list
+"""
+:meta private:
+
+Supported iterable types that are known to be:
+- ordered (which `set` isn't)
+- not match a str (which `Sequence[str]` does)
+- not imply a nested type (like `dict`)
+for use with `isinstance`.
+"""
+_Sequence: TypeAlias = Union[tuple[str, ...], list[str]]
+# This is how stringifying _Sequence would look in Python 3.10
+_sequence_type_repr = "tuple[str, ...] | list[str]"
+_OrderedStrSequence: TypeAlias = Union[str, dict[str, Any], Sequence[str]]
+"""
+:meta private:
+Avoid single-use iterable. Disallow sets.
+A poor approximation of an OrderedSequence (dict doesn't match a Sequence).
+"""
+
+
+def __getattr__(name: str) -> Any:  # pragma: no cover
+    if name == "sequence":
+        SetuptoolsDeprecationWarning.emit(
+            "`setuptools.dist.sequence` is an internal implementation detail.",
+            "Please define your own `sequence = tuple, list` instead.",
+            due_date=(2025, 8, 28),  # Originally added on 2024-08-27
+        )
+        return _sequence
+    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
+
+
+def check_importable(dist, attr, value):
+    try:
+        ep = metadata.EntryPoint(value=value, name=None, group=None)
+        assert not ep.extras
+    except (TypeError, ValueError, AttributeError, AssertionError) as e:
+        raise DistutilsSetupError(
+            f"{attr!r} must be importable 'module:attrs' string (got {value!r})"
+        ) from e
+
+
+def assert_string_list(dist, attr: str, value: _Sequence) -> None:
+    """Verify that value is a string list"""
+    try:
+        # verify that value is a list or tuple to exclude unordered
+        # or single-use iterables
+        assert isinstance(value, _sequence)
+        # verify that elements of value are strings
+        assert ''.join(value) != value
+    except (TypeError, ValueError, AttributeError, AssertionError) as e:
+        raise DistutilsSetupError(
+            f"{attr!r} must be of type <{_sequence_type_repr}> (got {value!r})"
+        ) from e
+
+
+def check_nsp(dist, attr, value):
+    """Verify that namespace packages are valid"""
+    ns_packages = value
+    assert_string_list(dist, attr, ns_packages)
+    for nsp in ns_packages:
+        if not dist.has_contents_for(nsp):
+            raise DistutilsSetupError(
+                f"Distribution contains no modules or packages for namespace package {nsp!r}"
+            )
+        parent, _sep, _child = nsp.rpartition('.')
+        if parent and parent not in ns_packages:
+            distutils.log.warn(
+                "WARNING: %r is declared as a package namespace, but %r"
+                " is not: please correct this in setup.py",
+                nsp,
+                parent,
+            )
+        SetuptoolsDeprecationWarning.emit(
+            "The namespace_packages parameter is deprecated.",
+            "Please replace its usage with implicit namespaces (PEP 420).",
+            see_docs="references/keywords.html#keyword-namespace-packages",
+            # TODO: define due_date, it may break old packages that are no longer
+            # maintained (e.g. sphinxcontrib extensions) when installed from source.
+            # Warning officially introduced in May 2022, however the deprecation
+            # was mentioned much earlier in the docs (May 2020, see #2149).
+        )
+
+
+def check_extras(dist, attr, value):
+    """Verify that extras_require mapping is valid"""
+    try:
+        list(itertools.starmap(_check_extra, value.items()))
+    except (TypeError, ValueError, AttributeError) as e:
+        raise DistutilsSetupError(
+            "'extras_require' must be a dictionary whose values are "
+            "strings or lists of strings containing valid project/version "
+            "requirement specifiers."
+        ) from e
+
+
+def _check_extra(extra, reqs):
+    _name, _sep, marker = extra.partition(':')
+    try:
+        _check_marker(marker)
+    except InvalidMarker:
+        msg = f"Invalid environment marker: {marker} ({extra!r})"
+        raise DistutilsSetupError(msg) from None
+    list(_reqs.parse(reqs))
+
+
+def _check_marker(marker):
+    if not marker:
+        return
+    m = Marker(marker)
+    m.evaluate()
+
+
+def assert_bool(dist, attr, value):
+    """Verify that value is True, False, 0, or 1"""
+    if bool(value) != value:
+        raise DistutilsSetupError(f"{attr!r} must be a boolean value (got {value!r})")
+
+
+def invalid_unless_false(dist, attr, value):
+    if not value:
+        DistDeprecationWarning.emit(f"{attr} is ignored.")
+        # TODO: should there be a `due_date` here?
+        return
+    raise DistutilsSetupError(f"{attr} is invalid.")
+
+
+def check_requirements(dist, attr: str, value: _OrderedStrSequence) -> None:
+    """Verify that install_requires is a valid requirements list"""
+    try:
+        list(_reqs.parse(value))
+        if isinstance(value, set):
+            raise TypeError("Unordered types are not allowed")
+    except (TypeError, ValueError) as error:
+        msg = (
+            f"{attr!r} must be a string or iterable of strings "
+            f"containing valid project/version requirement specifiers; {error}"
+        )
+        raise DistutilsSetupError(msg) from error
+
+
+def check_specifier(dist, attr, value):
+    """Verify that value is a valid version specifier"""
+    try:
+        SpecifierSet(value)
+    except (InvalidSpecifier, AttributeError) as error:
+        msg = f"{attr!r} must be a string containing valid version specifiers; {error}"
+        raise DistutilsSetupError(msg) from error
+
+
+def check_entry_points(dist, attr, value):
+    """Verify that entry_points map is parseable"""
+    try:
+        _entry_points.load(value)
+    except Exception as e:
+        raise DistutilsSetupError(e) from e
+
+
+def check_package_data(dist, attr, value):
+    """Verify that value is a dictionary of package names to glob lists"""
+    if not isinstance(value, dict):
+        raise DistutilsSetupError(
+            f"{attr!r} must be a dictionary mapping package names to lists of "
+            "string wildcard patterns"
+        )
+    for k, v in value.items():
+        if not isinstance(k, str):
+            raise DistutilsSetupError(
+                f"keys of {attr!r} dict must be strings (got {k!r})"
+            )
+        assert_string_list(dist, f'values of {attr!r} dict', v)
+
+
+def check_packages(dist, attr, value):
+    for pkgname in value:
+        if not re.match(r'\w+(\.\w+)*', pkgname):
+            distutils.log.warn(
+                "WARNING: %r not a valid package name; please use only "
+                ".-separated package names in setup.py",
+                pkgname,
+            )
+
+
+if TYPE_CHECKING:
+    # Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962
+    from distutils.core import Distribution as _Distribution
+else:
+    _Distribution = get_unpatched(distutils.core.Distribution)
+
+
+class Distribution(_Distribution):
+    """Distribution with support for tests and package data
+
+    This is an enhanced version of 'distutils.dist.Distribution' that
+    effectively adds the following new optional keyword arguments to 'setup()':
+
+     'install_requires' -- a string or sequence of strings specifying project
+        versions that the distribution requires when installed, in the format
+        used by 'pkg_resources.require()'.  They will be installed
+        automatically when the package is installed.  If you wish to use
+        packages that are not available in PyPI, or want to give your users an
+        alternate download location, you can add a 'find_links' option to the
+        '[easy_install]' section of your project's 'setup.cfg' file, and then
+        setuptools will scan the listed web pages for links that satisfy the
+        requirements.
+
+     'extras_require' -- a dictionary mapping names of optional "extras" to the
+        additional requirement(s) that using those extras incurs. For example,
+        this::
+
+            extras_require = dict(reST = ["docutils>=0.3", "reSTedit"])
+
+        indicates that the distribution can optionally provide an extra
+        capability called "reST", but it can only be used if docutils and
+        reSTedit are installed.  If the user installs your package using
+        EasyInstall and requests one of your extras, the corresponding
+        additional requirements will be installed if needed.
+
+     'package_data' -- a dictionary mapping package names to lists of filenames
+        or globs to use to find data files contained in the named packages.
+        If the dictionary has filenames or globs listed under '""' (the empty
+        string), those names will be searched for in every package, in addition
+        to any names for the specific package.  Data files found using these
+        names/globs will be installed along with the package, in the same
+        location as the package.  Note that globs are allowed to reference
+        the contents of non-package subdirectories, as long as you use '/' as
+        a path separator.  (Globs are automatically converted to
+        platform-specific paths at runtime.)
+
+    In addition to these new keywords, this class also has several new methods
+    for manipulating the distribution's contents.  For example, the 'include()'
+    and 'exclude()' methods can be thought of as in-place add and subtract
+    commands that add or remove packages, modules, extensions, and so on from
+    the distribution.
+    """
+
+    _DISTUTILS_UNSUPPORTED_METADATA = {
+        'long_description_content_type': lambda: None,
+        'project_urls': dict,
+        'provides_extras': dict,  # behaves like an ordered set
+        'license_expression': lambda: None,
+        'license_file': lambda: None,
+        'license_files': lambda: None,
+        'install_requires': list,
+        'extras_require': dict,
+    }
+
+    # Used by build_py, editable_wheel and install_lib commands for legacy namespaces
+    namespace_packages: list[str]  #: :meta private: DEPRECATED
+
+    # Any: Dynamic assignment results in Incompatible types in assignment
+    def __init__(self, attrs: MutableMapping[str, Any] | None = None) -> None:
+        have_package_data = hasattr(self, "package_data")
+        if not have_package_data:
+            self.package_data: dict[str, list[str]] = {}
+        attrs = attrs or {}
+        self.dist_files: list[tuple[str, str, str]] = []
+        self.include_package_data: bool | None = None
+        self.exclude_package_data: dict[str, list[str]] | None = None
+        # Filter-out setuptools' specific options.
+        self.src_root: str | None = attrs.pop("src_root", None)
+        self.dependency_links: list[str] = attrs.pop('dependency_links', [])
+        self.setup_requires: list[str] = attrs.pop('setup_requires', [])
+        for ep in metadata.entry_points(group='distutils.setup_keywords'):
+            vars(self).setdefault(ep.name, None)
+
+        metadata_only = set(self._DISTUTILS_UNSUPPORTED_METADATA)
+        metadata_only -= {"install_requires", "extras_require"}
+        dist_attrs = {k: v for k, v in attrs.items() if k not in metadata_only}
+        _Distribution.__init__(self, dist_attrs)
+
+        # Private API (setuptools-use only, not restricted to Distribution)
+        # Stores files that are referenced by the configuration and need to be in the
+        # sdist (e.g. `version = file: VERSION.txt`)
+        self._referenced_files = set[str]()
+
+        self.set_defaults = ConfigDiscovery(self)
+
+        self._set_metadata_defaults(attrs)
+
+        self.metadata.version = self._normalize_version(self.metadata.version)
+        self._finalize_requires()
+
+    def _validate_metadata(self):
+        required = {"name"}
+        provided = {
+            key
+            for key in vars(self.metadata)
+            if getattr(self.metadata, key, None) is not None
+        }
+        missing = required - provided
+
+        if missing:
+            msg = f"Required package metadata is missing: {missing}"
+            raise DistutilsSetupError(msg)
+
+    def _set_metadata_defaults(self, attrs):
+        """
+        Fill-in missing metadata fields not supported by distutils.
+        Some fields may have been set by other tools (e.g. pbr).
+        Those fields (vars(self.metadata)) take precedence to
+        supplied attrs.
+        """
+        for option, default in self._DISTUTILS_UNSUPPORTED_METADATA.items():
+            vars(self.metadata).setdefault(option, attrs.get(option, default()))
+
+    @staticmethod
+    def _normalize_version(version):
+        from . import sic
+
+        if isinstance(version, numbers.Number):
+            # Some people apparently take "version number" too literally :)
+            version = str(version)
+        elif isinstance(version, sic) or version is None:
+            return version
+
+        normalized = str(Version(version))
+        if version != normalized:
+            InformationOnly.emit(f"Normalizing '{version}' to '{normalized}'")
+            return normalized
+        return version
+
+    def _finalize_requires(self):
+        """
+        Set `metadata.python_requires` and fix environment markers
+        in `install_requires` and `extras_require`.
+        """
+        if getattr(self, 'python_requires', None):
+            self.metadata.python_requires = self.python_requires
+
+        self._normalize_requires()
+        self.metadata.install_requires = self.install_requires
+        self.metadata.extras_require = self.extras_require
+
+        if self.extras_require:
+            for extra in self.extras_require.keys():
+                # Setuptools allows a weird ": syntax for extras
+                extra = extra.split(':')[0]
+                if extra:
+                    self.metadata.provides_extras.setdefault(extra)
+
+    def _normalize_requires(self):
+        """Make sure requirement-related attributes exist and are normalized"""
+        install_requires = getattr(self, "install_requires", None) or []
+        extras_require = getattr(self, "extras_require", None) or {}
+
+        # Preserve the "static"-ness of values parsed from config files
+        list_ = _static.List if _static.is_static(install_requires) else list
+        self.install_requires = list_(map(str, _reqs.parse(install_requires)))
+
+        dict_ = _static.Dict if _static.is_static(extras_require) else dict
+        self.extras_require = dict_(
+            (k, list(map(str, _reqs.parse(v or [])))) for k, v in extras_require.items()
+        )
+
+    def _finalize_license_expression(self) -> None:
+        """
+        Normalize license and license_expression.
+        >>> dist = Distribution({"license_expression": _static.Str("mit aNd  gpl-3.0-OR-later")})
+        >>> _static.is_static(dist.metadata.license_expression)
+        True
+        >>> dist._finalize_license_expression()
+        >>> _static.is_static(dist.metadata.license_expression)  # preserve "static-ness"
+        True
+        >>> print(dist.metadata.license_expression)
+        MIT AND GPL-3.0-or-later
+        """
+        classifiers = self.metadata.get_classifiers()
+        license_classifiers = [cl for cl in classifiers if cl.startswith("License :: ")]
+
+        license_expr = self.metadata.license_expression
+        if license_expr:
+            str_ = _static.Str if _static.is_static(license_expr) else str
+            normalized = str_(_canonicalize_license_expression(license_expr))
+            if license_expr != normalized:
+                InformationOnly.emit(f"Normalizing '{license_expr}' to '{normalized}'")
+                self.metadata.license_expression = normalized
+            if license_classifiers:
+                raise InvalidConfigError(
+                    "License classifiers have been superseded by license expressions "
+                    "(see https://peps.python.org/pep-0639/). Please remove:\n\n"
+                    + "\n".join(license_classifiers),
+                )
+        elif license_classifiers:
+            pypa_guides = "guides/writing-pyproject-toml/#license"
+            SetuptoolsDeprecationWarning.emit(
+                "License classifiers are deprecated.",
+                "Please consider removing the following classifiers in favor of a "
+                "SPDX license expression:\n\n" + "\n".join(license_classifiers),
+                see_url=f"https://packaging.python.org/en/latest/{pypa_guides}",
+                # Warning introduced on 2025-02-17
+                # TODO: Should we add a due date? It may affect old/unmaintained
+                #       packages in the ecosystem and cause problems...
+            )
+
+    def _finalize_license_files(self) -> None:
+        """Compute names of all license files which should be included."""
+        license_files: list[str] | None = self.metadata.license_files
+        patterns = license_files or []
+
+        license_file: str | None = self.metadata.license_file
+        if license_file and license_file not in patterns:
+            patterns.append(license_file)
+
+        if license_files is None and license_file is None:
+            # Default patterns match the ones wheel uses
+            # See https://wheel.readthedocs.io/en/stable/user_guide.html
+            # -> 'Including license files in the generated wheel file'
+            patterns = ['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']
+            files = self._expand_patterns(patterns, enforce_match=False)
+        else:  # Patterns explicitly given by the user
+            files = self._expand_patterns(patterns, enforce_match=True)
+
+        self.metadata.license_files = list(unique_everseen(files))
+
+    @classmethod
+    def _expand_patterns(
+        cls, patterns: list[str], enforce_match: bool = True
+    ) -> Iterator[str]:
+        """
+        >>> getfixture('sample_project_cwd')
+        >>> list(Distribution._expand_patterns(['LICENSE.txt']))
+        ['LICENSE.txt']
+        >>> list(Distribution._expand_patterns(['pyproject.toml', 'LIC*']))
+        ['pyproject.toml', 'LICENSE.txt']
+        >>> list(Distribution._expand_patterns(['src/**/*.dat']))
+        ['src/sample/package_data.dat']
+        """
+        return (
+            path.replace(os.sep, "/")
+            for pattern in patterns
+            for path in sorted(cls._find_pattern(pattern, enforce_match))
+            if not path.endswith('~') and os.path.isfile(path)
+        )
+
+    @staticmethod
+    def _find_pattern(pattern: str, enforce_match: bool = True) -> list[str]:
+        r"""
+        >>> getfixture('sample_project_cwd')
+        >>> Distribution._find_pattern("LICENSE.txt")
+        ['LICENSE.txt']
+        >>> Distribution._find_pattern("/LICENSE.MIT")
+        Traceback (most recent call last):
+        ...
+        setuptools.errors.InvalidConfigError: Pattern '/LICENSE.MIT' should be relative...
+        >>> Distribution._find_pattern("../LICENSE.MIT")
+        Traceback (most recent call last):
+        ...
+        setuptools.warnings.SetuptoolsDeprecationWarning: ...Pattern '../LICENSE.MIT' cannot contain '..'...
+        >>> Distribution._find_pattern("LICEN{CSE*")
+        Traceback (most recent call last):
+        ...
+        setuptools.warnings.SetuptoolsDeprecationWarning: ...Pattern 'LICEN{CSE*' contains invalid characters...
+        """
+        pypa_guides = "specifications/glob-patterns/"
+        if ".." in pattern:
+            SetuptoolsDeprecationWarning.emit(
+                f"Pattern {pattern!r} cannot contain '..'",
+                """
+                Please ensure the files specified are contained by the root
+                of the Python package (normally marked by `pyproject.toml`).
+                """,
+                see_url=f"https://packaging.python.org/en/latest/{pypa_guides}",
+                due_date=(2026, 3, 20),  # Introduced in 2025-03-20
+                # Replace with InvalidConfigError after deprecation
+            )
+        if pattern.startswith((os.sep, "/")) or ":\\" in pattern:
+            raise InvalidConfigError(
+                f"Pattern {pattern!r} should be relative and must not start with '/'"
+            )
+        if re.match(r'^[\w\-\.\/\*\?\[\]]+$', pattern) is None:
+            SetuptoolsDeprecationWarning.emit(
+                "Please provide a valid glob pattern.",
+                "Pattern {pattern!r} contains invalid characters.",
+                pattern=pattern,
+                see_url=f"https://packaging.python.org/en/latest/{pypa_guides}",
+                due_date=(2026, 3, 20),  # Introduced in 2025-02-20
+            )
+
+        found = glob(pattern, recursive=True)
+
+        if enforce_match and not found:
+            SetuptoolsDeprecationWarning.emit(
+                "Cannot find any files for the given pattern.",
+                "Pattern {pattern!r} did not match any files.",
+                pattern=pattern,
+                due_date=(2026, 3, 20),  # Introduced in 2025-02-20
+                # PEP 639 requires us to error, but as a transition period
+                # we will only issue a warning to give people time to prepare.
+                # After the transition, this should raise an InvalidConfigError.
+            )
+        return found
+
+    # FIXME: 'Distribution._parse_config_files' is too complex (14)
+    def _parse_config_files(self, filenames=None):  # noqa: C901
+        """
+        Adapted from distutils.dist.Distribution.parse_config_files,
+        this method provides the same functionality in subtly-improved
+        ways.
+        """
+        from configparser import ConfigParser
+
+        # Ignore install directory options if we have a venv
+        ignore_options = (
+            []
+            if sys.prefix == sys.base_prefix
+            else [
+                'install-base',
+                'install-platbase',
+                'install-lib',
+                'install-platlib',
+                'install-purelib',
+                'install-headers',
+                'install-scripts',
+                'install-data',
+                'prefix',
+                'exec-prefix',
+                'home',
+                'user',
+                'root',
+            ]
+        )
+
+        ignore_options = frozenset(ignore_options)
+
+        if filenames is None:
+            filenames = self.find_config_files()
+
+        if DEBUG:
+            self.announce("Distribution.parse_config_files():")
+
+        parser = ConfigParser()
+        parser.optionxform = str
+        for filename in filenames:
+            with open(filename, encoding='utf-8') as reader:
+                if DEBUG:
+                    self.announce("  reading {filename}".format(**locals()))
+                parser.read_file(reader)
+            for section in parser.sections():
+                options = parser.options(section)
+                opt_dict = self.get_option_dict(section)
+
+                for opt in options:
+                    if opt == '__name__' or opt in ignore_options:
+                        continue
+
+                    val = parser.get(section, opt)
+                    opt = self._enforce_underscore(opt, section)
+                    opt = self._enforce_option_lowercase(opt, section)
+                    opt_dict[opt] = (filename, val)
+
+            # Make the ConfigParser forget everything (so we retain
+            # the original filenames that options come from)
+            parser.__init__()
+
+        if 'global' not in self.command_options:
+            return
+
+        # If there was a "global" section in the config file, use it
+        # to set Distribution options.
+
+        for opt, (src, val) in self.command_options['global'].items():
+            alias = self.negative_opt.get(opt)
+            if alias:
+                val = not strtobool(val)
+            elif opt in ('verbose', 'dry_run'):  # ugh!
+                val = strtobool(val)
+
+            try:
+                setattr(self, alias or opt, val)
+            except ValueError as e:
+                raise DistutilsOptionError(e) from e
+
+    def _enforce_underscore(self, opt: str, section: str) -> str:
+        if "-" not in opt or self._skip_setupcfg_normalization(section):
+            return opt
+
+        underscore_opt = opt.replace('-', '_')
+        affected = f"(Affected: {self.metadata.name})." if self.metadata.name else ""
+        SetuptoolsDeprecationWarning.emit(
+            f"Invalid dash-separated key {opt!r} in {section!r} (setup.cfg), "
+            f"please use the underscore name {underscore_opt!r} instead.",
+            f"""
+            Usage of dash-separated {opt!r} will not be supported in future
+            versions. Please use the underscore name {underscore_opt!r} instead.
+            {affected}
+            """,
+            see_docs="userguide/declarative_config.html",
+            due_date=(2026, 3, 3),
+            # Warning initially introduced in 3 Mar 2021
+        )
+        return underscore_opt
+
+    def _enforce_option_lowercase(self, opt: str, section: str) -> str:
+        if opt.islower() or self._skip_setupcfg_normalization(section):
+            return opt
+
+        lowercase_opt = opt.lower()
+        affected = f"(Affected: {self.metadata.name})." if self.metadata.name else ""
+        SetuptoolsDeprecationWarning.emit(
+            f"Invalid uppercase key {opt!r} in {section!r} (setup.cfg), "
+            f"please use lowercase {lowercase_opt!r} instead.",
+            f"""
+            Usage of uppercase key {opt!r} in {section!r} will not be supported in
+            future versions. Please use lowercase {lowercase_opt!r} instead.
+            {affected}
+            """,
+            see_docs="userguide/declarative_config.html",
+            due_date=(2026, 3, 3),
+            # Warning initially introduced in 6 Mar 2021
+        )
+        return lowercase_opt
+
+    def _skip_setupcfg_normalization(self, section: str) -> bool:
+        skip = (
+            'options.extras_require',
+            'options.data_files',
+            'options.entry_points',
+            'options.package_data',
+            'options.exclude_package_data',
+        )
+        return section in skip or not self._is_setuptools_section(section)
+
+    def _is_setuptools_section(self, section: str) -> bool:
+        return (
+            section == "metadata"
+            or section.startswith("options")
+            or section in _setuptools_commands()
+        )
+
+    # FIXME: 'Distribution._set_command_options' is too complex (14)
+    def _set_command_options(self, command_obj, option_dict=None):  # noqa: C901
+        """
+        Set the options for 'command_obj' from 'option_dict'.  Basically
+        this means copying elements of a dictionary ('option_dict') to
+        attributes of an instance ('command').
+
+        'command_obj' must be a Command instance.  If 'option_dict' is not
+        supplied, uses the standard option dictionary for this command
+        (from 'self.command_options').
+
+        (Adopted from distutils.dist.Distribution._set_command_options)
+        """
+        command_name = command_obj.get_command_name()
+        if option_dict is None:
+            option_dict = self.get_option_dict(command_name)
+
+        if DEBUG:
+            self.announce(f"  setting options for '{command_name}' command:")
+        for option, (source, value) in option_dict.items():
+            if DEBUG:
+                self.announce(f"    {option} = {value} (from {source})")
+            try:
+                bool_opts = [translate_longopt(o) for o in command_obj.boolean_options]
+            except AttributeError:
+                bool_opts = []
+            try:
+                neg_opt = command_obj.negative_opt
+            except AttributeError:
+                neg_opt = {}
+
+            try:
+                is_string = isinstance(value, str)
+                if option in neg_opt and is_string:
+                    setattr(command_obj, neg_opt[option], not strtobool(value))
+                elif option in bool_opts and is_string:
+                    setattr(command_obj, option, strtobool(value))
+                elif hasattr(command_obj, option):
+                    setattr(command_obj, option, value)
+                else:
+                    raise DistutilsOptionError(
+                        f"error in {source}: command '{command_name}' has no such option '{option}'"
+                    )
+            except ValueError as e:
+                raise DistutilsOptionError(e) from e
+
+    def _get_project_config_files(self, filenames: Iterable[StrPath] | None):
+        """Add default file and split between INI and TOML"""
+        tomlfiles = []
+        standard_project_metadata = Path(self.src_root or os.curdir, "pyproject.toml")
+        if filenames is not None:
+            parts = partition(lambda f: Path(f).suffix == ".toml", filenames)
+            filenames = list(parts[0])  # 1st element => predicate is False
+            tomlfiles = list(parts[1])  # 2nd element => predicate is True
+        elif standard_project_metadata.exists():
+            tomlfiles = [standard_project_metadata]
+        return filenames, tomlfiles
+
+    def parse_config_files(
+        self,
+        filenames: Iterable[StrPath] | None = None,
+        ignore_option_errors: bool = False,
+    ) -> None:
+        """Parses configuration files from various levels
+        and loads configuration.
+        """
+        inifiles, tomlfiles = self._get_project_config_files(filenames)
+
+        self._parse_config_files(filenames=inifiles)
+
+        setupcfg.parse_configuration(
+            self, self.command_options, ignore_option_errors=ignore_option_errors
+        )
+        for filename in tomlfiles:
+            pyprojecttoml.apply_configuration(self, filename, ignore_option_errors)
+
+        self._finalize_requires()
+        self._finalize_license_expression()
+        self._finalize_license_files()
+
+    def fetch_build_eggs(self, requires: _StrOrIter) -> list[metadata.Distribution]:
+        """Resolve pre-setup requirements"""
+        from .installer import _fetch_build_eggs
+
+        return _fetch_build_eggs(self, requires)
+
+    def finalize_options(self) -> None:
+        """
+        Allow plugins to apply arbitrary operations to the
+        distribution. Each hook may optionally define a 'order'
+        to influence the order of execution. Smaller numbers
+        go first and the default is 0.
+        """
+        group = 'setuptools.finalize_distribution_options'
+
+        def by_order(hook):
+            return getattr(hook, 'order', 0)
+
+        defined = metadata.entry_points(group=group)
+        filtered = itertools.filterfalse(self._removed, defined)
+        loaded = map(lambda e: e.load(), filtered)
+        for ep in sorted(loaded, key=by_order):
+            ep(self)
+
+    @staticmethod
+    def _removed(ep):
+        """
+        When removing an entry point, if metadata is loaded
+        from an older version of Setuptools, that removed
+        entry point will attempt to be loaded and will fail.
+        See #2765 for more details.
+        """
+        removed = {
+            # removed 2021-09-05
+            '2to3_doctests',
+        }
+        return ep.name in removed
+
+    def _finalize_setup_keywords(self):
+        for ep in metadata.entry_points(group='distutils.setup_keywords'):
+            value = getattr(self, ep.name, None)
+            if value is not None:
+                ep.load()(self, ep.name, value)
+
+    def get_egg_cache_dir(self):
+        from . import windows_support
+
+        egg_cache_dir = os.path.join(os.curdir, '.eggs')
+        if not os.path.exists(egg_cache_dir):
+            os.mkdir(egg_cache_dir)
+            windows_support.hide_file(egg_cache_dir)
+            readme_txt_filename = os.path.join(egg_cache_dir, 'README.txt')
+            with open(readme_txt_filename, 'w', encoding="utf-8") as f:
+                f.write(
+                    'This directory contains eggs that were downloaded '
+                    'by setuptools to build, test, and run plug-ins.\n\n'
+                )
+                f.write(
+                    'This directory caches those eggs to prevent '
+                    'repeated downloads.\n\n'
+                )
+                f.write('However, it is safe to delete this directory.\n\n')
+
+        return egg_cache_dir
+
+    def fetch_build_egg(self, req):
+        """Fetch an egg needed for building"""
+        from .installer import fetch_build_egg
+
+        return fetch_build_egg(self, req)
+
+    def get_command_class(self, command: str) -> type[distutils.cmd.Command]:  # type: ignore[override] # Not doing complex overrides yet
+        """Pluggable version of get_command_class()"""
+        if command in self.cmdclass:
+            return self.cmdclass[command]
+
+        # Special case bdist_wheel so it's never loaded from "wheel"
+        if command == 'bdist_wheel':
+            from .command.bdist_wheel import bdist_wheel
+
+            return bdist_wheel
+
+        eps = metadata.entry_points(group='distutils.commands', name=command)
+        for ep in eps:
+            self.cmdclass[command] = cmdclass = ep.load()
+            return cmdclass
+        else:
+            return _Distribution.get_command_class(self, command)
+
+    def print_commands(self):
+        for ep in metadata.entry_points(group='distutils.commands'):
+            if ep.name not in self.cmdclass:
+                cmdclass = ep.load()
+                self.cmdclass[ep.name] = cmdclass
+        return _Distribution.print_commands(self)
+
+    def get_command_list(self):
+        for ep in metadata.entry_points(group='distutils.commands'):
+            if ep.name not in self.cmdclass:
+                cmdclass = ep.load()
+                self.cmdclass[ep.name] = cmdclass
+        return _Distribution.get_command_list(self)
+
+    def include(self, **attrs) -> None:
+        """Add items to distribution that are named in keyword arguments
+
+        For example, 'dist.include(py_modules=["x"])' would add 'x' to
+        the distribution's 'py_modules' attribute, if it was not already
+        there.
+
+        Currently, this method only supports inclusion for attributes that are
+        lists or tuples.  If you need to add support for adding to other
+        attributes in this or a subclass, you can add an '_include_X' method,
+        where 'X' is the name of the attribute.  The method will be called with
+        the value passed to 'include()'.  So, 'dist.include(foo={"bar":"baz"})'
+        will try to call 'dist._include_foo({"bar":"baz"})', which can then
+        handle whatever special inclusion logic is needed.
+        """
+        for k, v in attrs.items():
+            include = getattr(self, '_include_' + k, None)
+            if include:
+                include(v)
+            else:
+                self._include_misc(k, v)
+
+    def exclude_package(self, package: str) -> None:
+        """Remove packages, modules, and extensions in named package"""
+
+        pfx = package + '.'
+        if self.packages:
+            self.packages = [
+                p for p in self.packages if p != package and not p.startswith(pfx)
+            ]
+
+        if self.py_modules:
+            self.py_modules = [
+                p for p in self.py_modules if p != package and not p.startswith(pfx)
+            ]
+
+        if self.ext_modules:
+            self.ext_modules = [
+                p
+                for p in self.ext_modules
+                if p.name != package and not p.name.startswith(pfx)
+            ]
+
+    def has_contents_for(self, package: str) -> bool:
+        """Return true if 'exclude_package(package)' would do something"""
+
+        pfx = package + '.'
+
+        for p in self.iter_distribution_names():
+            if p == package or p.startswith(pfx):
+                return True
+
+        return False
+
+    def _exclude_misc(self, name: str, value: _Sequence) -> None:
+        """Handle 'exclude()' for list/tuple attrs without a special handler"""
+        if not isinstance(value, _sequence):
+            raise DistutilsSetupError(
+                f"{name}: setting must be of type <{_sequence_type_repr}> (got {value!r})"
+            )
+        try:
+            old = getattr(self, name)
+        except AttributeError as e:
+            raise DistutilsSetupError(f"{name}: No such distribution setting") from e
+        if old is not None and not isinstance(old, _sequence):
+            raise DistutilsSetupError(
+                name + ": this setting cannot be changed via include/exclude"
+            )
+        elif old:
+            setattr(self, name, [item for item in old if item not in value])
+
+    def _include_misc(self, name: str, value: _Sequence) -> None:
+        """Handle 'include()' for list/tuple attrs without a special handler"""
+
+        if not isinstance(value, _sequence):
+            raise DistutilsSetupError(
+                f"{name}: setting must be of type <{_sequence_type_repr}> (got {value!r})"
+            )
+        try:
+            old = getattr(self, name)
+        except AttributeError as e:
+            raise DistutilsSetupError(f"{name}: No such distribution setting") from e
+        if old is None:
+            setattr(self, name, value)
+        elif not isinstance(old, _sequence):
+            raise DistutilsSetupError(
+                name + ": this setting cannot be changed via include/exclude"
+            )
+        else:
+            new = [item for item in value if item not in old]
+            setattr(self, name, list(old) + new)
+
+    def exclude(self, **attrs) -> None:
+        """Remove items from distribution that are named in keyword arguments
+
+        For example, 'dist.exclude(py_modules=["x"])' would remove 'x' from
+        the distribution's 'py_modules' attribute.  Excluding packages uses
+        the 'exclude_package()' method, so all of the package's contained
+        packages, modules, and extensions are also excluded.
+
+        Currently, this method only supports exclusion from attributes that are
+        lists or tuples.  If you need to add support for excluding from other
+        attributes in this or a subclass, you can add an '_exclude_X' method,
+        where 'X' is the name of the attribute.  The method will be called with
+        the value passed to 'exclude()'.  So, 'dist.exclude(foo={"bar":"baz"})'
+        will try to call 'dist._exclude_foo({"bar":"baz"})', which can then
+        handle whatever special exclusion logic is needed.
+        """
+        for k, v in attrs.items():
+            exclude = getattr(self, '_exclude_' + k, None)
+            if exclude:
+                exclude(v)
+            else:
+                self._exclude_misc(k, v)
+
+    def _exclude_packages(self, packages: _Sequence) -> None:
+        if not isinstance(packages, _sequence):
+            raise DistutilsSetupError(
+                f"packages: setting must be of type <{_sequence_type_repr}> (got {packages!r})"
+            )
+        list(map(self.exclude_package, packages))
+
+    def _parse_command_opts(self, parser, args):
+        # Remove --with-X/--without-X options when processing command args
+        self.global_options = self.__class__.global_options
+        self.negative_opt = self.__class__.negative_opt
+
+        # First, expand any aliases
+        command = args[0]
+        aliases = self.get_option_dict('aliases')
+        while command in aliases:
+            _src, alias = aliases[command]
+            del aliases[command]  # ensure each alias can expand only once!
+            import shlex
+
+            args[:1] = shlex.split(alias, True)
+            command = args[0]
+
+        nargs = _Distribution._parse_command_opts(self, parser, args)
+
+        # Handle commands that want to consume all remaining arguments
+        cmd_class = self.get_command_class(command)
+        if getattr(cmd_class, 'command_consumes_arguments', None):
+            self.get_option_dict(command)['args'] = ("command line", nargs)
+            if nargs is not None:
+                return []
+
+        return nargs
+
+    def get_cmdline_options(self) -> dict[str, dict[str, str | None]]:
+        """Return a '{cmd: {opt:val}}' map of all command-line options
+
+        Option names are all long, but do not include the leading '--', and
+        contain dashes rather than underscores.  If the option doesn't take
+        an argument (e.g. '--quiet'), the 'val' is 'None'.
+
+        Note that options provided by config files are intentionally excluded.
+        """
+
+        d: dict[str, dict[str, str | None]] = {}
+
+        for cmd, opts in self.command_options.items():
+            val: str | None
+            for opt, (src, val) in opts.items():
+                if src != "command line":
+                    continue
+
+                opt = opt.replace('_', '-')
+
+                if val == 0:
+                    cmdobj = self.get_command_obj(cmd)
+                    neg_opt = self.negative_opt.copy()
+                    neg_opt.update(getattr(cmdobj, 'negative_opt', {}))
+                    for neg, pos in neg_opt.items():
+                        if pos == opt:
+                            opt = neg
+                            val = None
+                            break
+                    else:
+                        raise AssertionError("Shouldn't be able to get here")
+
+                elif val == 1:
+                    val = None
+
+                d.setdefault(cmd, {})[opt] = val
+
+        return d
+
+    def iter_distribution_names(self):
+        """Yield all packages, modules, and extension names in distribution"""
+
+        yield from self.packages or ()
+
+        yield from self.py_modules or ()
+
+        for ext in self.ext_modules or ():
+            if isinstance(ext, tuple):
+                name, _buildinfo = ext
+            else:
+                name = ext.name
+            if name.endswith('module'):
+                name = name[:-6]
+            yield name
+
+    def handle_display_options(self, option_order):
+        """If there were any non-global "display-only" options
+        (--help-commands or the metadata display options) on the command
+        line, display the requested info and return true; else return
+        false.
+        """
+        import sys
+
+        if self.help_commands:
+            return _Distribution.handle_display_options(self, option_order)
+
+        # Stdout may be StringIO (e.g. in tests)
+        if not isinstance(sys.stdout, io.TextIOWrapper):
+            return _Distribution.handle_display_options(self, option_order)
+
+        # Don't wrap stdout if utf-8 is already the encoding. Provides
+        #  workaround for #334.
+        if sys.stdout.encoding.lower() in ('utf-8', 'utf8'):
+            return _Distribution.handle_display_options(self, option_order)
+
+        # Print metadata in UTF-8 no matter the platform
+        encoding = sys.stdout.encoding
+        sys.stdout.reconfigure(encoding='utf-8')
+        try:
+            return _Distribution.handle_display_options(self, option_order)
+        finally:
+            sys.stdout.reconfigure(encoding=encoding)
+
+    def run_command(self, command) -> None:
+        self.set_defaults()
+        # Postpone defaults until all explicit configuration is considered
+        # (setup() args, config files, command line and plugins)
+
+        super().run_command(command)
+
+
+@functools.cache
+def _setuptools_commands() -> set[str]:
+    try:
+        # Use older API for importlib.metadata compatibility
+        entry_points = metadata.distribution('setuptools').entry_points
+        eps: Iterable[str] = (ep.name for ep in entry_points)
+    except metadata.PackageNotFoundError:
+        # during bootstrapping, distribution doesn't exist
+        eps = []
+    return {*distutils.command.__all__, *eps}
+
+
+class DistDeprecationWarning(SetuptoolsDeprecationWarning):
+    """Class for warning about deprecations in dist in
+    setuptools. Not ignored by default, unlike DeprecationWarning."""
diff --git a/.venv/lib/python3.12/site-packages/setuptools/errors.py b/.venv/lib/python3.12/site-packages/setuptools/errors.py
new file mode 100644
index 0000000000000000000000000000000000000000..990ecbf4e2f18eb188addc9e0466152a20193a90
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/errors.py
@@ -0,0 +1,67 @@
+"""setuptools.errors
+
+Provides exceptions used by setuptools modules.
+"""
+
+from __future__ import annotations
+
+from distutils import errors as _distutils_errors
+
+# Re-export errors from distutils to facilitate the migration to PEP632
+
+ByteCompileError = _distutils_errors.DistutilsByteCompileError
+CCompilerError = _distutils_errors.CCompilerError
+ClassError = _distutils_errors.DistutilsClassError
+CompileError = _distutils_errors.CompileError
+ExecError = _distutils_errors.DistutilsExecError
+FileError = _distutils_errors.DistutilsFileError
+InternalError = _distutils_errors.DistutilsInternalError
+LibError = _distutils_errors.LibError
+LinkError = _distutils_errors.LinkError
+ModuleError = _distutils_errors.DistutilsModuleError
+OptionError = _distutils_errors.DistutilsOptionError
+PlatformError = _distutils_errors.DistutilsPlatformError
+PreprocessError = _distutils_errors.PreprocessError
+SetupError = _distutils_errors.DistutilsSetupError
+TemplateError = _distutils_errors.DistutilsTemplateError
+UnknownFileError = _distutils_errors.UnknownFileError
+
+# The root error class in the hierarchy
+BaseError = _distutils_errors.DistutilsError
+
+
+class InvalidConfigError(OptionError):  # type: ignore[valid-type, misc] # distutils imports are `Any` on python 3.12+
+    """Error used for invalid configurations."""
+
+
+class RemovedConfigError(OptionError):  # type: ignore[valid-type, misc] # distutils imports are `Any` on python 3.12+
+    """Error used for configurations that were deprecated and removed."""
+
+
+class RemovedCommandError(BaseError, RuntimeError):  # type: ignore[valid-type, misc] # distutils imports are `Any` on python 3.12+
+    """Error used for commands that have been removed in setuptools.
+
+    Since ``setuptools`` is built on ``distutils``, simply removing a command
+    from ``setuptools`` will make the behavior fall back to ``distutils``; this
+    error is raised if a command exists in ``distutils`` but has been actively
+    removed in ``setuptools``.
+    """
+
+
+class PackageDiscoveryError(BaseError, RuntimeError):  # type: ignore[valid-type, misc] # distutils imports are `Any` on python 3.12+
+    """Impossible to perform automatic discovery of packages and/or modules.
+
+    The current project layout or given discovery options can lead to problems when
+    scanning the project directory.
+
+    Setuptools might also refuse to complete auto-discovery if an error prone condition
+    is detected (e.g. when a project is organised as a flat-layout but contains
+    multiple directories that can be taken as top-level packages inside a single
+    distribution [*]_). In these situations the users are encouraged to be explicit
+    about which packages to include or to make the discovery parameters more specific.
+
+    .. [*] Since multi-package distributions are uncommon it is very likely that the
+       developers did not intend for all the directories to be packaged, and are just
+       leaving auxiliary code in the repository top-level, such as maintenance-related
+       scripts.
+    """
diff --git a/.venv/lib/python3.12/site-packages/setuptools/extension.py b/.venv/lib/python3.12/site-packages/setuptools/extension.py
new file mode 100644
index 0000000000000000000000000000000000000000..76e03d9d6bdcdbd72e443e90c85d34429c22c261
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/extension.py
@@ -0,0 +1,177 @@
+from __future__ import annotations
+
+import functools
+import re
+from typing import TYPE_CHECKING
+
+from setuptools._path import StrPath
+
+from .monkey import get_unpatched
+
+import distutils.core
+import distutils.errors
+import distutils.extension
+
+
+def _have_cython():
+    """
+    Return True if Cython can be imported.
+    """
+    cython_impl = 'Cython.Distutils.build_ext'
+    try:
+        # from (cython_impl) import build_ext
+        __import__(cython_impl, fromlist=['build_ext']).build_ext
+    except Exception:
+        return False
+    return True
+
+
+# for compatibility
+have_pyrex = _have_cython
+if TYPE_CHECKING:
+    # Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962
+    from distutils.core import Extension as _Extension
+else:
+    _Extension = get_unpatched(distutils.core.Extension)
+
+
+class Extension(_Extension):
+    """
+    Describes a single extension module.
+
+    This means that all source files will be compiled into a single binary file
+    ``.`` (with ```` derived from ``name`` and
+    ```` defined by one of the values in
+    ``importlib.machinery.EXTENSION_SUFFIXES``).
+
+    In the case ``.pyx`` files are passed as ``sources and`` ``Cython`` is **not**
+    installed in the build environment, ``setuptools`` may also try to look for the
+    equivalent ``.cpp`` or ``.c`` files.
+
+    :arg str name:
+      the full name of the extension, including any packages -- ie.
+      *not* a filename or pathname, but Python dotted name
+
+    :arg list[str|os.PathLike[str]] sources:
+      list of source filenames, relative to the distribution root
+      (where the setup script lives), in Unix form (slash-separated)
+      for portability.  Source files may be C, C++, SWIG (.i),
+      platform-specific resource files, or whatever else is recognized
+      by the "build_ext" command as source for a Python extension.
+
+    :keyword list[str] include_dirs:
+      list of directories to search for C/C++ header files (in Unix
+      form for portability)
+
+    :keyword list[tuple[str, str|None]] define_macros:
+      list of macros to define; each macro is defined using a 2-tuple:
+      the first item corresponding to the name of the macro and the second
+      item either a string with its value or None to
+      define it without a particular value (equivalent of "#define
+      FOO" in source or -DFOO on Unix C compiler command line)
+
+    :keyword list[str] undef_macros:
+      list of macros to undefine explicitly
+
+    :keyword list[str] library_dirs:
+      list of directories to search for C/C++ libraries at link time
+
+    :keyword list[str] libraries:
+      list of library names (not filenames or paths) to link against
+
+    :keyword list[str] runtime_library_dirs:
+      list of directories to search for C/C++ libraries at run time
+      (for shared extensions, this is when the extension is loaded).
+      Setting this will cause an exception during build on Windows
+      platforms.
+
+    :keyword list[str] extra_objects:
+      list of extra files to link with (eg. object files not implied
+      by 'sources', static library that must be explicitly specified,
+      binary resource files, etc.)
+
+    :keyword list[str] extra_compile_args:
+      any extra platform- and compiler-specific information to use
+      when compiling the source files in 'sources'.  For platforms and
+      compilers where "command line" makes sense, this is typically a
+      list of command-line arguments, but for other platforms it could
+      be anything.
+
+    :keyword list[str] extra_link_args:
+      any extra platform- and compiler-specific information to use
+      when linking object files together to create the extension (or
+      to create a new static Python interpreter).  Similar
+      interpretation as for 'extra_compile_args'.
+
+    :keyword list[str] export_symbols:
+      list of symbols to be exported from a shared extension.  Not
+      used on all platforms, and not generally necessary for Python
+      extensions, which typically export exactly one symbol: "init" +
+      extension_name.
+
+    :keyword list[str] swig_opts:
+      any extra options to pass to SWIG if a source file has the .i
+      extension.
+
+    :keyword list[str] depends:
+      list of files that the extension depends on
+
+    :keyword str language:
+      extension language (i.e. "c", "c++", "objc"). Will be detected
+      from the source extensions if not provided.
+
+    :keyword bool optional:
+      specifies that a build failure in the extension should not abort the
+      build process, but simply not install the failing extension.
+
+    :keyword bool py_limited_api:
+      opt-in flag for the usage of :doc:`Python's limited API `.
+
+    :raises setuptools.errors.PlatformError: if ``runtime_library_dirs`` is
+      specified on Windows. (since v63)
+    """
+
+    # These 4 are set and used in setuptools/command/build_ext.py
+    # The lack of a default value and risk of `AttributeError` is purposeful
+    # to avoid people forgetting to call finalize_options if they modify the extension list.
+    # See example/rationale in https://github.com/pypa/setuptools/issues/4529.
+    _full_name: str  #: Private API, internal use only.
+    _links_to_dynamic: bool  #: Private API, internal use only.
+    _needs_stub: bool  #: Private API, internal use only.
+    _file_name: str  #: Private API, internal use only.
+
+    def __init__(
+        self,
+        name: str,
+        sources: list[StrPath],
+        *args,
+        py_limited_api: bool = False,
+        **kw,
+    ) -> None:
+        # The *args is needed for compatibility as calls may use positional
+        # arguments. py_limited_api may be set only via keyword.
+        self.py_limited_api = py_limited_api
+        super().__init__(
+            name,
+            sources,  # type: ignore[arg-type] # Vendored version of setuptools supports PathLike
+            *args,
+            **kw,
+        )
+
+    def _convert_pyx_sources_to_lang(self):
+        """
+        Replace sources with .pyx extensions to sources with the target
+        language extension. This mechanism allows language authors to supply
+        pre-converted sources but to prefer the .pyx sources.
+        """
+        if _have_cython():
+            # the build has Cython, so allow it to compile the .pyx files
+            return
+        lang = self.language or ''
+        target_ext = '.cpp' if lang.lower() == 'c++' else '.c'
+        sub = functools.partial(re.sub, '.pyx$', target_ext)
+        self.sources = list(map(sub, self.sources))
+
+
+class Library(Extension):
+    """Just like a regular Extension, but built as a library instead"""
diff --git a/.venv/lib/python3.12/site-packages/setuptools/glob.py b/.venv/lib/python3.12/site-packages/setuptools/glob.py
new file mode 100644
index 0000000000000000000000000000000000000000..1dfff2cd50ff87b8cef9d936f1fc9d4a2478b136
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/glob.py
@@ -0,0 +1,185 @@
+"""
+Filename globbing utility. Mostly a copy of `glob` from Python 3.5.
+
+Changes include:
+ * `yield from` and PEP3102 `*` removed.
+ * Hidden files are not ignored.
+"""
+
+from __future__ import annotations
+
+import fnmatch
+import os
+import re
+from collections.abc import Iterable, Iterator
+from typing import TYPE_CHECKING, AnyStr, overload
+
+if TYPE_CHECKING:
+    from _typeshed import BytesPath, StrOrBytesPath, StrPath
+
+__all__ = ["glob", "iglob", "escape"]
+
+
+def glob(pathname: AnyStr, recursive: bool = False) -> list[AnyStr]:
+    """Return a list of paths matching a pathname pattern.
+
+    The pattern may contain simple shell-style wildcards a la
+    fnmatch. However, unlike fnmatch, filenames starting with a
+    dot are special cases that are not matched by '*' and '?'
+    patterns.
+
+    If recursive is true, the pattern '**' will match any files and
+    zero or more directories and subdirectories.
+    """
+    return list(iglob(pathname, recursive=recursive))
+
+
+def iglob(pathname: AnyStr, recursive: bool = False) -> Iterator[AnyStr]:
+    """Return an iterator which yields the paths matching a pathname pattern.
+
+    The pattern may contain simple shell-style wildcards a la
+    fnmatch. However, unlike fnmatch, filenames starting with a
+    dot are special cases that are not matched by '*' and '?'
+    patterns.
+
+    If recursive is true, the pattern '**' will match any files and
+    zero or more directories and subdirectories.
+    """
+    it = _iglob(pathname, recursive)
+    if recursive and _isrecursive(pathname):
+        s = next(it)  # skip empty string
+        assert not s
+    return it
+
+
+def _iglob(pathname: AnyStr, recursive: bool) -> Iterator[AnyStr]:
+    dirname, basename = os.path.split(pathname)
+    glob_in_dir = glob2 if recursive and _isrecursive(basename) else glob1
+
+    if not has_magic(pathname):
+        if basename:
+            if os.path.lexists(pathname):
+                yield pathname
+        else:
+            # Patterns ending with a slash should match only directories
+            if os.path.isdir(dirname):
+                yield pathname
+        return
+
+    if not dirname:
+        yield from glob_in_dir(dirname, basename)
+        return
+    # `os.path.split()` returns the argument itself as a dirname if it is a
+    # drive or UNC path.  Prevent an infinite recursion if a drive or UNC path
+    # contains magic characters (i.e. r'\\?\C:').
+    if dirname != pathname and has_magic(dirname):
+        dirs: Iterable[AnyStr] = _iglob(dirname, recursive)
+    else:
+        dirs = [dirname]
+    if not has_magic(basename):
+        glob_in_dir = glob0
+    for dirname in dirs:
+        for name in glob_in_dir(dirname, basename):
+            yield os.path.join(dirname, name)
+
+
+# These 2 helper functions non-recursively glob inside a literal directory.
+# They return a list of basenames. `glob1` accepts a pattern while `glob0`
+# takes a literal basename (so it only has to check for its existence).
+
+
+@overload
+def glob1(dirname: StrPath, pattern: str) -> list[str]: ...
+@overload
+def glob1(dirname: BytesPath, pattern: bytes) -> list[bytes]: ...
+def glob1(dirname: StrOrBytesPath, pattern: str | bytes) -> list[str] | list[bytes]:
+    if not dirname:
+        if isinstance(pattern, bytes):
+            dirname = os.curdir.encode('ASCII')
+        else:
+            dirname = os.curdir
+    try:
+        names = os.listdir(dirname)
+    except OSError:
+        return []
+    # mypy false-positives: str or bytes type possibility is always kept in sync
+    return fnmatch.filter(names, pattern)  # type: ignore[type-var, return-value]
+
+
+def glob0(dirname, basename):
+    if not basename:
+        # `os.path.split()` returns an empty basename for paths ending with a
+        # directory separator.  'q*x/' should match only directories.
+        if os.path.isdir(dirname):
+            return [basename]
+    else:
+        if os.path.lexists(os.path.join(dirname, basename)):
+            return [basename]
+    return []
+
+
+# This helper function recursively yields relative pathnames inside a literal
+# directory.
+
+
+@overload
+def glob2(dirname: StrPath, pattern: str) -> Iterator[str]: ...
+@overload
+def glob2(dirname: BytesPath, pattern: bytes) -> Iterator[bytes]: ...
+def glob2(dirname: StrOrBytesPath, pattern: str | bytes) -> Iterator[str | bytes]:
+    assert _isrecursive(pattern)
+    yield pattern[:0]
+    yield from _rlistdir(dirname)
+
+
+# Recursively yields relative pathnames inside a literal directory.
+@overload
+def _rlistdir(dirname: StrPath) -> Iterator[str]: ...
+@overload
+def _rlistdir(dirname: BytesPath) -> Iterator[bytes]: ...
+def _rlistdir(dirname: StrOrBytesPath) -> Iterator[str | bytes]:
+    if not dirname:
+        if isinstance(dirname, bytes):
+            dirname = os.curdir.encode('ASCII')
+        else:
+            dirname = os.curdir
+    try:
+        names = os.listdir(dirname)
+    except OSError:
+        return
+    for x in names:
+        yield x
+        # mypy false-positives: str or bytes type possibility is always kept in sync
+        path = os.path.join(dirname, x) if dirname else x  # type: ignore[arg-type]
+        for y in _rlistdir(path):
+            yield os.path.join(x, y)  # type: ignore[arg-type]
+
+
+magic_check = re.compile('([*?[])')
+magic_check_bytes = re.compile(b'([*?[])')
+
+
+def has_magic(s: str | bytes) -> bool:
+    if isinstance(s, bytes):
+        return magic_check_bytes.search(s) is not None
+    else:
+        return magic_check.search(s) is not None
+
+
+def _isrecursive(pattern: str | bytes) -> bool:
+    if isinstance(pattern, bytes):
+        return pattern == b'**'
+    else:
+        return pattern == '**'
+
+
+def escape(pathname):
+    """Escape all special characters."""
+    # Escaping is done by wrapping any of "*?[" between square brackets.
+    # Metacharacters do not work in the drive part and shouldn't be escaped.
+    drive, pathname = os.path.splitdrive(pathname)
+    if isinstance(pathname, bytes):
+        pathname = magic_check_bytes.sub(rb'[\1]', pathname)
+    else:
+        pathname = magic_check.sub(r'[\1]', pathname)
+    return drive + pathname
diff --git a/.venv/lib/python3.12/site-packages/setuptools/gui-32.exe b/.venv/lib/python3.12/site-packages/setuptools/gui-32.exe
new file mode 100644
index 0000000000000000000000000000000000000000..1eb430c6d614a5daea4139badc09c222a4b0e72a
Binary files /dev/null and b/.venv/lib/python3.12/site-packages/setuptools/gui-32.exe differ
diff --git a/.venv/lib/python3.12/site-packages/setuptools/gui-64.exe b/.venv/lib/python3.12/site-packages/setuptools/gui-64.exe
new file mode 100644
index 0000000000000000000000000000000000000000..031cb77c17ba8d8a983448268851d612e05e80d1
Binary files /dev/null and b/.venv/lib/python3.12/site-packages/setuptools/gui-64.exe differ
diff --git a/.venv/lib/python3.12/site-packages/setuptools/gui-arm64.exe b/.venv/lib/python3.12/site-packages/setuptools/gui-arm64.exe
new file mode 100644
index 0000000000000000000000000000000000000000..1e00ffacb182c2af206e5dd9d9fbc41d236da0d1
Binary files /dev/null and b/.venv/lib/python3.12/site-packages/setuptools/gui-arm64.exe differ
diff --git a/.venv/lib/python3.12/site-packages/setuptools/gui.exe b/.venv/lib/python3.12/site-packages/setuptools/gui.exe
new file mode 100644
index 0000000000000000000000000000000000000000..1eb430c6d614a5daea4139badc09c222a4b0e72a
Binary files /dev/null and b/.venv/lib/python3.12/site-packages/setuptools/gui.exe differ
diff --git a/.venv/lib/python3.12/site-packages/setuptools/installer.py b/.venv/lib/python3.12/site-packages/setuptools/installer.py
new file mode 100644
index 0000000000000000000000000000000000000000..2c26e3a1f4ec6a271198fffb7489dc6e6e612c06
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/installer.py
@@ -0,0 +1,155 @@
+from __future__ import annotations
+
+import glob
+import itertools
+import os
+import subprocess
+import sys
+import tempfile
+
+import packaging.requirements
+import packaging.utils
+
+from . import _reqs
+from ._importlib import metadata
+from .warnings import SetuptoolsDeprecationWarning
+from .wheel import Wheel
+
+from distutils import log
+from distutils.errors import DistutilsError
+
+
+def _fixup_find_links(find_links):
+    """Ensure find-links option end-up being a list of strings."""
+    if isinstance(find_links, str):
+        return find_links.split()
+    assert isinstance(find_links, (tuple, list))
+    return find_links
+
+
+def fetch_build_egg(dist, req):
+    """Fetch an egg needed for building.
+
+    Use pip/wheel to fetch/build a wheel."""
+    _DeprecatedInstaller.emit()
+    _warn_wheel_not_available(dist)
+    return _fetch_build_egg_no_warn(dist, req)
+
+
+def _present(req):
+    return any(_dist_matches_req(dist, req) for dist in metadata.distributions())
+
+
+def _fetch_build_eggs(dist, requires: _reqs._StrOrIter) -> list[metadata.Distribution]:
+    _DeprecatedInstaller.emit(stacklevel=3)
+    _warn_wheel_not_available(dist)
+
+    parsed_reqs = _reqs.parse(requires)
+
+    missing_reqs = itertools.filterfalse(_present, parsed_reqs)
+
+    needed_reqs = (
+        req for req in missing_reqs if not req.marker or req.marker.evaluate()
+    )
+    resolved_dists = [_fetch_build_egg_no_warn(dist, req) for req in needed_reqs]
+    for dist in resolved_dists:
+        # dist.locate_file('') is the directory containing EGG-INFO, where the importabl
+        # contents can be found.
+        sys.path.insert(0, str(dist.locate_file('')))
+    return resolved_dists
+
+
+def _dist_matches_req(egg_dist, req):
+    return (
+        packaging.utils.canonicalize_name(egg_dist.name)
+        == packaging.utils.canonicalize_name(req.name)
+        and egg_dist.version in req.specifier
+    )
+
+
+def _fetch_build_egg_no_warn(dist, req):  # noqa: C901  # is too complex (16)  # FIXME
+    # Ignore environment markers; if supplied, it is required.
+    req = strip_marker(req)
+    # Take easy_install options into account, but do not override relevant
+    # pip environment variables (like PIP_INDEX_URL or PIP_QUIET); they'll
+    # take precedence.
+    opts = dist.get_option_dict('easy_install')
+    if 'allow_hosts' in opts:
+        raise DistutilsError(
+            'the `allow-hosts` option is not supported '
+            'when using pip to install requirements.'
+        )
+    quiet = 'PIP_QUIET' not in os.environ and 'PIP_VERBOSE' not in os.environ
+    if 'PIP_INDEX_URL' in os.environ:
+        index_url = None
+    elif 'index_url' in opts:
+        index_url = opts['index_url'][1]
+    else:
+        index_url = None
+    find_links = (
+        _fixup_find_links(opts['find_links'][1])[:] if 'find_links' in opts else []
+    )
+    if dist.dependency_links:
+        find_links.extend(dist.dependency_links)
+    eggs_dir = os.path.realpath(dist.get_egg_cache_dir())
+    cached_dists = metadata.Distribution.discover(path=glob.glob(f'{eggs_dir}/*.egg'))
+    for egg_dist in cached_dists:
+        if _dist_matches_req(egg_dist, req):
+            return egg_dist
+    with tempfile.TemporaryDirectory() as tmpdir:
+        cmd = [
+            sys.executable,
+            '-m',
+            'pip',
+            '--disable-pip-version-check',
+            'wheel',
+            '--no-deps',
+            '-w',
+            tmpdir,
+        ]
+        if quiet:
+            cmd.append('--quiet')
+        if index_url is not None:
+            cmd.extend(('--index-url', index_url))
+        for link in find_links or []:
+            cmd.extend(('--find-links', link))
+        # If requirement is a PEP 508 direct URL, directly pass
+        # the URL to pip, as `req @ url` does not work on the
+        # command line.
+        cmd.append(req.url or str(req))
+        try:
+            subprocess.check_call(cmd)
+        except subprocess.CalledProcessError as e:
+            raise DistutilsError(str(e)) from e
+        wheel = Wheel(glob.glob(os.path.join(tmpdir, '*.whl'))[0])
+        dist_location = os.path.join(eggs_dir, wheel.egg_name())
+        wheel.install_as_egg(dist_location)
+        return metadata.Distribution.at(dist_location + '/EGG-INFO')
+
+
+def strip_marker(req):
+    """
+    Return a new requirement without the environment marker to avoid
+    calling pip with something like `babel; extra == "i18n"`, which
+    would always be ignored.
+    """
+    # create a copy to avoid mutating the input
+    req = packaging.requirements.Requirement(str(req))
+    req.marker = None
+    return req
+
+
+def _warn_wheel_not_available(dist):
+    try:
+        metadata.distribution('wheel')
+    except metadata.PackageNotFoundError:
+        dist.announce('WARNING: The wheel package is not available.', log.WARN)
+
+
+class _DeprecatedInstaller(SetuptoolsDeprecationWarning):
+    _SUMMARY = "setuptools.installer and fetch_build_eggs are deprecated."
+    _DETAILS = """
+    Requirements should be satisfied by a PEP 517 installer.
+    If you are using pip, you can try `pip install --use-pep517`.
+    """
+    _DUE_DATE = 2025, 10, 31
diff --git a/.venv/lib/python3.12/site-packages/setuptools/launch.py b/.venv/lib/python3.12/site-packages/setuptools/launch.py
new file mode 100644
index 0000000000000000000000000000000000000000..0d162647d55777d7afa1bf1e44a6c200a3f82419
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/launch.py
@@ -0,0 +1,36 @@
+"""
+Launch the Python script on the command line after
+setuptools is bootstrapped via import.
+"""
+
+# Note that setuptools gets imported implicitly by the
+# invocation of this script using python -m setuptools.launch
+
+import sys
+import tokenize
+
+
+def run() -> None:
+    """
+    Run the script in sys.argv[1] as if it had
+    been invoked naturally.
+    """
+    __builtins__
+    script_name = sys.argv[1]
+    namespace = dict(
+        __file__=script_name,
+        __name__='__main__',
+        __doc__=None,
+    )
+    sys.argv[:] = sys.argv[1:]
+
+    open_ = getattr(tokenize, 'open', open)
+    with open_(script_name) as fid:
+        script = fid.read()
+    norm_script = script.replace('\\r\\n', '\\n')
+    code = compile(norm_script, script_name, 'exec')
+    exec(code, namespace)
+
+
+if __name__ == '__main__':
+    run()
diff --git a/.venv/lib/python3.12/site-packages/setuptools/logging.py b/.venv/lib/python3.12/site-packages/setuptools/logging.py
new file mode 100644
index 0000000000000000000000000000000000000000..532da899f7dc02f9fea9a44c429086b98fe043d8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/logging.py
@@ -0,0 +1,40 @@
+import inspect
+import logging
+import sys
+
+from . import monkey
+
+import distutils.log
+
+
+def _not_warning(record):
+    return record.levelno < logging.WARNING
+
+
+def configure() -> None:
+    """
+    Configure logging to emit warning and above to stderr
+    and everything else to stdout. This behavior is provided
+    for compatibility with distutils.log but may change in
+    the future.
+    """
+    err_handler = logging.StreamHandler()
+    err_handler.setLevel(logging.WARNING)
+    out_handler = logging.StreamHandler(sys.stdout)
+    out_handler.addFilter(_not_warning)
+    handlers = err_handler, out_handler
+    logging.basicConfig(
+        format="{message}", style='{', handlers=handlers, level=logging.DEBUG
+    )
+    if inspect.ismodule(distutils.dist.log):
+        monkey.patch_func(set_threshold, distutils.log, 'set_threshold')
+        # For some reason `distutils.log` module is getting cached in `distutils.dist`
+        # and then loaded again when patched,
+        # implying: id(distutils.log) != id(distutils.dist.log).
+        # Make sure the same module object is used everywhere:
+        distutils.dist.log = distutils.log
+
+
+def set_threshold(level: int) -> int:
+    logging.root.setLevel(level * 10)
+    return set_threshold.unpatched(level)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/modified.py b/.venv/lib/python3.12/site-packages/setuptools/modified.py
new file mode 100644
index 0000000000000000000000000000000000000000..6ba02fab68734e1e96fd50d7c4b6ffb1442717fb
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/modified.py
@@ -0,0 +1,18 @@
+try:
+    # Ensure a DistutilsError raised by these methods is the same as distutils.errors.DistutilsError
+    from distutils._modified import (
+        newer,
+        newer_group,
+        newer_pairwise,
+        newer_pairwise_group,
+    )
+except ImportError:
+    # fallback for SETUPTOOLS_USE_DISTUTILS=stdlib, because _modified never existed in stdlib
+    from ._distutils._modified import (
+        newer,
+        newer_group,
+        newer_pairwise,
+        newer_pairwise_group,
+    )
+
+__all__ = ['newer', 'newer_pairwise', 'newer_group', 'newer_pairwise_group']
diff --git a/.venv/lib/python3.12/site-packages/setuptools/monkey.py b/.venv/lib/python3.12/site-packages/setuptools/monkey.py
new file mode 100644
index 0000000000000000000000000000000000000000..6ad1abac295c1df613942b8896edf089a103ae1f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/monkey.py
@@ -0,0 +1,126 @@
+"""
+Monkey patching of distutils.
+"""
+
+from __future__ import annotations
+
+import inspect
+import platform
+import sys
+import types
+from typing import TypeVar, cast, overload
+
+import distutils.filelist
+
+_T = TypeVar("_T")
+_UnpatchT = TypeVar("_UnpatchT", type, types.FunctionType)
+
+
+__all__: list[str] = []
+"""
+Everything is private. Contact the project team
+if you think you need this functionality.
+"""
+
+
+def _get_mro(cls):
+    """
+    Returns the bases classes for cls sorted by the MRO.
+
+    Works around an issue on Jython where inspect.getmro will not return all
+    base classes if multiple classes share the same name. Instead, this
+    function will return a tuple containing the class itself, and the contents
+    of cls.__bases__. See https://github.com/pypa/setuptools/issues/1024.
+    """
+    if platform.python_implementation() == "Jython":
+        return (cls,) + cls.__bases__
+    return inspect.getmro(cls)
+
+
+@overload
+def get_unpatched(item: _UnpatchT) -> _UnpatchT: ...
+@overload
+def get_unpatched(item: object) -> None: ...
+def get_unpatched(
+    item: type | types.FunctionType | object,
+) -> type | types.FunctionType | None:
+    if isinstance(item, type):
+        return get_unpatched_class(item)
+    if isinstance(item, types.FunctionType):
+        return get_unpatched_function(item)
+    return None
+
+
+def get_unpatched_class(cls: type[_T]) -> type[_T]:
+    """Protect against re-patching the distutils if reloaded
+
+    Also ensures that no other distutils extension monkeypatched the distutils
+    first.
+    """
+    external_bases = (
+        cast(type[_T], cls)
+        for cls in _get_mro(cls)
+        if not cls.__module__.startswith('setuptools')
+    )
+    base = next(external_bases)
+    if not base.__module__.startswith('distutils'):
+        msg = f"distutils has already been patched by {cls!r}"
+        raise AssertionError(msg)
+    return base
+
+
+def patch_all():
+    import setuptools
+
+    # we can't patch distutils.cmd, alas
+    distutils.core.Command = setuptools.Command  # type: ignore[misc,assignment] # monkeypatching
+
+    _patch_distribution_metadata()
+
+    # Install Distribution throughout the distutils
+    for module in distutils.dist, distutils.core, distutils.cmd:
+        module.Distribution = setuptools.dist.Distribution
+
+    # Install the patched Extension
+    distutils.core.Extension = setuptools.extension.Extension  # type: ignore[misc,assignment] # monkeypatching
+    distutils.extension.Extension = setuptools.extension.Extension  # type: ignore[misc,assignment] # monkeypatching
+    if 'distutils.command.build_ext' in sys.modules:
+        sys.modules[
+            'distutils.command.build_ext'
+        ].Extension = setuptools.extension.Extension
+
+
+def _patch_distribution_metadata():
+    from . import _core_metadata
+
+    """Patch write_pkg_file and read_pkg_file for higher metadata standards"""
+    for attr in (
+        'write_pkg_info',
+        'write_pkg_file',
+        'read_pkg_file',
+        'get_metadata_version',
+        'get_fullname',
+    ):
+        new_val = getattr(_core_metadata, attr)
+        setattr(distutils.dist.DistributionMetadata, attr, new_val)
+
+
+def patch_func(replacement, target_mod, func_name):
+    """
+    Patch func_name in target_mod with replacement
+
+    Important - original must be resolved by name to avoid
+    patching an already patched function.
+    """
+    original = getattr(target_mod, func_name)
+
+    # set the 'unpatched' attribute on the replacement to
+    # point to the original.
+    vars(replacement).setdefault('unpatched', original)
+
+    # replace the function in the original module
+    setattr(target_mod, func_name, replacement)
+
+
+def get_unpatched_function(candidate):
+    return candidate.unpatched
diff --git a/.venv/lib/python3.12/site-packages/setuptools/msvc.py b/.venv/lib/python3.12/site-packages/setuptools/msvc.py
new file mode 100644
index 0000000000000000000000000000000000000000..313a781ae095bbb20874795e5b87396790f0b9c9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/msvc.py
@@ -0,0 +1,1536 @@
+"""
+Environment info about Microsoft Compilers.
+
+>>> getfixture('windows_only')
+>>> ei = EnvironmentInfo('amd64')
+"""
+
+from __future__ import annotations
+
+import contextlib
+import itertools
+import json
+import os
+import os.path
+import platform
+from typing import TYPE_CHECKING, TypedDict
+
+from more_itertools import unique_everseen
+
+import distutils.errors
+
+if TYPE_CHECKING:
+    from typing_extensions import LiteralString, NotRequired
+
+# https://github.com/python/mypy/issues/8166
+if not TYPE_CHECKING and platform.system() == 'Windows':
+    import winreg
+    from os import environ
+else:
+    # Mock winreg and environ so the module can be imported on this platform.
+
+    class winreg:
+        HKEY_USERS = None
+        HKEY_CURRENT_USER = None
+        HKEY_LOCAL_MACHINE = None
+        HKEY_CLASSES_ROOT = None
+
+    environ: dict[str, str] = dict()
+
+
+class PlatformInfo:
+    """
+    Current and Target Architectures information.
+
+    Parameters
+    ----------
+    arch: str
+        Target architecture.
+    """
+
+    current_cpu = environ.get('processor_architecture', '').lower()
+
+    def __init__(self, arch) -> None:
+        self.arch = arch.lower().replace('x64', 'amd64')
+
+    @property
+    def target_cpu(self):
+        """
+        Return Target CPU architecture.
+
+        Return
+        ------
+        str
+            Target CPU
+        """
+        return self.arch[self.arch.find('_') + 1 :]
+
+    def target_is_x86(self):
+        """
+        Return True if target CPU is x86 32 bits..
+
+        Return
+        ------
+        bool
+            CPU is x86 32 bits
+        """
+        return self.target_cpu == 'x86'
+
+    def current_is_x86(self):
+        """
+        Return True if current CPU is x86 32 bits..
+
+        Return
+        ------
+        bool
+            CPU is x86 32 bits
+        """
+        return self.current_cpu == 'x86'
+
+    def current_dir(self, hidex86=False, x64=False) -> str:
+        """
+        Current platform specific subfolder.
+
+        Parameters
+        ----------
+        hidex86: bool
+            return '' and not '\x86' if architecture is x86.
+        x64: bool
+            return '\x64' and not '\amd64' if architecture is amd64.
+
+        Return
+        ------
+        str
+            subfolder: '\target', or '' (see hidex86 parameter)
+        """
+        return (
+            ''
+            if (self.current_cpu == 'x86' and hidex86)
+            else r'\x64'
+            if (self.current_cpu == 'amd64' and x64)
+            else rf'\{self.current_cpu}'
+        )
+
+    def target_dir(self, hidex86=False, x64=False) -> str:
+        r"""
+        Target platform specific subfolder.
+
+        Parameters
+        ----------
+        hidex86: bool
+            return '' and not '\x86' if architecture is x86.
+        x64: bool
+            return '\x64' and not '\amd64' if architecture is amd64.
+
+        Return
+        ------
+        str
+            subfolder: '\current', or '' (see hidex86 parameter)
+        """
+        return (
+            ''
+            if (self.target_cpu == 'x86' and hidex86)
+            else r'\x64'
+            if (self.target_cpu == 'amd64' and x64)
+            else rf'\{self.target_cpu}'
+        )
+
+    def cross_dir(self, forcex86=False):
+        r"""
+        Cross platform specific subfolder.
+
+        Parameters
+        ----------
+        forcex86: bool
+            Use 'x86' as current architecture even if current architecture is
+            not x86.
+
+        Return
+        ------
+        str
+            subfolder: '' if target architecture is current architecture,
+            '\current_target' if not.
+        """
+        current = 'x86' if forcex86 else self.current_cpu
+        return (
+            ''
+            if self.target_cpu == current
+            else self.target_dir().replace('\\', f'\\{current}_')
+        )
+
+
+class RegistryInfo:
+    """
+    Microsoft Visual Studio related registry information.
+
+    Parameters
+    ----------
+    platform_info: PlatformInfo
+        "PlatformInfo" instance.
+    """
+
+    HKEYS = (
+        winreg.HKEY_USERS,
+        winreg.HKEY_CURRENT_USER,
+        winreg.HKEY_LOCAL_MACHINE,
+        winreg.HKEY_CLASSES_ROOT,
+    )
+
+    def __init__(self, platform_info) -> None:
+        self.pi = platform_info
+
+    @property
+    def visualstudio(self) -> str:
+        """
+        Microsoft Visual Studio root registry key.
+
+        Return
+        ------
+        str
+            Registry key
+        """
+        return 'VisualStudio'
+
+    @property
+    def sxs(self):
+        """
+        Microsoft Visual Studio SxS registry key.
+
+        Return
+        ------
+        str
+            Registry key
+        """
+        return os.path.join(self.visualstudio, 'SxS')
+
+    @property
+    def vc(self):
+        """
+        Microsoft Visual C++ VC7 registry key.
+
+        Return
+        ------
+        str
+            Registry key
+        """
+        return os.path.join(self.sxs, 'VC7')
+
+    @property
+    def vs(self):
+        """
+        Microsoft Visual Studio VS7 registry key.
+
+        Return
+        ------
+        str
+            Registry key
+        """
+        return os.path.join(self.sxs, 'VS7')
+
+    @property
+    def vc_for_python(self) -> str:
+        """
+        Microsoft Visual C++ for Python registry key.
+
+        Return
+        ------
+        str
+            Registry key
+        """
+        return r'DevDiv\VCForPython'
+
+    @property
+    def microsoft_sdk(self) -> str:
+        """
+        Microsoft SDK registry key.
+
+        Return
+        ------
+        str
+            Registry key
+        """
+        return 'Microsoft SDKs'
+
+    @property
+    def windows_sdk(self):
+        """
+        Microsoft Windows/Platform SDK registry key.
+
+        Return
+        ------
+        str
+            Registry key
+        """
+        return os.path.join(self.microsoft_sdk, 'Windows')
+
+    @property
+    def netfx_sdk(self):
+        """
+        Microsoft .NET Framework SDK registry key.
+
+        Return
+        ------
+        str
+            Registry key
+        """
+        return os.path.join(self.microsoft_sdk, 'NETFXSDK')
+
+    @property
+    def windows_kits_roots(self) -> str:
+        """
+        Microsoft Windows Kits Roots registry key.
+
+        Return
+        ------
+        str
+            Registry key
+        """
+        return r'Windows Kits\Installed Roots'
+
+    def microsoft(self, key, x86=False):
+        """
+        Return key in Microsoft software registry.
+
+        Parameters
+        ----------
+        key: str
+            Registry key path where look.
+        x86: str
+            Force x86 software registry.
+
+        Return
+        ------
+        str
+            Registry key
+        """
+        node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node'
+        return os.path.join('Software', node64, 'Microsoft', key)
+
+    def lookup(self, key, name):
+        """
+        Look for values in registry in Microsoft software registry.
+
+        Parameters
+        ----------
+        key: str
+            Registry key path where look.
+        name: str
+            Value name to find.
+
+        Return
+        ------
+        str
+            value
+        """
+        key_read = winreg.KEY_READ
+        openkey = winreg.OpenKey
+        closekey = winreg.CloseKey
+        ms = self.microsoft
+        for hkey in self.HKEYS:
+            bkey = None
+            try:
+                bkey = openkey(hkey, ms(key), 0, key_read)
+            except OSError:
+                if not self.pi.current_is_x86():
+                    try:
+                        bkey = openkey(hkey, ms(key, True), 0, key_read)
+                    except OSError:
+                        continue
+                else:
+                    continue
+            try:
+                return winreg.QueryValueEx(bkey, name)[0]
+            except OSError:
+                pass
+            finally:
+                if bkey:
+                    closekey(bkey)
+        return None
+
+
+class SystemInfo:
+    """
+    Microsoft Windows and Visual Studio related system information.
+
+    Parameters
+    ----------
+    registry_info: RegistryInfo
+        "RegistryInfo" instance.
+    vc_ver: float
+        Required Microsoft Visual C++ version.
+    """
+
+    # Variables and properties in this class use originals CamelCase variables
+    # names from Microsoft source files for more easy comparison.
+    WinDir = environ.get('WinDir', '')
+    ProgramFiles = environ.get('ProgramFiles', '')
+    ProgramFilesx86 = environ.get('ProgramFiles(x86)', ProgramFiles)
+
+    def __init__(self, registry_info, vc_ver=None) -> None:
+        self.ri = registry_info
+        self.pi = self.ri.pi
+
+        self.known_vs_paths = self.find_programdata_vs_vers()
+
+        # Except for VS15+, VC version is aligned with VS version
+        self.vs_ver = self.vc_ver = vc_ver or self._find_latest_available_vs_ver()
+
+    def _find_latest_available_vs_ver(self):
+        """
+        Find the latest VC version
+
+        Return
+        ------
+        float
+            version
+        """
+        reg_vc_vers = self.find_reg_vs_vers()
+
+        if not (reg_vc_vers or self.known_vs_paths):
+            raise distutils.errors.DistutilsPlatformError(
+                'No Microsoft Visual C++ version found'
+            )
+
+        vc_vers = set(reg_vc_vers)
+        vc_vers.update(self.known_vs_paths)
+        return sorted(vc_vers)[-1]
+
+    def find_reg_vs_vers(self):
+        """
+        Find Microsoft Visual Studio versions available in registry.
+
+        Return
+        ------
+        list of float
+            Versions
+        """
+        ms = self.ri.microsoft
+        vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs)
+        vs_vers = []
+        for hkey, key in itertools.product(self.ri.HKEYS, vckeys):
+            try:
+                bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ)
+            except OSError:
+                continue
+            with bkey:
+                subkeys, values, _ = winreg.QueryInfoKey(bkey)
+                for i in range(values):
+                    with contextlib.suppress(ValueError):
+                        ver = float(winreg.EnumValue(bkey, i)[0])
+                        if ver not in vs_vers:
+                            vs_vers.append(ver)
+                for i in range(subkeys):
+                    with contextlib.suppress(ValueError):
+                        ver = float(winreg.EnumKey(bkey, i))
+                        if ver not in vs_vers:
+                            vs_vers.append(ver)
+        return sorted(vs_vers)
+
+    def find_programdata_vs_vers(self) -> dict[float, str]:
+        r"""
+        Find Visual studio 2017+ versions from information in
+        "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances".
+
+        Return
+        ------
+        dict
+            float version as key, path as value.
+        """
+        vs_versions: dict[float, str] = {}
+        instances_dir = r'C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances'
+
+        try:
+            hashed_names = os.listdir(instances_dir)
+
+        except OSError:
+            # Directory not exists with all Visual Studio versions
+            return vs_versions
+
+        for name in hashed_names:
+            try:
+                # Get VS installation path from "state.json" file
+                state_path = os.path.join(instances_dir, name, 'state.json')
+                with open(state_path, 'rt', encoding='utf-8') as state_file:
+                    state = json.load(state_file)
+                vs_path = state['installationPath']
+
+                # Raises OSError if this VS installation does not contain VC
+                os.listdir(os.path.join(vs_path, r'VC\Tools\MSVC'))
+
+                # Store version and path
+                vs_versions[self._as_float_version(state['installationVersion'])] = (
+                    vs_path
+                )
+
+            except (OSError, KeyError):
+                # Skip if "state.json" file is missing or bad format
+                continue
+
+        return vs_versions
+
+    @staticmethod
+    def _as_float_version(version):
+        """
+        Return a string version as a simplified float version (major.minor)
+
+        Parameters
+        ----------
+        version: str
+            Version.
+
+        Return
+        ------
+        float
+            version
+        """
+        return float('.'.join(version.split('.')[:2]))
+
+    @property
+    def VSInstallDir(self):
+        """
+        Microsoft Visual Studio directory.
+
+        Return
+        ------
+        str
+            path
+        """
+        # Default path
+        default = os.path.join(
+            self.ProgramFilesx86, f'Microsoft Visual Studio {self.vs_ver:0.1f}'
+        )
+
+        # Try to get path from registry, if fail use default path
+        return self.ri.lookup(self.ri.vs, f'{self.vs_ver:0.1f}') or default
+
+    @property
+    def VCInstallDir(self):
+        """
+        Microsoft Visual C++ directory.
+
+        Return
+        ------
+        str
+            path
+        """
+        path = self._guess_vc() or self._guess_vc_legacy()
+
+        if not os.path.isdir(path):
+            msg = 'Microsoft Visual C++ directory not found'
+            raise distutils.errors.DistutilsPlatformError(msg)
+
+        return path
+
+    def _guess_vc(self):
+        """
+        Locate Visual C++ for VS2017+.
+
+        Return
+        ------
+        str
+            path
+        """
+        if self.vs_ver <= 14.0:
+            return ''
+
+        try:
+            # First search in known VS paths
+            vs_dir = self.known_vs_paths[self.vs_ver]
+        except KeyError:
+            # Else, search with path from registry
+            vs_dir = self.VSInstallDir
+
+        guess_vc = os.path.join(vs_dir, r'VC\Tools\MSVC')
+
+        # Subdir with VC exact version as name
+        try:
+            # Update the VC version with real one instead of VS version
+            vc_ver = os.listdir(guess_vc)[-1]
+            self.vc_ver = self._as_float_version(vc_ver)
+            return os.path.join(guess_vc, vc_ver)
+        except (OSError, IndexError):
+            return ''
+
+    def _guess_vc_legacy(self):
+        """
+        Locate Visual C++ for versions prior to 2017.
+
+        Return
+        ------
+        str
+            path
+        """
+        default = os.path.join(
+            self.ProgramFilesx86,
+            rf'Microsoft Visual Studio {self.vs_ver:0.1f}\VC',
+        )
+
+        # Try to get "VC++ for Python" path from registry as default path
+        reg_path = os.path.join(self.ri.vc_for_python, f'{self.vs_ver:0.1f}')
+        python_vc = self.ri.lookup(reg_path, 'installdir')
+        default_vc = os.path.join(python_vc, 'VC') if python_vc else default
+
+        # Try to get path from registry, if fail use default path
+        return self.ri.lookup(self.ri.vc, f'{self.vs_ver:0.1f}') or default_vc
+
+    @property
+    def WindowsSdkVersion(self) -> tuple[LiteralString, ...]:
+        """
+        Microsoft Windows SDK versions for specified MSVC++ version.
+
+        Return
+        ------
+        tuple of str
+            versions
+        """
+        if self.vs_ver <= 9.0:
+            return '7.0', '6.1', '6.0a'
+        elif self.vs_ver == 10.0:
+            return '7.1', '7.0a'
+        elif self.vs_ver == 11.0:
+            return '8.0', '8.0a'
+        elif self.vs_ver == 12.0:
+            return '8.1', '8.1a'
+        elif self.vs_ver >= 14.0:
+            return '10.0', '8.1'
+        return ()
+
+    @property
+    def WindowsSdkLastVersion(self):
+        """
+        Microsoft Windows SDK last version.
+
+        Return
+        ------
+        str
+            version
+        """
+        return self._use_last_dir_name(os.path.join(self.WindowsSdkDir, 'lib'))
+
+    @property
+    def WindowsSdkDir(self) -> str | None:  # noqa: C901  # is too complex (12)  # FIXME
+        """
+        Microsoft Windows SDK directory.
+
+        Return
+        ------
+        str
+            path
+        """
+        sdkdir: str | None = ''
+        for ver in self.WindowsSdkVersion:
+            # Try to get it from registry
+            loc = os.path.join(self.ri.windows_sdk, f'v{ver}')
+            sdkdir = self.ri.lookup(loc, 'installationfolder')
+            if sdkdir:
+                break
+        if not sdkdir or not os.path.isdir(sdkdir):
+            # Try to get "VC++ for Python" version from registry
+            path = os.path.join(self.ri.vc_for_python, f'{self.vc_ver:0.1f}')
+            install_base = self.ri.lookup(path, 'installdir')
+            if install_base:
+                sdkdir = os.path.join(install_base, 'WinSDK')
+        if not sdkdir or not os.path.isdir(sdkdir):
+            # If fail, use default new path
+            for ver in self.WindowsSdkVersion:
+                intver = ver[: ver.rfind('.')]
+                path = rf'Microsoft SDKs\Windows Kits\{intver}'
+                d = os.path.join(self.ProgramFiles, path)
+                if os.path.isdir(d):
+                    sdkdir = d
+        if not sdkdir or not os.path.isdir(sdkdir):
+            # If fail, use default old path
+            for ver in self.WindowsSdkVersion:
+                path = rf'Microsoft SDKs\Windows\v{ver}'
+                d = os.path.join(self.ProgramFiles, path)
+                if os.path.isdir(d):
+                    sdkdir = d
+        if not sdkdir:
+            # If fail, use Platform SDK
+            sdkdir = os.path.join(self.VCInstallDir, 'PlatformSDK')
+        return sdkdir
+
+    @property
+    def WindowsSDKExecutablePath(self):
+        """
+        Microsoft Windows SDK executable directory.
+
+        Return
+        ------
+        str
+            path
+        """
+        # Find WinSDK NetFx Tools registry dir name
+        if self.vs_ver <= 11.0:
+            netfxver = 35
+            arch = ''
+        else:
+            netfxver = 40
+            hidex86 = True if self.vs_ver <= 12.0 else False
+            arch = self.pi.current_dir(x64=True, hidex86=hidex86).replace('\\', '-')
+        fx = f'WinSDK-NetFx{netfxver}Tools{arch}'
+
+        # list all possibles registry paths
+        regpaths = []
+        if self.vs_ver >= 14.0:
+            for ver in self.NetFxSdkVersion:
+                regpaths += [os.path.join(self.ri.netfx_sdk, ver, fx)]
+
+        for ver in self.WindowsSdkVersion:
+            regpaths += [os.path.join(self.ri.windows_sdk, f'v{ver}A', fx)]
+
+        # Return installation folder from the more recent path
+        for path in regpaths:
+            execpath = self.ri.lookup(path, 'installationfolder')
+            if execpath:
+                return execpath
+
+        return None
+
+    @property
+    def FSharpInstallDir(self):
+        """
+        Microsoft Visual F# directory.
+
+        Return
+        ------
+        str
+            path
+        """
+        path = os.path.join(self.ri.visualstudio, rf'{self.vs_ver:0.1f}\Setup\F#')
+        return self.ri.lookup(path, 'productdir') or ''
+
+    @property
+    def UniversalCRTSdkDir(self):
+        """
+        Microsoft Universal CRT SDK directory.
+
+        Return
+        ------
+        str
+            path
+        """
+        # Set Kit Roots versions for specified MSVC++ version
+        vers = ('10', '81') if self.vs_ver >= 14.0 else ()
+
+        # Find path of the more recent Kit
+        for ver in vers:
+            sdkdir = self.ri.lookup(self.ri.windows_kits_roots, f'kitsroot{ver}')
+            if sdkdir:
+                return sdkdir or ''
+
+        return None
+
+    @property
+    def UniversalCRTSdkLastVersion(self):
+        """
+        Microsoft Universal C Runtime SDK last version.
+
+        Return
+        ------
+        str
+            version
+        """
+        return self._use_last_dir_name(os.path.join(self.UniversalCRTSdkDir, 'lib'))
+
+    @property
+    def NetFxSdkVersion(self):
+        """
+        Microsoft .NET Framework SDK versions.
+
+        Return
+        ------
+        tuple of str
+            versions
+        """
+        # Set FxSdk versions for specified VS version
+        return (
+            ('4.7.2', '4.7.1', '4.7', '4.6.2', '4.6.1', '4.6', '4.5.2', '4.5.1', '4.5')
+            if self.vs_ver >= 14.0
+            else ()
+        )
+
+    @property
+    def NetFxSdkDir(self):
+        """
+        Microsoft .NET Framework SDK directory.
+
+        Return
+        ------
+        str
+            path
+        """
+        sdkdir = ''
+        for ver in self.NetFxSdkVersion:
+            loc = os.path.join(self.ri.netfx_sdk, ver)
+            sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder')
+            if sdkdir:
+                break
+        return sdkdir
+
+    @property
+    def FrameworkDir32(self):
+        """
+        Microsoft .NET Framework 32bit directory.
+
+        Return
+        ------
+        str
+            path
+        """
+        # Default path
+        guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework')
+
+        # Try to get path from registry, if fail use default path
+        return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw
+
+    @property
+    def FrameworkDir64(self):
+        """
+        Microsoft .NET Framework 64bit directory.
+
+        Return
+        ------
+        str
+            path
+        """
+        # Default path
+        guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework64')
+
+        # Try to get path from registry, if fail use default path
+        return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw
+
+    @property
+    def FrameworkVersion32(self) -> tuple[str, ...]:
+        """
+        Microsoft .NET Framework 32bit versions.
+
+        Return
+        ------
+        tuple of str
+            versions
+        """
+        return self._find_dot_net_versions(32)
+
+    @property
+    def FrameworkVersion64(self) -> tuple[str, ...]:
+        """
+        Microsoft .NET Framework 64bit versions.
+
+        Return
+        ------
+        tuple of str
+            versions
+        """
+        return self._find_dot_net_versions(64)
+
+    def _find_dot_net_versions(self, bits) -> tuple[str, ...]:
+        """
+        Find Microsoft .NET Framework versions.
+
+        Parameters
+        ----------
+        bits: int
+            Platform number of bits: 32 or 64.
+
+        Return
+        ------
+        tuple of str
+            versions
+        """
+        # Find actual .NET version in registry
+        reg_ver = self.ri.lookup(self.ri.vc, f'frameworkver{bits}')
+        dot_net_dir = getattr(self, f'FrameworkDir{bits}')
+        ver = reg_ver or self._use_last_dir_name(dot_net_dir, 'v') or ''
+
+        # Set .NET versions for specified MSVC++ version
+        if self.vs_ver >= 12.0:
+            return ver, 'v4.0'
+        elif self.vs_ver >= 10.0:
+            return 'v4.0.30319' if ver.lower()[:2] != 'v4' else ver, 'v3.5'
+        elif self.vs_ver == 9.0:
+            return 'v3.5', 'v2.0.50727'
+        elif self.vs_ver == 8.0:
+            return 'v3.0', 'v2.0.50727'
+        return ()
+
+    @staticmethod
+    def _use_last_dir_name(path, prefix=''):
+        """
+        Return name of the last dir in path or '' if no dir found.
+
+        Parameters
+        ----------
+        path: str
+            Use dirs in this path
+        prefix: str
+            Use only dirs starting by this prefix
+
+        Return
+        ------
+        str
+            name
+        """
+        matching_dirs = (
+            dir_name
+            for dir_name in reversed(os.listdir(path))
+            if os.path.isdir(os.path.join(path, dir_name))
+            and dir_name.startswith(prefix)
+        )
+        return next(matching_dirs, None) or ''
+
+
+class _EnvironmentDict(TypedDict):
+    include: str
+    lib: str
+    libpath: str
+    path: str
+    py_vcruntime_redist: NotRequired[str | None]
+
+
+class EnvironmentInfo:
+    """
+    Return environment variables for specified Microsoft Visual C++ version
+    and platform : Lib, Include, Path and libpath.
+
+    This function is compatible with Microsoft Visual C++ 9.0 to 14.X.
+
+    Script created by analysing Microsoft environment configuration files like
+    "vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ...
+
+    Parameters
+    ----------
+    arch: str
+        Target architecture.
+    vc_ver: float
+        Required Microsoft Visual C++ version. If not set, autodetect the last
+        version.
+    vc_min_ver: float
+        Minimum Microsoft Visual C++ version.
+    """
+
+    # Variables and properties in this class use originals CamelCase variables
+    # names from Microsoft source files for more easy comparison.
+
+    def __init__(self, arch, vc_ver=None, vc_min_ver=0) -> None:
+        self.pi = PlatformInfo(arch)
+        self.ri = RegistryInfo(self.pi)
+        self.si = SystemInfo(self.ri, vc_ver)
+
+        if self.vc_ver < vc_min_ver:
+            err = 'No suitable Microsoft Visual C++ version found'
+            raise distutils.errors.DistutilsPlatformError(err)
+
+    @property
+    def vs_ver(self):
+        """
+        Microsoft Visual Studio.
+
+        Return
+        ------
+        float
+            version
+        """
+        return self.si.vs_ver
+
+    @property
+    def vc_ver(self):
+        """
+        Microsoft Visual C++ version.
+
+        Return
+        ------
+        float
+            version
+        """
+        return self.si.vc_ver
+
+    @property
+    def VSTools(self):
+        """
+        Microsoft Visual Studio Tools.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        paths = [r'Common7\IDE', r'Common7\Tools']
+
+        if self.vs_ver >= 14.0:
+            arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
+            paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow']
+            paths += [r'Team Tools\Performance Tools']
+            paths += [rf'Team Tools\Performance Tools{arch_subdir}']
+
+        return [os.path.join(self.si.VSInstallDir, path) for path in paths]
+
+    @property
+    def VCIncludes(self):
+        """
+        Microsoft Visual C++ & Microsoft Foundation Class Includes.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        return [
+            os.path.join(self.si.VCInstallDir, 'Include'),
+            os.path.join(self.si.VCInstallDir, r'ATLMFC\Include'),
+        ]
+
+    @property
+    def VCLibraries(self):
+        """
+        Microsoft Visual C++ & Microsoft Foundation Class Libraries.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        if self.vs_ver >= 15.0:
+            arch_subdir = self.pi.target_dir(x64=True)
+        else:
+            arch_subdir = self.pi.target_dir(hidex86=True)
+        paths = [f'Lib{arch_subdir}', rf'ATLMFC\Lib{arch_subdir}']
+
+        if self.vs_ver >= 14.0:
+            paths += [rf'Lib\store{arch_subdir}']
+
+        return [os.path.join(self.si.VCInstallDir, path) for path in paths]
+
+    @property
+    def VCStoreRefs(self):
+        """
+        Microsoft Visual C++ store references Libraries.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        if self.vs_ver < 14.0:
+            return []
+        return [os.path.join(self.si.VCInstallDir, r'Lib\store\references')]
+
+    @property
+    def VCTools(self):
+        """
+        Microsoft Visual C++ Tools.
+
+        Return
+        ------
+        list of str
+            paths
+
+        When host CPU is ARM, the tools should be found for ARM.
+
+        >>> getfixture('windows_only')
+        >>> mp = getfixture('monkeypatch')
+        >>> mp.setattr(PlatformInfo, 'current_cpu', 'arm64')
+        >>> ei = EnvironmentInfo(arch='irrelevant')
+        >>> paths = ei.VCTools
+        >>> any('HostARM64' in path for path in paths)
+        True
+        """
+        si = self.si
+        tools = [os.path.join(si.VCInstallDir, 'VCPackages')]
+
+        forcex86 = True if self.vs_ver <= 10.0 else False
+        arch_subdir = self.pi.cross_dir(forcex86)
+        if arch_subdir:
+            tools += [os.path.join(si.VCInstallDir, f'Bin{arch_subdir}')]
+
+        if self.vs_ver == 14.0:
+            path = f'Bin{self.pi.current_dir(hidex86=True)}'
+            tools += [os.path.join(si.VCInstallDir, path)]
+
+        elif self.vs_ver >= 15.0:
+            host_id = self.pi.current_cpu.replace('amd64', 'x64').upper()
+            host_dir = os.path.join('bin', f'Host{host_id}%s')
+            tools += [
+                os.path.join(si.VCInstallDir, host_dir % self.pi.target_dir(x64=True))
+            ]
+
+            if self.pi.current_cpu != self.pi.target_cpu:
+                tools += [
+                    os.path.join(
+                        si.VCInstallDir, host_dir % self.pi.current_dir(x64=True)
+                    )
+                ]
+
+        else:
+            tools += [os.path.join(si.VCInstallDir, 'Bin')]
+
+        return tools
+
+    @property
+    def OSLibraries(self):
+        """
+        Microsoft Windows SDK Libraries.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        if self.vs_ver <= 10.0:
+            arch_subdir = self.pi.target_dir(hidex86=True, x64=True)
+            return [os.path.join(self.si.WindowsSdkDir, f'Lib{arch_subdir}')]
+
+        else:
+            arch_subdir = self.pi.target_dir(x64=True)
+            lib = os.path.join(self.si.WindowsSdkDir, 'lib')
+            libver = self._sdk_subdir
+            return [os.path.join(lib, f'{libver}um{arch_subdir}')]
+
+    @property
+    def OSIncludes(self):
+        """
+        Microsoft Windows SDK Include.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        include = os.path.join(self.si.WindowsSdkDir, 'include')
+
+        if self.vs_ver <= 10.0:
+            return [include, os.path.join(include, 'gl')]
+
+        else:
+            if self.vs_ver >= 14.0:
+                sdkver = self._sdk_subdir
+            else:
+                sdkver = ''
+            return [
+                os.path.join(include, f'{sdkver}shared'),
+                os.path.join(include, f'{sdkver}um'),
+                os.path.join(include, f'{sdkver}winrt'),
+            ]
+
+    @property
+    def OSLibpath(self):
+        """
+        Microsoft Windows SDK Libraries Paths.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        ref = os.path.join(self.si.WindowsSdkDir, 'References')
+        libpath = []
+
+        if self.vs_ver <= 9.0:
+            libpath += self.OSLibraries
+
+        if self.vs_ver >= 11.0:
+            libpath += [os.path.join(ref, r'CommonConfiguration\Neutral')]
+
+        if self.vs_ver >= 14.0:
+            libpath += [
+                ref,
+                os.path.join(self.si.WindowsSdkDir, 'UnionMetadata'),
+                os.path.join(ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'),
+                os.path.join(ref, 'Windows.Foundation.FoundationContract', '1.0.0.0'),
+                os.path.join(
+                    ref, 'Windows.Networking.Connectivity.WwanContract', '1.0.0.0'
+                ),
+                os.path.join(
+                    self.si.WindowsSdkDir,
+                    'ExtensionSDKs',
+                    'Microsoft.VCLibs',
+                    f'{self.vs_ver:0.1f}',
+                    'References',
+                    'CommonConfiguration',
+                    'neutral',
+                ),
+            ]
+        return libpath
+
+    @property
+    def SdkTools(self):
+        """
+        Microsoft Windows SDK Tools.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        return list(self._sdk_tools())
+
+    def _sdk_tools(self):
+        """
+        Microsoft Windows SDK Tools paths generator.
+
+        Return
+        ------
+        generator of str
+            paths
+        """
+        if self.vs_ver < 15.0:
+            bin_dir = 'Bin' if self.vs_ver <= 11.0 else r'Bin\x86'
+            yield os.path.join(self.si.WindowsSdkDir, bin_dir)
+
+        if not self.pi.current_is_x86():
+            arch_subdir = self.pi.current_dir(x64=True)
+            path = f'Bin{arch_subdir}'
+            yield os.path.join(self.si.WindowsSdkDir, path)
+
+        if self.vs_ver in (10.0, 11.0):
+            if self.pi.target_is_x86():
+                arch_subdir = ''
+            else:
+                arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
+            path = rf'Bin\NETFX 4.0 Tools{arch_subdir}'
+            yield os.path.join(self.si.WindowsSdkDir, path)
+
+        elif self.vs_ver >= 15.0:
+            path = os.path.join(self.si.WindowsSdkDir, 'Bin')
+            arch_subdir = self.pi.current_dir(x64=True)
+            sdkver = self.si.WindowsSdkLastVersion
+            yield os.path.join(path, f'{sdkver}{arch_subdir}')
+
+        if self.si.WindowsSDKExecutablePath:
+            yield self.si.WindowsSDKExecutablePath
+
+    @property
+    def _sdk_subdir(self):
+        """
+        Microsoft Windows SDK version subdir.
+
+        Return
+        ------
+        str
+            subdir
+        """
+        ucrtver = self.si.WindowsSdkLastVersion
+        return (f'{ucrtver}\\') if ucrtver else ''
+
+    @property
+    def SdkSetup(self):
+        """
+        Microsoft Windows SDK Setup.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        if self.vs_ver > 9.0:
+            return []
+
+        return [os.path.join(self.si.WindowsSdkDir, 'Setup')]
+
+    @property
+    def FxTools(self):
+        """
+        Microsoft .NET Framework Tools.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        pi = self.pi
+        si = self.si
+
+        if self.vs_ver <= 10.0:
+            include32 = True
+            include64 = not pi.target_is_x86() and not pi.current_is_x86()
+        else:
+            include32 = pi.target_is_x86() or pi.current_is_x86()
+            include64 = pi.current_cpu == 'amd64' or pi.target_cpu == 'amd64'
+
+        tools = []
+        if include32:
+            tools += [
+                os.path.join(si.FrameworkDir32, ver) for ver in si.FrameworkVersion32
+            ]
+        if include64:
+            tools += [
+                os.path.join(si.FrameworkDir64, ver) for ver in si.FrameworkVersion64
+            ]
+        return tools
+
+    @property
+    def NetFxSDKLibraries(self):
+        """
+        Microsoft .Net Framework SDK Libraries.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        if self.vs_ver < 14.0 or not self.si.NetFxSdkDir:
+            return []
+
+        arch_subdir = self.pi.target_dir(x64=True)
+        return [os.path.join(self.si.NetFxSdkDir, rf'lib\um{arch_subdir}')]
+
+    @property
+    def NetFxSDKIncludes(self):
+        """
+        Microsoft .Net Framework SDK Includes.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        if self.vs_ver < 14.0 or not self.si.NetFxSdkDir:
+            return []
+
+        return [os.path.join(self.si.NetFxSdkDir, r'include\um')]
+
+    @property
+    def VsTDb(self):
+        """
+        Microsoft Visual Studio Team System Database.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        return [os.path.join(self.si.VSInstallDir, r'VSTSDB\Deploy')]
+
+    @property
+    def MSBuild(self):
+        """
+        Microsoft Build Engine.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        if self.vs_ver < 12.0:
+            return []
+        elif self.vs_ver < 15.0:
+            base_path = self.si.ProgramFilesx86
+            arch_subdir = self.pi.current_dir(hidex86=True)
+        else:
+            base_path = self.si.VSInstallDir
+            arch_subdir = ''
+
+        path = rf'MSBuild\{self.vs_ver:0.1f}\bin{arch_subdir}'
+        build = [os.path.join(base_path, path)]
+
+        if self.vs_ver >= 15.0:
+            # Add Roslyn C# & Visual Basic Compiler
+            build += [os.path.join(base_path, path, 'Roslyn')]
+
+        return build
+
+    @property
+    def HTMLHelpWorkshop(self):
+        """
+        Microsoft HTML Help Workshop.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        if self.vs_ver < 11.0:
+            return []
+
+        return [os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop')]
+
+    @property
+    def UCRTLibraries(self):
+        """
+        Microsoft Universal C Runtime SDK Libraries.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        if self.vs_ver < 14.0:
+            return []
+
+        arch_subdir = self.pi.target_dir(x64=True)
+        lib = os.path.join(self.si.UniversalCRTSdkDir, 'lib')
+        ucrtver = self._ucrt_subdir
+        return [os.path.join(lib, f'{ucrtver}ucrt{arch_subdir}')]
+
+    @property
+    def UCRTIncludes(self):
+        """
+        Microsoft Universal C Runtime SDK Include.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        if self.vs_ver < 14.0:
+            return []
+
+        include = os.path.join(self.si.UniversalCRTSdkDir, 'include')
+        return [os.path.join(include, f'{self._ucrt_subdir}ucrt')]
+
+    @property
+    def _ucrt_subdir(self):
+        """
+        Microsoft Universal C Runtime SDK version subdir.
+
+        Return
+        ------
+        str
+            subdir
+        """
+        ucrtver = self.si.UniversalCRTSdkLastVersion
+        return (f'{ucrtver}\\') if ucrtver else ''
+
+    @property
+    def FSharp(self):
+        """
+        Microsoft Visual F#.
+
+        Return
+        ------
+        list of str
+            paths
+        """
+        if 11.0 > self.vs_ver > 12.0:
+            return []
+
+        return [self.si.FSharpInstallDir]
+
+    @property
+    def VCRuntimeRedist(self) -> str | None:
+        """
+        Microsoft Visual C++ runtime redistributable dll.
+
+        Returns the first suitable path found or None.
+        """
+        vcruntime = f'vcruntime{self.vc_ver}0.dll'
+        arch_subdir = self.pi.target_dir(x64=True).strip('\\')
+
+        # Installation prefixes candidates
+        prefixes = []
+        tools_path = self.si.VCInstallDir
+        redist_path = os.path.dirname(tools_path.replace(r'\Tools', r'\Redist'))
+        if os.path.isdir(redist_path):
+            # Redist version may not be exactly the same as tools
+            redist_path = os.path.join(redist_path, os.listdir(redist_path)[-1])
+            prefixes += [redist_path, os.path.join(redist_path, 'onecore')]
+
+        prefixes += [os.path.join(tools_path, 'redist')]  # VS14 legacy path
+
+        # CRT directory
+        crt_dirs = (
+            f'Microsoft.VC{self.vc_ver * 10}.CRT',
+            # Sometime store in directory with VS version instead of VC
+            f'Microsoft.VC{int(self.vs_ver) * 10}.CRT',
+        )
+
+        # vcruntime path
+        candidate_paths = (
+            os.path.join(prefix, arch_subdir, crt_dir, vcruntime)
+            for (prefix, crt_dir) in itertools.product(prefixes, crt_dirs)
+        )
+        return next(filter(os.path.isfile, candidate_paths), None)  # type: ignore[arg-type] #python/mypy#12682
+
+    def return_env(self, exists: bool = True) -> _EnvironmentDict:
+        """
+        Return environment dict.
+
+        Parameters
+        ----------
+        exists: bool
+            It True, only return existing paths.
+
+        Return
+        ------
+        dict
+            environment
+        """
+        env = _EnvironmentDict(
+            include=self._build_paths(
+                'include',
+                [
+                    self.VCIncludes,
+                    self.OSIncludes,
+                    self.UCRTIncludes,
+                    self.NetFxSDKIncludes,
+                ],
+                exists,
+            ),
+            lib=self._build_paths(
+                'lib',
+                [
+                    self.VCLibraries,
+                    self.OSLibraries,
+                    self.FxTools,
+                    self.UCRTLibraries,
+                    self.NetFxSDKLibraries,
+                ],
+                exists,
+            ),
+            libpath=self._build_paths(
+                'libpath',
+                [self.VCLibraries, self.FxTools, self.VCStoreRefs, self.OSLibpath],
+                exists,
+            ),
+            path=self._build_paths(
+                'path',
+                [
+                    self.VCTools,
+                    self.VSTools,
+                    self.VsTDb,
+                    self.SdkTools,
+                    self.SdkSetup,
+                    self.FxTools,
+                    self.MSBuild,
+                    self.HTMLHelpWorkshop,
+                    self.FSharp,
+                ],
+                exists,
+            ),
+        )
+        if self.vs_ver >= 14 and self.VCRuntimeRedist:
+            env['py_vcruntime_redist'] = self.VCRuntimeRedist
+        return env
+
+    def _build_paths(self, name, spec_path_lists, exists):
+        """
+        Given an environment variable name and specified paths,
+        return a pathsep-separated string of paths containing
+        unique, extant, directories from those paths and from
+        the environment variable. Raise an error if no paths
+        are resolved.
+
+        Parameters
+        ----------
+        name: str
+            Environment variable name
+        spec_path_lists: list of str
+            Paths
+        exists: bool
+            It True, only return existing paths.
+
+        Return
+        ------
+        str
+            Pathsep-separated paths
+        """
+        # flatten spec_path_lists
+        spec_paths = itertools.chain.from_iterable(spec_path_lists)
+        env_paths = environ.get(name, '').split(os.pathsep)
+        paths = itertools.chain(spec_paths, env_paths)
+        extant_paths = list(filter(os.path.isdir, paths)) if exists else paths
+        if not extant_paths:
+            msg = f"{name.upper()} environment variable is empty"
+            raise distutils.errors.DistutilsPlatformError(msg)
+        unique_paths = unique_everseen(extant_paths)
+        return os.pathsep.join(unique_paths)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/namespaces.py b/.venv/lib/python3.12/site-packages/setuptools/namespaces.py
new file mode 100644
index 0000000000000000000000000000000000000000..85ea2ebd654c480b8c19d1715b3772c4bcfd812e
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/namespaces.py
@@ -0,0 +1,106 @@
+import itertools
+import os
+
+from .compat import py312
+
+from distutils import log
+
+flatten = itertools.chain.from_iterable
+
+
+class Installer:
+    nspkg_ext = '-nspkg.pth'
+
+    def install_namespaces(self) -> None:
+        nsp = self._get_all_ns_packages()
+        if not nsp:
+            return
+        filename = self._get_nspkg_file()
+        self.outputs.append(filename)
+        log.info("Installing %s", filename)
+        lines = map(self._gen_nspkg_line, nsp)
+
+        if self.dry_run:
+            # always generate the lines, even in dry run
+            list(lines)
+            return
+
+        with open(filename, 'wt', encoding=py312.PTH_ENCODING) as f:
+            # Python<3.13 requires encoding="locale" instead of "utf-8"
+            # See: python/cpython#77102
+            f.writelines(lines)
+
+    def uninstall_namespaces(self) -> None:
+        filename = self._get_nspkg_file()
+        if not os.path.exists(filename):
+            return
+        log.info("Removing %s", filename)
+        os.remove(filename)
+
+    def _get_nspkg_file(self):
+        filename, _ = os.path.splitext(self._get_target())
+        return filename + self.nspkg_ext
+
+    def _get_target(self):
+        return self.target
+
+    _nspkg_tmpl = (
+        "import sys, types, os",
+        "p = os.path.join(%(root)s, *%(pth)r)",
+        "importlib = __import__('importlib.util')",
+        "__import__('importlib.machinery')",
+        (
+            "m = "
+            "sys.modules.setdefault(%(pkg)r, "
+            "importlib.util.module_from_spec("
+            "importlib.machinery.PathFinder.find_spec(%(pkg)r, "
+            "[os.path.dirname(p)])))"
+        ),
+        ("m = m or sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))"),
+        "mp = (m or []) and m.__dict__.setdefault('__path__',[])",
+        "(p not in mp) and mp.append(p)",
+    )
+    "lines for the namespace installer"
+
+    _nspkg_tmpl_multi = ('m and setattr(sys.modules[%(parent)r], %(child)r, m)',)
+    "additional line(s) when a parent package is indicated"
+
+    def _get_root(self):
+        return "sys._getframe(1).f_locals['sitedir']"
+
+    def _gen_nspkg_line(self, pkg):
+        pth = tuple(pkg.split('.'))
+        root = self._get_root()
+        tmpl_lines = self._nspkg_tmpl
+        parent, sep, child = pkg.rpartition('.')
+        if parent:
+            tmpl_lines += self._nspkg_tmpl_multi
+        return ';'.join(tmpl_lines) % locals() + '\n'
+
+    def _get_all_ns_packages(self):
+        """Return sorted list of all package namespaces"""
+        pkgs = self.distribution.namespace_packages or []
+        return sorted(set(flatten(map(self._pkg_names, pkgs))))
+
+    @staticmethod
+    def _pkg_names(pkg):
+        """
+        Given a namespace package, yield the components of that
+        package.
+
+        >>> names = Installer._pkg_names('a.b.c')
+        >>> set(names) == set(['a', 'a.b', 'a.b.c'])
+        True
+        """
+        parts = pkg.split('.')
+        while parts:
+            yield '.'.join(parts)
+            parts.pop()
+
+
+class DevelopInstaller(Installer):
+    def _get_root(self):
+        return repr(str(self.egg_path))
+
+    def _get_target(self):
+        return self.egg_link
diff --git a/.venv/lib/python3.12/site-packages/setuptools/script (dev).tmpl b/.venv/lib/python3.12/site-packages/setuptools/script (dev).tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..39a24b04888e79df51e2237577b303a2f901be63
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/script (dev).tmpl	
@@ -0,0 +1,6 @@
+# EASY-INSTALL-DEV-SCRIPT: %(spec)r,%(script_name)r
+__requires__ = %(spec)r
+__import__('pkg_resources').require(%(spec)r)
+__file__ = %(dev_path)r
+with open(__file__) as f:
+    exec(compile(f.read(), __file__, 'exec'))
diff --git a/.venv/lib/python3.12/site-packages/setuptools/script.tmpl b/.venv/lib/python3.12/site-packages/setuptools/script.tmpl
new file mode 100644
index 0000000000000000000000000000000000000000..ff5efbcab3b58063dd84787181c26a95fb663d94
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/script.tmpl
@@ -0,0 +1,3 @@
+# EASY-INSTALL-SCRIPT: %(spec)r,%(script_name)r
+__requires__ = %(spec)r
+__import__('pkg_resources').run_script(%(spec)r, %(script_name)r)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/unicode_utils.py b/.venv/lib/python3.12/site-packages/setuptools/unicode_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..f502f5b089619eafd28e2c7a61967e34e16920e5
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/unicode_utils.py
@@ -0,0 +1,102 @@
+import sys
+import unicodedata
+from configparser import RawConfigParser
+
+from .compat import py39
+from .warnings import SetuptoolsDeprecationWarning
+
+
+# HFS Plus uses decomposed UTF-8
+def decompose(path):
+    if isinstance(path, str):
+        return unicodedata.normalize('NFD', path)
+    try:
+        path = path.decode('utf-8')
+        path = unicodedata.normalize('NFD', path)
+        path = path.encode('utf-8')
+    except UnicodeError:
+        pass  # Not UTF-8
+    return path
+
+
+def filesys_decode(path):
+    """
+    Ensure that the given path is decoded,
+    ``None`` when no expected encoding works
+    """
+
+    if isinstance(path, str):
+        return path
+
+    fs_enc = sys.getfilesystemencoding() or 'utf-8'
+    candidates = fs_enc, 'utf-8'
+
+    for enc in candidates:
+        try:
+            return path.decode(enc)
+        except UnicodeDecodeError:
+            continue
+
+    return None
+
+
+def try_encode(string, enc):
+    "turn unicode encoding into a functional routine"
+    try:
+        return string.encode(enc)
+    except UnicodeEncodeError:
+        return None
+
+
+def _read_utf8_with_fallback(file: str, fallback_encoding=py39.LOCALE_ENCODING) -> str:
+    """
+    First try to read the file with UTF-8, if there is an error fallback to a
+    different encoding ("locale" by default). Returns the content of the file.
+    Also useful when reading files that might have been produced by an older version of
+    setuptools.
+    """
+    try:
+        with open(file, "r", encoding="utf-8") as f:
+            return f.read()
+    except UnicodeDecodeError:  # pragma: no cover
+        _Utf8EncodingNeeded.emit(file=file, fallback_encoding=fallback_encoding)
+        with open(file, "r", encoding=fallback_encoding) as f:
+            return f.read()
+
+
+def _cfg_read_utf8_with_fallback(
+    cfg: RawConfigParser, file: str, fallback_encoding=py39.LOCALE_ENCODING
+) -> None:
+    """Same idea as :func:`_read_utf8_with_fallback`, but for the
+    :meth:`RawConfigParser.read` method.
+
+    This method may call ``cfg.clear()``.
+    """
+    try:
+        cfg.read(file, encoding="utf-8")
+    except UnicodeDecodeError:  # pragma: no cover
+        _Utf8EncodingNeeded.emit(file=file, fallback_encoding=fallback_encoding)
+        cfg.clear()
+        cfg.read(file, encoding=fallback_encoding)
+
+
+class _Utf8EncodingNeeded(SetuptoolsDeprecationWarning):
+    _SUMMARY = """
+    `encoding="utf-8"` fails with {file!r}, trying `encoding={fallback_encoding!r}`.
+    """
+
+    _DETAILS = """
+    Fallback behavior for UTF-8 is considered **deprecated** and future versions of
+    `setuptools` may not implement it.
+
+    Please encode {file!r} with "utf-8" to ensure future builds will succeed.
+
+    If this file was produced by `setuptools` itself, cleaning up the cached files
+    and re-building/re-installing the package with a newer version of `setuptools`
+    (e.g. by updating `build-system.requires` in its `pyproject.toml`)
+    might solve the problem.
+    """
+    # TODO: Add a deadline?
+    #       Will we be able to remove this?
+    #       The question comes to mind mainly because of sdists that have been produced
+    #       by old versions of setuptools and published to PyPI...
diff --git a/.venv/lib/python3.12/site-packages/setuptools/version.py b/.venv/lib/python3.12/site-packages/setuptools/version.py
new file mode 100644
index 0000000000000000000000000000000000000000..ec253c414474677d3a5977511cfe901bfb786740
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/version.py
@@ -0,0 +1,6 @@
+from ._importlib import metadata
+
+try:
+    __version__ = metadata.version('setuptools') or '0.dev0+unknown'
+except Exception:
+    __version__ = '0.dev0+unknown'
diff --git a/.venv/lib/python3.12/site-packages/setuptools/warnings.py b/.venv/lib/python3.12/site-packages/setuptools/warnings.py
new file mode 100644
index 0000000000000000000000000000000000000000..96467787c237846bfbacf2d44eb833be0a88b633
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/warnings.py
@@ -0,0 +1,110 @@
+"""Provide basic warnings used by setuptools modules.
+
+Using custom classes (other than ``UserWarning``) allow users to set
+``PYTHONWARNINGS`` filters to run tests and prepare for upcoming changes in
+setuptools.
+"""
+
+from __future__ import annotations
+
+import os
+import warnings
+from datetime import date
+from inspect import cleandoc
+from textwrap import indent
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from typing_extensions import TypeAlias
+
+_DueDate: TypeAlias = tuple[int, int, int]  # time tuple
+_INDENT = 8 * " "
+_TEMPLATE = f"""{80 * '*'}\n{{details}}\n{80 * '*'}"""
+
+
+class SetuptoolsWarning(UserWarning):
+    """Base class in ``setuptools`` warning hierarchy."""
+
+    @classmethod
+    def emit(
+        cls,
+        summary: str | None = None,
+        details: str | None = None,
+        due_date: _DueDate | None = None,
+        see_docs: str | None = None,
+        see_url: str | None = None,
+        stacklevel: int = 2,
+        **kwargs,
+    ) -> None:
+        """Private: reserved for ``setuptools`` internal use only"""
+        # Default values:
+        summary_ = summary or getattr(cls, "_SUMMARY", None) or ""
+        details_ = details or getattr(cls, "_DETAILS", None) or ""
+        due_date = due_date or getattr(cls, "_DUE_DATE", None)
+        docs_ref = see_docs or getattr(cls, "_SEE_DOCS", None)
+        docs_url = docs_ref and f"https://setuptools.pypa.io/en/latest/{docs_ref}"
+        see_url = see_url or getattr(cls, "_SEE_URL", None)
+        due = date(*due_date) if due_date else None
+
+        text = cls._format(summary_, details_, due, see_url or docs_url, kwargs)
+        if due and due < date.today() and _should_enforce():
+            raise cls(text)
+        warnings.warn(text, cls, stacklevel=stacklevel + 1)
+
+    @classmethod
+    def _format(
+        cls,
+        summary: str,
+        details: str,
+        due_date: date | None = None,
+        see_url: str | None = None,
+        format_args: dict | None = None,
+    ) -> str:
+        """Private: reserved for ``setuptools`` internal use only"""
+        today = date.today()
+        summary = cleandoc(summary).format_map(format_args or {})
+        possible_parts = [
+            cleandoc(details).format_map(format_args or {}),
+            (
+                f"\nBy {due_date:%Y-%b-%d}, you need to update your project and remove "
+                "deprecated calls\nor your builds will no longer be supported."
+                if due_date and due_date > today
+                else None
+            ),
+            (
+                "\nThis deprecation is overdue, please update your project and remove "
+                "deprecated\ncalls to avoid build errors in the future."
+                if due_date and due_date < today
+                else None
+            ),
+            (f"\nSee {see_url} for details." if see_url else None),
+        ]
+        parts = [x for x in possible_parts if x]
+        if parts:
+            body = indent(_TEMPLATE.format(details="\n".join(parts)), _INDENT)
+            return "\n".join([summary, "!!\n", body, "\n!!"])
+        return summary
+
+
+class InformationOnly(SetuptoolsWarning):
+    """Currently there is no clear way of displaying messages to the users
+    that use the setuptools backend directly via ``pip``.
+    The only thing that might work is a warning, although it is not the
+    most appropriate tool for the job...
+
+    See pypa/packaging-problems#558.
+    """
+
+
+class SetuptoolsDeprecationWarning(SetuptoolsWarning):
+    """
+    Base class for warning deprecations in ``setuptools``
+
+    This class is not derived from ``DeprecationWarning``, and as such is
+    visible by default.
+    """
+
+
+def _should_enforce():
+    enforce = os.getenv("SETUPTOOLS_ENFORCE_DEPRECATION", "false").lower()
+    return enforce in ("true", "on", "ok", "1")
diff --git a/.venv/lib/python3.12/site-packages/setuptools/wheel.py b/.venv/lib/python3.12/site-packages/setuptools/wheel.py
new file mode 100644
index 0000000000000000000000000000000000000000..124e01ad2f95c2da29039c2c907a709e40d515d7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/wheel.py
@@ -0,0 +1,261 @@
+"""Wheels support."""
+
+import contextlib
+import email
+import functools
+import itertools
+import os
+import posixpath
+import re
+import zipfile
+
+from packaging.requirements import Requirement
+from packaging.tags import sys_tags
+from packaging.utils import canonicalize_name
+from packaging.version import Version as parse_version
+
+import setuptools
+from setuptools.archive_util import _unpack_zipfile_obj
+from setuptools.command.egg_info import _egg_basename, write_requirements
+
+from ._discovery import extras_from_deps
+from ._importlib import metadata
+from .unicode_utils import _read_utf8_with_fallback
+
+from distutils.util import get_platform
+
+WHEEL_NAME = re.compile(
+    r"""^(?P.+?)-(?P\d.*?)
+    ((-(?P\d.*?))?-(?P.+?)-(?P.+?)-(?P.+?)
+    )\.whl$""",
+    re.VERBOSE,
+).match
+
+NAMESPACE_PACKAGE_INIT = "__import__('pkg_resources').declare_namespace(__name__)\n"
+
+
+@functools.cache
+def _get_supported_tags():
+    # We calculate the supported tags only once, otherwise calling
+    # this method on thousands of wheels takes seconds instead of
+    # milliseconds.
+    return {(t.interpreter, t.abi, t.platform) for t in sys_tags()}
+
+
+def unpack(src_dir, dst_dir) -> None:
+    """Move everything under `src_dir` to `dst_dir`, and delete the former."""
+    for dirpath, dirnames, filenames in os.walk(src_dir):
+        subdir = os.path.relpath(dirpath, src_dir)
+        for f in filenames:
+            src = os.path.join(dirpath, f)
+            dst = os.path.join(dst_dir, subdir, f)
+            os.renames(src, dst)
+        for n, d in reversed(list(enumerate(dirnames))):
+            src = os.path.join(dirpath, d)
+            dst = os.path.join(dst_dir, subdir, d)
+            if not os.path.exists(dst):
+                # Directory does not exist in destination,
+                # rename it and prune it from os.walk list.
+                os.renames(src, dst)
+                del dirnames[n]
+    # Cleanup.
+    for dirpath, dirnames, filenames in os.walk(src_dir, topdown=True):
+        assert not filenames
+        os.rmdir(dirpath)
+
+
+@contextlib.contextmanager
+def disable_info_traces():
+    """
+    Temporarily disable info traces.
+    """
+    from distutils import log
+
+    saved = log.set_threshold(log.WARN)
+    try:
+        yield
+    finally:
+        log.set_threshold(saved)
+
+
+class Wheel:
+    def __init__(self, filename) -> None:
+        match = WHEEL_NAME(os.path.basename(filename))
+        if match is None:
+            raise ValueError(f'invalid wheel name: {filename!r}')
+        self.filename = filename
+        for k, v in match.groupdict().items():
+            setattr(self, k, v)
+
+    def tags(self):
+        """List tags (py_version, abi, platform) supported by this wheel."""
+        return itertools.product(
+            self.py_version.split('.'),
+            self.abi.split('.'),
+            self.platform.split('.'),
+        )
+
+    def is_compatible(self):
+        """Is the wheel compatible with the current platform?"""
+        return next((True for t in self.tags() if t in _get_supported_tags()), False)
+
+    def egg_name(self):
+        return (
+            _egg_basename(
+                self.project_name,
+                self.version,
+                platform=(None if self.platform == 'any' else get_platform()),
+            )
+            + ".egg"
+        )
+
+    def get_dist_info(self, zf):
+        # find the correct name of the .dist-info dir in the wheel file
+        for member in zf.namelist():
+            dirname = posixpath.dirname(member)
+            if dirname.endswith('.dist-info') and canonicalize_name(dirname).startswith(
+                canonicalize_name(self.project_name)
+            ):
+                return dirname
+        raise ValueError("unsupported wheel format. .dist-info not found")
+
+    def install_as_egg(self, destination_eggdir) -> None:
+        """Install wheel as an egg directory."""
+        with zipfile.ZipFile(self.filename) as zf:
+            self._install_as_egg(destination_eggdir, zf)
+
+    def _install_as_egg(self, destination_eggdir, zf):
+        dist_basename = f'{self.project_name}-{self.version}'
+        dist_info = self.get_dist_info(zf)
+        dist_data = f'{dist_basename}.data'
+        egg_info = os.path.join(destination_eggdir, 'EGG-INFO')
+
+        self._convert_metadata(zf, destination_eggdir, dist_info, egg_info)
+        self._move_data_entries(destination_eggdir, dist_data)
+        self._fix_namespace_packages(egg_info, destination_eggdir)
+
+    @staticmethod
+    def _convert_metadata(zf, destination_eggdir, dist_info, egg_info):
+        def get_metadata(name):
+            with zf.open(posixpath.join(dist_info, name)) as fp:
+                value = fp.read().decode('utf-8')
+                return email.parser.Parser().parsestr(value)
+
+        wheel_metadata = get_metadata('WHEEL')
+        # Check wheel format version is supported.
+        wheel_version = parse_version(wheel_metadata.get('Wheel-Version'))
+        wheel_v1 = parse_version('1.0') <= wheel_version < parse_version('2.0dev0')
+        if not wheel_v1:
+            raise ValueError(f'unsupported wheel format version: {wheel_version}')
+        # Extract to target directory.
+        _unpack_zipfile_obj(zf, destination_eggdir)
+        dist_info = os.path.join(destination_eggdir, dist_info)
+        install_requires, extras_require = Wheel._convert_requires(
+            destination_eggdir, dist_info
+        )
+        os.rename(dist_info, egg_info)
+        os.rename(
+            os.path.join(egg_info, 'METADATA'),
+            os.path.join(egg_info, 'PKG-INFO'),
+        )
+        setup_dist = setuptools.Distribution(
+            attrs=dict(
+                install_requires=install_requires,
+                extras_require=extras_require,
+            ),
+        )
+        with disable_info_traces():
+            write_requirements(
+                setup_dist.get_command_obj('egg_info'),
+                None,
+                os.path.join(egg_info, 'requires.txt'),
+            )
+
+    @staticmethod
+    def _convert_requires(destination_eggdir, dist_info):
+        md = metadata.Distribution.at(dist_info).metadata
+        deps = md.get_all('Requires-Dist') or []
+        reqs = list(map(Requirement, deps))
+
+        extras = extras_from_deps(deps)
+
+        # Note: Evaluate and strip markers now,
+        # as it's difficult to convert back from the syntax:
+        # foobar; "linux" in sys_platform and extra == 'test'
+        def raw_req(req):
+            req = Requirement(str(req))
+            req.marker = None
+            return str(req)
+
+        def eval(req, **env):
+            return not req.marker or req.marker.evaluate(env)
+
+        def for_extra(req):
+            try:
+                markers = req.marker._markers
+            except AttributeError:
+                markers = ()
+            return set(
+                marker[2].value
+                for marker in markers
+                if isinstance(marker, tuple) and marker[0].value == 'extra'
+            )
+
+        install_requires = list(
+            map(raw_req, filter(eval, itertools.filterfalse(for_extra, reqs)))
+        )
+        extras_require = {
+            extra: list(
+                map(
+                    raw_req,
+                    (req for req in reqs if for_extra(req) and eval(req, extra=extra)),
+                )
+            )
+            for extra in extras
+        }
+        return install_requires, extras_require
+
+    @staticmethod
+    def _move_data_entries(destination_eggdir, dist_data):
+        """Move data entries to their correct location."""
+        dist_data = os.path.join(destination_eggdir, dist_data)
+        dist_data_scripts = os.path.join(dist_data, 'scripts')
+        if os.path.exists(dist_data_scripts):
+            egg_info_scripts = os.path.join(destination_eggdir, 'EGG-INFO', 'scripts')
+            os.mkdir(egg_info_scripts)
+            for entry in os.listdir(dist_data_scripts):
+                # Remove bytecode, as it's not properly handled
+                # during easy_install scripts install phase.
+                if entry.endswith('.pyc'):
+                    os.unlink(os.path.join(dist_data_scripts, entry))
+                else:
+                    os.rename(
+                        os.path.join(dist_data_scripts, entry),
+                        os.path.join(egg_info_scripts, entry),
+                    )
+            os.rmdir(dist_data_scripts)
+        for subdir in filter(
+            os.path.exists,
+            (
+                os.path.join(dist_data, d)
+                for d in ('data', 'headers', 'purelib', 'platlib')
+            ),
+        ):
+            unpack(subdir, destination_eggdir)
+        if os.path.exists(dist_data):
+            os.rmdir(dist_data)
+
+    @staticmethod
+    def _fix_namespace_packages(egg_info, destination_eggdir):
+        namespace_packages = os.path.join(egg_info, 'namespace_packages.txt')
+        if os.path.exists(namespace_packages):
+            namespace_packages = _read_utf8_with_fallback(namespace_packages).split()
+
+            for mod in namespace_packages:
+                mod_dir = os.path.join(destination_eggdir, *mod.split('.'))
+                mod_init = os.path.join(mod_dir, '__init__.py')
+                if not os.path.exists(mod_dir):
+                    os.mkdir(mod_dir)
+                if not os.path.exists(mod_init):
+                    with open(mod_init, 'w', encoding="utf-8") as fp:
+                        fp.write(NAMESPACE_PACKAGE_INIT)
diff --git a/.venv/lib/python3.12/site-packages/setuptools/windows_support.py b/.venv/lib/python3.12/site-packages/setuptools/windows_support.py
new file mode 100644
index 0000000000000000000000000000000000000000..7a2b53a291409c66851961a559eb4d69be0f4acc
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/setuptools/windows_support.py
@@ -0,0 +1,30 @@
+import platform
+
+
+def windows_only(func):
+    if platform.system() != 'Windows':
+        return lambda *args, **kwargs: None
+    return func
+
+
+@windows_only
+def hide_file(path: str) -> None:
+    """
+    Set the hidden attribute on a file or directory.
+
+    From https://stackoverflow.com/questions/19622133/
+
+    `path` must be text.
+    """
+    import ctypes
+    import ctypes.wintypes
+
+    SetFileAttributes = ctypes.windll.kernel32.SetFileAttributesW
+    SetFileAttributes.argtypes = ctypes.wintypes.LPWSTR, ctypes.wintypes.DWORD
+    SetFileAttributes.restype = ctypes.wintypes.BOOL
+
+    FILE_ATTRIBUTE_HIDDEN = 0x02
+
+    ret = SetFileAttributes(path, FILE_ATTRIBUTE_HIDDEN)
+    if not ret:
+        raise ctypes.WinError()
diff --git a/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/INSTALLER
new file mode 100644
index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/INSTALLER
@@ -0,0 +1 @@
+uv
\ No newline at end of file
diff --git a/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/LICENSE b/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..710010f1fe0311afd9823e319ff958827a419964
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/LICENSE
@@ -0,0 +1,30 @@
+Copyright (C) 2010, 2011 Sebastian Thiel and contributors
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without 
+modification, are permitted provided that the following conditions 
+are met:
+
+* Redistributions of source code must retain the above copyright 
+notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright 
+notice, this list of conditions and the following disclaimer in the 
+documentation and/or other materials provided with the distribution.
+
+* Neither the name of the async project nor the names of 
+its contributors may be used to endorse or promote products derived 
+from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/METADATA b/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/METADATA
new file mode 100644
index 0000000000000000000000000000000000000000..ce0537bde0f6d4cfee9549b001156bd5cabc107a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/METADATA
@@ -0,0 +1,113 @@
+Metadata-Version: 2.1
+Name: smmap
+Version: 5.0.2
+Summary: A pure Python implementation of a sliding window memory map manager
+Home-page: https://github.com/gitpython-developers/smmap
+Author: Sebastian Thiel
+Author-email: byronimo@gmail.com
+License: BSD-3-Clause
+Platform: any
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Console
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Operating System :: POSIX
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3 :: Only
+Requires-Python: >=3.7
+Description-Content-Type: text/markdown
+License-File: LICENSE
+
+## Motivation
+
+When reading from many possibly large files in a fashion similar to random access, it is usually the fastest and most efficient to use memory maps.
+
+Although memory maps have many advantages, they represent a very limited system resource as every map uses one file descriptor, whose amount is limited per process. On 32 bit systems, the amount of memory you can have mapped at a time is naturally limited to theoretical 4GB of memory, which may not be enough for some applications.
+
+
+## Limitations
+
+* **System resources (file-handles) are likely to be leaked!** This is due to the library authors reliance on a deterministic `__del__()` destructor.
+* The memory access is read-only by design.
+
+
+## Overview
+
+![Python package](https://github.com/gitpython-developers/smmap/workflows/Python%20package/badge.svg)
+
+Smmap wraps an interface around mmap and tracks the mapped files as well as the amount of clients who use it. If the system runs out of resources, or if a memory limit is reached, it will automatically unload unused maps to allow continued operation.
+
+To allow processing large files even on 32 bit systems, it allows only portions of the file to be mapped. Once the user reads beyond the mapped region, smmap will automatically map the next required region, unloading unused regions using a LRU algorithm.
+
+Although the library can be used most efficiently with its native interface, a Buffer implementation is provided to hide these details behind a simple string-like interface.
+
+For performance critical 64 bit applications, a simplified version of memory mapping is provided which always maps the whole file, but still provides the benefit of unloading unused mappings on demand.
+
+
+
+## Prerequisites
+
+* Python 3.7+
+* OSX, Windows or Linux
+
+The package was tested on all of the previously mentioned configurations.
+
+## Installing smmap
+
+[![Documentation Status](https://readthedocs.org/projects/smmap/badge/?version=latest)](https://readthedocs.org/projects/smmap/?badge=latest)
+
+Its easiest to install smmap using the [pip](http://www.pip-installer.org/en/latest) program:
+
+```bash
+$ pip install smmap
+```
+
+As the command will install smmap in your respective python distribution, you will most likely need root permissions to authorize the required changes.
+
+If you have downloaded the source archive, the package can be installed by running the `setup.py` script:
+
+```bash
+$ python setup.py install
+```
+
+It is advised to have a look at the **Usage Guide** for a brief introduction on the different database implementations.
+
+
+
+## Homepage and Links
+
+The project is home on github at https://github.com/gitpython-developers/smmap .
+
+The latest source can be cloned from github as well:
+
+* git://github.com/gitpython-developers/smmap.git
+
+
+For support, please use the git-python mailing list:
+
+* http://groups.google.com/group/git-python
+
+
+Issues can be filed on github:
+
+* https://github.com/gitpython-developers/smmap/issues
+
+A link to the pypi page related to this repository:
+
+* https://pypi.org/project/smmap/
+
+
+## License Information
+
+*smmap* is licensed under the New BSD License.
+
diff --git a/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/RECORD b/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/RECORD
new file mode 100644
index 0000000000000000000000000000000000000000..308549593fe8f28f7d700f96caf0598a6ee2b238
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/RECORD
@@ -0,0 +1,18 @@
+smmap-5.0.2.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+smmap-5.0.2.dist-info/LICENSE,sha256=iOnZP3CNEQsyioNDAt0dXGr72lMOdyHRXYCzUR2G8jU,1519
+smmap-5.0.2.dist-info/METADATA,sha256=3BUpM_oN3bljLC5WDH9bxeo5JqXV-AGVQ4Epw1C5h2Y,4306
+smmap-5.0.2.dist-info/RECORD,,
+smmap-5.0.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+smmap-5.0.2.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
+smmap-5.0.2.dist-info/top_level.txt,sha256=h0Tp1UdaROCy2bjWNha1MFpwgVghxEUQsaLHbZs96Gw,6
+smmap-5.0.2.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
+smmap/__init__.py,sha256=Bf9KWm-zr5__SoSEEXI20pqKOJFpUApidvardLulRLY,343
+smmap/buf.py,sha256=wElndUNa4Njhnes6JYmI_Xp4zwaohirLdr7eVR9XYNU,5762
+smmap/mman.py,sha256=HYxohllZqpPY4KgRF_D_lNp4fx0akZ-NQosutLDrQ60,24022
+smmap/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+smmap/test/lib.py,sha256=jYIT4rKckHw2s7-l5lwysft1jqrwDFkjtyNLKNpu_j0,1489
+smmap/test/test_buf.py,sha256=BCg9FbusakiYNs5MUNoJSiyuTP5HcMYl65HzvpY9SNo,5439
+smmap/test/test_mman.py,sha256=o3cPg5idyYJpq32zXgDy1qtD10RA_e88-1zPYBWi4Ms,10822
+smmap/test/test_tutorial.py,sha256=ZwCphCbKAGYt_fn_CiOJ9lWwpWwpqE4-zBUp-v-t9eM,3174
+smmap/test/test_util.py,sha256=3hJyW9Km7k7XSgxxtDOkG8eVagk3lIzP4H2pR0S2ewg,3468
+smmap/util.py,sha256=_Sl3Sd2KWdd-5nmhUegFMfT6L1j5BGchRT4z53Sobug,7476
diff --git a/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/REQUESTED
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/WHEEL
new file mode 100644
index 0000000000000000000000000000000000000000..ae527e7d64811439e61b93aa375defb30e06edfe
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: setuptools (75.6.0)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/top_level.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2765cefa1c4976ec31a903f3ffb7b2f9f92531f3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/top_level.txt
@@ -0,0 +1 @@
+smmap
diff --git a/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/zip-safe b/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/zip-safe
new file mode 100644
index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/smmap-5.0.2.dist-info/zip-safe
@@ -0,0 +1 @@
+
diff --git a/.venv/lib/python3.12/site-packages/sympy-1.14.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/sympy-1.14.0.dist-info/INSTALLER
new file mode 100644
index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sympy-1.14.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+uv
\ No newline at end of file
diff --git a/.venv/lib/python3.12/site-packages/sympy-1.14.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/sympy-1.14.0.dist-info/METADATA
new file mode 100644
index 0000000000000000000000000000000000000000..5883009e816c08a2672bc24096295aa8b3252a51
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sympy-1.14.0.dist-info/METADATA
@@ -0,0 +1,319 @@
+Metadata-Version: 2.4
+Name: sympy
+Version: 1.14.0
+Summary: Computer algebra system (CAS) in Python
+Home-page: https://sympy.org
+Author: SymPy development team
+Author-email: sympy@googlegroups.com
+License: BSD
+Project-URL: Source, https://github.com/sympy/sympy
+Keywords: Math CAS
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Scientific/Engineering
+Classifier: Topic :: Scientific/Engineering :: Mathematics
+Classifier: Topic :: Scientific/Engineering :: Physics
+Classifier: Programming Language :: Python :: 3
+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 :: Only
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Requires-Python: >=3.9
+Description-Content-Type: text/markdown
+License-File: LICENSE
+License-File: AUTHORS
+Requires-Dist: mpmath<1.4,>=1.1.0
+Provides-Extra: dev
+Requires-Dist: pytest>=7.1.0; extra == "dev"
+Requires-Dist: hypothesis>=6.70.0; extra == "dev"
+Dynamic: author
+Dynamic: author-email
+Dynamic: classifier
+Dynamic: description
+Dynamic: description-content-type
+Dynamic: home-page
+Dynamic: keywords
+Dynamic: license
+Dynamic: license-file
+Dynamic: project-url
+Dynamic: provides-extra
+Dynamic: requires-dist
+Dynamic: requires-python
+Dynamic: summary
+
+# SymPy
+
+[![pypi version](https://img.shields.io/pypi/v/sympy.svg)](https://pypi.python.org/pypi/sympy)
+[![Join the chat at https://gitter.im/sympy/sympy](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/sympy/sympy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[![Zenodo Badge](https://zenodo.org/badge/18918/sympy/sympy.svg)](https://zenodo.org/badge/latestdoi/18918/sympy/sympy)
+[![Downloads](https://pepy.tech/badge/sympy/month)](https://pepy.tech/project/sympy)
+[![GitHub Issues](https://img.shields.io/badge/issue_tracking-github-blue.svg)](https://github.com/sympy/sympy/issues)
+[![Git Tutorial](https://img.shields.io/badge/PR-Welcome-%23FF8300.svg?)](https://git-scm.com/book/en/v2/GitHub-Contributing-to-a-Project)
+[![Powered by NumFocus](https://img.shields.io/badge/powered%20by-NumFOCUS-orange.svg?style=flat&colorA=E1523D&colorB=007D8A)](https://numfocus.org)
+[![Commits since last release](https://img.shields.io/github/commits-since/sympy/sympy/latest.svg?longCache=true&style=flat-square&logo=git&logoColor=fff)](https://github.com/sympy/sympy/releases)
+
+[![SymPy Banner](https://github.com/sympy/sympy/raw/master/banner.svg)](https://sympy.org/)
+
+
+See the [AUTHORS](AUTHORS) file for the list of authors.
+
+And many more people helped on the SymPy mailing list, reported bugs,
+helped organize SymPy's participation in the Google Summer of Code, the
+Google Highly Open Participation Contest, Google Code-In, wrote and
+blogged about SymPy...
+
+License: New BSD License (see the [LICENSE](LICENSE) file for details) covers all
+files in the sympy repository unless stated otherwise.
+
+Our mailing list is at
+.
+
+We have a community chat at [Gitter](https://gitter.im/sympy/sympy). Feel
+free to ask us anything there. We have a very welcoming and helpful
+community.
+
+## Download
+
+The recommended installation method is through Anaconda,
+
+
+You can also get the latest version of SymPy from
+
+
+To get the git version do
+
+    $ git clone https://github.com/sympy/sympy.git
+
+For other options (tarballs, debs, etc.), see
+.
+
+## Documentation and Usage
+
+For in-depth instructions on installation and building the
+documentation, see the [SymPy Documentation Style Guide](https://docs.sympy.org/dev/documentation-style-guide.html).
+
+Everything is at:
+
+
+
+You can generate everything at the above site in your local copy of
+SymPy by:
+
+    $ cd doc
+    $ make html
+
+Then the docs will be in \_build/html. If
+you don't want to read that, here is a short usage:
+
+From this directory, start Python and:
+
+``` python
+>>> from sympy import Symbol, cos
+>>> x = Symbol('x')
+>>> e = 1/cos(x)
+>>> print(e.series(x, 0, 10))
+1 + x**2/2 + 5*x**4/24 + 61*x**6/720 + 277*x**8/8064 + O(x**10)
+```
+
+SymPy also comes with a console that is a simple wrapper around the
+classic python console (or IPython when available) that loads the SymPy
+namespace and executes some common commands for you.
+
+To start it, issue:
+
+    $ bin/isympy
+
+from this directory, if SymPy is not installed or simply:
+
+    $ isympy
+
+if SymPy is installed.
+
+## Installation
+
+To install SymPy using PyPI, run the following command:
+
+    $ pip install sympy
+
+To install SymPy using Anaconda, run the following command:
+
+    $ conda install -c anaconda sympy
+
+To install SymPy from GitHub source, first clone SymPy using `git`:
+
+    $ git clone https://github.com/sympy/sympy.git
+
+Then, in the `sympy` repository that you cloned, simply run:
+
+    $ pip install .
+
+See  for more information.
+
+## Contributing
+
+We welcome contributions from anyone, even if you are new to open
+source. Please read our [Introduction to Contributing](https://docs.sympy.org/dev/contributing/introduction-to-contributing.html)
+page and the [SymPy Documentation Style Guide](https://docs.sympy.org/dev/documentation-style-guide.html). If you
+are new and looking for some way to contribute, a good place to start is
+to look at the issues tagged [Easy to Fix](https://github.com/sympy/sympy/issues?q=is%3Aopen+is%3Aissue+label%3A%22Easy+to+Fix%22).
+
+Please note that all participants in this project are expected to follow
+our Code of Conduct. By participating in this project you agree to abide
+by its terms. See [CODE\_OF\_CONDUCT.md](CODE_OF_CONDUCT.md).
+
+## Tests
+
+To execute all tests, run:
+
+    $./setup.py test
+
+in the current directory.
+
+For the more fine-grained running of tests or doctests, use `bin/test`
+or respectively `bin/doctest`. The master branch is automatically tested
+by GitHub Actions.
+
+To test pull requests, use
+[sympy-bot](https://github.com/sympy/sympy-bot).
+
+## Regenerate Experimental LaTeX Parser/Lexer
+
+The parser and lexer were generated with the [ANTLR4](http://antlr4.org)
+toolchain in `sympy/parsing/latex/_antlr` and checked into the repo.
+Presently, most users should not need to regenerate these files, but
+if you plan to work on this feature, you will need the `antlr4`
+command-line tool (and you must ensure that it is in your `PATH`).
+One way to get it is:
+
+    $ conda install -c conda-forge antlr=4.11.1
+
+Alternatively, follow the instructions on the ANTLR website and download
+the `antlr-4.11.1-complete.jar`. Then export the `CLASSPATH` as instructed
+and instead of creating `antlr4` as an alias, make it an executable file
+with the following contents:
+``` bash
+#!/bin/bash
+java -jar /usr/local/lib/antlr-4.11.1-complete.jar "$@"
+```
+
+After making changes to `sympy/parsing/latex/LaTeX.g4`, run:
+
+    $ ./setup.py antlr
+
+## Clean
+
+To clean everything (thus getting the same tree as in the repository):
+
+    $ git clean -Xdf
+
+which will clear everything ignored by `.gitignore`, and:
+
+    $ git clean -df
+
+to clear all untracked files. You can revert the most recent changes in
+git with:
+
+    $ git reset --hard
+
+WARNING: The above commands will all clear changes you may have made,
+and you will lose them forever. Be sure to check things with `git
+status`, `git diff`, `git clean -Xn`, and `git clean -n` before doing any
+of those.
+
+## Bugs
+
+Our issue tracker is at . Please
+report any bugs that you find. Or, even better, fork the repository on
+GitHub and create a pull request. We welcome all changes, big or small,
+and we will help you make the pull request if you are new to git (just
+ask on our mailing list or Gitter Channel). If you further have any queries, you can find answers
+on Stack Overflow using the [sympy](https://stackoverflow.com/questions/tagged/sympy) tag.
+
+## Brief History
+
+SymPy was started by Ondřej Čertík in 2005, he wrote some code during
+the summer, then he wrote some more code during summer 2006. In February
+2007, Fabian Pedregosa joined the project and helped fix many things,
+contributed documentation, and made it alive again. 5 students (Mateusz
+Paprocki, Brian Jorgensen, Jason Gedge, Robert Schwarz, and Chris Wu)
+improved SymPy incredibly during summer 2007 as part of the Google
+Summer of Code. Pearu Peterson joined the development during the summer
+2007 and he has made SymPy much more competitive by rewriting the core
+from scratch, which has made it from 10x to 100x faster. Jurjen N.E. Bos
+has contributed pretty-printing and other patches. Fredrik Johansson has
+written mpmath and contributed a lot of patches.
+
+SymPy has participated in every Google Summer of Code since 2007. You
+can see  for
+full details. Each year has improved SymPy by bounds. Most of SymPy's
+development has come from Google Summer of Code students.
+
+In 2011, Ondřej Čertík stepped down as lead developer, with Aaron
+Meurer, who also started as a Google Summer of Code student, taking his
+place. Ondřej Čertík is still active in the community but is too busy
+with work and family to play a lead development role.
+
+Since then, a lot more people have joined the development and some
+people have also left. You can see the full list in doc/src/aboutus.rst,
+or online at:
+
+
+
+The git history goes back to 2007 when development moved from svn to hg.
+To see the history before that point, look at
+.
+
+You can use git to see the biggest developers. The command:
+
+    $ git shortlog -ns
+
+will show each developer, sorted by commits to the project. The command:
+
+    $ git shortlog -ns --since="1 year"
+
+will show the top developers from the last year.
+
+## Citation
+
+To cite SymPy in publications use
+
+> Meurer A, Smith CP, Paprocki M, Čertík O, Kirpichev SB, Rocklin M,
+> Kumar A, Ivanov S, Moore JK, Singh S, Rathnayake T, Vig S, Granger BE,
+> Muller RP, Bonazzi F, Gupta H, Vats S, Johansson F, Pedregosa F, Curry
+> MJ, Terrel AR, Roučka Š, Saboo A, Fernando I, Kulal S, Cimrman R,
+> Scopatz A. (2017) SymPy: symbolic computing in Python. *PeerJ Computer
+> Science* 3:e103 
+
+A BibTeX entry for LaTeX users is
+
+``` bibtex
+@article{10.7717/peerj-cs.103,
+ title = {SymPy: symbolic computing in Python},
+ author = {Meurer, Aaron and Smith, Christopher P. and Paprocki, Mateusz and \v{C}ert\'{i}k, Ond\v{r}ej and Kirpichev, Sergey B. and Rocklin, Matthew and Kumar, Amit and Ivanov, Sergiu and Moore, Jason K. and Singh, Sartaj and Rathnayake, Thilina and Vig, Sean and Granger, Brian E. and Muller, Richard P. and Bonazzi, Francesco and Gupta, Harsh and Vats, Shivam and Johansson, Fredrik and Pedregosa, Fabian and Curry, Matthew J. and Terrel, Andy R. and Rou\v{c}ka, \v{S}t\v{e}p\'{a}n and Saboo, Ashutosh and Fernando, Isuru and Kulal, Sumith and Cimrman, Robert and Scopatz, Anthony},
+ year = 2017,
+ month = Jan,
+ keywords = {Python, Computer algebra system, Symbolics},
+ abstract = {
+            SymPy is an open-source computer algebra system written in pure Python. It is built with a focus on extensibility and ease of use, through both interactive and programmatic applications. These characteristics have led SymPy to become a popular symbolic library for the scientific Python ecosystem. This paper presents the architecture of SymPy, a description of its features, and a discussion of select submodules. The supplementary material provides additional examples and further outlines details of the architecture and features of SymPy.
+         },
+ volume = 3,
+ pages = {e103},
+ journal = {PeerJ Computer Science},
+ issn = {2376-5992},
+ url = {https://doi.org/10.7717/peerj-cs.103},
+ doi = {10.7717/peerj-cs.103}
+}
+```
+
+SymPy is BSD licensed, so you are free to use it whatever you like, be
+it academic, commercial, creating forks or derivatives, as long as you
+copy the BSD statement if you redistribute it (see the LICENSE file for
+details). That said, although not required by the SymPy license, if it
+is convenient for you, please cite SymPy when using it in your work and
+also consider contributing all your changes back, so that we can
+incorporate it and all of us will benefit in the end.
diff --git a/.venv/lib/python3.12/site-packages/sympy-1.14.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/sympy-1.14.0.dist-info/RECORD
new file mode 100644
index 0000000000000000000000000000000000000000..e70e18d39367f51cde5d4db65da3a8b1f1a598d3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sympy-1.14.0.dist-info/RECORD
@@ -0,0 +1,1573 @@
+../../../bin/isympy,sha256=E5jgd_ZVd7wW-wbKIeF2hw_ld2ifXbM8hcX2i3G50aQ,322
+../../../share/man/man1/isympy.1,sha256=9DZdSOIQLikrATHlbkdDZ04LBQigZDUE0_oCXBDvdBs,6659
+isympy.py,sha256=haEArczb_Ny-LyjOCwx2FNGal_S0Mo4d0deiWL2g_Gc,11220
+sympy-1.14.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+sympy-1.14.0.dist-info/METADATA,sha256=t1bC-_1b4FrFvbDrymH1VhjzD2M-2S2IpWh0KTE6dZU,12816
+sympy-1.14.0.dist-info/RECORD,,
+sympy-1.14.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy-1.14.0.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
+sympy-1.14.0.dist-info/entry_points.txt,sha256=Sp-vLJom4PRlhGfY6RpUre7SjYm33JNq9NCwCGeW-fQ,39
+sympy-1.14.0.dist-info/licenses/AUTHORS,sha256=grEDhSGwaWxjyQsx7UoKLJxMFoccZIsVewfKRLbxj40,55779
+sympy-1.14.0.dist-info/licenses/LICENSE,sha256=B6XpgZ9ye0mGrSgpx6KaYyDUJXX3IOsk1xt_71c6AoY,7885
+sympy-1.14.0.dist-info/top_level.txt,sha256=elXb5xfjLdjgSSoQFk4_2Qu3lp2CIaglF9MQtfIoH7o,13
+sympy/__init__.py,sha256=TpR2NIuhBf6rKNgvW89s26Lj6E3m4Fm7_noTcowKSrA,29422
+sympy/abc.py,sha256=-RYLRKQ6SZMJGXDIAOP9UWPDAyvYH0zPbp9ZFg0YKBY,3764
+sympy/algebras/__init__.py,sha256=7PRGOW30nlMOTeUPR7iy8l5xGoE2yCBEfRbjqDKWOgU,62
+sympy/algebras/quaternion.py,sha256=i2Fkvjj14vA6GtXgcSkpDwVmIYc7T5zT1hw49sb__tM,47483
+sympy/algebras/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/algebras/tests/test_quaternion.py,sha256=un4I6IKE2v6YEfUsFBSY5N6qaZ0agn1puX0LElb1k4E,16809
+sympy/assumptions/__init__.py,sha256=PFS8djTqiNbGVMjg7PaPjEfwmjyZVfioXiRVzqqA3E0,550
+sympy/assumptions/ask.py,sha256=B2W8CW-9ynvYqfBX9_4qCLhCSwE0g_gUGqq7lM50wUk,19376
+sympy/assumptions/ask_generated.py,sha256=whNIU5tj2UkEGHPAfk-_89ahvVERs4npOB0JYVbIQJc,23558
+sympy/assumptions/assume.py,sha256=_gcFc4h_YGs9-tshoD0gmLl_RtPivDQWMWhWWLX9seo,14606
+sympy/assumptions/cnf.py,sha256=mfthFXL8Jhpn7Vgj1IfG5acFzfBgaV02xztQ7KpoLmg,12509
+sympy/assumptions/facts.py,sha256=fimPoHEyusSUr0uI4kiDb4mzxHjEBglvLQW0DGdNFAs,8391
+sympy/assumptions/handlers/__init__.py,sha256=lvjAfPdz0MDjTxjuzbBSGBco2OmpZRiGixSG0oaiZi0,330
+sympy/assumptions/handlers/calculus.py,sha256=WF3gysxZa2frvd1kHxdDN7xd0PwYemER8dWutfDN-w8,7799
+sympy/assumptions/handlers/common.py,sha256=xgUR3xHPNgGFkLGyMqLV_nUIfMsQJLP8p6boy8Rc6JI,4314
+sympy/assumptions/handlers/matrices.py,sha256=Gdauk2xk1hKPRr4i6RpvOMHtDnyVD34x1OyhL-Oh8Hc,22321
+sympy/assumptions/handlers/ntheory.py,sha256=lRhW2ceJPK0gNqR00NAwkEq1FBjtRrxz1u6WevjRvjc,7660
+sympy/assumptions/handlers/order.py,sha256=-NCLn8ab7Fo_UzZLZi7m3UwtYGN-XiU1qvJDhi5z7ZQ,12280
+sympy/assumptions/handlers/sets.py,sha256=9P8X6ucARJIJA3jG_gj736L71ZQi5hfLCKL--0_ifrw,25841
+sympy/assumptions/lra_satask.py,sha256=FlmiLERsj6J9w6vygwEEEn7pyGPnD0JkPEFEdoE7bfM,9563
+sympy/assumptions/predicates/__init__.py,sha256=q1C7iWpvdDymEUZNyzJvZLsLtgwSkYtCixME-fYyIDw,110
+sympy/assumptions/predicates/calculus.py,sha256=vFnlYVYZVd6D9OwA7-3bDK_Q0jf2iCZCZiMlWenw0Vg,1889
+sympy/assumptions/predicates/common.py,sha256=zpByACpa_tF0nVNB0J_rJehnXkHtkxhchn1DvkVVS-s,2279
+sympy/assumptions/predicates/matrices.py,sha256=X3vbkEf3zwJLyanEjf6ijYXuRfFfSv-yatl1tJ25wDk,12142
+sympy/assumptions/predicates/ntheory.py,sha256=wvFNFSf0S4egbY7REw0V0ANC03CuiRU9PLmdi16VfHo,2546
+sympy/assumptions/predicates/order.py,sha256=ez1UZ824KDtimLssUASCZHD_KEQmo8Pv-qofVLhZUrk,9511
+sympy/assumptions/predicates/sets.py,sha256=-bTVXa-X1-yfXlIKzMBW_JxIqueS5PdEwEzChzIne38,9238
+sympy/assumptions/refine.py,sha256=FNv5neAYJh-MgvnHDZ8-tjC9RIKcIxarVj5WiE4EnYg,11946
+sympy/assumptions/relation/__init__.py,sha256=t2tZNEIK7w-xXshRQIRL8tIyiNe1W5fMhN7QNRPnQFo,261
+sympy/assumptions/relation/binrel.py,sha256=3iwnSEE53-vRsPv-bOnjydgOkCpbB12FTFR_sQ3CwvE,6313
+sympy/assumptions/relation/equality.py,sha256=ZBnSFpctNeroYy1nvam0kzSJCNpsUR3SlBeqFcAMM0U,7148
+sympy/assumptions/satask.py,sha256=P3iprPjuOyhT5Fwr0hX61xTOcD98M_bzXSAV-pXYhN4,11745
+sympy/assumptions/sathandlers.py,sha256=jx8B0u_N73fMoVoLKIfmXMdtSLz7-ZIKhJrxYl84AJk,9418
+sympy/assumptions/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/assumptions/tests/test_assumptions_2.py,sha256=oNgIDOoW-GpBbXxbtw05SWnE8I7sGislYmB3MDogwB4,1070
+sympy/assumptions/tests/test_context.py,sha256=I5gES7AY9_vz1-CEaCchy4MXABtX85ncNkvoRuLskG8,1153
+sympy/assumptions/tests/test_matrices.py,sha256=nzSofuawc18hNe9Nj0dN_lTeDwa2KbPjt4K2rvb3xmw,12258
+sympy/assumptions/tests/test_query.py,sha256=ENB3XoNNi7j0p1_-j9Xd9C5YYpRbu1dcAQe46foV8cU,104495
+sympy/assumptions/tests/test_refine.py,sha256=bHxYUnCOEIzA1yPU3B2xbU9JZfhDv6RkmPm8esetisQ,8834
+sympy/assumptions/tests/test_rel_queries.py,sha256=fRn_IMMb942GfEH8CNk6z50YCyDlA_GMXv2xOVFdSdk,6677
+sympy/assumptions/tests/test_satask.py,sha256=IIqqIxzkLfANpTNBKEsCGCp3Bm8zmDnYd23woqKh9EE,15741
+sympy/assumptions/tests/test_sathandlers.py,sha256=jMCZQb3G6pVQ5MHaSTWV_0eULHaCF8Mowu12Ll72rgs,1842
+sympy/assumptions/tests/test_wrapper.py,sha256=iE32j83rrerCz85HHt2hTolgJkqb44KddfEpI3H1Fb8,1159
+sympy/assumptions/wrapper.py,sha256=7GXR39zPCCfV-pcs8ph9KRRwZF3i_T5Lzv156vKFf_I,5434
+sympy/benchmarks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/benchmarks/bench_discrete_log.py,sha256=CNchIJ5HFMPpNlVZh2vOU0GgQ3bse6hqyqDovpDHlKE,2473
+sympy/benchmarks/bench_meijerint.py,sha256=dSNdZhoc8a4h50wRtbOxLwpmgUiuMFpe6ytTLURcplY,11610
+sympy/benchmarks/bench_symbench.py,sha256=UMD3eYf_Poht0qxjdH2_axGwwON6cZo1Sp700Ci1M1M,2997
+sympy/calculus/__init__.py,sha256=IWDc6qPbEcWyTm9QM6V8vSAs-5OtGNijimykoWz3Clc,828
+sympy/calculus/accumulationbounds.py,sha256=4nJD5JzBarlB5l6yPpVADX3iDamnHVwsor_nQJw9CAM,28682
+sympy/calculus/euler.py,sha256=0QrHD9TYKlSZuO8drnU3bUFJrSu8v5SncqtkRSWLjGM,3436
+sympy/calculus/finite_diff.py,sha256=X7qZJ5GmHlHKokUUMFoaQqrqX2jLRq4b7W2G5aWntzM,17053
+sympy/calculus/singularities.py,sha256=wBQ7WiJ1amuZStBJ-iMTiIHJexjzJHHwrc0tU2XVT10,12184
+sympy/calculus/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/calculus/tests/test_accumulationbounds.py,sha256=a_Ry2nKX5WbhSe1Bk2k0W6-VWOpVTg0FnA9u8rNSIV4,11195
+sympy/calculus/tests/test_euler.py,sha256=YWpts4pWSiYEwRsi5DLQ16JgC9109-9NKZIL_IO6_Aw,2683
+sympy/calculus/tests/test_finite_diff.py,sha256=9mIvXB8DaxJh2gzmhwi5eE3eBRYTFxyqYG5uIUpYg2w,7670
+sympy/calculus/tests/test_singularities.py,sha256=Zj4WPkT-KlXo7TF3Ir3ug3IkS_qhnFBS7VXAMnIuCso,5272
+sympy/calculus/tests/test_util.py,sha256=IJIhgudR9dym1VRAdu33G2tfSDoexNdWdz-Pgf-kh4o,18557
+sympy/calculus/util.py,sha256=qBDTlb5VP-xJPfh2Ndz37zNMVpseXW4d77hxBCLX5AA,28880
+sympy/categories/__init__.py,sha256=XiKBVC6pbDED-OVtNlSH-fGB8dB_jWLqwCEO7wBTAyA,984
+sympy/categories/baseclasses.py,sha256=1Kn7PIegQCbF78s0rhf1Bx1mbxwfQcfQi6v-QqloSoE,31360
+sympy/categories/diagram_drawing.py,sha256=lfR29f6QX-rK-cCAom-2mRJ4xLyhi55ch91CyEPCXAQ,95173
+sympy/categories/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/categories/tests/test_baseclasses.py,sha256=SwD6QsfSlrEdpD2dbkcN62CPVIRP5SadjCplLrMAoa8,5767
+sympy/categories/tests/test_drawing.py,sha256=IELPpadmnQyQ2x5a5qHC8ioq5kfT1UnAl4h1vO3gbqg,27848
+sympy/codegen/__init__.py,sha256=sQcJsyLyoRh9ccOPhv2eZ-wHjQrArByOON9ndj-MYgQ,974
+sympy/codegen/abstract_nodes.py,sha256=TY4ecftqnym5viYInnb59zGPPFXdeSGQwi--xTz6Pvo,490
+sympy/codegen/algorithms.py,sha256=GEvnadOTZ3afVrbN5WE52OhCP8gzMII_AN4oHQxvEpo,6383
+sympy/codegen/approximations.py,sha256=r7zyShIWp40QngshFQwqKVKWcA5KmHmQcJarXVtOATQ,6448
+sympy/codegen/ast.py,sha256=R_JhC-uryx9NoJ1ISQU6fb3aV4aRL1SModmV_Dc2z2U,56788
+sympy/codegen/cfunctions.py,sha256=jrFWaRjrQnpHzu6-mwFtd6cRspK8Jo7B3RDo1AOoTLo,12331
+sympy/codegen/cnodes.py,sha256=lsqy-JeRvr9WCk2fwDiRqPhAMFk0snInF7WlAlk9-Zg,3409
+sympy/codegen/cutils.py,sha256=vlzMs8OkC5Bu4sIP-AF2mYf_tIo7Uo4r2DAI_LNhZzM,383
+sympy/codegen/cxxnodes.py,sha256=Om-EBfYduFF97tgXOF68rr8zYbngem9kBRm9SJiKLSM,342
+sympy/codegen/fnodes.py,sha256=mdOPkMVmjjNp22XfHscvx8uMtUxbUqTl66Wki7ORMa8,18936
+sympy/codegen/futils.py,sha256=k-mxMJKr_Q_afTy6NrKNl_N2XQLBmSdZAssO5hBonNY,1792
+sympy/codegen/matrix_nodes.py,sha256=0d3qXy2zaq3isyklE48lP7NP5LTF7SkLXMHMbweVGXU,2284
+sympy/codegen/numpy_nodes.py,sha256=H6FoBnarEuO7R1sbX8Qa9hK4jCju90R2qN66_-1N6Ug,4543
+sympy/codegen/pynodes.py,sha256=Neo1gFQ9kC31T-gH8TeeCaDDNaDe5deIP97MRZFgMHk,243
+sympy/codegen/pyutils.py,sha256=HfF6SP710Y7yExZcSesI0usVaDiWdEPEmMtyMD3JtOY,838
+sympy/codegen/rewriting.py,sha256=8JtiMFgv0sA7uGu2MUU7L3uzldGw5xG1ksuk4zh2ZDE,11585
+sympy/codegen/scipy_nodes.py,sha256=hYlxtGyTM0Z64Nazm1TeMZ3Y8dMsiD_HNhNvbU9eiQY,2508
+sympy/codegen/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/codegen/tests/test_abstract_nodes.py,sha256=a_GKf3FpeNN8zfMc-V8AaSrQtEI1oiLfJOco2VKiSKI,451
+sympy/codegen/tests/test_algorithms.py,sha256=lgl6w3d7WaT8LJrp716wWicxbgnR4hQk4alzBCdDrIw,6919
+sympy/codegen/tests/test_applications.py,sha256=RFWS7EoE3DytUrr4HzkPaXbOtnaStpzJalVKrY1rVRE,2277
+sympy/codegen/tests/test_approximations.py,sha256=SZpOUzahb_bJOceD0DLdmeiw-jN37OPmf5TRp1dyRgM,2035
+sympy/codegen/tests/test_ast.py,sha256=aAWk-yAVVNAmFMkyUlYBbVA8mPlTFqULOtmXMEi3LO8,21688
+sympy/codegen/tests/test_cfunctions.py,sha256=4jJM7dCLPbfEU3CB1ZUKGC1pRuRvjqZfDkFfYSkrzsY,5119
+sympy/codegen/tests/test_cnodes.py,sha256=FlI5XP39K3kC1QWKQ-QKkzNQw8TROjj5mKXJhK1UU2c,3039
+sympy/codegen/tests/test_cxxnodes.py,sha256=5OwN8D_ZtKN9z5uNeUwbUkyAGzNLrTgIKUlcRWmOSpE,366
+sympy/codegen/tests/test_fnodes.py,sha256=dKSw6w7i4Z8NHVDu_ug4V9Fa49CfnSX1FVahDIdNPgI,6650
+sympy/codegen/tests/test_matrix_nodes.py,sha256=CTMSwQkW0JHaMgHR9Lys8kDk5UgQidTl4VQhWI8gw7s,1896
+sympy/codegen/tests/test_numpy_nodes.py,sha256=tEeyF9vDmw-EyKnHV2dYM6MWGfA2I2fmf3kBsYxqad0,2199
+sympy/codegen/tests/test_pynodes.py,sha256=Gso18KKzSwA-1AHC55SgHPAfH1GrGUCGaN6QR7iuEO0,432
+sympy/codegen/tests/test_pyutils.py,sha256=yvqif7d6EpsnaBjP8XXjVo3wEENBxI6Vm01I1Wow-js,299
+sympy/codegen/tests/test_rewriting.py,sha256=ELPziNI3CsJ4VS7mUbk4QWyG_94FbgZCdBKieMN20Vc,15852
+sympy/codegen/tests/test_scipy_nodes.py,sha256=LBWpjTRfgWN5NLTchLZEp6m7IMtu7HbiKoztLc6KNGY,1495
+sympy/combinatorics/__init__.py,sha256=Dx9xakpHuTIgy4G8zVjAY6pTu8J9_K3d_jKPizRMdVo,1500
+sympy/combinatorics/coset_table.py,sha256=soppBd8i6UV-sppdOzykfNE8evMeclU4l1TwfL5UlXI,43296
+sympy/combinatorics/fp_groups.py,sha256=0iLx7mtwxn1kjYFysgAzILAdtlS9e1VWPtWnszubhd8,47779
+sympy/combinatorics/free_groups.py,sha256=gt33RoIByKDZNaf661ay4pQMilPPMPY4hFNGIKi2ho0,39740
+sympy/combinatorics/galois.py,sha256=cpd2iHaSdim5-3-cqvk58YP-AuS6CvJeCdUWpiOdIZI,17867
+sympy/combinatorics/generators.py,sha256=UAdv0bvqtN6i9bfFgB873g-A-zssc0ZoiHckDOEI0M0,7479
+sympy/combinatorics/graycode.py,sha256=xbtr8AaFYb4SMmwUi7mf7913U87jH-XEYF_3pGZfj0o,11207
+sympy/combinatorics/group_constructs.py,sha256=IKx12_yWJqEQ7g-oBuAWd5VRLbCOWyL0LG4PQu43BS8,2021
+sympy/combinatorics/group_numbers.py,sha256=eOiK0RyVHewiGiPXHihfM7MIKlAlg7AhGnBL4f1D54M,9163
+sympy/combinatorics/homomorphisms.py,sha256=dCpmPM3V2ReRuYDdXDfdMTU3pt7zkjKBkYSF6X2MfE8,18844
+sympy/combinatorics/named_groups.py,sha256=zd_C9epKDrMG0drafGUcHuuJJkcMaDt1Nf2ik4NXNq8,8378
+sympy/combinatorics/partitions.py,sha256=ZXqVmVNjmauhMeiTWtCCqOP38b9MJg7UlBdZa-7aICQ,20841
+sympy/combinatorics/pc_groups.py,sha256=5AwEeYQCMdYcybhlEykvJZqEYtJQLgYbRxAnNQeajfE,21414
+sympy/combinatorics/perm_groups.py,sha256=sf0FfxmJ5uWCwlvzRD5m5HHduG5qqHzh3Y0n1Dax5Yo,184825
+sympy/combinatorics/permutations.py,sha256=nQoc4YgTeoT80eHkqetTBUxwAQQW40US2AnR2u4-E_k,87737
+sympy/combinatorics/polyhedron.py,sha256=-1y5GhorUK62_gJpn4tXTLye7BcG0hAup74waDQ8y2I,35928
+sympy/combinatorics/prufer.py,sha256=yN6d4w_ZVXNFhBoevA84gor4Xb5ttG529xbVgHxzKDo,12061
+sympy/combinatorics/rewritingsystem.py,sha256=cT1JrAuKj9rWI3IhaHekYYt0rdG56pwFLg32pcGC9aI,17095
+sympy/combinatorics/rewritingsystem_fsm.py,sha256=CKGhLqyvxY0mlmy8_Hb4WzkSdWYPUaU2yZYhz-0iZ5w,2433
+sympy/combinatorics/schur_number.py,sha256=YdsyA7n_z9tyfRTSRfIjEjtnGo5EuDGBMUS09AQ2MxU,4437
+sympy/combinatorics/subsets.py,sha256=oxuExuGyFnvunkmktl-vBYiLbiN66A2Q2MyzwWfy46A,16047
+sympy/combinatorics/tensor_can.py,sha256=JywytVypkN6aWdCpeEYletMW4vxyUvU29RfKq9-Lu0c,40738
+sympy/combinatorics/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/combinatorics/tests/test_coset_table.py,sha256=cEUF0OH6SNhN_kh069wMsq6h4eSVqbDLghrg2r9Ht48,28474
+sympy/combinatorics/tests/test_fp_groups.py,sha256=C4rWnSJyk7L6uZnI-kAYvtbfFXxba0o1XjpOTkWr97s,10203
+sympy/combinatorics/tests/test_free_groups.py,sha256=g47dnRsNXVG9lFcqppJyrGryGIUCw9dRASkcMa4QujA,6411
+sympy/combinatorics/tests/test_galois.py,sha256=w35JRx8lmlXCdzUBNdocgATPYWBOEZ6LH-tAxOPwCQ8,2763
+sympy/combinatorics/tests/test_generators.py,sha256=6YpOp0i5PRGtySPNZseQ8mjSXbwpfGfz0hDB4kfk40Q,3567
+sympy/combinatorics/tests/test_graycode.py,sha256=pI4e7Y615d5Bmmxui6fdEeyca6j6KSD0YmeychV6ORk,2800
+sympy/combinatorics/tests/test_group_constructs.py,sha256=jJLwMdhuUalKv4Aql9SzV2utK8Ex-IYdMecggr95pi8,450
+sympy/combinatorics/tests/test_group_numbers.py,sha256=spHwsvjzEDXsdAFxkrQYOnioTgtWoVF5K7k7FbgMvfg,4149
+sympy/combinatorics/tests/test_homomorphisms.py,sha256=UwBj5loCuZAiuvmqy5VAbwhCQTph8o6BzTaGrH0rzB4,3745
+sympy/combinatorics/tests/test_named_groups.py,sha256=tsuDVGv4iHGEZ0BVR87_ENhyAfZvFIl0M6Dv_HX1VoY,1931
+sympy/combinatorics/tests/test_partitions.py,sha256=oppszKJLLSpcEzHgespIveSmEC3fDZ0qkus1k7MBt4E,4097
+sympy/combinatorics/tests/test_pc_groups.py,sha256=wfkY_ilpG0XWrhaWMVK6r7yWMeXfM8WNTyti5oE9bdk,2728
+sympy/combinatorics/tests/test_perm_groups.py,sha256=70_AedMpr-YxiYDHHjw6fjvmYW7-YoznEGwUeGtJo0E,41195
+sympy/combinatorics/tests/test_permutations.py,sha256=E8J-WLCW2z9hTyTJcm1dsFgFXh8YAesIppYsXRu5pAs,20189
+sympy/combinatorics/tests/test_polyhedron.py,sha256=3SWkFQKeF-p1QWP4Iu9NIA1oTxAFo1BLRrrLerBFAhw,4180
+sympy/combinatorics/tests/test_prufer.py,sha256=OTJp0NxjiVswWkOuCIlnGFU2Gw4noRsrPpUJtp2XhEs,2649
+sympy/combinatorics/tests/test_rewriting.py,sha256=3COHq74k6knt2rqE7hfd4ZP_6whf0Kg14tYxFmTtYrI,1787
+sympy/combinatorics/tests/test_schur_number.py,sha256=wg13uTumFltWIGbVg_PEr6nhXIru19UWitsEZiakoRI,1727
+sympy/combinatorics/tests/test_subsets.py,sha256=6pyhLYV5HuXvx63r-gGVHr8LSrGRXcpDudhFn9fBqX8,2635
+sympy/combinatorics/tests/test_tensor_can.py,sha256=olH5D5wwTBOkZXjtqvLO6RKbvCG9KoMVK4__wDe95N4,24676
+sympy/combinatorics/tests/test_testutil.py,sha256=NYEI8X2qLWf7Gv1O7UthOHUSyWpbNCRrGmG_vdyEgF8,1733
+sympy/combinatorics/tests/test_util.py,sha256=sOYMWHxlbM0mqalqA7jNrYMm8DKcf_GwL5YBjs96_C4,4499
+sympy/combinatorics/testutil.py,sha256=GNnqy0mb6yPMa3zpGEzz2p6uxY7VtobPtwUialhfYEQ,11142
+sympy/combinatorics/util.py,sha256=lkOaITBImqB9yyLvN8DU0G-vraw27cSx2XaPdAPVBhg,16296
+sympy/concrete/__init__.py,sha256=2HDmg3VyLgM_ZPw3XsGpkOClGiQnyTlUNHSwVTtizA0,144
+sympy/concrete/delta.py,sha256=38M0E24b6V-dn706WXnylM4DAvvyb6tFy7eRE4oR5Bo,9870
+sympy/concrete/expr_with_intlimits.py,sha256=vj4PjttB9xE5aUYu37R1A4_KtGgxcPa65jzjv8-krsc,11352
+sympy/concrete/expr_with_limits.py,sha256=dL5u-b_CzwghTWIhNsGc4md8jPWfhXOvna5Lig6XVr0,21834
+sympy/concrete/gosper.py,sha256=JUFbnOWSls8Xo0Zj198Xm4adopE-rAJjXv6M2q9x7us,5499
+sympy/concrete/guess.py,sha256=6EejdhNgqjMK7lVTXms8-_xGmmpI3vtliLW7-L2eaDM,17474
+sympy/concrete/products.py,sha256=I_aGMeKtSuFU07PGfZPHHTwS1WxDRNbnjedNC_m1k24,18456
+sympy/concrete/summations.py,sha256=D5n4x-fzqhgRwTIWpdN3Xun4I8u847tB7Dcex7lh7lI,55998
+sympy/concrete/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/concrete/tests/test_delta.py,sha256=uI7xjMx7JuVb3kkN7cLR6_pGsKS4Ulq22p-Z9oti5Jc,23869
+sympy/concrete/tests/test_gosper.py,sha256=ZHiZfYGCeCS9I-0oqN6sFbiYa-284GeFoGsNbhIWq4I,7987
+sympy/concrete/tests/test_guess.py,sha256=TPW6Hy11Po6VLZG_dx95x3sMBYl5kcQH8wjJ6TOtu-k,3370
+sympy/concrete/tests/test_products.py,sha256=caYc-xlEIrX9I_A-KPQdwp5oDprVJSbfcOaKg_qUnsM,14521
+sympy/concrete/tests/test_sums_products.py,sha256=rg0cSziPNgkvyehA0wCf-3jj8fSmNT7ECOOkiXWN64o,66390
+sympy/conftest.py,sha256=wvibr2FiWTlUcLTYAFEL8tSAKWPnv_Q6Cj4lARBCM0U,2775
+sympy/core/__init__.py,sha256=0L72TGngrIg2JknW3elaPSIDmkpPjWGNVfNk33wKXJ0,3123
+sympy/core/_print_helpers.py,sha256=GQo9dI_BvAJtYHVFFfmroNr0L8d71UeI-tU7SGJgctk,2388
+sympy/core/add.py,sha256=TddwsicZFR4yEtVTkZmkzFtn9ndAlhfad7vJiECr6o4,43407
+sympy/core/alphabets.py,sha256=vWBs2atOvfRK6Xfg6hc5IKiB7s_0sZIiVJpcCUJL0N4,266
+sympy/core/assumptions.py,sha256=8K9rhYtT-kqS7hmc9ltkYC-wujYT3pe0BaUJNrOr8fg,23595
+sympy/core/assumptions_generated.py,sha256=0TJKYIHSIFyQcVHZdIHZ19b7tqst_sY7iZwjKzcvZBM,42817
+sympy/core/backend.py,sha256=hF9cftjzB92QEcwimekj0gQ9CEMXTH-leH3l8QcguGo,5273
+sympy/core/basic.py,sha256=IzZvs7iIcMvV8_athdOBnIvBYJg38khuO2fAJtd7uTE,76789
+sympy/core/benchmarks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/core/benchmarks/bench_arit.py,sha256=gfrnvKSXLCaUoFFxMgJhnLUp7rG9Pa_YT7OKgOrPP8E,412
+sympy/core/benchmarks/bench_assumptions.py,sha256=evfZzTgOUUvvvlK0DRdDZQRqxIlGLfJYzKu8QDMxSks,177
+sympy/core/benchmarks/bench_basic.py,sha256=YF0tTJ_AN_Wz11qidzM4bIhlwEhEqVc-IGVGrUx6SaA,210
+sympy/core/benchmarks/bench_expand.py,sha256=xgQYQMwqgXJtKajM4JVhuL-7AW8TLY-vdBpO6uyMDoQ,427
+sympy/core/benchmarks/bench_numbers.py,sha256=_1yHSbYmxriiTTzKtxFh7kJm-rXPL6roGs8MW1E5-sg,1135
+sympy/core/benchmarks/bench_sympify.py,sha256=G5iGInhhbkkxSY2pS08BNG945m9m4eZlNT1aJutGt5M,138
+sympy/core/cache.py,sha256=AyG7kganyV0jVx-aNBEUFogqRLHQqqFn8xU3ZSfJoaM,6172
+sympy/core/compatibility.py,sha256=XQH7ezmRi6l3R23qMHN2wfA-YMRWbh2YYjPY7LRo3lo,1145
+sympy/core/containers.py,sha256=IA8PWQeCl78Pn_1WczoYUzOhBoT9X2hM3_hJ232gyKc,11411
+sympy/core/core.py,sha256=lX3Af31Qyff-gwPiAH-vijjuOzBFSuQtN06nUqIcSTQ,547
+sympy/core/coreerrors.py,sha256=imD9TCL-UEM5hrHcqGQbbfLeyZCSJMx6vB1NBQITQIQ,642
+sympy/core/decorators.py,sha256=NO3oe7yIO4q-6Vvy2vTpIAS8GGI-JHjEOEGeAv97voY,8759
+sympy/core/evalf.py,sha256=X0BinrUYeCKyHPHJQnu3FGASTUB8iz5dweVvqeArBrU,62089
+sympy/core/expr.py,sha256=HR_xzh6273FBmdfNbogGzkWoqaI_k_CeIOnpmyzwCrQ,143770
+sympy/core/exprtools.py,sha256=Ft7C1K2kD4_EdpRrGxii_WDub4pK_rYnLTdeVhlBoeo,51544
+sympy/core/facts.py,sha256=54pFKhJwEzU8LkO7rL25TwGjIb5y5CvZleHEy_TpD68,19546
+sympy/core/function.py,sha256=y0vEdXaUD8thP3XasZVkkogqXOGB0cs2JnfHEa4DNjQ,117102
+sympy/core/intfunc.py,sha256=TigJI9bvsb4aN0aZJgCzxIHlv_eh0rElWeTurier3yI,14219
+sympy/core/kind.py,sha256=9kQvtDxm-SSRGi-155XsBl_rs-oN_7dw7fNNT3mDu2Q,11540
+sympy/core/logic.py,sha256=Qbj2WUh5lS0GjxvtPaNmcQXz7mbWXuWj5ZNFZ-N-YPg,10827
+sympy/core/mod.py,sha256=XDfpth2r4AWc04oLqtDWwFuYRLzI0l0MhLMwGSMu99g,8363
+sympy/core/mul.py,sha256=rjCiyOqxMgUTWR8sAcmilRfQLup9wv8Ij7s7JIfUkVM,79201
+sympy/core/multidimensional.py,sha256=NWX1okybO_nZCl9IhIOE8QYalY1WoC0zlzsvBg_E1eE,4233
+sympy/core/numbers.py,sha256=WEkS1JJ9ov-n4hXvxwrH1NPikNlyBCjLN5v-jOZxvsw,136741
+sympy/core/operations.py,sha256=_wjhMX-MD6cyUNKCc9K_QxyxpeG4WQehdypo0Fp76GM,25828
+sympy/core/parameters.py,sha256=EoT2S3W1dS2-HoV6WN7szBexXvn5_w43e2JFouKuvkU,3854
+sympy/core/power.py,sha256=FJSYRf0S3pk1JGkVy3kdWWQENhmpVme-Pf9SS2Rc6SE,73244
+sympy/core/random.py,sha256=miFdVpNKfutbkpYiIOzG9kVNUm5GTk-_nnmQqUhVDZs,6647
+sympy/core/relational.py,sha256=htbGT0uvzoYD07Q6_hdT0iDDsns0NDI2aml0W8mtAgM,51896
+sympy/core/rules.py,sha256=AJuZztmYKZ_yUITLZB6rhZjDy6ROBCtajcYqPa50sjc,1496
+sympy/core/singleton.py,sha256=2Ueja8GJ-0-cvBzlqPwL9hB9eOtjrJFXV6mkDZO1Bp4,7115
+sympy/core/sorting.py,sha256=PoL2-MtVeuYTu-DISGlbvqW2mt787BmWfzrwV1ibavE,10827
+sympy/core/symbol.py,sha256=0xwgxT7ZfhPjq87glw2CQq5MtIEO-DOrAoXu7bHwWFo,29820
+sympy/core/sympify.py,sha256=1vWheH469HqICHrADMY_k4T4eDQlGORNjEh7Re4WbmE,20546
+sympy/core/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/core/tests/test_args.py,sha256=gsvYkFoqSWM0G8sjwBkwwNpT_9jc-io1SyDcchI3fA4,184252
+sympy/core/tests/test_arit.py,sha256=TH4b9Z_sMGWlYqiU5MqpY5LmhzYRJSTHWtTNIrYv7fk,78611
+sympy/core/tests/test_assumptions.py,sha256=MjJdF_ymVL6mtgQx-aSr_rsNNxaTi2pHFLjyaPCBq5Q,41573
+sympy/core/tests/test_basic.py,sha256=skFOWZRNqDujmQ8GAskWH7OYcsrO0miBdM92Gy01k0U,10230
+sympy/core/tests/test_cache.py,sha256=p6Ci75a_T-bBXE_5HVxRKla62uSay_0Vuf57gUuH6sI,2001
+sympy/core/tests/test_compatibility.py,sha256=7pvNUEGIcRrfWl3doqHlm3AdNkGlcChO69gos3Fk09A,240
+sympy/core/tests/test_complex.py,sha256=koNGFMt6UMmzahJADSja_eD24gr-GG5gGCtyDgCRtPI,21906
+sympy/core/tests/test_constructor_postprocessor.py,sha256=0d7vbVuKi3GCm3PKLtiNqv_Au7v6RYt1rzRdHiD08tM,2441
+sympy/core/tests/test_containers.py,sha256=ijteBC6cjqzejfxXZZIELZJfMrgTQa1n6AlxHGCEQJs,7432
+sympy/core/tests/test_count_ops.py,sha256=eIA2WvCuWKXVBJEGfWoJrn6WfUshX_NXttrrfyLbNnI,5665
+sympy/core/tests/test_diff.py,sha256=6j4Vk9UCNRv8Oyx_4iv1ePjocwBg7_-3ftrSJ8u0cPo,5421
+sympy/core/tests/test_equal.py,sha256=RoOJuu4kMe4Rkk7eNyVOJov5S1770YHiVAiziNIKd2o,1678
+sympy/core/tests/test_eval.py,sha256=o0kZn3oaMidVYdNjeZYtx4uUKBoE3A2tWn2NS4hu72Q,2366
+sympy/core/tests/test_evalf.py,sha256=aIf2xMuFomr828s4qv6Q16xLHWNmBnDfC4FUv9g619M,28574
+sympy/core/tests/test_expand.py,sha256=jbIjBEmdsPPArsxJ9206YzMS7mPUVZo7j-7alM795eU,13886
+sympy/core/tests/test_expr.py,sha256=249jMUV5GnkjmVVcgPSkwWvzrT00ppWeygkK_9RO6kc,78428
+sympy/core/tests/test_exprtools.py,sha256=L7fi319z1EeFag6pH8myqDQYQ32H193QLKMdqlxACsY,19021
+sympy/core/tests/test_facts.py,sha256=YEZMZ-116VFnFqJ48h9bQsF2flhiB65trnZvJsRSh_o,11579
+sympy/core/tests/test_function.py,sha256=_YRRGTDthS85Bq4zoLo8bHMHNESPLUZW-4_VQaClCLs,51450
+sympy/core/tests/test_kind.py,sha256=NLJbwCpugzlNbaSyUlbb6NHoT_9dHuoXj023EDQMrNI,2048
+sympy/core/tests/test_logic.py,sha256=_YKSIod6Q0oIz9lDs78UQQrv9LU-uKaztd7w8LWwuwY,5634
+sympy/core/tests/test_match.py,sha256=yjVNccCLutvov8Ru_p38Hl_HDS0ltdsWierSi54m5Ts,22714
+sympy/core/tests/test_multidimensional.py,sha256=Fr-lagme3lwLrBpdaWP7O7oPezhIatn5X8fYYs-8bN8,848
+sympy/core/tests/test_noncommutative.py,sha256=IkGPcvLO4ACVj5LMT2IUgyj68F1RBvMKbm01iqTOK04,4436
+sympy/core/tests/test_numbers.py,sha256=SvTVZUpMWfeGvaqyJUp4e9FYIO8lFJLcbRcmBNWO8m0,78001
+sympy/core/tests/test_operations.py,sha256=mRxftKlrxxrn3zS3UPwqkF6Nr15l5Cv6j3c2RJX46s4,2859
+sympy/core/tests/test_parameters.py,sha256=wO9D-LcMMEyf5u5-EmDwVeQ02YzYbYwtFFR_o-M4ybQ,3560
+sympy/core/tests/test_power.py,sha256=H7SFxpWVnF4vgpF95ad-Fo5xHhIvKhtZJuGBg0J2SAk,24862
+sympy/core/tests/test_priority.py,sha256=6QlWz52qWwOU0CknBb4KErs4R87jp4wH3hVSQNAgdVI,3252
+sympy/core/tests/test_random.py,sha256=H58NfH5BYeQ3RIscbDct6SZkHQVRJjichVUSuSrhvAU,1233
+sympy/core/tests/test_relational.py,sha256=7ne7GIG_i0uVT8FfpCJ4ixnuIyQoVy-PwJm3kYNWbKI,43525
+sympy/core/tests/test_rules.py,sha256=iwmMX7hxC_73CuX9BizeAci-cO4JDq-y1sicKBXEGA4,349
+sympy/core/tests/test_singleton.py,sha256=xLJJgXwmkbKhsot_qTs-o4dniMjHUh3_va0xsA5h-KA,3036
+sympy/core/tests/test_sorting.py,sha256=6BZKYqUedAR-jeHcIgsJelJHFWuougml2c1NNilxGZg,902
+sympy/core/tests/test_subs.py,sha256=7ITJFDplgWBRImkcHfjRdnHqaKgjTxWb4j4WoRysvR8,30106
+sympy/core/tests/test_symbol.py,sha256=zYhPWsdyQp7_NiLVthpoCB1RyP9pmJcNlTdTN2kMdfY,13043
+sympy/core/tests/test_sympify.py,sha256=FEOsRduYX3EqE9EhMILwE9wu6jDj_o2WU9XEZHsXFUo,28195
+sympy/core/tests/test_traversal.py,sha256=cmgvMW8G-LZ20ZXy-wg5Vz5ogI_oq2p2bJSwMy9IMF0,4311
+sympy/core/tests/test_truediv.py,sha256=RYfJX39-mNhekRE3sj5TGFZXKra4ML9vGvObsRYuD3k,854
+sympy/core/tests/test_var.py,sha256=hexP-0q2nN9h_dyhKLCuvqFXgLC9e_Hroni8Ldb16Ko,1594
+sympy/core/trace.py,sha256=9WC8p3OpBL6TdHmZWMDK9jaCG-16f4uZV2VptduVH98,348
+sympy/core/traversal.py,sha256=PP4HUp-2gtsoCtGZwqxpbqZ33vtoRX-yYTbIqOQiaR0,8860
+sympy/crypto/__init__.py,sha256=i8GcbScXhIPbMEe7uuMgXqh_cU2mZm2f6hspIgmW5uM,2158
+sympy/crypto/crypto.py,sha256=iAERU5Qf9GizEUMUyxTgTUBI0U9QpSkbRir2nvKIKUA,89685
+sympy/crypto/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/crypto/tests/test_crypto.py,sha256=gPsRBNNHwc4QDBjgT5tvWTCuC5IA5d4aZRTblxXjngk,19758
+sympy/diffgeom/__init__.py,sha256=cWj4N7AfNgrYcGIBexX-UrWxfd1bP9DTNqUmLWUJ9nA,991
+sympy/diffgeom/diffgeom.py,sha256=DgxgxP9B7NT-M1Hb1-1AwHYHBvXZk_Y6_MqZcGPsmOg,72274
+sympy/diffgeom/rn.py,sha256=kvgth6rNJWt94kzVospZwiH53C-s4VSiorktQNmMobQ,6264
+sympy/diffgeom/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/diffgeom/tests/test_class_structure.py,sha256=LbRyxhhp-NnnfJ2gTn1SdlgCBQn2rhyB7xApOgcd_rM,1048
+sympy/diffgeom/tests/test_diffgeom.py,sha256=3BepCr6ned-4C_3me4zScu06HXG9Qx_dBBxIpiXAvy4,14145
+sympy/diffgeom/tests/test_function_diffgeom_book.py,sha256=GwhUAiAPtUv5I9oghdElMhtc6KX184tySgLSpVHhT0Q,5254
+sympy/diffgeom/tests/test_hyperbolic_space.py,sha256=c4xQJ_bBS4xrMj3pfx1Ms3oC2_LwuJuNYXNZxs-cVG8,2598
+sympy/discrete/__init__.py,sha256=A_Seud0IRr2gPYlz6JMQZa3sBhRL3O7gVqhIvMRRvE0,772
+sympy/discrete/convolutions.py,sha256=9L2d2Rrn6jqGfV2lBxCV6LmcTNBZUuOIqP_fuMIzPzk,18341
+sympy/discrete/recurrences.py,sha256=FqU5QG4qNNLSVBqcpL7HtKa7rQOlmHMXDQRzHZ_P_s0,5124
+sympy/discrete/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/discrete/tests/test_convolutions.py,sha256=K4S9bA1E2tg0VcFQ8SYtlxjL6HB666RxD5rriTuAK4k,17856
+sympy/discrete/tests/test_recurrences.py,sha256=s5ZEZQ262gcnBLpCjJVmeKlTKQByRTQBrc-N9p_4W8c,3019
+sympy/discrete/tests/test_transforms.py,sha256=vEORFaPvxmPSsw0f4Z2hLEN1wD0FdyQOYHDEY9aVm5A,5546
+sympy/discrete/transforms.py,sha256=lf-n6IN881uCfTUAxPNjdUaSguiRbYW0omuR96vKNlE,11681
+sympy/external/__init__.py,sha256=C6s4654Elc_X-D9UgI2cUQWiQyGDt9LG3IKUc8qqzuo,578
+sympy/external/gmpy.py,sha256=89vXc1KD5TiQCTwlL-98r01KdqsOsJpzc8XN1R7TwM4,9768
+sympy/external/importtools.py,sha256=Q7tS2cdGZ9a4NI_1sgGuoVcSDv_rIk-Av0BpFTa6EzA,7671
+sympy/external/ntheory.py,sha256=jnm13KGII1p6X42T-u2jVoBxiOCXZ8XGxjzhCuWXthE,16555
+sympy/external/pythonmpq.py,sha256=DBQYdZsCUajqIjV5ECYpzpCSbe4RR9qr4D1x_ZTuZaY,11259
+sympy/external/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/external/tests/test_autowrap.py,sha256=nUWYeQd1QF-Ou4A63iWypsn6pJMi0uMLyba8aRCOhnM,9759
+sympy/external/tests/test_codegen.py,sha256=t991vYFfQC6loodk7Vp1oKTCklBn8SwY7qLshVVou4w,12598
+sympy/external/tests/test_gmpy.py,sha256=8ITpuWYeitCymWwuQLpOOVBmRb3CJsO5sbqiZcVDulE,397
+sympy/external/tests/test_importtools.py,sha256=KrfontKYv11UvpazQ0vS1qyhxIvgZrCOXh1JFeACjeo,1394
+sympy/external/tests/test_ntheory.py,sha256=BJWirDnX7Y7McBoXreMomGQx33YlAjiuBTYQQLhobLU,8881
+sympy/external/tests/test_numpy.py,sha256=tuEji5l6GqbNjv74T6a3e8LDzI2zKUaLzvfluNXOFE0,10291
+sympy/external/tests/test_pythonmpq.py,sha256=L_FdZmmk5N-VEivE_O_qZa98BZhT1WSxRfdmG817bA0,5797
+sympy/external/tests/test_scipy.py,sha256=CVaw7D0-6DORgg78Q6b35SNKn05PlKwWJuqXOuU-qdY,1172
+sympy/functions/__init__.py,sha256=-u5IzcQAPk9emytXfMK22EVXqpXUtSau3pQtevZYmFo,5565
+sympy/functions/combinatorial/__init__.py,sha256=WqXI3qU_TTJ7nJA8m3Z-7ZAYKoApT8f9Xs0u2bTwy_c,53
+sympy/functions/combinatorial/factorials.py,sha256=cYMN1Bz76XninzSxzdLYxTN7-Mt1mzfUcSWIU-qpYAw,39184
+sympy/functions/combinatorial/numbers.py,sha256=PcW4rvWQ66POd1FcL8SJav2iUmGv2Ip_Vbbp_3qYIAY,101088
+sympy/functions/combinatorial/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/functions/combinatorial/tests/test_comb_factorials.py,sha256=B59sfZxTUmKtnG62LW9sPn7HnRxFBP6x_rnbKqbaaZ0,26317
+sympy/functions/combinatorial/tests/test_comb_numbers.py,sha256=GWeIeb9zJo3WJr9NGW7VD-PmCIt13ce-VufICHs1-A8,47593
+sympy/functions/elementary/__init__.py,sha256=Fj8p5qE-Rr1lqAyHI0aSgC3RYX56O-gWwo6wu-eUQYA,50
+sympy/functions/elementary/_trigonometric_special.py,sha256=FvrgSXisxjXjyBC4-NLLya6q2YyTMNMAUqqYzuYl34g,7271
+sympy/functions/elementary/benchmarks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/functions/elementary/benchmarks/bench_exp.py,sha256=PFBYa9eMovH5XOFN5XTxWr1VDj1EBoKwn4mAtj-_DdM,185
+sympy/functions/elementary/complexes.py,sha256=HQm4ViBWN5OkCtJZQPdLdChTnmzbk4Cb2A--MUzTgUs,44168
+sympy/functions/elementary/exponential.py,sha256=GoDlUBPcmpRjYehRZOooF0bISSK_oxpJlkqaE_KNTeQ,42610
+sympy/functions/elementary/hyperbolic.py,sha256=zewRSHNzxyvSFD9sP9XBKX-evG6Rds4k9jAAcfmDbIk,69329
+sympy/functions/elementary/integers.py,sha256=Dc5Bq6ds85NJYWkfHJPJccUeVTxbhzVloTeMo5HGG98,22394
+sympy/functions/elementary/miscellaneous.py,sha256=cPpv71po3HCQGsWIHgcQEql6RNb4Y3RrL24ujgtdTIQ,27944
+sympy/functions/elementary/piecewise.py,sha256=Nv32I9lktMPHF3k0JpifbcgCgIz7CyQ8KwI_t1BaVsI,58285
+sympy/functions/elementary/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/functions/elementary/tests/test_complexes.py,sha256=BaPIrUSkoguP74x7uLSt9bA9kxmB58fJ4wgNtx5RnCg,34016
+sympy/functions/elementary/tests/test_exponential.py,sha256=ojjC0aafWaQegZVYV7bgxyvWAES_erpeYTb3R0m9V5I,29733
+sympy/functions/elementary/tests/test_hyperbolic.py,sha256=TBJZbiV-o9c2uVUqnkaRuHr5FdSGBOpqKK5_2bWg92s,56898
+sympy/functions/elementary/tests/test_integers.py,sha256=_QaECu3JQRQa9uPi10jXlu9AohvtLTKB4rSe920g0tY,23266
+sympy/functions/elementary/tests/test_interface.py,sha256=dW0aHvLR5UPyyXzBS-tgKrQIp-H6Vu8sKjlQrBQgtZ4,2513
+sympy/functions/elementary/tests/test_miscellaneous.py,sha256=eCL30UmsusBhjvqICQNmToa1aJTML8fXav1L1J6b7FU,17148
+sympy/functions/elementary/tests/test_piecewise.py,sha256=b1c0IMbXHDLjbi_ESs7fQhJiMaEro30GEu9XYreicYA,62876
+sympy/functions/elementary/tests/test_trigonometric.py,sha256=ebRorj-WThNp56qvZOAGkvgpvWFnIlB6kFZyV1gCii4,90340
+sympy/functions/elementary/trigonometric.py,sha256=b2mE2SpNj0LZM3AcipW0FrjturAi9ozijK3PhqyfCbM,115945
+sympy/functions/special/__init__.py,sha256=5pjIq_RVCMsuCe1b-FlwIty30KxoUowZYKLmpIT9KHQ,59
+sympy/functions/special/benchmarks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/functions/special/benchmarks/bench_special.py,sha256=wzAoKTccuEaG4xrEYTlYfIJuLi3kUTMTEJ9iA113Wog,164
+sympy/functions/special/bessel.py,sha256=r-jJETBLp8a4JOKV9dW5EWr_yvERL0HMn0su328bug8,68656
+sympy/functions/special/beta_functions.py,sha256=de-ypTWZEjvFkq9RgPq3ghGOvFOfPmb9JfUDAPKQoXk,12777
+sympy/functions/special/bsplines.py,sha256=q7iZSeEMlRrx5gpXOzlk4uSTPIollduNZuGwMHB6fRM,10060
+sympy/functions/special/delta_functions.py,sha256=jopb1BX803pZtDAtLmKfFqRVjo3mySekyC80Lu-sJkM,19887
+sympy/functions/special/elliptic_integrals.py,sha256=YpCU-xyTvrmBxdO7Rj2pNYDbS2-bGfCvCaOHon9nSq0,14921
+sympy/functions/special/error_functions.py,sha256=GvKrm5fVMpat0Iierh9UsxsTeQknJyiHgMrWS-e7fhw,79556
+sympy/functions/special/gamma_functions.py,sha256=gkPQdMNR7k5uY1vSUu6zzyoJKMoQZfC90YjLRcyft5c,42943
+sympy/functions/special/hyper.py,sha256=oqeAhFALAN_H6eS0Fs_axdfL9jIYQ-aRFGGgQky2HYk,38829
+sympy/functions/special/mathieu_functions.py,sha256=kFmt0RUA70vhGphI7lhwUsOQ7KIy8Y95DU6gGkVQU5Y,6620
+sympy/functions/special/polynomials.py,sha256=IdxRf8D1iFA-oYfLyTcUmsSCeuWja4h2ExNb8UShwxg,46753
+sympy/functions/special/singularity_functions.py,sha256=ounzMgFQiOW-e1Rb5y_dCSopJGegXMdkj-EpjrP8tzU,8346
+sympy/functions/special/spherical_harmonics.py,sha256=MNAJ4ABTy_EIZe2XSK_MZ1SPnk1OeDnJAtrICAst4yw,11018
+sympy/functions/special/tensor_functions.py,sha256=UNoDpLu1EBR-2tX_dLoRpKhN0Yhm-z_hh8EtW50dNSE,12303
+sympy/functions/special/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/functions/special/tests/test_bessel.py,sha256=c6OKhj4MCjv4kpDG7udueaCOLykblsKHMOyP4CdFToA,37373
+sympy/functions/special/tests/test_beta_functions.py,sha256=yxfgu-wmNEeMfaFABiDHYmuZpZup9FTp0ZYerlc6hhc,3786
+sympy/functions/special/tests/test_bsplines.py,sha256=6UYg7IqXTi8fcSOut8TEzNVkxIA4ff-CyG22qJnbIYA,7145
+sympy/functions/special/tests/test_delta_functions.py,sha256=8xhSWG4SLL86z1QKFfLk_3b--bCrxjvCaxHlODBVToE,7138
+sympy/functions/special/tests/test_elliptic_integrals.py,sha256=scu7KemJ7Q2nsRcZtckQNruuthI-vla9M1sDVkLcbKM,6991
+sympy/functions/special/tests/test_error_functions.py,sha256=TkhkIKiuRZS78-F4ZTt5rvr3hfknp654IEhmHhRXT48,33565
+sympy/functions/special/tests/test_gamma_functions.py,sha256=aveGPdG1X9OdlC6xpdZbSsJ3S4M4DaG2osyOhm1fAYo,29908
+sympy/functions/special/tests/test_hyper.py,sha256=SEnQ9TtyO_dSQRc94AHrbQai6Q7-tmRfDIh40gNE3FE,16694
+sympy/functions/special/tests/test_mathieu.py,sha256=pqoFbnC84NDL6EQkigFtx5OQ1RFYppckTjzsm9XT0PY,1282
+sympy/functions/special/tests/test_singularity_functions.py,sha256=GEXzHHeR5F0swrZzhF7pEGR0tk9Vp3Ve2c-sE4WSpsY,6249
+sympy/functions/special/tests/test_spec_polynomials.py,sha256=wuiZaR_LwaM8SlNuGl3B1p4eOHC_-zZVSXMPNfzKRB4,19561
+sympy/functions/special/tests/test_spherical_harmonics.py,sha256=pUFtFpNPBnJTdnqou0jniSchijyh1rdzKv8H24RT9FU,3850
+sympy/functions/special/tests/test_tensor_functions.py,sha256=bblSDkPABZ6N1j1Rb2Bb5TZIzZoK1D8ks3fHizi69ZI,5546
+sympy/functions/special/tests/test_zeta_functions.py,sha256=2r59_aC0QOXQsBNXqxsHPr2PkJExusI6qvSydZBPbfw,10474
+sympy/functions/special/zeta_functions.py,sha256=QPRYVp0-WKs02CUSQiRIJUWD4N_3PmqrOsk1MGzBUfU,24091
+sympy/galgebra.py,sha256=yEosUPSnhLp9a1NWXvpCLoU20J6TQ58XNIvw07POkVk,123
+sympy/geometry/__init__.py,sha256=BU2MiKm8qJyZJ_hz1qC-3nFJTPEcuvx4hYd02jHjqSM,1240
+sympy/geometry/curve.py,sha256=F7b6XrlhUZ0QWLDoZJVojWfC5LeyOU-69OTFnYAREg8,10170
+sympy/geometry/ellipse.py,sha256=So0gH9e-1Oh3HGzrXoLqANyP4lTTBfGHHVo5vCdbfdk,50305
+sympy/geometry/entity.py,sha256=fvHhtSb6RvE6v-8yMyCNvm0ekLPoO7EO9J8TEsGyQGU,20668
+sympy/geometry/exceptions.py,sha256=XtUMA44UTdrBWt771jegFC-TXsobhDiI-10TDH_WNFM,131
+sympy/geometry/line.py,sha256=Yw8ns_w8rn6FEak6MmfsdccgNNjMQHhodDik2UX7jJM,80397
+sympy/geometry/parabola.py,sha256=YhtQ9C5Yco_iSQ6ZYRs0lZBWHmDkcvKUr-Y8RghY3M8,10716
+sympy/geometry/plane.py,sha256=z3hjEf3ibO735DV0Mb4DVjvthYlDyiPfekCeePSCDMM,26770
+sympy/geometry/point.py,sha256=vQB40V1fvhZze3D_D54HlMSS8gXeMIWerd2hBozmPFA,36661
+sympy/geometry/polygon.py,sha256=aooJyJVwf6ZPuxStYgTc-2jNjVaM2YHSvpVY3XRjAuo,82027
+sympy/geometry/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/geometry/tests/test_curve.py,sha256=xL4uRWAal4mXZxuQhcs9QOhs6MheCbFNyH1asq_a2IQ,4479
+sympy/geometry/tests/test_ellipse.py,sha256=f-EorUkNl_cg63bDvm4Qrbe1PWrBaCCRg8jAmdIYBuU,26509
+sympy/geometry/tests/test_entity.py,sha256=KSsncs7afLpqUAYTs9b0VH1dt6FhPrX5rrj2rdLpBUo,3896
+sympy/geometry/tests/test_geometrysets.py,sha256=vvOWrFrJuNAFgbrVh1wPY94o-H-85FWlnIyyo2Kst9c,1911
+sympy/geometry/tests/test_line.py,sha256=fLWGfHQNC3YqjxtT7zQbP--Cu0mjcPwSBfji4DA7BP0,38054
+sympy/geometry/tests/test_parabola.py,sha256=kd0RU5sGOcfp6jgwgXMtvT2B6kG1-M3-iGOLnUJfZOw,6150
+sympy/geometry/tests/test_plane.py,sha256=QRcfoDsJtCtcvjFb18hBEHupycLgAT2OohF6GpNShyQ,12525
+sympy/geometry/tests/test_point.py,sha256=YKXQdlBTQWsIVf9l3G6iPMq0OqMTo8nZlibhJDsq_Ic,16410
+sympy/geometry/tests/test_polygon.py,sha256=4D0t98SELQF42dYR-CbFqDU5yJds0LAE2RE8xqEdqag,27602
+sympy/geometry/tests/test_util.py,sha256=sbh1QvkQG1OqvE-kt4fNNIkMWnOFi5EpaBmnZS3pzNc,7044
+sympy/geometry/util.py,sha256=zF6Xx0ciab6Rxm4ekK5RawWXPWAk5oug8eDvgfNKoRc,20671
+sympy/holonomic/__init__.py,sha256=BgHIokaSOo3nwJlGO_caJHz37n6yoA8GeM9Xjn4zMpc,784
+sympy/holonomic/holonomic.py,sha256=vP5XabVIezsAURbK65p8P_QfPINQwhol0d62B7Br3s0,91887
+sympy/holonomic/holonomicerrors.py,sha256=qDyUoGbrRjPtVax4SeEEf_o6-264mASEZO_rZETXH5o,1193
+sympy/holonomic/numerical.py,sha256=MlP8xTBqjVQqM1-jwviS_yPICKj6tEVsa9f6wyMCSfo,2625
+sympy/holonomic/recurrence.py,sha256=HWSMokhjcdGTLW_oku0Xi8W8RHxWYS28bv8V7eubC58,10377
+sympy/holonomic/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/holonomic/tests/test_holonomic.py,sha256=NuoJtJi3RlpMYhMU_bGH1tuFuFoOZZrrSdMpgdHGhf0,35333
+sympy/holonomic/tests/test_recurrence.py,sha256=qHv0kn1Q4-aCD7XmbDK2xIdkjF0XkeZUKD2yeLajiq0,1342
+sympy/integrals/__init__.py,sha256=pZ-C3tDP_8woKActNoS7IwyW-9AuB5PMHB5B0ohlpw8,1970
+sympy/integrals/benchmarks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/integrals/benchmarks/bench_integrate.py,sha256=vk6wAO1bqzFT9oW4qsW7nKGfc_gP0XaB5PMYKx5339Q,396
+sympy/integrals/benchmarks/bench_trigintegrate.py,sha256=8XU3uB3mcavigvzHQZA7H1sHI32zgT-9RkSnLa-Y3Vc,305
+sympy/integrals/deltafunctions.py,sha256=ysIQLdRBcG_YR-bVDoxt-sxEVU8TG77oSgM-J0gI0mE,7435
+sympy/integrals/heurisch.py,sha256=Huq3dBJEXvYGzkBboXnG6xFCGorc5Uftf5ssbiltyIs,26706
+sympy/integrals/integrals.py,sha256=DV_ditazAyVBpUCzsUFNiYvvQRqJfx3UGjnSEB2HKcg,64887
+sympy/integrals/intpoly.py,sha256=SXjd_f295YrYsvoQpzE2EQM5xaQnnj0zvHdYW5KEdn0,43266
+sympy/integrals/laplace.py,sha256=y5NxZ3_nNtc-NETYWw4iha7LTVM9Ok9NWPtmMUXtZwg,87038
+sympy/integrals/manualintegrate.py,sha256=sO_5-9pbeRKhZSuEwXm6okxpvRsbpQZerbLBdRdDtp4,75721
+sympy/integrals/meijerint.py,sha256=yOsrHJA7doo1uDsmsiTqK2Tly4JF30sA5RElT92lfec,80775
+sympy/integrals/meijerint_doc.py,sha256=Sis9bZvwciU2_BZzr3GCvXLkBcXVUhP9ycqGIDu99xU,1204
+sympy/integrals/prde.py,sha256=3VqBQOX7SZv_lETmhoVgb9gRjb1z8hiZAJ6vv5AgNEo,52073
+sympy/integrals/quadrature.py,sha256=6Bg3JmlIjIduIfaGfNVcwNfSrgEiLOszcN8WPzsXNqE,17064
+sympy/integrals/rationaltools.py,sha256=ZAizq9unSt-_wQkCIAhl_FaemzbQcfBcE9A3mDiU788,11694
+sympy/integrals/rde.py,sha256=EKXK6bf4m-2O0NZ-rtHq0Ye2a_o9LmUrkoV8gNxZKVk,27393
+sympy/integrals/risch.py,sha256=4UsTHizZtAMe3VUmRHugdOuPMilw8sKiVOlSUMWj9oE,67352
+sympy/integrals/singularityfunctions.py,sha256=ONI8x-ed-IcqOF4K2l0LVUvEUN2_dHztvL4auRsi67U,2235
+sympy/integrals/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/integrals/tests/test_deltafunctions.py,sha256=ivFjS-WlLQ4aMqjVS7ZzMChP2Mmw_JUPnwI9otiLnvs,3709
+sympy/integrals/tests/test_failing_integrals.py,sha256=HKMA6O26exCIRWQ43KyKugOvJL3gOei3r9JObcKo8p0,7438
+sympy/integrals/tests/test_heurisch.py,sha256=-btpZS_LI9EO-2EmA0eHV6l7xykQjkHIcpDU_GziTlo,14299
+sympy/integrals/tests/test_integrals.py,sha256=8bVIN4ztTwOvpLrlDIk1SURQMtiNXoqu-LLMkZp-Wrk,81192
+sympy/integrals/tests/test_intpoly.py,sha256=NzGhkR2pUMfd8lIU2cFR9bFa0J89RzpHs3zDggAWtXo,37445
+sympy/integrals/tests/test_laplace.py,sha256=pWn5UEm15gJv_hD35lvJh03Uuw4o79rbCQYvbXQS2xk,38186
+sympy/integrals/tests/test_lineintegrals.py,sha256=zcPJ2n7DYt9KsgAe38t0gq3ARApUlb-kBahLThuRcq8,450
+sympy/integrals/tests/test_manual.py,sha256=unQ6Ew81SCHvVMva_aAOndh7hWpLeDwBtXs6W-yC0jE,34551
+sympy/integrals/tests/test_meijerint.py,sha256=G22dppQGMFU3JGexHZs65nce8UAK9wTilZn0D1XvZu0,32594
+sympy/integrals/tests/test_prde.py,sha256=2BZmEDasdx_3l64-9hioArysDj6Nl520GpQN2xnEE_A,16360
+sympy/integrals/tests/test_quadrature.py,sha256=iFMdqck36gkL-yksLflawIOYmw-0PzO2tFj_qdK6Hjg,19919
+sympy/integrals/tests/test_rationaltools.py,sha256=7QiPnpBXl7lo32RmhXo7ED6FYj5I1gjEFOziJYlqPtI,5669
+sympy/integrals/tests/test_rde.py,sha256=4d3vJupa-hRN4yNDISY8IC3rSI_cZW5BbtxoZm14y-Y,9571
+sympy/integrals/tests/test_risch.py,sha256=HaWg0JnErdrNzNmVfyz2Zz4XAgZPVVpZPt6Map3sQ58,38630
+sympy/integrals/tests/test_singularityfunctions.py,sha256=CSrHie59_NjNZ9B2GaHzKPNsMzxm5Kh6GuxlYk8zTuI,1266
+sympy/integrals/tests/test_transforms.py,sha256=mLl5RfuENf11oenV1OXgg6TfWdQTEY23txiGl7grCyo,27152
+sympy/integrals/tests/test_trigonometry.py,sha256=moMYr_Prc7gaYPjBK0McLjRpTEes2veUlN0vGv9UyEA,3869
+sympy/integrals/transforms.py,sha256=_rwRkrOnV4zby27MZJ9dpJrtQFG4gTbL7naLpFLXKDo,51750
+sympy/integrals/trigonometry.py,sha256=iOoBDGFDZx8PNbgL3XeZEd80I8ro0WAizNuC4P-u8x0,11083
+sympy/interactive/__init__.py,sha256=yokwEO2HF3eN2Xu65JSpUUsN4iYmPvvU4m_64f3Q33o,251
+sympy/interactive/printing.py,sha256=EZBO3YCga2dXBI1EfFHVDmUKBkF9UmrLMgwI76B0nyE,21562
+sympy/interactive/session.py,sha256=sG546e0mAtT0OrFkYNVM7QGvkWrDhAQZ5E1hfx03iBQ,15329
+sympy/interactive/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/interactive/tests/test_interactive.py,sha256=Pbopy9lODrd_P46_xxlWxLwqPfG6_4J3CWWC4IqfDL4,485
+sympy/interactive/tests/test_ipython.py,sha256=QZUvr77TxJHjdgQVNpihAuoglg1ghVHOTDFOVXJ9pcE,11797
+sympy/interactive/traversal.py,sha256=XbccdO6msNAvrG6FFJl2n4XmIiRISnvda4QflfEPg7U,3189
+sympy/liealgebras/__init__.py,sha256=K8tw7JqG33_y6mYl1LTr8ZNtKH5L21BqkjCHfLhP4aA,79
+sympy/liealgebras/cartan_matrix.py,sha256=yr2LoZi_Gxmu-EMKgFuPOPNMYPOsxucLAS6oRpSYi2U,524
+sympy/liealgebras/cartan_type.py,sha256=xLklg8Y5s40je6sXwmLmG9iyYi9YEk9KoxTSFz1GtdI,1790
+sympy/liealgebras/dynkin_diagram.py,sha256=ZzGuBGNOJ3lPDdJDs4n8hvGbz6wLhC5mwb8zFkDmyPw,535
+sympy/liealgebras/root_system.py,sha256=n1TKAZyWDPSlUDmjAga9Sa2Cm9qeRXddYPbl2XKyFq4,6673
+sympy/liealgebras/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/liealgebras/tests/test_cartan_matrix.py,sha256=KCsakn0fHKHRbIUcrUkHBIKkudl3_ISUdHrfJy-UOd4,303
+sympy/liealgebras/tests/test_cartan_type.py,sha256=t5PvYYDXbNIFL3CV59Je7SBIAeLLf-W3mOINPUoHK6E,339
+sympy/liealgebras/tests/test_dynkin_diagram.py,sha256=DSixbnt_yd0zrhKzXW_XqkXWXYe1Dk2MmXN-Rjb1dGg,260
+sympy/liealgebras/tests/test_root_system.py,sha256=YmGBdUeJ4PkLSfAfRgTF7GW62RCEd5nH27FSX9UaG5Q,927
+sympy/liealgebras/tests/test_type_A.py,sha256=x7QmpjxsGmXol-IYVtN1lmIOmM3HLYwpX1tSG5h6FMM,657
+sympy/liealgebras/tests/test_type_B.py,sha256=Gw0GP24wP2rPn38Wwla9W7BwWH4JtCGpaprZb5W6JVY,642
+sympy/liealgebras/tests/test_type_C.py,sha256=ysSy-vzE9lNwzAunrmvnFkLBoJwF7W2On7QpqS6RI1s,927
+sympy/liealgebras/tests/test_type_D.py,sha256=qrO4oCjrjkp1uDvrNtbgANVyaOExqOLNtIpIxD1uH0U,764
+sympy/liealgebras/tests/test_type_E.py,sha256=IQ46Bo75Kivz0O45mNKNvDO8jen7X2NwxVBTwvr5lQo,987
+sympy/liealgebras/tests/test_type_F.py,sha256=yUQJ7LzTemv4Cd1XW_dr3x7KEI07BahsWAyJfXLS1eA,1378
+sympy/liealgebras/tests/test_type_G.py,sha256=wVa6qcAHbdrc9dA63samexHL35cWWJS606pom-6mH2Q,548
+sympy/liealgebras/tests/test_weyl_group.py,sha256=HrzojRECbhNUsdLFQAXYnJEt8LfktOSJZuqVE45aRnc,1501
+sympy/liealgebras/type_a.py,sha256=Etn_aeyE5FdYrUXrGnjNiTL5k4r5-O_CysaO-_UKASA,4294
+sympy/liealgebras/type_b.py,sha256=ERH1aM38eHZUuTJYRsWLKFDm74G4y1MQtweSfkDFQYc,4547
+sympy/liealgebras/type_c.py,sha256=Al1pA5cw5IKjvBzx9LdMORd5-JZXgA0kaMv8aHhnYJ0,4426
+sympy/liealgebras/type_d.py,sha256=Cagn4GXrYuUP32W6jauIPnEv8IcD3H88sbn0fKS7pmU,4681
+sympy/liealgebras/type_e.py,sha256=KR84bp4Ga7d9itKRCsD3AYrarVewGiNNe7AHWNUfTAM,8280
+sympy/liealgebras/type_f.py,sha256=gIyk4zA04uIpwizcYdvKsZ_xkcpPLId6LIHR6tp38dk,4423
+sympy/liealgebras/type_g.py,sha256=Ife98dGPtarGd-ii8hJbXdB0SMsct4okDkSX2wLN8XI,2965
+sympy/liealgebras/weyl_group.py,sha256=5YFA8qC4GWDM0WLNR_6VgpuNFZDfyDA7fBFjBcZaLgA,14557
+sympy/logic/__init__.py,sha256=RfoXrq9MESnXdL7PkwpYEfWeaxH6wBPHiE4zCgLKvk0,456
+sympy/logic/algorithms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/logic/algorithms/dpll.py,sha256=zqiZDm1oD5sNxFqm_0Hen6NjfILIDp5uRgEOad1vYXI,9188
+sympy/logic/algorithms/dpll2.py,sha256=PEVvFUrEf-IwTwfnTYtAFLPLPjjqA1nmm39nASgsM1I,21497
+sympy/logic/algorithms/lra_theory.py,sha256=6JZAZMJhhHSbUXZuc0ddYwp_x7efVuthIYV7Dc9eUyc,31769
+sympy/logic/algorithms/minisat22_wrapper.py,sha256=uINcvkIHGWYJb8u-Q0OgnSgaHfVUd9tYYFbBAVNiASo,1317
+sympy/logic/algorithms/pycosat_wrapper.py,sha256=0vNFTbu9-YhSfjwYTsZsP_Z4HM8WpL11-xujLBS1kYg,1207
+sympy/logic/algorithms/z3_wrapper.py,sha256=mFmf7DWDV0Zu7006EdFK4qEDTP7sfmcXrfGZkMK97vo,3747
+sympy/logic/boolalg.py,sha256=GqxLtqUangGoSjsncKDzlLEE7iYVe5qsMtPhztniI9c,114972
+sympy/logic/inference.py,sha256=J2D8t9iHCSdotzS9iq6g3EvLPsI2B10kiNbmHsIz_oY,8983
+sympy/logic/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/logic/tests/test_boolalg.py,sha256=0ZjG56WpIhIwFLedUzg5IC-c0VF_g9qoU9aAbFydPYU,49748
+sympy/logic/tests/test_dimacs.py,sha256=EK_mA_k9zBLcQLTOKTZVrGhnGuQNza5mwXDQD_f-X1c,3886
+sympy/logic/tests/test_inference.py,sha256=9BXfPbJs6sBEgx1nwkmg2HcLl82lcfFXngKJL_ByzUI,16116
+sympy/logic/tests/test_lra_theory.py,sha256=hNCH66hP_32x5ioyqM6ltd0DbEl72kdctCBu-H6egG0,16834
+sympy/logic/utilities/__init__.py,sha256=WTn2vBgHcmhONRWI79PdMYNk8UxYDzsxRlZWuc-wtNI,55
+sympy/logic/utilities/dimacs.py,sha256=gfrGWQpn4F7gLJH1zHAnLB8-CMGa1XtXl9NCGhwAW9k,1671
+sympy/matrices/__init__.py,sha256=i2a37WlcCj8-AKG_Yy8BBdOHFgAuWois_2IcD_Ih00s,2634
+sympy/matrices/benchmarks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/matrices/benchmarks/bench_matrix.py,sha256=vGMlg-2il2cFeAWrf0NJ6pzPX3Yd3ZQMxFgQ4q5ILQE,306
+sympy/matrices/common.py,sha256=v_jMjH9t6mrHl7Z5QluhO-G07QUaJSdiioF2YC1xlhY,95395
+sympy/matrices/decompositions.py,sha256=5OjwFe5Z_eM2XoZPqrNFY9-Esq3cO_8yqFxT9QBPHgQ,47865
+sympy/matrices/dense.py,sha256=HTPNMKbi2M0W0nHImNtHib9JM5VC8si99ldL_FylKwQ,30461
+sympy/matrices/determinant.py,sha256=pDTDYCLBSWJov_j_B-ZZHdqH3-WQ_uwhZawrX3EDE9k,34550
+sympy/matrices/eigen.py,sha256=v09mqb2hZ0HqGbGzYpELOatIHnGTP5XADE-1RGcua-g,39811
+sympy/matrices/exceptions.py,sha256=8diN_ojMGC93XpqvZeS5ow4crlFxSkm6WqtnIK5j81E,503
+sympy/matrices/expressions/__init__.py,sha256=IMqXCSsPh0Vp_MC9HZTudA5DGM4WBq_yB-Bst0azyM8,1692
+sympy/matrices/expressions/_shape.py,sha256=TyQSwGx41aaMyAYs5Q7Er6atKVAdWK7DJ6YIVsiEAZg,3062
+sympy/matrices/expressions/adjoint.py,sha256=HV4OIWgmS2f_1_4PHRhQpgNmKJVCoX0eyHmrM6gfN5g,1515
+sympy/matrices/expressions/applyfunc.py,sha256=8scpWjZp7yzuvUNr0mxN03KgyTzLRa5NkGl9s81YPgY,6751
+sympy/matrices/expressions/blockmatrix.py,sha256=TPR1ZDTSzVGtT-Qo23XNzjnDVl1Js8NCi_2qV3fQA9U,31643
+sympy/matrices/expressions/companion.py,sha256=lXUJRbjQR6e1mdHQdJwNIJXMW80XmKbOVqNvUXjB57U,1705
+sympy/matrices/expressions/determinant.py,sha256=RyQXgUgqJkv_rvUPrn1_rOY45wzp5zYH6ZOf9S8NK8s,3281
+sympy/matrices/expressions/diagonal.py,sha256=XHWoT-Jv5QwJVsGNbfxHnNG2sygPy1CeR_t6zr8oUoM,6328
+sympy/matrices/expressions/dotproduct.py,sha256=sKdUhwVKTB3LEvd8xMwCDexNoQ1Dz43DCYsmm3UwFWw,1911
+sympy/matrices/expressions/factorizations.py,sha256=zFNjMBsJqhsIcDD8Me4W8-Q-TV89WptfG3Dd9yK_tPE,1456
+sympy/matrices/expressions/fourier.py,sha256=dvaftgB9jgkR_8ETyhzyVLtf1ZJu_wQC-ZbpTYMXZGE,2094
+sympy/matrices/expressions/funcmatrix.py,sha256=q6R75wLn0UdV4xJdVJUrNaofV1k1egXLLQdBeZcPtiY,3520
+sympy/matrices/expressions/hadamard.py,sha256=feXSZy0vbqxTzg0JeLmkSegiF4T2v5dOdcv0UQczK38,13920
+sympy/matrices/expressions/inverse.py,sha256=4UwgHWSIHgEoKOniObkClMYN9DrO2xNyvOSVToXSpj8,2963
+sympy/matrices/expressions/kronecker.py,sha256=kzCHqXDtcZGVQPln521lfN5redNwj6IjXJwjbv_Dkhg,13404
+sympy/matrices/expressions/matadd.py,sha256=0MSanal1HKVEuCBEpehKwfUX4fuM9UMy6Fg2H5noA0s,4773
+sympy/matrices/expressions/matexpr.py,sha256=nfV9MhDNBR9HkOP7zoXOtv9wWR4OFxNczprik4S3Uh8,27549
+sympy/matrices/expressions/matmul.py,sha256=n9LcrgYoEhEQuq2htALHuboIWN0p8P_3lZdKNo92Rf0,15509
+sympy/matrices/expressions/matpow.py,sha256=puuYB49wr1WzFxhT9DcTWtF2bGDFWcbJ7oh14ATQARs,5140
+sympy/matrices/expressions/permutation.py,sha256=Xe7yOx-EgeD6JrqWc4L-ApdN-3ZiV8XS_LQPmc1lhGw,8050
+sympy/matrices/expressions/sets.py,sha256=3zase_rDn2QdaXETX78BgkfKiWcRC7FmwVjjIU-WmdY,2033
+sympy/matrices/expressions/slice.py,sha256=aNdY1Ey4VJR-UCvoORX2kh2DmA6QjOp-waENvWg8WVE,3355
+sympy/matrices/expressions/special.py,sha256=hywkygOQjcJEGQn2fG4dF8oUqXL3N42U1TxbdJr4-6E,7499
+sympy/matrices/expressions/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/matrices/expressions/tests/test_adjoint.py,sha256=cxOc334yNSI9MazhG9HT8s1OCXjkDWr3Zj2JnyHS3Z4,1065
+sympy/matrices/expressions/tests/test_applyfunc.py,sha256=mxTJaoB4Ze50lk-2TgVopmrrbuQbEqUsZwc3K1H8w-Q,3522
+sympy/matrices/expressions/tests/test_blockmatrix.py,sha256=ANNR7e2eiGIvabMNezxRVzipmA8oUwmDTrBTz5ALMzU,16541
+sympy/matrices/expressions/tests/test_companion.py,sha256=Lam6r-cSOokjhSlJws55Kq-gL5_pHfeV_Xuvmn5PkRU,1657
+sympy/matrices/expressions/tests/test_derivatives.py,sha256=9mBeaAZDX7-JbYs6tMClNuGDygETVN_dCXSlHmyAhwg,15991
+sympy/matrices/expressions/tests/test_determinant.py,sha256=JSgptLz9KNC4_X27qnuFq-rscgHk6144s5TEUQpLxr0,2067
+sympy/matrices/expressions/tests/test_diagonal.py,sha256=3L6Vs_Yr36a8dgIqAeIcNEf0xcVyeyGhANNu0dlIpwI,4516
+sympy/matrices/expressions/tests/test_dotproduct.py,sha256=Zkv2N6oRPm0-sN4PFwsVFrM5Y_qv4x2gWqQQQD86hBY,1171
+sympy/matrices/expressions/tests/test_factorizations.py,sha256=6UPA_UhCL5JPbaQCOatMnxhGnQ-aIHmb3lXqbwrSoIE,786
+sympy/matrices/expressions/tests/test_fourier.py,sha256=0eD69faoHXBcuQ7g2Q31fqs-gyR_Xfe-gv-7DXhJh_c,1638
+sympy/matrices/expressions/tests/test_funcmatrix.py,sha256=uN9r0ECMIBqsIzOezg_n9uDYNs6ebYS8Yf5yexUjmAM,2230
+sympy/matrices/expressions/tests/test_hadamard.py,sha256=rR0l1howrI8SaJOnLb0fsCXS5cIx1rzahwFTfGldp3Y,4614
+sympy/matrices/expressions/tests/test_indexing.py,sha256=wwYQa7LNlzhBA5fU50gPyE8cqaJf0s3O70PUx4eNCEA,12038
+sympy/matrices/expressions/tests/test_inverse.py,sha256=n4gwv-GH0LPXZDVgzEB0lA_fk8MmNFK_BVNJb0FEUfY,2320
+sympy/matrices/expressions/tests/test_kronecker.py,sha256=e5H6av3ioOn8jkjyDBrT3NEmCkyHbN6ZEHOlyB9OYLk,5366
+sympy/matrices/expressions/tests/test_matadd.py,sha256=U1fL5YLP_cYEOsdi2uaGGrzm8qOsKcXn69BC1UV6RMM,1866
+sympy/matrices/expressions/tests/test_matexpr.py,sha256=pQkxhi8okFDbF6Uea-dbeRrjNfmlc6nzJdIXOJxAqUI,18441
+sympy/matrices/expressions/tests/test_matmul.py,sha256=2ofS4YHHIcI9DamZNU-8RhBQTnNogqGknKxqkyKXO8U,6187
+sympy/matrices/expressions/tests/test_matpow.py,sha256=dRbwvZ3vxwnqw09lnRqOu475f46xDPNo7V3oc1d_P2U,7308
+sympy/matrices/expressions/tests/test_permutation.py,sha256=93Cqjj2k3aoR3ayMJLdJUa5h1u87bRRxT3I8B4FQsvU,5607
+sympy/matrices/expressions/tests/test_sets.py,sha256=DfFGe6W1ppUs6bgo3vB3DSJvFemrT68s0F3QbyoIJiE,1408
+sympy/matrices/expressions/tests/test_slice.py,sha256=C7OGAQQTz0YZxZCa7g0m8_0Bqq8jaPRa22JHVSqK7tY,2027
+sympy/matrices/expressions/tests/test_special.py,sha256=Mhg71vnjjb4fm0jZgjDoWW8rAJMBeh8aDCM75gjEpKQ,6496
+sympy/matrices/expressions/tests/test_trace.py,sha256=fRlrw9CfdO3z3SI4TQb1fCUb_zVAndbtyOErEeCTCQ0,3383
+sympy/matrices/expressions/tests/test_transpose.py,sha256=P3wPPRywKnrAppX6gssgD66v0RIcolxqDkCaKGGPVcM,1987
+sympy/matrices/expressions/trace.py,sha256=skr53LvstLV5Yg9hkaRb0yWrTdxC9u95G-YzIc80aTs,5362
+sympy/matrices/expressions/transpose.py,sha256=QGQ1bgqvYmRNs6QiVolhtFlbluPYpwW3UNvkRZUqUHU,2645
+sympy/matrices/graph.py,sha256=4UBv9SI5Z8Xjc5jPJVOoPVGQDtfbyOmS8f0ENrPOuU8,9080
+sympy/matrices/immutable.py,sha256=okpJZ41FnaHp1PpwnPNCnB_o3afAU7DgRsr2NqBKtvg,5530
+sympy/matrices/inverse.py,sha256=aVjDn_SjZUfi-jdM_1uJ6u6lVItuvbnb3CppayHQ-Gs,13166
+sympy/matrices/kind.py,sha256=EJxDdD4gFgvVfC3lRex-bczhcGjwBhglf1hPDk2WzXE,2843
+sympy/matrices/matrices.py,sha256=iqgi7x7cjnLT6OuRp6TYjoObJtUtbcPpeGTOC-Enh9Q,23536
+sympy/matrices/matrixbase.py,sha256=snpv-ZBFi-CRtfMwY9lukSTPpELVXmJF16Gn8xPV3yI,165154
+sympy/matrices/normalforms.py,sha256=mX19wvJqQAirRiPY-WdSfEdljFOhst18LcDX4MvPM8A,4636
+sympy/matrices/reductions.py,sha256=MZ8LtryawSm1jXsWzRPsYBmir867lRUWX4LFNGVIHwI,12500
+sympy/matrices/repmatrix.py,sha256=d2vne9qNn1cu7dQbCdkftMPadA87evGMaoE7LkxaYXE,29914
+sympy/matrices/solvers.py,sha256=kWkUdy0XoWbn7RY4S1N0qNgxKAcUrHiTlicb25rR9z4,25163
+sympy/matrices/sparse.py,sha256=ER4tfnpvAMeockzWi10mKdKZJSYJNPISyoUL_m29UJ8,14673
+sympy/matrices/sparsetools.py,sha256=tzI541P8QW_v1eVJAXgOlo_KK1Xp6u1geawX_tdlBxY,9182
+sympy/matrices/subspaces.py,sha256=uLo4qnP0xvFcFo5hhf6g7pHSHiRbcQ1ATDKwGBxW7CE,3761
+sympy/matrices/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/matrices/tests/test_commonmatrix.py,sha256=CEYYa5frGDGPAVz78uYVTBC3IHoQLG18VRnq289XtBQ,40772
+sympy/matrices/tests/test_decompositions.py,sha256=LTm3KjUHh0QkGmR8UA1CwGWzpFu8o-qcl4sv2VKBi8g,14419
+sympy/matrices/tests/test_determinant.py,sha256=FxbLYbiO1wj12YYfsbQPoEOcgWpRGVu1CzVKLx4e8oQ,9560
+sympy/matrices/tests/test_domains.py,sha256=gUnMLr_LeEsN5unFjIwHCZje0URC9uVBN_Q76JbX9f4,3276
+sympy/matrices/tests/test_eigen.py,sha256=Xm-Xe7qGACLpqwOx5W2mDjve2KyJf-q8PyEi40jMgAk,22722
+sympy/matrices/tests/test_graph.py,sha256=ckfGDCg2M6gluv9XFnfURga8gxd2HTL7aX281s6wy6c,3213
+sympy/matrices/tests/test_immutable.py,sha256=JSu6YlGtPP-5iialCeatCKbZ4ScLDUzhQ-TMGhsalp8,4616
+sympy/matrices/tests/test_interactions.py,sha256=6T6wkHyTW5v2fwg0rz2HULoDElfA_NttApU2t-pZFKI,2070
+sympy/matrices/tests/test_matrices.py,sha256=YrwrczVsTuLoPAAkh4dlZYbfUcVEUe88wyM53fmUOtw,163196
+sympy/matrices/tests/test_matrixbase.py,sha256=2EUoz0Dl3hPFNvWU--ls6RPGfllZJbVvZSsDA9D6jPo,168868
+sympy/matrices/tests/test_normalforms.py,sha256=leFZ2rJGrWKv8Zz8aMaHQiNrCEQWOG1RqQ87omvlqIk,3730
+sympy/matrices/tests/test_reductions.py,sha256=tKv_KufpVc6qwH-MDz8XLpgVS4z6snnlNdd1ECAQSXM,13385
+sympy/matrices/tests/test_repmatrix.py,sha256=enu29IYEPzvKvGFeN6Bi9KYKqcXdy0EucifN9fV420Q,2084
+sympy/matrices/tests/test_solvers.py,sha256=55Zmvp2KC6OpdXFHBkXZRBRls_8wHfYNWO2UOQh9AoE,22311
+sympy/matrices/tests/test_sparse.py,sha256=GvXN6kBVldjqoR8WN8I_PjblKhRmyRWvVuLUgZEgugY,23281
+sympy/matrices/tests/test_sparsetools.py,sha256=pjQR6UaEMR92NolB_IGZ9Umk6FPZjvI0vk1Fd4H_C5I,4877
+sympy/matrices/tests/test_subspaces.py,sha256=poY6k6l2LSL7OCixQNGzrauLZIYbrjDul7J-yEE02S8,3465
+sympy/matrices/utilities.py,sha256=mMnNsDTxGKqiG0JATsM4W9b5jglhacy-vmRw2aZojgY,2117
+sympy/multipledispatch/__init__.py,sha256=aV2NC2cO_KmD6QFiwy4oC1D8fm3pFuPbaiTMeWmNWak,259
+sympy/multipledispatch/conflict.py,sha256=rR6tKn58MfhMMKZ4ZrhVduylXd9f5PjT2TpzM9LMB6o,2117
+sympy/multipledispatch/core.py,sha256=I4WOnmu1VtlaCnn2oD9R2-xckkYLRZPNFEWtCOTAYfM,2261
+sympy/multipledispatch/dispatcher.py,sha256=A2I4upt4qNollXGpwzrqg7M0oKHJhZx1BUMIBnjRIow,12226
+sympy/multipledispatch/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/multipledispatch/tests/test_conflict.py,sha256=msNVSiikuPOqsEm_MMGmjsNbA2CAR0F1FZaHskzzo04,1786
+sympy/multipledispatch/tests/test_core.py,sha256=UfH_7cyvZ6PHjdH8vmLG49CG7E30W8uxm3FthuMc1Jk,4048
+sympy/multipledispatch/tests/test_dispatcher.py,sha256=saJPpGXLpLOuRfw-ekzZGzY-Rys0NsS5ke0n33i9j0U,6228
+sympy/multipledispatch/utils.py,sha256=39wB9i8jNhlLFZyCTFnioLx5N_CNWv4r5VZwKrxswIE,3097
+sympy/ntheory/__init__.py,sha256=WpYPsusKb9OQ1xmXxZFla7kXacIWv4lNIpFIEVVzTLA,2810
+sympy/ntheory/bbp_pi.py,sha256=ILur1c9Ja-1F_blgnInUx-WopQ_WSvK-2OvNLEe2Zx8,5998
+sympy/ntheory/continued_fraction.py,sha256=bQW7PvdgDtWnbpCmkOwyz3mNYPOXh9_ehq3_ZpJO8Rw,10717
+sympy/ntheory/digits.py,sha256=ea3xSLy8RMaMHsekg8nq2gnVTug7SOvaDjw0lmB6NMU,3831
+sympy/ntheory/ecm.py,sha256=zg_GhL0XSyG5ZVSdGv3YceCguPZrFaS-hC-vXqOVOGQ,11793
+sympy/ntheory/egyptian_fraction.py,sha256=hW886hPWJtARqgZIrH1WjZFC0uvf9CHxMIn0X9MWZro,6923
+sympy/ntheory/elliptic_curve.py,sha256=ZT677EHi26BkZdRTLs1Tf4VSyMxDzJrwk1xECoMT7Qg,11544
+sympy/ntheory/factor_.py,sha256=MQQJSnjSpz-B9ex9fqMCnrrNTfqFOlhJV9RAWy3gJTw,83551
+sympy/ntheory/generate.py,sha256=B6LVkK627xWzQ1UE5XoeMxNM7Q1O8EwcP7fwKs5vkZk,33376
+sympy/ntheory/modular.py,sha256=wYNfNr5S8DqipQNBLVMR7cPUNg7twM25xcLhXBD14I4,8474
+sympy/ntheory/multinomial.py,sha256=rbm3STjgfRbNVbcPeH69qtWktthSCk0sC373NuDM6fU,5073
+sympy/ntheory/partitions_.py,sha256=S81zrWeTVHmYAphcP6yUGLz5CrlUwW_JFtkQRoeLlV4,8995
+sympy/ntheory/primetest.py,sha256=9EVL1yeBkOHDfa2phHiDwUnmidSJJrTl6Wzo0y1Sck0,25423
+sympy/ntheory/qs.py,sha256=8ZKxvqc-oGPqZNbEH-XcPsjXLy5-5aygVB5rR1BkSO8,14938
+sympy/ntheory/residue_ntheory.py,sha256=dDBaogSqroJHlyXRqnLmuzNVyXfy4rWB_hlhOCNQHpk,54359
+sympy/ntheory/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/ntheory/tests/test_bbp_pi.py,sha256=TgNpZOtKAfU-IVzO62Ko7Oy3LLNfdzEo9gWMTTDZL6c,9486
+sympy/ntheory/tests/test_continued_fraction.py,sha256=JJsyEXatgjNxYZTKkKubvymyVs0WDpLfbEiAY5SYk8g,3583
+sympy/ntheory/tests/test_digits.py,sha256=mrTfwboMCQkiOEpMgYg8Nrk12WE5pEtpmbQthRVt4Xc,1968
+sympy/ntheory/tests/test_ecm.py,sha256=yco77gknWe6co4VKTCoRNKHzd3jdqGKQWQFwuziYNWI,2290
+sympy/ntheory/tests/test_egyptian_fraction.py,sha256=tpHcwteuuQAahcPqvgBm4Mwq-efzcHOn8mldijynjlE,2378
+sympy/ntheory/tests/test_elliptic_curve.py,sha256=wc0EOsGo-qGpdevRq1o64htwTOT_YSUzUfyhJC-JVbg,624
+sympy/ntheory/tests/test_factor_.py,sha256=Z4B33C0blhcG9KuQzmJcDw7TtrTYJ6jAsWQd8Oi3YZw,26382
+sympy/ntheory/tests/test_generate.py,sha256=lhnJPjlz1TYrDHJ4Jq0F64P4KV8C7ngKmm3Jxtz7Wsk,9868
+sympy/ntheory/tests/test_hypothesis.py,sha256=Ztg-QoiBxpUp6euPy1RcPbF6yaLK_ij-Jcl637GGhNY,728
+sympy/ntheory/tests/test_modular.py,sha256=g73sUXtYNxzbDcq5UnMWT8NodAU8unwRj_E-PpvJqDs,1425
+sympy/ntheory/tests/test_multinomial.py,sha256=8uuj6XlatNyIILOpjJap13CMZmDwrCyGKn9LiIUiLV0,2344
+sympy/ntheory/tests/test_partitions.py,sha256=qkd-84AO0rpJv-MQW0lnXtIPtGTAfplFbu2ezKSfEQI,1088
+sympy/ntheory/tests/test_primetest.py,sha256=Lp4elAnnBDYSIPfVXENOUjZYuwAynsNQrE_CEiYDErU,9495
+sympy/ntheory/tests/test_qs.py,sha256=zbwQ1k5ywsUVNZzPewWul_yUnoqWxxvsSWOBAU5Albc,3956
+sympy/ntheory/tests/test_residue.py,sha256=7VGXsslZWVd3R7tAtIcLjYyh0TjZpb5BtG2GQehlAUY,16809
+sympy/parsing/__init__.py,sha256=KHuyDeHY1ifpVxT4aTOhomazCBYVIrKWd28jqp6YNJ8,125
+sympy/parsing/ast_parser.py,sha256=iJvr6bhm1RjM5rhWzZA4c4LGTH5lAFazN5zu8y8q-aY,2734
+sympy/parsing/autolev/Autolev.g4,sha256=980mo25mLWrQFmhRIg-aqIalUuwktYYaBGTXZ5_XZwA,4195
+sympy/parsing/autolev/__init__.py,sha256=sp5hzv5siVW3xUmhkp0S0iaA0Cz-PVB0HO1zC04pxYs,3611
+sympy/parsing/autolev/_antlr/__init__.py,sha256=MQ4ZacpTuP-NmruFXKdWLQatoeVJQ8SaBQ2DnYvtyE8,203
+sympy/parsing/autolev/_antlr/autolevlexer.py,sha256=K7HF_-5dUyAIv1_7GkhTmxqSCanEhCpzJG8fayAEB3Q,13609
+sympy/parsing/autolev/_antlr/autolevlistener.py,sha256=EDb3XkH9Y7CLzxGM-tY-nGqxMGfBHVkqKdVCPxABgRE,12821
+sympy/parsing/autolev/_antlr/autolevparser.py,sha256=BZYJ7IkurRmm44S50pYp_9JHCjT8fr1w5HeksAEPjtg,106291
+sympy/parsing/autolev/_build_autolev_antlr.py,sha256=0oIrC3sWUDe18zydeNyRmQrGGwPtRYQzIul8YcmMpk4,2578
+sympy/parsing/autolev/_listener_autolev_antlr.py,sha256=jv1jYvmrGmom6F3fhYTHG1sqLK6FFCO1-jrvDW-nPEM,104760
+sympy/parsing/autolev/_parse_autolev_antlr.py,sha256=b9hIaluJUd1V2XIAp1erak6U-c-CwKyDLH1UkYQuvKE,1736
+sympy/parsing/autolev/test-examples/README.txt,sha256=0C4m_nLROeV5J8nMfm3RYEfYgQJqmlHZaCpVD24boQY,528
+sympy/parsing/autolev/test-examples/pydy-example-repo/chaos_pendulum.al,sha256=HpTcX2wXzLqmgpp8fcSqNweKjxljk43iYK0wQmBbCDI,690
+sympy/parsing/autolev/test-examples/pydy-example-repo/chaos_pendulum.py,sha256=FSu4TP2BDTQjzYhMkcpRhXbb3kAD27XCyO_EoL55Ack,2274
+sympy/parsing/autolev/test-examples/pydy-example-repo/double_pendulum.al,sha256=wjeeRdCS3Es6ldX9Ug5Du1uaijUTyoXpfTqmhL0uYfk,427
+sympy/parsing/autolev/test-examples/pydy-example-repo/double_pendulum.py,sha256=uU9azTUGrY15BSDtw5T_V-7gmjyhHbXslzkmwBvFjGk,1583
+sympy/parsing/autolev/test-examples/pydy-example-repo/mass_spring_damper.al,sha256=Gf7OhgRlwqUEXq7rkfbf89yWA23u4uIUJ-buXTyOuXM,505
+sympy/parsing/autolev/test-examples/pydy-example-repo/mass_spring_damper.py,sha256=9ReCAqcUH5HYBgHmop9h5Zx54mfScWZN5L5F6rCHk4w,1366
+sympy/parsing/autolev/test-examples/pydy-example-repo/non_min_pendulum.al,sha256=p5v40h1nVFrWNqnB0K7GiNQT0b-MqwayYjZxXOY4M8M,362
+sympy/parsing/autolev/test-examples/pydy-example-repo/non_min_pendulum.py,sha256=DdxcWrm3HMQuyyY3Pk6sKHb4RXhQEM_EKY3HYZCP8ec,1503
+sympy/parsing/autolev/test-examples/ruletest1.al,sha256=mDJ02Q1Qm-ShVmGoyjzSfgDJHUOuDrsUg3YMnkpKdUw,176
+sympy/parsing/autolev/test-examples/ruletest1.py,sha256=eIKEFzEwkCFhPF0GTmf6SLuxXT384GqdCJnhiL2U0BQ,555
+sympy/parsing/autolev/test-examples/ruletest10.al,sha256=jKpV8BgX91iQsQDLFOJyaS396AyE5YQlUMxih5o9RK0,781
+sympy/parsing/autolev/test-examples/ruletest10.py,sha256=I1tsQcSAW6wqIguF-7lwlj9D4YZ8kCZqPqTKPUHR9oI,2726
+sympy/parsing/autolev/test-examples/ruletest11.al,sha256=j_q7giq2KIuXVRLWwNlwIlpbhNO6SqBMnLGLcxIkzwk,188
+sympy/parsing/autolev/test-examples/ruletest11.py,sha256=dYTRtXvMDXHiKzXHD2Sh0fcEukob3wr_GbSeqaZrrO8,475
+sympy/parsing/autolev/test-examples/ruletest12.al,sha256=drr2NLrK1ewn4FjMppXycpAUNbZEQ0IAMsdVx8nxk6I,185
+sympy/parsing/autolev/test-examples/ruletest12.py,sha256=ZG36s3PnkT0aKBM9Nx6H0sdJrtoLwaebU9386YSUql8,472
+sympy/parsing/autolev/test-examples/ruletest2.al,sha256=d-QjPpW0lzugaGBg8F6pDl_5sZHOR_EDJ8EvWLcz4FY,237
+sympy/parsing/autolev/test-examples/ruletest2.py,sha256=jrJfb0Jk2FP4GS5pDa0UB5ph0ijEVd1X8meKeZrTVng,820
+sympy/parsing/autolev/test-examples/ruletest3.al,sha256=1TAaOe8GI8-yBWJddfIxwnvScHNmOjSzSaQn0RS_v5k,308
+sympy/parsing/autolev/test-examples/ruletest3.py,sha256=O3K3IQo-HCjAIOSkfz3bDlst7dVUiRwhOZ0q_3jb5LU,1574
+sympy/parsing/autolev/test-examples/ruletest4.al,sha256=qPGlPbdDRrzTDUBeWydAIa7mbjs2o3uX938QAsWJ7Qk,302
+sympy/parsing/autolev/test-examples/ruletest4.py,sha256=WHod5yzKF4TNbEf4Yfxmx9WnimA7NOXqtTjZXR8FsP0,682
+sympy/parsing/autolev/test-examples/ruletest5.al,sha256=VuiKjiFmLK3uEdho0m3pk-n0qm4SNLoLPMRJqjMJ4GY,516
+sympy/parsing/autolev/test-examples/ruletest5.py,sha256=WvUtno1D3BrmFNPYYIBKR_gOA-PaHoxLlSTNDX67dcQ,1991
+sympy/parsing/autolev/test-examples/ruletest6.al,sha256=-HwgTmh_6X3wHjo3PQi7378t8YdizRJClc5Eb5DmjhE,703
+sympy/parsing/autolev/test-examples/ruletest6.py,sha256=vEO0jMOD-KIevAcVexmpvac0MGjN7O_dNipOBJJNzF0,1473
+sympy/parsing/autolev/test-examples/ruletest7.al,sha256=wR9S9rTzO9fyKL6Ofgwzw8XCFCV_p2hBpYotC8TvADI,773
+sympy/parsing/autolev/test-examples/ruletest7.py,sha256=_XvMrMe5r9RLopTrIqMGLhaYvHL1qjteWz9CKcotCL8,1696
+sympy/parsing/autolev/test-examples/ruletest8.al,sha256=P7Nu3Pq2R1mKcuFRc9dRO5jJ1_e5fwWdtqYG8NHVVds,682
+sympy/parsing/autolev/test-examples/ruletest8.py,sha256=8tgbwJ-ir0wiOCsgIFCAu4uD8SieYRrLoLzEfae5YQY,2690
+sympy/parsing/autolev/test-examples/ruletest9.al,sha256=txtZ5RH2p1FvAe6etwetSCH8rLktnpk5z0W72sCOdAA,755
+sympy/parsing/autolev/test-examples/ruletest9.py,sha256=GtqV-Wq2GGJzfblMscAz-KXCzs0P_4XqvA3FIdlPe04,1965
+sympy/parsing/c/__init__.py,sha256=J9CvkNRY-qy6CA06GZYuwTuxdnqas6oUP2g0qLztGro,65
+sympy/parsing/c/c_parser.py,sha256=fM1GGRIbjzrVQ7ErfkWz6Xzfjvym030BOoWlQLx37Oc,38124
+sympy/parsing/fortran/__init__.py,sha256=KraiVw2qxIgYeMRTFjs1vkMi-hqqDkxUBv8Rc2gwkCI,73
+sympy/parsing/fortran/fortran_parser.py,sha256=RpNQR3eNx5vgfzdt0nEZDCB56kF__SnYMaqWN3zla00,11483
+sympy/parsing/latex/LICENSE.txt,sha256=AHvDClj6QKmW53IEcSDeTq8x9REOT5w7X5P8374urKE,1075
+sympy/parsing/latex/LaTeX.g4,sha256=fG0ZUQPwYQOIbcyaPDAkGvcfGs3ZwwMB8ZnKW5yHUDY,5821
+sympy/parsing/latex/__init__.py,sha256=CfRRwZZo2qMF2HksyjGer8qSwhx0T4wnXsa2HKxCvUE,8968
+sympy/parsing/latex/_antlr/__init__.py,sha256=TAb79senorEsoYLCLwUa8wg8AUCHzmmZ7tLdi0XGNaE,384
+sympy/parsing/latex/_antlr/latexlexer.py,sha256=Y1hmY1VGL5FTSSlToTRQydPnyaLLNy1mDSWx76HaYwM,30502
+sympy/parsing/latex/_antlr/latexparser.py,sha256=ZvonpvTS3vLSOVpas88M3CfNnUhPUDsCCPPk4wBYUGE,123655
+sympy/parsing/latex/_build_latex_antlr.py,sha256=3Mwip9f_yXUNhiwwUTCG5Nk8l2uzAw8oZHU0HSl5gkE,2765
+sympy/parsing/latex/_parse_latex_antlr.py,sha256=sVaO04oSeHe_TaMeM-6toheCR88G_RmJYpUIx-Sef1g,20712
+sympy/parsing/latex/errors.py,sha256=adSpvQyWjTLsbN_2KHJ4HuXpY7_U9noeWiG0lskYLgE,45
+sympy/parsing/latex/lark/__init__.py,sha256=hhhvfKRGP3ON36wRJwVfxMWw_GA6rl0JKsIzjuaUX38,120
+sympy/parsing/latex/lark/grammar/greek_symbols.lark,sha256=-G8JGrBredhWAzCaurr1UmqgRMRrAJfs_pANub8kXyA,937
+sympy/parsing/latex/lark/grammar/latex.lark,sha256=yz3DXIHllvRDGHeBmpz49PzDCU92pKexg2RTKX4KhTE,13221
+sympy/parsing/latex/lark/latex_parser.py,sha256=lGFEo_RxSKXYmjVEb_E3DiZ-aXl5tAZyfHklzEuJ9VA,4430
+sympy/parsing/latex/lark/transformer.py,sha256=RuXe8enUnLrzUFC9ZfKQyQ1_IlgA-qPINNng6UnY1x4,25754
+sympy/parsing/mathematica.py,sha256=P2vWEgx_HXv4I_C-6vPE3RXtbpXNT2iIJQBrA6QfqkU,39614
+sympy/parsing/maxima.py,sha256=DhTnXRSAceijyA1OAm86c6TyW9-aeUVoZEELGu0oZtY,1835
+sympy/parsing/sym_expr.py,sha256=-hxarp961eyLtuwUhbg3D3qzy06HrEPZEYpGVcJzAv0,8895
+sympy/parsing/sympy_parser.py,sha256=PRRhNS_0LKBVcHFmg7LGygzxRyQt6-vyCSMisXkRhVE,43832
+sympy/parsing/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/parsing/tests/test_ast_parser.py,sha256=lcT8w7mn6UEZ8T-xfA4TqG4Mt7JxY00oHhOW7JtHQfY,803
+sympy/parsing/tests/test_autolev.py,sha256=tQuUFa8YqVdsHPOcUhAwlMKB8Uk08HejDhDCda8lXs0,6647
+sympy/parsing/tests/test_c_parser.py,sha256=VYl3K4if_23iIS-Be8MBSG0OKZo-6xgxHiN22laeAyo,155354
+sympy/parsing/tests/test_custom_latex.py,sha256=vX5uVHw9-UgEcRl0XVNOMgrjBbb1sXDlPLL4D8AeOiQ,2060
+sympy/parsing/tests/test_fortran_parser.py,sha256=SGbawrJ4a780TJAFVMONc7Y3Y8VYgVqsIHxVGaicbxE,11828
+sympy/parsing/tests/test_implicit_multiplication_application.py,sha256=xUlc9TKH4HjimhnvWMwCtTabHAJcHCupFdsuiOsZhUs,7389
+sympy/parsing/tests/test_latex.py,sha256=WvKNJ5mtxfzl-rBiE9hc2aEz81b5BmI3SKO7sRZiNbI,11765
+sympy/parsing/tests/test_latex_deps.py,sha256=oe5vm2eIKn05ZiCcXUaO8X6HCcRmN1qCuTsz6tB7Qrk,426
+sympy/parsing/tests/test_latex_lark.py,sha256=kXmZP-gMLwa5XAcNsd-rF_zrPok0RZuJsa6Ny6zfoLw,36059
+sympy/parsing/tests/test_mathematica.py,sha256=vAxwquc8ArTQE9UNbsO21FqSa6J17sCF-A4vhTPXLY0,13395
+sympy/parsing/tests/test_maxima.py,sha256=iIwnFm0lYD0-JcraUIymogqEMN3ji0c-0JeNFFGTEDs,1987
+sympy/parsing/tests/test_sym_expr.py,sha256=-wNR7GwvJHVmPSZxSuAuoX1_FJk83O0tcDi09qYY6Jk,5668
+sympy/parsing/tests/test_sympy_parser.py,sha256=H_dDL89ASex3sI8F92ceXIJy5Qd137rxTa20Rj-9l2A,12946
+sympy/physics/__init__.py,sha256=F_yvUMCuBq3HR-3Ai6W4oktBsXRg8KdutFLwT9FFJlY,220
+sympy/physics/biomechanics/__init__.py,sha256=dG1IoRAnmfXvSyPciqJVrPn5LLnuvbVnBt78hBG0maQ,1520
+sympy/physics/biomechanics/_mixin.py,sha256=0D3iBqlCRmR4HXKMxyC2LvYpKGHreOuZY5dXMJioQ4A,1493
+sympy/physics/biomechanics/activation.py,sha256=rUJegXrdO1LPHnzJpVRAYnA6DsvF46ndctkBt1JHDwI,25522
+sympy/physics/biomechanics/curve.py,sha256=PezKOUytiC15oLV11GC4M6u7ndXHpwtbilqdbIiqvgw,63154
+sympy/physics/biomechanics/musculotendon.py,sha256=LrYwFnwkShDWWZQtCgtSUqvc1H5IlTj_BNVKxfFNhE0,58274
+sympy/physics/biomechanics/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/physics/biomechanics/tests/test_activation.py,sha256=hdnMsFBLjloJylu8-cLZ44oamORG3kSsI0q2-eLQ_-I,13395
+sympy/physics/biomechanics/tests/test_curve.py,sha256=3jbWEHr8ieZYCfVlwJ1KTA1WJ9dcTCyQ1p3r8G2jp1I,75800
+sympy/physics/biomechanics/tests/test_mixin.py,sha256=ds-EoUCvfiSjVGnC_mBwjn5mI7z5W5wi2UTZZ4-pIIQ,1322
+sympy/physics/biomechanics/tests/test_musculotendon.py,sha256=Ls59mtJQ83W0fdpDGFGNeTgtxL8yAZ6ODW96N9mvFtM,32906
+sympy/physics/continuum_mechanics/__init__.py,sha256=GzYypKq3so15xfvY4qFerHYCNGFzZthfQ1aOcgu8yCM,191
+sympy/physics/continuum_mechanics/arch.py,sha256=TKaoHk7-ToNP0n6ZGFF_cW6x965C-D3iENiqFo74ZJU,39243
+sympy/physics/continuum_mechanics/beam.py,sha256=Vq1xJHrjatQEUigMsxlAk8qoZSyp_Z_0GiJ5y5AIMIg,159946
+sympy/physics/continuum_mechanics/cable.py,sha256=1IOpa_eLYwdyixNkDhLXJQSg4fQhwOH7Ybm1jWR4aQA,31420
+sympy/physics/continuum_mechanics/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/physics/continuum_mechanics/tests/test_arch.py,sha256=t_V-Pt0fxoPqYDZl5LB6ehQ4hBBVlveRWLsEXdyo_3k,2571
+sympy/physics/continuum_mechanics/tests/test_beam.py,sha256=Wmi8BCju21H1RaomKDqFATvCUqsrca_43o5IyA5s4ok,42085
+sympy/physics/continuum_mechanics/tests/test_cable.py,sha256=aANZVJxfihRkjAauCvFPGkXKD_zxMcJFb7vXU443dsQ,3832
+sympy/physics/continuum_mechanics/tests/test_truss.py,sha256=wgtF1GbQX5hzx20UrBg2ZyEvplVpsio3DiU8CS_bAk8,3269
+sympy/physics/continuum_mechanics/truss.py,sha256=hkLLnXA9aRaE2YvA0ods7T40zJQXUc_jLI108NjhtTI,44962
+sympy/physics/control/__init__.py,sha256=U0BeOmFRXOw-cpZ_F-RzNuEh78Tqzx31nuvZMZW2Fo8,1334
+sympy/physics/control/control_plots.py,sha256=lYa42ATTrSXMV44Q3POfEyYQCFSTV1W5Rb4UejYFdT8,37515
+sympy/physics/control/lti.py,sha256=o6n-qFXriBeETr4anCgOuYViUpQXDtBejckIdYdlxx0,177322
+sympy/physics/control/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/physics/control/tests/test_control_plots.py,sha256=dsXkabVUMzU6b1FmptRz2sGgXfe6y_lu3Mm0KN8is90,16899
+sympy/physics/control/tests/test_lti.py,sha256=YAvwA11zSKRcHmgLPuCJhBJXbFrPfjUNytaQsnEsEr8,104043
+sympy/physics/hep/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/physics/hep/gamma_matrices.py,sha256=WlSHLUtMU7NrgLyKEvTntMSYxMZq1r_6o2kqUEAdPaA,24253
+sympy/physics/hep/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/physics/hep/tests/test_gamma_matrices.py,sha256=iKqICj0bP7EK0sSuYFsPdPkDTbHGa6J_LMPZAzv1j4o,14722
+sympy/physics/hydrogen.py,sha256=R2wnNi1xB-WTQ8Z9aPUhX9Z8mQ8TdhCM1JAZIkyXgjw,7594
+sympy/physics/matrices.py,sha256=jHfbWkzL2myFt-39kodQo5wPubBxNZKXlljuSxZL4bE,3836
+sympy/physics/mechanics/__init__.py,sha256=7rgPJp1YItAMOc29gWqkRp68AKCdZLu4tFQG-CWgjOQ,2874
+sympy/physics/mechanics/actuator.py,sha256=d7Ffb9vo1Tmrcfiy71_g0r1btscfvvabE-vznku3Auk,43633
+sympy/physics/mechanics/body.py,sha256=Z9ZOReEab9FHEDsyv2RfIBQM4_ripXhFXmQtu1OmN_Y,24617
+sympy/physics/mechanics/body_base.py,sha256=bwP04lWmD0iY_T0Vsn6NWMbnWyzUMFFqkAoAyKsow_c,2491
+sympy/physics/mechanics/functions.py,sha256=KsX3z33rZdMCifwyVlyKIHVJh0Fve--zs5wd1rkGMl0,25190
+sympy/physics/mechanics/inertia.py,sha256=FKcEbpYPx_26LS9sSp8lcsJ83spqwSYIyxM5q_z1mYA,6172
+sympy/physics/mechanics/joint.py,sha256=o9SWJ9pXDO-sVh0gJ6VTI0LgY019gFB5w5FP1PN--UI,84621
+sympy/physics/mechanics/jointsmethod.py,sha256=nqkXawtuxeyP0D8DEAYURCJlDYS4Eka_vXx6UZP7y74,10415
+sympy/physics/mechanics/kane.py,sha256=XyvEDNF09i5DnHf-YlCecEHDdKK9j486N2P5YD8_XaM,37118
+sympy/physics/mechanics/lagrange.py,sha256=UTmClOP-PkY8S-LcPlcOAUmshndlq6mpmZailbvy4E4,20202
+sympy/physics/mechanics/linearize.py,sha256=YtAap6TzsZJd794cdDCEDJVlMOXd_wFAMaCZZacHwbE,17242
+sympy/physics/mechanics/loads.py,sha256=jnajZOg631Aqtd0-BplehohUL991C3Cji2OZZ3MVHdk,5406
+sympy/physics/mechanics/method.py,sha256=2vFRhA79ra4HR6AzVBHMr3oNncrcqgLLMRqdyif0DrI,660
+sympy/physics/mechanics/models.py,sha256=9q1g3I2xYpuTMi-v9geswEqxJWTP3RjcOquRfzMhHzM,6463
+sympy/physics/mechanics/particle.py,sha256=YKiEBkPLVRI9foXlEe8eu8Ys5W1GyQO1oOBf0T8fHFg,5985
+sympy/physics/mechanics/pathway.py,sha256=2T71x06X8CZ20NUyXkMnP83gKsx2wJJEbPJS-1gMXFA,26555
+sympy/physics/mechanics/rigidbody.py,sha256=s5b7ynsdcMsq9PN2nM1yozNcZd08RA-MgzMjcs7jnBI,10287
+sympy/physics/mechanics/system.py,sha256=VsPPQfJs0gBLKJYNQ2ZLmD98JWLd1IZy0QGRwI21_TM,59457
+sympy/physics/mechanics/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/physics/mechanics/tests/test_actuator.py,sha256=WoTnD_IGhewVOzcL7ybJXf_mrK0_E71gDcnon-3Mayg,41081
+sympy/physics/mechanics/tests/test_body.py,sha256=ygHfWeL6-3LW9gqXHfzdvqQhJWxg2-U2KQIHKqGNqKQ,12067
+sympy/physics/mechanics/tests/test_functions.py,sha256=Cl1aT3qT-0Ik6vJgS6at6PUaeWRQfpEG88QvEIxz1Bk,10447
+sympy/physics/mechanics/tests/test_inertia.py,sha256=On4K78tmq4tyr_QFs3Uo01efdlVj5R6yfn6CVs7ksnE,2726
+sympy/physics/mechanics/tests/test_joint.py,sha256=ojhQdBsWN6nR7egEhpw-LAyDRdFIPfSEsDoFL1bhzqs,57922
+sympy/physics/mechanics/tests/test_jointsmethod.py,sha256=8IUO7ntLPsydDT4YgA1FPyO5TXNpo4CIWC7O_JdWg9Q,10226
+sympy/physics/mechanics/tests/test_kane.py,sha256=K1HOm7cuPzXeO9ZmW7oAHgu-jxOg1IFqyKB3YheAbus,21545
+sympy/physics/mechanics/tests/test_kane2.py,sha256=qNLkbSV6UYclQzMDoRi_TJrJEz0-MUi7yf1e7XW4ba4,19256
+sympy/physics/mechanics/tests/test_kane3.py,sha256=-9MbXLfYUDhA_9D2o4NrNLgDTOnKDLLsgryQW3AHacs,14959
+sympy/physics/mechanics/tests/test_kane4.py,sha256=qJYUfvnq1F5UN_AQqTu_3BT5XqGceqxnCyP3d2gE04A,4709
+sympy/physics/mechanics/tests/test_kane5.py,sha256=gZvAyxJ8fkkLtH60xHlb_Gxrbt9d7VmJTckXSGmF0j4,5693
+sympy/physics/mechanics/tests/test_lagrange.py,sha256=iuHomulBF8MafLeorKGaLHUEF8CvFhXcxEtN0hk1akM,10119
+sympy/physics/mechanics/tests/test_lagrange2.py,sha256=VBonbWg0uLduE4AwNUWCsGGa_2KmaDbg9jROZL3DTWE,1402
+sympy/physics/mechanics/tests/test_linearity_of_velocity_constraints.py,sha256=Y9JYoaob8yyL7PS-VN7g-DC8YEfCxBGdFSSkQAKlOBI,1341
+sympy/physics/mechanics/tests/test_linearize.py,sha256=6yFFGEhJW60fx9Ny1duc6eyvGDg6rtmJVo_V1mgHgGk,13273
+sympy/physics/mechanics/tests/test_loads.py,sha256=Kw94kP0tfwKsV-jCDHGTQsyc-1dKQl3ABJfqJtR8AJg,2698
+sympy/physics/mechanics/tests/test_method.py,sha256=L7CnsvbQC-U7ijbSZdu7DEr03p88OLj4IPvFJ_3kCDo,154
+sympy/physics/mechanics/tests/test_models.py,sha256=GcsfCm5G4PPYQXsHCiAKI1dEW42RaZOh-x6aEouTYo4,5078
+sympy/physics/mechanics/tests/test_particle.py,sha256=JL6QAA6T3POQkSutUnungrVkR3xt6ZVX-hp75-EufQw,2682
+sympy/physics/mechanics/tests/test_pathway.py,sha256=oGCxlUOviyNc1GBMvhk5sYVZfu8C4o7lJMbqatBie3A,24944
+sympy/physics/mechanics/tests/test_rigidbody.py,sha256=ezMW5BWt9cWdNeY1B9aYcL4NsPcVkaKZuUS1C7S1qPk,6188
+sympy/physics/mechanics/tests/test_system.py,sha256=Dihu77qM5_QkDQg-zavHbVhh_nvaGEVztXgPNl2_enk,8700
+sympy/physics/mechanics/tests/test_system_class.py,sha256=Xe4VLrxWaYL3oRwP2SBaanWf5DY46d2yPlwf9H1XJ4M,38219
+sympy/physics/mechanics/tests/test_wrapping_geometry.py,sha256=aXwuEaprstnSW7BwLr3OKUyxSagRPO58L-tUMaq3I9s,10502
+sympy/physics/mechanics/wrapping_geometry.py,sha256=XkI331oQcBme4jDmH0RqQDWh_pq49o38nbRsXwT9O5E,21599
+sympy/physics/optics/__init__.py,sha256=0UmqIt2-u8WwNkAqsnOVt9VlkB9K0CRIJYiQaltJ73w,1647
+sympy/physics/optics/gaussopt.py,sha256=mVQ-JX7xmAp9XbNOYIlwsPAxkUukTw_QjbjIxuKWZW8,20898
+sympy/physics/optics/medium.py,sha256=cys0tWGi1VCPWMTZuKadcN_bToz_bqKsDHSEVzuV3CE,7124
+sympy/physics/optics/polarization.py,sha256=mIrZiOVXetGtKkLxl8Llaf2Z9coWenf6JKrClh4W8yU,21434
+sympy/physics/optics/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/physics/optics/tests/test_gaussopt.py,sha256=QMXJw_6mFCC3918b-pc_4b_zgO8Hsk7_SBvMupbEi5I,4222
+sympy/physics/optics/tests/test_medium.py,sha256=RxG7N3lzmCO_8hIoKyPnDKffmk8QFzA9yamu1_mr_dE,2194
+sympy/physics/optics/tests/test_polarization.py,sha256=81MzyA29HZckg_Ss-88-5o0g9augDqCr_LwcJIiXuA0,2605
+sympy/physics/optics/tests/test_utils.py,sha256=SjicjAptcZGwuX-ib_Lq7PlGONotvo2XJ4p3JA9iNVI,8553
+sympy/physics/optics/tests/test_waves.py,sha256=PeFfrl7MBkWBHdc796sDDYDuhGepat3DQk7PmyTXVnw,3397
+sympy/physics/optics/utils.py,sha256=BqfuvtrjO3PEcDQ1DecNyt2Th9Yps6xued1tEY4ysvk,22172
+sympy/physics/optics/waves.py,sha256=Iw-9gGksvWhPmQ_VepmI90ekKyzHdPlq6U41wdM4ikI,10042
+sympy/physics/paulialgebra.py,sha256=1r_qDBbVyl836qIXlVDdoF89Z9wedGvWIkHAbwQaK-4,6002
+sympy/physics/pring.py,sha256=SCMGGIcEhVoD7dwhY7_NWL1iKwo7OfgKdmm2Ok_9Xl0,2240
+sympy/physics/qho_1d.py,sha256=ZXemUsa_b0rLtPVTUkgAkZQ1Ecu2eIZxaiNSSXW0PDk,2005
+sympy/physics/quantum/__init__.py,sha256=vzWc2d2aXbMdZGJRx4tyS6FDCtjbtl7SqCJE7nSXN9A,1952
+sympy/physics/quantum/anticommutator.py,sha256=kXr7DWZZy0Sqcnxx-QEeA58mUCRXTL_VHDzQBjNV0DI,5072
+sympy/physics/quantum/boson.py,sha256=Nn4TN917TQv4tdFNZddlT1eEGg7O1uybnU9K5NI6AyM,5834
+sympy/physics/quantum/cartesian.py,sha256=9R9VDYLV1Xe-GkA9TQbj8PVlBLaD0fF6KXfHJ1ze5as,9092
+sympy/physics/quantum/cg.py,sha256=WK7HkAIRFejQQLjRsCC7rH0L--0fmXAoeL1JdTHb3GA,23319
+sympy/physics/quantum/circuitplot.py,sha256=SacQMhPyDhizKmGRNEs1vtXph8lR6bMn5bVJI4rJiXg,11799
+sympy/physics/quantum/circuitutils.py,sha256=mrQNUDbwM3LV1NZ1EqVpXyOY2mOXCBVZW7cQTiCxUaM,13882
+sympy/physics/quantum/commutator.py,sha256=MKk8OqwIi8LCzxkD8URmKCFM9OthAAxS8xFNgP_i09U,8139
+sympy/physics/quantum/constants.py,sha256=20VRATCkSprSnGFR5ejvMEYlWwEcv1B-dE3RPqPTQ9k,1420
+sympy/physics/quantum/dagger.py,sha256=P58FL8fLFfMhtu79jGsG69oEMjLjutRDrXsLwBat-9U,2484
+sympy/physics/quantum/density.py,sha256=ie_NJO-YNJ3PydAMp3jlmhC5H8bN3ldhn2SiCKY5N0s,9546
+sympy/physics/quantum/fermion.py,sha256=1ipn3FItUJ_ruLnflpp9MN_6t5w8CgHAJRJCOsukGGI,4983
+sympy/physics/quantum/gate.py,sha256=Iv7-qhSCe_An9qaJcYRDgwr8ClNraP47E75UlS5fCoQ,42588
+sympy/physics/quantum/grover.py,sha256=17KC5MJR3cCavmzqRqi9dB5OFTOpsYjfrTZuv03HiuE,10452
+sympy/physics/quantum/hilbert.py,sha256=qrja92vF7BUeSyHOLKVX8-XKcPGT7QaQMWrqWXjRNus,19632
+sympy/physics/quantum/identitysearch.py,sha256=Zh_ji5J0YeAy2AezsQcHV9W2icWoaa3ZwTbfjCCQmJo,27607
+sympy/physics/quantum/innerproduct.py,sha256=NKlJufh068C3MeKBORJG8k7zdH_vGJeo7jlH67PqU7E,4303
+sympy/physics/quantum/kind.py,sha256=odJyriPZKyievddvlY-kks8iBCSi1yZ7JEsaLANHoe4,2831
+sympy/physics/quantum/matrixcache.py,sha256=S6fPkkYmfX8ELBOc9EST-8XnQ1gtpSOBfd2KwLGKdYo,3587
+sympy/physics/quantum/matrixutils.py,sha256=53VGeUNYNpU1eA1sNcYFaX1_45I9SVabCfpTyHUDFog,8214
+sympy/physics/quantum/operator.py,sha256=Q-uiUjAZgMOXHKoBFe2oW_qJsJSjC8fM246yzxMmZ0A,19657
+sympy/physics/quantum/operatorordering.py,sha256=byAyZCNKTCeFWIFThmNx0NgdI4u32O4ydodYSa6Wrr8,10296
+sympy/physics/quantum/operatorset.py,sha256=h8nkScpQcUzCO3zemqKpgQfJDWiBbfj33IJzcl4J2_4,9563
+sympy/physics/quantum/pauli.py,sha256=lzxWFHXqxKWRiYK99QCo9zuVG9eVXiB8vFya7TvrVxQ,17250
+sympy/physics/quantum/piab.py,sha256=Zjb2cRGniVDV6e35gjP4uEpI4w0C7YGQIEXReaq_z-E,1912
+sympy/physics/quantum/qapply.py,sha256=5vqWe__WRvJuD6sWC2NTDe1qEcToBGTGaye9lzirgf4,9494
+sympy/physics/quantum/qasm.py,sha256=UWpcUIBgkK55SmEBZlpmz-1KGHZvW7dNeSVG8tHr44A,6288
+sympy/physics/quantum/qexpr.py,sha256=N87sU5AqFbGEXgHJof-B7ua46rk4SeysK16abU1GfYI,13940
+sympy/physics/quantum/qft.py,sha256=ua4qBQAm-gi923lRRxOgAebTsTCoR93pz8ZHPXBdcus,6425
+sympy/physics/quantum/qubit.py,sha256=mE0xebz8zqeKEc4xww9-Iv7tZzxHjm9Klsr2WgUQcVM,25997
+sympy/physics/quantum/represent.py,sha256=dBY9RLD21V0WuIBbigagjaLSUCiloDzitv8Wl6fU9dE,18729
+sympy/physics/quantum/sho1d.py,sha256=ZroR_FjxmjOmDcd0Fm04vWKTGCpvLaEu4NiuplKm708,20867
+sympy/physics/quantum/shor.py,sha256=DVwPxLAPSr8t3F3aXJIPe4o5XSuQiE6a6eA6OYmdZFw,5504
+sympy/physics/quantum/spin.py,sha256=l6168fTEZEC4FeaMS8iLqo3b4YMvUlPTZvq9N8iSV08,72986
+sympy/physics/quantum/state.py,sha256=618YaDGAtpUeuWCVYs6lDuO3mD87_f8KIUGUXaN9L4s,29971
+sympy/physics/quantum/tensorproduct.py,sha256=PvpDSuLku1IdIV3T2dK3fVv6AdMNVcc5eR8n34AB3OQ,12599
+sympy/physics/quantum/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/physics/quantum/tests/test_anticommutator.py,sha256=ckWHKwQFiAMWcDaYSa_26vi_GIsvs32_0O62I5lGsr8,1304
+sympy/physics/quantum/tests/test_boson.py,sha256=BZjdrZ-F1QhyhDqfK4Zc1VEFBJi1PeiPjMpfBcHekfo,1676
+sympy/physics/quantum/tests/test_cartesian.py,sha256=4szaB4w555oDUkhaRtBF8mOfIxiODNCqOlDHEVcUFOo,4417
+sympy/physics/quantum/tests/test_cg.py,sha256=BF-2ybLhoAYOb0wfWFlgnobMzH20zTYJvW5Z7v46SYI,9159
+sympy/physics/quantum/tests/test_circuitplot.py,sha256=c3v9wUzLHUH-eBVGj6_broVhHkioNwpaaApTDAJEflU,2096
+sympy/physics/quantum/tests/test_circuitutils.py,sha256=GrJAWRQVH_l8EIHrj1ve2jtxske72IriQ3lo94fqrVQ,13187
+sympy/physics/quantum/tests/test_commutator.py,sha256=keBstGDpNITFRr06uVFrka_Lje56g6oFoJQEpZXmnYw,2727
+sympy/physics/quantum/tests/test_constants.py,sha256=KBmYPIF49Sq34lbzbFCZRYWSyIdhnR3AK3q-VbU6grU,338
+sympy/physics/quantum/tests/test_dagger.py,sha256=pi2lhL5EaE8FuIuwM9gOce2NpDCP4S1JptqI-mx8FGM,2632
+sympy/physics/quantum/tests/test_density.py,sha256=EyxiEgyc0nDSweJwI0JUwta7gZ81TVHCl7YDEosTrvI,9718
+sympy/physics/quantum/tests/test_fermion.py,sha256=RK-J3nV1UO_9R5UyrBIp_qfWX-5iZ152aoyEllKWSIc,1636
+sympy/physics/quantum/tests/test_gate.py,sha256=7oBX1HoWnrYtHjABRoqv_wQDB9B829E99fdcJzaqawM,12496
+sympy/physics/quantum/tests/test_grover.py,sha256=uze62AG6H4x2MYJJA-EY3NtkqwvrDIQ2kONuvIRQiZ4,3640
+sympy/physics/quantum/tests/test_hilbert.py,sha256=IGP6rc2-b3we9dRDbpRniFAhQwp_TYtMfFzxusAprx0,2643
+sympy/physics/quantum/tests/test_identitysearch.py,sha256=3YGrXCsFLhLtN5MRyT5ZF8ELrSdkvDKTv6xKM4i2ims,17745
+sympy/physics/quantum/tests/test_innerproduct.py,sha256=37tT8p6MhHjAYeoay1Zyv7gCs-DeZQi4VdwUH2IffDE,1483
+sympy/physics/quantum/tests/test_kind.py,sha256=MCc0jobaf-fZmi99ir2VpZM1Lah5w9ZMxqygVQZfleU,2515
+sympy/physics/quantum/tests/test_matrixutils.py,sha256=wXc2jYLYaR07MgbVBpUU0cEsy2YQhQygbHV6M55u_ss,4115
+sympy/physics/quantum/tests/test_operator.py,sha256=kmNGQzjtdsko6PlUNcTT0kjOYX73LOMzHGuncS2SjSw,8417
+sympy/physics/quantum/tests/test_operatorordering.py,sha256=SFvJfrBxreMgMB3PEpXGcTvO_113Pi1O-Jco-A9_aVI,2003
+sympy/physics/quantum/tests/test_operatorset.py,sha256=DNfBeYBa_58kSG7PM5Ilo6xnzek8lSiAGX01uMFRYqI,2628
+sympy/physics/quantum/tests/test_pauli.py,sha256=Bhsx_gj5cpYv4BhVJRQohxlKk_rcp4jHtSRlTP-m_xs,4940
+sympy/physics/quantum/tests/test_piab.py,sha256=8ndnzyIsjF4AOu_9k6Yqap_1XUDTbiGnv7onJdrZBWA,1086
+sympy/physics/quantum/tests/test_printing.py,sha256=Qg34sHUAgKeh7DhHNp3KQX0_bhfdkMPCKJUvL08ufz8,30332
+sympy/physics/quantum/tests/test_qapply.py,sha256=ol5IJuuPand4r-P2cBv_Wycv2wnkMsW3dlJxDAp_PSs,6086
+sympy/physics/quantum/tests/test_qasm.py,sha256=ZvMjiheWBceSmIM9LHOL5fiFUl6HsUo8puqdzywrhkc,2976
+sympy/physics/quantum/tests/test_qexpr.py,sha256=0a80r2d31yxuJn11CcemrMn855o2ge5CH8j73ZCwuKI,1830
+sympy/physics/quantum/tests/test_qft.py,sha256=v-sGTaW9S-gcGTDAUPvjwd1kINF6rlI_u5Sf-Gso0r8,1931
+sympy/physics/quantum/tests/test_qubit.py,sha256=rj5RNjLWjZ8514fQwEsVp0wuUlI1SKAIomL0wZ-9PqA,9271
+sympy/physics/quantum/tests/test_represent.py,sha256=lEwzpL0fGxDGkojZ4_WoBAtCcA7aq2-S-i0Z0QrnTXg,5177
+sympy/physics/quantum/tests/test_sho1d.py,sha256=1QCoRXwhcK-P1oUrREgAroPHJPzPgWEBSrHomjS3meA,6893
+sympy/physics/quantum/tests/test_shor.py,sha256=3a3GCg6V5_mlJ2bltoXinGMGvlSxpq7GluapD_3SZaQ,666
+sympy/physics/quantum/tests/test_spin.py,sha256=p6F0cfQkPYwXlzE29mTL-dpsEYZmFU0EfAwm2MUA9xE,345698
+sympy/physics/quantum/tests/test_state.py,sha256=pjNwjEQN1zMXzC6NbL60ZLzpqBfZD5RscjuyiFuhTaE,6748
+sympy/physics/quantum/tests/test_tensorproduct.py,sha256=CL8xJyUl7plFU7TYsHHkLDYt9PtS4SM_H-SG4lKM5aE,5314
+sympy/physics/quantum/tests/test_trace.py,sha256=dbpTXcJArWRR_Hh5JTuy2GJIfgjVo6zS20o5mdVEGH4,3057
+sympy/physics/quantum/tests/test_transforms.py,sha256=w1fLTJO07CWPOQp5jFVU83MatsaoNBkxOI_YbM6I1w8,2346
+sympy/physics/quantum/trace.py,sha256=2ZqN9IEsz3LKHTLV8ZDwTK0sM5PfwL0p2sYet0N7Gis,6397
+sympy/physics/quantum/transforms.py,sha256=XLnWmyoWW8oXJ8I7dUIF7cMxFt9xr736HfNstD_tnK0,10929
+sympy/physics/secondquant.py,sha256=1XDsbnUpFC7zmJ4VC9DXEsK_smSqNMaDfsi6slIkws0,90974
+sympy/physics/sho.py,sha256=K8P9FAdZr6UfQKYZO9TlhDUqUd3YsMekXCsKy2HhaY0,2480
+sympy/physics/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/physics/tests/test_clebsch_gordan.py,sha256=gHMZnYkigoVWeDV7bwD4YARplb_L1AOMppzcLxSmWnI,9921
+sympy/physics/tests/test_hydrogen.py,sha256=kohRIR6JojE_GWYnlzLsMMgdhoKd8whazs0mq7cCTQc,4987
+sympy/physics/tests/test_paulialgebra.py,sha256=tyshEMsLNPR4iYzoAbPGZRZ-e_8t7GDP_xyjRyhepeQ,1477
+sympy/physics/tests/test_physics_matrices.py,sha256=Dha8iQRhzxLcl7TKSA6QP0pnEcBoqtj_Ob6tx01SMwI,2948
+sympy/physics/tests/test_pring.py,sha256=XScQQO9RhRrlqSII_ZyyOUpE-zs-7wphSFCZq2OuFnE,1261
+sympy/physics/tests/test_qho_1d.py,sha256=LD9WU-Y5lW7bVM7MyCkSGW9MU2FZhVjMB5Zk848_q1M,1775
+sympy/physics/tests/test_secondquant.py,sha256=vI9EuCMd1PMYNbZH2bHW9xHPYS5DS92QOmAk1kC12cQ,49401
+sympy/physics/tests/test_sho.py,sha256=aIs1f3eo6hb4ErRU8xrr_h_yhTmRx-fQgv9n27SfsLM,693
+sympy/physics/units/__init__.py,sha256=DVvWy9qNRm742NFGcBpybFY20ZK3BU7DWNbLMTXYiFo,12386
+sympy/physics/units/definitions/__init__.py,sha256=F3RyZc1AjM2Ch5b27Tt-VYdZ1HAIWvhgtQQQTfMiN6w,7470
+sympy/physics/units/definitions/dimension_definitions.py,sha256=QmPLCy7GvLx1z_YEZ5KFl8QUXg4rcuu3PuNI5wLpPdY,1633
+sympy/physics/units/definitions/unit_definitions.py,sha256=05wpHmAtyQvuJBeuzWm3cDQ6UYviNtsi4kVc0hv8VHw,14680
+sympy/physics/units/dimensions.py,sha256=GMuoSF1vqKDs_e3JJ7Y0lpFrneXMZLZk_qIC5d6Q5UU,20899
+sympy/physics/units/prefixes.py,sha256=_q2f8gA-kckBG7TutTFQazTf15PCZqNnaTR1gKXRfsk,6260
+sympy/physics/units/quantities.py,sha256=r5E231CULmsSEM7Rh7zfcTPuR85_X0CwRCVU_nDsek0,4671
+sympy/physics/units/systems/__init__.py,sha256=jJuvdc15c83yl11IuvhyjijwOZ9m1JGgZOgKwKv2e2o,244
+sympy/physics/units/systems/cgs.py,sha256=gXbX8uuZo7lcYIENA-CpAnyS9WVQy-vRisxlQm-198A,3702
+sympy/physics/units/systems/length_weight_time.py,sha256=DXIDSWdhjfxGLA0ldOziWhwQjzTAs7-VQTNCHzDvCgY,7004
+sympy/physics/units/systems/mks.py,sha256=Z3eX9yWK9BdvEosCROK2qRKtKFYOjtQ50Jk6vFT7AQY,1546
+sympy/physics/units/systems/mksa.py,sha256=U8cSI-maIuLJRvpKLBuZA8V19LDRYVc2I40Rao-wvjk,2002
+sympy/physics/units/systems/natural.py,sha256=43Odvmtxdpbz8UcW_xoRE9ArJVVdF7dgdAN2ByDAXx4,909
+sympy/physics/units/systems/si.py,sha256=YBPUuovW3-JBDZYuStXXRaC8cfzE3En3K5MjNy5pLJk,14478
+sympy/physics/units/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/physics/units/tests/test_dimensions.py,sha256=lzkgGfEXMHxB8Izv7nRTN2uOEPh65LXPYaG8Kr5H05o,6122
+sympy/physics/units/tests/test_dimensionsystem.py,sha256=s2_2RAJwOaPOTvyIiAO9SYap374ytZqWbatWkLCnbSU,2717
+sympy/physics/units/tests/test_prefixes.py,sha256=Y3vlIReWGu8bwwZTrNGZSVoWYjhzgUZC33CDeyIvw48,2238
+sympy/physics/units/tests/test_quantities.py,sha256=XPuM6ul7XrUHvQE7F8rvpoCaxT9N6TM2gX99qUM4gTA,19758
+sympy/physics/units/tests/test_unit_system_cgs_gauss.py,sha256=JepTWt8yGdtv5dQ2AKUKb9fxpuYqLWOp0oOmzov9vfY,3173
+sympy/physics/units/tests/test_unitsystem.py,sha256=1Xh78_8hbv-yP4ICWI_dUrOnk3cimlvP_VhO-EXOa7Q,3254
+sympy/physics/units/tests/test_util.py,sha256=76PaLp_Cd9BAiry6VcWUD4Hrr68D6lTOkScWcLyhmL0,9355
+sympy/physics/units/unitsystem.py,sha256=mLuXftt3003OLk3knUGdYb1XfGbQa6vp-pHcenL11-o,7593
+sympy/physics/units/util.py,sha256=GU43MjXBRl5fm0CrU1fhFsZi9Q7wPG0NSJVJb1Oc_Y8,9885
+sympy/physics/vector/__init__.py,sha256=jZmrNB6ZfY7NOP8nx8GWcfI2Ixb2mv7lXuGHn63kyOw,985
+sympy/physics/vector/dyadic.py,sha256=CuAkSBgnatfnZQ2gBazZQC1DBVXbksiFWWqxu2w3C2U,18042
+sympy/physics/vector/fieldfunctions.py,sha256=H3S7oeLHaInZns9ko04BPOcSSOK2M5hBK71jB813ExY,8610
+sympy/physics/vector/frame.py,sha256=J1sY-qaAamcZFKP26x54Q9TWITXBgjmE3v5PiEaFri4,57260
+sympy/physics/vector/functions.py,sha256=b4dHZOxbvS77SMcSlCV2GTaOzWj0TQiUtDaO_dhj3Uw,24810
+sympy/physics/vector/point.py,sha256=W1H6Jd-ZAIHY8yKO3YWj-vdEp7qI2yoglzk5HuT-y2E,20569
+sympy/physics/vector/printing.py,sha256=a1N-wziCnt4gHtY9luqe-CDW9aAtpZ0FcvWwQ0hMEEo,11790
+sympy/physics/vector/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/physics/vector/tests/test_dyadic.py,sha256=vCSLxo0pynrHrCCo7S4925lgRhoSm_Mr5YhIh_1bYcw,4265
+sympy/physics/vector/tests/test_fieldfunctions.py,sha256=FUjh18QzB6dXSau9iHutb36o28faSa7T9sB0icpja-M,5825
+sympy/physics/vector/tests/test_frame.py,sha256=S5xn5Q9dMj_ACIS9ff7ACTBS7sFFovi9DIov-crUsko,29683
+sympy/physics/vector/tests/test_functions.py,sha256=qHGT0RR-vkAp1oF30TFNgaeuOGvXdZnK2aKZuRgrZHg,21127
+sympy/physics/vector/tests/test_output.py,sha256=hgqlE-_zN_EPE_gIZ_v2uXB1y2auo39hReWpvFUm2QQ,2612
+sympy/physics/vector/tests/test_point.py,sha256=5CTzT3a-igd33auAra4uusm0PYUc_whPtV7KAoZ4g5w,12373
+sympy/physics/vector/tests/test_printing.py,sha256=qVBjz4f3TtDrduUYLNDrvlrzBVMBDqLo27JWsFHdX18,10967
+sympy/physics/vector/tests/test_vector.py,sha256=bqU1ltS6UGbSo74KXMtvP1mOpqKQ6XSV19Wjw2QoNFc,10259
+sympy/physics/vector/vector.py,sha256=R7P63QPZee0lfHHVDU7Uq91ZJs5ewjVK5HVj9HHNeQo,24874
+sympy/physics/wigner.py,sha256=L5QyzD4yH4SyTk7-WCCd07XK0_Pd9nz7sHwo2zOWV7I,39588
+sympy/plotting/__init__.py,sha256=hAdOjai8-laj79rLJ2HZbiW1okXlz0p1ck-CoeNU6m8,526
+sympy/plotting/backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/plotting/backends/base_backend.py,sha256=aof5cs8mWG6fl87DadR5sZBjY8ssMelOwKFWi27-FO0,14911
+sympy/plotting/backends/matplotlibbackend/__init__.py,sha256=Mzsz43gkanid12G8qnOcwJNbbvqf6eGppn-IZyJCMto,162
+sympy/plotting/backends/matplotlibbackend/matplotlib.py,sha256=9efu7ST_D3M-_fepZCPTcy8TdYEIX0PCkyocDGVfbPE,12548
+sympy/plotting/backends/textbackend/__init__.py,sha256=nnV_C7JJ_kwGcQe2C-tpnK635W8vTuIf5Grvvmur0rQ,92
+sympy/plotting/backends/textbackend/text.py,sha256=1ukArjwwQWUED9SB-1dmkB6YL5EcJ2rUosUf_NcBpXs,803
+sympy/plotting/experimental_lambdify.py,sha256=xcBhlvZ2h20aI1MpUN6qAEpO075Dv132AWbQJ7l3Wzg,22828
+sympy/plotting/intervalmath/__init__.py,sha256=fQV7sLZ9NHpZO5XGl2ZfqX56x-mdq-sYhtWEKLngHlU,479
+sympy/plotting/intervalmath/interval_arithmetic.py,sha256=jv5YolNs6pOawIhuSsTBVwgkgmdOFwPrGN_1KtjfcIs,15570
+sympy/plotting/intervalmath/interval_membership.py,sha256=1VpO1T7UjvPxcMySC5GhZl8-VM_DxIirSWC3ZGmxIAY,2385
+sympy/plotting/intervalmath/lib_interval.py,sha256=WY1qRtyub4MDJaZizw6cXQI5NMEIXBO9UEWPEI80aW8,14809
+sympy/plotting/intervalmath/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/plotting/intervalmath/tests/test_interval_functions.py,sha256=gdIo5z54tIbG8hDaGd3I8rBDP67oetMZWWdM-uvt1ec,9862
+sympy/plotting/intervalmath/tests/test_interval_membership.py,sha256=D1KjcrLxAwOmDEUqA-8TCqkFWGtmeerR9KwmzS7tyjk,4216
+sympy/plotting/intervalmath/tests/test_intervalmath.py,sha256=ndBMczrs6xYMN5RGnyCL9yq7pNUxrXHTSU1mdUsp5tU,9034
+sympy/plotting/plot.py,sha256=umvG47eIqOdEUo5qki3jDkS6LYKnTWFTZ5qzxZZBgAw,40882
+sympy/plotting/plot_implicit.py,sha256=1xIIr4eV1gIU4SkQ2k54PBZGUgYMCgsPttY-9uZ2eZM,7330
+sympy/plotting/plotgrid.py,sha256=QZmbxY-fcgPuseYLnKVDFsoH6m57Cosvy4W7l4jIqWw,6115
+sympy/plotting/pygletplot/__init__.py,sha256=DM7GURQbdSfcddHz23MxOShatBFc26tP_sd3G8pGCQE,3732
+sympy/plotting/pygletplot/color_scheme.py,sha256=NgPUamkldygfrIPj0LvC_1AzhscVtg18FSudElvFYB8,12522
+sympy/plotting/pygletplot/managed_window.py,sha256=N7AKtM7ELfIJLie6zvI-J6-OQRBnMZu6AL1USz7hFEk,3072
+sympy/plotting/pygletplot/plot.py,sha256=s-5AJB0KelHs9WGoFIVIdYrOoMXfdpnM5-G2cF8xzDQ,13352
+sympy/plotting/pygletplot/plot_axes.py,sha256=Q9YN8W0Hd1PeflHLvOvSZ-hxeLU4Kq3nUFLYDC0x0E8,8655
+sympy/plotting/pygletplot/plot_camera.py,sha256=myYtKbv1ov_ltgR34hf8BR76t3AwTSu4QFUY5YY-e1E,3928
+sympy/plotting/pygletplot/plot_controller.py,sha256=MroJJSPCbBDT8gGs_GdqpV_KHsllMNJpxx0MU3vKJV8,6941
+sympy/plotting/pygletplot/plot_curve.py,sha256=YwKA2lYC7IwCOQJaOVnww8AAG4P36cArgbC1iLV9OFI,2838
+sympy/plotting/pygletplot/plot_interval.py,sha256=doqr2wxnrED4MJDlkxQ07GFvaagX36HUb77ly_vIuKQ,5431
+sympy/plotting/pygletplot/plot_mode.py,sha256=Djq-ewVms_JoSriDpolDhhtttBJQdJO8BD4E0nyOWcQ,14156
+sympy/plotting/pygletplot/plot_mode_base.py,sha256=3z3WjeN7TTslHJevhr3X_7HRHPgUleYSngu6285lR6k,11502
+sympy/plotting/pygletplot/plot_modes.py,sha256=gKzJShz6OXa6EHKar8SuHWrELVznxg_s2d5IBQkkeYE,5352
+sympy/plotting/pygletplot/plot_object.py,sha256=qGtzcKup4It1CqZ2jxA7FnorCua4S9I-B_7I3SHBjcQ,330
+sympy/plotting/pygletplot/plot_rotation.py,sha256=KGMCTyaSh5_UQD9FKGCIpoI3-CbbOhXxTcjAc1o5-PU,1445
+sympy/plotting/pygletplot/plot_surface.py,sha256=C0q9tzDmxzC1IpWiNKY4llzcopx6dhotGOLpK1N9m3s,3803
+sympy/plotting/pygletplot/plot_window.py,sha256=5boC2Fkmk46-gWGqWzdTkPmTMNHHOpA0CnB9q946Hwc,4643
+sympy/plotting/pygletplot/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/plotting/pygletplot/tests/test_plotting.py,sha256=NisjR-yuBRJfQvjcb20skTR3yid2U3MhKHW6sy8RE10,2720
+sympy/plotting/pygletplot/util.py,sha256=mzQQgDDbp04B03KyJrossLp8Yq72RJzjp-3ArfjbMH8,4621
+sympy/plotting/series.py,sha256=hPkMicAi29RjoJkFpMCt1CRn0xdBVYU1wLniigHgCaM,96568
+sympy/plotting/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/plotting/tests/test_experimental_lambdify.py,sha256=EYshdXA5tAGWolaDX-nHAolp7xIJN4Oqb1Uc1C1IhJI,3127
+sympy/plotting/tests/test_plot.py,sha256=mXiNdG-dHkK1iJ_moNlehZQU3i8mCtUKhVJdTxSxjdA,48217
+sympy/plotting/tests/test_plot_implicit.py,sha256=wurrO7ntsHb4tWYPVs5VogRrtcylevu0EceCSEwiWQg,5799
+sympy/plotting/tests/test_region_and.png,sha256=EV0Lm4HtQPk_6eIWtPY4TPcQk-O7tkpdZIuLmFjGRaA,6864
+sympy/plotting/tests/test_region_not.png,sha256=3O_9_nPW149FMULEcT5RqI2-k2H3nHELbfJADt2cO8k,7939
+sympy/plotting/tests/test_region_or.png,sha256=5Bug09vyog-Cu3mky7pbtFjew5bMvbpe0ZXWsgDKfy4,8809
+sympy/plotting/tests/test_region_xor.png,sha256=kucVWBA9A98OpcR4did5aLXUyoq4z0O4C3PM6dliBSw,10002
+sympy/plotting/tests/test_series.py,sha256=Nwa6__YMoWCiY0mYSlvJPPnlvtfOYLACHR1RbkopMc0,65638
+sympy/plotting/tests/test_textplot.py,sha256=VurTGeMjUfBLpLdoMqzJK9gbcShNb7f1OrAcRNyrtag,12761
+sympy/plotting/tests/test_utils.py,sha256=FkcYZAFT8TnHRIbkknx9NvA3-LgR0ua7WFyzQEPsVIE,3602
+sympy/plotting/textplot.py,sha256=Xc-bWvzVOq8QUn_BxDlJv9bryCpgzsgV73XoQQwVj0Q,5075
+sympy/plotting/utils.py,sha256=Cimno9MQGjcbe4rQ6TN86r4rM2t62CgARqPtgF0mhR8,12259
+sympy/polys/__init__.py,sha256=qXmDNr7noCUAtIs0rJJRHHFbHjy3uHIvXZDajcSePc4,5577
+sympy/polys/agca/__init__.py,sha256=fahpWoG_0LgoqOXBnDBJS16Jj1fE1_VKG7edM3qZ2HE,130
+sympy/polys/agca/extensions.py,sha256=YmtFs9C0s-4DNMXFtdX1hYVNlby18mAJzhJ5Aqickrw,9388
+sympy/polys/agca/homomorphisms.py,sha256=gaMNV96pKUuYHZ8Bd7QOs27J1IbbJgkEjyWcTLe8GFI,21937
+sympy/polys/agca/ideals.py,sha256=S6rBl3H-hdeI44ZbELwjjt1rKlrhK11AKb8Aas-OtCE,11073
+sympy/polys/agca/modules.py,sha256=PeA138FHsCfUn8vkdrjPa92xmzq7Qqk0PrdYwVoNqf0,47240
+sympy/polys/agca/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/polys/agca/tests/test_extensions.py,sha256=i3IHQNXQByFMCvjjyd_hwwJSCiUj0z1rRwS9WFK2AFc,6455
+sympy/polys/agca/tests/test_homomorphisms.py,sha256=m0hFmcTzvZ8sZbbnWeENwzKyufpE9zWwZR-WCI4kdpU,4224
+sympy/polys/agca/tests/test_ideals.py,sha256=w76qXO-_HN6LQbV7l3h7gJZsM-DZ2io2X-kPWiHYRNw,3788
+sympy/polys/agca/tests/test_modules.py,sha256=HdfmcxdEVucEbtfmzVq8i_1wGojT5b5DE5VIfbTMx3k,13552
+sympy/polys/appellseqs.py,sha256=hWeDKsKnJuAuPN_5IU6m1okurAq9xMt3LQgMehcvBKQ,8305
+sympy/polys/benchmarks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/polys/benchmarks/bench_galoispolys.py,sha256=8RtN9ZQga2oxscVPPkMGB29Dz8UbskMS2szYtqZ69u0,1502
+sympy/polys/benchmarks/bench_groebnertools.py,sha256=YqGDCzewRszCye_GnneDXMRNB38ORSpVu_Jn0ELIySo,803
+sympy/polys/benchmarks/bench_solvers.py,sha256=gLrZguh6pE0E4_vM2GeOS5bHnrcSUQXqD0Qz9tItfmo,446778
+sympy/polys/compatibility.py,sha256=ti_gJSz8zB1sf-oIyfXAw4xkDGkQaZs_34RSC-6HldY,58373
+sympy/polys/constructor.py,sha256=rv8hQgE8-P9QXUtOQmlEiyA4XD-fb1Qzd8bvH3UHQls,11373
+sympy/polys/densearith.py,sha256=6wB6DJWKNB0RA7Tdg7jMgRIERjZdtAp32OQhIQ6wmV4,34114
+sympy/polys/densebasic.py,sha256=Kag6cqlxHhHUMYUXpr9GHLWh4E1Mnw2Esoh6zx9r14E,35954
+sympy/polys/densetools.py,sha256=A0Z1E16eVx577zw84m8KZgT5o6dbDXXonW8TqfISJZY,29746
+sympy/polys/dispersion.py,sha256=s6GIYnGA6U9jhGP7YXQQS8G3byG4-kPbr55BR6p-iz4,5740
+sympy/polys/distributedmodules.py,sha256=t8pLIgDQs_dMecGXwybVYoLavofEy2DXhFS8N5gj5SU,21827
+sympy/polys/domainmatrix.py,sha256=FmNqklNFQR1WrQYtP2r7jypw2IQadNKGP14EaUaxUqI,310
+sympy/polys/domains/__init__.py,sha256=T6qPNkU1EJ6D5BnvyJSXJv4zeJ5MUT5RLsovMkkXS9E,1872
+sympy/polys/domains/algebraicfield.py,sha256=JXK_o6BdQbYT_0gOLALNOLsmqgfV46DRFJb7cANJgAM,23051
+sympy/polys/domains/characteristiczero.py,sha256=vHYRUXPrfJzDF8wrd1KSFqG8WzwfITP_eweA-SHPVYA,382
+sympy/polys/domains/complexfield.py,sha256=VbhX6yigmU2OLhof8qEpPp1XZhzzkfXfcuZevx1LZsk,6159
+sympy/polys/domains/compositedomain.py,sha256=DAo2ISA9XdOnYzFu8azuPIQAT9fyVwSM8Pe425vmvww,1642
+sympy/polys/domains/domain.py,sha256=aj9HOp98G2m25wRzSDj4lWzoRLGyXDsuevzScdHjV6o,40850
+sympy/polys/domains/domainelement.py,sha256=IrG-Mzv_VlCAmE-hmJVH_d77TrsfyaGGfJVmU8FFvlY,860
+sympy/polys/domains/expressiondomain.py,sha256=AB7Mnd6kOLaS_yG4lMXZTpVPUuAHGcrd6RAjjFSVxNc,7593
+sympy/polys/domains/expressionrawdomain.py,sha256=cXarD2jXi97FGNiqNiDqQlX0g764EW2M1PEbrveImnY,1448
+sympy/polys/domains/field.py,sha256=fR0l6wZIv02UMJ5rioEP910jGUViVx38EdwtJuJX5vg,2959
+sympy/polys/domains/finitefield.py,sha256=1DHkfrCz08KXLEz7in4IK71B8udHPZZAWv50-jV5_vQ,10654
+sympy/polys/domains/fractionfield.py,sha256=BoqUCbVggwaoqGCVy_r42MIVHuAV-P3gONFVZIlWojU,5848
+sympy/polys/domains/gaussiandomains.py,sha256=YrcHg4QSPgIRW-Ft8J84q1zwmU-DVJuAscWXnVCQ9CI,19598
+sympy/polys/domains/gmpyfinitefield.py,sha256=WgSLnALNOVVKH931WpVT28ZWDilsrTDG8DyMee2xR94,437
+sympy/polys/domains/gmpyintegerring.py,sha256=qJ7w8K40cfzhztZtOuWlIL2DXa642xJcKIIxoAOlcSs,3037
+sympy/polys/domains/gmpyrationalfield.py,sha256=dZjrfcWaUA-BHUtutzLOWPlOSNLYzBqSFeukER6L_bA,3178
+sympy/polys/domains/groundtypes.py,sha256=hAla27w5ekoJR_8c-1Yo8vrEgIIggPc615tfe1udc9A,2102
+sympy/polys/domains/integerring.py,sha256=4oy49xTi8hV6qh8CWUAoBcgn2aJqgqwyh7bZBsjGfwI,7442
+sympy/polys/domains/modularinteger.py,sha256=k6gskb0eypXdrKJRxR3l_75mVjmICGnaOy7FdRMwG8E,6042
+sympy/polys/domains/mpelements.py,sha256=PREDWq_WA5er2zzftt5Lg0opnWzyFBsIkLKcyGXUVz0,5042
+sympy/polys/domains/old_fractionfield.py,sha256=TUVxyL2fS4QF3kgyW5EGfkl91ir3S1klu08UfZr3GuI,6226
+sympy/polys/domains/old_polynomialring.py,sha256=KQcH58oHnHzOpDdWojZiLlHDqrAiUd4OAaBIZigqpyc,15982
+sympy/polys/domains/polynomialring.py,sha256=OLPcwkyrbkt5pNZ342o3Ziv9TCrlpU3h18s9R-cxqXI,6223
+sympy/polys/domains/pythonfinitefield.py,sha256=lWp266ErnuUPuo7k8ju3z2S5IresFInpJAl4Ihsq0pI,453
+sympy/polys/domains/pythonintegerring.py,sha256=EH5s6nwaxmeaScLER_FfqPdhyuJnbjtBslHmgyOyEPs,2962
+sympy/polys/domains/pythonrational.py,sha256=M3VUGODh3MLElePjYtjt9b02ReMThw-XXpuQTkohgNs,548
+sympy/polys/domains/pythonrationalfield.py,sha256=x8BPkGKj0WPuwJzN2py5l9aAjHaY4djv65c4tzUTr3Y,2295
+sympy/polys/domains/quotientring.py,sha256=2NOUkkbFj4514qkDUszl6hl1EQuPikn3QEoQ1sDobGI,5911
+sympy/polys/domains/rationalfield.py,sha256=D-pA-iHDHbOi6fivqSrJnfmH2JTrheKJQ9ZFXXN5ftM,5982
+sympy/polys/domains/realfield.py,sha256=OdLfE_9OVxvQlnQiT4faJxuB6dZwnXdIt-o-5wGYroA,6456
+sympy/polys/domains/ring.py,sha256=p66U2X58acSHLHxOTU6aJZ0Umdcu1qiGIUDtV8iJCD0,3236
+sympy/polys/domains/simpledomain.py,sha256=_K-Zz8Opf505r3eHSrbPAlnGiGSjY_O4Cwa4OTeOSoY,369
+sympy/polys/domains/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/polys/domains/tests/test_domains.py,sha256=XJwTGPM-8W4Rn76-cec99rpUIDDjP-7HTFs7AKGNrEw,51028
+sympy/polys/domains/tests/test_polynomialring.py,sha256=8nNFKuQeicwiBk6pnqPLNk0cnI61iK8Dpl-TZifvORc,2895
+sympy/polys/domains/tests/test_quotientring.py,sha256=BYoq1CqI76RDSm0xQdp1v7Dv1n5sdcmes-b_y_AfW-0,1459
+sympy/polys/euclidtools.py,sha256=hAsNupzfR-07fTwFvTF2yDabyKMm_PLgFC8KX9c2CAo,41808
+sympy/polys/factortools.py,sha256=CupycipegP1IGCYPY9eY2T8f_6h4gmr6qId1A4UDJvk,43034
+sympy/polys/fglmtools.py,sha256=Z3VS_IKsZu1o3g22KPTIH1feNL8cTpieaNWvLuILmK4,4298
+sympy/polys/fields.py,sha256=RdSDkOls6n87yvHG1KXFnGng0JS3SAcUAlz4tTb1RZE,21250
+sympy/polys/galoistools.py,sha256=Z3ed_A0Nohj9RzuM3kom7-ay4MnzIHppwgV2QpONuTo,57238
+sympy/polys/groebnertools.py,sha256=NhK-XcFR9e4chDDJJ-diXb7XYuw9zcixFA_riomThPM,23342
+sympy/polys/heuristicgcd.py,sha256=rD3intgKCtAAMH3sqlgqbJL1XSq9QjfeG_MYzwCOek0,3732
+sympy/polys/matrices/__init__.py,sha256=ZaPJMi8l22d3F3rudS4NqzSt0xwxbs3uwnQwlhhR91o,397
+sympy/polys/matrices/_dfm.py,sha256=_3p5Hb9e86uBG_r__Ee6BAZxj4KiRhcf2uN3XnjHxwQ,32971
+sympy/polys/matrices/_typing.py,sha256=Egp2nMOaq-oJCW0bgNDxy1Bx6evTl8eMTpf4mzIw1s4,407
+sympy/polys/matrices/ddm.py,sha256=eMKUy8X1kQTEG1l7Fx_L4LG9Jqs9l0KEwlYQwlcl6Do,32362
+sympy/polys/matrices/dense.py,sha256=RYdogVHeWZoeO94Oal1mf1sJvj-JmyS_baRi8-d9Rms,23217
+sympy/polys/matrices/dfm.py,sha256=Fj_uF4FskrwcBDNuRSV--AozCP2cYkUz-BMitcMlkRE,1241
+sympy/polys/matrices/domainmatrix.py,sha256=MvjWRzQ4lKzX2rCl8mRtpiIXSe641_x6OMrTr8iscPA,115928
+sympy/polys/matrices/domainscalar.py,sha256=sPXC-it46yun2r_tJkun4MIuVQQyQMQTVsTedId8Rj4,3778
+sympy/polys/matrices/eigen.py,sha256=glArxs9_rTrE_ssz2fd2gKCTsguSyEHwoeQP82tmIcM,3015
+sympy/polys/matrices/exceptions.py,sha256=ay3Lv21X3QqszysBN71xdr9KGQuC5kDBl90a2Sjx6pM,1351
+sympy/polys/matrices/linsolve.py,sha256=p86jlJP9h3CxZX92xk2sh1WAvKBkB5_YD55k1oYpmGI,7534
+sympy/polys/matrices/lll.py,sha256=p5q_rb5QvAuHsGRZAzP2Jf_h2vdC_Be4Q62lkaYxW-c,3550
+sympy/polys/matrices/normalforms.py,sha256=6MJfVbiwlZuCk-0fYiyOMuqkPdVpyzTNdDP2s6jkRw0,17122
+sympy/polys/matrices/rref.py,sha256=5YK_phB382K9JFVIydlH2IYpziirEDUnKzyl5BfS2_o,15532
+sympy/polys/matrices/sdm.py,sha256=bb2fIO42KjyJuEuMBkiDFn_qLJVjD4gsRgWZ6GOmTHw,64293
+sympy/polys/matrices/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/polys/matrices/tests/test_ddm.py,sha256=BcnUntz3XR2Xyff02l4Mk2EsxwYlIBzM4lFSRo9oZT8,16699
+sympy/polys/matrices/tests/test_dense.py,sha256=ETC2h5yLKsPJ5GAgx_RUcWyR63iZx81pbtOZiWty1r0,9516
+sympy/polys/matrices/tests/test_domainmatrix.py,sha256=4QttFR5PYJg6Xn37VXdxmv1w9Xkm82fF-M-KRQIe0RU,50006
+sympy/polys/matrices/tests/test_domainscalar.py,sha256=HEFEKX5tP6SZ83_91nvLmFqgHxbVdpqOP4ZwzfFbHnc,3740
+sympy/polys/matrices/tests/test_eigen.py,sha256=T1lYZeW-0NwDxDOG6ZJLr-OICfxY2wa0fVHV2V6EXSk,3200
+sympy/polys/matrices/tests/test_fflu.py,sha256=7En_75QemdtHvR7lUyZrcHiV9Dv2rswBuuKVOPWi1pk,8542
+sympy/polys/matrices/tests/test_inverse.py,sha256=5DT5qvfS3MlrgNgeHnG8GLjgLnmhsxj94yR3cbtmEO8,5247
+sympy/polys/matrices/tests/test_linsolve.py,sha256=ur1BFzlIQyBrO_-aL71lXsLC67nfec9G5o-mzW_TFY4,3373
+sympy/polys/matrices/tests/test_lll.py,sha256=RGYTDGbLfFvAMTESv-S1kqSWzwtOIjmguqXO3yGCjH4,6624
+sympy/polys/matrices/tests/test_normalforms.py,sha256=x1-vkjXq3LGblUSJFz7z73IVmaxqZbVzWoGj9XS-yXw,5106
+sympy/polys/matrices/tests/test_nullspace.py,sha256=eAEDPlnVkKfFM9jn1gztOUQTos1Sm9qwH10C-t9uLUE,5418
+sympy/polys/matrices/tests/test_rref.py,sha256=mWTIfKAUP3vkGKhffCrPHuC_0DdF-iH41cchlSN8Pqc,25982
+sympy/polys/matrices/tests/test_sdm.py,sha256=fSE3bQlDU-atzTFTgp4AgAS3QL-7rvb_61stj3QGBnU,13415
+sympy/polys/matrices/tests/test_xxm.py,sha256=X9sP_VfPi4bUR7V1ZLq6ZHXPOBUA7tE5-gQULang_Ag,29778
+sympy/polys/modulargcd.py,sha256=gOwojISiV8IncaEHZJZMHOC1odN7xRKN5L1Ls4szROI,58729
+sympy/polys/monomials.py,sha256=6cDCTVNNPDeee5bt0RQnGuYnsO_fefioHA5mEDKHSuA,18458
+sympy/polys/multivariate_resultants.py,sha256=6bwdF-lcUqtKoVDrnOKhm_PyPfo8w0Yyy_GtESatQFw,15248
+sympy/polys/numberfields/__init__.py,sha256=ZfhC9MyfGfGUz_DT_rXasB-M_P2zUiZXOJUNh_Gtm8c,538
+sympy/polys/numberfields/basis.py,sha256=IPA6cSwz-53ClQwo-wkmRzfx9pRX4iBhiggdLMVSgJ0,8261
+sympy/polys/numberfields/exceptions.py,sha256=kHb-aB4eBzG3SsIpYtL5wLExDSb8lIOpNq0tk3guFIA,1594
+sympy/polys/numberfields/galois_resolvents.py,sha256=OL3u-G6sCwvZuBuuYQO0QpL-wWxtjxFaBjVQhtiQ_Z8,25006
+sympy/polys/numberfields/galoisgroups.py,sha256=c4s6z_mEUkoWKezFrfjKiU2tZnEnxR4m1LYaagicVno,20671
+sympy/polys/numberfields/minpoly.py,sha256=0F7QVSOk8WqViD50jKtT-94C6kQyuXPBCyNZPTf0ScA,27791
+sympy/polys/numberfields/modules.py,sha256=4MJykT6gtT_LC033LWsHT_CM7nEydLISAdQ0yA_bhkQ,69243
+sympy/polys/numberfields/primes.py,sha256=UXOkuMdnamVvHPQZxwilCbhdwNNNS7zQ4bwSFc5xGgk,23984
+sympy/polys/numberfields/resolvent_lookup.py,sha256=qfLNKOz_WjtXwpVlfzy8EkD4gw12epx9npE9HsjyIdg,40411
+sympy/polys/numberfields/subfield.py,sha256=Huh9iq2CDQN8p0fs2bSe-yEoB_V1OVr8rH58rJT4crk,16668
+sympy/polys/numberfields/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/polys/numberfields/tests/test_basis.py,sha256=96BJ7e4oPDKXyvlRrUkiQxmHyjRGpOkAC7R3ln-jgNE,4580
+sympy/polys/numberfields/tests/test_galoisgroups.py,sha256=3LFuMbV92VBFlqqEqjh37oQvmG8cgZ0pFxDCXUoYRL4,5036
+sympy/polys/numberfields/tests/test_minpoly.py,sha256=HjlmPWX90Cb-XK5G1H0-uirOiHdgTu7R8qpcG5ZI5iw,23115
+sympy/polys/numberfields/tests/test_modules.py,sha256=GU4166j_hMlB22uWxxIjV_ON8RsyvpaN7Ly3eK8_m8Y,22926
+sympy/polys/numberfields/tests/test_numbers.py,sha256=M0vZIBnjPBHV4vFUnPBILaqiR_cgSuU50kFB-v7l1gA,5988
+sympy/polys/numberfields/tests/test_primes.py,sha256=JhcAkaQMgjkOSziQ2jZApJ8b8oviil5cUy0hfFqNmZg,9779
+sympy/polys/numberfields/tests/test_subfield.py,sha256=EpBexToc17YwLB4-n6nE_HaleU8AcFGi5OpDReW7q0k,12776
+sympy/polys/numberfields/tests/test_utilities.py,sha256=rQGEJWctcfzjUtMwRuyCHerSqEsoP5C3z1bnddJA17o,3661
+sympy/polys/numberfields/utilities.py,sha256=IHKmfafE9tMGammfdtkyR4_IHb0mON-OBwitE0Ss1R0,13087
+sympy/polys/orderings.py,sha256=IFieyj4LkFa7NDiGTZD3VwUY7mSN3GEjThKk0z5WJ1s,8500
+sympy/polys/orthopolys.py,sha256=PIJg_Kml8MxmJs5QPCjGmRLjjlE_4QKhsWhU7-an7x0,10181
+sympy/polys/partfrac.py,sha256=ptYpXsp8dYyyeiPnLU-rdQ-uv3-GAvDnN79E81C68y8,14695
+sympy/polys/polyclasses.py,sha256=Zc9sjiwVxsM6wM8h9KCFP2Pjlfl4XA3MjC6NlguQzqM,96529
+sympy/polys/polyconfig.py,sha256=mgfFpp9SU159tA_PM2o04WZyzMoWfOtWZugRcHnP42c,1598
+sympy/polys/polyerrors.py,sha256=xByI-fqIHVYsYRm63NmHXlSSRCwSI9vZUoO-1Mf5Wlk,4744
+sympy/polys/polyfuncs.py,sha256=OEZpdYeHQADBJYqMw8JAyN4sw-jsJ6lzVH6m-CCoK8g,8547
+sympy/polys/polymatrix.py,sha256=iNa-EyIrzv7ZeXMaP_PjojgNd29AnIotZE3NeF2te44,9771
+sympy/polys/polyoptions.py,sha256=yG1k4ZPN2ufwpZ4lI4Sn8DSIHvd7EwDUHUaz64HxlUM,21806
+sympy/polys/polyquinticconst.py,sha256=mYLFWSBq3H3Y0I8cx76Z_xauLx1YeViC4xF6yWsSTPQ,96035
+sympy/polys/polyroots.py,sha256=bJToZlR6WZNl9fNxkIJxiPt3XoDQ_K6Pb3UOCBPfsKM,37025
+sympy/polys/polytools.py,sha256=BUwK8wJApazWeKQ3t27yg-HTMnwiv9ScPiavk2n02Rk,212876
+sympy/polys/polyutils.py,sha256=M8V-D4NkfMlsJe4LeAZzljrM4BKp10Qvuuutd8kmp3E,17248
+sympy/polys/puiseux.py,sha256=wc_5w5RyXHfAwC9Dde-6ecPm9y1z5DAmfthtSZ53CqA,26518
+sympy/polys/rationaltools.py,sha256=8vbkg3nuBxbd4ztR7lOj2jTF9taKUKTPah_fD38Ex6c,2838
+sympy/polys/ring_series.py,sha256=7DijmljyAaQrupWB0JmaNwGm3wNTstnEsBln-o1uX1M,59877
+sympy/polys/rings.py,sha256=KVdA3C04G2H3YN81rM-8ORsUHqmktIrz0S0tfwLwN4g,86080
+sympy/polys/rootisolation.py,sha256=QKCYgD4kMNBtSLYkUim8MXHa7louMtlO0riwKTiXRRI,64341
+sympy/polys/rootoftools.py,sha256=bMcVUcIwqaM643UU1WTCNmPmDFRepZEq9Xfdr9mm63Q,43092
+sympy/polys/solvers.py,sha256=FareKqDVVC6P2I4yjudi_3CS3kx0SbmAz3k7zHkETgE,13520
+sympy/polys/specialpolys.py,sha256=B2vijl75zgUKUTY1HCqjB9BTDFf3FM8ugwkKGTB83XA,11038
+sympy/polys/sqfreetools.py,sha256=oRFlOLhcXz5OKjc9SQtOuEhMvacvkHyTSEqhk6Snh1Y,19876
+sympy/polys/subresultants_qq_zz.py,sha256=VFKTAe3iGMCimLkc9GeuwG5o4SB0x2T5kRwxe4A5BDo,88241
+sympy/polys/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/polys/tests/test_appellseqs.py,sha256=YTERuRr30QtfxYR0erXvJG8D-INe9RaMFAF0ZM-H4Ks,3820
+sympy/polys/tests/test_constructor.py,sha256=t6JpLbIdy2hcHF4_s_Xiav0kVc4OOsH5EFg6IXOiwmI,7220
+sympy/polys/tests/test_densearith.py,sha256=ptFGN7ovPH3dqT5khr6Wm8CR6a1WOR7Fi8dj8SuBvJs,40827
+sympy/polys/tests/test_densebasic.py,sha256=FcjAfZngYRQSWJ__BrBPITAUYjMNkS_pOtGyuJv_Bs0,21590
+sympy/polys/tests/test_densetools.py,sha256=o7HZfOw4Xn71RpCDhk98srNipA3zXfikAP_lV0gShlM,26241
+sympy/polys/tests/test_dispersion.py,sha256=S3diTgz7WKH88ulGNT3ijiQ8Y53Rpul6jATBJMGyQdw,3182
+sympy/polys/tests/test_distributedmodules.py,sha256=dXmjhozX5Yzb7DsrtbdFTqAxi9Z1UZNJvGxj-vHM7cM,7639
+sympy/polys/tests/test_euclidtools.py,sha256=vEyj48eIjm6-KRQtThNfI4ic_VDNB6l7jMouxJAF9HE,19482
+sympy/polys/tests/test_factortools.py,sha256=K5_R-01Muc_45XOtolk_tQswE2l6tGrlOJ9zA_aNVYE,24903
+sympy/polys/tests/test_fields.py,sha256=DUvcax7WGtydkLDXq0aQ9Ksc6PkdV67NPZCvqTYN2w8,9869
+sympy/polys/tests/test_galoistools.py,sha256=0oN3eSWvV99juZSXco6Ek9n6s6BFOmOE4UIEhyZnQQs,28532
+sympy/polys/tests/test_groebnertools.py,sha256=ZWHBcCCOVNwDxuJWg1WPo0krTHx1m1wTPi2cOYPsAT4,18584
+sympy/polys/tests/test_heuristicgcd.py,sha256=87Yc0on955VExFyOgJuxBZhsIMFz1Vq4vclIVkQ--cE,4297
+sympy/polys/tests/test_hypothesis.py,sha256=LdokFa3JrQ59_umY15x21QSScPjJoCl8RxHRfn1NUOc,1109
+sympy/polys/tests/test_injections.py,sha256=EONGggBUNWaVSwi817CzLBYJgkTehFq8-m-Qdqes984,1286
+sympy/polys/tests/test_modulargcd.py,sha256=2SjCEjCPkf1-u-Co5vuVg5sJmfBLSY3gkFLicLl4g6E,9043
+sympy/polys/tests/test_monomials.py,sha256=YcMBU1qe6g7EJojtV89dfJZmdXsXVTSly2J5cF6SD6o,11083
+sympy/polys/tests/test_multivariate_resultants.py,sha256=DJu8CcZ3xwx8njpjDeSOyhyxeqZYmhfb7dkSCU-ll7Y,9501
+sympy/polys/tests/test_orderings.py,sha256=bdsIsqJTFJCVyZNRMAGVDXVk79ldw9rmAGejS_lwKP0,4254
+sympy/polys/tests/test_orthopolys.py,sha256=Ob5Osxd8hwebEjK3dDMarZ2VDXzl3pdsEdz_-kMa80M,6560
+sympy/polys/tests/test_partfrac.py,sha256=VfWOx30fZu_avMbWqxtbLDcLi-9x6iuQmLfyatnFc3Y,7838
+sympy/polys/tests/test_polyclasses.py,sha256=EuoT3b9a9KRTQFe95MjGrQIqAhRgLG5qUo42FtkM-Js,14765
+sympy/polys/tests/test_polyfuncs.py,sha256=VbgCgCRE06dtSY9I9GSdPH9T52ETYYoxk4J3N1WBtd4,4520
+sympy/polys/tests/test_polymatrix.py,sha256=pl2VrN_d2XGOVHvvAnaNQzkdFTdQgjt9ePgo41soBRs,7353
+sympy/polys/tests/test_polyoptions.py,sha256=z9DUdt8K3lYkm4IyLH1Cv-TKe76HP-EyaRkZVsfWb6U,12416
+sympy/polys/tests/test_polyroots.py,sha256=m0rnjXMqX59XdFn9X4rRtZJjI39vBN-MAt9cpQszq-I,26803
+sympy/polys/tests/test_polytools.py,sha256=JfaZlcguRIh6ZVNpkIUc2sSRJX_Hq6aluW7HxjCDWO4,139212
+sympy/polys/tests/test_polyutils.py,sha256=Qs3QQl0WYmTnkYE2ovTxdLeu6DYnWO_OoUmLwNDZzSw,11547
+sympy/polys/tests/test_puiseux.py,sha256=4PnBdJuYDWku8l6Jg2Dsn7umg14ldYBdvHqtVaaELbE,7304
+sympy/polys/tests/test_pythonrational.py,sha256=vYMlOTuYvf-15P0nKTFm-uRrhUc-nCFEkqYFAPLxg08,4143
+sympy/polys/tests/test_rationaltools.py,sha256=wkvjzNP1IH-SdubNk5JJ7OWcY-zNF6z3t32kfp9Ncs0,2397
+sympy/polys/tests/test_ring_series.py,sha256=j88HGfiJ9ZL114UezdJtC5aWtox8RG0I2Qyq9gLnkCE,31697
+sympy/polys/tests/test_rings.py,sha256=q8A_CeerrzGqVf1Ol-O-Jc-R7q-2X119YSNMnds3Ccs,48735
+sympy/polys/tests/test_rootisolation.py,sha256=x-n-T-Con-8phelNa05BPszkC_UCW1C0yAOwz658I60,32724
+sympy/polys/tests/test_rootoftools.py,sha256=1cv7M6EQ3mK7NhcwKqbIvour1l_QYtW_zr6vyZ3rzs0,23414
+sympy/polys/tests/test_solvers.py,sha256=kKyEvmqt91HHuAV42DS26B8fPDFQ2Ghtb_Nf3-1eWyI,13644
+sympy/polys/tests/test_specialpolys.py,sha256=v1i0RMNlixxe2EbwqoE-UlV7a2KY-md4T7thhUhWSx0,5031
+sympy/polys/tests/test_sqfreetools.py,sha256=m8ipJE28YC9eOrsVVAqxduCsw4iOW-s_nw76y0E8wUs,4886
+sympy/polys/tests/test_subresultants_qq_zz.py,sha256=bSFOjbtyIClbJHLDvvzgmOgoZyC_5Vdpe7vtW9VGU_s,13219
+sympy/printing/__init__.py,sha256=Hh-z1idh92pk17XjdXkjYwb8cJ5coEr4ZJQGcpE3d9I,2215
+sympy/printing/aesaracode.py,sha256=_iHvF9avR9MGiOnxLIwog2M5rUwhLRt-bdsbcJHeO_s,18921
+sympy/printing/c.py,sha256=qX8EkxVT5Z2Hncz8DJh8iar_jBuVcRDEiIt1d6jcerE,27011
+sympy/printing/codeprinter.py,sha256=QUAWM6H45YBkd7fX6jXXO3j-tfVaHhzxbXn7OnSm0Es,42371
+sympy/printing/conventions.py,sha256=hvN1VMZE5krMj8FL4AAuxAO0xCwhAugBFWrFQ0355AY,2586
+sympy/printing/cxx.py,sha256=vCQyzT-1eNLLDJy4NBwCp5X5OCoPOZ9icsx8YifaPsc,6123
+sympy/printing/defaults.py,sha256=YitLfIRfFH8ltNd18Y6YtBgq5H2te0wFKlHuIO4cvo8,135
+sympy/printing/dot.py,sha256=W0J798ZxBdlJercffBGnNDTp7J2tMdIYQkE_KIiyi3s,8274
+sympy/printing/fortran.py,sha256=zHVQMphFeeIGl-1x29Hw5Lekxi5NWou-V3Kn4O0oHQQ,28635
+sympy/printing/glsl.py,sha256=wsjPcpyzk92m4I2_re0cn3JwEjPmRlmlVWX9owhzM5w,20310
+sympy/printing/gtk.py,sha256=ptnwYxJr5ox3LG4TCDbRIgxsCikaVvEzWBaqIpITUXc,466
+sympy/printing/jscode.py,sha256=BORODo9Aio62FKh1HYlluG-IxtJC26_-IWrxAiEtmr8,11981
+sympy/printing/julia.py,sha256=V_Qbld0MOebIkT4VnmyP6VdIbgrPaqllgjkv7eSq8bk,23541
+sympy/printing/lambdarepr.py,sha256=BCx4eSdG8MQ8ZSUV1lWEd3CzbZ4IiMid-TTxPoV6FHU,8305
+sympy/printing/latex.py,sha256=jCqXe1XOWLNUcRNzsHOY3CMvbkPBKjf8tD4ii6PVKxU,123669
+sympy/printing/llvmjitcode.py,sha256=NjjHbgheH4j6SboFIDfrrIogkcrJRkW_kjlzM8DpDmQ,17127
+sympy/printing/maple.py,sha256=aG7t4RUEVojSFBVA14GDBzU702KtTcolTCelXozNiN0,10571
+sympy/printing/mathematica.py,sha256=to8LmNkhiP0ACh-Vun-hfaMnLFSEc0mF2OmZnQmihE8,12720
+sympy/printing/mathml.py,sha256=UT7g45oLTqvnrC1IUZeqywHTV6-LUClfuOF_U4AMlaI,76634
+sympy/printing/numpy.py,sha256=Kpq-ySkQSRKYCxtgQZEi_4lj3EmK7FgvbDJTzvB77Tc,21049
+sympy/printing/octave.py,sha256=1ofeBaIcoJfgzImezfPed9Z4dZ6Tk5gVbEQbr02TcLI,25469
+sympy/printing/precedence.py,sha256=hKIin20F4LJp2l5yhSQ-WAegTRHYQLhccABC1_ciI-U,5522
+sympy/printing/pretty/__init__.py,sha256=pJTe-DO4ctTlnjg1UvqyoeBY50B5znFjcGvivXRhM2U,344
+sympy/printing/pretty/pretty.py,sha256=KmEXRb6XEtI5tiQYssueibKKrJVF1F1mwrF1uHGxmpM,105024
+sympy/printing/pretty/pretty_symbology.py,sha256=v4FyDz_03mbSGX7tGni8Zw2L2qKTRGvmCIL85ttXg0s,24112
+sympy/printing/pretty/stringpict.py,sha256=UbG55kC4ve8-Dby_v0cm8AXUdLcu4YDLXVnA8F_iV8Y,19158
+sympy/printing/pretty/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/printing/pretty/tests/test_pretty.py,sha256=K2Xt--ptdxWLuUmtYJnuyh81ZrGIDRqNM_iOUADAdjA,187716
+sympy/printing/preview.py,sha256=3KacgHEch3KKJZeKc7kvD-L4pfftI8Gm62fx9-Vipak,14089
+sympy/printing/printer.py,sha256=z1phdIHrrcU2cyD-1GaBXREmhkcVl8bxeIWZrGAlczg,15868
+sympy/printing/pycode.py,sha256=LsDnREZEJVw0zsy_AjRSkYER-acomwQiDicrWBmnoW8,27048
+sympy/printing/python.py,sha256=oxGJhNQUXRKjHSEgqroGAyB-A4ZgJQqA_o7C8uXA1U8,3347
+sympy/printing/pytorch.py,sha256=HXEAoLbgbuE-SYj0hvDHeJ0zgzOVDBkK5qWErLCJhNo,10620
+sympy/printing/rcode.py,sha256=uBazXZwSRd95UUQjNoD6qLlpun7Xwlm8cfLG4qYhy3Y,14101
+sympy/printing/repr.py,sha256=GLO6WFOunZ8k0Om0g1VbGW2Y5uOP0IVEo_pIGO8HoNw,11545
+sympy/printing/rust.py,sha256=5Dh_zFxXZRVzbfHGowRQd9moUjzJZ7G2PXjxqsmBBw0,20831
+sympy/printing/smtlib.py,sha256=u2rVmprPITJdLq79FTYm7Fl-osteadr1JyXeQXlZW68,21752
+sympy/printing/str.py,sha256=dTK_CdT-Mm_zRoOL-Mwb4ocBA__h2wBFAAg6VvAiebQ,33239
+sympy/printing/tableform.py,sha256=pz4hXDLZ1IlqU8eOo0XBX1xaALYMqe4qHPUnEVq0mAE,11797
+sympy/printing/tensorflow.py,sha256=QmWoLUKpefBYJZ-J0Ei0NHHrU7Livp8Td4trWTDFzy8,8161
+sympy/printing/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/printing/tests/test_aesaracode.py,sha256=dX-SKJHfx9m91NSlt7pQ5Gul0x7fig3XZsHCt2kvtRc,21340
+sympy/printing/tests/test_c.py,sha256=etIEgJwlGWxtH6VRYg3Zkjyw_ktYRYj_-mvEMQehmjI,31227
+sympy/printing/tests/test_codeprinter.py,sha256=rVimgQTdJchy7PsjdZupQmXfqi6FBme2BPwnz7Z5WeQ,2142
+sympy/printing/tests/test_conventions.py,sha256=yqPpU3F0WcbxImPBBAHd3YEZpkFGfcq_TLK4WN_gtP4,5257
+sympy/printing/tests/test_cupy.py,sha256=E2T4a82XITbWAssmRZD4zCQdMCrpaZFCAjGXthUp8cY,1882
+sympy/printing/tests/test_cxx.py,sha256=0XQPQdtDCgEKVfpuelEB-79f7d2fe9QPgEKCCYVAI_w,3183
+sympy/printing/tests/test_dot.py,sha256=TSAtgGIgK_JbY-RMbQgUvnAI87SJqeJOqzcLjAobhKM,4648
+sympy/printing/tests/test_fortran.py,sha256=d5uy1zp0IgaBPupzXpNuk169YZYygnodPWhNRcvUtAQ,35456
+sympy/printing/tests/test_glsl.py,sha256=cfog9fp_EOFm_piJwqUcSvAIJ78bRwkFjecwr3ocCak,28421
+sympy/printing/tests/test_gtk.py,sha256=94gp1xRlPrFiALQGuqHnmh9xKrMxR52RQVkN0MXbUdA,500
+sympy/printing/tests/test_jax.py,sha256=5sOhmFNadFAKT2FumDuk5UqgOc0e0cYkRnJTczClnG8,11062
+sympy/printing/tests/test_jscode.py,sha256=ObahZne9lQbBiXyJZLohjQGdHsG2CnWCFOB8KbFOAqQ,11369
+sympy/printing/tests/test_julia.py,sha256=7KSmbPi_0J2ZSL2fI_xLWqoR9Gy73LfNtzFANHS8EiM,14014
+sympy/printing/tests/test_lambdarepr.py,sha256=DMoIfRNmUkF0KUXoNnJ_skNPU42tJ2xlEvqCT2GxwaA,6947
+sympy/printing/tests/test_latex.py,sha256=xHG_gSlEsD-QkOIiHUyWE4zb0brJxwgwGs1jJBvvOA0,138061
+sympy/printing/tests/test_llvmjit.py,sha256=EGPeRisM60_TIVgnk7PTLSm5F-Aod_88zLjHPZwfyZ8,5344
+sympy/printing/tests/test_maple.py,sha256=dhcz2bqapB_qMyBLMgsgnvVRIcHQ-HmxvAxC_Y0Z3q8,13078
+sympy/printing/tests/test_mathematica.py,sha256=oNNTHZzHviNi_ckua2issBb05IS7GaYyluBJiCjhGoM,10954
+sympy/printing/tests/test_mathml.py,sha256=yVIJYHlFNLbt1qtbKrqjxB2P88dsD6AtS9EVsDi_vEw,99434
+sympy/printing/tests/test_numpy.py,sha256=JZwyiFUS7Vfiwn3j1ogF31PJLJRzH57dtEHq_ujV9wQ,11715
+sympy/printing/tests/test_octave.py,sha256=WOJpsuGhMYLw2ikjwdTV6kVac6-I3xkjZ7qvUVheFbU,18545
+sympy/printing/tests/test_precedence.py,sha256=Za6JCTVvRGZhNY2dqiA8XpUpR5YGck7Op2cXiV78dZA,4067
+sympy/printing/tests/test_preview.py,sha256=dSVxiGqdNR6gbF40V4J2tGhQ-T4RDvSyGypHvYcPDYM,988
+sympy/printing/tests/test_pycode.py,sha256=txwQ1jKkQNZSOXoKw9w2n-_IeIdd-QUjh7WYvOgi1rA,18363
+sympy/printing/tests/test_python.py,sha256=HN7JkzQcKSnB6718i7kaEJZ5pYMqu56z1mSmHQGzY4k,8128
+sympy/printing/tests/test_rcode.py,sha256=TXyl449eCO4J-P3DQb3w9FWvMqOdXmfuiXgyBg_inQQ,13781
+sympy/printing/tests/test_repr.py,sha256=5XmDdIDlQlCWckWq8H95Fw82h-oDxrRpMWZePb6hHa4,12980
+sympy/printing/tests/test_rust.py,sha256=ClZ30PUmZj1ZoBWL2zSt4SsWQS0WB8FrFSQE-w3bxjU,11988
+sympy/printing/tests/test_smtlib.py,sha256=S-42OTOUGkZ-k17qCQ36DYgT-tyOeYqDCvtXzrL2Kzo,22553
+sympy/printing/tests/test_str.py,sha256=QhRuH-hwpXRcPTmKClxqBv8HQdlpNjR4dMXf_XJ3jCs,43523
+sympy/printing/tests/test_tableform.py,sha256=Ff5l1QL2HxN32WS_TdFhUAVqzop8YoWY3Uz1TThvVIM,5692
+sympy/printing/tests/test_tensorflow.py,sha256=-56L5IdYszZy9CGNxS8Ix8UaFStZudFuKQlyyz6Rzzs,17100
+sympy/printing/tests/test_theanocode.py,sha256=E36Fj72HxMK0e1pKTkoTpv9wI4UvwHdVufo-JA6dYq0,21394
+sympy/printing/tests/test_torch.py,sha256=yOtB4yl1DjbTbb8I_t2bdW8fAafVC5G8ubeIJpVLPus,16842
+sympy/printing/tests/test_tree.py,sha256=_8PGAhWMQ_A0f2DQLdDeMrpxY19889P5Ih9H41RZn8s,6080
+sympy/printing/theanocode.py,sha256=d1Aznwfqz6lFR680RGrIf5ht2AH0Ei7pz-7yGZt-CLY,19094
+sympy/printing/tree.py,sha256=tQNjLV3_c3QUUce068AbzAuCiUIJb47l_7F_TYxxFUQ,3868
+sympy/release.py,sha256=oitamisYLp3lrFI3uGv-5ZCJIeYOT9gZLcszUmVNhw8,23
+sympy/sandbox/__init__.py,sha256=IaEVOYHaZ97OHEuto1UGthFuO35c0uvAZFZU23YyEaU,189
+sympy/sandbox/indexed_integrals.py,sha256=svh4xDIa8nGpDeH4TeRb49gG8miMvXpCzEarbor58EE,2141
+sympy/sandbox/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/sandbox/tests/test_indexed_integrals.py,sha256=UK2E2wg9EMwda4Vwpzyj3rmXs6ni33HqcbyaqAww6ww,1179
+sympy/series/__init__.py,sha256=DYG9oisjzYeS55dIUpQpbAFcoDz7Q81fZJw36PRGu14,766
+sympy/series/acceleration.py,sha256=Phe8jpZ4uiS4vYAzh8PiUoHKVUjyu5K9MoAnZX3pV_w,3365
+sympy/series/approximants.py,sha256=tE-hHuoW62QJHDA3WhRlXaTkokCAODs1vXgjirhOYiQ,3181
+sympy/series/aseries.py,sha256=cHVGRQaza4ayqI6ji6OHNkdQEMV7Bko4f4vug2buEQY,255
+sympy/series/benchmarks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/series/benchmarks/bench_limit.py,sha256=2PtdeeJtD6qyEvt9HFNvyTnMM8phFZRjscgnb4fHndU,173
+sympy/series/benchmarks/bench_order.py,sha256=iC8sQJ0lLlTgiXltAyLzSCQ-3490cf-c6NFiIU44JSk,207
+sympy/series/formal.py,sha256=9HJp9fcNX-MxcIfTtMptdKEyAw6cQQ3LyyzMyEt-9aw,51668
+sympy/series/fourier.py,sha256=H3zDg_S5D0Qeewp-o87_t_30TIg3rad83bzAoz9yp_E,22941
+sympy/series/gruntz.py,sha256=fhHr8w8SeMd-6GKjHmlwajd4FUmzeYo97AA3C_q0lKg,23497
+sympy/series/kauers.py,sha256=PzD0MATMNjLjPi9GW5GQGL6Uqc2UT-uPwnzhi7TkJH8,1720
+sympy/series/limits.py,sha256=hoSFWORWR-DodSf6Po93uK_09JUePhNvI60YsH6V4CI,13307
+sympy/series/limitseq.py,sha256=WM1Lh3RXhSZM1gQaJrhWnUtYEgJunLujIEw1gmtVhYw,7752
+sympy/series/order.py,sha256=1JjdsH2zvP8X1rf75qpfIB-iI70fkcH9CmJsXsRqSxY,19558
+sympy/series/residues.py,sha256=k46s_fFfIHdJZqfst-B_-X1R-SAWs_rR9MQH7a9JLtg,2213
+sympy/series/sequences.py,sha256=WLubAZr5AevpllgI4b6WmIfXRX88QNVCpMGBofGkcqo,35543
+sympy/series/series.py,sha256=PveJ6LibsrDX-mymTbV7teNggnsX0cfaoGMLAP0KAIo,1863
+sympy/series/series_class.py,sha256=033NJ5Re8AS4eq-chmfct3-Lz2vBqdFqXtnrbxswTx0,2918
+sympy/series/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/series/tests/test_approximants.py,sha256=KViHMW1dPXn7xaPYhtTQ9L_WtLLkoIic6yfFnwZ8Q70,1012
+sympy/series/tests/test_aseries.py,sha256=5yWntog5ogRZrmjF3ZhY4e-d2mHYC1Ocs7IXRRFQrtI,2377
+sympy/series/tests/test_demidovich.py,sha256=JGYacqJMEqHS6oT2AYs9d7iutIEb32PkJs9EJqOHxcQ,4947
+sympy/series/tests/test_formal.py,sha256=TpMws7tgQHaokLrzEKZdlOcn6f04WIZhUqreaOOJ0kU,22496
+sympy/series/tests/test_fourier.py,sha256=-RnlhZUZJuctdE3pEtVMeGzCfyWzR1TFeojDhpJqyWo,5908
+sympy/series/tests/test_gruntz.py,sha256=3yPq8ksMGM7x4VQIfsFkk3JIgHlj-g1kJJBwIL76uPg,16445
+sympy/series/tests/test_kauers.py,sha256=Z85FhfXOOVki0HNGeK5BEBZOpkuB6SnKK3FqfK1-aLQ,1102
+sympy/series/tests/test_limits.py,sha256=YuwcBy8Atf-UGP8Li2sFU0guV_ZsjHqx-IdPwI6M3EA,48638
+sympy/series/tests/test_limitseq.py,sha256=QjEF99sYEDqfY7ULz1qjQTo6e0lIRUCflEOBgiDYRVA,5691
+sympy/series/tests/test_lseries.py,sha256=GlQvlBlD9wh02PPBP6zU83wmhurvGUFTuCRp44B4uI4,1875
+sympy/series/tests/test_nseries.py,sha256=uzhzYswSOe9Gh_nWKeO69tvGPMLd-9tqk4HBYX8JIm4,18284
+sympy/series/tests/test_order.py,sha256=AUlRkYKCUIs4DyT6g94ZGmjzT2fqHutPXOUGhpz4mb0,17901
+sympy/series/tests/test_residues.py,sha256=pT9xzPqtmfKGSbLLAxgDVZLTSy3TOxyfq3thTJs2VLw,3178
+sympy/series/tests/test_sequences.py,sha256=Oyq32yQZnGNQDS2uJ3by3bZ-y4G9c9BFfdQTcVuW2RM,11161
+sympy/series/tests/test_series.py,sha256=yPqSIfTytdiIYlYR9yvK7uS4IwF1fctpDiiktEnwvec,17006
+sympy/sets/__init__.py,sha256=3vjCm4v2esbpsVPY0ROwTXMETxns_66bG4FCIFZ96oM,1026
+sympy/sets/conditionset.py,sha256=mhodBVrMqJ6W5H8CuaFhO3FO9VdJifOEPjjFmV9lT2I,7792
+sympy/sets/contains.py,sha256=AlsfOc_6V0TH_6G7XTPIXhDywgtg3ECdjCTfSOmPC-E,1829
+sympy/sets/fancysets.py,sha256=tZ4wVasc937Yr0VkQuLVHe75Falcr72Nrkhk89178QQ,48187
+sympy/sets/handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/sets/handlers/add.py,sha256=_ucFvxuDv9wsmKxGkCDUERtYk3I_tQxjZjY3ZkroWs0,1863
+sympy/sets/handlers/comparison.py,sha256=WfT_vLrOkvPqRg2mf7gziVs_6cLg0kOTEFv-Nb1zIvo,1601
+sympy/sets/handlers/functions.py,sha256=jYSFqFNH6mXbKFPgvIAIGY8BhbLPo1dAvcNg4MxmCaI,8381
+sympy/sets/handlers/intersection.py,sha256=oYPmrx3FAkGLVWT3EjSimeMfsGqPsqVnpJm5UxQzuCM,17514
+sympy/sets/handlers/issubset.py,sha256=azka_5eOaUro3r3v72PmET0oY8-aaoJkzVEK7kuqXCA,4739
+sympy/sets/handlers/mul.py,sha256=XFbkOw4PDQumaOEUlHeQLvjhIom0f3iniSYv_Kau-xw,1842
+sympy/sets/handlers/power.py,sha256=84N3dIus7r09XV7PF_RiEpFRw1y5tOGD34WKzSM9F-4,3186
+sympy/sets/handlers/union.py,sha256=lrAdydqExnALUjM0dnoM-7JAZqtbgLb46Y2GGmFtQdw,4225
+sympy/sets/ordinals.py,sha256=GSyaBq7BHJC3pvgoCDoUKZQ0IE2VXyHtx6_g5OS64W4,7641
+sympy/sets/powerset.py,sha256=vIGnSYKngEPEt6V-6beDOXAOY9ugDLJ8fXOx5H9JJck,2913
+sympy/sets/setexpr.py,sha256=jMOQigDscLTrFPXvHqo1ODVRG9BqC4yn38Ej4m6WPa0,3019
+sympy/sets/sets.py,sha256=-iuLnPWLunCa0oOErHELO9EkLZXs6L2AFWQgkCFbJcA,81202
+sympy/sets/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/sets/tests/test_conditionset.py,sha256=UeutWuBCT5PZicNkkl9E94pREnU5CvLGWUfmRS29PcU,11370
+sympy/sets/tests/test_contains.py,sha256=ec6WRzriwV9nurz3jS9IXEqtfL1pZedtJFp--fSsBvY,1561
+sympy/sets/tests/test_fancysets.py,sha256=oiuu5PKNrDeDO-NtgsE4CpkwQaS7JNgCYuSyc1ykKKE,51938
+sympy/sets/tests/test_ordinals.py,sha256=L4DYc6ByQMDwJGFzJC3YhfSrVk5auW7pf4QYpJ5xY7w,2637
+sympy/sets/tests/test_powerset.py,sha256=nFvDGlhAf0wG-pZnPkgJjfwDHrTwdro3MYIinwyxn94,4805
+sympy/sets/tests/test_setexpr.py,sha256=E--SjYVzrmau0EbD8g4NTqp6aLD8qHzIuI7sAfuWxpY,14797
+sympy/sets/tests/test_sets.py,sha256=oMtqcr9gr68itfWH4j18POID8tD0hAZUV8HCz7-KKj0,68544
+sympy/simplify/__init__.py,sha256=MH1vkwHq0J5tNm7ss8V6v-mjrDGUXwfOsariIwfi38c,1274
+sympy/simplify/_cse_diff.py,sha256=Nq1gXn27o1v83t__SQUuacnOohhywa39ucPaR2jD518,10479
+sympy/simplify/combsimp.py,sha256=UzgG6eC_eIzieMS3j0oQFJnS5XkZ1gxNqOSzBqJcIFQ,3604
+sympy/simplify/cse_main.py,sha256=tcUmXXFGIZyoX4PvBEmZ-YYm-CBLXoAyGLHverxP23o,31310
+sympy/simplify/cse_opts.py,sha256=ZTCaOdOrgtifWxQmFzyngrLq9uwzByBdiSS5mE-DDoE,1618
+sympy/simplify/epathtools.py,sha256=fV3oeD2J3LJRQ4YoTV6gq3yFT_NSZi-7xxKnrX-2KJA,10122
+sympy/simplify/fu.py,sha256=exevmk1p4YgEAO-MmTgh7cG7cyVMMF8p7vZ1ymwwKiI,62208
+sympy/simplify/gammasimp.py,sha256=yzfkD_q1Zxu1eMCZuprmA3VzZXhecX3K0xdZlUdYlcY,18485
+sympy/simplify/hyperexpand.py,sha256=sTWd1-VeLr0NTr-cm6nftjmO_gVPqa1P9ud_rvSZS54,84420
+sympy/simplify/hyperexpand_doc.py,sha256=E8AD0mj8ULtelDSUkmJKJY7kYm5fVfCL4QH_DX65qEw,521
+sympy/simplify/powsimp.py,sha256=aCkX2zxgOm1S9oWc260KrzhSwcJw8p3JEMZ-vkmUapM,26754
+sympy/simplify/radsimp.py,sha256=VP83Ufl_9DUtmeHCRXSmOHqdg9zDsPoQJ_7eUyCiwpI,41649
+sympy/simplify/ratsimp.py,sha256=vTu0t0k1FDFTofl4mwcK9NuTMNZ20IsL8fIyFJRFNkg,7682
+sympy/simplify/simplify.py,sha256=3rK7cCfkh1svliMVsCuTv9_aQDz3nmZvA_HRAvmNkSo,73330
+sympy/simplify/sqrtdenest.py,sha256=MsTyPUcEhiMf51fpk6e5grlqudU-LMAHOuatPCxo5uA,21600
+sympy/simplify/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/simplify/tests/test_combsimp.py,sha256=O95WSxCvo2fDQs-UlarAcSf0_8M3PuTR76lhREDoNA8,2958
+sympy/simplify/tests/test_cse.py,sha256=JOjH5G6GPfeaKpKdeeKL1vvHgbt8TGxTkqmH2e1ovY8,25516
+sympy/simplify/tests/test_cse_diff.py,sha256=Y_zYZpldwjUoWiTTpFMlw4PXkqNp9r9Zwrw46gkmNoY,7275
+sympy/simplify/tests/test_epathtools.py,sha256=ugsQlfuK6POiixdeit63QovsVAlG5JyCaPlPp0j35LE,3525
+sympy/simplify/tests/test_fu.py,sha256=X2NNsynwefg2aH5GhyxQItL80fKPZ9md6nydMEedWbQ,19410
+sympy/simplify/tests/test_function.py,sha256=gzdcSFObuDzVFJDdAgmERtZJvG38WNSmclPAdG8OaPQ,2199
+sympy/simplify/tests/test_gammasimp.py,sha256=32cPRmtG-_Mz9g02lmmn-PWDD3J_Ku6sxLxIUU7WqxE,5320
+sympy/simplify/tests/test_hyperexpand.py,sha256=y3lxd97UZ1BkrrZe0r2l6MM8zNABTE_L4KtxFwmdzeQ,41043
+sympy/simplify/tests/test_powsimp.py,sha256=FWI_K8nozwYzhrhY1Q0CZkf6n88hFR0jjuYRvQnMius,14375
+sympy/simplify/tests/test_radsimp.py,sha256=Gf6Tf7-lb5G8gqF6hJA6kPrZNFES6fCz7_-ls7TFUfY,19128
+sympy/simplify/tests/test_ratsimp.py,sha256=bv-K60A7m2on-U9szzaeYO7Hlp1EK5P0HvBBO59oyas,2198
+sympy/simplify/tests/test_rewrite.py,sha256=LZj4V6a95GJj1o3NlKRoHMk7sWGPASFlw24nsm4z43k,1127
+sympy/simplify/tests/test_simplify.py,sha256=QkiaVVj0nqHXvu1lUUYZfYZ6_Ods6DfiBwFiqAJKSoo,41928
+sympy/simplify/tests/test_sqrtdenest.py,sha256=4zRtDQVGpKRRBYSAnEF5pSM0AR_fAMumONu2Ocb3tqg,7470
+sympy/simplify/tests/test_trigsimp.py,sha256=vG5PDTDNOuFypT7H9DSMjIollPqkKdNhWv5FBj6vFnE,19949
+sympy/simplify/traversaltools.py,sha256=pn_t9Yrk_SL1X0vl-zVR6yZaxkY25D4MwTBv4ywnD1Y,409
+sympy/simplify/trigsimp.py,sha256=t36QKVf_0yqVCNo1hUBlBp5locL0sMH1AlsAwQCzpLQ,46857
+sympy/solvers/__init__.py,sha256=nb1LceF2MwvULXsCN8VUq7X2tVgZOPsmWYHGFszIdW4,2333
+sympy/solvers/benchmarks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/solvers/benchmarks/bench_solvers.py,sha256=ZVK2TIW0XjWRDBex054ymmVlSBQw-RIBhEL1wS2ZAmU,288
+sympy/solvers/bivariate.py,sha256=Ku4Ttid3yU3CIc4IPfJ7bedLtXVaGKvFOonEIlZrXXw,17870
+sympy/solvers/decompogen.py,sha256=dWQla7hp7A4RqI2a0qRNQLWNPEuur68lD3dVTyktdBU,3757
+sympy/solvers/deutils.py,sha256=jukqDw6JGZfl36HRFjZIX_A0_iarmTawI0Up7z5tQDc,10315
+sympy/solvers/diophantine/__init__.py,sha256=I1p3uj3kFQv20cbsZ34K5rNCx1_pDS7JwHUCFstpBgs,128
+sympy/solvers/diophantine/diophantine.py,sha256=n74XrMIW6ZwrAfaVVks7nwB01GClI0SYdvTe5K90Jnc,122482
+sympy/solvers/diophantine/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/solvers/diophantine/tests/test_diophantine.py,sha256=N7ZCyNVfSV0JzAKbnc_8YbUFq5ZkiDLnO6MxxMxCG90,43335
+sympy/solvers/inequalities.py,sha256=kz6HzAytg4V2eqSKwGzbJuYyFfgj6bFSUgk1i15zVp0,33189
+sympy/solvers/ode/__init__.py,sha256=I7RKwCcaoerflUm5i3ZDJgBIOnkhBjb83BCHcVcFqfM,468
+sympy/solvers/ode/hypergeometric.py,sha256=t9KN5AdB7yF-rRoatEBzUYBcrwvIrKm83KBDa5BJYqY,10048
+sympy/solvers/ode/lie_group.py,sha256=C2HoOVtDbIT2ryx4TzEvQU46Yqc8LurTBBtjv5m2-BQ,39175
+sympy/solvers/ode/nonhomogeneous.py,sha256=Wi6UxjYNAPcfJsEzKHNvIJ8zhJ7-ZOkqccCR93MpUuM,17875
+sympy/solvers/ode/ode.py,sha256=9UQ0bqcrFOsGuSxGfRJpd2CllIWfiUYZPC2-A7Crefo,145379
+sympy/solvers/ode/riccati.py,sha256=3ZrkCy3ufXCyCx8gJfAuxyCOgbOLEHtvTh2VgJhA7Mk,30736
+sympy/solvers/ode/single.py,sha256=yBpPVFK3LsCf07dDb9_x6tzhp5d4ln_xIpMZpP2Ryrc,109465
+sympy/solvers/ode/subscheck.py,sha256=CIPca_qTxL9z5oaD2e2NrgME0eVQgF9PabZndcVqHZM,16130
+sympy/solvers/ode/systems.py,sha256=Kok2AMO4YEKX38fi2LrxLm5zpTcNXz398b7OBvrUezU,71467
+sympy/solvers/ode/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/solvers/ode/tests/test_lie_group.py,sha256=vg1yy_-a5x1Xm2IcVkEi5cD2uA5wE5gjqpfBwkV1vZc,5319
+sympy/solvers/ode/tests/test_ode.py,sha256=bEcTi9zA5TooKOE9duFtd6TAVRvLPrO92arX7fF85-Q,48508
+sympy/solvers/ode/tests/test_riccati.py,sha256=a2_pXzCFb9WDtT_kdxYkfLd-PG5xvs4rQn_vpFk-O9s,29348
+sympy/solvers/ode/tests/test_single.py,sha256=sEvhXws920Oe_IuvmBIYM5iPViR6I3njZNgJlc8sMC4,100200
+sympy/solvers/ode/tests/test_subscheck.py,sha256=Gzwc9h9n6zlNOhJ8Qh6fQDeB8ghaRmgv3ktBAfPJx-U,12468
+sympy/solvers/ode/tests/test_systems.py,sha256=PeIEHOx8-eZQNuqOfhjTvGEeFSVriLlHShPmy84mde4,129087
+sympy/solvers/pde.py,sha256=V2jxgmmepng2ofFFDVOsBHWqf-IUQCgIz4lXzhBUeyg,34913
+sympy/solvers/polysys.py,sha256=4CCK3FNhl-K6B4bYF_goSrAq1JOMubl6KsmaB_5R2f4,27168
+sympy/solvers/recurr.py,sha256=DyssZuOyemoC6J1cWq635O7zkg1WLHrR7KGoM-gNy0g,25389
+sympy/solvers/simplex.py,sha256=peRv44aDM30U_0wk8xZv3t6M_cpm_a9hBALiyr7zndY,35276
+sympy/solvers/solvers.py,sha256=xsSlQaj0XaunexmhfQZZI_uiRXHETmaUab9hgZrmmbU,138454
+sympy/solvers/solveset.py,sha256=6VxsiKcn4fwdJGYXA8k1si5zvYs6vNrSTtLcmSKpjMk,151683
+sympy/solvers/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/solvers/tests/test_constantsimp.py,sha256=9Feugsg9jD2BwQiG4EFpb9fORyst6JdBmZqq2GaOgH8,8707
+sympy/solvers/tests/test_decompogen.py,sha256=7GUsDQQZtYbZIK0p0UxsOuNEJxEt4IHeOSsem_k-k0U,2943
+sympy/solvers/tests/test_inequalities.py,sha256=whg3vGXEYxeIHNQS4yBeB9VQpoYWnfw5NBS4xLiqDJ8,21025
+sympy/solvers/tests/test_numeric.py,sha256=pKLBJuf4lGCewf5DgBjvH5T9phf6ilkyW1gB2R-5SzA,4734
+sympy/solvers/tests/test_pde.py,sha256=UGP3uWjF8pKQgfPifmdfvS5URVmzSg6m2NkS7LGzmio,9257
+sympy/solvers/tests/test_polysys.py,sha256=bK2ckIhQvbJCc6_qsUOAuAEyTc5CR8J2yiqN4MFOgpM,15266
+sympy/solvers/tests/test_recurr.py,sha256=-OeghSg16GFN70y_RUXC6CF6VU_b7NXaKDbejtRSocg,11418
+sympy/solvers/tests/test_simplex.py,sha256=pG7j7KxXrlK8K_fF04XH_mvMzwEOopSBpJZiagKjyNs,9037
+sympy/solvers/tests/test_solvers.py,sha256=eXM6AT2upxlRC7S2l1YxUSS6jJNM42PxzYCTz4C4vmc,107765
+sympy/solvers/tests/test_solveset.py,sha256=lOQ4hG7Dg6yuDgFriKIlvWdzEumehgL08Srsh4JwDHY,148480
+sympy/stats/__init__.py,sha256=9pY3OMdvAIeJ_Q1ZqynOZ8hHWFFrrxOx0qBHKkGfHCg,8487
+sympy/stats/compound_rv.py,sha256=SO1KXJ0aHGbD5y9QA8o6qOHbio3ua8wyO2Rsh0Hnw48,7965
+sympy/stats/crv.py,sha256=jd8iemE41aW-byXgFDYKaMv2VOOUnyUtiMx_QAXI-n4,21028
+sympy/stats/crv_types.py,sha256=qXYxtvJPg4ORJI7AxIkRhwnJ6lcz3sVBWf4toQy9vvA,122371
+sympy/stats/drv.py,sha256=Zu8IK9yt69XJFKt-ShoJfZbvZ-CGMwvrKNiXvi6q_n4,11994
+sympy/stats/drv_types.py,sha256=2jL6QYa9lAlbvTxvx9BXdfwU6PRt0uzV3CVXRIeZ5k0,19865
+sympy/stats/error_prop.py,sha256=a-H6GZEidsiP_4-iNw7nSD99AMyN6DNHsSl0IUZGIAs,3315
+sympy/stats/frv.py,sha256=vZROeD6DSVGX0kfOL_yOds3pZgjSiXFX-bMZtSUkVMA,16874
+sympy/stats/frv_types.py,sha256=Xkc3MU5dFLajPajIStW_lV4qiuiHg86rBo45zX_oGLk,23348
+sympy/stats/joint_rv.py,sha256=DcixlO2Ml4gnwMmZk2VTegiHVq88DkLdQlOTQ57SQtc,15963
+sympy/stats/joint_rv_types.py,sha256=PUatR4WcPHmAHadt8iRh5xYh5NJigzYh-EoAMR5blDw,30575
+sympy/stats/matrix_distributions.py,sha256=3OricwEMM_NU8b2lJxoiSTml7kvqrNQ6IUIn9Xy_DsY,21953
+sympy/stats/random_matrix.py,sha256=NmzLC5JMDWI2TvH8tY6go8lYyHmqcZ-B7sSIO7z7oAk,1028
+sympy/stats/random_matrix_models.py,sha256=7i5XAUYxt-ekmP5KDMaytUlmCvxglEspoWbswSf82tE,15328
+sympy/stats/rv.py,sha256=rmjdVpsvJrqtMgnreuT_7lU5S_V2nkvlrpUvA7zCWLM,54579
+sympy/stats/rv_interface.py,sha256=0ZSplwmkEtio_UTlh7bqSK_1aILgvk2JTfZ-CkOcfAc,13935
+sympy/stats/sampling/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/stats/sampling/sample_numpy.py,sha256=B4ZC7ZBrSD6ICQT468rOy-xrOgQDuecsHa0zJesAeYE,4229
+sympy/stats/sampling/sample_pymc.py,sha256=9g-n04aXSFc6F7FJ5zTYtHHL6W8-26g1nrgtamJc3Hw,2995
+sympy/stats/sampling/sample_scipy.py,sha256=ysqpDy8bp1RMH0g5FFgMmp2SQuXGFkcSH7JDZEpiZ8w,6329
+sympy/stats/sampling/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/stats/sampling/tests/test_sample_continuous_rv.py,sha256=Gh8hFN1hFFsthEv9wP2ZdgghQfaEnE8n7HlmyXXhN1E,5708
+sympy/stats/sampling/tests/test_sample_discrete_rv.py,sha256=Z3hqjHEFaIoB0hrhpy7YYL_7Dtqzmy5YMWAYrlqPu38,3360
+sympy/stats/sampling/tests/test_sample_finite_rv.py,sha256=dWwrFePw8eX2rBheAXi1AVxr_gqBD63VZKfW81hNoQc,3061
+sympy/stats/stochastic_process.py,sha256=pDz0rbKXTiaNmMmmz70dP3F_KWL_XhoCKFHYBNt1QeU,2312
+sympy/stats/stochastic_process_types.py,sha256=YEUA-saTh8WF6Pn7GcgrcyclRCb_SC13YnpgOIpu7-Q,88553
+sympy/stats/symbolic_multivariate_probability.py,sha256=R6Co7XCcxLoOtTqC6ZSnGuylZNUBrC5AD0DrJr2jE1A,10450
+sympy/stats/symbolic_probability.py,sha256=HkWUiH9EM_DQ1aM1FxNmBm_8xtqeviIsyUpqFl2RYnI,23266
+sympy/stats/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/stats/tests/test_compound_rv.py,sha256=2927chbHTThA34Ki-ji319QT7ajQ1ueC640Mga-18ZA,6263
+sympy/stats/tests/test_continuous_rv.py,sha256=BcBkiyX7e1QiwS6xnD-qLzXnijvqbsebTzXEn3IfGyE,56140
+sympy/stats/tests/test_discrete_rv.py,sha256=bLqMtKpH5fDjuP7uuRAtAjakbahuFz9x3dY-PPKFsGk,11230
+sympy/stats/tests/test_error_prop.py,sha256=xKAkw3F5XJ72xiDREI7PkyReWNVW_89CD_mjOY_diDY,1933
+sympy/stats/tests/test_finite_rv.py,sha256=JHYgY4snFF5t9qcnQfKaN5zaGsO7_SuNR7Tq234W4No,20413
+sympy/stats/tests/test_joint_rv.py,sha256=W28rCRYczv5Jax7k-bj7OveT-y-AP4q-kRR0-LNaWX0,18653
+sympy/stats/tests/test_matrix_distributions.py,sha256=9daJUiSGaLq34TeZfB-xPqC8xz6vECGrm0DdBZaQPyY,8857
+sympy/stats/tests/test_mix.py,sha256=Cplnw06Ki96Y_4fx6Bu7lUXjxoIfX7tNJasm9SOz5wQ,3991
+sympy/stats/tests/test_random_matrix.py,sha256=CiD1hV25MGHwTfHGaoaehGD3iJ4lqNYi-ZiwReO6CVk,5842
+sympy/stats/tests/test_rv.py,sha256=Bp7UwffIMO7oc8UnFV11yYGcXUjSa0NhsuOgQaNRMt8,12959
+sympy/stats/tests/test_stochastic_process.py,sha256=i-VCOZrjpJtvyTBm9xgniTCkk_iUYIuFnkiyxzkn6Ig,39323
+sympy/stats/tests/test_symbolic_multivariate.py,sha256=G3AgbRbt0DQ-p0DYXYDjbx4e4f5FIgd31F34e0NO2n8,5580
+sympy/stats/tests/test_symbolic_probability.py,sha256=k5trScMiwSgl9dzJt30BV-t0KuYcyD-s9HtT2-hVhQ0,9398
+sympy/strategies/__init__.py,sha256=XaTAPqDoi6527juvR8LLN1mv6ZcslDrGloTTBMjJzxA,1402
+sympy/strategies/branch/__init__.py,sha256=xxbMwR2LzLcQWsH9ss8ddE99VHFJTY-cYiR6xhO3tj0,356
+sympy/strategies/branch/core.py,sha256=QiXSa7uhvmUBTLyUwBQHrYkWlOceKh5p4kVD90VnCKM,2759
+sympy/strategies/branch/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/strategies/branch/tests/test_core.py,sha256=23KQWJxC_2T1arwMAkt9pY1ZtG59avlxTZcVTn81UPI,2246
+sympy/strategies/branch/tests/test_tools.py,sha256=4BDkqVqrTlsivQ0PldQr6PjVZsAikc39tSxGAQA3ir8,942
+sympy/strategies/branch/tests/test_traverse.py,sha256=6rikMnZdamSzww1sSiM-aQwqa4lQrpM-DpOU9XCbiOQ,1322
+sympy/strategies/branch/tools.py,sha256=tvv3IjmQGNYbo-slCbbDf_rylZd537wvLcpdBtT-bbY,357
+sympy/strategies/branch/traverse.py,sha256=7iBViQdNpKu-AHoFED7_C9KBSyYcQBfLGopEJQbNtvk,799
+sympy/strategies/core.py,sha256=nsH6LZgyc_aslv4Na5XvJMEizC6uSzscRlVW91k1pu4,3956
+sympy/strategies/rl.py,sha256=-M5lfrtcREkvbW4YfoiB19wfYyMLKz6SmL887ymKeoI,4404
+sympy/strategies/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/strategies/tests/test_core.py,sha256=42XHlv1hN1S1QPEf2r9pddZ2EQL6o4FEPQvfo-UmXcw,2152
+sympy/strategies/tests/test_rl.py,sha256=wm0L6pdvddBgRcwhpiSk-nCgyzVGickfnOCkmHWS0j4,1949
+sympy/strategies/tests/test_tools.py,sha256=UdMojFIn3f1b2x2iRGv1Wfnwdso-Kl57GTyjCU_DjzQ,875
+sympy/strategies/tests/test_traverse.py,sha256=jWuZhYEt-F18_rxEMhn6OgGQ1GNs-dM_GFZ2F5nHs2I,2082
+sympy/strategies/tests/test_tree.py,sha256=9NL948rt6i9tYU6CQz9VNxE6l1begQs-MxP2euzE3Sc,2400
+sympy/strategies/tools.py,sha256=ERASzEP2SP-EcJ8p-4XyREYB15q3t81x1cyamJ-M880,1368
+sympy/strategies/traverse.py,sha256=DhPnBJ5Rw_xzhGiBtSciTyV-H2zhlxgjYVjrNH-gLyk,1183
+sympy/strategies/tree.py,sha256=ggnP9l3NIpJsssBMVKr4-yM_m8uCkrkm191ZC6MfZjc,3770
+sympy/strategies/util.py,sha256=2fbR813IY4IYco5mBoGJLu5z88OhXmwuIxgOO9IvZO4,361
+sympy/tensor/__init__.py,sha256=VMNXCRSayigQT6a3cvf5M_M-wdV-KSil_JbAmHcuUQc,870
+sympy/tensor/array/__init__.py,sha256=lTT1EwV5tb3WAvmmS_mIjhCSWSLiB0NNPW4n9_3fu0k,8244
+sympy/tensor/array/array_comprehension.py,sha256=01PTIbkAGaq0CDcaI_2KsaMnYm1nxQ8sFAiHHcc__gw,12262
+sympy/tensor/array/array_derivatives.py,sha256=c-gYeA_qpXOY3aexyz7psSqmTVIGVBrcGDvSkW5dZV0,4796
+sympy/tensor/array/arrayop.py,sha256=KvFGYWcYvChWkThtVAotlaSrcfjHogAxvpxWvp6dSgo,18397
+sympy/tensor/array/dense_ndim_array.py,sha256=Ie8qVMJyp2Tsq7aVhmZpPX8X-KTlF9uaxkQfTzCZ9z8,6433
+sympy/tensor/array/expressions/__init__.py,sha256=OUMJjZY7HtWJL0ygqkdWC8LdCqibJZhHCfYeXu-eB4E,7045
+sympy/tensor/array/expressions/array_expressions.py,sha256=28TI8cwwQ4Q-QCI9X_j6X-lG0kR6amjAe9-h9JKb5EU,76961
+sympy/tensor/array/expressions/arrayexpr_derivatives.py,sha256=W9-bY2LL83lLSNHXItzqjOgvf-HIDbUXPoVw8uOymcg,6249
+sympy/tensor/array/expressions/conv_array_to_indexed.py,sha256=BIwlQr7RKC8bZN3mR8ICC5TYOC9uasYcV0Zc1VNKmiE,445
+sympy/tensor/array/expressions/conv_array_to_matrix.py,sha256=85YZBTZI4o9dJtKDJXXug_lJVLG8dT_22AT7l7DKoyE,416
+sympy/tensor/array/expressions/conv_indexed_to_array.py,sha256=EyW52TplBxIx25mUDvI_5Tzc8LD6Mnp6XNW9wIw9pH4,254
+sympy/tensor/array/expressions/conv_matrix_to_array.py,sha256=XYyqt0NsQSrgNpEkr8xTGeUhR7ZYeNljVFfVEF1K7vA,250
+sympy/tensor/array/expressions/from_array_to_indexed.py,sha256=3YIcsAzWVWQRJYQS90uPvSl2dM7ZqLV_qt7E9-uYU28,3936
+sympy/tensor/array/expressions/from_array_to_matrix.py,sha256=4HUbCfR2BHd54Ijws9DCcSs6aAJ-k8RwT195NTQImis,41435
+sympy/tensor/array/expressions/from_indexed_to_array.py,sha256=RUcKemmrwuK5RFRr19YSPVMCOkZfLAWlbbB56u8Wi0g,11187
+sympy/tensor/array/expressions/from_matrix_to_array.py,sha256=yIY1RupF9-FVV3jZLsqWxZ1ckoE1-HkQyM8cQIm4_Gs,3929
+sympy/tensor/array/expressions/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/tensor/array/expressions/tests/test_array_expressions.py,sha256=QUAdxQ9TvBpDEAZoJpLSWwbqjmuflPe3xBRP30lFZr0,31262
+sympy/tensor/array/expressions/tests/test_arrayexpr_derivatives.py,sha256=lpC4ly6MJLDRBcVt3GcP3H6ke9bI-o3VULw0xyF5QbY,2470
+sympy/tensor/array/expressions/tests/test_as_explicit.py,sha256=nOjFKXCqYNu2O7Szc1TD1x1bsUchPRAG3nGlNGEd1Yg,2568
+sympy/tensor/array/expressions/tests/test_convert_array_to_indexed.py,sha256=6yNxGXH6BX5607FTjMkwR2t9wNVlEhV8JMSh4UIWux8,2500
+sympy/tensor/array/expressions/tests/test_convert_array_to_matrix.py,sha256=2vkSep9CPKYrQQS0u8Ayn_sc7yek1zwzjjCWK5cfYe8,29311
+sympy/tensor/array/expressions/tests/test_convert_indexed_to_array.py,sha256=RVEG_qUsXiBH9gHtWp2-9pMC4J2aLc4iUdzBFM0QyTw,8615
+sympy/tensor/array/expressions/tests/test_convert_matrix_to_array.py,sha256=G2g5E0l-FABwYyQowbKKvLcEI8NViJXaYLW3eUEcvjw,4595
+sympy/tensor/array/expressions/tests/test_deprecated_conv_modules.py,sha256=DG8IoUtxCy2acWjUHUUKu4bRsTxXbeFLFjKMLA2GdLY,1216
+sympy/tensor/array/expressions/utils.py,sha256=Rn58boHHUEoBZFtinDpruLWFBkNBwgkVQ4c9m7Nym1o,3939
+sympy/tensor/array/mutable_ndim_array.py,sha256=M0PTt8IOIcVXqQPWe2N50sm4Eq2bodRXV4Vkd08crXk,277
+sympy/tensor/array/ndim_array.py,sha256=bZvA21fSTMc2sQLJN4AovQspddzmnINBWFPr-TeGmm0,19138
+sympy/tensor/array/sparse_ndim_array.py,sha256=4nD_Hg-JdC_1mYQTohmKFfL5M1Ugdq0fpnDUILkTtq8,6387
+sympy/tensor/array/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/tensor/array/tests/test_array_comprehension.py,sha256=e8MsWbvwmr-HxTfaM7i8HoIVofa8InLzF9PTCIVvzjU,4529
+sympy/tensor/array/tests/test_array_derivatives.py,sha256=hS10Bkb3F2kWoFoxk2ucr21p0r5DfwHDYPI8HcmAl0o,1601
+sympy/tensor/array/tests/test_arrayop.py,sha256=WahGcUnArsAo9eaMqGT7_AjKons0WgFzLOWTtNvnSEI,25844
+sympy/tensor/array/tests/test_immutable_ndim_array.py,sha256=9ji_14szn-qoL6DQ5muzIFNaXefT7n55PFigXoFwk50,15823
+sympy/tensor/array/tests/test_mutable_ndim_array.py,sha256=rFFa0o0AJYgPNnpqijl91Vb9EW2kgHGQc6cu9f1fIvY,13070
+sympy/tensor/array/tests/test_ndim_array.py,sha256=KH-9LAME3ldVIu5n7Vd_Xr36dN4frCdiF9qZdBWETu0,2232
+sympy/tensor/array/tests/test_ndim_array_conversions.py,sha256=CUGDCbCcslACy3Ngq-zoig9JnO4yHTw3IPcKy0FnRpw,648
+sympy/tensor/functions.py,sha256=FNZ2M0HGVaASJ_1AkPu1vF5NHNLmBXKLsj0Q5wKMX90,4168
+sympy/tensor/index_methods.py,sha256=dcX9kNKLHi_XXkFHBPS-fcM-PaeYKkX80jmzxC0siiQ,15434
+sympy/tensor/indexed.py,sha256=3qiQehMvXSDbr_RDXg6l_aEu6dHZXEfXrMoXCJj7Cqo,24515
+sympy/tensor/tensor.py,sha256=EQ9qJjtvij4gG101jB1eOuTtaSmvm1Da3IHVCBn1Flo,181094
+sympy/tensor/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/tensor/tests/test_functions.py,sha256=rBBHjJIUA2oR83UgEJ_GIASDWfTZXDzOllmcO90XYDU,1552
+sympy/tensor/tests/test_index_methods.py,sha256=Pu951z4yYYMOXBKcNteH63hTAxmNX8702nSQH_pciFE,7112
+sympy/tensor/tests/test_indexed.py,sha256=EB2-t0gYkaTOvrCkhyW2cEL_MVi4xBUZxbf35NA-puI,16376
+sympy/tensor/tests/test_printing.py,sha256=sUx_rChNTWFKPNwVl296QXO-d4-yemDJnkEHFislsmc,424
+sympy/tensor/tests/test_tensor.py,sha256=XQiLkOtyLLFKX_lWEqpCr2lXxQ6LK_hShj0rz7RrWpk,81236
+sympy/tensor/tests/test_tensor_element.py,sha256=1dF96FtqUGaJzethw23vJIj3H5KdxsU1Xyd4DU54EB4,908
+sympy/tensor/tests/test_tensor_operators.py,sha256=n4md5Dv7QoVSMR4cHbkEKSFDxgTTpEyxMiCAqoqyJyk,17966
+sympy/tensor/toperators.py,sha256=fniTUpdYz0OvtNnFgrHINedX86FxVcxfKj9l_l1p9Rw,8840
+sympy/testing/__init__.py,sha256=IN9aHvoksdZywonAYE0cExFnuPQs9z1E4P742aYZNnE,169
+sympy/testing/matrices.py,sha256=VWBPdjIUYNHE7fdbYcmQwQTYcIWpOP9tFn9A0rGCBmE,216
+sympy/testing/pytest.py,sha256=xiKL67rD4sKYVZ1Xqci4arPJQzrm3xWfg-m6I7eWNBM,13363
+sympy/testing/quality_unicode.py,sha256=quKFUpEUPftVGNpsj71WpRoeKZUzc8sgLjODRmHPf-w,3482
+sympy/testing/randtest.py,sha256=IKDFAm8b72Z1OkT7vpgnZjaW5LsSU_wf6g35sCkq9I0,562
+sympy/testing/runtests.py,sha256=dD9_0xtvG0wMgQtdvxDi7ePc2CL2ybNRqHKgHjJomzE,89921
+sympy/testing/runtests_pytest.py,sha256=KSqhzaRis7sx2AwctWsP6VjCbDcg6C5UCApp_LUNRuc,17801
+sympy/testing/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/testing/tests/diagnose_imports.py,sha256=VQAc0WLHgQgksu6kcQLmEVW4O98v7YgjLMzCLNZ6JfE,9508
+sympy/testing/tests/test_code_quality.py,sha256=Yzb1CAIAGTO29Ay2fsPn0-OqstAoy9FMiAMIed6iVZE,19214
+sympy/testing/tests/test_deprecated.py,sha256=wQZHs4wDNuK4flaKKLsJW6XRMtrVjMv_5rUP3WspgPA,183
+sympy/testing/tests/test_module_imports.py,sha256=5w6F6JW6K7lgpbB4X9Tj0Vw8AcNVlfaSuvbwKXJKD6c,1459
+sympy/testing/tests/test_pytest.py,sha256=iKO10Tvua1Xem6a22IWH4SDrpFfr-bM-rXx039Ua7YA,6778
+sympy/testing/tests/test_runtests_pytest.py,sha256=C4bo-SOx8BsxvT4M_0OPUiwSEXShaMqpYRoedMmbR1g,6083
+sympy/testing/tmpfiles.py,sha256=bF8ktKC9lDhS65gahB9hOewsZ378UkhLgq3QHiqWYXU,1042
+sympy/this.py,sha256=XfOkN5EIM2RuDxSm_q6k_R_WtkIoSy6PXWKp3aAXvoc,550
+sympy/unify/__init__.py,sha256=Upa9h7SSr9W1PXo0WkNESsGsMZ85rcWkeruBtkAi3Fg,293
+sympy/unify/core.py,sha256=-BCNPPMdfZuhhIWqyn9pYJoO8yFPGDX78Hn2551ABuE,7037
+sympy/unify/rewrite.py,sha256=Emr8Uoum3gxKpMDqFHJIjx3xChArUIN6XIy6NPfCS8I,1798
+sympy/unify/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/unify/tests/test_rewrite.py,sha256=BgA8zmdz9Nw-Xbu4-w3UABeWypqLvmy9VzL744EmYtE,2002
+sympy/unify/tests/test_sympy.py,sha256=UCItZJNAx9dG5F7O27pyXUF1-e6aOwkZ-cVdB6SZFZc,5922
+sympy/unify/tests/test_unify.py,sha256=4TlgchV6NWuBekJx9RGlMjx3-UwonzgIYXDytb7sBRU,3029
+sympy/unify/usympy.py,sha256=6Kxx96FXSdqXimLseVK_FkYwy2vqWhNnxMVPMRShvy4,3964
+sympy/utilities/__init__.py,sha256=nbQhzII8dw5zd4hQJ2SUyriK5dOrqf-bbjy10XKQXPw,840
+sympy/utilities/_compilation/__init__.py,sha256=uYUDPbwrMTbGEMVuago32EN_ix8fsi5M0SvcLOtwMOk,751
+sympy/utilities/_compilation/availability.py,sha256=ybxp3mboH5772JHTWKBN1D-cs6QxATQiaL4zJVV4RE0,2884
+sympy/utilities/_compilation/compilation.py,sha256=qkfDtSgSeRJ-97E5hN1asUqoDXS4nnNgYoHCA8WAmHA,22156
+sympy/utilities/_compilation/runners.py,sha256=KAinYF0m55dRdDFO5pvHBlvTxDY0ldoImqTOwX7VmL4,10237
+sympy/utilities/_compilation/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/utilities/_compilation/tests/test_compilation.py,sha256=DmdxCPlWaqgtNZPWzMVPwqRuqqJcgWBWxXfOEMZ21D0,3189
+sympy/utilities/_compilation/util.py,sha256=otSIKhM5YSYzbUNYycagG2xSj6owlQVrwIVl0rv7Owc,8611
+sympy/utilities/autowrap.py,sha256=l8noWRKmA-B814S92RK3O64YWAkARnMfqY1AQqi2S8g,42561
+sympy/utilities/codegen.py,sha256=1ieFSlxGgd9utMSPzN5NraTfoSSzlUM9V_BnGYMuMc8,81670
+sympy/utilities/decorator.py,sha256=NaZBNCDdMhG4_3esEOLZCbVQcjltvi3lrMIRCqewfR4,11184
+sympy/utilities/enumerative.py,sha256=ah4Td5KhqGdPR7EQHcUDOsYLDH64Sh2z6xm5gDpJSFg,43585
+sympy/utilities/exceptions.py,sha256=RoKY7jDIq6OsZbNSCyneWbVQb1Cw2MtOuioJlCKmBec,10570
+sympy/utilities/iterables.py,sha256=noIMRQmdSPfbg1LorRPHreozS265u4IHUmDkOvMB2po,91103
+sympy/utilities/lambdify.py,sha256=lepieExI19QvZYPrGl_WZaFwta8YZBo6AgxDnwq01ow,57816
+sympy/utilities/magic.py,sha256=ofrwi1-xwMWb4VCQOEIwe4J1QAwxOscigDq26uSn3iY,400
+sympy/utilities/matchpy_connector.py,sha256=7dSDOPbN5Y_XW6bIGVNK3dJ-gVdTB_liJ8O5rIqd28c,11948
+sympy/utilities/mathml/__init__.py,sha256=74VhxNJlvCZTm2Jh3t69N8QZRklTQjqMvqFMLVwCKQk,3388
+sympy/utilities/mathml/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/utilities/mathml/data/mmlctop.xsl,sha256=fi3CTNyg-mSscOGYBXLJv8veE_ItR_YTFMJ4jmjp6aE,114444
+sympy/utilities/mathml/data/mmltex.xsl,sha256=haX7emZOfD6_nbn5BjK93F-C85mSS8KogAbIBsW1aBA,137304
+sympy/utilities/mathml/data/simple_mmlctop.xsl,sha256=OM-Vge1satH-MAYwWhraeXcorn1KGtuqBK-PDddaOrk,114433
+sympy/utilities/memoization.py,sha256=jD6RjVMZkGpNZYaJ9481vTiqvmwyu1IKDpsF5PYIvf4,1838
+sympy/utilities/misc.py,sha256=C6C3Em9YUjqg4gUhVdu97K0Wt_gRxuhNHS0TW6MlrxQ,16000
+sympy/utilities/pkgdata.py,sha256=BiBonyObCsS6EnLUII_1kDtchDN7Tur0T4GMzoUo06M,935
+sympy/utilities/pytest.py,sha256=ifo5fkOQ94HKXjy_U__mLFSohNRGP2ft8Z4P5gAi4yw,440
+sympy/utilities/randtest.py,sha256=Qlr4MMe0ade6jUH6pIEE0-v4g4qGswJhKpdaQFS_GDw,435
+sympy/utilities/runtests.py,sha256=Js3GGuzxQDZw5Tr5Kge0DCYx9gi3eya4DZxfwib6e3Y,451
+sympy/utilities/source.py,sha256=ShIXRNtplSEfZNi5VDYD3yi6305eRz4TmchEOEvcicw,1127
+sympy/utilities/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/utilities/tests/test_autowrap.py,sha256=PA6zaRVrIWyQHq6bGKezqNB6YWTfcnnB9VH2vzaEfaQ,14865
+sympy/utilities/tests/test_codegen.py,sha256=pjfhZTa5LI9gdrakDfLtBBrIxgWjWORR_NBDW9J8Cq8,56130
+sympy/utilities/tests/test_codegen_julia.py,sha256=QYqSluVhmxq_BAagEKBb6eJIseFFAweMjpuLgNPMUWI,18540
+sympy/utilities/tests/test_codegen_octave.py,sha256=GUQIj6YjsMsj8ZLmVngWa9ovoyqfwvikWXdeS7m_c7I,17830
+sympy/utilities/tests/test_codegen_rust.py,sha256=seq9o51dsa09MmcqwKRkGiurbWLs1uAIjaz7xGOKNE4,12326
+sympy/utilities/tests/test_decorator.py,sha256=VYUvzUrVI7I7MK0YZxLLEmEu4pV5dqaB1CLEJ8Ocav4,3705
+sympy/utilities/tests/test_deprecated.py,sha256=LRrZ2UxuXnK6Jwxl8vT0EdLT-q-7jLkTC69U9JjuYYU,489
+sympy/utilities/tests/test_enumerative.py,sha256=nw-1r0MgxbzYkScmxv7HUAIAQycg8pUVOH_slDRLg7E,6097
+sympy/utilities/tests/test_exceptions.py,sha256=OKRa2yuHMtnVcnisu-xcaedi2RKsH9QrgU9exgoOK30,716
+sympy/utilities/tests/test_iterables.py,sha256=xYWyoDtVkYfnIOdA-5yUYZsxo4iTsExaNpVmjMuwpTc,35288
+sympy/utilities/tests/test_lambdify.py,sha256=emTSDwHdGprENO3ugK86k1L6m8Y3IamPj1oUNIKaYrQ,73531
+sympy/utilities/tests/test_matchpy_connector.py,sha256=mBrAev2Hxe4jg_1ny3ZaGIfh2xvWbr6BDRVjB9owJFM,4850
+sympy/utilities/tests/test_mathml.py,sha256=-6z1MRYEH4eYQi2_wt8zmdjwtt5Cn483zqsvD-o_r70,836
+sympy/utilities/tests/test_misc.py,sha256=xZh6ux9o2iZpWvfX_ivp4lD3WgJVeP5k6ipppNGHDsw,4643
+sympy/utilities/tests/test_pickling.py,sha256=ZXGzB_gQ9juws_N_n2vf6QPVojdfP4Mbl7uqdtkPb0E,23600
+sympy/utilities/tests/test_source.py,sha256=ObjrJxZFVhLgXjVmFHUy7bti9UPPgOh5Cptw8lHW9mM,289
+sympy/utilities/tests/test_timeutils.py,sha256=sCRC6BCSho1e9n4clke3QXHx4a3qYLru-bddS_sEmFA,337
+sympy/utilities/tests/test_wester.py,sha256=S79UNsGhEW5z6hDriQB8OY_Vhaz1j0Q7yv31HynyDfk,94866
+sympy/utilities/tests/test_xxe.py,sha256=xk1j0Dd96wsGYKRNDzXTW0hTQejGCfiZcEhYcYiqojg,66
+sympy/utilities/timeutils.py,sha256=PNHyvuEM66UEXLF8ye6PMilcEDK75B9wab3ai8a9cyo,1941
+sympy/utilities/tmpfiles.py,sha256=tQP3g7LTrnXTGqUgGNoIZtjug6O6lq6EYLrKPJcUGEU,450
+sympy/vector/__init__.py,sha256=bgKutVqUPWuvHWBpiEm6XWE6iGfAPJw3pt19NRn2n10,1969
+sympy/vector/basisdependent.py,sha256=P7LogUck6NWjEzAOH3dYr4A8M5QCOPaCW75lmiQpBqU,11857
+sympy/vector/coordsysrect.py,sha256=0BKdr8AG8BUJnShIIyyCvkU5OVdjtZsKcBuGgdbvdHw,36833
+sympy/vector/deloperator.py,sha256=4BJNjmI342HkVRmeQkqauqvibKsf2HOuzknQTfQMkpg,3191
+sympy/vector/dyadic.py,sha256=IOyrgONyGDHPtG0RINcMgetAVMSOmYI5a99s-OwXBTA,8571
+sympy/vector/functions.py,sha256=rmEBaV0XpZLkCPcJFojHS9CNHirV55v5sDwNqTPwqGM,15454
+sympy/vector/implicitregion.py,sha256=4-M_-shL4EJ9nfjAZ_xg00IXZVQghcCZ3bv8QGbkopI,16158
+sympy/vector/integrals.py,sha256=x8DrvKXPznE05JgnZ7I3IWLWrvFl9SEghGaFmHrBaE4,6837
+sympy/vector/kind.py,sha256=U6qxciZY7-ACzBSpGM0iSTI5tqflpPBvm4_J9ujyxyE,1812
+sympy/vector/operators.py,sha256=dNC7mNXZb3KxX5GyjFNDTLTkzSnYjOohZtBXuQ1keZE,9579
+sympy/vector/orienters.py,sha256=EtWNWfOvAuy_wipam9SA7_muKSrsP-43UPRCCz56sb0,11798
+sympy/vector/parametricregion.py,sha256=gAarunY8q3D8WVvryZEbJQ4g-nlXo4FnO-CbyEpVUXc,5932
+sympy/vector/point.py,sha256=-JFOibsQclOKC2m2qZRiGGB4oK9ieVZRd2ll5g4k3bE,4488
+sympy/vector/scalar.py,sha256=WAb25BC0TEOZ-_bEtL1gdySuM1adJzK47kJYTrC4Y4w,2024
+sympy/vector/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+sympy/vector/tests/test_coordsysrect.py,sha256=q9n9OIG_CpD4KQN20dzwRZIXoMv7VSgp8fHmVnkZfr0,19595
+sympy/vector/tests/test_dyadic.py,sha256=f1R-BL_63VBbc0XgEX_LYzV_3OupYd4hp5RzRk6dAbI,4949
+sympy/vector/tests/test_field_functions.py,sha256=v9l8Ex8K2MsPGxqAPhpEgu6WAo6wS6qvdWLKQMxgE4A,14094
+sympy/vector/tests/test_functions.py,sha256=Bs2sekdDJyw_wrUpG7vZQGH0y0S4C4AbxGSpeU_8C2s,8050
+sympy/vector/tests/test_implicitregion.py,sha256=wVilD5H-MhHiW58QT6P5U7uT79JdKHm9D7JgZoi6BE4,4028
+sympy/vector/tests/test_integrals.py,sha256=iDq3Wpr8I8EXAGkXqMpA-P9i1zEnV7lU8WR6xBOhEK0,5087
+sympy/vector/tests/test_operators.py,sha256=KexUWvc_Nwp2HWrEbhxiO7MeaFxYlckrp__Tkwg-wmU,1613
+sympy/vector/tests/test_parametricregion.py,sha256=OfKapF9A_g9X6JxgYc0UfxIhwXzRERzaj-EijQCJONw,4009
+sympy/vector/tests/test_printing.py,sha256=3BeW55iQ4qXdfDTFqptE2ufJPJIBOzdfIYVx84n_EwA,7708
+sympy/vector/tests/test_vector.py,sha256=rJpMlUWiezrY6drHUNdJc8_FDYMwGCG4lBy8zS4XSZk,10238
+sympy/vector/vector.py,sha256=aOSfyfRvkatzWHWuulY6n0_iqElI5mntAUjNPptpBiM,20322
diff --git a/.venv/lib/python3.12/site-packages/sympy-1.14.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/sympy-1.14.0.dist-info/REQUESTED
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/.venv/lib/python3.12/site-packages/sympy-1.14.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/sympy-1.14.0.dist-info/WHEEL
new file mode 100644
index 0000000000000000000000000000000000000000..8acb95590701b87bf84eec079cf4e3989f63b098
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sympy-1.14.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: setuptools (79.0.1)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/.venv/lib/python3.12/site-packages/sympy-1.14.0.dist-info/entry_points.txt b/.venv/lib/python3.12/site-packages/sympy-1.14.0.dist-info/entry_points.txt
new file mode 100644
index 0000000000000000000000000000000000000000..42a12f960335556fcee728e5754c346447d4e89c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sympy-1.14.0.dist-info/entry_points.txt
@@ -0,0 +1,2 @@
+[console_scripts]
+isympy = isympy:main
diff --git a/.venv/lib/python3.12/site-packages/sympy-1.14.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/sympy-1.14.0.dist-info/top_level.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0aa85c253508b68bb075f556be3c3f76dc4467ad
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sympy-1.14.0.dist-info/top_level.txt
@@ -0,0 +1,2 @@
+isympy
+sympy
diff --git a/.venv/lib/python3.12/site-packages/sympy/__init__.py b/.venv/lib/python3.12/site-packages/sympy/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..c67ba49409169c49ac5d34f2be365def3c30369b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sympy/__init__.py
@@ -0,0 +1,545 @@
+"""
+SymPy is a Python library for symbolic mathematics. It aims to become a
+full-featured computer algebra system (CAS) while keeping the code as simple
+as possible in order to be comprehensible and easily extensible.  SymPy is
+written entirely in Python. It depends on mpmath, and other external libraries
+may be optionally for things like plotting support.
+
+See the webpage for more information and documentation:
+
+    https://sympy.org
+
+"""
+
+
+# Keep this in sync with setup.py/pyproject.toml
+import sys
+if sys.version_info < (3, 9):
+    raise ImportError("Python version 3.9 or above is required for SymPy.")
+del sys
+
+
+try:
+    import mpmath
+except ImportError:
+    raise ImportError("SymPy now depends on mpmath as an external library. "
+    "See https://docs.sympy.org/latest/install.html#mpmath for more information.")
+
+del mpmath
+
+from sympy.release import __version__
+from sympy.core.cache import lazy_function
+
+if 'dev' in __version__:
+    def enable_warnings():
+        import warnings
+        warnings.filterwarnings('default',   '.*',   DeprecationWarning, module='sympy.*')
+        del warnings
+    enable_warnings()
+    del enable_warnings
+
+
+def __sympy_debug():
+    # helper function so we don't import os globally
+    import os
+    debug_str = os.getenv('SYMPY_DEBUG', 'False')
+    if debug_str in ('True', 'False'):
+        return eval(debug_str)
+    else:
+        raise RuntimeError("unrecognized value for SYMPY_DEBUG: %s" %
+                           debug_str)
+# Fails py2 test if using type hinting
+SYMPY_DEBUG = __sympy_debug()  # type: bool
+
+
+from .core import (sympify, SympifyError, cacheit, Basic, Atom,
+        preorder_traversal, S, Expr, AtomicExpr, UnevaluatedExpr, Symbol,
+        Wild, Dummy, symbols, var, Number, Float, Rational, Integer,
+        NumberSymbol, RealNumber, igcd, ilcm, seterr, E, I, nan, oo, pi, zoo,
+        AlgebraicNumber, comp, mod_inverse, Pow, integer_nthroot, integer_log,
+        trailing, Mul, prod, Add, Mod, Rel, Eq, Ne, Lt, Le, Gt, Ge, Equality,
+        GreaterThan, LessThan, Unequality, StrictGreaterThan, StrictLessThan,
+        vectorize, Lambda, WildFunction, Derivative, diff, FunctionClass,
+        Function, Subs, expand, PoleError, count_ops, expand_mul, expand_log,
+        expand_func, expand_trig, expand_complex, expand_multinomial, nfloat,
+        expand_power_base, expand_power_exp, arity, PrecisionExhausted, N,
+        evalf, Tuple, Dict, gcd_terms, factor_terms, factor_nc, evaluate,
+        Catalan, EulerGamma, GoldenRatio, TribonacciConstant, bottom_up, use,
+        postorder_traversal, default_sort_key, ordered, num_digits)
+
+from .logic import (to_cnf, to_dnf, to_nnf, And, Or, Not, Xor, Nand, Nor,
+        Implies, Equivalent, ITE, POSform, SOPform, simplify_logic, bool_map,
+        true, false, satisfiable)
+
+from .assumptions import (AppliedPredicate, Predicate, AssumptionsContext,
+        assuming, Q, ask, register_handler, remove_handler, refine)
+
+from .polys import (Poly, PurePoly, poly_from_expr, parallel_poly_from_expr,
+        degree, total_degree, degree_list, LC, LM, LT, pdiv, prem, pquo,
+        pexquo, div, rem, quo, exquo, half_gcdex, gcdex, invert,
+        subresultants, resultant, discriminant, cofactors, gcd_list, gcd,
+        lcm_list, lcm, terms_gcd, trunc, monic, content, primitive, compose,
+        decompose, sturm, gff_list, gff, sqf_norm, sqf_part, sqf_list, sqf,
+        factor_list, factor, intervals, refine_root, count_roots, all_roots,
+        real_roots, nroots, ground_roots, nth_power_roots_poly, cancel,
+        reduced, groebner, is_zero_dimensional, GroebnerBasis, poly,
+        symmetrize, horner, interpolate, rational_interpolate, viete, together,
+        BasePolynomialError, ExactQuotientFailed, PolynomialDivisionFailed,
+        OperationNotSupported, HeuristicGCDFailed, HomomorphismFailed,
+        IsomorphismFailed, ExtraneousFactors, EvaluationFailed,
+        RefinementFailed, CoercionFailed, NotInvertible, NotReversible,
+        NotAlgebraic, DomainError, PolynomialError, UnificationFailed,
+        GeneratorsError, GeneratorsNeeded, ComputationFailed,
+        UnivariatePolynomialError, MultivariatePolynomialError,
+        PolificationFailed, OptionError, FlagError, minpoly,
+        minimal_polynomial, primitive_element, field_isomorphism,
+        to_number_field, isolate, round_two, prime_decomp, prime_valuation,
+        galois_group, itermonomials, Monomial, lex, grlex,
+        grevlex, ilex, igrlex, igrevlex, CRootOf, rootof, RootOf,
+        ComplexRootOf, RootSum, roots, Domain, FiniteField, IntegerRing,
+        RationalField, RealField, ComplexField, PythonFiniteField,
+        GMPYFiniteField, PythonIntegerRing, GMPYIntegerRing, PythonRational,
+        GMPYRationalField, AlgebraicField, PolynomialRing, FractionField,
+        ExpressionDomain, FF_python, FF_gmpy, ZZ_python, ZZ_gmpy, QQ_python,
+        QQ_gmpy, GF, FF, ZZ, QQ, ZZ_I, QQ_I, RR, CC, EX, EXRAW,
+        construct_domain, swinnerton_dyer_poly, cyclotomic_poly,
+        symmetric_poly, random_poly, interpolating_poly, jacobi_poly,
+        chebyshevt_poly, chebyshevu_poly, hermite_poly, hermite_prob_poly,
+        legendre_poly, laguerre_poly, apart, apart_list, assemble_partfrac_list,
+        Options, ring, xring, vring, sring, field, xfield, vfield, sfield)
+
+from .series import (Order, O, limit, Limit, gruntz, series, approximants,
+        residue, EmptySequence, SeqPer, SeqFormula, sequence, SeqAdd, SeqMul,
+        fourier_series, fps, difference_delta, limit_seq)
+
+from .functions import (factorial, factorial2, rf, ff, binomial,
+        RisingFactorial, FallingFactorial, subfactorial, carmichael,
+        fibonacci, lucas, motzkin, tribonacci, harmonic, bernoulli, bell, euler,
+        catalan, genocchi, andre, partition, divisor_sigma, legendre_symbol,
+        jacobi_symbol, kronecker_symbol, mobius, primenu, primeomega,
+        totient, reduced_totient, primepi, sqrt, root, Min, Max, Id,
+        real_root, Rem, cbrt, re, im, sign, Abs, conjugate, arg, polar_lift,
+        periodic_argument, unbranched_argument, principal_branch, transpose,
+        adjoint, polarify, unpolarify, sin, cos, tan, sec, csc, cot, sinc,
+        asin, acos, atan, asec, acsc, acot, atan2, exp_polar, exp, ln, log,
+        LambertW, sinh, cosh, tanh, coth, sech, csch, asinh, acosh, atanh,
+        acoth, asech, acsch, floor, ceiling, frac, Piecewise, piecewise_fold,
+        piecewise_exclusive, erf, erfc, erfi, erf2, erfinv, erfcinv, erf2inv,
+        Ei, expint, E1, li, Li, Si, Ci, Shi, Chi, fresnels, fresnelc, gamma,
+        lowergamma, uppergamma, polygamma, loggamma, digamma, trigamma,
+        multigamma, dirichlet_eta, zeta, lerchphi, polylog, stieltjes, Eijk,
+        LeviCivita, KroneckerDelta, SingularityFunction, DiracDelta, Heaviside,
+        bspline_basis, bspline_basis_set, interpolating_spline, besselj,
+        bessely, besseli, besselk, hankel1, hankel2, jn, yn, jn_zeros, hn1,
+        hn2, airyai, airybi, airyaiprime, airybiprime, marcumq, hyper,
+        meijerg, appellf1, legendre, assoc_legendre, hermite, hermite_prob,
+        chebyshevt, chebyshevu, chebyshevu_root, chebyshevt_root, laguerre,
+        assoc_laguerre, gegenbauer, jacobi, jacobi_normalized, Ynm, Ynm_c,
+        Znm, elliptic_k, elliptic_f, elliptic_e, elliptic_pi, beta, mathieus,
+        mathieuc, mathieusprime, mathieucprime, riemann_xi, betainc, betainc_regularized)
+
+from .ntheory import (nextprime, prevprime, prime, primerange,
+        randprime, Sieve, sieve, primorial, cycle_length, composite,
+        compositepi, isprime, divisors, proper_divisors, factorint,
+        multiplicity, perfect_power, factor_cache, pollard_pm1, pollard_rho, primefactors,
+        divisor_count, proper_divisor_count,
+        factorrat,
+        mersenne_prime_exponent, is_perfect, is_mersenne_prime, is_abundant,
+        is_deficient, is_amicable, is_carmichael, abundance, npartitions, is_primitive_root,
+        is_quad_residue, n_order, sqrt_mod,
+        quadratic_residues, primitive_root, nthroot_mod, is_nthpow_residue,
+        sqrt_mod_iter, discrete_log, quadratic_congruence,
+        binomial_coefficients, binomial_coefficients_list,
+        multinomial_coefficients, continued_fraction_periodic,
+        continued_fraction_iterator, continued_fraction_reduce,
+        continued_fraction_convergents, continued_fraction, egyptian_fraction)
+
+from .concrete import product, Product, summation, Sum
+
+from .discrete import (fft, ifft, ntt, intt, fwht, ifwht, mobius_transform,
+        inverse_mobius_transform, convolution, covering_product,
+        intersecting_product)
+
+from .simplify import (simplify, hypersimp, hypersimilar, logcombine,
+        separatevars, posify, besselsimp, kroneckersimp, signsimp,
+        nsimplify, FU, fu, sqrtdenest, cse, epath, EPath, hyperexpand,
+        collect, rcollect, radsimp, collect_const, fraction, numer, denom,
+        trigsimp, exptrigsimp, powsimp, powdenest, combsimp, gammasimp,
+        ratsimp, ratsimpmodprime)
+
+from .sets import (Set, Interval, Union, EmptySet, FiniteSet, ProductSet,
+        Intersection, DisjointUnion, imageset, Complement, SymmetricDifference, ImageSet,
+        Range, ComplexRegion, Complexes, Reals, Contains, ConditionSet, Ordinal,
+        OmegaPower, ord0, PowerSet, Naturals, Naturals0, UniversalSet,
+        Integers, Rationals)
+
+from .solvers import (solve, solve_linear_system, solve_linear_system_LU,
+        solve_undetermined_coeffs, nsolve, solve_linear, checksol, det_quick,
+        inv_quick, check_assumptions, failing_assumptions, diophantine,
+        rsolve, rsolve_poly, rsolve_ratio, rsolve_hyper, checkodesol,
+        classify_ode, dsolve, homogeneous_order, solve_poly_system, factor_system,
+        solve_triangulated, pde_separate, pde_separate_add, pde_separate_mul,
+        pdsolve, classify_pde, checkpdesol, ode_order, reduce_inequalities,
+        reduce_abs_inequality, reduce_abs_inequalities, solve_poly_inequality,
+        solve_rational_inequalities, solve_univariate_inequality, decompogen,
+        solveset, linsolve, linear_eq_to_matrix, nonlinsolve, substitution)
+
+from .matrices import (ShapeError, NonSquareMatrixError, GramSchmidt,
+        casoratian, diag, eye, hessian, jordan_cell, list2numpy, matrix2numpy,
+        matrix_multiply_elementwise, ones, randMatrix, rot_axis1, rot_axis2,
+        rot_axis3, symarray, wronskian, zeros, MutableDenseMatrix,
+        DeferredVector, MatrixBase, Matrix, MutableMatrix,
+        MutableSparseMatrix, banded, ImmutableDenseMatrix,
+        ImmutableSparseMatrix, ImmutableMatrix, SparseMatrix, MatrixSlice,
+        BlockDiagMatrix, BlockMatrix, FunctionMatrix, Identity, Inverse,
+        MatAdd, MatMul, MatPow, MatrixExpr, MatrixSymbol, Trace, Transpose,
+        ZeroMatrix, OneMatrix, blockcut, block_collapse, matrix_symbols,
+        Adjoint, hadamard_product, HadamardProduct, HadamardPower,
+        Determinant, det, diagonalize_vector, DiagMatrix, DiagonalMatrix,
+        DiagonalOf, trace, DotProduct, kronecker_product, KroneckerProduct,
+        PermutationMatrix, MatrixPermute, Permanent, per, rot_ccw_axis1,
+        rot_ccw_axis2, rot_ccw_axis3, rot_givens)
+
+from .geometry import (Point, Point2D, Point3D, Line, Ray, Segment, Line2D,
+        Segment2D, Ray2D, Line3D, Segment3D, Ray3D, Plane, Ellipse, Circle,
+        Polygon, RegularPolygon, Triangle, rad, deg, are_similar, centroid,
+        convex_hull, idiff, intersection, closest_points, farthest_points,
+        GeometryError, Curve, Parabola)
+
+from .utilities import (flatten, group, take, subsets, variations,
+        numbered_symbols, cartes, capture, dict_merge, prefixes, postfixes,
+        sift, topological_sort, unflatten, has_dups, has_variety, reshape,
+        rotations, filldedent, lambdify,
+        threaded, xthreaded, public, memoize_property, timed)
+
+from .integrals import (integrate, Integral, line_integrate, mellin_transform,
+        inverse_mellin_transform, MellinTransform, InverseMellinTransform,
+        laplace_transform, laplace_correspondence, laplace_initial_conds,
+        inverse_laplace_transform, LaplaceTransform,
+        InverseLaplaceTransform, fourier_transform, inverse_fourier_transform,
+        FourierTransform, InverseFourierTransform, sine_transform,
+        inverse_sine_transform, SineTransform, InverseSineTransform,
+        cosine_transform, inverse_cosine_transform, CosineTransform,
+        InverseCosineTransform, hankel_transform, inverse_hankel_transform,
+        HankelTransform, InverseHankelTransform, singularityintegrate)
+
+from .tensor import (IndexedBase, Idx, Indexed, get_contraction_structure,
+        get_indices, shape, MutableDenseNDimArray, ImmutableDenseNDimArray,
+        MutableSparseNDimArray, ImmutableSparseNDimArray, NDimArray,
+        tensorproduct, tensorcontraction, tensordiagonal, derive_by_array,
+        permutedims, Array, DenseNDimArray, SparseNDimArray)
+
+from .parsing import parse_expr
+
+from .calculus import (euler_equations, singularities, is_increasing,
+        is_strictly_increasing, is_decreasing, is_strictly_decreasing,
+        is_monotonic, finite_diff_weights, apply_finite_diff,
+        differentiate_finite, periodicity, not_empty_in, AccumBounds,
+        is_convex, stationary_points, minimum, maximum)
+
+from .algebras import Quaternion
+
+from .printing import (pager_print, pretty, pretty_print, pprint,
+        pprint_use_unicode, pprint_try_use_unicode, latex, print_latex,
+        multiline_latex, mathml, print_mathml, python, print_python, pycode,
+        ccode, print_ccode, smtlib_code, glsl_code, print_glsl, cxxcode, fcode,
+        print_fcode, rcode, print_rcode, jscode, print_jscode, julia_code,
+        mathematica_code, octave_code, rust_code, print_gtk, preview, srepr,
+        print_tree, StrPrinter, sstr, sstrrepr, TableForm, dotprint,
+        maple_code, print_maple_code)
+
+test = lazy_function('sympy.testing.runtests_pytest', 'test')
+doctest = lazy_function('sympy.testing.runtests', 'doctest')
+
+# This module causes conflicts with other modules:
+# from .stats import *
+# Adds about .04-.05 seconds of import time
+# from combinatorics import *
+# This module is slow to import:
+#from physics import units
+from .plotting import plot, textplot, plot_backends, plot_implicit, plot_parametric
+from .interactive import init_session, init_printing, interactive_traversal
+
+evalf._create_evalf_table()
+
+__all__ = [
+    '__version__',
+
+    # sympy.core
+    'sympify', 'SympifyError', 'cacheit', 'Basic', 'Atom',
+    'preorder_traversal', 'S', 'Expr', 'AtomicExpr', 'UnevaluatedExpr',
+    'Symbol', 'Wild', 'Dummy', 'symbols', 'var', 'Number', 'Float',
+    'Rational', 'Integer', 'NumberSymbol', 'RealNumber', 'igcd', 'ilcm',
+    'seterr', 'E', 'I', 'nan', 'oo', 'pi', 'zoo', 'AlgebraicNumber', 'comp',
+    'mod_inverse', 'Pow', 'integer_nthroot', 'integer_log', 'trailing', 'Mul', 'prod',
+    'Add', 'Mod', 'Rel', 'Eq', 'Ne', 'Lt', 'Le', 'Gt', 'Ge', 'Equality',
+    'GreaterThan', 'LessThan', 'Unequality', 'StrictGreaterThan',
+    'StrictLessThan', 'vectorize', 'Lambda', 'WildFunction', 'Derivative',
+    'diff', 'FunctionClass', 'Function', 'Subs', 'expand', 'PoleError',
+    'count_ops', 'expand_mul', 'expand_log', 'expand_func', 'expand_trig',
+    'expand_complex', 'expand_multinomial', 'nfloat', 'expand_power_base',
+    'expand_power_exp', 'arity', 'PrecisionExhausted', 'N', 'evalf', 'Tuple',
+    'Dict', 'gcd_terms', 'factor_terms', 'factor_nc', 'evaluate', 'Catalan',
+    'EulerGamma', 'GoldenRatio', 'TribonacciConstant', 'bottom_up', 'use',
+    'postorder_traversal', 'default_sort_key', 'ordered', 'num_digits',
+
+    # sympy.logic
+    'to_cnf', 'to_dnf', 'to_nnf', 'And', 'Or', 'Not', 'Xor', 'Nand', 'Nor',
+    'Implies', 'Equivalent', 'ITE', 'POSform', 'SOPform', 'simplify_logic',
+    'bool_map', 'true', 'false', 'satisfiable',
+
+    # sympy.assumptions
+    'AppliedPredicate', 'Predicate', 'AssumptionsContext', 'assuming', 'Q',
+    'ask', 'register_handler', 'remove_handler', 'refine',
+
+    # sympy.polys
+    'Poly', 'PurePoly', 'poly_from_expr', 'parallel_poly_from_expr', 'degree',
+    'total_degree', 'degree_list', 'LC', 'LM', 'LT', 'pdiv', 'prem', 'pquo',
+    'pexquo', 'div', 'rem', 'quo', 'exquo', 'half_gcdex', 'gcdex', 'invert',
+    'subresultants', 'resultant', 'discriminant', 'cofactors', 'gcd_list',
+    'gcd', 'lcm_list', 'lcm', 'terms_gcd', 'trunc', 'monic', 'content',
+    'primitive', 'compose', 'decompose', 'sturm', 'gff_list', 'gff',
+    'sqf_norm', 'sqf_part', 'sqf_list', 'sqf', 'factor_list', 'factor',
+    'intervals', 'refine_root', 'count_roots', 'all_roots', 'real_roots',
+    'nroots', 'ground_roots', 'nth_power_roots_poly', 'cancel', 'reduced',
+    'groebner', 'is_zero_dimensional', 'GroebnerBasis', 'poly', 'symmetrize',
+    'horner', 'interpolate', 'rational_interpolate', 'viete', 'together',
+    'BasePolynomialError', 'ExactQuotientFailed', 'PolynomialDivisionFailed',
+    'OperationNotSupported', 'HeuristicGCDFailed', 'HomomorphismFailed',
+    'IsomorphismFailed', 'ExtraneousFactors', 'EvaluationFailed',
+    'RefinementFailed', 'CoercionFailed', 'NotInvertible', 'NotReversible',
+    'NotAlgebraic', 'DomainError', 'PolynomialError', 'UnificationFailed',
+    'GeneratorsError', 'GeneratorsNeeded', 'ComputationFailed',
+    'UnivariatePolynomialError', 'MultivariatePolynomialError',
+    'PolificationFailed', 'OptionError', 'FlagError', 'minpoly',
+    'minimal_polynomial', 'primitive_element', 'field_isomorphism',
+    'to_number_field', 'isolate', 'round_two', 'prime_decomp',
+    'prime_valuation', 'galois_group', 'itermonomials', 'Monomial', 'lex', 'grlex',
+    'grevlex', 'ilex', 'igrlex', 'igrevlex', 'CRootOf', 'rootof', 'RootOf',
+    'ComplexRootOf', 'RootSum', 'roots', 'Domain', 'FiniteField',
+    'IntegerRing', 'RationalField', 'RealField', 'ComplexField',
+    'PythonFiniteField', 'GMPYFiniteField', 'PythonIntegerRing',
+    'GMPYIntegerRing', 'PythonRational', 'GMPYRationalField',
+    'AlgebraicField', 'PolynomialRing', 'FractionField', 'ExpressionDomain',
+    'FF_python', 'FF_gmpy', 'ZZ_python', 'ZZ_gmpy', 'QQ_python', 'QQ_gmpy',
+    'GF', 'FF', 'ZZ', 'QQ', 'ZZ_I', 'QQ_I', 'RR', 'CC', 'EX', 'EXRAW',
+    'construct_domain', 'swinnerton_dyer_poly', 'cyclotomic_poly',
+    'symmetric_poly', 'random_poly', 'interpolating_poly', 'jacobi_poly',
+    'chebyshevt_poly', 'chebyshevu_poly', 'hermite_poly', 'hermite_prob_poly',
+    'legendre_poly', 'laguerre_poly', 'apart', 'apart_list', 'assemble_partfrac_list',
+    'Options', 'ring', 'xring', 'vring', 'sring', 'field', 'xfield', 'vfield',
+    'sfield',
+
+    # sympy.series
+    'Order', 'O', 'limit', 'Limit', 'gruntz', 'series', 'approximants',
+    'residue', 'EmptySequence', 'SeqPer', 'SeqFormula', 'sequence', 'SeqAdd',
+    'SeqMul', 'fourier_series', 'fps', 'difference_delta', 'limit_seq',
+
+    # sympy.functions
+    'factorial', 'factorial2', 'rf', 'ff', 'binomial', 'RisingFactorial',
+    'FallingFactorial', 'subfactorial', 'carmichael', 'fibonacci', 'lucas',
+    'motzkin', 'tribonacci', 'harmonic', 'bernoulli', 'bell', 'euler', 'catalan',
+    'genocchi', 'andre', 'partition',  'divisor_sigma', 'legendre_symbol', 'jacobi_symbol',
+    'kronecker_symbol', 'mobius', 'primenu', 'primeomega', 'totient', 'primepi',
+    'reduced_totient', 'sqrt', 'root', 'Min', 'Max', 'Id', 'real_root',
+    'Rem', 'cbrt', 're', 'im', 'sign', 'Abs', 'conjugate', 'arg', 'polar_lift',
+    'periodic_argument', 'unbranched_argument', 'principal_branch',
+    'transpose', 'adjoint', 'polarify', 'unpolarify', 'sin', 'cos', 'tan',
+    'sec', 'csc', 'cot', 'sinc', 'asin', 'acos', 'atan', 'asec', 'acsc',
+    'acot', 'atan2', 'exp_polar', 'exp', 'ln', 'log', 'LambertW', 'sinh',
+    'cosh', 'tanh', 'coth', 'sech', 'csch', 'asinh', 'acosh', 'atanh',
+    'acoth', 'asech', 'acsch', 'floor', 'ceiling', 'frac', 'Piecewise',
+    'piecewise_fold', 'piecewise_exclusive', 'erf', 'erfc', 'erfi', 'erf2',
+    'erfinv', 'erfcinv', 'erf2inv', 'Ei', 'expint', 'E1', 'li', 'Li', 'Si',
+    'Ci', 'Shi', 'Chi', 'fresnels', 'fresnelc', 'gamma', 'lowergamma',
+    'uppergamma', 'polygamma', 'loggamma', 'digamma', 'trigamma', 'multigamma',
+    'dirichlet_eta', 'zeta', 'lerchphi', 'polylog', 'stieltjes', 'Eijk', 'LeviCivita',
+    'KroneckerDelta', 'SingularityFunction', 'DiracDelta', 'Heaviside',
+    'bspline_basis', 'bspline_basis_set', 'interpolating_spline', 'besselj',
+    'bessely', 'besseli', 'besselk', 'hankel1', 'hankel2', 'jn', 'yn',
+    'jn_zeros', 'hn1', 'hn2', 'airyai', 'airybi', 'airyaiprime',
+    'airybiprime', 'marcumq', 'hyper', 'meijerg', 'appellf1', 'legendre',
+    'assoc_legendre', 'hermite', 'hermite_prob', 'chebyshevt', 'chebyshevu',
+    'chebyshevu_root', 'chebyshevt_root', 'laguerre', 'assoc_laguerre',
+    'gegenbauer', 'jacobi', 'jacobi_normalized', 'Ynm', 'Ynm_c', 'Znm',
+    'elliptic_k', 'elliptic_f', 'elliptic_e', 'elliptic_pi', 'beta',
+    'mathieus', 'mathieuc', 'mathieusprime', 'mathieucprime', 'riemann_xi','betainc',
+    'betainc_regularized',
+
+    # sympy.ntheory
+    'nextprime', 'prevprime', 'prime', 'primerange', 'randprime',
+    'Sieve', 'sieve', 'primorial', 'cycle_length', 'composite', 'compositepi',
+    'isprime', 'divisors', 'proper_divisors', 'factorint', 'multiplicity',
+    'perfect_power', 'pollard_pm1', 'factor_cache', 'pollard_rho', 'primefactors',
+    'divisor_count', 'proper_divisor_count',
+    'factorrat',
+    'mersenne_prime_exponent', 'is_perfect', 'is_mersenne_prime',
+    'is_abundant', 'is_deficient', 'is_amicable', 'is_carmichael', 'abundance',
+    'npartitions',
+    'is_primitive_root', 'is_quad_residue',
+    'n_order', 'sqrt_mod', 'quadratic_residues',
+    'primitive_root', 'nthroot_mod', 'is_nthpow_residue', 'sqrt_mod_iter',
+    'discrete_log', 'quadratic_congruence', 'binomial_coefficients',
+    'binomial_coefficients_list', 'multinomial_coefficients',
+    'continued_fraction_periodic', 'continued_fraction_iterator',
+    'continued_fraction_reduce', 'continued_fraction_convergents',
+    'continued_fraction', 'egyptian_fraction',
+
+    # sympy.concrete
+    'product', 'Product', 'summation', 'Sum',
+
+    # sympy.discrete
+    'fft', 'ifft', 'ntt', 'intt', 'fwht', 'ifwht', 'mobius_transform',
+    'inverse_mobius_transform', 'convolution', 'covering_product',
+    'intersecting_product',
+
+    # sympy.simplify
+    'simplify', 'hypersimp', 'hypersimilar', 'logcombine', 'separatevars',
+    'posify', 'besselsimp', 'kroneckersimp', 'signsimp',
+    'nsimplify', 'FU', 'fu', 'sqrtdenest', 'cse', 'epath', 'EPath',
+    'hyperexpand', 'collect', 'rcollect', 'radsimp', 'collect_const',
+    'fraction', 'numer', 'denom', 'trigsimp', 'exptrigsimp', 'powsimp',
+    'powdenest', 'combsimp', 'gammasimp', 'ratsimp', 'ratsimpmodprime',
+
+    # sympy.sets
+    'Set', 'Interval', 'Union', 'EmptySet', 'FiniteSet', 'ProductSet',
+    'Intersection', 'imageset', 'DisjointUnion', 'Complement', 'SymmetricDifference',
+    'ImageSet', 'Range', 'ComplexRegion', 'Reals', 'Contains', 'ConditionSet',
+    'Ordinal', 'OmegaPower', 'ord0', 'PowerSet', 'Naturals',
+    'Naturals0', 'UniversalSet', 'Integers', 'Rationals', 'Complexes',
+
+    # sympy.solvers
+    'solve', 'solve_linear_system', 'solve_linear_system_LU',
+    'solve_undetermined_coeffs', 'nsolve', 'solve_linear', 'checksol',
+    'det_quick', 'inv_quick', 'check_assumptions', 'failing_assumptions',
+    'diophantine', 'rsolve', 'rsolve_poly', 'rsolve_ratio', 'rsolve_hyper',
+    'checkodesol', 'classify_ode', 'dsolve', 'homogeneous_order',
+    'solve_poly_system', 'factor_system', 'solve_triangulated', 'pde_separate',
+    'pde_separate_add', 'pde_separate_mul', 'pdsolve', 'classify_pde',
+    'checkpdesol', 'ode_order', 'reduce_inequalities',
+    'reduce_abs_inequality', 'reduce_abs_inequalities',
+    'solve_poly_inequality', 'solve_rational_inequalities',
+    'solve_univariate_inequality', 'decompogen', 'solveset', 'linsolve',
+    'linear_eq_to_matrix', 'nonlinsolve', 'substitution',
+
+    # sympy.matrices
+    'ShapeError', 'NonSquareMatrixError', 'GramSchmidt', 'casoratian', 'diag',
+    'eye', 'hessian', 'jordan_cell', 'list2numpy', 'matrix2numpy',
+    'matrix_multiply_elementwise', 'ones', 'randMatrix', 'rot_axis1',
+    'rot_axis2', 'rot_axis3', 'symarray', 'wronskian', 'zeros',
+    'MutableDenseMatrix', 'DeferredVector', 'MatrixBase', 'Matrix',
+    'MutableMatrix', 'MutableSparseMatrix', 'banded', 'ImmutableDenseMatrix',
+    'ImmutableSparseMatrix', 'ImmutableMatrix', 'SparseMatrix', 'MatrixSlice',
+    'BlockDiagMatrix', 'BlockMatrix', 'FunctionMatrix', 'Identity', 'Inverse',
+    'MatAdd', 'MatMul', 'MatPow', 'MatrixExpr', 'MatrixSymbol', 'Trace',
+    'Transpose', 'ZeroMatrix', 'OneMatrix', 'blockcut', 'block_collapse',
+    'matrix_symbols', 'Adjoint', 'hadamard_product', 'HadamardProduct',
+    'HadamardPower', 'Determinant', 'det', 'diagonalize_vector', 'DiagMatrix',
+    'DiagonalMatrix', 'DiagonalOf', 'trace', 'DotProduct',
+    'kronecker_product', 'KroneckerProduct', 'PermutationMatrix',
+    'MatrixPermute', 'Permanent', 'per', 'rot_ccw_axis1', 'rot_ccw_axis2',
+    'rot_ccw_axis3', 'rot_givens',
+
+    # sympy.geometry
+    'Point', 'Point2D', 'Point3D', 'Line', 'Ray', 'Segment', 'Line2D',
+    'Segment2D', 'Ray2D', 'Line3D', 'Segment3D', 'Ray3D', 'Plane', 'Ellipse',
+    'Circle', 'Polygon', 'RegularPolygon', 'Triangle', 'rad', 'deg',
+    'are_similar', 'centroid', 'convex_hull', 'idiff', 'intersection',
+    'closest_points', 'farthest_points', 'GeometryError', 'Curve', 'Parabola',
+
+    # sympy.utilities
+    'flatten', 'group', 'take', 'subsets', 'variations', 'numbered_symbols',
+    'cartes', 'capture', 'dict_merge', 'prefixes', 'postfixes', 'sift',
+    'topological_sort', 'unflatten', 'has_dups', 'has_variety', 'reshape',
+    'rotations', 'filldedent', 'lambdify', 'threaded', 'xthreaded',
+    'public', 'memoize_property', 'timed',
+
+    # sympy.integrals
+    'integrate', 'Integral', 'line_integrate', 'mellin_transform',
+    'inverse_mellin_transform', 'MellinTransform', 'InverseMellinTransform',
+    'laplace_transform', 'inverse_laplace_transform', 'LaplaceTransform',
+    'laplace_correspondence', 'laplace_initial_conds',
+    'InverseLaplaceTransform', 'fourier_transform',
+    'inverse_fourier_transform', 'FourierTransform',
+    'InverseFourierTransform', 'sine_transform', 'inverse_sine_transform',
+    'SineTransform', 'InverseSineTransform', 'cosine_transform',
+    'inverse_cosine_transform', 'CosineTransform', 'InverseCosineTransform',
+    'hankel_transform', 'inverse_hankel_transform', 'HankelTransform',
+    'InverseHankelTransform', 'singularityintegrate',
+
+    # sympy.tensor
+    'IndexedBase', 'Idx', 'Indexed', 'get_contraction_structure',
+    'get_indices', 'shape', 'MutableDenseNDimArray', 'ImmutableDenseNDimArray',
+    'MutableSparseNDimArray', 'ImmutableSparseNDimArray', 'NDimArray',
+    'tensorproduct', 'tensorcontraction', 'tensordiagonal', 'derive_by_array',
+    'permutedims', 'Array', 'DenseNDimArray', 'SparseNDimArray',
+
+    # sympy.parsing
+    'parse_expr',
+
+    # sympy.calculus
+    'euler_equations', 'singularities', 'is_increasing',
+    'is_strictly_increasing', 'is_decreasing', 'is_strictly_decreasing',
+    'is_monotonic', 'finite_diff_weights', 'apply_finite_diff',
+    'differentiate_finite', 'periodicity', 'not_empty_in',
+    'AccumBounds', 'is_convex', 'stationary_points', 'minimum', 'maximum',
+
+    # sympy.algebras
+    'Quaternion',
+
+    # sympy.printing
+    'pager_print', 'pretty', 'pretty_print', 'pprint', 'pprint_use_unicode',
+    'pprint_try_use_unicode', 'latex', 'print_latex', 'multiline_latex',
+    'mathml', 'print_mathml', 'python', 'print_python', 'pycode', 'ccode',
+    'print_ccode', 'smtlib_code', 'glsl_code', 'print_glsl', 'cxxcode', 'fcode',
+    'print_fcode', 'rcode', 'print_rcode', 'jscode', 'print_jscode',
+    'julia_code', 'mathematica_code', 'octave_code', 'rust_code', 'print_gtk',
+    'preview', 'srepr', 'print_tree', 'StrPrinter', 'sstr', 'sstrrepr',
+    'TableForm', 'dotprint', 'maple_code', 'print_maple_code',
+
+    # sympy.plotting
+    'plot', 'textplot', 'plot_backends', 'plot_implicit', 'plot_parametric',
+
+    # sympy.interactive
+    'init_session', 'init_printing', 'interactive_traversal',
+
+    # sympy.testing
+    'test', 'doctest',
+]
+
+
+#===========================================================================#
+#                                                                           #
+# XXX: The names below were importable before SymPy 1.6 using               #
+#                                                                           #
+#          from sympy import *                                              #
+#                                                                           #
+# This happened implicitly because there was no __all__ defined in this     #
+# __init__.py file. Not every package is imported. The list matches what    #
+# would have been imported before. It is possible that these packages will  #
+# not be imported by a star-import from sympy in future.                    #
+#                                                                           #
+#===========================================================================#
+
+
+__all__.extend((
+    'algebras',
+    'assumptions',
+    'calculus',
+    'concrete',
+    'discrete',
+    'external',
+    'functions',
+    'geometry',
+    'interactive',
+    'multipledispatch',
+    'ntheory',
+    'parsing',
+    'plotting',
+    'polys',
+    'printing',
+    'release',
+    'strategies',
+    'tensor',
+    'utilities',
+))
diff --git a/.venv/lib/python3.12/site-packages/sympy/abc.py b/.venv/lib/python3.12/site-packages/sympy/abc.py
new file mode 100644
index 0000000000000000000000000000000000000000..a6f7a4056e60b0d651becc0b9909bd0ab3c1723f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sympy/abc.py
@@ -0,0 +1,111 @@
+"""
+This module exports all latin and greek letters as Symbols, so you can
+conveniently do
+
+    >>> from sympy.abc import x, y
+
+instead of the slightly more clunky-looking
+
+    >>> from sympy import symbols
+    >>> x, y = symbols('x y')
+
+Caveats
+=======
+
+1. As of the time of writing this, the names ``O``, ``S``, ``I``, ``N``,
+``E``, and ``Q`` are colliding with names defined in SymPy. If you import them
+from both ``sympy.abc`` and ``sympy``, the second import will "win".
+This is an issue only for * imports, which should only be used for short-lived
+code such as interactive sessions and throwaway scripts that do not survive
+until the next SymPy upgrade, where ``sympy`` may contain a different set of
+names.
+
+2. This module does not define symbol names on demand, i.e.
+``from sympy.abc import foo`` will be reported as an error because
+``sympy.abc`` does not contain the name ``foo``. To get a symbol named ``foo``,
+you still need to use ``Symbol('foo')`` or ``symbols('foo')``.
+You can freely mix usage of ``sympy.abc`` and ``Symbol``/``symbols``, though
+sticking with one and only one way to get the symbols does tend to make the code
+more readable.
+
+The module also defines some special names to help detect which names clash
+with the default SymPy namespace.
+
+``_clash1`` defines all the single letter variables that clash with
+SymPy objects; ``_clash2`` defines the multi-letter clashing symbols;
+and ``_clash`` is the union of both. These can be passed for ``locals``
+during sympification if one desires Symbols rather than the non-Symbol
+objects for those names.
+
+Examples
+========
+
+>>> from sympy import S
+>>> from sympy.abc import _clash1, _clash2, _clash
+>>> S("Q & C", locals=_clash1)
+C & Q
+>>> S('pi(x)', locals=_clash2)
+pi(x)
+>>> S('pi(C, Q)', locals=_clash)
+pi(C, Q)
+
+"""
+from __future__ import annotations
+from typing import Any
+
+import string
+
+from .core import Symbol, symbols
+from .core.alphabets import greeks
+from sympy.parsing.sympy_parser import null
+
+##### Symbol definitions #####
+
+# Implementation note: The easiest way to avoid typos in the symbols()
+# parameter is to copy it from the left-hand side of the assignment.
+
+a, b, c, d, e, f, g, h, i, j = symbols('a, b, c, d, e, f, g, h, i, j')
+k, l, m, n, o, p, q, r, s, t = symbols('k, l, m, n, o, p, q, r, s, t')
+u, v, w, x, y, z = symbols('u, v, w, x, y, z')
+
+A, B, C, D, E, F, G, H, I, J = symbols('A, B, C, D, E, F, G, H, I, J')
+K, L, M, N, O, P, Q, R, S, T = symbols('K, L, M, N, O, P, Q, R, S, T')
+U, V, W, X, Y, Z = symbols('U, V, W, X, Y, Z')
+
+alpha, beta, gamma, delta = symbols('alpha, beta, gamma, delta')
+epsilon, zeta, eta, theta = symbols('epsilon, zeta, eta, theta')
+iota, kappa, lamda, mu = symbols('iota, kappa, lamda, mu')
+nu, xi, omicron, pi = symbols('nu, xi, omicron, pi')
+rho, sigma, tau, upsilon = symbols('rho, sigma, tau, upsilon')
+phi, chi, psi, omega = symbols('phi, chi, psi, omega')
+
+
+##### Clashing-symbols diagnostics #####
+
+# We want to know which names in SymPy collide with those in here.
+# This is mostly for diagnosing SymPy's namespace during SymPy development.
+
+_latin = list(string.ascii_letters)
+# QOSINE should not be imported as they clash; gamma, pi and zeta clash, too
+_greek = list(greeks) # make a copy, so we can mutate it
+# Note: We import lamda since lambda is a reserved keyword in Python
+_greek.remove("lambda")
+_greek.append("lamda")
+
+ns: dict[str, Any] = {}
+exec('from sympy import *', ns)
+_clash1: dict[str, Any] = {}
+_clash2: dict[str, Any] = {}
+while ns:
+    _k, _ = ns.popitem()
+    if _k in _greek:
+        _clash2[_k] = null
+        _greek.remove(_k)
+    elif _k in _latin:
+        _clash1[_k] = null
+        _latin.remove(_k)
+_clash = {}
+_clash.update(_clash1)
+_clash.update(_clash2)
+
+del _latin, _greek, Symbol, _k, null
diff --git a/.venv/lib/python3.12/site-packages/sympy/conftest.py b/.venv/lib/python3.12/site-packages/sympy/conftest.py
new file mode 100644
index 0000000000000000000000000000000000000000..788ec7ed8a38de422b76c4ac19057513670d2d38
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sympy/conftest.py
@@ -0,0 +1,96 @@
+import sys
+
+sys._running_pytest = True  # type: ignore
+from sympy.external.importtools import version_tuple
+
+import pytest
+from sympy.core.cache import clear_cache, USE_CACHE
+from sympy.external.gmpy import GROUND_TYPES
+from sympy.utilities.misc import ARCH
+import re
+
+try:
+    import hypothesis
+
+    hypothesis.settings.register_profile("sympy_hypothesis_profile", deadline=None)
+    hypothesis.settings.load_profile("sympy_hypothesis_profile")
+except ImportError:
+    raise ImportError(
+        "hypothesis is a required dependency to run the SymPy test suite. "
+        "Install it with 'pip install hypothesis' or 'conda install -c conda-forge hypothesis'"
+    )
+
+
+sp = re.compile(r"([0-9]+)/([1-9][0-9]*)")
+
+
+def process_split(config, items):
+    split = config.getoption("--split")
+    if not split:
+        return
+    m = sp.match(split)
+    if not m:
+        raise ValueError(
+            "split must be a string of the form a/b " "where a and b are ints."
+        )
+    i, t = map(int, m.groups())
+    start, end = (i - 1) * len(items) // t, i * len(items) // t
+
+    if i < t:
+        # remove elements from end of list first
+        del items[end:]
+    del items[:start]
+
+
+def pytest_report_header(config):
+    s = "architecture: %s\n" % ARCH
+    s += "cache:        %s\n" % USE_CACHE
+    version = ""
+    if GROUND_TYPES == "gmpy":
+        import gmpy2
+
+        version = gmpy2.version()
+    elif GROUND_TYPES == "flint":
+        try:
+            from flint import __version__
+        except ImportError:
+            version = "unknown"
+        else:
+            version = f'(python-flint=={__version__})'
+    s += "ground types: %s %s\n" % (GROUND_TYPES, version)
+    return s
+
+
+def pytest_terminal_summary(terminalreporter):
+    if terminalreporter.stats.get("error", None) or terminalreporter.stats.get(
+        "failed", None
+    ):
+        terminalreporter.write_sep(" ", "DO *NOT* COMMIT!", red=True, bold=True)
+
+
+def pytest_addoption(parser):
+    parser.addoption("--split", action="store", default="", help="split tests")
+
+
+def pytest_collection_modifyitems(config, items):
+    """pytest hook."""
+    # handle splits
+    process_split(config, items)
+
+
+@pytest.fixture(autouse=True, scope="module")
+def file_clear_cache():
+    clear_cache()
+
+
+@pytest.fixture(autouse=True, scope="module")
+def check_disabled(request):
+    if getattr(request.module, "disabled", False):
+        pytest.skip("test requirements not met.")
+    elif getattr(request.module, "ipython", False):
+        # need to check version and options for ipython tests
+        if (
+            version_tuple(pytest.__version__) < version_tuple("2.6.3")
+            and pytest.config.getvalue("-s") != "no"
+        ):
+            pytest.skip("run py.test with -s or upgrade to newer version.")
diff --git a/.venv/lib/python3.12/site-packages/sympy/galgebra.py b/.venv/lib/python3.12/site-packages/sympy/galgebra.py
new file mode 100644
index 0000000000000000000000000000000000000000..be812bfcbcece834c86d7b55eb846286acd0ac08
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sympy/galgebra.py
@@ -0,0 +1 @@
+raise ImportError("""As of SymPy 1.0 the galgebra module is maintained separately at https://github.com/pygae/galgebra""")
diff --git a/.venv/lib/python3.12/site-packages/sympy/release.py b/.venv/lib/python3.12/site-packages/sympy/release.py
new file mode 100644
index 0000000000000000000000000000000000000000..b9f68edb2f571e7bfaad9472d36a2d357b5574b2
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sympy/release.py
@@ -0,0 +1 @@
+__version__ = "1.14.0"
diff --git a/.venv/lib/python3.12/site-packages/sympy/this.py b/.venv/lib/python3.12/site-packages/sympy/this.py
new file mode 100644
index 0000000000000000000000000000000000000000..c2a5504ac42c7d57790d65524b066a6df86ab539
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/sympy/this.py
@@ -0,0 +1,21 @@
+"""
+The Zen of SymPy.
+"""
+
+s = """The Zen of SymPy
+
+Unevaluated is better than evaluated.
+The user interface matters.
+Printing matters.
+Pure Python can be fast enough.
+If it's too slow, it's (probably) your fault.
+Documentation matters.
+Correctness is more important than speed.
+Push it in now and improve upon it later.
+Coverage by testing matters.
+Smart tests are better than random tests.
+But random tests sometimes find what your smartest test missed.
+The Python way is probably the right way.
+Community is more important than code."""
+
+print(s)
diff --git a/.venv/lib/python3.12/site-packages/threadpoolctl.py b/.venv/lib/python3.12/site-packages/threadpoolctl.py
new file mode 100644
index 0000000000000000000000000000000000000000..aea33509dc5512c0e845d387e01577c22f027676
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/threadpoolctl.py
@@ -0,0 +1,1292 @@
+"""threadpoolctl
+
+This module provides utilities to introspect native libraries that relies on
+thread pools (notably BLAS and OpenMP implementations) and dynamically set the
+maximal number of threads they can use.
+"""
+
+# License: BSD 3-Clause
+
+# The code to introspect dynamically loaded libraries on POSIX systems is
+# adapted from code by Intel developer @anton-malakhov available at
+# https://github.com/IntelPython/smp (Copyright (c) 2017, Intel Corporation)
+# and also published under the BSD 3-Clause license
+import os
+import re
+import sys
+import ctypes
+import itertools
+import textwrap
+from typing import final
+import warnings
+from ctypes.util import find_library
+from abc import ABC, abstractmethod
+from functools import lru_cache
+from contextlib import ContextDecorator
+
+__version__ = "3.6.0"
+__all__ = [
+    "threadpool_limits",
+    "threadpool_info",
+    "ThreadpoolController",
+    "LibController",
+    "register",
+]
+
+
+# One can get runtime errors or even segfaults due to multiple OpenMP libraries
+# loaded simultaneously which can happen easily in Python when importing and
+# using compiled extensions built with different compilers and therefore
+# different OpenMP runtimes in the same program. In particular libiomp (used by
+# Intel ICC) and libomp used by clang/llvm tend to crash. This can happen for
+# instance when calling BLAS inside a prange. Setting the following environment
+# variable allows multiple OpenMP libraries to be loaded. It should not degrade
+# performances since we manually take care of potential over-subscription
+# performance issues, in sections of the code where nested OpenMP loops can
+# happen, by dynamically reconfiguring the inner OpenMP runtime to temporarily
+# disable it while under the scope of the outer OpenMP parallel section.
+os.environ.setdefault("KMP_DUPLICATE_LIB_OK", "True")
+
+# Structure to cast the info on dynamically loaded library. See
+# https://linux.die.net/man/3/dl_iterate_phdr for more details.
+_SYSTEM_UINT = ctypes.c_uint64 if sys.maxsize > 2**32 else ctypes.c_uint32
+_SYSTEM_UINT_HALF = ctypes.c_uint32 if sys.maxsize > 2**32 else ctypes.c_uint16
+
+
+class _dl_phdr_info(ctypes.Structure):
+    _fields_ = [
+        ("dlpi_addr", _SYSTEM_UINT),  # Base address of object
+        ("dlpi_name", ctypes.c_char_p),  # path to the library
+        ("dlpi_phdr", ctypes.c_void_p),  # pointer on dlpi_headers
+        ("dlpi_phnum", _SYSTEM_UINT_HALF),  # number of elements in dlpi_phdr
+    ]
+
+
+# The RTLD_NOLOAD flag for loading shared libraries is not defined on Windows.
+try:
+    _RTLD_NOLOAD = os.RTLD_NOLOAD
+except AttributeError:
+    _RTLD_NOLOAD = ctypes.DEFAULT_MODE
+
+
+class LibController(ABC):
+    """Abstract base class for the individual library controllers
+
+    A library controller must expose the following class attributes:
+        - user_api : str
+            Usually the name of the library or generic specification the library
+            implements, e.g. "blas" is a specification with different implementations.
+        - internal_api : str
+            Usually the name of the library or concrete implementation of some
+            specification, e.g. "openblas" is an implementation of the "blas"
+            specification.
+        - filename_prefixes : tuple
+            Possible prefixes of the shared library's filename that allow to
+            identify the library. e.g. "libopenblas" for libopenblas.so.
+
+    and implement the following methods: `get_num_threads`, `set_num_threads` and
+    `get_version`.
+
+    Threadpoolctl loops through all the loaded shared libraries and tries to match
+    the filename of each library with the `filename_prefixes`. If a match is found, a
+    controller is instantiated and a handler to the library is stored in the `dynlib`
+    attribute as a `ctypes.CDLL` object. It can be used to access the necessary symbols
+    of the shared library to implement the above methods.
+
+    The following information will be exposed in the info dictionary:
+      - user_api : standardized API, if any, or a copy of internal_api.
+      - internal_api : implementation-specific API.
+      - num_threads : the current thread limit.
+      - prefix : prefix of the shared library's filename.
+      - filepath : path to the loaded shared library.
+      - version : version of the library (if available).
+
+    In addition, each library controller may expose internal API specific entries. They
+    must be set as attributes in the `set_additional_attributes` method.
+    """
+
+    @final
+    def __init__(self, *, filepath=None, prefix=None, parent=None):
+        """This is not meant to be overriden by subclasses."""
+        self.parent = parent
+        self.prefix = prefix
+        self.filepath = filepath
+        self.dynlib = ctypes.CDLL(filepath, mode=_RTLD_NOLOAD)
+        self._symbol_prefix, self._symbol_suffix = self._find_affixes()
+        self.version = self.get_version()
+        self.set_additional_attributes()
+
+    def info(self):
+        """Return relevant info wrapped in a dict"""
+        hidden_attrs = ("dynlib", "parent", "_symbol_prefix", "_symbol_suffix")
+        return {
+            "user_api": self.user_api,
+            "internal_api": self.internal_api,
+            "num_threads": self.num_threads,
+            **{k: v for k, v in vars(self).items() if k not in hidden_attrs},
+        }
+
+    def set_additional_attributes(self):
+        """Set additional attributes meant to be exposed in the info dict"""
+
+    @property
+    def num_threads(self):
+        """Exposes the current thread limit as a dynamic property
+
+        This is not meant to be used or overriden by subclasses.
+        """
+        return self.get_num_threads()
+
+    @abstractmethod
+    def get_num_threads(self):
+        """Return the maximum number of threads available to use"""
+
+    @abstractmethod
+    def set_num_threads(self, num_threads):
+        """Set the maximum number of threads to use"""
+
+    @abstractmethod
+    def get_version(self):
+        """Return the version of the shared library"""
+
+    def _find_affixes(self):
+        """Return the affixes for the symbols of the shared library"""
+        return "", ""
+
+    def _get_symbol(self, name):
+        """Return the symbol of the shared library accounding for the affixes"""
+        return getattr(
+            self.dynlib, f"{self._symbol_prefix}{name}{self._symbol_suffix}", None
+        )
+
+
+class OpenBLASController(LibController):
+    """Controller class for OpenBLAS"""
+
+    user_api = "blas"
+    internal_api = "openblas"
+    filename_prefixes = ("libopenblas", "libblas", "libscipy_openblas")
+
+    _symbol_prefixes = ("", "scipy_")
+    _symbol_suffixes = ("", "64_", "_64")
+
+    # All variations of "openblas_get_num_threads", accounting for the affixes
+    check_symbols = tuple(
+        f"{prefix}openblas_get_num_threads{suffix}"
+        for prefix, suffix in itertools.product(_symbol_prefixes, _symbol_suffixes)
+    )
+
+    def _find_affixes(self):
+        for prefix, suffix in itertools.product(
+            self._symbol_prefixes, self._symbol_suffixes
+        ):
+            if hasattr(self.dynlib, f"{prefix}openblas_get_num_threads{suffix}"):
+                return prefix, suffix
+
+    def set_additional_attributes(self):
+        self.threading_layer = self._get_threading_layer()
+        self.architecture = self._get_architecture()
+
+    def get_num_threads(self):
+        get_num_threads_func = self._get_symbol("openblas_get_num_threads")
+        if get_num_threads_func is not None:
+            return get_num_threads_func()
+        return None
+
+    def set_num_threads(self, num_threads):
+        set_num_threads_func = self._get_symbol("openblas_set_num_threads")
+        if set_num_threads_func is not None:
+            return set_num_threads_func(num_threads)
+        return None
+
+    def get_version(self):
+        # None means OpenBLAS is not loaded or version < 0.3.4, since OpenBLAS
+        # did not expose its version before that.
+        get_version_func = self._get_symbol("openblas_get_config")
+        if get_version_func is not None:
+            get_version_func.restype = ctypes.c_char_p
+            config = get_version_func().split()
+            if config[0] == b"OpenBLAS":
+                return config[1].decode("utf-8")
+            return None
+        return None
+
+    def _get_threading_layer(self):
+        """Return the threading layer of OpenBLAS"""
+        get_threading_layer_func = self._get_symbol("openblas_get_parallel")
+        if get_threading_layer_func is not None:
+            threading_layer = get_threading_layer_func()
+            if threading_layer == 2:
+                return "openmp"
+            elif threading_layer == 1:
+                return "pthreads"
+            return "disabled"
+        return "unknown"
+
+    def _get_architecture(self):
+        """Return the architecture detected by OpenBLAS"""
+        get_architecture_func = self._get_symbol("openblas_get_corename")
+        if get_architecture_func is not None:
+            get_architecture_func.restype = ctypes.c_char_p
+            return get_architecture_func().decode("utf-8")
+        return None
+
+
+class BLISController(LibController):
+    """Controller class for BLIS"""
+
+    user_api = "blas"
+    internal_api = "blis"
+    filename_prefixes = ("libblis", "libblas")
+    check_symbols = (
+        "bli_thread_get_num_threads",
+        "bli_thread_set_num_threads",
+        "bli_info_get_version_str",
+        "bli_info_get_enable_openmp",
+        "bli_info_get_enable_pthreads",
+        "bli_arch_query_id",
+        "bli_arch_string",
+    )
+
+    def set_additional_attributes(self):
+        self.threading_layer = self._get_threading_layer()
+        self.architecture = self._get_architecture()
+
+    def get_num_threads(self):
+        get_func = getattr(self.dynlib, "bli_thread_get_num_threads", lambda: None)
+        num_threads = get_func()
+        # by default BLIS is single-threaded and get_num_threads
+        # returns -1. We map it to 1 for consistency with other libraries.
+        return 1 if num_threads == -1 else num_threads
+
+    def set_num_threads(self, num_threads):
+        set_func = getattr(
+            self.dynlib, "bli_thread_set_num_threads", lambda num_threads: None
+        )
+        return set_func(num_threads)
+
+    def get_version(self):
+        get_version_ = getattr(self.dynlib, "bli_info_get_version_str", None)
+        if get_version_ is None:
+            return None
+
+        get_version_.restype = ctypes.c_char_p
+        return get_version_().decode("utf-8")
+
+    def _get_threading_layer(self):
+        """Return the threading layer of BLIS"""
+        if getattr(self.dynlib, "bli_info_get_enable_openmp", lambda: False)():
+            return "openmp"
+        elif getattr(self.dynlib, "bli_info_get_enable_pthreads", lambda: False)():
+            return "pthreads"
+        return "disabled"
+
+    def _get_architecture(self):
+        """Return the architecture detected by BLIS"""
+        bli_arch_query_id = getattr(self.dynlib, "bli_arch_query_id", None)
+        bli_arch_string = getattr(self.dynlib, "bli_arch_string", None)
+        if bli_arch_query_id is None or bli_arch_string is None:
+            return None
+
+        # the true restype should be BLIS' arch_t (enum) but int should work
+        # for us:
+        bli_arch_query_id.restype = ctypes.c_int
+        bli_arch_string.restype = ctypes.c_char_p
+        return bli_arch_string(bli_arch_query_id()).decode("utf-8")
+
+
+class FlexiBLASController(LibController):
+    """Controller class for FlexiBLAS"""
+
+    user_api = "blas"
+    internal_api = "flexiblas"
+    filename_prefixes = ("libflexiblas",)
+    check_symbols = (
+        "flexiblas_get_num_threads",
+        "flexiblas_set_num_threads",
+        "flexiblas_get_version",
+        "flexiblas_list",
+        "flexiblas_list_loaded",
+        "flexiblas_current_backend",
+    )
+
+    @property
+    def loaded_backends(self):
+        return self._get_backend_list(loaded=True)
+
+    @property
+    def current_backend(self):
+        return self._get_current_backend()
+
+    def info(self):
+        """Return relevant info wrapped in a dict"""
+        # We override the info method because the loaded and current backends
+        # are dynamic properties
+        exposed_attrs = super().info()
+        exposed_attrs["loaded_backends"] = self.loaded_backends
+        exposed_attrs["current_backend"] = self.current_backend
+
+        return exposed_attrs
+
+    def set_additional_attributes(self):
+        self.available_backends = self._get_backend_list(loaded=False)
+
+    def get_num_threads(self):
+        get_func = getattr(self.dynlib, "flexiblas_get_num_threads", lambda: None)
+        num_threads = get_func()
+        # by default BLIS is single-threaded and get_num_threads
+        # returns -1. We map it to 1 for consistency with other libraries.
+        return 1 if num_threads == -1 else num_threads
+
+    def set_num_threads(self, num_threads):
+        set_func = getattr(
+            self.dynlib, "flexiblas_set_num_threads", lambda num_threads: None
+        )
+        return set_func(num_threads)
+
+    def get_version(self):
+        get_version_ = getattr(self.dynlib, "flexiblas_get_version", None)
+        if get_version_ is None:
+            return None
+
+        major = ctypes.c_int()
+        minor = ctypes.c_int()
+        patch = ctypes.c_int()
+        get_version_(ctypes.byref(major), ctypes.byref(minor), ctypes.byref(patch))
+        return f"{major.value}.{minor.value}.{patch.value}"
+
+    def _get_backend_list(self, loaded=False):
+        """Return the list of available backends for FlexiBLAS.
+
+        If loaded is False, return the list of available backends from the FlexiBLAS
+        configuration. If loaded is True, return the list of actually loaded backends.
+        """
+        func_name = f"flexiblas_list{'_loaded' if loaded else ''}"
+        get_backend_list_ = getattr(self.dynlib, func_name, None)
+        if get_backend_list_ is None:
+            return None
+
+        n_backends = get_backend_list_(None, 0, 0)
+
+        backends = []
+        for i in range(n_backends):
+            backend_name = ctypes.create_string_buffer(1024)
+            get_backend_list_(backend_name, 1024, i)
+            if backend_name.value.decode("utf-8") != "__FALLBACK__":
+                # We don't know when to expect __FALLBACK__ but it is not a real
+                # backend and does not show up when running flexiblas list.
+                backends.append(backend_name.value.decode("utf-8"))
+        return backends
+
+    def _get_current_backend(self):
+        """Return the backend of FlexiBLAS"""
+        get_backend_ = getattr(self.dynlib, "flexiblas_current_backend", None)
+        if get_backend_ is None:
+            return None
+
+        backend = ctypes.create_string_buffer(1024)
+        get_backend_(backend, ctypes.sizeof(backend))
+        return backend.value.decode("utf-8")
+
+    def switch_backend(self, backend):
+        """Switch the backend of FlexiBLAS
+
+        Parameters
+        ----------
+        backend : str
+            The name or the path to the shared library of the backend to switch to. If
+            the backend is not already loaded, it will be loaded first.
+        """
+        if backend not in self.loaded_backends:
+            if backend in self.available_backends:
+                load_func = getattr(self.dynlib, "flexiblas_load_backend", lambda _: -1)
+            else:  # assume backend is a path to a shared library
+                load_func = getattr(
+                    self.dynlib, "flexiblas_load_backend_library", lambda _: -1
+                )
+            res = load_func(str(backend).encode("utf-8"))
+            if res == -1:
+                raise RuntimeError(
+                    f"Failed to load backend {backend!r}. It must either be the name of"
+                    " a backend available in the FlexiBLAS configuration "
+                    f"{self.available_backends} or the path to a valid shared library."
+                )
+
+            # Trigger a new search of loaded shared libraries since loading a new
+            # backend caused a dlopen.
+            self.parent._load_libraries()
+
+        switch_func = getattr(self.dynlib, "flexiblas_switch", lambda _: -1)
+        idx = self.loaded_backends.index(backend)
+        res = switch_func(idx)
+        if res == -1:
+            raise RuntimeError(f"Failed to switch to backend {backend!r}.")
+
+
+class MKLController(LibController):
+    """Controller class for MKL"""
+
+    user_api = "blas"
+    internal_api = "mkl"
+    filename_prefixes = ("libmkl_rt", "mkl_rt", "libblas")
+    check_symbols = (
+        "MKL_Get_Max_Threads",
+        "MKL_Set_Num_Threads",
+        "MKL_Get_Version_String",
+        "MKL_Set_Threading_Layer",
+    )
+
+    def set_additional_attributes(self):
+        self.threading_layer = self._get_threading_layer()
+
+    def get_num_threads(self):
+        get_func = getattr(self.dynlib, "MKL_Get_Max_Threads", lambda: None)
+        return get_func()
+
+    def set_num_threads(self, num_threads):
+        set_func = getattr(self.dynlib, "MKL_Set_Num_Threads", lambda num_threads: None)
+        return set_func(num_threads)
+
+    def get_version(self):
+        if not hasattr(self.dynlib, "MKL_Get_Version_String"):
+            return None
+
+        res = ctypes.create_string_buffer(200)
+        self.dynlib.MKL_Get_Version_String(res, 200)
+
+        version = res.value.decode("utf-8")
+        group = re.search(r"Version ([^ ]+) ", version)
+        if group is not None:
+            version = group.groups()[0]
+        return version.strip()
+
+    def _get_threading_layer(self):
+        """Return the threading layer of MKL"""
+        # The function mkl_set_threading_layer returns the current threading
+        # layer. Calling it with an invalid threading layer allows us to safely
+        # get the threading layer
+        set_threading_layer = getattr(
+            self.dynlib, "MKL_Set_Threading_Layer", lambda layer: -1
+        )
+        layer_map = {
+            0: "intel",
+            1: "sequential",
+            2: "pgi",
+            3: "gnu",
+            4: "tbb",
+            -1: "not specified",
+        }
+        return layer_map[set_threading_layer(-1)]
+
+
+class OpenMPController(LibController):
+    """Controller class for OpenMP"""
+
+    user_api = "openmp"
+    internal_api = "openmp"
+    filename_prefixes = ("libiomp", "libgomp", "libomp", "vcomp")
+    check_symbols = (
+        "omp_get_max_threads",
+        "omp_get_num_threads",
+    )
+
+    def get_num_threads(self):
+        get_func = getattr(self.dynlib, "omp_get_max_threads", lambda: None)
+        return get_func()
+
+    def set_num_threads(self, num_threads):
+        set_func = getattr(self.dynlib, "omp_set_num_threads", lambda num_threads: None)
+        return set_func(num_threads)
+
+    def get_version(self):
+        # There is no way to get the version number programmatically in OpenMP.
+        return None
+
+
+# Controllers for the libraries that we'll look for in the loaded libraries.
+# Third party libraries can register their own controllers.
+_ALL_CONTROLLERS = [
+    OpenBLASController,
+    BLISController,
+    MKLController,
+    OpenMPController,
+    FlexiBLASController,
+]
+
+# Helpers for the doc and test names
+_ALL_USER_APIS = list(set(lib.user_api for lib in _ALL_CONTROLLERS))
+_ALL_INTERNAL_APIS = [lib.internal_api for lib in _ALL_CONTROLLERS]
+_ALL_PREFIXES = list(
+    set(prefix for lib in _ALL_CONTROLLERS for prefix in lib.filename_prefixes)
+)
+_ALL_BLAS_LIBRARIES = [
+    lib.internal_api for lib in _ALL_CONTROLLERS if lib.user_api == "blas"
+]
+_ALL_OPENMP_LIBRARIES = OpenMPController.filename_prefixes
+
+
+def register(controller):
+    """Register a new controller"""
+    _ALL_CONTROLLERS.append(controller)
+    _ALL_USER_APIS.append(controller.user_api)
+    _ALL_INTERNAL_APIS.append(controller.internal_api)
+    _ALL_PREFIXES.extend(controller.filename_prefixes)
+
+
+def _format_docstring(*args, **kwargs):
+    def decorator(o):
+        if o.__doc__ is not None:
+            o.__doc__ = o.__doc__.format(*args, **kwargs)
+        return o
+
+    return decorator
+
+
+@lru_cache(maxsize=10000)
+def _realpath(filepath):
+    """Small caching wrapper around os.path.realpath to limit system calls"""
+    return os.path.realpath(filepath)
+
+
+@_format_docstring(USER_APIS=list(_ALL_USER_APIS), INTERNAL_APIS=_ALL_INTERNAL_APIS)
+def threadpool_info():
+    """Return the maximal number of threads for each detected library.
+
+    Return a list with all the supported libraries that have been found. Each
+    library is represented by a dict with the following information:
+
+      - "user_api" : user API. Possible values are {USER_APIS}.
+      - "internal_api": internal API. Possible values are {INTERNAL_APIS}.
+      - "prefix" : filename prefix of the specific implementation.
+      - "filepath": path to the loaded library.
+      - "version": version of the library (if available).
+      - "num_threads": the current thread limit.
+
+    In addition, each library may contain internal_api specific entries.
+    """
+    return ThreadpoolController().info()
+
+
+class _ThreadpoolLimiter:
+    """The guts of ThreadpoolController.limit
+
+    Refer to the docstring of ThreadpoolController.limit for more details.
+
+    It will only act on the library controllers held by the provided `controller`.
+    Using the default constructor sets the limits right away such that it can be used as
+    a callable. Setting the limits can be delayed by using the `wrap` class method such
+    that it can be used as a decorator.
+    """
+
+    def __init__(self, controller, *, limits=None, user_api=None):
+        self._controller = controller
+        self._limits, self._user_api, self._prefixes = self._check_params(
+            limits, user_api
+        )
+        self._original_info = self._controller.info()
+        self._set_threadpool_limits()
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, type, value, traceback):
+        self.restore_original_limits()
+
+    @classmethod
+    def wrap(cls, controller, *, limits=None, user_api=None):
+        """Return an instance of this class that can be used as a decorator"""
+        return _ThreadpoolLimiterDecorator(
+            controller=controller, limits=limits, user_api=user_api
+        )
+
+    def restore_original_limits(self):
+        """Set the limits back to their original values"""
+        for lib_controller, original_info in zip(
+            self._controller.lib_controllers, self._original_info
+        ):
+            lib_controller.set_num_threads(original_info["num_threads"])
+
+    # Alias of `restore_original_limits` for backward compatibility
+    unregister = restore_original_limits
+
+    def get_original_num_threads(self):
+        """Original num_threads from before calling threadpool_limits
+
+        Return a dict `{user_api: num_threads}`.
+        """
+        num_threads = {}
+        warning_apis = []
+
+        for user_api in self._user_api:
+            limits = [
+                lib_info["num_threads"]
+                for lib_info in self._original_info
+                if lib_info["user_api"] == user_api
+            ]
+            limits = set(limits)
+            n_limits = len(limits)
+
+            if n_limits == 1:
+                limit = limits.pop()
+            elif n_limits == 0:
+                limit = None
+            else:
+                limit = min(limits)
+                warning_apis.append(user_api)
+
+            num_threads[user_api] = limit
+
+        if warning_apis:
+            warnings.warn(
+                "Multiple value possible for following user apis: "
+                + ", ".join(warning_apis)
+                + ". Returning the minimum."
+            )
+
+        return num_threads
+
+    def _check_params(self, limits, user_api):
+        """Suitable values for the _limits, _user_api and _prefixes attributes"""
+
+        if isinstance(limits, str) and limits == "sequential_blas_under_openmp":
+            (
+                limits,
+                user_api,
+            ) = self._controller._get_params_for_sequential_blas_under_openmp().values()
+
+        if limits is None or isinstance(limits, int):
+            if user_api is None:
+                user_api = _ALL_USER_APIS
+            elif user_api in _ALL_USER_APIS:
+                user_api = [user_api]
+            else:
+                raise ValueError(
+                    f"user_api must be either in {_ALL_USER_APIS} or None. Got "
+                    f"{user_api} instead."
+                )
+
+            if limits is not None:
+                limits = {api: limits for api in user_api}
+            prefixes = []
+        else:
+            if isinstance(limits, list):
+                # This should be a list of dicts of library info, for
+                # compatibility with the result from threadpool_info.
+                limits = {
+                    lib_info["prefix"]: lib_info["num_threads"] for lib_info in limits
+                }
+            elif isinstance(limits, ThreadpoolController):
+                # To set the limits from the library controllers of a
+                # ThreadpoolController object.
+                limits = {
+                    lib_controller.prefix: lib_controller.num_threads
+                    for lib_controller in limits.lib_controllers
+                }
+
+            if not isinstance(limits, dict):
+                raise TypeError(
+                    "limits must either be an int, a list, a dict, or "
+                    f"'sequential_blas_under_openmp'. Got {type(limits)} instead"
+                )
+
+            # With a dictionary, can set both specific limit for given
+            # libraries and global limit for user_api. Fetch each separately.
+            prefixes = [prefix for prefix in limits if prefix in _ALL_PREFIXES]
+            user_api = [api for api in limits if api in _ALL_USER_APIS]
+
+        return limits, user_api, prefixes
+
+    def _set_threadpool_limits(self):
+        """Change the maximal number of threads in selected thread pools.
+
+        Return a list with all the supported libraries that have been found
+        matching `self._prefixes` and `self._user_api`.
+        """
+        if self._limits is None:
+            return
+
+        for lib_controller in self._controller.lib_controllers:
+            # self._limits is a dict {key: num_threads} where key is either
+            # a prefix or a user_api. If a library matches both, the limit
+            # corresponding to the prefix is chosen.
+            if lib_controller.prefix in self._limits:
+                num_threads = self._limits[lib_controller.prefix]
+            elif lib_controller.user_api in self._limits:
+                num_threads = self._limits[lib_controller.user_api]
+            else:
+                continue
+
+            if num_threads is not None:
+                lib_controller.set_num_threads(num_threads)
+
+
+class _ThreadpoolLimiterDecorator(_ThreadpoolLimiter, ContextDecorator):
+    """Same as _ThreadpoolLimiter but to be used as a decorator"""
+
+    def __init__(self, controller, *, limits=None, user_api=None):
+        self._limits, self._user_api, self._prefixes = self._check_params(
+            limits, user_api
+        )
+        self._controller = controller
+
+    def __enter__(self):
+        # we need to set the limits here and not in the __init__ because we want the
+        # limits to be set when calling the decorated function, not when creating the
+        # decorator.
+        self._original_info = self._controller.info()
+        self._set_threadpool_limits()
+        return self
+
+
+@_format_docstring(
+    USER_APIS=", ".join(f'"{api}"' for api in _ALL_USER_APIS),
+    BLAS_LIBS=", ".join(_ALL_BLAS_LIBRARIES),
+    OPENMP_LIBS=", ".join(_ALL_OPENMP_LIBRARIES),
+)
+class threadpool_limits(_ThreadpoolLimiter):
+    """Change the maximal number of threads that can be used in thread pools.
+
+    This object can be used either as a callable (the construction of this object
+    limits the number of threads), as a context manager in a `with` block to
+    automatically restore the original state of the controlled libraries when exiting
+    the block, or as a decorator through its `wrap` method.
+
+    Set the maximal number of threads that can be used in thread pools used in
+    the supported libraries to `limit`. This function works for libraries that
+    are already loaded in the interpreter and can be changed dynamically.
+
+    This effect is global and impacts the whole Python process. There is no thread level
+    isolation as these libraries do not offer thread-local APIs to configure the number
+    of threads to use in nested parallel calls.
+
+    Parameters
+    ----------
+    limits : int, dict, 'sequential_blas_under_openmp' or None (default=None)
+        The maximal number of threads that can be used in thread pools
+
+        - If int, sets the maximum number of threads to `limits` for each
+          library selected by `user_api`.
+
+        - If it is a dictionary `{{key: max_threads}}`, this function sets a
+          custom maximum number of threads for each `key` which can be either a
+          `user_api` or a `prefix` for a specific library.
+
+        - If 'sequential_blas_under_openmp', it will chose the appropriate `limits`
+          and `user_api` parameters for the specific use case of sequential BLAS
+          calls within an OpenMP parallel region. The `user_api` parameter is
+          ignored.
+
+        - If None, this function does not do anything.
+
+    user_api : {USER_APIS} or None (default=None)
+        APIs of libraries to limit. Used only if `limits` is an int.
+
+        - If "blas", it will only limit BLAS supported libraries ({BLAS_LIBS}).
+
+        - If "openmp", it will only limit OpenMP supported libraries
+          ({OPENMP_LIBS}). Note that it can affect the number of threads used
+          by the BLAS libraries if they rely on OpenMP.
+
+        - If None, this function will apply to all supported libraries.
+    """
+
+    def __init__(self, limits=None, user_api=None):
+        super().__init__(ThreadpoolController(), limits=limits, user_api=user_api)
+
+    @classmethod
+    def wrap(cls, limits=None, user_api=None):
+        return super().wrap(ThreadpoolController(), limits=limits, user_api=user_api)
+
+
+class ThreadpoolController:
+    """Collection of LibController objects for all loaded supported libraries
+
+    Attributes
+    ----------
+    lib_controllers : list of `LibController` objects
+        The list of library controllers of all loaded supported libraries.
+    """
+
+    # Cache for libc under POSIX and a few system libraries under Windows.
+    # We use a class level cache instead of an instance level cache because
+    # it's very unlikely that a shared library will be unloaded and reloaded
+    # during the lifetime of a program.
+    _system_libraries = dict()
+
+    def __init__(self):
+        self.lib_controllers = []
+        self._load_libraries()
+        self._warn_if_incompatible_openmp()
+
+    @classmethod
+    def _from_controllers(cls, lib_controllers):
+        new_controller = cls.__new__(cls)
+        new_controller.lib_controllers = lib_controllers
+        return new_controller
+
+    def info(self):
+        """Return lib_controllers info as a list of dicts"""
+        return [lib_controller.info() for lib_controller in self.lib_controllers]
+
+    def select(self, **kwargs):
+        """Return a ThreadpoolController containing a subset of its current
+        library controllers
+
+        It will select all libraries matching at least one pair (key, value) from kwargs
+        where key is an entry of the library info dict (like "user_api", "internal_api",
+        "prefix", ...) and value is the value or a list of acceptable values for that
+        entry.
+
+        For instance, `ThreadpoolController().select(internal_api=["blis", "openblas"])`
+        will select all library controllers whose internal_api is either "blis" or
+        "openblas".
+        """
+        for key, vals in kwargs.items():
+            kwargs[key] = [vals] if not isinstance(vals, list) else vals
+
+        lib_controllers = [
+            lib_controller
+            for lib_controller in self.lib_controllers
+            if any(
+                getattr(lib_controller, key, None) in vals
+                for key, vals in kwargs.items()
+            )
+        ]
+
+        return ThreadpoolController._from_controllers(lib_controllers)
+
+    def _get_params_for_sequential_blas_under_openmp(self):
+        """Return appropriate params to use for a sequential BLAS call in an OpenMP loop
+
+        This function takes into account the unexpected behavior of OpenBLAS with the
+        OpenMP threading layer.
+        """
+        if self.select(
+            internal_api="openblas", threading_layer="openmp"
+        ).lib_controllers:
+            return {"limits": None, "user_api": None}
+        return {"limits": 1, "user_api": "blas"}
+
+    @_format_docstring(
+        USER_APIS=", ".join('"{}"'.format(api) for api in _ALL_USER_APIS),
+        BLAS_LIBS=", ".join(_ALL_BLAS_LIBRARIES),
+        OPENMP_LIBS=", ".join(_ALL_OPENMP_LIBRARIES),
+    )
+    def limit(self, *, limits=None, user_api=None):
+        """Change the maximal number of threads that can be used in thread pools.
+
+        This function returns an object that can be used either as a callable (the
+        construction of this object limits the number of threads) or as a context
+        manager, in a `with` block to automatically restore the original state of the
+        controlled libraries when exiting the block.
+
+        Set the maximal number of threads that can be used in thread pools used in
+        the supported libraries to `limits`. This function works for libraries that
+        are already loaded in the interpreter and can be changed dynamically.
+
+        This effect is global and impacts the whole Python process. There is no thread
+        level isolation as these libraries do not offer thread-local APIs to configure
+        the number of threads to use in nested parallel calls.
+
+        Parameters
+        ----------
+        limits : int, dict, 'sequential_blas_under_openmp' or None (default=None)
+            The maximal number of threads that can be used in thread pools
+
+            - If int, sets the maximum number of threads to `limits` for each
+              library selected by `user_api`.
+
+            - If it is a dictionary `{{key: max_threads}}`, this function sets a
+              custom maximum number of threads for each `key` which can be either a
+              `user_api` or a `prefix` for a specific library.
+
+            - If 'sequential_blas_under_openmp', it will chose the appropriate `limits`
+              and `user_api` parameters for the specific use case of sequential BLAS
+              calls within an OpenMP parallel region. The `user_api` parameter is
+              ignored.
+
+            - If None, this function does not do anything.
+
+        user_api : {USER_APIS} or None (default=None)
+            APIs of libraries to limit. Used only if `limits` is an int.
+
+            - If "blas", it will only limit BLAS supported libraries ({BLAS_LIBS}).
+
+            - If "openmp", it will only limit OpenMP supported libraries
+              ({OPENMP_LIBS}). Note that it can affect the number of threads used
+              by the BLAS libraries if they rely on OpenMP.
+
+            - If None, this function will apply to all supported libraries.
+        """
+        return _ThreadpoolLimiter(self, limits=limits, user_api=user_api)
+
+    @_format_docstring(
+        USER_APIS=", ".join('"{}"'.format(api) for api in _ALL_USER_APIS),
+        BLAS_LIBS=", ".join(_ALL_BLAS_LIBRARIES),
+        OPENMP_LIBS=", ".join(_ALL_OPENMP_LIBRARIES),
+    )
+    def wrap(self, *, limits=None, user_api=None):
+        """Change the maximal number of threads that can be used in thread pools.
+
+        This function returns an object that can be used as a decorator.
+
+        Set the maximal number of threads that can be used in thread pools used in
+        the supported libraries to `limits`. This function works for libraries that
+        are already loaded in the interpreter and can be changed dynamically.
+
+        Parameters
+        ----------
+        limits : int, dict or None (default=None)
+            The maximal number of threads that can be used in thread pools
+
+            - If int, sets the maximum number of threads to `limits` for each
+              library selected by `user_api`.
+
+            - If it is a dictionary `{{key: max_threads}}`, this function sets a
+              custom maximum number of threads for each `key` which can be either a
+              `user_api` or a `prefix` for a specific library.
+
+            - If None, this function does not do anything.
+
+        user_api : {USER_APIS} or None (default=None)
+            APIs of libraries to limit. Used only if `limits` is an int.
+
+            - If "blas", it will only limit BLAS supported libraries ({BLAS_LIBS}).
+
+            - If "openmp", it will only limit OpenMP supported libraries
+              ({OPENMP_LIBS}). Note that it can affect the number of threads used
+              by the BLAS libraries if they rely on OpenMP.
+
+            - If None, this function will apply to all supported libraries.
+        """
+        return _ThreadpoolLimiter.wrap(self, limits=limits, user_api=user_api)
+
+    def __len__(self):
+        return len(self.lib_controllers)
+
+    def _load_libraries(self):
+        """Loop through loaded shared libraries and store the supported ones"""
+        if sys.platform == "darwin":
+            self._find_libraries_with_dyld()
+        elif sys.platform == "win32":
+            self._find_libraries_with_enum_process_module_ex()
+        elif "pyodide" in sys.modules:
+            self._find_libraries_pyodide()
+        else:
+            self._find_libraries_with_dl_iterate_phdr()
+
+    def _find_libraries_with_dl_iterate_phdr(self):
+        """Loop through loaded libraries and return binders on supported ones
+
+        This function is expected to work on POSIX system only.
+        This code is adapted from code by Intel developer @anton-malakhov
+        available at https://github.com/IntelPython/smp
+
+        Copyright (c) 2017, Intel Corporation published under the BSD 3-Clause
+        license
+        """
+        libc = self._get_libc()
+        if not hasattr(libc, "dl_iterate_phdr"):  # pragma: no cover
+            warnings.warn(
+                "Could not find dl_iterate_phdr in the C standard library.",
+                RuntimeWarning,
+            )
+            return []
+
+        # Callback function for `dl_iterate_phdr` which is called for every
+        # library loaded in the current process until it returns 1.
+        def match_library_callback(info, size, data):
+            # Get the path of the current library
+            filepath = info.contents.dlpi_name
+            if filepath:
+                filepath = filepath.decode("utf-8")
+
+                # Store the library controller if it is supported and selected
+                self._make_controller_from_path(filepath)
+            return 0
+
+        c_func_signature = ctypes.CFUNCTYPE(
+            ctypes.c_int,  # Return type
+            ctypes.POINTER(_dl_phdr_info),
+            ctypes.c_size_t,
+            ctypes.c_char_p,
+        )
+        c_match_library_callback = c_func_signature(match_library_callback)
+
+        data = ctypes.c_char_p(b"")
+        libc.dl_iterate_phdr(c_match_library_callback, data)
+
+    def _find_libraries_with_dyld(self):
+        """Loop through loaded libraries and return binders on supported ones
+
+        This function is expected to work on OSX system only
+        """
+        libc = self._get_libc()
+        if not hasattr(libc, "_dyld_image_count"):  # pragma: no cover
+            warnings.warn(
+                "Could not find _dyld_image_count in the C standard library.",
+                RuntimeWarning,
+            )
+            return []
+
+        n_dyld = libc._dyld_image_count()
+        libc._dyld_get_image_name.restype = ctypes.c_char_p
+
+        for i in range(n_dyld):
+            filepath = ctypes.string_at(libc._dyld_get_image_name(i))
+            filepath = filepath.decode("utf-8")
+
+            # Store the library controller if it is supported and selected
+            self._make_controller_from_path(filepath)
+
+    def _find_libraries_with_enum_process_module_ex(self):
+        """Loop through loaded libraries and return binders on supported ones
+
+        This function is expected to work on windows system only.
+        This code is adapted from code by Philipp Hagemeister @phihag available
+        at https://stackoverflow.com/questions/17474574
+        """
+        from ctypes.wintypes import DWORD, HMODULE, MAX_PATH
+
+        PROCESS_QUERY_INFORMATION = 0x0400
+        PROCESS_VM_READ = 0x0010
+
+        LIST_LIBRARIES_ALL = 0x03
+
+        ps_api = self._get_windll("Psapi")
+        kernel_32 = self._get_windll("kernel32")
+
+        h_process = kernel_32.OpenProcess(
+            PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, False, os.getpid()
+        )
+        if not h_process:  # pragma: no cover
+            raise OSError(f"Could not open PID {os.getpid()}")
+
+        try:
+            buf_count = 256
+            needed = DWORD()
+            # Grow the buffer until it becomes large enough to hold all the
+            # module headers
+            while True:
+                buf = (HMODULE * buf_count)()
+                buf_size = ctypes.sizeof(buf)
+                if not ps_api.EnumProcessModulesEx(
+                    h_process,
+                    ctypes.byref(buf),
+                    buf_size,
+                    ctypes.byref(needed),
+                    LIST_LIBRARIES_ALL,
+                ):
+                    raise OSError("EnumProcessModulesEx failed")
+                if buf_size >= needed.value:
+                    break
+                buf_count = needed.value // (buf_size // buf_count)
+
+            count = needed.value // (buf_size // buf_count)
+            h_modules = map(HMODULE, buf[:count])
+
+            # Loop through all the module headers and get the library path
+            # Allocate a buffer for the path 10 times the size of MAX_PATH to take
+            # into account long path names.
+            max_path = 10 * MAX_PATH
+            buf = ctypes.create_unicode_buffer(max_path)
+            n_size = DWORD()
+            for h_module in h_modules:
+                # Get the path of the current module
+                if not ps_api.GetModuleFileNameExW(
+                    h_process, h_module, ctypes.byref(buf), ctypes.byref(n_size)
+                ):
+                    raise OSError("GetModuleFileNameEx failed")
+                filepath = buf.value
+
+                if len(filepath) == max_path:  # pragma: no cover
+                    warnings.warn(
+                        "Could not get the full path of a dynamic library (path too "
+                        "long). This library will be ignored and threadpoolctl might "
+                        "not be able to control or display information about all "
+                        f"loaded libraries. Here's the truncated path: {filepath!r}",
+                        RuntimeWarning,
+                    )
+                else:
+                    # Store the library controller if it is supported and selected
+                    self._make_controller_from_path(filepath)
+        finally:
+            kernel_32.CloseHandle(h_process)
+
+    def _find_libraries_pyodide(self):
+        """Pyodide specific implementation for finding loaded libraries.
+
+        Adapted from suggestion in https://github.com/joblib/threadpoolctl/pull/169#issuecomment-1946696449.
+
+        One day, we may have a simpler solution. libc dl_iterate_phdr needs to
+        be implemented in Emscripten and exposed in Pyodide, see
+        https://github.com/emscripten-core/emscripten/issues/21354 for more
+        details.
+        """
+        try:
+            from pyodide_js._module import LDSO
+        except ImportError:
+            warnings.warn(
+                "Unable to import LDSO from pyodide_js._module. This should never "
+                "happen."
+            )
+            return
+
+        for filepath in LDSO.loadedLibsByName.as_object_map():
+            # Some libraries are duplicated by Pyodide and do not exist in the
+            # filesystem, so we first check for the existence of the file. For
+            # more details, see
+            # https://github.com/joblib/threadpoolctl/pull/169#issuecomment-1947946728
+            if os.path.exists(filepath):
+                self._make_controller_from_path(filepath)
+
+    def _make_controller_from_path(self, filepath):
+        """Store a library controller if it is supported and selected"""
+        # Required to resolve symlinks
+        filepath = _realpath(filepath)
+        # `lower` required to take account of OpenMP dll case on Windows
+        # (vcomp, VCOMP, Vcomp, ...)
+        filename = os.path.basename(filepath).lower()
+
+        # Loop through supported libraries to find if this filename corresponds
+        # to a supported one.
+        for controller_class in _ALL_CONTROLLERS:
+            # check if filename matches a supported prefix
+            prefix = self._check_prefix(filename, controller_class.filename_prefixes)
+
+            # filename does not match any of the prefixes of the candidate
+            # library. move to next library.
+            if prefix is None:
+                continue
+
+            # workaround for BLAS libraries packaged by conda-forge on windows, which
+            # are all renamed "libblas.dll". We thus have to check to which BLAS
+            # implementation it actually corresponds looking for implementation
+            # specific symbols.
+            if prefix == "libblas":
+                if filename.endswith(".dll"):
+                    libblas = ctypes.CDLL(filepath, _RTLD_NOLOAD)
+                    if not any(
+                        hasattr(libblas, func)
+                        for func in controller_class.check_symbols
+                    ):
+                        continue
+                else:
+                    # We ignore libblas on other platforms than windows because there
+                    # might be a libblas dso comming with openblas for instance that
+                    # can't be used to instantiate a pertinent LibController (many
+                    # symbols are missing) and would create confusion by making a
+                    # duplicate entry in threadpool_info.
+                    continue
+
+            # filename matches a prefix. Now we check if the library has the symbols we
+            # are looking for. If none of the symbols exists, it's very likely not the
+            # expected library (e.g. a library having a common prefix with one of the
+            # our supported libraries). Otherwise, create and store the library
+            # controller.
+            lib_controller = controller_class(
+                filepath=filepath, prefix=prefix, parent=self
+            )
+
+            if filepath in (lib.filepath for lib in self.lib_controllers):
+                # We already have a controller for this library.
+                continue
+
+            if not hasattr(controller_class, "check_symbols") or any(
+                hasattr(lib_controller.dynlib, func)
+                for func in controller_class.check_symbols
+            ):
+                self.lib_controllers.append(lib_controller)
+
+    def _check_prefix(self, library_basename, filename_prefixes):
+        """Return the prefix library_basename starts with
+
+        Return None if none matches.
+        """
+        for prefix in filename_prefixes:
+            if library_basename.startswith(prefix):
+                return prefix
+        return None
+
+    def _warn_if_incompatible_openmp(self):
+        """Raise a warning if llvm-OpenMP and intel-OpenMP are both loaded"""
+        prefixes = [lib_controller.prefix for lib_controller in self.lib_controllers]
+        msg = textwrap.dedent(
+            """
+            Found Intel OpenMP ('libiomp') and LLVM OpenMP ('libomp') loaded at
+            the same time. Both libraries are known to be incompatible and this
+            can cause random crashes or deadlocks on Linux when loaded in the
+            same Python program.
+            Using threadpoolctl may cause crashes or deadlocks. For more
+            information and possible workarounds, please see
+                https://github.com/joblib/threadpoolctl/blob/master/multiple_openmp.md
+            """
+        )
+        if "libomp" in prefixes and "libiomp" in prefixes:
+            warnings.warn(msg, RuntimeWarning)
+
+    @classmethod
+    def _get_libc(cls):
+        """Load the lib-C for unix systems."""
+        libc = cls._system_libraries.get("libc")
+        if libc is None:
+            # Remark: If libc is statically linked or if Python is linked against an
+            # alternative implementation of libc like musl, find_library will return
+            # None and CDLL will load the main program itself which should contain the
+            # libc symbols. We still name it libc for convenience.
+            # If the main program does not contain the libc symbols, it's ok because
+            # we check their presence later anyway.
+            libc = ctypes.CDLL(find_library("c"), mode=_RTLD_NOLOAD)
+            cls._system_libraries["libc"] = libc
+        return libc
+
+    @classmethod
+    def _get_windll(cls, dll_name):
+        """Load a windows DLL"""
+        dll = cls._system_libraries.get(dll_name)
+        if dll is None:
+            dll = ctypes.WinDLL(f"{dll_name}.dll")
+            cls._system_libraries[dll_name] = dll
+        return dll
+
+
+def _main():
+    """Commandline interface to display thread-pool information and exit."""
+    import argparse
+    import importlib
+    import json
+    import sys
+
+    parser = argparse.ArgumentParser(
+        usage="python -m threadpoolctl -i numpy scipy.linalg xgboost",
+        description="Display thread-pool information and exit.",
+    )
+    parser.add_argument(
+        "-i",
+        "--import",
+        dest="modules",
+        nargs="*",
+        default=(),
+        help="Python modules to import before introspecting thread-pools.",
+    )
+    parser.add_argument(
+        "-c",
+        "--command",
+        help="a Python statement to execute before introspecting thread-pools.",
+    )
+
+    options = parser.parse_args(sys.argv[1:])
+    for module in options.modules:
+        try:
+            importlib.import_module(module, package=None)
+        except ImportError:
+            print("WARNING: could not import", module, file=sys.stderr)
+
+    if options.command:
+        exec(options.command)
+
+    print(json.dumps(threadpool_info(), indent=2))
+
+
+if __name__ == "__main__":
+    _main()
diff --git a/.venv/lib/python3.12/site-packages/torch-2.8.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/torch-2.8.0.dist-info/INSTALLER
new file mode 100644
index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/torch-2.8.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+uv
\ No newline at end of file
diff --git a/.venv/lib/python3.12/site-packages/torch-2.8.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/torch-2.8.0.dist-info/METADATA
new file mode 100644
index 0000000000000000000000000000000000000000..675f7f55af4118581ea344c202f840b5a256635c
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/torch-2.8.0.dist-info/METADATA
@@ -0,0 +1,644 @@
+Metadata-Version: 2.4
+Name: torch
+Version: 2.8.0
+Summary: Tensors and Dynamic neural networks in Python with strong GPU acceleration
+Home-page: https://pytorch.org/
+Download-URL: https://github.com/pytorch/pytorch/tags
+Author: PyTorch Team
+Author-email: packages@pytorch.org
+License: BSD-3-Clause
+Project-URL: Homepage, https://pytorch.org/
+Project-URL: Documentation, https://pytorch.org/docs/
+Project-URL: Source, https://github.com/pytorch/pytorch
+Project-URL: Forum, https://discuss.pytorch.org/
+Keywords: pytorch,machine learning
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Education
+Classifier: Intended Audience :: Science/Research
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Topic :: Scientific/Engineering
+Classifier: Topic :: Scientific/Engineering :: Mathematics
+Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
+Classifier: Topic :: Software Development
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Programming Language :: C++
+Classifier: Programming Language :: Python :: 3
+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
+Requires-Python: >=3.9.0
+Description-Content-Type: text/markdown
+License-File: LICENSE
+License-File: NOTICE
+Requires-Dist: filelock
+Requires-Dist: typing-extensions>=4.10.0
+Requires-Dist: setuptools; python_version >= "3.12"
+Requires-Dist: sympy>=1.13.3
+Requires-Dist: networkx
+Requires-Dist: jinja2
+Requires-Dist: fsspec
+Requires-Dist: nvidia-cuda-nvrtc-cu12==12.8.93; platform_system == "Linux" and platform_machine == "x86_64"
+Requires-Dist: nvidia-cuda-runtime-cu12==12.8.90; platform_system == "Linux" and platform_machine == "x86_64"
+Requires-Dist: nvidia-cuda-cupti-cu12==12.8.90; platform_system == "Linux" and platform_machine == "x86_64"
+Requires-Dist: nvidia-cudnn-cu12==9.10.2.21; platform_system == "Linux" and platform_machine == "x86_64"
+Requires-Dist: nvidia-cublas-cu12==12.8.4.1; platform_system == "Linux" and platform_machine == "x86_64"
+Requires-Dist: nvidia-cufft-cu12==11.3.3.83; platform_system == "Linux" and platform_machine == "x86_64"
+Requires-Dist: nvidia-curand-cu12==10.3.9.90; platform_system == "Linux" and platform_machine == "x86_64"
+Requires-Dist: nvidia-cusolver-cu12==11.7.3.90; platform_system == "Linux" and platform_machine == "x86_64"
+Requires-Dist: nvidia-cusparse-cu12==12.5.8.93; platform_system == "Linux" and platform_machine == "x86_64"
+Requires-Dist: nvidia-cusparselt-cu12==0.7.1; platform_system == "Linux" and platform_machine == "x86_64"
+Requires-Dist: nvidia-nccl-cu12==2.27.3; platform_system == "Linux" and platform_machine == "x86_64"
+Requires-Dist: nvidia-nvtx-cu12==12.8.90; platform_system == "Linux" and platform_machine == "x86_64"
+Requires-Dist: nvidia-nvjitlink-cu12==12.8.93; platform_system == "Linux" and platform_machine == "x86_64"
+Requires-Dist: nvidia-cufile-cu12==1.13.1.3; platform_system == "Linux" and platform_machine == "x86_64"
+Requires-Dist: triton==3.4.0; platform_system == "Linux" and platform_machine == "x86_64"
+Provides-Extra: optree
+Requires-Dist: optree>=0.13.0; extra == "optree"
+Provides-Extra: opt-einsum
+Requires-Dist: opt-einsum>=3.3; extra == "opt-einsum"
+Provides-Extra: pyyaml
+Requires-Dist: pyyaml; extra == "pyyaml"
+Dynamic: author
+Dynamic: author-email
+Dynamic: classifier
+Dynamic: description
+Dynamic: description-content-type
+Dynamic: download-url
+Dynamic: home-page
+Dynamic: keywords
+Dynamic: license-file
+Dynamic: provides-extra
+Dynamic: requires-dist
+Dynamic: requires-python
+Dynamic: summary
+
+![PyTorch Logo](https://github.com/pytorch/pytorch/raw/main/docs/source/_static/img/pytorch-logo-dark.png)
+
+--------------------------------------------------------------------------------
+
+PyTorch is a Python package that provides two high-level features:
+- Tensor computation (like NumPy) with strong GPU acceleration
+- Deep neural networks built on a tape-based autograd system
+
+You can reuse your favorite Python packages such as NumPy, SciPy, and Cython to extend PyTorch when needed.
+
+Our trunk health (Continuous Integration signals) can be found at [hud.pytorch.org](https://hud.pytorch.org/ci/pytorch/pytorch/main).
+
+
+
+- [More About PyTorch](#more-about-pytorch)
+  - [A GPU-Ready Tensor Library](#a-gpu-ready-tensor-library)
+  - [Dynamic Neural Networks: Tape-Based Autograd](#dynamic-neural-networks-tape-based-autograd)
+  - [Python First](#python-first)
+  - [Imperative Experiences](#imperative-experiences)
+  - [Fast and Lean](#fast-and-lean)
+  - [Extensions Without Pain](#extensions-without-pain)
+- [Installation](#installation)
+  - [Binaries](#binaries)
+    - [NVIDIA Jetson Platforms](#nvidia-jetson-platforms)
+  - [From Source](#from-source)
+    - [Prerequisites](#prerequisites)
+      - [NVIDIA CUDA Support](#nvidia-cuda-support)
+      - [AMD ROCm Support](#amd-rocm-support)
+      - [Intel GPU Support](#intel-gpu-support)
+    - [Get the PyTorch Source](#get-the-pytorch-source)
+    - [Install Dependencies](#install-dependencies)
+    - [Install PyTorch](#install-pytorch)
+      - [Adjust Build Options (Optional)](#adjust-build-options-optional)
+  - [Docker Image](#docker-image)
+    - [Using pre-built images](#using-pre-built-images)
+    - [Building the image yourself](#building-the-image-yourself)
+  - [Building the Documentation](#building-the-documentation)
+    - [Building a PDF](#building-a-pdf)
+  - [Previous Versions](#previous-versions)
+- [Getting Started](#getting-started)
+- [Resources](#resources)
+- [Communication](#communication)
+- [Releases and Contributing](#releases-and-contributing)
+- [The Team](#the-team)
+- [License](#license)
+
+
+
+## More About PyTorch
+
+[Learn the basics of PyTorch](https://pytorch.org/tutorials/beginner/basics/intro.html)
+
+At a granular level, PyTorch is a library that consists of the following components:
+
+| Component | Description |
+| ---- | --- |
+| [**torch**](https://pytorch.org/docs/stable/torch.html) | A Tensor library like NumPy, with strong GPU support |
+| [**torch.autograd**](https://pytorch.org/docs/stable/autograd.html) | A tape-based automatic differentiation library that supports all differentiable Tensor operations in torch |
+| [**torch.jit**](https://pytorch.org/docs/stable/jit.html) | A compilation stack (TorchScript) to create serializable and optimizable models from PyTorch code  |
+| [**torch.nn**](https://pytorch.org/docs/stable/nn.html) | A neural networks library deeply integrated with autograd designed for maximum flexibility |
+| [**torch.multiprocessing**](https://pytorch.org/docs/stable/multiprocessing.html) | Python multiprocessing, but with magical memory sharing of torch Tensors across processes. Useful for data loading and Hogwild training |
+| [**torch.utils**](https://pytorch.org/docs/stable/data.html) | DataLoader and other utility functions for convenience |
+
+Usually, PyTorch is used either as:
+
+- A replacement for NumPy to use the power of GPUs.
+- A deep learning research platform that provides maximum flexibility and speed.
+
+Elaborating Further:
+
+### A GPU-Ready Tensor Library
+
+If you use NumPy, then you have used Tensors (a.k.a. ndarray).
+
+![Tensor illustration](./docs/source/_static/img/tensor_illustration.png)
+
+PyTorch provides Tensors that can live either on the CPU or the GPU and accelerates the
+computation by a huge amount.
+
+We provide a wide variety of tensor routines to accelerate and fit your scientific computation needs
+such as slicing, indexing, mathematical operations, linear algebra, reductions.
+And they are fast!
+
+### Dynamic Neural Networks: Tape-Based Autograd
+
+PyTorch has a unique way of building neural networks: using and replaying a tape recorder.
+
+Most frameworks such as TensorFlow, Theano, Caffe, and CNTK have a static view of the world.
+One has to build a neural network and reuse the same structure again and again.
+Changing the way the network behaves means that one has to start from scratch.
+
+With PyTorch, we use a technique called reverse-mode auto-differentiation, which allows you to
+change the way your network behaves arbitrarily with zero lag or overhead. Our inspiration comes
+from several research papers on this topic, as well as current and past work such as
+[torch-autograd](https://github.com/twitter/torch-autograd),
+[autograd](https://github.com/HIPS/autograd),
+[Chainer](https://chainer.org), etc.
+
+While this technique is not unique to PyTorch, it's one of the fastest implementations of it to date.
+You get the best of speed and flexibility for your crazy research.
+
+![Dynamic graph](https://github.com/pytorch/pytorch/raw/main/docs/source/_static/img/dynamic_graph.gif)
+
+### Python First
+
+PyTorch is not a Python binding into a monolithic C++ framework.
+It is built to be deeply integrated into Python.
+You can use it naturally like you would use [NumPy](https://www.numpy.org/) / [SciPy](https://www.scipy.org/) / [scikit-learn](https://scikit-learn.org) etc.
+You can write your new neural network layers in Python itself, using your favorite libraries
+and use packages such as [Cython](https://cython.org/) and [Numba](http://numba.pydata.org/).
+Our goal is to not reinvent the wheel where appropriate.
+
+### Imperative Experiences
+
+PyTorch is designed to be intuitive, linear in thought, and easy to use.
+When you execute a line of code, it gets executed. There isn't an asynchronous view of the world.
+When you drop into a debugger or receive error messages and stack traces, understanding them is straightforward.
+The stack trace points to exactly where your code was defined.
+We hope you never spend hours debugging your code because of bad stack traces or asynchronous and opaque execution engines.
+
+### Fast and Lean
+
+PyTorch has minimal framework overhead. We integrate acceleration libraries
+such as [Intel MKL](https://software.intel.com/mkl) and NVIDIA ([cuDNN](https://developer.nvidia.com/cudnn), [NCCL](https://developer.nvidia.com/nccl)) to maximize speed.
+At the core, its CPU and GPU Tensor and neural network backends
+are mature and have been tested for years.
+
+Hence, PyTorch is quite fast — whether you run small or large neural networks.
+
+The memory usage in PyTorch is extremely efficient compared to Torch or some of the alternatives.
+We've written custom memory allocators for the GPU to make sure that
+your deep learning models are maximally memory efficient.
+This enables you to train bigger deep learning models than before.
+
+### Extensions Without Pain
+
+Writing new neural network modules, or interfacing with PyTorch's Tensor API was designed to be straightforward
+and with minimal abstractions.
+
+You can write new neural network layers in Python using the torch API
+[or your favorite NumPy-based libraries such as SciPy](https://pytorch.org/tutorials/advanced/numpy_extensions_tutorial.html).
+
+If you want to write your layers in C/C++, we provide a convenient extension API that is efficient and with minimal boilerplate.
+No wrapper code needs to be written. You can see [a tutorial here](https://pytorch.org/tutorials/advanced/cpp_extension.html) and [an example here](https://github.com/pytorch/extension-cpp).
+
+
+## Installation
+
+### Binaries
+Commands to install binaries via Conda or pip wheels are on our website: [https://pytorch.org/get-started/locally/](https://pytorch.org/get-started/locally/)
+
+
+#### NVIDIA Jetson Platforms
+
+Python wheels for NVIDIA's Jetson Nano, Jetson TX1/TX2, Jetson Xavier NX/AGX, and Jetson AGX Orin are provided [here](https://forums.developer.nvidia.com/t/pytorch-for-jetson-version-1-10-now-available/72048) and the L4T container is published [here](https://catalog.ngc.nvidia.com/orgs/nvidia/containers/l4t-pytorch)
+
+They require JetPack 4.2 and above, and [@dusty-nv](https://github.com/dusty-nv) and [@ptrblck](https://github.com/ptrblck) are maintaining them.
+
+
+### From Source
+
+#### Prerequisites
+If you are installing from source, you will need:
+- Python 3.9 or later
+- A compiler that fully supports C++17, such as clang or gcc (gcc 9.4.0 or newer is required, on Linux)
+- Visual Studio or Visual Studio Build Tool (Windows only)
+
+\* PyTorch CI uses Visual C++ BuildTools, which come with Visual Studio Enterprise,
+Professional, or Community Editions. You can also install the build tools from
+https://visualstudio.microsoft.com/visual-cpp-build-tools/. The build tools *do not*
+come with Visual Studio Code by default.
+
+An example of environment setup is shown below:
+
+* Linux:
+
+```bash
+$ source /bin/activate
+$ conda create -y -n 
+$ conda activate 
+```
+
+* Windows:
+
+```bash
+$ source \Scripts\activate.bat
+$ conda create -y -n 
+$ conda activate 
+$ call "C:\Program Files\Microsoft Visual Studio\\Community\VC\Auxiliary\Build\vcvarsall.bat" x64
+```
+
+A conda environment is not required.  You can also do a PyTorch build in a
+standard virtual environment, e.g., created with tools like `uv`, provided
+your system has installed all the necessary dependencies unavailable as pip
+packages (e.g., CUDA, MKL.)
+
+##### NVIDIA CUDA Support
+If you want to compile with CUDA support, [select a supported version of CUDA from our support matrix](https://pytorch.org/get-started/locally/), then install the following:
+- [NVIDIA CUDA](https://developer.nvidia.com/cuda-downloads)
+- [NVIDIA cuDNN](https://developer.nvidia.com/cudnn) v8.5 or above
+- [Compiler](https://gist.github.com/ax3l/9489132) compatible with CUDA
+
+Note: You could refer to the [cuDNN Support Matrix](https://docs.nvidia.com/deeplearning/cudnn/backend/latest/reference/support-matrix.html) for cuDNN versions with the various supported CUDA, CUDA driver and NVIDIA hardware
+
+If you want to disable CUDA support, export the environment variable `USE_CUDA=0`.
+Other potentially useful environment variables may be found in `setup.py`.  If
+CUDA is installed in a non-standard location, set PATH so that the nvcc you
+want to use can be found (e.g., `export PATH=/usr/local/cuda-12.8/bin:$PATH`).
+
+If you are building for NVIDIA's Jetson platforms (Jetson Nano, TX1, TX2, AGX Xavier), Instructions to install PyTorch for Jetson Nano are [available here](https://devtalk.nvidia.com/default/topic/1049071/jetson-nano/pytorch-for-jetson-nano/)
+
+##### AMD ROCm Support
+If you want to compile with ROCm support, install
+- [AMD ROCm](https://rocm.docs.amd.com/en/latest/deploy/linux/quick_start.html) 4.0 and above installation
+- ROCm is currently supported only for Linux systems.
+
+By default the build system expects ROCm to be installed in `/opt/rocm`. If ROCm is installed in a different directory, the `ROCM_PATH` environment variable must be set to the ROCm installation directory. The build system automatically detects the AMD GPU architecture. Optionally, the AMD GPU architecture can be explicitly set with the `PYTORCH_ROCM_ARCH` environment variable [AMD GPU architecture](https://rocm.docs.amd.com/projects/install-on-linux/en/latest/reference/system-requirements.html#supported-gpus)
+
+If you want to disable ROCm support, export the environment variable `USE_ROCM=0`.
+Other potentially useful environment variables may be found in `setup.py`.
+
+##### Intel GPU Support
+If you want to compile with Intel GPU support, follow these
+- [PyTorch Prerequisites for Intel GPUs](https://www.intel.com/content/www/us/en/developer/articles/tool/pytorch-prerequisites-for-intel-gpus.html) instructions.
+- Intel GPU is supported for Linux and Windows.
+
+If you want to disable Intel GPU support, export the environment variable `USE_XPU=0`.
+Other potentially useful environment variables may be found in `setup.py`.
+
+#### Get the PyTorch Source
+```bash
+git clone https://github.com/pytorch/pytorch
+cd pytorch
+# if you are updating an existing checkout
+git submodule sync
+git submodule update --init --recursive
+```
+
+#### Install Dependencies
+
+**Common**
+
+```bash
+conda install cmake ninja
+# Run this command from the PyTorch directory after cloning the source code using the “Get the PyTorch Source“ section below
+pip install -r requirements.txt
+```
+
+**On Linux**
+
+```bash
+pip install mkl-static mkl-include
+# CUDA only: Add LAPACK support for the GPU if needed
+# magma installation: run with active conda environment. specify CUDA version to install
+.ci/docker/common/install_magma_conda.sh 12.4
+
+# (optional) If using torch.compile with inductor/triton, install the matching version of triton
+# Run from the pytorch directory after cloning
+# For Intel GPU support, please explicitly `export USE_XPU=1` before running command.
+make triton
+```
+
+**On MacOS**
+
+```bash
+# Add this package on intel x86 processor machines only
+pip install mkl-static mkl-include
+# Add these packages if torch.distributed is needed
+conda install pkg-config libuv
+```
+
+**On Windows**
+
+```bash
+pip install mkl-static mkl-include
+# Add these packages if torch.distributed is needed.
+# Distributed package support on Windows is a prototype feature and is subject to changes.
+conda install -c conda-forge libuv=1.39
+```
+
+#### Install PyTorch
+**On Linux**
+
+If you're compiling for AMD ROCm then first run this command:
+```bash
+# Only run this if you're compiling for ROCm
+python tools/amd_build/build_amd.py
+```
+
+Install PyTorch
+```bash
+export CMAKE_PREFIX_PATH="${CONDA_PREFIX:-'$(dirname $(which conda))/../'}:${CMAKE_PREFIX_PATH}"
+python setup.py develop
+```
+
+**On macOS**
+
+```bash
+python3 setup.py develop
+```
+
+**On Windows**
+
+If you want to build legacy python code, please refer to [Building on legacy code and CUDA](https://github.com/pytorch/pytorch/blob/main/CONTRIBUTING.md#building-on-legacy-code-and-cuda)
+
+**CPU-only builds**
+
+In this mode PyTorch computations will run on your CPU, not your GPU.
+
+```cmd
+python setup.py develop
+```
+
+Note on OpenMP: The desired OpenMP implementation is Intel OpenMP (iomp). In order to link against iomp, you'll need to manually download the library and set up the building environment by tweaking `CMAKE_INCLUDE_PATH` and `LIB`. The instruction [here](https://github.com/pytorch/pytorch/blob/main/docs/source/notes/windows.rst#building-from-source) is an example for setting up both MKL and Intel OpenMP. Without these configurations for CMake, Microsoft Visual C OpenMP runtime (vcomp) will be used.
+
+**CUDA based build**
+
+In this mode PyTorch computations will leverage your GPU via CUDA for faster number crunching
+
+[NVTX](https://docs.nvidia.com/gameworks/content/gameworkslibrary/nvtx/nvidia_tools_extension_library_nvtx.htm) is needed to build Pytorch with CUDA.
+NVTX is a part of CUDA distributive, where it is called "Nsight Compute". To install it onto an already installed CUDA run CUDA installation once again and check the corresponding checkbox.
+Make sure that CUDA with Nsight Compute is installed after Visual Studio.
+
+Currently, VS 2017 / 2019, and Ninja are supported as the generator of CMake. If `ninja.exe` is detected in `PATH`, then Ninja will be used as the default generator, otherwise, it will use VS 2017 / 2019.
+
If Ninja is selected as the generator, the latest MSVC will get selected as the underlying toolchain. + +Additional libraries such as +[Magma](https://developer.nvidia.com/magma), [oneDNN, a.k.a. MKLDNN or DNNL](https://github.com/oneapi-src/oneDNN), and [Sccache](https://github.com/mozilla/sccache) are often needed. Please refer to the [installation-helper](https://github.com/pytorch/pytorch/tree/main/.ci/pytorch/win-test-helpers/installation-helpers) to install them. + +You can refer to the [build_pytorch.bat](https://github.com/pytorch/pytorch/blob/main/.ci/pytorch/win-test-helpers/build_pytorch.bat) script for some other environment variables configurations + + +```cmd +cmd + +:: Set the environment variables after you have downloaded and unzipped the mkl package, +:: else CMake would throw an error as `Could NOT find OpenMP`. +set CMAKE_INCLUDE_PATH={Your directory}\mkl\include +set LIB={Your directory}\mkl\lib;%LIB% + +:: Read the content in the previous section carefully before you proceed. +:: [Optional] If you want to override the underlying toolset used by Ninja and Visual Studio with CUDA, please run the following script block. +:: "Visual Studio 2019 Developer Command Prompt" will be run automatically. +:: Make sure you have CMake >= 3.12 before you do this when you use the Visual Studio generator. +set CMAKE_GENERATOR_TOOLSET_VERSION=14.27 +set DISTUTILS_USE_SDK=1 +for /f "usebackq tokens=*" %i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -version [15^,17^) -products * -latest -property installationPath`) do call "%i\VC\Auxiliary\Build\vcvarsall.bat" x64 -vcvars_ver=%CMAKE_GENERATOR_TOOLSET_VERSION% + +:: [Optional] If you want to override the CUDA host compiler +set CUDAHOSTCXX=C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.27.29110\bin\HostX64\x64\cl.exe + +python setup.py develop + +``` + +**Intel GPU builds** + +In this mode PyTorch with Intel GPU support will be built. + +Please make sure [the common prerequisites](#prerequisites) as well as [the prerequisites for Intel GPU](#intel-gpu-support) are properly installed and the environment variables are configured prior to starting the build. For build tool support, `Visual Studio 2022` is required. + +Then PyTorch can be built with the command: + +```cmd +:: CMD Commands: +:: Set the CMAKE_PREFIX_PATH to help find corresponding packages +:: %CONDA_PREFIX% only works after `conda activate custom_env` + +if defined CMAKE_PREFIX_PATH ( + set "CMAKE_PREFIX_PATH=%CONDA_PREFIX%\Library;%CMAKE_PREFIX_PATH%" +) else ( + set "CMAKE_PREFIX_PATH=%CONDA_PREFIX%\Library" +) + +python setup.py develop +``` + +##### Adjust Build Options (Optional) + +You can adjust the configuration of cmake variables optionally (without building first), by doing +the following. For example, adjusting the pre-detected directories for CuDNN or BLAS can be done +with such a step. + +On Linux +```bash +export CMAKE_PREFIX_PATH="${CONDA_PREFIX:-'$(dirname $(which conda))/../'}:${CMAKE_PREFIX_PATH}" +CMAKE_ONLY=1 python setup.py build +ccmake build # or cmake-gui build +``` + +On macOS +```bash +export CMAKE_PREFIX_PATH="${CONDA_PREFIX:-'$(dirname $(which conda))/../'}:${CMAKE_PREFIX_PATH}" +MACOSX_DEPLOYMENT_TARGET=10.9 CC=clang CXX=clang++ CMAKE_ONLY=1 python setup.py build +ccmake build # or cmake-gui build +``` + +### Docker Image + +#### Using pre-built images + +You can also pull a pre-built docker image from Docker Hub and run with docker v19.03+ + +```bash +docker run --gpus all --rm -ti --ipc=host pytorch/pytorch:latest +``` + +Please note that PyTorch uses shared memory to share data between processes, so if torch multiprocessing is used (e.g. +for multithreaded data loaders) the default shared memory segment size that container runs with is not enough, and you +should increase shared memory size either with `--ipc=host` or `--shm-size` command line options to `nvidia-docker run`. + +#### Building the image yourself + +**NOTE:** Must be built with a docker version > 18.06 + +The `Dockerfile` is supplied to build images with CUDA 11.1 support and cuDNN v8. +You can pass `PYTHON_VERSION=x.y` make variable to specify which Python version is to be used by Miniconda, or leave it +unset to use the default. + +```bash +make -f docker.Makefile +# images are tagged as docker.io/${your_docker_username}/pytorch +``` + +You can also pass the `CMAKE_VARS="..."` environment variable to specify additional CMake variables to be passed to CMake during the build. +See [setup.py](./setup.py) for the list of available variables. + +```bash +make -f docker.Makefile +``` + +### Building the Documentation + +To build documentation in various formats, you will need [Sphinx](http://www.sphinx-doc.org) +and the pytorch_sphinx_theme2. + +Before you build the documentation locally, ensure `torch` is +installed in your environment. For small fixes, you can install the +nightly version as described in [Getting Started](https://pytorch.org/get-started/locally/). + +For more complex fixes, such as adding a new module and docstrings for +the new module, you might need to install torch [from source](#from-source). +See [Docstring Guidelines](https://github.com/pytorch/pytorch/wiki/Docstring-Guidelines) +for docstring conventions. + +```bash +cd docs/ +pip install -r requirements.txt +make html +make serve +``` + +Run `make` to get a list of all available output formats. + +If you get a katex error run `npm install katex`. If it persists, try +`npm install -g katex` + +> [!NOTE] +> If you installed `nodejs` with a different package manager (e.g., +> `conda`) then `npm` will probably install a version of `katex` that is not +> compatible with your version of `nodejs` and doc builds will fail. +> A combination of versions that is known to work is `node@6.13.1` and +> `katex@0.13.18`. To install the latter with `npm` you can run +> ```npm install -g katex@0.13.18``` + +> [!NOTE] +> If you see a numpy incompatibility error, run: +> ``` +> pip install 'numpy<2' +> ``` + +When you make changes to the dependencies run by CI, edit the +`.ci/docker/requirements-docs.txt` file. + +#### Building a PDF + +To compile a PDF of all PyTorch documentation, ensure you have +`texlive` and LaTeX installed. On macOS, you can install them using: + +``` +brew install --cask mactex +``` + +To create the PDF: + +1. Run: + + ``` + make latexpdf + ``` + + This will generate the necessary files in the `build/latex` directory. + +2. Navigate to this directory and execute: + + ``` + make LATEXOPTS="-interaction=nonstopmode" + ``` + + This will produce a `pytorch.pdf` with the desired content. Run this + command one more time so that it generates the correct table + of contents and index. + +> [!NOTE] +> To view the Table of Contents, switch to the **Table of Contents** +> view in your PDF viewer. + + +### Previous Versions + +Installation instructions and binaries for previous PyTorch versions may be found +on [our website](https://pytorch.org/get-started/previous-versions). + + +## Getting Started + +Three-pointers to get you started: +- [Tutorials: get you started with understanding and using PyTorch](https://pytorch.org/tutorials/) +- [Examples: easy to understand PyTorch code across all domains](https://github.com/pytorch/examples) +- [The API Reference](https://pytorch.org/docs/) +- [Glossary](https://github.com/pytorch/pytorch/blob/main/GLOSSARY.md) + +## Resources + +* [PyTorch.org](https://pytorch.org/) +* [PyTorch Tutorials](https://pytorch.org/tutorials/) +* [PyTorch Examples](https://github.com/pytorch/examples) +* [PyTorch Models](https://pytorch.org/hub/) +* [Intro to Deep Learning with PyTorch from Udacity](https://www.udacity.com/course/deep-learning-pytorch--ud188) +* [Intro to Machine Learning with PyTorch from Udacity](https://www.udacity.com/course/intro-to-machine-learning-nanodegree--nd229) +* [Deep Neural Networks with PyTorch from Coursera](https://www.coursera.org/learn/deep-neural-networks-with-pytorch) +* [PyTorch Twitter](https://twitter.com/PyTorch) +* [PyTorch Blog](https://pytorch.org/blog/) +* [PyTorch YouTube](https://www.youtube.com/channel/UCWXI5YeOsh03QvJ59PMaXFw) + +## Communication +* Forums: Discuss implementations, research, etc. https://discuss.pytorch.org +* GitHub Issues: Bug reports, feature requests, install issues, RFCs, thoughts, etc. +* Slack: The [PyTorch Slack](https://pytorch.slack.com/) hosts a primary audience of moderate to experienced PyTorch users and developers for general chat, online discussions, collaboration, etc. If you are a beginner looking for help, the primary medium is [PyTorch Forums](https://discuss.pytorch.org). If you need a slack invite, please fill this form: https://goo.gl/forms/PP1AGvNHpSaJP8to1 +* Newsletter: No-noise, a one-way email newsletter with important announcements about PyTorch. You can sign-up here: https://eepurl.com/cbG0rv +* Facebook Page: Important announcements about PyTorch. https://www.facebook.com/pytorch +* For brand guidelines, please visit our website at [pytorch.org](https://pytorch.org/) + +## Releases and Contributing + +Typically, PyTorch has three minor releases a year. Please let us know if you encounter a bug by [filing an issue](https://github.com/pytorch/pytorch/issues). + +We appreciate all contributions. If you are planning to contribute back bug-fixes, please do so without any further discussion. + +If you plan to contribute new features, utility functions, or extensions to the core, please first open an issue and discuss the feature with us. +Sending a PR without discussion might end up resulting in a rejected PR because we might be taking the core in a different direction than you might be aware of. + +To learn more about making a contribution to Pytorch, please see our [Contribution page](CONTRIBUTING.md). For more information about PyTorch releases, see [Release page](RELEASE.md). + +## The Team + +PyTorch is a community-driven project with several skillful engineers and researchers contributing to it. + +PyTorch is currently maintained by [Soumith Chintala](http://soumith.ch), [Gregory Chanan](https://github.com/gchanan), [Dmytro Dzhulgakov](https://github.com/dzhulgakov), [Edward Yang](https://github.com/ezyang), and [Nikita Shulga](https://github.com/malfet) with major contributions coming from hundreds of talented individuals in various forms and means. +A non-exhaustive but growing list needs to mention: [Trevor Killeen](https://github.com/killeent), [Sasank Chilamkurthy](https://github.com/chsasank), [Sergey Zagoruyko](https://github.com/szagoruyko), [Adam Lerer](https://github.com/adamlerer), [Francisco Massa](https://github.com/fmassa), [Alykhan Tejani](https://github.com/alykhantejani), [Luca Antiga](https://github.com/lantiga), [Alban Desmaison](https://github.com/albanD), [Andreas Koepf](https://github.com/andreaskoepf), [James Bradbury](https://github.com/jekbradbury), [Zeming Lin](https://github.com/ebetica), [Yuandong Tian](https://github.com/yuandong-tian), [Guillaume Lample](https://github.com/glample), [Marat Dukhan](https://github.com/Maratyszcza), [Natalia Gimelshein](https://github.com/ngimel), [Christian Sarofeen](https://github.com/csarofeen), [Martin Raison](https://github.com/martinraison), [Edward Yang](https://github.com/ezyang), [Zachary Devito](https://github.com/zdevito). + +Note: This project is unrelated to [hughperkins/pytorch](https://github.com/hughperkins/pytorch) with the same name. Hugh is a valuable contributor to the Torch community and has helped with many things Torch and PyTorch. + +## License + +PyTorch has a BSD-style license, as found in the [LICENSE](LICENSE) file. diff --git a/.venv/lib/python3.12/site-packages/torch-2.8.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/torch-2.8.0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..49f4640d0f6964cb9216f1c02b28ae7b928ce46e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/torch-2.8.0.dist-info/RECORD @@ -0,0 +1,11503 @@ +../../../bin/torchfrtrace,sha256=AvA-0qS9oyR16YCE-VY1SCmGe2P7OutUiFAfvKJdQVE,346 +../../../bin/torchrun,sha256=WnlvOO7mXXKcGiTZl9GCs0o3DvoKHHJKWxpArqmbTMs,337 +functorch/_C.cpython-312-x86_64-linux-gnu.so,sha256=CkS0iTKtzdSxtvCM_bQuaO8k4DlbHe6whsnKxQ17MsY,144744 +functorch/__init__.py,sha256=NAwGN21zq-tccaF-ROtv-VWFoPdb7y9iuAt6Hy6QCtc,1037 +functorch/_src/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +functorch/_src/aot_autograd/__init__.py,sha256=SGo7gh6XGYcOxTGf5g-R8Y9AF95ICcJbQwQD6rFyrpQ,291 +functorch/_src/eager_transforms/__init__.py,sha256=kX_52fDvSX9YX9OAwo5bjvJtrxyjEBUJ1PueW8xgsuw,291 +functorch/_src/make_functional/__init__.py,sha256=b3y8s3KhtCqFB8lM4Pi48AwuztUt7NBK-VISZNJYYjw,235 +functorch/_src/vmap/__init__.py,sha256=k8r2Esz6tB5D7U_UA0_BCDaWoOmn8JNVrRqK7nG7_fM,467 +functorch/compile/__init__.py,sha256=fZnNG56VBLfKlXMqX5Rj3tORQYLyxbyoA0rEoEBt3KM,756 +functorch/dim/__init__.py,sha256=0MSmaUmJWGTYlUaN45ThDu-_7-_0vpetOoneHU13h-g,4660 +functorch/dim/batch_tensor.py,sha256=DivqprUhdjkrwvNRL-NO1CQ30z5QuI2npkKwRGgR_cU,668 +functorch/dim/delayed_mul_tensor.py,sha256=MgDt11SI_m6vxEpUbyM3ywWCxQWZj8t-rI_MC8Rhicw,2377 +functorch/dim/dim.py,sha256=LTkho9lWceHJq69uzSYqdo_IlIVTYisF19_p4lIE5w8,3382 +functorch/dim/magic_trace.py,sha256=oUxIOV2TPg0eIStZDoGt03_l_T2vFxGohnhBtL6SB-w,1329 +functorch/dim/op_properties.py,sha256=GpW8Ylgq1YlMilQh6cgNQ66tAiN84WRyEjn_VkbJYFQ,6687 +functorch/dim/reference.py,sha256=mhltVoVKZceUFpd51nSb-Ntn8WUABoJHSlyTf821JjU,20332 +functorch/dim/tree_map.py,sha256=nYN6f98uIYQBq8o-0gx_Ad60GL71ZSnnaKw6evNQWD4,375 +functorch/dim/wrap_type.py,sha256=0V0QVkjtU4Q-wPnw9gpchfUx1yBUrAmnuEDZNrOimPk,1871 +functorch/einops/__init__.py,sha256=qNdomhBnsKNuNNlGsbqqipT9wYQkcxMuEQJKq4zhry8,59 +functorch/einops/_parsing.py,sha256=dEdZ4QULNxaJmGLsI444rtlV24EWW_wrIMnmETCRpmU,12309 +functorch/einops/rearrange.py,sha256=-ehp1M6a6YgdTVni0-Ih28EZIgos5NVu4nuDKdeATEk,8088 +functorch/experimental/__init__.py,sha256=oKN9tnpkCih0kicct1MwkKhGao2Qh7aoc5hsnqLadVE,273 +functorch/experimental/control_flow.py,sha256=CyLqEoRLJT5fSnwcTrmw_3LQk9SD54fJElQ4stSD_64,144 +functorch/experimental/ops.py,sha256=kDGcckdoYwOg9fS4JqvZnsIt8Ss9O6RGeasJSyi0OUY,57 +torch-2.8.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +torch-2.8.0.dist-info/METADATA,sha256=V-3kkmmIV1AxZPmrdhhbwNcKr9HgB44-pPE3pWMgQzw,30748 +torch-2.8.0.dist-info/RECORD,, +torch-2.8.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch-2.8.0.dist-info/WHEEL,sha256=mOLdKIdjcTjKHTFxLm6UV2tZjgE479djWqbDXi2KWvY,113 +torch-2.8.0.dist-info/entry_points.txt,sha256=b8t38q301MKrL4Cxfn-q_mop6bMzdkZsKmm2HsnNsmQ,199 +torch-2.8.0.dist-info/licenses/LICENSE,sha256=OZSRAYIBDFZBpi2Yv25Umh9Z3_rWMlU2_x3Ua0i8vdU,491608 +torch-2.8.0.dist-info/licenses/NOTICE,sha256=wsx78MrsdlLCtGCopHC-oWd_JB5KuOQx3zTPF_Wp_sA,23632 +torch-2.8.0.dist-info/top_level.txt,sha256=MsBcfJyMU15lW1efu5w7Tzd4MenrYHiuaixbHMfAoco,25 +torch/_C.cpython-312-x86_64-linux-gnu.so,sha256=2x5PliCMaylxhlhaBKzuUzA1cFcGJU691JU_uua5AiQ,25481 +torch/_C/_VariableFunctions.pyi,sha256=lpmA0nWXJ7oqEPBEu6oW_fsfyAF4OeZtlnWtrmJG-8s,1165098 +torch/_C/__init__.pyi,sha256=s0utjab9dgV-CqvZCvvXAE3M5qYi9YUrDOOPjRh2NLE,407063 +torch/_C/_aoti.pyi,sha256=gP8rTuk9mN1ZLsy43cpaitglNYOPbjfGGSW3F1e0wQU,6015 +torch/_C/_autograd.pyi,sha256=Yn_21fWMxN0yGTGXhZUi337Na9Iw1NQ8C1c4xh8x1AM,4796 +torch/_C/_cpu.pyi,sha256=zueFlTWiiTs-7ToY72TJctRiGZNTmS8Yi9nhHQzLW-g,434 +torch/_C/_cudnn.pyi,sha256=m4nyLKmUJTRrcb-e94ghGbQ7D_Y1E7eqzueyH6QeANs,317 +torch/_C/_cusparselt.pyi,sha256=kD30gidilch4jKuIr7-ItTvDuEtt6L7vxL1l56sb_mw,32 +torch/_C/_distributed_autograd.pyi,sha256=brb51Q-9UlMWCs4Aoy7ZLHH4hwhYq0acXm_hgfz_5HA,899 +torch/_C/_distributed_c10d.pyi,sha256=6g9e2qijtNWrkDBdi7x1NfDJGkbW-r40gSbafQpYa8Y,23262 +torch/_C/_distributed_rpc.pyi,sha256=0mlTwOqrF608gU4oUnIPc25nQ4zeJ6LWycHcuyfB3e8,6080 +torch/_C/_distributed_rpc_testing.pyi,sha256=2cUz7bIO5y9fKUzfafx6Uh84QNO10gtP3uJXAXmE8Dk,1017 +torch/_C/_dynamo/__init__.pyi,sha256=SaWAOaJcwLykHTgoiZswDLqTWxVbWY886-GnLsmwXh0,166 +torch/_C/_dynamo/compiled_autograd.pyi,sha256=98ncdF_NVVskq8Opr0kkzKWVdkbMw62DHAVhPDPzjYM,513 +torch/_C/_dynamo/eval_frame.pyi,sha256=JxS-6ZMVSoYB5prsfL12Q04TC05lYpiVPpe4Ty21HVg,2171 +torch/_C/_dynamo/guards.pyi,sha256=vmviWUJ18WsGkoaLICpi4QYtJxY-c52pk5d32ZOn-hw,5076 +torch/_C/_export/__init__.pyi,sha256=CUcMBKNDN_31e66pbRuZ9O8ilVR4-RSKAeMFRfRIxS8,257 +torch/_C/_export/pt2_archive_constants.pyi,sha256=ofxeiEoXbZaz64Wo4MV4NX_FCArY5Q28Ural17wLAmE,676 +torch/_C/_functions.pyi,sha256=0aapmQmjpa_pFZT_BWhddVB8jZh0DVLDKzXlWqYfz0k,608 +torch/_C/_functorch.pyi,sha256=7CLoFieigrnAaPHs4hTYc6WVMzWxQF1wvHvA0Wgiqgs,3240 +torch/_C/_instruction_counter.pyi,sha256=xjwkiHBgXbvN8Iw7UNVEvE2YDJKFUU1_EoeTS4prCV8,109 +torch/_C/_itt.pyi,sha256=6fhhHGYgreXbGka-VtqX9FjjPaSznfOmDHPVC171DII,169 +torch/_C/_lazy.pyi,sha256=h8_ZY1JSJTiGebWekqfMvfFGZdt_g8dqFq90TG0-7hI,1015 +torch/_C/_lazy_ts_backend.pyi,sha256=YfYcEssTgLqOHQqUDYg5ZgbdB_D52mG19VETBmh7yuc,326 +torch/_C/_monitor.pyi,sha256=GtW3IUEJrNNjwcOXtMzDqaFNDQLOvkzW5imhmUmWyNs,1438 +torch/_C/_nn.pyi,sha256=r0D2mihc1YKP56zVxXTO-vOm2d5QjFPsQ0xPpzL7wkc,4971 +torch/_C/_nvtx.pyi,sha256=MzekiGde65H1P_cJ_zER9kjXVRDW12eNtXUeItZG3WA,380 +torch/_C/_onnx.pyi,sha256=7xxwokJOBBQxnbox3lGLgMo9RkVGVgtfPBtsKcaGaTE,710 +torch/_C/_profiler.pyi,sha256=CvWdOuZqNr8i2m9HZ_B77Y48rWKX4lqF3NdjaJv_fR4,6244 +torch/_C/_verbose.pyi,sha256=vMdQYMqABMqBFxYykp8_VD0QBaubolczBmCEv-UcA00,134 +torch/_C_flatbuffer/__init__.pyi,sha256=SdmD2nh01AdVYvwMJeo32D5BJW2Ar2pvjQTup0uBIko,544 +torch/_VF.py,sha256=XIfh8pIjzvG0ySWcOScz4E067DFoT-8TkcmAG5oYn94,664 +torch/_VF.pyi,sha256=lpmA0nWXJ7oqEPBEu6oW_fsfyAF4OeZtlnWtrmJG-8s,1165098 +torch/__config__.py,sha256=VCuBweOjH807O10Xrumlb_BrtWNTQMYIGjb3aw6T2po,574 +torch/__future__.py,sha256=yk9l_KWsfVIzUBx9cGr-OdtWmb-pI8ZhcROAm3a_FQw,3185 +torch/__init__.py,sha256=Lw3rZtXf9rnAKmKDLDvzgkwu4DHEYqOv65yhcEZtpb8,102960 +torch/_appdirs.py,sha256=UJC2RLztWpH41ra7hw94K75FuIeoxxHn0KhaNbBimP8,26206 +torch/_awaits/__init__.py,sha256=T0RT6TwpQbHV9RX23zQYck-XpJwWC1bH3ObNLeqq6yA,1652 +torch/_classes.py,sha256=Kj3ZPXLp8ZRQZwuJ86V7W1rfJFcJ9qSaVRu_rTPENL8,1721 +torch/_compile.py,sha256=JKku2X4Yt5hMGxTeHjPu0ZeUmEY4pIjBr8CEa9KBMjE,2011 +torch/_custom_op/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/_custom_op/autograd.py,sha256=sVevcm4SG3r-N_53Db6I4UGOkcJxDx2EHNzrn-PhRsw,12083 +torch/_custom_op/impl.py,sha256=lek6Bi5N7WycFhYb93FQ7ucM_kCZK45KNu66Nf1v2Z0,27012 +torch/_custom_ops.py,sha256=DyTzqyo_fZ582qmMiyfoLdT9-cAnor776dgqb_GhPIo,12824 +torch/_decomp/__init__.py,sha256=_FTRJ4tAW60-5eFuFsI9E8rXahCy8GhLoBFTySn6jrU,19144 +torch/_decomp/decompositions.py,sha256=fDd2llcmZ9B9pJFH3_X7p3ue4Qean_PrQLPfofKagR0,176426 +torch/_decomp/decompositions_for_jvp.py,sha256=tJr2bRhlV98dhLMZEyouqiT90y1Z0FANu9T__Dzbq2M,11701 +torch/_decomp/decompositions_for_rng.py,sha256=TK-IuICKvw2mNRdUzBdimJgVYsrfxegrhIGD9irI77s,9178 +torch/_deploy.py,sha256=g1HU2xJxIgt17gVzWEOErF9o9xPWatuDxyTMs0vhSCM,3457 +torch/_dispatch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/_dispatch/python.py,sha256=Pf90bfAgqMsc-UQbQsBHBdgYsfb0XERdpVAc9-H52hA,6710 +torch/_dynamo/__init__.py,sha256=9tt2dQTphdKY94wjIR1GjZlnzvs5xeeNWq8eQAkWW3o,5232 +torch/_dynamo/_trace_wrapped_higher_order_op.py,sha256=aWbLHnzpA9iO2lXqKD1XPMBzfg1bnL1yl8mjOIES4k0,9231 +torch/_dynamo/backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/_dynamo/backends/common.py,sha256=LD-8mGMqGrsu8sJI0edLkhbmdhrS8YH27rHBhmrU1BE,5536 +torch/_dynamo/backends/cudagraphs.py,sha256=juLVAmyA6NLX2QBLVtk4boL2Wvwzw-EaGSjwf9-V31g,9262 +torch/_dynamo/backends/debugging.py,sha256=znv34DTCdzqmN97YWVEswy5SElysxywGGrLGj8uw09I,15580 +torch/_dynamo/backends/distributed.py,sha256=eJQKQ595WhmUl1Cz2Yhu0-9OhHp35ZITRZmxVlpSINA,26512 +torch/_dynamo/backends/inductor.py,sha256=eKIRUmaMlUaBMXU0teqhK1lG6IK_UbHVa5rClId0iRo,884 +torch/_dynamo/backends/onnxrt.py,sha256=p-xjUoLDCUsf3_ks4qGlrJipMyMkhO7MllYa985CpDQ,1541 +torch/_dynamo/backends/registry.py,sha256=q0OppMq0NxpCOOeL8-6D9ic-LWOnqUS72-_RaAHT7g0,5426 +torch/_dynamo/backends/tensorrt.py,sha256=oMtmZB2Op6yeZvFXskMDZUATwLmgRCg4xxh3OrcVqSY,406 +torch/_dynamo/backends/torchxla.py,sha256=mIMj0a3Zs2GjNfAWktDGzbJnB7oRkMvdgXK9qWFbYcM,1255 +torch/_dynamo/backends/tvm.py,sha256=wjHCm-Kyx2WcVO7dp51K8yq_LxGf8TL3vM2Y3ECf_3A,7634 +torch/_dynamo/bytecode_analysis.py,sha256=Yf1mxekC-IDj22x1c88eGneGFU18TgP5sMQeaxEl9XU,8525 +torch/_dynamo/bytecode_transformation.py,sha256=ZTGKXlrVtFgnSA4zBoFKYuYCuZqn_2UdlRo4-wWC5-Y,59711 +torch/_dynamo/cache_size.py,sha256=EhDDOGJdRuDLQ_GaiEkDV4c4rDPKPMSlSC95PriYmQc,7908 +torch/_dynamo/callback.py,sha256=I8Lts6fhJHdLUx7p7d99CLl332ZqqALACO7V3bpib5Y,5586 +torch/_dynamo/code_context.py,sha256=b7bQSNWb8DBuwDT9Q8qAHh_jSVIM33M5Hf6MIWYbO1Q,1818 +torch/_dynamo/codegen.py,sha256=h97zwIbId33UQHpVeYQajqQRzREPTUf0YA5FFPrBlZk,27833 +torch/_dynamo/compiled_autograd.py,sha256=MGNSS-ph0zm83hyBShF0EryS9QOrQWXBNONJMMvovi0,60443 +torch/_dynamo/comptime.py,sha256=lCluXB6ULcHjoND4zteZ-WIKyTrhfqQVZlnxOKvdmXE,14212 +torch/_dynamo/config.py,sha256=SWCL_rVhlIvn-RYhhZ8RESSHyDJjM_4ORtjC8-_IOE8,27352 +torch/_dynamo/convert_frame.py,sha256=pROl9WGNKxj0g_2nUBgLQdxdCaBS0BVcohuB8pQGn2g,56860 +torch/_dynamo/create_parameter_op.py,sha256=DDlPmpLSbg4fxLXqYIsMTpat-_U4y5n_k2QA2-EFoxE,2524 +torch/_dynamo/current_scope_id.py,sha256=CD2PnuBjVr2ns7_oNaPg6djM5wa-qNIaJYLFA4uThUo,1433 +torch/_dynamo/debug_utils.py,sha256=rFZCZLoAY-udYXg7AbQda4IMqlD_S5Nlyr43jN4QXBI,30356 +torch/_dynamo/decorators.py,sha256=GBjYzsxf2lcHh3QUWSgdLm-ceAD5b14c35cHWQpsky8,32805 +torch/_dynamo/device_interface.py,sha256=mGcq46rC28JC4LG38CTMDieakdOBdsH1-paNUuMYEbg,17744 +torch/_dynamo/distributed.py,sha256=n30oHn27d41P5Ak_qcaG_nYPMuZdmxVr8l37IVojCY0,1670 +torch/_dynamo/eval_frame.py,sha256=sNNW6PlyRQcHt5q2gL07KJ0_XVLczDX1fSGAPO37tGs,81689 +torch/_dynamo/exc.py,sha256=-81urUWyijav3n76Joq0rDIM474wH-nTGt0lEfeZ-BU,23635 +torch/_dynamo/external_utils.py,sha256=3QCWdACIzgvf_iQneiovzYy-mKRGKCU9TCz_aPg_v2k,6851 +torch/_dynamo/funcname_cache.py,sha256=62RNKTJ6LMz3E8oYi5qLXNKZBDkkB0Md-cANs-G_4D8,2548 +torch/_dynamo/graph_break_hints.py,sha256=RFPZornAqL4toJtG_uBO55RBpBRG_0cjPyIkqdIRrfM,1327 +torch/_dynamo/graph_break_registry.json,sha256=9maaL0rgRL6HUeOLn78pEaNtEx-uCqiOpjUOphLfvTA,84700 +torch/_dynamo/graph_deduplication.py,sha256=gfEvLjD7gc7gxsUeZnVpd4FAjopJwNnh9lbKvUDHffI,17018 +torch/_dynamo/graph_region_tracker.py,sha256=nlrpRs2_3_xSPRt2CcI97bJf4Kgb1Ziwer346BgCjWA,17500 +torch/_dynamo/graph_utils.py,sha256=QSElzynl4OFaUIX4y3leu8vQZkk1wRLGV_SicFeqWkg,2367 +torch/_dynamo/guards.py,sha256=5wlEhTTRGBKTLIgI91hVdQYWNR2OF8U2XYDdYf17R5s,145866 +torch/_dynamo/hooks.py,sha256=4ndb5sTX3itNFZArGzbT--N9W7-_S2KnVVEO3AKPKuY,867 +torch/_dynamo/logging.py,sha256=jlFTuSBNm24KD1n-xi8Zt7qR60YMtD0IsAFKotj9g3w,2188 +torch/_dynamo/metrics_context.py,sha256=tBeRxz8pt7Hjb3LZIgbASY_xeQ7Sz4kU8D17jZdbv6w,8020 +torch/_dynamo/mutation_guard.py,sha256=RaSc3gcsnOoMMzIqFemCyt6r4WilHGk3Ox_0pPF2xKw,5166 +torch/_dynamo/output_graph.py,sha256=3tgz-w8WJEB8MzX26c90xsfPAskVCQWL53kA8XYE2ow,133557 +torch/_dynamo/package.py,sha256=ASD5VYOgds8h1qn2gIwXEflifnBKY0zl-DPyTWbW1Ko,15955 +torch/_dynamo/pgo.py,sha256=_xJMifO3p1o80sESuxxhE8IQPvbbD0h2lL-miUbLe-s,32144 +torch/_dynamo/polyfills/__init__.py,sha256=iXPPhF6B098OU9TAmlXirO19sZBVLyhFZhhvIHhbgVE,9228 +torch/_dynamo/polyfills/builtins.py,sha256=kM4ClS_jRNJwCHw_9wWMo-O6VKJp8FTT_6hp_e0tlFQ,1399 +torch/_dynamo/polyfills/functools.py,sha256=KH9YAy5XUVFKyZ3FioIqBA06_MnteyFAfX7sSKHWPBY,966 +torch/_dynamo/polyfills/fx.py,sha256=MVlHMapPJOhDS6tl0pcloZydJP1zlRH49bjQ3-qtfUU,1323 +torch/_dynamo/polyfills/itertools.py,sha256=UiJyYWxj9gIVAyvVfbdDWMuuoMgIESzmLjS8RHoSubo,5979 +torch/_dynamo/polyfills/loader.py,sha256=xB6hHcBUxXpw10D4_uUssz5qLcTRLD-1u9buyu_mTWU,1227 +torch/_dynamo/polyfills/operator.py,sha256=GhIe_WfVeoh9Fc5_Ko4LSbndc8X6dE-sNIbnRpWY8QU,2924 +torch/_dynamo/polyfills/os.py,sha256=GiDwxYjoV6wajRo3SZxPpmCFDfT8rlrL8bnOO_MbXsM,978 +torch/_dynamo/polyfills/pytree.py,sha256=trSSHM5FwdWZ2DFlxkauGm81aXLXBU9gRwk7GquIOjg,15745 +torch/_dynamo/polyfills/sys.py,sha256=aWvisVWLgMt8mmYF3b0tmn0cutv9GhPPruRs0GIjdxE,447 +torch/_dynamo/polyfills/tensor.py,sha256=u5La8g1ucxhI_dzLNZsLCjGeyfesGr-117D-dNzfrME,1404 +torch/_dynamo/precompile_context.py,sha256=uVN3JKIAbAG7m0saT4gayLcU4pkOmFW1sxMa-Rxsv_0,5774 +torch/_dynamo/profiler.py,sha256=ExkFBm-Qo8cVCnXzEBNRA6e3kyuTPngIiK4AG7tQeNY,5711 +torch/_dynamo/replay_record.py,sha256=2uNvcay3GEsM5_zwH2kN0BNL-NP3fOPu5NSu6yV5CaA,4290 +torch/_dynamo/repro/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/_dynamo/repro/after_aot.py,sha256=7hFI2To42dergyl9e0lVLsM4GCXlvELv-VfIoqXVQuE,36314 +torch/_dynamo/repro/after_dynamo.py,sha256=T71_vsT2HSq83lOaakYsWHg1ButbKmHU73-R7oslqJY,20572 +torch/_dynamo/repro/aoti.py,sha256=HC11a9OPHgJhAC4FOWqBmhbFOxUd6FdxJKAy9qbNvls,21190 +torch/_dynamo/resume_execution.py,sha256=l6quW62fhlgwMNitgSxg7-Slyh9o_hA7pwa_1y3vLN0,21860 +torch/_dynamo/side_effects.py,sha256=0x2lnj40ILFit4OuF1zN8GCcF2JM5VjzWPl4F-KKcbU,48180 +torch/_dynamo/source.py,sha256=dDM8e5oTdPqLAmKzd5EGQ6DPHEVf4umbyyJsfrLKofA,33566 +torch/_dynamo/symbolic_convert.py,sha256=kF2KiKY0XINl6QxgJVOUqZuTlp8YLFTkDbtNmggjo7c,166965 +torch/_dynamo/tensor_version_op.py,sha256=-4QsYNWMAHSHjbfqIh2Pt4o1YVgIRyWpTJ0tCpzKkF8,2399 +torch/_dynamo/test_case.py,sha256=jD3uSlO3aXunb8gSESXUmfDQPqBjiLzKHWc4lYAyMx0,7258 +torch/_dynamo/test_dont_skip_tracing_functions.py,sha256=v8sCtPbLM_cEgEXvKUvtwoVnclOjJkaDcVDYgBWL5Ww,817 +torch/_dynamo/test_minifier_common.py,sha256=V-8g8CwUItHHke-Lb3YvI0uOktDHg8qFtpVLXHR21Mg,11910 +torch/_dynamo/testing.py,sha256=oYR3TB8QBlaFpzSMgVLBYaVUQaxOV3TKJLy9MD6UZVA,17071 +torch/_dynamo/trace_rules.py,sha256=ffUsKzMZPnVvlympVopechqS0MAVQc4fRm9GvEzQSQQ,154004 +torch/_dynamo/types.py,sha256=LCJmSliIkJO6K4o8uO16l4_aW_sLfPv9nBjNb2cGDsc,4155 +torch/_dynamo/utils.py,sha256=_IJtzXhS7lCvKI1Bx5-uAPBEey5Av4_CcH6ytt89IzY,161146 +torch/_dynamo/variables/__init__.py,sha256=7-BG9NajEB1MZBm0fAc-dbw39d1K8A7gT2pSBFZEwCE,6640 +torch/_dynamo/variables/base.py,sha256=0Xi2QEdnoPczLjBRl3x8QPBkh-6Ztbe2y8yuzbpZStA,23782 +torch/_dynamo/variables/builder.py,sha256=B-Gp6JI66FNTO50WWcgk7E2_ZzvWfbrC-PvNxss_SdY,154898 +torch/_dynamo/variables/builtin.py,sha256=-K_ZTT-oDluf6PXb0akcME0EkJxnFkE3UdWf4ymwnlA,102371 +torch/_dynamo/variables/constant.py,sha256=PbVCOytJvkTiT26jKmeUrLfbtREWwX7qm_7NZFVRwlY,10430 +torch/_dynamo/variables/ctx_manager.py,sha256=eK-1Czyw_hIuuH2zPDHC4N2K0eV5bphDKj9aKe73qdA,50852 +torch/_dynamo/variables/dicts.py,sha256=31YzCaeWDLZnihg9mwSK-vZ-rdB0U4gO9kmqMinIjfo,41553 +torch/_dynamo/variables/distributed.py,sha256=qISZZn0paDaMD-tpt5fvGoznMlDzzyKdBJiGMXm6Q9Q,16027 +torch/_dynamo/variables/functions.py,sha256=ZQ788y1LB_Z_pdJZHWWG3KbfcDIUn54sX6j9bLnydNo,88871 +torch/_dynamo/variables/higher_order_ops.py,sha256=qNsG9nc4gIwtlNwVyPtmvayMDYuxZJwNq1Pr0v6z_-E,131703 +torch/_dynamo/variables/iter.py,sha256=onF6pUskp1zxTQ402DV2pm4bnfIEAXCC02y9lvqb3qg,23241 +torch/_dynamo/variables/lazy.py,sha256=qxuo7pmFO3iv3Y6MeRSSh-dI_1APkgBlkNP1shQNNPA,7065 +torch/_dynamo/variables/lists.py,sha256=JCxEX1Se3I1twZaivONbyqAir__RXi-XxQaBZQDEKQM,40116 +torch/_dynamo/variables/misc.py,sha256=jMrQ5FNqyVfDtUmNAe8EhTEcw4gISSkKMwYyLJgTCSw,74588 +torch/_dynamo/variables/nn_module.py,sha256=kFga6KcfLYuMKJEpMZ4HuXdbx_CGfsI-dZc4JNpD1nc,51881 +torch/_dynamo/variables/optimizer.py,sha256=Bf7RWvXw3E2fXseaZ_lKk9x7nzA7G3b3rpCyhfn1Iac,16713 +torch/_dynamo/variables/script_object.py,sha256=MG-lKDiFxJlCW1OW_EWZUyRz1-QmhmSPMgT5Xl2NU2U,3762 +torch/_dynamo/variables/sdpa.py,sha256=InV5l9oUuBOSslZ9xP0m2kpw3NFhp7ZvRNzxMSSF0-s,2522 +torch/_dynamo/variables/tensor.py,sha256=dfGvc0B14EXqmMbKEoZgRgb2x0t1nnJDUQQtMaCX2hY,66690 +torch/_dynamo/variables/torch.py,sha256=JA7I8YzvQpAtmwJ5C0CD0Y8aHVnGxes-I6iI4P_4abY,71317 +torch/_dynamo/variables/torch_function.py,sha256=obCU1lT8yIYxygdKM1GKrTlt_9i83UTUnVIbP5gU60Q,27929 +torch/_dynamo/variables/user_defined.py,sha256=CSWTenqFM7qylxgmT7GR3M4ieWoxkijTNQhI-AOPT_g,72197 +torch/_environment.py,sha256=Oca0KVQVfJEZVAIXCNlBx_yCKtJyle46YLl_mL9w0dE,42 +torch/_export/__init__.py,sha256=rUhLDxQ2TjrExI2tVY4yJBCfDJmWAVv4rTCf9JSkd5I,6487 +torch/_export/converter.py,sha256=gOnxqswAkdcK7o4RJipI1ZytaJc7HoEPZHtLtl52-RQ,64540 +torch/_export/db/__init__.py,sha256=a3XxW1RcNAPwEVaI2g11hpnJvSHxGUFsGmDAWfDnLP8,206 +torch/_export/db/case.py,sha256=i_gKk2N0WRFW3cCiDxe0fxJriFXXB65WVPbOQLrN1fs,4998 +torch/_export/db/examples/__init__.py,sha256=PlziokXeStI1gWELwC7zX-T7ZViONPRitQ1uFWNAxL4,1648 +torch/_export/db/examples/assume_constant_result.py,sha256=MPGQemV_ymD07kXuOVQkizSxTaTlu4vr2bAP3xxE3M8,510 +torch/_export/db/examples/autograd_function.py,sha256=i7X61hQS_JQrjSLnLoqge7BhlupzW3v_ddBEH1Rc6Cs,578 +torch/_export/db/examples/class_method.py,sha256=KM6pkWdEc1SMybUR-O2hCN1tD9Vl0B22gPI2LgDiZZ4,499 +torch/_export/db/examples/cond_branch_class_method.py,sha256=PpspYrzN3LBl0CIuGjjZon6rGQeQ32YtaIqbpRDbFyc,1327 +torch/_export/db/examples/cond_branch_nested_function.py,sha256=HgN_wnbfzoATNkD7ksQSwdo6PVhiIP5RqneZvGSTxeQ,1302 +torch/_export/db/examples/cond_branch_nonlocal_variables.py,sha256=B_ROqnPdNhhy7zbP9cyqLUl1LgDgAYYGSr8EyNRiy-M,1841 +torch/_export/db/examples/cond_closed_over_variable.py,sha256=iSWSxhJhumU6CsRSzXUeW7YVxbgjf-C05EDc0Foi-a4,547 +torch/_export/db/examples/cond_operands.py,sha256=mBIzaZy_VYUzNWS8aqrTCPwUM1qbUPlJWXX4oWYgJGE,799 +torch/_export/db/examples/cond_predicate.py,sha256=PKpI-GKAdVMDh11EDIpQ3JXvRMXNGuJTKXbGb_uyQjY,663 +torch/_export/db/examples/constrain_as_size_example.py,sha256=1wYblW56aQgBRiP1u2WXkfAYXhLYbKJ6kBNWX1sdIK8,637 +torch/_export/db/examples/constrain_as_value_example.py,sha256=LYSBa_XokesbD6K7KydnVq6r9veGr2nSdm74xlk7G3o,689 +torch/_export/db/examples/decorator.py,sha256=U5YTo_25VfE_Hy9uoD4eYN8wKSJGpOG0dAJ1TZjL1i4,480 +torch/_export/db/examples/dictionary.py,sha256=gSkKkS68Vw_cropGQ57i61jqTleFsLECIXP6pZMqLBE,404 +torch/_export/db/examples/dynamic_shape_assert.py,sha256=Ff1KN5JG62k6wYqR7GvQ8i8lf1jyvc-GNVFASVfOx5w,450 +torch/_export/db/examples/dynamic_shape_constructor.py,sha256=68CRA-j59c0dPBc5hOBsEBmWpWz0opzovyqGIaTfKuw,396 +torch/_export/db/examples/dynamic_shape_if_guard.py,sha256=VG6YmeK31p2jwLPG_Ci3VWmWUMQnJIp_IuijY_UY-dU,560 +torch/_export/db/examples/dynamic_shape_map.py,sha256=XN8_Et1EVYaxmdnACPkiDAVJZ3jkYRaj9U6mU44ZntM,454 +torch/_export/db/examples/dynamic_shape_round.py,sha256=sMyW43DVXFtZa6jFO08XxVjyw06LcJ9oyuJH5jAsG4Q,525 +torch/_export/db/examples/dynamic_shape_slicing.py,sha256=Rowy_RfPf1kW1l_oCp71YVflt2ivhpP1u_HzTEupxQA,388 +torch/_export/db/examples/dynamic_shape_view.py,sha256=fEuSTQRghtJY8VcBi7qwiZTQXwd9ux8d1PRBYw6Qd6E,444 +torch/_export/db/examples/fn_with_kwargs.py,sha256=fEndpGN__D456vh3NAamoK1KdmtEyvmAsW0gZB0TExo,731 +torch/_export/db/examples/list_contains.py,sha256=aQsdZDUN4Rn94Waft7X30_Lpn4RDbEnmsY3hpvrfV2w,477 +torch/_export/db/examples/list_unpack.py,sha256=u7tczh6wbR1PoYQxuRPpsSfxgMiGhu4VocA50ZrwKFw,568 +torch/_export/db/examples/model_attr_mutation.py,sha256=dRMK4qEV23My5bisv9EGuDnBhs9JwYqVjekAuYbGrrQ,662 +torch/_export/db/examples/nested_function.py,sha256=S-MXIlaFn4RGeA4GEFIEHG-pcM9WBznXN3E2k-sDlZo,491 +torch/_export/db/examples/null_context_manager.py,sha256=LM5a8DO8x0A0yaA0ZAhOVybcRIcpHy2fxCH0YoqH_2M,478 +torch/_export/db/examples/optional_input.py,sha256=Iol5gTORtXrleJWRCQaLlp11GPzTj5m3dYv2sV6uxZs,455 +torch/_export/db/examples/pytree_flatten.py,sha256=MFIw36BZAXANZ-iajZi--lbPzf1EyDrcmWxPeQ_VCyA,376 +torch/_export/db/examples/scalar_output.py,sha256=_CMPbP3nbTMGzfiUy3ACOdxxoL8_F1lIEya_JH4HTZ4,543 +torch/_export/db/examples/specialized_attribute.py,sha256=GiKqGiSSPwPIL-QPinKinHNEiQvRsptBx319j7FpRfA,520 +torch/_export/db/examples/static_for_loop.py,sha256=wYtwzouoqqVC-Zsw_kHYe_Fzz-0mPxZ9zGwWrjdv8OQ,385 +torch/_export/db/examples/static_if.py,sha256=uzqQHEytorjGRbxJQQxam6nJZLHcPIPw1uzMfj7KU8w,397 +torch/_export/db/examples/tensor_setattr.py,sha256=LXED12gZiYOvhZXBzThejfW2meNYI8U-8_aIVwfdaqI,337 +torch/_export/db/examples/type_reflection_method.py,sha256=CoXAARKtqSNciuA5V7Tkx0MVhq-h6c4khPRSnxFDjcM,461 +torch/_export/db/examples/unsupported_operator.py,sha256=v-niL6z0og2TuXmvgFWbHFxMP_m0ldu6NefmmW3ikl8,411 +torch/_export/db/examples/user_input_mutation.py,sha256=KJY_YR4w_nEZ66YVMmPsBKv6PfQ7GnQwkV12NJcUCxs,302 +torch/_export/db/gen_example.py,sha256=YoR-ZOXBjcgEN2ypFHVINmHBETs9iYEWNytTmDhNUiA,462 +torch/_export/db/logging.py,sha256=R4Sjd7pyjHJ4_xKLdKLkqsKFuJkBjNpXPXsJG1G2Jmg,1651 +torch/_export/error.py,sha256=OjvFCTGZVLlBtbTvaL4xDbBAizynfhwO_2fONObaISk,1770 +torch/_export/non_strict_utils.py,sha256=Ykk7jk3ZybA7nwtn-1S4KeyTC9jsatOlkof7DWizyUs,39643 +torch/_export/pass_base.py,sha256=j5dFRvKiOh2hgJRGTfrV_Oh_eRflr0HnznMze196ZjY,18268 +torch/_export/pass_infra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/_export/pass_infra/node_metadata.py,sha256=fF9lD71OeQ5HlSfR--IzjNMe7s67_N9T5Z1QvhGsa5s,771 +torch/_export/pass_infra/proxy_value.py,sha256=Jh3ZGEhnKhQM0qBRYjKdSB5dvEIktxM4wtMIoXqz9ts,1269 +torch/_export/passes/__init__.py,sha256=78MzFjtaVabk1z2X12WMIlmtkjYaFmgNJlDCTCepNt8,88 +torch/_export/passes/_node_metadata_hook.py,sha256=qtwEr88tWv7q4vDWMMnKrsMEarcnkWFvXZcTI8Z7yZ0,2478 +torch/_export/passes/add_runtime_assertions_for_constraints_pass.py,sha256=4dcGN5uaZ2y29CswzfxQ0LKPQx6CrxmUC8qPdz7jSK8,10160 +torch/_export/passes/collect_tracepoints_pass.py,sha256=BV8aTA9NtOOFLGaySOc53Sj6Y-f-Y2Vv6pNo4WZ7Wl0,6548 +torch/_export/passes/constant_folding.py,sha256=hCZquWooDO7UdAEm6rY_WAjtee7MXvr5PsYEZGRY-Po,11276 +torch/_export/passes/functionalize_side_effectful_ops_pass.py,sha256=Omb1sfKVWkJ-TItG0B9e1i_p1Wgg2zd3maptwSdTk8Q,3279 +torch/_export/passes/insert_custom_op_guards.py,sha256=z9WY6D7lN7mZQ-fVCguO__1noBACu0YikRoHvwJiIsk,2855 +torch/_export/passes/lift_constants_pass.py,sha256=b1BXHFQWGTWNTeT0ewYqDUTTUtQIsbPaYGwIrDG_0C0,17458 +torch/_export/passes/remove_runtime_assertions.py,sha256=uyvuiO5Qy9AYVfbaOvyOYeBaZ_yUzgoPSsfJ1lDb0-g,1588 +torch/_export/passes/replace_autocast_with_hop_pass.py,sha256=d4vNSm2Zh1ucFeoQFbEAjo859BXACjEuWTZk5-1hFUI,7216 +torch/_export/passes/replace_quantized_ops_with_standard_ops_pass.py,sha256=d-MDtsBxPs5KY04XWUvz35_fHWntzH3p2cWxg5btaek,25899 +torch/_export/passes/replace_set_grad_with_hop_pass.py,sha256=lK2CuPm6LH7GDf0u7mJLN359IrTwcrMXu1NygAy7y-Y,4325 +torch/_export/passes/replace_view_ops_with_view_copy_ops_pass.py,sha256=H_HVEZsZNJYAE3sIAEvHLfvks4TSncljhSeHjJE9oL8,2411 +torch/_export/passes/replace_with_hop_pass_util.py,sha256=rXWVRFqnVTsnW0InGsl02qjID8ZxGK9GZERysSuKLMo,7552 +torch/_export/serde/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/_export/serde/dynamic_shapes.py,sha256=fB1cO4OrSSNLEICQUIWcEQR603AR6GWSX-bnP9aTC80,11551 +torch/_export/serde/export_schema.thrift,sha256=rgDMgI1U54Px3yZBWyxENHRe6jORNyD0zl4a3nkg5j4,7013 +torch/_export/serde/schema.py,sha256=-lnYQiYz9yXZDoNqBbwd2MiOV8boV9yumaZYbCZB3XQ,14643 +torch/_export/serde/schema.yaml,sha256=VeF-YACX_4ZJ5QkMpQZbNtXDT2q9DUlvwZ3qHlmPdQM,9497 +torch/_export/serde/schema_check.py,sha256=FUdQlBK_-nei6cz8SlxwI5wXqklXbU3L92qmDroyYDY,24442 +torch/_export/serde/serialize.py,sha256=Dtc-zWrlWG4KXrP-uZ4_hLZn3w5xvc-5kVgQXfiIHPQ,144049 +torch/_export/serde/union.py,sha256=wB_0As4TKSm4OKbW9nLcaJDGl4eVc2HZ5PdD040RSHs,2026 +torch/_export/tools.py,sha256=ZOtfgtBha3UCeHvYws996seTAuVUZMu8IDOgQ1xEh2w,4572 +torch/_export/utils.py,sha256=l7tTuhOQy4u2KGi8OyMWUII29icCvsi1uu44pBz91ts,55734 +torch/_export/verifier.py,sha256=nJDijsdZCUVF99fslxrFlxJH6KG9GNzqLZ6FCvr9c-s,19693 +torch/_export/wrappers.py,sha256=jN30r_-CphdW69zCFdq99k4fqOc-kw3MmgQUF4_JGtU,9372 +torch/_functorch/__init__.py,sha256=a3XxW1RcNAPwEVaI2g11hpnJvSHxGUFsGmDAWfDnLP8,206 +torch/_functorch/_activation_checkpointing/__init__.py,sha256=a3XxW1RcNAPwEVaI2g11hpnJvSHxGUFsGmDAWfDnLP8,206 +torch/_functorch/_activation_checkpointing/ac_logging_utils.py,sha256=AiHM_lfBPv_jjZ8H1DR7wMdbGZUHN2GMOJ3lGSxmjC4,5159 +torch/_functorch/_activation_checkpointing/graph_info_provider.py,sha256=ivSWzVfCUvtEraeeYwFnK3LZXlE9a6f2zBm2a2NI0mc,12766 +torch/_functorch/_activation_checkpointing/knapsack.py,sha256=Rmvq5_KUIi21lsX59ZR9-JYU6AWnw1vnDFpbQ77olds,3955 +torch/_functorch/_activation_checkpointing/knapsack_evaluator.py,sha256=kJuZKdhxQ9jqvZznFt0QLMXWo4BTbTm_xVjHoPb_7bw,11823 +torch/_functorch/_aot_autograd/__init__.py,sha256=a3XxW1RcNAPwEVaI2g11hpnJvSHxGUFsGmDAWfDnLP8,206 +torch/_functorch/_aot_autograd/autograd_cache.py,sha256=LlkWPmumcK3sg-ZZR5HvsqXhw7Xx-IfVJ-Avp9U7rMk,58030 +torch/_functorch/_aot_autograd/collect_metadata_analysis.py,sha256=4EkT3OGKmzXBDhk1iISDCEN8UcLMNiOC3b15ORbLePE,42658 +torch/_functorch/_aot_autograd/dispatch_and_compile_graph.py,sha256=YSMMaizvZqSqoW0Cat3olHaVEMIBaiDa-AKDMSpqDPs,12643 +torch/_functorch/_aot_autograd/functional_utils.py,sha256=Ou94xixH2NQ6QzJg2eM4NN39p8em2lRnxPB3LZ4HgZk,22690 +torch/_functorch/_aot_autograd/input_output_analysis.py,sha256=CYrWIQAqY3TpF32WJ-Sf8Gfgm6dBKobJoT2-BPra5hA,17612 +torch/_functorch/_aot_autograd/jit_compile_runtime_wrappers.py,sha256=R3K08NFTYjrKVqqpbyDAwD3qHyMueiWW-rZ_IkIAJl8,76516 +torch/_functorch/_aot_autograd/logging_utils.py,sha256=Q9iOYX8ZhnGlNkkSsnTlJz5vYu_Suinn4e6nkgMBy3o,4623 +torch/_functorch/_aot_autograd/runtime_wrappers.py,sha256=b0v3UthjAHVWLBQq50tfyeQRAwCTWv6jRaVvlCxpG7Q,106137 +torch/_functorch/_aot_autograd/schemas.py,sha256=mL5T4bLVxMb4hmHNXd2RXRKG0JjmvUIjCowxY2cREFs,41588 +torch/_functorch/_aot_autograd/subclass_parametrization.py,sha256=RMeykkG5WpKjYkOFIcbbrDB0SR0IYYih_pxnpUIh0hk,4087 +torch/_functorch/_aot_autograd/subclass_utils.py,sha256=rSLxnG9Mxb-wHhC8ybo9rjpKaiU6uAgO8zKoyKkoStE,18413 +torch/_functorch/_aot_autograd/traced_function_transforms.py,sha256=4fhX4vjlyjbzUQK8Bk0xuqid3oPfJ2OObx8n5-71bY4,45234 +torch/_functorch/_aot_autograd/utils.py,sha256=-nrxcQcgqVgU7FZZXPN1T8HJ6U1LuFtvcTkosE6vi70,19218 +torch/_functorch/aot_autograd.py,sha256=WjmX6gRYSMaM9nzo__zzbhZu13gjdyvS6ms4yp5A0M8,72659 +torch/_functorch/apis.py,sha256=MWWCuQ24hKWhUDktK4yMGuWgwKg7IbROuQ1DwZqCuSw,19089 +torch/_functorch/autograd_function.py,sha256=-4SBKk0jWIdSKVgKpRDa3BnHzMO-zC2FcN9Z8hDQrGw,28742 +torch/_functorch/batch_norm_replacement.py,sha256=liTJLEOFiap3ed2fBoxdRUM-9iuw-6985kJSXZnUlNs,857 +torch/_functorch/benchmark_utils.py,sha256=djB8aP7V3xO6OsYwQRKS4AcORAXrVHDfiUavDJ6nv1w,6280 +torch/_functorch/compile_utils.py,sha256=kRtrWa3QU2z4xNFCigfORaSdEOtjn3ekq363RQimWPY,7682 +torch/_functorch/compilers.py,sha256=-su1L4NCAoIH55AwIwFQsP0Fe0AQA-x5BLCv2N-4oi8,14049 +torch/_functorch/config.py,sha256=ibJScHKovBTNWBFJfmqiqc4JS4DRgnziQNkBjzKXZ8A,13673 +torch/_functorch/deprecated.py,sha256=IVXQKWJLCE2AP7VXsOeKvT5E_JZhLKGJQ6j-OI5swTk,5203 +torch/_functorch/eager_transforms.py,sha256=EUu0qQyDOf7mct0y5ilIKuoniBwM6LR0vJ24q9Tmkxg,71057 +torch/_functorch/functional_call.py,sha256=uT5kJa9DVkhwFVMfMMnilWQnie8kNyVZD0o07-HvS24,10578 +torch/_functorch/fx_minifier.py,sha256=cVb0jhQq73WYpFEjXf6a8G34eMHBmyyGJD7N4mMqDn0,17363 +torch/_functorch/make_functional.py,sha256=4MViMdytvAtyEEObs_q5XDQBFKkMoGLx_x2UFXfwfzI,22744 +torch/_functorch/partitioners.py,sha256=CFtAIiRitTdxDp_BgmuDQk_fsYbGf-IJud1CT-vqSFc,105221 +torch/_functorch/pyfunctorch.py,sha256=OUNmnww35aii66LFXlsBEUtDYYZs7y36jO2TFpe7-Uo,10676 +torch/_functorch/python_key.py,sha256=ZeINkQZ4B4PAdoz-zLMNU72w6cVHzE1nObMikLI0oL4,442 +torch/_functorch/pytree_hacks.py,sha256=dWzTSKcqGhK4zHgmgJbyis2kzB69OH71GP_YCDBDbp0,698 +torch/_functorch/top_operators_github_usage.py,sha256=D6DKMuEPb-yljYLabyQ_bZsRhigsbYHDPOq7rWZSIRM,21392 +torch/_functorch/utils.py,sha256=k_pKD9lkqHapxTE-UVa-68-NgRiZ2R8myImOhs1abaQ,1052 +torch/_functorch/vmap.py,sha256=BfCnmkSo3hArytwEm4I5s4OoGoP8yqgp6EJjb-FyGrY,18983 +torch/_guards.py,sha256=N8d6uqqXcEieJXEX12cefOzXF1DoyDYyw1bsMpollF4,39005 +torch/_higher_order_ops/__init__.py,sha256=dqprZeoXt3t6FIOLSvvmfM6oQP4x1x7EnQuNexI5eQw,2278 +torch/_higher_order_ops/_invoke_quant.py,sha256=TMqes175e4vsbs4agRJFoCLrganPUjssPUdMLuhCnrc,1897 +torch/_higher_order_ops/aoti_call_delegate.py,sha256=Sudu0ShjJqRz3t3EVrp7MgXp4Er0cdWC-owN4DTdj90,6063 +torch/_higher_order_ops/associative_scan.py,sha256=HXKMcLjtqNAwPKkAGF5nSHpzSa4_RTcJGaQiUcArYA0,18333 +torch/_higher_order_ops/auto_functionalize.py,sha256=3tcAyA4x1Vw2zgL1gwKHdDFBl2WrF7Zp-fy12B5p9xQ,36201 +torch/_higher_order_ops/base_hop.py,sha256=sTBdzKADadj0VXaeHsi3dzx1889IbjC-i8rl04zA4go,10537 +torch/_higher_order_ops/cond.py,sha256=70AbryE97yjfz45hRKYTbkEloHooiIwOleXOtw1Glf8,28173 +torch/_higher_order_ops/effects.py,sha256=2xUri7TMUG4ATmOQa74Da9fVXZVTbSe9NtRMTu8oZ44,10116 +torch/_higher_order_ops/executorch_call_delegate.py,sha256=-i8C-8G-Vo1JMmcLlMEUvjm6NBycDkJBdkw9881QbPw,5959 +torch/_higher_order_ops/flat_apply.py,sha256=L4_20mersdVVc1ARGDGAtoJdofbLiOZg1FHHPVvRQFU,4369 +torch/_higher_order_ops/flex_attention.py,sha256=NBgAijm0SaYgK_YWGrtqhI92yHhKBHSjrgK9XqBx_co,43038 +torch/_higher_order_ops/foreach_map.py,sha256=_zvE4x7ueODvyxKowbZ3s_Kn0Ir8QJi8_IwC1nm6fNk,663 +torch/_higher_order_ops/hints_wrap.py,sha256=2epLR5QZxq-G6D-O2vFnwdJh5qOLaWSZheI4RsEqglM,4837 +torch/_higher_order_ops/invoke_subgraph.py,sha256=TfMfkYzu9BG9Of3NERViO_v6yG3y5BR5icmL8k88RjQ,26299 +torch/_higher_order_ops/map.py,sha256=bYfIvrrsIeKgTdkPtAO4cU9dWOPVRylpj_jN4KiHUnU,10053 +torch/_higher_order_ops/out_dtype.py,sha256=41wNmjJBD26kqEi88tBZDg6x_93ZC4w-3P8YrocbrKI,5526 +torch/_higher_order_ops/run_const_graph.py,sha256=fRaR_InyyoumJ1MqbgQROQrORln2q9-YnG_3VHSsIqM,1890 +torch/_higher_order_ops/scan.py,sha256=4ARE5kJ8s2xr_iVgtZniy_b1iRTSsn7f-ayO9G9QdnE,38151 +torch/_higher_order_ops/schema.py,sha256=aUw_5WbHWRdZJokGCgPJb-E5-j4G3AzfcZO7_24cfsk,11128 +torch/_higher_order_ops/strict_mode.py,sha256=sfXZiOgPSnBgJ1Mrfj1ePHtCmrI4axbW7QxjRxtg1nU,3644 +torch/_higher_order_ops/torchbind.py,sha256=Su_tO68yKuJNz0WB_Xz1WLB71g1ke86O8oOTEK14o1M,6252 +torch/_higher_order_ops/triton_kernel_wrap.py,sha256=H9SJ_zWAbnDPjlhsLEA3QLbDOlVvYt7u3mwyivGnsc0,82438 +torch/_higher_order_ops/utils.py,sha256=5gkCaXbeIRvpSCBlCqxNSn_xZUttuJELrKMEkBrrN6g,43459 +torch/_higher_order_ops/while_loop.py,sha256=zRl5r127ZnRJe-mn0FnNvZvWj-x7LD8PFHRWZwdmRKM,17566 +torch/_higher_order_ops/wrap.py,sha256=TyCUiH84xtunT8JGgP0_CzHxKvPTMAwq3GylSYkKJB8,11222 +torch/_inductor/__autotune_main__.py,sha256=teBVDb5194YrCJgy05KRE1yAkfDrAfFIA2cIwYFnyRw,913 +torch/_inductor/__init__.py,sha256=6jqgN24EXlJt5bjzjpsl5tATPEuEasFdWhRSN7nXucU,13516 +torch/_inductor/analyze_preserves_zero_mask.py,sha256=lcNA4fWnojRY3UfNIBFQgEJOQWRLfEIefFmLoKNjW9g,5453 +torch/_inductor/aoti_eager.py,sha256=8eyKJkRmizvHMUA4ufNwhQO3vlboJC7sI31Z9O8qWAI,11138 +torch/_inductor/async_compile.py,sha256=_mZNIUrL1bg0nTeMJf-o2S_mQF3zMezKZFj_QyHGLpE,20040 +torch/_inductor/autoheuristic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/_inductor/autoheuristic/artifacts/_MMRankingA100.py,sha256=vtCNz-_a6qtWQTY8inPLPD7ptbyVPz8TeKbktN1Z3WA,28044 +torch/_inductor/autoheuristic/artifacts/_MMRankingH100.py,sha256=9wUukZe2Ko_VWV_MuI-67xORw6KfB3aser21vXcW4Wg,30668 +torch/_inductor/autoheuristic/artifacts/_MixedMMA100.py,sha256=qK855JzhAYjHICBM0Fh0MQ3nufurC4Ik4N-PgjoG7lg,7920 +torch/_inductor/autoheuristic/artifacts/_MixedMMH100.py,sha256=IWD7eKhbjfKnCyqR7PqvL370K5zWAKZS2LtlWso48mk,7882 +torch/_inductor/autoheuristic/artifacts/_PadMMA100.py,sha256=KReKRxB8tDQNfBLalXvw5H0Lludtu4eEXelYHk52xbY,4931 +torch/_inductor/autoheuristic/artifacts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/_inductor/autoheuristic/autoheuristic.py,sha256=ur2gNu9HSpg2I4l9-Gs20o-jIu0v_MH7YgJpcdvSgcQ,11934 +torch/_inductor/autoheuristic/autoheuristic_utils.py,sha256=IsUA-CMY6QEvqPLxx2M66EiecWoQujiXIEFB6K2phtg,11281 +torch/_inductor/autoheuristic/learned_heuristic_controller.py,sha256=B8idkoFWGcuQzyTA_u4JEtUFhOD44rkUMej9s1CxHBo,4317 +torch/_inductor/autoheuristic/learnedheuristic_interface.py,sha256=jeNIjUJJIosSaB5KrnjhhuKuTlTQOy1lzTUZI6LZvsI,2852 +torch/_inductor/autotune_process.py,sha256=wtQjFlkP5-WZezTN4imJR8mI3tkGFzmkzBA_lvkkDE4,29438 +torch/_inductor/bounds.py,sha256=cFDJf3j-Bie2NmSooZAP1ofIbDDol2g88jH8T2TRgXk,9697 +torch/_inductor/choices.py,sha256=355gRF6zLmNRVO6IoCt2QBzpUxkHJJConbwu8Yw7QOY,17660 +torch/_inductor/codecache.py,sha256=lxygl43k6SD9ReLpK2YCFuuKFLli5BDBOIr8Nxt3_rQ,155537 +torch/_inductor/codegen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/_inductor/codegen/aoti_hipify_utils.py,sha256=yrRxPSeWqI6iBnRN7T6ReAGyGEtF4Q_FXrbfIBBEwIc,1320 +torch/_inductor/codegen/aoti_runtime/interface.cpp,sha256=LchumEbziI1U4aoAHrYNapA4wFrPOVcR1Ms6qV-w--E,16345 +torch/_inductor/codegen/block_analysis.py,sha256=0G_3Wqvt6DF57PnTSxfRLduWDijCyaFM2BH3KL9hE_I,6679 +torch/_inductor/codegen/common.py,sha256=sHbtpFyKVaVKiKuiVtBXcOZiq8cyF6Shj5skgTrO07M,95878 +torch/_inductor/codegen/cpp.py,sha256=fQfpyJ0tS_ltvMxboB2UmmU5aQL5t2f0pJZGKrT9KWQ,223490 +torch/_inductor/codegen/cpp_bmm_template.py,sha256=C18nNDumvjLdfmYYLvgNug41J-AzA9EKFWuJtNu87hE,9359 +torch/_inductor/codegen/cpp_flex_attention_template.py,sha256=5VqRhJKHyse1mOJLkP4lw3b-ocJuuaviCZ-mr_czrNw,41024 +torch/_inductor/codegen/cpp_gemm_template.py,sha256=Ms15f4NMGBz0KcAREzwQrf8fHusAvgpkcn7I6wxc-0Q,75880 +torch/_inductor/codegen/cpp_grouped_gemm_template.py,sha256=0x9779lteJ9wKVf4aMBj3dFU5uojnUM0RrBm0j7HIAE,20382 +torch/_inductor/codegen/cpp_micro_gemm.py,sha256=TE2HJt1u1IEscfkuaPfWJ_uiMT8R-rcnHNboqpsBnKY,69500 +torch/_inductor/codegen/cpp_template.py,sha256=ZQH-pbsjrzm-H4TmjIU5Ppde5RsTweb3Ogz-LgSHgGY,4909 +torch/_inductor/codegen/cpp_template_kernel.py,sha256=gY603v_rywjhGuLAMCyreOxb2f9THDj48_LPJaPdm7Y,25116 +torch/_inductor/codegen/cpp_utils.py,sha256=BDckMtK0ZbyOBne3xHZKrXGKmqglsAEjIRDKe2GMItQ,27760 +torch/_inductor/codegen/cpp_wrapper_cpu.py,sha256=QV8L2QLOE4bldRPSl1uIwuAKTW4B2t2kjCyTjQQiZos,119387 +torch/_inductor/codegen/cpp_wrapper_cpu_array_ref.py,sha256=owQ1i3D1eojXPLB8Tw_1rkKmLMsDbRtlPBjfoqR4CBo,38369 +torch/_inductor/codegen/cpp_wrapper_gpu.py,sha256=nZ-MJFgd8DTljPtKr1vVfgwkhhdeNARBMjZIxvKE8Gk,28487 +torch/_inductor/codegen/cpp_wrapper_mps.py,sha256=5h7Re9oxPRRTc793KPNqzcuxHDQwcArQ4QqEo4hXHuE,3508 +torch/_inductor/codegen/cpu_device_op_overrides.py,sha256=Y2w9lzM5eHH6R7Jj9ALOIXhK5TLHKPBHn9bgeYmej24,632 +torch/_inductor/codegen/cuda/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/_inductor/codegen/cuda/cuda_cpp_scheduling.py,sha256=0NKO1GZX25S0tno_8DErRYlrFVLuX9rHLb6YhIhVkSw,11817 +torch/_inductor/codegen/cuda/cuda_env.py,sha256=WkU-REBQH3T-f4gSodgLuFuTXbSiZe_SKEa9VGfX724,1166 +torch/_inductor/codegen/cuda/cuda_kernel.py,sha256=_XmszJJDpBYeRiztQixBf9r1Eep8EfsF57ZF4ATtgTU,24040 +torch/_inductor/codegen/cuda/cuda_template.py,sha256=X5uT7x7qvKUbER0KWkY35gWCDorUs0ezIfw8B7m1tew,11208 +torch/_inductor/codegen/cuda/cutlass_cache.py,sha256=qVgS4A-d6DlfXcKEQ0Tzka5j9TrRCNYooouQID09PGI,3086 +torch/_inductor/codegen/cuda/cutlass_lib_extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/_inductor/codegen/cuda/cutlass_lib_extensions/evt_extensions.py,sha256=iFoYSwfu8I0BmXpm1cTTnKItAYHRuz6QMXmdOZSmbFg,9843 +torch/_inductor/codegen/cuda/cutlass_lib_extensions/gemm_operation_extensions.py,sha256=Md6z7iwTChXLhSkxXctD2zh_68D8I4WiJ1GLxl7cafY,18646 +torch/_inductor/codegen/cuda/cutlass_presets.py,sha256=JdkayT0PJYQYM_VY_o9tcG6i9p87mUaOc3YZdfmbTx8,25506 +torch/_inductor/codegen/cuda/cutlass_python_evt.py,sha256=1S47SoOjPA12sbvZqeNQswxGcM999I-7vrh-qHrkfUQ,11734 +torch/_inductor/codegen/cuda/cutlass_utils.py,sha256=fBRqyrJBrw2RtVfAP6KBupYQ-0MiT-1O9dOtgfZsyUc,17042 +torch/_inductor/codegen/cuda/device_op_overrides.py,sha256=TV8fxWtNJ3iYs01pG5JOooJaiiP0_IBHKT6cJp4t1Bk,14852 +torch/_inductor/codegen/cuda/gemm_template.py,sha256=IA-mazSxT93dKSS-8aVNhviJZhycAKEbuUkGQ7yvUPY,74860 +torch/_inductor/codegen/cuda/serialization.py,sha256=dRZTfddpwjfz1mRqai7N6N9tJU78R4cz8EiSm-27vi8,16468 +torch/_inductor/codegen/cuda_combined_scheduling.py,sha256=KRUPRqpxwkfvE_LJg1aoyEKmDrYlhJMWctDP3_RMCCU,5031 +torch/_inductor/codegen/debug_utils.py,sha256=cZdDrg9Zq1Xjxf_kxrgiNa7vZJi_F_DvKXYmiVzSSYc,11251 +torch/_inductor/codegen/halide.py,sha256=NzAusXp_WWuU-sj4zVEZ1Rzj42Gu7X_MH_tzX8l3Ie4,62216 +torch/_inductor/codegen/memory_planning.py,sha256=0NhWXzHj1czFExBfcLDxPdnxESAGUMAmeapwWRSvGUg,25081 +torch/_inductor/codegen/mps.py,sha256=RiXxHc-wQEejblMBi8GE42PtFphyc8eOytdGEleWAF0,37827 +torch/_inductor/codegen/mps_device_op_overrides.py,sha256=AY7QsOH2mELU0HsZz6B6ygxdSGwVnvS7v4ypb8KRXlo,671 +torch/_inductor/codegen/multi_kernel.py,sha256=TBLz42MlcVea22_lmAefVPU7abxlvojLu3tF0myzRlk,13609 +torch/_inductor/codegen/rocm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/_inductor/codegen/rocm/ck_conv_template.py,sha256=dyM_S23VB4TOCg2VLM0FLAsKmwQOXt6_A9eun-d2NZM,24404 +torch/_inductor/codegen/rocm/ck_template.py,sha256=VNDUdTyKz9wtSFG67fzsgOWafHjsEYiLaLVAaoDAw1k,3590 +torch/_inductor/codegen/rocm/ck_tile_template.py,sha256=6Y_MdWtGmhTjdfIz6ObUnqTcnPiaAq2-nQPaBIzu52A,1486 +torch/_inductor/codegen/rocm/ck_tile_universal_gemm_template.py,sha256=vb2FYPldAd-cNkLvG2vSxXdep9DCTD3azJ6r9D_VA-o,36356 +torch/_inductor/codegen/rocm/ck_universal_gemm_template.py,sha256=U3cdRW-amwQbfKTM7d_CPjyB2377kqAks0rhx1SxnDM,39510 +torch/_inductor/codegen/rocm/compile_command.py,sha256=N89cpB8vxp9jKcp_ZT9aQGy0Mf-y4Pe43BeoDywx_Lk,4472 +torch/_inductor/codegen/rocm/rocm_benchmark_request.py,sha256=6w774pdwyKRQhr_3p6Hx1dfu2QCfeF56_Aj1sM3T23M,5071 +torch/_inductor/codegen/rocm/rocm_cpp_scheduling.py,sha256=M5xC2VColp0VAL-7IPQdfbXOefojjKyhBLJt2ppB2-U,3799 +torch/_inductor/codegen/rocm/rocm_kernel.py,sha256=YM3qkE5nV1r7t3rsd1eHLw84QzSzJZuOPJ3Y-IEcNaA,10373 +torch/_inductor/codegen/rocm/rocm_template.py,sha256=Rcw9UUX2eEUjn633oBTBytzXIWiIogQ0oWBaiMcZnH4,6637 +torch/_inductor/codegen/rocm/rocm_template_buffer.py,sha256=AAlfh5CIKIXGR9I6YkzOWHU1roJvJJ9s3pdtlgMRnL8,827 +torch/_inductor/codegen/rocm/rocm_utils.py,sha256=Xtfs6gAmUdpTz7cVX2vKvUbbdfUCCeiKhbSttru-F8U,266 +torch/_inductor/codegen/simd.py,sha256=-9u5SUwJrYIRZGlLQW7942qWOgFo5yV3DzQzj0EyjK0,95661 +torch/_inductor/codegen/simd_kernel_features.py,sha256=XdfSEHE-z_Py2fBcPHTxLvXQWER94rQTgLfXGsbTLLE,23721 +torch/_inductor/codegen/subgraph.py,sha256=d2Ny-ZyZn6vHlllSMSeJJ3ofcw8Y_oEw3-6jElj3lts,7422 +torch/_inductor/codegen/triton.py,sha256=nH8Jym3TjiAPRjsyqsUbn75a6I8SufrRcEIsQigUFO0,177249 +torch/_inductor/codegen/triton_combo_kernel.py,sha256=rju2lW4NuUxPqfqw8JdeZlUGL9qK6t32SA3vsd30cv8,41413 +torch/_inductor/codegen/triton_split_scan.py,sha256=jKpS8F6VWe4CsTm9uNZjzlbPuwjy6N1KThb2s51GVjc,7243 +torch/_inductor/codegen/triton_utils.py,sha256=yl_4XL6q8-gXuoVF5SiY3mxVUXr3GYB6A8O4lkGr324,8965 +torch/_inductor/codegen/wrapper.py,sha256=pgnpOFy6glpoNdFKY1gzzK_NqmRPIr9YBRkph-iPAWQ,134938 +torch/_inductor/codegen/wrapper_fxir.py,sha256=ydzOVJYkhzPCZVYQU2nnld-vq7sIcdiv5mQn1R6GI6w,24613 +torch/_inductor/codegen/xpu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/_inductor/codegen/xpu/device_op_overrides.py,sha256=LGBrq9lbtXcIQ81YXX0d9VLPZ2UaBTbyixXyYOVSGKY,1804 +torch/_inductor/comm_analysis.py,sha256=ggFRjtJx5xdJPCS8_c1HPjdQcwL_Qg87cXocHjs3FsI,8286 +torch/_inductor/comm_lowering.py,sha256=p9foSbZnyOuS18fPmybs-7_m3Gl5sfBIx8svcO2iwb0,12664 +torch/_inductor/comms.py,sha256=KYyhMGuEvHuY0lEEi9D2oabetKPm5qCeZe3fuwO7u4M,42472 +torch/_inductor/compile_fx.py,sha256=ROE57BJc6W2KxE3xV3ZkU6cPfFsVIa5vDMN6z4KAtRs,102927 +torch/_inductor/compile_fx_async.py,sha256=-WTWx3M3tdZ9_W0_GZemp6DQ_zL7UCrdx4z6OFr0G4w,6437 +torch/_inductor/compile_fx_ext.py,sha256=4JtAp4Wrebhfk20pkPuyARF3Tl7Id_KLfP_OxCAu1wM,23144 +torch/_inductor/compile_fx_subproc.py,sha256=42dzDS3etsQEhKrQEFGCGDSIynBvQyc_IdnrBMFhSBQ,3197 +torch/_inductor/compile_worker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/_inductor/compile_worker/__main__.py,sha256=Yj1LnCvSJWe_Y8LWarFQLceqCmadcX7RhqP9uBS-zYA,2245 +torch/_inductor/compile_worker/subproc_pool.py,sha256=cqjkSn6lhuilJ0g0xDZ_YhJfpP9ep4M0OY3TaTYhT8M,13219 +torch/_inductor/compile_worker/tracked_process_pool.py,sha256=cp81uQxmiVTNYKC3U_UUhS2N1PQXdwX2fdhJnXoYlo0,3620 +torch/_inductor/compile_worker/utils.py,sha256=3b04aEtIzkbhy8xc0uIRXy6FkXex1lOSoFuq6vBMYvQ,1506 +torch/_inductor/compiler_bisector.py,sha256=bhZvCDpCcXW9WcD_70Ko5JsY0dr_YQ_jqOzC25vmdHA,22415 +torch/_inductor/config.py,sha256=nJ9odx4IaJ6PdaeXLJhXhAPNvUEGWnTr6OnxsMYjM80,70345 +torch/_inductor/constant_folding.py,sha256=YWROjzk9OyKyCiHOjD9ttYdUs72sftXHvCWqfHcLfC0,15216 +torch/_inductor/cpp_builder.py,sha256=I0WpFhNZd0hxXgxPbUwm6t2OG6Uxd8-75CfF94Yo2zo,67649 +torch/_inductor/cpu_vec_isa.py,sha256=L3jIn2pAo-NuuR2fH8vb-srOHmR2QQhkcZ1iFQfv5Cg,14035 +torch/_inductor/cudagraph_trees.py,sha256=zzkkWpfMjL_AViWf1IGNOvRa2F_HfJDTN6KAN6T76wg,103554 +torch/_inductor/cudagraph_utils.py,sha256=kCsCeuPOiEp7rrmqJqyCCj9lnYj-IZZjWlhcW1nCtYQ,13880 +torch/_inductor/custom_graph_pass.py,sha256=6PaKfeAg1VilMzTKJbgriXwopiPYPCcmN6GNr0vEAog,3858 +torch/_inductor/debug.py,sha256=2T-V5PNV5p0VKRk66NkKu57JbXQZ01A4jmbqsJx4QA0,33032 +torch/_inductor/decomposition.py,sha256=V1ujWMlZJFflEK_ZyJQ2OtzvNqBCqh8mxiiRn-nKTTo,36987 +torch/_inductor/dependencies.py,sha256=WND9RVABXSdG7N_nTHoshiWukLQ7uJDuFD1roG_w-uY,29675 +torch/_inductor/dtype_propagation.py,sha256=O9PbGmSGezGPcnk8SAl8uSMbxScsYDA7bMdiZBDMcfk,11497 +torch/_inductor/exc.py,sha256=_YJnBsJZ2cWMbYba3B1CrXVa_JMMCiiV7-psFmS_Zpc,4655 +torch/_inductor/extern_node_serializer.py,sha256=tvVk5NzYVvqTI0v-k2hQikc8h6EsXazK1NQ2soHZ2yc,830 +torch/_inductor/freezing.py,sha256=GmVT2GkPk8a0xfPkcAeizA-lgbKERP_69uqYpZmjoG4,10876 +torch/_inductor/freezing_utils.py,sha256=0kQfN4H2yEI_eNXINqa0ws5Q-mm9EVT6ARcAKrz3P80,1268 +torch/_inductor/fuzzer.py,sha256=XoAF5NNnoihjlHwIoeHUmZQmtJD1mBtqxjNGtaM5tPo,36872 +torch/_inductor/fx_passes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/_inductor/fx_passes/b2b_gemm.py,sha256=mLhcrE3jYDbRIpw4P8MPkhnqa9amYEZ_gnnHIt0ZuBg,24966 +torch/_inductor/fx_passes/binary_folding.py,sha256=3gg83QsmWDsXZWcyf1HCx2QdRr5dS1SGux74iUz67Wg,19850 +torch/_inductor/fx_passes/ddp_fusion.py,sha256=JhfCwHrXB1KdzbdMuG2YbG6HTDOov_f9BStILEic9Gc,21131 +torch/_inductor/fx_passes/decompose_mem_bound_mm.py,sha256=p30ouusnYxf-i93wxPl5X5dHSdZmBmcEyOIf2wF4VR0,5325 +torch/_inductor/fx_passes/dedupe_symint_uses.py,sha256=T1Tj8O1CjoX_KlIPD5SvQQhxvbLOqUkqCPXFgHHqzl4,2517 +torch/_inductor/fx_passes/efficient_conv_bn_eval.py,sha256=s_CbEnwov70368S4j7BvbuVM3VI7TBUiBV3JMqgXrCA,14078 +torch/_inductor/fx_passes/freezing_patterns.py,sha256=dsOZ8tj4apQ6jMn8LfL6bB8N1ux4b2TKSSJbexhwzKA,9030 +torch/_inductor/fx_passes/fuse_attention.py,sha256=klOmmSueNJpVfOpN12qizTH4xpnESOobFWSu9puC0yo,35058 +torch/_inductor/fx_passes/group_batch_fusion.py,sha256=-9eP6arogWszQUP_S6tgUDUi7BMJc8FhtLJKkdaIYVk,58707 +torch/_inductor/fx_passes/joint_graph.py,sha256=RfQLs4HhCQ_cSmaTlyYgrEESkrx2KsLb97PsfuujwDQ,33646 +torch/_inductor/fx_passes/micro_pipeline_tp.py,sha256=Tl8kblj-7KD-O2nyzuBWMtxFZ2ymcIMhVIKs74rfrVk,39007 +torch/_inductor/fx_passes/misc_patterns.py,sha256=cLU5ze8fkJ7NujLpZNfqQoJ158JJLQ9iqtSnc39K7hw,4787 +torch/_inductor/fx_passes/mkldnn_fusion.py,sha256=-cpUFOxTKqOwtMUs7PaLJrctOwTteuBcfAEwrowQKdk,60071 +torch/_inductor/fx_passes/numeric_utils.py,sha256=iAf4gzphnOWzRwXGUikWvb6AH9SS6fJgD1sR6WjkJnk,7278 +torch/_inductor/fx_passes/pad_mm.py,sha256=0IonFUxrjhPSo0dcR8Ubso-SSL4r3Xz0skUnVfCpExQ,29390 +torch/_inductor/fx_passes/post_grad.py,sha256=fCJPLIa_nagSc0rV2V1J2GNz3zZYgkc2Tp6pP9a2sNY,63002 +torch/_inductor/fx_passes/pre_grad.py,sha256=kahZZXp6d25aL0Mh-YAn0vsAU1jG3R_7l59Id9QtYyU,30532 +torch/_inductor/fx_passes/quantization.py,sha256=ArhqwEXNtpSATnRrDHFNRkTNakHq4y4lMgqHmRAUxAk,142310 +torch/_inductor/fx_passes/reinplace.py,sha256=9W4QQ19aojPj0wPowtuiVh0XT7U_W0znroef-Fp44JY,29842 +torch/_inductor/fx_passes/replace_random.py,sha256=1l2fLTfAfs_rDHP8p1_5jsS8u36dn5X6Q-Vvlt7Prsg,4052 +torch/_inductor/fx_passes/serialized_patterns/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_1.py,sha256=jLk3u9vyyPhzzmJCf82H-lEa_ycI9FGR2F1e14BF7JA,11177 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_10.py,sha256=68W2L3OqEIyiuu6XvfwyXl0PKeoyEKRVqxKvdt3DrfI,14216 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_11.py,sha256=YzKAdp1wKREpwh-ma-MPjMKRAImnfIFpWrqbpBtJBPc,13987 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_12.py,sha256=6o07OMAP3P4yuOSrmsW_AsSq9gYeTVD2SkxJh8xQoZc,15257 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_13.py,sha256=yQOZ4yfymHPP_gNISoxq4pOt3xPic8eJY0InMr9hsPg,7862 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_14.py,sha256=p729osgik7UB0ySzKMcthIj9Pk3rqh6OdP6wKZFNqvA,14323 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_15.py,sha256=MdfVHtUJn0ZzgPsDoLIsj7Sla3HnflFCIELfS1uAYy0,16213 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_16.py,sha256=-1tQU2MyC9heRGD6sOibLRER_Mlzx9civVLrREdQSMo,43596 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_17.py,sha256=6MsCMyU-wCAUxWOmGt6fTqmbmTmm1BPeb3QhI8SDQuc,17441 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_18.py,sha256=OAXyig6GrLjMznY5caHUpEHiImtO0Fw1MbVxzyW5H2w,32736 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_19.py,sha256=F2irIKcyqlFALHNcoob2Mnp5HUmnDIQbrc33MsOcITg,14046 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_2.py,sha256=-QeXpOThLf9w6HsKJT5nJdCfyXRK4sgxE6Jy4v87tdw,11187 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_20.py,sha256=KOQ8WwRe3nVV3gdnr_qNk523jMXtL8DtXlxhKW6dBhI,17341 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_21.py,sha256=USHVfuie8dood5SVXhlxWFvv3Q0egYNXPsof68zdlzE,15066 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_22.py,sha256=j3pykidhZaV1jQNgq5i_Qb9mO6k53OGq9z8-5DpfbSQ,15404 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_23.py,sha256=wvxm98mRv7X4j3rdmfEc7P2_gP8sprseCBCtI8DFuAY,15318 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_3.py,sha256=GAyzPOGSMzkdAxXkk3qpf-8y8GrWY-Wetzpy1aJU4oI,12447 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_4.py,sha256=x3mGjvSYS7hImiBFUpXbySYVw8ZNVjCyqjjTrNIiHsg,12411 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_5.py,sha256=ik5GT8rgEUQ4W03aYrqVxz-o8TeldqvCLvUwgnwhD0E,11413 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_6.py,sha256=yxUEqgvLGs_3Ta726GdkN-K2oi4YWYgpwqVL-_cRXdQ,12641 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_7.py,sha256=VFWwCNLXaRwyJrDMbPmA3KZpeE1-r8NZi1gHcd3aTt0,15436 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_8.py,sha256=sOkMfIPN06YFB2M7AQuUcgIOvZez_G9tUtK1iXWp0sc,14204 +torch/_inductor/fx_passes/serialized_patterns/_sfdp_pattern_9.py,sha256=n3KBeZyp-oa69T0ACMtj3DcAS1DnmVmDT8OGdaMZWZw,15444 +torch/_inductor/fx_passes/serialized_patterns/addmm_pattern.py,sha256=9cl9L8L2CDu--cjIWRF63Ngl7YX9F38gGXCeEz3r0bY,1858 +torch/_inductor/fx_passes/serialized_patterns/bmm_pattern.py,sha256=6PYPyMz8dOVNYEKIIR67MvfdWL6TlF0xC8VnzsbHBLo,1272 +torch/_inductor/fx_passes/serialized_patterns/mm_pattern.py,sha256=MIExrwuf-ZmoMuOiUU3SENtMW8AjnW2yL9Q-kKsiqs0,1260 +torch/_inductor/fx_passes/split_cat.py,sha256=Osl6KgikcHgB2cQo7jlF5UEpfB3ytkdZUleOrtiLDpw,118330 +torch/_inductor/fx_utils.py,sha256=eIniA3b9vmmO2RX8PefV_fNogWPFZzht447-EhemaN8,10064 +torch/_inductor/graph.py,sha256=sPiAB3p-Gv3L5f-wCquHynUOKvUWljxGpr4avx2k5ag,103834 +torch/_inductor/hooks.py,sha256=AtWwyD2BM4XhrcbKP7nlm8qA_vHV7ZPk3EDNptNxUz0,639 +torch/_inductor/index_propagation.py,sha256=L4cCmzu_T4Jg4ijlt_IzpMv_GLiX6aHRW1kOYJU53uE,12868 +torch/_inductor/inductor_prims.py,sha256=ysyDTQSzTKMFHWIhGHxe7GxAfKu9wXQ511ZOS2cD8jk,7301 +torch/_inductor/ir.py,sha256=0vO7xwikSm2ULyxpiJ2eKGt5CMvNWhXBV_dZcgyH-Ho,304456 +torch/_inductor/jagged_lowerings.py,sha256=0k_ricqGGOrhE01sj1C5y9AByjPVpcdIeYEJBVIivnQ,8966 +torch/_inductor/kernel/__init__.py,sha256=czWqLDNflWRY8jDt7n3HFxFBcEA2Hgtymhzp2BLr3I8,40 +torch/_inductor/kernel/bmm.py,sha256=p6l6Hshlu4skHbCMesf4YpiLJAzqdikQdqoJCa2F2u4,9808 +torch/_inductor/kernel/conv.py,sha256=GbAvq_Mthxwh9-OU6yZoHNk-PSAuVB3kHZCb_hyfDoM,21314 +torch/_inductor/kernel/flex_attention.py,sha256=qXXqYGvInuueY9qPCBQ9pSbMurehE6j4tz6TjSf44-M,100353 +torch/_inductor/kernel/flex_decoding.py,sha256=Cx3xHhrAZpb_DyHRAU66zl0eRGeUG4UZFdKkiQEw0bM,23261 +torch/_inductor/kernel/mm.py,sha256=Yvrw023FzPSmtTrm4aWQDHk5z0ASN3-SodLLtk-fm7o,44849 +torch/_inductor/kernel/mm_common.py,sha256=6BjOQ5FX_oPRIcp187QbALWqznTX-Vix4zt_32Uluxk,8899 +torch/_inductor/kernel/mm_plus_mm.py,sha256=fIDFlnD-iDS-xFXzgjTkRWvi7e_iyulAv8Qpdej1naA,5428 +torch/_inductor/kernel/mm_scaled_grouped.py,sha256=V4Kbu4aAY1Y5_pK9OnRgMDZTkDMroSjBYV_q9Sw1J_I,22387 +torch/_inductor/loop_body.py,sha256=sA6lp_DeNPeB6acZ9r0IB4dotG-4fkrPHM6ETYcfr9Q,24291 +torch/_inductor/lowering.py,sha256=Q1XKvSUvUDxDjnll6Wfr7RmeNeOPTH-flKY9sVDwwGM,232064 +torch/_inductor/memory.py,sha256=Q5ogs9I5XKcnIC90QZ0s1kKaBiQDMbv9EATR6hnryLs,26080 +torch/_inductor/metrics.py,sha256=OULvPKV0BKT9MUNR9HdpnUipW7-yKdvFDI3sVO9Itas,13871 +torch/_inductor/mkldnn_ir.py,sha256=fh_YlizVrLUmLkHh0UwDpkvBmSnomXih8iA8o0ON7YA,41911 +torch/_inductor/mkldnn_lowerings.py,sha256=Ssrp9HMMo9hOwM7VJZ_2sxAlm_RoR0vYUdePLkNmn-c,54213 +torch/_inductor/mock_cache.py,sha256=Y5mhaQ2fJWcb9TPa8n1j2cOb4OtTANQ64A8DiItRgJY,8556 +torch/_inductor/ops_handler.py,sha256=69hnzao_vI-UVCz6tsjJdMH_RIOy_-_tjIOSwfPhTqs,35527 +torch/_inductor/optimize_indexing.py,sha256=KdIFrlHwny2NlrQoiMp5G1fm0e-EwZEzzQ0tNXA4nXM,4135 +torch/_inductor/output_code.py,sha256=kBXBJRPkilUzhLuULpoaxcm4xlu-WyX10BCUXTSnsgM,29541 +torch/_inductor/package/__init__.py,sha256=s1BQDRTTu4baQTZPdR07q8CBO_b5kUpXEp5VawAeQQI,67 +torch/_inductor/package/build_package.py,sha256=ZS3248m9MtGnCGG0YWrne2y7FDTIoF2ZWmKdxvIRzMg,329 +torch/_inductor/package/package.py,sha256=OH75k9Ur3S4y3vxVAF9QXhWAFQDkAC8XCtV9-kV3Yug,4453 +torch/_inductor/pattern_matcher.py,sha256=erLeIEr4i4gMTEm1yF132Gqc3a6UiyljdMlDWK2naWE,80613 +torch/_inductor/quantized_lowerings.py,sha256=6k-byUEN6KUiY0HcS0o2o16K679_qhFkX5yjJZTR7P8,5646 +torch/_inductor/remote_cache.py,sha256=T25KMyMlrK9wU666DtDZBdifCjF4gkluFXXBEWsQT-c,12796 +torch/_inductor/runtime/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/_inductor/runtime/autotune_cache.py,sha256=xjm2pbV7XNeAWg-vtcKxC45BjpAL-xeTz89e29DX43g,22736 +torch/_inductor/runtime/benchmarking.py,sha256=3xcZsSMpFcv82MinxwuILaI5feh8gIZ1kqYrBRbKL8w,11218 +torch/_inductor/runtime/cache_dir_utils.py,sha256=VMMt-78VVHpAE90lDR7cNBLc9pxE3mE5pFuhGnyh1V0,1444 +torch/_inductor/runtime/compile_tasks.py,sha256=UtXp9mkvvxzlC_xlcOUh4-bamorWJIRQM_02HJKaBxc,2017 +torch/_inductor/runtime/coordinate_descent_tuner.py,sha256=16D6Y6HeIRksJVX4dhWzUnP1RHsJ3yl9cmvQX429I8g,10046 +torch/_inductor/runtime/halide_helpers.py,sha256=PFJ3PZNXkdinWPAjB6LuKOH4vd2uPKMO2G8X3iSvvr8,3542 +torch/_inductor/runtime/hints.py,sha256=szKL2z1wpE4PippjpFucmZVVZ4aNJbQQz-c9SI6v5x4,7037 +torch/_inductor/runtime/runtime_utils.py,sha256=VUQ4WxpKQnP2ptHHBNe60goTNTm2SMKkc1HSk9YxpAA,4982 +torch/_inductor/runtime/static_cuda_launcher.py,sha256=Z4kYTfaHc2EQlQvcRcMnXgCjfeKLybk8hvSJX3z2Lqw,9261 +torch/_inductor/runtime/triton_compat.py,sha256=0Q2-4OMUMVW86Jyl3uY0DqvXr2DjxJCmX9FyLs7J7oQ,4095 +torch/_inductor/runtime/triton_helpers.py,sha256=TAGpStbhXwc3eCUlFKfIBoNoDcaJ9z4TF-h7ejeA7yc,23098 +torch/_inductor/runtime/triton_heuristics.py,sha256=odpEdeyVcHW7xQjDXoJrqz60tI7uE6bvDjgmq7efgEQ,111499 +torch/_inductor/scheduler.py,sha256=A-dY3VOYWRMQ833-Ta_8RMipay33z8aduFRbdh4qZcA,195793 +torch/_inductor/script.ld,sha256=pYkdES4_8MJi9MkEF79gr1KFT4iPUvbMT7NlDWP7aec,430 +torch/_inductor/select_algorithm.py,sha256=Yi0U6LlnXzvcocC6qgBbBzWee-CYOek40S0gyT1wmQc,116990 +torch/_inductor/sizevars.py,sha256=34LfDG6L5SQ3T9Pq1Wl12IzzHzAkSclCmNZVW5rAYa0,37809 +torch/_inductor/standalone_compile.py,sha256=hx_mmOm4DtbwTJUeUMQt5XZpdjNacR3FkKyN9_xB6lg,9552 +torch/_inductor/subgraph_lowering.py,sha256=EKV5_3PCty9F0p3erX9SA4wmDNYCYuOx2ENKVECZ4iI,7350 +torch/_inductor/template_heuristics.py,sha256=CGsrBp8QZqj3arbW3yNf2gGWjEG-08iZ83M9JvpSqyY,44566 +torch/_inductor/test_case.py,sha256=8IBDEvcyyj5r30r5AgN8LSbhi_Bvi_X-P3dzI1z0PIg,1378 +torch/_inductor/test_operators.py,sha256=c31b1UrHeyW1KCgVb7Y1kWX65soWhzkFqHgAEakNu1I,933 +torch/_inductor/tiling_utils.py,sha256=orKnppeDhk2ojZM4WJ1myjFGjO498WopL8HRP70KFAw,25894 +torch/_inductor/triton_bundler.py,sha256=5FAu5ipRofWjA-px_DnqYNzXEN3agR5_oKvXWsypJBI,16149 +torch/_inductor/utils.py,sha256=x-28vmP2Gw2OGS3U8RrXpAF-JoTVqmF-t_-Awm7DBdg,102710 +torch/_inductor/virtualized.py,sha256=CIHcPCPx_4m7OfzdA3PA_6B8-cmkRSEObV32ba5qX4Y,14070 +torch/_inductor/wrapper_benchmark.py,sha256=YEgIHZtuDGW9cmUgO3liNTHxVawFwEwwdu2jgV_6074,15846 +torch/_jit_internal.py,sha256=-4eiCKDU84yf91m5rgaq32UI97aFzOpuybEA8VdObvI,53877 +torch/_lazy/__init__.py,sha256=F0lAsONCnXzChhzI8m2QTyNg7WFrXHak0C38ZsPW2SI,1793 +torch/_lazy/closure.py,sha256=Box1nl4M2gEROXjglTKlDc2XnhtWD9xUG6IOvq6HMAw,5572 +torch/_lazy/computation.py,sha256=qGyb2-6Mk9HtK1ekYjC-RaRAFG8sUEAazCoO4BuCM1U,919 +torch/_lazy/config.py,sha256=-3YMuc6MvZef7TCPIX4J-MtrHwXVsrHPKZDvdCQ6iYM,448 +torch/_lazy/debug.py,sha256=LOZyiVd9aFg1xYiUwq-FKA1jMxuGDyU0KcTpor81r1s,738 +torch/_lazy/device_context.py,sha256=0KTBJL_7TjdNIQuY-d4OtcIpD00-1yVHHeAyeDNM9lQ,681 +torch/_lazy/extract_compiled_graph.py,sha256=YCKEWPmQ9RIqWtForDTdsC-fgyo7_-feoZAJO8ccEDM,8423 +torch/_lazy/ir_cache.py,sha256=HcW7N_L3ff-7_dDnpTsuh0ZQHWU-CSkawOriqQQCoL4,348 +torch/_lazy/metrics.py,sha256=XT4Y9Loj4sLSAjhIVBiZ6pKrUXFNd4yBi1YvFoCm7to,545 +torch/_lazy/tensor_factory_functions.py,sha256=_vbfXc_XMUD6dXa3dP-BHlwmc1nWhnZ5R1SkY9sIZ98,1368 +torch/_lazy/ts_backend.py,sha256=BfAAT0WhImXNRhQp3Pfbp1tLuKuy7UBt4OziWTEYi9o,163 +torch/_library/__init__.py,sha256=nrHN83rXP5crQ0OeQiDyqXNcyoctiW-5dOFVAPArtzs,269 +torch/_library/autograd.py,sha256=pu76tqauib1DDXupq6-8gn391F1UvxmJhm8FFiyUmcI,8729 +torch/_library/custom_ops.py,sha256=c4DnzcyyMHRCyi1RZfgQ1byVp4o0ahnSplHL2h6ZwcE,37539 +torch/_library/fake_class_registry.py,sha256=ru-KiWdMjNn9IZOMX5TLj6Z8Nms4xYFJ7rfAfMYsW7w,12781 +torch/_library/fake_impl.py,sha256=1GayeWaNzMG2El1btE7wH7HdQyesTKoJTENz-JqMWKk,8763 +torch/_library/fake_profile.py,sha256=3HDD9BSLT31FZnMnGTAF1slWx8s3tsCmfdmcbieAeOE,11488 +torch/_library/infer_schema.py,sha256=dx7ELnLxAQJFCF4IQYaqe5VzVpBJR1M5ih9KOWkBeSI,12588 +torch/_library/simple_registry.py,sha256=tLgDcxC-NYFYo_eLx9i7-24mWhk_tOmAumVjeRIXAuI,2638 +torch/_library/triton.py,sha256=AulIpzRGo2N690MwMHnQaabhL3SBDBwOvWNoNIbm0fQ,11687 +torch/_library/utils.py,sha256=ro78jPAgp-iBxGAMZWTbA6flV9ydrZoKR6NCThLA1rI,18299 +torch/_linalg_utils.py,sha256=4vpZ7YEpzdIq0neaEEjZgmlnfLLIQOvJdHtCdme-gvQ,5164 +torch/_lobpcg.py,sha256=0JPYP7fSKsFYFhlKvFSJzbaHWsCxM41JnhNnhsbEB24,43427 +torch/_logging/__init__.py,sha256=8WpxVfsCJxPqQ2BAkP25KQ_AUGkg9VORNPd_hTgWXfE,799 +torch/_logging/_internal.py,sha256=jOI8gMlwOTvwBgSUccvjSIptbye5G2u_zbiVYuJ6kIs,48803 +torch/_logging/_registrations.py,sha256=J8gAKOnSTG4zmkhIs0oT3BFD147ikasLYKJxeND4z4k,7992 +torch/_logging/scribe.py,sha256=nU1SBsR2atY-2fXK4cDblQzxwyjB2XC2daC9gam4mUc,2578 +torch/_logging/structured.py,sha256=EGgfIPwg_x-Z6IiWyjgKYvAcRDy16uZR9eChPlydLHs,2927 +torch/_lowrank.py,sha256=7FYot_56Q1fdPLJ8GkzJbXW27y2ZpmW8RflrI9BZ7dg,10554 +torch/_meta_registrations.py,sha256=3WOwJO5-z9mcFVg0ymg6605ZQvc2mANPJgNR_VWWnDE,251384 +torch/_namedtensor_internals.py,sha256=EsJQ1IOY0FviEofxv3oCEctNUuQ_83vGoDxdZMT3XqE,5290 +torch/_numpy/__init__.py,sha256=bpeDB3XOafjktNj4KOvVeKopfQzI_munhA4k89ZdlW8,556 +torch/_numpy/_binary_ufuncs_impl.py,sha256=h_xCtCyRAdTEB2T_r0AZ25G7u9IM3PRPr1mVv8Cr0qY,1871 +torch/_numpy/_casting_dicts.py,sha256=EmT0GYUz7Ii1iT1prWZgsahqtZZ069I1O0T2pzhSlOE,42478 +torch/_numpy/_dtypes.py,sha256=vA1enP8uKq8exmJPc4z-BlgCMS4GJYY4nuTkZBFzGgA,10326 +torch/_numpy/_dtypes_impl.py,sha256=0Kr3Ykf9BS0Lz-UhDyNuvV72T0VDf2lYgZgMdtPRpvM,5907 +torch/_numpy/_funcs.py,sha256=vT_F_qnfL3CJoZr_R1NEHvETRCnIoEOrNGXVP5o-5aM,2097 +torch/_numpy/_funcs_impl.py,sha256=9Qmpmm9hsEf3K_vfYJyvvVQ97ziH1yPvs_UA4jSOZfI,59239 +torch/_numpy/_getlimits.py,sha256=QRUbPnrehERJA9Wk01SdUaF1F2FhksfgpewYN6SmtgE,269 +torch/_numpy/_ndarray.py,sha256=4KLpN3zrJRnM8PTH1JOn2pRqNNdljtoRxI7GPtWNqps,16644 +torch/_numpy/_normalizations.py,sha256=s2H3P_X612bygUNSPrnaSJ8hKwD7nucv2WDKFA-Kq8Y,8249 +torch/_numpy/_reductions_impl.py,sha256=kRfjvIs-8OMgrX0riDZ_8p8vKtFtRs1usCWbBUBvxDI,11800 +torch/_numpy/_ufuncs.py,sha256=CJoWG7TWKRZ2tM7JV3qsZXpAYlB2iRzfogs7721xGl0,8366 +torch/_numpy/_unary_ufuncs_impl.py,sha256=IO2kPjYhNoKCyu_9NcMFQF57le1U9VjFmqQFbsWA3Jc,1161 +torch/_numpy/_util.py,sha256=uUCtxtI1k7zTKkuVzitV6ktcCMgDvgVSY4DXBlLhnZQ,7557 +torch/_numpy/fft.py,sha256=lqeN-889bRRT8VjzgAaXoK9aAz8wdQdoiUSVN-oIhRQ,2805 +torch/_numpy/linalg.py,sha256=0YwsdXjndqqifpznxyxzU9ki7aQi5q3vZRrMK41Nsgw,5648 +torch/_numpy/random.py,sha256=yWr3GUftkX9_FZ10phn_V5SWy1gbzpZpnSqVQgqlhwo,4650 +torch/_numpy/testing/__init__.py,sha256=t5Re9c4lijwKoegYKS74CFviLCfSDSWZMvw78ohKYss,375 +torch/_numpy/testing/utils.py,sha256=x6DRPUCF7RPIePIUNT6Bdq9ZuTFjojePaKMzBIExK3w,76198 +torch/_ops.py,sha256=gpzYsnBfeIckCgqscZ2vbP25t4auob6HDYoy_pnfLvw,60538 +torch/_prims/__init__.py,sha256=SRtih4pGnqcyQATAObzBkFcCpdDu7bHf01LmlFNVEkc,81333 +torch/_prims/context.py,sha256=aL8N0T9qoSNGdxRAL78_8kylZfQauqJKYklDLrddF7I,6091 +torch/_prims/debug_prims.py,sha256=kPvBHAnYylT2_k5BYj5iHWjbsTP5D5dZgRTzvKyS45o,1889 +torch/_prims/executor.py,sha256=twXioPGLfG4w_UzRjsx_oQFDLNitHCbStGv5WFwv8_Y,1909 +torch/_prims/rng_prims.py,sha256=sqbRYVw0A9CGeGJQqMH5LVft3yXDfRhWxt1QWsNnizk,14466 +torch/_prims_common/__init__.py,sha256=VxYRX5OSSijF5bUcIdqn8Lp39FoyFpiur4YEFqBfvxI,69668 +torch/_prims_common/wrappers.py,sha256=VlSNpGoTe9YoEbu7dQnrrYn7Oz6wuAYqqrT_Kg576Pc,18232 +torch/_python_dispatcher.py,sha256=tuIlb49DqVSQW8fC4wYUrMYfpou5q_-AR_O47jTElgM,7137 +torch/_refs/__init__.py,sha256=-j7wjwi6-Z-6BodrPxgKZ6kRkovNd8f07yBWyw9c-_4,216413 +torch/_refs/_conversions.py,sha256=BkNyamBLOQLlKy6mB4BSdPRjAKP1ghZ5xcyQDkUs-_s,3533 +torch/_refs/fft.py,sha256=ILk-lg9tdyptz05EYmYkccpWZWGg9t-cM5tio9YuvUo,17967 +torch/_refs/linalg/__init__.py,sha256=qWy_ONrFcDamWjL2BXkV41r-F6As5boGkCJX0O-B_6U,11528 +torch/_refs/nn/__init__.py,sha256=juqd9PbXs4yg45zMJ7BHAOPQjb7sgEbWE9InBtGZhfo,24 +torch/_refs/nn/functional/__init__.py,sha256=kjcizI19vQEBHrbsdYr9c82So-2YtK92Mzu7CBAF7aw,42484 +torch/_refs/special/__init__.py,sha256=9dTprfayVZoIH0UgpUIOO3sZLgx3FrdPsgvAn1i04jc,6824 +torch/_size_docs.py,sha256=z2Ckk4Ufq-Ek6Av1-rs1Wcrg6OoJxTCRPYjEjhzG1Xo,900 +torch/_sources.py,sha256=xo2Lo1d_qEww_3v14jsEnI2K44P3XmEdqLBkMRZniH8,4423 +torch/_storage_docs.py,sha256=KyxMZ6j9UPSolidRjR7WDu7gJWkYot8TFggfPA1fygI,1335 +torch/_streambase.py,sha256=NAKZfV-BhZ22Jyt_3VQDH1m4QQdIVc4G8VvUBkZ9wJ8,435 +torch/_strobelight/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/_strobelight/cli_function_profiler.py,sha256=m1qh_sKvsmAolvN-BJio3wfV7iwksmSq6TVf6Fdpf48,11768 +torch/_strobelight/compile_time_profiler.py,sha256=sV7yywRZTrP3Jh59YJldORLypMapgiUm2P5gN1ZYouc,7524 +torch/_subclasses/__init__.py,sha256=7NDSoV4xw-GxCRtHasX_MFFLe_S1-1BHRFvoDcgE4J0,375 +torch/_subclasses/_fake_tensor_utils.py,sha256=NWdbxrzEAkVz6o7KJPa5K-v4ohxeiIwV4kmG7tDnHsM,8845 +torch/_subclasses/fake_impls.py,sha256=R_ijzwoxHzttRfD2yYuT2mPEUBlrKx7HGlqZb830aRY,37053 +torch/_subclasses/fake_tensor.py,sha256=fBtoZylWuifSPNQHvHl3qpep0AqnkbmKzkMRwrh71iw,129522 +torch/_subclasses/fake_utils.py,sha256=ay0yiIFtRbR3antGkmXliMDN1jXK6cSG_G4GBObiMPk,10283 +torch/_subclasses/functional_tensor.py,sha256=AmbKdUsCM5WFrpNIlo6tBq6OB6Ex-ZcOMtvKZdEIh2E,34271 +torch/_subclasses/meta_utils.py,sha256=21aNmpI-g7jyAbUM9mLipJL_DZdBQ3dNRMf4-P6kjv0,87065 +torch/_subclasses/schema_check_mode.py,sha256=uGnw3Q2KcryMZRmUAGbTEbhfXWRRl3VVboI2HT1o4Fk,8639 +torch/_tensor.py,sha256=mRWJ1goMF3l3oX7NWm9LgA-pQpAM0R4gIf3kSYXGljc,72764 +torch/_tensor_docs.py,sha256=5bwJ1DvCam1nLuGnPknjkSYrtAzi_Kq3V7Q4MQKN0SI,144150 +torch/_tensor_str.py,sha256=D3VCi5Clp3us1UH002zeUbbAkf5sjd5eVhYAvjUT-f0,28513 +torch/_thread_safe_fork.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/_torch_docs.py,sha256=SgQJPNCP8ob0_xToGwwZ1WI-nzb7YEppRUs0m-V9Zr4,426370 +torch/_utils.py,sha256=Ev9-YfGDOKhcuh3phl_UblCYTefN63A-18xvOu2SZr0,40418 +torch/_utils_internal.py,sha256=L2WG7_cXeIhMjpjTsm-_5xYNnDrxL727uTXkbG9Uc2U,8693 +torch/_vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/_vendor/packaging/__init__.py,sha256=EhCMuCSz60IgQJ93b_4wJyAoHpU9J-uddG4QaMT0Pu4,496 +torch/_vendor/packaging/_structures.py,sha256=q3eVNmbWJGG_S0Dit_S3Ao8qQqz_5PYTXFAKBZe5yr4,1431 +torch/_vendor/packaging/version.py,sha256=XjRBLNK17UMDgLeP8UHnqwiY3TdSi03xFQURtec211A,16236 +torch/_vmap_internals.py,sha256=oym7gxqPd_2aK-I2WlI4VsghS7pvK1dAyS82vpJZIN4,9442 +torch/_weights_only_unpickler.py,sha256=vC8P3LVlcQnQZpUdWIZOxCJ8j2XOTR8UQBUGLvCX5LI,22214 +torch/accelerator/__init__.py,sha256=n1B8AWmRziAafwqx9iSL9LFPvien5fZWJbbeo0F31fk,8345 +torch/accelerator/_utils.py,sha256=eoJCNwVy31085_36y6a6sTz6EfHD_6Zi77eBaGRvRHU,968 +torch/amp/__init__.py,sha256=OuEdgK_zzuRWo5_vfM_VhTooVAm1BXTBkUWxLkY6zlU,181 +torch/amp/autocast_mode.py,sha256=fH_iCiqDfbolD6xYM9jmV-C7_BL1v5IA9f4WDb1xuew,25000 +torch/amp/grad_scaler.py,sha256=U72zAMUDDF6tKcimCleNz_MJd34AzRuXQgUwkY9s5b0,30563 +torch/ao/__init__.py,sha256=t-7QOhmWP9oe0Mh2RHAH119yFNo-jVdfOB6VIZyYS5M,678 +torch/ao/nn/__init__.py,sha256=4j-eekD5zpSPgRw4Z-6hVhWmeeYaDcD9bar24mvxbVo,834 +torch/ao/nn/intrinsic/__init__.py,sha256=yaXY9qRdzLDsAk2wwLcm5x2PKvvwEi9DLbuDUcPYZeA,961 +torch/ao/nn/intrinsic/modules/__init__.py,sha256=FlHZWKH0Xumwd5xBvZgyeGkbYehn1oDCUnAqZ_c4WN4,655 +torch/ao/nn/intrinsic/modules/fused.py,sha256=zD5iJbUTl2tNt-0_JEOZxqIbvtzQeD_wjEwp3ZIQfJY,10317 +torch/ao/nn/intrinsic/qat/__init__.py,sha256=M0iylhjuqtPcbO2pAVdDg9n9e1ET359nQ9txOEErmMo,37 +torch/ao/nn/intrinsic/qat/modules/__init__.py,sha256=OIDHW4Q4Mjd_Y_eR1UWDLuPOVEIJOJJj5MbbaQRdI1A,547 +torch/ao/nn/intrinsic/qat/modules/conv_fused.py,sha256=8NzW7ZXTxOekeSKXsw3xxlqQGPBiqhvAOAdRDOaVSIo,32948 +torch/ao/nn/intrinsic/qat/modules/linear_fused.py,sha256=5j4oI6lVzPvTnmDAIyVgB4lkQmEHmPxiRtLv-JG1Z6g,6613 +torch/ao/nn/intrinsic/qat/modules/linear_relu.py,sha256=Ea08l1hZfvGf5Hht3VzT64NRwEHqMNjzmNYYB4vOB2o,1686 +torch/ao/nn/intrinsic/quantized/__init__.py,sha256=AyOx7RavGaehtAWX6dEUBQcrl23h2_S3Q9ZAWYimCLQ,236 +torch/ao/nn/intrinsic/quantized/dynamic/__init__.py,sha256=M0iylhjuqtPcbO2pAVdDg9n9e1ET359nQ9txOEErmMo,37 +torch/ao/nn/intrinsic/quantized/dynamic/modules/__init__.py,sha256=kbhh2dL33XSuVEAZNtSHMmt1papX7az6WlVALevMgZA,70 +torch/ao/nn/intrinsic/quantized/dynamic/modules/linear_relu.py,sha256=ePz_bRtW9rXxCA9IBHIkOWixoOWLslMeN6blZF95pYM,2027 +torch/ao/nn/intrinsic/quantized/modules/__init__.py,sha256=8aofpYeXIA8WQaao5pvOjWtLnf4rmL9xlJO9hZ8aras,409 +torch/ao/nn/intrinsic/quantized/modules/bn_relu.py,sha256=kh0yWikPYFulF-ijwFaJiDKB8uCcry3e0-tH0nvPaGE,3285 +torch/ao/nn/intrinsic/quantized/modules/conv_add.py,sha256=bFesZW8PT0zRp1aXUBLUHiUnx-bM9uktoPBKVb8meLU,4434 +torch/ao/nn/intrinsic/quantized/modules/conv_relu.py,sha256=jrIwRspLd-Q4MY2-dJJ6SfsYfFyb-6o6vibCsAOf9Nc,8451 +torch/ao/nn/intrinsic/quantized/modules/linear_relu.py,sha256=HNLFF5MHbtyyKxiuiOdrNKwl-P4iTG13j4AOCLp8OAs,6884 +torch/ao/nn/qat/__init__.py,sha256=M0iylhjuqtPcbO2pAVdDg9n9e1ET359nQ9txOEErmMo,37 +torch/ao/nn/qat/dynamic/__init__.py,sha256=M0iylhjuqtPcbO2pAVdDg9n9e1ET359nQ9txOEErmMo,37 +torch/ao/nn/qat/dynamic/modules/__init__.py,sha256=_5hfV0E5b71TgYVQPrPF9QZNrnw5XPuZppEwj0QItaM,50 +torch/ao/nn/qat/dynamic/modules/linear.py,sha256=3OCxBs80zVGY7Nz3Fc9rTdoDCrpnLHqltqWMoIQUGkM,1248 +torch/ao/nn/qat/modules/__init__.py,sha256=RmKZ7d0ds96EIRSqAGGTrDwuXVWerE_S_NCWuEuX_20,228 +torch/ao/nn/qat/modules/conv.py,sha256=LklOGy8PUx9Wp9bFLCsmuLNSzoS3rsxOZZO6h1Xy6eU,9577 +torch/ao/nn/qat/modules/embedding_ops.py,sha256=h-bMQFBvrutOSpeTAKrRB8trbtoPoG_vZFqwIlVeYpM,7817 +torch/ao/nn/qat/modules/linear.py,sha256=CIFCeilg3xlf81CNsj1pSvTcndokSjEHnPVpKhhVG1M,3051 +torch/ao/nn/quantizable/__init__.py,sha256=M0iylhjuqtPcbO2pAVdDg9n9e1ET359nQ9txOEErmMo,37 +torch/ao/nn/quantizable/modules/__init__.py,sha256=N6niR-YsfqqP_5a8ksCJ8BL-Ol1Mch_412g-I4Z8VeQ,145 +torch/ao/nn/quantizable/modules/activation.py,sha256=7HXnlWr45UHF8GmdwCASj1_aeVR9PG2IwBgJ8qTWkcg,23048 +torch/ao/nn/quantizable/modules/rnn.py,sha256=fyu58jBCcUzwA4FuLN_FHRGB3sgIVeTjeFFd4_Boh6M,21607 +torch/ao/nn/quantized/__init__.py,sha256=NtHecsYVtg9e1IhTCdvoXSV-oQEXRQnt54dZlScPo6I,686 +torch/ao/nn/quantized/dynamic/__init__.py,sha256=M0iylhjuqtPcbO2pAVdDg9n9e1ET359nQ9txOEErmMo,37 +torch/ao/nn/quantized/dynamic/modules/__init__.py,sha256=VzgogVUFAj-yE8a5Vmjiq5TV3SP51t0wXslabLVPITw,413 +torch/ao/nn/quantized/dynamic/modules/conv.py,sha256=1xvJZgQmYmX3GHmW-XGnBVmpTEzSr_E6vUM-B6b-DIY,18190 +torch/ao/nn/quantized/dynamic/modules/linear.py,sha256=xpWRFT2QVFpnNb47Psq7GZgqinj3S6G5oJY78dBPOsg,6350 +torch/ao/nn/quantized/dynamic/modules/rnn.py,sha256=_fxydzY3ZcgCIMIuelfyb8W8mbmovPeGkk53J_GVhtY,51348 +torch/ao/nn/quantized/functional.py,sha256=KUjrWQvOwJVI8wb5e5FMK5-XyeqHskgt1778wOjdKWU,29589 +torch/ao/nn/quantized/modules/__init__.py,sha256=vFYtEc5icLCdS6a_I7dJKANpFSmowEehAdgKXrYxX60,4521 +torch/ao/nn/quantized/modules/activation.py,sha256=WglypS9WTWkxl2aIF3iOCEvLrJDGC4e1HawnKSfasfo,11608 +torch/ao/nn/quantized/modules/batchnorm.py,sha256=7kwKVDBs0YdqAPwoHZUY4pPyl1TfDbpVSX8b9AzzIZk,4427 +torch/ao/nn/quantized/modules/conv.py,sha256=WrZguqYhwd654OosNTMHJtSRvsn5abZ8pBmf3cjqZLY,43414 +torch/ao/nn/quantized/modules/dropout.py,sha256=5nPYMBmjOyEzOZj0lz_CtUmJASXrrEr63YJnVytPTQs,806 +torch/ao/nn/quantized/modules/embedding_ops.py,sha256=1f6F7nc1GxO7u15UvcA1ZSW4ojQa6PTXhPSbhDCAsKw,14676 +torch/ao/nn/quantized/modules/functional_modules.py,sha256=0ExsuejwUYr1DIFeiWhhbWyCG5Dqks4x3fs4YIYP15I,9222 +torch/ao/nn/quantized/modules/linear.py,sha256=K63yGb4d4E-6YAobJPMfSLn5kan7HefLtQdf-cPaAP0,13609 +torch/ao/nn/quantized/modules/normalization.py,sha256=zFTgY6ABpezWNjWEoHnl09qgLGlNJVrxxznhRecYP5g,9539 +torch/ao/nn/quantized/modules/rnn.py,sha256=qq-6ce4d9f2lzygMwo0xWNG4vKgqk1orvIUtB5E-oDk,1822 +torch/ao/nn/quantized/modules/utils.py,sha256=rB3XTOd-17-iDGSmTUqcM-gd913629uTTjwdjAPIy64,4695 +torch/ao/nn/quantized/reference/__init__.py,sha256=hu3hyozMJtkc86Bu1v8tmyayuxAdeDSv0sUiOeNAr_s,284 +torch/ao/nn/quantized/reference/modules/__init__.py,sha256=YS8MYKlqBPoA2UEBcS-MgbOzZ8_HDp-7ET4BC7KtCBk,494 +torch/ao/nn/quantized/reference/modules/conv.py,sha256=VU7nL-d_SxFfClD8dXkU1KfDlGDpjvLEmNFuFo5e0Ww,15389 +torch/ao/nn/quantized/reference/modules/linear.py,sha256=npxOcx2NyLZjfG3am6AjjGp5xQ5DJaRBq-uRUojv0QU,2273 +torch/ao/nn/quantized/reference/modules/rnn.py,sha256=fbHzajlfmvXcQCZX7MRiRRkGhy_VkZoSLXUcn2Mqo_o,29682 +torch/ao/nn/quantized/reference/modules/sparse.py,sha256=NNTJW2kSI9Gt_cZmFVvmhj-k-nzCV7oJkKMOWDg3INs,4673 +torch/ao/nn/quantized/reference/modules/utils.py,sha256=7gt7nTbNIXGITZ181nFDVnbbxeLFoHFym1oPCv8-3cQ,15346 +torch/ao/nn/sparse/__init__.py,sha256=PfB-tgPOelyV_0eb_ipJK4LzPHwB5Z-wfJXeE_O3AK4,24 +torch/ao/nn/sparse/quantized/__init__.py,sha256=xO8RdXEcj7ggZh7-f2bReVBRO-F6PNBZjb5DBPwV52w,168 +torch/ao/nn/sparse/quantized/dynamic/__init__.py,sha256=lYDGtZ8rNR56jJLk6s5WOJ65E7qqQ_LFICpuD76NWYI,57 +torch/ao/nn/sparse/quantized/dynamic/linear.py,sha256=Yf1xFYVXeDkxiPy8-qPgDNzTrF8RAGN1lFP4greR05k,6328 +torch/ao/nn/sparse/quantized/linear.py,sha256=jdiSeEoAEc1XnZOvghP1VDXUQzMjCYTTwQZUshpcMCg,9017 +torch/ao/nn/sparse/quantized/utils.py,sha256=cAn7Xj5gmammzelwBCFjEbvtwuLjFKwINcHrQ-eIQas,2080 +torch/ao/ns/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/ao/ns/_numeric_suite.py,sha256=ywXFN-jxdmwJqkyDxyOPjq4MPb7g2Gu6EH37KaAvNAw,20076 +torch/ao/ns/_numeric_suite_fx.py,sha256=sQGBVaQ4OReR0zCuHSQGAdLXkL69e15-H_CnCn4MTSA,41358 +torch/ao/ns/fx/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/ao/ns/fx/graph_matcher.py,sha256=86FciRIE1hJN-oAaVfzOglOF8orm3pquIyNPZxPbfTg,19278 +torch/ao/ns/fx/graph_passes.py,sha256=iRR_9dt-A0fgvejdvmdgCxQWTwS61Q6ymmWF1tj0YA8,44351 +torch/ao/ns/fx/mappings.py,sha256=zrnfnskAaE0miAnyuV8H62K0-zP1JxcF_5LtxB09VbU,18300 +torch/ao/ns/fx/n_shadows_utils.py,sha256=9fx1t-cnKbUY20FmfvLBgb57OG05hHyA6UzDnC5c5_M,51181 +torch/ao/ns/fx/ns_types.py,sha256=4jCJmx87h4O3Ks3IYLVzsN783fN27T1n0aN-KI_ia1g,2327 +torch/ao/ns/fx/pattern_utils.py,sha256=x-kvuSk2FoyhtKOGmp-nfrSQkIYWsMwWSoxgCbMiR-4,8369 +torch/ao/ns/fx/qconfig_multi_mapping.py,sha256=Zvs9CeYlm8xnUQDFiWkZipMRmyXiUd3aZEXjC7RCn4Q,10183 +torch/ao/ns/fx/utils.py,sha256=loKOpn-n-XGrnpFpA42xrH1Igk6mj6nuHSOlPqrTc44,20643 +torch/ao/ns/fx/weight_utils.py,sha256=zvdbmn3Eqki48r-dhJ2LDC-BGu9nS5P_gMeDCo4bXbI,11382 +torch/ao/pruning/__init__.py,sha256=dp2CA7CO_FRHTE7E0Ft495uR5IlUi8HsnG8ujLsz6OA,640 +torch/ao/pruning/_experimental/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/ao/pruning/_experimental/activation_sparsifier/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/ao/pruning/_experimental/activation_sparsifier/activation_sparsifier.py,sha256=QHpBW5d4c1PVCJvl2-RDpCazCbmmrOcw7wQPg-Vp80s,19072 +torch/ao/pruning/_experimental/data_scheduler/__init__.py,sha256=q_95mAMpHldGWwLAgYS-F07ReGKDLIAZLsP7BtlmgTE,92 +torch/ao/pruning/_experimental/data_scheduler/base_data_scheduler.py,sha256=6to1j21zeOVaMEIsJ9tw5rWY4dpaFdZJNHOX7anzrnc,7665 +torch/ao/pruning/_experimental/data_sparsifier/__init__.py,sha256=9ktAif-dttGBmiw745tN2WtUTOTZch0E_HA4cE1bfTo,174 +torch/ao/pruning/_experimental/data_sparsifier/base_data_sparsifier.py,sha256=Lb4_2NYb-D1MAxsAsiRGpFt84WdSVVfbhmS-ZlZuVK0,13435 +torch/ao/pruning/_experimental/data_sparsifier/data_norm_sparsifier.py,sha256=T839Uyf4FaQziRM3GNWz7rCRCNKk9HoOQwnx1dP5TzY,7760 +torch/ao/pruning/_experimental/data_sparsifier/lightning/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/ao/pruning/_experimental/data_sparsifier/lightning/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/ao/pruning/_experimental/data_sparsifier/lightning/callbacks/_data_sparstity_utils.py,sha256=Vtb__BJ_qrCy7TBisG4BM8qNXzDYrl-xNDVLbHCsYIY,1636 +torch/ao/pruning/_experimental/data_sparsifier/lightning/callbacks/data_sparsity.py,sha256=Xg-aSdgp4OMobKjuqdHxBBjNQ8HhStzTmVgt3kCaCxk,6611 +torch/ao/pruning/_experimental/data_sparsifier/quantization_utils.py,sha256=KdjuQi6xCbg_U4kfkxZnGTYi-v8WajEUIcCbNRYZgY4,5890 +torch/ao/pruning/_experimental/pruner/FPGM_pruner.py,sha256=iKKXlb7xcywo5B_YQXpUr2RL0vyYq6ZMIDIk3xH4KyA,3456 +torch/ao/pruning/_experimental/pruner/__init__.py,sha256=abfUoG48Kc6ddQZmFAkYjQH5J8Hae8xJiqxz89MTgDE,260 +torch/ao/pruning/_experimental/pruner/base_structured_sparsifier.py,sha256=3ahDrEwHT6aoE3w9YiDqM-pvjxzy4Ml-j2DPY8RmAJM,10936 +torch/ao/pruning/_experimental/pruner/lstm_saliency_pruner.py,sha256=xbNRyMqNQzHlfDa-P1mp0ACXvCw4eIUb3_fKIEQmG30,2141 +torch/ao/pruning/_experimental/pruner/match_utils.py,sha256=smDy5_4xNYEIgaawVAvo6w9pP5ro_fTasnZROh6Rszc,1981 +torch/ao/pruning/_experimental/pruner/parametrization.py,sha256=2B0P-ow2zk2ITLq4xutBIG9taMUzNFpi_qjet5knIxo,1844 +torch/ao/pruning/_experimental/pruner/prune_functions.py,sha256=xh7CoBAgO4uKZFBaVwSGHvZA7EgXxW-EkNh-yAIZudI,19095 +torch/ao/pruning/_experimental/pruner/saliency_pruner.py,sha256=i60set06ICiSY3_G3_4NErJ0hHtwCSYDEHeZK6zc-vs,1400 +torch/ao/pruning/_mappings.py,sha256=O9aJePj2X0nV76KTcSU93V7GA8dxEzfr5G9QvjyWOqQ,597 +torch/ao/pruning/scheduler/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/ao/pruning/scheduler/base_scheduler.py,sha256=RpKDJ5BGMs1ecYpQvGEVQC3PtwCh8glq--1_PNt5NWg,6526 +torch/ao/pruning/scheduler/cubic_scheduler.py,sha256=CVOqMeHZKtxMD7lMfjDzzEzMpqTUWOTKdcKix_2PuZQ,3844 +torch/ao/pruning/scheduler/lambda_scheduler.py,sha256=nmRBFi65-H1nBowfTqSvfuk_F_cyOVbK02LaQxFSHTc,2114 +torch/ao/pruning/sparsifier/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/ao/pruning/sparsifier/base_sparsifier.py,sha256=ZP-kYdz2kP215UF40qNrzV18opECkzyOH---KE8Ro_M,13732 +torch/ao/pruning/sparsifier/nearly_diagonal_sparsifier.py,sha256=pqSSuwArujZqSOhmv9KeWoD4R1jeEiTvLWrvjVdcM3Y,2266 +torch/ao/pruning/sparsifier/utils.py,sha256=xdETLM3IkBlz7k-2l64L7nsyhLm_R19T0J_qgGVu2jk,4802 +torch/ao/pruning/sparsifier/weight_norm_sparsifier.py,sha256=bBJyknxvI91cEcTObIv_McusSU5qec149NSWoDUU7zI,9296 +torch/ao/quantization/__init__.py,sha256=Z_H421OWfbOHQ5TKfR6CP4xUICz--znJ7fCHzzRz3yc,7319 +torch/ao/quantization/_correct_bias.py,sha256=isUcDW470Zcc9Fi1w2Z9oYCkfIBM-8kWA6NWMlQcpk0,5427 +torch/ao/quantization/_equalize.py,sha256=_t1NUUCJU5Tsgcye6LMkcarYkQe4GZhCizlTsNGzmiY,9468 +torch/ao/quantization/_learnable_fake_quantize.py,sha256=bChZB9Xh-leqMW3z2wLz2-NO4_sezzCtV2anOwWkInw,7910 +torch/ao/quantization/backend_config/__init__.py,sha256=lnyxe_DTaYkdoSLhxNlUcMPlck2SAngmI8GNcYDHI4M,915 +torch/ao/quantization/backend_config/_common_operator_config_utils.py,sha256=KrCZPjelyASpLyNu0rIsabgKmDu_AzlJOTd7MvyPg3o,27512 +torch/ao/quantization/backend_config/_qnnpack_pt2e.py,sha256=Z0CgzNQw0gA1n96DRblor08FXUtWpEoVf7vYb9IEx5c,6431 +torch/ao/quantization/backend_config/backend_config.py,sha256=AFXRN-LCQpqWa1r5zARGYQdGth5pChpdkHJ3nXbOqZw,31661 +torch/ao/quantization/backend_config/executorch.py,sha256=t72escVnr3ztTYDpiyHIJlOWTwHgE1m4wn6fziIxyP4,16924 +torch/ao/quantization/backend_config/fbgemm.py,sha256=dCsGDn-PbASye1CPf_9poNM6eOMN10_NUFTQat95-0Q,4208 +torch/ao/quantization/backend_config/native.py,sha256=xKBjjC-uZ8qZsbHluftqC6Tn9vo_6ivcsVYFWLFFQtQ,8242 +torch/ao/quantization/backend_config/observation_type.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/ao/quantization/backend_config/onednn.py,sha256=5bL1hZJPwKJjnTVtPeONE4RTVpfTKRQ-NsT1tQJokhc,19083 +torch/ao/quantization/backend_config/qnnpack.py,sha256=PkPTzCS-Q3p8MiUq6mytJlFt-Zox7AO2kxbTg5Qx-AU,5400 +torch/ao/quantization/backend_config/tensorrt.py,sha256=Drb7vD3gWVGnSa41C6rYVR7po0qA420Nu3hoOH483-A,3021 +torch/ao/quantization/backend_config/utils.py,sha256=-g1HkHsVuAKc8wTXXbiKfZzAPnVxVPDhparV9VlIE6Q,12433 +torch/ao/quantization/backend_config/x86.py,sha256=4geWLr6mkFhSEYiXFXpkB8NegS05EXhv2fYHTpajNyg,3869 +torch/ao/quantization/fake_quantize.py,sha256=ymwXuOkDpenqhCEpO1tK15TRwj_RcLe7OW38LoF_DEE,22887 +torch/ao/quantization/fuse_modules.py,sha256=eF0N3kMbbbrFnwIsbuHZC2pMnCCLbbyPVR2nofVCScc,6825 +torch/ao/quantization/fuser_method_mappings.py,sha256=dWG1SKCjzm4eQgkCH5Q69BFEU0yiPmkLOctFuOg_eB8,10375 +torch/ao/quantization/fx/__init__.py,sha256=65h6iR_5XARcYpGS8A2qRza_2y7cvqCowqkwwziYz8M,81 +torch/ao/quantization/fx/_decomposed.py,sha256=HmoHy9EnUqdbIG-_Jziff1gqaYk8_6fB91WLfnlUYrw,42445 +torch/ao/quantization/fx/_equalize.py,sha256=3hpLzKZbAAq0hnfOn-Ak9PXl2cZwEjhw85eahRnNboI,37917 +torch/ao/quantization/fx/_lower_to_native_backend.py,sha256=IHMoi8T5CTzy7oiNY21fvcY5rlBgzRoCeMsxe6P6_DY,54631 +torch/ao/quantization/fx/_model_report/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/ao/quantization/fx/_model_report/detector.py,sha256=3N-xbeSzQFn8ZKPyq8NOi5oDtkGIaQ7FDzoeGEOGVQk,76399 +torch/ao/quantization/fx/_model_report/model_report.py,sha256=waptJtg93duOo1sXXPB8-3PD3MkF77ZISe0JuIPIj6I,29641 +torch/ao/quantization/fx/_model_report/model_report_observer.py,sha256=BWuxgjbh5Egg1xxq9eIQt3v8gDbxM28AQmhG0N1jUG0,12084 +torch/ao/quantization/fx/_model_report/model_report_visualizer.py,sha256=Jj2VQY_MVlRoxTqB0fN4P6EiAxzvkBv5y3ncZhljnVQ,32548 +torch/ao/quantization/fx/convert.py,sha256=y4Y_Zbz-EkWdnBFQnvnbL1apkuKQt_EaSztxpd9irc8,57746 +torch/ao/quantization/fx/custom_config.py,sha256=Vt_k9kuU5TNi4B7RS2W9tqzdBdRJi5qaAutVWqbUyTQ,21861 +torch/ao/quantization/fx/fuse.py,sha256=4udxFNB5hnFAnx11M4qaolqlN0lAOODVOfwJLDDNj3U,7256 +torch/ao/quantization/fx/fuse_handler.py,sha256=gqjn9iXCxHgMXUZGHsAXNRodXfNkxBqtr880bcigro4,4645 +torch/ao/quantization/fx/graph_module.py,sha256=-_tD59DgLcQIOYRhBQuoLSDDhD-DqzqLmUro9rLYkPY,6644 +torch/ao/quantization/fx/lower_to_fbgemm.py,sha256=ERyoustxB5yjBVinb4V2_YTZF-76c7NaPxaVP2l_Pbs,602 +torch/ao/quantization/fx/lower_to_qnnpack.py,sha256=pSLRhkHuxXdbu7a1yEzQPbomFrepOvfiKWpg4HNqEYw,527 +torch/ao/quantization/fx/lstm_utils.py,sha256=7p2PP2iCVoRS7j-PJT8U0yWyv4JGbL9OzZ2Vkb6TbXk,10316 +torch/ao/quantization/fx/match_utils.py,sha256=bNf_O-frv4PaY8jssv8HBilm-rYBtHZwmUi78i-JApc,8860 +torch/ao/quantization/fx/pattern_utils.py,sha256=aMM87O63tKrgnLtuAIPEPNXApbzTxL4SIOvyWZTofJI,3668 +torch/ao/quantization/fx/prepare.py,sha256=F9CHriubGC-r5U5E-s6Kr7mHJOfNUKY9M_l1ePrgDB4,87550 +torch/ao/quantization/fx/qconfig_mapping_utils.py,sha256=ZSElCyE951pr6Ochp8ZIcd20CE1SLKxhSHrbT2DzcuU,15359 +torch/ao/quantization/fx/quantize_handler.py,sha256=l4LsSi63WSUA6h89HwmFnelNB2s3WH6ZPGHl5pwWHJ0,7280 +torch/ao/quantization/fx/tracer.py,sha256=HggftehBqetZQJkH9Ix5ZzqH5IQoAwUn_0xx-5W-uAw,1688 +torch/ao/quantization/fx/utils.py,sha256=W0SqDUah2Sm17qnq987XLz9mX0plXyzbqwnLwVBSUL8,37615 +torch/ao/quantization/observer.py,sha256=t3tZp2Zf6221TjCbXsW6w-uHIP0QWHNKhuyLyDVNF44,79254 +torch/ao/quantization/pt2e/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/ao/quantization/pt2e/_affine_quantization.py,sha256=1YMFVWlHvyA6hZfxzDMwusO8N70bIpkc-anwYgGbvWM,34025 +torch/ao/quantization/pt2e/_numeric_debugger.py,sha256=RkN7oWTpBQgg-CRIb8dWq6niGuVfVHmk50zmuNuqVCQ,12134 +torch/ao/quantization/pt2e/duplicate_dq_pass.py,sha256=gYwZmEb4mI3UFU85nPASsM4QB1BlW6D5AvjYE5B8POc,3148 +torch/ao/quantization/pt2e/export_utils.py,sha256=nBVXF57e6m9MRcv_WDquLH9Cb8Ff4epBm8o8ApZQDBc,7990 +torch/ao/quantization/pt2e/graph_utils.py,sha256=4DiFFio55OK_nWxyBTcycZLnji4JAkkM6hZTqPK7YYw,6385 +torch/ao/quantization/pt2e/lowering.py,sha256=J8MKQtJuXaTqBFQV4XCb1qf8rVXY67N4jlwGrMHtmuY,1904 +torch/ao/quantization/pt2e/port_metadata_pass.py,sha256=hJjP05Eq9D1s8QJ-B3E3XpYjXy9-hDPcp8qyP5pjLsE,9219 +torch/ao/quantization/pt2e/prepare.py,sha256=VOeINUHglvJ4GXJhn65_hxXYU2lI_szd5HieQ3QoWTY,21552 +torch/ao/quantization/pt2e/qat_utils.py,sha256=demZeZkvvfv9OLrjxnGGH0VZI9oXrO2zM0sRmw8S_RA,36686 +torch/ao/quantization/pt2e/representation/__init__.py,sha256=Srf_T8fMTpFi64ZAgQom0S1A4CLFfKrtwPB_edoMu78,110 +torch/ao/quantization/pt2e/representation/rewrite.py,sha256=W12U4HokE15bjGX2eMEdDYUkXQAQaXLOFX447mUi6js,28375 +torch/ao/quantization/pt2e/utils.py,sha256=1yQDUPyX_ZhqfUHIRJZjKhzEm3e5KLs6v7wj-EtdSq0,23235 +torch/ao/quantization/qconfig.py,sha256=nbpSZ6qzyo8eB9T_AMUUCQJ9I9Dvqkc09ETCWGm0l58,24337 +torch/ao/quantization/qconfig_mapping.py,sha256=r-0wurGrVrNv4x-byQdXRwWRQ80LWDhqo0JxuvemDOo,14811 +torch/ao/quantization/quant_type.py,sha256=fY7JDEr_PzBFymxy9fQmnRIFKiQoL2QoKJtxZKSJsoc,760 +torch/ao/quantization/quantization_mappings.py,sha256=4l56xG1ma82nOPOYbfZAB0UO093H_4RGUgs0yz3UZ8w,13816 +torch/ao/quantization/quantize.py,sha256=kdXnLezgitSU7zq901mu1JHyUDvunpXwzlbiAZNWMP4,31011 +torch/ao/quantization/quantize_fx.py,sha256=iHxIfGPAwQGeXBTRBVTfzPw7cN-kFsHOYh2lfnUNJRs,32653 +torch/ao/quantization/quantize_jit.py,sha256=-85YvYHcqghAYpa3R0B9xvwEXMsWYsqOxLA4CECHF2s,14595 +torch/ao/quantization/quantize_pt2e.py,sha256=7Z-bsfDTrEr_Di-XQwmKyTt9JWEPCLx85JERXJ-T1_Q,9473 +torch/ao/quantization/quantizer/__init__.py,sha256=JCNfOoUz3U_LyPipiVeO4o0LRncgqc6kiuv5lAF2i7E,455 +torch/ao/quantization/quantizer/composable_quantizer.py,sha256=CJJ-OZqCH1nlBqnGb5UpH7WzssUJxCWPkhHvRdCBRUw,3012 +torch/ao/quantization/quantizer/embedding_quantizer.py,sha256=R6DBi866ecgApuPjnSXB82jirbRDMGW4uPMKKwITGFg,3457 +torch/ao/quantization/quantizer/quantizer.py,sha256=LYzYWiOLirH58coCuh3SbpsL4fR4VDpQLML6cVz9dXs,6624 +torch/ao/quantization/quantizer/utils.py,sha256=osZW9GUBREI0uDTSu-JRMYYQr3AaFBRbfreDo_tJuQk,3225 +torch/ao/quantization/quantizer/x86_inductor_quantizer.py,sha256=-lEB1pPlPVM24Rhfxd0S_y15_pxrD3KDT84bFhTSgW0,64874 +torch/ao/quantization/quantizer/xnnpack_quantizer.py,sha256=ozIEuupBNwxEqW_eaU9q5YrHO5VxvsJOfPQrBpFSqDE,16310 +torch/ao/quantization/quantizer/xnnpack_quantizer_utils.py,sha256=oipORA61ruWh6ADUf8EJDNPm-XPlzu45RVi2OzeM21g,40659 +torch/ao/quantization/quantizer/xpu_inductor_quantizer.py,sha256=UuDhZvnI8DrDnRUXIB2biRpTwCaRR1I_jTRbEmb4RSM,3875 +torch/ao/quantization/stubs.py,sha256=8Cd1FkoZaGQF0cLqksc090z-1_vjCYVQcS4yx7mlNQ0,2298 +torch/ao/quantization/utils.py,sha256=DlpYKpIQkC4Dj-WaIKX6kW8Kcb9WKmv_XSgbq-1W3eE,28921 +torch/autograd/__init__.py,sha256=BDkWemqKTFD3NFfJlhaYYREJY4obFG8KCE-h7jglJz0,25424 +torch/autograd/_functions/__init__.py,sha256=sdKJj6Dia1vNRSolyufzRKdn5qHkgCBCUI3OBm-PGW0,36 +torch/autograd/_functions/tensor.py,sha256=MUl9QrV_oOqb-BwoC-P9-a96ho-hFrJgtfubdfW75lY,2203 +torch/autograd/_functions/utils.py,sha256=cZcH1G8uklFUCRik7DgOCMzo3EivPyJodYrEiX7pe1U,2017 +torch/autograd/anomaly_mode.py,sha256=4yheU1PJy-LWTzYjZjPgCCH52tBfyW78C-F6xqmN_8U,4951 +torch/autograd/forward_ad.py,sha256=y_feGwYUufUTonWXSz1rmGOHZ3AQ-CTGkJqDYT7T7lA,7639 +torch/autograd/function.py,sha256=dCNUkD_KJvX3H_YNhw-7nR8X4DcH_rViAQrzL_JPeqk,33147 +torch/autograd/functional.py,sha256=9h8NeKipKuEMD_I0_jGCkaPMnsuJF9GJtFtgl2_HFDE,52600 +torch/autograd/grad_mode.py,sha256=E0fUE-6Od1OowMDMr_xmSWORzYYImR9fLVUdD_KvFtk,13318 +torch/autograd/gradcheck.py,sha256=3bFb5FWEHy8Hl6fosEXR-RWWBG18dDPFIqsB58Ayslk,90560 +torch/autograd/graph.py,sha256=7gTbQNwYK56fIl3aUqFL-SzNvO-AIRN8tECa7D1w1AU,30452 +torch/autograd/profiler.py,sha256=33bcx7J8MxWUDgNceS9EgrCi6LQxM0dv7WUDOAs_EPA,48935 +torch/autograd/profiler_legacy.py,sha256=nyXuIza0goFoREWdAvznMlF-EDUvlRbQREY3mQox-dY,11506 +torch/autograd/profiler_util.py,sha256=oY1zQNs3bejuBX715zqwZSOn5caQMdVWOtP1KXZKqug,41359 +torch/autograd/variable.py,sha256=N0cAiO8ZPZ6rtRtU-hJYH_6NVt-zun16CmO_KrZV7-0,391 +torch/backends/__init__.py,sha256=8fAlF9QfMBD2V9XgyXoopPDrjzZMkBhyCkv6N_Sf6tY,1777 +torch/backends/_coreml/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/backends/_coreml/preprocess.py,sha256=8m0YM0N1e3tLcaKQXoKXyN1VxlTtlrDI7ohke7J1Z9M,4301 +torch/backends/_nnapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/backends/_nnapi/prepare.py,sha256=PjHMOcCiTEvZyevQBsdWBKOarzAXEujzJ8YfMqO1OD8,6559 +torch/backends/_nnapi/serializer.py,sha256=NE3SHEYoVFoDLm8SnXhbcqDiX9jRL_yMMgfiI8V1N14,82895 +torch/backends/cpu/__init__.py,sha256=V3lRML9bHlNvfE5iOHJP9kN-P4WvEN23SeGQLLDYEnM,314 +torch/backends/cuda/__init__.py,sha256=eu5ZEA4KuNxB1AAoca5iI_cQzaA8rXOX08V-okDd7N0,19087 +torch/backends/cudnn/__init__.py,sha256=bMwIEv7Nb-4-qLEkQ87AuIBqH6Wv7k-Tpq2U2G-kC0s,6603 +torch/backends/cudnn/rnn.py,sha256=FO3LenxuwfEy6KVlQAs8SmAxrOLrMr8kNxJ7PKf2kRE,2061 +torch/backends/cusparselt/__init__.py,sha256=-CuL93luJqK7t-O-uyzn8oNsP8tj3L66cMcztCrlPsE,1246 +torch/backends/kleidiai/__init__.py,sha256=V5pt-IFG6zqGlgzrLQVDOakig9WD1QhZbl1AsfkDgTU,162 +torch/backends/mha/__init__.py,sha256=tUmUrm0Z3I5dnDnhSbuDgZifGDemdgPrdQdsokvVssA,718 +torch/backends/mkl/__init__.py,sha256=IxRqKwR_STL0QASEkBXrVjt-t-mS4SuOZB2ku5eluY8,1782 +torch/backends/mkldnn/__init__.py,sha256=BxeJpP6Fhu3hi1_NWv6Kmf0UF8NC47CabTzm5LnEJnI,3589 +torch/backends/mps/__init__.py,sha256=pu9IZfa4iMvb1wQzb2HcOfVn08dbVQj3OTAw436I2lc,1642 +torch/backends/nnpack/__init__.py,sha256=6cApdM4SVAbns4TuVAuFI9sXskhu7piYVsmP4rqpdOA,837 +torch/backends/openmp/__init__.py,sha256=h6ebEMpGQavTuGZ3eJVK4JMUMMUgTYcrmvHxh_Fcgy8,157 +torch/backends/opt_einsum/__init__.py,sha256=lp1ScWh-VUCGG542BeuPzMCgDspIVwYl4S_dXkDN0F0,3882 +torch/backends/quantized/__init__.py,sha256=vD2Ds81mKtQGrMrAYy1FAo_OPfyzvhDObF276kT_t-U,1861 +torch/backends/xeon/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/backends/xeon/run_cpu.py,sha256=kiTr6w0esy01-_9gVjmh1BeFrVnpUVAqiGNkG8ca5u4,37537 +torch/backends/xnnpack/__init__.py,sha256=BNL7CNyTlaTNxu4JVDE8VZgiu16mRO6JpZeQ0Ns3Nr8,702 +torch/bin/protoc,sha256=rpegwm-ffxXAvOSJ4kDfs8CS2F5nmEDECp3AcuMpHDU,5285920 +torch/bin/protoc-3.13.0.0,sha256=rpegwm-ffxXAvOSJ4kDfs8CS2F5nmEDECp3AcuMpHDU,5285920 +torch/bin/torch_shm_manager,sha256=hSOTyE8b3-G4rNboAsm4riGYa4xu9xLMS-v7uanqzxk,49104 +torch/compiler/__init__.py,sha256=1tWw7DSI43rDABi4sGI0QzwH4i5sQEhMDSe8vNlDLYA,23392 +torch/compiler/_cache.py,sha256=0aMq6ZXifsd1eHx6axa0vQqZnEEBZtKtfxflys5owYg,10911 +torch/compiler/config.py,sha256=ZBKZqrTeVbK7v3H3G6h3ZzpweBQqAYuV3zroXD60sN8,3629 +torch/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/contrib/_tensorboard_vis.py,sha256=NHCnPKg_OGbn9EMjqkTVzALEsShQh2N2-sFsBIDGwEo,5875 +torch/cpu/__init__.py,sha256=_mDbCZX4xP-_d1Vt3HZnpE7FanNArlJGpLV_f4uFkh4,4847 +torch/cpu/amp/__init__.py,sha256=E_HPtKk9IO3AW-gyneT7K5wtjnFZHx9GkvH424D_kb8,72 +torch/cpu/amp/autocast_mode.py,sha256=K5VNaqoZMb_ZvyxVYvqmcddx2FNNXWJ3QWoyrHYYhI0,1521 +torch/cpu/amp/grad_scaler.py,sha256=hT5iBOSV285sW4X7s00Mfg7Pi_eJ-sKCS5BT1qSrz-U,958 +torch/cuda/__init__.py,sha256=0_ZT6T2uFfXI6jz2XmbM4xVtH43eqgAOlwsUwKKXGKg,64719 +torch/cuda/_gpu_trace.py,sha256=cVKCmcRBSOX_-bwYupvFxYon6coU1CWNMDdzET_KWxs,2377 +torch/cuda/_memory_viz.py,sha256=_eiAPHTeTe5qKGGJg3JzJTVs5gPyRGZ48buxsVyAjyA,25848 +torch/cuda/_pin_memory_utils.py,sha256=rXT-CCaJTAVWeArykaJwa7Mf0CarSmdhJoYsqZ8crbE,747 +torch/cuda/_sanitizer.py,sha256=7fWwYLztMnZpuMcj-zRIpg-fZ1kmUAy-b_GCNqzvmPM,24199 +torch/cuda/_utils.py,sha256=xNShxTJNSzSLXfW2GefxRjQqA6-SHpPo0ynzHjDSo68,12463 +torch/cuda/amp/__init__.py,sha256=yWSy9txcjVNn_uuZjKdCodbaY6LgX0hok9LujNSzXUY,267 +torch/cuda/amp/autocast_mode.py,sha256=lQjFh0vpiPbm-7SnM1EwhMGNJs8Xg4F_WGD1ydybnL8,2819 +torch/cuda/amp/common.py,sha256=rPnt6OLsROsahVbMyX5w5Fl3R4G6VUiSl5uioKuDEFg,230 +torch/cuda/amp/grad_scaler.py,sha256=WTG3BGVvo4ifizDYTNTq18lRp_CkqVbYS7Kv5N9n6ag,1073 +torch/cuda/comm.py,sha256=DjabyiIf7WmKgD84HmP4_XYd3Cau_7X_E7dcW1UAUjg,344 +torch/cuda/error.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/cuda/gds.py,sha256=R2n5_QukjvJqG1M63RaVs41inq-0ZFPo1PfDC-kaeJY,5806 +torch/cuda/graphs.py,sha256=twfOetoW5AvtfliNoO9GsrMadKIK139NEApbifE_RLk,24226 +torch/cuda/jiterator.py,sha256=Dvq6dD0Kd4XBvScE7_6BjsdCzTd_ckg08xjT6tmLZ9A,6828 +torch/cuda/memory.py,sha256=Ov3mQlKADWzlN_9i1HHRxg-cB-qcACIHTbKZ2Q0fhl0,46762 +torch/cuda/nccl.py,sha256=o-n5mOZUle-d2083dxUOCo5BsdwSp0Dbed9xZAwexUo,4577 +torch/cuda/nvtx.py,sha256=tJlvU5AbHKvqolYEj44LOpv44B9SS1sN3GXhU_2daGg,3537 +torch/cuda/profiler.py,sha256=pf33aSDw7WVqCTuI6rWbV3IlU5mAuUx46-vHRkqHsCw,2401 +torch/cuda/random.py,sha256=zkgGFMIOSDmXrKs4XgWOCNpZqqqZGOhWMyypEXwjMlU,5441 +torch/cuda/sparse.py,sha256=H912FRisikGM9SSVDQQq-YX5TNIilBYBZs1AwmIv3GQ,67 +torch/cuda/streams.py,sha256=za2VCXfF61oP50Tq_4x_kZSBfeQPOAtA_bwpAyutP-8,9547 +torch/cuda/tunable.py,sha256=JWbPePH-HnVWn2_oFclSpZQqj9Zi8IITZaTgGB6t1YA,31301 +torch/distributed/__init__.py,sha256=9dKh8i8mp9cE9MzJG4kduzp3ShMOVNQDIurLsyDXSzI,5031 +torch/distributed/_checkpointable.py,sha256=VqtUDzJDcTeMUFOguRIo_AvLRXg4n4U73-frKynBqo8,1305 +torch/distributed/_composable/__init__.py,sha256=SUm5fu8Tg24Upb5iHCV849oZSrrtSacaG-7T7A5L2qA,125 +torch/distributed/_composable/checkpoint_activation.py,sha256=0Y9GW-H-SgNyrXK2d1sEfDvhAr3cFO5WSA6A9o6CHFo,4725 +torch/distributed/_composable/contract.py,sha256=ZsIB9xDIvOsBkvBNndpGDMAkyonvhg2q7tewFz4Hdh0,10376 +torch/distributed/_composable/fsdp/__init__.py,sha256=PAE0ViGvjHO65XbIe-CM5BgRx9KNsVL4xU7QRUyqL9M,169 +torch/distributed/_composable/fsdp/fully_shard.py,sha256=LggyTExDS-LXmmZeCoSp-GvlMKOmRulWv6k4aPyYsWM,240 +torch/distributed/_composable/replicate.py,sha256=WMWLiorhAigHnZvXH8qPp3D6GoIckLsx4ZS66nu4nWk,9302 +torch/distributed/_composable_state.py,sha256=xzIW6jxM0BMedcyLmexIps2_bbac127Oi9SCwBZSd48,1394 +torch/distributed/_functional_collectives.py,sha256=XTQJDZZSFc6NbOG8EuF4xoHYOrSR3WScScLWqlN86_4,44441 +torch/distributed/_functional_collectives_impl.py,sha256=BT2vtWeoZXED-Pb6kMUJBQzN_uZi0HH80iabeGvFfAg,3223 +torch/distributed/_serialization.py,sha256=UaX2dO5w2BjpHiGOQqOFtG3jPXBQFJbL--aOSOpm5-E,4472 +torch/distributed/_shard/__init__.py,sha256=9mrjpti4iifWb8jnGoZypHH0ovdZg3A2KQnxg6BACKI,87 +torch/distributed/_shard/_utils.py,sha256=ra_iuevX3gIRVWOz9tql3hLtLRm0JXzeREiL9vwVm14,1070 +torch/distributed/_shard/api.py,sha256=78naAvmEncj5QoJvUjNzy0LRIUYlJSuTQSqP17zyHh4,12396 +torch/distributed/_shard/checkpoint/__init__.py,sha256=kILV09-2Glss17itgmv1SbBq7hp2Y7HLUugCZRGYCFE,584 +torch/distributed/_shard/common_op_utils.py,sha256=i1pNW63w07q_g1TWw-kFxgQk60mQhLYbuYjZXLg_T5k,2179 +torch/distributed/_shard/metadata.py,sha256=mIFmEqOYlGJcEADg95GrVORCT19OIDigJSlcwTmApEg,2215 +torch/distributed/_shard/op_registry_utils.py,sha256=aK7aBTzgR298AvchxXB1VNdeIT6UCO5emnOoOadjM-I,1031 +torch/distributed/_shard/sharded_optim/__init__.py,sha256=sU5hr7r-XNBzCvpOImO6NA3CCrRlBz5N6AprwJavPIA,1869 +torch/distributed/_shard/sharded_optim/api.py,sha256=dWb5_8kyaRgie7EEVzUiB9XZ6pRf6Vjx6ahCZTupqjk,4277 +torch/distributed/_shard/sharded_tensor/__init__.py,sha256=zRPvcc2e0KD0gjGnRUKanXwugpIE_ApwbhpOKUpqHl8,19254 +torch/distributed/_shard/sharded_tensor/_ops/__init__.py,sha256=NW4TXyw5RGFCbWfikCuxxCOrWuVIUovX06jI8PgcqNM,498 +torch/distributed/_shard/sharded_tensor/_ops/_common.py,sha256=QtHsy0BJrgpmgLTvLNtOAQ0E-EK7KW6nQwKd3KK78k0,4196 +torch/distributed/_shard/sharded_tensor/_ops/binary_cmp.py,sha256=IQ9RiKLLcj4MsJQLb-PCxVCbDfDmAh06FIqLjoV4kS8,2736 +torch/distributed/_shard/sharded_tensor/_ops/init.py,sha256=Ny1FXXrRJ4xWIHae7Hk-66YLZTHykllEdVPcuL4akFs,5486 +torch/distributed/_shard/sharded_tensor/_ops/misc_ops.py,sha256=lp_SXuhFUagN_5bxfmZVeqY0SCMhgnGeLGxLq38qvsg,497 +torch/distributed/_shard/sharded_tensor/_ops/tensor_ops.py,sha256=BeGTFykwWXsuWJIj2QkruYX08kov6B6J3Y5T6IUEZ1c,7711 +torch/distributed/_shard/sharded_tensor/api.py,sha256=BDsjqU89WO-KYc_AyZH728B-0LvPWRSw7ZKkFspdb-k,54668 +torch/distributed/_shard/sharded_tensor/logger.py,sha256=yny64ZlZ-RzlS5DcX-SAipWqk4xcR2AQO4yl4Vf8B50,1084 +torch/distributed/_shard/sharded_tensor/logging_handlers.py,sha256=nXuxHnOEhAHqqDPhTk_-aLYIVHoZpQEYW_X4N1Qg0_Q,359 +torch/distributed/_shard/sharded_tensor/metadata.py,sha256=92isIH0O2TqPy3Li1CG00R0Ca1BZ40gGM2GHKkJraUA,2998 +torch/distributed/_shard/sharded_tensor/reshard.py,sha256=p_h7m8AxDJ1S2Ei86zcD31DQ89PhpzGOoWEcFU8_TeU,10726 +torch/distributed/_shard/sharded_tensor/shard.py,sha256=zNfIL2FIGBNwPUFcrfusf0E1R-YO04ZoOdcvY6DpXbA,2361 +torch/distributed/_shard/sharded_tensor/utils.py,sha256=DO69rj4G_JPr_ClKyyaqGkqRgK50P9bAYO7GdCUwfV8,11667 +torch/distributed/_shard/sharder.py,sha256=nErlvDuMU29IUB6oWB295FaXYj8H42FuDQJXdepvQ1E,901 +torch/distributed/_shard/sharding_plan/__init__.py,sha256=9w-j8bY8VJ0dGT_ueJR2yTfzuech180Xkq0XELn1tKs,47 +torch/distributed/_shard/sharding_plan/api.py,sha256=YCcfuZHVFgFisX_U6F-TkZykLVWLiCTkLcIDVgeN_po,3649 +torch/distributed/_shard/sharding_spec/__init__.py,sha256=hih75sKTSXPxdyUOS24Vl6K3Q3ht-74l33uAjM4I2Kw,291 +torch/distributed/_shard/sharding_spec/_internals.py,sha256=FIhsoJfBYjtKF3MKo88NZSiEgR2sz4eEfBph9AHzX7U,8407 +torch/distributed/_shard/sharding_spec/api.py,sha256=AGQSmyJhIptsAKQooSWn-ceSAI6CpC3qRSUWWC76i-M,9821 +torch/distributed/_shard/sharding_spec/chunk_sharding_spec.py,sha256=X3271sF67UfFKZf_A5JWduo8IODItneNNWiA6zXsGN4,9251 +torch/distributed/_shard/sharding_spec/chunk_sharding_spec_ops/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/distributed/_shard/sharding_spec/chunk_sharding_spec_ops/_common.py,sha256=5I4n8vJ2TEFRQnyGglsgiI4IMhWJtiu0e_7Z7fiU0bU,13029 +torch/distributed/_shard/sharding_spec/chunk_sharding_spec_ops/embedding.py,sha256=vCSV8WXu8DSO9BQon8hNE8mf4xiqbTAvNv6o_3jpj-U,11209 +torch/distributed/_shard/sharding_spec/chunk_sharding_spec_ops/embedding_bag.py,sha256=jFU9qfivU0p5awLa6YjSVYiaBy8nsmvdZ4RCxVRDBHk,18319 +torch/distributed/_sharded_tensor/__init__.py,sha256=sL4DKv-CLF-9k_iqL2U689tEIJCSKgeWLsKNtjoqjdg,617 +torch/distributed/_sharding_spec/__init__.py,sha256=PR7htIt_o1lIdWq_dvH3aXMHA22fj59dOpLT15vx3zE,646 +torch/distributed/_state_dict_utils.py,sha256=XnZ8ErKYYYNJJFv2vom6LWlt-Mb5CtFFZRzdI-uUWME,29277 +torch/distributed/_symmetric_memory/__init__.py,sha256=LGFNgSvA5t30s96xKf7VUVzxtXD9KND_G1PFVvf2gzg,61868 +torch/distributed/_symmetric_memory/_nvshmem_triton.py,sha256=Kma_Yh_JUdbm8Swcbx11EBuIJtjPskVhIKAMoDto9Z0,5539 +torch/distributed/_tensor/__init__.py,sha256=6PYDt4AcQDMnCvbcTdOSNy1z-3kiftGBIGxXnJWT0PM,969 +torch/distributed/_tensor/api.py,sha256=ErftfbUVGCDavz7FfrQ0rEVlfYC0XmcTiWyjb4IZ1BY,300 +torch/distributed/_tensor/placement_types.py,sha256=pvi_dHBNCsZl1C-tAM9sdfYOiKb0Jwr3SqyAWMKgfo8,384 +torch/distributed/_tools/__init__.py,sha256=DYVOrVO_6vBZuEuW8EaKdxsR8bFVoE16R2DiGfKo6JM,327 +torch/distributed/_tools/common_utils.py,sha256=UDK3OOV1docGbI2ZvLyaQj4_WFDEI9ePHr4ZAYowKgY,1188 +torch/distributed/_tools/fake_collectives.py,sha256=_kf6oxluA5EfIgOeKv8ZJSyAXt5EBNA5c_H_0ejDrg4,11859 +torch/distributed/_tools/fsdp2_mem_tracker.py,sha256=qLZXiA6AvJeR2dcVGPPh09A0-JvQyv5R0TGZmPowF90,23750 +torch/distributed/_tools/ilp_utils.py,sha256=gwiig3DsC28sa8eOBvlrBTf3CZqLOYcArJW19pxUQAY,10100 +torch/distributed/_tools/mem_tracker.py,sha256=Es3vRiDBEfFBYy3OZp7gkhmTo1H99j8gInJmoqzpa1Y,42836 +torch/distributed/_tools/memory_tracker.py,sha256=b287WMNa-rUmOmEngj9mq6sg9T_1CBL6kkfxvxqMnpM,11658 +torch/distributed/_tools/mod_tracker.py,sha256=DnxJ_n-vODAbQIBnsEsO8jF1wKPyim8B_1-ccLAuhPU,10028 +torch/distributed/_tools/runtime_estimator.py,sha256=tSTFeVdMzdrscQKQ3mgwHx7DgaSec2xeQby5ietJCs4,21147 +torch/distributed/_tools/sac_estimator.py,sha256=VzcuFUc1kPjr-7H92VxAZJxgADji-jSXtN5jV1uezOY,42286 +torch/distributed/_tools/sac_ilp.py,sha256=T4NVaHwHCD8hiq8UX2x9lEcd3jG_d-4ipN6DrWv2RDg,11321 +torch/distributed/algorithms/__init__.py,sha256=e5HJMsdSDkPOxNWYUh9YvO26GDODG5PBedTP2-Y16Nw,43 +torch/distributed/algorithms/_checkpoint/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/distributed/algorithms/_checkpoint/checkpoint_wrapper.py,sha256=33BvKTlw1Bd_K6ITPJ5f-06Y5CTfVsekKkUKAAdimwg,12299 +torch/distributed/algorithms/_comm_hooks/__init__.py,sha256=LkI4VBMJ6_1KGG3Nz0bPxGFOO-ZjfBj-38FNROlJt8Q,131 +torch/distributed/algorithms/_comm_hooks/default_hooks.py,sha256=j8ZZXukye0J9BCYyvvc3FGBss1wRLoH8dl1ikAY73Es,7653 +torch/distributed/algorithms/_optimizer_overlap/__init__.py,sha256=zuKlfE0DcQCZm0av9HrJZfXH6R5AzV2K3xrEAAgoVsk,52 +torch/distributed/algorithms/_optimizer_overlap/optimizer_overlap.py,sha256=zkw6dsk0wrK9Nj4-IYQU0q2yH2hzIL2tag5W9ccz9Oc,3753 +torch/distributed/algorithms/_quantization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/distributed/algorithms/_quantization/quantization.py,sha256=2tNy-iPauktflIPNYk401kaO8DEqpAFb_ehWc3F0y80,5610 +torch/distributed/algorithms/ddp_comm_hooks/__init__.py,sha256=dJBDKPeI4qsoX1s2brunjzHedyVGpgbjxL7DT7tyREQ,3597 +torch/distributed/algorithms/ddp_comm_hooks/ddp_zero_hook.py,sha256=G1ayVB0cInQcJFSax_0RxLsyZIAG14XGkD_O0uXBmoQ,19718 +torch/distributed/algorithms/ddp_comm_hooks/debugging_hooks.py,sha256=X49n0X3TnX650I-DeU-XtL_ssKjdYsn0RYyhB2Ndcms,1115 +torch/distributed/algorithms/ddp_comm_hooks/default_hooks.py,sha256=QHPsem7IGN_wU--vfZo8JP-iVfrw2ee9VuOL0qheUiI,7776 +torch/distributed/algorithms/ddp_comm_hooks/mixed_precision_hooks.py,sha256=sc2NnDmQKmSNYyZ6A83-s9DgYBV-YTfmMdrzurNouag,3254 +torch/distributed/algorithms/ddp_comm_hooks/optimizer_overlap_hooks.py,sha256=ga74pu0gYQodmZlJ6njBCX6ZlBvmA-94ED86sASdmTQ,6125 +torch/distributed/algorithms/ddp_comm_hooks/post_localSGD_hook.py,sha256=MPpCW3JHvFIqfC3frNnzuATOtlLiML6hXEgM6C8ZeGk,5150 +torch/distributed/algorithms/ddp_comm_hooks/powerSGD_hook.py,sha256=AJO3uUzhaIEZykm_yex-mJT3EC1V5YjM4M89ztMm6DQ,40414 +torch/distributed/algorithms/ddp_comm_hooks/quantization_hooks.py,sha256=D8RHL1ovgoQSDMKIAyh8gPjxO6MM4Cw3d7Vm8Y6YHSA,8234 +torch/distributed/algorithms/join.py,sha256=qmabLmhh8qX1FMr52fT7cUia2BfFN3me4ba7Mn52X7s,13386 +torch/distributed/algorithms/model_averaging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/distributed/algorithms/model_averaging/averagers.py,sha256=XFrjuSvdlieVZluQ8wiU6fliRgphdytft56QCQUVCdI,5455 +torch/distributed/algorithms/model_averaging/hierarchical_model_averager.py,sha256=FsBmpH6Jjc2G7LRcHbhKeJ45mAgT5JyTAWZI5JsuVUk,9794 +torch/distributed/algorithms/model_averaging/utils.py,sha256=cnPF0tYUSgbjvjYJpUyrVyb3H3fRtJ38_Tfp8-NMOoY,3161 +torch/distributed/argparse_util.py,sha256=NFxjw2asYt06aFffcu6rhJpC3FFthwKLu9wvWEm_qQU,3903 +torch/distributed/autograd/__init__.py,sha256=XZ0DochOG4HofBislMoYDkesBinL1HCCSyPRlOotv3I,1647 +torch/distributed/c10d_logger.py,sha256=qCH_KCiEaaI8Ba73h4036Gm1EnOr4Q6VHGbotFTzANs,3125 +torch/distributed/checkpoint/__init__.py,sha256=0g9Y6pHqLNQNoPJM8kAUY7C-1CV1FCNalXk4UzIAHWA,694 +torch/distributed/checkpoint/_async_executor.py,sha256=THCwvj7D_9VPDEhRPIfuRl6E7--RusMV1I4db8F7gX4,1097 +torch/distributed/checkpoint/_async_process_executor.py,sha256=qVQWahkte6zRrTeK5e3udEC07NG0uhXFm88AqCFRfl0,12385 +torch/distributed/checkpoint/_async_thread_executor.py,sha256=UeGhCyS6XB8RvHlMNtq5tcn_65sIp7nv1Bfx67HMawk,1365 +torch/distributed/checkpoint/_checkpointer.py,sha256=S884dJuQQ0TAukletSFpft3u-3FKvshBhXBd7DicsHw,3656 +torch/distributed/checkpoint/_dedup_save_plans.py,sha256=GmO9WidI0HRz5JwJQQZVTk4bHq7xRyv9SdJJuWFhhsA,2681 +torch/distributed/checkpoint/_dedup_tensors.py,sha256=MU2RaM-qHxvn2I-LmNTgvw1cvGwoqItiCHxhb5vNrRM,1991 +torch/distributed/checkpoint/_extension.py,sha256=AA822zNRYaiyYZFkvGjCycDky0VmSuU1Ayl8j-tm6PA,7703 +torch/distributed/checkpoint/_fsspec_filesystem.py,sha256=FSzE-iL-1HRkdCqFVkmEdrcwTv2L8xhoXXDQbpMAufI,5583 +torch/distributed/checkpoint/_hf_utils.py,sha256=GIsvjwX3eQYqw-gjYDGDrvdtF4gegknG_2OCRmVoH-w,2840 +torch/distributed/checkpoint/_nested_dict.py,sha256=IPtO0mCEYB9xT0Il5g4GW978lubbCFCj51Qo9mTDJWo,2273 +torch/distributed/checkpoint/_sharded_tensor_utils.py,sha256=HZOdtN7SH2D79rgdm2g7BDEacEwTxkAJWZhNtSVv6O0,4144 +torch/distributed/checkpoint/_state_dict_stager.py,sha256=-q5X3OSbimxjFmG1PZhT1VUygBx6Z580LqVQjLNvuKs,13520 +torch/distributed/checkpoint/_storage_utils.py,sha256=cUesOcN9IgJZdH1zj76mUgWBSt47FukmXNrQzTt5TO0,1408 +torch/distributed/checkpoint/_traverse.py,sha256=7NutdzzX4RxWIkCUwzaoadUmZDqYsbT9I81BQhHUp6Q,6876 +torch/distributed/checkpoint/_version.py,sha256=SVGnUOT2LrrOFXySPqUstbulvBXbaUFSyx4e-hEkcH4,122 +torch/distributed/checkpoint/api.py,sha256=2Ic6QbGHRFXIuz1f_OsXrEWHfdfHSTCtCEkhGLReq8Q,1417 +torch/distributed/checkpoint/default_planner.py,sha256=ZYPSyTkU6hO_4u4dgz1jcQBsh9qwoTcAFziiPZxBVnA,26212 +torch/distributed/checkpoint/filesystem.py,sha256=ErMWVJ0CcoxJTykUcsFXWHY1FZwnBv4r3b_PII5JOJg,34192 +torch/distributed/checkpoint/format_utils.py,sha256=OlATL-4yPwFG90MRwpfd8AKgRv3oYyEuVPE0LitHFuY,10234 +torch/distributed/checkpoint/hf_storage.py,sha256=Kx8xd3TBLOGsiSBoUsNceiGlCnVLb-GRrBaOCASUw5c,12941 +torch/distributed/checkpoint/logger.py,sha256=bcjpphPj6IbUDRVVvXobvXfU24mJVdpJcfUqHfW4vYw,3549 +torch/distributed/checkpoint/logging_handlers.py,sha256=Ky7OdEi5JzxTwc2u34Mq-tX5S-Y0MynrHAc1xe2nCEk,220 +torch/distributed/checkpoint/metadata.py,sha256=g0u5m07xmbydvmDwpOBtGw4hsWJEnXfX8SBfGWwLDwY,5597 +torch/distributed/checkpoint/optimizer.py,sha256=0Hy8Vj-NWb9Qze5d2tu599bZ_IM1zfJCckOWpOAOhg0,13153 +torch/distributed/checkpoint/planner.py,sha256=-zHLbO-NNiKjmnuXh3L0IguHtMZx6UG5qMDGMrCHJI0,16263 +torch/distributed/checkpoint/planner_helpers.py,sha256=DNkCJ3Ufd-WgqZ2IeMLngc-eYQ50r3qETFyXlCvrfd8,16517 +torch/distributed/checkpoint/resharding.py,sha256=1vF4sTAE5WaZaundzKVOOom7CgKohwF52TUQUoWpVxw,2275 +torch/distributed/checkpoint/staging.py,sha256=-tF4mAuGXlbOlgjsf5vzolGudUBWdS8ANWizg8c8V4M,4958 +torch/distributed/checkpoint/state_dict.py,sha256=c_7DwgNkf_FyGoPrUiqfvCTNFmM7LYg64k-jI2x-Tvg,56348 +torch/distributed/checkpoint/state_dict_loader.py,sha256=3d72X14rFHtWdDXnWm8lqdRhxmN1LYqac4oIxXdDYTo,12428 +torch/distributed/checkpoint/state_dict_saver.py,sha256=7FVZPPqHuVRKsFCnp313hkVdN3w5OMUOB5VkI5AIdNM,14138 +torch/distributed/checkpoint/stateful.py,sha256=1cJTGrO_TPFeSguyQhPYCw87ph_QjJRafyAVgx9u7bI,1061 +torch/distributed/checkpoint/storage.py,sha256=WscoQBR_gdHdUc_yF5Eg6PZoh42L5qBk2o3b4L8zg2Q,9725 +torch/distributed/checkpoint/utils.py,sha256=m-_mFOBbEuVikkCe-Gdyw1wGLFOWQkE3Z3zaVleeH7c,16073 +torch/distributed/collective_utils.py,sha256=FH21dkbwMekWMs-j4hrPFR3rXnJQz9a09c1jaBPEId8,7273 +torch/distributed/constants.py,sha256=uZML5nSsbYATkjdA-bl137WEofh51gYQNXKYx78iofE,1229 +torch/distributed/device_mesh.py,sha256=BUWb5TRHssbkkehT2aQ8OI16VJBeQ7jLkXVJNUF3ml4,51169 +torch/distributed/distributed_c10d.py,sha256=5MYVMjnW_4Y-vIkYfF3aQdyT_RtyjAM71Qu7FCnNOFs,222365 +torch/distributed/elastic/__init__.py,sha256=QMPilFK2QkDj895i4fqrtE5bzyO55GJkV4t4EM_1Qzw,3654 +torch/distributed/elastic/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/distributed/elastic/agent/server/__init__.py,sha256=p6OmZ3UC2RN276H3JLUZm0ygZ1GBWRcOOq4OQwozTo4,1401 +torch/distributed/elastic/agent/server/api.py,sha256=I7KFUHngywB7APXjHDVUahVVqNX7VJNIkL4T86pfB3M,37496 +torch/distributed/elastic/agent/server/health_check_server.py,sha256=XDdLOQE5JE7U35Vyl2OcPd9FwX74kjvSBDFbNF05Qc8,1679 +torch/distributed/elastic/agent/server/local_elastic_agent.py,sha256=JNDIbmEgBXvjo3MKiUIGTfSpvFfqhq5YqlLtDwNIW8E,16591 +torch/distributed/elastic/control_plane.py,sha256=EeYAL-0jl46KYrj16DDO8JVUybqqu7Jgwx7bm28VWmI,1181 +torch/distributed/elastic/events/__init__.py,sha256=Lq_xE4au-XoBlCPoLVV1mP5zDL3rH9vtOwkgTJMQdPI,5376 +torch/distributed/elastic/events/api.py,sha256=DuCeUrpxiD0I67jxpb7Af4sUqQuq3w57e7o29r-vXXI,3247 +torch/distributed/elastic/events/handlers.py,sha256=GVwoHQQFcpFeDW6eobtpDADBX8eIxYCN0tsiFO2pcDQ,556 +torch/distributed/elastic/metrics/__init__.py,sha256=uGLMavcRmY-Pc4ZSMFCspodf8VrarNgcv5NJQDwHttc,4902 +torch/distributed/elastic/metrics/api.py,sha256=SEYR5kSDL1wubvxsjQzYa5a79w9p2qVYyfNcelFBJ5U,5676 +torch/distributed/elastic/multiprocessing/__init__.py,sha256=Nc_tIJC9FEPZhNnqKms2Eb8crhywx8lwLlFjjKjdzcs,7378 +torch/distributed/elastic/multiprocessing/api.py,sha256=HYOE7dhOBlEokN446GWYNp6DPO745S5akxVapYZqR6A,33742 +torch/distributed/elastic/multiprocessing/errors/__init__.py,sha256=V6SQAh-mQ_JIvLn3Bje5rdGCXHGQWXyykwyCZ5zYJ0s,14491 +torch/distributed/elastic/multiprocessing/errors/error_handler.py,sha256=u-p3rnJiTrMfTSiJDE70eT_ruMbKbFdGMXNqsWAxegQ,6600 +torch/distributed/elastic/multiprocessing/errors/handlers.py,sha256=ea-3iQUTQRDp_YAUhk-3hZ2HQyCPqz_2iiL5_xfD9Q8,464 +torch/distributed/elastic/multiprocessing/redirects.py,sha256=areeeQwpnChPbp0uUJ7i-cyc7w8KEqn8FtyVIT57OJ0,2764 +torch/distributed/elastic/multiprocessing/subprocess_handler/__init__.py,sha256=pz2OzruD8OBIFFxiI1_TLy0Bd8LDBKNLowzW1_jZUSs,523 +torch/distributed/elastic/multiprocessing/subprocess_handler/handlers.py,sha256=FBCfNfvHU5DSVBRTFeiN7N3Cd5dtRoJfPkQWn6RGT5w,726 +torch/distributed/elastic/multiprocessing/subprocess_handler/subprocess_handler.py,sha256=mHHugB80bElS_K8syHll88mX2XS1UN9w4fx_RTTGboc,2436 +torch/distributed/elastic/multiprocessing/tail_log.py,sha256=TC38G7Ppa3ZIv_mCr0WSyXT9rZVBXB4TIp5NyC_Y8nI,4947 +torch/distributed/elastic/rendezvous/__init__.py,sha256=tcH-Q6v-bdJTWunq4JT-crJ7TxTh9jxEmdzt3vtRBbI,6269 +torch/distributed/elastic/rendezvous/_etcd_stub.py,sha256=_SXz6PhGHx25Hg6pMWzvKpJYR4HsXN34s27t0FoelVE,2014 +torch/distributed/elastic/rendezvous/api.py,sha256=9CI-fjQH_baZkgW7gpnBBjvZOHZB1iqUd5KjeUut26w,13094 +torch/distributed/elastic/rendezvous/c10d_rendezvous_backend.py,sha256=oVJSqNz3kd_h8Cc89QHzjssvFOBliDRZMqeMhPcs1Rk,10900 +torch/distributed/elastic/rendezvous/dynamic_rendezvous.py,sha256=4wuh169wRxLmQJEoVM0uMKz9BG6GtkvkVV6bsZATwwA,49425 +torch/distributed/elastic/rendezvous/etcd_rendezvous.py,sha256=viYgyTfWsb1N2LjuZDznZjUdDUX0ON9nG3sBgCNsIBw,43530 +torch/distributed/elastic/rendezvous/etcd_rendezvous_backend.py,sha256=QafmTsc9xrUhJ2RAdmAbcpO9XMBJWBf1erFfh1U4Ex0,7409 +torch/distributed/elastic/rendezvous/etcd_server.py,sha256=I-G9eR5HXzXXyRYJZNLFft49G5yxEtEME9S7u9uJh48,8437 +torch/distributed/elastic/rendezvous/etcd_store.py,sha256=IjI7u3Ab1RaMCrFTQdvBd-ErTARDVnFQrJ9_WdY1S48,7255 +torch/distributed/elastic/rendezvous/registry.py,sha256=hW6mec60A4FiguvwG23dIXmoeplT3rKhCM01gjMe2-g,3023 +torch/distributed/elastic/rendezvous/static_tcp_rendezvous.py,sha256=lx_9W3SwGVYqFINdYjlJ6ettZq0WSvZVsPB_CDFjsf0,3665 +torch/distributed/elastic/rendezvous/utils.py,sha256=wgfvzbq9iUkx_DsFmdwP_C75_PeOzdsbg_uJ57C90GI,8390 +torch/distributed/elastic/timer/__init__.py,sha256=IQ79GKrQX_UKab_Lbp0uqMuLgb2QzDQgd9EX7NJO5aU,1750 +torch/distributed/elastic/timer/api.py,sha256=1JZUs9lHUjEK4ZqHEFiW0mHLOoGqKGS-ytnK0z9K98A,9586 +torch/distributed/elastic/timer/debug_info_logging.py,sha256=6mix9Su3bo9BVTM_QXNz75c-WAbDLL8jNbojKbjdEsE,610 +torch/distributed/elastic/timer/file_based_local_timer.py,sha256=bvljCE39cEqNbqBOfqeXCQkUOaw4D6uM4UvXFdh4UoU,16449 +torch/distributed/elastic/timer/local_timer.py,sha256=JWzMEwIY2iLItGjY3ZU-SfP22HQJb_a6_pB-d-KrxEU,4285 +torch/distributed/elastic/utils/__init__.py,sha256=ztvtzgzf5pR1ixbbYfY0lGx85VhyhJuHH8er7-XNVpE,318 +torch/distributed/elastic/utils/api.py,sha256=QMd6BGMe0lVXYnOSMLinlB3Hh_EJn2s6R3Cre2K2l0s,1705 +torch/distributed/elastic/utils/data/__init__.py,sha256=tF96JUuxZmWRkvGOzP7tbZT6s8CKNz29F6O_OYTcL3o,372 +torch/distributed/elastic/utils/data/cycling_iterator.py,sha256=JgJwYYzIqpYKgg8ntGyNq21VpW6DTelCKIdDJS94QuA,1643 +torch/distributed/elastic/utils/data/elastic_distributed_sampler.py,sha256=6IpSuGzKw9QgkS03hhzNDCXhnfu_c2dj3Z-ieCfV-Sc,3034 +torch/distributed/elastic/utils/distributed.py,sha256=IOrLsxeFA0NXHKw0J2bjuuOoOvWeA7GIxIdbyOgkD_0,5923 +torch/distributed/elastic/utils/log_level.py,sha256=1pOw0YanV5aPrMGwqkATk8OTXm6iLF2iuUYKbUf1b6Q,339 +torch/distributed/elastic/utils/logging.py,sha256=hekWqKajcPTgfFfi_EP46yqOlbJRpYldCTyIgMjYnLA,2271 +torch/distributed/elastic/utils/store.py,sha256=TB4MctfUeeyzuSGet-ivM1USYcjJ-RON7ai8q77KldQ,7278 +torch/distributed/fsdp/__init__.py,sha256=QAAwtP_tJ_pGAahG-3sQXr-8pUKIPOi7-xIy88TQSwM,1740 +torch/distributed/fsdp/_common_utils.py,sha256=8ZPk5uu3qpC8QHteXIDMH3HgWyR-kJgLJLIKUEnJoJQ,22394 +torch/distributed/fsdp/_debug_utils.py,sha256=GA-HyH1Ba1q4dw49cb5e7A8uimc6SbiX1XKIqJxJYu8,5694 +torch/distributed/fsdp/_dynamo_utils.py,sha256=_YtXy6KD9FUp--G1z7xpA4rrcSWFnF0bMkRtcDptpeg,2631 +torch/distributed/fsdp/_exec_order_utils.py,sha256=X6LbbP4U6StT0zX6cGOlk0kAb0oQ0-_beQy4wyTdA0Y,16073 +torch/distributed/fsdp/_flat_param.py,sha256=nMTtWlmPnt-TogKEL81nyBiswTikVA0xmEltH2F3F0c,123630 +torch/distributed/fsdp/_fsdp_extensions.py,sha256=TsYe8uoZmaI1dtmEyLplxxxvmJt-dUpbdNun5eeLVto,4956 +torch/distributed/fsdp/_fully_shard/__init__.py,sha256=pcv6ncd5awjUqnqaDprxzFg76XdKv4zL4XcX6AYWT0E,376 +torch/distributed/fsdp/_fully_shard/_fsdp_api.py,sha256=ZJSEGY_Q26Ao8lqfcytZvXVwqvJAgsjXDwVt6jyvidw,3347 +torch/distributed/fsdp/_fully_shard/_fsdp_collectives.py,sha256=O_B5IZaC0alDm4F1SvZeh_aRV-xp8g8GXlVlUZ9wk9k,26800 +torch/distributed/fsdp/_fully_shard/_fsdp_common.py,sha256=2ZcthzapPh5XrKbJuyR_v9WsK3ZmOYTF3k8tW_Ihfcs,5680 +torch/distributed/fsdp/_fully_shard/_fsdp_init.py,sha256=lc0QH51cHafbGWX8Ls0rNEh45YpNgSmf2tGhIj4KYao,9142 +torch/distributed/fsdp/_fully_shard/_fsdp_param.py,sha256=EuRZSRWJtiMVhieEb4NxderjU-aF9pGHW162t4hIluE,41522 +torch/distributed/fsdp/_fully_shard/_fsdp_param_group.py,sha256=J5ioeyYxCR33OK-v9t_OrTjODMQhWFtDMeyq77TnhPU,35452 +torch/distributed/fsdp/_fully_shard/_fsdp_state.py,sha256=RsVaC4jDZFNNgDKhShOaABegDd_tHGtf_kPNahazR0s,17794 +torch/distributed/fsdp/_fully_shard/_fully_shard.py,sha256=kBIg79i26gFe69Ropomo7c9QI8_MNpQVnYkpUCYdjhU,30118 +torch/distributed/fsdp/_init_utils.py,sha256=lzpHWO0ulFqxj88Q-xqAFen-Mi9REzSDAfU048hCfiY,46223 +torch/distributed/fsdp/_limiter_utils.py,sha256=8QUeuAz8fTVqP0wxpRTDADpQKH-KrlyZqUfU6CTZbYU,1086 +torch/distributed/fsdp/_optim_utils.py,sha256=-P7x93gxFO6UH4VvMNZTwPu-thZvyCVx95q_e3TS41U,86620 +torch/distributed/fsdp/_runtime_utils.py,sha256=Lwc7rO10swHH0Yf_KF9B8gmATfh3_WuY0XTcMvdvBS0,66525 +torch/distributed/fsdp/_shard_utils.py,sha256=em12fl0MSWJqj7QWl2xAc-62QJ1tKVGMh8kHjoD82do,4623 +torch/distributed/fsdp/_state_dict_utils.py,sha256=vtOBjpn5dhgh3nvI0sV3pdOnALrxNBa-CcIJTcXkVHQ,34293 +torch/distributed/fsdp/_trace_utils.py,sha256=gBgd1Ooheg0C2Bl5mqKWcMHR16sJeBFv1RRRs7Rxecg,10752 +torch/distributed/fsdp/_traversal_utils.py,sha256=haGRJUg9EyPuRJjUyUWfBDaRmEdJ7VOaOPi722DQ2Xg,4610 +torch/distributed/fsdp/_unshard_param_utils.py,sha256=RoAJ41qCSEzmGOnXq4KEppSbVgHnCb7-IWDA0mpMbUA,11524 +torch/distributed/fsdp/_wrap_utils.py,sha256=IlYG6IHmynB_f_Lv8pkHsom6ClqmxkbIGrE3CdCzHPY,10895 +torch/distributed/fsdp/api.py,sha256=wAWWh4wALbNeji0--fcH5TNTYwZ5kKVVNlZOUs8uSAA,18975 +torch/distributed/fsdp/fully_sharded_data_parallel.py,sha256=BayIz9y8VJOeT3RHEVt97NGuECaMkMXQMf66r6ftilg,100272 +torch/distributed/fsdp/sharded_grad_scaler.py,sha256=NpU4umd1wqn-n7QRYWJTjQy_gpkE4US3bmsPGeFiHV0,16164 +torch/distributed/fsdp/wrap.py,sha256=dp6Ydp5DJIvPJLxT6bdNHBp8zzxbK6aK45-nw-ndsGs,22615 +torch/distributed/launch.py,sha256=e7R-D1Wy1X98YSTVQESxOkDBA3UwCkrzpEzraXovYYc,7595 +torch/distributed/launcher/__init__.py,sha256=V0J5Y0SVG1ybhGeHnjarMc4dcOgyhRsHpnP7lBMyB48,349 +torch/distributed/launcher/api.py,sha256=_L_tMJSyZyd0g_7gr9ubOy_z2m9ebIfXZxrHNKS8s_Q,11452 +torch/distributed/logging_handlers.py,sha256=nXuxHnOEhAHqqDPhTk_-aLYIVHoZpQEYW_X4N1Qg0_Q,359 +torch/distributed/nn/__init__.py,sha256=Ta4l0Jr2gOK7eQR-e32stWaDKPOlV_Kii0SExydXk_Q,145 +torch/distributed/nn/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/distributed/nn/api/remote_module.py,sha256=1Rd5_br7HKHZnQAubzLPF7wf73MKYlHj7wJ42AeL0RI,31286 +torch/distributed/nn/functional.py,sha256=VvAgYym09WBs-W-ZdO1TUFGrGFsAVzV7dGER1i147dg,15215 +torch/distributed/nn/jit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/distributed/nn/jit/instantiator.py,sha256=Vn0TFO4n_ws70i58TRFXJGRp3iXnoxg9lt6-FnsZNhU,5510 +torch/distributed/nn/jit/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/distributed/nn/jit/templates/remote_module_template.py,sha256=D_GFa70DG1KY1GwGwFAqvCC9gE9CwZSe1BJ-jHc2YMw,3463 +torch/distributed/optim/__init__.py,sha256=HxJ6GIWc3b6uAZWNh45oePXlQLAdX9Kmu3PByAOvlh4,1440 +torch/distributed/optim/_deprecation_warning.py,sha256=4lon9fWNrTNS4ARfi5Dag5tvohwNyPMflHBl9VFd_0E,547 +torch/distributed/optim/apply_optimizer_in_backward.py,sha256=uTNZylxBaBVQK1teYyPJ7R45-t8aqG6joeGT3qyi0AQ,5214 +torch/distributed/optim/functional_adadelta.py,sha256=8csPKBQ-yxUG3FxdiBlNl-kxyPCATmhWiE5tS9LzFOo,3954 +torch/distributed/optim/functional_adagrad.py,sha256=QqQpxZVEzN8SnFYgBcpaFsCjZ80nrNWyzxP2Pb6qLBM,4304 +torch/distributed/optim/functional_adam.py,sha256=riW0FEuGIyfDgh-dkgDAK6X7OfIQ6LLuOCOOgMYg8jk,7425 +torch/distributed/optim/functional_adamax.py,sha256=5w1u7d4m8gilBidr1YZNur5pShj7nDYVBZIbnO0pIe8,4651 +torch/distributed/optim/functional_adamw.py,sha256=UBixbGeeNC1gW61-CmjAj7B_8p5Bitf94PPW9lwYSlU,7545 +torch/distributed/optim/functional_rmsprop.py,sha256=ehWjlZrPx2AKf1yQBQiCSAgsW6Q0-epiTjcVa64G7gg,4684 +torch/distributed/optim/functional_rprop.py,sha256=d5SnaLr49RrY9KxuIxKm5Tzf_fdpjQj7UO0qRobvR-4,3838 +torch/distributed/optim/functional_sgd.py,sha256=OmA5hKzGPqwEH62ltxuUOYkiDCH-fC-0BrGnB6ILB1g,5938 +torch/distributed/optim/named_optimizer.py,sha256=ewHw9SYjzlwv55mJQMFU7tIY9EnExxPVZ8v8a3fQwuk,13985 +torch/distributed/optim/optimizer.py,sha256=cmCwmBJPCvhtVbYbeesR46MEhJCAvS0V6yTEQ7xihaw,9771 +torch/distributed/optim/post_localSGD_optimizer.py,sha256=AcIxQhHSDRV_GSWIO9kbCbsEW3pUPwmnV2p2YoczeJY,4467 +torch/distributed/optim/utils.py,sha256=JhzIndnTdt0CMJiEblAbg-tOdVDhFv6tMk00_WjBPMI,2238 +torch/distributed/optim/zero_redundancy_optimizer.py,sha256=CDAdIauWuZ-k7pxprFEcpIlDWz9mEo1MxyqErnO1DBU,72000 +torch/distributed/optim/zero_redundancy_optimizer.pyi,sha256=i1Qqt3SwgUsfWIJS_gj_wxMe6-wpGbfNnzsUb-lRPLg,2807 +torch/distributed/pipelining/_IR.py,sha256=xS29doB32hO5jwO7IbodQxdArBZ1fV-8KsU5Xwu9PwQ,49244 +torch/distributed/pipelining/__init__.py,sha256=rgIokSHTq-dR2W95BIeyxDnqPHTjR-dy427L7AmC8mU,641 +torch/distributed/pipelining/_backward.py,sha256=wF_8-Us3kMHCW4sTATqcXUldhmKTCt-yXOZ9ZVJs2co,15866 +torch/distributed/pipelining/_debug.py,sha256=dDUjgp_-XnUEsM4I-zj5-yQGxcapHbBb3JcRSdRuCqc,608 +torch/distributed/pipelining/_schedule_visualizer.py,sha256=xmBn9CtWx1Vi_etUO0k9HK-41nMfDhIq1bSO6zfG2KI,6922 +torch/distributed/pipelining/_unflatten.py,sha256=GUpLGbhqxbAMjcY9aDxandxUd3Rva1aXs4BRApx9Gj8,952 +torch/distributed/pipelining/_utils.py,sha256=b-T6u9y9fdGXqKKP0Pf10cTAW47HLCva5sXyA4xeEm8,3787 +torch/distributed/pipelining/microbatch.py,sha256=qTXN6DBIts_LFkV67gTGX-EV4uW2l1eoXaXPfpGQszY,16198 +torch/distributed/pipelining/schedules.py,sha256=8got9by5V30VmFCcvNqNSfd5iDfmEtM_twKEpqDGQGE,113840 +torch/distributed/pipelining/stage.py,sha256=wXeWGcro-6JeKh13uZNGAl-xzulqZ4eVJbNCX4oetJA,61677 +torch/distributed/remote_device.py,sha256=1fVMJwZembXV3LVYIeA_cutnk-KEt3WVBkkD3LgUVGw,4600 +torch/distributed/rendezvous.py,sha256=lwM94h7Ugm0XAEjMJeBteF0xteiB9itOSxe6DTJTUhY,10185 +torch/distributed/rpc/__init__.py,sha256=hHSugwap5MSOYxFFd4S_uBmYfE4QcOcuyOJUE0LDuCM,9945 +torch/distributed/rpc/_testing/__init__.py,sha256=xSU_1OK5D0vaFTGSxPlzX6Ad4CEvsZv7DIJYY1d2Unk,479 +torch/distributed/rpc/_testing/faulty_agent_backend_registry.py,sha256=KmeZ2KHDwSWJB3mdfA06hdaabt25vKkkUU1h8SFVk1o,1639 +torch/distributed/rpc/_utils.py,sha256=tcq0ADOGMmd7OkKYFY7apu6c44VbgFLxCLOCh-aiyvU,1649 +torch/distributed/rpc/api.py,sha256=iO5qnlWWbAZ_B-r9gfxrGH4QPJ6hj6-iyggrz01kA5Y,37004 +torch/distributed/rpc/backend_registry.py,sha256=Q6GR43Qeef8jt_XJc6lQzHEhXyueY2PQAAa4ltIgNgQ,16253 +torch/distributed/rpc/constants.py,sha256=AFzuxIi9gIVxb9nyZNZ6h70Is-DUMsCHZ-WEO3MOMP8,804 +torch/distributed/rpc/functions.py,sha256=9sE_wYiqlfUmb6AFBFPmOq6PApwYKxosNOnteoy_0Oc,7272 +torch/distributed/rpc/internal.py,sha256=y8K05r6QPwRy2gKRLVMNG5xGCy2KvhqEpBv4S2vOxXs,11112 +torch/distributed/rpc/options.py,sha256=A7oudXznmNFMgO8RbTqO2bXlCmJNDBapTbZKSDll614,7231 +torch/distributed/rpc/rref_proxy.py,sha256=olBD28H2iKQvS8w1FcqGsr5M4_xYCSln5BeokLrPLJQ,2673 +torch/distributed/rpc/server_process_global_profiler.py,sha256=t8yGxo0tj41hSM_cPMnCSQlnYZgB7D1aMB-U7iWrjsM,8445 +torch/distributed/run.py,sha256=q90FaHalL-9lhMAfP5uS7Zw43cgTgZIPIhuwJMLT3p0,31915 +torch/distributed/tensor/__init__.py,sha256=Muir-bYO3_7K1ADH97n_5nD11uZhripJDYRazGS8N6I,2169 +torch/distributed/tensor/_api.py,sha256=iqrdgXsPeNnG5nwC7-CLMUI0Krb0MCPEzMoGrINZsGI,57059 +torch/distributed/tensor/_collective_utils.py,sha256=opJBIUR2MxeE7_0ZCmzZ42AgygZc0n2fRTXOQnsdpCo,14006 +torch/distributed/tensor/_dispatch.py,sha256=iyMtWQYyhKK1Jv7DIMVKqJ_R8CBP2AqQEiBSXdkfwz4,19547 +torch/distributed/tensor/_dtensor_spec.py,sha256=HgrrQ84TUCaJRaZCmXJ9z1xorDP2i9x4ZEazeawA9kk,10309 +torch/distributed/tensor/_op_schema.py,sha256=B72lWDoHzVi3BqnCTD_X8uvLS9pJDPb46ogc32LIWdQ,20482 +torch/distributed/tensor/_ops/__init__.py,sha256=bQDReWqPLgNAfjBJC18_Q8Yg9wTj_ZlIut6CnQ_KBiY,380 +torch/distributed/tensor/_ops/_common_rules.py,sha256=6o7Mw-SIYKGanctQUWF03fy2S9BgH1SvX4wj4Y7Am4k,11805 +torch/distributed/tensor/_ops/_conv_ops.py,sha256=dvprVWrEP3YZtZNz0cA73HdspZSU4lEEx3VsNLf84eM,3521 +torch/distributed/tensor/_ops/_einsum_strategy.py,sha256=JF9pmC84lKj6FN4QSVLHgE1XfYK-g2-eG6wnErcvU50,6383 +torch/distributed/tensor/_ops/_embedding_ops.py,sha256=dN6vwQw0ciohpIdYUq-ODCKpS5fdRJGkJQ4PiRz-CNg,10154 +torch/distributed/tensor/_ops/_math_ops.py,sha256=u7SimHXQejnH8ma2RZYDPQV-HMdqAuiqZpk6Xx9Swww,41211 +torch/distributed/tensor/_ops/_matrix_ops.py,sha256=Lsq_l0L-cDIW0dMfMmHeHvuiuyffCOSNOE6TSXSWqjY,35956 +torch/distributed/tensor/_ops/_pointwise_ops.py,sha256=HgAC2Gq-iZJ5b_eXmJdqJJbqBVF5DTFkNXgzWTo9c4A,21385 +torch/distributed/tensor/_ops/_random_ops.py,sha256=-K3busPUMyyKZ_pAa7neYKYmEzahusIXbuh6dblhApU,1087 +torch/distributed/tensor/_ops/_tensor_ops.py,sha256=IOXdiexy--pS7B2Sz6s2tmOENUNXjVjVJUfe6HBhIuI,35307 +torch/distributed/tensor/_ops/_view_ops.py,sha256=2xnSJeZSCQ8HAa6aM0JpJeT4mfOm1jtRPnYvI54Z0wo,25609 +torch/distributed/tensor/_ops/utils.py,sha256=aCdoSemVSeuUeVcDHMsHDhNaZe7N1OLaVTJsmPDqrd8,10743 +torch/distributed/tensor/_random.py,sha256=tCtNp8JEk3_gVvJRuf3T45XxYSAryb9vYJvA9If7NeU,16206 +torch/distributed/tensor/_redistribute.py,sha256=L6P41EptF4E93xh8mPf74VYbSv1maXKF7L1zWy1Oltg,16274 +torch/distributed/tensor/_sharding_prop.py,sha256=9X6tXAMKrkCYy-UFnJyplB7W9w5YBxOrjdWOTwpLTo0,22956 +torch/distributed/tensor/_shards_wrapper.py,sha256=i8JCKlwp9Y5VesyCUVVrmt7euxcWfQqHXBz42tibrGA,13480 +torch/distributed/tensor/_tp_conv.py,sha256=aOmFGX39lcEmJn2ex_isT2RLpGA65b6qrkoMgvjnyjM,10154 +torch/distributed/tensor/_utils.py,sha256=cHW_Cjejo6-HAyIhQZJwUviJMeQYQwl15X7m00q--es,16353 +torch/distributed/tensor/debug/__init__.py,sha256=UhAixmUlON2pwFNDHXlRQLafPRriRKOUySTf9VgG7fk,850 +torch/distributed/tensor/debug/_comm_mode.py,sha256=PDZsDWBj48JRi72XUIbCwLIMxyTXKqbsSoG4jDD4Kac,28762 +torch/distributed/tensor/debug/_op_coverage.py,sha256=irVRRndqCL1zMKKryJ9JMxB7IuttQqaWqG-318gxU2A,3142 +torch/distributed/tensor/debug/_visualize_sharding.py,sha256=hU90eXVLSi-KXsidTUl1thXlqwYn026iqdx40WkDzrQ,7549 +torch/distributed/tensor/device_mesh.py,sha256=MONPC_i1XphcY2E0AAte1MW668BTlUGugMSQzfJqzik,190 +torch/distributed/tensor/experimental/__init__.py,sha256=9I70AFPtyjGyKxAuaBsRfDxvHyCeM5a7o7q3X1yUe5g,1424 +torch/distributed/tensor/experimental/_attention.py,sha256=a1uB5TM0hGfDzoOzKR4GuHbGT1dmlLHsEQ8BYPM58VI,51076 +torch/distributed/tensor/experimental/_func_map.py,sha256=IpWiYPBuKhwER7d38PRb0YSlKm9P6WopjpMtFkQLzrw,12185 +torch/distributed/tensor/experimental/_register_sharding.py,sha256=OsFu_9TwU1KMdXGYWX3DMw74z9hxo9h44L0Wrca0xig,5666 +torch/distributed/tensor/experimental/_tp_transform.py,sha256=hPOYgtHTPh1cSAVeiG5z5W-yQE_GLeMczpgWeQSlEOs,20280 +torch/distributed/tensor/parallel/__init__.py,sha256=_rDiCQ0mhllWr5HSDkOV47G4Z-WAZ3L1PCyzLhjgXeU,643 +torch/distributed/tensor/parallel/_data_parallel_utils.py,sha256=mZRfcks_Du6J65k2l3oQhBUkfRBimdTyB4Z8p9Uk0Js,1510 +torch/distributed/tensor/parallel/_utils.py,sha256=h9MzM-9LiBgS0bNppJUtHPkpoczDo-C_t1ccoaGB_Fs,2313 +torch/distributed/tensor/parallel/api.py,sha256=brkn6D4VKi3FY6teDNijbgay4bxHTCXIVTI0vAbplHc,6013 +torch/distributed/tensor/parallel/ddp.py,sha256=uvhaGI7v7IC0iBDY4ZpFyQn7vfLB4btFxNpwg7RfxKI,3734 +torch/distributed/tensor/parallel/fsdp.py,sha256=6eNW_byektIZXSXAf8m66csAP53ZE_twy3JvqqgZl2s,13668 +torch/distributed/tensor/parallel/input_reshard.py,sha256=UVmxn5nAths3LgB_wfweImMfO28MwIetBQR5fT1vVd0,3756 +torch/distributed/tensor/parallel/loss.py,sha256=lsa1kbzMXhOD04Cd04rCc2THnHxq3ap4I1AQKx2aKmQ,17793 +torch/distributed/tensor/parallel/style.py,sha256=mj9SqKR5lHZBf0mpQTao0QIsRVDQaW5yPvIxVwkNS3I,37226 +torch/distributed/tensor/placement_types.py,sha256=8j215tB9OjiFxtBXJd2aZYOfEqS8VHMsU7wt-DupXgs,29590 +torch/distributed/utils.py,sha256=dilLxjUx3BvBxnM6OZoRJf-pjUBIqWuTojsphRI0uhg,13354 +torch/distributions/__init__.py,sha256=FdWgzsV7zpnl3LMsEv73LOOxNCSkciXh9JGuxpiCyDU,6111 +torch/distributions/bernoulli.py,sha256=o9R3dKVrubrzcHbKdivFgj-VmqArucEodUheYgW7yKs,4613 +torch/distributions/beta.py,sha256=LBWFBQNHmtlLjsFIl8rieWS5YPovFrOoC679SFgGTcU,3976 +torch/distributions/binomial.py,sha256=tgfmtOut_7lTPy-RRo1MLCFRnR3lLWrNmcWdchut-kU,6318 +torch/distributions/categorical.py,sha256=OeNIrfTJuxRVMviqqN8JaSnuh9Gp0TOTXmR4btkTcD0,6063 +torch/distributions/cauchy.py,sha256=da6fNeKMIlEyls6zVAS3aOnOoaOnxN7SQitL2LYZbB4,3172 +torch/distributions/chi2.py,sha256=6P2DldE0LM7WYp-YPuAADuE5vNQP-NjgtF6N77nD3S4,1153 +torch/distributions/constraint_registry.py,sha256=Z49QrxE5eJqi7_84YM73khNXMNVGhM1jqb-ovWBKT-A,10306 +torch/distributions/constraints.py,sha256=b0CeEsLXcwQ5BRbURkb7h8TBAAxjon1E9GrcvkCtAJY,21602 +torch/distributions/continuous_bernoulli.py,sha256=zzhpBViwBgDY4DburvEj5nJJbFLAiZ-UvXCCIjYKzZ4,9107 +torch/distributions/dirichlet.py,sha256=dcJPb-1lg3OGWfOVxQvYStmAL7Z4XFcZXBXm916sBsA,4413 +torch/distributions/distribution.py,sha256=durlxRavhuxWnnKFM3Gac8FYZ8GsxpTJ2TOgkryzcng,12605 +torch/distributions/exp_family.py,sha256=G1cjwpbkzkxts5tBDMZzPW-VJKCFC4-DEpgyQfp-yvw,2437 +torch/distributions/exponential.py,sha256=p9OJL4kASA59-0kCuPu43kTWhOboSlZs6wmE2rI7EM4,2695 +torch/distributions/fishersnedecor.py,sha256=rhz8561K2I0JsPAVkicxmANt32TBWXrCt2i_qhal18U,3678 +torch/distributions/gamma.py,sha256=qlIHY8On-fzSYFG8UtSA2k-2jsaPd5jWl6V2ypIzi5w,3908 +torch/distributions/generalized_pareto.py,sha256=rjB5xH0c_MsMBrpmZ7JV7mUhiN1ccIC0rEU7ETG2uXY,5757 +torch/distributions/geometric.py,sha256=plvG1bODbLoSSJLBFKuAdqpqlW1hr2A2_U7DFWa_xkc,5053 +torch/distributions/gumbel.py,sha256=h36xJR-sPrINiR0w5pf8uK9TCkj-m42cDsC1DeMp3ho,3015 +torch/distributions/half_cauchy.py,sha256=0OmOoR0b8HnK8jJBJMMxufoefp8TBpE6km3rFhxPbaU,2608 +torch/distributions/half_normal.py,sha256=WSB04yzh7be1h8CVzg76KjQOsjm-d7nMNhTsbBt_H1Q,2373 +torch/distributions/independent.py,sha256=bfivuQWcufNN10Uxmj6wrVVcXdf4YQz6YrlTLZnNsZs,4982 +torch/distributions/inverse_gamma.py,sha256=sflme_h86Lm3QoXyCrvJJlmBcNO8FtHq4LgdCbJsnB8,2747 +torch/distributions/kl.py,sha256=4tZEFAXi2UKhp2jnnUs2s0CoQNavH4SLKQkbR05T9TY,31737 +torch/distributions/kumaraswamy.py,sha256=t9gC-_SegaLjdDNxFKMZj3io7pNNoJvA0OJpsGWL2iQ,3652 +torch/distributions/laplace.py,sha256=_827qLo2Ue8_2mhVNVE0TvO6yVZ0B3WFwQyl0uBqiHY,3503 +torch/distributions/lkj_cholesky.py,sha256=AEzkvpK7bhtErLxKJuriFcYTtDgiwaXuHwYHh7bqbas,6556 +torch/distributions/log_normal.py,sha256=ikhESLDdUKlmQfUjwyCrCgt_Y8KfmUUcOjG-POx69l8,2200 +torch/distributions/logistic_normal.py,sha256=nb-NhPXcbsbxVU0Ux39yBmdW-ywLuKFKx9Q-kN6O8Yw,2216 +torch/distributions/lowrank_multivariate_normal.py,sha256=gZo3S9kDA_RzPwMLi0uvd1_i-ghprdJ6yJQk5aFo3G8,10126 +torch/distributions/mixture_same_family.py,sha256=50umjGOlOIAgQj5BwULH5roZAOpnKYzzTR3judDt8vg,8652 +torch/distributions/multinomial.py,sha256=giITvVWUQPpQElLUOLSffwdEm3zJD-WvxrJ2NPH6jWE,5640 +torch/distributions/multivariate_normal.py,sha256=fh-Lkt2s1y6oZZeoXkRP-S713WKNamcz0rgApku4WYQ,11093 +torch/distributions/negative_binomial.py,sha256=pjIa0jHNz9zN6v_T24N40iRAkex4yoyv4pQvag-rLDk,5045 +torch/distributions/normal.py,sha256=yEnFp-sFF-EVuSwqsYKOo148kcOlqYFndZ5gT13f8_4,3866 +torch/distributions/one_hot_categorical.py,sha256=C0ZmfOIWxBQ26V_tbyKv2a-Jg9c3VrG9wR7s4V2RsQM,4991 +torch/distributions/pareto.py,sha256=FaJol4Ybao9jHfWHyg5avPHcXp9eSq-UVgT2pwOEzag,2498 +torch/distributions/poisson.py,sha256=q6MWwrNoDY4l9OLUsUH9JP9bJZKqDnFVDEkVMv9WDlk,2447 +torch/distributions/relaxed_bernoulli.py,sha256=YkenCkfnh8lkbCZEq5Q1eeEcGGX5h3gCLhdDZUWNZr0,5956 +torch/distributions/relaxed_categorical.py,sha256=9kz9Xg00-os1y6pM6mIB_Ah4sah9cDoLiLBMfypuOMg,5641 +torch/distributions/studentT.py,sha256=vRbxn6Oc6LA9wsNoyuJIN06HCoCjT4PBmIXdeeVXF3I,4150 +torch/distributions/transformed_distribution.py,sha256=ks5BtDZ5KgXKNLgABUGo61lxP-1pgFXAWbacaoxGWJ0,8874 +torch/distributions/transforms.py,sha256=VZsBzg8iul-D62O5hbGAnPqlY3iop50WacmmHBHQkV8,42417 +torch/distributions/uniform.py,sha256=Ttf0pknJ4p9Db1yJtCwJnOGZDOC8_XWmUqtk5ifD_tc,3361 +torch/distributions/utils.py,sha256=iVAVi0aU3h1Z4fZDqwiT_omlA8vSPSPq3FWQJpyUZkI,8027 +torch/distributions/von_mises.py,sha256=BystBrhGPpaz2wq4YzNu48bTD4SKszJJlk4hkyIBlFs,6325 +torch/distributions/weibull.py,sha256=uowfjPvpgX3Xxud9GZQ6_qdMSy8UTC5TgXcQNiX2ckk,3384 +torch/distributions/wishart.py,sha256=J5YeREw0GXa4ry9HbTQowkrwVQ9-V99tBZILrRhG4pM,13711 +torch/export/__init__.py,sha256=ufgXPfyFLTf_wW739olrWs0ziQd_3f7grCJ0Ksw2y2I,23523 +torch/export/_draft_export.py,sha256=kEmcuG1MzRxImed_mls6xKXWUzOgQb0xLCYvo5NOW_o,19263 +torch/export/_remove_auto_functionalized_pass.py,sha256=R93kMKP2T1lvjrIR77iRnTMsGiezjD3Hedp2NXQy5ME,1951 +torch/export/_remove_effect_tokens_pass.py,sha256=XqNkansSL-MT_iTPkdOaGB3Y3ZcrctECW5VeE-k0COU,6335 +torch/export/_safeguard.py,sha256=7peuxoS51C1SC2h-4XAVDbZIu772gPUzRRTrDCzo14Q,1956 +torch/export/_swap.py,sha256=p9Li6ld4meglSbNvgMgFXbSkT303qod0ogXa733Y8S0,17579 +torch/export/_trace.py,sha256=9tVjHCauLOZtISSXh0FL0PmyD5OkOtP59adKGH81aJ4,88303 +torch/export/_tree_utils.py,sha256=_nFyyQzesztBEyJvXRq8lYB_Yd-9z9QNHeguuEbIm1E,2262 +torch/export/_unlift.py,sha256=UdxzmY3kSFlRALcG6BlobmDKqr7Bh5FfI9nYa6OP98o,17528 +torch/export/_wrapper_utils.py,sha256=pAHvH2i0I5mn42Cpl4oyTBQzJz0rZjBi2siRQr2khOo,271 +torch/export/custom_obj.py,sha256=H6j0qawn-qiMlmafZDdjAuOOQY7YvQ7pbhiFW71hOMY,296 +torch/export/custom_ops.py,sha256=NWOb9vUwSqlIPjNgoBNmrgkjhvuxzv4t-fezC7cfSis,960 +torch/export/decomp_utils.py,sha256=9A9E-fhb9K0qY9cZXhTE1V73XapTrSEIykUHAdPSn50,5518 +torch/export/dynamic_shapes.py,sha256=Azaq45gAaAaNypEL4Rv7zSo9xDdiRsrjbaAHiX5o-V4,53430 +torch/export/experimental/__init__.py,sha256=Q2CZeo3oxwaapJ8Iz-yqQ3VG5LFr9YYqj9nh1gzyUmo,12698 +torch/export/exported_program.py,sha256=UjWy-1sye1DsXK4apL7Upo4HFoOV4C5lBCwVmW2h1hY,67025 +torch/export/graph_signature.py,sha256=9KljdP5fs7lx-lbBbk8YRcdsM-SJjyArlO5PRCOZopE,24816 +torch/export/passes/__init__.py,sha256=YpmQsGtkQjzJUmYV87YbTtmrSg0PTEsJMSwuKRNAE8E,2262 +torch/export/pt2_archive/__init__.py,sha256=vYIe2lq0nWwY77PVEsuIo_0fEWuAeX5E95FhGtXmwZ8,144 +torch/export/pt2_archive/_package.py,sha256=p9sJeAKTYIpyALs1lQtZeugL7ap8RXTcktuJ8YiJQyw,24457 +torch/export/pt2_archive/_package_weights.py,sha256=d9sDT3A2DpgWWF5Bbc4U7i-xFxddV51hupiEejao7Uo,3414 +torch/export/pt2_archive/constants.py,sha256=e5gbXG-DX97luZG5CQZZX6JiaTHGX84wEt-NaMzFsPY,1504 +torch/export/unflatten.py,sha256=JWU8SdQ5AwNHwhf3wC2DiCcYOl9yLwfrU6G_UCWwv9A,66052 +torch/fft/__init__.py,sha256=WqJ1Z1KMnbxX-6KdwopMcr7m-j6R9E0wd-cFlgf9VQ8,55337 +torch/func/__init__.py,sha256=sUjka9l7rIWURFlItERpQgoeiNfWjMi083ISlouT1cg,656 +torch/functional.py,sha256=qrw8NFx2cIQsJTcoQTmsOY-32Ay59RXB7QSsaSOZ6WI,88875 +torch/futures/__init__.py,sha256=XgQWW4_VMsFrbAnv4pfWGHIWZVbQnErp1BhsZMiGZbg,14496 +torch/fx/__init__.py,sha256=bxahFbSP3D5YPnkuLCe2TLjB3WREXkUSAdx_POdSDOY,4163 +torch/fx/_compatibility.py,sha256=eFnzJCHn6QvGLOZjki-ASLJ9osrasA13jblR3JcYS7k,1081 +torch/fx/_graph_pickler.py,sha256=8GZkWKJHY-f0x1vTRDZPtE6GePl0qzTwzwJeV07jfv4,21722 +torch/fx/_lazy_graph_module.py,sha256=qMgzNry9blRh8APXgcQBgSZFRudr4lVY2btG--WSd7g,7158 +torch/fx/_pytree.py,sha256=EdI6dvbZ1PXyhTGzXkHmVWYZ239qDvFSOt6mcqV3jXU,3587 +torch/fx/_symbolic_trace.py,sha256=PjuUNKOG4LDVOhcWEcC_O2pGircYtspFxNPd-6-03NA,50818 +torch/fx/_utils.py,sha256=C2b3HZBaEn6nuwO7XcXP7tCfzmhPxxH6HEqfR1pMcLE,1736 +torch/fx/annotate.py,sha256=3OjVD3HmPinz3R7luOaN3sdUa5QcAGAsJhezXLC8ojE,1256 +torch/fx/config.py,sha256=Vq4ADVOR87qBcbL9BMOX7DObs6TJQGEIiT3LivH4mkg,328 +torch/fx/experimental/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/fx/experimental/_backward_state.py,sha256=TzC9Uin0ccyk7oG5z7HQPhRP6I0_6-HC28DOXpzUZzE,967 +torch/fx/experimental/_config.py,sha256=0zlcvPr2-HCTfD9hf3mOuuThI-vS9_r2sO5Tcq6NEAM,4710 +torch/fx/experimental/_constant_symnode.py,sha256=O8DFcYBF1FxZbVRHCEsM3eVRW4rQ7DxP92PQ8W80xLw,1522 +torch/fx/experimental/_dynamism.py,sha256=d7X-1-ZTN3t5Idxw3Z-zDhEntpO2fjQMkhOiQhRNolo,4501 +torch/fx/experimental/accelerator_partitioner.py,sha256=CiAuulajENzwYEe7xvMTGkh8MGEF_X3BaVms05sP104,47767 +torch/fx/experimental/const_fold.py,sha256=HPo4snq1dxxDDO06nCOzpmtVuIGAr_JcAK6laJ3vdBc,12574 +torch/fx/experimental/debug.py,sha256=i3xyFB3WsaE3k09YInW_WUMTPi9mpNNbfTbB-7jFmvs,811 +torch/fx/experimental/graph_gradual_typechecker.py,sha256=Y_Fmk_3PUyT9Z3c6lGubLmLhdm5zjEyB1pq2UKbkGd8,34087 +torch/fx/experimental/merge_matmul.py,sha256=X6vCCcpQBJK4trJ0BI6khd6W6rUKWteR8J22j4auwMk,6063 +torch/fx/experimental/meta_tracer.py,sha256=Nf_W8JtiTi5SgOsvzUzYLw4a6ovixshru0qpVBVTllg,10577 +torch/fx/experimental/migrate_gradual_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/fx/experimental/migrate_gradual_types/constraint.py,sha256=1eZBF2hRL37sj1UOXKFExPogSsQCsvI7uLbErT4BGXs,17222 +torch/fx/experimental/migrate_gradual_types/constraint_generator.py,sha256=D15HSZ_Wh90oBdQvIVIIR0HFT3XXeAZtO_2bTv_LA1Q,51032 +torch/fx/experimental/migrate_gradual_types/constraint_transformation.py,sha256=47TaVzHkOu0DBXwm6RCuw_CZup3AT2LERl5HdZx49Kw,41000 +torch/fx/experimental/migrate_gradual_types/operation.py,sha256=v7haImk8KCWq9XBmzu2MWoApiGGQsQY4xF-VChtRBVI,287 +torch/fx/experimental/migrate_gradual_types/transform_to_z3.py,sha256=-g7CXC7NOib1wcfGO20IKnrP4RyFeWCzjjfIqzFN9N0,16370 +torch/fx/experimental/migrate_gradual_types/util.py,sha256=vzeLvWEKYcD1mh1bNhNlzKScLZURhem5h5hglJ8JInQ,1365 +torch/fx/experimental/migrate_gradual_types/z3_types.py,sha256=9CzjT7bWhU2CUNwZrQcdI7PDY8haxTrzGBRw_V7X7dk,807 +torch/fx/experimental/normalize.py,sha256=F2yaU-pMUesR-BPiaIWfFWA56a2oB9Wx0kLtdm9Gq1Q,5492 +torch/fx/experimental/optimization.py,sha256=R3zLiZWC9hqEHAT8XG_hWpFfZcVzzppyKwQf6iu9lg4,17686 +torch/fx/experimental/partitioner_utils.py,sha256=OlitD91AGdurF5-P7hITygyWArX5vL9iL95iZlwkGXA,12295 +torch/fx/experimental/proxy_tensor.py,sha256=VfisxnpCjyjIVoy5r_gw2bdAPoXZImU8uAB7HcuILGk,90911 +torch/fx/experimental/recording.py,sha256=Ob_veE0oQNyk11_YK-M3IwSZwsc6rUMvZQwVHSnY-NI,19923 +torch/fx/experimental/refinement_types.py,sha256=888K8p43hadBWu9MPg4lrPeDIF8g9XYRnmI5nnwFYpw,451 +torch/fx/experimental/rewriter.py,sha256=mGtn5wzw-Hg1dxTvs5rWOHv3JRajQvNXXkKaNk8BDik,5468 +torch/fx/experimental/schema_type_annotation.py,sha256=DFGAdzaKvb4IKpuG1cwHoNJMVjeueqJA_Ry9zfnpXrw,5379 +torch/fx/experimental/sym_node.py,sha256=RLyWM7oSXcbQLm9KRgWWa8r-xJFnaZH_Vf-6az4GQS8,60254 +torch/fx/experimental/symbolic_shapes.py,sha256=UUnd4uo58l_kCTOrzCUZuCP6d8ofmISJ-W6W-MY8pLQ,334461 +torch/fx/experimental/unification/__init__.py,sha256=zJTtn828T13Feto4TKixj88dtdoRz5_TjoseKssyzeo,196 +torch/fx/experimental/unification/core.py,sha256=i7z2VsI327nNcC3P7RAsDHvfIdOvsLY8_F-r-aZZOlA,2754 +torch/fx/experimental/unification/dispatch.py,sha256=6KLlCVPNpuiU9kI7LbZKwbhFt-t_qDSSpC0qU8j-opk,193 +torch/fx/experimental/unification/match.py,sha256=nIPkwl-CJykPeZ9s97iMksw4l3OV2eOIBxVaIYBLD5s,3414 +torch/fx/experimental/unification/more.py,sha256=JXGZmsnDHW37S5PUNdaoQcVrCvhjhOHMjj1o4u02OEQ,2962 +torch/fx/experimental/unification/multipledispatch/__init__.py,sha256=xjEjlwV6TTwkgteOREoXUJwMTiNnpJN3YfBALC9eslU,139 +torch/fx/experimental/unification/multipledispatch/conflict.py,sha256=76MhUBPYcV2HWORT9mcWoFACIkQof1UCdXbQWcDuRr8,4210 +torch/fx/experimental/unification/multipledispatch/core.py,sha256=djFh5jndVX6rbRYev0uEoeOIOJFvUK35ykKeCb__qDo,2568 +torch/fx/experimental/unification/multipledispatch/dispatcher.py,sha256=Bb49ThFF_j-t4-Fp8s9Urrk_H7kFzFnlAG8qrSVcyzw,13884 +torch/fx/experimental/unification/multipledispatch/utils.py,sha256=uDXLkIKphI7uPd4RpfSKFXuJTHq-wNQODWp56w9p3qA,3760 +torch/fx/experimental/unification/multipledispatch/variadic.py,sha256=irgdEYhgS2XLxgdZZLPrApQPiB7c2wh9jZt4J_yu4Xs,2962 +torch/fx/experimental/unification/unification_tools.py,sha256=uz1BytKNE5rzW327q9PWOZUhAeybmBOFPDGwRy-OOeo,10567 +torch/fx/experimental/unification/utils.py,sha256=ApZbWRa4eTG_LiIWH7gqPXmV14pBxIA_A7zFJSHHFSQ,2937 +torch/fx/experimental/unification/variable.py,sha256=w_B_t56i8Emw8xjoOIOMCnB_cMlA8oherlZaGQWRnn8,2065 +torch/fx/experimental/unify_refinements.py,sha256=96Sdy8EwXpOLfTBZQiN9JjMvvf00StIplFinBV-VU0Q,3151 +torch/fx/experimental/validator.py,sha256=ryo_JisqPGE8cSBcLZl5AoQQThXkNk1nZdcUnQ4AoDk,34006 +torch/fx/graph.py,sha256=E0WQn3bCawSKCccYiR-WMg0y_RzPPRd82Tgj4YExnh8,76317 +torch/fx/graph_module.py,sha256=436sLuchSnjgbrfgrKV5PxC3-SHL1H1qlDkroj_CTXA,42260 +torch/fx/immutable_collections.py,sha256=9OeeV5ZPLcswvuNEo7xEepwdUJsW0JYdIwaNNYCOKG4,3269 +torch/fx/interpreter.py,sha256=gk0YV1xLE0fZxl0I-TgCzVieT1F9qLPxqZuZuSObHPw,23170 +torch/fx/node.py,sha256=yMmlINJ9Uk2W9mupZMS0OYKf_UtBimnBuo7iFhHLrcI,34216 +torch/fx/operator_schemas.py,sha256=jSyTAm158NrKkxCNsdjS2oWWkT-6NxHUWD5izG2RCJ4,21854 +torch/fx/passes/__init__.py,sha256=-V2sH_a3cUE_lMEZNGjtu61tAIlOuLjdVVIOlR8yRbI,240 +torch/fx/passes/_tensorify_python_scalars.py,sha256=8P6iMGwEztqjxjUt805-LxYF5eR-71dnCG9bcPOuSlM,16090 +torch/fx/passes/annotate_getitem_nodes.py,sha256=6rUk0XQnHjbbUMn128aKbRwCo66dZ7LPXWxE7QAIb6o,2761 +torch/fx/passes/backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/fx/passes/backends/cudagraphs.py,sha256=R9_cp3JxDlzjsEbvFJwOs0-zH5icJdyjzPysb4rcjhw,2083 +torch/fx/passes/dialect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/fx/passes/dialect/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/fx/passes/dialect/common/cse_pass.py,sha256=CBRTo0Hw3MOIzh65C-f-l-eJ3nWt4IBgUHPg_klizyA,5248 +torch/fx/passes/fake_tensor_prop.py,sha256=2jMqrQCNPaZh0V5pXvwD5EEl5fN1_a6mmQhYK2IEPs4,4200 +torch/fx/passes/graph_drawer.py,sha256=FT1YZJhbXIZwIhMy_Gpe5knKl8AdGVAn7zDgnT9f-4w,19112 +torch/fx/passes/graph_manipulation.py,sha256=79lTwszALmTfBBC7Svch0J4jYSDFU5DmcoUbIYY9GTw,3964 +torch/fx/passes/graph_transform_observer.py,sha256=47iXcON6CNJJCRsKZq5x9TuwHkgkWmSQ8pvAxcEQE3k,7392 +torch/fx/passes/infra/__init__.py,sha256=UuBORluunUu4B8DKb0OlqkNIm71XUW_ZD_utIvqnBLs,27 +torch/fx/passes/infra/partitioner.py,sha256=EUocnSiwbxTyjkFGdPlzEBkPKWt1GP_3512ECmf2f8c,16459 +torch/fx/passes/infra/pass_base.py,sha256=YY_3_XKtZgrFK2Q-OvgE51gE36sjW7fU-Zg4Pc6odnU,2501 +torch/fx/passes/infra/pass_manager.py,sha256=C5xXQPI9jI8ZGMNGeQ_bliewAlLune8lw53M732I8lI,10345 +torch/fx/passes/net_min_base.py,sha256=GMyu4KG0X0ytxhM_kNkufRMOuGWW_lZ1VDCpAgQ5iOo,36570 +torch/fx/passes/operator_support.py,sha256=KUeMtZH3xsjtx-CApqYzX7RAsuvdj2FKEDXp--SgqXU,7632 +torch/fx/passes/param_fetch.py,sha256=pr5lpig_YYkcZydc__mD5TFmZbdFW-vxj2136tVdjWg,3714 +torch/fx/passes/pass_manager.py,sha256=hNhgXmG6A1wVxAYLiXY903yH0pn6QS7lkeEs7L4NNU8,7058 +torch/fx/passes/reinplace.py,sha256=4GgYMDzNuLh-L0-KhijXdVwr_m4aVolxLWkXQoeDYek,34529 +torch/fx/passes/runtime_assert.py,sha256=xe2IHT48btB8dz3mzEPLGJ7Wkhs6kGVToNb4UxgdABc,29065 +torch/fx/passes/shape_prop.py,sha256=2FrWgQFj58Au59mP6uBjGuAuEnux5-niRX7YL9uBfcQ,8324 +torch/fx/passes/split_module.py,sha256=yrwDxXRi4_VJUs6JYlYrIDkkK5HiurbocIbi49UutL8,26769 +torch/fx/passes/split_utils.py,sha256=Ek_iDBlI69ZCHQjkCTgT8d51GA2IGbWZBsVqGJ6h_zI,11473 +torch/fx/passes/splitter_base.py,sha256=xgABslcYDQQRec3fffFB35pcIVM1oUBwz1RqTUFI3Eg,33809 +torch/fx/passes/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/fx/passes/tests/test_pass_manager.py,sha256=_KIwhlzOTY0XQb2WeaYYOFuBUKDtLFRqYeSxHVrS76E,1873 +torch/fx/passes/tools_common.py,sha256=cS3inbKVleLTUeXFHKJC3f11W9owCI0irFNfz-FD0HE,11202 +torch/fx/passes/utils/__init__.py,sha256=6xOYtGu1w_nVvuaxvBoQkSRtxkiIkPh-MzuXjmNC1hI,74 +torch/fx/passes/utils/common.py,sha256=7dLH_IZQ1hl9UZXCnUHaRgLJFKu1YjO39Qp8XuytxPQ,3107 +torch/fx/passes/utils/fuser_utils.py,sha256=ykf7Omoxsm3hUdJZE91wYRCcqpL--_XOGp11f_HDlTo,9788 +torch/fx/passes/utils/matcher_utils.py,sha256=wB7CrwKdxBSKT5b_8jYuKlFg41D6s-p1pwZmf1SZ0LQ,17242 +torch/fx/passes/utils/matcher_with_name_node_map_utils.py,sha256=yeg0K16eYOnz7xXwJuanYWnSjpWQBe9epUe7Uhr5K6U,4197 +torch/fx/passes/utils/source_matcher_utils.py,sha256=URLmh2CZYZhshjeR3tGgZGIGq4GjZ8-y75fxP1s15L8,5768 +torch/fx/proxy.py,sha256=rVKTFCyYuy83Fq1tDZeDC3fZJx1oRpCHHt_zJYDxYzs,28504 +torch/fx/subgraph_rewriter.py,sha256=ls5TZqYe8Cvj-b9J8dhtN8a1tuaHl_elIkU8awfC4A4,15905 +torch/fx/tensor_type.py,sha256=iinrL5hRbvDcMs4cYVKmfYVr1ci6YrQiMmanyH0DIIc,3009 +torch/fx/traceback.py,sha256=Zj3KhqI5Pb4AGECpdOHJK-ZFLaOLgVgoANH9d91xTyg,7325 +torch/hub.py,sha256=pVPas7uNUbyanjcnqiUOVM1bqyviDj5ES4HQBNbSlRw,33459 +torch/include/ATen/ATen.h,sha256=mkjW3p2EH-mgdyzM_Nn8t3Mu9IneuDNjDyNHTlRYSrg,1107 +torch/include/ATen/AccumulateType.h,sha256=8d773CVzYqvlxFMLr1WLtYiA2NVSK0EdoStGbPbefAg,6111 +torch/include/ATen/ArrayRef.h,sha256=Oj53usfNJkz8L3fSOjmE20rpC0NUjhOVz4bmu68R28w,44 +torch/include/ATen/Backend.h,sha256=uKLWU9cBX4ldaPyhSACkAAe1BDHaJFKAS8yVHexzEuo,43 +torch/include/ATen/Backtrace.h,sha256=iQwNYKFPK-sNKn5Ngwy2vzkESKV84wJSLIQD1O7GGxg,46 +torch/include/ATen/BlasBackend.h,sha256=efbXIXni1yFbXKaEAHAKPQyLdLwtL7K7NMgrE1347V0,755 +torch/include/ATen/CPUApplyUtils.h,sha256=oAuezl8Y9XZPmFEPrh4oF8YlwCv0fp5ZXSEH1k2aAWU,10634 +torch/include/ATen/CPUFixedAllocator.h,sha256=256NsaOebpGlq051xlaG1-qVXBJDexEZlZIJ3uq7SY4,882 +torch/include/ATen/CPUFunctions.h,sha256=1sgkSeDNe-aEjWax1mFeJ5d5HkeiG-GEDPfYr_SaDDk,1954 +torch/include/ATen/CPUFunctions_inl.h,sha256=R5-pbb7H-mANQO7WyR3_NYo3ytRWix2K9-y0u3v3_PU,27291 +torch/include/ATen/CPUGeneratorImpl.h,sha256=7odrNEIgXf-N7AQJmE8dsYgrNKvdiaTaW3kXQFIMiQo,1527 +torch/include/ATen/CUDAFunctions.h,sha256=1hx0BqsL13QwJEjYlV_YWSBw5Aweo2rjKuXAUAPALFY,1955 +torch/include/ATen/CUDAFunctions_inl.h,sha256=RcW8W46MfolhqFJkChnPyDOLYLsMWpdd0ko78ujYQ3w,32620 +torch/include/ATen/CachedTensorUtils.h,sha256=A7gaS6-sexogUv0aYx8H9EYbRZxvaNXyNtgy4ovaZfM,1007 +torch/include/ATen/CollapseDims.h,sha256=YcFbH1j-lkxTcwHr5trswl3WJEZTp8VcW6sfsm11_oA,2560 +torch/include/ATen/CompositeExplicitAutogradFunctions.h,sha256=_61JCORNGlTwh3LSUebe-QyUeOUW703Bk0QeZEF9ZCs,1976 +torch/include/ATen/CompositeExplicitAutogradFunctions_inl.h,sha256=-lkbGJawTkN48xvSR2cDCVyptt1VcDEq-zcDc70z1HI,40675 +torch/include/ATen/CompositeExplicitAutogradNonFunctionalFunctions.h,sha256=Yte08jyGIy0epK4J9_PsmIjjrhrTWwOR82C_SNuuqL8,1989 +torch/include/ATen/CompositeExplicitAutogradNonFunctionalFunctions_inl.h,sha256=jnt23hMGJcAgfx6SBp52_mLThvBI-AzDPfKFsWEIsPU,26244 +torch/include/ATen/CompositeImplicitAutogradFunctions.h,sha256=ashjfHtnYtMvgzkDnJ1QutpAIOTMbJ1RaZlW_3DGA1E,1976 +torch/include/ATen/CompositeImplicitAutogradFunctions_inl.h,sha256=FtUUahIAcff36_bjcimQ2s6qHOUIq8S8OkY9wrJwZcQ,34943 +torch/include/ATen/CompositeImplicitAutogradNestedTensorFunctions.h,sha256=9M_qlGSuUPFomzb5lwBaQl10CYYGE046wscf8M6jQz0,1988 +torch/include/ATen/CompositeImplicitAutogradNestedTensorFunctions_inl.h,sha256=zPpmzVcthe0efVJmcYnz1EnWbn04TkoWlfsQ2-qlH-s,1126 +torch/include/ATen/Config.h,sha256=IGQBGHuQWjq3tAT54HC8f-h11sDtKh4Xw54WOG7laNQ,769 +torch/include/ATen/Context.h,sha256=HwRg-GPIWsPrxated5ueMa-QZX96pyePTKzDdRhh28w,22371 +torch/include/ATen/DLConvertor.h,sha256=4ZsSj6XAVUpXgUCzIiWuS7IE1pb2-b8Xd4cfb7y5O_I,657 +torch/include/ATen/Device.h,sha256=woU2HH6ul-aOLsILb-xFTxGbtA7mGnaMaJzzB-CrlBI,42 +torch/include/ATen/DeviceAccelerator.h,sha256=iEP61HhrhqO6hYXrYJ-VZZcLdcoG7SZL7DNCQ457t4w,3059 +torch/include/ATen/DeviceGuard.h,sha256=QLQGKvzsBznkReUHcsy1VetO-lw8foQXW_uhz9rJ2Gs,1165 +torch/include/ATen/DimVector.h,sha256=hDtEu7TLm-rmIlopY3DudvGcP7BopSyWYAoce6SRrKM,46 +torch/include/ATen/Dimname.h,sha256=ODvWq78JatYnrvxjZb8taxy-nOBOC6VE0sZnSpxiwFs,31 +torch/include/ATen/Dispatch.h,sha256=ihQ6k4AHy19ZTesuo61oIup3-WiChkf5flQZHBS2ybo,40146 +torch/include/ATen/Dispatch_v2.h,sha256=hWSaCK4_0JDBR6fS4GOh5L2XP3tSRx-KFJGxp_jWCFs,60336 +torch/include/ATen/DynamicLibrary.h,sha256=NtmAf4Y1_Bhzd7W1jAj4xQIN_EzK0GbysDx1F4oVSSM,679 +torch/include/ATen/EmptyTensor.h,sha256=iZqDKHcinOMLYmj1zOwdh9vFirz_WUWkP1csVsELkBg,4710 +torch/include/ATen/ExpandBase.h,sha256=nUXXodL4QrMGHb0xV9QpDjEJsG67gD6GHosIfNnrfxA,914 +torch/include/ATen/ExpandUtils.h,sha256=IRLMtSnIalYyTo4juPj78E0wPEMK-7THdOm-c4QLGfg,16681 +torch/include/ATen/Formatting.h,sha256=u9t8xVe-WAEXzWvHqFJ93K566ukel9wsbLhSEUeEqyc,34 +torch/include/ATen/FuncTorchTLS.h,sha256=1Rh_Sl0r0UOMpwzcl6_P1GEJXBgAtIGra6xE5t_B69o,1816 +torch/include/ATen/FunctionalStorageImpl.h,sha256=SrJ3IxrPQ69jrLrnqY-Ld-bdwEBhz2m6eLXtnQQhGtQ,7699 +torch/include/ATen/FunctionalTensorWrapper.h,sha256=Mfvzc64_Ab6z4e11t-NKLCEajHK_wwP4ZaTMnbK76k8,17766 +torch/include/ATen/Functions.h,sha256=zOfgQI7Mw4llCsGM2uYTo495H1CMSG55qFlbdsx3y-o,55096 +torch/include/ATen/Generator.h,sha256=KBeu-DIZBnbAWTvlDIPNIA7PMY3wFODHbAR0l-kToLc,46 +torch/include/ATen/InferSize.h,sha256=m09_Nnka7yDVadx5Qr0uPS19ekqUwtWoqJr8eeAYscc,2873 +torch/include/ATen/InitialTensorOptions.h,sha256=Hwbswfmyx2diKA1DCTPukc246Fnm7kn_MOVXQmVrVDw,439 +torch/include/ATen/Layout.h,sha256=l9TpZf-gLtmQbbV7_dn0UFiOuV88PVS7V047YMCtM-A,42 +torch/include/ATen/LegacyBatchedFallback.h,sha256=QNBsZOnkz1buoh0I2HhKGyf3uxGh8NGcHq-AtADej7U,974 +torch/include/ATen/LegacyBatchedTensorImpl.h,sha256=kJQM9K02HJN1iNDKTLlIvBOe55VDkT2Zmq9qE0R1ZCk,5544 +torch/include/ATen/LegacyVmapMode.h,sha256=w8ZTj-VP5xE5NMHEAGXqZE64gMm-i141FiOdWdNQU7Q,927 +torch/include/ATen/LegacyVmapTransforms.h,sha256=g0lBIYJ793Z5bMsvchyDxfExUt14w8ghWbjB-6t3nfU,7815 +torch/include/ATen/LinalgBackend.h,sha256=8FGeL3F7EUk5i_VVs5SHukfJhAQuXi7WtN5BNXXhDlw,719 +torch/include/ATen/MapAllocator.h,sha256=Im-laCx0FzaQv6Lvq3nikLaQJEB0Rel8WZNzVqsAEPQ,3626 +torch/include/ATen/MatrixRef.h,sha256=smOpMIy_-dyGqiNuoHkAueiXf9XzPFc781gsGm3pTlM,3006 +torch/include/ATen/MemoryOverlap.h,sha256=VMIgtWjB4dUQORQL0AwDnVO7KBVmGYqQTaGhESKS5FM,1287 +torch/include/ATen/MetaFunctions.h,sha256=OaAEO2SIhM252jC0YqyLZReeVbr5SX_Kl68kDrpDY9s,1955 +torch/include/ATen/MetaFunctions_inl.h,sha256=BTUtFqSn9BPE2LG_9si5K9myeeI6RpzgP4K2GUbm6Vo,15908 +torch/include/ATen/MethodOperators.h,sha256=zUlTJXKFi8LTThmkJ1ChMr33vmdTqoZoLjtMMeQUr7Q,15445 +torch/include/ATen/NamedTensor.h,sha256=DMs7TFFl5X2V_3sRIg9-qaiiC94x9s0n0SEdYf2SaHE,35 +torch/include/ATen/NamedTensorUtils.h,sha256=w-7v55EGa3EOn_jhvDyvCNz_nbTt4UnqmjRbx89nr70,6794 +torch/include/ATen/NativeFunctions.h,sha256=3xvg_Otn3JvtC-0_5BoSObVqe1NS-IaY-KbIZ9ooJhg,60823 +torch/include/ATen/NativeMetaFunctions.h,sha256=UUhArrQXObbGt7MZ53DR_SYrPc7MZQ1-DsuXiQKvz2I,57330 +torch/include/ATen/NestedTensorImpl.h,sha256=ny6fOf9Ah0DYzrMr5sTPyeGENXQePzGBeZdY21jhQzE,10222 +torch/include/ATen/NumericUtils.h,sha256=KsKuwyXANr3KcNudnqxhA6bE-uRNj2Mu3HXILz3ECvc,5137 +torch/include/ATen/OpMathType.h,sha256=QhaD5M-WJwGdkg6mEhZrs_Ivj0odTSS6qBBl7-QytwA,1581 +torch/include/ATen/OpaqueTensorImpl.h,sha256=xir-OwLx0tbUST0XeQrjXoBG5pL8oYzh-iE6m_yzFQE,6815 +torch/include/ATen/Operators.h,sha256=AZj_FO2K_YgvyK6mX98u4PHsiKy6U7XFX5BIQUghiyg,58911 +torch/include/ATen/PTThreadPool.h,sha256=X2FolpcFacvui54S5s9CQPBQ5gvbG8VoF5sH1jkGtkI,391 +torch/include/ATen/PadNd.h,sha256=hYs6qRjUkLQ_KP2BeS5uAY08DuQboctf7nUMGezZTUQ,126 +torch/include/ATen/Parallel-inl.h,sha256=P18LiFR88qOlKWs3Eq6bp_K3si6VmNvy02kYsIT3tHM,2293 +torch/include/ATen/Parallel.h,sha256=mn4vwhdioBj1IdL1e_czr1Z8cW84yyJMgsn1OnHJgJM,4753 +torch/include/ATen/ParallelFuture.h,sha256=F7kouoivcUuH2RnMd8_CcZ89KuOOrt_z_0BGnM1SM-o,299 +torch/include/ATen/ParallelNative.h,sha256=qFCBu3Ojiwqr5lR_vQ0FLSUdX9bCS5LduLvmuPJeVvA,292 +torch/include/ATen/ParallelOpenMP.h,sha256=VHE3xZuh-I2ijBlTBpsVUIAUfMpO3XQeajMJ7t0I-ao,1283 +torch/include/ATen/PythonTorchFunctionTLS.h,sha256=5mAho8FnYTQU8IyzgcwjBh5xH867BbAjXiI52-ZX0fw,1193 +torch/include/ATen/ROCmFABackend.h,sha256=z0sVBCR5nEi7my9esnAb6xdyiv3BmdFaTEVWaBWMX9U,723 +torch/include/ATen/RedispatchFunctions.h,sha256=wyIgZaxJaNSYp7pW24y86TrmZ1djOn8vnAe3fj6tfzs,2212938 +torch/include/ATen/RegistrationDeclarations.h,sha256=_eMXOEc3s-MmNWaesjZDoPffx0AbxLjh4Tg2tfv_VlY,914053 +torch/include/ATen/SDPBackend.h,sha256=qO4yBronPaPLlhGi2a2R7rijlC22PtWVGLQPWOaBFxA,253 +torch/include/ATen/SavedTensorHooks.h,sha256=DgRabpqelZ6_nZR_NB0faNV7eTaroKZLcPiBAvRqG7A,2486 +torch/include/ATen/Scalar.h,sha256=4uRhX5Y1wZouuAi4M8BriFoqJziRckfzIh1o7E3YS9k,44 +torch/include/ATen/ScalarOps.h,sha256=LAi47PbpJGUvoS3TObNAzA3mzkKbRCs8mA0XWuTPJBM,1595 +torch/include/ATen/ScalarType.h,sha256=YpUjgU8zTuDmlNt8pnUpNYjKVs-sJ-YUs4enjKJr72k,129 +torch/include/ATen/SequenceNumber.h,sha256=kkx0_BUyfdTbwboiGso0GwMNJG_KMxCjjibslHit6M0,333 +torch/include/ATen/SmallVector.h,sha256=b0zRaURtL0paOFhxGgB4flh3HczjPSX6gQ_GQzOOtHs,47 +torch/include/ATen/SparseCsrTensorImpl.h,sha256=mO4litPuIqLgmHKOQLdwZcrRB8ZdEyCfK52JPs35u2I,7109 +torch/include/ATen/SparseCsrTensorUtils.h,sha256=YAZLqCXjBFLBWobFFxLkQp6d7WRSTWu4aKnd6PW_Jqk,17774 +torch/include/ATen/SparseTensorImpl.h,sha256=sF4xVfLp5t3h7KrF4MTWddnqcQx2Rh3kCNLF9BfLwlY,15308 +torch/include/ATen/Storage.h,sha256=EgzD3SO1khvg7pZDcTsqoI0FFSlsZobb6FadLbAhJ5c,43 +torch/include/ATen/StorageUtils.h,sha256=2YW2GztTWHfE493VWxPhtJDvHpExCtwFl69ZTwk_OzM,1308 +torch/include/ATen/Tensor.h,sha256=ercOzXbYX_U4x-QpJ9H5rNwZ8kR6vku_MYQlzRInW28,44 +torch/include/ATen/TensorAccessor.h,sha256=0oPU5QjZ5q_5qJ54cCEa18Zas4Z_IMTVAT09j39idII,51 +torch/include/ATen/TensorGeometry.h,sha256=zySlELjSV_tt706Wt462lYIezLRZa2nwn56pouA9Hls,4556 +torch/include/ATen/TensorIndexing.h,sha256=XVmNusjMdfINiulQvBeYWMzuQbg3oKSYAOyRudVCG2c,24120 +torch/include/ATen/TensorIterator.h,sha256=jcwmpATzoUD8lSP3da_t_bhELEcXhHO-o-AMPMLW-pI,38878 +torch/include/ATen/TensorIteratorInternal.h,sha256=AJwnjP_kN7rvQREEyoH2iOcRTow0xL6Vp5_NCWXntQI,1931 +torch/include/ATen/TensorMeta.h,sha256=s1jIro_iR-HwLXP6sEIRNRz-sOKQeWzEQCN0lNtfW8E,5034 +torch/include/ATen/TensorNames.h,sha256=PZO12mzUUHryHxKpUHU86zIWkFRjRfz2tVzrRRPV1fQ,2571 +torch/include/ATen/TensorOperators.h,sha256=jj-erbmyn_-Vt12rreAuRpXG1iQ-U6qwLSsy2c9Q9Mc,2491 +torch/include/ATen/TensorOptions.h,sha256=0YGOolxkjb6QzEmeZ6xuo0bamoTct5GvrHAtPoMH-fU,49 +torch/include/ATen/TensorSubclassLikeUtils.h,sha256=ANoBU9K1Tmnej3Or16DASLt4CjeN0EO-wQnVWDTI07E,3229 +torch/include/ATen/TensorUtils.h,sha256=ytjTPAxmSIj4FdOpMG47kMtcQ8WwakFLtAxadkJtzOo,5958 +torch/include/ATen/ThreadLocalPythonObjects.h,sha256=EqG46_18Hph7JIFewnv3wiJlY4j1uowbGNa3JR23xUM,607 +torch/include/ATen/ThreadLocalState.h,sha256=x0Sar4-xyyK66gL2ruSef_JRl4xY1kGY_DyhmydqnhA,4309 +torch/include/ATen/TracerMode.h,sha256=kVg2ISLuWXLG6VkfKTDhQu6INAkEznaYN3C5vIlV0sw,5508 +torch/include/ATen/TypeDefault.h,sha256=7OqeZ1kt2k-LHMKNmrRTajR89ZmZqfsdr-bict7bwlk,443 +torch/include/ATen/Utils.h,sha256=TBfcxF9Yz8ebFrCzbeYequvcn2Qv9vU6usRbVHuA6FM,3564 +torch/include/ATen/Version.h,sha256=sTW04hFh7RGM5RkbXoQKLDcwTP4Ks7GNDyTLr3tQJdY,384 +torch/include/ATen/VmapGeneratedPlumbing.h,sha256=Uvahk9Bwi1po_70FUumjqRbMiIdPb8U5lyuNY0sMSVU,1748273 +torch/include/ATen/WrapDimUtils.h,sha256=aRwzNo3HBe92epQ7SMowzvQAz4fy3Yutbz_kvKu_qkw,4867 +torch/include/ATen/WrapDimUtilsMulti.h,sha256=1i_cHZn16UOSbcVH9A1P3rzHPlGt4T610zycW_Pbfe8,1075 +torch/include/ATen/autocast_mode.h,sha256=ZQ6LCyUKnHyMc0px08fk_Is8KopNDJTN24LxEQcv5b4,41240 +torch/include/ATen/ceil_div.h,sha256=TtkKnqzlAlpfznlNBIDaNUHxD8q1QRERVklj-VEm5Q4,497 +torch/include/ATen/code_template.h,sha256=A7vnQAdpQzI2ia5wk0z4Yv3S3guyN4Sg0dRqzkHeXyM,6951 +torch/include/ATen/core/ATenGeneral.h,sha256=dcnASl2yEZMXGYbsBQkH1W-KLBoQTSSoEfujumNJHjM,45 +torch/include/ATen/core/ATenOpList.h,sha256=aTsrO4WWb4s2HIBiOyha28F9RXajxvzINAGv3eQ5AEs,246 +torch/include/ATen/core/ATen_fwd.h,sha256=dj37GXi3sI-7Hoz4M4JLEHO2wh-cIENzENiDcYIZqn8,1024 +torch/include/ATen/core/ATen_pch.h,sha256=SbuCdS9JFp--ii1-DOsb2kP3Wxps-MR88fxFHF6Q5YY,5078 +torch/include/ATen/core/Array.h,sha256=Od99VeZg6tGKmLtz7-E7YTUHZtI10rmVIPpQPY7pHl8,1134 +torch/include/ATen/core/Backtrace.h,sha256=Vi1LiTrJX5BY0ADLxoJkwC7H27H30tTPM89xZ04A5GU,59 +torch/include/ATen/core/CachingHostAllocator.h,sha256=yRiZR91h5MD0dx1zszL7DhiWDom2gzdJ_xypEG6UJFk,27044 +torch/include/ATen/core/CheckMemoryFormat.h,sha256=hZxcTqOeRYgQEG3GnFxd-qqcO0YyMSqa_hojO7ych50,790 +torch/include/ATen/core/DeprecatedTypeProperties.h,sha256=CCHYGMiKiIY2LR7xVlHYrIztBDFrKy0T0kYXV1Kd0XQ,3879 +torch/include/ATen/core/DeprecatedTypePropertiesRegistry.h,sha256=BGXyKbHymMH77T9GtghhRz-FVnQThWT5WWLvuEqXfxE,844 +torch/include/ATen/core/Dict.h,sha256=TAcKLJAhnABrCAnHY7Pc61QindHDywoFNASj3S8rxgA,13276 +torch/include/ATen/core/Dict_inl.h,sha256=xF1by24Q1Oi7oYF1QHHcxx39PJVOIKvKzycAc9XWKyw,7462 +torch/include/ATen/core/DimVector.h,sha256=yOKoGLTzpQNSO-5Dc1iemVnT6Aa9mnjwtT6JpBhOfXs,279 +torch/include/ATen/core/Dimname.h,sha256=zzEGqv_P0b4bKxzX7y7BkCpT0xdi1nnyZVZp78hm0Fk,1167 +torch/include/ATen/core/DistributionsHelper.h,sha256=PS4La7YzyLZg4nH8JzZusw3AWG4oAXaqqZhFPdLjnVM,12603 +torch/include/ATen/core/Formatting.h,sha256=2-RIUD4XKg4X64G3OPnessK85uyo5NioFstRDHTFtxA,693 +torch/include/ATen/core/Generator.h,sha256=lZme09d3Xrc7WzCMhidsnLlFBEAGHHWoI2XCpslFxDU,6406 +torch/include/ATen/core/GeneratorForPrivateuseone.h,sha256=kqCjGBAiRE8mmtNaBYu5IGl7nVrVqz1fvEgrWXhKdLY,1081 +torch/include/ATen/core/IListRef.h,sha256=6pc_a0aukk03VIB4orzMbgM0V_b0moOjFhu5n4ORb9o,20936 +torch/include/ATen/core/IListRef_inl.h,sha256=sXpRt0Trd29KUgesqJ8XqG_4d_G9WKcJ7jq0Kae_M7Y,6205 +torch/include/ATen/core/LegacyTypeDispatch.h,sha256=OtpiQhk03P4M9Cxv7lihyc7sTNkEvbgyJSCyHzwh1dA,4857 +torch/include/ATen/core/List.h,sha256=OKes7RLbknRpKXUPdFZuRgfFFHPlylInZQD8ee7r7Oc,16078 +torch/include/ATen/core/List_inl.h,sha256=UqCebA1-JA9LVsX6GqF0L_oBdEFtc8utJ0A8L83Bdzk,10752 +torch/include/ATen/core/MT19937RNGEngine.h,sha256=UE78lVAVtnNeB_XkGLD08Qaj-mW678NMmy7tf8qNKH8,6510 +torch/include/ATen/core/NamedTensor.h,sha256=tsbKsW_JwjIDYcR6kymTEeTR5Vg3kEaKLQFrvFEmPuY,5232 +torch/include/ATen/core/NestedIntSymNodeImpl.h,sha256=xMw-QFs1uObzHFdXyXrKNe2GOAXbwMHIgnTP35Y87WA,5960 +torch/include/ATen/core/PhiloxRNGEngine.h,sha256=SUmcfyqkrXIxJxPt-cU-9w8XViPVxn_jED3sdXI2Csc,7714 +torch/include/ATen/core/PythonFallbackKernel.h,sha256=h0wOsEZyH7YzkNWf8zudeSZwdEQuSpBUmeNbY0zb_VI,1094 +torch/include/ATen/core/PythonOpRegistrationTrampoline.h,sha256=xedhMKWYJL6qkxzBlAICSiJGHLjX_QUlU9hwTcjHSJI,595 +torch/include/ATen/core/QuantizerBase.h,sha256=57UPm5ypcrZIqsp67oRyevImQUd9meaGrmIf8P7zuWs,2687 +torch/include/ATen/core/Range.h,sha256=v_kVnAoeiHq0eRTodrFqDV8nudjBOX1mwhdeiDrJRt4,418 +torch/include/ATen/core/Reduction.h,sha256=mrCDtCU9J2CCzD7EWxvijtEmbGxwn1Szr_pC8zkQmbI,399 +torch/include/ATen/core/Scalar.h,sha256=6_8TdN11df5vabWQCR30zjSFTLQbwnsUMTWfzKaM_sM,29 +torch/include/ATen/core/ScalarType.h,sha256=o6WgbV6_nD4gO6tVJo7Hx_NQfeXC-iB3TFXbfRusbBI,33 +torch/include/ATen/core/Tensor.h,sha256=rgPozZM3NwUArxc88XqLqZndFkmfGUf0i1Z6NK_geiE,2403 +torch/include/ATen/core/TensorAccessor.h,sha256=FgIzI9GYFRcuVd47Zzz4LYgEXBtN-ci8mzCGElS63s8,10405 +torch/include/ATen/core/TensorBase.h,sha256=Yzs7BZDfgVQouvchYfZci7eJlXsDDmFof7AfKidQSnc,38064 +torch/include/ATen/core/TensorBody.h,sha256=gSfsKekFFK2Ap5a0e5b3iD1_2Z1VRw-ebfDUQafiOGQ,292729 +torch/include/ATen/core/TorchDispatchUtils.h,sha256=1mDjM9W3xmII3IT2vznD_R7hnEQkp9lDKs_KOy9LwsU,484 +torch/include/ATen/core/TransformationHelper.h,sha256=d7p_LKAoxSWPjPDPsmNU9xoqkzaeC4LI98Ccococe0U,6854 +torch/include/ATen/core/UndefinedTensorImpl.h,sha256=gC73nEbVD9sYFdMxGPovAHH997995eRiDYXO3S1y5Uo,42 +torch/include/ATen/core/UnsafeFromTH.h,sha256=bQyv5mA6MEL7f6hp0LIQYaHBaQVilgt_frmUzNCD28c,708 +torch/include/ATen/core/VariableHooksInterface.h,sha256=-p0SCmkGH27Sb1MHzDd2sUUGJidjzrE7UynyPhKt9jI,3538 +torch/include/ATen/core/Variadic.h,sha256=ybiXfI9v-uojD-RMQVKXDLxm3w3c7g1tqy_zJrOHxCY,2380 +torch/include/ATen/core/Vitals.h,sha256=xkXsxOYc2yG4c32PxceZcjIqII1mLfEED6equlvRNiE,2433 +torch/include/ATen/core/alias_info.h,sha256=kkTqA05isaNXjpDDOmJdSyTfbJyATXEMbvTgC7sfAbQ,4582 +torch/include/ATen/core/aten_interned_strings.h,sha256=TxWFFa8cZNwSTxPozsaMp1z7ZJiEGFQC6a4pvF64Dns,56506 +torch/include/ATen/core/blob.h,sha256=HYs6oVb50RbJNCT3N417hux0J0LTKaSlX7fSRiiU_4k,5244 +torch/include/ATen/core/boxing/BoxedKernel.h,sha256=eq-zonPa3b2XK97rBAn-9PCm5UIeBgeck9cYZMjgw8s,8137 +torch/include/ATen/core/boxing/BoxedKernel_impl.h,sha256=d__NBn7jBD1RQ9KSPc1YfDIBLXkCCZcOx_GeUGL1DjU,3255 +torch/include/ATen/core/boxing/KernelFunction.h,sha256=gNwGeVHlGvVlpDAqhPpflUEbleuVA2A14LWfbePm9FM,8725 +torch/include/ATen/core/boxing/KernelFunction_impl.h,sha256=vXPAvnaRRHuws26COYTu-i43CdWRX6zs6UDbdNSnsCs,11693 +torch/include/ATen/core/boxing/OperatorKernel.h,sha256=kN8kQJOJ9t018qWB3CwAcAhCaxt4W3f6tGkR__zWVYw,691 +torch/include/ATen/core/boxing/impl/WrapFunctionIntoFunctor.h,sha256=-x_47aEjOp0Z-O3oO5FZRhefWHndRxb2LgV-P4fWXwQ,1328 +torch/include/ATen/core/boxing/impl/WrapFunctionIntoRuntimeFunctor.h,sha256=IOxAhhqTlfIW2oIqvY5Xi28b3_nKQl6XbHFM838UPQE,1432 +torch/include/ATen/core/boxing/impl/boxing.h,sha256=6Rb1GGNdNdBb4lpEuUXyDCP0yxnaixOqEiNwrXrhpCA,13565 +torch/include/ATen/core/boxing/impl/make_boxed_from_unboxed_functor.h,sha256=l3zNJHLveALyzyV32_1yxHULMR3eXN_5M1ruIof8T1Q,31688 +torch/include/ATen/core/boxing/impl/test_helpers.h,sha256=RV1cemc9bnt9LRO1BraHCD0UVrKkekGkHRAZsePs-_U,4401 +torch/include/ATen/core/builtin_function.h,sha256=3v2b3g1puVZZsZHTHqZkmS0mR7Fo4ipzgUwNbd1mo8Q,2044 +torch/include/ATen/core/class_type.h,sha256=hT9BDS2vi3NiJUuF76YKimTX94ipXyY4Nk4EtaYyWOw,14095 +torch/include/ATen/core/custom_class.h,sha256=K5zORoYA965P8p4mtrEBnTXN7HzMA1x7KECVMrvTPG8,744 +torch/include/ATen/core/dispatch/CppSignature.h,sha256=8cjjxgOmDtYXPvJJYTpyGYuogPVzNghznijWTKtOb24,2382 +torch/include/ATen/core/dispatch/DispatchKeyExtractor.h,sha256=KbYjEq7GFj7KWLsHEeRrBftCPx6-D3XjTXDnTCaarEI,10928 +torch/include/ATen/core/dispatch/Dispatcher.h,sha256=lr7CsngqP6Hq9592w6oC20vNIXbkjdRsRzgMq7erplc,34248 +torch/include/ATen/core/dispatch/ObservedOperators.h,sha256=GKoSUqGxIkIkNOO415E5tOFAUr33uxrk_tL1vWpo6G4,329 +torch/include/ATen/core/dispatch/OperatorEntry.h,sha256=wYNt5O-c5TDPCw9ww0k_mvBQYR-cfG7jpTtkAXMRmMk,13019 +torch/include/ATen/core/dispatch/OperatorOptions.h,sha256=rCzbHUuFbOPXYliCxGJ6YZjZFolCC31OVCFJp00SYvI,886 +torch/include/ATen/core/dispatch/RegistrationHandleRAII.h,sha256=0fmZkIlHxgDiisQtRaq45fb5X0PbTCpfJWIGdZoCx88,877 +torch/include/ATen/core/dynamic_type.h,sha256=-XUY1TTCyoOm2xyq-7tXKuv192edT4sJQKgh1jTDjHs,10737 +torch/include/ATen/core/enum_tag.h,sha256=SrAeBA7dtxI7QfNT8AxdyOC0hd38TNruiOt4oEsoL8E,605 +torch/include/ATen/core/enum_type.h,sha256=feh0xyR5UWMOTT9C3vB1MCCjU5y-wD8OLKLTN4vzZ2k,2840 +torch/include/ATen/core/function.h,sha256=ck8dplmQlMNUzahDR20_PsKs3D2J0C_LWhl1Ufrn89w,3455 +torch/include/ATen/core/function_schema.h,sha256=8XKkvoGw2HaUHoTLYhWy2cS8raLFer2ivuKg7viSbvc,24114 +torch/include/ATen/core/function_schema_inl.h,sha256=wnrTFwYgyoXuCIxQiwuZFhBJVs2IaVdMMvuaNwyCaK0,2071 +torch/include/ATen/core/functional.h,sha256=nQru70dgD4D1U6H27oL3mFRbFigON0XwekO7-6bgoVA,1464 +torch/include/ATen/core/grad_mode.h,sha256=uhkLSjH7WfIVojVuftgyWZ92HPS41dQiwclS9Sk4QOQ,210 +torch/include/ATen/core/interned_strings.h,sha256=ascLYehXhTHaNvqHXaG2AhZxdH-iCEojHXuCP71h-NM,13408 +torch/include/ATen/core/interned_strings_class.h,sha256=9oF2TEI8EKu5wcse3CzAyR3eD3vAYGOlrTyZ1FhV7-w,722 +torch/include/ATen/core/ivalue.h,sha256=s9IeVZAOGt1S04e3J3gJIoUb1Dv6MMaWyBSXMzwVtmc,51503 +torch/include/ATen/core/ivalue_inl.h,sha256=Hdoa-BpKngMJ-7hCHr4F8OXIBhh7jgLhGLbFkz-RL5U,87822 +torch/include/ATen/core/ivalue_to.h,sha256=8EZ5vbME27AVJ8AiBbT7jcvxzWCvGlTNS_12MWCdXK4,756 +torch/include/ATen/core/jit_type.h,sha256=9zAFxhxn_KcDFJkEFVOelYQDdd2PlEZzL_WiRHqbnkk,72205 +torch/include/ATen/core/jit_type_base.h,sha256=XSNyV_5KS6eJabRO9c1xiXk_CjhssIUsiQ8_iZDtpZc,23124 +torch/include/ATen/core/op_registration/adaption.h,sha256=7WjiOoK-nMOuEpjDmov3x0d-GF-4dUQWOc2GRnGVtpY,3216 +torch/include/ATen/core/op_registration/infer_schema.h,sha256=e0YEie6FiDtW6JqHMvk1Tlep14YWOB2GHZ11_KBUWEs,6727 +torch/include/ATen/core/op_registration/op_allowlist.h,sha256=kA6vpPPdmtSHQZVXP0V0MVWhhi5Va69-AgG1Ky4o0YU,6316 +torch/include/ATen/core/op_registration/op_registration.h,sha256=iMo4vFILJ03pt4ZOJZHGyEvIgPJJftE78cn2vyWWwUo,28579 +torch/include/ATen/core/operator_name.h,sha256=w5-HuZWn5_qp1hri0kvx1K9ghopQvL7oxKYTE3uFwB4,3034 +torch/include/ATen/core/qualified_name.h,sha256=0dN_w0uzEKRTrRVkIifW685vE2MZRte5XrQJeMnluZo,4373 +torch/include/ATen/core/rref_interface.h,sha256=-q8MP0PZsVuueSYC-5-ZzooQyQC_RKP6b3x8KBfDDYY,1208 +torch/include/ATen/core/stack.h,sha256=9LcsW3xhlUAltN3SOc3Cbokrhl_q6xhFp-INs3ddiRU,6182 +torch/include/ATen/core/symbol.h,sha256=BMm0By74ISHGnn8nnF9_kA4IS_bLWOWmoZAzcWFlTtA,5873 +torch/include/ATen/core/type_factory.h,sha256=a9E2b6E6lvhjvaNK5R1ym4c6bZd-3uRj3lhTt4YHsbk,3247 +torch/include/ATen/core/type_ptr.h,sha256=bLQzZcfOt8jjfhoed9oBvy18T-afIhYAl2JDPce5EJM,1218 +torch/include/ATen/core/typeid.h,sha256=kuExuy1u5A34ATIuUUgf_aPWYvKKXQwfE0OLtHs8hhM,29 +torch/include/ATen/cpp_custom_type_hack.h,sha256=j_wxuMgOWKLv9uotd8SW3Fm3QMyvVU9XQQePdrao1fM,5430 +torch/include/ATen/cpu/FlushDenormal.h,sha256=QqfyFZmXGzLL1WuhGhc7SWsNq6-TARAJufstsTuMjQw,537 +torch/include/ATen/cpu/Utils.h,sha256=RTNW98JUgf2zS8vE8def75cclbfnZ1mJraBNgVVogNk,803 +torch/include/ATen/cpu/vec/functional.h,sha256=PznpXDxQCMjC8FX6vXq8opByq5PY1JElx5blu6kIttA,102 +torch/include/ATen/cpu/vec/functional_base.h,sha256=eTJamoVTVkQnPQY9CfZrGM6Yv58d2yx4880gUeSzWko,15479 +torch/include/ATen/cpu/vec/functional_bfloat16.h,sha256=V-adXcH9VDmDpV_hfdp8rCHPbSEutYYgaQvjf1_7FwI,25411 +torch/include/ATen/cpu/vec/intrinsics.h,sha256=TyU4w201ohEBS37Of-12DBvn-m7t-An269-kKoO0fw0,2088 +torch/include/ATen/cpu/vec/sve/sve_helper.h,sha256=dO7dEqdaqNZdjKtRxzjRFM2qd3ec6mWgashxvRLv9A4,3169 +torch/include/ATen/cpu/vec/sve/vec_bfloat16.h,sha256=GsFSFfMN6MnARxHUEHK2cMlpGlwKNMrvzfYwToGql-Q,20221 +torch/include/ATen/cpu/vec/sve/vec_common_sve.h,sha256=AFZn76hJK1hN_dYjI2F99GkqhU4GwO6nz8-vhVSZwfU,8424 +torch/include/ATen/cpu/vec/sve/vec_double.h,sha256=1L0m7LrU2dUI27XTHX3AQLap6k_EthbqE5DWiIeareU,18957 +torch/include/ATen/cpu/vec/sve/vec_float.h,sha256=Dwmc5DxNqKA6yaX6S3N6NJdOyroC_NHSoQtYytS-oEo,26867 +torch/include/ATen/cpu/vec/sve/vec_int.h,sha256=t-XgpJ9Y7yHQi5tuJgbYWTkYg5JWMh-tDJxhJqFuPC8,27810 +torch/include/ATen/cpu/vec/sve/vec_qint.h,sha256=b_tlwirQKblV8VTX4C5SQCGB9iPqhNOc2QHjhKt6JiE,20043 +torch/include/ATen/cpu/vec/vec.h,sha256=8OdgxpoFknzgx31t0Zo3NiWsOmk6zTJvHAmfRQeeh4A,1349 +torch/include/ATen/cpu/vec/vec128/vec128.h,sha256=Hqo-EhdS-O78HSMIgjpfoadh1dlf7c8KkPH4Bxu8u0s,365 +torch/include/ATen/cpu/vec/vec128/vec128_bfloat16_neon.h,sha256=4qxb4WVpJv73cWonvuxL4a331Ub05Sxtn_GVV6G3cLA,22570 +torch/include/ATen/cpu/vec/vec128/vec128_convert.h,sha256=jwy7ek0ccJ-sZUi8M2KgRMd1W9gh8IfyBcHVFm38Alc,1987 +torch/include/ATen/cpu/vec/vec128/vec128_float_neon.h,sha256=SKK7UO4nBoWNfPRxeHJDPfOKlX8jLtzg7kyp9NAuWnw,19650 +torch/include/ATen/cpu/vec/vec128/vec128_half_neon.h,sha256=UIvcMcc2C7p3BjDgQuZDBuQ92NxPkIbj8HHgI-qOCmw,20094 +torch/include/ATen/cpu/vec/vec128/vec128_reduced_precision_common_neon.h,sha256=hAecc21BZva5VYKoNC0oj7t6-BuNO2KvFoJQLuJb2A4,9778 +torch/include/ATen/cpu/vec/vec256/missing_vld1_neon.h,sha256=rLVZSKT1P1gHHVB10alyEd8W7B7B6jpsgZ81FDKz-xU,13783 +torch/include/ATen/cpu/vec/vec256/missing_vst1_neon.h,sha256=cNXeqrvzcbS4jXSUletwuM_fTpTqyc8-0LCoywxrYnw,285 +torch/include/ATen/cpu/vec/vec256/vec256.h,sha256=RF0No_vv-02cKPgmJe4l8oc0gyYWjFFOjQ1KP8gm8lQ,12720 +torch/include/ATen/cpu/vec/vec256/vec256_16bit_float.h,sha256=Moi42EO54GP1yrKhU_LR_WuQv5zzBzO-P-hv2u8GHaM,28391 +torch/include/ATen/cpu/vec/vec256/vec256_bfloat16.h,sha256=uKtUfMlyMM7GhgBJrRCFQX_FyHYZf7X1oW4AVpNB7os,8807 +torch/include/ATen/cpu/vec/vec256/vec256_complex_double.h,sha256=MJRD5KXkpsA5jrIyEQU0Reah33H1kLudjefCrfrbXWM,19495 +torch/include/ATen/cpu/vec/vec256/vec256_complex_float.h,sha256=5zKCcvhOrYr2nNnWjKS-V7BEjBkub4Z_Lfd5a_S5xrg,21576 +torch/include/ATen/cpu/vec/vec256/vec256_convert.h,sha256=zW1ZdS_HIECZV8KuGKuz-fTJ1-e01sv0kE5vu18eomM,11033 +torch/include/ATen/cpu/vec/vec256/vec256_double.h,sha256=aiaAuEE9QJpWOWams4-b-1zelbMFKdmVp0SNO_u0ht0,15107 +torch/include/ATen/cpu/vec/vec256/vec256_float.h,sha256=uZ3TzIlFAmJCASaqpVWKAqgKvMgcommrIMHuM8DuQXI,24472 +torch/include/ATen/cpu/vec/vec256/vec256_half.h,sha256=Lo6OGWTTQ2xurhCeyQqMdXgv4O-03X3z58oNUvG1cLQ,8389 +torch/include/ATen/cpu/vec/vec256/vec256_int.h,sha256=AEKEhpd--kkt4d-kPEth6m1P9S5IZfawMM9UV7qaxz8,65410 +torch/include/ATen/cpu/vec/vec256/vec256_mask.h,sha256=GsAHh4tXtK37cZzRqWIz-pjTa__4T-JCPqQ0yf0xFdE,8772 +torch/include/ATen/cpu/vec/vec256/vec256_qint.h,sha256=OvgFWelXPy-ssyBCVwOw2USEbFqKBZLKERZZDYMvpGw,47862 +torch/include/ATen/cpu/vec/vec256/vsx/vec256_bfloat16_vsx.h,sha256=der-uMOca9VMxVmI_g5DGzIbBhhYTLsGH1U73EssKzU,2137 +torch/include/ATen/cpu/vec/vec256/vsx/vec256_common_vsx.h,sha256=YhuSkJCp24XOxTr6MfonhiXfX3bKWPXk3RUaA4fa3BU,8088 +torch/include/ATen/cpu/vec/vec256/vsx/vec256_complex_double_vsx.h,sha256=5z_C3x_gt7S1mTWON_hkwfq1ZQoOa1uEdGk8YuTtB8g,21682 +torch/include/ATen/cpu/vec/vec256/vsx/vec256_complex_float_vsx.h,sha256=D1w1-E-yfMQ_81FPk135k9je8BcddXBcV4LyGtJ4G0w,24371 +torch/include/ATen/cpu/vec/vec256/vsx/vec256_double_vsx.h,sha256=TNljv3XhQ5yvIovTr9X2nU3QDhQn_98T6zEjq4rWmvs,16206 +torch/include/ATen/cpu/vec/vec256/vsx/vec256_float_vsx.h,sha256=uT_hnmK5q2N90VlRX-LW4mjAyvnwKvfbn1TaaZxSozg,16757 +torch/include/ATen/cpu/vec/vec256/vsx/vec256_int16_vsx.h,sha256=Qgrh3OowYBDwSiUYanaiN0vv4TNvsXmlBUhFK7mYC2I,14274 +torch/include/ATen/cpu/vec/vec256/vsx/vec256_int32_vsx.h,sha256=T5LrrKI6ZOktP6ONZGLjS1_SlEcQU8im_SuucwhE0GE,12089 +torch/include/ATen/cpu/vec/vec256/vsx/vec256_int64_vsx.h,sha256=B87YTaZXongnoVIP_0AoMVy1P9zxyAv1Gj69PBcZWtQ,10358 +torch/include/ATen/cpu/vec/vec256/vsx/vec256_qint32_vsx.h,sha256=aGP1VcXClGGnJOG9cReOX1UMVWTgnhjNs9XTqNbdg18,10065 +torch/include/ATen/cpu/vec/vec256/vsx/vec256_qint8_vsx.h,sha256=8sVnwTtJJxbB9ZQbGDjBhIXiKxx4v1zv9BY6jBr2tX8,17418 +torch/include/ATen/cpu/vec/vec256/vsx/vec256_quint8_vsx.h,sha256=JjUeK23GYUUIhAKbi_CQyoFFLw3DAatJR-hjKNRCVLQ,18115 +torch/include/ATen/cpu/vec/vec256/vsx/vsx_helpers.h,sha256=Evm5RnXP61cLW2TEbQa5Qv-6adXmOtcvcbyG3d-pCys,20502 +torch/include/ATen/cpu/vec/vec256/zarch/vec256_zarch.h,sha256=ccdrXZ6MZWl8UE6UbHLIegAQKgjveOggH756n7Duk60,101968 +torch/include/ATen/cpu/vec/vec512/vec512.h,sha256=aMF09O95_zWw43WMD07IjHCCsxQ-B7ftgJ9mgB7vw2Y,11136 +torch/include/ATen/cpu/vec/vec512/vec512_bfloat16.h,sha256=WzesCuqS8TtTD9Q6N0y6Eg8kELkJ0rGEGzNpOwUVceY,66394 +torch/include/ATen/cpu/vec/vec512/vec512_complex_double.h,sha256=NTrI5yFeLuu4x9p9Q36gizzwOvTyiTMQiwZldkQ4t1U,24016 +torch/include/ATen/cpu/vec/vec512/vec512_complex_float.h,sha256=e1ILTrXcLLJxltSWC3ZwX_lMfXb-Ks3Q53wGK4GHzoE,44509 +torch/include/ATen/cpu/vec/vec512/vec512_convert.h,sha256=swqrMd_5Ka2a0VMPz7yy8KoIbZ0yxXlgPhMX9MxK4tY,9956 +torch/include/ATen/cpu/vec/vec512/vec512_double.h,sha256=Gi_LYHGrijlPiFkNT32V4q2gkpK-QJp4mtU9wbvllyo,16637 +torch/include/ATen/cpu/vec/vec512/vec512_float.h,sha256=b99hGV4dhi5NtbT_EIJlpcsGsHlUiGqLnMOr8lmZnmM,27119 +torch/include/ATen/cpu/vec/vec512/vec512_float8.h,sha256=XZZ4b3Ej8a1VUL1XOa9RvvS9P1bbpDisXZJDZVraI8w,23686 +torch/include/ATen/cpu/vec/vec512/vec512_int.h,sha256=_BLWDTa2uqrZ0bTDr0bCIx3MOY8XBsYqU9s75s2LiO0,58734 +torch/include/ATen/cpu/vec/vec512/vec512_mask.h,sha256=i_85b4_55K76tHt1rXXwh1VClmE64282vD9jLYuc1l4,12298 +torch/include/ATen/cpu/vec/vec512/vec512_qint.h,sha256=vcdbnsgE9O-GCBvPuSy46RiWD12rl0FWoWY7l1U9llg,50683 +torch/include/ATen/cpu/vec/vec_base.h,sha256=Lczpzf_SXKxS4nAcnaC6bD1nbXWC5UBMKns7v8xa8gY,48807 +torch/include/ATen/cpu/vec/vec_convert.h,sha256=-zaM58vu1KihgkrVgtbwB9PmnrhjHdLfcROtkOWQMIk,2289 +torch/include/ATen/cpu/vec/vec_half.h,sha256=ft_ziTVZvoKXUoMnMkd-16H4FilKY2GVkFfdT3yyHbI,4635 +torch/include/ATen/cpu/vec/vec_mask.h,sha256=qln-YCnyOe66bMwxkiyFA7bvqSOVKoJESx-cjASxgJc,9971 +torch/include/ATen/cpu/vec/vec_n.h,sha256=efB6DtE7bi1XcavDRsl5zOMZFARR10Nxpe1cyEkkWDw,13461 +torch/include/ATen/cpu/vml.h,sha256=cPRsw6vDrSR4vQY5VSByPRODnpmkS8AkYUxi2c9mz7k,6075 +torch/include/ATen/cuda/ATenCUDAGeneral.h,sha256=hzsZuf9MUxBbRSSpsefJgyxO9RnmYcnfdKYOKFEfIwE,190 +torch/include/ATen/cuda/ApplyGridUtils.cuh,sha256=OznX8USe4TdDRxZMO2O0VH7wlbxOY6wkGOdw1Ycdpfo,1309 +torch/include/ATen/cuda/AsmUtils.cuh,sha256=LQzRGYOe3jlVaNogJAUH-v9fldTuGQQshcNNeU4JzD4,3394 +torch/include/ATen/cuda/Atomic.cuh,sha256=-vaB50ZcMPUAUDj_c80qsUBDTxdNuy-dObapncIPF5c,27271 +torch/include/ATen/cuda/CUDAApplyUtils.cuh,sha256=QRPaDCmeCNrozKO-m9OcJEoIYgJHvxAdB1dMOYIoyN8,20446 +torch/include/ATen/cuda/CUDABlas.h,sha256=tj1x40hugX0684T-gpLWGovvzklT-fBtFgzNJzZGo3Q,16009 +torch/include/ATen/cuda/CUDAConfig.h,sha256=aLYMNgU-cX_93bmA6v_t5T8LIqLOtsPwCIFzM4GJ27g,925 +torch/include/ATen/cuda/CUDAContext.h,sha256=Ig-8EeKPP61vhcGchq20bULnvd-b4RkqJE-PGXs0qcY,238 +torch/include/ATen/cuda/CUDAContextLight.h,sha256=SNgFcw8K-78Br9ul_EvjMs_y1zGA9jVTTJrMmz8YxJM,2911 +torch/include/ATen/cuda/CUDADataType.h,sha256=xJD5uKAVU2xuzp1KG9Gk9Jw3XOwLKoNNHQt5koGTFwc,2787 +torch/include/ATen/cuda/CUDADevice.h,sha256=1n4a7Uxt4i3pixkfEVCh0fiz8ExGeaeQ-kdUzFp4Dlo,532 +torch/include/ATen/cuda/CUDAEvent.h,sha256=G-iIbfgdQ-fYbXVNXVqjMlsUcze8TZg1jC0tDm24pz4,9040 +torch/include/ATen/cuda/CUDAGeneratorImpl.h,sha256=i-Lgt6Ap3LnZCk3kJlNZoR8oMcbQZFI4m0Wua61XkIk,6082 +torch/include/ATen/cuda/CUDAGraph.h,sha256=NZ80JJfPPyhGLqJ1HDfCeHQqzt1wzlwGDVeqFLrksfI,3357 +torch/include/ATen/cuda/CUDAGraphsUtils.cuh,sha256=H7Aua86TJFzkOo53LTu4SZsRpAPD9Yf3qOGWeYEpdG0,1901 +torch/include/ATen/cuda/CUDASparse.h,sha256=Ix0CX2HkVzmNEs8iWRZIR14qrFJMkyjs_L4Kcl5Jpoc,2596 +torch/include/ATen/cuda/CUDASparseBlas.h,sha256=KwMS1Kx-_4zyvjDT03WI6lqysHB61M2v3DlC1fQYdRw,12775 +torch/include/ATen/cuda/CUDASparseDescriptors.h,sha256=CjQXSh0BlWxev95DHN5Wj32HPNiaaxyhFTSzu8jUg8w,9349 +torch/include/ATen/cuda/CUDATensorMethods.cuh,sha256=cyQq_CW-cqTVYG5n2yQKI552IDhMnlGZqFPurRzdUk4,270 +torch/include/ATen/cuda/CUDAUtils.h,sha256=agWW0ofbSjZFEJyLb5rueLR4p0-4S6Z-AN8trrvch40,416 +torch/include/ATen/cuda/CachingHostAllocator.h,sha256=lDkdOFrWAaYL99FhjlfyrsoCxl3DlHLA7GCqy_0lJ4Q,3032 +torch/include/ATen/cuda/DeviceUtils.cuh,sha256=M6xUbeOJBa9xF8gPLhSG7AIMDbK4e9B1avhM2eXLXXU,3280 +torch/include/ATen/cuda/EmptyTensor.h,sha256=wRp8R6rCIp9BYFFHNrZWyux9sQf0boSKI1SJIP-fW_k,1206 +torch/include/ATen/cuda/Exceptions.h,sha256=wUK5z-rmpX8DOQwkWg5uTCHFA-C5l8SK1m5Z8UeKIAU,12588 +torch/include/ATen/cuda/NumericLimits.cuh,sha256=Pucf4S3VEL-sT52Es3G9iXpX9WUXGjwR1-RvN1-6Hww,5214 +torch/include/ATen/cuda/PeerToPeerAccess.h,sha256=2Eihyjnz1Y3CNFJae3jciumAZ1yV_pJFmBXvn3Zc93Y,294 +torch/include/ATen/cuda/PhiloxCudaState.h,sha256=9ChfldlFF4J6Qcr7VNBtrQ_-jKVoQquIOuJKLYPXz0k,85 +torch/include/ATen/cuda/PhiloxUtils.cuh,sha256=pVnvthsuIHgFnebV4_gj01KXpuo3Fgddc3cWi1uswzE,95 +torch/include/ATen/cuda/PinnedMemoryAllocator.h,sha256=Wu12lLzq1r9t4vDzZUWv_w01NagUHb01SxiFcO6Gfjg,223 +torch/include/ATen/cuda/ScanUtils.cuh,sha256=OolU3qBsTJupQmcl407sFTkepxZ7gdUO5xi1i1iBQxI,2027 +torch/include/ATen/cuda/Sleep.h,sha256=yDtfwQXKe_TyuXKwZhqb_vQI1l9Wc_J2VPvl2MrHL_c,319 +torch/include/ATen/cuda/ThrustAllocator.h,sha256=JgK1MhDwoM1x_OSXcRaZqE6WmZ3DonmAWc0Z8WKiEyg,505 +torch/include/ATen/cuda/cub-RadixSortPairs.cuh,sha256=vUQazORo_8NizmxKgL_7nuJ2o0ieQPztes7owD3y0lM,2205 +torch/include/ATen/cuda/cub.cuh,sha256=EiM5y_Vvm3ZE_Y6OMhvULRtDzUKRDHG7pVa3xcTEWis,22561 +torch/include/ATen/cuda/cub.h,sha256=whK5xvYlfQeK_qRNFGaECPI23eYfJZ4rdZl3IoyCfX0,3368 +torch/include/ATen/cuda/cub_definitions.cuh,sha256=nLuI67EJelYONc8JmZQeCd-06Vfdixwnb6ZBFBpqIoM,1457 +torch/include/ATen/cuda/detail/CUDAHooks.h,sha256=L17kWBJDllyDtpf2yvyJNzrdg2EX9H8EdX_Xie-Of5M,2663 +torch/include/ATen/cuda/detail/DeviceThreadHandles.h,sha256=64OvgUt-NFTsK-8aHcpWMz6WtDymDfgXd2vPJckrNJw,7017 +torch/include/ATen/cuda/detail/IndexUtils.cuh,sha256=ut7xLLhgDhzVDYKy4rQ4mwr4mcUcvTxGVMUv-nVye3c,867 +torch/include/ATen/cuda/detail/IntegerDivider.cuh,sha256=qPJfE_dg2VjrM8lgNt7YVA7ltRg-X1qt92XJCSW-w_o,4019 +torch/include/ATen/cuda/detail/KernelUtils.h,sha256=sj0WUKCrfjZ3BSmHhMz1XRCvJfRD6cirRCwpGVM8Yzk,1535 +torch/include/ATen/cuda/detail/LazyNVRTC.h,sha256=mxEGFOoO7658DXR0jEe_TtUN0UShNHVM7xokcFufs3c,220 +torch/include/ATen/cuda/detail/OffsetCalculator.cuh,sha256=_gxX2yfSXqhBzqijq2njFuOqWCwSJpO3ZcagDmfgkS4,4388 +torch/include/ATen/cuda/detail/PhiloxCudaStateRaw.cuh,sha256=C5cTz2SWfNtFPyRjRaOtI85GHe6XuoOIXEDr5GStRjA,1358 +torch/include/ATen/cuda/detail/TensorInfo.cuh,sha256=Oq02D7A8aFXr0A1f5IG8vUa5arVyGiLJwI1B2sFhpIk,3239 +torch/include/ATen/cuda/detail/UnpackRaw.cuh,sha256=ZNyMP6GC-HakyHbCTX0VQLqQt0sUYz_Ly20qKwsJZTY,1739 +torch/include/ATen/cuda/jiterator.h,sha256=yfP0SBXGIcp0aZq4N5TqNF8XDSmIqgENZeGac4xd9ik,960 +torch/include/ATen/cuda/jiterator_impl.h,sha256=Lz5aduIpRzWMtJAWj0LEYrlQU5kgDGAuGz0TIJ3G31A,7101 +torch/include/ATen/cuda/llvm_jit_strings.h,sha256=xBnyc1FWrmVgnFrIS-tvwHdINw5reXg7j1W6wAHsa7c,428 +torch/include/ATen/cuda/tunable/GemmCommon.h,sha256=NKYgEiKO4qjvePYdnXVvZyr6-4YowRc82cXtEAJ83_Y,21822 +torch/include/ATen/cuda/tunable/GemmHipblaslt.h,sha256=tQDcioQL4P3VPf5FpzGv_XNk62C_zMGu-yMnbBg5VfA,21020 +torch/include/ATen/cuda/tunable/GemmRocblas.h,sha256=O4XFVCIgpEbpk4lt1arK7RthoUeXBzkN2WzZ3nKXIHk,10394 +torch/include/ATen/cuda/tunable/StreamTimer.h,sha256=jeaEFdGhREl_qFvxmyBeZ80jEEPdnrmgtBA2UsT0Eng,1045 +torch/include/ATen/cuda/tunable/Tunable.h,sha256=nQyXBpyBhhpRo9hCPvXjlScnhDidgo8aFo2xTDfDQ70,7538 +torch/include/ATen/cuda/tunable/TunableGemm.h,sha256=5Hy4WOBamJR4X9R94qzWaTEN4691qX81JK0PTToZ6hQ,9559 +torch/include/ATen/cuda/tunable/TunableOp.h,sha256=-N7Imij072IYV96co9xo1T5Y6hzwLO_dxPkdlfyLHk8,14694 +torch/include/ATen/cudnn/Descriptors.h,sha256=Lbxl5XrOAgWoIYPP6buPYn-_GZwq7MxSijvaMgniTD4,15043 +torch/include/ATen/cudnn/Handle.h,sha256=Q3u6V5WAOzyq9Hff0vEQf9sQG2dtn-xVJf19Z5YDA0Q,193 +torch/include/ATen/cudnn/Handles.h,sha256=y6xfj3ZCKYNVzohfiFqsqeNSeXCpHSVqtB7a5WLhF3g,44 +torch/include/ATen/cudnn/Types.h,sha256=SkDDXkxZvsugjktDgXDQLQhgd9ztjJmPV95PB0Yr09o,310 +torch/include/ATen/cudnn/Utils.h,sha256=nHth5X7BpAgUT_OjgpR0aj3yHUtWelEWqRW5GTMjuyQ,595 +torch/include/ATen/cudnn/cudnn-wrapper.h,sha256=6TsB0_lQJ63a8X3WCun71pSUBTBoz9lyYmx0ElhUEXc,540 +torch/include/ATen/detail/AcceleratorHooksInterface.h,sha256=6z00T9gwiL2js2lBksa3UyzO9VyzxC9mfTzvCq5q1wE,2994 +torch/include/ATen/detail/CUDAHooksInterface.h,sha256=863TWNkfFJZDUnNps6PaeBNoBLDOvpOngxB8OZV-W7A,7581 +torch/include/ATen/detail/FunctionTraits.h,sha256=hXxkxMEEYleExSPSUbTNXcan2bjV888M3A88DCo0PAY,3075 +torch/include/ATen/detail/HIPHooksInterface.h,sha256=KsNcTU0VS5dYvYs2gBFo1zpvLOMc319kIW4YtgnXF3U,2004 +torch/include/ATen/detail/HPUHooksInterface.h,sha256=C9nFutq2zSfAMN9rOHXMlqiB9CPvKbLBi8TDVsDcmjc,1445 +torch/include/ATen/detail/IPUHooksInterface.h,sha256=2qxiRe-9CnUTfs3oEUTMpoxXG63rb_QG4KCBRuvCqp4,1244 +torch/include/ATen/detail/MAIAHooksInterface.h,sha256=Ie3m5sNKJ-cqymFIrVoG_QPbwqkgBWFHcWhQJA714Ko,1275 +torch/include/ATen/detail/MPSHooksInterface.h,sha256=3wu0CcRS0z2S3g31KGLDvYSk2PC6EirLDMunG-_rJL4,3740 +torch/include/ATen/detail/MTIAHooksInterface.h,sha256=YG-sQuTxSkPkeoOCYtvKNto5688-H8afYstnYepZuJI,4269 +torch/include/ATen/detail/PrivateUse1HooksInterface.h,sha256=vcYuFOEI7jsa49V5NRD25f83JNLKxbiU50xwHc0TqwQ,2489 +torch/include/ATen/detail/XPUHooksInterface.h,sha256=YGv7I5gU2YPM_cSeHYRgnuRdYoneSLezMWh3DnlAT2o,2462 +torch/include/ATen/div_rtn.h,sha256=sPoWObIG7LtKrAxw_5aTCP-bqVotCNujmu_P0kzgN-g,211 +torch/include/ATen/dlpack.h,sha256=cB4k1Aju13mzar7wNvIPP1FeekL1bS8hqYp4gpObxOc,7007 +torch/include/ATen/functorch/ADInterpreters.h,sha256=-29jJLOGOyQb3oEA9TC5VEJig-INBgJiYAoh1GpUWX0,1560 +torch/include/ATen/functorch/BatchRulesHelper.h,sha256=h2OKWXtxix-IrXJ9EzmXuTZYhnSij_t7DMNuMrh-x7M,18710 +torch/include/ATen/functorch/BatchedFallback.h,sha256=bVftski7cdzVm1XocpesCC01mKpuw10BK7f_MDUlV3U,3439 +torch/include/ATen/functorch/BatchedTensorImpl.h,sha256=1Pqz9ZcJYbkmT5H7Fg_7iALNzd6FzWdlZ7h9Me27V7U,6394 +torch/include/ATen/functorch/BatchingMetaprogramming.h,sha256=5fR6gjn0yIWbP_RvzWYNzr_wnWy475wcwk09fG6tDig,4990 +torch/include/ATen/functorch/DynamicLayer.h,sha256=x08NiFNV2idAtbSoF4ICsWbipyPFQ0iIHBn3DAK-O7g,5561 +torch/include/ATen/functorch/FunctionalizeInterpreter.h,sha256=1wgCr1aYCHCafvUISKOkRq4UKq32Xb2Bu5vAVhejnOs,907 +torch/include/ATen/functorch/Interpreter.h,sha256=fkVookdp_eknT7wVfdOsecsYFifch8zMw9xc38kVwvM,14121 +torch/include/ATen/functorch/LegacyVmapTransforms.h,sha256=jq0i0UfU-lzmvclKqLfsTlxi3lkeF84dQ5l1VepHUEk,8252 +torch/include/ATen/functorch/Macros.h,sha256=KVN3mhngmrlRkvlHdP-X28YRu4zrvfxVBW_j9PFoR-w,50 +torch/include/ATen/functorch/PlumbingHelper.h,sha256=bigy-iGmETzD78ahEIhAWpq6D667nq8zWZOY7eWMfWs,2854 +torch/include/ATen/functorch/TensorWrapper.h,sha256=39MxwjK65pgDP_NMH2JhceWG3NuUjG9-FCYzTtqvu0c,4026 +torch/include/ATen/functorch/VmapInterpreter.h,sha256=n_Qh7KijuFE9YImBryejjToM735303IN1K9LbwchexA,957 +torch/include/ATen/hip/impl/HIPAllocatorMasqueradingAsCUDA.h,sha256=m2l4tBwXNPcqIKBtwpln1EBwhZ--4_9vKxjkWTimO4o,985 +torch/include/ATen/hip/impl/HIPCachingAllocatorMasqueradingAsCUDA.h,sha256=lwoGk5s7GA1FSagtjE0T1C0xEnhNbWWia9TOu3sbkg0,517 +torch/include/ATen/hip/impl/HIPGuardImplMasqueradingAsCUDA.h,sha256=q2NqVqXQA4sU_HhM3oO6X2ARyn7GYBHfrJVLqv6GZXo,15170 +torch/include/ATen/hip/impl/HIPStreamMasqueradingAsCUDA.h,sha256=Xev7D_h7zcX4gIS8tdAUSguenUBHVaak5MFMbE6Bj_I,4514 +torch/include/ATen/jit_macros.h,sha256=P9m1lVGf1syAvF20g-tKHFIc8eGCp8gO2rV5Fem9gfc,230 +torch/include/ATen/jiterator_macros.h,sha256=X8fFoFw_LAws7_Hw5k1APlSvvu03ahBZhGYQ_Qpfvi8,1506 +torch/include/ATen/metal/Context.h,sha256=uK1HIEo3bcUOErnkPKIdrLo46PRVEUg93n0SDuyFc9I,673 +torch/include/ATen/miopen/Descriptors.h,sha256=pBU61DXp00uzUvfEXZgD5iqK2g1vllowO5aacatv3_E,6662 +torch/include/ATen/miopen/Exceptions.h,sha256=iphmGt4foYp-oHbK3ZLW3vblofLb83gNnHY506HS5o8,1076 +torch/include/ATen/miopen/Handle.h,sha256=7HK5wmZki01A_B93N0VIXymzthrd0zaTEiS6Vg2Cj1c,189 +torch/include/ATen/miopen/Types.h,sha256=myV5X9yvgKu37IXd6L4Ku9oQXnla996i1NRY8hDCv6g,270 +torch/include/ATen/miopen/Utils.h,sha256=-NuO-vc2fpcO3dLawvuoRnXRibX8W0qzYxW-31BCQGo,401 +torch/include/ATen/miopen/miopen-wrapper.h,sha256=BnXfKeZ0T30890odGRRUG7xYrZo1aulovsvaGQRDQn8,528 +torch/include/ATen/mps/EmptyTensor.h,sha256=Uh2Nq2rxfVgVtQLv4b2UxczZYKZQJisUqF69Xua2FJA,755 +torch/include/ATen/mps/IndexKernels.h,sha256=Y9tUi0mjx6BYwQoNwnIZoXDPX9RPQ0SJWnVZYohRqr0,9476 +torch/include/ATen/mps/MPSAllocator.h,sha256=F7wsy3C1MKcfLf9bxw4PVzv_Uy4K3MnUCu4eOGsSjG8,18556 +torch/include/ATen/mps/MPSAllocatorInterface.h,sha256=M1LYNsmKhbWq0TgYtBpq1kYrJBt8j7CDA32e0laFmtA,2718 +torch/include/ATen/mps/MPSDevice.h,sha256=DeajBSxMM1G19C6pgWtQqJzqpJ98aHqNeREuCgaCbq0,1767 +torch/include/ATen/mps/MPSEvent.h,sha256=Iv2OmwYnSSF2DPiuYcc9JSDbdVIUPD4U0BTjtD3GJ88,3568 +torch/include/ATen/mps/MPSGeneratorImpl.h,sha256=RBRpwrFSX2B_D66-oYeI-6oMjC1lD3jDCYXmmULD1LE,1562 +torch/include/ATen/mps/MPSGuardImpl.h,sha256=atbTdjiFcNqDq0C7-9osWLug1_fSpvdcMNmUu94Vc-A,5419 +torch/include/ATen/mps/MPSHooks.h,sha256=Pos5hSJxfak4-n95YXqEMGhbcOzCmm2IsZOjhsLw4qE,2302 +torch/include/ATen/mps/MPSProfiler.h,sha256=PL4O56QpCxBP9OP3qVxWJJR0vKmhjmx7e5QF_n8YBX8,16590 +torch/include/ATen/mps/MPSStream.h,sha256=sh9ELWqWuae4bVqVpp0UqXCI4tsaqbxzMjrpEppR4dQ,4607 +torch/include/ATen/native/Activation.h,sha256=awhps1Kn8U9wDdaVA9Neq666ZaKpMbtnkspcLaVs_8g,3515 +torch/include/ATen/native/AdaptivePooling.h,sha256=hIOAwgq7gNwKInNwTf613BetCXPB4mTY0l2Z7scgwkE,2428 +torch/include/ATen/native/AmpKernels.h,sha256=BK8NK8Cq_2_6ASXgFvJx1w0Kzfwm-K8btEezEnhnyvY,617 +torch/include/ATen/native/BatchLinearAlgebra.h,sha256=fK-AGaABLKc--J4sDRX5PjEOz3fRBvhdV4KTszYkCVo,9896 +torch/include/ATen/native/BinaryOps.h,sha256=DgZbq0KQBl-xbRAaTKeUsUqHSJXQ6gukQt9n0lT9wXc,5932 +torch/include/ATen/native/BucketizationUtils.h,sha256=UJtNY9qw4EZNLbCRItmz1ZhsIhZGAJi3UbBdjdZHKOI,7789 +torch/include/ATen/native/CPUBlas.h,sha256=l6Z1ZZGyQ4wj8oU1efPf4AIYR3vBUgpEYNrRS5d7s3U,8365 +torch/include/ATen/native/CPUFallback.h,sha256=jOWihTOutM5FdtYGRU9BiKJgH5ovt8qijTgMFwIFAcI,2413 +torch/include/ATen/native/CanUse32BitIndexMath.h,sha256=gHAOodx1KsektVgmllOKAX4n1QvGJfKyzx8qSGE8GDk,242 +torch/include/ATen/native/ComplexHelper.h,sha256=btmQjpW5GwDNmwc4vJA5d8HEeDf9sIItsucn5CkHUxY,4048 +torch/include/ATen/native/CompositeRandomAccessor.h,sha256=BMz1QA7muxCi1EoKhTSfzjI_Zsr-EM6HvVfIRrKamBI,876 +torch/include/ATen/native/CompositeRandomAccessorCommon.h,sha256=R46csHYWaGheNhcj3irxcvZv2RfTYMwVaeTT-hQp_08,6733 +torch/include/ATen/native/ConvUtils.h,sha256=TgNyGQjvgDcxpPHUEWxUPhy6ATLQ5cp13N2xCqrc-X0,19681 +torch/include/ATen/native/ConvolutionMM3d.h,sha256=LPOz-FenTJ6sch4u2UYsmEfXHDC-qDvpLFNCV_wXDsg,340 +torch/include/ATen/native/Copy.h,sha256=g7MIP4FwnbNhSE00-WnyTPF9dsGUZAvEErtswrX6GeI,372 +torch/include/ATen/native/Cross.h,sha256=UxJtlJqvAOJLgisxu6yfjOXBCl_sh75ULuaKSzgSEes,259 +torch/include/ATen/native/DilatedConvolutionUtils.h,sha256=s0SPWkwNQEaPiBwa1OkN1bNtvhfdRkvAJ18Z-nqlN5E,6402 +torch/include/ATen/native/DispatchStub.h,sha256=jM1zHtItYHgBKYaxaFAc-CJGTvUErVCklqnFQDRyNVM,16019 +torch/include/ATen/native/Distance.h,sha256=7ilibZOX4RPtlxlCi1iDwWdnIxY2U0gnma8fJ7JMaCg,720 +torch/include/ATen/native/DistributionTemplates.h,sha256=-Ig5O5jESm7mFNGF-1k6RiP8QPm1fiE0wN3crLd2mPg,18494 +torch/include/ATen/native/Distributions.h,sha256=dgn_IGPmn1r-cLrwsv8M_izOUj-NI7VY0P_rrAJRySc,21572 +torch/include/ATen/native/EmbeddingBag.h,sha256=O_g3_DHVVp93oCfUmdt72AX0zZXRK-6XlVaJIktcBRA,5215 +torch/include/ATen/native/Fill.h,sha256=pIKasYX4k7VbWhYtremcZVeuwPS5g2mDyHx9uugbPWg,396 +torch/include/ATen/native/ForeachUtils.h,sha256=MxoxWgp8FC4Wc7OzmKra7xYSWyv0TBbSAEcTos8yjog,15067 +torch/include/ATen/native/FractionalMaxPooling.h,sha256=gsIZbVNlbraEQLJHz018Yfwo26QrrkY6gYoJ9bln9pQ,2161 +torch/include/ATen/native/FunctionOfAMatrixUtils.h,sha256=NVNsaTeAiQLyVeh-aWjCX-5AwbnY1UB8Sz3pNEePX-M,388 +torch/include/ATen/native/FusedAdagrad.h,sha256=ct4I_DMIs-GAzzAHQ3KoiP7DkhzJxWaUi5WRhQJMnd4,495 +torch/include/ATen/native/FusedAdam.h,sha256=JjUcZTJPZcEl7oaKRcpK1hm-wR_gbiyj54be9zgFiPo,683 +torch/include/ATen/native/FusedSGD.h,sha256=NBCRCH5JnQsM2Y28GillI4GK2ujTd0iDywFFjXcIWs4,516 +torch/include/ATen/native/Gelu.h,sha256=1ww4kktmpU5X8y0exYjPJen39keBFzW73cnqBnWUb4k,843 +torch/include/ATen/native/GridSampler.h,sha256=s9y-a59QhVLK5UNuJ6Xuy1qeUuinKR-p3Z-BSO0WYmU,10407 +torch/include/ATen/native/GridSamplerUtils.h,sha256=H3HEU7mlnSzk_H3ON_LEZOflWM23HKm9PU6ZkO2N9go,3499 +torch/include/ATen/native/Histogram.h,sha256=4jh5Wja1Jpp5Dfy1CDvmVe-BptOxwWLLDozJk8qp8Cw,745 +torch/include/ATen/native/IndexKernel.h,sha256=A4cRCTrqQLnQhvhnQXjYJYm3RkoFZc_X0rHWknfw4fs,1703 +torch/include/ATen/native/IndexingUtils.h,sha256=a0tX_WlzmZQJQ5kgTg3kC_Z2rPCQnh3wckUq4RzHIK4,5625 +torch/include/ATen/native/Lerp.h,sha256=mClCedrlHS4sb8VyTqJFXEsYrPtK-iImwJNYmsKO0g8,1461 +torch/include/ATen/native/LinearAlgebra.h,sha256=mHpOc3VdoNVhACEzyBoPbt6cOEjOcurJzHWbqz9IhYo,299 +torch/include/ATen/native/LinearAlgebraUtils.h,sha256=rhK4_rn5Dq6pLaWJifEuFCrRagcdb3IudjkX6aSI760,26357 +torch/include/ATen/native/LossMulti.h,sha256=b5N9Puce5fIQLBYQw41_01aepSuLfbe6ym0tLddh2ZM,2114 +torch/include/ATen/native/Math.h,sha256=bLoDvaV8G1Ow-Vpy2ZQyme30IT4djX4g3UlilJjWhsc,141845 +torch/include/ATen/native/MathBitFallThroughLists.h,sha256=Zqlt930nF4Gf0VDFlBv19LgxauGaOIP7u7Y16wWvKSk,4136 +torch/include/ATen/native/MathBitsFallback.h,sha256=6S1ID_MDoMhXhSLlHh5qNl6oA_pG7fD_84aDlpk-oqY,7318 +torch/include/ATen/native/MaxPooling.h,sha256=2xHWHuXutAcBiAFTO6ytZC8L92swSsv3MKZ10bfCGwg,3268 +torch/include/ATen/native/NonEmptyUtils.h,sha256=GssuIa3_Nbo70prkoJCxp8O7_6XqsNkAossTxmjuXoc,599 +torch/include/ATen/native/NonSymbolicBC.h,sha256=HJycKTcID9FKdMVig0iY1UgTFBwn3jJLSAgRSjJ4zC8,2876 +torch/include/ATen/native/Normalization.h,sha256=SWXfqbDxT5tsxohCiFnR81xlwo9xYYW-AHHTgwRches,553 +torch/include/ATen/native/Padding.h,sha256=dPV4B9nU82622Po3TTsKlbdFeUE7JkI7eX6ZolEhkFQ,2061 +torch/include/ATen/native/PixelShuffle.h,sha256=qbKdQAkb-uxxRVhRZ3Mok6297ioTohJlkK89mpNiwVk,1744 +torch/include/ATen/native/PointwiseOps.h,sha256=d9sKN1SbRNzJnhCYbDInKOG-DYu3ISmTf1ErYsJJSXQ,781 +torch/include/ATen/native/Pool.h,sha256=G8vBR34qDdeF-zW1o_IfIroiALSPWTquPRL2YeaBmVY,13252 +torch/include/ATen/native/Pow.h,sha256=BQi2rLJut6q3pad3AHFSM_8LFMIfwJZOm9KFz6xICP0,1649 +torch/include/ATen/native/RNN.h,sha256=usFuO-qlUCgp40hKz4U07NGTvfmp18RQgFWjApfGwEg,2504 +torch/include/ATen/native/RangeFactories.h,sha256=m-XT97Ps7QUkw0Vc02CATeEO1tt9MYJeyjcE0BdoFeA,354 +torch/include/ATen/native/RangeUtils.h,sha256=F0gCAYl_MXxv71a5gQ9nWGQReMqnnQHV66rH-vvxJhE,2121 +torch/include/ATen/native/ReduceAllOps.h,sha256=hR0y2FxDU1IkYkuUwlWcU_KvjYpbP2CXU1M-SJ58w8o,397 +torch/include/ATen/native/ReduceOps.h,sha256=r4Za94hMpNnhHJ5Qnzb4OPVgRHUPi_aT54RLCrW_d5g,1766 +torch/include/ATen/native/ReduceOpsUtils.h,sha256=i8da7HlR-9rCAxV_dljO_f46afsPKCegy0LwPWcWPSo,16437 +torch/include/ATen/native/ReductionType.h,sha256=85wiFtD3ZACowIlVumLOqXXoVAm8bDF4SyY_uSouwQI,1139 +torch/include/ATen/native/Repeat.h,sha256=nV38kH1j2_627DlJdgm_hiPnK_4QhOAbUtumjm5U8pY,1477 +torch/include/ATen/native/Resize.h,sha256=I2Kt0KSSHkz0UlhaOb6txae9YOY87r-aSxTsI7Au1ts,8165 +torch/include/ATen/native/ResizeCommon.h,sha256=vyIXmldyz9WCGP8cwwdZ7To0xA3AdlgsYsKb2DZ9jgs,2491 +torch/include/ATen/native/ScatterGatherChecks.h,sha256=ODhtoLeDwUNcVagAZhTj0GpQOLVyZ9-0g4qdfv5lleM,3736 +torch/include/ATen/native/SegmentReduce.h,sha256=BgQ9vBIAu3UfB8S3wkBF53ONFrVXCboxs5Q6U_jZwuk,1265 +torch/include/ATen/native/SharedReduceOps.h,sha256=8Vr8UwRTaZJiqTqBMVgsT6uE8rkNv1H9vCDjLnt9QDY,16123 +torch/include/ATen/native/SobolEngineOpsUtils.h,sha256=cxzZBRujtqn8MqU8oHtxqVu9Z94tr9jLD3f8YQlIUNs,1835 +torch/include/ATen/native/Sorting.h,sha256=ETX4yqd4ALc5sRx-ZUj5QeesxDHeMdrHzVWm4PU2xsU,616 +torch/include/ATen/native/SortingUtils.h,sha256=i3rlh9XmYm2DnlDmvbqvd3jym6slpXuYZszscT7GzhU,2672 +torch/include/ATen/native/SparseTensorUtils.h,sha256=srkJh_TAtVVnUPuj7vJt6wQKdSk3yv6E-gDlDy8gR4U,6500 +torch/include/ATen/native/SpectralOpsUtils.h,sha256=eTXkJ7Diq8m24BcUnbLPbRCtILcTUgTMlciNWe35XbY,3283 +torch/include/ATen/native/StridedRandomAccessor.h,sha256=cPMgSAbNq5QR8zwKYPa9mKxRl_1xPQlAsUgi-T4xe6Q,6835 +torch/include/ATen/native/TensorAdvancedIndexing.h,sha256=HpinZzqIRzQ4i3BbDgMZjOe9qEQwpOADGzOhkxZ71S4,2897 +torch/include/ATen/native/TensorAdvancedIndexingUtils.h,sha256=9BUJtjHDzNypIb0D8mZ5gr_diRMECNV0g1PbmYvhiHk,3246 +torch/include/ATen/native/TensorCompare.h,sha256=Lo0cym3Ug8t108c5ZP9EEvTYFzKRtqISOr5IDa2vd_0,1518 +torch/include/ATen/native/TensorConversions.h,sha256=GV4CYXvWyWN50dOuvjf1bYRCuvKjmhqcVAEQ5CNpZjk,838 +torch/include/ATen/native/TensorDimApply.h,sha256=uNqM6o_6xv3x8EY942wWiPHSDHldGEdOp_Oqw9sM_ac,1827 +torch/include/ATen/native/TensorFactories.h,sha256=QNqKXm2UHmBTuw7bVcIrnXzFw1SbN1bVxyL5VilSR0k,5398 +torch/include/ATen/native/TensorIterator.h,sha256=Q7J_N0bs9xO5NSiLO74hLIE5NUIcmr2FmkSS_eLf1GI,46 +torch/include/ATen/native/TensorIteratorDynamicCasting.h,sha256=4zmXC2P--HcKysaLC82OKHAxKLwFTe4zO5fFab-w8O0,1810 +torch/include/ATen/native/TensorProperties.h,sha256=S2KRCG52XtPNLE30U1z9NEr-lg7GJNCme19ynl5iGAQ,198 +torch/include/ATen/native/TensorShape.h,sha256=Z8TK3KXptBjfKfpeQA0Mmt-6ALMrJPgq1QICbv02EeU,4597 +torch/include/ATen/native/TensorTransformations.h,sha256=aKEOaHuTWKTWhmcch16oTgv_vND2Rbkdj6ocNnxMI-8,927 +torch/include/ATen/native/TopKImpl.h,sha256=3d9fyYLn0l-bo4_h69ZkX7iFFIXyX9XpdYIq6QgoY1o,3459 +torch/include/ATen/native/TransposeType.h,sha256=Ge4VLRl4sgZVIlqyOXjY7WarTsulnfG26a1QTljGWlQ,578 +torch/include/ATen/native/TriangularOpsUtils.h,sha256=U_pmKG-BzKvr5lCzXu_V70vpAdfrklsNCxzIqS_c0ko,2002 +torch/include/ATen/native/TypeProperties.h,sha256=37P3hHIx2SpByQQfOse5RTyJlH31uJNmyz_rQHBXeNA,658 +torch/include/ATen/native/UnaryOps.h,sha256=IL_KKFjzV_vSEr5ihJu4Bh8UHSNVi5L7TJJ955IyiMA,5415 +torch/include/ATen/native/Unfold2d.h,sha256=M848GDYHcGbX02rtLDgOEIhdsTOzGtJmoK7XPlZ851w,979 +torch/include/ATen/native/Unfold3d.h,sha256=u-4T4k0XRQ09rOEOOHdKFGPnTMWb3zSvrx5sa9T6lQA,873 +torch/include/ATen/native/UnfoldBackward.h,sha256=tFm-Qa_UAz0Wojqli5pi3KvnHsL1PV74G5hGCrWEu78,3100 +torch/include/ATen/native/UpSample.h,sha256=SOEzvC5npXPG_QH0lphnvqfluHAWrO2oCLAd0VhXHz0,19211 +torch/include/ATen/native/ao_sparse/quantized/cpu/fbgemm_utils.h,sha256=hJ-8vuxC6WVRs7NCGfoMPyTTcI98lNDGYahnWK92lVA,2900 +torch/include/ATen/native/ao_sparse/quantized/cpu/packed_params.h,sha256=g9MbLya0DyIJZoG6-MxxlVYeEVAvSNkibrCBZNrDFSA,2735 +torch/include/ATen/native/ao_sparse/quantized/cpu/qnnpack_utils.h,sha256=I_CtZx5vrBf2kzoTWQmKcX0o9U4W_DGJEpuscFJzSWE,3242 +torch/include/ATen/native/batch_norm.h,sha256=F3AMKsHMsg3wLIjCGriHM8OEv6-rNCv06yYJyz6T10w,1425 +torch/include/ATen/native/cpu/AtomicAddFloat.h,sha256=90ttAHoUfZSaD8x2wACxPWiyrBPiJAuMAzuAb-aQa3s,857 +torch/include/ATen/native/cpu/CatKernel.h,sha256=pif4tSuF3YWY8dyxplgdterhfDvvaPQs6bkEWbEyH2k,306 +torch/include/ATen/native/cpu/ChannelShuffleKernel.h,sha256=-P9JnX0H0mD1vtrjvSXcAh6nIBzyu3DH4DlBzMXN4pY,286 +torch/include/ATen/native/cpu/CopyKernel.h,sha256=6DtXHSeNNtjgiOSl5LxJ3uYLrRWHgjIXVvcnHPTWcho,312 +torch/include/ATen/native/cpu/DepthwiseConvKernel.h,sha256=-myyS-lrk5dhzew1x9GPWV6QXw7_Zl8H9ooNj44vSu4,470 +torch/include/ATen/native/cpu/DistributionTemplates.h,sha256=VqfAY2uzPFVFO-C5voXxUl5jcikk7VsczBYH6y8VsJ4,16464 +torch/include/ATen/native/cpu/Elu.h,sha256=bDIApyBVb8HAHVefIPHSoj-pqbpouNZKg0Ois8SXl-s,2866 +torch/include/ATen/native/cpu/Gelu.h,sha256=X7ybZ-W3EfVqlhNC7jOYUCu_cY3gdH_D-S7qARmJFCo,3104 +torch/include/ATen/native/cpu/GridSamplerKernel.h,sha256=XSe5PJzB0P99tJwNlrUk0_WHlJTHJSOT08WzHvtqT7I,823 +torch/include/ATen/native/cpu/IndexKernelUtils.h,sha256=CYH_QkxTRy_MdAMa25c49wKav-Hk22eHstqVGbcNW1E,2927 +torch/include/ATen/native/cpu/Intrinsics.h,sha256=Wb3_syiw1pcSafKzSTvrsYFxA9H5BnNhkfTd9Tnc9Js,1212 +torch/include/ATen/native/cpu/IsContiguous.h,sha256=nSsn2QXq8y8MnbiNFlhTvnlCRhr8ZaN9LQmm1g9YQnQ,2369 +torch/include/ATen/native/cpu/LogAddExp.h,sha256=Ickd2SGbL7bSxFJ-DklopBbsdr5n-Tt4GQQj-Id1fOI,2446 +torch/include/ATen/native/cpu/LogSoftmaxKernelImpl.h,sha256=q1RIo84py5Q1B2g85IJeUoDnt7bA4Zo_xc7cQFu0fSk,13150 +torch/include/ATen/native/cpu/Loops.h,sha256=0Z5v-jjbhYhiikibBw5iA57rEegi0Y8fWOB37QhZBU0,14905 +torch/include/ATen/native/cpu/MaxUnpoolKernel.h,sha256=3ensi4n9c8cnti_2Oqks7lDaKmrIyxkSiUXivtLmoKg,306 +torch/include/ATen/native/cpu/PixelShuffleKernel.h,sha256=AaxFbVKWUSewQ-EX4kj06zNHFTVG87C5zY7nPyg7y6Y,320 +torch/include/ATen/native/cpu/Reduce.h,sha256=rRVlatv_pvmK6MQxARo3rSlZf0r-ewXroGDRO41n1ks,12070 +torch/include/ATen/native/cpu/ReduceUtils.h,sha256=H-gXGNzyXJF6fgXKRjWm5L4gwp311mlw4wuW0M1aytI,8738 +torch/include/ATen/native/cpu/ReducedPrecisionFloatGemvFastPathKernel.h,sha256=4nacRNkZd1lWwo47JRv5AL2BWvS5-6abyaeMrGwfIss,1120 +torch/include/ATen/native/cpu/SampledAddmmKernel.h,sha256=gG-E6_kfS4M6PAwiOKXsf7cBA7Zb_GOjioZ3leATpbI,322 +torch/include/ATen/native/cpu/SerialStackImpl.h,sha256=g63BUt0qT6TsJuvbP0xKP-PQZRxGuLUZZtMiPyhbroM,5456 +torch/include/ATen/native/cpu/SoftmaxKernel.h,sha256=gK9dfhgwv0Q76ETnliAnuAtoEfApd5rlGFm_hXoa5pw,935 +torch/include/ATen/native/cpu/SpmmReduceKernel.h,sha256=RimQyxRVKwAeMJAg89hPjR3kgV57BIikEiwI24fSer4,1352 +torch/include/ATen/native/cpu/StackKernel.h,sha256=Vl5C1ShzfGNQl0Rxln2yiNmpB3F1Lmts6GPqqsHeUC8,308 +torch/include/ATen/native/cpu/UpSampleKernelAVXAntialias.h,sha256=cztVY9_FKQ_vjsP8VwFQRkfUMORjL4wNvMx_JLSNCZY,58178 +torch/include/ATen/native/cpu/WeightNormKernel.h,sha256=Pb3bGiYYgNv9iBxyfB0N_zgGKs0fr9FRae4HoExAX_k,550 +torch/include/ATen/native/cpu/avx_mathfun.h,sha256=myM3TvQ8ENSdsLchEId1advZBrVtLGdH75UcPDFS6Gg,17449 +torch/include/ATen/native/cpu/int_mm_kernel.h,sha256=YxY6blEinCFE7tsE4slNrRdilLENtUmLbI8JCwQ9TaI,1105 +torch/include/ATen/native/cpu/mixed_data_type.h,sha256=7DTJuuLt5Dz6FMBnbjAuaqVA0kDb8pnU75J-emh6YKY,1408 +torch/include/ATen/native/cpu/moments_utils.h,sha256=H6TTlR7nXAFJaKa7bV0cFpehLqmOfP6S2h6tAQI7eEg,6577 +torch/include/ATen/native/cpu/utils.h,sha256=QoKuOYHeuzgqNHuH35xaifs-zskVzljs5IgolvP2IhI,7151 +torch/include/ATen/native/cpu/zmath.h,sha256=aeBgFxiT1-w9Z4i4xUZhdURm4wjaG75KUMIWUsAPAFM,6622 +torch/include/ATen/native/cuda/Activation.h,sha256=qCpKRNnbyHxDHJKOty3v4v1Rs251KdGQQ-3GH9PVnWY,536 +torch/include/ATen/native/cuda/BinaryInternal.h,sha256=GyU3vzTz1CDG5VolPpVWXL0f0vs3kXt1PAtxA-iClZk,1187 +torch/include/ATen/native/cuda/CUDAJitLoops.cuh,sha256=Z1oM0F5DBoV55zYXf0eAfbcFAotEIIuh90Y3bVHMpaY,11878 +torch/include/ATen/native/cuda/CUDALoops.cuh,sha256=Zqpeo5wES3pp5hpAP9yNW7MuyAYq25VUGDNTlH17jPs,34597 +torch/include/ATen/native/cuda/CompositeRandomAccessor.h,sha256=XtnBCyL15XJYIjUN0B2DQLkeXDqFsn5BPd6U-jj1-TM,929 +torch/include/ATen/native/cuda/Copy.h,sha256=ud9aEC-s4-Nlx6UIxWk24oqUQobM0VgHvK6CdHxPSa8,154 +torch/include/ATen/native/cuda/CuFFTPlanCache.h,sha256=aphO_Z6hzd7XVnovw_VMX2c_3BcfyoL46JM8RT6VX0c,17929 +torch/include/ATen/native/cuda/CuFFTUtils.h,sha256=7H64250R2Wfb4jJ2ACbI2BFxCGCZLj-DFhUN9ilx0bU,1873 +torch/include/ATen/native/cuda/DeviceSqrt.cuh,sha256=KMqwjqPbuCqCr4st8VpYVazLVx_6I1wiGIDD7ea4M-c,585 +torch/include/ATen/native/cuda/DistributionTemplates.h,sha256=lKWY_wU6eCrc-oMWls7GVLwM0D97qHYyO8Be1b2Gyrk,29249 +torch/include/ATen/native/cuda/Distributions.h,sha256=12qzUlaPXjPsS9aqFt8o2r2bfp4IPLnZb0sBFH9AedI,641 +torch/include/ATen/native/cuda/EmbeddingBackwardKernel.cuh,sha256=-nXKO3ihPrqfKvC6Femw-38qSqDqaJbrJTOOLRNFrJY,555 +torch/include/ATen/native/cuda/ForeachFunctors.cuh,sha256=XDQhW594kbl3T_dSnXT95bFpCmXRcSfFHpqDU_HmtmQ,24638 +torch/include/ATen/native/cuda/ForeachMinMaxFunctors.cuh,sha256=UxN958DDHsTZXfypAx-jsBPcj-M4DM8sXJYcC18oTns,426 +torch/include/ATen/native/cuda/GridSampler.cuh,sha256=PaIF-xsgY9Sf5_kxHBRdZex81VF8Jy1PrnD_kvlANF8,10975 +torch/include/ATen/native/cuda/GridSampler.h,sha256=xMqeY9maT2pJJqXZFTTVzIuhWz-8PTaVyiBBG6n6dUA,1145 +torch/include/ATen/native/cuda/GroupMM.h,sha256=jbh0bTVenm-MkBkh5OstZVUD9iSVM3nGQ1aHP--mkHo,327 +torch/include/ATen/native/cuda/GroupMMCommon.cuh,sha256=f5rogEqbqZ6weX5FHbqsO4BWdq8dS8Q90aiQYOpYumg,6229 +torch/include/ATen/native/cuda/IndexKernel.h,sha256=HdMrdyRCWWscptf9LMdCGrgOi8thLp8cro6cdJjPRps,338 +torch/include/ATen/native/cuda/IndexKernelUtils.h,sha256=JEZ6oH6S4A86M7nbkhzVD-e0OyZ81aPHReKpjJgEFg4,1906 +torch/include/ATen/native/cuda/JitLoops.cuh,sha256=mFdOS9AEnVH88goLrQ_0a60WDdzYdGiXp5Mi6J_mtxU,6907 +torch/include/ATen/native/cuda/KernelUtils.cuh,sha256=qwhJ4oI3NuHYOeYaxTx-exd0R5s2fjDhIk8a0oUPPHA,12847 +torch/include/ATen/native/cuda/LaunchUtils.h,sha256=XHCa33KlVxd9qOULEsKSCW0B1PxVLmfw_G1b5FPSnuI,282 +torch/include/ATen/native/cuda/Loops.cuh,sha256=SwTgvm3p2VJt9OMh2k5Gpxd13TwPVvi9sG1SdBDbYQM,11738 +torch/include/ATen/native/cuda/Math.cuh,sha256=9MEZYL00sTqO_gZOg5qObE21jmCXd03xfJ8GgnakD6A,122943 +torch/include/ATen/native/cuda/MemoryAccess.cuh,sha256=v5npml01w9EaVWX3e4QNtCJpq2NA41Bur4GdQbXeQyo,22278 +torch/include/ATen/native/cuda/MiscUtils.h,sha256=0AVpAl43pltMA1AZqhjJz0QwtDH8gbttEV-qadhA_iQ,934 +torch/include/ATen/native/cuda/MultiTensorApply.cuh,sha256=iEKILDt7_JQA_UTpc8FqvH-CeP3V24y9XzHK9oRh2Do,13935 +torch/include/ATen/native/cuda/Normalization.cuh,sha256=yhLC4uattealw47erV2WqN2m78tXfJA_GFd5qsUQoM0,74225 +torch/include/ATen/native/cuda/PersistentSoftmax.cuh,sha256=MyBhyYsyk3E964fevUlfzthrfTpr6jRy3I0JZgJw65M,17983 +torch/include/ATen/native/cuda/Pow.cuh,sha256=Z8FazGPDys0VK6p1GYuqbakDV4b1PedeQvBZr1afaEU,2120 +torch/include/ATen/native/cuda/Randperm.cuh,sha256=Sru-BuuCjlL9KinD8-1_YAMNBbfDLITdj0jWtYNS-5c,2108 +torch/include/ATen/native/cuda/Reduce.cuh,sha256=Nr3XBm4usCEOJqErRjBTjEAK8HyDqLXFse8COVafvqk,50106 +torch/include/ATen/native/cuda/ReduceOps.h,sha256=QXTnH2PM5LiDRmlgHJKIjpmy4ACSuECo-O6HILgH8pM,488 +torch/include/ATen/native/cuda/Resize.h,sha256=ERM-Ff6IQfvHGVjo9eaxmDfVLnPJJn3WzIQBr16B-uw,1544 +torch/include/ATen/native/cuda/RowwiseScaledMM.h,sha256=VW6I1Bz94DEADsFkJM_zCU-rt-60WQ8yg_6wZcMjOMo,369 +torch/include/ATen/native/cuda/ScaledGroupMM.h,sha256=88VHKm9b5-Y7FFbPq3pxgrurTBAi5bIrazLxUpDuHmM,414 +torch/include/ATen/native/cuda/ScanKernels.h,sha256=YwFzB36CbHF6ELtQg5_QNq541L-YNT_BkabZwFcrnaI,779 +torch/include/ATen/native/cuda/ScanUtils.cuh,sha256=ytP0V6WKOAQ_9UeCGrsp_bX6vkieft7_jcXZ7ElOWaw,21273 +torch/include/ATen/native/cuda/Sort.h,sha256=S2RwrQkWVKojaNfpyLF6e5oKwmBYurvdO97-qZ6juh0,401 +torch/include/ATen/native/cuda/SortStable.h,sha256=WrJsgGdCq4vLF8-G9F7kHmUE3y7_7-BoZoRKQCAU9ac,439 +torch/include/ATen/native/cuda/SortUtils.cuh,sha256=nnEfuqWPynWSQa7RmQbA1Cz7PFp-ZSLTysuVW8QXd5k,12375 +torch/include/ATen/native/cuda/Sorting.h,sha256=3vgAqy1D5yEwJba4VeWePXrqmw7QuKBQOf5s9aeRJ5Q,396 +torch/include/ATen/native/cuda/SortingCommon.cuh,sha256=cOAeMB58kGbFskf138ysvht4B4uMCZXBIqj9EN6sINE,5456 +torch/include/ATen/native/cuda/SortingRadixSelect.cuh,sha256=5BxcsdplbonTrjsW0GrKW-N6Zx2tK40icq7u_gPPSc8,12314 +torch/include/ATen/native/cuda/TensorModeKernel.cuh,sha256=LdZYGmI2h2dwgx6hyAJp7Wa65BT95VQOfwty86Lsg3A,14429 +torch/include/ATen/native/cuda/TensorModeKernel.h,sha256=bsCE8kmztPxtITUabC4nPm51xdlG2ddcxtKRzVHZ7JE,419 +torch/include/ATen/native/cuda/TensorTopK.h,sha256=nEXSGjDWOCAE8avkhYJOM9quE_q4brHX40f_12FVwFI,254 +torch/include/ATen/native/cuda/UniqueCub.cuh,sha256=_tDOAhW2mU-DVSL-BwFRe-SaTXGfjRdfAyzv1oQXAl0,302 +torch/include/ATen/native/cuda/UpSample.cuh,sha256=sWQmAtVm1q2_7KUeYddQg6vADLvxc-RMdMdvZQBbOdc,11624 +torch/include/ATen/native/cuda/block_reduce.cuh,sha256=eNpaVPNA4gp2Gqh7zKswhfD0MR1GeSiukWR0B6impQ0,4228 +torch/include/ATen/native/cuda/cutlass_common.cuh,sha256=ZFNEsNp0x89vcRzWVr02ElXGkTPvvjzFbnDq9iUJ8FE,982 +torch/include/ATen/native/cuda/fused_adam_amsgrad_impl.cuh,sha256=_a_WA-XX8sWGJhWO-svKxzo52oyyy24z8FP-Q4h-0L8,1029 +torch/include/ATen/native/cuda/fused_adam_impl.cuh,sha256=Rm9vfA4D3CzkVeW9oK1i5R3X15KON-hxxminw3xTqi4,941 +torch/include/ATen/native/cuda/fused_adam_utils.cuh,sha256=2SRTDVNhPLS94KLF3HlnGu-6iAs-5IpwVboX3CkgOHU,6997 +torch/include/ATen/native/cuda/fused_adamw_amsgrad_impl.cuh,sha256=j5Wbf2nmZ8E5XcbgOa78dSV5hdAkLz8kT_4egk9VwPg,1031 +torch/include/ATen/native/cuda/fused_adamw_impl.cuh,sha256=X8R9ivscXiFBjK9tjUmpO2c2hMqZI1GMJ1NY_UaHros,943 +torch/include/ATen/native/cuda/im2col.cuh,sha256=QtBLx9F8CS8ieF0Qt3DUd4t_rOtEkTchwE9q9677lGo,9700 +torch/include/ATen/native/cuda/jit_utils.h,sha256=5NCqusfAAllICo_3yt4niTP2uyQA6CN8uT9Gbyik0BI,7111 +torch/include/ATen/native/cuda/reduction_template.cuh,sha256=1fU7oF_m-gpIfL6qB88SZGjGe7VigdZOp68lqTcVByg,21668 +torch/include/ATen/native/cuda/thread_constants.h,sha256=z1O0StVe8zM32AntZxA9QleQp_4CSmaVQ8jY8165mCo,660 +torch/include/ATen/native/cuda/vol2col.cuh,sha256=TtCL2LxGCxKD6i1wBqb1VJSquP8IyGJGvrj9SZ2-w_w,8093 +torch/include/ATen/native/group_norm.h,sha256=XZikGRs9EiWurMtDJC-c3sbQtxuqk6DAVw3RDdgXGko,905 +torch/include/ATen/native/hip/bgemm_kernels/bgemm_kernel_collection.h,sha256=Wj3tOI7Gp6C03Lq3S2v_GZcN0hi_TT3ITZZfxuEtBGI,3454 +torch/include/ATen/native/hip/bgemm_kernels/bgemm_kernel_template.h,sha256=AEfs-umzSpgJQZO5FuGcaj3WZbp8FkWEo_A6r3f2fQw,5978 +torch/include/ATen/native/hip/ck_bgemm.h,sha256=_BRjEVpvh6re_noptjXsQOvsDOv9pXnqVvE8UGsB0MM,445 +torch/include/ATen/native/hip/ck_gemm.h,sha256=77LCLXoNKU0REoa-_H-MelNNtVN_WihPsvtypEDDies,622 +torch/include/ATen/native/hip/ck_gemm_template.h,sha256=zhWx06KbkaGFkxcyr4gk2M2fdHf0Xgim-lPrpWjGJtg,15696 +torch/include/ATen/native/hip/ck_types.h,sha256=u59vfJCMlf42whYRxtmT_wD4Vu1bzxULN4-Shi6i70s,1642 +torch/include/ATen/native/im2col.h,sha256=L6_GHB1DZOIsaYSTPBO-yRMw2EdY1hPVNycA-5D4uCM,5237 +torch/include/ATen/native/im2col_shape_check.h,sha256=a-cYNZEI3uilB-BfDx8LX4fc0gEhirlFnxlLvKc2C30,6935 +torch/include/ATen/native/kleidiai/kai_kernels.h,sha256=iv5BHjhtpSCCm9jcjBuOGUoa8k0LWO80uZ_ytgzHG3g,1046 +torch/include/ATen/native/kleidiai/kai_pack.h,sha256=FtXf9TBwxwYSZfglWrQG_qAYxWSlNW7O_WaloVT4gzc,2709 +torch/include/ATen/native/kleidiai/kai_ukernel_interface.h,sha256=dRodnLUWaCvydRMan0zLHYPvPW5JNHKbJicThMXUTX4,4875 +torch/include/ATen/native/layer_norm.h,sha256=dpzIyRnYyVyxdPEnBOmd9PB3TezD_hjZJfAOQCTqydQ,4203 +torch/include/ATen/native/mkldnn/xpu/Conv.h,sha256=1fwV-B2ilvaFxPl5-ehA3OMMVTyz_nTBLGxQjsz_L-c,1557 +torch/include/ATen/native/mkldnn/xpu/FusionUtils.h,sha256=sons50Y3j0fpW4T-KCLDhgc2ABSExlA_1IKpwdVcTkc,1745 +torch/include/ATen/native/mkldnn/xpu/detail/Attr.h,sha256=D9BMykit5UpSsPr2vRLz087LU_wPyVRjlhAh0Gsk1Ik,17414 +torch/include/ATen/native/mkldnn/xpu/detail/DnnlExt.h,sha256=olZqNNuGMrXOS_h3GdqRDCaDOK8hWQQTz1sGoiRyMO8,18042 +torch/include/ATen/native/mkldnn/xpu/detail/LRUCache.h,sha256=ueh1w15OHZAblHyyQb3ZopXUoM0dtE3fNuRHEzWjgNQ,2461 +torch/include/ATen/native/mkldnn/xpu/detail/Utils.h,sha256=eCoS2O0kFvhl6wwPQMVp_2FPj9jcsDIgYlJ7rEggVeI,3856 +torch/include/ATen/native/mkldnn/xpu/detail/oneDNN.h,sha256=AcFP_TrBzXqY_DQs2zkI7wWOFbtEPMoCwGKvzZKACII,5257 +torch/include/ATen/native/mkldnn/xpu/detail/oneDNNContext.h,sha256=BY0nvU2Ljlhky1vYib8wV_4zywRUlDsg9QBKCVysqe4,2712 +torch/include/ATen/native/mps/Copy.h,sha256=_YKq-Fg6ODQ-TAPEjYrSqeeY7D7zL20cJFZICv0rbTA,295 +torch/include/ATen/native/mps/MPSGraphSequoiaOps.h,sha256=j9a0cGPKY5ri7knTqM5yWPhBoOShwupzsx8aGG1BY64,1519 +torch/include/ATen/native/mps/MPSGraphSonomaOps.h,sha256=_QSoOMw5_GnOlyRq1eyJjvBai_xP7aIjX7accmmCZus,2328 +torch/include/ATen/native/mps/MPSGraphVenturaOps.h,sha256=WhReGzEYloh_22V0-EACB8JnTRIO4bUJOc7fKcSPSkI,12217 +torch/include/ATen/native/mps/MetalShaderLibrary.h,sha256=5hmSDehz2MHc6ZddvgOi1L1NeU-GQo9JUKn9sL1ON40,5795 +torch/include/ATen/native/mps/OperationUtils.h,sha256=aE0fo6kKx6KyskVA-qG8l0rQ2BgbyNEdvBwnkSv-vvE,24946 +torch/include/ATen/native/mps/TensorFactory.h,sha256=7BBkSseMjPhXxAqnKmyRNqi_GwgPw04k2D6EsyPXozg,1004 +torch/include/ATen/native/mps/kernels/UpSample.h,sha256=wG-vi8-lBCMMeI_hRDdncNB19Rq21OMJCIDrnT-dNjg,451 +torch/include/ATen/native/mps/operations/BinaryKernel.h,sha256=PtIAJJqp9aVc_gHvlAnoZexkFuvr0a7-_Jg8jJr_v0Q,260 +torch/include/ATen/native/mps/operations/FusedAdamAmsgradKernelImpl.h,sha256=8I67KBGTTNWYzVFPw2hSPKaq8CYgE7s8r7NBu96R_t4,981 +torch/include/ATen/native/mps/operations/FusedAdamKernelImpl.h,sha256=Ttekjx23nGzHX_fkfKin8ig4kXOug8-f_kUDLawSdCQ,888 +torch/include/ATen/native/mps/operations/FusedAdamWAmsgradKernelImpl.h,sha256=IPOcRMd7YbeLrvHKIYVthlxv32RDdZIpUnGf1B1Hcq8,970 +torch/include/ATen/native/mps/operations/FusedAdamWKernelImpl.h,sha256=Wz2SlQJT3If8HwArtmMM1z7d_IpvL-y7t26TYyZ1doE,891 +torch/include/ATen/native/mps/operations/Indexing.h,sha256=eCtdftMyGKyUNvMWnj6YJfZRjd1ZbAhfR9zmk5R8mEo,247 +torch/include/ATen/native/mps/operations/MultiTensorApply.h,sha256=gNPqaJbjZib1dr3fQTGjYlKolkD0-e-ZUJ8xieO94zc,16024 +torch/include/ATen/native/mtia/EmptyTensor.h,sha256=E6TyC1nvu8-zoWP6mmTQPaFqiwro40iBKCA4FsID9ug,1082 +torch/include/ATen/native/nested/NestedTensorBinaryOps.h,sha256=6D1jjNcQ-ojBBxJXZchN89hxxjZAXinlSAhOdX_WD74,413 +torch/include/ATen/native/nested/NestedTensorMath.h,sha256=caTnSOffiNiq07gP96F8tVdB6OpTAupkvnHWldJn5fM,2718 +torch/include/ATen/native/nested/NestedTensorTransformerFunctions.h,sha256=t6oDPl1xgxh0t9LVPo0AiE3UNJaZctwctPOPS_6klQ4,2832 +torch/include/ATen/native/nested/NestedTensorTransformerUtils.h,sha256=Et1TSj1F6bQcynbQKxi8mAyKz_q0JzQzLOS1qHoPuhY,1398 +torch/include/ATen/native/nested/NestedTensorUtils.h,sha256=C74nXkuVY1cE_vj3WzIoSW2TSa4S3ZJ41PPyaOvQdUU,15248 +torch/include/ATen/native/quantized/AffineQuantizer.h,sha256=XsJeeClMjHA5nZKAyTix2sC15VKFg2CfL-N1YkeBGT8,3715 +torch/include/ATen/native/quantized/AffineQuantizerBase.h,sha256=hdkiUxCt0aAn04028nAJfSWiQX_d9eaSu-h1g9NKb5g,1427 +torch/include/ATen/native/quantized/ConvUtils.h,sha256=TDqUwX4q3fcVmYD4XLcYEwzVA7QMgAYVJnb6_4QnV-s,2240 +torch/include/ATen/native/quantized/Copy.h,sha256=SWata_3Pf8OoYcwynTB-Au4tT8FG3K7unvAT9-iQT6Y,156 +torch/include/ATen/native/quantized/FakeQuantAffine.h,sha256=PL9RqDhUaUzsZMbU64M5I2aPmaWEEmi_EYNqph7Fxgc,1787 +torch/include/ATen/native/quantized/IndexKernel.h,sha256=VPWwndaRYKXMBOv8NR5JN5qIUWfk6WFIxUkA-BBz8cw,580 +torch/include/ATen/native/quantized/PackedParams.h,sha256=tlgLUDIx-LXJEmA7CbejFLx1bWP3UZouZ9v9MZ_4ZCs,4739 +torch/include/ATen/native/quantized/cpu/ACLUtils.h,sha256=fhVmZUsBcls5YUp922-ZUeCbO6PkYkUCLpu9IpneTic,7887 +torch/include/ATen/native/quantized/cpu/BinaryOps.h,sha256=kjbF1hZjsWWX3ZrHEIKMuVuaDGUGtAvotNR2zukBOXk,168 +torch/include/ATen/native/quantized/cpu/EmbeddingPackedParams.h,sha256=1Eud4Yr6DEQ4ouMcW-PB3wF3ZLITAcnKut6kjC0oTdY,921 +torch/include/ATen/native/quantized/cpu/OnednnUtils.h,sha256=mwidS7gGZhSN6aQKnUBmSYIaCRR05cJ2K5RJmHhsoPE,13923 +torch/include/ATen/native/quantized/cpu/QnnpackUtils.h,sha256=6Jit8k3FuGE8bOuFdNd4mTQHtmDu8ZGh2XqRUVMv9bo,17410 +torch/include/ATen/native/quantized/cpu/QuantUtils.h,sha256=BNOGyO_dex4AIdIfjpOD6KAjen1vblhpCKRas-OfYsQ,8331 +torch/include/ATen/native/quantized/cpu/QuantizedOps.h,sha256=QSv5t1_NDvcBy4SHTbjEyWV7jqF56QG_Lt2OHPYTh98,8610 +torch/include/ATen/native/quantized/cpu/RuyUtils.h,sha256=_9kljo2FJzjl7f4BntrqAX7MoyEewcZcWP2Rb0zICkw,338 +torch/include/ATen/native/quantized/cpu/XnnpackUtils.h,sha256=UtASfpiXS3GB5oJtV_GFKYSvzCPuanbzpmWuB57ebN0,14105 +torch/include/ATen/native/quantized/cpu/conv_serialization.h,sha256=HJKS18afFJveFkgHLyVxtyXMiiQSR6BDH7XKP3HrymE,12888 +torch/include/ATen/native/quantized/cpu/fbgemm_utils.h,sha256=WHuczIFiQfLOPzCaB4RscjjU1vl5MfEZztbx7EfSEHg,11937 +torch/include/ATen/native/quantized/cpu/init_qnnpack.h,sha256=uHbq5Eujxxe1RKUikvWqMYLYt8965EgN9LH7xsPXxCc,121 +torch/include/ATen/native/quantized/cpu/qconv.h,sha256=h_MCs7VzIpH0goNjFTUVmF3Cii1ZB8xF2P5yhD8p-Tk,3555 +torch/include/ATen/native/quantized/cpu/qembeddingbag.h,sha256=ETrBa1I1wrnXj876iJlXxCpB0rrMUNNEY5QUswUiDOM,1015 +torch/include/ATen/native/quantized/cpu/qembeddingbag_prepack.h,sha256=iLlcFvFsb_YdoJnyXLcH5MHIO3BIujnytNu8VjKSzJU,294 +torch/include/ATen/native/quantized/cpu/qlinear.h,sha256=FTTTVsfAnBghhiImbw98-GLOAcyQ4zxbX3N65y_Hz7s,1678 +torch/include/ATen/native/quantized/cudnn/utils.h,sha256=hdCazqnXevV4Km7NUgyrL5UAJxA51Dv94zhUyZ_oiAA,10482 +torch/include/ATen/native/quantized/library.h,sha256=YCMzrAy8K2jUFF_BTEfIeQGfL9R37_dj-gV0dzADc5o,189 +torch/include/ATen/native/transformers/attention.h,sha256=ru4mKNP-VZ4Wn8MiAOxIW51IWcUH4L1yIaYqGsCSQ2A,2266 +torch/include/ATen/native/transformers/cuda/flash_attn/flash_api.h,sha256=LyYI6mZ4H6gfOxKax8dkutyg20EKtGVr7LKFs905h88,5354 +torch/include/ATen/native/transformers/cuda/flash_attn/static_switch.h,sha256=CPjlQpYIHpOjFHpsARSBVOVwDHowbJMbEC2ZhU7jgNQ,3767 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/debug_utils.h,sha256=C8cCnHfqQsgUTpKBSnQXx7Say03uAC-6B2OjWn7ifAI,10254 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/epilogue/epilogue_pipelined.h,sha256=HDVM8Tn2rmsF_SLEHjsYrQiNnRpxZfWKB5fDBdWtRfo,22287 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/epilogue/epilogue_rescale_output.h,sha256=AZN8vZD4N7oKoGdKERtWt6L9rJOs171GOEBmomMQ94w,7780 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/epilogue/epilogue_thread_apply_logsumexp.h,sha256=xcp3Ea5PlM0rHDzYJ1EUtXQLgEdIyk0IxQG-2fQvaUE,6111 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/gemm/custom_mma.h,sha256=W9tMZgbXOnb9dA52Lc2pCxdQOdXhPOwY4bB04ekXk5c,2491 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/gemm/custom_mma_base.h,sha256=koHwfOu2VPsxB6QVCpuEClSgMYlX7IxMlTVYbWNptsE,6241 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/gemm/custom_mma_multistage.h,sha256=K3PqdQ4vJFXUKittzfJVPtIjoe_NCGVoiGbzJT3YC0E,27281 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/gemm/custom_mma_pipelined.h,sha256=ccK7ktijulkmzyfRv0ZXzWGwGPnMbwPZNzhrux9IdvE,14145 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/gemm/find_default_mma.h,sha256=IjYTmRyWUgttvu7-sWC5SEg_gMtggA6B8YTCKg0zWZQ,5176 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/gemm/mma_accum_lambda_iterator.h,sha256=7GZmJvSd1LZXCj8oRzW_OLu9XVa1PEn_0_8fk0VGJ4Q,12350 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/gemm/mma_from_smem.h,sha256=qhjQ62gSA30ShHFGSiCIcZLIQZPo94OWuNOxo0QqvOg,68800 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/gemm_kernel_utils.h,sha256=zpFLdredksSHpEz8PtGfQdqUfQNUZyq3FkiQkfsKfL8,8608 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/iterators/default_warp_iterator_from_smem.h,sha256=xG2MAVB6SgSfeEVFN5syMZR1D5s0s0AKAg8szCu5Etg,5827 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/iterators/epilogue_predicated_tile_iterator.h,sha256=vuKwz3FTRqRclqRaFrvn82Kip00uxbHNRBCIGjkg_U8,23855 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/iterators/make_residual_last.h,sha256=F_ThtuS0c-IeEG0tT3GgcZamhJ1BvodpwW_S20Og5mY,1650 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/iterators/predicated_tile_access_iterator_residual_last.h,sha256=0X7DlCKeUuaTzqhTaU8CePgTYk3HzUl-KirOUV0dn_Y,64466 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/iterators/predicated_tile_iterator_residual_last.h,sha256=iAozg9cWtQ9VJ1ZNAeBmqOq-rOk6U0ViBlxV-k-q8iA,64499 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/iterators/transpose_warp_iterator.h,sha256=jrRm5-MBPaMtCgg6Pl35eNeYO4Oi3SpeNixSaJKuiU4,961 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/iterators/warp_iterator_from_smem.h,sha256=Ppf3s-rny1wh-5sYdhpkcrCxsxeSNQjGoM_PgkCYMlg,10055 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/kernel_backward.h,sha256=yuVELSpFUTR9IV8pJzWM5yjifxXbwz9Xw_SKWuLTlbQ,100034 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/kernel_forward.h,sha256=bcwdQPbwqSrzo6PCYMrCilS0XH72e2cAOuUYeNxBLTc,54114 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/kernels/cutlassB.h,sha256=KyYHzyws8sFiR1SFO3ZqvXW55X3YyhS2sB2sunlQnAM,96641 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/kernels/cutlassF.h,sha256=hl4hp7_ewjGtBYobjLHuMt2o1S4v1W-AEiTQXy0mBfo,25950 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/pytorch_utils.h,sha256=_1i1AiED9ZWOF9xsrUql5T32aSPDzmUe_Jz0QoGL2dw,960 +torch/include/ATen/native/transformers/cuda/mem_eff_attention/transform/tile_smem_loader.h,sha256=eY9VxKR_rbqtETFtehdfEGP2ORkGngNM9n3ryUai7LA,2152 +torch/include/ATen/native/transformers/cuda/sdp_utils.h,sha256=cpEoPxTwHwSAaN8ael4W50cguS7AMZ8_0eu5DexBckg,627 +torch/include/ATen/native/transformers/hip/aotriton_adapter.h,sha256=X6ZqJKEvvKvjDZuInaMdOA_n_Xr_zLSkr_4e-fwkEyo,3710 +torch/include/ATen/native/transformers/hip/flash_attn/ck/me_ck_api.h,sha256=UZSFtO10XX9hLAVucAtDFUzxwYjObc97s2EFHed88HE,1779 +torch/include/ATen/native/transformers/hip/flash_attn/flash_api.h,sha256=sfqa9GrdHuHCPIprKtJP_A-erfH85BIvoUV3NJNJYfg,21961 +torch/include/ATen/native/transformers/sdp_utils.h,sha256=o4syNTCL0Ocs__YSTnHNmaAN9mb2cMOQqyDdqeWlrZE,2871 +torch/include/ATen/native/transformers/sdp_utils_cpp.h,sha256=fBmGNUgt4TPLy8BKlhYNIVtTSpQmKqU1Q5h8ByZJet4,17917 +torch/include/ATen/native/utils/Factory.h,sha256=o3dvnszZ0_LGzMP6Jz-oZCljttPC70oZGfjfWRxRC5o,487 +torch/include/ATen/native/utils/ParamUtils.h,sha256=StodSX1GpH700thoobFxf1IuGD00-XAcgwfkKIgqXfg,1198 +torch/include/ATen/native/utils/ParamsHash.h,sha256=4d7c40z9WplS5EyNfBGU-QTnki9ta4M9QxufxTInsO0,3124 +torch/include/ATen/native/verbose_wrapper.h,sha256=3Xfi-C_0uzhaiTnuCN0KgTg3OAAUlIbK_-q9AglaUuY,193 +torch/include/ATen/native/vol2col.h,sha256=yjxz_iCNWzZHX41_Ri_AaQlmqCD8sWqA3ZqA9PZ5l4U,3555 +torch/include/ATen/ops/_adaptive_avg_pool2d.h,sha256=8BjBR-CaFSs3KCtvlKtBm0GcxVyT3vIztA-s0WJlv-8,4141 +torch/include/ATen/ops/_adaptive_avg_pool2d_backward.h,sha256=T9yT5OCIf1PHBm98YmxgN5-ZJc57m4hxylp_w2LtIp4,1452 +torch/include/ATen/ops/_adaptive_avg_pool2d_backward_compositeexplicitautograd_dispatch.h,sha256=JeLI_X8mSdsmv7f3ukT7TkBg9Ot4oJLStchOWDwFRb8,977 +torch/include/ATen/ops/_adaptive_avg_pool2d_backward_cpu_dispatch.h,sha256=Vvz5ASAMR98dEswKinTmRtFnS6jb9mEirjmGlx_2528,775 +torch/include/ATen/ops/_adaptive_avg_pool2d_backward_cuda_dispatch.h,sha256=FjbZJMJ-hJc-fYJy7mrq-HOHArmXUpDAr1i1_PhL20g,777 +torch/include/ATen/ops/_adaptive_avg_pool2d_backward_native.h,sha256=vdrp-Dk1uF7ewNq2O8rgHvkNL_RxOK0IxwLCa0bpbco,780 +torch/include/ATen/ops/_adaptive_avg_pool2d_backward_ops.h,sha256=zbNQyNazOjr31nRRe7wOYWWY-ESZilf1gQTDCNQh9mE,1893 +torch/include/ATen/ops/_adaptive_avg_pool2d_compositeexplicitautograd_dispatch.h,sha256=eUhdfYXL5lFZYX_w3-c-4VKL1gHZ-yc-JHAZM9Xthzs,1218 +torch/include/ATen/ops/_adaptive_avg_pool2d_cpu_dispatch.h,sha256=9KAZBHTLzD3xgNFL1r8ZfVhMMjBZCv5ctC-IjkFTfeQ,871 +torch/include/ATen/ops/_adaptive_avg_pool2d_cuda_dispatch.h,sha256=_XzGCsZJJxQSDFt8obcbdzm0gyuk6VGQaecK_ot9YnI,873 +torch/include/ATen/ops/_adaptive_avg_pool2d_native.h,sha256=AYeoxxMKZtz2RZuGxv-cXSvQSsYigNbZ3Hjpkcx0d-Q,976 +torch/include/ATen/ops/_adaptive_avg_pool2d_ops.h,sha256=-iBrWteQRPUwTpFj1oJHLlOtarEOXWxtKwmAXa0WheA,1851 +torch/include/ATen/ops/_adaptive_avg_pool3d.h,sha256=KBziWxsNiJwHeu8FZyP87iVdrfRj1KPrkt0iHHq3bcU,4141 +torch/include/ATen/ops/_adaptive_avg_pool3d_backward.h,sha256=O0SLX0j5yRreNpsCGXq-G-4G383hpurXr4n0S3cYFNQ,1452 +torch/include/ATen/ops/_adaptive_avg_pool3d_backward_compositeexplicitautograd_dispatch.h,sha256=IPSxlBRDTEOj8EpFcfVMmFsk8RrxyFVkrBVm__03nN0,977 +torch/include/ATen/ops/_adaptive_avg_pool3d_backward_cpu_dispatch.h,sha256=Cd1XxUGabRmbgumz9a0W5PkkLpTxAf22SNE9OU_k5qQ,775 +torch/include/ATen/ops/_adaptive_avg_pool3d_backward_cuda_dispatch.h,sha256=_oTYBZFPBPelRpzzJsqL-Z0TiztPCJ6YhdYyb1aT5BM,777 +torch/include/ATen/ops/_adaptive_avg_pool3d_backward_native.h,sha256=ktQX15Oy9SXna-BouVfJWnZv3QoVXHM9VHHyTCnuAh0,780 +torch/include/ATen/ops/_adaptive_avg_pool3d_backward_ops.h,sha256=_Rgv_DQAd3c3Rf9CgBnUfDpL1QwvTz06QxSZY9RiCyU,1893 +torch/include/ATen/ops/_adaptive_avg_pool3d_compositeexplicitautograd_dispatch.h,sha256=JAWXab9CmSmvmBr1txqxwK1Fvvm5kR2Vc86geT8EX-4,1218 +torch/include/ATen/ops/_adaptive_avg_pool3d_cpu_dispatch.h,sha256=8ib8k2gXCFUBcJHjwLLarNCGlU9PcT_1B3_QO2VRTP4,871 +torch/include/ATen/ops/_adaptive_avg_pool3d_cuda_dispatch.h,sha256=rMC9fg0uRRKjIdsom01TIQdxDBw5r9wJR6rBKbFxZc8,873 +torch/include/ATen/ops/_adaptive_avg_pool3d_native.h,sha256=96QF-7r61Noy3uNeJqvLAF4LGa7YBXAAOrtqNMeVmFg,865 +torch/include/ATen/ops/_adaptive_avg_pool3d_ops.h,sha256=ezKwIBnyeBogQII52ipIxHHIsVLl2D1E1i-R4OJjc7Y,1851 +torch/include/ATen/ops/_add_batch_dim.h,sha256=asaLImBh8Uhk8hjCXgb8stq9oyu-cpv4fs4DvjSVyAA,747 +torch/include/ATen/ops/_add_batch_dim_compositeimplicitautograd_dispatch.h,sha256=XMCD0HQdcEVa1BVZ1-AQu9xbFTK2PEvtCl6qIHalbYk,806 +torch/include/ATen/ops/_add_batch_dim_native.h,sha256=ZlHHn7EHoRswcbjiFARpy9qVX_1_WirDTYpx882OshY,518 +torch/include/ATen/ops/_add_batch_dim_ops.h,sha256=5NSRW7VALcMWvSKxzJcARghYgsojpBe30ReUGiceoCM,1093 +torch/include/ATen/ops/_add_relu.h,sha256=52BQs31b-gOy2zKWLOaz1Kv_6yixKu3HvLZ0N1n4ETE,2781 +torch/include/ATen/ops/_add_relu_compositeexplicitautograd_dispatch.h,sha256=vgIkXljW9LyU1ufXH1fSQOV8fTw6VeXeNA9Tfs_r7ow,979 +torch/include/ATen/ops/_add_relu_cpu_dispatch.h,sha256=W7fQahaeCmK8IjeYW7e3JcQSgfxAIor1d5wiaOO_wJM,1373 +torch/include/ATen/ops/_add_relu_meta_dispatch.h,sha256=nX5KL5CMnNFXOV7b2Cl5UHXWVX6cqIX5DatN3RjYhDg,884 +torch/include/ATen/ops/_add_relu_native.h,sha256=AeuZIxiE9HDbygD_kDHHDkj7SAkqPX2pMWQe6E5AItM,1128 +torch/include/ATen/ops/_add_relu_ops.h,sha256=Db6pDG-yEfgVpzuCkzYMz3G4oE4-uOnqI1cq55X_j-E,4840 +torch/include/ATen/ops/_addmm_activation.h,sha256=ovHUWFWFKNtTSOWMDzkAOiN7Jlfce2iiMpo7eNpTHuw,1847 +torch/include/ATen/ops/_addmm_activation_compositeexplicitautogradnonfunctional_dispatch.h,sha256=sIopst68KEwCsexgyCnhaKQcVewRkBhvuAambw3VR0w,927 +torch/include/ATen/ops/_addmm_activation_cpu_dispatch.h,sha256=QzxYnkcvNJS_ASTa8XGoe8NNNp8LX8WuaVqsQmbJ5OA,1278 +torch/include/ATen/ops/_addmm_activation_cuda_dispatch.h,sha256=0fer6SRslwjTIbqsNymKPeu-C6vbSZeq3gAoU04eOhs,1280 +torch/include/ATen/ops/_addmm_activation_meta.h,sha256=qz-q7NlIR6dQT8jb6E8S74WZER8_Jf4wQiqEPEoOqag,694 +torch/include/ATen/ops/_addmm_activation_meta_dispatch.h,sha256=D8lzWemIpHzxHTZptByJ2uFoKVhL3m_pRRPA8XJCKRY,1280 +torch/include/ATen/ops/_addmm_activation_native.h,sha256=LjhvDOYCdC6GLugyAW2WAO7B6ApxV4AlrmBb-IcLzZU,1031 +torch/include/ATen/ops/_addmm_activation_ops.h,sha256=OGEKKupGmd7I2kqCBBCiaS_N9o_oNjZsItIJpDqkwG8,2408 +torch/include/ATen/ops/_aminmax.h,sha256=3gxAbr5zvuF5XF1l9Mn4IdnKYOquTey9uSdb6oV_iOs,2250 +torch/include/ATen/ops/_aminmax_compositeexplicitautograd_dispatch.h,sha256=MteznG0qyXebTbb2FKJ_USrhV4HcPaOQEbmk2gA_JP0,1280 +torch/include/ATen/ops/_aminmax_cpu_dispatch.h,sha256=1vCSuD3wnn9AjWbSFZAn8s6TiC3_TfzSd3KWlMZZi8s,861 +torch/include/ATen/ops/_aminmax_cuda_dispatch.h,sha256=-_PjNMZ7ZQw1P1Oiohpll7EcvCoDOmv_lgHrgApoYS0,863 +torch/include/ATen/ops/_aminmax_native.h,sha256=eP_oAQQHK31iNWsdiA-PfpyEeHBX688WgkrIuiqz7qI,906 +torch/include/ATen/ops/_aminmax_ops.h,sha256=bdkGTydoo3jbw5ksChcPxlIHvI2mK7_enbCxpp6-v6I,3384 +torch/include/ATen/ops/_amp_foreach_non_finite_check_and_unscale.h,sha256=aTscZWq0s33IHA0EVSdj_9fKT7V59E2swqUC_NMC6q4,2123 +torch/include/ATen/ops/_amp_foreach_non_finite_check_and_unscale_compositeexplicitautograd_dispatch.h,sha256=fBpAv_Tw20SWKnINqW-Uyiql5FWE9WN6StvSpfoHq7w,1210 +torch/include/ATen/ops/_amp_foreach_non_finite_check_and_unscale_cpu_dispatch.h,sha256=jbIIFTrmYxFN86O4j3ZkF_u4QWo4D67RIuQ1icewDhc,800 +torch/include/ATen/ops/_amp_foreach_non_finite_check_and_unscale_cuda_dispatch.h,sha256=dQAHphuahsSeTuUWzDFme3yWmGF0je5Iotgc0yuNoH4,802 +torch/include/ATen/ops/_amp_foreach_non_finite_check_and_unscale_native.h,sha256=btOClAcDirguOJQtHounvBDoRsQpdbNJ9WgcSv0BbkY,1041 +torch/include/ATen/ops/_amp_foreach_non_finite_check_and_unscale_ops.h,sha256=PgC64dyBTJrY4UBHmCrHLYYY_ybT6FpS1Rz5c8kJ7FU,3008 +torch/include/ATen/ops/_amp_update_scale.h,sha256=HL8ALatQ_zhorSG4dvPHHJq1GUAzESxbVzwrh5Ui8DE,2764 +torch/include/ATen/ops/_amp_update_scale_compositeexplicitautograd_dispatch.h,sha256=lPi7X-wyRB-Xx3Wq0Xy9PkMM-IvaBzZ0YhZ0ToS1Gg4,1408 +torch/include/ATen/ops/_amp_update_scale_cpu_dispatch.h,sha256=-FfPHTbLp7tcTkegrNEFc_BPFLu_NUNb2JxuYR0yr8c,869 +torch/include/ATen/ops/_amp_update_scale_cuda_dispatch.h,sha256=ImG8gSNPCZJcoD117AkuE92U5pHrCqwPRQ-tusYsIak,871 +torch/include/ATen/ops/_amp_update_scale_meta_dispatch.h,sha256=pN2BeNTQkOi3g5pIF9LviKd7yyso4fAP9gSjwDh7otU,871 +torch/include/ATen/ops/_amp_update_scale_native.h,sha256=nq11zsv2ithC5D8q68u8y9mVPI7tGt95xEk0uja82cA,1304 +torch/include/ATen/ops/_amp_update_scale_ops.h,sha256=7UFYPty22zbswsg8-IKQ6FwKuWVS0Emgen7roP2Hh6s,3649 +torch/include/ATen/ops/_assert_async.h,sha256=j1dR4POUfFWT2izuBPnD7nPs-13C9qIevw6v08fWk0s,866 +torch/include/ATen/ops/_assert_async_cpu_dispatch.h,sha256=XuHdadMdTJdaSXspDUHs6UG4U0ZkgcZCfIPP3i9THE8,805 +torch/include/ATen/ops/_assert_async_cuda_dispatch.h,sha256=_ajTb4byimj7KyPeWTxtpt0B9AuY6AyTAPOQjXO7DX4,807 +torch/include/ATen/ops/_assert_async_native.h,sha256=cWHUU80UQ4yKfgQqqeZX6w5MgJLV1yk7vfWd-y8VDYs,726 +torch/include/ATen/ops/_assert_async_ops.h,sha256=sMeGvQVz2CQzpkxvkcdtl82NrwBqDUFbmgvkSREkGfE,1558 +torch/include/ATen/ops/_assert_scalar.h,sha256=bnUXhUJr-cu6fmi39gW18s2jbl0aWHL71GUZ04yRWTU,716 +torch/include/ATen/ops/_assert_scalar_compositeexplicitautograd_dispatch.h,sha256=IZAR5M7TpD3ygs4ronC-qJ_I6WjhmIdMZfuULM7TjHg,795 +torch/include/ATen/ops/_assert_scalar_native.h,sha256=qKFxayi6VGQFXpMmzEFmMRb2KB5SIer1-ncYxuWGL4I,507 +torch/include/ATen/ops/_assert_scalar_ops.h,sha256=3FimdiXU4FGfnzEDezlfpsSIlvM8a-ynQNtcUVLGDyc,1051 +torch/include/ATen/ops/_assert_tensor_metadata.h,sha256=6rgcW-WQzJqQaX9eYGjuUwF6O1JH710fdgPRHk0kkNI,3018 +torch/include/ATen/ops/_assert_tensor_metadata_compositeexplicitautograd_dispatch.h,sha256=k7gtGsGk8makIabacVrHJe93wZyTHiiN0Y7nqq6wP5g,1343 +torch/include/ATen/ops/_assert_tensor_metadata_meta_dispatch.h,sha256=rz5WeFdR3UCYF69aErcsbSceBbhnDWR_WuIBHCQ80Ns,1301 +torch/include/ATen/ops/_assert_tensor_metadata_native.h,sha256=XtBOEilFhTK3kZU2TWh6ks6g9jtCU1p5U9BY7pLeowA,1060 +torch/include/ATen/ops/_assert_tensor_metadata_ops.h,sha256=YarfgW9OiNA9nhNoOBfEqns0TTTsFqkIW_H8skaBp-c,1595 +torch/include/ATen/ops/_autocast_to_full_precision.h,sha256=ersiQmVuu870uwf2BaHC1w7pyfvyGLuG8-xdtPA4jFc,524 +torch/include/ATen/ops/_autocast_to_full_precision_compositeimplicitautograd_dispatch.h,sha256=6Ut2E5QD-UjXFmLachijmjXp_G3jtm7CF0ULFwnzP48,822 +torch/include/ATen/ops/_autocast_to_full_precision_native.h,sha256=nvX-e8trtdq26Hwt5OVYGMePTZKsyTxxMgdJ2D60D5k,534 +torch/include/ATen/ops/_autocast_to_full_precision_ops.h,sha256=sSrjvxmvltC8NWUzYUJABitN-GEf2zQGW-kupNP1HJg,1149 +torch/include/ATen/ops/_autocast_to_reduced_precision.h,sha256=CJ_4Pa6PULJlNqdDKN356p9VpD5y1lrihq9i4lBd_yY,527 +torch/include/ATen/ops/_autocast_to_reduced_precision_compositeimplicitautograd_dispatch.h,sha256=1aZkN7eFNg3n7_nQMXGuvb0cGVxGSh40KJl4Ge5Aeeo,878 +torch/include/ATen/ops/_autocast_to_reduced_precision_native.h,sha256=HhNQ4t_d2b44W8mbvyNBKelu1i4osF2HSVaEHiFxWEA,590 +torch/include/ATen/ops/_autocast_to_reduced_precision_ops.h,sha256=duBn3RQVQ15hLhCT07OeI4REa1Ye59HggTwazEdAero,1341 +torch/include/ATen/ops/_backward.h,sha256=sxPks9JK4nqLpapUjT22KlgVhjY40NbPDDFuoYr5M_Y,506 +torch/include/ATen/ops/_backward_compositeimplicitautograd_dispatch.h,sha256=mFUsAI-2jik1qd-06sFGdsdRSsMFFnAkMypN1V-l09s,909 +torch/include/ATen/ops/_backward_native.h,sha256=5-zGS7izWQANoO9l7-8TiLYA812LTlWVgTiShvSHNRI,621 +torch/include/ATen/ops/_backward_ops.h,sha256=0-lCKZyVHkCfwtY6YKI2SoAtGFutBBLRH8Q1SvLCsJQ,1364 +torch/include/ATen/ops/_batch_norm_impl_index.h,sha256=nW4e8dMbICBmpE4i8_eob0zgCmAnP30r1GqDslWTmFI,1255 +torch/include/ATen/ops/_batch_norm_impl_index_backward.h,sha256=SxwgGymo1dlASZ5ooAygEn4JAQ9JmsuAsH5miybcGzg,1523 +torch/include/ATen/ops/_batch_norm_impl_index_backward_compositeimplicitautograd_dispatch.h,sha256=FPJHZ8kJbqhVen0pQFKbYaIvTSwHWIINyQr8PgtO06E,1216 +torch/include/ATen/ops/_batch_norm_impl_index_backward_native.h,sha256=YiAfPBQG17pl1sMMiYsvRAia9TQOLfO1no3zyvOCkTI,928 +torch/include/ATen/ops/_batch_norm_impl_index_backward_ops.h,sha256=3yARFzn7ARezPDVE9l0Hobt1gNLNWd-qzVpJ1rClOGE,2419 +torch/include/ATen/ops/_batch_norm_impl_index_compositeimplicitautograd_dispatch.h,sha256=vOy0z1Qpriog0LV_XTRZKVsFHGvz7EIsyHM3GlqIVB8,1085 +torch/include/ATen/ops/_batch_norm_impl_index_native.h,sha256=1EuLEOLSFizOpsmeRZKdInJoAyh9SHf5MvAvZojaxxo,797 +torch/include/ATen/ops/_batch_norm_impl_index_ops.h,sha256=qIH1GNuFAU3a8YayoyeXPCWG4gFAPU2iPDk2h3Daxl4,2013 +torch/include/ATen/ops/_batch_norm_no_update.h,sha256=-fkjeGnIIJ5Tia65zq6IPx0qshYnQgIB3D5GNr1k5lY,2809 +torch/include/ATen/ops/_batch_norm_no_update_compositeexplicitautograd_dispatch.h,sha256=NzZBG7IgxAPrgWMtONLaL_PEyOO5n5ASDrY9Z_Zm14w,1880 +torch/include/ATen/ops/_batch_norm_no_update_native.h,sha256=Hxjc4RfgzapUMAlzdXaJ7JBgYN73mpFYDGclBzI-5DU,1172 +torch/include/ATen/ops/_batch_norm_no_update_ops.h,sha256=twFPY6Km0ivnsjBy2vwUTuBetZ_FpePNjiUJAlpa5BQ,3601 +torch/include/ATen/ops/_batch_norm_with_update.h,sha256=L7C1p8fa_Zm23fILt6qUgi5hkdz42JblMvFObhwhuMo,3493 +torch/include/ATen/ops/_batch_norm_with_update_compositeexplicitautograd_dispatch.h,sha256=OZzUPrwq-fw1ik7byqeRecdtmvPeRxxWZrifW8Cz6wY,1042 +torch/include/ATen/ops/_batch_norm_with_update_cpu_dispatch.h,sha256=XJ9VUMfdqmvwtfqHbx5eoFDL14jP-_aYbYNC9Vjo_v0,1732 +torch/include/ATen/ops/_batch_norm_with_update_cuda_dispatch.h,sha256=2rAv4f9nLscayoiePZ2QZFR3ypi3dW76R4qWHgtda_k,1734 +torch/include/ATen/ops/_batch_norm_with_update_native.h,sha256=7Dg9wyg3UjIxlZ3q_pNWx1uJvy4dyFlta7HunE60c3g,2418 +torch/include/ATen/ops/_batch_norm_with_update_ops.h,sha256=LstH96y_8ZzVmhXiSXs7t4_f7jrjPfE8NYMH9eFmPAs,4848 +torch/include/ATen/ops/_cast_Byte.h,sha256=O-R0u4BEHR-K8JHa9NkP7-29A4Jw-868ABsDWxaNhGg,717 +torch/include/ATen/ops/_cast_Byte_compositeimplicitautograd_dispatch.h,sha256=A8Bo7FtgsCwo9Lw_qpxlX_dskXN6CkAF7Dm1HrIyBLc,793 +torch/include/ATen/ops/_cast_Byte_native.h,sha256=r8ca_K-HHFZPC7EaRxCbnIQfHwUY_EsDL-UBpFVapMA,505 +torch/include/ATen/ops/_cast_Byte_ops.h,sha256=pjXDllkuFvr_tVd_2_I1KZtNv3TfMIWJj2oU2LJN2sg,1038 +torch/include/ATen/ops/_cast_Char.h,sha256=TleP8e96sPLVf84mbguWq-CT0Z6bwNdvI6ZoZR2ZT0k,717 +torch/include/ATen/ops/_cast_Char_compositeimplicitautograd_dispatch.h,sha256=l8_rLTLnKmPUDcFblxL9AqD1wFMYjNYOHTZG0r1FFxI,793 +torch/include/ATen/ops/_cast_Char_native.h,sha256=5eto8dQvywUal9BosyiU4OP3RuvVvIb2yE16snr9Lco,505 +torch/include/ATen/ops/_cast_Char_ops.h,sha256=Lb6pcjEw1P19Taia7cn-Lo-b_JI8290owoqYWZ0mWs8,1038 +torch/include/ATen/ops/_cast_Double.h,sha256=C9YDbsxtxiOKXajrv_u7LZAlrN-YGn9NyAhk2zlCYfA,725 +torch/include/ATen/ops/_cast_Double_compositeimplicitautograd_dispatch.h,sha256=NgPrwVgyMIcenixxf_Tt5WPCyciEscJzaTv0qYHDeHw,795 +torch/include/ATen/ops/_cast_Double_native.h,sha256=Ejp0_7dBm3zocyRprGMal74vGnmDjmPd1KS7BWaO6OE,507 +torch/include/ATen/ops/_cast_Double_ops.h,sha256=272qxXiza5a60Nao2IeSLYN9HIc6w8T6SSDvQA5qYks,1044 +torch/include/ATen/ops/_cast_Float.h,sha256=HOaNCu-JWNoiDul1NbMDUzeYBHrxGK53S6Hobize55Q,721 +torch/include/ATen/ops/_cast_Float_compositeimplicitautograd_dispatch.h,sha256=sGLB4Am5BKLcmkexHc-9CpK16SvW7KGbiapIiGBbS5Y,794 +torch/include/ATen/ops/_cast_Float_native.h,sha256=MFRIxMv7L_O12flNVUWDf47sUla-HuENLl58pxuxmJI,506 +torch/include/ATen/ops/_cast_Float_ops.h,sha256=hWF2wgFaFmafJGVlvXbSRHag_Q7qo9JQvOZuKYio3N8,1041 +torch/include/ATen/ops/_cast_Half.h,sha256=D-MSiQFKip7QUb5GEuk3HaQpC2p3hEzF4eeyga5PzAs,717 +torch/include/ATen/ops/_cast_Half_compositeimplicitautograd_dispatch.h,sha256=tRyePzWVLwvGxZe3nLl4byZ4CWYCsXBzTwdUWZAvcXQ,793 +torch/include/ATen/ops/_cast_Half_native.h,sha256=PlREDEiQukCpm1GAY1xwIiZYpBvUB3Mt2h6P4qtTsyk,505 +torch/include/ATen/ops/_cast_Half_ops.h,sha256=ThsM6gYkCW2sDTkQDi6Cnp6FfMPWAmMo4YR7YzTa4Sw,1038 +torch/include/ATen/ops/_cast_Int.h,sha256=UfdtCn95gWgGKXJyaz2slW8fdftjkjf03KKgHR31tdw,713 +torch/include/ATen/ops/_cast_Int_compositeimplicitautograd_dispatch.h,sha256=-59XKkROrFchPfiecd31MhM8OEk7m_HBHyk1sA9CKhI,792 +torch/include/ATen/ops/_cast_Int_native.h,sha256=ePmfT8yqxV-Ohjj3H134r5d-xYSnNNWVPfpIo2L2K44,504 +torch/include/ATen/ops/_cast_Int_ops.h,sha256=QZYmcHTTQYFCBXs_2O4B9wdzdlgho7cGmXhTdh42RoY,1035 +torch/include/ATen/ops/_cast_Long.h,sha256=DrnQXjhnchzlKONR28cLoYyrXcyYE3chAPxVumxNCsE,717 +torch/include/ATen/ops/_cast_Long_compositeimplicitautograd_dispatch.h,sha256=xUpMfaOaTlrop_EaFPhi3EITulMbMEMDSqK0gHx8SGg,793 +torch/include/ATen/ops/_cast_Long_native.h,sha256=sptC5BSJQITGjKh4Sm2qEL0WaNMazsXLR0b-P5YG_gs,505 +torch/include/ATen/ops/_cast_Long_ops.h,sha256=LSWIUrrGa_rw-eYwxrtL0GpBDRf7Y9Vv4l09HPRfElE,1038 +torch/include/ATen/ops/_cast_Short.h,sha256=x15kKRMMZcnvLV60yb8fTk4eqljUAZXVqgfsnJJxjPE,721 +torch/include/ATen/ops/_cast_Short_compositeimplicitautograd_dispatch.h,sha256=iMXfYViZJ1TyUQOfeI_XKhiGzo7yj0y960N7kbk7nVU,794 +torch/include/ATen/ops/_cast_Short_native.h,sha256=Jayv7mdZae3KqjSu_AHsTjyz-bUIXgpM3mmRy7Y0kLE,506 +torch/include/ATen/ops/_cast_Short_ops.h,sha256=8G5TxWV6vdljK3ceLoOqmedU3cATSZByMsLKuLV_X-o,1041 +torch/include/ATen/ops/_cdist_backward.h,sha256=brE6f1DbnReLRLv6o-YYrXPRGvcavpC7GQJd8UMJTlM,1552 +torch/include/ATen/ops/_cdist_backward_compositeexplicitautograd_dispatch.h,sha256=9A90H9iXtxiHJ1M8nIqWZXlljoWGMCOZCNZrBG18SMA,1049 +torch/include/ATen/ops/_cdist_backward_cpu_dispatch.h,sha256=Ni3BgfKM5kOEZT8Plxe-QW3vZd7ktBbGOkkycjUdV38,811 +torch/include/ATen/ops/_cdist_backward_cuda_dispatch.h,sha256=Ux_rbWPpQCYWfSANwgWuJAsbc7c0iG16lOOiUKHGKZA,813 +torch/include/ATen/ops/_cdist_backward_native.h,sha256=oLSVYY2bTFmqs7kDji8YbzY5bqtXOGHtu50KnWMsFdw,736 +torch/include/ATen/ops/_cdist_backward_ops.h,sha256=wlPipzKt5-gxfuPFYkz8tALHx50RQL1wdLHuTGKJqKY,2155 +torch/include/ATen/ops/_cdist_forward.h,sha256=WPEMIvW_M6TEWJfjG2FceWQCWdKBzwTM7SEoS6a-PMk,1485 +torch/include/ATen/ops/_cdist_forward_compositeexplicitautograd_dispatch.h,sha256=Tkr_LCK5qyGLMDMcPIR_9tWAR9zMZLmmYTZH8KHYYDE,1023 +torch/include/ATen/ops/_cdist_forward_cpu_dispatch.h,sha256=U5-ALx7OD_vYMv1WcugQRb-FRYiNu9SPfkI1-Tstp7M,798 +torch/include/ATen/ops/_cdist_forward_cuda_dispatch.h,sha256=tcy9k45kuxsXuxQHiJzUEtXQEUpubDCz4a5Ej6Ee5Tk,800 +torch/include/ATen/ops/_cdist_forward_native.h,sha256=teHw7a6ykDs7gkBaMkb97we2hrowYAShJ_lESj-7T-o,710 +torch/include/ATen/ops/_cdist_forward_ops.h,sha256=WhXmwVTHH-IvhmV2sWRZYrhTGfDXP2eIOlnZyW4R-TA,2057 +torch/include/ATen/ops/_cholesky_solve_helper.h,sha256=uNhpaIPcnjJhgQ2LGv0Z_HqTieIcVC8fSyas0vcW92E,1385 +torch/include/ATen/ops/_cholesky_solve_helper_compositeexplicitautograd_dispatch.h,sha256=R9dhWqSsEkt1nkOVqMzBvQnKT45ljx72pFJzHv1FnSM,967 +torch/include/ATen/ops/_cholesky_solve_helper_cpu_dispatch.h,sha256=43oV4ihJqQtfaq8sY6TAGC6FsRU4XeSqw365JopW-Zw,770 +torch/include/ATen/ops/_cholesky_solve_helper_cuda_dispatch.h,sha256=JXYLAPjiIVfr4JECqAlolanWHcig2qcg5ntyYwKptK8,772 +torch/include/ATen/ops/_cholesky_solve_helper_native.h,sha256=hI2ONRhPBdmJrixIUaT1yudn59qir5iqwgZpxG7AlN0,767 +torch/include/ATen/ops/_cholesky_solve_helper_ops.h,sha256=wC_cX82Xv4xrXoJFAf_6291UvZw3BJzG-SMElmSQonI,1875 +torch/include/ATen/ops/_choose_qparams_per_tensor.h,sha256=hNWq6rQhV7YMHQo3DN2dWGG3sBRFkJMhQQjjY0GZZSQ,805 +torch/include/ATen/ops/_choose_qparams_per_tensor_compositeimplicitautograd_dispatch.h,sha256=esZEMdKWR5RkSQE4sCaDUMhBYvMLOE-DoAKjXFPFahQ,827 +torch/include/ATen/ops/_choose_qparams_per_tensor_native.h,sha256=3fTINZzR5fTvclGatDMvYhH_xXh4ZPULboqXke3R20g,539 +torch/include/ATen/ops/_choose_qparams_per_tensor_ops.h,sha256=2pyzaIptY2E9hQ1FTr8C3aFIipT1N3AFAtySzT9O-eE,1146 +torch/include/ATen/ops/_chunk_cat.h,sha256=eRZ-Z8-h4sklw5BysU9IaN7RqiXznJE_GbaJ9uRimPs,1313 +torch/include/ATen/ops/_chunk_cat_compositeexplicitautograd_dispatch.h,sha256=Sv84YLH9XbleBK10RBeYW9r5g7flE-WhM1RPuO7MQC0,1029 +torch/include/ATen/ops/_chunk_cat_cuda_dispatch.h,sha256=YKs-QGVkHxMQJUGFP2V_IGoYGfOJne-EwtqZk2Tgri8,987 +torch/include/ATen/ops/_chunk_cat_native.h,sha256=3mHsG83eKwsUG1QxxU7HKpkp5e3fFF3jWKy0NpQV2z4,840 +torch/include/ATen/ops/_chunk_cat_ops.h,sha256=cPwWOJmz7Ucw1N1hmgrZ9OkNISoIpG8eTo5_WRxVUG8,1787 +torch/include/ATen/ops/_coalesce.h,sha256=OBxNewAcGusM6jumrW2zUDmytP6eBAOvk0utfTOndfQ,1057 +torch/include/ATen/ops/_coalesce_compositeexplicitautograd_dispatch.h,sha256=MqZGPzLt05vsPP3G2qlM-v14odHE9p_hFBho2phUCZ4,873 +torch/include/ATen/ops/_coalesce_native.h,sha256=vmv8A_ZMuGT4onyB5dlzRWOA3x1u6fT8ZgNbmdRIcdA,640 +torch/include/ATen/ops/_coalesce_ops.h,sha256=OCJT9dEmA566L4lAzLJBjS0RQCUaXWI68xJu9pNNFzc,1565 +torch/include/ATen/ops/_coalesced.h,sha256=jAp0wK0AGKQxsQjt0dVZh0fbvhcg1tlcBSRop18vq3o,1196 +torch/include/ATen/ops/_coalesced_compositeexplicitautograd_dispatch.h,sha256=y-77fkAkkrFMG6exwRBa3_jveOoWvSuLqF-EVlNUV3g,981 +torch/include/ATen/ops/_coalesced_meta_dispatch.h,sha256=xw8dgdJtozkYpwN_n6Jj-izJmM643tDjj3PV519DGEg,739 +torch/include/ATen/ops/_coalesced_native.h,sha256=Se-uixsb3Nu7gVfOFMbByx6qcUe7UTV7zWONYh2BhfI,672 +torch/include/ATen/ops/_coalesced_ops.h,sha256=008BpQJG1m0gSjuY1JJyDpT1eXOPuhwUDPRI1UUkZuo,2244 +torch/include/ATen/ops/_compute_linear_combination.h,sha256=5J54rPgi58oEvv43yJf2RnQ5M8iRUIySfxVmi3w2nZ0,1450 +torch/include/ATen/ops/_compute_linear_combination_cpu_dispatch.h,sha256=zMOsx4HlaNySt8Q6S2IztS8H12Jv7GslE7Tj4bYhyfM,1042 +torch/include/ATen/ops/_compute_linear_combination_cuda_dispatch.h,sha256=kL8uPnJ8onlkXzyYf5MpQXZcpkhlfvIyxgtf535Q-8o,1044 +torch/include/ATen/ops/_compute_linear_combination_native.h,sha256=88hWznlbZYYBIEmb8kS3G_-nnBdNRUxa_-42JrjGgbs,664 +torch/include/ATen/ops/_compute_linear_combination_ops.h,sha256=RyaPZLeNK-szXRgTQh1BQ_Kc6gYC0GeLJCchPTXrYFg,1893 +torch/include/ATen/ops/_conj.h,sha256=dCq40-zqHSKtSK38Nct-jkFVQ_vHIZX5rFL9cipefG4,639 +torch/include/ATen/ops/_conj_compositeexplicitautograd_dispatch.h,sha256=jZikQR9eAS7_84oP6v4-NemdI_qXnbJ9UUk-j6frXvU,763 +torch/include/ATen/ops/_conj_copy.h,sha256=_Rxn5O_TFg4F3I4amfhhanaTLFObr8X61rBLjSu2krw,1067 +torch/include/ATen/ops/_conj_copy_compositeexplicitautograd_dispatch.h,sha256=ITqhMVVxq5_OuH8sPJRg_OSydjhNu8a25UfNSIGcPdQ,875 +torch/include/ATen/ops/_conj_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=5uOogQG4enxDme07Ey3zH9HFZIHf-Y-1FUGgMDwftZE,794 +torch/include/ATen/ops/_conj_copy_native.h,sha256=GdQBuGU-uaUInCThdJNsJ8BL0NXWxGW4Ww5sfo93HuM,562 +torch/include/ATen/ops/_conj_copy_ops.h,sha256=Bfm6gSHz0T0vFjSR32FL6c_JfWJa0Ht7gnL_Iy3Yzxs,1571 +torch/include/ATen/ops/_conj_native.h,sha256=cpVCzD_i5POJObXvbJxWtoiWpurYIWzm_QPYhgbmlvA,475 +torch/include/ATen/ops/_conj_ops.h,sha256=J5KZvVtpZK9Qp_H_zK1fzuVkEez6u6anaM-_ZSTb68w,960 +torch/include/ATen/ops/_conj_physical.h,sha256=MAsjTStJDsPNlgV-Cb-xCVfD3DKPMNNAbi5fVyfx1k8,1107 +torch/include/ATen/ops/_conj_physical_compositeexplicitautograd_dispatch.h,sha256=36yGLH4a7Kbv2Z2kIKESRc6qNFut53VxqPMBlJ7o75g,945 +torch/include/ATen/ops/_conj_physical_native.h,sha256=Cf0G7n1zZYKw3TlFJybEWoQalM_4NsVKEjHjLCPc9p4,642 +torch/include/ATen/ops/_conj_physical_ops.h,sha256=7sm4GMV58h998Y80OknjfhRmW5gIufW6_ZWu4pTkpMc,1595 +torch/include/ATen/ops/_conv_depthwise2d.h,sha256=OpSTajiSMcaOCfr42MMTWWDBF9qXhAKrA6JZO54N0K0,7324 +torch/include/ATen/ops/_conv_depthwise2d_cuda_dispatch.h,sha256=ANfca4BTDuVWJHavHFt_IFDZX9iMMufXvn9PzMi0MSs,2263 +torch/include/ATen/ops/_conv_depthwise2d_native.h,sha256=eIpPSd57MbXoCOW41CSJHO-fhANSKdaR6oigizRwAkg,930 +torch/include/ATen/ops/_conv_depthwise2d_ops.h,sha256=KI8j3FUUggdTANjH8QcWPNK0nx1C-TSwOwXsPXG4Rdc,2869 +torch/include/ATen/ops/_convert_indices_from_coo_to_csr.h,sha256=Z6XjAwggZTIZCRvB12aWSA_G1KhCwzvarLP_61bxDhg,1539 +torch/include/ATen/ops/_convert_indices_from_coo_to_csr_compositeexplicitautogradnonfunctional_dispatch.h,sha256=md898hSb-4WDOxW9KufkwciemixPF_10Ve2vBN_lNDg,852 +torch/include/ATen/ops/_convert_indices_from_coo_to_csr_cpu_dispatch.h,sha256=hKgFj4X_n6sg3csuJ7jny8QZXY8jGPRi9hpKVLgj5iM,1057 +torch/include/ATen/ops/_convert_indices_from_coo_to_csr_cuda_dispatch.h,sha256=Iao57wZRMkkoGouJIRAUiUytDQPBAml9Y5VdSuIDKI8,1059 +torch/include/ATen/ops/_convert_indices_from_coo_to_csr_meta.h,sha256=w9X4oV7BZM7PGiVDG-Zr0cBC64OlD8YrxufCP7R8vnc,623 +torch/include/ATen/ops/_convert_indices_from_coo_to_csr_meta_dispatch.h,sha256=_1Q8s0nRcH5NL4dJG_8S8P1FP75gC-4dROpiWWjG-ng,1059 +torch/include/ATen/ops/_convert_indices_from_coo_to_csr_native.h,sha256=0u8m9p73OXMEzPPDHzQOzC3Pb1nbBDCGqIsOe6j3rFg,950 +torch/include/ATen/ops/_convert_indices_from_coo_to_csr_ops.h,sha256=37jl3dvUPosWDQ7V5HdtDfx4UoCUvM5wtLF948DWmU0,1920 +torch/include/ATen/ops/_convert_indices_from_csr_to_coo.h,sha256=68c-g-JcajGh7Fu2hJs31Iqz38ltJfdJKsjujtSdFQU,1875 +torch/include/ATen/ops/_convert_indices_from_csr_to_coo_compositeexplicitautogradnonfunctional_dispatch.h,sha256=TNtGIa4b90GgtIVS5jNxC8qoBHEjjMF5SdzIMrRLyCc,900 +torch/include/ATen/ops/_convert_indices_from_csr_to_coo_cpu_dispatch.h,sha256=yq_-BrDXjpDFMuVIuGZqybAfopDI1gzebezD4i-nKts,1195 +torch/include/ATen/ops/_convert_indices_from_csr_to_coo_cuda_dispatch.h,sha256=d_AHh3CDUBX4Z6xI-rvGIH31nV1MHRLY8qXCIAEIP1I,1197 +torch/include/ATen/ops/_convert_indices_from_csr_to_coo_meta.h,sha256=pqEKO3JLrLU2htj4NzuW8ZuHXna9ssxCwXt-pLm7Ctc,665 +torch/include/ATen/ops/_convert_indices_from_csr_to_coo_meta_dispatch.h,sha256=DakGc8cs4QhgEXH-DlHrUcnYoLtDKc2e5cQxO0oEKOA,1197 +torch/include/ATen/ops/_convert_indices_from_csr_to_coo_native.h,sha256=rfNdSf0ZotwN9h-9Z-VZ5ECadUfmdoSdB9aL4NsNSGY,1034 +torch/include/ATen/ops/_convert_indices_from_csr_to_coo_ops.h,sha256=F2p0QBcLDYSYK574rHwNGwhRjRhf0P7ulKlexwUnHJM,2202 +torch/include/ATen/ops/_convert_weight_to_int4pack.h,sha256=bggKJRhnn4YkXeMu5WWl0PEFreEjJlAA9bemDogpCGk,772 +torch/include/ATen/ops/_convert_weight_to_int4pack_cuda_dispatch.h,sha256=GxONpxSGHOf9Rsl9ctw8s-HfbPjCue6den_bnKPRU2s,764 +torch/include/ATen/ops/_convert_weight_to_int4pack_for_cpu.h,sha256=O1o-RnDKixGkg43xIhQV2vWIJXBdIKt8pafXiDdPBCE,804 +torch/include/ATen/ops/_convert_weight_to_int4pack_for_cpu_cpu_dispatch.h,sha256=uiT4p7NzJT0pg8hQL4PE_9iYX01KOWF9X_1l2G5sAYk,770 +torch/include/ATen/ops/_convert_weight_to_int4pack_for_cpu_native.h,sha256=ZsznCWdp2QaJWX-Le6HVokzlCjU_zhbmzMxZtJsvgwo,522 +torch/include/ATen/ops/_convert_weight_to_int4pack_for_cpu_ops.h,sha256=3cmxIyADEqAyIj_pEYa92DpXRffERYXnVsChIyxwReA,1112 +torch/include/ATen/ops/_convert_weight_to_int4pack_native.h,sha256=bi9uqHy77WGo5Y4rc2Lhm06cq9tPc6VZINaiNHXNyC4,523 +torch/include/ATen/ops/_convert_weight_to_int4pack_ops.h,sha256=XxHmkKJAG6ih3sAouhNFLNpF6SpDJ23z5HiI0wL-4YA,1088 +torch/include/ATen/ops/_convolution.h,sha256=s6o3C6lCchCD1edO4iZofhM52u-u-D2-vTkDhBd1Sc0,12978 +torch/include/ATen/ops/_convolution_compositeexplicitautograd_dispatch.h,sha256=sFFB29kUy2g3e5CG05IqP-7HDbA2yES0WNVHWlP4HJM,2947 +torch/include/ATen/ops/_convolution_compositeimplicitautograd_dispatch.h,sha256=qAe5gFqygNqNwOWu4uYEsdoJzFS02z40sID7lXLbq7k,1385 +torch/include/ATen/ops/_convolution_double_backward.h,sha256=pmh-9E-TUM__l1W7GUSPrrzJVfn3xyIQbgMgxzmQJHs,4035 +torch/include/ATen/ops/_convolution_double_backward_compositeimplicitautograd_dispatch.h,sha256=uoj9rrfRuX6U7zimcm6Y7GdJFTvllUCQKKbxRpvfagI,1655 +torch/include/ATen/ops/_convolution_double_backward_native.h,sha256=zbybeENk6O1akdf7aKB_K5Ccv49RCt1zRJZKwndFlIY,881 +torch/include/ATen/ops/_convolution_double_backward_ops.h,sha256=SgchYfdb_isvTc78klwYn__rEn49VDWXKJmk-c9QA44,2356 +torch/include/ATen/ops/_convolution_mode.h,sha256=9C060ftJnhfzlkHOvz_RLE2pyMdSVOLoNTmmYEis8tk,2456 +torch/include/ATen/ops/_convolution_mode_compositeimplicitautograd_dispatch.h,sha256=TtIZbqRFSpQWFkkvuBbos5-aTbQWO9WyVzrGVBqQuzI,1183 +torch/include/ATen/ops/_convolution_mode_native.h,sha256=NZueh-4UWUZ6Uwl0X_lAaUNGxevGNVKP4W7nqk8sTog,668 +torch/include/ATen/ops/_convolution_mode_ops.h,sha256=r98XcCwEAgi-nB1vbKmqgn8jGxurEW5k0aDZSIdIjss,1562 +torch/include/ATen/ops/_convolution_native.h,sha256=81IwLG8VWGfZCL4F6fQxUgHPyDMI6jDtVPUUQbgRMAk,1485 +torch/include/ATen/ops/_convolution_ops.h,sha256=ZUdolIql4NOoJ3Urky8xZzEFJXS_GcLPPyYCjev2Mbg,5073 +torch/include/ATen/ops/_copy_from.h,sha256=2f-4BNiEy92wsNAxZG2IcwMJZjmnGiHjiy3tU-5Rn3I,1376 +torch/include/ATen/ops/_copy_from_and_resize.h,sha256=D0B9HxgU8M8oUAd6GbIUUWEQ7O7sO3rYAiUeIKOppw8,1300 +torch/include/ATen/ops/_copy_from_and_resize_compositeexplicitautograd_dispatch.h,sha256=wdWwmc8ggmZTGrHuXMLqI3KwiChhLh1PXzr7sRHDTFo,945 +torch/include/ATen/ops/_copy_from_and_resize_native.h,sha256=hJfM5OK94L9JXa7o_cIrgchOxJTZ3MzyXnKRzPBO8bU,539 +torch/include/ATen/ops/_copy_from_and_resize_ops.h,sha256=UYBQQJ5xIqr6-BxuUnYZMeiSawUkCcMAsA5UaHP586E,1797 +torch/include/ATen/ops/_copy_from_compositeexplicitautograd_dispatch.h,sha256=ThwIHqVmopNlQavWr8FdTUNYxFjxbBYElKenPsAuvBY,967 +torch/include/ATen/ops/_copy_from_native.h,sha256=0KtcAKnbq5kAOS7jR5zlRk7KYmiX7QOdG5ltt16fXSg,547 +torch/include/ATen/ops/_copy_from_ops.h,sha256=HXbTSNOqOYEVymn2qSBg_WNrMLyV83f9voILOdCc7xg,1869 +torch/include/ATen/ops/_cslt_compress.h,sha256=irvsffo5gC9pTOpRxEhlbMcbVp_QbhnBsOmtsUCQ2E0,672 +torch/include/ATen/ops/_cslt_compress_cuda_dispatch.h,sha256=do4dKVCbxCekhM_ytJgcp7iQgwV6ARtxvFeZhD7pthg,731 +torch/include/ATen/ops/_cslt_compress_native.h,sha256=ifnJhI0auJPn1VD0I1wZcW5qK6m_Ymk04Rz25gWTSzs,485 +torch/include/ATen/ops/_cslt_compress_ops.h,sha256=nePHkpyBRd8kk-ctro8bZBz9Yjpo_oy-Gh8OqXrBLVY,984 +torch/include/ATen/ops/_cslt_sparse_mm.h,sha256=8g2wPVwunIlYDLm3EeVxlTN976SjRtJLPeSEGQFg2dI,1209 +torch/include/ATen/ops/_cslt_sparse_mm_cuda_dispatch.h,sha256=jUZCvUQdOm3UaXMciiUVXWuSpnN1CBCZzKSO-g3Vfq0,1007 +torch/include/ATen/ops/_cslt_sparse_mm_native.h,sha256=WnsINWI6kRDkVDqcdT4aw5gCSXIAL5xwAFPtsH9Yzqg,761 +torch/include/ATen/ops/_cslt_sparse_mm_ops.h,sha256=4mb-Zn1CGIFWQkqFvAkizKQRq87URlIx6EYXA38Pyh8,1798 +torch/include/ATen/ops/_cslt_sparse_mm_search.h,sha256=fKBfh9XR09DRsfiCO_1Xz9czE9nPc3BUpeGC8O_JPU4,1088 +torch/include/ATen/ops/_cslt_sparse_mm_search_cuda_dispatch.h,sha256=pLFg6ZYG7057pnPuZnnHrlnbNBPJglGve4XzEnj0piA,949 +torch/include/ATen/ops/_cslt_sparse_mm_search_native.h,sha256=o4yCxLe53k1ZrVArxspW7SOcvpTyoLZd3tWQTpolS4A,703 +torch/include/ATen/ops/_cslt_sparse_mm_search_ops.h,sha256=TYD6BosTp4jZrvAIokq9G_EajJ05n1EDeMNvDxAS_wI,1620 +torch/include/ATen/ops/_ctc_loss.h,sha256=hsrooc8FQOKUFkkKnWNH9Adq3Z-NWgAVL5pwHOX2bHI,3927 +torch/include/ATen/ops/_ctc_loss_backward.h,sha256=Fra7lFIu5VGOA7ZZmyKCmkFxWenDKOmjVr2XWO2SdyU,3310 +torch/include/ATen/ops/_ctc_loss_backward_compositeexplicitautograd_dispatch.h,sha256=kl_9-LmsGN6WMTNelqwUDYDfGEYvrMuBWLJIw2ffnrs,1347 +torch/include/ATen/ops/_ctc_loss_backward_cpu_dispatch.h,sha256=dPm-AFGOpsPQqGUewFPxT6mjN9uxvL3jpBD3U7yXC8k,1266 +torch/include/ATen/ops/_ctc_loss_backward_cuda_dispatch.h,sha256=nNB1uGt5DbIaevZyoRznT_HvUMGtYP56EB0Sc0t_Eww,1268 +torch/include/ATen/ops/_ctc_loss_backward_native.h,sha256=hTmFArD_s-BfsD86v6GKNvuEjnfMSTspcYvBnH-qbeE,1646 +torch/include/ATen/ops/_ctc_loss_backward_ops.h,sha256=yEmJBm9pGYYaOqfbq4gaQww2NNq0tOAjCI5NZtLoe38,4409 +torch/include/ATen/ops/_ctc_loss_compositeexplicitautograd_dispatch.h,sha256=Fq1vpNflvoQktKiMH1k2iDk_YEj7Cs0idp_99aHIMKk,1776 +torch/include/ATen/ops/_ctc_loss_cpu_dispatch.h,sha256=mIzleZfbzRhJgZGmpIEzucFVxTTrS7vBl_rUWT2iXbA,1114 +torch/include/ATen/ops/_ctc_loss_cuda_dispatch.h,sha256=ck6S8jzMRKK6u_EiPPf9KqGs6SwfWuxbuiqYBdwTSRA,1116 +torch/include/ATen/ops/_ctc_loss_meta_dispatch.h,sha256=xefcrBu9JD2xrpA4NoU3iKsghd5R_LCAuOadz2QVRUI,889 +torch/include/ATen/ops/_ctc_loss_native.h,sha256=cqmAWxTDWSH3pGNK6jVTphaynmNlKZpxRZqmln_s0fQ,1859 +torch/include/ATen/ops/_ctc_loss_ops.h,sha256=dfus7S4ttMRZkDmkppWap9OSMFCg1yd6NdYZTwoOwoQ,4994 +torch/include/ATen/ops/_cudnn_attention_forward.h,sha256=tw_Zjc9eKKYUA8q1gUJsTunnjiOzmmDXbDFM65JalkI,4559 +torch/include/ATen/ops/_cudnn_attention_forward_cuda_dispatch.h,sha256=3-UcBDrsgFjb8I02CDBV7Qz4c8AxFyLnMvPn0IJpfHE,1769 +torch/include/ATen/ops/_cudnn_attention_forward_native.h,sha256=soFZk3_a78RFBhAFptP4wpgo51yxaS5iZ8XGtx2JZLI,965 +torch/include/ATen/ops/_cudnn_attention_forward_ops.h,sha256=IQoya0ru8L-Q3Jo36La7dmpT7V61GkIUxdweATdThcY,2628 +torch/include/ATen/ops/_cudnn_ctc_loss.h,sha256=snobLpG-g0d4rIp85BL6kEaFTID6T0LyzHgC-MFklnY,2935 +torch/include/ATen/ops/_cudnn_ctc_loss_compositeexplicitautograd_dispatch.h,sha256=JF0waOMeVxAyo53VBOYnZiuW8ocLlTx_0yb0VBKInbA,1281 +torch/include/ATen/ops/_cudnn_ctc_loss_cuda_dispatch.h,sha256=Gf5sklXww9HrY3XRUTw0Iv6TRQLGFC_FhFdLnNPpl84,1152 +torch/include/ATen/ops/_cudnn_ctc_loss_native.h,sha256=GuCfPcyeqL9Zp-loLyKn0XTVf1rrPPqtKx0m6RQkmjY,1198 +torch/include/ATen/ops/_cudnn_ctc_loss_ops.h,sha256=P1KUK7iUJnuWUkeZXIFxrwWP8K7l6Ao5OFynaR9yNeE,3981 +torch/include/ATen/ops/_cudnn_init_dropout_state.h,sha256=Q-km2h1U5vgf4vglwCpyu5_QpobRTdIKvLTHXPSSE3g,2249 +torch/include/ATen/ops/_cudnn_init_dropout_state_compositeexplicitautograd_dispatch.h,sha256=CWO2TOuEgM6H101FHJp_zK2AYqALVb8rZZsgfQR8TKs,955 +torch/include/ATen/ops/_cudnn_init_dropout_state_cuda_dispatch.h,sha256=_w5k_luDbCWVlAJG_9Q0UDmYVNvTQWWXBkG8dO0xMmw,1036 +torch/include/ATen/ops/_cudnn_init_dropout_state_native.h,sha256=5bVMKYJ3U-oGQtusvtwLGg2KRJJnoyBSfERXLe0Xa6I,799 +torch/include/ATen/ops/_cudnn_init_dropout_state_ops.h,sha256=GgROaywQuKZ29HrvvyodP4yQXPNb1V31Ib-Vf0aMBS8,2328 +torch/include/ATen/ops/_cudnn_rnn.h,sha256=IumGP0y3wJOBDtX2xkXRZ0cb1bTOWHuNjiOxsuQaTYo,13343 +torch/include/ATen/ops/_cudnn_rnn_backward.h,sha256=gPu-g3jwA9D86i43e_HYwF2RVIfN5zwjqWoXr4mEFZc,16458 +torch/include/ATen/ops/_cudnn_rnn_backward_compositeexplicitautograd_dispatch.h,sha256=IgcQrPSoDVCRG9EpPIraKh_lC2aNDFGHshu7nktZPI0,3682 +torch/include/ATen/ops/_cudnn_rnn_backward_cuda_dispatch.h,sha256=PW8ltTcrVtdtjWkT1vDiJgSAzrxwL4OQSye4gqEls90,2125 +torch/include/ATen/ops/_cudnn_rnn_backward_native.h,sha256=XLvwxCR4VG8G0dxHrqwgkUAKCp2zIGplrqwIouijNao,1893 +torch/include/ATen/ops/_cudnn_rnn_backward_ops.h,sha256=TPU2lmqwtvyiA8juQqtO9i4FvcNqUuGlDoo7dYkw0t0,5961 +torch/include/ATen/ops/_cudnn_rnn_compositeexplicitautograd_dispatch.h,sha256=0mwuzcdLNTgiV_lUP7OcBqR_EEoL86gbysLiaM8x6vw,3166 +torch/include/ATen/ops/_cudnn_rnn_cuda_dispatch.h,sha256=dQ-OzbKvKz4b0aed5TnsRj_5IpahCw0wGXPx2L-9W_w,1677 +torch/include/ATen/ops/_cudnn_rnn_flatten_weight.h,sha256=asRlY0q_qVH965RTvulmpmVJEdMi-di-VF9Fe9l7uyg,7584 +torch/include/ATen/ops/_cudnn_rnn_flatten_weight_compositeexplicitautograd_dispatch.h,sha256=BJVzLpteJFddpkcwqbtzzRXZ84oFT0UJcFtR1PVeLes,1770 +torch/include/ATen/ops/_cudnn_rnn_flatten_weight_cuda_dispatch.h,sha256=u_pJP2-G8x-7euEaNl-8FsKjjViffuFU4hfxz6INzDE,1149 +torch/include/ATen/ops/_cudnn_rnn_flatten_weight_native.h,sha256=W0YH5fevS4PUvPePNHadPCtzVW_1zYTnfAy7uU0RFUo,927 +torch/include/ATen/ops/_cudnn_rnn_flatten_weight_ops.h,sha256=CqHK8KjQNhgFHQB2LPNgExcgVIJ1RS3g2nmsbuEa6ws,2787 +torch/include/ATen/ops/_cudnn_rnn_native.h,sha256=9fdbo_0aQZiUSVPRs4kYUKWseZ9u6oa6gIBrn8GoyvM,1540 +torch/include/ATen/ops/_cudnn_rnn_ops.h,sha256=JFBZOJsZl1k16JiRJH7W0vz_Bu27lNT6CqOm5NH1glE,4874 +torch/include/ATen/ops/_cufft_clear_plan_cache.h,sha256=8gnLaapvdU56ZcbnfrQRZJH3JJfaMGDuzSeIx3iwE-A,721 +torch/include/ATen/ops/_cufft_clear_plan_cache_compositeimplicitautograd_dispatch.h,sha256=_zAa6vTMd_tkB4mM8NvpiJxDMeaPhLWa_uUW8YjFjdI,780 +torch/include/ATen/ops/_cufft_clear_plan_cache_native.h,sha256=W7jOmBaN3E8ekKb9MGLVPU92LPEvCKuMlO7mFK9BBrQ,492 +torch/include/ATen/ops/_cufft_clear_plan_cache_ops.h,sha256=LYtPCfcOwE9WARLWNyHHx_N9eq8qO4PZ1qb3B6AAuqg,1006 +torch/include/ATen/ops/_cufft_get_plan_cache_max_size.h,sha256=f3Hx8zui-pOFaQS7ghctB5rDalAHU909t1uwpa8J5aY,753 +torch/include/ATen/ops/_cufft_get_plan_cache_max_size_compositeimplicitautograd_dispatch.h,sha256=lRuQnwaWw3MyqCJVobNl1rkQAGvo56gpvfXlrC2-eOc,790 +torch/include/ATen/ops/_cufft_get_plan_cache_max_size_native.h,sha256=aw0Y347xlTpzwjJjZcbkclIK_bDtAnNGCEaa4ck0BC4,502 +torch/include/ATen/ops/_cufft_get_plan_cache_max_size_ops.h,sha256=Wbal2de7D_goFEz2TvL15E0RRKFXhytEVkZ0l7vPm7M,1037 +torch/include/ATen/ops/_cufft_get_plan_cache_size.h,sha256=knzOMgWr7m0qtCzd2cAe0oONACWzB3A7JQCgVMnMYVY,737 +torch/include/ATen/ops/_cufft_get_plan_cache_size_compositeimplicitautograd_dispatch.h,sha256=2BTxfDzNqtSojYHHw8B7ccMCU592V4JhR_vjt4kdReQ,786 +torch/include/ATen/ops/_cufft_get_plan_cache_size_native.h,sha256=BlhmfT8Nu8TDewcIY-eEl6sKhjjApWkXDA6gWimbJL0,498 +torch/include/ATen/ops/_cufft_get_plan_cache_size_ops.h,sha256=wApHKwMtTBdyOo5wWMYDVn8GqKRj28oqrL0T0esnXjs,1025 +torch/include/ATen/ops/_cufft_set_plan_cache_max_size.h,sha256=Db1IqHfO0Fw3S9ZiB6KcAmwQEfZTsB9_f-OZ7Y1CKPg,791 +torch/include/ATen/ops/_cufft_set_plan_cache_max_size_compositeimplicitautograd_dispatch.h,sha256=DfuJklbzjdJcVOG2ZhX0tCmFNjAy1ysloO0GlJQGO3E,805 +torch/include/ATen/ops/_cufft_set_plan_cache_max_size_native.h,sha256=IwhvXxqeYR-s18u_jgLviCZlmGdnSukvAtL3-EXZWqY,517 +torch/include/ATen/ops/_cufft_set_plan_cache_max_size_ops.h,sha256=S-aHfd59-bQL3xaI2EZjhA_YsGRL2fG9FyyjwigqchQ,1086 +torch/include/ATen/ops/_cummax_helper.h,sha256=MWHaI3Ti_P14Kn8Zfa2emAPo6tcw0nqq-BMVvMdahlQ,785 +torch/include/ATen/ops/_cummax_helper_cpu_dispatch.h,sha256=prmNk9iXFh9bhJf5CY68Zj8DCMOamkPXTtYMwG-72-s,778 +torch/include/ATen/ops/_cummax_helper_cuda_dispatch.h,sha256=HnvH5-1VDTYq2XZxb043--mDlJhnHHq6Cc2mEdNv3x4,780 +torch/include/ATen/ops/_cummax_helper_native.h,sha256=iE9VBIYt5HUfa7l6ibKkzI2ZW1uaxEXmd58ECRoHyNg,653 +torch/include/ATen/ops/_cummax_helper_ops.h,sha256=0thmSGvsMwPZvoZCR0YFjnNoiz0C0KPGq2EiOZSZNSc,1156 +torch/include/ATen/ops/_cummin_helper.h,sha256=PPqvVAVVKq80_s2hE2pYsxMdx-v7E2FH_HVdI_cRipg,785 +torch/include/ATen/ops/_cummin_helper_cpu_dispatch.h,sha256=tcfCKkNvNnUVesdZAJKnoXZJzgPG02ycOCT41lgjV_w,778 +torch/include/ATen/ops/_cummin_helper_cuda_dispatch.h,sha256=aBX8KYjRtYg-DAyII4U9UAtlNWve1xRmJADhYs5VV6U,780 +torch/include/ATen/ops/_cummin_helper_native.h,sha256=wqvnTbOfO_t0TIWyYSMx4G5tbF5D7T0fiOkDFpXgisA,653 +torch/include/ATen/ops/_cummin_helper_ops.h,sha256=75Fe98CwMaxTulcmrOd-KloRmtYcqdQskGMJP9Njy64,1156 +torch/include/ATen/ops/_debug_has_internal_overlap.h,sha256=KEppDwLb4T_9TDQB1mm3443GKYu_BQ1pH2eqafYdiZ0,715 +torch/include/ATen/ops/_debug_has_internal_overlap_compositeimplicitautograd_dispatch.h,sha256=sV2dyBa-zqA9LrUeTsAbnaeNUNYiT4-EiVLehG_l-wI,782 +torch/include/ATen/ops/_debug_has_internal_overlap_native.h,sha256=-GZfKurfbjoDx8Ew2X2IvC68VqZNeynxwHfnr3jPMP4,494 +torch/include/ATen/ops/_debug_has_internal_overlap_ops.h,sha256=b-ImLi4WX_y3ja4YtVpEALRPHivCcLMjowMSbP4G9xk,1008 +torch/include/ATen/ops/_dimI.h,sha256=mQycZj-O_GK8seONlhsNn7lkRyz_C3thIwzKAPmOIHs,502 +torch/include/ATen/ops/_dimI_native.h,sha256=zgF8ABsT9qRbiePds6fpHuA_WucV8y3L6T7Uy3PdMWY,484 +torch/include/ATen/ops/_dimI_ops.h,sha256=Jbb7BLqj1nBP39uokkDF8l1QECE5eJGhLQcSkP1G1Hg,942 +torch/include/ATen/ops/_dimV.h,sha256=KRe2r22yPNxlHR3_BiDpuvjSaAeseh_1hKqNNYFUAQo,502 +torch/include/ATen/ops/_dimV_native.h,sha256=XNF8GomSmRE43VR_uiXbK6Ogn4z5emZt-nWyx_LdwIo,483 +torch/include/ATen/ops/_dimV_ops.h,sha256=Fhd8zl92LsOOn8wx3c2qLq5mU_cUxNEzome-JW-tfKA,942 +torch/include/ATen/ops/_dim_arange.h,sha256=1i6wTl_8m_JYBRzYd99-NBP-qUNMN4wBSb4E_S1ypXc,684 +torch/include/ATen/ops/_dim_arange_compositeimplicitautograd_dispatch.h,sha256=1Ypjbef56celQqCM8-Zaz3dnCH7w8xwEWxd7d3E-5Xs,782 +torch/include/ATen/ops/_dim_arange_native.h,sha256=kqKNTolvuBJhvlud4jyMKxWZBjzW-XkcoO4ibOWsRgI,494 +torch/include/ATen/ops/_dim_arange_ops.h,sha256=e0qBLqD2pm-4rdKUA-F0aXXxJU5A-jFFmTl9kYn6HVw,1016 +torch/include/ATen/ops/_dirichlet_grad.h,sha256=WcTDKL0bFfiI4EvcvCToeeT2hsPggBKxmSDFLcyzfeo,1372 +torch/include/ATen/ops/_dirichlet_grad_compositeexplicitautograd_dispatch.h,sha256=CJ2r5c7rwdF-oFbGBds_bkpj6ckaPTexp-q9iJcUeqg,983 +torch/include/ATen/ops/_dirichlet_grad_cpu_dispatch.h,sha256=2f4DEVNY5upltnWRxuG0UJ4qXW28myAnIerYbWhc6UY,778 +torch/include/ATen/ops/_dirichlet_grad_cuda_dispatch.h,sha256=BrBAlWqCoBf1imK-MlVkXdXVRWWDOBV9JzC8VDuKpko,780 +torch/include/ATen/ops/_dirichlet_grad_native.h,sha256=M3mDvOle-3R6-o16ZA8MiWQFBdnAX1f4yK0czZNe46c,791 +torch/include/ATen/ops/_dirichlet_grad_ops.h,sha256=kP6VDEnYTZJ8enGBrEaDwGH_4mOoID_zBHPRsg3ynXw,1927 +torch/include/ATen/ops/_dyn_quant_matmul_4bit.h,sha256=WZu-HcuYByWfue8I2_9bBwCHkJ7xgVo15SRSfWQ0-48,925 +torch/include/ATen/ops/_dyn_quant_matmul_4bit_cpu_dispatch.h,sha256=s8Tz9VDHFcldQcdlY3xVF-W3sPW8oMbQEgFwq4l60wI,833 +torch/include/ATen/ops/_dyn_quant_matmul_4bit_native.h,sha256=-almgJApYKm-RuGgM8MrBvDjouU7fRJ9pmpZoyvtQFw,593 +torch/include/ATen/ops/_dyn_quant_matmul_4bit_ops.h,sha256=M9Z-zdQcaOWwzQxD-ehE9ZI8vCm_Q2uOLXeEq1kzYMU,1319 +torch/include/ATen/ops/_dyn_quant_pack_4bit_weight.h,sha256=iQ2uJ1ulBI4_EHQP6w9sw4KZIvP7GB0uelC7Kot1mKg,1013 +torch/include/ATen/ops/_dyn_quant_pack_4bit_weight_cpu_dispatch.h,sha256=bLLEU3ZoGyDnu0IyT1RpFU0mql2mT5MyvYIX2czXwYI,882 +torch/include/ATen/ops/_dyn_quant_pack_4bit_weight_native.h,sha256=gPm7MhmLRuhusgyfv13wXa1b1pJGyvc0cx2y_PZu0wY,642 +torch/include/ATen/ops/_dyn_quant_pack_4bit_weight_ops.h,sha256=vZninOnoGDtAyVndBZV8rZ_bqrfFCr67nMTMXoTRS4w,1475 +torch/include/ATen/ops/_efficient_attention_backward.h,sha256=RMYWOa54FxPqAaC9LuDSMlaowUHR4JeChtFlp6wBXi4,5984 +torch/include/ATen/ops/_efficient_attention_backward_cuda_dispatch.h,sha256=dzBXCi6xksLpYJ9e9kncn6dzroxMP3_sRf0xQJrw08U,2221 +torch/include/ATen/ops/_efficient_attention_backward_native.h,sha256=bjZrrVSeQK_LJhkr75h5TGEOWPEZstAe75VMoXXxIdo,1191 +torch/include/ATen/ops/_efficient_attention_backward_ops.h,sha256=x4ie3O4f_2a_-L_WhCSzPveazM8mqZ3GchoI4AIDmP4,3166 +torch/include/ATen/ops/_efficient_attention_forward.h,sha256=J3jj2b5aqrmzM9i5qc7ataeD8MnAZMYPT5io-PVrCBw,5405 +torch/include/ATen/ops/_efficient_attention_forward_cuda_dispatch.h,sha256=TTj_QreJPc9OZgL-AwGfxL1Fh95An9dnmo9S6RzXTSE,1965 +torch/include/ATen/ops/_efficient_attention_forward_native.h,sha256=Ot7CG4uvOYbLnSd6Rwhg5Zg2Dy4pLpLIf-a-0N064d0,1063 +torch/include/ATen/ops/_efficient_attention_forward_ops.h,sha256=Cq5w4JPwH9eftzqUfErN_1BU16-PH2GFGMmzie_OGQE,2874 +torch/include/ATen/ops/_efficientzerotensor.h,sha256=PifLZJoxoWkkL9KKpoi3dRNcYzygBGG9cyzGbQC-Hrg,6040 +torch/include/ATen/ops/_efficientzerotensor_compositeexplicitautograd_dispatch.h,sha256=IUaSge0k6HkAyRavA8GukdP5Lewhrtum9tbYAMlImKs,1090 +torch/include/ATen/ops/_efficientzerotensor_cpu_dispatch.h,sha256=jGP_omHfJMKrOtOSlXTAqs2LXrn8ahiwOL5jTzSJjpc,1298 +torch/include/ATen/ops/_efficientzerotensor_cuda_dispatch.h,sha256=4HkRcxYMlLqlUFXGKeXA3yNpExbkr4Xm8lOm1p63w8A,1300 +torch/include/ATen/ops/_efficientzerotensor_meta_dispatch.h,sha256=ala4R6WhoZGTgr3l2w8F1Hhf02kFWQIGfoBl9Xv5mmE,1300 +torch/include/ATen/ops/_efficientzerotensor_native.h,sha256=bumMr6huCWDBYU5cCXLkOruZ9e5O3SxfmB_UnNPHaWo,1209 +torch/include/ATen/ops/_efficientzerotensor_ops.h,sha256=Fdt5iH6X-c8SV4rnvAaxJ2C7lG8OLru2TUVc3EVh7gg,2137 +torch/include/ATen/ops/_embedding_bag.h,sha256=cbZ8zsdaGJFOU_h5Fv-bwRy1yfV53hfgAPslBErH3h4,3178 +torch/include/ATen/ops/_embedding_bag_backward.h,sha256=sJQptmJg3Ynp_wiEkCGOnr2bI9H8ZaJJveIqpJUrzr0,3550 +torch/include/ATen/ops/_embedding_bag_backward_cpu_dispatch.h,sha256=45iHUyDese2X7uptuAS0tKYk_Gj17O6r07FlfuB_JKA,1429 +torch/include/ATen/ops/_embedding_bag_backward_cuda_dispatch.h,sha256=XhiFNBWLRELJIE9i9KtvOo5IB4qyK4D_M3ZBUEsFcDY,1431 +torch/include/ATen/ops/_embedding_bag_backward_native.h,sha256=zH8o6kn7DhjpZcl699XDomEujzVBT51iwY80fvD4NZA,809 +torch/include/ATen/ops/_embedding_bag_backward_ops.h,sha256=uYZlUThtdZZFM5b1FZshseYyu87I3NzBKaz1rp6FGx0,2008 +torch/include/ATen/ops/_embedding_bag_compositeexplicitautograd_dispatch.h,sha256=QuNQSnHR5iTZvJWKIgx0vPrz6QAfhlT6Q5HPUlVOeYg,1557 +torch/include/ATen/ops/_embedding_bag_cpu_dispatch.h,sha256=TOt0M4kLEjroHtBKm6F-yzZ7etExtkME038pnat2s5g,1014 +torch/include/ATen/ops/_embedding_bag_cuda_dispatch.h,sha256=85YmHY7AuYkNEoBmrFY8G3rP4udNPQmoC3DvkK-CELM,1016 +torch/include/ATen/ops/_embedding_bag_dense_backward.h,sha256=LC3OOvKdXY8nctWqiBrDvVoxZKdFpnhr8BnJ5pcBvlc,9292 +torch/include/ATen/ops/_embedding_bag_dense_backward_compositeexplicitautograd_dispatch.h,sha256=kCzTFP-YRMJJu7VEdbYrmcO51TcACTJB1gt9yfdRS6E,2188 +torch/include/ATen/ops/_embedding_bag_dense_backward_cpu_dispatch.h,sha256=XuPJExOck6RBEh-gr7ySK86ocCQ0ivOHqdAk84-j7IU,1359 +torch/include/ATen/ops/_embedding_bag_dense_backward_cuda_dispatch.h,sha256=eGWimdFJBSovKr5IwnjAjyyCJuySBh7-Xr-igWp7byw,1361 +torch/include/ATen/ops/_embedding_bag_dense_backward_native.h,sha256=HQ-uE_7LUW29hR28XVQsw2E1J-sGWMpUwCFF4Z7Ciac,1486 +torch/include/ATen/ops/_embedding_bag_dense_backward_ops.h,sha256=HrCrVYf-Kn4lRRq5B2CstohpKFd_eKCYbIVLiB5JEdw,3411 +torch/include/ATen/ops/_embedding_bag_forward_only.h,sha256=L4-Df3l-CzvB3rh6GbD6di6vp1Hs-VSpnANhxFV4kqU,3308 +torch/include/ATen/ops/_embedding_bag_forward_only_compositeexplicitautograd_dispatch.h,sha256=TaRMr51VIEw7Ohi_k_1Uu7KXtcyPj_3hduSa9EkTnb8,1583 +torch/include/ATen/ops/_embedding_bag_forward_only_cpu_dispatch.h,sha256=M9JFujbhLEQ1n7aHtMx2jV37HDTr7Qgs53XLYcrtxL0,1027 +torch/include/ATen/ops/_embedding_bag_forward_only_cuda_dispatch.h,sha256=iWskHRVPDV86z6YBRC2GeUZgI4sWhIvak2SoBwdDB5c,1029 +torch/include/ATen/ops/_embedding_bag_forward_only_native.h,sha256=wBylZrfB-MWhoZP118B7IMyw-gU4tMqfwYrD27zTBsQ,1576 +torch/include/ATen/ops/_embedding_bag_forward_only_ops.h,sha256=OKOOcs7RNPJxntCIjZJGHzqJMLK6Ur5fuAw-b1yNweA,3687 +torch/include/ATen/ops/_embedding_bag_native.h,sha256=8oIqwVJZcPvihObgJPtnHBwW_--hIr62cxT0gUHysnE,1537 +torch/include/ATen/ops/_embedding_bag_ops.h,sha256=_vODOtewaonZjFGedxUiEWximIuaX44hEyNhTvuEXVU,3609 +torch/include/ATen/ops/_embedding_bag_per_sample_weights_backward.h,sha256=uFBoigkgsIcHNp7Khfc4fszGueaksAr3o-5DEvBi8ZU,2299 +torch/include/ATen/ops/_embedding_bag_per_sample_weights_backward_compositeexplicitautograd_dispatch.h,sha256=htwTEtG3JsmKY1-yJ-RwrPYBh8WFL663lORtOfsmlHc,1240 +torch/include/ATen/ops/_embedding_bag_per_sample_weights_backward_cpu_dispatch.h,sha256=S_JX79qmZVij_rqqXdyOvOulHpWc-B7Pg6VFFoxmJfM,908 +torch/include/ATen/ops/_embedding_bag_per_sample_weights_backward_cuda_dispatch.h,sha256=3CIHmD-DAh_D-BG1xVGmdS3i37JOJ4HV_xxZoIS6yng,910 +torch/include/ATen/ops/_embedding_bag_per_sample_weights_backward_native.h,sha256=GLSkBhMfv1L0denm8NynKlmVxwxu3t1Dy0yE4VVRFNE,1178 +torch/include/ATen/ops/_embedding_bag_per_sample_weights_backward_ops.h,sha256=VW3Pqwcs011YKBbmzoyoA6l1EHelGyssbtY8FisTqmE,2747 +torch/include/ATen/ops/_embedding_bag_sparse_backward.h,sha256=_eU7UIzTxSIEnavlKvJtXLhh6mKDBH54XV6YNCXm6HA,3257 +torch/include/ATen/ops/_embedding_bag_sparse_backward_compositeimplicitautograd_dispatch.h,sha256=OmKJGrCFMnxhQ9GIirCKXtDwNmrANgCnzZyvWQ5HQ58,1389 +torch/include/ATen/ops/_embedding_bag_sparse_backward_native.h,sha256=BiTcSV8AhPqBZ7g00JpECY-w_f-Ny18KQyCxVgKwSsg,767 +torch/include/ATen/ops/_embedding_bag_sparse_backward_ops.h,sha256=1IR_I8J1JoBQZvIYPDmwDF9jf0dgHvDxS2bEjdTkCRw,1868 +torch/include/ATen/ops/_empty_affine_quantized.h,sha256=mbYVDWg6zWtAiYZV72Nin9LDMicy939owh_ZNtG0jqM,9219 +torch/include/ATen/ops/_empty_affine_quantized_compositeexplicitautograd_dispatch.h,sha256=59U7BWrmPuI9hkZiuL6lHDI74lPUpFDPCt_1G4e71ag,1502 +torch/include/ATen/ops/_empty_affine_quantized_cpu_dispatch.h,sha256=Wz9Bm0Kh8i6mcwkaD6qUt_tZqXelmZi3tVWveRr9t08,1710 +torch/include/ATen/ops/_empty_affine_quantized_native.h,sha256=6CZ9taBBbqsixJzjAVW6Ua_6jbJUnN5g8OqctyJSZ9M,1310 +torch/include/ATen/ops/_empty_affine_quantized_ops.h,sha256=cg9LMOuDLU-ECV3na_YPom68R-RCCkvkLlPVdqhL2OE,2751 +torch/include/ATen/ops/_empty_per_channel_affine_quantized.h,sha256=n8bTAr9cuhO0AO9Q5nHK-swO3W1wAilA4-FM77z7CUU,10527 +torch/include/ATen/ops/_empty_per_channel_affine_quantized_compositeexplicitautograd_dispatch.h,sha256=AGOweF2ztro8EMejc9s0q_hQPjtQywsomTVbRyP6jP0,1698 +torch/include/ATen/ops/_empty_per_channel_affine_quantized_cpu_dispatch.h,sha256=2kxVlZ5fSUwSoGUYH5HoKsmoyPXLPB6eJQY8ErTQyR4,1906 +torch/include/ATen/ops/_empty_per_channel_affine_quantized_native.h,sha256=QiC_c-ZCfKnbwSXRWajo4qO7j3ULiPq2jPj4nBKSXxo,1455 +torch/include/ATen/ops/_empty_per_channel_affine_quantized_ops.h,sha256=6lKtR1-S9hIafuo0HN0rBh9lMJffLLcW6AqK-ImAaaI,3067 +torch/include/ATen/ops/_euclidean_dist.h,sha256=Bu-Y3WK2hGOR-KIA_NF5uGuCESxQq0O1OxnGgcAXX1Q,1213 +torch/include/ATen/ops/_euclidean_dist_compositeexplicitautograd_dispatch.h,sha256=uHbSXlrLcuYEdrGJzbMZ2-aFWm3wWfMuBxhznBYuML0,1011 +torch/include/ATen/ops/_euclidean_dist_native.h,sha256=2gouzk59lCiuBL1xx4iuVV_xAaIUdWObrll8CCHJMtY,614 +torch/include/ATen/ops/_euclidean_dist_ops.h,sha256=74gtFa8yBzrZYrnFGI23wH7Mv_EZg1dmVD4RevH0wF0,1743 +torch/include/ATen/ops/_fake_quantize_learnable_per_channel_affine.h,sha256=sifXVfmMeejV4mSS8cr5uYAk0AAcIcPuyEAqC0h_Q68,2260 +torch/include/ATen/ops/_fake_quantize_learnable_per_channel_affine_backward.h,sha256=S41HWBoD8pM3gRAg9vxFJ0-HStwmWywG6j0-ZDpB9-8,1208 +torch/include/ATen/ops/_fake_quantize_learnable_per_channel_affine_backward_cpu_dispatch.h,sha256=Z3HjXf18jEHoIFJ_3ffWCsJKEn7UAjPv6CAD3SPXYiA,960 +torch/include/ATen/ops/_fake_quantize_learnable_per_channel_affine_backward_cuda_dispatch.h,sha256=twJrFK6zwJkcIxRZmi338cEPADDp4hCVc-M_SB4_TrI,962 +torch/include/ATen/ops/_fake_quantize_learnable_per_channel_affine_backward_native.h,sha256=BAby9eMYxiV1XUbKEP9NRYO3Xc7JMJQn550BO9bdRSE,716 +torch/include/ATen/ops/_fake_quantize_learnable_per_channel_affine_backward_ops.h,sha256=6lUjkdKPC-jR8ei0GqR2k3RczySRVfv-KFZBNc7e4WI,1733 +torch/include/ATen/ops/_fake_quantize_learnable_per_channel_affine_compositeexplicitautograd_dispatch.h,sha256=WYeo_28S6rERoId5--EQBXmtM4k3hplRm4bNPOM-V2Q,1203 +torch/include/ATen/ops/_fake_quantize_learnable_per_channel_affine_cpu_dispatch.h,sha256=5wJlQTF9DVnZDji9DT24FEjrq2PYFn-OnPeCIRbR2zI,890 +torch/include/ATen/ops/_fake_quantize_learnable_per_channel_affine_cuda_dispatch.h,sha256=a7OHKzFejNVNgsmcz8Xh36fTFjOVGp9pjxzKSDxoXnk,892 +torch/include/ATen/ops/_fake_quantize_learnable_per_channel_affine_native.h,sha256=orXxT81SHYeqwmJ7p9yl7PKPc6mBQh8bNNUaUBpLU0U,890 +torch/include/ATen/ops/_fake_quantize_learnable_per_channel_affine_ops.h,sha256=Dk5FOvIRci1Tt4h_57loOyhNo3y4Fn2Fryl9NiUagME,2627 +torch/include/ATen/ops/_fake_quantize_learnable_per_tensor_affine.h,sha256=GYZ6fudjvA1xEcWyonXb-HaG3zZtNwr81tKpArytOJQ,2160 +torch/include/ATen/ops/_fake_quantize_learnable_per_tensor_affine_backward.h,sha256=WacyAL2zIhSjRyESXBfc1cNfsJHao-etmWqckd4MeMs,1174 +torch/include/ATen/ops/_fake_quantize_learnable_per_tensor_affine_backward_cpu_dispatch.h,sha256=1ztJyBf1_t1uUSpn29ubEBpTJKE2Zca02ZUKYE82C7k,945 +torch/include/ATen/ops/_fake_quantize_learnable_per_tensor_affine_backward_cuda_dispatch.h,sha256=eZR2vo-E9FDxhwwI3KLVbh4D6d3wVg06UmhlamH8clQ,947 +torch/include/ATen/ops/_fake_quantize_learnable_per_tensor_affine_backward_native.h,sha256=Vfq-mm8oRaOlN8Sh15trWpvFk0FUSWf7ZQbS_xEajz8,701 +torch/include/ATen/ops/_fake_quantize_learnable_per_tensor_affine_backward_ops.h,sha256=_h3mdrFE6XOVvFil5t--8-0LCi5DmYi8O0JugBK2X4M,1683 +torch/include/ATen/ops/_fake_quantize_learnable_per_tensor_affine_compositeexplicitautograd_dispatch.h,sha256=psAB59dp84lyDfVPrtHMC29sZg1RAFuUyrxaKkfnBrs,1173 +torch/include/ATen/ops/_fake_quantize_learnable_per_tensor_affine_cpu_dispatch.h,sha256=gMuPyptXG0J8p46sumiDlFc0LQtauT2QrT14gy8Te-8,875 +torch/include/ATen/ops/_fake_quantize_learnable_per_tensor_affine_cuda_dispatch.h,sha256=pUwXpNO6buFyLj_9szcG_E1_jBCt4p30qjPpS0Y9CLU,877 +torch/include/ATen/ops/_fake_quantize_learnable_per_tensor_affine_native.h,sha256=WRE9Se2gH9rvWK_6OhtD3ji7HAWIJHsfOe1eGffXCT0,860 +torch/include/ATen/ops/_fake_quantize_learnable_per_tensor_affine_ops.h,sha256=lC82tDq7MSSShgw7hSmKnwPDW4tTEfK3VhG2091DgeU,2527 +torch/include/ATen/ops/_fake_quantize_per_tensor_affine_cachemask_tensor_qparams.h,sha256=P91BseAWOV1yLni9j2ttCGC3EchZTN7xfjIUvAUES4E,2611 +torch/include/ATen/ops/_fake_quantize_per_tensor_affine_cachemask_tensor_qparams_compositeexplicitautograd_dispatch.h,sha256=G5OP8iiLIZOAtseqIUdRq7HbGIRcrF6DFuePGY4AV40,1331 +torch/include/ATen/ops/_fake_quantize_per_tensor_affine_cachemask_tensor_qparams_cpu_dispatch.h,sha256=J_VRqE9ZWG3vx8oVLeGwZFnogRnt-S2zRujG6wBH-eo,930 +torch/include/ATen/ops/_fake_quantize_per_tensor_affine_cachemask_tensor_qparams_cuda_dispatch.h,sha256=oLkSNaG5PCAs0XaF6gK4-Y2yAfcfYnqFYKiYWjHOgN8,932 +torch/include/ATen/ops/_fake_quantize_per_tensor_affine_cachemask_tensor_qparams_native.h,sha256=FdhtKbzoEPRvlWRNxmQFURlrppTD1JszzSdSzMtin7U,996 +torch/include/ATen/ops/_fake_quantize_per_tensor_affine_cachemask_tensor_qparams_ops.h,sha256=QiXQMcqgYSCLCD17TKPu0jcjRaV_9Ns8bSPADzmcZfs,2989 +torch/include/ATen/ops/_fft_c2c.h,sha256=cahtFRYpxq96JImgRHTkvx_sYMbCxPrnszosP_d8ogY,4453 +torch/include/ATen/ops/_fft_c2c_cpu_dispatch.h,sha256=vG9P7rhfhPiolRBiKFzwCCkPX4hgWueqCsM5Q6a9Axw,1481 +torch/include/ATen/ops/_fft_c2c_cuda_dispatch.h,sha256=4sTsoNuL94y-DfqqkHhUhdvfdpjVHRrqSo89XUxSD64,1483 +torch/include/ATen/ops/_fft_c2c_native.h,sha256=aOnyVnsgGbd7VNglnlwlzZgxIZGVSZr_8vK_pErxOc8,946 +torch/include/ATen/ops/_fft_c2c_ops.h,sha256=s761ysrbrbWKnv08LDtJIl5vOrxuqyWOOyG-B8BgZo8,1973 +torch/include/ATen/ops/_fft_c2r.h,sha256=At4AAj-6z-5_l8tudtDhPPkDoYzlYRL-pejRFcVXLf0,4507 +torch/include/ATen/ops/_fft_c2r_cpu_dispatch.h,sha256=0E-hSl-YBGNvGnikAJme9itp-dWOoPtWEOq4jzQpXgw,1535 +torch/include/ATen/ops/_fft_c2r_cuda_dispatch.h,sha256=hbgk4cmX0ZIPHBW3Uk5vSB-_WFCZz776XzLaA7qDMDI,1537 +torch/include/ATen/ops/_fft_c2r_native.h,sha256=xFVk7wmWlm9yVjxn1m2UWNhq0lu6TkUGoHqi7SxiDWE,982 +torch/include/ATen/ops/_fft_c2r_ops.h,sha256=g2-kWjE614_z8J4QkymzY5dLMPpAPcPLRI7uVrf_1AY,2025 +torch/include/ATen/ops/_fft_r2c.h,sha256=ICe5R0ixIOdO-HJGOVVedhr9bBbOsEcKEpZTTRvM0-k,1449 +torch/include/ATen/ops/_fft_r2c_cpu_dispatch.h,sha256=HCV6m89fdPaz42FaYguyEBeVK3uSMVbDVu37R8al-9g,1060 +torch/include/ATen/ops/_fft_r2c_cuda_dispatch.h,sha256=bUkhmkjwaLBYCqKYZilmEX_UlvprzUKiQlbUlwKeNVI,1062 +torch/include/ATen/ops/_fft_r2c_native.h,sha256=S1s7VaZhPfDKiTDC5delfzfZ0jCwt_uBv1l1RWUNV_g,950 +torch/include/ATen/ops/_fft_r2c_ops.h,sha256=GXg-C4tLc8z5BfHlH6fWOBt6KhMZ4iC17b3spc9Unus,1949 +torch/include/ATen/ops/_fill_mem_eff_dropout_mask.h,sha256=yjPQpJuS_GcHkyg9KzTRhg1HSbFEhBb7uJrj8GAJoYI,836 +torch/include/ATen/ops/_fill_mem_eff_dropout_mask_cuda_dispatch.h,sha256=vQYSybPCXC_d0UdKdq6dT_VPS3g3GTehIGDktXx3_QU,787 +torch/include/ATen/ops/_fill_mem_eff_dropout_mask_meta_dispatch.h,sha256=_-mUYXvW1VRaAGykMy9tiEaL4qhpENa_etemoRI8Y6s,787 +torch/include/ATen/ops/_fill_mem_eff_dropout_mask_native.h,sha256=PKn_W_mxp3fwuNmAkuUHagtyPffkSGLFrRAQclOJnqc,541 +torch/include/ATen/ops/_fill_mem_eff_dropout_mask_ops.h,sha256=kdSBP1ZDZo4v3AeTWRx0BLQtQtd_YDQYV0l6bA03y8g,1177 +torch/include/ATen/ops/_flash_attention_backward.h,sha256=BcITrs4TKw21Z4TcbXdcwIv8DcWrG2lJ1tpZdFK7xYg,5060 +torch/include/ATen/ops/_flash_attention_backward_cuda_dispatch.h,sha256=pXwIxsy2rCiHOVUWsaHNF7IkZGogZ-27U1-geyU2kfo,1865 +torch/include/ATen/ops/_flash_attention_backward_native.h,sha256=Fr6NAJRhQeV_81yca578TiXsOYnelO2g3oAQQ5QYJNY,1009 +torch/include/ATen/ops/_flash_attention_backward_ops.h,sha256=W5Y44lGR7IA7YbxR29rhrLInz608_nD3W1jgc8EIn80,2632 +torch/include/ATen/ops/_flash_attention_forward.h,sha256=XSYnriHk1Hsdhjb0tylJOZTil2FGXO_lfkpZFNiPW4M,5359 +torch/include/ATen/ops/_flash_attention_forward_cuda_dispatch.h,sha256=XjdUT1y7y0VeD-eUHL5PyQ-4jZQlYZuat7ZW6D-hYgc,1949 +torch/include/ATen/ops/_flash_attention_forward_native.h,sha256=qLZ-vT8S5S7SihpDseMg-VpNVdFNH9UlfKwhLlTkhmE,1051 +torch/include/ATen/ops/_flash_attention_forward_ops.h,sha256=R2X74X1gdok2mjBwO-9KcLD1lYwgQA_KEYv_N8t_x2I,2810 +torch/include/ATen/ops/_foobar.h,sha256=rAg1YZO2N74pvupe_GpkWKXdyzUVb8MK0VbuQEzy97g,1367 +torch/include/ATen/ops/_foobar_compositeexplicitautograd_dispatch.h,sha256=eTMDwoehka6Yew01hrZlosLyhVbg2eQWEMrecSrVyaM,950 +torch/include/ATen/ops/_foobar_cpu_dispatch.h,sha256=U54bsQYe5HIg0ZNZacgqQQXBZgjbJkUrZk_zFpuULNs,769 +torch/include/ATen/ops/_foobar_native.h,sha256=7zf46jeqpcnYrILSfOWwmO6cNzS501vuFGvRAR7oMAU,636 +torch/include/ATen/ops/_foobar_ops.h,sha256=I_SalqDPvmaSe9CuPan_eB0VIEUHQknzuOuUnoFTRKU,1820 +torch/include/ATen/ops/_foreach_abs.h,sha256=4UgEcCUdOMw0UzrW7N5uOnMhSL-ziy-Otzvg5aUbvQ4,1222 +torch/include/ATen/ops/_foreach_abs_compositeexplicitautograd_dispatch.h,sha256=ea0-d6KEM2NnR8E_kcko4GTlRuqjtS2-0EB8I1TLWu8,981 +torch/include/ATen/ops/_foreach_abs_cuda_dispatch.h,sha256=5UlQXvudjhXWBNRzjmqqyHaJfUqA5ccfQ6hEJp2aT84,790 +torch/include/ATen/ops/_foreach_abs_native.h,sha256=j6j2PRBMt9OaK-_d0CvBH26GxQePuKt9C_mpTyDrQfE,784 +torch/include/ATen/ops/_foreach_abs_ops.h,sha256=pugzkmn1_zb38awLwmpFHvlfm4DISLZ66Onp3z-JgPk,2079 +torch/include/ATen/ops/_foreach_acos.h,sha256=o_MqqUQCpQohKt39G5yO3zrVJ6XWM8YyG45mQXjrDrA,1235 +torch/include/ATen/ops/_foreach_acos_compositeexplicitautograd_dispatch.h,sha256=jrQpMNRB9GT17CHqof06bsQzvvcnfGH0VZC1okd62ZE,985 +torch/include/ATen/ops/_foreach_acos_cuda_dispatch.h,sha256=NRoWwOrZzam4MFnN4nvHRPJa3a_mLnrUh_lJukCtHx8,792 +torch/include/ATen/ops/_foreach_acos_native.h,sha256=UiTX3vB_gtN5X4KIS4MfOTRM5I1o256shqy1LAGCBOw,789 +torch/include/ATen/ops/_foreach_acos_ops.h,sha256=-Xjk7F0hFrprTS8b7Ugs6fs4I3MPhooPrpQDAbbs8SQ,2088 +torch/include/ATen/ops/_foreach_add.h,sha256=nJvItgr9Y3wTjO-ah1BAHBDWB2Hp_fnpPG3ctacf0ZU,4832 +torch/include/ATen/ops/_foreach_add_compositeexplicitautograd_dispatch.h,sha256=a-XbIwCrE_SR_I5oBG8Pz-ptosXI2KOwgEyZ3MfntAk,2450 +torch/include/ATen/ops/_foreach_add_cuda_dispatch.h,sha256=ewlwgufQtICulyoxQOLsPdvUteiugdv9EQBcaeeTnDg,1486 +torch/include/ATen/ops/_foreach_add_native.h,sha256=I8AQTKHFtnZp_7BOIBZUHfadhBWIcu-jxeB8GYeVB1Q,2953 +torch/include/ATen/ops/_foreach_add_ops.h,sha256=M9HorDqYHbfeFkuujCKebwKVUOtG4qbm34RyDh45j7A,8836 +torch/include/ATen/ops/_foreach_addcdiv.h,sha256=Yz-ysRJp4aaKeJG_a-a6hCwKNDYTKgSZ2vQrWb4vclk,4880 +torch/include/ATen/ops/_foreach_addcdiv_compositeexplicitautograd_dispatch.h,sha256=wjJvxJBOn1sUu6ww6yaKDeuwCTsD9o9cPGYKgyEUJJY,2505 +torch/include/ATen/ops/_foreach_addcdiv_cuda_dispatch.h,sha256=vAAK2qZphWw5PllUGaQl7vk_Tu8hHmsVsxwXw1CxJqg,1526 +torch/include/ATen/ops/_foreach_addcdiv_native.h,sha256=36buZv7yxCDm_Kvg8Tkq9ckX3H1yxqvVMpZ6-e25UQU,2861 +torch/include/ATen/ops/_foreach_addcdiv_ops.h,sha256=pIA0tFg1ZUOhcmbKl-PehlkZfGbO9WtNQMgYyuZPzGk,8002 +torch/include/ATen/ops/_foreach_addcmul.h,sha256=ATnD4DBcNTw09pD1WI6raKtxUtkOWS4ebkkN-b4mbYM,4880 +torch/include/ATen/ops/_foreach_addcmul_compositeexplicitautograd_dispatch.h,sha256=j-ediaAs0Kp7iWkFdSZSeCJmH5Wmjg27ZzF5nNhxZho,2505 +torch/include/ATen/ops/_foreach_addcmul_cuda_dispatch.h,sha256=DpgTCL0YXc9a80E5UYbr52EmlCjpJL3Bdf4Q1-DTPVk,1526 +torch/include/ATen/ops/_foreach_addcmul_native.h,sha256=SK_isn09CIROWftskyfk0iD4picU_4h96p0RTZiMVG0,2861 +torch/include/ATen/ops/_foreach_addcmul_ops.h,sha256=JmlXQKbNyUqYWBEu3Qc-Q4zyJi0AdE_HQJgPEAy24Aw,8002 +torch/include/ATen/ops/_foreach_asin.h,sha256=5iTuK32dQ9Lq3REGhoHvZPMpFOqZxZ80kPUQT6zU3MU,1235 +torch/include/ATen/ops/_foreach_asin_compositeexplicitautograd_dispatch.h,sha256=E-mGYa2Dal-OtqVuecxsOCA2vQjbFZLkRrMTLEBska0,985 +torch/include/ATen/ops/_foreach_asin_cuda_dispatch.h,sha256=38H_01JqJQ1F4EWAi94S8tbhzO92Ywy_iVJrHwQpHcE,792 +torch/include/ATen/ops/_foreach_asin_native.h,sha256=Gqc5W7CRL1DLAgJsMdLtwNycS1SXo9rAlBRJt1qRBiA,789 +torch/include/ATen/ops/_foreach_asin_ops.h,sha256=E-KLL54giR-2CAsEjc0je_p2xY9GgxdCuVT47qgh_0I,2088 +torch/include/ATen/ops/_foreach_atan.h,sha256=yx2gtQHlIoy3Pm7pdIhQtrnvt2JVBYa7orZ8VzxsLsg,1235 +torch/include/ATen/ops/_foreach_atan_compositeexplicitautograd_dispatch.h,sha256=6gmtuOJeV_VajZ2quEruWgTyKTdgKed00VGGlNKe60o,985 +torch/include/ATen/ops/_foreach_atan_cuda_dispatch.h,sha256=GWRjqm41W79GslWxuejYEbMBJLDVFxkcAlSz5oyjOVg,792 +torch/include/ATen/ops/_foreach_atan_native.h,sha256=0lOM9BfTt6ZzYPapE6LK3cC9MXdPy8JGtA--CKRKG-k,789 +torch/include/ATen/ops/_foreach_atan_ops.h,sha256=fPkl4gHKgMWN0BrrfFArSjBw8T-sSBU3S961hEV91bI,2088 +torch/include/ATen/ops/_foreach_ceil.h,sha256=JwAKkhWk6FjS9WoMMDobb2g6ZasF-yUG0DlK1PTQ8-Y,1235 +torch/include/ATen/ops/_foreach_ceil_compositeexplicitautograd_dispatch.h,sha256=3iWC0YLq0QGYB8zItvW162YLcy3pE95juzx9E5Vp4EU,985 +torch/include/ATen/ops/_foreach_ceil_cuda_dispatch.h,sha256=ym66WI0oTa768dUfaWaR1Z_-iGx9d6HlmaPqAvR7Avg,792 +torch/include/ATen/ops/_foreach_ceil_native.h,sha256=a__7Z2GNreC3ACVEfZ1Uh7ToveFxkdR6KKQUmhaYdgM,789 +torch/include/ATen/ops/_foreach_ceil_ops.h,sha256=1Mgk1LxfMtjBSJamjCgdNXnlm_-rH2uL2YVhyFFFAhE,2088 +torch/include/ATen/ops/_foreach_clamp_max.h,sha256=_8NcRIoiSu__zs9jvac8m_qGskuARiE3uSsBAshmfRo,3680 +torch/include/ATen/ops/_foreach_clamp_max_compositeexplicitautograd_dispatch.h,sha256=C8esRwf4EwS0-mrTV32qjd5cnVGshlJncynkucBmdLk,1927 +torch/include/ATen/ops/_foreach_clamp_max_cuda_dispatch.h,sha256=kSGVSTx5UF7E3eTENGbq3cM_UYpCjXWK1Y4oZ3Sexlg,1236 +torch/include/ATen/ops/_foreach_clamp_max_native.h,sha256=VsDH7TVMsZkaJkwO3m3oCBPZ0JhLcqosW283ZNZbeVc,2212 +torch/include/ATen/ops/_foreach_clamp_max_ops.h,sha256=o6CeiVNNqvukr1e_B--rS86lRZy4AgRwYKPDIo9ID6Y,6517 +torch/include/ATen/ops/_foreach_clamp_min.h,sha256=6M3aXP1-6-qK07CJ1F7z6OuBD02ndYovm6Y7N1z-h44,3680 +torch/include/ATen/ops/_foreach_clamp_min_compositeexplicitautograd_dispatch.h,sha256=JcjQfhNeCu9L3C3NR2JC2snpv3smHeNsZqRts24G_To,1927 +torch/include/ATen/ops/_foreach_clamp_min_cuda_dispatch.h,sha256=zFVRm881gidg0CMY-C27EZKVdVwefNs8LhPMyqwcyCU,1236 +torch/include/ATen/ops/_foreach_clamp_min_native.h,sha256=bUGQ0aOkVOiAQFovlwDZ1PolbfKwRdqDKWO5Xw8aPr4,2212 +torch/include/ATen/ops/_foreach_clamp_min_ops.h,sha256=WXNnD7C2Rh2Wn-LuwHHnSGni6GrWRVyNLdAlN7BzuHk,6517 +torch/include/ATen/ops/_foreach_copy.h,sha256=yNVnNLFM0vUU4E_998MPdSWxUOuuBk1Ri6RI4SfpXQ4,1650 +torch/include/ATen/ops/_foreach_copy_compositeexplicitautograd_dispatch.h,sha256=obrrBt5AFt80SOwzITSdc4EYLE1dRTQ_OaEacTnHEno,1159 +torch/include/ATen/ops/_foreach_copy_cuda_dispatch.h,sha256=5T0gDhiArLSHK0k0eaNLeMICjZlZ4EzU_XrbZ8MYyVU,765 +torch/include/ATen/ops/_foreach_copy_native.h,sha256=gazc_Xke29hXGcgAaFrWp-2I9dJu-J5IfPYroR0uMq0,893 +torch/include/ATen/ops/_foreach_copy_ops.h,sha256=NndZROIkaa4m4bDRf6F3l74f5QdPizng-R5FAMTSnfI,2514 +torch/include/ATen/ops/_foreach_cos.h,sha256=YXNQmDrEtgKylzaqqm9wx1yogyAzmdcJHPx2lQdN9zE,1222 +torch/include/ATen/ops/_foreach_cos_compositeexplicitautograd_dispatch.h,sha256=fVCVu5MBC3cXgDCjwbshYkOLMTayFDubavCBwJAv1OQ,981 +torch/include/ATen/ops/_foreach_cos_cuda_dispatch.h,sha256=GsjGjHE-sZgyew1RPiPD93Wf4Q6ZK-i9lIZCqaYn4pk,790 +torch/include/ATen/ops/_foreach_cos_native.h,sha256=RBDL9CtuXDtUfesXcZsZ7qqEP7Ij3allTsCpHewXEJw,784 +torch/include/ATen/ops/_foreach_cos_ops.h,sha256=yC83kIweydMQOJaIN4ODEe4N1OriIcoz-SJARxkcRt4,2079 +torch/include/ATen/ops/_foreach_cosh.h,sha256=ryK3VvR-sqlHhICM64GITxTYiZALhStmOGA2OjN260o,1235 +torch/include/ATen/ops/_foreach_cosh_compositeexplicitautograd_dispatch.h,sha256=_Vy1c1V-vuLBqTxD2tvjEB7hiJ1lEuhdt-TzVUxGGkw,985 +torch/include/ATen/ops/_foreach_cosh_cuda_dispatch.h,sha256=Tjd_o4u9CbccpZCl7MsO8cl8h0phD4e73t4pRf5986A,792 +torch/include/ATen/ops/_foreach_cosh_native.h,sha256=dksWlBKDflY0KBaJk6Fa39FcUxCbHOC0XjEtUPHWSvo,789 +torch/include/ATen/ops/_foreach_cosh_ops.h,sha256=zC4MxWBAkNZjT39c7yrYS205x0OWGyQz3w44H_eH_OI,2088 +torch/include/ATen/ops/_foreach_div.h,sha256=naE1sZ3NvmaQuKKPrAeInpd8fhS19pm0Ikh9HbRqG8g,4416 +torch/include/ATen/ops/_foreach_div_compositeexplicitautograd_dispatch.h,sha256=WvYo2ZXeoKBfXkXm_0XKMxri3BOEmKzzes5x_9gJ5sM,2230 +torch/include/ATen/ops/_foreach_div_cuda_dispatch.h,sha256=blhBC-UbhxiAk7kKXdRfqEsaDydYA8bXKHPwUPpoB4k,1374 +torch/include/ATen/ops/_foreach_div_native.h,sha256=CgYWpnmhobz1X1B0dbSdE700GhJMc5PsU1TdWh-CjJc,2677 +torch/include/ATen/ops/_foreach_div_ops.h,sha256=dzpgufzvv35IL6dieKbtAF1n96aEclpI2K0CiqSx-Ak,8296 +torch/include/ATen/ops/_foreach_erf.h,sha256=gFAO9tqi-YwEtqp7GI81yYOoiq5N4UQ29hoXZqBd5-c,1222 +torch/include/ATen/ops/_foreach_erf_compositeexplicitautograd_dispatch.h,sha256=ScUqCzLbjlvsgIyuIYoQDR9xhfbJOfqr_4Cy9eI4NIg,981 +torch/include/ATen/ops/_foreach_erf_cuda_dispatch.h,sha256=WJjVQufaliIg_rLuhVlACskdOsRiofBS6g7uxf_ZUio,790 +torch/include/ATen/ops/_foreach_erf_native.h,sha256=g6my8A0_33qrTmj8L4_MPNflTb1oiP4xodM48kyn0uo,784 +torch/include/ATen/ops/_foreach_erf_ops.h,sha256=b4xahxcliraJq51e4sYNEXjIOus_vjKCcwEeGcGSE50,2079 +torch/include/ATen/ops/_foreach_erfc.h,sha256=_zMVllGsoPOS3tizBFbeuDJ4lrQtOA_UdGEARhdZyC8,1235 +torch/include/ATen/ops/_foreach_erfc_compositeexplicitautograd_dispatch.h,sha256=snQnXp6-Z5zMXG7MuFE8l29xatmzabbWMofdrRJJ-ic,985 +torch/include/ATen/ops/_foreach_erfc_cuda_dispatch.h,sha256=OGrzRWlfkYHpaVDfyvoktU8UQq6QyV7xGtCUT_m4P3E,792 +torch/include/ATen/ops/_foreach_erfc_native.h,sha256=oPBlyDrlZGnHD8snD2wE06uYWTElYWfwURSVZ35vLGk,789 +torch/include/ATen/ops/_foreach_erfc_ops.h,sha256=pXTa8RkxxfYXYKhfj1uPIpGqXzPrvRbV8-FXgqEuh3o,2088 +torch/include/ATen/ops/_foreach_exp.h,sha256=xdp_jnIZFrkK5m36zRaw-Xib4Gh5NziHESrdqHlsdVM,1222 +torch/include/ATen/ops/_foreach_exp_compositeexplicitautograd_dispatch.h,sha256=axGDWCrL-gGaEzm9XT_cdWY934Jx-RWC_dRMtVDzaCI,981 +torch/include/ATen/ops/_foreach_exp_cuda_dispatch.h,sha256=XWlwnhSYGBPeuwbKtqdimPK8GvWeKm_dYO5iKg6iQTc,790 +torch/include/ATen/ops/_foreach_exp_native.h,sha256=qBcwuA3w7wyG4uh0EMCW3r8BG9Axw6Cqx6MBFAfQpRY,784 +torch/include/ATen/ops/_foreach_exp_ops.h,sha256=hr42yueQkpw6C73dpFs5g_c5-VmrYWNkCrKDLAH_uew,2079 +torch/include/ATen/ops/_foreach_expm1.h,sha256=7S_RGrRbHIopd2bwP4mE2DCAd4iAY-bqNdkbGCjdvuA,1248 +torch/include/ATen/ops/_foreach_expm1_compositeexplicitautograd_dispatch.h,sha256=bAuhJuVXoCh1nCxirBElYAWKp4yylHvRUaOhIYm78Yk,989 +torch/include/ATen/ops/_foreach_expm1_cuda_dispatch.h,sha256=D5YmPyI30BE8PM7-_qlIfmWAXNESKCTBaqkopj5KbYQ,794 +torch/include/ATen/ops/_foreach_expm1_native.h,sha256=MFz-4ozlMNn9RJNMNlTIVVCPz5_Dbn3SWU142jffvJ8,794 +torch/include/ATen/ops/_foreach_expm1_ops.h,sha256=xJL5mmKFlawGzsY4WDscmjb9SuP9-qaa4VbmPEYh3jE,2097 +torch/include/ATen/ops/_foreach_floor.h,sha256=rddFHDMJDKzPbJEYzLIJBVa1uv4EKPqBcZMpLL0qv9A,1248 +torch/include/ATen/ops/_foreach_floor_compositeexplicitautograd_dispatch.h,sha256=CVbSrREQnNY_H9hQFYiifp6Zv9i4fal-ikxKsMwGBSo,989 +torch/include/ATen/ops/_foreach_floor_cuda_dispatch.h,sha256=55R0Y5YKLcpj_ChzYu_4yP_RrZcRkVn5C68n8VGhg94,794 +torch/include/ATen/ops/_foreach_floor_native.h,sha256=aEq-dcl5dkWNOsL5tbAHi51MLozRV38_w7JAhnC408A,794 +torch/include/ATen/ops/_foreach_floor_ops.h,sha256=C_sHFIH3xW4o8Rqjw4COJnMKm5hCgI0vfgeCuJd_5oc,2097 +torch/include/ATen/ops/_foreach_frac.h,sha256=czfrA-pXL7NIYxrsfqCcqX8KTI4BxZgiDzXSLnHYRfM,1235 +torch/include/ATen/ops/_foreach_frac_compositeexplicitautograd_dispatch.h,sha256=YXC4zaJ6Htl-7tieimHV-56rXeVJcFArfaKSHe2l34Y,985 +torch/include/ATen/ops/_foreach_frac_cuda_dispatch.h,sha256=IBPJo0ySAGJvURuBXiBrF2Wn_kCZKL-uIusdCA1p6iU,792 +torch/include/ATen/ops/_foreach_frac_native.h,sha256=dOCaXSExk1QHltUjJXbcSvN3cU5PrYJUTWAh7PFwMlA,789 +torch/include/ATen/ops/_foreach_frac_ops.h,sha256=DEPMCsIfXOvmq5WLAcYFMjT8s6EPySGIL6nX66lvWAo,2088 +torch/include/ATen/ops/_foreach_lerp.h,sha256=zgPLUjacL0idtv31pcYJAfWYYJU-snm1xnRJd7siDPo,4155 +torch/include/ATen/ops/_foreach_lerp_compositeexplicitautograd_dispatch.h,sha256=hUXeo5xiWSXzHD5obC73xZv-mUEhzIAlholvs1mcWL0,2171 +torch/include/ATen/ops/_foreach_lerp_cuda_dispatch.h,sha256=TVUAyy3YpqvNmZIVyMeQMSzcDGhurOZL-oP2cRR9Pl4,1358 +torch/include/ATen/ops/_foreach_lerp_native.h,sha256=p3QLIaBfL_CsmnqNm46vpXuPSgNa-jw5UvQGcmn5d3U,2465 +torch/include/ATen/ops/_foreach_lerp_ops.h,sha256=pFf73KJT_rJFgzSJJ1x3a_HI4YNhxxN1NW_lUK_5KzM,7156 +torch/include/ATen/ops/_foreach_lgamma.h,sha256=Xo_qjIHzFIhhxJgiSH9KIUysW7NThv8Pp75f6vSgpBg,1261 +torch/include/ATen/ops/_foreach_lgamma_compositeexplicitautograd_dispatch.h,sha256=G-Y70sbGXrtUWZBMPiDkWCrqoUvc2KIOqBFvMjBW0Bo,993 +torch/include/ATen/ops/_foreach_lgamma_cuda_dispatch.h,sha256=8N9U0VyHcVjoHtuRQsaKxTTzvIef3uZ4qNjcg_Tly4w,796 +torch/include/ATen/ops/_foreach_lgamma_native.h,sha256=SUmnjFyVZgTB5WVkfpMu-b4uUmlMvpeR8PrLHrKly0E,799 +torch/include/ATen/ops/_foreach_lgamma_ops.h,sha256=wgctujAws8gCDRvwEGz-5Th-0i9Isk8NM8OQ6afqgY4,2106 +torch/include/ATen/ops/_foreach_log.h,sha256=LPtZ81ilk6lAvVuDnWcd9oVmlKadgjtiYayKwqWkRh8,1222 +torch/include/ATen/ops/_foreach_log10.h,sha256=kV3dh0aJdDhAJyWFP495OLErX0I543O1PaE-O-3DoHk,1248 +torch/include/ATen/ops/_foreach_log10_compositeexplicitautograd_dispatch.h,sha256=zftQ39NdGmrBSNRxSq6YpZULw3RiT0n7KduxfSRkXoc,989 +torch/include/ATen/ops/_foreach_log10_cuda_dispatch.h,sha256=vuuyxCjXUYlHAM9JE3KOKRv7aLqAuINTDBf58pSfXS4,794 +torch/include/ATen/ops/_foreach_log10_native.h,sha256=y4CLpuy_Iuu9tGE2vcNi2yNXfyOoSFcel0Z1IfQqfcw,794 +torch/include/ATen/ops/_foreach_log10_ops.h,sha256=_kdqf51m2DGK2gTCh-x7dkl5uv1wdqrbSWydg6RyzjI,2097 +torch/include/ATen/ops/_foreach_log1p.h,sha256=ukdWM1kw-uvmJ60t1CXefz4tl__-egwZFylt_yGk3FE,1248 +torch/include/ATen/ops/_foreach_log1p_compositeexplicitautograd_dispatch.h,sha256=qvtQo3yEmIVfYTDryT9XeosFvTlBbl_Is_9py8xZesw,989 +torch/include/ATen/ops/_foreach_log1p_cuda_dispatch.h,sha256=eJGg9QKhLOU6lTcUwbFci09U7R8m2u1rIhjf_wlC9fk,794 +torch/include/ATen/ops/_foreach_log1p_native.h,sha256=anCejA0oSpABgLDTUP_nSYKQ4kJZcmwEHo5hLvq48Pk,794 +torch/include/ATen/ops/_foreach_log1p_ops.h,sha256=QBtU8UULtHOwg7a-u5owmU0hOZYGxOrHQfoddZq-WyM,2097 +torch/include/ATen/ops/_foreach_log2.h,sha256=POO54sZywOvbpEpvXpFUTIMtnNL7fZkX1IMBnok2NkI,1235 +torch/include/ATen/ops/_foreach_log2_compositeexplicitautograd_dispatch.h,sha256=jD7hKl_4czBvV7pSx__dwNaf1DzL_L69wZGyhyve8Xc,985 +torch/include/ATen/ops/_foreach_log2_cuda_dispatch.h,sha256=oOUXvy7zsR-xVGIya-T5mYfdE_VcwwFksrPBIb1VzhY,792 +torch/include/ATen/ops/_foreach_log2_native.h,sha256=DCk6SFGqASEtuBEBm77348lxVkRaIEl7F7iOcpq1wT0,789 +torch/include/ATen/ops/_foreach_log2_ops.h,sha256=7L-AhIdYOzNww9t6y87ZV12t3HTxIeoV-Fv5Kme02rk,2088 +torch/include/ATen/ops/_foreach_log_compositeexplicitautograd_dispatch.h,sha256=2g39xAsehwKUPcCkzbXUXOcDCjMffyy-Wvw4t54tyMw,981 +torch/include/ATen/ops/_foreach_log_cuda_dispatch.h,sha256=O7Cgtxe9NkYqZRutywEMbRqxvKUpQtikji0HI-55X7g,790 +torch/include/ATen/ops/_foreach_log_native.h,sha256=a4-WrfcTxKHDJH-IU_mhlrsdbPxVP3AvfuqA_uVNndI,784 +torch/include/ATen/ops/_foreach_log_ops.h,sha256=9fS2v7OcI_EArjW59dtRkIOLfCjkhlX-la49U_Ibsfo,2079 +torch/include/ATen/ops/_foreach_max.h,sha256=-Zk2MDhl68ypTmSLDpLhanS2xG0UVsc-I8mM7kmh_64,1074 +torch/include/ATen/ops/_foreach_max_compositeexplicitautograd_dispatch.h,sha256=R8nvIk-PLvPz5eP8hgGPMFWJS5rocX0QxYm6_oLsE5Q,930 +torch/include/ATen/ops/_foreach_max_cuda_dispatch.h,sha256=OhOjFkQ0SJn6yTBBCndzkZ3DSvOeFFPmsWovhJ6f2GQ,739 +torch/include/ATen/ops/_foreach_max_native.h,sha256=AWFocT8TAsPS-lFMh8g90xeX9wZWTShhQz8gxuh_nmU,660 +torch/include/ATen/ops/_foreach_max_ops.h,sha256=21NYtnt028-6N136pnyp9Q7Ncc-jJYQuJpBGcE4koVI,1586 +torch/include/ATen/ops/_foreach_maximum.h,sha256=POK6YBnacqciLN3TTio6qdqKZVba0129xlib1cCSJxo,3606 +torch/include/ATen/ops/_foreach_maximum_compositeexplicitautograd_dispatch.h,sha256=oAXK0Goeaaqt0HZZHPs7aXYV7NzqpY6yfGW9p1umMmw,1903 +torch/include/ATen/ops/_foreach_maximum_cuda_dispatch.h,sha256=RxKOWS1pl8YpZ34yZsK8_P_qffiTKHL7AFPzIVvscNg,1224 +torch/include/ATen/ops/_foreach_maximum_native.h,sha256=K-_yw6eIfm9m1sXedBwX-JUbel1QEWQk2y5lGTTMh74,2206 +torch/include/ATen/ops/_foreach_maximum_ops.h,sha256=ByiHz5MvCngk1n37geHErvtYqCjjf005UVXiLdwBUWs,6463 +torch/include/ATen/ops/_foreach_minimum.h,sha256=z-g1hecZtLpbpBMh5uTitcSKvMuw99sAk8bjCyXML8I,3606 +torch/include/ATen/ops/_foreach_minimum_compositeexplicitautograd_dispatch.h,sha256=YraMfO1UgDm1ErhAS7_B18IDu4i6Sb-FnW0dsOZznq8,1903 +torch/include/ATen/ops/_foreach_minimum_cuda_dispatch.h,sha256=qP4oz43SebofPXOZP2DXigz8hqBkRmaMquXE8hTMuKY,1224 +torch/include/ATen/ops/_foreach_minimum_native.h,sha256=-TfSa6Vg7Wh72G5y_UkWrOdoon0fV-5MSa5-OdLVEVo,2206 +torch/include/ATen/ops/_foreach_minimum_ops.h,sha256=uRXcZgijbF64CCVvxTxF1RrnEGPV1NdxtJN4QJ_bdRQ,6463 +torch/include/ATen/ops/_foreach_mul.h,sha256=P6CO9-P0TKAjuq8oydhJa6fjq0v5CHcNS1EQN7seGio,4416 +torch/include/ATen/ops/_foreach_mul_compositeexplicitautograd_dispatch.h,sha256=Z5Hks3c-mF_5vpzd__5Zgkle3FpRiW3YDiw6Gkh_1i0,2230 +torch/include/ATen/ops/_foreach_mul_cuda_dispatch.h,sha256=Di0b-BfdiK3lI1QxWtykED3EGhm8AX6EnBFCi8EK1X8,1374 +torch/include/ATen/ops/_foreach_mul_native.h,sha256=02Qz2A4smMLE-MlRlH9GOG2fFp5AzSJQ8XJQO1U6rwE,2677 +torch/include/ATen/ops/_foreach_mul_ops.h,sha256=wo13S3Ro7kf3fyulEWJUH3UAKtoWdFk18Osmz5Q7Xoc,8296 +torch/include/ATen/ops/_foreach_neg.h,sha256=EiFaopfMhapV8uugcYY3LAMyPTa9I4ingZpr-JHj0pQ,1222 +torch/include/ATen/ops/_foreach_neg_compositeexplicitautograd_dispatch.h,sha256=QQyLlTWTee0STX2VyfAM2cVL-E_Zq5KB1q3tBV9g2fM,981 +torch/include/ATen/ops/_foreach_neg_cuda_dispatch.h,sha256=LNirobRyRHGw6PbIqMf_mKBUAUSZJSjAGDRmRVBBRS4,790 +torch/include/ATen/ops/_foreach_neg_native.h,sha256=h7ZTHdp722yfGHrVtzdiGs623_6GyjhBx8-_FMbuDm4,784 +torch/include/ATen/ops/_foreach_neg_ops.h,sha256=OWjn0LtjV5-jjTNoGBAXXUAzBZvAZBEOex6Yr_SReho,2079 +torch/include/ATen/ops/_foreach_norm.h,sha256=VhtAed3u0NsvaDshlAdT4nTZOWwO22yixHYZbRSftNA,1499 +torch/include/ATen/ops/_foreach_norm_compositeexplicitautograd_dispatch.h,sha256=XwsSos2Euc_OkuAl0R7hvxykxM9DSB_DMBRrgOnWQkU,1156 +torch/include/ATen/ops/_foreach_norm_cuda_dispatch.h,sha256=Xa-SgLdRECv_A7TlhHo9V0OqTNXntCSoug2Ru1cWfac,820 +torch/include/ATen/ops/_foreach_norm_native.h,sha256=g5rxdxIY3zWBZKcwgncrk4OC8VTg_L97i0DHhcOnfpo,893 +torch/include/ATen/ops/_foreach_norm_ops.h,sha256=9CBDPvFpES9YUEBeN7pnb59reiMH8EM0vXUt1KEWND0,2067 +torch/include/ATen/ops/_foreach_pow.h,sha256=Kwg18J5W-zdXYXPg4zSeXkY5hi43HS4QWGzW_daMTKA,3786 +torch/include/ATen/ops/_foreach_pow_compositeexplicitautograd_dispatch.h,sha256=q5yzQAdlg72QUxq3oqlMBbZ2LoJFFuutNYy1UmjrfI4,1979 +torch/include/ATen/ops/_foreach_pow_cuda_dispatch.h,sha256=UtCRbPpjeMg-XjAVX4cTxmCUF6fZagEnaa1GkiTl53E,1312 +torch/include/ATen/ops/_foreach_pow_native.h,sha256=7C-pzRruCPP_WIt1wkXUEIHCsaQ9KM9GchhYw80E0rs,2398 +torch/include/ATen/ops/_foreach_pow_ops.h,sha256=LRI79GT88SO7SVRneUQMVHmZj1SpBsiXopV5e8q3bj0,7106 +torch/include/ATen/ops/_foreach_reciprocal.h,sha256=ngHalehmv5NfGL_npC8625KZCegC3E9Y6xzBY6SoVwU,1313 +torch/include/ATen/ops/_foreach_reciprocal_compositeexplicitautograd_dispatch.h,sha256=lTv6U45DvIakDoKoA06Cz5SJtZYTdVDDwl2wGLZox-k,1009 +torch/include/ATen/ops/_foreach_reciprocal_cuda_dispatch.h,sha256=wdyYLOJ0AOcxTxYEmb0AYmHZK-dwx6jc0sAibvlp43s,804 +torch/include/ATen/ops/_foreach_reciprocal_native.h,sha256=c8vKeQGQKnnOtDMohSqq988i2r4JwrewdDgs25aZHZw,819 +torch/include/ATen/ops/_foreach_reciprocal_ops.h,sha256=_jx5Pzs4fJomPiGe5ZEVVHXYCCAQw39vtSZ9tAcHnC0,2142 +torch/include/ATen/ops/_foreach_round.h,sha256=czU_LHs09YMRA4MS-W2i0KUw_xYZHuKex2CmgzdujIA,1248 +torch/include/ATen/ops/_foreach_round_compositeexplicitautograd_dispatch.h,sha256=Crxy4v3MV9TK8zzmrbj1I35WBfhdOnIuzFbDOCaeLgQ,989 +torch/include/ATen/ops/_foreach_round_cuda_dispatch.h,sha256=fCNyh3eTaIc43aXrhMYwL4VUFRVgcHvtpc9vY84gQ5s,794 +torch/include/ATen/ops/_foreach_round_native.h,sha256=x1QRmXU8gY5J3W4P1L6ik68iLBdRDWkoXvkRxyBjgkA,794 +torch/include/ATen/ops/_foreach_round_ops.h,sha256=l7GNFrcakHIdNTnhqvFxd1eUP3YXOxzYskMHrNEjem8,2097 +torch/include/ATen/ops/_foreach_rsqrt.h,sha256=hknxLdESeHIChXN1qMSo1-ZtcvZE0tiqnpv5zm5X28w,1248 +torch/include/ATen/ops/_foreach_rsqrt_compositeexplicitautograd_dispatch.h,sha256=NMlPV2kP2wL4-TIkSQWCtU59CKS69PbMxPpJl8bmFWw,989 +torch/include/ATen/ops/_foreach_rsqrt_cuda_dispatch.h,sha256=Mwh7QlRhIkl0hZVXoCjJqH30Pdi9IyZHcwRw8iyrvdY,794 +torch/include/ATen/ops/_foreach_rsqrt_native.h,sha256=Zp0nCHTr8uu75XlnxENi7YmEJJ59PJNoH83YMqnL2A8,794 +torch/include/ATen/ops/_foreach_rsqrt_ops.h,sha256=CdOemHktKhaL78E9D05vLG8ZFQotW1iCdXhcCJpZp50,2097 +torch/include/ATen/ops/_foreach_sigmoid.h,sha256=Gw_UzYz46fqXnAXeYbFzbxwHz4u8H6AL7RqZn-lpY1U,1274 +torch/include/ATen/ops/_foreach_sigmoid_compositeexplicitautograd_dispatch.h,sha256=mNUI6c7ywnb7tIGVVuHrsufafPdcTPInMf31PvcYZLw,997 +torch/include/ATen/ops/_foreach_sigmoid_cuda_dispatch.h,sha256=dEwMO5XnsNuy8JLKQ4luueKkJS451T3oCTRZUp_NWp8,798 +torch/include/ATen/ops/_foreach_sigmoid_native.h,sha256=86vYuIiEJeEv_zoUcAfYfzHC3skn34zguMK-KANNac8,804 +torch/include/ATen/ops/_foreach_sigmoid_ops.h,sha256=12RYxFsuxc61DKI6r1js-g-qyl_rt1mmMzRhGP05xQg,2115 +torch/include/ATen/ops/_foreach_sign.h,sha256=_8p2arvQ1zELvdD5-1uC7B7SyuD6EuoH3wc3tADtJ20,1235 +torch/include/ATen/ops/_foreach_sign_compositeexplicitautograd_dispatch.h,sha256=119CdU2pz6mATsCllZFTU-mYIL-d5uGq2281Z0PlymA,985 +torch/include/ATen/ops/_foreach_sign_cuda_dispatch.h,sha256=i7B1Z15lVhpkr2dtNit6VYZ2ltDw9aFZc0UbjBVSlMY,792 +torch/include/ATen/ops/_foreach_sign_native.h,sha256=Gns4vFgAgvIvhdwoVnGCzxqf2-i4uIb282DxZ6ibKJc,789 +torch/include/ATen/ops/_foreach_sign_ops.h,sha256=ZKxCz_m36DMsaVLW6X_dUj0090E6C-E49ucgLlM-4qc,2088 +torch/include/ATen/ops/_foreach_sin.h,sha256=q2AQB4kTDUzfxT6700AwO9SgHDwiNwKd1tK5EytuK8k,1222 +torch/include/ATen/ops/_foreach_sin_compositeexplicitautograd_dispatch.h,sha256=E7vAYn6mRWYeIupZtBKVzV3vMimhmRh4O018rKlhGmc,981 +torch/include/ATen/ops/_foreach_sin_cuda_dispatch.h,sha256=GkuQzGOsrNLYUe1Gq9_OKJjzayi1k_wOFxJGIIjAiW8,790 +torch/include/ATen/ops/_foreach_sin_native.h,sha256=V7ng0ItxEnx5pM4bfI39EprsqGZ5JCczq3x3KVWOa5k,784 +torch/include/ATen/ops/_foreach_sin_ops.h,sha256=n03LtHHqhVJT0RDRbfNmVB8N7qYXd21j4e7cjBxz4Yk,2079 +torch/include/ATen/ops/_foreach_sinh.h,sha256=QuThDx-U40E1K_iJo6-UTT51nVmmK4zLjwFI-oUkEYQ,1235 +torch/include/ATen/ops/_foreach_sinh_compositeexplicitautograd_dispatch.h,sha256=WuW_pHcH3snQxpiRdRNSO_DlLyV6FzuESyw3UwTVK7s,985 +torch/include/ATen/ops/_foreach_sinh_cuda_dispatch.h,sha256=Ds_xzRCY9t6plr2vnX_dWl__guO_L4z_gWq_Lpj6mR0,792 +torch/include/ATen/ops/_foreach_sinh_native.h,sha256=s9gFyrE2YKT42RzrgYpW-mXIJ8PvY2bqOoOv58wUVkg,789 +torch/include/ATen/ops/_foreach_sinh_ops.h,sha256=9A9qFysThlj5VCytgBEdLiJWV_XWDfSbBvRKg7yZ5tw,2088 +torch/include/ATen/ops/_foreach_sqrt.h,sha256=2yh5kwnBL30hzvIawoO6b554PziqWW4nSzZWmSi7Nho,1235 +torch/include/ATen/ops/_foreach_sqrt_compositeexplicitautograd_dispatch.h,sha256=dx79QBg5vjXu1_bm-UY3QgqFpJg6Bjcb6QKQAkmDpVk,985 +torch/include/ATen/ops/_foreach_sqrt_cuda_dispatch.h,sha256=R2I313_FMJORMUQMYqSkhx40Z6FT8OQdnUaBvM9w25g,792 +torch/include/ATen/ops/_foreach_sqrt_native.h,sha256=6hsSu779GLp2mBwjQVnVys9mUOzAUNQuYuwKa78D0RY,789 +torch/include/ATen/ops/_foreach_sqrt_ops.h,sha256=yeN3DoQFY4XvsiNbplTU6IiksHiLeMSE4PLAgzvT9Us,2088 +torch/include/ATen/ops/_foreach_sub.h,sha256=8ie-8tNqlWNqVpDr0H_NRG9esvc1ivP0PgLw35ZOqL8,3666 +torch/include/ATen/ops/_foreach_sub_compositeexplicitautograd_dispatch.h,sha256=PgLOsDEh2gIdUTxp6h0LE9bIpY1GAg2255Rps4dNczs,1965 +torch/include/ATen/ops/_foreach_sub_cuda_dispatch.h,sha256=clpGRsPk-bTft2ZLnilk2HuRI_1tsgszrVyToCH8rO0,1256 +torch/include/ATen/ops/_foreach_sub_native.h,sha256=P3M9bkfHbrQrjoMDwWgDBB0ZPs6F2Sf3HcoNpUz_mpg,2260 +torch/include/ATen/ops/_foreach_sub_ops.h,sha256=tu2EVnenxUm4fZ_hDXV4MqrGleoBtJ6swVCmWawgzHI,6625 +torch/include/ATen/ops/_foreach_tan.h,sha256=I3NJFs3e0ABQx_FmhVQtuV0FZwPPt66KuQ7aE7oC2zI,1222 +torch/include/ATen/ops/_foreach_tan_compositeexplicitautograd_dispatch.h,sha256=tWiT5GxFobzjgmN343rM2JtFwxsHBmwwjDogvzFeT50,981 +torch/include/ATen/ops/_foreach_tan_cuda_dispatch.h,sha256=r9_M7nVUJmzWLJHGPPYVbvB5p5Im-7WeMMeYhmZwZiY,790 +torch/include/ATen/ops/_foreach_tan_native.h,sha256=y4KmTas2BalIaBaROCe55VYjreO6p50uvMgiecKoLLg,784 +torch/include/ATen/ops/_foreach_tan_ops.h,sha256=vbBgiY21UeGFwIKsaVchsQl4Fan8JdjCjfcxcvyDiwY,2079 +torch/include/ATen/ops/_foreach_tanh.h,sha256=hhFBcEvf_5Q3e3Am0rD_PTk4PMtzmsd3I7-wmbCoivU,1235 +torch/include/ATen/ops/_foreach_tanh_compositeexplicitautograd_dispatch.h,sha256=eZiXEC9ShTFgCyo_irQy0LwKuaq6-xP1rA-gqwGKPXc,985 +torch/include/ATen/ops/_foreach_tanh_cuda_dispatch.h,sha256=hT4YelKEv44WsMi9mDhVUJXtAO0oW3ClYnhyHh1yWi4,792 +torch/include/ATen/ops/_foreach_tanh_native.h,sha256=BFAfPoTXDV14zq9Fn8rG25fVcWnUfN7fnZNzmNhe6tY,789 +torch/include/ATen/ops/_foreach_tanh_ops.h,sha256=vFTTsoPLMTzrZBuGD-OBDYRVCjhq9a_u3Lm4R3bEZR0,2088 +torch/include/ATen/ops/_foreach_trunc.h,sha256=X4Xwh4aZhKZFZk8kAF3yN0XEnH5IHdOtYWrsZeneOjo,1248 +torch/include/ATen/ops/_foreach_trunc_compositeexplicitautograd_dispatch.h,sha256=A-8iOv7LlH4hPqcl_Jup-Ca5BUg3XBUrHjKR03ST3mA,989 +torch/include/ATen/ops/_foreach_trunc_cuda_dispatch.h,sha256=9J3JZTWwXdi82irP1-EXfOK7OL_dzzEwzeFO7LUxxW4,794 +torch/include/ATen/ops/_foreach_trunc_native.h,sha256=6mu5bRnw-ICYVkoV6mMqlUNOA8BtdQtVFWZWdSoGdZs,794 +torch/include/ATen/ops/_foreach_trunc_ops.h,sha256=WALljbRTeeWxd6jrm3X2RcvrlHh_dG1aNbq6KFmeIn0,2097 +torch/include/ATen/ops/_foreach_zero.h,sha256=sAUy-wLWhQoWlcgEHBPc8N-lyg6IDMKfvKxj4WykWm0,1244 +torch/include/ATen/ops/_foreach_zero_compositeexplicitautograd_dispatch.h,sha256=QR7bf5PCuyffJIBDZH5d6dZksq-9o-EWDXJcrVxrQ14,985 +torch/include/ATen/ops/_foreach_zero_cuda_dispatch.h,sha256=xiDyMl4EnnJkOHF8FSTS6_4XN4yiehDDCZomxVXr268,720 +torch/include/ATen/ops/_foreach_zero_native.h,sha256=VPVrlfDLBlP440MVGOVjqNNQBBO8Mp5LlX5NpVjKvkY,695 +torch/include/ATen/ops/_foreach_zero_ops.h,sha256=8yFbfUq65evOCEQjXsfcluXyGgUOB5ZlpdV_o_u5MN8,2097 +torch/include/ATen/ops/_functional_assert_async.h,sha256=Qlnfngfx9JiloS4P-XNU4GiAsoexSWexMBnao71L2V0,833 +torch/include/ATen/ops/_functional_assert_async_cpu_dispatch.h,sha256=ot74CjLgr5VdbrT_dqIt5KVRcc5VP-Tqt4HGMsAXT1A,797 +torch/include/ATen/ops/_functional_assert_async_native.h,sha256=wm-KRQf6TC3uG1PV-gc28iGSDDwMEziFd5Wq6ep1t-A,561 +torch/include/ATen/ops/_functional_assert_async_ops.h,sha256=-B01jK3lmhgY4AMn0De5qyAdYOphlDSVAV1iQYin4-w,1212 +torch/include/ATen/ops/_functional_assert_scalar.h,sha256=3Kt7KwvWeHXcGEVEbEv3u5hj7qqN9S1L8jIR4dV3xSc,829 +torch/include/ATen/ops/_functional_assert_scalar_compositeexplicitautograd_dispatch.h,sha256=5rmSQMPsUHjMEHryg_oZ6miOh7UGhYL-QuF18bLLbKg,842 +torch/include/ATen/ops/_functional_assert_scalar_native.h,sha256=_tAPPVT9uX2sz-7Br-G_m4o7Kq5DLT49uI_jDR8ZIY8,554 +torch/include/ATen/ops/_functional_assert_scalar_ops.h,sha256=QXM77gtDWpJPCbDW_QhcAVny_eWcVLEbtE4hGk2CRKI,1204 +torch/include/ATen/ops/_functional_sym_constrain_range.h,sha256=fOfpNax_PYICPy1f5gV5rvEHpzPM-_9q5HFxjCQQTvA,886 +torch/include/ATen/ops/_functional_sym_constrain_range_compositeexplicitautograd_dispatch.h,sha256=eBXY9h8oUARa1CFRzZg6nJKCy4JxBtl61AcSLAWlBXI,879 +torch/include/ATen/ops/_functional_sym_constrain_range_for_size.h,sha256=cfaswW7FPM5ZdFyg7qhKRc5rM3dc3wMmwQ4bsdZ1Tcs,922 +torch/include/ATen/ops/_functional_sym_constrain_range_for_size_compositeexplicitautograd_dispatch.h,sha256=I3v0kG9E4hzE5aTLkyT5qBjfI_n0Kt82RFNaXgYnZjE,888 +torch/include/ATen/ops/_functional_sym_constrain_range_for_size_native.h,sha256=Rw2nSNUUx2ja7vGyqBH2JGnvawwudwF8WJbI8XhVnXg,600 +torch/include/ATen/ops/_functional_sym_constrain_range_for_size_ops.h,sha256=1molH3Qx0yjCCFEX4XkIhbLTnk7IiRqJbJ2136b1pts,1349 +torch/include/ATen/ops/_functional_sym_constrain_range_native.h,sha256=8ld3iEJe5ErBsxJJo9i3-TnG_vMGlsWGmFN2D8cRifI,591 +torch/include/ATen/ops/_functional_sym_constrain_range_ops.h,sha256=ItA8aPYF3CyLurDF89MQyIr2K5pkg0Qbo-CmMMfoK9A,1322 +torch/include/ATen/ops/_fused_adagrad.h,sha256=0wvEJOt5IN2uUzBCX-FOMsqbMPFR1Ll62l1F75QuNMw,6730 +torch/include/ATen/ops/_fused_adagrad_compositeexplicitautograd_dispatch.h,sha256=rs0-OuDLyx2i2fjLWza74uMnijT5-a1TGZJMNi3lkp4,2868 +torch/include/ATen/ops/_fused_adagrad_cpu_dispatch.h,sha256=0t5-0GN_XKp8u9VOqWmcNz7GBYkRJn8wEqfZZYcoiog,1292 +torch/include/ATen/ops/_fused_adagrad_native.h,sha256=8LYAUgc_ARYaq5RQAX85L3K9IFcIeGGBJCmoxrxdArQ,2564 +torch/include/ATen/ops/_fused_adagrad_ops.h,sha256=IaDOUkOBOQXuYHGECnQGm46NWW9OeBfPqTlS99Pza50,9616 +torch/include/ATen/ops/_fused_adam.h,sha256=rMLgvTT4r66d8uWokqBHPbqT6U-hMH3JmJtfo5m7kxU,8403 +torch/include/ATen/ops/_fused_adam_compositeexplicitautograd_dispatch.h,sha256=cKQIHEQjbq4XtdmdjElr3EQQOg7NiYgsgjP-pnlfzls,3426 +torch/include/ATen/ops/_fused_adam_cpu_dispatch.h,sha256=Od8eilHinkM8AIK7HoNwueUQkMR-XTTG7OxDcGPKgTE,1452 +torch/include/ATen/ops/_fused_adam_cuda_dispatch.h,sha256=M4zOd0u4dEYL_7BJoHd_ajFjVDn9o3y2kMD0B7hyNaA,1454 +torch/include/ATen/ops/_fused_adam_native.h,sha256=6p5e1osCNGXBHS-BrUGbp8qUMug0DWSr6U6owb-RD14,3932 +torch/include/ATen/ops/_fused_adam_ops.h,sha256=bA7Ie1CvlhEivbl2ADeuzmF4zRhZtKkxOWGtq7kOzuQ,11594 +torch/include/ATen/ops/_fused_adamw.h,sha256=GIlCfXvzFTrLd7PpeaRYoM5nA0_EV6_tjkARW5cjk2E,8428 +torch/include/ATen/ops/_fused_adamw_compositeexplicitautograd_dispatch.h,sha256=_TFA_X_bbIjlT-SbEnm9PToJghik33a8veF0x9e-XbM,3432 +torch/include/ATen/ops/_fused_adamw_cpu_dispatch.h,sha256=_43P1GmRG1bJi66L6azwwf3I0P6lLWCYQJsGHB-8l3Q,1454 +torch/include/ATen/ops/_fused_adamw_cuda_dispatch.h,sha256=cU1bOkbD_YEjXmJhhGRGHrkVWzxL4GSqqGxpqyVA-qU,1456 +torch/include/ATen/ops/_fused_adamw_native.h,sha256=yBtamoVKc7mhjKdb_uQLbCQBGH8FIuJGR2r8mQTrtJs,3940 +torch/include/ATen/ops/_fused_adamw_ops.h,sha256=HP5UbKR9Xn4wl3qgAgf-ZY1TjLPQCMdVSiHIZgEZ_AI,11612 +torch/include/ATen/ops/_fused_dropout.h,sha256=LZ5kik5SU9ME-iY8KVUBSEr9E_USZgoS66yVoXRy-m4,1650 +torch/include/ATen/ops/_fused_dropout_compositeexplicitautograd_dispatch.h,sha256=hO25KgQqkM_fJBkf-OkDzD9xAXCzkopdW7PnRBJFGi4,1096 +torch/include/ATen/ops/_fused_dropout_cuda_dispatch.h,sha256=uZMetVwx5KS6GCuFRjI-TnvkxEnzRQl_u7-X_2J6cyw,822 +torch/include/ATen/ops/_fused_dropout_native.h,sha256=ujMmqz9FlgerXV-TeD7q9Mwjud_DRnNULUO7fiDneW8,765 +torch/include/ATen/ops/_fused_dropout_ops.h,sha256=NYioQ83Z6Z0kQw2ggaW34xGmLmgTonFIZCVQBSl3wAc,2207 +torch/include/ATen/ops/_fused_moving_avg_obs_fq_helper.h,sha256=Yq7qhRzG1titIAflJ8ovDX2TsnMjK0o__hb8RL8dLik,4775 +torch/include/ATen/ops/_fused_moving_avg_obs_fq_helper_compositeexplicitautograd_dispatch.h,sha256=tBd5nwqRPK_YcyNF1-nW3MwbDw00wS3ggMbTyPAWjH0,2085 +torch/include/ATen/ops/_fused_moving_avg_obs_fq_helper_cpu_dispatch.h,sha256=72juH36Kwx_GmhJFveNm9GFakNipCNNOVaNfk1vKu7g,1071 +torch/include/ATen/ops/_fused_moving_avg_obs_fq_helper_cuda_dispatch.h,sha256=3I6zOASUf_AUFl_XBQpQC6syfdXhM6213Dfq2-akork,1073 +torch/include/ATen/ops/_fused_moving_avg_obs_fq_helper_native.h,sha256=JDeeyZgJzcvqJ2bjk-0-Q5uoGHrUHyVLzfpDalT7SV0,2164 +torch/include/ATen/ops/_fused_moving_avg_obs_fq_helper_ops.h,sha256=UJ4Mn_4AePMgkpyDuSj-88hcd-FfssSXs5pQiE-H7lI,5874 +torch/include/ATen/ops/_fused_rms_norm.h,sha256=5Wg1VwFmWHuo7GV-FxW70uQ58KHijewaZ0mPcZ038cU,835 +torch/include/ATen/ops/_fused_rms_norm_native.h,sha256=DW69q74kL-hRSBtQODQ_1TtZD8Pa55kFQGY44qsx-do,422 +torch/include/ATen/ops/_fused_rms_norm_ops.h,sha256=3yUyg9hxUXXo7H_eXwG9gB7ItRIJ8XEUUhB8glKOJ2M,1217 +torch/include/ATen/ops/_fused_sdp_choice.h,sha256=-wvY9jZ8qmOLnn_t0oEM-_CZek59dQcWUJKJMZwO7_Q,1093 +torch/include/ATen/ops/_fused_sdp_choice_cpu_dispatch.h,sha256=YyQ8YI2iqYOOZPBFiUeMqmHtpRC_5Z6lGUkVtjiFfac,942 +torch/include/ATen/ops/_fused_sdp_choice_cuda_dispatch.h,sha256=m4dlOmbdFe9Xn3reF85XIJSGTJb9xrzArIULRkHwaC8,944 +torch/include/ATen/ops/_fused_sdp_choice_meta_dispatch.h,sha256=kT7mIPHoU5l1Lj75eFz1fepH0oC9P7GKOW-8ogs88P8,944 +torch/include/ATen/ops/_fused_sdp_choice_native.h,sha256=v4PZ6YO3tVN46Sbi-JLAK9RH7QELJwHiytHhmwWA2ro,1264 +torch/include/ATen/ops/_fused_sdp_choice_ops.h,sha256=L4c_EmANLeja-qPkjUnPR0_6R_hSk-igGdleVAKc9Fo,1599 +torch/include/ATen/ops/_fused_sgd.h,sha256=tW5ZUXYM36F3tQ3J6183uWtVNBaS35j3RHIfWlqPfGM,7226 +torch/include/ATen/ops/_fused_sgd_compositeexplicitautograd_dispatch.h,sha256=ApVD3eFvvzxRpANGN4wv_Np38rwjf-h7bpjqGNLEfWc,2956 +torch/include/ATen/ops/_fused_sgd_cpu_dispatch.h,sha256=9yX7U5SkvQFw9IVijWLyFvwBnLsygMxo4-JN0XBO974,1330 +torch/include/ATen/ops/_fused_sgd_cuda_dispatch.h,sha256=qagIsaJBbcxoS4pPxmBqDI3Z6JCTVasiVo0RKWT0xmg,1332 +torch/include/ATen/ops/_fused_sgd_native.h,sha256=CW9gytsMNug0Q2YSBdf0QcdQx1t5bpGdZWXHP5eN_ck,3340 +torch/include/ATen/ops/_fused_sgd_ops.h,sha256=IaeZwc9_a-gx1hykFSs-40txckIUkq9LVIS-8CTGMZE,9878 +torch/include/ATen/ops/_fw_primal.h,sha256=mVpmlFPwtmCaYDl2nSsc4PNozQB_IwIQ9ATbWhBk4uo,507 +torch/include/ATen/ops/_fw_primal_compositeexplicitautograd_dispatch.h,sha256=NXQhFZcOOWc2XBL77CMAxQ8vOtxfeysFHmPttB2p8ig,783 +torch/include/ATen/ops/_fw_primal_copy.h,sha256=Xn2Ybp5-kXmR0ZHKJJoFmAMKh6wCCVUv932sqgb8vtQ,1216 +torch/include/ATen/ops/_fw_primal_copy_compositeexplicitautograd_dispatch.h,sha256=lATcwWFhcyj-_zQS-09IQxQMTYUIxYv55Oyblg55m0k,915 +torch/include/ATen/ops/_fw_primal_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=E1jl5KdNilfGNuQgYiezT0ISLkd1RcPptxbL_vgFcqM,814 +torch/include/ATen/ops/_fw_primal_copy_native.h,sha256=qVU4Fh0TwplAOF2RIhM5QbXNSxF2_lekfI3fik4IrAc,602 +torch/include/ATen/ops/_fw_primal_copy_ops.h,sha256=sY_fDkwXL5T3AzaKL_KeV27CQrStC9Hf0uzaWDVif4E,1701 +torch/include/ATen/ops/_fw_primal_native.h,sha256=DtLuqgevpxd3ONa1QZjkUbxZK7T9O5nbLfpqw1igUFI,495 +torch/include/ATen/ops/_fw_primal_ops.h,sha256=7AN7EU-Lyxf4hw5Ua2g9qUTv-gqqFScaa4xHAkFHaVM,1025 +torch/include/ATen/ops/_gather_sparse_backward.h,sha256=KfQKr2d9uU9k9RSoVUs8nF6BVURjtJQPEg_tF2gGwxo,823 +torch/include/ATen/ops/_gather_sparse_backward_compositeimplicitautograd_dispatch.h,sha256=t54YLE3sVhaFbLKLPm5y2uR8ed1-eSvjClTF58tsjGQ,845 +torch/include/ATen/ops/_gather_sparse_backward_native.h,sha256=yEOlmxa9V4QU8Vz6bXSCjUTdBRWexO9kNxb3AbAQ3Wc,557 +torch/include/ATen/ops/_gather_sparse_backward_ops.h,sha256=Nw2muu0IArA_5SAHYWmsxW9tbuwDsaHvqh809fBO8fQ,1221 +torch/include/ATen/ops/_grid_sampler_2d_cpu_fallback.h,sha256=3io-qabvo-8_chb-Hy-boNoZ6sZVXCv3FkJXLfjpDvw,1941 +torch/include/ATen/ops/_grid_sampler_2d_cpu_fallback_backward.h,sha256=bc6P1kf1M81EO0fZL9YmozePZQtsRR4Wo9g7yikcG3g,1093 +torch/include/ATen/ops/_grid_sampler_2d_cpu_fallback_backward_compositeimplicitautograd_dispatch.h,sha256=0S7kWiPEKRmi0iS8JQ3K5TChzSpZNhAm1mjyXQgaUT8,949 +torch/include/ATen/ops/_grid_sampler_2d_cpu_fallback_backward_native.h,sha256=c_fVMBPVr0_td2ydjDm9Yw0AoEmiqIdDci1xKy46pVs,661 +torch/include/ATen/ops/_grid_sampler_2d_cpu_fallback_backward_ops.h,sha256=rTI4E6UI-yYgnJKwR5rP-A4KkZe9gYSR3ll2u8Ktlek,1554 +torch/include/ATen/ops/_grid_sampler_2d_cpu_fallback_compositeexplicitautograd_dispatch.h,sha256=RNJ5ldvNJNBMZ6RcgTfpPIoIjCoBVAF3X9TPiz7JjpY,1278 +torch/include/ATen/ops/_grid_sampler_2d_cpu_fallback_native.h,sha256=K4nzk5b-wc_Ng__aB32pPFWRbhAXisDaDdPY69XJ_Hc,792 +torch/include/ATen/ops/_grid_sampler_2d_cpu_fallback_ops.h,sha256=niHgS35S6G7WmpPyxTF8X-fMfKY-nb1xAWAjEVYFNhU,2309 +torch/include/ATen/ops/_grouped_mm.h,sha256=x0oL0yzMxnfYfI3pmAyCYn454yxFxf62_ygO4MNITPc,938 +torch/include/ATen/ops/_grouped_mm_cuda_dispatch.h,sha256=2DGhMhFKzfsAfY05sjFLxhmJCPvWeYq8HcvTZ5sIAS0,900 +torch/include/ATen/ops/_grouped_mm_native.h,sha256=qTFY96cflzaLfJeVAFMEd1TyibmJ8-8ER9dGOmbMhus,659 +torch/include/ATen/ops/_grouped_mm_ops.h,sha256=wMlStWInZUH0hMN8ofRvDd6qJe6j3BAtqqdX8OqAI4g,1482 +torch/include/ATen/ops/_has_compatible_shallow_copy_type.h,sha256=d3Nk5y6ovWA_X3oYPprOaBTWeEtt6HHUWwG5pYCShuY,781 +torch/include/ATen/ops/_has_compatible_shallow_copy_type_compositeimplicitautograd_dispatch.h,sha256=2xdv2qflbXPUttIRc55S8s2MotOCl0vLyftGJ5zbOWg,810 +torch/include/ATen/ops/_has_compatible_shallow_copy_type_native.h,sha256=qeZT5hSichhkbW4OjM0AeMO7ghJLdJ55XGTEskUQYAo,522 +torch/include/ATen/ops/_has_compatible_shallow_copy_type_ops.h,sha256=MFRmwdOXKTk9NzxvaT8wY31Xqn6O3kz84Vve8Aq6Gbg,1101 +torch/include/ATen/ops/_has_same_storage_numel.h,sha256=xoTRvVwRt0uvXja8_iAFMHiXvYVltsIv6OqSIvvu4Ik,744 +torch/include/ATen/ops/_has_same_storage_numel_compositeexplicitautograd_dispatch.h,sha256=2KsHY-jtYgiEkqUzRXGTD-UuQAasT2xh3VJnh-Y5UmM,801 +torch/include/ATen/ops/_has_same_storage_numel_native.h,sha256=X-d35dI8TUEGyWm6DGhVWbv5fK6tKXqaMEk4818EdYM,513 +torch/include/ATen/ops/_has_same_storage_numel_ops.h,sha256=bbVI-STLyvTV3OPExCP4bYGftSM6PNG5pIk240B1D-4,1074 +torch/include/ATen/ops/_histogramdd_bin_edges.h,sha256=EJ4vuPF7NQDoLcZF7wptqheRhimhpQybv7Kl3GF6SLg,1918 +torch/include/ATen/ops/_histogramdd_bin_edges_compositeexplicitautograd_dispatch.h,sha256=MViELSWfzE5zkB70kIQEbMBJaPM9JHnQAbH-jaPzoy8,1161 +torch/include/ATen/ops/_histogramdd_bin_edges_cpu_dispatch.h,sha256=cN5bcMk9tbLCU_6TzWimrAJgmjBUZLjRmeqK0wVOcPc,900 +torch/include/ATen/ops/_histogramdd_bin_edges_native.h,sha256=JeYM2ppUxo9SILl4gr1y1yeSf3l4HYdBHXc-Ks95rqk,868 +torch/include/ATen/ops/_histogramdd_bin_edges_ops.h,sha256=keb1AWybO10F7RG5LmbW7qe7G6mlbTWLOrawPlm-cDw,2515 +torch/include/ATen/ops/_histogramdd_from_bin_cts.h,sha256=jxpI8cF7-fbOPCGXwT2kBSqFmf7krl_bz3zGftH3KcQ,1955 +torch/include/ATen/ops/_histogramdd_from_bin_cts_compositeexplicitautograd_dispatch.h,sha256=Vf56yUvS1uW3OjVRMXOYQpE7NuODGPyrZEMn1bAwXWw,1179 +torch/include/ATen/ops/_histogramdd_from_bin_cts_cpu_dispatch.h,sha256=SCBr01-DMRInbQy4CgFeyQ1_icS6mdsHYh9qMMIIjd0,888 +torch/include/ATen/ops/_histogramdd_from_bin_cts_native.h,sha256=aAtOSR857t_qnR4oVLlVOhvRkRbHBmSfRpSv03I6eEU,853 +torch/include/ATen/ops/_histogramdd_from_bin_cts_ops.h,sha256=kRz6ekUBfYcUTK8x6q82c-XabDu_w5DPLKgWI1sbld4,2510 +torch/include/ATen/ops/_histogramdd_from_bin_tensors.h,sha256=UyaFMXR6NRDU1Zh6cqWbuO--cBpeE3Qp0k5IMk5musw,1752 +torch/include/ATen/ops/_histogramdd_from_bin_tensors_compositeexplicitautograd_dispatch.h,sha256=TsJoFuX3gG1uZMr4G14-bMw8cae-IbeJTKdKcWcMZTY,1080 +torch/include/ATen/ops/_histogramdd_from_bin_tensors_cpu_dispatch.h,sha256=AVDmfaUS2CV5uEQucfz3kb6j2slZOrrdnbss4hHVmbY,831 +torch/include/ATen/ops/_histogramdd_from_bin_tensors_native.h,sha256=Kpd6ztUmgZNuaDtpL5dawJI97tYK0j-rzBDQwYMoSDY,750 +torch/include/ATen/ops/_histogramdd_from_bin_tensors_ops.h,sha256=jeJhQ5las2zDz5VIukhpoIumgSCB1J7RCepI8nzQAhE,2234 +torch/include/ATen/ops/_index_put_impl.h,sha256=sk1Z7Q700xXLSJ4brhKiqw4KrmvW-5QDImhYRSi4Mwk,2226 +torch/include/ATen/ops/_index_put_impl_compositeexplicitautograd_dispatch.h,sha256=KawxZ8MiPYCVXQLyo3UtBpC4lc3Lvzc9w6cymtSC1ks,1311 +torch/include/ATen/ops/_index_put_impl_cpu_dispatch.h,sha256=OcMMvPq-f7cAPOqNbr4w3o18awq7e0rHL-NUaGmdo5s,851 +torch/include/ATen/ops/_index_put_impl_cuda_dispatch.h,sha256=FV23bzfXa7YoUf7lpsj1gOcojbXKnuzIRwb0OQFOVlQ,853 +torch/include/ATen/ops/_index_put_impl_meta_dispatch.h,sha256=LNybZRBzxbDvblRuI2lBS-JqjAFfgbJK4O1UB84-c0w,853 +torch/include/ATen/ops/_index_put_impl_native.h,sha256=yv9ZtUwKdIOCKSP8gwIhHs8x-ANGx2pJ0VV9Vy2X_2U,1394 +torch/include/ATen/ops/_index_put_impl_ops.h,sha256=tQs-WKzGzeD1f8jEV5ZWtjopCBQjYfvZEM6_YU82sd0,3273 +torch/include/ATen/ops/_indices.h,sha256=56Adr5kJkjKm3zhSVkwamrMgxifcIQ7Noas819RMFdI,505 +torch/include/ATen/ops/_indices_copy.h,sha256=0JELGuG0es3Ml_nsDxy9_XE9-i0mGzFyT4JeUggtVN8,1097 +torch/include/ATen/ops/_indices_copy_compositeexplicitautograd_dispatch.h,sha256=jyTTcUBtctnqGmqzumiA_We9NEUuSbtjr-GlS5olODc,881 +torch/include/ATen/ops/_indices_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=7As9eYtRYosbx2LHlxoNmrZkYIbgrc6iUpUEutNA7Zs,797 +torch/include/ATen/ops/_indices_copy_native.h,sha256=svS206yL_9pAAkUE5n2z0lNSBbgLAPbENDKhgq-PEDY,568 +torch/include/ATen/ops/_indices_copy_ops.h,sha256=7Z4S8XMhopKkoswfIupilgcvMhaaITXlpfvQUQlsUs4,1589 +torch/include/ATen/ops/_indices_native.h,sha256=cFMuqOTdC3B5gFWOO0Z0PxhQcNPANBTkWVXidieG2SM,485 +torch/include/ATen/ops/_indices_ops.h,sha256=yYa-uMlOUM8uOZAjFKYMQPGQXgYJmYrvJrTQ_F2mMhw,969 +torch/include/ATen/ops/_int_mm.h,sha256=N0cZQdu7tZHx1ODm4A6W42Y4eADXKL37z10zt8zOtBQ,1169 +torch/include/ATen/ops/_int_mm_cpu_dispatch.h,sha256=1DFPv8kodBPzreDBqXj-udPUZR_sRYtUYKvY4sEVWj8,955 +torch/include/ATen/ops/_int_mm_cuda_dispatch.h,sha256=iJjZqqVijsGowuCmY8zrbuKbZO8fMLJ0jxK7cX_PHvQ,957 +torch/include/ATen/ops/_int_mm_native.h,sha256=yJTcKddfP9rmk8zCypH-yrtobE_G56RlEewyfnVINxw,808 +torch/include/ATen/ops/_int_mm_ops.h,sha256=Ex6gdDkp2Nr4y__F0LIttLh5pQpWTeu8mLlNTb4zTAA,1719 +torch/include/ATen/ops/_is_all_true.h,sha256=QeBYfNea4Q67YJNKqR1UJPdHhIHPABhTN5v-0HS_lXc,661 +torch/include/ATen/ops/_is_all_true_compositeexplicitautograd_dispatch.h,sha256=cF3R17YPvgyrHCreU1inqg62KpMC3PB48KGkRSHNpEY,770 +torch/include/ATen/ops/_is_all_true_native.h,sha256=pCE1xodo0siKP15x05pHtbZ9kN1aNHGSnrqPBFsnYy0,482 +torch/include/ATen/ops/_is_all_true_ops.h,sha256=9RJrQYFEPwJJ9tywCpT34u60PsSqUu9e_3C6_7rf2w0,975 +torch/include/ATen/ops/_is_any_true.h,sha256=0_jucq_X-pUuV0XgU0vTFFpNddgBqz0n5zPNaGUVyas,661 +torch/include/ATen/ops/_is_any_true_compositeexplicitautograd_dispatch.h,sha256=Ffuv62XoptwkDY-X2MWzJrLWrjrm0Dm1EHbA8QwS5Nk,770 +torch/include/ATen/ops/_is_any_true_native.h,sha256=G_KKsHE6Gsdmxb2J8SAsvB2xzElW2FTxu01ZPq4BFYE,482 +torch/include/ATen/ops/_is_any_true_ops.h,sha256=abBmFG5qNqgXzL0T_-V0_VpuvPRqvMB-_jz16lCWxX4,975 +torch/include/ATen/ops/_is_zerotensor.h,sha256=Sy4D-Wf7cyDF9NjOhZurSHm03XOgYqcUWeOy7ZMSeWo,672 +torch/include/ATen/ops/_is_zerotensor_compositeimplicitautograd_dispatch.h,sha256=FfbyZJnR2iPS_nbV5pJ0Tqc-uliqX5__GFKpmC6gAp8,766 +torch/include/ATen/ops/_is_zerotensor_native.h,sha256=w-Va65GneYs2toXH2JZCkk9RENaA4zX8I63OLmSiIYY,478 +torch/include/ATen/ops/_is_zerotensor_ops.h,sha256=6giA1lUPGKf-oU2iGkpq4PS8p9YKuT5YohCO1jYlGW0,961 +torch/include/ATen/ops/_jagged_to_padded_dense_forward.h,sha256=aA5iIVdJw5sC0T0r8EN2LoPU8ov4ASIB8pzJl1lzXeY,2120 +torch/include/ATen/ops/_jagged_to_padded_dense_forward_cpu_dispatch.h,sha256=tDeeWs0bmQ9DKCTtNOp0J7n-vyjtcN3jTU8Dj0EusD4,997 +torch/include/ATen/ops/_jagged_to_padded_dense_forward_cuda_dispatch.h,sha256=jUCHS6vLjowX54vHddVuIYEvjSrzyoyp2DLXUzVVzNA,999 +torch/include/ATen/ops/_jagged_to_padded_dense_forward_native.h,sha256=Hf3w0KEm6GIvbf0WEUppj0pZnz9uDLl8rm2Ec0fbdqw,753 +torch/include/ATen/ops/_jagged_to_padded_dense_forward_ops.h,sha256=7Q8Uh9oxtCbBFawvPXLXqxTSYT7rGIN4orM2nFcY0EE,1306 +torch/include/ATen/ops/_lazy_clone.h,sha256=-c5mhMvdcquQkPbnISb7uD7SYJD3QE4HJ9vqaBPDK0o,657 +torch/include/ATen/ops/_lazy_clone_compositeexplicitautograd_dispatch.h,sha256=7k9eJxEeAoIJMjetZpYwCy3NJo9UVrZZP83XvPB6Xds,769 +torch/include/ATen/ops/_lazy_clone_native.h,sha256=vkVYm8Ua9d5I1X8b3hp-D3WMMNLRqILPA98ncLFio70,481 +torch/include/ATen/ops/_lazy_clone_ops.h,sha256=7cy8eiThB-2sBZdIfyBV1oD-8TRXLPXh1xBGI0spfkg,972 +torch/include/ATen/ops/_linalg_check_errors.h,sha256=X1pJ0XJr1s-GTBC5hVhR42k4NO62OqbhaoricbCaBKQ,780 +torch/include/ATen/ops/_linalg_check_errors_compositeexplicitautograd_dispatch.h,sha256=Ila6TAxjI4yORIeMU0AaOdS4D1NKKLwkF1KqSlFgFYA,815 +torch/include/ATen/ops/_linalg_check_errors_native.h,sha256=WUrcAL8bKM8WSCUtKeOY9U1YMBknY3E5VYGikvR6G-k,527 +torch/include/ATen/ops/_linalg_check_errors_ops.h,sha256=Gp67vGcWlckjtfBFoo_H73nd_5cW5BQ6q5gW1tIeyiI,1120 +torch/include/ATen/ops/_linalg_det.h,sha256=lsQIkVQPkTa_nK_c_zKtp753lGhhQS-uOmyulrEtmkg,1485 +torch/include/ATen/ops/_linalg_det_compositeexplicitautogradnonfunctional_dispatch.h,sha256=WVjrpZSPqf_pbAlfMdCe-RqVM3pKJCEATFhL8igoqGo,828 +torch/include/ATen/ops/_linalg_det_cpu_dispatch.h,sha256=z_9T-waezr7f5_RXrOIJTWJ4xe3Xkhz8n8zOtZbZINU,1081 +torch/include/ATen/ops/_linalg_det_cuda_dispatch.h,sha256=mwAEh8wiAGNxowNzc6rugiNefKqymhmQgg09VbNvLMc,1083 +torch/include/ATen/ops/_linalg_det_meta.h,sha256=fLCP_LT-v3dpN53c7kfX6B8bi4CYGQAHK568Ck6GXR4,569 +torch/include/ATen/ops/_linalg_det_meta_dispatch.h,sha256=hihaWL2tAJfTjkOVw6Xu0gr7G1islScG7ba_sjKwJzQ,1083 +torch/include/ATen/ops/_linalg_det_native.h,sha256=trSmGzxXacohaeGD7OwQdTbVEAEjaxxmThbL39f0pJI,661 +torch/include/ATen/ops/_linalg_det_ops.h,sha256=Lk8jMUKemD_Y_1jFou5_ZHDrZDUNmxxmogjDBGykkkI,2021 +torch/include/ATen/ops/_linalg_eigh.h,sha256=91L64DQ46CtghcQ11OlitOUw-Iwieuui1xSIdeplqj0,1755 +torch/include/ATen/ops/_linalg_eigh_compositeexplicitautogradnonfunctional_dispatch.h,sha256=xjQc_Z7i5elpgIfxBJ4x_t2amOT7NB30PzRVvHn0S2o,866 +torch/include/ATen/ops/_linalg_eigh_cpu_dispatch.h,sha256=tY2qcoGuhgjrbN3haqFpMswV8v2KP6Vu-xWKOY0XykM,1170 +torch/include/ATen/ops/_linalg_eigh_cuda_dispatch.h,sha256=w9wi_MmlNh7OsHv87NeMqP_RpljQZHOR52LKS_Ebdk4,1172 +torch/include/ATen/ops/_linalg_eigh_meta.h,sha256=3YF4eA-0hjRWUZUk9jHd8YRg1LxR3N2E7qis9uFPzl0,609 +torch/include/ATen/ops/_linalg_eigh_meta_dispatch.h,sha256=SM0z3Y2bzH0ZK2zJ5PGsMd2eZGdFhBB6uTZc7TLGUnc,1172 +torch/include/ATen/ops/_linalg_eigh_native.h,sha256=Er_7xxu3cAzltkO3VSuovffAPpJbXOq2xbNDUzQk8Pw,691 +torch/include/ATen/ops/_linalg_eigh_ops.h,sha256=CiOlwFQoHlFK7jhYYld_XXwNRI-V5yKhf2aNN206Hmg,2214 +torch/include/ATen/ops/_linalg_eigvals.h,sha256=5mblwQglm9AgFawaZbTjMtA6UOBPEbfN-tWAajg4MTU,673 +torch/include/ATen/ops/_linalg_eigvals_cpu_dispatch.h,sha256=ahs5ImtGxpkCvrYzc7-Bkrcx6PevKHDj-ss2CbAbUYo,729 +torch/include/ATen/ops/_linalg_eigvals_cuda_dispatch.h,sha256=hxal6OnofLlMhm0HqtAAB1UZXKR33-k-YUuEEuheC6s,731 +torch/include/ATen/ops/_linalg_eigvals_native.h,sha256=sC9NFOgXZBz_TkX5kE75Q5OgsNVZwREDGPLy1OS6XLY,485 +torch/include/ATen/ops/_linalg_eigvals_ops.h,sha256=2kKkaBaBzCBwAra-u8svYv6X9w62KL6sDo-qaoc1ZgI,984 +torch/include/ATen/ops/_linalg_slogdet.h,sha256=1xUDW7Qmja-qBQExNfOTGgGWZ_HGkORNVNQVxQLI1I8,1712 +torch/include/ATen/ops/_linalg_slogdet_compositeexplicitautogradnonfunctional_dispatch.h,sha256=wcgAZj-HWA5gHoQ68MuZuTKVe5q7dp9hwCh49I4bIZ0,843 +torch/include/ATen/ops/_linalg_slogdet_cpu_dispatch.h,sha256=4N9OTDJhZ7h3C9z-yFcMzRp7EIjzFLS4Ts3ZSYPBoUk,1174 +torch/include/ATen/ops/_linalg_slogdet_cuda_dispatch.h,sha256=Vziuw0p8-GrayyOhYJuO32cm1K9kw9mAI_oYEsglTYc,1176 +torch/include/ATen/ops/_linalg_slogdet_meta.h,sha256=MuDg25S3g6ykawe4LdJgGmNjI8n50nmWFgFkJgONEo0,573 +torch/include/ATen/ops/_linalg_slogdet_meta_dispatch.h,sha256=FmgeiTaxUjXwftjSjyeE3c5K4OqVnSfBP76JSWevIlU,1176 +torch/include/ATen/ops/_linalg_slogdet_native.h,sha256=0Z4wLsBSDA0HhMvd6mJTtg7qXr_wfZTSWHemvVBwXKs,701 +torch/include/ATen/ops/_linalg_slogdet_ops.h,sha256=R-qETvuqbYRjrJcQiLLgvrhZk2PnqK4lTN9Rn2f8Kfg,2225 +torch/include/ATen/ops/_linalg_solve_ex.h,sha256=Vh2yIn6suSEURyTSFqPjVZuY4tR1diDnN6tOs99XS0k,2106 +torch/include/ATen/ops/_linalg_solve_ex_compositeexplicitautogradnonfunctional_dispatch.h,sha256=7aMKBqEyDg-ULzCyrUjFgil2tZkBabLavlFLluKS14Q,907 +torch/include/ATen/ops/_linalg_solve_ex_cpu_dispatch.h,sha256=H9U1RBF8PYQVjIfX9keD7WTTN2j35wNkaDmHUw7LY5k,1349 +torch/include/ATen/ops/_linalg_solve_ex_cuda_dispatch.h,sha256=TXUZXz65MCtetPkYashJ1pPS_P83PIKLrFa02jlCQQo,1351 +torch/include/ATen/ops/_linalg_solve_ex_meta.h,sha256=EcC0zVq4l2cr0zZQ4dylK-oWT7UPCYiRnkpZx6XoWIo,626 +torch/include/ATen/ops/_linalg_solve_ex_meta_dispatch.h,sha256=oMY9eXNYuKwf9XDPLLl4Zfc1_TfIy9EARkNvlF_VNeI,1351 +torch/include/ATen/ops/_linalg_solve_ex_native.h,sha256=y9_Vd0UZuMmmpbp8w9wrwOPwaBQT0rlQ-GBYQFwohnM,753 +torch/include/ATen/ops/_linalg_solve_ex_ops.h,sha256=Pz7eTNgKMjxxW_5P4LH-ozBzUOkf1vdNj9H229XaR4k,2599 +torch/include/ATen/ops/_linalg_svd.h,sha256=S5WFk0FuaAk_nKMtaZcUPdZIdHl3_9oCnrCxsXWeGys,1970 +torch/include/ATen/ops/_linalg_svd_compositeexplicitautogradnonfunctional_dispatch.h,sha256=GqkL19ZfCIB24LkTsaiVpkqGHV4hIF8PTQ8WdPL5UZ4,933 +torch/include/ATen/ops/_linalg_svd_cpu_dispatch.h,sha256=BLY-Rwfy3IDcgLnpO7i5eahkOZPzeTN6GOu_skm1Zns,1350 +torch/include/ATen/ops/_linalg_svd_cuda_dispatch.h,sha256=yyp-aVI3qo5xuJ0UyBeynZ-gN6udrEkZBcdcnasdKOY,1352 +torch/include/ATen/ops/_linalg_svd_meta.h,sha256=xzDsirtIu1qG_Frx7Sss4pqtI3dWdsIStNKnVgpTkMU,648 +torch/include/ATen/ops/_linalg_svd_meta_dispatch.h,sha256=_TYQcZgExT-kwEHBcnaZb7JdFQNbh7ZST0go_6PQjKI,1352 +torch/include/ATen/ops/_linalg_svd_native.h,sha256=hRms8jgyYg4nmgGDAIQcpp0DdIPbiaUwn3bunM3hpS8,730 +torch/include/ATen/ops/_linalg_svd_ops.h,sha256=IkyZzDlDyczDrcT-DHbR0ebiW4wLDClewIRuD4ULPIQ,2501 +torch/include/ATen/ops/_local_scalar_dense.h,sha256=_DKKF5xOtB8KRcwyRF8iC2RjuhDnNVEGvCWJ1TOzhAg,689 +torch/include/ATen/ops/_local_scalar_dense_cpu_dispatch.h,sha256=c9MkDN0UKXdbmFUiSp4GZVB-Nyc3vhdcnyqdzMXMNQQ,733 +torch/include/ATen/ops/_local_scalar_dense_cuda_dispatch.h,sha256=cp6tgJTSD8E2i4UzEABPpSDGdkvPRsg1oZcx9vm4Jg4,735 +torch/include/ATen/ops/_local_scalar_dense_native.h,sha256=4FCWKzkgw5tx48ImymC7foeYsezL1GTiEeWKuo1u52U,565 +torch/include/ATen/ops/_local_scalar_dense_ops.h,sha256=MlATKXbS1r5LCNU1ZyIILtWggYYvuIqJf4RaLbd67Wo,996 +torch/include/ATen/ops/_log_softmax.h,sha256=ljMhD4a4BMPCgcnc-Q8JflDFTpyXidtV6mVh4Rt4sRM,1333 +torch/include/ATen/ops/_log_softmax_backward_data.h,sha256=KwEsxO_zQMAm4h0ShBsHE0UAZzbc7xouRwhiIwiBxwY,1716 +torch/include/ATen/ops/_log_softmax_backward_data_compositeexplicitautogradnonfunctional_dispatch.h,sha256=AtFFuaP4HOc_1rtpvkM723kkaU5B1yfAqu2EOj0PkmU,885 +torch/include/ATen/ops/_log_softmax_backward_data_cpu_dispatch.h,sha256=mYIa9lFri9OvQmGnq99-HTj4im7kUI5dhJPXYwpE1_k,1162 +torch/include/ATen/ops/_log_softmax_backward_data_cuda_dispatch.h,sha256=GjAibzrSWBfnl8lSiuQL2H4R-yCcy6h0DXT4Nh5enoo,1164 +torch/include/ATen/ops/_log_softmax_backward_data_meta.h,sha256=TLm6BBEGNkKerG-gTICvKAGfBp3xY3BvVaznCsO_Mms,662 +torch/include/ATen/ops/_log_softmax_backward_data_meta_dispatch.h,sha256=sLpNSJ2Ybc0X-jH_quFwLcLultwvHom2FK5shNd-UKY,1164 +torch/include/ATen/ops/_log_softmax_backward_data_native.h,sha256=B_nfjRuYbPAsKMokvIcngRMTdRrfzp-0Px0zz6RskLs,984 +torch/include/ATen/ops/_log_softmax_backward_data_ops.h,sha256=cbee87rHYZ5hY3viKHP0xmzeyMg_coMZnHQlXKqVVyY,2167 +torch/include/ATen/ops/_log_softmax_compositeexplicitautogradnonfunctional_dispatch.h,sha256=LxaA31JJPm_ixF6Tw79UyGIIPG9h_hIdErE2weJUVa8,829 +torch/include/ATen/ops/_log_softmax_cpu_dispatch.h,sha256=O-n0FakeBSZc3Y8saOWFIt8KD6Kojkuazkpi4Jud_YA,994 +torch/include/ATen/ops/_log_softmax_cuda_dispatch.h,sha256=UnBbvEEosdPCdx7aLI7F7OCUchy5zJcj4QY8drotYfU,996 +torch/include/ATen/ops/_log_softmax_meta.h,sha256=QIi-g6xsJiE_kzzte9JTahoHQqUzLjPoc67qQyqiPy4,606 +torch/include/ATen/ops/_log_softmax_meta_dispatch.h,sha256=c2gpZI3nk0ehsIuV7lXzzmjFWoy11iLxdBNpOyuhmg0,996 +torch/include/ATen/ops/_log_softmax_native.h,sha256=SVqdThvXNjvN3CCWYug7V3U8wY3uDWQrNk1AcfW8J_c,840 +torch/include/ATen/ops/_log_softmax_ops.h,sha256=HBuES0thvyXNnuXmzhLCPsL0Bc9PyXKuqQd3y4tfr00,1803 +torch/include/ATen/ops/_logcumsumexp.h,sha256=oK8RPNwE5R1Hqw89KlDp9RRjy-DzxMgJuYcD0HtNyW0,1178 +torch/include/ATen/ops/_logcumsumexp_cpu_dispatch.h,sha256=3_RT4yQXkssBg2jSX8My8v-w7W1nw8u5IlKnlRTZr4E,937 +torch/include/ATen/ops/_logcumsumexp_cuda_dispatch.h,sha256=-hPUr5qAWMLY_DdiE3GgQc9KoLXe_Y9C6zErSjkd76w,939 +torch/include/ATen/ops/_logcumsumexp_native.h,sha256=csf5JZ7x5dB81ON_ztE4jbFwG8ThL4jcrKv_hO1I_m0,784 +torch/include/ATen/ops/_logcumsumexp_ops.h,sha256=JQk-grwIZ_5cVkqf-ZUtxVW3mD5XclvE7aZNyfbow-c,1677 +torch/include/ATen/ops/_lstm_mps.h,sha256=_QfixC-GrXhGeNy-p_6QsdIHWKNQLx2XrIbIrgjb5-o,2944 +torch/include/ATen/ops/_lstm_mps_compositeexplicitautograd_dispatch.h,sha256=d3rxtRj5Uu19DNLP0ydQF_sE_4WeHWgt8uDi5NXDDRc,1515 +torch/include/ATen/ops/_lstm_mps_native.h,sha256=sOhFGyqPs92xTgeLhWAnvMwrl3wIX_ryalDGjdpnDsM,824 +torch/include/ATen/ops/_lstm_mps_ops.h,sha256=eE_sO58BbM07TQdw8haYh8IHsD_GW1OasErSEn-gAxU,3449 +torch/include/ATen/ops/_lu_with_info.h,sha256=L7n8fMxtdmsuU_M0s44dGPXlBFE40wE0SvWvbTLPF0w,837 +torch/include/ATen/ops/_lu_with_info_compositeimplicitautograd_dispatch.h,sha256=sRAzB0rszPzNHEMEv9NdUSVIxSYv4bElHoVGQBYcUlI,848 +torch/include/ATen/ops/_lu_with_info_native.h,sha256=2XnxswVzpWW3oqF3llF6NmHa1yLZwQHSprK8fkg2Tz8,560 +torch/include/ATen/ops/_lu_with_info_ops.h,sha256=MDnAD6ltNwCPtwgDraPEZVBW8nuF5HOGRIzvzOuV32Y,1234 +torch/include/ATen/ops/_make_dep_token.h,sha256=FpcnNuz1HLlGV7UIO5MuqCn6WaWVjACk25kXOAT7WBw,1543 +torch/include/ATen/ops/_make_dep_token_cpu_dispatch.h,sha256=4fjYSUAm3zTKcdmEQ5IfVfQfk1uvBgsNh3BDlm2ns4s,1030 +torch/include/ATen/ops/_make_dep_token_native.h,sha256=dwpXgDzIbHP6BCgY3PO_KYHIx8jtmxlTAqXuI_MaybA,685 +torch/include/ATen/ops/_make_dep_token_ops.h,sha256=JXYFKgiocil6cRdfbmq3LdB0yZIWeopcvNej9G4qV9g,1564 +torch/include/ATen/ops/_make_dual.h,sha256=NIejaxYlIUW6WegSmRwxqIKp5UU4X92U1ch68qdcZUc,751 +torch/include/ATen/ops/_make_dual_compositeexplicitautograd_dispatch.h,sha256=dUzwG4r_3yFWRtDVJyJQlWdx6P5HvTvz9O85j_rL_U4,813 +torch/include/ATen/ops/_make_dual_copy.h,sha256=RHJnz_vloWu9P2NwbASl2L59heEnjgE5HOCnNMBA6qU,1393 +torch/include/ATen/ops/_make_dual_copy_compositeexplicitautograd_dispatch.h,sha256=s2qGBFT6ci_9R47QLovAHM8R6eX2yhXn9cu5_TJxc_4,975 +torch/include/ATen/ops/_make_dual_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=IeETUThSE0D2ScOlD4cf8fmBAMM0y0FoRRNaJYbOIeE,844 +torch/include/ATen/ops/_make_dual_copy_native.h,sha256=JZT2yeahHZfQHlF51UTaY3CqATTqGcB8VTRSIMfDurQ,662 +torch/include/ATen/ops/_make_dual_copy_ops.h,sha256=qOzxMgDmBokfiCugIHVZUD5EOQCTftnogG1M9OkX8aQ,1897 +torch/include/ATen/ops/_make_dual_native.h,sha256=Dd8tTIW-W9HEha47mH0X-rCojxuoYtvoyf49bQsA_uA,525 +torch/include/ATen/ops/_make_dual_ops.h,sha256=71KBu4Vg60Zufv75tIUGKdWtMI9TTKV5V2hj3Qzwvrw,1123 +torch/include/ATen/ops/_make_per_channel_quantized_tensor.h,sha256=D765VvG_apdp5yzL4zhkz30BR549qFTZKKvdDkRg3T0,1724 +torch/include/ATen/ops/_make_per_channel_quantized_tensor_compositeexplicitautograd_dispatch.h,sha256=FEGlc5nvemr85T-CaFu_DMbac_DhQT8iQ5g0Hy9ZAAo,1065 +torch/include/ATen/ops/_make_per_channel_quantized_tensor_cpu_dispatch.h,sha256=9ZBT-YbWqIEI0zydjeycTU-QoWWizgcOeFy81JXDIi8,819 +torch/include/ATen/ops/_make_per_channel_quantized_tensor_cuda_dispatch.h,sha256=H2NGxzzqFBHSFIQWI3aRzZFgN6Gs_0UPLrw5ubKD1FQ,821 +torch/include/ATen/ops/_make_per_channel_quantized_tensor_native.h,sha256=uV_fR0aVyH3jEVy8T8HTxU9iWHEiyTqO6VYdxyNESwY,912 +torch/include/ATen/ops/_make_per_channel_quantized_tensor_ops.h,sha256=hrQTNCDTng0hUU8C_yKrKB6zzXkohmyIGI27Dui26-Q,2183 +torch/include/ATen/ops/_make_per_tensor_quantized_tensor.h,sha256=lcsvEuygrbotuHEazVC2y-YftpSc_Pptg3Zb6jsTcs0,1543 +torch/include/ATen/ops/_make_per_tensor_quantized_tensor_compositeexplicitautograd_dispatch.h,sha256=9Gnh-sSsBW4Snm-r8gLBaIgG04QjaUUq5pmIr1MiIU0,989 +torch/include/ATen/ops/_make_per_tensor_quantized_tensor_cpu_dispatch.h,sha256=r3dyXRudFPxxjUy-DpIMp4YQtG8-UgpIQqmc00_l-VE,781 +torch/include/ATen/ops/_make_per_tensor_quantized_tensor_cuda_dispatch.h,sha256=rMFRFnBRUWdJ4cLq6caOYmQPsqAnfTFJ6XMaZh_Bsn8,783 +torch/include/ATen/ops/_make_per_tensor_quantized_tensor_native.h,sha256=0oiDMCdXOLbbk1aKzyPQfUVjJYW7y7ZW3VA0hPW0NUU,798 +torch/include/ATen/ops/_make_per_tensor_quantized_tensor_ops.h,sha256=V-z1nH7rGLqS2eblNpYJKc4a_Ry9KPa1byYWBGC38fs,1937 +torch/include/ATen/ops/_masked_scale.h,sha256=Djh0LyrcCiT0cpwC6AYpqFL5R4SnC5dK41youTwD5lM,1331 +torch/include/ATen/ops/_masked_scale_compositeexplicitautograd_dispatch.h,sha256=KEQnL0W77ie_hlZngDA2bjPtc_yfaoph4qvijwVrpQI,959 +torch/include/ATen/ops/_masked_scale_cuda_dispatch.h,sha256=WrK_ulgDWj0KicJyIPpMojZw3kJfFZlZ_aQF7VRvzio,768 +torch/include/ATen/ops/_masked_scale_native.h,sha256=cgAry6Yu7NRt480lYyd4LuFle29Onitys3dP9Z1ZrhI,650 +torch/include/ATen/ops/_masked_scale_ops.h,sha256=X2WTnxiVCVIvTM4PLu7aUZ9fuS6jpJUMePTpeT7tji4,1853 +torch/include/ATen/ops/_masked_softmax.h,sha256=EeS7-_7pfzTWV7j98QrcOKQ59qR-zMUZOOpVmLtI4ms,1663 +torch/include/ATen/ops/_masked_softmax_backward.h,sha256=R72-5DuutVRHfVfuYF_lgsNkDyeRwxic14CviVtGvxg,1732 +torch/include/ATen/ops/_masked_softmax_backward_compositeexplicitautograd_dispatch.h,sha256=fp4rU73pHLWELNVE2XoMoWkB9IflyhIrhVHGJx6YeAQ,1096 +torch/include/ATen/ops/_masked_softmax_backward_cpu_dispatch.h,sha256=7QGnaTcBkTAWkRGrLIxzR3nsv7O7UWbncKn4nc1M1Kc,842 +torch/include/ATen/ops/_masked_softmax_backward_cuda_dispatch.h,sha256=4jBc3mHQ2zVHvLQt-q_LgFG8K_iQg4OzNbPQy9_rMeQ,844 +torch/include/ATen/ops/_masked_softmax_backward_native.h,sha256=26nfj_g1HPVKQPMM_GGC6XDNEcIqRIikLGgeZE3AqOs,966 +torch/include/ATen/ops/_masked_softmax_backward_ops.h,sha256=BZhlm9PHUorl5SOwMiMU_cJhoPeDC6CPBSa0kuwy8Ts,2243 +torch/include/ATen/ops/_masked_softmax_compositeexplicitautograd_dispatch.h,sha256=MQs-a93Z6f2GJxoye2etJ9bFzoEmGpFfTNBhy3aqtTA,1097 +torch/include/ATen/ops/_masked_softmax_cpu_dispatch.h,sha256=lGtmsMJFmgXz5yxEbUDhquW17fdNWXNR1YLSTgi7DM4,850 +torch/include/ATen/ops/_masked_softmax_cuda_dispatch.h,sha256=J7lBFsU6c-qwoYRHEpIdeIiNo_3Yut52Hz4qCJOjJko,852 +torch/include/ATen/ops/_masked_softmax_native.h,sha256=FHsFJpoof2kbN6m47j3XXYaSCmCqSz58Z3yPW7tWbn0,975 +torch/include/ATen/ops/_masked_softmax_ops.h,sha256=q7aRXLhSUgumTFfPQyYXvoBqhEAIJzd-aDPJpWshYAk,2207 +torch/include/ATen/ops/_mixed_dtypes_linear.h,sha256=dEcsOXUc7HXtsWh_X1wsWqgOkBX5YSHrq2Yd5QqHnzk,961 +torch/include/ATen/ops/_mixed_dtypes_linear_cuda_dispatch.h,sha256=bKD7TDLP4hJSz8-MFTlS1W6SNECR40uDWWdS6Ghz5TI,896 +torch/include/ATen/ops/_mixed_dtypes_linear_native.h,sha256=MQqxi2Zd7us7L1Wlm5_JQmrblrkGCDNGyn8ScNR13ww,650 +torch/include/ATen/ops/_mixed_dtypes_linear_ops.h,sha256=B0BN5CGzcnIVMOjthHH9lYD0LPBGPx9i7T13eydt5Lw,1469 +torch/include/ATen/ops/_mkldnn_reshape.h,sha256=ptpukSuC_2x6ZPXM1A3-O7zi4i8n_XbKfB0xfwImxgk,1246 +torch/include/ATen/ops/_mkldnn_reshape_compositeexplicitautograd_dispatch.h,sha256=dQX0j3rjKvnDzOCC1NPH1K3CgBjUKOUpaN0ntQtYbt8,931 +torch/include/ATen/ops/_mkldnn_reshape_native.h,sha256=2EGYX_D3ffSFh7xGxlqbBmiwww6gs0vhMcbhCmEfP9M,617 +torch/include/ATen/ops/_mkldnn_reshape_ops.h,sha256=cvnnl5AKXNFGx5w1hZ-AToV3rAZ_AxuG6P4BoN19VPE,1753 +torch/include/ATen/ops/_mkldnn_transpose.h,sha256=2kPXIVMJTNRLkUiRe77XIEpAblYrv-_2RkfxhL3eNaY,1552 +torch/include/ATen/ops/_mkldnn_transpose_compositeexplicitautograd_dispatch.h,sha256=VRU7fTRIckiRxHBpXF9VJAR_cicO-IggHcJ7AJF-8KY,945 +torch/include/ATen/ops/_mkldnn_transpose_meta_dispatch.h,sha256=5RLin9bnxhXbMZs2kIHb9u6EPyLgKDXE58wvn67EPO4,758 +torch/include/ATen/ops/_mkldnn_transpose_native.h,sha256=3UR58elPwJ__PSa2kDhNN_r5PJQX7Q3XCBYwxkb40Yc,720 +torch/include/ATen/ops/_mkldnn_transpose_ops.h,sha256=eKUtDXuLP8ogaiXuuDxavpe9CxrBjf3p6yxcNeAvEI8,2427 +torch/include/ATen/ops/_mps_convolution.h,sha256=kqFf8jtTJzNX1S5ONq0aZTtQt6hKGYUlqasS37iQiOc,6855 +torch/include/ATen/ops/_mps_convolution_compositeexplicitautograd_dispatch.h,sha256=tEwqXGHWWXBg7bPsV3NaiHyHZFvzccbgrs0syRhkNX4,1750 +torch/include/ATen/ops/_mps_convolution_native.h,sha256=shFfFFxz6HvO47BxkKfVjUSwr1qtOU5ZpTI9SaA2Xl0,693 +torch/include/ATen/ops/_mps_convolution_ops.h,sha256=2HrRT4zk1mKe9enZGeSc73jnRvQ_xl_8xVxi3BJpLbE,2773 +torch/include/ATen/ops/_mps_convolution_transpose.h,sha256=os8I2whaXch5xTyopg4WgkypnvTcNRM-5iUGfESsb8A,7411 +torch/include/ATen/ops/_mps_convolution_transpose_compositeexplicitautograd_dispatch.h,sha256=rVxcIdde4asxWUVBKPFStVeFH2z51EpsGPIQdBEkLkk,1758 +torch/include/ATen/ops/_mps_convolution_transpose_native.h,sha256=SaDs-HbzJ-Q9vMEuaFPRrcc1dHFNoJigT7-fsODzbFw,697 +torch/include/ATen/ops/_mps_convolution_transpose_ops.h,sha256=O3V6vvza45nYZC0Ar0hxCN3Igc8M-h9Dv-lanq6Lnvk,2799 +torch/include/ATen/ops/_native_batch_norm_legit.h,sha256=LJ0CmQWD3acYZebuOIQBHUCkp_47tqGrN14Ghl3HvnE,5375 +torch/include/ATen/ops/_native_batch_norm_legit_compositeexplicitautograd_dispatch.h,sha256=J4S2L2GJQ2VZ-XAY0OLRyJ60DF-lOOO5oPpjQkaj48g,1047 +torch/include/ATen/ops/_native_batch_norm_legit_cpu_dispatch.h,sha256=VxIN5roxz3Ley9a2Jk--CX1pOXy6toXjG7e9OmKDOE4,2573 +torch/include/ATen/ops/_native_batch_norm_legit_cuda_dispatch.h,sha256=cec2Zww5TfD9ZoYwnNkVtUousJ4M7fWWlOlMAGB5soU,2575 +torch/include/ATen/ops/_native_batch_norm_legit_native.h,sha256=miNZv5Ocp-HvDgIih1ccTrigrtaIxjPCyxXuJP0JVfQ,3751 +torch/include/ATen/ops/_native_batch_norm_legit_no_training.h,sha256=-Y17oO8nZn6Zl7rORzRlIoifS9W2WuIJYg-Ph5xrkek,2698 +torch/include/ATen/ops/_native_batch_norm_legit_no_training_compositeexplicitautograd_dispatch.h,sha256=UgH-0dhOsZzmQyiBFg8Ko6oRml8RzEhB_amxyT3y_Aw,1748 +torch/include/ATen/ops/_native_batch_norm_legit_no_training_native.h,sha256=VUB6xfYVxCtf0WBB53mlRIHlvWl_bQT6jH2q3mrLBGo,1084 +torch/include/ATen/ops/_native_batch_norm_legit_no_training_ops.h,sha256=3YEUuSdD-5p3enFXi5qNPeV5pkvh3T5kMxj0_58e64w,3322 +torch/include/ATen/ops/_native_batch_norm_legit_ops.h,sha256=OIQvflBVfHVPsGsArWN46hVXZZLUR9J76Jd5eN3Yz2k,7358 +torch/include/ATen/ops/_native_multi_head_attention.h,sha256=vE5eF_CmU2REn-Pm4WbZ6gFXtFJtGidfTVWIFeHkUzE,3589 +torch/include/ATen/ops/_native_multi_head_attention_compositeexplicitautograd_dispatch.h,sha256=U-qeFursUEuYdGq45ZdSaihK30OPZDPQ5p2b9rZgXJg,1701 +torch/include/ATen/ops/_native_multi_head_attention_cpu_dispatch.h,sha256=OF74FFWbaq8Da22VGGrMAl98mtHEiSzU3fmCbP2O12s,1129 +torch/include/ATen/ops/_native_multi_head_attention_cuda_dispatch.h,sha256=O7ncaaC7XW2FahgyaeNFStryLYw7tmVQhrp7-UjCzb0,1131 +torch/include/ATen/ops/_native_multi_head_attention_native.h,sha256=EmYqriJEYUKjkpf0FhPog83BFAf2VFauBS6Hq6FQLy8,1836 +torch/include/ATen/ops/_native_multi_head_attention_ops.h,sha256=vEZ60GC5jBILRfDluOORTn0X6VToVXQnTAQ5VBdz-IA,4145 +torch/include/ATen/ops/_neg_view.h,sha256=xx33tfmyFb44vgkgW7qk2cN-fO1MAxAO4eEtBwGHtOU,655 +torch/include/ATen/ops/_neg_view_compositeexplicitautograd_dispatch.h,sha256=M1LBYfnOR-NeAdjN0g-3Z1PP2J5xy8QCwzgJnh8Twm0,767 +torch/include/ATen/ops/_neg_view_copy.h,sha256=Q1AJ_88phQllZ77yS1LFw5iOyO7QsbeEbiQgDwp_HYI,1107 +torch/include/ATen/ops/_neg_view_copy_compositeexplicitautograd_dispatch.h,sha256=ASqnPEJ4_R3Y7woBI7vW83fyQcJfOZjGn6mG0iVFnTU,883 +torch/include/ATen/ops/_neg_view_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=a95U0EWNIO2efX6AxunHMiwKTa6SHmgS38uM0pwG9l8,798 +torch/include/ATen/ops/_neg_view_copy_native.h,sha256=Ub2j0UzHLiaBMWc8xcMP_NLBZ-OPMwy4rI_4ofTtN_U,570 +torch/include/ATen/ops/_neg_view_copy_ops.h,sha256=X4uVCcaUONQVbRyKh-HNkhb4iiGL8iX-9mp1h0lVeH0,1595 +torch/include/ATen/ops/_neg_view_native.h,sha256=G4CYBJXhT33gy4p2JXxHYxWTphNV3qviPyliPdn9BBs,479 +torch/include/ATen/ops/_neg_view_ops.h,sha256=Z9-EVZfmgf-DI6xZ49Dczt880LD9TJsWVQ5Q7XySbjs,972 +torch/include/ATen/ops/_nested_compute_contiguous_strides_offsets.h,sha256=sYAOEWOxVhnGt3dbCe-SPNjT7JCHXPOFoBoLeebvy50,837 +torch/include/ATen/ops/_nested_compute_contiguous_strides_offsets_cpu_dispatch.h,sha256=7SQFUgUTT25jcg3K79ZlBrVCBh_7mEOieyywhSlTKNw,788 +torch/include/ATen/ops/_nested_compute_contiguous_strides_offsets_cuda_dispatch.h,sha256=kaSxaRr4VaTx_yfHAY_r8tWtIQpuKQUkXzmNnYP5Ds0,790 +torch/include/ATen/ops/_nested_compute_contiguous_strides_offsets_native.h,sha256=v72ooiCnYy6bEQiNXghjL1nvsGAQ5aCpgUwWtgLot4w,544 +torch/include/ATen/ops/_nested_compute_contiguous_strides_offsets_ops.h,sha256=j1u_swtEjGsteLduoMdFv9BG1Z8ghsVTjdMOFevTO8o,1171 +torch/include/ATen/ops/_nested_from_padded.h,sha256=rrRxpoGpKyjU79kZe3dX0I8H2wuhNY8M8WkTQkfMN2E,1736 +torch/include/ATen/ops/_nested_from_padded_and_nested_example.h,sha256=cTzf6PCtRhsthWRLc-EOejH9Vd-j_qRoWRdJ9IK09rg,1551 +torch/include/ATen/ops/_nested_from_padded_and_nested_example_compositeexplicitautograd_dispatch.h,sha256=-w0gJjiXz5II1sm-XSjIYU3n0VhdNOcmkXWlUrTiYfg,997 +torch/include/ATen/ops/_nested_from_padded_and_nested_example_native.h,sha256=cW31Kyf5C0tNSOuAjr-k8AF57kWfXXcieMFOUx9he5E,689 +torch/include/ATen/ops/_nested_from_padded_and_nested_example_ops.h,sha256=PPO36nWwkyLfDDrrF6W-FH3Sbocx9BMPkJso3qvcxHc,1953 +torch/include/ATen/ops/_nested_from_padded_compositeexplicitautograd_dispatch.h,sha256=iyZC19WxsyK6vUVPUNFiBQ2rDZdnJgonSzSQJ0Hxzi0,1045 +torch/include/ATen/ops/_nested_from_padded_cpu_dispatch.h,sha256=OAoLdpriJVeuwnzqwpXMrE8fLxx6HpAH8vUv782fhEw,812 +torch/include/ATen/ops/_nested_from_padded_cuda_dispatch.h,sha256=9cdVyHehT1qyqarv5tZfyzmNRpSQ95czCGTgtTv-Lac,814 +torch/include/ATen/ops/_nested_from_padded_native.h,sha256=Mh_YWH2JYtzVzGkkUhTnioNXb9ee9XiXfqd65FpngGE,889 +torch/include/ATen/ops/_nested_from_padded_ops.h,sha256=z2Yv9vy7pT1SIDWWc-amRBqgrrqDdrhBRp6gyja_lTw,2103 +torch/include/ATen/ops/_nested_from_padded_tensor.h,sha256=w8-TnbdZrY-cldC0_8F5dq-hVe2U7hh4JEOrcJPbw2o,2937 +torch/include/ATen/ops/_nested_from_padded_tensor_native.h,sha256=DW69q74kL-hRSBtQODQ_1TtZD8Pa55kFQGY44qsx-do,422 +torch/include/ATen/ops/_nested_from_padded_tensor_ops.h,sha256=KP72DG50OkvKk2Yc_HT-xOZtDj3Xe9LpDDxeNMC5CpU,1706 +torch/include/ATen/ops/_nested_get_jagged_dummy.h,sha256=JL4Zpi6IMYchxEyn4eL4XPpVjQHTNPFgbGPm2U58piM,706 +torch/include/ATen/ops/_nested_get_jagged_dummy_native.h,sha256=DW69q74kL-hRSBtQODQ_1TtZD8Pa55kFQGY44qsx-do,422 +torch/include/ATen/ops/_nested_get_jagged_dummy_ops.h,sha256=80a1Qu19nIR3sGHnBB_gFeVR1rM78g37QzvurNfb0eQ,1008 +torch/include/ATen/ops/_nested_get_lengths.h,sha256=ebpiKZjcsUTK_HIebCpwC5Ss_feCnccjcDP15m65n4s,689 +torch/include/ATen/ops/_nested_get_lengths_native.h,sha256=DW69q74kL-hRSBtQODQ_1TtZD8Pa55kFQGY44qsx-do,422 +torch/include/ATen/ops/_nested_get_lengths_ops.h,sha256=kffP0vBcQxyGXa1rTLcZqrM7iZqPdJqiwUiymxtcEJo,996 +torch/include/ATen/ops/_nested_get_max_seqlen.h,sha256=CX3-5CSw1frsRq4n09kiTOiN5_BBIPKh1GwVhry_J9A,701 +torch/include/ATen/ops/_nested_get_max_seqlen_native.h,sha256=DW69q74kL-hRSBtQODQ_1TtZD8Pa55kFQGY44qsx-do,422 +torch/include/ATen/ops/_nested_get_max_seqlen_ops.h,sha256=Z-ZIkMgRZ_OXsWF92E16rlrerDA1-hcsf_z5xPsnUq8,1005 +torch/include/ATen/ops/_nested_get_min_seqlen.h,sha256=guQRCD8Oq17IKTGU0dQw4-GWF04JVE4jYaUvH-NninY,701 +torch/include/ATen/ops/_nested_get_min_seqlen_native.h,sha256=DW69q74kL-hRSBtQODQ_1TtZD8Pa55kFQGY44qsx-do,422 +torch/include/ATen/ops/_nested_get_min_seqlen_ops.h,sha256=MNkdUNdohyiP-mMSTOxxKCukrNDcINZiTcv37BbSWnw,1005 +torch/include/ATen/ops/_nested_get_offsets.h,sha256=tEyMmr4w6AubYP8ezSdmuBnVbXFD35O4WdCKU-s-_hw,689 +torch/include/ATen/ops/_nested_get_offsets_native.h,sha256=DW69q74kL-hRSBtQODQ_1TtZD8Pa55kFQGY44qsx-do,422 +torch/include/ATen/ops/_nested_get_offsets_ops.h,sha256=JiY05IvS_ZkYU7z9XwZkh5DTlcsdN1X-2CXCB-Svq24,996 +torch/include/ATen/ops/_nested_get_ragged_idx.h,sha256=n-hm5dYd23m-avAmaMEafMRSxtVx7--TLpctdeVbm_c,695 +torch/include/ATen/ops/_nested_get_ragged_idx_native.h,sha256=DW69q74kL-hRSBtQODQ_1TtZD8Pa55kFQGY44qsx-do,422 +torch/include/ATen/ops/_nested_get_ragged_idx_ops.h,sha256=wonmJwgTSSqIOHwSdwKMACY0QjtAkGlzV-GpeCarYlg,993 +torch/include/ATen/ops/_nested_get_values.h,sha256=iXgBgMcX30M7FQZkYAiBQ3YyG59vxTGKF8znl7NgZl0,691 +torch/include/ATen/ops/_nested_get_values_copy.h,sha256=CPyMXjv9-QUTIWF1MPRIypdmwbKkTtjmaHzwpLQ0CyM,1197 +torch/include/ATen/ops/_nested_get_values_copy_compositeexplicitautograd_dispatch.h,sha256=-m7IQ0UzLJBaCPItHEj7dBrgWlmI3L1TzlrbBD5GpS8,901 +torch/include/ATen/ops/_nested_get_values_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=gZzOnSQPabcMIGSX-ic34UTH4orJSnpjCIZKMFnsI6A,807 +torch/include/ATen/ops/_nested_get_values_copy_native.h,sha256=gNbyVmnYt7uyDygf7S-dtNwRvBy97KNfLFOXrFDxZYo,588 +torch/include/ATen/ops/_nested_get_values_copy_ops.h,sha256=lL3cFkjTznM1XvL_nNyVeaXwCoO9jBnztRE2MrTL-sM,1649 +torch/include/ATen/ops/_nested_get_values_native.h,sha256=DW69q74kL-hRSBtQODQ_1TtZD8Pa55kFQGY44qsx-do,422 +torch/include/ATen/ops/_nested_get_values_ops.h,sha256=uGeBWP3-frmzS1zztKCg2TiZkwrwnW__bZ7PX5ctuGc,999 +torch/include/ATen/ops/_nested_select_backward.h,sha256=JcAABIERXONj5zJs62wELcZPcZmEDxxjKdBvK-6TVQw,1792 +torch/include/ATen/ops/_nested_select_backward_native.h,sha256=zrZ6QbvOHNcBibPBlZM7javLsvFNppEaS39UmI7L6aI,564 +torch/include/ATen/ops/_nested_select_backward_ops.h,sha256=GH-d8u2ySN9iJ0mKMKy_rcHz9XU7w3iT1qrZlUUULSE,1221 +torch/include/ATen/ops/_nested_sum_backward.h,sha256=8aHzEhjsjdPz8fMLIMc-rfTKLP5u4VKaQr28C963hLY,833 +torch/include/ATen/ops/_nested_sum_backward_native.h,sha256=-s5BiwhSiWOfEumrHrbC4HjPlt8ow4nhj0yhYSRyIaQ,568 +torch/include/ATen/ops/_nested_sum_backward_ops.h,sha256=HE575gmPY9CWm64hfjaOVaJGjIBrlxqhyzT0pyIhr5o,1232 +torch/include/ATen/ops/_nested_tensor_from_mask.h,sha256=f56hAVBIIabQXLRqIuFVLW2eQH75p4yCWB23Z_wX17A,1475 +torch/include/ATen/ops/_nested_tensor_from_mask_compositeexplicitautograd_dispatch.h,sha256=yG2KKPVZNa9fSt_uN3Q8JAxmTJVGQS8kbasEZZzlBpQ,986 +torch/include/ATen/ops/_nested_tensor_from_mask_cpu_dispatch.h,sha256=_db9ON9nYCPA3jwClpy-5YbOfmtYTEEkRn36GqPzmt8,782 +torch/include/ATen/ops/_nested_tensor_from_mask_cuda_dispatch.h,sha256=Lo80aWfYrvo_W_UxTIsmQlilRAJhjJ4MtYUgGNrYvGg,784 +torch/include/ATen/ops/_nested_tensor_from_mask_left_aligned.h,sha256=MQ1IHGuHnIuekd472eVUpLx9Y1nasMwmvZQsgw6ppnY,788 +torch/include/ATen/ops/_nested_tensor_from_mask_left_aligned_cpu_dispatch.h,sha256=W2lvBKb80HYKnPA1rppp6TyLBUs2PaJlfdr3lGx-8nM,767 +torch/include/ATen/ops/_nested_tensor_from_mask_left_aligned_cuda_dispatch.h,sha256=6HJcOCX2R8e251BiY7tEjWcmY0MlKF3gzG0bkLDFFUs,769 +torch/include/ATen/ops/_nested_tensor_from_mask_left_aligned_native.h,sha256=HhcabMmXZikf0oMBIgm0Y1dWwN2wHj4I11W0199J7EU,535 +torch/include/ATen/ops/_nested_tensor_from_mask_left_aligned_ops.h,sha256=MaIiB8Hcf9OQnpQi7GmatWAYt-nwJ3uFFtE4kRyeG0U,1104 +torch/include/ATen/ops/_nested_tensor_from_mask_native.h,sha256=tqA-6hR07LasfPzYked8RsOh6fgKgShjZpmhHpv1O2E,685 +torch/include/ATen/ops/_nested_tensor_from_mask_ops.h,sha256=UWLEwkdSVaEkmIPzbXtAb98Nr11lZWg3DFfLNEHlIUc,1927 +torch/include/ATen/ops/_nested_tensor_from_tensor_list.h,sha256=18JYWe4oesPqyL3BWURR6OCR_hM_9zVw5Aw8697LXF4,2198 +torch/include/ATen/ops/_nested_tensor_from_tensor_list_compositeexplicitautograd_dispatch.h,sha256=gFlQZLeWGJ5fsjkRZ5FWEWUDQ9YFxGs4lFODpx1ED0g,1539 +torch/include/ATen/ops/_nested_tensor_from_tensor_list_native.h,sha256=haf40hCL_DdeC0uVFZtjEDOJcWt_j0bYOFipUgPTQPY,946 +torch/include/ATen/ops/_nested_tensor_from_tensor_list_ops.h,sha256=m2IkMPj2U_66fVqeagYh-buUN29gYthqozkO9mIUtBg,2663 +torch/include/ATen/ops/_nested_tensor_size.h,sha256=rkmz-L46xhQObnSznlTltx4pfrugpTdzojRJ26O7HQA,983 +torch/include/ATen/ops/_nested_tensor_size_compositeexplicitautograd_dispatch.h,sha256=kRC_WD1a497H4IcJLxouqjtaNbuCBDtPtFPdTHzGW4s,893 +torch/include/ATen/ops/_nested_tensor_size_native.h,sha256=6GTtwT0Qbb83B4Y7aO4UiaFfPiwLr7cLa8YsK08vut4,580 +torch/include/ATen/ops/_nested_tensor_size_ops.h,sha256=4VGuzrPuKdRDNvctF95l4mU0HfPXjyLzSRG-kxzrbNo,1625 +torch/include/ATen/ops/_nested_tensor_softmax_with_shape.h,sha256=yp7DxmNblDah9tBzUga5PUojbjJDC1boptZRlccGlKc,792 +torch/include/ATen/ops/_nested_tensor_softmax_with_shape_native.h,sha256=fK8I9pDvDb6eHPOW2Y5kHD9dKrD8AMOQ4ol6PkqJY6M,631 +torch/include/ATen/ops/_nested_tensor_softmax_with_shape_ops.h,sha256=AeX1Qb2WRqCcoKQ-FqeltinestvlJgIh8BLiSj0N5EQ,1124 +torch/include/ATen/ops/_nested_tensor_storage_offsets.h,sha256=US-LTnoaOW0dKfrC6wpU3MURTFKNNzMajp2LXcYwiI8,1060 +torch/include/ATen/ops/_nested_tensor_storage_offsets_compositeexplicitautograd_dispatch.h,sha256=29pbwDFoil8GmlZjcmkDayjvoo123IQ8I5zz7AQWorU,915 +torch/include/ATen/ops/_nested_tensor_storage_offsets_native.h,sha256=dcEA-bj6Aj1NoM5oa66W8ktNekVr_vy05FngMdKea9A,602 +torch/include/ATen/ops/_nested_tensor_storage_offsets_ops.h,sha256=EMoA1wFPQm4O0gu8LlHkz7pNclGRB69O224kx9dvsao,1691 +torch/include/ATen/ops/_nested_tensor_strides.h,sha256=0n7GFzi8fRlUDdoVO1UaB6hje0jtcNj3Y-br7eKpma8,1004 +torch/include/ATen/ops/_nested_tensor_strides_compositeexplicitautograd_dispatch.h,sha256=PzjmLHBtpG65NfzI2svok35epdEUzO4yvuOd6FtBPCs,899 +torch/include/ATen/ops/_nested_tensor_strides_native.h,sha256=SgdtVuVSWEjz3LDfgXcDP9j4UXj_KF69-Eat4KzwIGQ,586 +torch/include/ATen/ops/_nested_tensor_strides_ops.h,sha256=veJk4Bz1Zz7xvnYFwuSMa4zxFJ9UeZ9rQGZ2fqSxNpY,1643 +torch/include/ATen/ops/_nested_view_from_buffer.h,sha256=ezeScCr5GYQcQtM1OQni9EEM7gb2e9EyGbo6PZyrvq8,907 +torch/include/ATen/ops/_nested_view_from_buffer_copy.h,sha256=iFNFRQmRhy9TswGoZR4ovX2qHmSM-mfoKVwDZEGuRmA,1833 +torch/include/ATen/ops/_nested_view_from_buffer_copy_compositeexplicitautograd_dispatch.h,sha256=4xsnR3nVM-AO3yBQJxO5gJu-n7NpWbvXfXhQrRUA1bQ,1103 +torch/include/ATen/ops/_nested_view_from_buffer_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=sDMSMr0Bk34tO9Euu5kbWD98NiTqd6GpQYg1pK7XslM,908 +torch/include/ATen/ops/_nested_view_from_buffer_copy_native.h,sha256=_YAKUuqh1FGlK4qGFatFAWT4kSv8MrgMhH834Op-N9g,790 +torch/include/ATen/ops/_nested_view_from_buffer_copy_ops.h,sha256=3Ky-_gqHOUvRExsPg5-miSaF9JH0C4p6sUFzoV23tbU,2303 +torch/include/ATen/ops/_nested_view_from_buffer_cpu_dispatch.h,sha256=ykOzV8r4xLOhbou-kyl1YCass5Dxc0xvdONF1Y1cxaQ,833 +torch/include/ATen/ops/_nested_view_from_buffer_cuda_dispatch.h,sha256=ReUTHWBjjs8oZU3EbRd9Zt8LWSjRiwdXJ0FcuUIN6Fc,835 +torch/include/ATen/ops/_nested_view_from_buffer_native.h,sha256=SzaXu2OpiF0LAq4GfyXjQ7_8TYQ-YwMfVD0ERPxo7PA,589 +torch/include/ATen/ops/_nested_view_from_buffer_ops.h,sha256=q4ao-UP47GUZsfAtbI7QN4-UvdReUlNYbKfxQa20Og4,1326 +torch/include/ATen/ops/_nested_view_from_jagged.h,sha256=pqAZ0nRiADXF8xoG3bXfRQ4LlwGIhTQVcFlaiTU2Peg,1122 +torch/include/ATen/ops/_nested_view_from_jagged_copy.h,sha256=YvtQvM25zMCw64hjMKCziA_eZwAWO3b3Yak3wkwwc0w,2467 +torch/include/ATen/ops/_nested_view_from_jagged_copy_compositeexplicitautograd_dispatch.h,sha256=uwnHVeJT5DxsneTZ1gSJcqRUY1eaBBP5kGs0Hb1e0WY,1354 +torch/include/ATen/ops/_nested_view_from_jagged_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=hJaob43NAAae_jqXC5nCr5I8nGnYqyzF2UNGBCpPLjw,1039 +torch/include/ATen/ops/_nested_view_from_jagged_copy_native.h,sha256=qu1lZQ8n3qlPfpavPO-daDa-Uh5rdtUjkd_EHFZGoow,1041 +torch/include/ATen/ops/_nested_view_from_jagged_copy_ops.h,sha256=4NLDuY9bDp292wklo2LVJYQeSBsxGM_uxWW0JrQ15cA,3105 +torch/include/ATen/ops/_nested_view_from_jagged_native.h,sha256=DW69q74kL-hRSBtQODQ_1TtZD8Pa55kFQGY44qsx-do,422 +torch/include/ATen/ops/_nested_view_from_jagged_ops.h,sha256=C_ErV2XyIVfPPKt4qb186WXbQmGUgWeycS8zK6lAvU0,1727 +torch/include/ATen/ops/_new_zeros_with_same_feature_meta.h,sha256=rPVjtWpqfm5o4sBPA2yPMeGdDDfV8AehkjA7lD9s-Gs,1676 +torch/include/ATen/ops/_new_zeros_with_same_feature_meta_compositeexplicitautograd_dispatch.h,sha256=9BENUHd0r_WwACCsYcRe3j9eT4RpazOGyJg7e6y4CQE,1171 +torch/include/ATen/ops/_new_zeros_with_same_feature_meta_native.h,sha256=CBmVt7sRU3uUcoctkKFFhnohr_GASzHJemhh4iqcaMQ,720 +torch/include/ATen/ops/_new_zeros_with_same_feature_meta_ops.h,sha256=5FfZUx-rTncEd4nPj6gF-gzrN0HLPnNby8Nfm2Ep2uQ,2072 +torch/include/ATen/ops/_nnpack_available.h,sha256=OWzLcj1OsM-kSxbzE51yZfeg_L2aIGqM-LfCj6-TJzc,635 +torch/include/ATen/ops/_nnpack_available_compositeimplicitautograd_dispatch.h,sha256=2oNwH-lWZpytHSxG8tJmeiHlNX8f7_nzcktVxqFpigk,746 +torch/include/ATen/ops/_nnpack_available_native.h,sha256=NU4jkiwZCK4BK_Bn_D_T4p24_gGk98dWBlRGQRhO54s,458 +torch/include/ATen/ops/_nnpack_available_ops.h,sha256=WRvL00N7uwGNCokSsOQHTlQs_38B6qLUKyk8qHXO2tU,893 +torch/include/ATen/ops/_nnpack_spatial_convolution.h,sha256=RJd3pbOsomf0CNgNw0gf9i3ddil6d_RwqK7m94_l0hQ,6190 +torch/include/ATen/ops/_nnpack_spatial_convolution_compositeexplicitautograd_dispatch.h,sha256=iuB92Rh4qWfLW2AruDg6UEfAN5Ujcgduu4LxC_OnrNs,2051 +torch/include/ATen/ops/_nnpack_spatial_convolution_native.h,sha256=jubJ0IPto0mHhwfsHO6ba31deRFyzqkMaLWuy17WZv0,851 +torch/include/ATen/ops/_nnpack_spatial_convolution_ops.h,sha256=QwlXPbSyUroVYEi3AjYlB_BmVj2YXXIidxuCSdMEcUc,2517 +torch/include/ATen/ops/_nnz.h,sha256=9Yt5LsTR7XXNTprLn0-rd2Lf8NeBdwYs8RQ50PH9x7w,501 +torch/include/ATen/ops/_nnz_native.h,sha256=jw0invqXBjDYiKKHiUMFIeMfc6fFspjSbtP_LCXBwXk,538 +torch/include/ATen/ops/_nnz_ops.h,sha256=J9zldFC6b9j3mLz0mZH2Wzn42eRXg3J8UKXkTVeBW68,939 +torch/include/ATen/ops/_pack_padded_sequence.h,sha256=ugqff_WkHJkeSf978F0xlSUHEoch4ZtbhvjgDbL8FVk,1699 +torch/include/ATen/ops/_pack_padded_sequence_backward.h,sha256=hxGcsNtATRdat8I8dKl7qxUvAHqFy3FBbEBzlA-CosI,2077 +torch/include/ATen/ops/_pack_padded_sequence_backward_compositeimplicitautograd_dispatch.h,sha256=yCm5tI9c10nPNYM7zmL6r_Z3extYX9DLNCBxJd2RGCk,1033 +torch/include/ATen/ops/_pack_padded_sequence_backward_native.h,sha256=Fb64S-Np3OGjj9elba6Eh2KtNluxYo1quHwDXqXEZmM,589 +torch/include/ATen/ops/_pack_padded_sequence_backward_ops.h,sha256=4IK-0QRQDzp5v96AkT1Oftb9ZPsjbf7cqcdlafP5YQE,1299 +torch/include/ATen/ops/_pack_padded_sequence_compositeexplicitautograd_dispatch.h,sha256=gzDQ_JNr51ONerugHWK1PYQoR8nBXA8Vw-6N6qwNkZc,1226 +torch/include/ATen/ops/_pack_padded_sequence_native.h,sha256=e4kC3JlvbutkwmlBHjXrNq-esT13PpBenc_W4NkEM88,750 +torch/include/ATen/ops/_pack_padded_sequence_ops.h,sha256=H_eflZ7xWqlDQgERikn5_m986s3DiboyJfO-DD6qeBo,2199 +torch/include/ATen/ops/_pad_circular.h,sha256=Q8_ruOxBwe9oqhK0_vsvcz-1vHXpVwuAwQF641sML7U,1440 +torch/include/ATen/ops/_pad_circular_compositeimplicitautograd_dispatch.h,sha256=QtTIA4I4E2HDYpcvc3WSw_4m_eBNk-ZlcRvGpsIt3Z0,885 +torch/include/ATen/ops/_pad_circular_native.h,sha256=JZwjVzvgAyfZb0iWYCkOWy-vgS85on4DKtcH2uFDzfw,515 +torch/include/ATen/ops/_pad_circular_ops.h,sha256=b3qa7t1PA95agg8QbbiVUdpy1LtSei7vcNO9ObtDZc8,1063 +torch/include/ATen/ops/_pad_enum.h,sha256=5NjatrdWm2bFz9cPHymUiLIQAFVBwQQYYhD5iY0OVrc,1746 +torch/include/ATen/ops/_pad_enum_compositeimplicitautograd_dispatch.h,sha256=_Ul2T3WfMKsBgHBLecQG7k5CxLU5PsJTxxWGtrLipAk,997 +torch/include/ATen/ops/_pad_enum_native.h,sha256=xaoAjJT-gj6ZCHgVctzHPS8u25--c8djm6VW_pvF45s,571 +torch/include/ATen/ops/_pad_enum_ops.h,sha256=TQA8e8ltAC4KE3GFX57luT11TafzdmQBBH0OhKOecHA,1204 +torch/include/ATen/ops/_pad_packed_sequence.h,sha256=h5dxhqUdtvoRPPQPZjgQXGoyVuF3BmqRdv-vspYmjqA,967 +torch/include/ATen/ops/_pad_packed_sequence_compositeimplicitautograd_dispatch.h,sha256=9G5cHee8yRICx-GiKm1ArlAkMn5awQqQHH3xTIYGO1Y,909 +torch/include/ATen/ops/_pad_packed_sequence_native.h,sha256=eAPciXJcYnb6fJYMQjTUOv6BljAEiOXZrkve2SEXt98,621 +torch/include/ATen/ops/_pad_packed_sequence_ops.h,sha256=L16no4xhNrDRlMnuj3AMTl0IvHU1sbXX7VhxcwKo_90,1429 +torch/include/ATen/ops/_padded_dense_to_jagged_forward.h,sha256=T17phqIK1yL2ZWm1POlWPDp9yu3TopAG454ADFUh0eA,2058 +torch/include/ATen/ops/_padded_dense_to_jagged_forward_cpu_dispatch.h,sha256=AZjvbhFpE6fSMKmOSDsVleqgbZCVdI7NEUMC4xJwawc,983 +torch/include/ATen/ops/_padded_dense_to_jagged_forward_cuda_dispatch.h,sha256=K_tFEMqc-lZY7uFfTFvj4BJgtN2MbDMag5pP92qdfBw,985 +torch/include/ATen/ops/_padded_dense_to_jagged_forward_native.h,sha256=j55D-SNQKIXmhcxDYUgrQpipCYqneAdVhxnc2faBudk,743 +torch/include/ATen/ops/_padded_dense_to_jagged_forward_ops.h,sha256=J58oWi8VRzM8BhpVDDmtEFFJHljMY41qlutxh7RSEcw,1245 +torch/include/ATen/ops/_pdist_backward.h,sha256=s7QMZYTgyYKw38QGN9WfYJt_fTHCDE6iPwUmGk7cIEA,1456 +torch/include/ATen/ops/_pdist_backward_compositeexplicitautograd_dispatch.h,sha256=H77KYk4noXgKrj5PWFq6QBNLGCcFXWJelo8ra8uHptk,1007 +torch/include/ATen/ops/_pdist_backward_cpu_dispatch.h,sha256=a0N0hDzke-MrHpOOshIQ0d5cDt3owOjIuhHw-GN33vw,790 +torch/include/ATen/ops/_pdist_backward_cuda_dispatch.h,sha256=X_MnIgPf0TDhjxhvF0WBUqtHBCudUXjakoj4wD_y5Cg,792 +torch/include/ATen/ops/_pdist_backward_native.h,sha256=RZw8vFMKw0tj6rdCc3VlBhx3kURJ-BpZeMTUVE4ANEc,694 +torch/include/ATen/ops/_pdist_backward_ops.h,sha256=imhWBIHwsfKF4VhZF1r1ubGRlP-8MBmqFFNkEj1ukio,2013 +torch/include/ATen/ops/_pdist_forward.h,sha256=r8EK5-Z_UJQRI_Vaxcif1bwkB5GkZhE4tTTjByo2ilM,1183 +torch/include/ATen/ops/_pdist_forward_compositeexplicitautograd_dispatch.h,sha256=rF6HXPJB3pFFLxcZ0POzqR-3Qk0XtPoA7s2EsT8nhnY,905 +torch/include/ATen/ops/_pdist_forward_cpu_dispatch.h,sha256=u2s3-RvtmlNDg1NunA4fvlG4zmeIOUQmwDGt7ZLmWFE,740 +torch/include/ATen/ops/_pdist_forward_cuda_dispatch.h,sha256=cXE4nR7YrJpW095VS1G6BQV9PsdQs5CfvMYxIiuODws,742 +torch/include/ATen/ops/_pdist_forward_native.h,sha256=-YH_I9GBAj2U4UG8YETxGrDprrU8p0W6Ps5uGnNGeqA,592 +torch/include/ATen/ops/_pdist_forward_ops.h,sha256=xkHIeX05VW6b6PNnw5FDUUINhxyEd8P8K1FLaD8doXM,1673 +torch/include/ATen/ops/_pin_memory.h,sha256=sJiDzztGZrNPqbBDtYjqL6eG41XcoTA1NQtGEdkNWYE,1302 +torch/include/ATen/ops/_pin_memory_compositeexplicitautograd_dispatch.h,sha256=xe3pci6o1fKBcgotklmg7ddO9xR65h2gtF4H1t8eC-U,1074 +torch/include/ATen/ops/_pin_memory_native.h,sha256=V-BYj_U_nYP5reuKqgmfaagf443T1SEMKbQnQovkMF4,1017 +torch/include/ATen/ops/_pin_memory_ops.h,sha256=MAidsFXqd8ypjvL6GYvw3EIm3EMr3zR0vbt_mjflUns,1821 +torch/include/ATen/ops/_prelu_kernel.h,sha256=MlPbDLBukySWyAv9e_TTFuVAeCUDyviH7NiR2llOtio,715 +torch/include/ATen/ops/_prelu_kernel_backward.h,sha256=317AdFyL867-FgPWU-nG99hEZv6NO0aH_houiLwBK3o,851 +torch/include/ATen/ops/_prelu_kernel_backward_cpu_dispatch.h,sha256=BmPFx4Bemy8zHsuSquGti1mRPy_wR76KKnfI9ixHFtI,820 +torch/include/ATen/ops/_prelu_kernel_backward_cuda_dispatch.h,sha256=C0v-2Nhx_sYL7mHSbmw4Ct6IvA92oPLq_PHThlNzhsI,822 +torch/include/ATen/ops/_prelu_kernel_backward_native.h,sha256=tIpTnIEtIrw6GG7EFeL0fDPhPj0O69mQdoROuT1IVxA,729 +torch/include/ATen/ops/_prelu_kernel_backward_ops.h,sha256=ZMiDKfwXE6exOY-74j-_fV7UlJQ_XzcNYwcIbhHLrMg,1283 +torch/include/ATen/ops/_prelu_kernel_cpu_dispatch.h,sha256=qNj7fNqiaxYmsptuzl8BBDkus1kso9PrFRkXxQzW3Ec,754 +torch/include/ATen/ops/_prelu_kernel_cuda_dispatch.h,sha256=G5XP80uUHs7AlHaKFBER66SCi6zJG49wdu4v-Ki0Cdg,756 +torch/include/ATen/ops/_prelu_kernel_native.h,sha256=Lo8r7sv2rl843cvVHk5d0tmwEJVz0T2ySrGReh8Gr-0,699 +torch/include/ATen/ops/_prelu_kernel_ops.h,sha256=U3olCi3OcgJb0wr84uX9qnklFFw5izSZ70yHBbNJo-U,1067 +torch/include/ATen/ops/_print.h,sha256=nIMEnz1ZnlR8iOnAw-BrELGvKGPwHW9zXqWZ7e85wjs,613 +torch/include/ATen/ops/_print_compositeexplicitautograd_dispatch.h,sha256=7atO-mRnQLIHquBmE-UcnShUZsV7bnvNzSCjSMrp6GI,753 +torch/include/ATen/ops/_print_native.h,sha256=wjgagQSENvcf5nzHhUUmPngwPLGT3V1AwLXEEUTeKHQ,465 +torch/include/ATen/ops/_print_ops.h,sha256=469F2Qk7ZRy-TPAz8trGncErSzo6UsYISCGeqhgXiiU,917 +torch/include/ATen/ops/_propagate_xla_data.h,sha256=CJF7cMWMKEyr7lFKq1WIUn5Bo4UHBLq2Y_8AyE94oV4,732 +torch/include/ATen/ops/_propagate_xla_data_compositeimplicitautograd_dispatch.h,sha256=wsy94CSqBa32NaPK8WoQg3q0WVuOMNaVcMfuKuYRY9Q,799 +torch/include/ATen/ops/_propagate_xla_data_native.h,sha256=WzdmczalCsYmbZyYSiV1VYMMEq-xHIQvYSonrdGBN_c,511 +torch/include/ATen/ops/_propagate_xla_data_ops.h,sha256=lmFLk3f0K4lTRAtauKEAzgQE4wR-bQJp8jycMh5EsHU,1066 +torch/include/ATen/ops/_remove_batch_dim.h,sha256=zuEFNHhm5Oo2ADGncB4AMxBoh-lnhzl6mjzeSd8ys4U,1706 +torch/include/ATen/ops/_remove_batch_dim_compositeimplicitautograd_dispatch.h,sha256=-4MIhEi7g_GgijR7415ox900SJMXqxXf1U-ZJWntbeo,955 +torch/include/ATen/ops/_remove_batch_dim_native.h,sha256=tBJIZ3AoHyynQ-MGrmYY5EOFoHtznqoQ6S76gZtzZDM,539 +torch/include/ATen/ops/_remove_batch_dim_ops.h,sha256=Zumll5aOoePgbJIStBZckFbk5_05f2LkP4CpHaRERIw,1176 +torch/include/ATen/ops/_reshape_alias.h,sha256=8E6DWJ6-4cJ3Co1bNiZEt8dW9bo_WDSYycIHipb5Sls,1695 +torch/include/ATen/ops/_reshape_alias_copy.h,sha256=9QsqgzrzH0iG4_VCpgICnh5PCAqA-Jva6z88cWvwXro,4560 +torch/include/ATen/ops/_reshape_alias_copy_compositeexplicitautograd_dispatch.h,sha256=cS5BkWxA2x-5DTjbVBtIdSrO1nVMxssyeFxVNZEaayg,1290 +torch/include/ATen/ops/_reshape_alias_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=_CSBaxNQzhLePnilQ9owacP8PBKrP2qlPbk9ef42CTg,977 +torch/include/ATen/ops/_reshape_alias_copy_native.h,sha256=Nkq4m4zeDhHmwDLoo_lFfyPAS5FW-GxhaPpRqW5s-DI,702 +torch/include/ATen/ops/_reshape_alias_copy_ops.h,sha256=pQeNuBXZLVsR4wrfjrUKQYc4QlnzXDG_CkhmkfqeGFY,1989 +torch/include/ATen/ops/_reshape_alias_cpu_dispatch.h,sha256=DdyfDNAZC0K8R_mkZv6gxXYf_NssjGVkhuPVUVu-Kow,897 +torch/include/ATen/ops/_reshape_alias_cuda_dispatch.h,sha256=xhTPREjP6wX4Ihy0FeZZwGNFSeskRVikcUPOsVORxNY,899 +torch/include/ATen/ops/_reshape_alias_meta_dispatch.h,sha256=FASzp0vwnNXOpA0Ri3aEypREKxq7W3edJ0BO5rrsgNA,899 +torch/include/ATen/ops/_reshape_alias_native.h,sha256=nh9_TdNFv5T60GU8253qQX6lAuF2RzsVsE2i9mH0vPk,530 +torch/include/ATen/ops/_reshape_alias_ops.h,sha256=l9QB49S3CWkW8phdUnD50XOZTtD85lG51zI8sEGvyfo,1169 +torch/include/ATen/ops/_reshape_copy.h,sha256=O0PQSjeoAqxRi0gQ_4BWY5g_Y0PcUjImtr3pbPeA-64,1450 +torch/include/ATen/ops/_reshape_copy_compositeexplicitautograd_dispatch.h,sha256=24ldQ0ihNYXysT-kNOVChfQn8sc24KGFnDWi92E0A4w,887 +torch/include/ATen/ops/_reshape_copy_native.h,sha256=3pZ9_1teFm0KESU3USL8Ph3Jw1L09UlYFrmGK692TM4,516 +torch/include/ATen/ops/_reshape_copy_ops.h,sha256=TAUqeAatM7x9FXbKfEHu5l6fY7ykTC-u3ipz0h53lj4,1066 +torch/include/ATen/ops/_reshape_from_tensor.h,sha256=X6BScG1H28IbU3rpOG3ls4_aLpzz-3XAkMezdyewMY0,740 +torch/include/ATen/ops/_reshape_from_tensor_compositeimplicitautograd_dispatch.h,sha256=RL6sIJViMUBVUHxeYzsgX0UAk3FeW63g8nx9qKJL0CA,804 +torch/include/ATen/ops/_reshape_from_tensor_native.h,sha256=eJLbxQCT7mmsdeyYcW7S4m5v03GbpkmtsmECuTjfqOo,516 +torch/include/ATen/ops/_reshape_from_tensor_ops.h,sha256=r6pM5yxFy4xZ1XLL2pRmuO1s1WdPNUMAFBezQyGoZcw,1085 +torch/include/ATen/ops/_resize_output.h,sha256=jezZFcyWjczIRtMSXIZM1kH_XZgaLl7hTjEsmfUexQ0,5396 +torch/include/ATen/ops/_resize_output_compositeexplicitautograd_dispatch.h,sha256=pgloaeJYto62i30hAB1KicDJoHaVdx_jY5c_9VzXpgM,1507 +torch/include/ATen/ops/_resize_output_meta_dispatch.h,sha256=3vo-D6zoG0rJv3vIMlo7tbMGBATzWK6JzR0y_EI68h0,903 +torch/include/ATen/ops/_resize_output_native.h,sha256=ppRF1p0j0VoQ_AJUbpZqrkYv9wtxWfPffBivF5KvRTc,798 +torch/include/ATen/ops/_resize_output_ops.h,sha256=wOoUBdCzQZbSPVhP7Q12pF0W2COEbtggHodoWTBM6lc,2649 +torch/include/ATen/ops/_rowwise_prune.h,sha256=XBkTWzHzST_daZ_WrWxnydqlVBAI7AdWCoPb2uki-Vo,858 +torch/include/ATen/ops/_rowwise_prune_compositeimplicitautograd_dispatch.h,sha256=TocpoDp_G7GtscYXJughflRSJ-y1EMt8bPcfnXSctAM,865 +torch/include/ATen/ops/_rowwise_prune_native.h,sha256=EZEoSQaCB8JsfopiZN09FXxpfnhymc0pGXNDAhPmaM0,577 +torch/include/ATen/ops/_rowwise_prune_ops.h,sha256=yTMLtTPi5JvwUfDkRAclFnqzjTWFcBpl2OJFBefqcDU,1290 +torch/include/ATen/ops/_safe_softmax.h,sha256=4amACZRM8QNZvXU4eAUFoDiMsJcBttk551aGKr-PBu0,777 +torch/include/ATen/ops/_safe_softmax_compositeexplicitautograd_dispatch.h,sha256=2y0s8ks6f4R4IxQZFctTZCLpvJG4Cm9EZSEZtnK8FMw,838 +torch/include/ATen/ops/_safe_softmax_native.h,sha256=MHWYlW3AkBW2u2P8XWkOHtf183XkqOpQaM-p_h59gXM,550 +torch/include/ATen/ops/_safe_softmax_ops.h,sha256=n0PCnRI1XNEDqMMfvkr-cmUoWGVVTvWbJ8olfsr0EZI,1157 +torch/include/ATen/ops/_sample_dirichlet.h,sha256=txJBKr4qh9NHJDMniXWPPejlrrrl_sSE5AVXmM4ALPc,1407 +torch/include/ATen/ops/_sample_dirichlet_compositeexplicitautograd_dispatch.h,sha256=cnuA970mzvFwenVPw9bFzLv7nm5H9soQtOMcPePKEBM,988 +torch/include/ATen/ops/_sample_dirichlet_cpu_dispatch.h,sha256=JtFQW9wFFi6NYmBrhkYOROGUJppcNyRegF9ob3UMo2I,788 +torch/include/ATen/ops/_sample_dirichlet_cuda_dispatch.h,sha256=AbluI8upWiRlaOBlrjslAghkgbGQzym0Jm6F5qwKDXs,790 +torch/include/ATen/ops/_sample_dirichlet_native.h,sha256=2KuA3wlSDf34omOK8w1XEJyLHJVKop6j5p3onATbrkc,796 +torch/include/ATen/ops/_sample_dirichlet_ops.h,sha256=jW4hHpqtym1g6-PHQj1UAeKhq2cYKhoO3uYSI15o6ow,1899 +torch/include/ATen/ops/_saturate_weight_to_fp16.h,sha256=n3ItQi2mgtzP8JJwzQdX9fxFwfYh0Ynqcz_WKdPZ2AI,715 +torch/include/ATen/ops/_saturate_weight_to_fp16_compositeimplicitautograd_dispatch.h,sha256=aftCRpQ_qbwk9mA6C4LJ68KjR2Tk5Zj4FopWMN246LA,784 +torch/include/ATen/ops/_saturate_weight_to_fp16_native.h,sha256=uX0WqBWkvuRWwt4Bhw2i63eB1tt6XrUfonzkpiojC-M,496 +torch/include/ATen/ops/_saturate_weight_to_fp16_ops.h,sha256=mhA6k2DUDpa2n4ikq1AhZUog9_cSqDFhYKi--EjpPZU,1017 +torch/include/ATen/ops/_scaled_dot_product_attention_math.h,sha256=79bS-mbwEtb1nBSMxHvV_7OuYEb148lBGptkQ94ui2E,1296 +torch/include/ATen/ops/_scaled_dot_product_attention_math_compositeimplicitautograd_dispatch.h,sha256=KBL2OG0_r9efvDyHPtdS4agCiF54RxT9lmWEKGNI_IY,1084 +torch/include/ATen/ops/_scaled_dot_product_attention_math_for_mps.h,sha256=wkJk4aRV2sDEou9nKOOghbqLXAiqGUwZtz8OtYCEKyk,1270 +torch/include/ATen/ops/_scaled_dot_product_attention_math_for_mps_native.h,sha256=DW69q74kL-hRSBtQODQ_1TtZD8Pa55kFQGY44qsx-do,422 +torch/include/ATen/ops/_scaled_dot_product_attention_math_for_mps_ops.h,sha256=VT12a_TJd0N8dyMG6N9CcWU1lNCmDfaSu6UGTDVPnC0,1872 +torch/include/ATen/ops/_scaled_dot_product_attention_math_native.h,sha256=Ky5PeK4bQpJVBQwJuw5RW5AaHOFmXxFuBBoV52ki43U,796 +torch/include/ATen/ops/_scaled_dot_product_attention_math_ops.h,sha256=8o_VL9S_BpSaCSZQPGBjyN76sZFdXN62wj99Az5Tpa8,1911 +torch/include/ATen/ops/_scaled_dot_product_cudnn_attention.h,sha256=NlHKwsSr4N9FAeKI-614X4KrBDnTrwUzvnQBjnG9XG0,1515 +torch/include/ATen/ops/_scaled_dot_product_cudnn_attention_backward.h,sha256=X9wpCDlj0A1MWVmQHI5WIHi9r384jkRhfEUFoGp9Vf8,4463 +torch/include/ATen/ops/_scaled_dot_product_cudnn_attention_backward_cuda_dispatch.h,sha256=aCeHqzhK-gW-dzM2QoEcg3Na9RXAuoLBu2y42WzR3gw,1739 +torch/include/ATen/ops/_scaled_dot_product_cudnn_attention_backward_native.h,sha256=gn9zV7m3maUmbanuqqj1PgSQj1W-Zzq8UQQEKWAISSk,955 +torch/include/ATen/ops/_scaled_dot_product_cudnn_attention_backward_ops.h,sha256=PdAYwjMTG4tJqYhEe2Jj-dj_8Agjdi6YSOY1udLinYU,2501 +torch/include/ATen/ops/_scaled_dot_product_cudnn_attention_cuda_dispatch.h,sha256=LPwvjcfbIBV0nFCUPqJORo-nJY-JKbdAeq5p5q3i_cM,1098 +torch/include/ATen/ops/_scaled_dot_product_cudnn_attention_native.h,sha256=KVIm_1ts9_O4gMTYfVZ7rwGGUrFyZvPr2eoHzG4xB7Q,1305 +torch/include/ATen/ops/_scaled_dot_product_cudnn_attention_ops.h,sha256=qujL8DcmwPs9ajNaJ5jNnTN3OJ-GWFA19RVxMwzisrU,2231 +torch/include/ATen/ops/_scaled_dot_product_efficient_attention.h,sha256=E26W3DFtAlSJBn5ECeiA-09JV-fm6Oh7v4Y-HYfEM_U,1308 +torch/include/ATen/ops/_scaled_dot_product_efficient_attention_backward.h,sha256=DL_WGtu1K89OLyhnpx1pCnhACQnrMxrolrrBxzbOLbM,1579 +torch/include/ATen/ops/_scaled_dot_product_efficient_attention_backward_cuda_dispatch.h,sha256=aZKsQDCmYK8-76kHnty6AdBC1HNLkiuKx80fuJ1DsLQ,1166 +torch/include/ATen/ops/_scaled_dot_product_efficient_attention_backward_native.h,sha256=NMA_G0gKlIHg7NuByPU1Mj26u_v8L3I6iKiZ2iKOQ5k,925 +torch/include/ATen/ops/_scaled_dot_product_efficient_attention_backward_ops.h,sha256=yFHVaXHwyC335v18ymT6M6gO4EjcgqcPyiKLxl3PW4E,2360 +torch/include/ATen/ops/_scaled_dot_product_efficient_attention_cuda_dispatch.h,sha256=DNq7vkciN2mtViOx7QvVZmLkBspKittT7pePdK2WGcM,1015 +torch/include/ATen/ops/_scaled_dot_product_efficient_attention_native.h,sha256=bu4NDYCGTlXjKgzNSGbp0lWCH9wo2uS2xVeD_qsG43w,1139 +torch/include/ATen/ops/_scaled_dot_product_efficient_attention_ops.h,sha256=mmBLJGDGa4em0wcF_3fGWHUjUxlZTQwJT0DgSoDnkUg,1901 +torch/include/ATen/ops/_scaled_dot_product_flash_attention.h,sha256=32azd3eKvDpVVLyYqYmBY2DErIKZF8cmw5pE-tKfvZI,1359 +torch/include/ATen/ops/_scaled_dot_product_flash_attention_backward.h,sha256=JBxWMcujDemUE27AcCwiKSkZpXkMdWJovQQIOWEtCx8,4325 +torch/include/ATen/ops/_scaled_dot_product_flash_attention_backward_cuda_dispatch.h,sha256=iiI2_d7ZHwmC_7zTd14780qdEujp5S_hblJQbLy0n0o,1679 +torch/include/ATen/ops/_scaled_dot_product_flash_attention_backward_native.h,sha256=1v12TqJJR6oVDgbTfJXng9oE3fhZoa8iHinz1q7mCKU,1430 +torch/include/ATen/ops/_scaled_dot_product_flash_attention_backward_ops.h,sha256=ztW78jKw6ws-SerZ-ejjexlSeTE9x7oaF1h4QsAWxnU,2434 +torch/include/ATen/ops/_scaled_dot_product_flash_attention_cuda_dispatch.h,sha256=fE966EM7n5ItLCED8HohWZhn1zXGF8u9isSRaJp_xjQ,1026 +torch/include/ATen/ops/_scaled_dot_product_flash_attention_for_cpu.h,sha256=n72iwwaiIuQjRkDIKA0N-aCvz1bVUvpD1CzOT7k0mAk,1197 +torch/include/ATen/ops/_scaled_dot_product_flash_attention_for_cpu_backward.h,sha256=Fbi4u-SW7do35-0tqftpeduLHKI9ZLhmRIAPbYToj1Y,1402 +torch/include/ATen/ops/_scaled_dot_product_flash_attention_for_cpu_backward_cpu_dispatch.h,sha256=b73Ga-umXeHdO1RlFOyknVSjxkGF-BFeV8Xy8v1eX14,1066 +torch/include/ATen/ops/_scaled_dot_product_flash_attention_for_cpu_backward_native.h,sha256=HZBDOOHlcjSTHWw1lXVp11p__hiQRPSGEkRwte9-3uY,818 +torch/include/ATen/ops/_scaled_dot_product_flash_attention_for_cpu_backward_ops.h,sha256=YCvs7F_-8g_QLCkQ6YTmBXlhoRUFdYoN4lkWgiRN6dQ,2073 +torch/include/ATen/ops/_scaled_dot_product_flash_attention_for_cpu_cpu_dispatch.h,sha256=xp1A7L7ZtuQ384_yUvLu8LSxbUpm8Ve-qDwGO8urri0,973 +torch/include/ATen/ops/_scaled_dot_product_flash_attention_for_cpu_native.h,sha256=udcJOpeFSpkw48yBxkPW5-fkg7QjHO0JfizfVbzuYds,725 +torch/include/ATen/ops/_scaled_dot_product_flash_attention_for_cpu_ops.h,sha256=ApRnsKFWx3yFWRZdoQ5ZZc-36GmPW2UBxbgdrEV18kI,1728 +torch/include/ATen/ops/_scaled_dot_product_flash_attention_native.h,sha256=gxfhDHf7j3I2rZarZrOeSH0G7HiX7TjWSG0qc5TZ-XY,1161 +torch/include/ATen/ops/_scaled_dot_product_flash_attention_ops.h,sha256=5vIUUtxqfnoN-_QV5hxOU7dami2V_Nn0UEni94NYSFg,1991 +torch/include/ATen/ops/_scaled_dot_product_fused_attention_overrideable.h,sha256=ld9QZJuydFJFTvB-QlNWJmC_P6vtXm7J95QsstpLq74,1505 +torch/include/ATen/ops/_scaled_dot_product_fused_attention_overrideable_backward.h,sha256=UGbj6QT4mokOaVoH15Lnxx-Nn4xuOXt0DlLz58uVm3k,5028 +torch/include/ATen/ops/_scaled_dot_product_fused_attention_overrideable_backward_compositeexplicitautograd_dispatch.h,sha256=4aguaGOCX_zLpG4bsRDqX4kuHrAhziHyzNKIEqYIANw,1905 +torch/include/ATen/ops/_scaled_dot_product_fused_attention_overrideable_backward_native.h,sha256=lZcVRnnoUA8pvY3lUog3ikREuMwmKWnILZ91FM0il9U,1012 +torch/include/ATen/ops/_scaled_dot_product_fused_attention_overrideable_backward_ops.h,sha256=krfzWnGkfalLoG_oMsvHzIo3wElSw0log5_9pXLr7zA,2750 +torch/include/ATen/ops/_scaled_dot_product_fused_attention_overrideable_compositeexplicitautograd_dispatch.h,sha256=pJVZ3tn2gRboKhd5VD1b3wI7_UWDq3qJr59E79oDBjE,1131 +torch/include/ATen/ops/_scaled_dot_product_fused_attention_overrideable_native.h,sha256=DqF3eRODWzL5kj3ZyujqgK8x_xtuPNiw0A-dbH57aqM,843 +torch/include/ATen/ops/_scaled_dot_product_fused_attention_overrideable_ops.h,sha256=eRJygw3EhgcWbSBvNGWYoNKjvFVh6GrNbjI6gmx0C0U,2194 +torch/include/ATen/ops/_scaled_grouped_mm.h,sha256=lVQucICCQ-yTsIUWvk22ASN4KVWdYn1NLWevCtP5Enw,1236 +torch/include/ATen/ops/_scaled_grouped_mm_cuda_dispatch.h,sha256=BNbFx6Zg4laD0IHdtVY9yua9_2v9tYy3kCjZUd71BwI,1043 +torch/include/ATen/ops/_scaled_grouped_mm_native.h,sha256=eLWcP2-BGf4AsBvrdACJQjrZCAo7N0vR_R23olHghHA,802 +torch/include/ATen/ops/_scaled_grouped_mm_ops.h,sha256=64aXjc2zs3o5cguw-JUEpP5LdWRHEqGi8sP1AnHy-d0,1926 +torch/include/ATen/ops/_scaled_mm.h,sha256=yuky8mnePsi7u3YztVBCRodFd9n52JFlkYMUlBmCkfQ,2483 +torch/include/ATen/ops/_scaled_mm_cpu_dispatch.h,sha256=lRXHwXPMgmoM5uAHxUeSOgF9TttBbOs5oYNr75beIqk,1654 +torch/include/ATen/ops/_scaled_mm_cuda_dispatch.h,sha256=zr-YSy513lHImQ1kjM3QERWNKPMZqwqffDr5mxuGe6Q,1656 +torch/include/ATen/ops/_scaled_mm_native.h,sha256=MnmKjoT4cHVNPDnoe5MiCrs3i8kUnWwqxHuIp_U0KRc,1722 +torch/include/ATen/ops/_scaled_mm_ops.h,sha256=U6DSZIRMeH_LlOpj-E46hU-j3h3E42AjLwb0x4TSbXY,3157 +torch/include/ATen/ops/_segment_reduce_backward.h,sha256=r2I3GZUp9TRjnl8LyI2z8kdsc85SXf7cAyo0-gbho5w,2453 +torch/include/ATen/ops/_segment_reduce_backward_compositeexplicitautograd_dispatch.h,sha256=tu-RuX0wEqCq5-QKdusGcQXL092zVnEn2rUGN9a82fM,1378 +torch/include/ATen/ops/_segment_reduce_backward_cpu_dispatch.h,sha256=dDIXzFV7f5g1HAIHVRo7bs3IFEBecC1_T7RCPF_wLWQ,987 +torch/include/ATen/ops/_segment_reduce_backward_cuda_dispatch.h,sha256=vPpCYBkOm8KXhwZqAh67YiWFVHJKI9QeV5FthofiBW8,989 +torch/include/ATen/ops/_segment_reduce_backward_native.h,sha256=aKM7nP9XJf4r9tFNC42K_HEqHmrPUQlbI3rQ49u8FdY,1072 +torch/include/ATen/ops/_segment_reduce_backward_ops.h,sha256=fUTu_eHTGl5V85CLobt7g_AE_9CoqMG2J2k4F1c1sBM,3154 +torch/include/ATen/ops/_shape_as_tensor.h,sha256=kuE0Kz4XB60lkG4IxVsWtjMkNwD46C9IXQ3eMPES578,677 +torch/include/ATen/ops/_shape_as_tensor_compositeimplicitautograd_dispatch.h,sha256=ZCQ-FBAebbCO5_RewQrirlmsICZLi6el5fA75zifXcI,774 +torch/include/ATen/ops/_shape_as_tensor_native.h,sha256=VrY8AxU9mCK5CHYjVPzhJWb4oztuR4E8gn9N46vEpuc,486 +torch/include/ATen/ops/_shape_as_tensor_ops.h,sha256=oQw8ktFFlVKR0mnSbRIKnAVBseqFyZ4wlUR1MTOqtRo,987 +torch/include/ATen/ops/_slow_conv2d_backward.h,sha256=9SvLU3EOf3lQtHqxJirHlOWnzVNlQOcbbZwp8kPe9ko,14530 +torch/include/ATen/ops/_slow_conv2d_backward_compositeexplicitautograd_dispatch.h,sha256=SPKoxCWOVsZtWFFMDNgntymGWajJRm4vycwcgTfdJs4,2122 +torch/include/ATen/ops/_slow_conv2d_backward_cpu_dispatch.h,sha256=AjxOTWVSuWk-ZehnE6JzFVMUWalOi1RJP6rKX1Axzn0,2585 +torch/include/ATen/ops/_slow_conv2d_backward_cuda_dispatch.h,sha256=u2bRQ_iKJPWpwOMyyxNezBlCRy4rO7Stu-GtiQU_Jyo,2587 +torch/include/ATen/ops/_slow_conv2d_backward_native.h,sha256=wrEWE3igO0Ph4OYejFm5lxXqv2lAmtL9I8InkFy1pSc,2016 +torch/include/ATen/ops/_slow_conv2d_backward_ops.h,sha256=7_KBkDMYMOePutHD_uS8Bmv7L1rDH9hfslttsv0Q95o,4861 +torch/include/ATen/ops/_slow_conv2d_forward.h,sha256=7GmHqB2Lhc3-ioXRyrM4H10A5iqYxg5yhrRCMs1JvvU,6781 +torch/include/ATen/ops/_slow_conv2d_forward_cpu_dispatch.h,sha256=mUomlv9PiZ86j7EY81n2TzhkzXS44Ffcxk5rKlipjsQ,2123 +torch/include/ATen/ops/_slow_conv2d_forward_cuda_dispatch.h,sha256=EDaWI-HSk8BPNRnAqXPIto6IB6fr_wvYb1VhDziCl0Y,2125 +torch/include/ATen/ops/_slow_conv2d_forward_native.h,sha256=hpIJnDWntOU51DWA8B_lgitMeDVSdQLy5BJFOF-owQk,1350 +torch/include/ATen/ops/_slow_conv2d_forward_ops.h,sha256=QQCYCDXF7gRs8wa6xdd08_9IzXjagShf-kHxxdzElcs,2703 +torch/include/ATen/ops/_sobol_engine_draw.h,sha256=j3x2oxdDibmhSVZQVcr6EFTrG7q0mU__HMSCZKzDbbs,973 +torch/include/ATen/ops/_sobol_engine_draw_compositeimplicitautograd_dispatch.h,sha256=SG6As2PX2mjB1s7ygreDjTdMsspv3YTmXugNRkt9UME,925 +torch/include/ATen/ops/_sobol_engine_draw_native.h,sha256=Mxq8jj-shWmWuV880ZKjho9UgYTYRcKsWY6i7mMGIxA,637 +torch/include/ATen/ops/_sobol_engine_draw_ops.h,sha256=_oFZKuRqf89N_FNKZO09612mo4Y0XfZPBaK93Rze0LI,1486 +torch/include/ATen/ops/_sobol_engine_ff.h,sha256=kLA5wga43821ZoVeaWSXTPewfxf_tEWeLmIPOlj_Fss,869 +torch/include/ATen/ops/_sobol_engine_ff_compositeimplicitautograd_dispatch.h,sha256=oN69ykgERDPFAUqxKcMz4J14glgmlVnjpQWGvxPGh_4,855 +torch/include/ATen/ops/_sobol_engine_ff_native.h,sha256=kKfg1yZnm2QtFoSNfNs8-4MJBURnENh9ceGUdQHYK8s,567 +torch/include/ATen/ops/_sobol_engine_ff_ops.h,sha256=vi12otIM463Q-xhYL30z4Lj-Pm-AMbeZO8DRUT1rM9Y,1261 +torch/include/ATen/ops/_sobol_engine_initialize_state.h,sha256=ROc1Jxm2l56MN2v8jJOUYEomh8VOCQ8AEjBrtt0H1Bs,785 +torch/include/ATen/ops/_sobol_engine_initialize_state_compositeimplicitautograd_dispatch.h,sha256=Xkt5-X095rrIFIOK6rbCCOYagoBCA8oLmeWBdE7V4pk,804 +torch/include/ATen/ops/_sobol_engine_initialize_state_native.h,sha256=MzRXomiP3onKERN_7kqYZTw5RpKwbidFA3i2qYv-UKY,516 +torch/include/ATen/ops/_sobol_engine_initialize_state_ops.h,sha256=-0Ip0X49SZIDcyabVHKyAjQrQOufoMBC9q4wDBmyLD4,1090 +torch/include/ATen/ops/_sobol_engine_scramble.h,sha256=eB-OAON2yGj5f-dSVxzeBs0Lgo0RDby3zxTuKmuZHgA,794 +torch/include/ATen/ops/_sobol_engine_scramble_compositeimplicitautograd_dispatch.h,sha256=rf2qEeQ3UScwdPD7bdd8jhU5hhrwTb6YCANYoqMjd-k,820 +torch/include/ATen/ops/_sobol_engine_scramble_native.h,sha256=wqI0YSOO5J3Fog_KSlsq27gjSNrIGMbzLATYIM5DH7I,532 +torch/include/ATen/ops/_sobol_engine_scramble_ops.h,sha256=era-vVpN-mNlDD4EKa4QwPz0dJoFduH89uPUVptpFBg,1146 +torch/include/ATen/ops/_softmax.h,sha256=78bFuMVI2YKvTRcKr3YpeLBrhwHFsM-pNGVulGPgaLk,1293 +torch/include/ATen/ops/_softmax_backward_data.h,sha256=l-Lj72ulTQfrLZca6UURuclxE6FrrXVdOJY1teQLFyQ,1718 +torch/include/ATen/ops/_softmax_backward_data_compositeexplicitautogradnonfunctional_dispatch.h,sha256=AyTIfPXb_mSBKwj7rFSev5REwPB0SZxUkUW7o3riwlk,881 +torch/include/ATen/ops/_softmax_backward_data_cpu_dispatch.h,sha256=bElt_gfrs4qXOP8a_fQJ2vrYFtcco7EU_uwwgp1AN_0,1164 +torch/include/ATen/ops/_softmax_backward_data_cuda_dispatch.h,sha256=qv7BKWg1r1FXzxI1ltZC4siFS9LyiZafQ9lnvAHrxYA,1166 +torch/include/ATen/ops/_softmax_backward_data_meta.h,sha256=10dnOrBQV4mp1YVmAOhpyIw3dqwicWOeVm0YM-yd_bU,658 +torch/include/ATen/ops/_softmax_backward_data_meta_dispatch.h,sha256=El7vZpGqS_wLRRTlcPi2juas9dtwWg-UeE3eBhCmWFY,1166 +torch/include/ATen/ops/_softmax_backward_data_native.h,sha256=0g9Sw_gz-jwbPU_80G-BcuBXKlLexQFYH9O0vcb2RP0,1124 +torch/include/ATen/ops/_softmax_backward_data_ops.h,sha256=XgYChzX0bHiBGJ361kddMlXrNjHWuQ90IJ8XICap4Yk,2164 +torch/include/ATen/ops/_softmax_compositeexplicitautogradnonfunctional_dispatch.h,sha256=svyT6QaGMAIAHozmiEuUso5WkiZPc4FTRse5XhmOqPA,825 +torch/include/ATen/ops/_softmax_cpu_dispatch.h,sha256=pmc7Nl8jx12s3tJH_yPd2ZxcCI_GDVgzFOzRVvOGw7k,982 +torch/include/ATen/ops/_softmax_cuda_dispatch.h,sha256=C-jhRtEUJPSJMeiOF0vP8O-Mx8bhnNiaN0ZEFILHwTA,984 +torch/include/ATen/ops/_softmax_meta.h,sha256=I9Jg0EQdDfvEZBQsZ3sZfDJEtQUImtD-HVuXrVgh-SA,602 +torch/include/ATen/ops/_softmax_meta_dispatch.h,sha256=JpHotH4q9vQmja9hgTRK7XskfqU2peQ5fD9qgtcv1qQ,984 +torch/include/ATen/ops/_softmax_native.h,sha256=rmJyjunC491jNBlnCaNfVnZ6s82lQgMzABJrCNvfQn0,1010 +torch/include/ATen/ops/_softmax_ops.h,sha256=DZ_DFdKSQbEtoXfJ07tha_dJ1ZTBsadOMpXcfJcj7ro,1779 +torch/include/ATen/ops/_sparse_addmm.h,sha256=ExNuiUYvhGcBqjj5u9lFU7VKNB_TLX3VV5cRZ9qDmQc,1657 +torch/include/ATen/ops/_sparse_addmm_compositeexplicitautograd_dispatch.h,sha256=QTGVUSqtiKwq3EVtbVY1kvPMYFYVCGez-gZW9UNsSG0,1253 +torch/include/ATen/ops/_sparse_addmm_native.h,sha256=g_6AwJqSUAgsq3D5IbH0GL7KXL_7tIo2ZVCexCQEu4w,774 +torch/include/ATen/ops/_sparse_addmm_ops.h,sha256=AG-E47WI2fa0XBDchI4e-Yq1pN3ez0cWDsogqZ2DoIw,2270 +torch/include/ATen/ops/_sparse_broadcast_to.h,sha256=61-SmvX-xhk-GQ_KJFG0pwRjZN_8KwMWoyPJveFDDSY,739 +torch/include/ATen/ops/_sparse_broadcast_to_copy.h,sha256=6AkK8Rsj7_3KpR0OwBthrwnKbTXBxq7PSfITDXE1nd4,1337 +torch/include/ATen/ops/_sparse_broadcast_to_copy_compositeexplicitautograd_dispatch.h,sha256=eDKmw089UZ0MxTEbTSAtd2bW_WIkcDgTiw7e_C3494E,949 +torch/include/ATen/ops/_sparse_broadcast_to_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=nTpWGiCH7KauFWBEVOW2YlzqFoh_qmG9LFcrWJnAEKw,831 +torch/include/ATen/ops/_sparse_broadcast_to_copy_native.h,sha256=14g-p5D0uFFh7NrSNUjgaQl7xRQWVz-j9qBV8RUX0J0,636 +torch/include/ATen/ops/_sparse_broadcast_to_copy_ops.h,sha256=GCMs1R5DE69QLB-gGvelwr5rBS0DfEEZYM825cegaJ8,1807 +torch/include/ATen/ops/_sparse_broadcast_to_native.h,sha256=7PVG4YXRvpH2SPVMEITqW4HZXyLBrP2eTaTVThPtBmg,511 +torch/include/ATen/ops/_sparse_broadcast_to_ops.h,sha256=T6L66wpUraWqsvEMnSh8YbHzfffRh-5akcGGJuBTc6k,1078 +torch/include/ATen/ops/_sparse_bsc_tensor_unsafe.h,sha256=WEfBv5VZ62zhy0OVU5ZHjFqYof7mN4_2eWDagyyBPTM,1780 +torch/include/ATen/ops/_sparse_bsc_tensor_unsafe_compositeimplicitautograd_dispatch.h,sha256=eXnrqplLcECr568OWH6mlTp_9dNFoBCW-pS9Ge-wJiw,1209 +torch/include/ATen/ops/_sparse_bsc_tensor_unsafe_native.h,sha256=HsWzMTV0Wb4LHT8CXLv8is9EhWDPxnbDlrpEgBfklnY,741 +torch/include/ATen/ops/_sparse_bsc_tensor_unsafe_ops.h,sha256=pSy2nhSHp8Sj9DiaP4_PLnhbF7d1XHC68Irxe_XZYZk,1800 +torch/include/ATen/ops/_sparse_bsr_tensor_unsafe.h,sha256=QsDcN4OKoj01ln7fnmenXLzPSMsvQKTJ9p2_6zxETU0,1780 +torch/include/ATen/ops/_sparse_bsr_tensor_unsafe_compositeimplicitautograd_dispatch.h,sha256=iGzRhza_-BuNSbVWZIqiYkBh-OO7_DfSIagrwvPblls,1209 +torch/include/ATen/ops/_sparse_bsr_tensor_unsafe_native.h,sha256=hx8-aHty4HGxmjGxwl3DYn3XdST2mS_Vl0TCklIynhE,741 +torch/include/ATen/ops/_sparse_bsr_tensor_unsafe_ops.h,sha256=nsXpRk3g0SMFgp8H_vZIRmNBIp_sWnlspLKKjnH2dfU,1800 +torch/include/ATen/ops/_sparse_compressed_tensor_unsafe.h,sha256=ImtESbJdJRURiK0WB1Y9czNE5tV8aUwP1HuInmwsqDs,5594 +torch/include/ATen/ops/_sparse_compressed_tensor_unsafe_compositeimplicitautograd_dispatch.h,sha256=5wiGdlCob4zxSm5Q-2Pnk26CypgYSvO53ciCJaxpxJg,1790 +torch/include/ATen/ops/_sparse_compressed_tensor_unsafe_native.h,sha256=0jQymVmnl9GOQ7JU3pb_sx_HyTkQTFxEHLc-IW4bT58,767 +torch/include/ATen/ops/_sparse_compressed_tensor_unsafe_ops.h,sha256=Dota9eeXQkjWOcdmQJZFwAdbQnYuU8F4puO7oJ-S00g,1860 +torch/include/ATen/ops/_sparse_compressed_tensor_with_dims.h,sha256=pAb21X7rm7XNzpgGJwRynwogbUfNPd107KWBaE5IQZ0,1867 +torch/include/ATen/ops/_sparse_compressed_tensor_with_dims_compositeexplicitautograd_dispatch.h,sha256=dFIwgdVguFXDBnL_Ip2tirWbthKztdwbMW4oPqAUznk,1216 +torch/include/ATen/ops/_sparse_compressed_tensor_with_dims_native.h,sha256=W00Fch00eKVt1oxTqbZVi8lbeimeGv-fZdPXxWK1nq8,745 +torch/include/ATen/ops/_sparse_compressed_tensor_with_dims_ops.h,sha256=Da_ZHxsxssyy6ebOQxpzvbh43Yc6E-oxY7wn4ZbX8E4,1821 +torch/include/ATen/ops/_sparse_coo_tensor_unsafe.h,sha256=yPujJ1mIe6yXo6g8U8JutF7224xRxIbVSvKPC2EE1ps,5307 +torch/include/ATen/ops/_sparse_coo_tensor_unsafe_compositeimplicitautograd_dispatch.h,sha256=zMolWRDUy85Vl49F8tyrwAbZemucJtG23PxsD56dZCY,1756 +torch/include/ATen/ops/_sparse_coo_tensor_unsafe_native.h,sha256=UWL3pIz3EhXIa7ks1jpzqJzwDyE7-fy_xOjQ0iIrORo,766 +torch/include/ATen/ops/_sparse_coo_tensor_unsafe_ops.h,sha256=h5TD2A27ujXhaBmPJS3d_encjq1cWun0LL_DBG-8Qzs,1816 +torch/include/ATen/ops/_sparse_coo_tensor_with_dims.h,sha256=Z4QyHGvx4fNNWNDuUlm_tmNmaudhYji2nGDy9Z9IBwM,2320 +torch/include/ATen/ops/_sparse_coo_tensor_with_dims_and_tensors.h,sha256=TpmiplYs3khNaS4jPtS5GJaxTkMV6PivyHV59KF8emg,10612 +torch/include/ATen/ops/_sparse_coo_tensor_with_dims_and_tensors_compositeexplicitautograd_dispatch.h,sha256=_mreM1o9QCqyy4e1c2j3_MpHVb6pCjXQ4K0bTNrKTzg,1720 +torch/include/ATen/ops/_sparse_coo_tensor_with_dims_and_tensors_meta_dispatch.h,sha256=STtjhqkpTsfIkmLchc6fFTdOZO3VQV3J5zLdsSjjPI0,1924 +torch/include/ATen/ops/_sparse_coo_tensor_with_dims_and_tensors_native.h,sha256=XjxVmr5rBX0DBfPHnI3lHldZjdPm2k2QOp_i4-IEnho,1061 +torch/include/ATen/ops/_sparse_coo_tensor_with_dims_and_tensors_ops.h,sha256=lNuuoGa1P40ejqKR_K8_6dnJcDSPcYQhQVFjfiiyG1c,3114 +torch/include/ATen/ops/_sparse_coo_tensor_with_dims_compositeexplicitautograd_dispatch.h,sha256=3_qpd3dRzrUGpaNZVexCFanmNev_Jt5MHsTFkOIbGPc,983 +torch/include/ATen/ops/_sparse_coo_tensor_with_dims_meta_dispatch.h,sha256=WqRjAa2k3kaVl4d6vPReHoF-_BQk2VmHK18iWv1_DAQ,1064 +torch/include/ATen/ops/_sparse_coo_tensor_with_dims_native.h,sha256=laHnbzabxmDveHHLF_EY5XvRg9wx4Ao2Mt2ACESzAGM,819 +torch/include/ATen/ops/_sparse_coo_tensor_with_dims_ops.h,sha256=LlME3ng57dmipzAtkHd74IAiVqScWYeGbE23fk2Z1Mc,2410 +torch/include/ATen/ops/_sparse_csc_tensor_unsafe.h,sha256=Ivdkb-GFWNZs6YBAO6YsgA_Wgn3g5qkBj6Mpz5gC3Ck,1780 +torch/include/ATen/ops/_sparse_csc_tensor_unsafe_compositeimplicitautograd_dispatch.h,sha256=qjt-_PnhHbHELtzrxNsBe7ausDJTVRKtCZQZ1AjLexk,1209 +torch/include/ATen/ops/_sparse_csc_tensor_unsafe_native.h,sha256=ALyl3JpspNdmR7rE9uwmvnlhQGDmRexIux5TQ4TYTQY,741 +torch/include/ATen/ops/_sparse_csc_tensor_unsafe_ops.h,sha256=8d-XbNfPjPlLRlAE5L7O3NwdSkd9e6HfHHe7FUsyttc,1800 +torch/include/ATen/ops/_sparse_csr_prod.h,sha256=ovdjdbP7N_UZae_0glA5H4Qx5TpZMsgvqipC_-xVDLo,1685 +torch/include/ATen/ops/_sparse_csr_prod_compositeexplicitautograd_dispatch.h,sha256=I-EBXSGOBvJIv4m-7Z1zu7G1ZKjCBTnLtc144padtDI,1056 +torch/include/ATen/ops/_sparse_csr_prod_native.h,sha256=aLlX_wUWch3P7iUkQF7GmkBnD0YaGKAdqQTY8u6YL7A,921 +torch/include/ATen/ops/_sparse_csr_prod_ops.h,sha256=YiaQ1giRb6o417xyGtdj7_rTcksVE3mM3p8PWhCraEs,2189 +torch/include/ATen/ops/_sparse_csr_sum.h,sha256=k8jQDpP9zmbIiuGSP8Cg4qjo8tIqVLFyE-IDoNGUNy4,1675 +torch/include/ATen/ops/_sparse_csr_sum_compositeexplicitautograd_dispatch.h,sha256=moUOACPsrkd1KK1YK4d5h7D_cEMV4LPSAADoV4OHezA,1054 +torch/include/ATen/ops/_sparse_csr_sum_native.h,sha256=qFUNnqmD6aOuyEJKKD3keIJO1MTmhL0GBed386w_4PE,918 +torch/include/ATen/ops/_sparse_csr_sum_ops.h,sha256=MudpV35YS2k0j--8QRF4ZBHdssi7f13xlweVb5oAuzI,2183 +torch/include/ATen/ops/_sparse_csr_tensor_unsafe.h,sha256=B8uqk2HqbMy8m0FqLNOgiqTKQ-uVoCFtvgRn5eQbIFw,1780 +torch/include/ATen/ops/_sparse_csr_tensor_unsafe_compositeimplicitautograd_dispatch.h,sha256=ooE0BK5M2RUnj3kwbMycqG7wzLDtZUE46Gq1jJ5U6vo,1209 +torch/include/ATen/ops/_sparse_csr_tensor_unsafe_native.h,sha256=yqwu3EdE0s-hK4RWEyOw727oI5K9a9B013el16rt5xQ,741 +torch/include/ATen/ops/_sparse_csr_tensor_unsafe_ops.h,sha256=tBuObCJgr2QB_OVWN2FsAFH_aWoRa4R1cUOfzdjJgm4,1800 +torch/include/ATen/ops/_sparse_log_softmax.h,sha256=gSzX0coVb2bdgVFdBKIheuQQIm7ancsPT482CXjCXI0,2010 +torch/include/ATen/ops/_sparse_log_softmax_backward_data.h,sha256=f-KzEecAYszS11jbS5vaU-bdwfzs94EjEsVzIJ2dC10,1723 +torch/include/ATen/ops/_sparse_log_softmax_backward_data_compositeexplicitautograd_dispatch.h,sha256=MSFjlT3B14wEGoxznxPm5tgApZYCwmgXuIG-xfn0izg,1065 +torch/include/ATen/ops/_sparse_log_softmax_backward_data_native.h,sha256=ngckj-dgjBJMBldp8O9T6tLq0bp4xN0boeW4rBaaSkY,902 +torch/include/ATen/ops/_sparse_log_softmax_backward_data_ops.h,sha256=MoJr-ZW7XDYcyc1yZAaDlyDO_1PnR-EwT2xUMmhIjvo,2183 +torch/include/ATen/ops/_sparse_log_softmax_compositeexplicitautograd_dispatch.h,sha256=hPFxbead6hMjkXtDX9CahezrpBKolM8DcwDA6VRh-tU,959 +torch/include/ATen/ops/_sparse_log_softmax_compositeimplicitautograd_dispatch.h,sha256=BZC_zTYchTJuPMmPdR50_qCMzGBf4cvOcAn7ETeawK8,982 +torch/include/ATen/ops/_sparse_log_softmax_native.h,sha256=yAe6u2N3QQlBh_knrN-3S6_gRmuOo9d0OKjAbMblsR4,1025 +torch/include/ATen/ops/_sparse_log_softmax_ops.h,sha256=O6GQlmQYVUPHX78Lx6kEDjRPrfX5t1sZhCWQ4DIqHi4,3334 +torch/include/ATen/ops/_sparse_mask_projection.h,sha256=RcTYdTkPG0q4MgRWbrJrChJaeNUslr8fZ6bKLarZhW0,1257 +torch/include/ATen/ops/_sparse_mask_projection_compositeexplicitautograd_dispatch.h,sha256=2WJL30FoTJjCjZgIv9ZCRVirrPSeGmHg5GhHokJh0Cs,1007 +torch/include/ATen/ops/_sparse_mask_projection_native.h,sha256=rIhal0x-2O3u5SGUp_csXpYI3OIMRAmwG7Z-LzXMfTc,693 +torch/include/ATen/ops/_sparse_mask_projection_ops.h,sha256=ErIN3lvXMWxwQgrY7IGNJAXtAyWXCGLxV16GGeInQu0,1989 +torch/include/ATen/ops/_sparse_mm.h,sha256=B-a013hRdf2fcNQl-SfL03DXpOs_2C6Zq4jwB4wiMnk,965 +torch/include/ATen/ops/_sparse_mm_compositeimplicitautograd_dispatch.h,sha256=5j2KIVy9W-sE7_AlMZaUqu9_bCfhRe5cAU_yq82DNoY,907 +torch/include/ATen/ops/_sparse_mm_native.h,sha256=jzJJn_oC9KyLBR1cmF3Ix4PpWlTBlrBNhlx7XcrgB1s,619 +torch/include/ATen/ops/_sparse_mm_ops.h,sha256=qdWDxEO_jLyDzyTRw5E4V1F2W3QarAeawye1ujE58l8,1765 +torch/include/ATen/ops/_sparse_mm_reduce_impl.h,sha256=dKIILZWLYShwpKdUW8cacNRtz8vyXAmrrF9f1ZtxEn0,828 +torch/include/ATen/ops/_sparse_mm_reduce_impl_backward.h,sha256=oXUuzLsogQQx_aUd5lD6GrEb8NgwFkwk3sV45DdgfRo,1044 +torch/include/ATen/ops/_sparse_mm_reduce_impl_backward_native.h,sha256=s-luHuDfdolpjeq35ZBmORssLdTPRMOjqKXfuN5Rdsg,684 +torch/include/ATen/ops/_sparse_mm_reduce_impl_backward_ops.h,sha256=45sZQVdcTCSZSJtS2jhJkgweiPwA9ul15euRJ8btvaE,1584 +torch/include/ATen/ops/_sparse_mm_reduce_impl_native.h,sha256=oxMRDp1D3fDv-HE5AkMJ2fcYLX7XiIC0ejoojd-XNEU,583 +torch/include/ATen/ops/_sparse_mm_reduce_impl_ops.h,sha256=6ZkRZoaovkfTIFn1t4zROsvQcdXLrPnsxJbcXXQjiZ0,1256 +torch/include/ATen/ops/_sparse_semi_structured_addmm.h,sha256=MsUX2yyH6QpnOQZvjIz8FXp8bABcYsssf0c9y_XR_P4,1078 +torch/include/ATen/ops/_sparse_semi_structured_addmm_cuda_dispatch.h,sha256=yfgeOlwtwVa6t6B4ZUF8LDm5WUJ9lCq30QYR0qw1oCA,939 +torch/include/ATen/ops/_sparse_semi_structured_addmm_native.h,sha256=82yftHzOTpwfxPE2s4RR3RbNbQcBw2GgyhmmUKZWGV8,693 +torch/include/ATen/ops/_sparse_semi_structured_addmm_ops.h,sha256=4o7Ue3nCCfWcWyiVVlK96iSK7c-vaBHQvLV6pnGzXA0,1616 +torch/include/ATen/ops/_sparse_semi_structured_apply.h,sha256=7bhbrmDKG04XSLaC0XpzRd4yXauzKgctuuUV8Ck8KXY,835 +torch/include/ATen/ops/_sparse_semi_structured_apply_cuda_dispatch.h,sha256=9N8eXvAAbuHB-OKGeCIIu9d3actuwXF9K9E9N72SWqQ,804 +torch/include/ATen/ops/_sparse_semi_structured_apply_dense.h,sha256=8wdjpDchzIToPGRoJHCprfymGK6n3FeVJz96_4Y5KRo,824 +torch/include/ATen/ops/_sparse_semi_structured_apply_dense_cuda_dispatch.h,sha256=QCLgDQlaTESjRUYxB2tAYC6ILBAhHL34SGKJdDYeqmo,785 +torch/include/ATen/ops/_sparse_semi_structured_apply_dense_native.h,sha256=HqSu3lG7MY7lbcT2neexrgzOGfqIKNRR7_MM-NUAzpk,539 +torch/include/ATen/ops/_sparse_semi_structured_apply_dense_ops.h,sha256=DWnRKsa-TU2mXs-EX4GlOE-5DtKEgX1k88-8Y8Hm-Hw,1154 +torch/include/ATen/ops/_sparse_semi_structured_apply_native.h,sha256=93RFs97FX5gFXytRN2CdQUYTIkvPmIviRXLswb4iyRI,558 +torch/include/ATen/ops/_sparse_semi_structured_apply_ops.h,sha256=uHc8gCNHnZmP1ygxLItxRzAq_HMICqdOtnuZTBTIEgY,1221 +torch/include/ATen/ops/_sparse_semi_structured_linear.h,sha256=Rre7yLXity-tdop4UeQMw0wmwfBfCd7RYMb3IvUp_lE,1095 +torch/include/ATen/ops/_sparse_semi_structured_linear_cuda_dispatch.h,sha256=6pn1Jc1_lG9dy20VmMeTqbQ4VppJO1i4nxm4mx-h8B8,963 +torch/include/ATen/ops/_sparse_semi_structured_linear_native.h,sha256=kcngFTu5kX5icSIjydZ4m6PqXN0WVnwj24lS2Cn_eDA,717 +torch/include/ATen/ops/_sparse_semi_structured_linear_ops.h,sha256=SjJ_2n3NwM8RKh-JcqDwpvczyHe2xKkl4GY0MF4p2dA,1643 +torch/include/ATen/ops/_sparse_semi_structured_mm.h,sha256=F9pa6v-ghYefMX6_X3lJnG9-2GxudE-XqPjRAypaHgM,920 +torch/include/ATen/ops/_sparse_semi_structured_mm_cuda_dispatch.h,sha256=uEd6LmDafSXsop86lHP49xMqVX7R4GwLr3vlsZxh59s,855 +torch/include/ATen/ops/_sparse_semi_structured_mm_native.h,sha256=iL0WJtr_sDSYey0BQF3PgCJJXIroIGonPD_Gfd1cAGo,609 +torch/include/ATen/ops/_sparse_semi_structured_mm_ops.h,sha256=alldgs9e6jsQlijtD0JvqdqPy4RLtVLrng0A_2L6FYE,1348 +torch/include/ATen/ops/_sparse_semi_structured_tile.h,sha256=X80qEwhmAIr3jq2vsTM_mbhQbAIeogk1mrjAbBPrd3A,939 +torch/include/ATen/ops/_sparse_semi_structured_tile_cuda_dispatch.h,sha256=LvNes56locG24C32tN2-k0GeUouxhOypBlHzx6G2oa8,857 +torch/include/ATen/ops/_sparse_semi_structured_tile_native.h,sha256=EZhkqb08iX9eDYPnVjVEx0OsvPXC8586NqGOJ_l6Zk8,611 +torch/include/ATen/ops/_sparse_semi_structured_tile_ops.h,sha256=9413KmHTXDTS5vCybux4L4UEFFZGOwQfT2HX1lN8cus,1393 +torch/include/ATen/ops/_sparse_softmax.h,sha256=At5zZbSnWkm_rszsHwKw6f_NO3Pl8JXsAhj1AZTSBQs,1946 +torch/include/ATen/ops/_sparse_softmax_backward_data.h,sha256=Z6BphYJNi8aTtT7XcBn2GgM2Drqa5XvNpQ0N7vgWvzY,1683 +torch/include/ATen/ops/_sparse_softmax_backward_data_compositeexplicitautograd_dispatch.h,sha256=pK9L-SCFibpejshNAes-ep8Gg7lEFD4YuO9vY66Fcc4,1057 +torch/include/ATen/ops/_sparse_softmax_backward_data_native.h,sha256=vekJYLeUuNcyUsunkKP_kFGG7gnJ9MTipef6a9qR06M,890 +torch/include/ATen/ops/_sparse_softmax_backward_data_ops.h,sha256=wu0V3OO7sRdtAdjmtCU_SMhatz6afO_jDcOBwXxz46g,2159 +torch/include/ATen/ops/_sparse_softmax_compositeexplicitautograd_dispatch.h,sha256=CAI5KiocSavdh38pDa8PD37YZwBRx5NnL_jj3RZ8hg0,951 +torch/include/ATen/ops/_sparse_softmax_compositeimplicitautograd_dispatch.h,sha256=aewpMWURX5ZylN4hbtiyZf0HbEfVp8xPI-hLxOwlfXY,974 +torch/include/ATen/ops/_sparse_softmax_native.h,sha256=AYz-qZHr7EmJdvhMUwQdK_CMWUo2GrmNGaFbo3N7TSE,1005 +torch/include/ATen/ops/_sparse_softmax_ops.h,sha256=BuuBMRfd9_9urKp9E69ptlka5XQFY3hKc7JrpByt33I,3286 +torch/include/ATen/ops/_sparse_sparse_matmul.h,sha256=ObeIj87Tehqejdw17hWz8y_P9zxm_J2-kxpUzc1-xWY,1318 +torch/include/ATen/ops/_sparse_sparse_matmul_compositeexplicitautograd_dispatch.h,sha256=BG_mISTQJ1_3dGvtKunqxjIm0PY17tRazzT67aBNEgg,949 +torch/include/ATen/ops/_sparse_sparse_matmul_native.h,sha256=XrLYtiicyaE58VAMNEszgBPLIQ5Gruu4SVYITEAiDJI,738 +torch/include/ATen/ops/_sparse_sparse_matmul_ops.h,sha256=REGM-FbCJjymfBKir89A95ZKHc_jGHDegfKcvkQGcXQ,1809 +torch/include/ATen/ops/_sparse_sum.h,sha256=fQb9Uf9e7kYS228VQssXw8_73DCl_7V9RAdxRekGWcg,1835 +torch/include/ATen/ops/_sparse_sum_backward.h,sha256=wqJzr8gv7HqmvOaDu1d2QrJvIB7Jn5JcHwO4uCP0MeI,1410 +torch/include/ATen/ops/_sparse_sum_backward_compositeexplicitautograd_dispatch.h,sha256=-ZHZZGrACRdFt9RXETr9wPG7c2nGthaffhhpiQ8oQlk,987 +torch/include/ATen/ops/_sparse_sum_backward_native.h,sha256=AocOLb2vBwLL9DgIhTRIggjWjKMCmHr6nZBl_knDCuc,797 +torch/include/ATen/ops/_sparse_sum_backward_ops.h,sha256=4972-LBlHN9IIdIwIk8O7FVQhBRal1cyIEoGhcuAmEQ,1937 +torch/include/ATen/ops/_sparse_sum_compositeexplicitautograd_dispatch.h,sha256=-uMzTNOdApGGoqSAxhIpPcbPY0jm2I0eBG4w6JtzQf0,999 +torch/include/ATen/ops/_sparse_sum_compositeimplicitautograd_dispatch.h,sha256=hxbc6Mn0RoBi7lPxx-DicjlKRXz7dALEIkH9oeWzYEo,952 +torch/include/ATen/ops/_sparse_sum_native.h,sha256=5jit57UVkhup7OVIMvhAToGhrAafIobORmA3Urlajko,852 +torch/include/ATen/ops/_sparse_sum_ops.h,sha256=0FRgROZUHyJq2dgFwyF_qw4Otf6ACbMPTqWMTq5LkW4,3566 +torch/include/ATen/ops/_spdiags.h,sha256=I0SHAN06RKhxMG4PvWoTCwvPiFh8jLEspnuw7NgThKQ,1605 +torch/include/ATen/ops/_spdiags_compositeexplicitautograd_dispatch.h,sha256=7Rflpmnf4HZ1ZYr30DmKXyG7fc3JHv-22GoYg6EnVzk,1070 +torch/include/ATen/ops/_spdiags_cpu_dispatch.h,sha256=PbUFKZ6z7o5JMVi57kq8AcJyJj3oHaND98NlOLYUUNs,829 +torch/include/ATen/ops/_spdiags_native.h,sha256=zg_Gh9M57znJeCPpX-fE7pDLaBRH4TYtz84ED9DA3S4,756 +torch/include/ATen/ops/_spdiags_ops.h,sha256=dDfntn_57KKdvzwXvPo0UvQT44h7D2_Y4UVc5UJ8ETM,2169 +torch/include/ATen/ops/_spsolve.h,sha256=N6hsTJ8Kpdnh4cxDd6GdOcxijl_mjj_KIulRhBqS7Po,712 +torch/include/ATen/ops/_spsolve_native.h,sha256=DMecf5sGDt9h_CSwHHTY0Y2Rw_5XIwAgDGu1jGwJrns,529 +torch/include/ATen/ops/_spsolve_ops.h,sha256=SnT4cTmkoJ3k63pCxX5h8tctflQJFmNIcuENJvNGp9M,1075 +torch/include/ATen/ops/_stack.h,sha256=F1xPY_bcr9p966YhpIJQSOq4KX_R5zcED5krkdoTY28,1139 +torch/include/ATen/ops/_stack_compositeexplicitautograd_dispatch.h,sha256=XPmvbCkXjicSsxv5hT7byCXYEVLWLbP-GpbR8sd483o,961 +torch/include/ATen/ops/_stack_cpu_dispatch.h,sha256=gyMVPfnMJFNwIesKmE9NCvaWnGf0G671tK4rfjbUgQ0,917 +torch/include/ATen/ops/_stack_native.h,sha256=2haJpjEPorP9tzUwRyUJjV7kO6r-byZKoNzoKeise9Q,746 +torch/include/ATen/ops/_stack_ops.h,sha256=EwTIco47AzODwFSu1yyAO4jYWgUHULuSkPS6mfplc0U,1637 +torch/include/ATen/ops/_standard_gamma.h,sha256=FDdl8TDYCsjHwHA8Vg2JTjf80e4kTOpjkHILnmJPJs0,1387 +torch/include/ATen/ops/_standard_gamma_compositeexplicitautograd_dispatch.h,sha256=9OJH8Di-WTnXQOft-tbnUThpyj81XBA_0EsYCEdIM9U,984 +torch/include/ATen/ops/_standard_gamma_cpu_dispatch.h,sha256=OjEKytZd5zc_742IRJz83jz04fXOwyzDxBGcNFEok_8,786 +torch/include/ATen/ops/_standard_gamma_cuda_dispatch.h,sha256=MZc6fPC5Lsk_hBg51WMqXTC466G7yKzS65qngyIKFzY,788 +torch/include/ATen/ops/_standard_gamma_grad.h,sha256=pZPa3Mk0FQUfaeqcwQkwMbE6Z_Ub2ub4syqMArpAMPY,1317 +torch/include/ATen/ops/_standard_gamma_grad_compositeexplicitautograd_dispatch.h,sha256=2xR6UnXRYLzFRExycBln4i7-yD52j-TPLmLrCmAjy9Y,949 +torch/include/ATen/ops/_standard_gamma_grad_cpu_dispatch.h,sha256=xsLiDQmTUeVhfMZKQaEHdUcSo_cZ1frj5GG2nJOOfmc,761 +torch/include/ATen/ops/_standard_gamma_grad_cuda_dispatch.h,sha256=c39AgyrLB-xOC2-4VsfkvmKeVupoapqZ0sB5ydlR8fo,763 +torch/include/ATen/ops/_standard_gamma_grad_native.h,sha256=mkKucfTVHOqUgo1PHIjdFoXx1uLfESDHnLxPWP9ciFU,740 +torch/include/ATen/ops/_standard_gamma_grad_ops.h,sha256=1fBU7Rh1zHt8UMzUIa8BuhQhrgGlW1z-nTajHWGIkL8,1809 +torch/include/ATen/ops/_standard_gamma_native.h,sha256=GdE8XkYlMmumIObiAxawAjl34bwBE5T4FhE4dyv1Wcw,786 +torch/include/ATen/ops/_standard_gamma_ops.h,sha256=aqom-II-Juvz8x7eESPTzREAUgd4pTVTAVeZnGitYz4,1887 +torch/include/ATen/ops/_test_ambiguous_defaults.h,sha256=OguBlsvXvoiOSErZwGm8vRFBIW9joQy0kmwags-S8nI,1019 +torch/include/ATen/ops/_test_ambiguous_defaults_compositeimplicitautograd_dispatch.h,sha256=47byKGQMPtyd3SUg4xtKqkffe-SqIc6D4dvhSJpYRcg,913 +torch/include/ATen/ops/_test_ambiguous_defaults_native.h,sha256=G8RggWiTAtIuHa6LmLPrC1Lx1uzPLo6nQ2qZINKB0Mo,631 +torch/include/ATen/ops/_test_ambiguous_defaults_ops.h,sha256=u7QQ5brORwC-tGcn1G-dvW4NGLkotxS9XTZ1FlUSmOY,1772 +torch/include/ATen/ops/_test_autograd_multiple_dispatch.h,sha256=Jn2C36EJvP5IldroGrcYf85gto_KJBcB2MyCnY4B508,1611 +torch/include/ATen/ops/_test_autograd_multiple_dispatch_compositeexplicitautograd_dispatch.h,sha256=_4SgReFkok3NlUaXE70PKKcCMwOBlAdOgs9Ok9qjmvY,999 +torch/include/ATen/ops/_test_autograd_multiple_dispatch_compositeimplicitautograd_dispatch.h,sha256=u1fU6MDlxVnjGEaMWEud_5l8DC86P_8IfkqetI0loQM,798 +torch/include/ATen/ops/_test_autograd_multiple_dispatch_native.h,sha256=KY_U0z1UDcFiCcSj55Ljc-ut847uosiI3EqHYK71mPA,727 +torch/include/ATen/ops/_test_autograd_multiple_dispatch_ops.h,sha256=wfN_wGRxmv8CgOsMenTUqrKuP4N8USjekcmNXqEDRrc,2408 +torch/include/ATen/ops/_test_autograd_multiple_dispatch_view.h,sha256=jVUQLW_8f7WeXjHZvS-GlEQZqu2NqST_kwMoinzboqI,767 +torch/include/ATen/ops/_test_autograd_multiple_dispatch_view_compositeexplicitautograd_dispatch.h,sha256=ShG32Y8O22F1mikUvW-HKUu4BQ2fvcltU6jT-TJl-K4,795 +torch/include/ATen/ops/_test_autograd_multiple_dispatch_view_copy.h,sha256=WOlgweOyPbTtKBi_HAP6e5DqFxVvvVxyx3LJE34x-BI,1387 +torch/include/ATen/ops/_test_autograd_multiple_dispatch_view_copy_compositeexplicitautograd_dispatch.h,sha256=0B3WYKdaoOO9F0gmsprXOafJu58zDJDtSlhQGZbPM-w,939 +torch/include/ATen/ops/_test_autograd_multiple_dispatch_view_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=9_LCvXLK5y8zBux6-buoGdOjW4SkXVTI0_j4CPZQLXE,826 +torch/include/ATen/ops/_test_autograd_multiple_dispatch_view_copy_native.h,sha256=NRCGaF9VqWe-ytCmYU0jmqRuehyKW8I3c57TzjTgD9I,626 +torch/include/ATen/ops/_test_autograd_multiple_dispatch_view_copy_ops.h,sha256=vcJdkFEd7VcsmqXdRQVnhMOnlTh8FtbysNOCmMb1_o8,1763 +torch/include/ATen/ops/_test_autograd_multiple_dispatch_view_native.h,sha256=faXJkTR3nGFa8sWHrgvt7Sw6cor0Ny0EA3KB8B8PUjQ,507 +torch/include/ATen/ops/_test_autograd_multiple_dispatch_view_ops.h,sha256=q2N0nlIi8PpFt42P5ZfgPunUK_lcOHmGByza-tPXydg,1056 +torch/include/ATen/ops/_test_check_tensor.h,sha256=s7mGCMIzgCRTHBNkMfDrbUaZOQkdUoaU1KbUEltyZbk,685 +torch/include/ATen/ops/_test_check_tensor_compositeimplicitautograd_dispatch.h,sha256=E8I0_VWxQ1AAjGOdFIvKJx1V_NNhSOo1ur_jfI3J2_A,776 +torch/include/ATen/ops/_test_check_tensor_native.h,sha256=FZdKhy0cD9P4AcWD7Ri5HuAsPrXV8-PAiz6eKq4Jk8E,488 +torch/include/ATen/ops/_test_check_tensor_ops.h,sha256=2VX5bQxQa37fNp-5s66L6C8Ch_HkkZd7-twHMMyjgLA,993 +torch/include/ATen/ops/_test_functorch_fallback.h,sha256=UQ6CAinQpRCvyL4BnPuKjDfnY-MOQP8m80LlxW4iluw,1348 +torch/include/ATen/ops/_test_functorch_fallback_compositeexplicitautograd_dispatch.h,sha256=92mowvVjVXdV8L3vPlGYdJFOqcBgqNfVEeYWzjzlo-8,955 +torch/include/ATen/ops/_test_functorch_fallback_cpu_dispatch.h,sha256=KtazLBaKD5akwda0xUfVXVTjrraI7P0jqykNg1ySnG8,764 +torch/include/ATen/ops/_test_functorch_fallback_native.h,sha256=8GTojP0hZEmmKUnT_aIAR5zr9PZWh9WMDaacF9XbCyQ,642 +torch/include/ATen/ops/_test_functorch_fallback_ops.h,sha256=ZJPQhUILsadzlK6f9Vvt2iChD9o6cstlJiWRflMIwgM,1827 +torch/include/ATen/ops/_test_optional_filled_intlist.h,sha256=F2NMeeMC1x35HUjYrOkDLS6Ixq2iOiavYBuMT-DCUnk,1452 +torch/include/ATen/ops/_test_optional_filled_intlist_compositeexplicitautograd_dispatch.h,sha256=qr8tdDam2XUh6D4Mz-H7zK-qhweREUt5EnnfhJbTh1E,983 +torch/include/ATen/ops/_test_optional_filled_intlist_cpu_dispatch.h,sha256=273YyCcDdwyN39oHF22kHPOrhb5Z_M3hoex2VkXtBRA,778 +torch/include/ATen/ops/_test_optional_filled_intlist_native.h,sha256=1CC-1J13yvzQdSvBk0othPKQRR6T7Tc9nmA-wxPFXkM,663 +torch/include/ATen/ops/_test_optional_filled_intlist_ops.h,sha256=iZxS25S39O25uIhk-YBZb0vm2RYDVj_0Fa5QH512ckg,1913 +torch/include/ATen/ops/_test_optional_floatlist.h,sha256=U_6oVWeOGc53M9ikEyraLBbV_QMMAIA3l3o0YurlN5A,1447 +torch/include/ATen/ops/_test_optional_floatlist_compositeexplicitautograd_dispatch.h,sha256=kmBhJ59q09URze4QEx9_MbLiru6Ypy0LhW2epM8Aq4g,1001 +torch/include/ATen/ops/_test_optional_floatlist_cpu_dispatch.h,sha256=q4W_UftuacE2giDiICfAZKt6rsvXLcSVMRj9nVPAt6U,787 +torch/include/ATen/ops/_test_optional_floatlist_native.h,sha256=7YYpDoEFhYq9k1C9b4_cLy1S9Bdr8ZW3yOj97ndQF_I,688 +torch/include/ATen/ops/_test_optional_floatlist_ops.h,sha256=e1cq3AL_sRM7wpjtd2GO_i5PvM2EGPBifuI7vuDWJeY,1969 +torch/include/ATen/ops/_test_optional_intlist.h,sha256=tsR2RQnex3kG1Xk2MOKtqdcC4x3NTJQx1S2sctnQf1g,1379 +torch/include/ATen/ops/_test_optional_intlist_compositeexplicitautograd_dispatch.h,sha256=eVJi6ab4DkHA1hpRYMU20_i-t6JWl5x4Gwd_3Qlem5c,969 +torch/include/ATen/ops/_test_optional_intlist_cpu_dispatch.h,sha256=4fatPAnfN2rAQBlteXcNtAfobxwhvOhbAGKYnLA6mTk,771 +torch/include/ATen/ops/_test_optional_intlist_native.h,sha256=dJskxa7ELJnLxfjjBNJtYQDhdJ6myKFBGu7LoOn5L0c,656 +torch/include/ATen/ops/_test_optional_intlist_ops.h,sha256=3hn5NoxhUOs0izbramuWfY9IxG2U8iLIUJ1M6vXz9JA,1869 +torch/include/ATen/ops/_test_parallel_materialize.h,sha256=1j0FNpBRh3TIWripOJC66xK_JTDDnbNXvMFMsLF1mZo,829 +torch/include/ATen/ops/_test_parallel_materialize_compositeexplicitautograd_dispatch.h,sha256=XE4Hk9a9vPYma_3WtJeaiWF7KjPC5_ZSYAl8ap08b3c,829 +torch/include/ATen/ops/_test_parallel_materialize_native.h,sha256=aByqHFEr_ej7oorX9TRWbrUpkj1zSy0pS9ooZNg9k4Y,541 +torch/include/ATen/ops/_test_parallel_materialize_ops.h,sha256=knKKMx5yGtcFU3tFq-4v7SUtHU5a7WOyvemdsrgdQmE,1151 +torch/include/ATen/ops/_test_serialization_subcmul.h,sha256=AghNWIQxfcjlfziAcH6_UjKAlN5hL7azXqb6dLnnuu0,819 +torch/include/ATen/ops/_test_serialization_subcmul_compositeimplicitautograd_dispatch.h,sha256=Wp0aj5ZRbbyZWS8wtq5talYpwhixe-oonjXzH1etaos,839 +torch/include/ATen/ops/_test_serialization_subcmul_native.h,sha256=mXdkiCSVbgfNj9WjVKyzb7OSV5pZtM5-I34YKLhq8fc,551 +torch/include/ATen/ops/_test_serialization_subcmul_ops.h,sha256=g9i15F8WHgfoQD4aKPwJv2Xearvr2sdlt5ZNz4oVXhY,1194 +torch/include/ATen/ops/_test_string_default.h,sha256=TBM7VhFC4YDZYYSUqpzK6pi_mZih_e5Dh9JARlphdjM,788 +torch/include/ATen/ops/_test_string_default_compositeimplicitautograd_dispatch.h,sha256=ykuGwGkSaYvf8c3IFy7ShiYsFUpHg-0gU39GV9vurFc,835 +torch/include/ATen/ops/_test_string_default_native.h,sha256=H7PAeBn9l_ruo-rHRHh3gX-jZueNASft3IxfAYhQrQc,547 +torch/include/ATen/ops/_test_string_default_ops.h,sha256=Q8VSFKAITaY6ZZ2JjVvjLC40JAcuzHZCR60s9mCzn_M,1158 +torch/include/ATen/ops/_test_warn_in_autograd.h,sha256=2QcCVS8Mnd1Qdn96naaXbwSWn44qmT6ZvxWIsOyvYSQ,1187 +torch/include/ATen/ops/_test_warn_in_autograd_compositeexplicitautograd_dispatch.h,sha256=tTOb7ZwkeT68ED7U4iQ36i5JV8nc5oae7fZyjgpSvMY,969 +torch/include/ATen/ops/_test_warn_in_autograd_native.h,sha256=aEbLX58hGF0yNaUEWCub5zcGt_c61uqCjUPaeRc-Pkg,586 +torch/include/ATen/ops/_test_warn_in_autograd_ops.h,sha256=RFh659h4TdMkksbAPpddQK6OLj871kk_AxQlM9iFcec,1643 +torch/include/ATen/ops/_thnn_differentiable_gru_cell_backward.h,sha256=3TNnvO0tTmk-t95MxuEQMMxrAWsbaRr7PrFh0ISwf9A,1200 +torch/include/ATen/ops/_thnn_differentiable_gru_cell_backward_compositeimplicitautograd_dispatch.h,sha256=V8AkkPDHv9J3yVgQMNzPIBrbplBOsYWFX9Ru9SX67Xc,1042 +torch/include/ATen/ops/_thnn_differentiable_gru_cell_backward_native.h,sha256=nq7LGsXDmtt8Iy8-XkH8R0GNPxTb40_SbFVo4BLV7i0,754 +torch/include/ATen/ops/_thnn_differentiable_gru_cell_backward_ops.h,sha256=z9JPb3dtVLnWyljPXi_JWQkjtZZZ46B9oFWzTYMoC_g,1867 +torch/include/ATen/ops/_thnn_differentiable_lstm_cell_backward.h,sha256=3CtfMwodh_iMxdeoxdKf5Ag6ZVzbnIIKqwe-VHCWjio,1331 +torch/include/ATen/ops/_thnn_differentiable_lstm_cell_backward_compositeimplicitautograd_dispatch.h,sha256=NHeGssyYpSOd8iW5-vTxf8Nv717NFZ09r0wGcDJPFKo,1128 +torch/include/ATen/ops/_thnn_differentiable_lstm_cell_backward_native.h,sha256=GM7irVmgvyvsfYjcZt9gxMfZSKUJg2zM2YVUIyayy0g,840 +torch/include/ATen/ops/_thnn_differentiable_lstm_cell_backward_ops.h,sha256=KvnpT7DKMbyNqh5G8KkqdkLMPoEqXejAxkGbdD4M0Wg,2143 +torch/include/ATen/ops/_thnn_fused_gru_cell.h,sha256=msab5bZesRGa1N7k-dkCEaFdvTXVlq0hA29xSMAOGQs,2286 +torch/include/ATen/ops/_thnn_fused_gru_cell_backward.h,sha256=AXKwcupEN1F3pS2o7K1H086oLSkX0M_iOzcaOu-5gjc,2247 +torch/include/ATen/ops/_thnn_fused_gru_cell_backward_compositeexplicitautograd_dispatch.h,sha256=pliN6RJ8qCKuNQFrlK3ihS6popLP7Qas9Ir9Hxs7lsE,1295 +torch/include/ATen/ops/_thnn_fused_gru_cell_backward_cuda_dispatch.h,sha256=8HjHcxn6nL6fJrElSvtigF-RRAilTePjybsy8ll9k0U,851 +torch/include/ATen/ops/_thnn_fused_gru_cell_backward_native.h,sha256=ZlM6BijtPOX9GqwHpaNOs5Hi8ujQBEZW8e_9vGMgPOY,902 +torch/include/ATen/ops/_thnn_fused_gru_cell_backward_ops.h,sha256=PCSL8U534_3eZpuaOWpex0aHfQKQzUlrRovzjvpQG7w,2736 +torch/include/ATen/ops/_thnn_fused_gru_cell_compositeexplicitautograd_dispatch.h,sha256=HohqvDNSWsfTLfReqnuRGwCf7J1eCc_X06t1g0AkO_A,1315 +torch/include/ATen/ops/_thnn_fused_gru_cell_cuda_dispatch.h,sha256=_hbLys3SLktulDkF_Y1qNuiqQm32y8OcgkepCTiECoQ,927 +torch/include/ATen/ops/_thnn_fused_gru_cell_native.h,sha256=LxKpLO-wfEc5cFqmHgoWrNuFwdsjXV3j_JuANi9lZKQ,985 +torch/include/ATen/ops/_thnn_fused_gru_cell_ops.h,sha256=twGtq6nLEdVv5jmWR5wIoXNC9tRpPO0lUq_U-b3cTOg,2931 +torch/include/ATen/ops/_thnn_fused_lstm_cell.h,sha256=1V7Uas0mG3_ySk0ZYxTpFIBRaMdp7SLG_2NmVqTa2-E,2449 +torch/include/ATen/ops/_thnn_fused_lstm_cell_backward.h,sha256=GeDJMH_RA1tfj6Ko9jltRWWrfzkist-kx84TssxEgg4,1098 +torch/include/ATen/ops/_thnn_fused_lstm_cell_backward_compositeimplicitautograd_dispatch.h,sha256=WsdQJEWMCssxvPgFD11M-cE2n-Mv8bsMhnu7eKco7oM,1002 +torch/include/ATen/ops/_thnn_fused_lstm_cell_backward_impl.h,sha256=av8CK9AhzX22HB4pqnPtkZy8jlCNGGGpNhJcl6dQ4z4,2496 +torch/include/ATen/ops/_thnn_fused_lstm_cell_backward_impl_compositeexplicitautograd_dispatch.h,sha256=1ty0WCM2UI952dD9jaZ25ICybHYY7nfN7WM6MtvPJSA,1395 +torch/include/ATen/ops/_thnn_fused_lstm_cell_backward_impl_cuda_dispatch.h,sha256=FJrzXVS32VJxIuvmlChbWqkVaXwUP1gzMSIndjaVjCc,943 +torch/include/ATen/ops/_thnn_fused_lstm_cell_backward_impl_native.h,sha256=3xccIgEXSlx5W-dsSP2SUbCFgwm23z0N_t-jcXyAnyM,1044 +torch/include/ATen/ops/_thnn_fused_lstm_cell_backward_impl_ops.h,sha256=9QZtLZiap6a9TXeAQpprUZMde9GzYR5pfQIPwcL4xYg,3150 +torch/include/ATen/ops/_thnn_fused_lstm_cell_backward_native.h,sha256=JpbWkNTdJzin2_rgT_UvRGX19Vs5NyHYHyIchO4l7Qk,714 +torch/include/ATen/ops/_thnn_fused_lstm_cell_backward_ops.h,sha256=rZa1lE5j0LN4ANHjRJqvg_6AjQD7iZklW-eTnDxY240,1745 +torch/include/ATen/ops/_thnn_fused_lstm_cell_compositeexplicitautograd_dispatch.h,sha256=b5aoBRWXPqoaJDDFPHc_-5zst2egppFQxfMflgA9Qrg,1381 +torch/include/ATen/ops/_thnn_fused_lstm_cell_cuda_dispatch.h,sha256=jNik6chSYEEGVfSemghHNfwAzcQGGSqTJRG_daSiwrM,939 +torch/include/ATen/ops/_thnn_fused_lstm_cell_native.h,sha256=g7euIgapGo1p4kwP8wJ7r5yGXC-uZf5_DD-s9lNo1aE,1030 +torch/include/ATen/ops/_thnn_fused_lstm_cell_ops.h,sha256=nUj-n_8DLhHBlfh1RkKyvx5_TxfBfJVwIjplYQS-snQ,3098 +torch/include/ATen/ops/_to_copy.h,sha256=1V2vxurUG3osxPwVKGYkC1ycTxW405XGwbAuVvqZKvo,2439 +torch/include/ATen/ops/_to_copy_compositeexplicitautograd_dispatch.h,sha256=lPdzG7mnGboDpIM7vLSTU1mP_1YrEr3Yg1Pzr9WDDTM,1472 +torch/include/ATen/ops/_to_copy_native.h,sha256=vYXG9-S4inX9mpz8k88QHVqrP-n68C1NFHS1wf_wCb0,1181 +torch/include/ATen/ops/_to_copy_ops.h,sha256=5iW2DD_DNGc3Ess3Hqc2b4jMyQTSgwOnsaouHlXCFZE,2527 +torch/include/ATen/ops/_to_cpu.h,sha256=KorRPFxEn1czRA6-Bvw8IoUJMXdq1pObXZ0iXGu3nBA,665 +torch/include/ATen/ops/_to_cpu_compositeimplicitautograd_dispatch.h,sha256=-EBNmOaU_6yFQfctFFWNuJN4cjKyq6MleA4NIZe5QCg,779 +torch/include/ATen/ops/_to_cpu_native.h,sha256=pG5Zexz8q4Fc6lyQ1MctwR8j3iJPW08wUL2PI2da44M,491 +torch/include/ATen/ops/_to_cpu_ops.h,sha256=zhK0BI-sA4dDBk81uy-0TxCVtXYCw5TKtpsmDW9kmTQ,1006 +torch/include/ATen/ops/_to_dense.h,sha256=iFRRnoWTIxQe1HTX74jmiZQ7r7UC-KxGhaS4iz-lUbM,1227 +torch/include/ATen/ops/_to_dense_compositeexplicitautograd_dispatch.h,sha256=ZAT64IcmJmrbLFB-XloOPAfuHfI0UlQxgqD6dmaeYmY,1051 +torch/include/ATen/ops/_to_dense_native.h,sha256=61rJ9X5ID4vP3mkRu0YRps1xtdBteQaKsjawhJjGYT0,1089 +torch/include/ATen/ops/_to_dense_ops.h,sha256=x91uiD795Cdd7pRkmmgnfSDi-0bEUDrvx8xwqr5MD2I,2069 +torch/include/ATen/ops/_to_sparse.h,sha256=99REMeCvDvecw2fxKb0canP9ITFOSjdG8cA4GRPWjV0,1925 +torch/include/ATen/ops/_to_sparse_bsc.h,sha256=GEJzQFDLDdNGhdI5SMycxxo3CwnpmdUgao8aMvNqojc,1211 +torch/include/ATen/ops/_to_sparse_bsc_compositeexplicitautograd_dispatch.h,sha256=fam1VQzSLLCZB3J1pahOgLKtYJf1tE0GNK5DY0cUoh8,1024 +torch/include/ATen/ops/_to_sparse_bsc_cpu_dispatch.h,sha256=blP7DE3uY9tz6im7ZexiFS2BPK6ZU3lNKXTGFESjnxo,806 +torch/include/ATen/ops/_to_sparse_bsc_cuda_dispatch.h,sha256=fIKb3dsD0QpLSEnnjH1qhOaAdUjX7psYxggIMu1-tO0,808 +torch/include/ATen/ops/_to_sparse_bsc_native.h,sha256=4KBK2zVd-AYyQiwAyKFziz5GRl7ntrf31BLxtp7lJrU,1016 +torch/include/ATen/ops/_to_sparse_bsc_ops.h,sha256=NqFguu8ojZC6wQiWSMHeLS8ULBjD3S4r1yCFIsHu_PY,2011 +torch/include/ATen/ops/_to_sparse_bsr.h,sha256=XxATAz0E2KQHP-BTSoKcyX9trJYFq_1s5V_nXiFtCSs,1211 +torch/include/ATen/ops/_to_sparse_bsr_compositeexplicitautograd_dispatch.h,sha256=3bHuExdABZIVZNGg0xT4kgMqGVezBIffU4fSrbNuEDw,1024 +torch/include/ATen/ops/_to_sparse_bsr_cpu_dispatch.h,sha256=Le4t3heVElAg3yXv8QMedDHAUOGNotq9-OQJfgUMD8Y,806 +torch/include/ATen/ops/_to_sparse_bsr_cuda_dispatch.h,sha256=nKQmh6kkgl5lpidMrQaT0c3QiVgF_zEed81w-vkYRv8,808 +torch/include/ATen/ops/_to_sparse_bsr_native.h,sha256=ULb-RPfmp6TrPLD4MT9-_ntjAJPDZEfOkoc4UovImNI,1016 +torch/include/ATen/ops/_to_sparse_bsr_ops.h,sha256=2XJ_9i1MAXvKAepEfrJGrNJkh-zrXBhMCAA3eyChQ-8,2011 +torch/include/ATen/ops/_to_sparse_compositeexplicitautograd_dispatch.h,sha256=EPyrKRTez9xEKtEcCmMyggR7TO0vfbKfkyX0zYcABj4,1339 +torch/include/ATen/ops/_to_sparse_cpu_dispatch.h,sha256=SBcCDseQjd1QxOINig5bGipQmP24BzJC3HhQjvadL44,954 +torch/include/ATen/ops/_to_sparse_csc.h,sha256=qynf7CHFs1nDN6ZM2Krc9IAfaH4X7SLYGBPJEnlPNKU,1099 +torch/include/ATen/ops/_to_sparse_csc_compositeexplicitautograd_dispatch.h,sha256=SQbXYWbGMoIpg8PMyOI2IXOzAW66jGkoPvqTx30rdfg,970 +torch/include/ATen/ops/_to_sparse_csc_cpu_dispatch.h,sha256=IqRFFzFseCoJh2hFAzMpJJVBzxMp7k5lgoLSHqhcBQU,779 +torch/include/ATen/ops/_to_sparse_csc_cuda_dispatch.h,sha256=KLaRadeIl0aQ49EuiB2X2-WuDPo3vqtWje0tAQvkAuI,781 +torch/include/ATen/ops/_to_sparse_csc_native.h,sha256=yhzoNsarm1vrkSqIM4rnsC3FQkWo9a2JR5VJo0MSkZg,908 +torch/include/ATen/ops/_to_sparse_csc_ops.h,sha256=XzJqKK50wuXGQ9sq0FM6edMpfuoVazEoSlkhqo_ajv8,1833 +torch/include/ATen/ops/_to_sparse_csr.h,sha256=N0dCYGTMZhHYuklhhYXQWusZdjlfWl_0v4uluVcIRWY,1099 +torch/include/ATen/ops/_to_sparse_csr_compositeexplicitautograd_dispatch.h,sha256=7uATwNV0f5Ddc5lXsYienkBuHlUHsKaqArO8OiGbz40,970 +torch/include/ATen/ops/_to_sparse_csr_cpu_dispatch.h,sha256=9y7osDwlpsQAOgAqQ5noFhBFVmfPKf0VIrgJdfm91KM,779 +torch/include/ATen/ops/_to_sparse_csr_cuda_dispatch.h,sha256=ksNj7dh79crqynpIOsvXgcy832JZFDqrw-IaKmwFgSs,781 +torch/include/ATen/ops/_to_sparse_csr_native.h,sha256=Gd6d3_XWzO2I3B_h7UMdtw-uskfrFQ-N7JQP5ZUULcY,908 +torch/include/ATen/ops/_to_sparse_csr_ops.h,sha256=FhMWpp5x_qrU01ns_sUhePiOSbiNw9kEYurtYdKWN5k,1833 +torch/include/ATen/ops/_to_sparse_cuda_dispatch.h,sha256=yIU_SizcV7c9oWajtExEsoGLzs-Ph2IlCtAMktuKCCk,956 +torch/include/ATen/ops/_to_sparse_native.h,sha256=1cl1V0zYiPXuLzEhc1TG8ozpEnhD_ZtMFNUvys_vgKs,1652 +torch/include/ATen/ops/_to_sparse_ops.h,sha256=XfRpK5rgAIQjF6kLwla9ouTB5wQ4hUB8t880XkbQe1M,3603 +torch/include/ATen/ops/_to_sparse_semi_structured.h,sha256=fDS_8lp0P0yl5q4VNKNog82raCQ6aP-08knyCO6LdoY,755 +torch/include/ATen/ops/_to_sparse_semi_structured_cuda_dispatch.h,sha256=fuBR9LuFkYTnI9RMXfM_oZXyAnYtbbZ0K1F8030-BA0,768 +torch/include/ATen/ops/_to_sparse_semi_structured_native.h,sha256=-wCaWb9t7ih6goIWqlnsmlkGRpYpoWwVizFTkRqQQq0,522 +torch/include/ATen/ops/_to_sparse_semi_structured_ops.h,sha256=TqDY10Eli8XmcZ_Au1TSv0GW4gJmiPR-wzMD9Zjk1F4,1105 +torch/include/ATen/ops/_transform_bias_rescale_qkv.h,sha256=LIujJDFkD1z7VU9iLY6gSAhiqrZKI7rJ2pSqir5qRzE,1891 +torch/include/ATen/ops/_transform_bias_rescale_qkv_compositeexplicitautograd_dispatch.h,sha256=xST10llBXdLyPiW3bmxHbESKM2zndrNbf3uOM65BOHY,1161 +torch/include/ATen/ops/_transform_bias_rescale_qkv_cpu_dispatch.h,sha256=BcWO5Q2hcIw00yN3xIuT0gkkX-j-2q_k9l-eqTgJ5U0,824 +torch/include/ATen/ops/_transform_bias_rescale_qkv_cuda_dispatch.h,sha256=HlM9SZU5PEIeSefcEVD-QZATP5m5NxkpgC4lssLFbqQ,826 +torch/include/ATen/ops/_transform_bias_rescale_qkv_native.h,sha256=sEdanw4EAlrDpjclyPPkVM-2tHfhvYYY4cixzeHefpg,970 +torch/include/ATen/ops/_transform_bias_rescale_qkv_ops.h,sha256=j9ifthzRmgps-lCQIiiKiHHbMK9C6T9LM2SZPy3-vPI,2394 +torch/include/ATen/ops/_transformer_encoder_layer_fwd.h,sha256=Y3U3fKFuDjaY-TOooEApJcnUFn6Uf_ZNXftXPyswHcc,4633 +torch/include/ATen/ops/_transformer_encoder_layer_fwd_compositeexplicitautograd_dispatch.h,sha256=VNeaKUWLSxqN8VuxDFQETMbUGC7vle0bOWdcxDqRxok,2015 +torch/include/ATen/ops/_transformer_encoder_layer_fwd_cpu_dispatch.h,sha256=Ud2yGJcp15VV9IpwyRzxKNMt1_8nWvgIHF63Eh_ahao,1303 +torch/include/ATen/ops/_transformer_encoder_layer_fwd_cuda_dispatch.h,sha256=nHbccHpB65qoAnJFr4fafUEe6r25uX5CJAnF4shhQkY,1305 +torch/include/ATen/ops/_transformer_encoder_layer_fwd_native.h,sha256=KKohB7vuIFJhmPR_mADpO__DWycyWQ37VyetBMSn3Sc,1705 +torch/include/ATen/ops/_transformer_encoder_layer_fwd_ops.h,sha256=jbWLPKpltnEsG2FwU9YlE_LpOnOeV-Vly_xwKAsehj8,5237 +torch/include/ATen/ops/_trilinear.h,sha256=db_c4IrK6Ejcx5-wyNjOWIswamr7YzkxVSv9j7F_pe8,2010 +torch/include/ATen/ops/_trilinear_compositeexplicitautograd_dispatch.h,sha256=h4OFRkJpe-f2PPOHOBxmhEstKlJLY06dhn-fT7ij8Po,1203 +torch/include/ATen/ops/_trilinear_compositeexplicitautogradnonfunctional_dispatch.h,sha256=MofkLAAcO-AxDBQLVW4wrhHwI8xsnREFX1HpFHNcfRo,959 +torch/include/ATen/ops/_trilinear_native.h,sha256=2DI_DU5RCu8OfjN0oDGdhShe9x6g6XnoG5lmNDcBT3Y,890 +torch/include/ATen/ops/_trilinear_ops.h,sha256=78aNLCjibzxiQsekTL868miP7lNjvDV5vhp7EEXFjZI,2651 +torch/include/ATen/ops/_triton_multi_head_attention.h,sha256=bMvL7Zxnml1fX-zECsJcTerIUw0_qTq15kn0zwFl0MM,2714 +torch/include/ATen/ops/_triton_multi_head_attention_compositeexplicitautograd_dispatch.h,sha256=1HnNA-rZks7QiEOTrczhNBk44OlXpbwQL476uj77mmc,1418 +torch/include/ATen/ops/_triton_multi_head_attention_cuda_dispatch.h,sha256=DAl2gI5EpPYhnp7wwgwO0v6bLGgtTDcO2dSstYgAzQw,999 +torch/include/ATen/ops/_triton_multi_head_attention_native.h,sha256=khrXr5Lmhx__s4IFDyjyTNlTkAdRpb9QkYnBJYio12k,1104 +torch/include/ATen/ops/_triton_multi_head_attention_ops.h,sha256=FmmJ85EVLJ6JIIVYxH_OrXZC-fcQX2nK6gbvZmGZ3nU,3335 +torch/include/ATen/ops/_triton_scaled_dot_attention.h,sha256=cgRxp5aKfe53i-P8quUlBeCtcolMMTJoWo3dvVcK0qg,1588 +torch/include/ATen/ops/_triton_scaled_dot_attention_compositeexplicitautograd_dispatch.h,sha256=vz8X1LBijz7MhuCMP1lqfWfP9LSDjC8HpJFjhh6N75g,1033 +torch/include/ATen/ops/_triton_scaled_dot_attention_cuda_dispatch.h,sha256=bDy9UkXTBmbkpbGFjHO1J9vUjhfvFQ2RyEK06rj7PoM,807 +torch/include/ATen/ops/_triton_scaled_dot_attention_native.h,sha256=KB0j40GD3RzFH4QeM_vCdPhGpcwM24KO86jiqHUCGYg,719 +torch/include/ATen/ops/_triton_scaled_dot_attention_ops.h,sha256=dDvnUVHho035K2uIMZG20jvujRQAbWMHV15jivmpkxk,2087 +torch/include/ATen/ops/_unique.h,sha256=Ta-OFqIMKRj_aiU3IXWkNg3Xr2Cs3urmT7du91fmU7M,1575 +torch/include/ATen/ops/_unique2.h,sha256=gLgfZkp4_ZfuGmD8ptSuBrHNf1Sn0T_3B6DFbGlrOxE,1933 +torch/include/ATen/ops/_unique2_compositeexplicitautograd_dispatch.h,sha256=RFxgYNv-dkWh-NMPvBuc9zOZ_Y2pkzqj3_7a1NdL638,1154 +torch/include/ATen/ops/_unique2_cpu_dispatch.h,sha256=SBLWdFoYgOvUSkjW-f9d-4MP5yfZJImBiL-3dJ5cIjs,829 +torch/include/ATen/ops/_unique2_cuda_dispatch.h,sha256=JPmSRM4Dy00a2hdVIPLjkhtMopQqZNePESg8t2dlW1c,831 +torch/include/ATen/ops/_unique2_native.h,sha256=-iYnfzSMjj0YHHzbn4Zku5fp-AHoRoXY4-VLCtWgAHI,970 +torch/include/ATen/ops/_unique2_ops.h,sha256=qf07nsz1rE6SZ1YbTZHvhRwUciSDE0DuW9OSPcifcxk,2366 +torch/include/ATen/ops/_unique_compositeexplicitautograd_dispatch.h,sha256=WHmU251GvT2zvtZbiWnp3rSjh4X-KuOivcGoUw8JFgA,1042 +torch/include/ATen/ops/_unique_cpu_dispatch.h,sha256=hp3VmTSL8ofYTOcxEeO58D6QPP7_e_qYYhjW19vrrRg,791 +torch/include/ATen/ops/_unique_cuda_dispatch.h,sha256=Lj3bVAgjF7sNcWF8Daz2Q28ETSXVajJO8nGC81mSCoA,793 +torch/include/ATen/ops/_unique_native.h,sha256=KH1X0pYxC-cnVg4OWoEHZlDFEbGhdV6drs1KdpTW63g,841 +torch/include/ATen/ops/_unique_ops.h,sha256=FHvVHFmquOQBkiMJIqlgT1vpWdPE8ylAY0UEOJMeFA4,2055 +torch/include/ATen/ops/_unpack_dual.h,sha256=HZa1ZcjJQliB2uPeIfGgTbTawWyyBvp34I3iry489iY,750 +torch/include/ATen/ops/_unpack_dual_compositeimplicitautograd_dispatch.h,sha256=sIKK7Hdi3-F01RcfasGV7xgAC6vbkdoWehxWmbuMeB4,810 +torch/include/ATen/ops/_unpack_dual_native.h,sha256=E3aQzkZa5BRcVxU81Ui_Xfp1I2CG47RXddLDXQz4xZw,522 +torch/include/ATen/ops/_unpack_dual_ops.h,sha256=3nXMF8NpDPIqQAMmFyAFGOn4_xvWkdV2GnxwjbgpgAU,1131 +torch/include/ATen/ops/_unsafe_index.h,sha256=BY4AoTT6ceA0yOXqylpy0LYRcph0poTm_nxrzBmm_zE,763 +torch/include/ATen/ops/_unsafe_index_compositeexplicitautograd_dispatch.h,sha256=3NsCnxhKNw1hpG8xuQEPi6n5289oxPMqnWGfpl5f5Z4,827 +torch/include/ATen/ops/_unsafe_index_native.h,sha256=DPvOhrc2AI1WIPh46zTaqc8OKama5u8eAwJpBdbWjJ0,539 +torch/include/ATen/ops/_unsafe_index_ops.h,sha256=gomGJBZjsM3fgkkNccK965wZEhxzno_EWAyP_6Q2EB0,1177 +torch/include/ATen/ops/_unsafe_index_put.h,sha256=uCW56dsuLlzfCXH9NDXO1BVZjJurQctzfuTz9NCBfFY,873 +torch/include/ATen/ops/_unsafe_index_put_compositeexplicitautograd_dispatch.h,sha256=H_-4iw21a2Saa8HaMsslOG9Du_TN492cOGHd_ByuvXA,881 +torch/include/ATen/ops/_unsafe_index_put_native.h,sha256=aQTdhdlkFDAbOqebSu7r5OLHvMifT6OXW8clmRoMvao,593 +torch/include/ATen/ops/_unsafe_index_put_ops.h,sha256=Jl9UUclO1TopqxWDRn-ebySF5Q0yd6eudxqBfulPaAM,1321 +torch/include/ATen/ops/_unsafe_masked_index.h,sha256=JEYAn3tLlhV1gtrPKjJNuZgW4MwOm_67ad1kc05mHm0,865 +torch/include/ATen/ops/_unsafe_masked_index_compositeexplicitautograd_dispatch.h,sha256=NpCYdjOWFe5gqhW-2NUIGH4dGhlBgGS1Q61YMbL85S4,884 +torch/include/ATen/ops/_unsafe_masked_index_native.h,sha256=qbiwi3jX10FuDGyH_sfG5duWfKwWsigHU4D7L8jkBbs,596 +torch/include/ATen/ops/_unsafe_masked_index_ops.h,sha256=_G41dkMHdwz5gy7_eJuLELbjJ4oQhIbwX0CErfPXLw4,1344 +torch/include/ATen/ops/_unsafe_masked_index_put_accumulate.h,sha256=R4maS1qvi3VmcUkjoOXiwJQrKqguKto7zAeft4Fk7Sk,931 +torch/include/ATen/ops/_unsafe_masked_index_put_accumulate_compositeexplicitautograd_dispatch.h,sha256=iKG9W5zLeBBaUvp7e2vQZTCKSSeIGJuzV_ZJI779c4k,901 +torch/include/ATen/ops/_unsafe_masked_index_put_accumulate_native.h,sha256=xn393XUbsWOLDzes4dxo7bG2irG-Tebgr_Bzey5QBYQ,613 +torch/include/ATen/ops/_unsafe_masked_index_put_accumulate_ops.h,sha256=VDkEewB1KPPvACiOt1rnVBmlj3do0NUZTPGOWrrH4-8,1395 +torch/include/ATen/ops/_unsafe_view.h,sha256=4fEB_59GmFcyERR3aqmBacN8xPxbsvRvzYNryJR67HE,3677 +torch/include/ATen/ops/_unsafe_view_compositeexplicitautograd_dispatch.h,sha256=__cRFBu4oiCt8v1aOz1Pu38u8ejj2H5DRi3vr3YWmJA,1333 +torch/include/ATen/ops/_unsafe_view_native.h,sha256=4Z7_zPyuL-_qM6JIt5xCCNuSC0xGMhCLrCIzdnJAzmY,621 +torch/include/ATen/ops/_unsafe_view_ops.h,sha256=FA8PbRTbHJiswWgDLcj8pWesCZY6PjMDTb37u8ccnJU,1759 +torch/include/ATen/ops/_upsample_bicubic2d_aa.h,sha256=1xwgqjbAA7Z1Xd7rLHYeZZJJNZW8iT1beg8micV43W0,8102 +torch/include/ATen/ops/_upsample_bicubic2d_aa_backward.h,sha256=9wZGcuKkxS5PtXZhxMeo4FAxpe1vP0ADYOb__m1BkRA,7808 +torch/include/ATen/ops/_upsample_bicubic2d_aa_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=mjgBYTXriMTbvYdmqJ6WlSxm4TQIVQIppE1boDM5kyg,1273 +torch/include/ATen/ops/_upsample_bicubic2d_aa_backward_cpu_dispatch.h,sha256=RUWLSKGJm7OREtOy7xMf3rEdmeNXKxcdPyXc4Toq3kA,2343 +torch/include/ATen/ops/_upsample_bicubic2d_aa_backward_cuda_dispatch.h,sha256=1W3iZtie5QdmeW-eX_f7AUMNs_oLaMfS89kkBNTdYlA,2345 +torch/include/ATen/ops/_upsample_bicubic2d_aa_backward_meta.h,sha256=EcA1ueTU0MvsH1c5B1-lNZv_9daoubiOGd5R03RzjGo,756 +torch/include/ATen/ops/_upsample_bicubic2d_aa_backward_meta_dispatch.h,sha256=tvlrACoIP6obBxmAVoneB5sKJs0eDfW3ihpjT1iT8Uw,2345 +torch/include/ATen/ops/_upsample_bicubic2d_aa_backward_native.h,sha256=-P9R8bMkn3kmxDnbYl6m2tI2EIyCFKlHd6rfAIaa6Uo,1213 +torch/include/ATen/ops/_upsample_bicubic2d_aa_backward_ops.h,sha256=Sf_3ZhFt6loXp9676EHQ40HaKq4ooCEMTBkdKkUXRgs,2807 +torch/include/ATen/ops/_upsample_bicubic2d_aa_compositeexplicitautogradnonfunctional_dispatch.h,sha256=h1rRskuYn3rT9kK6jEaeOoqIy4YQSzed41wHVIvc3iQ,1181 +torch/include/ATen/ops/_upsample_bicubic2d_aa_compositeimplicitautograd_dispatch.h,sha256=f6s6fJvLezEgR2WVAIJiBig-NpTUKHs3DYzukG7kwNY,1082 +torch/include/ATen/ops/_upsample_bicubic2d_aa_cpu_dispatch.h,sha256=e6A31185Om3JsWahpGgBRY5N3DxdtZTefCRjBFwzZMw,2039 +torch/include/ATen/ops/_upsample_bicubic2d_aa_cuda_dispatch.h,sha256=u0uBdlTL3HmZbzl7WVBit_-YIv_IKZqSPzB5STz_Lx4,2041 +torch/include/ATen/ops/_upsample_bicubic2d_aa_meta.h,sha256=kiFv3j40qioDTxvzVKoBlcuGIFRUwMwWjjQuMCI3LOg,706 +torch/include/ATen/ops/_upsample_bicubic2d_aa_meta_dispatch.h,sha256=Z41q_q9vvKtQpWc9cMvmkqmbEAO0SxeTdgnVxkBmhCc,2041 +torch/include/ATen/ops/_upsample_bicubic2d_aa_native.h,sha256=bL06iwoAH16xOQoMor51p4KSgXwHxAwITa8jh8c3qpM,1253 +torch/include/ATen/ops/_upsample_bicubic2d_aa_ops.h,sha256=kPjir1trpdApimIMayKjA1LHiv9Xqg3lWF57lAKObpY,3383 +torch/include/ATen/ops/_upsample_bilinear2d_aa.h,sha256=-5pOH3nH7fPaIZdmK64vGtffW_y4pGHXC23jiO8A6cU,8143 +torch/include/ATen/ops/_upsample_bilinear2d_aa_backward.h,sha256=cJMglBrplztXYwIlgjA_WVr3d_7Zb6TSo0KVaKnVdrI,7839 +torch/include/ATen/ops/_upsample_bilinear2d_aa_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=VvlCjCtOc9zr6vocH8Lc6_QOdBimCs6al80nKW7oLzQ,1275 +torch/include/ATen/ops/_upsample_bilinear2d_aa_backward_cpu_dispatch.h,sha256=D8__U9vuSC0bq7uE6bNEB0xLDbcLvbxOKZww8tiSXKc,2349 +torch/include/ATen/ops/_upsample_bilinear2d_aa_backward_cuda_dispatch.h,sha256=U5Z8rQ3Mnap8BSzNRxVL4G5zvk4RnYyoI0y2vS0udXE,2351 +torch/include/ATen/ops/_upsample_bilinear2d_aa_backward_meta.h,sha256=v57IqDM68AwrMNII3OQXx5cW0xTG7XM8Y2la25pyORw,757 +torch/include/ATen/ops/_upsample_bilinear2d_aa_backward_meta_dispatch.h,sha256=3PIcj7xnJI-i5Nl_UmM3sZ7spQ3IZBvGXCMOlGtdlSk,2351 +torch/include/ATen/ops/_upsample_bilinear2d_aa_backward_native.h,sha256=h2wEPtzpmx2GOaK6CDpfKgKlDu5ag6nob5woYYq6aNg,1218 +torch/include/ATen/ops/_upsample_bilinear2d_aa_backward_ops.h,sha256=62Sir_C_d_p-ScZouNvE1TEMP3H3w8m634tY5vnRkjw,2813 +torch/include/ATen/ops/_upsample_bilinear2d_aa_compositeexplicitautogradnonfunctional_dispatch.h,sha256=asBqyvdfFzEBKc9WmbI2QntBjrFU-kCY-zrmUQME4pQ,1183 +torch/include/ATen/ops/_upsample_bilinear2d_aa_compositeimplicitautograd_dispatch.h,sha256=4P5v_o4DuJGpdcmlb2aMFbUMpYNdgUovkuGKKbzn7i4,1084 +torch/include/ATen/ops/_upsample_bilinear2d_aa_cpu_dispatch.h,sha256=RgYJi2FBZt2k6MupmIYLTTl6JZAQliOcNBaKfziWTLs,2045 +torch/include/ATen/ops/_upsample_bilinear2d_aa_cuda_dispatch.h,sha256=7yYqMDLzbdAcZpgdTxCSSB5ISk1di83qcVQ5u5KJjgU,2047 +torch/include/ATen/ops/_upsample_bilinear2d_aa_meta.h,sha256=BN0dC3MChftOPogWCl_63pMqB-4U2OfOsGcTMpIV9RE,707 +torch/include/ATen/ops/_upsample_bilinear2d_aa_meta_dispatch.h,sha256=SMvhEig57M3ZdBFxC9qFdWlJjdYObepiZdNK3e0kCUY,2047 +torch/include/ATen/ops/_upsample_bilinear2d_aa_native.h,sha256=dCqiRGS3iLCOeHAxVLvjiADU8nIYr3UQgIaOzC1siYo,1259 +torch/include/ATen/ops/_upsample_bilinear2d_aa_ops.h,sha256=6MIT94V1DP3uSbBY5583skzFCIg9mDiTq9E2BhfdL3I,3392 +torch/include/ATen/ops/_upsample_nearest_exact1d.h,sha256=ysI_bouD6Xk2N_515UwAdVtj848Lo7iUCjzoNFIMUx0,6665 +torch/include/ATen/ops/_upsample_nearest_exact1d_backward.h,sha256=ksc5FzEAxOA2khLYvkpTaPAVG_cnC8fq1XDjCopczOE,6521 +torch/include/ATen/ops/_upsample_nearest_exact1d_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=jgloO5KLAbQ1O31ILM640r8ltLQuTptNgrnhV3xqaXg,1137 +torch/include/ATen/ops/_upsample_nearest_exact1d_backward_cpu_dispatch.h,sha256=rJQoTh0S7jFSPcqkrRM6eIb-yx8TBfCQnvgbjkGw8PA,1965 +torch/include/ATen/ops/_upsample_nearest_exact1d_backward_cuda_dispatch.h,sha256=1LZMkisIrY05B8Gt0d7HqcYlTsZ6VgsYSS6icKWFFCw,1967 +torch/include/ATen/ops/_upsample_nearest_exact1d_backward_meta.h,sha256=v41XwLq1VqRtSOCOJ0jxkMUQe87fXjGO25SOfyG7Ng0,703 +torch/include/ATen/ops/_upsample_nearest_exact1d_backward_meta_dispatch.h,sha256=1dWZe1EKuWSC4Tc5Bz-lSvdUSAxtroaOBwX_oDRi5aA,1967 +torch/include/ATen/ops/_upsample_nearest_exact1d_backward_native.h,sha256=_N44JcuOXQjnXmlHVl3eZMgGXWJ_x5qkcjnN6qWBUKg,1116 +torch/include/ATen/ops/_upsample_nearest_exact1d_backward_ops.h,sha256=Rhw9fQwOZSWGmRNmuQic6y7MgCyqDzQ3K05_qWYAvq8,2451 +torch/include/ATen/ops/_upsample_nearest_exact1d_compositeexplicitautogradnonfunctional_dispatch.h,sha256=xpcQPkojWbxNXHjRlLbPqigQ-IIcCv2AFBK6w4gHgPI,1045 +torch/include/ATen/ops/_upsample_nearest_exact1d_compositeimplicitautograd_dispatch.h,sha256=UuEgMAWviJpiHB3bfIKqf7z2VOz-Z3g6PRBl2ECJEdc,1048 +torch/include/ATen/ops/_upsample_nearest_exact1d_cpu_dispatch.h,sha256=-pB_Pd9ttETOe29P8DNK6hJHHq3iB6mkYhYOaqlfOtU,1661 +torch/include/ATen/ops/_upsample_nearest_exact1d_cuda_dispatch.h,sha256=Q7L67s9JzWxu6LRKENc7Dz3-DV-0CBbxIlRHXHl9934,1663 +torch/include/ATen/ops/_upsample_nearest_exact1d_meta.h,sha256=64PLnfvnZ1LHASWqzmBiCSaC8CJ-bpVpxiRbLN4xjFQ,653 +torch/include/ATen/ops/_upsample_nearest_exact1d_meta_dispatch.h,sha256=BcTTzGmNwrIzyx29HCLic8lkmIy-Z1B7i6kz-WNuwT0,1663 +torch/include/ATen/ops/_upsample_nearest_exact1d_native.h,sha256=n9aP0zwcRJiEgLRnFNQyqgQfeKNmpI-B4yTVmuvVsrw,1139 +torch/include/ATen/ops/_upsample_nearest_exact1d_ops.h,sha256=AVVUvfWvBJe884J8FasuniCLpVMnkwja7aZ0_8bLcnc,2970 +torch/include/ATen/ops/_upsample_nearest_exact2d.h,sha256=ZcVEtcu_gvioSCP_660yC0J6jphMf0rguNs-zQa-uBQ,7505 +torch/include/ATen/ops/_upsample_nearest_exact2d_backward.h,sha256=S_A4krRUJ6igsg5ugMjUSonDESFKwKyWJ9SMbwNPwE8,7361 +torch/include/ATen/ops/_upsample_nearest_exact2d_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=Figkb7pZLrYcquDC-TP65R60aJpEjm84HKxFiYwEmpU,1239 +torch/include/ATen/ops/_upsample_nearest_exact2d_backward_cpu_dispatch.h,sha256=Eo-NB-pZoHPmTg8teueJnQmMIoRVeF-ZkccJfhdgPlU,2241 +torch/include/ATen/ops/_upsample_nearest_exact2d_backward_cuda_dispatch.h,sha256=7C20N4N5wzhkqA9ie-7hYFeHerF8B7XE-id_KfXrArU,2243 +torch/include/ATen/ops/_upsample_nearest_exact2d_backward_meta.h,sha256=bBII53_E8E_P72QnzWlcfF_RGTyKi0BuN023AfUJAZA,739 +torch/include/ATen/ops/_upsample_nearest_exact2d_backward_meta_dispatch.h,sha256=TKWTX0th05SvUrEUwC4cwWarI5pQTQvhETZFEFrT0uA,2243 +torch/include/ATen/ops/_upsample_nearest_exact2d_backward_native.h,sha256=2E6VJ7gktkoI2t-DxzBfLMvsWiJDrrUP6sdfIfUPgFw,1188 +torch/include/ATen/ops/_upsample_nearest_exact2d_backward_ops.h,sha256=aHXjueIren0-5mBKpWRrxR8nwipvDlB0s8DznkwWJTY,2693 +torch/include/ATen/ops/_upsample_nearest_exact2d_compositeexplicitautogradnonfunctional_dispatch.h,sha256=mTG9GRUS9omcfZ1YV4MgQhFO4Hv4rdVwnuOD4d_1nW4,1147 +torch/include/ATen/ops/_upsample_nearest_exact2d_compositeimplicitautograd_dispatch.h,sha256=sEBmQ8ADwPxrwADA0wXKCE6o-iai-kazJ3gI5zJotmc,1048 +torch/include/ATen/ops/_upsample_nearest_exact2d_cpu_dispatch.h,sha256=MAu0fcjWe3o3_Jo5M0cRF7-DvDeyrby_GGS7vOFx4N0,1937 +torch/include/ATen/ops/_upsample_nearest_exact2d_cuda_dispatch.h,sha256=o0GMGmNHq5CuL480E3qUO8v2NVIFpS_lEC4gVc5zusg,1939 +torch/include/ATen/ops/_upsample_nearest_exact2d_meta.h,sha256=RxIPrtKWzthOQdF9PQR0gKjINUvxjgk9Um4Dbj6QLEc,689 +torch/include/ATen/ops/_upsample_nearest_exact2d_meta_dispatch.h,sha256=YYUbR8HnEBU8bQD1sA1HWiL8f49FKkKyM1tWOfb30hY,1939 +torch/include/ATen/ops/_upsample_nearest_exact2d_native.h,sha256=0vcLx6d7lrOgsjSRsw_v7pqeks9v2j3osWUNrkL-I88,1425 +torch/include/ATen/ops/_upsample_nearest_exact2d_ops.h,sha256=EP_pkGDvmFiOHcaIc_zwgbWdQoDXM7zPuBQRPyhqwPg,3212 +torch/include/ATen/ops/_upsample_nearest_exact3d.h,sha256=ntJxTCHWwm1mniWd6jGoKGN1vnG3XqE6qWmTVGAIJN0,8285 +torch/include/ATen/ops/_upsample_nearest_exact3d_backward.h,sha256=zbRSANlPzETBPXsqKfFDi7n4XtwVFUrjT-UIsf34QgQ,8141 +torch/include/ATen/ops/_upsample_nearest_exact3d_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=Ml5CfjG1ygJ1baFYZELuxmEJzXq8v7ZmmLtxs0kTmB0,1337 +torch/include/ATen/ops/_upsample_nearest_exact3d_backward_cpu_dispatch.h,sha256=fjgZj9B4_PTV8ApdclaZIFbPxVt9U-O-cIzWSG4OyK4,2505 +torch/include/ATen/ops/_upsample_nearest_exact3d_backward_cuda_dispatch.h,sha256=kXZjYa2ModKGKFsx9dy3FIcnRvuPetqMgu3DDhBIXiQ,2507 +torch/include/ATen/ops/_upsample_nearest_exact3d_backward_meta.h,sha256=8TLNe1w8fapUzRR9Jdb0Bnh7aVg8cy25TN-Uw4qkr0c,773 +torch/include/ATen/ops/_upsample_nearest_exact3d_backward_meta_dispatch.h,sha256=aq4B053yyEtxQd0hmGxYOYnHqwEvWIpxuEtmOrAj_wU,2507 +torch/include/ATen/ops/_upsample_nearest_exact3d_backward_native.h,sha256=VzrR0rlSIFNzQHr4CbK4AccOlUzcte0u6dT2Hkf6Wr0,1256 +torch/include/ATen/ops/_upsample_nearest_exact3d_backward_ops.h,sha256=B5viIrPbp4EI89NjLKdS4k9GCJ4-MfYLGtbAv8NZoiA,2923 +torch/include/ATen/ops/_upsample_nearest_exact3d_compositeexplicitautogradnonfunctional_dispatch.h,sha256=qzxWUtSqF6RJmqiWaoQma7bhMN3aYe1sqUcnsxWjYxQ,1245 +torch/include/ATen/ops/_upsample_nearest_exact3d_compositeimplicitautograd_dispatch.h,sha256=X12SmZXn6tt_dSoqrbwq4RJ2DQuJO6wD9t7coPAyjpE,1048 +torch/include/ATen/ops/_upsample_nearest_exact3d_cpu_dispatch.h,sha256=QUyoYaEKdbBd3cyuqriPu-nhdjlgTxtBgWa9Rj6A-pM,2201 +torch/include/ATen/ops/_upsample_nearest_exact3d_cuda_dispatch.h,sha256=zDnu5af7LmyPItr9urmA3i89LoDkS_R3DhNENvc0Ags,2203 +torch/include/ATen/ops/_upsample_nearest_exact3d_meta.h,sha256=f794M_o3IwN6o9zrHtJqYEP87WljHgWOrX3VRBPmtkA,723 +torch/include/ATen/ops/_upsample_nearest_exact3d_meta_dispatch.h,sha256=JwueezvAjnGeahTKJ3YNE_0J2MJjUHquLzgAZ1ldJ-M,2203 +torch/include/ATen/ops/_upsample_nearest_exact3d_native.h,sha256=azyktqFJbeN8JN8bytvQ2KN1PJK-Lkqdts3OzOb_lKs,1542 +torch/include/ATen/ops/_upsample_nearest_exact3d_ops.h,sha256=P0L90N4XBvMR6YSS3YKqjagiNDaTh2d1t28ThvlkqbQ,3442 +torch/include/ATen/ops/_use_cudnn_ctc_loss.h,sha256=Rpxbo3me0taI2PdYARf5xfsmK7ofQDPHBlUuaWJHy2w,1345 +torch/include/ATen/ops/_use_cudnn_ctc_loss_cuda_dispatch.h,sha256=_JdPWp-bKk5rX5Kl_Rcyfc_raJGUKB1uoELuKQt6LTw,1018 +torch/include/ATen/ops/_use_cudnn_ctc_loss_native.h,sha256=R07eImaK4YDd9WskhcdhavGBKx1g4wxfG7zeMUWst88,779 +torch/include/ATen/ops/_use_cudnn_ctc_loss_ops.h,sha256=Rc4V2bvvnyQbC0o35KH43oKR-4TVwoFGi1LPf1FSwB0,2255 +torch/include/ATen/ops/_use_cudnn_rnn_flatten_weight.h,sha256=Udd9iTns1g5LE-mv6n2-3PPQGuN6FGfwo0RwgNwBKKQ,683 +torch/include/ATen/ops/_use_cudnn_rnn_flatten_weight_compositeimplicitautograd_dispatch.h,sha256=i6BU07fqmo_ZVFMdOr5cuvGL-g5jXTnHCC4xqyoCG6A,758 +torch/include/ATen/ops/_use_cudnn_rnn_flatten_weight_native.h,sha256=VHgmDbRVs30B7NsxJ6mfG1crFC6AplXoaqVK4WZwBbo,470 +torch/include/ATen/ops/_use_cudnn_rnn_flatten_weight_ops.h,sha256=D4GSgqD6yoqgDs0-fFJBURFcTQKYLG6jHNwOLhjzxTw,929 +torch/include/ATen/ops/_validate_compressed_sparse_indices.h,sha256=7vDDW7oCMwW5crvhvHazRH6E9RPAhmf5T27cBiVD48s,953 +torch/include/ATen/ops/_validate_compressed_sparse_indices_cpu_dispatch.h,sha256=JlPzyXxgay2-czVFdBPfVlK1Y6MhjCdKlDjFaF4hmew,837 +torch/include/ATen/ops/_validate_compressed_sparse_indices_cuda_dispatch.h,sha256=XzfESsww_gubv4HgOO0Wbv7bb8D79GjWDfn8VMUdI1w,839 +torch/include/ATen/ops/_validate_compressed_sparse_indices_native.h,sha256=iM3PS0euMOTJILTxnVufBZ0M0GCjg5TcZb2s30Pox2w,773 +torch/include/ATen/ops/_validate_compressed_sparse_indices_ops.h,sha256=QjrEa56CeW74iR7P6PcXjk9d-ph8LZ2y3pW__5CQdpw,1333 +torch/include/ATen/ops/_validate_sparse_bsc_tensor_args.h,sha256=Gmh-JtSNZm1kM49-0SGNXfUPBX0g3ydSUF2IRieaEPY,1003 +torch/include/ATen/ops/_validate_sparse_bsc_tensor_args_compositeimplicitautograd_dispatch.h,sha256=GxSMGWZ40yq_3ZQBqeN7wMxlYG99t2Ojb6H4bchZBLg,925 +torch/include/ATen/ops/_validate_sparse_bsc_tensor_args_native.h,sha256=eeX_Io0oPmm6dkOxT31qvHUBnqyhNesuib6Lf348IEw,637 +torch/include/ATen/ops/_validate_sparse_bsc_tensor_args_ops.h,sha256=oxNtckVkyKfLPuGmj9OFBQjom2DZ4ZRT6-Vd_Dosuac,1426 +torch/include/ATen/ops/_validate_sparse_bsr_tensor_args.h,sha256=kaJJypsZiJYqR7mcriVdBSYrdAQegj5Mw3XLS-7_jK8,1003 +torch/include/ATen/ops/_validate_sparse_bsr_tensor_args_compositeimplicitautograd_dispatch.h,sha256=BtZjt786imkQg-Rg4GQh-NiMU6qwxVs_YG8dTfZvTgA,925 +torch/include/ATen/ops/_validate_sparse_bsr_tensor_args_native.h,sha256=dVLS61uAZ9yai2sqjX4nZyNRxpquukcQpE0eQjnk9XM,637 +torch/include/ATen/ops/_validate_sparse_bsr_tensor_args_ops.h,sha256=PHNRJ9on2O1a8i_8TLkMH5LfJftT4ETEteOfu75ykhU,1426 +torch/include/ATen/ops/_validate_sparse_compressed_tensor_args.h,sha256=PLgSOBf5uLK-9PqVsuqwmmNJMex6TyJxM70VA52U8uY,1097 +torch/include/ATen/ops/_validate_sparse_compressed_tensor_args_compositeimplicitautograd_dispatch.h,sha256=5ai9nnxIdXb6IhXiMF5G3hhhyZuK-hdRlJ5X9RtlkvU,959 +torch/include/ATen/ops/_validate_sparse_compressed_tensor_args_native.h,sha256=O15mswq1tAHQ0DOZiWxC0t-9X2OxmbKCxEwevbHATbw,671 +torch/include/ATen/ops/_validate_sparse_compressed_tensor_args_ops.h,sha256=oPk9TGGsSG3wfpcqZOjSk1JRomZCsYxTMLsZPxMzaLY,1536 +torch/include/ATen/ops/_validate_sparse_coo_tensor_args.h,sha256=mBlaKWJRNM-vB_bYhzYEq_vZNb-HVcM6ksez_Ww1EGk,1013 +torch/include/ATen/ops/_validate_sparse_coo_tensor_args_compositeimplicitautograd_dispatch.h,sha256=XLSuXbTcWr47WyrNn_w7AQlVzvLKX_mopna0RWMCvlM,939 +torch/include/ATen/ops/_validate_sparse_coo_tensor_args_native.h,sha256=rcQ7QbybBUBM_XJ9LoKzOlhMy_qWvutYSNB2YPKzh3w,651 +torch/include/ATen/ops/_validate_sparse_coo_tensor_args_ops.h,sha256=8PqWWr_ddR4x0QNyp5y_s9b7KR76x8FXh14vedek-fM,1427 +torch/include/ATen/ops/_validate_sparse_csc_tensor_args.h,sha256=cWJB7KJtyr330IHlvwMp7GSlKEsR9b5qeIzzaFlIAvQ,1003 +torch/include/ATen/ops/_validate_sparse_csc_tensor_args_compositeimplicitautograd_dispatch.h,sha256=NcPkbuEtdoAJt4cRcMIc9NsLaCy0Ts6cwvRsJGSm9bw,925 +torch/include/ATen/ops/_validate_sparse_csc_tensor_args_native.h,sha256=BXGR_IOwW9unI1CPvxR7AF2yYD46V_Jzgc_R4m7eWo0,637 +torch/include/ATen/ops/_validate_sparse_csc_tensor_args_ops.h,sha256=TptjpTOgdPFfI7eww-KyQAy9yPN80IA4Dy-VjC6tRpE,1426 +torch/include/ATen/ops/_validate_sparse_csr_tensor_args.h,sha256=5kAv4h4uRKgLsvgeX-clnLlYpjVpR30SM7uxKZR6c24,1003 +torch/include/ATen/ops/_validate_sparse_csr_tensor_args_compositeimplicitautograd_dispatch.h,sha256=QCIUf6EXgSGjsXHKwnCtehaUsCelWwutDwKfVWbjBaI,925 +torch/include/ATen/ops/_validate_sparse_csr_tensor_args_native.h,sha256=uuxTeC-Cq_bGk2fMLbMs1H4KIDmy-Bk2ddBF7F_GFSg,637 +torch/include/ATen/ops/_validate_sparse_csr_tensor_args_ops.h,sha256=sXBIAArrNmv5iSv_3RCz35G4ouFboqVxasGt-UF6KiM,1426 +torch/include/ATen/ops/_values.h,sha256=jdE_r4eBJdigYNWpOJ-svle2j6C-X5flsoC25rm6Dio,504 +torch/include/ATen/ops/_values_copy.h,sha256=H1BlV2aS6hZpQQAlAOIdH5Ud68zayTQHndDE0-EokP8,1087 +torch/include/ATen/ops/_values_copy_compositeexplicitautograd_dispatch.h,sha256=GUoRJIEBtqQUJUch6ZeSi4AL4qcdBTcunlETGm79B7Y,879 +torch/include/ATen/ops/_values_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=Kl9X6GR53jLoaeVz6TmqnybLLozZ6eoMceGsJy5ECfs,796 +torch/include/ATen/ops/_values_copy_native.h,sha256=PCHNB5fHnbJFXx9wWPAdncT3jCSJ3Olj46rM7brSBV8,566 +torch/include/ATen/ops/_values_copy_ops.h,sha256=8y_wITqTA4IprBsvzpBu5U1ZmNgwOltS3dZrXn_ew_U,1583 +torch/include/ATen/ops/_values_native.h,sha256=cRczV12q-vFaOjymmJYEgS0qdHYESRLfx_HIwTP_ll8,484 +torch/include/ATen/ops/_values_ops.h,sha256=LKAGBeFNE_bIJ7_CnJoZtej530Jwudm-tvMVKYwf5vI,966 +torch/include/ATen/ops/_version.h,sha256=YL86fwg1P0OXJ8lO7wyj5GuvoMHK_Ie8B47_gdNcJx0,505 +torch/include/ATen/ops/_version_compositeimplicitautograd_dispatch.h,sha256=VQe1SgTRZUcc1Q89pAXI0sPrXrCmizxErnitfKmkEiw,763 +torch/include/ATen/ops/_version_native.h,sha256=VvsYSog7FaJO9ro7wOWy7r4xACEnbRYLLBQ_nBNicXQ,475 +torch/include/ATen/ops/_version_ops.h,sha256=E2n6CXY4F4gx-WvnBjJuprUzHB7dXFdn51a9d-To82g,951 +torch/include/ATen/ops/_weight_int4pack_mm.h,sha256=YGicMR7XYm1R6TU9fhXxx49_NY6ZeG4g_XBJHqfCHoQ,855 +torch/include/ATen/ops/_weight_int4pack_mm_cuda_dispatch.h,sha256=K9-5RIQv8I6f8j5D2IiPa1ppKIyJw6nbESXREfI4HDg,815 +torch/include/ATen/ops/_weight_int4pack_mm_for_cpu.h,sha256=SNAU7pJQgK3pmfXpGC2D8UBI3YCkrjvUkeMwwAt1630,887 +torch/include/ATen/ops/_weight_int4pack_mm_for_cpu_cpu_dispatch.h,sha256=hO3chnb2tbIkOisF2SWg3dKcrz2jZJptqWK8HZ-BKIU,821 +torch/include/ATen/ops/_weight_int4pack_mm_for_cpu_native.h,sha256=e7SXWCG_PFb4mLLXklgYh4i6t-UZX0ImPM3VbUWIZDo,573 +torch/include/ATen/ops/_weight_int4pack_mm_for_cpu_ops.h,sha256=MKRJ27PbOGMiR2Odvn8J0SFblc0nZ4XLgjzPU6UMK4I,1281 +torch/include/ATen/ops/_weight_int4pack_mm_native.h,sha256=EfBpQz5sob4BNFpfaDHPCSruBBHTNsslZWIepfcW9qU,574 +torch/include/ATen/ops/_weight_int4pack_mm_ops.h,sha256=XTCifQewET_DVc625NyAa-BzZDZIbSscCwOWdfMxVdo,1257 +torch/include/ATen/ops/_weight_int4pack_mm_with_scales_and_zeros.h,sha256=waaxGKMjTGulZCTghqzdNJptZS1nAnR1faILrUANcIk,969 +torch/include/ATen/ops/_weight_int4pack_mm_with_scales_and_zeros_native.h,sha256=DW69q74kL-hRSBtQODQ_1TtZD8Pa55kFQGY44qsx-do,422 +torch/include/ATen/ops/_weight_int4pack_mm_with_scales_and_zeros_ops.h,sha256=WUopOAW5nNrptzn-gRHxCt3li45Hja60awPHaVVGe48,1388 +torch/include/ATen/ops/_weight_int8pack_mm.h,sha256=C3uZfFBJhqqX9UoVsfueZl5BxpFv-AXEgyDOlOY9sS4,783 +torch/include/ATen/ops/_weight_int8pack_mm_cpu_dispatch.h,sha256=IIlfdUaGwPaqLLqivBiaP3DMLwu-zb-XbnKuVls06qk,785 +torch/include/ATen/ops/_weight_int8pack_mm_native.h,sha256=WTJ_H2z8Yl34_nNpmIMUwaq5Wg-B3zyx1kw9Vr53Cdc,545 +torch/include/ATen/ops/_weight_int8pack_mm_ops.h,sha256=41OtHppXyZIesSuEi3cWrJpLWnQd6f-BJMefIjHYhbs,1168 +torch/include/ATen/ops/_weight_norm.h,sha256=daflFKGpbdkAYVck-qhQnPyz2qTL3KLdEPkuZvRbkxg,718 +torch/include/ATen/ops/_weight_norm_compositeimplicitautograd_dispatch.h,sha256=72xdbSxcp0pSanEpxTjc7JParWoyAh7wxoJDIhhDgA0,804 +torch/include/ATen/ops/_weight_norm_differentiable_backward.h,sha256=Ppf9cGxy68dWtSSxp9Qb-bUDyd2xCur9KC_cFs9f93E,996 +torch/include/ATen/ops/_weight_norm_differentiable_backward_compositeimplicitautograd_dispatch.h,sha256=6FfonTNV7IpjA113GFW1ZGh3359dnWKChhuQSCHiOo0,922 +torch/include/ATen/ops/_weight_norm_differentiable_backward_native.h,sha256=BYXRRkC8XmUV79FuIpnsd_8kcgyMyF9Zhki7w5dndeA,634 +torch/include/ATen/ops/_weight_norm_differentiable_backward_ops.h,sha256=ZFrhZG-Fr2kJ015ae6kMVjyDzu3jvqDfLikSP-EZ3PA,1470 +torch/include/ATen/ops/_weight_norm_interface.h,sha256=SOohbgT6PHv6Sx9UpGr7txfChG6juBzYnclUaob_4hY,1563 +torch/include/ATen/ops/_weight_norm_interface_backward.h,sha256=WNs-wYTd8WmO1XSG9UKk9el7sOUvHdoahLBpVbrZr_Q,2096 +torch/include/ATen/ops/_weight_norm_interface_backward_compositeexplicitautograd_dispatch.h,sha256=KeuaJ5RYGGtOqiKBbBgAbdBRDmglt9vvzA-z4LOEIy0,1217 +torch/include/ATen/ops/_weight_norm_interface_backward_cpu_dispatch.h,sha256=Zt3kPCHxlkUOEBxVnVrB763dq-1XfgHOK298LrdueyE,873 +torch/include/ATen/ops/_weight_norm_interface_backward_cuda_dispatch.h,sha256=PAJU5hlv45Hf54SNcN2Z1RZem_7u0gczcNYAWs4tBsc,875 +torch/include/ATen/ops/_weight_norm_interface_backward_native.h,sha256=ezTCIFCy3P7B8RPiQ45cVbbBUZa5l9hGF-AwBabLWwg,1076 +torch/include/ATen/ops/_weight_norm_interface_backward_ops.h,sha256=6KG9eRru7s_SEcXYVC7REc5kflOoE9akDtbk6-PNXRA,2625 +torch/include/ATen/ops/_weight_norm_interface_compositeexplicitautograd_dispatch.h,sha256=9MFh2LBtWygir10PGloql-X_3tFJJCd727a62LCMG5o,1059 +torch/include/ATen/ops/_weight_norm_interface_cpu_dispatch.h,sha256=IjGW79hTAoGDV6hN7OUo1RYQSgdjeBYDSzhdeuccVa0,795 +torch/include/ATen/ops/_weight_norm_interface_cuda_dispatch.h,sha256=eyK0rAmzHhKbBk020hL59btp6wAppzXkZpKTQgcrKwE,797 +torch/include/ATen/ops/_weight_norm_interface_native.h,sha256=CHJ9TezP9Y6JmcxoVOGlBgpmhAReG38n-4pLhbp14RQ,840 +torch/include/ATen/ops/_weight_norm_interface_ops.h,sha256=EWKJ0ohZ1SCHgrr5q6k3gxTgQXqdyKSoWqtxvfj8_zE,2117 +torch/include/ATen/ops/_weight_norm_native.h,sha256=uMagnlVSuc1U3uybCpxABGQLWpuz9BoVn13YJoxDaTA,516 +torch/include/ATen/ops/_weight_norm_ops.h,sha256=7ZeqClzre5yYR8BPUPYGspeBoNRLda40zs3oZp5cgoE,1086 +torch/include/ATen/ops/_wrapped_linear_prepack.h,sha256=d1MzulpZgWX75LpsMQwZ7oVfWjFYVBGhiTpyhyeDLUE,906 +torch/include/ATen/ops/_wrapped_linear_prepack_compositeimplicitautograd_dispatch.h,sha256=5vrvhqpddG2hILaeqUPI7hN8mO1S5b8guIgHqN_GTss,879 +torch/include/ATen/ops/_wrapped_linear_prepack_native.h,sha256=f4YGXiHKSR5TPpS4OWG8Laa9eQ-nWleAJoMi0xlkXpE,591 +torch/include/ATen/ops/_wrapped_linear_prepack_ops.h,sha256=h-ssWiWwg18MnnCc7IGUk5nOs8-GBdLrDmoSv2PtVa0,1326 +torch/include/ATen/ops/_wrapped_quantized_linear_prepacked.h,sha256=oKbK3wnPC0XfcSGz5C4xTMzGF45MY-xnH2ue---vFo4,1174 +torch/include/ATen/ops/_wrapped_quantized_linear_prepacked_compositeimplicitautograd_dispatch.h,sha256=qLa8p8mkFN-lUH3MPRz6iqq2LC19fgOhK2WFCV1UKM8,989 +torch/include/ATen/ops/_wrapped_quantized_linear_prepacked_native.h,sha256=5pfXMh-H53AWN1aoI67Brz0hUug47LrGN_5n3OV90Ic,701 +torch/include/ATen/ops/_wrapped_quantized_linear_prepacked_ops.h,sha256=NvujKTbTaPXYetA5jE1R9Pd-dQwezqrIq2LUnNJMawc,1677 +torch/include/ATen/ops/abs.h,sha256=kbbPzQIYeXQANmQISBSsx0Ntz13lv49dszNDNY8uylI,1130 +torch/include/ATen/ops/abs_compositeexplicitautograd_dispatch.h,sha256=Sd14UZRfkTCXhNMw0kzSi9_XNtp_kwSBd-kAPnpUWrY,809 +torch/include/ATen/ops/abs_cpu_dispatch.h,sha256=guyZY2PA6mb3ZW8ApEw8_dWj_yPTUfHqFD_ZjjHBv6k,817 +torch/include/ATen/ops/abs_cuda_dispatch.h,sha256=BH8wfKMo0aJ9_ZeRiRbjXCGIaqjc0dxQLHjJZtpvY_k,819 +torch/include/ATen/ops/abs_native.h,sha256=OK0rGfo2raMc60D3rln3nvpLqUnivApOCHmQVmuMSpg,1123 +torch/include/ATen/ops/abs_ops.h,sha256=yziG-2lbzXCjiYlgHWGMf_EQBxgiEpjdWoML2xH5k50,2019 +torch/include/ATen/ops/absolute.h,sha256=Q5CuFeUAop8vN1HDmNfRhPz5yHytMgrwaatN4pSHt0E,1047 +torch/include/ATen/ops/absolute_compositeimplicitautograd_dispatch.h,sha256=aCllNnGdo-W3Mi8_NeGVbjB8PQch7PJaQDUuuWTz_mg,980 +torch/include/ATen/ops/absolute_native.h,sha256=-js-9F-8QhvbWG4ctj2yguw85xYb-xTPEXvty0LMXjQ,611 +torch/include/ATen/ops/absolute_ops.h,sha256=lqYoPyJswexwctbdxJhz9Xuz_38rOI7ExNF62wZiWog,2064 +torch/include/ATen/ops/acos.h,sha256=SHT-hrEv4LSOPFnDco8lbdEaqTgPeCU2DyWeON0NMDk,1143 +torch/include/ATen/ops/acos_compositeexplicitautogradnonfunctional_dispatch.h,sha256=7m2UUrN6V0OifYWhq8Y4JdyeU27SaP4jsE7yeFNDkno,837 +torch/include/ATen/ops/acos_cpu_dispatch.h,sha256=_mWbzBse4usazcAkWTyaYcigoywudsk0Of_dlUUMyLs,920 +torch/include/ATen/ops/acos_cuda_dispatch.h,sha256=nHWrZz-0VZnbDGWKYT1YoO0l8UJXhjLl_bd_QxTFC6o,922 +torch/include/ATen/ops/acos_meta.h,sha256=ZNEnK_d7WZcJkoYa-MnJJ-0ITEXJBp0pSdtnPa_KG34,565 +torch/include/ATen/ops/acos_meta_dispatch.h,sha256=emksaFT8ie8u9RZaUXkWI1PmR1bLCQhFbM1xnO49M48,922 +torch/include/ATen/ops/acos_native.h,sha256=EsgIgrGX9gn6xHeLl-u4-vlR1UMbCO5gbM1SqZ0feoc,590 +torch/include/ATen/ops/acos_ops.h,sha256=W1oU4ZbrNf0rakFGlGV6HS-CDx5dw_0LHpW1PQ6DBaU,2028 +torch/include/ATen/ops/acosh.h,sha256=Tkh3qHFyQgWEddjEoR72mMbyidyHovnys1mf1NDqfHc,1156 +torch/include/ATen/ops/acosh_compositeexplicitautogradnonfunctional_dispatch.h,sha256=lvTlS6z_YYckApSKjIHMwcGS_4rHdhYtbyohz0Wlw6o,839 +torch/include/ATen/ops/acosh_cpu_dispatch.h,sha256=tSsT1qwMLbVIY3CHXWHQ4mi3yULlk6jglSXeOdIMzME,924 +torch/include/ATen/ops/acosh_cuda_dispatch.h,sha256=-xX9J6cRkppVrvGeEfSGMNPMQDEisgTvt4-eht5YVfM,926 +torch/include/ATen/ops/acosh_meta.h,sha256=9RRorVu1CZCuUH7SDzDUO0TTOonU_LrrJrlnDMXf-ks,566 +torch/include/ATen/ops/acosh_meta_dispatch.h,sha256=xHl13gBpTEhvg3VCexx1NtNv9vfED9X07Tk57EBaYQI,926 +torch/include/ATen/ops/acosh_native.h,sha256=QxiJ-zMzbW-2UM0WEHZP4hzgvM8Y6be3uFDEyZLisp0,593 +torch/include/ATen/ops/acosh_ops.h,sha256=ayGFGt_mEr3q5kRVYn3FFChgG093neJPqcRkR_2-C5o,2037 +torch/include/ATen/ops/adaptive_avg_pool1d.h,sha256=EVuYnzxHjWXgR8GeaVBCH2QSTLKU2Q5R2HQ_Pvn4zBU,1343 +torch/include/ATen/ops/adaptive_avg_pool1d_compositeexplicitautograd_dispatch.h,sha256=FiIbkS9o3TYKcZsNQiObKcj8BdxzKBonTiO6vIjJcCs,951 +torch/include/ATen/ops/adaptive_avg_pool1d_compositeimplicitautograd_dispatch.h,sha256=doSzMvk_5-_s1cYEsx_hdPOIlJnNA7bM-7ilOjVir4M,806 +torch/include/ATen/ops/adaptive_avg_pool1d_native.h,sha256=_qD8zOGhDf4AzsPDnB71eIhSV_t_3zK-zYWW9BMtwNM,638 +torch/include/ATen/ops/adaptive_avg_pool1d_ops.h,sha256=b0B6ntLS0N5EP9WfV_P9IKyWiNzIvwFM8DUbXl_NHF0,1815 +torch/include/ATen/ops/adaptive_avg_pool2d.h,sha256=O2IAmMVSGZho6JL2XAQex8gaRcpXof30hHRn6ilJv7k,4110 +torch/include/ATen/ops/adaptive_avg_pool2d_compositeimplicitautograd_dispatch.h,sha256=t58FI59zAhX2cTuIzvks0DctrAqrGLo1xp3oPn2oHAo,913 +torch/include/ATen/ops/adaptive_avg_pool2d_cpu_dispatch.h,sha256=TgCv13yCD1tH1Vq7ThKkaZdUxNJu5Rwe2byz8WiwuQg,1170 +torch/include/ATen/ops/adaptive_avg_pool2d_cuda_dispatch.h,sha256=wmhJw_rDfYi6KQPTe9931Gl83GKVUl30uRpAYbvyikM,1172 +torch/include/ATen/ops/adaptive_avg_pool2d_native.h,sha256=nh2FFzDPh-3euadEZT8bSMZ8giDBd9gMKrgg7-HItg8,910 +torch/include/ATen/ops/adaptive_avg_pool2d_ops.h,sha256=wRzRqfB9Gdmg46f1OJ2q4rTf809hD7OTxmEsW116fgs,1845 +torch/include/ATen/ops/adaptive_avg_pool3d.h,sha256=hXWHNBVx03lv4zKwxHaP3CpWqXdK_nnj2Hv1VXsqCHQ,4110 +torch/include/ATen/ops/adaptive_avg_pool3d_backward.h,sha256=6mo0NRLQuoFbW8Rz6MFZdHcgVOwPkSPyO_jPZDSxwA0,1246 +torch/include/ATen/ops/adaptive_avg_pool3d_backward_cpu_dispatch.h,sha256=v6wC2GCnAzBHhY85cfPPRnbOCOto6mPhp_VFvACFfwE,945 +torch/include/ATen/ops/adaptive_avg_pool3d_backward_cuda_dispatch.h,sha256=bFu-IufA6K9L-EadptOXvHGFDEbn7IevfhsZbXs-HQU,947 +torch/include/ATen/ops/adaptive_avg_pool3d_backward_native.h,sha256=er_kSHYtWj6Me1Bp4bctwXEnT-tNkgNzFuJfzHEwiiM,709 +torch/include/ATen/ops/adaptive_avg_pool3d_backward_ops.h,sha256=jYJWoQsk7ANounSUFSDrKBNa6CTlB-tUT3Q3eGRVc4s,1259 +torch/include/ATen/ops/adaptive_avg_pool3d_compositeimplicitautograd_dispatch.h,sha256=aK7I9UhQVql8xucJeUbx0XkuiwbGgAkAJtTPpT20xc4,913 +torch/include/ATen/ops/adaptive_avg_pool3d_cpu_dispatch.h,sha256=Oyft_GQQdDU8Sqq-4DhG7Gw8LiRQdcJ1IhY5jpDS38k,1170 +torch/include/ATen/ops/adaptive_avg_pool3d_cuda_dispatch.h,sha256=Jgk69BHO0qGFGxciujaApOHckujk_uuf5w5_VFjGzGk,1172 +torch/include/ATen/ops/adaptive_avg_pool3d_native.h,sha256=KJQVJqq-riKj2MMldj9UYjKEiRKVPUZ-8vmzP5ZOO8I,912 +torch/include/ATen/ops/adaptive_avg_pool3d_ops.h,sha256=amdqO-bvCMdNMWv3reiJIGNcVkiv0aYFOMOYD8RM8iw,1845 +torch/include/ATen/ops/adaptive_max_pool1d.h,sha256=fSIPllLkIL9j5lRU5KK4zGyKclCzrbtwyjKFf_VKWDg,786 +torch/include/ATen/ops/adaptive_max_pool1d_compositeimplicitautograd_dispatch.h,sha256=te6hl7u04jOMEM6Bv0pOyQ6fnJh6Nyh8iOIH_BpU5Rg,831 +torch/include/ATen/ops/adaptive_max_pool1d_native.h,sha256=8VjSa5KCBKxtNd0AsZtzMiqaieIoA0GM0meX4mfhMnE,543 +torch/include/ATen/ops/adaptive_max_pool1d_ops.h,sha256=ag8_hBoZf9Mu1W4VJYjN0LECLqCSlrsFJt2na7wH3qI,1176 +torch/include/ATen/ops/adaptive_max_pool2d.h,sha256=hyIV6lTgOOn55_7dPECAPFQOrqnUdmgpN1aHROnt6KA,1562 +torch/include/ATen/ops/adaptive_max_pool2d_backward.h,sha256=slp5jVT9rMLh-zztsZGTz57TyLkqUY9MnnYcGklT85w,1671 +torch/include/ATen/ops/adaptive_max_pool2d_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=Vcb_6ELEGK2EcZofQpiLp4hyAwqio5Pz2wUS6zOfofE,872 +torch/include/ATen/ops/adaptive_max_pool2d_backward_cpu_dispatch.h,sha256=Y92LqbI_PYF_A_GHOSML0rm7ZhoXlOBk9ozXUT_qg88,1137 +torch/include/ATen/ops/adaptive_max_pool2d_backward_cuda_dispatch.h,sha256=QSGHqMXSuCoFT4nX8HvDpOpkkrCIdazlvatyzS5gcog,1139 +torch/include/ATen/ops/adaptive_max_pool2d_backward_meta.h,sha256=STQy20635EJiFOw0cyCXcjaEeJQoAklldJCFf0ZvnUk,649 +torch/include/ATen/ops/adaptive_max_pool2d_backward_meta_dispatch.h,sha256=16jjySoVenD0G_MpOlRxESreNPcV25gUZpzEXeU566U,1139 +torch/include/ATen/ops/adaptive_max_pool2d_backward_native.h,sha256=fD23fMBRq2Wia0lVaKBUEiSCm9Dx_bpvObq7Ai7CEIg,990 +torch/include/ATen/ops/adaptive_max_pool2d_backward_ops.h,sha256=ui7k-pZmi9_bFIN7jdCE97PjfTB6gR65GujkJaAgAdM,2113 +torch/include/ATen/ops/adaptive_max_pool2d_compositeexplicitautogradnonfunctional_dispatch.h,sha256=aTrCt6bg9EtQzFEIUGk94x14eDa7xcdTcRci9zhBGcs,857 +torch/include/ATen/ops/adaptive_max_pool2d_cpu_dispatch.h,sha256=3rjzlZNn-GDG5yPtmg3ukZuhuh-4mQ0lD9lGKK7F9Ao,1126 +torch/include/ATen/ops/adaptive_max_pool2d_cuda_dispatch.h,sha256=Pe3PvthDJrktjTKO6Xn0pfbQk7IfHyLP7qsUYgJv8kY,1128 +torch/include/ATen/ops/adaptive_max_pool2d_meta.h,sha256=cqXthGktpRku3odhFYEwOIJqIDHaGpQY0eIalAVTmjY,609 +torch/include/ATen/ops/adaptive_max_pool2d_meta_dispatch.h,sha256=ojHCji9QFmLNqeB4XyiNUTv6h2IB54lyusJ2LxdjOJI,1128 +torch/include/ATen/ops/adaptive_max_pool2d_native.h,sha256=0s5dHvdyutBGEHJ0ouiXa-IQ1j91I-FotRvYTbsB06s,925 +torch/include/ATen/ops/adaptive_max_pool2d_ops.h,sha256=xm5U064EUbfyZV6xzmxyo6XKuiUEyhldQ6fM6Jq5WKQ,2073 +torch/include/ATen/ops/adaptive_max_pool3d.h,sha256=tbYQRCfHuGYsTeg2ofsDemUbscbhRTV1VZD15T-jCvo,1562 +torch/include/ATen/ops/adaptive_max_pool3d_backward.h,sha256=pZUUd8PcjkE4k2IUJqJ8svI7aBsdojYwfr8RSO83o2c,1671 +torch/include/ATen/ops/adaptive_max_pool3d_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=eNU_1o8hj1AWYfKIUIFDFYSwZ7sv6zXgVTnUsAdjMbY,872 +torch/include/ATen/ops/adaptive_max_pool3d_backward_cpu_dispatch.h,sha256=EmjSFgOSv3ieAJv_OjabIyVNFqdwHKxjAUzf0doAWWo,1137 +torch/include/ATen/ops/adaptive_max_pool3d_backward_cuda_dispatch.h,sha256=S3-VDdYDIrjMImEjeWftxuEVppzYwoFX85UPPNj1cf8,1139 +torch/include/ATen/ops/adaptive_max_pool3d_backward_meta.h,sha256=QdhKMs6tYcBf2HSaQkzZ36dTHHUQKUhNzvO2rtYMFNA,649 +torch/include/ATen/ops/adaptive_max_pool3d_backward_meta_dispatch.h,sha256=c2UMye4JyuLsqMW4LU-tlaqJ6edEcXKOnsKUUWMCPyM,1139 +torch/include/ATen/ops/adaptive_max_pool3d_backward_native.h,sha256=99Fz2KJgJVkgjl0MGxVfbpuqOnXXGnlUyCJyAN1D3xQ,990 +torch/include/ATen/ops/adaptive_max_pool3d_backward_ops.h,sha256=Gq3Eat0TxaLDbd8jt7VogKH6XxzkgiYuPvx7pVnR0mQ,2113 +torch/include/ATen/ops/adaptive_max_pool3d_compositeexplicitautogradnonfunctional_dispatch.h,sha256=lfPoQeG9XlF2KFsoXnSts9bueIh1KRiml78ivhBVVHY,857 +torch/include/ATen/ops/adaptive_max_pool3d_cpu_dispatch.h,sha256=Z6Z2a6x6qbbDFeMJOOAWiEG7yW11lu92JM7Kj2hv8uw,1126 +torch/include/ATen/ops/adaptive_max_pool3d_cuda_dispatch.h,sha256=9uMJCaI-YgJByAnN-lPBe5ZifV3rVgCN41QsLv1ILCE,1128 +torch/include/ATen/ops/adaptive_max_pool3d_meta.h,sha256=fWz9rZQF0_S7zm7sjZqEtxs6MSUVVqpqKMtTuZRwNRI,609 +torch/include/ATen/ops/adaptive_max_pool3d_meta_dispatch.h,sha256=aWkrLvG6luVX0kuPCRDyNaT-LGV3LZdCMtRE6xG_jlU,1128 +torch/include/ATen/ops/adaptive_max_pool3d_native.h,sha256=8omndOUlUGiSt8QpqLt6RZMzJZa0N1GTa7I8Rkc7Tso,925 +torch/include/ATen/ops/adaptive_max_pool3d_ops.h,sha256=pqI7MNodgHsL0lBWar2dFfHDF7TneAQgLE_vxZlpJcM,2073 +torch/include/ATen/ops/add.h,sha256=XmL3jnaTKHSMhKazig3-3Ctn4v1r_XCZVbFBwfJ_mhU,2138 +torch/include/ATen/ops/add_compositeexplicitautograd_dispatch.h,sha256=j0HNl6Gh438d0W_bXbYoOQRHdwqomzXlfil9BYvvW3w,1174 +torch/include/ATen/ops/add_compositeexplicitautogradnonfunctional_dispatch.h,sha256=xTZVQnxCa_zJUKytiGdSGOUDu7csGECwJVY26eQTkP8,943 +torch/include/ATen/ops/add_cpu_dispatch.h,sha256=1WX_RIqo8xb2cnpUm-ShjUWP7y6QOlVmU5OAfQBLQCo,1130 +torch/include/ATen/ops/add_cuda_dispatch.h,sha256=CCTkCi5g8XV-DJTx8SXCaiER0lDZfZjmcq-sfaCaJ-w,1132 +torch/include/ATen/ops/add_meta.h,sha256=DW8GHQbzN9PJOJOHvyzIg0tKxYyjzrqV3YNu7iM9CWY,623 +torch/include/ATen/ops/add_meta_dispatch.h,sha256=niarZ4Y-q3sdm4bTnG7umOl9AACTzkOZ5qp9JcRXkiI,1132 +torch/include/ATen/ops/add_native.h,sha256=fgUdSiN0tdi1WG18a2vhAJ6_GC51ZEcFcrEDYui11FA,2938 +torch/include/ATen/ops/add_ops.h,sha256=x0nTSmLM2xjXfrSdCNttkYX_pSuerfuc18G8n32SGe4,4732 +torch/include/ATen/ops/addbmm.h,sha256=bWBMw7rH_v8kamZx4KPzr7T63dy0yCq64YqJniNeXMk,1623 +torch/include/ATen/ops/addbmm_cpu_dispatch.h,sha256=9K63lzHWiGQKkohJm6rdZV-s56Ji7lSTExuQwyztFPY,1360 +torch/include/ATen/ops/addbmm_cuda_dispatch.h,sha256=fl_i4wrG3FQLzCM9GGRx0FLBfAl_oTBeaIGmLkoURQA,1362 +torch/include/ATen/ops/addbmm_meta_dispatch.h,sha256=u6BqOgk1T64Kf8Y3Qfy1RdHkdU028zTEV14g2WMLtsM,828 +torch/include/ATen/ops/addbmm_native.h,sha256=94J8RBl2UEt8U5DG9cSPKW80DFawOvAtKFKtbpu_zw8,928 +torch/include/ATen/ops/addbmm_ops.h,sha256=yy7cxqvokWy9CHsBJYb1sUvo_oVHLUJGuIUPQNz2_ok,3105 +torch/include/ATen/ops/addcdiv.h,sha256=on8EhVJgQi29zYWxYiUaWGr5I0PP93U70Kmah2awasY,1509 +torch/include/ATen/ops/addcdiv_compositeexplicitautogradnonfunctional_dispatch.h,sha256=O2hmGLuG1uMX3QbWpkkKUxG1bTQVcsdR-WGXc6w1lYE,1011 +torch/include/ATen/ops/addcdiv_cpu_dispatch.h,sha256=UaOpX_jcOji4dy8OyAaG2jeBPeI7uiAFWRK7XXoESE8,1266 +torch/include/ATen/ops/addcdiv_cuda_dispatch.h,sha256=RmmcfK8nLzBaKunJk7MRunpuI9gNerwY9LMWPkEavkk,1268 +torch/include/ATen/ops/addcdiv_meta.h,sha256=4RSNScmjJFGe0Gn18X0WpM5oKE6kTO-z8RYWVoXRYek,650 +torch/include/ATen/ops/addcdiv_meta_dispatch.h,sha256=nQI-eU_kFhkVcFFcIiJKlQwyBBtEtofhCUuwCrRspfM,1268 +torch/include/ATen/ops/addcdiv_native.h,sha256=PYhAHk7-rls0blvlyaVuqmaDLJGzcW2UmqDY2TfPokI,681 +torch/include/ATen/ops/addcdiv_ops.h,sha256=Rb2WkkWtSH24-oCRGlXjG02G3FOYv_07aVzr9kNr_Lo,2877 +torch/include/ATen/ops/addcmul.h,sha256=thcWRT_A3A0Utke-62g69r7ftJdazusr602cbU6PmX0,1509 +torch/include/ATen/ops/addcmul_compositeexplicitautogradnonfunctional_dispatch.h,sha256=iT2tdmTUBkdu2qGNWVuVXkiHs-MbXEM8cJYx91DNX3Y,1011 +torch/include/ATen/ops/addcmul_cpu_dispatch.h,sha256=TrQXPeEDv-tDOenvoO63lTWo70r7tq12JnITApzhDHw,1266 +torch/include/ATen/ops/addcmul_cuda_dispatch.h,sha256=KyvIz560P8S3-Keno6f-PLeAFGSofTSWhZ_cvfOGj3I,1268 +torch/include/ATen/ops/addcmul_meta.h,sha256=RHnecWIOgFIPbFRIuyRq9OBlzhFhidT3eWM3MzQo45g,650 +torch/include/ATen/ops/addcmul_meta_dispatch.h,sha256=BZKfPpgkZ094ykWvhuxXt1Ek3LgyZeN-2ZffHuSiR6U,1268 +torch/include/ATen/ops/addcmul_native.h,sha256=v9ozfiEoTKJmPaslcUkPVIGMAG6fDPEicmbpdPMnfko,681 +torch/include/ATen/ops/addcmul_ops.h,sha256=lz_PJPlY_LMxzIRLOCEdbdO0JcKhRh7tZSzH6ENO4Cs,2877 +torch/include/ATen/ops/addmm.h,sha256=XfE7OdJyLdt1BMurPjvYP-5QiOU3zRSHAcB0z9cKQzg,2866 +torch/include/ATen/ops/addmm_compositeexplicitautogradnonfunctional_dispatch.h,sha256=jqOudEZYHHcOXTWtPbeFfXnW6ObyVxS1l1KueE_7JPo,1049 +torch/include/ATen/ops/addmm_cpu_dispatch.h,sha256=hiyK_jE3TrYjxzoOuu675SWApWms-hKL-knXeGmOlKo,1340 +torch/include/ATen/ops/addmm_cuda_dispatch.h,sha256=EVyNv26e4Bc5g7ZQIJhH8301NbWuKMy9fcbZGhRLsnU,1939 +torch/include/ATen/ops/addmm_meta.h,sha256=kk9BVW2lZcBn0uU3IDIBnLPelffJ8WU2sFRb6ds85Uk,667 +torch/include/ATen/ops/addmm_meta_dispatch.h,sha256=fFBtcWp4A_kC2smXp662y7hLAQShxAuS0kCET8rNK8g,1342 +torch/include/ATen/ops/addmm_native.h,sha256=Ng_oJ-9EgU2PCM2b-RREdMpr553zC3GRFJ7FhktWMPg,3029 +torch/include/ATen/ops/addmm_ops.h,sha256=3LFofM6uMC1Wy1ds8y7v9Kjd3oqWQna6uprMeLbvoo8,5040 +torch/include/ATen/ops/addmv.h,sha256=aNBpqpWrHuySRwTwwIK2UD6Pgl_s6MfAuU2N5HWAkq0,1882 +torch/include/ATen/ops/addmv_compositeexplicitautogradnonfunctional_dispatch.h,sha256=fcvwwlmcROsF7RMPFKZrAkt22mT4-tLpx7367_thNDw,1045 +torch/include/ATen/ops/addmv_cpu_dispatch.h,sha256=f8GvqB9C5_E_8LJJtnhS2DmfZZsmaP3EA7zoGCVrlg4,1332 +torch/include/ATen/ops/addmv_cuda_dispatch.h,sha256=hXvCv9CLCih6tx03U4lhu6U0Q7s7JmSpoDeGwG5R5co,1334 +torch/include/ATen/ops/addmv_meta.h,sha256=Zhl_OuTJpkS9p6QYcep7P21V21sUFm0wWd-VfXjZn54,665 +torch/include/ATen/ops/addmv_meta_dispatch.h,sha256=jzzplhFZukrmnODgPZ1HN30Xixnu3aYzw9ZtStN-FgM,1334 +torch/include/ATen/ops/addmv_native.h,sha256=LHj5wLVGFKe3alwquDsg4nHO9TZRSKqssy2Y9EYCRS8,1332 +torch/include/ATen/ops/addmv_ops.h,sha256=QpKpMLZWas49UMoDLUv8PTZbwIOrn9UbdgrIlws9XPA,3042 +torch/include/ATen/ops/addr.h,sha256=ObVC4A2l8RbXNDu98_LmF2UPGQGDJAaB-tmhX2W76Kc,1567 +torch/include/ATen/ops/addr_compositeexplicitautograd_dispatch.h,sha256=bOc7NyFek0_nkLCbJp0hBRLou_qHwoFK2PrMA6IGMjI,1380 +torch/include/ATen/ops/addr_cpu_dispatch.h,sha256=jYQdOB0bS-rn2re-pqcnomYmtsNbHKDmREn4YQDkkj4,1182 +torch/include/ATen/ops/addr_cuda_dispatch.h,sha256=1OJL2mngSnw4kZoIgvmeiXg8qVmXK0F3-Z1zr8qlRW0,1184 +torch/include/ATen/ops/addr_native.h,sha256=7EWY1bOXX5kRQtQsmwebwLNA2O2uKKbCw8Zhj_w7PSk,1254 +torch/include/ATen/ops/addr_ops.h,sha256=h0PP1FtlDyOepiXn-EhrOsJnak7WQ2z0ovZI0cgiMhU,3051 +torch/include/ATen/ops/adjoint.h,sha256=Q2t8RpGgq60tF47-46X50f7N9qv0_aivz7cA43FoJ70,647 +torch/include/ATen/ops/adjoint_compositeimplicitautograd_dispatch.h,sha256=BesD70OxuwD2C4d9wA1ZThjrS_jgl-zwz2NcDz_mi3Q,765 +torch/include/ATen/ops/adjoint_native.h,sha256=aTCG1dA7VAV-HkzDwc40oe-VKg17rm7T20Ee4ifODGU,477 +torch/include/ATen/ops/adjoint_ops.h,sha256=FocQL1wPhSiAjqvMV--MaOCYQwdYq50_zSxwMrCuH9o,966 +torch/include/ATen/ops/affine_grid_generator.h,sha256=hSapgm_hNvPdGEBIH0aR9Y3_tZ7Be4SdpM2_XAI4h-4,4526 +torch/include/ATen/ops/affine_grid_generator_backward.h,sha256=pzaNGVpKgx7VMhLAQlUfl9bwjjdx58gqTIpxyzx8WAM,1817 +torch/include/ATen/ops/affine_grid_generator_backward_compositeimplicitautograd_dispatch.h,sha256=1YaTzIDDslmB_kRpQNu_XINLKr5hJbULjycu0w-JK4Q,961 +torch/include/ATen/ops/affine_grid_generator_backward_native.h,sha256=4bYUiIIpiG73M_jvIncTcgLh4plKtLKpJ_JYHrTZEdo,542 +torch/include/ATen/ops/affine_grid_generator_backward_ops.h,sha256=xf71Vns7oQ1Atqkk4hDJhTn6Nbbk0-qXYfGatNbne2A,1183 +torch/include/ATen/ops/affine_grid_generator_compositeexplicitautograd_dispatch.h,sha256=YSde4i--4aHYeOubjnopPAqprHHj_-BBFgMM0Jn2m48,1513 +torch/include/ATen/ops/affine_grid_generator_native.h,sha256=8vdvvWorzFVmnkUyX0pRIi-kal7l329wjtwXl8GLfeg,681 +torch/include/ATen/ops/affine_grid_generator_ops.h,sha256=K-omoeS-rz5GP4gu-6fLUIEiTepyxVtBm6R2UFYg4XI,1951 +torch/include/ATen/ops/alias.h,sha256=qV0deCU6ZYVzDbJjVoSHVieyYRRCuGAM_Zm-eIYLLYc,639 +torch/include/ATen/ops/alias_compositeexplicitautograd_dispatch.h,sha256=6VotKbMM5R0GKEdKbiGXEYg61aUVwBkp8UCnJpub_h0,763 +torch/include/ATen/ops/alias_copy.h,sha256=vCy7U5YArwl00uhigKqShp5X1Gy8t0lbU90HSADAarc,1067 +torch/include/ATen/ops/alias_copy_compositeexplicitautograd_dispatch.h,sha256=5WlJxs_AfbbyZgIoQOn5WluDQFgHf-hth5uNZAKiUpk,875 +torch/include/ATen/ops/alias_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=5igB1i3qMZZwWLGxwYjkaXdz9ocfcYrbOyoil58SKWQ,794 +torch/include/ATen/ops/alias_copy_native.h,sha256=QQ3pu9APKWVyq7YSW5wY_HuFBYbLdPb_z5MVe9nnkfc,562 +torch/include/ATen/ops/alias_copy_ops.h,sha256=MmjokSsjQdUWK6LZFqUTdQPdvym5NfUSwg4gXCK3-C8,1571 +torch/include/ATen/ops/alias_native.h,sha256=dnS4OjTrbbgMDtUuqUerKu41PsuU6fYAADG8B2g9_Ko,535 +torch/include/ATen/ops/alias_ops.h,sha256=e8l2YKkA7hxefohO-PAVwhIX9u4WpgiakIE6WyHVq70,960 +torch/include/ATen/ops/align_as.h,sha256=BZ4jTF9B7Ke8HriiWi9rN-TS14Kdjrxko2tsw5Rmu_w,505 +torch/include/ATen/ops/align_as_compositeimplicitautograd_dispatch.h,sha256=nb-cMhkOmO8r21o10vcHCUTjuffjRoyvRLWGU21kApA,792 +torch/include/ATen/ops/align_as_native.h,sha256=gy1YVg2CKI1VOWaz-3A_fgCTjbvpyF470dlRd7TMi_0,504 +torch/include/ATen/ops/align_as_ops.h,sha256=m3xSzLWCJ0yYy-Ou6f-fLHeZQ2WMY3r8K2UjDRemYRQ,1049 +torch/include/ATen/ops/align_tensors.h,sha256=25Aa0k-o9i9SXOh1N1RXPqCVL6PdzkfOTkoHLI3mEDY,689 +torch/include/ATen/ops/align_tensors_compositeimplicitautograd_dispatch.h,sha256=rU4xlJF6Io3cOrjdgnyjT0rdhqC3YJsWrxQF0FxX6sc,785 +torch/include/ATen/ops/align_tensors_native.h,sha256=lQeNZGXIaajRfP_hgG8S9PiDwc6guf8AH8Q0_Vd8iAE,497 +torch/include/ATen/ops/align_tensors_ops.h,sha256=HXqYfKKkLJbdhZ_2qhWmB9s86oCaKli1NUEqltOCOQc,1024 +torch/include/ATen/ops/align_to.h,sha256=vRSuaVlGtDHywc59-lMrbkXmpyMeK0A9y8cVH8cn1K8,505 +torch/include/ATen/ops/align_to_compositeimplicitautograd_dispatch.h,sha256=mB3Eed8SeV_HwN3Lo0_01loyRCJ5u70IzWGRL4uzlzs,890 +torch/include/ATen/ops/align_to_native.h,sha256=nF6ZBvbH-wUMMco3QjcQu7wvBhKzlYb2ZR0HYy2Ujfk,602 +torch/include/ATen/ops/align_to_ops.h,sha256=OR1TLAAlkBkd43rinoeAiw4E-Ib0mHwshOB4U76yRpo,1750 +torch/include/ATen/ops/all.h,sha256=THMK-Xo5wJlDgI3hSPY5ty_V8BXK9nLaGTDHrNjoto4,3355 +torch/include/ATen/ops/all_compositeexplicitautograd_dispatch.h,sha256=-_BR4CX8sA0M6UOMb_ZQtlBnDvtyDVf-eCOnhlXQ5PE,1053 +torch/include/ATen/ops/all_compositeexplicitautogradnonfunctional_dispatch.h,sha256=O8k4zyELAzs9wZSRkvhyxBeMpgOHW_LBnWeSwdgLU8s,971 +torch/include/ATen/ops/all_compositeimplicitautograd_dispatch.h,sha256=kQxvnzw9ZOK7Dl_a4eGZ14boarkNjAn83f9NDPoP4DM,1017 +torch/include/ATen/ops/all_cpu_dispatch.h,sha256=VEvzY92jUf1eH3De9dEu2qeBygt5u-zwB2e-UmhQTGw,1506 +torch/include/ATen/ops/all_cuda_dispatch.h,sha256=hLSwkUrnjWwnVGoZTeAK1x6ihYdCBadIcVHptJe5B4E,1508 +torch/include/ATen/ops/all_meta.h,sha256=-Il-B8-IZ7YqW1wxdbpwD00IlugZKancPvT7CPtH6-I,857 +torch/include/ATen/ops/all_meta_dispatch.h,sha256=gI7mXiscbMIjlHsW_c7wyYBNEzTjF0ipFOmn14rw7_k,1508 +torch/include/ATen/ops/all_native.h,sha256=JdKAqD1WO-UoQGb5bSOdgKP_6uSnNunRdxr14GwB8Zg,1495 +torch/include/ATen/ops/all_ops.h,sha256=D6lTSb5yxoYCkCBkZal_mwUPqHiWy9l6w0zlfFwezlc,5576 +torch/include/ATen/ops/allclose.h,sha256=KFEY6qT32l4LLZ6atsmdKalkIWhoVSF8LOhPrsGZ3ao,825 +torch/include/ATen/ops/allclose_compositeexplicitautograd_dispatch.h,sha256=qPU-HKtBOEw9esjjAgBPDcIbEW-M0vvEYcmaMjpiDBw,846 +torch/include/ATen/ops/allclose_native.h,sha256=jZpfcqkQfVXvyp7BA6Hu0lZue75KU5mLcvk0QFPkedY,558 +torch/include/ATen/ops/allclose_ops.h,sha256=d2L2YtINbEJJ1EFjCkhE1RndfMWjLllBVIw2r4S-Kco,1193 +torch/include/ATen/ops/alpha_dropout.h,sha256=FeaTDlB1Syg2IusO1qrUivvNAeWj3vnXCn2OBZUk1mA,937 +torch/include/ATen/ops/alpha_dropout_compositeimplicitautograd_dispatch.h,sha256=1rAd24UqhI4E6zicZY2T-xcYYa89_UNOgVLns3eX35o,874 +torch/include/ATen/ops/alpha_dropout_native.h,sha256=a6ZEnqLXSJsuaKaKuWZM8jRRA2OSz32MeZIGUFm2XSY,586 +torch/include/ATen/ops/alpha_dropout_ops.h,sha256=_C2S8vaODKBvI1MzPtCLL3YuH4f1_O_wU5rz_Dj4L4c,1659 +torch/include/ATen/ops/amax.h,sha256=U2fKdI2n2vB_HS0m8v_I5Uid0HbAlNM8g3lTo2G2dNA,1277 +torch/include/ATen/ops/amax_compositeexplicitautogradnonfunctional_dispatch.h,sha256=dhhdZ0veuV8ZJgob87RF2br8KyyKQ7_hHv3hKNw2nrI,832 +torch/include/ATen/ops/amax_cpu_dispatch.h,sha256=bTSDLN-9YaOQ19tOGvZfTcG5Ft-Uv_z31m3I2CJ13Uo,994 +torch/include/ATen/ops/amax_cuda_dispatch.h,sha256=7l9HqnUM1ITMjbqhv00bDf9RbVRIhqQaUUo3pujE_RA,996 +torch/include/ATen/ops/amax_meta.h,sha256=zJ7jrBpNrd2_2F_8DtTtwPFxWlko4pfLAZij9G2vGMA,600 +torch/include/ATen/ops/amax_meta_dispatch.h,sha256=N0iRa_0MrwaSYe7RERp08xNHexkJTaGAqNPFeVZp_-4,996 +torch/include/ATen/ops/amax_native.h,sha256=9pCQH2_HrZr27i9NW4Bo9orfvoZdycjlZKeOEhLfab0,625 +torch/include/ATen/ops/amax_ops.h,sha256=vVHwxA7DP-va4R_TEug2U6jjK-l41085Eid8SS2CWI0,1791 +torch/include/ATen/ops/amin.h,sha256=8FcFbMJQNoz-zqVrLYQOtItBcotVnF8YozcQ3NRS8UM,1277 +torch/include/ATen/ops/amin_compositeexplicitautogradnonfunctional_dispatch.h,sha256=n4l4av7to9aq1T46rMJIiK7x5QQ-ZdveuYq2Kt_1OKo,832 +torch/include/ATen/ops/amin_cpu_dispatch.h,sha256=ItocUOwWj82YOdae2NPjYLpsYE5CrmDxMWeJIFeG0Qk,994 +torch/include/ATen/ops/amin_cuda_dispatch.h,sha256=06N22SMiD6AgUOUtQVnUBq2Vu53hnzYeSRmWwIYLJ60,996 +torch/include/ATen/ops/amin_meta.h,sha256=uBi4OTj4CPs__j0wXGXNGL4AGWiWU0PAOX3OWhmu4XE,600 +torch/include/ATen/ops/amin_meta_dispatch.h,sha256=pygboi_K_Wt6BTxgR3EQukeYGMEIuBfHQ5sMz3_ByA4,996 +torch/include/ATen/ops/amin_native.h,sha256=0_544uxUfSmEbcsmYRKzufc5-5r1SwbrbhwGtWxFd1Y,625 +torch/include/ATen/ops/amin_ops.h,sha256=EU2w5fhaPatTWGe2lK_cpTu_FaOkvKB5isPAZJIV5PY,1791 +torch/include/ATen/ops/aminmax.h,sha256=RkaMmcR8o7S680Jrkxr46Qrq9DEQ_oEIowrh5FgkDps,1580 +torch/include/ATen/ops/aminmax_compositeexplicitautogradnonfunctional_dispatch.h,sha256=NVXGvDIkulwXzFbb3Epl5SX961thsbxfc7TyAiS8-lE,881 +torch/include/ATen/ops/aminmax_cpu_dispatch.h,sha256=Pvb1_DiJdKrgTc-zz2egUhYcuDlqIiTaxrNmDPzOxi0,1169 +torch/include/ATen/ops/aminmax_cuda_dispatch.h,sha256=QhP5UfR2npqMa0WcSLbOunCtQPD0izyq8vosz5Ei6Gs,1171 +torch/include/ATen/ops/aminmax_meta.h,sha256=p8Cok816qxVdO2CT3U7VTQc1NWCvrdjlVdE9toxPB_U,612 +torch/include/ATen/ops/aminmax_meta_dispatch.h,sha256=5hpxiZKAmQL_bUAWo1cCKcfTyHqbKIlBiF7x9aC2RW8,1171 +torch/include/ATen/ops/aminmax_native.h,sha256=IuBkUk-WvzQ0TW77_kClhNDijyXeKJzRtbJ8KCT07DA,667 +torch/include/ATen/ops/aminmax_ops.h,sha256=2BdHXZlqmd1R5YXPUGu9d9X_oPoZZxKQtR0R4wW3oa4,2128 +torch/include/ATen/ops/and.h,sha256=HVnjawF3qFUEy4run8_d05wLRhV1YELCyDQFqshq6Is,897 +torch/include/ATen/ops/and_compositeimplicitautograd_dispatch.h,sha256=ctXesQUpQ1hfL4Dn-q5t3U_t6qS_dBXGQnLmEqE1P08,1028 +torch/include/ATen/ops/and_native.h,sha256=tcYJSC82GFLuzOHrJF_YezsoGMmFmjiWce4bZm4a54k,740 +torch/include/ATen/ops/and_ops.h,sha256=shtYFCU0WBydAAFzAe-8IQ4tP7nQINEFe-Oy18TkV9k,2891 +torch/include/ATen/ops/angle.h,sha256=wr02fN-Lnri_OdBxaK60fZdkuYOrBOC9CsJurYe3wrY,1017 +torch/include/ATen/ops/angle_cpu_dispatch.h,sha256=WUvzdsQUiT6_2piUZ-GMqNj5TKPj1xR1ZXn3RAwELXE,874 +torch/include/ATen/ops/angle_cuda_dispatch.h,sha256=8GLy2DqUmgoypN1MhJlOpa6fTk_Ei-PCt1iFXpeJStk,876 +torch/include/ATen/ops/angle_native.h,sha256=7s93AzmCEsKqan7Po9tuCsOIA7PYPGm4fpXtlR9mk2A,704 +torch/include/ATen/ops/angle_ops.h,sha256=aZ15OYnpjyLA4ac-HuWv90n-WjvKlh1AbYXecMlL98A,1541 +torch/include/ATen/ops/any.h,sha256=vwnDldx5r03ONDzFWXi2FLxVrOy2C6vsgRszdVLA3pE,3355 +torch/include/ATen/ops/any_compositeexplicitautograd_dispatch.h,sha256=-Mfm-PFlLBwYShF2nkvgq3WrBzQpoTnGwGeDDKL8F5Y,1053 +torch/include/ATen/ops/any_compositeexplicitautogradnonfunctional_dispatch.h,sha256=SLNTt6owPxxWO7vdqEhXTG2XoFQZemNjKywixgesqcM,971 +torch/include/ATen/ops/any_compositeimplicitautograd_dispatch.h,sha256=w4q59ZxOZzeeaUeo56MtK3xxkRBreQxI0QSzYDqt0Qk,1017 +torch/include/ATen/ops/any_cpu_dispatch.h,sha256=0wPYeWS0J_m9Z2SvOHBwzi2-WvZlHNVyE_RqhkXO2GU,1506 +torch/include/ATen/ops/any_cuda_dispatch.h,sha256=YTBNllhYHcauscodcajqUciY3ZgkF2XtKuzfZWkiIfg,1508 +torch/include/ATen/ops/any_meta.h,sha256=PF0Cw_miMu7MeqkiE86r7UHlK8WpDlfx168dOrnQovU,857 +torch/include/ATen/ops/any_meta_dispatch.h,sha256=34-luNWSVXBKVgddZf7vWwUbimKA1TJ-qAGG_7MrY4s,1508 +torch/include/ATen/ops/any_native.h,sha256=0DFqjxZM5t6KnEpquTRx_9I7hPcrJgUDSMs_9S-XuYk,1456 +torch/include/ATen/ops/any_ops.h,sha256=hJX5uv6YYm1HXrSwcbPlbfjnVDkcTSYW84GB6es3wSA,5576 +torch/include/ATen/ops/arange.h,sha256=XwChu5eVCRtwuaSP1NfgMwdPRL7ZeaJJ2kQWRznfVak,4171 +torch/include/ATen/ops/arange_compositeexplicitautograd_dispatch.h,sha256=dCpSR-uMw9VWbtjyvKRpX5U93H4LrjMg1NOheP1OpHY,1862 +torch/include/ATen/ops/arange_cpu_dispatch.h,sha256=C9Ypq3u7F4qWbk789bHniQ9ymtIaKZcD3VuFsOBZtTg,923 +torch/include/ATen/ops/arange_cuda_dispatch.h,sha256=-qDx-i9dGxN7Hjb4agj2kiiqymNjlQk0Txo82GRrWA8,925 +torch/include/ATen/ops/arange_meta_dispatch.h,sha256=y9edn1kvM-Vqm1UQnotmCTG4u7QKl6O6C-g16zWW0i0,925 +torch/include/ATen/ops/arange_native.h,sha256=fEiBOr_VdQsewlP-_JmIc0PuIDbMQNDYR_6LtVCQ3Cs,1469 +torch/include/ATen/ops/arange_ops.h,sha256=6RJuVKNnntcXjmRC119PocnStT69n3gTzaiMeu8QWj0,5105 +torch/include/ATen/ops/arccos.h,sha256=CQJfJ4O2azJz8qHa-u4ZSdG7JV20gNPyKi-yyUHS64E,1169 +torch/include/ATen/ops/arccos_compositeimplicitautograd_dispatch.h,sha256=jw4rphQLHHWCbSigf7ZPoPWNUpOuOgheIke_dAHWvDU,972 +torch/include/ATen/ops/arccos_native.h,sha256=hxOUoKb1tGMk-KvqWd1Epey3zZwq9NClJgIwAjxOvyE,605 +torch/include/ATen/ops/arccos_ops.h,sha256=6WEpdZqkLm3ZiNIxrNWghWwmeWZFkk_-oITWg2nyz-c,2046 +torch/include/ATen/ops/arccosh.h,sha256=UrAqwlDGP_LqIZ08mL0V-XLKDai12YMqHx0RpwksePo,1182 +torch/include/ATen/ops/arccosh_compositeimplicitautograd_dispatch.h,sha256=M69I6MFjaHKVp03uZT2S_pDD6_hQ4jV8BJY8OU_1kBA,976 +torch/include/ATen/ops/arccosh_native.h,sha256=HfskqPY4VFS8-ONk_9VfURlve_0MXhbeEw1may9Cw7g,608 +torch/include/ATen/ops/arccosh_ops.h,sha256=LK7_RY1KidJmkeZ-69LZmJx5vFaiBWy8PF9uM8o6PjU,2055 +torch/include/ATen/ops/arcsin.h,sha256=1TeCzzO5aZIKv7Y0CC8OQyGHdd2vmha3z0-MpDiTSng,1169 +torch/include/ATen/ops/arcsin_compositeimplicitautograd_dispatch.h,sha256=76NDHrGaKw6hf5SOCh_z0Qp9UTlYFqLEkKJ2FIg4TbM,972 +torch/include/ATen/ops/arcsin_native.h,sha256=r5OHvQmeb0F8zNakT4iBdFpvf2jDU2NCKsdQ-KDIK5M,605 +torch/include/ATen/ops/arcsin_ops.h,sha256=WWKPtf3bTKxfyn3Fl4iYFljHSy3_HlTfDvSjePxS_tA,2046 +torch/include/ATen/ops/arcsinh.h,sha256=E4rqpTdkIKMC0fnB5WFqGmmNKqZDP5ACWOwPLuRwtKM,1182 +torch/include/ATen/ops/arcsinh_compositeimplicitautograd_dispatch.h,sha256=-u5rHNy3oW5PM8Chink7cK_FotvnDM7X7fiRO1zEpSY,976 +torch/include/ATen/ops/arcsinh_native.h,sha256=D8A_dzhIC-IoIhSgZbQx2tWDstpUzi8b8i83t048foI,608 +torch/include/ATen/ops/arcsinh_ops.h,sha256=RiWKnc1qqw13I9ksaCTKLty3STcxdwrn0A3FkDsYREA,2055 +torch/include/ATen/ops/arctan.h,sha256=BKjNbaXMXlf9OYD5o-4MOMGtLQcsDU14yJhIitgFFis,1169 +torch/include/ATen/ops/arctan2.h,sha256=tq4rsGQCUXKE-WqSJJkC7LDDulA4F3AnxQdrhr0B7ec,1178 +torch/include/ATen/ops/arctan2_compositeimplicitautograd_dispatch.h,sha256=Mj6_sZPVIQkHQPq0CLPgTvxVtWiTGUtPKxQI0nZEAIY,1080 +torch/include/ATen/ops/arctan2_native.h,sha256=kIIkHKYosJ5ckvro65s_sq5PxB5F6kq2Qiro7xVaNSc,686 +torch/include/ATen/ops/arctan2_ops.h,sha256=q5xXlHoF9oHdOa8bACPnX1OTlwv0_IRBn8hkOiGo4d8,2313 +torch/include/ATen/ops/arctan_compositeimplicitautograd_dispatch.h,sha256=a_ZC-HFEXxXTPp-PtIXhh8Oeb0Sxsw633Prlmc9Ido4,972 +torch/include/ATen/ops/arctan_native.h,sha256=JIJfYkHPhul1cc3Y8n6AFrCItwGsDlQ1dY4TP6MbZtE,605 +torch/include/ATen/ops/arctan_ops.h,sha256=IBj8Gt8pN_V6bO__M3ekx-kRrxIECQRxBldAZpOXGUc,2046 +torch/include/ATen/ops/arctanh.h,sha256=uG0xcKp6NYm7Z0GjDRTN7T3zVRnfb0RPT1dcfOLDAwU,1182 +torch/include/ATen/ops/arctanh_compositeimplicitautograd_dispatch.h,sha256=NHmbbBPpnIzLN7JRZNM1BjOwMHtjZOeAwTx3x8cy0gk,976 +torch/include/ATen/ops/arctanh_native.h,sha256=CN3DxSqbfmJ_LCDkY7Z_YZlXr6A0Q0HZFMWLFwMoE4c,608 +torch/include/ATen/ops/arctanh_ops.h,sha256=sZQAIzyX6UywGCgqPGjT_5m45lnbUxVOzcZb4vrORuk,2055 +torch/include/ATen/ops/argmax.h,sha256=qoq977FX8qUnkLkYnXFHZWy5TOzhEDri9HRzkUSQWCQ,1348 +torch/include/ATen/ops/argmax_compositeexplicitautogradnonfunctional_dispatch.h,sha256=IPLcxZnKqFbzFrli8nIYL4ylblLo7y3iHsMMoDvizf0,855 +torch/include/ATen/ops/argmax_cpu_dispatch.h,sha256=U0G4VI9t24oJ55ZBfuJ_t0LlaB5LAKyc4o1RJvqOsJ4,1051 +torch/include/ATen/ops/argmax_cuda_dispatch.h,sha256=ld08DRvrgyxHCH3lPD33JORAOhJgaTSnXiw45CQBcLs,1053 +torch/include/ATen/ops/argmax_meta.h,sha256=lAaLl55Xfj9ZH8HZOwZwnpGvlBX_Cs2jdYQIHS3nYfU,611 +torch/include/ATen/ops/argmax_meta_dispatch.h,sha256=Y0EREve1-4F505lD16_ewfzCAAzEu_mpgB3-Om5SRo0,1053 +torch/include/ATen/ops/argmax_native.h,sha256=tog8hu--ioIQzOLljPDPNVHSWI1FH0aF6gMW2pruN9w,640 +torch/include/ATen/ops/argmax_ops.h,sha256=Gv1FFK_jK8_HcZj_2iZJL-kFsb6wDYvfRTZRLYy6t14,1857 +torch/include/ATen/ops/argmin.h,sha256=oHZKRQTzqsSfUXtRH93Rr5jblpzJu53J5179f5oWCog,1348 +torch/include/ATen/ops/argmin_compositeexplicitautogradnonfunctional_dispatch.h,sha256=qxvIc4PO_FVE4Yd3I48iA62LNZZvLhIBgQpxa5-JUQA,855 +torch/include/ATen/ops/argmin_cpu_dispatch.h,sha256=NQpc_zxW6qGKo1Lbw_Zf-vRZgsywVi-s_c9xlIg4FUM,1051 +torch/include/ATen/ops/argmin_cuda_dispatch.h,sha256=r9YrJ2oKF8ZL9ctALaTza5rxRxS_q2qoQ9qylKmiJhs,1053 +torch/include/ATen/ops/argmin_meta.h,sha256=OVzkH3lzXohNCF-7bOjEkV3LOMZfPhya9zzsPAVaTdA,611 +torch/include/ATen/ops/argmin_meta_dispatch.h,sha256=KvJ8Lt_0OYSN6ofbqpENfaz86wTHNVfGYwzCTmNnzGg,1053 +torch/include/ATen/ops/argmin_native.h,sha256=m4pfaE1DZj-dF-PLkyyS1kFq6ug0sVZu3fDzvhP2Z18,640 +torch/include/ATen/ops/argmin_ops.h,sha256=taGMCU48XQYE4LjLBFg_6XbGISA0lDvv2YvkYhe4aVo,1857 +torch/include/ATen/ops/argsort.h,sha256=IDZYUy2XYNqIxL0WTI-Nb-Wxf1_uVl15ycCk5Ruvixw,1924 +torch/include/ATen/ops/argsort_compositeimplicitautograd_dispatch.h,sha256=01AU0zh6Jk4icz3GnKn9aHR7vLU30akb8s93Cq_ZUjQ,1260 +torch/include/ATen/ops/argsort_native.h,sha256=jhARQH5BFjtiidDTajz02WMUp1VNy1g8M42S_ka9RIo,840 +torch/include/ATen/ops/argsort_ops.h,sha256=9YZoYc9abfRdepuL-sCrWJMk-CiXHlAIBR6cLJpkNDI,3169 +torch/include/ATen/ops/argwhere.h,sha256=7fQtdrNqEePjnIjYA8tBGGFFVm1pS7kfoaflvbcFcAE,645 +torch/include/ATen/ops/argwhere_compositeimplicitautograd_dispatch.h,sha256=oEmh9Sazbi83mlQrioLOkXjuaGywuSxfAar2MLfTUkg,766 +torch/include/ATen/ops/argwhere_native.h,sha256=g-PPOmW9_h28xP3-C5cLalvphpDP8t54hVzjDNDBKFM,478 +torch/include/ATen/ops/argwhere_ops.h,sha256=n-6Cx2Dosg_yuuTzTACNRlSDARvyP7FSbnOBc1dyv20,963 +torch/include/ATen/ops/as_strided.h,sha256=fLjBTfobcK1v7aBvjzRqF7J-P3QcZldhMvEMVKOsipI,3878 +torch/include/ATen/ops/as_strided_compositeexplicitautogradnonfunctional_dispatch.h,sha256=HPd3b1oBOl8XHOox4HGpcw5xXFrTrlRA9uHgMwo_06E,1093 +torch/include/ATen/ops/as_strided_copy.h,sha256=TMEhUShkgYN2HcrNMt8nBz4w0LVWeafSbsfPPi0r1u0,5930 +torch/include/ATen/ops/as_strided_copy_compositeexplicitautograd_dispatch.h,sha256=UxWcI1GGzAQzst5B1Oa2BLy6Tr9NJgcYaqRJNzFmScs,1476 +torch/include/ATen/ops/as_strided_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=phK0t5P3lVOaI12M1rc7eCH1ZwP558sT4qbau2TdZ-c,1085 +torch/include/ATen/ops/as_strided_copy_native.h,sha256=Fxa40pSGkNv8FB5iB3IWkkkQ7U4XxbsX3kNwzsiP5xM,799 +torch/include/ATen/ops/as_strided_copy_ops.h,sha256=W2l678AU1j9gyK8cYWF9aMY_TVBK3wEy4FpLQaE6TGE,2263 +torch/include/ATen/ops/as_strided_cpu_dispatch.h,sha256=1WC_g8Xv100fiG8lWOFWwlcnkNEZDxPKYDipTHQQCsM,1005 +torch/include/ATen/ops/as_strided_cuda_dispatch.h,sha256=XgcapudRoxD8W4b6AuifAaLs4mwU81QLzmemEWSvtgQ,1007 +torch/include/ATen/ops/as_strided_meta_dispatch.h,sha256=I53Fha1vmdOAZR3DaihknkzBpLQC0wDi2unBqsGcmOU,1007 +torch/include/ATen/ops/as_strided_native.h,sha256=GqADkT4XFW5V0Ma6Sd8ktKqa1GVfUB0Tuf2lPXE1Kdc,1148 +torch/include/ATen/ops/as_strided_ops.h,sha256=9o-7N1QgTBqWgigvaCK8NLFHQo_5PAXdHPj54YSDlSI,2184 +torch/include/ATen/ops/as_strided_scatter.h,sha256=D4UucqusfCDvowZVyoWeq5ULmfF7vOJmDRKeEvLFJEI,6443 +torch/include/ATen/ops/as_strided_scatter_compositeexplicitautograd_dispatch.h,sha256=Vp7iL-jdE3ePiphziJzrAVSR6Eg2vQ2gWGtgEfYRLb4,1584 +torch/include/ATen/ops/as_strided_scatter_compositeexplicitautogradnonfunctional_dispatch.h,sha256=-rIFcMBQllSXfR-O2MP4Qp4Eyyi5w8UcArcEo6QqtKU,1139 +torch/include/ATen/ops/as_strided_scatter_native.h,sha256=sKfR2Ddepd5hxf6Y6jIWF_jb_qtZhSZoXOxy_E_7kfw,853 +torch/include/ATen/ops/as_strided_scatter_ops.h,sha256=mJjpY2B-LcsAceb_Gq9uWCu6yCBUOrYHG3RKIIDtruA,2441 +torch/include/ATen/ops/asin.h,sha256=MFJnyQ72-ryFbU27UecWhwhp4UjXWe-4F_LDVsU3NO4,1143 +torch/include/ATen/ops/asin_compositeexplicitautogradnonfunctional_dispatch.h,sha256=rQHAdGl_lnlZVEffdFCs2AaXlBmpzAb5wzfcIbds5I8,837 +torch/include/ATen/ops/asin_cpu_dispatch.h,sha256=OuHL9pTRifagYOu7g6iUfFQMJreoJodOWdFbgPVcJbk,920 +torch/include/ATen/ops/asin_cuda_dispatch.h,sha256=qVhBQh2CBSfrBUdf9zFKS-KGDXK26Sl1QIcnh1609z0,922 +torch/include/ATen/ops/asin_meta.h,sha256=nTyBuXMnUqcnschtdMG-sGILkSLeF5IWZRjdIrVHLLY,565 +torch/include/ATen/ops/asin_meta_dispatch.h,sha256=TBZ94Bnz43jcXA7HeDKlOcQsRZMPkHMCIFfL3j3dNRo,922 +torch/include/ATen/ops/asin_native.h,sha256=UNQ4jVdM7mmlTSH7K6yq1lTOIc9i-Gp1eZYSdGZZmAc,998 +torch/include/ATen/ops/asin_ops.h,sha256=hiWmYo97x_BTLU8y7H-9Pu0B4Nh09JgrLKSpteiawXc,2028 +torch/include/ATen/ops/asinh.h,sha256=ATyF091IkVCXizovQ2IVkDzpNLniygeH2bDY7NHnYAc,1156 +torch/include/ATen/ops/asinh_compositeexplicitautogradnonfunctional_dispatch.h,sha256=XWUBJVXWCOR1SGb3SMCsbBtGfphnS7peYo66N9DqMNQ,839 +torch/include/ATen/ops/asinh_cpu_dispatch.h,sha256=vaPhg0Yu4RhDrwXY9uMyvJW6cXCSsEN-Axudj9Z5Gx0,924 +torch/include/ATen/ops/asinh_cuda_dispatch.h,sha256=HhALrALFbQq2j0EFNZin2Gy0ofneP_RMuHZn2uWTjQ0,926 +torch/include/ATen/ops/asinh_meta.h,sha256=L8Wpwbx0g3g5y2WlHK1N8v2BCXNnGrkFWhf7HTXumqU,566 +torch/include/ATen/ops/asinh_meta_dispatch.h,sha256=ruiNpAsP9VM-FNYJnMUtSNrT6I5Sif_czWTS0GpSMLA,926 +torch/include/ATen/ops/asinh_native.h,sha256=qegUFDPDrx7HV5s9IF2YyswB-XdmhjDE3-nAuGCxKdM,1007 +torch/include/ATen/ops/asinh_ops.h,sha256=Tpi_iWvhhFOQ41puVbgoKIlPROiHJ725jfZSJ0ur128,2037 +torch/include/ATen/ops/atan.h,sha256=XoiEIeNblAWHEHHPFFGPiVcs5tKnvGtZUNA-eaUVP10,1143 +torch/include/ATen/ops/atan2.h,sha256=KPcs76v99D4TnvHyx-N2kECe5MObyaSuKa7yXhbRmDg,1158 +torch/include/ATen/ops/atan2_compositeexplicitautogradnonfunctional_dispatch.h,sha256=YqjnXLlGksLr4zjEjpY71TGCLDd_j8c6wekt-xoRi8g,891 +torch/include/ATen/ops/atan2_cpu_dispatch.h,sha256=zMYn6YJFoYVrRH1gECfH-7d8fQIy0pnPRyom2Fx6CT0,1028 +torch/include/ATen/ops/atan2_cuda_dispatch.h,sha256=xTWeq4ixt7FJW7kqKHfBxGm2B30LWHz3Me9ibvIuuv4,1030 +torch/include/ATen/ops/atan2_meta.h,sha256=PaOGVgjaATVANI_3ZHT6mIMri9mONE4sp0r7Fl2PWHc,592 +torch/include/ATen/ops/atan2_meta_dispatch.h,sha256=0FeW_H7IsRfktHWg5g928-SR-M3tSnUhxwRBTwDVQRs,1030 +torch/include/ATen/ops/atan2_native.h,sha256=T2inn-cofqEZlA7ON21w5e05KWGiSYY2A-NCs4T-2HU,619 +torch/include/ATen/ops/atan2_ops.h,sha256=p-N6dXl2AgspwUiUygN0oLDzABwqOERW7aPxLfLOj2k,2295 +torch/include/ATen/ops/atan_compositeexplicitautogradnonfunctional_dispatch.h,sha256=gOueKxq4BYkz195quAbAgPb9RC-j40NJoguAcxD7Qv8,837 +torch/include/ATen/ops/atan_cpu_dispatch.h,sha256=RsD0iE3lk2CMWuWAqHSt9ms0l_vLIWDmG3ZacMPf5Qg,920 +torch/include/ATen/ops/atan_cuda_dispatch.h,sha256=-0BMCkdLIhuV69cmGYNL6geP-n2cgn8CBR2IruVHQ5A,922 +torch/include/ATen/ops/atan_meta.h,sha256=mnkwvWqMfLvjVExM6HF3zqK0Zy_wP40pAMVhQYYG-V8,565 +torch/include/ATen/ops/atan_meta_dispatch.h,sha256=e3sGwmIE1cTJ2t9XlEKXzh7X_unwMwc44R9bXUn5lNM,922 +torch/include/ATen/ops/atan_native.h,sha256=M6GPqGJLQncjOcaHeLnYU06p0j1ZdtVbw3gAeU88Z7Y,998 +torch/include/ATen/ops/atan_ops.h,sha256=yaCnjjXGKFXLt4znXprX8Li3_IWb5q9Mo2WGWxG0Ls4,2028 +torch/include/ATen/ops/atanh.h,sha256=arYjjlv76DPRNJhCkIPJb18FlLKLxQzCg4sSNUVgcxw,1156 +torch/include/ATen/ops/atanh_compositeexplicitautogradnonfunctional_dispatch.h,sha256=Oo-i9SdkZbDJgWn5INR4ery80-el6-uZBItpmm_GU5I,839 +torch/include/ATen/ops/atanh_cpu_dispatch.h,sha256=DkPSF_j83bb_UQOtoNwRdKH8v0Z3a-IxFY2ryYlYS4o,924 +torch/include/ATen/ops/atanh_cuda_dispatch.h,sha256=0pjqsnPEi1m2644blhUV3ATJD-Z5Ok2lrLTzlCodzF4,926 +torch/include/ATen/ops/atanh_meta.h,sha256=j3IlyCnzG3-H-iGam8vRGNnPcK-O110_BnIhkRZUlyI,566 +torch/include/ATen/ops/atanh_meta_dispatch.h,sha256=ie0GfrrAcdXNSxSKl9isvfgjqa9ezsWqoaA4wZkp670,926 +torch/include/ATen/ops/atanh_native.h,sha256=M1WdCLrM8pyuxgmbdJ8W5K4MtIeyS3EZuT3AYW2e7SE,1007 +torch/include/ATen/ops/atanh_ops.h,sha256=b9bmmwZ8fcKfWUSiYwGPhaLqFtv4Nelv6RtiBxNO7K8,2037 +torch/include/ATen/ops/atleast_1d.h,sha256=G2NMODCzaZSyX8M8z2w5GZDzwlp0nTLFsO7cnKA4X_k,842 +torch/include/ATen/ops/atleast_1d_compositeimplicitautograd_dispatch.h,sha256=sabjmZ2Q4zW5Ki65GKr0Q5Jx9i5cug-_a4u6EFccaZo,840 +torch/include/ATen/ops/atleast_1d_native.h,sha256=1Ezi_cLA0OhRsyKxfu8QU7yowH7GSBul1iD3oVDs7nc,552 +torch/include/ATen/ops/atleast_1d_ops.h,sha256=ro1b4uI47p5PCseeY_c51blCSArjwSulVqF1wllOX9w,1553 +torch/include/ATen/ops/atleast_2d.h,sha256=8_OWy_WhsJx_K0JK-5cn-CGg8TfBAkQIYicur8QL_GY,842 +torch/include/ATen/ops/atleast_2d_compositeimplicitautograd_dispatch.h,sha256=Es7bwCTh6K1Hrc1ObtFm19xqK0SEAkdGyBhAeZgdWk8,840 +torch/include/ATen/ops/atleast_2d_native.h,sha256=RsWErgz09xCnUFOOhm_Cx5Pxoq1ddYoWhe1uO5MRKIo,552 +torch/include/ATen/ops/atleast_2d_ops.h,sha256=QDASsiDruhwlcOyaLIvjQPb5rkA40rlWaYQz4BotHww,1553 +torch/include/ATen/ops/atleast_3d.h,sha256=2JGCMgX0qITlHRAwoUXcGCpIflgcHJ7pZQg9ajHRS3g,842 +torch/include/ATen/ops/atleast_3d_compositeimplicitautograd_dispatch.h,sha256=SRXx--ZpMZn0FFTDEadUZ8qWG492up2Wf4YY4duHnYg,840 +torch/include/ATen/ops/atleast_3d_native.h,sha256=S8B52EfVSjXz-2Mrv2Nvkf4JA3v0iMDmEYB4n_b49bQ,552 +torch/include/ATen/ops/atleast_3d_ops.h,sha256=5RWRGqcrRRm9glMXqgSMZq-AbK-S9FfV-geIEPEKjMo,1553 +torch/include/ATen/ops/avg_pool1d.h,sha256=qJr2rVR7YnIX2e6xjLYAD4XuNficp4F3rfZcSs4mOks,1954 +torch/include/ATen/ops/avg_pool1d_compositeexplicitautograd_dispatch.h,sha256=wWHTdV54RnhJ50WEq644RKDHAHYNJhG8rZ1AIgKz4Ks,1127 +torch/include/ATen/ops/avg_pool1d_compositeimplicitautograd_dispatch.h,sha256=NGtYaWPA-VLongy8-xut4L3di2ee0PApSxIjMyZp2wM,902 +torch/include/ATen/ops/avg_pool1d_native.h,sha256=efIhvcDr-vnYh68GSjuvv9a7bacUFHEPh0EPdZd3cJE,814 +torch/include/ATen/ops/avg_pool1d_ops.h,sha256=CBHAF68jaxTkmNta_J3vxuyxpWGZVW32T2ZPg7Fw6kc,2383 +torch/include/ATen/ops/avg_pool2d.h,sha256=Q7cp6Dm6uFhkasDcvFxyhY66HEnQN7kVILLiBkPchvY,2251 +torch/include/ATen/ops/avg_pool2d_backward.h,sha256=Oxx6u1NmtlTHr4zGj8eEnc7fjzAh6EP-I_ACXUMSq4Y,2481 +torch/include/ATen/ops/avg_pool2d_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=-Z2q9NgVi6AGN-6ykCFgRRd9R_yW15VKrJrJNVzgRoQ,996 +torch/include/ATen/ops/avg_pool2d_backward_cpu_dispatch.h,sha256=srMr4tpU8ar8bunkx_JaP-V6BoAZ0X_6VXxGLA_c58s,1509 +torch/include/ATen/ops/avg_pool2d_backward_cuda_dispatch.h,sha256=53jzO9nw0TPsiBxaESCn-jUccPGcaQpjioBYR0pbNsA,1511 +torch/include/ATen/ops/avg_pool2d_backward_meta.h,sha256=juLorUi3TYe-fYCd1PTgDsz8ThdIKky7IYNNNja7nWQ,773 +torch/include/ATen/ops/avg_pool2d_backward_meta_dispatch.h,sha256=gvz_7H-6aEa_D9wcfbDX_5OgaFi_rOqd83KwUeJLUeU,1511 +torch/include/ATen/ops/avg_pool2d_backward_native.h,sha256=MdqeKZaC7ne_zu3LsUiDOLuWfQYnKlCRc_CveXGxOK0,1776 +torch/include/ATen/ops/avg_pool2d_backward_ops.h,sha256=P_nlcFaYwAOZ6qCbGFW5U-lDETDxp2vQWvw6u4isUBs,2925 +torch/include/ATen/ops/avg_pool2d_compositeexplicitautogradnonfunctional_dispatch.h,sha256=2BPsKlvsfeuTQcSj9bjCEiete-Vdwgz2Aw7LG7QvV90,986 +torch/include/ATen/ops/avg_pool2d_cpu_dispatch.h,sha256=473279q5SyVN7Ee5b8LyMDpCeSFhFm4c_LWzbPB0bHY,1434 +torch/include/ATen/ops/avg_pool2d_cuda_dispatch.h,sha256=7oPqxayY1wmj1aZV4ZJVKLjDs0VqaMPFM2hLzm7Beoc,1436 +torch/include/ATen/ops/avg_pool2d_meta.h,sha256=UOFtTLuD356ebV37q7lUTlYj9aOSYO73E8uNp7VyqQ8,3442 +torch/include/ATen/ops/avg_pool2d_meta_dispatch.h,sha256=A6WxkH5qwNf3pKQ3xAj4ByFFLyj1gfw0DCWWre6UNPg,1436 +torch/include/ATen/ops/avg_pool2d_native.h,sha256=ZvUxafeqmxny3uyekY8-yjzyL_5mvmmjeoAEThc9blA,1855 +torch/include/ATen/ops/avg_pool2d_ops.h,sha256=Cjbftz0Z-5SwSPtFZO7-5eSwTHNCDGyPvxUKBgMIi18,2663 +torch/include/ATen/ops/avg_pool3d.h,sha256=XhNr4eKzuWpgIQ7jNOX1-GWSYgbVgRwCkXUzuLYpyIc,2251 +torch/include/ATen/ops/avg_pool3d_backward.h,sha256=HdrtHpSWyGkp86X9iNSlOw5FiFaRE4b6O7XGB5zNOJQ,2481 +torch/include/ATen/ops/avg_pool3d_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=9821DfL0iyGKYszwkcd5DC97aeLtFKokjoKCNj86fM8,996 +torch/include/ATen/ops/avg_pool3d_backward_cpu_dispatch.h,sha256=AJG0cXUkJWlA9QA0fRP63Z6L4OQABbNjPukS-BXb0Y8,1509 +torch/include/ATen/ops/avg_pool3d_backward_cuda_dispatch.h,sha256=GVMcK9Z1OeM5MeLzLneaFMo8aVMzdcNZGPyEgfCsqEk,1511 +torch/include/ATen/ops/avg_pool3d_backward_meta.h,sha256=NnQtuQyvPk2cp9jhliR5RUMZBAymkCz-x4D62pdvBhQ,773 +torch/include/ATen/ops/avg_pool3d_backward_meta_dispatch.h,sha256=7j1SkEMKz16zCXui-aYywIj8IjTLbh1xp4snTdZeqdc,1511 +torch/include/ATen/ops/avg_pool3d_backward_native.h,sha256=eqPmZBPlIQ-lSGMtrlNRXrj71GzThb3-QI22Aw4Fpfo,1776 +torch/include/ATen/ops/avg_pool3d_backward_ops.h,sha256=vicpZsX1ET9YQsDKJy6l57dlJxGfJGAq50p4C_vuVj8,2925 +torch/include/ATen/ops/avg_pool3d_compositeexplicitautogradnonfunctional_dispatch.h,sha256=aIJ27Nzi1zmgSNSf4Ocba8tUhdLazS-mF38o5qvCnPo,986 +torch/include/ATen/ops/avg_pool3d_cpu_dispatch.h,sha256=HYyEhPyWgkx107ibAYlP6-a3ymQYdK4jxrpeujL9YTU,1434 +torch/include/ATen/ops/avg_pool3d_cuda_dispatch.h,sha256=ETMQHjmsJK8AUOfEbaGIxciVeAnCOCnGtX3QTuPJQqw,1436 +torch/include/ATen/ops/avg_pool3d_meta.h,sha256=VP-ZM3yagY_n1T2A-y629oeeV4jNsQuYQ6KQ9OV4FSE,732 +torch/include/ATen/ops/avg_pool3d_meta_dispatch.h,sha256=ikgqq3uW618qZsWqV-GNh6SGUfBRiT52ETDbOn6T5lE,1436 +torch/include/ATen/ops/avg_pool3d_native.h,sha256=mKQFsMilpG-RpMriwhfJk9zoeEisbk7zR8nhVael2rE,1859 +torch/include/ATen/ops/avg_pool3d_ops.h,sha256=euDQWG1Wk8OehkZwVKTCmfbgW0emnfsHm__zpLRzZ6c,2663 +torch/include/ATen/ops/baddbmm.h,sha256=RC_pWg8QQ_hBy_97iHrRJ8zPm8pBVBCC7WhM7TE5mN8,2976 +torch/include/ATen/ops/baddbmm_compositeexplicitautogradnonfunctional_dispatch.h,sha256=8Ofyo5U9fpQA2tb4KRJai0j1Sk0D6ilq5kJ0WnWFptI,1061 +torch/include/ATen/ops/baddbmm_cpu_dispatch.h,sha256=9bF7yR6FY-nWl9qxwu6Az1oPbYpqql3ULwyMmpoSsDM,1364 +torch/include/ATen/ops/baddbmm_cuda_dispatch.h,sha256=suQgjQJNKdXBNRiyXcGR9OH7CvRinoX7bf1Ax7eeZ1k,1981 +torch/include/ATen/ops/baddbmm_meta.h,sha256=vjgJeSAwizKeSAp8wB9w_WLTDrpuggZQNVJva0iLLmE,673 +torch/include/ATen/ops/baddbmm_meta_dispatch.h,sha256=ssHIzyYP0Iywmv5IUACn6bnK5wNfBZUIZuYBCquGTLU,1366 +torch/include/ATen/ops/baddbmm_native.h,sha256=ZMW_ixOYHir5bDsiw2zxl7K7tV0BfFe1az9kn-3rTUI,1585 +torch/include/ATen/ops/baddbmm_ops.h,sha256=f4AZVFWxIjTAk1pv2-AYqIn7F4Kbp2SH449QxQms9LY,5130 +torch/include/ATen/ops/bartlett_window.h,sha256=K8dSa4mXKm5MyVTteuwXCVjk7yIoL5Kpuk4UgVsLXK4,3417 +torch/include/ATen/ops/bartlett_window_compositeexplicitautograd_dispatch.h,sha256=49zFEVy7zDDg7G1qI59iXdHn00stIdE_TA6hZmaIpGM,1706 +torch/include/ATen/ops/bartlett_window_native.h,sha256=5xvas8BKnVMOEzp8OZA-QOOUHQYUSQweF1dcshAZQ4A,1067 +torch/include/ATen/ops/bartlett_window_ops.h,sha256=RG7mG6SRpqhqOxXnX47N_aqipocxUQi2jT9MCFp3rPo,3856 +torch/include/ATen/ops/batch_norm.h,sha256=Q1QlgQD8FV7WzQ5yM1fV-dFT1aTwtdrZ6PARTCx3nC4,1121 +torch/include/ATen/ops/batch_norm_backward.h,sha256=wJc1cv4qAk_1y_vaHqoyZYYpZ1REX3QjUpXMWDL3uhc,1355 +torch/include/ATen/ops/batch_norm_backward_cpu_dispatch.h,sha256=WsmxSSG8yLLZni5A3nbVBLJ3o6Csk2OLZdKVg4K7v_s,1105 +torch/include/ATen/ops/batch_norm_backward_cuda_dispatch.h,sha256=7ez8hB7YTE0MfV3r2ggBzjnThYoHmaqhPgFXqDe2pOM,1107 +torch/include/ATen/ops/batch_norm_backward_elemt.h,sha256=fOLDKy4Bgz1I9r8bnB3fptrYTh4jGJOZMLP_oTQ-QIo,2357 +torch/include/ATen/ops/batch_norm_backward_elemt_compositeexplicitautograd_dispatch.h,sha256=L_xRh45pKLZhA-GjC6SgQQDCaVWUsx_vzCw1NPi4Gag,1325 +torch/include/ATen/ops/batch_norm_backward_elemt_cuda_dispatch.h,sha256=DPT9v9x0rCpESlKzBisCIncQfoT-Eq6SV3FNfj9vPc4,951 +torch/include/ATen/ops/batch_norm_backward_elemt_native.h,sha256=iLrfug9Vbqmg9-bnIK18O_oY-03b9-Kp79PV_pGesIw,1017 +torch/include/ATen/ops/batch_norm_backward_elemt_ops.h,sha256=xC4Ul9smqNIiwARVi3l8STcuRKc6mLS-v1wd9-W7oi0,3035 +torch/include/ATen/ops/batch_norm_backward_native.h,sha256=gqkn3vInRy-GHDpzqfUoBjnupQjiZvvnN2ullkJcffc,1770 +torch/include/ATen/ops/batch_norm_backward_ops.h,sha256=IqZ0KpeNgwgUyNDtNJDWw-XFBnVtVORS3FNVFmrsepY,2212 +torch/include/ATen/ops/batch_norm_backward_reduce.h,sha256=Cb4Bkc_U8jard0z1ZXwlIfMH2Q3z-zHAo-0Tlokre8I,2736 +torch/include/ATen/ops/batch_norm_backward_reduce_compositeexplicitautograd_dispatch.h,sha256=_bW_4vY1LhlP5rSiWDoYSjGpE10w2qVg3HY9bAxy77o,1465 +torch/include/ATen/ops/batch_norm_backward_reduce_cuda_dispatch.h,sha256=JhyX2W3swXOYPA53gZQs1k8Yx9evOvzrinP3VBGTQTw,957 +torch/include/ATen/ops/batch_norm_backward_reduce_native.h,sha256=EGfZxuCf2bc8uNutYkVF3DJq1SUqk5-IzcembyM4Mxo,1093 +torch/include/ATen/ops/batch_norm_backward_reduce_ops.h,sha256=ho59ULssEsvN10O9vOlv8tVdgmr7X2_ju2biULeEz6I,3351 +torch/include/ATen/ops/batch_norm_compositeimplicitautograd_dispatch.h,sha256=kRgAkD30i6r6aAG-EJFKcUQzMPEwa0S9zlVFS0OtOqM,1018 +torch/include/ATen/ops/batch_norm_elemt.h,sha256=22yqVLvQlha43aLFyAXivUTEWLUdF5Lr9SuVGVwvx5s,1892 +torch/include/ATen/ops/batch_norm_elemt_cuda_dispatch.h,sha256=xWfe9fmG92oZkP5YzDbNXTUIid3gjKRHQx23QrE6O5Q,1362 +torch/include/ATen/ops/batch_norm_elemt_native.h,sha256=TrBC_4Mc0wgaNSuLeyWKQ3RNAcA9mmDjIY9e9sfAXoQ,886 +torch/include/ATen/ops/batch_norm_elemt_ops.h,sha256=K1oXtE7BpxvYqKJu3ak0D7iJKyiD4xW_bVizvdqfENU,2595 +torch/include/ATen/ops/batch_norm_gather_stats.h,sha256=TbasXvT2fhSkod_zOjG8oNHeDQsvEEvsPoOLd9Qw-UU,2514 +torch/include/ATen/ops/batch_norm_gather_stats_compositeexplicitautograd_dispatch.h,sha256=OXPEiLwwUgvd1clCY7uAR-YFEWfkPrkg7WKHLhK3vL8,1387 +torch/include/ATen/ops/batch_norm_gather_stats_cuda_dispatch.h,sha256=6N5vQeao7VpqFngSDUxWCew2xWmObLi3gyQLeMWf3Qs,960 +torch/include/ATen/ops/batch_norm_gather_stats_native.h,sha256=8_snx08X64Gy9RbpvGUbbN7kJdEDJb1Pdfz-MzEcBDs,1057 +torch/include/ATen/ops/batch_norm_gather_stats_ops.h,sha256=kputrXV5yPGulvrdUjygNRbokhwhQfs5aoe_uJa5YSM,3183 +torch/include/ATen/ops/batch_norm_gather_stats_with_counts.h,sha256=QPugWbNpR0hRSz83JKsYe7CWhcvzOr555107qkxShvg,2685 +torch/include/ATen/ops/batch_norm_gather_stats_with_counts_compositeexplicitautograd_dispatch.h,sha256=Fs464tXO4xaBOkDWGxGzl5w_6v1yZ2iyD8EONzqXmcE,1435 +torch/include/ATen/ops/batch_norm_gather_stats_with_counts_cuda_dispatch.h,sha256=XIMliA_MdBm-HNjybKdlbbCc44T6N7JpyQHD9rlLRb4,984 +torch/include/ATen/ops/batch_norm_gather_stats_with_counts_native.h,sha256=AQCfyTOSZISnXr5SnqEVOyeYAJ9BOMg2tMQMKDCJLdk,1105 +torch/include/ATen/ops/batch_norm_gather_stats_with_counts_ops.h,sha256=FpyB6vBppQoD5okZu6jdPkhp7a_f0haxe42lRWU-StA,3333 +torch/include/ATen/ops/batch_norm_native.h,sha256=H49sdjOBVi3HtJ-lKpARlp8fTLmrsWgTOssg30-Ao_Y,730 +torch/include/ATen/ops/batch_norm_ops.h,sha256=ASF482D7rK4KXmqd5JmAe81QZXapb0SMxO_neFRQGXo,1781 +torch/include/ATen/ops/batch_norm_stats.h,sha256=D0gNf6A6QbcYflJt_x9UJJYXjVE8PHufzWnVFafjHz4,1427 +torch/include/ATen/ops/batch_norm_stats_compositeexplicitautograd_dispatch.h,sha256=ZAPOV0qBmd-GZ87oVVTWdPecx6jR9YeA_z1gDsbwtTo,1007 +torch/include/ATen/ops/batch_norm_stats_cuda_dispatch.h,sha256=HOpegR8AfirwAstSrTCTvVDqyNrGWWNDY54bLXZOhYQ,770 +torch/include/ATen/ops/batch_norm_stats_native.h,sha256=HUmFlXHbYLZA7fmZrjOA0crz9OFbp3oI2MkgfA1tVVU,677 +torch/include/ATen/ops/batch_norm_stats_ops.h,sha256=VHLtOKUQwuUqaj3W2257PH5CeYMHcGRc6UxoFml4ilU,1951 +torch/include/ATen/ops/batch_norm_update_stats.h,sha256=bRz6ltLj7QBRA7Y_x6nq0xNr8nlSPGZt_qInRnQvjfQ,2049 +torch/include/ATen/ops/batch_norm_update_stats_compositeexplicitautograd_dispatch.h,sha256=TJVOKHnbsn1r1p2Y1TeXd0wNxJYYvVxuI7nr3bSG9Vg,1229 +torch/include/ATen/ops/batch_norm_update_stats_cpu_dispatch.h,sha256=IxSC7ZcJMUz-j6Y1kWigZGFvjGVLsJ9rDy-P2q4HI0A,879 +torch/include/ATen/ops/batch_norm_update_stats_cuda_dispatch.h,sha256=YBfHVDxsD8q6qubi7KW7ho_CfELFydIsdOePN9eyW08,881 +torch/include/ATen/ops/batch_norm_update_stats_native.h,sha256=IPj-BWV7tBezarviES1v9SqyTn_BPHQyAXnpIbsPSZc,1116 +torch/include/ATen/ops/batch_norm_update_stats_ops.h,sha256=KQvI69TP-nQszsq1GOszULp_sLO720hkT3PYKbGjr44,2653 +torch/include/ATen/ops/bernoulli.h,sha256=KWQ9ND7qBjEDj53gRc61P48yPcCrwPjYotD6CvAqKZs,3233 +torch/include/ATen/ops/bernoulli_compositeexplicitautograd_dispatch.h,sha256=j0NvIHxYeV4KjWOQMbvLc5a7z0p6VU4OL_JPEtnKVdM,1552 +torch/include/ATen/ops/bernoulli_compositeexplicitautogradnonfunctional_dispatch.h,sha256=VXTfBIDDWfhGDAnlPib8v-mSfUSsYuMZGV8ugOXwi9I,860 +torch/include/ATen/ops/bernoulli_cpu_dispatch.h,sha256=GjPMEcU9PNF6ouR0HdWqiDOSWniObN4VWSjIhTrVQ0o,1186 +torch/include/ATen/ops/bernoulli_cuda_dispatch.h,sha256=Qly2J3dg1T2OE1DzSrzJGhEMKv5scwVL_vzh-6mM-tY,1188 +torch/include/ATen/ops/bernoulli_meta_dispatch.h,sha256=xH3DQ9eyiBbUmuBJOOnskDhi8vl-aUqLqqsS3CIuN18,926 +torch/include/ATen/ops/bernoulli_native.h,sha256=PnjfJmH1f06tYf1yUob8XqCKHD5oWRftcBV2YO4oG74,1468 +torch/include/ATen/ops/bernoulli_ops.h,sha256=j9sndU7NidVHtTOFVykdQlopR_1Htx9sNWNwWiCa5Aw,6398 +torch/include/ATen/ops/bilinear.h,sha256=eJRreR3MJmwIeTqRiuXsZiq9C9uq1Yw_np2EdBlOQe8,821 +torch/include/ATen/ops/bilinear_compositeimplicitautograd_dispatch.h,sha256=HxUNiGaH-14s8_XWwZTnYKgfWbO_YVvJ9cQQuB4UM50,867 +torch/include/ATen/ops/bilinear_native.h,sha256=pEqHwcxrTbrEl_m4_TpfqPlnWg-Q6DGrOSgaZ5Gg34k,579 +torch/include/ATen/ops/bilinear_ops.h,sha256=ISN3RPZthVJo62I3gCz34tP7nwhX6SZfVehjcsk7K8I,1287 +torch/include/ATen/ops/binary_cross_entropy.h,sha256=QaYHutg1F42w3jvOKD8giWyxDq1GVBcs-KpYxmcq1ws,1732 +torch/include/ATen/ops/binary_cross_entropy_backward.h,sha256=3cl00KN05OSwoRmArkdTLd_BTk0EZqIqphRUIqJ2Psk,2087 +torch/include/ATen/ops/binary_cross_entropy_backward_cpu_dispatch.h,sha256=uVfJnBuINHvmcXds_-zxZbVAjKO-CO-OuwK5vquDDfc,1372 +torch/include/ATen/ops/binary_cross_entropy_backward_cuda_dispatch.h,sha256=Sl_UHuyC0NLXdOph14CyotpfmcEgIfc9oFQDwxwA8a8,1374 +torch/include/ATen/ops/binary_cross_entropy_backward_native.h,sha256=DJnFZaJSVjcNXcHdYMMRWvf850arAfGcNb4MO6SuZY0,1344 +torch/include/ATen/ops/binary_cross_entropy_backward_ops.h,sha256=cP73AADRlfx9BfTc3mwmESuiKSE-qHMrJ2In_TOLrGM,2539 +torch/include/ATen/ops/binary_cross_entropy_cpu_dispatch.h,sha256=B9U_i7dGjF8l4QpK-wUQyV_1Vbx7qOKZhfk5Pi-a6o8,1235 +torch/include/ATen/ops/binary_cross_entropy_cuda_dispatch.h,sha256=rc-f3TUSgE7qm7ZWzRREQemX47jrpg5gs3FfS8LlNFM,1237 +torch/include/ATen/ops/binary_cross_entropy_native.h,sha256=ZUTF_aCZixJdOtRx0JN-cpQ1wGJFHHxb9aDkVZzZjOs,1166 +torch/include/ATen/ops/binary_cross_entropy_ops.h,sha256=2nvUSJUgfEZcymgEsAadOf9hTIHP888ODL75cbuxX8o,2235 +torch/include/ATen/ops/binary_cross_entropy_with_logits.h,sha256=uXdLPMORhu940Il8mwIQHA1z5J3TlYgebSvo84l-uWU,2113 +torch/include/ATen/ops/binary_cross_entropy_with_logits_compositeexplicitautograd_dispatch.h,sha256=Vd_g6jTY4rye-gFq_e8T7V7zRuTcVVnIkCWuIsQbvK8,1465 +torch/include/ATen/ops/binary_cross_entropy_with_logits_native.h,sha256=ZVhhi4JAo7K8eurSHotzld3xTaZHSfnLVgUrlxczEXs,908 +torch/include/ATen/ops/binary_cross_entropy_with_logits_ops.h,sha256=IO1W6bHJP6FPT0v3ieA6fcI7g2wiwVKv1aq2pR63F08,2623 +torch/include/ATen/ops/bincount.h,sha256=ouYFm_YU5-KcEbF53ch-zYVdRSQ3Pm5xZ7mMr2sIdhk,4271 +torch/include/ATen/ops/bincount_compositeexplicitautograd_dispatch.h,sha256=K73XtnO6li_muGIj3kULk_vpzd1ycHYoaAns2mX2CRw,1320 +torch/include/ATen/ops/bincount_cpu_dispatch.h,sha256=cNW7GyBbdZceLpw07JBWz-DobT3-oDFocyROf3LyIoQ,927 +torch/include/ATen/ops/bincount_cuda_dispatch.h,sha256=6Nt3_BAXjvlYqQN18BVRXiHN1skSPTBZEYu-ek1jM78,929 +torch/include/ATen/ops/bincount_native.h,sha256=7-kDivLFP8xdLRuwR9pP3-p2pF4QmaQ2lhUUGoPuOtg,838 +torch/include/ATen/ops/bincount_ops.h,sha256=36bMmTaFVz1jRwk-hq6O_znHxnPZWaDlHNXGbELjkl8,2015 +torch/include/ATen/ops/binomial.h,sha256=xHbwmFUEGdveICwGS3Tl8jkccHytQzb1mzPoyPZzJOs,1458 +torch/include/ATen/ops/binomial_compositeexplicitautograd_dispatch.h,sha256=5yLUwmjkLjrord49nWVRCQJf6HpPWQm5t5xZI1TBX_o,1022 +torch/include/ATen/ops/binomial_cpu_dispatch.h,sha256=7PmfKrnPNNjNH71M0dNn16L_rjjH-vigjRwLUEr-LCY,805 +torch/include/ATen/ops/binomial_cuda_dispatch.h,sha256=6ZLvp5CuFYedrJBWhkyh1IS0dTQC839u-h5Y9SINDMY,807 +torch/include/ATen/ops/binomial_native.h,sha256=9W5GhvOJNHsCKTvcZ-lo40MZZYyZmaxm0awIa5JlEiY,863 +torch/include/ATen/ops/binomial_ops.h,sha256=W_dpe_s_lHn1Ay7SQmzVBuCSwu3grh2SgnfFiPP6f_k,2017 +torch/include/ATen/ops/bitwise_and.h,sha256=XDlGBpyqvfqSRVhgdewIxz2Fhd6cEAg6AaHY5BtL9BA,2808 +torch/include/ATen/ops/bitwise_and_compositeexplicitautograd_dispatch.h,sha256=8ZaI1H4eg5D_cXL24Bvi7jGQsDcbD5HbSUGog8qBGGU,1400 +torch/include/ATen/ops/bitwise_and_compositeexplicitautogradnonfunctional_dispatch.h,sha256=MJzsMWYVZHkb_5VyR6QjOujDYNDjFMvxA5bGR1eFi1I,903 +torch/include/ATen/ops/bitwise_and_cpu_dispatch.h,sha256=EKMSRJXrsv5kBpkeX3vZpLL7nOd_PhpHorM0coVr9QU,1052 +torch/include/ATen/ops/bitwise_and_cuda_dispatch.h,sha256=kj_kV0ws2q8v7fkOcnn5rNuai5j45Zxc0yiA634Ed4s,1054 +torch/include/ATen/ops/bitwise_and_meta.h,sha256=YvnhGWnpGBfAWb939Me_pHT2hGEV1HHfoq-WF_6yCpg,605 +torch/include/ATen/ops/bitwise_and_meta_dispatch.h,sha256=Wao3v2z8ZnVqJdDL2fM5AioaxK2PQz-U8U3gxwC-6OA,1054 +torch/include/ATen/ops/bitwise_and_native.h,sha256=pDIRg2pdk4-UTDI_Q4_uR6sDb6A5rsfnkE68kWpVoec,1128 +torch/include/ATen/ops/bitwise_and_ops.h,sha256=MD9tXuX1V7YP4XlGCKOYRZ5iulShrb_rcMj8ArVw_HY,5738 +torch/include/ATen/ops/bitwise_left_shift.h,sha256=UXTrO-GRhkPsO9M1pUG0wWlpUmp52_oG2n7YPv2ZCO0,3046 +torch/include/ATen/ops/bitwise_left_shift_compositeexplicitautograd_dispatch.h,sha256=AdNDWeu-6klGNM-OwdXdXixTJpsmOEpx-KERmCnp-64,1449 +torch/include/ATen/ops/bitwise_left_shift_compositeexplicitautogradnonfunctional_dispatch.h,sha256=xznL0Pf7UHLXibz6MhFFPfZTgxTqrueSt7SRfKgcAjE,917 +torch/include/ATen/ops/bitwise_left_shift_cpu_dispatch.h,sha256=Td4Rxgw-afqnp_BWY9OaUuaUFJ27Z-kSI-zTPvyHYNo,1080 +torch/include/ATen/ops/bitwise_left_shift_cuda_dispatch.h,sha256=-h5zYJVAPvIZN37TVh_ldkAjsqoQpKYEvjOLLLAjYj8,1082 +torch/include/ATen/ops/bitwise_left_shift_meta.h,sha256=TGSKIcJelUkABz0fE7NKdrsMfFud3APC2zXJi2iCJmE,612 +torch/include/ATen/ops/bitwise_left_shift_meta_dispatch.h,sha256=eb5xFg84HRrRHn1ubU7lzu2AtnmSndxOlo3lFpOeRTw,1082 +torch/include/ATen/ops/bitwise_left_shift_native.h,sha256=omh-vXxtxkafCdkaRCPUCBDwT0eNyQLoNw1oBaTuckQ,1184 +torch/include/ATen/ops/bitwise_left_shift_ops.h,sha256=6erJQ7o0AuIiqfOi4AtNJUmrYKAiZAa8C5_8yiaHF14,5969 +torch/include/ATen/ops/bitwise_not.h,sha256=Qf3KjsaWthfzmtaa9xVOJglh6AesYeiRCZElGp78--M,1077 +torch/include/ATen/ops/bitwise_not_compositeexplicitautogradnonfunctional_dispatch.h,sha256=dfVbUdqzflc6_VEoePRAtcu8wYq47L8kN_ceko_WtsI,851 +torch/include/ATen/ops/bitwise_not_cpu_dispatch.h,sha256=X1haEgPEEWAhmDS8eI1eFu26hgsW81IEdCd88L9DVKU,948 +torch/include/ATen/ops/bitwise_not_cuda_dispatch.h,sha256=2OSMuXUSuYRNZrIMTmCcISa-jw6xGvwRo9C7IC0652w,950 +torch/include/ATen/ops/bitwise_not_meta.h,sha256=Trd-iSVr1bQNdSKp0mrwEsTuHq7W1Pzgn97dpl-VUdw,572 +torch/include/ATen/ops/bitwise_not_meta_dispatch.h,sha256=zmQxz6rREY57rwObV5jcWvwixD3sVMKyW3VFUDSaN8Y,950 +torch/include/ATen/ops/bitwise_not_native.h,sha256=9cxhR79Hz3arWRD2dmYg5yEC3RJmkugvKmZEMzNeUeA,611 +torch/include/ATen/ops/bitwise_not_ops.h,sha256=ShPTG9U0EUOIxROll8o0XWMNOVmZRmqnS1xKiFk14Tk,2091 +torch/include/ATen/ops/bitwise_or.h,sha256=-dQRPYqNLgsYe682nT2UMeevnzelEZEoh4mcpSPjsz0,2780 +torch/include/ATen/ops/bitwise_or_compositeexplicitautograd_dispatch.h,sha256=JKEgJ0whAcAMbO8NtAt46GUbI-UyQiXVAqHDOIFyxz8,1393 +torch/include/ATen/ops/bitwise_or_compositeexplicitautogradnonfunctional_dispatch.h,sha256=Z1JKmNLgp7K5WmPpXI-oJKMrFrTGHLIogCIZH8m5Al4,901 +torch/include/ATen/ops/bitwise_or_cpu_dispatch.h,sha256=aNXdW5P3YMnd-YnGKwlcz6_jzyRxfVdno8JHOF0B6mk,1048 +torch/include/ATen/ops/bitwise_or_cuda_dispatch.h,sha256=QqkgltCyXXVfabHm7AUK7I6MCvynqL0jsgQ-ruX-nck,1050 +torch/include/ATen/ops/bitwise_or_meta.h,sha256=3J6PcbamSYQ9BkdmHJrUK12jyjwmJ-Gv7h_et0ii5FM,604 +torch/include/ATen/ops/bitwise_or_meta_dispatch.h,sha256=n4UN3njmnUIPZrAvOcob-GKThUtUpouN20gaPZvMunQ,1050 +torch/include/ATen/ops/bitwise_or_native.h,sha256=RyCVn3f2Au_wN-z5B2JPiJS5kYLO61qzphRCvMwoI2s,1120 +torch/include/ATen/ops/bitwise_or_ops.h,sha256=ZOQOPZvO-6n83urF1tvnX9oGAmkFpM6dpHarw84v0eg,5714 +torch/include/ATen/ops/bitwise_right_shift.h,sha256=dE_wO8mF9aV9D0Sn_qodY6-SGjk4ugnyMkBO3q9JW5A,3074 +torch/include/ATen/ops/bitwise_right_shift_compositeexplicitautograd_dispatch.h,sha256=Oeyu_Q4mUOeLpeJ6hLvmvW021M-XSTNwbOXTAhQrsgQ,1456 +torch/include/ATen/ops/bitwise_right_shift_compositeexplicitautogradnonfunctional_dispatch.h,sha256=ZLB1Q67deHOZ5bUJYfyETrdQdj21ElCllB4XTdTXkaQ,919 +torch/include/ATen/ops/bitwise_right_shift_cpu_dispatch.h,sha256=98hmqzwBwCeybollq59L1pkfYnPsYJLDEsmVD9ZNPN8,1084 +torch/include/ATen/ops/bitwise_right_shift_cuda_dispatch.h,sha256=3OCzn7HkXoXiXzX8CjCC10oOf4yYqeR74vVFSOHrTJ8,1086 +torch/include/ATen/ops/bitwise_right_shift_meta.h,sha256=zhgAxKeREnkTAatuTas0oGa2vi9pOVOVVHnIWcera3o,613 +torch/include/ATen/ops/bitwise_right_shift_meta_dispatch.h,sha256=SMycmub9tn68MQc92iVR0w1BGeldVvAj1wh9V2IuzLY,1086 +torch/include/ATen/ops/bitwise_right_shift_native.h,sha256=tvhmyE4JgqaYlG7fCun64TqHcdafA49VC70arNWC5SU,1192 +torch/include/ATen/ops/bitwise_right_shift_ops.h,sha256=no7_Z3CB2Dw0qaBnK4H3UkXJehNSIlMpsaIiFZd3RAo,5993 +torch/include/ATen/ops/bitwise_xor.h,sha256=7TO5jOjvIoVNWmzsl993aXdoumsTQFvtyz7ahodexg4,2808 +torch/include/ATen/ops/bitwise_xor_compositeexplicitautograd_dispatch.h,sha256=cXHAagz2ZgByCJMCtRiQDqol_MQNDNTnzRifpF6ijfk,1400 +torch/include/ATen/ops/bitwise_xor_compositeexplicitautogradnonfunctional_dispatch.h,sha256=rMofNG8-fc5ojqPyq3YW7EeHRF6qcjAHg-xyO-Z86hw,903 +torch/include/ATen/ops/bitwise_xor_cpu_dispatch.h,sha256=k_hDQhYCc6IQJKGHHZ7W8Z8_Isz7y28HJrNgXjFxLbw,1052 +torch/include/ATen/ops/bitwise_xor_cuda_dispatch.h,sha256=SZV093s2DccxtNOVrKT3ekL-Vz4iiXogTrUIZaCvN-c,1054 +torch/include/ATen/ops/bitwise_xor_meta.h,sha256=_B7hx8Pt7ECRSyrzCPTq1_X4S23jGPs5JSCd1K0zIKE,605 +torch/include/ATen/ops/bitwise_xor_meta_dispatch.h,sha256=7-USEQJJdk4qFc-jZEQAoisApIlk7O9s7L6Db5Tau8Y,1054 +torch/include/ATen/ops/bitwise_xor_native.h,sha256=nCC95mKv_02xynDHSVdOGm0Lpw7nHObhKmrrxdObWC4,1128 +torch/include/ATen/ops/bitwise_xor_ops.h,sha256=aMct96QaOa6QWdzBDu7hHkMWcQmbwEH2II23rD0CYUg,5738 +torch/include/ATen/ops/blackman_window.h,sha256=GnRBj6yTN4PwjRK_a7SK5pSthvFlsrNS3BsjCoZGm18,3417 +torch/include/ATen/ops/blackman_window_compositeexplicitautograd_dispatch.h,sha256=oSGfCTqu-iwfvrQYV1zFcmcF53-IFUg_qc_YBqk8B5I,1706 +torch/include/ATen/ops/blackman_window_native.h,sha256=cqw41bzuc36WepI_MfOVonAMkzjKnbYFJ31ga-ylsz8,1067 +torch/include/ATen/ops/blackman_window_ops.h,sha256=TMaZVfcNY58293gUR7z3bROotHKCNcHfRu-PxR94c_c,3856 +torch/include/ATen/ops/block_diag.h,sha256=JIjNZq7KbndytZHuA374b0iMIwfInsi16mEBvvZz1V8,1088 +torch/include/ATen/ops/block_diag_compositeexplicitautograd_dispatch.h,sha256=-LBKvYSKIsSqmw7cYB_Io13cOf82V2S1pV9EZik8Us4,930 +torch/include/ATen/ops/block_diag_native.h,sha256=0aGcg-VNfgZiI6A7LWRM_Z94gAtxXQaIVzsV4zlUmn8,560 +torch/include/ATen/ops/block_diag_ops.h,sha256=T4FmSiMkDqZMyw_lLgVmIPJokHIlcZrwWfuemdyI63I,1569 +torch/include/ATen/ops/bmm.h,sha256=FQfDjSJeZ3ONVAj8TQYf-4YnkU6Slny_h6gYeTWCxPM,1972 +torch/include/ATen/ops/bmm_compositeexplicitautogradnonfunctional_dispatch.h,sha256=lkPn0Te0SbAOYDsmtNCHddxe9ROOx6MJVuXdKj-PxP8,812 +torch/include/ATen/ops/bmm_cpu_dispatch.h,sha256=Tk5v-4kJbuVK7VkvuF8DGjJ7Fx6GncR276JJqANcUdw,943 +torch/include/ATen/ops/bmm_cuda_dispatch.h,sha256=UYPVBBIAamOZtutYWYWLfufmpvKt7bAVuObR_r49VAU,1300 +torch/include/ATen/ops/bmm_meta.h,sha256=5fsc62hfcdMLjgwGeeuJ_ybRUkOc9jp5RScBkIY7oVs,589 +torch/include/ATen/ops/bmm_meta_dispatch.h,sha256=a7XP4cS94lJOIKLhwdGa54UN5gcH5FhLZ7LeLfVGnLU,945 +torch/include/ATen/ops/bmm_native.h,sha256=2UoGfYnpYGIWU049j4gmlyN7v7nsWME9ojW8WObp7NU,1718 +torch/include/ATen/ops/bmm_ops.h,sha256=jTNa_M9bSRP6jR0id2kUOCvULgG9lILnWzg4Lm-nT5I,3148 +torch/include/ATen/ops/broadcast_tensors.h,sha256=MYwTWbSYPVLRmSNEBCcW2V1ASyQvQcGEZ53k8jJxVw0,705 +torch/include/ATen/ops/broadcast_tensors_compositeimplicitautograd_dispatch.h,sha256=2EVZOKuLXaOqBtct3I5TPA5NsexwS7BsyNJYEX3Xmq8,789 +torch/include/ATen/ops/broadcast_tensors_native.h,sha256=RDybWRSb0QIKPExw9BoSZlBfAVRKEdCrWkAm4-Y5_fs,501 +torch/include/ATen/ops/broadcast_tensors_ops.h,sha256=TGEzNaMyw_hWaiKBVjQxAqhBlZPpRPfB9kn29qOg77k,1036 +torch/include/ATen/ops/broadcast_to.h,sha256=dKLBJ-L-KjH9qm0Ena5jPfkSU_4g0wqXRe0PINKFMBk,1451 +torch/include/ATen/ops/broadcast_to_compositeimplicitautograd_dispatch.h,sha256=rDQbKsedKW7I22IjBMmS2vBlpXzpyNCnuZDMYBETdIc,885 +torch/include/ATen/ops/broadcast_to_native.h,sha256=ZUOLleFoLu9w_GAo_sOdBIL_TgXzw-j7j7RWvHCfdq0,515 +torch/include/ATen/ops/broadcast_to_ops.h,sha256=x4i-H6f-djBVcj4ZZyBXiD8E-YWKMBqdX6uCeERNL7U,1069 +torch/include/ATen/ops/bucketize.h,sha256=XvyQ6jMy3BNFZcZHJ3CFZ2TLeEfVZqNxbCPo7hedPhE,2635 +torch/include/ATen/ops/bucketize_compositeexplicitautograd_dispatch.h,sha256=shWFqfYbLNpHzLp0RpXHq2GZmSdGEaRVWFZUCzhDh4w,1003 +torch/include/ATen/ops/bucketize_cpu_dispatch.h,sha256=EQG2QlwM8JXWINglJO6oNeBStDj7td5AilcSdw6JVmU,1215 +torch/include/ATen/ops/bucketize_cuda_dispatch.h,sha256=F2wgBJDfZGEJpJDliBI-LipB8cHdVYOIdnKwz5ThySs,1217 +torch/include/ATen/ops/bucketize_native.h,sha256=wZkag-808d-YPQ5mWT9A_oQnC9uMikh9PPA6fomorHA,1388 +torch/include/ATen/ops/bucketize_ops.h,sha256=Tf_k6v2PK6jHtyKLa2TUpuUvLQqQpB-VtKSGaDlLXQs,3597 +torch/include/ATen/ops/can_cast.h,sha256=eIjOxUsP4iy0SEvWpDYrKIUyFeeVisw_JxK5FsdOzco,678 +torch/include/ATen/ops/can_cast_compositeimplicitautograd_dispatch.h,sha256=P-hoK6KYQUmv7L4eH2FOnCu2M2GFIi7a8HRgj9WR_20,776 +torch/include/ATen/ops/can_cast_native.h,sha256=agyyDktdQMq9pwx_G4-qAiGTG2OfJy49CSXXwnRhzIE,488 +torch/include/ATen/ops/can_cast_ops.h,sha256=8lb_Jfe416Zf5yYMXh48m4fEXMd657eJKXp8yqbA_Ak,1007 +torch/include/ATen/ops/cartesian_prod.h,sha256=GdUBPChbUsRsY2LbFT7esVTlyjt5Xajjq-jNn4V-9es,676 +torch/include/ATen/ops/cartesian_prod_compositeimplicitautograd_dispatch.h,sha256=dmymsj4ZqUPsOKgt_d4eaastHOR4f1xiXDui4nLIIlU,771 +torch/include/ATen/ops/cartesian_prod_native.h,sha256=ps_qy-boI_yADdH29ddMlR3BBOCSE0lwsAY4v3m4wjg,483 +torch/include/ATen/ops/cartesian_prod_ops.h,sha256=yNBLswct1b8xcLEl8IxB5GnEcayGoBSbfylp6gBKJ9M,980 +torch/include/ATen/ops/cat.h,sha256=dlz421lHIammFc5n2xWbWzZQaGtA1GJWtHBFyOgppaQ,1805 +torch/include/ATen/ops/cat_compositeexplicitautogradnonfunctional_dispatch.h,sha256=dRkwFq1hCtidrynksryEs8L442Z7RFwxTjywwk0rZUY,813 +torch/include/ATen/ops/cat_compositeimplicitautograd_dispatch.h,sha256=KxjO71AjyTb-EdyPDOUZTFEJp6fKFil6SUoNqWTdMkk,960 +torch/include/ATen/ops/cat_cpu_dispatch.h,sha256=_zpLE9puvNocjc75ELNUuJKbRyyZ0K2dYHFN-ks69P0,944 +torch/include/ATen/ops/cat_cuda_dispatch.h,sha256=9JBIkZv7H-4HETgykO21_GSencT12_zgC2YrBpfU60U,946 +torch/include/ATen/ops/cat_meta.h,sha256=LcFJn8GsdExsISE3A_y6vbWDTmAC9-xDThfI2CubW6I,4806 +torch/include/ATen/ops/cat_meta_dispatch.h,sha256=vAbAQK7lGBPpvvC7nGZ0XgqqecLedHQ_hkzB6SyWC0g,946 +torch/include/ATen/ops/cat_native.h,sha256=3jkuxBRNA7QbNagnNyzycY5AvgL4akkiHgKXXCjLisw,1551 +torch/include/ATen/ops/cat_ops.h,sha256=0jhOCMWALO5OMwTjNA7ilFvWEw7vflUAEe-TISi_Ync,2916 +torch/include/ATen/ops/cauchy.h,sha256=1w59rrBkSor1b03srQChc8hGJzEAvFlDPtZVfKHXhl4,1533 +torch/include/ATen/ops/cauchy_compositeexplicitautograd_dispatch.h,sha256=2Ua1jy1AZbIgYTCGj1fkVYs0FVHnxcQ3Jv1KYpUM5sc,1172 +torch/include/ATen/ops/cauchy_cpu_dispatch.h,sha256=SP1xQoL4gRuA_dxhb8RY4RIkHbmegMEfVVnOrFOwrVA,807 +torch/include/ATen/ops/cauchy_cuda_dispatch.h,sha256=kH2i_Vxa5eVAeeCum9iVSyK4BFNbdCaAviJGzcWyZ-M,809 +torch/include/ATen/ops/cauchy_meta_dispatch.h,sha256=FaIREEwHnZnLHtI5U40hEA8ls2xQzGHWjRdvc0JeOzE,809 +torch/include/ATen/ops/cauchy_native.h,sha256=pEvzBSAz44-OKLaFxLrtEfctwGEUDSxJ5smYkPOMgTE,856 +torch/include/ATen/ops/cauchy_ops.h,sha256=cPCJPzWqBwrzfQ73osnYS5vVgX7ddpbJz4jaf2K831E,2796 +torch/include/ATen/ops/ccol_indices.h,sha256=DcmbH34J8uGfUo99IVBWX7RBYnq2TEJ36zzwELoiL5s,509 +torch/include/ATen/ops/ccol_indices_compositeexplicitautograd_dispatch.h,sha256=4WGOOfAKe5FzRJgpgbWPk1dpC9mT6flRTo-jlF7Rgks,770 +torch/include/ATen/ops/ccol_indices_copy.h,sha256=S2PD0czMfdokJ8ojqFlSj5iOz5QhvqTeEdI_48qoI8g,1137 +torch/include/ATen/ops/ccol_indices_copy_compositeexplicitautograd_dispatch.h,sha256=bVxleViJAnqMvlb0AnvtmSsAQfECmNGip_Sf87jmMU0,889 +torch/include/ATen/ops/ccol_indices_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=3K7KeH9UnDDQD9xzgR5RCuyDyaRymqeSHQHgfU4Yzds,801 +torch/include/ATen/ops/ccol_indices_copy_native.h,sha256=3w1xKqQ6_TwTcfjb7cyhXd_OX4ds0-3qGnDlt45NBGw,576 +torch/include/ATen/ops/ccol_indices_copy_ops.h,sha256=2-SwCj0WC1w46ZuY18pKHYEQ9v8cvdCT5BaMGF4j9Ro,1613 +torch/include/ATen/ops/ccol_indices_native.h,sha256=WtoEaiob1kifXdBK737kQgcejCPjcAnGiuLBz-3NBzk,561 +torch/include/ATen/ops/ccol_indices_ops.h,sha256=RaNCUQL2KjTXpXV80G1YGn70ieyMtzDOdnBUATXmWOU,981 +torch/include/ATen/ops/cdist.h,sha256=rsKkD8RvhG76cv0brTrqmURD6yQwbcCj6vg5NAOLtMU,783 +torch/include/ATen/ops/cdist_compositeimplicitautograd_dispatch.h,sha256=tf1lklD-TzUpRYE0TIfETrhHG9nGwnPqSPVFI2qW1C4,850 +torch/include/ATen/ops/cdist_native.h,sha256=7RNYMvojjrctC0WB0raDqsXBkDHK9uHF4Fvyg0njrAg,562 +torch/include/ATen/ops/cdist_ops.h,sha256=X0NFEai8ieDK8Q35urEdV3WKN-w3hFXOvHNon_XMxLA,1192 +torch/include/ATen/ops/ceil.h,sha256=mR6j2C6el55vNiSmmj7x5z7hbvR9PTPKVjEgkgu9HxI,1143 +torch/include/ATen/ops/ceil_compositeexplicitautogradnonfunctional_dispatch.h,sha256=WP7fIJDR0KTZohmjmePgEXX00sN3cbK4u8hi4PCZM8Q,837 +torch/include/ATen/ops/ceil_cpu_dispatch.h,sha256=7zwcrxZGixOXwT561Voq0rQRziNhnK953PwQRbmsvAk,920 +torch/include/ATen/ops/ceil_cuda_dispatch.h,sha256=Tpk2bpAj3aYJ-gptXs2H-4Avb8GqWK_bZWVQa9-OfkU,922 +torch/include/ATen/ops/ceil_meta.h,sha256=f-9hDeKVCzCJnYxZTU5Tkcrs2efR1Kxc-QK4p_dhHcw,565 +torch/include/ATen/ops/ceil_meta_dispatch.h,sha256=QQfHlzGmC3pWl94y77QsMZP0SgSMC8cqV5tlvtDf-5E,922 +torch/include/ATen/ops/ceil_native.h,sha256=oummupXxTbLVthscStMJGIsm91ZobPi1zkUuhaR9AWk,998 +torch/include/ATen/ops/ceil_ops.h,sha256=GnAM4B2dhMIEIplvHSPjtPJiUkjgmzBx9NPpYgknTxg,2028 +torch/include/ATen/ops/celu.h,sha256=QxjXZRLNLSx1gV_8dS7XGhZKIaRxAkyzwJvIr2EGtHY,1359 +torch/include/ATen/ops/celu_compositeexplicitautograd_dispatch.h,sha256=d9sUBHZ9CwUxLUt7_FTphTtbiVqNbgB1DCAFPblSESM,1080 +torch/include/ATen/ops/celu_native.h,sha256=yj7x2GKfwZse9NmH-9h3poqOeIC_IMlK32ifXYNfFjo,685 +torch/include/ATen/ops/celu_ops.h,sha256=wjS1V5F144HWdQy51PEWpaC_WO6_QXtmubaDqhRBQZw,2298 +torch/include/ATen/ops/chain_matmul.h,sha256=5e0svL5GjE_OKnfYW7Jt92us8aeQbfhilgdZ7JAbGwA,1117 +torch/include/ATen/ops/chain_matmul_compositeimplicitautograd_dispatch.h,sha256=tXk-x1GHWlZ5qbO2J5K-nr7IReloIamp6TqcWRt3DU4,939 +torch/include/ATen/ops/chain_matmul_native.h,sha256=gY2BLo6j2zdzYLSQxm6X2FFq-lN26C74XyqXn-UfnQw,566 +torch/include/ATen/ops/chain_matmul_ops.h,sha256=CGL6NUTWLqfwPTQ3KdfoB_2dweY33MYURhF2yn5YClA,1587 +torch/include/ATen/ops/chalf.h,sha256=4nYON1r_t_yd8R_5Wed2-LoPkVqdHMCgw9y4Ol8RLNw,502 +torch/include/ATen/ops/chalf_compositeimplicitautograd_dispatch.h,sha256=58aHcm9DL2RtTTBuNeo7OpKysSgUyr03Ym_5GkzxDO0,827 +torch/include/ATen/ops/chalf_native.h,sha256=9pecLWB1WCkQCPsNdMsxWyolTOLfpsFN9fkh27Zsyo8,539 +torch/include/ATen/ops/chalf_ops.h,sha256=xH_CQGetrC3HpeW_-mDUbHi5IbvQf2KVcSN0Q6r3P24,1124 +torch/include/ATen/ops/channel_shuffle.h,sha256=W6fOl9O4xyNUkvNyJCFgFPIWbifyJs_wcw1Qc8fK6t4,3566 +torch/include/ATen/ops/channel_shuffle_compositeexplicitautograd_dispatch.h,sha256=xFU9dYhNLYPJpMfKn_q4cGbvRc5Vk1fyTysp6NINZQI,1146 +torch/include/ATen/ops/channel_shuffle_cpu_dispatch.h,sha256=QK4ePyg72Ho8o65p_egeK-ujnrdXE6obVJruRUgfAAQ,835 +torch/include/ATen/ops/channel_shuffle_cuda_dispatch.h,sha256=UgzPWdfgg7OGtmaGlgejv9Nl7gNTBh20AvJeY_ZuJS0,837 +torch/include/ATen/ops/channel_shuffle_native.h,sha256=f5Klv5bjOPaABtoGO1vB3I8A9P6cTjgYWJz7WyXD2H8,708 +torch/include/ATen/ops/channel_shuffle_ops.h,sha256=w2LToF7bFF2Ofs6-OS8YGUuQvXde548IHiFJsARqWwc,1737 +torch/include/ATen/ops/cholesky.h,sha256=ZmHCURdJ_lHPGjcWIzLfx8gTzjq46X_o_LiMuMwyMzs,1170 +torch/include/ATen/ops/cholesky_cpu_dispatch.h,sha256=D58RdBAXHqkk2keK-Dxh3TnwKkOsAE8AJvhg4Fbs6WU,931 +torch/include/ATen/ops/cholesky_cuda_dispatch.h,sha256=tgs2VPSBRRpUsnTj-vm-RmrCWgh1ckRu-K5WDSMzHIE,933 +torch/include/ATen/ops/cholesky_inverse.h,sha256=lTf28mcZxAoOV4hgMiODJHI2DhIFqGwEDJwp9RApxbM,1250 +torch/include/ATen/ops/cholesky_inverse_cpu_dispatch.h,sha256=DRZm5bNPDnMmwGXtdtRc7Hu7Sk3G9whru8UVL-GUN3U,955 +torch/include/ATen/ops/cholesky_inverse_cuda_dispatch.h,sha256=yuJ_hQntkLoX8PGe4sMY7IVf_vhR5lu-YnAh5KRZCYc,957 +torch/include/ATen/ops/cholesky_inverse_native.h,sha256=olzfLVWF-fnCQKkwWgaiE7YX8G8hVQff_vVOLXHunLI,604 +torch/include/ATen/ops/cholesky_inverse_ops.h,sha256=pYGG-FDe97T9M7bisegGBifElYFGlnXnxlYnT04Xbk8,1703 +torch/include/ATen/ops/cholesky_native.h,sha256=0o6ZCfQI2kOpgpHl8SC9Q4LYp6bhhc6anct_j512GNE,588 +torch/include/ATen/ops/cholesky_ops.h,sha256=bgyfZUQ-mvpXF_fu0j8XCpGeAD20qDJzCJuvIlIvJqY,1655 +torch/include/ATen/ops/cholesky_solve.h,sha256=32g71k5zgHEutNUz-F0yiFoTqZhb_ttsGzet9B5rVvc,1380 +torch/include/ATen/ops/cholesky_solve_compositeexplicitautograd_dispatch.h,sha256=7LP8DVCZhUR-9GC-shuwTxYYkZlm42sa5qXbAxHK1V0,1074 +torch/include/ATen/ops/cholesky_solve_native.h,sha256=z74F9QisaBsUPpwpVxi4f4rG1mrA2xeusXoW2xe2YXo,654 +torch/include/ATen/ops/cholesky_solve_ops.h,sha256=mIYT9ygMio-n6jqIGBS8l5k0C6bsLw7Dqu-1OZ0udyI,1869 +torch/include/ATen/ops/choose_qparams_optimized.h,sha256=Tv9Z0GRDVrBodSzk8KlhQmcbyUEtbsBz3Eut5jjSmZ8,895 +torch/include/ATen/ops/choose_qparams_optimized_compositeimplicitautograd_dispatch.h,sha256=RE1in_qBLmU9q-dyCaPFiEziALzkifJw1kLDiYL3t2c,872 +torch/include/ATen/ops/choose_qparams_optimized_native.h,sha256=QWiISZ-t_tL-RdesTCe_EQdlUM0efWvpn45rkMAaHds,584 +torch/include/ATen/ops/choose_qparams_optimized_ops.h,sha256=PBW8oq4PvLmX5SNEBVYS3jAESPES5mbLrEOhM-DjaoQ,1313 +torch/include/ATen/ops/chunk.h,sha256=wM3M_kk-3ZKCVAIdn5fqghP3mB7KuID2EsroUFJthb0,728 +torch/include/ATen/ops/chunk_compositeimplicitautograd_dispatch.h,sha256=Z9whs8-l3niDCyIflFS2476ld4SW4YEQK-EeS8fkay0,809 +torch/include/ATen/ops/chunk_native.h,sha256=Bm7RCdDEmfKip5Ibd6eDsS35YFcdbUoP_b0e0YYm_1k,634 +torch/include/ATen/ops/chunk_ops.h,sha256=TcR-Ha8ZAf2fArVCPRxOyMiNvjacdYHw1oc9Fcq9kRo,1111 +torch/include/ATen/ops/clamp.h,sha256=Reio9qjbGantyKW8Efony1AW6S_9XWunB2gWjGUe9-8,2954 +torch/include/ATen/ops/clamp_compositeexplicitautogradnonfunctional_dispatch.h,sha256=T0apPFk4JJOGan9u0tyYaeGJPD7gzUv0ZAebW3U0Qq4,1312 +torch/include/ATen/ops/clamp_cpu_dispatch.h,sha256=-gArl3MHHuvTcwEauQ2kC2g75XvhmKwxwtkSYh-oYjE,1901 +torch/include/ATen/ops/clamp_cuda_dispatch.h,sha256=qJt9b0in1x9IjCE-7ZCIcsqjv-Tuh9hEbdSx3nt4VvQ,1903 +torch/include/ATen/ops/clamp_max.h,sha256=4L2yK6gAZ1YF8yM9JWpz9H3QyEHnXavvEcUknBQEnrQ,2295 +torch/include/ATen/ops/clamp_max_compositeexplicitautogradnonfunctional_dispatch.h,sha256=W91EUPZhWIMWT_xkkeQz25f3Ye4BQ9YLX_7O7uxRWdE,1054 +torch/include/ATen/ops/clamp_max_cpu_dispatch.h,sha256=LDogH0WJh2EAWV4_ijIKirYlA6i87B_kdayAq3r46U0,1406 +torch/include/ATen/ops/clamp_max_cuda_dispatch.h,sha256=kXt-RgfBhjjof4v2D0k5iUYVwz-o3qyAKwPSbMeMR6o,1408 +torch/include/ATen/ops/clamp_max_meta.h,sha256=-aijKUwNcIARUnRkH2ld3tNe5xp8vrRGDjvuMkp8gXQ,738 +torch/include/ATen/ops/clamp_max_meta_dispatch.h,sha256=w4HHJ5S7uHbWPiIbjWK1BNXcbCzTc1IM7MgnEx3anQY,1408 +torch/include/ATen/ops/clamp_max_native.h,sha256=GNqQyYmuyIajfjObAMmiuN_OtBPECwzWiVyDmzoe-nc,814 +torch/include/ATen/ops/clamp_max_ops.h,sha256=mN5xQIh6HI6H3jc1MvAPD4IWOoOfvjIscFBFRUJbQbw,4230 +torch/include/ATen/ops/clamp_meta.h,sha256=9vYIkIQ8KdwprIoy9Sgep94Xnn--teyM2XV7UJeNx4I,790 +torch/include/ATen/ops/clamp_meta_dispatch.h,sha256=tMUcaMLvNZdAvsYA7riPJRbGp1JtTK5KR0bZO0iqR-w,1903 +torch/include/ATen/ops/clamp_min.h,sha256=OLyzZQTk_U2KeT7yAZ5UeDKbrHjSP2Rb-6CRpkEvoQE,2295 +torch/include/ATen/ops/clamp_min_compositeexplicitautogradnonfunctional_dispatch.h,sha256=VcAAaMT34NKMJrYZba60ovpPs6FqlyP2C9-notPuqXE,1054 +torch/include/ATen/ops/clamp_min_cpu_dispatch.h,sha256=pWeh2HkMexdrYj8juQmQlpidc-J2dkkaehdcNc7jZVE,1406 +torch/include/ATen/ops/clamp_min_cuda_dispatch.h,sha256=2GQ470IpvGVcnQBastPbzBNsxa-wRs8nMdIbUK4tpF8,1408 +torch/include/ATen/ops/clamp_min_meta.h,sha256=gMDzhIdp-V9B8BxM1JM-57Honwn5zzcuz6yDfxaNGbo,738 +torch/include/ATen/ops/clamp_min_meta_dispatch.h,sha256=rjEqR4kNym9Nt7gI8nKdXNPbZRRdPhpHFL1gJJmj048,1408 +torch/include/ATen/ops/clamp_min_native.h,sha256=5Pb_md-6UJMh_UWxBsSuY5JY9kETH5qkBhiGkP_jjEs,814 +torch/include/ATen/ops/clamp_min_ops.h,sha256=mFW-oPIuN3YjkZ_GYjGyrHC-1ZOgrjXb9ttcpBh11HA,4230 +torch/include/ATen/ops/clamp_native.h,sha256=hjObRWFSuXBOgOOMUKM35LaDODO_UISE-zVNV4_u7KM,1033 +torch/include/ATen/ops/clamp_ops.h,sha256=kXK8UUOXKUPbvDEItiLoplGtRyy2DD4fDvgRneUmMY8,5322 +torch/include/ATen/ops/clip.h,sha256=7XNZUY1hqYMwy8-ITjj-TuWU7ID0O35AyUV5O5-S4wU,2929 +torch/include/ATen/ops/clip_compositeimplicitautograd_dispatch.h,sha256=Woi8jGOoQgAT2x24KBNzyAz5vo6UhXSiycXfsmSQDBo,1937 +torch/include/ATen/ops/clip_native.h,sha256=ELHgT6lORnkgu4pKaJ0-ofTmqZlln9O2YzvkBu7EvOk,1340 +torch/include/ATen/ops/clip_ops.h,sha256=aEABNLNb3gepr4UEOan6tArcglxjKoiOxc2CO2QXVB4,5304 +torch/include/ATen/ops/clone.h,sha256=AeFIMCVNoQkuwQOpG1p2s-cPMyqhkTqBeX4qSix5els,1344 +torch/include/ATen/ops/clone_compositeexplicitautograd_dispatch.h,sha256=TQRJ1nYMyn8IMa50d186QfF3FFmPHRScMvbgczaxXDw,1095 +torch/include/ATen/ops/clone_native.h,sha256=xuRpTDumCP6jWHIqTawnYpskOq3dE_wuPrCMhtSdHeE,1299 +torch/include/ATen/ops/clone_ops.h,sha256=jE5hra_JNsiIvp5bCoBjxpR4nknslfqzcZ1aiAbEPIg,1878 +torch/include/ATen/ops/coalesce.h,sha256=6Ujzwihpj7zyicOROgoDID5FyhTWFcylj0QvSYO75UE,505 +torch/include/ATen/ops/coalesce_compositeimplicitautograd_dispatch.h,sha256=nFzB-bQ0N0MOJHUFrhpowltb0DP2uxWppm6if3mWlck,766 +torch/include/ATen/ops/coalesce_native.h,sha256=Oa86GIAfwYM1nc9iWyRR2mZpFmzsNwqP7vXzbQuJCPY,478 +torch/include/ATen/ops/coalesce_ops.h,sha256=jo2G3-pfAItiqpM7VZb_G8cavornc8GiAA87MCuKXcA,969 +torch/include/ATen/ops/col2im.h,sha256=UxTHTeCNqGZ977mt1oVDg5MG7H-iqT4cBv2olZqcOOc,5843 +torch/include/ATen/ops/col2im_cpu_dispatch.h,sha256=0p1Ni6Xvk1tICZ9Lfl_dX-BDlnbvlZBhu9wCIJ3JQSA,1919 +torch/include/ATen/ops/col2im_cuda_dispatch.h,sha256=cjoEDOZBzvgv4GDYNhFPR6RMuI-YnA7eAnRfJz93V7g,1921 +torch/include/ATen/ops/col2im_native.h,sha256=ZwNkZh1FCdW3xFUksmM5sWi1uAk5uFzE5OClGjY1G7g,1236 +torch/include/ATen/ops/col2im_ops.h,sha256=aiL0vvZxi15CCLihL77G6YthNmt65gL_xUIPSPHdulc,2455 +torch/include/ATen/ops/col_indices.h,sha256=Hd49EeeAzn_UHUg7IyXnIyHfVWrpfyKRlY9-0TWYebI,508 +torch/include/ATen/ops/col_indices_compositeexplicitautograd_dispatch.h,sha256=GcY7_tnCy0qrhoDP335cbHGm240I6UcwcrCd1n5-5KY,769 +torch/include/ATen/ops/col_indices_copy.h,sha256=MCPh3sOX1_czTe5NV_7iVHLkKHQ4nUlBE_xklO7BQsY,1127 +torch/include/ATen/ops/col_indices_copy_compositeexplicitautograd_dispatch.h,sha256=U65MR5erMxTs_pIluZKSFHpvZ6hHgcalC-vI-xYgng4,887 +torch/include/ATen/ops/col_indices_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=Xf1aREZaOYqUgVxd2-Qn1IjPYv8L18LmGzKFlh9HHGA,800 +torch/include/ATen/ops/col_indices_copy_native.h,sha256=z5dEMvUyjUxBznHleJFBjOBfhlwhxs80fxqvt6Hi7YA,574 +torch/include/ATen/ops/col_indices_copy_ops.h,sha256=INT1O2nxwgw8kSOECLSVN1z1uBhgal5Ry97JeI7X-1M,1607 +torch/include/ATen/ops/col_indices_native.h,sha256=2Bg35AaEL0mmvZFoEs7s-7SQJL98QyA008IbdWX5Tlk,559 +torch/include/ATen/ops/col_indices_ops.h,sha256=NNepE0Ypy-fkIkDQsh85avS_AfJviesasiTiCts0yR0,978 +torch/include/ATen/ops/column_stack.h,sha256=GLPjTyfJKqI_PXGHbnwMMfCvhK7jWalNpNOg3EWDRj8,1108 +torch/include/ATen/ops/column_stack_compositeimplicitautograd_dispatch.h,sha256=JXWKV1zOf0K0vJJILsp3bdowyzsxkdk1PIAlTL3OIDg,936 +torch/include/ATen/ops/column_stack_native.h,sha256=f5lYYkq6MXG3L-tMr8qtRyw0RGJ7mooyGKvl5Ioz_Ac,564 +torch/include/ATen/ops/column_stack_ops.h,sha256=AjtQ7cWCHy7av-6mGpNYm7E_FAN68Z6c01CtMqhKeFc,1581 +torch/include/ATen/ops/combinations.h,sha256=1GmQoUj_ZX88Yf4K9JFND-U04-RBZu32zz235AawZS4,762 +torch/include/ATen/ops/combinations_compositeimplicitautograd_dispatch.h,sha256=HINIJGIdGdJCnEIXLIEMdqOz8Pb_sUL09bhrFsb2bRs,812 +torch/include/ATen/ops/combinations_native.h,sha256=V97XOLk0vLw51jTrIUkT3JmJu9FMmGQg5nPN5_DFqHc,524 +torch/include/ATen/ops/combinations_ops.h,sha256=ZHRstyFlMUMoFqpq5tzoMSAFvxl2aR-itgAlr4bK34U,1096 +torch/include/ATen/ops/complex.h,sha256=AxAH53XE4sKdRSeJiQxVMb9zvgbuLiQmD-gzL5UcfoI,1169 +torch/include/ATen/ops/complex_compositeexplicitautograd_dispatch.h,sha256=FjsHHUPn2jQRIC77XSuQZZzklU-WCHpEexVYM9HJTq4,790 +torch/include/ATen/ops/complex_cpu_dispatch.h,sha256=-nU9jv1fw1XsrEK7JVNMMsor3ntzNGYO23zDDHgDE6Y,875 +torch/include/ATen/ops/complex_cuda_dispatch.h,sha256=BEbeYfSJrsq0qQcRdwz-Os_fCU_pumWVfLI-QDpL_Wk,877 +torch/include/ATen/ops/complex_native.h,sha256=Q1Gft4e7gxyTdw8peFfjt-Vwo0TLQ_GG2ZMyXeHPkGU,606 +torch/include/ATen/ops/complex_ops.h,sha256=eue0n_ASReHKLnqxRx3TNzP4K3W5Dtpci5vCl1HfDPc,1719 +torch/include/ATen/ops/concat.h,sha256=bz6izCKOZR3q2oFWYYStftqgOPEvHg4wLL-jmIAukjU,1826 +torch/include/ATen/ops/concat_compositeimplicitautograd_dispatch.h,sha256=XSGUZa5yG4c8FnA7utPF5aMBFpNPzrF-P7nhoxxeuFc,1220 +torch/include/ATen/ops/concat_native.h,sha256=JA60EognDnUJEq_IwfVmLt80elcsZLFzEoMvIYFvRiE,744 +torch/include/ATen/ops/concat_ops.h,sha256=RwrCoxSKNeAjiPfoafpx67xLlWzncZWTGB4gacuoqRI,2880 +torch/include/ATen/ops/concatenate.h,sha256=KyiRlNsrFDfehBKL-bsSnVmCEF8VYt9kJMqmho_lz98,1921 +torch/include/ATen/ops/concatenate_compositeimplicitautograd_dispatch.h,sha256=T6BBiY0g95HyKHsqrTldtdhEI99tUt2y6Zhwh6Km7-o,1250 +torch/include/ATen/ops/concatenate_native.h,sha256=Qzcddu52uCYEf_H89SX7KBDxh1F9uEVG28IPWq-GmVg,764 +torch/include/ATen/ops/concatenate_ops.h,sha256=XS9VgWE-0FArOYpOTZjObBMLYx4ONvMusPAfT7LaRE0,2940 +torch/include/ATen/ops/conj.h,sha256=FwshQ3IUdYJLKD4ZzP5g26_4OB4eKBniiP7VSFXkgrk,646 +torch/include/ATen/ops/conj_compositeimplicitautograd_dispatch.h,sha256=gG4OYaWXqTf9ozYuc1YoSj3ATrwz3RKKf9yw6asFJoA,762 +torch/include/ATen/ops/conj_native.h,sha256=nBLarQlkRPHvSQeH4avrf9QOO8spOJTVzytHNiG94ZI,474 +torch/include/ATen/ops/conj_ops.h,sha256=ZcBGYdo8H6jJyTzqu6iJ6W2c2tndoBUh80M0GhkLPYo,957 +torch/include/ATen/ops/conj_physical.h,sha256=tCf6OPoRQpIR2VwBFk-W2YVrw_HhwkhkymEAgqrqD98,1260 +torch/include/ATen/ops/conj_physical_compositeexplicitautograd_dispatch.h,sha256=on5qUc9_taEPD9Krkd4JsZdCoXvZUMQjsyUQvJO2J_o,768 +torch/include/ATen/ops/conj_physical_compositeimplicitautograd_dispatch.h,sha256=VmeGaNahiSzBJT4RTXMB8BDqQLvOZcjWe_xRimIPP2I,771 +torch/include/ATen/ops/conj_physical_cpu_dispatch.h,sha256=7iNhZm--iZqCsPzG4GWqc9BOPh7maSzZ9hxNtCkJxPQ,837 +torch/include/ATen/ops/conj_physical_cuda_dispatch.h,sha256=GjNPY76piL4f1icyPg4h3C95b9pnFthC2EyXFooNxZI,839 +torch/include/ATen/ops/conj_physical_native.h,sha256=9h_4IOD8MJBhpf8-oL4uALNv8OBMShZ2xP0crh2xzlU,883 +torch/include/ATen/ops/conj_physical_ops.h,sha256=LwtjO0A_rkVLWJfGrUP6FQbjFSCgx08T3vMbogRTf0w,2109 +torch/include/ATen/ops/constant_pad_nd.h,sha256=T6FWtkUWFPG8ZJZ5W7IP2sElBUId4fJpz9aaLWr5BXo,4248 +torch/include/ATen/ops/constant_pad_nd_compositeexplicitautograd_dispatch.h,sha256=ATkctSPAvOi9sYa5tHfr3rsOl7CMokuNOQvLfF3HsKE,1509 +torch/include/ATen/ops/constant_pad_nd_native.h,sha256=h7Zt3WmcTnGrhKPZG2Roz3HQ13ZGuFbV0Oec8xal4_A,679 +torch/include/ATen/ops/constant_pad_nd_ops.h,sha256=ix3dAAccdrzLQGl0ZFkUg-e6hQl7dw2PydN63oeyJqI,1947 +torch/include/ATen/ops/contiguous.h,sha256=v-bFD6aPpRuVCxLV7jjuJBwlbJDQoBR75NhInmbVCHw,507 +torch/include/ATen/ops/contiguous_compositeimplicitautograd_dispatch.h,sha256=Om4qfc58b6z-wIaMEJygUOkXxc1XKJT3UBJyIooVEs8,830 +torch/include/ATen/ops/contiguous_native.h,sha256=zHyrfkLMPK4I3vZyP0AEEECO1DzI6E1mqUhnqfmC5S8,542 +torch/include/ATen/ops/contiguous_ops.h,sha256=FPXMujAm_Qxtxh8PUWvSs75ddU19boEAJxvDWWF4wCU,1106 +torch/include/ATen/ops/conv1d.h,sha256=6MMk8wNP535t8D6SuKMPAYJ9HVSQEE1jEcbGZDjNp6A,4520 +torch/include/ATen/ops/conv1d_compositeimplicitautograd_dispatch.h,sha256=OWBI6dqXwH1nQt1DZXNFpn_jk6OU93QN3XrVhYKduD8,1696 +torch/include/ATen/ops/conv1d_native.h,sha256=A20npVZQQSTglRRt-kHjWJPXsgPmECF9cjd7hGALRuc,996 +torch/include/ATen/ops/conv1d_ops.h,sha256=oD43cSTNaCkI0SP8gEbqluIu6mst3OHxCm-5JHPRymY,2677 +torch/include/ATen/ops/conv2d.h,sha256=NHDnQbAArHTTdSmOVyuIsm7adwFZqa_FywVkuoxZVxI,4520 +torch/include/ATen/ops/conv2d_compositeimplicitautograd_dispatch.h,sha256=AOljqpFi1ljH0z2buNtdyJql8vaQ3XwqybwsgGcMcpU,1696 +torch/include/ATen/ops/conv2d_native.h,sha256=G-aEKDkPr6LYHcLXe2HpbqBF7HC1YGMQiQK1Qi8lU4g,996 +torch/include/ATen/ops/conv2d_ops.h,sha256=LpNdSiSsXHnGbmQe3AYpYuAWzx26N_6iJdPXiz1hr1w,2677 +torch/include/ATen/ops/conv3d.h,sha256=8xjWRBKeSGnZF3HxmUm-AdKWHin_f0Qn5gQYzfH6s7c,4520 +torch/include/ATen/ops/conv3d_compositeimplicitautograd_dispatch.h,sha256=Wk1r3G4ngLImQGximQ6E25efY0w1Hoh7DB8JWw8gzAk,1696 +torch/include/ATen/ops/conv3d_native.h,sha256=TxzdeWQX4OGpS1ngVOYP3AEstMzHhbRV1Fz_PMhRD1c,996 +torch/include/ATen/ops/conv3d_ops.h,sha256=N1iUNMY2iWREzZS0awA1Hx1L0SQwqWz9MODIat0mkyA,2677 +torch/include/ATen/ops/conv_depthwise3d.h,sha256=ave9v5oh7EkM5dZsOR1zIylZlF6AohrenNIzpr3myzs,7293 +torch/include/ATen/ops/conv_depthwise3d_compositeexplicitautograd_dispatch.h,sha256=oqzNaF0y5pgh9xry6i6fXb_uMLuMYLsQ2oVSLtoytI0,1802 +torch/include/ATen/ops/conv_depthwise3d_cuda_dispatch.h,sha256=qs12HHbFYRLGuwPFRLoOlosj4yg1coGbBsPRxrUoF-c,1165 +torch/include/ATen/ops/conv_depthwise3d_native.h,sha256=O7dOP4E2ZouFuoNriLsXuQKHiPkfrx1NZ38GxvLp81w,948 +torch/include/ATen/ops/conv_depthwise3d_ops.h,sha256=SH3J6r7hfBSWIUQwJKYyC4wP52nd2QMi40bSjggBZ90,2863 +torch/include/ATen/ops/conv_tbc.h,sha256=ljGd61Vmt3MuJNQjvK4RQKRnuEbTqlfWwmxDJ0dIpdY,1420 +torch/include/ATen/ops/conv_tbc_backward.h,sha256=MCPZ3yka23oDDL-NKEQtLVigTlm2W6RizYIceib61Sw,903 +torch/include/ATen/ops/conv_tbc_backward_compositeimplicitautograd_dispatch.h,sha256=6GxVEHNMyW6tottxNnWkUrZxE53IhThRBjiQLDvzbzw,902 +torch/include/ATen/ops/conv_tbc_backward_native.h,sha256=GQFZosWJBme3TQ3PHaOzbAYPoFXhlBod_kcnGFQ7E_0,614 +torch/include/ATen/ops/conv_tbc_backward_ops.h,sha256=-Gzt76RpVwbzp0_OcdxpS1XzzEkjgu2fi5oWb1-J4IQ,1418 +torch/include/ATen/ops/conv_tbc_compositeexplicitautograd_dispatch.h,sha256=hprKU4WmQ-Xao_LP1nzdSbQ31KxB2cBTP4mZeT9DTz0,1126 +torch/include/ATen/ops/conv_tbc_native.h,sha256=xNBUWxM_nozc3l7AYl5ilnMk8LYf6eAhDb3mA_NdOJI,690 +torch/include/ATen/ops/conv_tbc_ops.h,sha256=KxgoQYf3FAtHp7PidNkBZ7j1Ozy-8ebAic2L_9GuevA,1995 +torch/include/ATen/ops/conv_transpose1d.h,sha256=Gxwlf2t77oGJzNNJWBMn0GL01B82lDxMTNJzP7TrZcQ,3007 +torch/include/ATen/ops/conv_transpose1d_compositeimplicitautograd_dispatch.h,sha256=UthS6txZ_F3D-2rAAmzrnb4jDGklskGMXuLoU4sFMpc,1329 +torch/include/ATen/ops/conv_transpose1d_native.h,sha256=UL8cS2WEel6JZNpxdV_j5xTFNk6ECU-_nBQtSLOTY7g,771 +torch/include/ATen/ops/conv_transpose1d_ops.h,sha256=cdgNaazZo9OBfd6smqjX800JXZUr9f9igQNib2TztEc,1710 +torch/include/ATen/ops/conv_transpose2d.h,sha256=Z_ganM-8VVq7uzvUweup6T1sRyETPYCgwED_iHw7_IU,3043 +torch/include/ATen/ops/conv_transpose2d_compositeimplicitautograd_dispatch.h,sha256=PEg-LkSiwoj4SS9kqklSJvIKqSXGXBoCUlHpjAYwXbU,1329 +torch/include/ATen/ops/conv_transpose2d_native.h,sha256=-uZWdT2-58F3xm2FA9WeNokawu2ks--INcQsd4OG9Ug,771 +torch/include/ATen/ops/conv_transpose2d_ops.h,sha256=L13ppVYa4uMs7Drw3GJBI7bG1GxDcSKsl6tR-qb2vl4,1727 +torch/include/ATen/ops/conv_transpose3d.h,sha256=7XmGZx0kkCsDzvz0EvxS00zPGrHJivYp-Pj4UQj622E,3043 +torch/include/ATen/ops/conv_transpose3d_compositeimplicitautograd_dispatch.h,sha256=8yHU2kA6XYgpz2kJI_V6INFOV07_LZRRj6OCsX2oOo0,1329 +torch/include/ATen/ops/conv_transpose3d_native.h,sha256=ydyrfLYAyfp8C0lwYSxGkMt6kBBJwM7lMxeVWoFMyJ8,771 +torch/include/ATen/ops/conv_transpose3d_ops.h,sha256=9m3xPOnMNIeIq8wgfugZQmVAaDbdGF2AIo3nerqGU0k,1727 +torch/include/ATen/ops/convolution.h,sha256=XIb4Xwhq2IQ6Qww8HURvU3Myvz3XCWOqHwj1IUrB45Q,8086 +torch/include/ATen/ops/convolution_backward.h,sha256=DUX8_P89ixv2ljhKmRibXQB8TA9-NPDGUvUedhYJWm0,11487 +torch/include/ATen/ops/convolution_backward_compositeexplicitautograd_dispatch.h,sha256=-0UXHz1KzXh-H7Pe8e1f0vGLIKTqoqwQa-JcCu_xCVM,3314 +torch/include/ATen/ops/convolution_backward_cuda_dispatch.h,sha256=aLZEMnvCvqGljYhKD5L2LJGjzm4sCcISEKCoiOhz_1Y,1446 +torch/include/ATen/ops/convolution_backward_native.h,sha256=gos-5xwF3-TkzLUIoP1HeUewWKyJfIWEiU9gxgnOcUM,1267 +torch/include/ATen/ops/convolution_backward_ops.h,sha256=yfBfwNXZC-S6pcWC_S3s6mihRpboR-0iuKQRTXe8edE,3982 +torch/include/ATen/ops/convolution_backward_overrideable.h,sha256=5giokngTpvZr9eCQD1NZRC6z4JfmlxkJKd_TWMjNUAw,10684 +torch/include/ATen/ops/convolution_backward_overrideable_compositeexplicitautograd_dispatch.h,sha256=GWt8g0mT9y6Tq9Gusu0jhNFOAZ6WinapWWHLnYctkNg,3167 +torch/include/ATen/ops/convolution_backward_overrideable_native.h,sha256=gm7bPCJxpmjFrkawLZ8oRZarIiKZO2e3lRMg3A9UgA0,1218 +torch/include/ATen/ops/convolution_backward_overrideable_ops.h,sha256=B8fa8l3r1t5e73b-skkwD11FEES3iBuyClSUt9laIAE,3837 +torch/include/ATen/ops/convolution_compositeexplicitautograd_dispatch.h,sha256=j4QR_cFv04nU0_dRcb25DFx9mosqsvziPabeg-x1US8,2503 +torch/include/ATen/ops/convolution_native.h,sha256=y0t3Uovu6Kq5EAqW5ljWv6syGPQOkGtCb592-FJf9II,1011 +torch/include/ATen/ops/convolution_ops.h,sha256=jIC9FIXtyXsAAZoX-aK615ySpHyG-8cgPZvO1X8Qa-4,3099 +torch/include/ATen/ops/convolution_overrideable.h,sha256=JRVeZiE5krZEjrhSF26f9Pt1iqMMUJMnmRRQOh6h34M,8489 +torch/include/ATen/ops/convolution_overrideable_compositeexplicitautograd_dispatch.h,sha256=WgtJ3zynkTJwbkZWFwq2aDPHKQkjrbpbpVeTuqKI1as,2581 +torch/include/ATen/ops/convolution_overrideable_native.h,sha256=-y0TYZssFcUYRDiGlnON4Pia9S8bpWElSX03-J6rbCY,1037 +torch/include/ATen/ops/convolution_overrideable_ops.h,sha256=2NkiZ55OOOtQYq7sR4GJSRlPT1HKxW-3WQ7IvZNchVQ,3177 +torch/include/ATen/ops/copy.h,sha256=W5fBrZQ4FGJ4N08gHm1c_2AJY6roiCy1xPI-oQHly90,1316 +torch/include/ATen/ops/copy_compositeexplicitautograd_dispatch.h,sha256=VDFyaba0I6QR8Yg9StZHBY4CgstzvxVeyQPfsxTCdUE,1053 +torch/include/ATen/ops/copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=pRa4Ov8t7RUzBl7h3WOwkTPHPjcesg51lwjazDCtCAQ,837 +torch/include/ATen/ops/copy_meta_dispatch.h,sha256=wcPxFfAd6yECGRsuPQVHKDMHkYu-XmHgM5d5ZXCldUw,769 +torch/include/ATen/ops/copy_native.h,sha256=op04vVF19I8bbTglM39L28k23dbPj3G8SwRXYHwfCYk,1285 +torch/include/ATen/ops/copy_ops.h,sha256=gVd8Ekl5EfNQhxzIrEc_VgBfubz6PleT1VyYrU9t4D8,2475 +torch/include/ATen/ops/copy_sparse_to_sparse.h,sha256=PI0YnuR1i6eVOeltZiCqH666s1MEoZu9y3Qpwu0BxNo,1778 +torch/include/ATen/ops/copy_sparse_to_sparse_compositeexplicitautograd_dispatch.h,sha256=IEEjBlmvKAh1ItZgSsA0H0UPNjEQXT_VZAj6-YkPWPM,1107 +torch/include/ATen/ops/copy_sparse_to_sparse_meta_dispatch.h,sha256=vhi9mRGJHF2CPBPW9LjmsDnH08quPjkZa-8pPp5oxNk,783 +torch/include/ATen/ops/copy_sparse_to_sparse_native.h,sha256=f61lm-XxVM3R_JrF4N-UMUBiGwPyGgRPMHkZukQ2Tmw,781 +torch/include/ATen/ops/copy_sparse_to_sparse_ops.h,sha256=b7V8NDWZdIMsitTBB9LIWBOGA-io6h3jhyyWJAX789k,2628 +torch/include/ATen/ops/copysign.h,sha256=4nLSIGkTHTQwLM3npTlU7PvVm6o19ni6GgNe2_jbvt4,1928 +torch/include/ATen/ops/copysign_compositeexplicitautograd_dispatch.h,sha256=Y1mTBzgiIs3DB2TkEd3whbVatSUTnomrFtMPxAz3SR4,1084 +torch/include/ATen/ops/copysign_compositeexplicitautogradnonfunctional_dispatch.h,sha256=sO_Fu4Yucd8CXmcJJ-wm-t8WZwdjEG57WOYQ4FCITYo,897 +torch/include/ATen/ops/copysign_cpu_dispatch.h,sha256=h9zyuXG5gbYPpYL8XcLK-CrNXwcU0V0R_SOi1RIyl0c,1040 +torch/include/ATen/ops/copysign_cuda_dispatch.h,sha256=JdAHfnBrhDrHvXiwfJdx39rBw9qdTl3owMh6E7S5Lis,1042 +torch/include/ATen/ops/copysign_meta.h,sha256=bpYTlvJkBN_AlgPd0PutFfS_WLB9NWrT2YmP9ccH89I,602 +torch/include/ATen/ops/copysign_meta_dispatch.h,sha256=nVtC1rX3wIQtjbiUUQfJqnK5w03tHevfth62SoItA34,1042 +torch/include/ATen/ops/copysign_native.h,sha256=Uc1JEIfpBb6MNHUDzEerB0LPOXfxl9unFp1U4JCu0Qk,902 +torch/include/ATen/ops/copysign_ops.h,sha256=tamFZnIpDOu-RJt8YMD5P3drJ87-qNz5MDc0b-26Ae8,4288 +torch/include/ATen/ops/corrcoef.h,sha256=ib2m3TwDVAwRGLBeC9CNfbzAtB9JnR8khxD6yTB4Gj4,645 +torch/include/ATen/ops/corrcoef_compositeimplicitautograd_dispatch.h,sha256=Qgr8zzSJSOy98Wk5pDDY4duS-gwWzpWCRiyuGLgoISc,766 +torch/include/ATen/ops/corrcoef_native.h,sha256=99zeeRCwtM9i13UNFYqmE-DJp63ZwlxkiUzpl9vYGAw,478 +torch/include/ATen/ops/corrcoef_ops.h,sha256=OrVzSwveA88ifF8NajQmxOVCmQ4v1pDqiPoPqcBdCfc,963 +torch/include/ATen/ops/cos.h,sha256=kdO12_IZCrWyl7NiqP9bQHy0HOxsjLuRHtlPC6eU2O8,1130 +torch/include/ATen/ops/cos_compositeexplicitautogradnonfunctional_dispatch.h,sha256=XKVvuZOuv_bPHRX7C4cwM-cfWPODj0pSOvZ1ZGFxhJE,835 +torch/include/ATen/ops/cos_cpu_dispatch.h,sha256=gw277qd8XP-Y724zsxGLvWZHBCHldZxJXMs7ukg16MQ,916 +torch/include/ATen/ops/cos_cuda_dispatch.h,sha256=bqDfjG5_6nHr06NGx5DEwadJSmaiB-qJzokbef0p9Yo,918 +torch/include/ATen/ops/cos_meta.h,sha256=NxjTTYre8lMTAegHCiiJX2ruZdpi3nm53N43MjJzOlc,564 +torch/include/ATen/ops/cos_meta_dispatch.h,sha256=tSRNtVElJrkcfn1ByDc_ochQ6ZAk7DrI-3ZrEloalgQ,918 +torch/include/ATen/ops/cos_native.h,sha256=IR768c-nuz2GkhrxHCXLHcOI4eIeocbsfdoM4eNJe7k,651 +torch/include/ATen/ops/cos_ops.h,sha256=MEQ0YTmFPEJVv7i9W4OAilffCXOfIaFqE2_KC53_mTg,2019 +torch/include/ATen/ops/cosh.h,sha256=kudGy4ZFm3i1YaPOFS28b3Cdk6BvUkWmRbKkDbh8cAU,1143 +torch/include/ATen/ops/cosh_compositeexplicitautogradnonfunctional_dispatch.h,sha256=2Uur5cBIzb7T_-M4SQ1xA2Us7H7rhSDakvpYrF1eHSY,837 +torch/include/ATen/ops/cosh_cpu_dispatch.h,sha256=I5W4NGGCfpx2W1Lf3jjJn_Hq8tCI0OCD0hzhbvC5ku8,920 +torch/include/ATen/ops/cosh_cuda_dispatch.h,sha256=-nja_h6zd3bKq8ed_lFUz5aAqNhPBtkz5HYO39zYNqs,922 +torch/include/ATen/ops/cosh_meta.h,sha256=2MZerY5EAzH5ddN7et1eeB5T3fyOSKK2PjKdRZDTQiM,565 +torch/include/ATen/ops/cosh_meta_dispatch.h,sha256=yQDVX0UfehAZOYAEBrIJgDgC-5HjlM96eSEkK8dgbdw,922 +torch/include/ATen/ops/cosh_native.h,sha256=Xm3I6i46RhWd0mOaJtIiCo7xz-daFAR1L3fgAV76ltw,590 +torch/include/ATen/ops/cosh_ops.h,sha256=Q6OdvsUMtCaK2O8ahQQFaOJ-667DeZjuPIPsV-Ay-3M,2028 +torch/include/ATen/ops/cosine_embedding_loss.h,sha256=QcspFmHkI1OfRtANWjrfvcJaoGXQkwUL6OcqEo_GXa0,918 +torch/include/ATen/ops/cosine_embedding_loss_compositeimplicitautograd_dispatch.h,sha256=WHFcFub8Z_kx9KhnXYL3N8SQtZxtyd3ixksFaRlO6u8,893 +torch/include/ATen/ops/cosine_embedding_loss_native.h,sha256=epzsyIrMIXXrn6oMwPR_S7oL0jdv2PzxiSefaNcvq2o,605 +torch/include/ATen/ops/cosine_embedding_loss_ops.h,sha256=q8jAC_T70jZI7Pe6clxBOm5iEGq_B7THuYDDCB1w-KQ,1309 +torch/include/ATen/ops/cosine_similarity.h,sha256=Y6K4M3VQE1_07BphHOQ_sqg_boI8sCSZcsmmAmmshl8,784 +torch/include/ATen/ops/cosine_similarity_compositeimplicitautograd_dispatch.h,sha256=g6y8I6nkWoEnPKivCrGWtqMLMQxIBsQtfXSHOu26imk,829 +torch/include/ATen/ops/cosine_similarity_native.h,sha256=wxIjGGjFzGyVyc6koDzwJHWakfz2yHaQCKLvYY29YMk,541 +torch/include/ATen/ops/cosine_similarity_ops.h,sha256=9CSt6yIu8LDkrXwK0nMRVLuAELSTXqZTSGjOjFCFniU,1156 +torch/include/ATen/ops/count_nonzero.h,sha256=CuiWM2_qVorRFuuFIvEUo9zxFj3gglVWKxdXJPzm5f4,2048 +torch/include/ATen/ops/count_nonzero_compositeexplicitautograd_dispatch.h,sha256=_wbD-YLA17DmE_Y4Um2bKk9dtpIyzwDz9L39NDq-IMU,1275 +torch/include/ATen/ops/count_nonzero_cpu_dispatch.h,sha256=FBrVfXgU6x3nFFrROZJKjhe_gxOe32MuX6zhEQIwfes,748 +torch/include/ATen/ops/count_nonzero_cuda_dispatch.h,sha256=EE3oYmEhT5abweGJHngTdMQKNV7_XqWr0zoHg4NUWiM,750 +torch/include/ATen/ops/count_nonzero_native.h,sha256=nXzQ8Ozb-9mbhqbIkHLBeDgJdyVrKvbTUpQOp2sZhlg,934 +torch/include/ATen/ops/count_nonzero_ops.h,sha256=lHLnb7hAIr3MsVwVfv2Bs6O0RcMaKZoEWZQRudkvBog,3134 +torch/include/ATen/ops/cov.h,sha256=vafDd5qjyoYNC9gfncUP38JWehocCo7Vb2pHbuWRQcY,844 +torch/include/ATen/ops/cov_compositeimplicitautograd_dispatch.h,sha256=zXci1815i0BIB8gO88UAdc7m8BGSu1blVh_aI3eC9SM,881 +torch/include/ATen/ops/cov_native.h,sha256=Mj7f2hADsF5VSTiFatzy51ksvDHVWc0S_HuVAPi-Y40,593 +torch/include/ATen/ops/cov_ops.h,sha256=ZhtIh7PYgpfNr0Ru30Sb_yo0s8GYcwd_m8xZg36kcoM,1322 +torch/include/ATen/ops/cross.h,sha256=6C0V7Bsu51KuipXXF9zTZTjZx9TWiBi5gZLV-2krNoc,1338 +torch/include/ATen/ops/cross_compositeimplicitautograd_dispatch.h,sha256=wmpGjZD1KV-SUJjjYYLq2qkw_uYmurwgUQg4un634q0,1116 +torch/include/ATen/ops/cross_entropy_loss.h,sha256=DK9R4e4GINSTloQi4bQJ07V3eq77OiMR3pCfkFleWVI,2433 +torch/include/ATen/ops/cross_entropy_loss_compositeimplicitautograd_dispatch.h,sha256=Rsj1YTXo_u5IjIehWzMyCxUDPY8M89TkdKAnDvHeNg0,1189 +torch/include/ATen/ops/cross_entropy_loss_native.h,sha256=XD4ujviQce1-ErTK-uf6M6luDhD9NVu5vbhfU6ETPj4,667 +torch/include/ATen/ops/cross_entropy_loss_ops.h,sha256=DqmNmPeDypnHhLcNlp6Ll_jivuymMuwgTbu9utd2rcs,1469 +torch/include/ATen/ops/cross_native.h,sha256=SlP6IBfRC8USx5MOg_HaXvhVaqiiE7vOtu9K0tEX0sc,679 +torch/include/ATen/ops/cross_ops.h,sha256=Z4X0hMiPiGIueIs7c9z6NwdFnDr-U4DlBEAySn8H3Qk,1915 +torch/include/ATen/ops/crow_indices.h,sha256=OZ0FtK544pY11oic9zUdhMIRmRxJr_PsbXlt6mVqxGw,509 +torch/include/ATen/ops/crow_indices_compositeexplicitautograd_dispatch.h,sha256=_w78fpD5cPDBFa5l2dFfGTzKOBPSx1jMppVkqzJ-PyU,770 +torch/include/ATen/ops/crow_indices_copy.h,sha256=wUfEHFulkMNyg9Z3ssdKvB7u3kdzy6R1VmnIdfsqVXo,1137 +torch/include/ATen/ops/crow_indices_copy_compositeexplicitautograd_dispatch.h,sha256=LEDe0wumTfyPrFVFsmJz8FomL-_aWjXLgfmg3put_kk,889 +torch/include/ATen/ops/crow_indices_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=dor4GEDzsCoPlSlsDgkJFeuLcKD4k-i-dWyLIFgHUrw,801 +torch/include/ATen/ops/crow_indices_copy_native.h,sha256=5qEEVt8jqq4AXopJIj1aPUlcfCK_RdJssCnUKfnEOas,576 +torch/include/ATen/ops/crow_indices_copy_ops.h,sha256=ktNgOF8qXXRpdjl66GjPcscqlS5Ag1ebFCYgVbNiF4Y,1613 +torch/include/ATen/ops/crow_indices_native.h,sha256=EeoDXu8TljPXikgU1HCo2G9oJfpOZfAth75vkhGT9Ug,561 +torch/include/ATen/ops/crow_indices_ops.h,sha256=KmgoJUqN_DElUOh7bHoSRYSjVM31Uz4D9c7kesqFTAk,981 +torch/include/ATen/ops/ctc_loss.h,sha256=-2whryWP6hNxGSu3ZbJv8uCYHhYnGRhRJM9RpRGOQ8w,1582 +torch/include/ATen/ops/ctc_loss_compositeimplicitautograd_dispatch.h,sha256=lodC6QtfIpGZWarBKx1EU-y_ZEKhUrjerqhrkNgiWUU,1184 +torch/include/ATen/ops/ctc_loss_native.h,sha256=a-uO2H83XRPc0e6dzF0MZjTjMjV70MbLMmy5vLxUsQk,896 +torch/include/ATen/ops/ctc_loss_ops.h,sha256=B7WWDcGm8hLuU8tDZF90sBGKWYm3FoDTtQsuy1iMfsc,2534 +torch/include/ATen/ops/cudnn_affine_grid_generator.h,sha256=G_e0p5GpoC9vP3rzIwWF_ZJ619cGZTsQR_HugtYDY_8,1503 +torch/include/ATen/ops/cudnn_affine_grid_generator_backward.h,sha256=nyes2OqY0Rd_zvZdEVRJyWWlwN-1vr5TIalqab5DCBM,1590 +torch/include/ATen/ops/cudnn_affine_grid_generator_backward_compositeexplicitautograd_dispatch.h,sha256=omgVfGaYsaVWSRcW0IB3KYtsgv8tj7uY_3ok0Udwmuo,1015 +torch/include/ATen/ops/cudnn_affine_grid_generator_backward_cuda_dispatch.h,sha256=6c_zsSS4UaFFGCahgjtd-5WGJASaHHpFX-n89fHjLkU,796 +torch/include/ATen/ops/cudnn_affine_grid_generator_backward_native.h,sha256=IE6FyUT7lz4Ce7VO-p0jk3GLSBUCiPuzA86rkCRASP8,702 +torch/include/ATen/ops/cudnn_affine_grid_generator_backward_ops.h,sha256=IBrUK8je6H0ZOe3qakX4lIOVDW-jNgIOyebv66nZpGM,2042 +torch/include/ATen/ops/cudnn_affine_grid_generator_compositeexplicitautograd_dispatch.h,sha256=rXr_id-lUlL0PZLDFAndWFduVVPk9zmcBHSbRU54_34,999 +torch/include/ATen/ops/cudnn_affine_grid_generator_cuda_dispatch.h,sha256=Mg-Zbx9D4KO_RHKckBZyuaB5ZnmL5k_ybw-VOv9BAho,788 +torch/include/ATen/ops/cudnn_affine_grid_generator_native.h,sha256=lzXW8rZ8dCzoYdyMgx6_mhBKTXYZm_O-jmLC6rnH7Wk,694 +torch/include/ATen/ops/cudnn_affine_grid_generator_ops.h,sha256=G4SCo_lAt1U67RHqS2avQw5H46E9lYRPkjqMoaBj84I,1988 +torch/include/ATen/ops/cudnn_batch_norm.h,sha256=NBRWouVOMh1s_GG5_Lf7BJp4jg2rDh-YuxoUES-ZL8Q,3023 +torch/include/ATen/ops/cudnn_batch_norm_backward.h,sha256=GW8L3936pM6oPeCcMOZ4wrrRyvyl7N38KLR1RtlZV6w,3215 +torch/include/ATen/ops/cudnn_batch_norm_backward_compositeexplicitautograd_dispatch.h,sha256=M9ynmo58XETZga4xOR3FXE3aBlQbNlrD0N5BplXZrLg,1665 +torch/include/ATen/ops/cudnn_batch_norm_backward_cuda_dispatch.h,sha256=1Jy1BZmfFNPIENyXlSWYNjb1alVoV9xRX4fkq92k0P4,1078 +torch/include/ATen/ops/cudnn_batch_norm_backward_native.h,sha256=7Npgj5F2WPrfflAuw0UZ8e9g9Etcx-0Ua6FizYdqteg,1309 +torch/include/ATen/ops/cudnn_batch_norm_backward_ops.h,sha256=TdWvF9ioUFlh-ebThQYoj2rhlhP9FmMzNe1VmZCDJvo,4014 +torch/include/ATen/ops/cudnn_batch_norm_compositeexplicitautograd_dispatch.h,sha256=2_8gBdeAkFD0894w4NoP1AIRYDwpK3_wi2yrvQDSKRc,1579 +torch/include/ATen/ops/cudnn_batch_norm_cuda_dispatch.h,sha256=oT_kKmlSZdQcoCznpDQu0A5Lmx3MFoRohRsYKl_yXRk,1014 +torch/include/ATen/ops/cudnn_batch_norm_native.h,sha256=RrfG2G2A4aGqe-2GteoUoA84VbPpKNRLs7B-4beyKUo,1202 +torch/include/ATen/ops/cudnn_batch_norm_ops.h,sha256=5I-V5xM83XvU739Sgp1a7QmHh1k2bDXFMAmJdnwB6wo,3701 +torch/include/ATen/ops/cudnn_convolution.h,sha256=aMi-cCCQmyKUDBXLB_yYwB74JbelrWq3c-UUqxUrLHk,7636 +torch/include/ATen/ops/cudnn_convolution_add_relu.h,sha256=Z0Z9dRSuGCEUYpebxMSWGik1d-5KrW429Jzs3GfQ37U,8215 +torch/include/ATen/ops/cudnn_convolution_add_relu_compositeexplicitautograd_dispatch.h,sha256=FiQhO8AgxCUruvfWvcbtSS-K0kOz2A1B-HDsWSnGHXk,2050 +torch/include/ATen/ops/cudnn_convolution_add_relu_cuda_dispatch.h,sha256=kqV74BIl8sUnk19R74cOWVQe8u-cDaMJW1dzEKEsZZo,1289 +torch/include/ATen/ops/cudnn_convolution_add_relu_native.h,sha256=kZGEh6lbA-WNptIbx5dp-HJOqGMs5mrUq2xfElw-haE,1067 +torch/include/ATen/ops/cudnn_convolution_add_relu_ops.h,sha256=321vqLzTJQxrEHYjuC4sNv9LIa5gj7tPnZSOwdiDrJg,3257 +torch/include/ATen/ops/cudnn_convolution_cuda_dispatch.h,sha256=YaFY9gjKM4k1dNQOLAgTlELXPHQnZOONzBjMOnWfwrs,2251 +torch/include/ATen/ops/cudnn_convolution_native.h,sha256=AxjtJMj0uaUDs9oiX7XQwmN5H5S2BUV8lIae8HLy5H8,918 +torch/include/ATen/ops/cudnn_convolution_ops.h,sha256=NXog7kdMoLP97wn8UzRndFiBd_ALZ5erRvvQCIVS39g,2863 +torch/include/ATen/ops/cudnn_convolution_relu.h,sha256=e3EAEawNiWRJ7nqbIY7F9iGFy1hhCiBoQ0leO47Zffw,7041 +torch/include/ATen/ops/cudnn_convolution_relu_compositeexplicitautograd_dispatch.h,sha256=E8-Dw2wHlMtfb6v1T01xscasJkjpA2BLZ6PI2V2b5Ws,1774 +torch/include/ATen/ops/cudnn_convolution_relu_cuda_dispatch.h,sha256=CzOW78g0C5EYt5F0m4ZNZT979enGWKyrErbhPx4Mu0A,1151 +torch/include/ATen/ops/cudnn_convolution_relu_native.h,sha256=8YO2RDAOce-3OxqgMraSfT9iCaen0JHmIDYqU_VeuhI,929 +torch/include/ATen/ops/cudnn_convolution_relu_ops.h,sha256=YJcsjaBjsRV6hbL6frXp_syvQLxk28gpTNw19RlOA8w,2809 +torch/include/ATen/ops/cudnn_convolution_transpose.h,sha256=bcitKIKYtXW5PflM2dK2zf_p4TrMUM1MKyf8tEUwjC0,8852 +torch/include/ATen/ops/cudnn_convolution_transpose_compositeexplicitautograd_dispatch.h,sha256=Ak52MBqjj8TkxX5MtRq7zLDR_o3c0_SdQJXU1VDvbTc,1974 +torch/include/ATen/ops/cudnn_convolution_transpose_cuda_dispatch.h,sha256=xmvTx8EkFeA4j-xcn-a1SHY6LZr0XCE4xRAPxxn59T8,1251 +torch/include/ATen/ops/cudnn_convolution_transpose_native.h,sha256=kPQrDnV0hsSLUwqbBHdC96NYjpXeBRSksxVa_sfR9GI,1029 +torch/include/ATen/ops/cudnn_convolution_transpose_ops.h,sha256=B-qkcaIdShBLBOA8DG-Vmc_mKu8RtdpJMqEfEkws_As,3159 +torch/include/ATen/ops/cudnn_grid_sampler.h,sha256=zRSCHjCuLNUGJEhwKRUjARvJk-Kn-YI9jc7vcsjt53E,1286 +torch/include/ATen/ops/cudnn_grid_sampler_backward.h,sha256=Bfwqe2245ISAiCFyGCvtc6xvesPtujvBrJYGpohW4qM,1791 +torch/include/ATen/ops/cudnn_grid_sampler_backward_compositeexplicitautograd_dispatch.h,sha256=blb1k5yEpgDhv-rEyyTpUiJ3PcvMuOwe2aF3lUvf7HE,1117 +torch/include/ATen/ops/cudnn_grid_sampler_backward_cuda_dispatch.h,sha256=6uvFt8Ug12xf33J9ANxaaRw67-qmJMwSXdSY69QXI50,825 +torch/include/ATen/ops/cudnn_grid_sampler_backward_native.h,sha256=hSy5i-E4TbXzfJpHPg1dJeQxPUVRT03xGucIqBmHXO8,782 +torch/include/ATen/ops/cudnn_grid_sampler_backward_ops.h,sha256=7lplVJtYFOgoSMcppxGJq4sMwnRKd7IMCLq8b3SKEsM,2319 +torch/include/ATen/ops/cudnn_grid_sampler_compositeexplicitautograd_dispatch.h,sha256=sX_Pp6YMhGvDf0YSUuEeayTbV_Aqvp9oC_gHZ-HnzKI,941 +torch/include/ATen/ops/cudnn_grid_sampler_cuda_dispatch.h,sha256=YwZPDzrrJAefCUs8Vg15yk7OdmObQI4xsKjJ2veupUI,759 +torch/include/ATen/ops/cudnn_grid_sampler_native.h,sha256=2BBgEo_wr3TOH7dhAFLcw6_vWqKTnealg987CdlIB9Q,636 +torch/include/ATen/ops/cudnn_grid_sampler_ops.h,sha256=VLCeM8ZLuyeLB5BpNB-GoqwBrZAbRZf5xj-YwNkGkVE,1792 +torch/include/ATen/ops/cudnn_is_acceptable.h,sha256=ahSKRc45wLd5UyoAjuh0LOueIo49SSrUBEtK-gsfst0,681 +torch/include/ATen/ops/cudnn_is_acceptable_compositeimplicitautograd_dispatch.h,sha256=E0ZgfkRQcvvGaP34ONxiQn1PtPZlFmH9nyYCz3-wGUo,771 +torch/include/ATen/ops/cudnn_is_acceptable_native.h,sha256=TX3GXpOxkrqKJ7cV-fzeGjdkwusjYyp6x1TAMetroaQ,483 +torch/include/ATen/ops/cudnn_is_acceptable_ops.h,sha256=09zNce4RVV4ZFD0ciSWABSQsdmn50BLHKAwv38pxq6Y,976 +torch/include/ATen/ops/cummax.h,sha256=FA5Xj9NrR5XF3GYoVLM0wmsm7nr6D1UaF12vNp7_86c,2350 +torch/include/ATen/ops/cummax_compositeexplicitautograd_dispatch.h,sha256=JAsbyGjv8yHd9qvsIub_amJCvIqUy7kHnWUMFa1qpDk,1089 +torch/include/ATen/ops/cummax_compositeimplicitautograd_dispatch.h,sha256=ysKrZo_zIiCtP2NM7FWbx3t2jjXesY3qvwVU6d4PY8k,1101 +torch/include/ATen/ops/cummax_native.h,sha256=Ce4hkVZlCp-tENKtyiK5NPtmBfl_Hs7kzOxUj5rIWD0,900 +torch/include/ATen/ops/cummax_ops.h,sha256=1GgA5YEG610Liae0h-dv4hYfsrQzR8urlUxlIX7452A,3486 +torch/include/ATen/ops/cummaxmin_backward.h,sha256=nTnnlvM6E02USrHI5F3Hg71lHOok5FrvScjrM1dYTlo,812 +torch/include/ATen/ops/cummaxmin_backward_compositeimplicitautograd_dispatch.h,sha256=zgUxDvn3JeRV1T8HH7wlJ_SOmfWDblm-aRCdDuRLD6A,843 +torch/include/ATen/ops/cummaxmin_backward_native.h,sha256=fLfwo45uwaw7aYOrSZPKdDk3U8ILCYWHhMlAWBi05x4,555 +torch/include/ATen/ops/cummaxmin_backward_ops.h,sha256=u3mLBNF68DMY8_vYYo-xmzfAVnokipJsqWH2opSQgcg,1215 +torch/include/ATen/ops/cummin.h,sha256=dMz9di5ibj3nYOQoPrOCdkIXBinJP_peTgf4l3-2fp4,2350 +torch/include/ATen/ops/cummin_compositeexplicitautograd_dispatch.h,sha256=kc3U0-PI7dbqXr1f0IXzDXAdz29cGdqR4FhmKxkXw2o,1089 +torch/include/ATen/ops/cummin_compositeimplicitautograd_dispatch.h,sha256=-0jp9zEiv4a_fMS1YeyFUKwVVAAKkA_OE9GEkAn3IZI,1101 +torch/include/ATen/ops/cummin_native.h,sha256=4IKRwEzcBBWBCdiZE6N71isg_epEtgiGjNF0QPMV598,900 +torch/include/ATen/ops/cummin_ops.h,sha256=oXE8SAAweJhcqDmhxIXfoJv1-L42YOC_uxCEGrEJNiI,3486 +torch/include/ATen/ops/cumprod.h,sha256=RcrJ8-M8hkyAt1bzXQMVWhBw2C4v14MCQd0Y4TYSSfM,2291 +torch/include/ATen/ops/cumprod_backward.h,sha256=hDGA0y6dqV0Oc-a9AJvsuKfSAYJ5soSJaor9ceb1LjE,801 +torch/include/ATen/ops/cumprod_backward_compositeimplicitautograd_dispatch.h,sha256=wLxWUZU6Af4t8iHIxKTlR7jCShqfM-MmgAgxaE0ORHk,840 +torch/include/ATen/ops/cumprod_backward_native.h,sha256=AmPmjCOmUOLazXfhJ7OidBinIrw_CROiOwECzsD7eIY,552 +torch/include/ATen/ops/cumprod_backward_ops.h,sha256=UVW89PsuMGKcyogGJwwLlYIPe2fo2vIFkwnBHfV1xLo,1206 +torch/include/ATen/ops/cumprod_compositeexplicitautogradnonfunctional_dispatch.h,sha256=JzraimtePKAMn9p_roECtzfZ06XXYjNwmWYRA1P8V9Q,977 +torch/include/ATen/ops/cumprod_compositeimplicitautograd_dispatch.h,sha256=Q45oAhAVXUeNc0TkWM9eNYhJQZnqeta90DHtHLRQbLc,1245 +torch/include/ATen/ops/cumprod_cpu_dispatch.h,sha256=qsEnFPsganQjrj0gqSgBSxEq-_tTCm77MOcR0v9SnLU,1185 +torch/include/ATen/ops/cumprod_cuda_dispatch.h,sha256=lDi1Y0ARYav17J27Cbx3O0iRLpH5Am7AjTtMV6RNNgc,1187 +torch/include/ATen/ops/cumprod_meta.h,sha256=N5NA8mgQpqwt7Hpx3X6RvogpIjlnesURsIzFD1lYjMU,620 +torch/include/ATen/ops/cumprod_meta_dispatch.h,sha256=W2NL1dxsyJhZoAGx8gxmNcrxCqtlZ_APIsrx0K_HcCk,1187 +torch/include/ATen/ops/cumprod_native.h,sha256=75LSZMgafB3lbHFfeZigT9zW4gHl6UfpyI5Ns0NaJM8,1035 +torch/include/ATen/ops/cumprod_ops.h,sha256=EdKaCDHSGxf8JgxB7NQAPqMmZ2NhPI6cb-WYARrJGGc,4857 +torch/include/ATen/ops/cumsum.h,sha256=rY30BC-eD0nUKZDL9bweycdFtBtRcbulhnnxWL3bXHk,2272 +torch/include/ATen/ops/cumsum_compositeexplicitautogradnonfunctional_dispatch.h,sha256=6ZD3AdwkexbSfcUTOpSZF-1szE5KBdI104o6wLFfBiQ,975 +torch/include/ATen/ops/cumsum_compositeimplicitautograd_dispatch.h,sha256=BB7HXKkzMIlaaDNLruXnWqr-wArIFRu7zKI6JHbGVOU,1241 +torch/include/ATen/ops/cumsum_cpu_dispatch.h,sha256=sa1pIZYNXLQdQlUPKCnDhGC2Cqj_3gOpzggjbQKiHXU,1181 +torch/include/ATen/ops/cumsum_cuda_dispatch.h,sha256=_3SDPDF93LuJxzFw6DV5h5f0Vk_9Jn5ZrjJNaeVHS7Y,1183 +torch/include/ATen/ops/cumsum_meta.h,sha256=IOIXwknl9qIxM6jaeXnaDn3Iff9wb1odf2MyBlnTgHg,619 +torch/include/ATen/ops/cumsum_meta_dispatch.h,sha256=mMMsQHHkboQ6nhR4J5ss-fWBdb_dEBrBOAissQXax-Q,1183 +torch/include/ATen/ops/cumsum_native.h,sha256=-zA3A2WOFiJp7w6oLGpANiTkh8-Wjk0nkGxcBID4Asc,1029 +torch/include/ATen/ops/cumsum_ops.h,sha256=v33104P6FTbZOIC6Hc3pDXMW7NiplGemZZs8S6SCBEU,4839 +torch/include/ATen/ops/cumulative_trapezoid.h,sha256=mGUsxfNvcKUSUG2xUg7zSnz4ETF9x6Awvls05leqjEs,1011 +torch/include/ATen/ops/cumulative_trapezoid_compositeimplicitautograd_dispatch.h,sha256=8ZQtDNIBh_WBa_GRz5Y3eO_cP5jkEtEoH1wy8mknN5M,919 +torch/include/ATen/ops/cumulative_trapezoid_native.h,sha256=rST-OvAVHnYaZ4FksgdBZv2n2mn1j-Kypjn3sQaJl3I,631 +torch/include/ATen/ops/cumulative_trapezoid_ops.h,sha256=MeiDATDy2f9R1CmwmvMCoegKIxlD8PmOHDGMHKYzGPQ,1789 +torch/include/ATen/ops/data.h,sha256=gdVY3J_svAexaFOOFk4bCWG3i0dBxcJePTw1QJKCWtM,501 +torch/include/ATen/ops/data_compositeimplicitautograd_dispatch.h,sha256=7rT5Ey_ek1MWNWGDWMK7RTglC-A8vrIUHUoogFmPrO8,762 +torch/include/ATen/ops/data_native.h,sha256=rtO_fpeo1PjUbz2pXX2DnNq5L3BZt0CGWODVhUZyc8c,474 +torch/include/ATen/ops/data_ops.h,sha256=v-7enEcAvEL63317vd-gRCtFT-DAG27SU75lqbOfWrQ,951 +torch/include/ATen/ops/deg2rad.h,sha256=iwbLN3iM4PN5AaYT-4_CDgXG7buKe-Zrv4jiLiYFJUw,1182 +torch/include/ATen/ops/deg2rad_compositeexplicitautograd_dispatch.h,sha256=DLSR889V3eHxDTyTDgFYjBFbYzyQ4e_g78K8hW3VHqU,976 +torch/include/ATen/ops/deg2rad_native.h,sha256=vDQZNL5ozGGnjkBH1C5HyCBej0e_bQLOzuYKdKW_2Xo,1034 +torch/include/ATen/ops/deg2rad_ops.h,sha256=XLFwFA19Pj6vFeXvcxasd8T7JPYURE5JQrx4GF8qXmE,2055 +torch/include/ATen/ops/dense_dim.h,sha256=4fZrqFWyUQnvK9dllTJXzoRcAepLwznkyh2O9GX0Yg0,506 +torch/include/ATen/ops/dense_dim_compositeexplicitautograd_dispatch.h,sha256=Ui5ifUlQs-P3-k8zdjpecNs6nDTcm1yfXzdgc7flXXw,764 +torch/include/ATen/ops/dense_dim_native.h,sha256=WBphoXk9ym-SN9cazdbbL5HdKHokr2MKrClKgKiewkU,610 +torch/include/ATen/ops/dense_dim_ops.h,sha256=g0xD09TD4ezdLiF2F6mdqotONDN6fBDfAgokp1Mnu0Q,954 +torch/include/ATen/ops/dequantize.h,sha256=zbIKhGhg7j4cLvPiJuOZ31XpGbY9pUoeSM29KJd38GI,1720 +torch/include/ATen/ops/dequantize_compositeexplicitautograd_dispatch.h,sha256=G79b9-0_JeOtygHHMAr06A1m2Npq-6vwZf8wU4ddJl0,1026 +torch/include/ATen/ops/dequantize_cpu_dispatch.h,sha256=PgMCnHQK2lewGWM_pKyXztqTdjHq1sphVO5wR-wODHE,724 +torch/include/ATen/ops/dequantize_cuda_dispatch.h,sha256=sbUXS-BxQPCsWFII-0-Yj7zMWV1u_aMl5gIEhBtWCPY,726 +torch/include/ATen/ops/dequantize_native.h,sha256=hQKoXBJQHjfowCXsCXLJJAE60cwZ_WYGCcHtFqjl-pU,824 +torch/include/ATen/ops/dequantize_ops.h,sha256=sBIWG3gh1_bDg7Dm9hyhhI0I6YQJ6E33blTkrs2xjVw,2782 +torch/include/ATen/ops/det.h,sha256=2deLLLc2oY-etGAaIO0evkvCa6HjifbfXLdgI-QbE-w,625 +torch/include/ATen/ops/det_compositeimplicitautograd_dispatch.h,sha256=vMhr6dD8nYriKxlO7LAas9mbHdj_bRMEbwKfkByWGcg,761 +torch/include/ATen/ops/det_native.h,sha256=16WNOQzEA6WtqclXBThD26JmI8TXWv2NWpyB_gcwoN0,473 +torch/include/ATen/ops/det_ops.h,sha256=EPlGzzh9WY8HxgVDzFCwMzqlWrkDLuMoaOXOUGoDahA,948 +torch/include/ATen/ops/detach.h,sha256=Sl2jYPbyGUM52LoMg6G-MYl7E503za0uZi70eBPPuGg,785 +torch/include/ATen/ops/detach_compositeexplicitautograd_dispatch.h,sha256=crcZzI_9nwLGaJvHqbeMlfQpDK3rghIb7ftfD7-K2Yg,815 +torch/include/ATen/ops/detach_copy.h,sha256=U3vRFomDkVMdOt-RS4OUrtr0wTaFkT9jZmuk8DbzWLo,1077 +torch/include/ATen/ops/detach_copy_compositeexplicitautograd_dispatch.h,sha256=xqbDYRrKQ3HENY3MW9a_H2FO4r_MZEU-BPYRjjTJxKE,877 +torch/include/ATen/ops/detach_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=vW0hj53gbojTYp7awhEvCMgH3ATWwxH5LDf6gHofF-M,795 +torch/include/ATen/ops/detach_copy_native.h,sha256=z2_5KtGIRJZTF48JzDDdRrG3m-Hw7nEN2h5N2RZdaZQ,564 +torch/include/ATen/ops/detach_copy_ops.h,sha256=rD8P3tPLd7qhScfv3Y1lQ-0a3GNYZPzImczkDtbrrus,1577 +torch/include/ATen/ops/detach_native.h,sha256=MKpf1vx8bCOWGfXInBFSHRO-7iRTGHYGUKbChNuHh3s,527 +torch/include/ATen/ops/detach_ops.h,sha256=4Vy_SZrxkrVwc-z7fVo0Ia8i1h9KKSXPAqry7YE6Lco,1462 +torch/include/ATen/ops/diag.h,sha256=ew0rGqWW2l9Y0vr7SL3tJX3oKcuPTDpEdpC2iz9JQ_Y,1143 +torch/include/ATen/ops/diag_compositeimplicitautograd_dispatch.h,sha256=edYpNbx-uPwMurX3pmgjbRTGlyh-cXPUVb4hup1v0KU,973 +torch/include/ATen/ops/diag_embed.h,sha256=AKUYNTaNGIfFI3OQAWK-2Igx2Ssx8Uj5b_waKDcNb1Y,1395 +torch/include/ATen/ops/diag_embed_compositeexplicitautograd_dispatch.h,sha256=gg8ug0EHOEm6fljI_cZ4Uoc9S-U2fToKJT2DCO-DFr0,971 +torch/include/ATen/ops/diag_embed_compositeexplicitautogradnonfunctional_dispatch.h,sha256=BqH6JU_YRm-CUQFRUTvWhpZ3mYiXTmYXqqaZKlpTebs,846 +torch/include/ATen/ops/diag_embed_native.h,sha256=hmaRtMhqJIi3QtG5luDB2fdw8m52vjNbN_Ag5webUu0,658 +torch/include/ATen/ops/diag_embed_ops.h,sha256=2dnJXFCqj-D5N3kMM1M_5Y-R2TDpcVKSgB5wHNjWWmE,1881 +torch/include/ATen/ops/diag_native.h,sha256=EG-qHeRdzxbNlMztMGMWrucza0iTb79L-dSf8G3e_Tk,588 +torch/include/ATen/ops/diag_ops.h,sha256=KJiyf0PyfgXAuxRmjfBzCPKUFRFMk6qeueItHTlDiUE,1657 +torch/include/ATen/ops/diagflat.h,sha256=l3-_BhSeyvafDpciu9FqAPvjYWkIcq7GBqy5VhzbRVo,685 +torch/include/ATen/ops/diagflat_compositeimplicitautograd_dispatch.h,sha256=iAv9nwwoz86t3SP_zl3mQCCfQAF_QsHYZ_vBg8lLqh8,784 +torch/include/ATen/ops/diagflat_native.h,sha256=e-4lYP4GFNVEi-jBwDwQV0RcAXC1bSSyxg8BlMUugDw,496 +torch/include/ATen/ops/diagflat_ops.h,sha256=VkrsAoe15M4U9gEBdoNye2oXcK5zEHNWjE9ksAuF32g,1018 +torch/include/ATen/ops/diagonal.h,sha256=lLT_fzwbt_gHVX22H8jdMYC7tyedaMguxFEj4fzO_NQ,1085 +torch/include/ATen/ops/diagonal_backward.h,sha256=2jZ0AY2LgNKrB46BO1wMs_1R7a1h5Wt1HryvvfQVsxg,5212 +torch/include/ATen/ops/diagonal_backward_compositeexplicitautograd_dispatch.h,sha256=AOBdU5Kt4gWehW06MCyD-vmUcCLIvk6MhVu9952CYQw,1711 +torch/include/ATen/ops/diagonal_backward_native.h,sha256=_TRHJV69sKVT_l0IS56lL3ctA68rNWwLKunjNzBag9s,758 +torch/include/ATen/ops/diagonal_backward_ops.h,sha256=vvg1FfKxbDXnF9KpqF8wjpQWfhxTrHiExabPfqev-v4,2167 +torch/include/ATen/ops/diagonal_compositeexplicitautograd_dispatch.h,sha256=YNCL9amA_G1wo5NJs2cWY13NlZUJhLaoM6LR-9sjqJ0,816 +torch/include/ATen/ops/diagonal_compositeimplicitautograd_dispatch.h,sha256=eFMJSnz1dq1oAiLRHonak-2wtUy0wI6EqqvdkPVofBw,840 +torch/include/ATen/ops/diagonal_copy.h,sha256=TQAjZGR40OZmWMMWMJ7rLbW0TjyrIXzmjTqfNcjzuIs,1415 +torch/include/ATen/ops/diagonal_copy_compositeexplicitautograd_dispatch.h,sha256=FqVEXXDUGjaBarEzXdKgH6vPnanN89fGkBUiL3JWP3U,975 +torch/include/ATen/ops/diagonal_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=1eCt651vj8kU98-XvhO2c7oWCIupIeanTt5z2gUjEzc,847 +torch/include/ATen/ops/diagonal_copy_native.h,sha256=MTJGn6nsU1UNbrIXQoerj22S8aHLlAB9UHQubCRotr0,662 +torch/include/ATen/ops/diagonal_copy_ops.h,sha256=TTwuHm0MimeG-UdD815_ST_uXuWc2przLJzao5pVmdw,1895 +torch/include/ATen/ops/diagonal_native.h,sha256=m2xvoKGDzeO2MLL5CpJCZms9RLlT_wxjN-bU-BTpqss,658 +torch/include/ATen/ops/diagonal_ops.h,sha256=J3x1X-3IVZzoQRraUdN8YXSJWliwqj-17Ehz5z2q3GY,1910 +torch/include/ATen/ops/diagonal_scatter.h,sha256=W3W-whzalHXlDzXbBcBUFUcZ-OIL3GCGIy46We6w04Y,1568 +torch/include/ATen/ops/diagonal_scatter_compositeexplicitautograd_dispatch.h,sha256=St221yGHbtk1c16W8KqHEsb1Gp78kre72V65iTIC6Uc,1029 +torch/include/ATen/ops/diagonal_scatter_compositeexplicitautogradnonfunctional_dispatch.h,sha256=gQ6Hz-GumZ1598dqaSGzKKSS7l1kd3AcB3m4kIsIa8w,874 +torch/include/ATen/ops/diagonal_scatter_native.h,sha256=9KUzFDZ3Vf8ZlCperM0qNWhgM6Bn5hI2p-EmOGema1E,716 +torch/include/ATen/ops/diagonal_scatter_ops.h,sha256=K0CRllaQhvmDQ4NVMnSKgLCfy88p6XoBJZn-ZsBjvyA,2073 +torch/include/ATen/ops/diff.h,sha256=letjANIb2rcsV51vLI0WiC_LU0z5QjNYz4Ys0v2Kqpo,1635 +torch/include/ATen/ops/diff_compositeimplicitautograd_dispatch.h,sha256=IZ7Pp_125yA_6VmKZzwnN1dc2LJBBvoY5pO3BoxCZQc,1276 +torch/include/ATen/ops/diff_native.h,sha256=z6uBf_EuL4iSXo61OfKJi-11JKZ3udlcU8FUX2_FtFM,787 +torch/include/ATen/ops/diff_ops.h,sha256=QGDAHh4sGXN2vkZq8cjBcdaHqZlV9qVxLdzGp_5vols,2299 +torch/include/ATen/ops/digamma.h,sha256=AWnzWmV9WZKeZHC5r5-3isbL81Ec3AQTuGKPgD6XW1Y,1037 +torch/include/ATen/ops/digamma_compositeexplicitautogradnonfunctional_dispatch.h,sha256=9X1iwB9nsSRxsdntZOMRgNxmGsS45_ZQhKj_pP0pwAU,843 +torch/include/ATen/ops/digamma_cpu_dispatch.h,sha256=MzP1whO9KAJd-sPTA-0DmQFm4nc7wudhV4fErigm4RQ,932 +torch/include/ATen/ops/digamma_cuda_dispatch.h,sha256=jR7Kxf3Kutt18KxH-jso7KLOqnEnbxR_Bq6rrPpplc8,934 +torch/include/ATen/ops/digamma_meta.h,sha256=tuLCaFUELahlnlsqwnv5siVKY8fMUhsM0zbthvdUTUQ,568 +torch/include/ATen/ops/digamma_meta_dispatch.h,sha256=MuEijZQNPnfdw2EgfID4yZbv9rKtm1BwMxsKMYIHJ9Q,934 +torch/include/ATen/ops/digamma_native.h,sha256=hX6_mJIrHT8s7YUi8FOqEjwlJWStRyNzFl8FqKeu8Zs,599 +torch/include/ATen/ops/digamma_ops.h,sha256=V-aOjksCeORwx1G8EqJU_Gs-CuVwbAgFHQNhtOe0kGY,2055 +torch/include/ATen/ops/dist.h,sha256=ZfilQR4GbGd6WXHJjTr6HqrHLjVKM6SbX1m6kpngeGY,1263 +torch/include/ATen/ops/dist_compositeexplicitautograd_dispatch.h,sha256=pc6-HxVURdAmbQaoZuiKy0zBIcwKICyCIK7PEulFXsI,1063 +torch/include/ATen/ops/dist_native.h,sha256=HLchRNZYeC9K47Rmtn_FNTJPRtQObtGYRmeDFyjL7VE,648 +torch/include/ATen/ops/dist_ops.h,sha256=wfLQc4ek-VFco7gFN5NCbhVcvnRLj_rcHTdQVIEGS28,1859 +torch/include/ATen/ops/div.h,sha256=3dDQVVYHDzLwc77nTulZJiScAORrxHS9qo0WZVL0cnI,3737 +torch/include/ATen/ops/div_compositeexplicitautograd_dispatch.h,sha256=jGY81xqohLjATZfHV-8xA5iJtzG7DtjtDtG8DwZbufw,1614 +torch/include/ATen/ops/div_compositeexplicitautogradnonfunctional_dispatch.h,sha256=R4g2Ygc6XiMBmfvmt8TU1GRuv0QX5xrkQQH62cEu8YU,1136 +torch/include/ATen/ops/div_cpu_dispatch.h,sha256=Z0FGxxyAYxfZ8rU2h2EfSYne-88OdmnoluIgub7pgVw,1570 +torch/include/ATen/ops/div_cuda_dispatch.h,sha256=brfTgPlIsfAJhuEy-eB7UNCk_VL_vyJm6ffBs0w1AD0,1572 +torch/include/ATen/ops/div_meta.h,sha256=x7exyrV-UU64xSLEke0RZjsntHCZ-0FotNT3qCAdRZU,791 +torch/include/ATen/ops/div_meta_dispatch.h,sha256=oz5UE3e8SY829iYZzwL_Ysba-HApIX66AJ_zINMP2kM,1572 +torch/include/ATen/ops/div_native.h,sha256=HiJp7_Fk3D9m7KcrSIWlqr0Ez6F9Rc-2UI6BUk7hdZY,2508 +torch/include/ATen/ops/div_ops.h,sha256=bOHqJS0lRuL6Y5djrbMoxoDHsv6oPdxYJCBMm1TStU0,8959 +torch/include/ATen/ops/divide.h,sha256=FTPIIoe4VZ6ggHrz0-JlmKLKr01AZan6rq_xmF9bH74,2636 +torch/include/ATen/ops/divide_compositeimplicitautograd_dispatch.h,sha256=faKU5tLCupTnFlS3ykrAU4EeBOGbEy6wqfaj5uEQQzM,2050 +torch/include/ATen/ops/divide_native.h,sha256=f_GhN_TAnbZL6Uk4SzbqhLi2uapMCNvoWgJAy7qqcl4,1503 +torch/include/ATen/ops/divide_ops.h,sha256=4igD2Oj6PXLfSrx2KxmxtO-8lJWJ6UrkvS-PYb2B_u8,7505 +torch/include/ATen/ops/dot.h,sha256=6E8EIeurzlfJTgc848ANPNciBq3PtWYrKJ7uMoMv3eI,1147 +torch/include/ATen/ops/dot_compositeexplicitautograd_dispatch.h,sha256=usIcLuparc2NVrF5TntbpbjqgiYJq-dhnbfKBM-ixjs,915 +torch/include/ATen/ops/dot_cpu_dispatch.h,sha256=ywDI3f52zvW9ZKo6diE_gnWH81jZEoKLcdRwFswIp5w,744 +torch/include/ATen/ops/dot_cuda_dispatch.h,sha256=A86RTtAXKBpGuszl0vJJJXB-rvKc5BIKkMrFAUY8Wuk,746 +torch/include/ATen/ops/dot_native.h,sha256=5JC5I65GS6hllzwZxoG5IlAQu72PfHtmct8YJypvFV0,685 +torch/include/ATen/ops/dot_ops.h,sha256=-Bg9B5mqexnJC9HWFVDO5GRtaseE6FE2aQK6JC9zpxc,1707 +torch/include/ATen/ops/dropout.h,sha256=Lm9gMt1QxX7byfUMSlrKH3amsxW38cjokbkZ5_y_g6Q,895 +torch/include/ATen/ops/dropout_compositeimplicitautograd_dispatch.h,sha256=1oNmNCeNCWLHWL9vxzq3kL67NmtOUC8Ir9mLr81pUbE,862 +torch/include/ATen/ops/dropout_native.h,sha256=v6aHxY_JDeUwSkNAjH3z5xATlslRALfayObPGlgtr3A,574 +torch/include/ATen/ops/dropout_ops.h,sha256=6T6aJRkEs-Btb5Wwbdhm_ahgrpCB-S3slGzT8lF70Ok,1623 +torch/include/ATen/ops/dsplit.h,sha256=GLQgVsTDdAHgSZ8SZZ4cOAc3azdO5kd1Vpr0Blh2_Tg,939 +torch/include/ATen/ops/dsplit_compositeimplicitautograd_dispatch.h,sha256=Lc_xGCKzhxHNU6F2ztKMmp3eKEXyWbtDoBFsS4gUTb4,891 +torch/include/ATen/ops/dsplit_native.h,sha256=pjcKkFeuAbFveqdh-9sYzOYfX7IXUInbYHQd8DBT2Pk,603 +torch/include/ATen/ops/dsplit_ops.h,sha256=KwQKfuE_keDBxnfvk7Q4zQG0Fh2mPhVEoVdybPvw9CQ,1742 +torch/include/ATen/ops/dstack.h,sha256=sXl7_GgGaQ2OsISYoXDkeZbUfvicf0QYpSkHRuLh78Y,1048 +torch/include/ATen/ops/dstack_compositeimplicitautograd_dispatch.h,sha256=-PJkYjYTP0wjfRHnQapUQfgNNbnBYTdnEAawd6IfuGo,918 +torch/include/ATen/ops/dstack_native.h,sha256=e-atQF59iMcOF9zVb4WQXnYYxoQK0THGt-Du3KcRvic,552 +torch/include/ATen/ops/dstack_ops.h,sha256=jR64wknUPojPwUw7cn7T8_92G-kyCDo7CtjQ8UcKu6U,1545 +torch/include/ATen/ops/einsum.h,sha256=vbx-chJnhR5bMYXnqUZ8TeBsk1kN4lTgDcNozV-iX68,767 +torch/include/ATen/ops/einsum_compositeimplicitautograd_dispatch.h,sha256=Zmt4msCBOKK5HKC5PQoZU3miKuRq6ocR3BKgmzS3cjI,835 +torch/include/ATen/ops/einsum_native.h,sha256=NkEj9gJDjwl-2nqxdtAwmB6gQv0eBy0MmeEwOj8pttU,547 +torch/include/ATen/ops/einsum_ops.h,sha256=MlRfa4y9SnfAPpRfQRKwl0HTrgaTQrbMDWO41Rwfyj0,1148 +torch/include/ATen/ops/elu.h,sha256=kFcBdtM37RFunl261kGryEEZ_jz1W8Jv6CfODSTaApU,1808 +torch/include/ATen/ops/elu_backward.h,sha256=bXLu9S8bW1o4ps0rOx9FPqVl37aI56BMB7t6vbbJt8o,2048 +torch/include/ATen/ops/elu_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=qlCwY_IF0NLw-2QiE0jNvaoCpd26zZVloEMivjNBIZM,938 +torch/include/ATen/ops/elu_backward_cpu_dispatch.h,sha256=6oHWY1Wd3wCF7Qx-uJWFii0PBI5jgqRvVJ0mjgiQwnQ,1335 +torch/include/ATen/ops/elu_backward_cuda_dispatch.h,sha256=fdyXrsKqOmCUyGCynwJB6d4_L_c-d-d6VVSJIKHpi-I,1337 +torch/include/ATen/ops/elu_backward_meta.h,sha256=WOf9PIB9PK15uzwlkGAkK6XkyZRbsyRJCsI4En0mzc8,715 +torch/include/ATen/ops/elu_backward_meta_dispatch.h,sha256=aBlORlbO3W34dcLIfKr813uBE6GBuldwHwh28Ry-2N0,1337 +torch/include/ATen/ops/elu_backward_native.h,sha256=wHDc6Vt6IBoBWbu3eHGJgfo38VIc_yUh6srQkUE019E,763 +torch/include/ATen/ops/elu_backward_ops.h,sha256=Ff2DRae-K0n8Th16lMEQTUlcYLHFQjmkHbpg8eCz_hM,2553 +torch/include/ATen/ops/elu_compositeexplicitautogradnonfunctional_dispatch.h,sha256=h59xyTdy10hFVr9wqnuWFXy1RDVR5VG3mIUD1lDUzB8,1015 +torch/include/ATen/ops/elu_cpu_dispatch.h,sha256=MQ8vMkSuDHX55lAxNnE8GxsxQdYPu8IcXX_quyvHxRs,1270 +torch/include/ATen/ops/elu_cuda_dispatch.h,sha256=c65G_16MwTnsFIaTU3okr2z3a9D0i1a5jL3U43FHPmY,1272 +torch/include/ATen/ops/elu_meta.h,sha256=XdETgQUggXbXLmDjXBr1bHVEIIcgbQGzPkOVDx8-SoI,648 +torch/include/ATen/ops/elu_meta_dispatch.h,sha256=xRS9WTykrUufIbwrl9jW_EFxnQWvpVqdDpIOjNcLFek,1272 +torch/include/ATen/ops/elu_native.h,sha256=Xg2KEpBCVixeDO0n0LQQJB7LBQVNQjivSLYC6w8gY8w,671 +torch/include/ATen/ops/elu_ops.h,sha256=sGJpKiY94hcipToqvH2mvI4HLZC0VZ3SHF69A2Ds4Qs,2865 +torch/include/ATen/ops/embedding.h,sha256=ff5qRBjJcV414GDNpNjwd-YCUohFrLJ051EgZ6k5HAg,5360 +torch/include/ATen/ops/embedding_backward.h,sha256=MZhFwDAhta1PPcjKd193Nc-MeorJICT5dNPa-UNlfEQ,2191 +torch/include/ATen/ops/embedding_backward_compositeimplicitautograd_dispatch.h,sha256=5J0XifO3Bq1LhMsYc9b4M2PZNufoKk-ufaJlbRo3XBs,1073 +torch/include/ATen/ops/embedding_backward_native.h,sha256=HDOExksQOI2v7uf7afAXkVj7OURhms2K7UImwqn5CX0,611 +torch/include/ATen/ops/embedding_backward_ops.h,sha256=0tFxssoNUF-ga6DCqbe4eqRKugPdha0PVz6m_8pbLQU,1377 +torch/include/ATen/ops/embedding_bag.h,sha256=9wec6CBQ_7r8oOi6qLeo64Z8fE805KocLkvZM1RJXPw,1954 +torch/include/ATen/ops/embedding_bag_compositeimplicitautograd_dispatch.h,sha256=9BuoONsmj6yiKNSmu9pdWum6SGALKEIlpR3qtW1KGo4,1371 +torch/include/ATen/ops/embedding_bag_native.h,sha256=Iezn2XDGSMH-H_-63M6-1b64KAagSXVdMC31CwWooTU,1083 +torch/include/ATen/ops/embedding_bag_ops.h,sha256=2jF_gh5xJ0SIyzzCTK69IWSiUPnSiOTZlW_74HeapPM,3261 +torch/include/ATen/ops/embedding_compositeexplicitautograd_dispatch.h,sha256=8eczPbmzXRyTbrpuGcqT8-kLZzcRab0-ufAGe8L3x0w,1777 +torch/include/ATen/ops/embedding_dense_backward.h,sha256=Pvk1Cm220U95_jH73ouotaSjPjz3A4wsGJkLmu-6xx4,5987 +torch/include/ATen/ops/embedding_dense_backward_compositeexplicitautograd_dispatch.h,sha256=tq6A9LTD0Kiig8wrCh9tiiSnGPH7J1sYViaDC3p5x4c,1534 +torch/include/ATen/ops/embedding_dense_backward_cpu_dispatch.h,sha256=xflS-vDS_njA_ikBB6utdaTMEEwrXs9kkv3wQnXhRRQ,1029 +torch/include/ATen/ops/embedding_dense_backward_cuda_dispatch.h,sha256=FequpJMa_WaOnF_1PX5qQN4trbOiwDBe_ZqoDndUMQk,1031 +torch/include/ATen/ops/embedding_dense_backward_native.h,sha256=fENFnwBxtTZ20TE5k_BTv3LLOvXy0gJnVPBmfg3wkXM,992 +torch/include/ATen/ops/embedding_dense_backward_ops.h,sha256=XO5UlPI1djX-hR3T90lmNHBUNPxfjeDDp2niyJCC5nw,2375 +torch/include/ATen/ops/embedding_native.h,sha256=Q1wL1DsOD95P5r3BuYmpmdjxhMJkAn-XbucQLZfbx3E,949 +torch/include/ATen/ops/embedding_ops.h,sha256=w7akxIhvpFwBSUMwdA3Cl0-JBlaatUnoiuKvwCt9A5k,2209 +torch/include/ATen/ops/embedding_renorm.h,sha256=AfVGAQ1E18CCsXxv970ttnpI8q72y_tb27q5-FLeD7Q,1867 +torch/include/ATen/ops/embedding_renorm_compositeexplicitautograd_dispatch.h,sha256=Hk99z9_8zaNCvQpsJA4at3iwE8dSNruNPzMbJ_IRy9Q,1140 +torch/include/ATen/ops/embedding_renorm_cpu_dispatch.h,sha256=T1QIHLFDW8ngs9ywm8fFp1JHzZE_GzlBZfHc-JDjmKo,790 +torch/include/ATen/ops/embedding_renorm_cuda_dispatch.h,sha256=9hv7EzsAbrhN9UKfbqdPAzJ4yHqyoWndeDVBQuEM8bs,792 +torch/include/ATen/ops/embedding_renorm_meta_dispatch.h,sha256=YxzMsc1SvfuUmiH9JV6tZS1tgWYJ-yzPloNfM10S9pE,792 +torch/include/ATen/ops/embedding_renorm_native.h,sha256=3vHXqcea7dwJSKnt7qbGk2rvkzWlZOoU5E4C1VCo0eU,957 +torch/include/ATen/ops/embedding_renorm_ops.h,sha256=H530WloZQAauqjhL-deO9qaFY9XYtP3AG9uMQiriawI,2769 +torch/include/ATen/ops/embedding_sparse_backward.h,sha256=QqJ0eCJ78i9mmPnZVm-b5Lgr_Gxp_WOaz7FU78t9y-I,938 +torch/include/ATen/ops/embedding_sparse_backward_compositeimplicitautograd_dispatch.h,sha256=59UmBtuADhejdZEqMfc59TUmjy3d00WJ9AWdpNms-p0,878 +torch/include/ATen/ops/embedding_sparse_backward_native.h,sha256=wchEKSJtbcqFRtdKnsM6DH3UQMSL_CAiBmNi4mbpZrc,590 +torch/include/ATen/ops/embedding_sparse_backward_ops.h,sha256=OuUpxrtaftq2u2f3I0pL0-9SAxnMKqstbei-NOC6c9A,1323 +torch/include/ATen/ops/empty.h,sha256=C7fU9gUVefANlrKmgmPmBTyN27U9IVgRfw2L-layh1o,9228 +torch/include/ATen/ops/empty_compositeexplicitautograd_dispatch.h,sha256=Pg-u6nc1hTg5hgbXXN7D5Sqz5qjYqS3scw7L_snbeRk,1520 +torch/include/ATen/ops/empty_compositeimplicitautograd_dispatch.h,sha256=Ab2ZE86_FOwxSjIBAyZijv2vrVRNdNZbApOKiOCUQV4,1256 +torch/include/ATen/ops/empty_cpu_dispatch.h,sha256=gG-pV-BNnabTixJcdmtNT5LEJIpxn56BugUeU66o4T4,1464 +torch/include/ATen/ops/empty_cuda_dispatch.h,sha256=BVrJX4MYNNrdnBEoP30nV9NaAj0tZbRnspu9rFZmxPY,1466 +torch/include/ATen/ops/empty_like.h,sha256=bOplMNjEKK9Q7H_JikjHSr3JJtT1XPoog773u0vf94Q,2221 +torch/include/ATen/ops/empty_like_compositeexplicitautograd_dispatch.h,sha256=NKoLc4TIRD7zVWF3qNKUX1Q3EkCgl8jEMh3cblcBGGw,1392 +torch/include/ATen/ops/empty_like_native.h,sha256=WJRtc27X0rMQMboseAWCKU39HS-XUyHf1zkS9szB9nY,1987 +torch/include/ATen/ops/empty_like_ops.h,sha256=TxRSLyuitipRn3pS7WKM7ytPf8JyYn8bl-U4OC-j3iE,2401 +torch/include/ATen/ops/empty_meta_dispatch.h,sha256=qS0_VLwGOvGDuBMelOVg_pWU0y08ynSVTA_jZljk4Tw,1466 +torch/include/ATen/ops/empty_native.h,sha256=MVcz_f09F9ki828d8OjifgVxmxcP-gi5LrVve5GB6qo,3592 +torch/include/ATen/ops/empty_ops.h,sha256=Vxj2ZhBEWUfGW3UAetYwbAIrRQOE7GliMaEZISCSNuo,4615 +torch/include/ATen/ops/empty_permuted.h,sha256=LaXNxo-bc3QxBU_zWDvQHW0n75ubpz9qwwFzJkggfKc,6778 +torch/include/ATen/ops/empty_permuted_compositeexplicitautograd_dispatch.h,sha256=FqK5pM0eV6-WOdCt_gjN-x1HuO9MnwdJbkY-QTPjR9I,1938 +torch/include/ATen/ops/empty_permuted_native.h,sha256=W3t9gkqyfxwrdDjjMpvy-8M4lgCUXzkoSOh6R8DMOK0,809 +torch/include/ATen/ops/empty_permuted_ops.h,sha256=brPf5mdlkFE-PboQMl8DM4ooBN5l-PRXXqaI_KKxf2I,2313 +torch/include/ATen/ops/empty_quantized.h,sha256=KvVJvJ17IvDiEdllMs4fzNHqwnRuv91tsGBn24I95F4,2482 +torch/include/ATen/ops/empty_quantized_compositeexplicitautograd_dispatch.h,sha256=3a73sgUINb7bGc2T0AVhFuosSoGhimuZZ7v6wyrEYQ8,1048 +torch/include/ATen/ops/empty_quantized_native.h,sha256=O3aJeFUHtbF8X5duSsdCsp-cL7zzwIkFsqC3XuAkV-c,892 +torch/include/ATen/ops/empty_quantized_ops.h,sha256=sJ-HZil4XCj_VBgjAQ4XCWk0SVGkXDHFAFI4v9rc2vA,2595 +torch/include/ATen/ops/empty_strided.h,sha256=TwKI5qb1fOKVLSsYDb_PU4JtqQoyvRjBkl7QydXL8Ms,6641 +torch/include/ATen/ops/empty_strided_compositeexplicitautograd_dispatch.h,sha256=9Yx0W23lS5yaKrujxuaO8x3yQMEwplxPJfvN3GAtPTA,1166 +torch/include/ATen/ops/empty_strided_cpu_dispatch.h,sha256=RpE01KBO-jwQHXdOPNVvTavTUhmFLDXx3oJl1NYikRg,1374 +torch/include/ATen/ops/empty_strided_cuda_dispatch.h,sha256=PHkb8AeJjOpBqtjB_JyUfRBxNsu2J-fhAYnS6v7jQQw,1376 +torch/include/ATen/ops/empty_strided_meta_dispatch.h,sha256=3EVRhA43wL0iN2-rmPL-3OOBn9d-CvMl7QwIJ0meI5Y,1376 +torch/include/ATen/ops/empty_strided_native.h,sha256=Ta412gvTTfFwwsT8sP-yF6eBpPMHCQ53K5I8rNcY6x8,1546 +torch/include/ATen/ops/empty_strided_ops.h,sha256=Vch8lsA1gzOIyn6sP0RHiN91JTB3E2gkDwzSq5TTboo,2283 +torch/include/ATen/ops/eq.h,sha256=iQ-1qAkXOGffySf-8Y5MHMBS4DKG4chGoplXILA2sWQ,1842 +torch/include/ATen/ops/eq_compositeexplicitautogradnonfunctional_dispatch.h,sha256=KBRnPMNuCIKfzjhAwMFIMu8zPyTBUJclRyW5rJ4s3pM,1034 +torch/include/ATen/ops/eq_cpu_dispatch.h,sha256=dX1UerPELGPV4lzsajFq2BNzJ4U8rteB_iNG6pY9MJI,1366 +torch/include/ATen/ops/eq_cuda_dispatch.h,sha256=D5TAunT-rKbYZugffS42_hxtzXbOG7-6TYZN8kob5fI,1368 +torch/include/ATen/ops/eq_meta.h,sha256=j4DVmV_QrKIUWHM0bA-IhMVsCYvC6I4319S3fp1DZ-s,735 +torch/include/ATen/ops/eq_meta_dispatch.h,sha256=LB_A-cJJ9BdJ6XbKFiU-riS2O7XDrXM5M1AL9AU340k,1368 +torch/include/ATen/ops/eq_native.h,sha256=-Q3nDIWiB2rCjJd7hJibWhHzPTR-v6qUPdxaQ9RI6m0,1385 +torch/include/ATen/ops/eq_ops.h,sha256=VflP0Nw5QpCh4P0sN45kQR6x1NwTvKjlM1lP-WBz--c,4201 +torch/include/ATen/ops/equal.h,sha256=SKqjoknSeMLqIcgaTf73YTlNpwraWfiEM3WkQqllP0Y,672 +torch/include/ATen/ops/equal_cpu_dispatch.h,sha256=j5YI9YxgBAETewaOudtFJrDBjlWTdCdgu5JbKAuO3G4,739 +torch/include/ATen/ops/equal_cuda_dispatch.h,sha256=mchBRbi709Z6951APBwwJTDKDAvKDG1mstukVN301iQ,741 +torch/include/ATen/ops/equal_native.h,sha256=iIVC-DymV_xikZN5YFT67DAiIGq2Cahr8bpOYV8ldeg,664 +torch/include/ATen/ops/equal_ops.h,sha256=NoHX4NdpQuz7dQbW70lNxrO83afT0Zdksq2dz3igxSE,1020 +torch/include/ATen/ops/erf.h,sha256=A83-5H6IPmd3S5rxsF6eLEebLLyaRZ7sc8x4QIyDgx8,1130 +torch/include/ATen/ops/erf_compositeexplicitautogradnonfunctional_dispatch.h,sha256=wGh8OQNFw08ICErp-Tc3_W_brhkUYUmKJuLpARD357g,835 +torch/include/ATen/ops/erf_cpu_dispatch.h,sha256=AvAjSKKI1QA5n9miS9De5mCT6BjVMIxeImwH-wHWf-k,916 +torch/include/ATen/ops/erf_cuda_dispatch.h,sha256=GWFJ8FLIUo8rGp7_yO_BRyqAuw76g_VYO_uw50s8OoY,918 +torch/include/ATen/ops/erf_meta.h,sha256=YFDpr7aP9uE9yV2pqoMRDx44jktgwn4wdmAg-OaN5Ik,564 +torch/include/ATen/ops/erf_meta_dispatch.h,sha256=-R-SQRDApQ2COAqEA-W7zlEGrIZ1SBlzcQTi82sChNs,918 +torch/include/ATen/ops/erf_native.h,sha256=qe6lYXANkCsIB60WpZKTPXygdOXQcUwPsOm2O2pFxYY,989 +torch/include/ATen/ops/erf_ops.h,sha256=UyjInwAcb3C-RwQMo4prQoo1dNRsO-qRLQZqNCB8bB0,2019 +torch/include/ATen/ops/erfc.h,sha256=aBm3Ui4IP4WzRgr2C0cwLHIFRx7kEW8iaAc0jSrpK5k,1143 +torch/include/ATen/ops/erfc_compositeexplicitautogradnonfunctional_dispatch.h,sha256=q-k3nbIGUOcOnj5MsWXChdnTMBhn8-5kB8sd5Fb8xas,837 +torch/include/ATen/ops/erfc_cpu_dispatch.h,sha256=hEQv9d-XZkOz2G1sh8F3jnvjT6YJzyq3vw7tDttSjbg,920 +torch/include/ATen/ops/erfc_cuda_dispatch.h,sha256=d72OS5rCSKyaC6Xu_CqyaFFb8tHLu4Qex1YSUW1GpOA,922 +torch/include/ATen/ops/erfc_meta.h,sha256=a9x4t4o4jYSkkVKTGC8uc5FwwY_WY91wnWB_gWPOTU8,565 +torch/include/ATen/ops/erfc_meta_dispatch.h,sha256=CJYQDuJ3y2tvJcXtA7MiRjhxM_3TVPmnVuAwc4swutU,922 +torch/include/ATen/ops/erfc_native.h,sha256=665TdYaS8HRAD3WqHwfBa2HqBGcmbSpOMSxnDWmRKHM,590 +torch/include/ATen/ops/erfc_ops.h,sha256=F8YFtPdPrzyW-EYHoIjVOtoTvyx6xaMcEqfRqwQL8XU,2028 +torch/include/ATen/ops/erfinv.h,sha256=0aKpkDo2OIzPhBuHlUcWHEGSwqTnF-0bjkiX0LrQIi8,1027 +torch/include/ATen/ops/erfinv_compositeexplicitautogradnonfunctional_dispatch.h,sha256=fUne7QfatltqmYEf3TRh9rhA6Y-RciRnVYL_GASFiC0,841 +torch/include/ATen/ops/erfinv_cpu_dispatch.h,sha256=Ztz4EPsFCKaHv5JTq-fV5OlxVqC2is5dhPrd09k8w7s,928 +torch/include/ATen/ops/erfinv_cuda_dispatch.h,sha256=aY594X9BP_pSYTU5pf-ZV_YpZmtCLig4Ez01-xQZ3R0,930 +torch/include/ATen/ops/erfinv_meta.h,sha256=NadZRdL5M2rHj_i_5OEkmbjaOuEkb-ZdTGcKfSB7K24,567 +torch/include/ATen/ops/erfinv_meta_dispatch.h,sha256=IB6PXMBYIu_ZPlpUpHwJ0gtZR5cN03-SrfZ9lIdi-ac,930 +torch/include/ATen/ops/erfinv_native.h,sha256=1NILyKthmIypHhfinVSciPHUVtGcwNaa6PpBefWRFa8,1016 +torch/include/ATen/ops/erfinv_ops.h,sha256=A_n-GQQjIOxhhBYaSN5kLMHsr5ycElMFsoSt_8kTr8I,2046 +torch/include/ATen/ops/exp.h,sha256=92R6CTIqi3LpZ2WqOAfGgx4x7cEj0rFkEUdq0zTwvRU,1130 +torch/include/ATen/ops/exp2.h,sha256=9UIp8byJhh3KS-VyWfOoMbMhiyOlusoAzUm30-t2Cpg,1143 +torch/include/ATen/ops/exp2_compositeexplicitautogradnonfunctional_dispatch.h,sha256=0sDVcNT2fsPkoBdF7c2SLkRb8Qu5BC_ZhTYVXCxFTqM,837 +torch/include/ATen/ops/exp2_cpu_dispatch.h,sha256=ChHI9xli18UWhLndDAE-VrZtlBDl609msXhfsomnRT4,920 +torch/include/ATen/ops/exp2_cuda_dispatch.h,sha256=kHFfupmTIOQ3oT8rn6NcF0aEI8wbGtGBjX9Tk-maJ7M,922 +torch/include/ATen/ops/exp2_meta.h,sha256=fBrOcRy3bNe4M666Z5Wwmm04CF-fLT6ySNVibBoljTk,565 +torch/include/ATen/ops/exp2_meta_dispatch.h,sha256=wu8riUDi1xrg5kTk0hr_0c65UIbBLYFFwaJhTSr_fS8,922 +torch/include/ATen/ops/exp2_native.h,sha256=xKFu-mlxFX7kEZcP0rd-wkeEGx1X3DAavXvtvwVtR2w,590 +torch/include/ATen/ops/exp2_ops.h,sha256=oeFmV6KgNcmY4caHnaFGxS7RIFKnWfYm-2bPBs2UsPo,2028 +torch/include/ATen/ops/exp_compositeexplicitautogradnonfunctional_dispatch.h,sha256=2rqohVS0dE2UKHaFzeWd2kxoZGugi7StrreNa1F_gFE,835 +torch/include/ATen/ops/exp_cpu_dispatch.h,sha256=2lTOIfH7qlYfKCswj8TQo6jF6baWXRvXXOSZW6-qs78,916 +torch/include/ATen/ops/exp_cuda_dispatch.h,sha256=MY8adYFNWWvvAhwbmDuDMoWissmDQ7GMZDZTh8zwR0g,918 +torch/include/ATen/ops/exp_meta.h,sha256=40w0_tM5WBNzNX1fjqr01xNoCWgAjzjaPZDT2_CsQ8s,564 +torch/include/ATen/ops/exp_meta_dispatch.h,sha256=faByptHQZXch-YyobWHQDNAEq9PVfcLYmtDs8XX8jRg,918 +torch/include/ATen/ops/exp_native.h,sha256=c5vLKeghhkAr9pGMOVVH1BsPA2YGjqTIX9jnTG2tgmQ,587 +torch/include/ATen/ops/exp_ops.h,sha256=dNFRjpQbIXgQ81uOLGR0HiIwLvk_fgCto_qpGCT0nfo,2019 +torch/include/ATen/ops/expand.h,sha256=d-LJjKaLngIBMa6VOHcCCGYTfYVFL3AUFNXnnW7Zt5w,1044 +torch/include/ATen/ops/expand_as.h,sha256=pflNichogY8a3NzNL3hLTCM6RSf97ChXJ4zm-1_6JA8,506 +torch/include/ATen/ops/expand_as_compositeimplicitautograd_dispatch.h,sha256=cVEtvoJtb283EXs5fSIIfIxBIVYJDQd4ARPpCCyRD0g,793 +torch/include/ATen/ops/expand_as_native.h,sha256=tRYv30yjlZTx3eoGCytiOk-kyUqPmhNy6qe6xLi4QLI,505 +torch/include/ATen/ops/expand_as_ops.h,sha256=jHNRwLyxYtol8r5EmBGmaw2LW-C4ddr-PUqimhxSAxQ,1058 +torch/include/ATen/ops/expand_compositeexplicitautograd_dispatch.h,sha256=Qx0mu67trWhHgomTJ65zvGfIkyWratnKpZ8cCcS0Rhw,915 +torch/include/ATen/ops/expand_copy.h,sha256=a0uwcZdXhq_1g9AubNy7Y2cBWzlgIhVgVxzKqWPY3LM,4126 +torch/include/ATen/ops/expand_copy_compositeexplicitautograd_dispatch.h,sha256=PxfPj2yE_V2uwySAAggHjWBcQW_hqe2TA8zoYhgz52g,1226 +torch/include/ATen/ops/expand_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=roKXKPuF_RUB9Xj9pbeHE-qMCfmwbEU5uds7m8CcJvQ,951 +torch/include/ATen/ops/expand_copy_native.h,sha256=FiOhUesIg64_q9Dvc8GNXazQdc8EHq1yiiApWlFSY04,666 +torch/include/ATen/ops/expand_copy_ops.h,sha256=58qUb8Qwi8NPaWUbikb_KPgQ_R2p_p74AAWsd35OQrs,1870 +torch/include/ATen/ops/expand_native.h,sha256=we5VOP_s1f_RvJTM2E48YXEkGUTXuq6PoxV0mQpFQvI,519 +torch/include/ATen/ops/expand_ops.h,sha256=yml7WDEDRtVq6JHE1NrQPygKJI228iUrIkJ9I8PpcTY,1111 +torch/include/ATen/ops/expm1.h,sha256=fjgushZSjdZZxxIdWHhckUnUeJjqBKKh3p8Mf6vGeL8,1156 +torch/include/ATen/ops/expm1_compositeexplicitautogradnonfunctional_dispatch.h,sha256=vYezwxrqAV3EN_qhHxw729lZtQzbNnM3w8ieEuCtH9I,839 +torch/include/ATen/ops/expm1_cpu_dispatch.h,sha256=W0rNlEV4hw3jspSM4-TYpygYaW6JrTwYtG2KuHd0RCU,924 +torch/include/ATen/ops/expm1_cuda_dispatch.h,sha256=kgOBS_KiKvv-5_DCd6iN2G8JXMvGnoEnK8WyzCL7v6E,926 +torch/include/ATen/ops/expm1_meta.h,sha256=DiJRotQfClqgqC5aI0w7ac8tiW4dW7IU9aUe3F3uE_k,566 +torch/include/ATen/ops/expm1_meta_dispatch.h,sha256=8ssRg75TMNvGC5FTliSsbT4bmgqr6DAgNPS_hktXrvA,926 +torch/include/ATen/ops/expm1_native.h,sha256=jj54vpSEMhg9TjfCRz55MHir4WwGczNxeDCeuCVkI0Q,1007 +torch/include/ATen/ops/expm1_ops.h,sha256=B12lyKgYufsUV9arj9D1Hbi7MHw7jdAPGCsMNaLuwvU,2037 +torch/include/ATen/ops/exponential.h,sha256=t7zr2-u9_fb8S4ZblgJFdMaNWzgnLjKN_mVo3PpbxaI,1462 +torch/include/ATen/ops/exponential_compositeexplicitautograd_dispatch.h,sha256=l7rCj1wxjhWVtwsQpq9KIybjIa0jxB-6JJ2pxFeadHQ,1138 +torch/include/ATen/ops/exponential_cpu_dispatch.h,sha256=0vDNI67kjAHPfEBn5V7WtWuY3Xu7nlljoCgieHu0eKM,795 +torch/include/ATen/ops/exponential_cuda_dispatch.h,sha256=01VrYCz3oTzj6Tn1u7Y87Hz0lEdBX6PIDHKVrpzmH90,797 +torch/include/ATen/ops/exponential_meta_dispatch.h,sha256=9xHejLTHsd_h5y2rqrRCXxj3ti4yYOINfQjGpvnyHyA,797 +torch/include/ATen/ops/exponential_native.h,sha256=9NqL0ZlpayEX4X6DYpZaOmm9N5QSpfAOPqliguwLDF8,822 +torch/include/ATen/ops/exponential_ops.h,sha256=pRzOva3OT_CvZS6ZUcrJgFAGlfBrRAZPPjtuAL6qw5Y,2679 +torch/include/ATen/ops/eye.h,sha256=nW6aLx57q-iZEztiv5DUPkzuMt1dlaZCqEVV0dtev5A,9627 +torch/include/ATen/ops/eye_compositeexplicitautograd_dispatch.h,sha256=ehsUFJ30ITnyahPix5YLgEtyNsXSa45xW6zlFwQjy3o,1802 +torch/include/ATen/ops/eye_cpu_dispatch.h,sha256=WagdK81J4dFGus7FCwq8mk04QHk4watkX2B9KfU4hqw,1254 +torch/include/ATen/ops/eye_cuda_dispatch.h,sha256=fZMjlfp0iFk0bev_thUZx7p9nIENKSuCGO7OzIAGrIM,1256 +torch/include/ATen/ops/eye_meta_dispatch.h,sha256=2LSooSb0RwLAP5uJzCUe_hzKr4ptVi2_XpxzNpCGW70,1256 +torch/include/ATen/ops/eye_native.h,sha256=REgaCMG7LyvDDwY5NL4C_KFVNk6uLFjmV4qfnijRTP8,1105 +torch/include/ATen/ops/eye_ops.h,sha256=WOFm1awqr6UQLqn7Bfnq8qCg4rANeJbN-WL6wpEc_co,3590 +torch/include/ATen/ops/fake_quantize_per_channel_affine.h,sha256=vwK6nAHbCwDbSNqxignBnWErgOiujhV1CKcAsK9sUE0,970 +torch/include/ATen/ops/fake_quantize_per_channel_affine_cachemask.h,sha256=pFykLL-W3g0iCebrCnOGMa9PwBX9E_TLvNypeLy53eo,2293 +torch/include/ATen/ops/fake_quantize_per_channel_affine_cachemask_backward.h,sha256=mMcl46-QwLAWSygcv4QkqKMRXMKUKtSCHJkISUJ0VdA,861 +torch/include/ATen/ops/fake_quantize_per_channel_affine_cachemask_backward_compositeimplicitautograd_dispatch.h,sha256=JxMXUPjuACR5mmp7VN-Pg2NUmBPWCSm0EWJXrs54vto,834 +torch/include/ATen/ops/fake_quantize_per_channel_affine_cachemask_backward_native.h,sha256=Su2x3IMYG_whyXpWWrJLSozPt3-twXE7xCpYuGdvrZA,546 +torch/include/ATen/ops/fake_quantize_per_channel_affine_cachemask_backward_ops.h,sha256=rILBDx2_hzIUZK079CvolOEfI8I1VdzdASPCaXYCXS0,1175 +torch/include/ATen/ops/fake_quantize_per_channel_affine_cachemask_compositeexplicitautograd_dispatch.h,sha256=ZXMSU5pKcylMlfjAWD6neNwsbZbPKao2K5mEvx4dT5Y,1251 +torch/include/ATen/ops/fake_quantize_per_channel_affine_cachemask_cpu_dispatch.h,sha256=jVtpXecxXCFm47C-QGx8SZzCHyLdGaL4PQ4NJwMTQts,890 +torch/include/ATen/ops/fake_quantize_per_channel_affine_cachemask_cuda_dispatch.h,sha256=W5X4cgVJWmhcpTyyqsP3cV7X1A7mYOgucyrfRbYUrp8,892 +torch/include/ATen/ops/fake_quantize_per_channel_affine_cachemask_native.h,sha256=26GdKVXpEqA9BgNWZIW4n6_FkDZUnmvnrZQU2uJNci4,916 +torch/include/ATen/ops/fake_quantize_per_channel_affine_cachemask_ops.h,sha256=s-lJbo4BcTwtG50yxixonptxxxjaxeO6Ga39B_t6s5U,2743 +torch/include/ATen/ops/fake_quantize_per_channel_affine_compositeimplicitautograd_dispatch.h,sha256=NoDLgqodKweiKMwy428J_3MP4RoT5_zWod6iKVHWsKw,899 +torch/include/ATen/ops/fake_quantize_per_channel_affine_native.h,sha256=zBzKtUwam8Yuxh9Qh7pddkBL1R5lnd1G55QqAMBG3yk,611 +torch/include/ATen/ops/fake_quantize_per_channel_affine_ops.h,sha256=4TxEWWyu2J8jt7QRrxP-4cZmygtdkA9R-vKbA1duFbg,1393 +torch/include/ATen/ops/fake_quantize_per_tensor_affine.h,sha256=v60UT1pixtNEV4-aCxP-Y3FME0WokkYdp8gD09xFRX0,1348 +torch/include/ATen/ops/fake_quantize_per_tensor_affine_cachemask.h,sha256=n5njGX4ch4eaWOWfQcgiBYhf7m4zaXOWzv14WUY5rPQ,2112 +torch/include/ATen/ops/fake_quantize_per_tensor_affine_cachemask_backward.h,sha256=R0YQK0pkqYG71pbooMivpKrioI2KUfL-KUvMTiR-bSc,857 +torch/include/ATen/ops/fake_quantize_per_tensor_affine_cachemask_backward_compositeimplicitautograd_dispatch.h,sha256=kgv_kAu6UywN6olqJdChDagPCRRJwjtGMoox6s5AOaA,833 +torch/include/ATen/ops/fake_quantize_per_tensor_affine_cachemask_backward_native.h,sha256=6yI-jfYeO5wXUIVWE6N3RMIAjuxc_J9Cpln0hizFxRg,545 +torch/include/ATen/ops/fake_quantize_per_tensor_affine_cachemask_backward_ops.h,sha256=9ycfRRqgs_VXCaEd2UVIYFCMTM1yxvvZjTSllxu0OYE,1172 +torch/include/ATen/ops/fake_quantize_per_tensor_affine_cachemask_compositeexplicitautograd_dispatch.h,sha256=fbadkfIeu7cPKJZ13oVqbDSuZvJn07TygRegim86hRk,1175 +torch/include/ATen/ops/fake_quantize_per_tensor_affine_cachemask_cpu_dispatch.h,sha256=5_zbe8f0icL81e3zCyenXS5uFjQz8qjbIkFWONtP3dM,852 +torch/include/ATen/ops/fake_quantize_per_tensor_affine_cachemask_cuda_dispatch.h,sha256=zHXqY_YUz1Qz-NKDPDRfJr5bqx0SwSQ2AuNHcjG1il0,854 +torch/include/ATen/ops/fake_quantize_per_tensor_affine_cachemask_native.h,sha256=3u5BLBB0l5M2NZgrdekvS5NLf1TL1FfBA5AQ3bC0vn0,840 +torch/include/ATen/ops/fake_quantize_per_tensor_affine_cachemask_ops.h,sha256=A-_uKOZNbcCHFP9P7pFEbw6n0C4xdqCdEOftWsse2Ak,2497 +torch/include/ATen/ops/fake_quantize_per_tensor_affine_compositeimplicitautograd_dispatch.h,sha256=CuUbT204hgwVISgRbz0QceEE_pkHUyBMx8mblUoNBdc,1035 +torch/include/ATen/ops/fake_quantize_per_tensor_affine_native.h,sha256=1RC-vBFFDYwMYHjYFF5A7eAhZyr_GjdqKmOZCMLZDDo,747 +torch/include/ATen/ops/fake_quantize_per_tensor_affine_ops.h,sha256=H3pnCh48Fx7vinzfrQvlIDYLBnM7D2MvNVYuB6FYhBE,2200 +torch/include/ATen/ops/fbgemm_linear_fp16_weight.h,sha256=FG7SH6dPGBFFdt15iPTIzabot5Xmlvw3UoEjsDER3L4,831 +torch/include/ATen/ops/fbgemm_linear_fp16_weight_compositeimplicitautograd_dispatch.h,sha256=2TIc3R-T-6XY5prv9HM0Xk_v0DnkOs72lpAipZPTMIg,843 +torch/include/ATen/ops/fbgemm_linear_fp16_weight_fp32_activation.h,sha256=G-JCYJjzqM9V0CQaINGea5sILUtFN43A5PYAMBxsG_4,895 +torch/include/ATen/ops/fbgemm_linear_fp16_weight_fp32_activation_compositeimplicitautograd_dispatch.h,sha256=mTUsi4muu5y4oUza740uug5WYOpl3_9TAzZaC4rxWeY,859 +torch/include/ATen/ops/fbgemm_linear_fp16_weight_fp32_activation_native.h,sha256=xWjridW6s-2fCAgWCQwGRt6mD6IrOHPU-8m95pbaxkY,571 +torch/include/ATen/ops/fbgemm_linear_fp16_weight_fp32_activation_ops.h,sha256=hKQ1mj0UB6ATSPzvWK0xH5r1Q5GEhe997BV85Ktv1pg,1258 +torch/include/ATen/ops/fbgemm_linear_fp16_weight_native.h,sha256=NgxqsHWVLv5tkj0r1IYl7vsRU31KjyAToq_1UEOXEYQ,555 +torch/include/ATen/ops/fbgemm_linear_fp16_weight_ops.h,sha256=0qdPZ5BM3tF4aauIDAEdEvJsLP8fOrNE3RN0JDQ7Bm0,1210 +torch/include/ATen/ops/fbgemm_linear_int8_weight.h,sha256=OGjUJ5jPMrmMMGmMXL9MOZcnKFJNm5SVLHx3WwMqfuE,1076 +torch/include/ATen/ops/fbgemm_linear_int8_weight_compositeimplicitautograd_dispatch.h,sha256=VOF4hdnMmoQ5V3QOUBKW6kwK4jRD5YsTtIBkS8WsKnM,966 +torch/include/ATen/ops/fbgemm_linear_int8_weight_fp32_activation.h,sha256=NNdNJ0OYYhR3q177-vfCkufmhim3MYR7Z8gmERGKYG4,1140 +torch/include/ATen/ops/fbgemm_linear_int8_weight_fp32_activation_compositeimplicitautograd_dispatch.h,sha256=B32qVHSu0izolZAH83PYBxnLvJbCQquD-fdfj8E2c54,982 +torch/include/ATen/ops/fbgemm_linear_int8_weight_fp32_activation_native.h,sha256=NkLGW8AoihJNGt2pQpVf-UwW0JMPOLIXHRr1rgbGvTs,694 +torch/include/ATen/ops/fbgemm_linear_int8_weight_fp32_activation_ops.h,sha256=FLVwtU9Ss0FM4Gg1JvdCUFxk6_oCeOqelk7AOE_WkzQ,1659 +torch/include/ATen/ops/fbgemm_linear_int8_weight_native.h,sha256=QgVNG_v8aIIuxXeMDiQmZUEeSolofRyrhYHrStsTrJM,678 +torch/include/ATen/ops/fbgemm_linear_int8_weight_ops.h,sha256=yDf9rgXzwy7AH9_oWCGU4IalDe9q-DmEfN9mE-rFZ0Q,1611 +torch/include/ATen/ops/fbgemm_linear_quantize_weight.h,sha256=mfD3ULQ_PnqlUdSbHXVmAbH8nXyd7UAfYLYApE2vyEA,794 +torch/include/ATen/ops/fbgemm_linear_quantize_weight_compositeimplicitautograd_dispatch.h,sha256=0Zti5W1-T7nBUKrbxj2ACecAUbif0Mi1P616rTHAOBQ,828 +torch/include/ATen/ops/fbgemm_linear_quantize_weight_native.h,sha256=3oEWgVRkDALVpekpMGgY7EP90wxcznwybRv2j-pdN2Y,540 +torch/include/ATen/ops/fbgemm_linear_quantize_weight_ops.h,sha256=OaFp-08oB9qrYSmzGQJURfJeX8am7zIRpjCl3xLNsTo,1171 +torch/include/ATen/ops/fbgemm_pack_gemm_matrix_fp16.h,sha256=RQ7aWWSnu2YRXkZrMwGP_Ryo2h__syZ6zPiz0THB5D0,728 +torch/include/ATen/ops/fbgemm_pack_gemm_matrix_fp16_compositeimplicitautograd_dispatch.h,sha256=Ih5X5QkgaWjElwIZOm_kTdPRVdIJNO_zgwdUCBUd0xA,787 +torch/include/ATen/ops/fbgemm_pack_gemm_matrix_fp16_native.h,sha256=6L2yf6xFEvsWnNeax4PTvTdT6h-Vzm9T_aXtbi1cKVg,499 +torch/include/ATen/ops/fbgemm_pack_gemm_matrix_fp16_ops.h,sha256=Sh-G-Od9DXcXSHwWHdiXtZXBPMx6lqsZdYc6kRbwdw4,1026 +torch/include/ATen/ops/fbgemm_pack_quantized_matrix.h,sha256=bhTYkB9NIRA4s0PC8g1rq83FPIEZ24XXGf1H57cdjhs,980 +torch/include/ATen/ops/fbgemm_pack_quantized_matrix_compositeimplicitautograd_dispatch.h,sha256=5xbXNBNxNtd1wLmRxo1hwUOVwifhP5jCi7uPgLKYLr8,886 +torch/include/ATen/ops/fbgemm_pack_quantized_matrix_native.h,sha256=3tykektJY3IknTQ_QYWih_jsJd_plu3y3kP-Q2GCT3Q,598 +torch/include/ATen/ops/fbgemm_pack_quantized_matrix_ops.h,sha256=O23zeL32_D2rnw-8irHLuH1rBaZ1IGj51WukHdwuJ-M,1679 +torch/include/ATen/ops/feature_alpha_dropout.h,sha256=MKjz6plq9EPyiS7nyQtahi4eg-n-xZFNz7FmGMWREWM,993 +torch/include/ATen/ops/feature_alpha_dropout_compositeimplicitautograd_dispatch.h,sha256=Zh7Rcis7WPpgQySTDIHQCCX1AwNskH1EmBmvLhLYMQo,890 +torch/include/ATen/ops/feature_alpha_dropout_native.h,sha256=fzEhHbkam3g_hVTUknFNtTArEvV0FEdEO0exshGBL5k,602 +torch/include/ATen/ops/feature_alpha_dropout_ops.h,sha256=CpGd-eH76VJtatAr6KlsOq1ly2WPnLs2L3ZModuyzTw,1707 +torch/include/ATen/ops/feature_dropout.h,sha256=Q6nTxEU4tQpwc4DD3J-wtJ0jZHadLpDYAiJuXpOxxNA,951 +torch/include/ATen/ops/feature_dropout_compositeimplicitautograd_dispatch.h,sha256=wkzqotHqspY7l-2e0WUj3yjHtVDBM9sobxIsjs31qR0,878 +torch/include/ATen/ops/feature_dropout_native.h,sha256=S6uUBV4uLaJnwwXmCijeyTC5GZmWjo2Ppo7Y4IGisTc,590 +torch/include/ATen/ops/feature_dropout_ops.h,sha256=LhCD1s4y-4yeL37CayBVXAdHT8K6NxJNlDvW5OgJUPk,1671 +torch/include/ATen/ops/fft_fft.h,sha256=RGGWwBY_UPEAfB0D2OTPlkBOTeJlooWwgHNqjhZuTgY,5022 +torch/include/ATen/ops/fft_fft2.h,sha256=AhR76kogAJrxVdqZOfRMfzzPu_dEwcorWTpMLtLg7ag,5315 +torch/include/ATen/ops/fft_fft2_compositeimplicitautograd_dispatch.h,sha256=-63JI5LwDzL26TDo71QQtrucMIRHDtBlDfLG9ec53mk,1854 +torch/include/ATen/ops/fft_fft2_native.h,sha256=qScWOcJRA4LNlLLb_dYDlEfDDj4uNyOGwlJg4-LpYHs,792 +torch/include/ATen/ops/fft_fft2_ops.h,sha256=GAZmM4P6Ue_wvJ2Aa12NbdCVoINOG5weWvUKHLCHMIk,2193 +torch/include/ATen/ops/fft_fft_compositeimplicitautograd_dispatch.h,sha256=4lV_52KfhyykEVl7DXt7BqHQRwG7NE45vJTE3p0KvyA,1789 +torch/include/ATen/ops/fft_fft_native.h,sha256=dj3_TB4jV53jr6bpDxjhXpGebNYnq7Vf_w46VdbRnqU,773 +torch/include/ATen/ops/fft_fft_ops.h,sha256=3iuD8Dx8a7W9IhBl3KD7S1g8mxug8rbDiRRAwtOznSY,2129 +torch/include/ATen/ops/fft_fftfreq.h,sha256=U8IMEjUbUjRIS0YcVdWUwSy3clN5D9VRvw7CTTPcDEo,1764 +torch/include/ATen/ops/fft_fftfreq_compositeexplicitautograd_dispatch.h,sha256=HfrBKswuThmSxq6fiQjlI0Z20hqWBM9HOUgIKCArSzU,1162 +torch/include/ATen/ops/fft_fftfreq_native.h,sha256=CPt5eeTpAVVBetuMOnGWjajIdVVmAjxzeBVAb4BMBRk,717 +torch/include/ATen/ops/fft_fftfreq_ops.h,sha256=f48g2tBtcJnIR2-K1Bn2MJrVXTZN7AX4uN6T6p1AWC4,2065 +torch/include/ATen/ops/fft_fftn.h,sha256=qHSVUCje8xQ8E-GArpkOKskqALAtHOMPYU6gwoLT14o,5455 +torch/include/ATen/ops/fft_fftn_compositeimplicitautograd_dispatch.h,sha256=0p17AC3NBvhQzXAsWJaa3hwC_ouF4DV7TCm4A4ID3Mg,1930 +torch/include/ATen/ops/fft_fftn_native.h,sha256=KJU7NitLmJJK9gnPrRuU_jwVo3Mj9y7xEWIvvmvZQ8U,815 +torch/include/ATen/ops/fft_fftn_ops.h,sha256=XFcfASTuFQcxpTzzZ9mRl1WF33i0JjMec2ITlLBj4xo,2237 +torch/include/ATen/ops/fft_fftshift.h,sha256=-NwsC0dYuI1lq5eOqm9Hed3-4OGXot-_jLr3VDvvRks,728 +torch/include/ATen/ops/fft_fftshift_compositeimplicitautograd_dispatch.h,sha256=qGaK5cJvXcS_7dVd4GEU3Z3_Jn4w0ckokajhhKDPSmA,814 +torch/include/ATen/ops/fft_fftshift_native.h,sha256=X7OLUTiCLMpdvoK3_8SWgomPQNoQQu6J-EFcmvgy2ek,526 +torch/include/ATen/ops/fft_fftshift_ops.h,sha256=OgCmka1O7pFWJ-yfOJ5PxFmfK4JZcxPf7blCJSRVlkI,1076 +torch/include/ATen/ops/fft_hfft.h,sha256=vvW2BZVY8RUxfoAYrIia4i0VWERQdA8wBuoxzRLhh1Q,5053 +torch/include/ATen/ops/fft_hfft2.h,sha256=7FYKtg2xsOPUlgN-LFqV9xxSqr9X1LMZNzh78AcFu04,5346 +torch/include/ATen/ops/fft_hfft2_compositeimplicitautograd_dispatch.h,sha256=fhKNpwvd5s16f0Kl51Ts1eREbtbCHvYM6s2lO_t1JRo,1860 +torch/include/ATen/ops/fft_hfft2_native.h,sha256=jXHx1tQSYh9T4DFwBcIBfBYtpCDeeGxJe4lbVmX2cn8,794 +torch/include/ATen/ops/fft_hfft2_ops.h,sha256=-WbqOKJRaIPk0WYDr3-iuD7jRnD3_miO6vVM2LXgmTc,2199 +torch/include/ATen/ops/fft_hfft_compositeimplicitautograd_dispatch.h,sha256=Z1LglLvVY3U7PQiZVcCYQ84ekbV26-3ej-hL9c3OIjQ,1795 +torch/include/ATen/ops/fft_hfft_native.h,sha256=IH0OLDZz56ie2Znxi6gFGC7VaUaPR-sUnX7NVCgbdas,775 +torch/include/ATen/ops/fft_hfft_ops.h,sha256=JTq1q9mRSRbyeO0nFijPJzVh9DwBOdT8SqsU0m9Ni80,2135 +torch/include/ATen/ops/fft_hfftn.h,sha256=5uSsiwI_jxBYwuTu9zFBQ8NAR0iJMsaps_k4zx4Egpo,5486 +torch/include/ATen/ops/fft_hfftn_compositeimplicitautograd_dispatch.h,sha256=0cmXn5mCssUFPWGkbuZwNUW1zL2o9_fbEVri6QaN_hw,1936 +torch/include/ATen/ops/fft_hfftn_native.h,sha256=KCSYqMTHSYVs_ji7iuF_8s-D91W0BU-h3EjsJfEqdNA,817 +torch/include/ATen/ops/fft_hfftn_ops.h,sha256=iDY0hlNmkUsitOSo4TCl_ckunkT2O2DzRSPA7A4k7XE,2243 +torch/include/ATen/ops/fft_ifft.h,sha256=SqfMPA8ca-CFfV8GcU0S6UAlosE9HxoiWPju1h_V6jk,5053 +torch/include/ATen/ops/fft_ifft2.h,sha256=lGRmsXPKp5APbbOguA24hoe7xIvTRdUPbhzECz54yLE,5346 +torch/include/ATen/ops/fft_ifft2_compositeimplicitautograd_dispatch.h,sha256=t1OHLnjimNRbZULaDzAgUXUIyNzfWX50GlZ_N7l3wic,1860 +torch/include/ATen/ops/fft_ifft2_native.h,sha256=45Hh8RvwffprU5J_jJcBXjQtrKNWTUMg1Xk0VZE5kxM,794 +torch/include/ATen/ops/fft_ifft2_ops.h,sha256=vth50aXTPLZsSt6-ojbo5lGT3I_5qr2b7wn2rsems10,2199 +torch/include/ATen/ops/fft_ifft_compositeimplicitautograd_dispatch.h,sha256=QsjeBgxYFA32XKl2MHjTJWDxcpee2llGesu9_0Gwz_s,1795 +torch/include/ATen/ops/fft_ifft_native.h,sha256=zd3krxsYz9NGr3fwlXFMwF2h2kL0DLpbpjuaUEj8_v0,775 +torch/include/ATen/ops/fft_ifft_ops.h,sha256=FYc10LKgPSR3H7tHa_FyVc0SSDAGEUPUxPSVBs0DRNM,2135 +torch/include/ATen/ops/fft_ifftn.h,sha256=6N7DSSMpUYWxs2qkdSA_Q0zImhFOasPYsbFPYcnVG-I,5486 +torch/include/ATen/ops/fft_ifftn_compositeimplicitautograd_dispatch.h,sha256=Si8WuBPgT2bl671aHYBk9_RryMHBPMbLh3843FuKgT0,1936 +torch/include/ATen/ops/fft_ifftn_native.h,sha256=jHLznplViQ9Rj3jRpZTxaFe41M_n3z8vFOUmPBMh_Lk,817 +torch/include/ATen/ops/fft_ifftn_ops.h,sha256=Umv5RjGxMKQKAi7YYcywdUrfbndAijtQRP26Tpb5FCo,2243 +torch/include/ATen/ops/fft_ifftshift.h,sha256=xKpKjlT1bmWPoZn2ksEYu5h2RRwSYIgetwoPiZ4TfcA,732 +torch/include/ATen/ops/fft_ifftshift_compositeimplicitautograd_dispatch.h,sha256=uMvh2lZ10qWekzS8rp2_hSlMUnaaXL7uw2VJp2_L1ac,815 +torch/include/ATen/ops/fft_ifftshift_native.h,sha256=5FzEtKaCvLULA7zgfLEnQQYl13yCs5DbOs7BaKbitVw,527 +torch/include/ATen/ops/fft_ifftshift_ops.h,sha256=eZQaZQIsLyHmRRMng4hbIzfFXGvgs2JmXiPYQs6Znj0,1079 +torch/include/ATen/ops/fft_ihfft.h,sha256=byrqcvF_LJfIfZ86IZRPkbIpX0GQm2-z9I2lREOLce4,5084 +torch/include/ATen/ops/fft_ihfft2.h,sha256=V0i7rFiHq2IdQZ49nr7i7oMpedFHvALduV7bi8gpihs,5377 +torch/include/ATen/ops/fft_ihfft2_compositeimplicitautograd_dispatch.h,sha256=jOe28fv7afllXLczz0zPMmnkFfD6Tg0MvvofX8ULrPc,1866 +torch/include/ATen/ops/fft_ihfft2_native.h,sha256=tai6PTiCYle_Ihu94vZRjquNVt1evZ_RyfBSJ5neJkY,796 +torch/include/ATen/ops/fft_ihfft2_ops.h,sha256=yoaY9sYe48KKHOsCjtznxrgNOQnzXJmqlFH5uC0iX4g,2205 +torch/include/ATen/ops/fft_ihfft_compositeimplicitautograd_dispatch.h,sha256=wCiKc9ZesGAyvSVYWnZYfPQYHCysxiA3pnk7dFZETu4,1801 +torch/include/ATen/ops/fft_ihfft_native.h,sha256=vZNVCtiAuAC29TNq-BIPIToI4df6wTA3wXoPHiSlrdA,777 +torch/include/ATen/ops/fft_ihfft_ops.h,sha256=GxZRWnjanuhFHk8kiXmOWHtUz8oB88tu_F3K7BqYY00,2141 +torch/include/ATen/ops/fft_ihfftn.h,sha256=JqCf9NzODuqqnH9vzKz8WmV-NxqVIy_aay9FXG_QJUk,5517 +torch/include/ATen/ops/fft_ihfftn_compositeimplicitautograd_dispatch.h,sha256=CGe0kEy4qIh3RKPbTGdBy2zhhQAbRG0_YmeDHydSerE,1942 +torch/include/ATen/ops/fft_ihfftn_native.h,sha256=Jen-OyDD4JFXIoPWEZkgZJVeSjOst6nKSL2_eWJOw4A,819 +torch/include/ATen/ops/fft_ihfftn_ops.h,sha256=hS_g3AncrH5UH-7mjTKOfGvIT2wjXkibIOaHm5slk9Y,2249 +torch/include/ATen/ops/fft_irfft.h,sha256=7qB9E3Wsk3xo-QNFm3OS72lOHwaBk8NLb4N2Zz2hmko,5084 +torch/include/ATen/ops/fft_irfft2.h,sha256=rgrLddUQsWrDiY8F1Cef80TbwM681mgEicEIyx_0qBA,5377 +torch/include/ATen/ops/fft_irfft2_compositeimplicitautograd_dispatch.h,sha256=yTuVJByVNUTC1Cc90_J6RQwgy88-RxzLa_FG6FxPJV8,1866 +torch/include/ATen/ops/fft_irfft2_native.h,sha256=sGi_m3GB8669nE63hvw4mN9qrj6PV1dsuCdUk9L38Tc,796 +torch/include/ATen/ops/fft_irfft2_ops.h,sha256=mrG2jurPUzWtIs7oe8cVCnvyXfdiCxe1ORmq_Q1NEwE,2205 +torch/include/ATen/ops/fft_irfft_compositeimplicitautograd_dispatch.h,sha256=MlJkG8MnQYOR_jHmS9qxXADyhYt4y18d2cc414CC2Zw,1801 +torch/include/ATen/ops/fft_irfft_native.h,sha256=BC99ztD2OMwEguVuGBtKISpR_cPqWXHWVGv-qvh6pG8,777 +torch/include/ATen/ops/fft_irfft_ops.h,sha256=jHLf2Pe5rlxbiE8rb4p0cC_RbHBgsM4WMj4bR4uGIUA,2141 +torch/include/ATen/ops/fft_irfftn.h,sha256=bqGpodxxD2wfX5w-1nxm-kwcXu6Ayt8yXumKG_sVfLw,5517 +torch/include/ATen/ops/fft_irfftn_compositeimplicitautograd_dispatch.h,sha256=HfX261YyX0exsGBbjvWdPXUwrve6b3psgncmRbRExAQ,1942 +torch/include/ATen/ops/fft_irfftn_native.h,sha256=FL6KShD7xT49BVby3bs1Gsj2yF35fP__5vOovNr3h8o,819 +torch/include/ATen/ops/fft_irfftn_ops.h,sha256=-i4GHMIDTf2Urw1B6-mY8JWl_Gd3J4HTxJUloVKzKYc,2249 +torch/include/ATen/ops/fft_rfft.h,sha256=77HH-TGRiDxzpVrLT2H82VcD5fKvyK7HdjyWp1_Jrdo,5053 +torch/include/ATen/ops/fft_rfft2.h,sha256=3yOl7tyR3--zeb-9SnFzNQvE21iB4_iTcRfOP9h6sUI,5346 +torch/include/ATen/ops/fft_rfft2_compositeimplicitautograd_dispatch.h,sha256=DSGWBvw0F9aSczdkv-hIUUUMFgBZgvbpcBz3KOi8uHQ,1860 +torch/include/ATen/ops/fft_rfft2_native.h,sha256=1VoZ3OnKclcreKxl1QL9SPSR21GFmLSCRyBP0nF4Cko,794 +torch/include/ATen/ops/fft_rfft2_ops.h,sha256=ADn3fE1JyhpKTkut3n9k17nAiREwcSDJ7oMZK7OHE0w,2199 +torch/include/ATen/ops/fft_rfft_compositeimplicitautograd_dispatch.h,sha256=ZWujkLv9qtstBdOgEJMeGM7FW--GBnPWt-Mi0WRUyPQ,1795 +torch/include/ATen/ops/fft_rfft_native.h,sha256=wqt0FZclnnEceAwKZRms_KPjaH1ZOoy1QLucGclhcK8,775 +torch/include/ATen/ops/fft_rfft_ops.h,sha256=Gecbqz9aW2liloAaL3qSaToF2B2qnc9WOeQ1dfY9i_o,2135 +torch/include/ATen/ops/fft_rfftfreq.h,sha256=j6ZnCZJwAYbKz2k5Q9x568bPdmlU8yUGik8cm02XUXk,1777 +torch/include/ATen/ops/fft_rfftfreq_compositeexplicitautograd_dispatch.h,sha256=rs7f69poYTw6l38XI2DlxdDb7Bk7jJep_urRBEunSxY,1166 +torch/include/ATen/ops/fft_rfftfreq_native.h,sha256=9nnN8f45kFu6pM55NWPxDldCzPOYqv09rAogJX4e-J4,719 +torch/include/ATen/ops/fft_rfftfreq_ops.h,sha256=s9y4JfMREuLzKZGcJTskffyJfRdBDhckC_7m2sK_Zms,2071 +torch/include/ATen/ops/fft_rfftn.h,sha256=Efe9ZU_gf5_ugo-qyHa1CitM9xuKsS9M9JKqJeMdBfE,5486 +torch/include/ATen/ops/fft_rfftn_compositeimplicitautograd_dispatch.h,sha256=D0sMa7ywf9jkQk5f47LWRG_25mIapDLkdbdoszkNs0A,1936 +torch/include/ATen/ops/fft_rfftn_native.h,sha256=Qg2z3AkBLX49GM5403zFiRv5G6fwzDcEr1DXp1BEKY8,817 +torch/include/ATen/ops/fft_rfftn_ops.h,sha256=9HA9bUDOFJtRMwS4WM49eCJaUO2PzljhpYj5HeSfvBM,2243 +torch/include/ATen/ops/fill.h,sha256=D7fK0AhqIbv73CzN21TrMjdnJDQz5hxcGKgSWobtDEw,2274 +torch/include/ATen/ops/fill_compositeexplicitautograd_dispatch.h,sha256=eeuep9t-XE5g9z8ucNeM8xG-hMLfQENipkzrpSfpbVU,1276 +torch/include/ATen/ops/fill_cpu_dispatch.h,sha256=OgPpV9sOfxOj71SpumrO9Yf_J8orp491V9q4mKuANlA,816 +torch/include/ATen/ops/fill_cuda_dispatch.h,sha256=GIEBMjK-q5Z3YlI48yA8Sn5WVL_tJiEcS7QfnWgTGLI,818 +torch/include/ATen/ops/fill_diagonal.h,sha256=Dv-q9FiPXQ_A1e0ZP4lhM0w1K191a1lRR80xa28AuK0,510 +torch/include/ATen/ops/fill_diagonal_compositeimplicitautograd_dispatch.h,sha256=IC_PggetcV9GrhMDYpV67jcqVCbgOJUrY3rWlBAvbDA,816 +torch/include/ATen/ops/fill_diagonal_native.h,sha256=3_gNPXIK3SodKhXoLJz6CGixNfmjWbDU8k9qKsKBz7o,528 +torch/include/ATen/ops/fill_diagonal_ops.h,sha256=PlOuT4FtIMQeC41XA-MH65K9uRTnE3jnq51p0GBJh-U,1123 +torch/include/ATen/ops/fill_meta_dispatch.h,sha256=PulYSplLtOtLRK9kRr8EoAnppi9xv_1qUW7YmR98Oyg,818 +torch/include/ATen/ops/fill_native.h,sha256=HlvHHpd2suDy1F5t5skspvjESpDWnrvZJ8zTuJr_nGc,1526 +torch/include/ATen/ops/fill_ops.h,sha256=1GHcUM1fjOgCCevn30HuftDJcx7KiaoL1B-0KkZzTUc,4237 +torch/include/ATen/ops/fix.h,sha256=R9wETfL7ByCdja70IqsIH-uW6ePjb32N8tgYzAbpDtU,1130 +torch/include/ATen/ops/fix_compositeimplicitautograd_dispatch.h,sha256=1rAxNUzvAbucElQo95oHWe1wygZVKF-WEyl_Pj12c-Q,960 +torch/include/ATen/ops/fix_native.h,sha256=FyH-3X26dxEa8ItClewcVoiqF946epkS4LUJVtNGs4Q,596 +torch/include/ATen/ops/fix_ops.h,sha256=Rw3BWCCFq7HGC5JrjC2GHuWKJlEZqycQbGg1FPbwSNM,2019 +torch/include/ATen/ops/flatten.h,sha256=cM2RSxAIOk-HL2Rx3Ed-6S8z2eETTOe5cDyajMCf9Ds,1640 +torch/include/ATen/ops/flatten_compositeimplicitautograd_dispatch.h,sha256=rm0Uvy4fOf8eOGuu-oQHmobX2FZOwNqV79yT4vUfVek,1136 +torch/include/ATen/ops/flatten_dense_tensors.h,sha256=Oa5-24xwFHvyveL2xNw66RW7M-LgkXT5maRqF0PxELY,704 +torch/include/ATen/ops/flatten_dense_tensors_compositeimplicitautograd_dispatch.h,sha256=0lIlNllTecb53AhiJcPd97M7aH8sir5Y4rIEYtfZs7I,778 +torch/include/ATen/ops/flatten_dense_tensors_native.h,sha256=ER_0Geo91mwElASTAxhnl9TZ27iNiAlLvuNQ3X0viUY,490 +torch/include/ATen/ops/flatten_dense_tensors_ops.h,sha256=vpUmpXxlzKsZ5Ksq0wg6KBchRO5iIiVvwUyy3c_yDoQ,1001 +torch/include/ATen/ops/flatten_native.h,sha256=r7wFPkRZHUVLZMP02HZWHctccl5Jsku_XXG8f-CccmY,848 +torch/include/ATen/ops/flatten_ops.h,sha256=Ze4EenhvddT6k9NQsxjb3lyP1T9TLiJAgoAB4tGI5s4,3320 +torch/include/ATen/ops/flip.h,sha256=ffVjz6BTtCqGiGRbOuLGc5OQYQCGPwIm913BrAzGnrc,1127 +torch/include/ATen/ops/flip_compositeexplicitautograd_dispatch.h,sha256=-r_AVB4zpyiBpa71S-Hbhx4tKU98SysKQpsHszhozP0,907 +torch/include/ATen/ops/flip_cpu_dispatch.h,sha256=lL16AEyy7O39BJk2Gq0UlEvRDYU97F-RfJRMQc0upa8,740 +torch/include/ATen/ops/flip_cuda_dispatch.h,sha256=WhurYoQcJKvC2LwKjm7EpRRo3WxRGSd2K7s8um6BNLc,742 +torch/include/ATen/ops/flip_native.h,sha256=xoeiZxndc287FqeHzmFVQzPI0kxLWILvhV7JX6TJU2o,594 +torch/include/ATen/ops/flip_ops.h,sha256=rTAE9DSEYKvGN5vZRk_8qmj0OmSS663Yw6zTJjHgU2c,1681 +torch/include/ATen/ops/fliplr.h,sha256=e5Q_4xkZ1JHN5QAnr1hgRxcaSQRrrroEG3OZj0rg6tQ,637 +torch/include/ATen/ops/fliplr_compositeimplicitautograd_dispatch.h,sha256=9XV_gwYUMMmToJ3jz7Xt17O1Rq9U_ms2846qAHuoPOQ,764 +torch/include/ATen/ops/fliplr_native.h,sha256=s-n8tR6ML_M_Uv9CmKKpgMU5tXAU83bssnfNt1GCEnY,476 +torch/include/ATen/ops/fliplr_ops.h,sha256=mlaDCjMB3CFMZPd_06D4_ShMkfx7li25VC135-Eqx6M,957 +torch/include/ATen/ops/flipud.h,sha256=7rExDK5GxH8VdO7WF4-RXQ-h6DdaDY_KFi75GPpkzJ4,637 +torch/include/ATen/ops/flipud_compositeimplicitautograd_dispatch.h,sha256=mbHibeplISZiLHUH_R0-CtytsxvIvnb4VoU4Lw1w7c0,764 +torch/include/ATen/ops/flipud_native.h,sha256=J18-dEhEu3-OSHDiUY7n_hhHpYMLDYKFYmehMeif0Lc,476 +torch/include/ATen/ops/flipud_ops.h,sha256=VsTbYr6DyKFiCqUDgY_WzlQuf-DidxStggD-e4dB7fo,957 +torch/include/ATen/ops/float_power.h,sha256=0jV-w5Gr72nKcJx4BXJYUeUcN8BlrwfDrr1OEbNDDrs,2931 +torch/include/ATen/ops/float_power_compositeimplicitautograd_dispatch.h,sha256=xMb7SNBUrwOKBn-DJ28syR1Vq7tEon-BaC-QtFAJO-g,1819 +torch/include/ATen/ops/float_power_native.h,sha256=JY-Htam-2YRyWWbMCU1Na8k1psFdISFoDisi9FLOR-w,1192 +torch/include/ATen/ops/float_power_ops.h,sha256=Pmvffogf8q2zYgL3T9cHGAnNJJ6VVrstfJK6BET0-hY,5852 +torch/include/ATen/ops/floor.h,sha256=v37wDtWTUFl-CiPnuVf3DHMyOofhog0Pwas-nETQslc,1156 +torch/include/ATen/ops/floor_compositeexplicitautogradnonfunctional_dispatch.h,sha256=FSIm3qQRi1-5baZ2ID70SgUY0jQLlg2n2Iuz0erAj8k,839 +torch/include/ATen/ops/floor_cpu_dispatch.h,sha256=1HJEpSrpqg8gZcfcCCNZVhMiHclY0Zd5ihTTmGW6l6s,924 +torch/include/ATen/ops/floor_cuda_dispatch.h,sha256=ra0_yJwg-XznM-CY2zHiClZO4rosku8k3tZt62VEqh4,926 +torch/include/ATen/ops/floor_divide.h,sha256=7Fyy892rc3Evg2GYFUMPqEeF65S9ugZQChiMhKKCBG0,1990 +torch/include/ATen/ops/floor_divide_compositeexplicitautograd_dispatch.h,sha256=v4JOo2ZeNw04rzype5BbY67n3Y4dVqvrlBUK8ZQLCDE,1100 +torch/include/ATen/ops/floor_divide_cpu_dispatch.h,sha256=Z3r0UdfFs-NFum3XuxIZ57rJCzG9LSPwJkzmla3p9tY,1056 +torch/include/ATen/ops/floor_divide_cuda_dispatch.h,sha256=4QOyX8Rh04IPkn6gYnB2fiTA_6OFMkjQ6XGkEBYXoe0,1058 +torch/include/ATen/ops/floor_divide_meta_dispatch.h,sha256=QilrJ8wT4vLGLraRxa7tU_j8fHqFZklIW2_3Vpa9u98,751 +torch/include/ATen/ops/floor_divide_native.h,sha256=A-zhUY5NghoKf8WdarWdfYRTNAkHVkm8q5bQxBQ5Hdo,1295 +torch/include/ATen/ops/floor_divide_ops.h,sha256=3VDF2ZD-_F3OnBfGFeq3C9exwTQx1UKu2W9AspLBX58,4340 +torch/include/ATen/ops/floor_meta.h,sha256=dtnLDpV2rMBKQs7wVuJSCqGWPGfA5dmNgH6y2JWPzDs,566 +torch/include/ATen/ops/floor_meta_dispatch.h,sha256=Q0VTkuZDFb8pqAhmn8SrBhdf9wNGXhRyilLcb7nsZzo,926 +torch/include/ATen/ops/floor_native.h,sha256=aWVbxdZJmp9OTbLJB9yfKOab3mjH1-jibd2-7OJE1UI,1007 +torch/include/ATen/ops/floor_ops.h,sha256=hPwJ7WY5Xh0KYJmNw9ZVeavW4hKAZo4a_ItWcmJRjEA,2037 +torch/include/ATen/ops/fmax.h,sha256=xjlkQhIPv31Osk-eFD5dSiV1ItGS_RVOLGcoH0QxHT4,1148 +torch/include/ATen/ops/fmax_compositeexplicitautogradnonfunctional_dispatch.h,sha256=rYm_4zVW5tkQ6Z4ASegs8oV0rPCn9UmldA4B_EQpPDI,814 +torch/include/ATen/ops/fmax_cpu_dispatch.h,sha256=xC9MR5ByoXgZ9Od6ubz4U2UknfEudbmvsL_kYAgtKmY,949 +torch/include/ATen/ops/fmax_cuda_dispatch.h,sha256=Aqz3snnSr8Yc1S67AXDpNwR-bHePeaXWgH6tEHOV_Hk,951 +torch/include/ATen/ops/fmax_meta.h,sha256=Y8hBLTUBxF_xWTO6Q1R-OOBGILuYoJURJpCdmkCspjA,591 +torch/include/ATen/ops/fmax_meta_dispatch.h,sha256=DAKrhaUAbCZkXv5I4QfIZny8ccrC6ZR5EjPGgz1Ft1o,951 +torch/include/ATen/ops/fmax_native.h,sha256=NCkIhAgZx3w_VRIuKrdZfv75mh6Xmdo0J0Yct710_5Y,616 +torch/include/ATen/ops/fmax_ops.h,sha256=JZk0IixJdNRkhv1TBSEO4TDeTf-QFsvt8wegwo8QgiU,1707 +torch/include/ATen/ops/fmin.h,sha256=OwKRdtgcr9rR-WRjESRR4BF25JOnic_iyhzV2nThbWQ,1148 +torch/include/ATen/ops/fmin_compositeexplicitautogradnonfunctional_dispatch.h,sha256=tghe8Z72h6NhcS1me_RIjXKsmXBh3xa4auY1hON-pLM,814 +torch/include/ATen/ops/fmin_cpu_dispatch.h,sha256=guGGZ-3PCXFQGd8jZ2P0giHgurCwJSWt6A1IhExwtYQ,949 +torch/include/ATen/ops/fmin_cuda_dispatch.h,sha256=Sbk0kGzsBHYoI3fprtA29pCk3H4-bA9CfFSFPBhErlk,951 +torch/include/ATen/ops/fmin_meta.h,sha256=FgzY42GgedU22mELR9BtSTq0EIarKgV0NZgXquDgFnM,591 +torch/include/ATen/ops/fmin_meta_dispatch.h,sha256=G0x71CFiMKa5IQL_XM0aUvTvchZ33rpbqP5GUVbk-Ws,951 +torch/include/ATen/ops/fmin_native.h,sha256=WmNef6bmOK21QceoERKSp0ENhk3lr5H2iBwErT22erk,616 +torch/include/ATen/ops/fmin_ops.h,sha256=_OqKaIldwGstcZ2QUD6Cbh8jLhESswAounw_6g9DgL0,1707 +torch/include/ATen/ops/fmod.h,sha256=uQ4hw0oL8fipprOI88uGu-CZTYd1bz2yRpenTlr4C4w,1880 +torch/include/ATen/ops/fmod_compositeexplicitautograd_dispatch.h,sha256=251IW6ZRK6qalybZGy_y90lhXwb0S9bM-7ftj3eU_-s,1068 +torch/include/ATen/ops/fmod_compositeexplicitautogradnonfunctional_dispatch.h,sha256=5F5_7nBrp4Uz4mC5vP3MU7of7dy7rr0ZQWpJinlSHVI,889 +torch/include/ATen/ops/fmod_cpu_dispatch.h,sha256=pJsiXx87F2cIOTeJXt52oXY3Fst3qiPrwcs5dHUKYvw,1024 +torch/include/ATen/ops/fmod_cuda_dispatch.h,sha256=E8aPSaegD2ja82Mani5LuyyjBuFdtv23eb9xN9cIu_A,1026 +torch/include/ATen/ops/fmod_meta.h,sha256=Jed5kreBbsSFLa68Qs0eCd3CPtqVw2DUnuAxZa9tF6A,598 +torch/include/ATen/ops/fmod_meta_dispatch.h,sha256=4kze2hOScbx16l4wxC4VIjGlH9wsDVGIIzquxpzlrP0,1026 +torch/include/ATen/ops/fmod_native.h,sha256=VK2JR5mea2r8PZsFnilIoGkxg7IysG0biHSo8O41pPM,878 +torch/include/ATen/ops/fmod_ops.h,sha256=EV2JkwBVnR-NNz28mrrQbfmDB-2wlDoEPRMTxgs_m-Y,4237 +torch/include/ATen/ops/frac.h,sha256=U3yxxT77hdm2UbSA4kA1NgHD2VGXnAhTKqg0s8-_GSo,1143 +torch/include/ATen/ops/frac_compositeexplicitautogradnonfunctional_dispatch.h,sha256=W2V1FuAyQb8zcEn4avkmF3ud0GpTANgaosSqk8cio3Y,837 +torch/include/ATen/ops/frac_cpu_dispatch.h,sha256=gNULicNZORnxMkYnnGxYwWLv1rrk61j5eFx1bn171MQ,920 +torch/include/ATen/ops/frac_cuda_dispatch.h,sha256=19Mv27gfEZmjJfOyGx7Kx01XrKVwHGjcEfK0jUCbAWc,922 +torch/include/ATen/ops/frac_meta.h,sha256=i6hx61lcdSvGi1gdx_06zCVDymeZ52BvnLt2S4-0csY,565 +torch/include/ATen/ops/frac_meta_dispatch.h,sha256=qqFSiVycZ_nbQw2XZ1o01fXIVG-JvTS39K7YJN197fo,922 +torch/include/ATen/ops/frac_native.h,sha256=kwjdxujo_Qt4l75tp_irpIY_Blyqvehu98N2UpfanP8,998 +torch/include/ATen/ops/frac_ops.h,sha256=DnIex8CVlDGtA47tBZSfyPIqy63bATGHNXoyQTUg_lg,2028 +torch/include/ATen/ops/fractional_max_pool2d.h,sha256=WVO4Wkdm2S29OYJIY0rssf3b4WgZxsKK5WOIUbAFb9Q,2020 +torch/include/ATen/ops/fractional_max_pool2d_backward.h,sha256=Yyaiw7VNl5Mg4Pe3WoOPOohKzjxaZq5j-y4q4lhdnVw,2063 +torch/include/ATen/ops/fractional_max_pool2d_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=D7Fq_HvtOmaZNO-JmS8zMAaKmGTg-nhPE_QATnmy4BY,932 +torch/include/ATen/ops/fractional_max_pool2d_backward_cpu_dispatch.h,sha256=F8V1UiuYn8eLLp8-Z4VqUXkgy7ZP7fhNUKK3iDijiDg,1317 +torch/include/ATen/ops/fractional_max_pool2d_backward_cuda_dispatch.h,sha256=z4iHw57ZlWA8l44p9B0z2g2Msyofddy2TOW5mf3NnDo,1319 +torch/include/ATen/ops/fractional_max_pool2d_backward_meta.h,sha256=q9jCgKz2f7pzDg8xfbv-5Om6jQCrLnyXBOGTAVL0RXo,709 +torch/include/ATen/ops/fractional_max_pool2d_backward_meta_dispatch.h,sha256=Mx_5XZRVJ1m5StO2p5rVK9D_zyprPb2OC8m2qlr6428,1319 +torch/include/ATen/ops/fractional_max_pool2d_backward_native.h,sha256=DZ2vs6jOReTZF3bcJEA0ene1UWUHsJTAWxx08BhPnWY,1108 +torch/include/ATen/ops/fractional_max_pool2d_backward_ops.h,sha256=NFJXTzAEVQFnEgJCXFMU7e-6SLguwMtWn3DAJwMqe0Y,2505 +torch/include/ATen/ops/fractional_max_pool2d_compositeexplicitautogradnonfunctional_dispatch.h,sha256=iRQhoaCx7-QyzYW6Lv93EwBwVEDmqxNEd33AEWlCe9s,923 +torch/include/ATen/ops/fractional_max_pool2d_cpu_dispatch.h,sha256=YPDvgDmFvmY7yrfo7zqELDAWB5j5rnlXisfSn2pNj4E,1330 +torch/include/ATen/ops/fractional_max_pool2d_cuda_dispatch.h,sha256=lDcDU4Bj8JkJF8aIYbYMWR8koVjxWVulMtlWFaEMxX4,1332 +torch/include/ATen/ops/fractional_max_pool2d_meta.h,sha256=kqpi4tkBAfNOlk_Tallq4dfQBSHt6EPfC3KXlgAZ01Q,675 +torch/include/ATen/ops/fractional_max_pool2d_meta_dispatch.h,sha256=958iJSudzKBpARsyJZ2Sdpiv3ckBa-cwGu39qcMyxFU,1332 +torch/include/ATen/ops/fractional_max_pool2d_native.h,sha256=ie05or9YrbJeVrF8cmNL6E-dpH9AEYYS4ity-wYMscM,1069 +torch/include/ATen/ops/fractional_max_pool2d_ops.h,sha256=qpU2RhEGnXvvFLODpCeRTE5m-9yfVfyeF5-Wa2CS_zY,2519 +torch/include/ATen/ops/fractional_max_pool3d.h,sha256=I6kiT_jZ7YEkIUhALr_qB3DgCxxd4ow0KVJutvKxD8U,2020 +torch/include/ATen/ops/fractional_max_pool3d_backward.h,sha256=gd6LHuCtWUObgB_bjxju9eAfOI6w3y0MbcLN8vnVkdk,2063 +torch/include/ATen/ops/fractional_max_pool3d_backward_cpu_dispatch.h,sha256=DzmYqWlyLGQ56bt6q1LB0gu7UUVOM4I6_fMT5ZxZjqw,1317 +torch/include/ATen/ops/fractional_max_pool3d_backward_cuda_dispatch.h,sha256=YWehyENpjOCpl53WCN_LQ2tefhMdNYnCkqnpL9kXFyg,1319 +torch/include/ATen/ops/fractional_max_pool3d_backward_native.h,sha256=GvWZnZiTLbcgbAJlSSV8Bv2UlgC9i40TnZ8W4_uDSFM,1286 +torch/include/ATen/ops/fractional_max_pool3d_backward_ops.h,sha256=m6DmBy4E9qNh8iwWHh9RkwCrFTX4OX1LTx_XWfRQZ5w,2505 +torch/include/ATen/ops/fractional_max_pool3d_compositeexplicitautogradnonfunctional_dispatch.h,sha256=Df3mXbR3ZSkyBn_po-aw0S_klTBtq6W6cL7XuQsjO10,923 +torch/include/ATen/ops/fractional_max_pool3d_cpu_dispatch.h,sha256=5XSp9ULXOMzUcXdZhognO_kylqK4rJ7uqLqoO0JTrgA,1330 +torch/include/ATen/ops/fractional_max_pool3d_cuda_dispatch.h,sha256=XF2IigJH3aePEw4MLmQvrel4zghnJc52O-F5lZZxi-E,1332 +torch/include/ATen/ops/fractional_max_pool3d_meta.h,sha256=QKeATRpGUKty5rmHtFF1-d0Nb5xjiR4P6waMQIV-QQw,9607 +torch/include/ATen/ops/fractional_max_pool3d_meta_dispatch.h,sha256=_AlLnae2SvCij3j5DPZQ0a3_GuxGh_BtKrr4bVBtaxs,1332 +torch/include/ATen/ops/fractional_max_pool3d_native.h,sha256=kRaD_7o0QQqMg5YGUgwnZSK69T3p-4QmWX-5fO-wUug,1339 +torch/include/ATen/ops/fractional_max_pool3d_ops.h,sha256=fdQgE0vcoJSN32kWeXk6oD8CnjAOMVuXDa_7ipf7pds,2519 +torch/include/ATen/ops/frexp.h,sha256=zjNKzluOb41uUY9B8lKcAm2agwEHsLvgrg5xLxBXiRA,1368 +torch/include/ATen/ops/frexp_compositeexplicitautograd_dispatch.h,sha256=C5T-EyKRo4gmsuvd-iuV2vgx9Ja-Y_aE_Csd5VE5m4k,788 +torch/include/ATen/ops/frexp_cpu_dispatch.h,sha256=AgQXoTT3yVNRYAFBAT62HBou_K9BHfutH_1lVLkGvmk,931 +torch/include/ATen/ops/frexp_cuda_dispatch.h,sha256=8St131-MWHi5P3PMunUH2wXjpgCEbcawIzPduqZvQYs,933 +torch/include/ATen/ops/frexp_native.h,sha256=rOmhgqbvbCK5jjPpeWDX5a1FqBy_zPUmHRVisk2Mj5c,632 +torch/include/ATen/ops/frexp_ops.h,sha256=MwXizPvVb7ntkkc8TY2GU0RfV6_jYMsisBpK8rtY02I,1894 +torch/include/ATen/ops/frobenius_norm.h,sha256=Mm_IDAUgWCrpm7N-Y-sGeDjjYi-kcPSLRp-aiuDgzOk,1370 +torch/include/ATen/ops/frobenius_norm_compositeimplicitautograd_dispatch.h,sha256=Vplafky-TGtQHN_wEUvSKvkm-kTjOI8s7J07QLQHaDQ,1062 +torch/include/ATen/ops/frobenius_norm_native.h,sha256=upzPZmLV_3W8l8Y1Ol05CfgjYbSyvKnY7aqLiGCbB9Q,646 +torch/include/ATen/ops/frobenius_norm_ops.h,sha256=EU-ppXfnipA90yS8rkfWPjvD_F0QvLpeHcYMcCU7AyI,1856 +torch/include/ATen/ops/from_blob.h,sha256=vqG2laZSL6_gDQ5pfyS5dYndCp6fuANp7EXO6YxzDVo,4163 +torch/include/ATen/ops/from_file.h,sha256=q-9XzzEZg6X46rh3OfbrtyZf2SNxatHw0gcPxF6zBdA,2208 +torch/include/ATen/ops/from_file_compositeexplicitautograd_dispatch.h,sha256=RBl2aF8FqKZyQj5nMICd2S6_hVsTtcTZ3ak2EcnfUr0,1016 +torch/include/ATen/ops/from_file_cpu_dispatch.h,sha256=YRM2fuVveXR2HDnFF0OLYmGcC0qvKbePekNJBXyvwNY,1098 +torch/include/ATen/ops/from_file_native.h,sha256=D0GJoGwSIoyfHn9_OiWHu0eGY1TNd6fLSt9HNRGHSCc,860 +torch/include/ATen/ops/from_file_ops.h,sha256=6xJM-jMM8kZDZhrlTWGSEYy0oVVeqVHZZLzIl6-qd8w,2473 +torch/include/ATen/ops/full.h,sha256=NLS0V_uJeZwGosSdJVJUZS9AdND5hH938PO7QrpcLiE,7917 +torch/include/ATen/ops/full_compositeexplicitautograd_dispatch.h,sha256=fKR5xDo5VwK3HAR2hqvej_TBuobBV_RzxQdrMiwLVBc,2546 +torch/include/ATen/ops/full_like.h,sha256=hipV1uColaKxnYY--BBkYnvBCtqioGOSa8HaLziqimc,2456 +torch/include/ATen/ops/full_like_compositeexplicitautograd_dispatch.h,sha256=yGw24MrMiaK7Ei490idnvc3vdiTh4x_u3ow9shSrrW4,1512 +torch/include/ATen/ops/full_like_native.h,sha256=kuVypWwUkAFeJ9j7DQUU-nGGRprUS5UdapuRVHuPd48,892 +torch/include/ATen/ops/full_like_ops.h,sha256=eTNANTj1lO4g4YQ_Eo-Ce8coCQkaq8hmOHPTf2Zy23A,2597 +torch/include/ATen/ops/full_native.h,sha256=ZbPDRmCFRJZOuDxcT_GvFnhuDJ5nyQ0QB2MYS2W4Gys,1190 +torch/include/ATen/ops/full_ops.h,sha256=EVwB-6ghXBm8ikyFXq0Av-0pIkN86Zkr2IOQ02dk9ms,4298 +torch/include/ATen/ops/fused_moving_avg_obs_fake_quant.h,sha256=muPEL1x8ptgAyeCMYzSqRmuTAxTppRgs3DVfnvNcQ5o,1452 +torch/include/ATen/ops/fused_moving_avg_obs_fake_quant_compositeimplicitautograd_dispatch.h,sha256=XFqA-I8sGlLALfDNmDK3A2M13mx729qZA-RwCh95_Lo,1090 +torch/include/ATen/ops/fused_moving_avg_obs_fake_quant_native.h,sha256=AXDi80_YM9CsSUVhnr8Cf_bdgtFAPbq56YLxQ7g2GOI,802 +torch/include/ATen/ops/fused_moving_avg_obs_fake_quant_ops.h,sha256=ZdGTtFVU-Rd5AxWQZ9Kaz6IFKDT1C355NvYy_nO_eEQ,2009 +torch/include/ATen/ops/gather.h,sha256=LN7HSsA_Pd_WOKXdRtuch7umNt-1w512jrzUQRkH4No,2428 +torch/include/ATen/ops/gather_backward.h,sha256=BE179f_oJ0aQWkoL_RmYXYoSyyYFoF8LtzO4b4lD9tw,840 +torch/include/ATen/ops/gather_backward_compositeimplicitautograd_dispatch.h,sha256=RgBHAj_gl1r5mQeI7NFnyIVpwmngidmOrXQVqm9dTxM,855 +torch/include/ATen/ops/gather_backward_native.h,sha256=4Txo1OeZkpNW6ib3wkjV2RyM4Rt-jylwL1xuE4M9Vs0,567 +torch/include/ATen/ops/gather_backward_ops.h,sha256=xPC80ViHEzQtfIvdwYnEJyGCAB7N0WstNZX3aNNwXHc,1257 +torch/include/ATen/ops/gather_compositeexplicitautogradnonfunctional_dispatch.h,sha256=-ErXazNAujTIXpCCEuJwv92j1FwaEo1Inge9rqAP5O8,853 +torch/include/ATen/ops/gather_compositeimplicitautograd_dispatch.h,sha256=3xQOOuztKR3h5rAEFkB2__OoIHwIsEQWU24eAwOhoS4,1116 +torch/include/ATen/ops/gather_cpu_dispatch.h,sha256=iktZfREucwqpg7QzlrKHc3qnrccMdxt9Vz7lU_X90Ao,1060 +torch/include/ATen/ops/gather_cuda_dispatch.h,sha256=8ZAxgji8Dnl1NvdRxbs_HZnwFrkAG6LuZc4Qs1nrWD4,1062 +torch/include/ATen/ops/gather_meta.h,sha256=u7AIqhMwuJJ-KjVf7pN356OTJszu9d7rGUj17uxH0qc,624 +torch/include/ATen/ops/gather_meta_dispatch.h,sha256=UrzpQw1xHVUL8caKQVTHH1RB25UTWqhFipEDQ3_kqKQ,1062 +torch/include/ATen/ops/gather_native.h,sha256=lj9UeeAWq3O9TRGX1tLtkGJqbXXqsQMjIl3xyqmcsUE,913 +torch/include/ATen/ops/gather_ops.h,sha256=P_haOG6EyoBdv45vEHlIOIay567Px5rRmF_Q5FKNnuo,3506 +torch/include/ATen/ops/gcd.h,sha256=KkrWqcomYk_ed_s5cxRdTn-Mf18GtOwdPvYCTuKy6vQ,1318 +torch/include/ATen/ops/gcd_compositeexplicitautogradnonfunctional_dispatch.h,sha256=oYwr05ANpnT363tYor-dPh5OFXFoUrSiDAV91MAtGIA,887 +torch/include/ATen/ops/gcd_cpu_dispatch.h,sha256=l4YAY3lA9xO4eVQeROYHO8xm4Z6hnzi38B6ij_lj-tU,1020 +torch/include/ATen/ops/gcd_cuda_dispatch.h,sha256=zjshszA5noT6rIeVL_-kYsFT02KiQbBIfJ4nMykq6c8,1022 +torch/include/ATen/ops/gcd_meta.h,sha256=6bNUk7S-M_fIqt9mWRUzL4uzzxw42sCsNIfbVa3rt58,590 +torch/include/ATen/ops/gcd_meta_dispatch.h,sha256=9kvofJJIPymWLcuVdyQTph15kr7Xcf0EBojR-aivgCg,1022 +torch/include/ATen/ops/gcd_native.h,sha256=msHMq6iFf4lxJB2IAsOfGBogQfY3bmrIZUURByjywtY,613 +torch/include/ATen/ops/gcd_ops.h,sha256=ZoIV5ie84ovGNPQsK7rg2Cqur5aLG1rBdeopqPlCw_k,2277 +torch/include/ATen/ops/ge.h,sha256=6rWxAnaz2nRBAU9_Mbkn02RyUiXeoh9kwENddcw5ZZc,1842 +torch/include/ATen/ops/ge_compositeexplicitautogradnonfunctional_dispatch.h,sha256=cjmE8YjUbKJr2qoetaOOZa-m5kl9obt-pNmJdmaIm28,1034 +torch/include/ATen/ops/ge_cpu_dispatch.h,sha256=tRMpQFauiFzmEMU_eyMXuxGSyQGdwmcd_ciWApQZMnQ,1366 +torch/include/ATen/ops/ge_cuda_dispatch.h,sha256=WpJp8XvQMVCmi0C-V_gLxRGkKfFfqW4ff8CAJkc19Ok,1368 +torch/include/ATen/ops/ge_meta.h,sha256=7236yfXKeqfLBt3tnTv45upnYd4mRVYjSGR5w93ljNQ,735 +torch/include/ATen/ops/ge_meta_dispatch.h,sha256=CvQK0S14D2zdUH7i4-fsQiOrIYFUcG2Hs6xrjzfDSLs,1368 +torch/include/ATen/ops/ge_native.h,sha256=vYfnw_FjCFuPRXyGtG_97Q63V_3-ceYat3zT6DBgIjA,1295 +torch/include/ATen/ops/ge_ops.h,sha256=Z3NUsDF-doZG-NrdH7GP0PW7d2cJIHn_rw0v_C08g6A,4201 +torch/include/ATen/ops/gelu.h,sha256=m45i6EsQKW3pizQqYfhikLSYlQ5dEUHsVlIYv79cU-Y,1438 +torch/include/ATen/ops/gelu_backward.h,sha256=TtN8SicUMdG1Ki7VEIse-rsLtzwxx9qd9wjsfBAbKcc,1580 +torch/include/ATen/ops/gelu_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=LidVJsBV89MRQr3-axMjIZsLSlqtgRZAHntThFq8Q3g,866 +torch/include/ATen/ops/gelu_backward_cpu_dispatch.h,sha256=s3vhJTpyR2vxSRSiTK_Rm7UP9PFMP0P_6_OBimi3WW0,1112 +torch/include/ATen/ops/gelu_backward_cuda_dispatch.h,sha256=k_NUWbnYLchjNuqtoG2Sfp3nQztbT7vc3SosGypmcG0,1114 +torch/include/ATen/ops/gelu_backward_meta.h,sha256=thgsxcssYzNtutkgFUGhPc-3FpjCkw3mi0eCM_GfYc8,636 +torch/include/ATen/ops/gelu_backward_meta_dispatch.h,sha256=85Piox3vP6LC_9ZdrLu9ZNBC77VqyBE2gYi3IQ8Z7E4,1114 +torch/include/ATen/ops/gelu_backward_native.h,sha256=BZTMXNh2khzOksFrK8RD11OLZahkZkmMwjCFJs4fPZ8,1194 +torch/include/ATen/ops/gelu_backward_ops.h,sha256=JtLcHvu1vydXTsg7n7Nz5H5vZJao5KTKCP940vqGFwQ,2046 +torch/include/ATen/ops/gelu_compositeexplicitautogradnonfunctional_dispatch.h,sha256=eLvTVbIYed1-qTh8AdWPZ96wGZRm-sWy3zmc_BRBx0c,911 +torch/include/ATen/ops/gelu_cpu_dispatch.h,sha256=FZXTnR5XNKvMoV-RDTWezdASU2cKU2z4w7DEhy8S9qQ,1061 +torch/include/ATen/ops/gelu_cuda_dispatch.h,sha256=AZmXiQ0RjUhtnv_KIoDA1DKqh8kEy3BOCblP73l09_c,1063 +torch/include/ATen/ops/gelu_meta.h,sha256=czz9QVHn9uxppFJAN9SSV7y0ugCmcyyS27RuTqbQd2A,595 +torch/include/ATen/ops/gelu_meta_dispatch.h,sha256=JFOhpdjAQkT_e7FEnHbujceWux7UHLHPuMA8H3ue83A,1063 +torch/include/ATen/ops/gelu_native.h,sha256=2W9ZAeiS5vkIx0lNQgIFATwEaBREfKT4IrhuJDT1UVw,1400 +torch/include/ATen/ops/gelu_ops.h,sha256=uLI3RMnbI5SIls-7ubHkclTKhAe9U17okBrRFPGX9Hg,2340 +torch/include/ATen/ops/geometric.h,sha256=SK91VN_9gv_7-l5oe8RffvZAqZPI1YLpdzUh2hoAjuM,1396 +torch/include/ATen/ops/geometric_compositeexplicitautograd_dispatch.h,sha256=TUd0uqBKo9tR2-7IS94TsbZ_h-vIXJ0kYU4safr4hBk,1116 +torch/include/ATen/ops/geometric_cpu_dispatch.h,sha256=KlFg1l-OJ4GMzbz-KE6o4m3TmAoY9nrW2P4GTBXxGx4,787 +torch/include/ATen/ops/geometric_cuda_dispatch.h,sha256=ADqP9Mo98wuZgxm4Ta7UjmqsOc66Bm-E5CY3hTNX6MU,789 +torch/include/ATen/ops/geometric_meta_dispatch.h,sha256=wnReH5HldE1hdugv9ZgAEdIeJnFwM8bAUBkmXfnwyUI,789 +torch/include/ATen/ops/geometric_native.h,sha256=1VhkRzs9szXZwO46cEpoAOuwEdW1W6HPhyyqsELkkR4,800 +torch/include/ATen/ops/geometric_ops.h,sha256=yQkXo-4oZynPEmZzQlgzbrrf_yX6IhGjmd09aoVkUyw,2619 +torch/include/ATen/ops/geqrf.h,sha256=RoQXz_kLL8XLkLQiuvjFdSjQVbUn-130y_ixSzCph28,1210 +torch/include/ATen/ops/geqrf_cpu_dispatch.h,sha256=MnTJuO9R1jrWbFLy79DU4MIAXQXgFvmr8cSHQWsoNkQ,985 +torch/include/ATen/ops/geqrf_cuda_dispatch.h,sha256=WQip14-Uo9guM7oBv7vdJsptgdJ1nbf99KMgo07ItYU,987 +torch/include/ATen/ops/geqrf_native.h,sha256=B5TEMLxI5qcV36gwxJgS3ZCEqF4kORw21ojl0yTRfKU,620 +torch/include/ATen/ops/geqrf_ops.h,sha256=Ilr46JdZfg8S1CmgMG91N5O2UIDgsAP4yfr0Xv9mBmY,1787 +torch/include/ATen/ops/ger.h,sha256=XC-1Hv0RGy5rNHPcliESbX8J6tISUpZdtWDPMNz9XMs,1129 +torch/include/ATen/ops/ger_compositeimplicitautograd_dispatch.h,sha256=y2TnESZk-XQq0Puu_XfqIxCW8LQ8T0yZor9bL-vYC3E,987 +torch/include/ATen/ops/ger_native.h,sha256=oD07x4gMEW7ApiOl_ytYCc_mcHF1PPlkY4j5ma08ySY,598 +torch/include/ATen/ops/ger_ops.h,sha256=QehaY4IxBuzQw9MkQqcH_4nkTwiaX7e5y6Bgpbmp9yo,1695 +torch/include/ATen/ops/glu.h,sha256=PAgkEmk4PAX7REfyNprzANe0CImQWuMguF25iY1ULAc,1093 +torch/include/ATen/ops/glu_backward.h,sha256=bBzTnGMTC7znB8WrB2f75IfTZFzWiIoECvP14tSGvsc,1433 +torch/include/ATen/ops/glu_backward_cpu_dispatch.h,sha256=lQ4iPRYT1IvRyy_f06PnowzyH8oqxKhgyBPFO-AWGjo,1044 +torch/include/ATen/ops/glu_backward_cuda_dispatch.h,sha256=ElFssOj8pVG168yy0E8uiyTUFDWI9hGuWIk9koiH1AE,1046 +torch/include/ATen/ops/glu_backward_jvp.h,sha256=a2iCCTsHJvbZ1BgktskC8S5nxrc_EiuA24-TtmHRP7s,1790 +torch/include/ATen/ops/glu_backward_jvp_compositeexplicitautograd_dispatch.h,sha256=YarW-uwH0b0Ejr6MYuGMQVRgvg92VO0Y2cuFeCr-oWE,1125 +torch/include/ATen/ops/glu_backward_jvp_cpu_dispatch.h,sha256=Uo4n8sIPyTkP1BABdijnJXlCIkixZlS90aeVg9lYGyE,849 +torch/include/ATen/ops/glu_backward_jvp_cuda_dispatch.h,sha256=kTS7w4HjDPgtHfCRyPae5Bas41-DCaQ40XQsYt2k5Io,851 +torch/include/ATen/ops/glu_backward_jvp_native.h,sha256=zsD6LiHsWJm7KLClxsiL6jvJ4_4AoPj8yDh6T9MTZrI,812 +torch/include/ATen/ops/glu_backward_jvp_ops.h,sha256=kcHY6C4MlXGKKUktLwck7-KqT3Nhojdgi1V30QfV8os,2395 +torch/include/ATen/ops/glu_backward_native.h,sha256=QgBs6rrw1QsDgv83OzhXOlPt9_w7KEXhTvmdNVELmxg,922 +torch/include/ATen/ops/glu_backward_ops.h,sha256=VXWgs0XjdeFGbHyujU4p9zn2UQxuR3AgOikDfClgNBg,1921 +torch/include/ATen/ops/glu_compositeexplicitautogradnonfunctional_dispatch.h,sha256=uYoGBVMBtBeSUbmPmOaInltz-sUUTCkVJAOK0u56tC0,803 +torch/include/ATen/ops/glu_cpu_dispatch.h,sha256=crP-5fbYrSPl3-M_poyyfcflvxmAHKYvAv21WZoDSLc,913 +torch/include/ATen/ops/glu_cuda_dispatch.h,sha256=l1TPcyaWIMpbNf8mPbeATW_FComn246sWixL6Fal-YE,915 +torch/include/ATen/ops/glu_jvp.h,sha256=-J-emE37ZmAMOlcL_RipLnQ7-xbAApSjtrlaad-8KaA,1328 +torch/include/ATen/ops/glu_jvp_compositeexplicitautograd_dispatch.h,sha256=k_lw9-e2cU3fU2gtT7T6Ks1ICKswtwxGlnAd0fRCdLc,983 +torch/include/ATen/ops/glu_jvp_cpu_dispatch.h,sha256=CNgzSjRKNFmB-qot4tuHWPzgzv-NPDmHh5EncwoQCmY,778 +torch/include/ATen/ops/glu_jvp_cuda_dispatch.h,sha256=E3eGmMAoXJBHHQM9R6O-i5mJxfvTmVsFQ2yRQobwv-g,780 +torch/include/ATen/ops/glu_jvp_native.h,sha256=mCpTqNDWGqJihabQdb6w7T4Rah9u_aW2EYCfL7MbqHI,670 +torch/include/ATen/ops/glu_jvp_ops.h,sha256=X5pBZzRqw_DYTU1pvcqjpoFQgqW9GgA-RSlTMctEEao,1937 +torch/include/ATen/ops/glu_meta.h,sha256=FK-xn18_s2lc1gaSQBRO-aC7oATXzw76v3knpvZmkF8,577 +torch/include/ATen/ops/glu_meta_dispatch.h,sha256=Xwf774Ourowh70eqFz9gBQP0VdSoCJ4xAG5OYR1VqOM,915 +torch/include/ATen/ops/glu_native.h,sha256=teUYaznuc2kDmNHFKBc__KZE9z_sZr9Kl-ashZd7O00,600 +torch/include/ATen/ops/glu_ops.h,sha256=vJFg9szuOXoTdS6tGgpXq8ZZe2sO630GHmVXEn5KRnk,1623 +torch/include/ATen/ops/gradient.h,sha256=jXfstGqk6csIIRj8vGgPyFeR4wx9ip3D48tpHw-_7Vw,2872 +torch/include/ATen/ops/gradient_compositeimplicitautograd_dispatch.h,sha256=tLJE7osgAx6rPTQ1JZFfoUFD7Ic_RP7KrFxmBq_uQ9U,1784 +torch/include/ATen/ops/gradient_native.h,sha256=YbW7u7VZXn20CASVAG1cWT_3t8KBeoQBBtEocPBQ_gw,1496 +torch/include/ATen/ops/gradient_ops.h,sha256=hZCTZHSik6FIP1xkNys66q_mSrkxjpgJAshb9rjgmWI,6272 +torch/include/ATen/ops/greater.h,sha256=EKepsp-SgUZAbok4lvfmrB1skTChjhBXVTf-SVnhITw,1937 +torch/include/ATen/ops/greater_compositeimplicitautograd_dispatch.h,sha256=lbKvwzKcV77BSoYL8-6um4kdEKKIRWCAWz7fK4PWVjs,1450 +torch/include/ATen/ops/greater_equal.h,sha256=DBfPEzZYAZdKL9dqcCeDjM2Eb9m4Cqdmk2-pIRoW5ok,2051 +torch/include/ATen/ops/greater_equal_compositeimplicitautograd_dispatch.h,sha256=5PVSe4ZwHrBnH65M_m1MlSlnfQ4tb5uGqajsmHOoQn8,1498 +torch/include/ATen/ops/greater_equal_native.h,sha256=OPZ5-7RCrIasic5GUFiKjE0Cjp7v8h5iXS2FHqTBeoA,986 +torch/include/ATen/ops/greater_equal_ops.h,sha256=jPTpC3rEK0s7Mdlqdt0UvdU1WUEDkG6L8E27Kk-5LPI,4399 +torch/include/ATen/ops/greater_native.h,sha256=xe8_WVQBqtTwCw5rA3m6Qc2ZLp8lVTbNx5uU9K2F0Dk,950 +torch/include/ATen/ops/greater_ops.h,sha256=_4BkWLlE0G0OAvaAAUndoiPshWxaNL67NE_2N1FpqjM,4291 +torch/include/ATen/ops/grid_sampler.h,sha256=vcIR_9rUBIWH0jRnaLrCj_WNqMt0pw3EiRR-PcskeLs,889 +torch/include/ATen/ops/grid_sampler_2d.h,sha256=jYg6Q9fVVdmN0NkfOv7ktkF_bmwboGEFfCUFTcfNHYE,1801 +torch/include/ATen/ops/grid_sampler_2d_backward.h,sha256=UXY-IUCJV_0YvA7-v159M9iAoS-fnF_ICRqNCVMCtH4,2497 +torch/include/ATen/ops/grid_sampler_2d_backward_compositeexplicitautograd_dispatch.h,sha256=WF_mk4Q5ON0IqLSvXq5i8grWGrU1gJt_mFZKGfz_foY,1321 +torch/include/ATen/ops/grid_sampler_2d_backward_cpu_dispatch.h,sha256=rRMAfwtGcudMQ-ggTVDQk3XfT9Arxb01xT2zT3OpvsI,925 +torch/include/ATen/ops/grid_sampler_2d_backward_cuda_dispatch.h,sha256=ie6KxsszaVT2Rl1w38llNRmByVIIV8_jAHNiQxVlqL0,927 +torch/include/ATen/ops/grid_sampler_2d_backward_native.h,sha256=umcfUK0hRyquaNdFvmXF3_XJrgDesb3Ae0KORfpUl6c,1254 +torch/include/ATen/ops/grid_sampler_2d_backward_ops.h,sha256=2rqbYDLuKuVNYQwLzzG5bj6EZfjsXPjcb65UFyKgFi4,2961 +torch/include/ATen/ops/grid_sampler_2d_compositeexplicitautograd_dispatch.h,sha256=DdZ_puSwkm2nGBdv1fUrqB5c3kLMdkfzJTChUl1WNpc,1077 +torch/include/ATen/ops/grid_sampler_2d_cpu_dispatch.h,sha256=2Y4cKMMiJkyxfCNkyuIdVnWRDeoQtmvZ-fdIBKIDZlw,825 +torch/include/ATen/ops/grid_sampler_2d_cuda_dispatch.h,sha256=tnSJZgv6YQ2THedSiB7MDkgF3eh9xBKPDQaSCicxupk,827 +torch/include/ATen/ops/grid_sampler_2d_native.h,sha256=aAIsKlEjAPN0dLtXIe8PXEAlxUcawM1k90V_raHEFoE,932 +torch/include/ATen/ops/grid_sampler_2d_ops.h,sha256=wqTlhpIosRPjqpVvWS4p9OIp2_kTdFcDBQuu2vQJIxE,2225 +torch/include/ATen/ops/grid_sampler_3d.h,sha256=kmaKf_fHhmOxdDLloztaz6aR6RmQNlUsXyW3g-ZbjkA,1801 +torch/include/ATen/ops/grid_sampler_3d_backward.h,sha256=x_pdg9Kw5_ZyJRvHinXbsOdDCBQJpyeeVEGjjjUExLY,2497 +torch/include/ATen/ops/grid_sampler_3d_backward_compositeexplicitautograd_dispatch.h,sha256=ZbI7r9KSyNaoT6U4fM6FjD2x1id-RThaVn02AXYMIz4,1321 +torch/include/ATen/ops/grid_sampler_3d_backward_cpu_dispatch.h,sha256=kD7aG-f4_nkVLdoP8XpzU8isEZkvEOlF8kaWy7L7OYA,925 +torch/include/ATen/ops/grid_sampler_3d_backward_cuda_dispatch.h,sha256=gKxrdwb-2IT51ijl8PXANrELMJgsmkeTifAxivYBGHU,927 +torch/include/ATen/ops/grid_sampler_3d_backward_native.h,sha256=DutqYvbspTgA-0FBhXbXNDRgqkvzezgbOqCA3cIUlb8,1254 +torch/include/ATen/ops/grid_sampler_3d_backward_ops.h,sha256=xVExX0wg49UPI2KhFqoTWYLVRwquW2gAua130v95Ais,2961 +torch/include/ATen/ops/grid_sampler_3d_compositeexplicitautograd_dispatch.h,sha256=iYQyA7rCoJ94mqtNvDWElBk1cVQahmB2pX6HCkjKGbU,1077 +torch/include/ATen/ops/grid_sampler_3d_cpu_dispatch.h,sha256=hH2MXILS_78WmrIAiejNC2j3qreFWT9fGlCuWY5uFwA,825 +torch/include/ATen/ops/grid_sampler_3d_cuda_dispatch.h,sha256=u4IULs0PcIya_JzRwIKmqp0axB2qsIqJkrIH8eRWR54,827 +torch/include/ATen/ops/grid_sampler_3d_native.h,sha256=cw1OVAhsA38JhgiXfY57sPh0iIkHqC_4FQ3P_zy9ZZk,932 +torch/include/ATen/ops/grid_sampler_3d_ops.h,sha256=wnhgIovZoYenxHw70HSllk1LFpovTL5ZIR1_J3Vz4R0,2225 +torch/include/ATen/ops/grid_sampler_compositeimplicitautograd_dispatch.h,sha256=4lhrlXaY14QaSNZOXYJ-5TlDARdmL0V780NgdiQngDE,866 +torch/include/ATen/ops/grid_sampler_native.h,sha256=yta0XC79IR_Y1qsdheBDC5knSrWYTT4epBuzREGvNl0,578 +torch/include/ATen/ops/grid_sampler_ops.h,sha256=WNgwStmOZxEU8UoHVpSIlnEiB-ebakJzdxGOSpxwxHI,1287 +torch/include/ATen/ops/group_norm.h,sha256=yZeHvo2ltew6VaFL_uKfJ-Y3bGkzvH61l_LEJojo65M,955 +torch/include/ATen/ops/group_norm_compositeimplicitautograd_dispatch.h,sha256=Vner38XN3HuPSNF9Qvcuv0HghufQRwqC5qBD0ji7Gqs,924 +torch/include/ATen/ops/group_norm_native.h,sha256=pfkN8NDbIX4H5pp7EckzzVXstZ2_PY1pUlhssNgVpD0,636 +torch/include/ATen/ops/group_norm_ops.h,sha256=GRZPQttfYW-6_o-ALhAkfWeH6gMYLZaeg3IlQvFMn88,1443 +torch/include/ATen/ops/gru.h,sha256=EutjlTnD19dk8OZEyRdqC0f8qTHQ8cG803jU31ZvrP8,1572 +torch/include/ATen/ops/gru_cell.h,sha256=i90IQSepFRKq-LKspipJOnfHPJb1WrNkbeNPBzm6em8,914 +torch/include/ATen/ops/gru_cell_compositeimplicitautograd_dispatch.h,sha256=UmGvZYjJe85Zjo1saHKIiPervwhIjRupphiXAxNbJRY,930 +torch/include/ATen/ops/gru_cell_native.h,sha256=UQXnHDPMbb1oE20jHiJr2BRwuHbT1TSrPFs5IizPOH4,642 +torch/include/ATen/ops/gru_cell_ops.h,sha256=-lK7vbQJbzN48BPKGe_g9TXufgFSh1-1uZK6ICcBVzc,1489 +torch/include/ATen/ops/gru_compositeimplicitautograd_dispatch.h,sha256=xe0XpYVnxpc-1irRWnb_RBp7aHTIc6TB5_310usWhW8,1175 +torch/include/ATen/ops/gru_native.h,sha256=ABzde9UYDNg62fi8NPnn324xiAOsoFHkiSp_pmzH9_c,887 +torch/include/ATen/ops/gru_ops.h,sha256=8-I9OIcTf7BudTE9zTugj3MAB4ARbQIe9dWJb6SI1As,2689 +torch/include/ATen/ops/gt.h,sha256=6isSRKXCXRhf0FR5cXd3JsJu0xQa-t4ld1dZUL2--Ls,1842 +torch/include/ATen/ops/gt_compositeexplicitautogradnonfunctional_dispatch.h,sha256=Z2sQFPwBYrYYaz7bYFG7f1Rbyc0lzuLB6j0FmZdm_e4,1034 +torch/include/ATen/ops/gt_cpu_dispatch.h,sha256=KvaYtu_1mtuxBINrePTunv1ibkZ9j5jbvDQKH55X8Zg,1366 +torch/include/ATen/ops/gt_cuda_dispatch.h,sha256=22MIm_dB38x-ppbu4fTD2OFy4SCwOKR8aQrdqh5dKVY,1368 +torch/include/ATen/ops/gt_meta.h,sha256=505_eNrGL4HH6uWC8og_U6ixZGVCI6WZSFXZf7hWoWU,735 +torch/include/ATen/ops/gt_meta_dispatch.h,sha256=1PUK0yKZPj9DMvmkqw0uyxD2HqpSBrnVh2r-4Joyw-4,1368 +torch/include/ATen/ops/gt_native.h,sha256=s0um8U1mQm45wiWPkOoRftpklHuJsK9bwOMuRa4TmgI,1295 +torch/include/ATen/ops/gt_ops.h,sha256=ubQSIvLfRkn-qK77B7TEs8o9gbL69cCeNkHG_SnC7ds,4201 +torch/include/ATen/ops/hamming_window.h,sha256=GJXx4gtgJKfBKqKypZBHIUjRzuKaM3fdoxrmlLx1pic,7038 +torch/include/ATen/ops/hamming_window_compositeexplicitautograd_dispatch.h,sha256=ngk_T-tHVj7GMt7soxv6uoNPzlBHN67IIwi0Wc4JTq8,2910 +torch/include/ATen/ops/hamming_window_native.h,sha256=dGYNZZYZKp1ElaV2bkEhZ9RcqWLyN0BTYPPrhH91NGQ,1842 +torch/include/ATen/ops/hamming_window_ops.h,sha256=YCGyg2-nCZmm8H1-qAQCXJPVw35N1RIgqqvU0nexmjE,7776 +torch/include/ATen/ops/hann_window.h,sha256=-Q10f0-cqubTThZLjaBidYyXBRbC68hfAX6C9MxVjhk,3317 +torch/include/ATen/ops/hann_window_compositeexplicitautograd_dispatch.h,sha256=JxgJgwxa1OSAoBuGrQv7-0xKpRAJnuELHP1ZkjlHmaI,1674 +torch/include/ATen/ops/hann_window_native.h,sha256=oWQpxJEu5Scu5IE1c2JvHsT7z2HO5vdaNNnugM6i_mQ,1051 +torch/include/ATen/ops/hann_window_ops.h,sha256=tMoM82mEBcGIR0yPnPvszu64xIi4iIfJ9Vhc-N59wk8,3808 +torch/include/ATen/ops/hardshrink.h,sha256=AnLYo2nPTsD3f8QvPpb1TLahgDU1OU81WoO3Cs5-eqM,1228 +torch/include/ATen/ops/hardshrink_backward.h,sha256=SBXgDKVkoxw-0Eq1PpuBIEyYvn7JWZIdgn_chudwhqQ,1536 +torch/include/ATen/ops/hardshrink_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=T97Gh9wvV3lqj-8FMqWKimFZCd1BDLJhaIGjh_oWe-E,858 +torch/include/ATen/ops/hardshrink_backward_cpu_dispatch.h,sha256=R6_yNVq93aeFWmIJQwtUvpO7ZWcNeFPiRQvJb3eZ0yM,1095 +torch/include/ATen/ops/hardshrink_backward_cuda_dispatch.h,sha256=FIxyjseyeGX2SM1DHrf3X1B0fniRAlp1C2-kyZh_vvw,1097 +torch/include/ATen/ops/hardshrink_backward_meta.h,sha256=PmrhBLwPIBCCvltx8s8moeDAer3EmLjvrJh3eJtrwZI,635 +torch/include/ATen/ops/hardshrink_backward_meta_dispatch.h,sha256=N4bTge3dwG34fwY83eeqGt-tC70WprBB5R25iPgkMB4,1097 +torch/include/ATen/ops/hardshrink_backward_native.h,sha256=8-8NK5HcPNqYdPfkYfQeZIeKLHjSB2HievfjY4vuXDI,697 +torch/include/ATen/ops/hardshrink_backward_ops.h,sha256=-tOValN69GEHzOsUXRz7YJJIHsKmBqJX67zJYzHJI_w,2029 +torch/include/ATen/ops/hardshrink_compositeexplicitautogradnonfunctional_dispatch.h,sha256=BEljD7K-wbeB9zh-lTiXgJoHXH4E68zTp2V9R1hGbC0,824 +torch/include/ATen/ops/hardshrink_cpu_dispatch.h,sha256=5br9nPVtUGtCf2nHZ0HOmQDi1Q2D9ZBhbhadwBIayas,975 +torch/include/ATen/ops/hardshrink_cuda_dispatch.h,sha256=tSLTM6Ml5Sl89xFsFfxdEqr62V8QIc85fFqEj7AFIDE,977 +torch/include/ATen/ops/hardshrink_meta.h,sha256=aPUtRJrZzveCbp6RErdOkCIvT5bWEPmqdr-p8vdNm4E,597 +torch/include/ATen/ops/hardshrink_meta_dispatch.h,sha256=rzvVtNptBs_NW14dR1Y2efGfTJGzMzanxGYHSM25--s,977 +torch/include/ATen/ops/hardshrink_native.h,sha256=4RBWx3oG1W0nPFOXUDJVOFD3d6EufS0e6RD15gJV5mQ,634 +torch/include/ATen/ops/hardshrink_ops.h,sha256=tKAvNG4ZLAg1coIXdKpz76vF_wdhmotJFdLbIIJ4-QA,1751 +torch/include/ATen/ops/hardsigmoid.h,sha256=ufrNK8tXq5Wlb2w0f-gwM_lZagyJqFURlUhK-RtAHdw,1234 +torch/include/ATen/ops/hardsigmoid_backward.h,sha256=0i2qdAlrOyn6_dSzN8QOM0DSKRDDXmpRVC-LJambz4s,1432 +torch/include/ATen/ops/hardsigmoid_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=2mr7MsdmJNhyb9nJ09L7eGAk2AHhQUP2FvUUF0z_LMM,836 +torch/include/ATen/ops/hardsigmoid_backward_cpu_dispatch.h,sha256=f-SY4XsJM2VoT_mdgI-0Y6Lv5Qtxlyah1N61MT0DjZE,1029 +torch/include/ATen/ops/hardsigmoid_backward_cuda_dispatch.h,sha256=V-FVIqG8zbPeVBh4CbZlQQymph4aWIfXTm-Vq0Nx_GQ,1031 +torch/include/ATen/ops/hardsigmoid_backward_meta.h,sha256=ZObg808KPHhAh-DrBWLZ5tVz4Qt5k_3xgIdToLC-ZNE,613 +torch/include/ATen/ops/hardsigmoid_backward_meta_dispatch.h,sha256=bwhCBuN0XZfiladaNyJqcpMITZEtxEk_jaix3YI9vRU,1031 +torch/include/ATen/ops/hardsigmoid_backward_native.h,sha256=o_rnl3M1iIA6RITxQrGeGUiHepLTWChtL_VHIKvfRZs,677 +torch/include/ATen/ops/hardsigmoid_backward_ops.h,sha256=viqgIoMES0XqxW3CxjZPCiZdTp1X0dnAOaD8DsApYck,1881 +torch/include/ATen/ops/hardsigmoid_compositeexplicitautogradnonfunctional_dispatch.h,sha256=xhn5O2eYc0MdXm5NeL1kFCAzwmdAuZFSog28gALPnks,851 +torch/include/ATen/ops/hardsigmoid_cpu_dispatch.h,sha256=q53Nkv8F3mXxfv4NiRwzv2UbcI3w4zvBD5vIFSjcFtI,948 +torch/include/ATen/ops/hardsigmoid_cuda_dispatch.h,sha256=NxtvQvKoB4EGWbZChNDnwU49Wk6UZBqTjb-h7ZDC17Q,950 +torch/include/ATen/ops/hardsigmoid_meta.h,sha256=wbGIOg7RzzKydc28otAzyzuo04WcrxhsWif-Ud2OJLQ,572 +torch/include/ATen/ops/hardsigmoid_meta_dispatch.h,sha256=hH-nL7cRx3mPwN60hn3aPB5HjzCz5Ttn3OfhodRCeEI,950 +torch/include/ATen/ops/hardsigmoid_native.h,sha256=vOOK2_O2ifEIakKltSRwOd_25i7sk6_YOKr9sDoIqRs,781 +torch/include/ATen/ops/hardsigmoid_ops.h,sha256=dwkB54COjAzNMG_2UjjgqNurXLUI_GDqPZjnZJrxTFE,2091 +torch/include/ATen/ops/hardswish.h,sha256=cxydhjfYA82rgT2SQdMIFmdAlnT4YSYpOLbbSlNDQ-4,1208 +torch/include/ATen/ops/hardswish_backward.h,sha256=MQArpTyInPAnuiyQBzHw1bLVmIHkqVNP36MhUuNLS7k,1342 +torch/include/ATen/ops/hardswish_backward_compositeexplicitautograd_dispatch.h,sha256=rTgzA7AqtQGOeR4PK7ZDMgfqW8SDY5gY53CVc3yjoY0,955 +torch/include/ATen/ops/hardswish_backward_cpu_dispatch.h,sha256=HMty385lv2s0mC6Qj-c4UJAatCRzLrPOKwSV3JjjN2Y,764 +torch/include/ATen/ops/hardswish_backward_cuda_dispatch.h,sha256=PyhZciayHwqkWVRVM-51kiDfHh953Y9wBjCRckH1S_k,766 +torch/include/ATen/ops/hardswish_backward_native.h,sha256=K7WSBh6pxSCsQtAEtfWE_-UOrZ_luvjCmfipJ_Cmruc,642 +torch/include/ATen/ops/hardswish_backward_ops.h,sha256=LJI2fF8Gt6WwgQZW6-DU2Aqn_MUK_vyAfV07SugdSq8,1827 +torch/include/ATen/ops/hardswish_cpu_dispatch.h,sha256=kBXk20TCrpj5cd2PEP3wqVuQp_fzOZKFo5l5KxtyjSU,940 +torch/include/ATen/ops/hardswish_cuda_dispatch.h,sha256=jhpVRHRjJIK75jVRIdaMuAe40RtUFSC9FJjJHZdca1w,942 +torch/include/ATen/ops/hardswish_meta_dispatch.h,sha256=u0i5X_UvWqpMAbS2rrq8pQ1ZL7nAbaGZc6ZNqpK4eEs,722 +torch/include/ATen/ops/hardswish_native.h,sha256=TsIMuR00g3mAIW2kA-8qX1ZSn6gG5prEUj4eZOVyDIo,614 +torch/include/ATen/ops/hardswish_ops.h,sha256=yTlyu4fkVYlVpewzOiMNfBBtkp5u6bm2Tw_OJsGfJFI,2073 +torch/include/ATen/ops/hardtanh.h,sha256=9kObN_tpBzGtge8HoIYccuAOPfJ-Arb3jThhXuov-U4,1654 +torch/include/ATen/ops/hardtanh_backward.h,sha256=Tx2hyn1fDu3o6ts7Y147xXB67g40J74NpD9gBq1swMc,1720 +torch/include/ATen/ops/hardtanh_backward_cpu_dispatch.h,sha256=55Pc5PGjsQV609SFGDkUWnRUI5IdZdxWz-imeAfqqQo,1188 +torch/include/ATen/ops/hardtanh_backward_cuda_dispatch.h,sha256=cF7u7AY9huzUCIkXagwnALAjDoDGRYQDd0lulmCa1AQ,1190 +torch/include/ATen/ops/hardtanh_backward_native.h,sha256=jMZyo1ikBqnAH9zVRbKYKQxEW2iLyaAIWDg93jFdtRs,759 +torch/include/ATen/ops/hardtanh_backward_ops.h,sha256=wlXbSwnlR7K_9rfvod31rHObtP0hz7Ck_HWBuU9V6Co,2231 +torch/include/ATen/ops/hardtanh_cpu_dispatch.h,sha256=8W-va7CImYRRCHdu0YuvCWFmw1grgFK-KXl_x9BGCtE,1175 +torch/include/ATen/ops/hardtanh_cuda_dispatch.h,sha256=XF5uz9yI7n4I-ynsUPla4agbeJmbbrnqazzLO8PtR3o,1177 +torch/include/ATen/ops/hardtanh_meta_dispatch.h,sha256=k8nJkKne2QxLnQ8MQPKQYC6av_dpoCGzEtzx4331vvA,782 +torch/include/ATen/ops/hardtanh_native.h,sha256=J7oGzMFuyZrNuf3bNQ3oT6mpxR-W5ysY1Lt9TAjG_Fk,1198 +torch/include/ATen/ops/hardtanh_ops.h,sha256=oq8Dx9ImJ_Qh-N_vDbMqUA7hpQt53AROtyTvAm4CbMQ,2631 +torch/include/ATen/ops/heaviside.h,sha256=8ZEPgAx-WMb1EIPa6XnydwZxqKyE2kiGo1cHzlhXwB8,1207 +torch/include/ATen/ops/heaviside_compositeexplicitautogradnonfunctional_dispatch.h,sha256=rY-l1c0Jc7NU52e_G7kjORTH1MJviPxztOjWnHlp6J8,901 +torch/include/ATen/ops/heaviside_cpu_dispatch.h,sha256=5E2gw7PJtq3v7vSLkmeWtg0JsWNG0oxrhlbAMaFSKkg,1048 +torch/include/ATen/ops/heaviside_cuda_dispatch.h,sha256=vTspqK5PZ2b0wB2ehC_ykhevPvzkZBqO6bM3mEQWmws,1050 +torch/include/ATen/ops/heaviside_meta.h,sha256=GRtk7JYaFUsKHMkOSm3RuYASeonh-QweApkWBCQofFk,597 +torch/include/ATen/ops/heaviside_meta_dispatch.h,sha256=VzCDZYncqqHuScrY6tURCAgJMHwBKXnLW-fgjakdeWY,1050 +torch/include/ATen/ops/heaviside_native.h,sha256=FSNm1fZxeP-aDrr8pQHnX0FWhWdtBz0Uia_uHlRA31g,632 +torch/include/ATen/ops/heaviside_ops.h,sha256=9lU99RrcXcAOn5r_3b3hItZBRDK_16fcW7ui4zYZADs,2340 +torch/include/ATen/ops/hinge_embedding_loss.h,sha256=oDRYocCuX0yOD7K_6tHsKS4SSGXI-t4ERjHnLV-JXwI,858 +torch/include/ATen/ops/hinge_embedding_loss_compositeimplicitautograd_dispatch.h,sha256=4OceJCjlx2p7D1zTgc_DqrX6lU1epB_nwcQogI_bVAM,863 +torch/include/ATen/ops/hinge_embedding_loss_native.h,sha256=tB2cdpQxqLxJrUC6zNr95EtgZTa1fqnRrbkMHSsLkLk,575 +torch/include/ATen/ops/hinge_embedding_loss_ops.h,sha256=4QpqzfupwmrGs7viv-WD2NQQMKRr3Hve5pKLbQ7jsLc,1211 +torch/include/ATen/ops/histc.h,sha256=iIl-8DJWFUSV3PrhBVs_ILmX4b0Wwg5RVMgPYy3LrVk,1393 +torch/include/ATen/ops/histc_cpu_dispatch.h,sha256=C0qiQXah9GCe-_OjxI_I4qnHg7dwPTIiQ-f4n5f0sws,1076 +torch/include/ATen/ops/histc_cuda_dispatch.h,sha256=J8Sh6wmvB60gW73XplingglyU2cNEbAGXvQ1EnNEgiU,1078 +torch/include/ATen/ops/histc_native.h,sha256=pMWmOaHoNk3tV9hlXIfgTuZqPgkwKKgUSHZaRlNBfP4,978 +torch/include/ATen/ops/histc_ops.h,sha256=tZY5aXEjQXUFIaPts1f77X4SNcymZk0NkpVixjRECVk,1971 +torch/include/ATen/ops/histogram.h,sha256=UcWUVAB2vC6_otrqHvsiSXJIQoYsuAByXW78zMGgFoE,3516 +torch/include/ATen/ops/histogram_cpu_dispatch.h,sha256=DLxRcRdtLdIhKQNzU2hPV1OAgPqiGRbSmZ_dTRQH-wY,2038 +torch/include/ATen/ops/histogram_native.h,sha256=-lCv_5gIw_HimtR9gJaEGP9_Xq-3xD6gLpjJo-N6Y-o,1289 +torch/include/ATen/ops/histogram_ops.h,sha256=4cAZje4fjAnFuDJppySm4IoSw_zj5f0HNZCw__yqIec,4753 +torch/include/ATen/ops/histogramdd.h,sha256=pl4x1VjUV-DriLIC6nhDdLZuTjEhXKuVNP8cVjrLlVQ,1959 +torch/include/ATen/ops/histogramdd_compositeimplicitautograd_dispatch.h,sha256=ZbbAGMbD-m0oMUU_j3fdBXdMwqbsPYfLagfK5KdvDeo,1445 +torch/include/ATen/ops/histogramdd_native.h,sha256=Z9UBTu-xmOhZGjqiIeVOVTL0A59LAsHGhLnlmbOhyYM,1157 +torch/include/ATen/ops/histogramdd_ops.h,sha256=dB3q4RY6DP0mv1VIHFlKX6w2HP6d9S2V0FpiPvnAnfc,3759 +torch/include/ATen/ops/hsplit.h,sha256=1xxJOw9SFIzaK_Lf6eRjRaTsdilclymRt5DQNNadg3Y,939 +torch/include/ATen/ops/hsplit_compositeimplicitautograd_dispatch.h,sha256=VGu6nNlhjaBO0amvWPtO6gVWRbPoVnDUsA_C5U-BXGI,891 +torch/include/ATen/ops/hsplit_native.h,sha256=xux8e3DfO8BI-gKCpDwYpyq-itY9rxs9VpI1U9F5wDU,603 +torch/include/ATen/ops/hsplit_ops.h,sha256=2rWvjNxNacDGtQa3XY1g7Xjfmk-l3tIwPwAe_SbmUKg,1742 +torch/include/ATen/ops/hspmm.h,sha256=QvT56xnSJdNB-C_hlGwVavsbdI_3cHQfUuU6qUwjriw,1149 +torch/include/ATen/ops/hspmm_native.h,sha256=EjxYyLGk1-fejkb-3Tmv_bDct-RcroLE6N8yD3ZrCtY,828 +torch/include/ATen/ops/hspmm_ops.h,sha256=QbR9UG4wQOBfNhpQqEOaGI9nrbi1SfpxbpM9ogrzVCk,1707 +torch/include/ATen/ops/hstack.h,sha256=SyGQmtd1fVWTMJ1hd42T4HHIrhAXagzsBTrBK5FNxio,1048 +torch/include/ATen/ops/hstack_compositeimplicitautograd_dispatch.h,sha256=Zn5t1UPVI6QLC_chOuedSWl1gKC5V8PpacMcdmMvG4k,918 +torch/include/ATen/ops/hstack_native.h,sha256=cEJaEEYbs1Vuv5qMooozt9WFj9TP1yqh0VDvU1G0aSw,552 +torch/include/ATen/ops/hstack_ops.h,sha256=p66ueVPDBpg5-EIv6o-gQKS5eGzUiX8P9KK7Q3pB5rQ,1545 +torch/include/ATen/ops/huber_loss.h,sha256=xvbxSxQs8AyM4Kdh8BueOHCkdq_BTcdhelOG0jdVqBs,1529 +torch/include/ATen/ops/huber_loss_backward.h,sha256=N8MKf4qhvh5kHvR1gBydo2mvgLbD953VLXvfjB6JXnI,1781 +torch/include/ATen/ops/huber_loss_backward_compositeexplicitautograd_dispatch.h,sha256=oWwfQloaCaNs6W0cTMNX28N8m7s0paHjgD1xj-O45hg,869 +torch/include/ATen/ops/huber_loss_backward_cpu_dispatch.h,sha256=TUPK4NLqHjEZjCwAnV4RIqgmrQh6Dd_KpeYiv75xsDI,1047 +torch/include/ATen/ops/huber_loss_backward_cuda_dispatch.h,sha256=h0wjUErNkhjGs1lOtM8ZTlB4EQaWLXjOTVTutbS8vbs,1049 +torch/include/ATen/ops/huber_loss_backward_native.h,sha256=jbeNQ6qeJscdeZjBsKpv9Mdl0dtdQ6GTJrriPuYNYoI,771 +torch/include/ATen/ops/huber_loss_backward_ops.h,sha256=DU9LVpcSrIio0wCDwK1aRpvutmsfc1s4kPtKe4EQs6E,2254 +torch/include/ATen/ops/huber_loss_cpu_dispatch.h,sha256=dD7sZyQq99M_2dm3ierMP0aEgJci-9KjsWlXJLxBhh4,1117 +torch/include/ATen/ops/huber_loss_cuda_dispatch.h,sha256=zSvgocqIkQGHSS_gTNWi5VOLpcdP3BLQPV69OQ8nQD4,1119 +torch/include/ATen/ops/huber_loss_native.h,sha256=2PhVIApFl2y0YfCblVyPIVFVR9NoFbs9W-1rYhzL4TA,706 +torch/include/ATen/ops/huber_loss_ops.h,sha256=wa2Kj7Z8LfhUXXnjHMehbD6cozZqtQS6a3hE3tA3E6U,1989 +torch/include/ATen/ops/hypot.h,sha256=IwVPttelExc4N1PhB-RP4zUaw4EfzAQjUd5psR6zQ7o,1158 +torch/include/ATen/ops/hypot_compositeexplicitautogradnonfunctional_dispatch.h,sha256=4mC-9q67le8qKcTvaUL5G0gqCimjGu6tcAR1tet5NoY,891 +torch/include/ATen/ops/hypot_cpu_dispatch.h,sha256=D-Oigt-GgAUHAFbSGynUZBAuWcGbJTDuIcOsMMkCH1o,1028 +torch/include/ATen/ops/hypot_cuda_dispatch.h,sha256=rp74Dn1pEKpjHS9GKzfg4vfIMKZ5h2SzkLG2nD1Q3BQ,1030 +torch/include/ATen/ops/hypot_meta.h,sha256=oZHdE-gBcwwrpBCTqX6UDZ2MXiWBPzyUtCdHVUOz9dM,592 +torch/include/ATen/ops/hypot_meta_dispatch.h,sha256=a3w2-mTk6qmemOtv4oLln2txtIRa0vM928tLSmdPySQ,1030 +torch/include/ATen/ops/hypot_native.h,sha256=0auVyge7hcVhHCzgqWRr54D-nQFItiRUUj8PWF_3FdE,619 +torch/include/ATen/ops/hypot_ops.h,sha256=xq1QEAfe3S2n4J5HLdKyA0R50YWRY3ths-gpOsZuLVo,2295 +torch/include/ATen/ops/i0.h,sha256=gXadT2W7jePgsH2e2_PIGrbNrvUJ3bLQgb_xzGWKKYs,1117 +torch/include/ATen/ops/i0_compositeexplicitautogradnonfunctional_dispatch.h,sha256=TQVvSjHcOOwAB94DWCS8hAYR2Kp41H_y4-U1g1ADRjA,833 +torch/include/ATen/ops/i0_cpu_dispatch.h,sha256=6d_rlXT__vLzsO6Au-vcwQ0l7Wwrc8ce34VvENBsPKk,912 +torch/include/ATen/ops/i0_cuda_dispatch.h,sha256=KYkT6YopLjQE5A7YALQxaPV6LhpXvsg9QKVYHUahJU8,914 +torch/include/ATen/ops/i0_meta.h,sha256=HvUSGzFtJ8cL6V4_HNJoZba04kPkEbVaSFQ3pcE1JSM,563 +torch/include/ATen/ops/i0_meta_dispatch.h,sha256=-99oCMwLHT1cQ90-qkJP6wGMakMhrwaLrlhlttJN1dw,914 +torch/include/ATen/ops/i0_native.h,sha256=el_kVLuJua9SmuRXWmsTeVf0_hfO3lH8dzaU8iAqfwE,584 +torch/include/ATen/ops/i0_ops.h,sha256=2VNIEXQnA35NiXzZxqSLCeY7A3pu-uQmWjnqmZxp0Rs,2010 +torch/include/ATen/ops/igamma.h,sha256=DPmf0h3Nf-yVQTToXt5JFEaOOg8PbKPa_dBHFn8m4RM,1168 +torch/include/ATen/ops/igamma_compositeexplicitautogradnonfunctional_dispatch.h,sha256=RXQ2KFbVDx_ojy1P6X5UJe23uev5w89sYC7Co1s4Cmk,893 +torch/include/ATen/ops/igamma_cpu_dispatch.h,sha256=Fb5tOPCCrdyNgaD1UEbVKTFfKz04oFA1LQPI00gQNj4,1032 +torch/include/ATen/ops/igamma_cuda_dispatch.h,sha256=jLdlickXI6czL8mPlP6703L8rswUST7f8ijnM-MfcDM,1034 +torch/include/ATen/ops/igamma_meta.h,sha256=xhblVGOZhIzccnLtfToizt9U2ysVeVhxwuZDIWd77Wk,593 +torch/include/ATen/ops/igamma_meta_dispatch.h,sha256=6J4N1rvt0ej8pgVYhR6toFZ0xZzw6T9rPN1YxxLeMpo,1034 +torch/include/ATen/ops/igamma_native.h,sha256=NLzzE2MBquNC2d_LrQH-QLXSZTn8UHy8l5kDJpJP_Mc,622 +torch/include/ATen/ops/igamma_ops.h,sha256=lG4x7VU5tI0FnQc-JuyCNayF0vwiQ8Mna-uN29v-K4U,2304 +torch/include/ATen/ops/igammac.h,sha256=4-TMtLbu1hVH4qKJzXrbsPZVYgcrM3E1oOzzT-_9eXc,1178 +torch/include/ATen/ops/igammac_compositeexplicitautogradnonfunctional_dispatch.h,sha256=rBlRimdNlBbYDlff1U8EpRMpzxNpxqpRW4F7Emy-I0k,895 +torch/include/ATen/ops/igammac_cpu_dispatch.h,sha256=ntIbXmvldHTviroBpPKuymzsrKL4Xml71CRXEuY1Ehw,1036 +torch/include/ATen/ops/igammac_cuda_dispatch.h,sha256=j92eayh8eWXGNxH_ETekv00BrtA3hKP2ODLhC2Hglvo,1038 +torch/include/ATen/ops/igammac_meta.h,sha256=8kqNvFLtobk2uEjvDf9zH0KDI2BIt-h26pCXuxMH8jQ,594 +torch/include/ATen/ops/igammac_meta_dispatch.h,sha256=rE8v-jskpM9Mwjaxwbl7sWZjMZnxdBUv-AptmJlwU9M,1038 +torch/include/ATen/ops/igammac_native.h,sha256=L_FsPR237g6bso6bT_uUDBOvQGI4IVWLGCkFWIwbXVk,625 +torch/include/ATen/ops/igammac_ops.h,sha256=yDEENyYcHdMSQCEQQI6hV3zmrt20HrLIUtvcjSQwumY,2313 +torch/include/ATen/ops/im2col.h,sha256=LmBl6DQr-kzMrlY36dT3duFriHlWDo5BP-rI-qPFcg4,1663 +torch/include/ATen/ops/im2col_cpu_dispatch.h,sha256=_jvIGHMby1azb2qp6vPKYQpvoWHBe9TwqWy_a0IKDVs,1189 +torch/include/ATen/ops/im2col_cuda_dispatch.h,sha256=hkQedhLXBQWIE7xSjgA6OqvEsg17-6HALcK1kMCPmws,1191 +torch/include/ATen/ops/im2col_native.h,sha256=TXHwTDpTDihYKpxEhc8euvBzZLh01bxwAKE_7UmZ3H8,1120 +torch/include/ATen/ops/im2col_ops.h,sha256=EfqGXRV97mzxajl9z7OCpOVkF46iXZcZHHXyO9z7hrI,2235 +torch/include/ATen/ops/imag.h,sha256=RZgBjcdLQQWHCGFe5bj8AFLVPL8EXfwlvu2qZnnk4EY,635 +torch/include/ATen/ops/imag_compositeimplicitautograd_dispatch.h,sha256=k6fCdg6zHmwu0bceT5Z6Ojtpvc1Z4JEivk1aSSLa7Fg,762 +torch/include/ATen/ops/imag_native.h,sha256=1wDaJMe_PNiOMrzKyr_eHwSzdOHRpSgJ1sBrF_7QLQE,474 +torch/include/ATen/ops/imag_ops.h,sha256=SZcdGQFPVoTZyMcNEJxh14446FHv-GxXLyx64bS-RXQ,957 +torch/include/ATen/ops/index.h,sha256=Xe4LluVQ5fzc3amoOax4EGsNqyl2ewdgK1c8Kt0l-5M,1311 +torch/include/ATen/ops/index_add.h,sha256=Znm93Ly6zswOTHDYIgZ0mu8skIhku21RJsGPOphm0ek,1929 +torch/include/ATen/ops/index_add_compositeexplicitautogradnonfunctional_dispatch.h,sha256=jhkdb2fsCe29JTQ5J4K--HmYuspCmSa2JbZci2xBbxg,1035 +torch/include/ATen/ops/index_add_compositeimplicitautograd_dispatch.h,sha256=nIxcakQ_ZpfTNGdZS1L9dCgNGn6HVznqPm-eaCgBKk4,865 +torch/include/ATen/ops/index_add_cpu_dispatch.h,sha256=6Zm_b36B8uOa8RsK9oXnaz-jhAHU5gifS5XyEY66piU,1314 +torch/include/ATen/ops/index_add_cuda_dispatch.h,sha256=n7CoAI4atRcUGe0SydjiDDZGdiOT4tUyqVNkIdj9unE,1316 +torch/include/ATen/ops/index_add_meta.h,sha256=R9PfFJCjjNDrtpuRy9Ol3_aVrcO6vhVin8N4G8jDZsE,1104 +torch/include/ATen/ops/index_add_meta_dispatch.h,sha256=t5CO3gH4JaqR1iQSTHNvy88tA-r2yP6D_LvyaK-wNIM,1316 +torch/include/ATen/ops/index_add_native.h,sha256=lcraRQKCC52bi5FAtOXHkk24PKGAt8gW2881-6Jt25Q,1100 +torch/include/ATen/ops/index_add_ops.h,sha256=Pbb14-2wTPnwJTCjCekeaCSDsDa-j-KZvqaHCN5cjPY,3858 +torch/include/ATen/ops/index_compositeexplicitautogradnonfunctional_dispatch.h,sha256=mQjGM6Q3urjQ-RrwwcwNLHbNICvz4KmxM_jWGgo1rBY,845 +torch/include/ATen/ops/index_copy.h,sha256=HG9oP5Pw0XFKOhtHoxcsTJnvk_bRXDj7Ha2Oj2QGc0U,1734 +torch/include/ATen/ops/index_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=ISW5J3en26MzCOfMFHTZe_N86uljfTObV34QLSBnVqo,981 +torch/include/ATen/ops/index_copy_compositeimplicitautograd_dispatch.h,sha256=qjT-iZD3PVfjtbgtGiYU_k-jkH8vsLT0Kp1sw1be1Uk,963 +torch/include/ATen/ops/index_copy_cpu_dispatch.h,sha256=CTfro6ukWFyG7DDYZFC2uA3pMzxr5A_asVDws8Y5-Bs,1208 +torch/include/ATen/ops/index_copy_cuda_dispatch.h,sha256=IigJtGqwuYS8OiEzXKcWQa-MjtqX8z7pm_jXGH6_RlM,1210 +torch/include/ATen/ops/index_copy_meta.h,sha256=Sc0oYNi-akhItieM4k6ckhxe6IkFRHfE6xaUqnQhZU4,1079 +torch/include/ATen/ops/index_copy_meta_dispatch.h,sha256=YGC5re08ZphuUm9unU6INs8CPExCTxRf8ADh8OLBMwo,1210 +torch/include/ATen/ops/index_copy_native.h,sha256=AqeGdOQksBgNR46txS5NJ5UTm-t_mzKenX_m0wQ1t90,927 +torch/include/ATen/ops/index_copy_ops.h,sha256=kW-HTtREdEV5C0rZelpVEMsadO93oVCuUEk-Kq5atwc,4278 +torch/include/ATen/ops/index_cpu_dispatch.h,sha256=78FfP3VEGw9uTMoUejBcGTHWbICtoaPuGnRKJUgWVxw,1042 +torch/include/ATen/ops/index_cuda_dispatch.h,sha256=iH4qfSFzG75HnZBkvQyOKDla39oiYZjL_nHIjtsbfSQ,1044 +torch/include/ATen/ops/index_fill.h,sha256=TsSX6L7sc7xpjdWQuFUqyxf4X3yLqCR3fqQxt4J_xfk,3098 +torch/include/ATen/ops/index_fill_compositeexplicitautograd_dispatch.h,sha256=NblXRgpfk8q9MDoExCKsyZqDFE2AnHDq8sIDuldcOvo,1546 +torch/include/ATen/ops/index_fill_compositeimplicitautograd_dispatch.h,sha256=Sn3-N4_ga-20Tz3pLU2xtbQ1Qwr4AabBL_YTp4XqWmk,1212 +torch/include/ATen/ops/index_fill_cpu_dispatch.h,sha256=h5A-lEM80B5lglKP_IZeGcdBvk_IcN0-I06A3ea6aaI,906 +torch/include/ATen/ops/index_fill_cuda_dispatch.h,sha256=jGuEpQzxFEbpk2b_U0L2Z0OSxx6W5iJ490S-gWbK12w,908 +torch/include/ATen/ops/index_fill_meta_dispatch.h,sha256=EZVJfy7fgCF9nUsh4e6rzOx6yfDQ9vX_aNnkwInYTOw,908 +torch/include/ATen/ops/index_fill_native.h,sha256=_z5gD1ZTnbdYMYTFdgZfAYb2gbqtFNbQbxDIxlQlgSY,1726 +torch/include/ATen/ops/index_fill_ops.h,sha256=DakJ_yqN9-VtsblMW_8MZeo-Y9kOtLECpD2kwQLS02Y,8347 +torch/include/ATen/ops/index_meta.h,sha256=ao_sH6WUUvMzrj5vQ652hKVE2KU5Ym1qqp5NHzmasDo,1470 +torch/include/ATen/ops/index_meta_dispatch.h,sha256=P-8TWTTmftdxVFMe0pJuHpHuncixF-jZs1tk3xljEes,1044 +torch/include/ATen/ops/index_native.h,sha256=jFxdrBxSZU-sVntjfO43uSHTMTuLQU5ViTWrQ5ina2E,763 +torch/include/ATen/ops/index_ops.h,sha256=YJtNUdmPEfX4kKVRB6UGyWa9LiE6YxKb22l0BHjFJms,1940 +torch/include/ATen/ops/index_put.h,sha256=XUrE1SdadC5bxK6MTy2TsPAEV_zAWjJaJGKEYUNpct0,1970 +torch/include/ATen/ops/index_put_compositeexplicitautograd_dispatch.h,sha256=tSGeYQR41rQPY64Gpj8m2zDnBjBzXQqnxv1roV42Vew,1402 +torch/include/ATen/ops/index_put_native.h,sha256=o6cVhGU6qNyeDIob2JZskGs_TOj4eQjlLKUlkcxH9R4,926 +torch/include/ATen/ops/index_put_ops.h,sha256=-DymFLKS04y8IymPCNP-rTBJvZ0UO4b0n1HKyuc8QVA,3066 +torch/include/ATen/ops/index_reduce.h,sha256=FlUXqXfvvH-i-woe4BDg41HAeOrtfRLcA2vjrDvHHuE,1778 +torch/include/ATen/ops/index_reduce_compositeexplicitautogradnonfunctional_dispatch.h,sha256=R6N_xWY56S2n5BDeYZ4fTsl-Rj418YWb1lzPhnsH2kg,1083 +torch/include/ATen/ops/index_reduce_cpu_dispatch.h,sha256=kRvYVrYZRoeG2YrCNUe00SmO8GcqiAvPuLYl8tMZJcE,1407 +torch/include/ATen/ops/index_reduce_cuda_dispatch.h,sha256=NfFethrIFpZiwBOceL7Ek0PsMGFSXH4o-5fGa4Kvd4w,1409 +torch/include/ATen/ops/index_reduce_meta.h,sha256=fb2ZyPlQjmTJgZbqvmD7zN5rtU8g9A04Zg0rPQIcBJ8,1125 +torch/include/ATen/ops/index_reduce_meta_dispatch.h,sha256=Rzw9n8_oqGUMr0r2kJhQeoxk3cg0lL9o1K3lGD6O8wo,1409 +torch/include/ATen/ops/index_reduce_native.h,sha256=BCw2y1qAJQfyevitD7ff_3OG1qyD5S8ebxDg0pwrNbk,996 +torch/include/ATen/ops/index_reduce_ops.h,sha256=V8oafKGJtsflWdw7bKEx9Lj_VSowk1-XVYN-HMhh6Q4,3207 +torch/include/ATen/ops/index_select.h,sha256=YA0GyeE6gA7QazFou3LfavHU7ry2ZHq8ign5xtBe2-4,2182 +torch/include/ATen/ops/index_select_backward.h,sha256=KyTaQmIb9a1sUiFv4nLTOlEPlRTi4l_5tSV55-TTJuY,1848 +torch/include/ATen/ops/index_select_backward_compositeimplicitautograd_dispatch.h,sha256=o9hT6T-ge8JSAEMeGj4p5NzH8U4zMpm5oyH7eG1Lhd0,993 +torch/include/ATen/ops/index_select_backward_native.h,sha256=uWLSvxxXrYnWVC5kpC_CvUoGAZjNcXe4aEFVtr8lqzk,569 +torch/include/ATen/ops/index_select_backward_ops.h,sha256=M3G97DlXspL4JSrgwm1jeC2kR_VW81kPMolJKzdf2Sc,1238 +torch/include/ATen/ops/index_select_compositeimplicitautograd_dispatch.h,sha256=8f4FLKtwe-5DE8GYDoaNPjVtCl6od4KdpapNNtch7Jk,1068 +torch/include/ATen/ops/index_select_cpu_dispatch.h,sha256=1MbvSC-blT9bwOJEXpzUiFT73qsz5N-NBWlJDG1gMDI,1012 +torch/include/ATen/ops/index_select_cuda_dispatch.h,sha256=sCuNKKjpbY8aplDqQ9DyM1b-tdg-eGCjN4iTYyJLWzI,1014 +torch/include/ATen/ops/index_select_native.h,sha256=bCbKpA1fJ9HFeLH4-g4BnJmojXWl_q6u2lyGUaGX-T0,1565 +torch/include/ATen/ops/index_select_ops.h,sha256=voFN81L5Kd723AUMJ_nuiWoJ8pAT7zOK9vbkB-3OyHk,3308 +torch/include/ATen/ops/indices.h,sha256=j7a3YZInpMab8h6raELnewDN5NnEUpwYVQTUdjmcgkQ,504 +torch/include/ATen/ops/indices_compositeexplicitautograd_dispatch.h,sha256=NtptpbmpDMRQhc9OKENcOkIerT-wV-uREe6T_8m-dfI,765 +torch/include/ATen/ops/indices_copy.h,sha256=heD-KxZSxeoeHF1-of6ihFCgWCyrJksaBqx-NLDx8jk,1087 +torch/include/ATen/ops/indices_copy_compositeexplicitautograd_dispatch.h,sha256=C1pzb113Xh0GV7J5_wlGtrScbU25Y_pohFE5ynQBWsI,879 +torch/include/ATen/ops/indices_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=VIAEOpaWRV3GAQSCzjzommNlXXMWShHj9-z7J3okeJM,796 +torch/include/ATen/ops/indices_copy_native.h,sha256=Ncjh26wGgJhZDuZ60R3uSm1eCesvYFE_FsRXb3SWJFM,566 +torch/include/ATen/ops/indices_copy_ops.h,sha256=KYjFaBGnWJgqhq6Fji0gGmrDveGgWpzfIDMDr6mQ5Wg,1583 +torch/include/ATen/ops/indices_native.h,sha256=Z_GcO__Lqajh8Wl69dtIaQxEgOATAb66-vWDEv4SqSw,547 +torch/include/ATen/ops/indices_ops.h,sha256=-kfn5bPBktWmZf5iMzJCFK6aVOmzqrJlcHVxj5VTX4w,966 +torch/include/ATen/ops/infinitely_differentiable_gelu_backward.h,sha256=-84x9YnmTbrtZprUk4v_K1YYCt8Wx-cSfZR8FpxW1Sw,813 +torch/include/ATen/ops/infinitely_differentiable_gelu_backward_compositeimplicitautograd_dispatch.h,sha256=wm77Mxn6ul99VKoUWmCB3rkiqW7zMOv2KJyfSfOKyNc,822 +torch/include/ATen/ops/infinitely_differentiable_gelu_backward_native.h,sha256=M3l_a9br_ZqGjBI5kzdh-ETQxFf114MQce9nSzh92Hw,534 +torch/include/ATen/ops/infinitely_differentiable_gelu_backward_ops.h,sha256=8aAqsI60MODGvjLoPPcDYxTLYDXJbl0Wbx7yhhRvz4s,1139 +torch/include/ATen/ops/inner.h,sha256=mrhrwGGl0hpoRXk_d_m9-NJnthM9Kh04Sw0w4gg7pkM,1158 +torch/include/ATen/ops/inner_compositeimplicitautograd_dispatch.h,sha256=rGTQcl54_ubMF-wFN4RU1uZoxnBlVVOG1lG0Pl5lgFk,996 +torch/include/ATen/ops/inner_native.h,sha256=VvUrTrl1pAOevmhQy1LTpOSw1PoOAvA4EAR6M2fPV58,604 +torch/include/ATen/ops/inner_ops.h,sha256=OAof-yVCSctpmin0PADNZS9tVX_iEhMrAGyNCsbXIRc,1713 +torch/include/ATen/ops/instance_norm.h,sha256=cVOCfHYJFvUn7bFDkVd9CZVVg-_splRrEFWqTr8iisM,1154 +torch/include/ATen/ops/instance_norm_compositeimplicitautograd_dispatch.h,sha256=AX3uUTv2oAtq4DDZUalpehV5KNC4XzjP03cuWiMYD8g,1028 +torch/include/ATen/ops/instance_norm_native.h,sha256=eCSJWZPn-f6UFmMzawfvMm9lzUTvcUhtAc1ljS7HzEM,740 +torch/include/ATen/ops/instance_norm_ops.h,sha256=J5UJ1sXMByjV7xTWpfTD4ecbjBpTOVvxEaa9ZKpeibc,1811 +torch/include/ATen/ops/int_repr.h,sha256=DANDJ8rMCjQ7vOhcagMDzv7xTkvB1f--cSyx8Shr3jI,1047 +torch/include/ATen/ops/int_repr_compositeexplicitautograd_dispatch.h,sha256=N3eNdxRpL51j-SjJYR1v-_CqBZm5jKDtxN97k-xRFPs,871 +torch/include/ATen/ops/int_repr_native.h,sha256=ExwN4wXUH1q5nhbNj1_Cxf1ykRXNBQ1hDLaV_vAS-ZE,643 +torch/include/ATen/ops/int_repr_ops.h,sha256=_Y0dXXji_w0BPyKJdBn-QoNW_TKTWvUl62GH_Jwe5Xs,1559 +torch/include/ATen/ops/inverse.h,sha256=s4eynYpiEyQWDGqWmg0X9pEpZnUxIQ_nyYJXjcYvwsc,1037 +torch/include/ATen/ops/inverse_compositeimplicitautograd_dispatch.h,sha256=3hZZTApRJteFgetCKmFy3PcEGPweUE4OCTHURXkKLOw,924 +torch/include/ATen/ops/inverse_native.h,sha256=uj-CT2rMzP6OIIGgh9vkmUnYFZ0Sk6l_dl6Yo3kPqSI,556 +torch/include/ATen/ops/inverse_ops.h,sha256=y9sVUyhGX4cJaglluM1FwvBVCUoR6wFzoMySv2EwrPQ,1553 +torch/include/ATen/ops/is_coalesced.h,sha256=Y38gqjUDRzWKnRrBxzOFfSLzXXGzMIum80vrgxP9rBA,509 +torch/include/ATen/ops/is_coalesced_compositeexplicitautograd_dispatch.h,sha256=yC3pLDFtapo3PUsZdMjxINh25AJ9Oz0vSb1vAp0A_TM,764 +torch/include/ATen/ops/is_coalesced_native.h,sha256=S_KYU284-mDch-OFftlGekfeBQ9SUoDi1Gf0V75PwWk,545 +torch/include/ATen/ops/is_coalesced_ops.h,sha256=Bp0F_EHBlwc04kAR_7vGRn0bIGUiFYLWyLGUSCu6rnQ,955 +torch/include/ATen/ops/is_complex.h,sha256=fvrOjtDb2am4fndvSs_9E4IPSZHmmyrCOU1J3u4WokE,656 +torch/include/ATen/ops/is_complex_compositeimplicitautograd_dispatch.h,sha256=PM7uB6Z8ulJJUs-Q9Hkl7Ji_kWrQZb_PJr-8YDSieZw,762 +torch/include/ATen/ops/is_complex_native.h,sha256=FgK0ub3CivIpWtMNq6duqQZgwoO8TCEAiHzdEeD1MxQ,474 +torch/include/ATen/ops/is_complex_ops.h,sha256=jolgsY0eW2_GBcQ4skOTIym0iOA_dJ6--ebRtYi3arc,949 +torch/include/ATen/ops/is_conj.h,sha256=ukzPCdzVAY_c43CO-jUjOpMmagIhX-239sqoamVxdAs,644 +torch/include/ATen/ops/is_conj_compositeimplicitautograd_dispatch.h,sha256=6OF4Lw27SN7l5Gm1nWaZLYM32fjNnV1sgzw1ECfHe4s,759 +torch/include/ATen/ops/is_conj_native.h,sha256=4sSsceUPkvme-_Tc0vB0m2_CG9GT-egDnyC2dv_D0wg,471 +torch/include/ATen/ops/is_conj_ops.h,sha256=m8l3d1QAIupiNq1CcRdyDRzDxrMd9zFDbWGkGSj-7ck,940 +torch/include/ATen/ops/is_distributed.h,sha256=fxdUne8QwKpueQpzh8vhOWfoj_q9Q0E6VdiCwvxoGMY,661 +torch/include/ATen/ops/is_distributed_compositeimplicitautograd_dispatch.h,sha256=OPotyIqyIDnxX7pfFj1_Jd0iAajLgscjZNtEkAWNvNA,766 +torch/include/ATen/ops/is_distributed_native.h,sha256=qcLBeN_58hG0gi8iHqrpW3Gs-MI5kgVsMsKh3gjVAnE,478 +torch/include/ATen/ops/is_distributed_ops.h,sha256=0ChdDYg2mJCVhJI-BOYOMrjnJGb0Yk2kaqo7nOLaq7c,961 +torch/include/ATen/ops/is_floating_point.h,sha256=vknol7GiimV0ZvoPcpZT7JdFzoo2tiS9LaaVWQEMQPw,684 +torch/include/ATen/ops/is_floating_point_compositeimplicitautograd_dispatch.h,sha256=A06RbWpiRJ4J4i5_SmDn8j8mWdPdGioKZSH1QNYj2fA,769 +torch/include/ATen/ops/is_floating_point_native.h,sha256=3uoBelvwlH_8G1GLDBLQ9-Wu2D0AoOejwO1j7kXJfyE,481 +torch/include/ATen/ops/is_floating_point_ops.h,sha256=ZUIF5APP41gzuK510G1URzRX31d4dhfs_4nVE2asU9E,970 +torch/include/ATen/ops/is_inference.h,sha256=GPZ9z9v1E3wYYTD-_Jgz1yNnMIWr44hNYaujZv_0LIU,664 +torch/include/ATen/ops/is_inference_compositeimplicitautograd_dispatch.h,sha256=ec1er2INXV2_SN0Toc4IbcmTida63Ef-Z9l20OaopPU,764 +torch/include/ATen/ops/is_inference_native.h,sha256=Hb3BKZ61m4CzjjfzKLYO91AdloGDaJgS355Ahie_aR0,476 +torch/include/ATen/ops/is_inference_ops.h,sha256=zI6GbWYN5z6q4WCRi1mTCCOMATPhV4JmBHFHTAq4CG8,955 +torch/include/ATen/ops/is_leaf.h,sha256=g1GD-7ViwhJyXwCfl48d1Nnm-ENxLAsUrlt-_Qdadk8,504 +torch/include/ATen/ops/is_leaf_compositeimplicitautograd_dispatch.h,sha256=8YSwdYkXAlqtNyy6vzoHCZrf9kg3igxbHG8bKoTQOPE,759 +torch/include/ATen/ops/is_leaf_native.h,sha256=keMKE5Ho0rmkJLpF9MdkmhiumTI46031J1ctUnfcYsY,471 +torch/include/ATen/ops/is_leaf_ops.h,sha256=YtxhTHM4GrWAmbQmrHEkLMx48svFhsF2-1EjsFh2Ga8,940 +torch/include/ATen/ops/is_neg.h,sha256=a3XhM8P8eokjDVofx6BoUz7MCrczQrl0XAT7y6UKTFA,640 +torch/include/ATen/ops/is_neg_compositeimplicitautograd_dispatch.h,sha256=e5vmOK96ljDoaD1ZEQPEQ8zxrLyJ-8JTIeMgKSSJI4w,758 +torch/include/ATen/ops/is_neg_native.h,sha256=OfJinkQDDHJ5cpAaWYVX-sZGFBT_9UF6CA1VMqfAiKc,470 +torch/include/ATen/ops/is_neg_ops.h,sha256=j6mcsLRtq9tg7OU30VbAMeK2nwNLpER3aON29lY5HX0,937 +torch/include/ATen/ops/is_nonzero.h,sha256=25PkZLZEw0DuHPyvUugPPAQZv12GEsHA4EhKsqDjsP8,645 +torch/include/ATen/ops/is_nonzero_compositeimplicitautograd_dispatch.h,sha256=OQaAY2in-cvFGggANfAfZKV3BDAtaQCHqIyGSbD5vlE,762 +torch/include/ATen/ops/is_nonzero_native.h,sha256=ZXbNssowgL6LZm_1Q8kSMbcRmodh3_MIujIEaEdlowM,474 +torch/include/ATen/ops/is_nonzero_ops.h,sha256=zjNlmeAfWwTKWeTIT1wQOmcC93UOYAuFfs7ItLjMm6U,949 +torch/include/ATen/ops/is_pinned.h,sha256=newI-kNgMP2MGJ5AGCLqukGaNYE_zHs_chHLxhYZS60,506 +torch/include/ATen/ops/is_pinned_compositeexplicitautograd_dispatch.h,sha256=XvPDS86TxmonV3zEt7RgIVOSEJuxTJS9I8jagjfEJ3E,812 +torch/include/ATen/ops/is_pinned_native.h,sha256=MgWMN67Dk_AEsuebDxE0TiHxrpkF2vZWrxEmSFzlufw,757 +torch/include/ATen/ops/is_pinned_ops.h,sha256=wPqBfKBvRhhRBP6KK3Gl3cmH8M01YIWsLSup0l_Bog4,1068 +torch/include/ATen/ops/is_same_size.h,sha256=_hTc2pPP_4JyAy-Q6lDk0qW3OuWucInUAGRKOmmBdSc,700 +torch/include/ATen/ops/is_same_size_compositeexplicitautograd_dispatch.h,sha256=wAeTAaogkdseFll4Dul7AntZUVKfJbPlw09vsWq51A8,790 +torch/include/ATen/ops/is_same_size_native.h,sha256=IDOre-ZdaLgmLxNkvNsJPf282NmCS7u-z_qpW67bTto,589 +torch/include/ATen/ops/is_same_size_ops.h,sha256=W6TqO-2JZOJQ9GuPog2c0majy3h9F7oJcMD8r6qJol0,1041 +torch/include/ATen/ops/is_set_to.h,sha256=fTxqRU4TJZkZAoDM_sOVpcKU-V_ZTt29UUTBnlHbMxg,506 +torch/include/ATen/ops/is_set_to_cpu_dispatch.h,sha256=C44peVDc8STgF5TlW3naz32pjtVhxvXWuSwDqg_Yge0,744 +torch/include/ATen/ops/is_set_to_cuda_dispatch.h,sha256=7uE-ZcNeaYWMlZ21ayYc144zs83lk03eFWl0wrh-kCw,746 +torch/include/ATen/ops/is_set_to_native.h,sha256=8L1lG1QLtF4tZ6OSHChd1lclrzmnldlwWMpuuMUXe_s,500 +torch/include/ATen/ops/is_set_to_ops.h,sha256=dSyNvL_5SBhWlxnxR5zNhUBaVJSEgO83PKrIOrekY3A,1035 +torch/include/ATen/ops/is_signed.h,sha256=NacltfYFYJYBxNFFrCFEBiHu5UxwXOhEHsPs08UAq94,652 +torch/include/ATen/ops/is_signed_compositeimplicitautograd_dispatch.h,sha256=u884jU4_OTpQKNfWKRugrlpuLGpQacdZSKNz7saFGHQ,761 +torch/include/ATen/ops/is_signed_native.h,sha256=9O8Iu7h4jNUnniXUDwgWWxPm-QSdJAZe_zRYUVFkNlg,473 +torch/include/ATen/ops/is_signed_ops.h,sha256=j_JL3y02nXoELQoPUn2KyHmRQjZe_6MtaSYS7erhnu4,946 +torch/include/ATen/ops/is_vulkan_available.h,sha256=XL3VenzyJHw0l9Vs_-TGqGDOU1etF5RtnxN9t2AnL2k,643 +torch/include/ATen/ops/is_vulkan_available_compositeimplicitautograd_dispatch.h,sha256=HHFOPI52jZvU2ZZz2YXIVJucZIy6qadOfvDePH-_jgI,748 +torch/include/ATen/ops/is_vulkan_available_native.h,sha256=oHgVZ1IISzBKIF1S_DygV-HdCm-Ay2lXGYrZv11Hxug,460 +torch/include/ATen/ops/is_vulkan_available_ops.h,sha256=AsxW1mqmBnwol3rlGqLvAzWkvSEUBcYhAiryjeRr95E,899 +torch/include/ATen/ops/isclose.h,sha256=jQvO5rNxjWQh3jHMuanYsFOvYkNSh500C-VlumHQTGA,829 +torch/include/ATen/ops/isclose_compositeimplicitautograd_dispatch.h,sha256=2sPLXHrcWuqvEkhLXP_wSFANpRyDt6VpH5IVoYDmyFU,851 +torch/include/ATen/ops/isclose_native.h,sha256=KAdmtzsoh3-m5sLMXNvOjqT53Dfc_ZNaM44LNF-mgkU,563 +torch/include/ATen/ops/isclose_ops.h,sha256=bArZOpxNf0OMdGLg66Y5s_g3HOMoDhvBwjfU4o2t8KA,1210 +torch/include/ATen/ops/isfinite.h,sha256=rks7pzG5cJWLndec9BliAeNo39pHrPk1pLISzrPNBzU,645 +torch/include/ATen/ops/isfinite_compositeimplicitautograd_dispatch.h,sha256=6gIF-qr537lG4VF_zaUN8eX6t1JmilAHpTwSK2etg_Q,766 +torch/include/ATen/ops/isfinite_native.h,sha256=sBZTyfxXW_CrWd0bv_NbdDuZK5bXsWfYV7Hz6kdOCts,478 +torch/include/ATen/ops/isfinite_ops.h,sha256=JXi2CCJmgOSAO4EUPj97GoKVDKyR7Gb5lot1hEcydaI,963 +torch/include/ATen/ops/isin.h,sha256=gVP68D5zqMFGgE-gKuHdqWpxyCtNpkD7QQ-E0JeSlVE,3992 +torch/include/ATen/ops/isin_compositeexplicitautogradnonfunctional_dispatch.h,sha256=DbQSEtmQVYZYcTzdmbToOsTaJkczCnWxrgAOR1Y54kg,1139 +torch/include/ATen/ops/isin_cpu_dispatch.h,sha256=PIGEL0_12S73-xp3zUZjWt87LY15lu5yHfma-r8-E14,1986 +torch/include/ATen/ops/isin_cuda_dispatch.h,sha256=lnkeCo5IIHaxVBka7jb0rG0N96Z7hfYd4Y9na0VPrHA,1988 +torch/include/ATen/ops/isin_meta.h,sha256=tlVemH6WE_bx-8YvHl5xXG1C9fcpQ3tO8DofoqTfRx0,1034 +torch/include/ATen/ops/isin_meta_dispatch.h,sha256=r9pisiDzRKbOg2rHU-hd5srMZDR_hU4cfq6-RtDn5gc,1988 +torch/include/ATen/ops/isin_native.h,sha256=prJlb44u-Q7Ofnz4EQDi9yjgXnuxqAgrvmkqh874F28,1159 +torch/include/ATen/ops/isin_ops.h,sha256=-YUqQ5j8sUPSWSpJswI3vkXnBM-1Ndctx012P0FOT2Q,5407 +torch/include/ATen/ops/isinf.h,sha256=UIiWoXA9ddJYDAE9GfiEcJ1qr7TAOSEXq166n2RSn3M,1017 +torch/include/ATen/ops/isinf_compositeexplicitautograd_dispatch.h,sha256=2vxqvNiqAMknXD2tgc0XI0XPhKho-_3QdmaQ9uz9zDg,918 +torch/include/ATen/ops/isinf_native.h,sha256=5s7C8U0Qo7U-6plVr_OI_Mbq8El7B3kaKMmz8Ub12zE,807 +torch/include/ATen/ops/isinf_ops.h,sha256=Mi6VTEFhTK1s1w0NciAe5lwe16ltTafzzeSaKpA93mE,1541 +torch/include/ATen/ops/isnan.h,sha256=GT4b5CgydCCVnTvVI6Jsi5QlepDjF8hR2Har2kI60K8,1017 +torch/include/ATen/ops/isnan_compositeexplicitautograd_dispatch.h,sha256=0rerY-imE2tu_5ntwS1WYzpWFc_nfMmllp7BPHauBuE,865 +torch/include/ATen/ops/isnan_cpu_dispatch.h,sha256=RvGBhKcczUpWPCaNlnHs4yLGfy-9uJCZqPB_YMx5pkU,719 +torch/include/ATen/ops/isnan_cuda_dispatch.h,sha256=yeJ84r1Mf9u_6eB7k1NxWC--OHmdv1MGg9_Liiw3jfc,721 +torch/include/ATen/ops/isnan_native.h,sha256=tlw4VlTmve06SBA7mwyGIHceMdqep_1lf3FEDxJgtfs,742 +torch/include/ATen/ops/isnan_ops.h,sha256=OsAE-_-QYL2PqM1TTZ57pAdcOz4fyBRtK8CJD_f05Ok,1541 +torch/include/ATen/ops/isneginf.h,sha256=NWhs8un5NpzPePM_g0UALNIVKdT8l3f7Ocd_A8PxTFk,1047 +torch/include/ATen/ops/isneginf_compositeexplicitautogradnonfunctional_dispatch.h,sha256=na5G02vfqv_qZ06ZhunIX4FVd2OzbGBEJ8EKgcRYnx4,792 +torch/include/ATen/ops/isneginf_cpu_dispatch.h,sha256=_6-kZGLygWlMHzQNvQtJFcSDe4TsEr7fxpTgoW53y60,883 +torch/include/ATen/ops/isneginf_cuda_dispatch.h,sha256=-kgtLYxzmPSPTNUXJR7bX0iXrO9iSxmhJL-vEDidgzo,885 +torch/include/ATen/ops/isneginf_meta.h,sha256=xC6LFJlhu-Mg2DhGNObrWXs_KlY9qrDpEjp_Nv1rimQ,569 +torch/include/ATen/ops/isneginf_meta_dispatch.h,sha256=hApmHsMa8d_yZiCwzPRYeuqB_6XSISXrAAVguQ7_Lcg,885 +torch/include/ATen/ops/isneginf_native.h,sha256=tD6ZerOXp3qbeXh2g2Kq4QwRdyo-Anb9HGzORKrS-eU,979 +torch/include/ATen/ops/isneginf_ops.h,sha256=C1EVHdGdnNRbzuIcDK-wU3PAlbC3SDIEe_rrY_gVr_Y,1559 +torch/include/ATen/ops/isposinf.h,sha256=pHCwjhGomD48DddWmxF4DyHINOzg5EA2WSye1pK0MZU,1047 +torch/include/ATen/ops/isposinf_compositeexplicitautogradnonfunctional_dispatch.h,sha256=bkhRb_WOB7xTExZM9-UtXhWQ4GxJMVXHphjl119uNBI,792 +torch/include/ATen/ops/isposinf_cpu_dispatch.h,sha256=qwFtIg51JGhSsvkKHaVUSaocwRxmwEmVTlkygP8yfWk,883 +torch/include/ATen/ops/isposinf_cuda_dispatch.h,sha256=dUqldPPiINjCj26On1xOF9Xh-xfK5EekKc0zSFYzmSs,885 +torch/include/ATen/ops/isposinf_meta.h,sha256=v3zPTHVqC9jgna0EnlTOLmDQmj9maQmxk95JCWZJzXM,569 +torch/include/ATen/ops/isposinf_meta_dispatch.h,sha256=AwDhbHfTlqRygBVDMdrZn6dQ3likrfN9GnG27sEYFn0,885 +torch/include/ATen/ops/isposinf_native.h,sha256=sYIzKBLUk81X3rJV5oLBBX1CitGhUohpU3fnvG8Fq6U,979 +torch/include/ATen/ops/isposinf_ops.h,sha256=5LE7qFQtGXfCYVRuRwIpR2mtSxOVQeMTB0gj0dLJ5eA,1559 +torch/include/ATen/ops/isreal.h,sha256=qHUT3MfHwheKDXZCpV1hTOOUeKLu5EtqsFBxayiG3AU,637 +torch/include/ATen/ops/isreal_compositeimplicitautograd_dispatch.h,sha256=wssOt2_7Jl_wPKpeF8gBgvZigg7_2r8N1YK5V9G5Xyc,764 +torch/include/ATen/ops/isreal_native.h,sha256=UdiOQDEO09MYGCA8lAoWcRcsBD8uVDeZ5QhXVnIQtLU,476 +torch/include/ATen/ops/isreal_ops.h,sha256=Iiijjy4JGBhGiMaZu1ndBDXCrRi-TUt5Hdvygjjhbkc,957 +torch/include/ATen/ops/istft.h,sha256=qW63H6Pg4r8AFY_d3GP9dSyNhRWjMANVELDVurYGxgM,1238 +torch/include/ATen/ops/istft_compositeimplicitautograd_dispatch.h,sha256=gSuhyig2fA3hGgNr2KjRz4n5d63lxAQxKQh1nye-2NA,1092 +torch/include/ATen/ops/istft_native.h,sha256=65Lkmz1hJRghdVlIG0pUmegxgDdY3j1w0_tp-dPRglQ,804 +torch/include/ATen/ops/istft_ops.h,sha256=thTwC-JqqcLp3EzWCqtASVRACKWymCfbyFNV601cXM8,1800 +torch/include/ATen/ops/item.h,sha256=St4nKxc-jHvULDCLRHfQ2yG9FSqW04Vm_4zDPVE2RKo,501 +torch/include/ATen/ops/item_compositeimplicitautograd_dispatch.h,sha256=ZlQsPmxfYxn7eSvY3f1D_S6fYZrRSSveSxKGU6OrXA0,762 +torch/include/ATen/ops/item_native.h,sha256=7j1VOEcaUymj1VrWDkmsdDPLJfmIxyfR6QjoL0ys0dA,474 +torch/include/ATen/ops/item_ops.h,sha256=l6fW6hNlPILyFBmYgz-l1_VRcq4lz_-4q2LEhG-aSq4,951 +torch/include/ATen/ops/kaiser_window.h,sha256=2SvGDxCw8QStdeDLxPxLJTPU6fAEZlAGldlRCwSi62s,5004 +torch/include/ATen/ops/kaiser_window_compositeexplicitautograd_dispatch.h,sha256=QeHNOMuoZ6hVMVzgYYNVpIXVvwVbRah825cHrGUVXGo,2262 +torch/include/ATen/ops/kaiser_window_native.h,sha256=dzn1ZsjZG-lVmV8uMhO73egSI4IPHMDms2FNmBABBbw,1419 +torch/include/ATen/ops/kaiser_window_ops.h,sha256=CCK5b_JKr5PFFB2gwmdckqXeUD8QfWHe6Gl4NnMw8G4,5665 +torch/include/ATen/ops/kl_div.h,sha256=QBnkyEM3fO-1uX3VVxBONOqKNLZWPjqu1Y4WyMtQExs,818 +torch/include/ATen/ops/kl_div_compositeimplicitautograd_dispatch.h,sha256=cEFMesvwyJaiJr3hpFfTrd231Z6ZnUXKVb5vbOXWXNU,853 +torch/include/ATen/ops/kl_div_native.h,sha256=NzDYWTSXalneCpgjKPZbe6nS2cXbciCtYHJ5tw5mQmI,565 +torch/include/ATen/ops/kl_div_ops.h,sha256=8FNQxxIYNYkUrz-GDddKWJzQVtVCS0lKXPHaLUufP8k,1179 +torch/include/ATen/ops/kron.h,sha256=HQG8qFxOYdbr-gncmVOz0bzXn1T8u0mhI0oICsKvwpA,1148 +torch/include/ATen/ops/kron_compositeimplicitautograd_dispatch.h,sha256=M4rrYdqvzM-H0qSdsO59O-lkY57No4I6ENSI9Mkm-L4,993 +torch/include/ATen/ops/kron_native.h,sha256=uOOKoiEY6RBb71Mrw7CqRux1bLQ5D3crRKQXOX8cEVs,602 +torch/include/ATen/ops/kron_ops.h,sha256=lCJulcjhiZPL_xDu2h2LQIdnihIqXOULGj2DmprZ7G4,1707 +torch/include/ATen/ops/kthvalue.h,sha256=Li1SnZzOMNRcxLaB-0sjY91wkDpaYQzgIw-A7-YeeDE,9356 +torch/include/ATen/ops/kthvalue_compositeexplicitautograd_dispatch.h,sha256=H9cIpbRXljiJSbpywQJqIHZdETUN4cpI0SIuhm_fvDM,977 +torch/include/ATen/ops/kthvalue_compositeimplicitautograd_dispatch.h,sha256=MM_j-PYMmSNqwHCHpswVARJMGN-MIqz1oqjGp-xhSQA,1711 +torch/include/ATen/ops/kthvalue_cpu_dispatch.h,sha256=m8fOh6TI7_y4rqcuviqjhiHEdF4_KlZR7XyLmUiYO2c,1388 +torch/include/ATen/ops/kthvalue_cuda_dispatch.h,sha256=8XXz6iwSkmfTB4BKv98ykvXit1aGc19C-lE-ZBg8JoI,1390 +torch/include/ATen/ops/kthvalue_native.h,sha256=_V6C_gEuxAoOhD3lI0-gpOr2OsgLhESorAYKzjs6VYA,1202 +torch/include/ATen/ops/kthvalue_ops.h,sha256=HEB3fXFTDS4HW5Ls2tS2wn0wKnXvbCr8FdrdKcbMQUo,3953 +torch/include/ATen/ops/l1_loss.h,sha256=OrtttEHnjbtG0wWlRETfLIxUtnGetluUkZkl2ABXfxI,761 +torch/include/ATen/ops/l1_loss_compositeimplicitautograd_dispatch.h,sha256=DdwctjgHfcBJpq3IbK6ZSJlfoCeId2T4SJoq0F_Fs0U,831 +torch/include/ATen/ops/l1_loss_native.h,sha256=rfKHS3NQxglkbQMO4vsW1sWDdkxYyFc131ozVExI3pI,543 +torch/include/ATen/ops/l1_loss_ops.h,sha256=iFq59paL9rGeX8v3NfdPnW8XNDYIRdTfJYf9HUrNm0I,1116 +torch/include/ATen/ops/layer_norm.h,sha256=SDHNGubWt3KEEYB7cyaFsJToyCnhDdOIYfsDljIxM7c,2377 +torch/include/ATen/ops/layer_norm_compositeimplicitautograd_dispatch.h,sha256=lkM8SgUdPpTiXp73D1WHyPqG2xr4Eh8mcbT_y9zCbLI,1175 +torch/include/ATen/ops/layer_norm_native.h,sha256=2SH5DK7VzizB0iRor-HQobZFbdUAGNR8tVpyopbdPNk,660 +torch/include/ATen/ops/layer_norm_ops.h,sha256=AI52UozOeBfnDDKVidGxs1ISLCXOi7GZaDlU9lEfJaU,1499 +torch/include/ATen/ops/lcm.h,sha256=WAxB85TQzHs_xlUoTXRGUw1GIWRA_A15vMKR3IJlXas,1318 +torch/include/ATen/ops/lcm_compositeexplicitautogradnonfunctional_dispatch.h,sha256=cAGOr3tQDesg_kq6DxPStJVQjiQ_JZ1LnoT2_XLXxt8,887 +torch/include/ATen/ops/lcm_cpu_dispatch.h,sha256=EIqGnY-65IygmauP0_oPS_0Noeo7DtQE-qpapsaV-cw,1020 +torch/include/ATen/ops/lcm_cuda_dispatch.h,sha256=mH-0ksfTKxqtai8mGEwjGto_Z0weRSjShnJilzbFEyU,1022 +torch/include/ATen/ops/lcm_meta.h,sha256=a6sOgF_8FZEhjYlDqa3IPAy4tED3PaQUuQUGnxhloHs,590 +torch/include/ATen/ops/lcm_meta_dispatch.h,sha256=WZ8--OWURo4XcQEyY_OT0uBGK3s1AKvgYO_aRDcx1Ac,1022 +torch/include/ATen/ops/lcm_native.h,sha256=5xgIhIq-199A0Rrl4v6etOwMKqW7v-8Zq1srD_PlsH8,613 +torch/include/ATen/ops/lcm_ops.h,sha256=Juec4dqBVozH0ZWeCCZl80wN3LQ_eOfyUhy8pwWDOnM,2277 +torch/include/ATen/ops/ldexp.h,sha256=xu31A-Kk_gDmmvbgpX7dQNEREawlBl1XaOiuDQh3T8s,1358 +torch/include/ATen/ops/ldexp_compositeimplicitautograd_dispatch.h,sha256=aAt401aajRcGOgUSHa2dWY4Y4UHJ2IJ_8k89TR0TuaY,1072 +torch/include/ATen/ops/ldexp_native.h,sha256=Atk8nvM_FP7Usxs5W_z2F31bai0qdRi12-k3NOSLMRg,680 +torch/include/ATen/ops/ldexp_ops.h,sha256=NTIsyUtAuPAMXhM_ZxMlUEPiJoQ2vN7HyO4xVD69LMc,2315 +torch/include/ATen/ops/le.h,sha256=UZ9wdLNsdD6FZWHkRLAv8Go64WLOfa8k4ttUmizVuUI,1842 +torch/include/ATen/ops/le_compositeexplicitautogradnonfunctional_dispatch.h,sha256=nAFDacOx2DNEjeHaEtA44QHZSX5W4MlwF1YcJgvWt-c,1034 +torch/include/ATen/ops/le_cpu_dispatch.h,sha256=uhlqk131ERYt8VmsnuqP2p_91C3jOVHkcijSPAqQIKA,1366 +torch/include/ATen/ops/le_cuda_dispatch.h,sha256=fV_zhFJUL2NzCHJgbyLnKKPOiG3SPXxnyqTRCwl16Y8,1368 +torch/include/ATen/ops/le_meta.h,sha256=UbBHzYAeMAOf5AaQAHIPEg79BJO7-XIFTQ89ALnLoJY,735 +torch/include/ATen/ops/le_meta_dispatch.h,sha256=9ZuWvTOL5uQ9PO0A1yj_64Bh0uf3tVFGJrX6gWq2JFs,1368 +torch/include/ATen/ops/le_native.h,sha256=9EezCmoSne3tljQBNG7a24lfdI6diXWGoC06dwLyoOQ,1205 +torch/include/ATen/ops/le_ops.h,sha256=gMLuHvH9wHrP5viow8KeuCO8BPgVdj3r4M8XfLZx2hI,4201 +torch/include/ATen/ops/leaky_relu.h,sha256=ikRudcNj4XD2QkiB_4AoWCnbUugOZKyUfImwp2PZF68,1552 +torch/include/ATen/ops/leaky_relu_backward.h,sha256=JE_o7JCjXErXduK4MVe0HTvvBFcAps_XHW4fXq4qnNo,1818 +torch/include/ATen/ops/leaky_relu_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=JlDYL89-QIouDMoca884wLPQYT8XApBSRAbQ4ry3z6c,891 +torch/include/ATen/ops/leaky_relu_backward_cpu_dispatch.h,sha256=qnwO7C80nvREYGs3FpBFFyfRPOSTZMBXZ_faT7ZajB8,1194 +torch/include/ATen/ops/leaky_relu_backward_cuda_dispatch.h,sha256=7nXYkqzamWEWSL25DLwqZjClrHSdBAJAm950XI4ILwo,1196 +torch/include/ATen/ops/leaky_relu_backward_meta.h,sha256=FxXfy7fNdBPhEF-YJ3m9M9D4eRjso-kvGFLhTa7RqSA,668 +torch/include/ATen/ops/leaky_relu_backward_meta_dispatch.h,sha256=DTbDlJU99_xlks6iXwUEBc6RDnhOMwwz8XxeZvD7ZL0,1196 +torch/include/ATen/ops/leaky_relu_backward_native.h,sha256=ubQ6NmKM2XLlS_n2XyCX_h4cIGGLKS9PU4SqkeuEDaA,730 +torch/include/ATen/ops/leaky_relu_backward_ops.h,sha256=PT0BnXGYTjgG7ckaVnTdoRODuTtbbsr5WUvB-lEuzS8,2239 +torch/include/ATen/ops/leaky_relu_compositeexplicitautogradnonfunctional_dispatch.h,sha256=45bX3czHACelwUcKfixLNRmJ2GZ7gaBLWUHTgduZ3Wk,929 +torch/include/ATen/ops/leaky_relu_cpu_dispatch.h,sha256=MNnw9gneQFVevSNjYrd-zPmFWcW_64NhwWUAAVvHIP4,1099 +torch/include/ATen/ops/leaky_relu_cuda_dispatch.h,sha256=YpnBgBpwRy_wRWLjQaA3NcCz8DX-7oP3J57iqJksCEY,1101 +torch/include/ATen/ops/leaky_relu_meta.h,sha256=dduIvj81vS2fQRF1DULrUefqV2eKrDAOav7mjZQfyP4,606 +torch/include/ATen/ops/leaky_relu_meta_dispatch.h,sha256=UgSbPrZcazh0-M1-FTSRT6tstp6kmhSzck7iGWx-ulE,1101 +torch/include/ATen/ops/leaky_relu_native.h,sha256=St_oHeOkHGfdMpYda3R-tt8NP7t5aLWnfsFOT7F0PTg,995 +torch/include/ATen/ops/leaky_relu_ops.h,sha256=SfNkq_kLBK0Ka9kJNGWJ7D9UGs8jJBVR4zYveoevzJM,2436 +torch/include/ATen/ops/lerp.h,sha256=b3ls_C2uQuIg0j693HjFJhU2HexC5FCjJDSyyYyjn9k,2144 +torch/include/ATen/ops/lerp_compositeexplicitautogradnonfunctional_dispatch.h,sha256=k8mmxOxkIwdeJXsVaq8Ch5J7nps_YfZLW0UfXivqMC0,1142 +torch/include/ATen/ops/lerp_cpu_dispatch.h,sha256=cGDE7_7aMW-bmQ50UQnPdxFlmcmu19N2FvcdSU6oY-U,1582 +torch/include/ATen/ops/lerp_cuda_dispatch.h,sha256=46PgRppUorrhLx3oOHc3HLEg_f5fA4jkIw_wrMLTJ50,1584 +torch/include/ATen/ops/lerp_meta.h,sha256=6BbK73GPuDzOTtTvjBugu-8-hcQyOfBlyQvWpA4dFWk,789 +torch/include/ATen/ops/lerp_meta_dispatch.h,sha256=OAvSXM5utS3BzjkCyCRIj3OZMS0im-iHoX2YVs29Gpg,1584 +torch/include/ATen/ops/lerp_native.h,sha256=q3EQcU4vwYnsX660D43m4kLBg1W3T4AqQ2JiNwsOce4,849 +torch/include/ATen/ops/lerp_ops.h,sha256=cYZ_9p2CtG4NoqbVw3i0WqCtBWLjOtxQc73oZeKQzsI,4735 +torch/include/ATen/ops/less.h,sha256=K4n0KK6UFIh97c19wAJOjC-9W0PdgKOl44w0ySfT-rQ,1880 +torch/include/ATen/ops/less_compositeimplicitautograd_dispatch.h,sha256=h8_iLv2NZ_vWcXEJ2kE0oqxPktH5kRQxzaLZb5u7lAg,1426 +torch/include/ATen/ops/less_equal.h,sha256=O4XrMuCUDk3QwawYUP1He_hgfuLjOGmX5GVuIA2bXdg,1994 +torch/include/ATen/ops/less_equal_compositeimplicitautograd_dispatch.h,sha256=HldrZ7I4qFiLblsbZpNUp09lYq00e5bQXDatHI6DhKc,1474 +torch/include/ATen/ops/less_equal_native.h,sha256=LiO8Pyw0gmK2mS_FkwR9YloVrV526EzUQdHR4-w5Isw,968 +torch/include/ATen/ops/less_equal_ops.h,sha256=XY9jL9iR64wbU_GKM2mZNq7T4jWdnM98EAL6hQWbB8c,4345 +torch/include/ATen/ops/less_native.h,sha256=MS4m7F39M06IpFZW5Qvqi3gIxDXBYAQl-rUgrWilSCg,932 +torch/include/ATen/ops/less_ops.h,sha256=SzC6tiQJ9UNwYE2AwqHlsfIIW9U8e2Som3ab-cVXtQM,4237 +torch/include/ATen/ops/lgamma.h,sha256=HAYOWOonR9V4doyMagL8A5W2Uj6VjpjbUNjHnkqRG8c,1027 +torch/include/ATen/ops/lgamma_compositeexplicitautogradnonfunctional_dispatch.h,sha256=rZqhFXhcU-nmbZ2wOuD-u8KHNdgYMpRYQt-BzoxS8mw,841 +torch/include/ATen/ops/lgamma_cpu_dispatch.h,sha256=T8tQq41Ao_PKjQaKTYXKQJ03d9wMAtr9NEFUsgpJpS8,928 +torch/include/ATen/ops/lgamma_cuda_dispatch.h,sha256=MtY18bz8r_yeUjUFbl03hUIQGT8lhbqYbcBxrZu_FAU,930 +torch/include/ATen/ops/lgamma_meta.h,sha256=AiofU8RBmjA4QdoPyLU3PTINBJx51zjbo2Pb1frKi5Y,567 +torch/include/ATen/ops/lgamma_meta_dispatch.h,sha256=URa2AhwDKi5HIkPzGTb8jyjdf3PbplyHKUY8boVYgio,930 +torch/include/ATen/ops/lgamma_native.h,sha256=hLRCWbsa2SLWaxY5YRO5m9CHFQ235J9GiJT-3XLkv2c,596 +torch/include/ATen/ops/lgamma_ops.h,sha256=vb_RYgjgGIOC34XoYH2UjG69MoDsZeq9EvKVgCDDiLc,2046 +torch/include/ATen/ops/lift.h,sha256=tcnlL0QfuMJmB3Q96AwAmlP_COOTCHC-CKFVAEsyxZE,1007 +torch/include/ATen/ops/lift_compositeexplicitautograd_dispatch.h,sha256=s_qMNtOHoiiLcIa0ORiGWFYuUtN-sI25ELheo3nlNMU,915 +torch/include/ATen/ops/lift_fresh.h,sha256=DP7djgn4P9xUkbVFBdJxebVgb28KIlK3oCU40GgwQRw,659 +torch/include/ATen/ops/lift_fresh_compositeexplicitautograd_dispatch.h,sha256=Epp3eIkqwBXIoyRvlyOC37GqqputV8dEDUxfUlePUl4,768 +torch/include/ATen/ops/lift_fresh_copy.h,sha256=MopjZl2463_cPFJTWsT0IQ2TYI41xshlqpnNY2a001U,1117 +torch/include/ATen/ops/lift_fresh_copy_compositeexplicitautograd_dispatch.h,sha256=V638bvtxhLG_tfXr2hDynzA1nlm3F38RiERbWUGz8d4,885 +torch/include/ATen/ops/lift_fresh_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=mUq97k2g47V7UMj1bwth1mj96g3dmFpk93xGVY_hMXI,799 +torch/include/ATen/ops/lift_fresh_copy_native.h,sha256=nBB0tuDlWUF01phli5-TJ9sQfpWqP8cUGNCgeQGrwO4,572 +torch/include/ATen/ops/lift_fresh_copy_ops.h,sha256=IwN_o87AEAyU7ouYHn6yII_jhhIsDWFCmlCbFxLOSJY,1601 +torch/include/ATen/ops/lift_fresh_native.h,sha256=uq5XG5BqR28UCHIoh7RyPjotPfwsDedZWkziXmPMBfs,480 +torch/include/ATen/ops/lift_fresh_ops.h,sha256=IdKNhyirIje_UUAjOMqsgwgkgjr0R4Fvw6d46Qlatso,975 +torch/include/ATen/ops/lift_native.h,sha256=gG0soRJv4Sv1PUVlpX6QOUzxmfd_lTz9jX6iqOjWd5o,550 +torch/include/ATen/ops/lift_ops.h,sha256=88OHbJayesP9EKvblz2ipPJZudzL74s1ixrocDHcxF4,1535 +torch/include/ATen/ops/linalg_cholesky.h,sha256=mccDtbD3wniI717rr9d_NSCoimTtosQ55EHgebPfhNg,1243 +torch/include/ATen/ops/linalg_cholesky_compositeimplicitautograd_dispatch.h,sha256=AeQTce46XEZ1XMKLAvuoyta2I_xPueqUgkVSqn0ALUY,996 +torch/include/ATen/ops/linalg_cholesky_ex.h,sha256=AGdHRXNdWOmivZG9ip5o8sDiCm53ky3Jt6SwZhyKvBg,1661 +torch/include/ATen/ops/linalg_cholesky_ex_compositeexplicitautogradnonfunctional_dispatch.h,sha256=DmQ8sjVgsv-xvdPtLZ2jc6zlXH9nRqvTD7_is42Egx4,870 +torch/include/ATen/ops/linalg_cholesky_ex_cpu_dispatch.h,sha256=jp6nL0xHjHECvBbG-05mp0ZZH4oqU5bZ-NczqNyoWc0,1143 +torch/include/ATen/ops/linalg_cholesky_ex_cuda_dispatch.h,sha256=9KQ1cc9zfJyNExBdNjbpc5qfajZfbm9bW3uCRf6e_EY,1145 +torch/include/ATen/ops/linalg_cholesky_ex_meta.h,sha256=RuWJRIvLoc9_eXJH5o6Jt5VPaCHkUg7yrmU4rrrCM2o,610 +torch/include/ATen/ops/linalg_cholesky_ex_meta_dispatch.h,sha256=oZzyKuoZClkeng2J_RTrQGjLFgm_uAOvVnV9BU4twnk,1145 +torch/include/ATen/ops/linalg_cholesky_ex_native.h,sha256=9k2FWLquQXG8q6GP_dXjZXcFeco4xog55UzbxTQj9_A,686 +torch/include/ATen/ops/linalg_cholesky_ex_ops.h,sha256=v_kh1Y-oFbq0joo3Mo3V9CRvlJ2hcv1mquc7KrTkrFM,2107 +torch/include/ATen/ops/linalg_cholesky_native.h,sha256=zXoS7W1ed8lkHjJDrUrg-OU2eO9KQcZaJJXq39o-u_w,602 +torch/include/ATen/ops/linalg_cholesky_ops.h,sha256=qj6pY8TyigVfcwv-FH9MIEoePKDox8irm3WCi8y1tqs,1700 +torch/include/ATen/ops/linalg_cond.h,sha256=U63KWv0a6OwMGblG0xXOrGTMitl53zwrYk0cO854vKg,1977 +torch/include/ATen/ops/linalg_cond_compositeimplicitautograd_dispatch.h,sha256=hKTqwcicJYz5QNC_tUvGyCfjJ4cikQSG1pXWe0uITQ4,1369 +torch/include/ATen/ops/linalg_cond_native.h,sha256=gzNJzz-o5W3bqDDXOXu3L634GilYbTwUvLee8I3BeFw,839 +torch/include/ATen/ops/linalg_cond_ops.h,sha256=uxfU_3ky03yH82qBaZzC4wp4ewAQpkRV8JO1cCukkGA,3124 +torch/include/ATen/ops/linalg_cross.h,sha256=J4PYc3GXqHb4P3IGSirmwasVGz6rF3t1sapGo_45-HU,1327 +torch/include/ATen/ops/linalg_cross_compositeexplicitautogradnonfunctional_dispatch.h,sha256=WxdqUrIeN0dnAB1Dhsrvo4heiigJ1foqgQbNPkIrs-I,838 +torch/include/ATen/ops/linalg_cross_cpu_dispatch.h,sha256=GbUiLkfRAo3bBs_hP60FNHqbZtBvvu7k9t0HO5Uda1s,1018 +torch/include/ATen/ops/linalg_cross_cuda_dispatch.h,sha256=nJYsij1yAWkz-u6h9zD9C18rW1t6gYRcN_9KOlgo4dw,1020 +torch/include/ATen/ops/linalg_cross_meta.h,sha256=cmozQs4hq2uGVA-ifZp1oyeBPGVk8-St0hqMz0uITXk,612 +torch/include/ATen/ops/linalg_cross_meta_dispatch.h,sha256=llqEj0BPdym1Z4fNBLfVb4gdzOSVsbO2LeFRLPa_C48,1020 +torch/include/ATen/ops/linalg_cross_native.h,sha256=Z43iT3i-6P018UhGhJnph4IIeTjUKFLwD5-GdvaDLUA,766 +torch/include/ATen/ops/linalg_cross_ops.h,sha256=ljiNNqAOglQrM6nlB4juhX11Ti1NdvFhhTkyvWtyJUo,1852 +torch/include/ATen/ops/linalg_det.h,sha256=NxlvQ3YSDMqXeZR8CLZgNvLAlvgVVpIY3sJIXQ-6C8Y,1040 +torch/include/ATen/ops/linalg_det_compositeimplicitautograd_dispatch.h,sha256=dYCsBZYJzGdz__EwNHNbA4a0nbCgxN9B8p-1OA_6j8c,924 +torch/include/ATen/ops/linalg_det_native.h,sha256=YtoogQor_b16lp_wjmKw2FxpmqwUP2XcONU4WQq5rdI,556 +torch/include/ATen/ops/linalg_det_ops.h,sha256=YUexSFx_1SPZWK6R_lf7Eh8Tj7r_piFpu8T4zAKqx_A,1553 +torch/include/ATen/ops/linalg_diagonal.h,sha256=bSa-7Y9NPy-iiZZZwv4uJi7VrwSqgi2TjlZ38i5-LS8,785 +torch/include/ATen/ops/linalg_diagonal_compositeimplicitautograd_dispatch.h,sha256=PUqGbsFh5qRWriLNSflGHNEKFKsbKxCyFTz2dw2w7KA,822 +torch/include/ATen/ops/linalg_diagonal_native.h,sha256=cEuC_Z0H3BOnkaFavZKagq16uNJJjjRob1xbq5xwpLo,534 +torch/include/ATen/ops/linalg_diagonal_ops.h,sha256=4F8MkWXGKNu6E2LLzQBPWEDp5hx78J3ec4zwHpE3gXA,1139 +torch/include/ATen/ops/linalg_eig.h,sha256=_I4rdoZdJE21KODr_GvC28dmt9JA5Gms88f0BZZlBUI,1439 +torch/include/ATen/ops/linalg_eig_cpu_dispatch.h,sha256=IXX_aHDbu9PYzx7yxtEuljgMXaDewPUWb827O5NRo7U,1038 +torch/include/ATen/ops/linalg_eig_cuda_dispatch.h,sha256=H7rrfuazDX9pi6DtBjvZxco3-IS89JNEiz9yleGGRes,1040 +torch/include/ATen/ops/linalg_eig_native.h,sha256=Uph94TNerqK8Ud801qs-17psg9RKd3r0VlOBfPrRbX8,649 +torch/include/ATen/ops/linalg_eig_ops.h,sha256=fZpDQggoZhGvi1_Hxte7st1LYJrZsD4gam2AFFV9X2g,1918 +torch/include/ATen/ops/linalg_eigh.h,sha256=0qCOHzBomSW4ZETKlb4TaYH5m3gYXWJt6aIJI9y03uE,1548 +torch/include/ATen/ops/linalg_eigh_compositeimplicitautograd_dispatch.h,sha256=M7U0cEFF-8l5MHaH_NY4_w0D7R0qnDXUKdXLNk1yf88,1144 +torch/include/ATen/ops/linalg_eigh_native.h,sha256=RChyzFwn7T_Hx07VwOyh43qhvzyMszGlNLgJ0cXmTeY,692 +torch/include/ATen/ops/linalg_eigh_ops.h,sha256=OxbkFTAH5Jzz9gLJ3tyOdPm-bYfqXpOUSBeGEzlxJhs,2069 +torch/include/ATen/ops/linalg_eigvals.h,sha256=g02wDhfr1Kq5I8RU5VQJZWQkkpA65c76hvaaNiq3MnY,1107 +torch/include/ATen/ops/linalg_eigvals_compositeimplicitautograd_dispatch.h,sha256=WLOpJ_8pVIPUeTJWWrvLYYSr9UFVwQwftehDlxUduXg,772 +torch/include/ATen/ops/linalg_eigvals_cpu_dispatch.h,sha256=sf0tsRZxx9i3EWBCWwv9kJNwRUJujhz3yljugOM4C98,839 +torch/include/ATen/ops/linalg_eigvals_cuda_dispatch.h,sha256=avcvg216S9vIWDoq5XlgHHUQ2IsbkvsEEfie49qs_tg,841 +torch/include/ATen/ops/linalg_eigvals_native.h,sha256=I4_EhghHQ3UzU0heTawT6MEsMR5kPOlHVG-dBcTbtlo,570 +torch/include/ATen/ops/linalg_eigvals_ops.h,sha256=quhyFqlUOa013J33JHAZUMP1I-v8SVTnVnS4JrjRydU,1595 +torch/include/ATen/ops/linalg_eigvalsh.h,sha256=fLRDoNgmieWvWCr9QjcXjXvOCEFPYgJVqRNBZOul8uk,1254 +torch/include/ATen/ops/linalg_eigvalsh_compositeimplicitautograd_dispatch.h,sha256=d8dIOnhLKOFUXrncNdEQGrvs3_d_aQTvmNjzXnShNIU,1025 +torch/include/ATen/ops/linalg_eigvalsh_native.h,sha256=XBnQZILWEN3IqhriH3JySAyd_UIvooYMfLNdaG0Hwog,622 +torch/include/ATen/ops/linalg_eigvalsh_ops.h,sha256=e54A8K9wujuIcJczjy4c1Y3uyv2TQ7i-H-MllFSiQZk,1761 +torch/include/ATen/ops/linalg_householder_product.h,sha256=MHLqn-F9f_xn83eK-y4akafJ2oUxBZx061lr83NmzIU,1359 +torch/include/ATen/ops/linalg_householder_product_cpu_dispatch.h,sha256=bvrmTo0qKelasHOFD-6sMqk_utquKXUvu-emGLkJcYA,1012 +torch/include/ATen/ops/linalg_householder_product_cuda_dispatch.h,sha256=CZZJthOjlqWBINhAfbgeMrSa1fkNtH-e_mFTByXT_Qs,1014 +torch/include/ATen/ops/linalg_householder_product_native.h,sha256=zonwzwPCl5Cs8euK4ydWbmpcWyb9cIF50SnyQ58PJJM,644 +torch/include/ATen/ops/linalg_householder_product_ops.h,sha256=5wAe_uzSpcfjhDkcp4Cnw5EQJOvNuq_lbb-hiHm1PyI,1833 +torch/include/ATen/ops/linalg_inv.h,sha256=5X8o8qOIekq4sJGJJ4qOCoEXnHuDak5ojNppEv6uWt0,1040 +torch/include/ATen/ops/linalg_inv_compositeimplicitautograd_dispatch.h,sha256=xifdp404nbGnsKcgjlYZH3C5vYXt2AI5T3hJDBp0EXQ,924 +torch/include/ATen/ops/linalg_inv_ex.h,sha256=uxyB8m2K8-_6IoRLCWzs4LPZTSafH7x_LQN8g2EgfkY,1539 +torch/include/ATen/ops/linalg_inv_ex_compositeexplicitautogradnonfunctional_dispatch.h,sha256=y8IGNs_EPJG_OStM1lJshdO_oWINpDBaVu9FaO4449E,844 +torch/include/ATen/ops/linalg_inv_ex_cpu_dispatch.h,sha256=kGYXUMJSGuIF-4TZQFTx5K3oTE8O_5Ap0nm_-_fPspM,1083 +torch/include/ATen/ops/linalg_inv_ex_cuda_dispatch.h,sha256=8DyzJ7eKuZoJ9erFCn5zX7ogTCRIrXqGv3_U_TWym78,1085 +torch/include/ATen/ops/linalg_inv_ex_meta.h,sha256=EW2xtbDP8LJ1NPnP4QC2THx8JepRnwx-1HdCCqsgSxw,590 +torch/include/ATen/ops/linalg_inv_ex_meta_dispatch.h,sha256=cZ-MXYBoSiIhJ3mO2lR0A8b53lMirMGsvigO95H8zBY,1085 +torch/include/ATen/ops/linalg_inv_ex_native.h,sha256=u2LdsZMyqqN0m2pVxEwJqTe3vet7FRuiJudjcXvoN54,662 +torch/include/ATen/ops/linalg_inv_ex_ops.h,sha256=Y82Y3MQ1RIbu0sDS626t3PB0fB6lA3pFqyQVrYNd668,2011 +torch/include/ATen/ops/linalg_inv_native.h,sha256=DSyeeAWevZzyNKiI5J-WdFOmSa6olTkOwhyGUgVt-z0,556 +torch/include/ATen/ops/linalg_inv_ops.h,sha256=-SXOEkd-L3BhhcTbgETKPaGEfyy5gi3OrJkLHIUj9uU,1553 +torch/include/ATen/ops/linalg_ldl_factor.h,sha256=KqMB7ESsBdrrT0kb20KyISpoazfYKR7vgNTRuNVfq8A,1536 +torch/include/ATen/ops/linalg_ldl_factor_compositeimplicitautograd_dispatch.h,sha256=rLPIQKmR-hvDbhsNfxeamYsk9sp6OSehHs0I_5Wz4fs,1133 +torch/include/ATen/ops/linalg_ldl_factor_ex.h,sha256=bMScEhXdB0Zq7q0oswxwrycUOPYCrgNRBFH5P7N1lmw,1920 +torch/include/ATen/ops/linalg_ldl_factor_ex_compositeexplicitautogradnonfunctional_dispatch.h,sha256=XoLjJjg2Xgbe19pZplpYNkg7BzrQI08E0xGPlAInbb4,887 +torch/include/ATen/ops/linalg_ldl_factor_ex_cpu_dispatch.h,sha256=m8MTePggsQs0Nhw3JyHWCw4Ybsy8JxhVUhNG8ieg2n8,1242 +torch/include/ATen/ops/linalg_ldl_factor_ex_cuda_dispatch.h,sha256=EKIdD96tRVvOfjDzeBpt9PMsuS5gA43GrAKUeK94dtQ,1244 +torch/include/ATen/ops/linalg_ldl_factor_ex_meta.h,sha256=xUuBA6rTN4CH6lee_IlqtYpdw3LGEX_a6IooRo09Z8k,616 +torch/include/ATen/ops/linalg_ldl_factor_ex_meta_dispatch.h,sha256=OKxqYiYpXg2OHeW9Cn4NN57TeBXFaKjjuBIxqCFl_0E,1244 +torch/include/ATen/ops/linalg_ldl_factor_ex_native.h,sha256=AWMHC2DFvSUAQBSnKyOIrUJmEqb8ix6Ikmz_HU6Pn2Q,724 +torch/include/ATen/ops/linalg_ldl_factor_ex_ops.h,sha256=qJnQ-p6qMi4GGyFGoRIv90zFE8s87NvQOZ9FAltXeYk,2335 +torch/include/ATen/ops/linalg_ldl_factor_native.h,sha256=e9rKQ69CtiJKN4OVbyCuXajYdQHMNo48bjCTTJLmpwA,686 +torch/include/ATen/ops/linalg_ldl_factor_ops.h,sha256=bo3UalzIEYJV2Tj_Z4g8l95lashHwZEFnChKG0lal2g,2008 +torch/include/ATen/ops/linalg_ldl_solve.h,sha256=t03dhLEJUU3zOHa_tArVzRDb6A40h5IiGCYFa7cZnwk,1526 +torch/include/ATen/ops/linalg_ldl_solve_compositeexplicitautogradnonfunctional_dispatch.h,sha256=CV3VnzneqLbwlhVjHM7O2zDjR9HbSE7mR4X6Pby-8zw,869 +torch/include/ATen/ops/linalg_ldl_solve_cpu_dispatch.h,sha256=nLU_qlUPQbsrNp3mo2Mzn-ibSdThflElNnDKYxfaHI0,1108 +torch/include/ATen/ops/linalg_ldl_solve_cuda_dispatch.h,sha256=PWy4L7ZdrnQ_IAKEz4LlX0e_-JsjKtZCxSwaKusPsR4,1110 +torch/include/ATen/ops/linalg_ldl_solve_meta.h,sha256=aCb109CK7C6NeM7E1rf-TGqI_-G-fG7MWHI5mFQB_tI,640 +torch/include/ATen/ops/linalg_ldl_solve_meta_dispatch.h,sha256=tzqJiqVOMN5qcLSUIplJXZTaI2UslRcNgKcY80ANcmY,1110 +torch/include/ATen/ops/linalg_ldl_solve_native.h,sha256=47zC8XubtB4sQXag7t2H2ThD5xctv4pqSVeSeP3Q3wM,689 +torch/include/ATen/ops/linalg_ldl_solve_ops.h,sha256=1ZfssDIlsGPDDvxfaJRe6bBcYntAxQP91Ca7AI2iAf4,2044 +torch/include/ATen/ops/linalg_lstsq.h,sha256=x6ckfOTVRI7iBzc6alxa8PBreE7GpDRe5va1zsfTj10,2383 +torch/include/ATen/ops/linalg_lstsq_compositeexplicitautograd_dispatch.h,sha256=bU02tR0xyyVCynVnACWH7W13pthrtPVjq9R9obniNME,942 +torch/include/ATen/ops/linalg_lstsq_cpu_dispatch.h,sha256=7rMFmahk_QQYXOiHQvqMMgMhMU1SlDqiNvA0kDtRMzk,1317 +torch/include/ATen/ops/linalg_lstsq_cuda_dispatch.h,sha256=Ywup6yUkZpbIkX6Cjw53YtxXYntesVRwdNKkLy3U76A,1319 +torch/include/ATen/ops/linalg_lstsq_native.h,sha256=jocXDRFM8mKURkFnMBwjlbc2o5qq3EZkF4YVbGyte3U,964 +torch/include/ATen/ops/linalg_lstsq_ops.h,sha256=Pi7Px8SRf63RUSmi2LZ2A85hWvPSgaFx2c5myDEjsnQ,2934 +torch/include/ATen/ops/linalg_lu.h,sha256=mDVGF_VUr99-9v3tMLITBNfdkXrnT0Q1ZADkgtlnrtM,1475 +torch/include/ATen/ops/linalg_lu_compositeexplicitautogradnonfunctional_dispatch.h,sha256=smtmNY4C8YljZ1ykgna3nAc41ErMW43WWQg6y47dWtE,843 +torch/include/ATen/ops/linalg_lu_cpu_dispatch.h,sha256=Xp7_D4L1il7ip5dwwtKO_Pqz3OWh6N0JhFNzzjfoVIY,1099 +torch/include/ATen/ops/linalg_lu_cuda_dispatch.h,sha256=uiYoc1rh5c-9GtJj34CAPP-11HZIBg5k4eDnzkM0JtE,1101 +torch/include/ATen/ops/linalg_lu_factor.h,sha256=OG-Rj8tApuEC_OrboWMOu8U0ErUjd1Hp78SFfEf1rdk,1458 +torch/include/ATen/ops/linalg_lu_factor_compositeimplicitautograd_dispatch.h,sha256=8vUJQ8riaNtEXmkP4XvXh5qDoZu45IrXYH88MHa1EiU,1107 +torch/include/ATen/ops/linalg_lu_factor_ex.h,sha256=x8vDtiXnaPfd_7GclGzSmhbG_hFA6eLM7BAzFWu_c2U,1842 +torch/include/ATen/ops/linalg_lu_factor_ex_compositeexplicitautogradnonfunctional_dispatch.h,sha256=AuCmBCTJMmmgAt5ZTS_I4yS8dWyK9zES31pa99w-FFM,878 +torch/include/ATen/ops/linalg_lu_factor_ex_cpu_dispatch.h,sha256=R1P9J-kB3kP7sqH5Lc5ZT5odnFN9JVd10j4YCIm4t1M,1216 +torch/include/ATen/ops/linalg_lu_factor_ex_cuda_dispatch.h,sha256=Mn2CQlZLkBe1FhWH7rMyuCrAp9NzLyay3e4ZgDDw4BQ,1218 +torch/include/ATen/ops/linalg_lu_factor_ex_meta.h,sha256=rjQUzXSgk8PTiDyXg1QOfaXEwiIZBtM7pq5XAPyF2VU,608 +torch/include/ATen/ops/linalg_lu_factor_ex_meta_dispatch.h,sha256=eXbcXiFGM_Ikqgela1zN1EglIZilZxdeyYAHwZUwU14,1218 +torch/include/ATen/ops/linalg_lu_factor_ex_native.h,sha256=sXr_bY4BkYxF6mydelT9rOIYG6N7ImxLYb3gn5gVjfo,714 +torch/include/ATen/ops/linalg_lu_factor_ex_ops.h,sha256=yUlDzIq031vQg6BQvOMWRK0mgOAP356mEqGAB-U7pGo,2285 +torch/include/ATen/ops/linalg_lu_factor_native.h,sha256=nFScBn0_GaGrcOL5dYOn5G_cDuezA5ditCg13m1a7Kg,669 +torch/include/ATen/ops/linalg_lu_factor_ops.h,sha256=gFsYI4DoXutTv13Dok8jPFo0mPQLQg62d3V5HY7HZ5w,1958 +torch/include/ATen/ops/linalg_lu_meta.h,sha256=zT_zbkvO2E3SX1phQCrWmatz-VMWI15g7X41JPboGZk,579 +torch/include/ATen/ops/linalg_lu_meta_dispatch.h,sha256=B8teXxNTtx2YftlFlWnvYJz8Nw3QJVRb22GZmskEWno,1101 +torch/include/ATen/ops/linalg_lu_native.h,sha256=3u7iw-jc2b7jJ6zd6QaN9K-akf23Pr-5VP-3hf08eZo,656 +torch/include/ATen/ops/linalg_lu_ops.h,sha256=yiHAUM8z-IoLyEKqHUsAHPvZOtdlQfMM_TY5gDZoffU,2042 +torch/include/ATen/ops/linalg_lu_solve.h,sha256=iJAExg3zzsIoD9y8BQ6_yi_s4JuX8iVgKWPkZnzbK30,1607 +torch/include/ATen/ops/linalg_lu_solve_compositeexplicitautogradnonfunctional_dispatch.h,sha256=yDxFDDhx0T8KutdXHAJhMf9fnn7Iu4JOymmVwAUVA9w,882 +torch/include/ATen/ops/linalg_lu_solve_cpu_dispatch.h,sha256=Pogx7Ji1bVUqqADhPj-IyHf44YPpzm6VLgLM-h6K91g,1142 +torch/include/ATen/ops/linalg_lu_solve_cuda_dispatch.h,sha256=kYq9fGi2__Qzd9GDG9Klte-ryXKZDcl5vbaTSozunG0,1144 +torch/include/ATen/ops/linalg_lu_solve_meta.h,sha256=qoya0CrfMmyDyn0Idiqpbc_wc2l_9xDYOEEqknlVeSE,648 +torch/include/ATen/ops/linalg_lu_solve_meta_dispatch.h,sha256=nw642a0aRVtBLFvBXa5E_80BBLR7sT621A0pDE3JanE,1144 +torch/include/ATen/ops/linalg_lu_solve_native.h,sha256=f5Ylqdyo6g9W7SyNHCinlM-2fHOJlE8ZHH23EakDHo0,695 +torch/include/ATen/ops/linalg_lu_solve_ops.h,sha256=WI0fY-Mdb47wKZJl8_OJebF_RPPciNVrqn3Ht3KtOmA,2114 +torch/include/ATen/ops/linalg_matmul.h,sha256=UHNPIVqQs3qS5eqVdwu5voNlg_lAWv2mXucDBqix55U,1238 +torch/include/ATen/ops/linalg_matmul_compositeimplicitautograd_dispatch.h,sha256=Q3uz6RawtS4rq_yETtDzm23bP4v35zDesmU8KjkBj8I,1020 +torch/include/ATen/ops/linalg_matmul_native.h,sha256=n0S5DH5lLZYf6xyZSpJ0s_eNKV9P0iSryN11rm91MvA,620 +torch/include/ATen/ops/linalg_matmul_ops.h,sha256=IMd6CXJlI0zBLLubOnRHa78YmcovE2ZxXBJZZwl24ns,1761 +torch/include/ATen/ops/linalg_matrix_exp.h,sha256=ovPeP--cw3ZkDup5C4kXmtRjUOogM_nZVM7y9AGrIVw,1137 +torch/include/ATen/ops/linalg_matrix_exp_compositeexplicitautograd_dispatch.h,sha256=m7KVL5YQSOj0jmGsp6hX59dYJ14RQgYKiy-_kL3PsyQ,889 +torch/include/ATen/ops/linalg_matrix_exp_cpu_dispatch.h,sha256=brPS4ishlrgIM0GQYTkpMfzy5NC14JfUImajpcjnSX0,731 +torch/include/ATen/ops/linalg_matrix_exp_cuda_dispatch.h,sha256=skDxjmJFuCVge-yFNbu8ZZzu9Z3vxQnRtMwD7T3h2MQ,733 +torch/include/ATen/ops/linalg_matrix_exp_native.h,sha256=rkqhIfaXqR2WGXNvpQv9Hn2u1gVCiMer_L7Bk555JMA,576 +torch/include/ATen/ops/linalg_matrix_exp_ops.h,sha256=JWSNGbeM1WILW1FsH2eYmF31zl7ENiQmHw_uTKN-gRU,1613 +torch/include/ATen/ops/linalg_matrix_norm.h,sha256=UT5zhCE82UuVwTYwAKN7Zh_zFYEdCu6yic6aN3xf5Pg,3159 +torch/include/ATen/ops/linalg_matrix_norm_compositeimplicitautograd_dispatch.h,sha256=1trsOTo3butPXz_b-lWyFVctmC9AFx7btx53npcPdJ0,1914 +torch/include/ATen/ops/linalg_matrix_norm_native.h,sha256=ArcJ--ZjtOpwHsBVxu2x3libZepqiIDQVQlUsSY0Rvg,1186 +torch/include/ATen/ops/linalg_matrix_norm_ops.h,sha256=qseRGoX-YU4jroxbvt0CYU94fRGZjuzE_U0qc20k5Gs,4216 +torch/include/ATen/ops/linalg_matrix_power.h,sha256=5dtr0lg53OA1l3L7QNOTB4qjSyxdNigXVFxSF0iHnDI,1220 +torch/include/ATen/ops/linalg_matrix_power_compositeimplicitautograd_dispatch.h,sha256=3sHPktmIURdB8o_H5ehBCYGQBKRUcb3j3XyhXxILems,993 +torch/include/ATen/ops/linalg_matrix_power_native.h,sha256=oiril8Aply293UCVBdY-5zdGhA3471awNWivA7oqrXs,602 +torch/include/ATen/ops/linalg_matrix_power_ops.h,sha256=7UlczY_oaF2ZI_bX27KWrBsOVxPCgYXxupf7G3Ai0-E,1701 +torch/include/ATen/ops/linalg_matrix_rank.h,sha256=EaopYIYh82sANbfPUnmZ48KQPVnGEnMu6Cy-r5LGhYw,4915 +torch/include/ATen/ops/linalg_matrix_rank_compositeimplicitautograd_dispatch.h,sha256=cEyVGQUFRcVLxP75EE_-eXCROMl4MxmdgLrss34GvKw,2496 +torch/include/ATen/ops/linalg_matrix_rank_native.h,sha256=9UH4DnHzhiEs1PWGnjLIhNxIhHAmIYLgxZGiMf1yAoY,1598 +torch/include/ATen/ops/linalg_matrix_rank_ops.h,sha256=5vGz3pXJ5L3q1XkomPdX-ZZtIIIvdDU7tA3q1hPMzTE,7082 +torch/include/ATen/ops/linalg_multi_dot.h,sha256=8fYb3sRqyGgNSMB_iIPeMVsBwZBNy_9AX8AqbiXEGFA,1148 +torch/include/ATen/ops/linalg_multi_dot_compositeimplicitautograd_dispatch.h,sha256=bXaWoHpaT67VqY7UPfhALyd4AzXT1xFQUYeTIp5XiiY,948 +torch/include/ATen/ops/linalg_multi_dot_native.h,sha256=vlyyieGUdiunWUiV_dxLGF5MGJ_DrRxxLvv4SQi0vWQ,572 +torch/include/ATen/ops/linalg_multi_dot_ops.h,sha256=rnoKcZnEXAz8ZFe_0AVAzem14pkt_Oisnuh_MeZCl0s,1605 +torch/include/ATen/ops/linalg_norm.h,sha256=mt9FuRNl2x3KzHD-JO1R8gO-s_pTNTDwocbXoyoV_So,3165 +torch/include/ATen/ops/linalg_norm_compositeimplicitautograd_dispatch.h,sha256=owa4Qufyld8ARQq-jhdgEvJ6wEts_4eZy33EVl5yn2U,2017 +torch/include/ATen/ops/linalg_norm_native.h,sha256=E7ojaYXyIVhjRseKGQZiVbutRr0LM77n-DovyxVqIpw,1247 +torch/include/ATen/ops/linalg_norm_ops.h,sha256=uNezJ8vYnV1L911J3cSNmdXorypq3Z3N7L-AdhleUmg,4326 +torch/include/ATen/ops/linalg_pinv.h,sha256=7SCnoboCoaNThTcVMVBJEQ3m9jL5PvxywE7cM9IkYt8,4686 +torch/include/ATen/ops/linalg_pinv_compositeexplicitautograd_dispatch.h,sha256=g5Oo7lDsticDXlctTZJ00YxqvqGS1b2o1vJRGYCeWm0,1089 +torch/include/ATen/ops/linalg_pinv_compositeexplicitautogradnonfunctional_dispatch.h,sha256=XHXDU1STjIrDjPVsG2W2dMJsk-7rkjatyYZFAL03i5s,907 +torch/include/ATen/ops/linalg_pinv_compositeimplicitautograd_dispatch.h,sha256=MpN03GIu_UC98yBieVcu80CIbyP-qVnwNFk9Ra5ZUrU,1868 +torch/include/ATen/ops/linalg_pinv_native.h,sha256=0ycvuc0e-zo8BTU8XO0dpOt5XC8WwG6rsX2QGFrRRQw,1546 +torch/include/ATen/ops/linalg_pinv_ops.h,sha256=fQQdLFqQeyafYBhPsVOhKIZPyigCwr0-HL2H4h7q_pI,6938 +torch/include/ATen/ops/linalg_qr.h,sha256=TDnCkeluwcZD9Fbjmli6spiDRCXrhebv3_TuOsYhdJM,1380 +torch/include/ATen/ops/linalg_qr_compositeexplicitautogradnonfunctional_dispatch.h,sha256=FCJ9MjgtDChbM30XPAGtxzi58IARZx8Pix-jSwWHvYk,848 +torch/include/ATen/ops/linalg_qr_cpu_dispatch.h,sha256=euAJ4D_Rp71zYwdcpiG9s4ytOrpyg5ZVY2qlu6fzxOw,1073 +torch/include/ATen/ops/linalg_qr_cuda_dispatch.h,sha256=zb2_m3CL3xSR6-_W538KkXt04STnT-K4IAoEZSUK3nU,1075 +torch/include/ATen/ops/linalg_qr_meta.h,sha256=69b4u8PGrTElco6qUp9uyDo23x88HrSBabKjf0kRxvA,590 +torch/include/ATen/ops/linalg_qr_meta_dispatch.h,sha256=xf8OjPcSwRXrluAzKXJerNZIyVrDsQE-bQpsDwyYLGo,1075 +torch/include/ATen/ops/linalg_qr_native.h,sha256=s5Sj4U2HQu3MmLShfnOUbGGubG4rsa5HfDkIGOuE1k4,645 +torch/include/ATen/ops/linalg_qr_ops.h,sha256=tfOM_xcVPtGXFA7d1SVGxAb60nM25YeWISx_mABbyvA,1957 +torch/include/ATen/ops/linalg_slogdet.h,sha256=-1iDhHqCliELinC6-tK5fDz3N2PpFJGLht5EHWDx5cA,1362 +torch/include/ATen/ops/linalg_slogdet_compositeimplicitautograd_dispatch.h,sha256=di5jHc0VrIubufgKqQBOH1etWRrc2u9r9uc_CokyQsc,1065 +torch/include/ATen/ops/linalg_slogdet_native.h,sha256=L-dkXH2iyhOmiycwmU3sR41V0NZ5V6ZnqVVQ0acSr7o,641 +torch/include/ATen/ops/linalg_slogdet_ops.h,sha256=4J8WAfyHz2f3PwNGpWYes9JfRrmSxm2y25252D7lef8,1874 +torch/include/ATen/ops/linalg_solve.h,sha256=sLMX-LE3BO5uoJjytdqiK0val80leGsHOjbZsVwqCKg,1277 +torch/include/ATen/ops/linalg_solve_compositeimplicitautograd_dispatch.h,sha256=8cY-0PMm0sj75iR6eBQOs1kYVQj-KBEnWBDhHw1FuNM,1039 +torch/include/ATen/ops/linalg_solve_ex.h,sha256=ZBKbTpEbpUMHf-CNuflFxdJKU90uMti4impwUHCGbmg,1748 +torch/include/ATen/ops/linalg_solve_ex_compositeimplicitautograd_dispatch.h,sha256=HZUPTWcwao_GoW3OhxMFul8gRH0OHZBZ1C3eANupxFY,1240 +torch/include/ATen/ops/linalg_solve_ex_native.h,sha256=chpW9FdLE2omBoybjznQycG5tZZcYuC609WuVeRgvTI,755 +torch/include/ATen/ops/linalg_solve_ex_ops.h,sha256=fx-xN37wtiupi7VkQNSAvYiw_pfJa8TGNVGL_EFuHKQ,2242 +torch/include/ATen/ops/linalg_solve_native.h,sha256=NOpZbmUSrQwCOhDEsie8lp8H5pLkHTY8toNkKKpNhbk,631 +torch/include/ATen/ops/linalg_solve_ops.h,sha256=rWsf6Hbs_v-hIkUWZS9vqWfTjXR-zx0iNm7gYAjfios,1804 +torch/include/ATen/ops/linalg_solve_triangular.h,sha256=xWnKxWRkSAnPZ3RdiAfxhOlgBYV17WK7NcKoYmCNN_8,1702 +torch/include/ATen/ops/linalg_solve_triangular_cpu_dispatch.h,sha256=4U0K_-QB9weJUUAgCgnwIcCnvo1fDkGLZFqoYyjmcHQ,1145 +torch/include/ATen/ops/linalg_solve_triangular_cuda_dispatch.h,sha256=CQUuMO8zIXOdehSoQQaf1XJpppDNtM9HQ8dmk-yZjIU,1147 +torch/include/ATen/ops/linalg_solve_triangular_native.h,sha256=qa7zSZHUORfmhJEjQTssMvxysbwU4lyRXGYFsVJg_wg,729 +torch/include/ATen/ops/linalg_solve_triangular_ops.h,sha256=uA9GoTNv6L4UkYihar5W5k0sQAtOLu36Jx87jqZSnSc,2116 +torch/include/ATen/ops/linalg_svd.h,sha256=q_C2yCoZso1DLu1FBqQ_elxbNlC8VcHmksQo6Nzch3g,1792 +torch/include/ATen/ops/linalg_svd_compositeimplicitautograd_dispatch.h,sha256=ClT8lxwaMZLogmilMHvBALfnzgAgXN1wkKPJTle0Xhg,1328 +torch/include/ATen/ops/linalg_svd_native.h,sha256=SpFKaUieXIO7IdiW7SyreWkKnTizDKWg48LmahBHeAI,807 +torch/include/ATen/ops/linalg_svd_ops.h,sha256=cFJhjbgb9oi86oOy7Ub-9-f3hvbxFMcUKEDoW_RlLrA,2369 +torch/include/ATen/ops/linalg_svdvals.h,sha256=ZF2m7__CvcqKGjyEuzNUuBtcvqkPsYSU9oqpDre1pEM,1317 +torch/include/ATen/ops/linalg_svdvals_compositeimplicitautograd_dispatch.h,sha256=mkSpLo7FHRSmsiTf1ekCnJIrodJl-fMZsHSxbd0bZ_U,1092 +torch/include/ATen/ops/linalg_svdvals_native.h,sha256=mlNmowXp6Ae08ekHJ3mKMa9JBdgl3Ly_lXTkMyjn7eU,663 +torch/include/ATen/ops/linalg_svdvals_ops.h,sha256=teESZP2Cl05Vi2aPeeqf402XwfyykR-ZLlJ2ua4G4IE,1854 +torch/include/ATen/ops/linalg_tensorinv.h,sha256=jTN-rX1Pw3xkTdBwVOVZfwzTB54MnUpe8asRGl_GV1c,1218 +torch/include/ATen/ops/linalg_tensorinv_compositeimplicitautograd_dispatch.h,sha256=oBf9t0vCRaKFN8v36An5TaBsmM7utTyFWiKoNQjrhYc,994 +torch/include/ATen/ops/linalg_tensorinv_native.h,sha256=AgCcbB2Qr0xZDTC2KOErVgZHh3UNUfxtbdo4tMC9ebo,602 +torch/include/ATen/ops/linalg_tensorinv_ops.h,sha256=1qhjXCINB9Z86tNS65wLXPGKXqqhM9a887fIC5OyRes,1699 +torch/include/ATen/ops/linalg_tensorsolve.h,sha256=A6ZVM-a-_slGAbActVI8lcXqL8fZocxaGGVaq2ViRkg,1480 +torch/include/ATen/ops/linalg_tensorsolve_compositeimplicitautograd_dispatch.h,sha256=00A-B2Ulj7kv4kg65hBQgRKlTBB0F6xvL6gbkr8XMNY,1155 +torch/include/ATen/ops/linalg_tensorsolve_native.h,sha256=WkffWVTHq0bnZ03LE5NXnZp84k9a74sQMpXI5emVU5o,705 +torch/include/ATen/ops/linalg_tensorsolve_ops.h,sha256=bUiUpUG0VXg4Zqv9tJtLcaZEIuBXUMqaJg2mAZE3hvM,1997 +torch/include/ATen/ops/linalg_vander.h,sha256=dsCH7dzhgvR6iqDSECEAtjd4ptB6yNOTqmgkv6HKF7I,1586 +torch/include/ATen/ops/linalg_vander_compositeimplicitautograd_dispatch.h,sha256=TUMOCoy3Ya9COq_GAMAO6ESIPv5GwIGPH9Z15bJeokY,923 +torch/include/ATen/ops/linalg_vander_native.h,sha256=xPhMS7REgIxuD9pqXt4slvdSPMh3kPDrhFv_dEUHNh0,534 +torch/include/ATen/ops/linalg_vander_ops.h,sha256=Vd0dpTHrQKnb-22wvm9U_b3DUmRFj6xSitsX1InxVtc,1082 +torch/include/ATen/ops/linalg_vecdot.h,sha256=0Zzm2ZbvzTFbvaGips7D9IFw_UtFCbV_ExfRuij9KXc,1274 +torch/include/ATen/ops/linalg_vecdot_compositeimplicitautograd_dispatch.h,sha256=JNyVgc0Uml-umeeX2Jh2-odMb3qOTkShubw1fYz8N8o,1044 +torch/include/ATen/ops/linalg_vecdot_native.h,sha256=cxdsGXFjeDcPcW-WXciChbbg3Xfz-HCXlcAQjC2mDbY,635 +torch/include/ATen/ops/linalg_vecdot_ops.h,sha256=8EGpWzE1C1DXM8HmvGpBHjnOhwg7GOGQdwpoSATT8dA,1816 +torch/include/ATen/ops/linalg_vector_norm.h,sha256=HTgBGF_k61QnS6FKZlxFE7XEiliKhLLEaiMA-n1Nxxs,1850 +torch/include/ATen/ops/linalg_vector_norm_compositeexplicitautogradnonfunctional_dispatch.h,sha256=sWu91oIuE9anCXLQRj1zcK724Pmfwg8sMFaTfHdwMJw,946 +torch/include/ATen/ops/linalg_vector_norm_cpu_dispatch.h,sha256=QcVIXFhWRp4awoRfHKbJslH-siDlJV9HkC0DFSJ3qaI,1307 +torch/include/ATen/ops/linalg_vector_norm_cuda_dispatch.h,sha256=MP0y4yybRUcAKL1Knkemc52ZPMp1bOTL5pr-xp2hhNc,1309 +torch/include/ATen/ops/linalg_vector_norm_meta.h,sha256=6Rvx9jirUISjQ-lfpjYfdJsokvxRcLQXYr2LqfwTtvw,685 +torch/include/ATen/ops/linalg_vector_norm_meta_dispatch.h,sha256=d40xc2O19KxySDs-prhXfJqwB6YacTvKQMIRkgJIz5E,1309 +torch/include/ATen/ops/linalg_vector_norm_native.h,sha256=lbMCy3Adm3BP_SEWG90bMougED4tV1kz6qKvBcsnl44,738 +torch/include/ATen/ops/linalg_vector_norm_ops.h,sha256=5xjgkG07tJXHjLJZOjPgOgSJFLtEsYJPV3SuVzeA1Wg,2366 +torch/include/ATen/ops/linear.h,sha256=RbnkYe2aaGwfpCx19_2a2fAVLlbDEoKZH6qnEIGc1dM,1393 +torch/include/ATen/ops/linear_backward.h,sha256=qo6Z8abf8cw2xn0-8Xa_2IkrWmRvXj87x-2BRGz0mxQ,2026 +torch/include/ATen/ops/linear_backward_compositeexplicitautograd_dispatch.h,sha256=x8aT0z87AANsyzfWu6-B6cBrKo3cAe4XCTM7UVWAslw,1229 +torch/include/ATen/ops/linear_backward_native.h,sha256=Q6nmae4kZM3NoU38r2mpQchgPqmnF5m_O8DJqMMbW28,880 +torch/include/ATen/ops/linear_backward_ops.h,sha256=UcKb8Q33o2VambqGIP7ktHy06XDtuhTc3rHsagNToSo,2622 +torch/include/ATen/ops/linear_compositeexplicitautograd_dispatch.h,sha256=OYqpLbi_YuCq-40FAWLopipnPGe059Izwj3kFO53pt8,1010 +torch/include/ATen/ops/linear_compositeimplicitautograd_dispatch.h,sha256=_vxI_7noemSVxhonHddPBDWraV8YA9X4QzW82Ve0M3g,837 +torch/include/ATen/ops/linear_native.h,sha256=zojCU40JDShK30BNJvpNlR7dHCmM_K6pyRGNOR0xBv4,831 +torch/include/ATen/ops/linear_ops.h,sha256=BgbsPFzer553Koa8L1Z1q8l6hiASvkK11SKL2wG44TI,2011 +torch/include/ATen/ops/linspace.h,sha256=XKm6i4zkXaTEqZLjXDfnB2Ju1IuyGaN2g0zZLElpkTE,6876 +torch/include/ATen/ops/linspace_compositeexplicitautograd_dispatch.h,sha256=KYjQUjdc29e5ohCBYXk5Us3lvNj9oUNcFk0Rchrk3TE,2901 +torch/include/ATen/ops/linspace_cpu_dispatch.h,sha256=ulTqqc-sXxiBRDzc6AL_W6IcXoCnwS2Ysmy8aBGb67c,907 +torch/include/ATen/ops/linspace_cuda_dispatch.h,sha256=JMvkDEkCk-8XNIPY2d6NP0bTBfTkZS1V9I-YyLKp30k,909 +torch/include/ATen/ops/linspace_meta_dispatch.h,sha256=tkxveHfnIO5T9J0loBOcuyOII39B9qDRa1I2w0rD0BM,909 +torch/include/ATen/ops/linspace_native.h,sha256=0Ry7Ehlz5Y-PYDcwXuKYyWHunIc2UltEhg6F-emXAsg,2039 +torch/include/ATen/ops/linspace_ops.h,sha256=Gd8t6guRCTU5uswU9TFN0gNUpBZ8LeFwGvaxZn_qKTk,8162 +torch/include/ATen/ops/log.h,sha256=_DuI0qiAU-lkAYGvnbGTTObSVxL6puTf15Hq-fCq7a0,1130 +torch/include/ATen/ops/log10.h,sha256=EIAI7RVyFCMiEnEnKXB8iZ6W16Pu7sENOsVuR55A__0,1156 +torch/include/ATen/ops/log10_compositeexplicitautogradnonfunctional_dispatch.h,sha256=NP-hwD4L2nLYQH-POFuf7M7aaDxO37MIm9Eos7DE0Yo,839 +torch/include/ATen/ops/log10_cpu_dispatch.h,sha256=C5AAstlgh3CRIbh2CyQuPjf7cx1LOfzS1wOPuSD8Lek,924 +torch/include/ATen/ops/log10_cuda_dispatch.h,sha256=Zr1dHQslm1PiUXxdBtCE5i7egd5NXEYllVcqm1mgXu4,926 +torch/include/ATen/ops/log10_meta.h,sha256=5zZ5L9JEB6VxM3eY5uiP5eX0VCifdM7iKhKLT38eVs0,566 +torch/include/ATen/ops/log10_meta_dispatch.h,sha256=dY9_HA9tiH6FFYJRqxKOJFtclnp_fCOM6vSbkXxTh5A,926 +torch/include/ATen/ops/log10_native.h,sha256=8PAFZ0ZJ4RQ_IaZdEwZMYSCOMNx0ZyYHWzjU-RraO_w,593 +torch/include/ATen/ops/log10_ops.h,sha256=Su2GbxKoNoZ2Wn6O4gvsK_QUWKb-WOqafCXZWUwZ-y0,2037 +torch/include/ATen/ops/log1p.h,sha256=DM2TJypGHjAYeyf1g-r-drZZ-bwhZb0kC5DwBD595Fg,1156 +torch/include/ATen/ops/log1p_compositeexplicitautogradnonfunctional_dispatch.h,sha256=KqXkNFECg8JrjY62r8R2-r5pliijNanXqkSeXu61U2o,839 +torch/include/ATen/ops/log1p_cpu_dispatch.h,sha256=T5rxVVCiaBTfbTh_lTDDb3SwWXyu6zE805tKIyneuUA,924 +torch/include/ATen/ops/log1p_cuda_dispatch.h,sha256=pFopz_KuXJjjuKIH-9vQ1bYt-i0F7h8CSgmy9QIiHzI,926 +torch/include/ATen/ops/log1p_meta.h,sha256=C8lAcki6gO_HnUkHt-s36WQHEONjMHtnRUCc8L35ynw,566 +torch/include/ATen/ops/log1p_meta_dispatch.h,sha256=8vlGzc0v3JC6MpahpYFTVy4Dax2egP7eeMxu7gAcuHw,926 +torch/include/ATen/ops/log1p_native.h,sha256=174NgUtuIjQu_Fru5EsVv1BWx4gJfP2V_0h-bqPsOr8,1007 +torch/include/ATen/ops/log1p_ops.h,sha256=OGoGatUS9pNhB2-EpihvD1Kmx64omX_kpU_Uqq9ovY0,2037 +torch/include/ATen/ops/log2.h,sha256=3VGkY56IGQgopKkRJ1R0-GVvFF7KsF2ZCFOHA2PgP-0,1143 +torch/include/ATen/ops/log2_compositeexplicitautogradnonfunctional_dispatch.h,sha256=AUm0c7_tnZT8LUY2y0keVJiAqRKPfJJuEXDByhYW1Vg,837 +torch/include/ATen/ops/log2_cpu_dispatch.h,sha256=e3oy2Cug8UiLUkCC-bpv96mszAlcL_Ad4oMMAecPrn4,920 +torch/include/ATen/ops/log2_cuda_dispatch.h,sha256=sHoqPDKA3sLwRv2NWahdofJXAqBmmYM-st74vd6gaCs,922 +torch/include/ATen/ops/log2_meta.h,sha256=_j0Z-I93_NgU-V7cjthzY31h8hGSKFll0x1iXyS6S2Q,565 +torch/include/ATen/ops/log2_meta_dispatch.h,sha256=K6J2HMuMjNMaLtK_6oDLaiv9OaivrMKw_Iy-wr7otKI,922 +torch/include/ATen/ops/log2_native.h,sha256=emafEE8TJtGkEyn0GxIIPuKIePEbGUiEq_qONoMINgk,590 +torch/include/ATen/ops/log2_ops.h,sha256=BpB_RuhSzqmL9CQPoCprRTknCF_rB1ggOvLSPWRd_Eo,2028 +torch/include/ATen/ops/log_compositeexplicitautogradnonfunctional_dispatch.h,sha256=R0qUyXpi3CT-cR2n2n21s5JHMkySnZD19veEAMxnHVE,835 +torch/include/ATen/ops/log_cpu_dispatch.h,sha256=1scbJNlTj9Svm9bpk8dzdf6qnQaybTuz1FXKK4aM6uQ,916 +torch/include/ATen/ops/log_cuda_dispatch.h,sha256=wVeI5GNaoZ-qVyJot-7teBov9ZniPQ5qRydcuixamRg,918 +torch/include/ATen/ops/log_meta.h,sha256=0gkNcSOsjLTwHkQtvGNIJpYbn48cQ1hof-dRey9iREo,564 +torch/include/ATen/ops/log_meta_dispatch.h,sha256=auLInGknpTv08vFk7YujovQ7UrjDGGKQ7u_2kvcPOs0,918 +torch/include/ATen/ops/log_native.h,sha256=5zaAnttLgeQSnbdeeISEvqYt1j3J5mf9fUakVdE9Y9g,587 +torch/include/ATen/ops/log_normal.h,sha256=U4CVivbdnFnURhrOhLjvOTfI5pn__YDfDVgBVKZ4rGE,1537 +torch/include/ATen/ops/log_normal_compositeexplicitautograd_dispatch.h,sha256=3HCVdTzfQDzi35NnUvPv6RUtTcEjMsc8CEmWfKP4WXY,1172 +torch/include/ATen/ops/log_normal_cpu_dispatch.h,sha256=WWCZkvmygCYCiD4OJ2b9xUM23C9Sx9WUzWQaOhL8w14,807 +torch/include/ATen/ops/log_normal_cuda_dispatch.h,sha256=1wuglPjrB1bPkrdzPfOtkhr4tNsUUpwYAy8GQ87Av4I,809 +torch/include/ATen/ops/log_normal_meta_dispatch.h,sha256=16WMRLsYjc1ChbL_p7x0mbnJIewLYWdLnSGYOJ0usRY,809 +torch/include/ATen/ops/log_normal_native.h,sha256=pXBIvGcsZAeNCH5zMkxDt5uIuPCmgUTXiE0KfU36ZHE,856 +torch/include/ATen/ops/log_normal_ops.h,sha256=DzrKrul9ranemUiOhPbklyP49ysoSjxSqyalnlGrWaM,2796 +torch/include/ATen/ops/log_ops.h,sha256=xXv1ISTMMdIju8p67MmQnkO4qRUTH03nSvnAFIYASnE,2019 +torch/include/ATen/ops/log_sigmoid.h,sha256=Kf0PBAKEm4Y3hke0LE6Aya-IiWtaQg__a3gk6WueCz4,1077 +torch/include/ATen/ops/log_sigmoid_backward.h,sha256=HkcPgsgchuMobj63uz2fr5KqJ68b9klUSNBptuhe6gk,1582 +torch/include/ATen/ops/log_sigmoid_backward_cpu_dispatch.h,sha256=VYlouOZtPurG_oFO4ZTpUFIdUdmSissSXnHwwUbb1dk,1110 +torch/include/ATen/ops/log_sigmoid_backward_cuda_dispatch.h,sha256=3XkRbmt3bdVwdEph9u9rM2UL0uXLmp-9Z5DnSkA0D6A,1112 +torch/include/ATen/ops/log_sigmoid_backward_native.h,sha256=9MeG71SXXL_1_UyEr0KGHgZB5vvKF-bEOm70uFimTIY,1010 +torch/include/ATen/ops/log_sigmoid_backward_ops.h,sha256=t0vef5OqIMkx2ecn_chaywjG3ow2s9zKJ99ceO8BcMM,2059 +torch/include/ATen/ops/log_sigmoid_compositeimplicitautograd_dispatch.h,sha256=Ok7h2c47DYJL1q7NUjbtUav2BOUAybPnMr2b1VKNW_4,936 +torch/include/ATen/ops/log_sigmoid_forward.h,sha256=TL6k_p6-WOIvaMz8-2f9wgT4pe2ZNq5XsPnfbmo2RJw,1414 +torch/include/ATen/ops/log_sigmoid_forward_cpu_dispatch.h,sha256=5OgHsH3a5Z-kQMtzEfBi98VvNN8wm5IMMbBytL1IB0Q,1043 +torch/include/ATen/ops/log_sigmoid_forward_cuda_dispatch.h,sha256=-rD7OmAWyX1Wcaax8-makfMsekD_LD_9HWmldJPKt9I,1045 +torch/include/ATen/ops/log_sigmoid_forward_native.h,sha256=FrsCHAOmJkE2PBcwValrMciyIOs7-JV7RqG_wadXLXs,908 +torch/include/ATen/ops/log_sigmoid_forward_ops.h,sha256=jjo6_gkxGlRxBBEYttQPTKJF6IjRAXY7siQrbQEs4w0,1912 +torch/include/ATen/ops/log_sigmoid_native.h,sha256=K6f3LwPRVNYNj9DZgc2-GC440jn7gmmdKPtIPaGIyiY,564 +torch/include/ATen/ops/log_sigmoid_ops.h,sha256=351IDsKos6cD97tcs7hjiNffH-x4dqKB2Q7Vahbwd38,1577 +torch/include/ATen/ops/log_softmax.h,sha256=rkH4ZgqtNARxqyFF6Gt9kgrp01kXSldlScnYtQ0G-vY,1711 +torch/include/ATen/ops/log_softmax_compositeexplicitautograd_dispatch.h,sha256=YHCTHKUcE7mCTAfidCKXQP2-LgmV2be2s69fJVYrPIw,996 +torch/include/ATen/ops/log_softmax_compositeimplicitautograd_dispatch.h,sha256=vtZzi3QTLqRqRB59R92KG30nDdifqH_-bBtvx6JKeDk,966 +torch/include/ATen/ops/log_softmax_native.h,sha256=vHNhBlcCU00o3AQ7qH1RyQjspy3itUqJnbxeNoV-54A,813 +torch/include/ATen/ops/log_softmax_ops.h,sha256=uW132aNuYhLxi6Tuoa-pqhLVM7tYk9ErRhrza7dIEpA,2694 +torch/include/ATen/ops/logaddexp.h,sha256=BtNKHjH8jfPJ0eKFNeGUWxmqsq0ft_ZC432dApHa-x0,1198 +torch/include/ATen/ops/logaddexp2.h,sha256=w6BwqhBbCXn9Qxp1CNnULGXvZrL9HaGUYdneuBD-_68,1208 +torch/include/ATen/ops/logaddexp2_compositeexplicitautogradnonfunctional_dispatch.h,sha256=7I_XgYDS5ZZvdVRH0t4O-L0t9usQninkZbDmQ2R1Yg8,820 +torch/include/ATen/ops/logaddexp2_cpu_dispatch.h,sha256=u2Ny-PRgGqX-rACBKejXub6FbUsFj_1grlY29le0LYE,967 +torch/include/ATen/ops/logaddexp2_cuda_dispatch.h,sha256=N53TT8UTTQs_6_fj2r66JFVKnGSzdK9OMvYPakUoq-I,969 +torch/include/ATen/ops/logaddexp2_meta.h,sha256=237t9zPqf6bNWgb7rn69NGkJBpgBFsiaHgjufpGJchM,597 +torch/include/ATen/ops/logaddexp2_meta_dispatch.h,sha256=KSp3Tv1k2J3YO9vIcmk8U3V3IY7Qn_sLk2a-aLTHEbU,969 +torch/include/ATen/ops/logaddexp2_native.h,sha256=jz7fnRWk5Bx7g2oXksUbxlYup6yrnB3PMvWnbQqU7QU,634 +torch/include/ATen/ops/logaddexp2_ops.h,sha256=un7Vv8JGtFPU89EoDS8_E9CqebZmyfgwYpU6rFfOfPE,1743 +torch/include/ATen/ops/logaddexp_compositeexplicitautogradnonfunctional_dispatch.h,sha256=bc27hqaHJuF1-PlT74ga5CIv1PLV0HoH68lFFA3cGhQ,819 +torch/include/ATen/ops/logaddexp_cpu_dispatch.h,sha256=2ugkj6JTjpTaG1GFYnO6iTJdnakkXWesKbUEard7kSc,964 +torch/include/ATen/ops/logaddexp_cuda_dispatch.h,sha256=nkHjcXhFtrSZH2JCgkqFTVcLWhYDxd6WqQB6txJomh0,966 +torch/include/ATen/ops/logaddexp_meta.h,sha256=Ws4UraOmU4OwgLZ5tTl_ltX51i9hZV9ORGS2zMQLwP0,596 +torch/include/ATen/ops/logaddexp_meta_dispatch.h,sha256=DLlzaAJCcqQCfWMoYf-woFqFHzGT9OWtdlJgrDssoys,966 +torch/include/ATen/ops/logaddexp_native.h,sha256=ofalyf8ua3OJmxmqIK1jO71vkAIVHgkNKKXinIyM1Js,631 +torch/include/ATen/ops/logaddexp_ops.h,sha256=BwD_AeQ_Gh7aEw1wgjwn1OgYU4C1uYX8vNsumxsK014,1737 +torch/include/ATen/ops/logcumsumexp.h,sha256=cbGTQG3iahyDBf7hzrc3C8MrNyQSs3HnDeqZlMmlrAg,1900 +torch/include/ATen/ops/logcumsumexp_compositeexplicitautograd_dispatch.h,sha256=ZdaEVXzPSlWPdldhhjL0zBH0UhjHyuJRqoIoGr6E4CY,978 +torch/include/ATen/ops/logcumsumexp_compositeimplicitautograd_dispatch.h,sha256=maRoxGYE-a9toJGDnFV1WTVX1tVbIG8zT8D5giqhCrk,990 +torch/include/ATen/ops/logcumsumexp_native.h,sha256=WkjkIfvb62-ru8yGVa_txRDlbMDQq5YFKazsw3uL_04,770 +torch/include/ATen/ops/logcumsumexp_ops.h,sha256=nbcMksvK9N73sUU3Rt7JO4Y6dkNAEbsWKqm_Inpfiq0,2964 +torch/include/ATen/ops/logdet.h,sha256=m8D2HKFNhf1nx1AmcNlTmp6HCf6Wn-yjmMN29maB500,637 +torch/include/ATen/ops/logdet_compositeimplicitautograd_dispatch.h,sha256=sakW73DeFiy3EJoOI6TnZOja4Ux7KfqfyAJlzBDeRMM,764 +torch/include/ATen/ops/logdet_native.h,sha256=dYf3TDdISZw6iJKRzeFeTdRjvaOdc3lfUGUVW4QDBF0,476 +torch/include/ATen/ops/logdet_ops.h,sha256=4JS0wt1FghOWkqRuLrHMuLfWUbaPfA37VuY_0XsKXqc,957 +torch/include/ATen/ops/logical_and.h,sha256=g2QzEHdquADM_F5xQKxkYMj9wCg9DhB51Bg6rePA4sA,1218 +torch/include/ATen/ops/logical_and_compositeexplicitautograd_dispatch.h,sha256=B7nBUHUsDDcnUtb1jr4pi59wEiKtks43m2-cXR9IkrE,877 +torch/include/ATen/ops/logical_and_cpu_dispatch.h,sha256=dzXGNFHfpMDWfxKtEa08QXJNUBqzHveh55Bk8v7_1wA,885 +torch/include/ATen/ops/logical_and_cuda_dispatch.h,sha256=kwNsiuoj3AuhHyE9u5oh69fg8T64zXEoBzWy7gRZo0U,887 +torch/include/ATen/ops/logical_and_native.h,sha256=tgN2tN8NXxkwX3c8XVwIOZfK2YUD6hPo46vKyHeke_w,698 +torch/include/ATen/ops/logical_and_ops.h,sha256=4j00pDdGDLV1arcogbhfOdWQ7Nh1nk5DvhZ9j2-IlHw,2349 +torch/include/ATen/ops/logical_not.h,sha256=_EddlVBz2dVPD3kZnb8MIbVI8R45vS43AaCb4LFNo44,1077 +torch/include/ATen/ops/logical_not_compositeexplicitautograd_dispatch.h,sha256=c0_UHtPG0hnPwvNGu2xPlvh_qD7--bZ642QUCSnJgTM,825 +torch/include/ATen/ops/logical_not_cpu_dispatch.h,sha256=tob0XD-izc3K5yvLxOcasfCwB-kPs4OxhPT3Gs00oK0,833 +torch/include/ATen/ops/logical_not_cuda_dispatch.h,sha256=W9vTjbo5wcHui6al3CEyERTrkmEWS9zjyYAM4XzUvZ8,835 +torch/include/ATen/ops/logical_not_native.h,sha256=t_vao3UBSMuWsjNTPirCAUCXIPN39O4knVoelTf-p7o,761 +torch/include/ATen/ops/logical_not_ops.h,sha256=l7w8DsX76BETVL3tRsjarcDorc1aoxRaV-0vRUWisXo,2091 +torch/include/ATen/ops/logical_or.h,sha256=3S2wFKcwyEYRBNnmjnG4L-TE4sSSVG6Qlk11YjFBXt8,1208 +torch/include/ATen/ops/logical_or_compositeexplicitautograd_dispatch.h,sha256=xBax8bjDlC3gCLLjnSdnVH2g7hNdMvoWPKL4n1nryRI,875 +torch/include/ATen/ops/logical_or_cpu_dispatch.h,sha256=mxXGaNamr-nRzho_VXH3HaQyZocaHNaY-0kTBD1tjCQ,883 +torch/include/ATen/ops/logical_or_cuda_dispatch.h,sha256=2Rp4qKQPdSifWBc6nViCNeEK_D_O8jR07tAsFtpII6M,885 +torch/include/ATen/ops/logical_or_native.h,sha256=eQTT_EZKa9XfgCWHgeF2RTvPctix3KtrOuxQWP4EFSA,695 +torch/include/ATen/ops/logical_or_ops.h,sha256=QHbCogavSSXpGikdy5jmnVzS5tXhQc_J0ukA5SfM-zE,2340 +torch/include/ATen/ops/logical_xor.h,sha256=maa-smmDI3vTVdfeHWbYfVcGjEzSSEksC_ECJz-0u6M,1218 +torch/include/ATen/ops/logical_xor_compositeexplicitautograd_dispatch.h,sha256=byN_XRa6285A-DveuuYDr9Fh_2JvCXR3hQM3mM2qsec,877 +torch/include/ATen/ops/logical_xor_cpu_dispatch.h,sha256=MNYc3FYQRAZSXG-HbtrtX05RycNuWN4wniBkT3WKNXw,885 +torch/include/ATen/ops/logical_xor_cuda_dispatch.h,sha256=HlO2iVUTQ37xwSimEDWQrjpVQla4z9umbNEljEpeeTc,887 +torch/include/ATen/ops/logical_xor_native.h,sha256=C5NxiCMd1fwSwHCM_SFrjNVsZiGj6XaN12bKWdgBVcU,698 +torch/include/ATen/ops/logical_xor_ops.h,sha256=_jRMVx8SJo30LsCn_MFSk1s0OqUZ_LefsxgT3fw3_Io,2349 +torch/include/ATen/ops/logit.h,sha256=ybhoJxG-jc63gfoeASyjOJ22DZgggKlMm1uSNJnI1tA,1405 +torch/include/ATen/ops/logit_backward.h,sha256=SHWpClji4Ber83h-fdD6Kzm25oLF2azJv24Yv6bv4Uw,1555 +torch/include/ATen/ops/logit_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=sCzoa2HrPqdbQbvgMhgvwsJVxj3k9deS105PUPZGiUs,874 +torch/include/ATen/ops/logit_backward_cpu_dispatch.h,sha256=878iRlstGB4_OPbvlyQthgSSR4enw4SE5Tz8fGJazl4,1128 +torch/include/ATen/ops/logit_backward_cuda_dispatch.h,sha256=0O5umbwKdcyHs3V6V8BHMqDCFmmrDoikpbZdFrstH8w,1130 +torch/include/ATen/ops/logit_backward_meta.h,sha256=0oIKvRlcIEe_xOHRGQ8_ZOXL7FiEdCvPqfkkfwcZosM,636 +torch/include/ATen/ops/logit_backward_meta_dispatch.h,sha256=j1VGfXmFP4UKlvvmisQcxRsbVuuLWAHKBAUNzmhuPIw,1130 +torch/include/ATen/ops/logit_backward_native.h,sha256=HlvKwPs134HiZ0eEa5TbraQXSoxG7TUh3uH_ErQO_7s,688 +torch/include/ATen/ops/logit_backward_ops.h,sha256=lWjbBE9JGBFypydX-wi-DVUPNDi-AXShCK1Qu24TfUo,2045 +torch/include/ATen/ops/logit_cpu_dispatch.h,sha256=TLBjPucO0h-mFBFLTkGAvtwX6RpqbGzhMqCGk0304iU,1085 +torch/include/ATen/ops/logit_cuda_dispatch.h,sha256=S8T6fufN5G3AeoEI6piLi8AbuXyDawzOeObQzGW7ecI,1087 +torch/include/ATen/ops/logit_meta_dispatch.h,sha256=M7LC9p0ZXrWdLB35ZtKLKoK5J8_1IgXkSLyTzZzxOks,762 +torch/include/ATen/ops/logit_native.h,sha256=pzgh1isGXTRjxHP-SI9WgM_xQ9Kq89pLUbPPAfRRiwU,719 +torch/include/ATen/ops/logit_ops.h,sha256=vyt83YUWNcokWyS-nwf6y7pqttSXA2P_N0EjraqBrak,2337 +torch/include/ATen/ops/logspace.h,sha256=VNsVVYAlxWuoMgtWf5F3pHkfUnForMm3Jls_cBBKggs,7492 +torch/include/ATen/ops/logspace_compositeexplicitautograd_dispatch.h,sha256=WJMmhXCoZc0O7Itz5Ko6NEWIXO5R6ePtQQMuaj6Nh3M,3118 +torch/include/ATen/ops/logspace_cpu_dispatch.h,sha256=lIbC92l2z6YYc1i69eBuzc9N3mxFJLCF6E2Qvhs5-lY,938 +torch/include/ATen/ops/logspace_cuda_dispatch.h,sha256=Xi-FnMUCpNarqwhTiE7UEzDtf4sU79WyplN-hg-5C4c,940 +torch/include/ATen/ops/logspace_meta_dispatch.h,sha256=KLZEzq5BpvGTkBIU9xk5s4ksGuoETrCxMAhMLoM8Dwc,940 +torch/include/ATen/ops/logspace_native.h,sha256=7Hgb_BC5giT9hjvR3FluzmdQ0qjizW08uEMqkZfjTV0,2176 +torch/include/ATen/ops/logspace_ops.h,sha256=vvwUsvDTF1PnjPtibADNMrNhIcRLNQZkT4eiv_xBqKw,8570 +torch/include/ATen/ops/logsumexp.h,sha256=MB-tuAVOv9qHK17lfEGVrhj9qPZT_SvtSOfjDpV4pBw,2167 +torch/include/ATen/ops/logsumexp_compositeexplicitautograd_dispatch.h,sha256=UrwIhA6det4vpkAUASP5QCkB5lxt9mfTIe6sfKJ1o8A,808 +torch/include/ATen/ops/logsumexp_compositeexplicitautogradnonfunctional_dispatch.h,sha256=IcoFrqLTlhRccV0UBYg8WcUR8cDbWP8qUUs5DB5Sb2Y,975 +torch/include/ATen/ops/logsumexp_compositeimplicitautograd_dispatch.h,sha256=zK_0YBaabZeNgognSpNYm0UZxRn6RSwfzgBucfiUh8o,1047 +torch/include/ATen/ops/logsumexp_native.h,sha256=3OHVRNEiRgDCburMeZ_En21ReZompNHxfX5OLBxAUVY,850 +torch/include/ATen/ops/logsumexp_ops.h,sha256=7BCupsVxUexP9sA6-iQrgttkpNPrQ7p4mvLF0cXni_Y,3216 +torch/include/ATen/ops/lshift.h,sha256=MSp80qMZY1Ia9JV8_CqFiDU8kfOTB9w_y8jLmm4HySE,1990 +torch/include/ATen/ops/lshift_compositeexplicitautograd_dispatch.h,sha256=x4myy8Ylu7bzDpBNtcllaXLY81TSMLY0EPrn0eahh7M,1144 +torch/include/ATen/ops/lshift_cpu_dispatch.h,sha256=ImbVJ1OFP-DKtWTFKCfEkjMU-BE_IXHa9yro9xn7fJQ,996 +torch/include/ATen/ops/lshift_cuda_dispatch.h,sha256=Mfch3l_jbK5XQNfaLhXp6Ocvo5HUKhGNsQzHcfLPGw0,998 +torch/include/ATen/ops/lshift_meta_dispatch.h,sha256=8BQni9LNubFUj5L9wWrzOIIUCYoG5w_N5p0wEXKFP0U,830 +torch/include/ATen/ops/lshift_native.h,sha256=LXcpHvTIgrbd1ukjzSChq5Zor4db70pMmGyncfwPARM,982 +torch/include/ATen/ops/lshift_ops.h,sha256=8RdI9FoYxRuuPbQEnjHr8on_JEH9_q9QG003AIJjyqc,4345 +torch/include/ATen/ops/lstm.h,sha256=zUUVPBot0OE0cf5mDAyV7uy6RQ1vQWuj0TLy9O1c3t4,1613 +torch/include/ATen/ops/lstm_cell.h,sha256=7Wx6rs_L1-ItJeR-2KZ_5kPU51Re18-TT_3L-YeQzI8,951 +torch/include/ATen/ops/lstm_cell_compositeimplicitautograd_dispatch.h,sha256=3ENSc9qQQ_satAImfT98ee0-1Ylz0JYTdMFb8JBvHzg,952 +torch/include/ATen/ops/lstm_cell_native.h,sha256=ChAWJuSCwjUYC0o-nyR9ptBlMqCHotdhcUfnuRWfk2o,664 +torch/include/ATen/ops/lstm_cell_ops.h,sha256=5Y7KO0ZI4lcioV74wZdXnzmbeoSOkkMX9oY-eCTOKTg,1567 +torch/include/ATen/ops/lstm_compositeimplicitautograd_dispatch.h,sha256=fkyqlCWp8UACGxzhsW-_yZrLasxjQYICWWrzftAIlYQ,1191 +torch/include/ATen/ops/lstm_mps_backward.h,sha256=7BbQWPJHTOTuZc3gK66QHreLmPZCuZEf5p56dMCQONk,3675 +torch/include/ATen/ops/lstm_mps_backward_compositeexplicitautograd_dispatch.h,sha256=IIdaMF-wEAlREwa7OeMrS5Br8yszFgPQix_4Wkv-xro,1713 +torch/include/ATen/ops/lstm_mps_backward_native.h,sha256=weHyoiJWjmeV6Y4uZrJCmejM7Dyh9Wc3fKjMKT4phOQ,923 +torch/include/ATen/ops/lstm_mps_backward_ops.h,sha256=R82SOtdNTZcZP3pbGnZjRCDT0YdGgv3jr5qjipV_0Iw,4434 +torch/include/ATen/ops/lstm_native.h,sha256=s6nlnbSKE3Huk4kkP5-Pchd0lESemlIyz83IlpUOtNs,903 +torch/include/ATen/ops/lstm_ops.h,sha256=UljVL0J6Lxu5VFN1N4LdbcENjZri7309UADWrKNL2V8,2757 +torch/include/ATen/ops/lt.h,sha256=YKai2BchcqVHNVHccC59tGvciPVvCPE7yfNlXsC-Pho,1842 +torch/include/ATen/ops/lt_compositeexplicitautogradnonfunctional_dispatch.h,sha256=SArQJmYLRzdjhA9J0YtsRqanWbpkkk5zgBBStLxzXOM,1034 +torch/include/ATen/ops/lt_cpu_dispatch.h,sha256=8cQPcA5Xp1iDmRxCcxKx4l1mRAAiZXSIDRF1vZCSMBo,1366 +torch/include/ATen/ops/lt_cuda_dispatch.h,sha256=b8TooVTHOkBITOLJYs3wsDVv86xvYXtXSpy_Idg7Bd0,1368 +torch/include/ATen/ops/lt_meta.h,sha256=QpQQlHpkkx9lDlAu89jFF2Xai23l2ut25d8rNseaA9w,735 +torch/include/ATen/ops/lt_meta_dispatch.h,sha256=axFyNImwwx6g7Nw3KlcTs7sVCnQ3pVMj7fjo8FFv8DU,1368 +torch/include/ATen/ops/lt_native.h,sha256=d_7JRShnwlOPXgJ5HmC6dett00vQSDI1b3xg7hCj7kY,1205 +torch/include/ATen/ops/lt_ops.h,sha256=itTo4uFlbYGTgqs7rZKa5XjWmtxIm5f-g2koPynifZo,4201 +torch/include/ATen/ops/lu_solve.h,sha256=Q0XYK06yoUh6TDjiUAfL_cbVmrvwtHE_MhwO9MUwYv8,1383 +torch/include/ATen/ops/lu_solve_compositeimplicitautograd_dispatch.h,sha256=6-UqzkNBV-lzs9QzSQEJGtdwXCHupVq2c8AGODa6REs,1101 +torch/include/ATen/ops/lu_solve_native.h,sha256=aV-XKFRGHTWFQQshQ7PHF4CuKL-3t3Rxxyx2i_BAMRY,674 +torch/include/ATen/ops/lu_solve_ops.h,sha256=P567p7AV3dfRyXui616WGoPiK6oO6HYNQabvLC_sjHc,1939 +torch/include/ATen/ops/lu_unpack.h,sha256=SOUYFZveKYbqgAvxhXGrcfLtmUSINNyy0PMOzlSYNB4,1947 +torch/include/ATen/ops/lu_unpack_compositeexplicitautogradnonfunctional_dispatch.h,sha256=uknmKso8TMw5EhozlajBM-DkV9dXFO0WTr71SVUfMfU,910 +torch/include/ATen/ops/lu_unpack_cpu_dispatch.h,sha256=8V3hXMYZBq1UiRSMlyFWdNLUz8mgALSplAnvjrzu408,1295 +torch/include/ATen/ops/lu_unpack_cuda_dispatch.h,sha256=_n5AavfVhIkZe7F2ysigMDGt09FL2nPHkOJufdPE4l8,1297 +torch/include/ATen/ops/lu_unpack_meta.h,sha256=qCrhdm49pn7s5J2cjARdq7wOQDOdw_pS54twjt_8_qw,641 +torch/include/ATen/ops/lu_unpack_meta_dispatch.h,sha256=bjfhj2g0TjCgAjc4SIZir8fBgrR3otbysU-tIn5YHc4,1297 +torch/include/ATen/ops/lu_unpack_native.h,sha256=e9DtaFznwLiRkY-_je4LxT98QrHY2I1PpF2t15Eg29Q,718 +torch/include/ATen/ops/lu_unpack_ops.h,sha256=bVy7JdbMl0w-kbmlmNhhaHZlVNExDfwWRpfIUuPKTO0,2449 +torch/include/ATen/ops/mH.h,sha256=ZMwpdhbQjcJk9n8n5T4Mt7DvSS1fAUihx4OFwMbqR-A,499 +torch/include/ATen/ops/mH_compositeimplicitautograd_dispatch.h,sha256=Qgn_B4neSq-pHvFFlgIrYUmjDfzDkDXeFqWYTX2atNk,760 +torch/include/ATen/ops/mH_native.h,sha256=HGFt_fvzBnqDNQ7S3wApiP9vjuwDXyc8SiLyoO8nUDk,472 +torch/include/ATen/ops/mH_ops.h,sha256=KAj9sXFwIAzpQFcvPoE5ZO_fUo2V8ZXFRCLihUbM4Og,951 +torch/include/ATen/ops/mT.h,sha256=93NtIu6ZryPpXXo3bXuiUOkM-NwYwyNeO5DGCir4QXY,499 +torch/include/ATen/ops/mT_compositeimplicitautograd_dispatch.h,sha256=9XrU66nNz5i8TeYoRIaxRsuVZhBXBB4Plz9Vz_Tztp8,760 +torch/include/ATen/ops/mT_native.h,sha256=OcSSdCpIvuFp23cks8T6N6BVdodWDM0JgxXtRUWAYSY,472 +torch/include/ATen/ops/mT_ops.h,sha256=IE6abtZt8b9sjS6hzKymE4Lw2GJFvVStumKlmBTy_pk,951 +torch/include/ATen/ops/margin_ranking_loss.h,sha256=8kQM5VOwzbRFVMsPGBI-r5T1iugrnn_uMw8e3074-Mo,910 +torch/include/ATen/ops/margin_ranking_loss_compositeimplicitautograd_dispatch.h,sha256=bdfxYFL03AA-RibFkW4dSxsGa5Qi9yamzvUIEnD98LE,891 +torch/include/ATen/ops/margin_ranking_loss_native.h,sha256=gLnQbu7ORAh7-bOjV06Z47j_Mfk2l6RT979hGCVcMhc,603 +torch/include/ATen/ops/margin_ranking_loss_ops.h,sha256=H0CDlGFAS6GN7wXg-S9a4mU3IKIJvd2ZQryxpWzC5RE,1303 +torch/include/ATen/ops/masked_fill.h,sha256=dwO5N-kCLamoCFvRWQF9rtcz6W2fyBtPgic7yoMhitQ,2277 +torch/include/ATen/ops/masked_fill_compositeexplicitautograd_dispatch.h,sha256=GtC3FtJQ3kwkxuAo642-kJdb_qckd2ZzpS-iULidfzY,1468 +torch/include/ATen/ops/masked_fill_cpu_dispatch.h,sha256=DWObWOVWsfS7hpSg7kn_0utMHCfake_YmwAgAUT8JCg,880 +torch/include/ATen/ops/masked_fill_cuda_dispatch.h,sha256=WahwAlL1cI8vj-6jdBQ2mJQsYIOAlGJVTtm7MCjXWAs,882 +torch/include/ATen/ops/masked_fill_meta_dispatch.h,sha256=YXkifJG75xG23gj1o9EmvemHPRbvyhztqSN-knCvk4k,882 +torch/include/ATen/ops/masked_fill_native.h,sha256=tTFnejNZ-MQ42-lalFdWyVTwXSvm9o6KGEXbORL5_dA,1979 +torch/include/ATen/ops/masked_fill_ops.h,sha256=aNen1vcF1TxPctHpWCJ6iW9za0Ri0KChTXwMaYHt0ng,4861 +torch/include/ATen/ops/masked_scatter.h,sha256=9QBNrk-ZvslVfTzqgsCa8Be9jh52H-b5isL_2OMOc_I,1389 +torch/include/ATen/ops/masked_scatter_backward.h,sha256=UWcX9xBovI2RMrU1NTg5s80xEzUZed0Q9MsvZObWYN4,1790 +torch/include/ATen/ops/masked_scatter_backward_compositeexplicitautograd_dispatch.h,sha256=Fq6xvGYz_QvL5SMKHBwzjsQAZxOLgwe9Ze7A_6sdvTo,973 +torch/include/ATen/ops/masked_scatter_backward_native.h,sha256=JoKJmknM3JHgJyebSeFvqaPEZmqZhVegVQP2EwLjdfo,559 +torch/include/ATen/ops/masked_scatter_backward_ops.h,sha256=Zp8_-T5RFQty7p-vNBG0eIgF5MTlg009bAWU6LrG8XQ,1203 +torch/include/ATen/ops/masked_scatter_compositeexplicitautograd_dispatch.h,sha256=HMpnqJA8Prp_D--kCluZ7KhvCIW3rQAies1AhDq2unU,1101 +torch/include/ATen/ops/masked_scatter_cpu_dispatch.h,sha256=_Leg0ox_AEk2TWF1pr2Sa3HZk5ue1ydwvDImeokiFno,777 +torch/include/ATen/ops/masked_scatter_cuda_dispatch.h,sha256=sNnC6beSVZc45z4K7MmAehRQ73m-qmhgkZ1VJC0Fi6w,779 +torch/include/ATen/ops/masked_scatter_meta_dispatch.h,sha256=8XCK4lE9U9k7q_CCD857a5Xs38lcEOa7MDWa1D154SE,779 +torch/include/ATen/ops/masked_scatter_native.h,sha256=Esdks64DIbKZ4PBKGKNIsqRpm2XHiYq3acMrFzEExNg,905 +torch/include/ATen/ops/masked_scatter_ops.h,sha256=hYg8loYWmUy5mQhQT1_NfsbUAlZh2rHyOow6dZ7zRp8,2634 +torch/include/ATen/ops/masked_select.h,sha256=35B0W5I7zZxLn-14jctHRrASsvBMtts_Jo0XxJ0zNaY,1229 +torch/include/ATen/ops/masked_select_backward.h,sha256=Iw7mytB0FyBti181eqdrGujXJlNEjFptHfAbw5pJNBw,792 +torch/include/ATen/ops/masked_select_backward_compositeimplicitautograd_dispatch.h,sha256=2RFDp1nA2LuekX6SdqsjouB2lyEE4l2YKeK0dx9fJG4,831 +torch/include/ATen/ops/masked_select_backward_native.h,sha256=-G_oBmNAkJIhsh7v18w8Fcp0hjpIMliGWsdNPI4Zg0w,543 +torch/include/ATen/ops/masked_select_backward_ops.h,sha256=KGa0WTNjivw3nWg7dDpWL1SyIx6jO-veKRuqHiYbJUg,1174 +torch/include/ATen/ops/masked_select_cpu_dispatch.h,sha256=8Yt4cYmNe_XhIorOZp6C2YdCxP9qXjyAtqgfTeLourM,973 +torch/include/ATen/ops/masked_select_cuda_dispatch.h,sha256=flnYJp7xyQbIouWT8T3ojlp61gnxOuX3fRaylYutPjA,975 +torch/include/ATen/ops/masked_select_native.h,sha256=GJl_tk504-45WASRgDv5L9SDcsdjtsZRUjJGukZyqxc,832 +torch/include/ATen/ops/masked_select_ops.h,sha256=htn4YPUU7CxNgfWOuP6todvepc_H8Luw4v6BcujXWuU,1755 +torch/include/ATen/ops/matmul.h,sha256=0Y9EGBfgz-7JxsgkkOTUZ5F5iZWLgZ5vCmE4uaitbg0,1168 +torch/include/ATen/ops/matmul_backward.h,sha256=8W3EBzEfoXSB_sttjyvfSiBSqTnxNm1ymyLBiFGJVsc,1738 +torch/include/ATen/ops/matmul_backward_compositeexplicitautograd_dispatch.h,sha256=IfZAwsZXBjNcNtWoYyxLNmLAP-8-3M9v3CrPt_M6FD4,1135 +torch/include/ATen/ops/matmul_backward_native.h,sha256=XfodV32bVP_EBgb4rzTEp3OK3spHOjVd-dE4E5-Dy6M,807 +torch/include/ATen/ops/matmul_backward_ops.h,sha256=joP2-68jPtChgiiDw-D6vtN-tIzQN8iQO9xE0vKquMw,2371 +torch/include/ATen/ops/matmul_compositeimplicitautograd_dispatch.h,sha256=QJtM3u5qAYC6-faSHGw1n0_QJhQXCGkOInYiOQrSZaA,999 +torch/include/ATen/ops/matmul_native.h,sha256=gVzxli9XCbic3FdJV6u2uvg6kybPelEiDa74fJ-MEbc,804 +torch/include/ATen/ops/matmul_ops.h,sha256=5udYeRR6sBabeX1Cmuw65_XZOcz7E7xJFTGjvk5i_wM,1719 +torch/include/ATen/ops/matrix_H.h,sha256=7Y3tmiu7F6QPVFvvVOKWiLCQRdMvBv4fmkE_D2VFPF4,505 +torch/include/ATen/ops/matrix_H_compositeimplicitautograd_dispatch.h,sha256=-ajflMamPGTvhRi4uy3-skucUUufMNlXVi6AqqP6Z3M,766 +torch/include/ATen/ops/matrix_H_native.h,sha256=g-R9BwHKF4fi2M0LDeoBCiJdO2vcxmo67Phsns5tRnE,478 +torch/include/ATen/ops/matrix_H_ops.h,sha256=I4uCxtpo2hpQJVFDDjyFzemSLRLBYgPJA-d7Rf8YTB8,969 +torch/include/ATen/ops/matrix_exp.h,sha256=enNU_XpA8M3x3O1V92Z5dnzAeaxq0kJDeyAtyITblQ4,653 +torch/include/ATen/ops/matrix_exp_backward.h,sha256=kcJkRGAvkIeu5F30MqV3jwyBQpgigksct6_dgs1WjHE,733 +torch/include/ATen/ops/matrix_exp_backward_compositeimplicitautograd_dispatch.h,sha256=uUS28D7wPGFYwHYcsXYkmYCd5u92FjNrrjH6fIpR4Zs,802 +torch/include/ATen/ops/matrix_exp_backward_native.h,sha256=RB8s1zAtBu6Wrvky3BeT1IHMZ4NbVQ7ucrmLGJu7NXQ,514 +torch/include/ATen/ops/matrix_exp_backward_ops.h,sha256=GsPh7UZTc6VegcVlc2FgGYjnNniKOuTGw5CrzlGMY-Q,1079 +torch/include/ATen/ops/matrix_exp_compositeimplicitautograd_dispatch.h,sha256=BTFybvwdLa7K-3s4NLMqEih9GfEaV3V7dQcrPTSgBYI,768 +torch/include/ATen/ops/matrix_exp_native.h,sha256=_hPcGP9oF81qWvcRlQThE36LeGp4YXXbd58--OYfoXE,480 +torch/include/ATen/ops/matrix_exp_ops.h,sha256=rdls8Spikik36XnYuZbOBts-CCuEnW7X17DeH0RvSS4,969 +torch/include/ATen/ops/matrix_power.h,sha256=552FvzRgnGg1JO_7hJO8iQABmKA4BJ0-tRQ9QvKfLWA,1150 +torch/include/ATen/ops/matrix_power_compositeimplicitautograd_dispatch.h,sha256=ggBKUWmjgiq4CPpuybMr49VyOwqgVHHpTPKx2UgsQzQ,972 +torch/include/ATen/ops/matrix_power_native.h,sha256=Z83XSZ5M5_Q00QJoLSnAeRRPoITOwzPN461ZF_j7RNI,588 +torch/include/ATen/ops/matrix_power_ops.h,sha256=qz6mALCvI0VOrCJMaztz2Wk3kv_NhTjFyzuLeKDq-XE,1659 +torch/include/ATen/ops/max.h,sha256=KdIfAw2gmuTlgnJEBvjMv0aRvWvkA_Cm50EMCt30Rk8,3784 +torch/include/ATen/ops/max_compositeexplicitautogradnonfunctional_dispatch.h,sha256=oQgn2RhxmfMGPuFSINQjiah5-NfLl-LS3wWBFMjcf9s,845 +torch/include/ATen/ops/max_compositeimplicitautograd_dispatch.h,sha256=mZb4YTt4QH5ZgOdfKTAruj3yq4RwNUI4vDMBmDizdNE,1426 +torch/include/ATen/ops/max_cpu_dispatch.h,sha256=-8j1oSNDI9PSKEJtpILgrQNqadVeBa0MKmsdWTYdehc,1292 +torch/include/ATen/ops/max_cuda_dispatch.h,sha256=NT0jfu-2O7T2whYD6ZjkJdDH9Xsrr2S6sHs32eI1FjU,1294 +torch/include/ATen/ops/max_meta.h,sha256=LDWoMGbz5fCRPe8H_lyVG71hqEbaPAPbDcENFRH9eIU,1037 +torch/include/ATen/ops/max_meta_dispatch.h,sha256=3i8JSPJptX5dfA782p5dM_m43XoPtp7qKI7cyPxlrq8,1092 +torch/include/ATen/ops/max_native.h,sha256=Tnl-hfZd9c-pnqgN3jQkePBoXch6f1JEA-gaabOHGEs,1496 +torch/include/ATen/ops/max_ops.h,sha256=D9pg0vPrQ-pUSK27xcX6c_Hqx0p2iCPNVJFtq9M_6ng,6052 +torch/include/ATen/ops/max_pool1d.h,sha256=UZykDUQF_n44gfyaKwHNsoC1i98sXyOMppshXJHcEZ4,934 +torch/include/ATen/ops/max_pool1d_compositeimplicitautograd_dispatch.h,sha256=DF8sZvX435wLllAp6uvpUTgms25HffE1cgKSdBKBqyI,901 +torch/include/ATen/ops/max_pool1d_native.h,sha256=Khse4-_yfqRMVjUfl7iUmZl2ud3GqAUfUYDfalG8Aoo,613 +torch/include/ATen/ops/max_pool1d_ops.h,sha256=H06RPyIgHiv8vOKA5iWpwWLRa-E_aN0pm24C1-zTUdY,1380 +torch/include/ATen/ops/max_pool1d_with_indices.h,sha256=FtmN-lRXwo_qe2447OL5y1ee4vWlXm6zsmJqANoXmiA,1021 +torch/include/ATen/ops/max_pool1d_with_indices_compositeimplicitautograd_dispatch.h,sha256=C5kjki9wDdSlniaafgHM70uAptXDB_N20LU7ujYF9cw,939 +torch/include/ATen/ops/max_pool1d_with_indices_native.h,sha256=P-wCG7hor9_LqRsZGZOVt48qndPfMRgsijAACtI2G4w,651 +torch/include/ATen/ops/max_pool1d_with_indices_ops.h,sha256=lRoW0qtZBbJ6kQQIksYpkUTBvhQFF2W6OWNhLAOMWzk,1504 +torch/include/ATen/ops/max_pool2d.h,sha256=YOBhdC3Oe6rngr6FGHZOITRoCKzPJnEDf59ocPnTk0c,934 +torch/include/ATen/ops/max_pool2d_backward.h,sha256=b9NcdKjgINHRcG0-axS5ObuxBytD6xkpc98GNa1X1Sw,2182 +torch/include/ATen/ops/max_pool2d_backward_compositeexplicitautograd_dispatch.h,sha256=gQ7VUKhB527wOR2LVRrxQM5Y5FcwZ3J2TUYAlEZp1Uk,1210 +torch/include/ATen/ops/max_pool2d_backward_native.h,sha256=_YmBcypg5aRbj2IWIYl-gKXfgKP5mPlx8ap7nL_nIjs,665 +torch/include/ATen/ops/max_pool2d_backward_ops.h,sha256=P7KUliKbk9KJwHWCYtlTPUFCG7s5l4yqgXZbQwxwDH8,2655 +torch/include/ATen/ops/max_pool2d_compositeimplicitautograd_dispatch.h,sha256=9Gok7VNwNIHmV2NfC7fxifz_nFTTucD4vMQUNfmQhvM,901 +torch/include/ATen/ops/max_pool2d_native.h,sha256=Ph4XEcXdZ9WHKXsk6Q9nG1ZxJM4RAbL-tNIPptp2KY0,613 +torch/include/ATen/ops/max_pool2d_ops.h,sha256=UQgRmTNivHR0Czg_30ikS06w1uwdEzykvKD7TY8Taw0,1380 +torch/include/ATen/ops/max_pool2d_with_indices.h,sha256=NIKRXTUU4mf-bLaOt-hpNNXHYkgmyjaFujsQ8kUqqVU,2246 +torch/include/ATen/ops/max_pool2d_with_indices_backward.h,sha256=EbJOrZ68jZS8gq6VaV24-m8lrRb3O_NE6FYcNosw9NE,2476 +torch/include/ATen/ops/max_pool2d_with_indices_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=nGoahet0rfcevcKyuG56TOJh6cIRgbh8noXxmSrw-Ek,996 +torch/include/ATen/ops/max_pool2d_with_indices_backward_cpu_dispatch.h,sha256=lUEzB6hWsVCiLjqc2ll4C9r2p1XouZeYqe3v7tMIS48,1509 +torch/include/ATen/ops/max_pool2d_with_indices_backward_cuda_dispatch.h,sha256=HJxfFJ6ALfEI2gXa0su2rFCB8IXMNqi54YGvoAmX94c,1511 +torch/include/ATen/ops/max_pool2d_with_indices_backward_meta.h,sha256=aDLsZbMBPqt2jXXX4Qa_qkqNyAoXB1GRse_pzmt7lfs,773 +torch/include/ATen/ops/max_pool2d_with_indices_backward_meta_dispatch.h,sha256=u7b6dIwWfdxARQwo_3GP-bJxQgDH_MEQWdpsYt-6bu8,1511 +torch/include/ATen/ops/max_pool2d_with_indices_backward_native.h,sha256=Q4eHeBi2-N7kv6PV3TwI5nUtfgnfI85uPhSQGcV64j8,1250 +torch/include/ATen/ops/max_pool2d_with_indices_backward_ops.h,sha256=MMsiOSPQgPzdN_UbwItOVlj00h_4g4XoX7YGZfiZVp8,2933 +torch/include/ATen/ops/max_pool2d_with_indices_compositeexplicitautogradnonfunctional_dispatch.h,sha256=T5MFgLSN2CrBL-ec8cVQC8n0L0qV6_6lIL8JHVbLw2w,965 +torch/include/ATen/ops/max_pool2d_with_indices_cpu_dispatch.h,sha256=Ya8HebugKpVAxZwvxzenIVbhwld-JK6gpP5SszUp3ZI,1437 +torch/include/ATen/ops/max_pool2d_with_indices_cuda_dispatch.h,sha256=g86F4IdJFFVnwVQeWiIqaTyjZ8GY4AeMEtII0CHrSHM,1439 +torch/include/ATen/ops/max_pool2d_with_indices_meta.h,sha256=4jFf1gxEOfZInmj318fP55oCNDsc6WUqjofLDW_f4Po,704 +torch/include/ATen/ops/max_pool2d_with_indices_meta_dispatch.h,sha256=HUVhgX_v6sL4iuzPQXLee1LS1dUNlJR2ji6ZRWv6xrk,1439 +torch/include/ATen/ops/max_pool2d_with_indices_native.h,sha256=tujAfN4Jfr6n_M5z32_4StOwHjId4-PWo6Lkxg2oDYc,1127 +torch/include/ATen/ops/max_pool2d_with_indices_ops.h,sha256=_LFEdWGHmQsW9GuUAIRZOr9-ndVOQI2-zP5XFjDfDuQ,2729 +torch/include/ATen/ops/max_pool3d.h,sha256=HIlhzdpwIlmIsvZlVbDCwv4b5iEDUngpQn_APkGcl3k,934 +torch/include/ATen/ops/max_pool3d_compositeimplicitautograd_dispatch.h,sha256=-59HnY2XbtqxV1pnx0diBuSVITVyeCxnyC7aXE4EdbY,901 +torch/include/ATen/ops/max_pool3d_native.h,sha256=-6URFJakf47e4uAZSOIWr_O6Yf0qPQNIeaEFVVQsn4o,613 +torch/include/ATen/ops/max_pool3d_ops.h,sha256=HxZxiPeaiLv10S7iZaWrPuLw28ucwkAvbsEHuaFpn6E,1380 +torch/include/ATen/ops/max_pool3d_with_indices.h,sha256=48rmWLpMeP078YR0Cd-wnw1gO2PjApqtw7U-8msgk_o,2246 +torch/include/ATen/ops/max_pool3d_with_indices_backward.h,sha256=dE8ldxcf0vULdm4njdK96wwUrAw4JwtSX4JA1A_pkQQ,2476 +torch/include/ATen/ops/max_pool3d_with_indices_backward_cpu_dispatch.h,sha256=g9T7v0y6tUwzCVFeSiL0L_Ywsst0ZrfoUUsI2VGvySs,1509 +torch/include/ATen/ops/max_pool3d_with_indices_backward_cuda_dispatch.h,sha256=1UkC4-ToPZ3ZYtuEBAHRlALZ8H2s5SyqGCBXiGOUhzE,1511 +torch/include/ATen/ops/max_pool3d_with_indices_backward_native.h,sha256=PtaVDy1ptonW9TCorjFbN1ouqFL_rJOoXZXoGTOAyIE,1542 +torch/include/ATen/ops/max_pool3d_with_indices_backward_ops.h,sha256=Mq3dE3S04Semz8P4gmKoAerQIrVyyaz55epxhYQGlNo,2933 +torch/include/ATen/ops/max_pool3d_with_indices_cpu_dispatch.h,sha256=__GTLWy5DSpJjFbjGsNAPx-mnand2rtIgx15Wtkhb3U,1437 +torch/include/ATen/ops/max_pool3d_with_indices_cuda_dispatch.h,sha256=OJxVUg2NawWQ4mCgbx1rIbonSR8zDFxGhUjMNLj-Mz4,1439 +torch/include/ATen/ops/max_pool3d_with_indices_native.h,sha256=pNWmY28gj6ZuK9NFYT6cxJi7vg5MssK3N2AfRQtirLY,1426 +torch/include/ATen/ops/max_pool3d_with_indices_ops.h,sha256=jc_ehLhPE1LHguf2kjf267dK3bTqehcQ7ZcgG_cb2Qs,2729 +torch/include/ATen/ops/max_unpool2d.h,sha256=TPBNmcYYl68pRB9BGiY9RI6qPgWSrqHjphUgIkyV3zc,4433 +torch/include/ATen/ops/max_unpool2d_cpu_dispatch.h,sha256=Y5vJM3pG_qThEErm0kGhNUrm8P-Ge6qzeUmcQBSqBPU,1499 +torch/include/ATen/ops/max_unpool2d_cuda_dispatch.h,sha256=6LLpeLQzJQAJZJ5s5AavwziNRro5QpO9tfD6x1vsTfI,1501 +torch/include/ATen/ops/max_unpool2d_native.h,sha256=5J7c6DSPxYG0r_-D3gI0gOseOqVgAaqjZHek0iQojiY,1000 +torch/include/ATen/ops/max_unpool2d_ops.h,sha256=Ld6t2A2K5hz3ntS4h-QZf8NrFAa4u05wdlXV56juhbg,1987 +torch/include/ATen/ops/max_unpool3d.h,sha256=XmytVqXX2zh5jGL8OFAvInlMM_XAfJWqrhLkuaQzOAE,5411 +torch/include/ATen/ops/max_unpool3d_cpu_dispatch.h,sha256=11aqKZqsA1BaSqbM3qVgxe9XcL_9vRKaBut5vDSe3BU,1793 +torch/include/ATen/ops/max_unpool3d_cuda_dispatch.h,sha256=2uB5CE8MsqGfIDFNyJJqZbiNF0TCahD8LRMihrfp0ss,1795 +torch/include/ATen/ops/max_unpool3d_native.h,sha256=RIqHp4c6oqydbx1tMNYoKTfLkOLelIO3chZTipF79Jw,1196 +torch/include/ATen/ops/max_unpool3d_ops.h,sha256=NxNTSuZ3Y6FEoSpjyjxwE6VwOGL5-PoLqhRV5VdzCXs,2313 +torch/include/ATen/ops/maximum.h,sha256=BqjEc0PQH4dWyzN1gIGfhjbf3nN4YN6Jk9cL4QKE5RE,1178 +torch/include/ATen/ops/maximum_compositeexplicitautogradnonfunctional_dispatch.h,sha256=KKg9KPUCB80Lh0emHI4KNMDobeFWnyINTReOsZvw2xA,817 +torch/include/ATen/ops/maximum_cpu_dispatch.h,sha256=pghd81gWcT09b45l264XaXdvV3mSfUF84JYwWnaAHxg,958 +torch/include/ATen/ops/maximum_cuda_dispatch.h,sha256=pCVRIvSNume8t8k3N5X5A5M0xNpES1rttjbHt2oRRhE,960 +torch/include/ATen/ops/maximum_meta.h,sha256=gvY6gX3rS0ax8i5wqrJt6VfzsXnmlX34t9ZB2VGd-q8,594 +torch/include/ATen/ops/maximum_meta_dispatch.h,sha256=TgoZmCVzVoAgMXUROk3C0u_VY05zZEaKtscRbBKdfuU,960 +torch/include/ATen/ops/maximum_native.h,sha256=T6TnSlYalplUBGjJxX2H0k1sAoRSmpmX1kIN08aIbkA,625 +torch/include/ATen/ops/maximum_ops.h,sha256=MyBtXsTdGRb-xgA-6jyxAQndimF5mK7djKN3hYmLOiY,1725 +torch/include/ATen/ops/mean.h,sha256=sZ_MLorWpzglwzqH0Jjw7HgwQiJzrNLu4XMPSpHNY_g,3375 +torch/include/ATen/ops/mean_compositeexplicitautograd_dispatch.h,sha256=UHze8qfCMmukBNW8uk2eOh0d_sKxonLDFO8MdMPuUec,1062 +torch/include/ATen/ops/mean_compositeexplicitautogradnonfunctional_dispatch.h,sha256=aBpLpmX5708xJwl7qGYgng7t63c6kl4wNvaGN8dbJZA,891 +torch/include/ATen/ops/mean_compositeimplicitautograd_dispatch.h,sha256=ABeBA4kFpoZKvGeYdgPZVVSBVsA-bPMmKefFmUi2rzs,1179 +torch/include/ATen/ops/mean_cpu_dispatch.h,sha256=nTfWuFfm77cbn2Ab2mMn9AEHjYJV0jCSuvsFZ4dhECk,1159 +torch/include/ATen/ops/mean_cuda_dispatch.h,sha256=XPF9oKhY5yDoY0zvm8aWZitgcwu7SFIiZjEZXeQzMtw,1161 +torch/include/ATen/ops/mean_meta.h,sha256=JTabD6nyBFbAXyQPajZclsgHxvDwKZIJFjM_A3gUJnQ,651 +torch/include/ATen/ops/mean_meta_dispatch.h,sha256=VWWAmyrXaEdg5Wi6ILslojWoKHuOxy1sF7ZTo_-5fUM,1161 +torch/include/ATen/ops/mean_native.h,sha256=9v9SrGSsskli6ylpiK8v99IfSd9FCxDVJ_-SL0Jwnag,1541 +torch/include/ATen/ops/mean_ops.h,sha256=sKS8H38DZxDBpJDFIYL-vaFvZtM99BNcVTNoSNnCimo,5144 +torch/include/ATen/ops/median.h,sha256=cursRfpeorySS2pC0wD2N9R2cEaH0rxQeV3gK3gwbxA,3217 +torch/include/ATen/ops/median_compositeexplicitautograd_dispatch.h,sha256=NI0DRVMls9CQcIbk7ZQ9aIyTQBGyH1tB2oeC1ziWrpM,979 +torch/include/ATen/ops/median_compositeimplicitautograd_dispatch.h,sha256=kbnqBK7nzMdcrHhSDEYuNSbPJYiFqjAP747i5KtJ6yo,1155 +torch/include/ATen/ops/median_cpu_dispatch.h,sha256=L9rNNPcB-6eB5fPltEpOErlmdMbh2-K3aAKRlL5ntY4,1041 +torch/include/ATen/ops/median_cuda_dispatch.h,sha256=0U0DKCxKT3UjzwPSci0N0NA4TsV1FanDeUgidYpIeX8,1043 +torch/include/ATen/ops/median_native.h,sha256=qjutwhjV47nVSk-8n9JTbF1Ls16OGs5XwCqtAbn2Xf0,1329 +torch/include/ATen/ops/median_ops.h,sha256=6sLHXaP3OETe7ZohyJWIzHXOKes5E0ce0NURN67CBJU,4845 +torch/include/ATen/ops/meshgrid.h,sha256=MNouHXcXvRN5msUUiumyET6O-fuTLWQbD9dmrcDS4TY,906 +torch/include/ATen/ops/meshgrid_compositeimplicitautograd_dispatch.h,sha256=ZZdjr_0aAYZNtcE6hrK2wFBtY36i7QDkC9xzT4EwUOs,877 +torch/include/ATen/ops/meshgrid_native.h,sha256=ROlUjTBealDd8Qrdx_NleyjhBaU0i8NGOItlX7cGu48,589 +torch/include/ATen/ops/meshgrid_ops.h,sha256=LzgekW09isn02H3FxqYI-Q7OEW3ftISgpEPLevH74F4,1676 +torch/include/ATen/ops/min.h,sha256=oUC0zuxTzIaCDTX8n7KkldBhT1BC177drycJZD8XhTU,3796 +torch/include/ATen/ops/min_compositeexplicitautogradnonfunctional_dispatch.h,sha256=yZeiTF6DsejCuRMy90M7JEKF6ALrLVcp2Y1ChwFQyeQ,845 +torch/include/ATen/ops/min_compositeimplicitautograd_dispatch.h,sha256=MidFFlx8c5xpQi5Q7NNr9uG5kRFdwZNIxt7Ih6ukiBc,1428 +torch/include/ATen/ops/min_cpu_dispatch.h,sha256=bwBX5JE-M14njzn3lPfKH0JkPMeeGTKK-KRg-dyMA_o,1294 +torch/include/ATen/ops/min_cuda_dispatch.h,sha256=WquZLCzonaMeDRYUoJcgPkBio_4OTVxYA8tvYyCotV4,1296 +torch/include/ATen/ops/min_meta.h,sha256=d1dDf4WlMEc_DjhuBaaOqpPTN_JXZIKTmW8P-kG5WDY,1037 +torch/include/ATen/ops/min_meta_dispatch.h,sha256=0HbzU5-UhAgkLA2z8SoxdY9Uqfn9hHPCuMTAkPb40Js,1094 +torch/include/ATen/ops/min_native.h,sha256=cs9YfvQ2aiTbgmraCyOtaCpUtQLw867FzNpMfo04bwc,1498 +torch/include/ATen/ops/min_ops.h,sha256=qnvkGiUfSCuISIskkZOrnGBplu12txPb_ObHZhvyitU,6058 +torch/include/ATen/ops/minimum.h,sha256=FYIY4BjkSWQME-f4TY6okW6iJmvumO4z7q5DbJAPzSk,1178 +torch/include/ATen/ops/minimum_compositeexplicitautogradnonfunctional_dispatch.h,sha256=YViqkkU8aBEPsqtU_CIBn-IDUAhDTq46ZjK-zYOUfaU,817 +torch/include/ATen/ops/minimum_cpu_dispatch.h,sha256=YzBtN9UsGh6bYXNsxasIRHfnXj_vLVVGNEktglm5IG8,958 +torch/include/ATen/ops/minimum_cuda_dispatch.h,sha256=wA2E_-db0grMPAZSH-jE9xFzvjMZmIwqYgOTqNZFtYY,960 +torch/include/ATen/ops/minimum_meta.h,sha256=EOMMJ3MGNpx3lrtAI_wnZkx7oXc3qsmJ8sCZaq6OSG0,594 +torch/include/ATen/ops/minimum_meta_dispatch.h,sha256=ON8t_0TuLWdomudwTRnfRJlsj8fmkQE_Cw93feLpaVc,960 +torch/include/ATen/ops/minimum_native.h,sha256=7Fls4BqxVIOuX4_4CT0ghQphfTOZvkFyMWIBt1f2xNw,625 +torch/include/ATen/ops/minimum_ops.h,sha256=7n7-s9bBieljhaRKwPRzV_xSn7PpIIw0DAc3IpcKtg4,1725 +torch/include/ATen/ops/miopen_batch_norm.h,sha256=9HyLrvQnYs5g4SCKBIUmEXswlaoT4i0SUkYSZvarmMQ,2880 +torch/include/ATen/ops/miopen_batch_norm_backward.h,sha256=FFK734FyiEMcb5qOXkwWErE9sRKOikfDtc4WyMn2YVI,3021 +torch/include/ATen/ops/miopen_batch_norm_backward_compositeexplicitautograd_dispatch.h,sha256=JxlFLR6Rc8BFPvx9BGXEObWIjcnhrg_FBPecnVl_oWM,1601 +torch/include/ATen/ops/miopen_batch_norm_backward_cuda_dispatch.h,sha256=Rlczt86wXFdYbH7wgF50wPzJ8s-mO_qpbcbhjxU22g8,1046 +torch/include/ATen/ops/miopen_batch_norm_backward_native.h,sha256=WbelUkObdxewWqNk-s_aYjT0GUFgvrMiabvawu3-spU,1245 +torch/include/ATen/ops/miopen_batch_norm_backward_ops.h,sha256=Hl4rg9ryTAqZ4_QkfInOTB5NnGPflTCMwLusYulLvGE,3806 +torch/include/ATen/ops/miopen_batch_norm_compositeexplicitautograd_dispatch.h,sha256=QOW02ZhPLJhOigLkHEovbOFjTy9OxXyfXoDxwj7s-bA,1517 +torch/include/ATen/ops/miopen_batch_norm_cuda_dispatch.h,sha256=kuuLPvx5GxN6ZeIiLUc5unI7_f9sJ7_TdLl_wJNzMJo,1004 +torch/include/ATen/ops/miopen_batch_norm_native.h,sha256=Y_hqRoS-e5gKlIDsaT30zK63WtOyj_q8zbFw71zOPHI,1161 +torch/include/ATen/ops/miopen_batch_norm_ops.h,sha256=81TJ4LfLT9Ekj754aT1MyWrGptuGObbewbZWQcF89R0,3546 +torch/include/ATen/ops/miopen_convolution.h,sha256=gxKa-KA8HrtvqFtZXkKrg1Kvd8oAlKXjLy7HnEgof-M,7877 +torch/include/ATen/ops/miopen_convolution_add_relu.h,sha256=TL9BxV5kI6uIc7S6Bq1xwy7uODTYnE-sNaVaHrdP9Lk,2972 +torch/include/ATen/ops/miopen_convolution_add_relu_cuda_dispatch.h,sha256=WByDFmsltt6dKiDT1VUL2fFksVCrTST_QcwsFpeieTw,1291 +torch/include/ATen/ops/miopen_convolution_add_relu_native.h,sha256=tJBEvcn4RqjsoJTvy4QeIaG-q5fvMm2LmaGmS-2GhPI,722 +torch/include/ATen/ops/miopen_convolution_add_relu_ops.h,sha256=dhhxvh6uz3QuxguUyxtORguxwECk8guEc5FrFH2Rxjo,1815 +torch/include/ATen/ops/miopen_convolution_compositeexplicitautograd_dispatch.h,sha256=-4S46dpFOTXpePCHBmzbCCq2wq1ghUGYgoyjE_7JoOM,1902 +torch/include/ATen/ops/miopen_convolution_cuda_dispatch.h,sha256=02O5i3A_if7s_Fv78C3ckYm9QPeVbqOuBJh1U2FF-CA,1215 +torch/include/ATen/ops/miopen_convolution_native.h,sha256=N-YaTw-Gj6pb_f8jPEyyWSZhQqP96oQgwthIMitTqGI,993 +torch/include/ATen/ops/miopen_convolution_ops.h,sha256=iPGSPHJGwHlRWJlurc77nbzfo_vJlpmIHq4qN68x6Qw,3025 +torch/include/ATen/ops/miopen_convolution_relu.h,sha256=FabFfB-DmDF_7yjXCcMKhjifEtnPG9131ZukCPnxShU,2578 +torch/include/ATen/ops/miopen_convolution_relu_cuda_dispatch.h,sha256=FSik3rtgRJ-Me1H_ANYNVwXpqi1Oyr4mrq1kHoUB7eI,1153 +torch/include/ATen/ops/miopen_convolution_relu_native.h,sha256=j5nsC5O5vEH32lhxESgzWahgfAfL8nGuxBUZqLSFcHQ,653 +torch/include/ATen/ops/miopen_convolution_relu_ops.h,sha256=sSlWNsa_RwACiunqsZlhqvxp87bkA1qXkqVG2ToY6ls,1591 +torch/include/ATen/ops/miopen_convolution_transpose.h,sha256=bYzgbufuqPVUzpfQN4gcyHfkQ4sXnFIoCHODgEh_TpA,9093 +torch/include/ATen/ops/miopen_convolution_transpose_compositeexplicitautograd_dispatch.h,sha256=LX_c1SgDN7RzNScXmKs-IDy6M1mwuUWzhAhgYetauPg,2078 +torch/include/ATen/ops/miopen_convolution_transpose_cuda_dispatch.h,sha256=U8WL0Lz3w0gCD8nRqVeFgQHNf1YGgyhsKqcnSYXUWiM,1303 +torch/include/ATen/ops/miopen_convolution_transpose_native.h,sha256=kHYCoas2lzAbLossdfLu6umEcsGH745CEY0AlwneG98,1081 +torch/include/ATen/ops/miopen_convolution_transpose_ops.h,sha256=_3_KhoyCYkmxi5RZBAgjzC4MdLOgxSmJRNfB72gFX1Y,3321 +torch/include/ATen/ops/miopen_depthwise_convolution.h,sha256=dVO6vLBtTK3fXpT0lXl4EzvGpQcsdAaPyhBn_DnaOLc,8187 +torch/include/ATen/ops/miopen_depthwise_convolution_compositeexplicitautograd_dispatch.h,sha256=g3ORML6JzFIG_p0SjOUy-NINmoxguxdjw2dxkofV07o,1942 +torch/include/ATen/ops/miopen_depthwise_convolution_cuda_dispatch.h,sha256=fK8uNmj48AMrrDQxIAlZj-ELGxLW7kPjesuSMy-Hij0,1235 +torch/include/ATen/ops/miopen_depthwise_convolution_native.h,sha256=Xy8pgBXADEcQwORxNjn9LB0EuoLQE3ZnI90cZgACUGI,1013 +torch/include/ATen/ops/miopen_depthwise_convolution_ops.h,sha256=xHGP4fh1KaM3OmuoHbtUQf98Ee8LkhZ9GVugyVpW6KM,3085 +torch/include/ATen/ops/miopen_rnn.h,sha256=ieyRmRYT2ILb-m3Ez5ofs7oQjjwrHbvE49QdhqlFzCs,3710 +torch/include/ATen/ops/miopen_rnn_backward.h,sha256=iFLr1d4BetBY_WNICjr1eB1_ItD8gWro4Y-lCBlM3lM,4832 +torch/include/ATen/ops/miopen_rnn_backward_compositeexplicitautograd_dispatch.h,sha256=DoZX3-aSQa8CHtsoESjLPpYzPO6_irXA_RjSqHmYFaU,2139 +torch/include/ATen/ops/miopen_rnn_backward_cuda_dispatch.h,sha256=FCIJgaXJX-iBcR0tmAHnlsDW03YtbB6mu1JeXOioifo,1368 +torch/include/ATen/ops/miopen_rnn_backward_native.h,sha256=5ljTWIVLmxgA30vyxnlV8_uUTkN9cp-U7Pw27Fxlh-I,1836 +torch/include/ATen/ops/miopen_rnn_backward_ops.h,sha256=5II9qasw63SjPfVJyMwfSzsxE8JnKCiHoYHf8hqFPa0,5747 +torch/include/ATen/ops/miopen_rnn_compositeexplicitautograd_dispatch.h,sha256=AZkOJx4e9F-gnHlwUMX0DjjD52gF-dDixuhXd6J3pQo,1785 +torch/include/ATen/ops/miopen_rnn_cuda_dispatch.h,sha256=gNYIOz6OzeJ7Bk3s8TYVEfGPIiOj8Rbovomg_y3A8lE,1096 +torch/include/ATen/ops/miopen_rnn_native.h,sha256=Z0rMe492I-098oz3cYTwlC7KSPYWCwdZjhDRj18pZco,1387 +torch/include/ATen/ops/miopen_rnn_ops.h,sha256=yDcvl1-IKLF8d1w5ewhvxPlm1rcu4ioEUak-Ie30BUE,4354 +torch/include/ATen/ops/mish.h,sha256=5TWQa27egpb5LQr2XoQiid2Cr-FhdS-FIiZ-71JenSQ,1143 +torch/include/ATen/ops/mish_backward.h,sha256=v3xo5L1D9yVxWF3UBAIHa2YcSovFVrihbpupdHaOb3g,730 +torch/include/ATen/ops/mish_backward_compositeimplicitautograd_dispatch.h,sha256=sjaTqkPtn8DYRK9BKg9UdQ-Pu9GTC0NgTu0uVNeGBnA,803 +torch/include/ATen/ops/mish_backward_cpu_dispatch.h,sha256=bVODwoS__2M4DEA_Z5QYO7Jdt2SVPfeb3Xfcv1eA5Mc,759 +torch/include/ATen/ops/mish_backward_cuda_dispatch.h,sha256=Uo5MdmBtQwvTo5hwt7vR75_8sb45kGSDq1zwlu8Bchg,761 +torch/include/ATen/ops/mish_backward_native.h,sha256=OoS-oohBYl0O5rEpYrMDylKSMHLyrRZcdFVKxpVJqlU,613 +torch/include/ATen/ops/mish_backward_ops.h,sha256=vcdZ3WcWrZMwcUPgPl8QublWsHEkFlRiHgN9Pu7M_bk,1082 +torch/include/ATen/ops/mish_compositeexplicitautogradnonfunctional_dispatch.h,sha256=-ibYHDLqaY7uQmXRTpdWzBAAxQrERiQrEcAjj9nF2RQ,837 +torch/include/ATen/ops/mish_cpu_dispatch.h,sha256=7BfOMpIf3X27mStmRGBSwHo2RA2XvgeaDWlXbq9pM18,920 +torch/include/ATen/ops/mish_cuda_dispatch.h,sha256=69PawBuyzCIlcHgrkYXJ8MJy9u2tX34s-2t4L2GrKqg,922 +torch/include/ATen/ops/mish_meta.h,sha256=eyE13PWLNq2GzEdkEE9xLKXeGyGKdjdYkXK-POC6lbY,565 +torch/include/ATen/ops/mish_meta_dispatch.h,sha256=rRObNDUNIDNKE-iNn6tbPuUQfKlnY6AzeawySBqpyM0,922 +torch/include/ATen/ops/mish_native.h,sha256=sPw2M7xkY951QQ9PGFASRBlEwUSzljEuGBIcM-Qv3h8,590 +torch/include/ATen/ops/mish_ops.h,sha256=HyxWf_dLfBNGx7c-YhDUVIf9MyPIW8TzTjR4agXira0,2028 +torch/include/ATen/ops/mkldnn_adaptive_avg_pool2d.h,sha256=GCbiDDKcol_egSZNukrKP9zxP-Lo7i8agFpwK9VPMXo,1413 +torch/include/ATen/ops/mkldnn_adaptive_avg_pool2d_backward.h,sha256=As45MdkfC2zdxqAFVxn7vf1NdQkl-MMRwIcvImYoQWY,1512 +torch/include/ATen/ops/mkldnn_adaptive_avg_pool2d_backward_compositeexplicitautograd_dispatch.h,sha256=5IQtgzY_LduH3bknIOPNrIMWisl0hzHNYMmEqbl0Ez8,989 +torch/include/ATen/ops/mkldnn_adaptive_avg_pool2d_backward_native.h,sha256=xlddIjfcXgM_jSoB3vwYaZNvLjfgO3IJiaL8t5RWA1E,676 +torch/include/ATen/ops/mkldnn_adaptive_avg_pool2d_backward_ops.h,sha256=g1Qc9hlI_9TYPgtIcaDvsb5p-6oXXkenqbheurec9Yk,1929 +torch/include/ATen/ops/mkldnn_adaptive_avg_pool2d_native.h,sha256=jC6jVdunXOE59G6FX65WMphKBQE5SSKt1pjHvZdco68,652 +torch/include/ATen/ops/mkldnn_adaptive_avg_pool2d_ops.h,sha256=XXYd8wohsRWAqqpcnHa_nZRsxt7BuHDgW5C3qVpjVV8,1857 +torch/include/ATen/ops/mkldnn_convolution.h,sha256=wx61AqJ67J25CBYUr4mAccErtVovMeZdJ-r0vntSHZM,6917 +torch/include/ATen/ops/mkldnn_convolution_compositeexplicitautograd_dispatch.h,sha256=piK087UHd6MJ0P7djf_Xt6iQTqzic0eZCfLK5aGEMNc,2233 +torch/include/ATen/ops/mkldnn_convolution_native.h,sha256=vxhPyUc80_5StZmwBEW3hY7tMEz-anni8UPA9qqXUXU,921 +torch/include/ATen/ops/mkldnn_convolution_ops.h,sha256=Urb6TkLgI5bxLv_3iTmp50AdoJmf5sJFAD6EBm_ZQqc,2785 +torch/include/ATen/ops/mkldnn_linear.h,sha256=_OEtNEmOE6KnpPwBSfXKX6ydFuqiA7C0vxgVm0PxTLI,1454 +torch/include/ATen/ops/mkldnn_linear_backward.h,sha256=e9_IQfkSqYmEOElihS8VmBbgBQSxB0-492ogjH0CIN0,2096 +torch/include/ATen/ops/mkldnn_linear_backward_compositeexplicitautograd_dispatch.h,sha256=HshatuKdCbXJUkH0Damx9mGbmpzm8aYW3k3CoUYFlfM,1243 +torch/include/ATen/ops/mkldnn_linear_backward_input.h,sha256=_My9zn5LthMu6wwnwkUyRVbYVaIawwygvxBwL7F69V4,1634 +torch/include/ATen/ops/mkldnn_linear_backward_input_compositeexplicitautograd_dispatch.h,sha256=ainUrq6BsTfIX39oQ8UGaPZlfWaK48biV7tPwkaShrU,1035 +torch/include/ATen/ops/mkldnn_linear_backward_input_native.h,sha256=giYwRjs1rk_86aF1hM40LmNzkd4_UkgUiJ1_i-nzzvw,722 +torch/include/ATen/ops/mkldnn_linear_backward_input_ops.h,sha256=S7QCZ-DaTdNig6ghesQhvB4Bw9NBa1ggrraXdAPr7G0,2081 +torch/include/ATen/ops/mkldnn_linear_backward_native.h,sha256=_XGnOnj4W7-09om4I8AjGVKkbGK88Z5qnyE-8Q0RPEM,887 +torch/include/ATen/ops/mkldnn_linear_backward_ops.h,sha256=FNBsC5PeVubKdsS7g-nLQeC5Aq5KdrEd7Prqps1R7jg,2664 +torch/include/ATen/ops/mkldnn_linear_backward_weights.h,sha256=s9Q034IaTQZm9kad8L3n78jQLrV-O2CtsugKxocsfuc,1984 +torch/include/ATen/ops/mkldnn_linear_backward_weights_compositeexplicitautograd_dispatch.h,sha256=QM2v61b9EY6_DmLygIrYv-_Z1TA3vtUND8tGSSPNX7g,1167 +torch/include/ATen/ops/mkldnn_linear_backward_weights_native.h,sha256=SCpC121gl3Yg55DucbawpYrIW7Bz6jMbt5RC6YHiRC0,832 +torch/include/ATen/ops/mkldnn_linear_backward_weights_ops.h,sha256=b4V4jWhguRoCeBrE78sRFgLARabcPnceuDQuEBCJkr4,2461 +torch/include/ATen/ops/mkldnn_linear_compositeexplicitautograd_dispatch.h,sha256=lovBTNun6J6izRmXS05Zi98LB9_iwUTC9G7vG-E4OC0,1022 +torch/include/ATen/ops/mkldnn_linear_native.h,sha256=HH-x21wC2n5XJrO1tcPd37At_zpjnuR6zMGyb9rP8mY,709 +torch/include/ATen/ops/mkldnn_linear_ops.h,sha256=JtV7fGbTSRqaKljFj8lOlrWVXlGRUjLThIOuBQHrJ4c,2047 +torch/include/ATen/ops/mkldnn_max_pool2d.h,sha256=-_7X6DEsk1jAJ5PG5hVwKRop_ZOu_hDSY8_okaQqxHo,1967 +torch/include/ATen/ops/mkldnn_max_pool2d_backward.h,sha256=XCdIqyjgXy5irnEBLkr8m92JkBzTaOQ6z3eAwQW0g5s,2411 +torch/include/ATen/ops/mkldnn_max_pool2d_backward_compositeexplicitautograd_dispatch.h,sha256=W-GHd_uDPgGgJb61UDO_Nuqhz3Lb7r6bZW3FB1nin5k,1280 +torch/include/ATen/ops/mkldnn_max_pool2d_backward_native.h,sha256=FZLYLFKa1CqRqOEzIY3p_7H_RkXETz3HgcG_ugM33Lk,967 +torch/include/ATen/ops/mkldnn_max_pool2d_backward_ops.h,sha256=76gif8Zg83idgUGcDZQfFFFrRBKVvQ5eOEsKc90qWLg,2881 +torch/include/ATen/ops/mkldnn_max_pool2d_compositeexplicitautograd_dispatch.h,sha256=x-gDfK5YQ3KscofGu6LGMvKk14n3qVtujJBva6zMxtA,1142 +torch/include/ATen/ops/mkldnn_max_pool2d_native.h,sha256=ZCKBajm4SJgEbgEGQ2Zd-eJDJMCZHriyPcGCV2NXjTk,829 +torch/include/ATen/ops/mkldnn_max_pool2d_ops.h,sha256=jFL2IxH0cqgt9W-rHqwcJ-cC7frVFs1OQ5bf-jxb6o0,2435 +torch/include/ATen/ops/mkldnn_max_pool3d.h,sha256=ozEVzdQC_iOgelZFG6fwkvxPwdV-JktR-EO5yI4jU5M,1967 +torch/include/ATen/ops/mkldnn_max_pool3d_backward.h,sha256=OyaoxfakuAUcRP1pOBAeDzNDxbQA0D8gCmmNxhRfbQw,2411 +torch/include/ATen/ops/mkldnn_max_pool3d_backward_compositeexplicitautograd_dispatch.h,sha256=NOEZKZcef2UHL6jClsc_pGcwyO2riRVc85F2CRRvwmM,1280 +torch/include/ATen/ops/mkldnn_max_pool3d_backward_native.h,sha256=8R2kRx2BrI57XghQRtEyMchdPpP5ENrLLyJ0FTtpGl4,967 +torch/include/ATen/ops/mkldnn_max_pool3d_backward_ops.h,sha256=ed8DsMz7VKkMkEcCrwW1EOQRIUNf2U_KviEM52jlbPs,2881 +torch/include/ATen/ops/mkldnn_max_pool3d_compositeexplicitautograd_dispatch.h,sha256=vIDea-OnUwC_IdUQMpnlBLJn-rILWfhT3pD8ObMkaQw,1142 +torch/include/ATen/ops/mkldnn_max_pool3d_native.h,sha256=D5irLdTDOlCiIMfvuD5xQFVxqfZXo-G9UOxpnqef7gc,829 +torch/include/ATen/ops/mkldnn_max_pool3d_ops.h,sha256=DrjhUzAsNjqeUJkbHlf9ZLGJqedf42jBuZfhOXhxJBg,2435 +torch/include/ATen/ops/mkldnn_reorder_conv2d_weight.h,sha256=ZTxE0_JjLVZKQZRoSR19-grFMCTf_28MRXeonDspCPo,7765 +torch/include/ATen/ops/mkldnn_reorder_conv2d_weight_compositeexplicitautograd_dispatch.h,sha256=fgyhpMgWFHPAMhx6gaAz02pPpt7cl9tuylBjZFKKcEs,1757 +torch/include/ATen/ops/mkldnn_reorder_conv2d_weight_native.h,sha256=_cVR5vFLqcJ0zSrk9kIWet9M_z94YZ4lPe-Mxg-ZvOo,901 +torch/include/ATen/ops/mkldnn_reorder_conv2d_weight_ops.h,sha256=GDiZKqymxngPax4D4i-4R1tFYTyF4JpZiKQ78EMB3Ew,2685 +torch/include/ATen/ops/mkldnn_reorder_conv3d_weight.h,sha256=s50USZwr1m-xIExrN4uaI7p5OdaldZd4_QwKywZJJlU,7765 +torch/include/ATen/ops/mkldnn_reorder_conv3d_weight_compositeexplicitautograd_dispatch.h,sha256=1R4plubq8sLg-asv5Io_EWN4ujIO-uuBo4s1QVrjM3Y,1757 +torch/include/ATen/ops/mkldnn_reorder_conv3d_weight_native.h,sha256=G0KvlTt5e3uq1e9DGzgNZC33l_auBF0qRWc3BNLURAE,901 +torch/include/ATen/ops/mkldnn_reorder_conv3d_weight_ops.h,sha256=sFme_Al4wS4iEyDoSRzB8zrp65CilqtEBJxu2yKwX3c,2685 +torch/include/ATen/ops/mkldnn_rnn_layer.h,sha256=NcrGpYGWRK91S43P18n43suVFhyKiXP9eP1gYruPfGM,3755 +torch/include/ATen/ops/mkldnn_rnn_layer_backward.h,sha256=QjX9_8bpLxSGJeeudhOgSXXXlVHFhztomGxVTCcbg94,5579 +torch/include/ATen/ops/mkldnn_rnn_layer_backward_compositeexplicitautograd_dispatch.h,sha256=Zu4XQF3m3DvK0R9cXwXodiY7v60EiZxatzC5IlY1OyE,2465 +torch/include/ATen/ops/mkldnn_rnn_layer_backward_cpu_dispatch.h,sha256=UwF_W-oM6nMCFphzm_6fzRJvw0OnTmKeSTyMIwrHxiY,1392 +torch/include/ATen/ops/mkldnn_rnn_layer_backward_native.h,sha256=YHccmQd8ZOxc568gvwvw8E7y9LfMf8ZB4n11EXKbhZU,2025 +torch/include/ATen/ops/mkldnn_rnn_layer_backward_ops.h,sha256=CbeYIERrg8CNDNq2X_v0J4DM18FPqOb43CK6UGA1ru8,6474 +torch/include/ATen/ops/mkldnn_rnn_layer_compositeexplicitautograd_dispatch.h,sha256=9hMvweCvF-PAgR_aQ75FP-yAhd6xz7llMQZBjpGBkUg,1761 +torch/include/ATen/ops/mkldnn_rnn_layer_cpu_dispatch.h,sha256=EcOtf3uG9B90tGTX9B2nlGegs2YcfWx6XOYcuOaaHfg,1103 +torch/include/ATen/ops/mkldnn_rnn_layer_native.h,sha256=-BZMyuW-TpvCxn8sUEozUF5CxVzk-K9U1OtjHsYMh6s,1384 +torch/include/ATen/ops/mkldnn_rnn_layer_ops.h,sha256=4lpg6Yd7emrelur4gm3Da3SsztMaLDMNZZN_IOmhYoA,4337 +torch/include/ATen/ops/mm.h,sha256=lFj1eTLVWmSFlAFK-o5KCANAhriow_CVHSJAzK0pcHE,1953 +torch/include/ATen/ops/mm_compositeexplicitautogradnonfunctional_dispatch.h,sha256=ReNeYLeW2Oo6wzkTDXZ4WYv2FThtaA1Lh1D3--tv8DY,811 +torch/include/ATen/ops/mm_cpu_dispatch.h,sha256=5PdBe8AGnCEzTroRbrpiF7CZ-W8_9SA9Y-0XcQaCA2E,940 +torch/include/ATen/ops/mm_cuda_dispatch.h,sha256=p1Cx5TH4NsH5cgO5o43AilutX2uXFl9UX0ZFEvSIds0,1294 +torch/include/ATen/ops/mm_meta.h,sha256=0S7eY1snt14trfNZTFDRs-F7w_p1J4o76P044ow5n6A,588 +torch/include/ATen/ops/mm_meta_dispatch.h,sha256=jYXxC7gP-lJ6pE0E6clafVELaW7DDAbWShDhX_l7hsk,942 +torch/include/ATen/ops/mm_native.h,sha256=HR-UAz-1t87CRyI0lMRbvcxDT-0OwCngmMNO77IZvmM,1414 +torch/include/ATen/ops/mm_ops.h,sha256=n-GgHYslqay1Ndy81hvHbzNS1roe8TSig5TCvww25CU,3136 +torch/include/ATen/ops/mode.h,sha256=ds48Fntv0C78ZAuqzwI9viLodYHfeq_1ctZcSydGlxs,2621 +torch/include/ATen/ops/mode_compositeexplicitautograd_dispatch.h,sha256=yVjw9RBIyefmqmqCh4IwYpVi-ZDfiWk1VhlndK54bm8,1030 +torch/include/ATen/ops/mode_compositeimplicitautograd_dispatch.h,sha256=2cjpJiDxzaczkC3bcy-FxnFkC-rp__vLee7UyN4BAY0,1149 +torch/include/ATen/ops/mode_cpu_dispatch.h,sha256=LXChVP5tLSI6szhpdOuxZ0SkCf9o2Hvu-brMglWFvIA,779 +torch/include/ATen/ops/mode_cuda_dispatch.h,sha256=kVBHurRgXiDpsiIHkEE3vzPMd1dTBH3YJgw2pqCHPsE,781 +torch/include/ATen/ops/mode_native.h,sha256=u6YFmXD_fLisSLiryqryPaK0NUdtWtZbgXS-RGo5b88,963 +torch/include/ATen/ops/mode_ops.h,sha256=hqp9oU63Rbt2PPtKvMBPAubXjDvW_TO6nhwG4SLCd0U,3693 +torch/include/ATen/ops/moveaxis.h,sha256=eJ9YTeMiM3tANKPCnCAGyT8YJtEaR8IZClwwPtzx8VI,1016 +torch/include/ATen/ops/moveaxis_compositeimplicitautograd_dispatch.h,sha256=63FIf4CY-v2SiJFepjN4hutOjXSIXSkyinG5xgS37U8,912 +torch/include/ATen/ops/moveaxis_native.h,sha256=75kgu40C_b7fQDr1k8AX2m3-fRJI2bikdKLvobwcOCw,624 +torch/include/ATen/ops/moveaxis_ops.h,sha256=O60Gp50LkFnrn7thF_mwXZN9_kanCYGABZgyAorHMNQ,1809 +torch/include/ATen/ops/movedim.h,sha256=YuxSExJT2pTorb2fcx7NHoTee_sX340GJ-R1dlHex50,1009 +torch/include/ATen/ops/movedim_compositeimplicitautograd_dispatch.h,sha256=wB-0aaLseyYN1YOEQCyKxxsx2pJJ1UbPqDm2Jcb4z08,910 +torch/include/ATen/ops/movedim_native.h,sha256=K607vG305fCJuKIz9PmhgDLX3XxnsXBkSknVhEU8fgQ,622 +torch/include/ATen/ops/movedim_ops.h,sha256=mGxm1VUs7-HQPq3a6__0lESPUtrOkcGNb43AJcOJPE0,1803 +torch/include/ATen/ops/mps_convolution_backward.h,sha256=IWXEIGXqpTDfQPxJA7ACWZotAxekgd9aJ9iCRpvP4f0,8953 +torch/include/ATen/ops/mps_convolution_backward_compositeexplicitautograd_dispatch.h,sha256=Ll7yey86REXlxLuN7b7LmdCESa3p4zstiVMjtCQoNPs,2194 +torch/include/ATen/ops/mps_convolution_backward_native.h,sha256=Sqs1r0LE0hYYMHiSAV01C9mt_M7S_80o4qu3cB_5Q-k,804 +torch/include/ATen/ops/mps_convolution_backward_ops.h,sha256=Tm4JOAFwjNgF22BzD9LI6QQen5QL9126RRAPmIG2Wo4,3394 +torch/include/ATen/ops/mps_convolution_transpose_backward.h,sha256=LjxTSnLm5IfhB4Q-O4Qmv0J59LfhxsdrMtq2X91aTU0,9689 +torch/include/ATen/ops/mps_convolution_transpose_backward_compositeexplicitautograd_dispatch.h,sha256=6kAymuyRJtQ6yai2CYT_r2udh2LSas9MTt0d_5WrhyU,2242 +torch/include/ATen/ops/mps_convolution_transpose_backward_native.h,sha256=TarCja-zmFPKKvTr3JNjqks9k2MVYMoTvAjjuN1vfMQ,818 +torch/include/ATen/ops/mps_convolution_transpose_backward_ops.h,sha256=I7sh7K7fushfBotu6vdx1h74zU9THjkNsjGVGSCg8HU,3529 +torch/include/ATen/ops/mse_loss.h,sha256=KWVCuW6MV0kNroFu5QIgdE9ugJ-FAxZwpVcOlSq5OS4,1387 +torch/include/ATen/ops/mse_loss_backward.h,sha256=mzWCeOZzI6iZLjkiOrzW1_eh6mpJtWjy_LSmV3i1xdg,1687 +torch/include/ATen/ops/mse_loss_backward_cpu_dispatch.h,sha256=prsBkiByBVplwQwpPL6rcwkuHJQHzFumxoqWe86wMc0,1158 +torch/include/ATen/ops/mse_loss_backward_cuda_dispatch.h,sha256=k260nCwJOwDhqtC2PHfTluBlqWSBKxqaZlKYZJgGi60,1160 +torch/include/ATen/ops/mse_loss_backward_native.h,sha256=SGmGq_W7a4Kn6Joao4OAk_vjLCe7zXDUS8TqEaFiISk,739 +torch/include/ATen/ops/mse_loss_backward_ops.h,sha256=w1ZyvRfRLlVtUYSPLb8EeFsUqCkigj0pIqPNrvnmEVY,2165 +torch/include/ATen/ops/mse_loss_compositeexplicitautogradnonfunctional_dispatch.h,sha256=wzoVe5Ba0jGdHzrYtqkpLp8aK3DJDDJqAKMtmzPLsAQ,858 +torch/include/ATen/ops/mse_loss_cpu_dispatch.h,sha256=6ZVxCEppchCoCnWGbfhNpNQwIKv8_WEDpyJCCC3fo04,1061 +torch/include/ATen/ops/mse_loss_cuda_dispatch.h,sha256=ZP4HTD0MG8FfGl5OWy4IvOHhrMqswu_wusCfAr46I0M,1063 +torch/include/ATen/ops/mse_loss_meta.h,sha256=DN4iw-IPfo-2tev9vcmzyh2_GR6fMmUkkotKEn5swpg,615 +torch/include/ATen/ops/mse_loss_meta_dispatch.h,sha256=wH6I1oRvOpI6RSnxkIYkyHoJ_yKFXX6x0KkFS-xjm2w,1063 +torch/include/ATen/ops/mse_loss_native.h,sha256=u8QBXgjIrITIUuZi081t3XwJGuZPp2gbJd5-PvmAi9E,648 +torch/include/ATen/ops/mse_loss_ops.h,sha256=HW67SY5gXhWR_GiBeG35Yl7hYWMmEGqQHaPn2gcSEBs,1871 +torch/include/ATen/ops/msort.h,sha256=u4Zn3h3lbbeXrG0RNKbFBunQGyRNsO01ItdS-sVFYIM,1017 +torch/include/ATen/ops/msort_compositeimplicitautograd_dispatch.h,sha256=9Az46MGiuCu0enLjDsr61JyiCbNcoXskzCDxdmUzFPQ,918 +torch/include/ATen/ops/msort_native.h,sha256=ZOWRFwc2-XguWwPz6zRWuGZTgDHv6NHNZfMDIARQQpg,552 +torch/include/ATen/ops/msort_ops.h,sha256=pktZcWIuFL2zDttdwZK4xYkoHYQ-sbAcTHUCVJLQjJ4,1541 +torch/include/ATen/ops/mul.h,sha256=-hp5yv98GUYbY5KNM5TlK3_3ypHcQ6HtLgZBGoJ4sVI,1833 +torch/include/ATen/ops/mul_compositeexplicitautograd_dispatch.h,sha256=jEUDiEx5YLIIzB2n7F1C1JR2DKu7kyBX5uWh544STWE,1064 +torch/include/ATen/ops/mul_compositeexplicitautogradnonfunctional_dispatch.h,sha256=4Cxk7Qbusyzq3Hj-wvG6lPHkKlk3JaLhvl-wUboT-6Q,887 +torch/include/ATen/ops/mul_cpu_dispatch.h,sha256=PubA9Dmbd9biBychi74OR_taVAhP4Q3D4ZDxDunh6rM,1020 +torch/include/ATen/ops/mul_cuda_dispatch.h,sha256=pT_BMvqChXy5ih3nf4V4aCxMoanv2ZYdM6maN7r329I,1022 +torch/include/ATen/ops/mul_meta.h,sha256=suSanzk-R1RnuaBL0rSjM6zYVwoS2nBsFmv-0cBYUDk,597 +torch/include/ATen/ops/mul_meta_dispatch.h,sha256=uCHg0P3BoO1Mf07_NsrUU6c4DNnPnqubU3drOWzbFmA,1022 +torch/include/ATen/ops/mul_native.h,sha256=61rocPkSV9h-vAdp72q5zCKvpvKF0M_ChGxVkpIgvww,2484 +torch/include/ATen/ops/mul_ops.h,sha256=ZBSSfoyX0KofQUhpYs-sqRVkX2B1T2bts0Gx_4595_4,4198 +torch/include/ATen/ops/multi_margin_loss.h,sha256=DwEZviv3NOZlHvTjqHei5jBQZL71--TL5wrQOkjrGUA,1977 +torch/include/ATen/ops/multi_margin_loss_backward.h,sha256=yokBu01-7esnkhH_DRYeSjdtbC4Wjgw3g448dDVQWZM,2312 +torch/include/ATen/ops/multi_margin_loss_backward_cpu_dispatch.h,sha256=D4hzTedMU_p7kBUWseq_-AyvTEJKQtLON0WQjk2BZ-g,1510 +torch/include/ATen/ops/multi_margin_loss_backward_cuda_dispatch.h,sha256=uU06661kXvpnsKC-nW7f2W7_ORSrGSvRtrSdBOWPu54,1512 +torch/include/ATen/ops/multi_margin_loss_backward_native.h,sha256=uneg8VlE0CHICvf1SZYlauWGUpb0gPXBSk4QnTBbuYM,1528 +torch/include/ATen/ops/multi_margin_loss_backward_ops.h,sha256=Xl_nAi6wCvQKQcf67HR5CtZg1DUszS1RkrB1QSE6Doo,2847 +torch/include/ATen/ops/multi_margin_loss_cpu_dispatch.h,sha256=8vuVp8lt6jZPRGs7hbdKkT4bw5iJNgzBmMLIvDGqiEM,1381 +torch/include/ATen/ops/multi_margin_loss_cuda_dispatch.h,sha256=TiESz-gnu-pXt_8srOmrP3_UHWMwwzFlbESgCDG6PJY,1383 +torch/include/ATen/ops/multi_margin_loss_native.h,sha256=aZg4CpWNPQa3oS-E-epdaZzKE3nqoakwqU02ER1CKzw,1358 +torch/include/ATen/ops/multi_margin_loss_ops.h,sha256=Vdb_hoV1CReUOxKgKouupdnF8HwUcJ_eD2udqIUHBJc,2551 +torch/include/ATen/ops/multilabel_margin_loss.h,sha256=P8rNxAjQ0gj3xoXuUDm9V3uQMgvDPLDTS_SIY2-zg7w,1527 +torch/include/ATen/ops/multilabel_margin_loss_backward.h,sha256=qxQ3N9qkCgWBVSVL_LCK50DCJ9i8evbjPXWMdPAz-d4,2004 +torch/include/ATen/ops/multilabel_margin_loss_backward_cpu_dispatch.h,sha256=tHGtNMwj4_hMZc0iYfI7pKc4V6v-lC6d-0PYQFpDDZ0,1290 +torch/include/ATen/ops/multilabel_margin_loss_backward_cuda_dispatch.h,sha256=ztcKAbtClLZTFmf40Wi17o-_h9tf_QBy4YqAZwMMJXQ,1292 +torch/include/ATen/ops/multilabel_margin_loss_backward_native.h,sha256=HV34WBrXOYWB448q-y-opHDfJzm7CoRNm_IMEqfCzrY,1250 +torch/include/ATen/ops/multilabel_margin_loss_backward_ops.h,sha256=IFEjflB5c0WokNxm-5PvO538lwhm4Ttm7TwSGKc1bP4,2445 +torch/include/ATen/ops/multilabel_margin_loss_compositeimplicitautograd_dispatch.h,sha256=78u8wCES5KDdQni7yL6CIWml732XLYsgeNZg2IMJSKE,1147 +torch/include/ATen/ops/multilabel_margin_loss_forward.h,sha256=NOKz8TPCb2fzJpEddgOpxF7FMb21lcTGttOeyneoIPU,1830 +torch/include/ATen/ops/multilabel_margin_loss_forward_cpu_dispatch.h,sha256=IpM-D4rJyMjV8vt8vfaBPyFlimLBp2m15_nPOkyZXnQ,1220 +torch/include/ATen/ops/multilabel_margin_loss_forward_cuda_dispatch.h,sha256=bkrgCtg7hYcRFM2agOfQYADEZhNcLVHNTPfGsakARDs,1222 +torch/include/ATen/ops/multilabel_margin_loss_forward_native.h,sha256=2P38XAc-SCFtqPLsmmKAUXAV2xR7tOiD4Ng3F7xGTYg,1142 +torch/include/ATen/ops/multilabel_margin_loss_forward_ops.h,sha256=aU2m48IFHy4lexSnX5wNr9YFfdjwWNromRKkFqsBU6E,2292 +torch/include/ATen/ops/multilabel_margin_loss_native.h,sha256=ikXiNt3UCrUk8tvHg2wtmk1gSd_9R0rnwxdMuHq23Jg,698 +torch/include/ATen/ops/multilabel_margin_loss_ops.h,sha256=lvK0e6-qkFIqteROKgGLpb-FgYLOKAwbQfUgmwzwzic,1955 +torch/include/ATen/ops/multinomial.h,sha256=FOTte-Mnq1spGRH0fj3emMoBym9Z47_gczLbpnYI0Dg,5080 +torch/include/ATen/ops/multinomial_cpu_dispatch.h,sha256=2X6YWmWi-SiGuyfxTAJUbRVT39oYKjN29UqsGIV7Wos,1721 +torch/include/ATen/ops/multinomial_cuda_dispatch.h,sha256=GD4Y7fzOl-fwLEC2HtHP4ZFU8NpaA9na6IDyX8Pu3cw,1723 +torch/include/ATen/ops/multinomial_native.h,sha256=cyw_Mm01brAZ7f7U7rkTgjEShFeNyVMzKkZS5S-TU_s,747 +torch/include/ATen/ops/multinomial_ops.h,sha256=wF9O6C0WtX5nKJW_pmo-S-DqhLI3P3SSbo_a4gCSykw,2164 +torch/include/ATen/ops/multiply.h,sha256=w4B_k7yXxmKX-FlicfEoEB2HvxirJDo7OvMI8nMbZKo,1404 +torch/include/ATen/ops/multiply_compositeimplicitautograd_dispatch.h,sha256=JVc8j_zycpTTwTEds50BsZk3ARMCKnO18xq-tEkdv54,1245 +torch/include/ATen/ops/multiply_native.h,sha256=bsrxDs3fj2MO43HiiY1r0AB5-O818qjuBPcr0RPtG-o,850 +torch/include/ATen/ops/multiply_ops.h,sha256=exohzADOR7W9TTqsyUr_QamjopU4Bwt2BChdhAGemN4,3585 +torch/include/ATen/ops/mv.h,sha256=u3IUzH67k6JnVBTHIIWvrcHVqOyoqH3klhgMZRq7z2M,1110 +torch/include/ATen/ops/mv_compositeexplicitautograd_dispatch.h,sha256=lZCLFGCvxrwdhMImb-9WFpCfholt8zwix1JHc7p0zpM,981 +torch/include/ATen/ops/mv_native.h,sha256=5RV50Poh9yxJ29KzZtObih-RJTT-tUoTxNgkc8FdApc,675 +torch/include/ATen/ops/mv_ops.h,sha256=v1W59DvacK2bJ8PWfkdmoslXyl3_jIndItSFALbRDrQ,1683 +torch/include/ATen/ops/mvlgamma.h,sha256=JeIkGTjbh-BVFedrDFbo038EhIGTPoZM3CSVX5bnMBU,1110 +torch/include/ATen/ops/mvlgamma_compositeexplicitautograd_dispatch.h,sha256=JfMNo-T0tAdZIlt_AK8532RSvdJJGCepN6YkG2d0x5M,841 +torch/include/ATen/ops/mvlgamma_cpu_dispatch.h,sha256=U5q_MQLMvwJRvK2Ri2oEtfv4kN_ov_y97lZJpsnzeGM,849 +torch/include/ATen/ops/mvlgamma_cuda_dispatch.h,sha256=WYb2MIECUwJWCGovItVl7YXftKqjv9t6dJO0_FlVO50,851 +torch/include/ATen/ops/mvlgamma_native.h,sha256=HPbnWWOAWlsXxDRi342ZJvFAiJ1rWynlCEoQRP-10SM,644 +torch/include/ATen/ops/mvlgamma_ops.h,sha256=MkM7efmltJ-x0xaCSntM7HmDYe_UmzmWmbM1n6wpoYA,2178 +torch/include/ATen/ops/nan_to_num.h,sha256=pfDvWNNnmIIZGO1nEWZhfKVK5qSywxdRzIjpVGhq0mU,2040 +torch/include/ATen/ops/nan_to_num_compositeexplicitautograd_dispatch.h,sha256=qRnI0waPAW2bdK9ORwqlvbuFtFsmUsllFi9lojo0opk,1099 +torch/include/ATen/ops/nan_to_num_cpu_dispatch.h,sha256=WEmxvQzCzYLv0JwlKsDSotUe4yRXZD5qCpSyKJsyAdo,1062 +torch/include/ATen/ops/nan_to_num_cuda_dispatch.h,sha256=DQvGmtTbZhliEt2htxmzLujaQPB94uFrSYxEt4_0ygA,1064 +torch/include/ATen/ops/nan_to_num_native.h,sha256=JFUbouGH5kzN8GqaxskZiOmIFg-HpHT6ogI9CU8-JvI,1571 +torch/include/ATen/ops/nan_to_num_ops.h,sha256=9RImkFeO4TwFnheNUShI1NBs0gYKphBr7pSy2i_GPRQ,3036 +torch/include/ATen/ops/nanmean.h,sha256=i-GI9XCdGXI4Zf0mEUN7JKdNzG12Sq-T5WhIBZ9mxXg,1607 +torch/include/ATen/ops/nanmean_compositeimplicitautograd_dispatch.h,sha256=04JYzJ8PWiHknDx6jHgh6T2Ty5WV3XWat-6Mnu6awrg,1242 +torch/include/ATen/ops/nanmean_native.h,sha256=SrxJt0foAtuU7bWzmdCbJ96Z_nc0UI0hDNDJOX6wMTg,756 +torch/include/ATen/ops/nanmean_ops.h,sha256=4Z0JB-pYtMD_2sO44IeSYnOND2e7wrmz2vXhehJT5vE,2136 +torch/include/ATen/ops/nanmedian.h,sha256=pS1_vQ64dtKxg-5NzXoeSy_P_lM8hixxqeriPn2imc4,3301 +torch/include/ATen/ops/nanmedian_compositeexplicitautograd_dispatch.h,sha256=iIxLfSVV3FxrCFhK7FTSjyloybUN700orv8HItOEahE,988 +torch/include/ATen/ops/nanmedian_compositeimplicitautograd_dispatch.h,sha256=8Nh-81uGIeKupoirioYUEH5S_nhKBNsg7M5fW4gCOAU,1164 +torch/include/ATen/ops/nanmedian_cpu_dispatch.h,sha256=vctDDPRonNKcbe-RGaJdAXaLXgT1SVMUZbnQqFEswRc,1050 +torch/include/ATen/ops/nanmedian_cuda_dispatch.h,sha256=5JLcRz9B0xyLsNGWQ5xz19kPG3XqYS8Hh7pXNK5dZ-g,1052 +torch/include/ATen/ops/nanmedian_native.h,sha256=tXESTHMpYOUgyIcWRxCKE6WgfkqMmplWU6LGTqt9ojI,1353 +torch/include/ATen/ops/nanmedian_ops.h,sha256=Hhccj32y4grtNqo-f7FA12KAMINHAoVnjPeF1o5Q0eE,4899 +torch/include/ATen/ops/nanquantile.h,sha256=DWj5R2y6lCTpaqwFeSgIUmPlVpFc8v1zj47wL5XmqFA,2994 +torch/include/ATen/ops/nanquantile_compositeimplicitautograd_dispatch.h,sha256=bkAXVMQhD5eyhizwkif64Yj2P14qDFb_a0uqIyzsscE,1834 +torch/include/ATen/ops/nanquantile_native.h,sha256=cCHnWGxkPJmGFmlQ2w2as02iUqr9ZV4jo2dZfiSXDl8,1134 +torch/include/ATen/ops/nanquantile_ops.h,sha256=irRfDsZ1m3ku2C1170J4LnY0r7-ic2YCFnNDvKFDl7Y,4026 +torch/include/ATen/ops/nansum.h,sha256=O01JO9mCCYLcGmVCB5c6Uy3DFgKuVrr1QYt3BO5Si6E,1597 +torch/include/ATen/ops/nansum_cpu_dispatch.h,sha256=SVSAfgp_ZW9QRsF9qba6bg4iGZDf6RW-gQjHKVdQdcE,1195 +torch/include/ATen/ops/nansum_cuda_dispatch.h,sha256=jZ2aklnXDePzOxP7A_8QpkolPJAq1ZR3ffppN92Airo,1197 +torch/include/ATen/ops/nansum_native.h,sha256=FRKCUYHTDZLWZGvB8RuReB7Z_QsLpGdtwp3b5bgZAOk,754 +torch/include/ATen/ops/nansum_ops.h,sha256=KwXzk5C56PD1puRKljb9ySlQxdLKhzTHXE8jkX-e5bA,2130 +torch/include/ATen/ops/narrow.h,sha256=yZwArNomCj02lYHKpKeWnq0DahSpB_ZIKgOgmlw7zkI,2638 +torch/include/ATen/ops/narrow_compositeimplicitautograd_dispatch.h,sha256=oyEHgOR4sIWBFraHk2R3t_-9LN5NDaWKPrvv2n1MKs0,1150 +torch/include/ATen/ops/narrow_copy.h,sha256=4ELc5Z4z81JNUphubIc1DgdGAzb1rrQC3hmxEZmEVxs,4084 +torch/include/ATen/ops/narrow_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=WD9km1plJ9m4iDKP7zllGw1kkhGoi-LvksHuHsin80s,957 +torch/include/ATen/ops/narrow_copy_cpu_dispatch.h,sha256=aqWzrVAs2JtXkKTv_2XcpCfdu_emEKYGz-4tNVZK5aU,1427 +torch/include/ATen/ops/narrow_copy_native.h,sha256=i-wr0MPyn9357GtyZhcW3qCxHXzDnZA7GF117eLFBVc,906 +torch/include/ATen/ops/narrow_copy_ops.h,sha256=jvp6ys6xmr13m0YvS5fGAbyEmMe-7cSb9m3lpTJWuHI,1931 +torch/include/ATen/ops/narrow_native.h,sha256=W8OllgEnpJzpjevj5qoJ4wnZK8ViJfvJ2aOe38F0b4c,782 +torch/include/ATen/ops/narrow_ops.h,sha256=08E6NVAVtQpNxrKQtNk1PtKbUy5-1ckl83c1b7ex2Oc,1864 +torch/include/ATen/ops/native_batch_norm.h,sha256=d53zuBYNDyuhqeR3EthQJVykHgzViZT8DUrAA-NUPSU,2802 +torch/include/ATen/ops/native_batch_norm_backward.h,sha256=9ACb2XM2YFq89rztNvgukEoMoZSgkAziveORvvewC9I,3336 +torch/include/ATen/ops/native_batch_norm_backward_compositeexplicitautograd_dispatch.h,sha256=42oFXxwUu-kBJ7cbhaobVxtu75bduf1QUlP7PrqOaVE,1719 +torch/include/ATen/ops/native_batch_norm_backward_cpu_dispatch.h,sha256=v_D1ZtMiTAyb5imNTgl9bA4_TlDWQbEDVtWZr8QQv6c,1103 +torch/include/ATen/ops/native_batch_norm_backward_cuda_dispatch.h,sha256=3NtZvdoJ5JbcKG2IZIyWCjxokt7DHjoL9yPEingr7hg,1105 +torch/include/ATen/ops/native_batch_norm_backward_native.h,sha256=OzikzF844h1kwhJ1UCBM4x5G9MCej_CY5N9hKrK_a2E,2232 +torch/include/ATen/ops/native_batch_norm_backward_ops.h,sha256=6wYS8Q4YkraOfhE4U69LSA2MFi3Ij0exvSeOqFBJ6Xk,4192 +torch/include/ATen/ops/native_batch_norm_cpu_dispatch.h,sha256=e8-Xy26Rr8zJoo99L_DVfeO3BHYDZ_aBnjpRhrm_1wI,1816 +torch/include/ATen/ops/native_batch_norm_cuda_dispatch.h,sha256=wMYs2QM3mdRTTtygh1PGNL0LDMKy3fjvg-_QwhbTDLQ,1818 +torch/include/ATen/ops/native_batch_norm_native.h,sha256=Bp50N_e6XLCnzClgCNGVLSphgmEPa7lgnXXHZgrPNqo,2223 +torch/include/ATen/ops/native_batch_norm_ops.h,sha256=UobqS2qbFj60J65viiHxsW_EjjQGTKdShnggCkiDlmM,3551 +torch/include/ATen/ops/native_channel_shuffle.h,sha256=iVb50eLhwqrdoVam4-1RshQiBaZue8pFHTbmxpFpHvA,1481 +torch/include/ATen/ops/native_channel_shuffle_compositeimplicitautograd_dispatch.h,sha256=EgPaeX8pigfObD21AIgI9rq7IDkJUv6WwUDBKAc9jkU,893 +torch/include/ATen/ops/native_channel_shuffle_cpu_dispatch.h,sha256=ckCrqMRnuAkwJBAZ84zT3YwRxbT9f57rj-nL0Zh8d84,849 +torch/include/ATen/ops/native_channel_shuffle_native.h,sha256=eF39fOvuFi54Dz7V40jQ09KpSazjkvAT4oJ-CXFuSfQ,589 +torch/include/ATen/ops/native_channel_shuffle_ops.h,sha256=j4G3GF4wVCCImlj7iutoww8O7gFWfWTRjvI0cmoFcg8,1073 +torch/include/ATen/ops/native_dropout.h,sha256=1lgOqpjMX1jMqWbsNe6s-RgtmxPBuI_jgojttAYO4Yc,1536 +torch/include/ATen/ops/native_dropout_backward.h,sha256=EMZ2o22HM9kyw-O-dLupwDGIkerIpLZ2lo5mS3a-6v8,1494 +torch/include/ATen/ops/native_dropout_backward_compositeexplicitautograd_dispatch.h,sha256=U7WG2d3WTPdD_N8R0MTlRKtxzRbvGDzB8sL21JqHo78,993 +torch/include/ATen/ops/native_dropout_backward_cpu_dispatch.h,sha256=1bORFhHM3PR9-gzAYkolIhplknpTrNFVzax0CauxA6k,783 +torch/include/ATen/ops/native_dropout_backward_cuda_dispatch.h,sha256=5rlBnKFiOAEU8mGr4Rw3f0Ao9Ort-GtWlnjpj0O06W8,785 +torch/include/ATen/ops/native_dropout_backward_native.h,sha256=IjRvSUGzIjSNNaAhRq4XvVQpjsXRay0ZVtfRWRd1zak,802 +torch/include/ATen/ops/native_dropout_backward_ops.h,sha256=f1Ufw5vtcMBpfdEMfGf-rxkOpevhYsvmkm926fZxrwk,1955 +torch/include/ATen/ops/native_dropout_compositeexplicitautograd_dispatch.h,sha256=r8mgD659kCKU0CtpPCHT4WZroimdndXrIqtVMyZOkbs,1057 +torch/include/ATen/ops/native_dropout_cpu_dispatch.h,sha256=J1t24v-8w8dvEu4pV9iOB-4RoPCySqWbFyqzfD5ilKk,793 +torch/include/ATen/ops/native_dropout_cuda_dispatch.h,sha256=DpRPEEbKAofktJkwzN2J2QCiD5Af1InDfYeCScpgj3Y,795 +torch/include/ATen/ops/native_dropout_native.h,sha256=OfAo8pMMTQaT7ts0wrpefiunlzJ7fk-iQt2myXw3gLI,992 +torch/include/ATen/ops/native_dropout_ops.h,sha256=NquyHcO5tad-5Az3u-M8WjLx_U4x28ozt3MrdzbcTUc,2115 +torch/include/ATen/ops/native_group_norm.h,sha256=_xYSLRpijwyrY_ejaWl6qbWSSGbuXMgquZ42iujwcyk,7212 +torch/include/ATen/ops/native_group_norm_backward.h,sha256=oRQ_EQT5l9OQB-YacMUswxwcSMkA9grOLZVT8HLGE4c,8721 +torch/include/ATen/ops/native_group_norm_backward_compositeexplicitautograd_dispatch.h,sha256=nqzu8K6VcV76s_yBsCDsu71nDUCwD_vWV84GxDsHRgU,2290 +torch/include/ATen/ops/native_group_norm_backward_cpu_dispatch.h,sha256=9mxBLoL-qZ9rUwTbq3AlJGm_xWpLkaZWn7OiopFhhAY,1321 +torch/include/ATen/ops/native_group_norm_backward_cuda_dispatch.h,sha256=MMBHbLHY8UcUkIC4nz9URzC7xWnyOa7fl04eGWDDig8,1323 +torch/include/ATen/ops/native_group_norm_backward_native.h,sha256=WUbuD65QkmhyiEsp35PZnn-V1pi9JcIQObLeEjcAEoU,1144 +torch/include/ATen/ops/native_group_norm_backward_ops.h,sha256=MV6Kdp8F6m7z5O4ocsnMDFvWzANhrei21PeHU6bcXOw,3542 +torch/include/ATen/ops/native_group_norm_compositeexplicitautograd_dispatch.h,sha256=aZYvlIJKnIvFvQvG3Rys29fPQy4Raa5wUEycyHrL0VU,2537 +torch/include/ATen/ops/native_group_norm_cpu_dispatch.h,sha256=bjPQD0fL41trWIbDUhuWtUvtfKZC64RGh_K-w9YJyS4,1185 +torch/include/ATen/ops/native_group_norm_cuda_dispatch.h,sha256=A5FwbDVUfMmvw2DA6bbFJoW3r6zeIB_DAxm8rvRm_Os,1187 +torch/include/ATen/ops/native_group_norm_native.h,sha256=HT_P5twrfM1iy4s9bBEDV9t-Qpjcqxz5uyw0GXKZdrw,1256 +torch/include/ATen/ops/native_group_norm_ops.h,sha256=xf3Knmq-W-xhRDTiAXmqVxKKM9z9JBjhrqzvblM8WBo,3100 +torch/include/ATen/ops/native_layer_norm.h,sha256=XshiBECah3r2GOLrjtmdcVgSfFhVYkx5dG7d7NNAXcA,7032 +torch/include/ATen/ops/native_layer_norm_backward.h,sha256=ewzReFxhCciqmzyJFntSphZxfIkWZmZseJOAfLUBdtM,9201 +torch/include/ATen/ops/native_layer_norm_backward_compositeexplicitautograd_dispatch.h,sha256=I2r5ZdyaG2Zi7rvNOMUtCmcwFJVMnVXlXMovTKoJ0L4,2378 +torch/include/ATen/ops/native_layer_norm_backward_cpu_dispatch.h,sha256=_QfSUxGGVDe7zSG4gORp85X4oJxGRqeVLqAPGYRIF-U,1365 +torch/include/ATen/ops/native_layer_norm_backward_cuda_dispatch.h,sha256=Cd4QeMXRt6_XNiO1AKvY7dPyUYxkUUOoK_RR06iDn5w,1367 +torch/include/ATen/ops/native_layer_norm_backward_native.h,sha256=3HzipvWLKA56Po-vqO7wgc_xCQT-IX9uDJGlCc5Easw,1871 +torch/include/ATen/ops/native_layer_norm_backward_ops.h,sha256=0QhR4jrpqOgNbiXiuFWKLqh8kuT1NW66SWNPZve6SeI,3630 +torch/include/ATen/ops/native_layer_norm_compositeexplicitautograd_dispatch.h,sha256=QZRBGWVDv2rf9DFooL3xqMEjiXPZLiiCbZtStWVptH0,2417 +torch/include/ATen/ops/native_layer_norm_cpu_dispatch.h,sha256=Zf-n_svH1weG8aJJNATHHEZBN5FRkjUvVil2-E7uOuE,1145 +torch/include/ATen/ops/native_layer_norm_cuda_dispatch.h,sha256=dr0btFvVGwjv2f5XIcegaZqeuFCxpkjVhbNl-oLr36c,1147 +torch/include/ATen/ops/native_layer_norm_native.h,sha256=U1RhBD4CcRW66KepP9DWEmBYJJZl_jlidydxbr-vAVE,1670 +torch/include/ATen/ops/native_layer_norm_ops.h,sha256=qKa1Aregr5JY4Y-FH_OZVAlhvX5GgrKvNkHV-EXPqSI,2918 +torch/include/ATen/ops/native_norm.h,sha256=bZvhD5D3MATZbYL5MSwnUP6B7AjVRgQHrtISp4-foYI,2461 +torch/include/ATen/ops/native_norm_compositeexplicitautograd_dispatch.h,sha256=aqZLrU5I0-wzOTHeZLfIFlwUZoZj-HtqnJakjZduDxU,1316 +torch/include/ATen/ops/native_norm_native.h,sha256=KulsAvvLka9lhEx0jMfTq73EKbC8_fBtSqPhla5hSjk,998 +torch/include/ATen/ops/native_norm_ops.h,sha256=hScsg8EmF6SAVrXg8gledj8rCceKw6OhdNryuPq94z0,3718 +torch/include/ATen/ops/ne.h,sha256=UE2nedLJD3ZGL7qaNfL4WRr2_f9qdSu4IS7__ZEw6AQ,1842 +torch/include/ATen/ops/ne_compositeexplicitautogradnonfunctional_dispatch.h,sha256=0gw8iMctAjcy-Z2Hpb8eo75Imq53Z2GsmVOZPr-MxMw,1034 +torch/include/ATen/ops/ne_cpu_dispatch.h,sha256=wEEAkM1FWpUvaz-2utdvzOAnm3PnxBdiDy-w2om4mcM,1366 +torch/include/ATen/ops/ne_cuda_dispatch.h,sha256=BuZttwvEAMY1AsP0EBhQkXrIAVYH6qg3P8mZlygb_54,1368 +torch/include/ATen/ops/ne_meta.h,sha256=2LwEklF5D5WfK1Iwp7tjHk8CR6BhW7-izEZjKgvrXVQ,735 +torch/include/ATen/ops/ne_meta_dispatch.h,sha256=vSzkq-l9bjmE5jw-u4khf8OCCah1cgqc_kbXKer1jgk,1368 +torch/include/ATen/ops/ne_native.h,sha256=Y0qvS-fK55WtXMGZ3_LwcyKZJ267KUTTy3yACY1nRVw,1205 +torch/include/ATen/ops/ne_ops.h,sha256=0kCRmEB0GQK3LNJhOxxwDP9x_TNazSlPqxS_EFW58AE,4201 +torch/include/ATen/ops/neg.h,sha256=PQ866p__n30HUM3A99PrkC19UojISSUbgih_SECSbSQ,1130 +torch/include/ATen/ops/neg_compositeexplicitautogradnonfunctional_dispatch.h,sha256=8fv7s5dGUs5sM86rIZTZGxgn7oHZU349Mjo2GeU0eTU,835 +torch/include/ATen/ops/neg_cpu_dispatch.h,sha256=7euY_fTbQvFB3hm2rbv6Kn2OVEqlrHIa3QRYsPJiydI,916 +torch/include/ATen/ops/neg_cuda_dispatch.h,sha256=-KZliKkCLs-5szk1vYJ-idRfUsS7Wlku3MNcCka20ss,918 +torch/include/ATen/ops/neg_meta.h,sha256=z8GRgDpTXtpyWuTd3hEhPHTjZ23jTsH1TZf2ivpsoB0,564 +torch/include/ATen/ops/neg_meta_dispatch.h,sha256=VgpbxF9crPHGqcTksaTWQ2LO67i9nsHusQRU9HglkvI,918 +torch/include/ATen/ops/neg_native.h,sha256=FtW7ddS6vea8INZOEekFprjgo_pY5mrb6mswJUmA8zw,1114 +torch/include/ATen/ops/neg_ops.h,sha256=gjRVVOMBsGzVDbglEoVX6CEKT1uqjG-tbbI1j8Y9A_0,2019 +torch/include/ATen/ops/negative.h,sha256=DPuEMRGVpuMU78uDYu2tIOQe8dMNtlQqcpbiLDnfPxI,1195 +torch/include/ATen/ops/negative_compositeimplicitautograd_dispatch.h,sha256=op-PvxB7IRQOY89z8hgzpEvBJt3HwmIwPOd9bEEtroI,980 +torch/include/ATen/ops/negative_native.h,sha256=PcjvbokvaMLSsrS526TW7-18Fpv1WDWQEGkYlFaV2TM,611 +torch/include/ATen/ops/negative_ops.h,sha256=ojLqavbIBov4JgxDVgKO3_eFbuP2zbJZWebpBlZjIfw,2064 +torch/include/ATen/ops/nested_to_padded_tensor.h,sha256=KIY3Uc-eDeCgyS5frofXjeUXafn-aXZ5qopGhYsRpUQ,835 +torch/include/ATen/ops/nested_to_padded_tensor_compositeimplicitautograd_dispatch.h,sha256=6moNWBp6TyA5XkQGyx3md2g4RYuNi7qrh7Y-Oa_Oero,849 +torch/include/ATen/ops/nested_to_padded_tensor_native.h,sha256=5DHZtN-kteCW6v_6A420nuP5eHxYt69a1m1Vt0JOQ1U,561 +torch/include/ATen/ops/nested_to_padded_tensor_ops.h,sha256=_wJTDSVYJuDr7IQpGSLk0z4oiuDGcw-wVn_UnVi8s7k,1187 +torch/include/ATen/ops/new_empty.h,sha256=INZmuUNZREVCJ9qWVrW70hZxFlMKg_9HkhbmaKyvK-8,4335 +torch/include/ATen/ops/new_empty_compositeexplicitautograd_dispatch.h,sha256=TADXCha39FmaxQMON8wzlGAhCbhEJYak0QB6Sf5ehoc,1834 +torch/include/ATen/ops/new_empty_native.h,sha256=IC9s5PtTO8dvTjNPxBfGI4hgBfIb6-Ondk1Tdt-X-ZU,783 +torch/include/ATen/ops/new_empty_ops.h,sha256=fDPqUjiQUdOp3yeCZWB8cXnxzTfd6Q9D5yBSpSm6iPg,2237 +torch/include/ATen/ops/new_empty_strided.h,sha256=m2LGCqxNMAC9B9vSwD3a2nxdwPmFdDK1i5IwPVaJirI,5199 +torch/include/ATen/ops/new_empty_strided_compositeexplicitautograd_dispatch.h,sha256=7gyzd5EWa-1GeI9YpgmpoldiMEepkEIHYDrNHVjJ-f4,1282 +torch/include/ATen/ops/new_empty_strided_compositeexplicitautogradnonfunctional_dispatch.h,sha256=i9MjY0gZlrR4Jylt_pRMZ1bz-f5iXPRqMgyDbXaa2Jc,1560 +torch/include/ATen/ops/new_empty_strided_native.h,sha256=UeofYJipbxTy8iQruskvoNkm4_EwyrLmf9YuR4jMznU,855 +torch/include/ATen/ops/new_empty_strided_ops.h,sha256=5Sc5VwdE-bfIHH4Djxiy7SiUxBXaIVLCDfvZqOS8Moc,2473 +torch/include/ATen/ops/new_full.h,sha256=oGJuke-miqLWOTUopLB2WN8A9Qxv-cZ4gjEmuLUxsfU,4898 +torch/include/ATen/ops/new_full_compositeexplicitautograd_dispatch.h,sha256=nSzir5AGScE9p-_o4THWkEJdi3vlyaf23rLaDwjI3XE,2074 +torch/include/ATen/ops/new_full_native.h,sha256=oncOcqst0nVhMwIB7OdL_oz5pxBTjLEV31YSTAMfAvE,832 +torch/include/ATen/ops/new_full_ops.h,sha256=drHnJZNGPERaakiLlhDv72nvp49ahb9YwolMSi1N72o,2433 +torch/include/ATen/ops/new_ones.h,sha256=SUJ8PVXodHr1ebaUp1oydD_JJjSR-hhDWgBQD8dY4Fg,4306 +torch/include/ATen/ops/new_ones_compositeexplicitautograd_dispatch.h,sha256=Nf-FJ3K6dyK4crObgMCA1BcWNRFlMy1PoRLC4wyNew8,1826 +torch/include/ATen/ops/new_ones_native.h,sha256=T1p7R2cg_kT6AkRYZ6v6LerHDqdy2udKgQz590xPm9A,770 +torch/include/ATen/ops/new_ones_ops.h,sha256=c6L3br0-i3L7TxXndG7XBOzjH2J152Qeym02ENH1i9s,2231 +torch/include/ATen/ops/new_zeros.h,sha256=oz45Y4lQmKT49wSyLatI0msuIAqZTa0X81ADjMo2yio,4335 +torch/include/ATen/ops/new_zeros_compositeexplicitautograd_dispatch.h,sha256=UmRw1md59ZR00A3Tna8yD5EKFS7WsyJmOM8VrRbqtng,1834 +torch/include/ATen/ops/new_zeros_native.h,sha256=HT3nTqcJjfbUtEtPTfJq4OgS6njBdiHk5bqMyyOAXlE,772 +torch/include/ATen/ops/new_zeros_ops.h,sha256=KXtcfOFS0BeQUjMfjpTljcBcj5Bq2ZQQbAzdhSI4AR4,2237 +torch/include/ATen/ops/nextafter.h,sha256=8yIFLgbthVOHxdABCSyHPll6OCr2luID6Oh6P5b5USo,1198 +torch/include/ATen/ops/nextafter_compositeexplicitautogradnonfunctional_dispatch.h,sha256=vYK-Z3NDwBdNmqQX2vSsLBYvoEUs2XpRPveolkEZ9zA,899 +torch/include/ATen/ops/nextafter_cpu_dispatch.h,sha256=LYp7ltIrRJEYMPKE4dN3jbWCdjlbTM_aPbAnUJLpNkY,1044 +torch/include/ATen/ops/nextafter_cuda_dispatch.h,sha256=Lxj1Z4o1WtYT5iVDG6-b24VrV6WA51-NZ3BKSPLakhY,1046 +torch/include/ATen/ops/nextafter_meta.h,sha256=sWoYuMVwU5Kn8ZXTGplc4KYVeqHdJ8MWXAEhfmvG3u0,596 +torch/include/ATen/ops/nextafter_meta_dispatch.h,sha256=F9PSZdrbwfFI0LlwM3o8PVIPd9HDsZ136TQ8SOSQe14,1046 +torch/include/ATen/ops/nextafter_native.h,sha256=g-3q8XGPjaaLA3cM40SGI9gFjqwpZgbD0ILU2Btva2I,631 +torch/include/ATen/ops/nextafter_ops.h,sha256=4hFHUEey2g3clCeI1XLvKP98Lhyu1XF1MGrDJgkUebE,2331 +torch/include/ATen/ops/nll_loss.h,sha256=phUwKuspVNv8NJcmEU1d4zgINbwt25F1gsT19sLKzds,5523 +torch/include/ATen/ops/nll_loss2d.h,sha256=lQ3tpND3Me5t-DA7sgDywG6R_7jJPSfEgZD0JOichnY,5585 +torch/include/ATen/ops/nll_loss2d_backward.h,sha256=vO0k-oRmTAEzHXrGrE3Gm6FGp7Ci2zwyx3oWnb03nd4,7124 +torch/include/ATen/ops/nll_loss2d_backward_cpu_dispatch.h,sha256=9LDEQzQDY6AamKXGEBefTvbCKRVer-juBOiZ3a0pmpM,2289 +torch/include/ATen/ops/nll_loss2d_backward_cuda_dispatch.h,sha256=PAWKMeZzxOCPkLv3PrR5L4EO0O0spcEAuNs1Ij_WPRk,2291 +torch/include/ATen/ops/nll_loss2d_backward_native.h,sha256=BSEtRSsF1odTBo-41v5XuX_Fg9mWVHsPMGbkSKLsPbs,1478 +torch/include/ATen/ops/nll_loss2d_backward_ops.h,sha256=hJBLehWGXlD90f8irpfQh6-k5KhBGgjzZ3PsnRrgQC0,2845 +torch/include/ATen/ops/nll_loss2d_compositeimplicitautograd_dispatch.h,sha256=RCbvXOotnKwjCx-_C-9iwXPQjHeY6VaOewnAE294Ymk,1973 +torch/include/ATen/ops/nll_loss2d_forward.h,sha256=ZdzV0p76MJkp6qe_7H6VHgITXOqv6Bbd3fR8yKJACDA,6475 +torch/include/ATen/ops/nll_loss2d_forward_cpu_dispatch.h,sha256=8y-NCw8Nj7lGlBC8bIghB1tJdCn3Z8YQOsbJKPgIn2g,2143 +torch/include/ATen/ops/nll_loss2d_forward_cuda_dispatch.h,sha256=3hJzjJLba2Z0l6H6Rpdc6FRFgm7h-u79LEFT21zAd_w,2145 +torch/include/ATen/ops/nll_loss2d_forward_native.h,sha256=OdiRnX5QoxtXG6MrmsHVxCKWKljrOuqaxVQxdDkpq_Y,1364 +torch/include/ATen/ops/nll_loss2d_forward_ops.h,sha256=4nVH6pZnPwToFM2zSKMEddOweUDVaqC7yft6a8hRmcI,2686 +torch/include/ATen/ops/nll_loss2d_native.h,sha256=CDp2PqKQqyrsfI4W_DlkrpmX7UE1yk8MkaVe0NUvr3o,825 +torch/include/ATen/ops/nll_loss2d_ops.h,sha256=yPQsVkxOUkB0JK3OiE9AFVi2l99-xmcRLG_sobBIAfg,2357 +torch/include/ATen/ops/nll_loss_backward.h,sha256=u5Gwhdb7a-GlzBHt0l6DJSQ2d3PVD9gnUYtVgoVtbfE,7062 +torch/include/ATen/ops/nll_loss_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=3-UjUtwNaOFPvWwVnL_qJCuKkytye8ZJq4QRUkJehLg,1231 +torch/include/ATen/ops/nll_loss_backward_cpu_dispatch.h,sha256=YKWWLlIZjVl2YTu7HnZuAp3_Mzcylt1Pj3JKoRMDqJQ,2277 +torch/include/ATen/ops/nll_loss_backward_cuda_dispatch.h,sha256=7Kvl0VW9V0Kb_W9wyn4vxmp58YniqL0LtpXpNL_TI_w,2279 +torch/include/ATen/ops/nll_loss_backward_meta.h,sha256=aAHjKwcN_45ZzzIaL7pacArL7bT7Bf686jIi1t5DZck,741 +torch/include/ATen/ops/nll_loss_backward_meta_dispatch.h,sha256=Qm3b0sOpxzjMDl1Z2SvujXQVSod8ZRgkCOXVUDamRhA,2279 +torch/include/ATen/ops/nll_loss_backward_native.h,sha256=4q1FfnrLyTWhvVaNFmzUazuvMlIwNtu6cv8iJbkUmJs,1141 +torch/include/ATen/ops/nll_loss_backward_ops.h,sha256=hbTe51q1LsiamocnF1iEm7-shoMkOxGRUrLCSK7in4U,2833 +torch/include/ATen/ops/nll_loss_compositeimplicitautograd_dispatch.h,sha256=EKOAtEtvIyVhnhQFGhBf5ubz0XOlXj7ZPPgXX9rffPY,1961 +torch/include/ATen/ops/nll_loss_forward.h,sha256=rMktJ5mwQ8h4rkHDIhvJIb_PvafBCWs_t2Dr47GoRHM,6413 +torch/include/ATen/ops/nll_loss_forward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=3gJVYghyXXevttYbI1NNT8FDP3H5f_UAQ0Q1EM7K7n8,1149 +torch/include/ATen/ops/nll_loss_forward_cpu_dispatch.h,sha256=g83Uy2yRipG4mnC9fyE2MWONxm_mSE4hyWr_VAfcWNw,2131 +torch/include/ATen/ops/nll_loss_forward_cuda_dispatch.h,sha256=ZM8NseOZKXl2fTPBUc68AVPwb9-NWCJCWZ9BCSSL7TA,2133 +torch/include/ATen/ops/nll_loss_forward_meta.h,sha256=RuR2x-pVg31e8wtIUwD5bBtxMYzHXCAwkzKk61V990U,675 +torch/include/ATen/ops/nll_loss_forward_meta_dispatch.h,sha256=YzZut38BTN48wxdPMto5b7f4V0bgtHI5RLHiX3b_t7U,2133 +torch/include/ATen/ops/nll_loss_forward_native.h,sha256=C1J55wTZqaRq9cvCe5T60EVysZaJ1yklBz4ctdthQcs,1064 +torch/include/ATen/ops/nll_loss_forward_ops.h,sha256=ofhtD69oqMKfUJgCjfAnciVLrn810HmtNbqEUTvZEnc,2674 +torch/include/ATen/ops/nll_loss_native.h,sha256=8aN5Zkf8oM1uzb4rO1x3DlvxhVi14hYhYLF3IuqqGsk,821 +torch/include/ATen/ops/nll_loss_nd.h,sha256=GCLRxAI_3PwmDmOlo2YXhyYVkhMnxaY0HsdagFNdRZ8,2122 +torch/include/ATen/ops/nll_loss_nd_compositeimplicitautograd_dispatch.h,sha256=9q2xay-GOuc-ZkP9iE0cYeSvO8BuzH7-FNqLwezJPM0,1119 +torch/include/ATen/ops/nll_loss_nd_native.h,sha256=TNJb9QS_C4fF5Os7j3BaxRLkayG1mrcOnZtZ-awXw8I,632 +torch/include/ATen/ops/nll_loss_nd_ops.h,sha256=xgJi5aTsvLiSrzvS5vy0fgrzY-UGxmz3N6udWL9qzfQ,1365 +torch/include/ATen/ops/nll_loss_ops.h,sha256=U0IjnTY0yg2BpBUb7R_ib4DQpzlZs3JxiwjJC0PHd3s,2345 +torch/include/ATen/ops/nonzero.h,sha256=DR6W5whMHC_vt-U-4U2TzLKWKr59lrKlYygkYfXArUs,1037 +torch/include/ATen/ops/nonzero_cpu_dispatch.h,sha256=JVA9iWUJegnvjJsq2QZC7esCIvmq_8cnSqIKzxowGpE,880 +torch/include/ATen/ops/nonzero_cuda_dispatch.h,sha256=NS-AvF2Ad8-8_7bshKBnpna0l3ear6Ifh1dVV-94w6g,882 +torch/include/ATen/ops/nonzero_native.h,sha256=bTCtCEO1q48jRevJYdoZYei71HFINFqDakpO0WPJXPc,708 +torch/include/ATen/ops/nonzero_numpy.h,sha256=FalhBnYo4PnDlLRbdD8aXmm_yhNqu2uVqpXCVPvy2zc,682 +torch/include/ATen/ops/nonzero_numpy_compositeimplicitautograd_dispatch.h,sha256=2M9_IcFfBHE-FLPOGonmDs8s1PaaLDKOkB63BCamvi0,786 +torch/include/ATen/ops/nonzero_numpy_native.h,sha256=IerM7tROFRtXVuGAJrTGkD-M2qabwIXMaM1U7N1JxZM,498 +torch/include/ATen/ops/nonzero_numpy_ops.h,sha256=a8gSgOMfqvzWkcR-LLOcouPxHLWf2O0JD8bMcIpFaqU,1025 +torch/include/ATen/ops/nonzero_ops.h,sha256=w9Ri2dcRl8CTFzvSGPMVDkyczWBxxWymZZ3XPjeKxW0,1553 +torch/include/ATen/ops/nonzero_static.h,sha256=hoBhGvO7oGoOYeosJ4tnbWsseo64AXxm9DdsLQ33jfA,4003 +torch/include/ATen/ops/nonzero_static_cpu_dispatch.h,sha256=botX2kpsN8vQXd6nmJK9ZHQ6o3nIFUex4rHH_5J2Xzs,1385 +torch/include/ATen/ops/nonzero_static_cuda_dispatch.h,sha256=1zkUVeUbLjHJUL8djQIyD8RjJXCBlu6XrGyF1hlowRs,1387 +torch/include/ATen/ops/nonzero_static_native.h,sha256=5OhtT9zJboZ38XoJHFkaONu7O9FBtLT06bniHd5eUy8,878 +torch/include/ATen/ops/nonzero_static_ops.h,sha256=_ErdAYa0fnaZ1Y7KVW9OzM4XoHsrMDXPABDTQ83VQYA,1858 +torch/include/ATen/ops/norm.h,sha256=y6lXiA1fEnMaXV83jToeIIJKCLSuUG1TDSCeJl31Hm4,6262 +torch/include/ATen/ops/norm_compositeexplicitautograd_dispatch.h,sha256=J9QpOhV-MMTqtQ3bYpErP47YI54zTN6uetqYRDD1gCo,1373 +torch/include/ATen/ops/norm_compositeexplicitautogradnonfunctional_dispatch.h,sha256=axfrB--xzxUXGM2lXl4Nc1wVygGRUu7qV9yyC7de-80,1016 +torch/include/ATen/ops/norm_compositeimplicitautograd_dispatch.h,sha256=s_Npi2OBzIVu2FQh8HfRwv0WXjDo6SQ9uEhw2a7tk6w,1642 +torch/include/ATen/ops/norm_cpu_dispatch.h,sha256=oRHvQ2Z0wbeZAUVMs_mSiG_pSvLfTq3EVVaiE4jnd1w,1598 +torch/include/ATen/ops/norm_cuda_dispatch.h,sha256=-0QH5R8yDUDW6MQAywO2ZtxblPoWQ5cRtAazHPmQgQw,1600 +torch/include/ATen/ops/norm_except_dim.h,sha256=npK1VNOk_pBRehgi0lzsUcB3KqvdeEmE8p4jNlh9-Zk,726 +torch/include/ATen/ops/norm_except_dim_compositeimplicitautograd_dispatch.h,sha256=WFyBku0XyXj6g72Hb-vx6c3ss713V9R94V41HMQH3yw,800 +torch/include/ATen/ops/norm_except_dim_native.h,sha256=5ELhnSatI2hOFW-dwW8eEBwwmkkv3rhn4JaGt-rZA1g,512 +torch/include/ATen/ops/norm_except_dim_ops.h,sha256=aM-yEMfMSlZtfKhm2pt2Vyaq8NeEPJHpG450vTyl73A,1067 +torch/include/ATen/ops/norm_meta.h,sha256=D7P-RanqOZxmg1lfO0_5It_4KIUwhoHV5axzy07JXm0,849 +torch/include/ATen/ops/norm_meta_dispatch.h,sha256=tPerIpHfPUgAePF29glvJpS7FW7DBb3RAx_M48sqfiQ,1600 +torch/include/ATen/ops/norm_native.h,sha256=27UTuH50xUe7tbLaNvG46aNMuexGmLMaRL4QuDWVsRY,2258 +torch/include/ATen/ops/norm_ops.h,sha256=QcJvYA5jXzQl1HR8JsCrILlR68LLB_jcbdIcuLPWjxc,10250 +torch/include/ATen/ops/normal.h,sha256=YQHEs4og7yVqiYZMHbZvmiSszRnvVFgNIGbaGRt0yQM,11661 +torch/include/ATen/ops/normal_compositeexplicitautograd_dispatch.h,sha256=6tdhh8ylOxipXPELHt3va33ArCWClxNDnx0LiyfuGeA,2667 +torch/include/ATen/ops/normal_cpu_dispatch.h,sha256=fuuqby3n7A6tlry4f_rbmHhiOVkrCz5JDvr4Mi_PaVE,2052 +torch/include/ATen/ops/normal_cuda_dispatch.h,sha256=KuAPqktLKuGNbecRsplUJKjb_7lk43ZIG7Ek0b8K4NQ,2054 +torch/include/ATen/ops/normal_meta_dispatch.h,sha256=v2wWumBdYl_cwWdT_Nb2-vHExk74YiSN_N_Tzd7alM4,2054 +torch/include/ATen/ops/normal_native.h,sha256=h8WuYhtAeT3Lwwt5_1a0n26CRDD5kM877CWYaQIYc08,3333 +torch/include/ATen/ops/normal_ops.h,sha256=00DTeYDKEU0JEE3sPUfdszznOaj81gYN-87GAUqIgms,9634 +torch/include/ATen/ops/not_equal.h,sha256=9emjiEeO1fbSu2Kbu4bxrXYNUhuU111Tjl9DGpGo_Yg,1975 +torch/include/ATen/ops/not_equal_compositeimplicitautograd_dispatch.h,sha256=Hv7pe_IWW23uaET-h9de1yltqNvplM1rgp8QFiBY7Mk,1466 +torch/include/ATen/ops/not_equal_native.h,sha256=NLcBfqMHl_EOZJyrJq03leJFLAG9j01Xx8fFVVsFToc,962 +torch/include/ATen/ops/not_equal_ops.h,sha256=f9_95IUBPKYoIhVi_miouXFmZhvbvJ2lO8vhv3QfyiY,4327 +torch/include/ATen/ops/nuclear_norm.h,sha256=5DgwBSV0XClmdigQoslyA5VOzF_u8BXQbtplzKkXf3Y,2086 +torch/include/ATen/ops/nuclear_norm_compositeimplicitautograd_dispatch.h,sha256=vhRWRYRPERaidyNV-hbowCBZ2HlABPaku9map-l-4Bw,1339 +torch/include/ATen/ops/nuclear_norm_native.h,sha256=GT6ymZ46nK4JSuOJPfbY01tD-k2V1ajInPm_LH62rbA,820 +torch/include/ATen/ops/nuclear_norm_ops.h,sha256=WS6nJyvtWO7JbnlKaNcFueIwMyBH6PCkQqrX_w8_uHI,3090 +torch/include/ATen/ops/numpy_T.h,sha256=kiAY6Djd1ITWNlhDt4lgLA7w3VlT-4H0p4RYbTkn5XE,504 +torch/include/ATen/ops/numpy_T_compositeimplicitautograd_dispatch.h,sha256=OCNdHot_8eP2h9wLwO048woz7aS8pqQo8mcIujot0Ac,765 +torch/include/ATen/ops/numpy_T_native.h,sha256=d7ueS4hLvlkLxtlVJCYUIA4GxLaPH7HS10ZWKj5wp-Y,477 +torch/include/ATen/ops/numpy_T_ops.h,sha256=2eHUkAyG4pRIvTjxw3-j6RrVCVtoFiA9DNNxPRnpoVk,966 +torch/include/ATen/ops/one_hot.h,sha256=orZxSjqkWg03dEf92OMbBgbfgyFiYOU4e4bvc21q4mo,698 +torch/include/ATen/ops/one_hot_compositeimplicitautograd_dispatch.h,sha256=tVy4fq7qb7U4eeTz-cPl--TBb0Ee4jVUOWQsKswy5dU,789 +torch/include/ATen/ops/one_hot_native.h,sha256=72PXxcr9ENI-1KvJH0C6g5IvttRWEZigPsu-lLKP5P0,501 +torch/include/ATen/ops/one_hot_ops.h,sha256=Z8gsKED3bgLae8smkvu-g-NZrPiYjL5JLzgRxpGGyUg,1031 +torch/include/ATen/ops/ones.h,sha256=9--l7AyvAUdztKrgo_S-pWzYMNxzmecMxtkWHiqQszg,6829 +torch/include/ATen/ops/ones_compositeexplicitautograd_dispatch.h,sha256=AiETvdSXcoJBdtMd8A1xccPq6gZTUQW4_VpaPKlNWr0,2174 +torch/include/ATen/ops/ones_like.h,sha256=NuLy8y2K8iWdEQDEaobIIOWt5WBa-flSVjubYBXZVTs,2208 +torch/include/ATen/ops/ones_like_compositeexplicitautograd_dispatch.h,sha256=XR0hn113hRkgXqfdxjJiLMqfOufaOAnhxrs80iGHDjE,1388 +torch/include/ATen/ops/ones_like_native.h,sha256=4kfI2eTA6DJGe2df_wOSgWVvZCV8f7YXYXtK2c6fCwM,830 +torch/include/ATen/ops/ones_like_ops.h,sha256=77oCLzO6WpmRan27b7qOx6nNxFxHyf6tWb8SLv8sb_E,2395 +torch/include/ATen/ops/ones_native.h,sha256=G2fPkYzOO11iFnDWhHj8IJRJPUaRRJvEnhGFqAjO5Fk,1066 +torch/include/ATen/ops/ones_ops.h,sha256=QO_eBgdmiExpeZ5RX7SnShHA9AmPtnWsKUdY75Hu2cA,3894 +torch/include/ATen/ops/or.h,sha256=wxKjkA0MaILvHjlP92EwdQspqvbtI3YZUiknNujDjW8,890 +torch/include/ATen/ops/or_compositeimplicitautograd_dispatch.h,sha256=ig4iyemDkwA6VDc2NPJrsRjHRExSL6pSF370YKeHDCA,1024 +torch/include/ATen/ops/or_native.h,sha256=lmRG4Rysu3sdX-WvEgyONBv6zqmEx2nnouz-3RILtGc,736 +torch/include/ATen/ops/or_ops.h,sha256=IsmKq41nfk-VcDzTFJ43m9LMOlxOBxADGnz_TqISsT4,2879 +torch/include/ATen/ops/orgqr.h,sha256=RuC-HFDuiUoQNBEilEip2HohRSMy4K3780becbU1QFk,1167 +torch/include/ATen/ops/orgqr_compositeimplicitautograd_dispatch.h,sha256=oP1VzvT-WgUOZ9C1M2SqhSV68rJ5eMxLAjL7WEq5cBc,999 +torch/include/ATen/ops/orgqr_native.h,sha256=ZCj6T0sHQgr13wYU8aGYSK4Wlwim3PQVZ0SMGFOTN6Y,606 +torch/include/ATen/ops/orgqr_ops.h,sha256=WwzFXb9a2YdXHVIMmFOP6IXBXCN_niENpjDRKDcHIsc,1719 +torch/include/ATen/ops/ormqr.h,sha256=S9SameQftdUVlh128zp75bWw4Tluzbz7HZPI7aL-1SA,1585 +torch/include/ATen/ops/ormqr_cpu_dispatch.h,sha256=g--JcO7fArdX43KXgL9q76WVWPA_8MrPRRI-GYIAP48,1139 +torch/include/ATen/ops/ormqr_cuda_dispatch.h,sha256=lUHWwaDpsKUzgE4sTV9UaHhfOyCmDkr6GYea2rKQKjk,1141 +torch/include/ATen/ops/ormqr_native.h,sha256=_ZTtgfmrxBmjNb-kDcUDJ0LzXPFQsIQWCbbL5M0nIzQ,725 +torch/include/ATen/ops/ormqr_ops.h,sha256=zp6hkPJqvajt8BoFZWi-zaTycEMbfTKc13nJXTqKTlk,2105 +torch/include/ATen/ops/outer.h,sha256=ZUl885jI0ou9Hahlm-9AjZb0xirrqGZOzadh23JQTbM,1149 +torch/include/ATen/ops/outer_compositeimplicitautograd_dispatch.h,sha256=noHy4UXi4eieN5N9eRpcIwH86Dca3XXx2GXEZHoDD4E,993 +torch/include/ATen/ops/outer_native.h,sha256=tXJb-rMDDaIRb-qqHmH9XpVGBir6GMXeJsmbWglAX3o,602 +torch/include/ATen/ops/outer_ops.h,sha256=oeJTIG0umc6IVs31UrRlJRrxwFRKYqteZ5aaHoy6jc0,1707 +torch/include/ATen/ops/output_nr.h,sha256=vtkmzlWF2gMg37YiJASghtyIGWnEkTq30zBZPJF53m4,506 +torch/include/ATen/ops/output_nr_compositeimplicitautograd_dispatch.h,sha256=ADkZzPrF0NkaDkieqleI9Ul_CZA_0q6DGRRbqC1t9jw,764 +torch/include/ATen/ops/output_nr_native.h,sha256=xastFhleH0TdjVCk5BfLBM5Ae5YtkZlE49CKicx9XOI,476 +torch/include/ATen/ops/output_nr_ops.h,sha256=pkM9V2aGaUZAFf6TtPioFfMNQxo1kluD-_QM6c-Y1eA,954 +torch/include/ATen/ops/pad.h,sha256=xjcUuZxfEbYuBYSSXMCPyY7wbWi-kFMnv3FmpDK0kjc,1782 +torch/include/ATen/ops/pad_compositeimplicitautograd_dispatch.h,sha256=28fqM75cbYejZUJ1AbIZZ0GCqPJyUiyvMrCHR285_fQ,1025 +torch/include/ATen/ops/pad_native.h,sha256=sPPVVj-oC6_QC8F47RASP5DgmYBIm5Tdel6Vfri1tuM,585 +torch/include/ATen/ops/pad_ops.h,sha256=olIWdglnxdlbliioeptF30TbPfrVf2-b4tnjB24HyjE,1226 +torch/include/ATen/ops/pad_sequence.h,sha256=wgmO2ngolBj5nBTvj4TxNAuxca6b0W3T6QbuyNjoZhA,880 +torch/include/ATen/ops/pad_sequence_compositeimplicitautograd_dispatch.h,sha256=6jXwNXB-_-lM-S2Etnt46VFWVAmCVW6UH3-8Q7oxqcQ,860 +torch/include/ATen/ops/pad_sequence_native.h,sha256=mz-ENBB2UaFUhuJV6jy1OOrT8Rc2NSoNWANjiiqi7Nc,572 +torch/include/ATen/ops/pad_sequence_ops.h,sha256=_pTHo63qq7M_kpwvaad6y9cs_2oVJITdRIdxICb1Bho,1231 +torch/include/ATen/ops/pairwise_distance.h,sha256=YR_utOKGRgMBjgGsoRxrKvJi7A3f7hh_HKVDhhxKIvg,828 +torch/include/ATen/ops/pairwise_distance_compositeimplicitautograd_dispatch.h,sha256=PJCuj024kpFizJvOWremBwg2xrEckFuZL9p547qPVOM,846 +torch/include/ATen/ops/pairwise_distance_native.h,sha256=IOOl2SrplMyErLUPOTBvS9FhQmrqv1kFp31rA8-x0Ho,558 +torch/include/ATen/ops/pairwise_distance_ops.h,sha256=HW8iEwBrlsF7nC8FVisccoIgjx2LZuZ8cBcRLG6m2tU,1203 +torch/include/ATen/ops/pdist.h,sha256=RZcNhjBvTPDdou4wiMqlWRzOFCeXMuWWRVe06qC1QzQ,659 +torch/include/ATen/ops/pdist_compositeimplicitautograd_dispatch.h,sha256=nJWNmO-kEwhIwizjFVtsJ5uwPVp5WPR68hFqDbw9aVQ,775 +torch/include/ATen/ops/pdist_native.h,sha256=_DzeeGRkXI1t_uggiYDoI42_dm1ZWvqBnCnCTUm1W_k,487 +torch/include/ATen/ops/pdist_ops.h,sha256=WA0n8b-TT8sGCJbb25Xi4W093mkWG21VVjL3H8Z9qPk,993 +torch/include/ATen/ops/permute.h,sha256=DebxvwHNsnCXZuy8NvCqeScIaM6l2-Ut2vp08o0RoQo,687 +torch/include/ATen/ops/permute_compositeexplicitautograd_dispatch.h,sha256=znIjnggAXqVPwL1-FUsICvhKAqa70EppXBIJqSNI1is,787 +torch/include/ATen/ops/permute_copy.h,sha256=4dASTtWa1VC94UqnY2t-QItxZsvRgfD8yZxD3j0Jjrk,1207 +torch/include/ATen/ops/permute_copy_compositeexplicitautograd_dispatch.h,sha256=RpiSHAZlCmZc-aCUlLDmlcAtJhiSlbkm5oX3M-EUVtM,923 +torch/include/ATen/ops/permute_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=HvgN6ExkSCEPfBW-ty9WLMppLX7vJZZgksAl-GEE7LE,818 +torch/include/ATen/ops/permute_copy_native.h,sha256=tBY1QrSN9VKHHobnal2GmL05cBeXNQehle2NI3qFOjo,610 +torch/include/ATen/ops/permute_copy_ops.h,sha256=4RasIxUDygDr8MhqlEB2B6dwNm2yENLIV__OYdm1Ojs,1729 +torch/include/ATen/ops/permute_native.h,sha256=pCJ6PNU6hVeB_yy4qP8rfvIlrtxNZS4u1bt2lhxCjUU,587 +torch/include/ATen/ops/permute_ops.h,sha256=M91AnaNXwzsVxMk9rPCl1Vum7eQ_uU4erNrHPquJwmQ,1039 +torch/include/ATen/ops/pin_memory.h,sha256=S0Hp-nzy99t34bLr5Z8HZAbQBXup9KfR08PfWiNkzQ4,507 +torch/include/ATen/ops/pin_memory_compositeimplicitautograd_dispatch.h,sha256=kocXgLW-JBPhuFlPFQNNT8l5PUfCi7qUJNdd5lfs6xA,819 +torch/include/ATen/ops/pin_memory_native.h,sha256=wjLy25AgvK1AQsd4TVGz1VaQBizwQ_T6Y7ZaPrSEiLI,531 +torch/include/ATen/ops/pin_memory_ops.h,sha256=r24_BQ4p3ITHCycSdlhzR3sm8VMxPg2dToEseIobOKE,1097 +torch/include/ATen/ops/pinverse.h,sha256=QzM8gMSt6xQTRwFZmvZfU9Xoims_2-OXymtVkj6kPHo,691 +torch/include/ATen/ops/pinverse_compositeimplicitautograd_dispatch.h,sha256=agiX8Sh2u1RWp5cyWjjoAhOwJRJwjt_VFFCc2dstTk0,786 +torch/include/ATen/ops/pinverse_native.h,sha256=s6rEyXFVKzy8mpfDaVuABdpDKj7y0mAdBE8xUPBL3LY,498 +torch/include/ATen/ops/pinverse_ops.h,sha256=wXZDlyZJUGiz_L3-4Ns8snrqAwUUaMoohbWaeqRMy-A,1018 +torch/include/ATen/ops/pixel_shuffle.h,sha256=IFELUdS67xce-XI5_speJddSKrQVjls_eyiPQahVq0g,1277 +torch/include/ATen/ops/pixel_shuffle_compositeexplicitautograd_dispatch.h,sha256=T6qQA5lkzWlFqcBeRqBYqqr8W2sGmp8UU8nYQ1-8md8,929 +torch/include/ATen/ops/pixel_shuffle_compositeexplicitautogradnonfunctional_dispatch.h,sha256=2KlZIKqBxNARnD7EqM9_l6NYAEsIKZKdt7XyTteDjHI,821 +torch/include/ATen/ops/pixel_shuffle_cpu_dispatch.h,sha256=VMxgDyVdVzrE6RvjqLTJn6Bd0nF8ZDySg6wnkVa1asY,751 +torch/include/ATen/ops/pixel_shuffle_native.h,sha256=vEQlBiZoQEfavsX14ob1xVYheL67zRzBxVDd3fQ22Mc,710 +torch/include/ATen/ops/pixel_shuffle_ops.h,sha256=y6vOf8JKZgmyyD5kMup2Lhv-iJ0nDhO-zSRaK2Aa2cE,1743 +torch/include/ATen/ops/pixel_unshuffle.h,sha256=g6IOfFvJRX8ryrYitXfpLNwi9MmWikezslROyXyql6c,1315 +torch/include/ATen/ops/pixel_unshuffle_compositeexplicitautograd_dispatch.h,sha256=8bTZWrKYAEj0Aq0mluWXCi4pywLtREZ9MTT3Olkkqcw,937 +torch/include/ATen/ops/pixel_unshuffle_compositeexplicitautogradnonfunctional_dispatch.h,sha256=AYolLvLlcFkkPYMheQ6wRpA6wSN6NeW81mmMIbpHIW8,825 +torch/include/ATen/ops/pixel_unshuffle_cpu_dispatch.h,sha256=DSsyO3j2gQcQEqhQbIR0gc15Psd_KpyuKtWB8F_4hqo,755 +torch/include/ATen/ops/pixel_unshuffle_native.h,sha256=8F5a7npyuRfmdTegTKlXhG7PaBceuYPFKMujr7Vk7-0,722 +torch/include/ATen/ops/pixel_unshuffle_ops.h,sha256=cFFUO-6Wvox5EH0-aBHSIcZ6wIESNbXPHaMkTQv5vk0,1767 +torch/include/ATen/ops/poisson.h,sha256=yDGOVlwF4DYdST0wPYhERcnv9YNuXHyNh_oWDHd0ahg,1307 +torch/include/ATen/ops/poisson_compositeexplicitautograd_dispatch.h,sha256=yd4mV6N70240Pjy_0LXHLv_V7ABzCRoso8g78g17hlM,968 +torch/include/ATen/ops/poisson_cpu_dispatch.h,sha256=PKoQN6hYHJjTbp5BVVqRxm1OQ_VryWPHuNubHzV4xxQ,778 +torch/include/ATen/ops/poisson_cuda_dispatch.h,sha256=l-UPjooxB7EQBj4e_xPnIJaw6i6uwJpKLKWBOqdaCYg,780 +torch/include/ATen/ops/poisson_native.h,sha256=7KU2BMiiMOFQrKT8GjdohvtKidGVT8583QiBiqDNVTI,782 +torch/include/ATen/ops/poisson_nll_loss.h,sha256=6m827xr6gHdDtNp5RKrHCSOOZ3pYLd1zy_6YP4Yi2Gc,874 +torch/include/ATen/ops/poisson_nll_loss_compositeimplicitautograd_dispatch.h,sha256=ygZ3zCKxpcJtm0WO2GkK1QBV2JMv_tnEXyBzevESOJs,860 +torch/include/ATen/ops/poisson_nll_loss_native.h,sha256=2wNpqUdIZlZF0srVT2cs9lwNDw3YpmDJe10X-sjm5b0,572 +torch/include/ATen/ops/poisson_nll_loss_ops.h,sha256=9zvl985Uv5io0grtesX_6ouN_zfpYeXD1PT3Kt_FBGw,1277 +torch/include/ATen/ops/poisson_ops.h,sha256=u2l6DHUkF0XAase3jPJg8-e6Z4XAldK62jBESSu_8Qk,1839 +torch/include/ATen/ops/polar.h,sha256=kd_92QcqgiuBXMtjmeGGo1stY7hMD5WDBoxp5MRenRI,1149 +torch/include/ATen/ops/polar_compositeexplicitautograd_dispatch.h,sha256=63Ox2FepgsSZGwAawSnrPlaH1nEQTMtBTpAqr2jp9Xs,788 +torch/include/ATen/ops/polar_cpu_dispatch.h,sha256=Hi3FYfTFa594PdJqZ_sCnyvcb2ih16bPTEk_VNjBWqk,871 +torch/include/ATen/ops/polar_cuda_dispatch.h,sha256=9OVYb0efmcOF7CGqsy_FZuyciMVwUAixJxU47bG81E0,873 +torch/include/ATen/ops/polar_native.h,sha256=stOczZN6A7m89gAXRiA8TZGNxoHdZrorQd14YJVWaJU,602 +torch/include/ATen/ops/polar_ops.h,sha256=1zWq3ZwjSQvxrcYCJHwQiwbUlNymragmpC2rfvDqH4c,1707 +torch/include/ATen/ops/polygamma.h,sha256=mwG4HHIGsNXphAmv1eQ9a1vC4a9DxvVLA8zOqyfBH8k,1120 +torch/include/ATen/ops/polygamma_compositeexplicitautograd_dispatch.h,sha256=ASsPSw-zfYpT8PQJKUnnvPOEPvfjv0MK1OzTLYIr3ME,775 +torch/include/ATen/ops/polygamma_compositeexplicitautogradnonfunctional_dispatch.h,sha256=n1z83T4C-YPI2lWZLG_piOySec1DdPPhaCu3r9_BO2g,804 +torch/include/ATen/ops/polygamma_cpu_dispatch.h,sha256=LGTvcKphVkyna4BvZbWC-kuBS_iF2-ToySj08uKWr3Q,919 +torch/include/ATen/ops/polygamma_cuda_dispatch.h,sha256=MPsys_kw-m2u9VnJhu-rU4YTkA3eYoIUMNrr29FglYY,921 +torch/include/ATen/ops/polygamma_meta.h,sha256=N1H0azotuw7UE3X5bYaM5Dgd3Aw8vSIifAR458J6RHg,581 +torch/include/ATen/ops/polygamma_meta_dispatch.h,sha256=3JVEP9XNDBNhwIH6dLGs47YbN5tkBkudjoqDInPrRxU,921 +torch/include/ATen/ops/polygamma_native.h,sha256=bZueh1wZERV_hUPYLZmAzxCs72MvUAumg8KX6aBICrQ,681 +torch/include/ATen/ops/polygamma_ops.h,sha256=AjDh-R8qorErjyJo0CuUmASkcogFQ2bI6U3rT4cy86Y,2187 +torch/include/ATen/ops/positive.h,sha256=OF0RrAvD-sDJsVM5gL_9xlliLKaHDOG3IJOk6qbw1Lw,651 +torch/include/ATen/ops/positive_compositeimplicitautograd_dispatch.h,sha256=WBfoIVm0ikbK7jTxuwyaIIp6dgjxkHQs7DaueD2gshE,766 +torch/include/ATen/ops/positive_native.h,sha256=fUTOymqNClKnfvgP_BsSPk8X79xw7yG9-UpFN2W91bo,478 +torch/include/ATen/ops/positive_ops.h,sha256=r-lpBOXfJiW9u4wW-lJNGNJYYwxQED9sL1PMyn0AMw4,969 +torch/include/ATen/ops/pow.h,sha256=M-EzR0UXMj4JI0JnuV6gCrd6yYB7x0gFsrjpuiD2c-8,2707 +torch/include/ATen/ops/pow_compositeexplicitautogradnonfunctional_dispatch.h,sha256=JPxA_SlRKjv2a00yRoSSTDfQ0ZWgOpga2IgPfZbjhB4,1130 +torch/include/ATen/ops/pow_cpu_dispatch.h,sha256=chd2P9T6nZ32HKiEwa3Q8gdqXNYMuBEs8tehRt_Dh7g,1687 +torch/include/ATen/ops/pow_cuda_dispatch.h,sha256=9wRa2C-ltM77NCSAgchGBl07oBXPnEERms3hj2Mc7Ss,1689 +torch/include/ATen/ops/pow_meta.h,sha256=k7CXouDECaPZb8xkfVNWlDCF7EJw_WFLD00flYEoL1Y,900 +torch/include/ATen/ops/pow_meta_dispatch.h,sha256=qdZqUESwksiWvJPwZ1DmvRZQuUmZbl059u5bmS16pOI,1689 +torch/include/ATen/ops/pow_native.h,sha256=EOujbAc9obUdaL2pjIMDsdXGmC94wBxRets6MOi2EwU,1226 +torch/include/ATen/ops/pow_ops.h,sha256=GdJZwLQqTFCOzxJ6vbCVN7D3pyR1MzPhVW5Wpf0qynU,5660 +torch/include/ATen/ops/prelu.h,sha256=69NfbNl37RzcYoU6j7DeiFXG8vvANMH4SeuCK62U_kY,683 +torch/include/ATen/ops/prelu_compositeimplicitautograd_dispatch.h,sha256=DHiEZxoXkNYDxzuO5hTycax7fG_Y533mk3Gos-Vrmbg,790 +torch/include/ATen/ops/prelu_native.h,sha256=-spBJr03EKvj-bumO2BqjCVMGnnCZRIbyUfBe2Pf-d0,502 +torch/include/ATen/ops/prelu_ops.h,sha256=9jhYpwdr64GADOkDO862j1MYMVFMi9dqIx0bYHeoMoA,1043 +torch/include/ATen/ops/prod.h,sha256=B-FD39cb6bbzM-YZu4t8QB8SKqKComXZBDsW0z-YXBs,3306 +torch/include/ATen/ops/prod_compositeexplicitautograd_dispatch.h,sha256=QsRfBaTCebS9R2T7sK0sZ0C_zW3p7BkSkuT6Vco8VCE,956 +torch/include/ATen/ops/prod_compositeexplicitautogradnonfunctional_dispatch.h,sha256=uYoRgtpJW7R9VgJt4_TxmSxZL4fk2RpDCUEFxqhP2WM,875 +torch/include/ATen/ops/prod_compositeimplicitautograd_dispatch.h,sha256=p8umbXYvFIhiGysA46hzR3PjKmr5qb5MeCaoJvL-3Gc,1167 +torch/include/ATen/ops/prod_cpu_dispatch.h,sha256=njF3jj27O6aY3RSxHseo2C1FqAGRlSlWD_uXn9Pwvqc,1217 +torch/include/ATen/ops/prod_cuda_dispatch.h,sha256=0WjYcz0eeuh14QM_gKt6H5h0auIO8Yx_CrERlNBhwDI,1219 +torch/include/ATen/ops/prod_meta.h,sha256=2NDCKWxmyhTwvrpH2dn5tWYDRMpfvxb1lT86-2xuwA8,639 +torch/include/ATen/ops/prod_meta_dispatch.h,sha256=ExmlyZLuCh6uc3PghSl2zeXibrm0d6ypM96T0yKFU2c,1113 +torch/include/ATen/ops/prod_native.h,sha256=nwaNe1j_5ketmrtpcVC2HdMNT5nCRJwPIEgp1Kgl9LY,1174 +torch/include/ATen/ops/prod_ops.h,sha256=LNT6bbGpayfThb_Z09LY4PhPDmRUIDvn7EJpMYhvndk,5028 +torch/include/ATen/ops/promote_types.h,sha256=fHTFhnXNV_sCMQOkScr9IpLnGFnKYz-oku3JHgo3mEk,723 +torch/include/ATen/ops/promote_types_compositeimplicitautograd_dispatch.h,sha256=PXydHUCkUBtxwqqijy44jDt3ON4oHNWn46NmYrto8Vc,794 +torch/include/ATen/ops/promote_types_native.h,sha256=U4z_wHig-qoyu9h0Pa__UtpvGymkDPZMAoj_Kde_LBA,506 +torch/include/ATen/ops/promote_types_ops.h,sha256=xzu4pVUd_3R_0H99U1Wl4U7hWGsZ7CsAGnV3x0radvQ,1067 +torch/include/ATen/ops/put.h,sha256=9lbP4MVI6XwmU77YXdN0i6RVsIHvqYtIAPgoVW9Qztw,1456 +torch/include/ATen/ops/put_compositeexplicitautograd_dispatch.h,sha256=l_DGfdycUKgZmXB8dpRWMDuxdT_D5SbzcRmFYE-hwxw,1134 +torch/include/ATen/ops/put_cpu_dispatch.h,sha256=4w1kldOr0MjApINC2y1DTQKALWKSfnDGX3tRAfhZT7M,790 +torch/include/ATen/ops/put_cuda_dispatch.h,sha256=tO-ABuc3BLcp3EqlSCNDFWFnmnMw4czDZV1opfZlGtQ,792 +torch/include/ATen/ops/put_meta_dispatch.h,sha256=lDUgR5LgSZNlJfh8fguKabnS806cl8m5aMUWhhJcCqc,792 +torch/include/ATen/ops/put_native.h,sha256=yPvq-4hUaGJO86y5xvZxYZ5wmlB3lDQ_43A7Gs71R-s,818 +torch/include/ATen/ops/put_ops.h,sha256=XQGZKymtcScHlnRzSYVtMnjGnYp5HSHcWyxqzPUYnmc,2733 +torch/include/ATen/ops/q_per_channel_axis.h,sha256=xwQDM5Iwa7lB8hfUAOuRQJaUtYXG2ogtOxRBGiAYiro,679 +torch/include/ATen/ops/q_per_channel_axis_native.h,sha256=yEuzS_BS0WB4mMT72cNZM0M98ANCo9ljuC6f83a8sjI,485 +torch/include/ATen/ops/q_per_channel_axis_ops.h,sha256=kKEfO0UtuDa6HsPhMmAr13yVniOITPImJl_sO_mMAE4,981 +torch/include/ATen/ops/q_per_channel_scales.h,sha256=6DfLUgnqOsHjG6jUoCqqq-1I1PvKji60dtZRQOA9Chs,1167 +torch/include/ATen/ops/q_per_channel_scales_compositeexplicitautograd_dispatch.h,sha256=BdNy2NLW_IbfCFGdVTgfHs0DoPyvBCpTG8Y-we51VN0,895 +torch/include/ATen/ops/q_per_channel_scales_native.h,sha256=HqfZk2cC8V8wZVxx453T3ikZ5SMAaDpjMM5MKSoLkIk,582 +torch/include/ATen/ops/q_per_channel_scales_ops.h,sha256=iePBgTiXnK-NUEfHDW7oB6QvRUR5thpe77lBGA8arDk,1631 +torch/include/ATen/ops/q_per_channel_zero_points.h,sha256=sXnYsb2BCSfb0jASFTASUZVO78wH2P-kI1Vx4kR29m8,1217 +torch/include/ATen/ops/q_per_channel_zero_points_compositeexplicitautograd_dispatch.h,sha256=zyI61O2nyKl0qwm8xCWSLW-f43wbuolZhFGWwP8ZEOY,905 +torch/include/ATen/ops/q_per_channel_zero_points_native.h,sha256=NX_Dd0s-FG5tYpLwToO7RVmF9S_payMpCIdegSu5s7o,592 +torch/include/ATen/ops/q_per_channel_zero_points_ops.h,sha256=29EDahfCFjAqVr2kKTM_fPE5xAuVMdKB9e-NJejuTFA,1661 +torch/include/ATen/ops/q_scale.h,sha256=24gx10JkhPbtIitwd4RnZG6T0NbqT9f2ilmvqaeyPMs,636 +torch/include/ATen/ops/q_scale_native.h,sha256=KKZ1zPXa4OTSgWDHYDtgwnSzFhbiDVpJUDccoHOrc7U,479 +torch/include/ATen/ops/q_scale_ops.h,sha256=hDvpEhtlNYJOFLqsRXJN17yt2Lw-rccy6KRa3fGzkKk,947 +torch/include/ATen/ops/q_zero_point.h,sha256=Lx4n0GRk6imvPjjguJxcFDdo17BToISqv9jcVPpGFsM,655 +torch/include/ATen/ops/q_zero_point_native.h,sha256=etbL4tAbcSTcHzCFN45y-LKNAyEU5yZmnElE103yb-A,485 +torch/include/ATen/ops/q_zero_point_ops.h,sha256=qoC4q3bdbdTcWwEh-UW4Sl9djititUpv-t4SUF4vMXM,963 +torch/include/ATen/ops/qr.h,sha256=AVyeCR6P8ZIUrflG-U78etj8faWBFVl3q-qCNpxUoxM,1271 +torch/include/ATen/ops/qr_compositeimplicitautograd_dispatch.h,sha256=_HiiHYUpG2P_4m5PgJQoKRjKXlhj5zint8T9Dw-pyDY,1059 +torch/include/ATen/ops/qr_native.h,sha256=kWCmmfjIW4xIEEVy3Zqq1WolPmwV0xbU5vc_MvE09nQ,639 +torch/include/ATen/ops/qr_ops.h,sha256=1mm2n0vN_OW4UKndVFL-0ef3zlqSNO8LSifw0Vo-OmI,1847 +torch/include/ATen/ops/qscheme.h,sha256=cxJZ0oxtFhsz-3ssUeam2pfrRwE8z_SNu9hFSnM3zqw,504 +torch/include/ATen/ops/qscheme_native.h,sha256=c0krA00bfqQ7xMohCgn4BHxGS0taeddm_QVtpzgMqVs,484 +torch/include/ATen/ops/qscheme_ops.h,sha256=t55SZV7x9DFVWiTOzSKfK1xzklQ6DonnKeqgiW9bjyI,964 +torch/include/ATen/ops/quantile.h,sha256=KsW6ZKnyNByhoR6PINn_VNWvtcgLVv1c2Ja1Dh-_UB0,2937 +torch/include/ATen/ops/quantile_compositeimplicitautograd_dispatch.h,sha256=ErahUS5ag8nVf-y9Dsjj9amPj7vyzNFpkxNIwBssRvs,1816 +torch/include/ATen/ops/quantile_native.h,sha256=7vuQfoioBRAZwVZG9Z0mrjRaBT96x1rs6Gr4r1NBwLI,1122 +torch/include/ATen/ops/quantile_ops.h,sha256=qtYuWFS_4C7K9fvx5eE0aWgMGEd4FmY0iWdOlAf2o2g,3990 +torch/include/ATen/ops/quantize_per_channel.h,sha256=SNIcFBJvr9ZoiR0GszeEm_0I3qMDDxrJ7frydM2-NcE,1743 +torch/include/ATen/ops/quantize_per_channel_compositeexplicitautograd_dispatch.h,sha256=ickXjBuswE9V6JsJxytriK8dIwcmVSB_9xIm4fsYWVo,1085 +torch/include/ATen/ops/quantize_per_channel_cpu_dispatch.h,sha256=NaFKeju7p7cM_QrueHErPKxLyxqDHLEKBAdoB-RxWSU,829 +torch/include/ATen/ops/quantize_per_channel_cuda_dispatch.h,sha256=JIBPdR5Uc1MVf0MfcsdrjtPmQPNe51Yf8gfjGUNP8t4,831 +torch/include/ATen/ops/quantize_per_channel_native.h,sha256=nk_BfJRE9Is4_dcIR3LsEEE5BnG484IDwR-ZuEz8m2c,772 +torch/include/ATen/ops/quantize_per_channel_ops.h,sha256=kCDlhnkAepgpANt_A4LtcNzjm1-wfhb2Q8STKXe6XmU,2267 +torch/include/ATen/ops/quantize_per_tensor.h,sha256=KoDYremW81eIMQF1ZR_2Xi-Iipv-1w9RWHFMYlXrKmM,3934 +torch/include/ATen/ops/quantize_per_tensor_compositeexplicitautograd_dispatch.h,sha256=34H0NkvyD9s6dCwKQq2jiYB0XdkKWm33xFAaVYmiV6s,1677 +torch/include/ATen/ops/quantize_per_tensor_cpu_dispatch.h,sha256=QdydgcSmRkv_QespwhuE_vrXSRS-fnfVeKeIdsbTkuY,1097 +torch/include/ATen/ops/quantize_per_tensor_cuda_dispatch.h,sha256=UsMtktdrp4usMxFc-Ow1sMAmftneuH463NbGL6-8C2k,937 +torch/include/ATen/ops/quantize_per_tensor_dynamic.h,sha256=Ra9Gq7w94a_OMrFs0M4jVpNyHttGcH9z4sYFUpnwp7k,1534 +torch/include/ATen/ops/quantize_per_tensor_dynamic_compositeexplicitautograd_dispatch.h,sha256=ZYH0mlVOHbiGw-yaWCD6t1m3-4VAjXTN2ruaMlMEz5I,991 +torch/include/ATen/ops/quantize_per_tensor_dynamic_cpu_dispatch.h,sha256=1eJnnQqid2OO88F2-eDS4MYR-WkvOnHRAFp7hAiWk1I,782 +torch/include/ATen/ops/quantize_per_tensor_dynamic_cuda_dispatch.h,sha256=-7hnghli2hQCNRS_ZQ85qAkgLBNFFb3bMXVKwfmeiMI,784 +torch/include/ATen/ops/quantize_per_tensor_dynamic_native.h,sha256=UA8OiAU2aQ2qBN2J3oGZkgVupyHN6gdBq5IKV06t33Y,678 +torch/include/ATen/ops/quantize_per_tensor_dynamic_ops.h,sha256=j1dWop8pwbho64ytxXEF0H-_-jHwIi9usqOkIGQ-UB8,1955 +torch/include/ATen/ops/quantize_per_tensor_native.h,sha256=0Lk3aKPqjT8gHTLG1hSsictMimIxVzPax7hY_ROabXg,1382 +torch/include/ATen/ops/quantize_per_tensor_ops.h,sha256=A1wR-0UE-jvpU5TLw9Fch0OUpGMGQHVyveA1I0hkxbM,5574 +torch/include/ATen/ops/quantized_batch_norm.h,sha256=QVcsxQiyhnc8uLPCAWGRimlxxBOOYz5s_j80LBUzfYs,2277 +torch/include/ATen/ops/quantized_batch_norm_compositeexplicitautograd_dispatch.h,sha256=e6qFMuEYM5bISuNAhUcL10tnrQ_DK9QfnXKgzbNATIs,1287 +torch/include/ATen/ops/quantized_batch_norm_native.h,sha256=QjlQyeoxml6khgG3VwZrDJks578O2Za0Pu7B3nPssZY,974 +torch/include/ATen/ops/quantized_batch_norm_ops.h,sha256=gY_UpZrY1FCYcEgezyUZxh0L1ruVmgf-Oep-8xYfnhU,2913 +torch/include/ATen/ops/quantized_gru_cell.h,sha256=ZrD-WGB7jetl4m6ZL8uL_wZGEmjzv4bxRbw5u9ZeB68,1422 +torch/include/ATen/ops/quantized_gru_cell_compositeimplicitautograd_dispatch.h,sha256=mye9ElBjpdgd6Utk2WKGA8JdetNzbrgjRAGA12Jxv4M,1156 +torch/include/ATen/ops/quantized_gru_cell_native.h,sha256=AgxqqgyFbKRjO2Qz0SHfyzY0tvr-hod4pZcq5GaGRrc,868 +torch/include/ATen/ops/quantized_gru_cell_ops.h,sha256=xBYg6tFFe2tHG48rUIpkgKrQmo1CnIscOh1VtlILEOI,2237 +torch/include/ATen/ops/quantized_lstm_cell.h,sha256=9nHd5FiCeAJCp7QldQGmcyhHjwmasM06We_XKvQ9-Xo,1459 +torch/include/ATen/ops/quantized_lstm_cell_compositeimplicitautograd_dispatch.h,sha256=CHd94WfD-LLTc15yqjg8dg90n2DTA2ah_Sk-jCMj4Lw,1178 +torch/include/ATen/ops/quantized_lstm_cell_native.h,sha256=bfXsk_9rlzlmzhr6LPcbqU8QdFqH9CelaUdUb6kvov8,890 +torch/include/ATen/ops/quantized_lstm_cell_ops.h,sha256=G9aFeLEcqKk4maM2cG_vCo94puA4g4ahcwvn7V52kVM,2315 +torch/include/ATen/ops/quantized_max_pool1d.h,sha256=boOfIHQ84awpCKMQpeomLXxB7HragjRNN3gQtevRAeg,1997 +torch/include/ATen/ops/quantized_max_pool1d_compositeexplicitautograd_dispatch.h,sha256=nTpmlgbJPKzjSKB7TzdnAbX8fP4ozzrZfxz2tQ5GNLs,1148 +torch/include/ATen/ops/quantized_max_pool1d_native.h,sha256=f_SIDSuZTZFdDkCvwVHP42UT9tuxUNaPGbEGPA-R0Zk,835 +torch/include/ATen/ops/quantized_max_pool1d_ops.h,sha256=YZX7Y4N1WlNQHzX69yq_AEmSmY_8ZguDmIiai0s8Dew,2453 +torch/include/ATen/ops/quantized_max_pool2d.h,sha256=lq_o1ra9mwCU25kT2Y-OEETRft_vpRdvbSHa-gmFS5o,1997 +torch/include/ATen/ops/quantized_max_pool2d_compositeexplicitautograd_dispatch.h,sha256=_mAEwP7G_qANjqlEsnm_cHcW9OrEv9tKx4_Pr3OGjZA,1148 +torch/include/ATen/ops/quantized_max_pool2d_native.h,sha256=znLsYn6Ze2QfpR9NQFvyT4AVm3IJqCIMLzNgI-FRz_M,1042 +torch/include/ATen/ops/quantized_max_pool2d_ops.h,sha256=0kbh8Ludb8b8EOmVf0dZ6DWXZURZJ_sOBMRdTsXgP40,2453 +torch/include/ATen/ops/quantized_max_pool3d.h,sha256=17YUcNzEZRoDrMgGtvZ6Lb2wRDAO9kG2b18DGeLbLXM,1997 +torch/include/ATen/ops/quantized_max_pool3d_compositeexplicitautograd_dispatch.h,sha256=Ah4TkNI_CfYqSXg7NFT_JOgS5mW3XQxy_CwmZ4HBXEE,1148 +torch/include/ATen/ops/quantized_max_pool3d_native.h,sha256=jwxntBHnnUF09A9Lz_7ai3ykFkvXcz_7mzRCbSL6UN0,835 +torch/include/ATen/ops/quantized_max_pool3d_ops.h,sha256=kHYIwPhU5vntDd4cTQfm8RxShqplAPJminVLdmvMbh0,2453 +torch/include/ATen/ops/quantized_rnn_relu_cell.h,sha256=eqaRpaq-Lt19HoGRZ4vu_NORXFruRRJvXXzkM4BwVwA,1442 +torch/include/ATen/ops/quantized_rnn_relu_cell_compositeimplicitautograd_dispatch.h,sha256=_NTM5Jv2skraZVsgLwRmYOXHo0HdGdQLdSo5fmFHr2Y,1161 +torch/include/ATen/ops/quantized_rnn_relu_cell_native.h,sha256=fB7IDGSpvTL-ef_6UtNBOnVKxLdKCLqVwGJEuT5a4GI,873 +torch/include/ATen/ops/quantized_rnn_relu_cell_ops.h,sha256=88JXitrG0HHpXygB-D72dVPLkpvuYubYc7uWq3laqmA,2252 +torch/include/ATen/ops/quantized_rnn_tanh_cell.h,sha256=7R2JBzEpcyr97D2Uh-sf0gCShu9YfCOngVr3NH8vwYs,1442 +torch/include/ATen/ops/quantized_rnn_tanh_cell_compositeimplicitautograd_dispatch.h,sha256=hdXVManL2nYQGh-VksacdcyqKPGjaFuUZOYPX2vbkN4,1161 +torch/include/ATen/ops/quantized_rnn_tanh_cell_native.h,sha256=9E-jPiTGhCGcCoc4vcB1L7g06anoYS5XlYD78oi5nz8,873 +torch/include/ATen/ops/quantized_rnn_tanh_cell_ops.h,sha256=ND79VEd53z94_uwpuRokgx2KcCjRIljeO7K-6UypZUg,2252 +torch/include/ATen/ops/rad2deg.h,sha256=gsosnoaWatfqorx6mJ05pnD4IWRXnvbNzcL3h34tNJQ,1182 +torch/include/ATen/ops/rad2deg_compositeexplicitautograd_dispatch.h,sha256=rP60qflK5pdHdkhb17SLZSnBZb9bEXGr5_XPgc2Ix-g,976 +torch/include/ATen/ops/rad2deg_native.h,sha256=9LfVRZ6-WrtAnpT3CQXARtTNuYIXZZZWyBgQ9WY_NI8,1034 +torch/include/ATen/ops/rad2deg_ops.h,sha256=jP6Nce4SucU3F96-JTelGkwC1216uWvyKDl4qFXc-9M,2055 +torch/include/ATen/ops/rand.h,sha256=EcrLRhKJnXZ-scb9RyLZPeZMTV3aoYm_PUvDqkKhDwY,24764 +torch/include/ATen/ops/rand_compositeexplicitautograd_dispatch.h,sha256=2I7MyOoqZgqVneuLSsLtBakGygng1mkRQ1BRmHxG7SE,5074 +torch/include/ATen/ops/rand_compositeimplicitautograd_dispatch.h,sha256=wNRe6KPbNZXatGewiYryAMGq3-KsceP0nf2sO7ZKhl0,1194 +torch/include/ATen/ops/rand_like.h,sha256=DjpR_Wg-PP5CmnyIsikivwo9wwDuJ9YcMfmP4U7Cm9w,2208 +torch/include/ATen/ops/rand_like_compositeexplicitautograd_dispatch.h,sha256=GKvFilfx9HqPr3_Jki_qfXelQQIHQtupE7h58FFpcmQ,1388 +torch/include/ATen/ops/rand_like_native.h,sha256=lJ908YWiNX_pOK-xfkV_VO0xS4Q6nhRALuL0wMDLI-Y,830 +torch/include/ATen/ops/rand_like_ops.h,sha256=iPI9Oa206BXwnWV4Jc0YM-nEq7_AXao_lK-6rkXmVO0,2395 +torch/include/ATen/ops/rand_native.h,sha256=KVPipma4JoLlgD6RccfhjysbSNiR40HBocIVhUtGUVM,1915 +torch/include/ATen/ops/rand_ops.h,sha256=I75rhkYVw5DTx11WH4XvJ6KARjahpsxBYD6Xhx3j798,8092 +torch/include/ATen/ops/randint.h,sha256=kMLscWkVbnzbhOwrbgz0ftiwEXlWhur_Ov_rWAn3aG0,26007 +torch/include/ATen/ops/randint_compositeexplicitautograd_dispatch.h,sha256=kHll7vC6OAgUs4IESjCnO-IvQOUIiOR7_HMe9IPtucM,5822 +torch/include/ATen/ops/randint_like.h,sha256=Khcx3VYUr7S9SGNxVjZ2kMqkqylS1us1tDsy6Hu8S9U,17415 +torch/include/ATen/ops/randint_like_compositeexplicitautograd_dispatch.h,sha256=aBpBr4mIxBN6iuMajiRFfpLBaQ4eDYOh6BvWSLtUMjM,4692 +torch/include/ATen/ops/randint_like_native.h,sha256=emPGL3E7iWdOaLSJ2cEi9kenm409OC9tXpocuKPaulU,1839 +torch/include/ATen/ops/randint_like_ops.h,sha256=qX12uj2a8Wr7iqjrwF6uu8KZo-5-jY_6D6Cl4tpcfUk,6957 +torch/include/ATen/ops/randint_native.h,sha256=E9WhIF_ShwiObU12pAZFRn8KiP_e_P_NiihLB76CQ3M,1894 +torch/include/ATen/ops/randint_ops.h,sha256=RWwUNc1XxnQbp0zYfOxlt5Sg-PN5dLNeDWG5bvLWBWM,8314 +torch/include/ATen/ops/randn.h,sha256=ruJhsV7eLXagZ6_BuwGlc0A5aRdr0c-TFeq1-BzvHAc,24925 +torch/include/ATen/ops/randn_compositeexplicitautograd_dispatch.h,sha256=J6flTzRpf9yYKtM8rixKzxv_50iJFogLcvOE5SnqkWc,4782 +torch/include/ATen/ops/randn_compositeimplicitautograd_dispatch.h,sha256=X7CDngvZE9bRvykiy18ecTwuSTePIBdMTx0jqjc3TFg,1518 +torch/include/ATen/ops/randn_like.h,sha256=0MzDh_USdkPQnZ3G1QaCp7lKySwI1u2iBrlEFOOAnng,2221 +torch/include/ATen/ops/randn_like_compositeexplicitautograd_dispatch.h,sha256=_c5dtJZhGofSjz57wtvZkCHeLyucTvwg2WUsifaCmFg,1392 +torch/include/ATen/ops/randn_like_compositeimplicitautogradnestedtensor_dispatch.h,sha256=21b6Ptlw-c92kSHNoMfDxqsLCg_CohEkssr-kic8BM8,1138 +torch/include/ATen/ops/randn_like_native.h,sha256=7MOSTbWXLnVpON2Bjf3rBRoFqY80aCJ9b-OksCvs42o,832 +torch/include/ATen/ops/randn_like_ops.h,sha256=ey-A5bxk05L5ShqDng-y5bnzjrvZqHPC7qHdu66gDxo,2401 +torch/include/ATen/ops/randn_native.h,sha256=2JQ0Jk-SSZResonE85kcV_7CZG1-IaJJQGFUb3PdGeA,1923 +torch/include/ATen/ops/randn_ops.h,sha256=7Y-jka-1zRUcmOiYHB8yBWw4efQb_YMHpBEVseX7vZY,8116 +torch/include/ATen/ops/random.h,sha256=w7mVRYIFSpm3sSQb5__UOVmtMgxIzqOCYqY-v-eKloE,3232 +torch/include/ATen/ops/random_compositeexplicitautograd_dispatch.h,sha256=hideafbsoEfTN2z6DTI4DwGQ0wp6bECtV08Bbf95Tgc,1976 +torch/include/ATen/ops/random_cpu_dispatch.h,sha256=AX_KDBI3UWfuiyyoVDhWFK_yfV-EN3YWFhTjrbpmhv0,1045 +torch/include/ATen/ops/random_cuda_dispatch.h,sha256=okfAsOHoTYkczs9U17QqSHODz331J_BS0FfMSu68R3o,1047 +torch/include/ATen/ops/random_meta_dispatch.h,sha256=f0589RsXv5uLYTf-H-al-H2ErnpXKDejKJtyWhjqAEo,1047 +torch/include/ATen/ops/random_native.h,sha256=3rpPY5SB6nzCJrFIv3bbKD6yTNdP6ZefAQXTGQ8FDF0,2006 +torch/include/ATen/ops/random_ops.h,sha256=XhrEd3qJm51PxAE1OSPHdXwnR2ejvNSWrP5-EqafVv8,7140 +torch/include/ATen/ops/randperm.h,sha256=bEPdaPOrqyZve3jHrBvBKGeiulN9WS3BHgKbuPH8Nls,10968 +torch/include/ATen/ops/randperm_compositeexplicitautograd_dispatch.h,sha256=wrnWce2oyNo_kce7bkS5iEEeI39mZsm081e-zhm-4qM,2274 +torch/include/ATen/ops/randperm_cpu_dispatch.h,sha256=8l-gxOBKzpVl8q6TQxYieexGLrpp-DI-3w7YXYtlEqM,1122 +torch/include/ATen/ops/randperm_cuda_dispatch.h,sha256=53wTn-HUiD3SktVTyvC60TM7QSG89CMRd7If-2QkOxQ,1124 +torch/include/ATen/ops/randperm_native.h,sha256=3rPQxVemUkm4QPL5oq5614LGJ6rkwiiD5KgW4lLCFi8,1153 +torch/include/ATen/ops/randperm_ops.h,sha256=aFG-Hy89w5aWUzDYuPgBElUGGVvVD32FD8dXnxrhSSU,3868 +torch/include/ATen/ops/range.h,sha256=gtl5RydAJ_N5a0IRA0XLYWAhWwA_ktZo8srePIpqM2Y,3393 +torch/include/ATen/ops/range_compositeexplicitautograd_dispatch.h,sha256=EbhcEAP-37htLpxuxMu-GHvjIuj9uX3L-VTe-zw2Lkc,1629 +torch/include/ATen/ops/range_cpu_dispatch.h,sha256=-1-AMk7WfA1bS1JkZQir3cRFV9O-KyvXZ7CZqNyV-_E,921 +torch/include/ATen/ops/range_cuda_dispatch.h,sha256=gd8fhCREoZUKnkfzzy9bhrkAsw9FGBfv3jpaY2AFcU8,923 +torch/include/ATen/ops/range_meta_dispatch.h,sha256=ZfGyV-T6liGMdi1Z1IBqmzbUS_EKLhIjGWl03akaXO8,923 +torch/include/ATen/ops/range_native.h,sha256=1ApLv7l7bxzMwaOJY-3gCVzuSobomWnoHbiFEJG0FjQ,1288 +torch/include/ATen/ops/range_ops.h,sha256=AoC4yh4TdWP3hxNIFUHygenLG5Ts4b4FPH2GfU8o3XY,4136 +torch/include/ATen/ops/ravel.h,sha256=oLgXtqRBGm1BKeg7xyN7sg-ZqiaZCXleS0iMeVqpoP8,639 +torch/include/ATen/ops/ravel_compositeimplicitautograd_dispatch.h,sha256=y34AGJ-fC0ir8cxN9PK5iRh3NEVBVyFQpfdj3W1KmCY,763 +torch/include/ATen/ops/ravel_native.h,sha256=JMpvLmwhdYUbfmYNVFU-e4HZuQppQh_ujI8pb3CJg-k,475 +torch/include/ATen/ops/ravel_ops.h,sha256=WkYs1UO4NfhpxRIyGKcLQrx5sgQouAZDDqqoyqxH-Rw,960 +torch/include/ATen/ops/real.h,sha256=4o2NXD0Yp3GIYIPIsXnE9mCbL44tnQp2ta3UUpRZoHM,635 +torch/include/ATen/ops/real_compositeimplicitautograd_dispatch.h,sha256=6YNWDH2lAMTG3q3-6sMk-oGOjw_JJKHVoJB2_3UYkfc,762 +torch/include/ATen/ops/real_native.h,sha256=TyETnCQFF1x1BPiKsqR9ArZa7FvExDTmPDl8RNxlGAg,474 +torch/include/ATen/ops/real_ops.h,sha256=BrLnEfSZYgBxqGMtBf3hJpuJshiHfqJLGz-AFmCa5rQ,957 +torch/include/ATen/ops/reciprocal.h,sha256=oJ29vPiLtlXHAPFBqx07K9qow_RGFFqlDF7a4_oE9Kw,1221 +torch/include/ATen/ops/reciprocal_compositeexplicitautogradnonfunctional_dispatch.h,sha256=J_4RQFUT9KSGXNEDQkJiwN0dTGpeA0Zk6MignB34bTM,849 +torch/include/ATen/ops/reciprocal_cpu_dispatch.h,sha256=dDhXsZ1vOEoOMKD2kbBrBP6OM6QZxFlq5Xxt26h0Dgg,944 +torch/include/ATen/ops/reciprocal_cuda_dispatch.h,sha256=dYwm58iVZsm0GZvrj7v1O7wd9fo7sQb7wg4zuw7BdI8,946 +torch/include/ATen/ops/reciprocal_meta.h,sha256=FoeQWiL5vBQ58gtwLjLOa9vLht2CBlvfoiQe3zbdb8U,571 +torch/include/ATen/ops/reciprocal_meta_dispatch.h,sha256=tPwqi_RTl-9qeA9HsJoMQp87fRPKA_tpD2AhbCLAZlU,946 +torch/include/ATen/ops/reciprocal_native.h,sha256=VJ_9PcoCTy1NSt_hrpTL9e0ESeaBwlHiM_YI8IgE5uo,608 +torch/include/ATen/ops/reciprocal_ops.h,sha256=XdFMfjMjc-VMBJspaQAc7OpRPbwjdABOYqZ_4voH-l0,2082 +torch/include/ATen/ops/record_stream.h,sha256=SoJ1NfW-3KcX5hfc3JeuCt9f49Tra8H_H9JozQYnv4w,510 +torch/include/ATen/ops/record_stream_cuda_dispatch.h,sha256=GPazjuR-Wmnn4PUBOcIDw-vdf5P7Eq9eqL5vCR1K7Rk,731 +torch/include/ATen/ops/record_stream_native.h,sha256=3v6g40OyuWsGJulrkOs3ywYOUED55qbDqP9ktz-2FBE,490 +torch/include/ATen/ops/record_stream_ops.h,sha256=ydh7OzH7UcS7ygsn34JrNuyfa97oYJF2psZqQLAuCXk,992 +torch/include/ATen/ops/refine_names.h,sha256=ZcYooN-YC9GujM-HynrbQhO4PqRI8lirtIyMLoRGW4w,509 +torch/include/ATen/ops/refine_names_compositeimplicitautograd_dispatch.h,sha256=8H9ZnsGXJhB9doMZPEOxAMQ8SvcKB-Ka58LeGL-S63U,793 +torch/include/ATen/ops/refine_names_native.h,sha256=9XKNDuYWfEzmZJ6YLcgK5PbDPW4se6vfEnKuN-A9CKU,505 +torch/include/ATen/ops/refine_names_ops.h,sha256=k8WxPjbZBbaec1RVLEwtBDkFDX7crtX19Z7hlbbceNA,1061 +torch/include/ATen/ops/reflection_pad1d.h,sha256=vxaFl889lC02-hCskSRg9jJkkkJehEJ8KRoHyl5yBLk,3897 +torch/include/ATen/ops/reflection_pad1d_backward.h,sha256=eHlPHeMxtWlCUoCQVdKRC2RLUpq0P8I79fceRKYwwIA,5060 +torch/include/ATen/ops/reflection_pad1d_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=95aWO_HH8E3qaTZ7MJDPkeYHINT4hQw8M1A3o5J4Pqw,1007 +torch/include/ATen/ops/reflection_pad1d_backward_cpu_dispatch.h,sha256=AEFqDUiMaM2qny8f9ZDyJ4B92irNj7oSVLjQdswsb90,1605 +torch/include/ATen/ops/reflection_pad1d_backward_cuda_dispatch.h,sha256=XJxhGgNrK7Ea3sjjUbObaOE0JfGBavX2-YIdWmky778,1607 +torch/include/ATen/ops/reflection_pad1d_backward_meta.h,sha256=oBNkKqi27d8XYEXWcoP9Wo-1ni2S1JT49tc5DHLBkVU,649 +torch/include/ATen/ops/reflection_pad1d_backward_meta_dispatch.h,sha256=Sn2aR1y1iboVxz0C1yEMw_MEbBHgxbSmLpbH2nb2KU0,1607 +torch/include/ATen/ops/reflection_pad1d_backward_native.h,sha256=6myDrCT9tIS5x6LoAwoyxHq8B7SIdgEJk3x3RWB3GMo,981 +torch/include/ATen/ops/reflection_pad1d_backward_ops.h,sha256=dXZaSU69axKnIsgn72R9tG2S1egBrLS4OQJmp65VkYE,2107 +torch/include/ATen/ops/reflection_pad1d_compositeexplicitautogradnonfunctional_dispatch.h,sha256=I-LHGrJ1CA0q5q1062boCYDLfbpXVJZI8UOaU6uTUiE,925 +torch/include/ATen/ops/reflection_pad1d_cpu_dispatch.h,sha256=somMZblKAyhJAOaq6vDkT4EXtAlkptnsVJTCGty63sU,1331 +torch/include/ATen/ops/reflection_pad1d_cuda_dispatch.h,sha256=qN_8bcvpwSnl5FHYHY6rvhYWH6jFwCdgE9qUZ6wouX8,1333 +torch/include/ATen/ops/reflection_pad1d_meta.h,sha256=NUcXKBUJhOI6LLV7cNmfkiONvowjERvaSx-kFv_QoMg,608 +torch/include/ATen/ops/reflection_pad1d_meta_dispatch.h,sha256=gDk3u5d3Vpmk6aQe6RYRor9MpXf7pdOk16znJlJ_1Vs,1333 +torch/include/ATen/ops/reflection_pad1d_native.h,sha256=4w2GbYwY17H6XZziBJy-th8cHP1U5KNey5iNVDD1KsI,985 +torch/include/ATen/ops/reflection_pad1d_ops.h,sha256=phot9SuJrVlqEXihjY8cZnKyEmagw8MzG8r0SsovIFU,1803 +torch/include/ATen/ops/reflection_pad2d.h,sha256=SDRAiGDg46jnp_jsRwit-9w8cuNzyo_gu7MOXgiKji0,3897 +torch/include/ATen/ops/reflection_pad2d_backward.h,sha256=sbhnGP7IuDV4Amf4_7vcTJhrsIDqRVJU_nB1S2WrdkQ,5060 +torch/include/ATen/ops/reflection_pad2d_backward_cpu_dispatch.h,sha256=lQOD4hAqcxjNFI8cd7OiPUPiUdkVbMqxXS2uK87-zsY,1605 +torch/include/ATen/ops/reflection_pad2d_backward_cuda_dispatch.h,sha256=T0fmRINLtZQdJLuUq1d7gxcDCZLBnlMqFgI-N-9GthQ,1607 +torch/include/ATen/ops/reflection_pad2d_backward_native.h,sha256=1Gi4-cazA3QL1yUb4_lnVZ8TUoWqhNtLA8v7ISCjMEo,1022 +torch/include/ATen/ops/reflection_pad2d_backward_ops.h,sha256=tGnP52WyuRoXh2R9PF37ikxQe2fCeWNu6cOvK9tYSiE,2107 +torch/include/ATen/ops/reflection_pad2d_cpu_dispatch.h,sha256=NP32RgPsJ-Bglx5Mttvg0DtuhJ4qJObeIkGnpj6gq3g,1331 +torch/include/ATen/ops/reflection_pad2d_cuda_dispatch.h,sha256=OwmJRckbc_C6BGuQlFzanUgBjT4Fs_ennbuQbyFAI6U,1333 +torch/include/ATen/ops/reflection_pad2d_native.h,sha256=-cd3Dc_l_JbsZJurspJF77Zl5ZJ8xS1gegz1h5AEY0w,947 +torch/include/ATen/ops/reflection_pad2d_ops.h,sha256=n2jABUV9N59o5F6NacMsXBuYyr8AIO8P4VQuYpgfqY4,1803 +torch/include/ATen/ops/reflection_pad3d.h,sha256=VxeSbDnEnAKe7R1PHiUFRrTQodFhGAmeTZWj2ATBZ7k,3897 +torch/include/ATen/ops/reflection_pad3d_backward.h,sha256=1aSndVBiuXnHL2R3c1tNhUIM_iAeYm40DvfgbM5TtPs,5060 +torch/include/ATen/ops/reflection_pad3d_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=4wjSityiDuFqEswI5ejk4TVRUS9RTUZbI83xfeFBz2c,1007 +torch/include/ATen/ops/reflection_pad3d_backward_cpu_dispatch.h,sha256=WmwR_afgzOhcqnw07Xtu9ytsb08mhBSltOFRWRCes8E,1605 +torch/include/ATen/ops/reflection_pad3d_backward_cuda_dispatch.h,sha256=5VlhwkxKeUXDvUvHodhC7C7xcKMq12I0Ip2-2RnqPpc,1607 +torch/include/ATen/ops/reflection_pad3d_backward_meta.h,sha256=zU_sSbDMxmtpGKgYTTuNYgoJisHqt7WHAnysDL5kfNQ,649 +torch/include/ATen/ops/reflection_pad3d_backward_meta_dispatch.h,sha256=mcyvHJqET-Iv8htPZmEkFXnOwsk0kv_OixcfQnNJmCQ,1607 +torch/include/ATen/ops/reflection_pad3d_backward_native.h,sha256=OPr0yZIzSp7wXOVGIIspG91LjRpWxTZc5YVxB1o01Xw,981 +torch/include/ATen/ops/reflection_pad3d_backward_ops.h,sha256=NR4CA3uUy4kHjAYo6KnhVDTQBL_nysm_Nk6PZZGjFpo,2107 +torch/include/ATen/ops/reflection_pad3d_compositeexplicitautogradnonfunctional_dispatch.h,sha256=Gnd4ekIbrfW0akHtc0zCa7BqxprbYKmxlG8zLxyEvrU,925 +torch/include/ATen/ops/reflection_pad3d_cpu_dispatch.h,sha256=Mo-T70F9YhfSvxKOJBTLq9UbEqENR4LWREQy1t5BTX0,1331 +torch/include/ATen/ops/reflection_pad3d_cuda_dispatch.h,sha256=_UMYGJoet3_HdpKw8PE-zJUYOUXjFNVuT31B3wdS7Zw,1333 +torch/include/ATen/ops/reflection_pad3d_meta.h,sha256=ncwNLD1HD2rA9H50FwWxLlm3L9M6IR8oMergjjzMOGU,608 +torch/include/ATen/ops/reflection_pad3d_meta_dispatch.h,sha256=hVEo5ZqxmmKw-r1suerYQ4HmZ-BkcO5xxTJLE_W_Xvo,1333 +torch/include/ATen/ops/reflection_pad3d_native.h,sha256=jC1Gld_fmVm9oiX7YWF8EnsDvvts14MssCbhvh6AIgA,858 +torch/include/ATen/ops/reflection_pad3d_ops.h,sha256=5VK-BKX1kvIxq4hrjSvv8xg-jBQcZyVefbRrujNuIt4,1803 +torch/include/ATen/ops/relu.h,sha256=o9jTOVTQnpZFABrCHuhGQ-xhgS8OOC0wPe__sOJTJgA,1143 +torch/include/ATen/ops/relu6.h,sha256=F15Fem3-R51HtPkOPXqsQ8KVGlJIZvUdqKvRZtiq-JE,772 +torch/include/ATen/ops/relu6_compositeimplicitautograd_dispatch.h,sha256=mBhJc4t_xtPNb7dz4fXdXV_hppGR07QdI9CPd_l9uPU,813 +torch/include/ATen/ops/relu6_native.h,sha256=UrN9u7jaoHu4uqaesk5xNq75lbh5XiVeDKUxQS-Pmos,525 +torch/include/ATen/ops/relu6_ops.h,sha256=tW8LBbrPP66W3EKu_dvOF3azDQzPVlKu_ryIQRM2Mfw,1450 +torch/include/ATen/ops/relu_compositeexplicitautograd_dispatch.h,sha256=HH80q0NpxEA3ZuNRMMYaMfXVV_aDQbSh_W9lhx7LKaI,863 +torch/include/ATen/ops/relu_cpu_dispatch.h,sha256=lVcL-OUvag-ZLkRP-kSHn9nf-vKxfJUAG3281XbBERg,767 +torch/include/ATen/ops/relu_cuda_dispatch.h,sha256=RUuu-YvFcSRRfugnoQS8ITtE3QMyk1TqT_7w35POrRA,769 +torch/include/ATen/ops/relu_meta_dispatch.h,sha256=bpW3LZOp4PQbvaFxEVHfg9Atzy7-bmPd0KM1Kipg1U8,717 +torch/include/ATen/ops/relu_native.h,sha256=w6D78hzY1IjuaqyhflCMhtsmP8wMdqQViAntizPaQW0,1339 +torch/include/ATen/ops/relu_ops.h,sha256=ZxIVBBlRaPjPyS0lp6l3PWn5uM7JRHZXdzHkTMoAq1U,2028 +torch/include/ATen/ops/remainder.h,sha256=8HjmYYYMTpQHXXA2lRqU6eaN3lG8lr5ay9iOkpfF3PQ,2752 +torch/include/ATen/ops/remainder_compositeexplicitautograd_dispatch.h,sha256=2uzaypqoap8QoYtG7ImwiStZJQu1PdYCzDctZ_xZNps,1303 +torch/include/ATen/ops/remainder_compositeexplicitautogradnonfunctional_dispatch.h,sha256=zV24m3xE6UJDCRv3eSF3hojV_9JuNV-iHrY1rsxYOpk,899 +torch/include/ATen/ops/remainder_cpu_dispatch.h,sha256=8ySH5a0NLQt6y3Qmw5ZyMVzxjOgWv1rh3IfE-d-Y73s,1127 +torch/include/ATen/ops/remainder_cuda_dispatch.h,sha256=O9fSel-afEwEVp2jdg3MFkEwr1y9uXQJQ0_lOIna23A,1129 +torch/include/ATen/ops/remainder_meta.h,sha256=7JD7UpwE7VigQgrozdJu6C1VyffHltiVyOxG2f96caQ,603 +torch/include/ATen/ops/remainder_meta_dispatch.h,sha256=G8etek_LNqOEEGMnKEwaKKHWg5VEHaIdgU5EU1sW4Do,1046 +torch/include/ATen/ops/remainder_native.h,sha256=g6PHkrzN8jYif0FlaPXihrq_eECQLr4zafj3XS0Vd3Q,1112 +torch/include/ATen/ops/remainder_ops.h,sha256=W9oJinUsz6vdAmct_yuWVXqO4Fh1s5Gwpe1JKt5mP-Y,5690 +torch/include/ATen/ops/rename.h,sha256=4OXiRiYrYod99SFin289drR0kc3oarYW5-WMBGMEEo8,503 +torch/include/ATen/ops/rename_compositeimplicitautograd_dispatch.h,sha256=Dr-R55heYaJOtB4_-e0I8g3GCspIQUNiuBaCjoSdEA0,895 +torch/include/ATen/ops/rename_native.h,sha256=61RLZ-3w43NxJtDMdlRE8jfn3AomPs69KyykcY9xjMU,607 +torch/include/ATen/ops/rename_ops.h,sha256=BLHNeQn45sSUOWanmyo4JmoPBMbm7qW7C8hvzo7KFd8,1726 +torch/include/ATen/ops/renorm.h,sha256=-i-Xuvpo2uUtCucxFfG4Mw9VMvaUZW2clmKXuBmJM1U,1372 +torch/include/ATen/ops/renorm_compositeexplicitautogradnonfunctional_dispatch.h,sha256=QgUBZ9SHYogr9rAbqVqalcWvcVUrSiiB-0hxxBhwnK4,967 +torch/include/ATen/ops/renorm_cpu_dispatch.h,sha256=se6xcBN5LSr4CTKp3cxDGSn0nsvWUqZwKT_TCEtbK4M,1180 +torch/include/ATen/ops/renorm_cuda_dispatch.h,sha256=Y1ZhbG5I9ia_oEqRUKlG0MdLeXIqh7ZCTTZ6virg-es,1182 +torch/include/ATen/ops/renorm_meta.h,sha256=9nkIVNItLG5F3et_8_kM2G1xsN93SZUBLW0-UDVbWNg,630 +torch/include/ATen/ops/renorm_meta_dispatch.h,sha256=qIKDnf2I6fDc2c1bIfp5pCIEVnct3M1qw4-Om7rYQeE,1182 +torch/include/ATen/ops/renorm_native.h,sha256=tIY7NKYcnaMkTTwU4Popih4dbbkSWPpKA8U15Mj_6jA,659 +torch/include/ATen/ops/renorm_ops.h,sha256=uWMEwSlGsSayt4l7NoL_iMSB-zCccSWkm0X37uemZQY,2676 +torch/include/ATen/ops/repeat.h,sha256=oo8qUWvbvGYPSXYH-wZu_e_fc50mVCEG0-xDK3Jd95Q,3172 +torch/include/ATen/ops/repeat_compositeexplicitautograd_dispatch.h,sha256=oZMOkEWBjGt9qD_Qx4clQL9NOBrebKV_q9hIrsw6qXk,1315 +torch/include/ATen/ops/repeat_interleave.h,sha256=luQl-iotHSd9AKphXuOGgoo8ApKtogIlM3v7pZdOb-o,8278 +torch/include/ATen/ops/repeat_interleave_compositeexplicitautograd_dispatch.h,sha256=-6bvFUuvUOvD2LOaJL7sLSbNH9NbIJ_1hw5cOKecLlQ,1284 +torch/include/ATen/ops/repeat_interleave_compositeimplicitautograd_dispatch.h,sha256=qlhtCIRjZMS5JiNmdEIK1PbzTGEU5qt2lKoUbg_9DgE,1478 +torch/include/ATen/ops/repeat_interleave_cpu_dispatch.h,sha256=7zw_EBfyrrkSMOyPIkqYuh1NxDoozsqB3u8mqPZoolk,919 +torch/include/ATen/ops/repeat_interleave_cuda_dispatch.h,sha256=sfaCKLwRsL9k56-4-yfQ7VXqA0xRvil5keAiuC46fRI,921 +torch/include/ATen/ops/repeat_interleave_native.h,sha256=TfHuEgQYDNNB7nxIVk8V333qz4fKXAXs719_KblCYy4,1218 +torch/include/ATen/ops/repeat_interleave_ops.h,sha256=gCy3HmZLJBxYQA1gksR2wJ8fUJfEPtCjpJiZWIQf8eI,3733 +torch/include/ATen/ops/repeat_native.h,sha256=Y-hE4u19QJKJ9Zsh9xiaWhsj19E_wkJQu94Gc8koJtg,615 +torch/include/ATen/ops/repeat_ops.h,sha256=a1-n4wJiZmrbbNgj3L5AcWtEmAhXeq0Y-HP-hng17Xw,1741 +torch/include/ATen/ops/replication_pad1d.h,sha256=IPrOe0V8W2nyTfJPZ_PeF-tM8ERu-EfdWen5LrwCo1I,3928 +torch/include/ATen/ops/replication_pad1d_backward.h,sha256=WOPUKJ_0gbBKhu2jw1vmBeF1D75u8YnVexHzVFerWOM,5091 +torch/include/ATen/ops/replication_pad1d_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=L6biPZEg7OKEe3480KDefz2QY3l9WcUe1SIXuwG0en4,1009 +torch/include/ATen/ops/replication_pad1d_backward_cpu_dispatch.h,sha256=A6bDpsVajw6tiaetLap2fzPNbeUVvtNdfhBvyCAhumc,1611 +torch/include/ATen/ops/replication_pad1d_backward_cuda_dispatch.h,sha256=ahQQ9MuOSif0-tqAdF6C6-Xwu8z6erqEQRqE6pDUxT4,1613 +torch/include/ATen/ops/replication_pad1d_backward_meta.h,sha256=RQQNo4sP4ZgFR9JbiFycGo2KUlcgLk-JynyNA0--2wg,650 +torch/include/ATen/ops/replication_pad1d_backward_meta_dispatch.h,sha256=F_mHlFTrWkGe_deWecmju_tFrQm26x_Q4WuE-RcWMcI,1613 +torch/include/ATen/ops/replication_pad1d_backward_native.h,sha256=B4ooFJNkSZd_Vc4etLR1BFlUQrwkZs8KDS7-Lu0zRxM,986 +torch/include/ATen/ops/replication_pad1d_backward_ops.h,sha256=d1-NdEGEz6UqSHgHD4m9M0RdDYMUUSvXtHIkqVjINQc,2113 +torch/include/ATen/ops/replication_pad1d_compositeexplicitautogradnonfunctional_dispatch.h,sha256=z56ajy0Aga2UO2SPvQlo-jjMX0pt2HW37Par8tp55p0,927 +torch/include/ATen/ops/replication_pad1d_cpu_dispatch.h,sha256=3eaq6-Ei0_egbV6rj6y49GtHamE9cQ0_rZ-15UfwqGs,1337 +torch/include/ATen/ops/replication_pad1d_cuda_dispatch.h,sha256=8dnyHc6crRC3hb5xMtWecXawiQNXvzYYvmoWSwdHQtI,1339 +torch/include/ATen/ops/replication_pad1d_meta.h,sha256=7f11h6a3qt8gIuPb2R30g9A8e-UmaiMw2W7tjfJ5l0c,609 +torch/include/ATen/ops/replication_pad1d_meta_dispatch.h,sha256=6FzzCkI2IXLRKvEHlpJWJ6jiU3JNdQ1kw3nwZuZySL8,1339 +torch/include/ATen/ops/replication_pad1d_native.h,sha256=zLalidv0bKYcObaJEWtob1-jzlNYbA0nYDAqe6upUlQ,863 +torch/include/ATen/ops/replication_pad1d_ops.h,sha256=sXgVxt5dYdolaPJAnAQ7ZAblbOn0Q5HgLnA5nypc7LE,1809 +torch/include/ATen/ops/replication_pad2d.h,sha256=IHYh5DJzRA9oiDrWAVevWDTdVco5k2VVkX5NBCOtsdw,3928 +torch/include/ATen/ops/replication_pad2d_backward.h,sha256=xYmwwDbWmjvJyydq95P_USPcHO1B2UMpIBkjh5tKj28,5091 +torch/include/ATen/ops/replication_pad2d_backward_cpu_dispatch.h,sha256=wzmv9ZtGaU4aaGXboHPSbXSWJ-oDvTXN1o8CIXr7Ps8,1611 +torch/include/ATen/ops/replication_pad2d_backward_cuda_dispatch.h,sha256=oXbBRAtGaUob0EZEhlf0PP-1uaT8Pi6wBOCA9waB7eM,1613 +torch/include/ATen/ops/replication_pad2d_backward_native.h,sha256=sTwYo7ydM2f_71JrskcNTJTlLS0xpIRvRKs84s3w3To,1026 +torch/include/ATen/ops/replication_pad2d_backward_ops.h,sha256=sVoDpqDA4j3JtuAg6_eVQbHd1Ao7avh-aq-mADuuiLc,2113 +torch/include/ATen/ops/replication_pad2d_compositeexplicitautogradnonfunctional_dispatch.h,sha256=sSktEXd7PLFY9LUgT2KHGwtDLCMaQAtz-1mjKZHnIBY,927 +torch/include/ATen/ops/replication_pad2d_cpu_dispatch.h,sha256=pQEZB3EbhRcGZYqCajmWBt2YuOLWTpRCThvRiZpc29w,1337 +torch/include/ATen/ops/replication_pad2d_cuda_dispatch.h,sha256=L3xgS_4AWITvrfLUJdcZxWmZioirL7CQibs9tvY4a78,1339 +torch/include/ATen/ops/replication_pad2d_meta.h,sha256=hSp3_b3I1TPlnd9aupwjGj9nJmG3D5nkMZJo4x-kx9Y,609 +torch/include/ATen/ops/replication_pad2d_meta_dispatch.h,sha256=Ie6ig5u5_MoxPeD-NQt8H8v2ut2-YWOx3H7ZsOeEk4c,1339 +torch/include/ATen/ops/replication_pad2d_native.h,sha256=dAovwCukbjMJpHVwQa8aVZfBGYKvi5rj7iw8J4otFjw,863 +torch/include/ATen/ops/replication_pad2d_ops.h,sha256=68IW8BfpOWwDlmD9pq6Hn-U9DRqnYX-4WFW2Qk_bQMY,1809 +torch/include/ATen/ops/replication_pad3d.h,sha256=lXkgG_F48cYzmQi-xPLEs7sT-09Dq4UL8wWEl548kYY,3928 +torch/include/ATen/ops/replication_pad3d_backward.h,sha256=gW5jO1AyePo9dUFFYVriLQTFJ5JDlsiSKiDDf2UU-Xc,5091 +torch/include/ATen/ops/replication_pad3d_backward_cpu_dispatch.h,sha256=bvIfvjnpNuI_TkU23qn-AbZHVZZib23cm2_xt3ApOsc,1611 +torch/include/ATen/ops/replication_pad3d_backward_cuda_dispatch.h,sha256=TzedmNakDu0AkJ7a77-VfPwdcUqo2xEUdGvELmhNplw,1613 +torch/include/ATen/ops/replication_pad3d_backward_native.h,sha256=Ip7Nkx__0Ze_G5Mp2tPYEhspgVgq6kIzYHZYhasWxO0,1026 +torch/include/ATen/ops/replication_pad3d_backward_ops.h,sha256=r6nB63SURocU5lJV-QMwBzNvYfxxjvl-VBSoEh5_ymE,2113 +torch/include/ATen/ops/replication_pad3d_compositeexplicitautogradnonfunctional_dispatch.h,sha256=Tt8HnZ1OC0jx1MfKp5lUvHk1hWV66M2oX5x7I3ZnLhE,927 +torch/include/ATen/ops/replication_pad3d_cpu_dispatch.h,sha256=HGjUgC8LJ5hKGdIgBS7pQAaOZrzREHXkSpCKpBZJXGw,1337 +torch/include/ATen/ops/replication_pad3d_cuda_dispatch.h,sha256=VXym5ajSji7nIF1jj3H6KV9wf_FD9dPd3pqrZbtCODI,1339 +torch/include/ATen/ops/replication_pad3d_meta.h,sha256=QWeonaDhxOgsjoiGhIDqmWoebnfjWsTB46a3tdFWW-c,609 +torch/include/ATen/ops/replication_pad3d_meta_dispatch.h,sha256=WXwFp4CDgoWvUZudaQl9SYrKZGU9kOTaLDTHVnl0RVk,1339 +torch/include/ATen/ops/replication_pad3d_native.h,sha256=11_vJtErSw11PT0_qbtj3_t2Hv_6rKTFkn6fWPU8jo8,863 +torch/include/ATen/ops/replication_pad3d_ops.h,sha256=E8HOCpAF2zz20DC5wN_K1pBA7Lcd9-PTaMg7G4Gbazk,1809 +torch/include/ATen/ops/requires_grad.h,sha256=w8fACoKduiea3go-9fyi3aHe355u41_qj4YxoHfgunE,510 +torch/include/ATen/ops/requires_grad_compositeimplicitautograd_dispatch.h,sha256=JY3EEmTP0X6TiuKaZCpOJysT1agi5ePx5XIq_3MkpHE,793 +torch/include/ATen/ops/requires_grad_native.h,sha256=ilikXuahYtijf-ploNbk5zPYpD9SyG5p1r0-EdCrD7A,505 +torch/include/ATen/ops/requires_grad_ops.h,sha256=f8goG_4ueL78IJzGR-jEyXLXpzT_9l7Tn9W_7g_41sE,1048 +torch/include/ATen/ops/reshape.h,sha256=brQ66yL4vcoKAjuwZ2iHqEMjBuC-79_VStBaEASDKNE,1406 +torch/include/ATen/ops/reshape_as.h,sha256=XpJUWPTZSuH1AT15Lv0sPgY0ZeTxszH7JrC6qjsH5hc,507 +torch/include/ATen/ops/reshape_as_compositeimplicitautograd_dispatch.h,sha256=tLZKaKQ_EQR_ZMb4h_tmwgqgLDQErSDqS2dEjiqB7Hk,794 +torch/include/ATen/ops/reshape_as_compositeimplicitautogradnestedtensor_dispatch.h,sha256=LZQ44sFn2Y7mmwqFH2GUsHn3oylyRhRT2N7t-7JlUEA,818 +torch/include/ATen/ops/reshape_as_native.h,sha256=PzKEDl8yVxH03sMya3Ib3lNNilf7QquesyLEM0uwye0,597 +torch/include/ATen/ops/reshape_as_ops.h,sha256=3bmQJcFMfyrROcynrvlCjuwHU6TwBKuyye4DU7JF_Oo,1061 +torch/include/ATen/ops/reshape_compositeimplicitautograd_dispatch.h,sha256=EvhmAAjIA54gEVG3l-J8cdGrfHYsaC1AGh2FfV5AB10,877 +torch/include/ATen/ops/reshape_compositeimplicitautogradnestedtensor_dispatch.h,sha256=HigwPxjHEUwWEkIgGmQAH_xX2iYk_03CITv6UIQGKSI,901 +torch/include/ATen/ops/reshape_native.h,sha256=ZeL3cWlBxJkI0nY-vhdCtUPxI4VbPI82nBwe3JFOy2Q,607 +torch/include/ATen/ops/reshape_ops.h,sha256=SlVXeMjBjTemO-bDxd1uwfHbMVqLyhQBiBeW3WnDnek,1057 +torch/include/ATen/ops/resize.h,sha256=X4YkfHDw0o4huDU1X-BrZjwE0uoxdg50oQvvY1MOnXM,5343 +torch/include/ATen/ops/resize_as.h,sha256=DxJWGptcn6Jxxc34n2kgCpuckc47oJLJzyO5vg8RuRo,1959 +torch/include/ATen/ops/resize_as_compositeexplicitautograd_dispatch.h,sha256=IsOtUhQ_aNdtfOzf7EGx6jHhbp_e9Yz-JJQbqaZrXR8,1393 +torch/include/ATen/ops/resize_as_native.h,sha256=2vusdq1AmAqxEJYSeUwptXQcMOzvE3Dq3WmUwEd_MSI,914 +torch/include/ATen/ops/resize_as_ops.h,sha256=1l4TlNNoi8wQm7_KqvHLAWyhkqzf_ZEN6w99CIUqw6I,2973 +torch/include/ATen/ops/resize_as_sparse.h,sha256=6rUCVKjKqZQLTLYpljzjgnHfP6nPxhEOZkVhc8gz8rg,1607 +torch/include/ATen/ops/resize_as_sparse_compositeexplicitautograd_dispatch.h,sha256=QwJHx9R70LFmcTuKjLcFref0C94ZIafpD3UitdUo7bs,1074 +torch/include/ATen/ops/resize_as_sparse_meta_dispatch.h,sha256=1zzp20-5piFKDb5bpBXioZ3nBFFsPNifSErixgvrOhM,774 +torch/include/ATen/ops/resize_as_sparse_native.h,sha256=K8pZqGgeJHbraK_S5EJCbRheyHtw9USyjSNsG5jtA_s,875 +torch/include/ATen/ops/resize_as_sparse_ops.h,sha256=vTwvjNPvT8kankJL18dS1ivX2O6kjTnMIHoNnUV1j14,2529 +torch/include/ATen/ops/resize_compositeexplicitautograd_dispatch.h,sha256=W4ok_4RLyHqNDhsAZPg4tD5V4Qoi_ljukztknwm27U8,1699 +torch/include/ATen/ops/resize_cpu_dispatch.h,sha256=Stxen3YB8yc-BAjHaM75WS3IubKjEXtrEVCRhlZGOmM,975 +torch/include/ATen/ops/resize_cuda_dispatch.h,sha256=g1Hz4XD8OZdBvHy1elIBVPO0T1cwgWhFNIp3sQo3lHM,977 +torch/include/ATen/ops/resize_meta_dispatch.h,sha256=gDi16_RR1gAveIBj09rBdbfsefgOi9Q-EF1oPwDImZ0,977 +torch/include/ATen/ops/resize_native.h,sha256=LQPuK-aUn1wm_tWv4pkZLF3OVZAwvqMpInLKCovW7N8,1531 +torch/include/ATen/ops/resize_ops.h,sha256=ypz8dDzIGqZmbGE3Zvk7UrbADOGXYGeefPjW5ezA-jg,2889 +torch/include/ATen/ops/resolve_conj.h,sha256=mzB8zkqweaava3fj9skeNArqeVcd5cQD5VrFylyTEqQ,667 +torch/include/ATen/ops/resolve_conj_compositeimplicitautograd_dispatch.h,sha256=ZmkYQAlc3Apqab1QViSQ1RMQddMmlr51mmOZTGNrwlQ,770 +torch/include/ATen/ops/resolve_conj_native.h,sha256=cCRQwgHT9nWIUNsXdjW8NHLBofK9RlRwxBXk4melD8o,482 +torch/include/ATen/ops/resolve_conj_ops.h,sha256=qvazoaUXvoNLV-tRDH1KFNsu15lO8uK1Cf7BxDKQcYY,981 +torch/include/ATen/ops/resolve_neg.h,sha256=mtUw5vy_S-kbiYwqmpWlKLStMttrCgSQK_coBh3rZI0,663 +torch/include/ATen/ops/resolve_neg_compositeimplicitautograd_dispatch.h,sha256=11SrCruZeI90vY4Qwnsbj2XCnobYFpqT8xMbscyXtN4,769 +torch/include/ATen/ops/resolve_neg_native.h,sha256=vpn1xVuiRJ_WgU5S07eiV9VjVHhg1ve6bSQtmlQwjtI,481 +torch/include/ATen/ops/resolve_neg_ops.h,sha256=I4lgcSN_dY_tOeILMhprt2NgRZjMgttIdmwsb_BQdeM,978 +torch/include/ATen/ops/result_type.h,sha256=lX1zqvNwnwZ16-CugYxBHFc95BtDcDQLuvUqH6Ouh7E,1447 +torch/include/ATen/ops/result_type_compositeimplicitautograd_dispatch.h,sha256=khmrnKFhxJ1uBKU8OMoPD_OStz5DA0jWhmlVpVRmW3o,1078 +torch/include/ATen/ops/result_type_native.h,sha256=ObJjd_tN-pUod7ccbZyEwelHxeHZRoq26eWH-0By9CY,790 +torch/include/ATen/ops/result_type_ops.h,sha256=9w_gq3e7gBotaNnjY3GLq_hp-sSbUQmsHuNpo5xdwPo,3083 +torch/include/ATen/ops/retain_grad.h,sha256=OpChqe0mzuApKlGBp3jR4MBBNOjVqEKn7bFGbxcMUrE,508 +torch/include/ATen/ops/retain_grad_compositeimplicitautograd_dispatch.h,sha256=w1VMAh6zxvN6UsSRj3UtI-EgSKIZL4YgC0zX4olW7_E,757 +torch/include/ATen/ops/retain_grad_native.h,sha256=3S9KQTYMTl0ZiTObCtedCVwLVz96ZNOVCX5okH4-hx8,469 +torch/include/ATen/ops/retain_grad_ops.h,sha256=M4MX08UOmmkvtTFoUHLJp9vlgpq9BFaemtXr94HfrlA,936 +torch/include/ATen/ops/retains_grad.h,sha256=ss7qo6IL5lyohdX37GyHdbHA7cW6bvs7AYqxgeOqln0,509 +torch/include/ATen/ops/retains_grad_compositeimplicitautograd_dispatch.h,sha256=enUUiMkfZIH4mlTJNtUccCS-MentCylWDuXCVRaw-04,764 +torch/include/ATen/ops/retains_grad_native.h,sha256=Sm27qZ455a6iPbyyt3BewNU-QyVlgtc6wsvQXYqbP6A,476 +torch/include/ATen/ops/retains_grad_ops.h,sha256=i_koc4M6E5J7H90F3E-aIWcOLeadO7gp4yHKeXvpmVc,955 +torch/include/ATen/ops/rms_norm.h,sha256=ySvZRmJjsoXt6u3vcksaGvlsoQZ0m7BiOtuwhKd3rDc,2017 +torch/include/ATen/ops/rms_norm_compositeimplicitautograd_dispatch.h,sha256=1BB83u8_fLDmDg9Ip9tw7RCjgCj6yvTSX5QBLQy0NEo,1085 +torch/include/ATen/ops/rms_norm_native.h,sha256=Da1DlZGxy7r2RvDDKnnJX-EA8x0QQ6gt_jWoSFDNqM4,615 +torch/include/ATen/ops/rms_norm_ops.h,sha256=vwOGM3sMPMpxP_55WnWm9qz381wgv7oxzIZZak74chQ,1336 +torch/include/ATen/ops/rnn_relu.h,sha256=k_kCc4eEgHNm2szcIVX3IlwG3l35V2KvQPpREqFjX-o,1607 +torch/include/ATen/ops/rnn_relu_cell.h,sha256=MAuoxY3tUnillwSzd8FQehC76i5cG2jGbFhDqyZSHyk,934 +torch/include/ATen/ops/rnn_relu_cell_compositeimplicitautograd_dispatch.h,sha256=A8AuWIBfIFArHZqvXS5LD8TBUk9f9iIKoLS3GrnjDU8,935 +torch/include/ATen/ops/rnn_relu_cell_native.h,sha256=LxW_-REgf9U5C97J22IZvQ4pR_7dkXLtWerEzHOLusM,647 +torch/include/ATen/ops/rnn_relu_cell_ops.h,sha256=dHjNMVzK4sBwiHKp_EmbtVISJLK9Vqcf4yc8V1jsdFg,1504 +torch/include/ATen/ops/rnn_relu_compositeimplicitautograd_dispatch.h,sha256=vvp8xf-n-jkfmnmc-H19KrWJtAgSauCP9LG0-svyghI,1185 +torch/include/ATen/ops/rnn_relu_native.h,sha256=prm69DndN_DFG9Wn-fjdWyIibBTPpZkQybN8R7nHGDw,897 +torch/include/ATen/ops/rnn_relu_ops.h,sha256=S-KQbiC-Is3K1ZaAn2q2mSjmO7eDm8ID7yGx-DjX7Hs,2719 +torch/include/ATen/ops/rnn_tanh.h,sha256=0oyWzT6Fa4t2P3nR9RlSsg9ZB8lGQXHy9THx9uJhmnc,1607 +torch/include/ATen/ops/rnn_tanh_cell.h,sha256=ILEckhM391kvDrOz1U_IfJIHKnr6x8NN-a4CfIHYMD4,934 +torch/include/ATen/ops/rnn_tanh_cell_compositeimplicitautograd_dispatch.h,sha256=-XxQdG1L4xmwca0ACRE6-yipEuZg8laCa54GxRFnJhM,935 +torch/include/ATen/ops/rnn_tanh_cell_native.h,sha256=fyoMaZJUI9zgEdcAbPUe2x8DppXy2_uLHgxZWGxMrlM,647 +torch/include/ATen/ops/rnn_tanh_cell_ops.h,sha256=MM-Cf759ppB-tC6COk1AHG_z2OcbF4SfRCWVGhwvtLI,1504 +torch/include/ATen/ops/rnn_tanh_compositeimplicitautograd_dispatch.h,sha256=ZuKpLpwAz0scQRX62m2bFRa7s_Y-JhYpRMrMiZC7fEU,1185 +torch/include/ATen/ops/rnn_tanh_native.h,sha256=oVRzntaGxMank2u4FFfLjdrM5Z_YR-YM5k1g17sh3yI,897 +torch/include/ATen/ops/rnn_tanh_ops.h,sha256=4n5K0wOvpdbcK_NyiA5Cb5I5Rlkuo2cMjNyF_yBetc0,2719 +torch/include/ATen/ops/roll.h,sha256=zEi226Fy3RPTjZMMBVlXXNU6nMFE3KnxMDTZvzj1auY,3951 +torch/include/ATen/ops/roll_compositeexplicitautograd_dispatch.h,sha256=hBh_kkbiLd0M90PjUNrEYzzrjjJvWGihDHqs2kl6Q4Q,1228 +torch/include/ATen/ops/roll_cpu_dispatch.h,sha256=axXRlKC24d29HC0DBLbjdbS34lrRZu-qBSMktf0hkZY,879 +torch/include/ATen/ops/roll_cuda_dispatch.h,sha256=3dpd8y2z6ed-KRLK4ZIbB8waA2n6wzilPdNOzgZij1g,881 +torch/include/ATen/ops/roll_native.h,sha256=ga8SCUcAJgT3ZmRlCDRn0EdzDAv5HABBYY24iKrpiXA,762 +torch/include/ATen/ops/roll_ops.h,sha256=_KKanouHUkfLxRxd83Wq1iINEgLo2Ub0LX_Ai3Wn3HI,1879 +torch/include/ATen/ops/rot90.h,sha256=N1b-fni6HEeACnuIT4VwvGRLPifsHZwOZdhyEk-kZhs,1240 +torch/include/ATen/ops/rot90_compositeexplicitautograd_dispatch.h,sha256=GFYfZF7RcVShlT_17llI56R8iu2nmXXoqIl5Ba6iKYc,1033 +torch/include/ATen/ops/rot90_native.h,sha256=lZAGPdSefVvfdczFGHILaJmd529p75kVAAB3Z3OelF8,626 +torch/include/ATen/ops/rot90_ops.h,sha256=TlqNPwk29PzEv45wbwbkASJ9q6tif7CqU3aanyi8Fh4,1779 +torch/include/ATen/ops/round.h,sha256=o1oSFpUArB7_CUejHjJVowPpBqDlyzDJDJuPK4uzuU8,2057 +torch/include/ATen/ops/round_compositeexplicitautogradnonfunctional_dispatch.h,sha256=0V2NnobNWh0sQL1wHRn2TTH4zA3CTepuaCUn6y97NeM,978 +torch/include/ATen/ops/round_cpu_dispatch.h,sha256=vOTwuJ8VF-z9QHhiAnvAF6Q-XMY3FXZ4qz82uwVcj4g,1254 +torch/include/ATen/ops/round_cuda_dispatch.h,sha256=Cuvv4uKSE5Mp4QG_WozfoKgIJUrsnM3TKH6ybMZz-9o,1256 +torch/include/ATen/ops/round_meta.h,sha256=tF6NRji1ajbwuxvijZoAbxo6NgGafIV4WLiCEjrvadA,702 +torch/include/ATen/ops/round_meta_dispatch.h,sha256=SMBB9WAFYFPk8LF8UOJuWZCWtjDvsJyww0jMRYRSiPU,1256 +torch/include/ATen/ops/round_native.h,sha256=cDBfv_Ed3WcYDF2Ub4ZeqnJVvzTDNy5XS3RTDaGUSBk,1182 +torch/include/ATen/ops/round_ops.h,sha256=IW_GdxIGKbM-ys92PR-1E88nO5ZFXmlla8Oi4IGE_CE,3879 +torch/include/ATen/ops/row_indices.h,sha256=s9PKtMZxV0xOUI-ys-bECuHkh3hdrZtZ4IyLd3GWb5o,508 +torch/include/ATen/ops/row_indices_compositeexplicitautograd_dispatch.h,sha256=XRkeOOlfX0uG6In8T9KWjC1NzlrLAiT44gKXzuYmlIc,769 +torch/include/ATen/ops/row_indices_copy.h,sha256=OCH-ePh2rMMM64kf8VrnwlHZZXxfKrAQlO3BYPWwbZQ,1127 +torch/include/ATen/ops/row_indices_copy_compositeexplicitautograd_dispatch.h,sha256=dG-jU5bOeKZfsSoHa-IfF5QpatQOwe7wi3pV0ue4_7c,887 +torch/include/ATen/ops/row_indices_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=774cIYq8ig8CUXei9RCUIpVv02qOBZIdZH3SPDj6aJc,800 +torch/include/ATen/ops/row_indices_copy_native.h,sha256=govFMfR71N4-uNXkHrtbxZmw1LGoZyfOOu7WMuGYr0U,574 +torch/include/ATen/ops/row_indices_copy_ops.h,sha256=1L6KhhzlxrcVIYOLeDZFBoaOI-lonwOK-PgHcydBrPc,1607 +torch/include/ATen/ops/row_indices_native.h,sha256=aJ9ulwVaASpfF7yJlP3dNTxNyFP2MdgpLBIQItPM_qU,559 +torch/include/ATen/ops/row_indices_ops.h,sha256=lwLR3OJgr46Q5XHTfBBZYOdQWBLzXNPOi7DlV6Xrrbs,978 +torch/include/ATen/ops/row_stack.h,sha256=vQx3IzgDI-T2e5sLXoFHt3MeRCI9GijvYVolHTkbbrU,1078 +torch/include/ATen/ops/row_stack_compositeimplicitautograd_dispatch.h,sha256=-TXconVBv4tdpJS53iuXvkOfIA0caAzUdeWWM9YDCXo,927 +torch/include/ATen/ops/row_stack_native.h,sha256=Ea9aMgqEn_L7WhEwYYr34_i5n_xC13kLQA51DUehvpM,558 +torch/include/ATen/ops/row_stack_ops.h,sha256=6uA6D1PIB3TC19Q0f2KqFskie4rw4lLKPhWRYAsFL3E,1563 +torch/include/ATen/ops/rrelu.h,sha256=WQ20hmJHwj1isLWrYnX0J8N6V14ybfRr1se6q-fYXrc,1354 +torch/include/ATen/ops/rrelu_compositeimplicitautograd_dispatch.h,sha256=sOlNAtPqCLuCdxJiyZj7xjj_Q8ThjXPvbW4Ou7WM8H8,1123 +torch/include/ATen/ops/rrelu_native.h,sha256=riKmqPC41qtSGcT5jKTPs9LfFSl0eUZoKEHLeCpkYoQ,835 +torch/include/ATen/ops/rrelu_ops.h,sha256=9H74C08t4yZyfDQ6r7MDhuaCDuMDL3PYo8RzLystPGQ,2244 +torch/include/ATen/ops/rrelu_with_noise.h,sha256=cGf_UW3YLC02fSYqd5WZADfuN5het1uE3n77nWI_U6E,3178 +torch/include/ATen/ops/rrelu_with_noise_backward.h,sha256=A7MYeZpp-oLigeRtg3xZ6nJas0qFMprYeDG5XICmqbs,2129 +torch/include/ATen/ops/rrelu_with_noise_backward_compositeexplicitautograd_dispatch.h,sha256=uD68SYGhj86gXjXrfyu3f2towXP0Ya01yLDUj_E_gXU,1416 +torch/include/ATen/ops/rrelu_with_noise_backward_native.h,sha256=BUeh4nsvnoLKF_IE5FtA3IDe3mT0SYloj_akb7CKOwA,884 +torch/include/ATen/ops/rrelu_with_noise_backward_ops.h,sha256=4WUnuDlfgTikAxn6pX1N5PpSsx3AB7i5R-D2hmj2014,2625 +torch/include/ATen/ops/rrelu_with_noise_compositeexplicitautograd_dispatch.h,sha256=KSFp0gY-U6qfcyABCkd30xHegFzK5cpOLRjzZeFMsnU,991 +torch/include/ATen/ops/rrelu_with_noise_cpu_dispatch.h,sha256=B8Np9hd4jvYz-vzAB_gxZcVnyR6fitf9Cnkf9KCKaUI,1622 +torch/include/ATen/ops/rrelu_with_noise_cuda_dispatch.h,sha256=-GLbjJ9UeZz97v7eZIGrLhdqL6fWIi-FTJqDVxrtCxg,1624 +torch/include/ATen/ops/rrelu_with_noise_meta_dispatch.h,sha256=ioBi0kQdkszj80dbjBGL0k9qvrGYEHre33XorRgXIyM,904 +torch/include/ATen/ops/rrelu_with_noise_native.h,sha256=9_8jye1rTAbRVMm5H2gP9kWvk_B2Ux1OR_LU-py_1YY,2114 +torch/include/ATen/ops/rrelu_with_noise_ops.h,sha256=qcumdjXmB7BGiPOBV6O97MGfywO6rwB57sIj11MAIO0,4684 +torch/include/ATen/ops/rshift.h,sha256=XQLLxNZFcRl9XxhPreYKToLJjLAKrrVEPSkOx4pF8mk,1990 +torch/include/ATen/ops/rshift_compositeexplicitautograd_dispatch.h,sha256=MWeyIz9AL6i9msqCJ241dd9oz5P-cxSURc4eXaCcdpc,1144 +torch/include/ATen/ops/rshift_cpu_dispatch.h,sha256=j6s6RFQr6206UMOSBz36xXrtNyzTZrI9YMZ5J54-jdY,996 +torch/include/ATen/ops/rshift_cuda_dispatch.h,sha256=Xf3yEHw8BYpQjKTedmLqTrXiUtydmtCNvhRb7Xi2was,998 +torch/include/ATen/ops/rshift_meta_dispatch.h,sha256=yaz-qqIvrJamkQNjxL8_MvNMUqM6-c2TAAL_vjKvG_E,830 +torch/include/ATen/ops/rshift_native.h,sha256=TCGYwJxzwG_lPWSb0qI6_lpAXcVwjLPvtYTGdx1wfRA,982 +torch/include/ATen/ops/rshift_ops.h,sha256=vS4tYiQennZF29BL8qqxU-D8kL0oA8-976bQ5jr5XLQ,4345 +torch/include/ATen/ops/rsqrt.h,sha256=HE9NJwwIL8I3zaTvwFVLOd9LplEBmUT5aoLkpx1kTUk,1156 +torch/include/ATen/ops/rsqrt_compositeexplicitautogradnonfunctional_dispatch.h,sha256=fh3O5dkf6iLgqhP6Ka-pYX46GXAm6TZKasT8Wm7YaV0,839 +torch/include/ATen/ops/rsqrt_cpu_dispatch.h,sha256=ghHcwbzWagekZD7IWJrawN9nnqjLtT8fzj7mkwZXPtU,924 +torch/include/ATen/ops/rsqrt_cuda_dispatch.h,sha256=4cXGR1hqEhhL9F0LCgmL5LTGCVYA8JIUcD-e-kkZfX8,926 +torch/include/ATen/ops/rsqrt_meta.h,sha256=xr-u57akFGB9JIOrjp5BsW5V9k4Hqq70_kBQ5yUuYNU,566 +torch/include/ATen/ops/rsqrt_meta_dispatch.h,sha256=v-B-lt4IrnhGaVcmfiXJDbooi6DBtl7ML7CFulA3rl0,926 +torch/include/ATen/ops/rsqrt_native.h,sha256=wqcI6080AbFPyz5o7ygaaNIHsyhzC-43ynkibbXTGL4,593 +torch/include/ATen/ops/rsqrt_ops.h,sha256=cW1KtZMCtctoafRSykOSR_elEnHX3tuKGBkmmTMKVxo,2037 +torch/include/ATen/ops/rsub.h,sha256=m7pjiW9ThUYMyZyVKxosDuT5ypUDr04q3ndgxTFFw5A,2185 +torch/include/ATen/ops/rsub_compositeexplicitautograd_dispatch.h,sha256=GRZhj4F30Cf_B3pdhBnM88rAgEkKR-psUEn9RUn3Ux0,1334 +torch/include/ATen/ops/rsub_cpu_dispatch.h,sha256=VNc_G6174WTu0bOkSc4fObglbM5NPVZActMqQ4E83j4,772 +torch/include/ATen/ops/rsub_cuda_dispatch.h,sha256=zD99Ts02VA4iOECeHpHlCP3DCOT95NobE0N_HuA_YdE,774 +torch/include/ATen/ops/rsub_native.h,sha256=8q8ESM1DPltVTqJ8Cdzp9hJWJ3mg0ulvH4U1xtMRC4c,904 +torch/include/ATen/ops/rsub_ops.h,sha256=4BjzqQqeN1FTzkhyK8h_Wl-2dBCvj7k682AcaqOcr8Y,3394 +torch/include/ATen/ops/scalar_tensor.h,sha256=MiXPCqOFWNzZvaMHSqCjmSTiKHQXwSt1v8GuI6VLgBs,1734 +torch/include/ATen/ops/scalar_tensor_compositeexplicitautograd_dispatch.h,sha256=HCOV4UhfkXHIt0f0NRE_5T8OsvvbRRHC1RYeLeOIjnI,1166 +torch/include/ATen/ops/scalar_tensor_native.h,sha256=1LIkYhG0LhoVlRc9Q3qxuEkcwDOAzw7d9vaJkHY6HVQ,719 +torch/include/ATen/ops/scalar_tensor_ops.h,sha256=yn_Jj6B83Qh5rP5Fnh-XF4MN0R-GuEEjofuVZQs8LVo,2067 +torch/include/ATen/ops/scaled_dot_product_attention.h,sha256=2wyvIVj38hoU-_oIvZYxCOSpm1rTxBOcnfP-sE502E4,1143 +torch/include/ATen/ops/scaled_dot_product_attention_compositeimplicitautograd_dispatch.h,sha256=2YjeJBWw-tfcOT8DoVhC9J3mkH3MV9dDFJDT3N2uLA8,1000 +torch/include/ATen/ops/scaled_dot_product_attention_native.h,sha256=UYPo0Y6lR1c-ERdYaachCYfG0A8oNpryZyc9kisshpU,712 +torch/include/ATen/ops/scaled_dot_product_attention_ops.h,sha256=HKABySQgMp_iQ4FME7uoZ7kt-AWn1vKrzPbfkRcVWvs,1644 +torch/include/ATen/ops/scatter.h,sha256=OWs6WXTdYgg7RlGoNQDLmN1rC7F7fmAoF_jBn0XINVw,5091 +torch/include/ATen/ops/scatter_add.h,sha256=xaYcifrgbSEmQIyKhLADxfpa4R3jvc_GGjrPIbG1edU,1711 +torch/include/ATen/ops/scatter_add_compositeexplicitautogradnonfunctional_dispatch.h,sha256=m9ZRGQ5-e9ted2n75uGyI8pQyj2r8YvnhZJppfisSQI,977 +torch/include/ATen/ops/scatter_add_compositeimplicitautograd_dispatch.h,sha256=kTHxoN40Eqy0DWatvfY4OnbMZbDD2yzNMeZu4PQ5QQ4,836 +torch/include/ATen/ops/scatter_add_cpu_dispatch.h,sha256=NqBR0dGWODpIP4jns2Zg2AnCe7hAA5yTd9rhi3GuB2o,1200 +torch/include/ATen/ops/scatter_add_cuda_dispatch.h,sha256=NQ_hwDwRJBFMfhsySHaJKXbqR4scjgiJRAxk924FQWk,1202 +torch/include/ATen/ops/scatter_add_meta.h,sha256=LN75dOfcY3C-A_lCMu3fYVekWgKx8DWNw5YXkYxZEaM,635 +torch/include/ATen/ops/scatter_add_meta_dispatch.h,sha256=N4qbckPkeQqmGDmZQKkifoYx3lP0ngyFGI6_LnnVoCY,1202 +torch/include/ATen/ops/scatter_add_native.h,sha256=KoNQBgT6Fpi9nlR4a9jvHC9l9rAsjPEuyHlhutx_v7E,796 +torch/include/ATen/ops/scatter_add_ops.h,sha256=4ockrx85V_N2Wut2meRxhyK-V9ER6jPG7OjTuzzFmhs,3485 +torch/include/ATen/ops/scatter_compositeexplicitautogradnonfunctional_dispatch.h,sha256=oSWi6DoH_X37iafenDTcv72XwWPPONYMrDAWq2aw3cY,1776 +torch/include/ATen/ops/scatter_compositeimplicitautograd_dispatch.h,sha256=-XLZyuVGdwe61bYI2MnKM7SIFi68L9OyUxSXfJ2jWUo,956 +torch/include/ATen/ops/scatter_cpu_dispatch.h,sha256=5QwX_CejBN6WsbaOCFZwe306aR6jItzF1Nx0c5zM7U4,2954 +torch/include/ATen/ops/scatter_cuda_dispatch.h,sha256=Xid702LdxbTSPhBydoFnBULtsdy2rfEjeObx7UmGLtw,2956 +torch/include/ATen/ops/scatter_meta.h,sha256=2crLfDVYdWv810eWlDFJ7nq533RGNwfrN-HgLFxPkZE,1237 +torch/include/ATen/ops/scatter_meta_dispatch.h,sha256=9ztDwGQFf1ZSEdOgBRVd1kZ83TzTIAGx5yUBuUPtkEk,2956 +torch/include/ATen/ops/scatter_native.h,sha256=3Fcet0F1TJVY2vLNpUZsBoHFUWzjddfOtMyJe0ZpSo8,1640 +torch/include/ATen/ops/scatter_ops.h,sha256=h1m3VmF1zQN7oqOWXzHmw8ZQmPfKGv4Y7tWGdiqG2xI,11699 +torch/include/ATen/ops/scatter_reduce.h,sha256=vz-_2QsKoJLemY_zxcLDMech7j351zEgdTN78ya_ahM,1795 +torch/include/ATen/ops/scatter_reduce_compositeexplicitautogradnonfunctional_dispatch.h,sha256=6mqUFSz2AZR6e56S_5WA0j8SinAIg1MmiOi6po4oMp0,1081 +torch/include/ATen/ops/scatter_reduce_cpu_dispatch.h,sha256=fZM15D2sVXCTL4FOeaicUHCwwJg5txm9aWU-Kqe7tL4,1403 +torch/include/ATen/ops/scatter_reduce_cuda_dispatch.h,sha256=MygIO6BKIsw-SHGUOYrAaEr2-QPBxA74J-cekbMkwiQ,1405 +torch/include/ATen/ops/scatter_reduce_meta.h,sha256=mm-A6WkazoBsQwZZJKsxF5pzQNTIoUZufM7VeW79g0M,686 +torch/include/ATen/ops/scatter_reduce_meta_dispatch.h,sha256=5HzJlOg31A6DsoriYqxSjbtyq4x1e8KanRLVJUKgADE,1405 +torch/include/ATen/ops/scatter_reduce_native.h,sha256=B8q_zwH4IZ8V40LXMkypcUbRUHFKO71Tv40eeKMe0wI,731 +torch/include/ATen/ops/scatter_reduce_ops.h,sha256=Y60ALaPwsnoSfkn7RPNip8hnaQWu0yX3qKlR5ZI0cEs,3232 +torch/include/ATen/ops/searchsorted.h,sha256=rTAYXHXtl6pjkBR8B3wBJsEKUyhU72kL33vG4fx7RUw,3664 +torch/include/ATen/ops/searchsorted_cpu_dispatch.h,sha256=qFhm5VwNjvzseKiYJ-YT1oeIlYvyYziM1ximmStArLM,2132 +torch/include/ATen/ops/searchsorted_cuda_dispatch.h,sha256=JCsiyChmMxd4eSi2dGdcf41wlOXn1HKrb-yAf1wu-C8,2134 +torch/include/ATen/ops/searchsorted_native.h,sha256=HjB6ns_jMDtpqThvi9tntzY43xgX6KNuw94jtvzygW0,2338 +torch/include/ATen/ops/searchsorted_ops.h,sha256=z5Kx97_ZMUV71SLOTIEs8_ALVsXzABM0pI0A2Nu7Qe8,4801 +torch/include/ATen/ops/segment_reduce.h,sha256=RI-dpnDEs5ikFiWzAcyM8nuuoW9EiVzFiobmPhPC5wQ,2437 +torch/include/ATen/ops/segment_reduce_compositeexplicitautograd_dispatch.h,sha256=bbbkW5_Em_F7RukgvcbX3f3ncMzQ13Q7rRAz0vhjXig,1379 +torch/include/ATen/ops/segment_reduce_cpu_dispatch.h,sha256=B9WEYbiQJBVYUrlHioM_NRJcLkgt2HwELoAMQUvwrrA,992 +torch/include/ATen/ops/segment_reduce_cuda_dispatch.h,sha256=OfKPs90RFx3oaWs3pKk34ebclIRByWn7ExJWSpI0CYs,994 +torch/include/ATen/ops/segment_reduce_native.h,sha256=lBR6lLHLWT7U-QoV43ylH8dRz-X1HrQ7aRQHxGKv1lU,1073 +torch/include/ATen/ops/segment_reduce_ops.h,sha256=nLUaJyJdcE-hIq20x0npQ7sGvqaJoqbNwE3kHTfbehc,3150 +torch/include/ATen/ops/select.h,sha256=RvuqtI4eq1ngFTWY7bgu_F9gbFKN16XvpY9cdrr7j_U,1646 +torch/include/ATen/ops/select_backward.h,sha256=IX8lEjb678FRYPiDoKU3xS4dj3dTJrmgh3mbZiFyYRo,4832 +torch/include/ATen/ops/select_backward_compositeexplicitautograd_dispatch.h,sha256=aVLl5KjFA6W3K-1jAN7-mxJ99d5ti5seAoejVsSGm_8,1346 +torch/include/ATen/ops/select_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=ywf5WDI1JxlJ9Pq0wRErTcivTjxUoSBbD94yu4o9VSM,1005 +torch/include/ATen/ops/select_backward_native.h,sha256=ml2b9p9mM-79VyVYYFJ0LQXfBxy-tpaGdWxlPl-hqd0,730 +torch/include/ATen/ops/select_backward_ops.h,sha256=kF82eOuzrEDE4ucVQybYhF_ck5lvoaWrwBLih03L0vY,2079 +torch/include/ATen/ops/select_compositeexplicitautograd_dispatch.h,sha256=DnptFiET-zNkqvxgp5MfPtggpQA9Ie3X1YRJRs3x1ok,885 +torch/include/ATen/ops/select_compositeimplicitautograd_dispatch.h,sha256=Qin5wY6OUza9eU_7AQU4s9VuL0IssPn_BY91alwldq8,796 +torch/include/ATen/ops/select_copy.h,sha256=gf2c6SpZoosjc7b9gNNymZD8QOREoyCeoRsFccTinwk,3754 +torch/include/ATen/ops/select_copy_compositeexplicitautograd_dispatch.h,sha256=nShinM7M8_kqNYs-2E1ZskrTLaHPHO-p_h13mLXXvCg,1178 +torch/include/ATen/ops/select_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=H4YZEjo1Pj4YJOmidZe6rzs3v4ct0LEwwtnLix0GCFg,921 +torch/include/ATen/ops/select_copy_native.h,sha256=zxspNdk4wcnxFGCKjmAQdhhk0UWqxVFgeterQD6QSSU,744 +torch/include/ATen/ops/select_copy_ops.h,sha256=TfrblEcHb5Oyj-3SJGBikT-MJrF5FFRVJvUriXuW-j0,1818 +torch/include/ATen/ops/select_native.h,sha256=Spz4eLp9HAV5Zy4VmIhg49KtHZ8drg0RtSApQwC5n7c,783 +torch/include/ATen/ops/select_ops.h,sha256=YLikOH0flZs1314PyFg5geNFkO8tUo3s8q-n7lkjnW0,1722 +torch/include/ATen/ops/select_scatter.h,sha256=2LvKOLhaq5rwnUU5oGNIto4F7llWR43SIPLjb3HQbsE,4195 +torch/include/ATen/ops/select_scatter_compositeexplicitautograd_dispatch.h,sha256=x8ioi3LlnaGyLzLXfhluuyal4yC7T22aCV0zUVdhv4c,1286 +torch/include/ATen/ops/select_scatter_compositeexplicitautogradnonfunctional_dispatch.h,sha256=vhYMKNGEITJ1WVIWUKsB0bl-xduMSyxb89cty7LcAqI,975 +torch/include/ATen/ops/select_scatter_native.h,sha256=0arNBNw7toPpkkz81ECTNYqdZYQPw88fDhRltTpw6kU,696 +torch/include/ATen/ops/select_scatter_ops.h,sha256=t7NeeIlWUychp0ZTPWaS79FGq2ophfvg7XdI56GI3y0,1973 +torch/include/ATen/ops/selu.h,sha256=4F8aXEyWpLb-7bZUWPEL-d2xyONkPbzBYoHueGJLDVo,765 +torch/include/ATen/ops/selu_compositeimplicitautograd_dispatch.h,sha256=feIEgPVppKywSSF1J0UwT2WGtx9Fl8xcApm3uY3F9Y8,811 +torch/include/ATen/ops/selu_native.h,sha256=bkKuqmhOCxmMbLt6YA9ttJmDTLU0XaMUp87xQvNLuvE,523 +torch/include/ATen/ops/selu_ops.h,sha256=2Yue5ECiAR2S7TlH0GvLOlelgzCghcvDI6W_R_Z0gmQ,1444 +torch/include/ATen/ops/set.h,sha256=QhRk4oaQ6qbM5SJvatM2eBXKGZMmrbqWJABiF01x0oE,9210 +torch/include/ATen/ops/set_compositeexplicitautograd_dispatch.h,sha256=uffcQLn5cK01e5EMxZKzJJOtVep08CiB8xPQ4Mw0X48,2470 +torch/include/ATen/ops/set_compositeimplicitautograd_dispatch.h,sha256=qLaAXSCm0WXaxN6kyZted1krq9kQ7NX-zxuY1bJhKiI,1025 +torch/include/ATen/ops/set_cpu_dispatch.h,sha256=IQj-xc2kLWSfgH6xH97EyHVjo60kfnsE18Oe9h5o-MI,1158 +torch/include/ATen/ops/set_cuda_dispatch.h,sha256=obDehO4hPNPO7Mb0lwqxPeEIfBb07PRbc24sq5h_zIU,1160 +torch/include/ATen/ops/set_data.h,sha256=B38rRnQB1oJDGZs7dkqKNOAEFltUM1xaQYxgH6s575g,505 +torch/include/ATen/ops/set_data_compositeimplicitautograd_dispatch.h,sha256=yL2R4AodKWpLR5_gj1kDt-pkBcKkOUmz1XXBMZ4HLMQ,783 +torch/include/ATen/ops/set_data_native.h,sha256=egEEWgSdKOCxxzK_Zvw4Yr8I9pFFrOKZziz4BpvJjpc,495 +torch/include/ATen/ops/set_data_ops.h,sha256=HsblwiiyXBbawqIyxWWsy-xmFlawAXc71AIrO7HzCWs,1022 +torch/include/ATen/ops/set_meta_dispatch.h,sha256=k-R5X55oU6GtG0kR_HkHtakdSMWGBNUrP3PIegVB-80,1160 +torch/include/ATen/ops/set_native.h,sha256=skc7dEQ_fyLsXyLVoGdj79UbZm2bEIlTWWH1E5Z36ms,2414 +torch/include/ATen/ops/set_ops.h,sha256=D7t-INbF2niFQM09nlSqaixoQSeOBORDTrn1O0uUMEc,9684 +torch/include/ATen/ops/sgn.h,sha256=qVwpO1xe1qj6vtbMEjb53fis88zcEP2t41AzrvN_FoM,997 +torch/include/ATen/ops/sgn_compositeexplicitautogradnonfunctional_dispatch.h,sha256=5LZTvBGItXSSEPoj5He0vvDPrxfS51-kWkyZ6o9PjuI,835 +torch/include/ATen/ops/sgn_cpu_dispatch.h,sha256=N8oA9WbFOLV034Ofk9Y_lEoKo8EL_E1gNd6z37s1DH0,916 +torch/include/ATen/ops/sgn_cuda_dispatch.h,sha256=gEHpo5mz-1MJApDpovu2PBGyFHkKgnUGcC1GVKiyPU0,918 +torch/include/ATen/ops/sgn_meta.h,sha256=eXXTzAe3E1grmR38lqduXZzWt5-tIblOs-jksCPTnxs,564 +torch/include/ATen/ops/sgn_meta_dispatch.h,sha256=8yfX_c-0Sgq8Y_pRhnUy4PizSGbyX-pQMdajpHcDrNw,918 +torch/include/ATen/ops/sgn_native.h,sha256=Lyl-3VYpHUzzPJtGCXEilHNwCJWkisXCgz4a6eUUYLY,1114 +torch/include/ATen/ops/sgn_ops.h,sha256=3E0LlvxOL7jl9VNAqE0ibyP2ykPh-vuq_5gT5x7cGIk,2019 +torch/include/ATen/ops/sigmoid.h,sha256=K9jBcG793LzC-0erfDpCiRBdAMJxvcePV5lgIrlohMM,1182 +torch/include/ATen/ops/sigmoid_backward.h,sha256=4sXsIScOM7VbBjZQVLbDY1fnWagG2tB3qalLbdnWMhQ,1410 +torch/include/ATen/ops/sigmoid_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=KeXoyaLReJobC_434lczR2HcHZ-hbB5pAywmeclxVRg,834 +torch/include/ATen/ops/sigmoid_backward_cpu_dispatch.h,sha256=Z1EoGWZ4eiOSy7lPpI_5qwyzWyFqxY9wC-35gcOLfDo,1023 +torch/include/ATen/ops/sigmoid_backward_cuda_dispatch.h,sha256=6ZRfEflpXl5fpK-du2JcRoDGKNJjB-LydnnY6mq6OXI,1025 +torch/include/ATen/ops/sigmoid_backward_meta.h,sha256=-PG2VF8EJABiJwBtmQ9j-0tTsrHPfVPI88HXq0P2ARc,611 +torch/include/ATen/ops/sigmoid_backward_meta_dispatch.h,sha256=S4rELKVhE_1DTVGnOsDDb_8rwjqu0gHQwaJQyOuOLHw,1025 +torch/include/ATen/ops/sigmoid_backward_native.h,sha256=zWfWt4YdQWcMwL8foeG0VndAO4MHby97tlHHksPgrws,667 +torch/include/ATen/ops/sigmoid_backward_ops.h,sha256=vJJCDP9KrGJiLh5foZ_i-P7gx_x5VLci5Zt6Mx2Pxgc,1869 +torch/include/ATen/ops/sigmoid_compositeexplicitautogradnonfunctional_dispatch.h,sha256=PgSe5Gt31ah8_osPFrHYOww8BvxGX1FAB-T6z4sIlSo,843 +torch/include/ATen/ops/sigmoid_cpu_dispatch.h,sha256=9BqLnt0EV_KwajFVFxF7kDs_-hM6m2r-I-s6TA5mIYQ,932 +torch/include/ATen/ops/sigmoid_cuda_dispatch.h,sha256=QVopEHCkwY_JxiodcFYJU0G5YY7ClWLQK0oroxRrFO8,934 +torch/include/ATen/ops/sigmoid_meta.h,sha256=_v3V-2HBEPxjSgVjYD-f-ILhRtpUgCwPfHasT5s_wu8,568 +torch/include/ATen/ops/sigmoid_meta_dispatch.h,sha256=tHMuVNnesOCzw4LWcETtkRJUayW-Y2Mt3UMxT5SgayY,934 +torch/include/ATen/ops/sigmoid_native.h,sha256=zqsO6FoepXs8KVtH6e3t3NQH5XR4ciToAV_9yQ17PBs,789 +torch/include/ATen/ops/sigmoid_ops.h,sha256=WdSV9phw4l8Y_yQ7cwffV9T1CBIoyQlzfsOf8_x8YcQ,2055 +torch/include/ATen/ops/sign.h,sha256=x0kPHgC2Rj-qgbS2Jlos3cFJBdirhuo3hOCrGe0Ky80,1007 +torch/include/ATen/ops/sign_compositeexplicitautogradnonfunctional_dispatch.h,sha256=3sLCp0R6yinNMOZNmVu3TVD2Kz72SerkQDIQaN_rhLA,837 +torch/include/ATen/ops/sign_cpu_dispatch.h,sha256=dRjDYMA27wsP2uva7J-Kgdy5UvukNUp6IfQthimzXeE,920 +torch/include/ATen/ops/sign_cuda_dispatch.h,sha256=pqJj4CvmdlTDW6JNChQyrKMfUpUrFIobRDu3_I-X5i8,922 +torch/include/ATen/ops/sign_meta.h,sha256=mzlEnfZKL2y3L88J6PqPhHCtXSN-VLmdunBhWhXmXyw,565 +torch/include/ATen/ops/sign_meta_dispatch.h,sha256=zTgSWNx26W0SqO2nyrie1VW5-I62rLQKwHVk85ImOnI,922 +torch/include/ATen/ops/sign_native.h,sha256=y_XI_ArCQP-L0sTnaml34ipbsIQNMzQyDz0H_KhhlLM,998 +torch/include/ATen/ops/sign_ops.h,sha256=4b8TQNylif01ovtUnI8YhZIqBzWVW_iAyFqvWREAFo8,2028 +torch/include/ATen/ops/signbit.h,sha256=8u-tX-50_C71c2FFuqlQAAJQGaNrHwwBXlUdvf_HNyE,1037 +torch/include/ATen/ops/signbit_compositeexplicitautogradnonfunctional_dispatch.h,sha256=LKAm5dgD5A2eFGCFY4CYUTIl5OTWUm5om6_g5-oBr_4,791 +torch/include/ATen/ops/signbit_cpu_dispatch.h,sha256=rP9ipQWOjXsjCT1FWW15McZXhlElRK-CO_f0m4bDxYI,880 +torch/include/ATen/ops/signbit_cuda_dispatch.h,sha256=FcRfgPia_yIEHt6_4uzob0Cb5Lrew0BvKThRrXioHKU,882 +torch/include/ATen/ops/signbit_meta.h,sha256=EzpyvySQYeWeXYSyIhKERqdSL1TOFeuTBwQZo3FS7yM,568 +torch/include/ATen/ops/signbit_meta_dispatch.h,sha256=3TpQ5vHZrP7rtq8vi0ePFzQv_3gu8unuISKA9-JT8ac,882 +torch/include/ATen/ops/signbit_native.h,sha256=IlHrBXQHNotonA5cOTs7hgk0ercD7eILFxz5lxJhbQ8,903 +torch/include/ATen/ops/signbit_ops.h,sha256=IQU5cbRF0AXLbXBiSl1bSsnF0I3TtqbaTRIiobUxwPY,1553 +torch/include/ATen/ops/silu.h,sha256=6iWpdKXnPW0dqQiP417nCuoNhPBu0yW0EwGqSpLuqxk,1143 +torch/include/ATen/ops/silu_backward.h,sha256=MDAcuYc0ZOZGKiMQNIiSOH_VQq7qpcW8_shuZkw2p0Y,1362 +torch/include/ATen/ops/silu_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=8YaGB-R4mVUWiIqPoByZJbvej048UGQjTm_O8uBNCeY,829 +torch/include/ATen/ops/silu_backward_compositeimplicitautograd_dispatch.h,sha256=sQm5odhJYhYcckBGh6uwZ68ih5HjuNIIcfDJEQfFfCM,803 +torch/include/ATen/ops/silu_backward_cpu_dispatch.h,sha256=EG_bVXsVJLRVkSi9rLsFGSyu0Tk5y3d79jYiJirYAEI,1008 +torch/include/ATen/ops/silu_backward_cuda_dispatch.h,sha256=JhWuMQ6oEORadSG48Zy1zi_KaNEuhbfYeAtnG5s91Lc,1010 +torch/include/ATen/ops/silu_backward_meta.h,sha256=9CF1FOk8A9_S5Bbyh_W6mVZmWf8XteH7yLx7WAArnqU,606 +torch/include/ATen/ops/silu_backward_meta_dispatch.h,sha256=mVbl8ejUfC8rCxiW1E7c7S663PjTZUBADWI5m-3D_LA,1010 +torch/include/ATen/ops/silu_backward_native.h,sha256=eEHVrZbqD-0woJ8n7tIY749zqUekOgtEGdc2kjJgULY,854 +torch/include/ATen/ops/silu_backward_ops.h,sha256=JqJifWgy7hbhvBtJMuZ0F6p-kcMAa3V0BCA7QBHuS4c,1839 +torch/include/ATen/ops/silu_compositeexplicitautogradnonfunctional_dispatch.h,sha256=H7Lya-LmVuzj9XLj49GvmeR9pPgteBXanYLuB4TjXB8,837 +torch/include/ATen/ops/silu_cpu_dispatch.h,sha256=VW-LKzanvEolYrQajqECOJSHGJXlc7jhXv3Qc7vDo4Q,920 +torch/include/ATen/ops/silu_cuda_dispatch.h,sha256=xkK0nHdqbsDqgHS3WeyQdxyTOy295H73P6t_RQ8abuM,922 +torch/include/ATen/ops/silu_meta.h,sha256=WJX-jQjjjIBWWlJMmOpWlHKapqsLksOhzJX04mesSuw,565 +torch/include/ATen/ops/silu_meta_dispatch.h,sha256=_RFEFf5hb9lvl-zAckvaGyo213ylhEURqRJSZQgrLUg,922 +torch/include/ATen/ops/silu_native.h,sha256=bT9LZvqxlMN2Mk3h9_cr5pZsCXFBS5z1a3OXUMHvCFw,717 +torch/include/ATen/ops/silu_ops.h,sha256=EJfOZnjsgmfhavFUcZZ8zSedIXqvhNfN-PWH9ov-NeU,2028 +torch/include/ATen/ops/sin.h,sha256=h2H9Pwvbh3pGrtCwZ8X--neq4aDwuSmkWnYYsRdrL9g,1130 +torch/include/ATen/ops/sin_compositeexplicitautogradnonfunctional_dispatch.h,sha256=oK8texD6_Mm0uHJLY80EPD2e4B2awH0--EUWVnzkKw8,835 +torch/include/ATen/ops/sin_cpu_dispatch.h,sha256=N2sGALxM13cOEMx_Xw0Z5QpZSz3Z0QB1yv7EF95v5IA,916 +torch/include/ATen/ops/sin_cuda_dispatch.h,sha256=AY7K9iIlZ_AWmzB3s8bQes1AtzQ-Y0J6i8qX1sFlzC4,918 +torch/include/ATen/ops/sin_meta.h,sha256=0D7s1M86gYBpg0PuMnhcW3GuHQgwTk4msEwGqPNgjcM,564 +torch/include/ATen/ops/sin_meta_dispatch.h,sha256=ZXhNfI5oQuNj52zkysQ-oSrQDrUFP-c-_GIv1ZNN8v8,918 +torch/include/ATen/ops/sin_native.h,sha256=unvty-5y4KNnh4QatI4Qo0qEPr8ugomGxq8lV8-jCec,1053 +torch/include/ATen/ops/sin_ops.h,sha256=6Aw51ZGS7g4tS3G7bOLnAhjY_wO4VyK9ZTMp2a5uiUM,2019 +torch/include/ATen/ops/sinc.h,sha256=jzwZzYsY3sUgoGf7tMmr2IW3fiitFubqroUuE4HnCWQ,1143 +torch/include/ATen/ops/sinc_compositeexplicitautogradnonfunctional_dispatch.h,sha256=EKtHwrscBiEEOnQ1JHHKfTLAH3L0B8F6ALt0WtZqHDQ,837 +torch/include/ATen/ops/sinc_cpu_dispatch.h,sha256=GOS-7yL0DplD2PgWP2CYkwAHuTH1-VvXL5P4HcPu7Lg,920 +torch/include/ATen/ops/sinc_cuda_dispatch.h,sha256=BIV7_Q8rLDcOjtnQjPHdi6q_vtsmByrHmOMUi4olkB4,922 +torch/include/ATen/ops/sinc_meta.h,sha256=YBAEb0j32oKjfdqwTSX6OxS8nKzhGypR3-gjEvmvr2c,565 +torch/include/ATen/ops/sinc_meta_dispatch.h,sha256=oCzuPzKN9t28M61qlSg8r-8NDD3EQBpEHCXi4dMAHC4,922 +torch/include/ATen/ops/sinc_native.h,sha256=ObXGiGBpyuMR18NKqDQFIrbHavNWj9wKze4_PXyDuOA,590 +torch/include/ATen/ops/sinc_ops.h,sha256=0uKtRwvojKdmrGZz7NDS-hWiMdsJQVS4OhKqNAk6KnI,2028 +torch/include/ATen/ops/sinh.h,sha256=CaC2e5_mDF65RdHOwkl2YEkTMpJiKqrFlOQRKfNSnFY,1143 +torch/include/ATen/ops/sinh_compositeexplicitautogradnonfunctional_dispatch.h,sha256=Dn9LTkJi_S3ATtf0n85BJ8pNIzMktISmGDnvH8e_WXE,837 +torch/include/ATen/ops/sinh_cpu_dispatch.h,sha256=2XfrxA2i-RFpcoXYAgiDSDF-T2jKMKSn1Re9x-tEZVE,920 +torch/include/ATen/ops/sinh_cuda_dispatch.h,sha256=YTqiAu91XPCxq3bi-2QU5WAJXMOAIJ8XoNE8_Nmsf6c,922 +torch/include/ATen/ops/sinh_meta.h,sha256=DgH940y7fy_zFCQJYYPVAmPTiIYtYRAtW5oCeNzvUYk,565 +torch/include/ATen/ops/sinh_meta_dispatch.h,sha256=indTCakEIpKc7xjFjNcUu4CkXNSYnkjFlk0JkzsoNxU,922 +torch/include/ATen/ops/sinh_native.h,sha256=IK8iwEPi-wWAt42BTvVFoti0XIZZ8DeP_wnt7Tis9Hg,998 +torch/include/ATen/ops/sinh_ops.h,sha256=JBU19Zkp1cgKBUoASLzrVQoLVUDuAITXuI2iy3BUCJg,2028 +torch/include/ATen/ops/size.h,sha256=tVQIlfL4cUeU-KXtJXyHQ_z1vfXzVzdUsHbHuPDlltw,843 +torch/include/ATen/ops/size_compositeimplicitautograd_dispatch.h,sha256=lfBzXTWLGOAo9DfG-KPVaOTcT7h5ydQWZqIo3W8vF3k,838 +torch/include/ATen/ops/size_native.h,sha256=yLSilFYn6AYnZrpZ6LHiZ77tTtnK8NhOGmf1DMAeg9Q,550 +torch/include/ATen/ops/size_ops.h,sha256=TDw2uNQJtpGbVomrRSIXQWLeNhkt-pFlyPBeZUNetXE,1559 +torch/include/ATen/ops/slice.h,sha256=sqvbG9ITcpkHGprLlSNEO7eA-JZkGYBvpRz44iD2xwY,2238 +torch/include/ATen/ops/slice_backward.h,sha256=NoprrcF3J1gIidgFb2H3J1kZ_F9cJYBuzJmaO6L4FbY,5455 +torch/include/ATen/ops/slice_backward_compositeexplicitautograd_dispatch.h,sha256=bxgVa7Z0R7s3TzNHNwGTITuhC7ZFeD5qhSlLIj0hrFo,1795 +torch/include/ATen/ops/slice_backward_native.h,sha256=2prhNcuuQQ0qDrtFQ_ZUerzHvb8d52IYOueu6oGc580,775 +torch/include/ATen/ops/slice_backward_ops.h,sha256=xUAUmo0nxQt5DCr3w8_6ZGclxt09yXGgy25ssMGC604,2315 +torch/include/ATen/ops/slice_compositeexplicitautograd_dispatch.h,sha256=qEZmTTwQGrl5d8V6_v3EWHXq_TfFYzvcsVnIFvvsrVw,1081 +torch/include/ATen/ops/slice_copy.h,sha256=dB7zhnKm1c_xdRGBDB81ZVef5cGyTk95De-LyXy3WIY,6071 +torch/include/ATen/ops/slice_copy_compositeexplicitautograd_dispatch.h,sha256=wL23zOtj7Zy_EoC8c6sORGc0lPtJNMN7X2DL_QR5UzI,1502 +torch/include/ATen/ops/slice_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=As5hCaP0NH39_bSDijVs9jlmpKP0DFV0c9-Tk_QrVAc,1117 +torch/include/ATen/ops/slice_copy_native.h,sha256=KEbvWcvGaZZvzSQuG3nLFvdB6pEe5_SXzYbAixOOEug,826 +torch/include/ATen/ops/slice_copy_ops.h,sha256=n_qONL07mwmDl6NpijqfrfQudwue8ZD27JB4G53OjPM,2308 +torch/include/ATen/ops/slice_inverse.h,sha256=NalRgaWq79IEsYNMU_DvxDsxFjS5nS98CRZQMwZtxBQ,2424 +torch/include/ATen/ops/slice_inverse_compositeexplicitautograd_dispatch.h,sha256=IS_6lCeEdZ3bvMmiaoOJQC4Yte0oWmdu1dgb437dv-4,1145 +torch/include/ATen/ops/slice_inverse_native.h,sha256=TxuQpQ9wVT95HA54kLVPBnWkkQW5nivHmtuKJBvboA4,649 +torch/include/ATen/ops/slice_inverse_ops.h,sha256=fLHmqvikULQAXpIv8MSisRqt97WC1Hm6E5X0ak1pdBU,1412 +torch/include/ATen/ops/slice_native.h,sha256=6P61MTQTDyZq3egJYtKxhTEXIT3E0YKULtT3Ins9tSk,598 +torch/include/ATen/ops/slice_ops.h,sha256=CWJvLlM3XkvNQ7oK2W5z0k758DWak1AsKrpcSx58cF4,1328 +torch/include/ATen/ops/slice_scatter.h,sha256=Dqsuitfp20BzPRT5k1rluZZh-nGy5KVNBYr3L1iPWng,6458 +torch/include/ATen/ops/slice_scatter_compositeexplicitautograd_dispatch.h,sha256=opvWloVfs25y2-IEy5914V5zPp8xVOat2xh3bSYnYEs,1610 +torch/include/ATen/ops/slice_scatter_compositeexplicitautogradnonfunctional_dispatch.h,sha256=_xbk7oUkaSgcVBjqL9mpc5TrtC9-WShncg4EKzFS8dI,1171 +torch/include/ATen/ops/slice_scatter_native.h,sha256=2Qs7IuRAwsJhxkXD7y0FtFfXEHCi5dWj0rLLuZ9vkjA,847 +torch/include/ATen/ops/slice_scatter_ops.h,sha256=KUyKTRckXdg1tZJjafnmQuQsnBSAkOK1G0pSfNiEeyw,2445 +torch/include/ATen/ops/slogdet.h,sha256=XaN6nvH1eDUqFlzUcnOP8icqQzK6_7wHT-pK_uCFjO8,1319 +torch/include/ATen/ops/slogdet_compositeimplicitautograd_dispatch.h,sha256=dFmaIWSJbfcGLPpGVKU0-g4__WSEsC_xANNcSTF_EiQ,1053 +torch/include/ATen/ops/slogdet_native.h,sha256=CjBCX9j3gECtn9fAfkF3AiVS5bkNRqivxgRQooFNVZE,633 +torch/include/ATen/ops/slogdet_ops.h,sha256=pb3XwzQyWGfp3lHchnegJhPR6OsVTwwgHoXGHNA9wBE,1850 +torch/include/ATen/ops/slow_conv3d.h,sha256=f_ls6Z-YnpSP78nYOlGbuY5z_ZnOqXuO1TA0yoga4ns,6620 +torch/include/ATen/ops/slow_conv3d_compositeimplicitautograd_dispatch.h,sha256=7O0Fzb09NgMJ5aq2oM1R4XvA_iQTMqNVgDP7CzhUZGA,2181 +torch/include/ATen/ops/slow_conv3d_forward.h,sha256=U-yBFsodSZEP9Psmvjugcz2iLB8FfGgcR2IZYz8j0HM,6750 +torch/include/ATen/ops/slow_conv3d_forward_cpu_dispatch.h,sha256=-pVDQFwAvBup5Au-mePSOcsVMRSaImtS3quv7eLVguo,2117 +torch/include/ATen/ops/slow_conv3d_forward_native.h,sha256=6qRkSeKTF9NsmxGDhOfVitl6j81YV6ycsx0wPywjTmg,885 +torch/include/ATen/ops/slow_conv3d_forward_ops.h,sha256=lKPCF3cKLQQ4Di8KdtKUI8a5NszLF0ZrekMyKeIt9Rs,2697 +torch/include/ATen/ops/slow_conv3d_native.h,sha256=ooMknHxNMthlF4FoOVDa8BBraEPaWmIZVgIbCM8oii4,865 +torch/include/ATen/ops/slow_conv3d_ops.h,sha256=04f-YfGq3ZOg_Z3m1nb0HceO02F3rqjEd2Py7Eald0o,2649 +torch/include/ATen/ops/slow_conv_dilated2d.h,sha256=eDwj6tjJY1E_np0OxVt0QyDHuaXycmKN63J29diUyf4,7680 +torch/include/ATen/ops/slow_conv_dilated2d_compositeexplicitautograd_dispatch.h,sha256=-QSUHEpVTBzlXJ_k7FOHIZcNyuGgPteulZaECxbpDhQ,1871 +torch/include/ATen/ops/slow_conv_dilated2d_cpu_dispatch.h,sha256=dpfndg4BbIsgdRIdvqpXXoaRj_QZo3m712UC4tONQyI,1226 +torch/include/ATen/ops/slow_conv_dilated2d_cuda_dispatch.h,sha256=rOPEWBodJ9Xo3RmmiUkopO5t9rah15aWZ2UanHdh4GE,1228 +torch/include/ATen/ops/slow_conv_dilated2d_native.h,sha256=pFQs17MDWYRff5i7NSS5xU91EKkjZIcbEKc-tSDAosw,1216 +torch/include/ATen/ops/slow_conv_dilated2d_ops.h,sha256=KspXHv71prsSruEXXQaoyeDM8JQeeWPqqnlv7-gJxbQ,2903 +torch/include/ATen/ops/slow_conv_dilated3d.h,sha256=VzmyM7lMvGCyjZSejW3V_3FOxe5uClKnIS68vBUNDTQ,7680 +torch/include/ATen/ops/slow_conv_dilated3d_compositeexplicitautograd_dispatch.h,sha256=WZjAaiB594W1eQIeeaxvg0KbInaxgiNVc98VA67Kdj0,1871 +torch/include/ATen/ops/slow_conv_dilated3d_cpu_dispatch.h,sha256=9z7FJQAbVoWHrI_mLsz32t4QdKpHKImJam-Ccg5juh4,1226 +torch/include/ATen/ops/slow_conv_dilated3d_cuda_dispatch.h,sha256=Cbn_WElP-1ONK1EOSXwbOe87yxzKUsgmaVBeEoYMUaY,1228 +torch/include/ATen/ops/slow_conv_dilated3d_native.h,sha256=sZlmR_DM-P4y6BpCpQsJmfZXbo2pxJGl8hpEgT74iAg,1216 +torch/include/ATen/ops/slow_conv_dilated3d_ops.h,sha256=e5AiLfodFvzJ8L-Q753i1B43-CeYVUqeTa3Nc9Q6BUE,2903 +torch/include/ATen/ops/slow_conv_transpose2d.h,sha256=G6geuq8N6MFS9cVusoFENGrIoQSMoeGw83KqbwrNtY8,8734 +torch/include/ATen/ops/slow_conv_transpose2d_compositeexplicitautogradnonfunctional_dispatch.h,sha256=GK3W0OlYjXHeW_hwmjrk34AO7ep2NQcGSU9dc17ih-M,1385 +torch/include/ATen/ops/slow_conv_transpose2d_cpu_dispatch.h,sha256=uLvd2Gvfsn73qYdBLtAxg7m7L7-rnuB9daaVQVsfdqg,2637 +torch/include/ATen/ops/slow_conv_transpose2d_cuda_dispatch.h,sha256=bBNXMzOMfJiPaVCedEEzOv9LTom5Rurvlkl3MwnSIlI,2639 +torch/include/ATen/ops/slow_conv_transpose2d_meta.h,sha256=zXEVTSYudQ0i-EchBJPlt-auGjdK_al7JweE3_1uYtE,803 +torch/include/ATen/ops/slow_conv_transpose2d_meta_dispatch.h,sha256=rjqWqnzvwC37KGAia-HEFW5PnaWg9NlIfVm3J_zABOc,2639 +torch/include/ATen/ops/slow_conv_transpose2d_native.h,sha256=Q6o9HNHKCfydLYZjQ0Sn0X5ehlWT-xj_j-WxUHIxvXs,1277 +torch/include/ATen/ops/slow_conv_transpose2d_ops.h,sha256=MqWTeXRtYjNvXI9xvbUmXcK7do3IeNl-RE5frr1g2J0,3157 +torch/include/ATen/ops/slow_conv_transpose3d.h,sha256=4RMXntBOTMtIa5OS9CYsNJcCZuP7yBbOHPTemMOsNQA,8734 +torch/include/ATen/ops/slow_conv_transpose3d_cpu_dispatch.h,sha256=Orvj9yEdQbQHhXL8BZ2kY7ss7X472XVQG6Qb9f2j55w,2637 +torch/include/ATen/ops/slow_conv_transpose3d_cuda_dispatch.h,sha256=guQa-yX5FpxM1jUYgTwJ04viR_DPdn5BbJpiOxW3J98,2639 +torch/include/ATen/ops/slow_conv_transpose3d_native.h,sha256=n0FGRL1wc1okAaTaOU9l8E6PzBiHQ3JA7t2o5Nb2wwg,1606 +torch/include/ATen/ops/slow_conv_transpose3d_ops.h,sha256=lfCV8dMw9vJu5iVAZggQzOJOb6Z6RvUHMMQStSvzOQM,3157 +torch/include/ATen/ops/smm.h,sha256=ZdzjFSuPx-iOGVIHNNV6Is1aFj4aXrUxWChoMpO2yhU,669 +torch/include/ATen/ops/smm_compositeimplicitautograd_dispatch.h,sha256=zh39RzyV2CfnuPVHLm9WN2xAy1HIvmcNqnC7MHe5zFI,786 +torch/include/ATen/ops/smm_native.h,sha256=TZ42SA_aEpHx6GIw2WiWQxvnwPYGVxe_A0WwHVw9rWQ,498 +torch/include/ATen/ops/smm_ops.h,sha256=8FE_7yvQTFoIMDPnYJV4C3V2uncbwXwbY0KU0Y5gcwM,1031 +torch/include/ATen/ops/smooth_l1_loss.h,sha256=O8feEpzW33G250BN270ZMNo1DbOc-r_4NSQ-WdXa2Ho,1560 +torch/include/ATen/ops/smooth_l1_loss_backward.h,sha256=jzP6smUIrLeDihgvkElPUAjwIR9BFBrSS8WOKb7P8GA,1840 +torch/include/ATen/ops/smooth_l1_loss_backward_compositeexplicitautograd_dispatch.h,sha256=vsKgPteWSvoSkOolydVw8wIQi628eQK6CMAGa6Hfqw0,872 +torch/include/ATen/ops/smooth_l1_loss_backward_cpu_dispatch.h,sha256=L1-VS0yBMrCTZThtrofIh7OYl39FeV_fkTRBHRF8mu0,1053 +torch/include/ATen/ops/smooth_l1_loss_backward_cuda_dispatch.h,sha256=S3dEktFSk5jv-cVvrpmgNY-7Tv2Hq0TSpe4upvr_u5w,1055 +torch/include/ATen/ops/smooth_l1_loss_backward_native.h,sha256=aI4XEFJjOdcs7dUxaiSA5sWpNSCb5iYUxl8k1kmXx3s,777 +torch/include/ATen/ops/smooth_l1_loss_backward_ops.h,sha256=03qa8HQAPabvQe-65x9xeZMKbA7C5q5b0nGtigStZsk,2293 +torch/include/ATen/ops/smooth_l1_loss_compositeexplicitautogradnonfunctional_dispatch.h,sha256=QFDGg5V9bzZq5wAUY91lhZKYddS5-b-KTRFyH-UraU8,881 +torch/include/ATen/ops/smooth_l1_loss_cpu_dispatch.h,sha256=DdRIZPNl3E3o0ZyHWhe6h4fJRxcoMPF7XYN1KciQEN8,1126 +torch/include/ATen/ops/smooth_l1_loss_cuda_dispatch.h,sha256=D1DeBN2maEeNWynQLkZLW3TJ4YZePPtgrcdTY1uIoBI,1128 +torch/include/ATen/ops/smooth_l1_loss_meta.h,sha256=DZs8PQ93Hlvf47wUpE8lqjoDwAjuC4L3oLNPBq9CFvw,634 +torch/include/ATen/ops/smooth_l1_loss_meta_dispatch.h,sha256=OK6iuMA73rROWLuzfECUCATHlYFCpFtyw2EV_FsBgeI,1128 +torch/include/ATen/ops/smooth_l1_loss_native.h,sha256=Zpx9bANA9ochEHLS-TYSUu3fovKFqcDt9p9VHVG6wOM,679 +torch/include/ATen/ops/smooth_l1_loss_ops.h,sha256=5mvj8N7aQvX8VNwp9GkfahDcutVuzKCfppwLS9pteYc,2007 +torch/include/ATen/ops/soft_margin_loss.h,sha256=r4p66q21cnuhvTiIF-mvM41MhgT9DUBedyFhDiNMQfQ,1467 +torch/include/ATen/ops/soft_margin_loss_backward.h,sha256=vnmvz8HexrZBB2m1vyn6f1f-rimE-eKbAthldnwGjPc,1767 +torch/include/ATen/ops/soft_margin_loss_backward_compositeexplicitautograd_dispatch.h,sha256=mKz_T200YoVXHwVZm1EYe4W-Ggr1JntbpqZryBzN950,1226 +torch/include/ATen/ops/soft_margin_loss_backward_native.h,sha256=uN8A-lEVKbaW85dwvx_mtM5aBQNtOzzWSEfaAqULtEg,755 +torch/include/ATen/ops/soft_margin_loss_backward_ops.h,sha256=QekI9WxOaU_5D4bIYYIjlhaSPl3BMkwbWEhq4ZOIlmc,2213 +torch/include/ATen/ops/soft_margin_loss_compositeexplicitautograd_dispatch.h,sha256=IWQFxzXH4lpRClv65qSSE1EK0XZEPBV2QNpAxW7jZLw,1129 +torch/include/ATen/ops/soft_margin_loss_native.h,sha256=yEKreHilW5pIOEgargiwU82KpvvOkhSkcb0fqU2iMRQ,686 +torch/include/ATen/ops/soft_margin_loss_ops.h,sha256=-NdIQIlyDnp13LcMgzA2-ThkH2gb9qHX_7voK8mdCW4,1919 +torch/include/ATen/ops/softmax.h,sha256=mx3zXph07CuIQkxWNM52BkM_LG6yln5uTS8jIakcGJg,1659 +torch/include/ATen/ops/softmax_compositeexplicitautograd_dispatch.h,sha256=-otz4faWijkzKNiSCJjB6QUHz1qkXMUeFFikDmUzDx8,988 +torch/include/ATen/ops/softmax_compositeimplicitautograd_dispatch.h,sha256=A-1Q6l8C1r5c8itbgXfslohXezEnnH595_J9jY6VpbA,958 +torch/include/ATen/ops/softmax_native.h,sha256=qgEjbrnemQD9iIYz2aQUm30OweyrB6UM3_6Hf_YCRJw,801 +torch/include/ATen/ops/softmax_ops.h,sha256=PW81QWZ1G0SckCPccbBLVEQcRdBJKdVqU0TdMrpksSo,2658 +torch/include/ATen/ops/softplus.h,sha256=7lmC_mrRI1ryEZAwg-ROI8DHdQtnJZK6Fbza63lnbK4,1381 +torch/include/ATen/ops/softplus_backward.h,sha256=4u7BGaKHr-aPkVJWo5p9wfHGbK-2zE1ZMDtsRmY-4xQ,1711 +torch/include/ATen/ops/softplus_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=yRyvBXIpd0TxdF7jjYtPtuP6f6DhcJK_jfDlaBnvjaw,888 +torch/include/ATen/ops/softplus_backward_cpu_dispatch.h,sha256=irVg4G8fiE6bKANPwE7laR_bQ1s2ajXO1TCxz1-W9Ck,1185 +torch/include/ATen/ops/softplus_backward_cuda_dispatch.h,sha256=Em2nFwaFRtj0glqW9N0eTAapqi8hL4uSnkXNMTMV4ro,1187 +torch/include/ATen/ops/softplus_backward_meta.h,sha256=GR6FfK2vhtKow9LMyxBOzHBdBgMZOju9wst03uw64yI,665 +torch/include/ATen/ops/softplus_backward_meta_dispatch.h,sha256=7h0a9RE6mIXwiyuG1Qidxna_qUKXlmM3KBahwDNVB-w,1187 +torch/include/ATen/ops/softplus_backward_native.h,sha256=mhhEfE7d2W48M64FjdUztxskVnYj717_csp60sqMWvA,723 +torch/include/ATen/ops/softplus_backward_ops.h,sha256=_IF_OERFmhkCpgHnDH860qSZAu78YsglWor9eX6Ia64,2225 +torch/include/ATen/ops/softplus_compositeexplicitautogradnonfunctional_dispatch.h,sha256=AxsmNLiuVnwsNx5q0qW15VTu5qb5lRxeNdLH1LIzFWo,852 +torch/include/ATen/ops/softplus_cpu_dispatch.h,sha256=RR3_VIToYt9OWM9n3oi9MXMKX8DoM-EQo6cSM69IDv8,1058 +torch/include/ATen/ops/softplus_cuda_dispatch.h,sha256=pOtF5h-A9QmnyCkkOgC5bhqimw96GuIS5IlT04KVgHI,1060 +torch/include/ATen/ops/softplus_meta.h,sha256=RtMerg3M03jSyrHAZkB4Au1tRX0VHP8aTiiwL3KLc-Q,624 +torch/include/ATen/ops/softplus_meta_dispatch.h,sha256=zFKHcO4RG0-KFa8alMQuM9vpEfdctrT8v_9zweG66Cs,1060 +torch/include/ATen/ops/softplus_native.h,sha256=6YMv7QgiH6PEkWvhGPQ1GBncnF07-M8JGINn6uWcL9A,657 +torch/include/ATen/ops/softplus_ops.h,sha256=ujlF9pHnIo8OWPl0CXX93cvdFr27niBj1XR5Cszd49M,1931 +torch/include/ATen/ops/softshrink.h,sha256=MqQqMW_maaz-VdqHGPsU79QfnTLUdTxx2VzmZOxvb_g,1228 +torch/include/ATen/ops/softshrink_backward.h,sha256=Hfxt5eCyjUApL7LiExnlzuBg4ZigCL3wYYhFj_k8CMw,1563 +torch/include/ATen/ops/softshrink_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=3wp6BPR3KZD_5wBtv441LjL-OBB9oifbc3gEt2eTY24,861 +torch/include/ATen/ops/softshrink_backward_cpu_dispatch.h,sha256=bLAR1XXgeCw7rD_sahvpKF69olVHZrtcaWagQpp3ooQ,1104 +torch/include/ATen/ops/softshrink_backward_cuda_dispatch.h,sha256=34HDbvTWDXHSe--jDmSWh5ofNMzvGEH4Kp2cBIhORwQ,1106 +torch/include/ATen/ops/softshrink_backward_meta.h,sha256=iz5krjfamAVzblHhKAiuXiWhDTJS7PowA62wkRKRW7g,638 +torch/include/ATen/ops/softshrink_backward_meta_dispatch.h,sha256=RMrTtetoiE_Yb_tX9scUiwK7d8nLQO0y_L5ijIVAork,1106 +torch/include/ATen/ops/softshrink_backward_native.h,sha256=JTlca3O4ir5Z7_jmA6aJnL9iLr2lUp1Ra9axDuWfWKI,700 +torch/include/ATen/ops/softshrink_backward_ops.h,sha256=c2cuzuDWwAEepCCNV2XOWg714wmlxR_fnfpPRq8LLRE,2047 +torch/include/ATen/ops/softshrink_compositeexplicitautogradnonfunctional_dispatch.h,sha256=s5FhbiA_3ZoBewO0Sk-kh3SROi6CbYuhjf1SfRI5vGM,824 +torch/include/ATen/ops/softshrink_cpu_dispatch.h,sha256=lt20fAFHALyCo2tleiMU_VihjRPcuouBRCnMs8VIFzU,975 +torch/include/ATen/ops/softshrink_cuda_dispatch.h,sha256=1YMOzyji6HWxejuF8e6L7YTrP6WHoHdBYmMiy0KwQYk,977 +torch/include/ATen/ops/softshrink_meta.h,sha256=DO9PRpVe2de0_3Z6L9AJdaPDBbrOfEzCt-fTNtQAfLs,597 +torch/include/ATen/ops/softshrink_meta_dispatch.h,sha256=pGH6GMMP-sbvAxElBzqHkObdJOYA7WUisRru5CFnKt4,977 +torch/include/ATen/ops/softshrink_native.h,sha256=egReq5AnUnZGb3tPOO5JrDEfFB5Bj0FcEdKDPMLme_s,634 +torch/include/ATen/ops/softshrink_ops.h,sha256=ycgJuRDK0ILQcyNFAIbw14DcRl2FtIzPhofwDlkBqMA,1751 +torch/include/ATen/ops/sort.h,sha256=ItbrlyqMHbVJAzDHIk-S8noi3Jq5h3BnkVe2EmiEkwc,5276 +torch/include/ATen/ops/sort_compositeexplicitautograd_dispatch.h,sha256=OLvtUB29TbaRQl1_YxyXDxJEFQaW3oZqE3qb45HwbXg,1152 +torch/include/ATen/ops/sort_compositeexplicitautogradnonfunctional_dispatch.h,sha256=7ez2nJVR9ehASlCCkromlQC0OJSf_YSiJCZPSFguPfM,882 +torch/include/ATen/ops/sort_compositeimplicitautograd_dispatch.h,sha256=SlBcdRExjLotUKtQgnuJpnEkLyXnXJgRTvEOgso5cQg,1696 +torch/include/ATen/ops/sort_cpu_dispatch.h,sha256=O25uhAS4BQZdELtPdKzubbCWFNKRf5GkMF0nphDFDcM,1198 +torch/include/ATen/ops/sort_cuda_dispatch.h,sha256=bpEkVLAa8mo6DqNREsKPLnFrWR6MHONfzOTZlR55nrI,1200 +torch/include/ATen/ops/sort_meta.h,sha256=HzlsdsawPqEIkiH9zaaZnE7C6Vlj-qWK0nKDKAZ-Tww,632 +torch/include/ATen/ops/sort_meta_dispatch.h,sha256=E19d7kFL_fXSMjU6HLr1zkUJcy5g4iRvTFxYGdrlzak,1200 +torch/include/ATen/ops/sort_native.h,sha256=D247gvJNGZ1pfw3So1m91i3hD7mDx4-RZ9SXT2CaizA,1754 +torch/include/ATen/ops/sort_ops.h,sha256=4IlkAfFdgKWwbB8tvQyRtuMZTL53-Nm3UtBAQzQZ0io,7496 +torch/include/ATen/ops/sparse_bsc_tensor.h,sha256=97HUXfNArIbYCQFEDSEivp-TMYGlTrmK9rjrQbl-sHY,2993 +torch/include/ATen/ops/sparse_bsc_tensor_compositeimplicitautograd_dispatch.h,sha256=Oaq_2bhCcPXsOGDhfd09JEuwuKhp45D21DPQhDfDnFs,1626 +torch/include/ATen/ops/sparse_bsc_tensor_native.h,sha256=0G7JSJmWVsJsX5z309I9HNuBYXDqe7NsvalDJOLBkE8,1022 +torch/include/ATen/ops/sparse_bsc_tensor_ops.h,sha256=Z1eKjaxfZ0YP8cFvYjRlnbFqO93i--FPwNySmVkJT7g,3127 +torch/include/ATen/ops/sparse_bsr_tensor.h,sha256=zPv3Lwynzd3wPmrfZl6ShTQeTDZ1CJAr43zJVAO3dRQ,2993 +torch/include/ATen/ops/sparse_bsr_tensor_compositeimplicitautograd_dispatch.h,sha256=ztfBuW00EU5D6UWEYC9srfHMx_M8GBqc1Se2V2ie7os,1626 +torch/include/ATen/ops/sparse_bsr_tensor_native.h,sha256=YciAwxn91wLW250-3T_ruoB_9bRhexviUjFg2sxd7r8,1022 +torch/include/ATen/ops/sparse_bsr_tensor_ops.h,sha256=-o__PYd2GuyjIcM13eIimcX-43ARFBbEEqnmxEr4CgI,3127 +torch/include/ATen/ops/sparse_compressed_tensor.h,sha256=OpHq_Pv5iUWCb3QE6ldH-WWIVX_lz2Le-zseClb32oM,6970 +torch/include/ATen/ops/sparse_compressed_tensor_compositeexplicitautograd_dispatch.h,sha256=Ua6jqejXfxUPFU7b1goUu1Bh1Kf6u4-Q6O8gMUaUObY,2218 +torch/include/ATen/ops/sparse_compressed_tensor_native.h,sha256=36gkG2SDo9aDE7AIByHfpGFRyvdiSzitdgjVRxg60k8,1052 +torch/include/ATen/ops/sparse_compressed_tensor_ops.h,sha256=mhaRy6N2x6gSakcjfNqWYDomecw21Jgl5lZHZ1wP3Z4,3244 +torch/include/ATen/ops/sparse_coo_tensor.h,sha256=PTQFQQXR3NtI7zBrX05mKhQO9Te_KC7cyMyGvNmmdz0,4281 +torch/include/ATen/ops/sparse_coo_tensor_compositeexplicitautograd_dispatch.h,sha256=6gmDu71oxT_L139x66xJ7s2sBsTKz0nYrzrD5MdG-mg,1179 +torch/include/ATen/ops/sparse_coo_tensor_compositeimplicitautograd_dispatch.h,sha256=KhxI_NuAknTqPjCFGwufmpVE0FCYtgashGPRg_mtri8,1658 +torch/include/ATen/ops/sparse_coo_tensor_native.h,sha256=U6kWUz0hJvdHrb_KTBfrr3hk-vovCJpa0_YIyVjfGLo,1360 +torch/include/ATen/ops/sparse_coo_tensor_ops.h,sha256=hbPagIadNRsK-ZV6HzdArbh39yyzgaljSWoH55ydsrQ,4747 +torch/include/ATen/ops/sparse_csc_tensor.h,sha256=UmfaGpfhKIPQ-wPu5wy8GvfwL-6MQ0lhxWf8V0ywbI4,2993 +torch/include/ATen/ops/sparse_csc_tensor_compositeimplicitautograd_dispatch.h,sha256=1RwKhdA6DbR3fTgB3iFu6VO5AvWO70koWG3V9T7k518,1626 +torch/include/ATen/ops/sparse_csc_tensor_native.h,sha256=KdhFWa8Vp90wvUscghYAJ1MPEwg304dsvs_WK9Ej1Uc,1022 +torch/include/ATen/ops/sparse_csc_tensor_ops.h,sha256=o8GRMVLNciu8PgPivVy7M7fg7kC15xMno3oJTZmstqE,3127 +torch/include/ATen/ops/sparse_csr_tensor.h,sha256=W9dSI4QFMCKQLra6m6E3-B1n5Jn2KJBb44gucXpDIfE,2993 +torch/include/ATen/ops/sparse_csr_tensor_compositeimplicitautograd_dispatch.h,sha256=tg5pdKL2iiApucrbPynYr0pF1HHl7mvAsvqqGcZoHUc,1626 +torch/include/ATen/ops/sparse_csr_tensor_native.h,sha256=pTHwkai1ZJAcs1Qx93IOSm3cnWpNtZlmj2XMWJLv9_k,1022 +torch/include/ATen/ops/sparse_csr_tensor_ops.h,sha256=qtKXIj7BM5Hp0sQ6DtLpa4s_-eiN1Agn9a-btHuFlfs,3127 +torch/include/ATen/ops/sparse_dim.h,sha256=1JHT18piPjGJg1B9Ou1I1t9aSxoY6XOeOqxoAVQbWEk,507 +torch/include/ATen/ops/sparse_dim_compositeexplicitautograd_dispatch.h,sha256=gz-2bPdDY3EXY0b-Y_UGcVMVfIEkA6XoBwhm5TABTtE,765 +torch/include/ATen/ops/sparse_dim_native.h,sha256=f32Fv_e04mXR0JaljNraqOsE4r7D9nKwzoVOC4gI-Ro,613 +torch/include/ATen/ops/sparse_dim_ops.h,sha256=9DbYbIJpXo2akrhEFm3kR4vQ1e8UaaW6-QwHliLt3jY,957 +torch/include/ATen/ops/sparse_mask.h,sha256=r6k66KCVKQON7mVVs_ddxeyG_UT0lPUgFLTGBNtz4GI,1015 +torch/include/ATen/ops/sparse_mask_compositeexplicitautograd_dispatch.h,sha256=lZZ8zflSDKJfLpXu_gdweD8WAC1Dx_sysbqpu161Rl4,927 +torch/include/ATen/ops/sparse_mask_native.h,sha256=7kv8HCtEr1b37ugxB_WyrVC-fiKLoRYI8QVdOEiUWTk,716 +torch/include/ATen/ops/sparse_mask_ops.h,sha256=HwIMfFkUQZzYCazCnxZTd3UqQibCeWyHPLklvSskVlE,1743 +torch/include/ATen/ops/sparse_resize.h,sha256=2CXv0ZToqiH_aGc7XnH7zCLxNJVWKIlIe6RsNPveKHI,1520 +torch/include/ATen/ops/sparse_resize_and_clear.h,sha256=N_MnUHp3bGdtvDH9j1tH934K80qxsBTGPa9q20DpfKY,1620 +torch/include/ATen/ops/sparse_resize_and_clear_compositeexplicitautograd_dispatch.h,sha256=Ls6kS5ml4uGfjC8K_A38ow6ICk7nNTWQCrnhD03DJEk,1179 +torch/include/ATen/ops/sparse_resize_and_clear_meta_dispatch.h,sha256=Km3K7YrJCKVTiJ0gwwqE2nwwLTBhECQcFGYHRg9zdcA,809 +torch/include/ATen/ops/sparse_resize_and_clear_native.h,sha256=4HmF_xA3kA9aKL6nDgHg4PVdF8saRmZFgrOYeU-7txQ,863 +torch/include/ATen/ops/sparse_resize_and_clear_ops.h,sha256=5mBltW5aAklkmpbUEIcQ5kGIGRcCzF4XdeC8xJzFcJk,2871 +torch/include/ATen/ops/sparse_resize_compositeexplicitautograd_dispatch.h,sha256=n_ZcqJ29w16qtM-k4Cpydy8AXe696IkrSfxNiXrxO68,1149 +torch/include/ATen/ops/sparse_resize_meta_dispatch.h,sha256=TIW_sMk1jfmUGdgJQGhLs8nk-zLUkVj68Rdgvrr8sfA,799 +torch/include/ATen/ops/sparse_resize_native.h,sha256=vzXoDtjqZetOddohZMiSnYlOaf3ubHjCuGWrCDM3A1Q,833 +torch/include/ATen/ops/sparse_resize_ops.h,sha256=KnEy0zdT3jeqqg9AhvNEkbd7aPpi5GKhtz0vBawoWU0,2781 +torch/include/ATen/ops/sparse_sampled_addmm.h,sha256=HoOA-ef7uQVEVVsjwZK6eM5LimGb8_ogHyWZnSXW_go,1727 +torch/include/ATen/ops/sparse_sampled_addmm_native.h,sha256=_xV7o3UAg_0EkmHJQURCHn8ljuTVgQTV3II519CpKJw,1216 +torch/include/ATen/ops/sparse_sampled_addmm_ops.h,sha256=YfkwMa6AYhzJLB7e3fRBhDYoKWXqdrIKn1ia_V_XG0Y,2312 +torch/include/ATen/ops/special_airy_ai.h,sha256=m-qoWMTVe3ygyyRVSxN7xTB5AYFleplzZ3CNH_8dAB0,1090 +torch/include/ATen/ops/special_airy_ai_compositeexplicitautogradnonfunctional_dispatch.h,sha256=wXLDK7Z1CclTtHXRXETveaiueJ9157ffmycygpScvQ4,796 +torch/include/ATen/ops/special_airy_ai_cpu_dispatch.h,sha256=5eQjksGWxSERuaWFRpjU31bFZsmTRZQBxMC0IHO0v4U,895 +torch/include/ATen/ops/special_airy_ai_cuda_dispatch.h,sha256=2TW-ozb54TT126PSh6oF_e1X_bH_t79z6Fm4RipvYTQ,897 +torch/include/ATen/ops/special_airy_ai_meta.h,sha256=3bcx2ZuwRd8wOqUAuilj7JEcIOZDZ9vqlkh-CvYJkNE,573 +torch/include/ATen/ops/special_airy_ai_meta_dispatch.h,sha256=NmhGhErGzoVEo213-MT8hjnQ-Cm_svGG81-JHCOvp5w,897 +torch/include/ATen/ops/special_airy_ai_native.h,sha256=zTfeguwsfrJLsH3Nu6_PhmtrXENwiNyjIcyW8HC18mw,620 +torch/include/ATen/ops/special_airy_ai_ops.h,sha256=7HnF6hy1-d8oJtH3wHmjACAyU-jzanCo2Of0uaQf04k,1583 +torch/include/ATen/ops/special_bessel_j0.h,sha256=aubVjrK-7avCEPeKmCsBidar_wl8HO_zF1WO_vH0Iko,1137 +torch/include/ATen/ops/special_bessel_j0_compositeexplicitautogradnonfunctional_dispatch.h,sha256=sUN_-xH6BJrL6zS4TgVstPSH0E3pwzo9MZNAdRWF6fQ,801 +torch/include/ATen/ops/special_bessel_j0_cpu_dispatch.h,sha256=md-EpgEyXeXiy_mvwyWT4yJDS19AqOjGpjVwmJJ48Kg,910 +torch/include/ATen/ops/special_bessel_j0_cuda_dispatch.h,sha256=Q7HboI7OlqdoCdybyuVMqtNP2XRV2Y2UnmZLG6sUaUo,912 +torch/include/ATen/ops/special_bessel_j0_meta.h,sha256=5TwFg0gAo7iNI1KeqPMjqEizLLJd__khrBpzfemba88,578 +torch/include/ATen/ops/special_bessel_j0_meta_dispatch.h,sha256=4uQZkdZ072Iv32obwR3IqoBqZGG0Cik43tI4jMRfy54,912 +torch/include/ATen/ops/special_bessel_j0_native.h,sha256=VnSLoq16DZr2k3hb15y5kp-LaEypZigTcQiE7MS9oXU,629 +torch/include/ATen/ops/special_bessel_j0_ops.h,sha256=xw5AKGr_R1_ANylEpiyGmgNr9Az0oxkBdVIOGhQ6qcA,1613 +torch/include/ATen/ops/special_bessel_j1.h,sha256=xuPt9Yki5Ks4uo5Y00LA3nSK-MmD-Isa88nyUsPcUzM,1137 +torch/include/ATen/ops/special_bessel_j1_compositeexplicitautogradnonfunctional_dispatch.h,sha256=ErD3nUgCo-p6Mp4VCz45ATz-DlKhU8fuxhmZEyewvI8,801 +torch/include/ATen/ops/special_bessel_j1_cpu_dispatch.h,sha256=8TOzZvWYktdR2TZMd4LK26IiLRQgKhLNmYQFYiY05-8,910 +torch/include/ATen/ops/special_bessel_j1_cuda_dispatch.h,sha256=hqBs8c0fMmyeml0Jr9AapvDNe2qzynZL9RTSr3xGvMQ,912 +torch/include/ATen/ops/special_bessel_j1_meta.h,sha256=SabGokBjLcbFO9AiADMx1cEzvc7x0TTJrOMPhmtjXEk,578 +torch/include/ATen/ops/special_bessel_j1_meta_dispatch.h,sha256=yeICsiXvjKMibwalDoSne8ec9E3uMijJIAyLl8FdZOc,912 +torch/include/ATen/ops/special_bessel_j1_native.h,sha256=zSKL3erH0OYSXWb9hEdYdTA8ak2YYFlgMaATR326wS8,629 +torch/include/ATen/ops/special_bessel_j1_ops.h,sha256=UmOGbatbO5qrRyWDfN9A7UkcVwrR62vvUscWTOIeOHI,1613 +torch/include/ATen/ops/special_bessel_y0.h,sha256=uRY3aSEtquuL_k9hpEAllRVU5E0aAmmZWBhqSi8Ma1Y,1137 +torch/include/ATen/ops/special_bessel_y0_compositeexplicitautogradnonfunctional_dispatch.h,sha256=yi6XCUAj2a7WKU-aNabw6gk_sjRdkz_Gg-dwijd0n_w,801 +torch/include/ATen/ops/special_bessel_y0_cpu_dispatch.h,sha256=xy4oxo3wDUSeNWnm1X4Cq4EcZZY1Ex1Ybl_tzyfiW1U,910 +torch/include/ATen/ops/special_bessel_y0_cuda_dispatch.h,sha256=LbI3vgcJWlxI0NAdQbexwcYN-eBzFSQbUC1YIjFKoyk,912 +torch/include/ATen/ops/special_bessel_y0_meta.h,sha256=caNZfIyekwW5EcL9K3rYHHPzOS_IPhX6Ev292MrWdcQ,578 +torch/include/ATen/ops/special_bessel_y0_meta_dispatch.h,sha256=6EP2Q1rec2ncPr2f9KnicSI68aC5VcvbxwSlM8WC-Vg,912 +torch/include/ATen/ops/special_bessel_y0_native.h,sha256=mHtFsJaRxH9qAzWTZeAMa5mlOWSznHsyyaxWsDIJ8SY,629 +torch/include/ATen/ops/special_bessel_y0_ops.h,sha256=fuegfPn3SBYqEwDecAQINXvi46OO29QuUYz3j-mtdJw,1613 +torch/include/ATen/ops/special_bessel_y1.h,sha256=ELAjIFvMPoRnf7RJJ84me-eTHX7wtf_tG6pHtt20RLk,1137 +torch/include/ATen/ops/special_bessel_y1_compositeexplicitautogradnonfunctional_dispatch.h,sha256=WIZS4xuLu5ouddWjpdxEia00so8BiNpaOlUXqm9dnDU,801 +torch/include/ATen/ops/special_bessel_y1_cpu_dispatch.h,sha256=k87LEe8Edev-dG_fp59c_d14FnWlD_yi6mmwvLH8niI,910 +torch/include/ATen/ops/special_bessel_y1_cuda_dispatch.h,sha256=2iNoCFgKh3L0DU7aX-Ig34eKG2qpoBLISuOX8z77BC0,912 +torch/include/ATen/ops/special_bessel_y1_meta.h,sha256=rXnRKHpuuchhbVW6rOfZaRIb7Hd3R6DVv3hUVUfAGVM,578 +torch/include/ATen/ops/special_bessel_y1_meta_dispatch.h,sha256=TI8SUHiOBuSLjevY95zeT1NnmCCMRRhKtlnbmdTml-0,912 +torch/include/ATen/ops/special_bessel_y1_native.h,sha256=kYgzPqCZtA2VZ8_u1cVf4VVSW7qz7nBtdlUFURLRAfw,629 +torch/include/ATen/ops/special_bessel_y1_ops.h,sha256=CHYq9PSwfa7ozhb5Msje7Bl91-MpkxIvr2r9fa15gn8,1613 +torch/include/ATen/ops/special_chebyshev_polynomial_t.h,sha256=6qXW7GazX9RfLg984fij3adSNk_M9hTCSmMGN5RBopA,3091 +torch/include/ATen/ops/special_chebyshev_polynomial_t_compositeexplicitautograd_dispatch.h,sha256=VpDsIQwFNz9o04cPtTZ2pPp7LbTIgbQ_rmxgVy4KQdM,1390 +torch/include/ATen/ops/special_chebyshev_polynomial_t_compositeexplicitautogradnonfunctional_dispatch.h,sha256=2uRZ8wcT-VRmiclvi7iRTIZzwODjUEn2Ml7F0mPzl1A,833 +torch/include/ATen/ops/special_chebyshev_polynomial_t_cpu_dispatch.h,sha256=Pwops8mIBXgfxvHO9g9TE4CA7JKLBB6rupzSH62rIlM,1006 +torch/include/ATen/ops/special_chebyshev_polynomial_t_cuda_dispatch.h,sha256=3GdWBz8i-WDyvLvim-oiv2zz9ijf7Bo40U6yPyn6wc4,1008 +torch/include/ATen/ops/special_chebyshev_polynomial_t_meta.h,sha256=Lcf7xsu-CoC3niSJQSHMD4BaDKOO-OQbYY02rSgy83s,610 +torch/include/ATen/ops/special_chebyshev_polynomial_t_meta_dispatch.h,sha256=3rpaIPQMlrDAYT0928BMWPgPLGpCpnCcGTDa3YRYdNs,1008 +torch/include/ATen/ops/special_chebyshev_polynomial_t_native.h,sha256=7y7UoQX0AgJm0Ri6aLhpIBfiwIJbciiKVY79TUPGTfs,1123 +torch/include/ATen/ops/special_chebyshev_polynomial_t_ops.h,sha256=waURq43h-cdJFbxq9HmMdjjzTeY2osCli9OoagTjQSk,4655 +torch/include/ATen/ops/special_chebyshev_polynomial_u.h,sha256=KCOCPjIk3UXSE97Nid6Ytt-f4Tzk22S0zoGsyEsayUI,3091 +torch/include/ATen/ops/special_chebyshev_polynomial_u_compositeexplicitautograd_dispatch.h,sha256=0heZjuv7fa0_5AVSsrmT-P6AJLCiqpvMYHmflp6mY-Y,1390 +torch/include/ATen/ops/special_chebyshev_polynomial_u_compositeexplicitautogradnonfunctional_dispatch.h,sha256=MBfUz24uIavssctq6hpI1-nu8GMYnIIxWxIs2WzJFPA,833 +torch/include/ATen/ops/special_chebyshev_polynomial_u_cpu_dispatch.h,sha256=j_7L8vpq0C99C7cuWaLL6Bpa9WSS51J24ZBD1bhX6XQ,1006 +torch/include/ATen/ops/special_chebyshev_polynomial_u_cuda_dispatch.h,sha256=yvk6Ai46pk8DrABh6vsAXnV5dVvdFRyYqts6Th2FEyc,1008 +torch/include/ATen/ops/special_chebyshev_polynomial_u_meta.h,sha256=FbcXPf-gke479Bko9m3t_CvDj96AzUFj6ho8L4ZttAM,610 +torch/include/ATen/ops/special_chebyshev_polynomial_u_meta_dispatch.h,sha256=7JSc9wJBBM5Wnw0IasGJPrXaT-B5rUFKOSH8iss3AOw,1008 +torch/include/ATen/ops/special_chebyshev_polynomial_u_native.h,sha256=_pGZIABNPT1gX32fNgKKxdrJsiP_xzjow_9meuhXcTE,1123 +torch/include/ATen/ops/special_chebyshev_polynomial_u_ops.h,sha256=htdIrsejC4aSwASGmO8e1KPbEEXTe3TfCYobTiro2S0,4655 +torch/include/ATen/ops/special_chebyshev_polynomial_v.h,sha256=9m14iBtiohBKtmZKtYlXyw-pL33RPSiWTrnEYOVKT90,3091 +torch/include/ATen/ops/special_chebyshev_polynomial_v_compositeexplicitautograd_dispatch.h,sha256=6J2FsUG9MKVeATJbjKUtk8z8My8Ph_s3j9j3RxXCSzY,1390 +torch/include/ATen/ops/special_chebyshev_polynomial_v_compositeexplicitautogradnonfunctional_dispatch.h,sha256=9cvNUDRc88CLFGJgbTPmB5VFqI3PA63r6V8N3O0TO00,833 +torch/include/ATen/ops/special_chebyshev_polynomial_v_cpu_dispatch.h,sha256=J7-BFd21sJu1WT5axGZtIttcx_kks55fmQCFeAOC2zA,1006 +torch/include/ATen/ops/special_chebyshev_polynomial_v_cuda_dispatch.h,sha256=U3YvcI9RAhQv7aDOUzvZna8m1Vdezpw3tU6u6RlpxoY,1008 +torch/include/ATen/ops/special_chebyshev_polynomial_v_meta.h,sha256=XqUoK6E4MxherSxmaXO1VS7riQkueMNAIYnvVVY8kBM,610 +torch/include/ATen/ops/special_chebyshev_polynomial_v_meta_dispatch.h,sha256=uOtifp6hnOSb3ckBjqNUxA-hjs0RRxfgRyU5cgb-zrc,1008 +torch/include/ATen/ops/special_chebyshev_polynomial_v_native.h,sha256=hKFOjZEQoFWCzr2HkLhg-1LDae5HnjI5JBXsQahFwgg,1123 +torch/include/ATen/ops/special_chebyshev_polynomial_v_ops.h,sha256=SzlC9jaLq0Y_y5-m3ANsNi9rbICNp4x3_MIs_GSQ2Q0,4655 +torch/include/ATen/ops/special_chebyshev_polynomial_w.h,sha256=YXhd-vsRRZcXs5g1TQEn4fk3g0itgq8OcOSHAFDjzQw,3091 +torch/include/ATen/ops/special_chebyshev_polynomial_w_compositeexplicitautograd_dispatch.h,sha256=1uYSq3PS-9ey1SThcT-5qEdiDcrf-ecImvEsyZzf3cg,1390 +torch/include/ATen/ops/special_chebyshev_polynomial_w_compositeexplicitautogradnonfunctional_dispatch.h,sha256=u5QPrFza1uFoaT5OaloEFEHLJTjGrqxobHkOM63mfm0,833 +torch/include/ATen/ops/special_chebyshev_polynomial_w_cpu_dispatch.h,sha256=YEvt-BjUt2aS_cqqekfex0HNkwDOQ7vM4e426fYePZM,1006 +torch/include/ATen/ops/special_chebyshev_polynomial_w_cuda_dispatch.h,sha256=9BNxFogiA1iKlXdhkbEHIqPciA8B_o5iUKij2g95jo8,1008 +torch/include/ATen/ops/special_chebyshev_polynomial_w_meta.h,sha256=sDfdMImtpqqg3gq3JxcADFYcgugvaQnrS0W2-WsPo10,610 +torch/include/ATen/ops/special_chebyshev_polynomial_w_meta_dispatch.h,sha256=QoBjRSr8ZP5i8nFIyOCdUt3dz9GLBKdXehj65hsnwpw,1008 +torch/include/ATen/ops/special_chebyshev_polynomial_w_native.h,sha256=sFLZV7Gbf3ta8exoFULa0siwsp7GeAns8YxqAuwBuLo,1123 +torch/include/ATen/ops/special_chebyshev_polynomial_w_ops.h,sha256=wqId8iAkWtTTbt6nwqNUt2ko7xSu-8PyGyVn8j9ke84,4655 +torch/include/ATen/ops/special_digamma.h,sha256=np8wOHpucwW4FlbeGlCk_9qVYTSOzeZU0OUn3yhgMb4,1117 +torch/include/ATen/ops/special_digamma_compositeimplicitautograd_dispatch.h,sha256=6w44bSSTZmKE1ZDj3hDLW4hkMwRlnDMFeP-ewFOcl8g,948 +torch/include/ATen/ops/special_digamma_native.h,sha256=opRU0XoTJZ0RRvVTwKgCrOJ8ORod6-u2yZehP2LuIaE,572 +torch/include/ATen/ops/special_digamma_ops.h,sha256=UU8oZvRnTZkYUl5LsbgNGmFySLap9lC0V4VRWpNNAOw,1601 +torch/include/ATen/ops/special_entr.h,sha256=Igb0ocwR86-fl29cYdwrPNkwPafVvdXyI2zS-J8SU6Y,1087 +torch/include/ATen/ops/special_entr_compositeexplicitautogradnonfunctional_dispatch.h,sha256=KjDfuVMyVbkHAwN2bSyu2BCmbaYdD8tiG_OjT_P_x6g,796 +torch/include/ATen/ops/special_entr_cpu_dispatch.h,sha256=McR2Tw-PN0ls7ZM9RiN59USoxJnD2IIsfGZCyzwzrCI,895 +torch/include/ATen/ops/special_entr_cuda_dispatch.h,sha256=uTIkz7f9Md5faUax6Vbf_aX3vd4gycNxVcEwaEBhnWY,897 +torch/include/ATen/ops/special_entr_meta.h,sha256=mlO38HvCtmmPfthSCNIIiCzwhPO5348c9hD5_Vb7Ddc,573 +torch/include/ATen/ops/special_entr_meta_dispatch.h,sha256=qBJhu0Z-NTtoEhORfsjVO_gy8uXspvtIafFig3bPjhs,897 +torch/include/ATen/ops/special_entr_native.h,sha256=3nyD7Yyt00H6_Gq0IDWDl-FrA0ulELOpEqgnwj_40rw,614 +torch/include/ATen/ops/special_entr_ops.h,sha256=60NrHPg_xdOc4v8_nizKZqSbruMAWA3uG1lqnMhMBvE,1583 +torch/include/ATen/ops/special_erf.h,sha256=yl9ha3cB4SQj9jWdm3IIh9f2rlHSkXYB0mJNod5iJ4M,1077 +torch/include/ATen/ops/special_erf_compositeimplicitautograd_dispatch.h,sha256=LXyIpJtkmL8sWHfvO4zyFbhysvzz-HDTaoyOACIdlzg,936 +torch/include/ATen/ops/special_erf_native.h,sha256=dBlUCq8lFXLZdFL7wlBJntZl5nlueoeD1_sSgRBc3BI,564 +torch/include/ATen/ops/special_erf_ops.h,sha256=qRDkRtat0Rp1eeWdePKCUHTTRBVbBbpbVsMN5XF-F_A,1577 +torch/include/ATen/ops/special_erfc.h,sha256=1tI0HSNkdZ1AEt58uuFzm23YNO_l10p6n5iSfI68NKc,1087 +torch/include/ATen/ops/special_erfc_compositeimplicitautograd_dispatch.h,sha256=7VfX4Vi5s0HJhP1JBbeCaPpfd1ToMQ2CllbU8mMuZoY,939 +torch/include/ATen/ops/special_erfc_native.h,sha256=_QeQjuOVVv71RWEhdH1uvK7DxGP87A-8pGrMqWTtno8,566 +torch/include/ATen/ops/special_erfc_ops.h,sha256=TgezTuwEW09fsmcCXUFat4L__R38PLn0TV-UzQhdUVg,1583 +torch/include/ATen/ops/special_erfcx.h,sha256=l5_rG37FxyaLQB05tlOTfUWczl4UH7ujvcH774s8Ltk,1097 +torch/include/ATen/ops/special_erfcx_compositeexplicitautogradnonfunctional_dispatch.h,sha256=oUGC1DgJYkoP7bsHoaoErmaG2Rh0IJGSw7TkuVdbORM,797 +torch/include/ATen/ops/special_erfcx_cpu_dispatch.h,sha256=-PMyWWGc4qpjY2VsgmVsW2rN3NzjFMJlA24EOBNPbgk,898 +torch/include/ATen/ops/special_erfcx_cuda_dispatch.h,sha256=h64IJWxMneBUlrl1qBI260TcM9-oujtC5gJ-nTKji1I,900 +torch/include/ATen/ops/special_erfcx_meta.h,sha256=HDOWDwGKfp9laM4lz6nDIbTs8gQfFbXDssYdoz45n0s,574 +torch/include/ATen/ops/special_erfcx_meta_dispatch.h,sha256=YsnhB4hBJwdoRCUgoZ9GJHp9Zq7w0pkZaD1-TOFpTtY,900 +torch/include/ATen/ops/special_erfcx_native.h,sha256=gt-o802TP3z9w0Pyri8RwD6zcrcWe-zYmh3xlJYEHs4,617 +torch/include/ATen/ops/special_erfcx_ops.h,sha256=0YbpMNRYeYiqAPwHZFeNurjLpGrHljrqcEOvK2PJmtU,1589 +torch/include/ATen/ops/special_erfinv.h,sha256=Yof8JWaLWSWSF6vToCQXUqv_eGMp32GI7tkv9Oh_PqM,1107 +torch/include/ATen/ops/special_erfinv_compositeimplicitautograd_dispatch.h,sha256=RfLsNZLPvJFL_W82R-WqbREDQheGBxutpM2tLKCt-bM,945 +torch/include/ATen/ops/special_erfinv_native.h,sha256=CMGlaPfxuZ5KpDUcFQzdtaN1uS9NrZ9wqeKOtqtpDR4,570 +torch/include/ATen/ops/special_erfinv_ops.h,sha256=kX2CwKtk__RXKMIYq5S6abAKD_kIEt3ApCTrDhzWLzs,1595 +torch/include/ATen/ops/special_exp2.h,sha256=ZL2Su9D3GWetIE_HslvzNJBvConXbx_IBmf_gHq5f2o,1087 +torch/include/ATen/ops/special_exp2_compositeimplicitautograd_dispatch.h,sha256=F0aiY7TNNLmvPFPF1kBI-9yw0MfNKAAYJ_iZJb_P1CI,939 +torch/include/ATen/ops/special_exp2_native.h,sha256=zE3Pn1cDgabbukQKMa1HWEaOTtDif8vemytbQfVFcs4,566 +torch/include/ATen/ops/special_exp2_ops.h,sha256=9-ZwAzZB4Gnxx3SYaStds2fr9McA8XVZwSQU6AtIz78,1583 +torch/include/ATen/ops/special_expit.h,sha256=urcD3Iw6xHDTg9tvilQbd6aYCYYSeVPhtkDiakeCFAk,1097 +torch/include/ATen/ops/special_expit_compositeimplicitautograd_dispatch.h,sha256=zLWyfpT6oltRy4hTiJCODeKw3F4sEl7O6QLlkdPhmRg,942 +torch/include/ATen/ops/special_expit_native.h,sha256=-YEvqNrRG47mikqPmvp-jqpb1DqA8A9TcxRenNfz2LE,568 +torch/include/ATen/ops/special_expit_ops.h,sha256=XeL8p-0xazJBFJ4x8q_031Xpgbq2sTL_Uwb_cuJp0yc,1589 +torch/include/ATen/ops/special_expm1.h,sha256=EpzUMkDIJcQlT9esrqucGKrm0jwdhIehhNsCztoixRM,1097 +torch/include/ATen/ops/special_expm1_compositeimplicitautograd_dispatch.h,sha256=jbGsPGS3jR9Dw7iHdLF_pwpkyI3JN4QcQvqzCDOE1G8,942 +torch/include/ATen/ops/special_expm1_native.h,sha256=l3Nyy8CqNjSuUgaOqqxaIJFMUvwGJR7hmvJ0X6HGxkA,568 +torch/include/ATen/ops/special_expm1_ops.h,sha256=KwoyVhMpHNQmVCzAnEcbBTfK8DG4WrWXJs9DQDmqZKo,1589 +torch/include/ATen/ops/special_gammainc.h,sha256=uajPzZOGekvcFPDkskGPegXtWjUTkb0ecZc3-ceYVns,1268 +torch/include/ATen/ops/special_gammainc_compositeimplicitautograd_dispatch.h,sha256=UR3bqb2Ns6VYkHEJQTdM31uRgNXNDWEgFIH7aJPJxuw,1029 +torch/include/ATen/ops/special_gammainc_native.h,sha256=cJPW1GkAZiVhCzgQcJkJh0I0Qt2RqAdgkhT9sdglcr4,626 +torch/include/ATen/ops/special_gammainc_ops.h,sha256=8Al2PENGbWSkxKb_lULL7wlgOJjB7IC3Hl0LYkOno_0,1779 +torch/include/ATen/ops/special_gammaincc.h,sha256=oD8PMRwZmpve5YXGIn8L00-F_ACgNMPuDXNZys94b3M,1278 +torch/include/ATen/ops/special_gammaincc_compositeimplicitautograd_dispatch.h,sha256=WjrBNFqp5eMRJMcsGkTf5PfwKYnh7jyVEs7RmKc24Rg,1032 +torch/include/ATen/ops/special_gammaincc_native.h,sha256=9YvSpC8XuBCJBc8ua2B6_n0fA95glLV-8hGfCWyFbyE,628 +torch/include/ATen/ops/special_gammaincc_ops.h,sha256=k4kIuWDfEbhILdb71TpGrVr1WuztRKwaDOwdjUlmIlE,1785 +torch/include/ATen/ops/special_gammaln.h,sha256=Sh3LITxiHYXfUTeFYlApg568QkbnCYI-3B3GRXpnwEM,1117 +torch/include/ATen/ops/special_gammaln_compositeimplicitautograd_dispatch.h,sha256=hAn1wR_BMq-a8NzzClfSKgaCpw_kjo1LQGEtMyk-8Lc,948 +torch/include/ATen/ops/special_gammaln_native.h,sha256=mwnrZhJVrvhDjHVKvuC28iWA9zJCGDaqUo3d9iObSi4,572 +torch/include/ATen/ops/special_gammaln_ops.h,sha256=emHQJlU3EzLZHPKFarZDJytLKr_vbiLJ35CzXZB8gvw,1601 +torch/include/ATen/ops/special_hermite_polynomial_h.h,sha256=zzty93KmQXdgyWzx-8Xac0QO9zfd3T90HvsBj_K6sXo,3035 +torch/include/ATen/ops/special_hermite_polynomial_h_compositeexplicitautograd_dispatch.h,sha256=AanUrIkI37hSFJTv10akpZmhHxKqGn_0HsK3h-s6SV4,1378 +torch/include/ATen/ops/special_hermite_polynomial_h_compositeexplicitautogradnonfunctional_dispatch.h,sha256=swBvyPyyLRgoA7CsP3p3he2c5T8-ZIupTO8674pbJB0,831 +torch/include/ATen/ops/special_hermite_polynomial_h_cpu_dispatch.h,sha256=aHvWfOMs6yuooiZy1D0ibSnXiLZVg1ZVvsg0_uV4B2k,1000 +torch/include/ATen/ops/special_hermite_polynomial_h_cuda_dispatch.h,sha256=iG6l52b3uKWMUm33dOgBF0FFgfpdi7YcHuLZcZNyjz8,1002 +torch/include/ATen/ops/special_hermite_polynomial_h_meta.h,sha256=36aO5Y8fJeL98YmPPM1apN-YjMY_eoZEJdrjLK4zNJk,608 +torch/include/ATen/ops/special_hermite_polynomial_h_meta_dispatch.h,sha256=gXkYp_tbQc_G5MHf3-DktLl8mr2HSVUZ4Vkl954Ircc,1002 +torch/include/ATen/ops/special_hermite_polynomial_h_native.h,sha256=spv6nBiKRwRYDCuiMFGn6F2p4hWmcdnoIjw_ipjINp4,1109 +torch/include/ATen/ops/special_hermite_polynomial_h_ops.h,sha256=AzRKyyArrIgkFXssvJPKAiq9dKciZvOOWkATd3acyqM,4619 +torch/include/ATen/ops/special_hermite_polynomial_he.h,sha256=BK_sut8lkxmVXmdYlEV2Y5O3OrBL8ZebHFmBfED6Uvs,3063 +torch/include/ATen/ops/special_hermite_polynomial_he_compositeexplicitautograd_dispatch.h,sha256=A316dOAobAqpM6DHX8vYkn-UL1bR964IVXUGLTUVFpQ,1384 +torch/include/ATen/ops/special_hermite_polynomial_he_compositeexplicitautogradnonfunctional_dispatch.h,sha256=S_If2JhnUk95N3pFflFSFYixYO_wluYXF4VetvNyuhQ,832 +torch/include/ATen/ops/special_hermite_polynomial_he_cpu_dispatch.h,sha256=EAO8DuqY7mNku7MlGD0WRgJA_tl6oK5jEsUsP3rXZNU,1003 +torch/include/ATen/ops/special_hermite_polynomial_he_cuda_dispatch.h,sha256=z08zVsqCjdwEqxt6yP4ZCpzzebXaVill_eTrbTQw8kM,1005 +torch/include/ATen/ops/special_hermite_polynomial_he_meta.h,sha256=I7TKYVcnJ3Rc3yBYOYkE1-2VsdnezoGiyAQp-gjIPwk,609 +torch/include/ATen/ops/special_hermite_polynomial_he_meta_dispatch.h,sha256=IKHgF0NnGFI0g7m81VIBs3Ow7mA6Pqaxa7S9c0yAttM,1005 +torch/include/ATen/ops/special_hermite_polynomial_he_native.h,sha256=ak9nPuraPehVsZ7rXjJV7v1dIUB9luaj3tZV6M6o2vY,1116 +torch/include/ATen/ops/special_hermite_polynomial_he_ops.h,sha256=Aj4zlDx5p7wuZPT3Y7T3chNc7x1OvjAXrfYJ1JoPHiM,4637 +torch/include/ATen/ops/special_i0.h,sha256=-khNt84H3rfjWoqhM8KczOQRd7QLMedK0CcGpwcxF-o,1067 +torch/include/ATen/ops/special_i0_compositeimplicitautograd_dispatch.h,sha256=ibY7ZlRGRZZ65j7WpN2ayj6wjEb2mfp2NXVoP29m2Ls,933 +torch/include/ATen/ops/special_i0_native.h,sha256=JN3m7TieWRUiWGMmGJPA2IDT988Q0EHhf9DeoscX_ug,562 +torch/include/ATen/ops/special_i0_ops.h,sha256=5b1gtOkc9m-iZw-_Ir-mCRZQw1nsa6JQoKEiWA7MtU4,1571 +torch/include/ATen/ops/special_i0e.h,sha256=4q841gPD4c9YlZxK-xJfZJSXnQg3BTTI838vTbUEJYU,1077 +torch/include/ATen/ops/special_i0e_compositeexplicitautogradnonfunctional_dispatch.h,sha256=NSQBbBZMMw7qtku-SGq3OKeAZtr8G8WeNuLBN6hAbrk,795 +torch/include/ATen/ops/special_i0e_cpu_dispatch.h,sha256=PZYXMweF5rHdNV56LKtYyIhHzQ-d123DOUMVvCgZ0i8,892 +torch/include/ATen/ops/special_i0e_cuda_dispatch.h,sha256=-5xBTZnY-Qq714DWuItlqQgOCuSc4jIYzDvJgqfws70,894 +torch/include/ATen/ops/special_i0e_meta.h,sha256=qyD7IOD3Xcaq1rUp8aN6PQifRYMlLIS8HEUU658Z7GU,572 +torch/include/ATen/ops/special_i0e_meta_dispatch.h,sha256=ntwsVqswnn85egNYDVgPU_pEkNWAtNIDrPgaQOWlL4k,894 +torch/include/ATen/ops/special_i0e_native.h,sha256=0tBG7zcN4XjzWUZkVELABVlVbkOX55mBpYciDoDRWGQ,611 +torch/include/ATen/ops/special_i0e_ops.h,sha256=Cq5HqKjdfl4Vk-d8b3PfihtmrhvKRmJxm6NLi_OM9ag,1577 +torch/include/ATen/ops/special_i1.h,sha256=NRKF6RBD63k9Dvboz9MKXDd2qdc-WqyofQSv6Nh5QzI,1067 +torch/include/ATen/ops/special_i1_compositeexplicitautogradnonfunctional_dispatch.h,sha256=fQxWBf1SGPf8nNY2M5yagDETxXeIjGkwUJd4IvdUzDA,794 +torch/include/ATen/ops/special_i1_cpu_dispatch.h,sha256=i4E72MJwNH617AOJcu4xUNqSJJwR32hA6RXpLBkmhVs,889 +torch/include/ATen/ops/special_i1_cuda_dispatch.h,sha256=5s4xuDgy7sscUnwtIPZukrcB9KORrj4iVZTj1Kjxxfk,891 +torch/include/ATen/ops/special_i1_meta.h,sha256=xw7MFROAemIKAzVFIWJJHDrmGd6-N9MPVs6RgBP5--Y,571 +torch/include/ATen/ops/special_i1_meta_dispatch.h,sha256=HeD0y25IzkJ51K16PQqlKQ5njfSwMRx7XvdYH0LySvs,891 +torch/include/ATen/ops/special_i1_native.h,sha256=wg5cS2ikvsdLkQqTI2QXc3ocXyFNX7ONEcryaxZaiNQ,608 +torch/include/ATen/ops/special_i1_ops.h,sha256=xuitmqUixg6WMENV0gpSr7NFAMs8O8c33HHx3z-knj8,1571 +torch/include/ATen/ops/special_i1e.h,sha256=NTzOYs9aPUsWYYobTxlcsipybWm4K-YwMPCrqUXoH8A,1077 +torch/include/ATen/ops/special_i1e_compositeexplicitautogradnonfunctional_dispatch.h,sha256=o05RBzmqTnWMQs5ONUUBiCrWUz_T4YvJihoQWInsC7Q,795 +torch/include/ATen/ops/special_i1e_cpu_dispatch.h,sha256=ibSYEnHh7G-MvopZMljLKQy8tpzgSZ3-ZCELTXPooAo,892 +torch/include/ATen/ops/special_i1e_cuda_dispatch.h,sha256=n_Me5OC126v09hW4lrzI1djyAlqp0CchnECh8a5mR5Y,894 +torch/include/ATen/ops/special_i1e_meta.h,sha256=JxlGQG9TAurU0QNGjBud236r7gEGKEjAGPjDOSu8kDw,572 +torch/include/ATen/ops/special_i1e_meta_dispatch.h,sha256=i0JPXilT0kpxzILbdUzJrXDA7Z8RzOkh7nr81lOhK0w,894 +torch/include/ATen/ops/special_i1e_native.h,sha256=keFQJSwXhFwBNwXTKahD_K-E5u78Ubs60D-2GretJDk,611 +torch/include/ATen/ops/special_i1e_ops.h,sha256=Ex_EPjhstLgHA-0YN2VuFrbYFjrsyDs9Ty9qBPxGC1A,1577 +torch/include/ATen/ops/special_laguerre_polynomial_l.h,sha256=NY4b5CCcBCpODpsv_AgU_0mZT1pdUn5BCpW_5cgbywQ,3063 +torch/include/ATen/ops/special_laguerre_polynomial_l_compositeexplicitautograd_dispatch.h,sha256=oL8oD-J6rBm1bMClvOFWRXtuUXMQPbsfhn3NXSErwv4,1384 +torch/include/ATen/ops/special_laguerre_polynomial_l_compositeexplicitautogradnonfunctional_dispatch.h,sha256=P2AYbfKeZoasu9IA5LnQXYMvKr4mcn4hSSrD9YhfTXA,832 +torch/include/ATen/ops/special_laguerre_polynomial_l_cpu_dispatch.h,sha256=8qWQhqj7r2vrbj4potQdc8xyGKhCzgCUqba-DeNcyC8,1003 +torch/include/ATen/ops/special_laguerre_polynomial_l_cuda_dispatch.h,sha256=xSUPWtXWQkUWh623zTcY-8q3QVW8D7koGmlIc0Pzx-s,1005 +torch/include/ATen/ops/special_laguerre_polynomial_l_meta.h,sha256=vb8ygrv3OKRZv5-GUm5dRBabeRQA50qGMAfwZmWODWM,609 +torch/include/ATen/ops/special_laguerre_polynomial_l_meta_dispatch.h,sha256=PU-mKJD_HbTwLXZP37dPraUziXr7V7b6j5uQO8Q4Dbw,1005 +torch/include/ATen/ops/special_laguerre_polynomial_l_native.h,sha256=_JFxxouopvY_pOplikicwhjtxYM5r6XfuP8VUsnQJ6w,1116 +torch/include/ATen/ops/special_laguerre_polynomial_l_ops.h,sha256=jSK1-YCRclFuEXMA9yUiEB5ntneZxo9-dii8iZ-39m4,4637 +torch/include/ATen/ops/special_legendre_polynomial_p.h,sha256=6dmJF8mxyAGK--Hz6iI1jQwOm_qdaENu8CSGnnwXoRY,3063 +torch/include/ATen/ops/special_legendre_polynomial_p_compositeexplicitautograd_dispatch.h,sha256=v4k_IW3h5mIVXC-Yld3gVAY0ZVSSzorYUEmcCw8zUmo,1384 +torch/include/ATen/ops/special_legendre_polynomial_p_compositeexplicitautogradnonfunctional_dispatch.h,sha256=WJ4NXiY-HODQcIPIeKKIqeCPGUSe-rtKCTG7eOoGJE0,832 +torch/include/ATen/ops/special_legendre_polynomial_p_cpu_dispatch.h,sha256=rMzkXojw_UFEbtv37ljvw39uSwCm_eneOUTSW8_ZPS8,1003 +torch/include/ATen/ops/special_legendre_polynomial_p_cuda_dispatch.h,sha256=6_aBcam1NOo0Rffz-2Onljww770_ifYYEh38tEsTRMc,1005 +torch/include/ATen/ops/special_legendre_polynomial_p_meta.h,sha256=XFtANfzHAG_AW69yaghHwQSJ6nXqeiI6Mih6CyXpZ80,609 +torch/include/ATen/ops/special_legendre_polynomial_p_meta_dispatch.h,sha256=F31HVELU-CO4jkvo0ia_REk_p1Ah24hHMsvgoF46eu4,1005 +torch/include/ATen/ops/special_legendre_polynomial_p_native.h,sha256=uATt9JOeqcjEWhxRw2t_ExzdFVwEsQ1_i3F8K8uKI4Q,1116 +torch/include/ATen/ops/special_legendre_polynomial_p_ops.h,sha256=HTYoTZx1v7W6oFrjN6mL_sGX77PV_4G2vQHxzpQB23U,4637 +torch/include/ATen/ops/special_log1p.h,sha256=gIB_VaMvzkMLhcM_9gkcl2KZui15TIk3z-BuL6WIoUA,1097 +torch/include/ATen/ops/special_log1p_compositeimplicitautograd_dispatch.h,sha256=dA5Md7NFpvtf9_Eq2S6Z9ZHG4BbGMp2mTOrtfv0WOCo,942 +torch/include/ATen/ops/special_log1p_native.h,sha256=fE77vhedrebUsoVLHUBDCLCxP7c32sn1tgiyeRq2CMg,568 +torch/include/ATen/ops/special_log1p_ops.h,sha256=SvpOV53AdxEa6ft5g_Fc4lHdwQ_49pPg8DYBvMG5ED8,1589 +torch/include/ATen/ops/special_log_ndtr.h,sha256=yLdUGv9xvFMT_sQx3jVKbKaMokZkovf0-6mLT89ba7Q,1127 +torch/include/ATen/ops/special_log_ndtr_compositeexplicitautogradnonfunctional_dispatch.h,sha256=iiv-Xu799BLI1CcYa-xBmNh87WmM9YBYQlvxx22cz1k,800 +torch/include/ATen/ops/special_log_ndtr_cpu_dispatch.h,sha256=aZ-t2T-N3-IKF47DPQGKE0Yg65l-z09ds7aRJahEN2o,907 +torch/include/ATen/ops/special_log_ndtr_cuda_dispatch.h,sha256=ll7Pxu5LEo4IsYAvHiG5wNe7nRSJSxiAtboO4iagr1Q,909 +torch/include/ATen/ops/special_log_ndtr_meta.h,sha256=M6AnVbaGIQR3XKcR9Wby784vFwv1Lv_Wma5VtwNTZJk,577 +torch/include/ATen/ops/special_log_ndtr_meta_dispatch.h,sha256=p-cV0kYYDijY4J1dmWjtKEdAey60AsndLeWH5pL7VNc,909 +torch/include/ATen/ops/special_log_ndtr_native.h,sha256=U6Bc1Imgi0LNZmRHmNtCiNRkCXH2MhQzI4Ew4DOSyoc,626 +torch/include/ATen/ops/special_log_ndtr_ops.h,sha256=891KXQgcU_b2RmGD0WI9bTyx4KAJ7nYSZKmEFm9MScE,1607 +torch/include/ATen/ops/special_log_softmax.h,sha256=MNb8358JyHUAM5MzcYKsuWBW3vmBSru6N_0aSaSNQp0,804 +torch/include/ATen/ops/special_log_softmax_compositeimplicitautograd_dispatch.h,sha256=OGLtHls428RQA0Pftp5csLjJnhd2JnDsTO-BxM66xvA,844 +torch/include/ATen/ops/special_log_softmax_native.h,sha256=K0C1hfGwyMe53ax9Ibfv_RuFqbh3JpahUsSNiMLhWME,556 +torch/include/ATen/ops/special_log_softmax_ops.h,sha256=shvYXFtRMTOdDQ5rHVvkwKxpK4HRpM4L85FJMYsXlTI,1178 +torch/include/ATen/ops/special_logit.h,sha256=CUHenJb_bYDznnBFin7tGyZ2oHJurCu1C-tUtbzzXTM,1280 +torch/include/ATen/ops/special_logit_compositeimplicitautograd_dispatch.h,sha256=vKqtq_hG87tN8H_xMTupuVnfooBgT1ga0VPxT3naSeE,1059 +torch/include/ATen/ops/special_logit_native.h,sha256=iICOoXImUc74CjSH_2szp9G07tdDOnFtDMELDysuHrs,641 +torch/include/ATen/ops/special_logit_ops.h,sha256=xq6yNVmzysDV8atQYtGMoes8s11deHSZ68VQ8qxxzJ4,1789 +torch/include/ATen/ops/special_logsumexp.h,sha256=ZQR2dt6iYFeUsHB3Kp_mrS_ouGR4NRSw9Zz13fAoLbI,1392 +torch/include/ATen/ops/special_logsumexp_compositeimplicitautograd_dispatch.h,sha256=mNTT1HyXqJ0v9CGT387VGXuERWo_ZOgAaGSt6rbUydw,1071 +torch/include/ATen/ops/special_logsumexp_native.h,sha256=vxrbJyniKCOuWUQEkFFLpI2ZsqPkgtf5e4Byz-ISQpY,652 +torch/include/ATen/ops/special_logsumexp_ops.h,sha256=XQZ0HqdjQyUjny8RyQAHvcYktpv3Gfy-rW9LXHVUt5k,1863 +torch/include/ATen/ops/special_modified_bessel_i0.h,sha256=JFapj0H1zbYWNDWuPXNTc8NpfnzBdRBxpdvvy-x0Ibg,1227 +torch/include/ATen/ops/special_modified_bessel_i0_compositeexplicitautogradnonfunctional_dispatch.h,sha256=pFTxQmlG8pJC5wjAVnCD2hF96-LxD9VhfYzncPFdNLg,810 +torch/include/ATen/ops/special_modified_bessel_i0_cpu_dispatch.h,sha256=OBkfCgQ-NHvj-F4xwBogc5kWLfZmg7iKfX84thPxVpg,937 +torch/include/ATen/ops/special_modified_bessel_i0_cuda_dispatch.h,sha256=ZD6Gc5h0xd2KfPrQu2dhB3lBItRroyDYc03KXiX_SOY,939 +torch/include/ATen/ops/special_modified_bessel_i0_meta.h,sha256=L8g_qr6IOC6kS5QNmv1LpSkP1S_cBVkKUP1CEhcv7Ck,587 +torch/include/ATen/ops/special_modified_bessel_i0_meta_dispatch.h,sha256=afJwqTFPtmST8QP15q0uBie5LuCrxi0Uf7vTKUZ9z0U,939 +torch/include/ATen/ops/special_modified_bessel_i0_native.h,sha256=EqCqFgpx1SgTZ7ni9hmUrWACdJ-Sw1m8gzKSsUlc_YA,656 +torch/include/ATen/ops/special_modified_bessel_i0_ops.h,sha256=_mwUOuNyJE8yO5xIHc-14jXQQpk1GqLfw1n2UhYoiZk,1667 +torch/include/ATen/ops/special_modified_bessel_i1.h,sha256=qA2R9sMe-PTIwpFA-Z617_5G_iHX78Yx5wCROdWUleM,1227 +torch/include/ATen/ops/special_modified_bessel_i1_compositeexplicitautogradnonfunctional_dispatch.h,sha256=wM70WeVPkJ6u_izSY-YHTeYkNRsZN7ghmJ_Qgw4krkw,810 +torch/include/ATen/ops/special_modified_bessel_i1_cpu_dispatch.h,sha256=w-6Cr8smBL9DcjILWiwxEpeR0vtpRi5kYPC3SeRdkVY,937 +torch/include/ATen/ops/special_modified_bessel_i1_cuda_dispatch.h,sha256=UOpJamzlZR8zxJXHrsN2YKAi8KZBBxncTvAjGq5mBxo,939 +torch/include/ATen/ops/special_modified_bessel_i1_meta.h,sha256=A9KAjepr02AmX_TlP4DWNb59oLfgw-7eGFNpT372Yxk,587 +torch/include/ATen/ops/special_modified_bessel_i1_meta_dispatch.h,sha256=C-_cMGprsHLXNMaPg0xOHLyHP4a7Q4yzWMAsUqNQxnY,939 +torch/include/ATen/ops/special_modified_bessel_i1_native.h,sha256=QHh06fmNiUkK3ivH06ebDB5E1iQiySIDSkLX7ce1GXs,656 +torch/include/ATen/ops/special_modified_bessel_i1_ops.h,sha256=M88rNd3WBZHBiy-jp_kGOKI_1qpc0nShcr4fDX9Qjjs,1667 +torch/include/ATen/ops/special_modified_bessel_k0.h,sha256=TtMRQrdfRy9-Hk0ejI-zp4THeslbcf7vv7azbAlC_m4,1227 +torch/include/ATen/ops/special_modified_bessel_k0_compositeexplicitautogradnonfunctional_dispatch.h,sha256=BYumZpNsKhBrXX_J3eMifksgroqksjwnkSec10VPqJI,810 +torch/include/ATen/ops/special_modified_bessel_k0_cpu_dispatch.h,sha256=cE8X1_CwSFgWTO0pUqkHC0L5aNGD-JKFSWbQBCIZLPg,937 +torch/include/ATen/ops/special_modified_bessel_k0_cuda_dispatch.h,sha256=BMacjV3y9H5IY81d42d9SL-gVcas3HALmemIqxWgQ9A,939 +torch/include/ATen/ops/special_modified_bessel_k0_meta.h,sha256=hVW4bslsfjR1WQSkYWNAV0eOEmvU5nuwVhz-Qjm0lG8,587 +torch/include/ATen/ops/special_modified_bessel_k0_meta_dispatch.h,sha256=DgSCw1RtI4P7EFRrwYucRy4DXKZ07fWHts-C97x5qsU,939 +torch/include/ATen/ops/special_modified_bessel_k0_native.h,sha256=gL5W29KX0DCEHQXqTY88xrJNm6sA9NG9Nug_iujkgY4,656 +torch/include/ATen/ops/special_modified_bessel_k0_ops.h,sha256=ueB3xcDKWGA1Zq0RRtmC1Ohh_cSkOvU34ZwARqJDtn4,1667 +torch/include/ATen/ops/special_modified_bessel_k1.h,sha256=S-ZjzAOJ3n8dwmoRUTNs8YoWvyg-4y_2hIii1x74Sks,1227 +torch/include/ATen/ops/special_modified_bessel_k1_compositeexplicitautogradnonfunctional_dispatch.h,sha256=iKZaHnz44a25gWntfGs7NxDc3a4BQL0XmoERC02CRjY,810 +torch/include/ATen/ops/special_modified_bessel_k1_cpu_dispatch.h,sha256=dOLY08GTl629mUEiRTPl_iJO2Hc4TmMu4j_mX3hR_Tg,937 +torch/include/ATen/ops/special_modified_bessel_k1_cuda_dispatch.h,sha256=XKAPyZU2eE0VEQXdGQ5CPpH0twKZV2XzkyeVENoGZXk,939 +torch/include/ATen/ops/special_modified_bessel_k1_meta.h,sha256=Bjt8hRFz3jykEtYqvrhwLmOnawWekbV3IbmMi8Hmg8I,587 +torch/include/ATen/ops/special_modified_bessel_k1_meta_dispatch.h,sha256=6IJ0EyYaXDwZupjgU6vnnEbrRQl7e-Q51IOkXRMtxyg,939 +torch/include/ATen/ops/special_modified_bessel_k1_native.h,sha256=XduDoH810lZlDgpyeqto1CT4_UYMIWaGqTIpT0NUWzs,656 +torch/include/ATen/ops/special_modified_bessel_k1_ops.h,sha256=2vZ_6FLC8TPtJsFTh5UlnYpDASAXjamub-Hz51_LpYM,1667 +torch/include/ATen/ops/special_multigammaln.h,sha256=t16qS2-NG1lv5isQ1kyV1nAvcnnnvIdw9NpoJg45C8o,1230 +torch/include/ATen/ops/special_multigammaln_compositeimplicitautograd_dispatch.h,sha256=82J93BYLEfBwmbqgSMnFghLX8DMk0T2u0wm_hq0mz-g,996 +torch/include/ATen/ops/special_multigammaln_native.h,sha256=xd-TzigqNXPc6S-nYScO9QCfLGQAJqCnlxHhuBUcwvw,604 +torch/include/ATen/ops/special_multigammaln_ops.h,sha256=cUaKR8t6dQZGsQ0c8Ua48XnwhuIo8AblWaw01JVyfEs,1707 +torch/include/ATen/ops/special_ndtr.h,sha256=s0Jm8COmS44b6let8h8K-Bjs2fXHU0Cx5V-thCXmFAA,1087 +torch/include/ATen/ops/special_ndtr_compositeimplicitautograd_dispatch.h,sha256=H82QwdT47klwN0toZ6bPtyQHFdpO_jWCpYVywACRzrA,939 +torch/include/ATen/ops/special_ndtr_native.h,sha256=xsEP1XBw73f62JHPx0Ld5LLZHj507q9LcXx0A6H9AkM,566 +torch/include/ATen/ops/special_ndtr_ops.h,sha256=trRn8VV_ITKXulZwVmLAiUl4MoOoIQ_nsQAOpE8owyo,1583 +torch/include/ATen/ops/special_ndtri.h,sha256=W4igqSKSxdQnY6nH_USbNGqSkm_WVoBs_yqJxdJsQ18,1097 +torch/include/ATen/ops/special_ndtri_compositeexplicitautogradnonfunctional_dispatch.h,sha256=ENAJDN9CjseR0VgQFLywB8a94r20_LeNIsihKRqS0u0,797 +torch/include/ATen/ops/special_ndtri_cpu_dispatch.h,sha256=bM_BCYc949KX-D5eLsQws2ymflbbOvaISmEBAzNIA6I,898 +torch/include/ATen/ops/special_ndtri_cuda_dispatch.h,sha256=f1MFG28KQOXpkuDxKGjaVmtnhHGiyxniq2Qo09HYttU,900 +torch/include/ATen/ops/special_ndtri_meta.h,sha256=4ahw6AHoziSJwdGU7PjLnnNjRgwsPNAQYrB8PgDko64,574 +torch/include/ATen/ops/special_ndtri_meta_dispatch.h,sha256=x6pXJRL1UkZkXRIoJO4Tw6JWpULPkQLcVkFDuw3Cw_I,900 +torch/include/ATen/ops/special_ndtri_native.h,sha256=WwvrxAaU4IFPiddokRQS_Q40UvsnarL8oe-YzHfDKAk,617 +torch/include/ATen/ops/special_ndtri_ops.h,sha256=udoj8SwA12YyD6f3KXVpYNHj_97cOkRA5c6QimB5Z9o,1589 +torch/include/ATen/ops/special_polygamma.h,sha256=lwcZuQZrGA0tue9IrMw9Iw4q_VDLdkWqeKQU5CJnfY0,1200 +torch/include/ATen/ops/special_polygamma_compositeimplicitautograd_dispatch.h,sha256=URzm656vMhQ0cEV6jnK988gAb76F7OkveQE-QsELV5I,987 +torch/include/ATen/ops/special_polygamma_native.h,sha256=2SIoW5MRBCedv0TR5nngPSJouQC8R0XcnVAlcAXsbMA,598 +torch/include/ATen/ops/special_polygamma_ops.h,sha256=tnRieCyEf2n9nvMMHbLry359DEBOI87VrCVX7UgGAhQ,1689 +torch/include/ATen/ops/special_psi.h,sha256=BkDR4-OmydPwxd9D443ngOOJBK2_h_mx6GCDZs9VJBQ,1077 +torch/include/ATen/ops/special_psi_compositeimplicitautograd_dispatch.h,sha256=fV2_XWltrtP-QOC2biahvv4YVPev9VFN_eI2uAskoms,936 +torch/include/ATen/ops/special_psi_native.h,sha256=du_OjamGGtgNw3gzxN0_xzyDYTCY3I0vBnUi6jXblDc,564 +torch/include/ATen/ops/special_psi_ops.h,sha256=vfonPEJLHzw_ckSNtPMtTmVQ2ARgXYljslQSi5C-iQc,1577 +torch/include/ATen/ops/special_round.h,sha256=yhTiXqEoC_C9UK6mywgWdh975tiqx8Bdnk71TgHB6rM,1236 +torch/include/ATen/ops/special_round_compositeimplicitautograd_dispatch.h,sha256=sXOLVpyyJ9aGYKDH3QlsyzYM-YujyOZSfYOrp_cPtUQ,1000 +torch/include/ATen/ops/special_round_native.h,sha256=EqKL9t7b2fI3edwI4IGmb7sNwSiV-Xsenua0zyHWocE,606 +torch/include/ATen/ops/special_round_ops.h,sha256=JEOoF20oDfyHVnQa56nMi0XrrfGx-7XCVi64_EPcnPA,1714 +torch/include/ATen/ops/special_scaled_modified_bessel_k0.h,sha256=xxMU1Vg_5xwrf51oMxSXa83q0kYUZ7wpltaWMh81EiA,1270 +torch/include/ATen/ops/special_scaled_modified_bessel_k0_compositeexplicitautogradnonfunctional_dispatch.h,sha256=RVFjNrgqHV1OUgeJLheVcKc-1i6XELYH8u_HR3-8Ewo,814 +torch/include/ATen/ops/special_scaled_modified_bessel_k0_cpu_dispatch.h,sha256=67CKI6AU3pdHE1Q1KgIknBFB8khk4LCIHtFmqOkrLSA,949 +torch/include/ATen/ops/special_scaled_modified_bessel_k0_cuda_dispatch.h,sha256=JRc29qO6gMHQxWPOJswm9IIaOFSd78WcVBdWUOxKrXw,951 +torch/include/ATen/ops/special_scaled_modified_bessel_k0_meta.h,sha256=9WQDqBjMJK8qHgXRewum5FbIaG_ota9woE1qrWZamOA,591 +torch/include/ATen/ops/special_scaled_modified_bessel_k0_meta_dispatch.h,sha256=CH8pvStEioadt1rzmChjQatl4Smzmz8vHxjffF3IGQw,951 +torch/include/ATen/ops/special_scaled_modified_bessel_k0_native.h,sha256=wm9ZbNN7RsDb9anjApzXMZ-sZcpzxHL2LSB9ZGHGgAA,674 +torch/include/ATen/ops/special_scaled_modified_bessel_k0_ops.h,sha256=KUUY9DjwUgq-iXNMV2GiRTXIXJyA2Q3NXjQW-ZScFB8,1691 +torch/include/ATen/ops/special_scaled_modified_bessel_k1.h,sha256=dXsOSDJAByDxfG58nkZ0BcxeXwzr3QROWnJcIFQHrZ0,1270 +torch/include/ATen/ops/special_scaled_modified_bessel_k1_compositeexplicitautogradnonfunctional_dispatch.h,sha256=ZnQ8DxoZW3sfdE-iZg6iTiQVHvpSsQZpAt6BrDevO-k,814 +torch/include/ATen/ops/special_scaled_modified_bessel_k1_cpu_dispatch.h,sha256=uiB4nmbEBvF3xFj1g9O9aaosYNs9v1HStOJdqkQGbzY,949 +torch/include/ATen/ops/special_scaled_modified_bessel_k1_cuda_dispatch.h,sha256=AQ-F41GWwijoMGAUz1ESa-ClVCz5AZmYDhYhxka4tb4,951 +torch/include/ATen/ops/special_scaled_modified_bessel_k1_meta.h,sha256=1QYCjneZS4y-0oDVtJr7JQ6Z-e4r1Eu_-jYVQi22ZeE,591 +torch/include/ATen/ops/special_scaled_modified_bessel_k1_meta_dispatch.h,sha256=DeBURyTBajm0dgXy8ybjKOQXJJCFHiajuxkrejqqQLo,951 +torch/include/ATen/ops/special_scaled_modified_bessel_k1_native.h,sha256=rRxpwhvxF3mmC3-4Fs-0BztY2Zf0_N64jcO2Eo-01K8,674 +torch/include/ATen/ops/special_scaled_modified_bessel_k1_ops.h,sha256=u078Iw9hnts7p4sQCcojc4frxqOeR6wnriSckMEURDQ,1691 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_t.h,sha256=pPWclULaWhv3riKkKlth5iQZvHAups0gjdDSUXqh3l0,3315 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_t_compositeexplicitautograd_dispatch.h,sha256=6xazY4mCNaB8N5FL__jMJLaZbtH2-igmFbBTW__yMOo,1438 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_t_compositeexplicitautogradnonfunctional_dispatch.h,sha256=nZTdl2AAQWxlw7eZrNQZolc2nTeiQogH8xA133xCAPQ,841 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_t_cpu_dispatch.h,sha256=m_uPktXJzxp12TpOqaFYAUu-mOXt34YvDhpTYHDd_4g,1030 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_t_cuda_dispatch.h,sha256=E-mdsglRgIQeI5o-C-Jh7LTfJccV8lq0gh0XAuPzdyQ,1032 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_t_meta.h,sha256=2kh6-Otz-_wXXNygN55AW2g1Qk9A2LhaR_hWGRuoIJ0,618 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_t_meta_dispatch.h,sha256=fuCarhRt0LrpUs4gDDN8LZ2LxP7HXkJIBeaeyVpBupY,1032 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_t_native.h,sha256=oTf_6ZmDrF6vDKIoAVWbNv6nUL0IHBe2gWgawhO6RL4,1179 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_t_ops.h,sha256=iAhOFjiy7AgYB9eXv6-mCy6_MPPMY341IDO3uHezBrE,4799 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_u.h,sha256=R3lHpKVMN__sCFNl-JpTgpOe-jMa8vvRm1ouZ4K70pE,3315 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_u_compositeexplicitautograd_dispatch.h,sha256=q6ZDPq87qBHd_kIAnkgH2m2GlLfvkOwfOhv4AeEQI5o,1438 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_u_compositeexplicitautogradnonfunctional_dispatch.h,sha256=H1cJmN63ef7krFKYeNB4CXq1502TAeBGbp0h5xcqUU8,841 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_u_cpu_dispatch.h,sha256=5PysWKeTbh34L2PIxpF7ut1WMwR2HC9TKJ-AwaGFWH8,1030 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_u_cuda_dispatch.h,sha256=vefstgoAY3rLduRsJZLC0OMHefS8G3gqUwy5vyYFoTA,1032 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_u_meta.h,sha256=CT67-kXuX49_hO4UtvWkZCQoJnfmw89zQ-Pw8rYzxfo,618 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_u_meta_dispatch.h,sha256=6xf-klwCMubqJd7TZJmYRCXRDuCX6pLtz_w5d6oAQq4,1032 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_u_native.h,sha256=Xkmfcs_PhGud6gt2OFDhzhFTLXCgfxPHHoYHJHmBUX4,1179 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_u_ops.h,sha256=Loxz0F-N1wntWBOOps-OARIymNTi0zO8HTiVL-7_AGc,4799 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_v.h,sha256=FvF3Tx6ilprbmOCEQVFybu9NkV2Qov40PLz0Is7pf8U,3315 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_v_compositeexplicitautograd_dispatch.h,sha256=sOpTiUvCOsBRzDbDh-lBZvKAqhBVjbmRozzwJrgHV50,1438 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_v_compositeexplicitautogradnonfunctional_dispatch.h,sha256=N87bt3LEf9gyzJ-RTHCUTPQUexeCjwhQZKr9uAFnoes,841 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_v_cpu_dispatch.h,sha256=7uzy9rIrtMbiq99L1i67CIVFevYFxr5UTism-DR1Y9A,1030 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_v_cuda_dispatch.h,sha256=wlY0h1C7igIHnWD7LJss0mUmOpSh8WoqRNJTr1kpErY,1032 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_v_meta.h,sha256=9BW8b2ntSZ_Z-nfatCGyliVgEeMOBGc7KlMzbvgVlRQ,618 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_v_meta_dispatch.h,sha256=IjmVgD6qvGYrncyDyhEJal7PDhovS3lLYsK3gHvg_Gw,1032 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_v_native.h,sha256=9QK05UglLR7dsW7ytybvyZVk5Z2OL6Vb7ijXBVknkiI,1179 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_v_ops.h,sha256=0lbYhrNOQY9w_jw9AUcRs5GX1XXtyi7WcFczm6visIg,4799 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_w.h,sha256=lHSDg6R6G3DMTkqWLrLPRPWAsfOie3F5Jdn8QvZKY_4,3315 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_w_compositeexplicitautograd_dispatch.h,sha256=qoWodfM9DfmzjEKUE84lF6YTCCCpEPVa3JCz1J7e_P4,1438 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_w_compositeexplicitautogradnonfunctional_dispatch.h,sha256=trk_qFc1l31k8WiUOYb_slgS0Jn62XaVzUWna9KFMH4,841 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_w_cpu_dispatch.h,sha256=7k3JzOTdAuc5_yEZ2mCLEALLJU_d6mdHTNTKkZdiJpY,1030 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_w_cuda_dispatch.h,sha256=hkGprVCTc_LIVGGHfqLNc7kioRiLMkqrw5MD_WOXMdI,1032 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_w_meta.h,sha256=1-Mfgt_GpRLDhhtN4U0WvFHpD0s5Q76FcK90b6PU4Eg,618 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_w_meta_dispatch.h,sha256=1ahY_gKUyuI3wtsQEzrMQQCu6jA7BRibn0laIxjFTiw,1032 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_w_native.h,sha256=9VTQYhcHR1dAq1Xo9XM6giylBEeK39ef5lmRAeEuiyw,1179 +torch/include/ATen/ops/special_shifted_chebyshev_polynomial_w_ops.h,sha256=5pc8qVGMCLjEl1S3GWOU-gDKWaG55Y3-DlsMOTsQEN8,4799 +torch/include/ATen/ops/special_sinc.h,sha256=IAJxobZWgwlYyjMX88T_HCrlgtnOw3gfp0CD50bKq6s,1087 +torch/include/ATen/ops/special_sinc_compositeimplicitautograd_dispatch.h,sha256=-92rIAhFtCSwofQNa9HWSw2WgXE9kPsrQU03c1rULF8,939 +torch/include/ATen/ops/special_sinc_native.h,sha256=d0sTYFMc9iuW1tUKfmAMDkaDNxzrNfDWYf5pqdHTRFU,566 +torch/include/ATen/ops/special_sinc_ops.h,sha256=7mmJld3SxxT4lvtyQI9V0iHE_kbuiEK6gmW4cmSw0r0,1583 +torch/include/ATen/ops/special_softmax.h,sha256=qJxU_1G-bqEesjvCNFVq6NLVwT6uPKeHyTU7WNakd3o,785 +torch/include/ATen/ops/special_softmax_compositeimplicitautograd_dispatch.h,sha256=DK7PavCzVgDkNtjIPf-G2IAAiaceqaTxKmJzXiTIohE,840 +torch/include/ATen/ops/special_softmax_native.h,sha256=-Zz_fx-VPCNfI9hSOvi_79Uose3MwIlAgkx9lGm6-Ng,552 +torch/include/ATen/ops/special_softmax_ops.h,sha256=xNE4dCSev3vqmJkAhIveaXtrM12W9DuGyIAQl2wts5E,1163 +torch/include/ATen/ops/special_spherical_bessel_j0.h,sha256=A4fHnWoMIIjEmiJex_d5Lq4MYO2PFhAjZ4RwUpGGLVo,1210 +torch/include/ATen/ops/special_spherical_bessel_j0_compositeexplicitautogradnonfunctional_dispatch.h,sha256=wdGw9ThRcla9vNgNf0M2vn_cjaDxoU5lq20qPUs91Ks,808 +torch/include/ATen/ops/special_spherical_bessel_j0_cpu_dispatch.h,sha256=LZqxP_ytkHZQr0As-PXnIjojscydhpsqcE_AocUMHIM,931 +torch/include/ATen/ops/special_spherical_bessel_j0_cuda_dispatch.h,sha256=s_b49esmXDVuiSBTxspI2M2y5ddtIjV9mWxih20o65g,933 +torch/include/ATen/ops/special_spherical_bessel_j0_meta.h,sha256=zvnQ1Aq6wwEsciRJQraSy5_cimvoxmnNgq6UTelsRVU,585 +torch/include/ATen/ops/special_spherical_bessel_j0_meta_dispatch.h,sha256=UrKP5GGlqlsMDnrdJ0dTSgvBk8b5LRUT8Aal7F59MBc,933 +torch/include/ATen/ops/special_spherical_bessel_j0_native.h,sha256=O5FSM9Pv1ot2nEBDVGgGwLXvjeQYr3oIb-W73RnyU04,656 +torch/include/ATen/ops/special_spherical_bessel_j0_ops.h,sha256=lywGoEIHyaV4xdTRkMPkxdz-WgnMQjV_49W-7IZM-dw,1655 +torch/include/ATen/ops/special_xlog1py.h,sha256=Q5IQ0OW2TD-ncKu7AtmubpU2gDmqFSEOPP83eRdYUjc,2902 +torch/include/ATen/ops/special_xlog1py_compositeexplicitautograd_dispatch.h,sha256=_gDw2kWKn4upryrgw9vOGT8FLgraxtOf5-ZI7wL7MRc,1342 +torch/include/ATen/ops/special_xlog1py_compositeexplicitautogradnonfunctional_dispatch.h,sha256=jxmxbeBWUZyFGaNMAlEhiV6DbgQIXjjPqng0dQkvBgM,825 +torch/include/ATen/ops/special_xlog1py_cpu_dispatch.h,sha256=S3G8lT6TRFjRiRTjwbQdPJlW_6ytAHf2yRC4hTVVpMs,982 +torch/include/ATen/ops/special_xlog1py_cuda_dispatch.h,sha256=yeqY-LOPFXPH7eLHXGSEz3RryazYgXI5NeofG-GT31E,984 +torch/include/ATen/ops/special_xlog1py_meta.h,sha256=A8ihHAa6M24j7pP-oUUCs2xp12Qq2Y3RQ3xwvlSP4bY,602 +torch/include/ATen/ops/special_xlog1py_meta_dispatch.h,sha256=Fj4QClUHtvPhmxkaRMzBQfbu0dvQHZN7ONLnN-4gqEo,984 +torch/include/ATen/ops/special_xlog1py_native.h,sha256=p6LmyQNgFUNA5c9PIqRPVY48C8ikR7eXHfZR8H4-97I,1053 +torch/include/ATen/ops/special_xlog1py_ops.h,sha256=61rWcSJAm516SyqeH4b-gbTqtp5Ua6JYYk39vqNp5ic,4553 +torch/include/ATen/ops/special_xlogy.h,sha256=j3iXUtvyQWF1yoZF7nv3Q97eAH7ttU1ch7pcg-1RcwU,2846 +torch/include/ATen/ops/special_xlogy_compositeimplicitautograd_dispatch.h,sha256=BKjKr0lkc6Tva0JTdzlGlx6inXkKWaYKRtu5ucxZ2WY,1640 +torch/include/ATen/ops/special_xlogy_native.h,sha256=CiXDdaVV_Wt2lb2bTTMKE_qR7dvNCJvhw1-3wGC_NYw,1016 +torch/include/ATen/ops/special_xlogy_ops.h,sha256=LNHPOdSCBS4GwwgUZaOSWPSb2t9H6j1MOZERTONkfFw,4517 +torch/include/ATen/ops/special_zeta.h,sha256=jisLVFDC55C68FYsP1YvMolkYLWlgZBa6a1hOFvcPGQ,2818 +torch/include/ATen/ops/special_zeta_compositeexplicitautograd_dispatch.h,sha256=ehMK90ElYBTqmC1rabkLsK6TIGH8_4dZ_d9gXoVLMF0,1324 +torch/include/ATen/ops/special_zeta_compositeexplicitautogradnonfunctional_dispatch.h,sha256=BrOByt3V3pxs7dPg41B5H3uJdFsiqG9RTvZeiHcuEqo,822 +torch/include/ATen/ops/special_zeta_cpu_dispatch.h,sha256=F9rNT_iWm0DDIsbtMqqpmoEtmFwxtnPwSKlgPacligc,973 +torch/include/ATen/ops/special_zeta_cuda_dispatch.h,sha256=_nRfDkaHmPPaHTKY_cNSr60lkuQh6ydQkZX-dA2M1WA,975 +torch/include/ATen/ops/special_zeta_meta.h,sha256=jTKIugp8aVPjYUyYO8NlcbLQCPqTSOFDsgaOojG2d1g,599 +torch/include/ATen/ops/special_zeta_meta_dispatch.h,sha256=X52eMKbIW19vkkcFnfnYlG_p9gQ5K3XJZnBiBj-P30U,975 +torch/include/ATen/ops/special_zeta_native.h,sha256=G6h_DwqpsAiSLFULIk4kYNryGEFJAPHSGmxTLmnXEfw,1032 +torch/include/ATen/ops/special_zeta_ops.h,sha256=lI41_2KrP05iAFqlx-Fw7GHxSLDubbk4C41wxlEzOG0,4499 +torch/include/ATen/ops/split.h,sha256=vs3SP5vgqbc_v3PAE-ZNUs78WBLEwVVMHOGsFH3qnso,2709 +torch/include/ATen/ops/split_compositeexplicitautograd_dispatch.h,sha256=IoO99_TxuXApxAA34yqo4UwJrz66fF32QSa81-50MLE,927 +torch/include/ATen/ops/split_compositeimplicitautograd_dispatch.h,sha256=yShvDGMSibNucwcyJaMl0Z45poWFlDz77thn-G1i3QE,943 +torch/include/ATen/ops/split_copy.h,sha256=tfU43P5vCrx0plrF2pii8D6VVlY6hXztgTrDCNOvaY8,3947 +torch/include/ATen/ops/split_copy_compositeexplicitautograd_dispatch.h,sha256=iePmmIZybJDIg1z3DFLO8Dx-k4TsT2fLne_3zhylJ2Y,1174 +torch/include/ATen/ops/split_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=1Z_ZngoFAPAqvahJuvhzEAYKvqUSiYaBb3sNpmNWecc,963 +torch/include/ATen/ops/split_copy_native.h,sha256=Vx8nwITzJ85UpRbQTCIubAlkszoN3ewTWCSF2_0Riwk,664 +torch/include/ATen/ops/split_copy_ops.h,sha256=EmZVIcVI8CdDe1TkVFrSmufK5pqp9p37feAuNxF4Hsg,1887 +torch/include/ATen/ops/split_native.h,sha256=7PUmF9km2HS179k1Q6JhSVwNQAtU7SwCJhPpVwOTTm8,647 +torch/include/ATen/ops/split_ops.h,sha256=JVyfujqYeyrT7Fu2TWgfGRQm_tukRJLd7ZX-ZP_A4ZE,1882 +torch/include/ATen/ops/split_with_sizes.h,sha256=pZi61eP2Vl6XeIuZzGFq5PYcsxQaaVPL_Lh53mS35nQ,1741 +torch/include/ATen/ops/split_with_sizes_compositeexplicitautograd_dispatch.h,sha256=ZLqTTZeLViBbeFfMde3tboq_LN_DM_YorYGmAV0L2XM,967 +torch/include/ATen/ops/split_with_sizes_copy.h,sha256=egDOrEQ9DZQ44zdO7MEtEaWWu_3atnAuEQYgUr71Erw,4456 +torch/include/ATen/ops/split_with_sizes_copy_compositeexplicitautograd_dispatch.h,sha256=h8-c2MPRMEgJKMxB2DxkMxvBDItB5arIFhge9AhrpOc,1254 +torch/include/ATen/ops/split_with_sizes_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=voL3MYIKgAW0aOb9BKoQX17weTprRc5tWkdKVJwR00Y,1003 +torch/include/ATen/ops/split_with_sizes_copy_cuda_dispatch.h,sha256=gLuYeUlzigRs6Ris9TvjblyJd6WlSs6FAIxpS1AIwbA,1212 +torch/include/ATen/ops/split_with_sizes_copy_native.h,sha256=gEfnEnjNviGdhUdRgBf1L9qj_wL3caP8uEHDhjO_yLU,824 +torch/include/ATen/ops/split_with_sizes_copy_ops.h,sha256=a0wRk3FFPdiDyA6Cje_nr0YbVILECU6lP4D1hBCSNaM,1970 +torch/include/ATen/ops/split_with_sizes_native.h,sha256=vTXuC-0_f_uYaSchzCzU0zinmhpTOTX6HUOtBc0RruU,675 +torch/include/ATen/ops/split_with_sizes_ops.h,sha256=U7E3rxuvls-lHQNTB65a6-SO5q8KM9hdDrSwRWAJj5Q,1200 +torch/include/ATen/ops/sqrt.h,sha256=z3JkVDb37ZRKxqH_WEEAUhOlhC82O1WUNxWUy4fHcWE,1143 +torch/include/ATen/ops/sqrt_compositeexplicitautogradnonfunctional_dispatch.h,sha256=97jxEw3IBLKh7rAWcVQM4cFD7DYbQSPBouJn6uBZt6c,837 +torch/include/ATen/ops/sqrt_cpu_dispatch.h,sha256=FIzuC9ibPlGlRukNNXHYXwHr4uGhIuJIC3e5oc0MflA,920 +torch/include/ATen/ops/sqrt_cuda_dispatch.h,sha256=AG_H9LxQ22aS-YI9CaAwMdxvOIH-sk2oLT7mkOpkU4I,922 +torch/include/ATen/ops/sqrt_meta.h,sha256=Y1l2GGiSofPSNmwIMMdEiOMthQ_iQQRDZbuV8GIrG6A,565 +torch/include/ATen/ops/sqrt_meta_dispatch.h,sha256=BXqcvxE3iUMNkgelIAUU0UWcfXnMMyXnOFmcqRd_4PE,922 +torch/include/ATen/ops/sqrt_native.h,sha256=TYYmbONYfRSPwKXUcGVIR3jIdoIeWh5hJR0YAvhsArg,1063 +torch/include/ATen/ops/sqrt_ops.h,sha256=z-7Ai2DX_-LVlntE2lVTq8fh5ncOI93_gmxiyDyWwPU,2028 +torch/include/ATen/ops/square.h,sha256=P9k6vnO7WGqMGATUGryBKdQax622IDGlkCd6IWuhfvs,1169 +torch/include/ATen/ops/square_compositeimplicitautograd_dispatch.h,sha256=ArFyRehnUpDPKiX1cyyjno_6Tyecl6SC4XegaUSR2VY,972 +torch/include/ATen/ops/square_native.h,sha256=7G8VETBMhWDOIUoVgn23BwwM3hANh0RlPCkaUjNXbZU,605 +torch/include/ATen/ops/square_ops.h,sha256=xmGUzfaJToMHRycLx9NxvTmBFsHbzww0cFSHnxCnl2c,2046 +torch/include/ATen/ops/squeeze.h,sha256=j6gUGVzSSWG4xLODPD7S3CMQxvCawFigquEHcBiN0no,1212 +torch/include/ATen/ops/squeeze_compositeexplicitautograd_dispatch.h,sha256=JedZgFF9yVWNAk5y50Ry92u5MBpkVHswD5ZujA-roTw,1099 +torch/include/ATen/ops/squeeze_compositeimplicitautograd_dispatch.h,sha256=S9SO_l3wPUVxsrUVq6U_cIwCZonl40hAe6FNk_UeNAU,851 +torch/include/ATen/ops/squeeze_copy.h,sha256=tWhNSuuxaJjKzZuOuxMd-SDo3T-P-SX7k5BmmItJe3g,2491 +torch/include/ATen/ops/squeeze_copy_compositeexplicitautograd_dispatch.h,sha256=XOOhc9zI2HTk01FwjEtS1cXPWfHlT57Cqqq1c0pGRpo,1285 +torch/include/ATen/ops/squeeze_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=qsoDUAwDFa6Tb6Vh9IYW5iOBp6kM2PgBAZ8i4Fn0KFQ,950 +torch/include/ATen/ops/squeeze_copy_native.h,sha256=JlK7tYEgklM8132hDzWEWMhVrMy_9RmqAQMK_WT9dAQ,940 +torch/include/ATen/ops/squeeze_copy_ops.h,sha256=twuI1ZE-V9MWYMj4nWvORU68RflYbhp6CNk7Nz0gYds,4115 +torch/include/ATen/ops/squeeze_native.h,sha256=MSuCtw8G9kXtW6p9UiQDwh8_JiQP_sayFJgVRvrjka0,1409 +torch/include/ATen/ops/squeeze_ops.h,sha256=Lw2MMqxTrKYPFHIpeH1-rP30cCDEWQRLYYUroJnnZpA,4945 +torch/include/ATen/ops/sspaddmm.h,sha256=W8wS6Gajs2DhUS5QXp_fnCyOKPV85VQQs9-meAIpfL4,1607 +torch/include/ATen/ops/sspaddmm_compositeimplicitautograd_dispatch.h,sha256=nr_I9hBUFBQmfZOvwWeH9zLuQyBntS3slyqgcUqB8JY,871 +torch/include/ATen/ops/sspaddmm_cpu_dispatch.h,sha256=nAn1kOw6aJaflPQADEzUEiHtTRMxgKdbLCs6vxwsX28,1033 +torch/include/ATen/ops/sspaddmm_cuda_dispatch.h,sha256=qQrd39YejwtWGJnHD0VRTv1YWLFHgvWsgUpL9ylb9Yk,1035 +torch/include/ATen/ops/sspaddmm_native.h,sha256=6Km5-E7004oHuwr2c8cdtixm6w10J0ZgkUaGj8ygT_8,1349 +torch/include/ATen/ops/sspaddmm_ops.h,sha256=4a9FqzYNmyfeNlCDLv7FDzC_eBmaSNPNUK_CsIcl3XM,2240 +torch/include/ATen/ops/stack.h,sha256=MNGygaJ3ZT5878AZ7sSVs_w4_3dzcMzAU4-4XCe8Kbc,1129 +torch/include/ATen/ops/stack_compositeexplicitautograd_dispatch.h,sha256=OXt2k3jhQ4jnVGNOl8MbEMQYqXNwA__R6NjcHjIst-U,958 +torch/include/ATen/ops/stack_native.h,sha256=QE6WoL_82j-AT-mAu0irZIhHRghKLlX49kcFPL4zZFc,578 +torch/include/ATen/ops/stack_ops.h,sha256=6swrdiYcz6uSL64LpY9HVGpr8KJQkzyOBaFvAL_dZiw,1631 +torch/include/ATen/ops/std.h,sha256=aZhj3hA2E7nchFlV_rriF9njefeMYNkenDjQdjT8z8Q,4871 +torch/include/ATen/ops/std_compositeimplicitautograd_dispatch.h,sha256=WhKbemRRHqqWk6ZMKFLA2peKQ9PHnCImXOb4ZLHYElU,2021 +torch/include/ATen/ops/std_cpu_dispatch.h,sha256=91LWqGLXL4stVSiWJNgCiC-fVogJcgT5iraq90Ybayo,1213 +torch/include/ATen/ops/std_cuda_dispatch.h,sha256=-DiKXpgGulYlPrVRtUjB5YwuZ8zibfXwL0phAkhDCZE,1215 +torch/include/ATen/ops/std_mean.h,sha256=qtrKrnKrWEgIN3OcJkjeNhgT6Ad9hPghUWpUK_xLILA,3217 +torch/include/ATen/ops/std_mean_compositeexplicitautograd_dispatch.h,sha256=VCpaG6XBA7kUd_nHk0P_Q4fy0Qlf4Y_RtBdbcVGTLak,1183 +torch/include/ATen/ops/std_mean_compositeimplicitautograd_dispatch.h,sha256=0t2CCz_BRz4vw2-lm74cNf65DAmRpPNGM8P3SxeJ8Ko,1273 +torch/include/ATen/ops/std_mean_cpu_dispatch.h,sha256=Rjd4e1_wDVt1nH12JogCPzxJITes6YLs0TzERJRQqsY,874 +torch/include/ATen/ops/std_mean_cuda_dispatch.h,sha256=7DHVQB0VmevNgu4dlp2kHE4CPYJQ34k223yCll47J1Y,876 +torch/include/ATen/ops/std_mean_native.h,sha256=Hjhy0NPJa8-XfmPxMsdJFf_4fb6JuHjHtgsEVAphhCM,1437 +torch/include/ATen/ops/std_mean_ops.h,sha256=-etY1zBoUnack3n08JTq93awjvKdJC-GhHSuyTzbgzI,5696 +torch/include/ATen/ops/std_native.h,sha256=EY5BR-P8ic1qFdLDTxs2cH3r7v9G6dzLclVDXs5V-AA,2012 +torch/include/ATen/ops/std_ops.h,sha256=ny16C20P9KczcLt-ktgU5VqMZUSIKF9CGsT4gZRu9qU,7504 +torch/include/ATen/ops/stft.h,sha256=2OwFHHNm1EsfmwrsLbY17Hm7szDdBG_CB4suudrjau4,2072 +torch/include/ATen/ops/stft_compositeimplicitautograd_dispatch.h,sha256=HBoMncjEQwtJ9Z2U1IKpXOcsWKEId0W-IPvt78eR0FE,1516 +torch/include/ATen/ops/stft_native.h,sha256=fJdhwK_bwmVi2qC-EVSP51U8cMUCIccvchKDsu1wmoE,1267 +torch/include/ATen/ops/stft_ops.h,sha256=JIe69_mNoalxymdNYZKpl4hLjatNgPtsmAJtIrDsYQU,3345 +torch/include/ATen/ops/stride.h,sha256=EORLmkDCGRxA7Kk6XAUQNNM_uzzYwFDpvuPFQOexEyU,857 +torch/include/ATen/ops/stride_compositeimplicitautograd_dispatch.h,sha256=PN5dIRTcMD-TajrInu1eREZxjdwEEkE5pCfXJS47R4s,842 +torch/include/ATen/ops/stride_native.h,sha256=hxZpCBdZ47UAQUQg4eChAKQl7wgdR19R5drzNuXNKjQ,554 +torch/include/ATen/ops/stride_ops.h,sha256=TBi2gf5OQrkxFfgsS0k__dA7SZISeLEg91yxrFO-D-s,1571 +torch/include/ATen/ops/sub.h,sha256=s2jpiqF3MJpVVUzAZkBIdi0DhIfqgEmca9wkQ_aqd44,2138 +torch/include/ATen/ops/sub_compositeexplicitautograd_dispatch.h,sha256=6okzax7hRp3u7e6DzUg-ZoG_jEUvB2P94CKdfkaW0Ug,1174 +torch/include/ATen/ops/sub_compositeexplicitautogradnonfunctional_dispatch.h,sha256=OxvbKUWEyGxA1fKAq6Nl_M_a9_bzwTGFAgQe42YIjFY,943 +torch/include/ATen/ops/sub_cpu_dispatch.h,sha256=z338yo7XiXwDw1ho7R18dgS9aA0P5fNGfEPMP3s-kD4,1130 +torch/include/ATen/ops/sub_cuda_dispatch.h,sha256=Wmo_-nNh6xa4QvPSGoSWJf9Z2ReGrjwvKyHT5dxk_qc,1132 +torch/include/ATen/ops/sub_meta.h,sha256=VmiuskHDkS9szrzOWXh_OTG-3vhBOREKSZ4TN7j9pV8,623 +torch/include/ATen/ops/sub_meta_dispatch.h,sha256=5HtfxiQj1pfoE2WZFLkUIRgG_3u-X6Iod_SFLsDOQ5E,1132 +torch/include/ATen/ops/sub_native.h,sha256=cjpu5Ee78cnbAL_sYpLKptjRs2jL4ukM-NEkj9zl5do,1583 +torch/include/ATen/ops/sub_ops.h,sha256=ttSiEUTtOKk1x-lFc0S9Yg2yyUZMJqP1rufCDWglSvw,4732 +torch/include/ATen/ops/subtract.h,sha256=Tl1C5pmcn8_1d0b6U7waPwmjH8INIPAZjeMBaaAzkDQ,1609 +torch/include/ATen/ops/subtract_compositeimplicitautograd_dispatch.h,sha256=9wHS4xcrqPU8tU7zQcD4_T0hhuiyEbJkB5uquKplJbE,1411 +torch/include/ATen/ops/subtract_native.h,sha256=g35e24XRCMhIzVys2KD0WyfK7XQ4GvN2nfg0JY7gJFk,988 +torch/include/ATen/ops/subtract_ops.h,sha256=kL5YT14Cv3qUB9WXrTRxtVAE85Dh8_yPn36lTdAZMoM,4031 +torch/include/ATen/ops/sum.h,sha256=mb9AdZ1XetA6zER4YjLT-00CcvATQfpEVDwh-CcN0-Y,3407 +torch/include/ATen/ops/sum_compositeexplicitautograd_dispatch.h,sha256=IP9abaFWHQM8pk5nL2kCvQ9YpQsDrQhFUZEdaxMbIys,1059 +torch/include/ATen/ops/sum_compositeexplicitautogradnonfunctional_dispatch.h,sha256=XUFiDFN3PwsGzNvzGpGgQ3KxW5vXJG0hilLs0WDJKS8,890 +torch/include/ATen/ops/sum_compositeimplicitautograd_dispatch.h,sha256=cEAQUg2ymOSiRrJl5fc6A8BlFzEmIqPZv_YJ_NuuoDo,1176 +torch/include/ATen/ops/sum_cpu_dispatch.h,sha256=YC4p_IgfR7KN1fHcQ-y1gYZpeb7OS9HTi0TmuMfMlJE,1156 +torch/include/ATen/ops/sum_cuda_dispatch.h,sha256=dLb5T4gtrupSaacM9T-8tUMOCpDagbQ0hP4ftfOslxI,1158 +torch/include/ATen/ops/sum_meta.h,sha256=eye1f24tdXrJJchxUx5CzmRXFE4rgtkNkYXk55ZAlRw,658 +torch/include/ATen/ops/sum_meta_dispatch.h,sha256=sCUq_nqWWrYa8ksexfJQcYlNoAGoXuk52vpeyRN1O_E,1158 +torch/include/ATen/ops/sum_native.h,sha256=EA_XvjXDQFxDNsgrDiT0b3ccMY3zn7ELuyN3vjWOf7M,1925 +torch/include/ATen/ops/sum_ops.h,sha256=rVqlj0VN8mdN_ySEpqxTPuJnOIOY8L1JtXB78BDditc,5192 +torch/include/ATen/ops/sum_to_size.h,sha256=NGedymyyeGSkQK-WVynWoDFLsirTXElm-Ju7rza5M8w,1007 +torch/include/ATen/ops/sum_to_size_compositeimplicitautograd_dispatch.h,sha256=qcgFze61T-FTH-xR8GflgnxDysSHbyn5bhuF3sRYMrc,883 +torch/include/ATen/ops/sum_to_size_native.h,sha256=TU90_5drKIQO5QqEH_CfY5Xob37F_V8ujXTiceIhsvM,514 +torch/include/ATen/ops/sum_to_size_ops.h,sha256=asEGkn0UdI-ys08pRtxswDmjgXwrMTWjm5wfCUWyG_U,1060 +torch/include/ATen/ops/svd.h,sha256=YSec8zRTo4yCmhUczSW_Zaig5wxYmkjgGRd9E87uO_A,1585 +torch/include/ATen/ops/svd_compositeimplicitautograd_dispatch.h,sha256=o7YTegUFrNQb6keDLJQlhU2YgNLkjn55nfO4OSBRgTA,1192 +torch/include/ATen/ops/svd_native.h,sha256=-nuHzhzDKofsjnqQ3iz2SfLUuQAT7i0W6ayYtnBnGNY,720 +torch/include/ATen/ops/svd_ops.h,sha256=GBnYLjc_dEl3eju9M8nNjiYGwjSFY4afdFyMjmru_D4,2133 +torch/include/ATen/ops/swapaxes.h,sha256=OvwlMOUZ1l4_JhLP_fk4DqdHiAcP9S10pnyj7WOuJXs,717 +torch/include/ATen/ops/swapaxes_compositeimplicitautograd_dispatch.h,sha256=QKsdSRsNHmNLZMNM648kl4JIhtblxEy9qUCWiU0Wods,879 +torch/include/ATen/ops/swapaxes_native.h,sha256=Xj3-3UFJyLzigCOcG_bJRNmMAIY2_ezE0LC1QuagDpc,591 +torch/include/ATen/ops/swapaxes_ops.h,sha256=nFNQPJfDZ-vKN-xyket1g_kBWKX2l1MS79O_At2Ka2M,1674 +torch/include/ATen/ops/swapdims.h,sha256=IwtC7rSTMD_g-hEHUj6xvBEpocC6QQC3egwjWL6Qmj4,711 +torch/include/ATen/ops/swapdims_compositeimplicitautograd_dispatch.h,sha256=D0IefFV_MtyAO3vvRsnbBUFu9daGc-Mt3sDIyhpo41E,875 +torch/include/ATen/ops/swapdims_native.h,sha256=MECkqOlF0OzAimDTWzv7lIwTBEyCtu6fVwtoxapG7CA,587 +torch/include/ATen/ops/swapdims_ops.h,sha256=1JEgkZ8ej5Yc_JINZWdYsK7GFHrxSpNpeiHEVJXYBIs,1662 +torch/include/ATen/ops/sym_constrain_range.h,sha256=bbpzVFZsPLbNDY7oNWxr_q0R-RvReUnkuM7hMEPqCwI,812 +torch/include/ATen/ops/sym_constrain_range_compositeexplicitautograd_dispatch.h,sha256=zh4KdvzWMmek1t9OZq9u-M23Jvxj83ezqBgk7AzxZSI,861 +torch/include/ATen/ops/sym_constrain_range_for_size.h,sha256=hlwakdjOGBHAheXMX8LaRwXTHSmDwphoaRZ7F5kD640,848 +torch/include/ATen/ops/sym_constrain_range_for_size_compositeexplicitautograd_dispatch.h,sha256=tkU62YCRhLEq4WPAuylAHyWSzWD51UlhcABVgsHHZbg,870 +torch/include/ATen/ops/sym_constrain_range_for_size_native.h,sha256=6kHBqJLN6yRQqkBe7VSJ72IcvjtA6xQ8oqYVQYElBHs,582 +torch/include/ATen/ops/sym_constrain_range_for_size_ops.h,sha256=EiZlL9j0EETtYjsOsvr51Bh8mllTRV0oHIIsi9FnyrU,1206 +torch/include/ATen/ops/sym_constrain_range_native.h,sha256=r-u-ZBHxhZs3Nthe6Zu9LcThKV4DNYFaCo5DfYvbW_o,573 +torch/include/ATen/ops/sym_constrain_range_ops.h,sha256=ESSc_nLgJqKyfGJTDsWPsyZQ1zi2Hn5ZXfFr1xT6sOg,1179 +torch/include/ATen/ops/sym_numel.h,sha256=jKaucZGvctRoXprjvbimldxInaQ2n0Xa-tKkEG__Wk0,661 +torch/include/ATen/ops/sym_numel_compositeimplicitautograd_dispatch.h,sha256=tXTb3_7AIwh0kcLzbKzEuiNbTOaq_j1Kk_xhsNaDAJA,768 +torch/include/ATen/ops/sym_numel_native.h,sha256=XqoILOm1VXTo1B-kaaUAPipny2HWV9R2WBrsSN-Vy1Q,480 +torch/include/ATen/ops/sym_numel_ops.h,sha256=T4cqptVHPtcuc9sO3UaLHP-Opvov0QG22do9yeK-3OI,969 +torch/include/ATen/ops/sym_size.h,sha256=kUYvUasLOyWqM5btpEjYf56_8N4_-p1UsBlqYHdDDU4,692 +torch/include/ATen/ops/sym_size_compositeimplicitautograd_dispatch.h,sha256=BL6mxnuGiOQ05MHhNKLVcNSPHugjuNJZ_I6WwACuLz8,780 +torch/include/ATen/ops/sym_size_native.h,sha256=H0TXww8MaXRtOEomo-Hn41Ej96xK_2I-txxqD_D0m7s,492 +torch/include/ATen/ops/sym_size_ops.h,sha256=rzfHIbRbnUQF5FVA_V0_7aesZ-93sbk_t0DrnxoDZ6A,1021 +torch/include/ATen/ops/sym_storage_offset.h,sha256=IENXOdxovFxVIJLuYfVYt0oYKUdOEpU7qGsOuwIKi7I,697 +torch/include/ATen/ops/sym_storage_offset_compositeimplicitautograd_dispatch.h,sha256=itfuP6Tt093Zrofo3boMiLhS3W4jNkg5q49PxJfryMU,777 +torch/include/ATen/ops/sym_storage_offset_native.h,sha256=ZKAa_Uzrgxxin1KVn5QrBq1rTBihxLAzsNasXruCpeo,489 +torch/include/ATen/ops/sym_storage_offset_ops.h,sha256=Qvo4bmH5T_AmJJtep2ua_Vazq_IEFBQHkkaMcg1tckw,996 +torch/include/ATen/ops/sym_stride.h,sha256=4UawIYsi7g5CFeDapzxZhsIBVSe2kzg22FWd_6aR23E,700 +torch/include/ATen/ops/sym_stride_compositeimplicitautograd_dispatch.h,sha256=z320JcS739xpzhKp45x5gGX8P-RPD0U5yAqnDCNF4pM,782 +torch/include/ATen/ops/sym_stride_native.h,sha256=yU9AE-MYbIT0cF2vpXl7VKgMbw5qGZJiQYC6lzVCsZQ,494 +torch/include/ATen/ops/sym_stride_ops.h,sha256=PZk1QNFp2i-zaJbmfmuLZNd7gGHzEoGxk0jt7_DPzRc,1027 +torch/include/ATen/ops/t.h,sha256=s7PjnjXQhfW-oiDS2cB5tLxxGzGhBZEDtsCUFjSxV7U,623 +torch/include/ATen/ops/t_compositeexplicitautograd_dispatch.h,sha256=zNsOwQ5j1KDg2w1HL2Z4dXWsQQzz1CjOBaA6wNq2oEw,805 +torch/include/ATen/ops/t_copy.h,sha256=X1JezddDA67cyUHhF4nXzp0WokVwhW4kgIm-hhJSpMs,1027 +torch/include/ATen/ops/t_copy_compositeexplicitautograd_dispatch.h,sha256=JmlOfgw8-Q077uwpeK-Qr-l-zLj746wf_GwXU5ykEqM,867 +torch/include/ATen/ops/t_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=Tbn5mQk7oks1TQs9P7g977GDweM3MA2JYEKjnB4ztd4,790 +torch/include/ATen/ops/t_copy_native.h,sha256=r_NsrNli1pIZ6cL38LcZoKBUdLFU8qcdFLZ6U7o42qI,554 +torch/include/ATen/ops/t_copy_ops.h,sha256=nYuf7cvlKNtIJLYyE60Z3OyjBeq9iEl84pldWbM9rc0,1547 +torch/include/ATen/ops/t_native.h,sha256=QGipuSEE4w6vJ5ha5iGKBqO1xNudd7y4Njj4ENN37z0,517 +torch/include/ATen/ops/t_ops.h,sha256=R-2K3YEdYo43wg03DVOqmrbC19vVLd9mj9pJ0qpfLsg,1432 +torch/include/ATen/ops/take.h,sha256=TUnJ6dThw4TZZGKk2CLs_7RaSCSUhD1k6425QtKq6FU,1148 +torch/include/ATen/ops/take_along_dim.h,sha256=RmzUPV6p0YFXbz3oklhtaZNVqJPZwfkb1rnVHGRXLMk,1446 +torch/include/ATen/ops/take_along_dim_compositeimplicitautograd_dispatch.h,sha256=p6KUh4nZJJFkZGMyfONIRtjXatC4rLZDjkF_846eeFE,1149 +torch/include/ATen/ops/take_along_dim_native.h,sha256=GBpx2DEM2Rflev0cAipvvzZR1gCOS_bWN8vzSXD5ORI,701 +torch/include/ATen/ops/take_along_dim_ops.h,sha256=oRg7bn5xXudjBpmurSVwnhbziygsuRq0PhU_CmSYgaE,1981 +torch/include/ATen/ops/take_cpu_dispatch.h,sha256=hbO7KFlRMYRrW6Z2utqVYJgaOAY-RPfl0c1M4YMm-rA,949 +torch/include/ATen/ops/take_cuda_dispatch.h,sha256=qFDsLb8TP2rQs8mawSUMWLeFm0IgPkGpE7CUuaPIfDc,951 +torch/include/ATen/ops/take_native.h,sha256=1zE2NR2WFKymOap-Ucz_lN0a-15V5z_j7QvpGgvPUQA,602 +torch/include/ATen/ops/take_ops.h,sha256=vYXC9M3YWubbbmFr8sQFF3CqC101DQj5J_B6jWiT4o0,1707 +torch/include/ATen/ops/tan.h,sha256=3wufysOxw5Gj4BOPQu9HO88VvcnnTkuL72R_m0q-sjU,1130 +torch/include/ATen/ops/tan_compositeexplicitautogradnonfunctional_dispatch.h,sha256=9msSFgcRjrg8bX6bNI5kCV6baqM0-A6C6H969OR0Lqs,835 +torch/include/ATen/ops/tan_cpu_dispatch.h,sha256=DimMQnXgY-7pND9M75hivgWEZX8K1ApIJYxXSM9Ue8A,916 +torch/include/ATen/ops/tan_cuda_dispatch.h,sha256=uY_V5HawHgc4ftTPpHtwN3pMEOP6rPQDosiUNLdS_i4,918 +torch/include/ATen/ops/tan_meta.h,sha256=zKf2i2nhaTLTrjCFyRHE-FYXLtTDNDvcQd3ExWluQjg,564 +torch/include/ATen/ops/tan_meta_dispatch.h,sha256=ZHlw9zOpcjvJ0CGDflTwnhbGsY_MzNCMaZkckcrZDto,918 +torch/include/ATen/ops/tan_native.h,sha256=PlGInfGDyp-PqsnpMta-RCVQdFzk1sgWoshiQ9pxoUw,989 +torch/include/ATen/ops/tan_ops.h,sha256=lXQVjPaZKkby_YdwIYK8QY7tcEMndV8m_lpsxUKVxUQ,2019 +torch/include/ATen/ops/tanh.h,sha256=6WKgiUnMWhH7WUcJkCl546YzU3ebN-Kg7GU-TLbnlRQ,1143 +torch/include/ATen/ops/tanh_backward.h,sha256=ZITabvNWuf6OR2Me_s8lsM2oLmThwYqn2XTayZlaXD0,1380 +torch/include/ATen/ops/tanh_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=cewqqytrQiBMcumo4dIS19Pz-CcDTLuwRMPKd1SZDIw,831 +torch/include/ATen/ops/tanh_backward_cpu_dispatch.h,sha256=bTCDJx_2SfU3SBw37eRkerFIQoxFsr9nABzBG9etGyw,1014 +torch/include/ATen/ops/tanh_backward_cuda_dispatch.h,sha256=87yeDFFt_xrco8MecdA55JhYnFMPnpDr3rhNaZ3JiO0,1016 +torch/include/ATen/ops/tanh_backward_meta.h,sha256=LOrXfxRZzP5Q5-G8LhOvMF9QkE2IeByxPCnXDyHr6lk,608 +torch/include/ATen/ops/tanh_backward_meta_dispatch.h,sha256=c5hHwffvqGr8pxGIIBCwIvE0v5ZdXWtTnfpsJ5gAexg,1016 +torch/include/ATen/ops/tanh_backward_native.h,sha256=jDhIebc4DOZ0jgs0FLS4LUw1GXo5-ZRMQL_kaRT7Bh0,658 +torch/include/ATen/ops/tanh_backward_ops.h,sha256=9eXqegtrzB_-u0eahw-9hGoPTSQ2FlecJaCANTyjSvY,1851 +torch/include/ATen/ops/tanh_compositeexplicitautogradnonfunctional_dispatch.h,sha256=OQVmUti_spPJKlM5JmiXBvOTTJk6sDBboVm7n9Ufv3c,837 +torch/include/ATen/ops/tanh_cpu_dispatch.h,sha256=khq7PUKQ1THXtSJ0J-89jDHoCcUD-swCwAfQTIci5QA,920 +torch/include/ATen/ops/tanh_cuda_dispatch.h,sha256=yrcfdE38ouc1qkOXg1cjWUM-aKAHEPcmVNDPCd8xS2I,922 +torch/include/ATen/ops/tanh_meta.h,sha256=6u5Q07UBiWHC0a80WyYWl8Qe7AeYVeF1RCty0D5A6hM,565 +torch/include/ATen/ops/tanh_meta_dispatch.h,sha256=c9uDlsNwtm-qtf6XIwxdWoyGye5tH3PhTIrJRg7IPKw,922 +torch/include/ATen/ops/tanh_native.h,sha256=EW0txBCAMM5rJt3BHD3oocj432YsjF1RU-_p7Ym1LQE,1306 +torch/include/ATen/ops/tanh_ops.h,sha256=cHfwE4en1KnerOaWJtKT13cD7YX6DcVTidCrfqKq5rU,2028 +torch/include/ATen/ops/tensor.h,sha256=isoes7wQlV4GjiBAgES1eu_hDJ5Z7Nn1eDIo-vJTBnY,1631 +torch/include/ATen/ops/tensor_split.h,sha256=FRXg8hKGmAMa-XJKGtSBzwSlxC7z5qCIrNrm5LnnWPU,3206 +torch/include/ATen/ops/tensor_split_compositeimplicitautograd_dispatch.h,sha256=H5JK7evExo6-K1LJRsox4fWUBFHg8Y_uIHfEe09kWmY,1315 +torch/include/ATen/ops/tensor_split_native.h,sha256=H4b5Pa-rfgkBtQe3kBCieviky57SxiLiBXoFRmLnhco,821 +torch/include/ATen/ops/tensor_split_ops.h,sha256=N77MykmFBMRcKm85nKw3kdFlAHysCzM2tfcIEoYiB4M,2772 +torch/include/ATen/ops/tensordot.h,sha256=9svcZ6wdyHZHj-wfk8WvIN0WmVGUJ9J5d0xT1zjFWv8,1537 +torch/include/ATen/ops/tensordot_compositeimplicitautograd_dispatch.h,sha256=GKHo5YJ6o4nCJYKxewCITKpRbv-GH9EhEwcssXAv5X4,1173 +torch/include/ATen/ops/tensordot_native.h,sha256=aANql91t3dWUrqQopDfhtZUsaLgncdTN99LwUaGduQQ,722 +torch/include/ATen/ops/tensordot_ops.h,sha256=DStJDk_XAIkFVH-A8e-8hmeXGZRZFjCFI1BzaEaBCTw,2095 +torch/include/ATen/ops/thnn_conv2d.h,sha256=rDYxbe33idRqsk9NdVBrS6Y6x6nlzXSc3SHFSZS3xro,6620 +torch/include/ATen/ops/thnn_conv2d_compositeimplicitautograd_dispatch.h,sha256=W652Ye1hYBsL1pBGBMgIBkYSCl6X9Ex8GD5ZLsJpGVg,2181 +torch/include/ATen/ops/thnn_conv2d_native.h,sha256=s9K2wGprx-IeiUq5nR0lTwzlrX_KsGfp9rqDV2cr1j4,865 +torch/include/ATen/ops/thnn_conv2d_ops.h,sha256=AktbBliVwz2xXOSC5el_vtr4Q4RCbNqRWMBjmDOQIHc,2649 +torch/include/ATen/ops/threshold.h,sha256=uQ4VLeZUzy4lZytLjFJtJyOTtss9ZTlr-DcLOz1dOOY,1632 +torch/include/ATen/ops/threshold_backward.h,sha256=0DoWehXkY6jqJ47wveHkWC8vdOncpMGnp80HxL9tu3w,1589 +torch/include/ATen/ops/threshold_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=plJpzm1z4Vb6Y6LEFRhFuBRpKZhh4sJu71uBs8uKM3g,864 +torch/include/ATen/ops/threshold_backward_cpu_dispatch.h,sha256=3RRYQvqNz0pGBKIpm5GLlDfPm9tYdQEXqpwyWEHpw0M,1113 +torch/include/ATen/ops/threshold_backward_cuda_dispatch.h,sha256=XYavrW3lmvvVr0r_7B56-EhFKrqvHWgrr6X9Oqljafc,1115 +torch/include/ATen/ops/threshold_backward_meta.h,sha256=jSN0abkZSP4S5PQd5TJspEvlhWSxsmpGAyT-XdlVGxc,641 +torch/include/ATen/ops/threshold_backward_meta_dispatch.h,sha256=Io856UNegr-z9rZofa9ul0SVMGHprgfINFefKpXNaLo,1115 +torch/include/ATen/ops/threshold_backward_native.h,sha256=fRwk7ksYzT2GlcRyuwAEc0tcQazBa6n5aAscB0OjvZg,1591 +torch/include/ATen/ops/threshold_backward_ops.h,sha256=rKn0NrorU1aEv1D3436YbqW9ot7w6H8wwoDbaYy0gTs,2065 +torch/include/ATen/ops/threshold_compositeexplicitautogradnonfunctional_dispatch.h,sha256=jI63c68gBq8bfVtw_ycGKPUGhN8DIeWiqrVTPvQRyW8,959 +torch/include/ATen/ops/threshold_cpu_dispatch.h,sha256=rHiezQrfSYXnJn9n68hpVpu__zfxjl-6DWeSY2DvVmc,1164 +torch/include/ATen/ops/threshold_cuda_dispatch.h,sha256=k47K89InvDrRAOHe3yQ39OxOjn1DIpukBrz5zzi5A24,1166 +torch/include/ATen/ops/threshold_meta.h,sha256=PJMLYEjaoto-RoBSB753ftF49KEZpSFI9s0jUmT9ebY,626 +torch/include/ATen/ops/threshold_meta_dispatch.h,sha256=4hCfmDbySXTjcvz1NmZVFWE0DdFcM5HcIt--ImF8pHU,1166 +torch/include/ATen/ops/threshold_native.h,sha256=fj5srmnUx4Iff_1xeONVB30e9ZrVdPDYJEfRCvrB9Cs,788 +torch/include/ATen/ops/threshold_ops.h,sha256=CYtNHvjJumlcuCWnmbRD0tq9aJlJ5zwWO-gs9fXcq2A,2625 +torch/include/ATen/ops/tile.h,sha256=taiPzIqVavKqs1cukQGoMZUBiVph1JBwMKP-G8pluUI,1351 +torch/include/ATen/ops/tile_compositeimplicitautograd_dispatch.h,sha256=x2Ibfd3Xz2YWbxrJljyuI3hTpdJ7Pq-HRpOznFi5Nes,869 +torch/include/ATen/ops/tile_native.h,sha256=reTWCYCm4y0xAb5qTmAUNeSN854j3g2tkI_Ra4nyAqs,507 +torch/include/ATen/ops/tile_ops.h,sha256=1paaRN_Scx3GCv9S8AzcL9Gif3Gw-wSpJBQD9XyYdHI,1039 +torch/include/ATen/ops/to.h,sha256=HMtae-ILHiTdt7iLeiRqoovehCl8lthWPmJxgGtxzUs,499 +torch/include/ATen/ops/to_compositeimplicitautograd_dispatch.h,sha256=r2KeSPtakZVsWeKBqXu1v3rs9OHHro8a1upc-d5qD80,1727 +torch/include/ATen/ops/to_dense.h,sha256=yFgNgBkVXDBeaRixHLpOikhwWGtmj-5mwCeptop1kkg,505 +torch/include/ATen/ops/to_dense_backward.h,sha256=PJxFO7iDSStnL8xJaS9-hced6b6h4kGoqz8sbSVe8hI,815 +torch/include/ATen/ops/to_dense_backward_compositeimplicitautograd_dispatch.h,sha256=Wo9FBXmMIT_MGtN4krfxnT4R2QpMdf4Zv5Fm4XNqDxc,851 +torch/include/ATen/ops/to_dense_backward_native.h,sha256=vnvY3M8oRQ21GNisMGlW-r9kA6uHUZMpAsYqYLA2GTc,563 +torch/include/ATen/ops/to_dense_backward_ops.h,sha256=KUZR7XSouIGWXArdfW3sSeE0dSCHDX3GiaKEM8qX4B4,1193 +torch/include/ATen/ops/to_dense_compositeimplicitautograd_dispatch.h,sha256=wQJUOfatuqr9neebNb3UPKDN3OjI_NJ49W2ETETV_qc,870 +torch/include/ATen/ops/to_dense_native.h,sha256=FiK27JUTLWTELeGIooCOSmHhO_-2NJam99TtG15enhc,582 +torch/include/ATen/ops/to_dense_ops.h,sha256=rYJuTPSaAgu2lcVTJgbD1HG9YwwxmbiAtBw98QLOZTQ,1218 +torch/include/ATen/ops/to_mkldnn.h,sha256=GgURXRUiGz7M4mGgqTks-dNDrdj_XWjFKnLi-kiqqoQ,1068 +torch/include/ATen/ops/to_mkldnn_backward.h,sha256=uRC6KURbNjRKsoA2q9MoG2DM-298s96CoTQsNnWFUv8,732 +torch/include/ATen/ops/to_mkldnn_backward_compositeimplicitautograd_dispatch.h,sha256=o1U7p_HbsENzQVP_PpmA5ubxRWXo-UpTP8W794QHH7s,802 +torch/include/ATen/ops/to_mkldnn_backward_native.h,sha256=C63XoRi_ncto8aI_PgeWhY2AiPvJXV_Vsc4h16t5XdM,514 +torch/include/ATen/ops/to_mkldnn_backward_ops.h,sha256=qYS3hhhYYuBhvSwZkp_Z_ymuTePj_vmRz7oCLZZ_Plo,1079 +torch/include/ATen/ops/to_mkldnn_compositeexplicitautograd_dispatch.h,sha256=XINlXwQ_SoQu5KFr-ZCPynpJPN1YaW1HJzYSXwN3ELQ,966 +torch/include/ATen/ops/to_mkldnn_cpu_dispatch.h,sha256=1sV3JVcp4MHRIBkUTpT6hvHTm0xuabZNpzBOgn28C0k,777 +torch/include/ATen/ops/to_mkldnn_native.h,sha256=R-m5fLQIPPmDXMgeLR2sn3-uboySBmf6OkwP-QIL5r4,659 +torch/include/ATen/ops/to_mkldnn_ops.h,sha256=XtggO1gQj2_qKDuT6mG2-c1MZiEpa6xJwKDbJvSB6KM,1835 +torch/include/ATen/ops/to_native.h,sha256=XEUypKBPb1ddGClPiIRRC_l0QMEas2ld6l9G7VzUZWs,1292 +torch/include/ATen/ops/to_ops.h,sha256=c1gXmR0ZPYquLhivJF8Yy8RqSeKTGBx95iL5spTTYtM,4452 +torch/include/ATen/ops/to_padded_tensor.h,sha256=g3ph3KOhD3Z2q_vI2Or4_F2DM7dg_u40MvLRynknK1k,4347 +torch/include/ATen/ops/to_padded_tensor_compositeexplicitautograd_dispatch.h,sha256=i8nAK-OA5xqKHmNVU7sT2MbTvpjNRhkK3nyzgrZ_Nh4,1326 +torch/include/ATen/ops/to_padded_tensor_native.h,sha256=G_jehco7KVoxQDplGf2_EAuvPl2Ana-28FCq8H81bso,876 +torch/include/ATen/ops/to_padded_tensor_ops.h,sha256=GM3s6KHWZL59f5Wbxz3jJWlvBpkBdCt6hc6LGEoxr0U,1989 +torch/include/ATen/ops/to_sparse.h,sha256=T9pyiHNu6m70p4Yk7tKxDq-6Ards3Mewj1qVOIWZjQk,506 +torch/include/ATen/ops/to_sparse_bsc.h,sha256=6qbSd3SldkIevaIxbXoWm4Ubr-Nss4mlZEbG0skndTA,510 +torch/include/ATen/ops/to_sparse_bsc_compositeimplicitautograd_dispatch.h,sha256=BI0PUXycKmaVhFEpe-oSWETYtPinzGECUWEtYj30N4s,849 +torch/include/ATen/ops/to_sparse_bsc_native.h,sha256=3rm6r1RCUted9u2GZkdVQtUWmRI9pVsnO8U5aWQU4bE,561 +torch/include/ATen/ops/to_sparse_bsc_ops.h,sha256=uNu3ymkHVR6-Hmu5pfGYqoKjC_WiSW0hksi7_XDZl-0,1186 +torch/include/ATen/ops/to_sparse_bsr.h,sha256=UEI5na3HUdyltUAlv15zusvFRpi3MVjC3wyBQpRKyms,510 +torch/include/ATen/ops/to_sparse_bsr_compositeimplicitautograd_dispatch.h,sha256=9bAip_9OMsdlQeugNnY179i9wl6TA4e3Ie7P_a5unG4,849 +torch/include/ATen/ops/to_sparse_bsr_native.h,sha256=Pe4767kwF0JbqNg0bbOkDzjC2mUe8gMc8eX8EEz2LXo,561 +torch/include/ATen/ops/to_sparse_bsr_ops.h,sha256=dkxit_9sEtQDO3B4sfkcbJGwV7N-anGJriM8wTHzQew,1186 +torch/include/ATen/ops/to_sparse_compositeimplicitautograd_dispatch.h,sha256=hMGgKAnDH1NZO2gVQH0M5z4TTkW9MZxKCArbvnLsnZU,996 +torch/include/ATen/ops/to_sparse_csc.h,sha256=91QQ01T0citCgMU6HsOEL1JB9tYryJOp6keJWFPYcg8,510 +torch/include/ATen/ops/to_sparse_csc_compositeimplicitautograd_dispatch.h,sha256=sjRNS4-HcOwfmTgXrEp7Z1Vy0x809rQfl-rD6KxYwsg,822 +torch/include/ATen/ops/to_sparse_csc_native.h,sha256=rTlwHPJ1iSd9Kt6ceIl6uCEU6n5pBiL6FHS9zBNdTwA,534 +torch/include/ATen/ops/to_sparse_csc_ops.h,sha256=Sc1ynkhYIirvkHv6by4nV2VuhlJ0LPfA87s3OiB1VV4,1097 +torch/include/ATen/ops/to_sparse_csr.h,sha256=O85QzpREhUd-ntEaA-alBuTSl8WkoSuUk1d3HPJn4hc,510 +torch/include/ATen/ops/to_sparse_csr_compositeimplicitautograd_dispatch.h,sha256=0YpYHSKzhIXe8gjpwG7uUflK7TH5vzo9RinDjuEuomM,822 +torch/include/ATen/ops/to_sparse_csr_native.h,sha256=pmwG2dBeaReXUOU1NRhxKvfPe2e7ljv6BlIIe8FfEPs,534 +torch/include/ATen/ops/to_sparse_csr_ops.h,sha256=BmyBuRH0nlWYsuh9-0eSVdUQVm3ZJKqT_Vpq3SCXbJ0,1097 +torch/include/ATen/ops/to_sparse_native.h,sha256=e9759H1f8_xk2kfKBHhjAxnfas7aYaIXaKBrw80Zc0U,708 +torch/include/ATen/ops/to_sparse_ops.h,sha256=akISevk41jqgbBi9z08XC4yQX1ucUg2oNfMhQsQjOiI,1935 +torch/include/ATen/ops/topk.h,sha256=pW6WcR48_ZqT_5L70ZzyUR88nNUKRj1vcHk5qiYZqIk,5123 +torch/include/ATen/ops/topk_compositeexplicitautogradnonfunctional_dispatch.h,sha256=Qu0OyAdcsi7Ks4bmOM93_aOrfJ03amuShm2RqKLcI_k,1029 +torch/include/ATen/ops/topk_cpu_dispatch.h,sha256=ERn1_1XT0uAPbmK1lWlgdyvOj-Hd6_190nRgA4WHx6c,1725 +torch/include/ATen/ops/topk_cuda_dispatch.h,sha256=CD6MfzITyCeasLUBDCXzJlyRHKdInCNM0E7QGmAX_FY,1727 +torch/include/ATen/ops/topk_meta.h,sha256=grx0fi3-OQ0Am9GqTLFjge4KEYKFUXI-YV6JfaKske0,616 +torch/include/ATen/ops/topk_meta_dispatch.h,sha256=XCFU7buxPpBvL5pAtkHyRr9GkCzBTWig_VjP03734kg,1727 +torch/include/ATen/ops/topk_native.h,sha256=KxMXnhbiJGGuNwGBOo27eNLMojOdEQ_iMzgOlwMOTXM,1055 +torch/include/ATen/ops/topk_ops.h,sha256=HwnLzPeSIWYHIw2wGEuLFYyTXNq8dr12d0mg7RovIBc,2247 +torch/include/ATen/ops/trace.h,sha256=O4zdg0cO2YnMNBWv_zy791bv8vhHJVPTYMgIGxonmrA,1017 +torch/include/ATen/ops/trace_backward.h,sha256=AIXxeec030PzOKFSyeyq5vQjMDbg_mc5LMjM7G1Hm_0,1471 +torch/include/ATen/ops/trace_backward_compositeimplicitautograd_dispatch.h,sha256=tWr4wy15EycscUG1zc67fxftS1dMqw6CNkXOqIvdbpw,891 +torch/include/ATen/ops/trace_backward_native.h,sha256=A31-OnZaf9ZOJxfaOijxi-ZoPqKXg683fcxhaNFbjEQ,518 +torch/include/ATen/ops/trace_backward_ops.h,sha256=Bew02l0U51dG1jn-uupe8iN5yRhn0vQKQLdcE60z5C0,1072 +torch/include/ATen/ops/trace_compositeexplicitautograd_dispatch.h,sha256=F5a6MKncE7PD2pe2adeoMQ8wAyYfn7Rjwf3OPwOmgbM,865 +torch/include/ATen/ops/trace_cpu_dispatch.h,sha256=JXw0FbDz4QYdluTWpWIuOeS86zits71-T2xImJn-ujE,719 +torch/include/ATen/ops/trace_cuda_dispatch.h,sha256=s1JLCXg53uWtpfvz1nayHb7_NY9kfs6Fa9pkMFLGi-g,721 +torch/include/ATen/ops/trace_native.h,sha256=XyyYIBudHyoY-yImsr6jA-lYxzc5mxi9qd_gGRu4aeQ,614 +torch/include/ATen/ops/trace_ops.h,sha256=0tikoW9eau4op7gWyA-3RGenPzSuj1C2yhf5J5naA2k,1541 +torch/include/ATen/ops/transpose.h,sha256=PsBx3yrMRv6xgeWhTyiSdI1HPIjzTBZoX0qw0cIIi4s,965 +torch/include/ATen/ops/transpose_compositeexplicitautograd_dispatch.h,sha256=agl0kg8AFsNUCam_VNjFJUP0acOTmLhC9ibOP5O8WZg,877 +torch/include/ATen/ops/transpose_compositeimplicitautograd_dispatch.h,sha256=HDXuYGWsSldkOukUY8Jg5ldFKv0l8DBdbrkWhCxF4sw,803 +torch/include/ATen/ops/transpose_copy.h,sha256=tfU3SaJ2YKVoiwqyEaSlqqBaT_Yb1_cQtD_a4cUQOr8,1311 +torch/include/ATen/ops/transpose_copy_compositeexplicitautograd_dispatch.h,sha256=5j80UW3aOQTbObPFi5YKK6ElrRT-V9cpCi5qdYtfbM8,939 +torch/include/ATen/ops/transpose_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=XNWCxj8jCzhf3rX0tjlrlHn_TbHckzwH4pHBIkhcPLg,826 +torch/include/ATen/ops/transpose_copy_native.h,sha256=aG5mpyMAEKNLHgK6uv7PYo6SCL2byg6RX1G0xmPodkc,634 +torch/include/ATen/ops/transpose_copy_ops.h,sha256=mH489zdvcXHP_dZe-Du2nr-jzINUQ1269Xpcdy8fqb4,1806 +torch/include/ATen/ops/transpose_native.h,sha256=r36GvXGIayJk2py-lqRjzUlWToXfBrTB0eLbZbM5b_E,774 +torch/include/ATen/ops/transpose_ops.h,sha256=IHMxE2xElNtHaZani77MVKwp1j2NS0HQkp-QfmVrdMY,2343 +torch/include/ATen/ops/trapezoid.h,sha256=LyawNjH1OiJOqmOE8J5SEJXeLP4Quvxk15P9FT1srh8,934 +torch/include/ATen/ops/trapezoid_compositeimplicitautograd_dispatch.h,sha256=q8UX2QQ2Ad6z46_Rzvu7f8bjed6-3jUa1CbufrObrvA,897 +torch/include/ATen/ops/trapezoid_native.h,sha256=ZNuieZTrjW-oEb7r3EayjfkME6_1mWa6IlzP6GXd6M4,609 +torch/include/ATen/ops/trapezoid_ops.h,sha256=lfg7lEjp9ixexSIsH8gVAxT6bOPNNuhsN_JsOrwBjqo,1723 +torch/include/ATen/ops/trapz.h,sha256=oEjeNrubDTKe_3LUj1JLd2asEqoNhx6AwRm1iNTEg00,893 +torch/include/ATen/ops/trapz_compositeimplicitautograd_dispatch.h,sha256=wI6ESJPpPf_y3Z-Bdx9VteUyISKJxG3dmU28mTVKc10,877 +torch/include/ATen/ops/trapz_native.h,sha256=nYZnsUUpwmg27PZv8ZCBG1ZikfBjNGHTthc9WSO39rM,589 +torch/include/ATen/ops/trapz_ops.h,sha256=_HpW1WnOLfoWsmz93PdlvQuV_Wc2uhFu39bjZdtgLKM,1662 +torch/include/ATen/ops/triangular_solve.h,sha256=qtc2OxSk9K0ArwEYJmqitK8xdH2V4faGNxV9ZEJqdc0,1951 +torch/include/ATen/ops/triangular_solve_compositeexplicitautogradnonfunctional_dispatch.h,sha256=54BpzwCgBVYaCocIOnfpzoHGSxoeoeY5sxqEmA4PHNY,912 +torch/include/ATen/ops/triangular_solve_cpu_dispatch.h,sha256=4mDBERpVsAd87vv7zmPj38fvhI8oH71PvxnpdqozmEM,1258 +torch/include/ATen/ops/triangular_solve_cuda_dispatch.h,sha256=YGfgPUfS-ShS8mVX6Mgp0us3HTala9o7KfLjQMyBGhE,1260 +torch/include/ATen/ops/triangular_solve_meta.h,sha256=1n4cmKB2aN-ZMVsf06HiGNoowox7wysVj_TOq5JI5Rg,647 +torch/include/ATen/ops/triangular_solve_meta_dispatch.h,sha256=HsmQ_mvLK4TYiDJ6AzbGe-U-kcev6MW76ZZRUl4HVmE,1260 +torch/include/ATen/ops/triangular_solve_native.h,sha256=PReHzF_uD7nICyhhGysCv1zP9IoMz6yaPVvqw1MSE-Y,1145 +torch/include/ATen/ops/triangular_solve_ops.h,sha256=yO-9Po0oaiJNjZwMWTXXDZVdGnB62JCGCjC2a0TgTtI,2397 +torch/include/ATen/ops/tril.h,sha256=HsMZBEWi_7APnbbqjENpiJJwVRZnu3DI7m_COZepSm8,1143 +torch/include/ATen/ops/tril_compositeexplicitautogradnonfunctional_dispatch.h,sha256=5-ohybdX3JN90qOoOeDGCnVmMZHuu6_5-9Z6ujwYU30,877 +torch/include/ATen/ops/tril_cpu_dispatch.h,sha256=j_cPObg-KxVFPU6Y-vIKSj0dozihDnRrV7B3jgqqjHc,998 +torch/include/ATen/ops/tril_cuda_dispatch.h,sha256=miRBJvkWAqXD_TJc29cPccMPu-VDeF6uu6LUSVr4wbI,1000 +torch/include/ATen/ops/tril_indices.h,sha256=7dfvoIBrE-_4Mhhc29sg1FFIj7TJLsPp9wvAeuxFthw,1960 +torch/include/ATen/ops/tril_indices_compositeexplicitautograd_dispatch.h,sha256=UgI7ZUKrj4gfcTtZKisRtOPfGnksOxjaQNlk368jLcc,915 +torch/include/ATen/ops/tril_indices_cpu_dispatch.h,sha256=4WsHAA28nj8uQbgX0lGzzQ-onybP8mC_1mLnJNhPywQ,1004 +torch/include/ATen/ops/tril_indices_cuda_dispatch.h,sha256=WX5prJTtKgiy6TQd2nnRi1ao3wSwq5wyvS-fWVJuyFI,1006 +torch/include/ATen/ops/tril_indices_native.h,sha256=OlRCrUQXibaLZQLwrTLftHh0EV5v73rg-5MCGBmiWqc,1004 +torch/include/ATen/ops/tril_indices_ops.h,sha256=3uI8oMQLN-ObEn3DTGO5rjhJN_YCjwADTREP77-H2Xo,2199 +torch/include/ATen/ops/tril_meta.h,sha256=oGJwL54ukk_wZzimi-gh48MdJOsdzjI6kFOEIQLX28I,583 +torch/include/ATen/ops/tril_meta_dispatch.h,sha256=PrOhBhmmnHj57L3DJK8aXMf6jwrDSYp39VtAHfBGztQ,1000 +torch/include/ATen/ops/tril_native.h,sha256=TL2251QL7QIrn9jqFBM7oHcJtShwhSY3NkibILJkadQ,764 +torch/include/ATen/ops/tril_ops.h,sha256=NVoR9cFV7zuhdfU5ctHXx-OVA8i40Wo3ph2eHUJAqfc,2211 +torch/include/ATen/ops/triplet_margin_loss.h,sha256=fRw6jqOyA8E024ElmoeNFCXAFCbKLMQEktq5qRrX9YE,1028 +torch/include/ATen/ops/triplet_margin_loss_compositeimplicitautograd_dispatch.h,sha256=EMaoyps0TftBzAgskERCPXzYrks9LmGR02cxsBjaSuc,942 +torch/include/ATen/ops/triplet_margin_loss_native.h,sha256=Fowmf9LVxCZ03X6Sj2RXMqGnTVIF2FZldrJrKV3BBsY,654 +torch/include/ATen/ops/triplet_margin_loss_ops.h,sha256=uvSygQa3cEHITbwHtSd3-NJBkDdrdmaGszdn5Ww_j2E,1448 +torch/include/ATen/ops/triu.h,sha256=Y1E3e3xpDYmIuCOmber4NNLi3ixCsvK9npYo7DdCgao,1143 +torch/include/ATen/ops/triu_compositeexplicitautogradnonfunctional_dispatch.h,sha256=PX9h-oZr-IeA5-SCyd50CqcHOs0RCEjPdxbglUIWNh0,877 +torch/include/ATen/ops/triu_cpu_dispatch.h,sha256=Nd4WAAsCxgo6iW_nybTuUmbAtpgBbF3H3ksogxbNG6w,998 +torch/include/ATen/ops/triu_cuda_dispatch.h,sha256=WP0LKqNl0j_S-p9X53c3UjVUqOxfuNA673ouNfSM92Y,1000 +torch/include/ATen/ops/triu_indices.h,sha256=ZGKq6QAA_wtdHZuJu75zll1TR58wlGByZ7mpMlGhDNU,1960 +torch/include/ATen/ops/triu_indices_compositeexplicitautograd_dispatch.h,sha256=Q0aLJMcY8QNG1bYbVLkKUPuXQX3E-foqOf1YuXn1umA,915 +torch/include/ATen/ops/triu_indices_cpu_dispatch.h,sha256=JRsDpIhNb1XS3jrLjCZkRDQtH5ESiEQDkc9ZTSpLagg,1004 +torch/include/ATen/ops/triu_indices_cuda_dispatch.h,sha256=Bqv70mDTbPbW_ew3Y0xwbTxpzZyugZArZxP_QU_gSPU,1006 +torch/include/ATen/ops/triu_indices_native.h,sha256=RTgpNnPOS5AaTi8SqbNEtRmz31qJQIJJ2jegpSf5-XQ,1004 +torch/include/ATen/ops/triu_indices_ops.h,sha256=Q6BdmludARV1iA47bqyv-jVTc4iQaywrvY9BIXCuSM0,2199 +torch/include/ATen/ops/triu_meta.h,sha256=if8UrGtjo7YLZk3tmLgy4Ed1advnydJHvzT6QbuW1_8,583 +torch/include/ATen/ops/triu_meta_dispatch.h,sha256=zYdPTQ3dJWM0q7JcfWW1HKVpOJlqGtmisHjjVMEgPbI,1000 +torch/include/ATen/ops/triu_native.h,sha256=eIM84lbvH3YJAtBMudaRGYT8StIc1cwCOkM40OpUgKs,764 +torch/include/ATen/ops/triu_ops.h,sha256=dOfRsyeRG9pbm8uJZo1aaGr3IdW-NpWt8RD1ogTfGW4,2211 +torch/include/ATen/ops/true_divide.h,sha256=Jl_QgaAUnDsjJSHLgTVpFdpkhymi1TNcmdTVSHqtzuY,1443 +torch/include/ATen/ops/true_divide_compositeimplicitautograd_dispatch.h,sha256=zWjpyEZbtEpfWk7ccj5FH8Q00U1Nb66PiHBunX1ZYLw,1263 +torch/include/ATen/ops/true_divide_native.h,sha256=-SQI6yFBP4Tty_wS5R_IAoin62l6xmfS0-cK-EowEF4,865 +torch/include/ATen/ops/true_divide_ops.h,sha256=-EFrysciipJ7zNxWwyRv7o3QYcwrcJBDw8FKazsGm5c,3630 +torch/include/ATen/ops/trunc.h,sha256=ffd55pCL3oyjHTa1hb2oFrm25pvU08jR2BRPr5aA31g,1156 +torch/include/ATen/ops/trunc_compositeexplicitautogradnonfunctional_dispatch.h,sha256=dCO_GcChs0manJdXYG3KlV6SdApOOhvKj2NYp03Ecqw,839 +torch/include/ATen/ops/trunc_cpu_dispatch.h,sha256=7x287ZQuYusJI-GdJeLu_NOjQ5KwGCgIOUEPdBxqYos,924 +torch/include/ATen/ops/trunc_cuda_dispatch.h,sha256=2KiQVNSCQ77QcAck5fdOsL4p1QfoYrIrYzC_smc0upk,926 +torch/include/ATen/ops/trunc_meta.h,sha256=f1_kvxAiF7zhUhMExyEFzKkxn-5okS4iucLYv_wGGZM,566 +torch/include/ATen/ops/trunc_meta_dispatch.h,sha256=h1ynWuXUj0jZukKd3czkxuc5o-DmjfnjfAu56MeCzeQ,926 +torch/include/ATen/ops/trunc_native.h,sha256=TX-PPC4lqFRIEBTjVNub9FJbrjtUpU_rfga1srfC9UE,1007 +torch/include/ATen/ops/trunc_ops.h,sha256=ZpBW-BPvsn6IxpDp7UjtfQwBYpNOn3EnYkqdJZa70zE,2037 +torch/include/ATen/ops/type_as.h,sha256=PXIMe7WXfAh5heSPFkFr2By7BraPV7l8xAClYNqcwAc,504 +torch/include/ATen/ops/type_as_compositeimplicitautograd_dispatch.h,sha256=CWUeUszFQE7v0_E3bA0qmhH94ZNtfbkBe8x6Et0I2gk,791 +torch/include/ATen/ops/type_as_native.h,sha256=nOwP3BvyewB6LC0kzB-XNEP7yQDjsTkWXAC-s99NhYU,503 +torch/include/ATen/ops/type_as_ops.h,sha256=03d0EGpSutRseHbo5Ve28OPdHshYAxQT16Ck8lQWMas,1046 +torch/include/ATen/ops/unbind.h,sha256=kfUUvboxoz-JeE5Spokvd9mO6MGANQKPLdYGLYICq5I,918 +torch/include/ATen/ops/unbind_compositeexplicitautograd_dispatch.h,sha256=P5-8u0Hw_MalXZ1LzEQCnVuF13fL9PVqD_ZGryKK5Nk,794 +torch/include/ATen/ops/unbind_compositeimplicitautograd_dispatch.h,sha256=wKmUNYKWbty4rnXxlpkgupeVI0AYPjyCEIF682ucCLs,796 +torch/include/ATen/ops/unbind_copy.h,sha256=f5yzkz0VgWE4NHNnqA0TozR7qfrXXCgF8HLxYTF0bXg,1185 +torch/include/ATen/ops/unbind_copy_compositeexplicitautograd_dispatch.h,sha256=tZcnH7E_Rne2WwO-wZvuXod1gwYNnY9OzaJGbsK0OOQ,893 +torch/include/ATen/ops/unbind_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=BPbMKVtyBGozDLypoJSZ4YT3B0TYkXe8KJr9kPZ0AT4,825 +torch/include/ATen/ops/unbind_copy_native.h,sha256=U4QUiGk9Conx89U_oO8YYn1LqZ2zwUVDaHDFG7dV-fU,609 +torch/include/ATen/ops/unbind_copy_ops.h,sha256=XdxS3kL1oQ0ncgw80hRRUPkgtKob8pXmAfYSIr8juNs,1715 +torch/include/ATen/ops/unbind_native.h,sha256=gc4K1-gbhwIiTq4FZNFrXoTSYLIFiOXiKvWydIHJvu4,689 +torch/include/ATen/ops/unbind_ops.h,sha256=0A7Mwb8wgFSk56sU9KUEihaleqylj1-R4xqM6xp269Y,1713 +torch/include/ATen/ops/unflatten.h,sha256=IHLZ_g9G9WDRpQhWjRs74a-IemPiUbfLOX3UmWwVhPw,2781 +torch/include/ATen/ops/unflatten_compositeimplicitautograd_dispatch.h,sha256=zHf393MQjxFSlIYna-UxDi_6z7-sx6qxLCQj7k4xlG8,1158 +torch/include/ATen/ops/unflatten_dense_tensors.h,sha256=x6PYFta3Q8pGNXLQVInuhgb6EF5sacfVEJDZKB9bwbw,773 +torch/include/ATen/ops/unflatten_dense_tensors_compositeimplicitautograd_dispatch.h,sha256=cUSLTgLf47XSILcdecH2hWXx0hB7llik-5PY2Ar8Z90,820 +torch/include/ATen/ops/unflatten_dense_tensors_native.h,sha256=KWpTW71s4U14fhm_tSb7TM3r2XLIlQaWCCjcdX6G00I,532 +torch/include/ATen/ops/unflatten_dense_tensors_ops.h,sha256=3Pzt79icZ_9F947ciASBjOcOeTX28jNTCre4FQvwCrk,1137 +torch/include/ATen/ops/unflatten_native.h,sha256=0v0F3FFuLW6iLo1QLF6QGqTJ8Yej08_xxIzAetTeDtg,665 +torch/include/ATen/ops/unflatten_ops.h,sha256=D9Xrj4KyAh9oABz4m55rKZj8EqT4j0fGFTdZcn2-Z9w,1887 +torch/include/ATen/ops/unfold.h,sha256=wuAfLUQkWjcfiJjuTzpOiWdwsErx-8nLPPVFFoQxbh4,503 +torch/include/ATen/ops/unfold_backward.h,sha256=CHv40S6Pk-gMdLtxM6cNkiDWwUAnItyU4Q7gJEF4voo,4940 +torch/include/ATen/ops/unfold_backward_compositeexplicitautograd_dispatch.h,sha256=s21Jla9nR7AByxPwXA2TpPWa3kSZDuKexQsaREH-rEo,1374 +torch/include/ATen/ops/unfold_backward_cpu_dispatch.h,sha256=Cv4Sd5OPcEBovk7cKpvA5aFM2vjRZQ_Thgh3-PNP1Oo,949 +torch/include/ATen/ops/unfold_backward_cuda_dispatch.h,sha256=5CmCi4YmNeHvOoOtkB7yP7IIncUGYTo-rAW7joyHihA,951 +torch/include/ATen/ops/unfold_backward_native.h,sha256=tlVLVd1Lf7Ds4OPUoEPEJfH6IhOpzzFnw7Sya0T58nk,729 +torch/include/ATen/ops/unfold_backward_ops.h,sha256=DjFYKVkzQYKDgIvsXI30HS2Nx1PE-9-NOmB2Mnkhg6Q,2113 +torch/include/ATen/ops/unfold_copy.h,sha256=FRXH88RZJl_SJ-WFKt6o5WIjdLGmzE2hB3xtGmJht6g,1392 +torch/include/ATen/ops/unfold_copy_compositeexplicitautograd_dispatch.h,sha256=ZeVZzMP-Ic18S1lxjtH87mfqULiUckew3lODT8e6c24,971 +torch/include/ATen/ops/unfold_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=-LZwhut8sqMT6KgTHomKxgVBStqiFMcrpeOkx2BW9xA,842 +torch/include/ATen/ops/unfold_copy_native.h,sha256=Rp-9J7uO2GLiFRCvtFk_TiMSIJuahHOTjv7Yjwslk9M,658 +torch/include/ATen/ops/unfold_copy_ops.h,sha256=D_vesS46RV6wf7NP70pkOJZJReK_gilu-fFH8CxKdYM,1889 +torch/include/ATen/ops/unfold_cpu_dispatch.h,sha256=_bfZWQWfJQ8tAT9OpM4rihJ-LvYjSnRWAMe_IlYrnvc,767 +torch/include/ATen/ops/unfold_cuda_dispatch.h,sha256=bB615aoWn898Okd3MLj9w4pqZjqWBhf4rtELvCYCP2A,769 +torch/include/ATen/ops/unfold_meta_dispatch.h,sha256=xQP1zG-31Yy9Y22_jHUHu-gBIUaeutQbSFUAfbUXcIM,769 +torch/include/ATen/ops/unfold_native.h,sha256=BasInAcwALJdQaRv8UaWAJd9g1Ggot6SwY51O8VJOgY,523 +torch/include/ATen/ops/unfold_ops.h,sha256=aUb17f9WNnzig5yNAB3GjyTb_vAeSlF8yTkQOftvKX0,1119 +torch/include/ATen/ops/uniform.h,sha256=H1pD3THgU0kGzSODiHEhQ1uUruhmYhfpgPd_I7kqhf4,1498 +torch/include/ATen/ops/uniform_compositeexplicitautograd_dispatch.h,sha256=Fi8lVle-xd7MgudIS8m6IN5P1vYvNtGMvje46ULGrJ8,1160 +torch/include/ATen/ops/uniform_cpu_dispatch.h,sha256=LEEppc_fwpkHlqeQxAV8yBFU7G2QErVMhRemDC2ewCc,803 +torch/include/ATen/ops/uniform_cuda_dispatch.h,sha256=C-5Fzxoz_3QYe2AM-9MvihvsY9Xt1jngjE74jB6rj2I,805 +torch/include/ATen/ops/uniform_meta_dispatch.h,sha256=3yeQV2vb162MOw-pdbXB2OtkzMV0zZIDtLlVxCNGz3Y,805 +torch/include/ATen/ops/uniform_native.h,sha256=o3rhBO4cMD9cTZc4rezEP9dZjk7ewnyTx_wxgE8kUVk,986 +torch/include/ATen/ops/uniform_ops.h,sha256=rwXh-TQjHg91CQ_PmEkWjgS17eKB4OYgDwLmeGPaIQs,2760 +torch/include/ATen/ops/unique_consecutive.h,sha256=9yxTMeA-x6f5ZThuYac0v8AE88mw7FGEevqy_QjN3xY,2086 +torch/include/ATen/ops/unique_consecutive_compositeexplicitautograd_dispatch.h,sha256=iV2011uyNa-2rZIeixbnxNf2fTJSwz5BqKNvoLCPXhg,1218 +torch/include/ATen/ops/unique_consecutive_cpu_dispatch.h,sha256=TA4ars7KrDMTr-2BjEdmifI1pa1bW3cymXQ7uEUh1sw,866 +torch/include/ATen/ops/unique_consecutive_cuda_dispatch.h,sha256=YwVRs_SiOO2sq-TxsjGHVidcZNZy2ZYlwG7jzObJntA,868 +torch/include/ATen/ops/unique_consecutive_native.h,sha256=3GcSAhFttLwOc5YXL5I-Ck8adwr9aNaSOFdF6MgNHdk,1071 +torch/include/ATen/ops/unique_consecutive_ops.h,sha256=xwgTKp3sQNXupmFXXR3eZG0X21Ew7Zm-XicXwzMQeuU,2528 +torch/include/ATen/ops/unique_dim.h,sha256=l1gJ7nmoSQGJYrkS7SaFeiSruHoCyXy0i4nvoPcjWoU,2034 +torch/include/ATen/ops/unique_dim_compositeexplicitautograd_dispatch.h,sha256=mGvoKTDt91GrTz4tbwndAWq5NtzXbjcBolqf_6HLhrY,1184 +torch/include/ATen/ops/unique_dim_consecutive.h,sha256=ZcQOIqb7pCVYUtLNccW3ESwSIBw7aQXo57-hH8PF7Hk,2027 +torch/include/ATen/ops/unique_dim_consecutive_compositeexplicitautograd_dispatch.h,sha256=fhDAveSIwKj7Jf2U3akTUpp5YhDdggQwCrof8RgaOAw,1177 +torch/include/ATen/ops/unique_dim_consecutive_cpu_dispatch.h,sha256=nLPzCmjdXHWrskuLfFHHf-luFDHNgmHfBBzH-yxSlDI,838 +torch/include/ATen/ops/unique_dim_consecutive_cuda_dispatch.h,sha256=Oy4vN83UYw6fY0ufd_hxUSYN-ZMLFOuS8Km1sazC3D4,840 +torch/include/ATen/ops/unique_dim_consecutive_native.h,sha256=HOVAaf9IuW70GPZ41nOZ2-LvLtU4MD_Rw4RFYTQ54mk,1002 +torch/include/ATen/ops/unique_dim_consecutive_ops.h,sha256=1kHnCoz3b0GSDoglGfkcH642ddT2fNnUVYxVDUmOMqA,2438 +torch/include/ATen/ops/unique_dim_cpu_dispatch.h,sha256=YhfPIoL_2AJ2djZBElHEruKQu_nSdIRtU3BL4UpP84Q,844 +torch/include/ATen/ops/unique_dim_cuda_dispatch.h,sha256=wm4zKSt_s3pEMtFyEep_TQDj-2-xGSn1aQ7hDPFdf0g,846 +torch/include/ATen/ops/unique_dim_native.h,sha256=KLvrFcCCqFjeY-lNO5gk2YyUidyzRJlwAxf3puMZeoE,1015 +torch/include/ATen/ops/unique_dim_ops.h,sha256=VjVVoyKiBzWq4bs5ZrsQrpXw24_PnbdngDCyWqEVPLo,2466 +torch/include/ATen/ops/unsafe_chunk.h,sha256=Y9HPBBeoypD45CW_rz-N7jnL5soIr4JnWpEiuTrIsO4,745 +torch/include/ATen/ops/unsafe_chunk_compositeimplicitautograd_dispatch.h,sha256=pGS7-Ry2a7G1fkVO_TpKYV9brG-1kwH6wWrt0rWxDRc,816 +torch/include/ATen/ops/unsafe_chunk_native.h,sha256=NU2sjV1jxDC1xAYOyIjp5fpYsdWPbUVFQcOZ5CjEjxY,528 +torch/include/ATen/ops/unsafe_chunk_ops.h,sha256=2Q-OsblXjOxxaxupoK8bDSevPGEJnmDT12cnbNDgIcs,1121 +torch/include/ATen/ops/unsafe_split.h,sha256=PZ0D35g6sCfoP3w1210iLdLE7TN3JetZsmlU0yuXNbo,4009 +torch/include/ATen/ops/unsafe_split_compositeexplicitautograd_dispatch.h,sha256=RrpF52NWxPQsFD6bfk-7I9mVUxBKOj_ItvZ9jw_3t_k,1413 +torch/include/ATen/ops/unsafe_split_native.h,sha256=GZk6V7HvU6Et3NkeTmYOY9U9SdLOfBHt9wPqb7588kU,661 +torch/include/ATen/ops/unsafe_split_ops.h,sha256=DCUeVYSso-ivN1gKNCgYlomS_tsxvU3yiFH_9aQjhdY,1899 +torch/include/ATen/ops/unsafe_split_with_sizes.h,sha256=43V350BZEL_EAPEBi6J8WBHFi-ghwI6iKBmf1EubLys,4518 +torch/include/ATen/ops/unsafe_split_with_sizes_compositeexplicitautograd_dispatch.h,sha256=06Bb0pwSFzqWLUeRmSWte2QvItAWKEpqenWOcSu22OM,1533 +torch/include/ATen/ops/unsafe_split_with_sizes_native.h,sha256=8uC7VeZo5A_6r1huFxjynl-vshhSXxE92Rvoed8ju2U,694 +torch/include/ATen/ops/unsafe_split_with_sizes_ops.h,sha256=iouKKQyVi5qJu0Uto0LHBP8nzX0HlVQ19lf5PZg2J3U,1982 +torch/include/ATen/ops/unsqueeze.h,sha256=1fCi7I_mB0UR1gKh6njmmsJ8h0hQa1Tttqw773DDi7E,682 +torch/include/ATen/ops/unsqueeze_compositeexplicitautograd_dispatch.h,sha256=7lDGAOB3oT4Ws5A116arqp5AbXxIRXGkrbVI1cZN9YI,847 +torch/include/ATen/ops/unsqueeze_copy.h,sha256=w67bUwxPsx-602-1DthvvIhsI-2HezVKh4lh_3VEHxw,1188 +torch/include/ATen/ops/unsqueeze_copy_compositeexplicitautograd_dispatch.h,sha256=kYx0Dtzvdx26dxK07SQbptCeRRwv_db4JR0n1p4tYHw,909 +torch/include/ATen/ops/unsqueeze_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=FCD0pzttX8xpG4LFHpGw5e0Ml2QxY3wPSKaD1s9bkrA,811 +torch/include/ATen/ops/unsqueeze_copy_native.h,sha256=ALwfYkSzbp8YMLlw9KiK-yK_LLDUWqYi5Kd14t76S0M,596 +torch/include/ATen/ops/unsqueeze_copy_ops.h,sha256=wou11kn_aPFTkYvAMxYF89urTu7aJXue3nPaKFkq8Uo,1683 +torch/include/ATen/ops/unsqueeze_native.h,sha256=F6j3zk34uOl2LRqA-QTo70Swg_ixjh8upobTmyM5SHk,793 +torch/include/ATen/ops/unsqueeze_ops.h,sha256=9Wn7Vn3k6PsSKkFdUMTPdOOoye-hcLys6VPYuVjNP7Q,1568 +torch/include/ATen/ops/upsample_bicubic2d.h,sha256=nAvlURZjEjvmwwdYI2QRjqiwbUJK2A_uIXRJbz-DFEo,7938 +torch/include/ATen/ops/upsample_bicubic2d_backward.h,sha256=PNw7RkYhAed4dWvQPUtKkZlgXJAVrUgVyNcokPANjhs,7684 +torch/include/ATen/ops/upsample_bicubic2d_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=dLN10xguGKbEY1Wq99Zs95PKNl7wcRFahRq5BX3WNSc,1265 +torch/include/ATen/ops/upsample_bicubic2d_backward_cpu_dispatch.h,sha256=ek8SvOUZ7ARHChMG7vHHi1sYCZQKwjImSBlISMRT-oc,2319 +torch/include/ATen/ops/upsample_bicubic2d_backward_cuda_dispatch.h,sha256=tQCW1okclFFbP0Sj2aX5BQhLXXSxn8I79gpf59bWag4,2321 +torch/include/ATen/ops/upsample_bicubic2d_backward_meta.h,sha256=is_4qOfzw2GvVYSovf4IW5emgokLmfw7WwYThjoUNKk,752 +torch/include/ATen/ops/upsample_bicubic2d_backward_meta_dispatch.h,sha256=t4wcPddspu0B2bXJNPeIIVrJgiJRXsRN1H4jFRjz5Jk,2321 +torch/include/ATen/ops/upsample_bicubic2d_backward_native.h,sha256=K2tx8zMjQ7qXlhhmGoR178yHK9zHvgVdJJcPcsMLgu0,1193 +torch/include/ATen/ops/upsample_bicubic2d_backward_ops.h,sha256=8keHM89k1VdnPkJ7s3LWAgYvTG9OkeB3bnVaikIskqs,2783 +torch/include/ATen/ops/upsample_bicubic2d_compositeexplicitautogradnonfunctional_dispatch.h,sha256=YQGrpKSVGipVCMeMW7bWxzF-qo4CEbuSwsMOlR_Ynag,1173 +torch/include/ATen/ops/upsample_bicubic2d_compositeimplicitautograd_dispatch.h,sha256=msHeZWZxQA0x0eJLIabzjULzNZZ0bkYXhjX79giE61M,1074 +torch/include/ATen/ops/upsample_bicubic2d_cpu_dispatch.h,sha256=CtpdVyjVgZqQy0KkuD6wpGfIxUjBgTLTzfBBTUr3CxI,2015 +torch/include/ATen/ops/upsample_bicubic2d_cuda_dispatch.h,sha256=cqoohvqdyTmOi8FVYR-Bl90JXtnM-lf1MG0-HkVdMCM,2017 +torch/include/ATen/ops/upsample_bicubic2d_meta.h,sha256=lwrdXhO_rnv7aBVsEAs6K522g0yPsJg5tvIoKMwBCKQ,702 +torch/include/ATen/ops/upsample_bicubic2d_meta_dispatch.h,sha256=gWKB8Eztw0Sn8uelAuC_DeVhaxHuZqtkz3SCzUD5758,2017 +torch/include/ATen/ops/upsample_bicubic2d_native.h,sha256=W8SdBw8j9op-3UCz6RayRHpPeS-2Uaj1fedT-p-Lwz8,1229 +torch/include/ATen/ops/upsample_bicubic2d_ops.h,sha256=_vsAw5_MVcV1OYmLoSdzvzxWqmm19EQOAttEE1hrhLc,3347 +torch/include/ATen/ops/upsample_bilinear2d.h,sha256=UIiOrpFbAagcHezrEHbxtwWXFFqNMRe-IEffs2AO7fc,11893 +torch/include/ATen/ops/upsample_bilinear2d_backward.h,sha256=BEk3_Wnt_vP5d_tXieR2H8fQlm_UfLokqKpbuHDSOUs,7715 +torch/include/ATen/ops/upsample_bilinear2d_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=sG5ZlCVMucdjq-RkQAD8DyL0CoczeR5RyTTZX44dMyA,1267 +torch/include/ATen/ops/upsample_bilinear2d_backward_cpu_dispatch.h,sha256=TM2p8KxeW0YIcPfwA23XNZhvkq2NG-Kcmasg5JwfyCk,2325 +torch/include/ATen/ops/upsample_bilinear2d_backward_cuda_dispatch.h,sha256=i8MM9qu3HZb1cdiYjUHKGH7R-C3sZDgrttBquU6_vGY,2327 +torch/include/ATen/ops/upsample_bilinear2d_backward_meta.h,sha256=K5S00clh9XDd_BMmVD5I4QBOs15OiIHQsCqpOhX7Bjo,753 +torch/include/ATen/ops/upsample_bilinear2d_backward_meta_dispatch.h,sha256=zfQ3iPjO5RoEcUsHbFiWmpRWq086x2nMmnAr9rX7GC8,2327 +torch/include/ATen/ops/upsample_bilinear2d_backward_native.h,sha256=L44HjS0Ta5hNtlbKbAGy8W4BOE8ABZm9cNv-rercaMM,1198 +torch/include/ATen/ops/upsample_bilinear2d_backward_ops.h,sha256=vwnnIsnxhRXHr5Ck86X60GbQsSR6Tmz33LV_UAEXyo8,2789 +torch/include/ATen/ops/upsample_bilinear2d_compositeexplicitautograd_dispatch.h,sha256=8KYECV9FcyULdEjAAe-WvD1vLnobI3iqmvqcYQ_NwhA,1540 +torch/include/ATen/ops/upsample_bilinear2d_compositeexplicitautogradnonfunctional_dispatch.h,sha256=D_oNMKjQ09EvqWWHC9KC32EWUR8lJ2H6-Y-zhluvDm4,1175 +torch/include/ATen/ops/upsample_bilinear2d_compositeimplicitautograd_dispatch.h,sha256=WtSQs5sCNfo4XRCSnAlibyXnm3Y0m9KqzHAx4S2054g,1076 +torch/include/ATen/ops/upsample_bilinear2d_cpu_dispatch.h,sha256=hx2_mSwjsosbipvQ2sZ1FTcyV-AKCoimZiYt-rmTbFg,2021 +torch/include/ATen/ops/upsample_bilinear2d_cuda_dispatch.h,sha256=2w-1bX_OxRPbp_Gu6V0kaWVVtVVLphrwuapwAb5rYk8,2023 +torch/include/ATen/ops/upsample_bilinear2d_meta.h,sha256=u17HeTgFXcT7Z-b7wH5k-ndF93Hc4XKI-D87A_mquVI,703 +torch/include/ATen/ops/upsample_bilinear2d_meta_dispatch.h,sha256=BcczS1bkv9wJFbUqmLAPPTZ_HRUUeO_zelrCrB-b9sc,2023 +torch/include/ATen/ops/upsample_bilinear2d_native.h,sha256=0K7qi82ZQflQ-IhLeQNx9Bz2SEf5ZuXZeOd7sr2D9t0,1679 +torch/include/ATen/ops/upsample_bilinear2d_ops.h,sha256=SS54w1A5cS9TzdEUO1jdJg2NxLZSxz8w8jW9xlMsBuU,4366 +torch/include/ATen/ops/upsample_linear1d.h,sha256=FtN8IHabV20qbbe4tOSY08F13qxXhPsT0KSo_scxFZA,7057 +torch/include/ATen/ops/upsample_linear1d_backward.h,sha256=RekTPcqwR550LcA3WQfPImnd_KvOMO_WUmz14cykJOc,6813 +torch/include/ATen/ops/upsample_linear1d_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=xDwaqDgZv-99LIKvW7AqE3YEBu6PygdxZBNO4y_qEJI,1161 +torch/include/ATen/ops/upsample_linear1d_backward_cpu_dispatch.h,sha256=efpmAcvOnHH3l54XB-rTShh46nyhmHvFNnqag_Fdc44,2037 +torch/include/ATen/ops/upsample_linear1d_backward_cuda_dispatch.h,sha256=2EOky4bMpeN5MSRgbP3osKsaTTnCpr3UMSIVbV6DYKc,2039 +torch/include/ATen/ops/upsample_linear1d_backward_meta.h,sha256=1JWxrB_15aAxZMFp2dvRfnA4ghAihjVXgcPKAH-n1eM,715 +torch/include/ATen/ops/upsample_linear1d_backward_meta_dispatch.h,sha256=JuguFDfbeHlk20uOPCCdXFa-c169W_ZvDrvDX_SLT8s,2039 +torch/include/ATen/ops/upsample_linear1d_backward_native.h,sha256=qUIk9sHVklsTJ333NlSKhNZtkBHX70cSuT7pYWAI8j0,1116 +torch/include/ATen/ops/upsample_linear1d_backward_ops.h,sha256=_lf4t_VccnHMLltg8SA3jc99OaqY7c5yG63zi_RZlY0,2535 +torch/include/ATen/ops/upsample_linear1d_compositeexplicitautogradnonfunctional_dispatch.h,sha256=ldXj03z_Lvg1T3rK5cBMc1WM3gEWUxo_NfnjAke0cPU,1069 +torch/include/ATen/ops/upsample_linear1d_compositeimplicitautograd_dispatch.h,sha256=mFBdFYKiYCmIJGqVnRADGKGWccZXMklR2H5HYXLA68o,1072 +torch/include/ATen/ops/upsample_linear1d_cpu_dispatch.h,sha256=XzNVVySpBiS-jhNaphptKmSpSm3vxM4ES9ZSskfJGnk,1733 +torch/include/ATen/ops/upsample_linear1d_cuda_dispatch.h,sha256=4piemFWnKDhOoWu0sQVxWppUOrnlTlgkd_585XyJBwo,1735 +torch/include/ATen/ops/upsample_linear1d_meta.h,sha256=rvS1nnsmTA1AHOWUwbBg1eTHrU9SgryhwwtSX6vWltg,665 +torch/include/ATen/ops/upsample_linear1d_meta_dispatch.h,sha256=4b3uIHRhB0EB87OArZvcvNe80vEnL1BPBYTHaXrQPEM,1735 +torch/include/ATen/ops/upsample_linear1d_native.h,sha256=0mYL-q3uBhsnOAORx2xirNAK-67cO9uKzPHt9NjcnEc,1151 +torch/include/ATen/ops/upsample_linear1d_ops.h,sha256=jn11im-pgM5muQxOeRsiHV1ibYanvhYxTQ6S6D_9tTQ,3096 +torch/include/ATen/ops/upsample_nearest1d.h,sha256=eqJneeVndihK6ZDrqgh1UXZjrQuv1V3HJu5pyI7B3AA,6378 +torch/include/ATen/ops/upsample_nearest1d_backward.h,sha256=vPUMXp4z0WBNGM0sZIxtfZfbwmRbfU961huemSrRfGs,6304 +torch/include/ATen/ops/upsample_nearest1d_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=s-pRUoHc8xUYGb8H_MeR1HkdAP6uqtFhkHRFfjdKPQk,1123 +torch/include/ATen/ops/upsample_nearest1d_backward_cpu_dispatch.h,sha256=rcuOLcDVt4l1PlAuuPnwmIDqecFMldeR0Y07TGmzwZg,1923 +torch/include/ATen/ops/upsample_nearest1d_backward_cuda_dispatch.h,sha256=n5aO9rZXJUvQoaZ8TXy2kckNfL_ykBQl6ddumlzr4Qo,1925 +torch/include/ATen/ops/upsample_nearest1d_backward_meta.h,sha256=OHZgR5ojLr_TIuhXl8Lj2s2D8FXpv196impL_iISohw,696 +torch/include/ATen/ops/upsample_nearest1d_backward_meta_dispatch.h,sha256=3OcEZL3AShPG2tPk5aQVRYRVxgf6wy8ab-yInI11jiU,1925 +torch/include/ATen/ops/upsample_nearest1d_backward_native.h,sha256=33UIcU2O4tGS5OGy2rMpDh71yBJe_4FLrmk8yrbh2aU,1081 +torch/include/ATen/ops/upsample_nearest1d_backward_ops.h,sha256=q_86DsRwLb0eIvQ-5M8uw2CL4cJepKPjWnN8fczkbYE,2409 +torch/include/ATen/ops/upsample_nearest1d_compositeexplicitautogradnonfunctional_dispatch.h,sha256=5eTJNoafaSlEvq3-HhWKGxSZ_wY3I2NgIXOCzS3yhNk,1031 +torch/include/ATen/ops/upsample_nearest1d_compositeimplicitautograd_dispatch.h,sha256=d2N5Bb8I8-P_G98xsTdl_KuH1xB0ka2hn_AXz_8EHaM,1034 +torch/include/ATen/ops/upsample_nearest1d_cpu_dispatch.h,sha256=obvt6jmEw3Nn14uunyrtxKEzunmH-zTgu9n2FVCaOKw,1619 +torch/include/ATen/ops/upsample_nearest1d_cuda_dispatch.h,sha256=NX95MxEoeZp4avVVxhlrc3G0OMxQWdCeKkm2PDBWDe8,1621 +torch/include/ATen/ops/upsample_nearest1d_meta.h,sha256=WycfXYKHFlUHA7iDKoVO8Tvg2syo8h4mZHXuzKa4lIc,646 +torch/include/ATen/ops/upsample_nearest1d_meta_dispatch.h,sha256=lsi0knwK6de39Zo4sRDFbKIWLDIfPV0wYzs-Xz0yCRI,1621 +torch/include/ATen/ops/upsample_nearest1d_native.h,sha256=nACWY2q3hQXNesxUIwqV_PoQT5WS0eHC-ADfEDdmq7g,1097 +torch/include/ATen/ops/upsample_nearest1d_ops.h,sha256=Oic83H955CJ9ZzhWAF1KynGmL_B6q6K7HX6X1JS-X1A,2907 +torch/include/ATen/ops/upsample_nearest2d.h,sha256=ZTOdOk6zi8y14rX5eMydUQHenDm6y15S3FHXFmPs-bM,10752 +torch/include/ATen/ops/upsample_nearest2d_backward.h,sha256=gK-Wp6niUJraCE70X3ieRlXn_cUSywCCpoRa1IaVNxE,7144 +torch/include/ATen/ops/upsample_nearest2d_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=NwLpCZftg1BnIfMriZrvKHBk4UvsFUZ2G7p9DYRFWz8,1225 +torch/include/ATen/ops/upsample_nearest2d_backward_cpu_dispatch.h,sha256=btGri4XhpOAWxu6kY_oi_gUCwV3NRq_NPk6B6pSoCd0,2199 +torch/include/ATen/ops/upsample_nearest2d_backward_cuda_dispatch.h,sha256=71XLubnhtGPaSpERBFQ8LyJ0NR8Rb_meCyQ2vwuYAks,2201 +torch/include/ATen/ops/upsample_nearest2d_backward_meta.h,sha256=Pw1k8jqeFavHfv96FrWXsATOBwO1tESAkws2iMNuXzI,732 +torch/include/ATen/ops/upsample_nearest2d_backward_meta_dispatch.h,sha256=pOHZFwOnp67owY2H-pl5XZGPo8-hBFkFwmwUkYK4XKs,2201 +torch/include/ATen/ops/upsample_nearest2d_backward_native.h,sha256=LaDRgFf54_W_VoSru_3gYkfg-BYCqIwAQItdsLWHsZY,1153 +torch/include/ATen/ops/upsample_nearest2d_backward_ops.h,sha256=HvO198gXwOP0gaVLO7JD1qG8Tbh9F7vRKyem0UqWt-s,2651 +torch/include/ATen/ops/upsample_nearest2d_compositeexplicitautograd_dispatch.h,sha256=6xD14IqrGK5kLmLEyo17b0AcB9zCCElckf0FKiEv0oc,1456 +torch/include/ATen/ops/upsample_nearest2d_compositeexplicitautogradnonfunctional_dispatch.h,sha256=UN-W3fIZgZkERLOYAkz60dy294Qbebb9amPJjItFdLE,1133 +torch/include/ATen/ops/upsample_nearest2d_compositeimplicitautograd_dispatch.h,sha256=0hF5chYaqZXcFMYuddnCNMmOt5d9Ys2TgjqTEhFO2Dc,1034 +torch/include/ATen/ops/upsample_nearest2d_cpu_dispatch.h,sha256=V8dOhP9aRQDUvMxvSY-hy9n75nFbOi47CqSJcFD0qTo,1895 +torch/include/ATen/ops/upsample_nearest2d_cuda_dispatch.h,sha256=i5KAUhDWBU2l1RQru4g7jdaReu5iVyy4wjyjdhg5dIg,1897 +torch/include/ATen/ops/upsample_nearest2d_meta.h,sha256=0_7b9VTCveQdHss69AqCVu30365bfEtscAINEAtI3zs,682 +torch/include/ATen/ops/upsample_nearest2d_meta_dispatch.h,sha256=vhWAkwKvwaDRAbYbzxElgdNI5st7oa0wZfp2Ooro48I,1897 +torch/include/ATen/ops/upsample_nearest2d_native.h,sha256=4SHgIREFmwNdx2QW6HyGXLoNhOtX3748ca73fyNq84k,1571 +torch/include/ATen/ops/upsample_nearest2d_ops.h,sha256=pnXUUOePrMqh0tmR92H5sm7ciXfXKZeY7Yn1O5kHQS4,4090 +torch/include/ATen/ops/upsample_nearest3d.h,sha256=PuyfIUGHDWNRXmUSQyvXKBMZlLXajqt_dS1mCTQQO8c,7998 +torch/include/ATen/ops/upsample_nearest3d_backward.h,sha256=TSs5_IG8gxgNIOm0qGsPV5JF9kF5O4NRrmwg4whfb6w,7924 +torch/include/ATen/ops/upsample_nearest3d_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=hMvi1awyh9z83k4OCf7-p-3j-Djs5WCXyUv4oMiUm0Q,1323 +torch/include/ATen/ops/upsample_nearest3d_backward_cpu_dispatch.h,sha256=-ldbWLjNrybJ1k4Jc8QDcqBHuPpYlmNwY-6TAWIjsx8,2463 +torch/include/ATen/ops/upsample_nearest3d_backward_cuda_dispatch.h,sha256=k_UHW8upbibFPU76qT-R1QkOUHum1okqh2kU1L0BroI,2465 +torch/include/ATen/ops/upsample_nearest3d_backward_meta.h,sha256=gDTDdiM3vHFtTu4x7r1slPVle-5qo1vsSDWIaZ_WgyA,766 +torch/include/ATen/ops/upsample_nearest3d_backward_meta_dispatch.h,sha256=WnRqi9cqQQjICxebVjRkvE6ynVZN8UKqxkLY9WUm3fE,2465 +torch/include/ATen/ops/upsample_nearest3d_backward_native.h,sha256=v8D9_b6e_nUVI83vFa8SKnUjdkXoDpcuDsywWfB030I,1221 +torch/include/ATen/ops/upsample_nearest3d_backward_ops.h,sha256=NMjy-bpA3z26zxT2aNs1BAu_sgurP4m4mQZ6Uxhy6Zk,2881 +torch/include/ATen/ops/upsample_nearest3d_compositeexplicitautogradnonfunctional_dispatch.h,sha256=Trg3MzxmeilGnwurNoe6uQ5cG_i7Xs45onWAAYeSTFM,1231 +torch/include/ATen/ops/upsample_nearest3d_compositeimplicitautograd_dispatch.h,sha256=uI3lvYRWSyTrVZY5Q6Kh-81MwyljbfumqjsAcsbnMkg,1034 +torch/include/ATen/ops/upsample_nearest3d_cpu_dispatch.h,sha256=9wh0zXvMzsLIZsLi1PedzxxnpG8KA7TqwVAiO2WOkgM,2159 +torch/include/ATen/ops/upsample_nearest3d_cuda_dispatch.h,sha256=Y7Zvb0KQqlmuIOzUAh-0Zxt6NMocHXmi_zDvIruqQmU,2161 +torch/include/ATen/ops/upsample_nearest3d_meta.h,sha256=fY8_aYyY30T7KJP9D7Iyqd8I9x46PJeVmWlRjGBlhKo,716 +torch/include/ATen/ops/upsample_nearest3d_meta_dispatch.h,sha256=L-_qcQcb6BVSpZ52kkzRHRYHN7rlowK1aXkLmtAOGvU,2161 +torch/include/ATen/ops/upsample_nearest3d_native.h,sha256=b9lwvl5k-iGh3Y0v9vj8POAXilNkCmxCWT7FRTGk-fg,1493 +torch/include/ATen/ops/upsample_nearest3d_ops.h,sha256=LbNhyFBtZntzO1rgZzanB09TMqg1I-YaN_78-f38yoQ,3379 +torch/include/ATen/ops/upsample_trilinear3d.h,sha256=nw7xtTt5MIIJEdLweJ9EzyhFzkD3GPy9sPR1RU08sRQ,8800 +torch/include/ATen/ops/upsample_trilinear3d_backward.h,sha256=Cj5UpzHqYUScsis2woNBZ77dbeb0xH1BeIpQcG85d04,8526 +torch/include/ATen/ops/upsample_trilinear3d_backward_compositeexplicitautogradnonfunctional_dispatch.h,sha256=GylhoeNOR_ATIoTQHIbmC3fQBX8v3x6mPxQ6Fpp1H4o,1367 +torch/include/ATen/ops/upsample_trilinear3d_backward_cpu_dispatch.h,sha256=sCVMYrmjLGQtckZYcaMXHhSPMLVouFkHI-ozMPnj46c,2595 +torch/include/ATen/ops/upsample_trilinear3d_backward_cuda_dispatch.h,sha256=wjd4BwR-kG4xWczEgborR5PbgTeEKHcj5OrW9ncS8YA,2597 +torch/include/ATen/ops/upsample_trilinear3d_backward_meta.h,sha256=ZvV0_HgKhLBW1XL6NtLqdy2L2NbPqdFF5_qNKHj6wQU,788 +torch/include/ATen/ops/upsample_trilinear3d_backward_meta_dispatch.h,sha256=ovD4GYR8T69fZx34hqHFQjxjV_euJUR--qOFE0zgTSo,2597 +torch/include/ATen/ops/upsample_trilinear3d_backward_native.h,sha256=VsxdpskwUIIZvQQPkWfcLZA5RfOcS6GUP3OmqY0O5bk,1271 +torch/include/ATen/ops/upsample_trilinear3d_backward_ops.h,sha256=AwDvVAxYE-IYoexp52ZNjQvgbbbiwrbmtfe3AFASJ1I,3025 +torch/include/ATen/ops/upsample_trilinear3d_compositeexplicitautogradnonfunctional_dispatch.h,sha256=AXhNNoqmwIozjvaOwSVvgayfPGE4wUco_71qNI-7tmQ,1275 +torch/include/ATen/ops/upsample_trilinear3d_compositeimplicitautograd_dispatch.h,sha256=1l-eOiL_SHd8EPKL5gvp8gBQzX2noQ_ShZAt882OiHE,1078 +torch/include/ATen/ops/upsample_trilinear3d_cpu_dispatch.h,sha256=RyeObxZmEu5b-SCZYdYr9rVcf66M9oJI_AHXk7ZDATA,2291 +torch/include/ATen/ops/upsample_trilinear3d_cuda_dispatch.h,sha256=9BllU3KVNb93Er7nod_74lwBWy167RlQkCGa_3191rg,2293 +torch/include/ATen/ops/upsample_trilinear3d_meta.h,sha256=fENC3p1k_Vyv0RyOSBWaGHIoZgf4XDxAXRmPHd_z92A,738 +torch/include/ATen/ops/upsample_trilinear3d_meta_dispatch.h,sha256=MvLV90YqyEdSt3U2o8M3WrftG-j9DzszYlIwC025fQc,2293 +torch/include/ATen/ops/upsample_trilinear3d_native.h,sha256=x0LOFAq8b43lN7htlnwoP4Ejbr300Cy3_Zom3-7d9jU,1309 +torch/include/ATen/ops/upsample_trilinear3d_ops.h,sha256=W8qcfc_2ZJh1vHzNY0Eyv91oplkMQddQy-tbmX4IcOE,3595 +torch/include/ATen/ops/value_selecting_reduction_backward.h,sha256=NO6t3itpbike1gEo3dKFpbroNyj_u4ntkSNmPi-Gcdw,2081 +torch/include/ATen/ops/value_selecting_reduction_backward_compositeimplicitautograd_dispatch.h,sha256=h_iR2Hdmg98W58Uj3b3HlEhI95kZ4s8kNMS4pQoYh8I,1041 +torch/include/ATen/ops/value_selecting_reduction_backward_native.h,sha256=8vEHPDiOOkNS4PuRS1pEipSfgjTF3ECyGrQhK7YG5to,771 +torch/include/ATen/ops/value_selecting_reduction_backward_ops.h,sha256=vrx935tkNdZNrwiv8bH7zJNkKPa8jE7yy081PiH_cuo,1316 +torch/include/ATen/ops/values.h,sha256=0uw2_VbPPzTYf9kiv6WdAmgRcxlfcQ3ZR-bw144BwF4,503 +torch/include/ATen/ops/values_compositeexplicitautograd_dispatch.h,sha256=wZ0ZxzoPlFmOiTZ36yHmIFaloD1qC_tyx9UrccsHMVs,764 +torch/include/ATen/ops/values_copy.h,sha256=o55Y8qAQEV-yMxLGcRFKInxYSe4Jvh6PJURsw6jTXRo,1077 +torch/include/ATen/ops/values_copy_compositeexplicitautograd_dispatch.h,sha256=cvPG7K4CH6Ug0zbQbmP947Iybrob5Qvqzqhk1z7cYB0,877 +torch/include/ATen/ops/values_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=OzdOH5gU0sK_qg3oBFTI5y-eNVvxY0oXjmcTDopxckQ,795 +torch/include/ATen/ops/values_copy_native.h,sha256=h_NJDkXMh3DqXqfBedNJnNMElj6xxgonZiY6waT5XMM,564 +torch/include/ATen/ops/values_copy_ops.h,sha256=Ul2k5t8dRqQkOYz3hwW1EUb6oqP5MqNtBHdbyPsL854,1577 +torch/include/ATen/ops/values_native.h,sha256=8IZRVzzqhvLWsZtrOgNR65MEjKAjUOorfM4lrxSuXr0,671 +torch/include/ATen/ops/values_ops.h,sha256=AlqlUbF4A_ZsZcnqnpjatH54ksMSn0WJgKKJNSk8mOk,963 +torch/include/ATen/ops/vander.h,sha256=tOTdNWa6JT3LFYlgMXZtjTiHUgar3Z9gOERFO7EPJJ4,745 +torch/include/ATen/ops/vander_compositeimplicitautograd_dispatch.h,sha256=Z-aWH_ByvCtoTh5hk_voZuky3pRIfOxS9VkXuc91des,827 +torch/include/ATen/ops/vander_native.h,sha256=y1dkYRxcAe74EZ7R99w7nA64AnV8HvO-euDgpUR4rWI,539 +torch/include/ATen/ops/vander_ops.h,sha256=21XDvnutgYnWybcgHf-bOuFPJHFx4p6vg10sejJTMss,1106 +torch/include/ATen/ops/var.h,sha256=9smxLV8tl-MlyrdDXiVvufyiQWVNUy3Np_xh1wsElhc,4871 +torch/include/ATen/ops/var_compositeimplicitautograd_dispatch.h,sha256=PyGjy3MZqsC30g5szjPU6VVsRJ1FqJNBb6vw0RAEC_Q,2021 +torch/include/ATen/ops/var_cpu_dispatch.h,sha256=pe1Qx5675Ek8otsiaFj4nVn63p-UV2JZ6GRX09cZaVo,1213 +torch/include/ATen/ops/var_cuda_dispatch.h,sha256=rr-EnsRDkhAGBw5DiwoqnXG-wpq600aV2sV9Zgv4g6Q,1215 +torch/include/ATen/ops/var_mean.h,sha256=iex1B_u_wdOUAOcXtEJ9K6CgEoWWdyG0v678UTdSRws,3217 +torch/include/ATen/ops/var_mean_compositeexplicitautograd_dispatch.h,sha256=M4MUfT9vFM1WIRXVhBlvT8IQO1fDd7wUI3NswLfredc,1183 +torch/include/ATen/ops/var_mean_compositeimplicitautograd_dispatch.h,sha256=X61iT1Er85cDdXUeiE5FXrp9ZXtCooC-XVTHvxjnX7s,1273 +torch/include/ATen/ops/var_mean_cpu_dispatch.h,sha256=17C14_x3iQ3XVsd2fk8HS92Db7wQr475SqbalZnuh7E,874 +torch/include/ATen/ops/var_mean_cuda_dispatch.h,sha256=RGu96484viNU8J1snzAplLUkyTG6tQV2uQ94crVrwOo,876 +torch/include/ATen/ops/var_mean_native.h,sha256=nhNCgd-Sn2xwglWPDLgN3O9lsYfoaoYK9gONOBUapq8,1437 +torch/include/ATen/ops/var_mean_ops.h,sha256=WHGenAwiMOxq82XKfwOw1A5X3tLkfqpisZpMoErXNUg,5696 +torch/include/ATen/ops/var_native.h,sha256=RMO35w-zj2lQLNS0bXcnnr2g2hLMwXgs6c_aFSzcREI,1640 +torch/include/ATen/ops/var_ops.h,sha256=6b52oSE6RnEetK7VzZMSHdblRLZ_88u7X07M1WB6jHg,7504 +torch/include/ATen/ops/vdot.h,sha256=xV9dVRVUeem9awgUulPkDFU6qbyOsATxB7sp4C97G7Q,1148 +torch/include/ATen/ops/vdot_compositeexplicitautograd_dispatch.h,sha256=ZKCcA9apEkSe06mF5ORCbyv0P6j5S4ENgVwzy7Mmwq8,915 +torch/include/ATen/ops/vdot_cpu_dispatch.h,sha256=tpTH2sLO0-KyP3Ngte3Yy9I0hEdi_7Se6lGJ6rBI3f0,744 +torch/include/ATen/ops/vdot_cuda_dispatch.h,sha256=fuGVsu5H9Fk6zo37k2ikCA95uNMcvMIS2Rtvo25xdoQ,746 +torch/include/ATen/ops/vdot_native.h,sha256=HniU1ovs5DlDKN921SDbPoFoBZjL8VgYlK2KIIm3luk,685 +torch/include/ATen/ops/vdot_ops.h,sha256=_VCKaYfkqjyUoEnLkvMqQ2FFfU6v0374P0QZcn6LXkA,1707 +torch/include/ATen/ops/view.h,sha256=LYf3mK0ei_v55uwZgcP1hVxPvCdzEpvdOislmN2KGW8,972 +torch/include/ATen/ops/view_as.h,sha256=Ei31npN8suSfOxZQ6QpSkBzvJIbEeA0yKwUNS1TJWCQ,504 +torch/include/ATen/ops/view_as_complex.h,sha256=0aWFPwcGZPGmqer8FBCHkRhcSG-BTJJ2WxuNQH9PT7E,679 +torch/include/ATen/ops/view_as_complex_copy.h,sha256=X0m0e5Y8JOYN0MIxY-tRo_6AvKSPkr4qlDfuRux4F9U,1167 +torch/include/ATen/ops/view_as_complex_copy_compositeexplicitautograd_dispatch.h,sha256=wFiEAFvEymVv5H_kfmHPg_6N0z_3xANyBVMVNs2e0CQ,895 +torch/include/ATen/ops/view_as_complex_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=_m9Kh4YzfcJAioObWETYd7o-s91GBL-CDkm8sOLmKfE,804 +torch/include/ATen/ops/view_as_complex_copy_native.h,sha256=f30IYkt7QgedBAELPgT_9IhRlLz54ZseEuxnZp6xrQU,582 +torch/include/ATen/ops/view_as_complex_copy_ops.h,sha256=CmaMV7VftNaIG6queVJp7hy5wlND0YwVmtgeoWa3k0o,1631 +torch/include/ATen/ops/view_as_complex_cpu_dispatch.h,sha256=NtJGQ60SXJxFx61GVRv4faG_4N-WyTrPfgR_TOP5fzE,729 +torch/include/ATen/ops/view_as_complex_cuda_dispatch.h,sha256=ev_1isqi35w5x5Ukf6x4YpMWJ0-YFslSiUenneor1Lw,731 +torch/include/ATen/ops/view_as_complex_meta_dispatch.h,sha256=6Y1J87LXDtZeqPkLWcGl_zcFhQ1sr9Tl0k1j_UUjwpE,731 +torch/include/ATen/ops/view_as_complex_native.h,sha256=IQPC9zZt4rcKDnPiuNbcFCtdh9N7SknVk56ZW7lNmew,485 +torch/include/ATen/ops/view_as_complex_ops.h,sha256=pBcnji99SQZoch6i03fio2WOqiobgqGH00cT_Q-Ms0Y,990 +torch/include/ATen/ops/view_as_compositeimplicitautograd_dispatch.h,sha256=LrQARFU__8RnqvMfrRF-JJSc_WK039Ki2U261eDyFR0,791 +torch/include/ATen/ops/view_as_native.h,sha256=GMmX4B7qQ5989f2KCi1Aq5CB5c5yd3xBLkVoTaF1ubM,503 +torch/include/ATen/ops/view_as_ops.h,sha256=qREIn1_0LclLOLbPHSMsvptdeYLcVggYIjNcZphe-zc,1052 +torch/include/ATen/ops/view_as_real.h,sha256=sMg1YbY39luWdfEJrH6HJSo_9DeB9JWZC6r5OaPbq_Q,667 +torch/include/ATen/ops/view_as_real_copy.h,sha256=jV4GbgvZpqJ2imZo9cYivGYcCsQS-cWC_8DtOUAuOfA,1137 +torch/include/ATen/ops/view_as_real_copy_compositeexplicitautograd_dispatch.h,sha256=LUR2CsUwZd32YCTbRwoM-6X2M0hLlqSvF7Vbcquib9Q,889 +torch/include/ATen/ops/view_as_real_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=RmDY93B4glkGQ61neiw9Q9Nki7gmthSlTxzVTDdb0HQ,801 +torch/include/ATen/ops/view_as_real_copy_native.h,sha256=3Q0VjPYNwVWCgTSnt_X4u92ZHeNkhtGHgB2Az6ez7So,576 +torch/include/ATen/ops/view_as_real_copy_ops.h,sha256=lmmv4Z496benim6Jya0x57MUNbtmeILPjrCz9NRhpog,1613 +torch/include/ATen/ops/view_as_real_cpu_dispatch.h,sha256=Dql2XNSBOkJ4zVPAVB5WduYxEWIZU4edsVuXRNFRqx8,726 +torch/include/ATen/ops/view_as_real_cuda_dispatch.h,sha256=VPtRv93nAOjecTei4HbGhXQ2UY9jG_018FH0Cwj92-I,728 +torch/include/ATen/ops/view_as_real_meta_dispatch.h,sha256=JoEI_BGzdoNeHnKzGTjl2AsrjAlmDI_kP3YFNGINKgc,728 +torch/include/ATen/ops/view_as_real_native.h,sha256=TX81CaHs9uejG__7z2_D4zPNK2uV6gNEFttmvV5rIzI,482 +torch/include/ATen/ops/view_as_real_ops.h,sha256=yY4PXxWA0_l0_quCtYmFakpzV5YHGdE3dNHZwJR0a4E,981 +torch/include/ATen/ops/view_compositeexplicitautograd_dispatch.h,sha256=Cpp23JscpJ7BQ2Vc8z46TsDx21aK0HVtrwbzM_FQ-xc,784 +torch/include/ATen/ops/view_copy.h,sha256=XURlnETyrtSvxCIIN38sKhluioq8o_Y9Dj2ZEk6MSuc,4313 +torch/include/ATen/ops/view_copy_compositeexplicitautograd_dispatch.h,sha256=6CRxBiPOyx_-nd4Ou6BZy3XKqtigTqAb9yBYBOTzg6k,1353 +torch/include/ATen/ops/view_copy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=Ym30vRhIo5vs_6E8hkcAEgstMdKTE1N_Zsyzl44hY-w,984 +torch/include/ATen/ops/view_copy_native.h,sha256=DpOWfbxeSY_9MDBfGoOU57f8IUFk3Hi02KGDeB22Uo4,820 +torch/include/ATen/ops/view_copy_ops.h,sha256=oTavctVgCKnxJ7hudZTqZWmD8SfL5tpUiLV5CaKLcpg,3040 +torch/include/ATen/ops/view_cpu_dispatch.h,sha256=zBKOO38ZDoGx8Oy7Lolpl5pQUJwI56GbjUv1Cef0ppE,825 +torch/include/ATen/ops/view_cuda_dispatch.h,sha256=9ryNyKP12NGA6cRxJ5yIq0n337Xhhr6R-PsvgZh-_Qw,827 +torch/include/ATen/ops/view_meta_dispatch.h,sha256=57oTuaCVEuzW_Nl4WQsIqFsm2TCKCmZVDgkhyh9JcYc,827 +torch/include/ATen/ops/view_native.h,sha256=T0ljnhMmy8qx6O2M8_-d1eK1IXwkhIXdrQ3lhd5QZJc,738 +torch/include/ATen/ops/view_ops.h,sha256=rNq6pjN7CkR27UuEO2ssRjWwkQljKsLHxp19Xl3pjYY,1640 +torch/include/ATen/ops/vsplit.h,sha256=e5sVDD18fvJK7Zngr1ZxeKFQic-nRNaFmCLAv7_RVV4,939 +torch/include/ATen/ops/vsplit_compositeimplicitautograd_dispatch.h,sha256=pvbVUDBJs2GmMP05mUqLH55iA3PmkkO5Sf14iDAOiw4,891 +torch/include/ATen/ops/vsplit_native.h,sha256=1nCtTpOw8Jk8fdV_HXHICR3ri_7XxFNbLo4CW5wkqzI,603 +torch/include/ATen/ops/vsplit_ops.h,sha256=1ARZ0z4ynynLkC3vcELDYMKJ8SsinwCCJv9FvyfB8SU,1742 +torch/include/ATen/ops/vstack.h,sha256=lpQW1pj3iZGVc1MyTpdHFDXS-xGsz0rz9hId7rgOkb8,1048 +torch/include/ATen/ops/vstack_compositeimplicitautograd_dispatch.h,sha256=v0mm6a8MrJYi8e0ULYKziiEdQUdv2P7kvmgo6cFoNaI,918 +torch/include/ATen/ops/vstack_native.h,sha256=Ka91xnh6Hgbt-RzmQTseVuJNDSLYCq3kmRhvXerPyvs,552 +torch/include/ATen/ops/vstack_ops.h,sha256=LbSQasOZ6eJotBdWon_wJI_chXxrXUfbAY4TASyRZdg,1545 +torch/include/ATen/ops/where.h,sha256=Sx6NC3gHuYVC_1OA1k8AlqpB6rxOH2XzrhrxbQYQaI0,2303 +torch/include/ATen/ops/where_compositeimplicitautograd_dispatch.h,sha256=9c2jv_00MuqzYigsZrwDot2RsBmy_YKMVBIBCF_8OJU,1110 +torch/include/ATen/ops/where_cpu_dispatch.h,sha256=UyT7I7dXC067B8Qihp-mnUviKnIOIGmkN8HxLhkqtOQ,1042 +torch/include/ATen/ops/where_cuda_dispatch.h,sha256=3VoIl5JyvqVZAbuhq2D5dUGQ6287K2I5DHblvSByHqc,1044 +torch/include/ATen/ops/where_native.h,sha256=UgnJ7yVI09Ctr3bRVM8DRIM7vaSOWXWyoTZ228t6SeQ,1337 +torch/include/ATen/ops/where_ops.h,sha256=ZC4YMVturkSTaP1EXrpXQAWcUYStceYjQvmdrEkRs98,4627 +torch/include/ATen/ops/xlogy.h,sha256=o-6Q1uOT2zxJlo1nNTmk1e_G3zndH3lhfBOGcwrp8tc,3064 +torch/include/ATen/ops/xlogy_compositeexplicitautograd_dispatch.h,sha256=aCndbKpFqyvidjXsftZOaQtqbVasfDgvH4TfNRNbqEA,1358 +torch/include/ATen/ops/xlogy_compositeexplicitautogradnonfunctional_dispatch.h,sha256=9469MOB2sbfC6ZX6-98d0VKBKAHUBeIijBe2x3X2YrA,891 +torch/include/ATen/ops/xlogy_cpu_dispatch.h,sha256=a_4jjJPvRKe8W58ffFF6X9jcc4Izlg3RiYDs6w9Kf_E,1028 +torch/include/ATen/ops/xlogy_cuda_dispatch.h,sha256=it3-VIwe08mGOYunpBmQqrR7u_WQDSdYsTKCXAz6NGY,1030 +torch/include/ATen/ops/xlogy_meta.h,sha256=s90UV26ocqPsoU_j7I7m7HH18ccL2XL7c3Cu5QqLIOI,599 +torch/include/ATen/ops/xlogy_meta_dispatch.h,sha256=1k5c4CPLwmdU2dMFGWZYNQf7JqHMG8L-Sp0OLZ6aTRE,1030 +torch/include/ATen/ops/xlogy_native.h,sha256=CIedCIvuUb1oZ2r1qdwp0I2OsAC1yhCqRg17ha3rg0s,1066 +torch/include/ATen/ops/xlogy_ops.h,sha256=R-D4oaGcM-YmODQ8Xu9kI18YN7GAcOzw5h3hUQ7TZXA,5627 +torch/include/ATen/ops/xor.h,sha256=g7Dw5bi22HC8Iqziz3EGrWZPS41dY0Y_56HUQ_w216A,897 +torch/include/ATen/ops/xor_compositeimplicitautograd_dispatch.h,sha256=llZtzjLJvEQQuCrgVU0mvjT_ZGe8lEWekI2dEwYorgY,1028 +torch/include/ATen/ops/xor_native.h,sha256=UiYlWgWIGfEiCFt0PpAVg-00iHSvyi_51xqvgT00c84,740 +torch/include/ATen/ops/xor_ops.h,sha256=Gt2Gr0OshNenQUMsYkxGtFn3sQ41hwiNrL4PPxHex_o,2891 +torch/include/ATen/ops/zero.h,sha256=WBRzfflBvsqhZ5Ofo6G7SCj9fJCW3shBXQ9J007OXWU,1143 +torch/include/ATen/ops/zero_compositeexplicitautograd_dispatch.h,sha256=akQ1gLLIEeRjNWM6221-5HpQsE-FtekFlEkXLuNsMus,915 +torch/include/ATen/ops/zero_cpu_dispatch.h,sha256=ZrrM_UT_lg_XCh850CD4-HzzJ4joXroYO0MabtRXyuc,715 +torch/include/ATen/ops/zero_cuda_dispatch.h,sha256=s35FQV1MR_XSkax4X3GYTc6b7gKF4zRX_RXJCmloDcw,717 +torch/include/ATen/ops/zero_meta_dispatch.h,sha256=-QLfASKw9qhs_Yo9z5PlsI_3UwxT8qf6VS1JEWJelA0,717 +torch/include/ATen/ops/zero_native.h,sha256=y24QiYw762J1aP2-0smoCAj7787mOVBrkUBSI_rrrv0,881 +torch/include/ATen/ops/zero_ops.h,sha256=hix3SouKZlv3Q59u02MTBrONFdnDIkpJiFy4d_ZbYyQ,2028 +torch/include/ATen/ops/zeros.h,sha256=iVdUCeVp6AnSJ4ZwD14XGvGegWljcpz4GNifSzx8W-U,6882 +torch/include/ATen/ops/zeros_compositeexplicitautograd_dispatch.h,sha256=Kh6SRo2t9u5V6JaZozXgCjqVK4L66702z1-CgNvIILQ,2186 +torch/include/ATen/ops/zeros_like.h,sha256=4K72et7BGvARON4BY0qCOWu3Bwvhf_lNBJFYUP8mOI8,2221 +torch/include/ATen/ops/zeros_like_compositeexplicitautograd_dispatch.h,sha256=o4-g6WoSh_8juhIpBqkDUINtroPdSMr0ByLElHU-XuE,1392 +torch/include/ATen/ops/zeros_like_compositeimplicitautogradnestedtensor_dispatch.h,sha256=CXh71BT8VgCxLoY2zwGi30vXT6vqlLnFCjnM6HiDLlQ,1138 +torch/include/ATen/ops/zeros_like_native.h,sha256=jgd086mEajSHwwN1zv9qsFeuoNlzwtQZJeZecLi11b4,832 +torch/include/ATen/ops/zeros_like_ops.h,sha256=qDPPKsMRbR1nBceZHEsxrVz4byiw1-9efQKPyzCzw6g,2401 +torch/include/ATen/ops/zeros_native.h,sha256=2gyEws-GRGXKl7DucDwBvXB2H-ORKXfRrYd3YIHAsEM,1162 +torch/include/ATen/ops/zeros_ops.h,sha256=5YRb-gzLUuzNUXy_ZuQ9UAP83nD4jEq8qGd8A_Q6YkI,3906 +torch/include/ATen/quantized/QTensorImpl.h,sha256=3I_3ndOIY-Cm04o_GhJ3B3BtzDmxSAIndWnb5vcTmvQ,4021 +torch/include/ATen/quantized/Quantizer.h,sha256=X6j4TQoXgD0l-o5rrLNOz7ilIjXleXf7sY_HazqgToA,9232 +torch/include/ATen/record_function.h,sha256=JSRalvVrrzcxSbwKXMUUJQoyqWhMByZAIVZh0kv7O2A,24294 +torch/include/ATen/xpu/CachingHostAllocator.h,sha256=K5lXnX1DWqi5kOaOPclFffd7kzE94o5S3FCtgJkUbJs,1357 +torch/include/ATen/xpu/PinnedMemoryAllocator.h,sha256=p6z1z_XDauXZ_Pxz0sjDSWHbWHpkcOUynJ528Up7XiQ,246 +torch/include/ATen/xpu/XPUContext.h,sha256=Eor_d5xBekYy1nwWkNa4MSo2aX6KY3QbODtvBONnAi4,458 +torch/include/ATen/xpu/XPUDevice.h,sha256=nosB3k5z5s6_y6MsUf7MkYmonuixvYGjWCACp-_sBp0,267 +torch/include/ATen/xpu/XPUEvent.h,sha256=kv9aouELVLD9x9cQz2_ZsNBuQc-5JOuRVWNcs61PQB4,5788 +torch/include/ATen/xpu/XPUGeneratorImpl.h,sha256=A5lm90JwyW5kzmTQnobw9PuFCvnUOmIo2WcFOm6N1wo,1232 +torch/include/ATen/xpu/detail/XPUHooks.h,sha256=qq4Nt1qOukwWZtrW_alPWrxy-d4UWQBFuSLInEczUkU,1177 +torch/include/THC/THCAtomics.cuh,sha256=sbRzB6GvRcYIN-1cCMaM0h-ig8M5tmqMmy-NfmojNfs,118 +torch/include/THC/THCDeviceUtils.cuh,sha256=hkOQh_VmHYP8Ir9HM5Q7oenfTLrTFygGFkGcnmcP06E,78 +torch/include/advisor-annotate.h,sha256=VT5eIn6pi7-ih-3ha4eNRKxUCZMEp3LC3q8TAcKAw1g,23676 +torch/include/asmjit/a64.h,sha256=wY7RMkdbgBc_sLpAro0MyjHwc5244NeliTfHgOj_xYQ,2156 +torch/include/asmjit/arm.h,sha256=WAeo_gQYlPrTLBKLMfEGZxHIIP3vwTVeSZPAQ5Agw_Y,3775 +torch/include/asmjit/arm/a64assembler.h,sha256=Va5Gcw1ChZ6nbbacMEfuSMkyWbNp38rq8wNsAEONk0E,1294 +torch/include/asmjit/arm/a64builder.h,sha256=IkbZbWCPiQW2B2dkqC6PPmXYRmYx1iAtHweADP0gy5Y,1163 +torch/include/asmjit/arm/a64compiler.h,sha256=cjf7gE82a_fmf73VLsTS1Sqn14Ia2l-vh5ZZzAmzx7w,10421 +torch/include/asmjit/arm/a64emitter.h,sha256=2tpUI0W6gr_w1ASXbXxEscdMfETk_2SYJgDJREo8fNA,47135 +torch/include/asmjit/arm/a64globals.h,sha256=I0HoVbV-c3JNaTwFEBKwWvaWe9oSdWfMHF07khHoJ5U,128032 +torch/include/asmjit/arm/a64instdb.h,sha256=5rhzryVjeOR3DMOeUfki7bsuNFHA8dEFSF6kaY0SkDY,1932 +torch/include/asmjit/arm/a64operand.h,sha256=Divy5tdXyPQufg-8SC20jBTjJkw84WUoj_HF--uYT6c,28660 +torch/include/asmjit/arm/armglobals.h,sha256=Mv7AYVx2yA-HXgD_lnUwiWYmNizonDNsjsTnR9jGWW8,458 +torch/include/asmjit/arm/armoperand.h,sha256=M0MyxqrwskhDvp748Jdk_io44IVxKAZL-75LUz2EPBk,20986 +torch/include/asmjit/arm/armutils.h,sha256=hmaZU4f2Ot2-yVfPk_MTY8G5lKOTJj9FV7d8DHi7fWg,8586 +torch/include/asmjit/asmjit-scope-begin.h,sha256=-_44_xtq49EPCwKBWOckzSTTXw8XAUnP6pYVFZwOGFo,318 +torch/include/asmjit/asmjit-scope-end.h,sha256=G98jYY-t_oNg8dcV7e6Cg28riKCJz3ytNMKL_ZzZ_Tw,240 +torch/include/asmjit/asmjit.h,sha256=udi12G6OvMp6iySOl0GCjPOvTh4xIWxhi3MtNbO2mqY,1248 +torch/include/asmjit/core.h,sha256=VyjnFO9xA3XnPl1NKmws6rxH_r1iXwACu7xUP3pYy6U,99333 +torch/include/asmjit/core/api-config.h,sha256=k-kWfOcrYessQXBxsomsGg6XgrXAi5sSe2W3oEkQDxE,23692 +torch/include/asmjit/core/archcommons.h,sha256=wJ06NUCgwE5gkoGDBKexmwoWTgrNtEdnX1EbrWkBHU0,9610 +torch/include/asmjit/core/archtraits.h,sha256=TaBLO9K1Mt8XUGzr9BCXWdddevcrhNw2m01GyV4miCI,10824 +torch/include/asmjit/core/assembler.h,sha256=625yucyTiW8cTf8_MkqWrlcHEB9jxAjJCoFVzAq2MIM,4285 +torch/include/asmjit/core/builder.h,sha256=IkC5rD9XdOTYvyu_gyhqjqfEhWd5A0MfdGdhPY6hT8U,52481 +torch/include/asmjit/core/codebuffer.h,sha256=y8eFh_6toyJLaOsOsADRIjWCzHdFOxD8p4IGIs5syRw,3328 +torch/include/asmjit/core/codeholder.h,sha256=zO_Wv6lo_GBMf09Lj7m8GcMi5ovFe1gVV6FIxBfvpLs,44736 +torch/include/asmjit/core/compiler.h,sha256=cO3VdLZGLmwd1AEGyUCS4O4XGOFEsrve6R82HOak9pE,27594 +torch/include/asmjit/core/compilerdefs.h,sha256=VsUg3fGjiiBEHVjzqnWUKqJ-9PGvF_kSAAOSm-ImqPU,7098 +torch/include/asmjit/core/constpool.h,sha256=i2sqc-0idyQ2apxn-N0NTe2I2br8ODD7V9tFQHUUzyc,7060 +torch/include/asmjit/core/cpuinfo.h,sha256=vuleCQIhvV_VdcALULagovZHzWoQt64-WfnJqnwJ79s,63386 +torch/include/asmjit/core/emitter.h,sha256=-t8JHxv08D6XYP7C5NVX9lut6Bg4SVuR7K2nK8nAWO4,37151 +torch/include/asmjit/core/environment.h,sha256=QhqFAs-PTgLaUtJz7p465MZG8KCnjJ9WfkOxcdEZ1GY,18794 +torch/include/asmjit/core/errorhandler.h,sha256=tIXaoI8wK-A_e0KkJ1efyeoO1kwdwB4gxAJ6Y2uOl2Y,7850 +torch/include/asmjit/core/formatter.h,sha256=Jen3WWpT2npPDivxuAobSmkt0AYao0Km9xsLA_QadAA,8199 +torch/include/asmjit/core/func.h,sha256=Z44Ot-BAY1Le_m5NCrKN2tJ9_l5RaFGW-vVNgZ9G2Rk,70421 +torch/include/asmjit/core/globals.h,sha256=EQ6zuayPejayKibmMGDXQzTOtvrDa01dWZBbmKhnzYw,14922 +torch/include/asmjit/core/inst.h,sha256=KtJPpof8qzw0SkamNUl9ZGwfuUIQMMWuhCMkTJCOohc,32010 +torch/include/asmjit/core/jitallocator.h,sha256=ulqOxR3MiGSFEEc9pjmXW180PQbsFYTIU_R1Wnf9364,23583 +torch/include/asmjit/core/jitruntime.h,sha256=Ap4CaNISy0InAJmDOGWCFEmU2T0h7B3VRuCQJ8dTelY,3142 +torch/include/asmjit/core/logger.h,sha256=E83F3KSAKa-6V6SewAgBTYys7JtsIu7hhdjVlx32kd0,7242 +torch/include/asmjit/core/operand.h,sha256=MDyq2CA-5Zs28W9eN85TTMuypUcKAghvtr-MgJlB9C8,85805 +torch/include/asmjit/core/osutils.h,sha256=Iv0qUR7mZcI_sTejY3oPfVSd2zACwhEdYg01ctUFgeY,1198 +torch/include/asmjit/core/string.h,sha256=1Az3odvwLH4OfokB_bOYJ81Tr151fOnqeViasuhSGUk,13096 +torch/include/asmjit/core/support.h,sha256=nXkRY-3Yu7eBLHKBMMsd5EhHaB9_297ydl-ruYbdy0E,75030 +torch/include/asmjit/core/target.h,sha256=FruNERZhlwAHiPfHoaVEGWvEee1RuT6bM7M2T5eqFTM,1523 +torch/include/asmjit/core/type.h,sha256=CX6PsfnOxtzFMAGcZU_uswG493Mn_HKUwXSwi-yKVFg,17728 +torch/include/asmjit/core/virtmem.h,sha256=P3aiTNUegGrbg3Db_YEGN0IlM7aYriFN5eyGnGA8Q-4,13275 +torch/include/asmjit/core/zone.h,sha256=5cFo4ekzTL3VMWhqrZMcDUnx_xb2_onP2Rxai0TxwQo,20679 +torch/include/asmjit/core/zonehash.h,sha256=OcKp04R3Dkx8GR54UhII8q_9yoTmMjP_teFf--VwJeQ,5258 +torch/include/asmjit/core/zonelist.h,sha256=ci8Re7e86Qq-o2YytkRJny96XgwHnArgl7Fo3J1TRxw,4794 +torch/include/asmjit/core/zonestack.h,sha256=MQMdkU06KqdyJy_YOZlfK2yld52lcfoZ1BgIG0cLZE4,6164 +torch/include/asmjit/core/zonestring.h,sha256=R_nhtoo7GDzF8nIrSPrzpkfVH2VPOYxloh0B3Q5LHyo,3228 +torch/include/asmjit/core/zonetree.h,sha256=X5Ws3vsrxY2lsBE8qQp7uP7gKK2p0ZU3bjCfbc-Bgc8,11347 +torch/include/asmjit/core/zonevector.h,sha256=YBcBlnCSB-6G1VtRbzkwiN8252k-XzEkkJTs9BRSEj0,22541 +torch/include/asmjit/x86.h,sha256=mt3J2_0VaoGGorsDrHD-QAWktYsXpSJxXJe6xhJCcOE,3892 +torch/include/asmjit/x86/x86assembler.h,sha256=c_S3e02Sq1ZjPgTXtaZFbhwiq3619ADaTNAGTpFyU_Q,29411 +torch/include/asmjit/x86/x86builder.h,sha256=3thZcx1HaZAAWt9DSxdUOAMDHK8NrhqUsluM2ZktiQc,14013 +torch/include/asmjit/x86/x86compiler.h,sha256=P9iTqN9OyGZnr5ZYhxcM_YsYA5yMCxMG1jkaAamA4MQ,30508 +torch/include/asmjit/x86/x86emitter.h,sha256=syNjH6sNE4586m75IbESTVoGOgLFiG4vZFnFERmtywc,309381 +torch/include/asmjit/x86/x86globals.h,sha256=XJ9OrZ_B0q0p0rx8njqJtp1bAehYAwNkBIt7TX7Be-g,160772 +torch/include/asmjit/x86/x86instdb.h,sha256=R8fB84pzkQxUFL8QBNMi-bimzAOS0maJt_Y7_cEbEHk,29353 +torch/include/asmjit/x86/x86operand.h,sha256=K6LfRCG62DgTe8PpdC402r5AwsATbSFg5sKMtAPDJQ0,59019 +torch/include/c10/core/Allocator.h,sha256=Qq_oQQuyKgLwjIcvAdM6fxhjB9UIKoy89mkgduYxfak,13750 +torch/include/c10/core/AutogradState.h,sha256=mhl7qjZXy5QgbSBjv3EmhlJkTeX2zQLA67kJPtb3sMM,1654 +torch/include/c10/core/Backend.h,sha256=yjeYmDD1JsgDglx6KnFQCRujhEyrGBZid9U8snfQVp0,11296 +torch/include/c10/core/CPUAllocator.h,sha256=cUwX4Tp5hC1S62FypLhVGkisfbjQ-Wm27OjzNd2j2cc,1688 +torch/include/c10/core/CachingDeviceAllocator.h,sha256=uTiQdxONLTRSrxCWL2TpuMphOFqfgYGo3k3gtQY51aY,2003 +torch/include/c10/core/CompileTimeFunctionPointer.h,sha256=LE2HvZBcsZ_tRb-u43n7b5hnB9a4MUboRv5pJKCKmp8,1700 +torch/include/c10/core/ConstantSymNodeImpl.h,sha256=wFvqvVBLtfuyLQ2_unMj5tIChG4jVz6ZBBMCbp52Zzs,2990 +torch/include/c10/core/Contiguity.h,sha256=1tbfMtRZRVkyYbTJhcx3t6icXk7ZqaGJ3ioR_CqU8qw,4142 +torch/include/c10/core/CopyBytes.h,sha256=7W421Idnt23V7RkFT3AhQBS_F2JW2L9zQoERVg1IdCg,1343 +torch/include/c10/core/DefaultDtype.h,sha256=jA8dnRLdn4aC4enYeb1YiOXuQCzgQtQHFfHegFsmWtM,394 +torch/include/c10/core/DefaultTensorOptions.h,sha256=0u2cbxT6TMZBHA5_LOt6TJS1vEutjAFn8axX_MxzJfE,1064 +torch/include/c10/core/Device.h,sha256=jU_dtrNhrhMIcFLdLXQdQ-NDbMIVVfAVCbk5hZHcgFs,6895 +torch/include/c10/core/DeviceArray.h,sha256=K44Onr3IdISiXRSjiQMYKgp7Xy4rju7FSDagRklYeIc,683 +torch/include/c10/core/DeviceGuard.h,sha256=TTgAECP7NthZjjNnhywFp-Coz9vmLW1I5toIIOCEJDI,7810 +torch/include/c10/core/DeviceType.h,sha256=gvyZ3F38yAUtcBx5cZcLO_h5iEuEVm6x2E1Q5Cgi2HU,4476 +torch/include/c10/core/DispatchKey.h,sha256=xtxBTDHMCnPs5f0HaHuXQk4w-paFPD-6LjQkaXV0l00,32374 +torch/include/c10/core/DispatchKeySet.h,sha256=7IcRsR1F3mdvwmQAKBIboEFxaud1x58XPG2AHADT-D8,41738 +torch/include/c10/core/DynamicCast.h,sha256=P6wetNebedxi0d99udXrB-Cc2l8drZocIqfzVoj4lbU,4444 +torch/include/c10/core/Event.h,sha256=OuaYD8wklfsRGQi9nZG6EvEHGsinpCXAYq1mvBztNdg,4463 +torch/include/c10/core/GeneratorImpl.h,sha256=qpLHJNx7xOM2IYXrK1iHkl4gTTNjzibT-AV1Ph45j5Q,3937 +torch/include/c10/core/GradMode.h,sha256=8bLoWLLWdUsOSIqdNZ6DFMsLp6NcXhcqCWj005LrM-I,1661 +torch/include/c10/core/InferenceMode.h,sha256=w-6wZ-94SK8Iv81guTTnBkerJu6lex-t0DG7kROG5lE,3763 +torch/include/c10/core/Layout.h,sha256=ie5VbhR1SRHHegWXT-MxF4NGPwUeR7DZzunITlzLHfA,1943 +torch/include/c10/core/MemoryFormat.h,sha256=wsPYbHemnh_26P1-Yb-F_S4yIYT6kZL5q4mU06ocdJQ,9401 +torch/include/c10/core/OptionalRef.h,sha256=hWjNEDGGMt_kVVqsGhriRs1930q0J1b1fxbM3A8QK1g,521 +torch/include/c10/core/PyHandleCache.h,sha256=7Gb6mh98hla7kZlRVEE8jeVu_IRQsdfxx53cDzri64I,3101 +torch/include/c10/core/QEngine.h,sha256=-UoIngTGcXUXv2oe3LE5M0_n6WvrMj2dyeV1L0Okx6Y,1010 +torch/include/c10/core/QScheme.h,sha256=vcqI4auVfkZh-LwdTjdJmjUz_3T3jmdsiL2qyp_T68I,1566 +torch/include/c10/core/RefcountedDeleter.h,sha256=RWfSPD4bmuc53iH3N9oM43m3CH8OeQiEdBBqs_Oa5Nc,2251 +torch/include/c10/core/SafePyObject.h,sha256=gQXB96fbdw4Sr6TUDPGjrDILbiRkjy7Qk71RltnGPYc,3741 +torch/include/c10/core/Scalar.h,sha256=MEj9gmuU7vfkb9tMVHVHVsdU3ff59M6FYuaGz1q45D4,13998 +torch/include/c10/core/ScalarType.h,sha256=H19ltrdnP1m75qs4GV3vdcf-s7r_ZuD7josbnro7nkw,24742 +torch/include/c10/core/ScalarTypeToTypeMeta.h,sha256=8yuBriniMRc2o5TcQrP2xZDE273uJXcf8vkhU7T6OsY,1357 +torch/include/c10/core/Storage.h,sha256=g9w0BHtZDCNkE_97nDUy3nK50EXlnJhV2yoecqqvE8s,7059 +torch/include/c10/core/StorageImpl.h,sha256=g0Xj1b5t8PpUBQsIRz7pYO650yebYoZyRm2htAUEdNE,11381 +torch/include/c10/core/Stream.h,sha256=ZsQOPW7woZ7o0Ea03DNnsjdID5-gTxaTOd6JAaDmSMY,6365 +torch/include/c10/core/StreamGuard.h,sha256=mn0n47JfbsIb-y9rsQcjYJ3UaH2hGVB9QXqEy8d2eJ0,6590 +torch/include/c10/core/SymBool.h,sha256=jb5fKTiIVt-zwH8VFyLCPSOfctUs_bpOVTzloX0TYGk,4462 +torch/include/c10/core/SymFloat.h,sha256=kzavp9ahuQ5D19-bMLikPMgH9xPOUt_dkbIant502_s,3557 +torch/include/c10/core/SymInt.h,sha256=WvKaxoFTp0Bxx8qJpttt2QQLyRIr2PQFQPwWpzvnTRM,13906 +torch/include/c10/core/SymIntArrayRef.h,sha256=JU1wj8ZzsZRvR-jqiZm958FGtKnWBYChu4_TN_oo6MU,2714 +torch/include/c10/core/SymNodeImpl.h,sha256=V70ElZXEXOGS8mY1lPfA3a1MRuvlNq_4yIv_tttH6lg,7566 +torch/include/c10/core/SymbolicShapeMeta.h,sha256=oJZodX4mqwcauMnQmP2dTOY0pC8T4D4K1z9lHI_aW_Q,7347 +torch/include/c10/core/TensorImpl.h,sha256=g4PsjFWpHpd8rzj0dFbllasjxzfyP-rQBzPGoe-quh0,114529 +torch/include/c10/core/TensorOptions.h,sha256=besk1NUY766mz9JzjNlAnlIYU5B1Wu8trxT_KPYBR5E,26951 +torch/include/c10/core/UndefinedTensorImpl.h,sha256=CDmD4-Pb7c2M6d8yehcBuN06oOPS7C4p8x8c_JKWcrE,1221 +torch/include/c10/core/WrapDimMinimal.h,sha256=BZRtJfULHp6Dw2HlblnxQQ0xVfdwwxzaMKWqtbE7GnU,1360 +torch/include/c10/core/alignment.h,sha256=388Kxrz59CbzQ8Noj8oum3glnTyrbsyEvG8JGuXeDPU,564 +torch/include/c10/core/impl/COW.h,sha256=w4FGyQW53kizyK2uX-9CyytzbFawPtrU8CDjJZejh8Y,1056 +torch/include/c10/core/impl/COWDeleter.h,sha256=xZ1odJVNmSNpMoPs5yAFPus1ajeZ1MBcvITs6Aw9w-0,2098 +torch/include/c10/core/impl/DeviceGuardImplInterface.h,sha256=eK-mjlTmxtGrDS8Hq704sMfyM2kCfmHKrnuz0ZzFz1o,13553 +torch/include/c10/core/impl/FakeGuardImpl.h,sha256=XJtALPkLxOYkKnkBbq0LaVVGXAnrkNEvKJx3loxSa9E,3171 +torch/include/c10/core/impl/GPUTrace.h,sha256=NaGndfpzJjUYMSUkP30h08bUuRZioQBuymQL3SjpZjk,864 +torch/include/c10/core/impl/HermeticPyObjectTLS.h,sha256=DIfhMEE1JZvuPpb72B4ArALI5oKvGi4fZh06PrcmPKc,2546 +torch/include/c10/core/impl/InlineDeviceGuard.h,sha256=_yC4vrXC_9Y_13YTu755HmmJYOruinSM2muph5o8gWU,15911 +torch/include/c10/core/impl/InlineEvent.h,sha256=V8h_PK1x7dHmX2iuvId0jKqJ1Bk2SDAvIptAAm6-cRU,4308 +torch/include/c10/core/impl/InlineStreamGuard.h,sha256=qhh3hKvk_FQVaJzlJybawGQBY7Hx_RgULl0LizlbGLA,9914 +torch/include/c10/core/impl/LocalDispatchKeySet.h,sha256=y2hFSaKAiZhWmGxlwvpXfMGWogQlxfFwCnWcs7cMRlg,6533 +torch/include/c10/core/impl/PyInterpreter.h,sha256=B9BntJlhNltK8T8MDu9Tgf3IYdaFJ32TPcOhURpw1_Q,11328 +torch/include/c10/core/impl/PyObjectSlot.h,sha256=a1vPa1NEGDiP4fVRFeuwxpi4K0WrnfoZjokeKKbsggk,8167 +torch/include/c10/core/impl/PythonDispatcherTLS.h,sha256=ysp9mdMq7mgz7I1jb3CxQo-UQelzRWaAsc6vq8sCn3s,840 +torch/include/c10/core/impl/SizesAndStrides.h,sha256=-Sy8Kejci48Fc14GFncOvc8hPGaJupXy8FGTrBWDvHo,8665 +torch/include/c10/core/impl/TorchDispatchModeTLS.h,sha256=rzBcGog9-7cRtTtq-KhqyAHlHsWC_dTsaqq878IoU70,2281 +torch/include/c10/core/impl/VirtualGuardImpl.h,sha256=FSwRwxNlDlNlFOsFk2aEbBSZ2dvvBKFaoqc6Dpr5vUU,3302 +torch/include/c10/core/impl/alloc_cpu.h,sha256=3-UdGi_ohmU44JzoNs9_DzdiRAFHz4NXAp0A7De8HPk,672 +torch/include/c10/core/thread_pool.h,sha256=HU9cBi8jQVypSFnbrFaT4fhGeuCx-rE57mqsxDjjDao,2997 +torch/include/c10/cuda/CUDAAlgorithm.h,sha256=_osQlsXB3DJO07B4TJLXnMyjkHip-DJNSxvZCmF8EjE,1041 +torch/include/c10/cuda/CUDAAllocatorConfig.h,sha256=QOdhGN7m_s7kZ0OypE5dXSIF0Pqs01jdniK8JJ5HBGs,5106 +torch/include/c10/cuda/CUDACachingAllocator.h,sha256=xktkJ6iyXhXmAibgzjFX-_ZSq_dnoP2OA9IS8F0i4pU,18077 +torch/include/c10/cuda/CUDADeviceAssertion.h,sha256=mJqI9IjWewhzOUwUuso9YWFw7wWVxDktJehJ8pPm4M4,4162 +torch/include/c10/cuda/CUDADeviceAssertionHost.h,sha256=_QbWZmFSf7mWiJ42xz1xNUDbxLpafLx9ElQpIRMrRP8,6631 +torch/include/c10/cuda/CUDAException.h,sha256=EJl1TYsrGxNRQPOWbDfH4xlmvwsYMFdUvmfcfVAqJ-U,4411 +torch/include/c10/cuda/CUDAFunctions.h,sha256=J_KXGjfZA6KP-XS5mB-ApJgJhd1bY96lYeHFlc4qyQ4,3963 +torch/include/c10/cuda/CUDAGraphsC10Utils.h,sha256=E5vnvylyS_ZT5_te8pHxEDtin99jSE7I-LdcSJw_o0o,2991 +torch/include/c10/cuda/CUDAGuard.h,sha256=qXYIKGuMdJ5hQQZgu6olBhIboSJBZ-g_ALiFwp-v4x8,11345 +torch/include/c10/cuda/CUDAMacros.h,sha256=XGYaw_5z8tkveMtcSCXBKNMEtJ34j16LKiMVDv9Qr0k,1479 +torch/include/c10/cuda/CUDAMathCompat.h,sha256=zXP8jldgwXmP3I7hnp5lrEEZGYa7troS74crEwun0t8,3546 +torch/include/c10/cuda/CUDAMiscFunctions.h,sha256=VYjGElQ6pE-FHwGjhLaf0KOQKpapciFPXmW5mt_xTWo,306 +torch/include/c10/cuda/CUDAStream.h,sha256=LJYQ0hpvjJurYtl-9lyQH110cQFhTbdEzqKUiJCRhME,9628 +torch/include/c10/cuda/driver_api.h,sha256=K8Pbol8FC0RgW5Y1L2qbjv_CJX9BbXdHS0u0Zx0S8BI,3449 +torch/include/c10/cuda/impl/CUDAGuardImpl.h,sha256=t-xD8ltqfQ1Wd1XR4nrQzodGFOTYleeb5aMz2LhuAmI,9297 +torch/include/c10/cuda/impl/CUDATest.h,sha256=pmj2IDoGqxUBvCohXPrEkoKZn4ZEwgUDXAcN27a5bNg,114 +torch/include/c10/cuda/impl/cuda_cmake_macros.h,sha256=5SZTOQCUZPH9z5lM3IUfEXcEt6BdeTLUt-TeEEX0PPU,194 +torch/include/c10/macros/Export.h,sha256=HzXibtCozc0YB4d0qaDGA8kiAnbsIvnnkyJvbZPtDCk,2527 +torch/include/c10/macros/Macros.h,sha256=UUI0Ih_-01RPKdxQ6JfZSscaOVz5goKK4dHAYEUlQ74,18668 +torch/include/c10/macros/cmake_macros.h,sha256=VHySeoBhgBgNh01VyiCoWmoW8nTLITV7m5G7MyYW4dU,437 +torch/include/c10/metal/atomic.h,sha256=MP5LoTgZLqUgsiD3qgAXFyMMlGrVR-oq2jtHXj7yNRY,2864 +torch/include/c10/metal/common.h,sha256=9ZwK5HknwF4QJzQ5gVmyuO3F5hmu0jjZPK0G0nF0AQM,1431 +torch/include/c10/metal/expm1f.h,sha256=5MRohuhEYxDDAqOv2yWTAQ-_fY-U9voRXV9BQjnYYeI,3596 +torch/include/c10/metal/indexing.h,sha256=5xXK1PA_cIuYwtfwLEdHqGuv2KHofXmLZBTSS8nFfsc,24112 +torch/include/c10/metal/random.h,sha256=FBmL-axoGcwSjSqQCHD39oLbvz7C8EaQqrOkx-c-9d0,2217 +torch/include/c10/metal/reduction_utils.h,sha256=J32BAtVjFW-tkpDzZxPnmPUuC-u02pkYZ4Sb3C1UwO0,5367 +torch/include/c10/metal/special_math.h,sha256=L_BGMaJISgPjCPZP2yQ0auUAIJVSJ4GX5xvP09Rsjk8,50353 +torch/include/c10/metal/utils.h,sha256=91R3w19OH-uAlS6kXZ3P3ewdwD_xFr_7R15tgsv9HjY,7866 +torch/include/c10/mobile/CPUCachingAllocator.h,sha256=g96c3oRIZKgHl8HQbOcKotEm9IvNDFN9h8AXkm2iJqQ,4165 +torch/include/c10/mobile/CPUProfilingAllocator.h,sha256=SVCa0IId-pBtm4xI9w8Ag-i9iOu5uD8hNo2UBefg4mk,4736 +torch/include/c10/test/util/Macros.h,sha256=LT6zKMFCh5RXQUpgljqwhshyrNbNwaGUBiELVpxWbg0,176 +torch/include/c10/test/util/complex_math_test_common.h,sha256=e6EMxnozu0ittTqS0nG5uVijoR8HoHH8uAO5eaAU6Ws,21964 +torch/include/c10/test/util/complex_test_common.h,sha256=4Lse9uVGHBPKYrXtvsF9zhzsTy641as23vdMtVJOlF0,20546 +torch/include/c10/util/AbortHandler.h,sha256=Dc3itXodVVL6aohzf2sfZV8NVudkuq99W-h0RJ5CmDA,2192 +torch/include/c10/util/AlignOf.h,sha256=P8ZzdW8CGo583mnAw7NsgcnRAHREfBUEzVgzvofGB7Y,4906 +torch/include/c10/util/ApproximateClock.h,sha256=GNzNYYr_8JcPH3FAabJTSJuyG5mtcERGSpJYuW3YZMU,3482 +torch/include/c10/util/Array.h,sha256=PUhHWAOldn51e4l5qRuFTqZJxQLpxS56lesi6OSJw1E,450 +torch/include/c10/util/ArrayRef.h,sha256=HbjuDacONfOa02EDTu41igE_DXHgqdc4oNGlP3QMM08,10880 +torch/include/c10/util/BFloat16-inl.h,sha256=J7wfSI7TTEsO3UV6yl1sG1IF8qiV0-m1ZtvpWx53P34,10265 +torch/include/c10/util/BFloat16-math.h,sha256=pXGIN0ZzO8ziEwSZDivoipKShH2YBOBeO4SRZp95Mc8,8517 +torch/include/c10/util/BFloat16.h,sha256=RLgBwFzImOxX1yJofBuVwCS6t4O_k_EZC0EUYCiFAOA,3167 +torch/include/c10/util/Backtrace.h,sha256=lZXha4pz5h8PHcz1EA6BBKOGwV5OGhPQmQgF7sPbJg8,797 +torch/include/c10/util/Bitset.h,sha256=SnsU3_BWqDdnYGldyL9Ap4DTc0SWTUj6C36tj39u2DE,3421 +torch/include/c10/util/C++17.h,sha256=BVAEAXQkYPvwgykHjRRv4-fFI0AlP6PN4H8uRw8DK3Y,2403 +torch/include/c10/util/CallOnce.h,sha256=_aoEcmKjpfq66ZUoveTgMY0xWMP9QAGtpG1U2rrfhOI,2063 +torch/include/c10/util/ConstexprCrc.h,sha256=Xx9SPCbwwB9BtaNx2Z3o3QpDEvt6EpJjDelCdF5pXdc,6595 +torch/include/c10/util/DeadlockDetection.h,sha256=qH9HUXZENuZcQvI60sybaktwZ6UVbLUWMUWP6XEymIs,2217 +torch/include/c10/util/Deprecated.h,sha256=tyNcwEnjhdHs3LbgKssdAQqInoDJXNhgcSg1z2sj3nc,3579 +torch/include/c10/util/DimVector.h,sha256=kQxjIMzErk6BLDVVpLVCrzimZaZr-WG8pBjDePV6Njs,444 +torch/include/c10/util/DynamicCounter.h,sha256=oawmDGGf_eC7GwTPrEUEMMr8HVmB2QOSY1Uz9EY2XL0,1304 +torch/include/c10/util/Enumerate.h,sha256=M3PDAauajOBtCSsGmrRai9p1OAZ5auWCxL7XJ4Qlu9Y,4001 +torch/include/c10/util/Exception.h,sha256=m64PFCkjIGqcksnrUXHbaUiyq1-sXeY1OAFsdflaXao,29725 +torch/include/c10/util/ExclusivelyOwned.h,sha256=qwRVoJSLwNgKSoGTTRUfaF7PA7l8TGwJSCM9qg3I2a0,4453 +torch/include/c10/util/ExclusivelyOwnedTensorTraits.h,sha256=ik-PJqouBR29T6XDuf0QGhthRXQ69xpg81fuU3-8h4Y,2194 +torch/include/c10/util/FbcodeMaps.h,sha256=8vAkfHp4na6an2L9cXNB45k1POA9sLEkv5QZDcC3eHY,728 +torch/include/c10/util/Flags.h,sha256=tuiKmjA2Yl-8LbV9Z9Y1hPSBGdcEE-o37RhGNQy-7J4,10900 +torch/include/c10/util/Float4_e2m1fn_x2.h,sha256=vfZaaNkdL85l1DzdGf8Be05ScxMOkYhhVD4jZ8bT3mI,862 +torch/include/c10/util/Float8_e4m3fn-inl.h,sha256=qNQjkWi7W7hTQtZ1RdGrccNW8hX3rWe0ymI2FD6Q1G4,8566 +torch/include/c10/util/Float8_e4m3fn.h,sha256=naGvxRbIHjw4guQw4zaPjolIFLvGI0GombclWjjZEAs,8146 +torch/include/c10/util/Float8_e4m3fnuz-inl.h,sha256=cttH-JCo5d0lwcZw36F2JORYNf1mjnKUzma5t1Vgp84,8996 +torch/include/c10/util/Float8_e4m3fnuz.h,sha256=vbsDet8MfGF02FctTOv_EQmbhOG4uYQr-XFSAkmex1s,3814 +torch/include/c10/util/Float8_e5m2-inl.h,sha256=aCXYsCL6rLJy4qFlz_1kcmbKbBsNJMCL-lQmomOh7sY,8652 +torch/include/c10/util/Float8_e5m2.h,sha256=jzObT3uozffiPe0dJQHWjn1TT5FcHvZX_kusGczjUCI,4324 +torch/include/c10/util/Float8_e5m2fnuz-inl.h,sha256=lCpwXEIQ_-_yQHCOfu0yBGu_T4w-GHP5Dg61Qso1bfs,9225 +torch/include/c10/util/Float8_e5m2fnuz.h,sha256=P_yKQ4nOnWvr0c9mQqL3D36sDVaJrBsdd5FkMi7onPI,3843 +torch/include/c10/util/Float8_e8m0fnu-inl.h,sha256=S21LFiYV7RTYRkbNKgiagLTAYZzGtWIfsCxoKrPjcWI,3785 +torch/include/c10/util/Float8_e8m0fnu.h,sha256=RFc8Le6PbZ8TmK5R5-AKYwE7qFmXU-W_l4zpHCeDDcI,3098 +torch/include/c10/util/Float8_fnuz_cvt.h,sha256=9mj750VZ1H3zKWp1-5Y6ZkiRv5gI2YdzmVMsMwn7l74,1732 +torch/include/c10/util/FunctionRef.h,sha256=L2sIQ56zatHFPgwVDlVM0cA-KzBI4M6-Tv3M8G5WHSM,2296 +torch/include/c10/util/Gauge.h,sha256=HOwKw1NSJqSGLPMXGjeihIkqNp5KKl_1U7BMIAvT9Ms,1176 +torch/include/c10/util/Half-inl.h,sha256=2J-jM1hlLZ-Ixq6XrjiCbsSasVZGjrt5g7EmDs-6Ey8,10152 +torch/include/c10/util/Half.h,sha256=3o8_T9geBMhYkQUrPfWCIjwH8q0rbkxypj1jX_Lae0E,16340 +torch/include/c10/util/IdWrapper.h,sha256=uYBOWQaBbtb9DDcerY46K4BfnM1bT3LLnIM6rae4iI8,2336 +torch/include/c10/util/IntrusiveList.h,sha256=tjwYsnviuhQ8jCbbRAhyNbSNNWsS1lP5439mqa3cQ2Q,4495 +torch/include/c10/util/Lazy.h,sha256=29zqiEnjpyPvkuTCtqrQ6dSOnvXTk8v7XXoi7RC3aiI,2823 +torch/include/c10/util/LeftRight.h,sha256=mNdFyU0-nW32IE8bID3WqP_7qRajMyMgenxUEoGVqmo,7209 +torch/include/c10/util/Load.h,sha256=_DiWV1BF_M-sPlrZSsuB3YBy-X2lTrE7lfBZSjZB87M,924 +torch/include/c10/util/Logging.h,sha256=aMWO10G9S27xO2XbZcoWJk7_szsNauMSmwwLXZ45iE0,14085 +torch/include/c10/util/MathConstants.h,sha256=gyZaVPY6yUN2V2c1pdAkjtXy6UpD51eDmDixsacR_uU,3690 +torch/include/c10/util/MaybeOwned.h,sha256=JG-HdRwRjypc-liDVqV3r0FHmCq11gy9pKx2droIzYM,7155 +torch/include/c10/util/Metaprogramming.h,sha256=RWrjybE-bjBO5gUsAUDbR1vGHYco9r8Sf2i-TG7BtUk,7031 +torch/include/c10/util/NetworkFlow.h,sha256=5j7cCqBWNfsZahNdyLHI6vlnO0H6eTWnVi7WkC7ebfs,1137 +torch/include/c10/util/Optional.h,sha256=KkNCveZ04KrbciY7YFuf0yc3_EErBxkLxIduKNsZMTQ,1995 +torch/include/c10/util/OptionalArrayRef.h,sha256=NT2o5HC-eM7BZF74shyvp-si5NBuIPXt7Gcdvkyi0DI,7162 +torch/include/c10/util/ParallelGuard.h,sha256=AClhBs6-_lFnvdUTtuDKKmNp1Rdf_CbLOo-BZCPJoUI,373 +torch/include/c10/util/Registry.h,sha256=o0wziabCfgru-bO1F1x4DwqT2LkhI-uDyNySObe_4Dw,13260 +torch/include/c10/util/ScopeExit.h,sha256=7IV_xwvAerKLsjzHhU_TAFsjVlknfN22tOarz1mFjLc,1259 +torch/include/c10/util/Semaphore.h,sha256=FIHLtrceMv-i1xTdvI3ulZlfig1qQWZuYbtR_KPZElE,1418 +torch/include/c10/util/SmallBuffer.h,sha256=8FhfpVXad5SXYTyeIvLrQMyf7xwkBK5lsOammWGbHRQ,1762 +torch/include/c10/util/SmallVector.h,sha256=zowrjVUGkektT5x5j3O2Ce-ZHOBTwMI-OXeje55CpCU,49005 +torch/include/c10/util/StringUtil.h,sha256=HJ-baIyGohokZ4zfQm5RtKGTYdy8oqFshWJresyyCzc,6802 +torch/include/c10/util/Synchronized.h,sha256=-NRUh4zYhclBY8-9PNtlsHJTktOsEph1Uv9FQ-yBZZg,1925 +torch/include/c10/util/ThreadLocal.h,sha256=51x8UVlYgC-2ITRWWojAEvT1SIXQ8PN2vE_hNfJU99w,4020 +torch/include/c10/util/ThreadLocalDebugInfo.h,sha256=L3BuSCCdRyZ6FIBUOpyrKYp-5cnbUIdfcJMtAbGJwdY,2665 +torch/include/c10/util/Type.h,sha256=-GVwlf-O6CsPhLC_t9_t84apjpiCvwY7LlXpfp51OkY,646 +torch/include/c10/util/TypeCast.h,sha256=qUp_8H2sBXnPzrBDpb9FYWTjf1Z69ahTfiWe0DxNxLA,6665 +torch/include/c10/util/TypeIndex.h,sha256=Eaipx1TybtncmJZfr3sW-0FXuuscCB7L2HEVKX57zNI,4693 +torch/include/c10/util/TypeList.h,sha256=YA3psuJdiyHYbjPAsGYrJVRqVvtWMH7kgC25_jqufu8,16829 +torch/include/c10/util/TypeSafeSignMath.h,sha256=ItboL27IimgDESSU-BFR907wTGbgkO5UdOENC6jh94M,4363 +torch/include/c10/util/TypeTraits.h,sha256=-3g4qpe76E0OKxVK731TUYKxElzCCj99SoAMU-UebHg,5338 +torch/include/c10/util/Unicode.h,sha256=hCDwsMEcAI5h6nD8F3ZxJbO4M4lq1pCRpwj6h3H0DvE,295 +torch/include/c10/util/UniqueVoidPtr.h,sha256=Tldd2gQDNu77ZOxC0Cx6F3kxZ3RlnMMevX21osBXThI,4570 +torch/include/c10/util/Unroll.h,sha256=1R1zTrF4Blvqu0FRtpofBLroPSSVujaMdh0E8GIBDbw,843 +torch/include/c10/util/WaitCounter.h,sha256=rbJU761g4GXI_HpwJIazRhB2Qp7IU-846p_xgeM6fwg,2590 +torch/include/c10/util/WaitCounterDynamicBackend.h,sha256=VeDiJC2FNUXrbUbex94egiNUSfCGZZcUh3yrpu5fOaQ,662 +torch/include/c10/util/accumulate.h,sha256=hobsJjXaKqmwAZOCXogE7eZ-NFiScJS52hvnVjwElFs,4032 +torch/include/c10/util/bit_cast.h,sha256=yf7W0L7cefPO4yMaEaLR1tt4bsuQ6P7jJYaxm3yEueY,1247 +torch/include/c10/util/bits.h,sha256=5vkKgffFbfdboQtD9E4fDsMkxeGlMymsJuRwS_ChH-k,1449 +torch/include/c10/util/complex.h,sha256=NZTpzrgE8RT0AZR8_s9Qg0GL60S4V30T6jG55UKyKyo,19528 +torch/include/c10/util/complex_math.h,sha256=K-lYAOgfqRUPZQQBExYkHuDpQtVplmaY85WlaO18wjk,12533 +torch/include/c10/util/complex_utils.h,sha256=647X9XNovef-gDB7_p-Q7TskQ-boGgW5MedmVJtp58s,1077 +torch/include/c10/util/copysign.h,sha256=EGcQC191aoUTlLXo8tKfAF9NFnMpQCE0hyWxw47FQZs,832 +torch/include/c10/util/env.h,sha256=rH15ODP7BKuDLIrSMEObHL3opdjJWsHZ7VOkOpCpLFg,894 +torch/include/c10/util/error.h,sha256=HGPmydeaHN6bl2TMwyA4kYRneSwPl4QirmnIO-ldDtU,205 +torch/include/c10/util/flat_hash_map.h,sha256=oSaLrGZejJg2sE4vOS73g9wxznCzBzaTEOeUKPH2ctA,61931 +torch/include/c10/util/floating_point_utils.h,sha256=_UbsRR2UptjDnWsNgJv9hKRZFakoljmjXK9dgTj_qh8,798 +torch/include/c10/util/generic_math.h,sha256=ox-TlzdcFFHRTZTK-jXfpTfTmNM5Eihm1rLvokvXtlY,2892 +torch/include/c10/util/hash.h,sha256=VOrOev48dWJIWwLgW8Vyx84h0c09pI-37FBwl5eUmzg,11106 +torch/include/c10/util/int128.h,sha256=6B-OapJ188dixe1U3B4PQPzxyR9iueKI1WrCau6Rjyw,12452 +torch/include/c10/util/intrusive_ptr.h,sha256=IOQzaT9_oXc-8wrp9BZbSuem0p7bzLYFHEH2MsZ-6yQ,38523 +torch/include/c10/util/irange.h,sha256=JWuxICFEyNayPzP0VbEdubxbT2lyfymC3qamq26Q2W4,3440 +torch/include/c10/util/llvmMathExtras.h,sha256=AwuX_nTJy9_XXnpZ5cnzhuZ4ykdvVJ4nHmuELSi4SoY,29443 +torch/include/c10/util/logging_is_google_glog.h,sha256=LU7_XPpQPV1CNYYOfKAnm9vRSTpUNFmRT81AqXJHtVQ,3794 +torch/include/c10/util/logging_is_not_google_glog.h,sha256=ES9qcimZf-7a5zq5P2jCJrmlWeZJyzZ7u34D5yMIawI,8681 +torch/include/c10/util/numa.h,sha256=Zv_RgPaUVPp3KN4FmvwaqIr-hZ_Es1bd_djbAIYGzsU,713 +torch/include/c10/util/order_preserving_flat_hash_map.h,sha256=7XL2-F274yFsqyucP2JocsfQA0qJSqfnXSuMpt-umyM,65452 +torch/include/c10/util/overflows.h,sha256=2Im9QMah8zBl-LgCh-aWVGownyTg42xZ_NWRNYwdlUs,3467 +torch/include/c10/util/overloaded.h,sha256=HroKnPbEpPcmOJ2ud_43qLAENSCdvN9q3F_2REQLhvM,727 +torch/include/c10/util/python_stub.h,sha256=Nigc7ZGrniF0qdLn-Ra4KIicNWTBrgARkJYpxs6Ssek,56 +torch/include/c10/util/qint32.h,sha256=iyBUhux_GEJ8J92GhOPnukFSHXEp_vCM_ImIZTR1_Gg,319 +torch/include/c10/util/qint8.h,sha256=bc5SknZDfO9_2Ez-QlgP6CVXkFhkFIc8pV6vqx4y4vo,472 +torch/include/c10/util/quint2x4.h,sha256=yGJ_ehKTUy8Iq54q6LGOQtBf2-Tdwm-6k9EzgLZnwr4,366 +torch/include/c10/util/quint4x2.h,sha256=XARtXHqCVQKflXpoKx4ybFS1E3Pdq9FafFAb9J8SUH4,366 +torch/include/c10/util/quint8.h,sha256=nEiBXDtefPX4IlYbmn_mxC7Ec9QaPZ-d0nIO1BbsGh0,320 +torch/include/c10/util/safe_numerics.h,sha256=5qZcPQgjU-GqDQfltDSbO9VR_Mgv7qklqN_6bc724yY,2260 +torch/include/c10/util/signal_handler.h,sha256=pVfyBdhzgv1sUm84l_7CvvjBbsHS5OPFGiEsUu2HI84,3767 +torch/include/c10/util/sparse_bitset.h,sha256=c91vrBqd16okvlKdR5qBdWuormq7HnFd-y7s9bD29UA,26677 +torch/include/c10/util/ssize.h,sha256=fRC0RaSU4YWJOWGpglcKzJbJoogdxtvQoSUKqW3Sc4I,1369 +torch/include/c10/util/static_tracepoint.h,sha256=u_jYYCJlvn-3eHJvxlGmnfwt99aG1mmVv4G65dS97bE,1076 +torch/include/c10/util/static_tracepoint_elfx86.h,sha256=xgrY1j46yegvSFgdmujbhnjaP20IDOg-p8oO-zJWvwo,7673 +torch/include/c10/util/strides.h,sha256=636o3IYdyG6Ms5qYbliIFWW2WrUEmSvdIYRHgWNrzzQ,630 +torch/include/c10/util/string_utils.h,sha256=D4gfy-yhHd8mhHoGszDgZFZqjMTOne9yBb7QYIxHf9o,446 +torch/include/c10/util/string_view.h,sha256=_uAaZfjRWENbfsABH34EadaUZztIYwoM0Mg9B9iKtRg,18410 +torch/include/c10/util/strong_type.h,sha256=ZjcJAMwuT3uggcpxBy3FE1E5zK5sVFxmSSzkYlKkv7o,35534 +torch/include/c10/util/tempfile.h,sha256=eCtZ_I5XJ2I-WUUWs0UoTFdgAhr9HKsVcmFEJsI340A,2760 +torch/include/c10/util/thread_name.h,sha256=ekaynOg56CtdQLnuzKzGOPlMiqOwI3BVsm61zjNYiTA,186 +torch/include/c10/util/typeid.h,sha256=IULB_ECFcE-H94YAD0K-uf7HSc5HBA8W5yHhhWPPMuY,23330 +torch/include/c10/util/win32-headers.h,sha256=Hwx6Heb2BvTTMDfWYL3yRoXsqMd9xdu1_LRXz2nMzws,858 +torch/include/c10/xpu/XPUCachingAllocator.h,sha256=_mt4kmmw3R0STSiGloRR9XCb5ER_d3oG12rCh0_OmBg,660 +torch/include/c10/xpu/XPUDeviceProp.h,sha256=iVoooFTr4NcWZLbOVEIcxLfl8ovZGutH_MM4iP-0_b8,12428 +torch/include/c10/xpu/XPUException.h,sha256=ZSAWqWf8on0ZSjP-MHInJhN8BAt595-T8Qch7QO5DVs,415 +torch/include/c10/xpu/XPUFunctions.h,sha256=bZtBVG5NW_9GQUlZVgj-SX37waev5Z13RqLrc6VKKDo,1269 +torch/include/c10/xpu/XPUMacros.h,sha256=j3rgISZgHXKvaT-BCOVUdIs_icvCBY0nWc8LfMUMkEo,870 +torch/include/c10/xpu/XPUStream.h,sha256=U4g50BT69PVKFcZ3VOhkxwNYRh7DeT2Yji-ndwzW9J4,6723 +torch/include/c10/xpu/impl/XPUGuardImpl.h,sha256=kvKQatbh7gP6ObxfcwSJ1jH86Oxao7-Wn0UhQr50vSY,7038 +torch/include/c10/xpu/test/impl/XPUTest.h,sha256=0jRNcnoYkdHoNpGMBUn85JpjDyYfaADSTTVuPB2hcYE,466 +torch/include/caffe2/core/common.h,sha256=gQD81cqrtOJKS1VIDSgYPScBH-a7ozXX1YhKcSoL5cM,1359 +torch/include/caffe2/core/macros.h,sha256=6uEd2gtEn1p-uDHfd3fALrDveGeda8bMdqlWUPiVIMQ,2788 +torch/include/caffe2/core/timer.h,sha256=RDrnGkzLCVJtarLuTCNzNCxuVkNCyRAOaOyMISGLwoM,1218 +torch/include/caffe2/perfkernels/batch_box_cox_vec.h,sha256=-LkgcG5aV4yIqnRtr12tDbZ7E8ruk64jffQuyRp_mfg,9360 +torch/include/caffe2/perfkernels/common.h,sha256=T1oAZm5C1bkJtFfiG9DkYFRlwsJi_zSxQtGpQL0GGhs,6149 +torch/include/caffe2/perfkernels/embedding_lookup_idx.h,sha256=xFHjPqhE-a1kAQ9d4gkTVGoaSi42qpkI7H6tF5fIQhw,1674 +torch/include/caffe2/serialize/crc_alt.h,sha256=1S1Y-QOdR_AF6_ynNXP7skgrzBL4JA8knBpcUNniGFQ,75497 +torch/include/caffe2/serialize/file_adapter.h,sha256=TtkLEbrbCTNqHeBQR3YwoOet-K2wytpYfecuWH4MuCY,866 +torch/include/caffe2/serialize/in_memory_adapter.h,sha256=Pi5VbHTf06qgYYaC0kkzobEm8W6P8lJwHpbK0BCVlDs,641 +torch/include/caffe2/serialize/inline_container.h,sha256=d_Kx8oF_qva3AqcLYYo_uvMFANiMsRwHPODDtaWcp7E,10534 +torch/include/caffe2/serialize/istream_adapter.h,sha256=jhPL2xpULlEAj8d6YhmRaSRRjRK6ZFkodgYX3vlPZSs,669 +torch/include/caffe2/serialize/read_adapter_interface.h,sha256=TrtqY4bnGW_iI8ZpIunrB0fbQhRq11g2Xic-AlE2ZPo,556 +torch/include/caffe2/serialize/versions.h,sha256=v2fi7vl7l_cGOVlPEjyW4GRFReQ5Kn4VTyJACt7_Euw,6648 +torch/include/caffe2/utils/fixed_divisor.h,sha256=h-s9IGKmxByW1Q1NM-tEXspxjroKRoXf7mljJRZlTq4,3532 +torch/include/caffe2/utils/proto_wrap.h,sha256=19etQ73d98Z1pFTzPswCOr36TMxl-xFhwX10j6VxrFw,1260 +torch/include/caffe2/utils/string_utils.h,sha256=_Pp-dCE7FWyfyD3-FJiapNlrd6saPgAhA5r2wJbLyHQ,1206 +torch/include/caffe2/utils/threadpool/ThreadPool.h,sha256=Ssi5TAEB12NQbGjxabFogdpFfg17kkYGSDIKuxPgWuE,2376 +torch/include/caffe2/utils/threadpool/ThreadPoolCommon.h,sha256=w-Oa0TSn8C94N3W2btFWN_uKOMy2LxEsO5OOBNVBaNY,677 +torch/include/caffe2/utils/threadpool/WorkersPool.h,sha256=luOnHFYJy920zf3D0H3t8XOKz38pJD65Pw35qPYASHE,11639 +torch/include/caffe2/utils/threadpool/pthreadpool-cpp.h,sha256=YAz0XfStdJ19q9deK6quZbtWi-mjPrh0WG9FCSRjvNE,1464 +torch/include/caffe2/utils/threadpool/pthreadpool.h,sha256=8wkMc_C1nPt7V0OAYVfOEwq5bbfh-pjkcKb6GLyL4mA,6344 +torch/include/caffe2/utils/threadpool/thread_pool_guard.h,sha256=bwJATq9Li6y3TXfFdGyCGlUb4OfW8zV55aPp5vB5BNU,567 +torch/include/clog.h,sha256=AaXz8upLdfJ-qFoEjRHcpp3j-ps8jNkBgStV3V19X9A,4900 +torch/include/cpuinfo.h,sha256=G3s_xB2K42fYqPqm1JUXapf29fVwRskFBgY6uEbsWSs,55174 +torch/include/dnnl.h,sha256=I-QBhgaQhsKI9ws5dIWiC1mo_tzjMZTqcOEH2R_dquQ,826 +torch/include/dnnl.hpp,sha256=7EzmLtny7m_4pRVphyYiPvrsnwF6k4fScSQ6t_vOBrA,834 +torch/include/dnnl_config.h,sha256=4gxE_VLSQRh1ZjNU0b813RVsIqLRf1g97y8E7yYt6SE,854 +torch/include/dnnl_debug.h,sha256=54dhupUgTYXVDXGNKkIHZa_CIs5rNdGaB4jzaKOmsV8,850 +torch/include/dnnl_ocl.h,sha256=zEhZqSx-AMUh-leUn1DUIKre4POhBRoclUX204x0Ynw,842 +torch/include/dnnl_ocl.hpp,sha256=AtN-4Y8wmrv_Fe7DkOMq2-lZHNH3cZK97K6g3ZbkEzU,850 +torch/include/dnnl_sycl.h,sha256=hP8wUN2leTYsOSH0zxGhYCowrWL6y1qzEMJx82cMOLE,846 +torch/include/dnnl_sycl.hpp,sha256=qVdl2cYsFcyCF4NpURATOVd2ZsY9c2QWNSuwA-laDbk,854 +torch/include/dnnl_sycl_types.h,sha256=wx0L7FacMUW1yLGkQdl633awv1WIiMyjPit8VR0MxRw,870 +torch/include/dnnl_threadpool.h,sha256=pXtNzHokkJu8cOj1YHKPswQ2lHyfnlWmahZ6xSFUONk,870 +torch/include/dnnl_threadpool.hpp,sha256=Ma26PaVZ1D_dIKKuSC68LN7Ak0SYjJRnR9TCmvOhC1c,878 +torch/include/dnnl_threadpool_iface.hpp,sha256=6c4hFR5SOjW5ke4BfdrklzYSuFQH9av-3qYIstHvyfE,902 +torch/include/dnnl_types.h,sha256=mj-E92o3tq2jYmf4m5ApwxF5ZUpOHSW2Tcx1Ye2-CXE,850 +torch/include/dnnl_version.h,sha256=TMnzInMTsAACwnwp4ICpkZmiGXNhA4PHNkrHME_E8_8,858 +torch/include/experiments-config.h,sha256=rwhAx9ss4pJKG-7CRm6bKUsTcNMEbkr9ubfvv9xHhPI,512 +torch/include/fbgemm/ConvUtils.h,sha256=G50VbxylWBX8iv4XVQDhjLSgJJUCoOhuxm-YiGdki2Y,6245 +torch/include/fbgemm/Fbgemm.h,sha256=VEpfYyjB6O243f8L-9AhjEiJ0z0RjBv35VRE2K1dMvc,42803 +torch/include/fbgemm/FbgemmBuild.h,sha256=3L6mQFV9ybtcBELHvKnc7Fy35eGRBGVu9yLW4n7yoDo,3319 +torch/include/fbgemm/FbgemmConvert.h,sha256=eYEqrIOQLnAy0CSrggMQRiARI_h-vbigATDljZKhGWQ,4478 +torch/include/fbgemm/FbgemmEmbedding.h,sha256=PwFuZxDRLDNhNgfc2RA8NyACvUVHhAtflzlg7mbPY6o,12148 +torch/include/fbgemm/FbgemmFP16.h,sha256=VForNnWmtK58o_ajrfbsWf2ODuNBBxODPgpDMkPC21Y,1397 +torch/include/fbgemm/FbgemmFP32.h,sha256=YljwjorfD0uJAKPuhQE8YGOBfszMq-zzSGI8G8UiA0U,1272 +torch/include/fbgemm/FbgemmFPCommon.h,sha256=shKonvMaIRKYdPaYr-s5W2cHOaS_BiCbGwi9FBhZhG8,10390 +torch/include/fbgemm/FbgemmI64.h,sha256=DAORg_ftPTJ7JZAnP3InsKtlHzEUFafTPzZad5b_RiY,581 +torch/include/fbgemm/FbgemmI8DepthwiseAvx2.h,sha256=iIn-n0wYuyWyriub-LvyHIjlSP14ynR7PAlL3fR5-cI,3252 +torch/include/fbgemm/FbgemmI8DirectconvAvx2.h,sha256=mHM-A6YYRcSE1lSkY_LaP-66V1fKamuj3_RNk77RD-M,1575 +torch/include/fbgemm/FbgemmI8Spmdm.h,sha256=lWrDHEobDwnR3oclqjv5aUOEVUxsWZNNZ7uBNqvimEY,3393 +torch/include/fbgemm/FbgemmPackMatrixB.h,sha256=oXRXDKxBWmq_kcWhZF2HjJ8E9XCt4gs3d7ZtB5o-ph4,9035 +torch/include/fbgemm/FbgemmSparse.h,sha256=XzJQYJ2gGK8Vsuhe8jggQTp3Rab-tZRALZIvLrM4SnA,6377 +torch/include/fbgemm/FloatConversion.h,sha256=6bOMPmm9ClwkXY9rdv_jlaznhGGjY05zprTzqAWKNAA,11301 +torch/include/fbgemm/OutputProcessing-inl.h,sha256=Rts3FS-hsJDVUFNPJfyAhkpJZ69jU3Bj2dimZsSs97A,10216 +torch/include/fbgemm/PackingTraits-inl.h,sha256=8V4Rhuj8DkE3i057jxYiTkbUTyCwe5XcL_ZnIefSHKk,20783 +torch/include/fbgemm/QuantUtils.h,sha256=IR_JUzNcQ1o_h_wmK5IqdUuEy-b5H4wC4J3n3D-uZtI,12207 +torch/include/fbgemm/QuantUtilsAvx2.h,sha256=VByyLCiwo6C6VyTRecM78aCskTmU5JS5MHCRmiYuwGw,4608 +torch/include/fbgemm/QuantUtilsAvx512.h,sha256=c_ALI57To6WB66plST6vdB6ccAfZfQEAHIu6BFra7Mw,901 +torch/include/fbgemm/QuantUtilsNeon.h,sha256=7iEyN80IVg81U104kzPz1dxs6U__zkyJ30uWaHjkr9E,814 +torch/include/fbgemm/SimdUtils.h,sha256=UH_QleuIKNsa79Hk3fZItsEi23NKwoMiWJw13NYLGN0,1744 +torch/include/fbgemm/Types.h,sha256=GEs_8kwPKsV2wKCnaXW48mhWFgB_m8EECD9oHGf5iaI,546 +torch/include/fbgemm/Utils.h,sha256=qg1AdS4Ta6bSx2LHFfDShEV1j9GspiyXZt0NoKUJK0o,13281 +torch/include/fbgemm/UtilsAvx2.h,sha256=MxeTdRsHBTc4h6sSAxV5WyNwPwoBb5zL1QRMw_WyBts,2287 +torch/include/fbgemm/spmmUtils.h,sha256=zwm1XfLzfgHSlpEa82Tw2MeMiB_8rjrvuwOwQV9j7mc,1388 +torch/include/fbgemm/spmmUtilsAvx2.h,sha256=3C0r-4OfknzbAXqVD6e_z98kMO54CMshQe0NL6fXrZA,1092 +torch/include/fmt/args.h,sha256=zSpCMKK5XbYPAGc3Q3af5zcvD3CHE1IMq3hxc7UFUYc,7180 +torch/include/fmt/base.h,sha256=E8jZU_stmNyVUondFmmZvSLbq_66f0hrNpPCxtnWJ9s,103990 +torch/include/fmt/chrono.h,sha256=qk8RifAMooep5Vhcw7a1ffqPGgAO-vqIFDL0bq-92u0,79718 +torch/include/fmt/color.h,sha256=-8NNQq92LNTkXvLJPQUIAtlW1AbFyeTJLqmwdJ_QLYo,24290 +torch/include/fmt/compile.h,sha256=ENZI-_vOmUge7OKqpPnsjOirJf7whBRCKWtxScOGSHo,18792 +torch/include/fmt/core.h,sha256=srZpIUWxHHwndNsQYHjJdXWkfRjoDHeDxoVmR1Ml67Q,187 +torch/include/fmt/format-inl.h,sha256=esTUkHVorJnChGGIFZUFPS5ARe6ByJ0L3bd5xxjuc2w,80985 +torch/include/fmt/format.h,sha256=msNHOeuZ1f4iDJbVlRm_jan7AQjRz1utvtFXGH5MPPs,157793 +torch/include/fmt/os.h,sha256=Ax7PAuZarxvhbeSvF54bPRFUD4igmJZn1fQTj-k-WTg,12786 +torch/include/fmt/ostream.h,sha256=xJ8Hvle3-yq8ieNChFdHQMJMLebW3wsIr_GguPU3wJA,5024 +torch/include/fmt/printf.h,sha256=em_6w1TaoTfbmk7RTdgXt_e1tuXs9G2vJ0MfQeey5e8,20440 +torch/include/fmt/ranges.h,sha256=CWCSh3gpDIElvjvPklQu9osxVFtreaPBkwzQnrWzRHM,28211 +torch/include/fmt/std.h,sha256=ogzbgTUxCUFupDozCWAG0yMhWeiV6QtkEbaR0p9jR8A,22277 +torch/include/fmt/xchar.h,sha256=1KrSITylnW2sMBVw294A4PDaoIi_hmkHCwKD3pP3slE,13636 +torch/include/fp16.h,sha256=E8HMsWXIGhISM6v9jeIa5K3ncDxV_ccTbShVn-bEmb8,141 +torch/include/fp16/bitcasts.h,sha256=-HHES794UPVxsKBJZFIwWvGNKv6uy1cFyfmIA_PVWHE,2153 +torch/include/fp16/fp16.h,sha256=vOWAk3F5CTq4VfpJkHx6GyWFVts-oUfOMfYEdqrb-MU,22002 +torch/include/fp16/psimd.h,sha256=yEViGkRLfuUWRwCGgNK0hgFeX8yalmGv6zEX2J637ZA,6845 +torch/include/fxdiv.h,sha256=fSkLf2DxcdPiQIF9s90UAZYYt_70xe8TWmU1DA3MAc4,13149 +torch/include/google/protobuf/any.h,sha256=tHLgXHRcbjwFRI7sh3rP-vS5_3mL-eUnRKeX1KjViFU,6181 +torch/include/google/protobuf/any.pb.h,sha256=sw86cey8BUYety02zc4Nd06Hppt6ecBFX8zl-pIQgnw,15447 +torch/include/google/protobuf/api.pb.h,sha256=dRthzUSxqWAM37Gpbdi16Mr-1zaJt-tcnNvABgv6ql4,56085 +torch/include/google/protobuf/arena.h,sha256=D2_l7sLSI5fENGRKx4-gOMwPGmD0fvXVX-nFtuZ3oKM,31329 +torch/include/google/protobuf/arena_impl.h,sha256=WUzwEc2vjjlNDnCZ3Ep8YuqBzzO_LfhP4RGm6y6LY3A,14296 +torch/include/google/protobuf/arenastring.h,sha256=VBeXF1t1Ev-BO07qjz6vr51yW7g7VP4lHZ2tJLef_Qk,14674 +torch/include/google/protobuf/compiler/code_generator.h,sha256=wKdYxwOMgEy4AR3fW0c-lZYvWKscG1mYZnTD3OE-nA0,7853 +torch/include/google/protobuf/compiler/command_line_interface.h,sha256=ghyxQi06zUYsNRnYdtXtWuqJM3OWFKNJLxsvjkETK5w,20429 +torch/include/google/protobuf/compiler/cpp/cpp_generator.h,sha256=P61882U1ag3yGVeR1cg7Xw9Y7Q3OVJg1xgvTUV-PA5Y,4169 +torch/include/google/protobuf/compiler/csharp/csharp_generator.h,sha256=CXn-nwzmeUbluGay8pvlJs0biSxsN9UPAXJZauJ41Xs,2802 +torch/include/google/protobuf/compiler/csharp/csharp_names.h,sha256=Ej83v_UziQeKI6Wv7ePUNKYi3YkkKxDfyugnHNmEcB4,4090 +torch/include/google/protobuf/compiler/importer.h,sha256=3s2AE-qJs--SqTeEaYIl-SL0KRMcdpogPydjXKnkbQw,14256 +torch/include/google/protobuf/compiler/java/java_generator.h,sha256=MKBcNiYB0mMw0Rxx8E5tsJplWCeX_hxMCqCNYu2vHeg,3064 +torch/include/google/protobuf/compiler/java/java_names.h,sha256=Dq4mGOE0omBhhG3xPsdQc9IbwHkXEuRfCiapZ3UD2V4,3609 +torch/include/google/protobuf/compiler/js/js_generator.h,sha256=GoWBNxRj44N7ak8hQ0XPeiDhzSvaTUdet_jwDNqkKXc,15930 +torch/include/google/protobuf/compiler/js/well_known_types_embed.h,sha256=hujEl383IkxlfNjQppB1HM6tVeRpMmuruqJ7HySYhYQ,1981 +torch/include/google/protobuf/compiler/objectivec/objectivec_generator.h,sha256=3LO7M_atFJMuMpdv0soSi-BWh5-r1PWDydmH5-LbjyA,3430 +torch/include/google/protobuf/compiler/objectivec/objectivec_helpers.h,sha256=DsPXHNlqK8n_RLwlB2UgmNsNjCg1CP_Pa4R3Dc6-Axo,12294 +torch/include/google/protobuf/compiler/parser.h,sha256=tPN24mwoKclP3OJCarShP1exvGyxUFq4XsdK_TIW5Xs,27828 +torch/include/google/protobuf/compiler/php/php_generator.h,sha256=5G3ALACJwXJwMgQ4LkXdNpxTpzhBp0hhupKUE_aTHfI,3602 +torch/include/google/protobuf/compiler/plugin.h,sha256=sAKX4WLtEzOFU-R8cX0mJKgHc9C_SJMthLM3s7swLG8,4349 +torch/include/google/protobuf/compiler/plugin.pb.h,sha256=if8V-EGu6M_5Gyo87ldlYAwDBz9BrirSd3VfEqQncFM,76347 +torch/include/google/protobuf/compiler/python/python_generator.h,sha256=2Afy7n99LN5Dx45cUdvaWJ9OehL3UsYqWC0DufdkM5Q,7737 +torch/include/google/protobuf/compiler/ruby/ruby_generator.h,sha256=XRtwU_wjEMtTU4K4oJ73P3nHh-3N_xmGRceYBfv7crI,2811 +torch/include/google/protobuf/descriptor.h,sha256=Ua_-tZK0cfpcfcDDn-Lu3g0wYmNz0GfiSQuEPPjm1o4,96497 +torch/include/google/protobuf/descriptor.pb.h,sha256=MtEdI1N0moGtplWP6VXwEGrdMaJn3fkDM5FucAtE8gM,544698 +torch/include/google/protobuf/descriptor_database.h,sha256=qn9jw81Q2O4cYNiB5_gH_tyIzhbjyl6Ov_5BKo8bCPc,18765 +torch/include/google/protobuf/duration.pb.h,sha256=d-Hdg2Ej-TCgTvEFQ3MqUGaf5YWLkz-MiAGVVNYYJEc,9864 +torch/include/google/protobuf/dynamic_message.h,sha256=v2xY-QASYAwEIEMzaP5XCdqgw5xCCPfdvByXK03SCT0,9986 +torch/include/google/protobuf/empty.pb.h,sha256=w3wC6DsZDbxE4fj75NvzY34fczKa0QLRvEwXhS_ucA8,7682 +torch/include/google/protobuf/extension_set.h,sha256=4JpJzQrcDjZrEaYZMjdMrfsvOgp1dixHV_5W4YwM2eY,78461 +torch/include/google/protobuf/extension_set_inl.h,sha256=jK7ZQ1-FNHlcGtSzhr6rgr7zwWQ5fiFXaHkx1Om78hc,12437 +torch/include/google/protobuf/field_mask.pb.h,sha256=6bEftyPjFyV-1qmtajur0VWSrsQEN4061RHNYDo0BtA,11824 +torch/include/google/protobuf/generated_enum_reflection.h,sha256=beZ0Pq7NuOz6qpyBBL1LTW1fYM6_UJpKAdDQTcQjJOk,3993 +torch/include/google/protobuf/generated_enum_util.h,sha256=F9PJW2bBh3pfC1fqkje4AdaI8S0aoS3wrDHM0dxRGRY,3266 +torch/include/google/protobuf/generated_message_reflection.h,sha256=pojsjMD2ugmmbu2x8CXq8dlQJNfwlc99kEaSQur2SJA,13050 +torch/include/google/protobuf/generated_message_table_driven.h,sha256=m1WfF-BSX2fYJ0O6qBF2k3kTbLf501kD2FM-M_QNBr8,12610 +torch/include/google/protobuf/generated_message_util.h,sha256=2zGiLI_5k-1ZkAHjKg4JpYgua7HD1r5CsAqKHIUsss8,9560 +torch/include/google/protobuf/has_bits.h,sha256=ZQsRURECos1bSFmRs-s8Yg1y8HNxpCVd8hSqBDfkfqE,3528 +torch/include/google/protobuf/implicit_weak_message.h,sha256=CzUtDuAt0SuxNG3vcBmQ-DGXI0N2t6-ecnx3JgVeF_M,7014 +torch/include/google/protobuf/inlined_string_field.h,sha256=_tJ9QTMTbOVuwbM8MCri_2IQvUAWeSHJXEYXPMEgxQw,9332 +torch/include/google/protobuf/io/coded_stream.h,sha256=OTdZJHCp9c0fZKy9JoJsYGcH6agK8wNvolUGF3f3mFk,69376 +torch/include/google/protobuf/io/gzip_stream.h,sha256=-l2Y7FPELXj7sWbqRp14JCz-ZW0p9ZT6kY7jw90DYfg,6682 +torch/include/google/protobuf/io/io_win32.h,sha256=LTF4YaZ7ZsVNwrL1_gm5dJAD__Tv753JxxX-XiwXqho,5384 +torch/include/google/protobuf/io/printer.h,sha256=z5WrxcCWyZWbXn5RuUSKwSeNmJwpBjI-RY3sG1NLXrQ,15934 +torch/include/google/protobuf/io/strtod.h,sha256=ri1B0MEU1A5qH24w2AryLkbJx_4j7C9fUGiOG-A-Bss,2431 +torch/include/google/protobuf/io/tokenizer.h,sha256=LlxEAdD9LUbC4nHJZCvaK5hof7Wk2kw8DWWIOGdxroc,16757 +torch/include/google/protobuf/io/zero_copy_stream.h,sha256=vLd2SWvd9vJo8ZIkkt7zd-b_XKgzIAa8XYfYOkauElQ,10258 +torch/include/google/protobuf/io/zero_copy_stream_impl.h,sha256=oKsaYWRbmr9-tVcr2uUdInn7oOENpTzDw9xotMpyXbY,13188 +torch/include/google/protobuf/io/zero_copy_stream_impl_lite.h,sha256=SNohkWamNK42WBaWQWYM-PIiffZ1cvf9rq7eY13iiXE,16826 +torch/include/google/protobuf/map.h,sha256=FYt0ZTwe3_0q9aKPRx-4vjTTReQeJgh9bMHvRE1Pl-A,44754 +torch/include/google/protobuf/map_entry.h,sha256=TfjCXKosfDe6ky6s3aemQJTsLcS7k3lZ5rb5aENps9c,7276 +torch/include/google/protobuf/map_entry_lite.h,sha256=6rQ6Uh1D1kyeEyGYvjLhI0IL3CHeuZiEm9t47CIakRE,25878 +torch/include/google/protobuf/map_field.h,sha256=zMjnUD9AKCGn9bTOcKNRDU6BCNAoMCc5JG498NezVU0,31425 +torch/include/google/protobuf/map_field_inl.h,sha256=-p1HJfaX61XCZ2qHnjC8o0y7U8oXffP2-CzeUnbXwY0,14512 +torch/include/google/protobuf/map_field_lite.h,sha256=y6dx67Uyyad-c_C2P1T2cxtEo72OrUBSyduR0pdZc9Y,7559 +torch/include/google/protobuf/map_type_handler.h,sha256=y1_tCE7begh4unTqR3YSyWEOKCgzegMAH3hXXra2vWY,39082 +torch/include/google/protobuf/message.h,sha256=j-d4_UdhbltntpqRnBaHaM2q2y3p8EM2p3U63OvKyeE,62231 +torch/include/google/protobuf/message_lite.h,sha256=NoxJF8UV2SvDjnq-IP2sw52lg-kpnMf_O_qXAeNtClA,26575 +torch/include/google/protobuf/metadata.h,sha256=Yq7QejpBOIH3tUPx9U5NYdWZ_lsbv3Zug5cRZDCfS8c,1857 +torch/include/google/protobuf/metadata_lite.h,sha256=EMVPD-YCI7TPIa8qpafmoa4_Ir1q61wpH1AL1vrF9YU,8260 +torch/include/google/protobuf/parse_context.h,sha256=JX2wp36fYCyqBBlacFd8S3HNiLw077Th6on0owmMYfk,29618 +torch/include/google/protobuf/port.h,sha256=io-uYSJYa34thTOvDtTN5M2YBalbHs5LbOvfYioQYW8,2050 +torch/include/google/protobuf/reflection.h,sha256=3W7f_bBSlI9dSezXA5-rgE7SEC0qaHHD7zFAicSz1cU,22717 +torch/include/google/protobuf/reflection_ops.h,sha256=iMduOSuALQwoiAcgrskUo514bn99fqW8B637TIIz0ks,3810 +torch/include/google/protobuf/repeated_field.h,sha256=Q55Ry74scxpDpJuySKRX7ae8_SGPFEBIX1ga7m9NR84,101590 +torch/include/google/protobuf/service.h,sha256=oBIcMDxs4g247zXDhuPr-vrB891FV4IvYOsEiuQOaV4,13159 +torch/include/google/protobuf/source_context.pb.h,sha256=-s9X1fE7EMv6XVEmese66_fYWTS0JCO3fVddPZkEXFU,11530 +torch/include/google/protobuf/struct.pb.h,sha256=Kn0SwH7ujoN-wkQLewT5U8FNk2QSzHYNJrRiFIvfqyY,42303 +torch/include/google/protobuf/stubs/bytestream.h,sha256=Vrbyk7Gil3s8vT_AyFWGAFx3mqyJXegjj73pSPYCaos,11759 +torch/include/google/protobuf/stubs/callback.h,sha256=bdJUB8JnfWM5TpqzDvr2cpDRPU9dVGN6Bov-Jc2dzHk,17078 +torch/include/google/protobuf/stubs/casts.h,sha256=xWLAuFGf-Jmkoz3o0SSTUMzG6N8MYVmOwsB-vNvSQU4,5733 +torch/include/google/protobuf/stubs/common.h,sha256=CT8Z_sHtECNOsEt3LDtYFHSBZLq2me6VXEBiJ-XwA34,7283 +torch/include/google/protobuf/stubs/fastmem.h,sha256=FPGFbE4RdO7Zyv_Sarq3qM4pJwSGk4nAq1KjB5-vMBo,6016 +torch/include/google/protobuf/stubs/hash.h,sha256=xfplDSXoyJCV-ZokmGJVNrv4Zl7Qp4GReQE2cwvNaSc,4116 +torch/include/google/protobuf/stubs/logging.h,sha256=Dti8x-hz8wNeZJpbZCdiD6oKF-wMQKkSlG963Awvg-g,8915 +torch/include/google/protobuf/stubs/macros.h,sha256=5-vlE2KDw0XW2ZMvTHSGFdnptyY1U_IeFEMrNHCtzCM,4903 +torch/include/google/protobuf/stubs/map_util.h,sha256=J8WPjmcTqQFHdd_94IOOT6ajnvfbGknd64MYHQLvwgg,31213 +torch/include/google/protobuf/stubs/mutex.h,sha256=ew8EujN15cieg-hh7r557uAF4UXRIsef9XxAFP9DEs4,6157 +torch/include/google/protobuf/stubs/once.h,sha256=hF26IIrrRP0L9uH-QQmzXQZhkNBWZDnPpoZ3Wp4MmgQ,2184 +torch/include/google/protobuf/stubs/platform_macros.h,sha256=p7qIAIgM5-Li5gvmaAFSl0UwTHb8VfZDlcHx2Gz4XNg,5108 +torch/include/google/protobuf/stubs/port.h,sha256=Upfbx2LnIc02I6CPYHKC5t-Ge96GB_KEh3MOD-Fr7xQ,12744 +torch/include/google/protobuf/stubs/status.h,sha256=zzEeoqtMSZnPYZPSAzl8EFTiDha1AitZ116x8Tv1kh8,3939 +torch/include/google/protobuf/stubs/stl_util.h,sha256=WmLkhpTCn_XWjKNdK9go8d7f7R0Rb4rMH6UT_zIFtxk,3283 +torch/include/google/protobuf/stubs/stringpiece.h,sha256=ZZ3J4YOjn40_iYCEVbgoUlV287oPU86YVoRumyrefng,17861 +torch/include/google/protobuf/stubs/strutil.h,sha256=2B5fBAPk4RaGTf2Cnf_SmdYV3lKm6ozXAv9pkX8NsY4,38777 +torch/include/google/protobuf/stubs/template_util.h,sha256=eWWbVMKQnOR-6qJCAXcd-w5rfN3TCxDMa-7d-SFTvAg,4834 +torch/include/google/protobuf/text_format.h,sha256=g-jJl0aQzIuH98QKCTxBrjoPcVp4MpuPB5vSDbrWa90,28991 +torch/include/google/protobuf/timestamp.pb.h,sha256=rdapYETltdnRK1ei09CkrxwP_SxXdzV2TleyoUzMAaI,9927 +torch/include/google/protobuf/type.pb.h,sha256=mg-VNYu6knaFG_Du_273-xf9X4hjSx_4FL4t0ZM2vYI,96778 +torch/include/google/protobuf/unknown_field_set.h,sha256=1jHkSNUWmToJ1lAWk_yDmn8MAJRAm3R2IGhqR1fmJXI,14401 +torch/include/google/protobuf/util/delimited_message_util.h,sha256=KDwgq0aTz6jGmnLR5lJ50UiLOfubHTEMFppzmOlGOtQ,5399 +torch/include/google/protobuf/util/field_comparator.h,sha256=Sts5FN2gQKEydnynI3OmWxCNSjwymYVZ5V-0lJi9atc,10502 +torch/include/google/protobuf/util/field_mask_util.h,sha256=JZhC4WSkxj-yGD79LQAusvJDQOusKGL3cyCbIcXRNgU,11377 +torch/include/google/protobuf/util/json_util.h,sha256=iDsr9cgOr3wCleQRPc2t7-ChjZ_FbV8_D1pCmXIXrDQ,8429 +torch/include/google/protobuf/util/message_differencer.h,sha256=TtCqYocuqyLQzi-4s6MJRTeyy731SQZKc-78vowYm3o,45278 +torch/include/google/protobuf/util/time_util.h,sha256=ztJQapLnoKiney7z6lD5RIva58CozbzJeKdqE-BaP84,12153 +torch/include/google/protobuf/util/type_resolver.h,sha256=wMZnHBTv8l6M-ggLCXt7T9Qo-Ol8CcIdFGIhm5lzu18,2853 +torch/include/google/protobuf/util/type_resolver_util.h,sha256=sbr19FILDVJxttuJlQ97Wl_s5tjWVoTauTg_tsxlTgk,2407 +torch/include/google/protobuf/wire_format.h,sha256=JZr-Svt4gt5kpzbjsRA1UmfZPsn6wxuD9mIyVPsJd_o,17660 +torch/include/google/protobuf/wire_format_lite.h,sha256=-Rb3OC5-H53eEisuloroaXvk_0vu0XOf3tbQT4uXGtM,83648 +torch/include/google/protobuf/wrappers.pb.h,sha256=vr2dtQ0pmDWplWiJbwNCvFboN7ws-qQonPruXzJmUmk,59110 +torch/include/ittnotify-zca.h,sha256=AJ3-uM-RjzsIAGNOMOFsZTjEi4E19lX7xPW00a6LpSQ,3753 +torch/include/ittnotify.h,sha256=xdml2gsXFgbv19hYMBkFP7c_6uuH5_iVCbR4Orb2R24,196841 +torch/include/jitprofiling.h,sha256=jpp0kV1JxqTYcsdHbTrO3D-vNM-4zPvtOi7hMCR_Ipg,29618 +torch/include/kineto/AbstractConfig.h,sha256=andta5sDue8DlHhWgyed83AE1PofR2Osgpa3mxMTqik,3779 +torch/include/kineto/ActivityProfilerInterface.h,sha256=u6obzxIr28ajKQRKme0rSL_l0N570gKME-EDLWeP89M,3552 +torch/include/kineto/ActivityTraceInterface.h,sha256=Coq_Kwi86OF1LTZQRmfIuMrLIBNaHim726eYNAilhkU,584 +torch/include/kineto/ActivityType.h,sha256=kXahxRoCl3iWOsI1y6J3pDHbdrIMffbo5XsB7EJQXJ0,2283 +torch/include/kineto/ClientInterface.h,sha256=YDTshxyApoTPvnO3Jek420TFElhSSBqsqdQ6Rr6MwHc,657 +torch/include/kineto/Config.h,sha256=5m3HiYaKZO2RkUYjb7EuXQrWEM7mLK7M1PMLa7tsfaQ,15672 +torch/include/kineto/GenericTraceActivity.h,sha256=fFAt_aozgX_FvBVJ0JSDuOvce-38SQCd0bKAoyONHz0,3897 +torch/include/kineto/IActivityProfiler.h,sha256=nZ4opNgUHak2rjnmXRKsynHR_aOkka4YX2xMABhvUVk,5110 +torch/include/kineto/ILoggerObserver.h,sha256=28xm-nCg4W4bLCD4SUlVCPTnt4E1CCoxsmtJqN-nt9w,1817 +torch/include/kineto/ITraceActivity.h,sha256=WV6cpS0OCGvuazS4QCZwZUdGJZi5Ha4Xukm26RELGPk,2048 +torch/include/kineto/LoggingAPI.h,sha256=HLST4MqtkfPCyKw73wXzLbrmlec30-86wFPqtD6yskM,346 +torch/include/kineto/ThreadUtil.h,sha256=inXOgkTdSvHHt-yinit9MH9QL4qkwhy6TleToZ1G-2o,925 +torch/include/kineto/TraceSpan.h,sha256=aTEdtbt10iDgA4Pev45xyAY-FxHfsZlZSSWShdCURCM,976 +torch/include/kineto/libkineto.h,sha256=y4RjZbdPLwEyKT_TJu0YR_iqgNMaxL_xATYtXSiM8ms,3886 +torch/include/kineto/output_base.h,sha256=JCISl0d7_bVmbrxt_YWgNwOo9z8LF65DpeZtt2XGmAk,2164 +torch/include/kineto/time_since_epoch.h,sha256=mjpsNatkP_mbl52mcvHJTIRd7Y4QaQRqQ3iLbAhz9_M,516 +torch/include/legacy/ittnotify.h,sha256=EDDLoOyN7HZtUcObpn8wUCbosNZPLx34S9Q3At0vNro,37374 +torch/include/libittnotify.h,sha256=DLOGBo3lU_rfEdVlJhdcBHQl4fm7d3yfulrg5LZlWgY,569 +torch/include/libshm.h,sha256=6V9-_p0xRhavijNsGdB4oxPHUgjOUZjd2U00-3dd0fA,1196 +torch/include/nnpack.h,sha256=1msg-Qi_QPKPHQHBEDHVWvHj0ilZNjwVIUSEhhs7_es,33083 +torch/include/oneapi/dnnl/dnnl.h,sha256=XvBNl_evUn7GVRVMCfrGv54n6jPYq3Nofqbb4ZqEAss,183846 +torch/include/oneapi/dnnl/dnnl.hpp,sha256=J-8x4wCzK-izTrOqeTpMwTYFGdCmdLFOGtFxjjTdu2I,649300 +torch/include/oneapi/dnnl/dnnl_common.h,sha256=yK8l8x_svYEKa4hRotdk6ynGnv605ynIrQMOlz8rcx8,5656 +torch/include/oneapi/dnnl/dnnl_common.hpp,sha256=azYu46ePZz0j8-SvoaNOIiTScumqdxW_ABu_ek-0dHM,15713 +torch/include/oneapi/dnnl/dnnl_common_types.h,sha256=l0n_Uxw8c75yB7TU1UyxhfSb4zn1ZZL0Z_q2kYnSCpo,8202 +torch/include/oneapi/dnnl/dnnl_config.h,sha256=a8KBa6TC1E2RlaJEQ9UyRilrzdCE8A2ciumr4BtqdQM,6235 +torch/include/oneapi/dnnl/dnnl_debug.h,sha256=nWfvIc2lpvZqC8_h3Jw13I-9_yQJDy-uSXdDQCNLZIw,2310 +torch/include/oneapi/dnnl/dnnl_graph.h,sha256=CaPhfrXhiOjTGsFHzP8xX5Joy0ECJC4_iAa6OAC04Yo,32726 +torch/include/oneapi/dnnl/dnnl_graph.hpp,sha256=itPg7WVeB-5koZ3TLlDx20b2k57uEtdd9Fp-ydmnFoM,65424 +torch/include/oneapi/dnnl/dnnl_graph_ocl.h,sha256=LFfFrVa4uMQDYLz_cKY8YIv4oVvI3jOrCjBOdUkekkw,5928 +torch/include/oneapi/dnnl/dnnl_graph_ocl.hpp,sha256=aFRXRyP8gx9WtByBcvHRJyVfSexUL5QSuduwqiVn-A8,5433 +torch/include/oneapi/dnnl/dnnl_graph_sycl.h,sha256=QaPEeYUFAFoD9h1vxR_8Xiy0TBzW2v5lGVQYT-i4xro,3653 +torch/include/oneapi/dnnl/dnnl_graph_sycl.hpp,sha256=JAEj_GnHY_RtDuAw3Fzv78gEleQbM6V8P8QMSaOAvHw,4337 +torch/include/oneapi/dnnl/dnnl_graph_types.h,sha256=UhakslKQoUK4i0B9xw-mduTH8CW1QtKjxcb6pv7R40o,16800 +torch/include/oneapi/dnnl/dnnl_ocl.h,sha256=YFI9CfR3drMyF9eITqLcjEZW7y6MzHXNv1bhFlFZEkE,11859 +torch/include/oneapi/dnnl/dnnl_ocl.hpp,sha256=jfJgZr8GncJIP4IoZnKCI3TX5Wuk7j1PL8lnWvpLJSY,17779 +torch/include/oneapi/dnnl/dnnl_ocl_types.h,sha256=J3rW6OBG7OgDXOJqLMLxjsNCUhPvOtWNA9hC7Rs3fhw,1338 +torch/include/oneapi/dnnl/dnnl_sycl.h,sha256=BWiUd0aB6tmb2BzIX-Bx76FwYlaZ6qfZ_Wcq_k0Mtlk,8352 +torch/include/oneapi/dnnl/dnnl_sycl.hpp,sha256=BPTDFhLgxeU29tGD-UabVBxferIgbaHVPtHYrry2lYs,14844 +torch/include/oneapi/dnnl/dnnl_sycl_types.h,sha256=pdRCsKfL2oiE6dXeGQCOSuKEgMXkO5GT9RuzkH9Uqfk,1350 +torch/include/oneapi/dnnl/dnnl_threadpool.h,sha256=MRxtU6ucvrZgFMCsU5JBrYfaT9USXmj1d_EI4eDeTSo,4524 +torch/include/oneapi/dnnl/dnnl_threadpool.hpp,sha256=xEu-0Z6qWMREsxLr5wgDbbS50G3L3OkTOu6jNsP_HzQ,4327 +torch/include/oneapi/dnnl/dnnl_threadpool_iface.hpp,sha256=wI0Azl5YocPzAIn-Uf19cQCIN4msqw2sycrMazNYFLM,2206 +torch/include/oneapi/dnnl/dnnl_types.h,sha256=Q_kuDu1Q96c-0OzzpPSWhRgAk0ulna7EJ54qj5fy_Ss,98392 +torch/include/oneapi/dnnl/dnnl_ukernel.h,sha256=kQBgmDEj7pgY594rf4HIKGd9cstqEWo5VsByIqDRO1I,13516 +torch/include/oneapi/dnnl/dnnl_ukernel.hpp,sha256=9ECnz5XyRCnbXGlsQqEo3u9SM_lYQptWMRsb9-04dpY,17324 +torch/include/oneapi/dnnl/dnnl_ukernel_types.h,sha256=MF52ZASRif0lrA__R2FzpoBY21u22QU4c7wdKEW_2vU,2617 +torch/include/oneapi/dnnl/dnnl_version.h,sha256=f9_KKgcWSpZFqY3Sh9_j4r9b8mTsp6cpERRndHLwnFc,1012 +torch/include/oneapi/dnnl/dnnl_version_hash.h,sha256=CMwvwA9w8HLqZhbO2OAW5eVweQ2KWX01n1odCV9p3bI,1246 +torch/include/psimd.h,sha256=4z8bkysyKCCrjI4MhcqPUIsdV46VUcRfjK8vO3p5q14,45504 +torch/include/pthreadpool.h,sha256=r65lzOu4EN3iozcIdd9C-J7y5DUCo0MCCNe26YD0t0w,99328 +torch/include/pybind11/attr.h,sha256=QPjH7BfhL8QFwHHkrDak8gNOLMlb1itAO5fobjdoLp8,24334 +torch/include/pybind11/buffer_info.h,sha256=_FcQisqdpphfWXKeCGNv3Gq5ivy1z-qF3d1Noeteaok,7778 +torch/include/pybind11/cast.h,sha256=8gJ4Y4nc83dyq12CuU7ircAvAV1HoEZEVr0UyfeLQNA,71696 +torch/include/pybind11/chrono.h,sha256=A23naeloqn-1NKVAABOsJtHU9Vz8lfvrAICuLk-7qBM,8458 +torch/include/pybind11/common.h,sha256=ATg9Bt1pwF8qnNuI086fprM4CUTdrZdk_g2HXE1Sf6A,120 +torch/include/pybind11/complex.h,sha256=AaDZ-rEmK4tFaue-K9P5y3TxxnaQF6JwZ_6LAzkdLQI,2096 +torch/include/pybind11/detail/class.h,sha256=Bjk3K6xAMgwxPNTKfik7SC5Y24wgKs8Oz5VjvFdy0kA,29026 +torch/include/pybind11/detail/common.h,sha256=uxFMVYKW87YPbUz8Mo70xoVrpK2D1NzhKSwlDpwrJxo,54708 +torch/include/pybind11/detail/cpp_conduit.h,sha256=Bbx5728XzvyCL2gfW7kG6vgDltS5-V5gtkNQFPFevXg,2589 +torch/include/pybind11/detail/descr.h,sha256=D63pIHsF3luO_g51CjbJU8Wl9VOihciEXQhXvfRg-Rk,6035 +torch/include/pybind11/detail/exception_translation.h,sha256=fM1J19z00AuDlozHt0srpCJr-1uWW4kj_fLdSJDbdY8,2600 +torch/include/pybind11/detail/init.h,sha256=Sb1UkPecC5l9xj5naYLdUM7qIRLVpe614H9Frvyg8xg,17983 +torch/include/pybind11/detail/internals.h,sha256=xs-I7JdJACxx7gJf12HBLjL007jRXcAffPDsd0oTrq4,31985 +torch/include/pybind11/detail/type_caster_base.h,sha256=mdgZ-FIkxdSShMPPe69EXxjvd1eQDDBVX835B7XqCNo,48938 +torch/include/pybind11/detail/typeid.h,sha256=jw5pr9m72vkDsloT8vxl9wj17VJGcEdXDyziBlt89Js,1625 +torch/include/pybind11/detail/value_and_holder.h,sha256=hwNYlqxjUhlUqihwMjr6s3LhhKlZiTLaWREtQrgOAkQ,2814 +torch/include/pybind11/eigen.h,sha256=-HmSA1kgwCQ-GHUt7PHtTEc-vxqw9xARpF8PHWJip28,316 +torch/include/pybind11/eigen/common.h,sha256=dIeqmK7IzW5K4k2larPnA1A863rDp38U9YbNIwiIyYk,378 +torch/include/pybind11/eigen/matrix.h,sha256=VjCfx8M2AcD3m8THUbIEYidJyIClaNw9jMbd_Fzfo1s,32142 +torch/include/pybind11/eigen/tensor.h,sha256=csE3_N9yy-9k0SWQPJuAxmv8Jp_-lFrrPdVOyMV8-gc,18384 +torch/include/pybind11/embed.h,sha256=F3JQiOWnLGSuZ0NuEyBWFhHyVdczD8D_67kriU4QfsY,13362 +torch/include/pybind11/eval.h,sha256=7re-O2Eor1yD0Q_KgFkHIjKD17ejzII687Yszl9_KfE,4731 +torch/include/pybind11/functional.h,sha256=iOyYuNmbI-K3zgc1IMDwe4iHEOO3F8vwZbVSvbgxFQ4,5267 +torch/include/pybind11/gil.h,sha256=hsJj6z1iXqlo5c7fPCgEvK_-eeDoKZm7PKPwPNCdVVo,7702 +torch/include/pybind11/gil_safe_call_once.h,sha256=KKcy9Wgc_MJY-U5WpCZeNyzW7oVmC-d6yXkgephZ7zs,3993 +torch/include/pybind11/iostream.h,sha256=K5rPXoCYN325r1PptcJCIhPhgtRtTJQjMr7bvUIOwxk,8862 +torch/include/pybind11/numpy.h,sha256=xREhfycUTCOPF8CF-UWRdoLX0B23V6YWRiBqeRRElZg,84442 +torch/include/pybind11/operators.h,sha256=224RoAXcv1la4NNY9rQ3aD_AeC8S9ZKx3HVK1O8B4MU,9103 +torch/include/pybind11/options.h,sha256=qXvmnj--9fZSp56NYefnB3W5V17ppHlY1Srgo3DNBpw,2734 +torch/include/pybind11/pybind11.h,sha256=hbzXHRCBIW7dwtwaKjXKPC0Nl1MGHZ5-BjGsMlE3LuU,129898 +torch/include/pybind11/pytypes.h,sha256=BF8x4S5fsAzWf-d9pu83UsqjwRRo0ragHPy9sDOpUvk,99894 +torch/include/pybind11/stl.h,sha256=aMi1OCCw2Zb-IRLSlAtQEJJHtWsRJiLT9dKDMHST1Ic,15532 +torch/include/pybind11/stl/filesystem.h,sha256=lcYRCwNA8Xf4e4FRbeYh36SAwQjxKgyTXXdrguR4gM4,4559 +torch/include/pybind11/stl_bind.h,sha256=B5t8E0A4Zdgm2sF0J8Q_UI2U5uqEBQ9TsJCelsJ4q0E,28495 +torch/include/pybind11/type_caster_pyobject_ptr.h,sha256=H7pKBYTvUlibiJQEcKmeAkygSQwoCkuIyukNSDmVq-U,1929 +torch/include/pybind11/typing.h,sha256=PIjZFNNzY_KsrkHQPlg0Vt24jlTi6kThdOldEJjchtY,7000 +torch/include/qnnpack_func.h,sha256=kxuQHibZQi5M43Cvi-CVaYx3sB5V0w8IHvy8sTo8GtE,4146 +torch/include/sleef.h,sha256=tN3wkx-sAV38qn7Sq0Usf7VLFe2QitPFOItkQp0dMew,269286 +torch/include/tensorpipe/channel/basic/factory.h,sha256=3o2OYoXOWYEwtjoQ5X-EswIApRuljOdRWv-ojMhhx5Q,463 +torch/include/tensorpipe/channel/cma/factory.h,sha256=JxKJlqaLXICRxUAUQ4WhZvnlDJ16q50RaRNrd_LI1kk,459 +torch/include/tensorpipe/channel/context.h,sha256=7ALwdlTvgQ03mQaf62xXQ8DUyPOqwg45Qd0F4K6d6II,3701 +torch/include/tensorpipe/channel/cuda_basic/factory.h,sha256=at8Vfxcw7sl7Icu5C3OvPlhQsNo-Ggfus3ZHisNnwzc,508 +torch/include/tensorpipe/channel/cuda_gdr/error.h,sha256=FTObdfBN4ickdox3vVEy6wfZFHgQAsvO3NRcQRTQfuQ,639 +torch/include/tensorpipe/channel/cuda_gdr/factory.h,sha256=VC5FOeS1D0zz6ESv2mKRRRTGvtc7XBe1MvHGQ_L-6PI,592 +torch/include/tensorpipe/channel/cuda_ipc/factory.h,sha256=mBJkkIb-1aec9DdU4SPKaznA0-kGvZmb76JSr9ADaWI,469 +torch/include/tensorpipe/channel/cuda_xth/factory.h,sha256=bFkhT4AZfYZNq5UYpOW4_HH5i8U3Nwxq1brg6QoLCqw,469 +torch/include/tensorpipe/channel/error.h,sha256=gELOO5tHQHB4A-WmJuEs6ti1R4EsclrJwIKd9M7tcI4,778 +torch/include/tensorpipe/channel/mpt/factory.h,sha256=G0uZbp_uflgv-AFC7ne6m0txzApGqMZWvt_ICfq3WKo,646 +torch/include/tensorpipe/channel/xth/factory.h,sha256=EmAmNeIHbPe5bMKHnlHR6xdxzTEoCjczXWS8AhiKJWc,459 +torch/include/tensorpipe/common/buffer.h,sha256=p4M8zVk4PM2536SlgY44_RhxEKREEZT6I8oVDJxhyXA,3472 +torch/include/tensorpipe/common/cpu_buffer.h,sha256=pxnNAhil60W4izP77i80GpYRAM1C80jeBO34Jai1RfA,441 +torch/include/tensorpipe/common/cuda_buffer.h,sha256=CTwIuLbgn0Jfp7rKEPZGG_Fo01q9oU67Mpw48SICUls,468 +torch/include/tensorpipe/common/device.h,sha256=KZSlF-D7o3gWVCc8bzQpt_nDBLvW0maDdOFrx60Whis,1649 +torch/include/tensorpipe/common/error.h,sha256=JkbuNVfCczMIFayGA0woiLCOEHTaSLmqC3dHBCvl4_Y,3136 +torch/include/tensorpipe/common/optional.h,sha256=R8Io-h6lETspEfT9z8gWWkKuS2y1X1bTCVFDJCp-lTQ,26638 +torch/include/tensorpipe/config.h,sha256=W7Q7azBmk7xdHVyvraFKBKwxkk6dsQew26lwxuDJu84,351 +torch/include/tensorpipe/config_cuda.h,sha256=_SLA9yKKTnnSbVDgUCfc5BmO3kFgcjGxUxVWu-WH3VM,319 +torch/include/tensorpipe/core/context.h,sha256=jM_MOIRoc1ZUU_NJi6du5PtFaxAzOEQiqzAb_ludD_g,2468 +torch/include/tensorpipe/core/error.h,sha256=7NdEl7Irf82Yk0WL2XXTFnpIapv04vuiEMn7mCCq060,961 +torch/include/tensorpipe/core/listener.h,sha256=19RG5UUa6-8AZAf5Nr-yA6_4j7bfBsdOv0CUwJF83cM,2928 +torch/include/tensorpipe/core/message.h,sha256=ycfshEi1a8QOrNJlOKZwKWKVzNo7F42-pZeiZtglxuY,2885 +torch/include/tensorpipe/core/pipe.h,sha256=Zb0z4tKyoAPd99se_YZs1RQe3Cd7iydEsbanE8LbiD4,2972 +torch/include/tensorpipe/tensorpipe.h,sha256=UlOcNcNxPcn35eq0OBZ0YzjRGPaZevqfOas094Mya-Q,1449 +torch/include/tensorpipe/tensorpipe_cuda.h,sha256=jfoYOu7NbE5bgHtQoW_LktEqtjP-D8QNHl9PgXfuKsQ,704 +torch/include/tensorpipe/transport/context.h,sha256=L6kYLwqLG3V-o90buhzJFPh7eI_dzQEqPj-m0A96alw,2645 +torch/include/tensorpipe/transport/error.h,sha256=cmUQYONBrI-zmT28gCKojsqp6OxSUh-OJq2bNWqjbOA,919 +torch/include/tensorpipe/transport/ibv/error.h,sha256=K_0RM-V2HRRp_f-CQR8VWxXao-jyT_BhAycnAlAX7uw,920 +torch/include/tensorpipe/transport/ibv/factory.h,sha256=x1RLLvO58D4JQY_BTj-Jo7eKQwKA1FVueAeVzzGg9Gg,465 +torch/include/tensorpipe/transport/ibv/utility.h,sha256=OwgbqTXmVFVfbq4HYZTPShLz0lhYWLsJ7OyG1c1uEK0,569 +torch/include/tensorpipe/transport/shm/factory.h,sha256=7SEWso-QS8ZpHPaffz4QCP-o8rusTJAC1Vdw3bMpJt0,465 +torch/include/tensorpipe/transport/uv/error.h,sha256=YHLjjMatSyU_V1ulMyFi3WWJQT_xJZCzH4YHt20tYLg,716 +torch/include/tensorpipe/transport/uv/factory.h,sha256=1hSYHcEhiLvuRgvEanrpDPzO1wtzG_2ZgeLH72mtwAE,463 +torch/include/tensorpipe/transport/uv/utility.h,sha256=FRljgmy75kb4DNXkINUOSN3prXPrNWttP4FvsyR1t3c,1049 +torch/include/torch/csrc/CudaIPCTypes.h,sha256=1NWXzIO6hR-YbsXDEAaux5cXPhA6Dc4POOrVBstZJe0,3397 +torch/include/torch/csrc/DataLoader.h,sha256=lznqMptWAhecYOjR4xNw7M9uz_D_3i-erB2afkahEns,222 +torch/include/torch/csrc/Device.h,sha256=PZL2CZ-5qgubasIuzgds8jYi-pnZ0oe5k4e0EYsKBa4,485 +torch/include/torch/csrc/DeviceAccelerator.h,sha256=dLStLfp1tbkkFyjHMwh--L6B9ZDYpVp_K6AN57OBwwQ,176 +torch/include/torch/csrc/Dtype.h,sha256=49SevXMJtd8xMFqVrp0mXhsRkuB9UbpBgkfA_fn7TD4,836 +torch/include/torch/csrc/DynamicTypes.h,sha256=WYpRAtiPMFFvTu4Sbyfkh5PsDHFZzjvxhzuBNFk0_R4,1062 +torch/include/torch/csrc/Event.h,sha256=Y-GZ02Wfin5HR13XTikZhjiVr5kh7ppSXqolkM0DVfk,561 +torch/include/torch/csrc/Exceptions.h,sha256=Ewl_sNIoxXNQZn02pUoApIEYhCWKFWHji7EiOfoVkH0,16142 +torch/include/torch/csrc/Export.h,sha256=ZiMG2QBIF6DcKqwWj3KSiXLWEn55prmg9a5IjvOzjuM,157 +torch/include/torch/csrc/Generator.h,sha256=TVFID3kuvzBhlkMksQW0WXmj2zmuBF1ZmiszSqoODxo,1056 +torch/include/torch/csrc/Layout.h,sha256=9scMvly2O7boqY16Fviqa26G7g8ZOlUwZ1Z-eG_zVCI,586 +torch/include/torch/csrc/MemoryFormat.h,sha256=WbdxLF1JRTgXDClSt9uvAjsTnYQkkfNDjjSWmSsUAgE,682 +torch/include/torch/csrc/Module.h,sha256=wN2tjWdCnIQxJrMDwGu9qvP8r3Q-33eB2HkCvydBUUo,101 +torch/include/torch/csrc/PyInterpreter.h,sha256=ilADlYayiLe3ShJT172FL3t9Y9C8ZXtR6LX-L6JQOTw,383 +torch/include/torch/csrc/QScheme.h,sha256=GIEzYEWexvy47jwEt5dtcmFLkcvUcm1IZILrL5s_5Pg,608 +torch/include/torch/csrc/Size.h,sha256=GQqbYymnTvaO1mARcvAF08bv_kFkcjl7amSpE4pyAho,476 +torch/include/torch/csrc/Storage.h,sha256=EokXLs8Jc3_CMGEQ2BBO5F5xE4pIzX4XCC98gJwoZ3s,1570 +torch/include/torch/csrc/StorageMethods.h,sha256=rwOmQTprbmsE6JSQwctAI4et7C4sCu4T9aIfqLlcFY4,132 +torch/include/torch/csrc/StorageSharing.h,sha256=1CArzbCWa2fFqAh9GWj4fQovYycv5iY4D3UaamuAKJk,139 +torch/include/torch/csrc/Stream.h,sha256=JuPB-uDI3aPfV9GqGJHSeOtMcUnWaq3-j5ujRhghwBM,684 +torch/include/torch/csrc/THConcat.h,sha256=JD1F5DtB2Ep00ZJVpilglulqKt6sTBwu7hS_Cmw2Cek,691 +torch/include/torch/csrc/THP.h,sha256=SPSS5bsFlvR4N7dgR2kV4fhELIwwggJnA3a82Wtgh84,894 +torch/include/torch/csrc/TypeInfo.h,sha256=PD1WaEEG99zzVohwVBrre6McFQ1AlTxom80HTpGwkrI,565 +torch/include/torch/csrc/Types.h,sha256=AmVTjwFoCqH3ekahbAr32ulikXerrtB7ycNIDeX32lU,163 +torch/include/torch/csrc/api/include/torch/all.h,sha256=X9sts0j-1zn-uZI5Zk_iSjY4frvs-CY7iZ0arA08TY0,564 +torch/include/torch/csrc/api/include/torch/arg.h,sha256=iGZepqd2MO7wSLfVuxeFo4UIfEHsI344159ekT528Mg,1427 +torch/include/torch/csrc/api/include/torch/autograd.h,sha256=MsdrQ_Un675B93MTyFZ1cLc09aLGLpXhwvJTZhR_j0Y,172 +torch/include/torch/csrc/api/include/torch/cuda.h,sha256=OkaJ7vRty0lkoy8DRnF1kT-Q2yH3bs8Zk0mavBoRWQA,733 +torch/include/torch/csrc/api/include/torch/data.h,sha256=IlJ2vjVL1rzMXLX63z9TApxCDsc8FhcOPf5pKYq-l40,297 +torch/include/torch/csrc/api/include/torch/data/dataloader.h,sha256=eqA6EinX-lxMk672i64-YlqMTUNXrQlsa3_Vb_l9kmc,1897 +torch/include/torch/csrc/api/include/torch/data/dataloader/base.h,sha256=A0n42aEgfEV1Gvyvs-T7G2wA47pVrYN3YvIietAazOw,9221 +torch/include/torch/csrc/api/include/torch/data/dataloader/stateful.h,sha256=wRH5eauvwaA1Vn6aYtn583zH10EkyuMJm1O0Sf86Vfs,2335 +torch/include/torch/csrc/api/include/torch/data/dataloader/stateless.h,sha256=okOSiwL42r8u-_uG9bxAdUCWLy682NBg53ee_-0fdH8,2746 +torch/include/torch/csrc/api/include/torch/data/dataloader_options.h,sha256=7t7zN81ZNvoy1jvFEQfzKLC76vO8hdg_U_pdrIrvW84,2197 +torch/include/torch/csrc/api/include/torch/data/datasets.h,sha256=Q3_zlkvTD7BOp09o0vpPhI9r_1ABb_6n5CunXPS8ZCU,289 +torch/include/torch/csrc/api/include/torch/data/datasets/base.h,sha256=m4aZYn5z_ObqxaxrE1xL-zb5IgNOzm8iAirWpnrINrE,3176 +torch/include/torch/csrc/api/include/torch/data/datasets/chunk.h,sha256=E4mB-QSiDwYP3G6FGHXLDH4DjvZJKip1ZB6gyVVDiNQ,19176 +torch/include/torch/csrc/api/include/torch/data/datasets/map.h,sha256=BZmvFM-6O-M2jqNafo8D5oIqfNNCDvYr-tyFUklJHmA,4072 +torch/include/torch/csrc/api/include/torch/data/datasets/mnist.h,sha256=kqT0ofTkPdDcs7fnw2RBm2dGDURedbyhQwLdH8kNvgc,1229 +torch/include/torch/csrc/api/include/torch/data/datasets/shared.h,sha256=qkAzNu4ra5p-DMoEUo4tLhHZo3VDUhtSl4dCTZJJoHM,2595 +torch/include/torch/csrc/api/include/torch/data/datasets/stateful.h,sha256=T5a3MbYm4WfJebn389xORld2yiYJPbsA1cwGXX4_87U,2234 +torch/include/torch/csrc/api/include/torch/data/datasets/tensor.h,sha256=Iztkmo6aaoL78Wn0pPVRORA9gNd-BpnQQbY0eDkFkPo,931 +torch/include/torch/csrc/api/include/torch/data/detail/data_shuttle.h,sha256=ZBf4bnmgw3hiQgJZwTQrkPmUv0G2g9B_mNLfnglgxPo,2585 +torch/include/torch/csrc/api/include/torch/data/detail/queue.h,sha256=G2xElfb0L3ZIKGKfDt5TXexgRNTaOkJCJxAmBz4dUvM,2455 +torch/include/torch/csrc/api/include/torch/data/detail/sequencers.h,sha256=qWNExX4CMyc405aQByMSh-QVytIlIeCTTDYY9x9EoUY,4440 +torch/include/torch/csrc/api/include/torch/data/example.h,sha256=gwikrf8mLOUYa9OUitEpfroINCXrUqpWPAaMJHqj54U,1289 +torch/include/torch/csrc/api/include/torch/data/iterator.h,sha256=xcVrTG_jJ4lfyLLGMW6DUE9kUZpcfxuiBAjlEz-n_sQ,5305 +torch/include/torch/csrc/api/include/torch/data/samplers.h,sha256=ILkgiKXJN6RyR0Fjmmv8E8LuRBIoBH_P6lUmenbf8Ho,318 +torch/include/torch/csrc/api/include/torch/data/samplers/base.h,sha256=PAQat9dHr16nfB3qGSPXz9Izqk-D0_u1mSBri4Meweo,1166 +torch/include/torch/csrc/api/include/torch/data/samplers/custom_batch_request.h,sha256=mi_aMAZcaefwXbv6YrS3eg_frrHAxt8oB36g9zQKTo4,506 +torch/include/torch/csrc/api/include/torch/data/samplers/distributed.h,sha256=p2r7hoOaMwKxCFEpcRlCaH4UfNLXKHlX_BT9CgAKqxY,4060 +torch/include/torch/csrc/api/include/torch/data/samplers/random.h,sha256=4dWY0qlA4R4JEP6QsRxDoe1wEy9YHJpqbyl1rKGvFTo,1462 +torch/include/torch/csrc/api/include/torch/data/samplers/sequential.h,sha256=pxCPT34vO7NvmmZgIqbRZ6Dc4DA-pPkx-cV_Pi-QvXA,1194 +torch/include/torch/csrc/api/include/torch/data/samplers/serialize.h,sha256=ukCmjyoNDTEXbfKwraWJKxmBmBFI9iJ9H9zuHBNr6Ak,657 +torch/include/torch/csrc/api/include/torch/data/samplers/stream.h,sha256=HJOKYjmHk7S6zYDypUBelEJB9PjDyrfXATRuU5ac12Y,1973 +torch/include/torch/csrc/api/include/torch/data/transforms.h,sha256=TvF9SJpmwR6rCQJvCvO4T5egw3AsiQsCCoOXs_0UTrA,222 +torch/include/torch/csrc/api/include/torch/data/transforms/base.h,sha256=xwuwEENxs6d5SumKqHevnur63aLUb-zpOm9omUPQFQM,1579 +torch/include/torch/csrc/api/include/torch/data/transforms/collate.h,sha256=10zo0NvbQhAOhxPepDWrNrOtNXIHQGmgebadphUkTc8,1063 +torch/include/torch/csrc/api/include/torch/data/transforms/lambda.h,sha256=arHlXKcjyE6xKuuNGxPfCa4Fl9WS-G7YGqyUlakH6NI,1659 +torch/include/torch/csrc/api/include/torch/data/transforms/stack.h,sha256=JPC1_-sZIMA71j0gD4PmBCyRk9EGhLh4P_OLhqBQmMk,1374 +torch/include/torch/csrc/api/include/torch/data/transforms/tensor.h,sha256=Y5AH3XdWbAgixG8dvEUZ1R-fQosdix-r83CxmmjiSUA,2423 +torch/include/torch/csrc/api/include/torch/data/worker_exception.h,sha256=V00xZ8JCNaCPNW7YSlyHFcJGJQJE6J93D-e2SadjcXA,1119 +torch/include/torch/csrc/api/include/torch/detail/TensorDataContainer.h,sha256=FXUgClHjFzNhtprOR8Y3usAHTrCmapfnYYwITPo73-0,12952 +torch/include/torch/csrc/api/include/torch/detail/static.h,sha256=6iA7ggMuak4kvTadky44vtYuFKi23FkHiBsF62FWxXE,2093 +torch/include/torch/csrc/api/include/torch/enum.h,sha256=Q6Vdsmlu-nCxkQ9Ikp3okyZ6fFdfbz35Ty2s_9ZG67o,7450 +torch/include/torch/csrc/api/include/torch/expanding_array.h,sha256=EDZmv1ZQAVcPq2tv_CBfd1t64VItQ7jat8CJlbpgMCM,6673 +torch/include/torch/csrc/api/include/torch/fft.h,sha256=v-Ldz8yjbe3-_pYoRfhinVCyhm4TaZe_mDiBZ5d6cRg,12104 +torch/include/torch/csrc/api/include/torch/imethod.h,sha256=jKsQgJqScAnlZLk5Gpx_b0mE1crUn95AULY3w77hjmw,1740 +torch/include/torch/csrc/api/include/torch/jit.h,sha256=LSJVfWKSM4wYpxhsgWlWBsAk9AdWyJyISZdQgsAj2Oc,888 +torch/include/torch/csrc/api/include/torch/mps.h,sha256=YAU0DmAAIUx2clIDxoRGgmlUSae2OTl0beXKt_r4kfY,1194 +torch/include/torch/csrc/api/include/torch/nested.h,sha256=s5zXNEk5wq_07XVisaUvB58dZua7XaKuHsjOBEp5dRE,2773 +torch/include/torch/csrc/api/include/torch/nn.h,sha256=Iah9Blyam2lcEKX85589p1KxupDmsbwkigjMErXYSUI,251 +torch/include/torch/csrc/api/include/torch/nn/cloneable.h,sha256=cS8rs4M6i0NcONInMTMQDynCh1NgN_5twO2OgjBgOKo,3901 +torch/include/torch/csrc/api/include/torch/nn/functional.h,sha256=NysmuOu8Bb9fjjXr_qJk2eflRZtyB-B_txwODYKiCyU,642 +torch/include/torch/csrc/api/include/torch/nn/functional/activation.h,sha256=jLpA-S6aAXSmYYT03h75T8wRXGyOLUprCw8J9zGK-oI,29774 +torch/include/torch/csrc/api/include/torch/nn/functional/batchnorm.h,sha256=7lwP2-0zc46BLd9MLHx8i3_mEii8nqCcj2_Juxv8oyE,2015 +torch/include/torch/csrc/api/include/torch/nn/functional/conv.h,sha256=AjxXTpzB5pisKdVla-awsZlifsesrN0BtW6OIjRd8Wk,8097 +torch/include/torch/csrc/api/include/torch/nn/functional/distance.h,sha256=6YndjdJqr4fJm0Ypbzu7uXffTSRX91cOy3onIDvrcKg,2499 +torch/include/torch/csrc/api/include/torch/nn/functional/dropout.h,sha256=nzynfMgz_GnEwaBQDkT2RjnoKyeW9b1mBmfUgi46biA,6536 +torch/include/torch/csrc/api/include/torch/nn/functional/embedding.h,sha256=nTcLxrD2Zc3cp6lMpj9G7f5xSsfytFjaP4Vr_F6NOdA,6359 +torch/include/torch/csrc/api/include/torch/nn/functional/fold.h,sha256=A0CNlUWYsvou-oyd59U8Q_tSYnI9GpI64CfRQAxuJEs,2736 +torch/include/torch/csrc/api/include/torch/nn/functional/instancenorm.h,sha256=yyTWU5iiqrjQMolWnUE_XTr-EqkL9C3uufTm7f0sMCc,1555 +torch/include/torch/csrc/api/include/torch/nn/functional/linear.h,sha256=_bhU7IAQexdyh-Y75DqbEpI87AGmLyuWsFy8nRdD_Yc,761 +torch/include/torch/csrc/api/include/torch/nn/functional/loss.h,sha256=OVYtRALMIXHPzk6JPG955diu70R_Q7RByHNIp9MmXNw,31841 +torch/include/torch/csrc/api/include/torch/nn/functional/normalization.h,sha256=yx_utrpj3ypVMEx_svOyPvwdeVV3HBKS9valBMaaSHU,5971 +torch/include/torch/csrc/api/include/torch/nn/functional/padding.h,sha256=7H5UUm7KCn1Fe1-530FCOwVOqBfwJyR_Grbge-mshBc,1674 +torch/include/torch/csrc/api/include/torch/nn/functional/pixelshuffle.h,sha256=ZgJYDlJ4MEyv2wFtpb7Jb0Ql7Cz_TfPiDDOj7P-onVE,1293 +torch/include/torch/csrc/api/include/torch/nn/functional/pooling.h,sha256=1AeBng-rdz2R1wNkjH9AGgOCjVB48d4NjrB7ZFTfcNs,35410 +torch/include/torch/csrc/api/include/torch/nn/functional/upsampling.h,sha256=CfhZim2lvAxw8NWEQonHmjjIdXn3Ok3blOjXlpf_OfM,10728 +torch/include/torch/csrc/api/include/torch/nn/functional/vision.h,sha256=ZQIaO1X3mGAMPSAzvrHKajtq2WIDMts0ashFx5FPtyI,3579 +torch/include/torch/csrc/api/include/torch/nn/init.h,sha256=IrZVVZ4uTNvRJREpmAUTSe4EBOR3w05ikDFwNS5cZ0k,4919 +torch/include/torch/csrc/api/include/torch/nn/module.h,sha256=F8cuCUbtAGYKp4tQZ6VHbNdomW0AK2h9xzfwJvJepkA,26813 +torch/include/torch/csrc/api/include/torch/nn/modules.h,sha256=N-ch-fayJ5OIZEUP2hKxJK26CjIEGUnC2d8BnQWfK2M,1289 +torch/include/torch/csrc/api/include/torch/nn/modules/_functions.h,sha256=Sa9d7UR707bSQx-OB5Oj3Lk5Q8YSUYlGk5AEpJqU430,656 +torch/include/torch/csrc/api/include/torch/nn/modules/activation.h,sha256=O6ZqXUYqYPoIKMBJOHv4k9i480tDT1bsrlkEgfc85dg,30317 +torch/include/torch/csrc/api/include/torch/nn/modules/adaptive.h,sha256=gh-z0UHeh5epO1NDPB7e1pGtNbp-qIeyYOK8BnKRcvA,3517 +torch/include/torch/csrc/api/include/torch/nn/modules/batchnorm.h,sha256=DVrbcofTt8-DkIaq8w-hODim1asT14xiw2ph5Kj2gnI,8225 +torch/include/torch/csrc/api/include/torch/nn/modules/common.h,sha256=LZVT9fW1us1IXybrGnHOB24xnKFX-NBgEUOktcFAAcA,4348 +torch/include/torch/csrc/api/include/torch/nn/modules/container/any.h,sha256=tIrT5BuLd9Q4xVWBXFgdNqugKAif_5OPGZGM9fNi33g,13407 +torch/include/torch/csrc/api/include/torch/nn/modules/container/any_module_holder.h,sha256=N0TP5kWDI5SGYlNvny11-fV0NQVQ7zax_xstRHPyDkI,4996 +torch/include/torch/csrc/api/include/torch/nn/modules/container/any_value.h,sha256=o9JPg4ZNOJ4eibPVL2ECw3cDibTWYHhHjjJpA3e9_C4,4138 +torch/include/torch/csrc/api/include/torch/nn/modules/container/functional.h,sha256=TenXytQBcwSW0c38kDnMkasTI6bERpoJfAK2vumjD78,3343 +torch/include/torch/csrc/api/include/torch/nn/modules/container/moduledict.h,sha256=fBOiDxwEetjfYgBY74v7sBwEWjXkIxoLmcnVdScqdfM,8422 +torch/include/torch/csrc/api/include/torch/nn/modules/container/modulelist.h,sha256=-ogA8KQdcuIZz4MsIF8yD8AnUDa7RCJJ8Hc11fnjNPc,8942 +torch/include/torch/csrc/api/include/torch/nn/modules/container/named_any.h,sha256=gFXdY3K3kbAlhyPSYYz3hj4nctmZF1QAJW2-mQIA9wo,2442 +torch/include/torch/csrc/api/include/torch/nn/modules/container/parameterdict.h,sha256=gRzVzmpcxTeHWpwrUGGjdOVb2eeozcxFR9A0IgwZFcs,4457 +torch/include/torch/csrc/api/include/torch/nn/modules/container/parameterlist.h,sha256=ZSGQNhxDMM5-lJtMVEUb1ZuDOTGrnWEPeR9pVwEn8LY,5577 +torch/include/torch/csrc/api/include/torch/nn/modules/container/sequential.h,sha256=lA5cVmgCXt5DwMWdxYZbxVXtVaXlFAxC-3iT0gbnHOM,13734 +torch/include/torch/csrc/api/include/torch/nn/modules/conv.h,sha256=xvva1RFbMqOKcuEmW8y7573-9cBVWdH6TW9840slBb0,16259 +torch/include/torch/csrc/api/include/torch/nn/modules/distance.h,sha256=qhKhRgaeU0m-eIYJ_JRr6c3w_Hp9aRaftFpH0dVY3rk,3056 +torch/include/torch/csrc/api/include/torch/nn/modules/dropout.h,sha256=ha2pXcHgxbMFqNF7JQ_WqAl5iLZM_refbEYrge-u4Q0,6400 +torch/include/torch/csrc/api/include/torch/nn/modules/embedding.h,sha256=NUupuxmFczcvoUjw4qGjByUyGKnZ3dTTsKbnnp0AnxQ,6055 +torch/include/torch/csrc/api/include/torch/nn/modules/fold.h,sha256=esGBepGrpkQMBvRXSZo36f1UbhAuRuVzpj2hOVIHU0E,2822 +torch/include/torch/csrc/api/include/torch/nn/modules/instancenorm.h,sha256=IENzMqj5lJUoEIGSDyxXKFvSpCtXY7RUg9sWjOnwWjc,5514 +torch/include/torch/csrc/api/include/torch/nn/modules/linear.h,sha256=CJjLF2cpIcd_Fe4B4xbNSq3nytf5M0Hdb-JT6xLTKec,7485 +torch/include/torch/csrc/api/include/torch/nn/modules/loss.h,sha256=1fEYlwh-beM3Jac2QpmGWtorAVc_xkLSXgs8BmBLO_U,30982 +torch/include/torch/csrc/api/include/torch/nn/modules/normalization.h,sha256=wlevBBU2NUMPtg3t-l7jGYDrlLExshRYftZp5lrLLko,6931 +torch/include/torch/csrc/api/include/torch/nn/modules/padding.h,sha256=TnLnPlq5FaiVNunquYB254d7sCCM2hvRSAAGwuwNlTY,14346 +torch/include/torch/csrc/api/include/torch/nn/modules/pixelshuffle.h,sha256=hz0Szo-_gublg-WjfiZRKejPraut7c3ehOMgj8RSfv8,3109 +torch/include/torch/csrc/api/include/torch/nn/modules/pooling.h,sha256=2Lcglk6lHpQvM6CciWsDRuXTPd7BoaTqwwAzwCZAXik,29640 +torch/include/torch/csrc/api/include/torch/nn/modules/rnn.h,sha256=enClCyYr4cyq_OVDSNNoPvG6EdW1L83sOlsW1SjVRZQ,13448 +torch/include/torch/csrc/api/include/torch/nn/modules/transformer.h,sha256=pTTY-61a-KU3Y6NTxpEu3VEaPPwKCQ4cjX0KDEcFUq0,5322 +torch/include/torch/csrc/api/include/torch/nn/modules/transformercoder.h,sha256=0BvRj5KqRYJ288agJl1q40hNHu8WA1PINLRHeGtIKi8,5203 +torch/include/torch/csrc/api/include/torch/nn/modules/transformerlayer.h,sha256=PizoCsVL2RSxsip15by79qDhMer5JNN5ejDl65PhDbk,6407 +torch/include/torch/csrc/api/include/torch/nn/modules/upsampling.h,sha256=KCJCuTB_bXnhB6GvV5pN7ftnZ6K5EIANeY6NrvFSAdE,1619 +torch/include/torch/csrc/api/include/torch/nn/modules/utils.h,sha256=7Q4RIQ2Thvqjk-TFwRBCtAxApCxk6hp62I0199qHoxM,1400 +torch/include/torch/csrc/api/include/torch/nn/options.h,sha256=gilbiVyS93p7hxxyII0HQeKrDG8lVfOafzVF--xe6Jo,645 +torch/include/torch/csrc/api/include/torch/nn/options/activation.h,sha256=lomXpT9puWa8dYeJocW5vYrj_NXNH05mV5o3MZ-Dwys,19019 +torch/include/torch/csrc/api/include/torch/nn/options/adaptive.h,sha256=wlcgQaUQDqQl2eakmr8GgeRdIHEVP0-T9ju_C7Cwx3g,1057 +torch/include/torch/csrc/api/include/torch/nn/options/batchnorm.h,sha256=lWIy_fL1lygw0cIt9DijhbMeljg7t3Z0DAoh3wXoY5k,2759 +torch/include/torch/csrc/api/include/torch/nn/options/conv.h,sha256=umkpP9gB_i6GHdMaWuPgzLuBSycujUZGdxHLnFizRfc,13446 +torch/include/torch/csrc/api/include/torch/nn/options/distance.h,sha256=gNzOxjgCv8DVDA2KSeZtoA8Sx1LvzryteOtDa9NAz_Q,1989 +torch/include/torch/csrc/api/include/torch/nn/options/dropout.h,sha256=OazSYLHM6WTsbm4V37Yq5cbqb38fJclFOpYxBitxR2M,3045 +torch/include/torch/csrc/api/include/torch/nn/options/embedding.h,sha256=JSprIn0fcp7J6VRLaFxaMGiMvYdub9v9imfYxqTGbLg,11642 +torch/include/torch/csrc/api/include/torch/nn/options/fold.h,sha256=z4R8FjzcnUvSpSchux77wbenjkdXlYDiziWpK26yN0w,2913 +torch/include/torch/csrc/api/include/torch/nn/options/instancenorm.h,sha256=uXWUgUWSqKWEPV-Yaxyz_7GhoDdD88pkt8-UeNQVcaU,2296 +torch/include/torch/csrc/api/include/torch/nn/options/linear.h,sha256=Jh3KwwGHlOv5cZSPnIMBcFaKmiFe1MBuU_G4DmFWXJI,2779 +torch/include/torch/csrc/api/include/torch/nn/options/loss.h,sha256=q1k9V-eDJgcXloFdvR9L1n9g4P5m3tumRV4LyiPlRYk,26702 +torch/include/torch/csrc/api/include/torch/nn/options/normalization.h,sha256=cxjL3iVYISFS9pikq4DbAyA3SzvZ7cKg6pyKXdpDbgA,5497 +torch/include/torch/csrc/api/include/torch/nn/options/padding.h,sha256=mKaDSCU-WloH10olE7uxSK0JYRQ7CvSyyIH0-bP59nE,6835 +torch/include/torch/csrc/api/include/torch/nn/options/pixelshuffle.h,sha256=KTyODbPGxpwQbWiBcFXgjnnjFkMpd1IFpRzt1hxSzdo,1632 +torch/include/torch/csrc/api/include/torch/nn/options/pooling.h,sha256=5iOn_BDhid22nsRNvxj0tOcFnPOsN2ATi40umEQ5618,17718 +torch/include/torch/csrc/api/include/torch/nn/options/rnn.h,sha256=Ba_RuMaWdHYjILXdkkIZlXoZAmT3xxqNhKSTQJK9_Ho,8195 +torch/include/torch/csrc/api/include/torch/nn/options/transformer.h,sha256=ZfqxwS4VOAxOd2zNg6evGKES8Hd7ux2kQ3sNJ4ACeNU,1814 +torch/include/torch/csrc/api/include/torch/nn/options/transformercoder.h,sha256=9glBSDeUJ7v5CnAZSn_MLJW7LuTtBamzkbEi_RL7TNY,2319 +torch/include/torch/csrc/api/include/torch/nn/options/transformerlayer.h,sha256=szudWe7my6TVMn1e6Kr5ho2xb4HW8EA1BYap51OQWcI,2059 +torch/include/torch/csrc/api/include/torch/nn/options/upsampling.h,sha256=-nMUEVjGyQTaaUKhmqqaUc7lAeThTAy9mLd3Re9EiC0,4125 +torch/include/torch/csrc/api/include/torch/nn/options/vision.h,sha256=ABAxah8zfpzE9i0FA3V6r1PNbuOKJFKspaV9IJSFvKc,1086 +torch/include/torch/csrc/api/include/torch/nn/parallel/data_parallel.h,sha256=dk6ogtokQAOtz-zjPvvzNXaimKXuAmoiHUUkl5_SGnM,11198 +torch/include/torch/csrc/api/include/torch/nn/pimpl-inl.h,sha256=F-2WQ11FuaWHsDcz31hVCBBD1WKgkaxCXrabd8sclMc,3224 +torch/include/torch/csrc/api/include/torch/nn/pimpl.h,sha256=jWP7CqN2BEMzTGLq_Tput0fK1RKVqCXedxrC6Sh7i5M,6625 +torch/include/torch/csrc/api/include/torch/nn/utils.h,sha256=WCbMNSev0r4C0hIKiE0SK89PEv_fXBGZA3jregGzNNE,131 +torch/include/torch/csrc/api/include/torch/nn/utils/clip_grad.h,sha256=JtOf77FvFV0qf-NClXTdP9aNXQjcFo1qnUCSzR07Pig,4838 +torch/include/torch/csrc/api/include/torch/nn/utils/convert_parameters.h,sha256=rZB0C3GgreaGd001Bmt2o7NhCVqAGxV8L_N0MyFn9Ws,2376 +torch/include/torch/csrc/api/include/torch/nn/utils/rnn.h,sha256=eDB_i5fEuAWC7uV65QoCNH95hSeKlO949JD0dYqmzhs,12793 +torch/include/torch/csrc/api/include/torch/optim.h,sha256=N73RsN8bKIwQiMQ5Syj4MIBBHDHTkb-2aOxb4UuoQbA,394 +torch/include/torch/csrc/api/include/torch/optim/adagrad.h,sha256=uHwa97Dogf19yivAcxI7XdhxAGvas97QcRqwn2G5R-Q,3197 +torch/include/torch/csrc/api/include/torch/optim/adam.h,sha256=cKgG7UOz9SRKe_Z3CB30j99ugNAgQwVa2hJp77sa_Mk,2880 +torch/include/torch/csrc/api/include/torch/optim/adamw.h,sha256=Pc8SU9sjqzPvfdxArtbVkYqje1KbPd7_yCT0duQEQBI,2900 +torch/include/torch/csrc/api/include/torch/optim/lbfgs.h,sha256=ADUzx7SawOxpilib54JgRWqCUe3bn0_TVw8js3gMvco,3445 +torch/include/torch/csrc/api/include/torch/optim/optimizer.h,sha256=tam9bOcndEmGG2Z0k-ULrWnN22MtXcsgGvUcK0BdljY,8126 +torch/include/torch/csrc/api/include/torch/optim/rmsprop.h,sha256=WdzDaJMW508yOGAys2vAEDEkGMRt5V_X-gsBwOC9pgE,2898 +torch/include/torch/csrc/api/include/torch/optim/schedulers/lr_scheduler.h,sha256=WPI5AoI2I0l0hhrGcTrHvgv5qHSWUSPIhguXbiDoIiw,1119 +torch/include/torch/csrc/api/include/torch/optim/schedulers/reduce_on_plateau_scheduler.h,sha256=1Brx1qOStAjtIuqlZCj9DT6QUCnGoJ3ua3BTmXHgZvo,1380 +torch/include/torch/csrc/api/include/torch/optim/schedulers/step_lr.h,sha256=pPBh-2yVyxTEkEjR94oRaeHZnaQpzsjiRUMt-C8p9lA,399 +torch/include/torch/csrc/api/include/torch/optim/serialize.h,sha256=5jQ7c4sT-38I_Vzl_2Cma3uxJvvdKTAfnCZi05rXUWc,12673 +torch/include/torch/csrc/api/include/torch/optim/sgd.h,sha256=Ye-U0ch3462dz4eTiu0b4HWVMgL7YDEwePt_kx41C5I,2622 +torch/include/torch/csrc/api/include/torch/ordered_dict.h,sha256=TxTeA60yZZK17x0dYPPrHXY3rIwwZcSOMYyAm1Usiqg,16268 +torch/include/torch/csrc/api/include/torch/python.h,sha256=R6pEk8TByHyuOqPSlk9qtFAMUTKXLxpH2RWjoNh45zM,9904 +torch/include/torch/csrc/api/include/torch/python/init.h,sha256=32LPmEoDVj8_z8MWAwX_i4Q8GQjhT2v2CH7LCTvnZe0,204 +torch/include/torch/csrc/api/include/torch/serialize.h,sha256=IDE9Nj1gc7itAvC3fPOtydjauYKC7nLkWUi6vecOlLQ,5244 +torch/include/torch/csrc/api/include/torch/serialize/archive.h,sha256=IM8xj82javqyrkZJGayTWjrst7_FKKllzucRwSMrlos,101 +torch/include/torch/csrc/api/include/torch/serialize/input-archive.h,sha256=Vxtp1IEq8u3ARN0YnxXqhIwO7mhIUbwbUTfd_ssh-GE,3955 +torch/include/torch/csrc/api/include/torch/serialize/output-archive.h,sha256=S9Dop4ibVJV4XS8WZVtaY18Tt4CsnNype9TttFGknNI,2289 +torch/include/torch/csrc/api/include/torch/serialize/tensor.h,sha256=gEYAbqPp6R4AH1G7o4OuS-zVPY8osmtguUFfSSUt7jI,432 +torch/include/torch/csrc/api/include/torch/sparse.h,sha256=dyzTnOOmwxZzdMHcEjETw-cdgEZ1zyvy5EGdGQPkx8o,37 +torch/include/torch/csrc/api/include/torch/special.h,sha256=Xgi1ek4hlCJP_RNaa3dXwtHF5O0O1RFdz2qwPMzgy0c,38285 +torch/include/torch/csrc/api/include/torch/torch.h,sha256=VRdG_p1q4Y2gB0LtTmL_C4EpbrPQuvFVa-21l1wc-VU,154 +torch/include/torch/csrc/api/include/torch/types.h,sha256=6YArOeoes8IOlr-cPz_hyzC1YQbrdCniS7Z1Bxjdhz4,2388 +torch/include/torch/csrc/api/include/torch/utils.h,sha256=xJWTSPizKE6MVJUSEl7U42LCtmhMq5rOuAxrF28un5w,3585 +torch/include/torch/csrc/api/include/torch/version.h,sha256=TaK_YYkLErb8y_puB4vzFbPPuy71_xHvCfPKczNth3c,799 +torch/include/torch/csrc/api/include/torch/xpu.h,sha256=Dlrk3eATuICT4j9HpzznnO6T7nPzIoEXlbuUtwstXOU,602 +torch/include/torch/csrc/autograd/FunctionsManual.h,sha256=EpDtpGRfU1YLvD9J7E0-SfzrDKdNuwTAUS3haaWx4uk,33055 +torch/include/torch/csrc/autograd/InferenceMode.h,sha256=hyp9uc-uXPuSoiyVupyje0n6r0_RlcZKQS4xX8eVAWE,156 +torch/include/torch/csrc/autograd/VariableTypeUtils.h,sha256=ba0CpiV7xK5GuS82ImJmUbH40vU2VBPP3CMXCXv1Wck,14567 +torch/include/torch/csrc/autograd/anomaly_mode.h,sha256=QKow8SW7aYxus7-6fXN9AudEDWQo3SuA0DMMYKz-biI,1725 +torch/include/torch/csrc/autograd/autograd.h,sha256=JxafoWW-lhFTK3hrK9xcsq0kwL0lmX0-wdPTlg_cW94,5309 +torch/include/torch/csrc/autograd/autograd_not_implemented_fallback.h,sha256=4uoj4sHweJt--NEkq8qWqEWj2tb6md_kC31TOU1N8os,1142 +torch/include/torch/csrc/autograd/cpp_hook.h,sha256=DUky9TJiVX1VoN46vIboiaQoR5Q9kdu-32VNHrVYM7o,959 +torch/include/torch/csrc/autograd/custom_function.h,sha256=Xia7LFw8dZIJP9SS_Xbk8QykUytgP0uOkpGk7ng9D3w,21353 +torch/include/torch/csrc/autograd/edge.h,sha256=pqMnb2ppiPeiKn61IR_SY2jMpRQpU-OB9Vr7cqZrR6I,1615 +torch/include/torch/csrc/autograd/engine.h,sha256=VB2L5Btt4OO0sml-tl0WPr8zDZQqmAY5h71hyrxvVTI,10805 +torch/include/torch/csrc/autograd/forward_grad.h,sha256=ZNO10XOwB8izHey1V-2Tj8YcFc-UZOH98xTnEgLHBKY,8937 +torch/include/torch/csrc/autograd/function.h,sha256=7NXOpi4DaFc6tHt3g-EC74WrDBj-H-rpyMesbDPCiSM,30656 +torch/include/torch/csrc/autograd/function_hook.h,sha256=4absCzY4zkl726jSEu4WjPGVeUWQBgD3P0io1_aoWWI,2230 +torch/include/torch/csrc/autograd/functions/accumulate_grad.h,sha256=nexBb4hCBBZ1ZYD-A1lEkntrOOmJCcFDZQ9uB1eCaZI,14899 +torch/include/torch/csrc/autograd/functions/basic_ops.h,sha256=hwmDnapdY-3dhqmaeNdRdVrTHMVWgbbw4BlfaCIG3q0,3396 +torch/include/torch/csrc/autograd/functions/comm.h,sha256=sVYyhl0ZLDFX6oDteXgnfsqbmrWs3v8JomZJ5sSN3EY,1197 +torch/include/torch/csrc/autograd/functions/pybind.h,sha256=8TIM3bGfSHfCMQ8XDMMOn8B3hFVCEB3CLGR3OSJvTDQ,380 +torch/include/torch/csrc/autograd/functions/tensor.h,sha256=8AC7pQKj8Xza128aMcYFO_zk3JYXegIpS1lbVPetwMA,7269 +torch/include/torch/csrc/autograd/functions/utils.h,sha256=Pt14NXeIQe8VQKjFEqvWw0mkq0ULwR-cUgwN-odpVWg,3230 +torch/include/torch/csrc/autograd/generated/Functions.h,sha256=QUcMA8L0HnLdxXGmElEMiwn2pZLT2LFPUj2ewOSDSYQ,514901 +torch/include/torch/csrc/autograd/generated/VariableType.h,sha256=htTlCdoFj2wlcBIklDQk3iDV-Z7OjJdYTcQ7zxTkDN8,1505 +torch/include/torch/csrc/autograd/generated/ViewFuncs.h,sha256=9RlokBle3LFaIB1Q6vKR8JDYwIbrI5mDRBXc3Ua_z28,37323 +torch/include/torch/csrc/autograd/generated/python_functions.h,sha256=m2zbP4C796FvQVsVVmsXUCiz8Prj2O-pT55rbHA2NwA,891 +torch/include/torch/csrc/autograd/generated/python_return_types.h,sha256=duqsERucZcEvzjWUHjzvRJejN9GvrIcxhI29Sc7vrgQ,4062 +torch/include/torch/csrc/autograd/generated/variable_factories.h,sha256=PUtgz1L0lKrgRa1B67ylsaXEfDKo9AzzNntiVUUTZYU,56653 +torch/include/torch/csrc/autograd/grad_mode.h,sha256=uzjms9KcKXMdR0-20069M5-4lNYJoO6Dp5IrTlp0040,210 +torch/include/torch/csrc/autograd/graph_task.h,sha256=9n5oEYKjkr9n7LX8k_CqX2XIpi9vmkLKDLOUntPql8I,9376 +torch/include/torch/csrc/autograd/input_buffer.h,sha256=85F-0cJno2vNf7i_LvpVDYFuz_h6V7HF_tZD8wvbpOw,1996 +torch/include/torch/csrc/autograd/input_metadata.h,sha256=c0fYFoqxMeKpEVrT1-eprIezNZw5T01JBKW7g-11tv8,2982 +torch/include/torch/csrc/autograd/jit_decomp_interface.h,sha256=7RTBCOafTnMaQPuMayEbbfVYBOiw7KXF2ODioL9iCKU,1828 +torch/include/torch/csrc/autograd/profiler.h,sha256=6ArK8ksshLYFDODo91Q75_cqzFjn0mNzQ3-Tdefu4y4,112 +torch/include/torch/csrc/autograd/profiler_kineto.h,sha256=-HA8uimdi-dmOocVC3uj7ALKSBdrQCvDvKPfyo2zERM,8185 +torch/include/torch/csrc/autograd/profiler_legacy.h,sha256=A2fG1Sj2zZ7_dBB7y3FFiEtJXl-1GYsmEhNbZwAmmLU,10678 +torch/include/torch/csrc/autograd/profiler_python.h,sha256=8Z3Z8f9uC_BLJzWSJAGsNyhmig7RCpSZm0aYsYzkOZg,84 +torch/include/torch/csrc/autograd/python_anomaly_mode.h,sha256=F5A_qu_hFvsiZrx9efui1qlnqCBmsThAIKVcEh0qOHI,1186 +torch/include/torch/csrc/autograd/python_autograd.h,sha256=jNgjOp6kGrazGqTuB18gDmK0A944oAnnv_3AoZhh9m0,423 +torch/include/torch/csrc/autograd/python_cpp_function.h,sha256=6vF3zTvIxot5rbudsN9BS6CqQVWuxKYc9T9IvR-oMjo,5244 +torch/include/torch/csrc/autograd/python_engine.h,sha256=r4NGZcjGHlyMJMs6cYpxXRSdHLV8ehfhlqluuyYS9U8,1256 +torch/include/torch/csrc/autograd/python_enum_tag.h,sha256=xZjV2TWlkXw5USUrIq0aYpoVoG2mbDy4GXREOnPPkZI,120 +torch/include/torch/csrc/autograd/python_fft_functions.h,sha256=k5wfEJLjP_h8UOQPJ0UiXi0qO61KfDQaf_gTTLCORAg,135 +torch/include/torch/csrc/autograd/python_function.h,sha256=X6UNNUsZfPULGqI2P7AOGdqY7GnMibf7xb_JA0HaDMM,5215 +torch/include/torch/csrc/autograd/python_hook.h,sha256=Y2St2S-28hrxHzzJGuYs6-p0ehXZQBaR39PxPGUTWCo,2067 +torch/include/torch/csrc/autograd/python_legacy_variable.h,sha256=_8tWed3mDIVmtYpI_WPVUpfjK_eH7Qv4wNrSIIlahVQ,257 +torch/include/torch/csrc/autograd/python_linalg_functions.h,sha256=TMytxE664D3K0ZyeP9RDPXwUrGLCtXMQBu35Kw6Qf54,138 +torch/include/torch/csrc/autograd/python_nested_functions.h,sha256=Mt8UQj1eWERpXOQ8Rw-Bklnw406XWsrf25zAQR_dL5A,208 +torch/include/torch/csrc/autograd/python_nn_functions.h,sha256=uZP6q6ylbAtFkyVDSARs3pWmG-gbk-k6fCwG9fDApMA,129 +torch/include/torch/csrc/autograd/python_saved_variable_hooks.h,sha256=bNI59EbQ2N5ED_pfrlYrNbZoT7w72CtJLtPVHCfvm9c,1079 +torch/include/torch/csrc/autograd/python_sparse_functions.h,sha256=Nlusj1cAy0uHqi6JRyCb6bMND4Hg-FikZDnGwF78Ol8,138 +torch/include/torch/csrc/autograd/python_special_functions.h,sha256=4aL-AB82uQgv23ighEjK-D2dDl9jC0DTHAZcFAKX23I,138 +torch/include/torch/csrc/autograd/python_torch_functions.h,sha256=31Zj96tZ3OtMxIiSGum8YcTqhSypxRCStxksgg3dHX0,666 +torch/include/torch/csrc/autograd/python_variable.h,sha256=CZQFs2p_pi6nVz_eKqJuNCzJNp6WNPcAfrqTJhOwcCI,3512 +torch/include/torch/csrc/autograd/python_variable_indexing.h,sha256=MbNxTqIUSgc8hqrafHaWeAjJZ5yWDEO1p3olOF8wijg,2752 +torch/include/torch/csrc/autograd/record_function_ops.h,sha256=A01-BHejveN5ZZj6lO64LiiV7nQhj9XvCXzMMmHomkQ,935 +torch/include/torch/csrc/autograd/saved_variable.h,sha256=vvxqkMtUXtt6X1IVAVrnoOleT8Gk8zsYrhTrYP2ZZzQ,5024 +torch/include/torch/csrc/autograd/saved_variable_hooks.h,sha256=OBeIqdAZkQLlqnZ1FXqTfW3jYNF53dL32HHrNZJ-wQY,547 +torch/include/torch/csrc/autograd/symbolic.h,sha256=XMrwDnlSibXxnqZiy5pNDVwTBDsU8aIDamEg66kOEYk,300 +torch/include/torch/csrc/autograd/utils/error_messages.h,sha256=MDzsCv4cOe1XjppPx3i7fpR7s8VPs0DEpURV5EY50f8,495 +torch/include/torch/csrc/autograd/utils/grad_layout_contract.h,sha256=a5OE0iqU4BwZ7slkuusZm7K6U2KohHkMuq9r8uMNG9Q,2822 +torch/include/torch/csrc/autograd/utils/lambda_post_hook.h,sha256=y5TQfUiZZcUW5Y97uyiDBAB52Z3LBZpK-f29_qxggV4,1406 +torch/include/torch/csrc/autograd/utils/python_arg_parsing.h,sha256=qqap1e7rG3Rv2HmCB2J6Ip1H7xngAlhI72Bgmi1xBYU,1423 +torch/include/torch/csrc/autograd/utils/warnings.h,sha256=e09IIgYcdcmYq2V2l3f3qCKajFgAzhztvsZENTxfQPg,583 +torch/include/torch/csrc/autograd/utils/wrap_outputs.h,sha256=8QXlfTohJUpg8rVh65MK98nZPccu2rvzeYcS34W1gkA,3743 +torch/include/torch/csrc/autograd/variable.h,sha256=yNudqIQeXDpt6vOVtnP6i77-lubSECi1_tJVXtbPsiM,40345 +torch/include/torch/csrc/autograd/variable_info.h,sha256=eiOU9Fck8gFT-TjSrX7vrZqiWIpRwgXrzMRtlYvTOU8,608 +torch/include/torch/csrc/copy_utils.h,sha256=HPNwtV01Xllja6b16GsOFNYlpV0DrHkqsGCiwSYWnVg,1420 +torch/include/torch/csrc/cpu/Module.h,sha256=BbP2k8oU0koFRZfN77V20QMES8EXPmG7tMfdNAv2dyQ,139 +torch/include/torch/csrc/cuda/CUDAPluggableAllocator.h,sha256=s6gxlYNTH9B_1fhyc0sgLMXea04htc7FmLOvknL0uho,7066 +torch/include/torch/csrc/cuda/Event.h,sha256=W549PlUHEdVYiDAf6YnoNCLgyxKOSCnP3KNCLUQfS1k,433 +torch/include/torch/csrc/cuda/GdsFile.h,sha256=aa1a09kNkxsYsY1kKIyPFZ0Y5AnUs2aeMNR-ilKHOZQ,192 +torch/include/torch/csrc/cuda/Module.h,sha256=_TX1fMfrbIuKgQJyt3-ztUnSOeeaeUza39FBTupbjHw,484 +torch/include/torch/csrc/cuda/Stream.h,sha256=Qp6DFebuoMgWE05n4AIfc7pasukINXSwKLrkqRK7TJo,504 +torch/include/torch/csrc/cuda/THCP.h,sha256=X3rjQrL8gLJ3dsxzdhGmrdQkIyGPL9wGE4gfwBFpdf4,223 +torch/include/torch/csrc/cuda/comm.h,sha256=4JXnOwAl3REHI3JkeDpfdfs1GzwzqPi7HG7OprONjbA,1514 +torch/include/torch/csrc/cuda/device_set.h,sha256=8s6LLs4PG1uykInR1FW08Ey32EQP84vgg4rDfPcLmF4,185 +torch/include/torch/csrc/cuda/memory_snapshot.h,sha256=fo0I9V-4s8RUl0rlV2JTEsmv-uz0lpDttGZdHIESADQ,981 +torch/include/torch/csrc/cuda/nccl.h,sha256=KX4r5Awf2g-MWQun5gI9FViPszEapZ79Vaa20NnfnF8,5871 +torch/include/torch/csrc/cuda/python_comm.h,sha256=NVosjKfdUCpw5y2LwuYU-Xj5h5qDMaaRGCH6TJ_wKEo,171 +torch/include/torch/csrc/cuda/python_nccl.h,sha256=yNwoyNpT1EmyYAmk9k7UWpO3aNfErizqV0QVMiFtDts,682 +torch/include/torch/csrc/cuda/utils.h,sha256=XNFhAnfxpXpTbgvIsapzPGppszyrwKddEOc3iap0zws,215 +torch/include/torch/csrc/distributed/autograd/autograd.h,sha256=HtHK3KkwBR-zKH1iAcjRxvgP-kyxWWH-uxR9jpqFZSs,1636 +torch/include/torch/csrc/distributed/autograd/context/container.h,sha256=fOY_uJCMZNZ4Vk_LjDWdqG5pkz6lVFx4k6epKm00bS8,6384 +torch/include/torch/csrc/distributed/autograd/context/context.h,sha256=hBk4uOgBnmMuWPzqR0A9uuT3FieklH4ZLx_AxWCmnEE,6614 +torch/include/torch/csrc/distributed/autograd/engine/dist_engine.h,sha256=UMDIzRyPfAWo8RCbPLgyzzMWBD91YLpwJcJGym48oFE,7418 +torch/include/torch/csrc/distributed/autograd/functions/recvrpc_backward.h,sha256=VSg8g-TJZ4rBEAZgzYGE1qQuZcIdxMC4ExjtRc3t5KM,1662 +torch/include/torch/csrc/distributed/autograd/functions/sendrpc_backward.h,sha256=GfOWzTKw_gmBUk1PZkAmMo_Ixl6QaSezVOdced7M0JQ,1323 +torch/include/torch/csrc/distributed/autograd/python_autograd.h,sha256=z3iH_1832VeGkqU4909x_Gm8v-3-WobPAAylru00EkY,174 +torch/include/torch/csrc/distributed/autograd/rpc_messages/autograd_metadata.h,sha256=oZkfsMYw0kz82-dPzFzVjkLY48bIjB983NWLRzp-riw,700 +torch/include/torch/csrc/distributed/autograd/rpc_messages/cleanup_autograd_context_req.h,sha256=NPdqU8ktnXoqENviBuw8-agYqnDzLtvXr261m9d4jDQ,836 +torch/include/torch/csrc/distributed/autograd/rpc_messages/cleanup_autograd_context_resp.h,sha256=HlRk6_jQEtLvQdytMk1xkdmRDPC6pGxVESm0nAXbHTE,661 +torch/include/torch/csrc/distributed/autograd/rpc_messages/propagate_gradients_req.h,sha256=ZFfctJmiNPS3a7fXI6u6iK0XTZ4Ge1suK6IqLHghkXI,1248 +torch/include/torch/csrc/distributed/autograd/rpc_messages/propagate_gradients_resp.h,sha256=i9D93bVl0oIAtUTuD954d5zEK4varPfuuQ6p8jSdzDM,754 +torch/include/torch/csrc/distributed/autograd/rpc_messages/rpc_with_autograd.h,sha256=3OET3ynLlS_7upuDTK6Kh_ROL1HwFUpcWdgGs62e0FU,3506 +torch/include/torch/csrc/distributed/autograd/rpc_messages/rpc_with_profiling_req.h,sha256=J5x0TnGjPQSpBXNRBZAyCV4YlhMzlJ0XAKy0zJF3dWQ,2510 +torch/include/torch/csrc/distributed/autograd/rpc_messages/rpc_with_profiling_resp.h,sha256=PWskAW0b7aNOQRkuTBWp60pWOoMG1r1Co4AgZtpVZKM,2470 +torch/include/torch/csrc/distributed/autograd/rpc_messages/rref_backward_req.h,sha256=IGLDLeURhlkWFmdufWb7Td5G4aXFZXG6bltvzDPrwsU,1190 +torch/include/torch/csrc/distributed/autograd/rpc_messages/rref_backward_resp.h,sha256=uXRmBhrZ8lORItGM4X_hB26V5HxbMWqLmxRY6KLlpb0,508 +torch/include/torch/csrc/distributed/autograd/utils.h,sha256=o_gSGqxiz5TwmOFf3YUmzdIF7MVr7W3gHhFvAxwS0Ow,2647 +torch/include/torch/csrc/distributed/c10d/Backend.hpp,sha256=rifKxNCNKTwOLoJCMQ5___MILs-S3vfSdENiC8F10eg,14762 +torch/include/torch/csrc/distributed/c10d/Backoff.hpp,sha256=fWJcanSOcWUawhDaHYfx9-P0WxZyb5Xwt06JQrHm8DE,1051 +torch/include/torch/csrc/distributed/c10d/FakeProcessGroup.hpp,sha256=jj0sD1C4dQ9InMzSrfm6axJK1rBfjt90PTOgcWGN4Eo,6611 +torch/include/torch/csrc/distributed/c10d/FileStore.hpp,sha256=OiRJxkPpHg0gpeQ3wPF2BvJgcqWozBXc9OU-WZoo2dE,1539 +torch/include/torch/csrc/distributed/c10d/FlightRecorder.hpp,sha256=h5nzt2_AedJV4ZUMgrglh6lvHCCYDWoua0C4Xe5lNO8,10075 +torch/include/torch/csrc/distributed/c10d/FlightRecorderDetail.hpp,sha256=evsCzgty2OqNTyBMeBqA2lelLwQ8F2ICv-FUX1N1xgU,18130 +torch/include/torch/csrc/distributed/c10d/Functional.hpp,sha256=SNdK6szbRo4KvGr28aSxN6GiAcdpr2MBWKW6ZC5N5Wc,70 +torch/include/torch/csrc/distributed/c10d/GlooDeviceFactory.hpp,sha256=WNajhu75sSnmaDA2OD7J3VdLB-XJxyVW3F22h3Oaiu0,840 +torch/include/torch/csrc/distributed/c10d/GroupRegistry.hpp,sha256=RKAr_YgI343xv1HWH1kZNMIn4XL6ssgxqu8m6Occ6ag,569 +torch/include/torch/csrc/distributed/c10d/HashStore.hpp,sha256=nC9WYkNdiqNFB1cLqr2QRqMWk57bYqzzTkIEeReafME,2184 +torch/include/torch/csrc/distributed/c10d/NCCLUtils.hpp,sha256=oRGrjr1GVCC-4ZpR7IACS7TA0lNI0tIFVoQwFIOgiDQ,16101 +torch/include/torch/csrc/distributed/c10d/NanCheck.hpp,sha256=zlLnH1phdNMzbPDd-R6_Q_ryRhOWtTYYz-I-u6tvzKQ,328 +torch/include/torch/csrc/distributed/c10d/ParamCommsUtils.hpp,sha256=fSaP7UueW-D-WsIwTwEviGVWQf1vETI4FKdvKk64Qd0,8753 +torch/include/torch/csrc/distributed/c10d/PrefixStore.hpp,sha256=x1ENgGpCPdqB8tvintcUed8rUK4WjuvwUtVzuN7p6Jg,2222 +torch/include/torch/csrc/distributed/c10d/ProcessGroup.hpp,sha256=sELRdqRYTHYZtxf6ybnQF09_6nLIinvp3A6X3o3zwtE,35304 +torch/include/torch/csrc/distributed/c10d/ProcessGroupGloo.hpp,sha256=YySvPJP1vftz9dK8lJp-tQjzqeL3UXwSjj9yGjdz8to,15909 +torch/include/torch/csrc/distributed/c10d/ProcessGroupGlooDetail.hpp,sha256=20vrJbwPzDeNQjdNpXkaxZvQbJ90mf05nbFKvOvinEE,21146 +torch/include/torch/csrc/distributed/c10d/ProcessGroupMPI.hpp,sha256=r6yyjd61hPFvtG3ywB0a682NxOCAU96eHfAE7vnaLUo,8774 +torch/include/torch/csrc/distributed/c10d/ProcessGroupNCCL.hpp,sha256=z7hvET6d0OdBbVxqjbbdyGjD-RHerhR15mq-2WnDOjI,54466 +torch/include/torch/csrc/distributed/c10d/ProcessGroupUCC.hpp,sha256=VgYGE14_4HBRuRvgvneCkAHXu6XeHuvdzu3JIP6zK_w,11264 +torch/include/torch/csrc/distributed/c10d/ProcessGroupWrapper.hpp,sha256=WoWJp46RZc8OEI1S_jcOiYC02QAq4VYZhUkTuwHQUNQ,5077 +torch/include/torch/csrc/distributed/c10d/PyProcessGroup.hpp,sha256=nxYd1VM7zd60CeqwG1pZnjlg8KN5jjBRm-BTOdq4cqY,10044 +torch/include/torch/csrc/distributed/c10d/RankLocal.hpp,sha256=3Tl8PE5eivftjaKOdMyehv5mgwWniHaS7ynZT0RXvP8,2287 +torch/include/torch/csrc/distributed/c10d/Store.hpp,sha256=eTLquvW9xYC6XxzL97qmhmrriQ9KxXtmRavonzTldHY,4304 +torch/include/torch/csrc/distributed/c10d/TCPStore.hpp,sha256=3Sqq2Smmhy-Gg-xbqu0-byizH1UvruqmxPG9KYtX5Co,5054 +torch/include/torch/csrc/distributed/c10d/TCPStoreBackend.hpp,sha256=IPl6vQ34btgdMqupmAUaYKgt47to628unFqkFccmcrY,1567 +torch/include/torch/csrc/distributed/c10d/TraceUtils.h,sha256=2ir_cSYrLUUKYiBlK9gQQeEWzaU0y1plMYuF5eajWn0,9086 +torch/include/torch/csrc/distributed/c10d/Types.hpp,sha256=dsXZjW9PA00_5vmML3wRrybZ97qen5rgLJOjpC_sJWk,5016 +torch/include/torch/csrc/distributed/c10d/UCCTracing.hpp,sha256=t3jW1WV_VfbBnKvL-vJscb1D5oZDHiqIqUoO0AjRbw0,2301 +torch/include/torch/csrc/distributed/c10d/UCCUtils.hpp,sha256=zTuO2oVJfZfY1_Af46nWNn4U4PoZADeZI3_kI3RPAVU,6360 +torch/include/torch/csrc/distributed/c10d/UnixSockUtils.hpp,sha256=_kpDQ1OrlMit7seBqBK6jRPDiyDs_S0a2lS4hBlVrPw,550 +torch/include/torch/csrc/distributed/c10d/Utils.hpp,sha256=VaL5i9zBsSeMp3R22HC59DdMt1B2V6AT-CYRHhOG1Rw,23809 +torch/include/torch/csrc/distributed/c10d/WinSockUtils.hpp,sha256=Z1e98jBOcbOUdJo2e9Vzt9wcfkJFV3aW48UERrgSiKQ,541 +torch/include/torch/csrc/distributed/c10d/Work.hpp,sha256=jPEQ5HqOOkcNvbzHQgmNAuPapgAHg7NGGWja6-Rj4Kw,5512 +torch/include/torch/csrc/distributed/c10d/c10d.h,sha256=puTB5irWAY3tlrtidWSjJxZct4CdEqwfg62wGfCyEXI,166 +torch/include/torch/csrc/distributed/c10d/comm.hpp,sha256=bRP4Ub8_Gwto8BiR9YYAwo4S5DY9pxMFxTtAXbh0lFQ,4423 +torch/include/torch/csrc/distributed/c10d/control_collectives/ControlCollectives.hpp,sha256=f3jUR6cqKo6wQng6n6cSKgaRTUnK9gYdMeGxjQtVvAE,1705 +torch/include/torch/csrc/distributed/c10d/control_collectives/StoreCollectives.hpp,sha256=eR64jE3vFioPd5hiVGvOMEMpX-2CZWjrKjfUpQugwC4,1943 +torch/include/torch/csrc/distributed/c10d/control_plane/Handlers.hpp,sha256=M-R9C4JEn1tMQ1lA5gbnUetqx0vYJ95wgRGAGsbthw8,2144 +torch/include/torch/csrc/distributed/c10d/control_plane/WorkerServer.hpp,sha256=3pOGeBtC0SMR7B0spyHDZiV2yzH6N5TCNTcO7Txo4LI,597 +torch/include/torch/csrc/distributed/c10d/cuda/utils.hpp,sha256=NyQCIkZhl5QJose_O42_PVCqZ9wvbGMozkT4yFU0yIk,230 +torch/include/torch/csrc/distributed/c10d/debug.h,sha256=ihvgjPPnKL5CIZCt-b-sG9ZkGQOgR127o9j4JnzCZY0,604 +torch/include/torch/csrc/distributed/c10d/default_comm_hooks.hpp,sha256=yZm5rDul1jeAe4NzXlA3YWBgOLqQBMOQXKhX-7CXAjw,1745 +torch/include/torch/csrc/distributed/c10d/error.h,sha256=P8EBliIuULfykimKqdXHBRrAURSpxXYSDkHLZEgh1dw,1336 +torch/include/torch/csrc/distributed/c10d/exception.h,sha256=4tj-4rI4W4TB_t9-aEbWk4kI44_6NzFOB6gx1sEdL_8,1326 +torch/include/torch/csrc/distributed/c10d/logger.hpp,sha256=HgE79cHAmI043c8P3G6nvDAz3j0zxFKjzq2nxgCDBfA,6421 +torch/include/torch/csrc/distributed/c10d/logging.h,sha256=IUrQ4U-W6dR-WPCQXmc_ev5AkAVtx5m8kz9vKqgdz_k,1830 +torch/include/torch/csrc/distributed/c10d/python_comm_hook.h,sha256=Sha3ttN_vhvHq5xnGs3U5_vsd7gSwVvXHlGO8q4s9Vk,1073 +torch/include/torch/csrc/distributed/c10d/quantization/quantization.h,sha256=P9ZfH6xBrEd93SsGe9qLmW0VC4_l0PSdGKaZ1zLkxpI,455 +torch/include/torch/csrc/distributed/c10d/quantization/quantization_gpu.h,sha256=oN3oh7Kr6_mEtMEmQ-Q0NfgDpRhdyLhVy8HxqT8-CMM,457 +torch/include/torch/csrc/distributed/c10d/quantization/quantization_utils.h,sha256=1cN0r7ac2hKOA0wbIAwRnewZY13loktCxgEKohRajM8,1256 +torch/include/torch/csrc/distributed/c10d/reducer.hpp,sha256=eoklztne_D4DMPBsjLEgKKkC8eeQZuIGP3ZB-rDZyEU,26085 +torch/include/torch/csrc/distributed/c10d/reducer_timer.hpp,sha256=3QMeZJVsX8-LlAyTFaodzGfVXvw4caZXp9XSAsrNBVQ,2384 +torch/include/torch/csrc/distributed/c10d/sequence_num.hpp,sha256=IKhg-9QembBfbfRnSGG1Z81xGWUaoznnd-irr-734LE,1704 +torch/include/torch/csrc/distributed/c10d/socket.h,sha256=C8mPGX_uzpmPClsQCIAtdf3rFi71q8byRTJVsqyf-PQ,2460 +torch/include/torch/csrc/distributed/c10d/socket_fmt.h,sha256=X1s0AK8gfKXNX7LJTrfAg9Zi2p18s-VzDR4tibcDv9M,702 +torch/include/torch/csrc/distributed/c10d/symm_mem/CUDASymmetricMemory-inl.h,sha256=102WY8fIhTSt2wI1CIYS2Y5pcslpOLBVk5AbDhwSQLs,12973 +torch/include/torch/csrc/distributed/c10d/symm_mem/CUDASymmetricMemory.hpp,sha256=gQ2K4uYIVtTZdilamc2BxYSIU5Z67xImH0ijid6IV1Q,3599 +torch/include/torch/csrc/distributed/c10d/symm_mem/CUDASymmetricMemoryTypes.hpp,sha256=FvJXaMqF2NSA0y3cqhHQwUKux8CvjMmuthkzOGTtUaU,363 +torch/include/torch/csrc/distributed/c10d/symm_mem/CUDASymmetricMemoryUtils.hpp,sha256=xRSiHIT5F_XRJDFqlLZ7rzPaKq7f5m8NwMSViqkpcak,2966 +torch/include/torch/csrc/distributed/c10d/symm_mem/DMAConnectivity.hpp,sha256=IXNISSSTcpBKJhQ7XwOmJx1H87HomxtiHowSWmwWkvU,1277 +torch/include/torch/csrc/distributed/c10d/symm_mem/SymmetricMemory.hpp,sha256=M99ux1bwzCBNfM2bLVQ9LYgCA3PznGTbg1sKb8I6Ctc,7354 +torch/include/torch/csrc/distributed/c10d/symm_mem/intra_node_comm.hpp,sha256=LogcbQ0uMsqZJ3ctwNHJJEdqDHTk2JQaS4pD_B2NxzQ,2325 +torch/include/torch/csrc/distributed/rpc/agent_utils.h,sha256=VC8jFr6xIV7In_P92coHUz7A6hEsBgbc54dF8NVeH58,1629 +torch/include/torch/csrc/distributed/rpc/message.h,sha256=7VyOY3JyJuXik-UHAmkzHOFhlaPflHnrG-sCSNLcPJs,7633 +torch/include/torch/csrc/distributed/rpc/metrics/RpcMetricsHandler.h,sha256=zvnEmkaNutGLDJ5ZK6UxGbvAqagO7UjSlkh4bQ6Zrw4,1568 +torch/include/torch/csrc/distributed/rpc/profiler/remote_profiler_manager.h,sha256=n0-Q8-FVBUD_0JDOrc-8XuEB4ABWqbk-LuERV_vCXeU,2226 +torch/include/torch/csrc/distributed/rpc/profiler/server_process_global_profiler.h,sha256=h8gJlHj7HOOZKlQ0uBQ-YG0tFJ5RxslkBBN7RCC4EUU,4399 +torch/include/torch/csrc/distributed/rpc/py_rref.h,sha256=af7XmbpPotCcrJIAnsqiq1fDwiwKc9Ta-OgLk7HooW4,2965 +torch/include/torch/csrc/distributed/rpc/python_call.h,sha256=SuOvYCLjTwRHSCa4OkCBklYpwZedjaalBVPLzvethwE,811 +torch/include/torch/csrc/distributed/rpc/python_functions.h,sha256=l7rvYxyH_FwklGlEXvb4eD4u56HAieoaB8RHHbOFWDc,2257 +torch/include/torch/csrc/distributed/rpc/python_remote_call.h,sha256=Wpn9f0cp5z8A17M8gIivHbx_bd9n-SO-dgtSXBJ2uAQ,1328 +torch/include/torch/csrc/distributed/rpc/python_resp.h,sha256=EruALdG3oMMI9g9D33twsUjWUKbxFmnXUpRs2_MuvXY,621 +torch/include/torch/csrc/distributed/rpc/python_rpc_handler.h,sha256=qI898C_Q5cHWNLnCD0C6hzCI1RjFfJumYGB0oRHxZRo,4954 +torch/include/torch/csrc/distributed/rpc/request_callback.h,sha256=7fB4tEeBfZycq0AeiqjtWILjEPYMgJaWEggGfLFFOHU,1226 +torch/include/torch/csrc/distributed/rpc/request_callback_impl.h,sha256=pQHwpJE6B5HqNKABznjZNSYX28pAeuqXE0x2q6rVJVU,2082 +torch/include/torch/csrc/distributed/rpc/request_callback_no_python.h,sha256=eHwBIITOXD88DkY89MVo9HRZu7qMWsdIHRyinKPP3CI,3904 +torch/include/torch/csrc/distributed/rpc/rpc.h,sha256=gw6OlbaNdLZo4MjtGUi-olfI73hC4MXOoXWWH0USZLQ,164 +torch/include/torch/csrc/distributed/rpc/rpc_agent.h,sha256=sEcfKI5oVc9M7rPXOk6_-WyrEXw2qH0z8K04D10Bfxg,13597 +torch/include/torch/csrc/distributed/rpc/rpc_command_base.h,sha256=XSAUXFKG_5WVxmSzfM4CzwAU0GpSA7vW3suz7Ytm7PA,678 +torch/include/torch/csrc/distributed/rpc/rref_context.h,sha256=40OGmuVib6YY_mxBvDxLsj0sH2GQMHLnjoqBnoRIrRY,15769 +torch/include/torch/csrc/distributed/rpc/rref_impl.h,sha256=mON-2fdQ3qSd-7Qh07E7mB-eG7gjC8yPcBOlIN630Ac,16453 +torch/include/torch/csrc/distributed/rpc/rref_proto.h,sha256=makuZp8gL75MVYHheW4oz_IoZoAhWLIFH0ydQEIdo8s,5407 +torch/include/torch/csrc/distributed/rpc/script_call.h,sha256=wr3c170YK-FOXGhfqu6ocDNz-xABSf6qRQab2Q4Fn6s,2536 +torch/include/torch/csrc/distributed/rpc/script_remote_call.h,sha256=RZjCqd-U9AH2U0PO6hvRsZgkbhncoYW3ZdpAgfASSHE,1743 +torch/include/torch/csrc/distributed/rpc/script_resp.h,sha256=NQ-QeC9G6zkv3WTHH2JI9uqTGb5Qwhod_G46oijzDWI,699 +torch/include/torch/csrc/distributed/rpc/tensorpipe_agent.h,sha256=jn2c2s3lEqBgkbmUJp54WVlho9hdozoWM8mfkWzvWMU,17655 +torch/include/torch/csrc/distributed/rpc/tensorpipe_utils.h,sha256=7M5SGbeV9PF89K_ICDQvgUcD2_mnssvR96XACqqXTvE,4723 +torch/include/torch/csrc/distributed/rpc/testing/faulty_tensorpipe_agent.h,sha256=5TeVtMqt4WCh3Mcii4olH-Gys_OZI5UhlHiEa_bbcXc,3786 +torch/include/torch/csrc/distributed/rpc/testing/testing.h,sha256=i04x0DzmKQk30I28WdgfLge9Y8JAxdZ_91xNS_9H-hw,182 +torch/include/torch/csrc/distributed/rpc/torchscript_functions.h,sha256=Us3yN-yOHImkvRziIV2sizB_pdk70teGG9Z8t42bJBk,1656 +torch/include/torch/csrc/distributed/rpc/types.h,sha256=rerE2C6HOU8VSVgvgTiVGHxb6beB5MQgHwxdXKZPHF0,2199 +torch/include/torch/csrc/distributed/rpc/unpickled_python_call.h,sha256=MKru-ZIYPTM3RUixZCRI0vHF3q6c1kCanosDJjm9UsY,1388 +torch/include/torch/csrc/distributed/rpc/unpickled_python_remote_call.h,sha256=4bPjObXNMf73qJnrW_n1JrW0YX3z_1feutLdAVbo0cg,1207 +torch/include/torch/csrc/distributed/rpc/utils.h,sha256=q7yjho5-wtXLW7GvRBX3cRsW04p2U619p3pmMfdYHOI,3790 +torch/include/torch/csrc/dynamo/cache_entry.h,sha256=R0Uovezx0WbjOhb4sTp8aqY-5PpWSMI_MkjA6w0FOsM,2816 +torch/include/torch/csrc/dynamo/compiled_autograd.h,sha256=NPJ6k1aRvYOQd9WQsfoM8yYURRC3mQHHeTLzXGaYnSU,50359 +torch/include/torch/csrc/dynamo/cpp_shim.h,sha256=6a8TJKtkFG1PUMiE4-CmkRNSmrbHi7IWmiGn49yEb8o,357 +torch/include/torch/csrc/dynamo/cpython_defs.h,sha256=7rY3f4GbyM5IlOfdGN6M_5p2x1uhCA5G8V7eroTw4lQ,950 +torch/include/torch/csrc/dynamo/cpython_includes.h,sha256=_JvYiestJl25tmHhCGt0RvvbisHMHkbRm-JDHKM4dXQ,1014 +torch/include/torch/csrc/dynamo/debug_macros.h,sha256=vJ08HsTwa1z2g9KSKfSUJSwMoDw4-gZWZxMhBhqsIIg,3528 +torch/include/torch/csrc/dynamo/eval_frame.h,sha256=-43fB5nQ0ZMkBcX4wDNYQ-ExeXJp0rfTyql_ZBk63IQ,1487 +torch/include/torch/csrc/dynamo/eval_frame_cpp.h,sha256=RHm24C4s3bY4mg2zvyX5m9D1lqtK9-ta0dSMVtJ1cSc,471 +torch/include/torch/csrc/dynamo/extra_state.h,sha256=cbAQuQjqWniS2_T6iU_b0KpIgk0vn9l0nsf8Y4aTS1M,6319 +torch/include/torch/csrc/dynamo/framelocals_mapping.h,sha256=npquwVC_dhGx6QsxOOGOxISYSvK-bA3iJ_yP5LADTB0,2781 +torch/include/torch/csrc/dynamo/guards.h,sha256=peA0N_ZPFOyy9NY5BgOTsBxzAsBLPQz19JcE--80YDg,3014 +torch/include/torch/csrc/dynamo/init.h,sha256=Z7mSy66v0be8l9dl_jSqHqxaK8Qqqxj0lnv_sbHSA1Y,187 +torch/include/torch/csrc/dynamo/python_compiled_autograd.h,sha256=dFWRhXaGLZEKOxIRw_8aJK_qTrhZkEf8iUOAmg2a7PU,215 +torch/include/torch/csrc/dynamo/utils.h,sha256=u3d9P9HoSgcqIikwFQcT--wyHY3Fa0KykZBAT48u1Pk,518 +torch/include/torch/csrc/export/pt2_archive_constants.h,sha256=lBDRujYMMnqpTIfcPeFdBlkCojnT_q1RmX654VzCeFY,4268 +torch/include/torch/csrc/export/pybind.h,sha256=zq80LK7ZE936q3A6I-HuPmMVzfrQWzN5ivv06cUtd2M,142 +torch/include/torch/csrc/functorch/init.h,sha256=8IYkj8cpkNbuRRkdo31n1sw5KmR2i0wqkNNci3xGswo,106 +torch/include/torch/csrc/fx/node.h,sha256=ijSj7HQLYfWez3YETPTqTtH8Fv_dP7AouvVJt652CBI,130 +torch/include/torch/csrc/inductor/aoti_eager/kernel_holder.h,sha256=l-JENzU737dAX4eNIo9_R-RDaFRdpOHFgGi3Hbbii2g,4113 +torch/include/torch/csrc/inductor/aoti_eager/kernel_meta_info.h,sha256=apY_-qeXBaoFeeUcpvNMST9M8gO19_lj9oSsU3WNIlY,5712 +torch/include/torch/csrc/inductor/aoti_include/array_ref.h,sha256=islmDRN9j_-N0c4rzZce-2euuWxU1P8d3KoKv3AiRVo,301 +torch/include/torch/csrc/inductor/aoti_include/common.h,sha256=v8xWu4ep5ghdbamDQrC1zq0LnnUEWzoJM2vnHfBTNCg,416 +torch/include/torch/csrc/inductor/aoti_include/cpu.h,sha256=IKBUM6DSrja9rrVs5wB46Ou2f6PzA_JJcLJmVIUCUXI,132 +torch/include/torch/csrc/inductor/aoti_include/cuda.h,sha256=0hgItuJptuQsIGzy7bCnkueQ17AvPcnWBYmOqe6KIMk,133 +torch/include/torch/csrc/inductor/aoti_include/mps.h,sha256=nIHHEJsYrEaNZMnBde3VyIrqeYMEFTZtjYnI6LF2WVk,132 +torch/include/torch/csrc/inductor/aoti_include/xpu.h,sha256=4Jdx7-itpquLdxRysUqSFzgjWue9G46YR93ofAUfumU,132 +torch/include/torch/csrc/inductor/aoti_package/model_package_loader.h,sha256=sCirmAXj-E2gsgs0gFCao7sNV3yDyRZsrUCmBbFBiT8,1641 +torch/include/torch/csrc/inductor/aoti_package/pybind.h,sha256=y1LFmaxCFJaGisdG0-MhodDzmJdKKKur5Myx4y1hhC4,149 +torch/include/torch/csrc/inductor/aoti_runner/model_container_runner.h,sha256=E-F1wLaJMU0kyyUQYDcE9ZlfcvLrbcT5ap5QnBEhhr4,5168 +torch/include/torch/csrc/inductor/aoti_runner/model_container_runner_cpu.h,sha256=qE_mNe1fZ5s40paLRypczzyLcxRrDKDW1xrPPwlafsc,479 +torch/include/torch/csrc/inductor/aoti_runner/model_container_runner_cuda.h,sha256=khTNFm_lZ-E0gJMy9VXje2_S5c_NZOCD501VOvK-PZQ,1131 +torch/include/torch/csrc/inductor/aoti_runner/model_container_runner_mps.h,sha256=7UMW60_z_3exm8tJ6Gru2DrDN5GFA4XyN2lGIggA-oA,456 +torch/include/torch/csrc/inductor/aoti_runner/model_container_runner_xpu.h,sha256=VlJR-TrGUUMYeEkbHA9p2oxhAiE0wWl3cxAJPGbfVVQ,1216 +torch/include/torch/csrc/inductor/aoti_runner/pybind.h,sha256=9qh4-fASrKhHGajRPRFIPbyarL0evSWaK-LeL2HaZf8,148 +torch/include/torch/csrc/inductor/aoti_runtime/arrayref_tensor.h,sha256=Ak3gQr8wL15DKraT713HbTf8APdcKkdbW4IR4NcFnFU,6256 +torch/include/torch/csrc/inductor/aoti_runtime/constant_type.h,sha256=763SBAPL8VJS-akLWUq0n6__DPjY7HixYRVViuuVeA8,527 +torch/include/torch/csrc/inductor/aoti_runtime/device_utils.h,sha256=FdLHTy02LgMh9oxfCzMoq30H28ghIFQy5yFgLHUKrCo,2320 +torch/include/torch/csrc/inductor/aoti_runtime/interface.h,sha256=2lA_MFTV9vYDHO8uuIcQSQI24gepCYTqnjGcHiWq43o,9682 +torch/include/torch/csrc/inductor/aoti_runtime/mini_array_ref.h,sha256=23W39e7sN7YWmU9Lnbydh6NZYbtJrj7_jw7UHhv0uAE,4726 +torch/include/torch/csrc/inductor/aoti_runtime/model.h,sha256=Rl-O85isiRmnKzrmkLZ327lOKbadLDyaC7m8zYaNrp0,23918 +torch/include/torch/csrc/inductor/aoti_runtime/model_container.h,sha256=nkb-dPcPBMcnwfPGhjwMJJctLFNVBoGQMh7-vp0aA5E,27275 +torch/include/torch/csrc/inductor/aoti_runtime/scalar_to_tensor.h,sha256=LnBq4XZpuoIBqfR4EAN_UErnxBkssWVmAjjrxDQ6Kds,1590 +torch/include/torch/csrc/inductor/aoti_runtime/sycl_runtime_wrappers.h,sha256=B38gbpPDzUCmxQoAHms1WjFQT5iqoT17gbdDsFrVkxw,5967 +torch/include/torch/csrc/inductor/aoti_runtime/thread_local.h,sha256=phSPZpqErYj8d4X0gWL73yCSN3_8Q7X9qnpUm1anArk,4352 +torch/include/torch/csrc/inductor/aoti_runtime/utils.h,sha256=TsYCPtdxdLC4MeTRtH4T-5U5hPIBvjSOz8GSz40wkso,10544 +torch/include/torch/csrc/inductor/aoti_runtime/utils_cuda.h,sha256=HmNmOMfpnoDSU33IuPaiOqQrCC3bwwJzKElLskBvSE8,1825 +torch/include/torch/csrc/inductor/aoti_runtime/utils_xpu.h,sha256=cCJDRFTPWsBlBVMBAXn0abSU68zl5zzsE1NedtQ4sHo,1715 +torch/include/torch/csrc/inductor/aoti_torch/c/shim.h,sha256=cmj-1cSVSILr_z_aZ_w97v3lqjWuL_bps1EMhjZLqmk,29487 +torch/include/torch/csrc/inductor/aoti_torch/c/shim_cpu.h,sha256=AukPIj7bfglI6cwCnWP9l4wsrhdWDPbl1SJzE9iLYVQ,6979 +torch/include/torch/csrc/inductor/aoti_torch/c/shim_mps.h,sha256=5MQHXVLNQz6UnlO2ulTloe0jeDilFum0BMXZdvk-jfM,957 +torch/include/torch/csrc/inductor/aoti_torch/c/shim_xpu.h,sha256=WfkwSSX-oiTE7jx19AcHpVPPNDlFP_ne8yxvXExYD5Q,3101 +torch/include/torch/csrc/inductor/aoti_torch/generated/c_shim_cpu.h,sha256=y2vGxr132HAzVsJEYfgyI65zIyO8lvT_EYNUcChPuU4,30534 +torch/include/torch/csrc/inductor/aoti_torch/generated/c_shim_cuda.h,sha256=wwKSSGJevIwLBIW-9pHB8n8_4es_yDokKRveFlSht5c,35149 +torch/include/torch/csrc/inductor/aoti_torch/generated/c_shim_mps.h,sha256=Jed5AmjGlRNmi8a7kz4-Js-Sb6VTxwRo450inssrc2o,20410 +torch/include/torch/csrc/inductor/aoti_torch/generated/c_shim_xpu.h,sha256=d8J5g2d4DadVydoVWV3EGmwpYxi-1pHc3kIwem9Q8QM,10909 +torch/include/torch/csrc/inductor/aoti_torch/mkldnn_tensor.h,sha256=OkME3dFfr67cGRDB7702KRUIA1T_edv6_pd-50nb2Ps,371 +torch/include/torch/csrc/inductor/aoti_torch/oss_proxy_executor.h,sha256=C_KYpwrE9Y0598qd_BT1ZjJWSqq-swiQMLD7cEqGUIc,4639 +torch/include/torch/csrc/inductor/aoti_torch/proxy_executor.h,sha256=N1_JXylxJjB8P7m1qQ95yn2zkpeXc0hy4l4EXOginmQ,857 +torch/include/torch/csrc/inductor/aoti_torch/tensor_converter.h,sha256=u3u4ugRQozmZIJ_5m80JwdbCADCUjogbO4hEGNzhn8M,953 +torch/include/torch/csrc/inductor/aoti_torch/utils.h,sha256=abXcQ-qjhTyRJEzSHOBvlSA1qa9ftL8pK0Mxb_Pt8YY,7301 +torch/include/torch/csrc/inductor/array_ref_impl.h,sha256=Mvn8uXHRIF35ZRPt8NwRrWygfNL0kNEw5mmSqYUshu4,2992 +torch/include/torch/csrc/inductor/cpp_prefix.h,sha256=BmZsXvI6Dskv2d3dwCW1Dy0q0gCs-gxx90enrDJkcto,35593 +torch/include/torch/csrc/inductor/cpp_wrapper/array_ref.h,sha256=AN1yhD3IeQt4r072DmfFNzVKXuHbBLXveSGQuHgKBck,300 +torch/include/torch/csrc/inductor/cpp_wrapper/common.h,sha256=XifjebR0TL1NXVB39arSMhv0IBRQSKDO556iLsqyBG0,1961 +torch/include/torch/csrc/inductor/cpp_wrapper/cpu.h,sha256=Zz-RDTQRZR_tabpM9cMcurf3iXKxPwqEvG_JQ2V9oSo,131 +torch/include/torch/csrc/inductor/cpp_wrapper/cuda.h,sha256=DtE9VUEWBYmgV-V-yjZI1Bub5cao_ffuvMHnd3Faa8o,132 +torch/include/torch/csrc/inductor/cpp_wrapper/device_internal/cpu.h,sha256=j2h5BNjf9HHYvutBQP5hzh8pOiSVOgiply1x3NeUmts,79 +torch/include/torch/csrc/inductor/cpp_wrapper/device_internal/cuda.h,sha256=3ourNQok-G-G3dtXxO64SjOj2a5Tvm1oKPf_jSWJwFQ,137 +torch/include/torch/csrc/inductor/cpp_wrapper/device_internal/mps.h,sha256=cRYQemuP7SUMbtIWYyeFgul_OZS3H4h8qCp0Yf5TMqI,134 +torch/include/torch/csrc/inductor/cpp_wrapper/device_internal/xpu.h,sha256=2R1vZJ8SNxzCdbZNBNK1myAmKrJ34xOzAzbfL4tz37o,203 +torch/include/torch/csrc/inductor/cpp_wrapper/mps.h,sha256=Y3bk6XltqaQVQheseQ-IvIKp-VKzB7dhrtxNDHaJQkY,131 +torch/include/torch/csrc/inductor/cpp_wrapper/xpu.h,sha256=_VdqJe4pdUl_CimO_AX8LxGzOlI-29sVruLdbZxmPNc,131 +torch/include/torch/csrc/inductor/inductor_ops.h,sha256=HvweZA2MvxHR5BHI7YbsAvHN0ZEOaCvKJYTjEpdwdOU,1095 +torch/include/torch/csrc/inductor/static_cuda_launcher.h,sha256=yTcF7Q3lFeK4l2gfpIPmI8BV6gA4_cfBcS2SHLyN3J0,218 +torch/include/torch/csrc/instruction_counter/Module.h,sha256=YKI8ppqeYhkOi9enpFt1A5xZfsk-FVOGcGKjDoNfXfU,171 +torch/include/torch/csrc/itt.h,sha256=s_2CHgMGZHPWE3-e_nbgJwUxmy2DQEjbolV1dLP-Djc,181 +torch/include/torch/csrc/itt_wrapper.h,sha256=eCw9u1gl7vzf92sSxqO7WrEc0rhmVBW5cydUc2r_c3k,320 +torch/include/torch/csrc/jit/api/compilation_unit.h,sha256=ryxxGOE6ETMue0G7ZXT6szqwZs4Is9mLw2cafiyKR-w,11696 +torch/include/torch/csrc/jit/api/function_impl.h,sha256=zl3_DtrbL9aIEGcWvpWOZ9qvPlVRBw6aQUWu7WuL-Bc,5702 +torch/include/torch/csrc/jit/api/method.h,sha256=sS0T0BQZ4ZZiO0lGh5xdmsW_Q3vIFaGUNWH0O8770BA,2370 +torch/include/torch/csrc/jit/api/module.h,sha256=Ny5cZ9jFBj4Nphl9KOnh8euKjZCXA-GdjRX1eVvwOX4,23481 +torch/include/torch/csrc/jit/api/object.h,sha256=_p-qHNmzVzm0dPL57jHlNOLLQhyqmfS3gpYLhKNNnY0,6083 +torch/include/torch/csrc/jit/backends/backend.h,sha256=B657kJd0Vfpe3QYPRTRf0OA6lNbhOmG8_-THnlbw9wg,3832 +torch/include/torch/csrc/jit/backends/backend_debug_handler.h,sha256=EA_L4fknUQm93h7Pq6qdNd4i8RRZ0U3g1CI5jWuK_Jo,6331 +torch/include/torch/csrc/jit/backends/backend_debug_info.h,sha256=q2POOUEbpSJr9LCc3KaprJ9M9Vd_6Dnfi_pmE8G2jBw,2313 +torch/include/torch/csrc/jit/backends/backend_detail.h,sha256=XPwaX9LD5gBljdLcY20lU4g2AVbjxWsbQ-0zQ426PnE,1079 +torch/include/torch/csrc/jit/backends/backend_exception.h,sha256=CMGaroP-uO4-Ai8cngw39yH3c6TE7JVcveB3FsTZoTo,2116 +torch/include/torch/csrc/jit/backends/backend_init.h,sha256=kGSSfSDo_CZHFidzO7i3hGs2wpPbEOs6YfdLntnZImI,252 +torch/include/torch/csrc/jit/backends/backend_interface.h,sha256=XN8NlxwAhGEzCb_YnWhg4MaQWxOAntNCLYNobSsUwuY,1159 +torch/include/torch/csrc/jit/backends/backend_preprocess.h,sha256=HhmzQn4EjAVB9DcBj8c4cR0rPopA4PUr5mJwlJaXMCs,413 +torch/include/torch/csrc/jit/backends/backend_resolver.h,sha256=dEM9SJ4XGxznQkFOJuMbjN2R2Sz4EXdmBWD3RCGy1EM,252 +torch/include/torch/csrc/jit/backends/coreml/cpp/context.h,sha256=ll7E-LNobNo8L8RCnzEQ6PIh7WoTVvUyQQ8SmmXeipc,438 +torch/include/torch/csrc/jit/backends/coreml/objc/PTMCoreMLCompiler.h,sha256=PAVSDvSAaOf6ZODsvYoHy1kG0DjInrgkX5HtQSah8gY,533 +torch/include/torch/csrc/jit/backends/coreml/objc/PTMCoreMLExecutor.h,sha256=1FSNFw2YQq8DpM8Pnf3_e_BWhcgmmcmAim-GRPZFR9Y,414 +torch/include/torch/csrc/jit/backends/coreml/objc/PTMCoreMLFeatureProvider.h,sha256=ooifFh1UnGxqc8Mh9PwxGs3TlsDjVJ5BTjbSM5rUsNk,350 +torch/include/torch/csrc/jit/backends/coreml/objc/PTMCoreMLModelWrapper.h,sha256=SeoHdbfrqVnPIFqLzuY0OuxPlnwXV3FiVhG4ch4YWc4,922 +torch/include/torch/csrc/jit/backends/coreml/objc/PTMCoreMLTensorSpec.h,sha256=N4lNQj-kfGQvme5thx5Ft7a7qoM1UQgV6Xjeh3LsTM8,648 +torch/include/torch/csrc/jit/backends/xnnpack/compiler/xnn_compiler.h,sha256=5cQYhMyRvyDnUAuTBgTcdFgDPxQA04tCCSG7d4x4JGM,782 +torch/include/torch/csrc/jit/backends/xnnpack/executor/xnn_executor.h,sha256=K_PNsAxmjM4OZ8nuwRDEQ2seqSzgkaeO5cR8AjdFcQ0,1567 +torch/include/torch/csrc/jit/backends/xnnpack/serialization/serializer.h,sha256=rQOafBJkmEZfe6tIrK9jd3TuYnH3D1VBiP3eY0mXLOw,2779 +torch/include/torch/csrc/jit/backends/xnnpack/xnnpack_graph_builder.h,sha256=5DiPjmxKHPBoCiKl9eas_lNPzCvAEoWtO6XqVIWnYyk,3235 +torch/include/torch/csrc/jit/codegen/cuda/interface.h,sha256=B3-y5TtBeJwIU5KNcp3ME4dtooeQ1dd0EZS4gEQKS_8,1854 +torch/include/torch/csrc/jit/codegen/fuser/arg_spec.h,sha256=lqI018FKRbKG7r3q8hxvBBSpK5AmqqK-quAgzd6j_lk,1353 +torch/include/torch/csrc/jit/codegen/fuser/codegen.h,sha256=1j5pY8NNAaWDNENlSkehdQtC6oCW8xcLha8k6fXjn-k,745 +torch/include/torch/csrc/jit/codegen/fuser/compiler.h,sha256=qKnwgd95OzlN7onkgks_8QrBIj7_qRUS5Qkz7AemXVk,1834 +torch/include/torch/csrc/jit/codegen/fuser/cpu/fused_kernel.h,sha256=JjQBjvXp81Vd8pqPG7A41KcLCiclGcuMiZfPPaCWsvM,999 +torch/include/torch/csrc/jit/codegen/fuser/cpu/resource_strings.h,sha256=f89GIIuFB3SB060v-mlENfxTF6d5HhcCar611OAcyOw,2278 +torch/include/torch/csrc/jit/codegen/fuser/cpu/temp_file.h,sha256=BNfD6DMymKD0EvInQKrklR3szgzFZCT1fLMU027lMgc,2891 +torch/include/torch/csrc/jit/codegen/fuser/cuda/fused_kernel.h,sha256=vd5ltr8pEYjOvQeUlplJMxpMszN5PIyxUNzkOaA9LB0,1511 +torch/include/torch/csrc/jit/codegen/fuser/cuda/resource_strings.h,sha256=E1L2lDjysfAARn0LMdY50LOdeZ2fpIqBqx5gUx18VZ8,10748 +torch/include/torch/csrc/jit/codegen/fuser/executor.h,sha256=VWSQ2pKCRYK0ZxboB1SJugSajgb60_lprVmd8LX89LM,500 +torch/include/torch/csrc/jit/codegen/fuser/fallback.h,sha256=wHm80jeSN7YBdq-nq6eTw09xMPSHV66GZWJbEsgk3OI,174 +torch/include/torch/csrc/jit/codegen/fuser/fused_kernel.h,sha256=oJOE0xW4QiOvFrHZCvGBivvwHmwsa-6zGm5LR88mwvk,3305 +torch/include/torch/csrc/jit/codegen/fuser/interface.h,sha256=y9BPxKMo0ZaIDv07_1SuEFJLLGsgcydufhkx6wg14ZM,1722 +torch/include/torch/csrc/jit/codegen/fuser/kernel_cache.h,sha256=LsPmfUq-NZNtxQw-806XyVkK-_NWnqQ2rgnZRwQgvUQ,994 +torch/include/torch/csrc/jit/codegen/fuser/kernel_spec.h,sha256=GlF7x7kxKCZQJw4JZb84so3pVy0nvPew8KePoRelfno,4401 +torch/include/torch/csrc/jit/codegen/fuser/partition_desc.h,sha256=XjoP5B09rOpQ2dRyBEpDPwjziknmZckpgcpCp_iYhiM,1755 +torch/include/torch/csrc/jit/codegen/fuser/tensor_desc.h,sha256=FVJKhJSZaad1h7FE3GiBxdVKUNXFnTApi583jB-CYX4,2701 +torch/include/torch/csrc/jit/codegen/fuser/tensor_info.h,sha256=3u2MbJ96mCBdg0x1XBRFa-ijFwVcBSWTJgiyUz74D10,536 +torch/include/torch/csrc/jit/codegen/onednn/LlgaTensorImpl.h,sha256=ie1UwIpPRc_SeI7knBrXs3I2EYr3kJIXrPQV3bneyLE,7690 +torch/include/torch/csrc/jit/codegen/onednn/decompose_silu.h,sha256=OltITocFw9-qMTTj5tKaLYdgMjUbckwkEw3HZB1thQM,188 +torch/include/torch/csrc/jit/codegen/onednn/defer_size_check.h,sha256=-UKZNxJKVC3kYjd7RCqg7Ex4LBOVUmtO8XJjtfCkzGc,182 +torch/include/torch/csrc/jit/codegen/onednn/graph_fuser.h,sha256=XWP-ytsX1lWxMhxZ-dqOF4KLkSuR1h9eHp1d6TuSmUA,1216 +torch/include/torch/csrc/jit/codegen/onednn/graph_helper.h,sha256=SULzJ4gWglgonuzituYBHic2a3SpyTw8rrFfSJQ4o10,2464 +torch/include/torch/csrc/jit/codegen/onednn/guard_shape.h,sha256=5NLVbFaijhaq9ODg0YjalyiLmw0jXPPH581j_SDkR2s,184 +torch/include/torch/csrc/jit/codegen/onednn/interface.h,sha256=aazwqUvjZ40-2eq8SuMiCVDWLmBjw-t0cTLjPUav-lI,1398 +torch/include/torch/csrc/jit/codegen/onednn/kernel.h,sha256=L8pH2yQBXaKOsPSupArfLrCyBiEFJcUYcJ0F2MYdvVk,2694 +torch/include/torch/csrc/jit/codegen/onednn/layout_propagation.h,sha256=SJJM9dRi-XrqK3tPX5OYcnkxs4jX2DVFwfs1hnrLB3c,189 +torch/include/torch/csrc/jit/codegen/onednn/operator.h,sha256=d4J-qT1fYSWHGf0QnzY4fX0E7rtM7qd69o62OWPKPBM,3922 +torch/include/torch/csrc/jit/codegen/onednn/prepare_binary.h,sha256=c8oQB2IWQnUzFaLtMQn2_562rKH0iYDEEKzyyXP6P3M,499 +torch/include/torch/csrc/jit/cuda/cuda.h,sha256=NQCKkEUfjUjZBrPIVhvzSyIqs2PStwNF9i5O4T_ZpqI,5157 +torch/include/torch/csrc/jit/frontend/builtin_functions.h,sha256=N9lLDDymkcH6Da5WNOcr7RL-gu-yv4y2u56n1tLwLOw,215 +torch/include/torch/csrc/jit/frontend/canonicalize_modified_loop.h,sha256=FK3cjZ4Dp4FwFgHDW6XN7GfYgeIv2L_Z2P4V572Ew4w,287 +torch/include/torch/csrc/jit/frontend/concrete_module_type.h,sha256=5cH-x2pChi5b8zJW-47IkwNNA3kxxqdE92zXIs0JPPM,9037 +torch/include/torch/csrc/jit/frontend/convert_to_ssa.h,sha256=lbJSrtkGjBiipyC7GDSd8a4iw_TDOE4aC0nhXpXU0hs,302 +torch/include/torch/csrc/jit/frontend/edit_distance.h,sha256=JCUWjpEFwXDYl48nOdCMyZ_3IGUP2M9LoCjYvc3Azes,229 +torch/include/torch/csrc/jit/frontend/error_report.h,sha256=KYK4IrnyVl9-dEakj1Ap153X6XHq_KSAQ-ZAuZxJcVk,1436 +torch/include/torch/csrc/jit/frontend/exit_transforms.h,sha256=S0lwPjF5_06Niw5HYQVnhMwsv-c0ahnflhPGzPrMdbw,193 +torch/include/torch/csrc/jit/frontend/function_schema_parser.h,sha256=rGadv7t5fd1mmK3tIJWdCUqHJYtJh8krExvzyab2QnE,804 +torch/include/torch/csrc/jit/frontend/inline_loop_condition.h,sha256=jctM26T7Cxk6xtthR8arllVR0H-msVzR6jcQ5WKX5Nc,327 +torch/include/torch/csrc/jit/frontend/ir_emitter.h,sha256=bWP7TwoYtdRDx4AI4e8jeBGTwOxsYdSmnAja4NioybE,516 +torch/include/torch/csrc/jit/frontend/lexer.h,sha256=4nR_xhRHBhARS7J4aKDa4HvZn7841OPAmFtLzyAD5gI,19014 +torch/include/torch/csrc/jit/frontend/mini_environment.h,sha256=crwmw6iPa-TzpfsL7u0ylAal7Kj8EZkOx1pxGLGqRWg,1375 +torch/include/torch/csrc/jit/frontend/name_mangler.h,sha256=zAYp8zADBY1NzqeBU5GQY3Bgx9w-vJu1d6OMT8ZFZQA,630 +torch/include/torch/csrc/jit/frontend/parse_string_literal.h,sha256=qn2aHuN4QGPl9aT9SmJ3NIYzvGvJKNkFbT6Jaikm4Fg,2294 +torch/include/torch/csrc/jit/frontend/parser.h,sha256=7r8AmpY3GJrlv_czDl1WGF-cJbeQW-RjnwxRp5dhC8I,649 +torch/include/torch/csrc/jit/frontend/parser_constants.h,sha256=iagJK7k0P6LkEVclwfNweHuCS4ylQf-VKjD-yO87lPI,151 +torch/include/torch/csrc/jit/frontend/resolver.h,sha256=g9m8n-yNKYP4YErU2WJlknjp4h-jFY9mcNeCeZpWUjg,1957 +torch/include/torch/csrc/jit/frontend/schema_matching.h,sha256=Nq5izZL-h6NS2AUXSEd30-gWwv3xMjEQs36AaYCy6-8,2108 +torch/include/torch/csrc/jit/frontend/schema_type_parser.h,sha256=hfRMpl6HdaN-pFVW0kccuHdcULUPGFWfcWrscz65dso,1201 +torch/include/torch/csrc/jit/frontend/script_type_parser.h,sha256=P9YeUXXqqkTbnBXFUOpxKtY2hfOqd4x2mwV1DPxwDr8,1580 +torch/include/torch/csrc/jit/frontend/source_range.h,sha256=4_CisTybLppwMQFpyPVGXCWzLtTgJap8y6kIu8pLV2c,16913 +torch/include/torch/csrc/jit/frontend/source_ref.h,sha256=VgA3KBnZ7lm8xNVcgTugwBQEoIfQrxL-y2Kd1ulfSVo,1288 +torch/include/torch/csrc/jit/frontend/strtod.h,sha256=Vpx33Po6jFb8YysqU5b64P-1Y6HBKvuuhdgRZz9dD9A,216 +torch/include/torch/csrc/jit/frontend/sugared_value.h,sha256=IV_SuBEYCOWWnL9WXuYUBjeo26KXQz7ZWW-bHCna_uU,27897 +torch/include/torch/csrc/jit/frontend/tracer.h,sha256=zxLcxDbHFz48xrfPoK5aXIR3qtDIB8MBNkV8q2D5CAE,12790 +torch/include/torch/csrc/jit/frontend/tree.h,sha256=2BoWIu756sTFTqrdcZgUSSKprkRnEBshBOEkb2ywVc8,6599 +torch/include/torch/csrc/jit/frontend/tree_views.h,sha256=ZLqXbBe-WDj8xxA7q5E7Y4PTHq0BCL3w5RstjaBm77U,37200 +torch/include/torch/csrc/jit/frontend/versioned_symbols.h,sha256=tLssSRG7fdJ4BgcZ2sUqSUBOYY-TNYbZXx1bNjCiGhY,595 +torch/include/torch/csrc/jit/ir/alias_analysis.h,sha256=nS6EX-R4iVGxSq9M7dfvTy5mELwuucwTFT7yeI7gTwE,14289 +torch/include/torch/csrc/jit/ir/attributes.h,sha256=gIeGIwTIj79uewJ86LTn-UwINTqAyhKh1uIdXtuFVGY,4784 +torch/include/torch/csrc/jit/ir/constants.h,sha256=gtzaew4jNBbirPIy1vj4nwlVI9Pn19ph2DTu_X9LqD4,2008 +torch/include/torch/csrc/jit/ir/graph_node_list.h,sha256=xfVDMfQRcyU5JSF0wgrU2_-nwopQhAceRUJDfRquTKQ,6354 +torch/include/torch/csrc/jit/ir/graph_utils.h,sha256=HxQcilBWyFeP0H1SG1IAbSju0OcauktL3N72rtteEcM,504 +torch/include/torch/csrc/jit/ir/ir.h,sha256=PZxFgA6j3XbzQOAuMkWPf3xURkhZGlIz--dBLI7YEUU,53892 +torch/include/torch/csrc/jit/ir/ir_views.h,sha256=KedK8xniQe7Z6i5TFZbPZHJtUG-NWX16ooyhTYEx2DY,4623 +torch/include/torch/csrc/jit/ir/irparser.h,sha256=_Z9wSbbRPtJGO5esO1eskSL7gb29rYg3w90YM1rLUSc,1096 +torch/include/torch/csrc/jit/ir/named_value.h,sha256=sONGq59HHPd4lL8E_LbgWscQMFBM1_9WF837lklVAzk,2396 +torch/include/torch/csrc/jit/ir/node_hashing.h,sha256=e7BcYSSzRUdHouCPuHwoudhE40CNJxJ7WBxymUldYx0,265 +torch/include/torch/csrc/jit/ir/scope.h,sha256=05-wkq6x6su8FVbkG-s6ZEDfgENBFjOsNXPa6tbpGlM,7148 +torch/include/torch/csrc/jit/ir/subgraph_matcher.h,sha256=XTz0-hJJuX17RZE_-t0o8hCbPf7OUWlL5NObCjw8wRs,3126 +torch/include/torch/csrc/jit/ir/type_hashing.h,sha256=9nytJGHdiD4rJsWZc-rD_IU8PbKvBChPHry-SsRv1qE,434 +torch/include/torch/csrc/jit/jit_log.h,sha256=A2iWf_Pt1jF1_WAXuiW7qP1yufuEFkTv-MKSkJRN0GA,4806 +torch/include/torch/csrc/jit/jit_opt_limit.h,sha256=XJpbnGQnkJtquSPeb7x0KduePq5nER1diRowR5s3Khg,1381 +torch/include/torch/csrc/jit/mobile/code.h,sha256=En5_S6Bro3ljAHuxfjVD74WArspp_P1pLD0NwOAPsj4,1078 +torch/include/torch/csrc/jit/mobile/compatibility/backport.h,sha256=Cjl43vEPBm2T8Il8f4FSJCXHAgER8mVCCXKP4L67glw,639 +torch/include/torch/csrc/jit/mobile/compatibility/backport_manager.h,sha256=6v6-bsjZOlGdmDkbbSB49VCh930PNV8wSWZNjo4NvTU,1159 +torch/include/torch/csrc/jit/mobile/compatibility/model_compatibility.h,sha256=Tw3aswYn6InEk4OiF4TSycVu78t5d3vnyS7zJbESfEQ,3612 +torch/include/torch/csrc/jit/mobile/compatibility/runtime_compatibility.h,sha256=W7ZfWBj84IBORPa79xbeBr-JQTFIZsFTQvnl_fI0rVE,1197 +torch/include/torch/csrc/jit/mobile/debug_info.h,sha256=asNWlcmEwER60l_6g5nJD4izXdAcogTX4loA-QGEG5c,2205 +torch/include/torch/csrc/jit/mobile/file_format.h,sha256=uK-6oUJfo3g21B4jtC92ge187516qn1m5WE9WYP7OYY,6596 +torch/include/torch/csrc/jit/mobile/flatbuffer_loader.h,sha256=JLk2cOwGWU4WxiQwgJkgWa9fbWwmFbTJGb2Lx2EyOro,4963 +torch/include/torch/csrc/jit/mobile/frame.h,sha256=mZ01u-2-cRV5hCBHVEPZ1m59SWudn1v5RY_lLKl0YFI,811 +torch/include/torch/csrc/jit/mobile/function.h,sha256=AeJi_59lhezOHPCMwhtGY2_DgXGPCxG8FFMcRmY8jdI,2900 +torch/include/torch/csrc/jit/mobile/import.h,sha256=EmILgMSBf0Nh76yKNvdD1fkZk1Yw7DpbTCEwq8FSbv4,3840 +torch/include/torch/csrc/jit/mobile/import_data.h,sha256=rrwfgQfByxiy-sLwD39YL43UQ_-DvMDWv1KUjV765Xc,995 +torch/include/torch/csrc/jit/mobile/import_export_common.h,sha256=Mn3lS3DQL0enRVC9MzgXDl5ZQQhyUlthe4-1Hr9yuGE,477 +torch/include/torch/csrc/jit/mobile/interpreter.h,sha256=rRURKeJqZ3rN3yk72s8lSW2yZigCN3DdRC9b-HF2HAM,638 +torch/include/torch/csrc/jit/mobile/method.h,sha256=PxGPyD6Ob6ic-D8gyrwjosvKLTpfD2aK2_4WxbXB7Rc,824 +torch/include/torch/csrc/jit/mobile/model_tracer/BuildFeatureTracer.h,sha256=abdU7Ft7zGay3kx4egdL8vypgeu6vbtVj8W8hnFEdEs,961 +torch/include/torch/csrc/jit/mobile/model_tracer/CustomClassTracer.h,sha256=RZPilST3U8wa2Utwcs7JyEehaw4S3HEi6gH8iNTVgpI,959 +torch/include/torch/csrc/jit/mobile/model_tracer/KernelDTypeTracer.h,sha256=e8n23XjIEGD0HqbyIXktSHlYpsjGQVQ6AJbsCAe5h-k,1212 +torch/include/torch/csrc/jit/mobile/model_tracer/MobileModelRunner.h,sha256=S5B7XajTy4kuVVkOgWEYVrkmh_10xCX1lrK1ek8bS-g,5086 +torch/include/torch/csrc/jit/mobile/model_tracer/OperatorCallTracer.h,sha256=IBatI0fp74OWyW1cb5SGZV4ltJq9Mu1lSAFvyMe4mfw,918 +torch/include/torch/csrc/jit/mobile/model_tracer/TensorUtils.h,sha256=8JzQOlfo-kOII6qwDiKbEdmpqxfvwwppED5Ao3TFrr4,398 +torch/include/torch/csrc/jit/mobile/model_tracer/TracerRunner.h,sha256=RDwlV-P058diJGJXcfQAPs4q9h_v11OZfC9tLUC4fzY,1107 +torch/include/torch/csrc/jit/mobile/module.h,sha256=EbPRDu1mzN7PWBQo_Fnu7pBSv6r6LGdiynSW1NFAinQ,5921 +torch/include/torch/csrc/jit/mobile/nnc/aot_compiler.h,sha256=mjpvKr97ybwpjM_z8MxFlAuska8t4SPDeuius_WzEJY,630 +torch/include/torch/csrc/jit/mobile/nnc/context.h,sha256=EnGQvnLMliBdurV9gNqfhJ3Tw1T5Cu3i1UCBSAFFrF8,6616 +torch/include/torch/csrc/jit/mobile/nnc/registry.h,sha256=OsPMYFJGPiQ0n5D4ujIfdDYyxNMnLeg1NZdCHh9s5m0,1160 +torch/include/torch/csrc/jit/mobile/observer.h,sha256=GhQYLhyUPbiIkVzBO2PcZRyo44G8yIoVwtLEzZ5iaLY,3637 +torch/include/torch/csrc/jit/mobile/parse_bytecode.h,sha256=vEgtBoHpKaQ0YfR3CznfZIy5_NZQqK34bMlva0r-hXM,740 +torch/include/torch/csrc/jit/mobile/parse_operators.h,sha256=KErayQj-WQw2Q5wBA4Bxy71eZscQesCOLNGvlpd9C8s,709 +torch/include/torch/csrc/jit/mobile/prim_ops_registery.h,sha256=PASxNKpx71Vx2SbToMG_NCujQs4dMvQIQIgdiG379kE,595 +torch/include/torch/csrc/jit/mobile/profiler_edge.h,sha256=G0cmlkQTIjYi-eHDyksRLlzt1jfDcUIA3UnuXPDnWLY,4488 +torch/include/torch/csrc/jit/mobile/promoted_prim_ops.h,sha256=H9ce41Qp8BFZIGNFQbhHAx_60pb3ji0YgrSwKbwJR48,1043 +torch/include/torch/csrc/jit/mobile/quantization.h,sha256=GkNn9-1ZofH63_IP9Dj6K6zE3broXIey1v9uP8uYT5w,1238 +torch/include/torch/csrc/jit/mobile/register_ops_common_utils.h,sha256=8nmbLPeyKOqRPlldLXRHMVXKqPp6bid6zmAns2Y32BE,1687 +torch/include/torch/csrc/jit/mobile/train/export_data.h,sha256=EyriecCqCMDXnur59n-ij6nX9HxlsFcAtZRlttMYQds,1594 +torch/include/torch/csrc/jit/mobile/train/optim/sgd.h,sha256=_PAos6QtGJuvq6S5BCl-M_WiXEN6Dw9Nqa4d9MTVM3E,4331 +torch/include/torch/csrc/jit/mobile/train/random.h,sha256=T0rs_-eO-wEJodxwLhlcPt-op7X4yn9sWOf8IUU5yPE,1519 +torch/include/torch/csrc/jit/mobile/train/sequential.h,sha256=3faxeNLoyam7ujYSRfXC00ltc6EPQWFb7sn_F7pp6W4,1224 +torch/include/torch/csrc/jit/mobile/type_parser.h,sha256=6hoGT8B4nfL-0SIo5G9vYlC-Tw0OmbGF2LyY7yZtdH0,1443 +torch/include/torch/csrc/jit/mobile/upgrader_mobile.h,sha256=X7HNrKi5kXMm5YOc3YwN3mFnJloaPrIcAUVYTybR4Nk,905 +torch/include/torch/csrc/jit/operator_upgraders/upgraders.h,sha256=AMyExdbjEbuKmhV2_mtWnkKDJfbiWF2DnGM_Bam0h2k,1384 +torch/include/torch/csrc/jit/operator_upgraders/upgraders_entry.h,sha256=G6l-5X8C20t1r76K0m4D2Nur2OIBdArCcHZntkBr1jI,522 +torch/include/torch/csrc/jit/operator_upgraders/utils.h,sha256=Hjj1sLn8DFS4iSZgnC9CsFIVCiKPuHCpyqOnArfB4NU,1693 +torch/include/torch/csrc/jit/operator_upgraders/version_map.h,sha256=eDrITgffE6UrxrIBTd_EEsn8YeJoH6WhoYHI2WRizow,887 +torch/include/torch/csrc/jit/passes/add_if_then_else.h,sha256=SqzsU8rP9F3Vqay5CWuT7YaLtmDnhP2kJDdgAjR2Ws0,163 +torch/include/torch/csrc/jit/passes/annotate_warns.h,sha256=fPa2pkQCGnlzw-e9q5rk3diP-c92wlfpcCFxmZpo5zQ,167 +torch/include/torch/csrc/jit/passes/autocast.h,sha256=LDml9aHIr81KJeXT0sQvKWF51QY3idDSvsTQo6guAX8,242 +torch/include/torch/csrc/jit/passes/bailout_graph.h,sha256=ad8Y1rPYM1wAQdoqnBtCQKfB3y6PXRqRay3XCtO6t0E,1090 +torch/include/torch/csrc/jit/passes/batch_mm.h,sha256=48oVZGLKM07Co5ACKE3rCiPU-L5j_T0-WBrCjuiPYFk,131 +torch/include/torch/csrc/jit/passes/canonicalize.h,sha256=KdpN14lVQAASxOPl3mUsYAICqytdFaJw3D-3TivkM1k,467 +torch/include/torch/csrc/jit/passes/canonicalize_graph_fuser_ops.h,sha256=0KZ8gzL3zNsxdMcaKn69fgjmQacBjDnoO9LZUdPNozA,145 +torch/include/torch/csrc/jit/passes/check_strict_fusion.h,sha256=q-liogijqECywbsOAZ5szYGRp1XUbmGJWLIg3ehgqSU,166 +torch/include/torch/csrc/jit/passes/clear_profiling.h,sha256=GpqR36A1Ins8W2FLiDvW8rn8WzPH9t4-skIeQg8KlVU,468 +torch/include/torch/csrc/jit/passes/clear_undefinedness.h,sha256=eWi0gIwFbplRZTeyq_Ibvkwv2BYjhBFZBmP42vLv9J4,850 +torch/include/torch/csrc/jit/passes/common_subexpression_elimination.h,sha256=BGrSGphxkxrEdHVVWj1A0RhMRh5aX0Y2-o4HQyyWDrg,162 +torch/include/torch/csrc/jit/passes/concat_opt.h,sha256=2KVY2Y3TMdWBcyS_cvuENvq0yOMCjF0XFjD7Ds_QP2o,525 +torch/include/torch/csrc/jit/passes/constant_pooling.h,sha256=nF_RzkunPKRtM0PxSI8KC-_CKWvY_h_nnd9I5oS19m0,145 +torch/include/torch/csrc/jit/passes/constant_propagation.h,sha256=l_9XDRrWqiKKBO2D7CCXfmBSjdz2k5rgUdraw1doHXs,1289 +torch/include/torch/csrc/jit/passes/create_autodiff_subgraphs.h,sha256=5kVblBhnjxqtWX5IKevPTrdOWoI3vn1oIIcPifFh66c,513 +torch/include/torch/csrc/jit/passes/create_functional_graphs.h,sha256=zK8VjkR7kBYtphNK1XlAlRY-hpW8IJK6-pbBbbAdWrU,284 +torch/include/torch/csrc/jit/passes/dbr_quantization/remove_redundant_aliases.h,sha256=0R0hhhFH3Q9kbRXbDGFMhQtSHpZYx_GdWcvssVED47Q,340 +torch/include/torch/csrc/jit/passes/dead_code_elimination.h,sha256=m9NaVt10OXyfTM6pOrV49trrF9TveUr_RD_MWdSk-d8,1559 +torch/include/torch/csrc/jit/passes/decompose_ops.h,sha256=_8xtoLSnuTFCKsC3ygCm0sCv9ySqufbXClFfRYGI14U,136 +torch/include/torch/csrc/jit/passes/device_type_analysis.h,sha256=2a1paFDp2VO8LAu2XFNYzFyRtuxTn2ESkVOvOnnGa1w,242 +torch/include/torch/csrc/jit/passes/dtype_analysis.h,sha256=B9T6BLHBhnfRrCG_tGWrWlf3IkiD7aCzfjx1IA-6THk,389 +torch/include/torch/csrc/jit/passes/eliminate_no_ops.h,sha256=ntVyB4-DqePQ41UUDtANSL-26sOfU_BOL1z4nBSNjHA,492 +torch/include/torch/csrc/jit/passes/erase_number_types.h,sha256=LtBRyI6TMzIXTgA2WGQDkCirBNAl2L0K_XT_Od0eUyc,788 +torch/include/torch/csrc/jit/passes/fixup_trace_scope_blocks.h,sha256=siqmFD6fqb6r4w40g3oDqBsgNb0Dnjw609ZyaSVb8LA,1648 +torch/include/torch/csrc/jit/passes/fold_conv_bn.h,sha256=f8AqfbcNSwB4W0BdEVKumrwbWw_P1NDKyDyeIjb5ZyA,970 +torch/include/torch/csrc/jit/passes/fold_linear_bn.h,sha256=qzyUzlYV0zNOckcwDT_YaSqsYuKcBh6ZY3b7rET2VIo,666 +torch/include/torch/csrc/jit/passes/freeze_module.h,sha256=nFx8e_M2AjJElhLpfa47FeDmOIhYVZb0Pz23xH1rAJY,1219 +torch/include/torch/csrc/jit/passes/frozen_concat_linear.h,sha256=wgVhANKP7mWQXpwL5WqEklkUXXXG29XRSFy3-ksbmiU,252 +torch/include/torch/csrc/jit/passes/frozen_conv_add_relu_fusion.h,sha256=x_VBteq6eOYHonm-J4nMrfWcj8cBPTs77NuCA2oZTH8,304 +torch/include/torch/csrc/jit/passes/frozen_conv_folding.h,sha256=8Kt-Wf-I8P0tu6Nm5iwQdm6-vEQf2uPLseIonSXpbt0,847 +torch/include/torch/csrc/jit/passes/frozen_graph_optimizations.h,sha256=cufZDZ87-yO-38nNYwVAmmiW4QDlBKjscZinyQJDTjQ,441 +torch/include/torch/csrc/jit/passes/frozen_linear_folding.h,sha256=bpFNgW4uQFGhLuqqo-u0V-ecigUdcj4iS8xKplozeKw,345 +torch/include/torch/csrc/jit/passes/frozen_linear_transpose.h,sha256=83eH-ogd9UGm2cI7Y73Zwwvt9vmEgqLK3dp5FWa66Sk,261 +torch/include/torch/csrc/jit/passes/frozen_ops_to_mkldnn.h,sha256=l5fGupNo6vpH28iG6m_LwbLLVUy6ur1ubSGDGFAnQ6k,389 +torch/include/torch/csrc/jit/passes/fuse_linear.h,sha256=ifSEjZDMEgrzwlIWc1LymM3PIXKIZLchfaWTT9U5cGA,743 +torch/include/torch/csrc/jit/passes/fuse_relu.h,sha256=W1Yh8ZdLNfYHjNA7q1P_U6ZQSHzkx6DdSutcf_Et-wk,248 +torch/include/torch/csrc/jit/passes/graph_fuser.h,sha256=PlrCfCFXusmx71htAI5tcwJFBTEI-n5bnJtbWORxBmo,1226 +torch/include/torch/csrc/jit/passes/graph_rewrite_helper.h,sha256=r4QxNlqBRsvmIlli5Wp01br1cpvlT1T-8AF-hTsskBM,1735 +torch/include/torch/csrc/jit/passes/guard_elimination.h,sha256=ifoI_eEFuQzmsLr0Fg-TOuPcT7_4bpDzfIumVVi5yjg,351 +torch/include/torch/csrc/jit/passes/hoist_conv_packed_params.h,sha256=1FbeHqN5nWKU4JwX6UvJ-tpyoDqn_ZnMJnrRjxgAFuQ,186 +torch/include/torch/csrc/jit/passes/inline_autodiff_subgraphs.h,sha256=xOn17EMKsN9kTjDMq-nerxssJiTsplQDFaZjxfvYQpM,250 +torch/include/torch/csrc/jit/passes/inline_fork_wait.h,sha256=9C3SsJok6HcxmZ8EU9E1kkvekx6GBBIJt04Lizu5ixQ,522 +torch/include/torch/csrc/jit/passes/inline_forked_closures.h,sha256=HtmSYANlcwPCSg6CChxfZed_rbLhKhwMmM623CzYN-A,202 +torch/include/torch/csrc/jit/passes/inliner.h,sha256=lM2XE7KkZLwhPz6Eq0e20S2cxLdWkkZWTZ7raId2eUQ,229 +torch/include/torch/csrc/jit/passes/inplace_check.h,sha256=GxWaSePuolrvA_MNjy-HQYINuXX8VyrJOw1OXMEXkTc,136 +torch/include/torch/csrc/jit/passes/insert_guards.h,sha256=GtpucbqQnAfUNZaNk2XdP83qmg6N_tJqamW26gi5OOA,414 +torch/include/torch/csrc/jit/passes/integer_value_refinement.h,sha256=f7sxiTI0QQfQHfJWrzDuPk7z-s25PLWvTu69ntOQIl0,209 +torch/include/torch/csrc/jit/passes/lift_closures.h,sha256=tDmxkDUMgSKE-icmGwNVngQR37fSuSmgkCG6d_m-xV4,197 +torch/include/torch/csrc/jit/passes/liveness.h,sha256=kkQ_gJdjAI5UfHyo-p7D2iLmbBzmeG6utM--OfjZoR0,625 +torch/include/torch/csrc/jit/passes/loop_unrolling.h,sha256=bWg1UxAMJJI8DYH4fKxAoIZH9AYlEFOzfuhISqj8mD8,981 +torch/include/torch/csrc/jit/passes/lower_grad_of.h,sha256=2plAYjg7fEESZf8MSb9NXAVWWynf2bX7PRZ54pL6piA,323 +torch/include/torch/csrc/jit/passes/lower_graph.h,sha256=7tiixKEGcAhiCA9WtGitm2NIRFNVfTWPvZXSX37d-Z0,725 +torch/include/torch/csrc/jit/passes/lower_tuples.h,sha256=l8lohwGRY2iri9rt2VEYa1RzCGRC1hJWAhxlQrm3Mpo,641 +torch/include/torch/csrc/jit/passes/metal_rewrite.h,sha256=gI23EmH1oEFnwkNqNzcz5figxog77K6mxYYzeoeAECM,581 +torch/include/torch/csrc/jit/passes/mkldnn_rewrite.h,sha256=P3NmShFkexY9WpJcIN2BEYPrGbiJtcEAUOrxfcESLZk,605 +torch/include/torch/csrc/jit/passes/mobile_optimizer_type.h,sha256=FkImh2z2c2osMwsZ-dCA8wQysofiETWhhZMbcDg54Ek,237 +torch/include/torch/csrc/jit/passes/normalize_ops.h,sha256=WGodCS7W4mE-iuNWeg9auaEb8N_qw2_Q13B5LkX1bjI,511 +torch/include/torch/csrc/jit/passes/onednn_graph_fuser.h,sha256=TjR7kSM7pDJqEO3ce2ohPkXYjtKBnWdZscj8HucbLqw,1399 +torch/include/torch/csrc/jit/passes/onnx.h,sha256=qvFTNGzfgk1Qg_PjQCK1JTKJBiVja4OxSO1BFago1xM,832 +torch/include/torch/csrc/jit/passes/onnx/cast_all_constant_to_floating.h,sha256=g9P3tuAz92bBA4ZFegyb-81yBX7PiDKGc1exITzp-Gw,217 +torch/include/torch/csrc/jit/passes/onnx/constant_fold.h,sha256=OHEeSz3yZZP_dJCUIwLPyw7XhSfJg1uByU_Z5j7cccE,693 +torch/include/torch/csrc/jit/passes/onnx/constant_map.h,sha256=3Npm1iENN316ES6W3XOhO6QzMYxMsVvT8bZUpfNVCUk,4422 +torch/include/torch/csrc/jit/passes/onnx/deduplicate_initializers.h,sha256=pizsdToN2pF-mKupVBajaEwsHlsEwuiStn4IyIrTx3k,247 +torch/include/torch/csrc/jit/passes/onnx/eliminate_unused_items.h,sha256=Xq5scQaGQY08msax71MSlc-2QYIKQgcgR5XZF1NBrkI,343 +torch/include/torch/csrc/jit/passes/onnx/eval_peephole.h,sha256=-XxEOdSQ0T8T2xj6vDQF_bzZslUwFMb9b0whC6rTAj4,220 +torch/include/torch/csrc/jit/passes/onnx/fixup_onnx_controlflow.h,sha256=3S4Hf4vUX5tfrmfJvUPzLo-vpmQmFRKbx3QAoR80fpo,221 +torch/include/torch/csrc/jit/passes/onnx/function_extraction.h,sha256=8fbxzj2KNkW463hpuHDSIIlG8jLXPfXpoNM3Wwu5XvM,2251 +torch/include/torch/csrc/jit/passes/onnx/function_substitution.h,sha256=uZ73rH5KRD4dw71ik3MeM_e5vGSQhQRk2wCWQfFMzB4,135 +torch/include/torch/csrc/jit/passes/onnx/helper.h,sha256=MtvHWcUf3IHJH19OHOlJEhlykhvdmlEDLGPpZFjJFXQ,2131 +torch/include/torch/csrc/jit/passes/onnx/list_model_parameters.h,sha256=JtcroeZQn2lxNXDL3JqCvBu2bZQw9IJU0njjIYcLE30,239 +torch/include/torch/csrc/jit/passes/onnx/naming.h,sha256=bDqZc9eqzcjWkzFjGdoQlBGMo17eqdHdXJHFPr68DRc,760 +torch/include/torch/csrc/jit/passes/onnx/onnx_log.h,sha256=-c6qNmH0VOds5JgHxhO8nA1xn0QwaUMfFjFvUuWrP_g,584 +torch/include/torch/csrc/jit/passes/onnx/pattern_conversion/autograd_function_process.h,sha256=pnlyRzAGXGnc4l4CTSGb3s7S_di8J1pKRuJlzv5ieBs,175 +torch/include/torch/csrc/jit/passes/onnx/pattern_conversion/common.h,sha256=WZfMt73oWy30VcFAN0MOE4tG0V1jRlNiOJ3_Of2hF_M,336 +torch/include/torch/csrc/jit/passes/onnx/pattern_conversion/pattern_conversion.h,sha256=J_9RoTcdpugYQSLgrdqvdcrhSQ6uNtCTsqriw7Z0_sM,2095 +torch/include/torch/csrc/jit/passes/onnx/pattern_conversion/pattern_encapsulation.h,sha256=697sK7CqJ0PJNQ2J0wQa7HE-10qKXIdgHMt8sum8oK8,1362 +torch/include/torch/csrc/jit/passes/onnx/peephole.h,sha256=jaFw_UzofvT4wCIdFbkN1261jxebwUehTUoEZJsz4co,213 +torch/include/torch/csrc/jit/passes/onnx/prepare_division_for_onnx.h,sha256=dlicF-2GDregFW74LyqAcgUwvTg-OL9GRVw6ewBeVOw,451 +torch/include/torch/csrc/jit/passes/onnx/preprocess_for_onnx.h,sha256=n58RQ-qD5zuurwo1UUXT_D4BuKGlQtNbSgkuBffWwxE,155 +torch/include/torch/csrc/jit/passes/onnx/remove_inplace_ops_for_onnx.h,sha256=bU6w7bh79tpYIaxe6ji3iXU7zkchkcTtT9N44D6n2d4,201 +torch/include/torch/csrc/jit/passes/onnx/scalar_type_analysis.h,sha256=7DrlIrmWC3jcJ8Q93HRrsHvXmjjhYCyU1LjAg5wAXxw,280 +torch/include/torch/csrc/jit/passes/onnx/shape_type_inference.h,sha256=dT01aMvEABS1VwKpoD4aI_lr65HQVYvlEFPzIOTblss,3955 +torch/include/torch/csrc/jit/passes/onnx/unpack_quantized_weights.h,sha256=8zLEJlp_AqyJGrM92RUs2aRiMS6-9J1BorpXgwrL44c,427 +torch/include/torch/csrc/jit/passes/pass_manager.h,sha256=dTTEaB1MO0S3Dn2j6RSksfUb8Zn0rXWIjjwl-Di7k6U,4549 +torch/include/torch/csrc/jit/passes/peephole.h,sha256=TcAnx68gFYsDwg5KltP7-VrlmHWISj2VvVaRTNPy9Q0,482 +torch/include/torch/csrc/jit/passes/peephole_alias_sensitive.h,sha256=TgzvCyJ89Vqmj8uogJHuHeWHkho4pNdOmIyAXzdX-Ps,410 +torch/include/torch/csrc/jit/passes/peephole_dict_idioms.h,sha256=GVUd3kCbCfg1KzJbevwHJyRiqWV-ib6MI0J0DURz1yM,975 +torch/include/torch/csrc/jit/passes/peephole_list_idioms.h,sha256=l4W3EHa7glMKZkvJ-vyjrjvugUqd-CZX3F4Gfqfi8z0,1978 +torch/include/torch/csrc/jit/passes/peephole_non_tensor.h,sha256=NMWReecnowP8yShiTl4-_atPfpkDCNTRZBKGLdLYdBI,317 +torch/include/torch/csrc/jit/passes/prepack_folding.h,sha256=h-aHTSMo1X3R5_8g7g8joc1KKnsxFGZeIbaDjBNks4k,333 +torch/include/torch/csrc/jit/passes/quantization/dedup_module_uses.h,sha256=X9dZJAI_1iRI-52UO6T-bMEluEul3rO4ZrFbW4ssfDQ,801 +torch/include/torch/csrc/jit/passes/quantization/finalize.h,sha256=zg09ZolYFJWSry_AWxjTH42ErpkB_uxwIwaJpkdxp0k,2296 +torch/include/torch/csrc/jit/passes/quantization/fusion_passes.h,sha256=07RaFZcwJGI1TTG5WV0-ZDH6zjyCRKDx7fu8q1E2PLo,166 +torch/include/torch/csrc/jit/passes/quantization/helper.h,sha256=f3NnNP_2vNidzbRIvz93QFakE1I7Lfv4o3O5-NwHn-A,7459 +torch/include/torch/csrc/jit/passes/quantization/insert_observers.h,sha256=oZzSTKvnmFz5KM0a0xNBdUwDKrTHNee5JOSIogxl4I8,2326 +torch/include/torch/csrc/jit/passes/quantization/insert_quant_dequant.h,sha256=9jDot-HHS9QUiNoDbQSAP-b_T9ptfHlnsROKf0OB_VY,1425 +torch/include/torch/csrc/jit/passes/quantization/quantization_patterns.h,sha256=eFdejLSQUeUpJTRs9n5BR0H7n5XiTHxIWFVuhtNq37U,53365 +torch/include/torch/csrc/jit/passes/quantization/quantization_type.h,sha256=tZb4zUgmtrCIojoMB4KbKM4btcvJmOB1eaqXiHYVjvY,333 +torch/include/torch/csrc/jit/passes/quantization/register_packed_params.h,sha256=Q22nZU6_PjpdmrtK8dySPFLR471uSXKfbBiCTNjfaRo,489 +torch/include/torch/csrc/jit/passes/refine_tuple_types.h,sha256=Jux8D7hOzk34w5wP7svDq5arMN8xOz4A_M2Zl6E4VJI,242 +torch/include/torch/csrc/jit/passes/remove_dropout.h,sha256=KJkElpJYo7RIVQWOu0G0WCSIfR7WoeE7M_wgAnxPrZE,255 +torch/include/torch/csrc/jit/passes/remove_exceptions.h,sha256=SEB_5z3U_AHKWJJEsvAbOJAhPlarOnPt0kX68fnDq-A,929 +torch/include/torch/csrc/jit/passes/remove_expands.h,sha256=lutROSrK3K-Gy4Qh6oOXS33_-gTQRU119nGxRS3WiG8,143 +torch/include/torch/csrc/jit/passes/remove_inplace_ops.h,sha256=TIJotciLbSDLMNi7zrVAuRpD-XQKXNtlZovbxEtaGvY,271 +torch/include/torch/csrc/jit/passes/remove_mutation.h,sha256=1XDTabWwOaTULo96tzWZ68_J9AmF5mzu-G9z_oD0Yag,2637 +torch/include/torch/csrc/jit/passes/remove_redundant_profiles.h,sha256=1loSYnsacPiKPMBqt1WbzrwW6qPAyf-FVSz9cfUz70U,237 +torch/include/torch/csrc/jit/passes/replacement_of_old_operators.h,sha256=iGGFQbsPevD6vlwTk2dIlvbYEzrUJOY2NRqU9btVKgc,434 +torch/include/torch/csrc/jit/passes/requires_grad_analysis.h,sha256=GRWqIyC00uXNC3gAfrhjAS-5hk-qp71glZgns900s-8,221 +torch/include/torch/csrc/jit/passes/restore_mutation.h,sha256=iRuv29JV5ock1gZzIgUgsGVie9Ir9lSuQfd27FjZVEQ,1801 +torch/include/torch/csrc/jit/passes/shape_analysis.h,sha256=C27liVgubg0g1TkLxAtRX1I3GGrgPyYKcpsrh_NoDI0,1077 +torch/include/torch/csrc/jit/passes/specialize_autogradzero.h,sha256=pkBLCR8ZA3_uYb1Ptn7uByaAW_8CXsh5LoJJpGFDUbw,631 +torch/include/torch/csrc/jit/passes/subgraph_rewrite.h,sha256=Xv10XCG8o5cLPg4zzRl0ml8SEubnzo_lxi1nchuOUFU,4087 +torch/include/torch/csrc/jit/passes/symbolic_shape_analysis.h,sha256=KBwMxFyi0qVBiG6x0gNBXC4N3qrQwoWEEpMpSICS7cg,2078 +torch/include/torch/csrc/jit/passes/symbolic_shape_cache.h,sha256=qY3pwISkYPH68C9I5fxPixIT4qxixyqooZkUW45SyEU,1574 +torch/include/torch/csrc/jit/passes/symbolic_shape_runtime_fusion.h,sha256=RvR8HJfziaAAxgdSb6umHJfDdiKKNwoFd6ISGIInqk4,2339 +torch/include/torch/csrc/jit/passes/tensorexpr_fuser.h,sha256=qgrCs_X2rP6EEA11mR1zc90Mr1NSQVvAHkasMT-BEqc,2700 +torch/include/torch/csrc/jit/passes/update_differentiable_graph_requires_grad.h,sha256=Z_LPzJWI0GchVkPjIFgiEhgpl9GPVC0mysXthliHtxY,715 +torch/include/torch/csrc/jit/passes/utils/check_alias_annotation.h,sha256=fBn-vqe7PohhMJ8K0qjHL0Jy0cvuAT74cmuBDOg4gKc,587 +torch/include/torch/csrc/jit/passes/utils/memory_dag.h,sha256=IVoOTg6eoOcYik6lsElfD3C4xdiqGirAbNLRPu3e4HQ,6391 +torch/include/torch/csrc/jit/passes/utils/op_registry.h,sha256=PJedYy3cXEfzn3OOyN6xewmqnXvggPLptytzyEtbGvw,1008 +torch/include/torch/csrc/jit/passes/utils/optimization_utils.h,sha256=e8FW-ecJJbsYIE7beF8orX4Vtyr_6A1Q2TpjkRiqdJc,219 +torch/include/torch/csrc/jit/passes/utils/subgraph_utils.h,sha256=4D_RsRUNsi8YUe3GwuNS-QKOwCwmLPSY8mmQS_wqf3o,2360 +torch/include/torch/csrc/jit/passes/value_refinement_utils.h,sha256=G_MzKqZwrvjvcY-27FMPOOfDiWdjNrDHxv9_aEsNbIg,2607 +torch/include/torch/csrc/jit/passes/variadic_ops.h,sha256=nWdQbS91o6Xr27wuy8rPbvuTFZPPCQUqDw5W2NMiiVA,885 +torch/include/torch/csrc/jit/passes/vulkan_rewrite.h,sha256=JYOjWoqD8ZUR05Ez-e_gulm4_-r4lrgBY6It8TGenoI,673 +torch/include/torch/csrc/jit/passes/xnnpack_rewrite.h,sha256=jBXEUXGoNWqkLKqm0HcLvcyjZyvAFoTEUSnf7YBfiII,795 +torch/include/torch/csrc/jit/python/init.h,sha256=pi94AbCTzGH47EW-u9kPDdRazf2RXwVKISlt6S3p8dY,143 +torch/include/torch/csrc/jit/python/module_python.h,sha256=pG2XxtGcfzVLg41FJoF6Z7O_W26c4Ql9ACMtQWCk4-k,1873 +torch/include/torch/csrc/jit/python/pybind.h,sha256=2QsQ66TtfniUmF1goxOQ7Bbjvm8wNRZXeMa3YqYg1Qw,7913 +torch/include/torch/csrc/jit/python/pybind_utils.h,sha256=dKjet2amrEU0LCAejUUw3FNJ-YSWcg6d3GmlpT0Hih0,44564 +torch/include/torch/csrc/jit/python/python_arg_flatten.h,sha256=-4yxueFeKLSCSDJO8zWm5nZfIWwNItwux0kXf_7jxZc,3527 +torch/include/torch/csrc/jit/python/python_custom_class.h,sha256=8ZyiBXOmSpVC3-7DETl_KcxJPPELJ4U-KlvBeXhRoHw,412 +torch/include/torch/csrc/jit/python/python_dict.h,sha256=OUSPLAnpyWPxZ8qCBDFx-CPr8ck_Ba0xtILEMbM3bDg,3383 +torch/include/torch/csrc/jit/python/python_ir.h,sha256=NQBztQd2JZXnRRNYU0TE8k8dFxq92xhWQFxZXRMrAPw,1693 +torch/include/torch/csrc/jit/python/python_ivalue.h,sha256=3Yeu2NDVyDnntbLOfGBmO2c6ZD9JO-xahMJkHTTs0c4,3682 +torch/include/torch/csrc/jit/python/python_list.h,sha256=1cutGFlft069GkKn9RM-0CUhUVp7jneZ4jO6q2MuNdo,5494 +torch/include/torch/csrc/jit/python/python_sugared_value.h,sha256=6WYgHHfNJpWqZjm5-RLxmMvxsPjsqFSaSNK62CkeE2E,11335 +torch/include/torch/csrc/jit/python/python_tracer.h,sha256=OBYJohDlSm9YuWZ0rbnMenLbY-2_W1ySEUkPJPD9Xkc,1218 +torch/include/torch/csrc/jit/python/python_tree_views.h,sha256=yJkTxIBDBgYEkX-7zAjVE1ACKKBNXvBU9Kiq885knT0,150 +torch/include/torch/csrc/jit/python/script_init.h,sha256=-nyejY7PHUrX4O1LEs7W48ZifDvd_oMfucwL2vAndyg,152 +torch/include/torch/csrc/jit/python/update_graph_executor_opt.h,sha256=pdlOCRdZR5VsId6If4yEOtsN1zNtLDzzFT7fncH-B-U,185 +torch/include/torch/csrc/jit/python/utf8_decoding_ignore.h,sha256=rndEzjMH5efB3RbaWMpfujvNQUe3o2_mesOzreaJHPs,179 +torch/include/torch/csrc/jit/resource_guard.h,sha256=oMbaYMyJLGgPCEjIV4y5bAYsaYZ7_4p0QIQXTkawpHw,440 +torch/include/torch/csrc/jit/runtime/argument_spec.h,sha256=JD60JCkX_8aYm9RckTXa2kciFTcD80j8RjpkNPAYpLE,16345 +torch/include/torch/csrc/jit/runtime/autodiff.h,sha256=5apSR8BAAdifHGPPFeCsS4GpYstVS1EvAYbK_lsaQgc,3930 +torch/include/torch/csrc/jit/runtime/calculate_necessary_args.h,sha256=WQujo6Vy1MOEeHzKhO04AYoOAAztIHnIZJ2Om4eqg9Q,2281 +torch/include/torch/csrc/jit/runtime/custom_operator.h,sha256=eKEDGUFIrgJPlHsk_XiittC4gnRPq1XZwjpkvFOiLYo,1054 +torch/include/torch/csrc/jit/runtime/decomposition_registry.h,sha256=FVhRmGP1tlkuOhoGxINQCRF3lko7zp_9r-yqhQpnJ8A,1045 +torch/include/torch/csrc/jit/runtime/decomposition_registry_util.h,sha256=vJ_K-8X9IaDOmrmwHNS79NxyyBlPdNcwH4NUbuvVS34,261 +torch/include/torch/csrc/jit/runtime/exception_message.h,sha256=HhV725zokG4hM07w-JpB6F2BLLoCoxwCjcYg2-Ccduw,614 +torch/include/torch/csrc/jit/runtime/graph_executor.h,sha256=rkzcPp9vA9-ZQgYLYVzinu_CJ31XMie7MHid8yHMjy0,4659 +torch/include/torch/csrc/jit/runtime/graph_executor_impl.h,sha256=xNv6Say3ma6wDV4mkbol5wbdBCsL5xE4LsrhOBjZ4LA,4027 +torch/include/torch/csrc/jit/runtime/graph_iterator.h,sha256=pSfARVvHC2XCw_p83DAuKxfLgvSJOgvkAB5dFXVE8LQ,4938 +torch/include/torch/csrc/jit/runtime/instruction.h,sha256=z1R5fLzff85MErdJopGVILbT-7Jii2ROWeRMoDSBoJ4,5621 +torch/include/torch/csrc/jit/runtime/interpreter.h,sha256=2ODOp637NsmVNNR9GyclZw68wEJIDPKj6wY72gtlL18,4991 +torch/include/torch/csrc/jit/runtime/interpreter/can_emit_inline.h,sha256=0dr6O-DLLiExHby9g0nkT8mSC8QuCf84TrqEZcwJQLE,3954 +torch/include/torch/csrc/jit/runtime/interpreter/code_impl.h,sha256=LZt5sSGjWQf91tXMApFPdLHK_TzEg5YUbMMIXwPFEvI,33299 +torch/include/torch/csrc/jit/runtime/interpreter/frame.h,sha256=pnTBQNcshr5RjYee2Ix4D3la94TE9aJrG-hsZYUT03Y,1110 +torch/include/torch/csrc/jit/runtime/interpreter/preprocess_graph.h,sha256=Pkri_GGRYaZrhFfdzMRpBsMoEs9uykssBedGeE860Ec,401 +torch/include/torch/csrc/jit/runtime/jit_exception.h,sha256=O0Wa-C7GGWdeuJAhSyc3nEdSi9JvGI5FsWFugl9fgN4,1169 +torch/include/torch/csrc/jit/runtime/jit_trace.h,sha256=lDX6zpQ7WCm0zTKNpj_dezBpR59DI64d0k6UaJ8eLIs,207 +torch/include/torch/csrc/jit/runtime/logging.h,sha256=DiXbsvhSyBecmMquvado2d7eVFgxIx3FL81kM1Su_cw,2614 +torch/include/torch/csrc/jit/runtime/operator.h,sha256=NrjOW5tFovDXzwGBloPbGll6LxM4frCzE5b462bss8E,11618 +torch/include/torch/csrc/jit/runtime/operator_options.h,sha256=T3eF5cJyP0Gfv-8-8UDZQ67GYAznVFv4_S5MOr6QEow,164 +torch/include/torch/csrc/jit/runtime/print_handler.h,sha256=7BhckncLpvB1v1OViPjayXW-gMIDSyJ3HWc8ghENgBg,308 +torch/include/torch/csrc/jit/runtime/profiling_graph_executor_impl.h,sha256=rrsmCDhhjzfkezmFGg_yWsgR28I80WZiJlJWAmqCryU,2958 +torch/include/torch/csrc/jit/runtime/profiling_record.h,sha256=j5cXWqFSi4hxM1ncv4Z6u6kHOVf3c2f1MWlU1rqHq9Y,8543 +torch/include/torch/csrc/jit/runtime/register_ops_utils.h,sha256=e__Cq9TpkJCZUz4_hTxhf49EFWC7OnwZgC2dnqzA0BM,42612 +torch/include/torch/csrc/jit/runtime/script_profile.h,sha256=LhC78onpEwb2BZ0TeZ02UrfnrjFlmtw2peDWKKDgTE0,2613 +torch/include/torch/csrc/jit/runtime/serialized_shape_function_registry.h,sha256=TOfPLKlvlilf71WrOfkPCaCH0aFbnBCxFHSvxSAmEbE,356 +torch/include/torch/csrc/jit/runtime/shape_function_registry.h,sha256=E15CTZzgUVWvdzS4WBzzBLp53qHpRGIqi43STGjiymE,243 +torch/include/torch/csrc/jit/runtime/simple_graph_executor_impl.h,sha256=yMJXCsP6d25Q4tSpVc1aUNHbeuWT8Eq2Jw4Jzk8LSqE,643 +torch/include/torch/csrc/jit/runtime/slice_indices_adjust.h,sha256=-7bG6aoqU0uEXd4kXn8UNX63r3EETlWK8tZqBYiHhxI,782 +torch/include/torch/csrc/jit/runtime/static/ProcessedNodeInputs.h,sha256=GsJuH7z24G8SCunLMMeaVbJTN5dANmD-KCxHxIkcXlU,6403 +torch/include/torch/csrc/jit/runtime/static/fusion.h,sha256=xMRe2CpStACEatBCFnbzLnqYnppUPdJ2KHA4-zzdaKM,307 +torch/include/torch/csrc/jit/runtime/static/impl.h,sha256=t_SyaYN4Lo8-8XxNKKQMeKPXDYNU-wqiW5Oku0K7gHM,35970 +torch/include/torch/csrc/jit/runtime/static/init.h,sha256=dBoy_-IsqUy0p1muZ4g4C3ey3opLV6zB3zlILs5JYXM,149 +torch/include/torch/csrc/jit/runtime/static/memory_planner.h,sha256=UAPe3_yVTg56LapKDXRb2ipsSvG8_b9p6uuAA7rB0dI,9917 +torch/include/torch/csrc/jit/runtime/static/ops.h,sha256=EEjfw-Zeh8pHbhuZ7zUWBKMDYNl56xCtCArzy4_L1lc,5548 +torch/include/torch/csrc/jit/runtime/static/passes.h,sha256=4FWM-FvTIvBj-lY46iES68J1XEUpvbejwst1Twb80YU,3684 +torch/include/torch/csrc/jit/runtime/static/processed_node_wrapper.h,sha256=2qfRw-LkEHagsV5t8EGzWWjnX1m-PPN92TtNPV8BAfA,6595 +torch/include/torch/csrc/jit/runtime/static/static_method.h,sha256=zkTGqaMpr92KYFazR217cDP4E9xs_NbXO-mS_im-kIM,1324 +torch/include/torch/csrc/jit/runtime/static/te_wrapper.h,sha256=JrWPA8KQpYpucDSc2QY1W-RYxUBiwxMGjd5kxDjBqpo,1145 +torch/include/torch/csrc/jit/runtime/symbolic_script.h,sha256=MqvYc552QYwDJwu5Y3d5VQ-Ty4_PTN6l7Z79AMZ4yKo,562 +torch/include/torch/csrc/jit/runtime/symbolic_shape_registry.h,sha256=wvBFq-NqUqFJTSxmlZLPzWkyPzByX8_XWSC0RHLYTDE,2802 +torch/include/torch/csrc/jit/runtime/symbolic_shape_registry_util.h,sha256=7p1cL2YS8jZuGeWI48XXETdslPoD9tm0_0FU_p-kWVQ,351 +torch/include/torch/csrc/jit/runtime/vararg_functions.h,sha256=4YE78NgzdL8MxckVRrej9O0B_JXQkcKJhxbazpoHXBc,1147 +torch/include/torch/csrc/jit/runtime/variable_tensor_list.h,sha256=BLJu7wftFO19tgS1o_8g9Vo58vKoBqtdLiH0pYMCbdw,527 +torch/include/torch/csrc/jit/serialization/callstack_debug_info_serialization.h,sha256=yVyHpm78281r4Lddh46hDRmWXA5BpUyUK70ume17TeA,2604 +torch/include/torch/csrc/jit/serialization/export.h,sha256=IA9c3v9dy6cYlZ7xOfohBhYi4Xo44J2dz8hAUKklkoc,11540 +torch/include/torch/csrc/jit/serialization/export_bytecode.h,sha256=guqXICFsEir-QQ5VQS0KW2ff1gGh1o71NY_azN8D7oQ,1368 +torch/include/torch/csrc/jit/serialization/flatbuffer_serializer.h,sha256=V93r7sz8Ps8ZYqSOQoKEpd79Vy4NRpKlhyrjdMTSfFY,3043 +torch/include/torch/csrc/jit/serialization/flatbuffer_serializer_jit.h,sha256=ba6YMg9NRZIVrlgxuY11mdJDX2cMYms29cjLJGJhqGE,172 +torch/include/torch/csrc/jit/serialization/import.h,sha256=CdhrwoaPvQkM8yQ0_KfDRWPPxRR8a60kNf6WfqqqRbw,5009 +torch/include/torch/csrc/jit/serialization/import_export_constants.h,sha256=zkFq2CpJrrGZiezd_BKq5vXJRVWvD_ZBoKC_yIX0ngk,645 +torch/include/torch/csrc/jit/serialization/import_export_functions.h,sha256=GwP21rWSdHviVfH-qz05DrAQcKjCR_hKWLJ2gmlQNsg,389 +torch/include/torch/csrc/jit/serialization/import_export_helpers.h,sha256=JnAtp7YRC7X-Z3KUtm6A-lNk0bkRwhWEILm5ic6JliA,651 +torch/include/torch/csrc/jit/serialization/import_read.h,sha256=A2R8oRS_-CXTjOYDe2oBGcSiT1KXilq1E6rqYkJkr8w,832 +torch/include/torch/csrc/jit/serialization/import_source.h,sha256=50CV7J9Onkp5JEVO3LN9HI6pOgU27R1_5jBwXSwsBxg,3424 +torch/include/torch/csrc/jit/serialization/mobile_bytecode_generated.h,sha256=iro_aAKgetoxhqWT8_245l_OdgBQEXh006I1CLXdQ70,98610 +torch/include/torch/csrc/jit/serialization/onnx.h,sha256=y9W8Vp2prGtLkqktDi18KSp0mhta55dsGDoDpmH9ono,513 +torch/include/torch/csrc/jit/serialization/pickle.h,sha256=Ad87x2XtbWJT1lJ7fWorSudOVhbiqz6GYQLOSyPeXsk,4690 +torch/include/torch/csrc/jit/serialization/pickler.h,sha256=SqZbkSXZQwBeL_OaJjbcCEeGbArV8Kzb-QVTsimEuvE,13527 +torch/include/torch/csrc/jit/serialization/python_print.h,sha256=x828WdBN3KGyh1Qtx82eTZrDMUqtrwz6Gkyt2Eh6-NI,1309 +torch/include/torch/csrc/jit/serialization/source_range_serialization.h,sha256=a0-K_Nwk9gm_8_Faxldl3Et7Kqw5nGOUznqCLlZBGco,1666 +torch/include/torch/csrc/jit/serialization/source_range_serialization_impl.h,sha256=5ygYiNxXUq9rzzgJWN42_0XNVApa1-9G0MN3XFuuvTs,680 +torch/include/torch/csrc/jit/serialization/storage_context.h,sha256=7Y4_iJw9YtZ-RZ9yvx3_MB8H5ldo-s1q3kRhhvpmSCI,2487 +torch/include/torch/csrc/jit/serialization/type_name_uniquer.h,sha256=DYiGL3VcdzINSbyvH3FPXrLjeYQFj79-Y4cJtlxwrMI,754 +torch/include/torch/csrc/jit/serialization/unpickler.h,sha256=4qber9qv-IUF43lBNaQsMK9REClGUkIyvdZXScouev0,7649 +torch/include/torch/csrc/jit/tensorexpr/analysis.h,sha256=dqXdu0WroMBA2aE64FKskiD4lNKle7D_ks0WYESb8Kc,8982 +torch/include/torch/csrc/jit/tensorexpr/block_codegen.h,sha256=yh8ltX490BoS0jRJAhfUkDjoypHaNdOzMauwCrFsgTA,4290 +torch/include/torch/csrc/jit/tensorexpr/bounds_inference.h,sha256=Sd_Ayh9IjI_4Mageyoo_poJ74ifE-BjauD0E_yBfStM,2170 +torch/include/torch/csrc/jit/tensorexpr/bounds_overlap.h,sha256=JLeAwRP9xPVf5kGFDJTPvhAdb4xoQSCkbLwHG7jZsmk,4471 +torch/include/torch/csrc/jit/tensorexpr/codegen.h,sha256=0mA4KEJ24aEM3tcjKl7jPeTKP9RGD4KfEuFtUIk5WUo,7436 +torch/include/torch/csrc/jit/tensorexpr/cpp_codegen.h,sha256=C2OoTkNUamFMZh-_DM-XOnwqdap56Qo-6hdwqctOVWM,2347 +torch/include/torch/csrc/jit/tensorexpr/cpp_intrinsics.h,sha256=7wyjc4Ns-_peOesywng4toC-x16AfUcmGc8171omrsE,633 +torch/include/torch/csrc/jit/tensorexpr/cuda_codegen.h,sha256=LvAQ84ENqSwE40QIAaT9F4Nq2FyaQVxJaql1JBieDWQ,8234 +torch/include/torch/csrc/jit/tensorexpr/cuda_random.h,sha256=9B8UrIm_XmVUVsx5nDvhojVFywutq8VUUVNUlY9Bvfk,2592 +torch/include/torch/csrc/jit/tensorexpr/eval.h,sha256=PqWZ3IbrH6TK0Do1iPYHXO5XTmFI95-uOWB-qoZwwgQ,9866 +torch/include/torch/csrc/jit/tensorexpr/exceptions.h,sha256=4JoBnkTIs0YuVYRx_SMr813zihk6YFZOADG-8Xhc0ig,3193 +torch/include/torch/csrc/jit/tensorexpr/expr.h,sha256=KXae4qxJqcOgufEwplpnocNimZgwjG81oCF9A-dHZXQ,14181 +torch/include/torch/csrc/jit/tensorexpr/external_functions.h,sha256=ZF5vgZUi0-c0qWdvghIx5vkDImFfpYNuGGYfZGrSfDg,3433 +torch/include/torch/csrc/jit/tensorexpr/external_functions_core.h,sha256=5M6ljT1lNcKQNBt6fLnRQfr-E9Ui8W5yNjiCRM6trgo,453 +torch/include/torch/csrc/jit/tensorexpr/external_functions_registry.h,sha256=relM7bB7SFM1ZoOtylwY0Gm4CxI4ADoezq-uYoejM68,2306 +torch/include/torch/csrc/jit/tensorexpr/fwd_decls.h,sha256=QzBwXKkci34ADFkGLoZjCiij52E7qLX-W6OGLxtYSBU,2990 +torch/include/torch/csrc/jit/tensorexpr/graph_opt.h,sha256=3hTzsJhun7izGmDp-OjSeISFCc6nqP7e9q5HfZiq6qc,4434 +torch/include/torch/csrc/jit/tensorexpr/half_support.h,sha256=PjNR8uWlCVZSo95jmK6bXPZVp06tgk0ln37Ng2GoqFY,5889 +torch/include/torch/csrc/jit/tensorexpr/hash_provider.h,sha256=M4GJoCSguwWp54GDZvPoT67_pmwHlm3qNDEelcXTdSc,7533 +torch/include/torch/csrc/jit/tensorexpr/intrinsic_symbols.h,sha256=xFMYwrG9FzKJKOWVZSlyRaRn3YPR8SuDDAo9Md0GPVQ,420 +torch/include/torch/csrc/jit/tensorexpr/ir.h,sha256=Z4A7ARR5F3po64Xqhi8-lr7LPEOI1B0rZvdxX9X_9qk,22955 +torch/include/torch/csrc/jit/tensorexpr/ir_cloner.h,sha256=3d2hhPetePZn3XziNI0XTKL98FflyEp4QlXy86rGxZo,2343 +torch/include/torch/csrc/jit/tensorexpr/ir_mutator.h,sha256=4_vAeBWdogaDiNiCI_F_KxQiLacp2mCbCYaWnkupBjg,2369 +torch/include/torch/csrc/jit/tensorexpr/ir_printer.h,sha256=NJlLMgJ17DS2VLunSaHG-LP200YeYL_p1KzQzm7e5Rc,4192 +torch/include/torch/csrc/jit/tensorexpr/ir_simplifier.h,sha256=Y45v72urx62JrZcqnYpYzX_aAaM7TdQeGOKvy7mDpxk,15257 +torch/include/torch/csrc/jit/tensorexpr/ir_verifier.h,sha256=UcH6svLM94zgxg2GETJ_FMZlt9XQ5oxiw2_jmpbbeTo,1295 +torch/include/torch/csrc/jit/tensorexpr/ir_visitor.h,sha256=h8LzJsF7__3r82pTCKQa5TyUxHIL0VrPLZy1GSqHVnw,2187 +torch/include/torch/csrc/jit/tensorexpr/kernel.h,sha256=u2cYcfwJCSaVWMEkEmeSrJxujhfj8zyosDNFjmbV1iU,13392 +torch/include/torch/csrc/jit/tensorexpr/llvm_codegen.h,sha256=xkc8U7lUwg9BS5lHO_hIzrbh6hAixZTGGkd4PCS0orI,3839 +torch/include/torch/csrc/jit/tensorexpr/llvm_jit.h,sha256=kvQmcGAeqrXGtkqslvwf6hSEJHY_Ii9EEBhIdGGZy7k,1940 +torch/include/torch/csrc/jit/tensorexpr/loopnest.h,sha256=SmifRvgsMGDgoAhWgQtgQscegexh5dWm0pG_M_rvJ-4,21707 +torch/include/torch/csrc/jit/tensorexpr/loopnest_randomization.h,sha256=B4UQ_zRjQAbOzMqTmIO-nwEffSuuC83XuTjI4W4L76Y,309 +torch/include/torch/csrc/jit/tensorexpr/lowerings.h,sha256=Iq4-ttSxTzkWmHRN7htrJosQ7vTUgnmRAyLdmmdAitA,1280 +torch/include/torch/csrc/jit/tensorexpr/mem_dependency_checker.h,sha256=lwu_rOUnjG70SxRruj7yjNDcMuXjKMdOj4k-HG8GweQ,13461 +torch/include/torch/csrc/jit/tensorexpr/operators/conv2d.h,sha256=OHOBHwyBY4qva6dQsOuskR80vsOw0OdBosiE7UWWjnQ,2893 +torch/include/torch/csrc/jit/tensorexpr/operators/matmul.h,sha256=-_vDYdI6UntwJvyZLPGW2EASGZJ-ShZ2D2r4cCzFsJo,603 +torch/include/torch/csrc/jit/tensorexpr/operators/misc.h,sha256=ksF1cQfgYYxV_o3ceicVffTquaX3oxMF-cM1QnAcmV4,3288 +torch/include/torch/csrc/jit/tensorexpr/operators/norm.h,sha256=YoWAUONdwdM8IZeR-uBYyoG7k_g1ew_7CquN19OYALs,373 +torch/include/torch/csrc/jit/tensorexpr/operators/operators.h,sha256=uUMEOYPpfSOrKklAjsN4YdQD7agTgSCUzMni2PeH78s,471 +torch/include/torch/csrc/jit/tensorexpr/operators/pointwise.h,sha256=Mn7XLze5kfyvQYLsTze4cmB2y052nReXhvjX3_qI1Yk,3159 +torch/include/torch/csrc/jit/tensorexpr/operators/quantization.h,sha256=ukBEDxUwHwoGFJh47tCWCaoQ7Zq3rozQiscPuPt9fV0,5532 +torch/include/torch/csrc/jit/tensorexpr/operators/reduction.h,sha256=E_ietMkgPq-eP9dBtE47eKe1FIoOxYRLBeLpgPZliFY,1105 +torch/include/torch/csrc/jit/tensorexpr/operators/softmax.h,sha256=8Q4zCOon8-V1-yPRzox6bsw1V5aeJy85_4J0dkQiqP8,321 +torch/include/torch/csrc/jit/tensorexpr/reduction.h,sha256=fO2nOB_ePO17G083elLRj1SHIgn_op9oasHL8E6HIuI,8866 +torch/include/torch/csrc/jit/tensorexpr/registerizer.h,sha256=XleTpv4s_rE0efXMTuKN7opbWNeg0rE802fRQe829mY,12518 +torch/include/torch/csrc/jit/tensorexpr/stmt.h,sha256=9-abU2UxliccdnIzYRAz4q7wb3apXZXckcYBSJCUcJw,23800 +torch/include/torch/csrc/jit/tensorexpr/tensor.h,sha256=Ec3W8U9wOjE-JTYlPNuPZtUA7bJsemi067BVSJZT6UM,10499 +torch/include/torch/csrc/jit/tensorexpr/tensorexpr_init.h,sha256=0pkchsNPY_qjXdhviFyXdFCZzEF9Hzqnu30NbxyrF3M,243 +torch/include/torch/csrc/jit/tensorexpr/types.h,sha256=3tTvFI1I_bDpps81BFdCHszEvSVvRsuxkGDOAzIz3a0,4280 +torch/include/torch/csrc/jit/tensorexpr/unique_name_manager.h,sha256=QLXiwan89GB0Afh4NDu2evGokAD8mCrOkWYppBaUeVM,825 +torch/include/torch/csrc/jit/tensorexpr/var_substitutor.h,sha256=5FaoDaztQTQWrL2VFoLe_eyJOu4Gfu-2zYKENx6w6AM,1660 +torch/include/torch/csrc/jit/testing/catch_utils.hpp,sha256=WKblHu_a9sU9mIBZHyVtfWJ66aO-6KmwV6AtlD7Nvag,353 +torch/include/torch/csrc/jit/testing/file_check.h,sha256=H70VH_A5ae4fG_5CKLqDKGcb1l3tkhgHvICV5wVN21s,2543 +torch/include/torch/csrc/jit/testing/hooks_for_testing.h,sha256=0G7K1dRWD_Mkemti-Bs-3lnraTb3haTl9AtMttlt_pU,578 +torch/include/torch/csrc/lazy/backend/backend_data.h,sha256=5PkZAC5_M_co-3sDg4ZPy_1Bx0rUiA5d_zzN4gsIhLs,1180 +torch/include/torch/csrc/lazy/backend/backend_device.h,sha256=ri9JmrjcvxEW3H7JEq6Fa8Uj2nKXlSqDQVyG8f0APjs,2928 +torch/include/torch/csrc/lazy/backend/backend_interface.h,sha256=casb0BOybUi5IKjP807GfHUGhpucS1GscfoWRRh6vi4,4803 +torch/include/torch/csrc/lazy/backend/lowering_context.h,sha256=oc3r9xUE5eXROAwUcrVaVxJIWMTc6YGiRjHEHU_7JmU,3238 +torch/include/torch/csrc/lazy/core/cache.h,sha256=2w58t6jy7Hl6h1L6PsS9sm3xdfqmJDAcU74Hccy19ss,3725 +torch/include/torch/csrc/lazy/core/config.h,sha256=8N31eXBX_z034O6VUeyaA1TDocT5WcDuIxcf0IsJErs,911 +torch/include/torch/csrc/lazy/core/debug_util.h,sha256=uV8OiMvWN_zvwiKhfYEWD0MSttC7EhCGGAUGQ4tqGP4,1274 +torch/include/torch/csrc/lazy/core/dynamic_ir.h,sha256=3Oub9Sy5ptyT172c0E_o61uDCwMLWCl-6Mdu7v836M0,1397 +torch/include/torch/csrc/lazy/core/hash.h,sha256=9je3BZ6JenUtWlHEI4S5ZqvYAHMmexMWmudannsdyEU,7599 +torch/include/torch/csrc/lazy/core/helpers.h,sha256=tTaliPQL5bprmMEp--PoqJ2T6F2r8z36Fm2hyLuRu5A,2223 +torch/include/torch/csrc/lazy/core/internal_ops/ltc_ops.h,sha256=fSbygh6KD4Yy7pUXIguWv2NIoMWId2966Lef-yq5k1g,1450 +torch/include/torch/csrc/lazy/core/ir.h,sha256=eipiWFxFekQjqn_GeHOHSOZkDEO-FlwmLGa97wqJYys,7805 +torch/include/torch/csrc/lazy/core/ir_builder.h,sha256=l9SGQGhHtRFL1cttnCuoFWbVdzTKG92MxAjHYjBRMBI,4709 +torch/include/torch/csrc/lazy/core/ir_dump_util.h,sha256=j5qFBQ2d-8djH7UdEbaLrpirOfB0shTCdpjVV0oBTjM,664 +torch/include/torch/csrc/lazy/core/ir_metadata.h,sha256=cDadOIWncS80ldJkIAWDFR-ZFtgbPFDBXdpkiFNHHgg,1323 +torch/include/torch/csrc/lazy/core/ir_util.h,sha256=QKP8oMvs3lJ_GYhnmiGtiJioUfInrXz2YqRdqLXlA_k,1366 +torch/include/torch/csrc/lazy/core/lazy_graph_executor.h,sha256=7eiljdStcp4fsHWAAaBSDDLvkRmwdxNik1w1Mmn2oAQ,15018 +torch/include/torch/csrc/lazy/core/metrics.h,sha256=xlNx-TuSzBAOj2jEAPoEhCPv7NgRlOLwyog42sl-uYE,8251 +torch/include/torch/csrc/lazy/core/multi_wait.h,sha256=aNRG7qK4oBA8XHEiABHu4WXmslESz6JNLKWrHDWpo3o,1749 +torch/include/torch/csrc/lazy/core/ops/arithmetic_ir_ops.h,sha256=-ootnGcCDxrf1WclZ1Bl-ByOoUW7KZFk3CT9naKb57U,381 +torch/include/torch/csrc/lazy/core/ops/utils.h,sha256=7VyDi5xSCiDwVxVR4LGK0eUWEcRyFYUsXNTv4WizmLs,985 +torch/include/torch/csrc/lazy/core/permutation_util.h,sha256=_iCPd249YvvBWLI2haDK_1HwQ7fi5nUuilq6FPV158Q,1252 +torch/include/torch/csrc/lazy/core/shape.h,sha256=XJqkvqplPMoylBt-dJ7n7rH-rjdRgR2OknZDnn6LWbo,2015 +torch/include/torch/csrc/lazy/core/shape_inference.h,sha256=OrVkzJDcynGPqO-y3fcmQfvmW7Fb59lJi4CaVNp1Uog,15402 +torch/include/torch/csrc/lazy/core/tensor.h,sha256=w4gVa3kMZ9huUv1PZM3Ufp1IznBzG4igO4HOkKr6hfI,9869 +torch/include/torch/csrc/lazy/core/tensor_impl.h,sha256=DEq0J7CbJjDolsi_yXuVleHvP9HjvkxYfarD6ALml4w,1885 +torch/include/torch/csrc/lazy/core/tensor_util.h,sha256=IOlEAKyVv1GVngQIZ55GNAJxmWUSfcIX92L_YQdaOCk,2536 +torch/include/torch/csrc/lazy/core/thread_pool.h,sha256=ZV0BvwerdUpFVqxzh6_OKLMpy8SfedxY7Px0VrTdO9w,792 +torch/include/torch/csrc/lazy/core/trie.h,sha256=9Mp2u4zCRbju9x22EAzttzicJzl0KAZISfO85yIUlGY,2192 +torch/include/torch/csrc/lazy/core/unique.h,sha256=DUM_FLlyBViT202ZMa2emKSEblosUXM78CbBmevZRC0,1173 +torch/include/torch/csrc/lazy/core/util.h,sha256=hqs-aT9NdbfA8hdxse2zQ_jz7Jzwmj92Ch5QMibddNk,2873 +torch/include/torch/csrc/lazy/generated/LazyIr.h,sha256=Lx8u3WIuOs61UKdNJ1efgy61z5qJ75yH0YT_dnRXeDY,295822 +torch/include/torch/csrc/lazy/generated/LazyNativeFunctions.h,sha256=jhSN3TT-X93GqJ9RRk6d4ZRYHQgA5uHn7gPrdCqtZZg,22738 +torch/include/torch/csrc/lazy/generated/LazyNonNativeIr.h,sha256=6hICbyVB7W9ggzB2AAIlrz1KRGMiyco-B4Rfheun3jY,3943 +torch/include/torch/csrc/lazy/python/init.h,sha256=RC3mFsGs2OCJyMcAFTcCefnVQZCGvNGRZulwXgwsbHk,224 +torch/include/torch/csrc/lazy/python/python_util.h,sha256=5l09SSxlf2GKSnGFbzRFT47oAfbh8SqUcrt_tz39WU0,315 +torch/include/torch/csrc/lazy/ts_backend/config.h,sha256=zv0peK-Xe8qEHyXiRAgUtA-83VcnHtDAC8mYTBx3Kug,206 +torch/include/torch/csrc/lazy/ts_backend/dynamic_ir.h,sha256=YsFdKogLiOL9idtxZzW3_IecjN6XsnIIUHZjQrO3cgU,2356 +torch/include/torch/csrc/lazy/ts_backend/ir_builder.h,sha256=Iobg4Ti6YNUbUQUObVv2G3wVYkekZsNNzEIj-Ks-dcY,2391 +torch/include/torch/csrc/lazy/ts_backend/ops/device_data.h,sha256=zibVUqkj0qnrl2tSYp_lkrhAR3sZtPjbXsALuZ1rI1g,1296 +torch/include/torch/csrc/lazy/ts_backend/ops/generic.h,sha256=LmTxwKKU8nMNbd2l3Srjc23nnQon_F6bph2EwdUQUIw,1428 +torch/include/torch/csrc/lazy/ts_backend/ops/to_copy.h,sha256=MbcGLd1vCSZb5SCd76Qpxlt9S7IwiTqo-1vhsm7FT-4,4079 +torch/include/torch/csrc/lazy/ts_backend/tensor_aten_ops.h,sha256=3ouYSBeF4A5TG9A1Q9VQq88HFGNFag6yMXccIFq_MsQ,523 +torch/include/torch/csrc/lazy/ts_backend/ts_autograd_functions.h,sha256=ZIbbA3Jiw_Nl6tB1BcXZvyWpODVFsxXPOAtLQAZYzpE,626 +torch/include/torch/csrc/lazy/ts_backend/ts_backend_impl.h,sha256=zn1nchJ9sfyyJaOTEYwZIx7qyyiJACtz2H4KB4KA8Vo,1240 +torch/include/torch/csrc/lazy/ts_backend/ts_eager_fallback.h,sha256=ItBlp85x8HRd1j7c8kFiF_bYiqBBDWx3n9NckLQ8hQs,692 +torch/include/torch/csrc/lazy/ts_backend/ts_lowering_context.h,sha256=efKAbR7kpRS-gl-DlDx_A-I9YYwMNFcEvIbDLblSSs0,4508 +torch/include/torch/csrc/lazy/ts_backend/ts_node.h,sha256=ejlQuqQzwhtqlTTpMlXwYtTsZ0WP8lKN_VsMrQ24U-k,3351 +torch/include/torch/csrc/lazy/ts_backend/ts_node_lowering.h,sha256=FJ8DzHppnR0tX0SuXGzL7w0Ro8T3FW7rZRLHHUp6LhA,466 +torch/include/torch/csrc/monitor/counters.h,sha256=g0FZnh228GgxWUz4iuEWACTQ9TBreniPHzfWo4peyFE,7843 +torch/include/torch/csrc/monitor/events.h,sha256=uj-trCVXnB-i6Ql23YjyySl3BY7Yx9p7dKaDtAMyjio,2692 +torch/include/torch/csrc/monitor/python_init.h,sha256=_FLxEweZN8oNqEBk6D-rsJWqz0Hd2c1CD7M4wn_VK6o,127 +torch/include/torch/csrc/mps/Module.h,sha256=I-wAIO66X55C0siGIO6r8ewVyAh5cCVQibuefxdWIM0,173 +torch/include/torch/csrc/mtia/Module.h,sha256=gLPcR83gp2l7jsfoezy2wmyd2x0L_Rru-7Z3uYJgRxw,178 +torch/include/torch/csrc/mtia/profiler/MTIAMemoryProfiler.h,sha256=96iwQtBgyp1NSwR_9ynMvgwManvxeA_9eALUewE9AJs,547 +torch/include/torch/csrc/multiprocessing/init.h,sha256=zaipB0MfeYjJ-4yajki_G_qaL6f85r4gfSIZk9oVvAU,168 +torch/include/torch/csrc/onnx/back_compat.h,sha256=zc5K7G4NO3cMvNW8Uz5xvoUNnw5G0sZfc0j5Xwq4i8E,1024 +torch/include/torch/csrc/onnx/init.h,sha256=ZXAqnv9mhvV3QHMdSwzKprQOSAa-rSi3pcABk0aY3a4,146 +torch/include/torch/csrc/onnx/onnx.h,sha256=maYqI90ZK-RgvBNCc9ANFAk6pSALQ-32igUXLvyoMvw,505 +torch/include/torch/csrc/profiler/api.h,sha256=2zxTYEcugn1jicqDNnSJ84Lw1xsjlGMdvbmDl9coJCc,510 +torch/include/torch/csrc/profiler/collection.h,sha256=goDylsB68FxDq5GE7gHuS1YtdAL-vUcqEd870viKbAQ,20768 +torch/include/torch/csrc/profiler/combined_traceback.h,sha256=xwV1K_fHpnblNCdp_69zKLbw2PxESKY80gtskCzA0zs,2457 +torch/include/torch/csrc/profiler/containers.h,sha256=9uYo_3wdRcgVYooBYUiDB70ItUZ6GBWNZ3FUMVLjMT8,5994 +torch/include/torch/csrc/profiler/data_flow.h,sha256=IP69ntlzZNU9-suktXydOxNxQA_VajkquzxsDvc5pZM,3626 +torch/include/torch/csrc/profiler/events.h,sha256=Qvs2E3t1Um5YteMNhEqBaprVNaDB1v23FxKxV4bfuzA,1036 +torch/include/torch/csrc/profiler/kineto_client_interface.h,sha256=s0IEEx5reuFbQgXIyXOJgAgy4TyQuRepgyDlrGF3fbQ,246 +torch/include/torch/csrc/profiler/kineto_shim.h,sha256=_VidmusW4xNliXKCifWnPQS4dU1NrFAVjfE1p-I47nU,3915 +torch/include/torch/csrc/profiler/orchestration/observer.h,sha256=7P2brNongfFIfkjVpB8pdrxAGnahSy1vDoMN1IJINSg,6807 +torch/include/torch/csrc/profiler/orchestration/python_tracer.h,sha256=8ehydItKsfAoEzd8YhcBJouswMHulxuvp4p2qk-YhV4,2261 +torch/include/torch/csrc/profiler/orchestration/vulkan.h,sha256=koI8ajtify0fM2NEZmwfsLeWT-P6AzeJqYD2NZbOMSw,776 +torch/include/torch/csrc/profiler/perf-inl.h,sha256=Nm6lsLMAbshYhgON0Nh5V8XIQFKw4ze-aJ2JKhJ2ij8,1404 +torch/include/torch/csrc/profiler/perf.h,sha256=cjPMIhefuLLcTU4MruhIB3eHJIudCpxluygdIfJGcTk,2584 +torch/include/torch/csrc/profiler/python/combined_traceback.h,sha256=WouWwwSbtg_iw87Q7qH4w4ph2xw6a6LcvxbcLPYRtXY,992 +torch/include/torch/csrc/profiler/python/init.h,sha256=k-KYuPiMMAXyPewc8WGaVtHICqDVCXzBIBsETJTMNK4,998 +torch/include/torch/csrc/profiler/python/pybind.h,sha256=Xqmz82b0PXDrjvGToa5R4kIGz-3zwtya2Tv2Oyo8IVY,1261 +torch/include/torch/csrc/profiler/standalone/execution_trace_observer.h,sha256=OTUFGUGZ6YnqN-EH-Xdv94cWjA7Qi7vLilkvv5Wp8TY,628 +torch/include/torch/csrc/profiler/standalone/itt_observer.h,sha256=Uw0651Atw9iSKltiRYG9UFbPpgVz9bvllEKQROw5JVQ,224 +torch/include/torch/csrc/profiler/standalone/nvtx_observer.h,sha256=l1AKhCifuqehvjZdoZNkAweChgyu9OXtl3fkTD0jDB8,225 +torch/include/torch/csrc/profiler/standalone/privateuse1_observer.h,sha256=Hw7T5ebx_L_NpJ_nPv0P6OBZeEa-nhnFL0fhgl2F_p0,1445 +torch/include/torch/csrc/profiler/stubs/base.h,sha256=VqtpDKExtTiPDhq6blcnQ1dwrLmkCz0udGTbISYCSiE,1685 +torch/include/torch/csrc/profiler/unwind/action.h,sha256=_sJXIX3NwudAL4XgwRP9CgGu9Vo9mWqLk6xYakZ_yw0,1424 +torch/include/torch/csrc/profiler/unwind/communicate.h,sha256=SC9fyMRnWBeGoAgv0kSQJRSEcX0jpQjbIkJ-GETxDTk,2252 +torch/include/torch/csrc/profiler/unwind/debug_info.h,sha256=jjJOs7zRXTk_KBlJWd5moN6RMBgH34n643_uYJG_2wY,9100 +torch/include/torch/csrc/profiler/unwind/dwarf_enums.h,sha256=Ojxlhbz2WvETGEnqFq4XH0Oi7d4zYg4nRU3LgyA6gew,1110 +torch/include/torch/csrc/profiler/unwind/dwarf_symbolize_enums.h,sha256=kXzFzct1qxjU74GzRt9vcELvA2pSO13koBjv5nvP39s,4658 +torch/include/torch/csrc/profiler/unwind/eh_frame_hdr.h,sha256=W0NDClGsnyCD29_VDT28wkQN6RgV_AVJwJLQkhG9764,2680 +torch/include/torch/csrc/profiler/unwind/fast_symbolizer.h,sha256=V0mCniYHIDovcTX4aDCvPyfcWZiIuByUpK3KCOq7Umw,3303 +torch/include/torch/csrc/profiler/unwind/fde.h,sha256=qFUzk0UEvteJussmg0trmnVGD88kluR6lzCnH1ORPIM,12459 +torch/include/torch/csrc/profiler/unwind/lexer.h,sha256=kpwWj64GjeyTvljCWCe9wh3quW_MPsDM5RNYcNj_KSE,3881 +torch/include/torch/csrc/profiler/unwind/line_number_program.h,sha256=sqLiPn2VL4SeDbyJtupdrpdXud1tLTEEn1g8mNkJWWE,10825 +torch/include/torch/csrc/profiler/unwind/mem_file.h,sha256=_TRUjqdoVUfEbQuTy6B2jWyxafum9Inova3jVvIhGOI,4589 +torch/include/torch/csrc/profiler/unwind/range_table.h,sha256=jUibWQ30VWVCtCGuBxRGLZEca24pKaujaRrlHkAfI0c,2109 +torch/include/torch/csrc/profiler/unwind/sections.h,sha256=fBh0rkMG-cGB34EN6ChSKvtNbZvCaIfn4Ezzv5J03j0,3668 +torch/include/torch/csrc/profiler/unwind/unwind.h,sha256=_RIMx9qlo9Hn7MmVAzhUNL2ppFq0unGS6R9WptOAaIc,1133 +torch/include/torch/csrc/profiler/unwind/unwind_error.h,sha256=G7b3ObxTirwGw9EjNG342Ou09Qerz0PWjKdcjmAQ5Ag,898 +torch/include/torch/csrc/profiler/unwind/unwinder.h,sha256=wZtP6Dl2KcWNgrKtQPhHDbB-1M_BX_lv93ufj-nSyGI,2293 +torch/include/torch/csrc/profiler/util.h,sha256=TVj5yggT4Jx4X4YjQNoTylxMkc04v_halpqnCjstxFk,6856 +torch/include/torch/csrc/python_dimname.h,sha256=-Bk8OBU36eIs9dyAMtpnfXkfa5KgFVxxrLKjhAem5sM,214 +torch/include/torch/csrc/python_headers.h,sha256=ZjIaoShls4JWN3vgElKkmNlZuulOavbFlgugV5L7R70,649 +torch/include/torch/csrc/serialization.h,sha256=olcwp7eeOqqpLV_hqyjMf6FAn4ppF9vonr7Sflay_T4,681 +torch/include/torch/csrc/stable/library.h,sha256=4rxxaj8qyKfXVyJ1Kls_FHzb1XmF2XQ_W8bH0vmEV_o,13565 +torch/include/torch/csrc/stable/tensor.h,sha256=hHN2c_tr0bQIUPjJzTgvKH3nSZKG2QK_56rYIfQgOkI,4402 +torch/include/torch/csrc/tensor/python_tensor.h,sha256=Fkwj_nM3dMq1Vv0XpwP3bVFW4jZaUoRtAMyC1gqfhec,1200 +torch/include/torch/csrc/utils.h,sha256=WLAhU-xTJk8Jm6wfGAQXTUyM4eQGmCjL6r9tudzMufM,9105 +torch/include/torch/csrc/utils/byte_order.h,sha256=cctDr9EyNmBoFJi65wSZ0VhrV6dXdbB7-LFgMh04I2o,2254 +torch/include/torch/csrc/utils/cpp_stacktraces.h,sha256=MbynZ32tmwy-9y-REs2kHEfAzth_NVJKt_8qCYn4yko,230 +torch/include/torch/csrc/utils/cuda_enabled.h,sha256=1hv3GlF9I_yK7CTNCGJ1Re0jgcwUXsMFWFYlU8xFmWI,170 +torch/include/torch/csrc/utils/device_lazy_init.h,sha256=hqg-d_qtnIl2yAi-NarjKPgL3mwVJGPiVLxkin94tNM,2814 +torch/include/torch/csrc/utils/disable_torch_function.h,sha256=wrQ-qijlK9haBctyiV0gGcKsjekvZW5jNjwzpe5G688,1868 +torch/include/torch/csrc/utils/generated_serialization_types.h,sha256=A5Sf_D-pvjrt4X_qglzk5aNdhGB_d5F8QYRXiKHhuio,116892 +torch/include/torch/csrc/utils/init.h,sha256=s3Sas-t_M5fl8aS8DXoZ7-4a53iCpvuvklhprvLx7YQ,193 +torch/include/torch/csrc/utils/invalid_arguments.h,sha256=dXhIUSgk21JztsfxdrsNWq40fQOVhPLmMWRQocy_q3M,302 +torch/include/torch/csrc/utils/nested.h,sha256=5OZ6lzhetvHDaJT-LMHB6e7M2whcimwDDhOZ-8gKogk,306 +torch/include/torch/csrc/utils/numpy_stub.h,sha256=1m2vyb-8t2lQGtylJZrcH2cw8nHX_OEtyKk7Q_jGg34,399 +torch/include/torch/csrc/utils/object_ptr.h,sha256=VuPzddh4-kGdTZg7GxdAihFDic7lHrHxAxgekC2scpw,2000 +torch/include/torch/csrc/utils/out_types.h,sha256=45_LZPP_vy428wm05bdf0_j8EaDd7ekf0IHj2bVnDKE,320 +torch/include/torch/csrc/utils/pybind.h,sha256=nf2atdhPc4buyjb19idw-6FINfnHW6rLGHJzYmCfA7g,13010 +torch/include/torch/csrc/utils/pycfunction_helpers.h,sha256=4_LQ4gsIR9hC6vI7cHEW161VW8KxurlJyR3LvHQZxPo,385 +torch/include/torch/csrc/utils/pyobject_preservation.h,sha256=G0h2hb4Ou4yDrLX4UuNhp4-71hOioOugzTzqBilRFzI,181 +torch/include/torch/csrc/utils/python_arg_parser.h,sha256=0y2tuGCRWPi-x0HLpjfQguhRi7cESw4lljUJMvjJPR4,41554 +torch/include/torch/csrc/utils/python_compat.h,sha256=MOWNe7NCLE_IXFfGDcJ5n4KuBo-lvdVCof_MgvxZca4,1177 +torch/include/torch/csrc/utils/python_dispatch.h,sha256=wgSvQqmVhE6o2nSBRSMzuUxXD3BykfXxFN8LrwyeWQQ,397 +torch/include/torch/csrc/utils/python_numbers.h,sha256=kXdkoSa1Ha2af9nkDKweNHgiVNn5bVGAS-1E7bS1Z9E,5451 +torch/include/torch/csrc/utils/python_raii.h,sha256=cKNRfAaS4gd9eX-ZEXcrfkhbC1xQe_bJSCIYcS4MCUw,2658 +torch/include/torch/csrc/utils/python_scalars.h,sha256=0B3eh9OkkKWRg-YX_14y9GZxdo_-TSI4IZVr8NNI1sU,6137 +torch/include/torch/csrc/utils/python_strings.h,sha256=sUpmrooBikogEIv1RM004CTbiYkC3wOJNhg_rzkWtkU,4393 +torch/include/torch/csrc/utils/python_stub.h,sha256=Nigc7ZGrniF0qdLn-Ra4KIicNWTBrgARkJYpxs6Ssek,56 +torch/include/torch/csrc/utils/python_symnode.h,sha256=bBlyBmB-Ep0Blc8Otl28IJGglzquw7hpZ1XFXSyhqbU,10152 +torch/include/torch/csrc/utils/python_torch_function_mode.h,sha256=zcOHnz5lI6ASUW217SeTRtS5-ie6b-gCUrPbs6FCnFQ,840 +torch/include/torch/csrc/utils/python_tuples.h,sha256=JEW_jh6JU1IgufLh1yLU5AXNrW7ciXgq1jl4ji5_-W4,726 +torch/include/torch/csrc/utils/pythoncapi_compat.h,sha256=h4GJ-dS8aO-kTM0OmVQNr9CoZvTpB6h_-BfuRRsa3Zc,40879 +torch/include/torch/csrc/utils/schema_info.h,sha256=0MxoKEGsepuC1a0VgvxyzHqYnp-y5RyjlCr5rFxJnBk,3789 +torch/include/torch/csrc/utils/six.h,sha256=6n0o5ilgCWxtETANNkR2t6Q0rqhsITjczVaJ97D9aOM,1489 +torch/include/torch/csrc/utils/structseq.h,sha256=FqMWwrP01RtislZcWjq_AJP4ezz45bLXKMvu49_qQtQ,141 +torch/include/torch/csrc/utils/tensor_apply.h,sha256=22xkgkrqOnkINK9fW3550Ry7gdmbQNBcrE9VMcCa6rs,428 +torch/include/torch/csrc/utils/tensor_dtypes.h,sha256=STkoDxONbre5kkeVeeTp7ZjM0GQaXFoStb9FT5UKG2c,242 +torch/include/torch/csrc/utils/tensor_flatten.h,sha256=ml9PIt-3X-yGWJgw3MY3QfWDYHh_4dDrHnjv6HviZi4,2745 +torch/include/torch/csrc/utils/tensor_layouts.h,sha256=HllPBlHMJI52vdhnMUQABJVLHLzTXw94MX5bNq9Ldsk,69 +torch/include/torch/csrc/utils/tensor_list.h,sha256=NDU92TL1XjVG5lvOtiCbi2svqU8QJUYx6ncdLra7v7I,167 +torch/include/torch/csrc/utils/tensor_memoryformats.h,sha256=vSjxk-I3RkZ9DBChN-SPvhZM5hraH-r2Y0io4cblcWY,323 +torch/include/torch/csrc/utils/tensor_new.h,sha256=AqFH7m6ybOa9XT2nCNj1bqMOlNkIF2TCMdAPDT8FKf8,4031 +torch/include/torch/csrc/utils/tensor_numpy.h,sha256=tDkVdGXKf0RbYKG6MtdYdwpiXxaqrILNg4kLzAsxdJ0,812 +torch/include/torch/csrc/utils/tensor_qschemes.h,sha256=gjMlYXJP9UsN7LPlLM0DX2IjILfIjg8dV7OBCZlue7A,174 +torch/include/torch/csrc/utils/tensor_types.h,sha256=8zrEI8PbBZqd91gRsT3t-23m9oNPVTTcb8-c20LKxho,669 +torch/include/torch/csrc/utils/throughput_benchmark-inl.h,sha256=lBrOUwBAQzeJ-bV_K31n_3JRZN2_DNdnZKVArY0mmdo,6336 +torch/include/torch/csrc/utils/throughput_benchmark.h,sha256=S8jVt9f6coQz7HWOHC8jaLWK4MbM5RR0TfHuObZbaWo,6937 +torch/include/torch/csrc/utils/torch_dispatch_mode.h,sha256=1r16u6lvfE6nf15eE_Pf_f4fyb7LvzxdEgHWrP0habM,2275 +torch/include/torch/csrc/utils/variadic.h,sha256=upMO7qWiA0Tb9QS97J-uZVababBj3IcJqaXxwH_tLNA,3352 +torch/include/torch/csrc/utils/verbose.h,sha256=P_Dr_oOwiENVdRpzlGTBgJ4KOfl8PJI2ep2vCJeLsNg,138 +torch/include/torch/csrc/xpu/Event.h,sha256=JZyGhZ7x6gfee1pt-agLHGEWF3B6cnDoDtDTD-qFDwc,369 +torch/include/torch/csrc/xpu/Module.h,sha256=aqwqxUnsLYZ1g5nJeBmQYmOLSPJ6nPacKpQvuEWz9XQ,176 +torch/include/torch/csrc/xpu/Stream.h,sha256=Hbb8zcNcXj_JdslvzOROLYr77od5OpFGxFsocDiAaNc,437 +torch/include/torch/custom_class.h,sha256=2_XboYL52rriYtBoJ-ldcPTBOAVreWvJSFf9yD0XWV0,19806 +torch/include/torch/custom_class_detail.h,sha256=Hsq6cKIR7WHMLiOs2XFgzDAPd6U916DN_r_Q6T0a9So,7825 +torch/include/torch/extension.h,sha256=jIN4AnAwsUeV6tlDdnaZOlxuABYGNIEwSLtdbhwls9I,213 +torch/include/torch/headeronly/macros/Export.h,sha256=BMbINqcGmr2F4tWSkBn63Vh_HaK0aw2j4GiTlYdBqtw,3407 +torch/include/torch/library.h,sha256=7WsDkRJh3VWpL0w4yrJYbP37INzh9slIpa7gbg_FIP0,42180 +torch/include/torch/script.h,sha256=5qMfjbmTzP84r3BPgSRhTqyjMFGrsD54pxsE6C1e508,469 +torch/include/xnnpack.h,sha256=iJVqWtqWwFxVLpUG0a3W2LqmZ1SZUMIxir0OL1JcoA8,201003 +torch/jit/__init__.py,sha256=oS7Xd6YruE8ZmiPQezhEHI6z_AzsAyACLSXY3SgX3Tg,8344 +torch/jit/_async.py,sha256=_uWj0yoyTVobkY7SumObSP2ivtlXljGoXR7642tJ4jY,3827 +torch/jit/_await.py,sha256=UG6mM5L4tF7NRDT5BfFX4njoM1B_EhdoMT59KsyHF-8,852 +torch/jit/_builtins.py,sha256=IfWQjk8hBJzyZX3StrAMrJTLy5869JeZ4zWALPPJkyw,6791 +torch/jit/_check.py,sha256=CaatU1N-IyF0LwP_Cu9no73lrDBvELIuzIIidxcqzRQ,9386 +torch/jit/_dataclass_impls.py,sha256=0gSxbpCl6CsJyhUWz_zY9AOemcNjudhAsR6jI_nbBpg,6672 +torch/jit/_decomposition_utils.py,sha256=Iq21eE5B6HTWCG9nRYLMgN6syOnwEYREzdTHwrd83wo,404 +torch/jit/_decompositions.py,sha256=UlwWArH5z14_ewnCilfLdM2zaqCwueB6EWvvltu04L0,4374 +torch/jit/_freeze.py,sha256=c62b-W3oeDJq-ogRaXhjvfNobnS8VJoSwOgBMxgGORw,9503 +torch/jit/_fuser.py,sha256=ykvgYjFDSNvmKWhVTZxUYoAuhkPUiuA2UxmdNAZ4PqM,7083 +torch/jit/_ir_utils.py,sha256=bRHCNIHbRGtBg_54l6AKlLihuqheUwy2TDmqKLXXhQk,886 +torch/jit/_logging.py,sha256=jWaacYoHjd_Cao_1hzyPKYiADS7cb7tLQc1gRkZblUQ,257 +torch/jit/_monkeytype_config.py,sha256=JgM_bMrYyXGn269v2rZIpQ1ixQIgyMku09vHLSr3ZcU,7288 +torch/jit/_passes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/jit/_passes/_property_propagation.py,sha256=4L1s-f_Ngp4Abz0Mf5t-BtQlEnyIWV2GPya6N2Om7qE,1438 +torch/jit/_pickle.py,sha256=c8EQuGRAI2iUrXLPtgSqv8ID1CGtSOaDcb7FUk73-Nc,1168 +torch/jit/_recursive.py,sha256=Bxxq5GlL6IYaCMlq_ysYgJRLb6UKEbPb--XUqdxFydI,42373 +torch/jit/_script.py,sha256=2bxT_gDbPbCQXxwcc5jEvAGlZs5VCZ8KYS4zLyy4hbY,65303 +torch/jit/_script.pyi,sha256=oW41BUnY1XixxFLA6883wkNUDsbyM9-76DL2Z0ksIO4,9446 +torch/jit/_serialization.py,sha256=YZODTwxvMT6x-VVOiZfEgLR4HuJ994z2CRQZSLyGAPY,9687 +torch/jit/_shape_functions.py,sha256=eBKs4DZELMEFxi0IhH1l-OTHFnhCCdLeffsDwaCYLek,45269 +torch/jit/_state.py,sha256=OX-p4F2zq7BZo5PgiiNe4hY7yvCdtnFLUL6GRq3GAK4,3759 +torch/jit/_trace.py,sha256=ivkcSFH4JCS95-CtFXKB9VH7ZJHUYloecAsa7RJa-Uo,58377 +torch/jit/annotations.py,sha256=fsMo4R_fCupNQoR0KEjYlWPy5hsWVzBiyfHWWhoCjsE,17882 +torch/jit/frontend.py,sha256=_pPpis95cg40I6Om_8if1LPTGt07mytfweTMw1m_KHI,45233 +torch/jit/generate_bytecode.py,sha256=e-Ltj08oGvI5x-Q3SWgnU0v21B-YQafnhYDj3XJ_C2c,1042 +torch/jit/mobile/__init__.py,sha256=z7Iui_TP_TAKQu1FwYR0jBAg0yDUy1vkPPe_7r3L1Cw,8517 +torch/jit/quantized.py,sha256=_qdmUEm74_VB2z0Dipf61uuvyefSkNAz0kLSPGxFMmA,3193 +torch/jit/supported_ops.py,sha256=LBjTlzuKy5IX78pGywt1eufyrdAtvJv9SoWUP23qRwE,10267 +torch/jit/unsupported_tensor_ops.py,sha256=5ZCq7OgRNiFsMiC02OHBlE7ONBf_FM7ELW1h6P3zAEo,1987 +torch/lib/libc10.so,sha256=Ea3UGiXHgD3Lyud_tCtCjZTgkccnONSwuSPzvCH-LxE,1389289 +torch/lib/libc10_cuda.so,sha256=7j4V1XBcVX19gue8KJsZTurLIjsn175mQ1ySjUvemxo,667985 +torch/lib/libcaffe2_nvrtc.so,sha256=OUtsFii8A11mH683uzB05cCtw9somkjWMrkVJGUXeHA,27321 +torch/lib/libgomp.so.1,sha256=dsrHnTtcrdkniIwJ7Lztb8nqakEc1ECdMBVDhm6Kwl4,254305 +torch/lib/libshm.so,sha256=s0WQpfw3zRjPuX5-XN8xxfpHP5VVDjwxS7si8US6UWw,49177 +torch/lib/libshm/alloc_info.h,sha256=7SdVmCqiLa2mfKg-4DaB9rdpJnStPCmgXASpbjqtzBU,104 +torch/lib/libshm/err.h,sha256=j5cpgd5gS8_WgroKTyMLvDHDiXSe0GzbwE-1Sy88mA4,1258 +torch/lib/libshm/libshm.h,sha256=6V9-_p0xRhavijNsGdB4oxPHUgjOUZjd2U00-3dd0fA,1196 +torch/lib/libshm/socket.h,sha256=wRfqwnHCKnZivtUAY1Jt6TweDFXvWm4QqXAydi8GA7U,4404 +torch/lib/libshm_windows/libshm.h,sha256=9MByK00M3an2HH2S8gFjZMUJsFJb96VRYE190Z-ALFo,779 +torch/lib/libtorch.so,sha256=BZZ-KWcT9_TyxYe6jE60WoREDVsRJ4FplsdG_ImCQKs,391273 +torch/lib/libtorch_cpu.so,sha256=dVQOn_Au80uWH2wclDSpU7PJRfI5krk5wmPugJGo6zM,430926857 +torch/lib/libtorch_cuda.so,sha256=43HNjwfLfssrXqpHoYvNrd2uxaTjk2TQXzzuotsdz38,1015364433 +torch/lib/libtorch_cuda_linalg.so,sha256=jbJ2fVvJgTtqLdy7DAGqObLR5bQFxDRp5XAhHui-yqs,127492609 +torch/lib/libtorch_global_deps.so,sha256=Yr4sd9tHlgDtDIUGULT5F1Bx7DSIlYxgeCWlU12FysE,17321 +torch/lib/libtorch_python.so,sha256=NkHui_zVFV3KgXJRUw-oH3HAXsOm1iifbwZ2EfME3P8,27463257 +torch/library.py,sha256=ErS_Q3Bc0e3sjIPVv74sQGUjnhxxHMjJH7ahtZmQ8wA,62649 +torch/linalg/__init__.py,sha256=dwxFfWe4Rvc-R3IW5XlCVNBlLo7JKgFXcXe35bHIL0A,114871 +torch/masked/__init__.py,sha256=WljBC5z-aMQ3EB29GNw5BVnONrx14zh1Cgrz7fqJ-1E,928 +torch/masked/_docs.py,sha256=Jr-iD8MG_4F6Ip_yxboE8Or1ms-bo3ay_6pdggbfV44,49468 +torch/masked/_ops.py,sha256=jZaB01lJZD6f90Uz-GXLGYZEoXO-ZiVbCzjlzUhhoAQ,66202 +torch/masked/maskedtensor/__init__.py,sha256=yRXNgVmvSu6WET5dYVroutE7gtiXkN1JuBwogvcTvsY,344 +torch/masked/maskedtensor/_ops_refs.py,sha256=RlgH3P7murB5JOCeGuFoh3fBH0voWg1uw6kI_AzQzBU,17556 +torch/masked/maskedtensor/binary.py,sha256=8cYTvBPO3P-8I99WQrVr1oLMDrXsfcWPwU-HMu6I4Dk,5496 +torch/masked/maskedtensor/core.py,sha256=xSNiFxvKh3hCvntgExnK7za52oeb9EX_GjUBCFLlqIE,12822 +torch/masked/maskedtensor/creation.py,sha256=cuKrXLyknX42q3rbZOmrjmqgrFudey8GUh5Fmpu5YOs,605 +torch/masked/maskedtensor/passthrough.py,sha256=kEDm_BwgAN2erALo2NF8SLWX8J9jrAuySd9upW15-40,1447 +torch/masked/maskedtensor/reductions.py,sha256=4yJehs7A0Lg_tuBf-uzz9a0bFdIeYJX3V735PhIs24k,5585 +torch/masked/maskedtensor/unary.py,sha256=F3sO8CbzFYZhpTZxOx4nobOb7RRMgtx_gbY0IRbAXmA,4197 +torch/monitor/__init__.py,sha256=09VJnyTA2zwN2clbG0o6pV3Yf4YdzW2BospVTxNa4WU,1278 +torch/mps/__init__.py,sha256=L_-i7MJ6LOsBSs3PbsPUBY3PLoCbbNnHz-kddk72NTE,6281 +torch/mps/event.py,sha256=tobbRc_mwlY-8W0QlYxxQQg75sPO-25nLzR9Pw9Ix7Q,1730 +torch/mps/profiler.py,sha256=PhBWgPy4g-iA_FUROYYdsgWkQTLRQPPKykkmyj2sjOA,3278 +torch/mtia/__init__.py,sha256=Ut1Klv_apbiUNKUAT45J47vXZ0JYHoortTAVSSN839c,13452 +torch/mtia/_utils.py,sha256=4UJfB-hN2jC9pp0U-FuBOVHAstjmy2D9oeKQfPwWVHY,1597 +torch/mtia/memory.py,sha256=40UNf5qadFoCoAhv5vsNLZen0p-S-57c8MCxclXo_Eg,1757 +torch/multiprocessing/__init__.py,sha256=OwDLFFO9tcKACRyuy0qeyrlcLKW1ClqTNexktwlYiXw,3456 +torch/multiprocessing/_atfork.py,sha256=NDXE2HR06ENj_1bGrQumxLdTGSDOlP-2bzeNwij7ajA,790 +torch/multiprocessing/pool.py,sha256=HQtd_V3oWKnkpQRrR4cutVFdlwxWwgCiUVPYSQXaU7M,1743 +torch/multiprocessing/queue.py,sha256=OT9KTbNOpqjhun_ZQTXizodE1UOwrpSbJhYRKtAyNmE,1477 +torch/multiprocessing/reductions.py,sha256=jS_06h_M-We426jlGziD8R70GXTYuZS4lOcs-AerwfE,23140 +torch/multiprocessing/spawn.py,sha256=YynNkKyiV44SJA7mB5cu3khRI9cjb0_YvbQhq0ML_W0,12804 +torch/nested/__init__.py,sha256=AMFxG285OSBMigYnQg6Jr71YvDLAGgU42SD11oROSfA,21914 +torch/nested/_internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/nested/_internal/nested_int.py,sha256=He63N_1-sY4PZrAmsa4XJvwP6-WtUTJF6Xz___WdHM0,3190 +torch/nested/_internal/nested_tensor.py,sha256=0ZUrfRcb-5YyLNDDKiUrqwtbca-A3n91-p9fWABQVwE,24735 +torch/nested/_internal/ops.py,sha256=YAQrngHQ4l_d84kmVkZ33fnojWGKKgtzufJA7Rl-M6Y,97622 +torch/nested/_internal/sdpa.py,sha256=5lRfSkFia_xBA32CsPFgpVQ5EX929xkuidQxA9O6DlM,34484 +torch/nn/__init__.py,sha256=SGakM4nNGjI1YZwNintPfgNVTV5FrVKhiFXbq5Db8Pk,2425 +torch/nn/_reduction.py,sha256=OX_t4R7Yc31OwxBMOYyY7t6WZEm475fO04iq3hU7BQo,1625 +torch/nn/attention/__init__.py,sha256=uNB04nslllsCA8Y_p58sA2ZzatDegHipIPKjygW8R78,5917 +torch/nn/attention/_utils.py,sha256=as0D_7QKJTRcs61Z8az163Klb8GC6UvMIpW3rjaIk3E,2039 +torch/nn/attention/bias.py,sha256=evEeIaTK-sKLhq_m-Ru6aKF4pWKs5njzfgKN0qrrhPA,13425 +torch/nn/attention/experimental/__init__.py,sha256=8q8QEeQLDNIO2PIfZd7aoqM-nsHM8cpqs-jpEYNY8zg,110 +torch/nn/attention/experimental/_paged_attention.py,sha256=AQLldS3czbDYta4qMRufZ6-bBG8b5Z3mU1J4gIjltJY,12037 +torch/nn/attention/flex_attention.py,sha256=SoOq7YiwgX5Aw7H1DQpGtfxlAupJHnpylmTU3fpJPhY,58217 +torch/nn/backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/nn/backends/thnn.py,sha256=30l44thYgv59GXTfz_89j6MEYlkWUaVRv0laJO4UW6g,146 +torch/nn/common_types.py,sha256=9L8AfcjeRTRRJ8YInXCCU4cwEN5nQlbW4vezK3b_Q_0,2186 +torch/nn/cpp.py,sha256=TMiA6xzuxtvKLDIi99W1RtxHiOSFAf9ORBeYQ1JDGT8,3017 +torch/nn/functional.py,sha256=K260r_MFmQz_njLG_0D_gsH-xLXOuh9DVAFGqOtk6VI,243853 +torch/nn/functional.pyi,sha256=Q3HO3N8I7kM6w-KwBFE-wXj3crw5395NFZ-CXCHKJnI,26669 +torch/nn/grad.py,sha256=xEFG-ZnkxxjBMJajUSoNPqlHLiFFFAZ3wXBvn2kCRYc,9910 +torch/nn/init.py,sha256=20sBfcnBEowLav4ZGo9AQwkBD9_QEnO31B4qOnuBbVI,25743 +torch/nn/intrinsic/__init__.py,sha256=mLJtyO2jadna-iZ8mCcW_NoEMq4mJxr3BqaJDy0jBhE,697 +torch/nn/intrinsic/modules/__init__.py,sha256=bqi9D10UvQcO_cimMFdlI5HjLEvFOp7Y0hT6g3WOSMQ,517 +torch/nn/intrinsic/modules/fused.py,sha256=Q6ta4U7JgPoSVs66FPll7Hjhjl_pXcO3pFWKGnBzspk,563 +torch/nn/intrinsic/qat/__init__.py,sha256=7rFxYP3UVmm4gwSlC7aBqXoJF1s0Q4aIdJZuW7ST8wI,59 +torch/nn/intrinsic/qat/modules/__init__.py,sha256=n9s1HTHyr4Ao5_38irURngF2ZPh7pTJy9eos_oIz3Bs,637 +torch/nn/intrinsic/qat/modules/conv_fused.py,sha256=7E_n97P10No_DFQ-KoKcQ8_NKx97m8k6HoiSVDVr9Cw,854 +torch/nn/intrinsic/qat/modules/linear_fused.py,sha256=sUSyA7rEOEfPRmn-M6SbEltWGaTym-yzDhkCEARnqWI,455 +torch/nn/intrinsic/qat/modules/linear_relu.py,sha256=ST8JHORElUi8Ze5E9SxJ94JFaudNQzdRmBj7c9k4a3Q,455 +torch/nn/intrinsic/quantized/__init__.py,sha256=Elo1b9Ro7sj3MsWn3xys-1rGWSe21SANc4H6Ai3x25c,336 +torch/nn/intrinsic/quantized/dynamic/__init__.py,sha256=Ov25o_5XouCKIqpzK42xcw8dAwVd02tGZqS4XL8BOIM,73 +torch/nn/intrinsic/quantized/dynamic/modules/__init__.py,sha256=acHjDVSQZsjlTsbUoN4pI-971UJRfjavwMJRJuEOOBk,114 +torch/nn/intrinsic/quantized/dynamic/modules/linear_relu.py,sha256=z509nJpUu_QTwDUjE_ON9Fbu7n5GfMnOzWju35bEXRk,97 +torch/nn/intrinsic/quantized/modules/__init__.py,sha256=6WHqSE0pA7qaW20FVPWggCar8uawjVKABKVeNdxjNlo,379 +torch/nn/intrinsic/quantized/modules/bn_relu.py,sha256=3t9doT82fRh1SytKSivqQgxVfDP41KuvL-R9hz8oSb8,111 +torch/nn/intrinsic/quantized/modules/conv_relu.py,sha256=QWzacsMW7FtkasYvsZ9WqRExBjaMqsxHQHD5tYsZBFA,149 +torch/nn/intrinsic/quantized/modules/linear_relu.py,sha256=Zuj3I1owPTdoovG1-BoogRjn8JX1rMWM-WXYc-QFneU,89 +torch/nn/modules/__init__.py,sha256=chOVC7vb1QKPVwZ7I1MexX9fxazQGfbsmsaStR-cOgY,6494 +torch/nn/modules/_functions.py,sha256=r4JI3MQ_omMJKnZ8yRjcXpFyOy606OiW7vk-vtM37h4,11995 +torch/nn/modules/activation.py,sha256=ed_kbTG-Lo6M5okIg49C64Pswv-zZOBlFJ0RLIr8jgI,58405 +torch/nn/modules/adaptive.py,sha256=ME_PXZb2Gt_js59p_t_-nJTizyDpLhXBoE4ie87Fohs,12435 +torch/nn/modules/batchnorm.py,sha256=Xgf54QD-eafEO_m9JYw3Ug5_ovkq7Xw-dAhSBG3KiuE,38394 +torch/nn/modules/channelshuffle.py,sha256=xQ4wX3rFb-6VpWuBbwksB42clqzcUN_Wq9rzkAZwzAk,1548 +torch/nn/modules/container.py,sha256=8Tl_XUmrScF2ee9fvAQDKIrSaA3_K2HV3wTQaKa_wp8,36951 +torch/nn/modules/conv.py,sha256=3D8J963l05sV6sHJNzMd4RQoCOL3adYBUR9JXomEj7s,76163 +torch/nn/modules/distance.py,sha256=aW1CEdpy1d106QLr0dbG1PhjxoSobHH-mXx0hEFRVFA,3262 +torch/nn/modules/dropout.py,sha256=H8mXTDzyB43UvCV_c6-2pnFK3-wunQmdR8-SPnZtNJA,11187 +torch/nn/modules/flatten.py,sha256=LK81Hqqnog_og6YvV-Q3-ZJRqU6L_stSq2yi6pw6_bA,5537 +torch/nn/modules/fold.py,sha256=jYGD2almRU9sFhbTqWb1rdupbrUkIed_NQkxHLrDK8E,12959 +torch/nn/modules/instancenorm.py,sha256=422ynekPRRIgAhi1d8y-c_TzG4avvjj3zbGf8AzxU5A,20319 +torch/nn/modules/lazy.py,sha256=cu21FYOXjBZ-upaActl1Fmjx39V4GjyMCIbGvUxk3Ew,11611 +torch/nn/modules/linear.py,sha256=-iKsu0jkG_874md5wvJCsVZjuBV-VPDVWYdyGt2X0W8,11269 +torch/nn/modules/loss.py,sha256=eThdWs138ybK5v7jjx9X6qFEBJFJWzTge_rXlf2HZY0,93834 +torch/nn/modules/module.py,sha256=DLJJVkVBmaRuj8N6fM5Pwc_1ITDyX7brBSdF6eyokGc,126775 +torch/nn/modules/normalization.py,sha256=u0oCfFWa_R_cpe5kdaxo0lNoeblihuO0U1IzH7u-GJc,14954 +torch/nn/modules/padding.py,sha256=bi6Y4892I8abuLj9TisiV48--ikKA-1Mbh7MfYdRuV8,30947 +torch/nn/modules/pixelshuffle.py,sha256=WsifsWRqulNrpJtWLz3suAVK8HUwzBJFcYgSeHD-a9A,3680 +torch/nn/modules/pooling.py,sha256=BETriMRmzZxofaJJFPQCnOR2y9gctyxLNlcjCnH6FEY,59534 +torch/nn/modules/rnn.py,sha256=EPVg_2gDed94AmPdCwkFE5A1noeemirWvP-2jdL_jXA,74511 +torch/nn/modules/sparse.py,sha256=nvqNI29bKcQv-y7SgiwB2LJVxUu1hTqPkZ5wwG0QH6s,24084 +torch/nn/modules/transformer.py,sha256=VInlcHPbv6LQyi-FFf6RS2BtjmMQOtkqAbaiJ7EKNsI,51864 +torch/nn/modules/upsampling.py,sha256=meTVgguUC7mM3CLO6TizKrJ8Iv-YoElJCRkqARqd6f8,11541 +torch/nn/modules/utils.py,sha256=nDBVhYHfsmw1ahm0iXOVJ9wH4aNj0tgXGuChwQ_H2uM,2579 +torch/nn/parallel/__init__.py,sha256=z43J5FJ5qqe_aVSla6yG0qZ19t_Y9pX3g3BzYJvVuSs,760 +torch/nn/parallel/_functions.py,sha256=XXu7Yk6uYdzKpKCv8YawyztOrprsWHowzPj1G29OQEA,4949 +torch/nn/parallel/comm.py,sha256=lXQ6yaVpe1jXIftVRQMW5_R1vZA_ugjyiJo3vjTpRhU,10893 +torch/nn/parallel/data_parallel.py,sha256=ARzDrZW1gpbKNUY1jtWtYe_xXRyjg8SxAZx54kwuars,11761 +torch/nn/parallel/distributed.py,sha256=EEWv9wWm2V9p6RJFbhRANY7S81WB5f4zIjxbsa59-iI,109136 +torch/nn/parallel/parallel_apply.py,sha256=t3uJeg4YRW3iRG-gawTAcCipjGOH0JGn0-2G9uhDNXQ,4464 +torch/nn/parallel/replicate.py,sha256=E0TG3b1DhXASeOMjwuJwHzId8YGVHMeNQDQ-YQcrD_M,7020 +torch/nn/parallel/scatter_gather.py,sha256=mu1tK7MIfBI7h9w-MmwoVGwTbO8ENuBhty419oV1OOg,4983 +torch/nn/parameter.py,sha256=XRbHyrBVS-rTVLdZYhAf7tCrsG0po1qPfiZJ8RAC5b0,11378 +torch/nn/parameter.pyi,sha256=V03epYtsDyMo2jqNAhv9JOo3KNZl_uhF62tOy_4ok9M,1102 +torch/nn/qat/__init__.py,sha256=YHWepHV69h8eIIZ09WgM96wRwYTKAdZ5IXJG4ajhsHI,365 +torch/nn/qat/dynamic/__init__.py,sha256=3v0msXCbfI7q_EYZ2WUPfuW0IxLRr2s8ICycY6XraDQ,208 +torch/nn/qat/dynamic/modules/__init__.py,sha256=xnBpUudYrmz9leiWC2UaMEGbDX2eO--nNluoaO_V-LE,78 +torch/nn/qat/dynamic/modules/linear.py,sha256=io9A-B4H0RMvMRYuKeVSxRQPsBhdCy5WKfvr5QtKHuo,416 +torch/nn/qat/modules/__init__.py,sha256=xHBwOWC71Dzb9MLPvLkMMdcDlr39rf-dejMilOtshHM,501 +torch/nn/qat/modules/conv.py,sha256=hOAGSJ7oaQTf_9DWXPWrGGsQ9oPLASiCZaeHX4X3BMI,406 +torch/nn/qat/modules/embedding_ops.py,sha256=1BP3HyU1Xkr8yQBH1RNqv2rwZM9Uv6Wl6X5-DM_Jlls,458 +torch/nn/qat/modules/linear.py,sha256=cFw8lAcvlWfV__h6uSDi9w4I7snqlWGVAyfY7nnHiMA,392 +torch/nn/quantizable/__init__.py,sha256=O3C0GktZB_Ut6pNTL17JbCzJ2h2hCIK3KH8sVnhSuDs,57 +torch/nn/quantizable/modules/__init__.py,sha256=gr03bjx4iwgg1n8zCu3B1UpgNqhb6UxxSVvwO5894V4,207 +torch/nn/quantizable/modules/activation.py,sha256=m8gKPli1OoBlwAng4E2ndP9K3ov2t6D7pN1Zj84nv0g,440 +torch/nn/quantizable/modules/rnn.py,sha256=ymfl_ayTkuUunsNt9186sFOhtapsxsTHVtY_QlKFH9A,429 +torch/nn/quantized/__init__.py,sha256=OTMoZbhpm8nI1oMVMuB8tt4JvpDN3MsIoYww2nUJfQM,771 +torch/nn/quantized/_reference/__init__.py,sha256=VrWZh6FzvyvAi744iiFBcYarBX3Akn6kTarYzEIX0_w,66 +torch/nn/quantized/_reference/modules/__init__.py,sha256=yjgG2MRfrWs-u8l1CCILBoPTa-FBORFZh9i86vYKdy8,1018 +torch/nn/quantized/_reference/modules/conv.py,sha256=FclYIsMCbXH4erKiBDintHNWDsplOru0QY9LDToSL-s,579 +torch/nn/quantized/_reference/modules/linear.py,sha256=Cu-oS3rGRNtFQX8C8yBiLIeORYswqNnFbVw3ds6PMKQ,450 +torch/nn/quantized/_reference/modules/rnn.py,sha256=A6kV2QXCij5mpFFUTWBQwf6ynKQNJj0VMJbvHqkXa-Y,524 +torch/nn/quantized/_reference/modules/sparse.py,sha256=gSNXDYXlx1IoSrRN0i_pJHJt1znBH3sAPvDpivdfask,467 +torch/nn/quantized/_reference/modules/utils.py,sha256=nBSMp0qUV2OBmuCJYKGmpY6JtRinMoHhwPT2B8VbY08,590 +torch/nn/quantized/dynamic/__init__.py,sha256=lN8JfmyIwtMWNsOD0hWGgKMVPUXuUb5cu6QOvIlEJxg,58 +torch/nn/quantized/dynamic/modules/__init__.py,sha256=ORZYUZYDqtgat8r4CkTj2GRbnR4y2CxoK3hNUT1AFEU,993 +torch/nn/quantized/dynamic/modules/conv.py,sha256=bq0IjYdFIpdJbsQkIeF1LDHZOC8wvGInVohV5KwqqI0,669 +torch/nn/quantized/dynamic/modules/linear.py,sha256=67W7Cw26T5oIiE4n3RJykC6PX5VypOJU3lQcNP6mohg,448 +torch/nn/quantized/dynamic/modules/rnn.py,sha256=vWAb3OQKyHWubLC5U-qIGP-GgYq-GFpvltZCDXjogrA,740 +torch/nn/quantized/functional.py,sha256=YLagkINzH1ksH0zcEEizTr0Wdu8gvziqPkx-elbUkJE,276 +torch/nn/quantized/modules/__init__.py,sha256=oNogz5eH2YEYfHjRkYBEqp_XD2GduDqc9StknEGWx40,2101 +torch/nn/quantized/modules/activation.py,sha256=52tIo-6xSFg2QIuNL-Q7AKs2IiDBCcGFIvWNupA3fjA,528 +torch/nn/quantized/modules/batchnorm.py,sha256=OpxwQosX9Pv53csZ4ZXKhFOkya_tXMbtndDMSETJKV8,437 +torch/nn/quantized/modules/conv.py,sha256=3JBXb4gZOKXHIyWnnPEVxlb-WRDSMiNEUvuofCSNsSw,666 +torch/nn/quantized/modules/dropout.py,sha256=qdWqDycnaodhJDr_EyIQCiaSg4xK5dqEZ21IBIsC_8M,442 +torch/nn/quantized/modules/embedding_ops.py,sha256=smsmXs2h7t2y6WbToNuJ060heMQbZOXOfkNbdwOqVO4,547 +torch/nn/quantized/modules/functional_modules.py,sha256=bk60spaa4_sUCDXyh4GOh937FVIwNCH-l93lAPi39-Q,554 +torch/nn/quantized/modules/linear.py,sha256=zsZr5Rb4z4XJvTjc-hF9cqG4uqpsbkHOc3jJOxcSM4A,481 +torch/nn/quantized/modules/normalization.py,sha256=vUyWsXC7CFIm0oohnco73FD3Kz9xMGHeJKq3dNKZxKQ,626 +torch/nn/quantized/modules/rnn.py,sha256=NWI5IuH_qaC1YD1ROeoUbodbN3yS0MrZKOhXLmGfPDo,411 +torch/nn/quantized/modules/utils.py,sha256=ZkyzQS1nmLaK5Nvq2eKtSha7mHG3HyM08_5ueUc9FpQ,539 +torch/nn/utils/__init__.py,sha256=OKvEupi4W-bE9n1ixUHR3QBo2mOBG74UPweQAH6Ll40,1242 +torch/nn/utils/_deprecation_utils.py,sha256=2qJ2LKQJWGFwlL9gyAL9lq4TBGiS59bBOqyBpKu2DKk,1675 +torch/nn/utils/_expanded_weights/__init__.py,sha256=PHIe9H3l0iuImQ6z3WqMkWIS37FDQVQ16vMo0a08mtA,452 +torch/nn/utils/_expanded_weights/conv_expanded_weights.py,sha256=ugPaPBwdJ-4lXGUG314y8zdL0NbB2IFbLont3Y2FAvM,2816 +torch/nn/utils/_expanded_weights/conv_utils.py,sha256=jq6E2bWOPL022gq0jG9IXfH_Mjpis0Gs6JAQ44rbwgo,10729 +torch/nn/utils/_expanded_weights/embedding_expanded_weights.py,sha256=JoAb_U9gFokJanvmDZTG3vQKhrA7etGq1xxcNVm9Ce8,2850 +torch/nn/utils/_expanded_weights/expanded_weights_impl.py,sha256=a5Fng1iSLCvt3MH8Sk65pwiwIbdzpuR7OXG0_ie29_Q,6280 +torch/nn/utils/_expanded_weights/expanded_weights_utils.py,sha256=qymuok1L-XCz1mt6GW5nRdksOVAPPmIfigzwskPZaEs,7582 +torch/nn/utils/_expanded_weights/group_norm_expanded_weights.py,sha256=nYDBHibbYZbilR356suMzivMF1f6GNlZGIxbCqWJMqA,3450 +torch/nn/utils/_expanded_weights/instance_norm_expanded_weights.py,sha256=n4rWXbpLFlH6assM0v-OZ-iu-_XFbwA3maBrsOdq4O0,3729 +torch/nn/utils/_expanded_weights/layer_norm_expanded_weights.py,sha256=5kXJ4Tz7f_vsfPsWl2cW6_AnCSS5zPqoU8nP0De79dg,3246 +torch/nn/utils/_expanded_weights/linear_expanded_weights.py,sha256=CdGOJtVbVAMBAPp-WpGeNVKIcatjPaHbPTCkBWqpgow,2216 +torch/nn/utils/_named_member_accessor.py,sha256=_s3U2UmCyeK8P94Gx-Zsx9mN1_-0S_CSy9UzHJ60xkY,14163 +torch/nn/utils/_per_sample_grad.py,sha256=r2XSvB-eGfw7g-TOhHWT_v_w2K4oI3qaK7pHxm9S_iw,5775 +torch/nn/utils/clip_grad.py,sha256=U0851zCMarT6nlmUoXeAOr6VjOU9VoHmYEmpkX4BFcE,11151 +torch/nn/utils/convert_parameters.py,sha256=KrOW_WMVrniNK261nZXBOTAT6esTDA-pEngjaKpYBEo,3244 +torch/nn/utils/fusion.py,sha256=hWBzG7SJQ35obxMV7Bsrg1GNlWRLU4znQjUgGxiZ24Y,6434 +torch/nn/utils/init.py,sha256=eA5Y0Ti3Qjhd-pCKEx9MpLHjJuz4bA3XNw6W0lC55XE,2250 +torch/nn/utils/memory_format.py,sha256=FG3nOWaM3snIHrXJiKLzRsjiljZLt56iiJR-EOYpJ60,8204 +torch/nn/utils/parametrizations.py,sha256=c5WXxNbNRdNsmzu6u3yolT4SBLdw6QyhghIRuMNe3OE,25666 +torch/nn/utils/parametrize.py,sha256=zweEUHCAbEoe2_iNVB7lMvVC9nljToGPXRm5qKN9Fag,36222 +torch/nn/utils/prune.py,sha256=RGlpBTgHYfVWu1ZErAwA4TQOO04XZKcR-UlVFfprxUI,58091 +torch/nn/utils/rnn.py,sha256=-nR3GLI7NJ4tr-xoHXNcdVVC-FD2RldqioQVFjqfHGU,23231 +torch/nn/utils/spectral_norm.py,sha256=NJuyEDZnY9QQqQrXb1o0gDEE1xf_AQqm0jNyGRocOK4,14914 +torch/nn/utils/stateless.py,sha256=bPqTh0fceKayMXw_Shy3CCo637VWHgDervZDfMnBiRk,11696 +torch/nn/utils/weight_norm.py,sha256=AyxC7VlgSfeRd_e8Q8s42OyBum5Sh7kxUyohS1nSbV0,5882 +torch/onnx/__init__.py,sha256=OP2e4K0BInTaRij0U1tvxmC0SlYOFr-Fu8eiezWV4q8,21747 +torch/onnx/_constants.py,sha256=mAh6nE-PsYyn4Y_5gvC10GcLevC1rWeq0qMATSW9sYE,531 +torch/onnx/_experimental.py,sha256=HZDRgJ_2gR2664BL8J5I5QH02kiUwfeI55yiBWB22yE,1033 +torch/onnx/_flags.py,sha256=HkmuV5M1ecTQmmkDnmTLwovp1FSJ6huGSxrn8xJ7omE,1235 +torch/onnx/_globals.py,sha256=VG-ATYD-sqP85n1ssebOnVBn8lotTS37pCJqQLw-pkQ,2772 +torch/onnx/_internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/onnx/_internal/_exporter_legacy.py,sha256=OmU0EVoAIACELmziGdStBZy0F-QLCn9OzNlXj4DlfV8,19593 +torch/onnx/_internal/_lazy_import.py,sha256=vMUmso3vgG3UDj-ckDcvtbijCt9-4udzJCyLZLtw1gI,1228 +torch/onnx/_internal/exporter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/onnx/_internal/exporter/_analysis.py,sha256=IxBF4PFAAkIYf_nqoaNaNKMe8UTbThwuasGWY8-m73A,8859 +torch/onnx/_internal/exporter/_building.py,sha256=UHjlPSrnYusiv9BCakfeYjDGk1wdCEAt_bjD8H01Jf8,30810 +torch/onnx/_internal/exporter/_capture_strategies.py,sha256=h8r65s27Bu3R2rfsk7h7pFoX6ZhLeCkG7OodmNqWNxw,9737 +torch/onnx/_internal/exporter/_compat.py,sha256=vSVLT1cWMyOSiljH8HSx7KMnJRXWoafF1XRHUmPzltM,7560 +torch/onnx/_internal/exporter/_constants.py,sha256=y8ST-ZGRhbGrA_d-oM2xapBMa0hnFcEgLbLxt8pcMn4,378 +torch/onnx/_internal/exporter/_core.py,sha256=1uNOjqWrcB5VCrS53v002d2GTtARptUCMsQ6769CVGo,67299 +torch/onnx/_internal/exporter/_decomp.py,sha256=R2HZzP_E8_HNqRcXTHOE35reGb8Q5vo-Kzh8BF_JeIc,2837 +torch/onnx/_internal/exporter/_dispatching.py,sha256=lL4UKmRZhASWYdzMryp3kaM46ZaxLh5tTbUAG-2TZZU,14800 +torch/onnx/_internal/exporter/_dynamic_shapes.py,sha256=D3zXLVuwWVZCogrX3wlRS4higsnZKJ9UH3S4bWKoGl4,14009 +torch/onnx/_internal/exporter/_errors.py,sha256=VziOIvyap9A_UDRP41HkC_s0J-R13dbCrVLIusF-6A8,535 +torch/onnx/_internal/exporter/_flags.py,sha256=UvqwiYxRvZ6skAy295qTQp6zuNFn206W4VC2BOsUjew,642 +torch/onnx/_internal/exporter/_fx_passes.py,sha256=-riZRcK803q4kcR4v9d4x0uzZcEUFnWJoUjHLs47uFM,1724 +torch/onnx/_internal/exporter/_ir_passes.py,sha256=Wl6pkHtWMdp3VoB7TUGmzAJQZfBmXA8FsCb2y1szrDA,5854 +torch/onnx/_internal/exporter/_isolated.py,sha256=wyiDvz41mh9qh5HdHhKIBaHAAUM24fVhkzlNdRoUyQ0,1816 +torch/onnx/_internal/exporter/_onnx_program.py,sha256=iIfOy9Y3Gu3X29YxqY_qjNDOcDmEt73MCPSri57KFsE,18178 +torch/onnx/_internal/exporter/_registration.py,sha256=W3vQ4LovCj7nuJNDvVIBk5b4-L0owRx-E2O8gcStIYQ,12433 +torch/onnx/_internal/exporter/_reporting.py,sha256=pZUIr_8OtiM9kympS1WfGUTfKGwBQQrjjpzkpFE0NE0,7396 +torch/onnx/_internal/exporter/_schemas.py,sha256=1sy4XxcBS9BaaZc9vdiJsV2iPwbLUYSUX0xcQlbHgcI,20597 +torch/onnx/_internal/exporter/_tensors.py,sha256=DE1j78H2T1CvM3f1QSxROoBJg9KO6rrWa0qa9pEPdUk,2467 +torch/onnx/_internal/exporter/_testing.py,sha256=H-dSgw5RuCRLG-usjPGMt1_b4-90DUAFyNmzwXO80v8,3328 +torch/onnx/_internal/exporter/_torchlib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/onnx/_internal/exporter/_torchlib/_tensor_typing.py,sha256=AknXTea4BeyMq3wvA5_kjIqDutdeFbe3ETQkPHoJcTs,2042 +torch/onnx/_internal/exporter/_torchlib/_torchlib_registry.py,sha256=BSbZ1jGbsHQoQho_FesDx7r-YQoC2suP7Rc1FpAwOX4,2722 +torch/onnx/_internal/exporter/_torchlib/ops/__init__.py,sha256=bgyPtdiM9s1nE_1ZdR_6-0y0Tea9o0Qiyts2wWU7EM4,180 +torch/onnx/_internal/exporter/_torchlib/ops/core.py,sha256=8NKuddi9r_fzgAX6f3_DAPAKBgSy71uKMVfLLh2Z4mk,1543 +torch/onnx/_internal/exporter/_torchlib/ops/hop.py,sha256=wz02t6P3ho1UGNu2_hASITTBZBK1g6Ehx-kaVQaznz8,5309 +torch/onnx/_internal/exporter/_torchlib/ops/nn.py,sha256=5tpafeUsfOnM9OPAf7IX8lGtRNX0FPi0G6pbLr3hrQI,10469 +torch/onnx/_internal/exporter/_torchlib/ops/symbolic.py,sha256=L_UmNJ7hxPi4hytioLUT8T6a0vKhTiWqaBblRFITtYQ,4722 +torch/onnx/_internal/exporter/_torchlib/ops/symops.py,sha256=-KCcgEDVpH6umpPcpyXiDZnVA2hJtiRHVqemHLVI4jI,1147 +torch/onnx/_internal/exporter/_type_casting.py,sha256=hCTUy4U8abpYW0sNmvUPWvZfCxhjx-NyU5LuY9kvdY0,1161 +torch/onnx/_internal/exporter/_verification.py,sha256=55OC1W74ZlO5L-dMWHKQqQ-vqRS1f2HbGkiXQPVSAuo,12563 +torch/onnx/_internal/fx/__init__.py,sha256=nDN5RQ1wcu4ktXSPWCLgPREAyDWHnhr1EOMqcKSVDZs,172 +torch/onnx/_internal/fx/_pass.py,sha256=S5ZtJHJeZhQltQURhHY00MRtE-nI1WSTm6u2onpkiHc,8590 +torch/onnx/_internal/fx/decomposition_table.py,sha256=u-5BZqxmfe14VBc7MPx249FwYDGfM6EKL3O-xAdrSSE,5100 +torch/onnx/_internal/fx/dynamo_graph_extractor.py,sha256=ynldYhtLDz0QRiykKs0UgG3iClvcugWDv9rCTu1ZZ58,8285 +torch/onnx/_internal/fx/fx_onnx_interpreter.py,sha256=_T-NAhp0fG-zb5P_pVi7nvvFeNiZJJN3JS_vqZtDhuc,31204 +torch/onnx/_internal/fx/onnxfunction_dispatcher.py,sha256=VHDCCR91CA2NN7pI8QTz2VXRjPZ2CHB5molsEFGIgYo,30407 +torch/onnx/_internal/fx/passes/__init__.py,sha256=SO_DWZEqZA0Bpw-ShxxQ4PuG7G4v_B8icIRWSyx2PcI,552 +torch/onnx/_internal/fx/passes/_utils.py,sha256=GC8xzbWYRq6i6ms_Yjo4Wf1n9uAdH4T8RQjY3iBmkvk,4197 +torch/onnx/_internal/fx/passes/decomp.py,sha256=OF3auYOL3y5ydmFzVs6xDo0fByI-gMkQDsvMCSOHD7c,3546 +torch/onnx/_internal/fx/passes/functionalization.py,sha256=39Wo8h1VczXMoX3Al-EsxdHEw3liOPgfm9SBRyyNbKU,6261 +torch/onnx/_internal/fx/passes/modularization.py,sha256=CFTHxSswQB3oUFKlwHVaPX-qTq3PuNix99SbggWUTsA,34021 +torch/onnx/_internal/fx/passes/readability.py,sha256=_UK2ybMLWBqk-dKpkZWZmdOKd3aaKOTsf5EccB9FsVQ,5483 +torch/onnx/_internal/fx/passes/type_promotion.py,sha256=Sx7pLG3W0Tc4UMPjygyCrH0C8UvpyQsKKOxxB5f8MxA,64579 +torch/onnx/_internal/fx/passes/virtualization.py,sha256=i3OyLaH5s2fGDnnWsp1Hurvt_XcswfUwdQ8CVsyDazI,3813 +torch/onnx/_internal/fx/patcher.py,sha256=rJpx-gyAdd9KLTyo0O0sVMkXim47pivUMbejV0GNwqs,6039 +torch/onnx/_internal/fx/registration.py,sha256=FyXKoCfbiQEC6oUL97GpLISl-7AnuP1ZMLM25WVytZc,2984 +torch/onnx/_internal/fx/serialization.py,sha256=0z5zDuGbD7dc3wiMxUw-JIY4Z6Xrqj7ARTytms5giQY,11618 +torch/onnx/_internal/fx/type_utils.py,sha256=GudpK4H2xHotSh2P2JUMTFW9xjzp-_mrbI7tpUcnpiU,5837 +torch/onnx/_internal/io_adapter.py,sha256=7L1CZhnqw2zMB5KV74QSBXSR824eU5zJ1etkGpxYw_Y,23046 +torch/onnx/_internal/jit_utils.py,sha256=1vmwi4DCAz1uQWOX2IDmFBqLJ03BM8qCY_8jjMSfAu0,14129 +torch/onnx/_internal/onnx_proto_utils.py,sha256=qoj4mzvJcbPmhnexv5IumDCKZzCITBKgDDga1BUUTb4,9199 +torch/onnx/_internal/onnxruntime.py,sha256=axTT2rm5OmZIoRd2OjAbINPNjRssonNIrhZM1ELAnVQ,52944 +torch/onnx/_internal/registration.py,sha256=NXXVK2YQXLoop5YNHETYbNBTJfmGRjk7Bj4eR8TqaFI,11121 +torch/onnx/_onnx_supported_ops.py,sha256=w_e4BB4czhz5Q18tvjQ0GsuPB7Wi7G_MxF7Ln09BuuA,3319 +torch/onnx/_type_utils.py,sha256=XqHQdvYMVx5y6t2fj3srw1Q8qtIVTJYdXz37_CN13bQ,13940 +torch/onnx/errors.py,sha256=L_mjqZfWwFjKWCZaYIUZ2Kju63_Mdl3XupasjykS6-8,3464 +torch/onnx/operators.py,sha256=fS94DP2lYBZwI8qpxN6VWHWuTrvD7VWueC8eonqRbnE,1185 +torch/onnx/ops/__init__.py,sha256=40s8uIzhMoe3oAZ6LCQLgr9whSXd28bHRq11fNkur8Y,20944 +torch/onnx/ops/_dtype_mappings.py,sha256=T2_Rwd7HR5CzErvrphymlsCk0kRZhauZdhZbi0JZb3Y,836 +torch/onnx/ops/_impl.py,sha256=iHV1U1A413xmIXt3bQrD-ft3D7ql17uBqWbR4948KM4,13890 +torch/onnx/ops/_symbolic_impl.py,sha256=mjAW4-8Y2vA2iEy-_9xjpLJ88V2znragzdP4Miw3Ssk,11781 +torch/onnx/symbolic_caffe2.py,sha256=FX6R1arFhrT9YwkYV2xieJVbxIFCk1LzNpKbK_OKqCE,10971 +torch/onnx/symbolic_helper.py,sha256=SfsxRP3leu5wYSsQsahh5ducCh0Ahh6qu302WQduadc,82311 +torch/onnx/symbolic_opset10.py,sha256=uKq-PQs35HohSMRj1qekSBTFKo3rOZwmYnR1em7Z6HE,37428 +torch/onnx/symbolic_opset11.py,sha256=lYLtocPBBDcdn7xjsngalcfDogV6GhHH37YmqRO6L4g,53342 +torch/onnx/symbolic_opset12.py,sha256=Rsb55TyHCYR53CJDMzVo-7ORXdsdbc7juJOwXAEl9fI,15659 +torch/onnx/symbolic_opset13.py,sha256=7hoHmeunwBdRKUMm1iR8UMWcx2FHL_zJo1vYFuApRFM,41254 +torch/onnx/symbolic_opset14.py,sha256=eCTO99kilWb8tXmnQlncdk3KHMZ-BW5Dfh0kYAlTt7I,9473 +torch/onnx/symbolic_opset15.py,sha256=OSOAXpN-enLhjkSq28rY4kLE-cy4Yyu2R-U77Kyyv2Y,2872 +torch/onnx/symbolic_opset16.py,sha256=lBC9GWFVzqLcY_4BTdc5M6eGq_K2A7-v8TbQTryBPws,6410 +torch/onnx/symbolic_opset17.py,sha256=CWR5SjAGIEJdrgtV6VGtU_WFqoYr0h0ghqL0Yi1cBeQ,8039 +torch/onnx/symbolic_opset18.py,sha256=G7d69F577AtCywuA-4VCAJ7YRlXeZsI3mh9rqyamsDk,8092 +torch/onnx/symbolic_opset19.py,sha256=mdWDL5ju6R87V0WlsPnwFidl5EwyPeOb4roJkXLW1fI,536 +torch/onnx/symbolic_opset20.py,sha256=pSOXuR9JXtteuts26neYyAyyhhOy92hz7vX74Iz5qbg,2446 +torch/onnx/symbolic_opset7.py,sha256=DK-UZL7LNb1Prfrefk5CFeJjy4qklMDywfHvZcXJwZY,2118 +torch/onnx/symbolic_opset8.py,sha256=AlDVGnoKOpPwGWtxMhl9kIwpZTAdpW_zCoxXFysTJdo,14992 +torch/onnx/symbolic_opset9.py,sha256=M7i0Xg1hoODSU82Ssfrz48HumhnVMcbW8GcTk2KSVWQ,225245 +torch/onnx/utils.py,sha256=UmJYZxS4XetsJKnHZGzEZ6b0zG_4kKsDM5Q_j3CtmWU,74867 +torch/onnx/verification.py,sha256=7o5GoK3LPG295gPHdp2m2QOja754i9aRbCE-pMxk3k8,70529 +torch/optim/__init__.py,sha256=1uogogvL5ff7-vhRV89Hwbr-7wToIdCtFtJBNzNxx8A,2118 +torch/optim/_adafactor.py,sha256=99aZ-qJPtoCRE2TWLrlGmQzPnoGtNs1QV5-o4Ne4sz4,27988 +torch/optim/_functional.py,sha256=eY795IL4RB6y-qFAtzaOuq6C6uBdjkqUnFHob_JBpu8,3242 +torch/optim/_multi_tensor/__init__.py,sha256=DaZ72GF7H5n46jPJ_mAiz67xOsutFFE16KJsB05ylns,1027 +torch/optim/_multi_tensor/__init__.pyi,sha256=Q4IMQ5dfkrDDq7ooUZubB4Okb7KHVhWpjx5HUF3f7hY,537 +torch/optim/adadelta.py,sha256=y9UDoY1XAzFD_bTxxmp78vAbtqnWWgIas31-YjPvT7k,16681 +torch/optim/adagrad.py,sha256=tJE8AnOuFpefa2gxv9IDChwEz1cBGEwwSYBewURcpUs,20820 +torch/optim/adam.py,sha256=RilmRLgq5fLsrhfuMM5GkfTMIHig5HjsK-7xD_voAY4,39250 +torch/optim/adamax.py,sha256=R-QUNzJnZdSlmnxUdBGYIw6_37f0V48Dyh2sYhxJF0E,17345 +torch/optim/adamw.py,sha256=MRLYeZ75q2CLhVpXCAh9VXaXFGOSPEHUpqInAGhx9VE,7344 +torch/optim/asgd.py,sha256=tznOyxoCqGv-cTUx4W0lLCkynMY4Uj66rMYDGETckvo,16290 +torch/optim/lbfgs.py,sha256=DPVRaQq-BjNQ3D0ikj9oCbIwQk9V1LEFmIueF_Z4_Lg,18154 +torch/optim/lr_scheduler.py,sha256=OHZZDPwxJjCmjILN5gJDLSz2Bd0MHtOuuXk24RYhqaE,84169 +torch/optim/nadam.py,sha256=oIjcd22tl1Yr1KMyDGb4zP_Zl1ksWLE3BTLB5XTIpk0,26455 +torch/optim/optimizer.py,sha256=IuIa0QqnzZlBian6fRnBad9NydDE9KjcP8pqpFaPyr4,49829 +torch/optim/radam.py,sha256=ie8jI5FSMAXSTCxLgQcRXHPStEP3AB43IrxI9AodVCE,24685 +torch/optim/rmsprop.py,sha256=5AxYvmswvfNTxvMM29pCrcqlwFKrEsrCyUKtsg8DhnA,20436 +torch/optim/rprop.py,sha256=fY24Pebv2zEr7duXICev8FLyxQcLdX4fcLT8bIuU6Iw,17533 +torch/optim/sgd.py,sha256=1ofELRuAWgQealztbih8pZ0Y7EMnkaFiSw1-Ts4uoxQ,20064 +torch/optim/sparse_adam.py,sha256=0oMr2m4emE6_qkE25PzFvX1iPGPVMKeVFxJ6l4Swmg4,7958 +torch/optim/swa_utils.py,sha256=iff1TX7OKX9fyvWOBQIbW09MlPvKQhCjBy3CzMPjBKY,19169 +torch/overrides.py,sha256=pm_-GMtcgGN3w1qBjRBDrzkLLDaSz27lvoLM65itYBw,105216 +torch/package/__init__.py,sha256=ZLLvoviHHErV-XQZagde2I4cuNDK49dFLRGUC5oyOFc,388 +torch/package/_digraph.py,sha256=sFyjCdLOgMvWt5Z8I5f_iliJqdnSRahZF58FPpv4DLA,5630 +torch/package/_directory_reader.py,sha256=4CzVDaz_zvXlYrgPfIJe5PGxyBHpFhMBGNiJvOeGOLM,1915 +torch/package/_importlib.py,sha256=chdvY9cde326LrzXj8VnjV_3eNqrMlhuPwhuEO0BvaQ,2998 +torch/package/_mangling.py,sha256=vbVegGbKgH5mT4dmT3bCLzXc8KT0-piCYca01Z820hw,1891 +torch/package/_mock.py,sha256=n4L1d-hoa5XO7kyAlxbYMjeZWa2vwQ_-7Ih_dZfH0SA,2866 +torch/package/_package_pickler.py,sha256=iPB6Ta2LGEnvCHRbLwUwOKkkzkhV6OSubCBgaBuqKag,4988 +torch/package/_package_unpickler.py,sha256=hBxGPJDOF1wWmGVAfvN4FMCmm1o1Qt4pDKPmCdSdH-k,992 +torch/package/_stdlib.py,sha256=t2011Gsl316cpJ1pHm1uPXCmdjRYoTVi-FakMxA-GR0,4067 +torch/package/analyze/__init__.py,sha256=RtjmM0jmYQwfuv9mQoKgZQZBij-GQ4cFcJX7_-aihDg,130 +torch/package/analyze/find_first_use_of_broken_modules.py,sha256=6vS1J-nNAUNV7ZY9ND--3t3hsVC48D4owSdzWko9LP0,1035 +torch/package/analyze/is_from_package.py,sha256=xnYu_xdTqKdosT6pJtZrnrQaG14qGLlwyCBCwqCfu0I,404 +torch/package/analyze/trace_dependencies.py,sha256=cFSGRSoWJKDfssS_iAy38GPnnYMv-uNTg62qLzmyCjc,2235 +torch/package/file_structure_representation.py,sha256=zRsqSPwCy-po1E5NjR9noGXdVJxTMos5AYOpy6ks6xY,4753 +torch/package/find_file_dependencies.py,sha256=BABD1BJNkwec2xodD0tNUd7FSEc5zrBaedst9FpwZeo,3979 +torch/package/glob_group.py,sha256=JbQhUMBRqeLFEt95MvbZcOVaMRu81nvhSxkx9_vLbng,3665 +torch/package/importer.py,sha256=ZJU9WjFxnnsI6dD2HU5EELajro-kNMV2kCUzcd7A6Z0,8892 +torch/package/package_exporter.py,sha256=K-usydJf7ebF-B4crh-XqOYJqmyIUYLi88zZKM0AhJ4,50862 +torch/package/package_importer.py,sha256=YLCwEGgnsKcBNkQhqwfMPOkQUsWOWAPA0-ZADn0E4hk,31668 +torch/profiler/__init__.py,sha256=LQ4mf1JS7JQDuey4E23e0AI1Yad-jE-rMPumsutnLDg,1578 +torch/profiler/_memory_profiler.py,sha256=GkSxO-NgAu1Z8nwDUSDKDOkquL0iDqs04mhHyG2d1hw,48164 +torch/profiler/_pattern_matcher.py,sha256=TYXQqknMRYmCzQBSD5tmYlblWTLtrEBzgKiy2pRlAhs,24738 +torch/profiler/_utils.py,sha256=XD7s-Zb5UgeKFphsvQN5RLhKwmTWxO4sgNr7IRwYkKE,13937 +torch/profiler/itt.py,sha256=Ss_JzePIXOvQZnXu-gXFbZ0_8ncdZLR33Ivz_F_eWQk,1782 +torch/profiler/profiler.py,sha256=BbzbGNHn4wg-1uTSgKbYow4nRNEiJ5B9q9x92MGjYzQ,45224 +torch/profiler/python_tracer.py,sha256=x5uqqvGYD1eyQS0r1rPpzmkSnQ7ScZt7T6tR7uOgX9o,476 +torch/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/quantization/__init__.py,sha256=26_HXlyQPbMEKh9UcXaBxPecNP82NbYXWWd0JuWeNtE,2654 +torch/quantization/_numeric_suite.py,sha256=kGqWAUJhc0DKg9RjXhiKRx7dxfmX-M5Y1w9jAEnU0Z0,779 +torch/quantization/_numeric_suite_fx.py,sha256=LkEYZQLt_CRTe4d_9EAvCzpuKKHTmTjYoXLXcawjN-I,752 +torch/quantization/_quantized_conversions.py,sha256=NEKk44IHWcW_ghu3FnR06YVxHmFU7onKMTi-PzbiuOY,4321 +torch/quantization/fake_quantize.py,sha256=AZes9LhE_KB-0Q_nV0Qy5kSOY0N9cvv7NPEpdjTwBz4,1015 +torch/quantization/fuse_modules.py,sha256=qdHvwLDuYEu2zJaAKtztSbw157nYTS5rJLCKBbBLPRA,732 +torch/quantization/fuser_method_mappings.py,sha256=AWk97tidfAL4jlhmYQgpObMusEsNc8aFAX0sxT3XH6Y,511 +torch/quantization/fx/__init__.py,sha256=s3Wh6JJbmUuv-pk4yieKjDpsHZRUkEqW04jRMT9iJPs,594 +torch/quantization/fx/_equalize.py,sha256=8LGj1MjWPuZ9i4hXy2QPmD9BBBFGhiMZtlkf9j5GPuA,1250 +torch/quantization/fx/convert.py,sha256=31cFTuIm23CT4JLdSJBHzi_spOsZs0peGyNzhny_rgk,386 +torch/quantization/fx/fuse.py,sha256=wc8SOcX_IxIWXS-E5WZhj0N6KCxpDHQGTUoAaq2Y9JI,380 +torch/quantization/fx/fusion_patterns.py,sha256=Ijjfig2bxM_YzaoEzsIPTg4NuZG6p2BPgV_Dw2bmYFw,415 +torch/quantization/fx/graph_module.py,sha256=bI7jvFL15dnuHLO8c8-tKebQO16XaWRir9jwIditoXU,573 +torch/quantization/fx/match_utils.py,sha256=ndaJFGvJfXCqUmzEyUEfRiNkDwoYV2Xxd39iFQdD2bo,456 +torch/quantization/fx/pattern_utils.py,sha256=SHrwfsEdA-cdEMur19So_gDm_cmdD9nfecvt84fv3DY,1298 +torch/quantization/fx/prepare.py,sha256=onVEmW0FUj5y3NFZVdGnYVK7KbJWroMF23pNFBSVDk4,386 +torch/quantization/fx/quantization_patterns.py,sha256=Fa7d9WBM6N44Anrq8MsanKa_M4x4hCiUMS8q-kqe5uI,2087 +torch/quantization/fx/quantization_types.py,sha256=Jo921qhPFWn9AqlZODQOjBvmQYwdXUt_vOWVpwkEvwE,395 +torch/quantization/fx/utils.py,sha256=RlQwgLPd_v1OKtjeSfFNqhbPRHW7a-rZLfKsek3uY8s,723 +torch/quantization/observer.py,sha256=ztgpjHb7Q1mB7f_i32tZRkdj8i7eYQP3uSqVGFDAWDA,1078 +torch/quantization/qconfig.py,sha256=avG-OasJvdG1szQJli3PW8eoNo7TVH6vrLO-Lv14SNE,910 +torch/quantization/quant_type.py,sha256=wwPL8MSv-G6QV5LogvkL4xRJ28Ido0yA7kPzh2dsIo0,399 +torch/quantization/quantization_mappings.py,sha256=yIflm9ncZNnznvmc6oPKxWD4fPA6C44-EeMVbtAcVl0,1147 +torch/quantization/quantize.py,sha256=g8mA0kRJIO2z63PuUcawc9bovat-uVjeUxIKFwvLb-I,804 +torch/quantization/quantize_fx.py,sha256=hBC2pUobd9oeYUliLKlIYE2dQ-6BQvdxg2xTyvkFLa8,736 +torch/quantization/quantize_jit.py,sha256=JNw2_M1d6EITYzYrXlrAsUac_4tX-IDMuAO_7prxhk8,714 +torch/quantization/stubs.py,sha256=ukEM_vZ34I3UdBnhFPn5GFL713MwFlEysXUAfYGshnA,392 +torch/quantization/utils.py,sha256=kOpHHmJ602vEybccn81hVLOHwY0CQ2YdnG9n9kOVcQM,833 +torch/quasirandom.py,sha256=H13LdePTsr7eSeCrp4v13L7T3YEKsQEcnTQvvD9WbVA,7948 +torch/random.py,sha256=60SMExvsyvhltSknvbYguvzK92X2WRLh9G9_MMxDPOs,7197 +torch/return_types.py,sha256=FptLQ227rc2ihQg89v3hGmt2LCqBizLVDAVVWCxZAWk,1485 +torch/return_types.pyi,sha256=hHUj4bIDTq9gprXJamZ5xR0U5vLVBqQRSOwE0uIg8G4,17837 +torch/serialization.py,sha256=tQ-Ab5rucd-MQS25UzIhjeyh68t05pTT5SQdqO78BWw,84881 +torch/share/cmake/ATen/ATenConfig.cmake,sha256=Fu4Jh3vhuxYcjsZfQ71erhUpBbyJr0-gwuAk6I7FqKQ,263 +torch/share/cmake/Caffe2/Caffe2Config.cmake,sha256=A5TumkyqwF9wP1Cor7darefhLipCsRot63UA0p2eBpA,5348 +torch/share/cmake/Caffe2/Caffe2Targets-release.cmake,sha256=VpdsniqKE965CQGQj61S4i1Y8KokUFm3OXCuo7VLx_A,2508 +torch/share/cmake/Caffe2/Caffe2Targets.cmake,sha256=I2tv7SPeEBzgyg7b-uaU3eRyEuR99NvHouvOD_LSBs0,8025 +torch/share/cmake/Caffe2/FindCUDAToolkit.cmake,sha256=kl99c2bqe1LwRcg7k8VkwqL99pQ9CF9vTwG-kfKIu84,38768 +torch/share/cmake/Caffe2/FindCUDSS.cmake,sha256=54i8T6FKzYP1KMtHTXaUhsuCuXHogM8SDadMAUNZT8I,2698 +torch/share/cmake/Caffe2/FindCUSPARSELT.cmake,sha256=cjNafMBEzBGv0kZAQqMxdpku0Vyf2Yrn3vJ_OVHxdUQ,3068 +torch/share/cmake/Caffe2/FindSYCLToolkit.cmake,sha256=NtqJbwIibj8MDktkvj9tKLn0GThFiknaSVupYjKsY6w,4548 +torch/share/cmake/Caffe2/Modules_CUDA_fix/FindCUDA.cmake,sha256=78mPFyIV7lR9kDJ8vAjJwKUDLpFhbssiLz5tR9CmDxk,525 +torch/share/cmake/Caffe2/Modules_CUDA_fix/FindCUDNN.cmake,sha256=NKwIx_LRJ3uu9oJC9_apdlRooZ-uR_izK1_deByfEQo,3085 +torch/share/cmake/Caffe2/Modules_CUDA_fix/upstream/CMakeInitializeConfigs.cmake,sha256=v1O1FBKmJWk1h-Zbh8M6qVlP4OqVsmCcYD8zwdVfb6Q,1657 +torch/share/cmake/Caffe2/Modules_CUDA_fix/upstream/FindCUDA.cmake,sha256=vDw0pSaSRrWrnov_LFWC7nzArhnbYNk_fvB5oW40e0M,86659 +torch/share/cmake/Caffe2/Modules_CUDA_fix/upstream/FindCUDA/make2cmake.cmake,sha256=_KLZxL3AhZehZKubThy4o2C_gEH5mm4h3kMpxLHgajU,3925 +torch/share/cmake/Caffe2/Modules_CUDA_fix/upstream/FindCUDA/parse_cubin.cmake,sha256=h3Ka8c-mmE2Majl0s8342faZWVPAaDbO1rM_4m--YzA,3439 +torch/share/cmake/Caffe2/Modules_CUDA_fix/upstream/FindCUDA/run_nvcc.cmake,sha256=xCYQQ4GWx5BsAWYZZEOo79e_Pu_pK7S_pWM-6XNS-Tw,11813 +torch/share/cmake/Caffe2/Modules_CUDA_fix/upstream/FindCUDA/select_compute_arch.cmake,sha256=iivse4vuUptTIhqI4naToyuVDa1dWh7kd13b1I-cecg,11572 +torch/share/cmake/Caffe2/Modules_CUDA_fix/upstream/FindPackageHandleStandardArgs.cmake,sha256=aLA1Dg7qyjW9Eya73fiwm2RqiG0FGwPOV64Sm5-Nobc,14902 +torch/share/cmake/Caffe2/Modules_CUDA_fix/upstream/FindPackageMessage.cmake,sha256=ToKFxPt7HSmEA014cFkMZl79quM2gpF7tmcP8h1BuYs,1564 +torch/share/cmake/Caffe2/public/LoadHIP.cmake,sha256=W9ShEpzWxU8Jja98JNOKal6xDlJs1GsBbdGlyIpgHnc,10182 +torch/share/cmake/Caffe2/public/cuda.cmake,sha256=F1R8gQxMNupLNnl-EoR867acFs-xZ8obKOLfVfJDC6w,13434 +torch/share/cmake/Caffe2/public/gflags.cmake,sha256=YrTkm-nQX6N3mh3tpY9a-i1_s_lAlifW0V_UZdAHS-M,2620 +torch/share/cmake/Caffe2/public/glog.cmake,sha256=zy1mZaicXNUHxSG7eOZ4ZVnbgk5qN-IuG_-jXUi98nk,2320 +torch/share/cmake/Caffe2/public/mkl.cmake,sha256=y1E6axKvT-AvgfmJrVMptYDVJ5svt723jNuQYIJp_kg,1317 +torch/share/cmake/Caffe2/public/mkldnn.cmake,sha256=7D8oS35genLaLdSPfgh3foWXQMsa2bUsQnB5WKfKPAA,444 +torch/share/cmake/Caffe2/public/protobuf.cmake,sha256=weW3OuHBIqIt0KpWRWzAnzSCu-AJ9naUfkjxcvaRl5c,4003 +torch/share/cmake/Caffe2/public/utils.cmake,sha256=gJ2xgph--GB1BYHS_zX3D6THncwfsVq1YeFsKAMgnRU,22463 +torch/share/cmake/Caffe2/public/xpu.cmake,sha256=zK2k9T4Eq3ElVadC_ipnZt30Njcm0DMtsTWjkuQOpVY,1156 +torch/share/cmake/Tensorpipe/TensorpipeTargets-release.cmake,sha256=F61imoXwiRatw97-TefIPmRwlsVTdvAXItQDGEFGGPI,1803 +torch/share/cmake/Tensorpipe/TensorpipeTargets.cmake,sha256=C5p4srud2XVJzlY-wT_mEIbHSEj_XV5y52f-Dh59SWA,4685 +torch/share/cmake/Torch/TorchConfig.cmake,sha256=LRN9sMHwA-0Ix1HgxVQ1FR7XqPTcjKGCjaxIwZfj4Rk,5070 +torch/share/cmake/Torch/TorchConfigVersion.cmake,sha256=afkiN_xruUoSOaGJ8JKeK-pgbWQac65krclbQete2X8,366 +torch/signal/__init__.py,sha256=UvInN-gZ-pXXT9wvdSjrESHuSVNz5qEcnLMRzycbXiU,46 +torch/signal/windows/__init__.py,sha256=pqkf3lWIkwGpjDn_njgkrpqzYYCBpC25zEK7iD4FGaI,383 +torch/signal/windows/windows.py,sha256=nSQMabT-S5caVZjiRj_QBqgdexmobnV53cvnMGBT0Pk,22654 +torch/sparse/__init__.py,sha256=EW3nqh0EI3PpuUV0WYfzgW0u9u2mNZj5P_FpIQjYY_Y,25518 +torch/sparse/_semi_structured_conversions.py,sha256=4MJbS-JR997kLPEb24_h6Z48wYVP5Hg4PyDplP8gH5w,14016 +torch/sparse/_semi_structured_ops.py,sha256=daMED3IKtSPGgTroNBfvBjB391FLpcbqPrLBJ6QA0VE,6372 +torch/sparse/_triton_ops.py,sha256=A2QQ3UTGBJPxvUM2Vw_HIBsvY4vdGh4Pst-VYlEj8Uo,86150 +torch/sparse/_triton_ops_meta.py,sha256=Ycv7YsE36kR2kCbXHggRBIv3E3efmUxs-c5SL06eNy4,500784 +torch/sparse/semi_structured.py,sha256=8sS2gsADxMA_CMWYwML8FkjOSEpiFAi_WMcw4_2Ejgo,28043 +torch/special/__init__.py,sha256=rM7i9gy2jZvNciAWejOQRJaJBuj8XIHr4ki66BkNkIs,32735 +torch/storage.py,sha256=7QbeAcw5itf0KLRyBJ8HlErokqoaENiL1mu6l95Lx4U,52353 +torch/testing/__init__.py,sha256=8bgnxGIy9DJeX9dIh6iTPqrlGDQqeAEkmWvfhLdDnP4,187 +torch/testing/_comparison.py,sha256=KMRINTtN0N_DIy-Dn_7mr_BF8dIoC4BxlFAo-oHBf3k,66077 +torch/testing/_creation.py,sha256=vNUigrFArRjSWS6uxGVjIKF7M3P932P9gZ7AAHxW5pM,12198 +torch/testing/_internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/testing/_internal/autocast_test_lists.py,sha256=IGP-0Sh_lsYKzazvn0Ruo4pA-mYRszP0sYRxnJ07ShE,28391 +torch/testing/_internal/autograd_function_db.py,sha256=K78GnkbWfkS_KNQXxQSQ2jt-bk3vLUzaqTmLVdvYvas,19613 +torch/testing/_internal/check_kernel_launches.py,sha256=KDW-fjae41C_aetbWyQGeXsEBWpv0cQaM9S57AdbQPo,6027 +torch/testing/_internal/codegen/__init__.py,sha256=8QLhisbHub6VJl6egijnrOPKK5QNAe5FJhfcxEelj4Y,22 +torch/testing/_internal/common_cuda.py,sha256=DSSfPnGTOUtz9EP-92XmITCZ30i6uqzQU3x3LkZzojs,14728 +torch/testing/_internal/common_device_type.py,sha256=CLk9wrbVw7ij_a7js5VJ8nWqdZ7r09-NXljgrhrcqPs,72905 +torch/testing/_internal/common_dist_composable.py,sha256=IwtTq8pjXlXDotDZE_Fpy1o7tkpAP_WnNcx05GpJ3YA,3577 +torch/testing/_internal/common_distributed.py,sha256=yistZXaXxHfrAz0HCHh3RkFgx2l5tWawebIbRwTmM0k,64637 +torch/testing/_internal/common_dtype.py,sha256=xeAR6wfPNSFTwjmEolnFOk8il47JTDS5IqS-Rc8Eir0,4864 +torch/testing/_internal/common_fsdp.py,sha256=A4cptjvCs4PVUPn5Pd3tuP-7QEzYODHfolad5pZ02CQ,58019 +torch/testing/_internal/common_jit.py,sha256=aNJbM6ebSEfPaj3Ezyz1T3NtaFZ04gGCpr9e55aYtyU,15847 +torch/testing/_internal/common_methods_invocations.py,sha256=egJX9RnglpxK8wUKxgTh24EHjaYdQ9tI0FgugTs6p7c,1206661 +torch/testing/_internal/common_mkldnn.py,sha256=mi-zFdrl-jNUdhRtnmfkbemDHY3wUUUoXT4nx3GfbRk,2286 +torch/testing/_internal/common_modules.py,sha256=rDA3HjJd8oFOAl919xYBzI88LDgjWtZC6ykd_IEqk0E,216587 +torch/testing/_internal/common_mps.py,sha256=uL7-upiMNps4BOugkM6ZyUkg5VuW9OlmVNRi7KtqI5k,37664 +torch/testing/_internal/common_nn.py,sha256=k1OVGhdTWsoLK_SVyuVX04IAiHFMuNd7Ly_aaf6EXfw,172511 +torch/testing/_internal/common_optimizers.py,sha256=5jeD9YpD-MMzdsBmY7WDc5ceqr1bOt-FTeSVNcYG138,82356 +torch/testing/_internal/common_pruning.py,sha256=nYwQmrHvNQpRmIkuxK7yYki2CwmaMVJKsnIW_Z-xIw8,13655 +torch/testing/_internal/common_quantization.py,sha256=VQyoYALnTw3tNTg3G9LVUXNMXin-5o32NUGiBu28ASA,115782 +torch/testing/_internal/common_quantized.py,sha256=jwxRxmmHLGJiJwrgJVhrb2s--aWzgKTcqKSiS0B5YkA,17713 +torch/testing/_internal/common_subclass.py,sha256=KuR8kVBkExHzHymRPi9T4tSbkiWTbXgltgr1Chk6-3Q,12194 +torch/testing/_internal/common_utils.py,sha256=Z-7x0CyWWkg47uuDTqybIJVP9Q4DwCD60MwTjCcUSxA,236933 +torch/testing/_internal/composite_compliance.py,sha256=Tblwu1ukxw4A-HwnfveCwLL0N9zCP-QmK7FcgoRwfqc,26025 +torch/testing/_internal/custom_op_db.py,sha256=O-KOltP37Q_2ee7leslEDGSytAV6-sIYM_MPFL61o2c,19645 +torch/testing/_internal/custom_tensor.py,sha256=Gu9LmbvvlVJRUUjM49EIQpGrcOCbZDfKs8BuXbtCtmg,5220 +torch/testing/_internal/data/__init__.py,sha256=8QLhisbHub6VJl6egijnrOPKK5QNAe5FJhfcxEelj4Y,22 +torch/testing/_internal/data/network1.py,sha256=ksE5iUCq6Hhp8772pRhqpUna414huKdlzvh92oQgxTc,169 +torch/testing/_internal/data/network2.py,sha256=FlOrR6LuubeOhZHKURvkoWqIGHlvMNX2SyoZ22XPvfg,199 +torch/testing/_internal/dist_utils.py,sha256=WBlzvl4sb7bXl8T_Dv_-LDgZtUp4bdGCFfCNpbDjWck,7255 +torch/testing/_internal/distributed/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/testing/_internal/distributed/_shard/__init__.py,sha256=mIQlHRV-d0Zr3TOwF0Vr4fBPuPk4uNR8rzKPy1EhlPw,27 +torch/testing/_internal/distributed/_shard/sharded_tensor/__init__.py,sha256=GG4D85kYS3A-C91son7woTrb4j_tfVZz7nCO_K2XueY,3204 +torch/testing/_internal/distributed/_shard/sharded_tensor/_test_ops_common.py,sha256=Ngk6Ynz4RSlUmj5oDTB0d-1jr4hl7DiVvyGy1IZefcw,4011 +torch/testing/_internal/distributed/_shard/sharded_tensor/_test_st_common.py,sha256=Qys7mhe_-Ew8m14WqTGlJ1VJf_gINSZyraRfyDNP44w,1618 +torch/testing/_internal/distributed/_shard/test_common.py,sha256=0siUUMgcT2SqfCOGQvb4_k-_r2yHoOALNG0kM1OTGwc,1219 +torch/testing/_internal/distributed/_tensor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/testing/_internal/distributed/_tensor/common_dtensor.py,sha256=-gtKsqHcSqBuzIMuJ56FF_KyGFYOSalw1hO4Te5gcz0,21769 +torch/testing/_internal/distributed/checkpoint_utils.py,sha256=HkhBP1tBL4e6bZpIJZLUxO-QTgVDyiFjqvsa30l0SK4,5136 +torch/testing/_internal/distributed/common_state_dict.py,sha256=G2_DpKRea40PVSMIWEvYp8jKt8mZ6YWdScl8W8q3LIw,6705 +torch/testing/_internal/distributed/ddp_under_dist_autograd_test.py,sha256=LjhVFkvVHeoi1LGfz_yfF75aP949jGlZQiuCxdk0cAk,26953 +torch/testing/_internal/distributed/distributed_test.py,sha256=fE807WHNcGtMjkiJw_5thvIuWJxAe2pZvjzqAtq6m2w,437044 +torch/testing/_internal/distributed/distributed_utils.py,sha256=xmssLRQCNMLe0Q6mu1lAir0cD9gOyGXyTMoM9LwE_lw,1947 +torch/testing/_internal/distributed/fake_pg.py,sha256=urvgeWsPBu6RrO-fweSKEAlNIXFciyJhIsFs-UPqvmE,1033 +torch/testing/_internal/distributed/multi_threaded_pg.py,sha256=jAl3prpcndkyMhIQP5juZO5RwvIPUsCdidMCOvGCvqw,19302 +torch/testing/_internal/distributed/nn/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/testing/_internal/distributed/nn/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/testing/_internal/distributed/nn/api/remote_module_test.py,sha256=mR3YrhcJyN6CaPZx1o0Ht5o8aR3Y5iat9qQ0gWxy0Qw,29727 +torch/testing/_internal/distributed/rpc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/testing/_internal/distributed/rpc/dist_autograd_test.py,sha256=3y0TZlPg0YqCwocG53A2ndVce5tJV6sQlV28z1Ml4cM,107082 +torch/testing/_internal/distributed/rpc/dist_optimizer_test.py,sha256=HibDFt3xUTuOmm2mXOAnEzCctb48WKGzNPWZ-BRr-hk,10574 +torch/testing/_internal/distributed/rpc/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/testing/_internal/distributed/rpc/examples/parameter_server_test.py,sha256=QZxy4KtSAPDiCETSHqvZaH35WnXUuwxy22-ya9yz3-Q,4555 +torch/testing/_internal/distributed/rpc/examples/reinforcement_learning_rpc_test.py,sha256=TzFyGK70YPru2POUZWGpOAzmKPAWllzHxjmBdeixgWM,9255 +torch/testing/_internal/distributed/rpc/faulty_agent_rpc_test.py,sha256=zcYRImWXoIg0qmVWpkPlTKA_t0neQPYyi9YXm8IdgTo,14239 +torch/testing/_internal/distributed/rpc/faulty_rpc_agent_test_fixture.py,sha256=ziVR9Z4tDhdG0omKEvGVfOXDN2I10eeXCcMYJ6G_vYk,2142 +torch/testing/_internal/distributed/rpc/jit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/testing/_internal/distributed/rpc/jit/dist_autograd_test.py,sha256=bzq_ajaMCiVEFFmI1IsrK_bRKneEDMyyKOkXuiuq_ZE,4172 +torch/testing/_internal/distributed/rpc/jit/rpc_test.py,sha256=1eDbRFz5BDG0HKUL3IBsJLzV7GX4eFwOwv7MHcsNJL4,46880 +torch/testing/_internal/distributed/rpc/jit/rpc_test_faulty.py,sha256=8TjJOzAvAlbE2qy3izpDBUHnUzog6r7jjEPciYoLs7U,7974 +torch/testing/_internal/distributed/rpc/rpc_agent_test_fixture.py,sha256=y5P27BCxk82IPTVOzov4-Ojr3_tykIOGlLlSEwWEv_w,1874 +torch/testing/_internal/distributed/rpc/rpc_test.py,sha256=TvUEezycjiPrkTzUSedn_app3md3ZUYiVZ3-qdCnE94,226049 +torch/testing/_internal/distributed/rpc/tensorpipe_rpc_agent_test_fixture.py,sha256=6RTlsS0sKBh2iZqvLm_ieqIQpjSuwqlYc7qdgjb96YM,974 +torch/testing/_internal/distributed/rpc_utils.py,sha256=_ytL9c2DUzSMzTVyoOTKg8UJVlINNdy0R4mtFXBR0rI,6615 +torch/testing/_internal/dynamo_test_failures.py,sha256=SrES_-R238D68dPVZQ5r1n35iC9P01gBImXQPH0iOjE,5440 +torch/testing/_internal/fake_config_module.py,sha256=m7LTx36uQonXPnUIh974Q7FcJHUzPJyF1jvTB1y9BbU,1253 +torch/testing/_internal/fake_config_module2.py,sha256=gk8fSMjiMktUYrbdWi-Yvm4aH6TeOBa-7nnB5SDMZwQ,343 +torch/testing/_internal/fake_config_module3.py,sha256=AupEKLn6rt5m4EKpKAUtD6L1YZtVTJOOuxLaJqmAwwU,218 +torch/testing/_internal/generated/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/testing/_internal/generated/annotated_fn_args.py,sha256=ZxZebgyN1cgKuWASiELHeKUPiAiyjJAl2n51lz2F9mE,550494 +torch/testing/_internal/hop_db.py,sha256=uyA46U-4q_ahcyyeOCkrYrAgvN180eS1FldzOLgvCII,13923 +torch/testing/_internal/hypothesis_utils.py,sha256=Y3DWgamCO1gKq5W141ecnpbA7uKtgnZd4U9SJk2Xrsw,14622 +torch/testing/_internal/inductor_utils.py,sha256=6fHXwAfDXAmxRn8_edGhmFVDrCSBYmTwooNqlxYlXP8,11138 +torch/testing/_internal/jit_metaprogramming_utils.py,sha256=1kwm5pbc86-z8EJJgadxnDJogN7Km6P0p8Z9PaIbtn4,34057 +torch/testing/_internal/jit_utils.py,sha256=4Pf0ma3dxTFfxPsMxvVp4R65iTPEKh99qy7Bcb-RZ58,33951 +torch/testing/_internal/logging_tensor.py,sha256=GE23NCZ6hhwkck48gOOyOFO5SigntV7VeSxUdNF8juk,6646 +torch/testing/_internal/logging_utils.py,sha256=Nw60d045h42VlN06V1oQ3dvIYikRDg89CYNHk8gQCRk,8205 +torch/testing/_internal/opinfo/__init__.py,sha256=6PWvlARagSjyrZW5xCOA5YGQjLqwaijc36TFX4UrU9g,116 +torch/testing/_internal/opinfo/core.py,sha256=AdOHgXz1RwovRHyBFXsmaPnRmpuH0j81vqPtg1rJDys,123946 +torch/testing/_internal/opinfo/definitions/__init__.py,sha256=8IK85dB9S_tx3aiyuUwfSYLKgoGRaBmVnajBKn__Vqk,452 +torch/testing/_internal/opinfo/definitions/_masked.py,sha256=cDCLOPr48QsgGvweV6MKvpXL-nw8hSj4HOq-TWiKyMw,46439 +torch/testing/_internal/opinfo/definitions/fft.py,sha256=Obl4XOqN-AA0J9eNBvF1Xgs8lHpD_qBotZ3fATffRos,29445 +torch/testing/_internal/opinfo/definitions/linalg.py,sha256=FCVGDfhvVoxwCLF629XtBDtISVzjVt2xf9kJ8U5H86g,83990 +torch/testing/_internal/opinfo/definitions/nested.py,sha256=La40O5kyOtO_dfuBxYsyvAMvgvcxVZxDy0X76g-7xx4,59720 +torch/testing/_internal/opinfo/definitions/signal.py,sha256=ZwEc-jrOHBFzJVC4SOlgyT8MABFUwO4flLV_EVcEBCk,15342 +torch/testing/_internal/opinfo/definitions/sparse.py,sha256=QBGUIpw00LuuCnFPdrKhlqs5-mu-Y1SrxUoBGp0W8_0,33781 +torch/testing/_internal/opinfo/definitions/special.py,sha256=psYsZInA0_mBJzqeLMOJgPef-QP7MkCCdGeL35zE084,27544 +torch/testing/_internal/opinfo/refs.py,sha256=rhbUtkE6NiONK48DR72RB6-JLzIbqXh5JvJ5vw0daQk,8039 +torch/testing/_internal/opinfo/utils.py,sha256=JDxcfGMl0uPDIIEtxKDHJXXYBJqVjzQMTNeuTRsBCE4,8722 +torch/testing/_internal/optests/__init__.py,sha256=o-8t0Cva860Tcw0Ig_-rBr6-9Uuc9bDRWRMG6FgKzbA,372 +torch/testing/_internal/optests/aot_autograd.py,sha256=ZqrOQIdcdHvH-ubmB0oHFq5Ooa6tT1hWXNpM1SvHjoM,6434 +torch/testing/_internal/optests/autograd_registration.py,sha256=NTcmeU6cAPjNIfzcet__ZD4tPZIW2gpXy27U0DQprPo,5692 +torch/testing/_internal/optests/fake_tensor.py,sha256=WuT0PGbogjTGPJeRYo2JiQYoTzj_iZET56L1zkv0W4w,257 +torch/testing/_internal/optests/generate_tests.py,sha256=9hAyfVO4pdbf8TdqKJl179fdwfabTTY9H36Y2-ZuKpQ,31776 +torch/testing/_internal/optests/make_fx.py,sha256=NtdQVRYx1E7dkxjVDGcpd5ZNSoGlntV7Mqr17ANPGuY,3268 +torch/testing/_internal/quantization_torch_package_models.py,sha256=DnChjrnLG54TWRhINvfTxOFmNZt5WflDBJDpam4fj9w,951 +torch/testing/_internal/static_module.py,sha256=bwmblZ7N3UtKuO6UEJDUUJdqdhxxIpCwvmrJEESauOk,893 +torch/testing/_internal/subclasses.py,sha256=1St6tCwY8ANR6ShOVrCx_5eiKUczI5xulrQ3PF1GGFA,2530 +torch/testing/_internal/test_module/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/testing/_internal/test_module/future_div.py,sha256=298hLJlLz2QCJ80OVQXQM6CgN71nbkFMNnkesgzSnY8,114 +torch/testing/_internal/test_module/no_future_div.py,sha256=sksxzWFUupRBbSThS69P366IwnWBNGJe9EqQx8m5EKM,145 +torch/testing/_internal/torchbind_impls.py,sha256=mBq9E00EyP8vFSEvOcPPaA_49sS1fBw4lDr-G4fF71U,5256 +torch/testing/_internal/triton_utils.py,sha256=i9s1-zM2oGHnZN2lGInqqKgds5zmj38ftvMh1aUldJA,29342 +torch/testing/_internal/two_tensor.py,sha256=Z0efSEeFEVXUEwIPyeYjO-PEWfFwGGLPRhv7Z-Xg8yY,3601 +torch/testing/_utils.py,sha256=Qsxfa_GOOgLO6bC19Sx9MXKTtr94LNC3brfdkjqZKRU,2039 +torch/torch_version.py,sha256=MheXhDr4r0O0mDO_Eaxz7vYLl0HRWr8hnQciPgx4QuI,2530 +torch/types.py,sha256=dM8DCdxMF6JwTpjWK-VCJlP8KRAMKte1j41H8vb5LHg,3687 +torch/utils/__init__.py,sha256=McH6WeVu1fwTyFuHPlXFTIe6j6udvuMc9YyYyO-RxuI,4061 +torch/utils/_appending_byte_serializer.py,sha256=3ezL10EhhJ_pmatfbWZHtXGiERi1gxxXbs4onRopfy0,3657 +torch/utils/_backport_slots.py,sha256=ApEayoUoQ_Q43QDs43v3PTSvTVZW9StOVPGkpb9gvBE,4605 +torch/utils/_config_module.py,sha256=bdv0msUSCzaNUzPhbH6uHCwrjEeFwsdM1EClXh5xjpc,29801 +torch/utils/_config_typing.pyi,sha256=PUOH5PeJ_x5yF_Ob3HXT5fFM0PW4rN_DLlOzNT_1XYI,1219 +torch/utils/_content_store.py,sha256=OODkt6D32iRIVdHcRH0YlbdrQuzTnonIXt4CcBfr8t8,9222 +torch/utils/_contextlib.py,sha256=XsEQW5J_d-1ISiR57g93hbwAEYgMRPxnrQ04IxhE7aE,6026 +torch/utils/_cpp_embed_headers.py,sha256=KirpX3zda3PVin9RtsQAJk5lvr7ug6suwtjujA6imn0,1806 +torch/utils/_cpp_extension_versioner.py,sha256=c6b_dRx1ETqW9VdOqlmsGNEf5eW7566B--UoGaYitbk,1938 +torch/utils/_cxx_pytree.py,sha256=-hfDiIARZQKJ9Lla3UZzZmonwowar4OUTk27rRhXZEU,38302 +torch/utils/_device.py,sha256=PTz-0Dk-qgImpQu7qIIW9xdiXQVtEziSautL6aEWVF8,3709 +torch/utils/_dtype_abbrs.py,sha256=L-w4-UnjmD5QeuGbwRqilJ0iCc9Q1XsTjQUvygG0GE0,755 +torch/utils/_exposed_in.py,sha256=7XFQf5ScUNIIEbv3f9rkk_iqscTttF7g-xP7xQRo9oo,693 +torch/utils/_filelock.py,sha256=gCLF9B3MBcBdKfs3DejL6bYpxSh0TCwo0OKL7tEA2pw,1567 +torch/utils/_foreach_utils.py,sha256=ACSrkRq6ybv07tYpGcAnQFK9QzZYN7kpLaee_96mIF4,2427 +torch/utils/_freeze.py,sha256=APGnhwP3ZDNyM4ypHOHgPN5_U-A_CL1MSi0_0BgaLnc,9996 +torch/utils/_functools.py,sha256=YnCRB4UUXnNjsIPzzHG-WQaqnudSrwF3qFOH9k4PnxM,1423 +torch/utils/_get_clean_triton.py,sha256=MjPw0MO7Qy8FR01OSe611mjMI1qxDj6XTeq5qiZUGyY,6968 +torch/utils/_helion.py,sha256=JcmLdzWOPs6yfikYYZDIzWUSejX4gF598lAo9x2Sbcg,364 +torch/utils/_import_utils.py,sha256=JZ--HpC7TVfTHhisDamxF6WNGjqECLeZoPEkfbebAdA,1329 +torch/utils/_mode_utils.py,sha256=sYjEYYBLwkP2g45Acigb32vqjNru3RgCJ-Efd1ol0A8,255 +torch/utils/_ordered_set.py,sha256=3GoOgBB0Lc_zoO-8jJFQm3XO1h1cm7qEa7YesDoC918,5512 +torch/utils/_python_dispatch.py,sha256=RJihfY0bjKm6qj0GMBwVtiz2A_mhGBUQ_KPrr64RoRU,28557 +torch/utils/_pytree.py,sha256=mqm5NMk8JQxmyhmEJC4VV5GeZL2pSQ3lMkX-3OF1Ng0,71788 +torch/utils/_stats.py,sha256=snLdJ0aaFfOVaMvHb4gcW9qKZ5xa4YmQijk-88U0Z2g,1014 +torch/utils/_strobelight/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/utils/_strobelight/cli_function_profiler.py,sha256=vwOK3vsKTcf6c76wqlvZeflrH0kDy-9HG6pH6m3QKEI,11341 +torch/utils/_sympy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/utils/_sympy/functions.py,sha256=FRovkeI8wSnwK_vdYZkrTdVM_QfAKS1PTBwXONLcpuY,50787 +torch/utils/_sympy/interp.py,sha256=y3Vc6DkL0z-I_ihw001aS9jvx5Jb3V-IdUOyWDuehWE,7141 +torch/utils/_sympy/numbers.py,sha256=OYV2s_DBM7wpniUM9FTrS2fYvcBxVUIrHk9-m3BChjQ,11401 +torch/utils/_sympy/printers.py,sha256=UHVb_jo0uPta-xnyASeIEUYg-M6Q0NwJkPY0vjb5-fg,20555 +torch/utils/_sympy/reference.py,sha256=jI0y7Vc-H29in7aAqlTCfOTfpenEYDZUzAypJkVMwXA,13604 +torch/utils/_sympy/singleton_int.py,sha256=9PUdm9IPUBHJRrDyvRN_ycN_b4Js22Zr-6Uje17KLTM,2967 +torch/utils/_sympy/solve.py,sha256=1sK6IgFv8OSxOkkIXNp6iWgXuNXJQVh957L9DHR4lPA,6509 +torch/utils/_sympy/symbol.py,sha256=px8ORBa7esbEAn3oc3pcIdq-uRzti8Bu8tER9RBz8GA,3719 +torch/utils/_sympy/value_ranges.py,sha256=hvixaEUhXNh-yAbo7abWfKIloj7RbkpAgPfgXkYGmq8,35220 +torch/utils/_thunk.py,sha256=ahGim6yrtjXwVmcSyyw-3J0eQzr9hloQE3vaXt0IbmY,625 +torch/utils/_traceback.py,sha256=ezygBvlx4w_bBEs-f709eNYoK6kWyxrdKgbZzuzKbJg,10248 +torch/utils/_triton.py,sha256=G_cYE2aMkGeGlrhGh-D1HjDXs5tYPIIOr0rEdvQaxt8,4348 +torch/utils/_typing_utils.py,sha256=3PQ_pcjyV9gxhP2cL-D3XWVIv2asfY1wR4JUBIUlK_I,378 +torch/utils/_zip.py,sha256=V8LnOEydaOHwTZr3j2owsz6OCcg6yeDBzGrjCyog2Kg,2455 +torch/utils/backcompat/__init__.py,sha256=Xe5tRlobHKdgFsI9vLiVS_amqSIp_elpybE_2LWx6po,662 +torch/utils/backend_registration.py,sha256=v9ilesYVTiZH9M2bIc5BQ0QP5yWZtbSYRvpx4fik4jQ,19485 +torch/utils/benchmark/__init__.py,sha256=VMZoFTt8YGaScu3-5L1uWwMOED8FRYoEBAX37RFJ3c0,411 +torch/utils/benchmark/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/utils/benchmark/examples/compare.py,sha256=Vp5KSGUDOYAE54NMXRHXImY7MjEfpfgO3XZRcntJsUI,2915 +torch/utils/benchmark/examples/fuzzer.py,sha256=xMSTZ5wa8d0TK5XLDBdffNHGManXq0NTcdqGHQhvlGs,2650 +torch/utils/benchmark/examples/op_benchmark.py,sha256=oCr3uM1db1zmVrgSMyaB9cxMwjSKELQXjbGFEv6mu4E,4227 +torch/utils/benchmark/examples/simple_timeit.py,sha256=ioYkChBgAPHwz5gWqTfUFTCiqqk6CRud3Lob1tFe9uM,541 +torch/utils/benchmark/examples/spectral_ops_fuzz_test.py,sha256=3yRGHfcj2AxlB4Hf1iL2Eq7Ho6LI4JV0G59ATHWPLg8,4779 +torch/utils/benchmark/op_fuzzers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/utils/benchmark/op_fuzzers/binary.py,sha256=hqr5_VG-GMwDiNCRNE4nqjBZm9mwf0QvOqtzQ-LYjIk,4136 +torch/utils/benchmark/op_fuzzers/sparse_binary.py,sha256=4COEJAYa4kQWyPOQYBXFuSDFd4BLKtoLxYqTV15TStE,4218 +torch/utils/benchmark/op_fuzzers/sparse_unary.py,sha256=dTC5UmD-BJO9d99RMYxbFuRPJhLFHsJgRgyt1IQZVAM,3246 +torch/utils/benchmark/op_fuzzers/spectral.py,sha256=tb2YizjJzwteeoQpS1e3l5jqzx5j7QmNHKXRAz0h6Ik,3624 +torch/utils/benchmark/op_fuzzers/unary.py,sha256=7MpUeiaL6_0zhEKB6oHz0Rt_CHgNSZYhTKOy1hvZvNA,3146 +torch/utils/benchmark/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/utils/benchmark/utils/_stubs.py,sha256=PI1zkojK2tgsszW5gnHnyfMwojKOq3M4VhlnimefV4g,999 +torch/utils/benchmark/utils/common.py,sha256=a-eWyu4sGEqznxBFjRyOav1bTNZvT0X8VLd92EIGibE,13660 +torch/utils/benchmark/utils/compare.py,sha256=EsXEG5QpZZAqXOk4cOHn9SXsB9k9uHAfJT2Fm0ejCbY,13270 +torch/utils/benchmark/utils/compile.py,sha256=0ezsAamJCYycynY8eYm0UcR-DjsFn_ntLUIqEsMxZn8,7602 +torch/utils/benchmark/utils/cpp_jit.py,sha256=2lPzqtSbfjIREjZqFlqo-RkhoBd48B_SvzklpfiVV5c,6805 +torch/utils/benchmark/utils/fuzzer.py,sha256=9cgsyWS1R0Lj6xeOpwXd8Maq0hGyVL-sGbL9Eo3hYVE,18367 +torch/utils/benchmark/utils/sparse_fuzzer.py,sha256=wenl86d0425y7fyi7yejTUEIwahfHoszWQk8fSRcqXQ,5160 +torch/utils/benchmark/utils/timeit_template.cpp,sha256=Wzz-o6Yjgq3tkmUTxeRldQxrsETh48T2hwL8xbOkRSg,1009 +torch/utils/benchmark/utils/timer.py,sha256=Sj8ru6FCFrIUdNUkZPJ7B7PoyBP-0Vt5tmsGEIfFUtE,21199 +torch/utils/benchmark/utils/valgrind_wrapper/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/utils/benchmark/utils/valgrind_wrapper/callgrind.h,sha256=wK1NVdRImF_4WVLlQrXkufunvE0qLr5dw50v86KPIo0,5744 +torch/utils/benchmark/utils/valgrind_wrapper/compat_bindings.cpp,sha256=ysuof0blt-4g76St1LUrdlZLiaCBKjwjwZXE0L4nI74,813 +torch/utils/benchmark/utils/valgrind_wrapper/timer_callgrind_template.cpp,sha256=ILVnffXHThuakEj3hzKv4usmt8rICdXpSTBeWKz7jU4,1676 +torch/utils/benchmark/utils/valgrind_wrapper/timer_interface.py,sha256=1OohF_CDzC5pMM99QlVhzDQwyLWoQMwTyOeOl5yTMBo,37262 +torch/utils/benchmark/utils/valgrind_wrapper/valgrind.h,sha256=8MpV41sjwR0bIML04pxlLIjVuGhRtWdy1Kmtax4jFLI,422653 +torch/utils/bottleneck/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/utils/bottleneck/__main__.py,sha256=rcc3etV0u3JSHYTmUZRi23lJz-E3LqUIj0vXr-L0-XY,7190 +torch/utils/bundled_inputs.py,sha256=JTY3lZ6nbiUbCnXbJsQi4a859wdTl4YKWK8MGqNDgeM,22582 +torch/utils/checkpoint.py,sha256=JV7C7M-hhOs-E-9HkZVdH28BDY7x9Z56jsy52iYWExQ,68071 +torch/utils/collect_env.py,sha256=Lsw7fmwesJfURq_5u3Kx4-alEZxVC_SmQAQr4PVygL8,24503 +torch/utils/cpp_backtrace.py,sha256=GxSqoJwxCh9nbgm7EJS9KRJ0Mn5UwIwJUje-0vC4C-Y,483 +torch/utils/cpp_extension.py,sha256=yGDVYSghwT8damx2q1m51hYusmDwQfRmh2-Ii_GX1ec,132431 +torch/utils/data/__init__.py,sha256=Otj8bSQaqlFBPEvdrTGo81j_wSk6V2ok4ZD0g9RatYg,1654 +torch/utils/data/_utils/__init__.py,sha256=teNSfYfmOXzz8rjO0ypF8IWXnMOIX7jHpvoDK-VPXJA,1625 +torch/utils/data/_utils/collate.py,sha256=mczU6V-Pkn0TJI8_F857SUxQXj4DExzPSTE1PK-24g4,15964 +torch/utils/data/_utils/fetch.py,sha256=V_nDlVcBJqpooFee1a_k0yf9HVVnkpr3TNPZX4i1Z74,1953 +torch/utils/data/_utils/pin_memory.py,sha256=ck5LQXDXPPRocJFXk6EiQ4bWPugnJvyO5_x2kTf1RWY,4451 +torch/utils/data/_utils/signal_handling.py,sha256=-ZW2LiTfOn6P2Mqi6pQBPoqFi2ZTGxXwC1v6BFvDQk0,3171 +torch/utils/data/_utils/worker.py,sha256=02Us-QLfUBpugkZZtaB5QdubaJg0oqzRjavxby4GM-g,13800 +torch/utils/data/backward_compatibility.py,sha256=iVwgc7nHC98OaKE5yYMXPiMoWjAqxSVNewbENz9WU00,309 +torch/utils/data/dataloader.py,sha256=-9JgyVh_rbL4HcJVsIA1YmHfOmgnJywH8RqxrCAnbvA,79585 +torch/utils/data/datapipes/__init__.py,sha256=OIYy5fRjiWdLc8SRrug0pOjrXouG0I-f2G8QQB0ynto,88 +torch/utils/data/datapipes/_decorator.py,sha256=g-0SoTyNKpy2GjIERa5PaoXhFSHJUOjPCy00Md9P2AM,7832 +torch/utils/data/datapipes/_hook_iterator.py,sha256=mm8AxXojE6NlB3UIZKRbQOtrbdH9bgZM0T_c9wK_80Q,11949 +torch/utils/data/datapipes/_typing.py,sha256=qBbN3NAYfshx9A2Ch6JAq0EKOwdjZ_CYUEjZQEfeFck,16294 +torch/utils/data/datapipes/dataframe/__init__.py,sha256=zg3Y8ywGmOOMnssugIi2Hd2h95uG2Kx3VmMjRVOaNOU,331 +torch/utils/data/datapipes/dataframe/dataframe_wrapper.py,sha256=cJ7u_aPzzg3_OHhvfWkh3RKfd5Uq6_v-4JTlYRzDqVY,3293 +torch/utils/data/datapipes/dataframe/dataframes.py,sha256=KqOoMCvT9Iael0qSHbhMAhrKj3dPdOruDkfGaxVOjac,13468 +torch/utils/data/datapipes/dataframe/datapipes.py,sha256=UwFjL8j6h4IGnT9UY7U4UnAoNq68I9Rb0PLKCHGrVa8,4537 +torch/utils/data/datapipes/dataframe/structures.py,sha256=7NKwwvZI2CtjpjvA1eJmyFQcbet7cYXdaxSMTFPZdx0,662 +torch/utils/data/datapipes/datapipe.py,sha256=enzAnRKQRtFXWqI89i22uZTzoYbW9iuEwgep6hWMhvY,16796 +torch/utils/data/datapipes/datapipe.pyi,sha256=UW-MHXrvNFSJINzTSxr_gpY1OaY3rReyTaEtU_hPN0w,32415 +torch/utils/data/datapipes/gen_pyi.py,sha256=kSyHih9h-JRt9DVlxKgmuKnzwH8XjqETrIoPn_n25d0,11818 +torch/utils/data/datapipes/iter/__init__.py,sha256=Hr1OY3BP9JmWuHzG2_6Lw0rsfrMZLfe3S7CpshN5tYc,1815 +torch/utils/data/datapipes/iter/callable.py,sha256=yhy9wxSugH2GLbPt5WgdzKsdN3P9p1ZMvm0xmtrpYyw,9065 +torch/utils/data/datapipes/iter/combinatorics.py,sha256=7XL8fqckeEpVCbAjW-wpZJI6_gbHdD_4kcRGIYfK6GY,6460 +torch/utils/data/datapipes/iter/combining.py,sha256=Xi0kdqkpuM6KKza1izRl-gMH9B5VVUK5UJ3BQ-2Q-bk,27318 +torch/utils/data/datapipes/iter/filelister.py,sha256=tD8HIppD_c472KkMfoOBueXVYiUDm4L2rL4icUdAiZ4,2596 +torch/utils/data/datapipes/iter/fileopener.py,sha256=j1FT-pt4EBeULs0prVcfxDJIOkr70OgRkxazCHguIsQ,2823 +torch/utils/data/datapipes/iter/grouping.py,sha256=ocypFkOu3SgSDGiH8jXSpAe6k-SUsHw3sfbVasmUIvI,12354 +torch/utils/data/datapipes/iter/routeddecoder.py,sha256=3gr3bB-3awQE_nu55sKFHmLTQiZXjMSx790SkES4VOE,2731 +torch/utils/data/datapipes/iter/selecting.py,sha256=cXP-13jcpl_Jt2R1GWWrJT4rblrVQRsxxafFjAxEQgA,3313 +torch/utils/data/datapipes/iter/sharding.py,sha256=j8HO5S7hpKnHCJCCgxdww0QKj_bkXQlz7fhSDJG2Jk4,3510 +torch/utils/data/datapipes/iter/streamreader.py,sha256=jGtXojKnbGs7DYI2WprakwFYffs5JTRnRhgYVFzGFJk,1560 +torch/utils/data/datapipes/iter/utils.py,sha256=Wpasg7MfXcCiCB7QnlqPBjNeyMih674liCFoXf0EEcI,1809 +torch/utils/data/datapipes/map/__init__.py,sha256=gLZThKg4SBTjZlzOWyt3zFVZkY04WG6Ia731L9LyRho,667 +torch/utils/data/datapipes/map/callable.py,sha256=o4GBT6ullXxvbHFHLNqiabkbuHLXOVMMTA1kNHB4BLM,1860 +torch/utils/data/datapipes/map/combinatorics.py,sha256=EWOb4kySfvvvmxaZIPuzpM_heihpelpfu7DElLm2Bgk,4191 +torch/utils/data/datapipes/map/combining.py,sha256=5bgulVPrv88K0pm73UT7ZK-b6THA7yF4vYTJiRVPZNI,3699 +torch/utils/data/datapipes/map/grouping.py,sha256=oB7zwvPMLYq6kuHyu2KYt7coE0UYSLLJM6FYCdY03_s,2457 +torch/utils/data/datapipes/map/utils.py,sha256=dDeCKb2Kc7jK8i1PBZgtJQU63coB_BZtUi0HLTmRxJw,1575 +torch/utils/data/datapipes/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/utils/data/datapipes/utils/common.py,sha256=ahu816VrUir2zc-kFnHyy7cI-v6pKTHS6IHhvdmU-Zo,13700 +torch/utils/data/datapipes/utils/decoder.py,sha256=O1e16joBAVCk8Jj973KEz9R21u1F3zaqkqdVOULOBag,11887 +torch/utils/data/datapipes/utils/snapshot.py,sha256=A1pbz3hTPOWq_AbsyKBwBA6-bqleeXLnIbWuqapUcJc,3103 +torch/utils/data/dataset.py,sha256=CMYdVMiJEshQS38UrUeHOEeAKnA0yHTHQDOJn8IbJwY,19467 +torch/utils/data/distributed.py,sha256=7s1ypWMwgnI-nDhfLiIDnKWFOEAZ5LsIXbfMIQpKCeA,6125 +torch/utils/data/graph.py,sha256=pY1gRmU82y2HO9e_aDPpHYo9Lb8AP0t0SLgz09_YdR0,5799 +torch/utils/data/graph_settings.py,sha256=0VYGJhgU6fu0LnqwsHd4tD8nAPVgKI6NvTPw1a1x6MQ,5540 +torch/utils/data/sampler.py,sha256=iDoIY8Rub3-qyCSGP9IdrAFEshFnW-8UDaeb8URTEeg,12836 +torch/utils/deterministic.py,sha256=aDwP89FjYTIT-3g2RtWBoagN59_JbqE2BZbgV4HttMU,611 +torch/utils/dlpack.py,sha256=nroQc8OgKmFORB83GCqeJLeVaWXkNdX04Hneq8gX0iU,4509 +torch/utils/file_baton.py,sha256=SczzEyTXS6TVEb2Ef2uBGUAQOOVE0hVgpQzn2UHSWCM,2040 +torch/utils/flop_counter.py,sha256=iwvM1PaUNQtkw5NH6z4Zqz_TLwVxkpCJ7BhvyPpRlxw,28775 +torch/utils/hipify/__init__.py,sha256=Jzb_RfgvXCrm_SQ4AfeGVi1N36YybxnM5mpyxrnihgI,33 +torch/utils/hipify/constants.py,sha256=sogTIVpPGdJWQA7OvnjfeAgNwbp8BpbWtO12xV-KBFE,1174 +torch/utils/hipify/cuda_to_hip_mappings.py,sha256=8k4CT7yGneLEBJIPTPw3fPk8Vz_h-KGATYHs1llonnE,358399 +torch/utils/hipify/hipify_python.py,sha256=0pZ4VMnqTps5536yuGQ8HH1Lft64NOi6Fujszfs5Apc,47054 +torch/utils/hipify/version.py,sha256=RsZjRjMprNcDm97wqRRSk6rTLgTX8N0GyicZyZ8OsBQ,22 +torch/utils/hooks.py,sha256=_OdFeDFiSd6qepGjas3qTb8Sh5w12zopokJ7VYEj24E,9982 +torch/utils/jit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/utils/jit/log_extract.py,sha256=QRw9IN4zR3CIb130CFBe7G5PEwi3Af4IrC6pTaVoCGE,3751 +torch/utils/mkldnn.py,sha256=Tmjrw5aTght4xAnX__P6LK9_WTE2s54-2MNb40CBUuE,7908 +torch/utils/mobile_optimizer.py,sha256=yrjBnBTjWlcpUlEnk_u9VFBqSlyO01GHu5K1D5dmN7Q,6430 +torch/utils/model_dump/__init__.py,sha256=nLkz0-rkYTn4bnR1wJiJArQUoZq-v6o4LJIB89CJmYg,16782 +torch/utils/model_dump/__main__.py,sha256=jYGPuoI11jzWgGKtK1-E550XLfPkrbHq_BRUbLusK-A,79 +torch/utils/model_dump/code.js,sha256=70w_JAT7N8dkWHrpQsA1enZCJK7VJOIy23ukbbbXQAg,19251 +torch/utils/model_dump/htm.mjs,sha256=m-psDFjVL3_BzcZnYkiRT8AIYhFhCY5ERrh_LP4sEH4,1230 +torch/utils/model_dump/preact.mjs,sha256=005yDhrtmGbeMNCyf7SMhNktZT4VisnDde1R-TMD5gk,10078 +torch/utils/model_dump/skeleton.html,sha256=vq4r1yFKZEchXwQmky1zpj3q65MCPDBD7wDt6SqzJGg,384 +torch/utils/model_zoo.py,sha256=o2NC-XaU8fqDXbUBQDv7thQBld_LOK-Ko63GS4x8Iyg,117 +torch/utils/module_tracker.py,sha256=ISRnQMGGJlLvLLXBm3yoRIJr9sqXDWNyGHQN20kTGs8,5385 +torch/utils/serialization/__init__.py,sha256=urhmq5QqBImvzUxNXw7VkCHllOx41xEyNenyZOWKYQQ,21 +torch/utils/serialization/config.py,sha256=YqqvOY23n8R_UqHaNAA1xxKndeh4FLf7DeqpW1kT0O0,658 +torch/utils/show_pickle.py,sha256=YSnYdo-v9YT2oHkcPrpm1jTCJw6JYhpa31ftNCmKSZc,5391 +torch/utils/tensorboard/__init__.py,sha256=ZYXvCXkbnXG7fJB_0aD2m1tdKIjo39LFzMqN7fku94w,480 +torch/utils/tensorboard/_convert_np.py,sha256=Dd2DxEZ26JjZRac5v_91nbIJoTiK1RmYvznK2tjzklY,734 +torch/utils/tensorboard/_embedding.py,sha256=6ExXXIQiSnA2cIAS9RLxGdKFmuBT01PqdB739x3yT6E,3224 +torch/utils/tensorboard/_onnx_graph.py,sha256=SFq-ITBelVCTHuHCAkAeCyj-l5o-1CIaJQyyFdAUwxI,1887 +torch/utils/tensorboard/_proto_graph.py,sha256=9GOFVhEEovaFOScEzeeXAkCouCZ4B4Blai4M9KCRREA,1758 +torch/utils/tensorboard/_pytorch_graph.py,sha256=eZo_8A1YiKXiz56GcjG8XfroDc1InmvjX7t_DnjqDq4,13692 +torch/utils/tensorboard/_utils.py,sha256=f4JQoI69Z5TKC8wC5Da8dfg2RqArkiF9ojlTjevQIQk,4187 +torch/utils/tensorboard/summary.py,sha256=3V8pmra3rVN8Owqaw00RprpKn6OXEeo2zLZBDIP1bV8,34462 +torch/utils/tensorboard/writer.py,sha256=quqlQZ9cToxF3FS9UlagcJpgD0iyLS4_QQhLjJbYoV4,46659 +torch/utils/throughput_benchmark.py,sha256=f50CNVwm_mvpevFUkpz7dgDdWab519jmZIReB8qhVB0,6502 +torch/utils/viz/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torch/utils/viz/_cycles.py,sha256=SVAVBxQA5beKsKVf7TljuKfasUHP1aEtICXtDV5Ea-A,16740 +torch/utils/weak.py,sha256=ZOTcI0KIGFzmyPJBAs6oIjFh-3ZMO4PdrjA4P0vz9d4,11166 +torch/version.py,sha256=uiPmmeqJMXefFMz8s-1txnoURS1eSevZGHXUGNuip_E,281 +torch/xpu/__init__.py,sha256=ba5iGcoHXyDUB9QPkocSpg8OxMNRW1dW1nAA9m7ri1s,17938 +torch/xpu/_gpu_trace.py,sha256=HPWR1guFWwMGsNMvVAaW5cjPtasOSLqZeOc0eL2Ckkk,2355 +torch/xpu/_utils.py,sha256=JTETOkI3J7tp4SFKV3ukRlvQDO-6Qg850KkJ8wO6gh0,1591 +torch/xpu/memory.py,sha256=I0oFF_CClT9Hgp5DC04wyv09Q8L3BIgGrUiAR6mN6fs,8028 +torch/xpu/random.py,sha256=Gnb8qvbp2aV8uv15vSfiluInrnvenQlB-o_soJHWGjY,5239 +torch/xpu/streams.py,sha256=yU9s4qUZIvdxXQbhW2_Nl3_eqNILBeWquapMezQ8MvI,5858 +torchgen/__init__.py,sha256=iirTpG38WcCsNMhEbi1dg7_jad6ptk_uzZ-BzaGBFyU,348 +torchgen/aoti/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torchgen/aoti/fallback_ops.py,sha256=PsrQT_THJQUM4oBcvk2-P6ICJxaJdpSv0LXboy7heK4,7483 +torchgen/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torchgen/api/autograd.py,sha256=nhFrD22zbt1UdCyuEejF0tTKVU89bSXlnsYQOymYydY,38959 +torchgen/api/cpp.py,sha256=ZnH8_cUb1xko-rfX2U412o3a9LFtgUNDrzLw6r98IBU,16279 +torchgen/api/dispatcher.py,sha256=gPrg9tJ0ob1ErXWoGXJDGZTg0MbDCivTALKE7kjITU4,3479 +torchgen/api/functionalization.py,sha256=HZg1Rgl6Yn22oZ-4-yh2bWye3Ry0xp-LbJ6FmT9iWwI,7566 +torchgen/api/lazy.py,sha256=0Kmm1C4M9a4urfEMPke1oUI41I_yohnXCMRem9zmakc,17053 +torchgen/api/meta.py,sha256=zJYzviYI2gY9V9yPUyiZ_fShcdm_5LbigqGUH_WfaWw,483 +torchgen/api/native.py,sha256=xpye5SVepNMaHSIFzIfcrG-6BkDxCB3fPdQUPcilf5w,5205 +torchgen/api/python.py,sha256=H2bMn2UUx_islGq2fvdFA_ie-zxuIrGzR5aNHJyi38c,59687 +torchgen/api/structured.py,sha256=MuPt1MpFhHtZxXtUY5KaeccGY0Q_T5RHHXAFqgrdQVY,6115 +torchgen/api/translate.py,sha256=KBoD8K6_1e4I1ftVf2hDAoYri4zILxrs-3ZziTqYN2s,19297 +torchgen/api/types/__init__.py,sha256=bQ29sz_GNJGgqoDUsE6i_AcYZq81pOs1ATXQJQhUhLY,144 +torchgen/api/types/signatures.py,sha256=yomA1WZyFB_WKaO9T-8rWSpgvWyvkpJaUJswabddW-c,15722 +torchgen/api/types/types.py,sha256=j_c7wHdhV1x0eTQNFmhx1q13szc6BA1bocTgjM0tTJ4,6139 +torchgen/api/types/types_base.py,sha256=5KzMRZ7idtW8VS7xUrBH24FeNpGQ4Byw7UAo5sjUKH0,7184 +torchgen/api/ufunc.py,sha256=BukIDKwJTKZMoasfvzKLTiP-kGOIoc7ptAiLtrM3egk,6693 +torchgen/api/unboxing.py,sha256=_JIvPzZF-LoRDkenskzx4JVTqiAit5-TTXk0F-pOjaU,9381 +torchgen/code_template.py,sha256=4Yo5Pc1lZapXIPqc1WPfV8tlOuZJxwoLDgFeE1CgaT4,3211 +torchgen/context.py,sha256=po9tdqU2GNwJ5LY0qBhUn19D2yW7ExBOanIZw57C0CI,4056 +torchgen/dest/__init__.py,sha256=qECRwrljRjK-kMdBqfc9X8JPVVPU6XlUuWNEdFD9u0w,805 +torchgen/dest/lazy_ir.py,sha256=1IHFEXWgszQIySWsc__f_zs3VsVXmX96RfnS3VuilGM,28990 +torchgen/dest/lazy_ts_lowering.py,sha256=9QUvL_Z-mGqRfhtK_X2RSEHlGmFOqDr_J8cYJrALUG4,1831 +torchgen/dest/native_functions.py,sha256=I4SK08v3bRwTfFJP7CKtqP3yqmR6Bxt1MqR8g5h_LPg,3171 +torchgen/dest/register_dispatch_key.py,sha256=-rvA6YCoRAprlXo-L9ZxZYLkNCk_kmO709ElL0CAwhE,41484 +torchgen/dest/ufunc.py,sha256=WpaJR5EltYdz21ihQYVqfYq9UFD2J-s9p5HoFKAzSvw,17837 +torchgen/gen.py,sha256=evzw9LnTRLvFf_atB521baSow7_eTdVYFvfHBJPPw1c,113467 +torchgen/gen_aoti_c_shim.py,sha256=s-KBW7PEaFE6l3aGfATTj5a69p7P5zkHA5i1T6BrI-E,25798 +torchgen/gen_backend_stubs.py,sha256=j8Rj42y-fqd0DfTAEM1j_e7SopdGDurCDTOSGO1tGpM,22400 +torchgen/gen_functionalization_type.py,sha256=j7DwK8O1OxSHIYod9Qs7t_wjSpZB_vovzAeNoKSwsa4,38235 +torchgen/gen_lazy_tensor.py,sha256=h6PbUJHwsLyJr5amR7-snYb-qIjgQ5GBvcjzz-TRMa0,22730 +torchgen/gen_schema_utils.py,sha256=xdI5n22JtjBYiURb49NX3-fCv0MveKInWqjAOA8HMUY,3317 +torchgen/gen_vmap_plumbing.py,sha256=YDIYmJ9oFyabMgsfu4k6lDtKf3EpUdNLK7Dkw8tyxng,9395 +torchgen/local.py,sha256=dMVxtnuOWt4kewxkG2G7TN4EjPr37shRFzdtajs3QR8,2167 +torchgen/model.py,sha256=GVVKf0pAdpVBtAT3-x6cs2t_vhJ8wVOmCKx_Ov2x_Ps,114399 +torchgen/native_function_generation.py,sha256=ZiAx2ok6wMnUY08LijwHMiEsj-Qk12ni824x-tt4I5s,29765 +torchgen/operator_versions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torchgen/operator_versions/gen_mobile_upgraders.py,sha256=9nZmtZqsKNqQSpAJ_rQzB49vwNXAHPLoo1_H-FhsHts,12388 +torchgen/operator_versions/gen_mobile_upgraders_constant.py,sha256=C-U6rHQybm_FTcxsz27RMgJDj464NOLhlOzVjSjEn0w,243 +torchgen/packaged/ATen/native/native_functions.yaml,sha256=uj5Y1fZAyzRj0H5DJLOUBlkzT9DnGh6SoYHa9aOq8J8,606478 +torchgen/packaged/ATen/native/tags.yaml,sha256=YcpGyBrM7nGtUZH5Yfv2KruKozA3GmbM6MtaPvy4sH0,5284 +torchgen/packaged/ATen/templates/ATenOpList.cpp,sha256=YobnhIm91ECCc6uYD2uDOrvFM4WqutKQbQ5x_Fh_5IE,1059 +torchgen/packaged/ATen/templates/CompositeViewCopyKernels.cpp,sha256=H64AHoCBB7MJIECAHNzki8NiTVPND0hy2vJ-KqiSF2c,2077 +torchgen/packaged/ATen/templates/DispatchKeyFunction.h,sha256=npUU8WpU76sZv8oqUQqBpcV_QHT6RW9j42EVTSA6pvA,702 +torchgen/packaged/ATen/templates/DispatchKeyFunctions.h,sha256=KlLfLZSYEG_7miq0fD8yuFtgVtm2mO4CQ9WiT6xoEpY,1937 +torchgen/packaged/ATen/templates/DispatchKeyFunctions_inl.h,sha256=nlAU0xWHQqRZn7JNV163YgAv5wwlykwsikMyjzZeZkI,824 +torchgen/packaged/ATen/templates/DispatchKeyNativeFunctions.cpp,sha256=DYjxJmYQ5Yegq1XW4hakqjvZo5skNWi2f5ZNLPUyQ4c,184 +torchgen/packaged/ATen/templates/DispatchKeyNativeFunctions.h,sha256=e8lUNJZ4jt0uMHjio6HOupVpMv83DtAaFBudAG6pEDw,384 +torchgen/packaged/ATen/templates/Function.h,sha256=Z2OimxsHx42itqLZE04H7-RiAPb7UJRzNZQvmVUW6n4,519 +torchgen/packaged/ATen/templates/FunctionalInverses.h,sha256=azlMYM1eJqDFCBeDRBEgpKgbeDIsWLIPEWBVyLVoocY,1231 +torchgen/packaged/ATen/templates/Functions.cpp,sha256=zP3erpXtV2qI6DJuX39obNEs3XffyrODDkCzXDLi8qM,3085 +torchgen/packaged/ATen/templates/Functions.h,sha256=3WE9DmsTPozVLTXJSuqYNO8Ap-p_uub6U4T9dUT1fa4,4677 +torchgen/packaged/ATen/templates/LazyIr.h,sha256=-aVOIdjB719-6SLBsUURho6x0xdKyXsN6h5QP76HTd8,585 +torchgen/packaged/ATen/templates/LazyNonNativeIr.h,sha256=KQbjyZ0Q8qK8JcgqAaF-M-ZhvRE5UhTj8JIHyceNK9Y,178 +torchgen/packaged/ATen/templates/MethodOperators.h,sha256=ifirYleNPll8bjo_OYaC8jEuLWIJlP0Asy0xjtdGfQo,830 +torchgen/packaged/ATen/templates/NativeFunction.h,sha256=C8rQosVZF3aESwI4IvZitd60sdGov6kvClkDAtnx3Oc,366 +torchgen/packaged/ATen/templates/NativeFunctions.h,sha256=N351coNgM9F_jpdma5OK3DvlNUw2oY51iBXKe5dK81Y,1149 +torchgen/packaged/ATen/templates/NativeMetaFunction.h,sha256=C0alnIY6J-5Mlca9_0ocI7bnFKo5jQ_QTuLzo8iCUDQ,452 +torchgen/packaged/ATen/templates/NativeMetaFunctions.h,sha256=mIOwmpkQY9zYolxUXK39c4nRDyG32vBsqEeSDo76p_k,306 +torchgen/packaged/ATen/templates/Operator.h,sha256=5TqYc2DVGzB2gwuIwC8p4D6KnSDIylKXScnjwLfpdu0,448 +torchgen/packaged/ATen/templates/Operators.cpp,sha256=cjfjkIMtfc8n1w0TDy_JJDJq0DK2cT9DfhkZw3YnTWM,347 +torchgen/packaged/ATen/templates/Operators.h,sha256=oU939CI59Drfg2QlKfkCdU6yVQBVi0y9Ia_kQ0rFC5k,3200 +torchgen/packaged/ATen/templates/RedispatchFunctions.cpp,sha256=pNhfp3gMBw4km2c_4EfeF6ge3DGZi8xtGCnkmjzfpi0,307 +torchgen/packaged/ATen/templates/RedispatchFunctions.h,sha256=HFJ8SLBmg2LNRbsp9MdhHBGGztTGhcjzdWCgYPx5f7c,882 +torchgen/packaged/ATen/templates/RegisterBackendSelect.cpp,sha256=Op-RV7_8UnF_dbxO-hZ8X-7DBnFHsP9s11cM0JkJOWY,752 +torchgen/packaged/ATen/templates/RegisterCodegenUnboxedKernels.cpp,sha256=tux2wSt9RalK0o5AfliXJB3N4diTi-88PwNHOobDbmM,1119 +torchgen/packaged/ATen/templates/RegisterDispatchDefinitions.ini,sha256=hnJyQ-BCtiix4FaiwW1l3Zaq95M8C0lFTwbySsiWMDs,455 +torchgen/packaged/ATen/templates/RegisterDispatchKey.cpp,sha256=0Ri1YQipXQ__CsN65PsIdqzWi_svKL8xx-JnVqwHuI8,1575 +torchgen/packaged/ATen/templates/RegisterFunctionalization.cpp,sha256=h0ucuPI5qKCM7i-xPkdJmbA0J_9mwQc7AJqfKdnM8IY,3464 +torchgen/packaged/ATen/templates/RegisterSchema.cpp,sha256=HoLTDNwRhe8xJJucgd6qCkqTglPtxLYCRGCD_4-S0j0,383 +torchgen/packaged/ATen/templates/RegistrationDeclarations.h,sha256=KImic_ILyhxavbGxVna-Ascf--okibalZJlK44a5dic,160 +torchgen/packaged/ATen/templates/TensorBody.h,sha256=WSjZTh8kN3SpKRDTTypreaJFJFxAX5_RQ05jSTFsDuw,29148 +torchgen/packaged/ATen/templates/TensorMethods.cpp,sha256=4C7qcrfgMsziFBzG1-J96WteoZ3xsl4eWaLkc-9P6PQ,2613 +torchgen/packaged/ATen/templates/UfuncCPU.cpp,sha256=LrnISndBkXtdugvOWeRk9ZGYQlztFI5yqytoaZiKOQk,445 +torchgen/packaged/ATen/templates/UfuncCPUKernel.cpp,sha256=paz66F7U6E9e2X-rpbxlVDGcxevcXIOialaEqAaoArc,350 +torchgen/packaged/ATen/templates/UfuncCUDA.cu,sha256=HOBz8yO4QFxxmX_6gCF7L8MJvrGPwGQjoE-qDf8kF9Y,494 +torchgen/packaged/ATen/templates/UnboxingFunctions.cpp,sha256=wwdlYUaaCjXwhlaoqieeO-3fOqoQSBj62j6Is4n-UKY,709 +torchgen/packaged/ATen/templates/UnboxingFunctions.h,sha256=bcs4ET0LLtzs7nSSWhKA8jJzongib5CGlNA5yaExfKw,1026 +torchgen/packaged/ATen/templates/aten_interned_strings.h,sha256=_FM2jXAhATj9GZ66dXXUPx72q2uYnzT8iN4hkTz0rmI,805 +torchgen/packaged/ATen/templates/enum_tag.h,sha256=w3hCov4CToJ5qyHrnadei9907AIZkDLALSOlOZ1gP2Q,179 +torchgen/packaged/autograd/BUILD.bazel,sha256=Jd76gG6LQlmmEKK9IYlDbkVSmc8L5bTvGjG159L3rJA,104 +torchgen/packaged/autograd/README.md,sha256=hGiUzBaCs0wzBhEXB3vkWUXU4Lima6y5_wPKjAoKQ-Q,147 +torchgen/packaged/autograd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torchgen/packaged/autograd/build.bzl,sha256=P4Ox76V35gWtKl8p08d-av4YEb10BC5Lcr5qHiqK7uE,348 +torchgen/packaged/autograd/context.py,sha256=dCc7BQeTGaYqoeVRsU1pmcpSn0SBTZzL_McNXWEtE3A,945 +torchgen/packaged/autograd/deprecated.yaml,sha256=UbtajzWo89jf1Q5sw8JNJIZT5s-iDBagnrGJthtDS2Q,6250 +torchgen/packaged/autograd/derivatives.yaml,sha256=xrdpD1t6W4dtuVjUDVkngxlX3SttS4MOrVZQiXe5qY8,181563 +torchgen/packaged/autograd/gen_annotated_fn_args.py,sha256=7SpCWSDgEj5pBYtWqsPAseC6Cfvyi_hUt-ST7BSF9hY,4476 +torchgen/packaged/autograd/gen_autograd.py,sha256=p6C9Pjmm1DyvB5QnFPqLs4EnWSR82cSpX975BipgJT8,4617 +torchgen/packaged/autograd/gen_autograd_functions.py,sha256=YX3Bo6zoYW-FEGMNlqFcj_c_xFwoDDPtRtdMYjHbp9A,38108 +torchgen/packaged/autograd/gen_inplace_or_view_type.py,sha256=3RBWDOThyx13Dwt6SKlcMag2M-uCqI4LrphOWyCAiOs,22721 +torchgen/packaged/autograd/gen_python_functions.py,sha256=fK0Rbcr2XQ8TVg9dFZm2CHNSazHmvXGB01GkGXZZ89A,46359 +torchgen/packaged/autograd/gen_trace_type.py,sha256=1CDmaBhzHFmhPdkYKZ8uShp1PWPETVl8WOTvi9CTeR0,18974 +torchgen/packaged/autograd/gen_variable_factories.py,sha256=a0Q5N1zGSVxfES_cnq88tjwOB9P79piRqchTrhWCNPk,4479 +torchgen/packaged/autograd/gen_variable_type.py,sha256=7pH0VeU_My-VXLb5lkr1sYk1LywLU1INo813Rc3SoBk,83821 +torchgen/packaged/autograd/gen_view_funcs.py,sha256=vSdnTGGFcRqc3z1spnST9wsFylihaeTFy7jjtQoj7Hc,11589 +torchgen/packaged/autograd/load_derivatives.py,sha256=qakpxyLD_yMwLl0f5XL0S-jpaPiV3NenePUninTHimo,40338 +torchgen/packaged/autograd/templates/ADInplaceOrViewType.cpp,sha256=6juXEdMJaxhS1nv6bqUmx-cJLWPyc4Yb3teAeTyEBG4,790 +torchgen/packaged/autograd/templates/Functions.cpp,sha256=j8Ga18okz6DI1ulIL5BErJIskHKQZPhuuNlj5RcgUuE,1478 +torchgen/packaged/autograd/templates/Functions.h,sha256=9vjETYlNoQKCJVRNBmIvBoj6T2GA9-c16T5FPVmWpa0,1577 +torchgen/packaged/autograd/templates/TraceType.cpp,sha256=aqACTyrT05ElIiBLJYkfSgxNjwhum6_QUPDAtXwoKqo,695 +torchgen/packaged/autograd/templates/VariableType.cpp,sha256=Hq7yishySi1MFscqstv4xIG89pvotA0NEQTnJPJjz3c,1852 +torchgen/packaged/autograd/templates/VariableType.h,sha256=DVuAf8_kgQCcnZsVBFxSwwJTNZbFo01C5dVFhcROmq4,1467 +torchgen/packaged/autograd/templates/ViewFuncs.cpp,sha256=oas1Pw6wWyAfRy2uh5K7fGD3qR60LD2Oiv-B5OW6Tvc,269 +torchgen/packaged/autograd/templates/ViewFuncs.h,sha256=7RoIEE9NQ6jWdg3SfdbRsQxACKdscsrnZN45JTJER5I,498 +torchgen/packaged/autograd/templates/annotated_fn_args.py.in,sha256=gRgF9BZmylhyfXrVSAVjg9Y4TUYpq1FgYVd3_hk_9no,199 +torchgen/packaged/autograd/templates/python_enum_tag.cpp,sha256=2cTLq6vaU-qjAXLrChZAnCmpVqA4oDvZLOsBY3GWmDA,495 +torchgen/packaged/autograd/templates/python_fft_functions.cpp,sha256=g8Ub5jh_YhgeourCXjB2zlH15Lm_oGwLa37IjibMoEc,1951 +torchgen/packaged/autograd/templates/python_functions.cpp,sha256=b20LJcBbbQHQ4hTt-2h__DIuk8uHksD4ZVD5csfM43Q,1121 +torchgen/packaged/autograd/templates/python_functions.h,sha256=WX52FzntisoprdObQvGXrzMhehgWZ2rIfnHzgNpX-5U,345 +torchgen/packaged/autograd/templates/python_linalg_functions.cpp,sha256=3NyK1tMOYwAsnxyb1k04RPiN9MQkriblY3GnPVOrfSY,1614 +torchgen/packaged/autograd/templates/python_nested_functions.cpp,sha256=2wivRfIBY8pkU9mqHVQfqnsIff6K1Al-BTJyRFuUeAw,2029 +torchgen/packaged/autograd/templates/python_nn_functions.cpp,sha256=8AyIozHVM21KEaVuOPgT-zPfPGj6hpNMC5SiwqUixW0,3501 +torchgen/packaged/autograd/templates/python_return_types.cpp,sha256=GB75OiT-5X3rmgXZlc30MB67qMlvqkzhrDIfIj1mZp4,1219 +torchgen/packaged/autograd/templates/python_return_types.h,sha256=ZDLPH-bxSjCpeyWLhU6kZsuAxa2pnCgTE57YpxXmDmQ,198 +torchgen/packaged/autograd/templates/python_sparse_functions.cpp,sha256=NW_L2mF7ZrR2FvXtDE7iNfw0BM-wTyUzBa_C1B5RZ7A,1551 +torchgen/packaged/autograd/templates/python_special_functions.cpp,sha256=JqcAExUPCnbzUujY2PIDL5Gap-ZKlltKiZmDTleO_8s,1972 +torchgen/packaged/autograd/templates/python_torch_functions.cpp,sha256=QEgUPJbi5TunkkQog-o6zoIjpLFZ6VFHQYxKe7BUAiw,2601 +torchgen/packaged/autograd/templates/python_variable_methods.cpp,sha256=XjBQdyQ2rt1ymgKOdiyBd2YqDqxAFufncXMekvy2pDs,53968 +torchgen/packaged/autograd/templates/variable_factories.h,sha256=g-WOn2XuHBsLNGUk-emw0-KcrNsosDjqAu4NjPJCFA4,5637 +torchgen/selective_build/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torchgen/selective_build/operator.py,sha256=21lWnZeGQ9LiBusCMMFGr8rUosEiDxRwhI05taXViio,6509 +torchgen/selective_build/selector.py,sha256=fZckUgNq4stv6nGT74vG483RlQ8hQGv5cdHq5XejyZU,12666 +torchgen/static_runtime/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torchgen/static_runtime/config.py,sha256=JWrHLk7Men1T9lAINaTOnIeQOCorU5cfY-hXYYy_2J4,14487 +torchgen/static_runtime/gen_static_runtime_ops.py,sha256=TptS5k4o0sOYyCrYH1ePMiDfqyWJFbWVQC54zNkHmUA,7408 +torchgen/static_runtime/generator.py,sha256=SCAzUYjGAJlHFjDDuo5jkbyyjhG8KtdVsLfW69WCT8s,27116 +torchgen/utils.py,sha256=7yUF0rM0olnnLRn4Ae6K7PJiRbkAUrM9xYiKMKxcWF4,18584 +torchgen/yaml_utils.py,sha256=kw3UXTMgpTgOG8SK1pxEOlAz1ASzlvMRFf6Rt-vKJVI,1080 diff --git a/.venv/lib/python3.12/site-packages/torch-2.8.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/torch-2.8.0.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/torch-2.8.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/torch-2.8.0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..a903c688388346961f2b11b235d5775ef8e0977f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/torch-2.8.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (79.0.1) +Root-Is-Purelib: false +Tag: cp312-cp312-manylinux_2_28_x86_64 + diff --git a/.venv/lib/python3.12/site-packages/torch-2.8.0.dist-info/entry_points.txt b/.venv/lib/python3.12/site-packages/torch-2.8.0.dist-info/entry_points.txt new file mode 100644 index 0000000000000000000000000000000000000000..43e5bc29823736ad435669527c514ef7980aaf7c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/torch-2.8.0.dist-info/entry_points.txt @@ -0,0 +1,6 @@ +[console_scripts] +torchfrtrace = tools.flight_recorder.fr_trace:main +torchrun = torch.distributed.run:main + +[torchrun.logs_specs] +default = torch.distributed.elastic.multiprocessing:DefaultLogsSpecs diff --git a/.venv/lib/python3.12/site-packages/torch-2.8.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/torch-2.8.0.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..90d81bec1d35eb3996334268974885a5398b6c6c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/torch-2.8.0.dist-info/top_level.txt @@ -0,0 +1,3 @@ +functorch +torch +torchgen diff --git a/.venv/lib/python3.12/site-packages/torchaudio-2.8.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/torchaudio-2.8.0.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/torchaudio-2.8.0.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/torchaudio-2.8.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/torchaudio-2.8.0.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..18e1712428e86ec941807570b91338af23bb88dd --- /dev/null +++ b/.venv/lib/python3.12/site-packages/torchaudio-2.8.0.dist-info/METADATA @@ -0,0 +1,136 @@ +Metadata-Version: 2.4 +Name: torchaudio +Version: 2.8.0 +Summary: An audio package for PyTorch +Home-page: https://github.com/pytorch/audio +Author: Soumith Chintala, David Pollack, Sean Naren, Peter Goldsborough, Moto Hira, Caroline Chen, Jeff Hwang, Zhaoheng Ni, Xiaohui Zhang +Author-email: soumith@pytorch.org +Maintainer: Moto Hira, Caroline Chen, Jeff Hwang, Zhaoheng Ni, Xiaohui Zhang +Maintainer-email: moto@meta.com +Classifier: Environment :: Plugins +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Science/Research +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX +Classifier: Programming Language :: C++ +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Topic :: Multimedia :: Sound/Audio +Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: torch==2.8.0 +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: description-content-type +Dynamic: home-page +Dynamic: license-file +Dynamic: maintainer +Dynamic: maintainer-email +Dynamic: requires-dist +Dynamic: summary + +torchaudio: an audio library for PyTorch +======================================== + +[![Documentation](https://img.shields.io/badge/dynamic/json.svg?label=docs&url=https%3A%2F%2Fpypi.org%2Fpypi%2Ftorchaudio%2Fjson&query=%24.info.version&colorB=brightgreen&prefix=v)](https://pytorch.org/audio/main/) +[![Anaconda Badge](https://anaconda.org/pytorch/torchaudio/badges/downloads.svg)](https://anaconda.org/pytorch/torchaudio) +[![Anaconda-Server Badge](https://anaconda.org/pytorch/torchaudio/badges/platforms.svg)](https://anaconda.org/pytorch/torchaudio) + +![TorchAudio Logo](docs/source/_static/img/logo.png) + +> [!NOTE] +> **We are in the process of refactoring TorchAudio and transitioning it into a +> maintenance phase. This process will include removing some user-facing +> features: those features are deprecated from TorchAudio 2.8 and will be removed in 2.9. +> Our main goals are to reduce redundancies with the rest of the +> PyTorch ecosystem, make it easier to maintain, and create a version of +> TorchAudio that is more tightly scoped to its strengths: processing audio +> data for ML. Please see +> [our community message](https://github.com/pytorch/audio/issues/3902) +> for more details.** + +The aim of torchaudio is to apply [PyTorch](https://github.com/pytorch/pytorch) to +the audio domain. By supporting PyTorch, torchaudio follows the same philosophy +of providing strong GPU acceleration, having a focus on trainable features through +the autograd system, and having consistent style (tensor names and dimension names). +Therefore, it is primarily a machine learning library and not a general signal +processing library. The benefits of PyTorch can be seen in torchaudio through +having all the computations be through PyTorch operations which makes it easy +to use and feel like a natural extension. + +- [Support audio I/O (Load files, Save files)](http://pytorch.org/audio/main/) + - Load a variety of audio formats, such as `wav`, `mp3`, `ogg`, `flac`, `opus`, `sphere`, into a torch Tensor using SoX + - [Kaldi (ark/scp)](http://pytorch.org/audio/main/kaldi_io.html) +- [Dataloaders for common audio datasets](http://pytorch.org/audio/main/datasets.html) +- Audio and speech processing functions + - [forced_align](https://pytorch.org/audio/main/generated/torchaudio.functional.forced_align.html) +- Common audio transforms + - [Spectrogram, AmplitudeToDB, MelScale, MelSpectrogram, MFCC, MuLawEncoding, MuLawDecoding, Resample](http://pytorch.org/audio/main/transforms.html) +- Compliance interfaces: Run code using PyTorch that align with other libraries + - [Kaldi: spectrogram, fbank, mfcc](https://pytorch.org/audio/main/compliance.kaldi.html) + +Installation +------------ + +Please refer to https://pytorch.org/audio/main/installation.html for installation and build process of TorchAudio. + + +API Reference +------------- + +API Reference is located here: http://pytorch.org/audio/main/ + +Contributing Guidelines +----------------------- + +Please refer to [CONTRIBUTING.md](./CONTRIBUTING.md) + +Citation +-------- + +If you find this package useful, please cite as: + +```bibtex +@article{yang2021torchaudio, + title={TorchAudio: Building Blocks for Audio and Speech Processing}, + author={Yao-Yuan Yang and Moto Hira and Zhaoheng Ni and Anjali Chourdia and Artyom Astafurov and Caroline Chen and Ching-Feng Yeh and Christian Puhrsch and David Pollack and Dmitriy Genzel and Donny Greenberg and Edward Z. Yang and Jason Lian and Jay Mahadeokar and Jeff Hwang and Ji Chen and Peter Goldsborough and Prabhat Roy and Sean Narenthiran and Shinji Watanabe and Soumith Chintala and Vincent Quenneville-Bélair and Yangyang Shi}, + journal={arXiv preprint arXiv:2110.15018}, + year={2021} +} +``` + +```bibtex +@misc{hwang2023torchaudio, + title={TorchAudio 2.1: Advancing speech recognition, self-supervised learning, and audio processing components for PyTorch}, + author={Jeff Hwang and Moto Hira and Caroline Chen and Xiaohui Zhang and Zhaoheng Ni and Guangzhi Sun and Pingchuan Ma and Ruizhe Huang and Vineel Pratap and Yuekai Zhang and Anurag Kumar and Chin-Yun Yu and Chuang Zhu and Chunxi Liu and Jacob Kahn and Mirco Ravanelli and Peng Sun and Shinji Watanabe and Yangyang Shi and Yumeng Tao and Robin Scheibler and Samuele Cornell and Sean Kim and Stavros Petridis}, + year={2023}, + eprint={2310.17864}, + archivePrefix={arXiv}, + primaryClass={eess.AS} +} +``` + +Disclaimer on Datasets +---------------------- + +This is a utility library that downloads and prepares public datasets. We do not host or distribute these datasets, vouch for their quality or fairness, or claim that you have license to use the dataset. It is your responsibility to determine whether you have permission to use the dataset under the dataset's license. + +If you're a dataset owner and wish to update any part of it (description, citation, etc.), or do not want your dataset to be included in this library, please get in touch through a GitHub issue. Thanks for your contribution to the ML community! + +Pre-trained Model License +------------------------- + +The pre-trained models provided in this library may have their own licenses or terms and conditions derived from the dataset used for training. It is your responsibility to determine whether you have permission to use the models for your use case. + +For instance, SquimSubjective model is released under the Creative Commons Attribution Non Commercial 4.0 International (CC-BY-NC 4.0) license. See [the link](https://zenodo.org/record/4660670#.ZBtWPOxuerN) for additional details. + +Other pre-trained models that have different license are noted in documentation. Please checkout the [documentation page](https://pytorch.org/audio/main/). diff --git a/.venv/lib/python3.12/site-packages/torchaudio-2.8.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/torchaudio-2.8.0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..931104461ac8f055ef07204182daf22592daf5bf --- /dev/null +++ b/.venv/lib/python3.12/site-packages/torchaudio-2.8.0.dist-info/RECORD @@ -0,0 +1,151 @@ +torchaudio-2.8.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +torchaudio-2.8.0.dist-info/METADATA,sha256=OckS0WMRSG0XrYz9w857UycDcGxBCxQKdMlRH6hof-8,7236 +torchaudio-2.8.0.dist-info/RECORD,, +torchaudio-2.8.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torchaudio-2.8.0.dist-info/WHEEL,sha256=veFjf4mxcxOj9ePcrtqmEOyNCkQXlnxoQX8NUvUNV2o,113 +torchaudio-2.8.0.dist-info/licenses/LICENSE,sha256=k6WIYahYzBCOa2uDPgjnbosqZjOeSoAHyKWowf-cQNY,1338 +torchaudio-2.8.0.dist-info/top_level.txt,sha256=GT0MktEbHKoLnvd-6ii7_dhJVvshupOujk840BcHU4U,17 +torchaudio/__init__.py,sha256=cLZT12ZxOTKUPx0KIrpBdeHTgvTkVCAL1j-E63Wauoo,1482 +torchaudio/_backend/__init__.py,sha256=6zMYGajHaaCXUE_U7HuGLp0fqcviYAjBZdFDI4E7C-0,1631 +torchaudio/_backend/backend.py,sha256=hSrfZcj5FMzx5ZpwubN-LLMvBFb7ENyw7HvT_6pVYVU,1565 +torchaudio/_backend/common.py,sha256=55Y0r0MsdW6gvTOT_Zy60UGFXc60DfdJ7uvycJKK3is,1783 +torchaudio/_backend/ffmpeg.py,sha256=oL_whDjkPtHzo6HJLiEPlGHdrOqzjlu81g-vlaNkRBA,11294 +torchaudio/_backend/soundfile.py,sha256=n0Epw0J9rBb89xVJWTXaDfb96YFz0-i2xarXIdDd-Cw,1703 +torchaudio/_backend/soundfile_backend.py,sha256=qJHEEXU1egCkPJ2Y9uJWFvVhW3AqDZ7z7P7mkJjJJWM,17376 +torchaudio/_backend/sox.py,sha256=JS9DQ9Mb124g-3UKcwagbzlxOmT1kXoQfp5wocB4Up8,3370 +torchaudio/_backend/utils.py,sha256=lnQajYPh1KSYtatbsIHWkO757c5i2yp5IXoYfem0yrg,15377 +torchaudio/_extension/__init__.py,sha256=lQPB8K7VSxWmtTEiMmF-u7WVq1O10_t5nEghkjCf4Ks,2202 +torchaudio/_extension/utils.py,sha256=4FTD6xwcSLqVJ3Kmpx5cvJp1oAUKmWwRjwuxpcbrmzw,6258 +torchaudio/_internal/__init__.py,sha256=gjU8g9HhVd9hHrHXJM0xOlZL6cT8ktO60MN8RHI6ZbA,241 +torchaudio/_internal/module_utils.py,sha256=FgGycq1NuYIEMqMJtpMnpkNPakfRtr_IbH-i3z7LHtU,5249 +torchaudio/_torchcodec.py,sha256=sIvYjJWEI5J4FoKg5kGdz97Fb5zbZWBuricgccCyZmg,13786 +torchaudio/backend/__init__.py,sha256=AL8njOL5hDhIGq5tjRxfFzZXxQdTGlz5gs9g4RToblY,281 +torchaudio/backend/_no_backend.py,sha256=9Ss3b4BMFao5Kfdqh6S8JSLUoYCodbPgNQCiDHbNhDQ,757 +torchaudio/backend/_sox_io_backend.py,sha256=PnH-ClsiOy0ekOTY1RKB-cL6xrTPtrzmuXGX3ugATps,11456 +torchaudio/backend/common.py,sha256=T_iYc4u_EzfIh7zbG_xW052fyJMXUXEpPfDOaAQ6sAY,443 +torchaudio/backend/no_backend.py,sha256=96r0ThXQzM9nD5s3gYOGrIIWTdIcFNM23IUKCXxKQEM,469 +torchaudio/backend/soundfile_backend.py,sha256=jZePQm-HiDbiR_YpYXHR5feAziW5kLpSA70m4mG4oqw,499 +torchaudio/backend/sox_io_backend.py,sha256=iFKlWKNRNo39mUriDAt3XVbxKJYB8T_xTNRDMJXRdSQ,477 +torchaudio/compliance/__init__.py,sha256=hhNObUS0c-fS-VMudM7zl3-CvupvCDmESlikntSMn5g,48 +torchaudio/compliance/kaldi.py,sha256=XL6hpYTd6nSPb2imIdeU4TM06I2fqh1AmG968y8ZbSk,36666 +torchaudio/datasets/__init__.py,sha256=taRr3duDaEK1Pfzj9N1dFuZpXfy8e4uFItcJiRLAQwQ,1171 +torchaudio/datasets/cmuarctic.py,sha256=KAhTHUJ3g5RSlmsU5mCTcvutOCm3Oqcd3643u3HNqIg,7097 +torchaudio/datasets/cmudict.py,sha256=9OEpNDYpyqeEyinAnyGIU8FampDj7ziSOHRwJLIlq2M,5990 +torchaudio/datasets/commonvoice.py,sha256=9khedUCmdEkCKPU6_r8VWz6I2VdJokatuziZ6BxJMZs,2763 +torchaudio/datasets/dr_vctk.py,sha256=Km4-tKllAgnOKCuq66YRWhTlNWmC7D0Xz3dAttRRGSo,4377 +torchaudio/datasets/fluentcommands.py,sha256=u3tkO4-AAaTWdbRQi6lIvad4x2plZgXM39KljGtmRsw,3245 +torchaudio/datasets/gtzan.py,sha256=I5dRP_QGuQ1joXWRwZwtvpwi22uZTb8QZm9Mr2W55Mg,24357 +torchaudio/datasets/iemocap.py,sha256=X_WCoXOzRqcWRRRoUtY0AlD9SJcUUOACIcgbV0irt48,4930 +torchaudio/datasets/librilight_limited.py,sha256=fAwpX0hEMze5aV57BP7rjBLwRiZa3Aje_NXi_3o16wA,4179 +torchaudio/datasets/librimix.py,sha256=VtKOhf6VJc1ysWCvUvh0SbtjOkXJChmBM_BhoSkg_2A,5116 +torchaudio/datasets/librispeech.py,sha256=zkzJFWchWs4AktYAI-ghmWH4ZeJ84C0uDo9E1_pTgSI,6308 +torchaudio/datasets/librispeech_biasing.py,sha256=d-02tyrXI-CSGbXBFYFcnM_yT8WSGABHfpNiFxyadL0,6958 +torchaudio/datasets/libritts.py,sha256=EtWOoCDz7_qGLZF5YcZfnHaLxH4Y8QJCnopafLiqFno,5870 +torchaudio/datasets/ljspeech.py,sha256=92NeLQsC1iKpqfiMkKKbcJDpaYdZKVdVEBQJze1wmxY,3494 +torchaudio/datasets/musdb_hq.py,sha256=TYKjpat6JKr9bkFqUecu7_hRdshRfQP2UbknaYR3Q0U,5075 +torchaudio/datasets/quesst14.py,sha256=QyGd4fMS820ATbP8YgBtu7bSSK09pw5RZklsPJ8Jf0Y,4455 +torchaudio/datasets/snips.py,sha256=WaYUknGFM3rnLklOj5ZYHSX5mhlf_Ce4p3LBZdA9yJc,5008 +torchaudio/datasets/speechcommands.py,sha256=cLSgiVYlQjEOuYPpFeAtcXSGirraH4IMoP8p9WIvUoY,7481 +torchaudio/datasets/tedlium.py,sha256=a8Hf2QvOki7_chgXcMAFMk-piTjodktfnc3HRbUVJkU,8698 +torchaudio/datasets/utils.py,sha256=QaI02lOcesy6Dnvlof4BeTDIbiOqUcoVEPxL5_T8vwU,1689 +torchaudio/datasets/vctk.py,sha256=twR_n8LyQcT8A_HrJoMx3RkaVrRXXZAnIVU1d0E0npQ,5699 +torchaudio/datasets/voxceleb1.py,sha256=9vU0ftB4-2usO8ZiEUKR_IQTEdHhA0M8l9scXCNehnw,11725 +torchaudio/datasets/yesno.py,sha256=4sgfMeSxz8HaRDk6A2UIFP-20q29MwEO_r8DoEtfbvE,3026 +torchaudio/functional/__init__.py,sha256=Yot97UqucROk3WJJt71HrA5BNh12lWUYr1U16U1KqCo,2487 +torchaudio/functional/_alignment.py,sha256=wmDeohWvuoYORYDeIRxnYUhUqv1uCUkaCZYLEK_ryUg,4695 +torchaudio/functional/filtering.py,sha256=oQU0dKo-ExoLoGWcoV9kZ6Seb4DD8OkEH1RpskoX7QI,61642 +torchaudio/functional/functional.py,sha256=3I_YNOHd-FK_STStq7qIsVJPvUwGkNN6oQyiTLGJe_s,96181 +torchaudio/io/__init__.py,sha256=6P7Qz8_Bc8reLDqW1QZUZkZACVXR2SS4ZxR51rYY6wo,733 +torchaudio/io/_effector.py,sha256=APDrIU2biwFsSVmhrXjelmc4ndcmb0JL-H189Zp689g,11870 +torchaudio/io/_playback.py,sha256=70IxGrGPkI1h4rz8_04SFCGsbbGZkTiUdRhbPOMLLgQ,2321 +torchaudio/kaldi_io.py,sha256=7louKyGjWYfwBWod2mGorCd4YaNa5TFEtQPabLyjQAw,5226 +torchaudio/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torchaudio/lib/_torchaudio.so,sha256=9hqI8-VfdtlQt4JVAYSMFl_N8POlr_7n679qEg41koE,150520 +torchaudio/lib/_torchaudio_sox.so,sha256=LSL5QG_UVFDyAD4ypmeI7lkgDhvgdVUy4QBO2mPtUsA,282632 +torchaudio/lib/libctc_prefix_decoder.so,sha256=tECbRfNL4sEOkJhy2lqyqN_Up04gh3ZOHi0yxE49E3I,7132944 +torchaudio/lib/libtorchaudio.so,sha256=_JZJ59mhutUng_xUN1GJkGz-92lTwJ5i68sVRX85CH8,3355816 +torchaudio/lib/libtorchaudio_sox.so,sha256=iZ21O0ExSYGhLjqK9R1nciaG6S-L0TtnXwH8iQwcjZU,148456 +torchaudio/lib/pybind11_prefixctc.so,sha256=IKv_bxLP1QTAGq6BIXTIVko8zrEWh-FrrHg8DC-Oqtk,256888 +torchaudio/models/__init__.py,sha256=BNMNGuwpJAFRsdtwHYQ6slGClkrUTu31_7mXh7FjeV4,1995 +torchaudio/models/_hdemucs.py,sha256=VPnQ73lA9lfAxRjZ85NCGJYP36mPNwTjS-TU4qelu_k,38242 +torchaudio/models/conformer.py,sha256=5IceU-jcZKofkHTTqRKoytubQ75MzZPrPlfkLsIlxeA,10068 +torchaudio/models/conv_tasnet.py,sha256=v-DI_Ej9FCBBbSH-Spkh3tzq8rkBhbQNA-Wp52Uf32E,12540 +torchaudio/models/decoder/__init__.py,sha256=x3M4zEH-Dc-3yI4yKTF1bdjDQj8GlNc9p_lHaqhI0mA,1466 +torchaudio/models/decoder/_ctc_decoder.py,sha256=K3gSsG9htU08fe7tKSuIJPDIs7ruY50pJ3eNdNhXSVY,20082 +torchaudio/models/decoder/_cuda_ctc_decoder.py,sha256=rtpN1Z_Xni1LlHgHx6jJ1Jap4TnQ0rRRMvwGWa-xnvA,7186 +torchaudio/models/deepspeech.py,sha256=kQW3B6YcjYuq7xRzWjRJFGr7ZNraY9gMYDTxII7Cgtg,2746 +torchaudio/models/emformer.py,sha256=ncDeEcYegUmIKQoDBoufUhVWj4dYpZAXxLX0qmEqt1A,37766 +torchaudio/models/rnnt.py,sha256=jz66nwDd1qGT6KQR1lbA_urPktygewhm0FH66T7P3Ek,35541 +torchaudio/models/rnnt_decoder.py,sha256=IwlDsuw1SA-uCRrXGMBqm05auGFSha2bZ-8BOImnK0c,12839 +torchaudio/models/squim/__init__.py,sha256=b98nAaL28Q4w3lrqd_6wUd0An-xNhhJn4Tj8oZlzQnc,346 +torchaudio/models/squim/objective.py,sha256=YPkEWdDMyeP7GcR0OzUPHr2wKhIKFMjy4peYsABmZQk,12289 +torchaudio/models/squim/subjective.py,sha256=N00kILSPm0akWyNsrNYKmHgZmooo8gbyUm5IVLf7bx8,5797 +torchaudio/models/tacotron2.py,sha256=FimYhGSI8FKwWb87CLk4h3yKWatCU2HvFmU1t5WUn4E,45914 +torchaudio/models/wav2letter.py,sha256=KNcq4p0qZG2Bwfdakv7YwLCvi_yGT-qB4fJwGMuFQhg,3278 +torchaudio/models/wav2vec2/__init__.py,sha256=WlafukV6GwuSNh0CZifrYUt4V5l59kjvGX7AZNonjfk,927 +torchaudio/models/wav2vec2/components.py,sha256=DRmW-GHYf-JReCg_0l1ovNWJBnAavePO3S2vPY-1ze4,47077 +torchaudio/models/wav2vec2/model.py,sha256=Z2VN6KbDOOdq5JtP7lxPQebwYqsxKms1Eu4IjDJtZaQ,60092 +torchaudio/models/wav2vec2/utils/__init__.py,sha256=qmMbz4HAN5kEEyl4cSGm_JQZI47beyh4witydPC_qns,181 +torchaudio/models/wav2vec2/utils/import_fairseq.py,sha256=oCwG6qpG0bCXue2V56fjDcC8cA2rgy4b3O_nu_FI9ZY,9198 +torchaudio/models/wav2vec2/utils/import_huggingface.py,sha256=1nVCipp-lOUAyl_-P103DWLUeTOZi9X_ffX93bOXxEk,5946 +torchaudio/models/wav2vec2/wavlm_attention.py,sha256=1DU_pkoLCeHQwSF4lJ06cez0PsMVoXNxiYKP0Yv0qFQ,10844 +torchaudio/models/wavernn.py,sha256=5xUyao5g69jRXX4ReNi4mP_aTSIonJPP6XcPrqKybEk,15446 +torchaudio/pipelines/__init__.py,sha256=Xy8NmInKwTcNBHwLTTjHjrfczRLuQq8a67ENt1OTVXM,2745 +torchaudio/pipelines/_source_separation_pipeline.py,sha256=WxngB1d13H5IVqs4RPqSL53ZGYsJ3tnfCpxgc5FNSOM,4224 +torchaudio/pipelines/_squim_pipeline.py,sha256=eCimTeoqNX8LilIQNGmb8UaRtnLIXa4LNShXFjodcZM,6280 +torchaudio/pipelines/_tts/__init__.py,sha256=PP7l8XzVURqelwuMJFgfOCv4fvzZunDiy90ZQlRkv7g,426 +torchaudio/pipelines/_tts/impl.py,sha256=Tig4_5sITJADwxN5eZGek7Ath_-e3sV8CTM5t6UpeUU,15374 +torchaudio/pipelines/_tts/interface.py,sha256=yUaS0UK3PTRruYXRWFil7lAhr-1iYiyBaDBLmEnJPUQ,10224 +torchaudio/pipelines/_tts/utils.py,sha256=2-P3FdKzB4W6lP3-NnbJsw2QvYjGDizK7XMwBzCLqXg,4810 +torchaudio/pipelines/_wav2vec2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torchaudio/pipelines/_wav2vec2/aligner.py,sha256=pIWRgQ-kdYUxtL8bdc0qk9wBjwRrHY1uSWL3L4e2vxs,2709 +torchaudio/pipelines/_wav2vec2/impl.py,sha256=zdXFjytJO5MvnB-3aygzUUFKxCTkQGU_OX_rhUh9c0k,65561 +torchaudio/pipelines/_wav2vec2/utils.py,sha256=Q8_fWOR2JDnHu0TTRmHzRjI3BOJa0hGIAl0cjtALgsQ,6971 +torchaudio/pipelines/rnnt_pipeline.py,sha256=Qy37z7v6d1jLOHd67zbRu21dgL6Fml1rTd7j4Jl1NsM,13749 +torchaudio/prototype/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torchaudio/prototype/datasets/__init__.py,sha256=GSMcp2CykcBc-krhlHTrPm5DCvDFwnA7_6GFNCGwsaQ,47 +torchaudio/prototype/datasets/musan.py,sha256=Q8O3RjR6NN2B2D2hAu6-2g92Yyd_QCGruD7cUKp31AU,2206 +torchaudio/prototype/functional/__init__.py,sha256=GlbhnDHcNyUWdRd3R-ATzRkG2FXsbqjL56OptyTXpec,562 +torchaudio/prototype/functional/_dsp.py,sha256=ZbsiuTgq48iRWJWjTDC4RWobVX2x4aUoxTr3iSR2MNI,16827 +torchaudio/prototype/functional/_rir.py,sha256=09j38Lh-dHd4z9MvxFy5eDKTf5G-mUYyWerBpuNotbo,17354 +torchaudio/prototype/functional/functional.py,sha256=EPynehFuKopP-PE5O-C3svkAcB3iOALiUFiQTBXzdRM,6563 +torchaudio/prototype/models/__init__.py,sha256=LWGyzWaA-ZQPV0gOXxlQ8DikxAbhRof5hxAQNQKp4io,1407 +torchaudio/prototype/models/_conformer_wav2vec2.py,sha256=GzpTaW9suVHy7B1SogFUeinCEQOlGCKwmEfHOCMlA_Y,29723 +torchaudio/prototype/models/_emformer_hubert.py,sha256=VPJWixyjonzN17UnZn4nuCPDb7wpa07TwbXNjOX3tOA,13598 +torchaudio/prototype/models/conv_emformer.py,sha256=4uzNW_dE-veL8gnOTBDxr-EBsRE15OPBC1HygfK4eW0,23210 +torchaudio/prototype/models/hifi_gan.py,sha256=nAybDiCiVDeGZNNUMt053Awl6JZloAPQXbdqGxSZ-KY,12663 +torchaudio/prototype/models/rnnt.py,sha256=XlW-v_SoL1qzg9W5QKX7WzkN4z6l9iZBMDhlNzW_tNY,30995 +torchaudio/prototype/models/rnnt_decoder.py,sha256=v-RwECvKoeNRx1BK_LqxMQ9KRv5JlkmKKM2ZFsMARM0,15829 +torchaudio/prototype/pipelines/__init__.py,sha256=Y6jfYwzJnXUfwJlcSdEN_rLbFy38w8woJ0Lz4I-IYW4,801 +torchaudio/prototype/pipelines/_vggish/__init__.py,sha256=O6HbJHT77bYGJLGP2ZasFrpmbthor8dbDeY6USAk_UI,222 +torchaudio/prototype/pipelines/_vggish/_vggish_impl.py,sha256=iaAd-YGbSr82itrqwulEXRZdeab3zspfk8FofOwJXHA,8614 +torchaudio/prototype/pipelines/_vggish/_vggish_pipeline.py,sha256=TYQe1qpx4vK16Wr7k_KoFTFoxThDDkS0be3uSXYaKqo,2776 +torchaudio/prototype/pipelines/hifigan_pipeline.py,sha256=IzrfPzRswawd2BBbYAyP_oRnwduDTNifhWscv6r3TQU,9810 +torchaudio/prototype/pipelines/rnnt_pipeline.py,sha256=3wwlCg3refzmsAA69XulFRy4GIixA2EH5ZqFbNFKXhk,2184 +torchaudio/prototype/transforms/__init__.py,sha256=1PcKxc8AWlSRkmchT6IDYDseO7itWY7ueZrlcAUZlkY,225 +torchaudio/prototype/transforms/_transforms.py,sha256=1Cs2XAH8Vzvr2TL0HLcXs-iw3MSw9moh8x3sZqs2t5w,19350 +torchaudio/sox_effects/__init__.py,sha256=gCxdiwHK3ldlGCeYc9VatJW5HyzjWIgw_Sz_krp_rOw,262 +torchaudio/sox_effects/sox_effects.py,sha256=RbH2wbQADiEe-CJG8FInZpAJW_53tplJgTMuqbhuddw,11053 +torchaudio/transforms/__init__.py,sha256=8S_XYRPQA9PE49SvHWf-8ROOu3ub_xJBDRLUEktkJPM,1397 +torchaudio/transforms/_multi_channel.py,sha256=GZ2rrwFt2KtSG7At7kS9Bqh1KmYYw0HwcUnEjc-AWr8,22221 +torchaudio/transforms/_transforms.py,sha256=mfwMNKNdrjiapFu92bDrUOEG_Jrw7WvZmuJDfs1hkyg,86938 +torchaudio/utils/__init__.py,sha256=NCtfdIUxDi1u0zaamscSbiWzbxn2TOI-MHHWOKU0RnQ,174 +torchaudio/utils/download.py,sha256=_i--5oyYtYIxkKfjOGkATbYQ66Yxcm8pdz-ux3Ki_M0,2963 +torchaudio/utils/ffmpeg_utils.py,sha256=3I6YM95eNyOAg2K-ebEy9kjBzEDq3_OBqggXztPIDcU,319 +torchaudio/utils/sox_utils.py,sha256=BiOyDtQkhq1eh_nkueyjK9KUTxgxU3nMZHI-goeLeYs,3040 +torchaudio/version.py,sha256=tWjS-JLUjqQpuJuN2kM3emEJzwf2wFVrA8Sr7AJmy8k,85 +torio/__init__.py,sha256=aX9s0XAHxHhEXE1akQt74BZ0cMUDgBPhaYHQH1lCbXQ,111 +torio/_extension/__init__.py,sha256=q5jjeOhSrzqn0WTEwrx61Fr13aCjb7IQCDGsBqAdGEU,313 +torio/_extension/utils.py,sha256=ktE0L_z-RF1qkpLVGgdG4DEGHa2Zn6uokOAmwC7Evvo,4904 +torio/io/__init__.py,sha256=xz7REkkyfRhAASzVCAfoNruFtAGIx1I--usPAa2tMww,226 +torio/io/_streaming_media_decoder.py,sha256=LdWiB8rvNbphtGUhjrPFKFAH_faqKi9_Q5zaRxvOFcs,34375 +torio/io/_streaming_media_encoder.py,sha256=rSTYmHdi7RPJ6YPgAyGJhbQvn4mcxLem3nlnr_ophTs,19722 +torio/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +torio/lib/_torio_ffmpeg4.so,sha256=qi24RiJupw0Fu6b1LXBcZNgwNU1XfWYAIw_En5ey-FQ,531736 +torio/lib/_torio_ffmpeg5.so,sha256=Nren-HMZJNO34Kw7DjDoMURvrqJgVnc-S4e58WwI-Kk,531736 +torio/lib/_torio_ffmpeg6.so,sha256=hJIBZvtSOP0POZyCfKOSq8luQHbjuyj_qNHYXOCH8HI,531736 +torio/lib/libtorio_ffmpeg4.so,sha256=IJUktpIJfAebQQM84HxkzM0JAsyl_lxpooN4WgQ5Uhc,647904 +torio/lib/libtorio_ffmpeg5.so,sha256=sOg3uFuw0TXXSAbEs_LaWN01lQaUP0hYVgNdLalGRP4,647904 +torio/lib/libtorio_ffmpeg6.so,sha256=4I40GxTKaMe5CB9XqVEYk6Z4XtKbRgINA0Rq-En4xaI,647904 +torio/utils/__init__.py,sha256=ScHtnontymRDNn9qEIC0neue5mfG82yhB8bwETOb0Z4,56 +torio/utils/ffmpeg_utils.py,sha256=5k6hG0CkIRTLIJVYoFpc06lJXocrr6oVh-0AR0N_MRE,8773 diff --git a/.venv/lib/python3.12/site-packages/torchaudio-2.8.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/torchaudio-2.8.0.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/torchaudio-2.8.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/torchaudio-2.8.0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..ec243cef59f5cc26e84fb072deb1b45275121b87 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/torchaudio-2.8.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (78.1.1) +Root-Is-Purelib: false +Tag: cp312-cp312-manylinux_2_28_x86_64 + diff --git a/.venv/lib/python3.12/site-packages/torchaudio-2.8.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/torchaudio-2.8.0.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..9f448fc64e7113394edf208556101c579616cc18 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/torchaudio-2.8.0.dist-info/top_level.txt @@ -0,0 +1,2 @@ +torchaudio +torio diff --git a/.venv/lib/python3.12/site-packages/torchaudio/__init__.py b/.venv/lib/python3.12/site-packages/torchaudio/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e533cafe9d57a7f2af2d3197494d96f4cb646743 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/torchaudio/__init__.py @@ -0,0 +1,64 @@ +from torchaudio._internal.module_utils import dropping_io_support, dropping_class_io_support + +# Initialize extension and backend first +from . import _extension # noqa # usort: skip +from ._backend import ( # noqa # usort: skip + AudioMetaData as _AudioMetaData, + get_audio_backend as _get_audio_backend, + info as _info, + list_audio_backends as _list_audio_backends, + load, + save, + set_audio_backend as _set_audio_backend, +) +from ._torchcodec import load_with_torchcodec, save_with_torchcodec + +AudioMetaData = dropping_class_io_support(_AudioMetaData) +get_audio_backend = dropping_io_support(_get_audio_backend) +info = dropping_io_support(_info) +list_audio_backends = dropping_io_support(_list_audio_backends) +set_audio_backend = dropping_io_support(_set_audio_backend) + +from . import ( # noqa: F401 + compliance, + datasets, + functional, + io, + kaldi_io, + models, + pipelines, + sox_effects, + transforms, + utils, +) + +# For BC +from . import backend # noqa # usort: skip + +try: + from .version import __version__, git_version # noqa: F401 +except ImportError: + pass + + +__all__ = [ + "AudioMetaData", + "load", + "load_with_torchcodec", + "save_with_torchcodec", + "info", + "save", + "io", + "compliance", + "datasets", + "functional", + "models", + "pipelines", + "kaldi_io", + "utils", + "sox_effects", + "transforms", + "list_audio_backends", + "get_audio_backend", + "set_audio_backend", +] diff --git a/.venv/lib/python3.12/site-packages/torchaudio/_torchcodec.py b/.venv/lib/python3.12/site-packages/torchaudio/_torchcodec.py new file mode 100644 index 0000000000000000000000000000000000000000..ab82e1fb77413f4b8b7888b68936b22158753166 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/torchaudio/_torchcodec.py @@ -0,0 +1,352 @@ +"""TorchCodec integration for TorchAudio.""" + +import os +from typing import BinaryIO, Optional, Tuple, Union + +import torch + + +def load_with_torchcodec( + uri: Union[BinaryIO, str, os.PathLike], + frame_offset: int = 0, + num_frames: int = -1, + normalize: bool = True, + channels_first: bool = True, + format: Optional[str] = None, + buffer_size: int = 4096, + backend: Optional[str] = None, +) -> Tuple[torch.Tensor, int]: + """Load audio data from source using TorchCodec's AudioDecoder. + + .. note:: + + This function supports the same API as :func:`~torchaudio.load`, and + relies on TorchCodec's decoding capabilities under the hood. It is + provided for convenience, but we do recommend that you port your code to + natively use ``torchcodec``'s ``AudioDecoder`` class for better + performance: + https://docs.pytorch.org/torchcodec/stable/generated/torchcodec.decoders.AudioDecoder. + In TorchAudio 2.9, :func:`~torchaudio.load` will be relying on + :func:`~torchaudio.load_with_torchcodec`. Note that some parameters of + :func:`~torchaudio.load`, like ``normalize``, ``buffer_size``, and + ``backend``, are ignored by :func:`~torchaudio.load_with_torchcodec`. + + + Args: + uri (path-like object or file-like object): + Source of audio data. The following types are accepted: + + * ``path-like``: File path or URL. + * ``file-like``: Object with ``read(size: int) -> bytes`` method. + + frame_offset (int, optional): + Number of samples to skip before start reading data. + num_frames (int, optional): + Maximum number of samples to read. ``-1`` reads all the remaining samples, + starting from ``frame_offset``. + normalize (bool, optional): + TorchCodec always returns normalized float32 samples. This parameter + is ignored and a warning is issued if set to False. + Default: ``True``. + channels_first (bool, optional): + When True, the returned Tensor has dimension `[channel, time]`. + Otherwise, the returned Tensor's dimension is `[time, channel]`. + format (str or None, optional): + Format hint for the decoder. May not be supported by all TorchCodec + decoders. (Default: ``None``) + buffer_size (int, optional): + Not used by TorchCodec AudioDecoder. Provided for API compatibility. + backend (str or None, optional): + Not used by TorchCodec AudioDecoder. Provided for API compatibility. + + Returns: + (torch.Tensor, int): Resulting Tensor and sample rate. + Always returns float32 tensors. If ``channels_first=True``, shape is + `[channel, time]`, otherwise `[time, channel]`. + + Raises: + ImportError: If torchcodec is not available. + ValueError: If unsupported parameters are used. + RuntimeError: If TorchCodec fails to decode the audio. + + Note: + - TorchCodec always returns normalized float32 samples, so the ``normalize`` + parameter has no effect. + - The ``buffer_size`` and ``backend`` parameters are ignored. + - Not all audio formats supported by torchaudio backends may be supported + by TorchCodec. + """ + # Import torchcodec here to provide clear error if not available + try: + from torchcodec.decoders import AudioDecoder + except ImportError as e: + raise ImportError( + "TorchCodec is required for load_with_torchcodec. " + "Please install torchcodec to use this function." + ) from e + + # Parameter validation and warnings + if not normalize: + import warnings + warnings.warn( + "TorchCodec AudioDecoder always returns normalized float32 samples. " + "The 'normalize=False' parameter is ignored.", + UserWarning, + stacklevel=2 + ) + + if buffer_size != 4096: + import warnings + warnings.warn( + "The 'buffer_size' parameter is not used by TorchCodec AudioDecoder.", + UserWarning, + stacklevel=2 + ) + + if backend is not None: + import warnings + warnings.warn( + "The 'backend' parameter is not used by TorchCodec AudioDecoder.", + UserWarning, + stacklevel=2 + ) + + if format is not None: + import warnings + warnings.warn( + "The 'format' parameter is not supported by TorchCodec AudioDecoder.", + UserWarning, + stacklevel=2 + ) + + # Create AudioDecoder + try: + decoder = AudioDecoder(uri) + except Exception as e: + raise RuntimeError(f"Failed to create AudioDecoder for {uri}: {e}") from e + + # Get sample rate from metadata + sample_rate = decoder.metadata.sample_rate + if sample_rate is None: + raise RuntimeError("Unable to determine sample rate from audio metadata") + + # Decode the entire file first, then subsample manually + # This is the simplest approach since torchcodec uses time-based indexing + try: + audio_samples = decoder.get_all_samples() + except Exception as e: + raise RuntimeError(f"Failed to decode audio samples: {e}") from e + + data = audio_samples.data + + # Apply frame_offset and num_frames (which are actually sample offsets) + if frame_offset > 0: + if frame_offset >= data.shape[1]: + # Return empty tensor if offset is beyond available data + empty_shape = (data.shape[0], 0) if channels_first else (0, data.shape[0]) + return torch.zeros(empty_shape, dtype=torch.float32), sample_rate + data = data[:, frame_offset:] + + if num_frames == 0: + # Return empty tensor if num_frames is 0 + empty_shape = (data.shape[0], 0) if channels_first else (0, data.shape[0]) + return torch.zeros(empty_shape, dtype=torch.float32), sample_rate + elif num_frames > 0: + data = data[:, :num_frames] + + # TorchCodec returns data in [channel, time] format by default + # Handle channels_first parameter + if not channels_first: + data = data.transpose(0, 1) # [channel, time] -> [time, channel] + + return data, sample_rate + + +def save_with_torchcodec( + uri: Union[str, os.PathLike], + src: torch.Tensor, + sample_rate: int, + channels_first: bool = True, + format: Optional[str] = None, + encoding: Optional[str] = None, + bits_per_sample: Optional[int] = None, + buffer_size: int = 4096, + backend: Optional[str] = None, + compression: Optional[Union[float, int]] = None, +) -> None: + """Save audio data to file using TorchCodec's AudioEncoder. + + .. note:: + + This function supports the same API as :func:`~torchaudio.save`, and + relies on TorchCodec's encoding capabilities under the hood. It is + provided for convenience, but we do recommend that you port your code to + natively use ``torchcodec``'s ``AudioEncoder`` class for better + performance: + https://docs.pytorch.org/torchcodec/stable/generated/torchcodec.encoders.AudioEncoder. + In TorchAudio 2.9, :func:`~torchaudio.save` will be relying on + :func:`~torchaudio.save_with_torchcodec`. Note that some parameters of + :func:`~torchaudio.save`, like ``format``, ``encoding``, + ``bits_per_sample``, ``buffer_size``, and ``backend``, are ignored by + are ignored by :func:`~torchaudio.save_with_torchcodec`. + + This function provides a TorchCodec-based alternative to torchaudio.save + with the same API. TorchCodec's AudioEncoder provides efficient encoding + with FFmpeg under the hood. + + Args: + uri (path-like object): + Path to save the audio file. The file extension determines the format. + + src (torch.Tensor): + Audio data to save. Must be a 1D or 2D tensor with float32 values + in the range [-1, 1]. If 2D, shape should be [channel, time] when + channels_first=True, or [time, channel] when channels_first=False. + + sample_rate (int): + Sample rate of the audio data. + + channels_first (bool, optional): + Indicates whether the input tensor has channels as the first dimension. + If True, expects [channel, time]. If False, expects [time, channel]. + Default: True. + + format (str or None, optional): + Audio format hint. Not used by TorchCodec (format is determined by + file extension). A warning is issued if provided. + Default: None. + + encoding (str or None, optional): + Audio encoding. Not fully supported by TorchCodec AudioEncoder. + A warning is issued if provided. Default: None. + + bits_per_sample (int or None, optional): + Bits per sample. Not directly supported by TorchCodec AudioEncoder. + A warning is issued if provided. Default: None. + + buffer_size (int, optional): + Not used by TorchCodec AudioEncoder. Provided for API compatibility. + A warning is issued if not default value. Default: 4096. + + backend (str or None, optional): + Not used by TorchCodec AudioEncoder. Provided for API compatibility. + A warning is issued if provided. Default: None. + + compression (float, int or None, optional): + Compression level or bit rate. Maps to bit_rate parameter in + TorchCodec AudioEncoder. Default: None. + + Raises: + ImportError: If torchcodec is not available. + ValueError: If input parameters are invalid. + RuntimeError: If TorchCodec fails to encode the audio. + + Note: + - TorchCodec AudioEncoder expects float32 samples in [-1, 1] range. + - Some parameters (format, encoding, bits_per_sample, buffer_size, backend) + are not used by TorchCodec but are provided for API compatibility. + - The output format is determined by the file extension in the uri. + - TorchCodec uses FFmpeg under the hood for encoding. + """ + # Import torchcodec here to provide clear error if not available + try: + from torchcodec.encoders import AudioEncoder + except ImportError as e: + raise ImportError( + "TorchCodec is required for save_with_torchcodec. " + "Please install torchcodec to use this function." + ) from e + + # Parameter validation and warnings + if format is not None: + import warnings + warnings.warn( + "The 'format' parameter is not used by TorchCodec AudioEncoder. " + "Format is determined by the file extension.", + UserWarning, + stacklevel=2 + ) + + if encoding is not None: + import warnings + warnings.warn( + "The 'encoding' parameter is not fully supported by TorchCodec AudioEncoder.", + UserWarning, + stacklevel=2 + ) + + if bits_per_sample is not None: + import warnings + warnings.warn( + "The 'bits_per_sample' parameter is not directly supported by TorchCodec AudioEncoder.", + UserWarning, + stacklevel=2 + ) + + if buffer_size != 4096: + import warnings + warnings.warn( + "The 'buffer_size' parameter is not used by TorchCodec AudioEncoder.", + UserWarning, + stacklevel=2 + ) + + if backend is not None: + import warnings + warnings.warn( + "The 'backend' parameter is not used by TorchCodec AudioEncoder.", + UserWarning, + stacklevel=2 + ) + + # Input validation + if not isinstance(src, torch.Tensor): + raise ValueError(f"Expected src to be a torch.Tensor, got {type(src)}") + + if src.dtype != torch.float32: + src = src.float() + + if sample_rate <= 0: + raise ValueError(f"sample_rate must be positive, got {sample_rate}") + + # Handle tensor shape and channels_first + if src.ndim == 1: + # Convert to 2D: [1, time] for channels_first=True + if channels_first: + data = src.unsqueeze(0) # [1, time] + else: + # For channels_first=False, input is [time] -> reshape to [time, 1] -> transpose to [1, time] + data = src.unsqueeze(1).transpose(0, 1) # [time, 1] -> [1, time] + elif src.ndim == 2: + if channels_first: + data = src # Already [channel, time] + else: + data = src.transpose(0, 1) # [time, channel] -> [channel, time] + else: + raise ValueError(f"Expected 1D or 2D tensor, got {src.ndim}D tensor") + + # Create AudioEncoder + try: + encoder = AudioEncoder(data, sample_rate=sample_rate) + except Exception as e: + raise RuntimeError(f"Failed to create AudioEncoder: {e}") from e + + # Determine bit_rate from compression parameter + bit_rate = None + if compression is not None: + if isinstance(compression, (int, float)): + bit_rate = int(compression) + else: + import warnings + warnings.warn( + f"Unsupported compression type {type(compression)}. " + "TorchCodec AudioEncoder expects int or float for bit_rate.", + UserWarning, + stacklevel=2 + ) + + # Save to file + try: + encoder.to_file(uri, bit_rate=bit_rate) + except Exception as e: + raise RuntimeError(f"Failed to save audio to {uri}: {e}") from e diff --git a/.venv/lib/python3.12/site-packages/torchaudio/kaldi_io.py b/.venv/lib/python3.12/site-packages/torchaudio/kaldi_io.py new file mode 100644 index 0000000000000000000000000000000000000000..40b67ddbbc3cf0fe30572bd803a023a1eba00c5c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/torchaudio/kaldi_io.py @@ -0,0 +1,150 @@ +# To use this file, the dependency (https://github.com/vesis84/kaldi-io-for-python) +# needs to be installed. This is a light wrapper around kaldi_io that returns +# torch.Tensors. +from typing import Any, Callable, Iterable, Tuple + +import torch +from torch import Tensor +from torchaudio._internal import module_utils as _mod_utils +from torchaudio._internal.module_utils import dropping_support + +if _mod_utils.is_module_available("numpy"): + import numpy as np + + +__all__ = [ + "read_vec_int_ark", + "read_vec_flt_scp", + "read_vec_flt_ark", + "read_mat_scp", + "read_mat_ark", +] + + +def _convert_method_output_to_tensor( + file_or_fd: Any, fn: Callable, convert_contiguous: bool = False +) -> Iterable[Tuple[str, Tensor]]: + r"""Takes a method invokes it. The output is converted to a tensor. + + Args: + file_or_fd (str/FileDescriptor): File name or file descriptor + fn (Callable): Function that has the signature (file name/descriptor) and converts it to + Iterable[Tuple[str, Tensor]]. + convert_contiguous (bool, optional): Determines whether the array should be converted into a + contiguous layout. (Default: ``False``) + + Returns: + Iterable[Tuple[str, Tensor]]: The string is the key and the tensor is vec/mat + """ + for key, np_arr in fn(file_or_fd): + if convert_contiguous: + np_arr = np.ascontiguousarray(np_arr) + yield key, torch.from_numpy(np_arr) + + +@dropping_support +@_mod_utils.requires_module("kaldi_io", "numpy") +def read_vec_int_ark(file_or_fd: Any) -> Iterable[Tuple[str, Tensor]]: + r"""Create generator of (key,vector) tuples, which reads from the ark file/stream. + + Args: + file_or_fd (str/FileDescriptor): ark, gzipped ark, pipe or opened file descriptor + + Returns: + Iterable[Tuple[str, Tensor]]: The string is the key and the tensor is the vector read from file + + Example + >>> # read ark to a 'dictionary' + >>> d = { u:d for u,d in torchaudio.kaldi_io.read_vec_int_ark(file) } + """ + + import kaldi_io + + # Requires convert_contiguous to be True because elements from int32 vector are + # sorted in tuples: (sizeof(int32), value) so strides are (5,) instead of (4,) which will throw an error + # in from_numpy as it expects strides to be a multiple of 4 (int32). + return _convert_method_output_to_tensor(file_or_fd, kaldi_io.read_vec_int_ark, convert_contiguous=True) + + +@dropping_support +@_mod_utils.requires_module("kaldi_io", "numpy") +def read_vec_flt_scp(file_or_fd: Any) -> Iterable[Tuple[str, Tensor]]: + r"""Create generator of (key,vector) tuples, read according to Kaldi scp. + + Args: + file_or_fd (str/FileDescriptor): scp, gzipped scp, pipe or opened file descriptor + + Returns: + Iterable[Tuple[str, Tensor]]: The string is the key and the tensor is the vector read from file + + Example + >>> # read scp to a 'dictionary' + >>> # d = { u:d for u,d in torchaudio.kaldi_io.read_vec_flt_scp(file) } + """ + + import kaldi_io + + return _convert_method_output_to_tensor(file_or_fd, kaldi_io.read_vec_flt_scp) + + +@dropping_support +@_mod_utils.requires_module("kaldi_io", "numpy") +def read_vec_flt_ark(file_or_fd: Any) -> Iterable[Tuple[str, Tensor]]: + r"""Create generator of (key,vector) tuples, which reads from the ark file/stream. + + Args: + file_or_fd (str/FileDescriptor): ark, gzipped ark, pipe or opened file descriptor + + Returns: + Iterable[Tuple[str, Tensor]]: The string is the key and the tensor is the vector read from file + + Example + >>> # read ark to a 'dictionary' + >>> d = { u:d for u,d in torchaudio.kaldi_io.read_vec_flt_ark(file) } + """ + + import kaldi_io + + return _convert_method_output_to_tensor(file_or_fd, kaldi_io.read_vec_flt_ark) + + +@dropping_support +@_mod_utils.requires_module("kaldi_io", "numpy") +def read_mat_scp(file_or_fd: Any) -> Iterable[Tuple[str, Tensor]]: + r"""Create generator of (key,matrix) tuples, read according to Kaldi scp. + + Args: + file_or_fd (str/FileDescriptor): scp, gzipped scp, pipe or opened file descriptor + + Returns: + Iterable[Tuple[str, Tensor]]: The string is the key and the tensor is the matrix read from file + + Example + >>> # read scp to a 'dictionary' + >>> d = { u:d for u,d in torchaudio.kaldi_io.read_mat_scp(file) } + """ + + import kaldi_io + + return _convert_method_output_to_tensor(file_or_fd, kaldi_io.read_mat_scp) + + +@dropping_support +@_mod_utils.requires_module("kaldi_io", "numpy") +def read_mat_ark(file_or_fd: Any) -> Iterable[Tuple[str, Tensor]]: + r"""Create generator of (key,matrix) tuples, which reads from the ark file/stream. + + Args: + file_or_fd (str/FileDescriptor): ark, gzipped ark, pipe or opened file descriptor + + Returns: + Iterable[Tuple[str, Tensor]]: The string is the key and the tensor is the matrix read from file + + Example + >>> # read ark to a 'dictionary' + >>> d = { u:d for u,d in torchaudio.kaldi_io.read_mat_ark(file) } + """ + + import kaldi_io + + return _convert_method_output_to_tensor(file_or_fd, kaldi_io.read_mat_ark) diff --git a/.venv/lib/python3.12/site-packages/torchaudio/version.py b/.venv/lib/python3.12/site-packages/torchaudio/version.py new file mode 100644 index 0000000000000000000000000000000000000000..7a9f7244d999ad2cb7963ec247f7c7abae63beb4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/torchaudio/version.py @@ -0,0 +1,2 @@ +__version__ = '2.8.0+cu128' +git_version = '6e1c7fe9ff6d82b8665d0a46d859d3357d2ebaaa' diff --git a/.venv/lib/python3.12/site-packages/torchvision.libs/libsharpyuv.b609dd4c.so.0 b/.venv/lib/python3.12/site-packages/torchvision.libs/libsharpyuv.b609dd4c.so.0 new file mode 100644 index 0000000000000000000000000000000000000000..f77d830efb43c49a8e614599b30b3a90cbd08bab Binary files /dev/null and b/.venv/lib/python3.12/site-packages/torchvision.libs/libsharpyuv.b609dd4c.so.0 differ diff --git a/.venv/lib/python3.12/site-packages/torchvision/__init__.py b/.venv/lib/python3.12/site-packages/torchvision/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5d06156c25f1dfd34e9f01529e5a6b4bbeda7b42 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/torchvision/__init__.py @@ -0,0 +1,105 @@ +import os +import warnings +from modulefinder import Module + +import torch + +# Don't re-order these, we need to load the _C extension (done when importing +# .extensions) before entering _meta_registrations. +from .extension import _HAS_OPS # usort:skip +from torchvision import _meta_registrations, datasets, io, models, ops, transforms, utils # usort:skip + +try: + from .version import __version__ # noqa: F401 +except ImportError: + pass + + +# Check if torchvision is being imported within the root folder +if not _HAS_OPS and os.path.dirname(os.path.realpath(__file__)) == os.path.join( + os.path.realpath(os.getcwd()), "torchvision" +): + message = ( + "You are importing torchvision within its own root folder ({}). " + "This is not expected to work and may give errors. Please exit the " + "torchvision project source and relaunch your python interpreter." + ) + warnings.warn(message.format(os.getcwd())) + +_image_backend = "PIL" + +_video_backend = "pyav" + + +def set_image_backend(backend): + """ + Specifies the package used to load images. + + Args: + backend (string): Name of the image backend. one of {'PIL', 'accimage'}. + The :mod:`accimage` package uses the Intel IPP library. It is + generally faster than PIL, but does not support as many operations. + """ + global _image_backend + if backend not in ["PIL", "accimage"]: + raise ValueError(f"Invalid backend '{backend}'. Options are 'PIL' and 'accimage'") + _image_backend = backend + + +def get_image_backend(): + """ + Gets the name of the package used to load images + """ + return _image_backend + + +def set_video_backend(backend): + """ + Specifies the package used to decode videos. + + Args: + backend (string): Name of the video backend. one of {'pyav', 'video_reader'}. + The :mod:`pyav` package uses the 3rd party PyAv library. It is a Pythonic + binding for the FFmpeg libraries. + The :mod:`video_reader` package includes a native C++ implementation on + top of FFMPEG libraries, and a python API of TorchScript custom operator. + It generally decodes faster than :mod:`pyav`, but is perhaps less robust. + + .. note:: + Building with FFMPEG is disabled by default in the latest `main`. If you want to use the 'video_reader' + backend, please compile torchvision from source. + """ + global _video_backend + if backend not in ["pyav", "video_reader", "cuda"]: + raise ValueError("Invalid video backend '%s'. Options are 'pyav', 'video_reader' and 'cuda'" % backend) + if backend == "video_reader" and not io._HAS_CPU_VIDEO_DECODER: + # TODO: better messages + message = "video_reader video backend is not available. Please compile torchvision from source and try again" + raise RuntimeError(message) + elif backend == "cuda" and not io._HAS_GPU_VIDEO_DECODER: + # TODO: better messages + message = "cuda video backend is not available." + raise RuntimeError(message) + else: + _video_backend = backend + + +def get_video_backend(): + """ + Returns the currently active video backend used to decode videos. + + Returns: + str: Name of the video backend. one of {'pyav', 'video_reader'}. + """ + + return _video_backend + + +def _is_tracing(): + return torch._C._get_tracing_state() + + +def disable_beta_transforms_warning(): + # Noop, only exists to avoid breaking existing code. + # See https://github.com/pytorch/vision/issues/7896 + pass diff --git a/.venv/lib/python3.12/site-packages/torchvision/_internally_replaced_utils.py b/.venv/lib/python3.12/site-packages/torchvision/_internally_replaced_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..e0fa72489f1ac8fba771fc7bc20fc80424a71d85 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/torchvision/_internally_replaced_utils.py @@ -0,0 +1,51 @@ +import importlib.machinery +import os + +from torch.hub import _get_torch_home + + +_HOME = os.path.join(_get_torch_home(), "datasets", "vision") +_USE_SHARDED_DATASETS = False +IN_FBCODE = False + + +def _download_file_from_remote_location(fpath: str, url: str) -> None: + pass + + +def _is_remote_location_available() -> bool: + return False + + +try: + from torch.hub import load_state_dict_from_url # noqa: 401 +except ImportError: + from torch.utils.model_zoo import load_url as load_state_dict_from_url # noqa: 401 + + +def _get_extension_path(lib_name): + + lib_dir = os.path.dirname(__file__) + if os.name == "nt": + # Register the main torchvision library location on the default DLL path + import ctypes + + kernel32 = ctypes.WinDLL("kernel32.dll", use_last_error=True) + with_load_library_flags = hasattr(kernel32, "AddDllDirectory") + prev_error_mode = kernel32.SetErrorMode(0x0001) + + if with_load_library_flags: + kernel32.AddDllDirectory.restype = ctypes.c_void_p + + os.add_dll_directory(lib_dir) + + kernel32.SetErrorMode(prev_error_mode) + + loader_details = (importlib.machinery.ExtensionFileLoader, importlib.machinery.EXTENSION_SUFFIXES) + + extfinder = importlib.machinery.FileFinder(lib_dir, loader_details) + ext_specs = extfinder.find_spec(lib_name) + if ext_specs is None: + raise ImportError + + return ext_specs.origin diff --git a/.venv/lib/python3.12/site-packages/torchvision/_meta_registrations.py b/.venv/lib/python3.12/site-packages/torchvision/_meta_registrations.py new file mode 100644 index 0000000000000000000000000000000000000000..f75bfb77a7f25a1842509de595f109f232994574 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/torchvision/_meta_registrations.py @@ -0,0 +1,225 @@ +import functools + +import torch +import torch._custom_ops +import torch.library + +# Ensure that torch.ops.torchvision is visible +import torchvision.extension # noqa: F401 + + +@functools.lru_cache(None) +def get_meta_lib(): + return torch.library.Library("torchvision", "IMPL", "Meta") + + +def register_meta(op_name, overload_name="default"): + def wrapper(fn): + if torchvision.extension._has_ops(): + get_meta_lib().impl(getattr(getattr(torch.ops.torchvision, op_name), overload_name), fn) + return fn + + return wrapper + + +@register_meta("roi_align") +def meta_roi_align(input, rois, spatial_scale, pooled_height, pooled_width, sampling_ratio, aligned): + torch._check(rois.size(1) == 5, lambda: "rois must have shape as Tensor[K, 5]") + torch._check( + input.dtype == rois.dtype, + lambda: ( + "Expected tensor for input to have the same type as tensor for rois; " + f"but type {input.dtype} does not equal {rois.dtype}" + ), + ) + num_rois = rois.size(0) + channels = input.size(1) + return input.new_empty((num_rois, channels, pooled_height, pooled_width)) + + +@register_meta("_roi_align_backward") +def meta_roi_align_backward( + grad, rois, spatial_scale, pooled_height, pooled_width, batch_size, channels, height, width, sampling_ratio, aligned +): + torch._check( + grad.dtype == rois.dtype, + lambda: ( + "Expected tensor for grad to have the same type as tensor for rois; " + f"but type {grad.dtype} does not equal {rois.dtype}" + ), + ) + return grad.new_empty((batch_size, channels, height, width)) + + +@register_meta("ps_roi_align") +def meta_ps_roi_align(input, rois, spatial_scale, pooled_height, pooled_width, sampling_ratio): + torch._check(rois.size(1) == 5, lambda: "rois must have shape as Tensor[K, 5]") + torch._check( + input.dtype == rois.dtype, + lambda: ( + "Expected tensor for input to have the same type as tensor for rois; " + f"but type {input.dtype} does not equal {rois.dtype}" + ), + ) + channels = input.size(1) + torch._check( + channels % (pooled_height * pooled_width) == 0, + "input channels must be a multiple of pooling height * pooling width", + ) + + num_rois = rois.size(0) + out_size = (num_rois, channels // (pooled_height * pooled_width), pooled_height, pooled_width) + return input.new_empty(out_size), torch.empty(out_size, dtype=torch.int32, device="meta") + + +@register_meta("_ps_roi_align_backward") +def meta_ps_roi_align_backward( + grad, + rois, + channel_mapping, + spatial_scale, + pooled_height, + pooled_width, + sampling_ratio, + batch_size, + channels, + height, + width, +): + torch._check( + grad.dtype == rois.dtype, + lambda: ( + "Expected tensor for grad to have the same type as tensor for rois; " + f"but type {grad.dtype} does not equal {rois.dtype}" + ), + ) + return grad.new_empty((batch_size, channels, height, width)) + + +@register_meta("roi_pool") +def meta_roi_pool(input, rois, spatial_scale, pooled_height, pooled_width): + torch._check(rois.size(1) == 5, lambda: "rois must have shape as Tensor[K, 5]") + torch._check( + input.dtype == rois.dtype, + lambda: ( + "Expected tensor for input to have the same type as tensor for rois; " + f"but type {input.dtype} does not equal {rois.dtype}" + ), + ) + num_rois = rois.size(0) + channels = input.size(1) + out_size = (num_rois, channels, pooled_height, pooled_width) + return input.new_empty(out_size), torch.empty(out_size, device="meta", dtype=torch.int32) + + +@register_meta("_roi_pool_backward") +def meta_roi_pool_backward( + grad, rois, argmax, spatial_scale, pooled_height, pooled_width, batch_size, channels, height, width +): + torch._check( + grad.dtype == rois.dtype, + lambda: ( + "Expected tensor for grad to have the same type as tensor for rois; " + f"but type {grad.dtype} does not equal {rois.dtype}" + ), + ) + return grad.new_empty((batch_size, channels, height, width)) + + +@register_meta("ps_roi_pool") +def meta_ps_roi_pool(input, rois, spatial_scale, pooled_height, pooled_width): + torch._check(rois.size(1) == 5, lambda: "rois must have shape as Tensor[K, 5]") + torch._check( + input.dtype == rois.dtype, + lambda: ( + "Expected tensor for input to have the same type as tensor for rois; " + f"but type {input.dtype} does not equal {rois.dtype}" + ), + ) + channels = input.size(1) + torch._check( + channels % (pooled_height * pooled_width) == 0, + "input channels must be a multiple of pooling height * pooling width", + ) + num_rois = rois.size(0) + out_size = (num_rois, channels // (pooled_height * pooled_width), pooled_height, pooled_width) + return input.new_empty(out_size), torch.empty(out_size, device="meta", dtype=torch.int32) + + +@register_meta("_ps_roi_pool_backward") +def meta_ps_roi_pool_backward( + grad, rois, channel_mapping, spatial_scale, pooled_height, pooled_width, batch_size, channels, height, width +): + torch._check( + grad.dtype == rois.dtype, + lambda: ( + "Expected tensor for grad to have the same type as tensor for rois; " + f"but type {grad.dtype} does not equal {rois.dtype}" + ), + ) + return grad.new_empty((batch_size, channels, height, width)) + + +@torch.library.register_fake("torchvision::nms") +def meta_nms(dets, scores, iou_threshold): + torch._check(dets.dim() == 2, lambda: f"boxes should be a 2d tensor, got {dets.dim()}D") + torch._check(dets.size(1) == 4, lambda: f"boxes should have 4 elements in dimension 1, got {dets.size(1)}") + torch._check(scores.dim() == 1, lambda: f"scores should be a 1d tensor, got {scores.dim()}") + torch._check( + dets.size(0) == scores.size(0), + lambda: f"boxes and scores should have same number of elements in dimension 0, got {dets.size(0)} and {scores.size(0)}", + ) + ctx = torch._custom_ops.get_ctx() + num_to_keep = ctx.create_unbacked_symint() + return dets.new_empty(num_to_keep, dtype=torch.long) + + +@register_meta("deform_conv2d") +def meta_deform_conv2d( + input, + weight, + offset, + mask, + bias, + stride_h, + stride_w, + pad_h, + pad_w, + dil_h, + dil_w, + n_weight_grps, + n_offset_grps, + use_mask, +): + + out_height, out_width = offset.shape[-2:] + out_channels = weight.shape[0] + batch_size = input.shape[0] + return input.new_empty((batch_size, out_channels, out_height, out_width)) + + +@register_meta("_deform_conv2d_backward") +def meta_deform_conv2d_backward( + grad, + input, + weight, + offset, + mask, + bias, + stride_h, + stride_w, + pad_h, + pad_w, + dilation_h, + dilation_w, + groups, + offset_groups, + use_mask, +): + + grad_input = input.new_empty(input.shape) + grad_weight = weight.new_empty(weight.shape) + grad_offset = offset.new_empty(offset.shape) + grad_mask = mask.new_empty(mask.shape) + grad_bias = bias.new_empty(bias.shape) + return grad_input, grad_weight, grad_offset, grad_mask, grad_bias diff --git a/.venv/lib/python3.12/site-packages/torchvision/_utils.py b/.venv/lib/python3.12/site-packages/torchvision/_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..aee2676df45d1fa3ade4fc31e3890c9d36600fc7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/torchvision/_utils.py @@ -0,0 +1,33 @@ +import enum +from collections.abc import Sequence +from typing import TypeVar + +T = TypeVar("T", bound=enum.Enum) + + +class StrEnumMeta(enum.EnumMeta): + auto = enum.auto + + def from_str(self: type[T], member: str) -> T: # type: ignore[misc] + try: + return self[member] + except KeyError: + # TODO: use `add_suggestion` from torchvision.prototype.utils._internal to improve the error message as + # soon as it is migrated. + raise ValueError(f"Unknown value '{member}' for {self.__name__}.") from None + + +class StrEnum(enum.Enum, metaclass=StrEnumMeta): + pass + + +def sequence_to_str(seq: Sequence, separate_last: str = "") -> str: + if not seq: + return "" + if len(seq) == 1: + return f"'{seq[0]}'" + + head = "'" + "', '".join([str(item) for item in seq[:-1]]) + "'" + tail = f"{'' if separate_last and len(seq) == 2 else ','} {separate_last}'{seq[-1]}'" + + return head + tail diff --git a/.venv/lib/python3.12/site-packages/torchvision/extension.py b/.venv/lib/python3.12/site-packages/torchvision/extension.py new file mode 100644 index 0000000000000000000000000000000000000000..67801056e88b44d40bc2d382d62c389bf4ef039e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/torchvision/extension.py @@ -0,0 +1,92 @@ +import os +import sys + +import torch + +from ._internally_replaced_utils import _get_extension_path + + +_HAS_OPS = False + + +def _has_ops(): + return False + + +try: + # On Windows Python-3.8.x has `os.add_dll_directory` call, + # which is called to configure dll search path. + # To find cuda related dlls we need to make sure the + # conda environment/bin path is configured Please take a look: + # https://stackoverflow.com/questions/59330863/cant-import-dll-module-in-python + # Please note: if some path can't be added using add_dll_directory we simply ignore this path + if os.name == "nt" and sys.version_info < (3, 9): + env_path = os.environ["PATH"] + path_arr = env_path.split(";") + for path in path_arr: + if os.path.exists(path): + try: + os.add_dll_directory(path) # type: ignore[attr-defined] + except Exception: + pass + + lib_path = _get_extension_path("_C") + torch.ops.load_library(lib_path) + _HAS_OPS = True + + def _has_ops(): # noqa: F811 + return True + +except (ImportError, OSError): + pass + + +def _assert_has_ops(): + if not _has_ops(): + raise RuntimeError( + "Couldn't load custom C++ ops. This can happen if your PyTorch and " + "torchvision versions are incompatible, or if you had errors while compiling " + "torchvision from source. For further information on the compatible versions, check " + "https://github.com/pytorch/vision#installation for the compatibility matrix. " + "Please check your PyTorch version with torch.__version__ and your torchvision " + "version with torchvision.__version__ and verify if they are compatible, and if not " + "please reinstall torchvision so that it matches your PyTorch install." + ) + + +def _check_cuda_version(): + """ + Make sure that CUDA versions match between the pytorch install and torchvision install + """ + if not _HAS_OPS: + return -1 + from torch.version import cuda as torch_version_cuda + + _version = torch.ops.torchvision._cuda_version() + if _version != -1 and torch_version_cuda is not None: + tv_version = str(_version) + if int(tv_version) < 10000: + tv_major = int(tv_version[0]) + tv_minor = int(tv_version[2]) + else: + tv_major = int(tv_version[0:2]) + tv_minor = int(tv_version[3]) + t_version = torch_version_cuda.split(".") + t_major = int(t_version[0]) + t_minor = int(t_version[1]) + if t_major != tv_major: + raise RuntimeError( + "Detected that PyTorch and torchvision were compiled with different CUDA major versions. " + f"PyTorch has CUDA Version={t_major}.{t_minor} and torchvision has " + f"CUDA Version={tv_major}.{tv_minor}. " + "Please reinstall the torchvision that matches your PyTorch install." + ) + return _version + + +def _load_library(lib_name): + lib_path = _get_extension_path(lib_name) + torch.ops.load_library(lib_path) + + +_check_cuda_version() diff --git a/.venv/lib/python3.12/site-packages/torchvision/utils.py b/.venv/lib/python3.12/site-packages/torchvision/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..0d819ef8330024daec22ad07321ce983ba7edd8c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/torchvision/utils.py @@ -0,0 +1,799 @@ +import collections +import math +import pathlib +import warnings +from itertools import repeat +from types import FunctionType +from typing import Any, BinaryIO, Optional, Union + +import numpy as np +import torch +from PIL import __version__ as PILLOW_VERSION_STRING, Image, ImageColor, ImageDraw, ImageFont + + +__all__ = [ + "_Image_fromarray", + "make_grid", + "save_image", + "draw_bounding_boxes", + "draw_segmentation_masks", + "draw_keypoints", + "flow_to_image", +] + + +@torch.no_grad() +def make_grid( + tensor: Union[torch.Tensor, list[torch.Tensor]], + nrow: int = 8, + padding: int = 2, + normalize: bool = False, + value_range: Optional[tuple[int, int]] = None, + scale_each: bool = False, + pad_value: float = 0.0, +) -> torch.Tensor: + """ + Make a grid of images. + + Args: + tensor (Tensor or list): 4D mini-batch Tensor of shape (B x C x H x W) + or a list of images all of the same size. + nrow (int, optional): Number of images displayed in each row of the grid. + The final grid size is ``(B / nrow, nrow)``. Default: ``8``. + padding (int, optional): amount of padding. Default: ``2``. + normalize (bool, optional): If True, shift the image to the range (0, 1), + by the min and max values specified by ``value_range``. Default: ``False``. + value_range (tuple, optional): tuple (min, max) where min and max are numbers, + then these numbers are used to normalize the image. By default, min and max + are computed from the tensor. + scale_each (bool, optional): If ``True``, scale each image in the batch of + images separately rather than the (min, max) over all images. Default: ``False``. + pad_value (float, optional): Value for the padded pixels. Default: ``0``. + + Returns: + grid (Tensor): the tensor containing grid of images. + """ + if not torch.jit.is_scripting() and not torch.jit.is_tracing(): + _log_api_usage_once(make_grid) + if not torch.is_tensor(tensor): + if isinstance(tensor, list): + for t in tensor: + if not torch.is_tensor(t): + raise TypeError(f"tensor or list of tensors expected, got a list containing {type(t)}") + else: + raise TypeError(f"tensor or list of tensors expected, got {type(tensor)}") + + # if list of tensors, convert to a 4D mini-batch Tensor + if isinstance(tensor, list): + tensor = torch.stack(tensor, dim=0) + + if tensor.dim() == 2: # single image H x W + tensor = tensor.unsqueeze(0) + if tensor.dim() == 3: # single image + if tensor.size(0) == 1: # if single-channel, convert to 3-channel + tensor = torch.cat((tensor, tensor, tensor), 0) + tensor = tensor.unsqueeze(0) + + if tensor.dim() == 4 and tensor.size(1) == 1: # single-channel images + tensor = torch.cat((tensor, tensor, tensor), 1) + + if normalize is True: + tensor = tensor.clone() # avoid modifying tensor in-place + if value_range is not None and not isinstance(value_range, tuple): + raise TypeError("value_range has to be a tuple (min, max) if specified. min and max are numbers") + + def norm_ip(img, low, high): + img.clamp_(min=low, max=high) + img.sub_(low).div_(max(high - low, 1e-5)) + + def norm_range(t, value_range): + if value_range is not None: + norm_ip(t, value_range[0], value_range[1]) + else: + norm_ip(t, float(t.min()), float(t.max())) + + if scale_each is True: + for t in tensor: # loop over mini-batch dimension + norm_range(t, value_range) + else: + norm_range(tensor, value_range) + + if not isinstance(tensor, torch.Tensor): + raise TypeError("tensor should be of type torch.Tensor") + if tensor.size(0) == 1: + return tensor.squeeze(0) + + # make the mini-batch of images into a grid + nmaps = tensor.size(0) + xmaps = min(nrow, nmaps) + ymaps = int(math.ceil(float(nmaps) / xmaps)) + height, width = int(tensor.size(2) + padding), int(tensor.size(3) + padding) + num_channels = tensor.size(1) + grid = tensor.new_full((num_channels, height * ymaps + padding, width * xmaps + padding), pad_value) + k = 0 + for y in range(ymaps): + for x in range(xmaps): + if k >= nmaps: + break + # Tensor.copy_() is a valid method but seems to be missing from the stubs + # https://pytorch.org/docs/stable/tensors.html#torch.Tensor.copy_ + grid.narrow(1, y * height + padding, height - padding).narrow( # type: ignore[attr-defined] + 2, x * width + padding, width - padding + ).copy_(tensor[k]) + k = k + 1 + return grid + + +class _ImageDrawTV(ImageDraw.ImageDraw): + """ + A wrapper around PIL.ImageDraw to add functionalities for drawing rotated bounding boxes. + """ + + def oriented_rectangle(self, xy, fill=None, outline=None, width=1): + self.dashed_line(((xy[0], xy[1]), (xy[2], xy[3])), width=width, fill=outline) + for i in range(2, len(xy), 2): + self.line( + ((xy[i], xy[i + 1]), (xy[(i + 2) % len(xy)], xy[(i + 3) % len(xy)])), + width=width, + fill=outline, + ) + self.polygon(xy, fill=fill, outline=None, width=0) + + def dashed_line(self, xy, fill=None, width=0, joint=None, dash_length=5, space_length=5): + # Calculate the total length of the line + total_length = 0 + for i in range(1, len(xy)): + x1, y1 = xy[i - 1] + x2, y2 = xy[i] + total_length += ((x2 - x1) ** 2 + (y2 - y1) ** 2) ** 0.5 + # Initialize the current position and the current dash + current_position = 0 + current_dash = True + # Iterate over the coordinates of the line + for i in range(1, len(xy)): + x1, y1 = xy[i - 1] + x2, y2 = xy[i] + # Calculate the length of this segment + segment_length = ((x2 - x1) ** 2 + (y2 - y1) ** 2) ** 0.5 + # While there are still dashes to draw on this segment + while segment_length > 0: + # Calculate the length of this dash + dash_length_to_draw = min(segment_length, dash_length if current_dash else space_length) + # Calculate the end point of this dash + dx = x2 - x1 + dy = y2 - y1 + angle = math.atan2(dy, dx) + end_x = x1 + math.cos(angle) * dash_length_to_draw + end_y = y1 + math.sin(angle) * dash_length_to_draw + # If this is a dash, draw it + if current_dash: + self.line([(x1, y1), (end_x, end_y)], fill, width, joint) + # Update the current position and the current dash + current_position += dash_length_to_draw + segment_length -= dash_length_to_draw + x1, y1 = end_x, end_y + current_dash = not current_dash + + +def _Image_fromarray( + obj: np.ndarray, + mode: str, +) -> Image.Image: + """ + A wrapper around PIL.Image.fromarray to mitigate the deprecation of the + mode paramter. See: + https://pillow.readthedocs.io/en/stable/releasenotes/11.3.0.html#image-fromarray-mode-parameter + """ + + # This may throw if the version string is from an install that comes from a + # non-stable or development version. We'll fall back to the old behavior in + # such cases. + try: + PILLOW_VERSION = tuple(int(x) for x in PILLOW_VERSION_STRING.split(".")) + except Exception: + PILLOW_VERSION = None + + if PILLOW_VERSION is not None and PILLOW_VERSION >= (11, 3): + # The actual PR that implements the deprecation has more context for why + # it was done, and also points out some problems: + # + # https://github.com/python-pillow/Pillow/pull/9018 + # + # Our use case falls into those problems. We actually rely on the old + # behavior of Image.fromarray(): + # + # new behavior: PIL will infer the image mode from the data passed + # in. That is, the type and shape determines the mode. + # + # old behiavor: The mode will change how PIL reads the image, + # regardless of the data. That is, it will make the + # data work with the mode. + # + # Our uses of Image.fromarray() are effectively a "turn into PIL image + # AND convert the kind" operation. In particular, in + # functional.to_pil_image() and transforms.ToPILImage. + # + # However, Image.frombuffer() still performs this conversion. The code + # below is lifted from the new implementation of Image.fromarray(). We + # omit the code that infers the mode, and use the code that figures out + # from the data passed in (obj) what the correct parameters are to + # Image.frombuffer(). + # + # Note that the alternate solution below does not work: + # + # img = Image.fromarray(obj) + # img = img.convert(mode) + # + # The resulting image has very different actual pixel values than before. + # + # TODO: Issue #9151. Pillow has an open PR to restore the functionality + # we rely on: + # + # https://github.com/python-pillow/Pillow/pull/9063 + # + # When that is part of a release, we can revisit this hack below. + arr = obj.__array_interface__ + shape = arr["shape"] + ndim = len(shape) + size = 1 if ndim == 1 else shape[1], shape[0] + + strides = arr.get("strides", None) + contiguous_obj: Union[np.ndarray, bytes] = obj + if strides is not None: + # We require that the data is contiguous; if it is not, we need to + # convert it into a contiguous format. + if hasattr(obj, "tobytes"): + contiguous_obj = obj.tobytes() + elif hasattr(obj, "tostring"): + contiguous_obj = obj.tostring() + else: + raise ValueError("Unable to convert obj into contiguous format") + + return Image.frombuffer(mode, size, contiguous_obj, "raw", mode, 0, 1) + else: + return Image.fromarray(obj, mode) + + +@torch.no_grad() +def save_image( + tensor: Union[torch.Tensor, list[torch.Tensor]], + fp: Union[str, pathlib.Path, BinaryIO], + format: Optional[str] = None, + **kwargs, +) -> None: + """ + Save a given Tensor into an image file. + + Args: + tensor (Tensor or list): Image to be saved. If given a mini-batch tensor, + saves the tensor as a grid of images by calling ``make_grid``. + fp (string or file object): A filename or a file object + format(Optional): 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. + **kwargs: Other arguments are documented in ``make_grid``. + """ + + if not torch.jit.is_scripting() and not torch.jit.is_tracing(): + _log_api_usage_once(save_image) + grid = make_grid(tensor, **kwargs) + # Add 0.5 after unnormalizing to [0, 255] to round to the nearest integer + ndarr = grid.mul(255).add_(0.5).clamp_(0, 255).permute(1, 2, 0).to("cpu", torch.uint8).numpy() + im = Image.fromarray(ndarr) + im.save(fp, format=format) + + +@torch.no_grad() +def draw_bounding_boxes( + image: torch.Tensor, + boxes: torch.Tensor, + labels: Optional[list[str]] = None, + colors: Optional[Union[list[Union[str, tuple[int, int, int]]], str, tuple[int, int, int]]] = None, + fill: Optional[bool] = False, + width: int = 1, + font: Optional[str] = None, + font_size: Optional[int] = None, + label_colors: Optional[Union[list[Union[str, tuple[int, int, int]]], str, tuple[int, int, int]]] = None, + fill_labels: bool = False, +) -> torch.Tensor: + """ + Draws bounding boxes on given RGB image. + The image values should be uint8 in [0, 255] or float in [0, 1]. + If fill is True, Resulting Tensor should be saved as PNG image. + + Args: + image (Tensor): Tensor of shape (C, H, W) and dtype uint8 or float. + boxes (Tensor): Tensor of size (N, 4) or (N, 8) containing bounding boxes. + For (N, 4), the format is (xmin, ymin, xmax, ymax) and the boxes are absolute coordinates with respect to the image. + In other words: `0 <= xmin < xmax < W` and `0 <= ymin < ymax < H`. + For (N, 8), the format is (x1, y1, x2, y2, x3, y3, x4, y4) and the boxes are absolute coordinates with respect to the underlying + object, so no need to verify the latter inequalities. + labels (List[str]): List containing the labels of bounding boxes. + colors (color or list of colors, optional): List containing the colors + of the boxes or single color for all boxes. The color can be represented as + PIL strings e.g. "red" or "#FF00FF", or as RGB tuples e.g. ``(240, 10, 157)``. + By default, random colors are generated for boxes. + fill (bool): If `True` fills the bounding box with specified color. + width (int): Width of bounding box. + font (str): A filename containing a TrueType font. If the file is not found in this filename, the loader may + also search in other directories, such as the `fonts/` directory on Windows or `/Library/Fonts/`, + `/System/Library/Fonts/` and `~/Library/Fonts/` on macOS. + font_size (int): The requested font size in points. + label_colors (color or list of colors, optional): Colors for the label text. See the description of the + `colors` argument for details. Defaults to the same colors used for the boxes, or to black if ``fill_labels`` is True. + fill_labels (bool): If `True` fills the label background with specified box color (from the ``colors`` parameter). Default: False. + + Returns: + img (Tensor[C, H, W]): Image Tensor of dtype uint8 with bounding boxes plotted. + + """ + import torchvision.transforms.v2.functional as F # noqa + + if not torch.jit.is_scripting() and not torch.jit.is_tracing(): + _log_api_usage_once(draw_bounding_boxes) + if not isinstance(image, torch.Tensor): + raise TypeError(f"Tensor expected, got {type(image)}") + elif not (image.dtype == torch.uint8 or image.is_floating_point()): + raise ValueError(f"The image dtype must be uint8 or float, got {image.dtype}") + elif image.dim() != 3: + raise ValueError("Pass individual images, not batches") + elif image.size(0) not in {1, 3}: + raise ValueError("Only grayscale and RGB images are supported") + elif boxes.shape[-1] == 4 and ((boxes[:, 0] > boxes[:, 2]).any() or (boxes[:, 1] > boxes[:, 3]).any()): + raise ValueError( + "Boxes need to be in (xmin, ymin, xmax, ymax) format. Use torchvision.ops.box_convert to convert them" + ) + + num_boxes = boxes.shape[0] + + if num_boxes == 0: + warnings.warn("boxes doesn't contain any box. No box was drawn") + return image + + if labels is None: + labels: Union[list[str], list[None]] = [None] * num_boxes # type: ignore[no-redef] + elif len(labels) != num_boxes: + raise ValueError( + f"Number of boxes ({num_boxes}) and labels ({len(labels)}) mismatch. Please specify labels for each box." + ) + + colors = _parse_colors(colors, num_objects=num_boxes) + if label_colors or fill_labels: + label_colors = _parse_colors(label_colors if label_colors else "black", num_objects=num_boxes) # type: ignore[assignment] + else: + label_colors = colors.copy() # type: ignore[assignment] + + if font is None: + if font_size is not None: + warnings.warn("Argument 'font_size' will be ignored since 'font' is not set.") + txt_font = ImageFont.load_default() + else: + txt_font = ImageFont.truetype(font=font, size=font_size or 10) + + # Handle Grayscale images + if image.size(0) == 1: + image = torch.tile(image, (3, 1, 1)) + + original_dtype = image.dtype + if original_dtype.is_floating_point: + image = F.to_dtype(image, dtype=torch.uint8, scale=True) + + img_to_draw = F.to_pil_image(image) + img_boxes = boxes.to(torch.int64).tolist() + + if fill: + draw = _ImageDrawTV(img_to_draw, "RGBA") + else: + draw = _ImageDrawTV(img_to_draw) + + for bbox, color, label, label_color in zip(img_boxes, colors, labels, label_colors): # type: ignore[arg-type] + draw_method = draw.oriented_rectangle if len(bbox) > 4 else draw.rectangle + fill_color = color + (100,) if fill else None + draw_method(bbox, width=width, outline=color, fill=fill_color) + + if label is not None: + box_margin = 1 + margin = width + box_margin + if fill_labels: + left, top, right, bottom = draw.textbbox((bbox[0] + margin, bbox[1] + margin), label, font=txt_font) + draw.rectangle( + (left - box_margin, top - box_margin, right + box_margin, bottom + box_margin), fill=color + ) + draw.text((bbox[0] + margin, bbox[1] + margin), label, fill=label_color, font=txt_font) # type: ignore[arg-type] + + out = F.pil_to_tensor(img_to_draw) + if original_dtype.is_floating_point: + out = F.to_dtype(out, dtype=original_dtype, scale=True) + return out + + +@torch.no_grad() +def draw_segmentation_masks( + image: torch.Tensor, + masks: torch.Tensor, + alpha: float = 0.8, + colors: Optional[Union[list[Union[str, tuple[int, int, int]]], str, tuple[int, int, int]]] = None, +) -> torch.Tensor: + """ + Draws segmentation masks on given RGB image. + The image values should be uint8 in [0, 255] or float in [0, 1]. + + Args: + image (Tensor): Tensor of shape (3, H, W) and dtype uint8 or float. + masks (Tensor): Tensor of shape (num_masks, H, W) or (H, W) and dtype bool. + alpha (float): Float number between 0 and 1 denoting the transparency of the masks. + 0 means full transparency, 1 means no transparency. + colors (color or list of colors, optional): List containing the colors + of the masks or single color for all masks. The color can be represented as + PIL strings e.g. "red" or "#FF00FF", or as RGB tuples e.g. ``(240, 10, 157)``. + By default, random colors are generated for each mask. + + Returns: + img (Tensor[C, H, W]): Image Tensor, with segmentation masks drawn on top. + """ + + if not torch.jit.is_scripting() and not torch.jit.is_tracing(): + _log_api_usage_once(draw_segmentation_masks) + if not isinstance(image, torch.Tensor): + raise TypeError(f"The image must be a tensor, got {type(image)}") + elif not (image.dtype == torch.uint8 or image.is_floating_point()): + raise ValueError(f"The image dtype must be uint8 or float, got {image.dtype}") + elif image.dim() != 3: + raise ValueError("Pass individual images, not batches") + elif image.size()[0] != 3: + raise ValueError("Pass an RGB image. Other Image formats are not supported") + if masks.ndim == 2: + masks = masks[None, :, :] + if masks.ndim != 3: + raise ValueError("masks must be of shape (H, W) or (batch_size, H, W)") + if masks.dtype != torch.bool: + raise ValueError(f"The masks must be of dtype bool. Got {masks.dtype}") + if masks.shape[-2:] != image.shape[-2:]: + raise ValueError("The image and the masks must have the same height and width") + + num_masks = masks.size()[0] + overlapping_masks = masks.sum(dim=0) > 1 + + if num_masks == 0: + warnings.warn("masks doesn't contain any mask. No mask was drawn") + return image + + original_dtype = image.dtype + colors = [ + torch.tensor(color, dtype=original_dtype, device=image.device) + for color in _parse_colors(colors, num_objects=num_masks, dtype=original_dtype) + ] + + img_to_draw = image.detach().clone() + # TODO: There might be a way to vectorize this + for mask, color in zip(masks, colors): + img_to_draw[:, mask] = color[:, None] + + img_to_draw[:, overlapping_masks] = 0 + + out = image * (1 - alpha) + img_to_draw * alpha + # Note: at this point, out is a float tensor in [0, 1] or [0, 255] depending on original_dtype + return out.to(original_dtype) + + +@torch.no_grad() +def draw_keypoints( + image: torch.Tensor, + keypoints: torch.Tensor, + connectivity: Optional[list[tuple[int, int]]] = None, + colors: Optional[Union[str, tuple[int, int, int]]] = None, + radius: int = 2, + width: int = 3, + visibility: Optional[torch.Tensor] = None, +) -> torch.Tensor: + """ + Draws Keypoints on given RGB image. + The image values should be uint8 in [0, 255] or float in [0, 1]. + Keypoints can be drawn for multiple instances at a time. + + This method allows that keypoints and their connectivity are drawn based on the visibility of this keypoint. + + Args: + image (Tensor): Tensor of shape (3, H, W) and dtype uint8 or float. + keypoints (Tensor): Tensor of shape (num_instances, K, 2) the K keypoint locations for each of the N instances, + in the format [x, y]. + connectivity (List[Tuple[int, int]]]): A List of tuple where each tuple contains a pair of keypoints + to be connected. + If at least one of the two connected keypoints has a ``visibility`` of False, + this specific connection is not drawn. + Exclusions due to invisibility are computed per-instance. + colors (str, Tuple): The color can be represented as + PIL strings e.g. "red" or "#FF00FF", or as RGB tuples e.g. ``(240, 10, 157)``. + radius (int): Integer denoting radius of keypoint. + width (int): Integer denoting width of line connecting keypoints. + visibility (Tensor): Tensor of shape (num_instances, K) specifying the visibility of the K + keypoints for each of the N instances. + True means that the respective keypoint is visible and should be drawn. + False means invisible, so neither the point nor possible connections containing it are drawn. + The input tensor will be cast to bool. + Default ``None`` means that all the keypoints are visible. + For more details, see :ref:`draw_keypoints_with_visibility`. + + Returns: + img (Tensor[C, H, W]): Image Tensor with keypoints drawn. + """ + + if not torch.jit.is_scripting() and not torch.jit.is_tracing(): + _log_api_usage_once(draw_keypoints) + # validate image + if not isinstance(image, torch.Tensor): + raise TypeError(f"The image must be a tensor, got {type(image)}") + elif not (image.dtype == torch.uint8 or image.is_floating_point()): + raise ValueError(f"The image dtype must be uint8 or float, got {image.dtype}") + elif image.dim() != 3: + raise ValueError("Pass individual images, not batches") + elif image.size()[0] != 3: + raise ValueError("Pass an RGB image. Other Image formats are not supported") + + # validate keypoints + if keypoints.ndim != 3: + raise ValueError("keypoints must be of shape (num_instances, K, 2)") + + # validate visibility + if visibility is None: # set default + visibility = torch.ones(keypoints.shape[:-1], dtype=torch.bool) + if visibility.ndim == 3: + # If visibility was passed as pred.split([2, 1], dim=-1), it will be of shape (num_instances, K, 1). + # We make sure it is of shape (num_instances, K). This isn't documented, we're just being nice. + visibility = visibility.squeeze(-1) + if visibility.ndim != 2: + raise ValueError(f"visibility must be of shape (num_instances, K). Got ndim={visibility.ndim}") + if visibility.shape != keypoints.shape[:-1]: + raise ValueError( + "keypoints and visibility must have the same dimensionality for num_instances and K. " + f"Got {visibility.shape = } and {keypoints.shape = }" + ) + + original_dtype = image.dtype + if original_dtype.is_floating_point: + from torchvision.transforms.v2.functional import to_dtype # noqa + + image = to_dtype(image, dtype=torch.uint8, scale=True) + + ndarr = image.permute(1, 2, 0).cpu().numpy() + img_to_draw = Image.fromarray(ndarr) + draw = ImageDraw.Draw(img_to_draw) + img_kpts = keypoints.to(torch.int64).tolist() + img_vis = visibility.cpu().bool().tolist() + + for kpt_inst, vis_inst in zip(img_kpts, img_vis): + for kpt_coord, kp_vis in zip(kpt_inst, vis_inst): + if not kp_vis: + continue + x1 = kpt_coord[0] - radius + x2 = kpt_coord[0] + radius + y1 = kpt_coord[1] - radius + y2 = kpt_coord[1] + radius + draw.ellipse([x1, y1, x2, y2], fill=colors, outline=None, width=0) + + if connectivity: + for connection in connectivity: + if (not vis_inst[connection[0]]) or (not vis_inst[connection[1]]): + continue + start_pt_x = kpt_inst[connection[0]][0] + start_pt_y = kpt_inst[connection[0]][1] + + end_pt_x = kpt_inst[connection[1]][0] + end_pt_y = kpt_inst[connection[1]][1] + + draw.line( + ((start_pt_x, start_pt_y), (end_pt_x, end_pt_y)), + width=width, + ) + + out = torch.from_numpy(np.array(img_to_draw)).permute(2, 0, 1) + if original_dtype.is_floating_point: + out = to_dtype(out, dtype=original_dtype, scale=True) + return out + + +# Flow visualization code adapted from https://github.com/tomrunia/OpticalFlow_Visualization +@torch.no_grad() +def flow_to_image(flow: torch.Tensor) -> torch.Tensor: + """ + Converts a flow to an RGB image. + + Args: + flow (Tensor): Flow of shape (N, 2, H, W) or (2, H, W) and dtype torch.float. + + Returns: + img (Tensor): Image Tensor of dtype uint8 where each color corresponds + to a given flow direction. Shape is (N, 3, H, W) or (3, H, W) depending on the input. + """ + + if flow.dtype != torch.float: + raise ValueError(f"Flow should be of dtype torch.float, got {flow.dtype}.") + + orig_shape = flow.shape + if flow.ndim == 3: + flow = flow[None] # Add batch dim + + if flow.ndim != 4 or flow.shape[1] != 2: + raise ValueError(f"Input flow should have shape (2, H, W) or (N, 2, H, W), got {orig_shape}.") + + max_norm = torch.sum(flow**2, dim=1).sqrt().max() + epsilon = torch.finfo((flow).dtype).eps + normalized_flow = flow / (max_norm + epsilon) + img = _normalized_flow_to_image(normalized_flow) + + if len(orig_shape) == 3: + img = img[0] # Remove batch dim + return img + + +@torch.no_grad() +def _normalized_flow_to_image(normalized_flow: torch.Tensor) -> torch.Tensor: + """ + Converts a batch of normalized flow to an RGB image. + + Args: + normalized_flow (torch.Tensor): Normalized flow tensor of shape (N, 2, H, W) + Returns: + img (Tensor(N, 3, H, W)): Flow visualization image of dtype uint8. + """ + + N, _, H, W = normalized_flow.shape + device = normalized_flow.device + flow_image = torch.zeros((N, 3, H, W), dtype=torch.uint8, device=device) + colorwheel = _make_colorwheel().to(device) # shape [55x3] + num_cols = colorwheel.shape[0] + norm = torch.sum(normalized_flow**2, dim=1).sqrt() + a = torch.atan2(-normalized_flow[:, 1, :, :], -normalized_flow[:, 0, :, :]) / torch.pi + fk = (a + 1) / 2 * (num_cols - 1) + k0 = torch.floor(fk).to(torch.long) + k1 = k0 + 1 + k1[k1 == num_cols] = 0 + f = fk - k0 + + for c in range(colorwheel.shape[1]): + tmp = colorwheel[:, c] + col0 = tmp[k0] / 255.0 + col1 = tmp[k1] / 255.0 + col = (1 - f) * col0 + f * col1 + col = 1 - norm * (1 - col) + flow_image[:, c, :, :] = torch.floor(255 * col) + return flow_image + + +def _make_colorwheel() -> torch.Tensor: + """ + Generates a color wheel for optical flow visualization as presented in: + Baker et al. "A Database and Evaluation Methodology for Optical Flow" (ICCV, 2007) + URL: http://vision.middlebury.edu/flow/flowEval-iccv07.pdf. + + Returns: + colorwheel (Tensor[55, 3]): Colorwheel Tensor. + """ + + RY = 15 + YG = 6 + GC = 4 + CB = 11 + BM = 13 + MR = 6 + + ncols = RY + YG + GC + CB + BM + MR + colorwheel = torch.zeros((ncols, 3)) + col = 0 + + # RY + colorwheel[0:RY, 0] = 255 + colorwheel[0:RY, 1] = torch.floor(255 * torch.arange(0, RY) / RY) + col = col + RY + # YG + colorwheel[col : col + YG, 0] = 255 - torch.floor(255 * torch.arange(0, YG) / YG) + colorwheel[col : col + YG, 1] = 255 + col = col + YG + # GC + colorwheel[col : col + GC, 1] = 255 + colorwheel[col : col + GC, 2] = torch.floor(255 * torch.arange(0, GC) / GC) + col = col + GC + # CB + colorwheel[col : col + CB, 1] = 255 - torch.floor(255 * torch.arange(CB) / CB) + colorwheel[col : col + CB, 2] = 255 + col = col + CB + # BM + colorwheel[col : col + BM, 2] = 255 + colorwheel[col : col + BM, 0] = torch.floor(255 * torch.arange(0, BM) / BM) + col = col + BM + # MR + colorwheel[col : col + MR, 2] = 255 - torch.floor(255 * torch.arange(MR) / MR) + colorwheel[col : col + MR, 0] = 255 + return colorwheel + + +def _generate_color_palette(num_objects: int): + palette = torch.tensor([2**25 - 1, 2**15 - 1, 2**21 - 1]) + return [tuple((i * palette) % 255) for i in range(num_objects)] + + +def _parse_colors( + colors: Union[None, str, tuple[int, int, int], list[Union[str, tuple[int, int, int]]]], + *, + num_objects: int, + dtype: torch.dtype = torch.uint8, +) -> list[tuple[int, int, int]]: + """ + Parses a specification of colors for a set of objects. + + Args: + colors: A specification of colors for the objects. This can be one of the following: + - None: to generate a color palette automatically. + - A list of colors: where each color is either a string (specifying a named color) or an RGB tuple. + - A string or an RGB tuple: to use the same color for all objects. + + If `colors` is a tuple, it should be a 3-tuple specifying the RGB values of the color. + If `colors` is a list, it should have at least as many elements as the number of objects to color. + + num_objects (int): The number of objects to color. + + Returns: + A list of 3-tuples, specifying the RGB values of the colors. + + Raises: + ValueError: If the number of colors in the list is less than the number of objects to color. + If `colors` is not a list, tuple, string or None. + """ + if colors is None: + colors = _generate_color_palette(num_objects) + elif isinstance(colors, list): + if len(colors) < num_objects: + raise ValueError( + f"Number of colors must be equal or larger than the number of objects, but got {len(colors)} < {num_objects}." + ) + elif not isinstance(colors, (tuple, str)): + raise ValueError(f"`colors` must be a tuple or a string, or a list thereof, but got {colors}.") + elif isinstance(colors, tuple) and len(colors) != 3: + raise ValueError(f"If passed as tuple, colors should be an RGB triplet, but got {colors}.") + else: # colors specifies a single color for all objects + colors = [colors] * num_objects + + colors = [ImageColor.getrgb(color) if isinstance(color, str) else color for color in colors] + if dtype.is_floating_point: # [0, 255] -> [0, 1] + colors = [tuple(v / 255 for v in color) for color in colors] # type: ignore[union-attr] + return colors # type: ignore[return-value] + + +def _log_api_usage_once(obj: Any) -> None: + """ + Logs API usage(module and name) within an organization. + In a large ecosystem, it's often useful to track the PyTorch and + TorchVision APIs usage. This API provides the similar functionality to the + logging module in the Python stdlib. It can be used for debugging purpose + to log which methods are used and by default it is inactive, unless the user + manually subscribes a logger via the `SetAPIUsageLogger method `_. + Please note it is triggered only once for the same API call within a process. + It does not collect any data from open-source users since it is no-op by default. + For more information, please refer to + * PyTorch note: https://pytorch.org/docs/stable/notes/large_scale_deployments.html#api-usage-logging; + * Logging policy: https://github.com/pytorch/vision/issues/5052; + + Args: + obj (class instance or method): an object to extract info from. + """ + module = obj.__module__ + if not module.startswith("torchvision"): + module = f"torchvision.internal.{module}" + name = obj.__class__.__name__ + if isinstance(obj, FunctionType): + name = obj.__name__ + torch._C._log_api_usage_once(f"{module}.{name}") + + +def _make_ntuple(x: Any, n: int) -> tuple[Any, ...]: + """ + Make n-tuple from input x. If x is an iterable, then we just convert it to tuple. + Otherwise, we will make a tuple of length n, all with value of x. + reference: https://github.com/pytorch/pytorch/blob/master/torch/nn/modules/utils.py#L8 + + Args: + x (Any): input value + n (int): length of the resulting tuple + """ + if isinstance(x, collections.abc.Iterable): + return tuple(x) + return tuple(repeat(x, n)) diff --git a/.venv/lib/python3.12/site-packages/torchvision/version.py b/.venv/lib/python3.12/site-packages/torchvision/version.py new file mode 100644 index 0000000000000000000000000000000000000000..c003f79a5faf4b50c90760e7f5600b26dfd8d579 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/torchvision/version.py @@ -0,0 +1,5 @@ +__version__ = '0.23.0+cu128' +git_version = '824e8c8726b65fd9d5abdc9702f81c2b0c4c0dc8' +from torchvision.extension import _check_cuda_version +if _check_cuda_version() > 0: + cuda = _check_cuda_version() diff --git a/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..facd8df0060cf2e45a7e37020bd18185d8730fb0 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/METADATA @@ -0,0 +1,840 @@ +Metadata-Version: 2.4 +Name: transformers +Version: 4.54.0.dev0 +Summary: State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow +Home-page: https://github.com/huggingface/transformers +Author: The Hugging Face team (past and future) with the help of all our contributors (https://github.com/huggingface/transformers/graphs/contributors) +Author-email: transformers@huggingface.co +License: Apache 2.0 License +Keywords: NLP vision speech deep learning transformer pytorch tensorflow jax BERT GPT-2 Wav2Vec2 ViT +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Education +Classifier: Intended Audience :: Science/Research +Classifier: License :: OSI Approved :: Apache Software License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 3 +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: Topic :: Scientific/Engineering :: Artificial Intelligence +Requires-Python: >=3.9.0 +Description-Content-Type: text/markdown +License-File: LICENSE +Requires-Dist: filelock +Requires-Dist: huggingface-hub<1.0,>=0.30.0 +Requires-Dist: numpy>=1.17 +Requires-Dist: packaging>=20.0 +Requires-Dist: pyyaml>=5.1 +Requires-Dist: regex!=2019.12.17 +Requires-Dist: requests +Requires-Dist: tokenizers<0.22,>=0.21 +Requires-Dist: safetensors>=0.4.3 +Requires-Dist: tqdm>=4.27 +Provides-Extra: ja +Requires-Dist: fugashi>=1.0; extra == "ja" +Requires-Dist: ipadic<2.0,>=1.0.0; extra == "ja" +Requires-Dist: unidic_lite>=1.0.7; extra == "ja" +Requires-Dist: unidic>=1.0.2; extra == "ja" +Requires-Dist: sudachipy>=0.6.6; extra == "ja" +Requires-Dist: sudachidict_core>=20220729; extra == "ja" +Requires-Dist: rhoknp<1.3.1,>=1.1.0; extra == "ja" +Provides-Extra: sklearn +Requires-Dist: scikit-learn; extra == "sklearn" +Provides-Extra: tf +Requires-Dist: tensorflow<2.16,>2.9; extra == "tf" +Requires-Dist: onnxconverter-common; extra == "tf" +Requires-Dist: tf2onnx; extra == "tf" +Requires-Dist: tensorflow-text<2.16; extra == "tf" +Requires-Dist: keras-nlp<0.14.0,>=0.3.1; extra == "tf" +Provides-Extra: tf-cpu +Requires-Dist: keras<2.16,>2.9; extra == "tf-cpu" +Requires-Dist: tensorflow-cpu<2.16,>2.9; extra == "tf-cpu" +Requires-Dist: onnxconverter-common; extra == "tf-cpu" +Requires-Dist: tf2onnx; extra == "tf-cpu" +Requires-Dist: tensorflow-text<2.16; extra == "tf-cpu" +Requires-Dist: keras-nlp<0.14.0,>=0.3.1; extra == "tf-cpu" +Requires-Dist: tensorflow-probability<0.24; extra == "tf-cpu" +Provides-Extra: torch +Requires-Dist: torch>=2.1; extra == "torch" +Requires-Dist: accelerate>=0.26.0; extra == "torch" +Provides-Extra: accelerate +Requires-Dist: accelerate>=0.26.0; extra == "accelerate" +Provides-Extra: hf-xet +Requires-Dist: hf_xet; extra == "hf-xet" +Provides-Extra: retrieval +Requires-Dist: faiss-cpu; extra == "retrieval" +Requires-Dist: datasets!=2.5.0; extra == "retrieval" +Provides-Extra: flax +Requires-Dist: jax<=0.4.13,>=0.4.1; extra == "flax" +Requires-Dist: jaxlib<=0.4.13,>=0.4.1; extra == "flax" +Requires-Dist: flax<=0.7.0,>=0.4.1; extra == "flax" +Requires-Dist: optax<=0.1.4,>=0.0.8; extra == "flax" +Requires-Dist: scipy<1.13.0; extra == "flax" +Provides-Extra: tokenizers +Requires-Dist: tokenizers<0.22,>=0.21; extra == "tokenizers" +Provides-Extra: ftfy +Requires-Dist: ftfy; extra == "ftfy" +Provides-Extra: onnxruntime +Requires-Dist: onnxruntime>=1.4.0; extra == "onnxruntime" +Requires-Dist: onnxruntime-tools>=1.4.2; extra == "onnxruntime" +Provides-Extra: onnx +Requires-Dist: onnxconverter-common; extra == "onnx" +Requires-Dist: tf2onnx; extra == "onnx" +Requires-Dist: onnxruntime>=1.4.0; extra == "onnx" +Requires-Dist: onnxruntime-tools>=1.4.2; extra == "onnx" +Provides-Extra: modelcreation +Requires-Dist: cookiecutter==1.7.3; extra == "modelcreation" +Provides-Extra: sagemaker +Requires-Dist: sagemaker>=2.31.0; extra == "sagemaker" +Provides-Extra: deepspeed +Requires-Dist: deepspeed>=0.9.3; extra == "deepspeed" +Requires-Dist: accelerate>=0.26.0; extra == "deepspeed" +Provides-Extra: optuna +Requires-Dist: optuna; extra == "optuna" +Provides-Extra: ray +Requires-Dist: ray[tune]>=2.7.0; extra == "ray" +Provides-Extra: sigopt +Requires-Dist: sigopt; extra == "sigopt" +Provides-Extra: hub-kernels +Requires-Dist: kernels<0.7,>=0.6.1; extra == "hub-kernels" +Provides-Extra: integrations +Requires-Dist: kernels<0.7,>=0.6.1; extra == "integrations" +Requires-Dist: optuna; extra == "integrations" +Requires-Dist: ray[tune]>=2.7.0; extra == "integrations" +Requires-Dist: sigopt; extra == "integrations" +Provides-Extra: serving +Requires-Dist: pydantic>=2; extra == "serving" +Requires-Dist: uvicorn; extra == "serving" +Requires-Dist: fastapi; extra == "serving" +Requires-Dist: starlette; extra == "serving" +Requires-Dist: torch>=2.1; extra == "serving" +Requires-Dist: accelerate>=0.26.0; extra == "serving" +Provides-Extra: audio +Requires-Dist: librosa; extra == "audio" +Requires-Dist: pyctcdecode>=0.4.0; extra == "audio" +Requires-Dist: phonemizer; extra == "audio" +Requires-Dist: kenlm; extra == "audio" +Provides-Extra: speech +Requires-Dist: torchaudio; extra == "speech" +Requires-Dist: librosa; extra == "speech" +Requires-Dist: pyctcdecode>=0.4.0; extra == "speech" +Requires-Dist: phonemizer; extra == "speech" +Requires-Dist: kenlm; extra == "speech" +Provides-Extra: torch-speech +Requires-Dist: torchaudio; extra == "torch-speech" +Requires-Dist: librosa; extra == "torch-speech" +Requires-Dist: pyctcdecode>=0.4.0; extra == "torch-speech" +Requires-Dist: phonemizer; extra == "torch-speech" +Requires-Dist: kenlm; extra == "torch-speech" +Provides-Extra: tf-speech +Requires-Dist: librosa; extra == "tf-speech" +Requires-Dist: pyctcdecode>=0.4.0; extra == "tf-speech" +Requires-Dist: phonemizer; extra == "tf-speech" +Requires-Dist: kenlm; extra == "tf-speech" +Provides-Extra: flax-speech +Requires-Dist: librosa; extra == "flax-speech" +Requires-Dist: pyctcdecode>=0.4.0; extra == "flax-speech" +Requires-Dist: phonemizer; extra == "flax-speech" +Requires-Dist: kenlm; extra == "flax-speech" +Provides-Extra: vision +Requires-Dist: Pillow<=15.0,>=10.0.1; extra == "vision" +Provides-Extra: timm +Requires-Dist: timm<=1.0.11; extra == "timm" +Provides-Extra: torch-vision +Requires-Dist: torchvision; extra == "torch-vision" +Requires-Dist: Pillow<=15.0,>=10.0.1; extra == "torch-vision" +Provides-Extra: natten +Requires-Dist: natten<0.15.0,>=0.14.6; extra == "natten" +Provides-Extra: codecarbon +Requires-Dist: codecarbon>=2.8.1; extra == "codecarbon" +Provides-Extra: video +Requires-Dist: av; extra == "video" +Provides-Extra: num2words +Requires-Dist: num2words; extra == "num2words" +Provides-Extra: sentencepiece +Requires-Dist: sentencepiece!=0.1.92,>=0.1.91; extra == "sentencepiece" +Requires-Dist: protobuf; extra == "sentencepiece" +Provides-Extra: tiktoken +Requires-Dist: tiktoken; extra == "tiktoken" +Requires-Dist: blobfile; extra == "tiktoken" +Provides-Extra: mistral-common +Requires-Dist: mistral-common[opencv]>=1.6.3; extra == "mistral-common" +Provides-Extra: testing +Requires-Dist: pytest>=7.2.0; extra == "testing" +Requires-Dist: pytest-asyncio; extra == "testing" +Requires-Dist: pytest-rich; extra == "testing" +Requires-Dist: pytest-xdist; extra == "testing" +Requires-Dist: pytest-order; extra == "testing" +Requires-Dist: pytest-rerunfailures; extra == "testing" +Requires-Dist: timeout-decorator; extra == "testing" +Requires-Dist: parameterized; extra == "testing" +Requires-Dist: psutil; extra == "testing" +Requires-Dist: datasets!=2.5.0; extra == "testing" +Requires-Dist: dill<0.3.5; extra == "testing" +Requires-Dist: evaluate>=0.2.0; extra == "testing" +Requires-Dist: pytest-timeout; extra == "testing" +Requires-Dist: ruff==0.11.2; extra == "testing" +Requires-Dist: rouge-score!=0.0.7,!=0.0.8,!=0.1,!=0.1.1; extra == "testing" +Requires-Dist: nltk<=3.8.1; extra == "testing" +Requires-Dist: GitPython<3.1.19; extra == "testing" +Requires-Dist: sacremoses; extra == "testing" +Requires-Dist: rjieba; extra == "testing" +Requires-Dist: beautifulsoup4; extra == "testing" +Requires-Dist: tensorboard; extra == "testing" +Requires-Dist: pydantic>=2; extra == "testing" +Requires-Dist: sentencepiece!=0.1.92,>=0.1.91; extra == "testing" +Requires-Dist: sacrebleu<2.0.0,>=1.4.12; extra == "testing" +Requires-Dist: faiss-cpu; extra == "testing" +Requires-Dist: datasets!=2.5.0; extra == "testing" +Requires-Dist: cookiecutter==1.7.3; extra == "testing" +Requires-Dist: mistral-common[opencv]>=1.6.3; extra == "testing" +Provides-Extra: deepspeed-testing +Requires-Dist: deepspeed>=0.9.3; extra == "deepspeed-testing" +Requires-Dist: accelerate>=0.26.0; extra == "deepspeed-testing" +Requires-Dist: pytest>=7.2.0; extra == "deepspeed-testing" +Requires-Dist: pytest-asyncio; extra == "deepspeed-testing" +Requires-Dist: pytest-rich; extra == "deepspeed-testing" +Requires-Dist: pytest-xdist; extra == "deepspeed-testing" +Requires-Dist: pytest-order; extra == "deepspeed-testing" +Requires-Dist: pytest-rerunfailures; extra == "deepspeed-testing" +Requires-Dist: timeout-decorator; extra == "deepspeed-testing" +Requires-Dist: parameterized; extra == "deepspeed-testing" +Requires-Dist: psutil; extra == "deepspeed-testing" +Requires-Dist: datasets!=2.5.0; extra == "deepspeed-testing" +Requires-Dist: dill<0.3.5; extra == "deepspeed-testing" +Requires-Dist: evaluate>=0.2.0; extra == "deepspeed-testing" +Requires-Dist: pytest-timeout; extra == "deepspeed-testing" +Requires-Dist: ruff==0.11.2; extra == "deepspeed-testing" +Requires-Dist: rouge-score!=0.0.7,!=0.0.8,!=0.1,!=0.1.1; extra == "deepspeed-testing" +Requires-Dist: nltk<=3.8.1; extra == "deepspeed-testing" +Requires-Dist: GitPython<3.1.19; extra == "deepspeed-testing" +Requires-Dist: sacremoses; extra == "deepspeed-testing" +Requires-Dist: rjieba; extra == "deepspeed-testing" +Requires-Dist: beautifulsoup4; extra == "deepspeed-testing" +Requires-Dist: tensorboard; extra == "deepspeed-testing" +Requires-Dist: pydantic>=2; extra == "deepspeed-testing" +Requires-Dist: sentencepiece!=0.1.92,>=0.1.91; extra == "deepspeed-testing" +Requires-Dist: sacrebleu<2.0.0,>=1.4.12; extra == "deepspeed-testing" +Requires-Dist: faiss-cpu; extra == "deepspeed-testing" +Requires-Dist: datasets!=2.5.0; extra == "deepspeed-testing" +Requires-Dist: cookiecutter==1.7.3; extra == "deepspeed-testing" +Requires-Dist: mistral-common[opencv]>=1.6.3; extra == "deepspeed-testing" +Requires-Dist: optuna; extra == "deepspeed-testing" +Requires-Dist: sentencepiece!=0.1.92,>=0.1.91; extra == "deepspeed-testing" +Requires-Dist: protobuf; extra == "deepspeed-testing" +Provides-Extra: ruff +Requires-Dist: ruff==0.11.2; extra == "ruff" +Provides-Extra: quality +Requires-Dist: datasets!=2.5.0; extra == "quality" +Requires-Dist: ruff==0.11.2; extra == "quality" +Requires-Dist: GitPython<3.1.19; extra == "quality" +Requires-Dist: urllib3<2.0.0; extra == "quality" +Requires-Dist: libcst; extra == "quality" +Requires-Dist: rich; extra == "quality" +Requires-Dist: pandas<2.3.0; extra == "quality" +Provides-Extra: all +Requires-Dist: tensorflow<2.16,>2.9; extra == "all" +Requires-Dist: onnxconverter-common; extra == "all" +Requires-Dist: tf2onnx; extra == "all" +Requires-Dist: tensorflow-text<2.16; extra == "all" +Requires-Dist: keras-nlp<0.14.0,>=0.3.1; extra == "all" +Requires-Dist: torch>=2.1; extra == "all" +Requires-Dist: accelerate>=0.26.0; extra == "all" +Requires-Dist: jax<=0.4.13,>=0.4.1; extra == "all" +Requires-Dist: jaxlib<=0.4.13,>=0.4.1; extra == "all" +Requires-Dist: flax<=0.7.0,>=0.4.1; extra == "all" +Requires-Dist: optax<=0.1.4,>=0.0.8; extra == "all" +Requires-Dist: scipy<1.13.0; extra == "all" +Requires-Dist: sentencepiece!=0.1.92,>=0.1.91; extra == "all" +Requires-Dist: protobuf; extra == "all" +Requires-Dist: tokenizers<0.22,>=0.21; extra == "all" +Requires-Dist: torchaudio; extra == "all" +Requires-Dist: librosa; extra == "all" +Requires-Dist: pyctcdecode>=0.4.0; extra == "all" +Requires-Dist: phonemizer; extra == "all" +Requires-Dist: kenlm; extra == "all" +Requires-Dist: Pillow<=15.0,>=10.0.1; extra == "all" +Requires-Dist: kernels<0.7,>=0.6.1; extra == "all" +Requires-Dist: optuna; extra == "all" +Requires-Dist: ray[tune]>=2.7.0; extra == "all" +Requires-Dist: sigopt; extra == "all" +Requires-Dist: timm<=1.0.11; extra == "all" +Requires-Dist: torchvision; extra == "all" +Requires-Dist: Pillow<=15.0,>=10.0.1; extra == "all" +Requires-Dist: codecarbon>=2.8.1; extra == "all" +Requires-Dist: accelerate>=0.26.0; extra == "all" +Requires-Dist: av; extra == "all" +Requires-Dist: num2words; extra == "all" +Requires-Dist: mistral-common[opencv]>=1.6.3; extra == "all" +Provides-Extra: dev-torch +Requires-Dist: pytest>=7.2.0; extra == "dev-torch" +Requires-Dist: pytest-asyncio; extra == "dev-torch" +Requires-Dist: pytest-rich; extra == "dev-torch" +Requires-Dist: pytest-xdist; extra == "dev-torch" +Requires-Dist: pytest-order; extra == "dev-torch" +Requires-Dist: pytest-rerunfailures; extra == "dev-torch" +Requires-Dist: timeout-decorator; extra == "dev-torch" +Requires-Dist: parameterized; extra == "dev-torch" +Requires-Dist: psutil; extra == "dev-torch" +Requires-Dist: datasets!=2.5.0; extra == "dev-torch" +Requires-Dist: dill<0.3.5; extra == "dev-torch" +Requires-Dist: evaluate>=0.2.0; extra == "dev-torch" +Requires-Dist: pytest-timeout; extra == "dev-torch" +Requires-Dist: ruff==0.11.2; extra == "dev-torch" +Requires-Dist: rouge-score!=0.0.7,!=0.0.8,!=0.1,!=0.1.1; extra == "dev-torch" +Requires-Dist: nltk<=3.8.1; extra == "dev-torch" +Requires-Dist: GitPython<3.1.19; extra == "dev-torch" +Requires-Dist: sacremoses; extra == "dev-torch" +Requires-Dist: rjieba; extra == "dev-torch" +Requires-Dist: beautifulsoup4; extra == "dev-torch" +Requires-Dist: tensorboard; extra == "dev-torch" +Requires-Dist: pydantic>=2; extra == "dev-torch" +Requires-Dist: sentencepiece!=0.1.92,>=0.1.91; extra == "dev-torch" +Requires-Dist: sacrebleu<2.0.0,>=1.4.12; extra == "dev-torch" +Requires-Dist: faiss-cpu; extra == "dev-torch" +Requires-Dist: datasets!=2.5.0; extra == "dev-torch" +Requires-Dist: cookiecutter==1.7.3; extra == "dev-torch" +Requires-Dist: mistral-common[opencv]>=1.6.3; extra == "dev-torch" +Requires-Dist: torch>=2.1; extra == "dev-torch" +Requires-Dist: accelerate>=0.26.0; extra == "dev-torch" +Requires-Dist: sentencepiece!=0.1.92,>=0.1.91; extra == "dev-torch" +Requires-Dist: protobuf; extra == "dev-torch" +Requires-Dist: tokenizers<0.22,>=0.21; extra == "dev-torch" +Requires-Dist: torchaudio; extra == "dev-torch" +Requires-Dist: librosa; extra == "dev-torch" +Requires-Dist: pyctcdecode>=0.4.0; extra == "dev-torch" +Requires-Dist: phonemizer; extra == "dev-torch" +Requires-Dist: kenlm; extra == "dev-torch" +Requires-Dist: Pillow<=15.0,>=10.0.1; extra == "dev-torch" +Requires-Dist: kernels<0.7,>=0.6.1; extra == "dev-torch" +Requires-Dist: optuna; extra == "dev-torch" +Requires-Dist: ray[tune]>=2.7.0; extra == "dev-torch" +Requires-Dist: sigopt; extra == "dev-torch" +Requires-Dist: timm<=1.0.11; extra == "dev-torch" +Requires-Dist: torchvision; extra == "dev-torch" +Requires-Dist: Pillow<=15.0,>=10.0.1; extra == "dev-torch" +Requires-Dist: codecarbon>=2.8.1; extra == "dev-torch" +Requires-Dist: datasets!=2.5.0; extra == "dev-torch" +Requires-Dist: ruff==0.11.2; extra == "dev-torch" +Requires-Dist: GitPython<3.1.19; extra == "dev-torch" +Requires-Dist: urllib3<2.0.0; extra == "dev-torch" +Requires-Dist: libcst; extra == "dev-torch" +Requires-Dist: rich; extra == "dev-torch" +Requires-Dist: pandas<2.3.0; extra == "dev-torch" +Requires-Dist: fugashi>=1.0; extra == "dev-torch" +Requires-Dist: ipadic<2.0,>=1.0.0; extra == "dev-torch" +Requires-Dist: unidic_lite>=1.0.7; extra == "dev-torch" +Requires-Dist: unidic>=1.0.2; extra == "dev-torch" +Requires-Dist: sudachipy>=0.6.6; extra == "dev-torch" +Requires-Dist: sudachidict_core>=20220729; extra == "dev-torch" +Requires-Dist: rhoknp<1.3.1,>=1.1.0; extra == "dev-torch" +Requires-Dist: scikit-learn; extra == "dev-torch" +Requires-Dist: cookiecutter==1.7.3; extra == "dev-torch" +Requires-Dist: onnxruntime>=1.4.0; extra == "dev-torch" +Requires-Dist: onnxruntime-tools>=1.4.2; extra == "dev-torch" +Requires-Dist: num2words; extra == "dev-torch" +Provides-Extra: dev-tensorflow +Requires-Dist: pytest>=7.2.0; extra == "dev-tensorflow" +Requires-Dist: pytest-asyncio; extra == "dev-tensorflow" +Requires-Dist: pytest-rich; extra == "dev-tensorflow" +Requires-Dist: pytest-xdist; extra == "dev-tensorflow" +Requires-Dist: pytest-order; extra == "dev-tensorflow" +Requires-Dist: pytest-rerunfailures; extra == "dev-tensorflow" +Requires-Dist: timeout-decorator; extra == "dev-tensorflow" +Requires-Dist: parameterized; extra == "dev-tensorflow" +Requires-Dist: psutil; extra == "dev-tensorflow" +Requires-Dist: datasets!=2.5.0; extra == "dev-tensorflow" +Requires-Dist: dill<0.3.5; extra == "dev-tensorflow" +Requires-Dist: evaluate>=0.2.0; extra == "dev-tensorflow" +Requires-Dist: pytest-timeout; extra == "dev-tensorflow" +Requires-Dist: ruff==0.11.2; extra == "dev-tensorflow" +Requires-Dist: rouge-score!=0.0.7,!=0.0.8,!=0.1,!=0.1.1; extra == "dev-tensorflow" +Requires-Dist: nltk<=3.8.1; extra == "dev-tensorflow" +Requires-Dist: GitPython<3.1.19; extra == "dev-tensorflow" +Requires-Dist: sacremoses; extra == "dev-tensorflow" +Requires-Dist: rjieba; extra == "dev-tensorflow" +Requires-Dist: beautifulsoup4; extra == "dev-tensorflow" +Requires-Dist: tensorboard; extra == "dev-tensorflow" +Requires-Dist: pydantic>=2; extra == "dev-tensorflow" +Requires-Dist: sentencepiece!=0.1.92,>=0.1.91; extra == "dev-tensorflow" +Requires-Dist: sacrebleu<2.0.0,>=1.4.12; extra == "dev-tensorflow" +Requires-Dist: faiss-cpu; extra == "dev-tensorflow" +Requires-Dist: datasets!=2.5.0; extra == "dev-tensorflow" +Requires-Dist: cookiecutter==1.7.3; extra == "dev-tensorflow" +Requires-Dist: mistral-common[opencv]>=1.6.3; extra == "dev-tensorflow" +Requires-Dist: tensorflow<2.16,>2.9; extra == "dev-tensorflow" +Requires-Dist: onnxconverter-common; extra == "dev-tensorflow" +Requires-Dist: tf2onnx; extra == "dev-tensorflow" +Requires-Dist: tensorflow-text<2.16; extra == "dev-tensorflow" +Requires-Dist: keras-nlp<0.14.0,>=0.3.1; extra == "dev-tensorflow" +Requires-Dist: sentencepiece!=0.1.92,>=0.1.91; extra == "dev-tensorflow" +Requires-Dist: protobuf; extra == "dev-tensorflow" +Requires-Dist: tokenizers<0.22,>=0.21; extra == "dev-tensorflow" +Requires-Dist: Pillow<=15.0,>=10.0.1; extra == "dev-tensorflow" +Requires-Dist: datasets!=2.5.0; extra == "dev-tensorflow" +Requires-Dist: ruff==0.11.2; extra == "dev-tensorflow" +Requires-Dist: GitPython<3.1.19; extra == "dev-tensorflow" +Requires-Dist: urllib3<2.0.0; extra == "dev-tensorflow" +Requires-Dist: libcst; extra == "dev-tensorflow" +Requires-Dist: rich; extra == "dev-tensorflow" +Requires-Dist: pandas<2.3.0; extra == "dev-tensorflow" +Requires-Dist: scikit-learn; extra == "dev-tensorflow" +Requires-Dist: cookiecutter==1.7.3; extra == "dev-tensorflow" +Requires-Dist: onnxconverter-common; extra == "dev-tensorflow" +Requires-Dist: tf2onnx; extra == "dev-tensorflow" +Requires-Dist: onnxruntime>=1.4.0; extra == "dev-tensorflow" +Requires-Dist: onnxruntime-tools>=1.4.2; extra == "dev-tensorflow" +Requires-Dist: librosa; extra == "dev-tensorflow" +Requires-Dist: pyctcdecode>=0.4.0; extra == "dev-tensorflow" +Requires-Dist: phonemizer; extra == "dev-tensorflow" +Requires-Dist: kenlm; extra == "dev-tensorflow" +Provides-Extra: dev +Requires-Dist: tensorflow<2.16,>2.9; extra == "dev" +Requires-Dist: onnxconverter-common; extra == "dev" +Requires-Dist: tf2onnx; extra == "dev" +Requires-Dist: tensorflow-text<2.16; extra == "dev" +Requires-Dist: keras-nlp<0.14.0,>=0.3.1; extra == "dev" +Requires-Dist: torch>=2.1; extra == "dev" +Requires-Dist: accelerate>=0.26.0; extra == "dev" +Requires-Dist: jax<=0.4.13,>=0.4.1; extra == "dev" +Requires-Dist: jaxlib<=0.4.13,>=0.4.1; extra == "dev" +Requires-Dist: flax<=0.7.0,>=0.4.1; extra == "dev" +Requires-Dist: optax<=0.1.4,>=0.0.8; extra == "dev" +Requires-Dist: scipy<1.13.0; extra == "dev" +Requires-Dist: sentencepiece!=0.1.92,>=0.1.91; extra == "dev" +Requires-Dist: protobuf; extra == "dev" +Requires-Dist: tokenizers<0.22,>=0.21; extra == "dev" +Requires-Dist: torchaudio; extra == "dev" +Requires-Dist: librosa; extra == "dev" +Requires-Dist: pyctcdecode>=0.4.0; extra == "dev" +Requires-Dist: phonemizer; extra == "dev" +Requires-Dist: kenlm; extra == "dev" +Requires-Dist: Pillow<=15.0,>=10.0.1; extra == "dev" +Requires-Dist: kernels<0.7,>=0.6.1; extra == "dev" +Requires-Dist: optuna; extra == "dev" +Requires-Dist: ray[tune]>=2.7.0; extra == "dev" +Requires-Dist: sigopt; extra == "dev" +Requires-Dist: timm<=1.0.11; extra == "dev" +Requires-Dist: torchvision; extra == "dev" +Requires-Dist: Pillow<=15.0,>=10.0.1; extra == "dev" +Requires-Dist: codecarbon>=2.8.1; extra == "dev" +Requires-Dist: accelerate>=0.26.0; extra == "dev" +Requires-Dist: av; extra == "dev" +Requires-Dist: num2words; extra == "dev" +Requires-Dist: mistral-common[opencv]>=1.6.3; extra == "dev" +Requires-Dist: pytest>=7.2.0; extra == "dev" +Requires-Dist: pytest-asyncio; extra == "dev" +Requires-Dist: pytest-rich; extra == "dev" +Requires-Dist: pytest-xdist; extra == "dev" +Requires-Dist: pytest-order; extra == "dev" +Requires-Dist: pytest-rerunfailures; extra == "dev" +Requires-Dist: timeout-decorator; extra == "dev" +Requires-Dist: parameterized; extra == "dev" +Requires-Dist: psutil; extra == "dev" +Requires-Dist: datasets!=2.5.0; extra == "dev" +Requires-Dist: dill<0.3.5; extra == "dev" +Requires-Dist: evaluate>=0.2.0; extra == "dev" +Requires-Dist: pytest-timeout; extra == "dev" +Requires-Dist: ruff==0.11.2; extra == "dev" +Requires-Dist: rouge-score!=0.0.7,!=0.0.8,!=0.1,!=0.1.1; extra == "dev" +Requires-Dist: nltk<=3.8.1; extra == "dev" +Requires-Dist: GitPython<3.1.19; extra == "dev" +Requires-Dist: sacremoses; extra == "dev" +Requires-Dist: rjieba; extra == "dev" +Requires-Dist: beautifulsoup4; extra == "dev" +Requires-Dist: tensorboard; extra == "dev" +Requires-Dist: pydantic>=2; extra == "dev" +Requires-Dist: sentencepiece!=0.1.92,>=0.1.91; extra == "dev" +Requires-Dist: sacrebleu<2.0.0,>=1.4.12; extra == "dev" +Requires-Dist: faiss-cpu; extra == "dev" +Requires-Dist: datasets!=2.5.0; extra == "dev" +Requires-Dist: cookiecutter==1.7.3; extra == "dev" +Requires-Dist: mistral-common[opencv]>=1.6.3; extra == "dev" +Requires-Dist: datasets!=2.5.0; extra == "dev" +Requires-Dist: ruff==0.11.2; extra == "dev" +Requires-Dist: GitPython<3.1.19; extra == "dev" +Requires-Dist: urllib3<2.0.0; extra == "dev" +Requires-Dist: libcst; extra == "dev" +Requires-Dist: rich; extra == "dev" +Requires-Dist: pandas<2.3.0; extra == "dev" +Requires-Dist: fugashi>=1.0; extra == "dev" +Requires-Dist: ipadic<2.0,>=1.0.0; extra == "dev" +Requires-Dist: unidic_lite>=1.0.7; extra == "dev" +Requires-Dist: unidic>=1.0.2; extra == "dev" +Requires-Dist: sudachipy>=0.6.6; extra == "dev" +Requires-Dist: sudachidict_core>=20220729; extra == "dev" +Requires-Dist: rhoknp<1.3.1,>=1.1.0; extra == "dev" +Requires-Dist: scikit-learn; extra == "dev" +Requires-Dist: cookiecutter==1.7.3; extra == "dev" +Provides-Extra: torchhub +Requires-Dist: filelock; extra == "torchhub" +Requires-Dist: huggingface-hub<1.0,>=0.30.0; extra == "torchhub" +Requires-Dist: importlib_metadata; extra == "torchhub" +Requires-Dist: numpy>=1.17; extra == "torchhub" +Requires-Dist: packaging>=20.0; extra == "torchhub" +Requires-Dist: protobuf; extra == "torchhub" +Requires-Dist: regex!=2019.12.17; extra == "torchhub" +Requires-Dist: requests; extra == "torchhub" +Requires-Dist: sentencepiece!=0.1.92,>=0.1.91; extra == "torchhub" +Requires-Dist: torch>=2.1; extra == "torchhub" +Requires-Dist: tokenizers<0.22,>=0.21; extra == "torchhub" +Requires-Dist: tqdm>=4.27; extra == "torchhub" +Provides-Extra: benchmark +Requires-Dist: optimum-benchmark>=0.3.0; extra == "benchmark" +Provides-Extra: open-telemetry +Requires-Dist: opentelemetry-api; extra == "open-telemetry" +Requires-Dist: opentelemetry-exporter-otlp; extra == "open-telemetry" +Requires-Dist: opentelemetry-sdk; extra == "open-telemetry" +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: description-content-type +Dynamic: home-page +Dynamic: keywords +Dynamic: license +Dynamic: license-file +Dynamic: provides-extra +Dynamic: requires-dist +Dynamic: requires-python +Dynamic: summary + + + +

+ + + + Hugging Face Transformers Library + +
+
+

+ +

+ Checkpoints on Hub + Build + GitHub + Documentation + GitHub release + Contributor Covenant + DOI +

+ +

+

+ English | + 简体中文 | + 繁體中文 | + 한국어 | + Español | + 日本語 | + हिन्दी | + Русский | + Рortuguês | + తెలుగు | + Français | + Deutsch | + Tiếng Việt | + العربية | + اردو | +

+

+ +

+

State-of-the-art pretrained models for inference and training

+

+ +

+ +

+ + +Transformers acts as the model-definition framework for state-of-the-art machine learning models in text, computer +vision, audio, video, and multimodal model, for both inference and training. + +It centralizes the model definition so that this definition is agreed upon across the ecosystem. `transformers` is the +pivot across frameworks: if a model definition is supported, it will be compatible with the majority of training +frameworks (Axolotl, Unsloth, DeepSpeed, FSDP, PyTorch-Lightning, ...), inference engines (vLLM, SGLang, TGI, ...), +and adjacent modeling libraries (llama.cpp, mlx, ...) which leverage the model definition from `transformers`. + +We pledge to help support new state-of-the-art models and democratize their usage by having their model definition be +simple, customizable, and efficient. + +There are over 1M+ Transformers [model checkpoints](https://huggingface.co/models?library=transformers&sort=trending) on the [Hugging Face Hub](https://huggingface.com/models) you can use. + +Explore the [Hub](https://huggingface.com/) today to find a model and use Transformers to help you get started right away. + +## Installation + +Transformers works with Python 3.9+ [PyTorch](https://pytorch.org/get-started/locally/) 2.1+, [TensorFlow](https://www.tensorflow.org/install/pip) 2.6+, and [Flax](https://flax.readthedocs.io/en/latest/) 0.4.1+. + +Create and activate a virtual environment with [venv](https://docs.python.org/3/library/venv.html) or [uv](https://docs.astral.sh/uv/), a fast Rust-based Python package and project manager. + +```py +# venv +python -m venv .my-env +source .my-env/bin/activate +# uv +uv venv .my-env +source .my-env/bin/activate +``` + +Install Transformers in your virtual environment. + +```py +# pip +pip install "transformers[torch]" + +# uv +uv pip install "transformers[torch]" +``` + +Install Transformers from source if you want the latest changes in the library or are interested in contributing. However, the *latest* version may not be stable. Feel free to open an [issue](https://github.com/huggingface/transformers/issues) if you encounter an error. + +```shell +git clone https://github.com/huggingface/transformers.git +cd transformers + +# pip +pip install .[torch] + +# uv +uv pip install .[torch] +``` + +## Quickstart + +Get started with Transformers right away with the [Pipeline](https://huggingface.co/docs/transformers/pipeline_tutorial) API. The `Pipeline` is a high-level inference class that supports text, audio, vision, and multimodal tasks. It handles preprocessing the input and returns the appropriate output. + +Instantiate a pipeline and specify model to use for text generation. The model is downloaded and cached so you can easily reuse it again. Finally, pass some text to prompt the model. + +```py +from transformers import pipeline + +pipeline = pipeline(task="text-generation", model="Qwen/Qwen2.5-1.5B") +pipeline("the secret to baking a really good cake is ") +[{'generated_text': 'the secret to baking a really good cake is 1) to use the right ingredients and 2) to follow the recipe exactly. the recipe for the cake is as follows: 1 cup of sugar, 1 cup of flour, 1 cup of milk, 1 cup of butter, 1 cup of eggs, 1 cup of chocolate chips. if you want to make 2 cakes, how much sugar do you need? To make 2 cakes, you will need 2 cups of sugar.'}] +``` + +To chat with a model, the usage pattern is the same. The only difference is you need to construct a chat history (the input to `Pipeline`) between you and the system. + +> [!TIP] +> You can also chat with a model directly from the command line. +> ```shell +> transformers chat Qwen/Qwen2.5-0.5B-Instruct +> ``` + +```py +import torch +from transformers import pipeline + +chat = [ + {"role": "system", "content": "You are a sassy, wise-cracking robot as imagined by Hollywood circa 1986."}, + {"role": "user", "content": "Hey, can you tell me any fun things to do in New York?"} +] + +pipeline = pipeline(task="text-generation", model="meta-llama/Meta-Llama-3-8B-Instruct", torch_dtype=torch.bfloat16, device_map="auto") +response = pipeline(chat, max_new_tokens=512) +print(response[0]["generated_text"][-1]["content"]) +``` + +Expand the examples below to see how `Pipeline` works for different modalities and tasks. + +
+Automatic speech recognition + +```py +from transformers import pipeline + +pipeline = pipeline(task="automatic-speech-recognition", model="openai/whisper-large-v3") +pipeline("https://huggingface.co/datasets/Narsil/asr_dummy/resolve/main/mlk.flac") +{'text': ' I have a dream that one day this nation will rise up and live out the true meaning of its creed.'} +``` + +
+ +
+Image classification + +

+ +

+ +```py +from transformers import pipeline + +pipeline = pipeline(task="image-classification", model="facebook/dinov2-small-imagenet1k-1-layer") +pipeline("https://huggingface.co/datasets/Narsil/image_dummy/raw/main/parrots.png") +[{'label': 'macaw', 'score': 0.997848391532898}, + {'label': 'sulphur-crested cockatoo, Kakatoe galerita, Cacatua galerita', + 'score': 0.0016551691805943847}, + {'label': 'lorikeet', 'score': 0.00018523589824326336}, + {'label': 'African grey, African gray, Psittacus erithacus', + 'score': 7.85409429227002e-05}, + {'label': 'quail', 'score': 5.502637941390276e-05}] +``` + +
+ +
+Visual question answering + + +

+ +

+ +```py +from transformers import pipeline + +pipeline = pipeline(task="visual-question-answering", model="Salesforce/blip-vqa-base") +pipeline( + image="https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/tasks/idefics-few-shot.jpg", + question="What is in the image?", +) +[{'answer': 'statue of liberty'}] +``` + +
+ +## Why should I use Transformers? + +1. Easy-to-use state-of-the-art models: + - High performance on natural language understanding & generation, computer vision, audio, video, and multimodal tasks. + - Low barrier to entry for researchers, engineers, and developers. + - Few user-facing abstractions with just three classes to learn. + - A unified API for using all our pretrained models. + +1. Lower compute costs, smaller carbon footprint: + - Share trained models instead of training from scratch. + - Reduce compute time and production costs. + - Dozens of model architectures with 1M+ pretrained checkpoints across all modalities. + +1. Choose the right framework for every part of a models lifetime: + - Train state-of-the-art models in 3 lines of code. + - Move a single model between PyTorch/JAX/TF2.0 frameworks at will. + - Pick the right framework for training, evaluation, and production. + +1. Easily customize a model or an example to your needs: + - We provide examples for each architecture to reproduce the results published by its original authors. + - Model internals are exposed as consistently as possible. + - Model files can be used independently of the library for quick experiments. + + + Hugging Face Enterprise Hub +
+ +## Why shouldn't I use Transformers? + +- This library is not a modular toolbox of building blocks for neural nets. The code in the model files is not refactored with additional abstractions on purpose, so that researchers can quickly iterate on each of the models without diving into additional abstractions/files. +- The training API is optimized to work with PyTorch models provided by Transformers. For generic machine learning loops, you should use another library like [Accelerate](https://huggingface.co/docs/accelerate). +- The [example scripts]((https://github.com/huggingface/transformers/tree/main/examples)) are only *examples*. They may not necessarily work out-of-the-box on your specific use case and you'll need to adapt the code for it to work. + +## 100 projects using Transformers + +Transformers is more than a toolkit to use pretrained models, it's a community of projects built around it and the +Hugging Face Hub. We want Transformers to enable developers, researchers, students, professors, engineers, and anyone +else to build their dream projects. + +In order to celebrate Transformers 100,000 stars, we wanted to put the spotlight on the +community with the [awesome-transformers](./awesome-transformers.md) page which lists 100 +incredible projects built with Transformers. + +If you own or use a project that you believe should be part of the list, please open a PR to add it! + +## Example models + +You can test most of our models directly on their [Hub model pages](https://huggingface.co/models). + +Expand each modality below to see a few example models for various use cases. + +
+Audio + +- Audio classification with [Whisper](https://huggingface.co/openai/whisper-large-v3-turbo) +- Automatic speech recognition with [Moonshine](https://huggingface.co/UsefulSensors/moonshine) +- Keyword spotting with [Wav2Vec2](https://huggingface.co/superb/wav2vec2-base-superb-ks) +- Speech to speech generation with [Moshi](https://huggingface.co/kyutai/moshiko-pytorch-bf16) +- Text to audio with [MusicGen](https://huggingface.co/facebook/musicgen-large) +- Text to speech with [Bark](https://huggingface.co/suno/bark) + +
+ +
+Computer vision + +- Automatic mask generation with [SAM](https://huggingface.co/facebook/sam-vit-base) +- Depth estimation with [DepthPro](https://huggingface.co/apple/DepthPro-hf) +- Image classification with [DINO v2](https://huggingface.co/facebook/dinov2-base) +- Keypoint detection with [SuperGlue](https://huggingface.co/magic-leap-community/superglue_outdoor) +- Keypoint matching with [SuperGlue](https://huggingface.co/magic-leap-community/superglue) +- Object detection with [RT-DETRv2](https://huggingface.co/PekingU/rtdetr_v2_r50vd) +- Pose Estimation with [VitPose](https://huggingface.co/usyd-community/vitpose-base-simple) +- Universal segmentation with [OneFormer](https://huggingface.co/shi-labs/oneformer_ade20k_swin_large) +- Video classification with [VideoMAE](https://huggingface.co/MCG-NJU/videomae-large) + +
+ +
+Multimodal + +- Audio or text to text with [Qwen2-Audio](https://huggingface.co/Qwen/Qwen2-Audio-7B) +- Document question answering with [LayoutLMv3](https://huggingface.co/microsoft/layoutlmv3-base) +- Image or text to text with [Qwen-VL](https://huggingface.co/Qwen/Qwen2.5-VL-3B-Instruct) +- Image captioning [BLIP-2](https://huggingface.co/Salesforce/blip2-opt-2.7b) +- OCR-based document understanding with [GOT-OCR2](https://huggingface.co/stepfun-ai/GOT-OCR-2.0-hf) +- Table question answering with [TAPAS](https://huggingface.co/google/tapas-base) +- Unified multimodal understanding and generation with [Emu3](https://huggingface.co/BAAI/Emu3-Gen) +- Vision to text with [Llava-OneVision](https://huggingface.co/llava-hf/llava-onevision-qwen2-0.5b-ov-hf) +- Visual question answering with [Llava](https://huggingface.co/llava-hf/llava-1.5-7b-hf) +- Visual referring expression segmentation with [Kosmos-2](https://huggingface.co/microsoft/kosmos-2-patch14-224) + +
+ +
+NLP + +- Masked word completion with [ModernBERT](https://huggingface.co/answerdotai/ModernBERT-base) +- Named entity recognition with [Gemma](https://huggingface.co/google/gemma-2-2b) +- Question answering with [Mixtral](https://huggingface.co/mistralai/Mixtral-8x7B-v0.1) +- Summarization with [BART](https://huggingface.co/facebook/bart-large-cnn) +- Translation with [T5](https://huggingface.co/google-t5/t5-base) +- Text generation with [Llama](https://huggingface.co/meta-llama/Llama-3.2-1B) +- Text classification with [Qwen](https://huggingface.co/Qwen/Qwen2.5-0.5B) + +
+ +## Citation + +We now have a [paper](https://www.aclweb.org/anthology/2020.emnlp-demos.6/) you can cite for the 🤗 Transformers library: +```bibtex +@inproceedings{wolf-etal-2020-transformers, + title = "Transformers: State-of-the-Art Natural Language Processing", + author = "Thomas Wolf and Lysandre Debut and Victor Sanh and Julien Chaumond and Clement Delangue and Anthony Moi and Pierric Cistac and Tim Rault and Rémi Louf and Morgan Funtowicz and Joe Davison and Sam Shleifer and Patrick von Platen and Clara Ma and Yacine Jernite and Julien Plu and Canwen Xu and Teven Le Scao and Sylvain Gugger and Mariama Drame and Quentin Lhoest and Alexander M. Rush", + booktitle = "Proceedings of the 2020 Conference on Empirical Methods in Natural Language Processing: System Demonstrations", + month = oct, + year = "2020", + address = "Online", + publisher = "Association for Computational Linguistics", + url = "https://www.aclweb.org/anthology/2020.emnlp-demos.6", + pages = "38--45" +} +``` diff --git a/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..ac77fe04d0319c9bacf1f5b4bf8a49ccff1206e2 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/RECORD @@ -0,0 +1,13 @@ +../../../bin/transformers,sha256=jDjvarUxZzNsmAVRPUTxO9_9UeTIKO0nho-bzFUE2Jc,354 +../../../bin/transformers-cli,sha256=giKhkrZO-0sA6914bc6rjIZ2kWDwVcKS7ujCKESLc3M,362 +__editable__.transformers-4.54.0.dev0.pth,sha256=Egz_RV-Z_UBgyDvPK-GYKdvSFj--lhg0qIoq8kx2VWE,50 +transformers-4.54.0.dev0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +transformers-4.54.0.dev0.dist-info/METADATA,sha256=XS8JSsC2DTYncU6GeIBDt2mnMw-dOu8LIBKD-H8Oam8,41619 +transformers-4.54.0.dev0.dist-info/RECORD,, +transformers-4.54.0.dev0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +transformers-4.54.0.dev0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91 +transformers-4.54.0.dev0.dist-info/direct_url.json,sha256=nK5LPlgvo8C9wHQUqSiObq8VZAOST3hszyDB3eQfftU,91 +transformers-4.54.0.dev0.dist-info/entry_points.txt,sha256=Zra3dVQyt6Q3fU_suoD3gF81JV3WeV8gH66vzoev408,144 +transformers-4.54.0.dev0.dist-info/licenses/LICENSE,sha256=d_1HEN757DwPYiWADgI18VpCWr1KiwNVkSf814JhIEk,11418 +transformers-4.54.0.dev0.dist-info/top_level.txt,sha256=GLBaeTo_CSdhnHvbxQ0kzpEHdlLuA_33foIogaWxntI,13 +transformers-4.54.0.dev0.dist-info/uv_cache.json,sha256=-6cp7Rg7w7Ru14xBRfE_CRzphx8JU9PrU-2G7wnYdUM,194 diff --git a/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..e7fa31b6f3f78deb1022c1f7927f07d4d16da822 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/direct_url.json b/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/direct_url.json new file mode 100644 index 0000000000000000000000000000000000000000..78e07bba43708205e8221df740255717692117cb --- /dev/null +++ b/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/direct_url.json @@ -0,0 +1 @@ +{"url":"file:///home/ubuntu/lyl/QwenIllustrious/transformers","dir_info":{"editable":true}} \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/entry_points.txt b/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/entry_points.txt new file mode 100644 index 0000000000000000000000000000000000000000..47f047547c476346d81519e9d380af1a6be26155 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +transformers = transformers.commands.transformers_cli:main +transformers-cli = transformers.commands.transformers_cli:main_cli diff --git a/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..976a2b1f3998279c10c413279a095be86bf69167 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/top_level.txt @@ -0,0 +1 @@ +transformers diff --git a/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/uv_cache.json b/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/uv_cache.json new file mode 100644 index 0000000000000000000000000000000000000000..a1e0640619511163cc33ff74a14b0040eb13e4d7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/transformers-4.54.0.dev0.dist-info/uv_cache.json @@ -0,0 +1 @@ +{"timestamp":{"secs_since_epoch":1752469560,"nanos_since_epoch":530678355},"commit":null,"tags":null,"env":{},"directories":{"src":{"secs_since_epoch":1752469560,"nanos_since_epoch":530678355}}} \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/triton/__init__.py b/.venv/lib/python3.12/site-packages/triton/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..63ee791a2ee90a885d71295d558368fd0229c668 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/triton/__init__.py @@ -0,0 +1,76 @@ +"""isort:skip_file""" +__version__ = '3.4.0' + +# --------------------------------------- +# Note: import order is significant here. + +# submodules +from .runtime import ( + autotune, + Config, + heuristics, + JITFunction, + KernelInterface, + reinterpret, + TensorWrapper, + OutOfResources, + InterpreterError, + MockTensor, +) +from .runtime.jit import jit +from .compiler import compile, CompilationError +from .errors import TritonError +from .runtime._allocation import set_allocator + +from . import language +from . import testing +from . import tools + +must_use_result = language.core.must_use_result + +__all__ = [ + "autotune", + "cdiv", + "CompilationError", + "compile", + "Config", + "heuristics", + "InterpreterError", + "jit", + "JITFunction", + "KernelInterface", + "language", + "MockTensor", + "must_use_result", + "next_power_of_2", + "OutOfResources", + "reinterpret", + "runtime", + "set_allocator", + "TensorWrapper", + "TritonError", + "testing", + "tools", +] + +# ------------------------------------- +# misc. utilities that don't fit well +# into any specific module +# ------------------------------------- + + +def cdiv(x: int, y: int): + return (x + y - 1) // y + + +def next_power_of_2(n: int): + """Return the smallest power of 2 greater than or equal to n""" + n -= 1 + n |= n >> 1 + n |= n >> 2 + n |= n >> 4 + n |= n >> 8 + n |= n >> 16 + n |= n >> 32 + n += 1 + return n diff --git a/.venv/lib/python3.12/site-packages/triton/_filecheck.py b/.venv/lib/python3.12/site-packages/triton/_filecheck.py new file mode 100644 index 0000000000000000000000000000000000000000..2a857d8f14f80b171237469e6a2144e1d6be5f2a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/triton/_filecheck.py @@ -0,0 +1,87 @@ +import os +import inspect +import subprocess +import tempfile + +import triton +from triton.compiler import ASTSource, make_backend +from triton.backends.compiler import GPUTarget +from triton.experimental.gluon._runtime import GluonASTSource +from triton._C.libtriton import ir + +# ===-----------------------------------------------------------------------===# +# filecheck_test +# ===-----------------------------------------------------------------------===# + +# Stub target for testing the frontend. +stub_target = GPUTarget("cuda", 100, 32) +stub_backend = make_backend(stub_target) + +triton_dir = os.path.dirname(__file__) +filecheck_path = os.path.join(triton_dir, "FileCheck") + + +class MatchError(ValueError): + + def __init__(self, message, module_str): + super().__init__(message) + self.module_str = module_str + + def __str__(self): + return f"{super().__str__()}\n{self.module_str}" + + +def run_filecheck(name, module_str, check_template): + with tempfile.TemporaryDirectory() as tempdir: + temp_module = os.path.join(tempdir, "module") + with open(temp_module, "w") as temp: + temp.write(module_str) + + temp_expected = os.path.join(tempdir, "expected") + with open(temp_expected, "w") as temp: + temp.write(check_template) + + try: + subprocess.check_output([filecheck_path, temp_expected, "--input-file", temp_module], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as error: + decoded = error.output.decode('unicode_escape') + raise ValueError(decoded) + + +def run_parser(kernel_fn): + sigkeys = [x.name for x in kernel_fn.params] + sigvals = [f"arg{i}" for i in range(len(sigkeys))] + signature = {k: v for (k, v) in zip(sigkeys, sigvals)} + source_cls = GluonASTSource if kernel_fn.is_gluon() else ASTSource + src = source_cls(fn=kernel_fn, signature=signature) + + context = ir.context() + ir.load_dialects(context) + stub_backend.load_dialects(context) + + extra_options = src.parse_options() + options = stub_backend.parse_options(dict(**extra_options)) + codegen_fns = stub_backend.get_codegen_implementation(options) + module_map = stub_backend.get_module_map() + module = src.make_ir(options, codegen_fns, module_map, context) + assert module.verify() + return module + + +def run_filecheck_test(kernel_fn): + assert isinstance(kernel_fn, triton.runtime.JITFunction) + check_template = inspect.getsource(kernel_fn.fn) + if check_template is None: + raise ValueError("kernel function must have a docstring with FileCheck template") + mlir_module = run_parser(kernel_fn) + + run_filecheck("placeholder", mlir_module.str_nodebug(), check_template) + + +def filecheck_test(fn): + + def test_fn(): + run_filecheck_test(fn) + + return test_fn diff --git a/.venv/lib/python3.12/site-packages/triton/_internal_testing.py b/.venv/lib/python3.12/site-packages/triton/_internal_testing.py new file mode 100644 index 0000000000000000000000000000000000000000..8a2bc944166911233e5932f6d5a3f0efe403648b --- /dev/null +++ b/.venv/lib/python3.12/site-packages/triton/_internal_testing.py @@ -0,0 +1,189 @@ +import os +import re +import numpy as np +import torch +import triton +import triton.language as tl +from triton import knobs +import pytest + +from numpy.random import RandomState +from typing import Optional, Union +from triton.runtime.jit import TensorWrapper, reinterpret, type_canonicalisation_dict + +int_dtypes = ['int8', 'int16', 'int32', 'int64'] +uint_dtypes = ['uint8', 'uint16', 'uint32', 'uint64'] +integral_dtypes = int_dtypes + uint_dtypes +float_dtypes = ['float16', 'float32', 'float64'] +float_dtypes_with_bfloat16 = float_dtypes + ['bfloat16'] +dtypes = integral_dtypes + float_dtypes +dtypes_with_bfloat16 = dtypes + ['bfloat16'] +torch_float8_dtypes = ['float8_e4m3fn', 'float8_e5m2'] +torch_dtypes = ['bool'] + int_dtypes + ['uint8'] + float_dtypes + ['bfloat16'] +tma_dtypes = sorted(set(dtypes_with_bfloat16) - {"int64", "uint64", "float64"}) + + +def is_interpreter(): + return os.environ.get('TRITON_INTERPRET', '0') == '1' + + +def get_current_target(): + if is_interpreter(): + return None + return triton.runtime.driver.active.get_current_target() + + +def is_cuda(): + target = get_current_target() + return False if target is None else target.backend == "cuda" + + +def is_hopper(): + return is_cuda() and torch.cuda.get_device_capability()[0] >= 9 + + +def is_hip(): + target = get_current_target() + return False if target is None else target.backend == "hip" + + +def is_hip_cdna2(): + target = get_current_target() + return target is not None and target.backend == 'hip' and target.arch == 'gfx90a' + + +def is_hip_cdna3(): + target = get_current_target() + return target is not None and target.backend == 'hip' and target.arch == 'gfx942' + + +def is_hip_cdna4(): + target = get_current_target() + return target is not None and target.backend == 'hip' and target.arch == 'gfx950' + + +def is_hip_gfx12(): + target = get_current_target() + print(target.arch) + return target is not None and target.backend == 'hip' and 'gfx12' in target.arch + + +def is_hip_cdna(): + return is_hip_cdna2() or is_hip_cdna3() or is_hip_cdna4() + + +def is_xpu(): + target = get_current_target() + return False if target is None else target.backend == "xpu" + + +def get_arch(): + target = get_current_target() + return "" if target is None else str(target.arch) + + +def numpy_random(shape, dtype_str, rs: Optional[RandomState] = None, low=None, high=None): + """ + Override `rs` if you're calling this function twice and don't want the same + result for both calls. + """ + if isinstance(shape, int): + shape = (shape, ) + if rs is None: + rs = RandomState(seed=17) + if dtype_str in int_dtypes + uint_dtypes: + iinfo = np.iinfo(getattr(np, dtype_str)) + low = iinfo.min if low is None else max(low, iinfo.min) + high = iinfo.max if high is None else min(high, iinfo.max) + dtype = getattr(np, dtype_str) + x = rs.randint(low, high, shape, dtype=dtype) + x[x == 0] = 1 # Workaround. Never return zero so tests of division don't error out. + return x + elif dtype_str and 'float8' in dtype_str: + x = rs.randint(20, 40, shape, dtype=np.int8) + return x + elif dtype_str in float_dtypes: + return rs.normal(0, 1, shape).astype(dtype_str) + elif dtype_str == 'bfloat16': + return (rs.normal(0, 1, shape).astype('float32').view('uint32') & np.uint32(0xffff0000)).view('float32') + elif dtype_str in ['bool', 'int1', 'bool_']: + return rs.normal(0, 1, shape) > 0.0 + else: + raise RuntimeError(f'Unknown dtype {dtype_str}') + + +def to_triton(x: np.ndarray, device, dst_type=None) -> Union[TensorWrapper, torch.Tensor]: + ''' + Note: We need dst_type because the type of x can be different from dst_type. + For example: x is of type `float32`, dst_type is `bfloat16`. + If dst_type is None, we infer dst_type from x. + ''' + t = x.dtype.name + if t in uint_dtypes: + signed_type_name = t.lstrip('u') # e.g. "uint16" -> "int16" + x_signed = x.astype(getattr(np, signed_type_name)) + return reinterpret(torch.tensor(x_signed, device=device), getattr(tl, t)) + else: + if dst_type and 'float8' in dst_type: + return reinterpret(torch.tensor(x, device=device), getattr(tl, dst_type)) + if t == 'float32' and dst_type == 'bfloat16': + return torch.tensor(x, device=device).bfloat16() + return torch.tensor(x, device=device) + + +def str_to_triton_dtype(x: str) -> tl.dtype: + return tl.str_to_ty(type_canonicalisation_dict[x]) + + +def torch_dtype_name(dtype) -> str: + if isinstance(dtype, triton.language.dtype): + return dtype.name + elif isinstance(dtype, torch.dtype): + # 'torch.int64' -> 'int64' + m = re.match(r'^torch\.(\w+)$', str(dtype)) + return m.group(1) + else: + raise TypeError(f'not a triton or torch dtype: {type(dtype)}') + + +def to_numpy(x): + if isinstance(x, TensorWrapper): + return x.base.cpu().numpy().astype(getattr(np, torch_dtype_name(x.dtype))) + elif isinstance(x, torch.Tensor): + if x.dtype is torch.bfloat16: + return x.cpu().float().numpy() + return x.cpu().numpy() + else: + raise ValueError(f"Not a triton-compatible tensor: {x}") + + +def supports_tma(byval_only=False): + if is_interpreter(): + return True + if not is_cuda(): + return False + cuda_version = knobs.nvidia.ptxas.version + min_cuda_version = (12, 0) if byval_only else (12, 3) + cuda_version_tuple = tuple(map(int, cuda_version.split("."))) + assert len(cuda_version_tuple) == 2, cuda_version_tuple + return torch.cuda.get_device_capability()[0] >= 9 and cuda_version_tuple >= min_cuda_version + + +def tma_skip_msg(byval_only=False): + if byval_only: + return "Requires __grid_constant__ TMA support (NVIDIA Hopper or higher, CUDA 12.0 or higher)" + else: + return "Requires advanced TMA support (NVIDIA Hopper or higher, CUDA 12.3 or higher)" + + +requires_tma = pytest.mark.skipif(not supports_tma(), reason=tma_skip_msg()) + + +def default_alloc_fn(size: int, align: int, _): + return torch.empty(size, dtype=torch.int8, device="cuda") + + +def unwrap_tensor(t: Union[torch.Tensor, triton.runtime.jit.TensorWrapper]) -> torch.Tensor: + if isinstance(t, triton.runtime.jit.TensorWrapper): + return t.base + return t diff --git a/.venv/lib/python3.12/site-packages/triton/_utils.py b/.venv/lib/python3.12/site-packages/triton/_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..1da8d692b97cf935a8523e786d8c9828cac1b7e4 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/triton/_utils.py @@ -0,0 +1,124 @@ +from __future__ import annotations + +from functools import reduce +from typing import Any, Callable, TYPE_CHECKING, Union, List, Dict + +if TYPE_CHECKING: + from .language import core + IterableType = Union[list[Any], tuple[Any, ...], core.tuple, core.tuple_type] + ObjPath = tuple[int, ...] + +TRITON_MAX_TENSOR_NUMEL = 1048576 + + +def get_iterable_path(iterable: IterableType, path: ObjPath) -> Any: + return reduce(lambda a, idx: a[idx], path, iterable) # type: ignore[index] + + +def set_iterable_path(iterable: IterableType, path: tuple[int, ...], val: Any): + assert len(path) != 0 + prev = iterable if len(path) == 1 else get_iterable_path(iterable, path[:-1]) + prev[path[-1]] = val # type: ignore[index] + + +def find_paths_if(iterable: Union[IterableType, Any], pred: Callable[[ObjPath, Any], bool]) -> list[ObjPath]: + from .language import core + is_iterable: Callable[[Any], bool] = lambda x: isinstance(x, (list, tuple, core.tuple, core.tuple_type)) + # We need to use dict so that ordering is maintained, while set doesn't guarantee order + ret: dict[ObjPath, None] = {} + + def _impl(path: tuple[int, ...], current: Any): + if is_iterable(current): + for idx, item in enumerate(current): + _impl((*path, idx), item) + elif pred(path, current): + ret[path] = None + + _impl((), iterable) + + return list(ret.keys()) + + +def is_power_of_two(x): + return (x & (x - 1)) == 0 + + +def validate_block_shape(shape: List[int]): + numel = 1 + for i, d in enumerate(shape): + if not isinstance(d, int): + raise TypeError(f"Shape element {i} must have type `constexpr[int]`, got `constexpr[{type(d)}]") + if not is_power_of_two(d): + raise ValueError(f"Shape element {i} must be a power of 2") + numel *= d + + if numel > TRITON_MAX_TENSOR_NUMEL: + raise ValueError(f"numel ({numel}) exceeds triton maximum tensor numel ({TRITON_MAX_TENSOR_NUMEL})") + return numel + + +type_canonicalisation_dict = { + # we canonicalise all bools to be unsigned: + "bool": "u1", + "int1": "u1", + "uint1": "u1", + "i1": "u1", + # floating-point dtypes: + "float8e4nv": "fp8e4nv", + "float8e5": "fp8e5", + "float8e4b15": "fp8e4b15", + "float8_e4m3fn": "fp8e4nv", + "float8e4b8": "fp8e4b8", + "float8_e4m3fnuz": "fp8e4b8", + "float8_e5m2": "fp8e5", + "float8e5b16": "fp8e5b16", + "float8_e5m2fnuz": "fp8e5b16", + "half": "fp16", + "float16": "fp16", + "bfloat16": "bf16", + "float": "fp32", + "float32": "fp32", + "double": "fp64", + "float64": "fp64", + # signed integers: + "int8": "i8", + "int16": "i16", + "int": "i32", + "int32": "i32", + "int64": "i64", + # unsigned integers: + "uint8": "u8", + "uint16": "u16", + "uint32": "u32", + "uint64": "u64", + "void": "void", +} + +for v in list(type_canonicalisation_dict.values()): + type_canonicalisation_dict[v] = v + + +def canonicalize_dtype(dtype): + dtype_str = str(dtype).split(".")[-1] + return type_canonicalisation_dict[dtype_str] + + +BITWIDTH_DICT: Dict[str, int] = { + **{f"u{n}": n + for n in (1, 8, 16, 32, 64)}, + **{f"i{n}": n + for n in (1, 8, 16, 32, 64)}, + **{f"fp{n}": n + for n in (16, 32, 64)}, + **{f"fp8{suffix}": 8 + for suffix in ("e4nv", "e4b15", "e4b8", "e5", "e5b16")}, + "bf16": 16, + "void": 0, +} + +for k, v in type_canonicalisation_dict.items(): + BITWIDTH_DICT[k] = BITWIDTH_DICT[v] + + +def get_primitive_bitwidth(dtype: str) -> int: + return BITWIDTH_DICT[dtype] diff --git a/.venv/lib/python3.12/site-packages/triton/errors.py b/.venv/lib/python3.12/site-packages/triton/errors.py new file mode 100644 index 0000000000000000000000000000000000000000..3a0a863553b9d62ccb60d0090ee6d66f8b9e3b79 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/triton/errors.py @@ -0,0 +1,5 @@ +"""Base class for all errors raised by Triton""" + + +class TritonError(Exception): + ... diff --git a/.venv/lib/python3.12/site-packages/triton/knobs.py b/.venv/lib/python3.12/site-packages/triton/knobs.py new file mode 100644 index 0000000000000000000000000000000000000000..30804b170cbfe1323d71faa47cd08066a59b3049 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/triton/knobs.py @@ -0,0 +1,465 @@ +from __future__ import annotations + +import importlib +import os +import re +import subprocess +import sysconfig + +from dataclasses import dataclass +from contextlib import contextmanager +from typing import cast, Any, Callable, Generator, Generic, Optional, Protocol, Type, TypeVar, TypedDict, TYPE_CHECKING, Union + +if TYPE_CHECKING: + from .runtime.cache import CacheManager, RemoteCacheBackend + from .runtime.jit import JitFunctionInfo, KernelParam + from .compiler.compiler import ASTSource, LazyDict, IRSource + + +class Env: + pass + + +env = Env() + +propagate_env: bool = True + + +def getenv(key: str) -> Optional[str]: + res = os.getenv(key) + return res.strip() if res is not None else res + + +def setenv(key: str, value: Optional[str]) -> None: + if not propagate_env: + return + + if value is not None: + os.environ[key] = value + elif key in os.environ: + del os.environ[key] + + +def toenv(val: Any) -> Union[None, tuple[Optional[str]]]: + if val is None: + return (None, ) + + t = type(val) + if t is bool: + return ("1" if val else "0", ) + + if t is str: + return (val, ) + + if t is int: + return (str(val), ) + + return None + + +# There's an asymmetry here so that e.g. env_nvidia_tool can be specified with a +# a string but return an NvidiaTool. +SetType = TypeVar("SetType") +GetType = TypeVar("GetType") + + +class env_base(Generic[SetType, GetType]): + + def __init__(self, key: str, default: Union[SetType, Callable[[], SetType]]) -> None: + self.key = key + self.default: Callable[[], SetType] = default if callable(default) else lambda: default + + def __set_name__(self, objclass: Type[object], name: str) -> None: + self.name = name + + def __get__(self, obj: Optional[object], objclass: Optional[Type[object]]) -> GetType: + if obj is None: + raise AttributeError(f"Cannot access {type(self)} on non-instance") + + if self.name in obj.__dict__: + return self.transform(obj.__dict__[self.name]) + else: + return self.get() + + @property + def env_val(self) -> str | None: + return getenv(self.key) + + def get(self) -> GetType: + env = self.env_val + return self.transform(self.default() if env is None else self.from_env(env)) + + def __set__(self, obj: object, value: Union[SetType, Env]) -> None: + if isinstance(value, Env): + obj.__dict__.pop(self.name, None) + else: + obj.__dict__[self.name] = value + if env_val := toenv(value): + setenv(self.key, env_val[0]) + + def __delete__(self, obj: object) -> None: + obj.__dict__.pop(self.name, None) + + def transform(self, val: SetType) -> GetType: + # See comment about GetType/SetType in their definition above. Only needed + # if GetType != SetType. + return cast(GetType, val) + + def from_env(self, val: str) -> SetType: + raise NotImplementedError() + + +class env_str(env_base[str, str]): + + def from_env(self, val: str) -> str: + return val + + +class env_bool(env_base[bool, bool]): + + def __init__(self, key: str, default: Union[bool, Callable[[], bool]] = False) -> None: + super().__init__(key, default) + + def from_env(self, val: str) -> bool: + return val.lower() in ("1", "true", "yes", "on", "y") + + +class env_int(env_base[int, int]): + + def __init__(self, key: str, default: Union[int, Callable[[], int]] = 0) -> None: + super().__init__(key, default) + + def from_env(self, val: str) -> int: + try: + return int(val) + except ValueError as exc: + raise RuntimeError(f"Unable to use {self.key}={val}: expected int") from exc + + +class env_opt_base(Generic[GetType, SetType], env_base[Optional[GetType], Optional[SetType]]): + + def __init__(self, key: str) -> None: + super().__init__(key, None) + + +ClassType = TypeVar("ClassType") + + +class env_class(Generic[ClassType], env_opt_base[Type[ClassType], Type[ClassType]]): + + def __init__(self, key: str, type: str) -> None: + super().__init__(key) + # We can't pass the type directly to avoid import cycles + self.type = type + + def from_env(self, val: str) -> Type[ClassType]: + comps = val.split(":", 1) + if len(comps) != 2: + raise RuntimeError(f"Unable to read {self.key}: '{val}' isn't of the form MODULE:CLASS") + cls = getattr(importlib.import_module(comps[0]), comps[1]) + + if not any((c.__name__ == self.type for c in cls.mro())): + raise RuntimeError(f"Unable to use '{val}' from {self.key}: not of type '{self.type}'") + + return cast(Type[ClassType], cls) + + +@dataclass +class NvidiaTool: + path: str + version: str + + @staticmethod + def from_path(path: str) -> Optional[NvidiaTool]: + try: + result = subprocess.check_output([path, "--version"], stderr=subprocess.STDOUT) + if result is None: + return None + version = re.search(r".*release (\d+\.\d+).*", result.decode("utf-8"), flags=re.MULTILINE) + if version is None: + return None + return NvidiaTool(path, version.group(1)) + except subprocess.CalledProcessError: + return None + + +class env_nvidia_tool(env_base[str, NvidiaTool]): + + def __init__(self, binary: str) -> None: + binary += sysconfig.get_config_var("EXE") + self.binary = binary + super().__init__(f"TRITON_{binary.upper()}_PATH", lambda: os.path.join( + os.path.dirname(__file__), + "backends", + "nvidia", + "bin", + self.binary, + )) + + def transform(self, path: str) -> NvidiaTool: + paths = [ + path, + # We still add default as fallback in case the pointed binary isn't + # accessible. + self.default(), + ] + for path in paths: + if not path or not os.access(path, os.X_OK): + continue + if tool := NvidiaTool.from_path(path): + return tool + + raise RuntimeError(f"Cannot find {self.binary}") + + def from_env(self, val: str) -> str: + return val + + +# Separate classes so that types are correct +class env_opt_str(env_opt_base[str, str], env_str): + pass + + +class env_opt_bool(env_opt_base[bool, bool], env_bool): + pass + + +@dataclass(frozen=True) +class CompileTimes: + """ + Model holding timing information for an invocation of the compiler. + + All times in microseconds. + """ + + # Duration of make_ir + ir_initialization: int + + # Ordered mapping from lowering stage to duration spent in that stage. + # Keyed by stage extension, e.g. ttir, ttgir + lowering_stages: list[tuple[str, int]] + + # Duration of saving artifacts/metadata to cache + store_results: int + + @property + def total_lowering(self) -> int: + return sum((stage[1] for stage in self.lowering_stages)) + + @property + def total(self) -> int: + return self.ir_initialization + self.total_lowering + self.store_results + + +class CompilationListener(Protocol): + + def __call__(self, *, src: Union[ASTSource, IRSource], metadata: dict[str, Any], metadata_group: dict[str, str], + times: CompileTimes, cache_hit: bool) -> None: + ... + + +knobs_type = TypeVar("knobs_type", bound='base_knobs') + + +class base_knobs: + + @property + def knob_descriptors(self) -> dict[str, env_base]: + return { + k: v + # data descriptors live on the class object + for k, v in type(self).__dict__.items() + if isinstance(v, env_base) + } + + @property + def knobs(self) -> dict[str, Any]: + return {k: getattr(self, k) for k in self.knob_descriptors.keys()} + + def copy(self: knobs_type) -> knobs_type: + res = type(self)() + res.__dict__.update(self.__dict__) + return res + + def reset(self: knobs_type) -> knobs_type: + for knob in self.knob_descriptors.keys(): + delattr(self, knob) + return self + + @contextmanager + def scope(self) -> Generator[None, None, None]: + try: + initial_env = {knob.key: knob.env_val for knob in self.knob_descriptors.values()} + orig = dict(self.__dict__) + yield + finally: + self.__dict__.clear() + self.__dict__.update(orig) + + for k, v in initial_env.items(): + if v is not None: + os.environ[k] = v + elif k in os.environ: + del os.environ[k] + + +class BuildImpl(Protocol): + + def __call__(self, name: str, src: str, srcdir: str, library_dirs: list[str], include_dirs: list[str], + libraries: list[str], /) -> str: + ... + + +class build_knobs(base_knobs): + """Configuration controlling how the native compiler is invoked""" + cc: env_opt_str = env_opt_str("CC") + + cudacrt_path: env_opt_str = env_opt_str("TRITON_CUDACRT_PATH") + cudart_path: env_opt_str = env_opt_str("TRITON_CUDART_PATH") + + impl: Optional[BuildImpl] = None + + @property + def backend_dirs(self) -> set[str]: + return {path for path in (self.cudacrt_path, self.cudart_path) if path is not None} + + +class redis_knobs(base_knobs): + key_format: env_str = env_str("TRITON_REDIS_KEY_FORMAT", "triton:{key}:{filename}") + host: env_str = env_str("TRITON_REDIS_HOST", "localhost") + port: env_int = env_int("TRITON_REDIS_PORT", 6379) + + +cache: cache_knobs + + +class cache_knobs(base_knobs): + home_dir: env_str = env_str("TRITON_HOME", lambda: os.path.expanduser("~/")) + + dump_dir: env_str = env_str("TRITON_DUMP_DIR", lambda: cache.get_triton_dir("dump")) + override_dir: env_str = env_str("TRITON_OVERRIDE_DIR", lambda: cache.get_triton_dir("override")) + dir: env_str = env_str("TRITON_CACHE_DIR", lambda: cache.get_triton_dir("cache")) + + manager_class: env_class[CacheManager] = env_class("TRITON_CACHE_MANAGER", "CacheManager") + remote_manager_class: env_class[RemoteCacheBackend] = env_class("TRITON_REMOTE_CACHE_BACKEND", "RemoteCacheBackend") + + def get_triton_dir(self, dirname: str) -> str: + return os.path.join(self.home_dir, ".triton", dirname) + + +class compilation_knobs(base_knobs): + override: env_bool = env_bool("TRITON_KERNEL_OVERRIDE") + dump_ir: env_bool = env_bool("TRITON_KERNEL_DUMP") + store_binary_only: env_bool = env_bool("TRITON_STORE_BINARY_ONLY") + always_compile: env_bool = env_bool("TRITON_ALWAYS_COMPILE") + # TODO: Use enum to constrain / 'typecheck' the values + use_ir_loc: env_opt_str = env_opt_str("USE_IR_LOC") + enable_asan: env_bool = env_bool("TRITON_ENABLE_ASAN") + disable_line_info: env_bool = env_bool("TRITON_DISABLE_LINE_INFO") + front_end_debugging: env_bool = env_bool("TRITON_FRONT_END_DEBUGGING") + allow_non_constexpr_globals: env_bool = env_bool("TRITON_ALLOW_NON_CONSTEXPR_GLOBALS") + listener: Union[CompilationListener, None] = None + + +class autotuning_knobs(base_knobs): + cache: env_bool = env_bool("TRITON_CACHE_AUTOTUNING") + print: env_bool = env_bool("TRITON_PRINT_AUTOTUNING") + + +class LaunchHook(Protocol): + + def __call__(self, metadata: LazyDict) -> None: + ... + + +# This is of the form [attr_name, attr_val] +# TODO: Use tuple instead of list for better typing. +KernelAttr = list[Union[str, int]] + + +class JITHookCompileInfo(TypedDict): + key: str + signature: dict[KernelParam, str] + device: int + constants: None + num_warps: int + num_ctas: int + num_stages: int + enable_fp_fusion: bool + launch_cooperative_grid: bool + extern_libs: tuple[tuple[str, str], ...] + configs: list[dict[tuple[int, ...], list[KernelAttr]]] + specialization_data: str + is_warmup: bool + + +class JITHook(Protocol): + + def __call__(self, *, key: str, repr: str, fn: JitFunctionInfo, compile: JITHookCompileInfo, is_manual_warmup: bool, + already_compiled: bool) -> Optional[bool]: + ... + + +class runtime_knobs(base_knobs): + interpret: env_bool = env_bool("TRITON_INTERPRET") + debug: env_bool = env_bool("TRITON_DEBUG") + override_arch: env_opt_str = env_opt_str("TRITON_OVERRIDE_ARCH") + + launch_enter_hook: Optional[LaunchHook] = None + launch_exit_hook: Optional[LaunchHook] = None + + # Hook for inspecting compiled functions and modules + jit_cache_hook: Optional[JITHook] = None + # Hook to signal that a kernel is done compiling and inspect compiled function. + # jit_cache_hook will always be called before compilation and jit_post_compile_hook after. + jit_post_compile_hook: Optional[JITHook] = None + + +class language_knobs(base_knobs): + fp32_default: env_opt_str = env_opt_str("TRITON_F32_DEFAULT") + default_fp_fusion: env_bool = env_bool("TRITON_DEFAULT_FP_FUSION", True) + + +class nvidia_knobs(base_knobs): + cuobjdump: env_nvidia_tool = env_nvidia_tool("cuobjdump") + nvdisasm: env_nvidia_tool = env_nvidia_tool("nvdisasm") + ptxas: env_nvidia_tool = env_nvidia_tool("ptxas") + + dump_nvptx: env_bool = env_bool("NVPTX_ENABLE_DUMP") + disable_ptxas_opt: env_bool = env_bool("DISABLE_PTXAS_OPT") + mock_ptx_version: env_opt_str = env_opt_str("TRITON_MOCK_PTX_VERSION") + + libdevice_path: env_opt_str = env_opt_str("TRITON_LIBDEVICE_PATH") + libcuda_path: env_opt_str = env_opt_str("TRITON_LIBCUDA_PATH") + + +class amd_knobs(base_knobs): + use_buffer_ops: env_bool = env_bool("AMDGCN_USE_BUFFER_OPS") + dump_amdgcn: env_bool = env_bool("AMDGCN_ENABLE_DUMP") + libhip_path: env_opt_str = env_opt_str("TRITON_LIBHIP_PATH") + lld_path: env_opt_str = env_opt_str("TRITON_HIP_LLD_PATH") + + # We use strs so that we can have a default value based on other runtime info + use_block_pingpong: env_opt_bool = env_opt_bool("TRITON_HIP_USE_BLOCK_PINGPONG") + use_in_thread_transpose: env_opt_bool = env_opt_bool("TRITON_HIP_USE_IN_THREAD_TRANSPOSE") + + global_prefetch: env_int = env_int("TRITON_HIP_GLOBAL_PREFETCH") + local_prefetch: env_int = env_int("TRITON_HIP_LOCAL_PREFETCH") + use_async_copy: env_bool = env_bool("TRITON_HIP_USE_ASYNC_COPY") + scalarize_packed_fops: env_bool = env_bool("AMDGCN_SCALARIZE_PACKED_FOPS") + + +class proton_knobs(base_knobs): + cupti_dir: env_opt_str = env_opt_str("TRITON_CUPTI_LIB_PATH") + + +build = build_knobs() +redis = redis_knobs() +cache = cache_knobs() +compilation = compilation_knobs() +autotuning = autotuning_knobs() +runtime = runtime_knobs() +language = language_knobs() +nvidia = nvidia_knobs() +amd = amd_knobs() +proton = proton_knobs() diff --git a/.venv/lib/python3.12/site-packages/triton/testing.py b/.venv/lib/python3.12/site-packages/triton/testing.py new file mode 100644 index 0000000000000000000000000000000000000000..1fd88c648779f632eb5bf3db3b31f13095208c40 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/triton/testing.py @@ -0,0 +1,543 @@ +import functools +import math +import os +import statistics +import subprocess +import sys +from contextlib import contextmanager +from typing import Any, Dict, List +from . import language as tl +from . import runtime + + +def nvsmi(attrs): + attrs = ','.join(attrs) + cmd = ['nvidia-smi', '-i', '0', '--query-gpu=' + attrs, '--format=csv,noheader,nounits'] + out = subprocess.check_output(cmd) + ret = out.decode(sys.stdout.encoding).split(',') + ret = [int(x) for x in ret] + return ret + + +# pure Python implementation of np.quantile/torch.quantile +# to avoid unnecessary runtime dependency on numpy/torch + + +def _quantile(a, q): + n = len(a) + a = sorted(a) + + def get_quantile(q): + if not (0 <= q <= 1): + raise ValueError("Quantiles must be in the range [0, 1]") + point = q * (n - 1) + lower = math.floor(point) + upper = math.ceil(point) + t = point - lower + return (1 - t) * a[lower] + t * a[upper] + + return [get_quantile(q) for q in q] + + +def _summarize_statistics(times, quantiles, return_mode): + if quantiles is not None: + ret = _quantile(times, quantiles) + if len(ret) == 1: + ret = ret[0] + return ret + if return_mode == "all": + return times + elif return_mode == "min": + return min(times) + elif return_mode == "max": + return max(times) + elif return_mode == "mean": + return statistics.mean(times) + elif return_mode == "median": + return statistics.median(times) + + +def do_bench_cudagraph(fn, rep=20, grad_to_none=None, quantiles=None, return_mode="mean"): + """ + Benchmark the runtime of the provided function. + + :param fn: Function to benchmark + :type fn: Callable + :param rep: Repetition time (in ms) + :type rep: int + :param grad_to_none: Reset the gradient of the provided tensor to None + :type grad_to_none: torch.tensor, optional + :param return_mode: The statistical measure to return. Options are "min", "max", "mean", "median", or "all". Default is "mean". + :type return_mode: str + """ + import torch + assert return_mode in ["min", "max", "mean", "median", "all"] + + with torch.cuda.stream(torch.cuda.Stream()): + # warmup + fn() + if grad_to_none is not None: + for x in grad_to_none: + x.detach_() + x.requires_grad_(True) + x.grad = None + # step 1 - we estimate the amount of time the kernel call takes + # NOTE: this estimate isn't super accurate because the GPU isn't warmed up at this point + # but it is probably good enough + # NOTE: we don't use a graph to estimate the runtime because creating a graph is expensive, + # ~300ms on A100, so we default to the same method used in `do_bench` (minus the L2 + # cache flush). + start_event = torch.cuda.Event(enable_timing=True) + end_event = torch.cuda.Event(enable_timing=True) + start_event.record() + for _ in range(5): + fn() + end_event.record() + torch.cuda.synchronize() + estimate_ms = start_event.elapsed_time(end_event) / 5 + # Rewrite to avoid possible division by 0 issues with fast benchmarks + if estimate_ms == 0: + n_repeat = 1000 + else: + n_repeat = max(1, int(rep / estimate_ms)) + # step 2 - construct a cuda graph with `n_repeat` unrolled function calls to minimize + # host overhead + g = torch.cuda.CUDAGraph() + with torch.cuda.graph(g): + for _ in range(n_repeat): + if grad_to_none is not None: + for x in grad_to_none: + x.grad = None + fn() + torch.cuda.synchronize() + # measure time and return + ret = [] + n_retries = 10 + for _ in range(n_retries): + start_event = torch.cuda.Event(enable_timing=True) + end_event = torch.cuda.Event(enable_timing=True) + start_event.record() + g.replay() + end_event.record() + torch.cuda.synchronize() + ret += [start_event.elapsed_time(end_event) / n_repeat] + return _summarize_statistics(ret, quantiles, return_mode) + + +def do_bench(fn, warmup=25, rep=100, grad_to_none=None, quantiles=None, return_mode="mean"): + """ + Benchmark the runtime of the provided function. By default, return the median runtime of :code:`fn` along with + the 20-th and 80-th performance percentile. + + :param fn: Function to benchmark + :type fn: Callable + :param warmup: Warmup time (in ms) + :type warmup: int + :param rep: Repetition time (in ms) + :type rep: int + :param grad_to_none: Reset the gradient of the provided tensor to None + :type grad_to_none: torch.tensor, optional + :param quantiles: Performance percentile to return in addition to the median. + :type quantiles: list[float], optional + :param return_mode: The statistical measure to return. Options are "min", "max", "mean", "median", or "all". Default is "mean". + :type return_mode: str + """ + assert return_mode in ["min", "max", "mean", "median", "all"] + + di = runtime.driver.active.get_device_interface() + + fn() + di.synchronize() + + cache = runtime.driver.active.get_empty_cache_for_benchmark() + + # Estimate the runtime of the function + start_event = di.Event(enable_timing=True) + end_event = di.Event(enable_timing=True) + start_event.record() + for _ in range(5): + runtime.driver.active.clear_cache(cache) + fn() + end_event.record() + di.synchronize() + estimate_ms = start_event.elapsed_time(end_event) / 5 + + # compute number of warmup and repeat + n_warmup = max(1, int(warmup / estimate_ms)) + n_repeat = max(1, int(rep / estimate_ms)) + start_event = [di.Event(enable_timing=True) for i in range(n_repeat)] + end_event = [di.Event(enable_timing=True) for i in range(n_repeat)] + # Warm-up + for _ in range(n_warmup): + fn() + # Benchmark + for i in range(n_repeat): + # we don't want `fn` to accumulate gradient values + # if it contains a backward pass. So we clear the + # provided gradients + if grad_to_none is not None: + for x in grad_to_none: + x.grad = None + # we clear the L2 cache before each run + runtime.driver.active.clear_cache(cache) + # record time of `fn` + start_event[i].record() + fn() + end_event[i].record() + # Record clocks + di.synchronize() + times = [s.elapsed_time(e) for s, e in zip(start_event, end_event)] + return _summarize_statistics(times, quantiles, return_mode) + + +def assert_close(x, y, atol=None, rtol=None, err_msg=''): + """ + Asserts that two inputs are close within a certain tolerance. + + :param x: The first input. + :type x: scala, list, numpy.ndarray, or torch.Tensor + :param y: The second input. + :type y: scala, list, numpy.ndarray, or torch.Tensor + :param atol: The absolute tolerance. Default value is 1e-2. + :type atol: float, optional + :param rtol: The relative tolerance. Default value is 0. + :type rtol: float, optional + :param err_msg: The error message to use if the assertion fails. + :type err_msg: str + """ + import numpy as np + import torch + + # canonicalize arguments to be tensors + if not isinstance(x, torch.Tensor): + x = torch.tensor(x) + if not isinstance(y, torch.Tensor): + y = torch.tensor(y) + # absolute tolerance + if atol is None: + atol = 1e-2 + atol = atol(x.dtype) if callable(atol) else atol + # relative tolerance hook + if rtol is None: + rtol = 0. + rtol = rtol(x.dtype) if callable(rtol) else rtol + # we use numpy instead of pytorch + # as it seems more memory efficient + # pytorch tends to oom on large tensors + if isinstance(x, torch.Tensor): + if x.dtype == torch.bfloat16: + x = x.float() + x = x.cpu().detach().numpy() + if isinstance(y, torch.Tensor): + if y.dtype == torch.bfloat16: + y = y.float() + y = y.cpu().detach().numpy() + # we handle size==1 case separately as we can + # provide better error message there + if x.size > 1 or y.size > 1: + np.testing.assert_allclose(x, y, atol=atol, rtol=rtol, equal_nan=True) + return + if not np.allclose(x, y, atol=atol, rtol=rtol): + raise AssertionError(f'{err_msg} {x} is not close to {y} (atol={atol}, rtol={rtol})') + + +class Benchmark: + """ + This class is used by the :code:`perf_report` function to generate line plots with a concise API. + """ + + def __init__( + self, + x_names: List[str], + x_vals: List[Any], + line_arg: str, + line_vals: List[Any], + line_names: List[str], + plot_name: str, + args: Dict[str, Any], + xlabel: str = '', + ylabel: str = '', + x_log: bool = False, + y_log: bool = False, + styles=None, + ): + """ + Constructor. + x_vals can be a list of scalars or a list of tuples/lists. If x_vals is a list + of scalars and there are multiple x_names, all arguments will have the same value. + If x_vals is a list of tuples/lists, each element should have the same length as + x_names. + + :param x_names: Name of the arguments that should appear on the x axis of the plot. + :type x_names: List[str] + :param x_vals: List of values to use for the arguments in :code:`x_names`. + :type x_vals: List[Any] + :param line_arg: Argument name for which different values correspond to different lines in the plot. + :type line_arg: str + :param line_vals: List of values to use for the arguments in :code:`line_arg`. + :type line_vals: List[Any] + :param line_names: Label names for the different lines. + :type line_names: List[str] + :param plot_name: Name of the plot. + :type plot_name: str + :param args: Dictionary of keyword arguments to remain fixed throughout the benchmark. + :type args: Dict[str, Any] + :param xlabel: Label for the x axis of the plot. + :type xlabel: str, optional + :param ylabel: Label for the y axis of the plot. + :type ylabel: str, optional + :param x_log: Whether the x axis should be log scale. + :type x_log: bool, optional + :param y_log: Whether the y axis should be log scale. + :type y_log: bool, optional + :param styles: A list of tuples, where each tuple contains two elements: a color and a linestyle. + :type styles: list[tuple[str, str]] + """ + self.x_names = x_names + self.x_vals = x_vals + self.x_log = x_log + self.line_arg = line_arg + self.line_vals = line_vals + self.line_names = line_names + self.y_log = y_log + self.styles = styles + # plot info + self.xlabel = xlabel + self.ylabel = ylabel + self.plot_name = plot_name + self.args = args + + +class Mark: + + def __init__(self, fn, benchmarks): + self.fn = fn + self.benchmarks = benchmarks + + def _run(self, bench: Benchmark, save_path: str, show_plots: bool, print_data: bool, diff_col=False, + save_precision=6, **kwrags): + import os + + import matplotlib.pyplot as plt + import pandas as pd + y_mean = bench.line_names + y_min = [f'{x}-min' for x in bench.line_names] + y_max = [f'{x}-max' for x in bench.line_names] + x_names = list(bench.x_names) + df = pd.DataFrame(columns=x_names + y_mean + y_min + y_max) + for x in bench.x_vals: + # x can be a single value or a sequence of values. + if not isinstance(x, (list, tuple)): + x = [x for _ in x_names] + + if len(x) != len(x_names): + raise ValueError(f"Expected {len(x_names)} values, got {x}") + x_args = dict(zip(x_names, x)) + + row_mean, row_min, row_max = [], [], [] + for y in bench.line_vals: + ret = self.fn(**x_args, **{bench.line_arg: y}, **bench.args, **kwrags) + try: + y_mean, y_min, y_max = ret + except TypeError: + y_mean, y_min, y_max = ret, None, None + row_mean += [y_mean] + row_min += [y_min] + row_max += [y_max] + df.loc[len(df)] = list(x) + row_mean + row_min + row_max + + if bench.plot_name: + plt.figure() + ax = plt.subplot() + # Plot first x value on x axis if there are multiple. + first_x = x_names[0] + for i, y in enumerate(bench.line_names): + y_min, y_max = df[y + '-min'], df[y + '-max'] + col = bench.styles[i][0] if bench.styles else None + sty = bench.styles[i][1] if bench.styles else None + ax.plot(df[first_x], df[y], label=y, color=col, ls=sty) + if not y_min.isnull().all() and not y_max.isnull().all(): + y_min = y_min.astype(float) + y_max = y_max.astype(float) + ax.fill_between(df[first_x], y_min, y_max, alpha=0.15, color=col) + ax.legend() + ax.set_xlabel(bench.xlabel or first_x) + ax.set_ylabel(bench.ylabel) + # ax.set_title(bench.plot_name) + ax.set_xscale("log" if bench.x_log else "linear") + ax.set_yscale("log" if bench.y_log else "linear") + if show_plots: + plt.show() + if save_path: + plt.savefig(os.path.join(save_path, f"{bench.plot_name}.png")) + df = df[x_names + bench.line_names] + if diff_col and df.shape[1] == 2: + col0, col1 = df.columns.tolist() + df['Diff'] = df[col1] - df[col0] + + if print_data: + print(bench.plot_name + ':') + print(df.to_string()) + if save_path: + df.to_csv(os.path.join(save_path, f"{bench.plot_name}.csv"), float_format=f"%.{save_precision}f", + index=False) + return df + + def run(self, show_plots=False, print_data=False, save_path='', return_df=False, **kwargs): + has_single_bench = isinstance(self.benchmarks, Benchmark) + benchmarks = [self.benchmarks] if has_single_bench else self.benchmarks + result_dfs = [] + try: + for bench in benchmarks: + result_dfs.append(self._run(bench, save_path, show_plots, print_data, **kwargs)) + finally: + if save_path: + # Create directory if it doesn't exist + os.makedirs(save_path, exist_ok=True) + with open(os.path.join(save_path, "results.html"), "w") as html: + html.write("\n") + for bench in benchmarks[:len(result_dfs)]: + html.write(f"\n") + html.write("\n") + if return_df: + if has_single_bench: + return result_dfs[0] + else: + return result_dfs + return None + + +def perf_report(benchmarks): + """ + Mark a function for benchmarking. The benchmark can then be executed by using the :code:`.run` method on the return value. + + :param benchmarks: Benchmarking configurations. + :type benchmarks: List of :class:`Benchmark` + """ + wrapper = lambda fn: Mark(fn, benchmarks) + return wrapper + + +def get_dram_gbps(device=None): + ''' return DRAM bandwidth in GB/s ''' + import torch + + from .runtime import driver + if not device: + device = torch.cuda.current_device() + mem_clock_khz = driver.active.utils.get_device_properties(device)["mem_clock_rate"] # in kHz + bus_width = driver.active.utils.get_device_properties(device)["mem_bus_width"] + bw_gbps = mem_clock_khz * bus_width * 2 / 1e6 / 8 # In GB/s + return bw_gbps + + +def get_max_tensorcore_tflops(dtype, clock_rate, device=None): + import torch + + from .runtime import driver + if not device: + device = torch.cuda.current_device() + + num_subcores = driver.active.utils.get_device_properties(device)["multiprocessor_count"] * 4 + capability = torch.cuda.get_device_capability(device) + if capability[0] < 8: + assert dtype == torch.float16 + ops_per_sub_core = 256 # 2 4x4x4 Tensor Cores + else: + if dtype in [torch.float32, torch.int32]: + ops_per_sub_core = 256 + elif dtype in [torch.float16, torch.bfloat16, torch.int16]: + ops_per_sub_core = 512 + elif dtype in [torch.int8, tl.float8e4nv, tl.float8e4b15, tl.float8e5]: + ops_per_sub_core = 1024 + else: + raise RuntimeError("dtype not supported") + tflops = num_subcores * clock_rate * ops_per_sub_core * 1e-9 + return tflops + + +# create decorator that wraps test function into +# a cuda-memcheck system call + + +def cuda_memcheck(**target_kwargs): + + def decorator(test_fn): + + @functools.wraps(test_fn) + def wrapper(*args, **kwargs): + import psutil + ppid_name = psutil.Process(os.getppid()).name() + run_cuda_memcheck = target_kwargs.items() <= kwargs.items() + if run_cuda_memcheck and ppid_name != "cuda-memcheck": + path = os.path.realpath(test_fn.__globals__["__file__"]) + # get path of current file + env = {"PATH": os.environ["PATH"], "PYTORCH_NO_CUDA_MEMORY_CACHING": "1"} + assert 'request' in kwargs, "memcheck'ed test must have a (possibly unused) `request` fixture" + test_id = kwargs['request'].node.callspec.id + cmd = f"{path}::{test_fn.__name__}[{test_id}]" + out = subprocess.run(["cuda-memcheck", "pytest", "-vs", cmd], capture_output=True, env=env) + assert out.returncode == 0, "cuda-memcheck returned an error: bounds checking failed" + assert "ERROR SUMMARY: 0 errors" in str(out.stdout) + else: + test_fn(*args, **kwargs) + + return wrapper + + return decorator + + +@contextmanager +def set_gpu_clock(ref_sm_clock=1350, ref_mem_clock=1215): + try: + subprocess.check_output(["nvidia-smi", "-i", "0", "-pm", "1"]) + subprocess.check_output([ + "nvidia-smi", + "-i", + "0", + f"--lock-gpu-clocks={ref_sm_clock},{ref_sm_clock}", + ]) + subprocess.check_output([ + "nvidia-smi", + "-i", + "0", + f"--lock-memory-clocks={ref_mem_clock},{ref_mem_clock}", + ]) + cur_sm_clock = nvsmi(["clocks.current.sm"])[0] + cur_mem_clock = nvsmi(["clocks.current.memory"])[0] + assert abs(cur_sm_clock - ref_sm_clock) < 10, f"GPU SMs must run at {ref_sm_clock} MHz" + assert abs(cur_mem_clock - ref_mem_clock) < 10, f"GPU SMs must run at {ref_mem_clock} MHz" + tflops = 1e-6 * 2 * 108 * 4 * 256 * ref_sm_clock + gbps = 640 * 2 * ref_mem_clock * 1e-3 + yield tflops, gbps + finally: + subprocess.check_output(["nvidia-smi", "-i", "0", "-pm", "0"]) + subprocess.check_output(["nvidia-smi", "-i", "0", "-rgc"]) + subprocess.check_output(["nvidia-smi", "-i", "0", "-rmc"]) + + +def get_max_simd_tflops(dtype, clock_rate, device=None): + import torch + + from .runtime import driver + if not device: + device = torch.cuda.current_device() + + num_subcores = driver.active.utils.get_device_properties(device)["multiprocessor_count"] * 4 + capability = torch.cuda.get_device_capability() + if capability[0] < 8: + if dtype == torch.float32: + ops_per_sub_core = 32 # 2*16 + elif dtype == torch.float16: + ops_per_sub_core = 64 + else: + raise RuntimeError("dtype not supported") + else: + if dtype == torch.float32: + ops_per_sub_core = 32 + elif dtype in [torch.float16, torch.bfloat16]: + ops_per_sub_core = 64 + else: + raise RuntimeError("dtype not supported") + tflops = num_subcores * clock_rate * ops_per_sub_core * 1e-9 + return tflops diff --git a/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..b09cb50e1f9a42a8eb58a4339cbdb26250368375 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/METADATA @@ -0,0 +1,72 @@ +Metadata-Version: 2.4 +Name: typing_extensions +Version: 4.15.0 +Summary: Backported and Experimental Type Hints for Python 3.9+ +Keywords: annotations,backport,checker,checking,function,hinting,hints,type,typechecking,typehinting,typehints,typing +Author-email: "Guido van Rossum, Jukka Lehtosalo, Łukasz Langa, Michael Lee" +Requires-Python: >=3.9 +Description-Content-Type: text/markdown +License-Expression: PSF-2.0 +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: Operating System :: OS Independent +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: Topic :: Software Development +License-File: LICENSE +Project-URL: Bug Tracker, https://github.com/python/typing_extensions/issues +Project-URL: Changes, https://github.com/python/typing_extensions/blob/main/CHANGELOG.md +Project-URL: Documentation, https://typing-extensions.readthedocs.io/ +Project-URL: Home, https://github.com/python/typing_extensions +Project-URL: Q & A, https://github.com/python/typing/discussions +Project-URL: Repository, https://github.com/python/typing_extensions + +# Typing Extensions + +[![Chat at https://gitter.im/python/typing](https://badges.gitter.im/python/typing.svg)](https://gitter.im/python/typing) + +[Documentation](https://typing-extensions.readthedocs.io/en/latest/#) – +[PyPI](https://pypi.org/project/typing-extensions/) + +## Overview + +The `typing_extensions` module serves two related purposes: + +- Enable use of new type system features on older Python versions. For example, + `typing.TypeGuard` is new in Python 3.10, but `typing_extensions` allows + users on previous Python versions to use it too. +- Enable experimentation with new type system PEPs before they are accepted and + added to the `typing` module. + +`typing_extensions` is treated specially by static type checkers such as +mypy and pyright. Objects defined in `typing_extensions` are treated the same +way as equivalent forms in `typing`. + +`typing_extensions` uses +[Semantic Versioning](https://semver.org/). The +major version will be incremented only for backwards-incompatible changes. +Therefore, it's safe to depend +on `typing_extensions` like this: `typing_extensions ~=x.y`, +where `x.y` is the first version that includes all features you need. +[This](https://packaging.python.org/en/latest/specifications/version-specifiers/#compatible-release) +is equivalent to `typing_extensions >=x.y, <(x+1)`. Do not depend on `~= x.y.z` +unless you really know what you're doing; that defeats the purpose of +semantic versioning. + +## Included items + +See [the documentation](https://typing-extensions.readthedocs.io/en/latest/#) for a +complete listing of module contents. + +## Contributing + +See [CONTRIBUTING.md](https://github.com/python/typing_extensions/blob/main/CONTRIBUTING.md) +for how to contribute to `typing_extensions`. + diff --git a/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..ec54c2e277f970bd4ae36e237f8fe0f4dff7f900 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/RECORD @@ -0,0 +1,7 @@ +typing_extensions-4.15.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +typing_extensions-4.15.0.dist-info/METADATA,sha256=wTg3j-jxiTSsmd4GBTXFPsbBOu7WXpTDJkHafuMZKnI,3259 +typing_extensions-4.15.0.dist-info/RECORD,, +typing_extensions-4.15.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +typing_extensions-4.15.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82 +typing_extensions-4.15.0.dist-info/licenses/LICENSE,sha256=Oy-B_iHRgcSZxZolbI4ZaEVdZonSaaqFNzv7avQdo78,13936 +typing_extensions.py,sha256=Qz0R0XDTok0usGXrwb_oSM6n49fOaFZ6tSvqLUwvftg,160429 diff --git a/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..d8b9936dad9ab2513fa6979f411560d3b6b57e37 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/typing_extensions-4.15.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.12.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/typing_extensions.py b/.venv/lib/python3.12/site-packages/typing_extensions.py new file mode 100644 index 0000000000000000000000000000000000000000..77f33e1614fd7d46ccd66b394d6c5d663bf8a8c6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/typing_extensions.py @@ -0,0 +1,4317 @@ +import abc +import builtins +import collections +import collections.abc +import contextlib +import enum +import functools +import inspect +import io +import keyword +import operator +import sys +import types as _types +import typing +import warnings + +# Breakpoint: https://github.com/python/cpython/pull/119891 +if sys.version_info >= (3, 14): + import annotationlib + +__all__ = [ + # Super-special typing primitives. + 'Any', + 'ClassVar', + 'Concatenate', + 'Final', + 'LiteralString', + 'ParamSpec', + 'ParamSpecArgs', + 'ParamSpecKwargs', + 'Self', + 'Type', + 'TypeVar', + 'TypeVarTuple', + 'Unpack', + + # ABCs (from collections.abc). + 'Awaitable', + 'AsyncIterator', + 'AsyncIterable', + 'Coroutine', + 'AsyncGenerator', + 'AsyncContextManager', + 'Buffer', + 'ChainMap', + + # Concrete collection types. + 'ContextManager', + 'Counter', + 'Deque', + 'DefaultDict', + 'NamedTuple', + 'OrderedDict', + 'TypedDict', + + # Structural checks, a.k.a. protocols. + 'SupportsAbs', + 'SupportsBytes', + 'SupportsComplex', + 'SupportsFloat', + 'SupportsIndex', + 'SupportsInt', + 'SupportsRound', + 'Reader', + 'Writer', + + # One-off things. + 'Annotated', + 'assert_never', + 'assert_type', + 'clear_overloads', + 'dataclass_transform', + 'deprecated', + 'disjoint_base', + 'Doc', + 'evaluate_forward_ref', + 'get_overloads', + 'final', + 'Format', + 'get_annotations', + 'get_args', + 'get_origin', + 'get_original_bases', + 'get_protocol_members', + 'get_type_hints', + 'IntVar', + 'is_protocol', + 'is_typeddict', + 'Literal', + 'NewType', + 'overload', + 'override', + 'Protocol', + 'Sentinel', + 'reveal_type', + 'runtime', + 'runtime_checkable', + 'Text', + 'TypeAlias', + 'TypeAliasType', + 'TypeForm', + 'TypeGuard', + 'TypeIs', + 'TYPE_CHECKING', + 'type_repr', + 'Never', + 'NoReturn', + 'ReadOnly', + 'Required', + 'NotRequired', + 'NoDefault', + 'NoExtraItems', + + # Pure aliases, have always been in typing + 'AbstractSet', + 'AnyStr', + 'BinaryIO', + 'Callable', + 'Collection', + 'Container', + 'Dict', + 'ForwardRef', + 'FrozenSet', + 'Generator', + 'Generic', + 'Hashable', + 'IO', + 'ItemsView', + 'Iterable', + 'Iterator', + 'KeysView', + 'List', + 'Mapping', + 'MappingView', + 'Match', + 'MutableMapping', + 'MutableSequence', + 'MutableSet', + 'Optional', + 'Pattern', + 'Reversible', + 'Sequence', + 'Set', + 'Sized', + 'TextIO', + 'Tuple', + 'Union', + 'ValuesView', + 'cast', + 'no_type_check', + 'no_type_check_decorator', +] + +# for backward compatibility +PEP_560 = True +GenericMeta = type +# Breakpoint: https://github.com/python/cpython/pull/116129 +_PEP_696_IMPLEMENTED = sys.version_info >= (3, 13, 0, "beta") + +# Added with bpo-45166 to 3.10.1+ and some 3.9 versions +_FORWARD_REF_HAS_CLASS = "__forward_is_class__" in typing.ForwardRef.__slots__ + +# The functions below are modified copies of typing internal helpers. +# They are needed by _ProtocolMeta and they provide support for PEP 646. + + +class _Sentinel: + def __repr__(self): + return "" + + +_marker = _Sentinel() + + +# Breakpoint: https://github.com/python/cpython/pull/27342 +if sys.version_info >= (3, 10): + def _should_collect_from_parameters(t): + return isinstance( + t, (typing._GenericAlias, _types.GenericAlias, _types.UnionType) + ) +else: + def _should_collect_from_parameters(t): + return isinstance(t, (typing._GenericAlias, _types.GenericAlias)) + + +NoReturn = typing.NoReturn + +# Some unconstrained type variables. These are used by the container types. +# (These are not for export.) +T = typing.TypeVar('T') # Any type. +KT = typing.TypeVar('KT') # Key type. +VT = typing.TypeVar('VT') # Value type. +T_co = typing.TypeVar('T_co', covariant=True) # Any type covariant containers. +T_contra = typing.TypeVar('T_contra', contravariant=True) # Ditto contravariant. + + +# Breakpoint: https://github.com/python/cpython/pull/31841 +if sys.version_info >= (3, 11): + from typing import Any +else: + + class _AnyMeta(type): + def __instancecheck__(self, obj): + if self is Any: + raise TypeError("typing_extensions.Any cannot be used with isinstance()") + return super().__instancecheck__(obj) + + def __repr__(self): + if self is Any: + return "typing_extensions.Any" + return super().__repr__() + + class Any(metaclass=_AnyMeta): + """Special type indicating an unconstrained type. + - Any is compatible with every type. + - Any assumed to have all methods. + - All values assumed to be instances of Any. + Note that all the above statements are true from the point of view of + static type checkers. At runtime, Any should not be used with instance + checks. + """ + def __new__(cls, *args, **kwargs): + if cls is Any: + raise TypeError("Any cannot be instantiated") + return super().__new__(cls, *args, **kwargs) + + +ClassVar = typing.ClassVar + +# Vendored from cpython typing._SpecialFrom +# Having a separate class means that instances will not be rejected by +# typing._type_check. +class _SpecialForm(typing._Final, _root=True): + __slots__ = ('_name', '__doc__', '_getitem') + + def __init__(self, getitem): + self._getitem = getitem + self._name = getitem.__name__ + self.__doc__ = getitem.__doc__ + + def __getattr__(self, item): + if item in {'__name__', '__qualname__'}: + return self._name + + raise AttributeError(item) + + def __mro_entries__(self, bases): + raise TypeError(f"Cannot subclass {self!r}") + + def __repr__(self): + return f'typing_extensions.{self._name}' + + def __reduce__(self): + return self._name + + def __call__(self, *args, **kwds): + raise TypeError(f"Cannot instantiate {self!r}") + + def __or__(self, other): + return typing.Union[self, other] + + def __ror__(self, other): + return typing.Union[other, self] + + def __instancecheck__(self, obj): + raise TypeError(f"{self} cannot be used with isinstance()") + + def __subclasscheck__(self, cls): + raise TypeError(f"{self} cannot be used with issubclass()") + + @typing._tp_cache + def __getitem__(self, parameters): + return self._getitem(self, parameters) + + +# Note that inheriting from this class means that the object will be +# rejected by typing._type_check, so do not use it if the special form +# is arguably valid as a type by itself. +class _ExtensionsSpecialForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + +Final = typing.Final + +# Breakpoint: https://github.com/python/cpython/pull/30530 +if sys.version_info >= (3, 11): + final = typing.final +else: + # @final exists in 3.8+, but we backport it for all versions + # before 3.11 to keep support for the __final__ attribute. + # See https://bugs.python.org/issue46342 + def final(f): + """This decorator can be used to indicate to type checkers that + the decorated method cannot be overridden, and decorated class + cannot be subclassed. For example: + + class Base: + @final + def done(self) -> None: + ... + class Sub(Base): + def done(self) -> None: # Error reported by type checker + ... + @final + class Leaf: + ... + class Other(Leaf): # Error reported by type checker + ... + + There is no runtime checking of these properties. The decorator + sets the ``__final__`` attribute to ``True`` on the decorated object + to allow runtime introspection. + """ + try: + f.__final__ = True + except (AttributeError, TypeError): + # Skip the attribute silently if it is not writable. + # AttributeError happens if the object has __slots__ or a + # read-only property, TypeError if it's a builtin class. + pass + return f + + +if hasattr(typing, "disjoint_base"): # 3.15 + disjoint_base = typing.disjoint_base +else: + def disjoint_base(cls): + """This decorator marks a class as a disjoint base. + + Child classes of a disjoint base cannot inherit from other disjoint bases that are + not parent classes of the disjoint base. + + For example: + + @disjoint_base + class Disjoint1: pass + + @disjoint_base + class Disjoint2: pass + + class Disjoint3(Disjoint1, Disjoint2): pass # Type checker error + + Type checkers can use knowledge of disjoint bases to detect unreachable code + and determine when two types can overlap. + + See PEP 800.""" + cls.__disjoint_base__ = True + return cls + + +def IntVar(name): + return typing.TypeVar(name) + + +# A Literal bug was fixed in 3.11.0, 3.10.1 and 3.9.8 +# Breakpoint: https://github.com/python/cpython/pull/29334 +if sys.version_info >= (3, 10, 1): + Literal = typing.Literal +else: + def _flatten_literal_params(parameters): + """An internal helper for Literal creation: flatten Literals among parameters""" + params = [] + for p in parameters: + if isinstance(p, _LiteralGenericAlias): + params.extend(p.__args__) + else: + params.append(p) + return tuple(params) + + def _value_and_type_iter(params): + for p in params: + yield p, type(p) + + class _LiteralGenericAlias(typing._GenericAlias, _root=True): + def __eq__(self, other): + if not isinstance(other, _LiteralGenericAlias): + return NotImplemented + these_args_deduped = set(_value_and_type_iter(self.__args__)) + other_args_deduped = set(_value_and_type_iter(other.__args__)) + return these_args_deduped == other_args_deduped + + def __hash__(self): + return hash(frozenset(_value_and_type_iter(self.__args__))) + + class _LiteralForm(_ExtensionsSpecialForm, _root=True): + def __init__(self, doc: str): + self._name = 'Literal' + self._doc = self.__doc__ = doc + + def __getitem__(self, parameters): + if not isinstance(parameters, tuple): + parameters = (parameters,) + + parameters = _flatten_literal_params(parameters) + + val_type_pairs = list(_value_and_type_iter(parameters)) + try: + deduped_pairs = set(val_type_pairs) + except TypeError: + # unhashable parameters + pass + else: + # similar logic to typing._deduplicate on Python 3.9+ + if len(deduped_pairs) < len(val_type_pairs): + new_parameters = [] + for pair in val_type_pairs: + if pair in deduped_pairs: + new_parameters.append(pair[0]) + deduped_pairs.remove(pair) + assert not deduped_pairs, deduped_pairs + parameters = tuple(new_parameters) + + return _LiteralGenericAlias(self, parameters) + + Literal = _LiteralForm(doc="""\ + A type that can be used to indicate to type checkers + that the corresponding value has a value literally equivalent + to the provided parameter. For example: + + var: Literal[4] = 4 + + The type checker understands that 'var' is literally equal to + the value 4 and no other value. + + Literal[...] cannot be subclassed. There is no runtime + checking verifying that the parameter is actually a value + instead of a type.""") + + +_overload_dummy = typing._overload_dummy + + +if hasattr(typing, "get_overloads"): # 3.11+ + overload = typing.overload + get_overloads = typing.get_overloads + clear_overloads = typing.clear_overloads +else: + # {module: {qualname: {firstlineno: func}}} + _overload_registry = collections.defaultdict( + functools.partial(collections.defaultdict, dict) + ) + + def overload(func): + """Decorator for overloaded functions/methods. + + In a stub file, place two or more stub definitions for the same + function in a row, each decorated with @overload. For example: + + @overload + def utf8(value: None) -> None: ... + @overload + def utf8(value: bytes) -> bytes: ... + @overload + def utf8(value: str) -> bytes: ... + + In a non-stub file (i.e. a regular .py file), do the same but + follow it with an implementation. The implementation should *not* + be decorated with @overload. For example: + + @overload + def utf8(value: None) -> None: ... + @overload + def utf8(value: bytes) -> bytes: ... + @overload + def utf8(value: str) -> bytes: ... + def utf8(value): + # implementation goes here + + The overloads for a function can be retrieved at runtime using the + get_overloads() function. + """ + # classmethod and staticmethod + f = getattr(func, "__func__", func) + try: + _overload_registry[f.__module__][f.__qualname__][ + f.__code__.co_firstlineno + ] = func + except AttributeError: + # Not a normal function; ignore. + pass + return _overload_dummy + + def get_overloads(func): + """Return all defined overloads for *func* as a sequence.""" + # classmethod and staticmethod + f = getattr(func, "__func__", func) + if f.__module__ not in _overload_registry: + return [] + mod_dict = _overload_registry[f.__module__] + if f.__qualname__ not in mod_dict: + return [] + return list(mod_dict[f.__qualname__].values()) + + def clear_overloads(): + """Clear all overloads in the registry.""" + _overload_registry.clear() + + +# This is not a real generic class. Don't use outside annotations. +Type = typing.Type + +# Various ABCs mimicking those in collections.abc. +# A few are simply re-exported for completeness. +Awaitable = typing.Awaitable +Coroutine = typing.Coroutine +AsyncIterable = typing.AsyncIterable +AsyncIterator = typing.AsyncIterator +Deque = typing.Deque +DefaultDict = typing.DefaultDict +OrderedDict = typing.OrderedDict +Counter = typing.Counter +ChainMap = typing.ChainMap +Text = typing.Text +TYPE_CHECKING = typing.TYPE_CHECKING + + +# Breakpoint: https://github.com/python/cpython/pull/118681 +if sys.version_info >= (3, 13, 0, "beta"): + from typing import AsyncContextManager, AsyncGenerator, ContextManager, Generator +else: + def _is_dunder(attr): + return attr.startswith('__') and attr.endswith('__') + + + class _SpecialGenericAlias(typing._SpecialGenericAlias, _root=True): + def __init__(self, origin, nparams, *, inst=True, name=None, defaults=()): + super().__init__(origin, nparams, inst=inst, name=name) + self._defaults = defaults + + def __setattr__(self, attr, val): + allowed_attrs = {'_name', '_inst', '_nparams', '_defaults'} + if _is_dunder(attr) or attr in allowed_attrs: + object.__setattr__(self, attr, val) + else: + setattr(self.__origin__, attr, val) + + @typing._tp_cache + def __getitem__(self, params): + if not isinstance(params, tuple): + params = (params,) + msg = "Parameters to generic types must be types." + params = tuple(typing._type_check(p, msg) for p in params) + if ( + self._defaults + and len(params) < self._nparams + and len(params) + len(self._defaults) >= self._nparams + ): + params = (*params, *self._defaults[len(params) - self._nparams:]) + actual_len = len(params) + + if actual_len != self._nparams: + if self._defaults: + expected = f"at least {self._nparams - len(self._defaults)}" + else: + expected = str(self._nparams) + if not self._nparams: + raise TypeError(f"{self} is not a generic class") + raise TypeError( + f"Too {'many' if actual_len > self._nparams else 'few'}" + f" arguments for {self};" + f" actual {actual_len}, expected {expected}" + ) + return self.copy_with(params) + + _NoneType = type(None) + Generator = _SpecialGenericAlias( + collections.abc.Generator, 3, defaults=(_NoneType, _NoneType) + ) + AsyncGenerator = _SpecialGenericAlias( + collections.abc.AsyncGenerator, 2, defaults=(_NoneType,) + ) + ContextManager = _SpecialGenericAlias( + contextlib.AbstractContextManager, + 2, + name="ContextManager", + defaults=(typing.Optional[bool],) + ) + AsyncContextManager = _SpecialGenericAlias( + contextlib.AbstractAsyncContextManager, + 2, + name="AsyncContextManager", + defaults=(typing.Optional[bool],) + ) + + +_PROTO_ALLOWLIST = { + 'collections.abc': [ + 'Callable', 'Awaitable', 'Iterable', 'Iterator', 'AsyncIterable', + 'Hashable', 'Sized', 'Container', 'Collection', 'Reversible', 'Buffer', + ], + 'contextlib': ['AbstractContextManager', 'AbstractAsyncContextManager'], + 'typing_extensions': ['Buffer'], +} + + +_EXCLUDED_ATTRS = frozenset(typing.EXCLUDED_ATTRIBUTES) | { + "__match_args__", "__protocol_attrs__", "__non_callable_proto_members__", + "__final__", +} + + +def _get_protocol_attrs(cls): + attrs = set() + for base in cls.__mro__[:-1]: # without object + if base.__name__ in {'Protocol', 'Generic'}: + continue + annotations = getattr(base, '__annotations__', {}) + for attr in (*base.__dict__, *annotations): + if (not attr.startswith('_abc_') and attr not in _EXCLUDED_ATTRS): + attrs.add(attr) + return attrs + + +def _caller(depth=1, default='__main__'): + try: + return sys._getframemodulename(depth + 1) or default + except AttributeError: # For platforms without _getframemodulename() + pass + try: + return sys._getframe(depth + 1).f_globals.get('__name__', default) + except (AttributeError, ValueError): # For platforms without _getframe() + pass + return None + + +# `__match_args__` attribute was removed from protocol members in 3.13, +# we want to backport this change to older Python versions. +# Breakpoint: https://github.com/python/cpython/pull/110683 +if sys.version_info >= (3, 13): + Protocol = typing.Protocol +else: + def _allow_reckless_class_checks(depth=2): + """Allow instance and class checks for special stdlib modules. + The abc and functools modules indiscriminately call isinstance() and + issubclass() on the whole MRO of a user class, which may contain protocols. + """ + return _caller(depth) in {'abc', 'functools', None} + + def _no_init(self, *args, **kwargs): + if type(self)._is_protocol: + raise TypeError('Protocols cannot be instantiated') + + def _type_check_issubclass_arg_1(arg): + """Raise TypeError if `arg` is not an instance of `type` + in `issubclass(arg, )`. + + In most cases, this is verified by type.__subclasscheck__. + Checking it again unnecessarily would slow down issubclass() checks, + so, we don't perform this check unless we absolutely have to. + + For various error paths, however, + we want to ensure that *this* error message is shown to the user + where relevant, rather than a typing.py-specific error message. + """ + if not isinstance(arg, type): + # Same error message as for issubclass(1, int). + raise TypeError('issubclass() arg 1 must be a class') + + # Inheriting from typing._ProtocolMeta isn't actually desirable, + # but is necessary to allow typing.Protocol and typing_extensions.Protocol + # to mix without getting TypeErrors about "metaclass conflict" + class _ProtocolMeta(type(typing.Protocol)): + # This metaclass is somewhat unfortunate, + # but is necessary for several reasons... + # + # NOTE: DO NOT call super() in any methods in this class + # That would call the methods on typing._ProtocolMeta on Python <=3.11 + # and those are slow + def __new__(mcls, name, bases, namespace, **kwargs): + if name == "Protocol" and len(bases) < 2: + pass + elif {Protocol, typing.Protocol} & set(bases): + for base in bases: + if not ( + base in {object, typing.Generic, Protocol, typing.Protocol} + or base.__name__ in _PROTO_ALLOWLIST.get(base.__module__, []) + or is_protocol(base) + ): + raise TypeError( + f"Protocols can only inherit from other protocols, " + f"got {base!r}" + ) + return abc.ABCMeta.__new__(mcls, name, bases, namespace, **kwargs) + + def __init__(cls, *args, **kwargs): + abc.ABCMeta.__init__(cls, *args, **kwargs) + if getattr(cls, "_is_protocol", False): + cls.__protocol_attrs__ = _get_protocol_attrs(cls) + + def __subclasscheck__(cls, other): + if cls is Protocol: + return type.__subclasscheck__(cls, other) + if ( + getattr(cls, '_is_protocol', False) + and not _allow_reckless_class_checks() + ): + if not getattr(cls, '_is_runtime_protocol', False): + _type_check_issubclass_arg_1(other) + raise TypeError( + "Instance and class checks can only be used with " + "@runtime_checkable protocols" + ) + if ( + # this attribute is set by @runtime_checkable: + cls.__non_callable_proto_members__ + and cls.__dict__.get("__subclasshook__") is _proto_hook + ): + _type_check_issubclass_arg_1(other) + non_method_attrs = sorted(cls.__non_callable_proto_members__) + raise TypeError( + "Protocols with non-method members don't support issubclass()." + f" Non-method members: {str(non_method_attrs)[1:-1]}." + ) + return abc.ABCMeta.__subclasscheck__(cls, other) + + def __instancecheck__(cls, instance): + # We need this method for situations where attributes are + # assigned in __init__. + if cls is Protocol: + return type.__instancecheck__(cls, instance) + if not getattr(cls, "_is_protocol", False): + # i.e., it's a concrete subclass of a protocol + return abc.ABCMeta.__instancecheck__(cls, instance) + + if ( + not getattr(cls, '_is_runtime_protocol', False) and + not _allow_reckless_class_checks() + ): + raise TypeError("Instance and class checks can only be used with" + " @runtime_checkable protocols") + + if abc.ABCMeta.__instancecheck__(cls, instance): + return True + + for attr in cls.__protocol_attrs__: + try: + val = inspect.getattr_static(instance, attr) + except AttributeError: + break + # this attribute is set by @runtime_checkable: + if val is None and attr not in cls.__non_callable_proto_members__: + break + else: + return True + + return False + + def __eq__(cls, other): + # Hack so that typing.Generic.__class_getitem__ + # treats typing_extensions.Protocol + # as equivalent to typing.Protocol + if abc.ABCMeta.__eq__(cls, other) is True: + return True + return cls is Protocol and other is typing.Protocol + + # This has to be defined, or the abc-module cache + # complains about classes with this metaclass being unhashable, + # if we define only __eq__! + def __hash__(cls) -> int: + return type.__hash__(cls) + + @classmethod + def _proto_hook(cls, other): + if not cls.__dict__.get('_is_protocol', False): + return NotImplemented + + for attr in cls.__protocol_attrs__: + for base in other.__mro__: + # Check if the members appears in the class dictionary... + if attr in base.__dict__: + if base.__dict__[attr] is None: + return NotImplemented + break + + # ...or in annotations, if it is a sub-protocol. + annotations = getattr(base, '__annotations__', {}) + if ( + isinstance(annotations, collections.abc.Mapping) + and attr in annotations + and is_protocol(other) + ): + break + else: + return NotImplemented + return True + + class Protocol(typing.Generic, metaclass=_ProtocolMeta): + __doc__ = typing.Protocol.__doc__ + __slots__ = () + _is_protocol = True + _is_runtime_protocol = False + + def __init_subclass__(cls, *args, **kwargs): + super().__init_subclass__(*args, **kwargs) + + # Determine if this is a protocol or a concrete subclass. + if not cls.__dict__.get('_is_protocol', False): + cls._is_protocol = any(b is Protocol for b in cls.__bases__) + + # Set (or override) the protocol subclass hook. + if '__subclasshook__' not in cls.__dict__: + cls.__subclasshook__ = _proto_hook + + # Prohibit instantiation for protocol classes + if cls._is_protocol and cls.__init__ is Protocol.__init__: + cls.__init__ = _no_init + + +# Breakpoint: https://github.com/python/cpython/pull/113401 +if sys.version_info >= (3, 13): + runtime_checkable = typing.runtime_checkable +else: + def runtime_checkable(cls): + """Mark a protocol class as a runtime protocol. + + Such protocol can be used with isinstance() and issubclass(). + Raise TypeError if applied to a non-protocol class. + This allows a simple-minded structural check very similar to + one trick ponies in collections.abc such as Iterable. + + For example:: + + @runtime_checkable + class Closable(Protocol): + def close(self): ... + + assert isinstance(open('/some/file'), Closable) + + Warning: this will check only the presence of the required methods, + not their type signatures! + """ + if not issubclass(cls, typing.Generic) or not getattr(cls, '_is_protocol', False): + raise TypeError(f'@runtime_checkable can be only applied to protocol classes,' + f' got {cls!r}') + cls._is_runtime_protocol = True + + # typing.Protocol classes on <=3.11 break if we execute this block, + # because typing.Protocol classes on <=3.11 don't have a + # `__protocol_attrs__` attribute, and this block relies on the + # `__protocol_attrs__` attribute. Meanwhile, typing.Protocol classes on 3.12.2+ + # break if we *don't* execute this block, because *they* assume that all + # protocol classes have a `__non_callable_proto_members__` attribute + # (which this block sets) + if isinstance(cls, _ProtocolMeta) or sys.version_info >= (3, 12, 2): + # PEP 544 prohibits using issubclass() + # with protocols that have non-method members. + # See gh-113320 for why we compute this attribute here, + # rather than in `_ProtocolMeta.__init__` + cls.__non_callable_proto_members__ = set() + for attr in cls.__protocol_attrs__: + try: + is_callable = callable(getattr(cls, attr, None)) + except Exception as e: + raise TypeError( + f"Failed to determine whether protocol member {attr!r} " + "is a method member" + ) from e + else: + if not is_callable: + cls.__non_callable_proto_members__.add(attr) + + return cls + + +# The "runtime" alias exists for backwards compatibility. +runtime = runtime_checkable + + +# Our version of runtime-checkable protocols is faster on Python <=3.11 +# Breakpoint: https://github.com/python/cpython/pull/112717 +if sys.version_info >= (3, 12): + SupportsInt = typing.SupportsInt + SupportsFloat = typing.SupportsFloat + SupportsComplex = typing.SupportsComplex + SupportsBytes = typing.SupportsBytes + SupportsIndex = typing.SupportsIndex + SupportsAbs = typing.SupportsAbs + SupportsRound = typing.SupportsRound +else: + @runtime_checkable + class SupportsInt(Protocol): + """An ABC with one abstract method __int__.""" + __slots__ = () + + @abc.abstractmethod + def __int__(self) -> int: + pass + + @runtime_checkable + class SupportsFloat(Protocol): + """An ABC with one abstract method __float__.""" + __slots__ = () + + @abc.abstractmethod + def __float__(self) -> float: + pass + + @runtime_checkable + class SupportsComplex(Protocol): + """An ABC with one abstract method __complex__.""" + __slots__ = () + + @abc.abstractmethod + def __complex__(self) -> complex: + pass + + @runtime_checkable + class SupportsBytes(Protocol): + """An ABC with one abstract method __bytes__.""" + __slots__ = () + + @abc.abstractmethod + def __bytes__(self) -> bytes: + pass + + @runtime_checkable + class SupportsIndex(Protocol): + __slots__ = () + + @abc.abstractmethod + def __index__(self) -> int: + pass + + @runtime_checkable + class SupportsAbs(Protocol[T_co]): + """ + An ABC with one abstract method __abs__ that is covariant in its return type. + """ + __slots__ = () + + @abc.abstractmethod + def __abs__(self) -> T_co: + pass + + @runtime_checkable + class SupportsRound(Protocol[T_co]): + """ + An ABC with one abstract method __round__ that is covariant in its return type. + """ + __slots__ = () + + @abc.abstractmethod + def __round__(self, ndigits: int = 0) -> T_co: + pass + + +if hasattr(io, "Reader") and hasattr(io, "Writer"): + Reader = io.Reader + Writer = io.Writer +else: + @runtime_checkable + class Reader(Protocol[T_co]): + """Protocol for simple I/O reader instances. + + This protocol only supports blocking I/O. + """ + + __slots__ = () + + @abc.abstractmethod + def read(self, size: int = ..., /) -> T_co: + """Read data from the input stream and return it. + + If *size* is specified, at most *size* items (bytes/characters) will be + read. + """ + + @runtime_checkable + class Writer(Protocol[T_contra]): + """Protocol for simple I/O writer instances. + + This protocol only supports blocking I/O. + """ + + __slots__ = () + + @abc.abstractmethod + def write(self, data: T_contra, /) -> int: + """Write *data* to the output stream and return the number of items written.""" # noqa: E501 + + +_NEEDS_SINGLETONMETA = ( + not hasattr(typing, "NoDefault") or not hasattr(typing, "NoExtraItems") +) + +if _NEEDS_SINGLETONMETA: + class SingletonMeta(type): + def __setattr__(cls, attr, value): + # TypeError is consistent with the behavior of NoneType + raise TypeError( + f"cannot set {attr!r} attribute of immutable type {cls.__name__!r}" + ) + + +if hasattr(typing, "NoDefault"): + NoDefault = typing.NoDefault +else: + class NoDefaultType(metaclass=SingletonMeta): + """The type of the NoDefault singleton.""" + + __slots__ = () + + def __new__(cls): + return globals().get("NoDefault") or object.__new__(cls) + + def __repr__(self): + return "typing_extensions.NoDefault" + + def __reduce__(self): + return "NoDefault" + + NoDefault = NoDefaultType() + del NoDefaultType + +if hasattr(typing, "NoExtraItems"): + NoExtraItems = typing.NoExtraItems +else: + class NoExtraItemsType(metaclass=SingletonMeta): + """The type of the NoExtraItems singleton.""" + + __slots__ = () + + def __new__(cls): + return globals().get("NoExtraItems") or object.__new__(cls) + + def __repr__(self): + return "typing_extensions.NoExtraItems" + + def __reduce__(self): + return "NoExtraItems" + + NoExtraItems = NoExtraItemsType() + del NoExtraItemsType + +if _NEEDS_SINGLETONMETA: + del SingletonMeta + + +# Update this to something like >=3.13.0b1 if and when +# PEP 728 is implemented in CPython +_PEP_728_IMPLEMENTED = False + +if _PEP_728_IMPLEMENTED: + # The standard library TypedDict in Python 3.9.0/1 does not honour the "total" + # keyword with old-style TypedDict(). See https://bugs.python.org/issue42059 + # The standard library TypedDict below Python 3.11 does not store runtime + # information about optional and required keys when using Required or NotRequired. + # Generic TypedDicts are also impossible using typing.TypedDict on Python <3.11. + # Aaaand on 3.12 we add __orig_bases__ to TypedDict + # to enable better runtime introspection. + # On 3.13 we deprecate some odd ways of creating TypedDicts. + # Also on 3.13, PEP 705 adds the ReadOnly[] qualifier. + # PEP 728 (still pending) makes more changes. + TypedDict = typing.TypedDict + _TypedDictMeta = typing._TypedDictMeta + is_typeddict = typing.is_typeddict +else: + # 3.10.0 and later + _TAKES_MODULE = "module" in inspect.signature(typing._type_check).parameters + + def _get_typeddict_qualifiers(annotation_type): + while True: + annotation_origin = get_origin(annotation_type) + if annotation_origin is Annotated: + annotation_args = get_args(annotation_type) + if annotation_args: + annotation_type = annotation_args[0] + else: + break + elif annotation_origin is Required: + yield Required + annotation_type, = get_args(annotation_type) + elif annotation_origin is NotRequired: + yield NotRequired + annotation_type, = get_args(annotation_type) + elif annotation_origin is ReadOnly: + yield ReadOnly + annotation_type, = get_args(annotation_type) + else: + break + + class _TypedDictMeta(type): + + def __new__(cls, name, bases, ns, *, total=True, closed=None, + extra_items=NoExtraItems): + """Create new typed dict class object. + + This method is called when TypedDict is subclassed, + or when TypedDict is instantiated. This way + TypedDict supports all three syntax forms described in its docstring. + Subclasses and instances of TypedDict return actual dictionaries. + """ + for base in bases: + if type(base) is not _TypedDictMeta and base is not typing.Generic: + raise TypeError('cannot inherit from both a TypedDict type ' + 'and a non-TypedDict base class') + if closed is not None and extra_items is not NoExtraItems: + raise TypeError(f"Cannot combine closed={closed!r} and extra_items") + + if any(issubclass(b, typing.Generic) for b in bases): + generic_base = (typing.Generic,) + else: + generic_base = () + + ns_annotations = ns.pop('__annotations__', None) + + # typing.py generally doesn't let you inherit from plain Generic, unless + # the name of the class happens to be "Protocol" + tp_dict = type.__new__(_TypedDictMeta, "Protocol", (*generic_base, dict), ns) + tp_dict.__name__ = name + if tp_dict.__qualname__ == "Protocol": + tp_dict.__qualname__ = name + + if not hasattr(tp_dict, '__orig_bases__'): + tp_dict.__orig_bases__ = bases + + annotations = {} + own_annotate = None + if ns_annotations is not None: + own_annotations = ns_annotations + elif sys.version_info >= (3, 14): + if hasattr(annotationlib, "get_annotate_from_class_namespace"): + own_annotate = annotationlib.get_annotate_from_class_namespace(ns) + else: + # 3.14.0a7 and earlier + own_annotate = ns.get("__annotate__") + if own_annotate is not None: + own_annotations = annotationlib.call_annotate_function( + own_annotate, Format.FORWARDREF, owner=tp_dict + ) + else: + own_annotations = {} + else: + own_annotations = {} + msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type" + if _TAKES_MODULE: + own_checked_annotations = { + n: typing._type_check(tp, msg, module=tp_dict.__module__) + for n, tp in own_annotations.items() + } + else: + own_checked_annotations = { + n: typing._type_check(tp, msg) + for n, tp in own_annotations.items() + } + required_keys = set() + optional_keys = set() + readonly_keys = set() + mutable_keys = set() + extra_items_type = extra_items + + for base in bases: + base_dict = base.__dict__ + + if sys.version_info <= (3, 14): + annotations.update(base_dict.get('__annotations__', {})) + required_keys.update(base_dict.get('__required_keys__', ())) + optional_keys.update(base_dict.get('__optional_keys__', ())) + readonly_keys.update(base_dict.get('__readonly_keys__', ())) + mutable_keys.update(base_dict.get('__mutable_keys__', ())) + + # This was specified in an earlier version of PEP 728. Support + # is retained for backwards compatibility, but only for Python + # 3.13 and lower. + if (closed and sys.version_info < (3, 14) + and "__extra_items__" in own_checked_annotations): + annotation_type = own_checked_annotations.pop("__extra_items__") + qualifiers = set(_get_typeddict_qualifiers(annotation_type)) + if Required in qualifiers: + raise TypeError( + "Special key __extra_items__ does not support " + "Required" + ) + if NotRequired in qualifiers: + raise TypeError( + "Special key __extra_items__ does not support " + "NotRequired" + ) + extra_items_type = annotation_type + + annotations.update(own_checked_annotations) + for annotation_key, annotation_type in own_checked_annotations.items(): + qualifiers = set(_get_typeddict_qualifiers(annotation_type)) + + if Required in qualifiers: + required_keys.add(annotation_key) + elif NotRequired in qualifiers: + optional_keys.add(annotation_key) + elif total: + required_keys.add(annotation_key) + else: + optional_keys.add(annotation_key) + if ReadOnly in qualifiers: + mutable_keys.discard(annotation_key) + readonly_keys.add(annotation_key) + else: + mutable_keys.add(annotation_key) + readonly_keys.discard(annotation_key) + + # Breakpoint: https://github.com/python/cpython/pull/119891 + if sys.version_info >= (3, 14): + def __annotate__(format): + annos = {} + for base in bases: + if base is Generic: + continue + base_annotate = base.__annotate__ + if base_annotate is None: + continue + base_annos = annotationlib.call_annotate_function( + base_annotate, format, owner=base) + annos.update(base_annos) + if own_annotate is not None: + own = annotationlib.call_annotate_function( + own_annotate, format, owner=tp_dict) + if format != Format.STRING: + own = { + n: typing._type_check(tp, msg, module=tp_dict.__module__) + for n, tp in own.items() + } + elif format == Format.STRING: + own = annotationlib.annotations_to_string(own_annotations) + elif format in (Format.FORWARDREF, Format.VALUE): + own = own_checked_annotations + else: + raise NotImplementedError(format) + annos.update(own) + return annos + + tp_dict.__annotate__ = __annotate__ + else: + tp_dict.__annotations__ = annotations + tp_dict.__required_keys__ = frozenset(required_keys) + tp_dict.__optional_keys__ = frozenset(optional_keys) + tp_dict.__readonly_keys__ = frozenset(readonly_keys) + tp_dict.__mutable_keys__ = frozenset(mutable_keys) + tp_dict.__total__ = total + tp_dict.__closed__ = closed + tp_dict.__extra_items__ = extra_items_type + return tp_dict + + __call__ = dict # static method + + def __subclasscheck__(cls, other): + # Typed dicts are only for static structural subtyping. + raise TypeError('TypedDict does not support instance and class checks') + + __instancecheck__ = __subclasscheck__ + + _TypedDict = type.__new__(_TypedDictMeta, 'TypedDict', (), {}) + + def _create_typeddict( + typename, + fields, + /, + *, + typing_is_inline, + total, + closed, + extra_items, + **kwargs, + ): + if fields is _marker or fields is None: + if fields is _marker: + deprecated_thing = ( + "Failing to pass a value for the 'fields' parameter" + ) + else: + deprecated_thing = "Passing `None` as the 'fields' parameter" + + example = f"`{typename} = TypedDict({typename!r}, {{}})`" + deprecation_msg = ( + f"{deprecated_thing} is deprecated and will be disallowed in " + "Python 3.15. To create a TypedDict class with 0 fields " + "using the functional syntax, pass an empty dictionary, e.g. " + ) + example + "." + warnings.warn(deprecation_msg, DeprecationWarning, stacklevel=2) + # Support a field called "closed" + if closed is not False and closed is not True and closed is not None: + kwargs["closed"] = closed + closed = None + # Or "extra_items" + if extra_items is not NoExtraItems: + kwargs["extra_items"] = extra_items + extra_items = NoExtraItems + fields = kwargs + elif kwargs: + raise TypeError("TypedDict takes either a dict or keyword arguments," + " but not both") + if kwargs: + # Breakpoint: https://github.com/python/cpython/pull/104891 + if sys.version_info >= (3, 13): + raise TypeError("TypedDict takes no keyword arguments") + warnings.warn( + "The kwargs-based syntax for TypedDict definitions is deprecated " + "in Python 3.11, will be removed in Python 3.13, and may not be " + "understood by third-party type checkers.", + DeprecationWarning, + stacklevel=2, + ) + + ns = {'__annotations__': dict(fields)} + module = _caller(depth=4 if typing_is_inline else 2) + if module is not None: + # Setting correct module is necessary to make typed dict classes + # pickleable. + ns['__module__'] = module + + td = _TypedDictMeta(typename, (), ns, total=total, closed=closed, + extra_items=extra_items) + td.__orig_bases__ = (TypedDict,) + return td + + class _TypedDictSpecialForm(_SpecialForm, _root=True): + def __call__( + self, + typename, + fields=_marker, + /, + *, + total=True, + closed=None, + extra_items=NoExtraItems, + **kwargs + ): + return _create_typeddict( + typename, + fields, + typing_is_inline=False, + total=total, + closed=closed, + extra_items=extra_items, + **kwargs, + ) + + def __mro_entries__(self, bases): + return (_TypedDict,) + + @_TypedDictSpecialForm + def TypedDict(self, args): + """A simple typed namespace. At runtime it is equivalent to a plain dict. + + TypedDict creates a dictionary type such that a type checker will expect all + instances to have a certain set of keys, where each key is + associated with a value of a consistent type. This expectation + is not checked at runtime. + + Usage:: + + class Point2D(TypedDict): + x: int + y: int + label: str + + a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK + b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check + + assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first') + + The type info can be accessed via the Point2D.__annotations__ dict, and + the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets. + TypedDict supports an additional equivalent form:: + + Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str}) + + By default, all keys must be present in a TypedDict. It is possible + to override this by specifying totality:: + + class Point2D(TypedDict, total=False): + x: int + y: int + + This means that a Point2D TypedDict can have any of the keys omitted. A type + checker is only expected to support a literal False or True as the value of + the total argument. True is the default, and makes all items defined in the + class body be required. + + The Required and NotRequired special forms can also be used to mark + individual keys as being required or not required:: + + class Point2D(TypedDict): + x: int # the "x" key must always be present (Required is the default) + y: NotRequired[int] # the "y" key can be omitted + + See PEP 655 for more details on Required and NotRequired. + """ + # This runs when creating inline TypedDicts: + if not isinstance(args, dict): + raise TypeError( + "TypedDict[...] should be used with a single dict argument" + ) + + return _create_typeddict( + "", + args, + typing_is_inline=True, + total=True, + closed=True, + extra_items=NoExtraItems, + ) + + _TYPEDDICT_TYPES = (typing._TypedDictMeta, _TypedDictMeta) + + def is_typeddict(tp): + """Check if an annotation is a TypedDict class + + For example:: + class Film(TypedDict): + title: str + year: int + + is_typeddict(Film) # => True + is_typeddict(Union[list, str]) # => False + """ + return isinstance(tp, _TYPEDDICT_TYPES) + + +if hasattr(typing, "assert_type"): + assert_type = typing.assert_type + +else: + def assert_type(val, typ, /): + """Assert (to the type checker) that the value is of the given type. + + When the type checker encounters a call to assert_type(), it + emits an error if the value is not of the specified type:: + + def greet(name: str) -> None: + assert_type(name, str) # ok + assert_type(name, int) # type checker error + + At runtime this returns the first argument unchanged and otherwise + does nothing. + """ + return val + + +if hasattr(typing, "ReadOnly"): # 3.13+ + get_type_hints = typing.get_type_hints +else: # <=3.13 + # replaces _strip_annotations() + def _strip_extras(t): + """Strips Annotated, Required and NotRequired from a given type.""" + if isinstance(t, typing._AnnotatedAlias): + return _strip_extras(t.__origin__) + if hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired, ReadOnly): + return _strip_extras(t.__args__[0]) + if isinstance(t, typing._GenericAlias): + stripped_args = tuple(_strip_extras(a) for a in t.__args__) + if stripped_args == t.__args__: + return t + return t.copy_with(stripped_args) + if hasattr(_types, "GenericAlias") and isinstance(t, _types.GenericAlias): + stripped_args = tuple(_strip_extras(a) for a in t.__args__) + if stripped_args == t.__args__: + return t + return _types.GenericAlias(t.__origin__, stripped_args) + if hasattr(_types, "UnionType") and isinstance(t, _types.UnionType): + stripped_args = tuple(_strip_extras(a) for a in t.__args__) + if stripped_args == t.__args__: + return t + return functools.reduce(operator.or_, stripped_args) + + return t + + def get_type_hints(obj, globalns=None, localns=None, include_extras=False): + """Return type hints for an object. + + This is often the same as obj.__annotations__, but it handles + forward references encoded as string literals, adds Optional[t] if a + default value equal to None is set and recursively replaces all + 'Annotated[T, ...]', 'Required[T]' or 'NotRequired[T]' with 'T' + (unless 'include_extras=True'). + + The argument may be a module, class, method, or function. The annotations + are returned as a dictionary. For classes, annotations include also + inherited members. + + TypeError is raised if the argument is not of a type that can contain + annotations, and an empty dictionary is returned if no annotations are + present. + + BEWARE -- the behavior of globalns and localns is counterintuitive + (unless you are familiar with how eval() and exec() work). The + search order is locals first, then globals. + + - If no dict arguments are passed, an attempt is made to use the + globals from obj (or the respective module's globals for classes), + and these are also used as the locals. If the object does not appear + to have globals, an empty dictionary is used. + + - If one dict argument is passed, it is used for both globals and + locals. + + - If two dict arguments are passed, they specify globals and + locals, respectively. + """ + hint = typing.get_type_hints( + obj, globalns=globalns, localns=localns, include_extras=True + ) + # Breakpoint: https://github.com/python/cpython/pull/30304 + if sys.version_info < (3, 11): + _clean_optional(obj, hint, globalns, localns) + if include_extras: + return hint + return {k: _strip_extras(t) for k, t in hint.items()} + + _NoneType = type(None) + + def _could_be_inserted_optional(t): + """detects Union[..., None] pattern""" + if not isinstance(t, typing._UnionGenericAlias): + return False + # Assume if last argument is not None they are user defined + if t.__args__[-1] is not _NoneType: + return False + return True + + # < 3.11 + def _clean_optional(obj, hints, globalns=None, localns=None): + # reverts injected Union[..., None] cases from typing.get_type_hints + # when a None default value is used. + # see https://github.com/python/typing_extensions/issues/310 + if not hints or isinstance(obj, type): + return + defaults = typing._get_defaults(obj) # avoid accessing __annotations___ + if not defaults: + return + original_hints = obj.__annotations__ + for name, value in hints.items(): + # Not a Union[..., None] or replacement conditions not fullfilled + if (not _could_be_inserted_optional(value) + or name not in defaults + or defaults[name] is not None + ): + continue + original_value = original_hints[name] + # value=NoneType should have caused a skip above but check for safety + if original_value is None: + original_value = _NoneType + # Forward reference + if isinstance(original_value, str): + if globalns is None: + if isinstance(obj, _types.ModuleType): + globalns = obj.__dict__ + else: + nsobj = obj + # Find globalns for the unwrapped object. + while hasattr(nsobj, '__wrapped__'): + nsobj = nsobj.__wrapped__ + globalns = getattr(nsobj, '__globals__', {}) + if localns is None: + localns = globalns + elif localns is None: + localns = globalns + + original_value = ForwardRef( + original_value, + is_argument=not isinstance(obj, _types.ModuleType) + ) + original_evaluated = typing._eval_type(original_value, globalns, localns) + # Compare if values differ. Note that even if equal + # value might be cached by typing._tp_cache contrary to original_evaluated + if original_evaluated != value or ( + # 3.10: ForwardRefs of UnionType might be turned into _UnionGenericAlias + hasattr(_types, "UnionType") + and isinstance(original_evaluated, _types.UnionType) + and not isinstance(value, _types.UnionType) + ): + hints[name] = original_evaluated + +# Python 3.9 has get_origin() and get_args() but those implementations don't support +# ParamSpecArgs and ParamSpecKwargs, so only Python 3.10's versions will do. +# Breakpoint: https://github.com/python/cpython/pull/25298 +if sys.version_info >= (3, 10): + get_origin = typing.get_origin + get_args = typing.get_args +# 3.9 +else: + def get_origin(tp): + """Get the unsubscripted version of a type. + + This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar + and Annotated. Return None for unsupported types. Examples:: + + get_origin(Literal[42]) is Literal + get_origin(int) is None + get_origin(ClassVar[int]) is ClassVar + get_origin(Generic) is Generic + get_origin(Generic[T]) is Generic + get_origin(Union[T, int]) is Union + get_origin(List[Tuple[T, T]][int]) == list + get_origin(P.args) is P + """ + if isinstance(tp, typing._AnnotatedAlias): + return Annotated + if isinstance(tp, (typing._BaseGenericAlias, _types.GenericAlias, + ParamSpecArgs, ParamSpecKwargs)): + return tp.__origin__ + if tp is typing.Generic: + return typing.Generic + return None + + def get_args(tp): + """Get type arguments with all substitutions performed. + + For unions, basic simplifications used by Union constructor are performed. + Examples:: + get_args(Dict[str, int]) == (str, int) + get_args(int) == () + get_args(Union[int, Union[T, int], str][int]) == (int, str) + get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int]) + get_args(Callable[[], T][int]) == ([], int) + """ + if isinstance(tp, typing._AnnotatedAlias): + return (tp.__origin__, *tp.__metadata__) + if isinstance(tp, (typing._GenericAlias, _types.GenericAlias)): + res = tp.__args__ + if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis: + res = (list(res[:-1]), res[-1]) + return res + return () + + +# 3.10+ +if hasattr(typing, 'TypeAlias'): + TypeAlias = typing.TypeAlias +# 3.9 +else: + @_ExtensionsSpecialForm + def TypeAlias(self, parameters): + """Special marker indicating that an assignment should + be recognized as a proper type alias definition by type + checkers. + + For example:: + + Predicate: TypeAlias = Callable[..., bool] + + It's invalid when used anywhere except as in the example above. + """ + raise TypeError(f"{self} is not subscriptable") + + +def _set_default(type_param, default): + type_param.has_default = lambda: default is not NoDefault + type_param.__default__ = default + + +def _set_module(typevarlike): + # for pickling: + def_mod = _caller(depth=2) + if def_mod != 'typing_extensions': + typevarlike.__module__ = def_mod + + +class _DefaultMixin: + """Mixin for TypeVarLike defaults.""" + + __slots__ = () + __init__ = _set_default + + +# Classes using this metaclass must provide a _backported_typevarlike ClassVar +class _TypeVarLikeMeta(type): + def __instancecheck__(cls, __instance: Any) -> bool: + return isinstance(__instance, cls._backported_typevarlike) + + +if _PEP_696_IMPLEMENTED: + from typing import TypeVar +else: + # Add default and infer_variance parameters from PEP 696 and 695 + class TypeVar(metaclass=_TypeVarLikeMeta): + """Type variable.""" + + _backported_typevarlike = typing.TypeVar + + def __new__(cls, name, *constraints, bound=None, + covariant=False, contravariant=False, + default=NoDefault, infer_variance=False): + if hasattr(typing, "TypeAliasType"): + # PEP 695 implemented (3.12+), can pass infer_variance to typing.TypeVar + typevar = typing.TypeVar(name, *constraints, bound=bound, + covariant=covariant, contravariant=contravariant, + infer_variance=infer_variance) + else: + typevar = typing.TypeVar(name, *constraints, bound=bound, + covariant=covariant, contravariant=contravariant) + if infer_variance and (covariant or contravariant): + raise ValueError("Variance cannot be specified with infer_variance.") + typevar.__infer_variance__ = infer_variance + + _set_default(typevar, default) + _set_module(typevar) + + def _tvar_prepare_subst(alias, args): + if ( + typevar.has_default() + and alias.__parameters__.index(typevar) == len(args) + ): + args += (typevar.__default__,) + return args + + typevar.__typing_prepare_subst__ = _tvar_prepare_subst + return typevar + + def __init_subclass__(cls) -> None: + raise TypeError(f"type '{__name__}.TypeVar' is not an acceptable base type") + + +# Python 3.10+ has PEP 612 +if hasattr(typing, 'ParamSpecArgs'): + ParamSpecArgs = typing.ParamSpecArgs + ParamSpecKwargs = typing.ParamSpecKwargs +# 3.9 +else: + class _Immutable: + """Mixin to indicate that object should not be copied.""" + __slots__ = () + + def __copy__(self): + return self + + def __deepcopy__(self, memo): + return self + + class ParamSpecArgs(_Immutable): + """The args for a ParamSpec object. + + Given a ParamSpec object P, P.args is an instance of ParamSpecArgs. + + ParamSpecArgs objects have a reference back to their ParamSpec: + + P.args.__origin__ is P + + This type is meant for runtime introspection and has no special meaning to + static type checkers. + """ + def __init__(self, origin): + self.__origin__ = origin + + def __repr__(self): + return f"{self.__origin__.__name__}.args" + + def __eq__(self, other): + if not isinstance(other, ParamSpecArgs): + return NotImplemented + return self.__origin__ == other.__origin__ + + class ParamSpecKwargs(_Immutable): + """The kwargs for a ParamSpec object. + + Given a ParamSpec object P, P.kwargs is an instance of ParamSpecKwargs. + + ParamSpecKwargs objects have a reference back to their ParamSpec: + + P.kwargs.__origin__ is P + + This type is meant for runtime introspection and has no special meaning to + static type checkers. + """ + def __init__(self, origin): + self.__origin__ = origin + + def __repr__(self): + return f"{self.__origin__.__name__}.kwargs" + + def __eq__(self, other): + if not isinstance(other, ParamSpecKwargs): + return NotImplemented + return self.__origin__ == other.__origin__ + + +if _PEP_696_IMPLEMENTED: + from typing import ParamSpec + +# 3.10+ +elif hasattr(typing, 'ParamSpec'): + + # Add default parameter - PEP 696 + class ParamSpec(metaclass=_TypeVarLikeMeta): + """Parameter specification.""" + + _backported_typevarlike = typing.ParamSpec + + def __new__(cls, name, *, bound=None, + covariant=False, contravariant=False, + infer_variance=False, default=NoDefault): + if hasattr(typing, "TypeAliasType"): + # PEP 695 implemented, can pass infer_variance to typing.TypeVar + paramspec = typing.ParamSpec(name, bound=bound, + covariant=covariant, + contravariant=contravariant, + infer_variance=infer_variance) + else: + paramspec = typing.ParamSpec(name, bound=bound, + covariant=covariant, + contravariant=contravariant) + paramspec.__infer_variance__ = infer_variance + + _set_default(paramspec, default) + _set_module(paramspec) + + def _paramspec_prepare_subst(alias, args): + params = alias.__parameters__ + i = params.index(paramspec) + if i == len(args) and paramspec.has_default(): + args = [*args, paramspec.__default__] + if i >= len(args): + raise TypeError(f"Too few arguments for {alias}") + # Special case where Z[[int, str, bool]] == Z[int, str, bool] in PEP 612. + if len(params) == 1 and not typing._is_param_expr(args[0]): + assert i == 0 + args = (args,) + # Convert lists to tuples to help other libraries cache the results. + elif isinstance(args[i], list): + args = (*args[:i], tuple(args[i]), *args[i + 1:]) + return args + + paramspec.__typing_prepare_subst__ = _paramspec_prepare_subst + return paramspec + + def __init_subclass__(cls) -> None: + raise TypeError(f"type '{__name__}.ParamSpec' is not an acceptable base type") + +# 3.9 +else: + + # Inherits from list as a workaround for Callable checks in Python < 3.9.2. + class ParamSpec(list, _DefaultMixin): + """Parameter specification variable. + + Usage:: + + P = ParamSpec('P') + + Parameter specification variables exist primarily for the benefit of static + type checkers. They are used to forward the parameter types of one + callable to another callable, a pattern commonly found in higher order + functions and decorators. They are only valid when used in ``Concatenate``, + or s the first argument to ``Callable``. In Python 3.10 and higher, + they are also supported in user-defined Generics at runtime. + See class Generic for more information on generic types. An + example for annotating a decorator:: + + T = TypeVar('T') + P = ParamSpec('P') + + def add_logging(f: Callable[P, T]) -> Callable[P, T]: + '''A type-safe decorator to add logging to a function.''' + def inner(*args: P.args, **kwargs: P.kwargs) -> T: + logging.info(f'{f.__name__} was called') + return f(*args, **kwargs) + return inner + + @add_logging + def add_two(x: float, y: float) -> float: + '''Add two numbers together.''' + return x + y + + Parameter specification variables defined with covariant=True or + contravariant=True can be used to declare covariant or contravariant + generic types. These keyword arguments are valid, but their actual semantics + are yet to be decided. See PEP 612 for details. + + Parameter specification variables can be introspected. e.g.: + + P.__name__ == 'T' + P.__bound__ == None + P.__covariant__ == False + P.__contravariant__ == False + + Note that only parameter specification variables defined in global scope can + be pickled. + """ + + # Trick Generic __parameters__. + __class__ = typing.TypeVar + + @property + def args(self): + return ParamSpecArgs(self) + + @property + def kwargs(self): + return ParamSpecKwargs(self) + + def __init__(self, name, *, bound=None, covariant=False, contravariant=False, + infer_variance=False, default=NoDefault): + list.__init__(self, [self]) + self.__name__ = name + self.__covariant__ = bool(covariant) + self.__contravariant__ = bool(contravariant) + self.__infer_variance__ = bool(infer_variance) + if bound: + self.__bound__ = typing._type_check(bound, 'Bound must be a type.') + else: + self.__bound__ = None + _DefaultMixin.__init__(self, default) + + # for pickling: + def_mod = _caller() + if def_mod != 'typing_extensions': + self.__module__ = def_mod + + def __repr__(self): + if self.__infer_variance__: + prefix = '' + elif self.__covariant__: + prefix = '+' + elif self.__contravariant__: + prefix = '-' + else: + prefix = '~' + return prefix + self.__name__ + + def __hash__(self): + return object.__hash__(self) + + def __eq__(self, other): + return self is other + + def __reduce__(self): + return self.__name__ + + # Hack to get typing._type_check to pass. + def __call__(self, *args, **kwargs): + pass + + +# 3.9 +if not hasattr(typing, 'Concatenate'): + # Inherits from list as a workaround for Callable checks in Python < 3.9.2. + + # 3.9.0-1 + if not hasattr(typing, '_type_convert'): + def _type_convert(arg, module=None, *, allow_special_forms=False): + """For converting None to type(None), and strings to ForwardRef.""" + if arg is None: + return type(None) + if isinstance(arg, str): + if sys.version_info <= (3, 9, 6): + return ForwardRef(arg) + if sys.version_info <= (3, 9, 7): + return ForwardRef(arg, module=module) + return ForwardRef(arg, module=module, is_class=allow_special_forms) + return arg + else: + _type_convert = typing._type_convert + + class _ConcatenateGenericAlias(list): + + # Trick Generic into looking into this for __parameters__. + __class__ = typing._GenericAlias + + def __init__(self, origin, args): + super().__init__(args) + self.__origin__ = origin + self.__args__ = args + + def __repr__(self): + _type_repr = typing._type_repr + return (f'{_type_repr(self.__origin__)}' + f'[{", ".join(_type_repr(arg) for arg in self.__args__)}]') + + def __hash__(self): + return hash((self.__origin__, self.__args__)) + + # Hack to get typing._type_check to pass in Generic. + def __call__(self, *args, **kwargs): + pass + + @property + def __parameters__(self): + return tuple( + tp for tp in self.__args__ if isinstance(tp, (typing.TypeVar, ParamSpec)) + ) + + # 3.9 used by __getitem__ below + def copy_with(self, params): + if isinstance(params[-1], _ConcatenateGenericAlias): + params = (*params[:-1], *params[-1].__args__) + elif isinstance(params[-1], (list, tuple)): + return (*params[:-1], *params[-1]) + elif (not (params[-1] is ... or isinstance(params[-1], ParamSpec))): + raise TypeError("The last parameter to Concatenate should be a " + "ParamSpec variable or ellipsis.") + return self.__class__(self.__origin__, params) + + # 3.9; accessed during GenericAlias.__getitem__ when substituting + def __getitem__(self, args): + if self.__origin__ in (Generic, Protocol): + # Can't subscript Generic[...] or Protocol[...]. + raise TypeError(f"Cannot subscript already-subscripted {self}") + if not self.__parameters__: + raise TypeError(f"{self} is not a generic class") + + if not isinstance(args, tuple): + args = (args,) + args = _unpack_args(*(_type_convert(p) for p in args)) + params = self.__parameters__ + for param in params: + prepare = getattr(param, "__typing_prepare_subst__", None) + if prepare is not None: + args = prepare(self, args) + # 3.9 & typing.ParamSpec + elif isinstance(param, ParamSpec): + i = params.index(param) + if ( + i == len(args) + and getattr(param, '__default__', NoDefault) is not NoDefault + ): + args = [*args, param.__default__] + if i >= len(args): + raise TypeError(f"Too few arguments for {self}") + # Special case for Z[[int, str, bool]] == Z[int, str, bool] + if len(params) == 1 and not _is_param_expr(args[0]): + assert i == 0 + args = (args,) + elif ( + isinstance(args[i], list) + # 3.9 + # This class inherits from list do not convert + and not isinstance(args[i], _ConcatenateGenericAlias) + ): + args = (*args[:i], tuple(args[i]), *args[i + 1:]) + + alen = len(args) + plen = len(params) + if alen != plen: + raise TypeError( + f"Too {'many' if alen > plen else 'few'} arguments for {self};" + f" actual {alen}, expected {plen}" + ) + + subst = dict(zip(self.__parameters__, args)) + # determine new args + new_args = [] + for arg in self.__args__: + if isinstance(arg, type): + new_args.append(arg) + continue + if isinstance(arg, TypeVar): + arg = subst[arg] + if ( + (isinstance(arg, typing._GenericAlias) and _is_unpack(arg)) + or ( + hasattr(_types, "GenericAlias") + and isinstance(arg, _types.GenericAlias) + and getattr(arg, "__unpacked__", False) + ) + ): + raise TypeError(f"{arg} is not valid as type argument") + + elif isinstance(arg, + typing._GenericAlias + if not hasattr(_types, "GenericAlias") else + (typing._GenericAlias, _types.GenericAlias) + ): + subparams = arg.__parameters__ + if subparams: + subargs = tuple(subst[x] for x in subparams) + arg = arg[subargs] + new_args.append(arg) + return self.copy_with(tuple(new_args)) + +# 3.10+ +else: + _ConcatenateGenericAlias = typing._ConcatenateGenericAlias + + # 3.10 + if sys.version_info < (3, 11): + + class _ConcatenateGenericAlias(typing._ConcatenateGenericAlias, _root=True): + # needed for checks in collections.abc.Callable to accept this class + __module__ = "typing" + + def copy_with(self, params): + if isinstance(params[-1], (list, tuple)): + return (*params[:-1], *params[-1]) + if isinstance(params[-1], typing._ConcatenateGenericAlias): + params = (*params[:-1], *params[-1].__args__) + elif not (params[-1] is ... or isinstance(params[-1], ParamSpec)): + raise TypeError("The last parameter to Concatenate should be a " + "ParamSpec variable or ellipsis.") + return super(typing._ConcatenateGenericAlias, self).copy_with(params) + + def __getitem__(self, args): + value = super().__getitem__(args) + if isinstance(value, tuple) and any(_is_unpack(t) for t in value): + return tuple(_unpack_args(*(n for n in value))) + return value + + +# 3.9.2 +class _EllipsisDummy: ... + + +# <=3.10 +def _create_concatenate_alias(origin, parameters): + if parameters[-1] is ... and sys.version_info < (3, 9, 2): + # Hack: Arguments must be types, replace it with one. + parameters = (*parameters[:-1], _EllipsisDummy) + if sys.version_info >= (3, 10, 3): + concatenate = _ConcatenateGenericAlias(origin, parameters, + _typevar_types=(TypeVar, ParamSpec), + _paramspec_tvars=True) + else: + concatenate = _ConcatenateGenericAlias(origin, parameters) + if parameters[-1] is not _EllipsisDummy: + return concatenate + # Remove dummy again + concatenate.__args__ = tuple(p if p is not _EllipsisDummy else ... + for p in concatenate.__args__) + if sys.version_info < (3, 10): + # backport needs __args__ adjustment only + return concatenate + concatenate.__parameters__ = tuple(p for p in concatenate.__parameters__ + if p is not _EllipsisDummy) + return concatenate + + +# <=3.10 +@typing._tp_cache +def _concatenate_getitem(self, parameters): + if parameters == (): + raise TypeError("Cannot take a Concatenate of no types.") + if not isinstance(parameters, tuple): + parameters = (parameters,) + if not (parameters[-1] is ... or isinstance(parameters[-1], ParamSpec)): + raise TypeError("The last parameter to Concatenate should be a " + "ParamSpec variable or ellipsis.") + msg = "Concatenate[arg, ...]: each arg must be a type." + parameters = (*(typing._type_check(p, msg) for p in parameters[:-1]), + parameters[-1]) + return _create_concatenate_alias(self, parameters) + + +# 3.11+; Concatenate does not accept ellipsis in 3.10 +# Breakpoint: https://github.com/python/cpython/pull/30969 +if sys.version_info >= (3, 11): + Concatenate = typing.Concatenate +# <=3.10 +else: + @_ExtensionsSpecialForm + def Concatenate(self, parameters): + """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a + higher order function which adds, removes or transforms parameters of a + callable. + + For example:: + + Callable[Concatenate[int, P], int] + + See PEP 612 for detailed information. + """ + return _concatenate_getitem(self, parameters) + + +# 3.10+ +if hasattr(typing, 'TypeGuard'): + TypeGuard = typing.TypeGuard +# 3.9 +else: + @_ExtensionsSpecialForm + def TypeGuard(self, parameters): + """Special typing form used to annotate the return type of a user-defined + type guard function. ``TypeGuard`` only accepts a single type argument. + At runtime, functions marked this way should return a boolean. + + ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static + type checkers to determine a more precise type of an expression within a + program's code flow. Usually type narrowing is done by analyzing + conditional code flow and applying the narrowing to a block of code. The + conditional expression here is sometimes referred to as a "type guard". + + Sometimes it would be convenient to use a user-defined boolean function + as a type guard. Such a function should use ``TypeGuard[...]`` as its + return type to alert static type checkers to this intention. + + Using ``-> TypeGuard`` tells the static type checker that for a given + function: + + 1. The return value is a boolean. + 2. If the return value is ``True``, the type of its argument + is the type inside ``TypeGuard``. + + For example:: + + def is_str(val: Union[str, float]): + # "isinstance" type guard + if isinstance(val, str): + # Type of ``val`` is narrowed to ``str`` + ... + else: + # Else, type of ``val`` is narrowed to ``float``. + ... + + Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower + form of ``TypeA`` (it can even be a wider form) and this may lead to + type-unsafe results. The main reason is to allow for things like + narrowing ``List[object]`` to ``List[str]`` even though the latter is not + a subtype of the former, since ``List`` is invariant. The responsibility of + writing type-safe type guards is left to the user. + + ``TypeGuard`` also works with type variables. For more information, see + PEP 647 (User-Defined Type Guards). + """ + item = typing._type_check(parameters, f'{self} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + + +# 3.13+ +if hasattr(typing, 'TypeIs'): + TypeIs = typing.TypeIs +# <=3.12 +else: + @_ExtensionsSpecialForm + def TypeIs(self, parameters): + """Special typing form used to annotate the return type of a user-defined + type narrower function. ``TypeIs`` only accepts a single type argument. + At runtime, functions marked this way should return a boolean. + + ``TypeIs`` aims to benefit *type narrowing* -- a technique used by static + type checkers to determine a more precise type of an expression within a + program's code flow. Usually type narrowing is done by analyzing + conditional code flow and applying the narrowing to a block of code. The + conditional expression here is sometimes referred to as a "type guard". + + Sometimes it would be convenient to use a user-defined boolean function + as a type guard. Such a function should use ``TypeIs[...]`` as its + return type to alert static type checkers to this intention. + + Using ``-> TypeIs`` tells the static type checker that for a given + function: + + 1. The return value is a boolean. + 2. If the return value is ``True``, the type of its argument + is the intersection of the type inside ``TypeIs`` and the argument's + previously known type. + + For example:: + + def is_awaitable(val: object) -> TypeIs[Awaitable[Any]]: + return hasattr(val, '__await__') + + def f(val: Union[int, Awaitable[int]]) -> int: + if is_awaitable(val): + assert_type(val, Awaitable[int]) + else: + assert_type(val, int) + + ``TypeIs`` also works with type variables. For more information, see + PEP 742 (Narrowing types with TypeIs). + """ + item = typing._type_check(parameters, f'{self} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + + +# 3.14+? +if hasattr(typing, 'TypeForm'): + TypeForm = typing.TypeForm +# <=3.13 +else: + class _TypeFormForm(_ExtensionsSpecialForm, _root=True): + # TypeForm(X) is equivalent to X but indicates to the type checker + # that the object is a TypeForm. + def __call__(self, obj, /): + return obj + + @_TypeFormForm + def TypeForm(self, parameters): + """A special form representing the value that results from the evaluation + of a type expression. This value encodes the information supplied in the + type expression, and it represents the type described by that type expression. + + When used in a type expression, TypeForm describes a set of type form objects. + It accepts a single type argument, which must be a valid type expression. + ``TypeForm[T]`` describes the set of all type form objects that represent + the type T or types that are assignable to T. + + Usage: + + def cast[T](typ: TypeForm[T], value: Any) -> T: ... + + reveal_type(cast(int, "x")) # int + + See PEP 747 for more information. + """ + item = typing._type_check(parameters, f'{self} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + + + + +if hasattr(typing, "LiteralString"): # 3.11+ + LiteralString = typing.LiteralString +else: + @_SpecialForm + def LiteralString(self, params): + """Represents an arbitrary literal string. + + Example:: + + from typing_extensions import LiteralString + + def query(sql: LiteralString) -> ...: + ... + + query("SELECT * FROM table") # ok + query(f"SELECT * FROM {input()}") # not ok + + See PEP 675 for details. + + """ + raise TypeError(f"{self} is not subscriptable") + + +if hasattr(typing, "Self"): # 3.11+ + Self = typing.Self +else: + @_SpecialForm + def Self(self, params): + """Used to spell the type of "self" in classes. + + Example:: + + from typing import Self + + class ReturnsSelf: + def parse(self, data: bytes) -> Self: + ... + return self + + """ + + raise TypeError(f"{self} is not subscriptable") + + +if hasattr(typing, "Never"): # 3.11+ + Never = typing.Never +else: + @_SpecialForm + def Never(self, params): + """The bottom type, a type that has no members. + + This can be used to define a function that should never be + called, or a function that never returns:: + + from typing_extensions import Never + + def never_call_me(arg: Never) -> None: + pass + + def int_or_str(arg: int | str) -> None: + never_call_me(arg) # type checker error + match arg: + case int(): + print("It's an int") + case str(): + print("It's a str") + case _: + never_call_me(arg) # ok, arg is of type Never + + """ + + raise TypeError(f"{self} is not subscriptable") + + +if hasattr(typing, 'Required'): # 3.11+ + Required = typing.Required + NotRequired = typing.NotRequired +else: # <=3.10 + @_ExtensionsSpecialForm + def Required(self, parameters): + """A special typing construct to mark a key of a total=False TypedDict + as required. For example: + + class Movie(TypedDict, total=False): + title: Required[str] + year: int + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + + There is no runtime checking that a required key is actually provided + when instantiating a related TypedDict. + """ + item = typing._type_check(parameters, f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + + @_ExtensionsSpecialForm + def NotRequired(self, parameters): + """A special typing construct to mark a key of a TypedDict as + potentially missing. For example: + + class Movie(TypedDict): + title: str + year: NotRequired[int] + + m = Movie( + title='The Matrix', # typechecker error if key is omitted + year=1999, + ) + """ + item = typing._type_check(parameters, f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + + +if hasattr(typing, 'ReadOnly'): + ReadOnly = typing.ReadOnly +else: # <=3.12 + @_ExtensionsSpecialForm + def ReadOnly(self, parameters): + """A special typing construct to mark an item of a TypedDict as read-only. + + For example: + + class Movie(TypedDict): + title: ReadOnly[str] + year: int + + def mutate_movie(m: Movie) -> None: + m["year"] = 1992 # allowed + m["title"] = "The Matrix" # typechecker error + + There is no runtime checking for this property. + """ + item = typing._type_check(parameters, f'{self._name} accepts only a single type.') + return typing._GenericAlias(self, (item,)) + + +_UNPACK_DOC = """\ +Type unpack operator. + +The type unpack operator takes the child types from some container type, +such as `tuple[int, str]` or a `TypeVarTuple`, and 'pulls them out'. For +example: + + # For some generic class `Foo`: + Foo[Unpack[tuple[int, str]]] # Equivalent to Foo[int, str] + + Ts = TypeVarTuple('Ts') + # Specifies that `Bar` is generic in an arbitrary number of types. + # (Think of `Ts` as a tuple of an arbitrary number of individual + # `TypeVar`s, which the `Unpack` is 'pulling out' directly into the + # `Generic[]`.) + class Bar(Generic[Unpack[Ts]]): ... + Bar[int] # Valid + Bar[int, str] # Also valid + +From Python 3.11, this can also be done using the `*` operator: + + Foo[*tuple[int, str]] + class Bar(Generic[*Ts]): ... + +The operator can also be used along with a `TypedDict` to annotate +`**kwargs` in a function signature. For instance: + + class Movie(TypedDict): + name: str + year: int + + # This function expects two keyword arguments - *name* of type `str` and + # *year* of type `int`. + def foo(**kwargs: Unpack[Movie]): ... + +Note that there is only some runtime checking of this operator. Not +everything the runtime allows may be accepted by static type checkers. + +For more information, see PEP 646 and PEP 692. +""" + + +# PEP 692 changed the repr of Unpack[] +# Breakpoint: https://github.com/python/cpython/pull/104048 +if sys.version_info >= (3, 12): + Unpack = typing.Unpack + + def _is_unpack(obj): + return get_origin(obj) is Unpack + +else: # <=3.11 + class _UnpackSpecialForm(_ExtensionsSpecialForm, _root=True): + def __init__(self, getitem): + super().__init__(getitem) + self.__doc__ = _UNPACK_DOC + + class _UnpackAlias(typing._GenericAlias, _root=True): + if sys.version_info < (3, 11): + # needed for compatibility with Generic[Unpack[Ts]] + __class__ = typing.TypeVar + + @property + def __typing_unpacked_tuple_args__(self): + assert self.__origin__ is Unpack + assert len(self.__args__) == 1 + arg, = self.__args__ + if isinstance(arg, (typing._GenericAlias, _types.GenericAlias)): + if arg.__origin__ is not tuple: + raise TypeError("Unpack[...] must be used with a tuple type") + return arg.__args__ + return None + + @property + def __typing_is_unpacked_typevartuple__(self): + assert self.__origin__ is Unpack + assert len(self.__args__) == 1 + return isinstance(self.__args__[0], TypeVarTuple) + + def __getitem__(self, args): + if self.__typing_is_unpacked_typevartuple__: + return args + return super().__getitem__(args) + + @_UnpackSpecialForm + def Unpack(self, parameters): + item = typing._type_check(parameters, f'{self._name} accepts only a single type.') + return _UnpackAlias(self, (item,)) + + def _is_unpack(obj): + return isinstance(obj, _UnpackAlias) + + +def _unpack_args(*args): + newargs = [] + for arg in args: + subargs = getattr(arg, '__typing_unpacked_tuple_args__', None) + if subargs is not None and (not (subargs and subargs[-1] is ...)): + newargs.extend(subargs) + else: + newargs.append(arg) + return newargs + + +if _PEP_696_IMPLEMENTED: + from typing import TypeVarTuple + +elif hasattr(typing, "TypeVarTuple"): # 3.11+ + + # Add default parameter - PEP 696 + class TypeVarTuple(metaclass=_TypeVarLikeMeta): + """Type variable tuple.""" + + _backported_typevarlike = typing.TypeVarTuple + + def __new__(cls, name, *, default=NoDefault): + tvt = typing.TypeVarTuple(name) + _set_default(tvt, default) + _set_module(tvt) + + def _typevartuple_prepare_subst(alias, args): + params = alias.__parameters__ + typevartuple_index = params.index(tvt) + for param in params[typevartuple_index + 1:]: + if isinstance(param, TypeVarTuple): + raise TypeError( + f"More than one TypeVarTuple parameter in {alias}" + ) + + alen = len(args) + plen = len(params) + left = typevartuple_index + right = plen - typevartuple_index - 1 + var_tuple_index = None + fillarg = None + for k, arg in enumerate(args): + if not isinstance(arg, type): + subargs = getattr(arg, '__typing_unpacked_tuple_args__', None) + if subargs and len(subargs) == 2 and subargs[-1] is ...: + if var_tuple_index is not None: + raise TypeError( + "More than one unpacked " + "arbitrary-length tuple argument" + ) + var_tuple_index = k + fillarg = subargs[0] + if var_tuple_index is not None: + left = min(left, var_tuple_index) + right = min(right, alen - var_tuple_index - 1) + elif left + right > alen: + raise TypeError(f"Too few arguments for {alias};" + f" actual {alen}, expected at least {plen - 1}") + if left == alen - right and tvt.has_default(): + replacement = _unpack_args(tvt.__default__) + else: + replacement = args[left: alen - right] + + return ( + *args[:left], + *([fillarg] * (typevartuple_index - left)), + replacement, + *([fillarg] * (plen - right - left - typevartuple_index - 1)), + *args[alen - right:], + ) + + tvt.__typing_prepare_subst__ = _typevartuple_prepare_subst + return tvt + + def __init_subclass__(self, *args, **kwds): + raise TypeError("Cannot subclass special typing classes") + +else: # <=3.10 + class TypeVarTuple(_DefaultMixin): + """Type variable tuple. + + Usage:: + + Ts = TypeVarTuple('Ts') + + In the same way that a normal type variable is a stand-in for a single + type such as ``int``, a type variable *tuple* is a stand-in for a *tuple* + type such as ``Tuple[int, str]``. + + Type variable tuples can be used in ``Generic`` declarations. + Consider the following example:: + + class Array(Generic[*Ts]): ... + + The ``Ts`` type variable tuple here behaves like ``tuple[T1, T2]``, + where ``T1`` and ``T2`` are type variables. To use these type variables + as type parameters of ``Array``, we must *unpack* the type variable tuple using + the star operator: ``*Ts``. The signature of ``Array`` then behaves + as if we had simply written ``class Array(Generic[T1, T2]): ...``. + In contrast to ``Generic[T1, T2]``, however, ``Generic[*Shape]`` allows + us to parameterise the class with an *arbitrary* number of type parameters. + + Type variable tuples can be used anywhere a normal ``TypeVar`` can. + This includes class definitions, as shown above, as well as function + signatures and variable annotations:: + + class Array(Generic[*Ts]): + + def __init__(self, shape: Tuple[*Ts]): + self._shape: Tuple[*Ts] = shape + + def get_shape(self) -> Tuple[*Ts]: + return self._shape + + shape = (Height(480), Width(640)) + x: Array[Height, Width] = Array(shape) + y = abs(x) # Inferred type is Array[Height, Width] + z = x + x # ... is Array[Height, Width] + x.get_shape() # ... is tuple[Height, Width] + + """ + + # Trick Generic __parameters__. + __class__ = typing.TypeVar + + def __iter__(self): + yield self.__unpacked__ + + def __init__(self, name, *, default=NoDefault): + self.__name__ = name + _DefaultMixin.__init__(self, default) + + # for pickling: + def_mod = _caller() + if def_mod != 'typing_extensions': + self.__module__ = def_mod + + self.__unpacked__ = Unpack[self] + + def __repr__(self): + return self.__name__ + + def __hash__(self): + return object.__hash__(self) + + def __eq__(self, other): + return self is other + + def __reduce__(self): + return self.__name__ + + def __init_subclass__(self, *args, **kwds): + if '_root' not in kwds: + raise TypeError("Cannot subclass special typing classes") + + +if hasattr(typing, "reveal_type"): # 3.11+ + reveal_type = typing.reveal_type +else: # <=3.10 + def reveal_type(obj: T, /) -> T: + """Reveal the inferred type of a variable. + + When a static type checker encounters a call to ``reveal_type()``, + it will emit the inferred type of the argument:: + + x: int = 1 + reveal_type(x) + + Running a static type checker (e.g., ``mypy``) on this example + will produce output similar to 'Revealed type is "builtins.int"'. + + At runtime, the function prints the runtime type of the + argument and returns it unchanged. + + """ + print(f"Runtime type is {type(obj).__name__!r}", file=sys.stderr) + return obj + + +if hasattr(typing, "_ASSERT_NEVER_REPR_MAX_LENGTH"): # 3.11+ + _ASSERT_NEVER_REPR_MAX_LENGTH = typing._ASSERT_NEVER_REPR_MAX_LENGTH +else: # <=3.10 + _ASSERT_NEVER_REPR_MAX_LENGTH = 100 + + +if hasattr(typing, "assert_never"): # 3.11+ + assert_never = typing.assert_never +else: # <=3.10 + def assert_never(arg: Never, /) -> Never: + """Assert to the type checker that a line of code is unreachable. + + Example:: + + def int_or_str(arg: int | str) -> None: + match arg: + case int(): + print("It's an int") + case str(): + print("It's a str") + case _: + assert_never(arg) + + If a type checker finds that a call to assert_never() is + reachable, it will emit an error. + + At runtime, this throws an exception when called. + + """ + value = repr(arg) + if len(value) > _ASSERT_NEVER_REPR_MAX_LENGTH: + value = value[:_ASSERT_NEVER_REPR_MAX_LENGTH] + '...' + raise AssertionError(f"Expected code to be unreachable, but got: {value}") + + +# dataclass_transform exists in 3.11 but lacks the frozen_default parameter +# Breakpoint: https://github.com/python/cpython/pull/99958 +if sys.version_info >= (3, 12): # 3.12+ + dataclass_transform = typing.dataclass_transform +else: # <=3.11 + def dataclass_transform( + *, + eq_default: bool = True, + order_default: bool = False, + kw_only_default: bool = False, + frozen_default: bool = False, + field_specifiers: typing.Tuple[ + typing.Union[typing.Type[typing.Any], typing.Callable[..., typing.Any]], + ... + ] = (), + **kwargs: typing.Any, + ) -> typing.Callable[[T], T]: + """Decorator that marks a function, class, or metaclass as providing + dataclass-like behavior. + + Example: + + from typing_extensions import dataclass_transform + + _T = TypeVar("_T") + + # Used on a decorator function + @dataclass_transform() + def create_model(cls: type[_T]) -> type[_T]: + ... + return cls + + @create_model + class CustomerModel: + id: int + name: str + + # Used on a base class + @dataclass_transform() + class ModelBase: ... + + class CustomerModel(ModelBase): + id: int + name: str + + # Used on a metaclass + @dataclass_transform() + class ModelMeta(type): ... + + class ModelBase(metaclass=ModelMeta): ... + + class CustomerModel(ModelBase): + id: int + name: str + + Each of the ``CustomerModel`` classes defined in this example will now + behave similarly to a dataclass created with the ``@dataclasses.dataclass`` + decorator. For example, the type checker will synthesize an ``__init__`` + method. + + The arguments to this decorator can be used to customize this behavior: + - ``eq_default`` indicates whether the ``eq`` parameter is assumed to be + True or False if it is omitted by the caller. + - ``order_default`` indicates whether the ``order`` parameter is + assumed to be True or False if it is omitted by the caller. + - ``kw_only_default`` indicates whether the ``kw_only`` parameter is + assumed to be True or False if it is omitted by the caller. + - ``frozen_default`` indicates whether the ``frozen`` parameter is + assumed to be True or False if it is omitted by the caller. + - ``field_specifiers`` specifies a static list of supported classes + or functions that describe fields, similar to ``dataclasses.field()``. + + At runtime, this decorator records its arguments in the + ``__dataclass_transform__`` attribute on the decorated object. + + See PEP 681 for details. + + """ + def decorator(cls_or_fn): + cls_or_fn.__dataclass_transform__ = { + "eq_default": eq_default, + "order_default": order_default, + "kw_only_default": kw_only_default, + "frozen_default": frozen_default, + "field_specifiers": field_specifiers, + "kwargs": kwargs, + } + return cls_or_fn + return decorator + + +if hasattr(typing, "override"): # 3.12+ + override = typing.override +else: # <=3.11 + _F = typing.TypeVar("_F", bound=typing.Callable[..., typing.Any]) + + def override(arg: _F, /) -> _F: + """Indicate that a method is intended to override a method in a base class. + + Usage: + + class Base: + def method(self) -> None: + pass + + class Child(Base): + @override + def method(self) -> None: + super().method() + + When this decorator is applied to a method, the type checker will + validate that it overrides a method with the same name on a base class. + This helps prevent bugs that may occur when a base class is changed + without an equivalent change to a child class. + + There is no runtime checking of these properties. The decorator + sets the ``__override__`` attribute to ``True`` on the decorated object + to allow runtime introspection. + + See PEP 698 for details. + + """ + try: + arg.__override__ = True + except (AttributeError, TypeError): + # Skip the attribute silently if it is not writable. + # AttributeError happens if the object has __slots__ or a + # read-only property, TypeError if it's a builtin class. + pass + return arg + + +# Python 3.13.3+ contains a fix for the wrapped __new__ +# Breakpoint: https://github.com/python/cpython/pull/132160 +if sys.version_info >= (3, 13, 3): + deprecated = warnings.deprecated +else: + _T = typing.TypeVar("_T") + + class deprecated: + """Indicate that a class, function or overload is deprecated. + + When this decorator is applied to an object, the type checker + will generate a diagnostic on usage of the deprecated object. + + Usage: + + @deprecated("Use B instead") + class A: + pass + + @deprecated("Use g instead") + def f(): + pass + + @overload + @deprecated("int support is deprecated") + def g(x: int) -> int: ... + @overload + def g(x: str) -> int: ... + + The warning specified by *category* will be emitted at runtime + on use of deprecated objects. For functions, that happens on calls; + for classes, on instantiation and on creation of subclasses. + If the *category* is ``None``, no warning is emitted at runtime. + The *stacklevel* determines where the + warning is emitted. If it is ``1`` (the default), the warning + is emitted at the direct caller of the deprecated object; if it + is higher, it is emitted further up the stack. + Static type checker behavior is not affected by the *category* + and *stacklevel* arguments. + + The deprecation message passed to the decorator is saved in the + ``__deprecated__`` attribute on the decorated object. + If applied to an overload, the decorator + must be after the ``@overload`` decorator for the attribute to + exist on the overload as returned by ``get_overloads()``. + + See PEP 702 for details. + + """ + def __init__( + self, + message: str, + /, + *, + category: typing.Optional[typing.Type[Warning]] = DeprecationWarning, + stacklevel: int = 1, + ) -> None: + if not isinstance(message, str): + raise TypeError( + "Expected an object of type str for 'message', not " + f"{type(message).__name__!r}" + ) + self.message = message + self.category = category + self.stacklevel = stacklevel + + def __call__(self, arg: _T, /) -> _T: + # Make sure the inner functions created below don't + # retain a reference to self. + msg = self.message + category = self.category + stacklevel = self.stacklevel + if category is None: + arg.__deprecated__ = msg + return arg + elif isinstance(arg, type): + import functools + from types import MethodType + + original_new = arg.__new__ + + @functools.wraps(original_new) + def __new__(cls, /, *args, **kwargs): + if cls is arg: + warnings.warn(msg, category=category, stacklevel=stacklevel + 1) + if original_new is not object.__new__: + return original_new(cls, *args, **kwargs) + # Mirrors a similar check in object.__new__. + elif cls.__init__ is object.__init__ and (args or kwargs): + raise TypeError(f"{cls.__name__}() takes no arguments") + else: + return original_new(cls) + + arg.__new__ = staticmethod(__new__) + + original_init_subclass = arg.__init_subclass__ + # We need slightly different behavior if __init_subclass__ + # is a bound method (likely if it was implemented in Python) + if isinstance(original_init_subclass, MethodType): + original_init_subclass = original_init_subclass.__func__ + + @functools.wraps(original_init_subclass) + def __init_subclass__(*args, **kwargs): + warnings.warn(msg, category=category, stacklevel=stacklevel + 1) + return original_init_subclass(*args, **kwargs) + + arg.__init_subclass__ = classmethod(__init_subclass__) + # Or otherwise, which likely means it's a builtin such as + # object's implementation of __init_subclass__. + else: + @functools.wraps(original_init_subclass) + def __init_subclass__(*args, **kwargs): + warnings.warn(msg, category=category, stacklevel=stacklevel + 1) + return original_init_subclass(*args, **kwargs) + + arg.__init_subclass__ = __init_subclass__ + + arg.__deprecated__ = __new__.__deprecated__ = msg + __init_subclass__.__deprecated__ = msg + return arg + elif callable(arg): + import asyncio.coroutines + import functools + import inspect + + @functools.wraps(arg) + def wrapper(*args, **kwargs): + warnings.warn(msg, category=category, stacklevel=stacklevel + 1) + return arg(*args, **kwargs) + + if asyncio.coroutines.iscoroutinefunction(arg): + # Breakpoint: https://github.com/python/cpython/pull/99247 + if sys.version_info >= (3, 12): + wrapper = inspect.markcoroutinefunction(wrapper) + else: + wrapper._is_coroutine = asyncio.coroutines._is_coroutine + + arg.__deprecated__ = wrapper.__deprecated__ = msg + return wrapper + else: + raise TypeError( + "@deprecated decorator with non-None category must be applied to " + f"a class or callable, not {arg!r}" + ) + +# Breakpoint: https://github.com/python/cpython/pull/23702 +if sys.version_info < (3, 10): + def _is_param_expr(arg): + return arg is ... or isinstance( + arg, (tuple, list, ParamSpec, _ConcatenateGenericAlias) + ) +else: + def _is_param_expr(arg): + return arg is ... or isinstance( + arg, + ( + tuple, + list, + ParamSpec, + _ConcatenateGenericAlias, + typing._ConcatenateGenericAlias, + ), + ) + + +# We have to do some monkey patching to deal with the dual nature of +# Unpack/TypeVarTuple: +# - We want Unpack to be a kind of TypeVar so it gets accepted in +# Generic[Unpack[Ts]] +# - We want it to *not* be treated as a TypeVar for the purposes of +# counting generic parameters, so that when we subscript a generic, +# the runtime doesn't try to substitute the Unpack with the subscripted type. +if not hasattr(typing, "TypeVarTuple"): + def _check_generic(cls, parameters, elen=_marker): + """Check correct count for parameters of a generic cls (internal helper). + + This gives a nice error message in case of count mismatch. + """ + # If substituting a single ParamSpec with multiple arguments + # we do not check the count + if (inspect.isclass(cls) and issubclass(cls, typing.Generic) + and len(cls.__parameters__) == 1 + and isinstance(cls.__parameters__[0], ParamSpec) + and parameters + and not _is_param_expr(parameters[0]) + ): + # Generic modifies parameters variable, but here we cannot do this + return + + if not elen: + raise TypeError(f"{cls} is not a generic class") + if elen is _marker: + if not hasattr(cls, "__parameters__") or not cls.__parameters__: + raise TypeError(f"{cls} is not a generic class") + elen = len(cls.__parameters__) + alen = len(parameters) + if alen != elen: + expect_val = elen + if hasattr(cls, "__parameters__"): + parameters = [p for p in cls.__parameters__ if not _is_unpack(p)] + num_tv_tuples = sum(isinstance(p, TypeVarTuple) for p in parameters) + if (num_tv_tuples > 0) and (alen >= elen - num_tv_tuples): + return + + # deal with TypeVarLike defaults + # required TypeVarLikes cannot appear after a defaulted one. + if alen < elen: + # since we validate TypeVarLike default in _collect_type_vars + # or _collect_parameters we can safely check parameters[alen] + if ( + getattr(parameters[alen], '__default__', NoDefault) + is not NoDefault + ): + return + + num_default_tv = sum(getattr(p, '__default__', NoDefault) + is not NoDefault for p in parameters) + + elen -= num_default_tv + + expect_val = f"at least {elen}" + + # Breakpoint: https://github.com/python/cpython/pull/27515 + things = "arguments" if sys.version_info >= (3, 10) else "parameters" + raise TypeError(f"Too {'many' if alen > elen else 'few'} {things}" + f" for {cls}; actual {alen}, expected {expect_val}") +else: + # Python 3.11+ + + def _check_generic(cls, parameters, elen): + """Check correct count for parameters of a generic cls (internal helper). + + This gives a nice error message in case of count mismatch. + """ + if not elen: + raise TypeError(f"{cls} is not a generic class") + alen = len(parameters) + if alen != elen: + expect_val = elen + if hasattr(cls, "__parameters__"): + parameters = [p for p in cls.__parameters__ if not _is_unpack(p)] + + # deal with TypeVarLike defaults + # required TypeVarLikes cannot appear after a defaulted one. + if alen < elen: + # since we validate TypeVarLike default in _collect_type_vars + # or _collect_parameters we can safely check parameters[alen] + if ( + getattr(parameters[alen], '__default__', NoDefault) + is not NoDefault + ): + return + + num_default_tv = sum(getattr(p, '__default__', NoDefault) + is not NoDefault for p in parameters) + + elen -= num_default_tv + + expect_val = f"at least {elen}" + + raise TypeError(f"Too {'many' if alen > elen else 'few'} arguments" + f" for {cls}; actual {alen}, expected {expect_val}") + +if not _PEP_696_IMPLEMENTED: + typing._check_generic = _check_generic + + +def _has_generic_or_protocol_as_origin() -> bool: + try: + frame = sys._getframe(2) + # - Catch AttributeError: not all Python implementations have sys._getframe() + # - Catch ValueError: maybe we're called from an unexpected module + # and the call stack isn't deep enough + except (AttributeError, ValueError): + return False # err on the side of leniency + else: + # If we somehow get invoked from outside typing.py, + # also err on the side of leniency + if frame.f_globals.get("__name__") != "typing": + return False + origin = frame.f_locals.get("origin") + # Cannot use "in" because origin may be an object with a buggy __eq__ that + # throws an error. + return origin is typing.Generic or origin is Protocol or origin is typing.Protocol + + +_TYPEVARTUPLE_TYPES = {TypeVarTuple, getattr(typing, "TypeVarTuple", None)} + + +def _is_unpacked_typevartuple(x) -> bool: + if get_origin(x) is not Unpack: + return False + args = get_args(x) + return ( + bool(args) + and len(args) == 1 + and type(args[0]) in _TYPEVARTUPLE_TYPES + ) + + +# Python 3.11+ _collect_type_vars was renamed to _collect_parameters +if hasattr(typing, '_collect_type_vars'): + def _collect_type_vars(types, typevar_types=None): + """Collect all type variable contained in types in order of + first appearance (lexicographic order). For example:: + + _collect_type_vars((T, List[S, T])) == (T, S) + """ + if typevar_types is None: + typevar_types = typing.TypeVar + tvars = [] + + # A required TypeVarLike cannot appear after a TypeVarLike with a default + # if it was a direct call to `Generic[]` or `Protocol[]` + enforce_default_ordering = _has_generic_or_protocol_as_origin() + default_encountered = False + + # Also, a TypeVarLike with a default cannot appear after a TypeVarTuple + type_var_tuple_encountered = False + + for t in types: + if _is_unpacked_typevartuple(t): + type_var_tuple_encountered = True + elif ( + isinstance(t, typevar_types) and not isinstance(t, _UnpackAlias) + and t not in tvars + ): + if enforce_default_ordering: + has_default = getattr(t, '__default__', NoDefault) is not NoDefault + if has_default: + if type_var_tuple_encountered: + raise TypeError('Type parameter with a default' + ' follows TypeVarTuple') + default_encountered = True + elif default_encountered: + raise TypeError(f'Type parameter {t!r} without a default' + ' follows type parameter with a default') + + tvars.append(t) + if _should_collect_from_parameters(t): + tvars.extend([t for t in t.__parameters__ if t not in tvars]) + elif isinstance(t, tuple): + # Collect nested type_vars + # tuple wrapped by _prepare_paramspec_params(cls, params) + for x in t: + for collected in _collect_type_vars([x]): + if collected not in tvars: + tvars.append(collected) + return tuple(tvars) + + typing._collect_type_vars = _collect_type_vars +else: + def _collect_parameters(args): + """Collect all type variables and parameter specifications in args + in order of first appearance (lexicographic order). + + For example:: + + assert _collect_parameters((T, Callable[P, T])) == (T, P) + """ + parameters = [] + + # A required TypeVarLike cannot appear after a TypeVarLike with default + # if it was a direct call to `Generic[]` or `Protocol[]` + enforce_default_ordering = _has_generic_or_protocol_as_origin() + default_encountered = False + + # Also, a TypeVarLike with a default cannot appear after a TypeVarTuple + type_var_tuple_encountered = False + + for t in args: + if isinstance(t, type): + # We don't want __parameters__ descriptor of a bare Python class. + pass + elif isinstance(t, tuple): + # `t` might be a tuple, when `ParamSpec` is substituted with + # `[T, int]`, or `[int, *Ts]`, etc. + for x in t: + for collected in _collect_parameters([x]): + if collected not in parameters: + parameters.append(collected) + elif hasattr(t, '__typing_subst__'): + if t not in parameters: + if enforce_default_ordering: + has_default = ( + getattr(t, '__default__', NoDefault) is not NoDefault + ) + + if type_var_tuple_encountered and has_default: + raise TypeError('Type parameter with a default' + ' follows TypeVarTuple') + + if has_default: + default_encountered = True + elif default_encountered: + raise TypeError(f'Type parameter {t!r} without a default' + ' follows type parameter with a default') + + parameters.append(t) + else: + if _is_unpacked_typevartuple(t): + type_var_tuple_encountered = True + for x in getattr(t, '__parameters__', ()): + if x not in parameters: + parameters.append(x) + + return tuple(parameters) + + if not _PEP_696_IMPLEMENTED: + typing._collect_parameters = _collect_parameters + +# Backport typing.NamedTuple as it exists in Python 3.13. +# In 3.11, the ability to define generic `NamedTuple`s was supported. +# This was explicitly disallowed in 3.9-3.10, and only half-worked in <=3.8. +# On 3.12, we added __orig_bases__ to call-based NamedTuples +# On 3.13, we deprecated kwargs-based NamedTuples +# Breakpoint: https://github.com/python/cpython/pull/105609 +if sys.version_info >= (3, 13): + NamedTuple = typing.NamedTuple +else: + def _make_nmtuple(name, types, module, defaults=()): + fields = [n for n, t in types] + annotations = {n: typing._type_check(t, f"field {n} annotation must be a type") + for n, t in types} + nm_tpl = collections.namedtuple(name, fields, + defaults=defaults, module=module) + nm_tpl.__annotations__ = nm_tpl.__new__.__annotations__ = annotations + return nm_tpl + + _prohibited_namedtuple_fields = typing._prohibited + _special_namedtuple_fields = frozenset({'__module__', '__name__', '__annotations__'}) + + class _NamedTupleMeta(type): + def __new__(cls, typename, bases, ns): + assert _NamedTuple in bases + for base in bases: + if base is not _NamedTuple and base is not typing.Generic: + raise TypeError( + 'can only inherit from a NamedTuple type and Generic') + bases = tuple(tuple if base is _NamedTuple else base for base in bases) + if "__annotations__" in ns: + types = ns["__annotations__"] + elif "__annotate__" in ns: + # TODO: Use inspect.VALUE here, and make the annotations lazily evaluated + types = ns["__annotate__"](1) + else: + types = {} + default_names = [] + for field_name in types: + if field_name in ns: + default_names.append(field_name) + elif default_names: + raise TypeError(f"Non-default namedtuple field {field_name} " + f"cannot follow default field" + f"{'s' if len(default_names) > 1 else ''} " + f"{', '.join(default_names)}") + nm_tpl = _make_nmtuple( + typename, types.items(), + defaults=[ns[n] for n in default_names], + module=ns['__module__'] + ) + nm_tpl.__bases__ = bases + if typing.Generic in bases: + if hasattr(typing, '_generic_class_getitem'): # 3.12+ + nm_tpl.__class_getitem__ = classmethod(typing._generic_class_getitem) + else: + class_getitem = typing.Generic.__class_getitem__.__func__ + nm_tpl.__class_getitem__ = classmethod(class_getitem) + # update from user namespace without overriding special namedtuple attributes + for key, val in ns.items(): + if key in _prohibited_namedtuple_fields: + raise AttributeError("Cannot overwrite NamedTuple attribute " + key) + elif key not in _special_namedtuple_fields: + if key not in nm_tpl._fields: + setattr(nm_tpl, key, ns[key]) + try: + set_name = type(val).__set_name__ + except AttributeError: + pass + else: + try: + set_name(val, nm_tpl, key) + except BaseException as e: + msg = ( + f"Error calling __set_name__ on {type(val).__name__!r} " + f"instance {key!r} in {typename!r}" + ) + # BaseException.add_note() existed on py311, + # but the __set_name__ machinery didn't start + # using add_note() until py312. + # Making sure exceptions are raised in the same way + # as in "normal" classes seems most important here. + # Breakpoint: https://github.com/python/cpython/pull/95915 + if sys.version_info >= (3, 12): + e.add_note(msg) + raise + else: + raise RuntimeError(msg) from e + + if typing.Generic in bases: + nm_tpl.__init_subclass__() + return nm_tpl + + _NamedTuple = type.__new__(_NamedTupleMeta, 'NamedTuple', (), {}) + + def _namedtuple_mro_entries(bases): + assert NamedTuple in bases + return (_NamedTuple,) + + def NamedTuple(typename, fields=_marker, /, **kwargs): + """Typed version of namedtuple. + + Usage:: + + class Employee(NamedTuple): + name: str + id: int + + This is equivalent to:: + + Employee = collections.namedtuple('Employee', ['name', 'id']) + + The resulting class has an extra __annotations__ attribute, giving a + dict that maps field names to types. (The field names are also in + the _fields attribute, which is part of the namedtuple API.) + An alternative equivalent functional syntax is also accepted:: + + Employee = NamedTuple('Employee', [('name', str), ('id', int)]) + """ + if fields is _marker: + if kwargs: + deprecated_thing = "Creating NamedTuple classes using keyword arguments" + deprecation_msg = ( + "{name} is deprecated and will be disallowed in Python {remove}. " + "Use the class-based or functional syntax instead." + ) + else: + deprecated_thing = "Failing to pass a value for the 'fields' parameter" + example = f"`{typename} = NamedTuple({typename!r}, [])`" + deprecation_msg = ( + "{name} is deprecated and will be disallowed in Python {remove}. " + "To create a NamedTuple class with 0 fields " + "using the functional syntax, " + "pass an empty list, e.g. " + ) + example + "." + elif fields is None: + if kwargs: + raise TypeError( + "Cannot pass `None` as the 'fields' parameter " + "and also specify fields using keyword arguments" + ) + else: + deprecated_thing = "Passing `None` as the 'fields' parameter" + example = f"`{typename} = NamedTuple({typename!r}, [])`" + deprecation_msg = ( + "{name} is deprecated and will be disallowed in Python {remove}. " + "To create a NamedTuple class with 0 fields " + "using the functional syntax, " + "pass an empty list, e.g. " + ) + example + "." + elif kwargs: + raise TypeError("Either list of fields or keywords" + " can be provided to NamedTuple, not both") + if fields is _marker or fields is None: + warnings.warn( + deprecation_msg.format(name=deprecated_thing, remove="3.15"), + DeprecationWarning, + stacklevel=2, + ) + fields = kwargs.items() + nt = _make_nmtuple(typename, fields, module=_caller()) + nt.__orig_bases__ = (NamedTuple,) + return nt + + NamedTuple.__mro_entries__ = _namedtuple_mro_entries + + +if hasattr(collections.abc, "Buffer"): + Buffer = collections.abc.Buffer +else: + class Buffer(abc.ABC): # noqa: B024 + """Base class for classes that implement the buffer protocol. + + The buffer protocol allows Python objects to expose a low-level + memory buffer interface. Before Python 3.12, it is not possible + to implement the buffer protocol in pure Python code, or even + to check whether a class implements the buffer protocol. In + Python 3.12 and higher, the ``__buffer__`` method allows access + to the buffer protocol from Python code, and the + ``collections.abc.Buffer`` ABC allows checking whether a class + implements the buffer protocol. + + To indicate support for the buffer protocol in earlier versions, + inherit from this ABC, either in a stub file or at runtime, + or use ABC registration. This ABC provides no methods, because + there is no Python-accessible methods shared by pre-3.12 buffer + classes. It is useful primarily for static checks. + + """ + + # As a courtesy, register the most common stdlib buffer classes. + Buffer.register(memoryview) + Buffer.register(bytearray) + Buffer.register(bytes) + + +# Backport of types.get_original_bases, available on 3.12+ in CPython +if hasattr(_types, "get_original_bases"): + get_original_bases = _types.get_original_bases +else: + def get_original_bases(cls, /): + """Return the class's "original" bases prior to modification by `__mro_entries__`. + + Examples:: + + from typing import TypeVar, Generic + from typing_extensions import NamedTuple, TypedDict + + T = TypeVar("T") + class Foo(Generic[T]): ... + class Bar(Foo[int], float): ... + class Baz(list[str]): ... + Eggs = NamedTuple("Eggs", [("a", int), ("b", str)]) + Spam = TypedDict("Spam", {"a": int, "b": str}) + + assert get_original_bases(Bar) == (Foo[int], float) + assert get_original_bases(Baz) == (list[str],) + assert get_original_bases(Eggs) == (NamedTuple,) + assert get_original_bases(Spam) == (TypedDict,) + assert get_original_bases(int) == (object,) + """ + try: + return cls.__dict__.get("__orig_bases__", cls.__bases__) + except AttributeError: + raise TypeError( + f'Expected an instance of type, not {type(cls).__name__!r}' + ) from None + + +# NewType is a class on Python 3.10+, making it pickleable +# The error message for subclassing instances of NewType was improved on 3.11+ +# Breakpoint: https://github.com/python/cpython/pull/30268 +if sys.version_info >= (3, 11): + NewType = typing.NewType +else: + class NewType: + """NewType creates simple unique types with almost zero + runtime overhead. NewType(name, tp) is considered a subtype of tp + by static type checkers. At runtime, NewType(name, tp) returns + a dummy callable that simply returns its argument. Usage:: + UserId = NewType('UserId', int) + def name_by_id(user_id: UserId) -> str: + ... + UserId('user') # Fails type check + name_by_id(42) # Fails type check + name_by_id(UserId(42)) # OK + num = UserId(5) + 1 # type: int + """ + + def __call__(self, obj, /): + return obj + + def __init__(self, name, tp): + self.__qualname__ = name + if '.' in name: + name = name.rpartition('.')[-1] + self.__name__ = name + self.__supertype__ = tp + def_mod = _caller() + if def_mod != 'typing_extensions': + self.__module__ = def_mod + + def __mro_entries__(self, bases): + # We defined __mro_entries__ to get a better error message + # if a user attempts to subclass a NewType instance. bpo-46170 + supercls_name = self.__name__ + + class Dummy: + def __init_subclass__(cls): + subcls_name = cls.__name__ + raise TypeError( + f"Cannot subclass an instance of NewType. " + f"Perhaps you were looking for: " + f"`{subcls_name} = NewType({subcls_name!r}, {supercls_name})`" + ) + + return (Dummy,) + + def __repr__(self): + return f'{self.__module__}.{self.__qualname__}' + + def __reduce__(self): + return self.__qualname__ + + # Breakpoint: https://github.com/python/cpython/pull/21515 + if sys.version_info >= (3, 10): + # PEP 604 methods + # It doesn't make sense to have these methods on Python <3.10 + + def __or__(self, other): + return typing.Union[self, other] + + def __ror__(self, other): + return typing.Union[other, self] + + +# Breakpoint: https://github.com/python/cpython/pull/124795 +if sys.version_info >= (3, 14): + TypeAliasType = typing.TypeAliasType +# <=3.13 +else: + # Breakpoint: https://github.com/python/cpython/pull/103764 + if sys.version_info >= (3, 12): + # 3.12-3.13 + def _is_unionable(obj): + """Corresponds to is_unionable() in unionobject.c in CPython.""" + return obj is None or isinstance(obj, ( + type, + _types.GenericAlias, + _types.UnionType, + typing.TypeAliasType, + TypeAliasType, + )) + else: + # <=3.11 + def _is_unionable(obj): + """Corresponds to is_unionable() in unionobject.c in CPython.""" + return obj is None or isinstance(obj, ( + type, + _types.GenericAlias, + _types.UnionType, + TypeAliasType, + )) + + if sys.version_info < (3, 10): + # Copied and pasted from https://github.com/python/cpython/blob/986a4e1b6fcae7fe7a1d0a26aea446107dd58dd2/Objects/genericaliasobject.c#L568-L582, + # so that we emulate the behaviour of `types.GenericAlias` + # on the latest versions of CPython + _ATTRIBUTE_DELEGATION_EXCLUSIONS = frozenset({ + "__class__", + "__bases__", + "__origin__", + "__args__", + "__unpacked__", + "__parameters__", + "__typing_unpacked_tuple_args__", + "__mro_entries__", + "__reduce_ex__", + "__reduce__", + "__copy__", + "__deepcopy__", + }) + + class _TypeAliasGenericAlias(typing._GenericAlias, _root=True): + def __getattr__(self, attr): + if attr in _ATTRIBUTE_DELEGATION_EXCLUSIONS: + return object.__getattr__(self, attr) + return getattr(self.__origin__, attr) + + + class TypeAliasType: + """Create named, parameterized type aliases. + + This provides a backport of the new `type` statement in Python 3.12: + + type ListOrSet[T] = list[T] | set[T] + + is equivalent to: + + T = TypeVar("T") + ListOrSet = TypeAliasType("ListOrSet", list[T] | set[T], type_params=(T,)) + + The name ListOrSet can then be used as an alias for the type it refers to. + + The type_params argument should contain all the type parameters used + in the value of the type alias. If the alias is not generic, this + argument is omitted. + + Static type checkers should only support type aliases declared using + TypeAliasType that follow these rules: + + - The first argument (the name) must be a string literal. + - The TypeAliasType instance must be immediately assigned to a variable + of the same name. (For example, 'X = TypeAliasType("Y", int)' is invalid, + as is 'X, Y = TypeAliasType("X", int), TypeAliasType("Y", int)'). + + """ + + def __init__(self, name: str, value, *, type_params=()): + if not isinstance(name, str): + raise TypeError("TypeAliasType name must be a string") + if not isinstance(type_params, tuple): + raise TypeError("type_params must be a tuple") + self.__value__ = value + self.__type_params__ = type_params + + default_value_encountered = False + parameters = [] + for type_param in type_params: + if ( + not isinstance(type_param, (TypeVar, TypeVarTuple, ParamSpec)) + # <=3.11 + # Unpack Backport passes isinstance(type_param, TypeVar) + or _is_unpack(type_param) + ): + raise TypeError(f"Expected a type param, got {type_param!r}") + has_default = ( + getattr(type_param, '__default__', NoDefault) is not NoDefault + ) + if default_value_encountered and not has_default: + raise TypeError(f"non-default type parameter '{type_param!r}'" + " follows default type parameter") + if has_default: + default_value_encountered = True + if isinstance(type_param, TypeVarTuple): + parameters.extend(type_param) + else: + parameters.append(type_param) + self.__parameters__ = tuple(parameters) + def_mod = _caller() + if def_mod != 'typing_extensions': + self.__module__ = def_mod + # Setting this attribute closes the TypeAliasType from further modification + self.__name__ = name + + def __setattr__(self, name: str, value: object, /) -> None: + if hasattr(self, "__name__"): + self._raise_attribute_error(name) + super().__setattr__(name, value) + + def __delattr__(self, name: str, /) -> Never: + self._raise_attribute_error(name) + + def _raise_attribute_error(self, name: str) -> Never: + # Match the Python 3.12 error messages exactly + if name == "__name__": + raise AttributeError("readonly attribute") + elif name in {"__value__", "__type_params__", "__parameters__", "__module__"}: + raise AttributeError( + f"attribute '{name}' of 'typing.TypeAliasType' objects " + "is not writable" + ) + else: + raise AttributeError( + f"'typing.TypeAliasType' object has no attribute '{name}'" + ) + + def __repr__(self) -> str: + return self.__name__ + + if sys.version_info < (3, 11): + def _check_single_param(self, param, recursion=0): + # Allow [], [int], [int, str], [int, ...], [int, T] + if param is ...: + return ... + if param is None: + return None + # Note in <= 3.9 _ConcatenateGenericAlias inherits from list + if isinstance(param, list) and recursion == 0: + return [self._check_single_param(arg, recursion+1) + for arg in param] + return typing._type_check( + param, f'Subscripting {self.__name__} requires a type.' + ) + + def _check_parameters(self, parameters): + if sys.version_info < (3, 11): + return tuple( + self._check_single_param(item) + for item in parameters + ) + return tuple(typing._type_check( + item, f'Subscripting {self.__name__} requires a type.' + ) + for item in parameters + ) + + def __getitem__(self, parameters): + if not self.__type_params__: + raise TypeError("Only generic type aliases are subscriptable") + if not isinstance(parameters, tuple): + parameters = (parameters,) + # Using 3.9 here will create problems with Concatenate + if sys.version_info >= (3, 10): + return _types.GenericAlias(self, parameters) + type_vars = _collect_type_vars(parameters) + parameters = self._check_parameters(parameters) + alias = _TypeAliasGenericAlias(self, parameters) + # alias.__parameters__ is not complete if Concatenate is present + # as it is converted to a list from which no parameters are extracted. + if alias.__parameters__ != type_vars: + alias.__parameters__ = type_vars + return alias + + def __reduce__(self): + return self.__name__ + + def __init_subclass__(cls, *args, **kwargs): + raise TypeError( + "type 'typing_extensions.TypeAliasType' is not an acceptable base type" + ) + + # The presence of this method convinces typing._type_check + # that TypeAliasTypes are types. + def __call__(self): + raise TypeError("Type alias is not callable") + + # Breakpoint: https://github.com/python/cpython/pull/21515 + if sys.version_info >= (3, 10): + def __or__(self, right): + # For forward compatibility with 3.12, reject Unions + # that are not accepted by the built-in Union. + if not _is_unionable(right): + return NotImplemented + return typing.Union[self, right] + + def __ror__(self, left): + if not _is_unionable(left): + return NotImplemented + return typing.Union[left, self] + + +if hasattr(typing, "is_protocol"): + is_protocol = typing.is_protocol + get_protocol_members = typing.get_protocol_members +else: + def is_protocol(tp: type, /) -> bool: + """Return True if the given type is a Protocol. + + Example:: + + >>> from typing_extensions import Protocol, is_protocol + >>> class P(Protocol): + ... def a(self) -> str: ... + ... b: int + >>> is_protocol(P) + True + >>> is_protocol(int) + False + """ + return ( + isinstance(tp, type) + and getattr(tp, '_is_protocol', False) + and tp is not Protocol + and tp is not typing.Protocol + ) + + def get_protocol_members(tp: type, /) -> typing.FrozenSet[str]: + """Return the set of members defined in a Protocol. + + Example:: + + >>> from typing_extensions import Protocol, get_protocol_members + >>> class P(Protocol): + ... def a(self) -> str: ... + ... b: int + >>> get_protocol_members(P) + frozenset({'a', 'b'}) + + Raise a TypeError for arguments that are not Protocols. + """ + if not is_protocol(tp): + raise TypeError(f'{tp!r} is not a Protocol') + if hasattr(tp, '__protocol_attrs__'): + return frozenset(tp.__protocol_attrs__) + return frozenset(_get_protocol_attrs(tp)) + + +if hasattr(typing, "Doc"): + Doc = typing.Doc +else: + 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:: + + >>> from typing_extensions import Annotated, Doc + >>> def hi(to: Annotated[str, Doc("Who to say hi to")]) -> None: ... + """ + 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 + + +_CapsuleType = getattr(_types, "CapsuleType", None) + +if _CapsuleType is None: + try: + import _socket + except ImportError: + pass + else: + _CAPI = getattr(_socket, "CAPI", None) + if _CAPI is not None: + _CapsuleType = type(_CAPI) + +if _CapsuleType is not None: + CapsuleType = _CapsuleType + __all__.append("CapsuleType") + + +if sys.version_info >= (3, 14): + from annotationlib import Format, get_annotations +else: + # Available since Python 3.14.0a3 + # PR: https://github.com/python/cpython/pull/124415 + class Format(enum.IntEnum): + VALUE = 1 + VALUE_WITH_FAKE_GLOBALS = 2 + FORWARDREF = 3 + STRING = 4 + + # Available since Python 3.14.0a1 + # PR: https://github.com/python/cpython/pull/119891 + def get_annotations(obj, *, globals=None, locals=None, eval_str=False, + format=Format.VALUE): + """Compute the annotations dict for an object. + + obj may be a callable, class, or module. + Passing in an object of any other type raises TypeError. + + Returns a dict. get_annotations() returns a new dict every time + it's called; calling it twice on the same object will return two + different but equivalent dicts. + + This is a backport of `inspect.get_annotations`, which has been + in the standard library since Python 3.10. See the standard library + documentation for more: + + https://docs.python.org/3/library/inspect.html#inspect.get_annotations + + This backport adds the *format* argument introduced by PEP 649. The + three formats supported are: + * VALUE: the annotations are returned as-is. This is the default and + it is compatible with the behavior on previous Python versions. + * FORWARDREF: return annotations as-is if possible, but replace any + undefined names with ForwardRef objects. The implementation proposed by + PEP 649 relies on language changes that cannot be backported; the + typing-extensions implementation simply returns the same result as VALUE. + * STRING: return annotations as strings, in a format close to the original + source. Again, this behavior cannot be replicated directly in a backport. + As an approximation, typing-extensions retrieves the annotations under + VALUE semantics and then stringifies them. + + The purpose of this backport is to allow users who would like to use + FORWARDREF or STRING semantics once PEP 649 is implemented, but who also + want to support earlier Python versions, to simply write: + + typing_extensions.get_annotations(obj, format=Format.FORWARDREF) + + """ + format = Format(format) + if format is Format.VALUE_WITH_FAKE_GLOBALS: + raise ValueError( + "The VALUE_WITH_FAKE_GLOBALS format is for internal use only" + ) + + if eval_str and format is not Format.VALUE: + raise ValueError("eval_str=True is only supported with format=Format.VALUE") + + if isinstance(obj, type): + # class + obj_dict = getattr(obj, '__dict__', None) + if obj_dict and hasattr(obj_dict, 'get'): + ann = obj_dict.get('__annotations__', None) + if isinstance(ann, _types.GetSetDescriptorType): + ann = None + else: + ann = None + + obj_globals = None + module_name = getattr(obj, '__module__', None) + if module_name: + module = sys.modules.get(module_name, None) + if module: + obj_globals = getattr(module, '__dict__', None) + obj_locals = dict(vars(obj)) + unwrap = obj + elif isinstance(obj, _types.ModuleType): + # module + ann = getattr(obj, '__annotations__', None) + obj_globals = obj.__dict__ + obj_locals = None + unwrap = None + elif callable(obj): + # this includes types.Function, types.BuiltinFunctionType, + # types.BuiltinMethodType, functools.partial, functools.singledispatch, + # "class funclike" from Lib/test/test_inspect... on and on it goes. + ann = getattr(obj, '__annotations__', None) + obj_globals = getattr(obj, '__globals__', None) + obj_locals = None + unwrap = obj + elif hasattr(obj, '__annotations__'): + ann = obj.__annotations__ + obj_globals = obj_locals = unwrap = None + else: + raise TypeError(f"{obj!r} is not a module, class, or callable.") + + if ann is None: + return {} + + if not isinstance(ann, dict): + raise ValueError(f"{obj!r}.__annotations__ is neither a dict nor None") + + if not ann: + return {} + + if not eval_str: + if format is Format.STRING: + return { + key: value if isinstance(value, str) else typing._type_repr(value) + for key, value in ann.items() + } + return dict(ann) + + if unwrap is not None: + while True: + if hasattr(unwrap, '__wrapped__'): + unwrap = unwrap.__wrapped__ + continue + if isinstance(unwrap, functools.partial): + unwrap = unwrap.func + continue + break + if hasattr(unwrap, "__globals__"): + obj_globals = unwrap.__globals__ + + if globals is None: + globals = obj_globals + if locals is None: + locals = obj_locals or {} + + # "Inject" type parameters into the local namespace + # (unless they are shadowed by assignments *in* the local namespace), + # as a way of emulating annotation scopes when calling `eval()` + if type_params := getattr(obj, "__type_params__", ()): + locals = {param.__name__: param for param in type_params} | locals + + return_value = {key: + value if not isinstance(value, str) else eval(value, globals, locals) + for key, value in ann.items() } + return return_value + + +if hasattr(typing, "evaluate_forward_ref"): + evaluate_forward_ref = typing.evaluate_forward_ref +else: + # Implements annotationlib.ForwardRef.evaluate + def _eval_with_owner( + forward_ref, *, owner=None, globals=None, locals=None, type_params=None + ): + if forward_ref.__forward_evaluated__: + return forward_ref.__forward_value__ + if getattr(forward_ref, "__cell__", None) is not None: + try: + value = forward_ref.__cell__.cell_contents + except ValueError: + pass + else: + forward_ref.__forward_evaluated__ = True + forward_ref.__forward_value__ = value + return value + if owner is None: + owner = getattr(forward_ref, "__owner__", None) + + if ( + globals is None + and getattr(forward_ref, "__forward_module__", None) is not None + ): + globals = getattr( + sys.modules.get(forward_ref.__forward_module__, None), "__dict__", None + ) + if globals is None: + globals = getattr(forward_ref, "__globals__", None) + if globals is None: + if isinstance(owner, type): + module_name = getattr(owner, "__module__", None) + if module_name: + module = sys.modules.get(module_name, None) + if module: + globals = getattr(module, "__dict__", None) + elif isinstance(owner, _types.ModuleType): + globals = getattr(owner, "__dict__", None) + elif callable(owner): + globals = getattr(owner, "__globals__", None) + + # If we pass None to eval() below, the globals of this module are used. + if globals is None: + globals = {} + + if locals is None: + locals = {} + if isinstance(owner, type): + locals.update(vars(owner)) + + if type_params is None and owner is not None: + # "Inject" type parameters into the local namespace + # (unless they are shadowed by assignments *in* the local namespace), + # as a way of emulating annotation scopes when calling `eval()` + type_params = getattr(owner, "__type_params__", None) + + # Type parameters exist in their own scope, which is logically + # between the locals and the globals. We simulate this by adding + # them to the globals. + if type_params is not None: + globals = dict(globals) + for param in type_params: + globals[param.__name__] = param + + arg = forward_ref.__forward_arg__ + if arg.isidentifier() and not keyword.iskeyword(arg): + if arg in locals: + value = locals[arg] + elif arg in globals: + value = globals[arg] + elif hasattr(builtins, arg): + return getattr(builtins, arg) + else: + raise NameError(arg) + else: + code = forward_ref.__forward_code__ + value = eval(code, globals, locals) + forward_ref.__forward_evaluated__ = True + forward_ref.__forward_value__ = value + return value + + def evaluate_forward_ref( + forward_ref, + *, + owner=None, + globals=None, + locals=None, + type_params=None, + format=None, + _recursive_guard=frozenset(), + ): + """Evaluate a forward reference as a type hint. + + This is similar to calling the ForwardRef.evaluate() method, + but unlike that method, evaluate_forward_ref() also: + + * Recursively evaluates forward references nested within the type hint. + * Rejects certain objects that are not valid type hints. + * Replaces type hints that evaluate to None with types.NoneType. + * Supports the *FORWARDREF* and *STRING* formats. + + *forward_ref* must be an instance of ForwardRef. *owner*, if given, + should be the object that holds the annotations that the forward reference + derived from, such as a module, class object, or function. It is used to + infer the namespaces to use for looking up names. *globals* and *locals* + can also be explicitly given to provide the global and local namespaces. + *type_params* is a tuple of type parameters that are in scope when + evaluating the forward reference. This parameter must be provided (though + it may be an empty tuple) if *owner* is not given and the forward reference + does not already have an owner set. *format* specifies the format of the + annotation and is a member of the annotationlib.Format enum. + + """ + if format == Format.STRING: + return forward_ref.__forward_arg__ + if forward_ref.__forward_arg__ in _recursive_guard: + return forward_ref + + # Evaluate the forward reference + try: + value = _eval_with_owner( + forward_ref, + owner=owner, + globals=globals, + locals=locals, + type_params=type_params, + ) + except NameError: + if format == Format.FORWARDREF: + return forward_ref + else: + raise + + if isinstance(value, str): + value = ForwardRef(value) + + # Recursively evaluate the type + if isinstance(value, ForwardRef): + if getattr(value, "__forward_module__", True) is not None: + globals = None + return evaluate_forward_ref( + value, + globals=globals, + locals=locals, + type_params=type_params, owner=owner, + _recursive_guard=_recursive_guard, format=format + ) + if sys.version_info < (3, 12, 5) and type_params: + # Make use of type_params + locals = dict(locals) if locals else {} + for tvar in type_params: + if tvar.__name__ not in locals: # lets not overwrite something present + locals[tvar.__name__] = tvar + if sys.version_info < (3, 12, 5): + return typing._eval_type( + value, + globals, + locals, + recursive_guard=_recursive_guard | {forward_ref.__forward_arg__}, + ) + else: + return typing._eval_type( + value, + globals, + locals, + type_params, + recursive_guard=_recursive_guard | {forward_ref.__forward_arg__}, + ) + + +class Sentinel: + """Create a unique sentinel object. + + *name* should be the name of the variable to which the return value shall be assigned. + + *repr*, if supplied, will be used for the repr of the sentinel object. + If not provided, "" will be used. + """ + + def __init__( + self, + name: str, + repr: typing.Optional[str] = None, + ): + self._name = name + self._repr = repr if repr is not None else f'<{name}>' + + def __repr__(self): + return self._repr + + if sys.version_info < (3, 11): + # The presence of this method convinces typing._type_check + # that Sentinels are types. + def __call__(self, *args, **kwargs): + raise TypeError(f"{type(self).__name__!r} object is not callable") + + # Breakpoint: https://github.com/python/cpython/pull/21515 + if sys.version_info >= (3, 10): + def __or__(self, other): + return typing.Union[self, other] + + def __ror__(self, other): + return typing.Union[other, self] + + def __getstate__(self): + raise TypeError(f"Cannot pickle {type(self).__name__!r} object") + + +if sys.version_info >= (3, 14, 0, "beta"): + type_repr = annotationlib.type_repr +else: + def type_repr(value): + """Convert a Python value to a format suitable for use with the STRING format. + + This is intended as a helper for tools that support the STRING format but do + not have access to the code that originally produced the annotations. It uses + repr() for most objects. + + """ + if isinstance(value, (type, _types.FunctionType, _types.BuiltinFunctionType)): + if value.__module__ == "builtins": + return value.__qualname__ + return f"{value.__module__}.{value.__qualname__}" + if value is ...: + return "..." + return repr(value) + + +# Aliases for items that are in typing in all supported versions. +# We use hasattr() checks so this library will continue to import on +# future versions of Python that may remove these names. +_typing_names = [ + "AbstractSet", + "AnyStr", + "BinaryIO", + "Callable", + "Collection", + "Container", + "Dict", + "FrozenSet", + "Hashable", + "IO", + "ItemsView", + "Iterable", + "Iterator", + "KeysView", + "List", + "Mapping", + "MappingView", + "Match", + "MutableMapping", + "MutableSequence", + "MutableSet", + "Optional", + "Pattern", + "Reversible", + "Sequence", + "Set", + "Sized", + "TextIO", + "Tuple", + "Union", + "ValuesView", + "cast", + "no_type_check", + "no_type_check_decorator", + # This is private, but it was defined by typing_extensions for a long time + # and some users rely on it. + "_AnnotatedAlias", +] +globals().update( + {name: getattr(typing, name) for name in _typing_names if hasattr(typing, name)} +) +# These are defined unconditionally because they are used in +# typing-extensions itself. +Generic = typing.Generic +ForwardRef = typing.ForwardRef +Annotated = typing.Annotated diff --git a/.venv/lib/python3.12/site-packages/typing_inspection/__init__.py b/.venv/lib/python3.12/site-packages/typing_inspection/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/typing_inspection/introspection.py b/.venv/lib/python3.12/site-packages/typing_inspection/introspection.py new file mode 100644 index 0000000000000000000000000000000000000000..d6c083e5456c8ad5da7559a3c0a901e0812a5014 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/typing_inspection/introspection.py @@ -0,0 +1,587 @@ +"""High-level introspection utilities, used to inspect type annotations.""" + +from __future__ import annotations + +import sys +import types +from collections.abc import Generator +from dataclasses import InitVar +from enum import Enum, IntEnum, auto +from typing import Any, Literal, NamedTuple, cast + +from typing_extensions import TypeAlias, assert_never, get_args, get_origin + +from . import typing_objects + +__all__ = ( + 'AnnotationSource', + 'ForbiddenQualifier', + 'InspectedAnnotation', + 'Qualifier', + 'get_literal_values', + 'inspect_annotation', + 'is_union_origin', +) + +if sys.version_info >= (3, 14) or sys.version_info < (3, 10): + + def is_union_origin(obj: Any, /) -> bool: + """Return whether the provided origin is the union form. + + ```pycon + >>> is_union_origin(typing.Union) + True + >>> is_union_origin(get_origin(int | str)) + True + >>> is_union_origin(types.UnionType) + True + ``` + + !!! note + Since Python 3.14, both `Union[, , ...]` and ` | | ...` forms create instances + of the same [`typing.Union`][] class. As such, it is recommended to not use this function + anymore (provided that you only support Python 3.14 or greater), and instead use the + [`typing_objects.is_union()`][typing_inspection.typing_objects.is_union] function directly: + + ```python + from typing import Union, get_origin + + from typing_inspection import typing_objects + + typ = int | str # Or Union[int, str] + origin = get_origin(typ) + if typing_objects.is_union(origin): + ... + ``` + """ + return typing_objects.is_union(obj) + + +else: + + def is_union_origin(obj: Any, /) -> bool: + """Return whether the provided origin is the union form. + + ```pycon + >>> is_union_origin(typing.Union) + True + >>> is_union_origin(get_origin(int | str)) + True + >>> is_union_origin(types.UnionType) + True + ``` + + !!! note + Since Python 3.14, both `Union[, , ...]` and ` | | ...` forms create instances + of the same [`typing.Union`][] class. As such, it is recommended to not use this function + anymore (provided that you only support Python 3.14 or greater), and instead use the + [`typing_objects.is_union()`][typing_inspection.typing_objects.is_union] function directly: + + ```python + from typing import Union, get_origin + + from typing_inspection import typing_objects + + typ = int | str # Or Union[int, str] + origin = get_origin(typ) + if typing_objects.is_union(origin): + ... + ``` + """ + return typing_objects.is_union(obj) or obj is types.UnionType + + +def _literal_type_check(value: Any, /) -> None: + """Type check the provided literal value against the legal parameters.""" + if ( + not isinstance(value, (int, bytes, str, bool, Enum, typing_objects.NoneType)) + and value is not typing_objects.NoneType + ): + raise TypeError(f'{value} is not a valid literal value, must be one of: int, bytes, str, Enum, None.') + + +def get_literal_values( + annotation: Any, + /, + *, + type_check: bool = False, + unpack_type_aliases: Literal['skip', 'lenient', 'eager'] = 'eager', +) -> Generator[Any]: + """Yield the values contained in the provided [`Literal`][typing.Literal] [special form][]. + + Args: + annotation: The [`Literal`][typing.Literal] [special form][] to unpack. + type_check: Whether to check if the literal values are [legal parameters][literal-legal-parameters]. + Raises a [`TypeError`][] otherwise. + unpack_type_aliases: What to do when encountering [PEP 695](https://peps.python.org/pep-0695/) + [type aliases][type-aliases]. Can be one of: + + - `'skip'`: Do not try to parse type aliases. Note that this can lead to incorrect results: + ```pycon + >>> type MyAlias = Literal[1, 2] + >>> list(get_literal_values(Literal[MyAlias, 3], unpack_type_aliases="skip")) + [MyAlias, 3] + ``` + + - `'lenient'`: Try to parse type aliases, and fallback to `'skip'` if the type alias can't be inspected + (because of an undefined forward reference). + + - `'eager'`: Parse type aliases and raise any encountered [`NameError`][] exceptions (the default): + ```pycon + >>> type MyAlias = Literal[1, 2] + >>> list(get_literal_values(Literal[MyAlias, 3], unpack_type_aliases="eager")) + [1, 2, 3] + ``` + + Note: + While `None` is [equivalent to][none] `type(None)`, the runtime implementation of [`Literal`][typing.Literal] + does not de-duplicate them. This function makes sure this de-duplication is applied: + + ```pycon + >>> list(get_literal_values(Literal[NoneType, None])) + [None] + ``` + + Example: + ```pycon + >>> type Ints = Literal[1, 2] + >>> list(get_literal_values(Literal[1, Ints], unpack_type_alias="skip")) + ["a", Ints] + >>> list(get_literal_values(Literal[1, Ints])) + [1, 2] + >>> list(get_literal_values(Literal[1.0], type_check=True)) + Traceback (most recent call last): + ... + TypeError: 1.0 is not a valid literal value, must be one of: int, bytes, str, Enum, None. + ``` + """ + # `literal` is guaranteed to be a `Literal[...]` special form, so use + # `__args__` directly instead of calling `get_args()`. + + if unpack_type_aliases == 'skip': + _has_none = False + # `Literal` parameters are already deduplicated, no need to do it ourselves. + # (we only check for `None` and `NoneType`, which should be considered as duplicates). + for arg in annotation.__args__: + if type_check: + _literal_type_check(arg) + if arg is None or arg is typing_objects.NoneType: + if not _has_none: + yield None + _has_none = True + else: + yield arg + else: + # We'll need to manually deduplicate parameters, see the `Literal` implementation in `typing`. + values_and_type: list[tuple[Any, type[Any]]] = [] + + for arg in annotation.__args__: + # Note: we could also check for generic aliases with a type alias as an origin. + # However, it is very unlikely that this happens as type variables can't appear in + # `Literal` forms, so the only valid (but unnecessary) use case would be something like: + # `type Test[T] = Literal['a']` (and then use `Test[SomeType]`). + if typing_objects.is_typealiastype(arg): + try: + alias_value = arg.__value__ + except NameError: + if unpack_type_aliases == 'eager': + raise + # unpack_type_aliases == "lenient": + if type_check: + _literal_type_check(arg) + values_and_type.append((arg, type(arg))) + else: + sub_args = get_literal_values( + alias_value, type_check=type_check, unpack_type_aliases=unpack_type_aliases + ) + values_and_type.extend((a, type(a)) for a in sub_args) # pyright: ignore[reportUnknownArgumentType] + else: + if type_check: + _literal_type_check(arg) + if arg is typing_objects.NoneType: + values_and_type.append((None, typing_objects.NoneType)) + else: + values_and_type.append((arg, type(arg))) # pyright: ignore[reportUnknownArgumentType] + + try: + dct = dict.fromkeys(values_and_type) + except TypeError: + # Unhashable parameters, the Python implementation allows them + yield from (p for p, _ in values_and_type) + else: + yield from (p for p, _ in dct) + + +Qualifier: TypeAlias = Literal['required', 'not_required', 'read_only', 'class_var', 'init_var', 'final'] +"""A [type qualifier][].""" + +_all_qualifiers: set[Qualifier] = set(get_args(Qualifier)) + + +# TODO at some point, we could switch to an enum flag, so that multiple sources +# can be combined. However, is there a need for this? +class AnnotationSource(IntEnum): + # TODO if/when https://peps.python.org/pep-0767/ is accepted, add 'read_only' + # to CLASS and NAMED_TUPLE (even though for named tuples it is redundant). + + """The source of an annotation, e.g. a class or a function. + + Depending on the source, different [type qualifiers][type qualifier] may be (dis)allowed. + """ + + ASSIGNMENT_OR_VARIABLE = auto() + """An annotation used in an assignment or variable annotation: + + ```python + x: Final[int] = 1 + y: Final[str] + ``` + + **Allowed type qualifiers:** [`Final`][typing.Final]. + """ + + CLASS = auto() + """An annotation used in the body of a class: + + ```python + class Test: + x: Final[int] = 1 + y: ClassVar[str] + ``` + + **Allowed type qualifiers:** [`ClassVar`][typing.ClassVar], [`Final`][typing.Final]. + """ + + DATACLASS = auto() + """An annotation used in the body of a dataclass: + + ```python + @dataclass + class Test: + x: Final[int] = 1 + y: InitVar[str] = 'test' + ``` + + **Allowed type qualifiers:** [`ClassVar`][typing.ClassVar], [`Final`][typing.Final], [`InitVar`][dataclasses.InitVar]. + """ # noqa: E501 + + TYPED_DICT = auto() + """An annotation used in the body of a [`TypedDict`][typing.TypedDict]: + + ```python + class TD(TypedDict): + x: Required[ReadOnly[int]] + y: ReadOnly[NotRequired[str]] + ``` + + **Allowed type qualifiers:** [`ReadOnly`][typing.ReadOnly], [`Required`][typing.Required], + [`NotRequired`][typing.NotRequired]. + """ + + NAMED_TUPLE = auto() + """An annotation used in the body of a [`NamedTuple`][typing.NamedTuple]. + + ```python + class NT(NamedTuple): + x: int + y: str + ``` + + **Allowed type qualifiers:** none. + """ + + FUNCTION = auto() + """An annotation used in a function, either for a parameter or the return value. + + ```python + def func(a: int) -> str: + ... + ``` + + **Allowed type qualifiers:** none. + """ + + ANY = auto() + """An annotation that might come from any source. + + **Allowed type qualifiers:** all. + """ + + BARE = auto() + """An annotation that is inspected as is. + + **Allowed type qualifiers:** none. + """ + + @property + def allowed_qualifiers(self) -> set[Qualifier]: + """The allowed [type qualifiers][type qualifier] for this annotation source.""" + # TODO use a match statement when Python 3.9 support is dropped. + if self is AnnotationSource.ASSIGNMENT_OR_VARIABLE: + return {'final'} + elif self is AnnotationSource.CLASS: + return {'final', 'class_var'} + elif self is AnnotationSource.DATACLASS: + return {'final', 'class_var', 'init_var'} + elif self is AnnotationSource.TYPED_DICT: + return {'required', 'not_required', 'read_only'} + elif self in (AnnotationSource.NAMED_TUPLE, AnnotationSource.FUNCTION, AnnotationSource.BARE): + return set() + elif self is AnnotationSource.ANY: + return _all_qualifiers + else: # pragma: no cover + assert_never(self) + + +class ForbiddenQualifier(Exception): + """The provided [type qualifier][] is forbidden.""" + + qualifier: Qualifier + """The forbidden qualifier.""" + + def __init__(self, qualifier: Qualifier, /) -> None: + self.qualifier = qualifier + + +class _UnknownTypeEnum(Enum): + UNKNOWN = auto() + + def __str__(self) -> str: + return 'UNKNOWN' + + def __repr__(self) -> str: + return '' + + +UNKNOWN = _UnknownTypeEnum.UNKNOWN +"""A sentinel value used when no [type expression][] is present.""" + +_UnkownType: TypeAlias = Literal[_UnknownTypeEnum.UNKNOWN] +"""The type of the [`UNKNOWN`][typing_inspection.introspection.UNKNOWN] sentinel value.""" + + +class InspectedAnnotation(NamedTuple): + """The result of the inspected annotation.""" + + type: Any | _UnkownType + """The final [type expression][], with [type qualifiers][type qualifier] and annotated metadata stripped. + + If no type expression is available, the [`UNKNOWN`][typing_inspection.introspection.UNKNOWN] sentinel + value is used instead. This is the case when a [type qualifier][] is used with no type annotation: + + ```python + ID: Final = 1 + + class C: + x: ClassVar = 'test' + ``` + """ + + qualifiers: set[Qualifier] + """The [type qualifiers][type qualifier] present on the annotation.""" + + metadata: list[Any] + """The annotated metadata.""" + + +def inspect_annotation( # noqa: PLR0915 + annotation: Any, + /, + *, + annotation_source: AnnotationSource, + unpack_type_aliases: Literal['skip', 'lenient', 'eager'] = 'skip', +) -> InspectedAnnotation: + """Inspect an [annotation expression][], extracting any [type qualifier][] and metadata. + + An [annotation expression][] is a [type expression][] optionally surrounded by one or more + [type qualifiers][type qualifier] or by [`Annotated`][typing.Annotated]. This function will: + + - Unwrap the type expression, keeping track of the type qualifiers. + - Unwrap [`Annotated`][typing.Annotated] forms, keeping track of the annotated metadata. + + Args: + annotation: The annotation expression to be inspected. + annotation_source: The source of the annotation. Depending on the source (e.g. a class), different type + qualifiers may be (dis)allowed. To allow any type qualifier, use + [`AnnotationSource.ANY`][typing_inspection.introspection.AnnotationSource.ANY]. + unpack_type_aliases: What to do when encountering [PEP 695](https://peps.python.org/pep-0695/) + [type aliases][type-aliases]. Can be one of: + + - `'skip'`: Do not try to parse type aliases (the default): + ```pycon + >>> type MyInt = Annotated[int, 'meta'] + >>> inspect_annotation(MyInt, annotation_source=AnnotationSource.BARE, unpack_type_aliases='skip') + InspectedAnnotation(type=MyInt, qualifiers={}, metadata=[]) + ``` + + - `'lenient'`: Try to parse type aliases, and fallback to `'skip'` if the type alias + can't be inspected (because of an undefined forward reference): + ```pycon + >>> type MyInt = Annotated[Undefined, 'meta'] + >>> inspect_annotation(MyInt, annotation_source=AnnotationSource.BARE, unpack_type_aliases='lenient') + InspectedAnnotation(type=MyInt, qualifiers={}, metadata=[]) + >>> Undefined = int + >>> inspect_annotation(MyInt, annotation_source=AnnotationSource.BARE, unpack_type_aliases='lenient') + InspectedAnnotation(type=int, qualifiers={}, metadata=['meta']) + ``` + + - `'eager'`: Parse type aliases and raise any encountered [`NameError`][] exceptions. + + Returns: + The result of the inspected annotation, where the type expression, used qualifiers and metadata is stored. + + Example: + ```pycon + >>> inspect_annotation( + ... Final[Annotated[ClassVar[Annotated[int, 'meta_1']], 'meta_2']], + ... annotation_source=AnnotationSource.CLASS, + ... ) + ... + InspectedAnnotation(type=int, qualifiers={'class_var', 'final'}, metadata=['meta_1', 'meta_2']) + ``` + """ + allowed_qualifiers = annotation_source.allowed_qualifiers + qualifiers: set[Qualifier] = set() + metadata: list[Any] = [] + + while True: + annotation, _meta = _unpack_annotated(annotation, unpack_type_aliases=unpack_type_aliases) + if _meta: + metadata = _meta + metadata + continue + + origin = get_origin(annotation) + if origin is not None: + if typing_objects.is_classvar(origin): + if 'class_var' not in allowed_qualifiers: + raise ForbiddenQualifier('class_var') + qualifiers.add('class_var') + annotation = annotation.__args__[0] + elif typing_objects.is_final(origin): + if 'final' not in allowed_qualifiers: + raise ForbiddenQualifier('final') + qualifiers.add('final') + annotation = annotation.__args__[0] + elif typing_objects.is_required(origin): + if 'required' not in allowed_qualifiers: + raise ForbiddenQualifier('required') + qualifiers.add('required') + annotation = annotation.__args__[0] + elif typing_objects.is_notrequired(origin): + if 'not_required' not in allowed_qualifiers: + raise ForbiddenQualifier('not_required') + qualifiers.add('not_required') + annotation = annotation.__args__[0] + elif typing_objects.is_readonly(origin): + if 'read_only' not in allowed_qualifiers: + raise ForbiddenQualifier('not_required') + qualifiers.add('read_only') + annotation = annotation.__args__[0] + else: + # origin is not None but not a type qualifier nor `Annotated` (e.g. `list[int]`): + break + elif isinstance(annotation, InitVar): + if 'init_var' not in allowed_qualifiers: + raise ForbiddenQualifier('init_var') + qualifiers.add('init_var') + annotation = cast(Any, annotation.type) + else: + break + + # `Final`, `ClassVar` and `InitVar` are type qualifiers allowed to be used as a bare annotation: + if typing_objects.is_final(annotation): + if 'final' not in allowed_qualifiers: + raise ForbiddenQualifier('final') + qualifiers.add('final') + annotation = UNKNOWN + elif typing_objects.is_classvar(annotation): + if 'class_var' not in allowed_qualifiers: + raise ForbiddenQualifier('class_var') + qualifiers.add('class_var') + annotation = UNKNOWN + elif annotation is InitVar: + if 'init_var' not in allowed_qualifiers: + raise ForbiddenQualifier('init_var') + qualifiers.add('init_var') + annotation = UNKNOWN + + return InspectedAnnotation(annotation, qualifiers, metadata) + + +def _unpack_annotated_inner( + annotation: Any, unpack_type_aliases: Literal['lenient', 'eager'], check_annotated: bool +) -> tuple[Any, list[Any]]: + origin = get_origin(annotation) + if check_annotated and typing_objects.is_annotated(origin): + annotated_type = annotation.__origin__ + metadata = list(annotation.__metadata__) + + # The annotated type might be a PEP 695 type alias, so we need to recursively + # unpack it. Because Python already flattens `Annotated[Annotated[, ...], ...]` forms, + # we can skip the `is_annotated()` check in the next call: + annotated_type, sub_meta = _unpack_annotated_inner( + annotated_type, unpack_type_aliases=unpack_type_aliases, check_annotated=False + ) + metadata = sub_meta + metadata + return annotated_type, metadata + elif typing_objects.is_typealiastype(annotation): + try: + value = annotation.__value__ + except NameError: + if unpack_type_aliases == 'eager': + raise + else: + typ, metadata = _unpack_annotated_inner( + value, unpack_type_aliases=unpack_type_aliases, check_annotated=True + ) + if metadata: + # Having metadata means the type alias' `__value__` was an `Annotated` form + # (or, recursively, a type alias to an `Annotated` form). It is important to check + # for this, as we don't want to unpack other type aliases (e.g. `type MyInt = int`). + return typ, metadata + return annotation, [] + elif typing_objects.is_typealiastype(origin): + # When parameterized, PEP 695 type aliases become generic aliases + # (e.g. with `type MyList[T] = Annotated[list[T], ...]`, `MyList[int]` + # is a generic alias). + try: + value = origin.__value__ + except NameError: + if unpack_type_aliases == 'eager': + raise + else: + # While Python already handles type variable replacement for simple `Annotated` forms, + # we need to manually apply the same logic for PEP 695 type aliases: + # - With `MyList = Annotated[list[T], ...]`, `MyList[int] == Annotated[list[int], ...]` + # - With `type MyList[T] = Annotated[list[T], ...]`, `MyList[int].__value__ == Annotated[list[T], ...]`. + + try: + # To do so, we emulate the parameterization of the value with the arguments: + # with `type MyList[T] = Annotated[list[T], ...]`, to emulate `MyList[int]`, + # we do `Annotated[list[T], ...][int]` (which gives `Annotated[list[T], ...]`): + value = value[annotation.__args__] + except TypeError: + # Might happen if the type alias is parameterized, but its value doesn't have any + # type variables, e.g. `type MyInt[T] = int`. + pass + typ, metadata = _unpack_annotated_inner( + value, unpack_type_aliases=unpack_type_aliases, check_annotated=True + ) + if metadata: + return typ, metadata + return annotation, [] + + return annotation, [] + + +# This could eventually be made public: +def _unpack_annotated( + annotation: Any, /, *, unpack_type_aliases: Literal['skip', 'lenient', 'eager'] = 'eager' +) -> tuple[Any, list[Any]]: + if unpack_type_aliases == 'skip': + if typing_objects.is_annotated(get_origin(annotation)): + return annotation.__origin__, list(annotation.__metadata__) + else: + return annotation, [] + + return _unpack_annotated_inner(annotation, unpack_type_aliases=unpack_type_aliases, check_annotated=True) diff --git a/.venv/lib/python3.12/site-packages/typing_inspection/py.typed b/.venv/lib/python3.12/site-packages/typing_inspection/py.typed new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/typing_inspection/typing_objects.py b/.venv/lib/python3.12/site-packages/typing_inspection/typing_objects.py new file mode 100644 index 0000000000000000000000000000000000000000..dc44ba98cd8ccdba374982819083d89bbf808c2e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/typing_inspection/typing_objects.py @@ -0,0 +1,607 @@ +"""Low-level introspection utilities for [`typing`][] members. + +The provided functions in this module check against both the [`typing`][] and [`typing_extensions`][] +variants, if they exists and are different. +""" +# ruff: noqa: UP006 + +import collections.abc +import contextlib +import re +import sys +import typing +import warnings +from textwrap import dedent +from types import FunctionType, GenericAlias +from typing import Any, Final + +import typing_extensions +from typing_extensions import LiteralString, TypeAliasType, TypeIs, deprecated + +__all__ = ( + 'DEPRECATED_ALIASES', + 'NoneType', + 'is_annotated', + 'is_any', + 'is_classvar', + 'is_concatenate', + 'is_deprecated', + 'is_final', + 'is_forwardref', + 'is_generic', + 'is_literal', + 'is_literalstring', + 'is_namedtuple', + 'is_never', + 'is_newtype', + 'is_nodefault', + 'is_noextraitems', + 'is_noreturn', + 'is_notrequired', + 'is_paramspec', + 'is_paramspecargs', + 'is_paramspeckwargs', + 'is_readonly', + 'is_required', + 'is_self', + 'is_typealias', + 'is_typealiastype', + 'is_typeguard', + 'is_typeis', + 'is_typevar', + 'is_typevartuple', + 'is_union', + 'is_unpack', +) + +_IS_PY310 = sys.version_info[:2] == (3, 10) + + +def _compile_identity_check_function(member: LiteralString, function_name: LiteralString) -> FunctionType: + """Create a function checking that the function argument is the (unparameterized) typing `member`. + + The function will make sure to check against both the `typing` and `typing_extensions` + variants as depending on the Python version, the `typing_extensions` variant might be different. + For instance, on Python 3.9: + + ```pycon + >>> from typing import Literal as t_Literal + >>> from typing_extensions import Literal as te_Literal, get_origin + + >>> t_Literal is te_Literal + False + >>> get_origin(t_Literal[1]) + typing.Literal + >>> get_origin(te_Literal[1]) + typing_extensions.Literal + ``` + """ + in_typing = hasattr(typing, member) + in_typing_extensions = hasattr(typing_extensions, member) + + if in_typing and in_typing_extensions: + if getattr(typing, member) is getattr(typing_extensions, member): + check_code = f'obj is typing.{member}' + else: + check_code = f'obj is typing.{member} or obj is typing_extensions.{member}' + elif in_typing and not in_typing_extensions: + check_code = f'obj is typing.{member}' + elif not in_typing and in_typing_extensions: + check_code = f'obj is typing_extensions.{member}' + else: + check_code = 'False' + + func_code = dedent(f""" + def {function_name}(obj: Any, /) -> bool: + return {check_code} + """) + + locals_: dict[str, Any] = {} + globals_: dict[str, Any] = {'Any': Any, 'typing': typing, 'typing_extensions': typing_extensions} + exec(func_code, globals_, locals_) + return locals_[function_name] + + +def _compile_isinstance_check_function(member: LiteralString, function_name: LiteralString) -> FunctionType: + """Create a function checking that the function is an instance of the typing `member`. + + The function will make sure to check against both the `typing` and `typing_extensions` + variants as depending on the Python version, the `typing_extensions` variant might be different. + """ + in_typing = hasattr(typing, member) + in_typing_extensions = hasattr(typing_extensions, member) + + if in_typing and in_typing_extensions: + if getattr(typing, member) is getattr(typing_extensions, member): + check_code = f'isinstance(obj, typing.{member})' + else: + check_code = f'isinstance(obj, (typing.{member}, typing_extensions.{member}))' + elif in_typing and not in_typing_extensions: + check_code = f'isinstance(obj, typing.{member})' + elif not in_typing and in_typing_extensions: + check_code = f'isinstance(obj, typing_extensions.{member})' + else: + check_code = 'False' + + func_code = dedent(f""" + def {function_name}(obj: Any, /) -> 'TypeIs[{member}]': + return {check_code} + """) + + locals_: dict[str, Any] = {} + globals_: dict[str, Any] = {'Any': Any, 'typing': typing, 'typing_extensions': typing_extensions} + exec(func_code, globals_, locals_) + return locals_[function_name] + + +if sys.version_info >= (3, 10): + from types import NoneType +else: + NoneType = type(None) + +# Keep this ordered, as per `typing.__all__`: + +is_annotated = _compile_identity_check_function('Annotated', 'is_annotated') +is_annotated.__doc__ = """ +Return whether the argument is the [`Annotated`][typing.Annotated] [special form][]. + +```pycon +>>> is_annotated(Annotated) +True +>>> is_annotated(Annotated[int, ...]) +False +``` +""" + +is_any = _compile_identity_check_function('Any', 'is_any') +is_any.__doc__ = """ +Return whether the argument is the [`Any`][typing.Any] [special form][]. + +```pycon +>>> is_any(Any) +True +``` +""" + +is_classvar = _compile_identity_check_function('ClassVar', 'is_classvar') +is_classvar.__doc__ = """ +Return whether the argument is the [`ClassVar`][typing.ClassVar] [type qualifier][]. + +```pycon +>>> is_classvar(ClassVar) +True +>>> is_classvar(ClassVar[int]) +>>> False +``` +""" + +is_concatenate = _compile_identity_check_function('Concatenate', 'is_concatenate') +is_concatenate.__doc__ = """ +Return whether the argument is the [`Concatenate`][typing.Concatenate] [special form][]. + +```pycon +>>> is_concatenate(Concatenate) +True +>>> is_concatenate(Concatenate[int, P]) +False +``` +""" + +is_final = _compile_identity_check_function('Final', 'is_final') +is_final.__doc__ = """ +Return whether the argument is the [`Final`][typing.Final] [type qualifier][]. + +```pycon +>>> is_final(Final) +True +>>> is_final(Final[int]) +False +``` +""" + + +# Unlikely to have a different version in `typing-extensions`, but keep it consistent. +# Also note that starting in 3.14, this is an alias to `annotationlib.ForwardRef`, but +# accessing it from `typing` doesn't seem to be deprecated. +is_forwardref = _compile_isinstance_check_function('ForwardRef', 'is_forwardref') +is_forwardref.__doc__ = """ +Return whether the argument is an instance of [`ForwardRef`][typing.ForwardRef]. + +```pycon +>>> is_forwardref(ForwardRef('T')) +True +``` +""" + + +is_generic = _compile_identity_check_function('Generic', 'is_generic') +is_generic.__doc__ = """ +Return whether the argument is the [`Generic`][typing.Generic] [special form][]. + +```pycon +>>> is_generic(Generic) +True +>>> is_generic(Generic[T]) +False +``` +""" + +is_literal = _compile_identity_check_function('Literal', 'is_literal') +is_literal.__doc__ = """ +Return whether the argument is the [`Literal`][typing.Literal] [special form][]. + +```pycon +>>> is_literal(Literal) +True +>>> is_literal(Literal["a"]) +False +``` +""" + + +# `get_origin(Optional[int]) is Union`, so `is_optional()` isn't implemented. + +is_paramspec = _compile_isinstance_check_function('ParamSpec', 'is_paramspec') +is_paramspec.__doc__ = """ +Return whether the argument is an instance of [`ParamSpec`][typing.ParamSpec]. + +```pycon +>>> P = ParamSpec('P') +>>> is_paramspec(P) +True +``` +""" + +# Protocol? + +is_typevar = _compile_isinstance_check_function('TypeVar', 'is_typevar') +is_typevar.__doc__ = """ +Return whether the argument is an instance of [`TypeVar`][typing.TypeVar]. + +```pycon +>>> T = TypeVar('T') +>>> is_typevar(T) +True +``` +""" + +is_typevartuple = _compile_isinstance_check_function('TypeVarTuple', 'is_typevartuple') +is_typevartuple.__doc__ = """ +Return whether the argument is an instance of [`TypeVarTuple`][typing.TypeVarTuple]. + +```pycon +>>> Ts = TypeVarTuple('Ts') +>>> is_typevartuple(Ts) +True +``` +""" + +is_union = _compile_identity_check_function('Union', 'is_union') +is_union.__doc__ = """ +Return whether the argument is the [`Union`][typing.Union] [special form][]. + +This function can also be used to check for the [`Optional`][typing.Optional] [special form][], +as at runtime, `Optional[int]` is equivalent to `Union[int, None]`. + +```pycon +>>> is_union(Union) +True +>>> is_union(Union[int, str]) +False +``` + +!!! warning + This does not check for unions using the [new syntax][types-union] (e.g. `int | str`). +""" + + +def is_namedtuple(obj: Any, /) -> bool: + """Return whether the argument is a named tuple type. + + This includes [`NamedTuple`][typing.NamedTuple] subclasses and classes created from the + [`collections.namedtuple`][] factory function. + + ```pycon + >>> class User(NamedTuple): + ... name: str + ... + >>> is_namedtuple(User) + True + >>> City = collections.namedtuple('City', []) + >>> is_namedtuple(City) + True + >>> is_namedtuple(NamedTuple) + False + ``` + """ + return isinstance(obj, type) and issubclass(obj, tuple) and hasattr(obj, '_fields') # pyright: ignore[reportUnknownArgumentType] + + +# TypedDict? + +# BinaryIO? IO? TextIO? + +is_literalstring = _compile_identity_check_function('LiteralString', 'is_literalstring') +is_literalstring.__doc__ = """ +Return whether the argument is the [`LiteralString`][typing.LiteralString] [special form][]. + +```pycon +>>> is_literalstring(LiteralString) +True +``` +""" + +is_never = _compile_identity_check_function('Never', 'is_never') +is_never.__doc__ = """ +Return whether the argument is the [`Never`][typing.Never] [special form][]. + +```pycon +>>> is_never(Never) +True +``` +""" + +if sys.version_info >= (3, 10): + is_newtype = _compile_isinstance_check_function('NewType', 'is_newtype') +else: # On Python 3.10, `NewType` is a function. + + def is_newtype(obj: Any, /) -> bool: + return hasattr(obj, '__supertype__') + + +is_newtype.__doc__ = """ +Return whether the argument is a [`NewType`][typing.NewType]. + +```pycon +>>> UserId = NewType("UserId", int) +>>> is_newtype(UserId) +True +``` +""" + +is_nodefault = _compile_identity_check_function('NoDefault', 'is_nodefault') +is_nodefault.__doc__ = """ +Return whether the argument is the [`NoDefault`][typing.NoDefault] sentinel object. + +```pycon +>>> is_nodefault(NoDefault) +True +``` +""" + +is_noextraitems = _compile_identity_check_function('NoExtraItems', 'is_noextraitems') +is_noextraitems.__doc__ = """ +Return whether the argument is the `NoExtraItems` sentinel object. + +```pycon +>>> is_noextraitems(NoExtraItems) +True +``` +""" + +is_noreturn = _compile_identity_check_function('NoReturn', 'is_noreturn') +is_noreturn.__doc__ = """ +Return whether the argument is the [`NoReturn`][typing.NoReturn] [special form][]. + +```pycon +>>> is_noreturn(NoReturn) +True +>>> is_noreturn(Never) +False +``` +""" + +is_notrequired = _compile_identity_check_function('NotRequired', 'is_notrequired') +is_notrequired.__doc__ = """ +Return whether the argument is the [`NotRequired`][typing.NotRequired] [special form][]. + +```pycon +>>> is_notrequired(NotRequired) +True +``` +""" + +is_paramspecargs = _compile_isinstance_check_function('ParamSpecArgs', 'is_paramspecargs') +is_paramspecargs.__doc__ = """ +Return whether the argument is an instance of [`ParamSpecArgs`][typing.ParamSpecArgs]. + +```pycon +>>> P = ParamSpec('P') +>>> is_paramspecargs(P.args) +True +``` +""" + +is_paramspeckwargs = _compile_isinstance_check_function('ParamSpecKwargs', 'is_paramspeckwargs') +is_paramspeckwargs.__doc__ = """ +Return whether the argument is an instance of [`ParamSpecKwargs`][typing.ParamSpecKwargs]. + +```pycon +>>> P = ParamSpec('P') +>>> is_paramspeckwargs(P.kwargs) +True +``` +""" + +is_readonly = _compile_identity_check_function('ReadOnly', 'is_readonly') +is_readonly.__doc__ = """ +Return whether the argument is the [`ReadOnly`][typing.ReadOnly] [special form][]. + +```pycon +>>> is_readonly(ReadOnly) +True +``` +""" + +is_required = _compile_identity_check_function('Required', 'is_required') +is_required.__doc__ = """ +Return whether the argument is the [`Required`][typing.Required] [special form][]. + +```pycon +>>> is_required(Required) +True +``` +""" + +is_self = _compile_identity_check_function('Self', 'is_self') +is_self.__doc__ = """ +Return whether the argument is the [`Self`][typing.Self] [special form][]. + +```pycon +>>> is_self(Self) +True +``` +""" + +# TYPE_CHECKING? + +is_typealias = _compile_identity_check_function('TypeAlias', 'is_typealias') +is_typealias.__doc__ = """ +Return whether the argument is the [`TypeAlias`][typing.TypeAlias] [special form][]. + +```pycon +>>> is_typealias(TypeAlias) +True +``` +""" + +is_typeguard = _compile_identity_check_function('TypeGuard', 'is_typeguard') +is_typeguard.__doc__ = """ +Return whether the argument is the [`TypeGuard`][typing.TypeGuard] [special form][]. + +```pycon +>>> is_typeguard(TypeGuard) +True +``` +""" + +is_typeis = _compile_identity_check_function('TypeIs', 'is_typeis') +is_typeis.__doc__ = """ +Return whether the argument is the [`TypeIs`][typing.TypeIs] [special form][]. + +```pycon +>>> is_typeis(TypeIs) +True +``` +""" + +_is_typealiastype_inner = _compile_isinstance_check_function('TypeAliasType', '_is_typealiastype_inner') + + +if _IS_PY310: + # Parameterized PEP 695 type aliases are instances of `types.GenericAlias` in typing_extensions>=4.13.0. + # On Python 3.10, with `Alias[int]` being such an instance of `GenericAlias`, + # `isinstance(Alias[int], TypeAliasType)` returns `True`. + # See https://github.com/python/cpython/issues/89828. + def is_typealiastype(obj: Any, /) -> 'TypeIs[TypeAliasType]': + return type(obj) is not GenericAlias and _is_typealiastype_inner(obj) +else: + is_typealiastype = _compile_isinstance_check_function('TypeAliasType', 'is_typealiastype') + +is_typealiastype.__doc__ = """ +Return whether the argument is a [`TypeAliasType`][typing.TypeAliasType] instance. + +```pycon +>>> type MyInt = int +>>> is_typealiastype(MyInt) +True +>>> MyStr = TypeAliasType("MyStr", str) +>>> is_typealiastype(MyStr): +True +>>> type MyList[T] = list[T] +>>> is_typealiastype(MyList[int]) +False +``` +""" + +is_unpack = _compile_identity_check_function('Unpack', 'is_unpack') +is_unpack.__doc__ = """ +Return whether the argument is the [`Unpack`][typing.Unpack] [special form][]. + +```pycon +>>> is_unpack(Unpack) +True +>>> is_unpack(Unpack[Ts]) +False +``` +""" + + +if sys.version_info >= (3, 13): + + def is_deprecated(obj: Any, /) -> 'TypeIs[deprecated]': + return isinstance(obj, (warnings.deprecated, typing_extensions.deprecated)) + +else: + + def is_deprecated(obj: Any, /) -> 'TypeIs[deprecated]': + return isinstance(obj, typing_extensions.deprecated) + + +is_deprecated.__doc__ = """ +Return whether the argument is a [`deprecated`][warnings.deprecated] instance. + +This also includes the [`typing_extensions` backport][typing_extensions.deprecated]. + +```pycon +>>> is_deprecated(warnings.deprecated('message')) +True +>>> is_deprecated(typing_extensions.deprecated('message')) +True +``` +""" + + +# Aliases defined in the `typing` module using `typing._SpecialGenericAlias` (itself aliased as `alias()`): +DEPRECATED_ALIASES: Final[dict[Any, type[Any]]] = { + typing.Hashable: collections.abc.Hashable, + typing.Awaitable: collections.abc.Awaitable, + typing.Coroutine: collections.abc.Coroutine, + typing.AsyncIterable: collections.abc.AsyncIterable, + typing.AsyncIterator: collections.abc.AsyncIterator, + typing.Iterable: collections.abc.Iterable, + typing.Iterator: collections.abc.Iterator, + typing.Reversible: collections.abc.Reversible, + typing.Sized: collections.abc.Sized, + typing.Container: collections.abc.Container, + typing.Collection: collections.abc.Collection, + # type ignore reason: https://github.com/python/typeshed/issues/6257: + typing.Callable: collections.abc.Callable, # pyright: ignore[reportAssignmentType, reportUnknownMemberType] + typing.AbstractSet: collections.abc.Set, + typing.MutableSet: collections.abc.MutableSet, + typing.Mapping: collections.abc.Mapping, + typing.MutableMapping: collections.abc.MutableMapping, + typing.Sequence: collections.abc.Sequence, + typing.MutableSequence: collections.abc.MutableSequence, + typing.Tuple: tuple, + typing.List: list, + typing.Deque: collections.deque, + typing.Set: set, + typing.FrozenSet: frozenset, + typing.MappingView: collections.abc.MappingView, + typing.KeysView: collections.abc.KeysView, + typing.ItemsView: collections.abc.ItemsView, + typing.ValuesView: collections.abc.ValuesView, + typing.Dict: dict, + typing.DefaultDict: collections.defaultdict, + typing.OrderedDict: collections.OrderedDict, + typing.Counter: collections.Counter, + typing.ChainMap: collections.ChainMap, + typing.Generator: collections.abc.Generator, + typing.AsyncGenerator: collections.abc.AsyncGenerator, + typing.Type: type, + # Defined in `typing.__getattr__`: + typing.Pattern: re.Pattern, + typing.Match: re.Match, + typing.ContextManager: contextlib.AbstractContextManager, + typing.AsyncContextManager: contextlib.AbstractAsyncContextManager, + # Skipped: `ByteString` (deprecated, removed in 3.14) +} +"""A mapping between the deprecated typing aliases to their replacement, as per [PEP 585](https://peps.python.org/pep-0585/).""" + + +# Add the `typing_extensions` aliases: +for alias, target in list(DEPRECATED_ALIASES.items()): + # Use `alias.__name__` when we drop support for Python 3.9 + if (te_alias := getattr(typing_extensions, alias._name, None)) is not None: + DEPRECATED_ALIASES[te_alias] = target diff --git a/.venv/lib/python3.12/site-packages/typing_inspection/typing_objects.pyi b/.venv/lib/python3.12/site-packages/typing_inspection/typing_objects.pyi new file mode 100644 index 0000000000000000000000000000000000000000..5071598005a21063ff3b2a2a1dedced885267de6 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/typing_inspection/typing_objects.pyi @@ -0,0 +1,417 @@ +# Stub file generated using: +# `stubgen --inspect-mode --include-docstrings -m typing_inspection.typing_objects` +# (manual edits need to be applied). +"""Low-level introspection utilities for [`typing`][] members. + +The provided functions in this module check against both the [`typing`][] and [`typing_extensions`][] +variants, if they exists and are different. +""" + +import sys +from typing import Any, Final, ForwardRef, NewType, TypeVar + +from typing_extensions import ParamSpec, ParamSpecArgs, ParamSpecKwargs, TypeAliasType, TypeIs, TypeVarTuple, deprecated + +__all__ = [ + 'DEPRECATED_ALIASES', + 'NoneType', + 'is_annotated', + 'is_any', + 'is_classvar', + 'is_concatenate', + 'is_deprecated', + 'is_final', + 'is_generic', + 'is_literal', + 'is_literalstring', + 'is_namedtuple', + 'is_never', + 'is_newtype', + 'is_nodefault', + 'is_noextraitems', + 'is_noreturn', + 'is_notrequired', + 'is_paramspec', + 'is_paramspecargs', + 'is_paramspeckwargs', + 'is_readonly', + 'is_required', + 'is_self', + 'is_typealias', + 'is_typealiastype', + 'is_typeguard', + 'is_typeis', + 'is_typevar', + 'is_typevartuple', + 'is_union', + 'is_unpack', +] + +if sys.version_info >= (3, 10): + from types import NoneType +else: + NoneType = type(None) + +def is_annotated(obj: Any, /) -> bool: + """ + Return whether the argument is the [`Annotated`][typing.Annotated] [special form][]. + + ```pycon + >>> is_annotated(Annotated) + True + >>> is_annotated(Annotated[int, ...]) + False + ``` + """ + +def is_any(obj: Any, /) -> bool: + """ + Return whether the argument is the [`Any`][typing.Any] [special form][]. + + ```pycon + >>> is_any(Any) + True + ``` + """ + +def is_classvar(obj: Any, /) -> bool: + """ + Return whether the argument is the [`ClassVar`][typing.ClassVar] [type qualifier][]. + + ```pycon + >>> is_classvar(ClassVar) + True + >>> is_classvar(ClassVar[int]) + >>> False + ``` + """ + +def is_concatenate(obj: Any, /) -> bool: + """ + Return whether the argument is the [`Concatenate`][typing.Concatenate] [special form][]. + + ```pycon + >>> is_concatenate(Concatenate) + True + >>> is_concatenate(Concatenate[int, P]) + False + ``` + """ + +def is_final(obj: Any, /) -> bool: + """ + Return whether the argument is the [`Final`][typing.Final] [type qualifier][]. + + ```pycon + >>> is_final(Final) + True + >>> is_final(Final[int]) + False + ``` + """ + +def is_forwardref(obj: Any, /) -> TypeIs[ForwardRef]: + """ + Return whether the argument is an instance of [`ForwardRef`][typing.ForwardRef]. + + ```pycon + >>> is_forwardref(ForwardRef('T')) + True + ``` + """ + +def is_generic(obj: Any, /) -> bool: + """ + Return whether the argument is the [`Generic`][typing.Generic] [special form][]. + + ```pycon + >>> is_generic(Generic) + True + >>> is_generic(Generic[T]) + False + ``` + """ + +def is_literal(obj: Any, /) -> bool: + """ + Return whether the argument is the [`Literal`][typing.Literal] [special form][]. + + ```pycon + >>> is_literal(Literal) + True + >>> is_literal(Literal["a"]) + False + ``` + """ + +def is_paramspec(obj: Any, /) -> TypeIs[ParamSpec]: + """ + Return whether the argument is an instance of [`ParamSpec`][typing.ParamSpec]. + + ```pycon + >>> P = ParamSpec('P') + >>> is_paramspec(P) + True + ``` + """ + +def is_typevar(obj: Any, /) -> TypeIs[TypeVar]: + """ + Return whether the argument is an instance of [`TypeVar`][typing.TypeVar]. + + ```pycon + >>> T = TypeVar('T') + >>> is_typevar(T) + True + ``` + """ + +def is_typevartuple(obj: Any, /) -> TypeIs[TypeVarTuple]: + """ + Return whether the argument is an instance of [`TypeVarTuple`][typing.TypeVarTuple]. + + ```pycon + >>> Ts = TypeVarTuple('Ts') + >>> is_typevartuple(Ts) + True + ``` + """ + +def is_union(obj: Any, /) -> bool: + """ + Return whether the argument is the [`Union`][typing.Union] [special form][]. + + This function can also be used to check for the [`Optional`][typing.Optional] [special form][], + as at runtime, `Optional[int]` is equivalent to `Union[int, None]`. + + ```pycon + >>> is_union(Union) + True + >>> is_union(Union[int, str]) + False + ``` + + !!! warning + This does not check for unions using the [new syntax][types-union] (e.g. `int | str`). + """ + +def is_namedtuple(obj: Any, /) -> bool: + """Return whether the argument is a named tuple type. + + This includes [`NamedTuple`][typing.NamedTuple] subclasses and classes created from the + [`collections.namedtuple`][] factory function. + + ```pycon + >>> class User(NamedTuple): + ... name: str + ... + >>> is_namedtuple(User) + True + >>> City = collections.namedtuple('City', []) + >>> is_namedtuple(City) + True + >>> is_namedtuple(NamedTuple) + False + ``` + """ + +def is_literalstring(obj: Any, /) -> bool: + """ + Return whether the argument is the [`LiteralString`][typing.LiteralString] [special form][]. + + ```pycon + >>> is_literalstring(LiteralString) + True + ``` + """ + +def is_never(obj: Any, /) -> bool: + """ + Return whether the argument is the [`Never`][typing.Never] [special form][]. + + ```pycon + >>> is_never(Never) + True + ``` + """ + +def is_newtype(obj: Any, /) -> TypeIs[NewType]: + """ + Return whether the argument is a [`NewType`][typing.NewType]. + + ```pycon + >>> UserId = NewType("UserId", int) + >>> is_newtype(UserId) + True + ``` + """ + +def is_nodefault(obj: Any, /) -> bool: + """ + Return whether the argument is the [`NoDefault`][typing.NoDefault] sentinel object. + + ```pycon + >>> is_nodefault(NoDefault) + True + ``` + """ + +def is_noextraitems(obj: Any, /) -> bool: + """ + Return whether the argument is the `NoExtraItems` sentinel object. + + ```pycon + >>> is_noextraitems(NoExtraItems) + True + ``` + """ + +def is_noreturn(obj: Any, /) -> bool: + """ + Return whether the argument is the [`NoReturn`][typing.NoReturn] [special form][]. + + ```pycon + >>> is_noreturn(NoReturn) + True + >>> is_noreturn(Never) + False + ``` + """ + +def is_notrequired(obj: Any, /) -> bool: + """ + Return whether the argument is the [`NotRequired`][typing.NotRequired] [special form][]. + + ```pycon + >>> is_notrequired(NotRequired) + True + ``` + """ + +def is_paramspecargs(obj: Any, /) -> TypeIs[ParamSpecArgs]: + """ + Return whether the argument is an instance of [`ParamSpecArgs`][typing.ParamSpecArgs]. + + ```pycon + >>> P = ParamSpec('P') + >>> is_paramspecargs(P.args) + True + ``` + """ + +def is_paramspeckwargs(obj: Any, /) -> TypeIs[ParamSpecKwargs]: + """ + Return whether the argument is an instance of [`ParamSpecKwargs`][typing.ParamSpecKwargs]. + + ```pycon + >>> P = ParamSpec('P') + >>> is_paramspeckwargs(P.kwargs) + True + ``` + """ + +def is_readonly(obj: Any, /) -> bool: + """ + Return whether the argument is the [`ReadOnly`][typing.ReadOnly] [special form][]. + + ```pycon + >>> is_readonly(ReadOnly) + True + ``` + """ + +def is_required(obj: Any, /) -> bool: + """ + Return whether the argument is the [`Required`][typing.Required] [special form][]. + + ```pycon + >>> is_required(Required) + True + ``` + """ + +def is_self(obj: Any, /) -> bool: + """ + Return whether the argument is the [`Self`][typing.Self] [special form][]. + + ```pycon + >>> is_self(Self) + True + ``` + """ + +def is_typealias(obj: Any, /) -> bool: + """ + Return whether the argument is the [`TypeAlias`][typing.TypeAlias] [special form][]. + + ```pycon + >>> is_typealias(TypeAlias) + True + ``` + """ + +def is_typeguard(obj: Any, /) -> bool: + """ + Return whether the argument is the [`TypeGuard`][typing.TypeGuard] [special form][]. + + ```pycon + >>> is_typeguard(TypeGuard) + True + ``` + """ + +def is_typeis(obj: Any, /) -> bool: + """ + Return whether the argument is the [`TypeIs`][typing.TypeIs] [special form][]. + + ```pycon + >>> is_typeis(TypeIs) + True + ``` + """ + +def is_typealiastype(obj: Any, /) -> TypeIs[TypeAliasType]: + """ + Return whether the argument is a [`TypeAliasType`][typing.TypeAliasType] instance. + + ```pycon + >>> type MyInt = int + >>> is_typealiastype(MyInt) + True + >>> MyStr = TypeAliasType("MyStr", str) + >>> is_typealiastype(MyStr): + True + >>> type MyList[T] = list[T] + >>> is_typealiastype(MyList[int]) + False + ``` + """ + +def is_unpack(obj: Any, /) -> bool: + """ + Return whether the argument is the [`Unpack`][typing.Unpack] [special form][]. + + ```pycon + >>> is_unpack(Unpack) + True + >>> is_unpack(Unpack[Ts]) + False + ``` + """ + +def is_deprecated(obj: Any, /) -> TypeIs[deprecated]: + """ + Return whether the argument is a [`deprecated`][warnings.deprecated] instance. + + This also includes the [`typing_extensions` backport][typing_extensions.deprecated]. + + ```pycon + >>> is_deprecated(warnings.deprecated('message')) + True + >>> is_deprecated(typing_extensions.deprecated('deprecated')) + True + ``` + """ + +DEPRECATED_ALIASES: Final[dict[Any, type[Any]]] +"""A mapping between the deprecated typing aliases to their replacement, as per [PEP 585](https://peps.python.org/pep-0585/).""" diff --git a/.venv/lib/python3.12/site-packages/urllib3-2.5.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/urllib3-2.5.0.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/urllib3-2.5.0.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/urllib3-2.5.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/urllib3-2.5.0.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..15116c784ed1f8f0f524513d606a26bf6765e923 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/urllib3-2.5.0.dist-info/METADATA @@ -0,0 +1,154 @@ +Metadata-Version: 2.4 +Name: urllib3 +Version: 2.5.0 +Summary: HTTP library with thread-safe connection pooling, file post, and more. +Project-URL: Changelog, https://github.com/urllib3/urllib3/blob/main/CHANGES.rst +Project-URL: Documentation, https://urllib3.readthedocs.io +Project-URL: Code, https://github.com/urllib3/urllib3 +Project-URL: Issue tracker, https://github.com/urllib3/urllib3/issues +Author-email: Andrey Petrov +Maintainer-email: Seth Michael Larson , Quentin Pradet , Illia Volochii +License-Expression: MIT +License-File: LICENSE.txt +Keywords: filepost,http,httplib,https,pooling,ssl,threadsafe,urllib +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +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.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: WWW/HTTP +Classifier: Topic :: Software Development :: Libraries +Requires-Python: >=3.9 +Provides-Extra: brotli +Requires-Dist: brotli>=1.0.9; (platform_python_implementation == 'CPython') and extra == 'brotli' +Requires-Dist: brotlicffi>=0.8.0; (platform_python_implementation != 'CPython') and extra == 'brotli' +Provides-Extra: h2 +Requires-Dist: h2<5,>=4; extra == 'h2' +Provides-Extra: socks +Requires-Dist: pysocks!=1.5.7,<2.0,>=1.5.6; extra == 'socks' +Provides-Extra: zstd +Requires-Dist: zstandard>=0.18.0; extra == 'zstd' +Description-Content-Type: text/markdown + +

+ +![urllib3](https://github.com/urllib3/urllib3/raw/main/docs/_static/banner_github.svg) + +

+ +

+ PyPI Version + Python Versions + Join our Discord + Coverage Status + Build Status on GitHub + Documentation Status
+ OpenSSF Scorecard + SLSA 3 + CII Best Practices +

+ +urllib3 is a powerful, *user-friendly* HTTP client for Python. Much of the +Python ecosystem already uses urllib3 and you should too. +urllib3 brings many critical features that are missing from the Python +standard libraries: + +- Thread safety. +- Connection pooling. +- Client-side SSL/TLS verification. +- File uploads with multipart encoding. +- Helpers for retrying requests and dealing with HTTP redirects. +- Support for gzip, deflate, brotli, and zstd encoding. +- Proxy support for HTTP and SOCKS. +- 100% test coverage. + +urllib3 is powerful and easy to use: + +```python3 +>>> import urllib3 +>>> resp = urllib3.request("GET", "http://httpbin.org/robots.txt") +>>> resp.status +200 +>>> resp.data +b"User-agent: *\nDisallow: /deny\n" +``` + +## Installing + +urllib3 can be installed with [pip](https://pip.pypa.io): + +```bash +$ python -m pip install urllib3 +``` + +Alternatively, you can grab the latest source code from [GitHub](https://github.com/urllib3/urllib3): + +```bash +$ git clone https://github.com/urllib3/urllib3.git +$ cd urllib3 +$ pip install . +``` + + +## Documentation + +urllib3 has usage and reference documentation at [urllib3.readthedocs.io](https://urllib3.readthedocs.io). + + +## Community + +urllib3 has a [community Discord channel](https://discord.gg/urllib3) for asking questions and +collaborating with other contributors. Drop by and say hello 👋 + + +## Contributing + +urllib3 happily accepts contributions. Please see our +[contributing documentation](https://urllib3.readthedocs.io/en/latest/contributing.html) +for some tips on getting started. + + +## Security Disclosures + +To report a security vulnerability, please use the +[Tidelift security contact](https://tidelift.com/security). +Tidelift will coordinate the fix and disclosure with maintainers. + + +## Maintainers + +- [@sethmlarson](https://github.com/sethmlarson) (Seth M. Larson) +- [@pquentin](https://github.com/pquentin) (Quentin Pradet) +- [@illia-v](https://github.com/illia-v) (Illia Volochii) +- [@theacodes](https://github.com/theacodes) (Thea Flowers) +- [@haikuginger](https://github.com/haikuginger) (Jess Shapiro) +- [@lukasa](https://github.com/lukasa) (Cory Benfield) +- [@sigmavirus24](https://github.com/sigmavirus24) (Ian Stapleton Cordasco) +- [@shazow](https://github.com/shazow) (Andrey Petrov) + +👋 + + +## Sponsorship + +If your company benefits from this library, please consider [sponsoring its +development](https://urllib3.readthedocs.io/en/latest/sponsors.html). + + +## For Enterprise + +Professional support for urllib3 is available as part of the [Tidelift +Subscription][1]. Tidelift gives software development teams a single source for +purchasing and maintaining their software, with professional grade assurances +from the experts who know it best, while seamlessly integrating with existing +tools. + +[1]: https://tidelift.com/subscription/pkg/pypi-urllib3?utm_source=pypi-urllib3&utm_medium=referral&utm_campaign=readme diff --git a/.venv/lib/python3.12/site-packages/urllib3-2.5.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/urllib3-2.5.0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..69197e5c04c7e1336cf995b73ce0b33ce22e2ff8 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/urllib3-2.5.0.dist-info/RECORD @@ -0,0 +1,44 @@ +urllib3-2.5.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +urllib3-2.5.0.dist-info/METADATA,sha256=maYkTIZt0a-lkEC-hMZWbCBmcGZyJcYOeRk4_nuTrNc,6461 +urllib3-2.5.0.dist-info/RECORD,, +urllib3-2.5.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +urllib3-2.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87 +urllib3-2.5.0.dist-info/licenses/LICENSE.txt,sha256=Ew46ZNX91dCWp1JpRjSn2d8oRGnehuVzIQAmgEHj1oY,1093 +urllib3/__init__.py,sha256=JMo1tg1nIV1AeJ2vENC_Txfl0e5h6Gzl9DGVk1rWRbo,6979 +urllib3/_base_connection.py,sha256=T1cwH3RhzsrBh6Bz3AOGVDboRsE7veijqZPXXQTR2Rg,5568 +urllib3/_collections.py,sha256=tM7c6J1iKtWZYV_QGYb8-r7Nr1524Dehnsa0Ufh6_mU,17295 +urllib3/_request_methods.py,sha256=gCeF85SO_UU4WoPwYHIoz_tw-eM_EVOkLFp8OFsC7DA,9931 +urllib3/_version.py,sha256=ZlSUkBo_Pd90B6pM0GDO7l2vitQD3QCK3xPR_K0zFJA,511 +urllib3/connection.py,sha256=iP4pgSJtpusXyYlejzNn-gih_wWCxMU-qy6OU1kaapc,42613 +urllib3/connectionpool.py,sha256=ZEhudsa8BIubD2M0XoxBBsjxbsXwMgUScH7oQ9i-j1Y,43371 +urllib3/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +urllib3/contrib/emscripten/__init__.py,sha256=u6KNgzjlFZbuAAXa_ybCR7gQ71VJESnF-IIdDA73brw,733 +urllib3/contrib/emscripten/connection.py,sha256=j8DR_flE7hsoFhNfiqHLiaPaCsVbzG44jgahwvsQ52A,8771 +urllib3/contrib/emscripten/emscripten_fetch_worker.js,sha256=CDfYF_9CDobtx2lGidyJ1zjDEvwNT5F-dchmVWXDh0E,3655 +urllib3/contrib/emscripten/fetch.py,sha256=kco06lWoQ-fdFfN51-nzeTywPVBEHg89WIst33H3xcg,23484 +urllib3/contrib/emscripten/request.py,sha256=mL28szy1KvE3NJhWor5jNmarp8gwplDU-7gwGZY5g0Q,566 +urllib3/contrib/emscripten/response.py,sha256=7oVPENYZHuzEGRtG40HonpH5tAIYHsGcHPbJt2Z0U-Y,9507 +urllib3/contrib/pyopenssl.py,sha256=Xp5Ym05VgXGhHa0C4wlutvHxY8SnKSS6WLb2t5Miu0s,19720 +urllib3/contrib/socks.py,sha256=-iardc61GypsJzD6W6yuRS7KVCyfowcQrl_719H7lIM,7549 +urllib3/exceptions.py,sha256=pziumHf0Vwx3z4gvUy7ou8nlM2yIYX0N3l3znEdeF5U,9938 +urllib3/fields.py,sha256=FCf7UULSkf10cuTRUWTQESzxgl1WT8e2aCy3kfyZins,10829 +urllib3/filepost.py,sha256=U8eNZ-mpKKHhrlbHEEiTxxgK16IejhEa7uz42yqA_dI,2388 +urllib3/http2/__init__.py,sha256=xzrASH7R5ANRkPJOot5lGnATOq3KKuyXzI42rcnwmqs,1741 +urllib3/http2/connection.py,sha256=4DB0DkZEC3yIkhGjUDIHB17wrYCLaL0Ag5bDW2_mGPI,12694 +urllib3/http2/probe.py,sha256=nnAkqbhAakOiF75rz7W0udZ38Eeh_uD8fjV74N73FEI,3014 +urllib3/poolmanager.py,sha256=oKsgP1EsAI4OVgK9-9D3AYXZS5HYV8yKUSog-QbJ8Ts,23866 +urllib3/py.typed,sha256=UaCuPFa3H8UAakbt-5G8SPacldTOGvJv18pPjUJ5gDY,93 +urllib3/response.py,sha256=TVTSu6Q1U0U7hoHYMIRxxuh4zroeMo8b5EI4DOA13Eo,46480 +urllib3/util/__init__.py,sha256=-qeS0QceivazvBEKDNFCAI-6ACcdDOE4TMvo7SLNlAQ,1001 +urllib3/util/connection.py,sha256=JjO722lzHlzLXPTkr9ZWBdhseXnMVjMSb1DJLVrXSnQ,4444 +urllib3/util/proxy.py,sha256=seP8-Q5B6bB0dMtwPj-YcZZQ30vHuLqRu-tI0JZ2fzs,1148 +urllib3/util/request.py,sha256=XuAsEBT58DAZYUTwpMH5Hr3A1OPoMNvNIYIunbIqbc8,8411 +urllib3/util/response.py,sha256=vQE639uoEhj1vpjEdxu5lNIhJCSUZkd7pqllUI0BZOA,3374 +urllib3/util/retry.py,sha256=bj-2YUqblxLlv8THg5fxww-DM54XCbjgZXIQ71XioCY,18459 +urllib3/util/ssl_.py,sha256=jxnQ3msYkVaokJVWqHNnAVdVtDdidrTHDeyk50gwqaQ,19786 +urllib3/util/ssl_match_hostname.py,sha256=Di7DU7zokoltapT_F0Sj21ffYxwaS_cE5apOtwueeyA,5845 +urllib3/util/ssltransport.py,sha256=Ez4O8pR_vT8dan_FvqBYS6dgDfBXEMfVfrzcdUoWfi4,8847 +urllib3/util/timeout.py,sha256=4eT1FVeZZU7h7mYD1Jq2OXNe4fxekdNvhoWUkZusRpA,10346 +urllib3/util/url.py,sha256=WRh-TMYXosmgp8m8lT4H5spoHw5yUjlcMCfU53AkoAs,15205 +urllib3/util/util.py,sha256=j3lbZK1jPyiwD34T8IgJzdWEZVT-4E-0vYIJi9UjeNA,1146 +urllib3/util/wait.py,sha256=_ph8IrUR3sqPqi0OopQgJUlH4wzkGeM5CiyA7XGGtmI,4423 diff --git a/.venv/lib/python3.12/site-packages/urllib3-2.5.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/urllib3-2.5.0.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/urllib3-2.5.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/urllib3-2.5.0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..12228d414b6cfed7c39d3781c85c63256a1d7fb5 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/urllib3-2.5.0.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.27.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/.venv/lib/python3.12/site-packages/urllib3/__init__.py b/.venv/lib/python3.12/site-packages/urllib3/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3fe782c8a45bbabcf240f3cac4303ac12b0ec274 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/urllib3/__init__.py @@ -0,0 +1,211 @@ +""" +Python HTTP library with thread-safe connection pooling, file post support, user friendly, and more +""" + +from __future__ import annotations + +# Set default logging handler to avoid "No handler found" warnings. +import logging +import sys +import typing +import warnings +from logging import NullHandler + +from . import exceptions +from ._base_connection import _TYPE_BODY +from ._collections import HTTPHeaderDict +from ._version import __version__ +from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool, connection_from_url +from .filepost import _TYPE_FIELDS, encode_multipart_formdata +from .poolmanager import PoolManager, ProxyManager, proxy_from_url +from .response import BaseHTTPResponse, HTTPResponse +from .util.request import make_headers +from .util.retry import Retry +from .util.timeout import Timeout + +# Ensure that Python is compiled with OpenSSL 1.1.1+ +# If the 'ssl' module isn't available at all that's +# fine, we only care if the module is available. +try: + import ssl +except ImportError: + pass +else: + if not ssl.OPENSSL_VERSION.startswith("OpenSSL "): # Defensive: + warnings.warn( + "urllib3 v2 only supports OpenSSL 1.1.1+, currently " + f"the 'ssl' module is compiled with {ssl.OPENSSL_VERSION!r}. " + "See: https://github.com/urllib3/urllib3/issues/3020", + exceptions.NotOpenSSLWarning, + ) + elif ssl.OPENSSL_VERSION_INFO < (1, 1, 1): # Defensive: + raise ImportError( + "urllib3 v2 only supports OpenSSL 1.1.1+, currently " + f"the 'ssl' module is compiled with {ssl.OPENSSL_VERSION!r}. " + "See: https://github.com/urllib3/urllib3/issues/2168" + ) + +__author__ = "Andrey Petrov (andrey.petrov@shazow.net)" +__license__ = "MIT" +__version__ = __version__ + +__all__ = ( + "HTTPConnectionPool", + "HTTPHeaderDict", + "HTTPSConnectionPool", + "PoolManager", + "ProxyManager", + "HTTPResponse", + "Retry", + "Timeout", + "add_stderr_logger", + "connection_from_url", + "disable_warnings", + "encode_multipart_formdata", + "make_headers", + "proxy_from_url", + "request", + "BaseHTTPResponse", +) + +logging.getLogger(__name__).addHandler(NullHandler()) + + +def add_stderr_logger( + level: int = logging.DEBUG, +) -> logging.StreamHandler[typing.TextIO]: + """ + Helper for quickly adding a StreamHandler to the logger. Useful for + debugging. + + Returns the handler after adding it. + """ + # This method needs to be in this __init__.py to get the __name__ correct + # even if urllib3 is vendored within another package. + logger = logging.getLogger(__name__) + handler = logging.StreamHandler() + handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s")) + logger.addHandler(handler) + logger.setLevel(level) + logger.debug("Added a stderr logging handler to logger: %s", __name__) + return handler + + +# ... Clean up. +del NullHandler + + +# All warning filters *must* be appended unless you're really certain that they +# shouldn't be: otherwise, it's very hard for users to use most Python +# mechanisms to silence them. +# SecurityWarning's always go off by default. +warnings.simplefilter("always", exceptions.SecurityWarning, append=True) +# InsecurePlatformWarning's don't vary between requests, so we keep it default. +warnings.simplefilter("default", exceptions.InsecurePlatformWarning, append=True) + + +def disable_warnings(category: type[Warning] = exceptions.HTTPWarning) -> None: + """ + Helper for quickly disabling all urllib3 warnings. + """ + warnings.simplefilter("ignore", category) + + +_DEFAULT_POOL = PoolManager() + + +def request( + method: str, + url: str, + *, + body: _TYPE_BODY | None = None, + fields: _TYPE_FIELDS | None = None, + headers: typing.Mapping[str, str] | None = None, + preload_content: bool | None = True, + decode_content: bool | None = True, + redirect: bool | None = True, + retries: Retry | bool | int | None = None, + timeout: Timeout | float | int | None = 3, + json: typing.Any | None = None, +) -> BaseHTTPResponse: + """ + A convenience, top-level request method. It uses a module-global ``PoolManager`` instance. + Therefore, its side effects could be shared across dependencies relying on it. + To avoid side effects create a new ``PoolManager`` instance and use it instead. + The method does not accept low-level ``**urlopen_kw`` keyword arguments. + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param url: + The URL to perform the request on. + + :param body: + Data to send in the request body, either :class:`str`, :class:`bytes`, + an iterable of :class:`str`/:class:`bytes`, or a file-like object. + + :param fields: + Data to encode and send in the request body. + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. + + :param bool preload_content: + If True, the response's body will be preloaded into memory. + + :param bool decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param redirect: + If True, automatically handle redirects (status codes 301, 302, + 303, 307, 308). Each redirect counts as a retry. Disabling retries + will disable redirect, too. + + :param retries: + Configure the number of retries to allow before raising a + :class:`~urllib3.exceptions.MaxRetryError` exception. + + If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a + :class:`~urllib3.util.retry.Retry` object for fine-grained control + over different types of retries. + Pass an integer number to retry connection errors that many times, + but no other types of errors. Pass zero to never retry. + + If ``False``, then retries are disabled and any exception is raised + immediately. Also, instead of raising a MaxRetryError on redirects, + the redirect response will be returned. + + :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. + + :param timeout: + If specified, overrides the default timeout for this one + request. It may be a float (in seconds) or an instance of + :class:`urllib3.util.Timeout`. + + :param json: + Data to encode and send as JSON with UTF-encoded in the request body. + The ``"Content-Type"`` header will be set to ``"application/json"`` + unless specified otherwise. + """ + + return _DEFAULT_POOL.request( + method, + url, + body=body, + fields=fields, + headers=headers, + preload_content=preload_content, + decode_content=decode_content, + redirect=redirect, + retries=retries, + timeout=timeout, + json=json, + ) + + +if sys.platform == "emscripten": + from .contrib.emscripten import inject_into_urllib3 # noqa: 401 + + inject_into_urllib3() diff --git a/.venv/lib/python3.12/site-packages/urllib3/_base_connection.py b/.venv/lib/python3.12/site-packages/urllib3/_base_connection.py new file mode 100644 index 0000000000000000000000000000000000000000..dc0f318c0b380926eed0f4209d395c79963eaf9e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/urllib3/_base_connection.py @@ -0,0 +1,165 @@ +from __future__ import annotations + +import typing + +from .util.connection import _TYPE_SOCKET_OPTIONS +from .util.timeout import _DEFAULT_TIMEOUT, _TYPE_TIMEOUT +from .util.url import Url + +_TYPE_BODY = typing.Union[bytes, typing.IO[typing.Any], typing.Iterable[bytes], str] + + +class ProxyConfig(typing.NamedTuple): + ssl_context: ssl.SSLContext | None + use_forwarding_for_https: bool + assert_hostname: None | str | typing.Literal[False] + assert_fingerprint: str | None + + +class _ResponseOptions(typing.NamedTuple): + # TODO: Remove this in favor of a better + # HTTP request/response lifecycle tracking. + request_method: str + request_url: str + preload_content: bool + decode_content: bool + enforce_content_length: bool + + +if typing.TYPE_CHECKING: + import ssl + from typing import Protocol + + from .response import BaseHTTPResponse + + class BaseHTTPConnection(Protocol): + default_port: typing.ClassVar[int] + default_socket_options: typing.ClassVar[_TYPE_SOCKET_OPTIONS] + + host: str + port: int + timeout: None | ( + float + ) # Instance doesn't store _DEFAULT_TIMEOUT, must be resolved. + blocksize: int + source_address: tuple[str, int] | None + socket_options: _TYPE_SOCKET_OPTIONS | None + + proxy: Url | None + proxy_config: ProxyConfig | None + + is_verified: bool + proxy_is_verified: bool | None + + def __init__( + self, + host: str, + port: int | None = None, + *, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + source_address: tuple[str, int] | None = None, + blocksize: int = 8192, + socket_options: _TYPE_SOCKET_OPTIONS | None = ..., + proxy: Url | None = None, + proxy_config: ProxyConfig | None = None, + ) -> None: ... + + def set_tunnel( + self, + host: str, + port: int | None = None, + headers: typing.Mapping[str, str] | None = None, + scheme: str = "http", + ) -> None: ... + + def connect(self) -> None: ... + + def request( + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + # We know *at least* botocore is depending on the order of the + # first 3 parameters so to be safe we only mark the later ones + # as keyword-only to ensure we have space to extend. + *, + chunked: bool = False, + preload_content: bool = True, + decode_content: bool = True, + enforce_content_length: bool = True, + ) -> None: ... + + def getresponse(self) -> BaseHTTPResponse: ... + + def close(self) -> None: ... + + @property + def is_closed(self) -> bool: + """Whether the connection either is brand new or has been previously closed. + If this property is True then both ``is_connected`` and ``has_connected_to_proxy`` + properties must be False. + """ + + @property + def is_connected(self) -> bool: + """Whether the connection is actively connected to any origin (proxy or target)""" + + @property + def has_connected_to_proxy(self) -> bool: + """Whether the connection has successfully connected to its proxy. + This returns False if no proxy is in use. Used to determine whether + errors are coming from the proxy layer or from tunnelling to the target origin. + """ + + class BaseHTTPSConnection(BaseHTTPConnection, Protocol): + default_port: typing.ClassVar[int] + default_socket_options: typing.ClassVar[_TYPE_SOCKET_OPTIONS] + + # Certificate verification methods + cert_reqs: int | str | None + assert_hostname: None | str | typing.Literal[False] + assert_fingerprint: str | None + ssl_context: ssl.SSLContext | None + + # Trusted CAs + ca_certs: str | None + ca_cert_dir: str | None + ca_cert_data: None | str | bytes + + # TLS version + ssl_minimum_version: int | None + ssl_maximum_version: int | None + ssl_version: int | str | None # Deprecated + + # Client certificates + cert_file: str | None + key_file: str | None + key_password: str | None + + def __init__( + self, + host: str, + port: int | None = None, + *, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + source_address: tuple[str, int] | None = None, + blocksize: int = 16384, + socket_options: _TYPE_SOCKET_OPTIONS | None = ..., + proxy: Url | None = None, + proxy_config: ProxyConfig | None = None, + cert_reqs: int | str | None = None, + assert_hostname: None | str | typing.Literal[False] = None, + assert_fingerprint: str | None = None, + server_hostname: str | None = None, + ssl_context: ssl.SSLContext | None = None, + ca_certs: str | None = None, + ca_cert_dir: str | None = None, + ca_cert_data: None | str | bytes = None, + ssl_minimum_version: int | None = None, + ssl_maximum_version: int | None = None, + ssl_version: int | str | None = None, # Deprecated + cert_file: str | None = None, + key_file: str | None = None, + key_password: str | None = None, + ) -> None: ... diff --git a/.venv/lib/python3.12/site-packages/urllib3/_collections.py b/.venv/lib/python3.12/site-packages/urllib3/_collections.py new file mode 100644 index 0000000000000000000000000000000000000000..1b6c1364213b990c716c39d7cc6427cb319cb948 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/urllib3/_collections.py @@ -0,0 +1,479 @@ +from __future__ import annotations + +import typing +from collections import OrderedDict +from enum import Enum, auto +from threading import RLock + +if typing.TYPE_CHECKING: + # We can only import Protocol if TYPE_CHECKING because it's a development + # dependency, and is not available at runtime. + from typing import Protocol + + from typing_extensions import Self + + class HasGettableStringKeys(Protocol): + def keys(self) -> typing.Iterator[str]: ... + + def __getitem__(self, key: str) -> str: ... + + +__all__ = ["RecentlyUsedContainer", "HTTPHeaderDict"] + + +# Key type +_KT = typing.TypeVar("_KT") +# Value type +_VT = typing.TypeVar("_VT") +# Default type +_DT = typing.TypeVar("_DT") + +ValidHTTPHeaderSource = typing.Union[ + "HTTPHeaderDict", + typing.Mapping[str, str], + typing.Iterable[tuple[str, str]], + "HasGettableStringKeys", +] + + +class _Sentinel(Enum): + not_passed = auto() + + +def ensure_can_construct_http_header_dict( + potential: object, +) -> ValidHTTPHeaderSource | None: + if isinstance(potential, HTTPHeaderDict): + return potential + elif isinstance(potential, typing.Mapping): + # Full runtime checking of the contents of a Mapping is expensive, so for the + # purposes of typechecking, we assume that any Mapping is the right shape. + return typing.cast(typing.Mapping[str, str], potential) + elif isinstance(potential, typing.Iterable): + # Similarly to Mapping, full runtime checking of the contents of an Iterable is + # expensive, so for the purposes of typechecking, we assume that any Iterable + # is the right shape. + return typing.cast(typing.Iterable[tuple[str, str]], potential) + elif hasattr(potential, "keys") and hasattr(potential, "__getitem__"): + return typing.cast("HasGettableStringKeys", potential) + else: + return None + + +class RecentlyUsedContainer(typing.Generic[_KT, _VT], typing.MutableMapping[_KT, _VT]): + """ + Provides a thread-safe dict-like container which maintains up to + ``maxsize`` keys while throwing away the least-recently-used keys beyond + ``maxsize``. + + :param maxsize: + Maximum number of recent elements to retain. + + :param dispose_func: + Every time an item is evicted from the container, + ``dispose_func(value)`` is called. Callback which will get called + """ + + _container: typing.OrderedDict[_KT, _VT] + _maxsize: int + dispose_func: typing.Callable[[_VT], None] | None + lock: RLock + + def __init__( + self, + maxsize: int = 10, + dispose_func: typing.Callable[[_VT], None] | None = None, + ) -> None: + super().__init__() + self._maxsize = maxsize + self.dispose_func = dispose_func + self._container = OrderedDict() + self.lock = RLock() + + def __getitem__(self, key: _KT) -> _VT: + # Re-insert the item, moving it to the end of the eviction line. + with self.lock: + item = self._container.pop(key) + self._container[key] = item + return item + + def __setitem__(self, key: _KT, value: _VT) -> None: + evicted_item = None + with self.lock: + # Possibly evict the existing value of 'key' + try: + # If the key exists, we'll overwrite it, which won't change the + # size of the pool. Because accessing a key should move it to + # the end of the eviction line, we pop it out first. + evicted_item = key, self._container.pop(key) + self._container[key] = value + except KeyError: + # When the key does not exist, we insert the value first so that + # evicting works in all cases, including when self._maxsize is 0 + self._container[key] = value + if len(self._container) > self._maxsize: + # If we didn't evict an existing value, and we've hit our maximum + # size, then we have to evict the least recently used item from + # the beginning of the container. + evicted_item = self._container.popitem(last=False) + + # After releasing the lock on the pool, dispose of any evicted value. + if evicted_item is not None and self.dispose_func: + _, evicted_value = evicted_item + self.dispose_func(evicted_value) + + def __delitem__(self, key: _KT) -> None: + with self.lock: + value = self._container.pop(key) + + if self.dispose_func: + self.dispose_func(value) + + def __len__(self) -> int: + with self.lock: + return len(self._container) + + def __iter__(self) -> typing.NoReturn: + raise NotImplementedError( + "Iteration over this class is unlikely to be threadsafe." + ) + + def clear(self) -> None: + with self.lock: + # Copy pointers to all values, then wipe the mapping + values = list(self._container.values()) + self._container.clear() + + if self.dispose_func: + for value in values: + self.dispose_func(value) + + def keys(self) -> set[_KT]: # type: ignore[override] + with self.lock: + return set(self._container.keys()) + + +class HTTPHeaderDictItemView(set[tuple[str, str]]): + """ + HTTPHeaderDict is unusual for a Mapping[str, str] in that it has two modes of + address. + + If we directly try to get an item with a particular name, we will get a string + back that is the concatenated version of all the values: + + >>> d['X-Header-Name'] + 'Value1, Value2, Value3' + + However, if we iterate over an HTTPHeaderDict's items, we will optionally combine + these values based on whether combine=True was called when building up the dictionary + + >>> d = HTTPHeaderDict({"A": "1", "B": "foo"}) + >>> d.add("A", "2", combine=True) + >>> d.add("B", "bar") + >>> list(d.items()) + [ + ('A', '1, 2'), + ('B', 'foo'), + ('B', 'bar'), + ] + + This class conforms to the interface required by the MutableMapping ABC while + also giving us the nonstandard iteration behavior we want; items with duplicate + keys, ordered by time of first insertion. + """ + + _headers: HTTPHeaderDict + + def __init__(self, headers: HTTPHeaderDict) -> None: + self._headers = headers + + def __len__(self) -> int: + return len(list(self._headers.iteritems())) + + def __iter__(self) -> typing.Iterator[tuple[str, str]]: + return self._headers.iteritems() + + def __contains__(self, item: object) -> bool: + if isinstance(item, tuple) and len(item) == 2: + passed_key, passed_val = item + if isinstance(passed_key, str) and isinstance(passed_val, str): + return self._headers._has_value_for_header(passed_key, passed_val) + return False + + +class HTTPHeaderDict(typing.MutableMapping[str, str]): + """ + :param headers: + An iterable of field-value pairs. Must not contain multiple field names + when compared case-insensitively. + + :param kwargs: + Additional field-value pairs to pass in to ``dict.update``. + + A ``dict`` like container for storing HTTP Headers. + + Field names are stored and compared case-insensitively in compliance with + RFC 7230. Iteration provides the first case-sensitive key seen for each + case-insensitive pair. + + Using ``__setitem__`` syntax overwrites fields that compare equal + case-insensitively in order to maintain ``dict``'s api. For fields that + compare equal, instead create a new ``HTTPHeaderDict`` and use ``.add`` + in a loop. + + If multiple fields that are equal case-insensitively are passed to the + constructor or ``.update``, the behavior is undefined and some will be + lost. + + >>> headers = HTTPHeaderDict() + >>> headers.add('Set-Cookie', 'foo=bar') + >>> headers.add('set-cookie', 'baz=quxx') + >>> headers['content-length'] = '7' + >>> headers['SET-cookie'] + 'foo=bar, baz=quxx' + >>> headers['Content-Length'] + '7' + """ + + _container: typing.MutableMapping[str, list[str]] + + def __init__(self, headers: ValidHTTPHeaderSource | None = None, **kwargs: str): + super().__init__() + self._container = {} # 'dict' is insert-ordered + if headers is not None: + if isinstance(headers, HTTPHeaderDict): + self._copy_from(headers) + else: + self.extend(headers) + if kwargs: + self.extend(kwargs) + + def __setitem__(self, key: str, val: str) -> None: + # avoid a bytes/str comparison by decoding before httplib + if isinstance(key, bytes): + key = key.decode("latin-1") + self._container[key.lower()] = [key, val] + + def __getitem__(self, key: str) -> str: + val = self._container[key.lower()] + return ", ".join(val[1:]) + + def __delitem__(self, key: str) -> None: + del self._container[key.lower()] + + def __contains__(self, key: object) -> bool: + if isinstance(key, str): + return key.lower() in self._container + return False + + def setdefault(self, key: str, default: str = "") -> str: + return super().setdefault(key, default) + + def __eq__(self, other: object) -> bool: + maybe_constructable = ensure_can_construct_http_header_dict(other) + if maybe_constructable is None: + return False + else: + other_as_http_header_dict = type(self)(maybe_constructable) + + return {k.lower(): v for k, v in self.itermerged()} == { + k.lower(): v for k, v in other_as_http_header_dict.itermerged() + } + + def __ne__(self, other: object) -> bool: + return not self.__eq__(other) + + def __len__(self) -> int: + return len(self._container) + + def __iter__(self) -> typing.Iterator[str]: + # Only provide the originally cased names + for vals in self._container.values(): + yield vals[0] + + def discard(self, key: str) -> None: + try: + del self[key] + except KeyError: + pass + + def add(self, key: str, val: str, *, combine: bool = False) -> None: + """Adds a (name, value) pair, doesn't overwrite the value if it already + exists. + + If this is called with combine=True, instead of adding a new header value + as a distinct item during iteration, this will instead append the value to + any existing header value with a comma. If no existing header value exists + for the key, then the value will simply be added, ignoring the combine parameter. + + >>> headers = HTTPHeaderDict(foo='bar') + >>> headers.add('Foo', 'baz') + >>> headers['foo'] + 'bar, baz' + >>> list(headers.items()) + [('foo', 'bar'), ('foo', 'baz')] + >>> headers.add('foo', 'quz', combine=True) + >>> list(headers.items()) + [('foo', 'bar, baz, quz')] + """ + # avoid a bytes/str comparison by decoding before httplib + if isinstance(key, bytes): + key = key.decode("latin-1") + key_lower = key.lower() + new_vals = [key, val] + # Keep the common case aka no item present as fast as possible + vals = self._container.setdefault(key_lower, new_vals) + if new_vals is not vals: + # if there are values here, then there is at least the initial + # key/value pair + assert len(vals) >= 2 + if combine: + vals[-1] = vals[-1] + ", " + val + else: + vals.append(val) + + def extend(self, *args: ValidHTTPHeaderSource, **kwargs: str) -> None: + """Generic import function for any type of header-like object. + Adapted version of MutableMapping.update in order to insert items + with self.add instead of self.__setitem__ + """ + if len(args) > 1: + raise TypeError( + f"extend() takes at most 1 positional arguments ({len(args)} given)" + ) + other = args[0] if len(args) >= 1 else () + + if isinstance(other, HTTPHeaderDict): + for key, val in other.iteritems(): + self.add(key, val) + elif isinstance(other, typing.Mapping): + for key, val in other.items(): + self.add(key, val) + elif isinstance(other, typing.Iterable): + other = typing.cast(typing.Iterable[tuple[str, str]], other) + for key, value in other: + self.add(key, value) + elif hasattr(other, "keys") and hasattr(other, "__getitem__"): + # THIS IS NOT A TYPESAFE BRANCH + # In this branch, the object has a `keys` attr but is not a Mapping or any of + # the other types indicated in the method signature. We do some stuff with + # it as though it partially implements the Mapping interface, but we're not + # doing that stuff safely AT ALL. + for key in other.keys(): + self.add(key, other[key]) + + for key, value in kwargs.items(): + self.add(key, value) + + @typing.overload + def getlist(self, key: str) -> list[str]: ... + + @typing.overload + def getlist(self, key: str, default: _DT) -> list[str] | _DT: ... + + def getlist( + self, key: str, default: _Sentinel | _DT = _Sentinel.not_passed + ) -> list[str] | _DT: + """Returns a list of all the values for the named field. Returns an + empty list if the key doesn't exist.""" + try: + vals = self._container[key.lower()] + except KeyError: + if default is _Sentinel.not_passed: + # _DT is unbound; empty list is instance of List[str] + return [] + # _DT is bound; default is instance of _DT + return default + else: + # _DT may or may not be bound; vals[1:] is instance of List[str], which + # meets our external interface requirement of `Union[List[str], _DT]`. + return vals[1:] + + def _prepare_for_method_change(self) -> Self: + """ + Remove content-specific header fields before changing the request + method to GET or HEAD according to RFC 9110, Section 15.4. + """ + content_specific_headers = [ + "Content-Encoding", + "Content-Language", + "Content-Location", + "Content-Type", + "Content-Length", + "Digest", + "Last-Modified", + ] + for header in content_specific_headers: + self.discard(header) + return self + + # Backwards compatibility for httplib + getheaders = getlist + getallmatchingheaders = getlist + iget = getlist + + # Backwards compatibility for http.cookiejar + get_all = getlist + + def __repr__(self) -> str: + return f"{type(self).__name__}({dict(self.itermerged())})" + + def _copy_from(self, other: HTTPHeaderDict) -> None: + for key in other: + val = other.getlist(key) + self._container[key.lower()] = [key, *val] + + def copy(self) -> Self: + clone = type(self)() + clone._copy_from(self) + return clone + + def iteritems(self) -> typing.Iterator[tuple[str, str]]: + """Iterate over all header lines, including duplicate ones.""" + for key in self: + vals = self._container[key.lower()] + for val in vals[1:]: + yield vals[0], val + + def itermerged(self) -> typing.Iterator[tuple[str, str]]: + """Iterate over all headers, merging duplicate ones together.""" + for key in self: + val = self._container[key.lower()] + yield val[0], ", ".join(val[1:]) + + def items(self) -> HTTPHeaderDictItemView: # type: ignore[override] + return HTTPHeaderDictItemView(self) + + def _has_value_for_header(self, header_name: str, potential_value: str) -> bool: + if header_name in self: + return potential_value in self._container[header_name.lower()][1:] + return False + + def __ior__(self, other: object) -> HTTPHeaderDict: + # Supports extending a header dict in-place using operator |= + # combining items with add instead of __setitem__ + maybe_constructable = ensure_can_construct_http_header_dict(other) + if maybe_constructable is None: + return NotImplemented + self.extend(maybe_constructable) + return self + + def __or__(self, other: object) -> Self: + # Supports merging header dicts using operator | + # combining items with add instead of __setitem__ + maybe_constructable = ensure_can_construct_http_header_dict(other) + if maybe_constructable is None: + return NotImplemented + result = self.copy() + result.extend(maybe_constructable) + return result + + def __ror__(self, other: object) -> Self: + # Supports merging header dicts using operator | when other is on left side + # combining items with add instead of __setitem__ + maybe_constructable = ensure_can_construct_http_header_dict(other) + if maybe_constructable is None: + return NotImplemented + result = type(self)(maybe_constructable) + result.extend(self) + return result diff --git a/.venv/lib/python3.12/site-packages/urllib3/_request_methods.py b/.venv/lib/python3.12/site-packages/urllib3/_request_methods.py new file mode 100644 index 0000000000000000000000000000000000000000..297c271bf401c1cb48c6225f8822e78f58c3ca56 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/urllib3/_request_methods.py @@ -0,0 +1,278 @@ +from __future__ import annotations + +import json as _json +import typing +from urllib.parse import urlencode + +from ._base_connection import _TYPE_BODY +from ._collections import HTTPHeaderDict +from .filepost import _TYPE_FIELDS, encode_multipart_formdata +from .response import BaseHTTPResponse + +__all__ = ["RequestMethods"] + +_TYPE_ENCODE_URL_FIELDS = typing.Union[ + typing.Sequence[tuple[str, typing.Union[str, bytes]]], + typing.Mapping[str, typing.Union[str, bytes]], +] + + +class RequestMethods: + """ + Convenience mixin for classes who implement a :meth:`urlopen` method, such + as :class:`urllib3.HTTPConnectionPool` and + :class:`urllib3.PoolManager`. + + Provides behavior for making common types of HTTP request methods and + decides which type of request field encoding to use. + + Specifically, + + :meth:`.request_encode_url` is for sending requests whose fields are + encoded in the URL (such as GET, HEAD, DELETE). + + :meth:`.request_encode_body` is for sending requests whose fields are + encoded in the *body* of the request using multipart or www-form-urlencoded + (such as for POST, PUT, PATCH). + + :meth:`.request` is for making any kind of request, it will look up the + appropriate encoding format and use one of the above two methods to make + the request. + + Initializer parameters: + + :param headers: + Headers to include with all requests, unless other headers are given + explicitly. + """ + + _encode_url_methods = {"DELETE", "GET", "HEAD", "OPTIONS"} + + def __init__(self, headers: typing.Mapping[str, str] | None = None) -> None: + self.headers = headers or {} + + def urlopen( + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + encode_multipart: bool = True, + multipart_boundary: str | None = None, + **kw: typing.Any, + ) -> BaseHTTPResponse: # Abstract + raise NotImplementedError( + "Classes extending RequestMethods must implement " + "their own ``urlopen`` method." + ) + + def request( + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + fields: _TYPE_FIELDS | None = None, + headers: typing.Mapping[str, str] | None = None, + json: typing.Any | None = None, + **urlopen_kw: typing.Any, + ) -> BaseHTTPResponse: + """ + Make a request using :meth:`urlopen` with the appropriate encoding of + ``fields`` based on the ``method`` used. + + This is a convenience method that requires the least amount of manual + effort. It can be used in most situations, while still having the + option to drop down to more specific methods when necessary, such as + :meth:`request_encode_url`, :meth:`request_encode_body`, + or even the lowest level :meth:`urlopen`. + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param url: + The URL to perform the request on. + + :param body: + Data to send in the request body, either :class:`str`, :class:`bytes`, + an iterable of :class:`str`/:class:`bytes`, or a file-like object. + + :param fields: + Data to encode and send in the URL or request body, depending on ``method``. + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. If None, pool headers are used. If provided, + these headers completely replace any pool-specific headers. + + :param json: + Data to encode and send as JSON with UTF-encoded in the request body. + The ``"Content-Type"`` header will be set to ``"application/json"`` + unless specified otherwise. + """ + method = method.upper() + + if json is not None and body is not None: + raise TypeError( + "request got values for both 'body' and 'json' parameters which are mutually exclusive" + ) + + if json is not None: + if headers is None: + headers = self.headers + + if not ("content-type" in map(str.lower, headers.keys())): + headers = HTTPHeaderDict(headers) + headers["Content-Type"] = "application/json" + + body = _json.dumps(json, separators=(",", ":"), ensure_ascii=False).encode( + "utf-8" + ) + + if body is not None: + urlopen_kw["body"] = body + + if method in self._encode_url_methods: + return self.request_encode_url( + method, + url, + fields=fields, # type: ignore[arg-type] + headers=headers, + **urlopen_kw, + ) + else: + return self.request_encode_body( + method, url, fields=fields, headers=headers, **urlopen_kw + ) + + def request_encode_url( + self, + method: str, + url: str, + fields: _TYPE_ENCODE_URL_FIELDS | None = None, + headers: typing.Mapping[str, str] | None = None, + **urlopen_kw: str, + ) -> BaseHTTPResponse: + """ + Make a request using :meth:`urlopen` with the ``fields`` encoded in + the url. This is useful for request methods like GET, HEAD, DELETE, etc. + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param url: + The URL to perform the request on. + + :param fields: + Data to encode and send in the URL. + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. If None, pool headers are used. If provided, + these headers completely replace any pool-specific headers. + """ + if headers is None: + headers = self.headers + + extra_kw: dict[str, typing.Any] = {"headers": headers} + extra_kw.update(urlopen_kw) + + if fields: + url += "?" + urlencode(fields) + + return self.urlopen(method, url, **extra_kw) + + def request_encode_body( + self, + method: str, + url: str, + fields: _TYPE_FIELDS | None = None, + headers: typing.Mapping[str, str] | None = None, + encode_multipart: bool = True, + multipart_boundary: str | None = None, + **urlopen_kw: str, + ) -> BaseHTTPResponse: + """ + Make a request using :meth:`urlopen` with the ``fields`` encoded in + the body. This is useful for request methods like POST, PUT, PATCH, etc. + + When ``encode_multipart=True`` (default), then + :func:`urllib3.encode_multipart_formdata` is used to encode + the payload with the appropriate content type. Otherwise + :func:`urllib.parse.urlencode` is used with the + 'application/x-www-form-urlencoded' content type. + + Multipart encoding must be used when posting files, and it's reasonably + safe to use it in other times too. However, it may break request + signing, such as with OAuth. + + Supports an optional ``fields`` parameter of key/value strings AND + key/filetuple. A filetuple is a (filename, data, MIME type) tuple where + the MIME type is optional. For example:: + + fields = { + 'foo': 'bar', + 'fakefile': ('foofile.txt', 'contents of foofile'), + 'realfile': ('barfile.txt', open('realfile').read()), + 'typedfile': ('bazfile.bin', open('bazfile').read(), + 'image/jpeg'), + 'nonamefile': 'contents of nonamefile field', + } + + When uploading a file, providing a filename (the first parameter of the + tuple) is optional but recommended to best mimic behavior of browsers. + + Note that if ``headers`` are supplied, the 'Content-Type' header will + be overwritten because it depends on the dynamic random boundary string + which is used to compose the body of the request. The random boundary + string can be explicitly set with the ``multipart_boundary`` parameter. + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param url: + The URL to perform the request on. + + :param fields: + Data to encode and send in the request body. + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. If None, pool headers are used. If provided, + these headers completely replace any pool-specific headers. + + :param encode_multipart: + If True, encode the ``fields`` using the multipart/form-data MIME + format. + + :param multipart_boundary: + If not specified, then a random boundary will be generated using + :func:`urllib3.filepost.choose_boundary`. + """ + if headers is None: + headers = self.headers + + extra_kw: dict[str, typing.Any] = {"headers": HTTPHeaderDict(headers)} + body: bytes | str + + if fields: + if "body" in urlopen_kw: + raise TypeError( + "request got values for both 'fields' and 'body', can only specify one." + ) + + if encode_multipart: + body, content_type = encode_multipart_formdata( + fields, boundary=multipart_boundary + ) + else: + body, content_type = ( + urlencode(fields), # type: ignore[arg-type] + "application/x-www-form-urlencoded", + ) + + extra_kw["body"] = body + extra_kw["headers"].setdefault("Content-Type", content_type) + + extra_kw.update(urlopen_kw) + + return self.urlopen(method, url, **extra_kw) diff --git a/.venv/lib/python3.12/site-packages/urllib3/_version.py b/.venv/lib/python3.12/site-packages/urllib3/_version.py new file mode 100644 index 0000000000000000000000000000000000000000..49707ce59783e1f6c31fc722c949232506af7dda --- /dev/null +++ b/.venv/lib/python3.12/site-packages/urllib3/_version.py @@ -0,0 +1,21 @@ +# file generated by setuptools-scm +# don't change, don't track in version control + +__all__ = ["__version__", "__version_tuple__", "version", "version_tuple"] + +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import Tuple + from typing import 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 = '2.5.0' +__version_tuple__ = version_tuple = (2, 5, 0) diff --git a/.venv/lib/python3.12/site-packages/urllib3/connection.py b/.venv/lib/python3.12/site-packages/urllib3/connection.py new file mode 100644 index 0000000000000000000000000000000000000000..8082387d94b6559ca2a24126ac1511285389dc6f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/urllib3/connection.py @@ -0,0 +1,1093 @@ +from __future__ import annotations + +import datetime +import http.client +import logging +import os +import re +import socket +import sys +import threading +import typing +import warnings +from http.client import HTTPConnection as _HTTPConnection +from http.client import HTTPException as HTTPException # noqa: F401 +from http.client import ResponseNotReady +from socket import timeout as SocketTimeout + +if typing.TYPE_CHECKING: + from .response import HTTPResponse + from .util.ssl_ import _TYPE_PEER_CERT_RET_DICT + from .util.ssltransport import SSLTransport + +from ._collections import HTTPHeaderDict +from .http2 import probe as http2_probe +from .util.response import assert_header_parsing +from .util.timeout import _DEFAULT_TIMEOUT, _TYPE_TIMEOUT, Timeout +from .util.util import to_str +from .util.wait import wait_for_read + +try: # Compiled with SSL? + import ssl + + BaseSSLError = ssl.SSLError +except (ImportError, AttributeError): + ssl = None # type: ignore[assignment] + + class BaseSSLError(BaseException): # type: ignore[no-redef] + pass + + +from ._base_connection import _TYPE_BODY +from ._base_connection import ProxyConfig as ProxyConfig +from ._base_connection import _ResponseOptions as _ResponseOptions +from ._version import __version__ +from .exceptions import ( + ConnectTimeoutError, + HeaderParsingError, + NameResolutionError, + NewConnectionError, + ProxyError, + SystemTimeWarning, +) +from .util import SKIP_HEADER, SKIPPABLE_HEADERS, connection, ssl_ +from .util.request import body_to_chunks +from .util.ssl_ import assert_fingerprint as _assert_fingerprint +from .util.ssl_ import ( + create_urllib3_context, + is_ipaddress, + resolve_cert_reqs, + resolve_ssl_version, + ssl_wrap_socket, +) +from .util.ssl_match_hostname import CertificateError, match_hostname +from .util.url import Url + +# Not a no-op, we're adding this to the namespace so it can be imported. +ConnectionError = ConnectionError +BrokenPipeError = BrokenPipeError + + +log = logging.getLogger(__name__) + +port_by_scheme = {"http": 80, "https": 443} + +# When it comes time to update this value as a part of regular maintenance +# (ie test_recent_date is failing) update it to ~6 months before the current date. +RECENT_DATE = datetime.date(2025, 1, 1) + +_CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]") + + +class HTTPConnection(_HTTPConnection): + """ + Based on :class:`http.client.HTTPConnection` but provides an extra constructor + backwards-compatibility layer between older and newer Pythons. + + Additional keyword parameters are used to configure attributes of the connection. + Accepted parameters include: + + - ``source_address``: Set the source address for the current connection. + - ``socket_options``: Set specific options on the underlying socket. If not specified, then + defaults are loaded from ``HTTPConnection.default_socket_options`` which includes disabling + Nagle's algorithm (sets TCP_NODELAY to 1) unless the connection is behind a proxy. + + For example, if you wish to enable TCP Keep Alive in addition to the defaults, + you might pass: + + .. code-block:: python + + HTTPConnection.default_socket_options + [ + (socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1), + ] + + Or you may want to disable the defaults by passing an empty list (e.g., ``[]``). + """ + + default_port: typing.ClassVar[int] = port_by_scheme["http"] # type: ignore[misc] + + #: Disable Nagle's algorithm by default. + #: ``[(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)]`` + default_socket_options: typing.ClassVar[connection._TYPE_SOCKET_OPTIONS] = [ + (socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + ] + + #: Whether this connection verifies the host's certificate. + is_verified: bool = False + + #: Whether this proxy connection verified the proxy host's certificate. + # If no proxy is currently connected to the value will be ``None``. + proxy_is_verified: bool | None = None + + blocksize: int + source_address: tuple[str, int] | None + socket_options: connection._TYPE_SOCKET_OPTIONS | None + + _has_connected_to_proxy: bool + _response_options: _ResponseOptions | None + _tunnel_host: str | None + _tunnel_port: int | None + _tunnel_scheme: str | None + + def __init__( + self, + host: str, + port: int | None = None, + *, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + source_address: tuple[str, int] | None = None, + blocksize: int = 16384, + socket_options: None | ( + connection._TYPE_SOCKET_OPTIONS + ) = default_socket_options, + proxy: Url | None = None, + proxy_config: ProxyConfig | None = None, + ) -> None: + super().__init__( + host=host, + port=port, + timeout=Timeout.resolve_default_timeout(timeout), + source_address=source_address, + blocksize=blocksize, + ) + self.socket_options = socket_options + self.proxy = proxy + self.proxy_config = proxy_config + + self._has_connected_to_proxy = False + self._response_options = None + self._tunnel_host: str | None = None + self._tunnel_port: int | None = None + self._tunnel_scheme: str | None = None + + @property + def host(self) -> str: + """ + Getter method to remove any trailing dots that indicate the hostname is an FQDN. + + In general, SSL certificates don't include the trailing dot indicating a + fully-qualified domain name, and thus, they don't validate properly when + checked against a domain name that includes the dot. In addition, some + servers may not expect to receive the trailing dot when provided. + + However, the hostname with trailing dot is critical to DNS resolution; doing a + lookup with the trailing dot will properly only resolve the appropriate FQDN, + whereas a lookup without a trailing dot will search the system's search domain + list. Thus, it's important to keep the original host around for use only in + those cases where it's appropriate (i.e., when doing DNS lookup to establish the + actual TCP connection across which we're going to send HTTP requests). + """ + return self._dns_host.rstrip(".") + + @host.setter + def host(self, value: str) -> None: + """ + Setter for the `host` property. + + We assume that only urllib3 uses the _dns_host attribute; httplib itself + only uses `host`, and it seems reasonable that other libraries follow suit. + """ + self._dns_host = value + + def _new_conn(self) -> socket.socket: + """Establish a socket connection and set nodelay settings on it. + + :return: New socket connection. + """ + try: + sock = connection.create_connection( + (self._dns_host, self.port), + self.timeout, + source_address=self.source_address, + socket_options=self.socket_options, + ) + except socket.gaierror as e: + raise NameResolutionError(self.host, self, e) from e + except SocketTimeout as e: + raise ConnectTimeoutError( + self, + f"Connection to {self.host} timed out. (connect timeout={self.timeout})", + ) from e + + except OSError as e: + raise NewConnectionError( + self, f"Failed to establish a new connection: {e}" + ) from e + + sys.audit("http.client.connect", self, self.host, self.port) + + return sock + + def set_tunnel( + self, + host: str, + port: int | None = None, + headers: typing.Mapping[str, str] | None = None, + scheme: str = "http", + ) -> None: + if scheme not in ("http", "https"): + raise ValueError( + f"Invalid proxy scheme for tunneling: {scheme!r}, must be either 'http' or 'https'" + ) + super().set_tunnel(host, port=port, headers=headers) + self._tunnel_scheme = scheme + + if sys.version_info < (3, 11, 9) or ((3, 12) <= sys.version_info < (3, 12, 3)): + # Taken from python/cpython#100986 which was backported in 3.11.9 and 3.12.3. + # When using connection_from_host, host will come without brackets. + def _wrap_ipv6(self, ip: bytes) -> bytes: + if b":" in ip and ip[0] != b"["[0]: + return b"[" + ip + b"]" + return ip + + if sys.version_info < (3, 11, 9): + # `_tunnel` copied from 3.11.13 backporting + # https://github.com/python/cpython/commit/0d4026432591d43185568dd31cef6a034c4b9261 + # and https://github.com/python/cpython/commit/6fbc61070fda2ffb8889e77e3b24bca4249ab4d1 + def _tunnel(self) -> None: + _MAXLINE = http.client._MAXLINE # type: ignore[attr-defined] + connect = b"CONNECT %s:%d HTTP/1.0\r\n" % ( # type: ignore[str-format] + self._wrap_ipv6(self._tunnel_host.encode("ascii")), # type: ignore[union-attr] + self._tunnel_port, + ) + headers = [connect] + for header, value in self._tunnel_headers.items(): # type: ignore[attr-defined] + headers.append(f"{header}: {value}\r\n".encode("latin-1")) + headers.append(b"\r\n") + # Making a single send() call instead of one per line encourages + # the host OS to use a more optimal packet size instead of + # potentially emitting a series of small packets. + self.send(b"".join(headers)) + del headers + + response = self.response_class(self.sock, method=self._method) # type: ignore[attr-defined] + try: + (version, code, message) = response._read_status() # type: ignore[attr-defined] + + if code != http.HTTPStatus.OK: + self.close() + raise OSError( + f"Tunnel connection failed: {code} {message.strip()}" + ) + while True: + line = response.fp.readline(_MAXLINE + 1) + if len(line) > _MAXLINE: + raise http.client.LineTooLong("header line") + if not line: + # for sites which EOF without sending a trailer + break + if line in (b"\r\n", b"\n", b""): + break + + if self.debuglevel > 0: + print("header:", line.decode()) + finally: + response.close() + + elif (3, 12) <= sys.version_info < (3, 12, 3): + # `_tunnel` copied from 3.12.11 backporting + # https://github.com/python/cpython/commit/23aef575c7629abcd4aaf028ebd226fb41a4b3c8 + def _tunnel(self) -> None: # noqa: F811 + connect = b"CONNECT %s:%d HTTP/1.1\r\n" % ( # type: ignore[str-format] + self._wrap_ipv6(self._tunnel_host.encode("idna")), # type: ignore[union-attr] + self._tunnel_port, + ) + headers = [connect] + for header, value in self._tunnel_headers.items(): # type: ignore[attr-defined] + headers.append(f"{header}: {value}\r\n".encode("latin-1")) + headers.append(b"\r\n") + # Making a single send() call instead of one per line encourages + # the host OS to use a more optimal packet size instead of + # potentially emitting a series of small packets. + self.send(b"".join(headers)) + del headers + + response = self.response_class(self.sock, method=self._method) # type: ignore[attr-defined] + try: + (version, code, message) = response._read_status() # type: ignore[attr-defined] + + self._raw_proxy_headers = http.client._read_headers(response.fp) # type: ignore[attr-defined] + + if self.debuglevel > 0: + for header in self._raw_proxy_headers: + print("header:", header.decode()) + + if code != http.HTTPStatus.OK: + self.close() + raise OSError( + f"Tunnel connection failed: {code} {message.strip()}" + ) + + finally: + response.close() + + def connect(self) -> None: + self.sock = self._new_conn() + if self._tunnel_host: + # If we're tunneling it means we're connected to our proxy. + self._has_connected_to_proxy = True + + # TODO: Fix tunnel so it doesn't depend on self.sock state. + self._tunnel() + + # If there's a proxy to be connected to we are fully connected. + # This is set twice (once above and here) due to forwarding proxies + # not using tunnelling. + self._has_connected_to_proxy = bool(self.proxy) + + if self._has_connected_to_proxy: + self.proxy_is_verified = False + + @property + def is_closed(self) -> bool: + return self.sock is None + + @property + def is_connected(self) -> bool: + if self.sock is None: + return False + return not wait_for_read(self.sock, timeout=0.0) + + @property + def has_connected_to_proxy(self) -> bool: + return self._has_connected_to_proxy + + @property + def proxy_is_forwarding(self) -> bool: + """ + Return True if a forwarding proxy is configured, else return False + """ + return bool(self.proxy) and self._tunnel_host is None + + @property + def proxy_is_tunneling(self) -> bool: + """ + Return True if a tunneling proxy is configured, else return False + """ + return self._tunnel_host is not None + + def close(self) -> None: + try: + super().close() + finally: + # Reset all stateful properties so connection + # can be re-used without leaking prior configs. + self.sock = None + self.is_verified = False + self.proxy_is_verified = None + self._has_connected_to_proxy = False + self._response_options = None + self._tunnel_host = None + self._tunnel_port = None + self._tunnel_scheme = None + + def putrequest( + self, + method: str, + url: str, + skip_host: bool = False, + skip_accept_encoding: bool = False, + ) -> None: + """""" + # Empty docstring because the indentation of CPython's implementation + # is broken but we don't want this method in our documentation. + match = _CONTAINS_CONTROL_CHAR_RE.search(method) + if match: + raise ValueError( + f"Method cannot contain non-token characters {method!r} (found at least {match.group()!r})" + ) + + return super().putrequest( + method, url, skip_host=skip_host, skip_accept_encoding=skip_accept_encoding + ) + + def putheader(self, header: str, *values: str) -> None: # type: ignore[override] + """""" + if not any(isinstance(v, str) and v == SKIP_HEADER for v in values): + super().putheader(header, *values) + elif to_str(header.lower()) not in SKIPPABLE_HEADERS: + skippable_headers = "', '".join( + [str.title(header) for header in sorted(SKIPPABLE_HEADERS)] + ) + raise ValueError( + f"urllib3.util.SKIP_HEADER only supports '{skippable_headers}'" + ) + + # `request` method's signature intentionally violates LSP. + # urllib3's API is different from `http.client.HTTPConnection` and the subclassing is only incidental. + def request( # type: ignore[override] + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + *, + chunked: bool = False, + preload_content: bool = True, + decode_content: bool = True, + enforce_content_length: bool = True, + ) -> None: + # Update the inner socket's timeout value to send the request. + # This only triggers if the connection is re-used. + if self.sock is not None: + self.sock.settimeout(self.timeout) + + # Store these values to be fed into the HTTPResponse + # object later. TODO: Remove this in favor of a real + # HTTP lifecycle mechanism. + + # We have to store these before we call .request() + # because sometimes we can still salvage a response + # off the wire even if we aren't able to completely + # send the request body. + self._response_options = _ResponseOptions( + request_method=method, + request_url=url, + preload_content=preload_content, + decode_content=decode_content, + enforce_content_length=enforce_content_length, + ) + + if headers is None: + headers = {} + header_keys = frozenset(to_str(k.lower()) for k in headers) + skip_accept_encoding = "accept-encoding" in header_keys + skip_host = "host" in header_keys + self.putrequest( + method, url, skip_accept_encoding=skip_accept_encoding, skip_host=skip_host + ) + + # Transform the body into an iterable of sendall()-able chunks + # and detect if an explicit Content-Length is doable. + chunks_and_cl = body_to_chunks(body, method=method, blocksize=self.blocksize) + chunks = chunks_and_cl.chunks + content_length = chunks_and_cl.content_length + + # When chunked is explicit set to 'True' we respect that. + if chunked: + if "transfer-encoding" not in header_keys: + self.putheader("Transfer-Encoding", "chunked") + else: + # Detect whether a framing mechanism is already in use. If so + # we respect that value, otherwise we pick chunked vs content-length + # depending on the type of 'body'. + if "content-length" in header_keys: + chunked = False + elif "transfer-encoding" in header_keys: + chunked = True + + # Otherwise we go off the recommendation of 'body_to_chunks()'. + else: + chunked = False + if content_length is None: + if chunks is not None: + chunked = True + self.putheader("Transfer-Encoding", "chunked") + else: + self.putheader("Content-Length", str(content_length)) + + # Now that framing headers are out of the way we send all the other headers. + if "user-agent" not in header_keys: + self.putheader("User-Agent", _get_default_user_agent()) + for header, value in headers.items(): + self.putheader(header, value) + self.endheaders() + + # If we're given a body we start sending that in chunks. + if chunks is not None: + for chunk in chunks: + # Sending empty chunks isn't allowed for TE: chunked + # as it indicates the end of the body. + if not chunk: + continue + if isinstance(chunk, str): + chunk = chunk.encode("utf-8") + if chunked: + self.send(b"%x\r\n%b\r\n" % (len(chunk), chunk)) + else: + self.send(chunk) + + # Regardless of whether we have a body or not, if we're in + # chunked mode we want to send an explicit empty chunk. + if chunked: + self.send(b"0\r\n\r\n") + + def request_chunked( + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + ) -> None: + """ + Alternative to the common request method, which sends the + body with chunked encoding and not as one block + """ + warnings.warn( + "HTTPConnection.request_chunked() is deprecated and will be removed " + "in urllib3 v2.1.0. Instead use HTTPConnection.request(..., chunked=True).", + category=DeprecationWarning, + stacklevel=2, + ) + self.request(method, url, body=body, headers=headers, chunked=True) + + def getresponse( # type: ignore[override] + self, + ) -> HTTPResponse: + """ + Get the response from the server. + + If the HTTPConnection is in the correct state, returns an instance of HTTPResponse or of whatever object is returned by the response_class variable. + + If a request has not been sent or if a previous response has not be handled, ResponseNotReady is raised. If the HTTP response indicates that the connection should be closed, then it will be closed before the response is returned. When the connection is closed, the underlying socket is closed. + """ + # Raise the same error as http.client.HTTPConnection + if self._response_options is None: + raise ResponseNotReady() + + # Reset this attribute for being used again. + resp_options = self._response_options + self._response_options = None + + # Since the connection's timeout value may have been updated + # we need to set the timeout on the socket. + self.sock.settimeout(self.timeout) + + # This is needed here to avoid circular import errors + from .response import HTTPResponse + + # Save a reference to the shutdown function before ownership is passed + # to httplib_response + # TODO should we implement it everywhere? + _shutdown = getattr(self.sock, "shutdown", None) + + # Get the response from http.client.HTTPConnection + httplib_response = super().getresponse() + + try: + assert_header_parsing(httplib_response.msg) + except (HeaderParsingError, TypeError) as hpe: + log.warning( + "Failed to parse headers (url=%s): %s", + _url_from_connection(self, resp_options.request_url), + hpe, + exc_info=True, + ) + + headers = HTTPHeaderDict(httplib_response.msg.items()) + + response = HTTPResponse( + body=httplib_response, + headers=headers, + status=httplib_response.status, + version=httplib_response.version, + version_string=getattr(self, "_http_vsn_str", "HTTP/?"), + reason=httplib_response.reason, + preload_content=resp_options.preload_content, + decode_content=resp_options.decode_content, + original_response=httplib_response, + enforce_content_length=resp_options.enforce_content_length, + request_method=resp_options.request_method, + request_url=resp_options.request_url, + sock_shutdown=_shutdown, + ) + return response + + +class HTTPSConnection(HTTPConnection): + """ + Many of the parameters to this constructor are passed to the underlying SSL + socket by means of :py:func:`urllib3.util.ssl_wrap_socket`. + """ + + default_port = port_by_scheme["https"] # type: ignore[misc] + + cert_reqs: int | str | None = None + ca_certs: str | None = None + ca_cert_dir: str | None = None + ca_cert_data: None | str | bytes = None + ssl_version: int | str | None = None + ssl_minimum_version: int | None = None + ssl_maximum_version: int | None = None + assert_fingerprint: str | None = None + _connect_callback: typing.Callable[..., None] | None = None + + def __init__( + self, + host: str, + port: int | None = None, + *, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + source_address: tuple[str, int] | None = None, + blocksize: int = 16384, + socket_options: None | ( + connection._TYPE_SOCKET_OPTIONS + ) = HTTPConnection.default_socket_options, + proxy: Url | None = None, + proxy_config: ProxyConfig | None = None, + cert_reqs: int | str | None = None, + assert_hostname: None | str | typing.Literal[False] = None, + assert_fingerprint: str | None = None, + server_hostname: str | None = None, + ssl_context: ssl.SSLContext | None = None, + ca_certs: str | None = None, + ca_cert_dir: str | None = None, + ca_cert_data: None | str | bytes = None, + ssl_minimum_version: int | None = None, + ssl_maximum_version: int | None = None, + ssl_version: int | str | None = None, # Deprecated + cert_file: str | None = None, + key_file: str | None = None, + key_password: str | None = None, + ) -> None: + super().__init__( + host, + port=port, + timeout=timeout, + source_address=source_address, + blocksize=blocksize, + socket_options=socket_options, + proxy=proxy, + proxy_config=proxy_config, + ) + + self.key_file = key_file + self.cert_file = cert_file + self.key_password = key_password + self.ssl_context = ssl_context + self.server_hostname = server_hostname + self.assert_hostname = assert_hostname + self.assert_fingerprint = assert_fingerprint + self.ssl_version = ssl_version + self.ssl_minimum_version = ssl_minimum_version + self.ssl_maximum_version = ssl_maximum_version + self.ca_certs = ca_certs and os.path.expanduser(ca_certs) + self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir) + self.ca_cert_data = ca_cert_data + + # cert_reqs depends on ssl_context so calculate last. + if cert_reqs is None: + if self.ssl_context is not None: + cert_reqs = self.ssl_context.verify_mode + else: + cert_reqs = resolve_cert_reqs(None) + self.cert_reqs = cert_reqs + self._connect_callback = None + + def set_cert( + self, + key_file: str | None = None, + cert_file: str | None = None, + cert_reqs: int | str | None = None, + key_password: str | None = None, + ca_certs: str | None = None, + assert_hostname: None | str | typing.Literal[False] = None, + assert_fingerprint: str | None = None, + ca_cert_dir: str | None = None, + ca_cert_data: None | str | bytes = None, + ) -> None: + """ + This method should only be called once, before the connection is used. + """ + warnings.warn( + "HTTPSConnection.set_cert() is deprecated and will be removed " + "in urllib3 v2.1.0. Instead provide the parameters to the " + "HTTPSConnection constructor.", + category=DeprecationWarning, + stacklevel=2, + ) + + # If cert_reqs is not provided we'll assume CERT_REQUIRED unless we also + # have an SSLContext object in which case we'll use its verify_mode. + if cert_reqs is None: + if self.ssl_context is not None: + cert_reqs = self.ssl_context.verify_mode + else: + cert_reqs = resolve_cert_reqs(None) + + self.key_file = key_file + self.cert_file = cert_file + self.cert_reqs = cert_reqs + self.key_password = key_password + self.assert_hostname = assert_hostname + self.assert_fingerprint = assert_fingerprint + self.ca_certs = ca_certs and os.path.expanduser(ca_certs) + self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir) + self.ca_cert_data = ca_cert_data + + def connect(self) -> None: + # Today we don't need to be doing this step before the /actual/ socket + # connection, however in the future we'll need to decide whether to + # create a new socket or re-use an existing "shared" socket as a part + # of the HTTP/2 handshake dance. + if self._tunnel_host is not None and self._tunnel_port is not None: + probe_http2_host = self._tunnel_host + probe_http2_port = self._tunnel_port + else: + probe_http2_host = self.host + probe_http2_port = self.port + + # Check if the target origin supports HTTP/2. + # If the value comes back as 'None' it means that the current thread + # is probing for HTTP/2 support. Otherwise, we're waiting for another + # probe to complete, or we get a value right away. + target_supports_http2: bool | None + if "h2" in ssl_.ALPN_PROTOCOLS: + target_supports_http2 = http2_probe.acquire_and_get( + host=probe_http2_host, port=probe_http2_port + ) + else: + # If HTTP/2 isn't going to be offered it doesn't matter if + # the target supports HTTP/2. Don't want to make a probe. + target_supports_http2 = False + + if self._connect_callback is not None: + self._connect_callback( + "before connect", + thread_id=threading.get_ident(), + target_supports_http2=target_supports_http2, + ) + + try: + sock: socket.socket | ssl.SSLSocket + self.sock = sock = self._new_conn() + server_hostname: str = self.host + tls_in_tls = False + + # Do we need to establish a tunnel? + if self.proxy_is_tunneling: + # We're tunneling to an HTTPS origin so need to do TLS-in-TLS. + if self._tunnel_scheme == "https": + # _connect_tls_proxy will verify and assign proxy_is_verified + self.sock = sock = self._connect_tls_proxy(self.host, sock) + tls_in_tls = True + elif self._tunnel_scheme == "http": + self.proxy_is_verified = False + + # If we're tunneling it means we're connected to our proxy. + self._has_connected_to_proxy = True + + self._tunnel() + # Override the host with the one we're requesting data from. + server_hostname = typing.cast(str, self._tunnel_host) + + if self.server_hostname is not None: + server_hostname = self.server_hostname + + is_time_off = datetime.date.today() < RECENT_DATE + if is_time_off: + warnings.warn( + ( + f"System time is way off (before {RECENT_DATE}). This will probably " + "lead to SSL verification errors" + ), + SystemTimeWarning, + ) + + # Remove trailing '.' from fqdn hostnames to allow certificate validation + server_hostname_rm_dot = server_hostname.rstrip(".") + + sock_and_verified = _ssl_wrap_socket_and_match_hostname( + sock=sock, + cert_reqs=self.cert_reqs, + ssl_version=self.ssl_version, + ssl_minimum_version=self.ssl_minimum_version, + ssl_maximum_version=self.ssl_maximum_version, + ca_certs=self.ca_certs, + ca_cert_dir=self.ca_cert_dir, + ca_cert_data=self.ca_cert_data, + cert_file=self.cert_file, + key_file=self.key_file, + key_password=self.key_password, + server_hostname=server_hostname_rm_dot, + ssl_context=self.ssl_context, + tls_in_tls=tls_in_tls, + assert_hostname=self.assert_hostname, + assert_fingerprint=self.assert_fingerprint, + ) + self.sock = sock_and_verified.socket + + # If an error occurs during connection/handshake we may need to release + # our lock so another connection can probe the origin. + except BaseException: + if self._connect_callback is not None: + self._connect_callback( + "after connect failure", + thread_id=threading.get_ident(), + target_supports_http2=target_supports_http2, + ) + + if target_supports_http2 is None: + http2_probe.set_and_release( + host=probe_http2_host, port=probe_http2_port, supports_http2=None + ) + raise + + # If this connection doesn't know if the origin supports HTTP/2 + # we report back to the HTTP/2 probe our result. + if target_supports_http2 is None: + supports_http2 = sock_and_verified.socket.selected_alpn_protocol() == "h2" + http2_probe.set_and_release( + host=probe_http2_host, + port=probe_http2_port, + supports_http2=supports_http2, + ) + + # Forwarding proxies can never have a verified target since + # the proxy is the one doing the verification. Should instead + # use a CONNECT tunnel in order to verify the target. + # See: https://github.com/urllib3/urllib3/issues/3267. + if self.proxy_is_forwarding: + self.is_verified = False + else: + self.is_verified = sock_and_verified.is_verified + + # If there's a proxy to be connected to we are fully connected. + # This is set twice (once above and here) due to forwarding proxies + # not using tunnelling. + self._has_connected_to_proxy = bool(self.proxy) + + # Set `self.proxy_is_verified` unless it's already set while + # establishing a tunnel. + if self._has_connected_to_proxy and self.proxy_is_verified is None: + self.proxy_is_verified = sock_and_verified.is_verified + + def _connect_tls_proxy(self, hostname: str, sock: socket.socket) -> ssl.SSLSocket: + """ + Establish a TLS connection to the proxy using the provided SSL context. + """ + # `_connect_tls_proxy` is called when self._tunnel_host is truthy. + proxy_config = typing.cast(ProxyConfig, self.proxy_config) + ssl_context = proxy_config.ssl_context + sock_and_verified = _ssl_wrap_socket_and_match_hostname( + sock, + cert_reqs=self.cert_reqs, + ssl_version=self.ssl_version, + ssl_minimum_version=self.ssl_minimum_version, + ssl_maximum_version=self.ssl_maximum_version, + ca_certs=self.ca_certs, + ca_cert_dir=self.ca_cert_dir, + ca_cert_data=self.ca_cert_data, + server_hostname=hostname, + ssl_context=ssl_context, + assert_hostname=proxy_config.assert_hostname, + assert_fingerprint=proxy_config.assert_fingerprint, + # Features that aren't implemented for proxies yet: + cert_file=None, + key_file=None, + key_password=None, + tls_in_tls=False, + ) + self.proxy_is_verified = sock_and_verified.is_verified + return sock_and_verified.socket # type: ignore[return-value] + + +class _WrappedAndVerifiedSocket(typing.NamedTuple): + """ + Wrapped socket and whether the connection is + verified after the TLS handshake + """ + + socket: ssl.SSLSocket | SSLTransport + is_verified: bool + + +def _ssl_wrap_socket_and_match_hostname( + sock: socket.socket, + *, + cert_reqs: None | str | int, + ssl_version: None | str | int, + ssl_minimum_version: int | None, + ssl_maximum_version: int | None, + cert_file: str | None, + key_file: str | None, + key_password: str | None, + ca_certs: str | None, + ca_cert_dir: str | None, + ca_cert_data: None | str | bytes, + assert_hostname: None | str | typing.Literal[False], + assert_fingerprint: str | None, + server_hostname: str | None, + ssl_context: ssl.SSLContext | None, + tls_in_tls: bool = False, +) -> _WrappedAndVerifiedSocket: + """Logic for constructing an SSLContext from all TLS parameters, passing + that down into ssl_wrap_socket, and then doing certificate verification + either via hostname or fingerprint. This function exists to guarantee + that both proxies and targets have the same behavior when connecting via TLS. + """ + default_ssl_context = False + if ssl_context is None: + default_ssl_context = True + context = create_urllib3_context( + ssl_version=resolve_ssl_version(ssl_version), + ssl_minimum_version=ssl_minimum_version, + ssl_maximum_version=ssl_maximum_version, + cert_reqs=resolve_cert_reqs(cert_reqs), + ) + else: + context = ssl_context + + context.verify_mode = resolve_cert_reqs(cert_reqs) + + # In some cases, we want to verify hostnames ourselves + if ( + # `ssl` can't verify fingerprints or alternate hostnames + assert_fingerprint + or assert_hostname + # assert_hostname can be set to False to disable hostname checking + or assert_hostname is False + # We still support OpenSSL 1.0.2, which prevents us from verifying + # hostnames easily: https://github.com/pyca/pyopenssl/pull/933 + or ssl_.IS_PYOPENSSL + or not ssl_.HAS_NEVER_CHECK_COMMON_NAME + ): + context.check_hostname = False + + # Try to load OS default certs if none are given. We need to do the hasattr() check + # for custom pyOpenSSL SSLContext objects because they don't support + # load_default_certs(). + if ( + not ca_certs + and not ca_cert_dir + and not ca_cert_data + and default_ssl_context + and hasattr(context, "load_default_certs") + ): + context.load_default_certs() + + # Ensure that IPv6 addresses are in the proper format and don't have a + # scope ID. Python's SSL module fails to recognize scoped IPv6 addresses + # and interprets them as DNS hostnames. + if server_hostname is not None: + normalized = server_hostname.strip("[]") + if "%" in normalized: + normalized = normalized[: normalized.rfind("%")] + if is_ipaddress(normalized): + server_hostname = normalized + + ssl_sock = ssl_wrap_socket( + sock=sock, + keyfile=key_file, + certfile=cert_file, + key_password=key_password, + ca_certs=ca_certs, + ca_cert_dir=ca_cert_dir, + ca_cert_data=ca_cert_data, + server_hostname=server_hostname, + ssl_context=context, + tls_in_tls=tls_in_tls, + ) + + try: + if assert_fingerprint: + _assert_fingerprint( + ssl_sock.getpeercert(binary_form=True), assert_fingerprint + ) + elif ( + context.verify_mode != ssl.CERT_NONE + and not context.check_hostname + and assert_hostname is not False + ): + cert: _TYPE_PEER_CERT_RET_DICT = ssl_sock.getpeercert() # type: ignore[assignment] + + # Need to signal to our match_hostname whether to use 'commonName' or not. + # If we're using our own constructed SSLContext we explicitly set 'False' + # because PyPy hard-codes 'True' from SSLContext.hostname_checks_common_name. + if default_ssl_context: + hostname_checks_common_name = False + else: + hostname_checks_common_name = ( + getattr(context, "hostname_checks_common_name", False) or False + ) + + _match_hostname( + cert, + assert_hostname or server_hostname, # type: ignore[arg-type] + hostname_checks_common_name, + ) + + return _WrappedAndVerifiedSocket( + socket=ssl_sock, + is_verified=context.verify_mode == ssl.CERT_REQUIRED + or bool(assert_fingerprint), + ) + except BaseException: + ssl_sock.close() + raise + + +def _match_hostname( + cert: _TYPE_PEER_CERT_RET_DICT | None, + asserted_hostname: str, + hostname_checks_common_name: bool = False, +) -> None: + # Our upstream implementation of ssl.match_hostname() + # only applies this normalization to IP addresses so it doesn't + # match DNS SANs so we do the same thing! + stripped_hostname = asserted_hostname.strip("[]") + if is_ipaddress(stripped_hostname): + asserted_hostname = stripped_hostname + + try: + match_hostname(cert, asserted_hostname, hostname_checks_common_name) + except CertificateError as e: + log.warning( + "Certificate did not match expected hostname: %s. Certificate: %s", + asserted_hostname, + cert, + ) + # Add cert to exception and reraise so client code can inspect + # the cert when catching the exception, if they want to + e._peer_cert = cert # type: ignore[attr-defined] + raise + + +def _wrap_proxy_error(err: Exception, proxy_scheme: str | None) -> ProxyError: + # Look for the phrase 'wrong version number', if found + # then we should warn the user that we're very sure that + # this proxy is HTTP-only and they have a configuration issue. + error_normalized = " ".join(re.split("[^a-z]", str(err).lower())) + is_likely_http_proxy = ( + "wrong version number" in error_normalized + or "unknown protocol" in error_normalized + or "record layer failure" in error_normalized + ) + http_proxy_warning = ( + ". Your proxy appears to only use HTTP and not HTTPS, " + "try changing your proxy URL to be HTTP. See: " + "https://urllib3.readthedocs.io/en/latest/advanced-usage.html" + "#https-proxy-error-http-proxy" + ) + new_err = ProxyError( + f"Unable to connect to proxy" + f"{http_proxy_warning if is_likely_http_proxy and proxy_scheme == 'https' else ''}", + err, + ) + new_err.__cause__ = err + return new_err + + +def _get_default_user_agent() -> str: + return f"python-urllib3/{__version__}" + + +class DummyConnection: + """Used to detect a failed ConnectionCls import.""" + + +if not ssl: + HTTPSConnection = DummyConnection # type: ignore[misc, assignment] # noqa: F811 + + +VerifiedHTTPSConnection = HTTPSConnection + + +def _url_from_connection( + conn: HTTPConnection | HTTPSConnection, path: str | None = None +) -> str: + """Returns the URL from a given connection. This is mainly used for testing and logging.""" + + scheme = "https" if isinstance(conn, HTTPSConnection) else "http" + + return Url(scheme=scheme, host=conn.host, port=conn.port, path=path).url diff --git a/.venv/lib/python3.12/site-packages/urllib3/connectionpool.py b/.venv/lib/python3.12/site-packages/urllib3/connectionpool.py new file mode 100644 index 0000000000000000000000000000000000000000..3a0685b4cdd0562e508b9dd032765b5c759ea61e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/urllib3/connectionpool.py @@ -0,0 +1,1178 @@ +from __future__ import annotations + +import errno +import logging +import queue +import sys +import typing +import warnings +import weakref +from socket import timeout as SocketTimeout +from types import TracebackType + +from ._base_connection import _TYPE_BODY +from ._collections import HTTPHeaderDict +from ._request_methods import RequestMethods +from .connection import ( + BaseSSLError, + BrokenPipeError, + DummyConnection, + HTTPConnection, + HTTPException, + HTTPSConnection, + ProxyConfig, + _wrap_proxy_error, +) +from .connection import port_by_scheme as port_by_scheme +from .exceptions import ( + ClosedPoolError, + EmptyPoolError, + FullPoolError, + HostChangedError, + InsecureRequestWarning, + LocationValueError, + MaxRetryError, + NewConnectionError, + ProtocolError, + ProxyError, + ReadTimeoutError, + SSLError, + TimeoutError, +) +from .response import BaseHTTPResponse +from .util.connection import is_connection_dropped +from .util.proxy import connection_requires_http_tunnel +from .util.request import _TYPE_BODY_POSITION, set_file_position +from .util.retry import Retry +from .util.ssl_match_hostname import CertificateError +from .util.timeout import _DEFAULT_TIMEOUT, _TYPE_DEFAULT, Timeout +from .util.url import Url, _encode_target +from .util.url import _normalize_host as normalize_host +from .util.url import parse_url +from .util.util import to_str + +if typing.TYPE_CHECKING: + import ssl + + from typing_extensions import Self + + from ._base_connection import BaseHTTPConnection, BaseHTTPSConnection + +log = logging.getLogger(__name__) + +_TYPE_TIMEOUT = typing.Union[Timeout, float, _TYPE_DEFAULT, None] + + +# Pool objects +class ConnectionPool: + """ + Base class for all connection pools, such as + :class:`.HTTPConnectionPool` and :class:`.HTTPSConnectionPool`. + + .. note:: + ConnectionPool.urlopen() does not normalize or percent-encode target URIs + which is useful if your target server doesn't support percent-encoded + target URIs. + """ + + scheme: str | None = None + QueueCls = queue.LifoQueue + + def __init__(self, host: str, port: int | None = None) -> None: + if not host: + raise LocationValueError("No host specified.") + + self.host = _normalize_host(host, scheme=self.scheme) + self.port = port + + # This property uses 'normalize_host()' (not '_normalize_host()') + # to avoid removing square braces around IPv6 addresses. + # This value is sent to `HTTPConnection.set_tunnel()` if called + # because square braces are required for HTTP CONNECT tunneling. + self._tunnel_host = normalize_host(host, scheme=self.scheme).lower() + + def __str__(self) -> str: + return f"{type(self).__name__}(host={self.host!r}, port={self.port!r})" + + def __enter__(self) -> Self: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> typing.Literal[False]: + self.close() + # Return False to re-raise any potential exceptions + return False + + def close(self) -> None: + """ + Close all pooled connections and disable the pool. + """ + + +# This is taken from http://hg.python.org/cpython/file/7aaba721ebc0/Lib/socket.py#l252 +_blocking_errnos = {errno.EAGAIN, errno.EWOULDBLOCK} + + +class HTTPConnectionPool(ConnectionPool, RequestMethods): + """ + Thread-safe connection pool for one host. + + :param host: + Host used for this HTTP Connection (e.g. "localhost"), passed into + :class:`http.client.HTTPConnection`. + + :param port: + Port used for this HTTP Connection (None is equivalent to 80), passed + into :class:`http.client.HTTPConnection`. + + :param timeout: + Socket timeout in seconds for each individual connection. This can + be a float or integer, which sets the timeout for the HTTP request, + or an instance of :class:`urllib3.util.Timeout` which gives you more + fine-grained control over request timeouts. After the constructor has + been parsed, this is always a `urllib3.util.Timeout` object. + + :param maxsize: + Number of connections to save that can be reused. More than 1 is useful + in multithreaded situations. If ``block`` is set to False, more + connections will be created but they will not be saved once they've + been used. + + :param block: + If set to True, no more than ``maxsize`` connections will be used at + a time. When no free connections are available, the call will block + until a connection has been released. This is a useful side effect for + particular multithreaded situations where one does not want to use more + than maxsize connections per host to prevent flooding. + + :param headers: + Headers to include with all requests, unless other headers are given + explicitly. + + :param retries: + Retry configuration to use by default with requests in this pool. + + :param _proxy: + Parsed proxy URL, should not be used directly, instead, see + :class:`urllib3.ProxyManager` + + :param _proxy_headers: + A dictionary with proxy headers, should not be used directly, + instead, see :class:`urllib3.ProxyManager` + + :param \\**conn_kw: + Additional parameters are used to create fresh :class:`urllib3.connection.HTTPConnection`, + :class:`urllib3.connection.HTTPSConnection` instances. + """ + + scheme = "http" + ConnectionCls: type[BaseHTTPConnection] | type[BaseHTTPSConnection] = HTTPConnection + + def __init__( + self, + host: str, + port: int | None = None, + timeout: _TYPE_TIMEOUT | None = _DEFAULT_TIMEOUT, + maxsize: int = 1, + block: bool = False, + headers: typing.Mapping[str, str] | None = None, + retries: Retry | bool | int | None = None, + _proxy: Url | None = None, + _proxy_headers: typing.Mapping[str, str] | None = None, + _proxy_config: ProxyConfig | None = None, + **conn_kw: typing.Any, + ): + ConnectionPool.__init__(self, host, port) + RequestMethods.__init__(self, headers) + + if not isinstance(timeout, Timeout): + timeout = Timeout.from_float(timeout) + + if retries is None: + retries = Retry.DEFAULT + + self.timeout = timeout + self.retries = retries + + self.pool: queue.LifoQueue[typing.Any] | None = self.QueueCls(maxsize) + self.block = block + + self.proxy = _proxy + self.proxy_headers = _proxy_headers or {} + self.proxy_config = _proxy_config + + # Fill the queue up so that doing get() on it will block properly + for _ in range(maxsize): + self.pool.put(None) + + # These are mostly for testing and debugging purposes. + self.num_connections = 0 + self.num_requests = 0 + self.conn_kw = conn_kw + + if self.proxy: + # Enable Nagle's algorithm for proxies, to avoid packet fragmentation. + # We cannot know if the user has added default socket options, so we cannot replace the + # list. + self.conn_kw.setdefault("socket_options", []) + + self.conn_kw["proxy"] = self.proxy + self.conn_kw["proxy_config"] = self.proxy_config + + # Do not pass 'self' as callback to 'finalize'. + # Then the 'finalize' would keep an endless living (leak) to self. + # By just passing a reference to the pool allows the garbage collector + # to free self if nobody else has a reference to it. + pool = self.pool + + # Close all the HTTPConnections in the pool before the + # HTTPConnectionPool object is garbage collected. + weakref.finalize(self, _close_pool_connections, pool) + + def _new_conn(self) -> BaseHTTPConnection: + """ + Return a fresh :class:`HTTPConnection`. + """ + self.num_connections += 1 + log.debug( + "Starting new HTTP connection (%d): %s:%s", + self.num_connections, + self.host, + self.port or "80", + ) + + conn = self.ConnectionCls( + host=self.host, + port=self.port, + timeout=self.timeout.connect_timeout, + **self.conn_kw, + ) + return conn + + def _get_conn(self, timeout: float | None = None) -> BaseHTTPConnection: + """ + Get a connection. Will return a pooled connection if one is available. + + If no connections are available and :prop:`.block` is ``False``, then a + fresh connection is returned. + + :param timeout: + Seconds to wait before giving up and raising + :class:`urllib3.exceptions.EmptyPoolError` if the pool is empty and + :prop:`.block` is ``True``. + """ + conn = None + + if self.pool is None: + raise ClosedPoolError(self, "Pool is closed.") + + try: + conn = self.pool.get(block=self.block, timeout=timeout) + + except AttributeError: # self.pool is None + raise ClosedPoolError(self, "Pool is closed.") from None # Defensive: + + except queue.Empty: + if self.block: + raise EmptyPoolError( + self, + "Pool is empty and a new connection can't be opened due to blocking mode.", + ) from None + pass # Oh well, we'll create a new connection then + + # If this is a persistent connection, check if it got disconnected + if conn and is_connection_dropped(conn): + log.debug("Resetting dropped connection: %s", self.host) + conn.close() + + return conn or self._new_conn() + + def _put_conn(self, conn: BaseHTTPConnection | None) -> None: + """ + Put a connection back into the pool. + + :param conn: + Connection object for the current host and port as returned by + :meth:`._new_conn` or :meth:`._get_conn`. + + If the pool is already full, the connection is closed and discarded + because we exceeded maxsize. If connections are discarded frequently, + then maxsize should be increased. + + If the pool is closed, then the connection will be closed and discarded. + """ + if self.pool is not None: + try: + self.pool.put(conn, block=False) + return # Everything is dandy, done. + except AttributeError: + # self.pool is None. + pass + except queue.Full: + # Connection never got put back into the pool, close it. + if conn: + conn.close() + + if self.block: + # This should never happen if you got the conn from self._get_conn + raise FullPoolError( + self, + "Pool reached maximum size and no more connections are allowed.", + ) from None + + log.warning( + "Connection pool is full, discarding connection: %s. Connection pool size: %s", + self.host, + self.pool.qsize(), + ) + + # Connection never got put back into the pool, close it. + if conn: + conn.close() + + def _validate_conn(self, conn: BaseHTTPConnection) -> None: + """ + Called right before a request is made, after the socket is created. + """ + + def _prepare_proxy(self, conn: BaseHTTPConnection) -> None: + # Nothing to do for HTTP connections. + pass + + def _get_timeout(self, timeout: _TYPE_TIMEOUT) -> Timeout: + """Helper that always returns a :class:`urllib3.util.Timeout`""" + if timeout is _DEFAULT_TIMEOUT: + return self.timeout.clone() + + if isinstance(timeout, Timeout): + return timeout.clone() + else: + # User passed us an int/float. This is for backwards compatibility, + # can be removed later + return Timeout.from_float(timeout) + + def _raise_timeout( + self, + err: BaseSSLError | OSError | SocketTimeout, + url: str, + timeout_value: _TYPE_TIMEOUT | None, + ) -> None: + """Is the error actually a timeout? Will raise a ReadTimeout or pass""" + + if isinstance(err, SocketTimeout): + raise ReadTimeoutError( + self, url, f"Read timed out. (read timeout={timeout_value})" + ) from err + + # See the above comment about EAGAIN in Python 3. + if hasattr(err, "errno") and err.errno in _blocking_errnos: + raise ReadTimeoutError( + self, url, f"Read timed out. (read timeout={timeout_value})" + ) from err + + def _make_request( + self, + conn: BaseHTTPConnection, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + retries: Retry | None = None, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + chunked: bool = False, + response_conn: BaseHTTPConnection | None = None, + preload_content: bool = True, + decode_content: bool = True, + enforce_content_length: bool = True, + ) -> BaseHTTPResponse: + """ + Perform a request on a given urllib connection object taken from our + pool. + + :param conn: + a connection from one of our connection pools + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param url: + The URL to perform the request on. + + :param body: + Data to send in the request body, either :class:`str`, :class:`bytes`, + an iterable of :class:`str`/:class:`bytes`, or a file-like object. + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. If None, pool headers are used. If provided, + these headers completely replace any pool-specific headers. + + :param retries: + Configure the number of retries to allow before raising a + :class:`~urllib3.exceptions.MaxRetryError` exception. + + Pass ``None`` to retry until you receive a response. Pass a + :class:`~urllib3.util.retry.Retry` object for fine-grained control + over different types of retries. + Pass an integer number to retry connection errors that many times, + but no other types of errors. Pass zero to never retry. + + If ``False``, then retries are disabled and any exception is raised + immediately. Also, instead of raising a MaxRetryError on redirects, + the redirect response will be returned. + + :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. + + :param timeout: + If specified, overrides the default timeout for this one + request. It may be a float (in seconds) or an instance of + :class:`urllib3.util.Timeout`. + + :param chunked: + If True, urllib3 will send the body using chunked transfer + encoding. Otherwise, urllib3 will send the body using the standard + content-length form. Defaults to False. + + :param response_conn: + Set this to ``None`` if you will handle releasing the connection or + set the connection to have the response release it. + + :param preload_content: + If True, the response's body will be preloaded during construction. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param enforce_content_length: + Enforce content length checking. Body returned by server must match + value of Content-Length header, if present. Otherwise, raise error. + """ + self.num_requests += 1 + + timeout_obj = self._get_timeout(timeout) + timeout_obj.start_connect() + conn.timeout = Timeout.resolve_default_timeout(timeout_obj.connect_timeout) + + try: + # Trigger any extra validation we need to do. + try: + self._validate_conn(conn) + except (SocketTimeout, BaseSSLError) as e: + self._raise_timeout(err=e, url=url, timeout_value=conn.timeout) + raise + + # _validate_conn() starts the connection to an HTTPS proxy + # so we need to wrap errors with 'ProxyError' here too. + except ( + OSError, + NewConnectionError, + TimeoutError, + BaseSSLError, + CertificateError, + SSLError, + ) as e: + new_e: Exception = e + if isinstance(e, (BaseSSLError, CertificateError)): + new_e = SSLError(e) + # If the connection didn't successfully connect to it's proxy + # then there + if isinstance( + new_e, (OSError, NewConnectionError, TimeoutError, SSLError) + ) and (conn and conn.proxy and not conn.has_connected_to_proxy): + new_e = _wrap_proxy_error(new_e, conn.proxy.scheme) + raise new_e + + # conn.request() calls http.client.*.request, not the method in + # urllib3.request. It also calls makefile (recv) on the socket. + try: + conn.request( + method, + url, + body=body, + headers=headers, + chunked=chunked, + preload_content=preload_content, + decode_content=decode_content, + enforce_content_length=enforce_content_length, + ) + + # We are swallowing BrokenPipeError (errno.EPIPE) since the server is + # legitimately able to close the connection after sending a valid response. + # With this behaviour, the received response is still readable. + except BrokenPipeError: + pass + except OSError as e: + # MacOS/Linux + # EPROTOTYPE and ECONNRESET are needed on macOS + # https://erickt.github.io/blog/2014/11/19/adventures-in-debugging-a-potential-osx-kernel-bug/ + # Condition changed later to emit ECONNRESET instead of only EPROTOTYPE. + if e.errno != errno.EPROTOTYPE and e.errno != errno.ECONNRESET: + raise + + # Reset the timeout for the recv() on the socket + read_timeout = timeout_obj.read_timeout + + if not conn.is_closed: + # In Python 3 socket.py will catch EAGAIN and return None when you + # try and read into the file pointer created by http.client, which + # instead raises a BadStatusLine exception. Instead of catching + # the exception and assuming all BadStatusLine exceptions are read + # timeouts, check for a zero timeout before making the request. + if read_timeout == 0: + raise ReadTimeoutError( + self, url, f"Read timed out. (read timeout={read_timeout})" + ) + conn.timeout = read_timeout + + # Receive the response from the server + try: + response = conn.getresponse() + except (BaseSSLError, OSError) as e: + self._raise_timeout(err=e, url=url, timeout_value=read_timeout) + raise + + # Set properties that are used by the pooling layer. + response.retries = retries + response._connection = response_conn # type: ignore[attr-defined] + response._pool = self # type: ignore[attr-defined] + + log.debug( + '%s://%s:%s "%s %s %s" %s %s', + self.scheme, + self.host, + self.port, + method, + url, + response.version_string, + response.status, + response.length_remaining, + ) + + return response + + def close(self) -> None: + """ + Close all pooled connections and disable the pool. + """ + if self.pool is None: + return + # Disable access to the pool + old_pool, self.pool = self.pool, None + + # Close all the HTTPConnections in the pool. + _close_pool_connections(old_pool) + + def is_same_host(self, url: str) -> bool: + """ + Check if the given ``url`` is a member of the same host as this + connection pool. + """ + if url.startswith("/"): + return True + + # TODO: Add optional support for socket.gethostbyname checking. + scheme, _, host, port, *_ = parse_url(url) + scheme = scheme or "http" + if host is not None: + host = _normalize_host(host, scheme=scheme) + + # Use explicit default port for comparison when none is given + if self.port and not port: + port = port_by_scheme.get(scheme) + elif not self.port and port == port_by_scheme.get(scheme): + port = None + + return (scheme, host, port) == (self.scheme, self.host, self.port) + + def urlopen( # type: ignore[override] + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + retries: Retry | bool | int | None = None, + redirect: bool = True, + assert_same_host: bool = True, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + pool_timeout: int | None = None, + release_conn: bool | None = None, + chunked: bool = False, + body_pos: _TYPE_BODY_POSITION | None = None, + preload_content: bool = True, + decode_content: bool = True, + **response_kw: typing.Any, + ) -> BaseHTTPResponse: + """ + Get a connection from the pool and perform an HTTP request. This is the + lowest level call for making a request, so you'll need to specify all + the raw details. + + .. note:: + + More commonly, it's appropriate to use a convenience method + such as :meth:`request`. + + .. note:: + + `release_conn` will only behave as expected if + `preload_content=False` because we want to make + `preload_content=False` the default behaviour someday soon without + breaking backwards compatibility. + + :param method: + HTTP request method (such as GET, POST, PUT, etc.) + + :param url: + The URL to perform the request on. + + :param body: + Data to send in the request body, either :class:`str`, :class:`bytes`, + an iterable of :class:`str`/:class:`bytes`, or a file-like object. + + :param headers: + Dictionary of custom headers to send, such as User-Agent, + If-None-Match, etc. If None, pool headers are used. If provided, + these headers completely replace any pool-specific headers. + + :param retries: + Configure the number of retries to allow before raising a + :class:`~urllib3.exceptions.MaxRetryError` exception. + + If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a + :class:`~urllib3.util.retry.Retry` object for fine-grained control + over different types of retries. + Pass an integer number to retry connection errors that many times, + but no other types of errors. Pass zero to never retry. + + If ``False``, then retries are disabled and any exception is raised + immediately. Also, instead of raising a MaxRetryError on redirects, + the redirect response will be returned. + + :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. + + :param redirect: + If True, automatically handle redirects (status codes 301, 302, + 303, 307, 308). Each redirect counts as a retry. Disabling retries + will disable redirect, too. + + :param assert_same_host: + If ``True``, will make sure that the host of the pool requests is + consistent else will raise HostChangedError. When ``False``, you can + use the pool on an HTTP proxy and request foreign hosts. + + :param timeout: + If specified, overrides the default timeout for this one + request. It may be a float (in seconds) or an instance of + :class:`urllib3.util.Timeout`. + + :param pool_timeout: + If set and the pool is set to block=True, then this method will + block for ``pool_timeout`` seconds and raise EmptyPoolError if no + connection is available within the time period. + + :param bool preload_content: + If True, the response's body will be preloaded into memory. + + :param bool decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param release_conn: + If False, then the urlopen call will not release the connection + back into the pool once a response is received (but will release if + you read the entire contents of the response such as when + `preload_content=True`). This is useful if you're not preloading + the response's content immediately. You will need to call + ``r.release_conn()`` on the response ``r`` to return the connection + back into the pool. If None, it takes the value of ``preload_content`` + which defaults to ``True``. + + :param bool chunked: + If True, urllib3 will send the body using chunked transfer + encoding. Otherwise, urllib3 will send the body using the standard + content-length form. Defaults to False. + + :param int body_pos: + Position to seek to in file-like body in the event of a retry or + redirect. Typically this won't need to be set because urllib3 will + auto-populate the value when needed. + """ + parsed_url = parse_url(url) + destination_scheme = parsed_url.scheme + + if headers is None: + headers = self.headers + + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect, default=self.retries) + + if release_conn is None: + release_conn = preload_content + + # Check host + if assert_same_host and not self.is_same_host(url): + raise HostChangedError(self, url, retries) + + # Ensure that the URL we're connecting to is properly encoded + if url.startswith("/"): + url = to_str(_encode_target(url)) + else: + url = to_str(parsed_url.url) + + conn = None + + # Track whether `conn` needs to be released before + # returning/raising/recursing. Update this variable if necessary, and + # leave `release_conn` constant throughout the function. That way, if + # the function recurses, the original value of `release_conn` will be + # passed down into the recursive call, and its value will be respected. + # + # See issue #651 [1] for details. + # + # [1] + release_this_conn = release_conn + + http_tunnel_required = connection_requires_http_tunnel( + self.proxy, self.proxy_config, destination_scheme + ) + + # Merge the proxy headers. Only done when not using HTTP CONNECT. We + # have to copy the headers dict so we can safely change it without those + # changes being reflected in anyone else's copy. + if not http_tunnel_required: + headers = headers.copy() # type: ignore[attr-defined] + headers.update(self.proxy_headers) # type: ignore[union-attr] + + # Must keep the exception bound to a separate variable or else Python 3 + # complains about UnboundLocalError. + err = None + + # Keep track of whether we cleanly exited the except block. This + # ensures we do proper cleanup in finally. + clean_exit = False + + # Rewind body position, if needed. Record current position + # for future rewinds in the event of a redirect/retry. + body_pos = set_file_position(body, body_pos) + + try: + # Request a connection from the queue. + timeout_obj = self._get_timeout(timeout) + conn = self._get_conn(timeout=pool_timeout) + + conn.timeout = timeout_obj.connect_timeout # type: ignore[assignment] + + # Is this a closed/new connection that requires CONNECT tunnelling? + if self.proxy is not None and http_tunnel_required and conn.is_closed: + try: + self._prepare_proxy(conn) + except (BaseSSLError, OSError, SocketTimeout) as e: + self._raise_timeout( + err=e, url=self.proxy.url, timeout_value=conn.timeout + ) + raise + + # If we're going to release the connection in ``finally:``, then + # the response doesn't need to know about the connection. Otherwise + # it will also try to release it and we'll have a double-release + # mess. + response_conn = conn if not release_conn else None + + # Make the request on the HTTPConnection object + response = self._make_request( + conn, + method, + url, + timeout=timeout_obj, + body=body, + headers=headers, + chunked=chunked, + retries=retries, + response_conn=response_conn, + preload_content=preload_content, + decode_content=decode_content, + **response_kw, + ) + + # Everything went great! + clean_exit = True + + except EmptyPoolError: + # Didn't get a connection from the pool, no need to clean up + clean_exit = True + release_this_conn = False + raise + + except ( + TimeoutError, + HTTPException, + OSError, + ProtocolError, + BaseSSLError, + SSLError, + CertificateError, + ProxyError, + ) as e: + # Discard the connection for these exceptions. It will be + # replaced during the next _get_conn() call. + clean_exit = False + new_e: Exception = e + if isinstance(e, (BaseSSLError, CertificateError)): + new_e = SSLError(e) + if isinstance( + new_e, + ( + OSError, + NewConnectionError, + TimeoutError, + SSLError, + HTTPException, + ), + ) and (conn and conn.proxy and not conn.has_connected_to_proxy): + new_e = _wrap_proxy_error(new_e, conn.proxy.scheme) + elif isinstance(new_e, (OSError, HTTPException)): + new_e = ProtocolError("Connection aborted.", new_e) + + retries = retries.increment( + method, url, error=new_e, _pool=self, _stacktrace=sys.exc_info()[2] + ) + retries.sleep() + + # Keep track of the error for the retry warning. + err = e + + finally: + if not clean_exit: + # We hit some kind of exception, handled or otherwise. We need + # to throw the connection away unless explicitly told not to. + # Close the connection, set the variable to None, and make sure + # we put the None back in the pool to avoid leaking it. + if conn: + conn.close() + conn = None + release_this_conn = True + + if release_this_conn: + # Put the connection back to be reused. If the connection is + # expired then it will be None, which will get replaced with a + # fresh connection during _get_conn. + self._put_conn(conn) + + if not conn: + # Try again + log.warning( + "Retrying (%r) after connection broken by '%r': %s", retries, err, url + ) + return self.urlopen( + method, + url, + body, + headers, + retries, + redirect, + assert_same_host, + timeout=timeout, + pool_timeout=pool_timeout, + release_conn=release_conn, + chunked=chunked, + body_pos=body_pos, + preload_content=preload_content, + decode_content=decode_content, + **response_kw, + ) + + # Handle redirect? + redirect_location = redirect and response.get_redirect_location() + if redirect_location: + if response.status == 303: + # Change the method according to RFC 9110, Section 15.4.4. + method = "GET" + # And lose the body not to transfer anything sensitive. + body = None + headers = HTTPHeaderDict(headers)._prepare_for_method_change() + + try: + retries = retries.increment(method, url, response=response, _pool=self) + except MaxRetryError: + if retries.raise_on_redirect: + response.drain_conn() + raise + return response + + response.drain_conn() + retries.sleep_for_retry(response) + log.debug("Redirecting %s -> %s", url, redirect_location) + return self.urlopen( + method, + redirect_location, + body, + headers, + retries=retries, + redirect=redirect, + assert_same_host=assert_same_host, + timeout=timeout, + pool_timeout=pool_timeout, + release_conn=release_conn, + chunked=chunked, + body_pos=body_pos, + preload_content=preload_content, + decode_content=decode_content, + **response_kw, + ) + + # Check if we should retry the HTTP response. + has_retry_after = bool(response.headers.get("Retry-After")) + if retries.is_retry(method, response.status, has_retry_after): + try: + retries = retries.increment(method, url, response=response, _pool=self) + except MaxRetryError: + if retries.raise_on_status: + response.drain_conn() + raise + return response + + response.drain_conn() + retries.sleep(response) + log.debug("Retry: %s", url) + return self.urlopen( + method, + url, + body, + headers, + retries=retries, + redirect=redirect, + assert_same_host=assert_same_host, + timeout=timeout, + pool_timeout=pool_timeout, + release_conn=release_conn, + chunked=chunked, + body_pos=body_pos, + preload_content=preload_content, + decode_content=decode_content, + **response_kw, + ) + + return response + + +class HTTPSConnectionPool(HTTPConnectionPool): + """ + Same as :class:`.HTTPConnectionPool`, but HTTPS. + + :class:`.HTTPSConnection` uses one of ``assert_fingerprint``, + ``assert_hostname`` and ``host`` in this order to verify connections. + If ``assert_hostname`` is False, no verification is done. + + The ``key_file``, ``cert_file``, ``cert_reqs``, ``ca_certs``, + ``ca_cert_dir``, ``ssl_version``, ``key_password`` are only used if :mod:`ssl` + is available and are fed into :meth:`urllib3.util.ssl_wrap_socket` to upgrade + the connection socket into an SSL socket. + """ + + scheme = "https" + ConnectionCls: type[BaseHTTPSConnection] = HTTPSConnection + + def __init__( + self, + host: str, + port: int | None = None, + timeout: _TYPE_TIMEOUT | None = _DEFAULT_TIMEOUT, + maxsize: int = 1, + block: bool = False, + headers: typing.Mapping[str, str] | None = None, + retries: Retry | bool | int | None = None, + _proxy: Url | None = None, + _proxy_headers: typing.Mapping[str, str] | None = None, + key_file: str | None = None, + cert_file: str | None = None, + cert_reqs: int | str | None = None, + key_password: str | None = None, + ca_certs: str | None = None, + ssl_version: int | str | None = None, + ssl_minimum_version: ssl.TLSVersion | None = None, + ssl_maximum_version: ssl.TLSVersion | None = None, + assert_hostname: str | typing.Literal[False] | None = None, + assert_fingerprint: str | None = None, + ca_cert_dir: str | None = None, + **conn_kw: typing.Any, + ) -> None: + super().__init__( + host, + port, + timeout, + maxsize, + block, + headers, + retries, + _proxy, + _proxy_headers, + **conn_kw, + ) + + self.key_file = key_file + self.cert_file = cert_file + self.cert_reqs = cert_reqs + self.key_password = key_password + self.ca_certs = ca_certs + self.ca_cert_dir = ca_cert_dir + self.ssl_version = ssl_version + self.ssl_minimum_version = ssl_minimum_version + self.ssl_maximum_version = ssl_maximum_version + self.assert_hostname = assert_hostname + self.assert_fingerprint = assert_fingerprint + + def _prepare_proxy(self, conn: HTTPSConnection) -> None: # type: ignore[override] + """Establishes a tunnel connection through HTTP CONNECT.""" + if self.proxy and self.proxy.scheme == "https": + tunnel_scheme = "https" + else: + tunnel_scheme = "http" + + conn.set_tunnel( + scheme=tunnel_scheme, + host=self._tunnel_host, + port=self.port, + headers=self.proxy_headers, + ) + conn.connect() + + def _new_conn(self) -> BaseHTTPSConnection: + """ + Return a fresh :class:`urllib3.connection.HTTPConnection`. + """ + self.num_connections += 1 + log.debug( + "Starting new HTTPS connection (%d): %s:%s", + self.num_connections, + self.host, + self.port or "443", + ) + + if not self.ConnectionCls or self.ConnectionCls is DummyConnection: # type: ignore[comparison-overlap] + raise ImportError( + "Can't connect to HTTPS URL because the SSL module is not available." + ) + + actual_host: str = self.host + actual_port = self.port + if self.proxy is not None and self.proxy.host is not None: + actual_host = self.proxy.host + actual_port = self.proxy.port + + return self.ConnectionCls( + host=actual_host, + port=actual_port, + timeout=self.timeout.connect_timeout, + cert_file=self.cert_file, + key_file=self.key_file, + key_password=self.key_password, + cert_reqs=self.cert_reqs, + ca_certs=self.ca_certs, + ca_cert_dir=self.ca_cert_dir, + assert_hostname=self.assert_hostname, + assert_fingerprint=self.assert_fingerprint, + ssl_version=self.ssl_version, + ssl_minimum_version=self.ssl_minimum_version, + ssl_maximum_version=self.ssl_maximum_version, + **self.conn_kw, + ) + + def _validate_conn(self, conn: BaseHTTPConnection) -> None: + """ + Called right before a request is made, after the socket is created. + """ + super()._validate_conn(conn) + + # Force connect early to allow us to validate the connection. + if conn.is_closed: + conn.connect() + + # TODO revise this, see https://github.com/urllib3/urllib3/issues/2791 + if not conn.is_verified and not conn.proxy_is_verified: + warnings.warn( + ( + f"Unverified HTTPS request is being made to host '{conn.host}'. " + "Adding certificate verification is strongly advised. See: " + "https://urllib3.readthedocs.io/en/latest/advanced-usage.html" + "#tls-warnings" + ), + InsecureRequestWarning, + ) + + +def connection_from_url(url: str, **kw: typing.Any) -> HTTPConnectionPool: + """ + Given a url, return an :class:`.ConnectionPool` instance of its host. + + This is a shortcut for not having to parse out the scheme, host, and port + of the url before creating an :class:`.ConnectionPool` instance. + + :param url: + Absolute URL string that must include the scheme. Port is optional. + + :param \\**kw: + Passes additional parameters to the constructor of the appropriate + :class:`.ConnectionPool`. Useful for specifying things like + timeout, maxsize, headers, etc. + + Example:: + + >>> conn = connection_from_url('http://google.com/') + >>> r = conn.request('GET', '/') + """ + scheme, _, host, port, *_ = parse_url(url) + scheme = scheme or "http" + port = port or port_by_scheme.get(scheme, 80) + if scheme == "https": + return HTTPSConnectionPool(host, port=port, **kw) # type: ignore[arg-type] + else: + return HTTPConnectionPool(host, port=port, **kw) # type: ignore[arg-type] + + +@typing.overload +def _normalize_host(host: None, scheme: str | None) -> None: ... + + +@typing.overload +def _normalize_host(host: str, scheme: str | None) -> str: ... + + +def _normalize_host(host: str | None, scheme: str | None) -> str | None: + """ + Normalize hosts for comparisons and use with sockets. + """ + + host = normalize_host(host, scheme) + + # httplib doesn't like it when we include brackets in IPv6 addresses + # Specifically, if we include brackets but also pass the port then + # httplib crazily doubles up the square brackets on the Host header. + # Instead, we need to make sure we never pass ``None`` as the port. + # However, for backward compatibility reasons we can't actually + # *assert* that. See http://bugs.python.org/issue28539 + if host and host.startswith("[") and host.endswith("]"): + host = host[1:-1] + return host + + +def _url_from_pool( + pool: HTTPConnectionPool | HTTPSConnectionPool, path: str | None = None +) -> str: + """Returns the URL from a given connection pool. This is mainly used for testing and logging.""" + return Url(scheme=pool.scheme, host=pool.host, port=pool.port, path=path).url + + +def _close_pool_connections(pool: queue.LifoQueue[typing.Any]) -> None: + """Drains a queue of connections and closes each one.""" + try: + while True: + conn = pool.get(block=False) + if conn: + conn.close() + except queue.Empty: + pass # Done. diff --git a/.venv/lib/python3.12/site-packages/urllib3/exceptions.py b/.venv/lib/python3.12/site-packages/urllib3/exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..a0de9d6cc4f0cc88123d696daa4fd095e1743ece --- /dev/null +++ b/.venv/lib/python3.12/site-packages/urllib3/exceptions.py @@ -0,0 +1,335 @@ +from __future__ import annotations + +import socket +import typing +import warnings +from email.errors import MessageDefect +from http.client import IncompleteRead as httplib_IncompleteRead + +if typing.TYPE_CHECKING: + from .connection import HTTPConnection + from .connectionpool import ConnectionPool + from .response import HTTPResponse + from .util.retry import Retry + +# Base Exceptions + + +class HTTPError(Exception): + """Base exception used by this module.""" + + +class HTTPWarning(Warning): + """Base warning used by this module.""" + + +_TYPE_REDUCE_RESULT = tuple[typing.Callable[..., object], tuple[object, ...]] + + +class PoolError(HTTPError): + """Base exception for errors caused within a pool.""" + + def __init__(self, pool: ConnectionPool, message: str) -> None: + self.pool = pool + self._message = message + super().__init__(f"{pool}: {message}") + + def __reduce__(self) -> _TYPE_REDUCE_RESULT: + # For pickling purposes. + return self.__class__, (None, self._message) + + +class RequestError(PoolError): + """Base exception for PoolErrors that have associated URLs.""" + + def __init__(self, pool: ConnectionPool, url: str, message: str) -> None: + self.url = url + super().__init__(pool, message) + + def __reduce__(self) -> _TYPE_REDUCE_RESULT: + # For pickling purposes. + return self.__class__, (None, self.url, self._message) + + +class SSLError(HTTPError): + """Raised when SSL certificate fails in an HTTPS connection.""" + + +class ProxyError(HTTPError): + """Raised when the connection to a proxy fails.""" + + # The original error is also available as __cause__. + original_error: Exception + + def __init__(self, message: str, error: Exception) -> None: + super().__init__(message, error) + self.original_error = error + + +class DecodeError(HTTPError): + """Raised when automatic decoding based on Content-Type fails.""" + + +class ProtocolError(HTTPError): + """Raised when something unexpected happens mid-request/response.""" + + +#: Renamed to ProtocolError but aliased for backwards compatibility. +ConnectionError = ProtocolError + + +# Leaf Exceptions + + +class MaxRetryError(RequestError): + """Raised when the maximum number of retries is exceeded. + + :param pool: The connection pool + :type pool: :class:`~urllib3.connectionpool.HTTPConnectionPool` + :param str url: The requested Url + :param reason: The underlying error + :type reason: :class:`Exception` + + """ + + def __init__( + self, pool: ConnectionPool, url: str, reason: Exception | None = None + ) -> None: + self.reason = reason + + message = f"Max retries exceeded with url: {url} (Caused by {reason!r})" + + super().__init__(pool, url, message) + + def __reduce__(self) -> _TYPE_REDUCE_RESULT: + # For pickling purposes. + return self.__class__, (None, self.url, self.reason) + + +class HostChangedError(RequestError): + """Raised when an existing pool gets a request for a foreign host.""" + + def __init__( + self, pool: ConnectionPool, url: str, retries: Retry | int = 3 + ) -> None: + message = f"Tried to open a foreign host with url: {url}" + super().__init__(pool, url, message) + self.retries = retries + + +class TimeoutStateError(HTTPError): + """Raised when passing an invalid state to a timeout""" + + +class TimeoutError(HTTPError): + """Raised when a socket timeout error occurs. + + Catching this error will catch both :exc:`ReadTimeoutErrors + ` and :exc:`ConnectTimeoutErrors `. + """ + + +class ReadTimeoutError(TimeoutError, RequestError): + """Raised when a socket timeout occurs while receiving data from a server""" + + +# This timeout error does not have a URL attached and needs to inherit from the +# base HTTPError +class ConnectTimeoutError(TimeoutError): + """Raised when a socket timeout occurs while connecting to a server""" + + +class NewConnectionError(ConnectTimeoutError, HTTPError): + """Raised when we fail to establish a new connection. Usually ECONNREFUSED.""" + + def __init__(self, conn: HTTPConnection, message: str) -> None: + self.conn = conn + self._message = message + super().__init__(f"{conn}: {message}") + + def __reduce__(self) -> _TYPE_REDUCE_RESULT: + # For pickling purposes. + return self.__class__, (None, self._message) + + @property + def pool(self) -> HTTPConnection: + warnings.warn( + "The 'pool' property is deprecated and will be removed " + "in urllib3 v2.1.0. Use 'conn' instead.", + DeprecationWarning, + stacklevel=2, + ) + + return self.conn + + +class NameResolutionError(NewConnectionError): + """Raised when host name resolution fails.""" + + def __init__(self, host: str, conn: HTTPConnection, reason: socket.gaierror): + message = f"Failed to resolve '{host}' ({reason})" + self._host = host + self._reason = reason + super().__init__(conn, message) + + def __reduce__(self) -> _TYPE_REDUCE_RESULT: + # For pickling purposes. + return self.__class__, (self._host, None, self._reason) + + +class EmptyPoolError(PoolError): + """Raised when a pool runs out of connections and no more are allowed.""" + + +class FullPoolError(PoolError): + """Raised when we try to add a connection to a full pool in blocking mode.""" + + +class ClosedPoolError(PoolError): + """Raised when a request enters a pool after the pool has been closed.""" + + +class LocationValueError(ValueError, HTTPError): + """Raised when there is something wrong with a given URL input.""" + + +class LocationParseError(LocationValueError): + """Raised when get_host or similar fails to parse the URL input.""" + + def __init__(self, location: str) -> None: + message = f"Failed to parse: {location}" + super().__init__(message) + + self.location = location + + +class URLSchemeUnknown(LocationValueError): + """Raised when a URL input has an unsupported scheme.""" + + def __init__(self, scheme: str): + message = f"Not supported URL scheme {scheme}" + super().__init__(message) + + self.scheme = scheme + + +class ResponseError(HTTPError): + """Used as a container for an error reason supplied in a MaxRetryError.""" + + GENERIC_ERROR = "too many error responses" + SPECIFIC_ERROR = "too many {status_code} error responses" + + +class SecurityWarning(HTTPWarning): + """Warned when performing security reducing actions""" + + +class InsecureRequestWarning(SecurityWarning): + """Warned when making an unverified HTTPS request.""" + + +class NotOpenSSLWarning(SecurityWarning): + """Warned when using unsupported SSL library""" + + +class SystemTimeWarning(SecurityWarning): + """Warned when system time is suspected to be wrong""" + + +class InsecurePlatformWarning(SecurityWarning): + """Warned when certain TLS/SSL configuration is not available on a platform.""" + + +class DependencyWarning(HTTPWarning): + """ + Warned when an attempt is made to import a module with missing optional + dependencies. + """ + + +class ResponseNotChunked(ProtocolError, ValueError): + """Response needs to be chunked in order to read it as chunks.""" + + +class BodyNotHttplibCompatible(HTTPError): + """ + Body should be :class:`http.client.HTTPResponse` like + (have an fp attribute which returns raw chunks) for read_chunked(). + """ + + +class IncompleteRead(HTTPError, httplib_IncompleteRead): + """ + Response length doesn't match expected Content-Length + + Subclass of :class:`http.client.IncompleteRead` to allow int value + for ``partial`` to avoid creating large objects on streamed reads. + """ + + partial: int # type: ignore[assignment] + expected: int + + def __init__(self, partial: int, expected: int) -> None: + self.partial = partial + self.expected = expected + + def __repr__(self) -> str: + return "IncompleteRead(%i bytes read, %i more expected)" % ( + self.partial, + self.expected, + ) + + +class InvalidChunkLength(HTTPError, httplib_IncompleteRead): + """Invalid chunk length in a chunked response.""" + + def __init__(self, response: HTTPResponse, length: bytes) -> None: + self.partial: int = response.tell() # type: ignore[assignment] + self.expected: int | None = response.length_remaining + self.response = response + self.length = length + + def __repr__(self) -> str: + return "InvalidChunkLength(got length %r, %i bytes read)" % ( + self.length, + self.partial, + ) + + +class InvalidHeader(HTTPError): + """The header provided was somehow invalid.""" + + +class ProxySchemeUnknown(AssertionError, URLSchemeUnknown): + """ProxyManager does not support the supplied scheme""" + + # TODO(t-8ch): Stop inheriting from AssertionError in v2.0. + + def __init__(self, scheme: str | None) -> None: + # 'localhost' is here because our URL parser parses + # localhost:8080 -> scheme=localhost, remove if we fix this. + if scheme == "localhost": + scheme = None + if scheme is None: + message = "Proxy URL had no scheme, should start with http:// or https://" + else: + message = f"Proxy URL had unsupported scheme {scheme}, should use http:// or https://" + super().__init__(message) + + +class ProxySchemeUnsupported(ValueError): + """Fetching HTTPS resources through HTTPS proxies is unsupported""" + + +class HeaderParsingError(HTTPError): + """Raised by assert_header_parsing, but we convert it to a log.warning statement.""" + + def __init__( + self, defects: list[MessageDefect], unparsed_data: bytes | str | None + ) -> None: + message = f"{defects or 'Unknown'}, unparsed data: {unparsed_data!r}" + super().__init__(message) + + +class UnrewindableBodyError(HTTPError): + """urllib3 encountered an error when trying to rewind a body""" diff --git a/.venv/lib/python3.12/site-packages/urllib3/fields.py b/.venv/lib/python3.12/site-packages/urllib3/fields.py new file mode 100644 index 0000000000000000000000000000000000000000..97c4730cff0df570e1ab47f77e6aa879ec3c36e7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/urllib3/fields.py @@ -0,0 +1,341 @@ +from __future__ import annotations + +import email.utils +import mimetypes +import typing + +_TYPE_FIELD_VALUE = typing.Union[str, bytes] +_TYPE_FIELD_VALUE_TUPLE = typing.Union[ + _TYPE_FIELD_VALUE, + tuple[str, _TYPE_FIELD_VALUE], + tuple[str, _TYPE_FIELD_VALUE, str], +] + + +def guess_content_type( + filename: str | None, default: str = "application/octet-stream" +) -> str: + """ + Guess the "Content-Type" of a file. + + :param filename: + The filename to guess the "Content-Type" of using :mod:`mimetypes`. + :param default: + If no "Content-Type" can be guessed, default to `default`. + """ + if filename: + return mimetypes.guess_type(filename)[0] or default + return default + + +def format_header_param_rfc2231(name: str, value: _TYPE_FIELD_VALUE) -> str: + """ + Helper function to format and quote a single header parameter using the + strategy defined in RFC 2231. + + Particularly useful for header parameters which might contain + non-ASCII values, like file names. This follows + `RFC 2388 Section 4.4 `_. + + :param name: + The name of the parameter, a string expected to be ASCII only. + :param value: + The value of the parameter, provided as ``bytes`` or `str``. + :returns: + An RFC-2231-formatted unicode string. + + .. deprecated:: 2.0.0 + Will be removed in urllib3 v2.1.0. This is not valid for + ``multipart/form-data`` header parameters. + """ + import warnings + + warnings.warn( + "'format_header_param_rfc2231' is deprecated and will be " + "removed in urllib3 v2.1.0. This is not valid for " + "multipart/form-data header parameters.", + DeprecationWarning, + stacklevel=2, + ) + + if isinstance(value, bytes): + value = value.decode("utf-8") + + if not any(ch in value for ch in '"\\\r\n'): + result = f'{name}="{value}"' + try: + result.encode("ascii") + except (UnicodeEncodeError, UnicodeDecodeError): + pass + else: + return result + + value = email.utils.encode_rfc2231(value, "utf-8") + value = f"{name}*={value}" + + return value + + +def format_multipart_header_param(name: str, value: _TYPE_FIELD_VALUE) -> str: + """ + Format and quote a single multipart header parameter. + + This follows the `WHATWG HTML Standard`_ as of 2021/06/10, matching + the behavior of current browser and curl versions. Values are + assumed to be UTF-8. The ``\\n``, ``\\r``, and ``"`` characters are + percent encoded. + + .. _WHATWG HTML Standard: + https://html.spec.whatwg.org/multipage/ + form-control-infrastructure.html#multipart-form-data + + :param name: + The name of the parameter, an ASCII-only ``str``. + :param value: + The value of the parameter, a ``str`` or UTF-8 encoded + ``bytes``. + :returns: + A string ``name="value"`` with the escaped value. + + .. versionchanged:: 2.0.0 + Matches the WHATWG HTML Standard as of 2021/06/10. Control + characters are no longer percent encoded. + + .. versionchanged:: 2.0.0 + Renamed from ``format_header_param_html5`` and + ``format_header_param``. The old names will be removed in + urllib3 v2.1.0. + """ + if isinstance(value, bytes): + value = value.decode("utf-8") + + # percent encode \n \r " + value = value.translate({10: "%0A", 13: "%0D", 34: "%22"}) + return f'{name}="{value}"' + + +def format_header_param_html5(name: str, value: _TYPE_FIELD_VALUE) -> str: + """ + .. deprecated:: 2.0.0 + Renamed to :func:`format_multipart_header_param`. Will be + removed in urllib3 v2.1.0. + """ + import warnings + + warnings.warn( + "'format_header_param_html5' has been renamed to " + "'format_multipart_header_param'. The old name will be " + "removed in urllib3 v2.1.0.", + DeprecationWarning, + stacklevel=2, + ) + return format_multipart_header_param(name, value) + + +def format_header_param(name: str, value: _TYPE_FIELD_VALUE) -> str: + """ + .. deprecated:: 2.0.0 + Renamed to :func:`format_multipart_header_param`. Will be + removed in urllib3 v2.1.0. + """ + import warnings + + warnings.warn( + "'format_header_param' has been renamed to " + "'format_multipart_header_param'. The old name will be " + "removed in urllib3 v2.1.0.", + DeprecationWarning, + stacklevel=2, + ) + return format_multipart_header_param(name, value) + + +class RequestField: + """ + A data container for request body parameters. + + :param name: + The name of this request field. Must be unicode. + :param data: + The data/value body. + :param filename: + An optional filename of the request field. Must be unicode. + :param headers: + An optional dict-like object of headers to initially use for the field. + + .. versionchanged:: 2.0.0 + The ``header_formatter`` parameter is deprecated and will + be removed in urllib3 v2.1.0. + """ + + def __init__( + self, + name: str, + data: _TYPE_FIELD_VALUE, + filename: str | None = None, + headers: typing.Mapping[str, str] | None = None, + header_formatter: typing.Callable[[str, _TYPE_FIELD_VALUE], str] | None = None, + ): + self._name = name + self._filename = filename + self.data = data + self.headers: dict[str, str | None] = {} + if headers: + self.headers = dict(headers) + + if header_formatter is not None: + import warnings + + warnings.warn( + "The 'header_formatter' parameter is deprecated and " + "will be removed in urllib3 v2.1.0.", + DeprecationWarning, + stacklevel=2, + ) + self.header_formatter = header_formatter + else: + self.header_formatter = format_multipart_header_param + + @classmethod + def from_tuples( + cls, + fieldname: str, + value: _TYPE_FIELD_VALUE_TUPLE, + header_formatter: typing.Callable[[str, _TYPE_FIELD_VALUE], str] | None = None, + ) -> RequestField: + """ + A :class:`~urllib3.fields.RequestField` factory from old-style tuple parameters. + + Supports constructing :class:`~urllib3.fields.RequestField` from + parameter of key/value strings AND key/filetuple. A filetuple is a + (filename, data, MIME type) tuple where the MIME type is optional. + For example:: + + 'foo': 'bar', + 'fakefile': ('foofile.txt', 'contents of foofile'), + 'realfile': ('barfile.txt', open('realfile').read()), + 'typedfile': ('bazfile.bin', open('bazfile').read(), 'image/jpeg'), + 'nonamefile': 'contents of nonamefile field', + + Field names and filenames must be unicode. + """ + filename: str | None + content_type: str | None + data: _TYPE_FIELD_VALUE + + if isinstance(value, tuple): + if len(value) == 3: + filename, data, content_type = value + else: + filename, data = value + content_type = guess_content_type(filename) + else: + filename = None + content_type = None + data = value + + request_param = cls( + fieldname, data, filename=filename, header_formatter=header_formatter + ) + request_param.make_multipart(content_type=content_type) + + return request_param + + def _render_part(self, name: str, value: _TYPE_FIELD_VALUE) -> str: + """ + Override this method to change how each multipart header + parameter is formatted. By default, this calls + :func:`format_multipart_header_param`. + + :param name: + The name of the parameter, an ASCII-only ``str``. + :param value: + The value of the parameter, a ``str`` or UTF-8 encoded + ``bytes``. + + :meta public: + """ + return self.header_formatter(name, value) + + def _render_parts( + self, + header_parts: ( + dict[str, _TYPE_FIELD_VALUE | None] + | typing.Sequence[tuple[str, _TYPE_FIELD_VALUE | None]] + ), + ) -> str: + """ + Helper function to format and quote a single header. + + Useful for single headers that are composed of multiple items. E.g., + 'Content-Disposition' fields. + + :param header_parts: + A sequence of (k, v) tuples or a :class:`dict` of (k, v) to format + as `k1="v1"; k2="v2"; ...`. + """ + iterable: typing.Iterable[tuple[str, _TYPE_FIELD_VALUE | None]] + + parts = [] + if isinstance(header_parts, dict): + iterable = header_parts.items() + else: + iterable = header_parts + + for name, value in iterable: + if value is not None: + parts.append(self._render_part(name, value)) + + return "; ".join(parts) + + def render_headers(self) -> str: + """ + Renders the headers for this request field. + """ + lines = [] + + sort_keys = ["Content-Disposition", "Content-Type", "Content-Location"] + for sort_key in sort_keys: + if self.headers.get(sort_key, False): + lines.append(f"{sort_key}: {self.headers[sort_key]}") + + for header_name, header_value in self.headers.items(): + if header_name not in sort_keys: + if header_value: + lines.append(f"{header_name}: {header_value}") + + lines.append("\r\n") + return "\r\n".join(lines) + + def make_multipart( + self, + content_disposition: str | None = None, + content_type: str | None = None, + content_location: str | None = None, + ) -> None: + """ + Makes this request field into a multipart request field. + + This method overrides "Content-Disposition", "Content-Type" and + "Content-Location" headers to the request parameter. + + :param content_disposition: + The 'Content-Disposition' of the request body. Defaults to 'form-data' + :param content_type: + The 'Content-Type' of the request body. + :param content_location: + The 'Content-Location' of the request body. + + """ + content_disposition = (content_disposition or "form-data") + "; ".join( + [ + "", + self._render_parts( + (("name", self._name), ("filename", self._filename)) + ), + ] + ) + + self.headers["Content-Disposition"] = content_disposition + self.headers["Content-Type"] = content_type + self.headers["Content-Location"] = content_location diff --git a/.venv/lib/python3.12/site-packages/urllib3/filepost.py b/.venv/lib/python3.12/site-packages/urllib3/filepost.py new file mode 100644 index 0000000000000000000000000000000000000000..14f70b05b4778f91137e4a9e7059d7514aa44d28 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/urllib3/filepost.py @@ -0,0 +1,89 @@ +from __future__ import annotations + +import binascii +import codecs +import os +import typing +from io import BytesIO + +from .fields import _TYPE_FIELD_VALUE_TUPLE, RequestField + +writer = codecs.lookup("utf-8")[3] + +_TYPE_FIELDS_SEQUENCE = typing.Sequence[ + typing.Union[tuple[str, _TYPE_FIELD_VALUE_TUPLE], RequestField] +] +_TYPE_FIELDS = typing.Union[ + _TYPE_FIELDS_SEQUENCE, + typing.Mapping[str, _TYPE_FIELD_VALUE_TUPLE], +] + + +def choose_boundary() -> str: + """ + Our embarrassingly-simple replacement for mimetools.choose_boundary. + """ + return binascii.hexlify(os.urandom(16)).decode() + + +def iter_field_objects(fields: _TYPE_FIELDS) -> typing.Iterable[RequestField]: + """ + Iterate over fields. + + Supports list of (k, v) tuples and dicts, and lists of + :class:`~urllib3.fields.RequestField`. + + """ + iterable: typing.Iterable[RequestField | tuple[str, _TYPE_FIELD_VALUE_TUPLE]] + + if isinstance(fields, typing.Mapping): + iterable = fields.items() + else: + iterable = fields + + for field in iterable: + if isinstance(field, RequestField): + yield field + else: + yield RequestField.from_tuples(*field) + + +def encode_multipart_formdata( + fields: _TYPE_FIELDS, boundary: str | None = None +) -> tuple[bytes, str]: + """ + Encode a dictionary of ``fields`` using the multipart/form-data MIME format. + + :param fields: + Dictionary of fields or list of (key, :class:`~urllib3.fields.RequestField`). + Values are processed by :func:`urllib3.fields.RequestField.from_tuples`. + + :param boundary: + If not specified, then a random boundary will be generated using + :func:`urllib3.filepost.choose_boundary`. + """ + body = BytesIO() + if boundary is None: + boundary = choose_boundary() + + for field in iter_field_objects(fields): + body.write(f"--{boundary}\r\n".encode("latin-1")) + + writer(body).write(field.render_headers()) + data = field.data + + if isinstance(data, int): + data = str(data) # Backwards compatibility + + if isinstance(data, str): + writer(body).write(data) + else: + body.write(data) + + body.write(b"\r\n") + + body.write(f"--{boundary}--\r\n".encode("latin-1")) + + content_type = f"multipart/form-data; boundary={boundary}" + + return body.getvalue(), content_type diff --git a/.venv/lib/python3.12/site-packages/urllib3/poolmanager.py b/.venv/lib/python3.12/site-packages/urllib3/poolmanager.py new file mode 100644 index 0000000000000000000000000000000000000000..5763fea808184690042cca497b3ac358f412c83f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/urllib3/poolmanager.py @@ -0,0 +1,653 @@ +from __future__ import annotations + +import functools +import logging +import typing +import warnings +from types import TracebackType +from urllib.parse import urljoin + +from ._collections import HTTPHeaderDict, RecentlyUsedContainer +from ._request_methods import RequestMethods +from .connection import ProxyConfig +from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool, port_by_scheme +from .exceptions import ( + LocationValueError, + MaxRetryError, + ProxySchemeUnknown, + URLSchemeUnknown, +) +from .response import BaseHTTPResponse +from .util.connection import _TYPE_SOCKET_OPTIONS +from .util.proxy import connection_requires_http_tunnel +from .util.retry import Retry +from .util.timeout import Timeout +from .util.url import Url, parse_url + +if typing.TYPE_CHECKING: + import ssl + + from typing_extensions import Self + +__all__ = ["PoolManager", "ProxyManager", "proxy_from_url"] + + +log = logging.getLogger(__name__) + +SSL_KEYWORDS = ( + "key_file", + "cert_file", + "cert_reqs", + "ca_certs", + "ca_cert_data", + "ssl_version", + "ssl_minimum_version", + "ssl_maximum_version", + "ca_cert_dir", + "ssl_context", + "key_password", + "server_hostname", +) +# Default value for `blocksize` - a new parameter introduced to +# http.client.HTTPConnection & http.client.HTTPSConnection in Python 3.7 +_DEFAULT_BLOCKSIZE = 16384 + + +class PoolKey(typing.NamedTuple): + """ + All known keyword arguments that could be provided to the pool manager, its + pools, or the underlying connections. + + All custom key schemes should include the fields in this key at a minimum. + """ + + key_scheme: str + key_host: str + key_port: int | None + key_timeout: Timeout | float | int | None + key_retries: Retry | bool | int | None + key_block: bool | None + key_source_address: tuple[str, int] | None + key_key_file: str | None + key_key_password: str | None + key_cert_file: str | None + key_cert_reqs: str | None + key_ca_certs: str | None + key_ca_cert_data: str | bytes | None + key_ssl_version: int | str | None + key_ssl_minimum_version: ssl.TLSVersion | None + key_ssl_maximum_version: ssl.TLSVersion | None + key_ca_cert_dir: str | None + key_ssl_context: ssl.SSLContext | None + key_maxsize: int | None + key_headers: frozenset[tuple[str, str]] | None + key__proxy: Url | None + key__proxy_headers: frozenset[tuple[str, str]] | None + key__proxy_config: ProxyConfig | None + key_socket_options: _TYPE_SOCKET_OPTIONS | None + key__socks_options: frozenset[tuple[str, str]] | None + key_assert_hostname: bool | str | None + key_assert_fingerprint: str | None + key_server_hostname: str | None + key_blocksize: int | None + + +def _default_key_normalizer( + key_class: type[PoolKey], request_context: dict[str, typing.Any] +) -> PoolKey: + """ + Create a pool key out of a request context dictionary. + + According to RFC 3986, both the scheme and host are case-insensitive. + Therefore, this function normalizes both before constructing the pool + key for an HTTPS request. If you wish to change this behaviour, provide + alternate callables to ``key_fn_by_scheme``. + + :param key_class: + The class to use when constructing the key. This should be a namedtuple + with the ``scheme`` and ``host`` keys at a minimum. + :type key_class: namedtuple + :param request_context: + A dictionary-like object that contain the context for a request. + :type request_context: dict + + :return: A namedtuple that can be used as a connection pool key. + :rtype: PoolKey + """ + # Since we mutate the dictionary, make a copy first + context = request_context.copy() + context["scheme"] = context["scheme"].lower() + context["host"] = context["host"].lower() + + # These are both dictionaries and need to be transformed into frozensets + for key in ("headers", "_proxy_headers", "_socks_options"): + if key in context and context[key] is not None: + context[key] = frozenset(context[key].items()) + + # The socket_options key may be a list and needs to be transformed into a + # tuple. + socket_opts = context.get("socket_options") + if socket_opts is not None: + context["socket_options"] = tuple(socket_opts) + + # Map the kwargs to the names in the namedtuple - this is necessary since + # namedtuples can't have fields starting with '_'. + for key in list(context.keys()): + context["key_" + key] = context.pop(key) + + # Default to ``None`` for keys missing from the context + for field in key_class._fields: + if field not in context: + context[field] = None + + # Default key_blocksize to _DEFAULT_BLOCKSIZE if missing from the context + if context.get("key_blocksize") is None: + context["key_blocksize"] = _DEFAULT_BLOCKSIZE + + return key_class(**context) + + +#: A dictionary that maps a scheme to a callable that creates a pool key. +#: This can be used to alter the way pool keys are constructed, if desired. +#: Each PoolManager makes a copy of this dictionary so they can be configured +#: globally here, or individually on the instance. +key_fn_by_scheme = { + "http": functools.partial(_default_key_normalizer, PoolKey), + "https": functools.partial(_default_key_normalizer, PoolKey), +} + +pool_classes_by_scheme = {"http": HTTPConnectionPool, "https": HTTPSConnectionPool} + + +class PoolManager(RequestMethods): + """ + Allows for arbitrary requests while transparently keeping track of + necessary connection pools for you. + + :param num_pools: + Number of connection pools to cache before discarding the least + recently used pool. + + :param headers: + Headers to include with all requests, unless other headers are given + explicitly. + + :param \\**connection_pool_kw: + Additional parameters are used to create fresh + :class:`urllib3.connectionpool.ConnectionPool` instances. + + Example: + + .. code-block:: python + + import urllib3 + + http = urllib3.PoolManager(num_pools=2) + + resp1 = http.request("GET", "https://google.com/") + resp2 = http.request("GET", "https://google.com/mail") + resp3 = http.request("GET", "https://yahoo.com/") + + print(len(http.pools)) + # 2 + + """ + + proxy: Url | None = None + proxy_config: ProxyConfig | None = None + + def __init__( + self, + num_pools: int = 10, + headers: typing.Mapping[str, str] | None = None, + **connection_pool_kw: typing.Any, + ) -> None: + super().__init__(headers) + if "retries" in connection_pool_kw: + retries = connection_pool_kw["retries"] + if not isinstance(retries, Retry): + # When Retry is initialized, raise_on_redirect is based + # on a redirect boolean value. + # But requests made via a pool manager always set + # redirect to False, and raise_on_redirect always ends + # up being False consequently. + # Here we fix the issue by setting raise_on_redirect to + # a value needed by the pool manager without considering + # the redirect boolean. + raise_on_redirect = retries is not False + retries = Retry.from_int(retries, redirect=False) + retries.raise_on_redirect = raise_on_redirect + connection_pool_kw = connection_pool_kw.copy() + connection_pool_kw["retries"] = retries + self.connection_pool_kw = connection_pool_kw + + self.pools: RecentlyUsedContainer[PoolKey, HTTPConnectionPool] + self.pools = RecentlyUsedContainer(num_pools) + + # Locally set the pool classes and keys so other PoolManagers can + # override them. + self.pool_classes_by_scheme = pool_classes_by_scheme + self.key_fn_by_scheme = key_fn_by_scheme.copy() + + def __enter__(self) -> Self: + return self + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> typing.Literal[False]: + self.clear() + # Return False to re-raise any potential exceptions + return False + + def _new_pool( + self, + scheme: str, + host: str, + port: int, + request_context: dict[str, typing.Any] | None = None, + ) -> HTTPConnectionPool: + """ + Create a new :class:`urllib3.connectionpool.ConnectionPool` based on host, port, scheme, and + any additional pool keyword arguments. + + If ``request_context`` is provided, it is provided as keyword arguments + to the pool class used. This method is used to actually create the + connection pools handed out by :meth:`connection_from_url` and + companion methods. It is intended to be overridden for customization. + """ + pool_cls: type[HTTPConnectionPool] = self.pool_classes_by_scheme[scheme] + if request_context is None: + request_context = self.connection_pool_kw.copy() + + # Default blocksize to _DEFAULT_BLOCKSIZE if missing or explicitly + # set to 'None' in the request_context. + if request_context.get("blocksize") is None: + request_context["blocksize"] = _DEFAULT_BLOCKSIZE + + # Although the context has everything necessary to create the pool, + # this function has historically only used the scheme, host, and port + # in the positional args. When an API change is acceptable these can + # be removed. + for key in ("scheme", "host", "port"): + request_context.pop(key, None) + + if scheme == "http": + for kw in SSL_KEYWORDS: + request_context.pop(kw, None) + + return pool_cls(host, port, **request_context) + + def clear(self) -> None: + """ + Empty our store of pools and direct them all to close. + + This will not affect in-flight connections, but they will not be + re-used after completion. + """ + self.pools.clear() + + def connection_from_host( + self, + host: str | None, + port: int | None = None, + scheme: str | None = "http", + pool_kwargs: dict[str, typing.Any] | None = None, + ) -> HTTPConnectionPool: + """ + Get a :class:`urllib3.connectionpool.ConnectionPool` based on the host, port, and scheme. + + If ``port`` isn't given, it will be derived from the ``scheme`` using + ``urllib3.connectionpool.port_by_scheme``. If ``pool_kwargs`` is + provided, it is merged with the instance's ``connection_pool_kw`` + variable and used to create the new connection pool, if one is + needed. + """ + + if not host: + raise LocationValueError("No host specified.") + + request_context = self._merge_pool_kwargs(pool_kwargs) + request_context["scheme"] = scheme or "http" + if not port: + port = port_by_scheme.get(request_context["scheme"].lower(), 80) + request_context["port"] = port + request_context["host"] = host + + return self.connection_from_context(request_context) + + def connection_from_context( + self, request_context: dict[str, typing.Any] + ) -> HTTPConnectionPool: + """ + Get a :class:`urllib3.connectionpool.ConnectionPool` based on the request context. + + ``request_context`` must at least contain the ``scheme`` key and its + value must be a key in ``key_fn_by_scheme`` instance variable. + """ + if "strict" in request_context: + warnings.warn( + "The 'strict' parameter is no longer needed on Python 3+. " + "This will raise an error in urllib3 v2.1.0.", + DeprecationWarning, + ) + request_context.pop("strict") + + scheme = request_context["scheme"].lower() + pool_key_constructor = self.key_fn_by_scheme.get(scheme) + if not pool_key_constructor: + raise URLSchemeUnknown(scheme) + pool_key = pool_key_constructor(request_context) + + return self.connection_from_pool_key(pool_key, request_context=request_context) + + def connection_from_pool_key( + self, pool_key: PoolKey, request_context: dict[str, typing.Any] + ) -> HTTPConnectionPool: + """ + Get a :class:`urllib3.connectionpool.ConnectionPool` based on the provided pool key. + + ``pool_key`` should be a namedtuple that only contains immutable + objects. At a minimum it must have the ``scheme``, ``host``, and + ``port`` fields. + """ + with self.pools.lock: + # If the scheme, host, or port doesn't match existing open + # connections, open a new ConnectionPool. + pool = self.pools.get(pool_key) + if pool: + return pool + + # Make a fresh ConnectionPool of the desired type + scheme = request_context["scheme"] + host = request_context["host"] + port = request_context["port"] + pool = self._new_pool(scheme, host, port, request_context=request_context) + self.pools[pool_key] = pool + + return pool + + def connection_from_url( + self, url: str, pool_kwargs: dict[str, typing.Any] | None = None + ) -> HTTPConnectionPool: + """ + Similar to :func:`urllib3.connectionpool.connection_from_url`. + + If ``pool_kwargs`` is not provided and a new pool needs to be + constructed, ``self.connection_pool_kw`` is used to initialize + the :class:`urllib3.connectionpool.ConnectionPool`. If ``pool_kwargs`` + is provided, it is used instead. Note that if a new pool does not + need to be created for the request, the provided ``pool_kwargs`` are + not used. + """ + u = parse_url(url) + return self.connection_from_host( + u.host, port=u.port, scheme=u.scheme, pool_kwargs=pool_kwargs + ) + + def _merge_pool_kwargs( + self, override: dict[str, typing.Any] | None + ) -> dict[str, typing.Any]: + """ + Merge a dictionary of override values for self.connection_pool_kw. + + This does not modify self.connection_pool_kw and returns a new dict. + Any keys in the override dictionary with a value of ``None`` are + removed from the merged dictionary. + """ + base_pool_kwargs = self.connection_pool_kw.copy() + if override: + for key, value in override.items(): + if value is None: + try: + del base_pool_kwargs[key] + except KeyError: + pass + else: + base_pool_kwargs[key] = value + return base_pool_kwargs + + def _proxy_requires_url_absolute_form(self, parsed_url: Url) -> bool: + """ + Indicates if the proxy requires the complete destination URL in the + request. Normally this is only needed when not using an HTTP CONNECT + tunnel. + """ + if self.proxy is None: + return False + + return not connection_requires_http_tunnel( + self.proxy, self.proxy_config, parsed_url.scheme + ) + + def urlopen( # type: ignore[override] + self, method: str, url: str, redirect: bool = True, **kw: typing.Any + ) -> BaseHTTPResponse: + """ + Same as :meth:`urllib3.HTTPConnectionPool.urlopen` + with custom cross-host redirect logic and only sends the request-uri + portion of the ``url``. + + The given ``url`` parameter must be absolute, such that an appropriate + :class:`urllib3.connectionpool.ConnectionPool` can be chosen for it. + """ + u = parse_url(url) + + if u.scheme is None: + warnings.warn( + "URLs without a scheme (ie 'https://') are deprecated and will raise an error " + "in a future version of urllib3. To avoid this DeprecationWarning ensure all URLs " + "start with 'https://' or 'http://'. Read more in this issue: " + "https://github.com/urllib3/urllib3/issues/2920", + category=DeprecationWarning, + stacklevel=2, + ) + + conn = self.connection_from_host(u.host, port=u.port, scheme=u.scheme) + + kw["assert_same_host"] = False + kw["redirect"] = False + + if "headers" not in kw: + kw["headers"] = self.headers + + if self._proxy_requires_url_absolute_form(u): + response = conn.urlopen(method, url, **kw) + else: + response = conn.urlopen(method, u.request_uri, **kw) + + redirect_location = redirect and response.get_redirect_location() + if not redirect_location: + return response + + # Support relative URLs for redirecting. + redirect_location = urljoin(url, redirect_location) + + if response.status == 303: + # Change the method according to RFC 9110, Section 15.4.4. + method = "GET" + # And lose the body not to transfer anything sensitive. + kw["body"] = None + kw["headers"] = HTTPHeaderDict(kw["headers"])._prepare_for_method_change() + + retries = kw.get("retries", response.retries) + if not isinstance(retries, Retry): + retries = Retry.from_int(retries, redirect=redirect) + + # Strip headers marked as unsafe to forward to the redirected location. + # Check remove_headers_on_redirect to avoid a potential network call within + # conn.is_same_host() which may use socket.gethostbyname() in the future. + if retries.remove_headers_on_redirect and not conn.is_same_host( + redirect_location + ): + new_headers = kw["headers"].copy() + for header in kw["headers"]: + if header.lower() in retries.remove_headers_on_redirect: + new_headers.pop(header, None) + kw["headers"] = new_headers + + try: + retries = retries.increment(method, url, response=response, _pool=conn) + except MaxRetryError: + if retries.raise_on_redirect: + response.drain_conn() + raise + return response + + kw["retries"] = retries + kw["redirect"] = redirect + + log.info("Redirecting %s -> %s", url, redirect_location) + + response.drain_conn() + return self.urlopen(method, redirect_location, **kw) + + +class ProxyManager(PoolManager): + """ + Behaves just like :class:`PoolManager`, but sends all requests through + the defined proxy, using the CONNECT method for HTTPS URLs. + + :param proxy_url: + The URL of the proxy to be used. + + :param proxy_headers: + A dictionary containing headers that will be sent to the proxy. In case + of HTTP they are being sent with each request, while in the + HTTPS/CONNECT case they are sent only once. Could be used for proxy + authentication. + + :param proxy_ssl_context: + The proxy SSL context is used to establish the TLS connection to the + proxy when using HTTPS proxies. + + :param use_forwarding_for_https: + (Defaults to False) If set to True will forward requests to the HTTPS + proxy to be made on behalf of the client instead of creating a TLS + tunnel via the CONNECT method. **Enabling this flag means that request + and response headers and content will be visible from the HTTPS proxy** + whereas tunneling keeps request and response headers and content + private. IP address, target hostname, SNI, and port are always visible + to an HTTPS proxy even when this flag is disabled. + + :param proxy_assert_hostname: + The hostname of the certificate to verify against. + + :param proxy_assert_fingerprint: + The fingerprint of the certificate to verify against. + + Example: + + .. code-block:: python + + import urllib3 + + proxy = urllib3.ProxyManager("https://localhost:3128/") + + resp1 = proxy.request("GET", "https://google.com/") + resp2 = proxy.request("GET", "https://httpbin.org/") + + print(len(proxy.pools)) + # 1 + + resp3 = proxy.request("GET", "https://httpbin.org/") + resp4 = proxy.request("GET", "https://twitter.com/") + + print(len(proxy.pools)) + # 3 + + """ + + def __init__( + self, + proxy_url: str, + num_pools: int = 10, + headers: typing.Mapping[str, str] | None = None, + proxy_headers: typing.Mapping[str, str] | None = None, + proxy_ssl_context: ssl.SSLContext | None = None, + use_forwarding_for_https: bool = False, + proxy_assert_hostname: None | str | typing.Literal[False] = None, + proxy_assert_fingerprint: str | None = None, + **connection_pool_kw: typing.Any, + ) -> None: + if isinstance(proxy_url, HTTPConnectionPool): + str_proxy_url = f"{proxy_url.scheme}://{proxy_url.host}:{proxy_url.port}" + else: + str_proxy_url = proxy_url + proxy = parse_url(str_proxy_url) + + if proxy.scheme not in ("http", "https"): + raise ProxySchemeUnknown(proxy.scheme) + + if not proxy.port: + port = port_by_scheme.get(proxy.scheme, 80) + proxy = proxy._replace(port=port) + + self.proxy = proxy + self.proxy_headers = proxy_headers or {} + self.proxy_ssl_context = proxy_ssl_context + self.proxy_config = ProxyConfig( + proxy_ssl_context, + use_forwarding_for_https, + proxy_assert_hostname, + proxy_assert_fingerprint, + ) + + connection_pool_kw["_proxy"] = self.proxy + connection_pool_kw["_proxy_headers"] = self.proxy_headers + connection_pool_kw["_proxy_config"] = self.proxy_config + + super().__init__(num_pools, headers, **connection_pool_kw) + + def connection_from_host( + self, + host: str | None, + port: int | None = None, + scheme: str | None = "http", + pool_kwargs: dict[str, typing.Any] | None = None, + ) -> HTTPConnectionPool: + if scheme == "https": + return super().connection_from_host( + host, port, scheme, pool_kwargs=pool_kwargs + ) + + return super().connection_from_host( + self.proxy.host, self.proxy.port, self.proxy.scheme, pool_kwargs=pool_kwargs # type: ignore[union-attr] + ) + + def _set_proxy_headers( + self, url: str, headers: typing.Mapping[str, str] | None = None + ) -> typing.Mapping[str, str]: + """ + Sets headers needed by proxies: specifically, the Accept and Host + headers. Only sets headers not provided by the user. + """ + headers_ = {"Accept": "*/*"} + + netloc = parse_url(url).netloc + if netloc: + headers_["Host"] = netloc + + if headers: + headers_.update(headers) + return headers_ + + def urlopen( # type: ignore[override] + self, method: str, url: str, redirect: bool = True, **kw: typing.Any + ) -> BaseHTTPResponse: + "Same as HTTP(S)ConnectionPool.urlopen, ``url`` must be absolute." + u = parse_url(url) + if not connection_requires_http_tunnel(self.proxy, self.proxy_config, u.scheme): + # For connections using HTTP CONNECT, httplib sets the necessary + # headers on the CONNECT to the proxy. If we're not using CONNECT, + # we'll definitely need to set 'Host' at the very least. + headers = kw.get("headers", self.headers) + kw["headers"] = self._set_proxy_headers(url, headers) + + return super().urlopen(method, url, redirect=redirect, **kw) + + +def proxy_from_url(url: str, **kw: typing.Any) -> ProxyManager: + return ProxyManager(proxy_url=url, **kw) diff --git a/.venv/lib/python3.12/site-packages/urllib3/py.typed b/.venv/lib/python3.12/site-packages/urllib3/py.typed new file mode 100644 index 0000000000000000000000000000000000000000..5f3ea3d919363f08ab03edbc85b6099bc4df5647 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/urllib3/py.typed @@ -0,0 +1,2 @@ +# Instruct type checkers to look for inline type annotations in this package. +# See PEP 561. diff --git a/.venv/lib/python3.12/site-packages/urllib3/response.py b/.venv/lib/python3.12/site-packages/urllib3/response.py new file mode 100644 index 0000000000000000000000000000000000000000..5632dab3b2f93df91bf132384c3820a6b5518f43 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/urllib3/response.py @@ -0,0 +1,1307 @@ +from __future__ import annotations + +import collections +import io +import json as _json +import logging +import re +import socket +import sys +import typing +import warnings +import zlib +from contextlib import contextmanager +from http.client import HTTPMessage as _HttplibHTTPMessage +from http.client import HTTPResponse as _HttplibHTTPResponse +from socket import timeout as SocketTimeout + +if typing.TYPE_CHECKING: + from ._base_connection import BaseHTTPConnection + +try: + try: + import brotlicffi as brotli # type: ignore[import-not-found] + except ImportError: + import brotli # type: ignore[import-not-found] +except ImportError: + brotli = None + +from . import util +from ._base_connection import _TYPE_BODY +from ._collections import HTTPHeaderDict +from .connection import BaseSSLError, HTTPConnection, HTTPException +from .exceptions import ( + BodyNotHttplibCompatible, + DecodeError, + HTTPError, + IncompleteRead, + InvalidChunkLength, + InvalidHeader, + ProtocolError, + ReadTimeoutError, + ResponseNotChunked, + SSLError, +) +from .util.response import is_fp_closed, is_response_to_head +from .util.retry import Retry + +if typing.TYPE_CHECKING: + from .connectionpool import HTTPConnectionPool + +log = logging.getLogger(__name__) + + +class ContentDecoder: + def decompress(self, data: bytes) -> bytes: + raise NotImplementedError() + + def flush(self) -> bytes: + raise NotImplementedError() + + +class DeflateDecoder(ContentDecoder): + def __init__(self) -> None: + self._first_try = True + self._data = b"" + self._obj = zlib.decompressobj() + + def decompress(self, data: bytes) -> bytes: + if not data: + return data + + if not self._first_try: + return self._obj.decompress(data) + + self._data += data + try: + decompressed = self._obj.decompress(data) + if decompressed: + self._first_try = False + self._data = None # type: ignore[assignment] + return decompressed + except zlib.error: + self._first_try = False + self._obj = zlib.decompressobj(-zlib.MAX_WBITS) + try: + return self.decompress(self._data) + finally: + self._data = None # type: ignore[assignment] + + def flush(self) -> bytes: + return self._obj.flush() + + +class GzipDecoderState: + FIRST_MEMBER = 0 + OTHER_MEMBERS = 1 + SWALLOW_DATA = 2 + + +class GzipDecoder(ContentDecoder): + def __init__(self) -> None: + self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS) + self._state = GzipDecoderState.FIRST_MEMBER + + def decompress(self, data: bytes) -> bytes: + ret = bytearray() + if self._state == GzipDecoderState.SWALLOW_DATA or not data: + return bytes(ret) + while True: + try: + ret += self._obj.decompress(data) + except zlib.error: + previous_state = self._state + # Ignore data after the first error + self._state = GzipDecoderState.SWALLOW_DATA + if previous_state == GzipDecoderState.OTHER_MEMBERS: + # Allow trailing garbage acceptable in other gzip clients + return bytes(ret) + raise + data = self._obj.unused_data + if not data: + return bytes(ret) + self._state = GzipDecoderState.OTHER_MEMBERS + self._obj = zlib.decompressobj(16 + zlib.MAX_WBITS) + + def flush(self) -> bytes: + return self._obj.flush() + + +if brotli is not None: + + class BrotliDecoder(ContentDecoder): + # Supports both 'brotlipy' and 'Brotli' packages + # since they share an import name. The top branches + # are for 'brotlipy' and bottom branches for 'Brotli' + def __init__(self) -> None: + self._obj = brotli.Decompressor() + if hasattr(self._obj, "decompress"): + setattr(self, "decompress", self._obj.decompress) + else: + setattr(self, "decompress", self._obj.process) + + def flush(self) -> bytes: + if hasattr(self._obj, "flush"): + return self._obj.flush() # type: ignore[no-any-return] + return b"" + + +try: + # Python 3.14+ + from compression import zstd # type: ignore[import-not-found] # noqa: F401 + + HAS_ZSTD = True + + class ZstdDecoder(ContentDecoder): + def __init__(self) -> None: + self._obj = zstd.ZstdDecompressor() + + def decompress(self, data: bytes) -> bytes: + if not data: + return b"" + data_parts = [self._obj.decompress(data)] + while self._obj.eof and self._obj.unused_data: + unused_data = self._obj.unused_data + self._obj = zstd.ZstdDecompressor() + data_parts.append(self._obj.decompress(unused_data)) + return b"".join(data_parts) + + def flush(self) -> bytes: + if not self._obj.eof: + raise DecodeError("Zstandard data is incomplete") + return b"" + +except ImportError: + try: + # Python 3.13 and earlier require the 'zstandard' module. + import zstandard as zstd + + # The package 'zstandard' added the 'eof' property starting + # in v0.18.0 which we require to ensure a complete and + # valid zstd stream was fed into the ZstdDecoder. + # See: https://github.com/urllib3/urllib3/pull/2624 + _zstd_version = tuple( + map(int, re.search(r"^([0-9]+)\.([0-9]+)", zstd.__version__).groups()) # type: ignore[union-attr] + ) + if _zstd_version < (0, 18): # Defensive: + raise ImportError("zstandard module doesn't have eof") + except (AttributeError, ImportError, ValueError): # Defensive: + HAS_ZSTD = False + else: + HAS_ZSTD = True + + class ZstdDecoder(ContentDecoder): # type: ignore[no-redef] + def __init__(self) -> None: + self._obj = zstd.ZstdDecompressor().decompressobj() + + def decompress(self, data: bytes) -> bytes: + if not data: + return b"" + data_parts = [self._obj.decompress(data)] + while self._obj.eof and self._obj.unused_data: + unused_data = self._obj.unused_data + self._obj = zstd.ZstdDecompressor().decompressobj() + data_parts.append(self._obj.decompress(unused_data)) + return b"".join(data_parts) + + def flush(self) -> bytes: + ret = self._obj.flush() # note: this is a no-op + if not self._obj.eof: + raise DecodeError("Zstandard data is incomplete") + return ret # type: ignore[no-any-return] + + +class MultiDecoder(ContentDecoder): + """ + From RFC7231: + If one or more encodings have been applied to a representation, the + sender that applied the encodings MUST generate a Content-Encoding + header field that lists the content codings in the order in which + they were applied. + """ + + def __init__(self, modes: str) -> None: + self._decoders = [_get_decoder(m.strip()) for m in modes.split(",")] + + def flush(self) -> bytes: + return self._decoders[0].flush() + + def decompress(self, data: bytes) -> bytes: + for d in reversed(self._decoders): + data = d.decompress(data) + return data + + +def _get_decoder(mode: str) -> ContentDecoder: + if "," in mode: + return MultiDecoder(mode) + + # According to RFC 9110 section 8.4.1.3, recipients should + # consider x-gzip equivalent to gzip + if mode in ("gzip", "x-gzip"): + return GzipDecoder() + + if brotli is not None and mode == "br": + return BrotliDecoder() + + if HAS_ZSTD and mode == "zstd": + return ZstdDecoder() + + return DeflateDecoder() + + +class BytesQueueBuffer: + """Memory-efficient bytes buffer + + To return decoded data in read() and still follow the BufferedIOBase API, we need a + buffer to always return the correct amount of bytes. + + This buffer should be filled using calls to put() + + Our maximum memory usage is determined by the sum of the size of: + + * self.buffer, which contains the full data + * the largest chunk that we will copy in get() + + The worst case scenario is a single chunk, in which case we'll make a full copy of + the data inside get(). + """ + + def __init__(self) -> None: + self.buffer: typing.Deque[bytes] = collections.deque() + self._size: int = 0 + + def __len__(self) -> int: + return self._size + + def put(self, data: bytes) -> None: + self.buffer.append(data) + self._size += len(data) + + def get(self, n: int) -> bytes: + if n == 0: + return b"" + elif not self.buffer: + raise RuntimeError("buffer is empty") + elif n < 0: + raise ValueError("n should be > 0") + + fetched = 0 + ret = io.BytesIO() + while fetched < n: + remaining = n - fetched + chunk = self.buffer.popleft() + chunk_length = len(chunk) + if remaining < chunk_length: + left_chunk, right_chunk = chunk[:remaining], chunk[remaining:] + ret.write(left_chunk) + self.buffer.appendleft(right_chunk) + self._size -= remaining + break + else: + ret.write(chunk) + self._size -= chunk_length + fetched += chunk_length + + if not self.buffer: + break + + return ret.getvalue() + + def get_all(self) -> bytes: + buffer = self.buffer + if not buffer: + assert self._size == 0 + return b"" + if len(buffer) == 1: + result = buffer.pop() + else: + ret = io.BytesIO() + ret.writelines(buffer.popleft() for _ in range(len(buffer))) + result = ret.getvalue() + self._size = 0 + return result + + +class BaseHTTPResponse(io.IOBase): + CONTENT_DECODERS = ["gzip", "x-gzip", "deflate"] + if brotli is not None: + CONTENT_DECODERS += ["br"] + if HAS_ZSTD: + CONTENT_DECODERS += ["zstd"] + REDIRECT_STATUSES = [301, 302, 303, 307, 308] + + DECODER_ERROR_CLASSES: tuple[type[Exception], ...] = (IOError, zlib.error) + if brotli is not None: + DECODER_ERROR_CLASSES += (brotli.error,) + + if HAS_ZSTD: + DECODER_ERROR_CLASSES += (zstd.ZstdError,) + + def __init__( + self, + *, + headers: typing.Mapping[str, str] | typing.Mapping[bytes, bytes] | None = None, + status: int, + version: int, + version_string: str, + reason: str | None, + decode_content: bool, + request_url: str | None, + retries: Retry | None = None, + ) -> None: + if isinstance(headers, HTTPHeaderDict): + self.headers = headers + else: + self.headers = HTTPHeaderDict(headers) # type: ignore[arg-type] + self.status = status + self.version = version + self.version_string = version_string + self.reason = reason + self.decode_content = decode_content + self._has_decoded_content = False + self._request_url: str | None = request_url + self.retries = retries + + self.chunked = False + tr_enc = self.headers.get("transfer-encoding", "").lower() + # Don't incur the penalty of creating a list and then discarding it + encodings = (enc.strip() for enc in tr_enc.split(",")) + if "chunked" in encodings: + self.chunked = True + + self._decoder: ContentDecoder | None = None + self.length_remaining: int | None + + def get_redirect_location(self) -> str | None | typing.Literal[False]: + """ + Should we redirect and where to? + + :returns: Truthy redirect location string if we got a redirect status + code and valid location. ``None`` if redirect status and no + location. ``False`` if not a redirect status code. + """ + if self.status in self.REDIRECT_STATUSES: + return self.headers.get("location") + return False + + @property + def data(self) -> bytes: + raise NotImplementedError() + + def json(self) -> typing.Any: + """ + Deserializes the body of the HTTP response as a Python object. + + The body of the HTTP response must be encoded using UTF-8, as per + `RFC 8529 Section 8.1 `_. + + To use a custom JSON decoder pass the result of :attr:`HTTPResponse.data` to + your custom decoder instead. + + If the body of the HTTP response is not decodable to UTF-8, a + `UnicodeDecodeError` will be raised. If the body of the HTTP response is not a + valid JSON document, a `json.JSONDecodeError` will be raised. + + Read more :ref:`here `. + + :returns: The body of the HTTP response as a Python object. + """ + data = self.data.decode("utf-8") + return _json.loads(data) + + @property + def url(self) -> str | None: + raise NotImplementedError() + + @url.setter + def url(self, url: str | None) -> None: + raise NotImplementedError() + + @property + def connection(self) -> BaseHTTPConnection | None: + raise NotImplementedError() + + @property + def retries(self) -> Retry | None: + return self._retries + + @retries.setter + def retries(self, retries: Retry | None) -> None: + # Override the request_url if retries has a redirect location. + if retries is not None and retries.history: + self.url = retries.history[-1].redirect_location + self._retries = retries + + def stream( + self, amt: int | None = 2**16, decode_content: bool | None = None + ) -> typing.Iterator[bytes]: + raise NotImplementedError() + + def read( + self, + amt: int | None = None, + decode_content: bool | None = None, + cache_content: bool = False, + ) -> bytes: + raise NotImplementedError() + + def read1( + self, + amt: int | None = None, + decode_content: bool | None = None, + ) -> bytes: + raise NotImplementedError() + + def read_chunked( + self, + amt: int | None = None, + decode_content: bool | None = None, + ) -> typing.Iterator[bytes]: + raise NotImplementedError() + + def release_conn(self) -> None: + raise NotImplementedError() + + def drain_conn(self) -> None: + raise NotImplementedError() + + def shutdown(self) -> None: + raise NotImplementedError() + + def close(self) -> None: + raise NotImplementedError() + + def _init_decoder(self) -> None: + """ + Set-up the _decoder attribute if necessary. + """ + # Note: content-encoding value should be case-insensitive, per RFC 7230 + # Section 3.2 + content_encoding = self.headers.get("content-encoding", "").lower() + if self._decoder is None: + if content_encoding in self.CONTENT_DECODERS: + self._decoder = _get_decoder(content_encoding) + elif "," in content_encoding: + encodings = [ + e.strip() + for e in content_encoding.split(",") + if e.strip() in self.CONTENT_DECODERS + ] + if encodings: + self._decoder = _get_decoder(content_encoding) + + def _decode( + self, data: bytes, decode_content: bool | None, flush_decoder: bool + ) -> bytes: + """ + Decode the data passed in and potentially flush the decoder. + """ + if not decode_content: + if self._has_decoded_content: + raise RuntimeError( + "Calling read(decode_content=False) is not supported after " + "read(decode_content=True) was called." + ) + return data + + try: + if self._decoder: + data = self._decoder.decompress(data) + self._has_decoded_content = True + except self.DECODER_ERROR_CLASSES as e: + content_encoding = self.headers.get("content-encoding", "").lower() + raise DecodeError( + "Received response with content-encoding: %s, but " + "failed to decode it." % content_encoding, + e, + ) from e + if flush_decoder: + data += self._flush_decoder() + + return data + + def _flush_decoder(self) -> bytes: + """ + Flushes the decoder. Should only be called if the decoder is actually + being used. + """ + if self._decoder: + return self._decoder.decompress(b"") + self._decoder.flush() + return b"" + + # Compatibility methods for `io` module + def readinto(self, b: bytearray) -> int: + temp = self.read(len(b)) + if len(temp) == 0: + return 0 + else: + b[: len(temp)] = temp + return len(temp) + + # Compatibility methods for http.client.HTTPResponse + def getheaders(self) -> HTTPHeaderDict: + warnings.warn( + "HTTPResponse.getheaders() is deprecated and will be removed " + "in urllib3 v2.6.0. Instead access HTTPResponse.headers directly.", + category=DeprecationWarning, + stacklevel=2, + ) + return self.headers + + def getheader(self, name: str, default: str | None = None) -> str | None: + warnings.warn( + "HTTPResponse.getheader() is deprecated and will be removed " + "in urllib3 v2.6.0. Instead use HTTPResponse.headers.get(name, default).", + category=DeprecationWarning, + stacklevel=2, + ) + return self.headers.get(name, default) + + # Compatibility method for http.cookiejar + def info(self) -> HTTPHeaderDict: + return self.headers + + def geturl(self) -> str | None: + return self.url + + +class HTTPResponse(BaseHTTPResponse): + """ + HTTP Response container. + + Backwards-compatible with :class:`http.client.HTTPResponse` but the response ``body`` is + loaded and decoded on-demand when the ``data`` property is accessed. This + class is also compatible with the Python standard library's :mod:`io` + module, and can hence be treated as a readable object in the context of that + framework. + + Extra parameters for behaviour not present in :class:`http.client.HTTPResponse`: + + :param preload_content: + If True, the response's body will be preloaded during construction. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param original_response: + When this HTTPResponse wrapper is generated from an :class:`http.client.HTTPResponse` + object, it's convenient to include the original for debug purposes. It's + otherwise unused. + + :param retries: + The retries contains the last :class:`~urllib3.util.retry.Retry` that + was used during the request. + + :param enforce_content_length: + Enforce content length checking. Body returned by server must match + value of Content-Length header, if present. Otherwise, raise error. + """ + + def __init__( + self, + body: _TYPE_BODY = "", + headers: typing.Mapping[str, str] | typing.Mapping[bytes, bytes] | None = None, + status: int = 0, + version: int = 0, + version_string: str = "HTTP/?", + reason: str | None = None, + preload_content: bool = True, + decode_content: bool = True, + original_response: _HttplibHTTPResponse | None = None, + pool: HTTPConnectionPool | None = None, + connection: HTTPConnection | None = None, + msg: _HttplibHTTPMessage | None = None, + retries: Retry | None = None, + enforce_content_length: bool = True, + request_method: str | None = None, + request_url: str | None = None, + auto_close: bool = True, + sock_shutdown: typing.Callable[[int], None] | None = None, + ) -> None: + super().__init__( + headers=headers, + status=status, + version=version, + version_string=version_string, + reason=reason, + decode_content=decode_content, + request_url=request_url, + retries=retries, + ) + + self.enforce_content_length = enforce_content_length + self.auto_close = auto_close + + self._body = None + self._fp: _HttplibHTTPResponse | None = None + self._original_response = original_response + self._fp_bytes_read = 0 + self.msg = msg + + if body and isinstance(body, (str, bytes)): + self._body = body + + self._pool = pool + self._connection = connection + + if hasattr(body, "read"): + self._fp = body # type: ignore[assignment] + self._sock_shutdown = sock_shutdown + + # Are we using the chunked-style of transfer encoding? + self.chunk_left: int | None = None + + # Determine length of response + self.length_remaining = self._init_length(request_method) + + # Used to return the correct amount of bytes for partial read()s + self._decoded_buffer = BytesQueueBuffer() + + # If requested, preload the body. + if preload_content and not self._body: + self._body = self.read(decode_content=decode_content) + + def release_conn(self) -> None: + if not self._pool or not self._connection: + return None + + self._pool._put_conn(self._connection) + self._connection = None + + def drain_conn(self) -> None: + """ + Read and discard any remaining HTTP response data in the response connection. + + Unread data in the HTTPResponse connection blocks the connection from being released back to the pool. + """ + try: + self.read() + except (HTTPError, OSError, BaseSSLError, HTTPException): + pass + + @property + def data(self) -> bytes: + # For backwards-compat with earlier urllib3 0.4 and earlier. + if self._body: + return self._body # type: ignore[return-value] + + if self._fp: + return self.read(cache_content=True) + + return None # type: ignore[return-value] + + @property + def connection(self) -> HTTPConnection | None: + return self._connection + + def isclosed(self) -> bool: + return is_fp_closed(self._fp) + + def tell(self) -> int: + """ + Obtain the number of bytes pulled over the wire so far. May differ from + the amount of content returned by :meth:``urllib3.response.HTTPResponse.read`` + if bytes are encoded on the wire (e.g, compressed). + """ + return self._fp_bytes_read + + def _init_length(self, request_method: str | None) -> int | None: + """ + Set initial length value for Response content if available. + """ + length: int | None + content_length: str | None = self.headers.get("content-length") + + if content_length is not None: + if self.chunked: + # This Response will fail with an IncompleteRead if it can't be + # received as chunked. This method falls back to attempt reading + # the response before raising an exception. + log.warning( + "Received response with both Content-Length and " + "Transfer-Encoding set. This is expressly forbidden " + "by RFC 7230 sec 3.3.2. Ignoring Content-Length and " + "attempting to process response as Transfer-Encoding: " + "chunked." + ) + return None + + try: + # RFC 7230 section 3.3.2 specifies multiple content lengths can + # be sent in a single Content-Length header + # (e.g. Content-Length: 42, 42). This line ensures the values + # are all valid ints and that as long as the `set` length is 1, + # all values are the same. Otherwise, the header is invalid. + lengths = {int(val) for val in content_length.split(",")} + if len(lengths) > 1: + raise InvalidHeader( + "Content-Length contained multiple " + "unmatching values (%s)" % content_length + ) + length = lengths.pop() + except ValueError: + length = None + else: + if length < 0: + length = None + + else: # if content_length is None + length = None + + # Convert status to int for comparison + # In some cases, httplib returns a status of "_UNKNOWN" + try: + status = int(self.status) + except ValueError: + status = 0 + + # Check for responses that shouldn't include a body + if status in (204, 304) or 100 <= status < 200 or request_method == "HEAD": + length = 0 + + return length + + @contextmanager + def _error_catcher(self) -> typing.Generator[None]: + """ + Catch low-level python exceptions, instead re-raising urllib3 + variants, so that low-level exceptions are not leaked in the + high-level api. + + On exit, release the connection back to the pool. + """ + clean_exit = False + + try: + try: + yield + + except SocketTimeout as e: + # FIXME: Ideally we'd like to include the url in the ReadTimeoutError but + # there is yet no clean way to get at it from this context. + raise ReadTimeoutError(self._pool, None, "Read timed out.") from e # type: ignore[arg-type] + + except BaseSSLError as e: + # FIXME: Is there a better way to differentiate between SSLErrors? + if "read operation timed out" not in str(e): + # SSL errors related to framing/MAC get wrapped and reraised here + raise SSLError(e) from e + + raise ReadTimeoutError(self._pool, None, "Read timed out.") from e # type: ignore[arg-type] + + except IncompleteRead as e: + if ( + e.expected is not None + and e.partial is not None + and e.expected == -e.partial + ): + arg = "Response may not contain content." + else: + arg = f"Connection broken: {e!r}" + raise ProtocolError(arg, e) from e + + except (HTTPException, OSError) as e: + raise ProtocolError(f"Connection broken: {e!r}", e) from e + + # If no exception is thrown, we should avoid cleaning up + # unnecessarily. + clean_exit = True + finally: + # If we didn't terminate cleanly, we need to throw away our + # connection. + if not clean_exit: + # The response may not be closed but we're not going to use it + # anymore so close it now to ensure that the connection is + # released back to the pool. + if self._original_response: + self._original_response.close() + + # Closing the response may not actually be sufficient to close + # everything, so if we have a hold of the connection close that + # too. + if self._connection: + self._connection.close() + + # If we hold the original response but it's closed now, we should + # return the connection back to the pool. + if self._original_response and self._original_response.isclosed(): + self.release_conn() + + def _fp_read( + self, + amt: int | None = None, + *, + read1: bool = False, + ) -> bytes: + """ + Read a response with the thought that reading the number of bytes + larger than can fit in a 32-bit int at a time via SSL in some + known cases leads to an overflow error that has to be prevented + if `amt` or `self.length_remaining` indicate that a problem may + happen. + + The known cases: + * CPython < 3.9.7 because of a bug + https://github.com/urllib3/urllib3/issues/2513#issuecomment-1152559900. + * urllib3 injected with pyOpenSSL-backed SSL-support. + * CPython < 3.10 only when `amt` does not fit 32-bit int. + """ + assert self._fp + c_int_max = 2**31 - 1 + if ( + (amt and amt > c_int_max) + or ( + amt is None + and self.length_remaining + and self.length_remaining > c_int_max + ) + ) and (util.IS_PYOPENSSL or sys.version_info < (3, 10)): + if read1: + return self._fp.read1(c_int_max) + buffer = io.BytesIO() + # Besides `max_chunk_amt` being a maximum chunk size, it + # affects memory overhead of reading a response by this + # method in CPython. + # `c_int_max` equal to 2 GiB - 1 byte is the actual maximum + # chunk size that does not lead to an overflow error, but + # 256 MiB is a compromise. + max_chunk_amt = 2**28 + while amt is None or amt != 0: + if amt is not None: + chunk_amt = min(amt, max_chunk_amt) + amt -= chunk_amt + else: + chunk_amt = max_chunk_amt + data = self._fp.read(chunk_amt) + if not data: + break + buffer.write(data) + del data # to reduce peak memory usage by `max_chunk_amt`. + return buffer.getvalue() + elif read1: + return self._fp.read1(amt) if amt is not None else self._fp.read1() + else: + # StringIO doesn't like amt=None + return self._fp.read(amt) if amt is not None else self._fp.read() + + def _raw_read( + self, + amt: int | None = None, + *, + read1: bool = False, + ) -> bytes: + """ + Reads `amt` of bytes from the socket. + """ + if self._fp is None: + return None # type: ignore[return-value] + + fp_closed = getattr(self._fp, "closed", False) + + with self._error_catcher(): + data = self._fp_read(amt, read1=read1) if not fp_closed else b"" + if amt is not None and amt != 0 and not data: + # Platform-specific: Buggy versions of Python. + # Close the connection when no data is returned + # + # This is redundant to what httplib/http.client _should_ + # already do. However, versions of python released before + # December 15, 2012 (http://bugs.python.org/issue16298) do + # not properly close the connection in all cases. There is + # no harm in redundantly calling close. + self._fp.close() + if ( + self.enforce_content_length + and self.length_remaining is not None + and self.length_remaining != 0 + ): + # This is an edge case that httplib failed to cover due + # to concerns of backward compatibility. We're + # addressing it here to make sure IncompleteRead is + # raised during streaming, so all calls with incorrect + # Content-Length are caught. + raise IncompleteRead(self._fp_bytes_read, self.length_remaining) + elif read1 and ( + (amt != 0 and not data) or self.length_remaining == len(data) + ): + # All data has been read, but `self._fp.read1` in + # CPython 3.12 and older doesn't always close + # `http.client.HTTPResponse`, so we close it here. + # See https://github.com/python/cpython/issues/113199 + self._fp.close() + + if data: + self._fp_bytes_read += len(data) + if self.length_remaining is not None: + self.length_remaining -= len(data) + return data + + def read( + self, + amt: int | None = None, + decode_content: bool | None = None, + cache_content: bool = False, + ) -> bytes: + """ + Similar to :meth:`http.client.HTTPResponse.read`, but with two additional + parameters: ``decode_content`` and ``cache_content``. + + :param amt: + How much of the content to read. If specified, caching is skipped + because it doesn't make sense to cache partial content as the full + response. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + + :param cache_content: + If True, will save the returned data such that the same result is + returned despite of the state of the underlying file object. This + is useful if you want the ``.data`` property to continue working + after having ``.read()`` the file object. (Overridden if ``amt`` is + set.) + """ + self._init_decoder() + if decode_content is None: + decode_content = self.decode_content + + if amt and amt < 0: + # Negative numbers and `None` should be treated the same. + amt = None + elif amt is not None: + cache_content = False + + if len(self._decoded_buffer) >= amt: + return self._decoded_buffer.get(amt) + + data = self._raw_read(amt) + + flush_decoder = amt is None or (amt != 0 and not data) + + if not data and len(self._decoded_buffer) == 0: + return data + + if amt is None: + data = self._decode(data, decode_content, flush_decoder) + if cache_content: + self._body = data + else: + # do not waste memory on buffer when not decoding + if not decode_content: + if self._has_decoded_content: + raise RuntimeError( + "Calling read(decode_content=False) is not supported after " + "read(decode_content=True) was called." + ) + return data + + decoded_data = self._decode(data, decode_content, flush_decoder) + self._decoded_buffer.put(decoded_data) + + while len(self._decoded_buffer) < amt and data: + # TODO make sure to initially read enough data to get past the headers + # For example, the GZ file header takes 10 bytes, we don't want to read + # it one byte at a time + data = self._raw_read(amt) + decoded_data = self._decode(data, decode_content, flush_decoder) + self._decoded_buffer.put(decoded_data) + data = self._decoded_buffer.get(amt) + + return data + + def read1( + self, + amt: int | None = None, + decode_content: bool | None = None, + ) -> bytes: + """ + Similar to ``http.client.HTTPResponse.read1`` and documented + in :meth:`io.BufferedReader.read1`, but with an additional parameter: + ``decode_content``. + + :param amt: + How much of the content to read. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + """ + if decode_content is None: + decode_content = self.decode_content + if amt and amt < 0: + # Negative numbers and `None` should be treated the same. + amt = None + # try and respond without going to the network + if self._has_decoded_content: + if not decode_content: + raise RuntimeError( + "Calling read1(decode_content=False) is not supported after " + "read1(decode_content=True) was called." + ) + if len(self._decoded_buffer) > 0: + if amt is None: + return self._decoded_buffer.get_all() + return self._decoded_buffer.get(amt) + if amt == 0: + return b"" + + # FIXME, this method's type doesn't say returning None is possible + data = self._raw_read(amt, read1=True) + if not decode_content or data is None: + return data + + self._init_decoder() + while True: + flush_decoder = not data + decoded_data = self._decode(data, decode_content, flush_decoder) + self._decoded_buffer.put(decoded_data) + if decoded_data or flush_decoder: + break + data = self._raw_read(8192, read1=True) + + if amt is None: + return self._decoded_buffer.get_all() + return self._decoded_buffer.get(amt) + + def stream( + self, amt: int | None = 2**16, decode_content: bool | None = None + ) -> typing.Generator[bytes]: + """ + A generator wrapper for the read() method. A call will block until + ``amt`` bytes have been read from the connection or until the + connection is closed. + + :param amt: + How much of the content to read. The generator will return up to + much data per iteration, but may return less. This is particularly + likely when using compressed data. However, the empty string will + never be returned. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + """ + if self.chunked and self.supports_chunked_reads(): + yield from self.read_chunked(amt, decode_content=decode_content) + else: + while not is_fp_closed(self._fp) or len(self._decoded_buffer) > 0: + data = self.read(amt=amt, decode_content=decode_content) + + if data: + yield data + + # Overrides from io.IOBase + def readable(self) -> bool: + return True + + def shutdown(self) -> None: + if not self._sock_shutdown: + raise ValueError("Cannot shutdown socket as self._sock_shutdown is not set") + if self._connection is None: + raise RuntimeError( + "Cannot shutdown as connection has already been released to the pool" + ) + self._sock_shutdown(socket.SHUT_RD) + + def close(self) -> None: + self._sock_shutdown = None + + if not self.closed and self._fp: + self._fp.close() + + if self._connection: + self._connection.close() + + if not self.auto_close: + io.IOBase.close(self) + + @property + def closed(self) -> bool: + if not self.auto_close: + return io.IOBase.closed.__get__(self) # type: ignore[no-any-return] + elif self._fp is None: + return True + elif hasattr(self._fp, "isclosed"): + return self._fp.isclosed() + elif hasattr(self._fp, "closed"): + return self._fp.closed + else: + return True + + def fileno(self) -> int: + if self._fp is None: + raise OSError("HTTPResponse has no file to get a fileno from") + elif hasattr(self._fp, "fileno"): + return self._fp.fileno() + else: + raise OSError( + "The file-like object this HTTPResponse is wrapped " + "around has no file descriptor" + ) + + def flush(self) -> None: + if ( + self._fp is not None + and hasattr(self._fp, "flush") + and not getattr(self._fp, "closed", False) + ): + return self._fp.flush() + + def supports_chunked_reads(self) -> bool: + """ + Checks if the underlying file-like object looks like a + :class:`http.client.HTTPResponse` object. We do this by testing for + the fp attribute. If it is present we assume it returns raw chunks as + processed by read_chunked(). + """ + return hasattr(self._fp, "fp") + + def _update_chunk_length(self) -> None: + # First, we'll figure out length of a chunk and then + # we'll try to read it from socket. + if self.chunk_left is not None: + return None + line = self._fp.fp.readline() # type: ignore[union-attr] + line = line.split(b";", 1)[0] + try: + self.chunk_left = int(line, 16) + except ValueError: + self.close() + if line: + # Invalid chunked protocol response, abort. + raise InvalidChunkLength(self, line) from None + else: + # Truncated at start of next chunk + raise ProtocolError("Response ended prematurely") from None + + def _handle_chunk(self, amt: int | None) -> bytes: + returned_chunk = None + if amt is None: + chunk = self._fp._safe_read(self.chunk_left) # type: ignore[union-attr] + returned_chunk = chunk + self._fp._safe_read(2) # type: ignore[union-attr] # Toss the CRLF at the end of the chunk. + self.chunk_left = None + elif self.chunk_left is not None and amt < self.chunk_left: + value = self._fp._safe_read(amt) # type: ignore[union-attr] + self.chunk_left = self.chunk_left - amt + returned_chunk = value + elif amt == self.chunk_left: + value = self._fp._safe_read(amt) # type: ignore[union-attr] + self._fp._safe_read(2) # type: ignore[union-attr] # Toss the CRLF at the end of the chunk. + self.chunk_left = None + returned_chunk = value + else: # amt > self.chunk_left + returned_chunk = self._fp._safe_read(self.chunk_left) # type: ignore[union-attr] + self._fp._safe_read(2) # type: ignore[union-attr] # Toss the CRLF at the end of the chunk. + self.chunk_left = None + return returned_chunk # type: ignore[no-any-return] + + def read_chunked( + self, amt: int | None = None, decode_content: bool | None = None + ) -> typing.Generator[bytes]: + """ + Similar to :meth:`HTTPResponse.read`, but with an additional + parameter: ``decode_content``. + + :param amt: + How much of the content to read. If specified, caching is skipped + because it doesn't make sense to cache partial content as the full + response. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + """ + self._init_decoder() + # FIXME: Rewrite this method and make it a class with a better structured logic. + if not self.chunked: + raise ResponseNotChunked( + "Response is not chunked. " + "Header 'transfer-encoding: chunked' is missing." + ) + if not self.supports_chunked_reads(): + raise BodyNotHttplibCompatible( + "Body should be http.client.HTTPResponse like. " + "It should have have an fp attribute which returns raw chunks." + ) + + with self._error_catcher(): + # Don't bother reading the body of a HEAD request. + if self._original_response and is_response_to_head(self._original_response): + self._original_response.close() + return None + + # If a response is already read and closed + # then return immediately. + if self._fp.fp is None: # type: ignore[union-attr] + return None + + if amt and amt < 0: + # Negative numbers and `None` should be treated the same, + # but httplib handles only `None` correctly. + amt = None + + while True: + self._update_chunk_length() + if self.chunk_left == 0: + break + chunk = self._handle_chunk(amt) + decoded = self._decode( + chunk, decode_content=decode_content, flush_decoder=False + ) + if decoded: + yield decoded + + if decode_content: + # On CPython and PyPy, we should never need to flush the + # decoder. However, on Jython we *might* need to, so + # lets defensively do it anyway. + decoded = self._flush_decoder() + if decoded: # Platform-specific: Jython. + yield decoded + + # Chunk content ends with \r\n: discard it. + while self._fp is not None: + line = self._fp.fp.readline() + if not line: + # Some sites may not end with '\r\n'. + break + if line == b"\r\n": + break + + # We read everything; close the "file". + if self._original_response: + self._original_response.close() + + @property + def url(self) -> str | None: + """ + Returns the URL that was the source of this response. + If the request that generated this response redirected, this method + will return the final redirect location. + """ + return self._request_url + + @url.setter + def url(self, url: str) -> None: + self._request_url = url + + def __iter__(self) -> typing.Iterator[bytes]: + buffer: list[bytes] = [] + for chunk in self.stream(decode_content=True): + if b"\n" in chunk: + chunks = chunk.split(b"\n") + yield b"".join(buffer) + chunks[0] + b"\n" + for x in chunks[1:-1]: + yield x + b"\n" + if chunks[-1]: + buffer = [chunks[-1]] + else: + buffer = [] + else: + buffer.append(chunk) + if buffer: + yield b"".join(buffer) diff --git a/.venv/lib/python3.12/site-packages/wandb-0.22.2.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/wandb-0.22.2.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/wandb-0.22.2.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/wandb-0.22.2.dist-info/METADATA b/.venv/lib/python3.12/site-packages/wandb-0.22.2.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..1a735f0e5fc31ab153ce9c7f4626300757964a49 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/wandb-0.22.2.dist-info/METADATA @@ -0,0 +1,223 @@ +Metadata-Version: 2.4 +Name: wandb +Version: 0.22.2 +Summary: A CLI and library for interacting with the Weights & Biases API. +Project-URL: Source, https://github.com/wandb/wandb +Project-URL: Bug Reports, https://github.com/wandb/wandb/issues +Project-URL: Documentation, https://docs.wandb.ai/ +Author-email: Weights & Biases +License: MIT License + + Copyright (c) 2021 Weights and Biases, Inc. + + 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. +License-File: LICENSE +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Science/Research +Classifier: License :: OSI Approved :: MIT License +Classifier: Natural Language :: English +Classifier: Programming Language :: Go +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: Topic :: Scientific/Engineering :: Artificial Intelligence +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: System :: Logging +Classifier: Topic :: System :: Monitoring +Requires-Python: >=3.8 +Requires-Dist: click>=8.0.1 +Requires-Dist: eval-type-backport; python_version < '3.10' +Requires-Dist: gitpython!=3.1.29,>=1.0.0 +Requires-Dist: packaging +Requires-Dist: platformdirs +Requires-Dist: protobuf!=4.21.0,!=5.28.0,<7,>=3.12.0; python_version < '3.9' and sys_platform == 'linux' +Requires-Dist: protobuf!=4.21.0,!=5.28.0,<7,>=3.15.0; python_version == '3.9' and sys_platform == 'linux' +Requires-Dist: protobuf!=4.21.0,!=5.28.0,<7,>=3.19.0; python_version > '3.9' and sys_platform == 'linux' +Requires-Dist: protobuf!=4.21.0,!=5.28.0,<7,>=3.19.0; sys_platform != 'linux' +Requires-Dist: pydantic<3 +Requires-Dist: pyyaml +Requires-Dist: requests<3,>=2.0.0 +Requires-Dist: sentry-sdk>=2.0.0 +Requires-Dist: typing-extensions<5,>=4.8 +Provides-Extra: aws +Requires-Dist: boto3; extra == 'aws' +Requires-Dist: botocore>=1.5.76; extra == 'aws' +Provides-Extra: azure +Requires-Dist: azure-identity; extra == 'azure' +Requires-Dist: azure-storage-blob; extra == 'azure' +Provides-Extra: gcp +Requires-Dist: google-cloud-storage; extra == 'gcp' +Provides-Extra: importers +Requires-Dist: filelock; extra == 'importers' +Requires-Dist: mlflow; extra == 'importers' +Requires-Dist: polars<=1.2.1; extra == 'importers' +Requires-Dist: rich; extra == 'importers' +Requires-Dist: tenacity; extra == 'importers' +Provides-Extra: kubeflow +Requires-Dist: google-cloud-storage; extra == 'kubeflow' +Requires-Dist: kubernetes; extra == 'kubeflow' +Requires-Dist: minio; extra == 'kubeflow' +Requires-Dist: sh; extra == 'kubeflow' +Provides-Extra: launch +Requires-Dist: awscli; extra == 'launch' +Requires-Dist: azure-containerregistry; extra == 'launch' +Requires-Dist: azure-identity; extra == 'launch' +Requires-Dist: azure-storage-blob; extra == 'launch' +Requires-Dist: boto3; extra == 'launch' +Requires-Dist: botocore>=1.5.76; extra == 'launch' +Requires-Dist: chardet; extra == 'launch' +Requires-Dist: google-auth; extra == 'launch' +Requires-Dist: google-cloud-aiplatform; extra == 'launch' +Requires-Dist: google-cloud-artifact-registry; extra == 'launch' +Requires-Dist: google-cloud-compute; extra == 'launch' +Requires-Dist: google-cloud-storage; extra == 'launch' +Requires-Dist: iso8601; extra == 'launch' +Requires-Dist: jsonschema; extra == 'launch' +Requires-Dist: kubernetes; extra == 'launch' +Requires-Dist: kubernetes-asyncio; extra == 'launch' +Requires-Dist: nbconvert; extra == 'launch' +Requires-Dist: nbformat; extra == 'launch' +Requires-Dist: optuna; extra == 'launch' +Requires-Dist: pydantic; extra == 'launch' +Requires-Dist: pyyaml>=6.0.0; extra == 'launch' +Requires-Dist: tomli; extra == 'launch' +Requires-Dist: tornado>=6.5.0; (python_version >= '3.9') and extra == 'launch' +Requires-Dist: typing-extensions; extra == 'launch' +Provides-Extra: media +Requires-Dist: bokeh; extra == 'media' +Requires-Dist: imageio>=2.28.1; extra == 'media' +Requires-Dist: moviepy>=1.0.0; extra == 'media' +Requires-Dist: numpy; extra == 'media' +Requires-Dist: pillow; extra == 'media' +Requires-Dist: plotly>=5.18.0; extra == 'media' +Requires-Dist: rdkit; extra == 'media' +Requires-Dist: soundfile; extra == 'media' +Provides-Extra: models +Requires-Dist: cloudpickle; extra == 'models' +Provides-Extra: perf +Requires-Dist: orjson; extra == 'perf' +Provides-Extra: sweeps +Requires-Dist: sweeps>=0.2.0; extra == 'sweeps' +Provides-Extra: workspaces +Requires-Dist: wandb-workspaces; extra == 'workspaces' +Description-Content-Type: text/markdown + +
+

+
+ +# Weights and Biases [![PyPI](https://img.shields.io/pypi/v/wandb)](https://pypi.python.org/pypi/wandb) [![Conda (channel only)](https://img.shields.io/conda/vn/conda-forge/wandb)](https://anaconda.org/conda-forge/wandb) [![CircleCI](https://img.shields.io/circleci/build/github/wandb/wandb/main)](https://circleci.com/gh/wandb/wandb) [![Codecov](https://img.shields.io/codecov/c/gh/wandb/wandb)](https://codecov.io/gh/wandb/wandb) + +Use W&B to build better models faster. Track and visualize all the pieces of your machine learning pipeline, from datasets to production machine learning models. Get started with W&B today, [sign up for an account!](https://wandb.com?utm_source=github&utm_medium=code&utm_campaign=wandb&utm_content=readme) + + + +See the [W&B Developer Guide](https://docs.wandb.ai/?utm_source=github&utm_medium=code&utm_campaign=wandb&utm_content=documentation) and [API Reference Guide](https://docs.wandb.ai/ref?utm_source=github&utm_medium=code&utm_campaign=wandb&utm_content=documentation) for a full technical description of the W&B platform. + +  + +# Quickstart + +Get started with W&B in four steps: + +1. First, sign up for a [W&B account](https://wandb.ai/login?utm_source=github&utm_medium=code&utm_campaign=wandb&utm_content=quickstart). + +2. Second, install the W&B SDK with [pip](https://pip.pypa.io/en/stable/). Navigate to your terminal and type the following command: + +```shell +pip install wandb +``` + +3. Third, log into W&B: + +```python +wandb.login() +``` + +4. Use the example code snippet below as a template to integrate W&B to your Python script: + +```python +import wandb + +# Start a W&B Run with wandb.init +run = wandb.init(project="my_first_project") + +# Save model inputs and hyperparameters in a wandb.config object +config = run.config +config.learning_rate = 0.01 + +# Model training code here ... + +# Log metrics over time to visualize performance with wandb.log +for i in range(10): + run.log({"loss": ...}) + +# Mark the run as finished, and finish uploading all data +run.finish() +``` + +For example, if the preceding code was stored in a script called train.py: + +```shell +python train.py +``` + +You will see a URL in your terminal logs when your script starts and finishes. Data is staged locally in a directory named _wandb_ relative to your script. Navigate to the W&B App to view a dashboard of your first W&B Experiment. Use the W&B App to compare multiple experiments in a unified place, dive into the results of a single run, and much more! + +  + +# Integrations + +Use your favorite framework with W&B. W&B integrations make it fast and easy to set up experiment tracking and data versioning inside existing projects. For more information on how to integrate W&B with the framework of your choice, see [W&B Integrations](https://docs.wandb.ai/guides/integrations) in the W&B Developer Guide. + +  + +# Python Version Support + +We are committed to supporting our minimum required Python version for *at least* six months after its official end-of-life (EOL) date, as defined by the Python Software Foundation. You can find a list of Python EOL dates [here](https://devguide.python.org/versions/). + +When we discontinue support for a Python version, we will increment the library’s minor version number to reflect this change. + +  + +# Contribution guidelines +Weights & Biases ❤️ open source, and we welcome contributions from the community! See the [Contribution guide](https://github.com/wandb/wandb/blob/main/CONTRIBUTING.md) for more information on the development workflow and the internals of the wandb library. For wandb bugs and feature requests, visit [GitHub Issues](https://github.com/wandb/wandb/issues) or contact support@wandb.com. + +  + +# Academic Researchers +Reach out to W&B Support at support@wandb.com to get a [free academic license](https://www.wandb.com/academic) for you and your research group. + +  + +# W&B Community + +Be a part of the growing W&B Community and interact with the W&B team in our [Discord](https://wandb.me/discord). Stay connected with the latest ML updates and tutorials with [W&B Fully Connected](https://wandb.ai/fully-connected). + +  + +# License + +[MIT License](https://github.com/wandb/wandb/blob/main/LICENSE) diff --git a/.venv/lib/python3.12/site-packages/wandb-0.22.2.dist-info/RECORD b/.venv/lib/python3.12/site-packages/wandb-0.22.2.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..4a63c6621143d0eec701b49549595c6b147d2f3c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/wandb-0.22.2.dist-info/RECORD @@ -0,0 +1,930 @@ +../../../bin/wandb,sha256=pgts_alfm6xsO70VfHxr5jPc1N8GwqOqhAHryoqo9pA,327 +../../../bin/wb,sha256=pgts_alfm6xsO70VfHxr5jPc1N8GwqOqhAHryoqo9pA,327 +package_readme.md,sha256=U9047nyMDICgctm1HLm4HfXwFnFKsEn2m77hsYPUZ1I,4298 +wandb-0.22.2.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +wandb-0.22.2.dist-info/METADATA,sha256=LFEVr8vc56D-G1vYkTtNeY6_OAG4VL20gfn3Ru6uiHU,10244 +wandb-0.22.2.dist-info/RECORD,, +wandb-0.22.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb-0.22.2.dist-info/WHEEL,sha256=s7sPrB31V08tAWW9qF5-VggdUkNTqVMOnYoTbE5U7gA,141 +wandb-0.22.2.dist-info/entry_points.txt,sha256=v4FCOZ9gW7Pc6KLsmgQqpCiKTrA1wh2XHmNf-NUP1-I,67 +wandb-0.22.2.dist-info/licenses/LICENSE,sha256=izOKRJpGOx1PrJiGOKR0HsNdlB5JdH2d0Z4P7a7ssTc,1081 +wandb/__init__.py,sha256=Y9S4ch6m-EjeentHBNI6uFxXYuck5Dz9EAqt7U09qMA,6771 +wandb/__init__.pyi,sha256=KOzGIVVup-nr_-Rl-EX-GgTUSdlNV15niRiFF5dsX4Q,46467 +wandb/__main__.py,sha256=gripuDgB7J8wMMeJt4CIBRjn1BMSFr5zvsrt585Pnj4,64 +wandb/_analytics.py,sha256=XkUDTTo599nAU7HsnX2yzox6NdcJoPrlDLWcd7U4WfY,1986 +wandb/_iterutils.py,sha256=sGQvC8x9Wq1UHbqheUk44U3ckS_HO4bg0x0mGVES308,2748 +wandb/_pydantic/__init__.py,sha256=1dM5nFwOIhaIO7Li_uYiGpitqC9A4yTwOC01BgKN2A8,670 +wandb/_pydantic/base.py,sha256=YMNaJlsU5rH5AYHtCeZbNMzBkre9qh23CUoeyehjc_4,4084 +wandb/_pydantic/field_types.py,sha256=8xuUOQ9KyD6EDVQl3f0wmizIZEX2X4EtmafSSTGmMsU,839 +wandb/_pydantic/utils.py,sha256=XcnRAEOdjANLxvr_FMnMPKmqv4f2HwjbNo-5J_JKKe0,2810 +wandb/_pydantic/v1_compat.py,sha256=kwtbMei9ibcS3pLbVFI7wOyF7VSoQVjZthP5xQG4yEM,12315 +wandb/_strutils.py,sha256=IDRQWWX-DqeKjrJHQUGOgM8DOlM5inpZLPeWhomMc84,1391 +wandb/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/agents/pyagent.py,sha256=cQ0YlusW6_JV4NlPJJpodJ6HCLr-NiubXjEBaVAPgrg,14287 +wandb/analytics/__init__.py,sha256=WG_Mh20Hr8d3vDmGMcfDXCMEIew3uzDYZAJwFsrbAug,50 +wandb/analytics/sentry.py,sha256=XFrWH5ZBE4l2mS2aGIMRDG65TnVcFzsjthvnuqPqJDs,8440 +wandb/apis/__init__.py,sha256=Ui1oj_xYTt8a1uOgg2WT3K-6FAzGKARfZ37cQtvsHss,1374 +wandb/apis/attrs.py,sha256=h2EyZ-l63HWQYfN0_Dd-sjxqF2MDN2M9OaR738J5FLk,1472 +wandb/apis/importers/__init__.py,sha256=ewph-RBUC9FnxL6uWbuuW0Hmdv2cHsxfqnZi0546ds4,38 +wandb/apis/importers/internals/internal.py,sha256=rulu7UDs4n75zGjGQj969nlCjpFWdNnC_2u6s3p4lT8,12887 +wandb/apis/importers/internals/protocols.py,sha256=shZBD2bRMo81xPSUQdK2d9KK8qpop4CmMkizg8FI3WM,2885 +wandb/apis/importers/internals/util.py,sha256=lvqLAqNA1UpoOuD6eqe_1ZzRwxj8IkYKxYcP9h0S5jI,2081 +wandb/apis/importers/mlflow.py,sha256=GK48DpS9e1q_lRVapNw3_zZun1dajqW6kJ5DaleW8Y8,8257 +wandb/apis/importers/validation.py,sha256=5odBqLZ3BQvyyO8CkYrm7bPXySOeDt_amQPwIrMcJRE,3183 +wandb/apis/importers/wandb.py,sha256=A4Dd0ioykPa1OmTlxkuvpYzgtH5aNpercDl4AxH_CHo,54531 +wandb/apis/internal.py,sha256=wV0pxOGZB1JFda7MV9sqW8W-rJWfQJtIlSPs4UuGA1I,7772 +wandb/apis/normalize.py,sha256=uPhY0Wx-ykjtJEXCf8YmKBWjNz6I4fvEcJ83PQjqHg0,2701 +wandb/apis/paginator.py,sha256=aG4t64c0O8tCLbmJf6de2EDRRSdczOyEQu8fXJVIiWw,3859 +wandb/apis/public/__init__.py,sha256=0flyGXjkHPPqecFcA-Tym9PhI7MpGWkrYyy3U7C7VBk,2217 +wandb/apis/public/api.py,sha256=ZxV4g8OXrCDMiVvWsuyKWZfLd5Q0thj2xTFNipA2IDs,90003 +wandb/apis/public/artifacts.py,sha256=xLU9DetVduUkzw64diSWbqr5sMjsr4fgf0kqaO53LRk,35032 +wandb/apis/public/automations.py,sha256=YISVkOYAUijYSDpZ4fBfi36__azXgsUzE0jviTpz64Q,2645 +wandb/apis/public/const.py,sha256=lalM0qiWJrpneya84HRspYDrm8htRSaZcxC7_SPhIJA,157 +wandb/apis/public/files.py,sha256=atVVf6l-BqH8ciybKf4aUKoMCP3RKO3cz0Q_FC78gAk,13689 +wandb/apis/public/history.py,sha256=CHH0MVGcZ0N0DDp4AFqY1GZFWiaZDLpWa-_OR5EZxgw,6324 +wandb/apis/public/integrations.py,sha256=sDpClnpwCew3MO3M_yhxMSESzYcrM38zDXWoOyOiy4E,7260 +wandb/apis/public/jobs.py,sha256=sVc6OtiGbEcR6rrCZuJL4ys6usl7JerPc_-AhLs_TTI,26279 +wandb/apis/public/projects.py,sha256=r69Vw7ezlfSM0z5Ab0VlSfElYlO5XHNicLK0kC3G_S0,8050 +wandb/apis/public/query_generator.py,sha256=2SFLJWogQd8GBmkMuus-UwvouZo5vEigXDW4oTxnbZA,6564 +wandb/apis/public/registries/__init__.py,sha256=FLugggtF8YwBR1zJJYF3DGKJlYThAwQTOFTFEL24c_0,152 +wandb/apis/public/registries/_freezable_list.py,sha256=XmbjYTW5mKFUOslzQvqSm5EFvDpVEiA3MyQGGhWnjDg,6324 +wandb/apis/public/registries/_utils.py,sha256=VXkUNQ2ruYb8284QB1qOtMQyN-VpNSOIqig8uUTWzNA,4297 +wandb/apis/public/registries/registries_search.py,sha256=ouGQ_MWt_IhmoMgyG-neUIT67OlYI7MpOrQ0SEXCMpQ,11519 +wandb/apis/public/registries/registry.py,sha256=QbJ88BrSM0QMu0X8X9mA0Jrtp9ND4MBSO-_fQmvkRzw,13874 +wandb/apis/public/reports.py,sha256=Immy6MPEx93icHIwznIsrhEGaGb13a3UevA_uzcLpkc,19855 +wandb/apis/public/runs.py,sha256=7ww9poXAqygIPy9jX_qZ1H53P5fYwnI5NfA3nhUKhU0,49856 +wandb/apis/public/sweeps.py,sha256=HSUG8DUArYnEc-gbz6V3ghZuijrqa9wclk_iRp3Ec5w,12932 +wandb/apis/public/teams.py,sha256=99RjMMOguKK2tyTqC5mndOVfMgIgD6xE8Sh50lOZVCs,6651 +wandb/apis/public/users.py,sha256=cqbZlIpngutnuLOpvE1vwbeODvYI2nhACadQoRrgiTY,5386 +wandb/apis/public/utils.py,sha256=GfG6lt5Y_BMWfUF6wUJ8E6XAE-rPBK3yx09XnD5-YUU,7859 +wandb/apis/reports/__init__.py,sha256=5ZkKvOqwks3UmVeaPuwIZYwyV8_afsGbtybDOXYjSsg,32 +wandb/apis/reports/v1/__init__.py,sha256=L5l1BqMaap-w5wXCzn8pC8RXB7_72MsjTx8HeLuC3XU,263 +wandb/apis/reports/v2/__init__.py,sha256=jdLiTqf5QIRxnWH8drWV9Y06bZEhI5N6uGGSKAT7tv0,263 +wandb/apis/workspaces/__init__.py,sha256=buvqtqELpCFWC7sHzEuslJgx24ZWDj-5lo2mdV6aHdg,265 +wandb/automations/__init__.py,sha256=rX1bkA2ZpN6CHlyjf6gpwFhWYmrRdGVs14Sm1Z9RBjA,2365 +wandb/automations/_filters/__init__.py,sha256=UxAEPbeI7wUqZY-w8FgsMU4Jp5rgSxwuQXc_VhdpDWE,466 +wandb/automations/_filters/expressions.py,sha256=pAhjWPExgw4ks9RmgAb7_deAtRhimPONB00iewQqIT8,6636 +wandb/automations/_filters/operators.py,sha256=s-M00trfqVifiqOykKM0F9QmE7DWOPdAae5dJogf5VU,7193 +wandb/automations/_filters/run_metrics.py,sha256=bTgFimrEej0HS3RYWCdfUD5qeCNDWxyOTptn9qHhxMA,12879 +wandb/automations/_generated/__init__.py,sha256=6lDq2fuqAJT-UJUNqGNAKkvoerDgYBOS-s87eF0paDo,3279 +wandb/automations/_generated/create_automation.py,sha256=AYIaKKLZDSo0VPU_JqjbVSdcoNfFArUtoxWinf-Q3Uc,340 +wandb/automations/_generated/create_generic_webhook_integration.py,sha256=dZg3VG3meN8RuHTf1wdKi7B_XhNM7b6znzlECZFnA5A,1143 +wandb/automations/_generated/delete_automation.py,sha256=0fE1tCt-Yl3iU8UxpGBUj5jk2zXPOFwa6MZ_WfP2ZLE,301 +wandb/automations/_generated/enums.py,sha256=HHw1endeSevcOoy27zEz1XMaCbe50DXdf8CLbtYbFPk,869 +wandb/automations/_generated/fragments.py,sha256=O07tlUiSwo6h21z0r-vnsX7pfPGxNiW_YUe5d7dhIuE,9295 +wandb/automations/_generated/generic_webhook_integrations_by_entity.py,sha256=bwuMMOI2pZYzXWn50HvGH2yC6atEOTU5oNzYvw_MWfU,599 +wandb/automations/_generated/get_automations.py,sha256=4fdmSADMbWV5oHcWc6sUK8PVbAuTUOuhK2izLjfDj0c,537 +wandb/automations/_generated/get_automations_by_entity.py,sha256=G7w2Pnb4zOPq6gkw9jttmoQLjIXGnHXMl0bSeNZKTQw,591 +wandb/automations/_generated/input_types.py,sha256=-BIaTQ03aoDb7vxao_D8s8pkyofFdkAYnkw9sK8IojE,3541 +wandb/automations/_generated/integrations_by_entity.py,sha256=mRAf-LVgyWEmoGIKsI6CKPmuHbX2-Y1qpRzOKIAVW6c,501 +wandb/automations/_generated/operations.py,sha256=hL1Wqd_JJUthNybyQcY4CVnbl6VDzXlXySpyeB0iyhg,11122 +wandb/automations/_generated/slack_integrations_by_entity.py,sha256=87KtiuB4wfsHCiQD9m9_1GF9DYhdvqm4EsZ1dOxYtRU,536 +wandb/automations/_generated/update_automation.py,sha256=M58fqPcHAAYReJ45GU68FZAUCxERvTtWhNLlpiQJEag,340 +wandb/automations/_utils.py,sha256=3a39L976vsrckevwYLNHiHA5bcs-YlCdOwIuJ8UG3jc,8269 +wandb/automations/_validators.py,sha256=jCHTm0qo8SZsQuypZfOEbTUrMqb3yAOXjH-ssvcbwB4,5736 +wandb/automations/actions.py,sha256=Ai0H6_xrqOZqRcN3rLbQr0y1o3Dg2gfChXY3JnPspak,7429 +wandb/automations/automations.py,sha256=95Mw-WjGSp5XUdIlW6qX-lDxbVPufwJC2rxEhn87R3g,2642 +wandb/automations/events.py,sha256=vXBfejBGzRP6UuM3DRnBlOH1JYRs25N0FhWyUehlEEU,10464 +wandb/automations/integrations.py,sha256=ok3oBN8c8DMEbjt-wpFViwTd_FC-6nwRMuamsDAOiQc,1059 +wandb/automations/scopes.py,sha256=8H6bxXqffuQHOV__Od28mm29A6ptvVPe9fg0cnKQB-M,2310 +wandb/beta/workflows.py,sha256=jxJ7UizRFqapLuDVlJWNNjhf9s5iuFHtnF7NoA3NO3c,11501 +wandb/bin/gpu_stats,sha256=Eb1fTMTU1QwzKNDOcBDLoUJIXSVDj59d9bMgM56KEMQ,11651576 +wandb/bin/wandb-core,sha256=hmIcy7VKs7hcdrSJ2YlR3BMUEfDLGEc_WymwQ86vG0M,43516088 +wandb/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/cli/beta.py,sha256=5yjMQFZpB8E4R5dM_l2h3losjoV2A768LR0PFlG-MUE,2578 +wandb/cli/beta_leet.py,sha256=QKqtX4pZSso8wwmitEIoKQSXADr7V_ptGyksGygMKCM,1896 +wandb/cli/beta_sync.py,sha256=qmK1aeyBxwYTCGhOmo3dxVOkOl7iZM4IgLBOkcA4vQg,6954 +wandb/cli/cli.py,sha256=Mz1Ju-9rXWZYXBkizMFIpxeoCwZJthRWgk1ekE6lWLA,95962 +wandb/data_types.py,sha256=M-wqAO0FtIqvj28556u3h4nzSwlRcbxhXoX0B9jlJSo,2283 +wandb/docker/__init__.py,sha256=tupkiHAEcpXmrcS7V8gcjHnqim2k9GJH6ZogVMC8nts,8663 +wandb/docker/names.py,sha256=xI3DYzgYpkjaCz4qTKnm49fnexMkNCUjBuhunxAYyeE,1311 +wandb/docker/wandb-entrypoint.sh,sha256=P4eTMG7wYsgTfJIws_HT7QFlYBI70ZLnNlDGTZdmYVE,989 +wandb/env.py,sha256=DuzNj6MX0DRDQ8Y_t80jTLC3wiroLZPxA7otwASoE44,14012 +wandb/errors/__init__.py,sha256=Tlij_4c-SxruLN-p0sDfDpF-HktBcHeAbztwGqQ84cU,293 +wandb/errors/errors.py,sha256=FyCJHVK0VS_B2jfX2grF085c8xStm9rZItnV8SslhsI,981 +wandb/errors/links.py,sha256=sNwJ74e9qb0w4GRZfnbPXK5ZdpIqc5lkuaT4T2Snnqw,2520 +wandb/errors/term.py,sha256=kH_noQvl9BDsGBXm7fQfH5u0ERmI6bOZa8Rp49DoAPk,12207 +wandb/errors/util.py,sha256=YqO_fpZeXubXWBUAVQ6gmuLAiz3Q9pYVE5LbQ2Rq5Ck,1711 +wandb/errors/warnings.py,sha256=kyLP3bfXSmlztp8nOepLtfTdM-03N-i7Ho1Y568BOtk,57 +wandb/filesync/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/filesync/dir_watcher.py,sha256=9Y57-5-hWZFzCuBL6HQVtm-MIE9p8kCRU_g_YZUVHvo,16345 +wandb/filesync/stats.py,sha256=bjVBoCU9I9ke_XbEqtUgmKJVSmFxxAs2JciBzGWPhDg,3050 +wandb/filesync/step_checksum.py,sha256=IWp7I8WMobTTrZw3Ip2IBlBmvElaG_ihvsis0HW5QMM,4672 +wandb/filesync/step_prepare.py,sha256=CmGgLMbDtwgqC-ooDrLIfWP1NZWAf0Icl3sx3KNXnCc,5495 +wandb/filesync/step_upload.py,sha256=fzLWa_7SD-Jze5EWT8jpGqH6Sw0Qdv2puCbv6HHa7hg,10256 +wandb/filesync/upload_job.py,sha256=t-QgMheUcxFE8C9TIJ5Wp9Cl_4jlpCzCKfsRoPzrHbs,5493 +wandb/integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/integration/catboost/__init__.py,sha256=dxef0C9s9Xez_sF3sOqSJpKaSrAibAqMA_TpVUyIwac,127 +wandb/integration/catboost/catboost.py,sha256=DutGAM8LNWFCgN5JIXpwXFIrgmZtSmq46A-LlEWXlcA,5986 +wandb/integration/cohere/__init__.py,sha256=8yTJGhWznxEAxHYDY7oMghvsak_KqLngSLpqjR8ic3g,52 +wandb/integration/cohere/cohere.py,sha256=CyALJXyLDnic6ZRc8I4UXmR7hYqtIOCi5Wav12JcXd0,453 +wandb/integration/cohere/resolver.py,sha256=pQ2kbO-ynL3FKemSZeCdcsqusQrfB3n29znj-KdSiNo,13813 +wandb/integration/diffusers/__init__.py,sha256=Rdenl4seOrFR1_LrKP9P2Sl9_JwGnD3BC5bgHSWXRgY,55 +wandb/integration/diffusers/autologger.py,sha256=RsfHZsJQnZBGL9TSHHkspdWOTotveBcVPXGdeqL2YBE,3254 +wandb/integration/diffusers/pipeline_resolver.py,sha256=AYnMDpSwjSdf0mno9y43tDCVHhkv1UJYOtTxc5y4WFw,1834 +wandb/integration/diffusers/resolvers/__init__.py,sha256=07La9KIUpsOE3nNPnrcA4fESpmkeJFNhTI4FL8WPJ5k,201 +wandb/integration/diffusers/resolvers/multimodal.py,sha256=msmBLGd2-GDBx8doet0QfM-POPVhn6K4STqKgmAtHF8,29990 +wandb/integration/diffusers/resolvers/utils.py,sha256=30RyIDGzkVd_SAIbIy6WT0DYjhgcigJtMyIwHDvvqBI,3824 +wandb/integration/dspy/__init__.py,sha256=gC1FQ9NNB9Npa7zi_QmrzmxkfYvWreTm2AQtf0b2Ol0,106 +wandb/integration/dspy/dspy.py,sha256=Lp8JnIPXDwSVzHCX-JHs7aDUL8JmfoupD3tzwE06eIo,15526 +wandb/integration/fastai/__init__.py,sha256=A_bmZh0TaQlM4qkcDtZvwIdw8it2hdwR-WCjgLiz5Uw,9302 +wandb/integration/gym/__init__.py,sha256=oE_dAVdB6CbzS72UG7VOl7gKD3oYaDPVSlFdxz5AWJQ,2994 +wandb/integration/huggingface/__init__.py,sha256=Ui-JiM_yel2YZuS9U_LyReL3mhBZZSIUNLBODjS4l4Q,57 +wandb/integration/huggingface/huggingface.py,sha256=KYP0dCYERORLNE1Sk6jfp0krHF2BQ4q4qOlF-0H-vrI,443 +wandb/integration/huggingface/resolver.py,sha256=Atc0vwdPar5yKzTcJF6Fu2i7h7a6TX0W9B2HdeToqrI,7858 +wandb/integration/keras/__init__.py,sha256=8_vNA4cEbj1lg2pgMz-c5Iu1XJQtX9x5_vNGrv4NBdE,345 +wandb/integration/keras/callbacks/__init__.py,sha256=sY5AMC3x28iA815fqbqkkArtjctqG-m9N2D5FnyR0no,223 +wandb/integration/keras/callbacks/metrics_logger.py,sha256=gA2ui3ZCV4DyOEx5vQZOYRA59jPIlsZhCIC6IGvdy5k,4919 +wandb/integration/keras/callbacks/model_checkpoint.py,sha256=9gXBUwreT6XXsIsv5iyIHk80iLGecZFG-z-fGThAOLU,8529 +wandb/integration/keras/callbacks/tables_builder.py,sha256=01NGafVQJoJFvu6JmaLZsXIdmhYRkzY08KYyzOrxmEg,8881 +wandb/integration/keras/keras.py,sha256=pruhbbGr4mqRqzICwT1qppEHkaLR2c6Ufa7oiZN1XEY,44131 +wandb/integration/kfp/__init__.py,sha256=WhBhg3mQopGNDbWzGJ8Xyld8w3FAAvmP1G1Wtv_QaC0,136 +wandb/integration/kfp/helpers.py,sha256=yEVO9rrz27hc4nk3WwNL3v1aRAUlS-OlXMC8Rj0G1tI,1016 +wandb/integration/kfp/kfp_patch.py,sha256=mnpx9cayPj06adPD95t-ghNnVpIGbFMWbONvuSYi-CM,13204 +wandb/integration/kfp/wandb_logging.py,sha256=fYMImccQagOm6HB-dTV7O1LvH_1jJ-qrd7ngTUu7WVo,6167 +wandb/integration/langchain/__init__.py,sha256=afRoYH4bPOw6Tyrn9YvuYHzbSae97iQT-Ufi0puJqNw,66 +wandb/integration/langchain/wandb_tracer.py,sha256=oDtb_4-3P1xxBY-nXrA4h6-WnsdEYpH6AD1z5pen0eI,2253 +wandb/integration/lightgbm/__init__.py,sha256=ztwqeaVneF_f6ngCd0UowwksnNopl1bhSQN9hAeyoyU,7981 +wandb/integration/lightning/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/integration/lightning/fabric/__init__.py,sha256=RFhr2VkC0D8b6bAXUMZzcVzqDQaoOozfP_HOogAWi9o,94 +wandb/integration/lightning/fabric/logger.py,sha256=pLCuJze-pQKPQd5i9ejgcXA0qmkgRhhLPsMXCFDb-Sg,27233 +wandb/integration/metaflow/__init__.py,sha256=viDBeFSetM0I4BnUx8TEjJScHSDzO9mHHBijkZ1L9zk,263 +wandb/integration/metaflow/data_pandas.py,sha256=od7wFqM_j2Z1ra96wJl13wGN7M1WrZksEB6wQxCZRhc,1713 +wandb/integration/metaflow/data_pytorch.py,sha256=aFM7vNRM15hi9nu-HPXHdla2DPij6ZBBRHJWTgcu6Ko,1712 +wandb/integration/metaflow/data_sklearn.py,sha256=I3Ty2lUZUeFD1Qjbsg5tG6RKtt72VaHmyW9XOjpNG5Y,1753 +wandb/integration/metaflow/errors.py,sha256=wRKiojelW9DuPwhEiU7RzbV2aWIgHQPG7RXrcGiLgM0,347 +wandb/integration/metaflow/metaflow.py,sha256=fW-viHlO0FS7fKTwXMst-FdOnKiDUWD05phrpFMUqgA,9411 +wandb/integration/openai/__init__.py,sha256=T6x9lIFfw2NzOSi46wi3otY_-DSwyMgnIjMIOIod7iU,66 +wandb/integration/openai/fine_tuning.py,sha256=B9rrlNbZE6LI0bPWWGfHi9fMaMwOwLxbiIRVQEzOSLw,18509 +wandb/integration/openai/openai.py,sha256=zGD1kj6yjDP1IfAAYRcltuXD5Bf569CSnMHMf4rDm54,496 +wandb/integration/openai/resolver.py,sha256=KrndobRki0C9SNRL8SgPISea0-D7-1g8kvJMcFMN7SA,8164 +wandb/integration/prodigy/__init__.py,sha256=1-Hg98Y4T1kSNAbrlR9TUrr7dwL1Gxxa-Exu0fsfxl0,66 +wandb/integration/prodigy/prodigy.py,sha256=RQBptD6zlcyuHM5hHU8XUYUZG3smHPRYtgonmW5tirw,11368 +wandb/integration/sacred/__init__.py,sha256=Zzi-m0yRvtbyEgB0N67xSUAWZc1eYBz8Nni3e3OxcHg,5751 +wandb/integration/sagemaker/__init__.py,sha256=Eyd7rArw0IKGR2ll6zGiTXBi6y7E5nYYuyrVABt_9sI,360 +wandb/integration/sagemaker/auth.py,sha256=X8ns8yOQNikjz4_er-BAy4zdYMmWnQobHmHY1Ytc66w,1013 +wandb/integration/sagemaker/config.py,sha256=XLtB9n0cEUe3hA1N9OAA51E9rKco5UZGRteGv_9SCfE,1748 +wandb/integration/sagemaker/files.py,sha256=WaMBg5Im3GoPHM2AU8GnWEzqmm29EAOtsGsaE3nBaB4,89 +wandb/integration/sagemaker/resources.py,sha256=YBU_dgMt3kqhOCShc2Ge6FFPlwPBjb44fvgvkRMfvJk,1944 +wandb/integration/sb3/__init__.py,sha256=w_VX7C6qAE1vK7xb24JchQtORxzfpXvl6YmZHUIskJM,60 +wandb/integration/sb3/sb3.py,sha256=25fGTc_A1Zve-4MK_6IelAIaKYPg0pj4hB4ZWr0xekw,4797 +wandb/integration/sklearn/__init__.py,sha256=bDnzytR60EFQblaAZdw76WlJBmfG-O7NrKavIEY97Ck,861 +wandb/integration/sklearn/calculate/__init__.py,sha256=T19bKtuyTbRNzcIg3QbnRD8vm085QJXQFZfQ_STjrCk,1055 +wandb/integration/sklearn/calculate/calibration_curves.py,sha256=MFGIyWnK5b3Y5uHMHJhtI_a_ZKFawtCgzVYzlw9pf_U,3823 +wandb/integration/sklearn/calculate/class_proportions.py,sha256=ve4zo0F-qslaLoYo2oqzLA5-P4oiYKJNegwSPIFoeZg,2119 +wandb/integration/sklearn/calculate/confusion_matrix.py,sha256=9u6AieiGlq8NPyQIiybalJ4ZQRkPDAZMxFrB2Q_KaJM,2543 +wandb/integration/sklearn/calculate/decision_boundaries.py,sha256=jHpx3WN6_Dbv3C4mUrRegZYmi5NnT-8SJGKkXpNG4Qo,1087 +wandb/integration/sklearn/calculate/elbow_curve.py,sha256=bMkksPxpySAVgzITe-WECwkCXQa2iT6wH1v3po6G328,1454 +wandb/integration/sklearn/calculate/feature_importances.py,sha256=MyhIsOac3P43wDyExblzfFK54ZI2-FfAZpF9jK7is5o,2261 +wandb/integration/sklearn/calculate/learning_curve.py,sha256=c45MVKN9c6nusVs4qNDLr7H5C0OvfHX8W-6yRUQCZJ4,1728 +wandb/integration/sklearn/calculate/outlier_candidates.py,sha256=JCva0qVvamlYQ3KbvL50Wdd9M1-NV9mIos5O1pmjAAw,1946 +wandb/integration/sklearn/calculate/residuals.py,sha256=mkw4VH_JjK2i31zu3dliTWqnwwQa41PZzUMc3abpaHI,2371 +wandb/integration/sklearn/calculate/silhouette.py,sha256=efTHK3sCjLDiTrpnfrYg7AwhSPg8MTRbLPOehfnG2vc,3321 +wandb/integration/sklearn/calculate/summary_metrics.py,sha256=moxThZVRHVHMIp_A5mzS7_66FZTN3w2hu1wY_AgcKj4,2010 +wandb/integration/sklearn/plot/__init__.py,sha256=7RgC3Dakive4tLSmYOBTiNWtFZz-9AJG3AWtW00xcJs,1368 +wandb/integration/sklearn/plot/classifier.py,sha256=d9YsJ5Y3j8F4WikxkALruvqilzq5hqLIFuhghzyB48E,11753 +wandb/integration/sklearn/plot/clusterer.py,sha256=iV-UsSjc3VVUKboILhTUZRmd_rLm58HR-xda_5D-fAg,4917 +wandb/integration/sklearn/plot/regressor.py,sha256=mFLK7yhxfRE6MAoF2aQRPbS1hDJQtTxw8W-yD2uau8s,3943 +wandb/integration/sklearn/plot/shared.py,sha256=elvnlCHFlASs7Olnh-MVh7kvuuR89qvfmit2GmbeRMo,2763 +wandb/integration/sklearn/utils.py,sha256=xLJ1VnZkiHcNUxkFRnOoHHq5OKKB8JoVl5buCfIL5gU,5884 +wandb/integration/tensorboard/__init__.py,sha256=6WVdC0NhHePhIk1-j_yqm7jNjByevhfzxgotGsfGm-g,199 +wandb/integration/tensorboard/log.py,sha256=ErQjGuuqkiMNs3PCdbtycX-hDbAIe7cRtF5MuZyP8so,14076 +wandb/integration/tensorboard/monkeypatch.py,sha256=o-kduzrUmuFfapyFSOKtVtJV4auaut8jZg9p6gcO-UU,6886 +wandb/integration/tensorflow/__init__.py,sha256=_g9Ic-Y4giu-kQHge9Q3kIT52gvFOYVtGXUtMpmH1KU,97 +wandb/integration/tensorflow/estimator_hook.py,sha256=fi-UtjNZxUnDkzfbNP09iH074z5x0ItZiB4dkvwMGNI,1756 +wandb/integration/torch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/integration/torch/wandb_torch.py,sha256=2mXNijW9HrjwIM7qpFSdIOosiqaIVhcaUeL2BVAwobw,21526 +wandb/integration/ultralytics/__init__.py,sha256=omtrUwOg6GWKWyunhIPclFdQmxW8XIXQaPoF1Ai5WxU,340 +wandb/integration/ultralytics/bbox_utils.py,sha256=668VePOI5SuQj2frko8wovH-XZsEwU8s-BQd_yX-NcE,8023 +wandb/integration/ultralytics/callback.py,sha256=1TrtTPYfezTNV1_ugOfNEAhFiaochREnYk2Dx4gNQdU,21297 +wandb/integration/ultralytics/classification_utils.py,sha256=NZy8LA1GYf_qP9SptwIWw8FCaODteuLvbTAd7z_YzAs,3168 +wandb/integration/ultralytics/mask_utils.py,sha256=uzUtDjg4C9_4Z6_fWo0wiylauYvmckZ9JQn0afd1NJg,7080 +wandb/integration/ultralytics/pose_utils.py,sha256=oknJAy_eoyokJY1u03uk_MpR7kKrJIK4wSRBK5lJGyw,3709 +wandb/integration/weave/__init__.py,sha256=SW1X2J6az3XTbAKmxfi0O0R9qBuJRv7o9SITiJmiRUg,158 +wandb/integration/weave/interface.py,sha256=7PMQEDN9rOGMmhf_EKAsViYQ65wl6YccJcQGSLCJQCQ,1165 +wandb/integration/weave/weave.py,sha256=XLmRy3x-r0JlPXbX3OF8Qk6i5cg60L3S3Yx56ZqiROI,3555 +wandb/integration/xgboost/__init__.py,sha256=Pwmii-OI64seJjfwM28cS4BdkEN2_Ms_qMGdwp3QRn8,343 +wandb/integration/xgboost/xgboost.py,sha256=fAaoNixCOeUhxIqVTtrlu85oQu9jmx2aUCVp49oCDYo,6495 +wandb/integration/yolov8/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/integration/yolov8/yolov8.py,sha256=srryantZqO65pj1x52MiKD5m2SzdQc4nIsj4o1GPK8o,11371 +wandb/jupyter.py,sha256=NEH-o2QdHfKorPmeOHORLRq25Am64GBDwuAPFz-x7uk,17323 +wandb/mpmain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/mpmain/__main__.py,sha256=bLhspPeHQvNMyRNR7xi9v-02XfW1mhJY2yBWI3bYtbg,57 +wandb/old/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/old/core.py,sha256=bIIFlU3_E2hvrVCzMQk1VcwJtxbadbAZwDC0qsF4C2g,1501 +wandb/old/settings.py,sha256=Cug-8ImuV4_x8HFGOnlATuh0iJ4JOUfmymguPFqlXvE,6482 +wandb/old/summary.py,sha256=FZrkTyycUy6MRY1n4q7713rlTV7h-CMr8_kNoeb-wzY,13856 +wandb/plot/__init__.py,sha256=M7sOlxO_hYYV64ZM007B3OjmIqSxF8ToY1aU0DtC-dI,863 +wandb/plot/bar.py,sha256=gH29ianBYs-1iV8jjuUlWO6KixXA8Bq_kz0nXmlLmQU,2036 +wandb/plot/confusion_matrix.py,sha256=cjRFhh6WXVvQzuuhieKZ0fVcaMDLLevkrNZlofWiXds,6339 +wandb/plot/custom_chart.py,sha256=GzLoUoeCMg1bdDKkCWDFjNHKsYs_bU52USVSEFshbWg,5010 +wandb/plot/histogram.py,sha256=aZqM5gLaVU3BbFnngFfchb-2QFSYc32TtEqcBofhaTQ,1701 +wandb/plot/line.py,sha256=LCGgQKhvDwEsypsUMVuGMKKi_AmC716mNh1p1hCWljE,2369 +wandb/plot/line_series.py,sha256=Mr_UgVS8VAWytDZryQ5R3rXc_K8war2B1OZNaLJFYog,5558 +wandb/plot/pr_curve.py,sha256=fZDATHJHK10vCLcZaUSmIwSCfr8uoMVfvO4njLTzC0E,6602 +wandb/plot/roc_curve.py,sha256=rzKwKKC-ZQzkKLoUZ3rKmG4-oRF2sZcRkcsQEnKRZWM,5619 +wandb/plot/scatter.py,sha256=ilFNDknfPL7qj6VRCTKVTeq3-8agS-3ogU83UpP-gUA,2045 +wandb/plot/utils.py,sha256=Q3SN29syvRBsb6dpcg11-Km6PFT4uQPOEfhtNrG0pmQ,6769 +wandb/plot/viz.py,sha256=ni1MTkt3k1TxSqArdIR_sVcD1cV0j9eKmJdX0QlBcOM,943 +wandb/proto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/proto/v3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/proto/v3/wandb_api_pb2.py,sha256=Cx8yDcK1sbfzwFoycqdPcMnmr5CdTYLmoZeuIHIzx38,4739 +wandb/proto/v3/wandb_base_pb2.py,sha256=_nsr_HW4Fdz62-KiXGo6Dw0_9bwdXz07auZkkH2QfSI,2355 +wandb/proto/v3/wandb_internal_pb2.py,sha256=Dc2HP5vPqW5fZ_otzvo8B_cgk18vawzyw05qU5vf3BE,118029 +wandb/proto/v3/wandb_server_pb2.py,sha256=33NuxrfmMp_vPnyFilHrWJf3S15CBRpGy3vinjt-okY,14710 +wandb/proto/v3/wandb_settings_pb2.py,sha256=n2uk7g8hC-Gf8P9HkXimxw5e1viEu3Zs97uww-IEi50,21738 +wandb/proto/v3/wandb_sync_pb2.py,sha256=NYMsp1c1tZlIRlIcmG465LF26YXxoMX9IogiShnAIrQ,5740 +wandb/proto/v3/wandb_telemetry_pb2.py,sha256=zK5kHr1qp4sabaj7MGU990CekdNqU2pPu-iFW0U-qrA,14594 +wandb/proto/v4/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/proto/v4/wandb_api_pb2.py,sha256=LBLIhrKFNnQONGOOu5WQMqAdYrrLXWm2-RhIxwv7Ezo,2559 +wandb/proto/v4/wandb_base_pb2.py,sha256=El5lnZMRpkc1W4SlZgeKoDPqWiJau0BG8QfigMMt6bM,1422 +wandb/proto/v4/wandb_internal_pb2.py,sha256=W66fw_cykAAx3IpimKBwwWSgGuBT-9SsGCVNMNrum-E,54327 +wandb/proto/v4/wandb_server_pb2.py,sha256=h7_ErrgaVqnjTH0RpMZQY3Ywr0syLRCaUMHnidjSeYY,7056 +wandb/proto/v4/wandb_settings_pb2.py,sha256=KQMS3AFOPYMhicukGPMynEYeAwW9nD2O5DoNq99k-b0,18113 +wandb/proto/v4/wandb_sync_pb2.py,sha256=Q5E_QGMoa0U7cLgjpAP7MBZsGCHUJZ2MDKc-pjA_1Q4,2899 +wandb/proto/v4/wandb_telemetry_pb2.py,sha256=JD6TcSwHajE1zO63IsYHBODp5JSXitoCdB45-l1oQ0A,12041 +wandb/proto/v5/wandb_api_pb2.py,sha256=2Dkcn4oAvZxRj1aJ5OaueICCN27XzRyRWISXwzqFGn0,2781 +wandb/proto/v5/wandb_base_pb2.py,sha256=u7VVWdExJ4WDkYNMV-xvWvyQ-NTIbAqToTKtgEqt_Lg,1572 +wandb/proto/v5/wandb_internal_pb2.py,sha256=4vobxBFEgVcvtA54R4CefxB5iYtX3kKaa1FSsB1YLF8,58698 +wandb/proto/v5/wandb_server_pb2.py,sha256=PnUMsWWwoSI4v8677z8hWbYmu9B908dFl_JHoOQ0uM4,7566 +wandb/proto/v5/wandb_settings_pb2.py,sha256=Yh9U7wsMDk6MLo2IWVyiID7esO8egiQVHxFkRCq3z_A,18469 +wandb/proto/v5/wandb_sync_pb2.py,sha256=f_BPiREv3iL85U6DwqRwubxBGIdFGSKDcK0IqDPv7lA,3169 +wandb/proto/v5/wandb_telemetry_pb2.py,sha256=zl_Thc55nmp-ukAQCkSckEgW-9MkBQJFWZEx_sui2D4,12311 +wandb/proto/v6/wandb_api_pb2.py,sha256=WcMhVjNrrnsJ7HB9sH3ogZ4JVNFUwtydDRPhTZlsD1s,3029 +wandb/proto/v6/wandb_base_pb2.py,sha256=33R_7uY3eNy3D-UOpqU9NqkKOXxxBi-qCBRvFygjB1Y,1821 +wandb/proto/v6/wandb_internal_pb2.py,sha256=LV2RjUMqTc20ZyJEwhlexkq92MngBl891iW7yQslQ1I,58951 +wandb/proto/v6/wandb_server_pb2.py,sha256=653uvl0HXdl3U1ZOpI5Hqz6LCmdxhfld_FdUtun7e70,7817 +wandb/proto/v6/wandb_settings_pb2.py,sha256=o4B29qYvWpT-GhAvhE9s2imjDBUMcjl1nIXNQnrTuDs,18722 +wandb/proto/v6/wandb_sync_pb2.py,sha256=xQ7FRFYnFguM_0ZZRaZU19WtgqQMBTU_djwLTixgulk,3418 +wandb/proto/v6/wandb_telemetry_pb2.py,sha256=ggidm5T1n3w8UzWusXy825TtWmUd5GUJ9_jQV7nrjkA,12565 +wandb/proto/wandb_api_pb2.py,sha256=MSQh2A8g7MzBm4F4cqpP_v2f99xDNcu86yLkn_A2PAc,608 +wandb/proto/wandb_base_pb2.py,sha256=h09-9fMDbMC82T3jWHzmh8MB8llZ9o9za0uBOF_bL_s,385 +wandb/proto/wandb_deprecated.py,sha256=HGCueNNxy04yz-iUsCeg3-XNKlFAVMdwyiy3muDlmtk,2776 +wandb/proto/wandb_generate_deprecated.py,sha256=Iyf7PwIL_2B7XohrckYLbjjT09lccwbqknxtWelJpJM,1002 +wandb/proto/wandb_generate_proto.py,sha256=BRtjAvvHYcvcElxzBOdcDIpjWL0SktQ08a9C68yundQ,1325 +wandb/proto/wandb_internal_pb2.py,sha256=hTpbURqJyoNcwHD7o3k0ofmKgZdmKEYUm9sBqLHa5iA,628 +wandb/proto/wandb_server_pb2.py,sha256=82Cf27PnDjHbkQT_-ZPjQyI0AVYd9YWoAlABrGPD-xo,393 +wandb/proto/wandb_settings_pb2.py,sha256=Wba7SDYTKy-Sm2sYrTmqwYH6sBvdD9ogE1i87lt_85A,401 +wandb/proto/wandb_sync_pb2.py,sha256=CjyWIcWc6tM1zgOPwN8OssJebfoUou6QlXVDarwnt90,385 +wandb/proto/wandb_telemetry_pb2.py,sha256=Y9uzA6O9yUJ3HKqYOi86hg-mZ49NtSzQ_XRFSCONolA,405 +wandb/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/sdk/__init__.py,sha256=N-GTAC0AzbZF2J8RzB33DTmYk9u-jubllCwvhWrPgsE,813 +wandb/sdk/artifacts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/sdk/artifacts/_factories.py,sha256=JXGLyZkdqt-4jmyzZkQiYbhSa-hUpj5HWR29tgCWY60,833 +wandb/sdk/artifacts/_generated/__init__.py,sha256=_Y1clyDgGwVBy5egNuYkwTiiDLInKrPKON9Kzr59NVI,6914 +wandb/sdk/artifacts/_generated/add_aliases.py,sha256=-DCPnnu6-ydKyALEd8E0pxDXGGaYriHj-ktUixML4Lk,395 +wandb/sdk/artifacts/_generated/artifact_by_id.py,sha256=elaALkxo-D9zkv7oBH0ig1_uc7iFaNxFeGz8XhpHMcc,320 +wandb/sdk/artifacts/_generated/artifact_by_name.py,sha256=upCWxMT1qIfUfyEsfEwff3s77sI6dlCanNwzJD5MrfA,447 +wandb/sdk/artifacts/_generated/artifact_collection_membership_file_urls.py,sha256=ILoPMihh3N7uN7Nn4MLwI3TNi9OkB1tTg9WbHvudU18,1382 +wandb/sdk/artifacts/_generated/artifact_collection_membership_files.py,sha256=iRG2YDjHIDgG9_O5sI4ltkJX6ILCAGJgEKJA_ImO7wE,1343 +wandb/sdk/artifacts/_generated/artifact_created_by.py,sha256=hvMced5PSNV7uUwbeyhACAtgXMF7WBSZ6aUonZJimEM,1216 +wandb/sdk/artifacts/_generated/artifact_file_urls.py,sha256=sDJYJnPVmHuUPbxYSApMmfo_Juk3ojoJH7R9EVrydU8,458 +wandb/sdk/artifacts/_generated/artifact_type.py,sha256=TUxnlVUX50cQtLBsp_oVj4S2vMLvWpZNbe_wuKNo06Q,686 +wandb/sdk/artifacts/_generated/artifact_used_by.py,sha256=M3IOar70stenLIQT3Xfvl8r0-lQTcxZFm-CS2q_UwkE,1080 +wandb/sdk/artifacts/_generated/artifact_version_files.py,sha256=-yPP-7wlB-jgv4BKrIm_7NeCucCcDwGvOb2rZSphUsk,925 +wandb/sdk/artifacts/_generated/artifact_via_membership_by_name.py,sha256=kLL8GbZkAxFMr05vZAAQpnUMg1uYtG0kiquZ-p7vb0U,634 +wandb/sdk/artifacts/_generated/create_artifact_collection_tag_assignments.py,sha256=jvM9wBU7gfqbIyno7IvfQAh9Jbvp_bMFFDKph7qjpe8,1034 +wandb/sdk/artifacts/_generated/delete_aliases.py,sha256=0lDMciBaXsJR2XSjFO0_zgTaNRc0gPrBMwmOJLogD_A,419 +wandb/sdk/artifacts/_generated/delete_artifact.py,sha256=ZmRLfOb5CCB-JhNVWBWKDyalaiyGdPEgVk6ISRY6sS0,595 +wandb/sdk/artifacts/_generated/delete_artifact_collection_tag_assignments.py,sha256=BHJKnPZbWcdHLg4-uQ2V51i4OM2voSUdovfYM5Zv7JA,642 +wandb/sdk/artifacts/_generated/delete_artifact_portfolio.py,sha256=lsGr5vO2EiySHEOZ-KdWdaEXdLO9-YEavXlbuIKanYY,991 +wandb/sdk/artifacts/_generated/delete_artifact_sequence.py,sha256=UGA_ymMNL8t1Bu5ThfKRl89B-elsUYhCdMa9_dGkUXU,979 +wandb/sdk/artifacts/_generated/enums.py,sha256=pMqG6Z705_yedZaa0u1K4NfsjXvIv48-zgiEJXIWJHk,442 +wandb/sdk/artifacts/_generated/fetch_artifact_manifest.py,sha256=SadWxTSiAdqPNsFLUjsd9TfV0lAIHQ3cKsQCLoXI-SY,1055 +wandb/sdk/artifacts/_generated/fetch_linked_artifacts.py,sha256=17-3j0Tmiv2D1H--RpdE1AoRC5sHnw6DoGpavaEZ52U,2148 +wandb/sdk/artifacts/_generated/fetch_registries.py,sha256=jYKGKjdVEJKrO-t1O1bIUSQ5-sug95_Xc3rQ1UeZOyA,722 +wandb/sdk/artifacts/_generated/fragments.py,sha256=M-XrXpKIrW2HaakPTTf8PkpI5h3LJDQio9VcIggsoyw,16508 +wandb/sdk/artifacts/_generated/input_types.py,sha256=nq6YCF9DLpvIXqcJ7HiJ1MJgmed69sFJUcU7hV5TRSE,1514 +wandb/sdk/artifacts/_generated/link_artifact.py,sha256=3yMxhQe4ZZ_CBdMjERA7su1GVVYx9rq55xyi9Gmvtt4,666 +wandb/sdk/artifacts/_generated/move_artifact_collection.py,sha256=Jb4kOxyoNinSY_UUuqskBG3uuFN0KanevXOgXb4Q8mw,962 +wandb/sdk/artifacts/_generated/operations.py,sha256=KGNon8MRnYS9Si30B1klA6k65H5-hWSaQsCOx41KtpA,24130 +wandb/sdk/artifacts/_generated/project_artifact_collection.py,sha256=sMfS5nueyZz_EEQrdo2J7h9xo346jsAglAofHnqz1-k,3393 +wandb/sdk/artifacts/_generated/project_artifact_collections.py,sha256=4vgbbsMeblXtbn3ebloXDGz20x5JRxzwZpZhci5x--w,865 +wandb/sdk/artifacts/_generated/project_artifact_type.py,sha256=NOYXo2UYV1cslcbjSi0Q9167Iv5iksj9LS1XzcbsbXM,543 +wandb/sdk/artifacts/_generated/project_artifact_types.py,sha256=rGbW8kK66zR72kLklWuAcDnzmp7KkCFTXLvsUctlewY,542 +wandb/sdk/artifacts/_generated/project_artifacts.py,sha256=noPODtFVotO69VK5oHZeU8NF4aYLKsPPRHOtNMPhn38,1128 +wandb/sdk/artifacts/_generated/registry_collections.py,sha256=yBZTeGp4hCOOGByxe7XH3WWFpDNiUuFg047kdoDpflw,835 +wandb/sdk/artifacts/_generated/registry_versions.py,sha256=0vYKEQSWAp6oXcTorhbI3ZbukFUCa9VAGBxmI66Q-Q4,805 +wandb/sdk/artifacts/_generated/run_input_artifacts.py,sha256=fYAqLW9jaxgk9INzvFUYcqe1FtJFEvNZi_JFfvNCGxE,716 +wandb/sdk/artifacts/_generated/run_output_artifacts.py,sha256=XW83javqYVaQMcd2Y5Z2QrXvMdZPx-3aQx9KCB7qrFA,728 +wandb/sdk/artifacts/_generated/type_info.py,sha256=guEQnoK_vLBeWKLW-CZzeMtOtan9dmaEDrUmfOrSgIE,360 +wandb/sdk/artifacts/_generated/unlink_artifact.py,sha256=ypYTHlZEPNBMljgEqZ_0W5l8bAtJYwAYnUJueyjSCx4,571 +wandb/sdk/artifacts/_generated/update_artifact.py,sha256=FYzYORox9ZaQA_QI-bBYJnytHQeq9DrrRgOj4iYvlQc,540 +wandb/sdk/artifacts/_generated/update_artifact_portfolio.py,sha256=tcEUGOoAE8pqr27tCwJoA9-Z95s_gNlIQkDCaUiFqkE,978 +wandb/sdk/artifacts/_generated/update_artifact_sequence.py,sha256=dwkvOYLgSbTSuwjZKvW9DtlW1Q78YPgQnayuVIA1P0U,966 +wandb/sdk/artifacts/_gqlutils.py,sha256=jwbK_lb2VjVyqc8S0ZM41zPvscM2sZk7VY_N9GHEt7k,1549 +wandb/sdk/artifacts/_internal_artifact.py,sha256=Jx79CVB5xfuJCXTmmRIN8yegqknsqKNpKJa-WJmCO3w,1975 +wandb/sdk/artifacts/_models/__init__.py,sha256=8PpmvWO1KzNWPObw2fbJR3F1LMOKwRgAvrEfxsqY0rQ,107 +wandb/sdk/artifacts/_models/base_model.py,sha256=JNs9Ain1-iX7ZHDkYfEFO0DUZJEO-ryNoncK5jCgAMM,761 +wandb/sdk/artifacts/_validators.py,sha256=4axMMJd1dCi6GdHLNuhp6ZBuWzPZ7WqvKkroCZ5WXks,12059 +wandb/sdk/artifacts/artifact.py,sha256=4VCWeI-8Oz8mKO51d86MCFuvquQJOoo78i095mriYiQ,103037 +wandb/sdk/artifacts/artifact_download_logger.py,sha256=bS4v544rFcTOTRTXMUSd6tfLUXzezbIogDpQmZUjah0,1537 +wandb/sdk/artifacts/artifact_file_cache.py,sha256=ZsGRP2hIH4XlHcbpGqR4vlZRIf7uzDBx6OT2RBkjFK0,9989 +wandb/sdk/artifacts/artifact_instance_cache.py,sha256=atSUDvKjkp6by6QY0w2FLMrFUg9UKc8TIrMVWGauV64,501 +wandb/sdk/artifacts/artifact_manifest.py,sha256=ZBdtazpTXt0X_8F4y6O-SDmrTY69hCp0HckCXbnZkII,2534 +wandb/sdk/artifacts/artifact_manifest_entry.py,sha256=d0x4rCZrlYNCLrmaXb8lr9JKL3wg7Ni6iBAUb4dG-LM,10603 +wandb/sdk/artifacts/artifact_manifests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/sdk/artifacts/artifact_manifests/artifact_manifest_v1.py,sha256=nQt76-WzeO1L8NoMc7DAuwWE4LhNrgL6Fn9eaOPChX0,3497 +wandb/sdk/artifacts/artifact_saver.py,sha256=RwJIveLn87UE3_tk5zAJ8ry-TAvxwTS94yNAChwhLAk,9654 +wandb/sdk/artifacts/artifact_state.py,sha256=JbPVinN8Vaq16IKdPtFmiYbBdBtCKLDMVU_ViMI8QOA,272 +wandb/sdk/artifacts/artifact_ttl.py,sha256=kD_JfKVcQzZlif6PF-WKnemucWPwcISq8hX6Y8lKtY8,122 +wandb/sdk/artifacts/exceptions.py,sha256=AoSQ-g92IyvBY5h_sMTbE5ReSiw7Yu4eMwdr_zLPB-o,2357 +wandb/sdk/artifacts/staging.py,sha256=QNbPQhlW-w2liQ_5ZgGJaxsp-prPrOAZ1InmCx3N9Zk,890 +wandb/sdk/artifacts/storage_handler.py,sha256=9-04vxaDdHm2WLIc00m_Xf_3i35tzdCxucRSKJP_RHo,2045 +wandb/sdk/artifacts/storage_handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/sdk/artifacts/storage_handlers/azure_handler.py,sha256=YckDbo7LGSlE8UapQgFQwDZia_9caQzCoyhaC1yUnI4,8472 +wandb/sdk/artifacts/storage_handlers/gcs_handler.py,sha256=jzDvXVccQknkYJhMRwCVvKANSU5f0ArJwAyLITOCims,8574 +wandb/sdk/artifacts/storage_handlers/http_handler.py,sha256=2PeaQIIe3EbT2SDeRpi8w2blFX68ThnJil3F1nC46j8,4044 +wandb/sdk/artifacts/storage_handlers/local_file_handler.py,sha256=4gIlr3lnx2NKNODGR90aCaMofdkhXzTywJ5bi_b26aE,5536 +wandb/sdk/artifacts/storage_handlers/multi_handler.py,sha256=GAs8eUy2Fo_9kYAmi8usxc9esuscuKLfHAfDpt-XFQw,1898 +wandb/sdk/artifacts/storage_handlers/s3_handler.py,sha256=o5Bt8mc0IEgBd01pZ3UgHkUGsJAxNw9XXGUCUQHceZs,12924 +wandb/sdk/artifacts/storage_handlers/tracking_handler.py,sha256=Ntb2k4dcVnta47DAr-CzVuXihjZ4YS8pl0SlobaHvSc,2476 +wandb/sdk/artifacts/storage_handlers/wb_artifact_handler.py,sha256=b1TreDrqMucyptvwaNSvGAGnLFNdF7a0feJm6gTVYts,4992 +wandb/sdk/artifacts/storage_handlers/wb_local_artifact_handler.py,sha256=deRwMl0_APHirSi1oHMvMVoTfrKPmD24xF8MSGYfdr8,2604 +wandb/sdk/artifacts/storage_layout.py,sha256=JeI2uVqreJynIVNhFVvh7Acm-Wem25ueFQvcxg_svZU,109 +wandb/sdk/artifacts/storage_policies/__init__.py,sha256=bgpWKElL-3iHcLO8pF-L8oezG-dQbp_6vcCYo7CEFAU,226 +wandb/sdk/artifacts/storage_policies/_factories.py,sha256=r5vJ-TJvGnGNMviWozdCQjl0r7INizjU4eC0KPfF45I,2249 +wandb/sdk/artifacts/storage_policies/_multipart.py,sha256=6ZcvjvWVVjS68Fsej6yISjmShFz0_WmI0dOgXUcxT_o,7177 +wandb/sdk/artifacts/storage_policies/register.py,sha256=xT7kUxubtLqyE-9S6U9E4mCo1PtXl0ZEJ6gVQiS-kGQ,49 +wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py,sha256=tkna8iTT-DnO5sZlx81Z1bXV_Hl4-MbldJOCyb4nm80,12900 +wandb/sdk/artifacts/storage_policy.py,sha256=fKCxBn-kSH-iZvB4dGkVg5SwiUPp50SjZBUpsHSlM3M,2478 +wandb/sdk/backend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/sdk/backend/backend.py,sha256=yh3ldxlKXXIXGk7vKDwEu0OolI6MD1B2LTQSu6sBEg4,1310 +wandb/sdk/data_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/sdk/data_types/_dtypes.py,sha256=rzcYQjkQeD9Xb9zJg-5w8RWs7LEMcXf_167zKWgXfiE,29988 +wandb/sdk/data_types/_private.py,sha256=zp2NRarTlIq4Hk3R2xp7j_qPGNzBMnaGHrZUN82shaY,299 +wandb/sdk/data_types/audio.py,sha256=-Onlt-II7XPkfUWoR4whI920Y8q5otYkfgxSg-aOSjI,6508 +wandb/sdk/data_types/base_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/sdk/data_types/base_types/json_metadata.py,sha256=oKpimndUQvDW30n15tb2pbyyyKhhWwm8wsYNBEot060,1553 +wandb/sdk/data_types/base_types/media.py,sha256=usjTlGJTbXWOx0B5maCDs-YtLz9Kctl72EFNi4vcCYI,12568 +wandb/sdk/data_types/base_types/wb_value.py,sha256=1HMmyMv7khaAu2scP9GYsmXbr0XAw75W2-RCNK5Nm_o,12067 +wandb/sdk/data_types/bokeh.py,sha256=V7KQciT354M7SSvss3IZui_CNoto7Qiky35vXBXdWk8,3033 +wandb/sdk/data_types/graph.py,sha256=c8P0aN9sNzS6ebccantN4kl_wK-xRczrCBIn4JnJs2A,12845 +wandb/sdk/data_types/helper_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/sdk/data_types/helper_types/bounding_boxes_2d.py,sha256=fuKAx8gtQWQvOQr32_zISLKLOIxVVDQKUyyA7VSxR3U,13794 +wandb/sdk/data_types/helper_types/classes.py,sha256=cg-AwUgULbb3T_-ptOdliXGvYjXZYRj8rvSmT7fl9ds,5520 +wandb/sdk/data_types/helper_types/image_mask.py,sha256=g2xFh0lm5w0-iafiXw_8rpe0W2F1-wTD4RV5W488bM8,8875 +wandb/sdk/data_types/histogram.py,sha256=RieB8Y0h-XDY9EBr1fbNdK9F2ei4bpPw6Gn1OZC2v8Q,3369 +wandb/sdk/data_types/html.py,sha256=S_5Nk874c6ALQWaNK1fJBg2sehD-lnsnqZerTU6Kh2g,5019 +wandb/sdk/data_types/image.py,sha256=TZUTrCK9IU7wHXaTPVmiZGK178-vI_29pmxC_-xcwLs,36193 +wandb/sdk/data_types/molecule.py,sha256=9eWp5ZeXF_QCZk1P-IRRTPxCDF2VSejQK0wIZ4q17ok,8972 +wandb/sdk/data_types/object_3d.py,sha256=Loou1Y8w6BVvkim7VZPGt4XrrRr1-iyTQyPmoXUE5G4,19176 +wandb/sdk/data_types/plotly.py,sha256=oqLHe4b9boJR0_crd7AFgz9AsJz9VCw_fGVCKPK4VVM,3357 +wandb/sdk/data_types/saved_model.py,sha256=pj1jM8ZeDxTY0YEpDv0zdC1bYUD7Z8rHxn7KGz2ViTo,16224 +wandb/sdk/data_types/table.py,sha256=AWnTuX20tFSpvQ0Xg9O6XXE0gkQI0L0669UHpg-45B4,54445 +wandb/sdk/data_types/table_decorators.py,sha256=2D0dlnv7sBvrHyYH61LCCMhowmg8CENqyvth9BID-VQ,3727 +wandb/sdk/data_types/trace_tree.py,sha256=zPWT-tofO4YqaFhB65SnqFMnpXgTwRl48apU04y6VeQ,14838 +wandb/sdk/data_types/utils.py,sha256=s9aVonN1_vZH7o29lHJPGOMKZkyjroAs3VkT6ENlN0g,8962 +wandb/sdk/data_types/video.py,sha256=X-ruhlMdoQBjvWGR0IvKBgN5aj3z2YQUqr1PCYIjrzU,10605 +wandb/sdk/integration_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/sdk/integration_utils/auto_logging.py,sha256=90aPWkkZM9KPb4aYel1c-Yr8YV50cFBt1bVAAc2z45s,8205 +wandb/sdk/integration_utils/data_logging.py,sha256=DDFtDaUu50aeTTgxCHHYd2f85guqqf2xfEOburRlwwQ,19533 +wandb/sdk/interface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/sdk/interface/constants.py,sha256=NJNBFr7LkshLI837D3LU3JuEURLzBwza9H-kxcy4ihw,60 +wandb/sdk/interface/interface.py,sha256=CpNlwX5w4g5nh_gFp4zckjtyU20snaEUJOnw6lb78gE,39451 +wandb/sdk/interface/interface_queue.py,sha256=YC2WAFZWhA0ih4eNFTi6AFRfwaDN5D6ECQGvP_Bkk20,1421 +wandb/sdk/interface/interface_shared.py,sha256=aEo-2DqOBR6Gk6tZ3xbSFq1X-Bn-KqfbRKbRfWup4s0,17809 +wandb/sdk/interface/interface_sock.py,sha256=7Yy-90qPczoy2pe9npaJSlboHLKLCGnJ28m5SKXuwxI,1685 +wandb/sdk/interface/summary_record.py,sha256=-wDv_zLYueeUY8IzyF9NPYnYwF3iBpUWbrsGcHAd2YM,1677 +wandb/sdk/internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/sdk/internal/_generated/__init__.py,sha256=mEJ0eZ7YAZRsOPDX8PU9Ts_ViO4FHoS8fzcgLvtAF6c,200 +wandb/sdk/internal/_generated/enums.py,sha256=vPRi0TPBkZS4D4to-WzhesolsPj3a2B1Okr1xmjRy5k,124 +wandb/sdk/internal/_generated/input_types.py,sha256=vPRi0TPBkZS4D4to-WzhesolsPj3a2B1Okr1xmjRy5k,124 +wandb/sdk/internal/_generated/operations.py,sha256=e_jeUmwP9mnaFe2WUssdmKw2AeN-Iuf3j3FkG9fDnUY,245 +wandb/sdk/internal/_generated/server_features_query.py,sha256=WGjefMl-SDR7o7OVtJk7xKEYICBFA2wceGMXbuyj5QU,647 +wandb/sdk/internal/context.py,sha256=dnmsKEoQ2xOtmXM5OBVHdyGO3XdTQrqjxfb1lRADSTc,2518 +wandb/sdk/internal/datastore.py,sha256=cbTtq452ZGSbzDVnvEJ3wdVTTCkCqSEmHullPYOnEvE,9774 +wandb/sdk/internal/file_pusher.py,sha256=G6JLdqasdMMeNV6wiHUVgv3nGXiCrXFYwGOa7vOfTR0,6023 +wandb/sdk/internal/file_stream.py,sha256=dLzl8RnbQQR2taSgcLff1Br5XghCKSPgxSxkBgzegyY,25604 +wandb/sdk/internal/handler.py,sha256=ZmZrzaJUw-caclqGmOfQMRmv5s5ha9eDf72pcOFOcZc,31549 +wandb/sdk/internal/incremental_table_util.py,sha256=SwcLG366undgZ5nw4xfyVZ5BZS4mAUw1K9HfmenFbqg,1490 +wandb/sdk/internal/internal_api.py,sha256=MqU2e8cUXKGHsPsQEsfmjZVA8Y5JUiu6FNbuJv7P4qU,163977 +wandb/sdk/internal/job_builder.py,sha256=4HqAtpjuSFd01vQBzFTAcxL86WRL10v1PzuwwqO2TXg,23470 +wandb/sdk/internal/profiler.py,sha256=6IJ18j5saTzX_GrGUm-qKLAYF8tW_iQMCP_jUqbaVn8,2391 +wandb/sdk/internal/progress.py,sha256=526DYzhDMnfBNAI3TN0p7R487c-0W8PBT8_Yft334Ro,2289 +wandb/sdk/internal/run.py,sha256=MD3D-o8Zo9hYpd-v9UlslTAuvCJHD6J1gBnZeZoD4o0,624 +wandb/sdk/internal/sample.py,sha256=USAWLhEeP83J13BVOSIy1Rb3kDDKTK9kzR88SlhO7dw,2470 +wandb/sdk/internal/sender.py,sha256=gR2Exzx9OSABgvekRPfqAx6B_AnqjwFkRGu1rLNO2WM,65240 +wandb/sdk/internal/sender_config.py,sha256=wZkz25ikhSoO9FCb-H06QdPKGCkZzdyXXa1LZEdpu2g,7133 +wandb/sdk/internal/settings_static.py,sha256=qCOWjgCRdCK-ytjNAZmMMPJYKfxfZo1HBWVqRE1SUnI,1175 +wandb/sdk/internal/tb_watcher.py,sha256=wqlBUrQKyTYQth8q_ABPb9MNMNmeatyv59_Bq5pCtaU,18676 +wandb/sdk/internal/thread_local_settings.py,sha256=UqD6kfjsy6mvxIWcjhd-vJWkNRCeU1whuRe_-VGIklQ,527 +wandb/sdk/launch/__init__.py,sha256=moXY557JibPbvE1GSSh3nGFiyelTVaJZMdFSv61Dn2k,399 +wandb/sdk/launch/_launch.py,sha256=V4CvEVWawFIZaxwFGa0KnBCsVisZeaaYH4wwaeWAnTY,11815 +wandb/sdk/launch/_launch_add.py,sha256=OD6JPJN3dCNz5FZ_8MzgUOBAWGRtcgMz8UYoeV26g9c,8795 +wandb/sdk/launch/_project_spec.py,sha256=n21vwzY3leq0Fi7EjpL1RFIN_c5Y6JRgpQCzvGbRi1Q,21631 +wandb/sdk/launch/agent/__init__.py,sha256=nwGHzJptq87cXCSAJi7Wv2ShL-HZwDgMo2aFC2Rh20w,85 +wandb/sdk/launch/agent/agent.py,sha256=D1b7FI-m_1GiyuLxcQ2Sc_L-IAqZBKsZP-JaDR3XoNg,36719 +wandb/sdk/launch/agent/config.py,sha256=eojwtkeke7PEiPJW2EJfJSRNjB2E0AzL7mn3fCRNY24,9498 +wandb/sdk/launch/agent/job_status_tracker.py,sha256=yIOlPfsEi1vEQZNRnxZ9hmyW1ZjuXOf4LYzlruC6_DM,1830 +wandb/sdk/launch/agent/run_queue_item_file_saver.py,sha256=o8I_TV-HRz4Pv3dwYkscd5u2_sHEWcsWmplpab4Ic8w,1308 +wandb/sdk/launch/builder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/sdk/launch/builder/abstract.py,sha256=OveMZmrZ-L7qkS5ex0_5RChnSaLXPLfaCdL6jb-zmpk,5076 +wandb/sdk/launch/builder/build.py,sha256=Nm-KUxZBIQOIRhywCoiBcGldXL4SDJLSD8KoR7m1SDs,10813 +wandb/sdk/launch/builder/context_manager.py,sha256=p8rVZj7TDolsU4DEY0HRTgXBt_ht31Bv4GODMRxQSms,9618 +wandb/sdk/launch/builder/docker_builder.py,sha256=0HbPxwWuCHXogmIc6o18nvBaHIx-xcWOQfHnfOJIgcI,6316 +wandb/sdk/launch/builder/kaniko_builder.py,sha256=bYiKdWfFixoY3RVK8QeJYQBZkzP83qKSXS7TFR6Cq8M,23963 +wandb/sdk/launch/builder/noop.py,sha256=A--UUfqkcZxCpTmBKbL6JvkE_emcJRXIkAhAfVv5u7k,1900 +wandb/sdk/launch/builder/templates/_wandb_bootstrap.py,sha256=AuI7GXUoW3MdPWVTkSjhxZQTmW4Rk_nTNllHbZzfoDQ,7316 +wandb/sdk/launch/builder/templates/dockerfile.py,sha256=X8D6n6tyyh69oNHeFiUp-d8ClEtMpHSjwk45N12CR68,2264 +wandb/sdk/launch/create_job.py,sha256=5wwwNY2fRA5tKHaxdFpmVXJo5vhpyKiYEGjp7PutpAE,17760 +wandb/sdk/launch/environment/abstract.py,sha256=FpQSsLqJ_PpU5Jy25YpZnWTiN4_6cihmynZFI1isuE0,951 +wandb/sdk/launch/environment/aws_environment.py,sha256=9Os8Q6wXIq7ouXUWF-58lC2r-ijeZ83BRu6H56p-34M,12522 +wandb/sdk/launch/environment/azure_environment.py,sha256=2Yt-NVD-_eXR73-pp6CAneKvb9T30x6eQU-YtHO1Tko,3885 +wandb/sdk/launch/environment/gcp_environment.py,sha256=OiL1M9GAGF6du1TlyEEmxSa1ALSMHk0tzkQhQF1mgI4,12715 +wandb/sdk/launch/environment/local_environment.py,sha256=rtcmVq_wrqcNXxWXG153SOKKbFmTbIwhDxCfd5Pgidg,2245 +wandb/sdk/launch/errors.py,sha256=G86cx2IZSRBBxLE7thxcVFKzN9d4mGyvciaWRz0uWWM,275 +wandb/sdk/launch/git_reference.py,sha256=6pTVlD7-BICWoraN8PsAzCKu64GV7g_GzqMSD9w3Sos,3758 +wandb/sdk/launch/inputs/files.py,sha256=BWGDmAfDPLja7zz0bUzYLaF3wH4cTPesD9LqDuJRUoU,4685 +wandb/sdk/launch/inputs/internal.py,sha256=liYeKB9yE1ckc0rv6kbKFIk9TloCM59h_HLGwoUdPCs,9807 +wandb/sdk/launch/inputs/manage.py,sha256=O0IsTWhjUftihohRfK7DkT186LjHMMcE2NSMeG8IFBY,5003 +wandb/sdk/launch/inputs/schema.py,sha256=OvLqb8yBI4KsuT6aFp9_M361ACVjFQARUJqqEuicO8w,2779 +wandb/sdk/launch/loader.py,sha256=rSXCgiR7dStpdd_FQTm3qqzY3aa5L2vMpF1M_0OsxEE,8927 +wandb/sdk/launch/registry/abstract.py,sha256=eTiuMPEm0NnrTkU6M8UteZQfK50BN-n7tyev4nMA1oQ,1146 +wandb/sdk/launch/registry/anon.py,sha256=jft6cCObrI1mgydwX2rR9xYfdLDokHv8RqlZiMHBFCI,943 +wandb/sdk/launch/registry/azure_container_registry.py,sha256=KzzoDGA1zoYV55oK1b11aTAm4Q6L23W1shGG8gUI5VY,4542 +wandb/sdk/launch/registry/elastic_container_registry.py,sha256=_5kZ-Z4qEfzbGaJYv0h0-wlc-KVlOyJ9tU47DCbQRmo,7248 +wandb/sdk/launch/registry/google_artifact_registry.py,sha256=R7qe5VhZzL5vgZWrFYv9jpZogO4clBjqEf36DCjW1Fo,8510 +wandb/sdk/launch/registry/local_registry.py,sha256=mwYOeZPGBb0eIYUgDCuQBLRJl4TqFK2GsOt770emolE,1755 +wandb/sdk/launch/runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/sdk/launch/runner/abstract.py,sha256=CltVdK6hFECpxFfP8yaxacJ-HxoRWNgakShGmQfnhC8,5619 +wandb/sdk/launch/runner/kubernetes_monitor.py,sha256=OoGauekACMI8EgXvBgmJ1RfBbttEfJ98AoIkPCGwLBo,17825 +wandb/sdk/launch/runner/kubernetes_runner.py,sha256=8VOLOMTbs9VRL-9hlEr40TtmK1NAZu-UwbAWv_agOgc,47857 +wandb/sdk/launch/runner/local_container.py,sha256=nhxTNwQop183VRFGSzO-2GjctWohqJnzCSc8FC7LiqM,10358 +wandb/sdk/launch/runner/local_process.py,sha256=jomOlDY2eiKTNMSejG6dcBnVHqZOVaFjpUWIjCDNXS8,2665 +wandb/sdk/launch/runner/sagemaker_runner.py,sha256=in9-GMypI5J1tGgSDpz2rdHZjwafUIMEwrp0AzL_IGI,15268 +wandb/sdk/launch/runner/vertex_runner.py,sha256=e_tvhRPX03ntftx6ZOwDs_6xXplRlFgfRDrNIKqCiPs,8169 +wandb/sdk/launch/sweeps/__init__.py,sha256=Vv3WTuhqs7064cv23YevqzX8jGca9qYZ5JpdhorBk0w,906 +wandb/sdk/launch/sweeps/scheduler.py,sha256=mdvHLgOGgLeZi9C88qsp56H5aTcbsPZbKMo-6hKDEdk,26859 +wandb/sdk/launch/sweeps/scheduler_sweep.py,sha256=beM0go0u9xRcXW20ED8MyTqq4ol_O5_4PODbA7FpN88,2987 +wandb/sdk/launch/sweeps/utils.py,sha256=T6Zr3QwEN1dl26mRDcRwlHXrZMoVNysAwqyCQ4MIU6k,10229 +wandb/sdk/launch/utils.py,sha256=2rhynbIezfLbalpNSorcJjX8XEIednziyOOBWG7XLu8,28024 +wandb/sdk/launch/wandb_reference.py,sha256=t4REjZz5lwB9fjDW2eo8uRgw9KeLsPeZ1Uu8tiFDBfA,4253 +wandb/sdk/lib/__init__.py,sha256=53BA5lIOtUXciXZcPpNsFbp-iUPzI5gQFxplTykNmOE,183 +wandb/sdk/lib/apikey.py,sha256=s2uw8B2O53rCpBCDgiWyF8ceQH9MNpOdaTil1uvy-O0,11097 +wandb/sdk/lib/asyncio_compat.py,sha256=WRBv3Xj0rU5lnxFmsOTpoWad3dXWvN_D0TS5zEbIps8,9321 +wandb/sdk/lib/asyncio_manager.py,sha256=ftMrz3qAGtOkfLoo-4xeoUj3etuayw7XXStPWHmM43U,8760 +wandb/sdk/lib/capped_dict.py,sha256=T2CNkmzfWeFgXOsFmx5IDPzgYmP1jxaVAb-nn3-qBBE,820 +wandb/sdk/lib/config_util.py,sha256=Y00nnEcq0zspuDb0yqraxRuo5JIUh1HflDvZYppeDyk,2893 +wandb/sdk/lib/console_capture.py,sha256=gUG6n-FtogU6DW9dJ9icF_8mMh3svW5yiC3s_fiof9U,6622 +wandb/sdk/lib/credentials.py,sha256=WmVdzL1rFy7S0WIHf1ZYd98_eaHTxPKUobReRSPQgBM,4737 +wandb/sdk/lib/deprecate.py,sha256=6M1Eb07bhgeDt0XuYFOJLMjY0B2jUZVQIYSLs7SQPzQ,817 +wandb/sdk/lib/disabled.py,sha256=DO_I4aGCvg0sRzMZQ7bMgQijaxTzY1-Yf7LPf9PuJDU,911 +wandb/sdk/lib/exit_hooks.py,sha256=_4oozaRQCJi8NJfZvHsA8livvFb0trZKLOGB8_UcHGk,1540 +wandb/sdk/lib/file_stream_utils.py,sha256=NN4qaSyNufvlkg96a1YUjQ8-pYtCVU7xagJaW8mqAyI,4018 +wandb/sdk/lib/filenames.py,sha256=GvzWOq85BO_Od7f69PkRKS6zYhQ88dTCtMr3c19jLEk,2006 +wandb/sdk/lib/filesystem.py,sha256=AWEY6dqFYPw_ump1nsaOCl-FwVo0ui5iZ_YH0tUTeyQ,14132 +wandb/sdk/lib/fsm.py,sha256=2fRiEF2jF95xDoVVbOmeIRaxMqlOv0bCv4RD8F2Ivj0,5044 +wandb/sdk/lib/gitlib.py,sha256=UAKpxnWTyIp3xkn4bRRqxvUmv5c_xyN0gmc5nL91hpM,7832 +wandb/sdk/lib/gql_request.py,sha256=XzBmCX-X3yx7rvyA6qWuzBFLTeMO7Nb-dmapaKp-pLY,2665 +wandb/sdk/lib/handler_util.py,sha256=mT9CKsmboq4lPWElsi4uo9ORHhx6OYTr7KY2QtgbM6M,589 +wandb/sdk/lib/hashutil.py,sha256=Heudn0Prj3C-1Ypyq4IU1QfflRJrJMbgoreTGnWGraE,3592 +wandb/sdk/lib/import_hooks.py,sha256=0_TJwq-EuZFUIFzFOZoVDWOMP9-Af1r2Vq5sV5sfzg4,10271 +wandb/sdk/lib/interrupt.py,sha256=Ea7aSVHcY0wLYnyMfm4jZbQcwGPgRcuPuoBSe0Swuds,1353 +wandb/sdk/lib/ipython.py,sha256=TcKyUyoCBGCNkItaJRDx8PFUHtvuPPDDIduqyMYuGfQ,3810 +wandb/sdk/lib/json_util.py,sha256=pfUM7euVZYhiRvUOPuZJx1NKXejr3lpU_u1x1fCmJX0,2466 +wandb/sdk/lib/lazyloader.py,sha256=4Seis4C8Ph6a2-3scfYqeHh7KjKG6PO0WJ2ZPW30O0o,1891 +wandb/sdk/lib/module.py,sha256=p4vKIq-rfj-txcUfj0RdknLlb-8sCNmwakn49HTWdsQ,1916 +wandb/sdk/lib/paths.py,sha256=-5mtOUwtf7LUrM4NxQWqi0HGsNSHujD0QOhLa0HXzFo,4565 +wandb/sdk/lib/preinit.py,sha256=11QkGmBpoGazNaUGvyDIzBJA4QTggj7fGQdugpCvOiw,1450 +wandb/sdk/lib/printer.py,sha256=HH9hdBwj78F4K_HPVD6y3Eip0uRSdugqvFr6NX_v85w,16018 +wandb/sdk/lib/printer_asyncio.py,sha256=WCBPCS95bPi0EbYVZiJE88AfvhB3LmKID28hBWDAXf8,1497 +wandb/sdk/lib/progress.py,sha256=ktOAZmo03LTjTxNVHmjAWHJvR2PDk31Q3keVcBp0chI,7932 +wandb/sdk/lib/proto_util.py,sha256=JxDldi8j6dfF_Lx9zOwqYL6LQZhYYGgKt_AfZtYHAW0,2894 +wandb/sdk/lib/redirect.py,sha256=X4JHJ3W5-kG25aOYUyuYc8TBgVAKwhpV6yTaFWaR7tI,27373 +wandb/sdk/lib/retry.py,sha256=LylZjVYWp44slZkbW7ehx_F4xdIvqHiVbaaFhQyJXls,13079 +wandb/sdk/lib/run_moment.py,sha256=hFx3S-F9SAF6xtwRYbBnP5fMDkllMJvGSSla_eqbsA8,2419 +wandb/sdk/lib/runid.py,sha256=Qa-5ft4B85YUazkV_18OYwf9JhMaAVp0JAInZzxzX5o,392 +wandb/sdk/lib/server.py,sha256=dn0RXjAnpmigAULLyq75XHYyHxw17MLCJ9fhPq2v22I,1623 +wandb/sdk/lib/service/ipc_support.py,sha256=UPa-bV_wY25WhYmMelVyLGZzbztSz2LSf2SUhVB-omQ,341 +wandb/sdk/lib/service/service_client.py,sha256=_4aCW3H0iGr4aIkfD0RudxY_ASI2v2M1DuhcnE8YXJM,3071 +wandb/sdk/lib/service/service_connection.py,sha256=93OcdHJ-dlCECuhz--voBcf1rzpPVlig0v_zB1PwI0c,7591 +wandb/sdk/lib/service/service_port_file.py,sha256=Oonq46Ml4vKR-JTcxx4exi5MthGYRvIqy8hCv84Lc5k,2943 +wandb/sdk/lib/service/service_process.py,sha256=EDv7zYeUpF4NgN4ClTlzyUtfs75CiimdmLxTqvo72Tg,3100 +wandb/sdk/lib/service/service_token.py,sha256=2J68CZqNqN4sRIkI7oDUpqX6vhfWQi8pXNzU_hUxoGc,5107 +wandb/sdk/lib/sparkline.py,sha256=9xQkuZ0z1DM7rHE2jFNDy5vBdPirnurd__A_bC6-Bgc,1363 +wandb/sdk/lib/telemetry.py,sha256=Q7rh7qyeicy9KdgQffVK6ZI8vHUdOtwFvxEeeJtOWcI,2754 +wandb/sdk/lib/timed_input.py,sha256=D7vx3N3m4LlxwkQpzS7lDi7IOnccqZ3WfLlDYRs8Oes,3229 +wandb/sdk/lib/timer.py,sha256=VZUyl7fmQAB75l9QXw9Nkz9SxyYj-CvLPawAk866YKM,440 +wandb/sdk/lib/wb_logging.py,sha256=9km7TAnJRSwBTQaFSYA4BmQZV3_Gb0y1PBlzqEOdUHA,4839 +wandb/sdk/mailbox/__init__.py,sha256=C4x3ym_hymymKFZiI1Q0HPlGx5x5CzeKPVF229Z2V8w,856 +wandb/sdk/mailbox/mailbox.py,sha256=R9H0MCNt3dhDfCZXu0qZIbYa4mJtxXucOzKvR4smss8,4806 +wandb/sdk/mailbox/mailbox_handle.py,sha256=l0u-Xe6qu12UECwSK_sIR51LHPMJyvKiVA5DXvfAM6U,4072 +wandb/sdk/mailbox/response_handle.py,sha256=nRUe3tTC6-dmQAsRT2AaD3tWaQfXPZRJHgLLrIroOJ4,2816 +wandb/sdk/mailbox/wait_with_progress.py,sha256=xurloFYzaYjJVcmayHJBNXIjB2vT1r3IBzi6yP_ymSM,3096 +wandb/sdk/projects/_generated/__init__.py,sha256=Oat5Pm5mfE1Qwd9eA94Xktwco8hSnAXoa3pgPzJ1U_U,701 +wandb/sdk/projects/_generated/delete_project.py,sha256=2fguHdSAtLSjUl-J_t4kqCjoAeW6i5OUl2H3yv6Ig3o,495 +wandb/sdk/projects/_generated/enums.py,sha256=vPRi0TPBkZS4D4to-WzhesolsPj3a2B1Okr1xmjRy5k,124 +wandb/sdk/projects/_generated/fetch_registry.py,sha256=DvhQFgvf4yI32Q2puWRMcXzn7ZuHJfOgKmBxEk3AuLU,436 +wandb/sdk/projects/_generated/fragments.py,sha256=iFeb_dX2RwGhnwKItiqv4QUX9stJwvGTS2UCsGwIynM,1101 +wandb/sdk/projects/_generated/input_types.py,sha256=izPOosvLVVURHZgWC6yL6_uL_b7PDwz3Kw7qc3uoU0M,278 +wandb/sdk/projects/_generated/operations.py,sha256=MhLYsTHjekySMt3jdlXdruDI0Sg5xR6wQSOeDvTu5b4,1785 +wandb/sdk/projects/_generated/rename_project.py,sha256=gwiq_rlA55YUT-ZB6mukewxMmi7jSbMr1drZMKpFV98,595 +wandb/sdk/projects/_generated/upsert_registry_project.py,sha256=yNkkeiImzvEbwL9yeJLIePZoBEfVuJB-u8VZFgixymo,597 +wandb/sdk/verify/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/sdk/verify/verify.py,sha256=Z7-QyYaro0-I0iIv4r9X7argyq2ks_SyOl_TGE9KYHE,18258 +wandb/sdk/wandb_alerts.py,sha256=SwBPBiXRxknMTMGbsVoMMWqWK65UWMcKAdTWZtdwAeo,193 +wandb/sdk/wandb_config.py,sha256=uvbpaV3yObsYsaYedYde95B4CzDZBlaU3zOPJ7tPkhs,10692 +wandb/sdk/wandb_helper.py,sha256=IbJ7opO8UkfwCDekSjRYIrGBblUxnTPBfp1EdesfF4U,1824 +wandb/sdk/wandb_init.py,sha256=KfU7riYpOxn4UZMvzAKTP2Us6UnSlM1RLUBI2UrYzCI,61681 +wandb/sdk/wandb_login.py,sha256=IqUQl3wJe9XWDY47sZLV0korWjM00EEO3HiZbDk1V9c,12210 +wandb/sdk/wandb_metric.py,sha256=HihH23rZWw6AOH5Vn4KsFREMTFJGkjYqUBhqfOlHg2I,3346 +wandb/sdk/wandb_require.py,sha256=MYzhsbMLjXVoGRHxssg802w79BPsoyEbBXuZ6cffjCs,2713 +wandb/sdk/wandb_require_helpers.py,sha256=ZmKv5aXXHDTTU6nYHMLKW4_pt9X-PlaMtbRJl77kHX8,1331 +wandb/sdk/wandb_run.py,sha256=03SY6nc4yveWPJ0v-4ec4cS_dhlNLi-9ZLX51z1xHlI,147452 +wandb/sdk/wandb_settings.py,sha256=PYAwkb3dsxJqP_7KApSTt6EPtJTZNvNNBkRv0KxHIwg,75727 +wandb/sdk/wandb_setup.py,sha256=BanGtCYoQSStnXFJkMrDO9GgN16Z-GEi4Q5TR5Qa5ls,19115 +wandb/sdk/wandb_summary.py,sha256=yQdOVIPrZaZanhBQ7yuSfPLX0x6dxwkN_KAn4SgjSZU,4536 +wandb/sdk/wandb_sweep.py,sha256=6PfqlKi0_2g6pt_hu-sqgCgO5YyCRppBbLwBxw8r2JY,3982 +wandb/sdk/wandb_sync.py,sha256=GNC8xYqT56ws4aw6Vd_mV_Ck-InEbqnyioSqHIybDug,2106 +wandb/sdk/wandb_watch.py,sha256=kN5qgovHkUPyUeNypoPg7tsPg-P8AKhYD8hDx46nZas,4746 +wandb/sklearn.py,sha256=hbPkefhS39A1RRymn0nHZZmKM2TrOd4xjlkthTZe9pY,803 +wandb/sync/__init__.py,sha256=e0Ygm4RRlZjqQe8tkBkF7aYtXAYWxR2PeY_zLi-dzZc,79 +wandb/sync/sync.py,sha256=10Esy932jf3DMsIlJ0Z6UBG70rzeo8Zg2GhcXrRYeZ0,16215 +wandb/trigger.py,sha256=PaitU3sX6ekGkd2R8iD6d_VtI72ypF7LaPBXh3rXY7Q,615 +wandb/util.py,sha256=47WPL86PNn4UPzLmpU6cWxZS1GPrtKwo1lJyWbrcfzA,65368 +wandb/vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/vendor/gql-0.2.0/setup.py,sha256=_osNap_aCOVvTlDwP9f-aI10TJbpIflxcG6fEQoAxNs,1314 +wandb/vendor/gql-0.2.0/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/vendor/gql-0.2.0/tests/starwars/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/vendor/gql-0.2.0/tests/starwars/fixtures.py,sha256=VSe1BHn6ltu2O_tPOUAFqDg7extW41OiEFO7AjRb8ic,1681 +wandb/vendor/gql-0.2.0/tests/starwars/schema.py,sha256=uCQATEOQIPd3IP4LSEtVjLHmKJ_ArLvEUArVFDf-D0U,4755 +wandb/vendor/gql-0.2.0/tests/starwars/test_dsl.py,sha256=DjJ5ot7wAt84ICcFPpKcxlL-zZTHUBn0QF6I2eSZk9o,5986 +wandb/vendor/gql-0.2.0/tests/starwars/test_query.py,sha256=QcjH1DwLqqb1N-qCx5shN4yo9Qw3-j-NlxoBUw3huqI,7964 +wandb/vendor/gql-0.2.0/tests/starwars/test_validation.py,sha256=DvaFqQ_hy0W92Gsw2av8IjQR51QN2tfZ-NHTM_KCbWs,3350 +wandb/vendor/gql-0.2.0/tests/test_client.py,sha256=ITo-5YBgRTq7vrBOJ97ztXKcyQn5niWOnAkE6sqJgM0,697 +wandb/vendor/gql-0.2.0/tests/test_transport.py,sha256=BZwYV6DQBlRFkLVHp_dH2CzSbOQsLNR9peEPCsZCqmU,2240 +wandb/vendor/gql-0.2.0/wandb_gql/__init__.py,sha256=sokx5ACyDmgDtL-io3gBEVKcBAi1CV4nSiXEjeGMjVI,77 +wandb/vendor/gql-0.2.0/wandb_gql/client.py,sha256=AR45930Eaf1DYmcA-Fm9NouXU0kKTCl1aEyZtECKAY0,2964 +wandb/vendor/gql-0.2.0/wandb_gql/dsl.py,sha256=fZTUZlq7NbuJULYgfTpQ_oNSK-gvF1v3O7Soaq-tv6M,4375 +wandb/vendor/gql-0.2.0/wandb_gql/gql.py,sha256=JZEozVeuOxb6rkactNzs_0v6TvM799KYCdXQTzorbFM,348 +wandb/vendor/gql-0.2.0/wandb_gql/transport/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/vendor/gql-0.2.0/wandb_gql/transport/http.py,sha256=W_21pHSLCNme_ukkin1muiF7vUtK21LEzUcqPNK-6_k,172 +wandb/vendor/gql-0.2.0/wandb_gql/transport/local_schema.py,sha256=rWiVTPpRJGCZIkLkp-QiV26pc0YkgVpt2PtoNivihGg,316 +wandb/vendor/gql-0.2.0/wandb_gql/transport/requests.py,sha256=eaSF9HIGaBwRvXbUzifemmQ5uNbDj4e9lzGfD4zH-gY,1637 +wandb/vendor/gql-0.2.0/wandb_gql/utils.py,sha256=rV2mkAU9-6DBPTyyHTSq0lBuXC5o6HHOLN2Kmy0ifds,676 +wandb/vendor/graphql-core-1.1/setup.py,sha256=FV94NFqGy3w5eILAhH_rldtMwyQ7KNEomxwUR-ceJ3M,2743 +wandb/vendor/graphql-core-1.1/wandb_graphql/__init__.py,sha256=x2MzOC7YipdFCsvCmZV1ogaacUxvdHaTTkbdb4dZuNI,7300 +wandb/vendor/graphql-core-1.1/wandb_graphql/error/__init__.py,sha256=9yXJJMFCWiPT_ljIvpkYvDJ0-nx9BAx3bEGOwtPQkqA,251 +wandb/vendor/graphql-core-1.1/wandb_graphql/error/base.py,sha256=PxPIdFLH6bfl3cy1HUsFNch6jcMS6lqUUwL2lTvOPOM,1270 +wandb/vendor/graphql-core-1.1/wandb_graphql/error/format_error.py,sha256=McCg1u3AEgfAGug7QnhXWUHHYpzbsJpaW8YGbS5f4XQ,296 +wandb/vendor/graphql-core-1.1/wandb_graphql/error/located_error.py,sha256=HLg4fJmLDtRcUVZ331dhd2rIA9Lg3DI09kpq7cSS6BA,757 +wandb/vendor/graphql-core-1.1/wandb_graphql/error/syntax_error.py,sha256=d5oGQt5wjORtqR7o8hKYsITl1COSt_gZlT3gScSds4I,1132 +wandb/vendor/graphql-core-1.1/wandb_graphql/execution/__init__.py,sha256=OQcZ6FkavSXLlrqOYTC1Q-H7CQ4S1onzJnsSOkta6IA,723 +wandb/vendor/graphql-core-1.1/wandb_graphql/execution/base.py,sha256=WYuAfruLgMl9__UMg24yA9VBckcOaWgw0csrFDQ_oxw,11264 +wandb/vendor/graphql-core-1.1/wandb_graphql/execution/executor.py,sha256=67xPlV8im1YlLem8V3KY8_9U3mKaiCZlNKiRRfCRI0c,14308 +wandb/vendor/graphql-core-1.1/wandb_graphql/execution/executors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/vendor/graphql-core-1.1/wandb_graphql/execution/executors/asyncio.py,sha256=FS5HajefEO68JXw8vqpcYMir0jIsU-z9YQu5YT6bnjw,1794 +wandb/vendor/graphql-core-1.1/wandb_graphql/execution/executors/gevent.py,sha256=o4R9f77Fe0lldzeqnTPQOvkSsnrfT-90l6y2fBZkfTk,495 +wandb/vendor/graphql-core-1.1/wandb_graphql/execution/executors/process.py,sha256=ncW65pMpnfsXSw171p-sqEJRSfPC5bjbHY5A6hGtbxQ,757 +wandb/vendor/graphql-core-1.1/wandb_graphql/execution/executors/sync.py,sha256=57ylY5MVMo3P5EJbaNPQtsx8kLCK7ZaHDKuXKY30HQ8,157 +wandb/vendor/graphql-core-1.1/wandb_graphql/execution/executors/thread.py,sha256=JdXtp1hJaW7V1I58C7cGHziCqC3U3DAmOuYMXPrbvcc,953 +wandb/vendor/graphql-core-1.1/wandb_graphql/execution/executors/utils.py,sha256=3zHgHZKyrbDIWsDRuLHRtrRYFmi6mQ14q6n9hjKr30E,151 +wandb/vendor/graphql-core-1.1/wandb_graphql/execution/experimental/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/vendor/graphql-core-1.1/wandb_graphql/execution/experimental/executor.py,sha256=gxwgR0Fl59b8dGkx5JgwjReBH4h3bT0-Bz0alL_5fcA,2251 +wandb/vendor/graphql-core-1.1/wandb_graphql/execution/experimental/fragment.py,sha256=ppscuvjnaMT-YgHcomzSfXRjrxT9rEQe-vb_zj5g_Ao,8329 +wandb/vendor/graphql-core-1.1/wandb_graphql/execution/experimental/resolver.py,sha256=9bakNNeCgI8YuAnV2OTAEkyvYOScuaIELCQ4WK5KqUg,6344 +wandb/vendor/graphql-core-1.1/wandb_graphql/execution/experimental/utils.py,sha256=akGcXkBUaN7Qig7Y1itPkcarjVZYwjfJG-97sNsylT4,149 +wandb/vendor/graphql-core-1.1/wandb_graphql/execution/middleware.py,sha256=CQBz9W-qgfLOHOKbm3TuDSG7It49PXTHfcUzozxjDpQ,1725 +wandb/vendor/graphql-core-1.1/wandb_graphql/execution/values.py,sha256=1t_uGTA86cs1vIa0imkMt-zSLKG1LyxbOA38yF-3Q7Y,4734 +wandb/vendor/graphql-core-1.1/wandb_graphql/graphql.py,sha256=9Fu2CLxgimKzSpZAkqeWUSA8lDK8LRRkUnn40-rTjF0,2224 +wandb/vendor/graphql-core-1.1/wandb_graphql/language/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/vendor/graphql-core-1.1/wandb_graphql/language/ast.py,sha256=VnBTudpMQoxmGHgiHyKN_74uGf3baiU-c13zzcR_QtQ,35008 +wandb/vendor/graphql-core-1.1/wandb_graphql/language/base.py,sha256=Q07CFeZbOfxiR_Dc5HOIBRbJK6Zk5mfl8KE2e7WVrq4,408 +wandb/vendor/graphql-core-1.1/wandb_graphql/language/lexer.py,sha256=TMlNjDoNatPxVdJsCjp4OSvH3K-lxfBhips4MqRbkoI,11467 +wandb/vendor/graphql-core-1.1/wandb_graphql/language/location.py,sha256=QcVvx0NKUUt8L2LSGQzSWKjnCoaOnoxqokp-mlnQ9TI,746 +wandb/vendor/graphql-core-1.1/wandb_graphql/language/parser.py,sha256=da64gvknlunEC3TDgrgf74h22NpfsEPPm-OdBmof92A,20905 +wandb/vendor/graphql-core-1.1/wandb_graphql/language/printer.py,sha256=gH-I-gieWXw9FcCIISaXoxK_v_mzmhzAOqwiRiVbTQ8,5888 +wandb/vendor/graphql-core-1.1/wandb_graphql/language/source.py,sha256=UI0kq69Mujj986BiQoqeNDrxEBWPzf3IH4C1NZ1PXW8,405 +wandb/vendor/graphql-core-1.1/wandb_graphql/language/visitor.py,sha256=VzScdaIv3q8cCt95GiK3h8bDPXm8p-0wdBot8cthSuY,6247 +wandb/vendor/graphql-core-1.1/wandb_graphql/language/visitor_meta.py,sha256=Qat5ccqiNGOHE9W7z38YGdtW2c8koBw0OUWEMxpdBhk,2980 +wandb/vendor/graphql-core-1.1/wandb_graphql/pyutils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/vendor/graphql-core-1.1/wandb_graphql/pyutils/cached_property.py,sha256=eaxADzdmkEbkQmiu47k5p72Inyce9pxaVto1gvkkmMY,588 +wandb/vendor/graphql-core-1.1/wandb_graphql/pyutils/contain_subset.py,sha256=_-KYbYuSC3fiVSxcCwKHpwtS_Ig85HgRP6Hx5xbDF0E,1006 +wandb/vendor/graphql-core-1.1/wandb_graphql/pyutils/default_ordered_dict.py,sha256=xcFbt16AzcQsYehMOn-w04q1gOUiMzzySsKhpvpSQ6o,1288 +wandb/vendor/graphql-core-1.1/wandb_graphql/pyutils/ordereddict.py,sha256=YNLfN47G2nhMvl9Tm4DYPSJMzeLXKwTwAIPDarMtgjk,256 +wandb/vendor/graphql-core-1.1/wandb_graphql/pyutils/pair_set.py,sha256=gbdnd55q1UkTBdNSsAYVquiSSOAhI1q4mMWe6xfNkoI,1168 +wandb/vendor/graphql-core-1.1/wandb_graphql/pyutils/version.py,sha256=sd6hPQbuZ3kwijdPr5h6iZm2YFsmvqc9-6KtjRw7JU8,2454 +wandb/vendor/graphql-core-1.1/wandb_graphql/type/__init__.py,sha256=B_uo71CK3pr19VJjJf0G2dSEvS5p6zA5KbS53OxGsbY,1366 +wandb/vendor/graphql-core-1.1/wandb_graphql/type/definition.py,sha256=IHihIcxrBxVlyYtFFQr4R_4qC6o3JELUvwzL08goRbw,19250 +wandb/vendor/graphql-core-1.1/wandb_graphql/type/directives.py,sha256=snrI-WsyBoxOqEG3NC5Aau2e8_YiMmBPd2_TBgu41ks,4098 +wandb/vendor/graphql-core-1.1/wandb_graphql/type/introspection.py,sha256=ZyyA_zIbe8HaSThL7O9Kl44NKDBAkKKp0QOlbUI4tLU,17580 +wandb/vendor/graphql-core-1.1/wandb_graphql/type/scalars.py,sha256=HTwIgW1-O5tww8nA9pnEDTykaAVWQDWX1VV5zbkseK4,3849 +wandb/vendor/graphql-core-1.1/wandb_graphql/type/schema.py,sha256=grEnY-oKDaEu3Fr5LfsUk7xtXVhXD7MuOrIprkQ5q14,3474 +wandb/vendor/graphql-core-1.1/wandb_graphql/type/typemap.py,sha256=SB-wqQ7-UVtyv4HrK0rh5DLb6-T3nYpsOvhJAjHlwhU,6844 +wandb/vendor/graphql-core-1.1/wandb_graphql/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/vendor/graphql-core-1.1/wandb_graphql/utils/assert_valid_name.py,sha256=xqNTzOxF5UjdxIev_hUy3ewgBA9OOCiBQbIpdQlI3xs,308 +wandb/vendor/graphql-core-1.1/wandb_graphql/utils/ast_from_value.py,sha256=mx8CdvRQAllQniX0IXiVaTOvbKXYHFqMFPr5dNMhRK4,1998 +wandb/vendor/graphql-core-1.1/wandb_graphql/utils/ast_to_code.py,sha256=b5Qf8SeaqULW16bGIWTCZV5Mi0n-oUB_d5A-ez-d47k,1200 +wandb/vendor/graphql-core-1.1/wandb_graphql/utils/ast_to_dict.py,sha256=VtXQ63TuKC1EpKt3A96v4Bd4xIciRqVhk-rIHv8JiSs,638 +wandb/vendor/graphql-core-1.1/wandb_graphql/utils/base.py,sha256=qDhQwKRHDUN63UY4UbocIj6J8BnCEZD4meP_15ujzeo,2120 +wandb/vendor/graphql-core-1.1/wandb_graphql/utils/build_ast_schema.py,sha256=ljtWw-UiZZWaBsY3lfuHvsyXpoJ39KaxBwFVpbQjJ2o,10596 +wandb/vendor/graphql-core-1.1/wandb_graphql/utils/build_client_schema.py,sha256=a3x9-BKBa0xAQRNAuFtY6xbn9PChLrqpdG4Nu2sYPcY,10027 +wandb/vendor/graphql-core-1.1/wandb_graphql/utils/concat_ast.py,sha256=M7tFprLml4VjGXpxB5C_HLrRPbXcgr0dYyS_Ph4XCI8,204 +wandb/vendor/graphql-core-1.1/wandb_graphql/utils/extend_schema.py,sha256=A3uGbTgctv3AK4Bjd1tJM0x-AP6nP1Tv9MUYfoGhlaw,14143 +wandb/vendor/graphql-core-1.1/wandb_graphql/utils/get_field_def.py,sha256=PQU-C728tQHUOHBwK0oIc6iCnI8RdAEWZ3D3xtAJUkc,1108 +wandb/vendor/graphql-core-1.1/wandb_graphql/utils/get_operation_ast.py,sha256=h5_Ftqy1f2UExbOIw0EWENj-CY91KP_eStXRBvuL9Xc,834 +wandb/vendor/graphql-core-1.1/wandb_graphql/utils/introspection_query.py,sha256=UUt5Uiw9JHpkClfXyd6CIuC9LABoW2qxg95EH6OK_OE,1472 +wandb/vendor/graphql-core-1.1/wandb_graphql/utils/is_valid_literal_value.py,sha256=bIU5jV8pLARq_3qIc6jAWf1N2OCC5w4-qYCMzljupss,2366 +wandb/vendor/graphql-core-1.1/wandb_graphql/utils/is_valid_value.py,sha256=mUXwjh_1HKTJ0Mke7JSLhFWOaLT7qDQqBzpSHvTE4cY,2143 +wandb/vendor/graphql-core-1.1/wandb_graphql/utils/quoted_or_list.py,sha256=5nxFfvx2zMjravOr4uQadWxZ5KilIfOFSFyiGuTv9SY,704 +wandb/vendor/graphql-core-1.1/wandb_graphql/utils/schema_printer.py,sha256=dPXqQI-HjebWPcjKJj_Sgj4jiRauGPQMTaV808PLpCw,4792 +wandb/vendor/graphql-core-1.1/wandb_graphql/utils/suggestion_list.py,sha256=eW9FUe3-ByBL86Rr3EZ5wSMqX_rEHWkzSFCYt_67jeI,1845 +wandb/vendor/graphql-core-1.1/wandb_graphql/utils/type_comparators.py,sha256=bwHJP9lVRgTiwhxgGyrwpcn9VAE1q7J2coJRJZs-ETg,2433 +wandb/vendor/graphql-core-1.1/wandb_graphql/utils/type_from_ast.py,sha256=TylOn7PMSmbggdoKibSA2-O2A_JvGn3NDWySeJ6k9g8,703 +wandb/vendor/graphql-core-1.1/wandb_graphql/utils/type_info.py,sha256=md0lKaIolOlyzHSHY0ZwZaE_9DB6aO3ztQRYFrxiE0w,4886 +wandb/vendor/graphql-core-1.1/wandb_graphql/utils/value_from_ast.py,sha256=XVlFlYTPeXf5HaO0M3lZjFj_3jvlAMJRjbQTFcQhURc,2437 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/__init__.py,sha256=dZtKP6R2FTSCc2gAkTOTayqmyrRl_gQlzBQCIDb3k10,111 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/__init__.py,sha256=WZsygnTsLd51GgWvmSWUAhGptrV0GzCB2pIcmDExFYA,2736 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/arguments_of_correct_type.py,sha256=0aFzTM-khI1psJZWnAMG91bCg1aZZphdSYRGslaymwc,982 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/base.py,sha256=7hsprORsfDPcxJ4Ojvpn0qe1ZuGrnbrDT9BgK3Bd3EQ,165 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/default_values_of_correct_type.py,sha256=l6khdFWN7K_we_uftEOjpaIx6bFYNdqTNqHr9RZHG9Q,1851 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/fields_on_correct_type.py,sha256=hySfm5eYpG8ZIhDMVqpd2Ryo2JksBQBAlJZUoMekdF4,4401 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/fragments_on_composite_types.py,sha256=5C4MABVySMpqlnE54e54D1gUn1EtBgQxTliu_aG_IV4,1333 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/known_argument_names.py,sha256=4uDSSjMWlCVmYspTU0TR-SKBmjGqcuhBdbtejx4pctY,2484 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/known_directives.py,sha256=0_V12oJpcblXXdPD7yZDeL_vp2lg90xfKT0-Q1N-nU0,3434 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/known_fragment_names.py,sha256=W7k15YYDlsQqET6Frdn7_5NRhXGz0YKzlH8sBj30m90,597 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/known_type_names.py,sha256=bLW1UkYYCu_KFEGZJEHydMjqrSprkHjHlzAnmLZjQnw,1258 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/lone_anonymous_operation.py,sha256=egiNdBr2s1QHpP9Z2i9fL6PtlT17GXPfEiMh2F_v1Os,897 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/no_fragment_cycles.py,sha256=myRMv0nLzmW0KGfyYUxvv6DiMZG50y7FTfNKkFg42MU,2275 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/no_undefined_variables.py,sha256=1LqfZ3rH9E5KH3zQA5wd_BXUQdihrkFSTqVKG4j7gVo,1391 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/no_unused_fragments.py,sha256=RG4DVQL7nA2MCijZtCVqKLVJN4dqQxVM_I1IQUUluNA,1489 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/no_unused_variables.py,sha256=L4N3BzkvJ1GZI6gi2zsMinhej5bWQZqUcJWQ1qu5YJw,1508 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/overlapping_fields_can_be_merged.py,sha256=CbtRQfEnQP6FTsqC_Jk9XSw4eGVb8shkA5P6yWxAxpk,24145 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/possible_fragment_spreads.py,sha256=Oky-9Ta0aYgP0Fe1oOtwiOjrWvTpLVWGGwY6B-18l0g,2193 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/provided_non_null_arguments.py,sha256=fdTVBXQXVgi3MKhGL0FYHYGq-HKCh3HLKAyc9POqD5c,1869 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/scalar_leafs.py,sha256=Dei3Ss_ycaYfan-F8UBL6IyBjCWdGRoQ85nAq8Y2sFg,1115 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/unique_argument_names.py,sha256=sCdSGBraWwNUpHhwPu3dk0t3aCPqVWgo1Y4v6LOMnzU,1024 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/unique_fragment_names.py,sha256=xb_0UhQF7YDlK-vA1u-li6PL3V2FF3Z_kTLGTEKksoI,1002 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/unique_input_field_names.py,sha256=_YAne-NO_OIenreW4v1XnJES4AK3Y9O_HhUk6M6ilPo,1187 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/unique_operation_names.py,sha256=kzWjZtAiBou5WhZaOMzrB9UE_fZ8iN2M-VuOlN93lqA,1114 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/unique_variable_names.py,sha256=5L90Rgnz8UM7N16mxZ4f263Wa4GCGIfLIHKh-UW3mlU,1034 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/variables_are_input_types.py,sha256=9SbvdDA6ZshPM9-LqHI1byqO-3PwtfvyIgeXr5PHHvA,826 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/rules/variables_in_allowed_position.py,sha256=hqX7f5LJw3we9OoPWXgQpF333nrYDG_AZr4sSITTyXc,2367 +wandb/vendor/graphql-core-1.1/wandb_graphql/validation/validation.py,sha256=hRqVfs3jp0GFm44DDfKQipb0BEs-dgEeg8X5BcXf8lA,5580 +wandb/vendor/promise-2.3.0/conftest.py,sha256=c455VxJib8oEtrs7X9Ixz9rHVlQVl9mRVDVnJ5PhE0g,1010 +wandb/vendor/promise-2.3.0/setup.py,sha256=Wy9FnhHpm7dTLLnTPWYP2RTD8w_31zNlzl28GVv67PQ,1899 +wandb/vendor/promise-2.3.0/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/vendor/promise-2.3.0/tests/conftest.py,sha256=cNLj3Q3qG2bYyALzO3xbkYp1Gz5zwjZf_aHw-kk_84U,268 +wandb/vendor/promise-2.3.0/tests/test_awaitable.py,sha256=hWLcgrmySIF2o6dvQqbp84sWVsAowa7GU91rccvhge4,598 +wandb/vendor/promise-2.3.0/tests/test_awaitable_35.py,sha256=h2xndKzvtHF5aEtfRbtKXwjIeHuufOt2luBIBpmOjp8,980 +wandb/vendor/promise-2.3.0/tests/test_benchmark.py,sha256=6X2wr4_cSNXI0oKrPar8DUU3pUMnBQ3morJ4CwzW8iY,2868 +wandb/vendor/promise-2.3.0/tests/test_complex_threads.py,sha256=kQEw0mH2o1VGAx4qOKrcaE_zJJSUicGpvCpMEUOPZNA,485 +wandb/vendor/promise-2.3.0/tests/test_dataloader.py,sha256=gC_PiGDR64LxCcs4aB1NCl-G9oIssbvkwGazrTOrN9U,10637 +wandb/vendor/promise-2.3.0/tests/test_dataloader_awaitable_35.py,sha256=7toBZsOtUXsxeheeHFYiRNOaw1OAc-jDwHx46342hxU,2846 +wandb/vendor/promise-2.3.0/tests/test_dataloader_extra.py,sha256=nN9X1HevfUSkBuFXdLZKad9OFKCYTL9aDIqaeq5Qs1U,1522 +wandb/vendor/promise-2.3.0/tests/test_extra.py,sha256=F2cYhWoIEXo57xZQMU01lQBOZmVNbRhejdjX63xGUMY,14574 +wandb/vendor/promise-2.3.0/tests/test_issues.py,sha256=1eKsHU6MJqHo4CH2sJ2Q7zKPNj3dZLZM678Nx1LKdb4,3690 +wandb/vendor/promise-2.3.0/tests/test_promise_list.py,sha256=aqZT-Nm4qc_vPxWiUZXIpY-Zp2SecQLjpZihiWHbE20,1645 +wandb/vendor/promise-2.3.0/tests/test_spec.py,sha256=uRzXFn6lmPhs8ti9Zj2hRoK2LX2z2lTqDozyGmIEXrA,13441 +wandb/vendor/promise-2.3.0/tests/test_thread_safety.py,sha256=b5HZX5yHFShzG19Pm1qR-cbJUiqfrQhJZCKmPOeOShA,3457 +wandb/vendor/promise-2.3.0/tests/utils.py,sha256=_u7VFe-6rDWwj8xrQQnI_hnQNgYo5OqusSx7LPkImCE,178 +wandb/vendor/promise-2.3.0/wandb_promise/__init__.py,sha256=3IRTDwg63JE0VfdxcJyLIrstgU85Ya8HIkkHN1XWCUY,873 +wandb/vendor/promise-2.3.0/wandb_promise/async_.py,sha256=7L041olXPGPURDMFMgIxec_BWptaZ0ArFh_APTJ8I7Y,3959 +wandb/vendor/promise-2.3.0/wandb_promise/compat.py,sha256=IvY3E6_FyJZDkOahxPTX0or-86V0MFe5YnW3ve90a8Y,875 +wandb/vendor/promise-2.3.0/wandb_promise/dataloader.py,sha256=D_wZCiqu6CTOulhOGttyku6G31ravLt0nV_vXR3PQKQ,10853 +wandb/vendor/promise-2.3.0/wandb_promise/iterate_promise.py,sha256=1HGjWRD8KJkhvgx0hQYhipi-O6Hq1xMRrtajMZrgXA8,296 +wandb/vendor/promise-2.3.0/wandb_promise/promise.py,sha256=_hNgnM_uSrQwQDXCJCxD7tvfQCVQ9gA0TJo0NCSGyro,28218 +wandb/vendor/promise-2.3.0/wandb_promise/promise_list.py,sha256=aODoTWMTTAWRXa1VXZx-9i2R4VNyPkVHvoVwiRPnFdk,4646 +wandb/vendor/promise-2.3.0/wandb_promise/pyutils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/vendor/promise-2.3.0/wandb_promise/pyutils/version.py,sha256=crKfIm8N8Ysy1DEuoZFOgnPZCnntpmZtNbS3ZeP00gY,2507 +wandb/vendor/promise-2.3.0/wandb_promise/schedulers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +wandb/vendor/promise-2.3.0/wandb_promise/schedulers/asyncio.py,sha256=Z94PwkAXd8PDybJUM7_tLlDh_AUw5JbI4CFRg6J3_NI,512 +wandb/vendor/promise-2.3.0/wandb_promise/schedulers/gevent.py,sha256=PnB1LI2OXnbK64qvIcXnoV98VbF6kPah5zqsh8v6QmA,502 +wandb/vendor/promise-2.3.0/wandb_promise/schedulers/immediate.py,sha256=4addDLBFlXwiOBXU2i2wGced_zotfEoYEm-_sdeKy1U,663 +wandb/vendor/promise-2.3.0/wandb_promise/schedulers/thread.py,sha256=pK8zhnJWhvM3iiAc8ryoo8-bK2ny2hE4JoSsISBBU9s,435 +wandb/vendor/promise-2.3.0/wandb_promise/utils.py,sha256=v7Ssd2lm9_gLEvy64Q7oXv5VhOt4rPMHOGhIlr9oJew,1624 +wandb/vendor/pygments/__init__.py,sha256=IkWLwTf8uusJPx4c3f3gpHwVJVi0UqeD6mo5wWHHdWs,3145 +wandb/vendor/pygments/cmdline.py,sha256=_UA78VWUx--QdlHM0OuTu0W3zjDPhoVz8ipmjomf2Ac,19326 +wandb/vendor/pygments/console.py,sha256=AgkV5lDp2ghozb_J8V_UPZ0uudTWTz3isSK9oUvo750,1809 +wandb/vendor/pygments/filter.py,sha256=IY_c2IryClkgeVQDEQeEXoauZryHx9cvTOiedX3jaHc,2036 +wandb/vendor/pygments/filters/__init__.py,sha256=IlSO7Mm0meA1rfssNwR9u7626sscMnASJlZ94yEy9lA,11573 +wandb/vendor/pygments/formatter.py,sha256=RaPGA3oLywAbvSkzrj61U5Y8xzYai32bbajg8L3xreE,2948 +wandb/vendor/pygments/formatters/__init__.py,sha256=g5hpFvO_jy4-OV-pJr0jRP-vktKi6pXJKZ1z_XDnLIE,5099 +wandb/vendor/pygments/formatters/_mapping.py,sha256=E2DsoS8TZDCv_jJ1mn48rb3Qs4RIr8SJwILEVhYpGk4,6214 +wandb/vendor/pygments/formatters/bbcode.py,sha256=LYa2w-Ep7475be0GSH4MbuYiwH3gWIqpG4dQFCZLbGk,3314 +wandb/vendor/pygments/formatters/html.py,sha256=gvCUGAsxerTWRKnXa3a5ezmJg70KLt7wDb57JGjZhug,31759 +wandb/vendor/pygments/formatters/img.py,sha256=yWhrS8vgisj9XGOWjUrwtd8VZNuAffnBF8IAWl6p44g,19780 +wandb/vendor/pygments/formatters/irc.py,sha256=ApSLVBfjK78gkzXnMC8UrgnBLQulOvb-fza2e-D_RCI,5775 +wandb/vendor/pygments/formatters/latex.py,sha256=izNn5NjCTgu0gq8cyvvwNlRwc52CyAwvprN6DwlbUvU,17758 +wandb/vendor/pygments/formatters/other.py,sha256=p14Jboe9qeJ4xy9Xcb4f90szuQvQ0RI0q9DctZZmXDY,5162 +wandb/vendor/pygments/formatters/rtf.py,sha256=8HK5CpvqLchEWlbYptQKQ2RxQmayNLYyPOovCYBw4l4,5049 +wandb/vendor/pygments/formatters/svg.py,sha256=FT-DWvkC7bJ34-szEmh14PoqyqQkUE2jDPPVwOelqYQ,5840 +wandb/vendor/pygments/formatters/terminal.py,sha256=KkBzLxYMDvfbJXOecTgM1rAIVkYWYkE1a9pNIqtT0AA,4919 +wandb/vendor/pygments/formatters/terminal256.py,sha256=UKFNPANdnt2eGvYw_fRkyW2gKKXVpL9A5jVgEpGxd3g,10776 +wandb/vendor/pygments/lexer.py,sha256=CrUoLYlQbxO5W0YLH_a1lI8Dkw2l4nRZ5ciy9-R-n0I,31054 +wandb/vendor/pygments/lexers/__init__.py,sha256=77oWKOFsKWByzG5aMNDsWBJl0lQYSNgIC8XUUV2pCW4,10906 +wandb/vendor/pygments/lexers/_asy_builtins.py,sha256=zph3J1PBBtWjrvc_hUrmY-vk2WkN4zwT3OICH6nnyN0,27321 +wandb/vendor/pygments/lexers/_cl_builtins.py,sha256=1KrMgXg50ZsI02_WlDFuHugJzA-1LygeeUmHGBHBIgw,14053 +wandb/vendor/pygments/lexers/_cocoa_builtins.py,sha256=8b6As01z9rEa14hp7DXSXKHQXnRVI13eWEZ5rcmn5Bk,39982 +wandb/vendor/pygments/lexers/_csound_builtins.py,sha256=JkAZhrG-lXndYAsxWU2VqPce7MfwDknl7u8cYi3tAjs,21643 +wandb/vendor/pygments/lexers/_lasso_builtins.py,sha256=5PNX3gtW3f0X8gzoZUR6JW3mYzck3RcO1J6XtkUhY6Q,134534 +wandb/vendor/pygments/lexers/_lua_builtins.py,sha256=q1bCBDWwe7g-FwonM_xrGibEGdEYJvcdWRVGpYgBPpo,8340 +wandb/vendor/pygments/lexers/_mapping.py,sha256=_P3TsdkuN1NptMfEQkplLCf5TklMRNDi6Rtl_oc4XR4,54715 +wandb/vendor/pygments/lexers/_mql_builtins.py,sha256=qa9s_tUqXl16pAKzRDtWsJKzDPoU-bGaGdgrmHx7XOk,24736 +wandb/vendor/pygments/lexers/_openedge_builtins.py,sha256=0ADwH8PqzC6rJ_SZ8b3j0TgNut1kvLZL8RxpbC3J_nk,48362 +wandb/vendor/pygments/lexers/_php_builtins.py,sha256=cpkFCfHjPG1zExNaMI1JQY1at54pf5RMu-yCBmtwl78,154368 +wandb/vendor/pygments/lexers/_postgres_builtins.py,sha256=me5pfqtfUMpXYSvuTYXscyLCYUY9TcmBX8pGwimHG1U,11210 +wandb/vendor/pygments/lexers/_scilab_builtins.py,sha256=yXpa-XsMiNRgsPpDENWvZFZZJndUGFgv8uioKcfnejI,52405 +wandb/vendor/pygments/lexers/_sourcemod_builtins.py,sha256=DPFvfIi76GawQ603QG_EAFzl_6mmK5xZxfGWl4qlKFM,27113 +wandb/vendor/pygments/lexers/_stan_builtins.py,sha256=y6S6OymabXXmgrF3tMVkTxMHzkdyOi-Q_s0M2qdh-Xk,10121 +wandb/vendor/pygments/lexers/_stata_builtins.py,sha256=Hods-_4P3Ixov_hf9z6oeq-J_W_a8XOo8p--bECWJKo,25139 +wandb/vendor/pygments/lexers/_tsql_builtins.py,sha256=opXLcAQOoAgcizztFXepEHBABOrGjSvDWU3TxVvJS3M,15484 +wandb/vendor/pygments/lexers/_vim_builtins.py,sha256=mIYAlwzf687UR8oPcY6y48H-BjTyPNDx1oHiRc5wByw,57090 +wandb/vendor/pygments/lexers/actionscript.py,sha256=HWGTyjPGho_kZVVTxemN0_5iyhK5wFgFQQ0MYVS0as8,11179 +wandb/vendor/pygments/lexers/agile.py,sha256=zB0BGCISgQB2b-3ch3QNRgV2O7jq7QPD5nvBbXA_K5U,900 +wandb/vendor/pygments/lexers/algebra.py,sha256=fhvIdLDS3oW0iMsDSBAzg8l9DuBXxfLRFC53xRIzvHU,7201 +wandb/vendor/pygments/lexers/ambient.py,sha256=M77HDk4dzs3Amlgwi_ogj1PV1Ch77PyEW6SU5AFtL4w,2557 +wandb/vendor/pygments/lexers/ampl.py,sha256=6AQASY--hmYXtgkzsthZymRM4bzQ_RQZZC5Wu4RJG-A,4120 +wandb/vendor/pygments/lexers/apl.py,sha256=Lpv2nI1bSC_VpmckdbpMiuxWx-WVb6FgFWoJ31q002w,3167 +wandb/vendor/pygments/lexers/archetype.py,sha256=IwyDTh-U673WHQQThuf2dBz0Fc-XlcAP-xAhtnR2DH4,11136 +wandb/vendor/pygments/lexers/asm.py,sha256=YysaSPQr0Ir79aM5C0eOB7g8-vJ6m2gWdbghsEElGBM,25261 +wandb/vendor/pygments/lexers/automation.py,sha256=g_laLS9LstvXvrvVQI01fAf0fyZ4-ZHFewdVLtXPXf0,19648 +wandb/vendor/pygments/lexers/basic.py,sha256=XfeI7Tk78lN7YMqS6bYdRB6CRPa2vxcm5PzA2EbILnc,20306 +wandb/vendor/pygments/lexers/bibtex.py,sha256=CcIdWMtCUtpHoqXU9JtGYKOmQJscO7AwofABCnEx_lk,4724 +wandb/vendor/pygments/lexers/business.py,sha256=iJbZjsF_CwJvkWmuDEAUgRhIPN3Vus_sp2m4V9AsKuo,27665 +wandb/vendor/pygments/lexers/c_cpp.py,sha256=Ty6LTWNG_ZR8eLNmcdBmJfUIJlkAyd033JqIH3Mfric,10523 +wandb/vendor/pygments/lexers/c_like.py,sha256=AoPSFV_TFPudAnvkV0wLlM00A3_JrddoTd5GRMyeHcY,24124 +wandb/vendor/pygments/lexers/capnproto.py,sha256=8pl0G3q7k5AJeoe6Yfr2eMs81V8KW1IlduzkRPaNb7A,2188 +wandb/vendor/pygments/lexers/chapel.py,sha256=dMPHVlkPsEhqOHQmuX8QlxJ48c1Pml8Q7RpOhc9lYUk,3511 +wandb/vendor/pygments/lexers/clean.py,sha256=IukUXRAQigyPLlD4K1tfZhQ8qH2l6gVn4hGFuHsz_vc,10403 +wandb/vendor/pygments/lexers/compiled.py,sha256=9d_XDV75Fi_EnrsVPAexfs8LRAysfixpZDNqVEhjGUU,1385 +wandb/vendor/pygments/lexers/configs.py,sha256=W1OdBJEwZWp7imQE1bC6a4sGIGoKw5LIbmYwau6u6Ds,28266 +wandb/vendor/pygments/lexers/console.py,sha256=7rwV7Jsci0nC74JDVzNlD5-PlRsfzZspHfYFSC8OKtM,4120 +wandb/vendor/pygments/lexers/crystal.py,sha256=h6M1BX-vWjCUtPQNXH1KqgqoxuB6geR3kCKp74sIb48,16845 +wandb/vendor/pygments/lexers/csound.py,sha256=t4bVgLEkNXEFZdfdfB43yWGXjJhT3aiKN9uLHNrTtZA,12544 +wandb/vendor/pygments/lexers/css.py,sha256=QZadHT6mYr14tMhZKDVRP3uu_AWSmGNIkn7KUr5H_0k,31513 +wandb/vendor/pygments/lexers/d.py,sha256=G-RV1UPAI8Q6mstQCMa4vNhwop0pG2NO1APHkOLdayA,9530 +wandb/vendor/pygments/lexers/dalvik.py,sha256=RDuGFuTqCG1PxhBn-I80iJxegrJpUDi_knxCKwN1cKQ,4420 +wandb/vendor/pygments/lexers/data.py,sha256=HOBr6Xw4QOYI6sD3vvtuSoYRdMaQYBnT0IcgpWUwTx0,18771 +wandb/vendor/pygments/lexers/diff.py,sha256=n7dxgD0_z0W2LH_zf55E1erBs5iBHFZPZ0bItXSeiLI,4873 +wandb/vendor/pygments/lexers/dotnet.py,sha256=IxNL30wmyw-Z_QRSUf9G9kbhz017BP5Pjqn2LEcfE9A,27668 +wandb/vendor/pygments/lexers/dsls.py,sha256=6v76UwDB7PngLM0Ye79mz_cXpg075RBgTzS9VXIsMQw,33336 +wandb/vendor/pygments/lexers/dylan.py,sha256=YJyoYcXD622vwYxqCNtyoHqi-L67CABzNfcg9ERt1Ic,10421 +wandb/vendor/pygments/lexers/ecl.py,sha256=bRD_m3B8npOLIyHjxhcWUCXeD9YCw7I6u5WsCUb7Kpc,5875 +wandb/vendor/pygments/lexers/eiffel.py,sha256=1-7weE5oflU_dzDVhaQFx5ildMbBuCZ3OFXJpJ-U78c,2482 +wandb/vendor/pygments/lexers/elm.py,sha256=3qhDIU0GFJ4YzuVDyvbHZ39dOyY60QFVpRvDdpDeYUw,2996 +wandb/vendor/pygments/lexers/erlang.py,sha256=Ja1SxnF-f48ort3oRb4R9aaHXbLJZIp0ItsgI8ITDJM,18936 +wandb/vendor/pygments/lexers/esoteric.py,sha256=wdtih2rpCV-N79Rf2laxzlqSBMYwzQgeCtBcutTbt1E,9490 +wandb/vendor/pygments/lexers/ezhil.py,sha256=o3qj60N-fyLGjcho1lrVW4x-CZ0Fu-rDe7tuqInD5oQ,3020 +wandb/vendor/pygments/lexers/factor.py,sha256=Trq7bM5thNo6BFZ2Tnit82FnWuUK70TL-atOKkLdHng,17864 +wandb/vendor/pygments/lexers/fantom.py,sha256=AGk3ewVsJ8gA9dU6N7iWGIlJLt5p61vRiJmVhyCj7Zg,9982 +wandb/vendor/pygments/lexers/felix.py,sha256=94Ffv8uhADGlom_cjKKvlE1QYpDnxRo0EdPGYhlxNTA,9408 +wandb/vendor/pygments/lexers/forth.py,sha256=uN-Sr5fi4SHnTluGzwmUsPgMFt470gSM212e7M5bH9Q,7144 +wandb/vendor/pygments/lexers/fortran.py,sha256=QRpUhXg0xnUu8EqsbEG6S33Sn8AWU326RiC8J-r2ZVw,9768 +wandb/vendor/pygments/lexers/foxpro.py,sha256=AWnHAryB1dUQkU4I48zoJFI8M_DsoIlEmvemDs1O4nM,26236 +wandb/vendor/pygments/lexers/functional.py,sha256=PPhynqxQZ602rCV-TxNldHRwhjMPfyHkGIOPx0BoVZo,698 +wandb/vendor/pygments/lexers/go.py,sha256=6IS3PKkqGG18OIlYqDGUp46S1jcfAfjhixNJe8Dd3gQ,3701 +wandb/vendor/pygments/lexers/grammar_notation.py,sha256=xyJi8t0X8SFElc3HTsmzF4_2mZs-qHW-ur9kZKt48D0,6328 +wandb/vendor/pygments/lexers/graph.py,sha256=-71ZtKUTq4LX4uUyRWFruA57Z_8lyOas1z1bWdq8eMs,2370 +wandb/vendor/pygments/lexers/graphics.py,sha256=6AoxbU5ybgg0_Mm48dZlspishe7PuK0pa_8bsPsDVfM,25836 +wandb/vendor/pygments/lexers/haskell.py,sha256=uOzfXPsn9mBs26GzEm-3qNNNJy6Oxc66lt0NVDVRR-M,31218 +wandb/vendor/pygments/lexers/haxe.py,sha256=QWR5lBwbdCMrJbFcUAs4SnP66zVV8O-DiT4c4x6XtBA,30953 +wandb/vendor/pygments/lexers/hdl.py,sha256=h3CYE2ShUOejE7w0d1eSHkGgCS4EXJodOVgjfACWYz8,18699 +wandb/vendor/pygments/lexers/hexdump.py,sha256=-_u6qAw7yH7b8_25tHNLiybD3r5-OaYYwtf9RsSxfeE,3507 +wandb/vendor/pygments/lexers/html.py,sha256=1f0HI3wZoGZL9PY6dLdmG8AytfT3oEosKHQp3DuDZmY,19269 +wandb/vendor/pygments/lexers/idl.py,sha256=gNT6hIlEMD-ZjLZ3ru6RH7Z-X5u7TKQ7lRP_9Sz1SSg,14984 +wandb/vendor/pygments/lexers/igor.py,sha256=PsDRDxMkwslMjd4GdZmAOXLrblsc2FANBMlNfv-XbVg,19994 +wandb/vendor/pygments/lexers/inferno.py,sha256=G4uU8YQD9EGvDNCc6Av6LWvyRB8unp5qv47PfLUek_8,3116 +wandb/vendor/pygments/lexers/installers.py,sha256=QEPP1YcZ38DvErs7xwpChvKQFh1I6fIp0V6MXxIwzeY,12866 +wandb/vendor/pygments/lexers/int_fiction.py,sha256=m8rYr5egxBSxZ9FKunV_Yl_EtpoRBz1j55J_dtKG9oo,55778 +wandb/vendor/pygments/lexers/iolang.py,sha256=1kvzEPcJZ2eyF7mu58NT9yyqZobOvx1MlbUxA98bLJs,1904 +wandb/vendor/pygments/lexers/j.py,sha256=e6oqJQslgSl6q3hfEuTl0mtFnqCj4G29SwcMnTeBDBc,4525 +wandb/vendor/pygments/lexers/javascript.py,sha256=79c9e2wn7HiJ9S4WvRQ0qymMqj-YV7CS308_pMRNirk,60134 +wandb/vendor/pygments/lexers/julia.py,sha256=vXU5r_p6ko-I_fKITqiFmMn0rpw0NWujKVGGrndkfOI,14093 +wandb/vendor/pygments/lexers/jvm.py,sha256=9fjCUGFGApGNMDsrCWzaEiDA_CcoZPDaYAvBHFAh524,66792 +wandb/vendor/pygments/lexers/lisp.py,sha256=xeF_wQGdm-vkJ2j1JQd9mipAAtjwY3giOEH2cW2j0jk,140673 +wandb/vendor/pygments/lexers/make.py,sha256=jGkYUD5PD4vG7gC6qExOAp8ZE4KoB0jLMcTF5NzZGQs,7332 +wandb/vendor/pygments/lexers/markup.py,sha256=KQsXc5K_rn7G7bATKt81yURJjHHi5kCLokJXXom997I,20452 +wandb/vendor/pygments/lexers/math.py,sha256=TaE1dzOfu6ukxMFAInZI_Ua105LDnxx86Ufk4YdnvfA,700 +wandb/vendor/pygments/lexers/matlab.py,sha256=GUys_wezfz7n_F9U5l2HZauxlsSxrRh0bkgIjfhZ7ro,29146 +wandb/vendor/pygments/lexers/ml.py,sha256=GDreW4Pmgqt8BlfbeRG43e3wS3wSm_A7cHPKGeGY9hQ,27891 +wandb/vendor/pygments/lexers/modeling.py,sha256=4O9hB4wAhjD2HFYa74HsLf9QK4E8kq7k8c5M7WuM1f4,12833 +wandb/vendor/pygments/lexers/modula2.py,sha256=mNbhlG9lNZRXP9kzx9diJj7l6hiPQVqmOzrfJ53GarU,52561 +wandb/vendor/pygments/lexers/monte.py,sha256=MNswWqletPzSIg6TcECb-Dcq1bv-eSPhYLsbc-vnJzM,6307 +wandb/vendor/pygments/lexers/ncl.py,sha256=cvnCcYpC0jpHBrwtNXZhY5HheY3UMThsYbgscHGvZLM,63986 +wandb/vendor/pygments/lexers/nimrod.py,sha256=OizSj9kHvtFEu0OPXuBrXZCf_ZBMnR4hm3nMbjw50hQ,5174 +wandb/vendor/pygments/lexers/nit.py,sha256=1SJ45GOd9zQKuWhPuVy-KhLkXQn6V-7ESvjJJ7U9Vpo,2743 +wandb/vendor/pygments/lexers/nix.py,sha256=dWzLp5QxzLmskDflyMUGidGNau9Vtk8ViK5c6B2O4kI,4031 +wandb/vendor/pygments/lexers/oberon.py,sha256=CbqkEOdqxvWXfez5FiGeCE-f-Vlt6tu9kqPFEVVXAQc,3733 +wandb/vendor/pygments/lexers/objective.py,sha256=qCPLLfU3hl-CIOJWAZdQYjeIl1Ox1igZaPRGKowKYbs,22764 +wandb/vendor/pygments/lexers/ooc.py,sha256=_lnOynAR0vHli7OKWJ4yZs9-hyPZE5DGKwkdkP6SCuM,2999 +wandb/vendor/pygments/lexers/other.py,sha256=Utlo5esm3DetK9PSOueHu5mEjR25dIN3T_g2-u9cmeA,1768 +wandb/vendor/pygments/lexers/parasail.py,sha256=Egm3PcuCBjDgwUvvI0-t3oXMz4PPRY0dER3orrrlnfc,2737 +wandb/vendor/pygments/lexers/parsers.py,sha256=S93llPIzcqL0VU2C_SzFa-pykNcFE37Kvy9_7fNmdg4,27582 +wandb/vendor/pygments/lexers/pascal.py,sha256=tA4Na6dzTz8nd9-Q_tcBFVDo161qfG9bR4xIIIB8FY0,32646 +wandb/vendor/pygments/lexers/pawn.py,sha256=eJJfXLHPYEPflh3fKgyq4PD5b52pcqjBnAGDicqHK9w,8094 +wandb/vendor/pygments/lexers/perl.py,sha256=-k_lftMhedsEIQwmvEZyy8Xbtlgj28_fic6HhN_ZYGI,32012 +wandb/vendor/pygments/lexers/php.py,sha256=7tgZQvYAnprFRQfAUxOlXgNkeUyOYsvIkM5oW0NSzOU,10730 +wandb/vendor/pygments/lexers/praat.py,sha256=GpHWS1IyqpvFCbZ7JWXSyxgBl8KAQED8IonkJM-Ml28,12556 +wandb/vendor/pygments/lexers/prolog.py,sha256=GWAUHvP3eyROfr3ry8g_7OJSNpD3vwfAFTJsrqIL7cQ,12066 +wandb/vendor/pygments/lexers/python.py,sha256=pn_V-1XyAqoYD4x8Pclk4eP8nkYvacpObY06xwDYSq8,42384 +wandb/vendor/pygments/lexers/qvt.py,sha256=LnLMM2jTrc5k4kInraQmmui1mgf76Wx5Ln2CfBFqj4I,6111 +wandb/vendor/pygments/lexers/r.py,sha256=6aUQ8T4qn6qIWuR-D9P_r05CEcP5SJ8wMadzUVrgJ4k,23755 +wandb/vendor/pygments/lexers/rdf.py,sha256=qbH7suOIdGY435x721iWhJ9QXT3MYPo874Jss8f2WCY,9398 +wandb/vendor/pygments/lexers/rebol.py,sha256=91kF9ueomN_2-Za8u1nIsuYN6uTQ3T1UbZk3IfRoiD8,18617 +wandb/vendor/pygments/lexers/resource.py,sha256=uS23PmW5COZfyyCte9s6xLhqZS2uhfH5P3G41QGp5U0,2933 +wandb/vendor/pygments/lexers/rnc.py,sha256=QvwLWh7IG9LYp9hmD7dI2MM4ikNaeqSQf26LmCBPVLA,1990 +wandb/vendor/pygments/lexers/roboconf.py,sha256=2__rRqffgFjrqyFFHHjibSg1qHCm2_lIT7lOGu63DNI,2070 +wandb/vendor/pygments/lexers/robotframework.py,sha256=qKKxIoTAMDnr_iDEnFsF9sjH63uhr1hDQUMStJydMYo,18744 +wandb/vendor/pygments/lexers/ruby.py,sha256=pukJJddNWR7jC4Uxc_qnOrcFXI5am3d0A8ApiZfeAnQ,22141 +wandb/vendor/pygments/lexers/rust.py,sha256=q18g2pHP_srVa8ANoyiIoMmXB4XkYI6Xp57rC-V9xmU,7695 +wandb/vendor/pygments/lexers/sas.py,sha256=oKP_7bIyKUnFS8kDGKsWxHqKhkRBV4emle28pX3MUzE,9449 +wandb/vendor/pygments/lexers/scripting.py,sha256=1_gD_-cQHrDF-uDEmBlelrUSWV5cQoz79EZ_3RpAh48,67761 +wandb/vendor/pygments/lexers/shell.py,sha256=H1byqbwQefCUaSDra6EabmI2o15zfpeusc73p1mBDQw,31426 +wandb/vendor/pygments/lexers/smalltalk.py,sha256=QJHVLNsCWr8zWHoF9C9Lev7LwefFYgI7L4mtY-LWJRU,7215 +wandb/vendor/pygments/lexers/smv.py,sha256=HnLzDk-zFDXhaHXxFI-ZINv6pW6stkk0IfxVHgStOLY,2802 +wandb/vendor/pygments/lexers/snobol.py,sha256=glC_g_14iKbHWaYCdkMibhGBNdPbzOgl7MVlJbpZMUI,2756 +wandb/vendor/pygments/lexers/special.py,sha256=Q1qXU0E8ecPXL_rMnzksLNbcisjOexxjXmo9j5ODBmM,3151 +wandb/vendor/pygments/lexers/sql.py,sha256=eMnkUuSpfII72c_lCcxOSWkuAuZH7qN6GkwM1xllMpA,29445 +wandb/vendor/pygments/lexers/stata.py,sha256=XI46JTQw-9YH5TkDB35FfFy8lJVaxr7qbdnOWbFhG3s,3627 +wandb/vendor/pygments/lexers/supercollider.py,sha256=t5LHZNcKwtgxnnCJnHCSYGjHBGXZzNR_TLF_pPOMrh4,3516 +wandb/vendor/pygments/lexers/tcl.py,sha256=ulsFqa6OwCKUoJHDvqBLdVHZL31EA5BDI8bf3iB703E,5398 +wandb/vendor/pygments/lexers/templates.py,sha256=V4ColLZh2NTkxI_zr4J7JnpRAafVNEezy3GWjq8knXs,73457 +wandb/vendor/pygments/lexers/testing.py,sha256=lup3FuiDthufXo3ujm6HSV4QKBscYoTpNe9K5iHCZ2g,10751 +wandb/vendor/pygments/lexers/text.py,sha256=7nKWCivruly7wyquaug3-IF8v2fUdXUVSYXHMVUlkdY,977 +wandb/vendor/pygments/lexers/textedit.py,sha256=1kH4uCnq4j80c0KGSMRJgzRgglm_5ckoiQVZX21qXvc,6057 +wandb/vendor/pygments/lexers/textfmts.py,sha256=sOQ4vDo5P1SpY_obS7N5oObolY501Tk2va69iDQyWWM,10852 +wandb/vendor/pygments/lexers/theorem.py,sha256=HmW981GDEA0TM8H7siEv2lszH-U9S7hcExRyom2eX2w,19034 +wandb/vendor/pygments/lexers/trafficscript.py,sha256=Xyfa407k2_rzvlKuExUcB5h6bC89WN6eQx6K3o3uV3o,1546 +wandb/vendor/pygments/lexers/typoscript.py,sha256=c6gOzNkvqgixbRKeQO7vXSrEPf6K4pnrFLFQ9SzusA8,8392 +wandb/vendor/pygments/lexers/urbi.py,sha256=p4gxSltIKKMFL8wJuLQ_cC40zSAC8bqc7aXZT0s5M7Y,5750 +wandb/vendor/pygments/lexers/varnish.py,sha256=5aSrhCfYFRbZsvKtQuKzJjHGCosabDGua3_LCG-JLlA,7265 +wandb/vendor/pygments/lexers/verification.py,sha256=Pb3upMUI03UFFsYAmqEzB2VK3N-ppMs1KYExE_tS1yE,3705 +wandb/vendor/pygments/lexers/web.py,sha256=5RB4JP962BUzuMIHon_H3tRgO458FAROJ91S8uk-gno,918 +wandb/vendor/pygments/lexers/webmisc.py,sha256=-6FzwUll5VU4oN6rSutfQmqPqD_OvWZ7kZWO1H3WMXo,39891 +wandb/vendor/pygments/lexers/whiley.py,sha256=YNCb-hXy8rPPVH9ea4q8QAXqncXNPHtgqAqAKPLfHfM,4012 +wandb/vendor/pygments/lexers/x10.py,sha256=FyCneMc5r_PRL7a0iaAsvgfyrxk2dYSzE9WJa1O4ZBw,1965 +wandb/vendor/pygments/modeline.py,sha256=83v6_3Zo9RHEc0K8aepm1Sc8Iou7mRcwogl9MLUciiI,1010 +wandb/vendor/pygments/plugin.py,sha256=NFKzfoPXMA8x6yXqMVbM0k1YKHHeQIwnDEvVlxd-MEQ,1721 +wandb/vendor/pygments/regexopt.py,sha256=t8y9hRPYQKJysZQhWd_t15aqRAGJSk0ABynKyjmogIQ,3094 +wandb/vendor/pygments/scanner.py,sha256=Mf-ya9riFJ85N3AiGKvBUsAsjSEfTd0Bqw8KMxhj2ew,3123 +wandb/vendor/pygments/sphinxext.py,sha256=1nig8VbRlfIDZd1fhOKQmX2KcxyQoexwsOdX3DSGE1I,4655 +wandb/vendor/pygments/style.py,sha256=R-iSqMPqnMtciPTgPMvqNjUkWtg7CCQTpyyCBSAOv_g,4807 +wandb/vendor/pygments/styles/__init__.py,sha256=bMJA5A-As-KqVlgOVa1ryoivG-fI20nE5mlEvneFqT0,2553 +wandb/vendor/pygments/styles/abap.py,sha256=GYW80_C20vlhROQCE9RIn8oOy0uFO5VeJJMm7FXCEz8,751 +wandb/vendor/pygments/styles/algol.py,sha256=mE09x_hUp-ferskH8OLAWO115u9GQQYMR70Y-2zS6Y0,2263 +wandb/vendor/pygments/styles/algol_nu.py,sha256=psY0F47Bl5LThIeih15ghZvkalijyq6jrUnx84n_TFg,2278 +wandb/vendor/pygments/styles/arduino.py,sha256=UTcdXehOhcXWda9NlYeW3piKZUbEpQQkx0ARZCC7Ih4,4492 +wandb/vendor/pygments/styles/autumn.py,sha256=FpFEPsPBnGugtBp4M-3FqKPbKlGomYAbq9HSNiYh3DU,2144 +wandb/vendor/pygments/styles/borland.py,sha256=QhOMFrDL3PAwEJZkuEt9qzPN8T8cVooRG5vtLROgkRc,1562 +wandb/vendor/pygments/styles/bw.py,sha256=vBQMjQhou4csJMHTIE5AJCeLESFaE5UqgSPN2ZAli6o,1355 +wandb/vendor/pygments/styles/colorful.py,sha256=SWzWtEmeEoZ7DNIh6XQeZxDz6bIOlgBuwGFePB2PUmc,2778 +wandb/vendor/pygments/styles/default.py,sha256=UUN6VGAOB12b_R2ky-6cHfdaXE34xJaNtaHwsBzl1I8,2532 +wandb/vendor/pygments/styles/emacs.py,sha256=CndsC1mEd6GGVCFA1uKmb5rMqjMiMCqJ1uBE2fgKCQA,2486 +wandb/vendor/pygments/styles/friendly.py,sha256=0c5ibD9ldewtsKzgjHSV2-FN_iRdDa_bUkVWi8ZnfdE,2515 +wandb/vendor/pygments/styles/fruity.py,sha256=cSYED092upFc4kekb2m-f34Wh4BWxXyEb8N6rYLZOas,1298 +wandb/vendor/pygments/styles/igor.py,sha256=MZNAVwgWh5FU2bRbKaXo5uV9_h2IH3gh4Cm3MspnFIc,739 +wandb/vendor/pygments/styles/lovelace.py,sha256=u0fQW4OUZ37w9Z1A0pzsMYfJX68kxMrp0CbjWJg46pE,3173 +wandb/vendor/pygments/styles/manni.py,sha256=2CC4G2o6hUYfCceP4O9bpNBgNONW7HCJ--L3khD7jhc,2374 +wandb/vendor/pygments/styles/monokai.py,sha256=QgSUuhv6DDz7LhyfL9lW5VMIRJIj0ID_GqTJupunWgs,5080 +wandb/vendor/pygments/styles/murphy.py,sha256=m2bWPIFW8gnR2miA6FF2kYDwXT2sltSJ9MMcA1AEnqg,2751 +wandb/vendor/pygments/styles/native.py,sha256=V_mDvPG5eUm8V2XSCYsKsqkp0XfxK8gn80wryiUmmIU,1938 +wandb/vendor/pygments/styles/paraiso_dark.py,sha256=YxdADs3KBND4ZXfYqkQBXGxIs2y4VxjaiKw8f3AdoFo,5641 +wandb/vendor/pygments/styles/paraiso_light.py,sha256=-HHuYHnJBX7Yehza875DjU4ly95ATC1sh6o_RuqbeuI,5645 +wandb/vendor/pygments/styles/pastie.py,sha256=6ku_vX2aFP7N9iAsT8a-1lzf9L1DR_j0SE37HVbFoSw,2473 +wandb/vendor/pygments/styles/perldoc.py,sha256=CJq6FzNG22C3aru3QO2_l6ebksqnAQhSBLeiYYoJjhw,2175 +wandb/vendor/pygments/styles/rainbow_dash.py,sha256=mPI5Yfl-sRW9Aig_xvs63i3sjWqO5fpihBbiaLozyYM,2480 +wandb/vendor/pygments/styles/rrt.py,sha256=-UUYhzna_sPcmwUXEtnDoAaUD8cnomk8grWfKOsmJ-M,852 +wandb/vendor/pygments/styles/sas.py,sha256=yKgZ8AiADLXtVZLZ0JtxlB6BGJAVkcX0DyG8g-R8nSE,1441 +wandb/vendor/pygments/styles/stata.py,sha256=PH8ckY6Q68QQXjv8wDK_Z-DVbyiWfy5vRLd4HUYd02k,1249 +wandb/vendor/pygments/styles/tango.py,sha256=Bsn6unVhwiOJ8dc1-csQYHanRjIZ7JOxQndq6Dc-BxE,7096 +wandb/vendor/pygments/styles/trac.py,sha256=XlcVcvSqIfVeqSsQo34V5B0VLi_FQwlpfaE0Zv7LE0M,1933 +wandb/vendor/pygments/styles/vim.py,sha256=hgv2HvW3kriTmj0HE7TdzeJhJhxvblqEG-S3ELiZEX4,1976 +wandb/vendor/pygments/styles/vs.py,sha256=X7jUKNoKNnNE6ewe9vsGgTeSxK4a7x8oBbaA6LNzpGQ,1073 +wandb/vendor/pygments/styles/xcode.py,sha256=gWl7xboxgvDzARO8-VmBfUX0GOnWWbGU9Su_TAIyEFQ,1501 +wandb/vendor/pygments/token.py,sha256=qowb6oBuyzaRfTyUe7F-LeMcNwRNLEiwjZl9o3ggsxc,6167 +wandb/vendor/pygments/unistring.py,sha256=rkFlgo6QzdUFWQ-uiOaz6MFfQg1f0-hH_B5F3PFXwXA,51150 +wandb/vendor/pygments/util.py,sha256=xO8RGLd_-7DoXCL799lWYgyub4whP2pSmnIsx1-wMyA,11900 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/__init__.py,sha256=-eu2NcfsKwZHudYM1DwZob-gagx9DhVVvJOvmIGWo8o,684 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/events.py,sha256=1ASc8KEcDJr9zL47lLmV2oz8qgepk6NvaZgYKyRLUaE,18623 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/observers/__init__.py,sha256=tu15U0Qqv_306QJNVbjkxDuiTXJ0qV11uVboBBwiDFM,3864 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/observers/api.py,sha256=cS0sFG4bwV6ZgdpDasBIqLjvure7herE5TsiBP4sMAo,11738 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/observers/fsevents.py,sha256=EbY5I7F7jCkHovbVq2w2kUtHBVeUIL4t4dZPzZFevFI,6546 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/observers/fsevents2.py,sha256=yumICK41X4-r7AeSHF3MzzT9BXnGRkRhLzOT53oM9fY,9043 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/observers/inotify.py,sha256=n8kgJ1zC_96SLEw8v0ZqHvOMRUg558zShwvIgdgLdJQ,8546 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/observers/inotify_buffer.py,sha256=KT-1Unpru9UOMrKVgBt-KcI67dffMZNQpBmU27-uNgg,3055 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/observers/inotify_c.py,sha256=IVH9UuEyLnYYiMXcLzpF0udT8jxAGSNbdCM38ttmf7M,18911 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/observers/kqueue.py,sha256=vND2sY0FHxbliFacyaaKAoFg0XIfOPW4azk0UshVBVs,25419 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/observers/polling.py,sha256=qoWOKJ4g_FFbrz_kzMEHAU5F7piWsQr-TsJzHA2tk0g,4875 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/observers/read_directory_changes.py,sha256=qDppFQyKrxC9eZy8DRWVbalIBLjYAN10fwp1OSrzugY,5262 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/observers/winapi.py,sha256=dsnl8btBrosGICw-EW_aHr-B0c5hgsS0P7O6JAjAVOw,11685 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/patterns.py,sha256=xWatHsYukVTIwVOw8O4BB0-_-ajx2z2vgGCA_NUfyss,10698 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/tricks/__init__.py,sha256=b9I8ob_xvIQXqAVOx6Ujvhac2Es5mj4Kcp9m5gB1sd8,5198 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/__init__.py,sha256=GK7wGeS6z8UkG5BDUB-sHuLu8lFt7h77UdHFe5QBZmY,4472 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/bricks.py,sha256=0iMa_IT_lX2-aY_GR0Yf2d925kFCajvnz-kXdV5d1vc,7548 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/compat.py,sha256=ezeJrD0LTvg2arGGWhQ0oq4CB31Hqi2FvJoJbDgX7No,860 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/decorators.py,sha256=MkJQIlK9L3FD62_mzr-zlgJMQG6bey8C06xUxWkKucQ,4451 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/delayed_queue.py,sha256=Q0Eylpvc-9wuKmvVcgk1dvm8GKJvif7zdLdk58P17aI,2926 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/dirsnapshot.py,sha256=l4I52zaCbXS3wt1dxYHT2JhaH8UUuQtrcyvULlBFP3o,9319 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/echo.py,sha256=frPR2B1ZOvNue66V1WfzDjjX-fnv5_xYw36hQT6tuU4,5305 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/event_backport.py,sha256=vnOWu6nNdSJJo69VSxeCfnfsXOo4CwTQDlMr6jAa6eI,902 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/importlib2.py,sha256=cJIaJ2EQsoA8eWZ3d8hvgXq3OUxoRDdD5jo9XbUMRMI,1840 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/platform.py,sha256=UORYTNVcUSE2NpFcq9UVLIS-tsS0TS_Qw8akhKxn2eY,1506 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/unicode_paths.py,sha256=UWX8DB97ygkEeSxWQUYCHR4MahNilux7vl5TCTQtPPk,2190 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/utils/win32stat.py,sha256=ZOevOTbSo8NRiIxkuBVGaG4yigWnPoO0goxAi-jsBkM,3828 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/version.py,sha256=GAUJcx7KVSbKa25fjKipygYc9zPtEPDDDcwlUpKfgmI,974 +wandb/vendor/watchdog_0_9_0/wandb_watchdog/watchmedo.py,sha256=wiAGdrwO3F3bOxExzkzve0cSPEUskuPo4VR1u41qRU8,17458 +wandb/wandb_agent.py,sha256=JWRqi6f_np-4XGcNz0QdKE6OmYvCdSQ2K6gcE-oJF9w,22815 +wandb/wandb_controller.py,sha256=SksJdgwn14PpnUoIaBjJ9Ki4Nksl9BpQGGn42hT0xZg,24936 +wandb/wandb_run.py,sha256=RRjZweHEMpfTyQghzOrSZdmViIItT9wLJYzcZ4nlvjk,128 diff --git a/.venv/lib/python3.12/site-packages/wandb-0.22.2.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/wandb-0.22.2.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/wandb-0.22.2.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/wandb-0.22.2.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..3bb071302aa652134e6a1a3f3fbe6beaa2a1690f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/wandb-0.22.2.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.27.0 +Root-Is-Purelib: true +Tag: py3-none-manylinux_2_17_x86_64 +Tag: py3-none-manylinux2014_x86_64 + diff --git a/.venv/lib/python3.12/site-packages/wandb-0.22.2.dist-info/entry_points.txt b/.venv/lib/python3.12/site-packages/wandb-0.22.2.dist-info/entry_points.txt new file mode 100644 index 0000000000000000000000000000000000000000..bd9806f6b8b58b8ad6081627328a817c45942064 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/wandb-0.22.2.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +wandb = wandb.cli.cli:cli +wb = wandb.cli.cli:cli diff --git a/.venv/lib/python3.12/site-packages/wandb/__init__.py b/.venv/lib/python3.12/site-packages/wandb/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a1c2e09fdcbcb01e86fa45034ce01ebe959f3862 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/wandb/__init__.py @@ -0,0 +1,248 @@ +"""Use wandb to track machine learning work. + +Train and fine-tune models, manage models from experimentation to production. + +For guides and examples, see https://docs.wandb.ai. + +For scripts and interactive notebooks, see https://github.com/wandb/examples. + +For reference documentation, see https://docs.wandb.com/ref/python. +""" +from __future__ import annotations + +__version__ = "0.22.2" + + +from wandb.errors import Error + +# This needs to be early as other modules call it. +from wandb.errors.term import termsetup, termlog, termerror, termwarn + +# Configure the logger as early as possible for consistent behavior. +from wandb.sdk.lib import wb_logging as _wb_logging +_wb_logging.configure_wandb_logger() + +from wandb import sdk as wandb_sdk + +import wandb + +wandb.wandb_lib = wandb_sdk.lib # type: ignore + +init = wandb_sdk.init +setup = wandb_sdk.setup +attach = _attach = wandb_sdk._attach +_sync = wandb_sdk._sync +teardown = _teardown = wandb_sdk.teardown +finish = wandb_sdk.finish +join = finish +login = wandb_sdk.login +helper = wandb_sdk.helper +sweep = wandb_sdk.sweep +controller = wandb_sdk.controller +require = wandb_sdk.require +Artifact = wandb_sdk.Artifact +AlertLevel = wandb_sdk.AlertLevel +Settings = wandb_sdk.Settings +Config = wandb_sdk.Config + +from wandb.apis import InternalApi, PublicApi +from wandb.errors import CommError, UsageError + +_preinit = wandb.wandb_lib.preinit # type: ignore +_lazyloader = wandb.wandb_lib.lazyloader # type: ignore + +from wandb.integration.torch import wandb_torch + +# Move this (keras.__init__ expects it at top level) +from wandb.sdk.data_types._private import _cleanup_media_tmp_dir + +_cleanup_media_tmp_dir() + +from wandb.data_types import Graph +from wandb.data_types import Image +from wandb.data_types import Plotly + +# from wandb.data_types import Bokeh # keeping out of top level for now since Bokeh plots have poor UI +from wandb.data_types import Video +from wandb.data_types import Audio +from wandb.data_types import Table +from wandb.data_types import Html +from wandb.data_types import box3d +from wandb.data_types import Object3D +from wandb.data_types import Molecule +from wandb.data_types import Histogram +from wandb.data_types import Classes +from wandb.data_types import JoinedTable + +from wandb.wandb_agent import agent + +from wandb.plot import visualize, plot_table +from wandb.integration.sagemaker import sagemaker_auth +from wandb.sdk.internal import profiler +from wandb.sdk.wandb_run import Run + +# Artifact import types +from wandb.sdk.artifacts.artifact_ttl import ArtifactTTL + +# Used to make sure we don't use some code in the incorrect process context +_IS_INTERNAL_PROCESS = False + + +def _set_internal_process(disable=False): + global _IS_INTERNAL_PROCESS + if _IS_INTERNAL_PROCESS is None: + return + if disable: + _IS_INTERNAL_PROCESS = None + return + _IS_INTERNAL_PROCESS = True + + +def _assert_is_internal_process(): + if _IS_INTERNAL_PROCESS is None: + return + assert _IS_INTERNAL_PROCESS + + +def _assert_is_user_process(): + if _IS_INTERNAL_PROCESS is None: + return + assert not _IS_INTERNAL_PROCESS + + +# globals +Api = PublicApi +api = InternalApi() +run: Run | None = None +config = _preinit.PreInitObject("wandb.config", wandb_sdk.wandb_config.Config) +summary = _preinit.PreInitObject("wandb.summary", wandb_sdk.wandb_summary.Summary) +log = _preinit.PreInitCallable("wandb.log", Run.log) # type: ignore +watch = _preinit.PreInitCallable("wandb.watch", Run.watch) # type: ignore +unwatch = _preinit.PreInitCallable("wandb.unwatch", Run.unwatch) # type: ignore +save = _preinit.PreInitCallable("wandb.save", Run.save) # type: ignore +restore = wandb_sdk.wandb_run.restore +use_artifact = _preinit.PreInitCallable( + "wandb.use_artifact", Run.use_artifact # type: ignore +) +log_artifact = _preinit.PreInitCallable( + "wandb.log_artifact", Run.log_artifact # type: ignore +) +log_model = _preinit.PreInitCallable( + "wandb.log_model", Run.log_model # type: ignore +) +use_model = _preinit.PreInitCallable( + "wandb.use_model", Run.use_model # type: ignore +) +link_model = _preinit.PreInitCallable( + "wandb.link_model", Run.link_model # type: ignore +) +define_metric = _preinit.PreInitCallable( + "wandb.define_metric", Run.define_metric # type: ignore +) + +mark_preempting = _preinit.PreInitCallable( + "wandb.mark_preempting", Run.mark_preempting # type: ignore +) + +alert = _preinit.PreInitCallable("wandb.alert", Run.alert) # type: ignore + +# record of patched libraries +patched = {"tensorboard": [], "keras": [], "gym": []} # type: ignore + +keras = _lazyloader.LazyLoader("wandb.keras", globals(), "wandb.integration.keras") +sklearn = _lazyloader.LazyLoader("wandb.sklearn", globals(), "wandb.sklearn") +tensorflow = _lazyloader.LazyLoader( + "wandb.tensorflow", globals(), "wandb.integration.tensorflow" +) +xgboost = _lazyloader.LazyLoader( + "wandb.xgboost", globals(), "wandb.integration.xgboost" +) +catboost = _lazyloader.LazyLoader( + "wandb.catboost", globals(), "wandb.integration.catboost" +) +tensorboard = _lazyloader.LazyLoader( + "wandb.tensorboard", globals(), "wandb.integration.tensorboard" +) +gym = _lazyloader.LazyLoader("wandb.gym", globals(), "wandb.integration.gym") +lightgbm = _lazyloader.LazyLoader( + "wandb.lightgbm", globals(), "wandb.integration.lightgbm" +) +jupyter = _lazyloader.LazyLoader("wandb.jupyter", globals(), "wandb.jupyter") +sacred = _lazyloader.LazyLoader("wandb.sacred", globals(), "wandb.integration.sacred") + + +def ensure_configured(): + global api + api = InternalApi() + + +def set_trace(): + import pdb # TODO: support other debuggers + + # frame = sys._getframe().f_back + pdb.set_trace() # TODO: pass the parent stack... + + +def load_ipython_extension(ipython): + ipython.register_magics(wandb.jupyter.WandBMagics) + + +if wandb_sdk.lib.ipython.in_notebook(): + from IPython import get_ipython # type: ignore[import-not-found] + + load_ipython_extension(get_ipython()) + + +from .analytics import Sentry as _Sentry + +if "dev" in __version__: + import wandb.env + import os + + # Disable error reporting in dev versions. + os.environ[wandb.env.ERROR_REPORTING] = os.environ.get( + wandb.env.ERROR_REPORTING, + "false", + ) + +_sentry = _Sentry() +_sentry.setup() + + +__all__ = ( + "__version__", + "init", + "finish", + "setup", + "save", + "sweep", + "controller", + "agent", + "config", + "log", + "summary", + "join", + "Api", + "Graph", + "Image", + "Plotly", + "Video", + "Audio", + "Table", + "Html", + "box3d", + "Object3D", + "Molecule", + "Histogram", + "ArtifactTTL", + "log_artifact", + "use_artifact", + "log_model", + "use_model", + "link_model", + "define_metric", + "watch", + "unwatch", + "plot_table", + "Run", +) diff --git a/.venv/lib/python3.12/site-packages/wandb/__init__.pyi b/.venv/lib/python3.12/site-packages/wandb/__init__.pyi new file mode 100644 index 0000000000000000000000000000000000000000..84e3445b0fc98444c2f9fab0f35695f89f6bbeca --- /dev/null +++ b/.venv/lib/python3.12/site-packages/wandb/__init__.pyi @@ -0,0 +1,1233 @@ +"""Use wandb to track machine learning work. + +Train and fine-tune models, manage models from experimentation to production. + +For guides and examples, see https://docs.wandb.ai. + +For scripts and interactive notebooks, see https://github.com/wandb/examples. + +For reference documentation, see https://docs.wandb.com/ref/python. +""" + +from __future__ import annotations + +__all__ = ( + "__version__", # doc:exclude + "init", + "finish", + "setup", + "login", + "save", # doc:exclude + "sweep", + "controller", + "agent", + "config", # doc:exclude + "log", # doc:exclude + "summary", # doc:exclude + "Api", + "Graph", # doc:exclude + "Image", + "Plotly", + "Video", + "Audio", + "Table", + "Html", + "box3d", + "Object3D", + "Molecule", + "Histogram", + "ArtifactTTL", # doc:exclude + "log_artifact", # doc:exclude + "use_artifact", # doc:exclude + "log_model", # doc:exclude + "use_model", # doc:exclude + "link_model", # doc:exclude + "define_metric", # doc:exclude + "Error", # doc:exclude + "termsetup", # doc:exclude + "termlog", # doc:exclude + "termerror", # doc:exclude + "termwarn", # doc:exclude + "Artifact", + "Settings", + "teardown", + "watch", # doc:exclude + "unwatch", # doc:exclude + "plot", # doc:exclude + "plot_table", + "restore", + "Run", +) + +import os +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + Iterable, + List, + Literal, + Optional, + Sequence, + TextIO, + Union, +) + +import wandb.plot as plot +from wandb.analytics import Sentry +from wandb.apis import InternalApi +from wandb.apis import PublicApi as Api +from wandb.data_types import ( + Audio, + Graph, + Histogram, + Html, + Image, + Molecule, + Object3D, + Plotly, + Table, + Video, + box3d, +) +from wandb.errors import Error +from wandb.errors.term import termerror, termlog, termsetup, termwarn +from wandb.sdk import Artifact, Settings, wandb_config, wandb_metric, wandb_summary +from wandb.sdk.artifacts.artifact_ttl import ArtifactTTL +from wandb.sdk.interface.interface import PolicyName +from wandb.sdk.lib.paths import FilePathStr, StrPath +from wandb.sdk.wandb_run import Run +from wandb.sdk.wandb_setup import _WandbSetup +from wandb.wandb_controller import _WandbController + +if TYPE_CHECKING: + import torch # type: ignore [import-not-found] + + import wandb + from wandb.plot import CustomChart + +__version__: str = "0.22.2" + +run: Run | None +config: wandb_config.Config +summary: wandb_summary.Summary + +# private attributes +_sentry: Sentry +api: InternalApi +patched: Dict[str, List[Callable]] + +def require( + requirement: str | Iterable[str] | None = None, + experiment: str | Iterable[str] | None = None, +) -> None: + """Indicate which experimental features are used by the script. + + This should be called before any other `wandb` functions, ideally right + after importing `wandb`. + + Args: + requirement: The name of a feature to require or an iterable of + feature names. + experiment: An alias for `requirement`. + + Raises: + wandb.errors.UnsupportedError: If a feature name is unknown. + """ + ... + +def setup(settings: Settings | None = None) -> _WandbSetup: + """Prepares W&B for use in the current process and its children. + + You can usually ignore this as it is implicitly called by `wandb.init()`. + + When using wandb in multiple processes, calling `wandb.setup()` + in the parent process before starting child processes may improve + performance and resource utilization. + + Note that `wandb.setup()` modifies `os.environ`, and it is important + that child processes inherit the modified environment variables. + + See also `wandb.teardown()`. + + Args: + settings: Configuration settings to apply globally. These can be + overridden by subsequent `wandb.init()` calls. + + Example: + ```python + import multiprocessing + + import wandb + + def run_experiment(params): + with wandb.init(config=params): + # Run experiment + pass + + if __name__ == "__main__": + # Start backend and set global config + wandb.setup(settings={"project": "my_project"}) + + # Define experiment parameters + experiment_params = [ + {"learning_rate": 0.01, "epochs": 10}, + {"learning_rate": 0.001, "epochs": 20}, + ] + + # Start multiple processes, each running a separate experiment + processes = [] + for params in experiment_params: + p = multiprocessing.Process(target=run_experiment, args=(params,)) + p.start() + processes.append(p) + + # Wait for all processes to complete + for p in processes: + p.join() + + # Optional: Explicitly shut down the backend + wandb.teardown() + ``` + """ + ... + +def teardown(exit_code: int | None = None) -> None: + """Waits for W&B to finish and frees resources. + + Completes any runs that were not explicitly finished + using `run.finish()` and waits for all data to be uploaded. + + It is recommended to call this at the end of a session + that used `wandb.setup()`. It is invoked automatically + in an `atexit` hook, but this is not reliable in certain setups + such as when using Python's `multiprocessing` module. + """ + ... + +def init( + entity: str | None = None, + project: str | None = None, + dir: StrPath | None = None, + id: str | None = None, + name: str | None = None, + notes: str | None = None, + tags: Sequence[str] | None = None, + config: dict[str, Any] | str | None = None, + config_exclude_keys: list[str] | None = None, + config_include_keys: list[str] | None = None, + allow_val_change: bool | None = None, + group: str | None = None, + job_type: str | None = None, + mode: Literal["online", "offline", "disabled", "shared"] | None = None, + force: bool | None = None, + anonymous: Literal["never", "allow", "must"] | None = None, + reinit: ( + bool + | Literal[ + None, + "default", + "return_previous", + "finish_previous", + "create_new", + ] + ) = None, + resume: bool | Literal["allow", "never", "must", "auto"] | None = None, + resume_from: str | None = None, + fork_from: str | None = None, + save_code: bool | None = None, + tensorboard: bool | None = None, + sync_tensorboard: bool | None = None, + monitor_gym: bool | None = None, + settings: Settings | dict[str, Any] | None = None, +) -> Run: + r"""Start a new run to track and log to W&B. + + In an ML training pipeline, you could add `wandb.init()` to the beginning of + your training script as well as your evaluation script, and each piece would + be tracked as a run in W&B. + + `wandb.init()` spawns a new background process to log data to a run, and it + also syncs data to https://wandb.ai by default, so you can see your results + in real-time. When you're done logging data, call `wandb.Run.finish()` to end the run. + If you don't call `run.finish()`, the run will end when your script exits. + + Run IDs must not contain any of the following special characters `/ \ # ? % :` + + Args: + entity: The username or team name the runs are logged to. + The entity must already exist, so ensure you create your account + or team in the UI before starting to log runs. If not specified, the + run will default your default entity. To change the default entity, + go to your settings and update the + "Default location to create new projects" under "Default team". + project: The name of the project under which this run will be logged. + If not specified, we use a heuristic to infer the project name based + on the system, such as checking the git root or the current program + file. If we can't infer the project name, the project will default to + `"uncategorized"`. + dir: The absolute path to the directory where experiment logs and + metadata files are stored. If not specified, this defaults + to the `./wandb` directory. Note that this does not affect the + location where artifacts are stored when calling `download()`. + id: A unique identifier for this run, used for resuming. It must be unique + within the project and cannot be reused once a run is deleted. For + a short descriptive name, use the `name` field, + or for saving hyperparameters to compare across runs, use `config`. + name: A short display name for this run, which appears in the UI to help + you identify it. By default, we generate a random two-word name + allowing easy cross-reference runs from table to charts. Keeping these + run names brief enhances readability in chart legends and tables. For + saving hyperparameters, we recommend using the `config` field. + notes: A detailed description of the run, similar to a commit message in + Git. Use this argument to capture any context or details that may + help you recall the purpose or setup of this run in the future. + tags: A list of tags to label this run in the UI. Tags are helpful for + organizing runs or adding temporary identifiers like "baseline" or + "production." You can easily add, remove tags, or filter by tags in + the UI. + If resuming a run, the tags provided here will replace any existing + tags. To add tags to a resumed run without overwriting the current + tags, use `run.tags += ("new_tag",)` after calling `run = wandb.init()`. + config: Sets `wandb.config`, a dictionary-like object for storing input + parameters to your run, such as model hyperparameters or data + preprocessing settings. + The config appears in the UI in an overview page, allowing you to + group, filter, and sort runs based on these parameters. + Keys should not contain periods (`.`), and values should be + smaller than 10 MB. + If a dictionary, `argparse.Namespace`, or `absl.flags.FLAGS` is + provided, the key-value pairs will be loaded directly into + `wandb.config`. + If a string is provided, it is interpreted as a path to a YAML file, + from which configuration values will be loaded into `wandb.config`. + config_exclude_keys: A list of specific keys to exclude from `wandb.config`. + config_include_keys: A list of specific keys to include in `wandb.config`. + allow_val_change: Controls whether config values can be modified after their + initial set. By default, an exception is raised if a config value is + overwritten. For tracking variables that change during training, such as + a learning rate, consider using `wandb.log()` instead. By default, this + is `False` in scripts and `True` in Notebook environments. + group: Specify a group name to organize individual runs as part of a larger + experiment. This is useful for cases like cross-validation or running + multiple jobs that train and evaluate a model on different test sets. + Grouping allows you to manage related runs collectively in the UI, + making it easy to toggle and review results as a unified experiment. + job_type: Specify the type of run, especially helpful when organizing runs + within a group as part of a larger experiment. For example, in a group, + you might label runs with job types such as "train" and "eval". + Defining job types enables you to easily filter and group similar runs + in the UI, facilitating direct comparisons. + mode: Specifies how run data is managed, with the following options: + - `"online"` (default): Enables live syncing with W&B when a network + connection is available, with real-time updates to visualizations. + - `"offline"`: Suitable for air-gapped or offline environments; data + is saved locally and can be synced later. Ensure the run folder + is preserved to enable future syncing. + - `"disabled"`: Disables all W&B functionality, making the run’s methods + no-ops. Typically used in testing to bypass W&B operations. + - `"shared"`: (This is an experimental feature). Allows multiple processes, + possibly on different machines, to simultaneously log to the same run. + In this approach you use a primary node and one or more worker nodes + to log data to the same run. Within the primary node you + initialize a run. For each worker node, initialize a run + using the run ID used by the primary node. + force: Determines if a W&B login is required to run the script. If `True`, + the user must be logged in to W&B; otherwise, the script will not + proceed. If `False` (default), the script can proceed without a login, + switching to offline mode if the user is not logged in. + anonymous: Specifies the level of control over anonymous data logging. + Available options are: + - `"never"` (default): Requires you to link your W&B account before + tracking the run. This prevents unintentional creation of anonymous + runs by ensuring each run is associated with an account. + - `"allow"`: Enables a logged-in user to track runs with their account, + but also allows someone running the script without a W&B account + to view the charts and data in the UI. + - `"must"`: Forces the run to be logged to an anonymous account, even + if the user is logged in. + reinit: Shorthand for the "reinit" setting. Determines the behavior of + `wandb.init()` when a run is active. + resume: Controls the behavior when resuming a run with the specified `id`. + Available options are: + - `"allow"`: If a run with the specified `id` exists, it will resume + from the last step; otherwise, a new run will be created. + - `"never"`: If a run with the specified `id` exists, an error will + be raised. If no such run is found, a new run will be created. + - `"must"`: If a run with the specified `id` exists, it will resume + from the last step. If no run is found, an error will be raised. + - `"auto"`: Automatically resumes the previous run if it crashed on + this machine; otherwise, starts a new run. + - `True`: Deprecated. Use `"auto"` instead. + - `False`: Deprecated. Use the default behavior (leaving `resume` + unset) to always start a new run. + If `resume` is set, `fork_from` and `resume_from` cannot be + used. When `resume` is unset, the system will always start a new run. + resume_from: Specifies a moment in a previous run to resume a run from, + using the format `{run_id}?_step={step}`. This allows users to truncate + the history logged to a run at an intermediate step and resume logging + from that step. The target run must be in the same project. + If an `id` argument is also provided, the `resume_from` argument will + take precedence. + `resume`, `resume_from` and `fork_from` cannot be used together, only + one of them can be used at a time. + Note that this feature is in beta and may change in the future. + fork_from: Specifies a point in a previous run from which to fork a new + run, using the format `{id}?_step={step}`. This creates a new run that + resumes logging from the specified step in the target run’s history. + The target run must be part of the current project. + If an `id` argument is also provided, it must be different from the + `fork_from` argument, an error will be raised if they are the same. + `resume`, `resume_from` and `fork_from` cannot be used together, only + one of them can be used at a time. + Note that this feature is in beta and may change in the future. + save_code: Enables saving the main script or notebook to W&B, aiding in + experiment reproducibility and allowing code comparisons across runs in + the UI. By default, this is disabled, but you can change the default to + enable on your settings page. + tensorboard: Deprecated. Use `sync_tensorboard` instead. + sync_tensorboard: Enables automatic syncing of W&B logs from TensorBoard + or TensorBoardX, saving relevant event files for viewing in + the W&B UI. + monitor_gym: Enables automatic logging of videos of the environment when + using OpenAI Gym. + settings: Specifies a dictionary or `wandb.Settings` object with advanced + settings for the run. + + Returns: + A `Run` object. + + Raises: + Error: If some unknown or internal error happened during the run + initialization. + AuthenticationError: If the user failed to provide valid credentials. + CommError: If there was a problem communicating with the WandB server. + UsageError: If the user provided invalid arguments. + KeyboardInterrupt: If user interrupts the run. + + Examples: + `wandb.init()` returns a `Run` object. Use the run object to log data, + save artifacts, and manage the run lifecycle. + + ```python + import wandb + + config = {"lr": 0.01, "batch_size": 32} + with wandb.init(config=config) as run: + # Log accuracy and loss to the run + acc = 0.95 # Example accuracy + loss = 0.05 # Example loss + run.log({"accuracy": acc, "loss": loss}) + ``` + """ + ... + +def finish( + exit_code: int | None = None, + quiet: bool | None = None, +) -> None: + """Finish a run and upload any remaining data. + + Marks the completion of a W&B run and ensures all data is synced to the server. + The run's final state is determined by its exit conditions and sync status. + + Run States: + - Running: Active run that is logging data and/or sending heartbeats. + - Crashed: Run that stopped sending heartbeats unexpectedly. + - Finished: Run completed successfully (`exit_code=0`) with all data synced. + - Failed: Run completed with errors (`exit_code!=0`). + + Args: + exit_code: Integer indicating the run's exit status. Use 0 for success, + any other value marks the run as failed. + quiet: Deprecated. Configure logging verbosity using `wandb.Settings(quiet=...)`. + """ + ... + +def login( + anonymous: Optional[Literal["must", "allow", "never"]] = None, + key: Optional[str] = None, + relogin: Optional[bool] = None, + host: Optional[str] = None, + force: Optional[bool] = None, + timeout: Optional[int] = None, + verify: bool = False, + referrer: Optional[str] = None, +) -> bool: + """Set up W&B login credentials. + + By default, this will only store credentials locally without + verifying them with the W&B server. To verify credentials, pass + `verify=True`. + + Args: + anonymous: Set to "must", "allow", or "never". + If set to "must", always log a user in anonymously. If set to + "allow", only create an anonymous user if the user + isn't already logged in. If set to "never", never log a + user anonymously. Default set to "never". Defaults to `None`. + key: The API key to use. + relogin: If true, will re-prompt for API key. + host: The host to connect to. + force: If true, will force a relogin. + timeout: Number of seconds to wait for user input. + verify: Verify the credentials with the W&B server. + referrer: The referrer to use in the URL login request. + + + Returns: + bool: If `key` is configured. + + Raises: + AuthenticationError: If `api_key` fails verification with the server. + UsageError: If `api_key` cannot be configured and no tty. + """ + ... + +def log( + data: dict[str, Any], + step: int | None = None, + commit: bool | None = None, +) -> None: + """Upload run data. + + Use `log` to log data from runs, such as scalars, images, video, + histograms, plots, and tables. See [Log objects and media](https://docs.wandb.ai/guides/track/log) for + code snippets, best practices, and more. + + Basic usage: + + ```python + import wandb + + with wandb.init() as run: + run.log({"train-loss": 0.5, "accuracy": 0.9}) + ``` + + The previous code snippet saves the loss and accuracy to the run's + history and updates the summary values for these metrics. + + Visualize logged data in a workspace at [wandb.ai](https://wandb.ai), + or locally on a [self-hosted instance](https://docs.wandb.ai/guides/hosting) + of the W&B app, or export data to visualize and explore locally, such as in a + Jupyter notebook, with the [Public API](https://docs.wandb.ai/guides/track/public-api-guide). + + Logged values don't have to be scalars. You can log any + [W&B supported Data Type](https://docs.wandb.ai/ref/python/data-types/) + such as images, audio, video, and more. For example, you can use + `wandb.Table` to log structured data. See + [Log tables, visualize and query data](https://docs.wandb.ai/guides/models/tables/tables-walkthrough) + tutorial for more details. + + W&B organizes metrics with a forward slash (`/`) in their name + into sections named using the text before the final slash. For example, + the following results in two sections named "train" and "validate": + + ```python + with wandb.init() as run: + # Log metrics in the "train" section. + run.log( + { + "train/accuracy": 0.9, + "train/loss": 30, + "validate/accuracy": 0.8, + "validate/loss": 20, + } + ) + ``` + + Only one level of nesting is supported; `run.log({"a/b/c": 1})` + produces a section named "a/b". + + `run.log()` is not intended to be called more than a few times per second. + For optimal performance, limit your logging to once every N iterations, + or collect data over multiple iterations and log it in a single step. + + By default, each call to `log` creates a new "step". + The step must always increase, and it is not possible to log + to a previous step. You can use any metric as the X axis in charts. + See [Custom log axes](https://docs.wandb.ai/guides/track/log/customize-logging-axes/) + for more details. + + In many cases, it is better to treat the W&B step like + you'd treat a timestamp rather than a training step. + + ```python + with wandb.init() as run: + # Example: log an "epoch" metric for use as an X axis. + run.log({"epoch": 40, "train-loss": 0.5}) + ``` + + It is possible to use multiple `wandb.Run.log()` invocations to log to + the same step with the `step` and `commit` parameters. + The following are all equivalent: + + ```python + with wandb.init() as run: + # Normal usage: + run.log({"train-loss": 0.5, "accuracy": 0.8}) + run.log({"train-loss": 0.4, "accuracy": 0.9}) + + # Implicit step without auto-incrementing: + run.log({"train-loss": 0.5}, commit=False) + run.log({"accuracy": 0.8}) + run.log({"train-loss": 0.4}, commit=False) + run.log({"accuracy": 0.9}) + + # Explicit step: + run.log({"train-loss": 0.5}, step=current_step) + run.log({"accuracy": 0.8}, step=current_step) + current_step += 1 + run.log({"train-loss": 0.4}, step=current_step) + run.log({"accuracy": 0.9}, step=current_step) + ``` + + Args: + data: A `dict` with `str` keys and values that are serializable + Python objects including: `int`, `float` and `string`; + any of the `wandb.data_types`; lists, tuples and NumPy arrays + of serializable Python objects; other `dict`s of this + structure. + step: The step number to log. If `None`, then an implicit + auto-incrementing step is used. See the notes in + the description. + commit: If true, finalize and upload the step. If false, then + accumulate data for the step. See the notes in the description. + If `step` is `None`, then the default is `commit=True`; + otherwise, the default is `commit=False`. + + Examples: + For more and more detailed examples, see + [our guides to logging](https://docs.wandb.com/guides/track/log). + + Basic usage + + ```python + import wandb + + with wandb.init() as run: + run.log({"train-loss": 0.5, "accuracy": 0.9 + ``` + + Incremental logging + + ```python + import wandb + + with wandb.init() as run: + run.log({"loss": 0.2}, commit=False) + # Somewhere else when I'm ready to report this step: + run.log({"accuracy": 0.8}) + ``` + + Histogram + + ```python + import numpy as np + import wandb + + # sample gradients at random from normal distribution + gradients = np.random.randn(100, 100) + with wandb.init() as run: + run.log({"gradients": wandb.Histogram(gradients)}) + ``` + + Image from NumPy + + ```python + import numpy as np + import wandb + + with wandb.init() as run: + examples = [] + for i in range(3): + pixels = np.random.randint(low=0, high=256, size=(100, 100, 3)) + image = wandb.Image(pixels, caption=f"random field {i}") + examples.append(image) + run.log({"examples": examples}) + ``` + + Image from PIL + + ```python + import numpy as np + from PIL import Image as PILImage + import wandb + + with wandb.init() as run: + examples = [] + for i in range(3): + pixels = np.random.randint( + low=0, + high=256, + size=(100, 100, 3), + dtype=np.uint8, + ) + pil_image = PILImage.fromarray(pixels, mode="RGB") + image = wandb.Image(pil_image, caption=f"random field {i}") + examples.append(image) + run.log({"examples": examples}) + ``` + + Video from NumPy + + ```python + import numpy as np + import wandb + + with wandb.init() as run: + # axes are (time, channel, height, width) + frames = np.random.randint( + low=0, + high=256, + size=(10, 3, 100, 100), + dtype=np.uint8, + ) + run.log({"video": wandb.Video(frames, fps=4)}) + ``` + + Matplotlib plot + + ```python + from matplotlib import pyplot as plt + import numpy as np + import wandb + + with wandb.init() as run: + fig, ax = plt.subplots() + x = np.linspace(0, 10) + y = x * x + ax.plot(x, y) # plot y = x^2 + run.log({"chart": fig}) + ``` + + PR Curve + + ```python + import wandb + + with wandb.init() as run: + run.log({"pr": wandb.plot.pr_curve(y_test, y_probas, labels)}) + ``` + + 3D Object + + ```python + import wandb + + with wandb.init() as run: + run.log( + { + "generated_samples": [ + wandb.Object3D(open("sample.obj")), + wandb.Object3D(open("sample.gltf")), + wandb.Object3D(open("sample.glb")), + ] + } + ) + ``` + + Raises: + wandb.Error: If called before `wandb.init()`. + ValueError: If invalid data is passed. + """ + ... + +def save( + glob_str: str | os.PathLike, + base_path: str | os.PathLike | None = None, + policy: PolicyName = "live", +) -> bool | list[str]: + """Sync one or more files to W&B. + + Relative paths are relative to the current working directory. + + A Unix glob, such as "myfiles/*", is expanded at the time `save` is + called regardless of the `policy`. In particular, new files are not + picked up automatically. + + A `base_path` may be provided to control the directory structure of + uploaded files. It should be a prefix of `glob_str`, and the directory + structure beneath it is preserved. + + When given an absolute path or glob and no `base_path`, one + directory level is preserved as in the example above. + + Files are automatically deduplicated: calling `save()` multiple times + on the same file without modifications will not re-upload it. + + Args: + glob_str: A relative or absolute path or Unix glob. + base_path: A path to use to infer a directory structure; see examples. + policy: One of `live`, `now`, or `end`. + - live: upload the file as it changes, overwriting the previous version + - now: upload the file once now + - end: upload file when the run ends + + Returns: + Paths to the symlinks created for the matched files. + + For historical reasons, this may return a boolean in legacy code. + + ```python + import wandb + + run = wandb.init() + + run.save("these/are/myfiles/*") + # => Saves files in a "these/are/myfiles/" folder in the run. + + run.save("these/are/myfiles/*", base_path="these") + # => Saves files in an "are/myfiles/" folder in the run. + + run.save("/Users/username/Documents/run123/*.txt") + # => Saves files in a "run123/" folder in the run. See note below. + + run.save("/Users/username/Documents/run123/*.txt", base_path="/Users") + # => Saves files in a "username/Documents/run123/" folder in the run. + + run.save("files/*/saveme.txt") + # => Saves each "saveme.txt" file in an appropriate subdirectory + # of "files/". + + # Explicitly finish the run since a context manager is not used. + run.finish() + ``` + """ + ... + +def sweep( + sweep: Union[dict, Callable], + entity: Optional[str] = None, + project: Optional[str] = None, + prior_runs: Optional[List[str]] = None, +) -> str: + """Initialize a hyperparameter sweep. + + Search for hyperparameters that optimizes a cost function + of a machine learning model by testing various combinations. + + Make note the unique identifier, `sweep_id`, that is returned. + At a later step provide the `sweep_id` to a sweep agent. + + See [Sweep configuration structure](https://docs.wandb.ai/guides/sweeps/define-sweep-configuration) + for information on how to define your sweep. + + Args: + sweep: The configuration of a hyperparameter search. + (or configuration generator). + If you provide a callable, ensure that the callable does + not take arguments and that it returns a dictionary that + conforms to the W&B sweep config spec. + entity: The username or team name where you want to send W&B + runs created by the sweep to. Ensure that the entity you + specify already exists. If you don't specify an entity, + the run will be sent to your default entity, + which is usually your username. + project: The name of the project where W&B runs created from + the sweep are sent to. If the project is not specified, the + run is sent to a project labeled 'Uncategorized'. + prior_runs: The run IDs of existing runs to add to this sweep. + + Returns: + str: A unique identifier for the sweep. + """ + ... + +def controller( + sweep_id_or_config: Optional[Union[str, Dict]] = None, + entity: Optional[str] = None, + project: Optional[str] = None, +) -> _WandbController: + """Public sweep controller constructor. + + Examples: + ```python + import wandb + + tuner = wandb.controller(...) + print(tuner.sweep_config) + print(tuner.sweep_id) + tuner.configure_search(...) + tuner.configure_stopping(...) + ``` + """ + ... + +def agent( + sweep_id: str, + function: Optional[Callable] = None, + entity: Optional[str] = None, + project: Optional[str] = None, + count: Optional[int] = None, +) -> None: + """Start one or more sweep agents. + + The sweep agent uses the `sweep_id` to know which sweep it + is a part of, what function to execute, and (optionally) how + many agents to run. + + Args: + sweep_id: The unique identifier for a sweep. A sweep ID + is generated by W&B CLI or Python SDK. + function: A function to call instead of the "program" + specified in the sweep config. + entity: The username or team name where you want to send W&B + runs created by the sweep to. Ensure that the entity you + specify already exists. If you don't specify an entity, + the run will be sent to your default entity, + which is usually your username. + project: The name of the project where W&B runs created from + the sweep are sent to. If the project is not specified, the + run is sent to a project labeled "Uncategorized". + count: The number of sweep config trials to try. + """ + ... + +def define_metric( + name: str, + step_metric: str | wandb_metric.Metric | None = None, + step_sync: bool | None = None, + hidden: bool | None = None, + summary: str | None = None, + goal: str | None = None, + overwrite: bool | None = None, +) -> wandb_metric.Metric: + """Customize metrics logged with `wandb.Run.log()`. + + Args: + name: The name of the metric to customize. + step_metric: The name of another metric to serve as the X-axis + for this metric in automatically generated charts. + step_sync: Automatically insert the last value of step_metric into + `wandb.Run.log()` if it is not provided explicitly. Defaults to True + if step_metric is specified. + hidden: Hide this metric from automatic plots. + summary: Specify aggregate metrics added to summary. + Supported aggregations include "min", "max", "mean", "last", + "first", "best", "copy" and "none". "none" prevents a summary + from being generated. "best" is used together with the goal + parameter, "best" is deprecated and should not be used, use + "min" or "max" instead. "copy" is deprecated and should not be + used. + goal: Specify how to interpret the "best" summary type. + Supported options are "minimize" and "maximize". "goal" is + deprecated and should not be used, use "min" or "max" instead. + overwrite: If false, then this call is merged with previous + `define_metric` calls for the same metric by using their + values for any unspecified parameters. If true, then + unspecified parameters overwrite values specified by + previous calls. + + Returns: + An object that represents this call but can otherwise be discarded. + """ + ... + +def log_artifact( + artifact_or_path: Artifact | StrPath, + name: str | None = None, + type: str | None = None, + aliases: list[str] | None = None, + tags: list[str] | None = None, +) -> Artifact: + """Declare an artifact as an output of a run. + + Args: + artifact_or_path: (str or Artifact) A path to the contents of this artifact, + can be in the following forms: + - `/local/directory` + - `/local/directory/file.txt` + - `s3://bucket/path` + You can also pass an Artifact object created by calling + `wandb.Artifact`. + name: (str, optional) An artifact name. Valid names can be in the following forms: + - name:version + - name:alias + - digest + This will default to the basename of the path prepended with the current + run id if not specified. + type: (str) The type of artifact to log, examples include `dataset`, `model` + aliases: (list, optional) Aliases to apply to this artifact, + defaults to `["latest"]` + tags: (list, optional) Tags to apply to this artifact, if any. + + Returns: + An `Artifact` object. + """ + ... + +def use_artifact( + artifact_or_name: str | Artifact, + type: str | None = None, + aliases: list[str] | None = None, + use_as: str | None = None, +) -> Artifact: + """Declare an artifact as an input to a run. + + Call `download` or `file` on the returned object to get the contents locally. + + Args: + artifact_or_name: The name of the artifact to use. May be prefixed + with the name of the project the artifact was logged to + ("" or "/"). If no + entity is specified in the name, the Run or API setting's entity is used. + Valid names can be in the following forms + - name:version + - name:alias + type: The type of artifact to use. + aliases: Aliases to apply to this artifact + use_as: This argument is deprecated and does nothing. + + Returns: + An `Artifact` object. + + Examples: + ```python + import wandb + + run = wandb.init(project="") + + # Use an artifact by name and alias + artifact_a = run.use_artifact(artifact_or_name=":") + + # Use an artifact by name and version + artifact_b = run.use_artifact(artifact_or_name=":v") + + # Use an artifact by entity/project/name:alias + artifact_c = run.use_artifact(artifact_or_name="//:") + + # Use an artifact by entity/project/name:version + artifact_d = run.use_artifact( + artifact_or_name="//:v" + ) + + # Explicitly finish the run since a context manager is not used. + run.finish() + ``` + """ + ... + +def log_model( + path: StrPath, + name: str | None = None, + aliases: list[str] | None = None, +) -> None: + """Logs a model artifact containing the contents inside the 'path' to a run and marks it as an output to this run. + + The name of model artifact can only contain alphanumeric characters, + underscores, and hyphens. + + Args: + path: (str) A path to the contents of this model, + can be in the following forms: + - `/local/directory` + - `/local/directory/file.txt` + - `s3://bucket/path` + name: A name to assign to the model artifact that + the file contents will be added to. This will default to the + basename of the path prepended with the current run id if + not specified. + aliases: Aliases to apply to the created model artifact, + defaults to `["latest"]` + + Raises: + ValueError: If name has invalid special characters. + + Returns: + None + """ + ... + +def use_model(name: str) -> FilePathStr: + """Download the files logged in a model artifact 'name'. + + Args: + name: A model artifact name. 'name' must match the name of an existing logged + model artifact. May be prefixed with `entity/project/`. Valid names + can be in the following forms + - model_artifact_name:version + - model_artifact_name:alias + + Returns: + path (str): Path to downloaded model artifact file(s). + + Raises: + AssertionError: If model artifact 'name' is of a type that does + not contain the substring 'model'. + """ + ... + +def link_model( + path: StrPath, + registered_model_name: str, + name: str | None = None, + aliases: list[str] | None = None, +) -> Artifact | None: + """Log a model artifact version and link it to a registered model in the model registry. + + Linked model versions are visible in the UI for the specified registered model. + + This method will: + - Check if 'name' model artifact has been logged. If so, use the artifact version that matches the files + located at 'path' or log a new version. Otherwise log files under 'path' as a new model artifact, 'name' + of type 'model'. + - Check if registered model with name 'registered_model_name' exists in the 'model-registry' project. + If not, create a new registered model with name 'registered_model_name'. + - Link version of model artifact 'name' to registered model, 'registered_model_name'. + - Attach aliases from 'aliases' list to the newly linked model artifact version. + + Args: + path: (str) A path to the contents of this model, can be in the + following forms: + - `/local/directory` + - `/local/directory/file.txt` + - `s3://bucket/path` + registered_model_name: The name of the registered model that the + model is to be linked to. A registered model is a collection of + model versions linked to the model registry, typically + representing a team's specific ML Task. The entity that this + registered model belongs to will be derived from the run. + name: The name of the model artifact that files in 'path' will be + logged to. This will default to the basename of the path + prepended with the current run id if not specified. + aliases: Aliases that will only be applied on this linked artifact + inside the registered model. The alias "latest" will always be + applied to the latest version of an artifact that is linked. + + Raises: + AssertionError: If registered_model_name is a path or + if model artifact 'name' is of a type that does not contain + the substring 'model'. + ValueError: If name has invalid special characters. + + Returns: + The linked artifact if linking was successful, otherwise `None`. + """ + ... + +def plot_table( + vega_spec_name: str, + data_table: wandb.Table, + fields: dict[str, Any], + string_fields: dict[str, Any] | None = None, + split_table: bool = False, +) -> CustomChart: + """Creates a custom charts using a Vega-Lite specification and a `wandb.Table`. + + This function creates a custom chart based on a Vega-Lite specification and + a data table represented by a `wandb.Table` object. The specification needs + to be predefined and stored in the W&B backend. The function returns a custom + chart object that can be logged to W&B using `wandb.Run.log()`. + + Args: + vega_spec_name: The name or identifier of the Vega-Lite spec + that defines the visualization structure. + data_table: A `wandb.Table` object containing the data to be + visualized. + fields: A mapping between the fields in the Vega-Lite spec and the + corresponding columns in the data table to be visualized. + string_fields: A dictionary for providing values for any string constants + required by the custom visualization. + split_table: Whether the table should be split into a separate section + in the W&B UI. If `True`, the table will be displayed in a section named + "Custom Chart Tables". Default is `False`. + + Returns: + CustomChart: A custom chart object that can be logged to W&B. To log the + chart, pass the chart object as argument to `wandb.Run.log()`. + + Raises: + wandb.Error: If `data_table` is not a `wandb.Table` object. + + Example: + ```python + # Create a custom chart using a Vega-Lite spec and the data table. + import wandb + + data = [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]] + table = wandb.Table(data=data, columns=["x", "y"]) + fields = {"x": "x", "y": "y", "title": "MY TITLE"} + + with wandb.init() as run: + # Training code goes here + + # Create a custom title with `string_fields`. + my_custom_chart = wandb.plot_table( + vega_spec_name="wandb/line/v0", + data_table=table, + fields=fields, + string_fields={"title": "Title"}, + ) + + run.log({"custom_chart": my_custom_chart}) + ``` + """ + ... + +def watch( + models: torch.nn.Module | Sequence[torch.nn.Module], + criterion: torch.F | None = None, + log: Literal["gradients", "parameters", "all"] | None = "gradients", + log_freq: int = 1000, + idx: int | None = None, + log_graph: bool = False, +) -> None: + """Hook into given PyTorch model to monitor gradients and the model's computational graph. + + This function can track parameters, gradients, or both during training. + + Args: + models: A single model or a sequence of models to be monitored. + criterion: The loss function being optimized (optional). + log: Specifies whether to log "gradients", "parameters", or "all". + Set to None to disable logging. (default="gradients"). + log_freq: Frequency (in batches) to log gradients and parameters. (default=1000) + idx: Index used when tracking multiple models with `wandb.watch`. (default=None) + log_graph: Whether to log the model's computational graph. (default=False) + + Raises: + ValueError: + If `wandb.init()` has not been called or if any of the models are not instances + of `torch.nn.Module`. + """ + ... + +def unwatch( + models: torch.nn.Module | Sequence[torch.nn.Module] | None = None, +) -> None: + """Remove pytorch model topology, gradient and parameter hooks. + + Args: + models: Optional list of pytorch models that have had watch called on them. + """ + ... + +def restore( + name: str, + run_path: str | None = None, + replace: bool = False, + root: str | None = None, +) -> None | TextIO: + """Download the specified file from cloud storage. + + File is placed into the current directory or run directory. + By default, will only download the file if it doesn't already exist. + + Args: + name: The name of the file. + run_path: Optional path to a run to pull files from, i.e. `username/project_name/run_id` + if wandb.init has not been called, this is required. + replace: Whether to download the file even if it already exists locally + root: The directory to download the file to. Defaults to the current + directory or the run directory if wandb.init was called. + + Returns: + None if it can't find the file, otherwise a file object open for reading. + + Raises: + CommError: If W&B can't connect to the W&B backend. + ValueError: If the file is not found or can't find run_path. + """ + ... diff --git a/.venv/lib/python3.12/site-packages/wandb/__main__.py b/.venv/lib/python3.12/site-packages/wandb/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..e32b1ef386cb475c83b09fb527a9621a7370847a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/wandb/__main__.py @@ -0,0 +1,3 @@ +from wandb.cli import cli + +cli.cli(prog_name="python -m wandb") diff --git a/.venv/lib/python3.12/site-packages/wandb/_analytics.py b/.venv/lib/python3.12/site-packages/wandb/_analytics.py new file mode 100644 index 0000000000000000000000000000000000000000..ff102fffde9f805b00c8c55d663159989d40f455 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/wandb/_analytics.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +from contextvars import ContextVar +from dataclasses import dataclass, field +from functools import wraps +from typing import Callable, Final, TypeVar +from uuid import UUID, uuid4 + +from typing_extensions import ParamSpec + +from wandb._strutils import nameof + +P = ParamSpec("P") +R = TypeVar("R") + +# Header keys for tracking the calling function +X_WANDB_PYTHON_FUNC: Final[str] = "X-Wandb-Python-Func" +X_WANDB_PYTHON_CALL_ID: Final[str] = "X-Wandb-Python-Call-Id" + + +@dataclass(frozen=True) +class TrackedFuncInfo: + func: str + """The fully qualified namespace of the tracked function.""" + + call_id: UUID = field(default_factory=uuid4) + """A unique identifier assigned to each invocation.""" + + def to_headers(self) -> dict[str, str]: + return { + X_WANDB_PYTHON_FUNC: self.func, + X_WANDB_PYTHON_CALL_ID: str(self.call_id), + } + + +_current_func: ContextVar[TrackedFuncInfo] = ContextVar("_current_func") +"""An internal, threadsafe context variable to hold the current function being tracked.""" + + +def tracked(func: Callable[P, R]) -> Callable[P, R]: + """A decorator to inject the calling function name into any GraphQL request headers. + + If a tracked function calls another tracked function, only the outermost function in + the call stack will be tracked. + """ + func_namespace = f"{func.__module__}.{nameof(func)}" + + @wraps(func) + def wrapper(*args: P.args, **kwargs: P.kwargs) -> R: + # Don't override the current tracked function if it's already set + if tracked_func(): + return func(*args, **kwargs) + + token = _current_func.set(TrackedFuncInfo(func=func_namespace)) + try: + return func(*args, **kwargs) + finally: + _current_func.reset(token) + + return wrapper + + +def tracked_func() -> TrackedFuncInfo | None: + """Returns info on the current tracked function, if any, otherwise None.""" + return _current_func.get(None) diff --git a/.venv/lib/python3.12/site-packages/wandb/_iterutils.py b/.venv/lib/python3.12/site-packages/wandb/_iterutils.py new file mode 100644 index 0000000000000000000000000000000000000000..0401cb4b8ba1302bc51ac0f7329f08eae516877d --- /dev/null +++ b/.venv/lib/python3.12/site-packages/wandb/_iterutils.py @@ -0,0 +1,73 @@ +from __future__ import annotations + +from collections.abc import Hashable +from typing import TYPE_CHECKING, Any, Iterable, TypeVar, Union, overload + +if TYPE_CHECKING: + T = TypeVar("T") + HashableT = TypeVar("HashableT", bound=Hashable) + ClassInfo = Union[type[T], tuple[type[T], ...]] + + +@overload +def always_list(obj: Iterable[T], base_type: ClassInfo = ...) -> list[T]: ... +@overload +def always_list(obj: T, base_type: ClassInfo = ...) -> list[T]: ... +def always_list(obj: Any, base_type: Any = (str, bytes)) -> list[T]: + """Return a guaranteed list of objects from a single instance OR iterable of such objects. + + By default, assume the returned list should have string-like elements (i.e. `str`/`bytes`). + + Adapted from `more_itertools.always_iterable`, but simplified for internal use. See: + https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.always_iterable + """ + return [obj] if isinstance(obj, base_type) else list(obj) + + +def unique_list(iterable: Iterable[HashableT]) -> list[HashableT]: + """Return a deduplicated list of items from the given iterable, preserving order.""" + # Trick for O(1) uniqueness check that maintains order + return list(dict.fromkeys(iterable)) + + +def one( + iterable: Iterable[T], + too_short: type[Exception] | Exception | None = None, + too_long: type[Exception] | Exception | None = None, +) -> T: + """Return the only item in the iterable. + + Note: + This is intended **only** as an internal helper/convenience function, + and its implementation is directly adapted from `more_itertools.one`. + Users needing similar functionality are strongly encouraged to use + that library instead: + https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.one + + Args: + iterable: The iterable to get the only item from. + too_short: Custom exception to raise if the iterable has no items. + too_long: Custom exception to raise if the iterable has multiple items. + + Raises: + ValueError or `too_short`: If the iterable has no items. + ValueError or `too_long`: If the iterable has multiple items. + """ + # For a general iterable, avoid inadvertently iterating through all values, + # which may be costly or impossible (e.g. if infinite). Only check that: + + # ... the first item exists + it = iter(iterable) + try: + obj = next(it) + except StopIteration: + raise (too_short or ValueError("Expected 1 item in iterable, got 0")) from None + + # ...the second item doesn't + try: + _ = next(it) + except StopIteration: + return obj + raise ( + too_long or ValueError("Expected 1 item in iterable, got multiple") + ) from None diff --git a/.venv/lib/python3.12/site-packages/wandb/_strutils.py b/.venv/lib/python3.12/site-packages/wandb/_strutils.py new file mode 100644 index 0000000000000000000000000000000000000000..b4e9b97d4b4f6585b0dd79a212d4b52988051c4c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/wandb/_strutils.py @@ -0,0 +1,40 @@ +from __future__ import annotations + +from typing import Any + + +def removeprefix(s: str, prefix: str) -> str: + """Removes a prefix from a string. + + This roughly backports the built-in `str.removeprefix` function from Python 3.9+. + Once Python 3.8 support is dropped, just replace this with `str.removeprefix`. + """ + return s[len(prefix) :] if s.startswith(prefix) else s + + +def removesuffix(s: str, suffix: str) -> str: + """Removes a suffix from a string. + + This roughly backports the built-in `str.removesuffix` function from Python 3.9+. + Once Python 3.8 support is dropped, just replace this with `str.removesuffix`. + """ + return s[: -len(suffix)] if s.endswith(suffix) else s + + +def ensureprefix(s: str, prefix: str) -> str: + """Ensures the string has the given prefix prepended.""" + return s if s.startswith(prefix) else f"{prefix}{s}" + + +def ensuresuffix(s: str, suffix: str) -> str: + """Ensures the string has the given suffix appended.""" + return s if s.endswith(suffix) else f"{s}{suffix}" + + +def nameof(obj: Any, full: bool = True) -> str: + """Internal convenience helper that returns the object's `__name__` or `__qualname__`. + + If `full` is True, attempt to return the object's `__qualname__` attribute, + falling back on the `__name__` attribute. + """ + return getattr(obj, "__qualname__", obj.__name__) if full else obj.__name__ diff --git a/.venv/lib/python3.12/site-packages/wandb/data_types.py b/.venv/lib/python3.12/site-packages/wandb/data_types.py new file mode 100644 index 0000000000000000000000000000000000000000..f28e283d8436d1b366cb59ba66d4171df88bcda7 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/wandb/data_types.py @@ -0,0 +1,66 @@ +"""This module defines data types for logging rich, interactive visualizations to W&B. + +Data types include common media types, like images, audio, and videos, +flexible containers for information, like tables and HTML, and more. + +For more on logging media, see [our guide](https://docs.wandb.com/guides/track/log/media) + +For more on logging structured data for interactive dataset and model analysis, +see [our guide to W&B Tables](https://docs.wandb.com/guides/models/tables/). + +All of these special data types are subclasses of WBValue. All the data types +serialize to JSON, since that is what wandb uses to save the objects locally +and upload them to the W&B server. +""" + +from .sdk.data_types.audio import Audio +from .sdk.data_types.base_types.media import BatchableMedia, Media +from .sdk.data_types.base_types.wb_value import WBValue +from .sdk.data_types.bokeh import Bokeh +from .sdk.data_types.graph import Graph, Node +from .sdk.data_types.helper_types.bounding_boxes_2d import BoundingBoxes2D +from .sdk.data_types.helper_types.classes import Classes +from .sdk.data_types.helper_types.image_mask import ImageMask +from .sdk.data_types.histogram import Histogram +from .sdk.data_types.html import Html +from .sdk.data_types.image import Image +from .sdk.data_types.molecule import Molecule +from .sdk.data_types.object_3d import Object3D, box3d +from .sdk.data_types.plotly import Plotly +from .sdk.data_types.saved_model import _SavedModel +from .sdk.data_types.table import JoinedTable, PartitionedTable, Table +from .sdk.data_types.trace_tree import WBTraceTree +from .sdk.data_types.video import Video + +# Note: we are importing everything from the sdk/data_types to maintain a namespace for now. +# Once we fully type this file and move it all into sdk, then we will need to clean up the +# other internal imports + +__all__ = [ + # Untyped Exports + "Audio", + "Table", + "JoinedTable", + "PartitionedTable", + "Bokeh", + "Node", + "Graph", + # Typed Exports + "Histogram", + "Html", + "Image", + "Molecule", + "box3d", + "Object3D", + "Plotly", + "Video", + "WBTraceTree", + "_SavedModel", + "WBValue", + "Media", + "BatchableMedia", + # Typed Legacy Exports (I'd like to remove these) + "ImageMask", + "BoundingBoxes2D", + "Classes", +] diff --git a/.venv/lib/python3.12/site-packages/wandb/env.py b/.venv/lib/python3.12/site-packages/wandb/env.py new file mode 100644 index 0000000000000000000000000000000000000000..0b69eace7c3fc846fbc443776d66aadd2161268e --- /dev/null +++ b/.venv/lib/python3.12/site-packages/wandb/env.py @@ -0,0 +1,535 @@ +"""All of W&B's environment variables. + +Getters and putters for all of them should go here. That way it'll be easier to +avoid typos with names and be consistent about environment variables' semantics. + +Environment variables are not the authoritative source for these values in many +cases. +""" + +from __future__ import annotations + +import json +import os +import sys +from pathlib import Path +from typing import MutableMapping + +import platformdirs + +CONFIG_PATHS = "WANDB_CONFIG_PATHS" +SWEEP_PARAM_PATH = "WANDB_SWEEP_PARAM_PATH" +SHOW_RUN = "WANDB_SHOW_RUN" +DEBUG = "WANDB_DEBUG" +SILENT = "WANDB_SILENT" +QUIET = "WANDB_QUIET" +INITED = "WANDB_INITED" +DIR = "WANDB_DIR" +# Deprecate DESCRIPTION in a future release +DESCRIPTION = "WANDB_DESCRIPTION" +NAME = "WANDB_NAME" +NOTEBOOK_NAME = "WANDB_NOTEBOOK_NAME" +NOTES = "WANDB_NOTES" +USERNAME = "WANDB_USERNAME" +USER_EMAIL = "WANDB_USER_EMAIL" +PROJECT = "WANDB_PROJECT" +ENTITY = "WANDB_ENTITY" +ORGANIZATION = "WANDB_ORGANIZATION" +BASE_URL = "WANDB_BASE_URL" +APP_URL = "WANDB_APP_URL" +PROGRAM = "WANDB_PROGRAM" +ARGS = "WANDB_ARGS" +MODE = "WANDB_MODE" +START_METHOD = "WANDB_START_METHOD" +RESUME = "WANDB_RESUME" +RUN_ID = "WANDB_RUN_ID" +RUN_STORAGE_ID = "WANDB_RUN_STORAGE_ID" +RUN_GROUP = "WANDB_RUN_GROUP" +RUN_DIR = "WANDB_RUN_DIR" +SWEEP_ID = "WANDB_SWEEP_ID" +HTTP_TIMEOUT = "WANDB_HTTP_TIMEOUT" +FILE_PUSHER_TIMEOUT = "WANDB_FILE_PUSHER_TIMEOUT" +API_KEY = "WANDB_API_KEY" +IDENTITY_TOKEN_FILE = "WANDB_IDENTITY_TOKEN_FILE" +CREDENTIALS_FILE = "WANDB_CREDENTIALS_FILE" +JOB_TYPE = "WANDB_JOB_TYPE" +DISABLE_CODE = "WANDB_DISABLE_CODE" +DISABLE_GIT = "WANDB_DISABLE_GIT" +GIT_ROOT = "WANDB_GIT_ROOT" +SAVE_CODE = "WANDB_SAVE_CODE" +TAGS = "WANDB_TAGS" +IGNORE = "WANDB_IGNORE_GLOBS" +ERROR_REPORTING = "WANDB_ERROR_REPORTING" +CORE_DEBUG = "WANDB_CORE_DEBUG" +DOCKER = "WANDB_DOCKER" +AGENT_REPORT_INTERVAL = "WANDB_AGENT_REPORT_INTERVAL" +AGENT_KILL_DELAY = "WANDB_AGENT_KILL_DELAY" +AGENT_DISABLE_FLAPPING = "WANDB_AGENT_DISABLE_FLAPPING" +AGENT_MAX_INITIAL_FAILURES = "WANDB_AGENT_MAX_INITIAL_FAILURES" +CRASH_NOSYNC_TIME = "WANDB_CRASH_NOSYNC_TIME" +MAGIC = "WANDB_MAGIC" +HOST = "WANDB_HOST" +ANONYMOUS = "WANDB_ANONYMOUS" +JUPYTER = "WANDB_JUPYTER" +CONFIG_DIR = "WANDB_CONFIG_DIR" +DATA_DIR = "WANDB_DATA_DIR" +ARTIFACT_DIR = "WANDB_ARTIFACT_DIR" +ARTIFACT_FETCH_FILE_URL_BATCH_SIZE = "WANDB_ARTIFACT_FETCH_FILE_URL_BATCH_SIZE" +CACHE_DIR = "WANDB_CACHE_DIR" +DISABLE_SSL = "WANDB_INSECURE_DISABLE_SSL" +SERVICE = "WANDB_SERVICE" +SENTRY_DSN = "WANDB_SENTRY_DSN" +INIT_TIMEOUT = "WANDB_INIT_TIMEOUT" +GIT_COMMIT = "WANDB_GIT_COMMIT" +GIT_REMOTE_URL = "WANDB_GIT_REMOTE_URL" +_EXECUTABLE = "WANDB_X_EXECUTABLE" +LAUNCH_QUEUE_NAME = "WANDB_LAUNCH_QUEUE_NAME" +LAUNCH_QUEUE_ENTITY = "WANDB_LAUNCH_QUEUE_ENTITY" +LAUNCH_TRACE_ID = "WANDB_LAUNCH_TRACE_ID" +ENABLE_DCGM_PROFILING = "WANDB_ENABLE_DCGM_PROFILING" + +# For testing, to be removed in future version +USE_V1_ARTIFACTS = "_WANDB_USE_V1_ARTIFACTS" + + +def immutable_keys() -> list[str]: + """These are env keys that shouldn't change within a single process. + + We use this to maintain certain values between multiple calls to wandb.init within a single process. + """ + return [ + DIR, + ENTITY, + PROJECT, + API_KEY, + IGNORE, + DISABLE_CODE, + DISABLE_GIT, + DOCKER, + MODE, + BASE_URL, + ERROR_REPORTING, + CRASH_NOSYNC_TIME, + MAGIC, + USERNAME, + USER_EMAIL, + DIR, + SILENT, + CONFIG_PATHS, + ANONYMOUS, + RUN_GROUP, + JOB_TYPE, + TAGS, + RESUME, + AGENT_REPORT_INTERVAL, + HTTP_TIMEOUT, + HOST, + DATA_DIR, + ARTIFACT_DIR, + ARTIFACT_FETCH_FILE_URL_BATCH_SIZE, + CACHE_DIR, + USE_V1_ARTIFACTS, + DISABLE_SSL, + IDENTITY_TOKEN_FILE, + CREDENTIALS_FILE, + ] + + +def _env_as_bool( + var: str, default: str | None = None, env: MutableMapping | None = None +) -> bool: + if env is None: + env = os.environ + val = env.get(var, default) + if not isinstance(val, str): + return False + try: + return strtobool(val) + except ValueError: + return False + + +def is_debug(default: str | None = None, env: MutableMapping | None = None) -> bool: + return _env_as_bool(DEBUG, default=default, env=env) + + +def is_offline(env: MutableMapping | None = None) -> bool: + if env is None: + env = os.environ + return env.get(MODE) == "offline" + + +def is_quiet() -> bool: + return _env_as_bool(QUIET, default="false") + + +def is_silent() -> bool: + return _env_as_bool(SILENT, default="false") + + +def error_reporting_enabled() -> bool: + return _env_as_bool(ERROR_REPORTING, default="True") + + +def core_debug(default: str | None = None) -> bool: + return _env_as_bool(CORE_DEBUG, default=default) or is_debug() + + +def ssl_disabled() -> bool: + return _env_as_bool(DISABLE_SSL, default="False") + + +def dcgm_profiling_enabled() -> bool: + """Checks whether collecting profiling metrics for Nvidia GPUs using DCGM is requested. + + Note: Enabling this feature can lead to increased resource usage + compared to standard monitoring. + Requires the `nvidia-dcgm` service to be running on the machine. + """ + return _env_as_bool(ENABLE_DCGM_PROFILING, default="False") + + +def get_error_reporting( + default: bool | str = True, + env: MutableMapping | None = None, +) -> bool | str: + if env is None: + env = os.environ + + return env.get(ERROR_REPORTING, default) + + +def get_run( + default: str | None = None, env: MutableMapping | None = None +) -> str | None: + if env is None: + env = os.environ + + return env.get(RUN_ID, default) + + +def get_args( + default: list[str] | None = None, env: MutableMapping | None = None +) -> list[str] | None: + if env is None: + env = os.environ + if env.get(ARGS): + try: + return json.loads(env.get(ARGS, "[]")) # type: ignore + except ValueError: + return None + else: + return default or sys.argv[1:] + + +def get_docker( + default: str | None = None, env: MutableMapping | None = None +) -> str | None: + if env is None: + env = os.environ + + return env.get(DOCKER, default) + + +def get_http_timeout(default: int = 20, env: MutableMapping | None = None) -> int: + if env is None: + env = os.environ + + return int(env.get(HTTP_TIMEOUT, default)) + + +def get_file_pusher_timeout( + default: int | None = None, + env: MutableMapping | None = None, +) -> int | None: + if env is None: + env = os.environ + + timeout = env.get(FILE_PUSHER_TIMEOUT, default) + return int(timeout) if timeout else None + + +def get_ignore( + default: list[str] | None = None, env: MutableMapping | None = None +) -> list[str] | None: + if env is None: + env = os.environ + ignore = env.get(IGNORE) + if ignore is not None: + return ignore.split(",") + else: + return default + + +def get_project( + default: str | None = None, env: MutableMapping | None = None +) -> str | None: + if env is None: + env = os.environ + + return env.get(PROJECT, default) + + +def get_username( + default: str | None = None, env: MutableMapping | None = None +) -> str | None: + if env is None: + env = os.environ + + return env.get(USERNAME, default) + + +def get_user_email( + default: str | None = None, env: MutableMapping | None = None +) -> str | None: + if env is None: + env = os.environ + + return env.get(USER_EMAIL, default) + + +def get_entity( + default: str | None = None, env: MutableMapping | None = None +) -> str | None: + if env is None: + env = os.environ + + return env.get(ENTITY, default) + + +def get_organization( + default: str | None = None, env: MutableMapping | None = None +) -> str | None: + if env is None: + env = os.environ + + return env.get(ORGANIZATION, default) + + +def get_base_url( + default: str | None = None, env: MutableMapping | None = None +) -> str | None: + if env is None: + env = os.environ + + return env.get(BASE_URL, default) + + +def get_app_url( + default: str | None = None, env: MutableMapping | None = None +) -> str | None: + if env is None: + env = os.environ + + return env.get(APP_URL, default) + + +def get_show_run(default: str | None = None, env: MutableMapping | None = None) -> bool: + if env is None: + env = os.environ + + return bool(env.get(SHOW_RUN, default)) + + +def get_description( + default: str | None = None, env: MutableMapping | None = None +) -> str | None: + if env is None: + env = os.environ + + return env.get(DESCRIPTION, default) + + +def get_tags(default: str = "", env: MutableMapping | None = None) -> list[str]: + if env is None: + env = os.environ + + return [tag for tag in env.get(TAGS, default).split(",") if tag] + + +def get_dir( + default: str | None = None, env: MutableMapping | None = None +) -> str | None: + if env is None: + env = os.environ + return env.get(DIR, default) + + +def get_config_paths( + default: str | None = None, env: MutableMapping | None = None +) -> str | None: + if env is None: + env = os.environ + return env.get(CONFIG_PATHS, default) + + +def get_agent_report_interval( + default: str | None = None, env: MutableMapping | None = None +) -> int | None: + if env is None: + env = os.environ + val = env.get(AGENT_REPORT_INTERVAL, default) + try: + val = int(val) # type: ignore + except ValueError: + val = None # silently ignore env format errors, caller should handle. + return val + + +def get_agent_kill_delay( + default: str | None = None, env: MutableMapping | None = None +) -> int | None: + if env is None: + env = os.environ + val = env.get(AGENT_KILL_DELAY, default) + try: + val = int(val) # type: ignore + except ValueError: + val = None # silently ignore env format errors, caller should handle. + return val + + +def get_crash_nosync_time( + default: str | None = None, env: MutableMapping | None = None +) -> int | None: + if env is None: + env = os.environ + val = env.get(CRASH_NOSYNC_TIME, default) + try: + val = int(val) # type: ignore + except ValueError: + val = None # silently ignore env format errors, caller should handle. + return val + + +def get_magic( + default: str | None = None, env: MutableMapping | None = None +) -> str | None: + if env is None: + env = os.environ + val = env.get(MAGIC, default) + return val + + +def get_data_dir(env: MutableMapping | None = None) -> str: + default_dir = platformdirs.user_data_dir("wandb") + if env is None: + env = os.environ + val = env.get(DATA_DIR, default_dir) + return val + + +def get_artifact_dir(env: MutableMapping | None = None) -> str: + default_dir = os.path.join(".", "artifacts") + if env is None: + env = os.environ + val = env.get(ARTIFACT_DIR, default_dir) + return os.path.abspath(str(val)) + + +def get_artifact_fetch_file_url_batch_size(env: MutableMapping | None = None) -> int: + default_batch_size = 5000 + if env is None: + env = os.environ + val = int(env.get(ARTIFACT_FETCH_FILE_URL_BATCH_SIZE, default_batch_size)) + return val + + +def get_cache_dir(env: MutableMapping | None = None) -> Path: + env = env or os.environ + return Path(env.get(CACHE_DIR, platformdirs.user_cache_dir("wandb"))) + + +def get_use_v1_artifacts(env: MutableMapping | None = None) -> bool: + if env is None: + env = os.environ + val = bool(env.get(USE_V1_ARTIFACTS, False)) + return val + + +def get_agent_max_initial_failures( + default: int | None = None, env: MutableMapping | None = None +) -> int | None: + if env is None: + env = os.environ + val = env.get(AGENT_MAX_INITIAL_FAILURES, default) + try: + val = int(val) # type: ignore + except ValueError: + val = default + return val + + +def set_entity(value: str, env: MutableMapping | None = None) -> None: + if env is None: + env = os.environ + env[ENTITY] = value + + +def set_project(value: str, env: MutableMapping | None = None) -> None: + if env is None: + env = os.environ + env[PROJECT] = value or "uncategorized" + + +def should_save_code() -> bool: + save_code = _env_as_bool(SAVE_CODE, default="False") + code_disabled = _env_as_bool(DISABLE_CODE, default="False") + return save_code and not code_disabled + + +def disable_git(env: MutableMapping | None = None) -> bool: + if env is None: + env = os.environ + val = env.get(DISABLE_GIT, default="False") + if isinstance(val, str): + val = False if val.lower() == "false" else True + return val + + +def get_launch_queue_name(env: MutableMapping | None = None) -> str | None: + if env is None: + env = os.environ + val = env.get(LAUNCH_QUEUE_NAME, None) + return val + + +def get_launch_queue_entity(env: MutableMapping | None = None) -> str | None: + if env is None: + env = os.environ + val = env.get(LAUNCH_QUEUE_ENTITY, None) + return val + + +def get_launch_trace_id(env: MutableMapping | None = None) -> str | None: + if env is None: + env = os.environ + val = env.get(LAUNCH_TRACE_ID, None) + return val + + +def get_credentials_file(default: str, env: MutableMapping | None = None) -> Path: + """Retrieve the path for the credentials file used to save access tokens. + + The credentials file path can be set via an environment variable, otherwise + the default path is used. + """ + if env is None: + env = os.environ + credentials_file = env.get(CREDENTIALS_FILE, default) + return Path(credentials_file) + + +def strtobool(val: str) -> bool: + """Convert a string representation of truth to true or false. + + Copied from distutils. distutils was removed in Python 3.12. + """ + 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}") diff --git a/.venv/lib/python3.12/site-packages/wandb/jupyter.py b/.venv/lib/python3.12/site-packages/wandb/jupyter.py new file mode 100644 index 0000000000000000000000000000000000000000..ee69ebc04d620ca5f3803cfe05f66c657839f435 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/wandb/jupyter.py @@ -0,0 +1,538 @@ +from __future__ import annotations + +import json +import logging +import os +import re +import shutil +import sys +import traceback +from base64 import b64encode +from typing import Any + +import IPython +import IPython.display +import requests +from IPython.core.magic import Magics, line_cell_magic, magics_class +from IPython.core.magic_arguments import argument, magic_arguments, parse_argstring +from requests.compat import urljoin + +import wandb +import wandb.util +from wandb.sdk import wandb_setup +from wandb.sdk.lib import filesystem + +logger = logging.getLogger(__name__) + + +def display_if_magic_is_used(run: wandb.Run) -> bool: + """Display a run's page if the cell has the %%wandb cell magic. + + Args: + run: The run to display. + + Returns: + Whether the %%wandb cell magic was present. + """ + if not _current_cell_wandb_magic: + return False + + _current_cell_wandb_magic.display_if_allowed(run) + return True + + +class _WandbCellMagicState: + """State for a cell with the %%wandb cell magic.""" + + def __init__(self, *, height: int) -> None: + """Initializes the %%wandb cell magic state. + + Args: + height: The desired height for displayed iframes. + """ + self._height = height + self._already_displayed = False + + def display_if_allowed(self, run: wandb.Run) -> None: + """Display a run's iframe if one is not already displayed. + + Args: + run: The run to display. + """ + if self._already_displayed: + return + self._already_displayed = True + + _display_wandb_run(run, height=self._height) + + +_current_cell_wandb_magic: _WandbCellMagicState | None = None + + +def _display_by_wandb_path(path: str, *, height: int) -> None: + """Display a wandb object (usually in an iframe) given its URI. + + Args: + path: A path to a run, sweep, project, report, etc. + height: Height of the iframe in pixels. + """ + api = wandb.Api() + + try: + obj = api.from_path(path) + + IPython.display.display_html( + obj.to_html(height=height), + raw=True, + ) + except wandb.Error: + traceback.print_exc() + IPython.display.display_html( + f"Path {path!r} does not refer to a W&B object you can access.", + raw=True, + ) + + +def _display_wandb_run(run: wandb.Run, *, height: int) -> None: + """Display a run (usually in an iframe). + + Args: + run: The run to display. + height: Height of the iframe in pixels. + """ + IPython.display.display_html( + run.to_html(height=height), + raw=True, + ) + + +@magics_class +class WandBMagics(Magics): + def __init__(self, shell): + super().__init__(shell) + + @magic_arguments() + @argument( + "path", + default=None, + nargs="?", + help="The path to a resource you want to display.", + ) + @argument( + "-h", + "--height", + default=420, + type=int, + help="The height of the iframe in pixels.", + ) + @line_cell_magic + def wandb(self, line: str, cell: str | None = None) -> None: + """Display wandb resources in Jupyter. + + This can be used as a line magic: + + %wandb USERNAME/PROJECT/runs/RUN_ID + + Or as a cell magic: + + %%wandb -h 1024 + with wandb.init() as run: + run.log({"loss": 1}) + """ + global _current_cell_wandb_magic + + args = parse_argstring(self.wandb, line) + path: str | None = args.path + height: int = args.height + + if path: + _display_by_wandb_path(path, height=height) + displayed = True + elif run := wandb_setup.singleton().most_recent_active_run: + _display_wandb_run(run, height=height) + displayed = True + else: + displayed = False + + # If this is being used as a line magic ("%wandb"), we are done. + # When used as a cell magic ("%%wandb"), we must run the cell. + if cell is None: + return + + if not displayed: + _current_cell_wandb_magic = _WandbCellMagicState(height=height) + + try: + IPython.get_ipython().run_cell(cell) + finally: + _current_cell_wandb_magic = None + + +def notebook_metadata_from_jupyter_servers_and_kernel_id(): + servers, kernel_id = jupyter_servers_and_kernel_id() + for s in servers: + if s.get("password"): + raise ValueError("Can't query password protected kernel") + res = requests.get( + urljoin(s["url"], "api/sessions"), params={"token": s.get("token", "")} + ).json() + for nn in res: + if isinstance(nn, dict) and nn.get("kernel") and "notebook" in nn: + if nn["kernel"]["id"] == kernel_id: + return { + "root": s.get("root_dir", s.get("notebook_dir", os.getcwd())), + "path": nn["notebook"]["path"], + "name": nn["notebook"]["name"], + } + + if not kernel_id: + return None + + # Built-in notebook server in VS Code + try: + from IPython import get_ipython + + ipython = get_ipython() + notebook_path = ipython.kernel.shell.user_ns.get("__vsc_ipynb_file__") + if notebook_path: + return { + "root": os.path.dirname(notebook_path), + "path": notebook_path, + "name": os.path.basename(notebook_path), + } + except Exception: + return None + + +def notebook_metadata(silent: bool) -> dict[str, str]: + """Attempt to query jupyter for the path and name of the notebook file. + + This can handle different jupyter environments, specifically: + + 1. Colab + 2. Kaggle + 3. JupyterLab + 4. Notebooks + 5. Other? + """ + error_message = ( + "Failed to detect the name of this notebook. You can set it manually" + " with the WANDB_NOTEBOOK_NAME environment variable to enable code" + " saving." + ) + try: + jupyter_metadata = notebook_metadata_from_jupyter_servers_and_kernel_id() + + # Colab: + # request the most recent contents + ipynb = attempt_colab_load_ipynb() + if ipynb is not None and jupyter_metadata is not None: + return { + "root": "/content", + "path": jupyter_metadata["path"], + "name": jupyter_metadata["name"], + } + + # Kaggle: + if wandb.util._is_kaggle(): + # request the most recent contents + ipynb = attempt_kaggle_load_ipynb() + if ipynb: + return { + "root": "/kaggle/working", + "path": ipynb["metadata"]["name"], + "name": ipynb["metadata"]["name"], + } + + if jupyter_metadata: + return jupyter_metadata + except Exception: + logger.exception(error_message) + + wandb.termerror(error_message) + return {} + + +def jupyter_servers_and_kernel_id(): + """Return a list of servers and the current kernel_id. + + Used to query for the name of the notebook. + """ + try: + import ipykernel # type: ignore + + kernel_id = re.search( + "kernel-(.*).json", ipykernel.connect.get_connection_file() + ).group(1) + # We're either in jupyterlab or a notebook, lets prefer the newer jupyter_server package + serverapp = wandb.util.get_module("jupyter_server.serverapp") + notebookapp = wandb.util.get_module("notebook.notebookapp") + servers = [] + if serverapp is not None: + servers.extend(list(serverapp.list_running_servers())) + if notebookapp is not None: + servers.extend(list(notebookapp.list_running_servers())) + except (AttributeError, ValueError, ImportError): + return [], None + + return servers, kernel_id + + +def attempt_colab_load_ipynb(): + colab = wandb.util.get_module("google.colab") + if colab: + # This isn't thread safe, never call in a thread + response = colab._message.blocking_request("get_ipynb", timeout_sec=5) + if response: + return response["ipynb"] + + +def attempt_kaggle_load_ipynb(): + kaggle = wandb.util.get_module("kaggle_session") + if not kaggle: + return None + + try: + client = kaggle.UserSessionClient() + parsed = json.loads(client.get_exportable_ipynb()["source"]) + # TODO: couldn't find a way to get the name of the notebook... + parsed["metadata"]["name"] = "kaggle.ipynb" + except Exception: + wandb.termerror("Unable to load kaggle notebook.") + logger.exception("Unable to load kaggle notebook.") + return None + + return parsed + + +def attempt_colab_login( + app_url: str, + referrer: str | None = None, +): + """This renders an iframe to wandb in the hopes it posts back an api key.""" + from google.colab import output # type: ignore + from google.colab._message import MessageError # type: ignore + from IPython import display + + display.display( + display.Javascript( + """ + window._wandbApiKey = new Promise((resolve, reject) => {{ + function loadScript(url) {{ + return new Promise(function(resolve, reject) {{ + let newScript = document.createElement("script"); + newScript.onerror = reject; + newScript.onload = resolve; + document.body.appendChild(newScript); + newScript.src = url; + }}); + }} + loadScript("https://cdn.jsdelivr.net/npm/postmate/build/postmate.min.js").then(() => {{ + const iframe = document.createElement('iframe') + iframe.style.cssText = "width:0;height:0;border:none" + document.body.appendChild(iframe) + const handshake = new Postmate({{ + container: iframe, + url: '{}/authorize{}' + }}); + const timeout = setTimeout(() => reject("Couldn't auto authenticate"), 5000) + handshake.then(function(child) {{ + child.on('authorize', data => {{ + clearTimeout(timeout) + resolve(data) + }}); + }}); + }}) + }}); + """.format( + app_url.replace("http:", "https:"), + f"?ref={referrer}" if referrer else "", + ) + ) + ) + try: + return output.eval_js("_wandbApiKey") + except MessageError: + return None + + +class Notebook: + def __init__(self, settings: wandb.Settings) -> None: + self.outputs: dict[int, Any] = {} + self.settings = settings + self.shell = IPython.get_ipython() + + def save_display(self, exc_count, data_with_metadata): + self.outputs[exc_count] = self.outputs.get(exc_count, []) + + # byte values such as images need to be encoded in base64 + # otherwise nbformat.v4.new_output will throw a NotebookValidationError + data = data_with_metadata["data"] + b64_data = {} + for key in data: + val = data[key] + if isinstance(val, bytes): + b64_data[key] = b64encode(val).decode("utf-8") + else: + b64_data[key] = val + + self.outputs[exc_count].append( + {"data": b64_data, "metadata": data_with_metadata["metadata"]} + ) + + def probe_ipynb(self): + """Return notebook as dict or None.""" + relpath = self.settings.x_jupyter_path + if relpath: + if os.path.exists(relpath): + with open(relpath) as json_file: + data = json.load(json_file) + return data + + colab_ipynb = attempt_colab_load_ipynb() + if colab_ipynb: + return colab_ipynb + + kaggle_ipynb = attempt_kaggle_load_ipynb() + if kaggle_ipynb and len(kaggle_ipynb["cells"]) > 0: + return kaggle_ipynb + + return + + def save_ipynb(self) -> bool: + if not self.settings.save_code: + logger.info("not saving jupyter notebook") + return False + ret = False + try: + ret = self._save_ipynb() + except Exception: + wandb.termerror("Failed to save notebook.") + logger.exception("Problem saving notebook.") + return ret + + def _save_ipynb(self) -> bool: + relpath = self.settings.x_jupyter_path + logger.info("looking for notebook: %s", relpath) + if relpath: + if os.path.exists(relpath): + shutil.copy( + relpath, + os.path.join( + self.settings._tmp_code_dir, os.path.basename(relpath) + ), + ) + return True + + # TODO: likely only save if the code has changed + colab_ipynb = attempt_colab_load_ipynb() + if colab_ipynb: + try: + jupyter_metadata = ( + notebook_metadata_from_jupyter_servers_and_kernel_id() + ) + nb_name = jupyter_metadata["name"] + except Exception: + nb_name = "colab.ipynb" + if not nb_name.endswith(".ipynb"): + nb_name += ".ipynb" + with open( + os.path.join( + self.settings._tmp_code_dir, + nb_name, + ), + "w", + encoding="utf-8", + ) as f: + f.write(json.dumps(colab_ipynb)) + return True + + kaggle_ipynb = attempt_kaggle_load_ipynb() + if kaggle_ipynb and len(kaggle_ipynb["cells"]) > 0: + with open( + os.path.join( + self.settings._tmp_code_dir, kaggle_ipynb["metadata"]["name"] + ), + "w", + encoding="utf-8", + ) as f: + f.write(json.dumps(kaggle_ipynb)) + return True + + return False + + def save_history(self, run: wandb.Run): + """This saves all cell executions in the current session as a new notebook.""" + try: + from nbformat import v4, validator, write # type: ignore + except ImportError: + wandb.termerror( + "The nbformat package was not found." + " It is required to save notebook history." + ) + return + # TODO: some tests didn't patch ipython properly? + if self.shell is None: + return + cells = [] + hist = list(self.shell.history_manager.get_range(output=True)) + if len(hist) <= 1 or not self.settings.save_code: + logger.info("not saving jupyter history") + return + try: + for _, execution_count, exc in hist: + if exc[1]: + # TODO: capture stderr? + outputs = [ + v4.new_output(output_type="stream", name="stdout", text=exc[1]) + ] + else: + outputs = [] + if self.outputs.get(execution_count): + for out in self.outputs[execution_count]: + outputs.append( + v4.new_output( + output_type="display_data", + data=out["data"], + metadata=out["metadata"] or {}, + ) + ) + cells.append( + v4.new_code_cell( + execution_count=execution_count, source=exc[0], outputs=outputs + ) + ) + if hasattr(self.shell, "kernel"): + language_info = self.shell.kernel.language_info + else: + language_info = {"name": "python", "version": sys.version} + logger.info("saving %i cells to _session_history.ipynb", len(cells)) + nb = v4.new_notebook( + cells=cells, + metadata={ + "kernelspec": { + "display_name": f"Python {sys.version_info[0]}", + "name": f"python{sys.version_info[0]}", + "language": "python", + }, + "language_info": language_info, + }, + ) + state_path = os.path.join("code", "_session_history.ipynb") + run._set_config_wandb("session_history", state_path) + filesystem.mkdir_exists_ok(os.path.join(self.settings.files_dir, "code")) + with open( + os.path.join(self.settings._tmp_code_dir, "_session_history.ipynb"), + "w", + encoding="utf-8", + ) as f: + write(nb, f, version=4) + with open( + os.path.join(self.settings.files_dir, state_path), + "w", + encoding="utf-8", + ) as f: + write(nb, f, version=4) + except (OSError, validator.NotebookValidationError): + wandb.termerror("Unable to save notebook session history.") + logger.exception("Unable to save notebook session history.") diff --git a/.venv/lib/python3.12/site-packages/wandb/py.typed b/.venv/lib/python3.12/site-packages/wandb/py.typed new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/wandb/sklearn.py b/.venv/lib/python3.12/site-packages/wandb/sklearn.py new file mode 100644 index 0000000000000000000000000000000000000000..5c72b8228b5cb342ba2a217104ee5ce9267a2d2f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/wandb/sklearn.py @@ -0,0 +1,35 @@ +from wandb.integration.sklearn import ( + plot_calibration_curve, + plot_class_proportions, + plot_classifier, + plot_clusterer, + plot_confusion_matrix, + plot_elbow_curve, + plot_feature_importances, + plot_learning_curve, + plot_outlier_candidates, + plot_precision_recall, + plot_regressor, + plot_residuals, + plot_roc, + plot_silhouette, + plot_summary_metrics, +) + +__all__ = ( + "plot_classifier", + "plot_clusterer", + "plot_regressor", + "plot_summary_metrics", + "plot_learning_curve", + "plot_feature_importances", + "plot_class_proportions", + "plot_calibration_curve", + "plot_roc", + "plot_precision_recall", + "plot_confusion_matrix", + "plot_elbow_curve", + "plot_silhouette", + "plot_residuals", + "plot_outlier_candidates", +) diff --git a/.venv/lib/python3.12/site-packages/wandb/trigger.py b/.venv/lib/python3.12/site-packages/wandb/trigger.py new file mode 100644 index 0000000000000000000000000000000000000000..15b52eec9e121a430ae64f1a5b8265f2cb2b8786 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/wandb/trigger.py @@ -0,0 +1,29 @@ +"""Module to facilitate adding hooks to wandb actions. + +Usage: + import trigger + trigger.register('on_something', func) + trigger.call('on_something', *args, **kwargs) + trigger.unregister('on_something', func) +""" + +from typing import Any, Callable + +_triggers = {} + + +def reset(): + _triggers.clear() + + +def register(event: str, func: Callable): + _triggers.setdefault(event, []).append(func) + + +def call(event_str: str, *args: Any, **kwargs: Any): + for func in _triggers.get(event_str, []): + func(*args, **kwargs) + + +def unregister(event: str, func: Callable): + _triggers[event].remove(func) diff --git a/.venv/lib/python3.12/site-packages/wandb/util.py b/.venv/lib/python3.12/site-packages/wandb/util.py new file mode 100644 index 0000000000000000000000000000000000000000..264e526ed0d4f02cbf866c196192dc4b2ca25581 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/wandb/util.py @@ -0,0 +1,2040 @@ +import colorsys +import contextlib +import dataclasses +import enum +import functools +import gzip +import importlib +import importlib.util +import itertools +import json +import logging +import math +import numbers +import os +import pathlib +import platform +import queue +import random +import re +import secrets +import shlex +import socket +import string +import sys +import tarfile +import tempfile +import threading +import time +import types +import urllib +from dataclasses import asdict, is_dataclass +from datetime import date, datetime, timedelta +from importlib import import_module +from sys import getsizeof +from types import ModuleType +from typing import ( + IO, + TYPE_CHECKING, + Callable, + Dict, + Iterable, + List, + Mapping, + Optional, + Sequence, + TextIO, + Tuple, + Union, +) + +import requests +import yaml +from typing_extensions import Any, Generator, TypeGuard, TypeVar + +import wandb +import wandb.env +from wandb.errors import ( + AuthenticationError, + CommError, + UsageError, + WandbCoreNotAvailableError, + term, +) +from wandb.sdk.internal.thread_local_settings import _thread_local_api_settings +from wandb.sdk.lib import filesystem, runid +from wandb.sdk.lib.json_util import dump, dumps +from wandb.sdk.lib.paths import FilePathStr, StrPath + +if TYPE_CHECKING: + import wandb.sdk.internal.settings_static + import wandb.sdk.wandb_settings + from wandb.sdk.artifacts.artifact import Artifact + +CheckRetryFnType = Callable[[Exception], Union[bool, timedelta]] +T = TypeVar("T") + + +logger = logging.getLogger(__name__) +_not_importable = set() + +LAUNCH_JOB_ARTIFACT_SLOT_NAME = "_wandb_job" + +MAX_LINE_BYTES = (10 << 20) - (100 << 10) # imposed by back end +IS_GIT = os.path.exists(os.path.join(os.path.dirname(__file__), "..", ".git")) + +# From https://docs.docker.com/engine/reference/commandline/tag/ +# "Name components may contain lowercase letters, digits and separators. +# A separator is defined as a period, one or two underscores, or one or more dashes. +# A name component may not start or end with a separator." +DOCKER_IMAGE_NAME_SEPARATOR = "(?:__|[._]|[-]+)" +RE_DOCKER_IMAGE_NAME_SEPARATOR_START = re.compile("^" + DOCKER_IMAGE_NAME_SEPARATOR) +RE_DOCKER_IMAGE_NAME_SEPARATOR_END = re.compile(DOCKER_IMAGE_NAME_SEPARATOR + "$") +RE_DOCKER_IMAGE_NAME_SEPARATOR_REPEAT = re.compile(DOCKER_IMAGE_NAME_SEPARATOR + "{2,}") +RE_DOCKER_IMAGE_NAME_CHARS = re.compile(r"[^a-z0-9._\-]") + +# these match the environments for gorilla +if IS_GIT: + SENTRY_ENV = "development" +else: + SENTRY_ENV = "production" + + +POW_10_BYTES = [ + ("B", 10**0), + ("KB", 10**3), + ("MB", 10**6), + ("GB", 10**9), + ("TB", 10**12), + ("PB", 10**15), + ("EB", 10**18), +] + +POW_2_BYTES = [ + ("B", 2**0), + ("KiB", 2**10), + ("MiB", 2**20), + ("GiB", 2**30), + ("TiB", 2**40), + ("PiB", 2**50), + ("EiB", 2**60), +] + + +def vendor_setup() -> Callable: + """Create a function that restores user paths after vendor imports. + + This enables us to use the vendor directory for packages we don't depend on. Call + the returned function after imports are complete. If you don't you may modify the + user's path which is never good. + + Usage: + + ```python + reset_path = vendor_setup() + # do any vendor imports... + reset_path() + ``` + """ + original_path = [directory for directory in sys.path] + + def reset_import_path() -> None: + sys.path = original_path + + parent_dir = os.path.abspath(os.path.dirname(__file__)) + vendor_dir = os.path.join(parent_dir, "vendor") + vendor_packages = ( + "gql-0.2.0", + "graphql-core-1.1", + "watchdog_0_9_0", + "promise-2.3.0", + ) + package_dirs = [os.path.join(vendor_dir, p) for p in vendor_packages] + for p in [vendor_dir] + package_dirs: + if p not in sys.path: + sys.path.insert(1, p) + + return reset_import_path + + +def vendor_import(name: str) -> Any: + reset_path = vendor_setup() + module = import_module(name) + reset_path() + return module + + +class LazyModuleState: + def __init__(self, module: types.ModuleType) -> None: + self.module = module + self.load_started = False + self.lock = threading.RLock() + + def load(self) -> None: + with self.lock: + if self.load_started: + return + self.load_started = True + assert self.module.__spec__ is not None + assert self.module.__spec__.loader is not None + self.module.__spec__.loader.exec_module(self.module) + self.module.__class__ = types.ModuleType + + # Set the submodule as an attribute on the parent module + # This enables access to the submodule via normal attribute access. + parent, _, child = self.module.__name__.rpartition(".") + if parent: + parent_module = sys.modules[parent] + setattr(parent_module, child, self.module) + + +class LazyModule(types.ModuleType): + def __getattribute__(self, name: str) -> Any: + state = object.__getattribute__(self, "__lazy_module_state__") + state.load() + return object.__getattribute__(self, name) + + def __setattr__(self, name: str, value: Any) -> None: + state = object.__getattribute__(self, "__lazy_module_state__") + state.load() + object.__setattr__(self, name, value) + + def __delattr__(self, name: str) -> None: + state = object.__getattribute__(self, "__lazy_module_state__") + state.load() + object.__delattr__(self, name) + + +def import_module_lazy(name: str) -> types.ModuleType: + """Import a module lazily, only when it is used. + + Inspired by importlib.util.LazyLoader, but improved so that the module loading is + thread-safe. Circular dependency between modules can lead to a deadlock if the two + modules are loaded from different threads. + + :param (str) name: Dot-separated module path. E.g., 'scipy.stats'. + """ + try: + return sys.modules[name] + except KeyError: + spec = importlib.util.find_spec(name) + if spec is None: + raise ModuleNotFoundError + module = importlib.util.module_from_spec(spec) + module.__lazy_module_state__ = LazyModuleState(module) # type: ignore + module.__class__ = LazyModule + sys.modules[name] = module + return module + + +def get_module( + name: str, + required: Optional[str] = None, + lazy: bool = True, +) -> Any: + """Return module or None. Absolute import is required. + + :param (str) name: Dot-separated module path. E.g., 'scipy.stats'. + :param (str) required: A string to raise a ValueError if missing + :param (bool) lazy: If True, return a lazy loader for the module. + :return: (module|None) If import succeeds, the module will be returned. + """ + if name not in _not_importable: + try: + if not lazy: + return import_module(name) + else: + return import_module_lazy(name) + except Exception: + _not_importable.add(name) + msg = f"Error importing optional module {name}" + if required: + logger.exception(msg) + if required and name in _not_importable: + raise wandb.Error(required) + + +def get_optional_module(name) -> Optional["importlib.ModuleInterface"]: # type: ignore + return get_module(name) + + +np = get_module("numpy") + +pd_available = False +pandas_spec = importlib.util.find_spec("pandas") +if pandas_spec is not None: + pd_available = True + +# TODO: Revisit these limits +VALUE_BYTES_LIMIT = 100000 + + +def app_url(api_url: str) -> str: + """Return the frontend app url without a trailing slash.""" + # TODO: move me to settings + app_url = wandb.env.get_app_url() + if app_url is not None: + return str(app_url.strip("/")) + if "://api.wandb.test" in api_url: + # dev mode + return api_url.replace("://api.", "://app.").strip("/") + elif "://api.wandb." in api_url: + # cloud + return api_url.replace("://api.", "://").strip("/") + elif "://api." in api_url: + # onprem cloud + return api_url.replace("://api.", "://app.").strip("/") + # wandb/local + return api_url + + +def get_full_typename(o: Any) -> Any: + """Determine types based on type names. + + Avoids needing to to import (and therefore depend on) PyTorch, TensorFlow, etc. + """ + instance_name = o.__class__.__module__ + "." + o.__class__.__name__ + if instance_name in ["builtins.module", "__builtin__.module"]: + return o.__name__ + else: + return instance_name + + +def get_h5_typename(o: Any) -> Any: + typename = get_full_typename(o) + if is_tf_tensor_typename(typename): + return "tensorflow.Tensor" + elif is_pytorch_tensor_typename(typename): + return "torch.Tensor" + else: + return o.__class__.__module__.split(".")[0] + "." + o.__class__.__name__ + + +def is_uri(string: str) -> bool: + parsed_uri = urllib.parse.urlparse(string) + return len(parsed_uri.scheme) > 0 + + +def local_file_uri_to_path(uri: str) -> str: + """Convert URI to local filesystem path. + + No-op if the uri does not have the expected scheme. + """ + path = urllib.parse.urlparse(uri).path if uri.startswith("file:") else uri + return urllib.request.url2pathname(path) + + +def get_local_path_or_none(path_or_uri: str) -> Optional[str]: + """Return path if local, None otherwise. + + Return None if the argument is a local path (not a scheme or file:///). Otherwise + return `path_or_uri`. + """ + parsed_uri = urllib.parse.urlparse(path_or_uri) + if ( + len(parsed_uri.scheme) == 0 + or parsed_uri.scheme == "file" + and len(parsed_uri.netloc) == 0 + ): + return local_file_uri_to_path(path_or_uri) + else: + return None + + +def check_windows_valid_filename(path: Union[int, str]) -> bool: + r"""Verify that the given path does not contain any invalid characters for a Windows filename. + + Windows filenames cannot contain the following characters: + < > : " \ / | ? * + + For more details, refer to the official documentation: + https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions + + Args: + path: The file path to check, which can be either an integer or a string. + + Returns: + bool: True if the path does not contain any invalid characters, False otherwise. + """ + return not bool(re.search(r'[<>:"\\?*]', path)) # type: ignore + + +def make_file_path_upload_safe(path: str) -> str: + r"""Makes the provide path safe for file upload. + + The filename is made safe by: + 1. Removing any leading slashes to prevent writing to absolute paths + 2. Replacing '.' and '..' with underscores to prevent directory traversal attacks + + Raises: + ValueError: If running on Windows and the key contains invalid filename characters + (\, :, *, ?, ", <, >, |) + """ + sys_platform = platform.system() + if sys_platform == "Windows" and not check_windows_valid_filename(path): + raise ValueError( + f"Path {path} is invalid. Please remove invalid filename characters" + r' (\, :, *, ?, ", <, >, |)' + ) + + # On Windows, convert forward slashes to backslashes. + # This ensures that the key is a valid filename on Windows. + if sys_platform == "Windows": + path = str(path).replace("/", os.sep) + + # Avoid writing to absolute paths by striping any leading slashes. + # The key has already been validated for windows operating systems in util.check_windows_valid_filename + # This ensures the key does not contain invalid characters for windows, such as '\' or ':'. + # So we can check only for '/' in the key. + path = path.lstrip(os.sep) + + # Avoid directory traversal by replacing dots with underscores. + paths = path.split(os.sep) + safe_paths = [ + p.replace(".", "_") if p in (os.curdir, os.pardir) else p for p in paths + ] + + # Recombine the key into a relative path. + return os.sep.join(safe_paths) + + +def make_tarfile( + output_filename: str, + source_dir: str, + archive_name: str, + custom_filter: Optional[Callable] = None, +) -> None: + # Helper for filtering out modification timestamps + def _filter_timestamps(tar_info: "tarfile.TarInfo") -> Optional["tarfile.TarInfo"]: + tar_info.mtime = 0 + return tar_info if custom_filter is None else custom_filter(tar_info) + + descriptor, unzipped_filename = tempfile.mkstemp() + try: + with tarfile.open(unzipped_filename, "w") as tar: + tar.add(source_dir, arcname=archive_name, filter=_filter_timestamps) + # When gzipping the tar, don't include the tar's filename or modification time in the + # zipped archive (see https://docs.python.org/3/library/gzip.html#gzip.GzipFile) + with gzip.GzipFile( + filename="", fileobj=open(output_filename, "wb"), mode="wb", mtime=0 + ) as gzipped_tar, open(unzipped_filename, "rb") as tar_file: + gzipped_tar.write(tar_file.read()) + finally: + os.close(descriptor) + os.remove(unzipped_filename) + + +def is_tf_tensor(obj: Any) -> bool: + import tensorflow # type: ignore + + return isinstance(obj, tensorflow.Tensor) + + +def is_tf_tensor_typename(typename: str) -> bool: + return typename.startswith("tensorflow.") and ( + "Tensor" in typename or "Variable" in typename + ) + + +def is_tf_eager_tensor_typename(typename: str) -> bool: + return typename.startswith("tensorflow.") and ("EagerTensor" in typename) + + +def is_pytorch_tensor(obj: Any) -> bool: + import torch # type: ignore + + return isinstance(obj, torch.Tensor) + + +def is_pytorch_tensor_typename(typename: str) -> bool: + return typename.startswith("torch.") and ( + "Tensor" in typename or "Variable" in typename + ) + + +def is_jax_tensor_typename(typename: str) -> bool: + return typename.startswith("jaxlib.") and "Array" in typename + + +def get_jax_tensor(obj: Any) -> Optional[Any]: + import jax # type: ignore + + return jax.device_get(obj) + + +def is_fastai_tensor_typename(typename: str) -> bool: + return typename.startswith("fastai.") and ("Tensor" in typename) + + +def is_pandas_data_frame_typename(typename: str) -> bool: + return typename.startswith("pandas.") and "DataFrame" in typename + + +def is_matplotlib_typename(typename: str) -> bool: + return typename.startswith("matplotlib.") + + +def is_plotly_typename(typename: str) -> bool: + return typename.startswith("plotly.") + + +def is_plotly_figure_typename(typename: str) -> bool: + return typename.startswith("plotly.") and typename.endswith(".Figure") + + +def is_numpy_array(obj: Any) -> bool: + return np and isinstance(obj, np.ndarray) + + +def is_pandas_data_frame(obj: Any) -> bool: + if pd_available: + import pandas as pd + + return isinstance(obj, pd.DataFrame) + else: + return is_pandas_data_frame_typename(get_full_typename(obj)) + + +def ensure_matplotlib_figure(obj: Any) -> Any: + """Extract the current figure from a matplotlib object. + + Return the object itself if it's a figure. + Raises ValueError if the object can't be converted. + """ + import matplotlib # type: ignore + from matplotlib.figure import Figure # type: ignore + + # there are combinations of plotly and matplotlib versions that don't work well together, + # this patches matplotlib to add a removed method that plotly assumes exists + from matplotlib.spines import Spine # type: ignore + + def is_frame_like(self: Any) -> bool: + """Return True if directly on axes frame. + + This is useful for determining if a spine is the edge of an + old style MPL plot. If so, this function will return True. + """ + position = self._position or ("outward", 0.0) + if isinstance(position, str): + if position == "center": + position = ("axes", 0.5) + elif position == "zero": + position = ("data", 0) + if len(position) != 2: + raise ValueError("position should be 2-tuple") + position_type, amount = position # type: ignore + if position_type == "outward" and amount == 0: + return True + else: + return False + + Spine.is_frame_like = is_frame_like + + if obj == matplotlib.pyplot: + obj = obj.gcf() + elif not isinstance(obj, Figure): + if hasattr(obj, "figure"): + obj = obj.figure + # Some matplotlib objects have a figure function + if not isinstance(obj, Figure): + raise ValueError( + "Only matplotlib.pyplot or matplotlib.pyplot.Figure objects are accepted." + ) + return obj + + +def matplotlib_to_plotly(obj: Any) -> Any: + obj = ensure_matplotlib_figure(obj) + tools = get_module( + "plotly.tools", + required=( + "plotly is required to log interactive plots, install with: " + "`pip install plotly` or convert the plot to an image with `wandb.Image(plt)`" + ), + ) + return tools.mpl_to_plotly(obj) + + +def matplotlib_contains_images(obj: Any) -> bool: + obj = ensure_matplotlib_figure(obj) + return any(len(ax.images) > 0 for ax in obj.axes) + + +def _numpy_generic_convert(obj: Any) -> Any: + obj = obj.item() + if isinstance(obj, float) and math.isnan(obj): + obj = None + elif isinstance(obj, np.generic) and ( + obj.dtype.kind == "f" or obj.dtype == "bfloat16" + ): + # obj is a numpy float with precision greater than that of native python float + # (i.e., float96 or float128) or it is of custom type such as bfloat16. + # in these cases, obj.item() does not return a native + # python float (in the first case - to avoid loss of precision, + # so we need to explicitly cast this down to a 64bit float) + obj = float(obj) + return obj + + +def _sanitize_numpy_keys( + d: Dict, + visited: Optional[Dict[int, Dict]] = None, +) -> Tuple[Dict, bool]: + """Returns a dictionary where all NumPy keys are converted. + + Args: + d: The dictionary to sanitize. + + Returns: + A sanitized dictionary, and a boolean indicating whether anything was + changed. + """ + out: Dict[Any, Any] = dict() + converted = False + + # Work with recursive dictionaries: if a dictionary has already been + # converted, reuse its converted value to retain the recursive structure + # of the input. + if visited is None: + visited = {id(d): out} + elif id(d) in visited: + return visited[id(d)], False + visited[id(d)] = out + + for key, value in d.items(): + if isinstance(value, dict): + value, converted_value = _sanitize_numpy_keys(value, visited) + converted |= converted_value + if isinstance(key, np.generic): + key = _numpy_generic_convert(key) + converted = True + out[key] = value + + return out, converted + + +def json_friendly( # noqa: C901 + obj: Any, +) -> Union[Tuple[Any, bool], Tuple[Union[None, str, float], bool]]: + """Convert an object into something that's more becoming of JSON.""" + converted = True + typename = get_full_typename(obj) + + if is_tf_eager_tensor_typename(typename): + obj = obj.numpy() + elif is_tf_tensor_typename(typename): + try: + obj = obj.eval() + except RuntimeError: + obj = obj.numpy() + elif is_pytorch_tensor_typename(typename) or is_fastai_tensor_typename(typename): + try: + if obj.requires_grad: + obj = obj.detach() + except AttributeError: + pass # before 0.4 is only present on variables + + try: + obj = obj.data + except RuntimeError: + pass # happens for Tensors before 0.4 + + if obj.size(): + obj = obj.cpu().detach().numpy() + else: + return obj.item(), True + elif is_jax_tensor_typename(typename): + obj = get_jax_tensor(obj) + + if is_numpy_array(obj): + if obj.size == 1: + obj = obj.flatten()[0] + elif obj.size <= 32: + obj = obj.tolist() + elif np and isinstance(obj, np.generic): + obj = _numpy_generic_convert(obj) + elif isinstance(obj, bytes): + obj = obj.decode("utf-8") + elif isinstance(obj, (datetime, date)): + obj = obj.isoformat() + elif callable(obj): + obj = ( + f"{obj.__module__}.{obj.__qualname__}" + if hasattr(obj, "__qualname__") and hasattr(obj, "__module__") + else str(obj) + ) + elif isinstance(obj, float) and math.isnan(obj): + obj = None + elif isinstance(obj, dict) and np: + obj, converted = _sanitize_numpy_keys(obj) + elif isinstance(obj, set): + # set is not json serializable, so we convert it to tuple + obj = tuple(obj) + elif isinstance(obj, enum.Enum): + obj = obj.name + else: + converted = False + if getsizeof(obj) > VALUE_BYTES_LIMIT: + wandb.termwarn( + f"Serializing object of type {type(obj).__name__} that is {getsizeof(obj)} bytes" + ) + return obj, converted + + +def json_friendly_val(val: Any) -> Any: + """Make any value (including dict, slice, sequence, dataclass) JSON friendly.""" + converted: Union[dict, list] + if isinstance(val, dict): + converted = {} + for key, value in val.items(): + converted[key] = json_friendly_val(value) + return converted + if isinstance(val, slice): + converted = dict( + slice_start=val.start, slice_step=val.step, slice_stop=val.stop + ) + return converted + val, _ = json_friendly(val) + if isinstance(val, Sequence) and not isinstance(val, str): + converted = [] + for value in val: + converted.append(json_friendly_val(value)) + return converted + if is_dataclass(val) and not isinstance(val, type): + converted = asdict(val) + return json_friendly_val(converted) + else: + if val.__class__.__module__ not in ("builtins", "__builtin__"): + val = str(val) + return val + + +def alias_is_version_index(alias: str) -> bool: + return len(alias) >= 2 and alias[0] == "v" and alias[1:].isnumeric() + + +def convert_plots(obj: Any) -> Any: + if is_matplotlib_typename(get_full_typename(obj)): + tools = get_module( + "plotly.tools", + required=( + "plotly is required to log interactive plots, install with: " + "`pip install plotly` or convert the plot to an image with `wandb.Image(plt)`" + ), + ) + obj = tools.mpl_to_plotly(obj) + + if is_plotly_typename(get_full_typename(obj)): + return {"_type": "plotly", "plot": obj.to_plotly_json()} + else: + return obj + + +def maybe_compress_history(obj: Any) -> Tuple[Any, bool]: + if np and isinstance(obj, np.ndarray) and obj.size > 32: + return wandb.Histogram(obj, num_bins=32).to_json(), True + else: + return obj, False + + +def maybe_compress_summary(obj: Any, h5_typename: str) -> Tuple[Any, bool]: + if np and isinstance(obj, np.ndarray) and obj.size > 32: + return ( + { + "_type": h5_typename, # may not be ndarray + "var": np.var(obj).item(), + "mean": np.mean(obj).item(), + "min": np.amin(obj).item(), + "max": np.amax(obj).item(), + "10%": np.percentile(obj, 10), + "25%": np.percentile(obj, 25), + "75%": np.percentile(obj, 75), + "90%": np.percentile(obj, 90), + "size": obj.size, + }, + True, + ) + else: + return obj, False + + +def launch_browser(attempt_launch_browser: bool = True) -> bool: + """Decide if we should launch a browser.""" + _display_variables = ["DISPLAY", "WAYLAND_DISPLAY", "MIR_SOCKET"] + _webbrowser_names_blocklist = ["www-browser", "lynx", "links", "elinks", "w3m"] + + import webbrowser + + launch_browser = attempt_launch_browser + if launch_browser: + if "linux" in sys.platform and not any( + os.getenv(var) for var in _display_variables + ): + launch_browser = False + try: + browser = webbrowser.get() + if hasattr(browser, "name") and browser.name in _webbrowser_names_blocklist: + launch_browser = False + except webbrowser.Error: + launch_browser = False + + return launch_browser + + +def generate_id(length: int = 8) -> str: + # Do not use this; use wandb.sdk.lib.runid.generate_id instead. + # This is kept only for legacy code. + return runid.generate_id(length) + + +def parse_tfjob_config() -> Any: + """Attempt to parse TFJob config, returning False if it can't find it.""" + if os.getenv("TF_CONFIG"): + try: + return json.loads(os.environ["TF_CONFIG"]) + except ValueError: + return False + else: + return False + + +class WandBJSONEncoder(json.JSONEncoder): + """A JSON Encoder that handles some extra types.""" + + def default(self, obj: Any) -> Any: + if hasattr(obj, "json_encode"): + return obj.json_encode() + # if hasattr(obj, 'to_json'): + # return obj.to_json() + tmp_obj, converted = json_friendly(obj) + if converted: + return tmp_obj + return json.JSONEncoder.default(self, obj) + + +class WandBJSONEncoderOld(json.JSONEncoder): + """A JSON Encoder that handles some extra types.""" + + def default(self, obj: Any) -> Any: + tmp_obj, converted = json_friendly(obj) + tmp_obj, compressed = maybe_compress_summary(tmp_obj, get_h5_typename(obj)) + if converted: + return tmp_obj + return json.JSONEncoder.default(self, tmp_obj) + + +class WandBHistoryJSONEncoder(json.JSONEncoder): + """A JSON Encoder that handles some extra types. + + This encoder turns numpy like objects with a size > 32 into histograms. + """ + + def default(self, obj: Any) -> Any: + obj, converted = json_friendly(obj) + obj, compressed = maybe_compress_history(obj) + if converted: + return obj + return json.JSONEncoder.default(self, obj) + + +class JSONEncoderUncompressed(json.JSONEncoder): + """A JSON Encoder that handles some extra types. + + This encoder turns numpy like objects with a size > 32 into histograms. + """ + + def default(self, obj: Any) -> Any: + if is_numpy_array(obj): + return obj.tolist() + elif np and isinstance(obj, np.number): + return obj.item() + elif np and isinstance(obj, np.generic): + obj = obj.item() + return json.JSONEncoder.default(self, obj) + + +def json_dump_safer(obj: Any, fp: IO[str], **kwargs: Any) -> None: + """Convert obj to json, with some extra encodable types.""" + return dump(obj, fp, cls=WandBJSONEncoder, **kwargs) + + +def json_dumps_safer(obj: Any, **kwargs: Any) -> str: + """Convert obj to json, with some extra encodable types.""" + return dumps(obj, cls=WandBJSONEncoder, **kwargs) + + +# This is used for dumping raw json into files +def json_dump_uncompressed(obj: Any, fp: IO[str], **kwargs: Any) -> None: + """Convert obj to json, with some extra encodable types.""" + return dump(obj, fp, cls=JSONEncoderUncompressed, **kwargs) + + +def json_dumps_safer_history(obj: Any, **kwargs: Any) -> str: + """Convert obj to json, with some extra encodable types, including histograms.""" + return dumps(obj, cls=WandBHistoryJSONEncoder, **kwargs) + + +def make_json_if_not_number( + v: Union[int, float, str, Mapping, Sequence], +) -> Union[int, float, str]: + """If v is not a basic type convert it to json.""" + if isinstance(v, (float, int)): + return v + return json_dumps_safer(v) + + +def make_safe_for_json(obj: Any) -> Any: + """Replace invalid json floats with strings. Also converts to lists and dicts.""" + if isinstance(obj, Mapping): + return {k: make_safe_for_json(v) for k, v in obj.items()} + elif isinstance(obj, str): + # str's are Sequence, so we need to short-circuit + return obj + elif isinstance(obj, Sequence): + return [make_safe_for_json(v) for v in obj] + elif isinstance(obj, float): + # W&B backend and UI handle these strings + if obj != obj: # standard way to check for NaN + return "NaN" + elif obj == float("+inf"): + return "Infinity" + elif obj == float("-inf"): + return "-Infinity" + return obj + + +def no_retry_4xx(e: Exception) -> bool: + if not isinstance(e, requests.HTTPError): + return True + assert e.response is not None + if not (400 <= e.response.status_code < 500) or e.response.status_code == 429: + return True + body = json.loads(e.response.content) + raise UsageError(body["errors"][0]["message"]) + + +def parse_backend_error_messages(response: requests.Response) -> List[str]: + """Returns error messages stored in a backend response. + + If the response is not in an expected format, an empty list is returned. + + Args: + response: A response to an HTTP request to the W&B server. + """ + try: + data = response.json() + except requests.JSONDecodeError: + return [] + + if not isinstance(data, dict): + return [] + + # Backend error values are returned in one of two ways: + # - A string containing the error message + # - A JSON object with a "message" field that is a string + def get_message(error: Any) -> Optional[str]: + if isinstance(error, str): + return error + elif ( + isinstance(error, dict) + and (message := error.get("message")) + and isinstance(message, str) + ): + return message + else: + return None + + # The response can contain an "error" field with a single error + # or an "errors" field with a list of errors. + if error := data.get("error"): + message = get_message(error) + return [message] if message else [] + + elif (errors := data.get("errors")) and isinstance(errors, list): + messages: List[str] = [] + for error in errors: + message = get_message(error) + if message: + messages.append(message) + return messages + + else: + return [] + + +def no_retry_auth(e: Any) -> bool: + if hasattr(e, "exception"): + e = e.exception + if not isinstance(e, requests.HTTPError): + return True + if e.response is None: + return True + # Don't retry bad request errors; raise immediately + if e.response.status_code in (400, 409): + return False + # Retry all non-forbidden/unauthorized/not-found errors. + if e.response.status_code not in (401, 403, 404): + return True + + # Crash with more informational message on forbidden/unauthorized errors. + # UnauthorizedError + if e.response.status_code == 401: + raise AuthenticationError( + "The API key you provided is either invalid or missing. " + f"If the `{wandb.env.API_KEY}` environment variable is set, make sure it is correct. " + "Otherwise, to resolve this issue, you may try running the 'wandb login --relogin' command. " + "If you are using a local server, make sure that you're using the correct hostname. " + "If you're not sure, you can try logging in again using the 'wandb login --relogin --host [hostname]' command." + f"(Error {e.response.status_code}: {e.response.reason})" + ) + # ForbiddenError + if e.response.status_code == 403: + if wandb.run: + raise CommError(f"Permission denied to access {wandb.run.path}") + else: + raise CommError( + "It appears that you do not have permission to access the requested resource. " + "Please reach out to the project owner to grant you access. " + "If you have the correct permissions, verify that there are no issues with your networking setup." + f"(Error {e.response.status_code}: {e.response.reason})" + ) + + # NotFoundError + if e.response.status_code == 404: + # If error message is empty, raise a more generic NotFoundError message. + if parse_backend_error_messages(e.response): + return False + else: + raise LookupError( + f"Failed to find resource. Please make sure you have the correct resource path. " + f"(Error {e.response.status_code}: {e.response.reason})" + ) + return False + + +def check_retry_conflict(e: Any) -> Optional[bool]: + """Check if the exception is a conflict type so it can be retried. + + Returns: + True - Should retry this operation + False - Should not retry this operation + None - No decision, let someone else decide + """ + if hasattr(e, "exception"): + e = e.exception + if isinstance(e, requests.HTTPError) and e.response is not None: + if e.response.status_code == 409: + return True + return None + + +def check_retry_conflict_or_gone(e: Any) -> Optional[bool]: + """Check if the exception is a conflict or gone type, so it can be retried or not. + + Returns: + True - Should retry this operation + False - Should not retry this operation + None - No decision, let someone else decide + """ + if hasattr(e, "exception"): + e = e.exception + if isinstance(e, requests.HTTPError) and e.response is not None: + if e.response.status_code == 409: + return True + if e.response.status_code == 410: + return False + return None + + +def make_check_retry_fn( + fallback_retry_fn: CheckRetryFnType, + check_fn: Callable[[Exception], Optional[bool]], + check_timedelta: Optional[timedelta] = None, +) -> CheckRetryFnType: + """Return a check_retry_fn which can be used by lib.Retry(). + + Args: + fallback_fn: Use this function if check_fn didn't decide if a retry should happen. + check_fn: Function which returns bool if retry should happen or None if unsure. + check_timedelta: Optional retry timeout if we check_fn matches the exception + """ + + def check_retry_fn(e: Exception) -> Union[bool, timedelta]: + check = check_fn(e) + if check is None: + return fallback_retry_fn(e) + if check is False: + return False + if check_timedelta: + return check_timedelta + return True + + return check_retry_fn + + +def find_runner(program: str) -> Union[None, list, List[str]]: + """Return a command that will run program. + + Args: + program: The string name of the program to try to run. + + Returns: + commandline list of strings to run the program (eg. with subprocess.call()) or None + """ + if os.path.isfile(program) and not os.access(program, os.X_OK): + # program is a path to a non-executable file + try: + opened = open(program) + except OSError: # PermissionError doesn't exist in 2.7 + return None + first_line = opened.readline().strip() + if first_line.startswith("#!"): + return shlex.split(first_line[2:]) + if program.endswith(".py"): + return [sys.executable] + return None + + +def downsample(values: Sequence, target_length: int) -> list: + """Downsample 1d values to target_length, including start and end. + + Algorithm just rounds index down. + + Values can be any sequence, including a generator. + """ + if not target_length > 1: + raise UsageError("target_length must be > 1") + values = list(values) + if len(values) < target_length: + return values + ratio = float(len(values) - 1) / (target_length - 1) + result = [] + for i in range(target_length): + result.append(values[int(i * ratio)]) + return result + + +def has_num(dictionary: Mapping, key: Any) -> bool: + return key in dictionary and isinstance(dictionary[key], numbers.Number) + + +def docker_image_regex(image: str) -> Any: + """Regex match for valid docker image names.""" + if image: + return re.match( + r"^(?:(?=[^:\/]{1,253})(?!-)[a-zA-Z0-9-]{1,63}(? Optional[str]: + """Scan docker run args and attempt to find the most likely docker image argument. + + It excludes any arguments that start with a dash, and the argument after it if it + isn't a boolean switch. This can be improved, we currently fallback gracefully when + this fails. + """ + bool_args = [ + "-t", + "--tty", + "--rm", + "--privileged", + "--oom-kill-disable", + "--no-healthcheck", + "-i", + "--interactive", + "--init", + "--help", + "--detach", + "-d", + "--sig-proxy", + "-it", + "-itd", + ] + last_flag = -2 + last_arg = "" + possible_images = [] + if len(args) > 0 and args[0] == "run": + args.pop(0) + for i, arg in enumerate(args): + if arg.startswith("-"): + last_flag = i + last_arg = arg + elif "@sha256:" in arg: + # Because our regex doesn't match digests + possible_images.append(arg) + elif docker_image_regex(arg): + if last_flag == i - 2: + possible_images.append(arg) + elif "=" in last_arg: + possible_images.append(arg) + elif last_arg in bool_args and last_flag == i - 1: + possible_images.append(arg) + most_likely = None + for img in possible_images: + if ":" in img or "@" in img or "/" in img: + most_likely = img + break + if most_likely is None and len(possible_images) > 0: + most_likely = possible_images[0] + return most_likely + + +def load_yaml(file: Any) -> Any: + return yaml.safe_load(file) + + +def image_id_from_k8s() -> Optional[str]: + """Ping the k8s metadata service for the image id. + + Specify the KUBERNETES_NAMESPACE environment variable if your pods are not in the + default namespace: + + - name: KUBERNETES_NAMESPACE valueFrom: + fieldRef: + fieldPath: metadata.namespace + """ + token_path = "/var/run/secrets/kubernetes.io/serviceaccount/token" + + if not os.path.exists(token_path): + return None + + try: + with open(token_path) as token_file: + token = token_file.read() + except FileNotFoundError: + logger.warning(f"Token file not found at {token_path}.") + return None + except PermissionError as e: + current_uid = os.getuid() + warning = ( + f"Unable to read the token file at {token_path} due to permission error ({e})." + f"The current user id is {current_uid}. " + "Consider changing the securityContext to run the container as the current user." + ) + logger.warning(warning) + wandb.termwarn(warning) + return None + + if not token: + return None + + k8s_server = "https://{}:{}/api/v1/namespaces/{}/pods/{}".format( + os.getenv("KUBERNETES_SERVICE_HOST"), + os.getenv("KUBERNETES_PORT_443_TCP_PORT"), + os.getenv("KUBERNETES_NAMESPACE", "default"), + os.getenv("HOSTNAME"), + ) + try: + res = requests.get( + k8s_server, + verify="/var/run/secrets/kubernetes.io/serviceaccount/ca.crt", + timeout=3, + headers={"Authorization": f"Bearer {token}"}, + ) + res.raise_for_status() + except requests.RequestException: + return None + try: + return str( # noqa: B005 + res.json()["status"]["containerStatuses"][0]["imageID"] + ).strip("docker-pullable://") + except (ValueError, KeyError, IndexError): + logger.exception("Error checking kubernetes for image id") + return None + + +def async_call( + target: Callable, timeout: Optional[Union[int, float]] = None +) -> Callable: + """Wrap a method to run in the background with an optional timeout. + + Returns a new method that will call the original with any args, waiting for upto + timeout seconds. This new method blocks on the original and returns the result or + None if timeout was reached, along with the thread. You can check thread.is_alive() + to determine if a timeout was reached. If an exception is thrown in the thread, we + reraise it. + """ + q: queue.Queue = queue.Queue() + + def wrapped_target(q: "queue.Queue", *args: Any, **kwargs: Any) -> Any: + try: + q.put(target(*args, **kwargs)) + except Exception as e: + q.put(e) + + def wrapper( + *args: Any, **kwargs: Any + ) -> Union[Tuple[Exception, "threading.Thread"], Tuple[None, "threading.Thread"]]: + thread = threading.Thread( + target=wrapped_target, args=(q,) + args, kwargs=kwargs + ) + thread.daemon = True + thread.start() + + try: + result = q.get(True, timeout) + except queue.Empty: + return None, thread + + if isinstance(result, Exception): + raise result.with_traceback(sys.exc_info()[2]) + return result, thread + + return wrapper + + +def read_many_from_queue( + q: "queue.Queue", max_items: int, queue_timeout: Union[int, float] +) -> list: + try: + item = q.get(True, queue_timeout) + except queue.Empty: + return [] + items = [item] + for _ in range(max_items): + try: + item = q.get_nowait() + except queue.Empty: + return items + items.append(item) + return items + + +def stopwatch_now() -> float: + """Get a time value for interval comparisons. + + When possible it is a monotonic clock to prevent backwards time issues. + """ + return time.monotonic() + + +def class_colors(class_count: int) -> List[List[int]]: + # make class 0 black, and the rest equally spaced fully saturated hues + return [[0, 0, 0]] + [ + colorsys.hsv_to_rgb(i / (class_count - 1.0), 1.0, 1.0) # type: ignore + for i in range(class_count - 1) + ] + + +def _prompt_choice( + input_timeout: Union[int, float, None] = None, + jupyter: bool = False, +) -> str: + input_fn: Callable = input + prompt = term.LOG_STRING + if input_timeout is not None: + # delayed import to mitigate risk of timed_input complexity + from wandb.sdk.lib import timed_input + + input_fn = functools.partial(timed_input.timed_input, timeout=input_timeout) + # timed_input doesn't handle enhanced prompts + if platform.system() == "Windows": + prompt = "wandb" + + text = f"{prompt}: Enter your choice: " + if input_fn == input: + choice = input_fn(text) + else: + choice = input_fn(text, jupyter=jupyter) + return choice # type: ignore + + +def prompt_choices( + choices: Sequence[str], + input_timeout: Union[int, float, None] = None, + jupyter: bool = False, +) -> str: + """Allow a user to choose from a list of options.""" + for i, choice in enumerate(choices): + wandb.termlog(f"({i + 1}) {choice}") + + idx = -1 + while idx < 0 or idx > len(choices) - 1: + choice = _prompt_choice(input_timeout=input_timeout, jupyter=jupyter) + if not choice: + continue + idx = -1 + try: + idx = int(choice) - 1 + except ValueError: + pass + if idx < 0 or idx > len(choices) - 1: + wandb.termwarn("Invalid choice") + result = choices[idx] + wandb.termlog(f"You chose {result!r}") + return result + + +def guess_data_type(shape: Sequence[int], risky: bool = False) -> Optional[str]: + """Infer the type of data based on the shape of the tensors. + + Args: + shape (Sequence[int]): The shape of the data + risky(bool): some guesses are more likely to be wrong. + """ + # (samples,) or (samples,logits) + if len(shape) in (1, 2): + return "label" + # Assume image mask like fashion mnist: (no color channel) + # This is risky because RNNs often have 3 dim tensors: batch, time, channels + if risky and len(shape) == 3: + return "image" + if len(shape) == 4: + if shape[-1] in (1, 3, 4): + # (samples, height, width, Y \ RGB \ RGBA) + return "image" + else: + # (samples, height, width, logits) + return "segmentation_mask" + return None + + +def download_file_from_url( + dest_path: str, source_url: str, api_key: Optional[str] = None +) -> None: + auth = None + if not _thread_local_api_settings.cookies: + auth = ("api", api_key or "") + response = requests.get( + source_url, + auth=auth, + headers=_thread_local_api_settings.headers, + cookies=_thread_local_api_settings.cookies, + stream=True, + timeout=5, + ) + response.raise_for_status() + + if os.sep in dest_path: + filesystem.mkdir_exists_ok(os.path.dirname(dest_path)) + with fsync_open(dest_path, "wb") as file: + for data in response.iter_content(chunk_size=1024): + file.write(data) + + +def download_file_into_memory(source_url: str, api_key: Optional[str] = None) -> bytes: + auth = None + if not _thread_local_api_settings.cookies: + auth = ("api", api_key or "") + response = requests.get( + source_url, + auth=auth, + headers=_thread_local_api_settings.headers, + cookies=_thread_local_api_settings.cookies, + stream=True, + timeout=5, + ) + response.raise_for_status() + return response.content + + +def isatty(ob: IO) -> bool: + return hasattr(ob, "isatty") and ob.isatty() + + +def to_human_size(size: int, units: Optional[List[Tuple[str, Any]]] = None) -> str: + units = units or POW_10_BYTES + unit, value = units[0] + factor = round(float(size) / value, 1) + return ( + f"{factor}{unit}" + if factor < 1024 or len(units) == 1 + else to_human_size(size, units[1:]) + ) + + +def from_human_size(size: str, units: Optional[List[Tuple[str, Any]]] = None) -> int: + units = units or POW_10_BYTES + units_dict = {unit.upper(): value for (unit, value) in units} + regex = re.compile( + r"(\d+\.?\d*)\s*({})?".format("|".join(units_dict.keys())), re.IGNORECASE + ) + match = re.match(regex, size) + if not match: + raise ValueError("size must be of the form `10`, `10B` or `10 B`.") + factor, unit = ( + float(match.group(1)), + units_dict[match.group(2).upper()] if match.group(2) else 1, + ) + return int(factor * unit) + + +def auto_project_name(program: Optional[str]) -> str: + # if we're in git, set project name to git repo name + relative path within repo + from wandb.sdk.lib.gitlib import GitRepo + + root_dir = GitRepo().root_dir + if root_dir is None: + return "uncategorized" + # On windows, GitRepo returns paths in unix style, but os.path is windows + # style. Coerce here. + root_dir = to_native_slash_path(root_dir) + repo_name = os.path.basename(root_dir) + if program is None: + return str(repo_name) + if not os.path.isabs(program): + program = os.path.join(os.curdir, program) + prog_dir = os.path.dirname(os.path.abspath(program)) + if not prog_dir.startswith(root_dir): + return str(repo_name) + project = repo_name + sub_path = os.path.relpath(prog_dir, root_dir) + if sub_path != ".": + project += "-" + sub_path + return str(project.replace(os.sep, "_")) + + +def are_paths_on_same_drive(path1: str, path2: str) -> bool: + """Check if two paths are on the same drive. + + This check is only relevant on Windows, + since the concept of drives only exists on Windows. + """ + if platform.system() != "Windows": + return True + + try: + path1_drive = pathlib.Path(path1).resolve().drive + path2_drive = pathlib.Path(path2).resolve().drive + except OSError: + # If either path is not a valid Windows path, an OSError is raised. + return False + + return path1_drive == path2_drive + + +# TODO(hugh): Deprecate version here and use wandb/sdk/lib/paths.py +def to_forward_slash_path(path: str) -> str: + if platform.system() == "Windows": + path = path.replace("\\", "/") + return path + + +# TODO(hugh): Deprecate version here and use wandb/sdk/lib/paths.py +def to_native_slash_path(path: str) -> FilePathStr: + return FilePathStr(path.replace("/", os.sep)) + + +def check_and_warn_old(files: List[str]) -> bool: + if "wandb-metadata.json" in files: + wandb.termwarn("These runs were logged with a previous version of wandb.") + wandb.termwarn( + "Run pip install wandb<0.10.0 to get the old library and sync your runs." + ) + return True + return False + + +class ImportMetaHook: + def __init__(self) -> None: + self.modules: Dict[str, ModuleType] = dict() + self.on_import: Dict[str, list] = dict() + + def add(self, fullname: str, on_import: Callable) -> None: + self.on_import.setdefault(fullname, []).append(on_import) + + def install(self) -> None: + sys.meta_path.insert(0, self) # type: ignore + + def uninstall(self) -> None: + sys.meta_path.remove(self) # type: ignore + + def find_module( + self, fullname: str, path: Optional[str] = None + ) -> Optional["ImportMetaHook"]: + if fullname in self.on_import: + return self + return None + + def load_module(self, fullname: str) -> ModuleType: + self.uninstall() + mod = importlib.import_module(fullname) + self.install() + self.modules[fullname] = mod + on_imports = self.on_import.get(fullname) + if on_imports: + for f in on_imports: + f() + return mod + + def get_modules(self) -> Tuple[str, ...]: + return tuple(self.modules) + + def get_module(self, module: str) -> ModuleType: + return self.modules[module] + + +_import_hook: Optional[ImportMetaHook] = None + + +def add_import_hook(fullname: str, on_import: Callable) -> None: + global _import_hook + if _import_hook is None: + _import_hook = ImportMetaHook() + _import_hook.install() + _import_hook.add(fullname, on_import) + + +def host_from_path(path: Optional[str]) -> str: + """Return the host of the path.""" + url = urllib.parse.urlparse(path) + return str(url.netloc) + + +def uri_from_path(path: Optional[str]) -> str: + """Return the URI of the path.""" + url = urllib.parse.urlparse(path) + uri = url.path if url.path[0] != "/" else url.path[1:] + return str(uri) + + +def is_unicode_safe(stream: TextIO) -> bool: + """Return True if the stream supports UTF-8.""" + encoding = getattr(stream, "encoding", None) + return encoding.lower() in {"utf-8", "utf_8"} if encoding else False + + +def _has_internet() -> bool: + """Returns whether we have internet access. + + Checks for internet access by attempting to open a DNS connection to + Google's root servers. + """ + try: + s = socket.create_connection(("8.8.8.8", 53), 0.5) + s.close() + except OSError: + return False + + return True + + +def rand_alphanumeric( + length: int = 8, rand: Optional[Union[ModuleType, random.Random]] = None +) -> str: + wandb.termerror("rand_alphanumeric is deprecated, use 'secrets.token_hex'") + rand = rand or random + return "".join(rand.choice("0123456789ABCDEF") for _ in range(length)) + + +@contextlib.contextmanager +def fsync_open( + path: StrPath, mode: str = "w", encoding: Optional[str] = None +) -> Generator[IO[Any], None, None]: + """Open a path for I/O and guarantee that the file is flushed and synced.""" + with open(path, mode, encoding=encoding) as f: + yield f + + f.flush() + os.fsync(f.fileno()) + + +def _is_kaggle() -> bool: + return ( + os.getenv("KAGGLE_KERNEL_RUN_TYPE") is not None + or "kaggle_environments" in sys.modules + ) + + +def _is_likely_kaggle() -> bool: + # Telemetry to mark first runs from Kagglers. + return ( + _is_kaggle() + or os.path.exists( + os.path.expanduser(os.path.join("~", ".kaggle", "kaggle.json")) + ) + or "kaggle" in sys.modules + ) + + +def _is_databricks() -> bool: + # check if we are running inside a databricks notebook by + # inspecting sys.modules, searching for dbutils and verifying that + # it has the appropriate structure + + if "dbutils" in sys.modules: + dbutils = sys.modules["dbutils"] + if hasattr(dbutils, "shell"): + shell = dbutils.shell + if hasattr(shell, "sc"): + sc = shell.sc + if hasattr(sc, "appName"): + return bool(sc.appName == "Databricks Shell") + return False + + +def _is_py_requirements_or_dockerfile(path: str) -> bool: + file = os.path.basename(path) + return ( + file.endswith(".py") + or file.startswith("Dockerfile") + or file == "requirements.txt" + ) + + +def artifact_to_json(artifact: "Artifact") -> Dict[str, Any]: + return { + "_type": "artifactVersion", + "_version": "v0", + "id": artifact.id, + "version": artifact.source_version, + "sequenceName": artifact.source_name.split(":")[0], + "usedAs": artifact.use_as, + } + + +def check_dict_contains_nested_artifact(d: dict, nested: bool = False) -> bool: + for item in d.values(): + if isinstance(item, dict): + contains_artifacts = check_dict_contains_nested_artifact(item, True) + if contains_artifacts: + return True + elif (isinstance(item, wandb.Artifact) or _is_artifact_string(item)) and nested: + return True + return False + + +def load_json_yaml_dict(config: str) -> Any: + ext = os.path.splitext(config)[-1] + if ext == ".json": + with open(config) as f: + return json.load(f) + elif ext == ".yaml": + with open(config) as f: + return yaml.safe_load(f) + else: + try: + return json.loads(config) + except ValueError: + return None + + +def _parse_entity_project_item(path: str) -> tuple: + """Parse paths with the following formats: {item}, {project}/{item}, & {entity}/{project}/{item}. + + Args: + path: `str`, input path; must be between 0 and 3 in length. + + Returns: + tuple of length 3 - (item, project, entity) + + Example: + alias, project, entity = _parse_entity_project_item("myproj/mymodel:best") + + assert entity == "" + assert project == "myproj" + assert alias == "mymodel:best" + + """ + words = path.split("/") + if len(words) > 3: + raise ValueError( + "Invalid path: must be str the form {item}, {project}/{item}, or {entity}/{project}/{item}" + ) + padded_words = [""] * (3 - len(words)) + words + return tuple(reversed(padded_words)) + + +def _resolve_aliases(aliases: Optional[Union[str, Iterable[str]]]) -> List[str]: + """Add the 'latest' alias and ensure that all aliases are unique. + + Takes in `aliases` which can be None, str, or List[str] and returns List[str]. + Ensures that "latest" is always present in the returned list. + + Args: + aliases: `Optional[Union[str, List[str]]]` + + Returns: + List[str], with "latest" always present. + + Usage: + + ```python + aliases = _resolve_aliases(["best", "dev"]) + assert aliases == ["best", "dev", "latest"] + + aliases = _resolve_aliases("boom") + assert aliases == ["boom", "latest"] + ``` + """ + aliases = aliases or ["latest"] + + if isinstance(aliases, str): + aliases = [aliases] + + try: + return list(set(aliases) | {"latest"}) + except TypeError as exc: + raise ValueError("`aliases` must be Iterable or None") from exc + + +def _is_artifact_object(v: Any) -> "TypeGuard[wandb.Artifact]": + return isinstance(v, wandb.Artifact) + + +def _is_artifact_string(v: Any) -> "TypeGuard[str]": + return isinstance(v, str) and v.startswith("wandb-artifact://") + + +def _is_artifact_version_weave_dict(v: Any) -> "TypeGuard[dict]": + return isinstance(v, dict) and v.get("_type") == "artifactVersion" + + +def _is_artifact_representation(v: Any) -> bool: + return ( + _is_artifact_object(v) + or _is_artifact_string(v) + or _is_artifact_version_weave_dict(v) + ) + + +def parse_artifact_string(v: str) -> Tuple[str, Optional[str], bool]: + if not v.startswith("wandb-artifact://"): + raise ValueError(f"Invalid artifact string: {v}") + parsed_v = v[len("wandb-artifact://") :] + base_uri = None + url_info = urllib.parse.urlparse(parsed_v) + if url_info.scheme != "": + base_uri = f"{url_info.scheme}://{url_info.netloc}" + parts = url_info.path.split("/")[1:] + else: + parts = parsed_v.split("/") + if parts[0] == "_id": + # for now can't fetch paths but this will be supported in the future + # when we allow passing typed media objects, this can be extended + # to include paths + return parts[1], base_uri, True + + if len(parts) < 3: + raise ValueError(f"Invalid artifact string: {v}") + + # for now can't fetch paths but this will be supported in the future + # when we allow passing typed media objects, this can be extended + # to include paths + entity, project, name_and_alias_or_version = parts[:3] + return f"{entity}/{project}/{name_and_alias_or_version}", base_uri, False + + +def _get_max_cli_version() -> Union[str, None]: + max_cli_version = wandb.api.max_cli_version() + return str(max_cli_version) if max_cli_version is not None else None + + +def ensure_text( + string: Union[str, bytes], encoding: str = "utf-8", errors: str = "strict" +) -> str: + """Coerce s to str.""" + if isinstance(string, bytes): + return string.decode(encoding, errors) + elif isinstance(string, str): + return string + else: + raise TypeError(f"not expecting type {type(string)!r}") + + +def make_artifact_name_safe(name: str) -> str: + """Make an artifact name safe for use in artifacts.""" + # artifact names may only contain alphanumeric characters, dashes, underscores, and dots. + cleaned = re.sub(r"[^a-zA-Z0-9_\-.]", "_", name) + if len(cleaned) <= 128: + return cleaned + # truncate with dots in the middle using regex + return re.sub(r"(^.{63}).*(.{63}$)", r"\g<1>..\g<2>", cleaned) + + +def make_docker_image_name_safe(name: str) -> str: + """Make a docker image name safe for use in artifacts.""" + safe_chars = RE_DOCKER_IMAGE_NAME_CHARS.sub("__", name.lower()) + deduped = RE_DOCKER_IMAGE_NAME_SEPARATOR_REPEAT.sub("__", safe_chars) + trimmed_start = RE_DOCKER_IMAGE_NAME_SEPARATOR_START.sub("", deduped) + trimmed = RE_DOCKER_IMAGE_NAME_SEPARATOR_END.sub("", trimmed_start) + return trimmed if trimmed else "image" + + +def merge_dicts( + source: Dict[str, Any], + destination: Dict[str, Any], +) -> Dict[str, Any]: + """Recursively merge two dictionaries. + + This mutates the destination and its nested dictionaries and lists. + + Instances of `dict` are recursively merged and instances of `list` + are appended to the destination. If the destination type is not + `dict` or `list`, respectively, the key is overwritten with the + source value. + + For all other types, the source value overwrites the destination value. + """ + for key, value in source.items(): + if isinstance(value, dict): + node = destination.get(key) + if isinstance(node, dict): + merge_dicts(value, node) + else: + destination[key] = value + + elif isinstance(value, list): + dest_value = destination.get(key) + if isinstance(dest_value, list): + dest_value.extend(value) + else: + destination[key] = value + + else: + destination[key] = value + + return destination + + +def coalesce(*arg: Any) -> Any: + """Return the first non-none value in the list of arguments. + + Similar to ?? in C#. + """ + return next((a for a in arg if a is not None), None) + + +def recursive_cast_dictlike_to_dict(d: Dict[str, Any]) -> Dict[str, Any]: + for k, v in d.items(): + if isinstance(v, dict): + recursive_cast_dictlike_to_dict(v) + elif hasattr(v, "keys"): + d[k] = dict(v) + recursive_cast_dictlike_to_dict(d[k]) + return d + + +def remove_keys_with_none_values( + d: Union[Dict[str, Any], Any], +) -> Union[Dict[str, Any], Any]: + # otherwise iterrows will create a bunch of ugly charts + if not isinstance(d, dict): + return d + + if isinstance(d, dict): + new_dict = {} + for k, v in d.items(): + new_v = remove_keys_with_none_values(v) + if new_v is not None and not (isinstance(new_v, dict) and len(new_v) == 0): + new_dict[k] = new_v + return new_dict if new_dict else None + + +def batched(n: int, iterable: Iterable[T]) -> Generator[List[T], None, None]: + i = iter(iterable) + batch = list(itertools.islice(i, n)) + while batch: + yield batch + batch = list(itertools.islice(i, n)) + + +def random_string(length: int = 12) -> str: + """Generate a random string of a given length. + + :param length: Length of the string to generate. + :return: Random string. + """ + return "".join( + secrets.choice(string.ascii_lowercase + string.digits) for _ in range(length) + ) + + +def sample_with_exponential_decay_weights( + xs: Union[Iterable, Iterable[Iterable]], + ys: Iterable[Iterable], + keys: Optional[Iterable] = None, + sample_size: int = 1500, +) -> Tuple[List, List, Optional[List]]: + """Sample from a list of lists with weights that decay exponentially. + + May be used with the wandb.plot.line_series function. + """ + xs_array = np.array(xs) + ys_array = np.array(ys) + keys_array = np.array(keys) if keys else None + weights = np.exp(-np.arange(len(xs_array)) / len(xs_array)) + weights /= np.sum(weights) + sampled_indices = np.random.choice(len(xs_array), size=sample_size, p=weights) + sampled_xs = xs_array[sampled_indices].tolist() + sampled_ys = ys_array[sampled_indices].tolist() + sampled_keys = keys_array[sampled_indices].tolist() if keys_array else None + + return sampled_xs, sampled_ys, sampled_keys + + +@dataclasses.dataclass(frozen=True) +class InstalledDistribution: + """An installed distribution. + + Attributes: + key: The distribution name as it would be imported. + version: The distribution's version string. + """ + + key: str + version: str + + +def working_set() -> Iterable[InstalledDistribution]: + """Return the working set of installed distributions.""" + from importlib.metadata import distributions + + for d in distributions(): + try: + # In some distributions, the "Name" attribute may not be present, + # which can raise a KeyError. To handle this, we catch the exception + # and skip those distributions. + # For additional context, see: https://github.com/python/importlib_metadata/issues/371. + + # From Sentry events we observed that UnicodeDecodeError can occur when + # trying to decode the metadata of a distribution. To handle this, we catch + # the exception and skip those distributions. + yield InstalledDistribution(key=d.metadata["Name"], version=d.version) + except (KeyError, UnicodeDecodeError): + pass + + +def get_core_path() -> str: + """Returns the path to the wandb-core binary. + + The path can be set explicitly via the _WANDB_CORE_PATH environment + variable. Otherwise, the path to the binary in the current package + is returned. + + Returns: + str: The path to the wandb-core package. + + Raises: + WandbCoreNotAvailableError: If wandb-core was not built for the current system. + """ + # NOTE: Environment variable _WANDB_CORE_PATH is a temporary development feature + # to assist in running the core service from a live development directory. + path_from_env: str = os.environ.get("_WANDB_CORE_PATH", "") + if path_from_env: + wandb.termwarn( + f"Using wandb-core from path `_WANDB_CORE_PATH={path_from_env}`. " + "This is a development feature and may not work as expected." + ) + return path_from_env + + bin_path = pathlib.Path(__file__).parent / "bin" / "wandb-core" + if not bin_path.exists(): + raise WandbCoreNotAvailableError( + f"File not found: {bin_path}." + " Please contact support at support@wandb.com." + f" Your platform is: {platform.platform()}." + ) + + return str(bin_path) + + +class NonOctalStringDumper(yaml.Dumper): + """Prevents strings containing non-octal values like "008" and "009" from being converted to numbers in in the yaml string saved as the sweep config.""" + + def represent_scalar(self, tag, value, style=None): + if tag == "tag:yaml.org,2002:str" and value.startswith("0") and len(value) > 1: + return super().represent_scalar(tag, value, style="'") + return super().represent_scalar(tag, value, style) diff --git a/.venv/lib/python3.12/site-packages/wandb/wandb_agent.py b/.venv/lib/python3.12/site-packages/wandb/wandb_agent.py new file mode 100644 index 0000000000000000000000000000000000000000..30819c702f8e3b81165adb92b9db3ca3612aac39 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/wandb/wandb_agent.py @@ -0,0 +1,611 @@ +import logging +import multiprocessing +import os +import platform +import queue +import re +import signal +import socket +import subprocess +import sys +import time +import traceback +from typing import Any, Callable, Dict, List, Optional + +import yaml + +import wandb +from wandb import util, wandb_lib, wandb_sdk +from wandb.agents.pyagent import pyagent +from wandb.apis import InternalApi +from wandb.sdk.launch.sweeps import utils as sweep_utils +from wandb.sdk.lib import ipython + +logger = logging.getLogger(__name__) + + +class AgentError(Exception): + pass + + +class AgentProcess: + """Launch and manage a process.""" + + def __init__( + self, env=None, command=None, function=None, run_id=None, in_jupyter=None + ): + self._popen = None + self._proc = None + self._finished_q = multiprocessing.Queue() + self._proc_killed = False + + if command: + if platform.system() == "Windows": + kwargs = dict(creationflags=subprocess.CREATE_NEW_PROCESS_GROUP) + env.pop(wandb.env.SERVICE, None) + # TODO: Determine if we need the same stdin workaround as POSIX case below. + self._popen = subprocess.Popen(command, env=env, **kwargs) + else: + if sys.version_info >= (3, 11): + # preexec_fn=os.setpgrp is not thread-safe; process_group was introduced in + # python 3.11 to replace it, so use that when possible + kwargs = dict(process_group=0) + else: + kwargs = dict(preexec_fn=os.setpgrp) + env.pop(wandb.env.SERVICE, None) + # Upon spawning the subprocess in a new process group, the child's process group is + # not connected to the controlling terminal's stdin. If it tries to access stdin, + # it gets a SIGTTIN and blocks until we give it the terminal, which we don't want + # to do. + # + # By using subprocess.PIPE, we give it an independent stdin. However, it will still + # block if it tries to read from stdin, because we're not writing anything to it. + # We immediately close the subprocess's stdin here so it can fail fast and get an + # EOF. + # + # (One situation that makes this relevant is that importing `readline` even + # indirectly can cause the child to attempt to access stdin, which can trigger the + # deadlock. In Python 3.13, `import torch` indirectly imports `readline` via `pdb`, + # meaning `import torch` in a run script can deadlock unless we override stdin. + # See https://github.com/wandb/wandb/pull/10489 description for more details.) + # + # Also, we avoid spawning a new session because that breaks preempted child process + # handling. + self._popen = subprocess.Popen( + command, + env=env, + stdin=subprocess.PIPE, + **kwargs, + ) + self._popen.stdin.close() + elif function: + self._proc = multiprocessing.Process( + target=self._start, + args=(self._finished_q, env, function, run_id, in_jupyter), + ) + self._proc.start() + else: + raise AgentError("Agent Process requires command or function") + + def _start(self, finished_q, env, function, run_id, in_jupyter): + if env: + for k, v in env.items(): + os.environ[k] = v + + # call user function + wandb.termlog(f"Agent Started Run: {run_id}") + if function: + function() + wandb.termlog(f"Agent Finished Run: {run_id}\n") + + # complete the run + run = wandb.run + if run: + wandb.join() + + # signal that the process is finished + finished_q.put(True) + + def poll(self): + if self._popen: + return self._popen.poll() + if self._proc_killed: + # we need to join process to prevent zombies + self._proc.join() + return True + try: + finished = self._finished_q.get(False, 0) + if finished: + return True + except queue.Empty: + pass + return + + def wait(self): + if self._popen: + # if on windows, wait() will block and we won't be able to interrupt + if platform.system() == "Windows": + while True: + p = self._popen.poll() + if p is not None: + return p + time.sleep(1) + return self._popen.wait() + return self._proc.join() + + def kill(self): + if self._popen: + return self._popen.kill() + pid = self._proc.pid + if pid: + ret = os.kill(pid, signal.SIGKILL) + self._proc_killed = True + return ret + return + + def terminate(self): + if self._popen: + # windows terminate is too strong, send Ctrl-C instead + if platform.system() == "Windows": + return self._popen.send_signal(signal.CTRL_C_EVENT) + return self._popen.terminate() + return self._proc.terminate() + + +class Agent: + POLL_INTERVAL = 5 + REPORT_INTERVAL = 0 + KILL_DELAY = 30 + FLAPPING_MAX_SECONDS = 60 + FLAPPING_MAX_FAILURES = 3 + MAX_INITIAL_FAILURES = 5 + DEFAULT_SWEEP_COMMAND: List[str] = [ + "${env}", + "${interpreter}", + "${program}", + "${args}", + ] + SWEEP_COMMAND_ENV_VAR_REGEX = re.compile(r"\$\{envvar\:([A-Z0-9_]*)\}") + + def __init__( + self, api, queue, sweep_id=None, function=None, in_jupyter=None, count=None + ): + self._api = api + self._queue = queue + self._run_processes = {} # keyed by run.id (GQL run name) + self._server_responses = [] + self._sweep_id = sweep_id + self._in_jupyter = in_jupyter + self._log = [] + self._running = True + self._last_report_time = None + self._function = function + self._report_interval = wandb.env.get_agent_report_interval( + self.REPORT_INTERVAL + ) + self._kill_delay = wandb.env.get_agent_kill_delay(self.KILL_DELAY) + self._finished = 0 + self._failed = 0 + self._count = count + self._sweep_command = [] + self._max_initial_failures = wandb.env.get_agent_max_initial_failures( + self.MAX_INITIAL_FAILURES + ) + if self._report_interval is None: + raise AgentError("Invalid agent report interval") + if self._kill_delay is None: + raise AgentError("Invalid agent kill delay") + # if the directory to log to is not set, set it + if os.environ.get("WANDB_DIR") is None: + os.environ["WANDB_DIR"] = os.path.abspath(os.getcwd()) + + def is_flapping(self): + """Determine if the process is flapping. + + Flapping occurs if the agents receives FLAPPING_MAX_FAILURES non-0 exit codes in + the first FLAPPING_MAX_SECONDS. + """ + if os.getenv(wandb.env.AGENT_DISABLE_FLAPPING) == "true": + return False + if time.time() < wandb.START_TIME + self.FLAPPING_MAX_SECONDS: + return self._failed >= self.FLAPPING_MAX_FAILURES + + def is_failing(self): + return ( + self._failed >= self._finished + and self._max_initial_failures <= self._failed + ) + + def run(self): # noqa: C901 + # TODO: catch exceptions, handle errors, show validation warnings, and make more generic + sweep_obj = self._api.sweep(self._sweep_id, "{}") + if sweep_obj: + sweep_yaml = sweep_obj.get("config") + if sweep_yaml: + sweep_config = yaml.safe_load(sweep_yaml) + if sweep_config: + sweep_command = sweep_config.get("command") + if sweep_command and isinstance(sweep_command, list): + self._sweep_command = sweep_command + + # TODO: include sweep ID + agent = self._api.register_agent(socket.gethostname(), sweep_id=self._sweep_id) + agent_id = agent["id"] + + try: + while self._running: + commands = util.read_many_from_queue( + self._queue, 100, self.POLL_INTERVAL + ) + for command in commands: + command["resp_queue"].put(self._process_command(command)) + + now = util.stopwatch_now() + if self._last_report_time is None or ( + self._report_interval != 0 + and now > self._last_report_time + self._report_interval + ): + logger.info("Running runs: %s", list(self._run_processes.keys())) + self._last_report_time = now + run_status = {} + for run_id, run_process in list(self._run_processes.items()): + poll_result = run_process.poll() + if poll_result is None: + run_status[run_id] = True + continue + elif ( + not isinstance(poll_result, bool) + and isinstance(poll_result, int) + and poll_result > 0 + ): + self._failed += 1 + if self.is_flapping(): + logger.error( + "Detected %i failed runs in the first %i seconds, shutting down.", + self.FLAPPING_MAX_FAILURES, + self.FLAPPING_MAX_SECONDS, + ) + logger.info( + "To disable this check set WANDB_AGENT_DISABLE_FLAPPING=true" + ) + self._running = False + break + if self.is_failing(): + logger.error( + "Detected %i failed runs in a row, shutting down.", + self._max_initial_failures, + ) + logger.info( + "To change this value set WANDB_AGENT_MAX_INITIAL_FAILURES=val" + ) + self._running = False + break + logger.info("Cleaning up finished run: %s", run_id) + + # wandb.teardown() was added with wandb service and is a hammer to make + # sure that active runs are finished before moving on to another agent run + # + # In the future, a lighter weight way to implement this could be to keep a + # service process open for all the agent instances and inform_finish when + # the run should be marked complete. This however could require + # inform_finish on every run created by this process. + if hasattr(wandb, "teardown"): + exit_code = 0 + if isinstance(poll_result, int): + exit_code = poll_result + elif isinstance(poll_result, bool): + exit_code = -1 + wandb.teardown(exit_code) + + del self._run_processes[run_id] + self._last_report_time = None + self._finished += 1 + + if self._count and self._finished >= self._count or not self._running: + self._running = False + continue + + commands = self._api.agent_heartbeat(agent_id, {}, run_status) + + # TODO: send _server_responses + self._server_responses = [] + for command in commands: + self._server_responses.append(self._process_command(command)) + + except KeyboardInterrupt: + try: + wandb.termlog( + "Ctrl-c pressed. Waiting for runs to end. Press ctrl-c again to terminate them." + ) + for _, run_process in self._run_processes.items(): + run_process.wait() + except KeyboardInterrupt: + pass + finally: + try: + if not self._in_jupyter: + wandb.termlog("Terminating and syncing runs. Press ctrl-c to kill.") + for _, run_process in self._run_processes.items(): + try: + run_process.terminate() + except OSError: + pass # if process is already dead + for _, run_process in self._run_processes.items(): + run_process.wait() + except KeyboardInterrupt: + wandb.termlog("Killing runs and quitting.") + for _, run_process in self._run_processes.items(): + try: + run_process.kill() + except OSError: + pass # if process is already dead + + def _process_command(self, command): + logger.info( + "Agent received command: %s" + % (command["type"] if "type" in command else "Unknown") + ) + response = { + "id": command.get("id"), + "result": None, + } + try: + command_type = command["type"] + if command_type == "run": + result = self._command_run(command) + elif command_type == "stop": + result = self._command_stop(command) + elif command_type == "exit": + result = self._command_exit(command) + elif command_type == "resume": + result = self._command_run(command) + else: + raise AgentError(f"No such command: {command_type}") # noqa: TRY301 + response["result"] = result + except Exception: + logger.exception("Exception while processing command: %s", command) + ex_type, ex, tb = sys.exc_info() + response["exception"] = f"{ex_type.__name__}: {str(ex)}" + response["traceback"] = traceback.format_tb(tb) + del tb + + self._log.append((command, response)) + + return response + + def _command_run(self, command): + logger.info( + "Agent starting run with config:\n" + + "\n".join( + ["\t{}: {}".format(k, v["value"]) for k, v in command["args"].items()] + ) + ) + if self._in_jupyter: + wandb.termlog( + f"Agent Starting Run: {command.get('run_id')} with config:\n" + + "\n".join( + [f"\t{k}: {v['value']}" for k, v in command["args"].items()] + ) + ) + + # Setup sweep command + sweep_command: List[str] = sweep_utils.create_sweep_command(self._sweep_command) + + run_id = command.get("run_id") + sweep_id = os.environ.get(wandb.env.SWEEP_ID) + # TODO(jhr): move into settings + config_file = os.path.join( + "wandb", "sweep-" + sweep_id, "config-" + run_id + ".yaml" + ) + json_file = os.path.join( + "wandb", "sweep-" + sweep_id, "config-" + run_id + ".json" + ) + + os.environ[wandb.env.RUN_ID] = run_id + + base_dir = os.environ.get(wandb.env.DIR, "") + sweep_param_path = os.path.join(base_dir, config_file) + os.environ[wandb.env.SWEEP_PARAM_PATH] = sweep_param_path + wandb_lib.config_util.save_config_file_from_dict( + sweep_param_path, command["args"] + ) + + env = dict(os.environ) + + sweep_vars: Dict[str, Any] = sweep_utils.create_sweep_command_args(command) + + if "${args_json_file}" in sweep_command: + with open(json_file, "w") as fp: + fp.write(sweep_vars["args_json"][0]) + + if self._function: + # make sure that each run regenerates setup singleton + wandb.teardown() + proc = AgentProcess( + function=self._function, + env=env, + run_id=run_id, + in_jupyter=self._in_jupyter, + ) + else: + sweep_vars["interpreter"] = ["python"] + sweep_vars["program"] = [command["program"]] + sweep_vars["args_json_file"] = [json_file] + if not platform.system() == "Windows": + sweep_vars["env"] = ["/usr/bin/env"] + command_list = [] + for c in sweep_command: + c = str(c) + if c.startswith("${") and c.endswith("}"): + replace_list = sweep_vars.get(c[2:-1]) + command_list += replace_list or [] + else: + command_list += [c] + logger.info( + "About to run command: {}".format( + " ".join(f'"{c}"' if " " in c else c for c in command_list) + ) + ) + proc = AgentProcess(command=command_list, env=env) + self._run_processes[run_id] = proc + + # we keep track of when we sent the sigterm to give processes a chance + # to handle the signal before sending sigkill every heartbeat + self._run_processes[run_id].last_sigterm_time = None + self._last_report_time = None + + def _command_stop(self, command): + run_id = command["run_id"] + if run_id in self._run_processes: + proc = self._run_processes[run_id] + now = util.stopwatch_now() + if proc.last_sigterm_time is None: + proc.last_sigterm_time = now + logger.info("Stop: %s", run_id) + try: + proc.terminate() + except OSError: # if process is already dead + pass + elif now > proc.last_sigterm_time + self._kill_delay: + logger.info("Kill: %s", run_id) + try: + proc.kill() + except OSError: # if process is already dead + pass + else: + logger.error("Run %s not running", run_id) + + def _command_exit(self, command): + logger.info("Received exit command. Killing runs and quitting.") + for _, proc in self._run_processes.items(): + try: + proc.kill() + except OSError: + # process is already dead + pass + self._running = False + + +class AgentApi: + def __init__(self, queue): + self._queue = queue + self._command_id = 0 + self._multiproc_manager = multiprocessing.Manager() + + def command(self, command): + command["origin"] = "local" + command["id"] = f"local-{self._command_id}" + self._command_id += 1 + resp_queue = self._multiproc_manager.Queue() + command["resp_queue"] = resp_queue + self._queue.put(command) + result = resp_queue.get() + print("result:", result) # noqa: T201 + if "exception" in result: + print("Exception occurred while running command") # noqa: T201 + for line in result["traceback"]: + print(line.strip()) # noqa: T201 + print(result["exception"]) # noqa: T201 + return result + + +def run_agent( + sweep_id, function=None, in_jupyter=None, entity=None, project=None, count=None +): + parts = dict(entity=entity, project=project, name=sweep_id) + err = sweep_utils.parse_sweep_id(parts) + if err: + wandb.termerror(err) + return + entity = parts.get("entity") or entity + project = parts.get("project") or project + sweep_id = parts.get("name") or sweep_id + + if entity: + wandb.env.set_entity(entity) + if project: + wandb.env.set_project(project) + if sweep_id: + # TODO(jhr): remove when jobspec is merged + os.environ[wandb.env.SWEEP_ID] = sweep_id + logger.setLevel(logging.DEBUG) + ch = logging.StreamHandler() + log_level = logging.DEBUG + if in_jupyter: + log_level = logging.ERROR + ch.setLevel(log_level) + formatter = logging.Formatter( + "%(asctime)s - %(name)s - %(levelname)s - %(message)s" + ) + ch.setFormatter(formatter) + try: + logger.addHandler(ch) + + api = InternalApi() + queue = multiprocessing.Queue() + agent = Agent( + api, + queue, + sweep_id=sweep_id, + function=function, + in_jupyter=in_jupyter, + count=count, + ) + agent.run() + finally: + # make sure we remove the logging handler (important for jupyter notebooks) + logger.removeHandler(ch) + + +def agent( + sweep_id: str, + function: Optional[Callable] = None, + entity: Optional[str] = None, + project: Optional[str] = None, + count: Optional[int] = None, +) -> None: + """Start one or more sweep agents. + + The sweep agent uses the `sweep_id` to know which sweep it + is a part of, what function to execute, and (optionally) how + many agents to run. + + Args: + sweep_id: The unique identifier for a sweep. A sweep ID + is generated by W&B CLI or Python SDK. + function: A function to call instead of the "program" + specified in the sweep config. + entity: The username or team name where you want to send W&B + runs created by the sweep to. Ensure that the entity you + specify already exists. If you don't specify an entity, + the run will be sent to your default entity, + which is usually your username. + project: The name of the project where W&B runs created from + the sweep are sent to. If the project is not specified, the + run is sent to a project labeled "Uncategorized". + count: The number of sweep config trials to try. + """ + global _INSTANCES + _INSTANCES += 1 + try: + # make sure we are logged in + wandb_sdk.wandb_login._login(_silent=True) + if function: + return pyagent(sweep_id, function, entity, project, count) + return run_agent( + sweep_id, + function=function, + in_jupyter=ipython.in_jupyter(), + entity=entity, + project=project, + count=count, + ) + finally: + _INSTANCES -= 1 + + +_INSTANCES = 0 + + +def _is_running(): + return bool(_INSTANCES) diff --git a/.venv/lib/python3.12/site-packages/wandb/wandb_controller.py b/.venv/lib/python3.12/site-packages/wandb/wandb_controller.py new file mode 100644 index 0000000000000000000000000000000000000000..f819dc995e27192c7debdc87fa1042b3e77f1c98 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/wandb/wandb_controller.py @@ -0,0 +1,719 @@ +"""Sweep controller. + +This module implements the sweep controller. + +On error an exception is raised: + ControllerError + +Example: + import wandb + + # + # create a sweep controller + # + # There are three different ways sweeps can be created: + # (1) create with sweep id from `wandb sweep` command + sweep_id = 'xyzxyz2' + tuner = wandb.controller(sweep_id) + # (2) create with sweep config + sweep_config = {} + tuner = wandb.controller() + tuner.configure(sweep_config) + tuner.create() + # (3) create by constructing programmatic sweep configuration + tuner = wandb.controller() + tuner.configure_search('random') + tuner.configure_program('train-dummy.py') + tuner.configure_parameter('param1', values=[1,2,3]) + tuner.configure_parameter('param2', values=[1,2,3]) + tuner.configure_controller(type="local") + tuner.create() + # + # run the sweep controller + # + # There are three different ways sweeps can be executed: + # (1) run to completion + tuner.run() + # (2) run in a simple loop + while not tuner.done(): + tuner.step() + tuner.print_status() + # (3) run in a more complex loop + while not tuner.done(): + params = tuner.search() + tuner.schedule(params) + runs = tuner.stopping() + if runs: + tuner.stop_runs(runs) +""" + +import json +import os +import random +import string +import time +from typing import Callable, Dict, List, Optional, Tuple, Union + +import yaml + +from wandb import env +from wandb.apis import InternalApi +from wandb.sdk import wandb_sweep +from wandb.sdk.launch.sweeps.utils import ( + handle_sweep_config_violations, + sweep_config_err_text_from_jsonschema_violations, +) +from wandb.util import get_module + +# TODO(jhr): Add metric status +# TODO(jhr): Add print_space +# TODO(jhr): Add print_summary + + +sweeps = get_module( + "sweeps", + required="wandb[sweeps] is required to use the local controller. " + "Please run `pip install wandb[sweeps]`.", +) + + +# This should be something like 'pending' (but we need to make sure everyone else is ok with that) +SWEEP_INITIAL_RUN_STATE = sweeps.RunState.pending + + +def _id_generator(size=10, chars=string.ascii_lowercase + string.digits): + return "".join(random.choice(chars) for _ in range(size)) + + +class ControllerError(Exception): + """Base class for sweep errors.""" + + +class _WandbController: + """Sweep controller class. + + Internal datastructures on the sweep object to coordinate local controller with + cloud controller. + + Data structures: + controller: { + schedule: [ + { id: SCHEDULE_ID + data: {param1: val1, param2: val2}}, + ] + earlystop: [RUN_ID, ...] + scheduler: + scheduled: [ + { id: SCHEDULE_ID + runid: RUN_ID}, + ] + + `controller` is only updated by the client + `scheduler` is only updated by the cloud backend + + Protocols: + Scheduling a run: + - client controller adds a schedule entry on the controller.schedule list + - cloud backend notices the new entry and creates a run with the parameters + - cloud backend adds a scheduled entry on the scheduler.scheduled list + - client controller notices that the run has been scheduled and removes it from + controller.schedule list + + Current implementation details: + - Runs are only schedule if there are no other runs scheduled. + + """ + + def __init__(self, sweep_id_or_config=None, entity=None, project=None): + # sweep id configured in constructor + self._sweep_id: Optional[str] = None + + # configured parameters + # Configuration to be created + self._create: Dict = {} + # Custom search + self._custom_search: Optional[ + Callable[ + [Union[dict, sweeps.SweepConfig], List[sweeps.SweepRun]], + Optional[sweeps.SweepRun], + ] + ] = None + # Custom stopping + self._custom_stopping: Optional[ + Callable[ + [Union[dict, sweeps.SweepConfig], List[sweeps.SweepRun]], + List[sweeps.SweepRun], + ] + ] = None + # Program function (used for future jupyter support) + self._program_function = None + + # The following are updated every sweep step + # raw sweep object (dict of strings) + self._sweep_obj = None + # parsed sweep config (dict) + self._sweep_config: Optional[Union[dict, sweeps.SweepConfig]] = None + # sweep metric used to optimize (str or None) + self._sweep_metric: Optional[str] = None + # list of _Run objects + self._sweep_runs: Optional[List[sweeps.SweepRun]] = None + # dictionary mapping name of run to run object + self._sweep_runs_map: Optional[Dict[str, sweeps.SweepRun]] = None + # scheduler dict (read only from controller) - used as feedback from the server + self._scheduler: Optional[Dict] = None + # controller dict (write only from controller) - used to send commands to server + self._controller: Optional[Dict] = None + # keep track of controller dict from previous step + self._controller_prev_step: Optional[Dict] = None + + # Internal + # Keep track of whether the sweep has been started + self._started: bool = False + # indicate whether there is more to schedule + self._done_scheduling: bool = False + # indicate whether the sweep needs to be created + self._defer_sweep_creation: bool = False + # count of logged lines since last status + self._logged: int = 0 + # last status line printed + self._laststatus: str = "" + # keep track of logged actions for print_actions() + self._log_actions: List[Tuple[str, str]] = [] + # keep track of logged debug for print_debug() + self._log_debug: List[str] = [] + + # all backend commands use internal api + environ = os.environ + if entity: + env.set_entity(entity, env=environ) + if project: + env.set_project(project, env=environ) + self._api = InternalApi(environ=environ) + + if isinstance(sweep_id_or_config, str): + self._sweep_id = sweep_id_or_config + elif isinstance(sweep_id_or_config, dict) or isinstance( + sweep_id_or_config, sweeps.SweepConfig + ): + self._create = sweeps.SweepConfig(sweep_id_or_config) + + # check for custom search and or stopping functions + for config_key, controller_attr in zip( + ["method", "early_terminate"], ["_custom_search", "_custom_stopping"] + ): + if callable(config_key in self._create and self._create[config_key]): + setattr(self, controller_attr, self._create[config_key]) + self._create[config_key] = "custom" + + self._sweep_id = self.create(from_dict=True) + elif sweep_id_or_config is None: + self._defer_sweep_creation = True + return + else: + raise ControllerError("Unhandled sweep controller type") + sweep_obj = self._sweep_object_read_from_backend() + if sweep_obj is None: + raise ControllerError("Can not find sweep") + self._sweep_obj = sweep_obj + + def configure_search( + self, + search: Union[ + str, + Callable[ + [Union[dict, sweeps.SweepConfig], List[sweeps.SweepRun]], + Optional[sweeps.SweepRun], + ], + ], + ): + self._configure_check() + if isinstance(search, str): + self._create["method"] = search + elif callable(search): + self._create["method"] = "custom" + self._custom_search = search + else: + raise ControllerError("Unhandled search type.") + + def configure_stopping( + self, + stopping: Union[ + str, + Callable[ + [Union[dict, sweeps.SweepConfig], List[sweeps.SweepRun]], + List[sweeps.SweepRun], + ], + ], + **kwargs, + ): + self._configure_check() + if isinstance(stopping, str): + self._create.setdefault("early_terminate", {}) + self._create["early_terminate"]["type"] = stopping + for k, v in kwargs.items(): + self._create["early_terminate"][k] = v + elif callable(stopping): + self._custom_stopping = stopping(kwargs) + self._create.setdefault("early_terminate", {}) + self._create["early_terminate"]["type"] = "custom" + else: + raise ControllerError("Unhandled stopping type.") + + def configure_metric(self, metric, goal=None): + self._configure_check() + self._create.setdefault("metric", {}) + self._create["metric"]["name"] = metric + if goal: + self._create["metric"]["goal"] = goal + + def configure_program(self, program): + self._configure_check() + if isinstance(program, str): + self._create["program"] = program + elif callable(program): + self._create["program"] = "__callable__" + self._program_function = program + raise ControllerError("Program functions are not supported yet") + else: + raise ControllerError("Unhandled sweep program type") + + def configure_name(self, name): + self._configure_check() + self._create["name"] = name + + def configure_description(self, description): + self._configure_check() + self._create["description"] = description + + def configure_parameter( + self, + name, + values=None, + value=None, + distribution=None, + min=None, + max=None, + mu=None, + sigma=None, + q=None, + a=None, + b=None, + ): + self._configure_check() + self._create.setdefault("parameters", {}).setdefault(name, {}) + if value is not None or ( + values is None and min is None and max is None and distribution is None + ): + self._create["parameters"][name]["value"] = value + if values is not None: + self._create["parameters"][name]["values"] = values + if distribution is not None: + self._create["parameters"][name]["distribution"] = distribution + if min is not None: + self._create["parameters"][name]["min"] = min + if max is not None: + self._create["parameters"][name]["max"] = max + if mu is not None: + self._create["parameters"][name]["mu"] = mu + if sigma is not None: + self._create["parameters"][name]["sigma"] = sigma + if q is not None: + self._create["parameters"][name]["q"] = q + if a is not None: + self._create["parameters"][name]["a"] = a + if b is not None: + self._create["parameters"][name]["b"] = b + + def configure_controller(self, type): + """Configure controller to local if type == 'local'.""" + self._configure_check() + self._create.setdefault("controller", {}) + self._create["controller"].setdefault("type", type) + + def configure(self, sweep_dict_or_config): + self._configure_check() + if self._create: + raise ControllerError("Already configured.") + if isinstance(sweep_dict_or_config, dict): + self._create = sweep_dict_or_config + elif isinstance(sweep_dict_or_config, str): + self._create = yaml.safe_load(sweep_dict_or_config) + else: + raise ControllerError("Unhandled sweep controller type") + + @property + def sweep_config(self) -> Union[dict, sweeps.SweepConfig]: + return self._sweep_config + + @property + def sweep_id(self) -> str: + return self._sweep_id + + def _log(self) -> None: + self._logged += 1 + + def _error(self, s: str) -> None: + print("ERROR:", s) # noqa: T201 + self._log() + + def _warn(self, s: str) -> None: + print("WARN:", s) # noqa: T201 + self._log() + + def _info(self, s: str) -> None: + print("INFO:", s) # noqa: T201 + self._log() + + def _debug(self, s: str) -> None: + print("DEBUG:", s) # noqa: T201 + self._log() + + def _configure_check(self) -> None: + if self._started: + raise ControllerError("Can not configure after sweep has been started.") + + def _validate(self, config: Dict) -> str: + violations = sweeps.schema_violations_from_proposed_config(config) + msg = ( + sweep_config_err_text_from_jsonschema_violations(violations) + if len(violations) > 0 + else "" + ) + return msg + + def create(self, from_dict: bool = False) -> str: + if self._started: + raise ControllerError("Can not create after sweep has been started.") + if not self._defer_sweep_creation and not from_dict: + raise ControllerError("Can not use create on already created sweep.") + if not self._create: + raise ControllerError("Must configure sweep before create.") + + # validate sweep config + self._create = sweeps.SweepConfig(self._create) + + # Create sweep + sweep_id, warnings = self._api.upsert_sweep(self._create) + handle_sweep_config_violations(warnings) + + print("Create sweep with ID:", sweep_id) # noqa: T201 + sweep_url = wandb_sweep._get_sweep_url(self._api, sweep_id) + if sweep_url: + print("Sweep URL:", sweep_url) # noqa: T201 + self._sweep_id = sweep_id + self._defer_sweep_creation = False + return sweep_id + + def run( + self, + verbose: bool = False, + print_status: bool = True, + print_actions: bool = False, + print_debug: bool = False, + ) -> None: + if verbose: + print_status = True + print_actions = True + print_debug = True + self._start_if_not_started() + while not self.done(): + if print_status: + self.print_status() + self.step() + if print_actions: + self.print_actions() + if print_debug: + self.print_debug() + time.sleep(5) + + def _sweep_object_read_from_backend(self) -> Optional[dict]: + specs_json = {} + if self._sweep_metric: + k = ["_step"] + k.append(self._sweep_metric) + specs_json = {"keys": k, "samples": 100000} + specs = json.dumps(specs_json) + # TODO(jhr): catch exceptions? + sweep_obj = self._api.sweep(self._sweep_id, specs) + if not sweep_obj: + return + self._sweep_obj = sweep_obj + self._sweep_config = yaml.safe_load(sweep_obj["config"]) + self._sweep_metric = self._sweep_config.get("metric", {}).get("name") + + _sweep_runs: List[sweeps.SweepRun] = [] + for r in sweep_obj["runs"]: + rr = r.copy() + if "summaryMetrics" in rr: + if rr["summaryMetrics"]: + rr["summaryMetrics"] = json.loads(rr["summaryMetrics"]) + if "config" not in rr: + raise ValueError("sweep object is missing config") + rr["config"] = json.loads(rr["config"]) + if "history" in rr: + if isinstance(rr["history"], list): + rr["history"] = [json.loads(d) for d in rr["history"]] + else: + raise ValueError( + "Invalid history value: expected list of json strings: {}".format( + rr["history"] + ) + ) + if "sampledHistory" in rr: + sampled_history = [] + for historyDictList in rr["sampledHistory"]: + sampled_history += historyDictList + rr["sampledHistory"] = sampled_history + _sweep_runs.append(sweeps.SweepRun(**rr)) + + self._sweep_runs = _sweep_runs + self._sweep_runs_map = {r.name: r for r in self._sweep_runs} + + self._controller = json.loads(sweep_obj.get("controller") or "{}") + self._scheduler = json.loads(sweep_obj.get("scheduler") or "{}") + self._controller_prev_step = self._controller.copy() + return sweep_obj + + def _sweep_object_sync_to_backend(self) -> None: + if self._controller == self._controller_prev_step: + return + sweep_obj_id = self._sweep_obj["id"] + controller = json.dumps(self._controller) + _, warnings = self._api.upsert_sweep( + self._sweep_config, controller=controller, obj_id=sweep_obj_id + ) + handle_sweep_config_violations(warnings) + self._controller_prev_step = self._controller.copy() + + def _start_if_not_started(self) -> None: + if self._started: + return + if self._defer_sweep_creation: + raise ControllerError( + "Must specify or create a sweep before running controller." + ) + obj = self._sweep_object_read_from_backend() + if not obj: + return + is_local = self._sweep_config.get("controller", {}).get("type") == "local" + if not is_local: + raise ControllerError( + "Only sweeps with a local controller are currently supported." + ) + self._started = True + # reset controller state, we might want to parse this and decide + # what we can continue and add a version key, but for now we can + # be safe and just reset things on start + self._controller = {} + self._sweep_object_sync_to_backend() + + def _parse_scheduled(self): + scheduled_list = self._scheduler.get("scheduled") or [] + started_ids = [] + stopped_runs = [] + done_runs = [] + for s in scheduled_list: + runid = s.get("runid") + objid = s.get("id") + r = self._sweep_runs_map.get(runid) + if not r: + continue + if r.stopped: + stopped_runs.append(runid) + summary = r.summary_metrics + if r.state == SWEEP_INITIAL_RUN_STATE and not summary: + continue + started_ids.append(objid) + if r.state != "running": + done_runs.append(runid) + return started_ids, stopped_runs, done_runs + + def _step(self) -> None: + self._start_if_not_started() + self._sweep_object_read_from_backend() + + started_ids, stopped_runs, done_runs = self._parse_scheduled() + + # Remove schedule entry from controller dict if already scheduled + schedule_list = self._controller.get("schedule", []) + new_schedule_list = [s for s in schedule_list if s.get("id") not in started_ids] + self._controller["schedule"] = new_schedule_list + + # Remove earlystop entry from controller if already stopped + earlystop_list = self._controller.get("earlystop", []) + new_earlystop_list = [ + r for r in earlystop_list if r not in stopped_runs and r not in done_runs + ] + self._controller["earlystop"] = new_earlystop_list + + # Clear out step logs + self._log_actions = [] + self._log_debug = [] + + def step(self) -> None: + self._step() + suggestion = self.search() + self.schedule(suggestion) + to_stop = self.stopping() + if len(to_stop) > 0: + self.stop_runs(to_stop) + + def done(self) -> bool: + self._start_if_not_started() + state = self._sweep_obj.get("state") + if state in [ + s.upper() + for s in ( + sweeps.RunState.preempting.value, + SWEEP_INITIAL_RUN_STATE.value, + sweeps.RunState.running.value, + ) + ]: + return False + return True + + def _search(self) -> Optional[sweeps.SweepRun]: + search = self._custom_search or sweeps.next_run + next_run = search(self._sweep_config, self._sweep_runs or []) + if next_run is None: + self._done_scheduling = True + return next_run + + def search(self) -> Optional[sweeps.SweepRun]: + self._start_if_not_started() + suggestion = self._search() + return suggestion + + def _stopping(self) -> List[sweeps.SweepRun]: + if "early_terminate" not in self.sweep_config: + return [] + stopper = self._custom_stopping or sweeps.stop_runs + stop_runs = stopper(self._sweep_config, self._sweep_runs or []) + + debug_lines = [ + " ".join([f"{k}={v}" for k, v in run.early_terminate_info.items()]) + for run in stop_runs + if run.early_terminate_info is not None + ] + if debug_lines: + self._log_debug += debug_lines + + return stop_runs + + def stopping(self) -> List[sweeps.SweepRun]: + self._start_if_not_started() + return self._stopping() + + def schedule(self, run: Optional[sweeps.SweepRun]) -> None: + self._start_if_not_started() + + # only schedule one run at a time (for now) + if self._controller and self._controller.get("schedule"): + return + + schedule_id = _id_generator() + + if run is None: + schedule_list = [{"id": schedule_id, "data": {"args": None}}] + else: + param_list = [ + "{}={}".format(k, v.get("value")) for k, v in sorted(run.config.items()) + ] + self._log_actions.append(("schedule", ",".join(param_list))) + + # schedule one run + schedule_list = [{"id": schedule_id, "data": {"args": run.config}}] + + self._controller["schedule"] = schedule_list + self._sweep_object_sync_to_backend() + + def stop_runs(self, runs: List[sweeps.SweepRun]) -> None: + earlystop_list = list({run.name for run in runs}) + self._log_actions.append(("stop", ",".join(earlystop_list))) + self._controller["earlystop"] = earlystop_list + self._sweep_object_sync_to_backend() + + def print_status(self) -> None: + status = _sweep_status(self._sweep_obj, self._sweep_config, self._sweep_runs) + if self._laststatus != status or self._logged: + print(status) # noqa: T201 + self._laststatus = status + self._logged = 0 + + def print_actions(self) -> None: + for action, line in self._log_actions: + self._info(f"{action.capitalize()} ({line})") + self._log_actions = [] + + def print_debug(self) -> None: + for line in self._log_debug: + self._debug(line) + self._log_debug = [] + + def print_space(self) -> None: + self._warn("Method not implemented yet.") + + def print_summary(self) -> None: + self._warn("Method not implemented yet.") + + +def _get_run_counts(runs: List[sweeps.SweepRun]) -> Dict[str, int]: + metrics = {} + categories = [name for name, _ in sweeps.RunState.__members__.items()] + ["unknown"] + for r in runs: + state = r.state + found = "unknown" + for c in categories: + if state == c: + found = c + break + metrics.setdefault(found, 0) + metrics[found] += 1 + return metrics + + +def _get_runs_status(metrics): + categories = [name for name, _ in sweeps.RunState.__members__.items()] + ["unknown"] + mlist = [] + for c in categories: + if not metrics.get(c): + continue + mlist.append(f"{c.capitalize()}: {metrics[c]}") + s = ", ".join(mlist) + return s + + +def _sweep_status( + sweep_obj: dict, + sweep_conf: Union[dict, sweeps.SweepConfig], + sweep_runs: List[sweeps.SweepRun], +) -> str: + sweep = sweep_obj["name"] + _ = sweep_obj["state"] + run_count = len(sweep_runs) + run_type_counts = _get_run_counts(sweep_runs) + stopped = len([r for r in sweep_runs if r.stopped]) + stopping = len([r for r in sweep_runs if r.should_stop]) + stopstr = "" + if stopped or stopping: + stopstr = f"Stopped: {stopped}" + if stopping: + stopstr += f" (Stopping: {stopping})" + runs_status = _get_runs_status(run_type_counts) + method = sweep_conf.get("method", "unknown") + stopping = sweep_conf.get("early_terminate", None) + sweep_options = [] + sweep_options.append(method) + if stopping: + sweep_options.append(stopping.get("type", "unknown")) + sweep_options = ",".join(sweep_options) + sections = [] + sections.append(f"Sweep: {sweep} ({sweep_options})") + if runs_status: + sections.append(f"Runs: {run_count} ({runs_status})") + else: + sections.append(f"Runs: {run_count}") + if stopstr: + sections.append(stopstr) + sections = " | ".join(sections) + return sections diff --git a/.venv/lib/python3.12/site-packages/wandb/wandb_run.py b/.venv/lib/python3.12/site-packages/wandb/wandb_run.py new file mode 100644 index 0000000000000000000000000000000000000000..01cfb8ce2874a86f646c055127b8f568b3a55681 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/wandb/wandb_run.py @@ -0,0 +1,8 @@ +"""Compatibility wandb_run module. + +Please use `wandb.Run` instead. +""" + +from wandb.sdk.wandb_run import Run + +__all__ = ["Run"] diff --git a/.venv/lib/python3.12/site-packages/zipp-3.23.0.dist-info/INSTALLER b/.venv/lib/python3.12/site-packages/zipp-3.23.0.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/.venv/lib/python3.12/site-packages/zipp-3.23.0.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/.venv/lib/python3.12/site-packages/zipp-3.23.0.dist-info/METADATA b/.venv/lib/python3.12/site-packages/zipp-3.23.0.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..6420117987041c052142deea6b16884cdb435c2f --- /dev/null +++ b/.venv/lib/python3.12/site-packages/zipp-3.23.0.dist-info/METADATA @@ -0,0 +1,106 @@ +Metadata-Version: 2.4 +Name: zipp +Version: 3.23.0 +Summary: Backport of pathlib-compatible object wrapper for zip files +Author-email: "Jason R. Coombs" +License-Expression: MIT +Project-URL: Source, https://github.com/jaraco/zipp +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Requires-Python: >=3.9 +Description-Content-Type: text/x-rst +License-File: LICENSE +Provides-Extra: test +Requires-Dist: pytest!=8.1.*,>=6; extra == "test" +Requires-Dist: jaraco.itertools; extra == "test" +Requires-Dist: jaraco.functools; extra == "test" +Requires-Dist: more_itertools; extra == "test" +Requires-Dist: big-O; extra == "test" +Requires-Dist: pytest-ignore-flaky; extra == "test" +Requires-Dist: jaraco.test; extra == "test" +Provides-Extra: doc +Requires-Dist: sphinx>=3.5; extra == "doc" +Requires-Dist: jaraco.packaging>=9.3; extra == "doc" +Requires-Dist: rst.linker>=1.9; extra == "doc" +Requires-Dist: furo; extra == "doc" +Requires-Dist: sphinx-lint; extra == "doc" +Requires-Dist: jaraco.tidelift>=1.4; extra == "doc" +Provides-Extra: check +Requires-Dist: pytest-checkdocs>=2.4; extra == "check" +Requires-Dist: pytest-ruff>=0.2.1; sys_platform != "cygwin" and extra == "check" +Provides-Extra: cover +Requires-Dist: pytest-cov; extra == "cover" +Provides-Extra: enabler +Requires-Dist: pytest-enabler>=2.2; extra == "enabler" +Provides-Extra: type +Requires-Dist: pytest-mypy; extra == "type" +Dynamic: license-file + +.. image:: https://img.shields.io/pypi/v/zipp.svg + :target: https://pypi.org/project/zipp + +.. image:: https://img.shields.io/pypi/pyversions/zipp.svg + +.. image:: https://github.com/jaraco/zipp/actions/workflows/main.yml/badge.svg + :target: https://github.com/jaraco/zipp/actions?query=workflow%3A%22tests%22 + :alt: tests + +.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json + :target: https://github.com/astral-sh/ruff + :alt: Ruff + +.. image:: https://readthedocs.org/projects/zipp/badge/?version=latest +.. :target: https://zipp.readthedocs.io/en/latest/?badge=latest + +.. image:: https://img.shields.io/badge/skeleton-2025-informational + :target: https://blog.jaraco.com/skeleton + +.. image:: https://tidelift.com/badges/package/pypi/zipp + :target: https://tidelift.com/subscription/pkg/pypi-zipp?utm_source=pypi-zipp&utm_medium=readme + + +A pathlib-compatible Zipfile object wrapper. Official backport of the standard library +`Path object `_. + + +Compatibility +============= + +New features are introduced in this third-party library and later merged +into CPython. The following table indicates which versions of this library +were contributed to different versions in the standard library: + +.. list-table:: + :header-rows: 1 + + * - zipp + - stdlib + * - 3.18 + - 3.13 + * - 3.16 + - 3.12 + * - 3.5 + - 3.11 + * - 3.2 + - 3.10 + * - 3.3 ?? + - 3.9 + * - 1.0 + - 3.8 + + +Usage +===== + +Use ``zipp.Path`` in place of ``zipfile.Path`` on any Python. + +For Enterprise +============== + +Available as part of the Tidelift Subscription. + +This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use. + +`Learn more `_. diff --git a/.venv/lib/python3.12/site-packages/zipp-3.23.0.dist-info/RECORD b/.venv/lib/python3.12/site-packages/zipp-3.23.0.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..167e234a530053b60d4bf6d51b12598723433a8c --- /dev/null +++ b/.venv/lib/python3.12/site-packages/zipp-3.23.0.dist-info/RECORD @@ -0,0 +1,14 @@ +zipp-3.23.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +zipp-3.23.0.dist-info/METADATA,sha256=vdZ9TRbPC_O4k-fRjNPS13StuC837Zhbx3cMYHIms1s,3563 +zipp-3.23.0.dist-info/RECORD,, +zipp-3.23.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +zipp-3.23.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91 +zipp-3.23.0.dist-info/licenses/LICENSE,sha256=WlfLTbheKi3YjCkGKJCK3VfjRRRJ4KmnH9-zh3b9dZ0,1076 +zipp-3.23.0.dist-info/top_level.txt,sha256=iAbdoSHfaGqBfVb2XuR9JqSQHCoOsOtG6y9C_LSpqFw,5 +zipp/__init__.py,sha256=ieXh9GIMdABjKRX_JUJtP9k5wdBLK4Mt5X4nszSkmYE,11976 +zipp/_functools.py,sha256=f6Kt9LxZ4TE-cY1lJVdXSId3memSXmH9IdgMbU-_x2k,575 +zipp/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +zipp/compat/overlay.py,sha256=oEIGAnbr8yGjuKTrVSO2ByewPui71uppbX18BLnYTKE,783 +zipp/compat/py310.py,sha256=S7i6N9mToEn3asNb2ILyjnzvITOXrATD_J4emjyBbDU,256 +zipp/compat/py313.py,sha256=RndvDNtuY7H2D9ecnnzcPBMZ8mZc42gmXD_IwQAXXAE,654 +zipp/glob.py,sha256=DLV9LBsDxA6YVW82e3-tkoNrus1h4R-j3BR6VqS0AzE,3382 diff --git a/.venv/lib/python3.12/site-packages/zipp-3.23.0.dist-info/REQUESTED b/.venv/lib/python3.12/site-packages/zipp-3.23.0.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.12/site-packages/zipp-3.23.0.dist-info/WHEEL b/.venv/lib/python3.12/site-packages/zipp-3.23.0.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..e7fa31b6f3f78deb1022c1f7927f07d4d16da822 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/zipp-3.23.0.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: setuptools (80.9.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/.venv/lib/python3.12/site-packages/zipp-3.23.0.dist-info/top_level.txt b/.venv/lib/python3.12/site-packages/zipp-3.23.0.dist-info/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..e82f676f82a3381fa909d1e6578c7a22044fafca --- /dev/null +++ b/.venv/lib/python3.12/site-packages/zipp-3.23.0.dist-info/top_level.txt @@ -0,0 +1 @@ +zipp diff --git a/.venv/lib/python3.12/site-packages/zipp/__init__.py b/.venv/lib/python3.12/site-packages/zipp/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ed5b21463295b8cee93d855316fb5e9000f9b8ec --- /dev/null +++ b/.venv/lib/python3.12/site-packages/zipp/__init__.py @@ -0,0 +1,456 @@ +""" +A Path-like interface for zipfiles. + +This codebase is shared between zipfile.Path in the stdlib +and zipp in PyPI. See +https://github.com/python/importlib_metadata/wiki/Development-Methodology +for more detail. +""" + +import functools +import io +import itertools +import pathlib +import posixpath +import re +import stat +import sys +import zipfile + +from ._functools import save_method_args +from .compat.py310 import text_encoding +from .glob import Translator + +__all__ = ['Path'] + + +def _parents(path): + """ + Given a path with elements separated by + posixpath.sep, generate all parents of that path. + + >>> list(_parents('b/d')) + ['b'] + >>> list(_parents('/b/d/')) + ['/b'] + >>> list(_parents('b/d/f/')) + ['b/d', 'b'] + >>> list(_parents('b')) + [] + >>> list(_parents('')) + [] + """ + return itertools.islice(_ancestry(path), 1, None) + + +def _ancestry(path): + """ + Given a path with elements separated by + posixpath.sep, generate all elements of that path. + + >>> list(_ancestry('b/d')) + ['b/d', 'b'] + >>> list(_ancestry('/b/d/')) + ['/b/d', '/b'] + >>> list(_ancestry('b/d/f/')) + ['b/d/f', 'b/d', 'b'] + >>> list(_ancestry('b')) + ['b'] + >>> list(_ancestry('')) + [] + + Multiple separators are treated like a single. + + >>> list(_ancestry('//b//d///f//')) + ['//b//d///f', '//b//d', '//b'] + """ + path = path.rstrip(posixpath.sep) + while path.rstrip(posixpath.sep): + yield path + path, tail = posixpath.split(path) + + +_dedupe = dict.fromkeys +"""Deduplicate an iterable in original order""" + + +def _difference(minuend, subtrahend): + """ + Return items in minuend not in subtrahend, retaining order + with O(1) lookup. + """ + return itertools.filterfalse(set(subtrahend).__contains__, minuend) + + +class InitializedState: + """ + Mix-in to save the initialization state for pickling. + """ + + @save_method_args + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def __getstate__(self): + return self._saved___init__.args, self._saved___init__.kwargs + + def __setstate__(self, state): + args, kwargs = state + super().__init__(*args, **kwargs) + + +class CompleteDirs(InitializedState, zipfile.ZipFile): + """ + A ZipFile subclass that ensures that implied directories + are always included in the namelist. + + >>> list(CompleteDirs._implied_dirs(['foo/bar.txt', 'foo/bar/baz.txt'])) + ['foo/', 'foo/bar/'] + >>> list(CompleteDirs._implied_dirs(['foo/bar.txt', 'foo/bar/baz.txt', 'foo/bar/'])) + ['foo/'] + """ + + @staticmethod + def _implied_dirs(names): + parents = itertools.chain.from_iterable(map(_parents, names)) + as_dirs = (p + posixpath.sep for p in parents) + return _dedupe(_difference(as_dirs, names)) + + def namelist(self): + names = super().namelist() + return names + list(self._implied_dirs(names)) + + def _name_set(self): + return set(self.namelist()) + + def resolve_dir(self, name): + """ + If the name represents a directory, return that name + as a directory (with the trailing slash). + """ + names = self._name_set() + dirname = name + '/' + dir_match = name not in names and dirname in names + return dirname if dir_match else name + + def getinfo(self, name): + """ + Supplement getinfo for implied dirs. + """ + try: + return super().getinfo(name) + except KeyError: + if not name.endswith('/') or name not in self._name_set(): + raise + return zipfile.ZipInfo(filename=name) + + @classmethod + def make(cls, source): + """ + Given a source (filename or zipfile), return an + appropriate CompleteDirs subclass. + """ + if isinstance(source, CompleteDirs): + return source + + if not isinstance(source, zipfile.ZipFile): + return cls(source) + + # Only allow for FastLookup when supplied zipfile is read-only + if 'r' not in source.mode: + cls = CompleteDirs + + source.__class__ = cls + return source + + @classmethod + def inject(cls, zf: zipfile.ZipFile) -> zipfile.ZipFile: + """ + Given a writable zip file zf, inject directory entries for + any directories implied by the presence of children. + """ + for name in cls._implied_dirs(zf.namelist()): + zf.writestr(name, b"") + return zf + + +class FastLookup(CompleteDirs): + """ + ZipFile subclass to ensure implicit + dirs exist and are resolved rapidly. + """ + + def namelist(self): + return self._namelist + + @functools.cached_property + def _namelist(self): + return super().namelist() + + def _name_set(self): + return self._name_set_prop + + @functools.cached_property + def _name_set_prop(self): + return super()._name_set() + + +def _extract_text_encoding(encoding=None, *args, **kwargs): + # compute stack level so that the caller of the caller sees any warning. + is_pypy = sys.implementation.name == 'pypy' + # PyPy no longer special cased after 7.3.19 (or maybe 7.3.18) + # See jaraco/zipp#143 + is_old_pypi = is_pypy and sys.pypy_version_info < (7, 3, 19) + stack_level = 3 + is_old_pypi + return text_encoding(encoding, stack_level), args, kwargs + + +class Path: + """ + A :class:`importlib.resources.abc.Traversable` interface for zip files. + + Implements many of the features users enjoy from + :class:`pathlib.Path`. + + Consider a zip file with this structure:: + + . + ├── a.txt + └── b + ├── c.txt + └── d + └── e.txt + + >>> data = io.BytesIO() + >>> zf = zipfile.ZipFile(data, 'w') + >>> zf.writestr('a.txt', 'content of a') + >>> zf.writestr('b/c.txt', 'content of c') + >>> zf.writestr('b/d/e.txt', 'content of e') + >>> zf.filename = 'mem/abcde.zip' + + Path accepts the zipfile object itself or a filename + + >>> path = Path(zf) + + From there, several path operations are available. + + Directory iteration (including the zip file itself): + + >>> a, b = path.iterdir() + >>> a + Path('mem/abcde.zip', 'a.txt') + >>> b + Path('mem/abcde.zip', 'b/') + + name property: + + >>> b.name + 'b' + + join with divide operator: + + >>> c = b / 'c.txt' + >>> c + Path('mem/abcde.zip', 'b/c.txt') + >>> c.name + 'c.txt' + + Read text: + + >>> c.read_text(encoding='utf-8') + 'content of c' + + existence: + + >>> c.exists() + True + >>> (b / 'missing.txt').exists() + False + + Coercion to string: + + >>> import os + >>> str(c).replace(os.sep, posixpath.sep) + 'mem/abcde.zip/b/c.txt' + + At the root, ``name``, ``filename``, and ``parent`` + resolve to the zipfile. + + >>> str(path) + 'mem/abcde.zip/' + >>> path.name + 'abcde.zip' + >>> path.filename == pathlib.Path('mem/abcde.zip') + True + >>> str(path.parent) + 'mem' + + If the zipfile has no filename, such attributes are not + valid and accessing them will raise an Exception. + + >>> zf.filename = None + >>> path.name + Traceback (most recent call last): + ... + TypeError: ... + + >>> path.filename + Traceback (most recent call last): + ... + TypeError: ... + + >>> path.parent + Traceback (most recent call last): + ... + TypeError: ... + + # workaround python/cpython#106763 + >>> pass + """ + + __repr = "{self.__class__.__name__}({self.root.filename!r}, {self.at!r})" + + def __init__(self, root, at=""): + """ + Construct a Path from a ZipFile or filename. + + Note: When the source is an existing ZipFile object, + its type (__class__) will be mutated to a + specialized type. If the caller wishes to retain the + original type, the caller should either create a + separate ZipFile object or pass a filename. + """ + self.root = FastLookup.make(root) + self.at = at + + def __eq__(self, other): + """ + >>> Path(zipfile.ZipFile(io.BytesIO(), 'w')) == 'foo' + False + """ + if self.__class__ is not other.__class__: + return NotImplemented + return (self.root, self.at) == (other.root, other.at) + + def __hash__(self): + return hash((self.root, self.at)) + + def open(self, mode='r', *args, pwd=None, **kwargs): + """ + Open this entry as text or binary following the semantics + of ``pathlib.Path.open()`` by passing arguments through + to io.TextIOWrapper(). + """ + if self.is_dir(): + raise IsADirectoryError(self) + zip_mode = mode[0] + if zip_mode == 'r' and not self.exists(): + raise FileNotFoundError(self) + stream = self.root.open(self.at, zip_mode, pwd=pwd) + if 'b' in mode: + if args or kwargs: + raise ValueError("encoding args invalid for binary operation") + return stream + # Text mode: + encoding, args, kwargs = _extract_text_encoding(*args, **kwargs) + return io.TextIOWrapper(stream, encoding, *args, **kwargs) + + def _base(self): + return pathlib.PurePosixPath(self.at) if self.at else self.filename + + @property + def name(self): + return self._base().name + + @property + def suffix(self): + return self._base().suffix + + @property + def suffixes(self): + return self._base().suffixes + + @property + def stem(self): + return self._base().stem + + @property + def filename(self): + return pathlib.Path(self.root.filename).joinpath(self.at) + + def read_text(self, *args, **kwargs): + encoding, args, kwargs = _extract_text_encoding(*args, **kwargs) + with self.open('r', encoding, *args, **kwargs) as strm: + return strm.read() + + def read_bytes(self): + with self.open('rb') as strm: + return strm.read() + + def _is_child(self, path): + return posixpath.dirname(path.at.rstrip("/")) == self.at.rstrip("/") + + def _next(self, at): + return self.__class__(self.root, at) + + def is_dir(self): + return not self.at or self.at.endswith("/") + + def is_file(self): + return self.exists() and not self.is_dir() + + def exists(self): + return self.at in self.root._name_set() + + def iterdir(self): + if not self.is_dir(): + raise ValueError("Can't listdir a file") + subs = map(self._next, self.root.namelist()) + return filter(self._is_child, subs) + + def match(self, path_pattern): + return pathlib.PurePosixPath(self.at).match(path_pattern) + + def is_symlink(self): + """ + Return whether this path is a symlink. + """ + info = self.root.getinfo(self.at) + mode = info.external_attr >> 16 + return stat.S_ISLNK(mode) + + def glob(self, pattern): + if not pattern: + raise ValueError(f"Unacceptable pattern: {pattern!r}") + + prefix = re.escape(self.at) + tr = Translator(seps='/') + matches = re.compile(prefix + tr.translate(pattern)).fullmatch + return map(self._next, filter(matches, self.root.namelist())) + + def rglob(self, pattern): + return self.glob(f'**/{pattern}') + + def relative_to(self, other, *extra): + return posixpath.relpath(str(self), str(other.joinpath(*extra))) + + def __str__(self): + return posixpath.join(self.root.filename, self.at) + + def __repr__(self): + return self.__repr.format(self=self) + + def joinpath(self, *other): + next = posixpath.join(self.at, *other) + return self._next(self.root.resolve_dir(next)) + + __truediv__ = joinpath + + @property + def parent(self): + if not self.at: + return self.filename.parent + parent_at = posixpath.dirname(self.at.rstrip('/')) + if parent_at: + parent_at += '/' + return self._next(parent_at) diff --git a/.venv/lib/python3.12/site-packages/zipp/_functools.py b/.venv/lib/python3.12/site-packages/zipp/_functools.py new file mode 100644 index 0000000000000000000000000000000000000000..7390be21873e4ba439bde0553e4c0dcde8eb7d74 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/zipp/_functools.py @@ -0,0 +1,20 @@ +import collections +import functools + + +# from jaraco.functools 4.0.2 +def save_method_args(method): + """ + Wrap a method such that when it is called, the args and kwargs are + saved on the method. + """ + args_and_kwargs = collections.namedtuple('args_and_kwargs', 'args kwargs') # noqa: PYI024 + + @functools.wraps(method) + def wrapper(self, /, *args, **kwargs): + attr_name = '_saved_' + method.__name__ + attr = args_and_kwargs(args, kwargs) + setattr(self, attr_name, attr) + return method(self, *args, **kwargs) + + return wrapper diff --git a/.venv/lib/python3.12/site-packages/zipp/glob.py b/.venv/lib/python3.12/site-packages/zipp/glob.py new file mode 100644 index 0000000000000000000000000000000000000000..1b4ffb33187b65b4378925c472071c845bcecc26 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/zipp/glob.py @@ -0,0 +1,116 @@ +import os +import re + +from .compat.py313 import legacy_end_marker + +_default_seps = os.sep + str(os.altsep) * bool(os.altsep) + + +class Translator: + """ + >>> Translator('xyz') + Traceback (most recent call last): + ... + AssertionError: Invalid separators + + >>> Translator('') + Traceback (most recent call last): + ... + AssertionError: Invalid separators + """ + + seps: str + + def __init__(self, seps: str = _default_seps): + assert seps and set(seps) <= set(_default_seps), "Invalid separators" + self.seps = seps + + def translate(self, pattern): + """ + Given a glob pattern, produce a regex that matches it. + """ + return self.extend(self.match_dirs(self.translate_core(pattern))) + + @legacy_end_marker + def extend(self, pattern): + r""" + Extend regex for pattern-wide concerns. + + Apply '(?s:)' to create a non-matching group that + matches newlines (valid on Unix). + + Append '\z' to imply fullmatch even when match is used. + """ + return rf'(?s:{pattern})\z' + + def match_dirs(self, pattern): + """ + Ensure that zipfile.Path directory names are matched. + + zipfile.Path directory names always end in a slash. + """ + return rf'{pattern}[/]?' + + def translate_core(self, pattern): + r""" + Given a glob pattern, produce a regex that matches it. + + >>> t = Translator() + >>> t.translate_core('*.txt').replace('\\\\', '') + '[^/]*\\.txt' + >>> t.translate_core('a?txt') + 'a[^/]txt' + >>> t.translate_core('**/*').replace('\\\\', '') + '.*/[^/][^/]*' + """ + self.restrict_rglob(pattern) + return ''.join(map(self.replace, separate(self.star_not_empty(pattern)))) + + def replace(self, match): + """ + Perform the replacements for a match from :func:`separate`. + """ + return match.group('set') or ( + re.escape(match.group(0)) + .replace('\\*\\*', r'.*') + .replace('\\*', rf'[^{re.escape(self.seps)}]*') + .replace('\\?', r'[^/]') + ) + + def restrict_rglob(self, pattern): + """ + Raise ValueError if ** appears in anything but a full path segment. + + >>> Translator().translate('**foo') + Traceback (most recent call last): + ... + ValueError: ** must appear alone in a path segment + """ + seps_pattern = rf'[{re.escape(self.seps)}]+' + segments = re.split(seps_pattern, pattern) + if any('**' in segment and segment != '**' for segment in segments): + raise ValueError("** must appear alone in a path segment") + + def star_not_empty(self, pattern): + """ + Ensure that * will not match an empty segment. + """ + + def handle_segment(match): + segment = match.group(0) + return '?*' if segment == '*' else segment + + not_seps_pattern = rf'[^{re.escape(self.seps)}]+' + return re.sub(not_seps_pattern, handle_segment, pattern) + + +def separate(pattern): + """ + Separate out character sets to avoid translating their contents. + + >>> [m.group(0) for m in separate('*.txt')] + ['*.txt'] + >>> [m.group(0) for m in separate('a[?]txt')] + ['a', '[?]', 'txt'] + """ + return re.finditer(r'([^\[]+)|(?P[\[].*?[\]])|([\[][^\]]*$)', pattern) diff --git a/.venv/share/man/man1/isympy.1 b/.venv/share/man/man1/isympy.1 new file mode 100644 index 0000000000000000000000000000000000000000..0ff966158a28c5ad1a6cd954e454842b25fdd999 --- /dev/null +++ b/.venv/share/man/man1/isympy.1 @@ -0,0 +1,188 @@ +'\" -*- coding: us-ascii -*- +.if \n(.g .ds T< \\FC +.if \n(.g .ds T> \\F[\n[.fam]] +.de URL +\\$2 \(la\\$1\(ra\\$3 +.. +.if \n(.g .mso www.tmac +.TH isympy 1 2007-10-8 "" "" +.SH NAME +isympy \- interactive shell for SymPy +.SH SYNOPSIS +'nh +.fi +.ad l +\fBisympy\fR \kx +.if (\nx>(\n(.l/2)) .nr x (\n(.l/5) +'in \n(.iu+\nxu +[\fB-c\fR | \fB--console\fR] [\fB-p\fR ENCODING | \fB--pretty\fR ENCODING] [\fB-t\fR TYPE | \fB--types\fR TYPE] [\fB-o\fR ORDER | \fB--order\fR ORDER] [\fB-q\fR | \fB--quiet\fR] [\fB-d\fR | \fB--doctest\fR] [\fB-C\fR | \fB--no-cache\fR] [\fB-a\fR | \fB--auto\fR] [\fB-D\fR | \fB--debug\fR] [ +-- | PYTHONOPTIONS] +'in \n(.iu-\nxu +.ad b +'hy +'nh +.fi +.ad l +\fBisympy\fR \kx +.if (\nx>(\n(.l/2)) .nr x (\n(.l/5) +'in \n(.iu+\nxu +[ +{\fB-h\fR | \fB--help\fR} +| +{\fB-v\fR | \fB--version\fR} +] +'in \n(.iu-\nxu +.ad b +'hy +.SH DESCRIPTION +isympy is a Python shell for SymPy. It is just a normal python shell +(ipython shell if you have the ipython package installed) that executes +the following commands so that you don't have to: +.PP +.nf +\*(T< +>>> from __future__ import division +>>> from sympy import * +>>> x, y, z = symbols("x,y,z") +>>> k, m, n = symbols("k,m,n", integer=True) + \*(T> +.fi +.PP +So starting isympy is equivalent to starting python (or ipython) and +executing the above commands by hand. It is intended for easy and quick +experimentation with SymPy. For more complicated programs, it is recommended +to write a script and import things explicitly (using the "from sympy +import sin, log, Symbol, ..." idiom). +.SH OPTIONS +.TP +\*(T<\fB\-c \fR\*(T>\fISHELL\fR, \*(T<\fB\-\-console=\fR\*(T>\fISHELL\fR +Use the specified shell (python or ipython) as +console backend instead of the default one (ipython +if present or python otherwise). + +Example: isympy -c python + +\fISHELL\fR could be either +\&'ipython' or 'python' +.TP +\*(T<\fB\-p \fR\*(T>\fIENCODING\fR, \*(T<\fB\-\-pretty=\fR\*(T>\fIENCODING\fR +Setup pretty printing in SymPy. By default, the most pretty, unicode +printing is enabled (if the terminal supports it). You can use less +pretty ASCII printing instead or no pretty printing at all. + +Example: isympy -p no + +\fIENCODING\fR must be one of 'unicode', +\&'ascii' or 'no'. +.TP +\*(T<\fB\-t \fR\*(T>\fITYPE\fR, \*(T<\fB\-\-types=\fR\*(T>\fITYPE\fR +Setup the ground types for the polys. By default, gmpy ground types +are used if gmpy2 or gmpy is installed, otherwise it falls back to python +ground types, which are a little bit slower. You can manually +choose python ground types even if gmpy is installed (e.g., for testing purposes). + +Note that sympy ground types are not supported, and should be used +only for experimental purposes. + +Note that the gmpy1 ground type is primarily intended for testing; it the +use of gmpy even if gmpy2 is available. + +This is the same as setting the environment variable +SYMPY_GROUND_TYPES to the given ground type (e.g., +SYMPY_GROUND_TYPES='gmpy') + +The ground types can be determined interactively from the variable +sympy.polys.domains.GROUND_TYPES inside the isympy shell itself. + +Example: isympy -t python + +\fITYPE\fR must be one of 'gmpy', +\&'gmpy1' or 'python'. +.TP +\*(T<\fB\-o \fR\*(T>\fIORDER\fR, \*(T<\fB\-\-order=\fR\*(T>\fIORDER\fR +Setup the ordering of terms for printing. The default is lex, which +orders terms lexicographically (e.g., x**2 + x + 1). You can choose +other orderings, such as rev-lex, which will use reverse +lexicographic ordering (e.g., 1 + x + x**2). + +Note that for very large expressions, ORDER='none' may speed up +printing considerably, with the tradeoff that the order of the terms +in the printed expression will have no canonical order + +Example: isympy -o rev-lax + +\fIORDER\fR must be one of 'lex', 'rev-lex', 'grlex', +\&'rev-grlex', 'grevlex', 'rev-grevlex', 'old', or 'none'. +.TP +\*(T<\fB\-q\fR\*(T>, \*(T<\fB\-\-quiet\fR\*(T> +Print only Python's and SymPy's versions to stdout at startup, and nothing else. +.TP +\*(T<\fB\-d\fR\*(T>, \*(T<\fB\-\-doctest\fR\*(T> +Use the same format that should be used for doctests. This is +equivalent to '\fIisympy -c python -p no\fR'. +.TP +\*(T<\fB\-C\fR\*(T>, \*(T<\fB\-\-no\-cache\fR\*(T> +Disable the caching mechanism. Disabling the cache may slow certain +operations down considerably. This is useful for testing the cache, +or for benchmarking, as the cache can result in deceptive benchmark timings. + +This is the same as setting the environment variable SYMPY_USE_CACHE +to 'no'. +.TP +\*(T<\fB\-a\fR\*(T>, \*(T<\fB\-\-auto\fR\*(T> +Automatically create missing symbols. Normally, typing a name of a +Symbol that has not been instantiated first would raise NameError, +but with this option enabled, any undefined name will be +automatically created as a Symbol. This only works in IPython 0.11. + +Note that this is intended only for interactive, calculator style +usage. In a script that uses SymPy, Symbols should be instantiated +at the top, so that it's clear what they are. + +This will not override any names that are already defined, which +includes the single character letters represented by the mnemonic +QCOSINE (see the "Gotchas and Pitfalls" document in the +documentation). You can delete existing names by executing "del +name" in the shell itself. You can see if a name is defined by typing +"'name' in globals()". + +The Symbols that are created using this have default assumptions. +If you want to place assumptions on symbols, you should create them +using symbols() or var(). + +Finally, this only works in the top level namespace. So, for +example, if you define a function in isympy with an undefined +Symbol, it will not work. +.TP +\*(T<\fB\-D\fR\*(T>, \*(T<\fB\-\-debug\fR\*(T> +Enable debugging output. This is the same as setting the +environment variable SYMPY_DEBUG to 'True'. The debug status is set +in the variable SYMPY_DEBUG within isympy. +.TP +-- \fIPYTHONOPTIONS\fR +These options will be passed on to \fIipython (1)\fR shell. +Only supported when ipython is being used (standard python shell not supported). + +Two dashes (--) are required to separate \fIPYTHONOPTIONS\fR +from the other isympy options. + +For example, to run iSymPy without startup banner and colors: + +isympy -q -c ipython -- --colors=NoColor +.TP +\*(T<\fB\-h\fR\*(T>, \*(T<\fB\-\-help\fR\*(T> +Print help output and exit. +.TP +\*(T<\fB\-v\fR\*(T>, \*(T<\fB\-\-version\fR\*(T> +Print isympy version information and exit. +.SH FILES +.TP +\*(T<\fI${HOME}/.sympy\-history\fR\*(T> +Saves the history of commands when using the python +shell as backend. +.SH BUGS +The upstreams BTS can be found at \(lahttps://github.com/sympy/sympy/issues\(ra +Please report all bugs that you find in there, this will help improve +the overall quality of SymPy. +.SH "SEE ALSO" +\fBipython\fR(1), \fBpython\fR(1) diff --git a/data_tool/caption_image/README.md b/data_tool/caption_image/README.md new file mode 100644 index 0000000000000000000000000000000000000000..7f6e42c695654e7fc503911c15999a9387ee1c15 --- /dev/null +++ b/data_tool/caption_image/README.md @@ -0,0 +1,122 @@ +# Natural Language Caption Generation + +这个工具使用 Qwen-VL 模型为生成的图像创建自然语言描述,并将结果保存到metadata文件中。 + +## 功能特点 + +- 使用 Qwen2.5-VL-7B-Instruct 模型分析图像 +- 结合原始的positive和negative prompts生成更准确的描述 +- 批量处理metadata文件 +- 内存管理和错误处理 +- 可配置的参数设置 + +## 文件结构 + +``` +caption_image/ +├── generate_natural_captions.py # 主脚本 +├── test_caption_generation.py # 测试脚本 +├── config.json # 配置文件 +└── README.md # 说明文档 +``` + +## 使用方法 + +### 1. 批量处理所有metadata文件 + +```bash +# 使用默认设置 +python data_tool/caption_image/generate_natural_captions.py + +# 使用自定义参数 +python data_tool/caption_image/generate_natural_captions.py \ + --metadata_dir illustrious_generated/metadata \ + --images_dir illustrious_generated \ + --model_name models/Qwen2.5-VL-7B-Instruct \ + --batch_size 10 +``` + +### 2. 测试单个图像 + +```bash +python data_tool/caption_image/test_caption_generation.py +``` + +### 3. 参数说明 + +- `--metadata_dir`: metadata JSON文件目录(默认:illustrious_generated/metadata) +- `--images_dir`: 图像文件目录(默认:illustrious_generated) +- `--model_name`: Qwen-VL模型路径(默认:models/Qwen2.5-VL-7B-Instruct) +- `--no_skip_existing`: 处理所有文件,包括已有caption的文件 +- `--batch_size`: 批处理大小,用于内存管理(默认:10) + +## 输出格式 + +脚本会在每个metadata文件中添加 `natural_caption_data` 字段: + +```json +{ + "filename_hash": "00a7174aa78f", + "original_prompt_data": { + "positive_prompt": "...", + "negative_prompt": "...", + "multi_character_focus": false, + "generation_attempt": 10057, + "sample_sources": [...] + }, + "generation_parameters": {...}, + "model_info": {...}, + "natural_caption_data": { + "natural_caption": "A dynamic space knight in glowing blue and pink armor soars through a neon-lit alien city at night...", + "generation_timestamp": 1753764908.409203, + "model_used": "models/Qwen2.5-VL-7B-Instruct", + "source_prompts": { + "positive_prompt": "...", + "negative_prompt": "...", + "multi_character_focus": false, + "generation_attempt": 10057, + "sample_sources": [...] + } + } +} +``` + +## 依赖要求 + +- torch +- transformers +- qwen_vl_utils +- PIL (Pillow) +- 足够的GPU内存来运行Qwen2.5-VL-7B模型 + +## 注意事项 + +1. **内存管理**: 脚本包含内存清理机制,每处理一定数量的文件后会清理GPU内存 +2. **错误处理**: 如果某个文件处理失败,脚本会继续处理其他文件 +3. **跳过已处理**: 默认跳过已有natural_caption_data的文件,可使用`--no_skip_existing`强制重新处理 +4. **日志记录**: 处理过程会记录到`natural_caption_generation.log`文件 + +## 示例输出 + +``` +2024-01-29 20:15:30,123 - INFO - Starting natural language caption generation... +2024-01-29 20:15:30,124 - INFO - Loading Qwen-VL model from models/Qwen2.5-VL-7B-Instruct... +2024-01-29 20:15:45,567 - INFO - Qwen-VL model loaded successfully +2024-01-29 20:15:45,568 - INFO - Found 1000 metadata files +2024-01-29 20:15:45,569 - INFO - Processing 00a7174aa78f (1/1000) +2024-01-29 20:15:48,234 - INFO - Generated caption for illustrious_generated/00a7174aa78f.png: A dynamic space knight... +2024-01-29 20:15:48,345 - INFO - Successfully processed 00a7174aa78f +``` + +## 故障排除 + +1. **模型加载失败**: 确保模型路径正确,或者检查网络连接以从Hugging Face下载 +2. **GPU内存不足**: 减少batch_size参数 +3. **图像文件未找到**: 检查images_dir路径是否正确 +4. **权限错误**: 确保对metadata文件有写入权限 + +## 性能优化 + +- 使用较大的batch_size可以提高效率,但需要更多GPU内存 +- 如果GPU内存充足,可以考虑使用flash_attention_2加速 +- 对于大量文件,建议分批次处理 diff --git a/data_tool/caption_image/config.json b/data_tool/caption_image/config.json new file mode 100644 index 0000000000000000000000000000000000000000..152cd492f4319a331bb14232139c9f96db0f8980 --- /dev/null +++ b/data_tool/caption_image/config.json @@ -0,0 +1,9 @@ +{ + "model_name": "models/Qwen2.5-VL-7B-Instruct", + "metadata_dir": "illustrious_generated/metadata", + "images_dir": "illustrious_generated", + "batch_size": 10, + "skip_existing": true, + "max_new_tokens": 256, + "temperature": 0.7 +} diff --git a/data_tool/caption_image/generate_natural_captions.py b/data_tool/caption_image/generate_natural_captions.py new file mode 100644 index 0000000000000000000000000000000000000000..c97291f2394c3b1034b950ec737dee9ef9398c1c --- /dev/null +++ b/data_tool/caption_image/generate_natural_captions.py @@ -0,0 +1,336 @@ +#!/usr/bin/env python3 +""" +Natural Language Caption Generation Script +使用 Qwen-VL 对生成的图像进行自然语言描述,并更新metadata +""" + +import os +import json +import gc +import time +from pathlib import Path +from typing import List, Dict, Any, Optional +from PIL import Image +import logging + +# Configure logging first +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s', + handlers=[ + logging.FileHandler('natural_caption_generation.log'), + logging.StreamHandler() + ] +) +logger = logging.getLogger(__name__) + +import torch +try: + from transformers import Qwen2_5_VLForConditionalGeneration, AutoTokenizer, AutoProcessor +except ImportError: + # 兼容不同的transformers版本 + try: + from transformers import Qwen2_5_VLForConditionalGeneration as Qwen2VLForConditionalGeneration + from transformers import AutoTokenizer, AutoProcessor + except ImportError: + logger.error("Failed to import Qwen2VL models. Please check your transformers version.") + raise + +try: + from qwen_vl_utils import process_vision_info +except ImportError: + logger.error("qwen_vl_utils not found. Please install qwen-vl-utils package.") + raise + +class NaturalCaptionGenerator: + """使用 Qwen-VL 生成自然语言图像描述""" + + def __init__(self, model_name: str = "models/Qwen2.5-VL-7B-Instruct"): + """ + 初始化Qwen-VL模型 + + Args: + model_name: Qwen-VL模型路径 + """ + self.model_name = model_name + self.model = None + self.processor = None + self.load_model() + + def load_model(self): + """加载Qwen-VL模型""" + logger.info(f"Loading Qwen-VL model from {self.model_name}...") + try: + self.model = Qwen2_5_VLForConditionalGeneration.from_pretrained( + self.model_name, + torch_dtype=torch.bfloat16, + device_map="auto", + ) + self.processor = AutoProcessor.from_pretrained(self.model_name) + logger.info("Qwen-VL model loaded successfully") + except Exception as e: + logger.error(f"Failed to load Qwen-VL model: {e}") + raise + + def generate_caption(self, image_path: str, positive_prompt: str, negative_prompt: str) -> str: + """ + 根据图像和prompt生成自然语言描述 + + Args: + image_path: 图像文件路径 + positive_prompt: 正面提示词 + negative_prompt: 负面提示词 + + Returns: + 自然语言描述文本 + """ + prompt = f"""You are an expert art critic and image analyst. Please analyze this AI-generated image and provide a natural language description. + +The image was generated using these prompts: +Positive prompt: {positive_prompt} +Negative prompt: {negative_prompt} + +Please provide a natural, flowing description of what you see in the image. Focus on: +1. The main subject(s) and their appearance +2. The setting/background +3. The artistic style and visual quality +4. The mood and atmosphere +5. Notable details and elements + +Write your description as if you're describing the image to someone who cannot see it. Be descriptive but concise, using natural language rather than technical tags or keywords. Aim for 2-3 sentences that capture the essence of the image. + +Example format: "A [description of main subject] [action/pose] in [setting]. The image features [notable visual elements] with [artistic style description]. The overall atmosphere is [mood description]." + +Please respond with only the natural description, no additional formatting or explanations.""" + + try: + messages = [ + { + "role": "user", + "content": [ + {"type": "image", "image": image_path}, + {"type": "text", "text": prompt} + ], + } + ] + + text = self.processor.apply_chat_template( + messages, tokenize=False, add_generation_prompt=True + ) + image_inputs, video_inputs = process_vision_info(messages) + inputs = self.processor( + text=[text], + images=image_inputs, + videos=video_inputs, + padding=True, + return_tensors="pt", + ) + inputs = inputs.to("cuda") + + with torch.no_grad(): + generated_ids = self.model.generate(**inputs, max_new_tokens=256, temperature=0.7) + generated_ids_trimmed = [ + out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids) + ] + response = self.processor.batch_decode( + generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False + )[0] + + # 清理响应文本 + caption = response.strip() + # 移除可能的引号 + if caption.startswith('"') and caption.endswith('"'): + caption = caption[1:-1] + + logger.info(f"Generated caption for {image_path}: {caption}") + return caption + + except Exception as e: + logger.error(f"Error generating caption for {image_path}: {e}") + return f"Failed to generate caption: {str(e)}" + + def clear_memory(self): + """清理GPU内存""" + if self.model is not None: + del self.model + self.model = None + if self.processor is not None: + del self.processor + self.processor = None + torch.cuda.empty_cache() + gc.collect() + +def process_metadata_files( + metadata_dir: str = "illustrious_generated/metadata", + images_dir: str = "illustrious_generated", + model_name: str = "models/Qwen2.5-VL-7B-Instruct", + skip_existing: bool = True, + batch_size: int = 10 +): + """ + 处理所有metadata文件,生成自然语言描述 + + Args: + metadata_dir: metadata文件目录 + images_dir: 图像文件目录 + model_name: Qwen-VL模型路径 + skip_existing: 是否跳过已有natural_caption_data的文件 + batch_size: 每批处理的文件数量,用于内存管理 + """ + metadata_path = Path(metadata_dir) + images_path = Path(images_dir) + + if not metadata_path.exists(): + logger.error(f"Metadata directory not found: {metadata_path}") + return + + if not images_path.exists(): + logger.error(f"Images directory not found: {images_path}") + return + + # 获取所有metadata文件 + metadata_files = list(metadata_path.glob("*.json")) + logger.info(f"Found {len(metadata_files)} metadata files") + + if not metadata_files: + logger.warning("No metadata files found") + return + + # 初始化caption生成器 + caption_generator = NaturalCaptionGenerator(model_name) + + processed_count = 0 + skipped_count = 0 + error_count = 0 + + try: + for i, metadata_file in enumerate(metadata_files): + try: + # 读取metadata + with open(metadata_file, 'r', encoding='utf-8') as f: + metadata = json.load(f) + + # 检查是否已经有natural_caption_data + if skip_existing and 'natural_caption_data' in metadata: + skipped_count += 1 + logger.debug(f"Skipping {metadata_file.name} - already has natural_caption_data") + continue + + # 构建图像路径 + filename_hash = metadata.get('filename_hash') + if not filename_hash: + logger.warning(f"No filename_hash in {metadata_file.name}") + error_count += 1 + continue + + image_path = images_path / f"{filename_hash}.png" + if not image_path.exists(): + logger.warning(f"Image file not found: {image_path}") + error_count += 1 + continue + + # 获取prompts + original_prompt_data = metadata.get('original_prompt_data', {}) + positive_prompt = original_prompt_data.get('positive_prompt', '') + negative_prompt = original_prompt_data.get('negative_prompt', '') + + if not positive_prompt: + logger.warning(f"No positive_prompt in {metadata_file.name}") + error_count += 1 + continue + + # 生成自然语言描述 + logger.info(f"Processing {filename_hash} ({i+1}/{len(metadata_files)})") + natural_caption = caption_generator.generate_caption( + str(image_path), positive_prompt, negative_prompt + ) + + # 添加natural_caption_data到metadata + metadata['natural_caption_data'] = { + 'natural_caption': natural_caption, + 'generation_timestamp': time.time(), + 'model_used': model_name, + 'source_prompts': { + 'positive_prompt': positive_prompt, + 'negative_prompt': negative_prompt, + 'multi_character_focus': original_prompt_data.get('multi_character_focus', False), + 'generation_attempt': original_prompt_data.get('generation_attempt', 0), + 'sample_sources': original_prompt_data.get('sample_sources', []) + } + } + + # 保存更新后的metadata + with open(metadata_file, 'w', encoding='utf-8') as f: + json.dump(metadata, f, indent=2, ensure_ascii=False) + + processed_count += 1 + logger.info(f"Successfully processed {filename_hash}") + + # 每处理batch_size个文件后清理一次内存 + if (i + 1) % batch_size == 0: + logger.info(f"Processed {i + 1} files, clearing memory...") + torch.cuda.empty_cache() + gc.collect() + time.sleep(1) # 短暂休息 + + except Exception as e: + logger.error(f"Error processing {metadata_file.name}: {e}") + error_count += 1 + continue + + finally: + # 清理资源 + caption_generator.clear_memory() + logger.info(f"Processing completed. Processed: {processed_count}, Skipped: {skipped_count}, Errors: {error_count}") + +def main(): + """主函数""" + import argparse + + parser = argparse.ArgumentParser(description="Generate natural language captions for images using Qwen-VL") + parser.add_argument("--metadata_dir", default="illustrious_generated/metadata", + help="Directory containing metadata JSON files") + parser.add_argument("--images_dir", default="illustrious_generated", + help="Directory containing image files") + parser.add_argument("--model_name", default="models/Qwen2.5-VL-7B-Instruct", + help="Path to Qwen-VL model") + parser.add_argument("--no_skip_existing", action="store_true", + help="Process all files, even those with existing captions") + parser.add_argument("--batch_size", type=int, default=10, + help="Number of files to process before clearing memory") + + args = parser.parse_args() + + # 确保路径存在 + if not os.path.exists(args.metadata_dir): + logger.error(f"Metadata directory does not exist: {args.metadata_dir}") + return + + if not os.path.exists(args.images_dir): + logger.error(f"Images directory does not exist: {args.images_dir}") + return + + # 检查模型路径 + if not os.path.exists(args.model_name): + logger.warning(f"Model path does not exist locally: {args.model_name}") + logger.info("Will try to download from Hugging Face Hub") + + logger.info("Starting natural language caption generation...") + logger.info(f"Metadata directory: {args.metadata_dir}") + logger.info(f"Images directory: {args.images_dir}") + logger.info(f"Model: {args.model_name}") + logger.info(f"Skip existing: {not args.no_skip_existing}") + logger.info(f"Batch size: {args.batch_size}") + + process_metadata_files( + metadata_dir=args.metadata_dir, + images_dir=args.images_dir, + model_name=args.model_name, + skip_existing=not args.no_skip_existing, + batch_size=args.batch_size + ) + + logger.info("Caption generation completed!") + +if __name__ == "__main__": + main() diff --git a/data_tool/caption_image/run_caption_generation.sh b/data_tool/caption_image/run_caption_generation.sh new file mode 100644 index 0000000000000000000000000000000000000000..2dd46253365d2c851cc51435b5f021603191d4b5 --- /dev/null +++ b/data_tool/caption_image/run_caption_generation.sh @@ -0,0 +1,131 @@ +#!/bin/bash + +# Natural Language Caption Generation Launcher +# 自然语言图像描述生成启动脚本 + +echo "=== Qwen-VL Natural Language Caption Generation ===" +echo "Starting caption generation for illustrious_generated images..." +echo "" + +# 检查当前目录 +if [ ! -d "illustrious_generated" ]; then + echo "Error: illustrious_generated directory not found!" + echo "Please run this script from the project root directory." + exit 1 +fi + +if [ ! -d "illustrious_generated/metadata" ]; then + echo "Error: illustrious_generated/metadata directory not found!" + exit 1 +fi + +# 设置默认参数 +METADATA_DIR="illustrious_generated/metadata" +IMAGES_DIR="illustrious_generated" +MODEL_NAME="models/Qwen2.5-VL-7B-Instruct" +BATCH_SIZE=5 +SKIP_EXISTING=true + +# 解析命令行参数 +while [[ $# -gt 0 ]]; do + case $1 in + --metadata_dir) + METADATA_DIR="$2" + shift 2 + ;; + --images_dir) + IMAGES_DIR="$2" + shift 2 + ;; + --model_name) + MODEL_NAME="$2" + shift 2 + ;; + --batch_size) + BATCH_SIZE="$2" + shift 2 + ;; + --no_skip_existing) + SKIP_EXISTING=false + shift + ;; + --test) + echo "Running test mode..." + python data_tool/caption_image/test_caption_generation.py + exit $? + ;; + --help|-h) + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " --metadata_dir DIR Metadata directory (default: illustrious_generated/metadata)" + echo " --images_dir DIR Images directory (default: illustrious_generated)" + echo " --model_name PATH Model path (default: models/Qwen2.5-VL-7B-Instruct)" + echo " --batch_size N Batch size (default: 5)" + echo " --no_skip_existing Process all files, even with existing captions" + echo " --test Run test mode with single image" + echo " --help, -h Show this help message" + echo "" + echo "Examples:" + echo " $0 # Run with default settings" + echo " $0 --test # Test with single image" + echo " $0 --batch_size 10 # Use larger batch size" + echo " $0 --no_skip_existing # Reprocess all files" + exit 0 + ;; + *) + echo "Unknown option: $1" + echo "Use --help for usage information" + exit 1 + ;; + esac +done + +# 显示配置 +echo "Configuration:" +echo " Metadata directory: $METADATA_DIR" +echo " Images directory: $IMAGES_DIR" +echo " Model: $MODEL_NAME" +echo " Batch size: $BATCH_SIZE" +echo " Skip existing: $SKIP_EXISTING" +echo "" + +# 检查目录 +if [ ! -d "$METADATA_DIR" ]; then + echo "Error: Metadata directory not found: $METADATA_DIR" + exit 1 +fi + +if [ ! -d "$IMAGES_DIR" ]; then + echo "Error: Images directory not found: $IMAGES_DIR" + exit 1 +fi + +# 构建Python命令 +CMD="python data_tool/caption_image/generate_natural_captions.py" +CMD="$CMD --metadata_dir $METADATA_DIR" +CMD="$CMD --images_dir $IMAGES_DIR" +CMD="$CMD --model_name $MODEL_NAME" +CMD="$CMD --batch_size $BATCH_SIZE" + +if [ "$SKIP_EXISTING" = false ]; then + CMD="$CMD --no_skip_existing" +fi + +echo "Executing: $CMD" +echo "" + +# 运行脚本 +$CMD + +# 检查执行结果 +if [ $? -eq 0 ]; then + echo "" + echo "=== Caption generation completed successfully! ===" + echo "Check the log file: natural_caption_generation.log" +else + echo "" + echo "=== Caption generation failed! ===" + echo "Check the log file for details: natural_caption_generation.log" + exit 1 +fi diff --git a/data_tool/caption_image/test_caption_generation.py b/data_tool/caption_image/test_caption_generation.py new file mode 100644 index 0000000000000000000000000000000000000000..7cb17b088dce847d511b5db8056446f2269a00d0 --- /dev/null +++ b/data_tool/caption_image/test_caption_generation.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +""" +测试脚本:为单个图像生成自然语言描述 +""" + +import sys +import os +import json +from pathlib import Path + +# 添加项目根目录到Python路径 +project_root = Path(__file__).parent.parent.parent +sys.path.append(str(project_root)) + +from data_tool.caption_image.generate_natural_captions import NaturalCaptionGenerator + +def test_single_image(): + """测试单个图像的caption生成""" + + # 配置 + model_name = "models/Qwen2.5-VL-7B-Instruct" + test_image = "illustrious_generated/00a7174aa78f.png" # 使用附件中提到的图像 + test_metadata = "illustrious_generated/metadata/00a7174aa78f.json" + + # 检查文件是否存在 + if not os.path.exists(test_image): + print(f"Test image not found: {test_image}") + return + + if not os.path.exists(test_metadata): + print(f"Test metadata not found: {test_metadata}") + return + + # 读取metadata获取prompts + with open(test_metadata, 'r', encoding='utf-8') as f: + metadata = json.load(f) + + original_prompt_data = metadata.get('original_prompt_data', {}) + positive_prompt = original_prompt_data.get('positive_prompt', '') + negative_prompt = original_prompt_data.get('negative_prompt', '') + + print(f"Testing caption generation for: {test_image}") + print(f"Positive prompt: {positive_prompt[:100]}...") + print(f"Negative prompt: {negative_prompt[:100]}...") + print("-" * 80) + + try: + # 初始化caption生成器 + generator = NaturalCaptionGenerator(model_name) + + # 生成caption + caption = generator.generate_caption(test_image, positive_prompt, negative_prompt) + + print("Generated Caption:") + print(caption) + print("-" * 80) + + # 清理内存 + generator.clear_memory() + + # 保存测试结果 + test_result = { + 'image_path': test_image, + 'original_prompts': { + 'positive': positive_prompt, + 'negative': negative_prompt + }, + 'generated_caption': caption + } + + with open('test_caption_result.json', 'w', encoding='utf-8') as f: + json.dump(test_result, f, indent=2, ensure_ascii=False) + + print("Test completed successfully!") + print("Result saved to: test_caption_result.json") + + except Exception as e: + print(f"Error during test: {e}") + import traceback + traceback.print_exc() + +if __name__ == "__main__": + test_single_image() diff --git a/data_tool/clean_civitai_data/README-clean-prompt.md b/data_tool/clean_civitai_data/README-clean-prompt.md new file mode 100644 index 0000000000000000000000000000000000000000..63c45d67f36032d6938cfe4a158a537b616c538e --- /dev/null +++ b/data_tool/clean_civitai_data/README-clean-prompt.md @@ -0,0 +1,219 @@ +# 数据清洗工具 + +这个目录包含用于清洗和处理civitai数据集的工具脚本。 + +## clean_civitai_data.py + +### 功能 +清洗civitai_image.csv文件,去除prompt和neg prompt都为空的行。 + +### 特性 +- 自动检测并处理空字符串和NaN值 +- 提供详细的清洗统计信息 +- 自动创建原始文件备份 +- 支持指定输出文件路径 +- 包含错误处理和详细日志 + +### 使用方法 + +#### 1. 默认清洗(推荐) +```bash +python clean_civitai_data.py +``` +这将清洗默认的civitai_image.csv文件,原文件会被替换,同时创建.backup备份文件。 + +#### 2. 指定输入文件 +```bash +python clean_civitai_data.py /path/to/your/civitai_image.csv +``` + +#### 3. 指定输入和输出文件 +```bash +python clean_civitai_data.py input.csv output_cleaned.csv +``` + +### 输出示例 +``` +============================================================ +Civitai数据清洗工具 +============================================================ +正在读取文件: /home/ubuntu/lyl/QwenIllustrious/civitai_image.csv +原始数据行数: 40953 +原始数据列数: 6 +列名: ['web-scraper-order', 'web-scraper-start-url', 'link', 'link-href', 'prompt', 'neg prompt'] + +清洗前统计: +- prompt为空的行数: 35000 +- neg prompt为空的行数: 38000 +- prompt和neg prompt都为空的行数: 30000 + +清洗后统计: +- 保留的行数: 10953 +- 删除的行数: 30000 +- 数据保留率: 26.75% + +原始文件已备份到: /home/ubuntu/lyl/QwenIllustrious/civitai_image.csv.backup +清洗后的数据已保存到: /home/ubuntu/lyl/QwenIllustrious/civitai_image.csv + +✅ 数据清洗完成! +``` + +### 依赖项 +- pandas + +### 安装依赖 +```bash +pip install pandas +``` + +## deduplicate_by_link.py + +### 功能 +按link-href去重,优先保留prompt内容以"Show less"结尾的行。 + +### 特性 +- 智能去重:相同link-href的记录只保留一条 +- 优先级规则:优先保留prompt以"Show less"结尾的行 +- 详细统计信息:显示去重前后的数据对比 +- 示例展示:显示具体的去重处理例子 +- 自动创建备份文件 + +### 去重规则 +1. 按`link-href`列进行分组 +2. 对于相同的`link-href`,优先保留prompt以"Show less"结尾的行 +3. 如果都有或都没有"Show less",则保留第一行出现的记录 + +### 使用方法 + +#### 1. 默认去重(推荐) +```bash +python deduplicate_by_link.py +``` +这将对默认的civitai_image.csv文件进行去重,原文件会被替换,同时创建.backup备份文件。 + +#### 2. 指定输入文件 +```bash +python deduplicate_by_link.py /path/to/your/civitai_image.csv +``` + +#### 3. 指定输入和输出文件 +```bash +python deduplicate_by_link.py input.csv output_deduplicated.csv +``` + +### 输出示例 +``` +============================================================ +Civitai数据去重工具 - 按link-href去重 +============================================================ +正在读取文件: /home/ubuntu/lyl/QwenIllustrious/civitai_image.csv +原始数据行数: 10953 +原始数据列数: 6 +列名: ['web-scraper-order', 'web-scraper-start-url', 'link', 'link-href', 'prompt', 'neg prompt'] + +去重前统计: +- 总行数: 10953 +- 唯一link-href数量: 5476 +- 重复行数: 5477 + +去重后统计: +- 保留的行数: 5476 +- 删除的行数: 5477 +- 数据保留率: 50.00% +- 唯一link-href数量: 5476 +- 其中以'Show less'结尾的行数: 4523 + +去重处理示例: +示例 1: https://civitai.com/images/81915044 + 原始行数: 2 + 保留行的prompt结尾: Show less + +✅ 数据去重完成! +``` + +### 依赖项 +- pandas + +## clean_prompt_endings.py + +### 功能 +清理prompt和neg prompt列中的内容,移除末尾的"Show less"、"Show more"和"..."等后缀。 + +### 特性 +- 智能识别并移除多种后缀格式 +- 支持"Show less"、"Show more"(不区分大小写) +- 支持"..."(三个或更多点)和"…"(省略号字符) +- 处理前后空格 +- 详细的统计信息显示清理前后的对比 +- 提供清理示例展示 +- 自动创建备份文件 + +### 清理规则 +1. 移除prompt和neg prompt末尾的"Show less"(不区分大小写) +2. 移除prompt和neg prompt末尾的"Show more"(不区分大小写) +3. 移除prompt和neg prompt末尾的"..."(三个或更多连续点) +4. 移除prompt和neg prompt末尾的"…"(省略号字符) +5. 自动处理前后的空格 + +### 使用方法 + +#### 1. 默认清理(推荐) +```bash +python clean_prompt_endings.py +``` +这将清理默认的civitai_image.csv文件,原文件会被替换,同时创建.backup备份文件。 + +#### 2. 指定输入文件 +```bash +python clean_prompt_endings.py /path/to/your/civitai_image.csv +``` + +#### 3. 指定输入和输出文件 +```bash +python clean_prompt_endings.py input.csv output_cleaned.csv +``` + +### 输出示例 +``` +============================================================ +Civitai数据清理工具 - 移除prompt末尾后缀 +============================================================ +正在读取文件: /home/ubuntu/lyl/QwenIllustrious/civitai_image.csv +原始数据行数: 5476 +原始数据列数: 6 +列名: ['web-scraper-order', 'web-scraper-start-url', 'link', 'link-href', 'prompt', 'neg prompt'] + +清理前统计: +Prompt列: + - 以'Show less'结尾的行数: 4523 + - 以'Show more'结尾的行数: 128 + - 以'...'或'…'结尾的行数: 45 + +Neg prompt列: + - 以'Show less'结尾的行数: 892 + - 以'Show more'结尾的行数: 23 + - 以'...'或'…'结尾的行数: 12 + +清理后统计: +Prompt列: + - 以'Show less'结尾的行数: 0 + - 以'Show more'结尾的行数: 0 + - 以'...'或'…'结尾的行数: 0 + +Neg prompt列: + - 以'Show less'结尾的行数: 0 + - 以'Show more'结尾的行数: 0 + - 以'...'或'…'结尾的行数: 0 + +总共清理了 5623 个后缀 + +清理示例: +示例 1: + Prompt原文末尾: ...advertisements., Show less + Prompt清理后末尾: ...advertisements., + +✅ 数据清理完成! +``` + +### 依赖项 +- pandas diff --git a/data_tool/clean_civitai_data/README-clean-weights-and-loras.md b/data_tool/clean_civitai_data/README-clean-weights-and-loras.md new file mode 100644 index 0000000000000000000000000000000000000000..561eb1ec9c1338fce4b460f344801baf02ab8d5c --- /dev/null +++ b/data_tool/clean_civitai_data/README-clean-weights-and-loras.md @@ -0,0 +1,305 @@ +# 数据清洗工具 + +这个目录包含用于清洗和处理civitai数据集的工具脚本。 + +## clean_civitai_data.py + +### 功能 +清洗civitai_image.csv文件,去除prompt和neg prompt都为空的行。 + +### 特性 +- 自动检测并处理空字符串和NaN值 +- 提供详细的清洗统计信息 +- 自动创建原始文件备份 +- 支持指定输出文件路径 +- 包含错误处理和详细日志 + +### 使用方法 + +#### 1. 默认清洗(推荐) +```bash +python clean_civitai_data.py +``` +这将清洗默认的civitai_image.csv文件,原文件会被替换,同时创建.backup备份文件。 + +#### 2. 指定输入文件 +```bash +python clean_civitai_data.py /path/to/your/civitai_image.csv +``` + +#### 3. 指定输入和输出文件 +```bash +python clean_civitai_data.py input.csv output_cleaned.csv +``` + +### 输出示例 +``` +============================================================ +Civitai数据清洗工具 +============================================================ +正在读取文件: /home/ubuntu/lyl/QwenIllustrious/civitai_image.csv +原始数据行数: 40953 +原始数据列数: 6 +列名: ['web-scraper-order', 'web-scraper-start-url', 'link', 'link-href', 'prompt', 'neg prompt'] + +清洗前统计: +- prompt为空的行数: 35000 +- neg prompt为空的行数: 38000 +- prompt和neg prompt都为空的行数: 30000 + +清洗后统计: +- 保留的行数: 10953 +- 删除的行数: 30000 +- 数据保留率: 26.75% + +原始文件已备份到: /home/ubuntu/lyl/QwenIllustrious/civitai_image.csv.backup +清洗后的数据已保存到: /home/ubuntu/lyl/QwenIllustrious/civitai_image.csv + +✅ 数据清洗完成! +``` + +### 依赖项 +- pandas + +### 安装依赖 +```bash +pip install pandas +``` + +## deduplicate_by_link.py + +### 功能 +按link-href去重,优先保留prompt内容以"Show less"结尾的行。 + +### 特性 +- 智能去重:相同link-href的记录只保留一条 +- 优先级规则:优先保留prompt以"Show less"结尾的行 +- 详细统计信息:显示去重前后的数据对比 +- 示例展示:显示具体的去重处理例子 +- 自动创建备份文件 + +### 去重规则 +1. 按`link-href`列进行分组 +2. 对于相同的`link-href`,优先保留prompt以"Show less"结尾的行 +3. 如果都有或都没有"Show less",则保留第一行出现的记录 + +### 使用方法 + +#### 1. 默认去重(推荐) +```bash +python deduplicate_by_link.py +``` +这将对默认的civitai_image.csv文件进行去重,原文件会被替换,同时创建.backup备份文件。 + +#### 2. 指定输入文件 +```bash +python deduplicate_by_link.py /path/to/your/civitai_image.csv +``` + +#### 3. 指定输入和输出文件 +```bash +python deduplicate_by_link.py input.csv output_deduplicated.csv +``` + +### 输出示例 +``` +============================================================ +Civitai数据去重工具 - 按link-href去重 +============================================================ +正在读取文件: /home/ubuntu/lyl/QwenIllustrious/civitai_image.csv +原始数据行数: 10953 +原始数据列数: 6 +列名: ['web-scraper-order', 'web-scraper-start-url', 'link', 'link-href', 'prompt', 'neg prompt'] + +去重前统计: +- 总行数: 10953 +- 唯一link-href数量: 5476 +- 重复行数: 5477 + +去重后统计: +- 保留的行数: 5476 +- 删除的行数: 5477 +- 数据保留率: 50.00% +- 唯一link-href数量: 5476 +- 其中以'Show less'结尾的行数: 4523 + +去重处理示例: +示例 1: https://civitai.com/images/81915044 + 原始行数: 2 + 保留行的prompt结尾: Show less + +✅ 数据去重完成! +``` + +### 依赖项 +- pandas + +## clean_prompt_endings.py + +### 功能 +清理prompt和neg prompt列中的内容,移除末尾的"Show less"、"Show more"和"..."等后缀。 + +### 特性 +- 智能识别并移除多种后缀格式 +- 支持"Show less"、"Show more"(不区分大小写) +- 支持"..."(三个或更多点)和"…"(省略号字符) +- 处理前后空格 +- 详细的统计信息显示清理前后的对比 +- 提供清理示例展示 +- 自动创建备份文件 + +### 清理规则 +1. 移除prompt和neg prompt末尾的"Show less"(不区分大小写) +2. 移除prompt和neg prompt末尾的"Show more"(不区分大小写) +3. 移除prompt和neg prompt末尾的"..."(三个或更多连续点) +4. 移除prompt和neg prompt末尾的"…"(省略号字符) +5. 自动处理前后的空格 + +### 使用方法 + +#### 1. 默认清理(推荐) +```bash +python clean_prompt_endings.py +``` +这将清理默认的civitai_image.csv文件,原文件会被替换,同时创建.backup备份文件。 + +#### 2. 指定输入文件 +```bash +python clean_prompt_endings.py /path/to/your/civitai_image.csv +``` + +#### 3. 指定输入和输出文件 +```bash +python clean_prompt_endings.py input.csv output_cleaned.csv +``` + +### 输出示例 +``` +============================================================ +Civitai数据清理工具 - 移除prompt末尾后缀 +============================================================ +正在读取文件: /home/ubuntu/lyl/QwenIllustrious/civitai_image.csv +原始数据行数: 5476 +原始数据列数: 6 +列名: ['web-scraper-order', 'web-scraper-start-url', 'link', 'link-href', 'prompt', 'neg prompt'] + +清理前统计: +Prompt列: + - 以'Show less'结尾的行数: 4523 + - 以'Show more'结尾的行数: 128 + - 以'...'或'…'结尾的行数: 45 + +Neg prompt列: + - 以'Show less'结尾的行数: 892 + - 以'Show more'结尾的行数: 23 + - 以'...'或'…'结尾的行数: 12 + +清理后统计: +Prompt列: + - 以'Show less'结尾的行数: 0 + - 以'Show more'结尾的行数: 0 + - 以'...'或'…'结尾的行数: 0 + +Neg prompt列: + - 以'Show less'结尾的行数: 0 + - 以'Show more'结尾的行数: 0 + - 以'...'或'…'结尾的行数: 0 + +总共清理了 5623 个后缀 + +清理示例: +示例 1: + Prompt原文末尾: ...advertisements., Show less + Prompt清理后末尾: ...advertisements., + +✅ 数据清理完成! +``` + +### 依赖项 +- pandas + +## clean_weights_and_loras.py + +### 功能 +清理prompt和neg prompt列中的权重值和LoRA标签,标准化prompt格式。 + +### 特性 +- 移除LoRA标签:删除类似 `` 的内容 +- 清理权重值:将 `(detailed background:1.1)` 转换为 `detailed background` +- 支持多重嵌套括号:处理 `((text:1.2))` 和 `(((text:1.3)))` 格式 +- 智能空格处理:自动清理多余的空格和逗号 +- 详细统计信息:显示清理前后的对比数据 +- 提供清理示例展示 +- 自动创建备份文件 + +### 清理规则 +1. **LoRA标签清理**:完全删除 `` 格式的标签 +2. **权重值清理**: + - `(text:1.2)` → `text` + - `((text:1.2))` → `text` + - `(((text:1.2)))` → `text` +3. **格式整理**: + - 移除多余的连续空格 + - 清理多余的逗号 + - 修正标点符号格式 + +### 使用方法 + +#### 1. 默认清理(推荐) +```bash +python clean_weights_and_loras.py +``` +这将清理默认的civitai_image.csv文件,原文件会被替换,同时创建.backup备份文件。 + +#### 2. 指定输入文件 +```bash +python clean_weights_and_loras.py /path/to/your/civitai_image.csv +``` + +#### 3. 指定输入和输出文件 +```bash +python clean_weights_and_loras.py input.csv output_cleaned.csv +``` + +### 输出示例 +``` +============================================================ +Civitai数据清理工具 - 移除权重值和LoRA标签 +============================================================ +正在读取文件: /home/ubuntu/lyl/QwenIllustrious/civitai_image.csv +原始数据行数: 5476 +原始数据列数: 6 + +清理前统计: +Prompt列: + - LoRA标签数量: 8924 + - 权重值数量: 12456 + +Neg prompt列: + - LoRA标签数量: 234 + - 权重值数量: 2341 + +清理后统计: +Prompt列: + - LoRA标签数量: 0 + - 权重值数量: 0 + +Neg prompt列: + - LoRA标签数量: 0 + - 权重值数量: 0 + +清理总结: +- 清理的LoRA标签数量: 9158 +- 清理的权重值数量: 14797 +- 总共清理的元素数量: 23955 + +清理示例: +示例 1: + Prompt中移除的LoRA: ['', ''] + Prompt中移除的权重: ['(highly detailed, intricate details, cinematic lighting:1.3)', '(character portrait with instrument:1.1)'] + +✅ 数据清理完成! +``` + +### 依赖项 +- pandas diff --git a/data_tool/clean_civitai_data/README.md b/data_tool/clean_civitai_data/README.md new file mode 100644 index 0000000000000000000000000000000000000000..71ea5e9ef40772bdeed67118de792635f6920b03 --- /dev/null +++ b/data_tool/clean_civitai_data/README.md @@ -0,0 +1,67 @@ +# 数据清洗工具 + +这个目录包含用于清洗和处理civitai数据集的工具脚本。 + +## clean_civitai_data.py + +### 功能 +清洗civitai_image.csv文件,去除prompt和neg prompt都为空的行。 + +### 特性 +- 自动检测并处理空字符串和NaN值 +- 提供详细的清洗统计信息 +- 自动创建原始文件备份 +- 支持指定输出文件路径 +- 包含错误处理和详细日志 + +### 使用方法 + +#### 1. 默认清洗(推荐) +```bash +python clean_civitai_data.py +``` +这将清洗默认的civitai_image.csv文件,原文件会被替换,同时创建.backup备份文件。 + +#### 2. 指定输入文件 +```bash +python clean_civitai_data.py /path/to/your/civitai_image.csv +``` + +#### 3. 指定输入和输出文件 +```bash +python clean_civitai_data.py input.csv output_cleaned.csv +``` + +### 输出示例 +``` +============================================================ +Civitai数据清洗工具 +============================================================ +正在读取文件: /home/ubuntu/lyl/QwenIllustrious/civitai_image.csv +原始数据行数: 40953 +原始数据列数: 6 +列名: ['web-scraper-order', 'web-scraper-start-url', 'link', 'link-href', 'prompt', 'neg prompt'] + +清洗前统计: +- prompt为空的行数: 35000 +- neg prompt为空的行数: 38000 +- prompt和neg prompt都为空的行数: 30000 + +清洗后统计: +- 保留的行数: 10953 +- 删除的行数: 30000 +- 数据保留率: 26.75% + +原始文件已备份到: /home/ubuntu/lyl/QwenIllustrious/civitai_image.csv.backup +清洗后的数据已保存到: /home/ubuntu/lyl/QwenIllustrious/civitai_image.csv + +✅ 数据清洗完成! +``` + +### 依赖项 +- pandas + +### 安装依赖 +```bash +pip install pandas +``` diff --git a/data_tool/clean_civitai_data/README_deduplicate.md b/data_tool/clean_civitai_data/README_deduplicate.md new file mode 100644 index 0000000000000000000000000000000000000000..7de734320ba54b25c515c9bb7e9b2cc30119e76e --- /dev/null +++ b/data_tool/clean_civitai_data/README_deduplicate.md @@ -0,0 +1,135 @@ +# 数据清洗工具 + +这个目录包含用于清洗和处理civitai数据集的工具脚本。 + +## clean_civitai_data.py + +### 功能 +清洗civitai_image.csv文件,去除prompt和neg prompt都为空的行。 + +### 特性 +- 自动检测并处理空字符串和NaN值 +- 提供详细的清洗统计信息 +- 自动创建原始文件备份 +- 支持指定输出文件路径 +- 包含错误处理和详细日志 + +### 使用方法 + +#### 1. 默认清洗(推荐) +```bash +python clean_civitai_data.py +``` +这将清洗默认的civitai_image.csv文件,原文件会被替换,同时创建.backup备份文件。 + +#### 2. 指定输入文件 +```bash +python clean_civitai_data.py /path/to/your/civitai_image.csv +``` + +#### 3. 指定输入和输出文件 +```bash +python clean_civitai_data.py input.csv output_cleaned.csv +``` + +### 输出示例 +``` +============================================================ +Civitai数据清洗工具 +============================================================ +正在读取文件: /home/ubuntu/lyl/QwenIllustrious/civitai_image.csv +原始数据行数: 40953 +原始数据列数: 6 +列名: ['web-scraper-order', 'web-scraper-start-url', 'link', 'link-href', 'prompt', 'neg prompt'] + +清洗前统计: +- prompt为空的行数: 35000 +- neg prompt为空的行数: 38000 +- prompt和neg prompt都为空的行数: 30000 + +清洗后统计: +- 保留的行数: 10953 +- 删除的行数: 30000 +- 数据保留率: 26.75% + +原始文件已备份到: /home/ubuntu/lyl/QwenIllustrious/civitai_image.csv.backup +清洗后的数据已保存到: /home/ubuntu/lyl/QwenIllustrious/civitai_image.csv + +✅ 数据清洗完成! +``` + +### 依赖项 +- pandas + +### 安装依赖 +```bash +pip install pandas +``` + +## deduplicate_by_link.py + +### 功能 +按link-href去重,优先保留prompt内容以"Show less"结尾的行。 + +### 特性 +- 智能去重:相同link-href的记录只保留一条 +- 优先级规则:优先保留prompt以"Show less"结尾的行 +- 详细统计信息:显示去重前后的数据对比 +- 示例展示:显示具体的去重处理例子 +- 自动创建备份文件 + +### 去重规则 +1. 按`link-href`列进行分组 +2. 对于相同的`link-href`,优先保留prompt以"Show less"结尾的行 +3. 如果都有或都没有"Show less",则保留第一行出现的记录 + +### 使用方法 + +#### 1. 默认去重(推荐) +```bash +python deduplicate_by_link.py +``` +这将对默认的civitai_image.csv文件进行去重,原文件会被替换,同时创建.backup备份文件。 + +#### 2. 指定输入文件 +```bash +python deduplicate_by_link.py /path/to/your/civitai_image.csv +``` + +#### 3. 指定输入和输出文件 +```bash +python deduplicate_by_link.py input.csv output_deduplicated.csv +``` + +### 输出示例 +``` +============================================================ +Civitai数据去重工具 - 按link-href去重 +============================================================ +正在读取文件: /home/ubuntu/lyl/QwenIllustrious/civitai_image.csv +原始数据行数: 10953 +原始数据列数: 6 +列名: ['web-scraper-order', 'web-scraper-start-url', 'link', 'link-href', 'prompt', 'neg prompt'] + +去重前统计: +- 总行数: 10953 +- 唯一link-href数量: 5476 +- 重复行数: 5477 + +去重后统计: +- 保留的行数: 5476 +- 删除的行数: 5477 +- 数据保留率: 50.00% +- 唯一link-href数量: 5476 +- 其中以'Show less'结尾的行数: 4523 + +去重处理示例: +示例 1: https://civitai.com/images/81915044 + 原始行数: 2 + 保留行的prompt结尾: Show less + +✅ 数据去重完成! +``` + +### 依赖项 +- pandas diff --git a/data_tool/clean_civitai_data/clean_civitai_data.py b/data_tool/clean_civitai_data/clean_civitai_data.py new file mode 100644 index 0000000000000000000000000000000000000000..e6bd7597ee45af41cff20db77fd2b771f7d23dd8 --- /dev/null +++ b/data_tool/clean_civitai_data/clean_civitai_data.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python3 +""" +数据清洗脚本:去除civitai_image.csv中prompt和neg prompt都为空的行 +""" + +import pandas as pd +import os +import sys +from pathlib import Path + +def clean_civitai_data(input_file, output_file=None): + """ + 清洗civitai数据,去除prompt和neg prompt都为空的行 + + Args: + input_file (str): 输入CSV文件路径 + output_file (str, optional): 输出CSV文件路径,如果不指定则覆盖原文件 + """ + + # 检查输入文件是否存在 + if not os.path.exists(input_file): + print(f"错误: 输入文件 {input_file} 不存在") + return False + + try: + # 读取CSV文件 + print(f"正在读取文件: {input_file}") + df = pd.read_csv(input_file) + + print(f"原始数据行数: {len(df)}") + print(f"原始数据列数: {len(df.columns)}") + print(f"列名: {list(df.columns)}") + + # 检查是否存在必要的列 + if 'prompt' not in df.columns or 'neg prompt' not in df.columns: + print("错误: CSV文件中缺少'prompt'或'neg prompt'列") + return False + + # 清洗前的统计 + empty_prompt_count = df['prompt'].isna().sum() + (df['prompt'] == '').sum() + empty_neg_prompt_count = df['neg prompt'].isna().sum() + (df['neg prompt'] == '').sum() + both_empty_count = ((df['prompt'].isna() | (df['prompt'] == '')) & + (df['neg prompt'].isna() | (df['neg prompt'] == ''))).sum() + + print(f"\n清洗前统计:") + print(f"- prompt为空的行数: {empty_prompt_count}") + print(f"- neg prompt为空的行数: {empty_neg_prompt_count}") + print(f"- prompt和neg prompt都为空的行数: {both_empty_count}") + + # 清洗数据:去除prompt和neg prompt都为空的行 + # 考虑空字符串和NaN值 + mask = ~((df['prompt'].isna() | (df['prompt'] == '')) & + (df['neg prompt'].isna() | (df['neg prompt'] == ''))) + + cleaned_df = df[mask].copy() + + # 清洗后的统计 + print(f"\n清洗后统计:") + print(f"- 保留的行数: {len(cleaned_df)}") + print(f"- 删除的行数: {len(df) - len(cleaned_df)}") + print(f"- 数据保留率: {len(cleaned_df)/len(df)*100:.2f}%") + + # 确定输出文件路径 + if output_file is None: + output_file = input_file + backup_file = input_file + '.backup' + # 创建备份 + df.to_csv(backup_file, index=False) + print(f"原始文件已备份到: {backup_file}") + + # 保存清洗后的数据 + cleaned_df.to_csv(output_file, index=False) + print(f"清洗后的数据已保存到: {output_file}") + + return True + + except Exception as e: + print(f"处理过程中发生错误: {str(e)}") + return False + +def main(): + """主函数""" + # 默认输入文件路径 + default_input = "/home/ubuntu/lyl/QwenIllustrious/civitai_image.csv" + + # 解析命令行参数 + if len(sys.argv) == 1: + input_file = default_input + output_file = None + elif len(sys.argv) == 2: + input_file = sys.argv[1] + output_file = None + elif len(sys.argv) == 3: + input_file = sys.argv[1] + output_file = sys.argv[2] + else: + print("使用方法:") + print(" python clean_civitai_data.py") + print(" python clean_civitai_data.py ") + print(" python clean_civitai_data.py ") + return + + print("=" * 60) + print("Civitai数据清洗工具") + print("=" * 60) + + # 执行清洗 + success = clean_civitai_data(input_file, output_file) + + if success: + print("\n✅ 数据清洗完成!") + else: + print("\n❌ 数据清洗失败!") + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/data_tool/clean_civitai_data/clean_prompt_endings.py b/data_tool/clean_civitai_data/clean_prompt_endings.py new file mode 100644 index 0000000000000000000000000000000000000000..09450daf2529605f9ce4472513c078d5473a8b75 --- /dev/null +++ b/data_tool/clean_civitai_data/clean_prompt_endings.py @@ -0,0 +1,237 @@ +#!/usr/bin/env python3 +""" +数据清理脚本:移除prompt和neg prompt中末尾的"Show less"、"Show more"和"..." +""" + +import pandas as pd +import os +import sys +import re +from pathlib import Path + +def clean_prompt_endings(input_file, output_file=None): + """ + 清理prompt和neg prompt列,移除末尾的"Show less"、"Show more"和"..." + + Args: + input_file (str): 输入CSV文件路径 + output_file (str, optional): 输出CSV文件路径,如果不指定则覆盖原文件 + """ + + # 检查输入文件是否存在 + if not os.path.exists(input_file): + print(f"错误: 输入文件 {input_file} 不存在") + return False + + try: + # 读取CSV文件 + print(f"正在读取文件: {input_file}") + df = pd.read_csv(input_file) + + print(f"原始数据行数: {len(df)}") + print(f"原始数据列数: {len(df.columns)}") + print(f"列名: {list(df.columns)}") + + # 检查是否存在必要的列 + if 'prompt' not in df.columns or 'neg prompt' not in df.columns: + print("错误: CSV文件中缺少'prompt'或'neg prompt'列") + return False + + # 定义清理函数 + def clean_text_endings(text): + """清理文本末尾的特定后缀""" + if pd.isna(text) or text == '': + return text + + text = str(text).strip() + + # 移除末尾的"Show less"、"Show more"和"..." + # 使用正则表达式匹配这些模式 + patterns = [ + r'Show\s+less\s*$', # "Show less" (可能有空格) + r'Show\s+more\s*$', # "Show more" (可能有空格) + r'\.{3,}\s*$', # 三个或更多点 (...) + r'…\s*$' # 省略号字符 + ] + + for pattern in patterns: + text = re.sub(pattern, '', text, flags=re.IGNORECASE) + text = text.strip() + + return text + + # 统计清理前的情况 + prompt_show_less_before = df['prompt'].apply( + lambda x: pd.notna(x) and str(x).strip().endswith('Show less') + ).sum() + + prompt_show_more_before = df['prompt'].apply( + lambda x: pd.notna(x) and str(x).strip().endswith('Show more') + ).sum() + + prompt_dots_before = df['prompt'].apply( + lambda x: pd.notna(x) and (str(x).strip().endswith('...') or str(x).strip().endswith('…')) + ).sum() + + neg_prompt_show_less_before = df['neg prompt'].apply( + lambda x: pd.notna(x) and str(x).strip().endswith('Show less') + ).sum() + + neg_prompt_show_more_before = df['neg prompt'].apply( + lambda x: pd.notna(x) and str(x).strip().endswith('Show more') + ).sum() + + neg_prompt_dots_before = df['neg prompt'].apply( + lambda x: pd.notna(x) and (str(x).strip().endswith('...') or str(x).strip().endswith('…')) + ).sum() + + print(f"\n清理前统计:") + print(f"Prompt列:") + print(f" - 以'Show less'结尾的行数: {prompt_show_less_before}") + print(f" - 以'Show more'结尾的行数: {prompt_show_more_before}") + print(f" - 以'...'或'…'结尾的行数: {prompt_dots_before}") + print(f"Neg prompt列:") + print(f" - 以'Show less'结尾的行数: {neg_prompt_show_less_before}") + print(f" - 以'Show more'结尾的行数: {neg_prompt_show_more_before}") + print(f" - 以'...'或'…'结尾的行数: {neg_prompt_dots_before}") + + # 创建清理后的数据副本 + cleaned_df = df.copy() + + # 清理prompt列 + cleaned_df['prompt'] = cleaned_df['prompt'].apply(clean_text_endings) + + # 清理neg prompt列 + cleaned_df['neg prompt'] = cleaned_df['neg prompt'].apply(clean_text_endings) + + # 统计清理后的情况 + prompt_show_less_after = cleaned_df['prompt'].apply( + lambda x: pd.notna(x) and str(x).strip().endswith('Show less') + ).sum() + + prompt_show_more_after = cleaned_df['prompt'].apply( + lambda x: pd.notna(x) and str(x).strip().endswith('Show more') + ).sum() + + prompt_dots_after = cleaned_df['prompt'].apply( + lambda x: pd.notna(x) and (str(x).strip().endswith('...') or str(x).strip().endswith('…')) + ).sum() + + neg_prompt_show_less_after = cleaned_df['neg prompt'].apply( + lambda x: pd.notna(x) and str(x).strip().endswith('Show less') + ).sum() + + neg_prompt_show_more_after = cleaned_df['neg prompt'].apply( + lambda x: pd.notna(x) and str(x).strip().endswith('Show more') + ).sum() + + neg_prompt_dots_after = cleaned_df['neg prompt'].apply( + lambda x: pd.notna(x) and (str(x).strip().endswith('...') or str(x).strip().endswith('…')) + ).sum() + + print(f"\n清理后统计:") + print(f"Prompt列:") + print(f" - 以'Show less'结尾的行数: {prompt_show_less_after}") + print(f" - 以'Show more'结尾的行数: {prompt_show_more_after}") + print(f" - 以'...'或'…'结尾的行数: {prompt_dots_after}") + print(f"Neg prompt列:") + print(f" - 以'Show less'结尾的行数: {neg_prompt_show_less_after}") + print(f" - 以'Show more'结尾的行数: {neg_prompt_show_more_after}") + print(f" - 以'...'或'…'结尾的行数: {neg_prompt_dots_after}") + + # 计算清理的总数 + total_cleaned = ( + (prompt_show_less_before - prompt_show_less_after) + + (prompt_show_more_before - prompt_show_more_after) + + (prompt_dots_before - prompt_dots_after) + + (neg_prompt_show_less_before - neg_prompt_show_less_after) + + (neg_prompt_show_more_before - neg_prompt_show_more_after) + + (neg_prompt_dots_before - neg_prompt_dots_after) + ) + + print(f"\n总共清理了 {total_cleaned} 个后缀") + + # 确定输出文件路径 + if output_file is None: + output_file = input_file + backup_file = input_file + '.backup' + # 创建备份 + df.to_csv(backup_file, index=False) + print(f"原始文件已备份到: {backup_file}") + + # 保存清理后的数据 + cleaned_df.to_csv(output_file, index=False) + print(f"清理后的数据已保存到: {output_file}") + + # 展示一些清理的例子 + print(f"\n清理示例:") + + # 找出被清理的行的例子 + examples_shown = 0 + max_examples = 3 + + for idx, row in df.iterrows(): + if examples_shown >= max_examples: + break + + original_prompt = str(row['prompt']) if pd.notna(row['prompt']) else '' + cleaned_prompt = str(cleaned_df.loc[idx, 'prompt']) if pd.notna(cleaned_df.loc[idx, 'prompt']) else '' + + original_neg_prompt = str(row['neg prompt']) if pd.notna(row['neg prompt']) else '' + cleaned_neg_prompt = str(cleaned_df.loc[idx, 'neg prompt']) if pd.notna(cleaned_df.loc[idx, 'neg prompt']) else '' + + if original_prompt != cleaned_prompt or original_neg_prompt != cleaned_neg_prompt: + examples_shown += 1 + print(f"\n示例 {examples_shown}:") + + if original_prompt != cleaned_prompt: + print(f" Prompt原文末尾: ...{original_prompt[-50:]}") + print(f" Prompt清理后末尾: ...{cleaned_prompt[-50:]}") + + if original_neg_prompt != cleaned_neg_prompt: + print(f" Neg prompt原文末尾: ...{original_neg_prompt[-50:]}") + print(f" Neg prompt清理后末尾: ...{cleaned_neg_prompt[-50:]}") + + return True + + except Exception as e: + print(f"处理过程中发生错误: {str(e)}") + return False + +def main(): + """主函数""" + # 默认输入文件路径 + default_input = "/home/ubuntu/lyl/QwenIllustrious/civitai_image.csv" + + # 解析命令行参数 + if len(sys.argv) == 1: + input_file = default_input + output_file = None + elif len(sys.argv) == 2: + input_file = sys.argv[1] + output_file = None + elif len(sys.argv) == 3: + input_file = sys.argv[1] + output_file = sys.argv[2] + else: + print("使用方法:") + print(" python clean_prompt_endings.py") + print(" python clean_prompt_endings.py ") + print(" python clean_prompt_endings.py ") + return + + print("=" * 60) + print("Civitai数据清理工具 - 移除prompt末尾后缀") + print("=" * 60) + + # 执行清理 + success = clean_prompt_endings(input_file, output_file) + + if success: + print("\n✅ 数据清理完成!") + else: + print("\n❌ 数据清理失败!") + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/data_tool/clean_civitai_data/clean_weights_and_loras.py b/data_tool/clean_civitai_data/clean_weights_and_loras.py new file mode 100644 index 0000000000000000000000000000000000000000..a128eaceefe960e381085e0c7ec8a918d330cb4d --- /dev/null +++ b/data_tool/clean_civitai_data/clean_weights_and_loras.py @@ -0,0 +1,249 @@ +#!/usr/bin/env python3 +""" +数据清理脚本:清理prompt和neg prompt中的权重值和LoRA标签 +""" + +import pandas as pd +import os +import sys +import re +from pathlib import Path + +def clean_prompt_weights_and_loras(input_file, output_file=None): + """ + 清理prompt和neg prompt列,移除权重值和LoRA标签 + + Args: + input_file (str): 输入CSV文件路径 + output_file (str, optional): 输出CSV文件路径,如果不指定则覆盖原文件 + """ + + # 检查输入文件是否存在 + if not os.path.exists(input_file): + print(f"错误: 输入文件 {input_file} 不存在") + return False + + try: + # 读取CSV文件 + print(f"正在读取文件: {input_file}") + df = pd.read_csv(input_file) + + print(f"原始数据行数: {len(df)}") + print(f"原始数据列数: {len(df.columns)}") + print(f"列名: {list(df.columns)}") + + # 检查是否存在必要的列 + if 'prompt' not in df.columns or 'neg prompt' not in df.columns: + print("错误: CSV文件中缺少'prompt'或'neg prompt'列") + return False + + # 定义清理函数 + def clean_prompt_text(text): + """清理prompt文本中的权重值和LoRA标签""" + if pd.isna(text) or text == '': + return text + + text = str(text) + + # 1. 移除LoRA标签 + # 匹配 格式 + lora_pattern = r']*>' + text = re.sub(lora_pattern, '', text) + + # 2. 移除权重值 (text:number) + # 匹配 (内容:数字) 格式,保留内容,移除权重 + weight_pattern = r'\(([^():]+):[\d.]+\)' + text = re.sub(weight_pattern, r'\1', text) + + # 3. 移除多重嵌套的括号权重 ((text:number)) + nested_weight_pattern = r'\(\(([^()]+):[\d.]+\)\)' + text = re.sub(nested_weight_pattern, r'\1', text) + + # 4. 移除三重嵌套的括号权重 (((text:number))) + triple_weight_pattern = r'\(\(\(([^()]+):[\d.]+\)\)\)' + text = re.sub(triple_weight_pattern, r'\1', text) + + # 5. 清理多余的空格和逗号 + # 移除多个连续空格 + text = re.sub(r'\s+', ' ', text) + # 移除多个连续逗号 + text = re.sub(r',\s*,+', ',', text) + # 移除开头和结尾的逗号和空格 + text = text.strip().strip(',').strip() + # 移除孤立的逗号 + text = re.sub(r',\s*,', ',', text) + + return text + + # 统计清理前的情况 + def count_patterns(series, pattern, description): + """统计指定模式在系列中的出现次数""" + count = 0 + for text in series: + if pd.notna(text): + matches = re.findall(pattern, str(text)) + count += len(matches) + return count + + # LoRA标签统计 + lora_pattern = r']*>' + prompt_loras_before = count_patterns(df['prompt'], lora_pattern, 'LoRA标签') + neg_prompt_loras_before = count_patterns(df['neg prompt'], lora_pattern, 'LoRA标签') + + # 权重值统计 + weight_pattern = r'\([^():]+:[\d.]+\)' + prompt_weights_before = count_patterns(df['prompt'], weight_pattern, '权重值') + neg_prompt_weights_before = count_patterns(df['neg prompt'], weight_pattern, '权重值') + + print(f"\n清理前统计:") + print(f"Prompt列:") + print(f" - LoRA标签数量: {prompt_loras_before}") + print(f" - 权重值数量: {prompt_weights_before}") + print(f"Neg prompt列:") + print(f" - LoRA标签数量: {neg_prompt_loras_before}") + print(f" - 权重值数量: {neg_prompt_weights_before}") + + # 创建清理后的数据副本 + cleaned_df = df.copy() + + # 清理prompt列 + cleaned_df['prompt'] = cleaned_df['prompt'].apply(clean_prompt_text) + + # 清理neg prompt列 + cleaned_df['neg prompt'] = cleaned_df['neg prompt'].apply(clean_prompt_text) + + # 统计清理后的情况 + prompt_loras_after = count_patterns(cleaned_df['prompt'], lora_pattern, 'LoRA标签') + neg_prompt_loras_after = count_patterns(cleaned_df['neg prompt'], lora_pattern, 'LoRA标签') + prompt_weights_after = count_patterns(cleaned_df['prompt'], weight_pattern, '权重值') + neg_prompt_weights_after = count_patterns(cleaned_df['neg prompt'], weight_pattern, '权重值') + + print(f"\n清理后统计:") + print(f"Prompt列:") + print(f" - LoRA标签数量: {prompt_loras_after}") + print(f" - 权重值数量: {prompt_weights_after}") + print(f"Neg prompt列:") + print(f" - LoRA标签数量: {neg_prompt_loras_after}") + print(f" - 权重值数量: {neg_prompt_weights_after}") + + # 计算清理的总数 + total_loras_cleaned = ( + (prompt_loras_before - prompt_loras_after) + + (neg_prompt_loras_before - neg_prompt_loras_after) + ) + total_weights_cleaned = ( + (prompt_weights_before - prompt_weights_after) + + (neg_prompt_weights_before - neg_prompt_weights_after) + ) + + print(f"\n清理总结:") + print(f"- 清理的LoRA标签数量: {total_loras_cleaned}") + print(f"- 清理的权重值数量: {total_weights_cleaned}") + print(f"- 总共清理的元素数量: {total_loras_cleaned + total_weights_cleaned}") + + # 确定输出文件路径 + if output_file is None: + output_file = input_file + backup_file = input_file + '.backup' + # 创建备份 + df.to_csv(backup_file, index=False) + print(f"原始文件已备份到: {backup_file}") + + # 保存清理后的数据 + cleaned_df.to_csv(output_file, index=False) + print(f"清理后的数据已保存到: {output_file}") + + # 展示一些清理的例子 + print(f"\n清理示例:") + + examples_shown = 0 + max_examples = 3 + + for idx, row in df.iterrows(): + if examples_shown >= max_examples: + break + + original_prompt = str(row['prompt']) if pd.notna(row['prompt']) else '' + cleaned_prompt = str(cleaned_df.loc[idx, 'prompt']) if pd.notna(cleaned_df.loc[idx, 'prompt']) else '' + + original_neg_prompt = str(row['neg prompt']) if pd.notna(row['neg prompt']) else '' + cleaned_neg_prompt = str(cleaned_df.loc[idx, 'neg prompt']) if pd.notna(cleaned_df.loc[idx, 'neg prompt']) else '' + + # 检查是否有变化 + prompt_changed = original_prompt != cleaned_prompt + neg_prompt_changed = original_neg_prompt != cleaned_neg_prompt + + if prompt_changed or neg_prompt_changed: + examples_shown += 1 + print(f"\n示例 {examples_shown}:") + + if prompt_changed: + # 显示具体的变化 + lora_matches = re.findall(lora_pattern, original_prompt) + weight_matches = re.findall(weight_pattern, original_prompt) + + if lora_matches: + print(f" Prompt中移除的LoRA: {lora_matches[:3]}{'...' if len(lora_matches) > 3 else ''}") + if weight_matches: + print(f" Prompt中移除的权重: {weight_matches[:3]}{'...' if len(weight_matches) > 3 else ''}") + + # 显示前后对比(截取部分) + if len(original_prompt) > 100: + print(f" Prompt原文片段: {original_prompt[:100]}...") + print(f" Prompt清理后片段: {cleaned_prompt[:100]}...") + else: + print(f" Prompt原文: {original_prompt}") + print(f" Prompt清理后: {cleaned_prompt}") + + if neg_prompt_changed: + lora_matches = re.findall(lora_pattern, original_neg_prompt) + weight_matches = re.findall(weight_pattern, original_neg_prompt) + + if lora_matches: + print(f" Neg prompt中移除的LoRA: {lora_matches}") + if weight_matches: + print(f" Neg prompt中移除的权重: {weight_matches}") + + return True + + except Exception as e: + print(f"处理过程中发生错误: {str(e)}") + return False + +def main(): + """主函数""" + # 默认输入文件路径 + default_input = "/home/ubuntu/lyl/QwenIllustrious/civitai_image.csv" + + # 解析命令行参数 + if len(sys.argv) == 1: + input_file = default_input + output_file = None + elif len(sys.argv) == 2: + input_file = sys.argv[1] + output_file = None + elif len(sys.argv) == 3: + input_file = sys.argv[1] + output_file = sys.argv[2] + else: + print("使用方法:") + print(" python clean_weights_and_loras.py") + print(" python clean_weights_and_loras.py ") + print(" python clean_weights_and_loras.py ") + return + + print("=" * 60) + print("Civitai数据清理工具 - 移除权重值和LoRA标签") + print("=" * 60) + + # 执行清理 + success = clean_prompt_weights_and_loras(input_file, output_file) + + if success: + print("\n✅ 数据清理完成!") + else: + print("\n❌ 数据清理失败!") + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/data_tool/clean_civitai_data/deduplicate_by_link.py b/data_tool/clean_civitai_data/deduplicate_by_link.py new file mode 100644 index 0000000000000000000000000000000000000000..ab890fcfd00fc57b6c436e3289a44410ad396170 --- /dev/null +++ b/data_tool/clean_civitai_data/deduplicate_by_link.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 +""" +数据去重脚本:按link-href去重,优先保留prompt以"Show less"结尾的行 +""" + +import pandas as pd +import os +import sys +from pathlib import Path + +def deduplicate_by_link_href(input_file, output_file=None): + """ + 按link-href去重,优先保留prompt以"Show less"结尾的行 + + Args: + input_file (str): 输入CSV文件路径 + output_file (str, optional): 输出CSV文件路径,如果不指定则覆盖原文件 + """ + + # 检查输入文件是否存在 + if not os.path.exists(input_file): + print(f"错误: 输入文件 {input_file} 不存在") + return False + + try: + # 读取CSV文件 + print(f"正在读取文件: {input_file}") + df = pd.read_csv(input_file) + + print(f"原始数据行数: {len(df)}") + print(f"原始数据列数: {len(df.columns)}") + print(f"列名: {list(df.columns)}") + + # 检查是否存在必要的列 + if 'link-href' not in df.columns or 'prompt' not in df.columns: + print("错误: CSV文件中缺少'link-href'或'prompt'列") + return False + + # 去重前的统计 + unique_links_before = df['link-href'].nunique() + duplicate_count = len(df) - unique_links_before + + print(f"\n去重前统计:") + print(f"- 总行数: {len(df)}") + print(f"- 唯一link-href数量: {unique_links_before}") + print(f"- 重复行数: {duplicate_count}") + + # 创建一个优先级列,用于排序 + # prompt以"Show less"结尾的行优先级为1,其他为2 + df['_priority'] = df['prompt'].apply( + lambda x: 1 if pd.notna(x) and str(x).strip().endswith('Show less') else 2 + ) + + # 按link-href分组,然后按优先级排序,保留每组的第一行 + # 首先按priority排序(1优先于2),然后按原始顺序排序(保持稳定性) + df_sorted = df.sort_values(['link-href', '_priority'], kind='stable') + + # 按link-href去重,保留每组的第一行(即优先级最高的行) + deduplicated_df = df_sorted.drop_duplicates(subset=['link-href'], keep='first') + + # 删除临时的优先级列 + deduplicated_df = deduplicated_df.drop('_priority', axis=1) + + # 按原始顺序重新排序(基于索引) + deduplicated_df = deduplicated_df.sort_index() + + # 去重后的统计 + print(f"\n去重后统计:") + print(f"- 保留的行数: {len(deduplicated_df)}") + print(f"- 删除的行数: {len(df) - len(deduplicated_df)}") + print(f"- 数据保留率: {len(deduplicated_df)/len(df)*100:.2f}%") + print(f"- 唯一link-href数量: {deduplicated_df['link-href'].nunique()}") + + # 统计优先保留的"Show less"行数 + show_less_count = deduplicated_df['prompt'].apply( + lambda x: pd.notna(x) and str(x).strip().endswith('Show less') + ).sum() + print(f"- 其中以'Show less'结尾的行数: {show_less_count}") + + # 确定输出文件路径 + if output_file is None: + output_file = input_file + backup_file = input_file + '.backup' + # 创建备份 + df.to_csv(backup_file, index=False) + print(f"原始文件已备份到: {backup_file}") + + # 保存去重后的数据 + deduplicated_df.to_csv(output_file, index=False) + print(f"去重后的数据已保存到: {output_file}") + + # 展示一些去重的例子 + print(f"\n去重处理示例:") + + # 找出有重复的link-href + duplicate_links = df[df.duplicated(subset=['link-href'], keep=False)]['link-href'].unique() + + if len(duplicate_links) > 0: + # 展示前3个重复的例子 + for i, link in enumerate(duplicate_links[:3]): + print(f"\n示例 {i+1}: {link}") + original_rows = df[df['link-href'] == link] + kept_row = deduplicated_df[deduplicated_df['link-href'] == link] + + print(f" 原始行数: {len(original_rows)}") + print(f" 保留行的prompt结尾: {'Show less' if kept_row.iloc[0]['prompt'] and str(kept_row.iloc[0]['prompt']).strip().endswith('Show less') else '其他'}") + + return True + + except Exception as e: + print(f"处理过程中发生错误: {str(e)}") + return False + +def main(): + """主函数""" + # 默认输入文件路径 + default_input = "/home/ubuntu/lyl/QwenIllustrious/civitai_image.csv" + + # 解析命令行参数 + if len(sys.argv) == 1: + input_file = default_input + output_file = None + elif len(sys.argv) == 2: + input_file = sys.argv[1] + output_file = None + elif len(sys.argv) == 3: + input_file = sys.argv[1] + output_file = sys.argv[2] + else: + print("使用方法:") + print(" python deduplicate_by_link.py") + print(" python deduplicate_by_link.py ") + print(" python deduplicate_by_link.py ") + return + + print("=" * 60) + print("Civitai数据去重工具 - 按link-href去重") + print("=" * 60) + + # 执行去重 + success = deduplicate_by_link_href(input_file, output_file) + + if success: + print("\n✅ 数据去重完成!") + else: + print("\n❌ 数据去重失败!") + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/data_tool/generate_alignment_data/README.md b/data_tool/generate_alignment_data/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/data_tool/generate_alignment_data/cleanup.py b/data_tool/generate_alignment_data/cleanup.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/data_tool/generate_alignment_data/generate_illustrious_images.py b/data_tool/generate_alignment_data/generate_illustrious_images.py new file mode 100644 index 0000000000000000000000000000000000000000..4e0167528cf7c03d6461c342bcf559326c0488ca --- /dev/null +++ b/data_tool/generate_alignment_data/generate_illustrious_images.py @@ -0,0 +1,237 @@ +#!/usr/bin/env python3 +""" +Generate images using Illustrious model from augmented prompts. +Supports resuming from interruptions by checking existing files. +""" + +import json +import os +import random +import argparse +import hashlib +from pathlib import Path +from tqdm import tqdm +import torch +from diffusers import StableDiffusionXLPipeline +from PIL import Image +import logging + +# Set up logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') +logger = logging.getLogger(__name__) + +class IllustriousImageGenerator: + def __init__(self, model_path, output_dir, jsonl_path): + self.model_path = model_path + self.output_dir = Path(output_dir) + self.jsonl_path = jsonl_path + self.pipe = None + + # Image dimensions to choose from + self.dimensions = [512, 768, 1024, 1536, 2048] + + # Create output directories + self.output_dir.mkdir(parents=True, exist_ok=True) + self.metadata_dir = self.output_dir / "metadata" + self.metadata_dir.mkdir(exist_ok=True) + + def load_model(self): + """Load the Illustrious model""" + logger.info(f"Loading model from {self.model_path}") + try: + self.pipe = StableDiffusionXLPipeline.from_single_file( + self.model_path, + torch_dtype=torch.float16, + use_safetensors=True, + ) + self.pipe.to("cuda") + logger.info("Model loaded successfully") + except Exception as e: + logger.error(f"Error loading model: {e}") + raise + + def generate_filename_hash(self, prompt_data, width, height): + """Generate a unique filename hash based on prompt and dimensions""" + content = f"{prompt_data['positive_prompt']}_{prompt_data['negative_prompt']}_{width}_{height}" + return hashlib.md5(content.encode()).hexdigest()[:12] + + def is_already_generated(self, filename_hash): + """Check if image with this hash already exists""" + image_path = self.output_dir / f"{filename_hash}.png" + metadata_path = self.metadata_dir / f"{filename_hash}.json" + return image_path.exists() and metadata_path.exists() + + def load_prompts(self): + """Load prompts from JSONL file""" + prompts = [] + try: + with open(self.jsonl_path, 'r', encoding='utf-8') as f: + for line_num, line in enumerate(f, 1): + try: + prompt_data = json.loads(line.strip()) + prompts.append(prompt_data) + except json.JSONDecodeError as e: + logger.warning(f"Error parsing line {line_num}: {e}") + continue + logger.info(f"Loaded {len(prompts)} prompts from {self.jsonl_path}") + return prompts + except Exception as e: + logger.error(f"Error loading prompts: {e}") + raise + + def get_random_dimensions(self): + """Get random width and height from available dimensions""" + width = random.choice(self.dimensions) + height = random.choice(self.dimensions) + return width, height + + def save_metadata(self, filename_hash, prompt_data, width, height, generation_params): + """Save metadata for the generated image""" + metadata = { + "filename_hash": filename_hash, + "original_prompt_data": prompt_data, + "generation_parameters": { + "width": width, + "height": height, + **generation_params + }, + "model_info": { + "model_path": self.model_path, + "model_type": "StableDiffusionXL", + "torch_dtype": "float16" + } + } + + metadata_path = self.metadata_dir / f"{filename_hash}.json" + with open(metadata_path, 'w', encoding='utf-8') as f: + json.dump(metadata, f, indent=2, ensure_ascii=False) + + def generate_single_image(self, prompt_data, width, height, num_inference_steps=35, guidance_scale=7.5): + """Generate a single image from prompt data""" + try: + positive_prompt = prompt_data.get('positive_prompt', '') + negative_prompt = prompt_data.get('negative_prompt', '') + + # Generate image + image = self.pipe( + prompt=positive_prompt, + negative_prompt=negative_prompt, + num_inference_steps=num_inference_steps, + guidance_scale=guidance_scale, + width=width, + height=height + ).images[0] + + return image, { + "num_inference_steps": num_inference_steps, + "guidance_scale": guidance_scale + } + + except Exception as e: + logger.error(f"Error generating image: {e}") + raise + + def generate_images(self, max_images=None, num_inference_steps=35, guidance_scale=7.5): + """Generate images from all prompts""" + # Load model if not already loaded + if self.pipe is None: + self.load_model() + + # Load prompts + prompts = self.load_prompts() + + if max_images: + prompts = prompts[:max_images] + + generated_count = 0 + skipped_count = 0 + + # Set random seed for reproducible dimension selection + random.seed(42) + + logger.info(f"Starting generation for {len(prompts)} prompts") + + for i, prompt_data in enumerate(tqdm(prompts, desc="Generating images")): + try: + # Get random dimensions + width, height = self.get_random_dimensions() + + # Generate filename hash + filename_hash = self.generate_filename_hash(prompt_data, width, height) + + # Check if already generated + if self.is_already_generated(filename_hash): + logger.info(f"Skipping {filename_hash} - already exists") + skipped_count += 1 + continue + + # Generate image + logger.info(f"Generating image {i+1}/{len(prompts)} - {width}x{height}") + image, generation_params = self.generate_single_image( + prompt_data, width, height, num_inference_steps, guidance_scale + ) + + # Save image + image_path = self.output_dir / f"{filename_hash}.png" + image.save(image_path) + + # Save metadata + self.save_metadata(filename_hash, prompt_data, width, height, generation_params) + + generated_count += 1 + logger.info(f"Saved image: {image_path}") + + except Exception as e: + logger.error(f"Error processing prompt {i+1}: {e}") + continue + + logger.info(f"Generation complete! Generated: {generated_count}, Skipped: {skipped_count}") + + def cleanup(self): + """Clean up resources""" + if self.pipe is not None: + del self.pipe + torch.cuda.empty_cache() + + +def main(): + parser = argparse.ArgumentParser(description="Generate images using Illustrious model") + parser.add_argument("--model-path", + default="models/waiNSFWIllustrious_v140.safetensors", + help="Path to the Illustrious model file") + parser.add_argument("--jsonl-path", + default="augmented_prompts.jsonl", + help="Path to the JSONL file containing prompts") + parser.add_argument("--output-dir", + default="illustrious_generated", + help="Output directory for generated images") + parser.add_argument("--max-images", type=int, default=None, + help="Maximum number of images to generate (for testing)") + parser.add_argument("--num-inference-steps", type=int, default=35, + help="Number of inference steps") + parser.add_argument("--guidance-scale", type=float, default=7.5, + help="Guidance scale for generation") + + args = parser.parse_args() + + # Create generator + generator = IllustriousImageGenerator( + model_path=args.model_path, + output_dir=args.output_dir, + jsonl_path=args.jsonl_path + ) + + try: + # Generate images + generator.generate_images( + max_images=args.max_images, + num_inference_steps=args.num_inference_steps, + guidance_scale=args.guidance_scale + ) + finally: + # Clean up + generator.cleanup() + + +if __name__ == "__main__": + main() diff --git a/data_tool/generate_alignment_data/generate_images.py b/data_tool/generate_alignment_data/generate_images.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/data_tool/generate_alignment_data/monitor_progress.py b/data_tool/generate_alignment_data/monitor_progress.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/data_tool/generate_alignment_data/run_generation.sh b/data_tool/generate_alignment_data/run_generation.sh new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/data_tool/generate_alignment_data/run_illustrious_generation.sh b/data_tool/generate_alignment_data/run_illustrious_generation.sh new file mode 100644 index 0000000000000000000000000000000000000000..bf0723a05c8508371ae72bb8acd0651c0ba45c6b --- /dev/null +++ b/data_tool/generate_alignment_data/run_illustrious_generation.sh @@ -0,0 +1,127 @@ +#!/bin/bash + +# Generate images using Illustrious model +# This script provides easy-to-use commands for running the image generator + +set -e + +# Default paths (relative to project root) +PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +MODEL_PATH="${PROJECT_ROOT}/models/waiNSFWIllustrious_v140.safetensors" +JSONL_PATH="${PROJECT_ROOT}/augmented_prompts.jsonl" +OUTPUT_DIR="${PROJECT_ROOT}/illustrious_generated" + +# Default generation parameters +NUM_INFERENCE_STEPS=35 +GUIDANCE_SCALE=7.5 +MAX_IMAGES="" + +# Function to display usage +usage() { + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " --model-path PATH Path to Illustrious model (default: models/waiNSFWIllustrious_v140.safetensors)" + echo " --jsonl-path PATH Path to prompts JSONL file (default: augmented_prompts.jsonl)" + echo " --output-dir PATH Output directory for images (default: illustrious_generated)" + echo " --max-images N Maximum number of images to generate (for testing)" + echo " --num-inference-steps N Number of inference steps (default: 35)" + echo " --guidance-scale N Guidance scale (default: 7.5)" + echo " --help Show this help message" + echo "" + echo "Examples:" + echo " $0 # Generate all images with default settings" + echo " $0 --max-images 10 # Generate only 10 images for testing" + echo " $0 --num-inference-steps 50 --guidance-scale 8.0 # Custom generation params" +} + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + --model-path) + MODEL_PATH="$2" + shift 2 + ;; + --jsonl-path) + JSONL_PATH="$2" + shift 2 + ;; + --output-dir) + OUTPUT_DIR="$2" + shift 2 + ;; + --max-images) + MAX_IMAGES="$2" + shift 2 + ;; + --num-inference-steps) + NUM_INFERENCE_STEPS="$2" + shift 2 + ;; + --guidance-scale) + GUIDANCE_SCALE="$2" + shift 2 + ;; + --help) + usage + exit 0 + ;; + *) + echo "Unknown option: $1" + usage + exit 1 + ;; + esac +done + +# Check if required files exist +if [[ ! -f "$MODEL_PATH" ]]; then + echo "Error: Model file not found at $MODEL_PATH" + echo "Please make sure the Illustrious model is downloaded to the correct location." + exit 1 +fi + +if [[ ! -f "$JSONL_PATH" ]]; then + echo "Error: JSONL file not found at $JSONL_PATH" + echo "Please make sure the augmented_prompts.jsonl file exists." + exit 1 +fi + +# Check GPU availability +if ! nvidia-smi &>/dev/null; then + echo "Warning: nvidia-smi not found. Make sure CUDA is available." +fi + +# Create output directory if it doesn't exist +mkdir -p "$OUTPUT_DIR" + +# Build the command +CMD="python ${PROJECT_ROOT}/data_tool/generate_alignment_data/generate_illustrious_images.py" +CMD="$CMD --model-path \"$MODEL_PATH\"" +CMD="$CMD --jsonl-path \"$JSONL_PATH\"" +CMD="$CMD --output-dir \"$OUTPUT_DIR\"" +CMD="$CMD --num-inference-steps $NUM_INFERENCE_STEPS" +CMD="$CMD --guidance-scale $GUIDANCE_SCALE" + +if [[ -n "$MAX_IMAGES" ]]; then + CMD="$CMD --max-images $MAX_IMAGES" +fi + +echo "Starting Illustrious image generation..." +echo "Model: $MODEL_PATH" +echo "Prompts: $JSONL_PATH" +echo "Output: $OUTPUT_DIR" +echo "Inference steps: $NUM_INFERENCE_STEPS" +echo "Guidance scale: $GUIDANCE_SCALE" +if [[ -n "$MAX_IMAGES" ]]; then + echo "Max images: $MAX_IMAGES" +fi +echo "" + +# Run the command +eval "$CMD" + +echo "" +echo "Image generation completed!" +echo "Generated images are saved in: $OUTPUT_DIR" +echo "Metadata files are saved in: $OUTPUT_DIR/metadata" diff --git a/data_tool/improve_data_quality/config.json b/data_tool/improve_data_quality/config.json new file mode 100644 index 0000000000000000000000000000000000000000..425c7a4289736356db67ffb87a3fee49704e62ad --- /dev/null +++ b/data_tool/improve_data_quality/config.json @@ -0,0 +1,26 @@ +{ + "models": { + "qwen_vl_model": "Qwen/Qwen2.5-VL-7B-Instruct", + "illustrious_model": "models/waiNSFWIllustrious_v140.safetensors" + }, + "paths": { + "illustrious_generated_dir": "/home/ubuntu/lyl/QwenIllustrious/illustrious_generated", + "metadata_dir": "/home/ubuntu/lyl/QwenIllustrious/illustrious_generated/metadata", + "candidates_dir": "/home/ubuntu/lyl/QwenIllustrious/illustrious_generated/candidates", + "improved_dir": "/home/ubuntu/lyl/QwenIllustrious/illustrious_generated/improved" + }, + "optimization_settings": { + "batch_size": 30, + "num_candidates_per_image": 5, + "quality_threshold": 7, + "improvement_threshold": 1, + "max_images_to_process": null + }, + "generation_parameters": { + "width": 1024, + "height": 512, + "num_inference_steps": 35, + "guidance_scale": 7.5 + }, + "assessment_prompt": "请仔细分析这张动漫风格图像的质量,特别关注以下方面:\n\n1. 角色脸部质量(重点评估):\n - 脸部细节是否清晰\n - 眼睛是否对称且细节丰富\n - 鼻子和嘴巴的比例是否正确\n - 脸部轮廓是否自然\n - 是否有模糊、扭曲或不自然的地方\n\n2. 整体图像质量:\n - 线条清晰度\n - 色彩饱和度和对比度\n - 构图和比例\n - 细节丰富程度\n\n3. 技术问题:\n - 是否有伪影或噪点\n - 是否有明显的生成错误\n - 分辨率是否足够\n\n请给出总体质量评分(1-10分,10分最高),并说明具体问题。如果脸部质量有问题或总分低于7分,建议重新生成。\n\n请按以下格式回答:\n评分:X/10\n脸部质量:[好/一般/差]\n主要问题:[具体描述问题]\n是否需要重新生成:[是/否]" +} diff --git a/data_tool/improve_data_quality/quality_optimizer.py b/data_tool/improve_data_quality/quality_optimizer.py new file mode 100644 index 0000000000000000000000000000000000000000..cedad9d0161e089583502e6d0d255d5c004edfab --- /dev/null +++ b/data_tool/improve_data_quality/quality_optimizer.py @@ -0,0 +1,662 @@ +#!/usr/bin/env python3 +""" +Image Quality Assessment and Optimization System +Uses Qwen-VL 2.5 for image quality assessment and Illustrious model to regenerate low-quality images +Focuses on anime character facial quality +""" + +import os +import json +import gc +import time +from pathlib import Path +from typing import List, Dict, Any, Tuple +from PIL import Image +import torch +from transformers import Qwen2_5_VLForConditionalGeneration, AutoTokenizer, AutoProcessor +from qwen_vl_utils import process_vision_info +from diffusers import StableDiffusionXLPipeline +import logging + +# Configure logging +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s', + handlers=[ + logging.FileHandler('data_quality_optimization.log'), + logging.StreamHandler() + ] +) +logger = logging.getLogger(__name__) + +class QualityAssessment: + """Use Qwen-VL 2.5 for image quality assessment""" + + def __init__(self, model_name: str = "Qwen/Qwen2.5-VL-7B-Instruct"): + self.model_name = model_name + self.model = None + self.processor = None + self.load_model() + + def load_model(self): + """Load Qwen-VL model""" + logger.info("Loading Qwen-VL 2.5 model...") + try: + self.model = Qwen2_5_VLForConditionalGeneration.from_pretrained( + self.model_name, + torch_dtype=torch.bfloat16, + attn_implementation="flash_attention_2", + device_map="auto", + ) + self.processor = AutoProcessor.from_pretrained(self.model_name) + logger.info("Qwen-VL 2.5 model loaded successfully") + except Exception as e: + logger.error(f"Failed to load Qwen-VL model: {e}") + raise + + def assess_image_quality(self, image_path: str) -> Dict[str, Any]: + """ + Assess image quality, especially focusing on anime character faces + + Args: + image_path: Image file path + + Returns: + Dictionary containing quality score and detailed analysis + """ + prompt = """You are a professional illustrator. Now an AI model generates some images for you. To satisfy your high-end customers, please carefully analyze the quality of this anime-style image, paying special attention to the following aspects: + +1. Character Face Quality (Primary Focus): + - Are facial details clear and well-defined? + - Are the eyes symmetrical and detailed? + - Are the proportions of nose and mouth correct? + - Is the facial contour natural? + - Are there any blurry, distorted, or unnatural areas? + +2. Overall Image Quality: + - Line clarity and sharpness + - Color saturation and contrast + - Composition and proportions + - Level of detail richness + +3. Technical Issues: + - Are there any artifacts or noise? + - Are there obvious generation errors? + - Is the resolution sufficient? + +Please provide an overall quality score (1-10, with 10 being the highest) and explain specific issues. If there are facial quality problems or the total score is below 7, recommend regeneration. + +Please respond in the following format: +Score: X/10 +Face Quality: [Good/Average/Poor] +Main Issues: [Specific description of problems] +Needs Regeneration: [Yes/No]""" + + try: + messages = [ + { + "role": "user", + "content": [ + {"type": "image", "image": image_path}, + {"type": "text", "text": prompt} + ], + } + ] + + text = self.processor.apply_chat_template( + messages, tokenize=False, add_generation_prompt=True + ) + image_inputs, video_inputs = process_vision_info(messages) + inputs = self.processor( + text=[text], + images=image_inputs, + videos=video_inputs, + padding=True, + return_tensors="pt", + ) + inputs = inputs.to("cuda") + + with torch.no_grad(): + generated_ids = self.model.generate(**inputs, max_new_tokens=512) + generated_ids_trimmed = [ + out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids) + ] + response = self.processor.batch_decode( + generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False + )[0] + + # 解析响应 + assessment = self.parse_assessment_response(response) + assessment['raw_response'] = response + assessment['image_path'] = image_path + print(f"Image:\n{image_path} Assessment Result:\n{response}") + + return assessment + + except Exception as e: + logger.error(f"Error evaluating image {image_path}: {e}") + return { + 'score': 0, + 'face_quality': 'Unable to assess', + 'issues': f'Assessment failed: {str(e)}', + 'needs_regeneration': True, + 'raw_response': '', + 'image_path': image_path + } + + def parse_assessment_response(self, response: str) -> Dict[str, Any]: + """Parse assessment response""" + assessment = { + 'score': 5, # Default medium score + 'face_quality': 'Average', + 'issues': '', + 'needs_regeneration': False + } + + try: + lines = response.split('\n') + for line in lines: + line = line.strip() + if line.startswith('Score:') or line.startswith('评分:') or line.startswith('评分:'): + score_text = line.split(':')[-1].strip() + if ':' in line: + score_text = line.split(':')[-1].strip() + score_match = score_text.split('/')[0].strip() + try: + assessment['score'] = int(score_match) + except ValueError: + pass + elif line.startswith('Face Quality:') or line.startswith('脸部质量:') or line.startswith('脸部质量:'): + if ':' in line: + assessment['face_quality'] = line.split(':')[-1].strip() + elif ':' in line: + assessment['face_quality'] = line.split(':')[-1].strip() + elif line.startswith('Main Issues:') or line.startswith('主要问题:') or line.startswith('主要问题:'): + if ':' in line: + assessment['issues'] = line.split(':')[-1].strip() + elif ':' in line: + assessment['issues'] = line.split(':')[-1].strip() + elif line.startswith('Needs Regeneration:') or line.startswith('是否需要重新生成:') or line.startswith('是否需要重新生成:'): + if ':' in line: + regen_text = line.split(':')[-1].strip() + assessment['needs_regeneration'] = regen_text.lower() in ['yes', 'true', '是'] + elif ':' in line: + regen_text = line.split(':')[-1].strip() + assessment['needs_regeneration'] = regen_text == '是' + except Exception as e: + logger.warning(f"Error parsing assessment response: {e}") + + # If score is below 7, mark for regeneration + if assessment['score'] < 7: + assessment['needs_regeneration'] = True + + return assessment + + def clear_memory(self): + """Clear GPU memory""" + if self.model is not None: + del self.model + self.model = None + if self.processor is not None: + del self.processor + self.processor = None + torch.cuda.empty_cache() + gc.collect() + +class ImageGenerator: + """使用Illustrious模型重新生成图像""" + + def __init__(self, model_path: str = "models/waiNSFWIllustrious_v140.safetensors"): + self.model_path = model_path + self.pipeline = None + self.load_model() + + def load_model(self): + """加载Illustrious模型""" + logger.info("Loading Illustrious model...") + try: + self.pipeline = StableDiffusionXLPipeline.from_single_file( + self.model_path, + torch_dtype=torch.float16, + use_safetensors=True + ) + self.pipeline = self.pipeline.to("cuda") + logger.info("Illustrious model loaded successfully") + except Exception as e: + logger.error(f"Failed to load Illustrious model: {e}") + raise + + def generate_images(self, prompt_data: Dict[str, Any], num_candidates: int = 5) -> List[Image.Image]: + """ + 基于原始提示词生成多个候选图像 + + Args: + prompt_data: 包含positive_prompt和negative_prompt的字典 + num_candidates: 生成候选图像数量 + + Returns: + 生成的图像列表 + """ + positive_prompt = prompt_data.get('positive_prompt', '') + negative_prompt = prompt_data.get('negative_prompt', '') + + logger.info(f"Generating {num_candidates} candidate images") + + images = [] + try: + for i in range(num_candidates): + logger.info(f"Generating image {i+1}/{num_candidates}") + + image = self.pipeline( + prompt=positive_prompt, + negative_prompt=negative_prompt, + width=1024, + height=512, + num_inference_steps=35, + guidance_scale=7.5, + num_images_per_prompt=1, + ).images[0] + + images.append(image) + + # 清理中间内存 + torch.cuda.empty_cache() + + except Exception as e: + logger.error(f"Error generating images: {e}") + + return images + + def clear_memory(self): + """Clear GPU memory""" + if self.pipeline is not None: + del self.pipeline + self.pipeline = None + torch.cuda.empty_cache() + gc.collect() + +class QualityOptimizer: + """Image quality optimization main class""" + + def __init__(self, + illustrious_generated_dir: str = "/home/ubuntu/lyl/QwenIllustrious/illustrious_generated", + qwen_model_name: str = "Qwen/Qwen2.5-VL-7B-Instruct", + illustrious_model_path: str = "models/waiNSFWIllustrious_v140.safetensors"): + + self.illustrious_generated_dir = Path(illustrious_generated_dir) + self.metadata_dir = self.illustrious_generated_dir / "metadata" + self.candidates_dir = self.illustrious_generated_dir / "candidates" + self.improved_dir = self.illustrious_generated_dir / "improved" + + # Create necessary directories + self.candidates_dir.mkdir(exist_ok=True) + self.improved_dir.mkdir(exist_ok=True) + + self.qwen_model_name = qwen_model_name + self.illustrious_model_path = illustrious_model_path + + # Initialize components + self.assessor = None + self.generator = None + + # Result records + self.low_quality_images = [] + self.optimization_results = [] + + def scan_and_assess_images(self, batch_size: int = 50) -> List[Dict[str, Any]]: + """ + Scan and assess quality of all images + + Args: + batch_size: Batch size, clean memory after processing specified number + + Returns: + List of low-quality images + """ + logger.info("Starting to scan and assess image quality...") + + # Load Qwen-VL model + if self.assessor is None: + self.assessor = QualityAssessment(self.qwen_model_name) + + # Get all image files + image_files = list(self.illustrious_generated_dir.glob("*.png")) + total_images = len(image_files) + logger.info(f"Found {total_images} image files") + + low_quality_images = [] + processed_count = 0 + + for image_file in image_files: + try: + # 检查对应的metadata文件是否存在 + metadata_file = self.metadata_dir / f"{image_file.stem}.json" + if not metadata_file.exists(): + logger.warning(f"Missing metadata file: {metadata_file}") + continue + + # Load metadata + with open(metadata_file, 'r', encoding='utf-8') as f: + metadata = json.load(f) + + logger.info(f"Assessing image ({processed_count + 1}/{total_images}): {image_file.name}") + + # Assess image quality + assessment = self.assessor.assess_image_quality(str(image_file)) + + # If regeneration needed, record to low quality image list + if assessment['needs_regeneration']: + low_quality_record = { + 'image_file': str(image_file), + 'metadata_file': str(metadata_file), + 'metadata': metadata, + 'assessment': assessment, + 'timestamp': time.time() + } + low_quality_images.append(low_quality_record) + logger.info(f"Detected low quality image: {image_file.name} (Score: {assessment['score']}/10)") + + processed_count += 1 + + # Batch memory cleanup + if processed_count % batch_size == 0: + logger.info(f"Processed {processed_count} images, clearing memory...") + torch.cuda.empty_cache() + gc.collect() + + except Exception as e: + logger.error(f"Error processing image {image_file}: {e}") + continue + + logger.info(f"Quality assessment completed. Found {len(low_quality_images)} low quality images") + self.low_quality_images = low_quality_images + + # Save low quality image records + self.save_low_quality_records() + + return low_quality_images + + def save_low_quality_records(self): + """Save low quality image records to JSON file""" + output_file = self.illustrious_generated_dir / "low_quality_images.json" + try: + with open(output_file, 'w', encoding='utf-8') as f: + json.dump(self.low_quality_images, f, ensure_ascii=False, indent=2) + logger.info(f"Low quality image records saved to: {output_file}") + except Exception as e: + logger.error(f"Failed to save low quality image records: {e}") + + def regenerate_low_quality_images(self, max_images: int = None): + """ + Regenerate candidate images for low quality images + + Args: + max_images: Maximum number of images to process, None means process all + """ + if not self.low_quality_images: + logger.info("No low quality images need regeneration") + return + + # Clear Qwen-VL model memory, load Illustrious model + if self.assessor is not None: + self.assessor.clear_memory() + self.assessor = None + + if self.generator is None: + self.generator = ImageGenerator(self.illustrious_model_path) + + images_to_process = self.low_quality_images + if max_images is not None: + images_to_process = images_to_process[:max_images] + + logger.info(f"Starting regeneration for {len(images_to_process)} low quality images...") + + for idx, record in enumerate(images_to_process): + try: + image_hash = Path(record['image_file']).stem + logger.info(f"Regenerating image ({idx + 1}/{len(images_to_process)}): {image_hash}") + + # Generate candidate images + prompt_data = record['metadata']['original_prompt_data'] + candidate_images = self.generator.generate_images(prompt_data, num_candidates=5) + + # Save candidate images + candidate_dir = self.candidates_dir / image_hash + candidate_dir.mkdir(exist_ok=True) + + candidate_files = [] + for i, img in enumerate(candidate_images): + candidate_file = candidate_dir / f"candidate_{i+1}.png" + img.save(candidate_file) + candidate_files.append(str(candidate_file)) + + # Record generation results + generation_result = { + 'original_image': record['image_file'], + 'candidates': candidate_files, + 'generation_timestamp': time.time(), + 'original_assessment': record['assessment'] + } + + self.optimization_results.append(generation_result) + logger.info(f"Generated {len(candidate_images)} candidate images for {image_hash}") + + except Exception as e: + logger.error(f"Error regenerating image: {e}") + continue + + # Save generation results + self.save_generation_results() + + def save_generation_results(self): + """Save regeneration results""" + output_file = self.illustrious_generated_dir / "regeneration_results.json" + try: + with open(output_file, 'w', encoding='utf-8') as f: + json.dump(self.optimization_results, f, ensure_ascii=False, indent=2) + logger.info(f"Regeneration results saved to: {output_file}") + except Exception as e: + logger.error(f"Failed to save regeneration results: {e}") + + def select_best_candidates(self): + """评估候选图像并选择最佳替换""" + if not self.optimization_results: + logger.info("没有候选图像需要评估") + return + + # 清理Illustrious模型内存,重新加载Qwen-VL模型 + if self.generator is not None: + self.generator.clear_memory() + self.generator = None + + if self.assessor is None: + self.assessor = QualityAssessment(self.qwen_model_name) + + logger.info(f"开始评估 {len(self.optimization_results)} 组候选图像...") + + final_results = [] + + for idx, result in enumerate(self.optimization_results): + try: + logger.info(f"评估候选组 ({idx + 1}/{len(self.optimization_results)})") + + candidate_assessments = [] + for candidate_file in result['candidates']: + assessment = self.assessor.assess_image_quality(candidate_file) + candidate_assessments.append({ + 'file': candidate_file, + 'assessment': assessment + }) + + # 选择质量最好的候选图像 + best_candidate = max(candidate_assessments, key=lambda x: x['assessment']['score']) + + # 只有当最佳候选图像的质量明显优于原图时才替换 + original_score = result['original_assessment']['score'] + best_score = best_candidate['assessment']['score'] + + if best_score > original_score + 1: # 至少提升1分才替换 + # 复制最佳候选图像到improved目录 + original_filename = Path(result['original_image']).name + improved_file = self.improved_dir / original_filename + + best_image = Image.open(best_candidate['file']) + best_image.save(improved_file) + + final_result = { + 'original_image': result['original_image'], + 'improved_image': str(improved_file), + 'original_score': original_score, + 'improved_score': best_score, + 'improvement': best_score - original_score, + 'best_candidate_source': best_candidate['file'], + 'all_candidates': candidate_assessments + } + + logger.info(f"图像质量提升: {original_filename} " + f"({original_score}/10 -> {best_score}/10, +{best_score - original_score})") + else: + final_result = { + 'original_image': result['original_image'], + 'improved_image': None, + 'original_score': original_score, + 'best_candidate_score': best_score, + 'improvement': 0, + 'reason': '候选图像质量未达到替换标准', + 'all_candidates': candidate_assessments + } + + logger.info(f"保持原图: {Path(result['original_image']).name} " + f"(原图{original_score}/10, 最佳候选{best_score}/10)") + + final_results.append(final_result) + + except Exception as e: + logger.error(f"评估候选图像时出错: {e}") + continue + + # 保存最终结果 + self.save_final_results(final_results) + + # 生成统计报告 + self.generate_summary_report(final_results) + + def save_final_results(self, final_results: List[Dict[str, Any]]): + """保存最终优化结果""" + output_file = self.illustrious_generated_dir / "optimization_final_results.json" + try: + with open(output_file, 'w', encoding='utf-8') as f: + json.dump(final_results, f, ensure_ascii=False, indent=2) + logger.info(f"最终优化结果已保存到: {output_file}") + except Exception as e: + logger.error(f"保存最终结果失败: {e}") + + def generate_summary_report(self, final_results: List[Dict[str, Any]]): + """生成优化总结报告""" + improved_count = sum(1 for r in final_results if r.get('improved_image') is not None) + total_processed = len(final_results) + total_low_quality = len(self.low_quality_images) + + if improved_count > 0: + avg_improvement = sum(r.get('improvement', 0) for r in final_results if r.get('improvement', 0) > 0) / improved_count + else: + avg_improvement = 0 + + report = f""" +=== 图像质量优化总结报告 === + +处理统计: +- 总图像数: {len(list(self.illustrious_generated_dir.glob('*.png')))} +- 检测到低质量图像: {total_low_quality} +- 重新生成处理: {total_processed} +- 成功改善质量: {improved_count} +- 改善成功率: {improved_count/total_processed*100:.1f}% + +质量提升: +- 平均质量提升: {avg_improvement:.1f} 分 +- 改善图像保存位置: {self.improved_dir} + +详细结果文件: +- 低质量图像记录: low_quality_images.json +- 重新生成结果: regeneration_results.json +- 最终优化结果: optimization_final_results.json + +优化完成时间: {time.strftime('%Y-%m-%d %H:%M:%S')} +""" + + # 保存报告 + report_file = self.illustrious_generated_dir / "optimization_summary_report.txt" + with open(report_file, 'w', encoding='utf-8') as f: + f.write(report) + + logger.info(report) + logger.info(f"优化报告已保存到: {report_file}") + + def run_full_optimization(self, batch_size: int = 50, max_regenerate: int = None): + """ + 运行完整的优化流程 + + Args: + batch_size: 评估批处理大小 + max_regenerate: 最大重新生成图像数量 + """ + logger.info("开始完整的图像质量优化流程...") + + try: + # 步骤1: 扫描和评估所有图像 + logger.info("=== 步骤1: 图像质量评估 ===") + self.scan_and_assess_images(batch_size=batch_size) + + if not self.low_quality_images: + logger.info("未发现需要优化的低质量图像,优化流程结束") + return + + # 步骤2: 重新生成低质量图像 + logger.info("=== 步骤2: 重新生成图像 ===") + self.regenerate_low_quality_images(max_images=max_regenerate) + + # 步骤3: 评估候选图像并选择最佳替换 + logger.info("=== 步骤3: 选择最佳候选图像 ===") + self.select_best_candidates() + + logger.info("=== 图像质量优化完成 ===") + + except Exception as e: + logger.error(f"优化流程出错: {e}") + raise + finally: + # Clear memory + if self.assessor is not None: + self.assessor.clear_memory() + if self.generator is not None: + self.generator.clear_memory() + +def main(): + """主函数""" + # 配置参数 + config = { + 'illustrious_generated_dir': '/home/ubuntu/lyl/QwenIllustrious/illustrious_generated', + 'qwen_model_name': 'models/Qwen2.5-VL-7B-Instruct', + 'illustrious_model_path': 'models/waiNSFWIllustrious_v140.safetensors', + 'batch_size': 30, # 减小批处理大小以节省内存 + 'max_regenerate': 100 # 限制重新生成数量,可设为None处理全部 + } + + logger.info("启动图像质量优化系统...") + logger.info(f"配置参数: {config}") + + # 创建优化器实例 + optimizer = QualityOptimizer( + illustrious_generated_dir=config['illustrious_generated_dir'], + qwen_model_name=config['qwen_model_name'], + illustrious_model_path=config['illustrious_model_path'] + ) + + # 运行完整优化流程 + optimizer.run_full_optimization( + batch_size=config['batch_size'], + max_regenerate=config['max_regenerate'] + ) + +if __name__ == "__main__": + main() diff --git a/data_tool/improve_data_quality/run_optimizer.sh b/data_tool/improve_data_quality/run_optimizer.sh new file mode 100644 index 0000000000000000000000000000000000000000..d2cbc53895317081e2a98ed59e444f09e4bdeb14 --- /dev/null +++ b/data_tool/improve_data_quality/run_optimizer.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +# 图像质量优化系统运行脚本 + +echo "=== 图像质量优化系统 ===" +echo "请选择运行模式:" +echo "1. 测试模式 (快速验证功能)" +echo "2. 完整优化 (处理所有图像)" +echo "3. 限制优化 (处理前100张低质量图像)" + +read -p "请输入选择 (1-3): " choice + +case $choice in + 1) + echo "启动测试模式..." + cd /home/ubuntu/lyl/QwenIllustrious/data_tool/improve_data_quality + python test_optimizer.py + ;; + 2) + echo "启动完整优化模式..." + cd /home/ubuntu/lyl/QwenIllustrious/data_tool/improve_data_quality + python quality_optimizer.py + ;; + 3) + echo "启动限制优化模式..." + cd /home/ubuntu/lyl/QwenIllustrious/data_tool/improve_data_quality + python -c " +from quality_optimizer import QualityOptimizer +optimizer = QualityOptimizer() +optimizer.run_full_optimization(batch_size=20, max_regenerate=100) +" + ;; + *) + echo "无效选择" + exit 1 + ;; +esac + +echo "处理完成!" diff --git a/data_tool/improve_data_quality/test_optimizer.py b/data_tool/improve_data_quality/test_optimizer.py new file mode 100644 index 0000000000000000000000000000000000000000..8e870f9fa854001a88eb9f95c913d30266f02d26 --- /dev/null +++ b/data_tool/improve_data_quality/test_optimizer.py @@ -0,0 +1,245 @@ +#!/usr/bin/env python3 +""" +图像质量优化系统 - 测试版本 +用于快速测试评估和生成功能 +""" + +import os +import json +import gc +import time +from pathlib import Path +from typing import List, Dict, Any +from PIL import Image +import torch +from transformers import Qwen2VLForConditionalGeneration, AutoTokenizer, AutoProcessor +from qwen_vl_utils import process_vision_info +from diffusers import StableDiffusionXLPipeline +import logging + +# 配置日志 +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') +logger = logging.getLogger(__name__) + +def test_quality_assessment(image_path: str, model_name: str = "Qwen/Qwen2.5-VL-7B-Instruct"): + """测试单张图像的质量评估""" + logger.info(f"测试图像质量评估: {image_path}") + + # 加载模型 + model = Qwen2VLForConditionalGeneration.from_pretrained( + model_name, + torch_dtype=torch.bfloat16, + attn_implementation="flash_attention_2", + device_map="auto", + ) + processor = AutoProcessor.from_pretrained(model_name) + + prompt = """请仔细分析这张动漫风格图像的质量,特别关注以下方面: + +1. 角色脸部质量(重点评估): + - 脸部细节是否清晰 + - 眼睛是否对称且细节丰富 + - 鼻子和嘴巴的比例是否正确 + - 脸部轮廓是否自然 + +2. 整体图像质量: + - 线条清晰度 + - 色彩饱和度和对比度 + - 构图和比例 + +请给出总体质量评分(1-10分),并说明是否需要重新生成。 + +请按以下格式回答: +评分:X/10 +脸部质量:[好/一般/差] +主要问题:[具体描述问题] +是否需要重新生成:[是/否]""" + + try: + messages = [ + { + "role": "user", + "content": [ + {"type": "image", "image": image_path}, + {"type": "text", "text": prompt} + ], + } + ] + + text = processor.apply_chat_template( + messages, tokenize=False, add_generation_prompt=True + ) + image_inputs, video_inputs = process_vision_info(messages) + inputs = processor( + text=[text], + images=image_inputs, + videos=video_inputs, + padding=True, + return_tensors="pt", + ) + inputs = inputs.to("cuda") + + with torch.no_grad(): + generated_ids = model.generate(**inputs, max_new_tokens=512) + generated_ids_trimmed = [ + out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids) + ] + response = processor.batch_decode( + generated_ids_trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=False + )[0] + + logger.info(f"评估结果:\n{response}") + return response + + except Exception as e: + logger.error(f"评估失败: {e}") + return None + finally: + # 清理内存 + del model, processor + torch.cuda.empty_cache() + gc.collect() + +def test_image_generation(prompt_data: Dict[str, Any], model_path: str = "models/waiNSFWIllustrious_v140.safetensors"): + """测试图像生成""" + logger.info("测试图像生成功能") + + # 加载Illustrious模型 + pipeline = StableDiffusionXLPipeline.from_single_file( + model_path, + torch_dtype=torch.float16, + use_safetensors=True + ) + pipeline = pipeline.to("cuda") + + try: + positive_prompt = prompt_data.get('positive_prompt', '') + negative_prompt = prompt_data.get('negative_prompt', '') + + logger.info(f"正面提示词: {positive_prompt[:100]}...") + + image = pipeline( + prompt=positive_prompt, + negative_prompt=negative_prompt, + width=1024, + height=512, + num_inference_steps=35, + guidance_scale=7.5, + num_images_per_prompt=1, + ).images[0] + + # 保存测试图像 + output_path = "/home/ubuntu/lyl/QwenIllustrious/data_tool/improve_data_quality/test_generated.png" + image.save(output_path) + logger.info(f"测试图像已保存到: {output_path}") + + return output_path + + except Exception as e: + logger.error(f"生成失败: {e}") + return None + finally: + # 清理内存 + del pipeline + torch.cuda.empty_cache() + gc.collect() + +def quick_assessment_test(): + """快速评估测试 - 评估几张样本图像""" + illustrious_dir = Path("/home/ubuntu/lyl/QwenIllustrious/illustrious_generated") + metadata_dir = illustrious_dir / "metadata" + + # 获取前5张图像进行测试 + image_files = list(illustrious_dir.glob("*.png"))[:5] + + logger.info(f"快速测试 - 评估前 {len(image_files)} 张图像") + + results = [] + for image_file in image_files: + logger.info(f"评估: {image_file.name}") + + # 检查metadata + metadata_file = metadata_dir / f"{image_file.stem}.json" + if metadata_file.exists(): + with open(metadata_file, 'r', encoding='utf-8') as f: + metadata = json.load(f) + else: + logger.warning(f"缺少metadata: {metadata_file}") + continue + + # 评估图像 + assessment = test_quality_assessment(str(image_file)) + if assessment: + results.append({ + 'image': image_file.name, + 'assessment': assessment, + 'metadata': metadata + }) + + # 为了节省时间,每次评估后稍微休息 + time.sleep(2) + + # 保存结果 + output_file = "/home/ubuntu/lyl/QwenIllustrious/data_tool/improve_data_quality/quick_test_results.json" + with open(output_file, 'w', encoding='utf-8') as f: + json.dump(results, f, ensure_ascii=False, indent=2) + + logger.info(f"快速测试完成,结果保存到: {output_file}") + return results + +def generation_test(): + """生成测试 - 使用样本metadata生成图像""" + illustrious_dir = Path("/home/ubuntu/lyl/QwenIllustrious/illustrious_generated") + metadata_dir = illustrious_dir / "metadata" + + # 获取第一个metadata文件 + metadata_files = list(metadata_dir.glob("*.json")) + if not metadata_files: + logger.error("未找到metadata文件") + return None + + metadata_file = metadata_files[0] + logger.info(f"使用metadata: {metadata_file.name}") + + with open(metadata_file, 'r', encoding='utf-8') as f: + metadata = json.load(f) + + prompt_data = metadata['original_prompt_data'] + logger.info(f"提示词数据: {prompt_data}") + + # 生成测试图像 + generated_image = test_image_generation(prompt_data) + return generated_image + +def main(): + """主测试函数""" + logger.info("开始图像质量优化系统测试") + + # 测试选项 + test_options = { + '1': ('快速质量评估测试', quick_assessment_test), + '2': ('图像生成测试', generation_test), + '3': ('完整流程测试', lambda: logger.info("完整流程测试尚未实现")) + } + + print("请选择测试类型:") + for key, (name, _) in test_options.items(): + print(f"{key}. {name}") + + choice = input("输入选择 (1-3): ").strip() + + if choice in test_options: + name, func = test_options[choice] + logger.info(f"执行: {name}") + try: + result = func() + logger.info(f"测试完成: {name}") + return result + except Exception as e: + logger.error(f"测试失败: {e}") + raise + else: + logger.error("无效选择") + +if __name__ == "__main__": + main() diff --git a/data_tool/prompt_augmentation/README.md b/data_tool/prompt_augmentation/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b99cd41fd0738fc540d81f20ffb4904e56d3b159 --- /dev/null +++ b/data_tool/prompt_augmentation/README.md @@ -0,0 +1,102 @@ +# Prompt Augmentation Tool + +This tool uses Qwen3 to generate new prompt pairs based on examples from the civitai_image.csv dataset. + +## Features + +- Randomly samples existing prompts as examples for Qwen3 +- 40% probability of generating multi-character focused prompts +- Cleans prompts by removing technical embeddings and prefixes +- Generates 10,000 new prompt pairs by default +- **Batch saving every 10 prompts** for safety against interruptions +- **Resume capability** - automatically detects and continues from existing files +- Saves results in JSONL format for easy processing +- Includes progress tracking and error handling +- **Interrupt-safe** - saves progress even if stopped with Ctrl+C + +## Usage + +### Basic Usage +```bash +cd prepare_tool/prompt_augmentation +python augment_prompts.py +``` + +### Custom Parameters +```bash +python augment_prompts.py \ + --target_count 5000 \ + --multi_char_prob 0.3 \ + --save_every 20 \ + --output my_prompts.jsonl \ + --csv_path ../../civitai_image.csv +``` + +### Resume Generation +If your generation is interrupted, simply run the same command again. The script will automatically detect existing prompts and continue from where it left off: + +```bash +# This will continue from existing prompts_10k.jsonl if it exists +python augment_prompts.py --target_count 10000 --output prompts_10k.jsonl +``` + +### Parameters + +- `--model`: Model name (default: "Qwen/Qwen3-8B") +- `--csv_path`: Path to civitai CSV file (default: "../../civitai_image.csv") +- `--target_count`: Number of prompts to generate (default: 10000) +- `--multi_char_prob`: Probability of multi-character prompts (default: 0.4) +- `--samples_per_batch`: Number of examples to show model (default: 3) +- `--save_every`: Save to file every N successful generations (default: 10) +- `--output`: Output file name (default: "augmented_prompts.jsonl") + +## Output Format + +The script generates a JSONL file where each line is a JSON object: + +```json +{ + "positive_prompt": "detailed positive prompt here...", + "negative_prompt": "negative prompt with quality controls", + "multi_character_focus": true, + "generation_attempt": 42, + "sample_sources": ["sample 1...", "sample 2...", "sample 3..."] +} +``` + +## Example Output + +Each generated prompt pair will be similar to: + +**Multi-character focused:** +```json +{ + "positive_prompt": "masterpiece, best quality, 2girls, sitting together on park bench, one girl with long brown hair reading book aloud, other girl with short blonde hair listening intently, warm afternoon sunlight, cherry blossoms falling, detailed facial expressions, friendship, casual clothing, peaceful atmosphere", + "negative_prompt": "worst quality, low quality, bad anatomy, bad hands, blurry, watermark, signature, text" +} +``` + +**General prompt:** +```json +{ + "positive_prompt": "high quality, detailed illustration, mystical forest scene, ancient stone ruins covered in glowing moss, ethereal lighting through canopy, magical atmosphere, fantasy landscape, intricate details, vibrant colors", + "negative_prompt": "low quality, bad anatomy, blurry, watermark, signature, worst quality" +} +``` + +## Tips + +1. **Monitor Progress**: The script shows progress every 100 attempts +2. **Batch Saving**: Results are saved every 10 successful generations by default +3. **Resume Safely**: You can interrupt (Ctrl+C) and resume generation anytime +4. **Adjust Parameters**: Lower `multi_char_prob` if you want fewer multi-character prompts +5. **Change Batch Size**: Use `--save_every` to control how often data is saved +6. **GPU Memory**: The script uses "auto" device mapping, ensure sufficient GPU memory + +## Requirements + +- transformers +- torch +- pandas +- Python 3.7+ +- Sufficient GPU memory for Qwen3-8B model diff --git a/data_tool/prompt_augmentation/augment_prompts.py b/data_tool/prompt_augmentation/augment_prompts.py new file mode 100644 index 0000000000000000000000000000000000000000..b835bc7e5467d0c233c081178809c3b2e946d394 --- /dev/null +++ b/data_tool/prompt_augmentation/augment_prompts.py @@ -0,0 +1,334 @@ +#!/usr/bin/env python3 +""" +Prompt Augmentation Script for QwenIllustrious +Generates new prompt pairs using Qwen3 model based on samples from civitai_image.csv +""" + +import pandas as pd +import random +import json +import argparse +import os +from typing import List, Tuple, Dict +from transformers import AutoModelForCausalLM, AutoTokenizer +import torch + + +class PromptAugmenter: + def __init__(self, model_name: str = "Qwen/Qwen3-8B", csv_path: str = None): + """Initialize the prompt augmenter with Qwen3 model""" + self.model_name = model_name + self.csv_path = csv_path or "../../civitai_image.csv" + + print(f"Loading model: {model_name}") + self.tokenizer = AutoTokenizer.from_pretrained(model_name) + self.model = AutoModelForCausalLM.from_pretrained( + model_name, + torch_dtype="auto", + device_map="auto" + ) + print("Model loaded successfully!") + + # Load the CSV data + self.load_data() + + def load_data(self): + """Load and process the civitai CSV data""" + print(f"Loading data from: {self.csv_path}") + try: + self.df = pd.read_csv(self.csv_path) + # Clean the data - remove rows with empty prompts + self.df = self.df.dropna(subset=['prompt', 'neg prompt']) + self.df = self.df[self.df['prompt'].str.strip() != ''] + print(f"Loaded {len(self.df)} valid prompt pairs") + except Exception as e: + print(f"Error loading CSV: {e}") + raise + + def get_random_samples(self, n_samples: int = 3) -> List[Tuple[str, str]]: + """Get random prompt and negative prompt pairs from the dataset""" + samples = self.df.sample(n=min(n_samples, len(self.df))) + return [(row['prompt'], row['neg prompt']) for _, row in samples.iterrows()] + + def clean_prompt(self, prompt: str) -> str: + """Clean the prompt by removing embeddings and technical prefixes""" + # Remove embedding tags + cleaned = prompt + if 'embedding:' in cleaned: + # Remove embedding tags like "embedding:Illustrious\IllusP0s, " + parts = cleaned.split(',') + parts = [part.strip() for part in parts if not part.strip().startswith('embedding:')] + cleaned = ', '.join(parts) + + # Remove common technical prefixes + # prefixes_to_remove = ['safe_pos,', 'masterpiece,', 'best quality,'] + # for prefix in prefixes_to_remove: + # if cleaned.startswith(prefix): + # cleaned = cleaned[len(prefix):].strip() + + return cleaned.strip() + + def generate_prompt_instruction(self, samples: List[Tuple[str, str]], + multi_character_focus: bool = False) -> str: + """Generate the instruction for Qwen3 to create new prompts""" + + base_instruction = """You are an expert prompt engineer for AI image generation. I will provide you with some example prompt pairs (positive prompt and negative prompt) from a dataset. Your task is to create NEW, ORIGINAL prompt pairs that are similar in style, length, and quality to the examples. + +Here are the example prompt pairs: + +""" + + # Add sample prompts + for i, (pos_prompt, neg_prompt) in enumerate(samples, 1): + cleaned_pos = self.clean_prompt(pos_prompt) + base_instruction += f"Example {i}:\n" + base_instruction += f"Positive prompt: {cleaned_pos}\n" + base_instruction += f"Negative prompt: {neg_prompt}\n\n" + + if multi_character_focus: + specific_instruction = """Please generate 1 NEW prompt pair that emphasizes MULTIPLE CHARACTERS interacting with each other. Focus on creating scenes with 2 or more characters, their relationships, interactions, and dynamic compositions. The prompt should be detailed and similar in length to the examples.""" + else: + specific_instruction = """Please generate 1 NEW prompt pair that is creative and original, maintaining similar style, detail level, and length as the examples. Focus on creating visually interesting and detailed descriptions.""" + + format_instruction = """ + +Format your response as a JSON object with this exact structure: +{ + "positive_prompt": "your new positive prompt here", + "negative_prompt": "your new negative prompt here" +} + +Make sure: +1. The positive prompt is detailed and descriptive (similar length to examples) +2. The negative prompt includes common quality control terms +3. Both prompts are in English +4. The content is appropriate and creative +5. Return ONLY the JSON object, no additional text""" + + return base_instruction + specific_instruction + format_instruction + + def generate_with_qwen3(self, instruction: str) -> Dict[str, str]: + """Generate new prompt using Qwen3 model""" + try: + messages = [ + {"role": "user", "content": instruction} + ] + + text = self.tokenizer.apply_chat_template( + messages, + tokenize=False, + add_generation_prompt=True, + enable_thinking=True + ) + + model_inputs = self.tokenizer([text], return_tensors="pt").to(self.model.device) + + # Generate with reasonable parameters + generated_ids = self.model.generate( + **model_inputs, + max_new_tokens=2048, + temperature=0.7, + do_sample=True, + top_p=0.9, + pad_token_id=self.tokenizer.eos_token_id + ) + + output_ids = generated_ids[0][len(model_inputs.input_ids[0]):].tolist() + + # Parse thinking content + try: + index = len(output_ids) - output_ids[::-1].index(151668) # + except ValueError: + index = 0 + + thinking_content = self.tokenizer.decode(output_ids[:index], skip_special_tokens=True).strip("\n") + content = self.tokenizer.decode(output_ids[index:], skip_special_tokens=True).strip("\n") + + print(f"Thinking content: {thinking_content[:200]}...") + print(f"Response: {content[:200]}...") + + # Try to parse JSON from the response + try: + # Clean the content - sometimes model adds extra text + content = content.strip() + if content.startswith('```json'): + content = content[7:] + if content.endswith('```'): + content = content[:-3] + content = content.strip() + + result = json.loads(content) + return result + except json.JSONDecodeError as e: + print(f"JSON parsing error: {e}") + print(f"Raw content: {content}") + return None + + except Exception as e: + print(f"Generation error: {e}") + return None + + def generate_prompts(self, target_count: int = 10000, + multi_character_prob: float = 0.4, + samples_per_batch: int = 3, + output_file: str = "augmented_prompts.jsonl", + save_every: int = 10): + """Generate specified number of new prompts with periodic saving""" + + print(f"Starting prompt generation: target={target_count}, multi_char_prob={multi_character_prob}") + print(f"Saving every {save_every} successful generations to: {output_file}") + + # Check if output file already exists and count existing entries + existing_count = 0 + if os.path.exists(output_file): + try: + with open(output_file, 'r', encoding='utf-8') as f: + existing_count = sum(1 for line in f if line.strip()) + print(f"Found {existing_count} existing prompts in {output_file}") + if existing_count >= target_count: + print(f"Target already reached! ({existing_count} >= {target_count})") + return [] + except Exception as e: + print(f"Warning: Could not read existing file: {e}") + + generated_prompts = [] + successful_generations = existing_count + total_attempts = 0 + temp_batch = [] # Store prompts before writing to file + + # Open file in append mode if it exists, write mode if new + file_mode = 'a' if existing_count > 0 else 'w' + + try: + with open(output_file, file_mode, encoding='utf-8') as f: + while successful_generations < target_count: + total_attempts += 1 + + # Get random samples + samples = self.get_random_samples(samples_per_batch) + + # Decide if this should be multi-character focused + is_multi_character = random.random() < multi_character_prob + + # Generate instruction + instruction = self.generate_prompt_instruction(samples, is_multi_character) + + # Generate new prompt + print(f"\nAttempt {total_attempts} (Success: {successful_generations}/{target_count})") + print(f"Multi-character focus: {is_multi_character}") + + result = self.generate_with_qwen3(instruction) + + if result and 'positive_prompt' in result and 'negative_prompt' in result: + # Add metadata + result['multi_character_focus'] = is_multi_character + result['generation_attempt'] = total_attempts + result['sample_sources'] = [self.clean_prompt(s[0])[:100] + "..." for s in samples] + + # Add to temporary batch + temp_batch.append(result) + generated_prompts.append(result) + successful_generations += 1 + + print(f"✓ Generated prompt {successful_generations}") + print(f"Positive: {result['positive_prompt'][:100]}...") + print(f"Negative: {result['negative_prompt'][:50]}...") + + # Save batch every N successful generations + if len(temp_batch) >= save_every: + for prompt in temp_batch: + f.write(json.dumps(prompt, ensure_ascii=False) + '\n') + f.flush() + print(f"💾 Saved batch of {len(temp_batch)} prompts to file") + temp_batch = [] + + else: + print("✗ Failed to generate valid prompt") + + # Progress update every 100 attempts + if total_attempts % 100 == 0: + success_rate = successful_generations / total_attempts * 100 + print(f"\n=== Progress Update ===") + print(f"Attempts: {total_attempts}") + print(f"Successful: {successful_generations}") + print(f"Success rate: {success_rate:.1f}%") + print(f"Remaining: {target_count - successful_generations}") + print(f"Current batch size: {len(temp_batch)}") + + # Save any remaining prompts in the final batch + if temp_batch: + for prompt in temp_batch: + f.write(json.dumps(prompt, ensure_ascii=False) + '\n') + f.flush() + print(f"💾 Saved final batch of {len(temp_batch)} prompts to file") + + except KeyboardInterrupt: + print(f"\n🛑 Generation interrupted by user!") + # Save any remaining prompts before exiting + if temp_batch: + with open(output_file, 'a', encoding='utf-8') as f: + for prompt in temp_batch: + f.write(json.dumps(prompt, ensure_ascii=False) + '\n') + f.flush() + print(f"💾 Saved {len(temp_batch)} prompts before exit") + raise + except Exception as e: + print(f"❌ Unexpected error: {e}") + # Save any remaining prompts before exiting + if temp_batch: + try: + with open(output_file, 'a', encoding='utf-8') as f: + for prompt in temp_batch: + f.write(json.dumps(prompt, ensure_ascii=False) + '\n') + f.flush() + print(f"💾 Saved {len(temp_batch)} prompts before error exit") + except: + pass + raise + + print(f"\n=== Generation Complete ===") + print(f"Total attempts: {total_attempts}") + print(f"Successful generations: {successful_generations}") + print(f"Success rate: {successful_generations/total_attempts*100:.1f}%") + print(f"Output saved to: {output_file}") + + return generated_prompts + + +def main(): + parser = argparse.ArgumentParser(description='Generate augmented prompts using Qwen3') + parser.add_argument('--model', default='models/Qwen3-8B', + help='Model name or path') + parser.add_argument('--csv_path', default='civitai_image.csv', + help='Path to civitai CSV file') + parser.add_argument('--target_count', type=int, default=10000, + help='Number of prompts to generate') + parser.add_argument('--multi_char_prob', type=float, default=0.4, + help='Probability of multi-character focused prompts') + parser.add_argument('--samples_per_batch', type=int, default=3, + help='Number of sample prompts to show to model each time') + parser.add_argument('--save_every', type=int, default=10, + help='Save to file every N successful generations') + parser.add_argument('--output', default='augmented_prompts.jsonl', + help='Output file name') + + args = parser.parse_args() + + # Initialize augmenter + augmenter = PromptAugmenter( + model_name=args.model, + csv_path=args.csv_path + ) + + # Generate prompts + augmenter.generate_prompts( + target_count=args.target_count, + multi_character_prob=args.multi_char_prob, + samples_per_batch=args.samples_per_batch, + output_file=args.output, + save_every=args.save_every + ) + + +if __name__ == "__main__": + main() diff --git a/data_tool/prompt_augmentation/run.sh b/data_tool/prompt_augmentation/run.sh new file mode 100644 index 0000000000000000000000000000000000000000..6d80ac5d118e2105f0e6c66b60e294e84f9ac0da --- /dev/null +++ b/data_tool/prompt_augmentation/run.sh @@ -0,0 +1,76 @@ +#!/bin/bash +# Quick start script for prompt augmentation + +echo "🚀 QwenIllustrious Prompt Augmentation Tool" +echo "===========================================" + +# Check if we're in the right directory +if [ ! -f "augment_prompts.py" ]; then + echo "❌ Please run this script from the prompt_augmentation directory" + exit 1 +fi + +# Check if CSV file exists +if [ ! -f "../../civitai_image.csv" ]; then + echo "❌ civitai_image.csv not found in expected location" + echo "Please ensure the CSV file is at: ../../civitai_image.csv" + exit 1 +fi + +echo "📋 Available options:" +echo "1. Run test (generate 2 prompts to verify setup)" +echo "2. Generate 1,000 prompts (quick run)" +echo "3. Generate 10,000 prompts (full run)" +echo "4. Resume/continue existing generation" +echo "5. Custom parameters" +echo "" + +read -p "Choose option (1-5): " choice + +case $choice in + 1) + echo "🧪 Running test..." + python test_augmentation.py + ;; + 2) + echo "🔄 Generating 1,000 prompts..." + python augment_prompts.py --target_count 1000 --output prompts_1k.jsonl + ;; + 3) + echo "🔄 Generating 10,000 prompts (this will take a while)..." + python augment_prompts.py --target_count 10000 --output prompts_10k.jsonl + ;; + 4) + echo "🔄 Resuming/continuing existing generation..." + echo "Available output files:" + ls -la *.jsonl 2>/dev/null || echo "No existing .jsonl files found" + echo "" + read -p "Enter output filename to continue (e.g., prompts_10k.jsonl): " resume_file + read -p "Target count (default 10000): " target + target=${target:-10000} + + echo "🔄 Continuing generation to reach $target prompts in $resume_file..." + python augment_prompts.py --target_count $target --output "$resume_file" + ;; + 5) + echo "Enter custom parameters:" + read -p "Target count (default 1000): " target + read -p "Multi-character probability (default 0.4): " prob + read -p "Save every N prompts (default 10): " save_every + read -p "Output filename (default custom_prompts.jsonl): " output + + target=${target:-1000} + prob=${prob:-0.4} + save_every=${save_every:-10} + output=${output:-custom_prompts.jsonl} + + echo "🔄 Generating $target prompts with multi-char prob $prob, saving every $save_every..." + python augment_prompts.py --target_count $target --multi_char_prob $prob --save_every $save_every --output $output + ;; + *) + echo "❌ Invalid option" + exit 1 + ;; +esac + +echo "✅ Done! Check the output files in this directory." diff --git a/data_tool/prompt_augmentation/test_augmentation.py b/data_tool/prompt_augmentation/test_augmentation.py new file mode 100644 index 0000000000000000000000000000000000000000..37dde60919a285ec8f23df4f5f57fb5d30084f8d --- /dev/null +++ b/data_tool/prompt_augmentation/test_augmentation.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +""" +Test script for prompt augmentation +Generates a small number of prompts to test the functionality +""" + +import sys +import os +sys.path.append(os.path.dirname(__file__)) + +from augment_prompts import PromptAugmenter + + +def test_prompt_generation(): + """Test the prompt generation with a small sample""" + print("=== Testing Prompt Augmentation ===\n") + + # Initialize with a small test + try: + augmenter = PromptAugmenter(csv_path="../../civitai_image.csv") + print("✓ Model loaded successfully") + except Exception as e: + print(f"✗ Error loading model: {e}") + return False + + # Test data loading + try: + print(f"✓ Loaded {len(augmenter.df)} prompt pairs from CSV") + except Exception as e: + print(f"✗ Error loading data: {e}") + return False + + # Test sample generation + try: + samples = augmenter.get_random_samples(3) + print(f"✓ Generated {len(samples)} sample prompts") + + for i, (pos, neg) in enumerate(samples, 1): + print(f" Sample {i}: {pos[:50]}...") + except Exception as e: + print(f"✗ Error getting samples: {e}") + return False + + # Test prompt cleaning + try: + sample_prompt = samples[0][0] + cleaned = augmenter.clean_prompt(sample_prompt) + print(f"✓ Prompt cleaning works") + print(f" Original: {sample_prompt[:60]}...") + print(f" Cleaned: {cleaned[:60]}...") + except Exception as e: + print(f"✗ Error cleaning prompt: {e}") + return False + + # Test instruction generation + try: + instruction = augmenter.generate_prompt_instruction(samples, multi_character_focus=True) + print(f"✓ Generated instruction ({len(instruction)} characters)") + except Exception as e: + print(f"✗ Error generating instruction: {e}") + return False + + # Test actual generation (just 2 prompts) + print("\n=== Testing Actual Generation ===") + try: + results = augmenter.generate_prompts( + target_count=2, + multi_character_prob=0.5, + save_every=1, # Save every prompt for testing + output_file="test_output.jsonl" + ) + print(f"✓ Successfully generated {len(results)} prompts") + + for i, result in enumerate(results, 1): + print(f"\nGenerated Prompt {i}:") + print(f" Positive: {result['positive_prompt'][:80]}...") + print(f" Negative: {result['negative_prompt'][:50]}...") + print(f" Multi-char: {result['multi_character_focus']}") + + return True + + except Exception as e: + print(f"✗ Error in generation: {e}") + return False + + +if __name__ == "__main__": + success = test_prompt_generation() + if success: + print("\n🎉 All tests passed! The augmentation script is ready to use.") + print("\nTo generate 10,000 prompts, run:") + print("python augment_prompts.py") + else: + print("\n❌ Tests failed. Please check the errors above.") diff --git a/sentence-transformers/sentence_transformers/__pycache__/LoggingHandler.cpython-312.pyc b/sentence-transformers/sentence_transformers/__pycache__/LoggingHandler.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ee038cf623c3cd804d06bd971acc5cf58dbd72c2 Binary files /dev/null and b/sentence-transformers/sentence_transformers/__pycache__/LoggingHandler.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/__pycache__/__init__.cpython-312.pyc b/sentence-transformers/sentence_transformers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca9bb2650192c9e45b3cc43de004fe877c908037 Binary files /dev/null and b/sentence-transformers/sentence_transformers/__pycache__/__init__.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/__pycache__/backend.cpython-312.pyc b/sentence-transformers/sentence_transformers/__pycache__/backend.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ab73410ce3bb75489708589b50ea8fcc38b07931 Binary files /dev/null and b/sentence-transformers/sentence_transformers/__pycache__/backend.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/__pycache__/data_collator.cpython-312.pyc b/sentence-transformers/sentence_transformers/__pycache__/data_collator.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5fb41cb5a7fbbc9251848e37f8a369462bed1b45 Binary files /dev/null and b/sentence-transformers/sentence_transformers/__pycache__/data_collator.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/__pycache__/fit_mixin.cpython-312.pyc b/sentence-transformers/sentence_transformers/__pycache__/fit_mixin.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d0acd361228e0b8a81e7f8fd73405e933e11377c Binary files /dev/null and b/sentence-transformers/sentence_transformers/__pycache__/fit_mixin.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/__pycache__/model_card.cpython-312.pyc b/sentence-transformers/sentence_transformers/__pycache__/model_card.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..02f1fdbe878b619b7bf4009dbe929cb0e70da7e3 Binary files /dev/null and b/sentence-transformers/sentence_transformers/__pycache__/model_card.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/__pycache__/model_card_templates.cpython-312.pyc b/sentence-transformers/sentence_transformers/__pycache__/model_card_templates.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..91fb1d86c75b5b6a6d574c114b773c3e60315e10 Binary files /dev/null and b/sentence-transformers/sentence_transformers/__pycache__/model_card_templates.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/__pycache__/peft_mixin.cpython-312.pyc b/sentence-transformers/sentence_transformers/__pycache__/peft_mixin.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7a4af32bc4727e3bdc891d8547991ffd981aa5fe Binary files /dev/null and b/sentence-transformers/sentence_transformers/__pycache__/peft_mixin.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/__pycache__/quantization.cpython-312.pyc b/sentence-transformers/sentence_transformers/__pycache__/quantization.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bb5372d2fb148c47b80dcfb918e9b18e2eb37e76 Binary files /dev/null and b/sentence-transformers/sentence_transformers/__pycache__/quantization.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/__pycache__/sampler.cpython-312.pyc b/sentence-transformers/sentence_transformers/__pycache__/sampler.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..17dfa4aae42d2701f940aa88c97651ed17e3ec7b Binary files /dev/null and b/sentence-transformers/sentence_transformers/__pycache__/sampler.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/__pycache__/similarity_functions.cpython-312.pyc b/sentence-transformers/sentence_transformers/__pycache__/similarity_functions.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dca43a7f1371a4e93d97f48981715c35a644edb0 Binary files /dev/null and b/sentence-transformers/sentence_transformers/__pycache__/similarity_functions.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/__pycache__/trainer.cpython-312.pyc b/sentence-transformers/sentence_transformers/__pycache__/trainer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1017e2ff948f0b2806d0d98eb82aa898eef603a9 Binary files /dev/null and b/sentence-transformers/sentence_transformers/__pycache__/trainer.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/__pycache__/training_args.cpython-312.pyc b/sentence-transformers/sentence_transformers/__pycache__/training_args.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..965b2b97549692f179b088442e03512e2ffb4a1b Binary files /dev/null and b/sentence-transformers/sentence_transformers/__pycache__/training_args.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/__pycache__/util.cpython-312.pyc b/sentence-transformers/sentence_transformers/__pycache__/util.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bbf3dee04fc55f9755621cf2dab2584bb09d2374 Binary files /dev/null and b/sentence-transformers/sentence_transformers/__pycache__/util.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/CrossEncoder.cpython-312.pyc b/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/CrossEncoder.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca25c7f1201d13a8aa159f40e5d88369ba4e2961 Binary files /dev/null and b/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/CrossEncoder.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/__init__.cpython-312.pyc b/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bb949b61a49dac7c60580926085ffd9c8688c440 Binary files /dev/null and b/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/__init__.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/data_collator.cpython-312.pyc b/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/data_collator.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4bbc67faf1b7f7267181b3cb3832e665d6ccc9f0 Binary files /dev/null and b/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/data_collator.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/fit_mixin.cpython-312.pyc b/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/fit_mixin.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2f6f8d34c73ee609d6946973526a9ce6b02b24e9 Binary files /dev/null and b/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/fit_mixin.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/model_card.cpython-312.pyc b/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/model_card.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7a522a88c3d3b68cf304666681cecd3b6b2bdff9 Binary files /dev/null and b/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/model_card.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/trainer.cpython-312.pyc b/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/trainer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..113d41cb392439bf2aeeef901381e02959cc871d Binary files /dev/null and b/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/trainer.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/training_args.cpython-312.pyc b/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/training_args.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1471b1ed4b1cc0d79b8c94b7eef8507eb67135e8 Binary files /dev/null and b/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/training_args.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/util.cpython-312.pyc b/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/util.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3a3e6ae353a50fba005a0aa3a9e8a777cc75065a Binary files /dev/null and b/sentence-transformers/sentence_transformers/cross_encoder/__pycache__/util.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/cross_encoder/evaluation/__init__.py b/sentence-transformers/sentence_transformers/cross_encoder/evaluation/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d41f879842e4a29c96bc065af268aedbf8b75a01 --- /dev/null +++ b/sentence-transformers/sentence_transformers/cross_encoder/evaluation/__init__.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +import sys + +from .classification import CrossEncoderClassificationEvaluator +from .correlation import CrossEncoderCorrelationEvaluator +from .deprecated import ( + CEBinaryAccuracyEvaluator, + CEBinaryClassificationEvaluator, + CECorrelationEvaluator, + CEF1Evaluator, + CERerankingEvaluator, + CESoftmaxAccuracyEvaluator, +) +from .nano_beir import CrossEncoderNanoBEIREvaluator +from .reranking import CrossEncoderRerankingEvaluator + +# Ensure that imports using deprecated paths still work +# Although importing via `from sentence_transformers.cross_encoder.evaluation import ...` is recommended +deprecated_modules = [ + "sentence_transformers.cross_encoder.evaluation.CEBinaryAccuracyEvaluator", + "sentence_transformers.cross_encoder.evaluation.CEBinaryClassificationEvaluator", + "sentence_transformers.cross_encoder.evaluation.CEF1Evaluator", + "sentence_transformers.cross_encoder.evaluation.CESoftmaxAccuracyEvaluator", + "sentence_transformers.cross_encoder.evaluation.CECorrelationEvaluator", + "sentence_transformers.cross_encoder.evaluation.CERerankingEvaluator", +] +for module in deprecated_modules: + sys.modules[module] = sys.modules["sentence_transformers.cross_encoder.evaluation.deprecated"] + +__all__ = [ + "CrossEncoderClassificationEvaluator", + "CrossEncoderCorrelationEvaluator", + "CrossEncoderRerankingEvaluator", + "CrossEncoderNanoBEIREvaluator", + # Deprecated: + "CERerankingEvaluator", + "CECorrelationEvaluator", + "CEBinaryAccuracyEvaluator", + "CEBinaryClassificationEvaluator", + "CEF1Evaluator", + "CESoftmaxAccuracyEvaluator", +] diff --git a/sentence-transformers/sentence_transformers/cross_encoder/evaluation/classification.py b/sentence-transformers/sentence_transformers/cross_encoder/evaluation/classification.py new file mode 100644 index 0000000000000000000000000000000000000000..ea4c3b0ec6c9a1668142c80f194557ac0cebd26b --- /dev/null +++ b/sentence-transformers/sentence_transformers/cross_encoder/evaluation/classification.py @@ -0,0 +1,180 @@ +from __future__ import annotations + +import csv +import logging +import os +from typing import TYPE_CHECKING + +import numpy as np +from sklearn.metrics import average_precision_score, f1_score + +from sentence_transformers.evaluation import BinaryClassificationEvaluator +from sentence_transformers.evaluation.SentenceEvaluator import SentenceEvaluator + +if TYPE_CHECKING: + from sentence_transformers.cross_encoder.CrossEncoder import CrossEncoder + +logger = logging.getLogger(__name__) + + +class CrossEncoderClassificationEvaluator(SentenceEvaluator): + """ + Evaluate a CrossEncoder model based on the accuracy of the predicted class vs. the gold labels. + The evaluator expects a list of sentence pairs and a list of gold labels. If the model has a single output, + it is assumed to be a binary classification model and the evaluator will calculate accuracy, F1, precision, recall, + and average precision. If the model has multiple outputs, the evaluator will calculate macro F1, micro F1, and + weighted F1. + + Args: + sentence_pairs (List[List[str]]): A list of sentence pairs with each element being a list of two strings. + labels (List[int]): A list of integers with the gold labels for each sentence pair. + name (str): Name of the evaluator, useful for the generated model card. + batch_size (int): Batch size used for the evaluation. Defaults to 32. + show_progress_bar (bool): Output a progress bar. Defaults to None, which shows the progress bar if the logging level is INFO or DEBUG. + write_csv (bool): Write results to a CSV file. If a CSV already exists, then values are appended. Defaults to True. + + Example: + :: + + from sentence_transformers import CrossEncoder + from sentence_transformers.cross_encoder.evaluation import CrossEncoderClassificationEvaluator + from datasets import load_dataset + + # Load a model + model = CrossEncoder("cross-encoder/nli-deberta-v3-base") + + # Load a dataset with two text columns and a class label column (https://huggingface.co/datasets/sentence-transformers/all-nli) + eval_dataset = load_dataset("sentence-transformers/all-nli", "pair-class", split="dev[-1000:]") + + # Create a list of pairs, and map the labels to the labels that the model knows + pairs = list(zip(eval_dataset["premise"], eval_dataset["hypothesis"])) + label_mapping = {0: 1, 1: 2, 2: 0} + labels = [label_mapping[label] for label in eval_dataset["label"]] + + # Initialize the evaluator + cls_evaluator = CrossEncoderClassificationEvaluator( + sentence_pairs=pairs, + labels=labels, + name="all-nli-dev", + ) + results = cls_evaluator(model) + ''' + CrossEncoderClassificationEvaluator: Evaluating the model on all-nli-dev dataset: + Macro F1: 89.43 + Micro F1: 89.30 + Weighted F1: 89.33 + ''' + print(cls_evaluator.primary_metric) + # => all-nli-dev_f1_macro + print(results[cls_evaluator.primary_metric]) + # => 0.8942858180262628 + """ + + def __init__( + self, + sentence_pairs: list[list[str]], + labels: list[int], + *, + name: str = "", + batch_size: int = 32, + show_progress_bar: bool | None = None, + write_csv: bool = True, + **kwargs, + ): + if len(sentence_pairs) != len(labels): + raise ValueError("sentence_pairs and labels must have the same length") + + self.sentence_pairs = sentence_pairs + self.labels = np.asarray(labels) + self.name = name + self.batch_size = batch_size + if show_progress_bar is None: + show_progress_bar = logger.getEffectiveLevel() in (logging.INFO, logging.DEBUG) + self.show_progress_bar = show_progress_bar + + self.csv_file = "CrossEncoderClassificationEvaluator" + ("_" + name if name else "") + "_results.csv" + self.write_csv = write_csv + + def __call__( + self, model: CrossEncoder, output_path: str | None = None, epoch: int = -1, steps: int = -1 + ) -> dict[str, float]: + if epoch != -1: + if steps == -1: + out_txt = f" after epoch {epoch}" + else: + out_txt = f" in epoch {epoch} after {steps} steps" + else: + out_txt = "" + + logger.info(f"CrossEncoderClassificationEvaluator: Evaluating the model on {self.name} dataset{out_txt}:") + pred_scores = model.predict( + self.sentence_pairs, convert_to_numpy=True, show_progress_bar=self.show_progress_bar + ) + + if model.num_labels == 1: + acc, acc_threshold = BinaryClassificationEvaluator.find_best_acc_and_threshold( + pred_scores, self.labels, True + ) + f1, precision, recall, f1_threshold = BinaryClassificationEvaluator.find_best_f1_and_threshold( + pred_scores, self.labels, True + ) + ap = average_precision_score(self.labels, pred_scores) + + logger.info(f"Accuracy: {acc * 100:.2f}\t(Threshold: {acc_threshold:.4f})") + logger.info(f"F1: {f1 * 100:.2f}\t(Threshold: {f1_threshold:.4f})") + logger.info(f"Precision: {precision * 100:.2f}") + logger.info(f"Recall: {recall * 100:.2f}") + logger.info(f"Average Precision: {ap * 100:.2f}") + + metrics = { + "accuracy": acc, + "accuracy_threshold": acc_threshold, + "f1": f1, + "f1_threshold": f1_threshold, + "precision": precision, + "recall": recall, + "average_precision": ap, + } + self.csv_headers = [ + "epoch", + "steps", + "Accuracy", + "Accuracy_Threshold", + "F1", + "F1_Threshold", + "Precision", + "Recall", + "Average_Precision", + ] + self.primary_metric = "average_precision" + else: + pred_labels = np.argmax(pred_scores, axis=1) + f1_macro = f1_score(self.labels, pred_labels, average="macro") + f1_micro = f1_score(self.labels, pred_labels, average="micro") + f1_weighted = f1_score(self.labels, pred_labels, average="weighted") + + logger.info(f"Macro F1: {f1_macro * 100:.2f}") + logger.info(f"Micro F1: {f1_micro * 100:.2f}") + logger.info(f"Weighted F1: {f1_weighted * 100:.2f}") + + metrics = { + "f1_macro": f1_macro, + "f1_micro": f1_micro, + "f1_weighted": f1_weighted, + } + self.csv_headers = ["epoch", "steps", "Macro_F1", "Micro_F1", "Weighted_F1"] + self.primary_metric = "f1_macro" + + if output_path is not None and self.write_csv: + csv_path = os.path.join(output_path, self.csv_file) + output_file_exists = os.path.isfile(csv_path) + with open(csv_path, mode="a" if output_file_exists else "w", encoding="utf-8") as f: + writer = csv.writer(f) + if not output_file_exists: + writer.writerow(self.csv_headers) + + writer.writerow([epoch, steps, *metrics.values()]) + + metrics = self.prefix_name_to_metrics(metrics, self.name) + self.store_metrics_in_model_card_data(model, metrics, epoch, steps) + return metrics diff --git a/sentence-transformers/sentence_transformers/cross_encoder/evaluation/correlation.py b/sentence-transformers/sentence_transformers/cross_encoder/evaluation/correlation.py new file mode 100644 index 0000000000000000000000000000000000000000..69ec55eebe3bb6b545be44f56c168138db60dd2c --- /dev/null +++ b/sentence-transformers/sentence_transformers/cross_encoder/evaluation/correlation.py @@ -0,0 +1,131 @@ +from __future__ import annotations + +import csv +import logging +import os +from typing import TYPE_CHECKING + +from scipy.stats import pearsonr, spearmanr + +from sentence_transformers import InputExample +from sentence_transformers.evaluation.SentenceEvaluator import SentenceEvaluator + +if TYPE_CHECKING: + from sentence_transformers.cross_encoder.CrossEncoder import CrossEncoder + +logger = logging.getLogger(__name__) + + +class CrossEncoderCorrelationEvaluator(SentenceEvaluator): + """ + This evaluator can be used with the CrossEncoder class. Given sentence pairs and continuous scores, + it compute the pearson & spearman correlation between the predicted score for the sentence pair + and the gold score. + + Args: + sentence_pairs (List[List[str]]): A list of sentence pairs with each element being a list of two strings. + labels (List[int]): A list of integers with the gold labels for each sentence pair. + name (str): Name of the evaluator, useful for the generated model card. + batch_size (int): Batch size used for the evaluation. Defaults to 32. + show_progress_bar (bool): Output a progress bar. Defaults to None, which shows the progress bar if the logging level is INFO or DEBUG. + write_csv (bool): Write results to a CSV file. If a CSV already exists, then values are appended. Defaults to True. + + Examples: + :: + + from datasets import load_dataset + from sentence_transformers import CrossEncoder + from sentence_transformers.cross_encoder.evaluation import CrossEncoderCorrelationEvaluator + + # Load a model + model = CrossEncoder("cross-encoder/ms-marco-MiniLM-L6-v2") + + # Load the STSB dataset (https://huggingface.co/datasets/sentence-transformers/stsb) + eval_dataset = load_dataset("sentence-transformers/stsb", split="validation") + pairs = list(zip(eval_dataset["sentence1"], eval_dataset["sentence2"])) + + # Initialize the evaluator + dev_evaluator = CrossEncoderCorrelationEvaluator( + sentence_pairs=pairs, + scores=eval_dataset["score"], + name="sts_dev", + ) + results = dev_evaluator(model) + ''' + CrossEncoderCorrelationEvaluator: Evaluating the model on sts_dev dataset: + Correlation: Pearson: 0.8503 Spearman: 0.8486 + ''' + print(dev_evaluator.primary_metric) + # => sts_dev_spearman + print(results[dev_evaluator.primary_metric]) + # => 0.8486467897872038 + """ + + def __init__( + self, + sentence_pairs: list[list[str]], + scores: list[float], + name: str = "", + batch_size: int = 32, + show_progress_bar: bool | None = None, + write_csv: bool = True, + ): + self.sentence_pairs = sentence_pairs + self.scores = scores + self.name = name + self.batch_size = batch_size + if show_progress_bar is None: + show_progress_bar = logger.getEffectiveLevel() in (logging.INFO, logging.DEBUG) + self.show_progress_bar = show_progress_bar + self.write_csv = write_csv + + self.csv_file = "CrossEncoderCorrelationEvaluator" + ("_" + name if name else "") + "_results.csv" + self.csv_headers = ["epoch", "steps", "Pearson_Correlation", "Spearman_Correlation"] + self.primary_metric = "spearman" + + @classmethod + def from_input_examples(cls, examples: list[InputExample], **kwargs): + sentence_pairs = [] + scores = [] + + for example in examples: + sentence_pairs.append(example.texts) + scores.append(example.label) + return cls(sentence_pairs, scores, **kwargs) + + def __call__( + self, model: CrossEncoder, output_path: str | None = None, epoch: int = -1, steps: int = -1 + ) -> dict[str, float]: + if epoch != -1: + if steps == -1: + out_txt = f" after epoch {epoch}" + else: + out_txt = f" in epoch {epoch} after {steps} steps" + else: + out_txt = "" + + logger.info(f"CrossEncoderCorrelationEvaluator: Evaluating the model on {self.name} dataset{out_txt}:") + pred_scores = model.predict(self.sentence_pairs, convert_to_numpy=True, show_progress_bar=False) + + eval_pearson, _ = pearsonr(self.scores, pred_scores) + eval_spearman, _ = spearmanr(self.scores, pred_scores) + + logger.info(f"Correlation:\tPearson: {eval_pearson:.4f}\tSpearman: {eval_spearman:.4f}") + + if output_path is not None and self.write_csv: + csv_path = os.path.join(output_path, self.csv_file) + output_file_exists = os.path.isfile(csv_path) + with open(csv_path, mode="a" if output_file_exists else "w", encoding="utf-8") as f: + writer = csv.writer(f) + if not output_file_exists: + writer.writerow(self.csv_headers) + + writer.writerow([epoch, steps, eval_pearson, eval_spearman]) + + metrics = { + "pearson": eval_pearson, + "spearman": eval_spearman, + } + metrics = self.prefix_name_to_metrics(metrics, self.name) + self.store_metrics_in_model_card_data(model, metrics, epoch, steps) + return metrics diff --git a/sentence-transformers/sentence_transformers/cross_encoder/evaluation/deprecated.py b/sentence-transformers/sentence_transformers/cross_encoder/evaluation/deprecated.py new file mode 100644 index 0000000000000000000000000000000000000000..a4b1693343d2620652717c8b5458451813861af6 --- /dev/null +++ b/sentence-transformers/sentence_transformers/cross_encoder/evaluation/deprecated.py @@ -0,0 +1,108 @@ +from __future__ import annotations + +from typing_extensions import deprecated + +from sentence_transformers import InputExample +from sentence_transformers.cross_encoder.evaluation.classification import CrossEncoderClassificationEvaluator +from sentence_transformers.cross_encoder.evaluation.correlation import CrossEncoderCorrelationEvaluator +from sentence_transformers.cross_encoder.evaluation.reranking import CrossEncoderRerankingEvaluator + + +@deprecated( + "This evaluator has been deprecated in favor of the more general CrossEncoderClassificationEvaluator. " + "Please use CrossEncoderClassificationEvaluator instead, which supports both binary and multi-class " + "evaluation. It accepts approximately the same inputs as this evaluator." +) +class CEBinaryAccuracyEvaluator(CrossEncoderClassificationEvaluator): + """ + This evaluator has been deprecated in favor of the more general CrossEncoderClassificationEvaluator. + """ + + @classmethod + def from_input_examples(cls, examples: list[InputExample], **kwargs): + sentence_pairs = [] + labels = [] + + for example in examples: + sentence_pairs.append(example.texts) + labels.append(example.label) + return cls(sentence_pairs, labels, **kwargs) + + +@deprecated( + "This evaluator has been deprecated in favor of the more general CrossEncoderClassificationEvaluator. " + "Please use CrossEncoderClassificationEvaluator instead, which supports both binary and multi-class " + "evaluation. It accepts approximately the same inputs as this evaluator." +) +class CEBinaryClassificationEvaluator(CrossEncoderClassificationEvaluator): + """ + This evaluator has been deprecated in favor of the more general CrossEncoderClassificationEvaluator. + """ + + @classmethod + def from_input_examples(cls, examples: list[InputExample], **kwargs): + sentence_pairs = [] + labels = [] + + for example in examples: + sentence_pairs.append(example.texts) + labels.append(example.label) + return cls(sentence_pairs, labels, **kwargs) + + +@deprecated( + "This evaluator has been deprecated in favor of the more general CrossEncoderClassificationEvaluator. " + "Please use CrossEncoderClassificationEvaluator instead, which supports both binary and multi-class " + "evaluation. It accepts approximately the same inputs as this evaluator." +) +class CEF1Evaluator(CrossEncoderClassificationEvaluator): + """ + This evaluator has been deprecated in favor of the more general CrossEncoderClassificationEvaluator. + """ + + @classmethod + def from_input_examples(cls, examples: list[InputExample], **kwargs): + sentence_pairs = [] + labels = [] + + for example in examples: + sentence_pairs.append(example.texts) + labels.append(example.label) + return cls(sentence_pairs, labels, **kwargs) + + +@deprecated( + "This evaluator has been deprecated in favor of the more general CrossEncoderClassificationEvaluator. " + "Please use CrossEncoderClassificationEvaluator instead, which supports both binary and multi-class " + "evaluation. It accepts approximately the same inputs as this evaluator." +) +class CESoftmaxAccuracyEvaluator(CrossEncoderClassificationEvaluator): + """ + This evaluator has been deprecated in favor of the more general CrossEncoderClassificationEvaluator. + """ + + @classmethod + def from_input_examples(cls, examples: list[InputExample], **kwargs): + sentence_pairs = [] + labels = [] + + for example in examples: + sentence_pairs.append(example.texts) + labels.append(example.label) + return cls(sentence_pairs, labels, **kwargs) + + +@deprecated( + "The CECorrelationEvaluator has been renamed to CrossEncoderCorrelationEvaluator. " + "Please use CrossEncoderCorrelationEvaluator instead." +) +class CECorrelationEvaluator(CrossEncoderCorrelationEvaluator): + pass + + +@deprecated( + "The CERerankingEvaluator has been renamed to CrossEncoderCorrelationEvaluator. " + "Please use CrossEncoderCorrelationEvaluator instead." +) +class CERerankingEvaluator(CrossEncoderRerankingEvaluator): + pass diff --git a/sentence-transformers/sentence_transformers/cross_encoder/evaluation/nano_beir.py b/sentence-transformers/sentence_transformers/cross_encoder/evaluation/nano_beir.py new file mode 100644 index 0000000000000000000000000000000000000000..5dde4ce09fe4b7189d06e3b345d31a99a61483e2 --- /dev/null +++ b/sentence-transformers/sentence_transformers/cross_encoder/evaluation/nano_beir.py @@ -0,0 +1,346 @@ +from __future__ import annotations + +import logging +import os +from typing import TYPE_CHECKING, Callable, Literal + +import numpy as np +from tqdm import tqdm + +from sentence_transformers.cross_encoder.evaluation.reranking import CrossEncoderRerankingEvaluator +from sentence_transformers.evaluation.SentenceEvaluator import SentenceEvaluator +from sentence_transformers.util import is_datasets_available + +if TYPE_CHECKING: + from sentence_transformers.cross_encoder.CrossEncoder import CrossEncoder + +logger = logging.getLogger(__name__) + +DatasetNameType = Literal[ + "climatefever", + "dbpedia", + "fever", + "fiqa2018", + "hotpotqa", + "msmarco", + "nfcorpus", + "nq", + "quoraretrieval", + "scidocs", + "arguana", + "scifact", + "touche2020", +] + +dataset_name_to_id = { + "climatefever": "sentence-transformers/NanoClimateFEVER-bm25", + "dbpedia": "sentence-transformers/NanoDBPedia-bm25", + "fever": "sentence-transformers/NanoFEVER-bm25", + "fiqa2018": "sentence-transformers/NanoFiQA2018-bm25", + "hotpotqa": "sentence-transformers/NanoHotpotQA-bm25", + "msmarco": "sentence-transformers/NanoMSMARCO-bm25", + "nfcorpus": "sentence-transformers/NanoNFCorpus-bm25", + "nq": "sentence-transformers/NanoNQ-bm25", + "quoraretrieval": "sentence-transformers/NanoQuoraRetrieval-bm25", + "scidocs": "sentence-transformers/NanoSCIDOCS-bm25", + "arguana": "sentence-transformers/NanoArguAna-bm25", + "scifact": "sentence-transformers/NanoSciFact-bm25", + "touche2020": "sentence-transformers/NanoTouche2020-bm25", +} + +dataset_name_to_human_readable = { + "climatefever": "ClimateFEVER", + "dbpedia": "DBPedia", + "fever": "FEVER", + "fiqa2018": "FiQA2018", + "hotpotqa": "HotpotQA", + "msmarco": "MSMARCO", + "nfcorpus": "NFCorpus", + "nq": "NQ", + "quoraretrieval": "QuoraRetrieval", + "scidocs": "SCIDOCS", + "arguana": "ArguAna", + "scifact": "SciFact", + "touche2020": "Touche2020", +} + + +class CrossEncoderNanoBEIREvaluator(SentenceEvaluator): + """ + This class evaluates a CrossEncoder model on the NanoBEIR collection of Information Retrieval datasets. + + The collection is a set of datasets based on the BEIR collection, but with a significantly smaller size, so it can + be used for quickly evaluating the retrieval performance of a model before committing to a full evaluation. + The datasets are available on Hugging Face in the `NanoBEIR with BM25 collection `_. + This evaluator will return the same metrics as the CrossEncoderRerankingEvaluator (i.e., MRR@k, nDCG@k, MAP), for each dataset and on average. + + Rather than reranking all documents for each query, the evaluator will only rerank the ``rerank_k`` documents from + a BM25 ranking. When your logging is set to INFO, the evaluator will print the MAP, MRR@k, and nDCG@k for each dataset + and the average over all datasets. + + Note that the maximum score is 1.0 by default, because all positive documents are included in the ranking. This + can be toggled off by setting ``always_rerank_positives=False``, at which point the maximum score will be bound by + the number of positive documents that BM25 ranks in the top ``rerank_k`` documents. + + .. note:: + This evaluator outputs its results using keys in the format ``NanoBEIR_R{rerank_k}_{aggregate_key}_{metric}``, + where ``metric`` is one of ``map``, ``mrr@{at_k}``, or ``ndcg@{at_k}``, and ``rerank_k``, ``aggregate_key`` and + ``at_k`` are the parameters of the evaluator. The primary metric is ``ndcg@{at_k}``. By default, the name of + the primary metric is ``NanoBEIR_R100_mean_ndcg@10``. + + For the results of each dataset, the keys are in the format ``Nano{dataset_name}_R{rerank_k}_{metric}``, + for example ``NanoMSMARCO_R100_mrr@10``. + + These can be used as ``metric_for_best_model`` alongside ``load_best_model_at_end=True`` in the + :class:`~sentence_transformers.cross_encoder.training_args.CrossEncoderTrainingArguments` to automatically load the + best model based on a specific metric of interest. + + Args: + dataset_names (List[str]): The names of the datasets to evaluate on. If not specified, use all datasets except arguana and touche2020. + rerank_k (int): The number of documents to rerank from the BM25 ranking. Defaults to 100. + at_k (int, optional): Only consider the top k most similar documents to each query for the evaluation. Defaults to 10. + always_rerank_positives (bool): If True, always evaluate with all positives included. If False, only include + the positives that are already in the documents list. Always set to True if your ``samples`` contain ``negative`` + instead of ``documents``. When using ``documents``, setting this to True will result in a more useful evaluation + signal, but setting it to False will result in a more realistic evaluation. Defaults to True. + batch_size (int): Batch size to compute sentence embeddings. Defaults to 64. + show_progress_bar (bool): Show progress bar when computing embeddings. Defaults to False. + write_csv (bool): Write results to CSV file. Defaults to True. + aggregate_fn (Callable[[list[float]], float]): The function to aggregate the scores. Defaults to np.mean. + aggregate_key (str): The key to use for the aggregated score. Defaults to "mean". + + Example: + :: + + from sentence_transformers.cross_encoder import CrossEncoder + from sentence_transformers.cross_encoder.evaluation import CrossEncoderNanoBEIREvaluator + import logging + + logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(message)s") + + # Load a model + model = CrossEncoder("cross-encoder/ms-marco-MiniLM-L6-v2") + + # Load & run the evaluator + dataset_names = ["msmarco", "nfcorpus", "nq"] + evaluator = CrossEncoderNanoBEIREvaluator(dataset_names) + results = evaluator(model) + ''' + NanoBEIR Evaluation of the model on ['msmarco', 'nfcorpus', 'nq'] dataset: + Evaluating NanoMSMARCO + CrossEncoderRerankingEvaluator: Evaluating the model on the NanoMSMARCO dataset: + Base -> Reranked + MAP: 48.96 -> 60.35 + MRR@10: 47.75 -> 59.63 + NDCG@10: 54.04 -> 66.86 + + Evaluating NanoNFCorpus + CrossEncoderRerankingEvaluator: Evaluating the model on the NanoNFCorpus dataset: + Queries: 50 Positives: Min 1.0, Mean 50.4, Max 463.0 Negatives: Min 54.0, Mean 92.8, Max 100.0 + Base -> Reranked + MAP: 26.10 -> 34.61 + MRR@10: 49.98 -> 58.85 + NDCG@10: 32.50 -> 39.30 + + Evaluating NanoNQ + CrossEncoderRerankingEvaluator: Evaluating the model on the NanoNQ dataset: + Queries: 50 Positives: Min 1.0, Mean 1.1, Max 2.0 Negatives: Min 98.0, Mean 99.0, Max 100.0 + Base -> Reranked + MAP: 41.96 -> 70.98 + MRR@10: 42.67 -> 73.55 + NDCG@10: 50.06 -> 75.99 + + CrossEncoderNanoBEIREvaluator: Aggregated Results: + Base -> Reranked + MAP: 39.01 -> 55.31 + MRR@10: 46.80 -> 64.01 + NDCG@10: 45.54 -> 60.72 + ''' + print(evaluator.primary_metric) + # NanoBEIR_R100_mean_ndcg@10 + print(results[evaluator.primary_metric]) + # 0.60716840988382 + """ + + def __init__( + self, + dataset_names: list[DatasetNameType] | None = None, + rerank_k: int = 100, + at_k: int = 10, + always_rerank_positives: bool = True, + batch_size: int = 32, + show_progress_bar: bool = False, + write_csv: bool = True, + aggregate_fn: Callable[[list[float]], float] = np.mean, + aggregate_key: str = "mean", + ): + super().__init__() + if dataset_names is None: + # We exclude arguana and touche2020 because their Argument Retrieval meaningfully task differs from the others + dataset_names = [key for key in dataset_name_to_id if key not in ["arguana", "touche2020"]] + self.dataset_names = dataset_names + self.rerank_k = rerank_k + self.at_k = at_k + self.always_rerank_positives = always_rerank_positives + self.show_progress_bar = show_progress_bar + self.batch_size = batch_size + self.write_csv = write_csv + self.aggregate_fn = aggregate_fn + self.aggregate_key = aggregate_key + + self.name = f"NanoBEIR_R{rerank_k:d}_{self.aggregate_key}" + + self._validate_dataset_names() + + reranking_kwargs = { + "at_k": self.at_k, + "always_rerank_positives": self.always_rerank_positives, + "show_progress_bar": self.show_progress_bar, + "batch_size": self.batch_size, + "write_csv": self.write_csv, + } + + self.evaluators = [ + self._load_dataset(name, **reranking_kwargs) + for name in tqdm(self.dataset_names, desc="Loading NanoBEIR datasets", leave=False) + ] + + self.csv_file: str = f"NanoBEIR_evaluation_{aggregate_key}_results.csv" + self.csv_headers = ["epoch", "steps", "MAP", f"MRR@{self.at_k}", f"NDCG@{self.at_k}"] + + self.primary_metric = f"ndcg@{self.at_k}" + + def __call__( + self, model: CrossEncoder, output_path: str | None = None, epoch: int = -1, steps: int = -1, *args, **kwargs + ) -> dict[str, float]: + per_metric_results = {} + per_dataset_results = {} + if epoch != -1: + if steps == -1: + out_txt = f" after epoch {epoch}" + else: + out_txt = f" in epoch {epoch} after {steps} steps" + else: + out_txt = "" + logger.info(f"NanoBEIR Evaluation of the model on {self.dataset_names} dataset{out_txt}:") + + for evaluator in tqdm(self.evaluators, desc="Evaluating datasets", disable=not self.show_progress_bar): + logger.info(f"Evaluating {evaluator.name}") + evaluation = evaluator(model, output_path, epoch, steps) + for k in evaluation: + dataset, _rerank_k, metric = k.split("_", maxsplit=2) + if metric not in per_metric_results: + per_metric_results[metric] = [] + per_dataset_results[f"{dataset}_R{self.rerank_k}_{metric}"] = evaluation[k] + per_metric_results[metric].append(evaluation[k]) + logger.info("") + + agg_results = {} + for metric in per_metric_results: + agg_results[metric] = self.aggregate_fn(per_metric_results[metric]) + + if output_path is not None and self.write_csv: + csv_path = os.path.join(output_path, self.csv_file) + if not os.path.isfile(csv_path): + fOut = open(csv_path, mode="w", encoding="utf-8") + fOut.write(",".join(self.csv_headers)) + fOut.write("\n") + + else: + fOut = open(csv_path, mode="a", encoding="utf-8") + + output_data = [ + epoch, + steps, + agg_results["map"], + agg_results[f"mrr@{self.at_k}"], + agg_results[f"ndcg@{self.at_k}"], + ] + + fOut.write(",".join(map(str, output_data))) + fOut.write("\n") + fOut.close() + + logger.info("CrossEncoderNanoBEIREvaluator: Aggregated Results:") + logger.info(f"{' ' * len(str(self.at_k))} Base -> Reranked") + logger.info( + f"MAP:{' ' * len(str(self.at_k))} {agg_results['base_map'] * 100:.2f} -> {agg_results['map'] * 100:.2f}" + ) + logger.info( + f"MRR@{self.at_k}: {agg_results[f'base_mrr@{self.at_k}'] * 100:.2f} -> {agg_results[f'mrr@{self.at_k}'] * 100:.2f}" + ) + logger.info( + f"NDCG@{self.at_k}: {agg_results[f'base_ndcg@{self.at_k}'] * 100:.2f} -> {agg_results[f'ndcg@{self.at_k}'] * 100:.2f}" + ) + + model_card_metrics = { + "map": f"{agg_results['map']:.4f} ({agg_results['map'] - agg_results['base_map']:+.4f})", + f"mrr@{self.at_k}": f"{agg_results[f'mrr@{self.at_k}']:.4f} ({agg_results[f'mrr@{self.at_k}'] - agg_results[f'base_mrr@{self.at_k}']:+.4f})", + f"ndcg@{self.at_k}": f"{agg_results[f'ndcg@{self.at_k}']:.4f} ({agg_results[f'ndcg@{self.at_k}'] - agg_results[f'base_ndcg@{self.at_k}']:+.4f})", + } + model_card_metrics = self.prefix_name_to_metrics(model_card_metrics, self.name) + self.store_metrics_in_model_card_data(model, model_card_metrics, epoch, steps) + + agg_results = self.prefix_name_to_metrics(agg_results, self.name) + per_dataset_results.update(agg_results) + + return per_dataset_results + + def _get_human_readable_name(self, dataset_name: DatasetNameType) -> str: + human_readable_name = f"Nano{dataset_name_to_human_readable[dataset_name.lower()]}_R{self.rerank_k}" + return human_readable_name + + def _load_dataset(self, dataset_name: DatasetNameType, **ir_evaluator_kwargs) -> CrossEncoderRerankingEvaluator: + if not is_datasets_available(): + raise ValueError( + "datasets is not available. Please install it to use the CrossEncoderNanoBEIREvaluator via `pip install datasets`." + ) + from datasets import load_dataset + + dataset_path = dataset_name_to_id[dataset_name.lower()] + corpus = load_dataset(dataset_path, "corpus", split="train") + corpus_mapping = dict(zip(corpus["_id"], corpus["text"])) + queries = load_dataset(dataset_path, "queries", split="train") + query_mapping = dict(zip(queries["_id"], queries["text"])) + relevance = load_dataset(dataset_path, "relevance", split="train") + + def mapper(sample, corpus_mapping: dict[str, str], query_mapping: dict[str, str], rerank_k: int): + query = query_mapping[sample["query-id"]] + positives = [corpus_mapping[positive_id] for positive_id in sample["positive-corpus-ids"]] + documents = [corpus_mapping[document_id] for document_id in sample["bm25-ranked-ids"][:rerank_k]] + return { + "query": query, + "positive": positives, + "documents": documents, + } + + relevance = relevance.map( + mapper, + fn_kwargs={"corpus_mapping": corpus_mapping, "query_mapping": query_mapping, "rerank_k": self.rerank_k}, + ) + + human_readable_name = self._get_human_readable_name(dataset_name) + return CrossEncoderRerankingEvaluator( + samples=list(relevance), + name=human_readable_name, + **ir_evaluator_kwargs, + ) + + def _validate_dataset_names(self): + if len(self.dataset_names) == 0: + raise ValueError("dataset_names cannot be empty. Use None to evaluate on all datasets.") + if missing_datasets := [ + dataset_name for dataset_name in self.dataset_names if dataset_name.lower() not in dataset_name_to_id + ]: + raise ValueError( + f"Dataset(s) {missing_datasets} not found in the NanoBEIR collection. " + f"Valid dataset names are: {list(dataset_name_to_id.keys())}" + ) + + def get_config_dict(self): + return { + "dataset_names": self.dataset_names, + "rerank_k": self.rerank_k, + "at_k": self.at_k, + "always_rerank_positives": self.always_rerank_positives, + } diff --git a/sentence-transformers/sentence_transformers/cross_encoder/evaluation/reranking.py b/sentence-transformers/sentence_transformers/cross_encoder/evaluation/reranking.py new file mode 100644 index 0000000000000000000000000000000000000000..8a851f5b2ff2bddaa583fb41019bddb143cfa1a8 --- /dev/null +++ b/sentence-transformers/sentence_transformers/cross_encoder/evaluation/reranking.py @@ -0,0 +1,301 @@ +from __future__ import annotations + +import csv +import logging +import os +from typing import TYPE_CHECKING + +import numpy as np +from sklearn.metrics import average_precision_score, ndcg_score +from tqdm import tqdm + +from sentence_transformers.evaluation.SentenceEvaluator import SentenceEvaluator + +if TYPE_CHECKING: + from sentence_transformers.cross_encoder.CrossEncoder import CrossEncoder + +logger = logging.getLogger(__name__) + + +class CrossEncoderRerankingEvaluator(SentenceEvaluator): + """ + This class evaluates a CrossEncoder model for the task of re-ranking. + + Given a query and a list of documents, it computes the score [query, doc_i] for all possible + documents and sorts them in decreasing order. Then, MRR@10, NDCG@10 and MAP are computed to measure the quality of the ranking. + + The evaluator expects a list of samples. Each sample is a dictionary with the mandatory "query" and "positive" keys, + and either a "negative" or a "documents" key. The "query" is the search query, the "positive" is a list of relevant + documents, and the "negative" is a list of irrelevant documents. Alternatively, the "documents" key can be used to + provide a list of all documents, including the positive ones. In this case, the evaluator will assume that the list + is already ranked by similarity, with the most similar documents first, and will report both the reranking performance + as well as the performance before reranking. This can be useful to measure the improvement of the reranking on + top of a first-stage retrieval (e.g. a SentenceTransformer model). + + Note that the maximum score is 1.0 by default, because all positive documents are included in the ranking. This + can be toggled off by using samples with ``documents`` instead of ``negative``, i.e. ranked lists of all documents + including the positive ones, together with ``always_rerank_positives=False``. ``always_rerank_positives=False`` only + works when using ``documents`` instead of ``negative``. + + Args: + samples (list): A list of dictionaries, where each dictionary represents a sample and has the following keys: + - 'query' (mandatory): The search query. + - 'positive' (mandatory): A list of positive (relevant) documents. + - 'negative' (optional): A list of negative (irrelevant) documents. Mutually exclusive with 'documents'. + - 'documents' (optional): A list of all documents, including the positive ones. This list is assumed to be + ranked by similarity, with the most similar documents first. Mutually exclusive with 'negative'. + at_k (int, optional): Only consider the top k most similar documents to each query for the evaluation. Defaults to 10. + always_rerank_positives (bool): If True, always evaluate with all positives included. If False, only include + the positives that are already in the documents list. Always set to True if your ``samples`` contain ``negative`` + instead of ``documents``. When using ``documents``, setting this to True will result in a more useful evaluation + signal, but setting it to False will result in a more realistic evaluation. Defaults to True. + name (str, optional): Name of the evaluator, used for logging, saving in a CSV, and the model card. Defaults to "". + batch_size (int): Batch size to compute sentence embeddings. Defaults to 64. + show_progress_bar (bool): Show progress bar when computing embeddings. Defaults to False. + write_csv (bool): Write results to CSV file. Defaults to True. + mrr_at_k (Optional[int], optional): Deprecated parameter. Please use `at_k` instead. Defaults to None. + + Example: + :: + + from sentence_transformers import CrossEncoder + from sentence_transformers.cross_encoder.evaluation import CrossEncoderRerankingEvaluator + from datasets import load_dataset + + # Load a model + model = CrossEncoder("cross-encoder/ms-marco-MiniLM-L6-v2") + + # Load a dataset with queries, positives, and negatives + eval_dataset = load_dataset("microsoft/ms_marco", "v1.1", split="validation") + + samples = [ + { + "query": sample["query"], + "positive": [text for is_selected, text in zip(sample["passages"]["is_selected"], sample["passages"]["passage_text"]) if is_selected], + "documents": sample["passages"]["passage_text"], + # or + # "negative": [text for is_selected, text in zip(sample["passages"]["is_selected"], sample["passages"]["passage_text"]) if not is_selected], + } + for sample in eval_dataset + ] + + # Initialize the evaluator + reranking_evaluator = CrossEncoderRerankingEvaluator( + samples=samples, + name="ms-marco-dev", + show_progress_bar=True, + ) + results = reranking_evaluator(model) + ''' + CrossEncoderRerankingEvaluator: Evaluating the model on the ms-marco-dev dataset: + Queries: 10047 Positives: Min 0.0, Mean 1.1, Max 5.0 Negatives: Min 1.0, Mean 7.1, Max 10.0 + Base -> Reranked + MAP: 34.03 -> 62.36 + MRR@10: 34.67 -> 62.96 + NDCG@10: 49.05 -> 71.05 + ''' + print(reranking_evaluator.primary_metric) + # => ms-marco-dev_ndcg@10 + print(results[reranking_evaluator.primary_metric]) + # => 0.7104656857184184 + """ + + def __init__( + self, + samples: list[dict[str, str | list[str]]], + at_k: int = 10, + always_rerank_positives: bool = True, # TODO: This is also confusing, perhaps setting="" + name: str = "", + batch_size: int = 64, + show_progress_bar: bool = False, + write_csv: bool = True, + mrr_at_k: int | None = None, + ): + super().__init__() + self.samples = samples + if mrr_at_k is not None: + logger.warning(f"The `mrr_at_k` parameter has been deprecated; please use `at_k={mrr_at_k}` instead.") + self.at_k = mrr_at_k + else: + self.at_k = at_k + self.always_rerank_positives = always_rerank_positives + + self.name = name + self.batch_size = batch_size + self.show_progress_bar = show_progress_bar + + if isinstance(self.samples, dict): + self.samples = list(self.samples.values()) + + self.csv_file = "CrossEncoderRerankingEvaluator" + ("_" + name if name else "") + f"_results_@{self.at_k}.csv" + self.csv_headers = ["epoch", "steps", "MAP", f"MRR@{self.at_k}", f"NDCG@{self.at_k}"] + self.write_csv = write_csv + self.primary_metric = f"ndcg@{self.at_k}" + + def __call__( + self, model: CrossEncoder, output_path: str | None = None, epoch: int = -1, steps: int = -1 + ) -> dict[str, float]: + if epoch != -1: + if steps == -1: + out_txt = f" after epoch {epoch}" + else: + out_txt = f" in epoch {epoch} after {steps} steps" + else: + out_txt = "" + + logger.info(f"CrossEncoderRerankingEvaluator: Evaluating the model on the {self.name} dataset{out_txt}:") + + base_mrr_scores = [] + base_ndcg_scores = [] + base_ap_scores = [] + all_mrr_scores = [] + all_ndcg_scores = [] + all_ap_scores = [] + num_queries = 0 + num_positives = [] + num_negatives = [] + for instance in tqdm(self.samples, desc="Evaluating samples", disable=not self.show_progress_bar, leave=False): + if "query" not in instance: + raise ValueError("CrossEncoderRerankingEvaluator requires a 'query' key in each sample.") + if "positive" not in instance: + raise ValueError("CrossEncoderRerankingEvaluator requires a 'positive' key in each sample.") + if ("negative" in instance and "documents" in instance) or ( + "negative" not in instance and "documents" not in instance + ): + raise ValueError( + "CrossEncoderRerankingEvaluator requires exactly one of 'negative' and 'documents' in each sample." + ) + + query = instance["query"] + positive = instance["positive"] + if isinstance(positive, str): + positive = [positive] + + negative = instance.get("negative", None) + documents = instance.get("documents", None) + + if documents: + base_is_relevant = [int(sample in positive) for sample in documents] + if sum(base_is_relevant) == 0: + base_mrr, base_ndcg, base_ap = 0, 0, 0 + else: + # If not all positives are in documents, we need to add them at the end + base_is_relevant += [1] * (len(positive) - sum(base_is_relevant)) + base_pred_scores = np.array(range(len(base_is_relevant), 0, -1)) + base_mrr, base_ndcg, base_ap = self.compute_metrics(base_is_relevant, base_pred_scores) + base_mrr_scores.append(base_mrr) + base_ndcg_scores.append(base_ndcg) + base_ap_scores.append(base_ap) + + if self.always_rerank_positives: + docs = positive + [doc for doc in documents if doc not in positive] + is_relevant = [1] * len(positive) + [0] * (len(docs) - len(positive)) + else: + docs = documents + is_relevant = [int(sample in positive) for sample in documents] + else: + docs = positive + negative + is_relevant = [1] * len(positive) + [0] * len(negative) + + num_queries += 1 + + num_positives.append(len(positive)) + num_negatives.append(len(is_relevant) - sum(is_relevant)) + + if sum(is_relevant) == 0: + all_mrr_scores.append(0) + all_ndcg_scores.append(0) + all_ap_scores.append(0) + continue + + model_input = [[query, doc] for doc in docs] + pred_scores = model.predict(model_input, convert_to_numpy=True, show_progress_bar=False) + + # Add the ignored positives at the end + if num_ignored_positives := len(is_relevant) - len(pred_scores): + pred_scores = np.concatenate([pred_scores, np.zeros(num_ignored_positives)]) + + mrr, ndcg, ap = self.compute_metrics(is_relevant, pred_scores) + + all_mrr_scores.append(mrr) + all_ndcg_scores.append(ndcg) + all_ap_scores.append(ap) + + mean_mrr = np.mean(all_mrr_scores) + mean_ndcg = np.mean(all_ndcg_scores) + mean_ap = np.mean(all_ap_scores) + metrics = { + "map": mean_ap, + f"mrr@{self.at_k}": mean_mrr, + f"ndcg@{self.at_k}": mean_ndcg, + } + + logger.info( + f"Queries: {num_queries}\t" + f"Positives: Min {np.min(num_positives):.1f}, Mean {np.mean(num_positives):.1f}, Max {np.max(num_positives):.1f}\t" + f"Negatives: Min {np.min(num_negatives):.1f}, Mean {np.mean(num_negatives):.1f}, Max {np.max(num_negatives):.1f}" + ) + if documents: + mean_base_mrr = np.mean(base_mrr_scores) + mean_base_ndcg = np.mean(base_ndcg_scores) + mean_base_ap = np.mean(base_ap_scores) + base_metrics = { + "base_map": mean_base_ap, + f"base_mrr@{self.at_k}": mean_base_mrr, + f"base_ndcg@{self.at_k}": mean_base_ndcg, + } + logger.info(f"{' ' * len(str(self.at_k))} Base -> Reranked") + logger.info(f"MAP:{' ' * len(str(self.at_k))} {mean_base_ap * 100:.2f} -> {mean_ap * 100:.2f}") + logger.info(f"MRR@{self.at_k}: {mean_base_mrr * 100:.2f} -> {mean_mrr * 100:.2f}") + logger.info(f"NDCG@{self.at_k}: {mean_base_ndcg * 100:.2f} -> {mean_ndcg * 100:.2f}") + + model_card_metrics = { + "map": f"{mean_ap:.4f} ({mean_ap - mean_base_ap:+.4f})", + f"mrr@{self.at_k}": f"{mean_mrr:.4f} ({mean_mrr - mean_base_mrr:+.4f})", + f"ndcg@{self.at_k}": f"{mean_ndcg:.4f} ({mean_ndcg - mean_base_ndcg:+.4f})", + } + model_card_metrics = self.prefix_name_to_metrics(model_card_metrics, self.name) + self.store_metrics_in_model_card_data(model, model_card_metrics, epoch, steps) + + metrics.update(base_metrics) + metrics = self.prefix_name_to_metrics(metrics, self.name) + else: + logger.info(f"MAP:{' ' * len(str(self.at_k))} {mean_ap * 100:.2f}") + logger.info(f"MRR@{self.at_k}: {mean_mrr * 100:.2f}") + logger.info(f"NDCG@{self.at_k}: {mean_ndcg * 100:.2f}") + + metrics = self.prefix_name_to_metrics(metrics, self.name) + self.store_metrics_in_model_card_data(model, metrics, epoch, steps) + + if output_path is not None and self.write_csv: + csv_path = os.path.join(output_path, self.csv_file) + output_file_exists = os.path.isfile(csv_path) + with open(csv_path, mode="a" if output_file_exists else "w", encoding="utf-8") as f: + writer = csv.writer(f) + if not output_file_exists: + writer.writerow(self.csv_headers) + + writer.writerow([epoch, steps, mean_ap, mean_mrr, mean_ndcg]) + + return metrics + + def compute_metrics(self, y_true, y_pred): + ranking = np.argsort(y_pred)[::-1] + + mrr = 0 + for rank, index in enumerate(ranking[0 : self.at_k]): + if y_true[index]: + mrr = 1 / (rank + 1) + break + + ndcg = ndcg_score([y_true], [y_pred], k=self.at_k) + ap = average_precision_score(y_true, y_pred) + return mrr, ndcg, ap + + def get_config_dict(self): + config_dict = { + "at_k": self.at_k, + } + if self.samples and "documents" in self.samples[0]: + config_dict["always_rerank_positives"] = self.always_rerank_positives + return config_dict diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/BinaryCrossEntropyLoss.py b/sentence-transformers/sentence_transformers/cross_encoder/losses/BinaryCrossEntropyLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..766c77621fe3342c8db995f9a6b9d6e39fbae50b --- /dev/null +++ b/sentence-transformers/sentence_transformers/cross_encoder/losses/BinaryCrossEntropyLoss.py @@ -0,0 +1,116 @@ +from __future__ import annotations + +from torch import Tensor, nn + +from sentence_transformers.cross_encoder.CrossEncoder import CrossEncoder +from sentence_transformers.util import fullname + + +class BinaryCrossEntropyLoss(nn.Module): + def __init__( + self, + model: CrossEncoder, + activation_fn: nn.Module = nn.Identity(), + pos_weight: Tensor | None = None, + **kwargs, + ) -> None: + """ + Computes the Binary Cross Entropy Loss for a CrossEncoder model. This loss is used to train a model to predict + a high logit for positive pairs and a low logit for negative pairs. The model should be initialized with + ``num_labels = 1`` (a.k.a. the default) to predict one class. + + It has been used to train many of the strong `CrossEncoder MS MARCO Reranker models `_. + + Args: + model (:class:`~sentence_transformers.cross_encoder.CrossEncoder`): A CrossEncoder model to be trained. + activation_fn (:class:`~torch.nn.Module`): Activation function applied to the logits before computing the loss. Defaults to :class:`~torch.nn.Identity`. + pos_weight (Tensor, optional): A weight of positive examples. Must be a :class:`torch.Tensor` like ``torch.tensor(4)`` for a weight of 4. Defaults to None. + **kwargs: Additional keyword arguments passed to the underlying :class:`torch.nn.BCEWithLogitsLoss`. + + References: + - :class:`torch.nn.BCEWithLogitsLoss` + - `Cross Encoder > Training Examples > Semantic Textual Similarity <../../../examples/cross_encoder/training/sts/README.html>`_ + - `Cross Encoder > Training Examples > Quora Duplicate Questions <../../../examples/cross_encoder/training/quora_duplicate_questions/README.html>`_ + - `Cross Encoder > Training Examples > MS MARCO <../../../examples/cross_encoder/training/ms_marco/README.html>`_ + - `Cross Encoder > Training Examples > Rerankers <../../../examples/cross_encoder/training/rerankers/README.html>`_ + + Requirements: + 1. Your model must be initialized with `num_labels = 1` (a.k.a. the default) to predict one class. + + Inputs: + +-------------------------------------------------+----------------------------------------+-------------------------------+ + | Texts | Labels | Number of Model Output Labels | + +=================================================+========================================+===============================+ + | (anchor, positive/negative) pairs | 1 if positive, 0 if negative | 1 | + +-------------------------------------------------+----------------------------------------+-------------------------------+ + | (sentence_A, sentence_B) pairs | float similarity score between 0 and 1 | 1 | + +-------------------------------------------------+----------------------------------------+-------------------------------+ + + Recommendations: + - Use :class:`~sentence_transformers.util.mine_hard_negatives` with ``output_format="labeled-pair"`` + to convert question-answer pairs to the ``(anchor, positive/negative) pairs`` format with labels as 1 or 0, + using hard negatives. + + Example: + :: + + from sentence_transformers.cross_encoder import CrossEncoder, CrossEncoderTrainer, losses + from datasets import Dataset + + model = CrossEncoder("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "query": ["What are pandas?", "What are pandas?"], + "response": ["Pandas are a kind of bear.", "Pandas are a kind of fish."], + "label": [1, 0], + }) + loss = losses.BinaryCrossEntropyLoss(model) + + trainer = CrossEncoderTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.model = model + self.activation_fn = activation_fn + self.pos_weight = pos_weight + self.bce_with_logits_loss = nn.BCEWithLogitsLoss(pos_weight=pos_weight, **kwargs) + + if not isinstance(self.model, CrossEncoder): + raise ValueError( + f"{self.__class__.__name__} expects a model of type CrossEncoder, " + f"but got a model of type {type(self.model)}." + ) + + if self.model.num_labels != 1: + raise ValueError( + f"{self.__class__.__name__} expects a model with 1 output label, " + f"but got a model with {self.model.num_labels} output labels." + ) + + def forward(self, inputs: list[list[str]], labels: Tensor) -> Tensor: + if len(inputs) != 2: + raise ValueError( + f"BinaryCrossEntropyLoss expects a dataset with two non-label columns, but got a dataset with {len(inputs)} columns." + ) + + pairs = list(zip(inputs[0], inputs[1])) + tokens = self.model.tokenizer( + pairs, + padding=True, + truncation=True, + return_tensors="pt", + ) + tokens.to(self.model.device) + logits = self.model(**tokens)[0].view(-1) + logits = self.activation_fn(logits) + loss = self.bce_with_logits_loss(logits, labels.float()) + return loss + + def get_config_dict(self): + return { + "activation_fn": fullname(self.activation_fn), + "pos_weight": self.pos_weight if self.pos_weight is None else self.pos_weight.item(), + } diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/CachedMultipleNegativesRankingLoss.py b/sentence-transformers/sentence_transformers/cross_encoder/losses/CachedMultipleNegativesRankingLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..8dea020ad0871b12c5e4e1daec34fff6c9822e8b --- /dev/null +++ b/sentence-transformers/sentence_transformers/cross_encoder/losses/CachedMultipleNegativesRankingLoss.py @@ -0,0 +1,281 @@ +from __future__ import annotations + +from collections.abc import Iterator +from contextlib import nullcontext +from functools import partial + +import torch +import tqdm +from torch import Tensor, nn +from torch.utils.checkpoint import get_device_states, set_device_states + +from sentence_transformers.cross_encoder.CrossEncoder import CrossEncoder +from sentence_transformers.cross_encoder.losses.MultipleNegativesRankingLoss import MultipleNegativesRankingLoss + + +class RandContext: + """ + Random-state context manager class. Reference: https://github.com/luyug/GradCache. + + This class will back up the pytorch's random state during initialization. Then when the context is activated, + the class will set up the random state with the backed-up one. + """ + + def __init__(self, *tensors) -> None: + self.fwd_cpu_state = torch.get_rng_state() + self.fwd_gpu_devices, self.fwd_gpu_states = get_device_states(*tensors) + + def __enter__(self) -> None: + self._fork = torch.random.fork_rng(devices=self.fwd_gpu_devices, enabled=True) + self._fork.__enter__() + torch.set_rng_state(self.fwd_cpu_state) + set_device_states(self.fwd_gpu_devices, self.fwd_gpu_states) + + def __exit__(self, exc_type, exc_val, exc_tb) -> None: + self._fork.__exit__(exc_type, exc_val, exc_tb) + self._fork = None + + +def _backward_hook( + grad_output: Tensor, + pairs: list[list[str]], + loss_obj: CachedMultipleNegativesRankingLoss, +) -> None: + """A backward hook to backpropagate the cached gradients mini-batch by mini-batch.""" + assert loss_obj.cache is not None + assert loss_obj.random_states is not None + with torch.enable_grad(): + # for sentence_feature, grad, random_states in zip(pairs, loss_obj.cache, loss_obj.random_states): + for (minibatch_logits, _), minibatch_grad in zip( + loss_obj.predict_minibatch_iter( + pairs=pairs, + with_grad=True, + copy_random_state=False, + random_states=loss_obj.random_states, + ), + loss_obj.cache, + ): + surrogate = torch.dot(minibatch_logits.flatten(), minibatch_grad.flatten()) * grad_output + surrogate.backward() + + +class CachedMultipleNegativesRankingLoss(MultipleNegativesRankingLoss): + def __init__( + self, + model: CrossEncoder, + num_negatives: int | None = 4, + scale: float = 10.0, + activation_fn: nn.Module | None = nn.Sigmoid(), + mini_batch_size: int = 32, + show_progress_bar: bool = False, + ) -> None: + """ + Boosted version of :class:`~sentence_transformers.cross_encoder.losses.MultipleNegativesRankingLoss` that + caches the gradients of the logits wrt. the loss. This allows for much higher batch sizes without extra + memory usage. However, it is slightly slower. + + In detail: + + (1) It first does a quick prediction step without gradients/computation graphs to get all the logits; + (2) Calculate the loss, backward up to the logits and cache the gradients wrt. to the logits; + (3) A 2nd prediction step with gradients/computation graphs and connect the cached gradients into the backward chain. + + Notes: All steps are done with mini-batches. In the original implementation of GradCache, (2) is not done in + mini-batches and requires a lot memory when the batch size is large. The gradient caching will sacrifice around + 20% computation time according to the paper. + + Given a list of (anchor, positive) pairs or (anchor, positive, negative) triplets, this loss optimizes the following: + + * Given an anchor (e.g. a question), assign the highest similarity to the corresponding positive (i.e. answer) + out of every single positive and negative (e.g. all answers) in the batch. + + If you provide the optional negatives, they will all be used as extra options from which the model must pick the + correct positive. Within reason, the harder this "picking" is, the stronger the model will become. Because of + this, a higher batch size results in more in-batch negatives, which then increases performance (to a point). + + This loss function works great to train embeddings for retrieval setups where you have positive pairs + (e.g. (query, answer)) as it will sample in each batch ``n-1`` negative docs randomly. + + This loss is also known as InfoNCE loss with GradCache. + + Args: + model (:class:`~sentence_transformers.cross_encoder.CrossEncoder`): A CrossEncoder model to be trained. + num_negatives (int, optional): Number of in-batch negatives to sample for each anchor. Defaults to 4. + scale (int, optional): Output of similarity function is multiplied by scale value. Defaults to 10.0. + activation_fn (:class:`~torch.nn.Module`): Activation function applied to the logits before computing the loss. Defaults to :class:`~torch.nn.Sigmoid`. + mini_batch_size (int, optional): Mini-batch size for the forward pass. This informs the memory usage. Defaults to 32. + show_progress_bar (bool, optional): Whether to show a progress bar during the forward pass. Defaults to False. + + .. note:: + + The current default values are subject to change in the future. Experimentation is encouraged. + + References: + - Efficient Natural Language Response Suggestion for Smart Reply, Section 4.4: https://arxiv.org/pdf/1705.00652.pdf + - Scaling Deep Contrastive Learning Batch Size under Memory Limited Setup: https://arxiv.org/pdf/2101.06983.pdf + - `Cross Encoder > Training Examples > MS MARCO <../../../examples/cross_encoder/training/ms_marco/README.html>`_ + - `Cross Encoder > Training Examples > Rerankers <../../../examples/cross_encoder/training/rerankers/README.html>`_ + + Requirements: + 1. Your model must be initialized with `num_labels = 1` (a.k.a. the default) to predict one class. + 2. Should be used with large `per_device_train_batch_size` and low `mini_batch_size` for superior performance, + but slower training time than :class:`MultipleNegativesRankingLoss`. + + Inputs: + +-------------------------------------------------+--------+-------------------------------+ + | Texts | Labels | Number of Model Output Labels | + +=================================================+========+===============================+ + | (anchor, positive) pairs | none | 1 | + +-------------------------------------------------+--------+-------------------------------+ + | (anchor, positive, negative) triplets | none | 1 | + +-------------------------------------------------+--------+-------------------------------+ + | (anchor, positive, negative_1, ..., negative_n) | none | 1 | + +-------------------------------------------------+--------+-------------------------------+ + + Recommendations: + - Use ``BatchSamplers.NO_DUPLICATES`` (:class:`docs `) to + ensure that no in-batch negatives are duplicates of the anchor or positive samples. + - Use :class:`~sentence_transformers.util.mine_hard_negatives` with ``output_format="n-tuple"`` or + ``output_format="triplet"`` to convert question-answer pairs to triplets with hard negatives. + + Relations: + - Equivalent to :class:`~sentence_transformers.cross_encoder.losses.MultipleNegativesRankingLoss`, but with + caching that allows for much higher batch sizes (and thus better performance) without extra memory usage. + This loss also trains slower than :class:`~sentence_transformers.cross_encoder.losses.MultipleNegativesRankingLoss`. + + Example: + :: + + from sentence_transformers.cross_encoder import CrossEncoder, CrossEncoderTrainer, losses + from datasets import Dataset + + model = CrossEncoder("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "query": ["What are pandas?", "What is the capital of France?"], + "answer": ["Pandas are a kind of bear.", "The capital of France is Paris."], + }) + loss = losses.CachedMultipleNegativesRankingLoss(model, mini_batch_size=32) + + trainer = CrossEncoderTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__(model, num_negatives, scale, activation_fn) + self.mini_batch_size = mini_batch_size + self.show_progress_bar = show_progress_bar + + self.cross_entropy_loss = nn.CrossEntropyLoss() + self.cache: list[list[Tensor]] | None = None + self.random_states: list[list[RandContext]] | None = None + + if not isinstance(self.model, CrossEncoder): + raise ValueError( + f"{self.__class__.__name__} expects a model of type CrossEncoder, " + f"but got a model of type {type(self.model)}." + ) + + if self.model.num_labels != 1: + raise ValueError( + f"{self.__class__.__name__} expects a model with 1 output label, " + f"but got a model with {self.model.num_labels} output labels." + ) + + def predict_minibatch( + self, + pairs: list[list[str]], + with_grad: bool, + copy_random_state: bool, + random_state: RandContext | None = None, + ) -> tuple[Tensor, RandContext | None]: + """Do forward pass on a minibatch of the input features and return corresponding embeddings.""" + grad_context = nullcontext if with_grad else torch.no_grad + random_state_context = nullcontext() if random_state is None else random_state + with random_state_context: + with grad_context(): + random_state = RandContext(pairs) if copy_random_state else None + logits = self.call_model_with_pairs(pairs) + return logits, random_state + + def predict_minibatch_iter( + self, + pairs: list[list[str]], + with_grad: bool, + copy_random_state: bool, + random_states: list[RandContext] | None = None, + ) -> Iterator[tuple[Tensor, RandContext | None]]: + """Do forward pass on all the minibatches of the input features and yield corresponding embeddings.""" + for i, b in enumerate( + tqdm.trange( + 0, + len(pairs), + self.mini_batch_size, + desc="Predict mini-batches", + disable=not self.show_progress_bar, + ) + ): + e = b + self.mini_batch_size + mini_batch_pairs = pairs[b:e] + + logits, random_state = self.predict_minibatch( + pairs=mini_batch_pairs, + with_grad=with_grad, + copy_random_state=copy_random_state, + random_state=None if random_states is None else random_states[i], + ) + yield logits, random_state # reps: (mbsz, hdim) + + def calculate_loss_and_cache_gradients(self, logits: list[Tensor], batch_size: int) -> Tensor: + """Calculate the cross-entropy loss and cache the gradients wrt. the embeddings.""" + loss = self.calculate_loss(logits, batch_size) + loss.backward() + loss = loss.detach().requires_grad_() + + self.cache = [logit.grad for logit in logits] + + return loss + + def forward(self, inputs: list[list[str]], labels: Tensor) -> Tensor: + # Step (1): A quick embedding step without gradients/computation graphs to get all the embeddings + anchors = inputs[0][::] + candidates = inputs[1][::] + batch_size = len(anchors) + + # In-batch negatives: + for negatives in self.get_in_batch_negatives(inputs[0], inputs[1:]): + anchors.extend(inputs[0]) + candidates.extend(negatives) + + # Hard negatives: + for negatives in inputs[2:]: + anchors.extend(inputs[0]) + candidates.extend(negatives) + + pairs = list(zip(anchors, candidates)) + + logits = [] + self.random_states = [] + for minibatch_logits, random_state in self.predict_minibatch_iter( + pairs=pairs, + with_grad=False, + copy_random_state=True, + ): + logits.append(minibatch_logits.detach().requires_grad_()) + self.random_states.append(random_state) + + if torch.is_grad_enabled(): + # Step (2): Calculate the loss, backward up to the embeddings and cache the gradients wrt. to the embeddings + loss = self.calculate_loss_and_cache_gradients(logits, batch_size) + + # Step (3): A 2nd embedding step with gradients/computation graphs and connect the cached gradients into the backward chain + loss.register_hook(partial(_backward_hook, pairs=pairs, loss_obj=self)) + else: + # If grad is not enabled (e.g. in evaluation), then we don't have to worry about the gradients or backward hook + loss = self.calculate_loss(logits, batch_size) + + return loss + + def get_config_dict(self): + return {**super().get_config_dict(), "mini_batch_size": self.mini_batch_size} diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/CrossEntropyLoss.py b/sentence-transformers/sentence_transformers/cross_encoder/losses/CrossEntropyLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..f8c16f5d0a5671f3952a6bfba2e3d22db2623317 --- /dev/null +++ b/sentence-transformers/sentence_transformers/cross_encoder/losses/CrossEntropyLoss.py @@ -0,0 +1,84 @@ +from __future__ import annotations + +from torch import Tensor, nn + +from sentence_transformers.cross_encoder.CrossEncoder import CrossEncoder + + +class CrossEntropyLoss(nn.Module): + def __init__(self, model: CrossEncoder, activation_fn: nn.Module = nn.Identity(), **kwargs) -> None: + """ + Computes the Cross Entropy Loss for a CrossEncoder model. This loss is used to train a model to predict the + correct class label for a given pair of sentences. The number of classes should be equal to the number of model + output labels. + + Args: + model (:class:`~sentence_transformers.cross_encoder.CrossEncoder`): A CrossEncoder model to be trained. + activation_fn (:class:`~torch.nn.Module`): Activation function applied to the logits before computing the loss. Defaults to :class:`~torch.nn.Identity`. + **kwargs: Additional keyword arguments passed to the underlying :class:`torch.nn.CrossEntropyLoss`. + + References: + - :class:`torch.nn.CrossEntropyLoss` + - `Cross Encoder > Training Examples > Natural Language Inference <../../../examples/cross_encoder/training/nli/README.html>`_ + + Requirements: + 1. Your model can be initialized with `num_labels > 1` to predict multiple classes. + 2. The number of dataset classes should be equal to the number of model output labels (`model.num_labels`). + + Inputs: + +-------------------------------------------------+--------+-------------------------------+ + | Texts | Labels | Number of Model Output Labels | + +=================================================+========+===============================+ + | (sentence_A, sentence_B) pairs | class | `num_classes` | + +-------------------------------------------------+--------+-------------------------------+ + + Example: + :: + + from sentence_transformers.cross_encoder import CrossEncoder, CrossEncoderTrainer, losses + from datasets import Dataset + + model = CrossEncoder("microsoft/mpnet-base", num_labels=2) + train_dataset = Dataset.from_dict({ + "sentence1": ["How can I be a good geologist?", "What is the capital of France?"], + "sentence2": ["What should I do to be a great geologist?", "What is the capital of Germany?"], + "label": [1, 0], # 1: duplicate, 0: not duplicate + }) + loss = losses.CrossEntropyLoss(model) + + trainer = CrossEncoderTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.model = model + self.activation_fn = activation_fn + self.ce_loss = nn.CrossEntropyLoss(**kwargs) + + if not isinstance(self.model, CrossEncoder): + raise ValueError( + f"{self.__class__.__name__} expects a model of type CrossEncoder, " + f"but got a model of type {type(self.model)}." + ) + + def forward(self, inputs: list[list[str]], labels: Tensor) -> Tensor: + if len(inputs) != 2: + raise ValueError( + f"CrossEntropyLoss expects a dataset with two non-label columns, but got a dataset with {len(inputs)} columns." + ) + + pairs = list(zip(inputs[0], inputs[1])) + tokens = self.model.tokenizer( + pairs, + padding=True, + truncation=True, + return_tensors="pt", + ) + tokens.to(self.model.device) + logits = self.model(**tokens)[0] + logits = self.activation_fn(logits) + loss = self.ce_loss(logits, labels) + return loss diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/LambdaLoss.py b/sentence-transformers/sentence_transformers/cross_encoder/losses/LambdaLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..e2067259d7ac79c3cea958df0718bf2e8bb0217d --- /dev/null +++ b/sentence-transformers/sentence_transformers/cross_encoder/losses/LambdaLoss.py @@ -0,0 +1,360 @@ +from __future__ import annotations + +from typing import Literal + +import torch +from torch import Tensor, nn + +from sentence_transformers.cross_encoder import CrossEncoder +from sentence_transformers.util import fullname + + +class BaseWeightingScheme(nn.Module): + """Base class for implementing weighting schemes in LambdaLoss.""" + + def __init__(self, *args, **kwargs) -> None: + """""" + super().__init__(*args, **kwargs) + + def forward(self, gain: Tensor, discount: Tensor, true_sorted: Tensor) -> Tensor: + """ + Calculate weights for the loss function. + + Args: + gain: Normalized gains tensor + discount: Discount tensor + true_sorted: Sorted ground truth labels + + Returns: + Tensor: Calculated weights for the loss + """ + raise NotImplementedError + + +class NoWeightingScheme(BaseWeightingScheme): + """Implementation of no weighting scheme (weights = 1.0).""" + + def forward(self, gain: Tensor, discount: Tensor, true_sorted: Tensor) -> Tensor: + return torch.tensor(1.0, device=gain.device) + + +class NDCGLoss1Scheme(BaseWeightingScheme): + """Implementation of NDCG Loss1 weighting scheme. + + It is used to optimize for the NDCG metric, but this weighting scheme is not recommended as the + NDCGLoss2Scheme and NDCGLoss2PPScheme were shown to reach superior performance in the original + LambdaLoss paper. + """ + + def forward(self, gain: Tensor, discount: Tensor, true_sorted: Tensor) -> Tensor: + return (gain / discount)[:, :, None] + + +class NDCGLoss2Scheme(BaseWeightingScheme): + """Implementation of NDCG Loss2 weighting scheme. + + This scheme uses a tighter bound than NDCGLoss1Scheme and was shown to reach + superior performance in the original LambdaLoss paper. It is used to optimize + for the NDCG metric. + """ + + def forward(self, gain: Tensor, discount: Tensor, true_sorted: Tensor) -> Tensor: + pos_idxs = torch.arange(1, gain.shape[1] + 1, device=gain.device) + delta_idxs = torch.abs(pos_idxs[:, None] - pos_idxs[None, :]) + deltas = torch.abs( + torch.pow(torch.abs(discount[0, delta_idxs - 1]), -1.0) + - torch.pow(torch.abs(discount[0, delta_idxs]), -1.0) + ) + deltas.diagonal().zero_() + return deltas[None, :, :] * torch.abs(gain[:, :, None] - gain[:, None, :]) + + +class LambdaRankScheme(BaseWeightingScheme): + """Implementation of LambdaRank weighting scheme. + + This weighting optimizes a coarse upper bound of NDCG. + """ + + def forward(self, gain: Tensor, discount: Tensor, true_sorted: Tensor) -> Tensor: + return torch.abs(torch.pow(discount[:, :, None], -1.0) - torch.pow(discount[:, None, :], -1.0)) * torch.abs( + gain[:, :, None] - gain[:, None, :] + ) + + +class NDCGLoss2PPScheme(BaseWeightingScheme): + """Implementation of NDCG Loss2++ weighting scheme. + + It is a hybrid weighting scheme that combines the NDCGLoss2 and LambdaRank schemes. It + was shown to reach the strongest performance in the original LambdaLoss paper. + """ + + def __init__(self, mu: float = 10.0): + super().__init__() + self.mu = mu + self.ndcg_loss2 = NDCGLoss2Scheme() + self.lambda_rank = LambdaRankScheme() + + def forward(self, gain: Tensor, discount: Tensor, true_sorted: Tensor) -> Tensor: + ndcg_weights = self.ndcg_loss2(gain, discount, true_sorted) + lambda_weights = self.lambda_rank(gain, discount, true_sorted) + return self.mu * ndcg_weights + lambda_weights + + +class LambdaLoss(nn.Module): + def __init__( + self, + model: CrossEncoder, + weighting_scheme: BaseWeightingScheme | None = NDCGLoss2PPScheme(), + k: int | None = None, + sigma: float = 1.0, + eps: float = 1e-10, + reduction_log: Literal["natural", "binary"] = "binary", + activation_fn: nn.Module | None = nn.Identity(), + mini_batch_size: int | None = None, + ) -> None: + """ + The LambdaLoss Framework for Ranking Metric Optimization. This loss function implements the LambdaLoss framework for ranking metric optimization, + which provides various weighting schemes including LambdaRank and NDCG variations. + The implementation is optimized to handle padded documents efficiently by only + processing valid documents during model inference. + + .. note:: + + The number of documents per query can vary between samples with the ``LambdaLoss``. + + Args: + model (CrossEncoder): CrossEncoder model to be trained + weighting_scheme (:class:`~sentence_transformers.cross_encoder.losses.LambdaLoss.BaseWeightingScheme`, optional): Weighting scheme to use for the loss. + + - :class:`~sentence_transformers.cross_encoder.losses.NoWeightingScheme`: No weighting scheme (weights = 1.0) + - :class:`~sentence_transformers.cross_encoder.losses.NDCGLoss1Scheme`: NDCG Loss1 weighting scheme + - :class:`~sentence_transformers.cross_encoder.losses.NDCGLoss2Scheme`: NDCG Loss2 weighting scheme + - :class:`~sentence_transformers.cross_encoder.losses.LambdaRankScheme`: LambdaRank weighting scheme + - :class:`~sentence_transformers.cross_encoder.losses.NDCGLoss2PPScheme`: NDCG Loss2++ weighting scheme + + Defaults to NDCGLoss2PPScheme. In the original LambdaLoss paper, the NDCGLoss2PPScheme was shown to reach + the strongest performance, with the NDCGLoss2Scheme following closely. + k (int, optional): Number of documents to consider for NDCG@K. Defaults to None (use all documents). + sigma (float): Score difference weight used in sigmoid + eps (float): Small constant for numerical stability + reduction_log (str): Type of logarithm to use + - "natural": Natural logarithm (log) + - "binary": Binary logarithm (log2) + activation_fn (:class:`~torch.nn.Module`): Activation function applied to the logits before computing the + loss. Defaults to :class:`~torch.nn.Identity`. + mini_batch_size (int, optional): Number of samples to process in each forward pass. This has a significant + impact on the memory consumption and speed of the training process. Three cases are possible: + + - If ``mini_batch_size`` is None, the ``mini_batch_size`` is set to the batch size. + - If ``mini_batch_size`` is greater than 0, the batch is split into mini-batches of size ``mini_batch_size``. + - If ``mini_batch_size`` is <= 0, the entire batch is processed at once. + + Defaults to None. + + References: + - The LambdaLoss Framework for Ranking Metric Optimization: https://marc.najork.org/papers/cikm2018.pdf + - Context-Aware Learning to Rank with Self-Attention: https://arxiv.org/abs/2005.10084 + - `Cross Encoder > Training Examples > MS MARCO <../../../examples/cross_encoder/training/ms_marco/README.html>`_ + + Requirements: + 1. Query with multiple documents (listwise approach) + 2. Documents must have relevance scores/labels. Both binary and continuous labels are supported. + + Inputs: + +----------------------------------------+--------------------------------+-------------------------------+ + | Texts | Labels | Number of Model Output Labels | + +========================================+================================+===============================+ + | (query, [doc1, doc2, ..., docN]) | [score1, score2, ..., scoreN] | 1 | + +----------------------------------------+--------------------------------+-------------------------------+ + + Recommendations: + - Use :class:`~sentence_transformers.util.mine_hard_negatives` with ``output_format="labeled-list"`` + to convert question-answer pairs to the required input format with hard negatives. + + Relations: + - :class:`~sentence_transformers.cross_encoder.losses.LambdaLoss` anecdotally performs better than + the other losses with the same input format. + + Example: + :: + + from sentence_transformers.cross_encoder import CrossEncoder, CrossEncoderTrainer, losses + from datasets import Dataset + + model = CrossEncoder("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "query": ["What are pandas?", "What is the capital of France?"], + "docs": [ + ["Pandas are a kind of bear.", "Pandas are kind of like fish."], + ["The capital of France is Paris.", "Paris is the capital of France.", "Paris is quite large."], + ], + "labels": [[1, 0], [1, 1, 0]], + }) + loss = losses.LambdaLoss(model) + + trainer = CrossEncoderTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.model = model + self.weighting_scheme = weighting_scheme or NoWeightingScheme() + self.k = k + self.sigma = sigma + self.eps = eps + self.reduction_log = reduction_log + self.activation_fn = activation_fn or nn.Identity() + self.mini_batch_size = mini_batch_size + + if self.model.num_labels != 1: + raise ValueError( + f"{self.__class__.__name__} supports a model with 1 output label, " + f"but got a model with {self.model.num_labels} output labels." + ) + + def forward(self, inputs: list[list[str], list[list[str]]], labels: list[Tensor]) -> Tensor: + """ + Compute LambdaLoss for a batch of queries and their documents. + + Args: + inputs: List of (queries, documents_list) + labels: Ground truth relevance scores, shape (batch_size, num_documents) + + Returns: + Tensor: LambdaLoss loss over the batch + """ + if isinstance(labels, Tensor): + raise ValueError( + "LambdaLoss expects a list of labels for each sample, but got a single value for each sample." + ) + if len(inputs) != 2: + raise ValueError(f"LambdaLoss expects two inputs (queries, documents_list), but got {len(inputs)} inputs.") + + queries, docs_list = inputs + docs_per_query = [len(docs) for docs in docs_list] + max_docs = max(docs_per_query) + batch_size = len(queries) + + if docs_per_query != [len(labels) for labels in labels]: + raise ValueError( + f"Number of documents per query in inputs ({docs_per_query}) does not match number of labels per query ({[len(labels) for labels in labels]})." + ) + + # Create input pairs for the model + pairs = [(query, document) for query, docs in zip(queries, docs_list) for document in docs] + + if not pairs: + # Handle edge case where all documents are padded + return torch.tensor(0.0, device=self.model.device, requires_grad=True) + + mini_batch_size = self.mini_batch_size or batch_size + if mini_batch_size <= 0: + mini_batch_size = len(pairs) + + logits_list = [] + for i in range(0, len(pairs), mini_batch_size): + mini_batch_pairs = pairs[i : i + mini_batch_size] + + tokens = self.model.tokenizer( + mini_batch_pairs, + padding=True, + truncation=True, + return_tensors="pt", + ) + tokens = tokens.to(self.model.device) + + logits = self.model(**tokens)[0].view(-1) + logits_list.append(logits) + + logits = torch.cat(logits_list, dim=0) + logits = self.activation_fn(logits) + + # Create output tensor filled with 0 (padded logits will be ignored via labels) + logits_matrix = torch.full((batch_size, max_docs), -1e16, device=self.model.device) + + # Place logits in the desired positions in the logit matrix + doc_indices = torch.cat([torch.arange(len(docs)) for docs in docs_list], dim=0) + batch_indices = torch.repeat_interleave(torch.arange(batch_size), torch.tensor(docs_per_query)) + logits_matrix[batch_indices, doc_indices] = logits + + # Idem for labels, but fill with -inf to 0 out padded logits in the loss + labels_matrix = torch.full_like(logits_matrix, float("-inf")) + labels_matrix[batch_indices, doc_indices] = torch.cat(labels, dim=0).float() + labels_matrix = labels_matrix.to(self.model.device) + + # Calculate LambdaLoss components + logits_matrix_sorted, indices_pred = logits_matrix.sort(descending=True, dim=-1) + labels_matrix_sorted, _ = labels_matrix.sort(descending=True, dim=-1) + + # Create masks for valid pairs + true_sorted_by_preds = torch.gather(labels_matrix, dim=1, index=indices_pred) + true_diffs = true_sorted_by_preds[:, :, None] - true_sorted_by_preds[:, None, :] + padded_pairs_mask = torch.isfinite(true_diffs) + + if not isinstance(self.weighting_scheme, NDCGLoss1Scheme): + padded_pairs_mask = padded_pairs_mask & (true_diffs > 0) + + # Create truncation mask if k is specified + k = self.k or max_docs + ndcg_at_k_mask = torch.zeros((max_docs, max_docs), dtype=torch.bool, device=self.model.device) + ndcg_at_k_mask[:k, :k] = 1 + + # Calculate gains and discounts + true_sorted_by_preds.clamp_(min=0.0) + labels_matrix_sorted.clamp_(min=0.0) + + pos_idxs = torch.arange(1, max_docs + 1).to(self.model.device) + discount = torch.log2(1.0 + pos_idxs.float())[None, :] + maxDCGs = torch.sum(((torch.pow(2, labels_matrix_sorted) - 1) / discount)[:, :k], dim=-1).clamp(min=self.eps) + gain = (torch.pow(2, true_sorted_by_preds) - 1) / maxDCGs[:, None] + + # Apply weighting scheme + weights = self.weighting_scheme(gain, discount, true_sorted_by_preds) + + # Calculate scores differences and probabilities + scores_diffs = (logits_matrix_sorted[:, :, None] - logits_matrix_sorted[:, None, :]).clamp(min=-1e8, max=1e8) + scores_diffs.masked_fill_(torch.isnan(scores_diffs), 0.0) + weighted_probas = (torch.sigmoid(self.sigma * scores_diffs).clamp(min=self.eps) ** weights).clamp(min=self.eps) + + # Calculate losses based on specified logarithm base + if self.reduction_log == "natural": + losses = torch.log(weighted_probas) + else: # binary + losses = torch.log2(weighted_probas) + + # Apply masks and reduction + masked_losses = losses[padded_pairs_mask & ndcg_at_k_mask] + loss = -torch.mean(masked_losses) + return loss + + def get_config_dict(self) -> dict[str, float | int | str | None]: + """ + Get configuration parameters for this loss function. + + Returns: + Dictionary containing the configuration parameters + """ + return { + "weighting_scheme": fullname(self.weighting_scheme), + "k": self.k, + "sigma": self.sigma, + "eps": self.eps, + "reduction_log": self.reduction_log, + "activation_fn": fullname(self.activation_fn), + "mini_batch_size": self.mini_batch_size, + } + + @property + def citation(self) -> str: + return """ +@inproceedings{wang2018lambdaloss, + title={The LambdaLoss Framework for Ranking Metric Optimization}, + author={Wang, Xuanhui and Li, Cheng and Golbandi, Nadav and Bendersky, Michael and Najork, Marc}, + booktitle={Proceedings of the 27th ACM international conference on information and knowledge management}, + pages={1313--1322}, + year={2018} +} +""" diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/ListMLELoss.py b/sentence-transformers/sentence_transformers/cross_encoder/losses/ListMLELoss.py new file mode 100644 index 0000000000000000000000000000000000000000..8c6708a8068f27f31d1fa961584aa0768a290c8d --- /dev/null +++ b/sentence-transformers/sentence_transformers/cross_encoder/losses/ListMLELoss.py @@ -0,0 +1,126 @@ +from __future__ import annotations + +from torch import nn + +from sentence_transformers.cross_encoder import CrossEncoder +from sentence_transformers.cross_encoder.losses.PListMLELoss import PListMLELoss + + +class ListMLELoss(PListMLELoss): + def __init__( + self, + model: CrossEncoder, + activation_fn: nn.Module | None = nn.Identity(), + mini_batch_size: int | None = None, + respect_input_order: bool = True, + ) -> None: + """ + This loss function implements the ListMLE learning to rank algorithm, which uses a list-wise + approach based on maximum likelihood estimation of permutations. It maximizes the likelihood + of the permutation induced by the ground truth labels. + + .. note:: + + The number of documents per query can vary between samples with the ``ListMLELoss``. + + Args: + model (CrossEncoder): CrossEncoder model to be trained + activation_fn (:class:`~torch.nn.Module`): Activation function applied to the logits before computing the + loss. Defaults to :class:`~torch.nn.Identity`. + mini_batch_size (int, optional): Number of samples to process in each forward pass. This has a significant + impact on the memory consumption and speed of the training process. Three cases are possible: + + - If ``mini_batch_size`` is None, the ``mini_batch_size`` is set to the batch size. + - If ``mini_batch_size`` is greater than 0, the batch is split into mini-batches of size ``mini_batch_size``. + - If ``mini_batch_size`` is <= 0, the entire batch is processed at once. + + Defaults to None. + respect_input_order (bool): Whether to respect the original input order of documents. + If True, assumes the input documents are already ordered by relevance (most relevant first). + If False, sorts documents by label values. Defaults to True. + + References: + - Listwise approach to learning to rank: theory and algorithm: https://dl.acm.org/doi/abs/10.1145/1390156.1390306 + - `Cross Encoder > Training Examples > MS MARCO <../../../examples/cross_encoder/training/ms_marco/README.html>`_ + + Requirements: + 1. Query with multiple documents (listwise approach) + 2. Documents must have relevance scores/labels. Both binary and continuous labels are supported. + 3. Documents must be sorted in a defined rank order. + + Inputs: + +----------------------------------------+--------------------------------+-------------------------------+ + | Texts | Labels | Number of Model Output Labels | + +========================================+================================+===============================+ + | (query, [doc1, doc2, ..., docN]) | [score1, score2, ..., scoreN] | 1 | + +----------------------------------------+--------------------------------+-------------------------------+ + + Recommendations: + - Use :class:`~sentence_transformers.util.mine_hard_negatives` with ``output_format="labeled-list"`` + to convert question-answer pairs to the required input format with hard negatives. + + Relations: + - The :class:`~sentence_transformers.cross_encoder.losses.PListMLELoss` is an extension of the + :class:`~sentence_transformers.cross_encoder.losses.ListMLELoss` and allows for positional weighting + of the loss. :class:`~sentence_transformers.cross_encoder.losses.PListMLELoss` generally outperforms + :class:`~sentence_transformers.cross_encoder.losses.ListMLELoss` and is recommended over it. + - :class:`~sentence_transformers.cross_encoder.losses.LambdaLoss` takes the same inputs, and generally + outperforms this loss. + + Example: + :: + + from sentence_transformers.cross_encoder import CrossEncoder, CrossEncoderTrainer, losses + from datasets import Dataset + + model = CrossEncoder("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "query": ["What are pandas?", "What is the capital of France?"], + "docs": [ + ["Pandas are a kind of bear.", "Pandas are kind of like fish."], + ["The capital of France is Paris.", "Paris is the capital of France.", "Paris is quite large."], + ], + "labels": [[1, 0], [1, 1, 0]], + }) + + # Standard ListMLE loss respecting input order + loss = losses.ListMLELoss(model) + + trainer = CrossEncoderTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__( + model=model, + lambda_weight=None, + activation_fn=activation_fn, + mini_batch_size=mini_batch_size, + respect_input_order=respect_input_order, + ) + + def get_config_dict(self) -> dict[str, float | int | str | None]: + """ + Get configuration parameters for this loss function. + + Returns: + Dictionary containing the configuration parameters + """ + config = super().get_config_dict() + del config["lambda_weight"] + return config + + @property + def citation(self) -> str: + return """ +@inproceedings{10.1145/1390156.1390306, + title = {Listwise Approach to Learning to Rank - Theory and Algorithm}, + author = {Xia, Fen and Liu, Tie-Yan and Wang, Jue and Zhang, Wensheng and Li, Hang}, + booktitle = {Proceedings of the 25th International Conference on Machine Learning}, + pages = {1192-1199}, + year = {2008}, + url = {https://doi.org/10.1145/1390156.1390306}, +} +""" diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/ListNetLoss.py b/sentence-transformers/sentence_transformers/cross_encoder/losses/ListNetLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..8032d76fd3c8bd29a2d3f1abe82a925c3edc57df --- /dev/null +++ b/sentence-transformers/sentence_transformers/cross_encoder/losses/ListNetLoss.py @@ -0,0 +1,197 @@ +from __future__ import annotations + +import torch +from torch import Tensor, nn + +from sentence_transformers.cross_encoder import CrossEncoder +from sentence_transformers.util import fullname + + +class ListNetLoss(nn.Module): + def __init__( + self, + model: CrossEncoder, + activation_fn: nn.Module | None = nn.Identity(), + mini_batch_size: int | None = None, + ) -> None: + """ + ListNet loss for learning to rank. This loss function implements the ListNet ranking algorithm + which uses a list-wise approach to learn ranking models. It minimizes the cross entropy + between the predicted ranking distribution and the ground truth ranking distribution. + The implementation is optimized to handle padded documents efficiently by only processing + valid documents during model inference. + + .. note:: + + The number of documents per query can vary between samples with the ``ListNetLoss``. + + Args: + model (CrossEncoder): CrossEncoder model to be trained + activation_fn (:class:`~torch.nn.Module`): Activation function applied to the logits before computing the + loss. Defaults to :class:`~torch.nn.Identity`. + mini_batch_size (int, optional): Number of samples to process in each forward pass. This has a significant + impact on the memory consumption and speed of the training process. Three cases are possible: + + - If ``mini_batch_size`` is None, the ``mini_batch_size`` is set to the batch size. + - If ``mini_batch_size`` is greater than 0, the batch is split into mini-batches of size ``mini_batch_size``. + - If ``mini_batch_size`` is <= 0, the entire batch is processed at once. + + Defaults to None. + + References: + - Learning to Rank: From Pairwise Approach to Listwise Approach: https://www.microsoft.com/en-us/research/publication/learning-to-rank-from-pairwise-approach-to-listwise-approach/ + - Context-Aware Learning to Rank with Self-Attention: https://arxiv.org/abs/2005.10084 + - `Cross Encoder > Training Examples > MS MARCO <../../../examples/cross_encoder/training/ms_marco/README.html>`_ + + Requirements: + 1. Query with multiple documents (listwise approach) + 2. Documents must have relevance scores/labels. Both binary and continuous labels are supported. + + Inputs: + +----------------------------------------+--------------------------------+-------------------------------+ + | Texts | Labels | Number of Model Output Labels | + +========================================+================================+===============================+ + | (query, [doc1, doc2, ..., docN]) | [score1, score2, ..., scoreN] | 1 | + +----------------------------------------+--------------------------------+-------------------------------+ + + Recommendations: + - Use :class:`~sentence_transformers.util.mine_hard_negatives` with ``output_format="labeled-list"`` + to convert question-answer pairs to the required input format with hard negatives. + + Relations: + - :class:`~sentence_transformers.cross_encoder.losses.LambdaLoss` takes the same inputs, and generally + outperforms this loss. + + Example: + :: + + from sentence_transformers.cross_encoder import CrossEncoder, CrossEncoderTrainer, losses + from datasets import Dataset + + model = CrossEncoder("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "query": ["What are pandas?", "What is the capital of France?"], + "docs": [ + ["Pandas are a kind of bear.", "Pandas are kind of like fish."], + ["The capital of France is Paris.", "Paris is the capital of France.", "Paris is quite large."], + ], + "labels": [[1, 0], [1, 1, 0]], + }) + loss = losses.ListNetLoss(model) + + trainer = CrossEncoderTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.model = model + self.activation_fn = activation_fn or nn.Identity() + self.mini_batch_size = mini_batch_size + self.cross_entropy_loss = nn.CrossEntropyLoss() + + if self.model.num_labels != 1: + raise ValueError( + f"{self.__class__.__name__} supports a model with 1 output label, " + f"but got a model with {self.model.num_labels} output labels." + ) + + def forward(self, inputs: list[list[str], list[list[str]]], labels: list[Tensor]) -> Tensor: + """ + Compute ListNet loss for a batch of queries and their documents. + + Args: + inputs: List of (queries, documents_list) + labels: Ground truth relevance scores, shape (batch_size, num_documents) + + Returns: + Tensor: Mean ListNet loss over the batch + """ + if isinstance(labels, Tensor): + raise ValueError( + "ListNetLoss expects a list of labels for each sample, but got a single value for each sample." + ) + + if len(inputs) != 2: + raise ValueError( + f"ListNetLoss expects two inputs (queries, documents_list), but got {len(inputs)} inputs." + ) + + queries, docs_list = inputs + docs_per_query = [len(docs) for docs in docs_list] + max_docs = max(docs_per_query) + batch_size = len(queries) + + if docs_per_query != [len(labels) for labels in labels]: + raise ValueError( + f"Number of documents per query in inputs ({docs_per_query}) does not match number of labels per query ({[len(labels) for labels in labels]})." + ) + + pairs = [(query, document) for query, docs in zip(queries, docs_list) for document in docs] + + if not pairs: + # Handle edge case where there are no documents + return torch.tensor(0.0, device=self.model.device, requires_grad=True) + + mini_batch_size = self.mini_batch_size or batch_size + if mini_batch_size <= 0: + mini_batch_size = len(pairs) + + logits_list = [] + for i in range(0, len(pairs), mini_batch_size): + mini_batch_pairs = pairs[i : i + mini_batch_size] + + tokens = self.model.tokenizer( + mini_batch_pairs, + padding=True, + truncation=True, + return_tensors="pt", + ) + tokens = tokens.to(self.model.device) + + logits = self.model(**tokens)[0].view(-1) + logits_list.append(logits) + + logits = torch.cat(logits_list, dim=0) + logits = self.activation_fn(logits) + + # Create output tensor filled with 0 (padded logits will be ignored via labels) + logits_matrix = torch.full((batch_size, max_docs), -1e16, device=self.model.device) + + # Place logits in the desired positions in the logit matrix + doc_indices = torch.cat([torch.arange(len(docs)) for docs in docs_list], dim=0) + batch_indices = torch.repeat_interleave(torch.arange(batch_size), torch.tensor(docs_per_query)) + logits_matrix[batch_indices, doc_indices] = logits + + # Idem for labels, but fill with -inf to 0 out padded logits in the loss + labels_matrix = torch.full_like(logits_matrix, float("-inf")) + labels_matrix[batch_indices, doc_indices] = torch.cat(labels, dim=0).float() + labels_matrix = labels_matrix.to(self.model.device) + + # Compute cross entropy loss between distributions + loss = self.cross_entropy_loss(logits_matrix, labels_matrix.softmax(dim=1)) + + return loss + + def get_config_dict(self) -> dict[str, float]: + """ + Get configuration parameters for this loss function. + + Returns: + Dictionary containing the configuration parameters + """ + return {"activation_fn": fullname(self.activation_fn), "mini_batch_size": self.mini_batch_size} + + @property + def citation(self) -> str: + return """ +@inproceedings{cao2007learning, + title={Learning to Rank: From Pairwise Approach to Listwise Approach}, + author={Cao, Zhe and Qin, Tao and Liu, Tie-Yan and Tsai, Ming-Feng and Li, Hang}, + booktitle={Proceedings of the 24th international conference on Machine learning}, + pages={129--136}, + year={2007} +} +""" diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/MSELoss.py b/sentence-transformers/sentence_transformers/cross_encoder/losses/MSELoss.py new file mode 100644 index 0000000000000000000000000000000000000000..66ca0a707ded6ddcfb77a26add228ea137f053e2 --- /dev/null +++ b/sentence-transformers/sentence_transformers/cross_encoder/losses/MSELoss.py @@ -0,0 +1,110 @@ +from __future__ import annotations + +from torch import Tensor, nn + +from sentence_transformers.cross_encoder.CrossEncoder import CrossEncoder +from sentence_transformers.util import fullname + + +class MSELoss(nn.Module): + def __init__(self, model: CrossEncoder, activation_fn: nn.Module = nn.Identity(), **kwargs) -> None: + """ + Computes the MSE loss between the computed query-passage score and a target query-passage score. This loss + is used to distill a cross-encoder model from a teacher cross-encoder model or gold labels. + + Args: + model (:class:`~sentence_transformers.cross_encoder.CrossEncoder`): A CrossEncoder model to be trained. + activation_fn (:class:`~torch.nn.Module`): Activation function applied to the logits before computing the loss. + **kwargs: Additional keyword arguments passed to the underlying :class:`torch.nn.MSELoss`. + + .. note:: + + Be mindful of the magnitude of both the labels and what the model produces. If the teacher model produces + logits with Sigmoid to bound them to [0, 1], then you may wish to use a Sigmoid activation function in the loss. + + References: + - Improving Efficient Neural Ranking Models with Cross-Architecture Knowledge Distillation: https://arxiv.org/abs/2010.02666 + - `Cross Encoder > Training Examples > Distillation <../../../examples/cross_encoder/training/distillation/README.html>`_ + + Requirements: + 1. Your model must be initialized with `num_labels = 1` (a.k.a. the default) to predict one class. + 2. Usually uses a finetuned CrossEncoder teacher M in a knowledge distillation setup. + + Inputs: + +-----------------------------------------+-----------------------------+-------------------------------+ + | Texts | Labels | Number of Model Output Labels | + +=========================================+=============================+===============================+ + | (sentence_A, sentence_B) pairs | similarity score | 1 | + +-----------------------------------------+-----------------------------+-------------------------------+ + + Relations: + - :class:`MarginMSELoss` is similar to this loss, but with a margin through a negative pair. + + Example: + :: + + from sentence_transformers.cross_encoder import CrossEncoder, CrossEncoderTrainer, losses + from datasets import Dataset + + student_model = CrossEncoder("microsoft/mpnet-base") + teacher_model = CrossEncoder("cross-encoder/ms-marco-MiniLM-L12-v2") + train_dataset = Dataset.from_dict({ + "query": ["What are pandas?", "What is the capital of France?"], + "answer": ["Pandas are a kind of bear.", "The capital of France is Paris."], + }) + + def compute_labels(batch): + return { + "label": teacher_model.predict(list(zip(batch["query"], batch["answer"]))) + } + + train_dataset = train_dataset.map(compute_labels, batched=True) + loss = losses.MSELoss(student_model) + + trainer = CrossEncoderTrainer( + model=student_model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.model = model + self.activation_fn = activation_fn + self.loss_fct = nn.MSELoss(**kwargs) + + if not isinstance(self.model, CrossEncoder): + raise ValueError( + f"{self.__class__.__name__} expects a model of type CrossEncoder, " + f"but got a model of type {type(self.model)}." + ) + + if self.model.num_labels != 1: + raise ValueError( + f"{self.__class__.__name__} expects a model with 1 output label, " + f"but got a model with {self.model.num_labels} output labels." + ) + + def forward(self, inputs: list[list[str]], labels: Tensor) -> Tensor: + if len(inputs) != 2: + raise ValueError( + f"MSELoss expects a dataset with two non-label columns, but got a dataset with {len(inputs)} columns." + ) + + pairs = list(zip(inputs[0], inputs[1])) + tokens = self.model.tokenizer( + pairs, + padding=True, + truncation=True, + return_tensors="pt", + ) + tokens.to(self.model.device) + logits = self.model(**tokens)[0].view(-1) + logits = self.activation_fn(logits) + loss = self.loss_fct(logits, labels.float()) + return loss + + def get_config_dict(self): + return { + "activation_fn": fullname(self.activation_fn), + } diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/MarginMSELoss.py b/sentence-transformers/sentence_transformers/cross_encoder/losses/MarginMSELoss.py new file mode 100644 index 0000000000000000000000000000000000000000..aae96896527ca158bf2ce32464bcff148a968e71 --- /dev/null +++ b/sentence-transformers/sentence_transformers/cross_encoder/losses/MarginMSELoss.py @@ -0,0 +1,143 @@ +from __future__ import annotations + +from torch import Tensor, nn + +from sentence_transformers.cross_encoder.CrossEncoder import CrossEncoder +from sentence_transformers.util import fullname + + +class MarginMSELoss(nn.Module): + def __init__(self, model: CrossEncoder, activation_fn: nn.Module = nn.Identity(), **kwargs) -> None: + """ + Computes the MSE loss between ``|sim(Query, Pos) - sim(Query, Neg)|`` and ``|gold_sim(Query, Pos) - gold_sim(Query, Neg)|``. + This loss is often used to distill a cross-encoder model from a teacher cross-encoder model or gold labels. + + In contrast to :class:`~sentence_transformers.cross_encoder.losses.MultipleNegativesRankingLoss`, the two passages do not + have to be strictly positive and negative, both can be relevant or not relevant for a given query. This can be + an advantage of MarginMSELoss over MultipleNegativesRankingLoss. + + .. note:: + + Be mindful of the magnitude of both the labels and what the model produces. If the teacher model produces + logits with Sigmoid to bound them to [0, 1], then you may wish to use a Sigmoid activation function in the loss. + + Args: + model (:class:`~sentence_transformers.cross_encoder.CrossEncoder`): A CrossEncoder model to be trained. + activation_fn (:class:`~torch.nn.Module`): Activation function applied to the logits before computing the loss. + **kwargs: Additional keyword arguments passed to the underlying :class:`torch.nn.MSELoss`. + + References: + - Improving Efficient Neural Ranking Models with Cross-Architecture Knowledge Distillation: https://arxiv.org/abs/2010.02666 + - `Cross Encoder > Training Examples > Distillation <../../../examples/cross_encoder/training/distillation/README.html>`_ + + Requirements: + 1. Your model must be initialized with `num_labels = 1` (a.k.a. the default) to predict one class. + 2. Usually uses a finetuned CrossEncoder teacher M in a knowledge distillation setup. + + Inputs: + +--------------------------------------------+-------------------------------------------------------------+-------------------------------+ + | Texts | Labels | Number of Model Output Labels | + +============================================+=============================================================+===============================+ + | (query, passage_one, passage_two) triplets | gold_sim(query, passage_one) - gold_sim(query, passage_two) | 1 | + +--------------------------------------------+-------------------------------------------------------------+-------------------------------+ + + Relations: + - :class:`MSELoss` is similar to this loss, but without a margin through the negative pair. + + Example: + :: + + from sentence_transformers.cross_encoder import CrossEncoder, CrossEncoderTrainer, losses + from datasets import Dataset + + student_model = CrossEncoder("microsoft/mpnet-base") + teacher_model = CrossEncoder("cross-encoder/ms-marco-MiniLM-L12-v2") + train_dataset = Dataset.from_dict({ + "query": ["What are pandas?", "What is the capital of France?"], + "positive": ["Pandas are a kind of bear.", "The capital of France is Paris."], + "negative": ["Pandas are a kind of fish.", "The capital of France is Berlin."], + }) + + def compute_labels(batch): + positive_scores = teacher_model.predict(list(zip(batch["query"], batch["positive"]))) + negative_scores = teacher_model.predict(list(zip(batch["query"], batch["negative"]))) + return { + "label": positive_scores - negative_scores + } + + train_dataset = train_dataset.map(compute_labels, batched=True) + loss = losses.MarginMSELoss(student_model) + + trainer = CrossEncoderTrainer( + model=student_model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.model = model + self.activation_fn = activation_fn + self.loss_fct = nn.MSELoss(**kwargs) + + if not isinstance(self.model, CrossEncoder): + raise ValueError( + f"{self.__class__.__name__} expects a model of type CrossEncoder, " + f"but got a model of type {type(self.model)}." + ) + + if self.model.num_labels != 1: + raise ValueError( + f"{self.__class__.__name__} expects a model with 1 output label, " + f"but got a model with {self.model.num_labels} output labels." + ) + + def forward(self, inputs: list[list[str]], labels: Tensor) -> Tensor: + if len(inputs) != 3: + raise ValueError( + f"MSELoss expects a dataset with three non-label columns, but got a dataset with {len(inputs)} columns." + ) + + positive_pairs = list(zip(inputs[0], inputs[1])) + tokens = self.model.tokenizer( + positive_pairs, + padding=True, + truncation=True, + return_tensors="pt", + ) + tokens.to(self.model.device) + positive_logits = self.model(**tokens)[0].view(-1) + positive_logits = self.activation_fn(positive_logits) + + negative_pairs = list(zip(inputs[0], inputs[2])) + tokens = self.model.tokenizer( + negative_pairs, + padding=True, + truncation=True, + return_tensors="pt", + ) + tokens.to(self.model.device) + negative_logits = self.model(**tokens)[0].view(-1) + negative_logits = self.activation_fn(negative_logits) + + margin_logits = positive_logits - negative_logits + loss = self.loss_fct(margin_logits, labels.float()) + return loss + + def get_config_dict(self): + return { + "activation_fn": fullname(self.activation_fn), + } + + @property + def citation(self) -> str: + return """ +@misc{hofstätter2021improving, + title={Improving Efficient Neural Ranking Models with Cross-Architecture Knowledge Distillation}, + author={Sebastian Hofstätter and Sophia Althammer and Michael Schröder and Mete Sertkan and Allan Hanbury}, + year={2021}, + eprint={2010.02666}, + archivePrefix={arXiv}, + primaryClass={cs.IR} +} +""" diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/MultipleNegativesRankingLoss.py b/sentence-transformers/sentence_transformers/cross_encoder/losses/MultipleNegativesRankingLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..8d0afffa95be75765105cac6b05472aaba2d09c0 --- /dev/null +++ b/sentence-transformers/sentence_transformers/cross_encoder/losses/MultipleNegativesRankingLoss.py @@ -0,0 +1,191 @@ +from __future__ import annotations + +from collections.abc import Generator + +import torch +from torch import Tensor, nn + +from sentence_transformers.cross_encoder.CrossEncoder import CrossEncoder +from sentence_transformers.util import fullname + + +class MultipleNegativesRankingLoss(nn.Module): + def __init__( + self, + model: CrossEncoder, + num_negatives: int | None = 4, + scale: int = 10.0, + activation_fn: nn.Module | None = nn.Sigmoid(), + ) -> None: + """ + Given a list of (anchor, positive) pairs or (anchor, positive, negative) triplets, this loss optimizes the following: + + * Given an anchor (e.g. a question), assign the highest similarity to the corresponding positive (i.e. answer) + out of every single positive and negative (e.g. all answers) in the batch. + + If you provide the optional negatives, they will all be used as extra options from which the model must pick the + correct positive. Within reason, the harder this "picking" is, the stronger the model will become. Because of + this, a higher batch size results in more in-batch negatives, which then increases performance (to a point). + + This loss function works great to train embeddings for retrieval setups where you have positive pairs + (e.g. (query, answer)) as it will sample in each batch ``n-1`` negative docs randomly. + + This loss is also known as InfoNCE loss, SimCSE loss, Cross-Entropy Loss with in-batch negatives, or simply + in-batch negatives loss. + + Args: + model (:class:`~sentence_transformers.cross_encoder.CrossEncoder`): A CrossEncoder model to be trained. + num_negatives (int, optional): Number of in-batch negatives to sample for each anchor. Defaults to 4. + scale (int, optional): Output of similarity function is multiplied by scale value. Defaults to 10.0. + activation_fn (:class:`~torch.nn.Module`): Activation function applied to the logits before computing the loss. Defaults to :class:`~torch.nn.Sigmoid`. + + .. note:: + + The current default values are subject to change in the future. Experimentation is encouraged. + + References: + - Efficient Natural Language Response Suggestion for Smart Reply, Section 4.4: https://arxiv.org/pdf/1705.00652.pdf + + Requirements: + 1. Your model must be initialized with `num_labels = 1` (a.k.a. the default) to predict one class. + + Inputs: + +-------------------------------------------------+--------+-------------------------------+ + | Texts | Labels | Number of Model Output Labels | + +=================================================+========+===============================+ + | (anchor, positive) pairs | none | 1 | + +-------------------------------------------------+--------+-------------------------------+ + | (anchor, positive, negative) triplets | none | 1 | + +-------------------------------------------------+--------+-------------------------------+ + | (anchor, positive, negative_1, ..., negative_n) | none | 1 | + +-------------------------------------------------+--------+-------------------------------+ + + Recommendations: + - Use ``BatchSamplers.NO_DUPLICATES`` (:class:`docs `) to + ensure that no in-batch negatives are duplicates of the anchor or positive samples. + - Use :class:`~sentence_transformers.util.mine_hard_negatives` with ``output_format="n-tuple"`` or + ``output_format="triplet"`` to convert question-answer pairs to triplets with hard negatives. + + Relations: + - :class:`CachedMultipleNegativesRankingLoss` is equivalent to this loss, but it uses caching that allows for + much higher batch sizes (and thus better performance) without extra memory usage. However, it is slightly + slower. + + Example: + :: + + from sentence_transformers.cross_encoder import CrossEncoder, CrossEncoderTrainer, losses + from datasets import Dataset + + model = CrossEncoder("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "query": ["What are pandas?", "What is the capital of France?"], + "answer": ["Pandas are a kind of bear.", "The capital of France is Paris."], + }) + loss = losses.MultipleNegativesRankingLoss(model) + + trainer = CrossEncoderTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.model = model + self.num_negatives = num_negatives + self.scale = scale + self.activation_fn = activation_fn + + self.cross_entropy_loss = nn.CrossEntropyLoss() + + if not isinstance(self.model, CrossEncoder): + raise ValueError( + f"{self.__class__.__name__} expects a model of type CrossEncoder, " + f"but got a model of type {type(self.model)}." + ) + + if self.model.num_labels != 1: + raise ValueError( + f"{self.__class__.__name__} expects a model with 1 output label, " + f"but got a model with {self.model.num_labels} output labels." + ) + + def call_model_with_columns(self, anchors: list[str], candidates: list[str]) -> Tensor: + pairs = list(zip(anchors, candidates)) + return self.call_model_with_pairs(pairs) + + def call_model_with_pairs(self, pairs: list[list[str]]) -> Tensor: + tokens = self.model.tokenizer( + pairs, + padding=True, + truncation=True, + return_tensors="pt", + ) + tokens.to(self.model.device) + logits = self.model(**tokens)[0] + return logits.squeeze(1) + + def get_in_batch_negatives( + self, anchors: list[str], candidates: list[list[str]] + ) -> Generator[list[str], None, None]: + batch_size = len(anchors) + num_columns = len(candidates) + + # Given N anchors, we want to select num_negatives negatives for each anchor + candidates_flattened = [candidate for sublist in candidates for candidate in sublist] + + # Create a mask for each anchor to each candidate index, where the matching positive + # and hard negatives are masked out. + mask = ~torch.eye(batch_size, dtype=torch.bool).repeat(1, num_columns) + if self.num_negatives is not None and self.num_negatives < len(candidates_flattened): + # From the remaining options, we randomly select num_negatives indices. + negative_indices = torch.multinomial(mask.float(), self.num_negatives) + else: + # If num_negatives is None or larger than the number of candidates, we select all negatives + # by using the mask as a slicer to get the indices of the negative candidates + all_indices = torch.arange(batch_size).repeat(batch_size * num_columns, 1) + negative_indices = all_indices[mask].reshape(batch_size, -1) + + for negative_indices_row in negative_indices.T: + yield [candidates_flattened[negative_idx] for negative_idx in negative_indices_row] + + def calculate_loss(self, logits: Tensor, batch_size: int) -> Tensor: + # (bsz, 1 + num_rand_negatives + num_hard_negatives) + logits = torch.cat(logits, dim=0).reshape(-1, batch_size).T + + # Apply the post-processing on the logits + if self.activation_fn: + logits = self.activation_fn(logits) + if self.scale: + logits = logits * self.scale + + # For each sample in the batch, the first label is the positive, the rest are negatives + labels = torch.zeros(batch_size, device=logits.device, dtype=torch.long) + + loss = self.cross_entropy_loss(logits, labels) + return loss + + def forward(self, inputs: list[list[str]], labels: Tensor) -> Tensor: + anchors = inputs[0] + positives = inputs[1] + batch_size = len(anchors) + + scores = [self.call_model_with_columns(anchors, positives)] + + # In-batch negatives: + for negatives in self.get_in_batch_negatives(anchors, inputs[1:]): + scores.append(self.call_model_with_columns(anchors, negatives)) + + # Hard negatives: + for negatives in inputs[2:]: + scores.append(self.call_model_with_columns(anchors, negatives)) + + return self.calculate_loss(scores, batch_size) + + def get_config_dict(self) -> dict[str, float]: + return { + "scale": self.scale, + "num_negatives": self.num_negatives, + "activation_fn": fullname(self.activation_fn), + } diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/PListMLELoss.py b/sentence-transformers/sentence_transformers/cross_encoder/losses/PListMLELoss.py new file mode 100644 index 0000000000000000000000000000000000000000..64725bc4f3d677314bcded56acf5c064f49f1a51 --- /dev/null +++ b/sentence-transformers/sentence_transformers/cross_encoder/losses/PListMLELoss.py @@ -0,0 +1,294 @@ +from __future__ import annotations + +import torch +from torch import Tensor, nn + +from sentence_transformers.cross_encoder import CrossEncoder +from sentence_transformers.util import fullname + + +class PListMLELambdaWeight(nn.Module): + """Base class for implementing weighting schemes in Position-Aware ListMLE Loss.""" + + def __init__(self, rank_discount_fn=None) -> None: + """ + Initialize a lambda weight for PListMLE loss. + + Args: + rank_discount_fn: Function that computes a discount for each rank position. + If None, uses default discount of 2^(num_docs - rank) - 1. + """ + super().__init__() + self.rank_discount_fn = rank_discount_fn + + def forward(self, mask: Tensor) -> Tensor: + """ + Calculate position-aware weights for the PListMLE loss. + + Args: + mask: A boolean mask indicating valid positions [batch_size, num_docs] + + Returns: + Tensor: Weights for each position [batch_size, num_docs] + """ + if self.rank_discount_fn is not None: + return self.rank_discount_fn(mask) + + # Apply default rank discount: 2^(num_docs - rank) - 1 + num_docs_per_query = mask.sum(dim=1, keepdim=True) + ranks = torch.arange(mask.size(1), device=mask.device).expand_as(mask) + weights = torch.pow(2.0, num_docs_per_query - ranks) - 1.0 + weights = weights * mask + return weights + + +class PListMLELoss(nn.Module): + def __init__( + self, + model: CrossEncoder, + lambda_weight: PListMLELambdaWeight | None = PListMLELambdaWeight(), + activation_fn: nn.Module | None = nn.Identity(), + mini_batch_size: int | None = None, + respect_input_order: bool = True, + ) -> None: + """ + PListMLE loss for learning to rank with position-aware weighting. This loss function implements + the ListMLE ranking algorithm which uses a list-wise approach based on maximum likelihood + estimation of permutations. It maximizes the likelihood of the permutation induced by the + ground truth labels with position-aware weighting. + + This loss is also known as Position-Aware ListMLE or p-ListMLE. + + .. note:: + + The number of documents per query can vary between samples with the ``PListMLELoss``. + + Args: + model (CrossEncoder): CrossEncoder model to be trained + lambda_weight (PListMLELambdaWeight, optional): Weighting scheme to use. When specified, + implements Position-Aware ListMLE which applies different weights to different rank + positions. Default is None (standard PListMLE). + activation_fn (:class:`~torch.nn.Module`): Activation function applied to the logits before computing the + loss. Defaults to :class:`~torch.nn.Identity`. + mini_batch_size (int, optional): Number of samples to process in each forward pass. This has a significant + impact on the memory consumption and speed of the training process. Three cases are possible: + + - If ``mini_batch_size`` is None, the ``mini_batch_size`` is set to the batch size. + - If ``mini_batch_size`` is greater than 0, the batch is split into mini-batches of size ``mini_batch_size``. + - If ``mini_batch_size`` is <= 0, the entire batch is processed at once. + + Defaults to None. + respect_input_order (bool): Whether to respect the original input order of documents. + If True, assumes the input documents are already ordered by relevance (most relevant first). + If False, sorts documents by label values. Defaults to True. + + References: + - Position-Aware ListMLE: A Sequential Learning Process for Ranking: https://auai.org/uai2014/proceedings/individuals/164.pdf + - `Cross Encoder > Training Examples > MS MARCO <../../../examples/cross_encoder/training/ms_marco/README.html>`_ + + Requirements: + 1. Query with multiple documents (listwise approach) + 2. Documents must have relevance scores/labels. Both binary and continuous labels are supported. + 3. Documents must be sorted in a defined rank order. + + Inputs: + +----------------------------------------+--------------------------------+-------------------------------+ + | Texts | Labels | Number of Model Output Labels | + +========================================+================================+===============================+ + | (query, [doc1, doc2, ..., docN]) | [score1, score2, ..., scoreN] | 1 | + +----------------------------------------+--------------------------------+-------------------------------+ + + Recommendations: + - Use :class:`~sentence_transformers.util.mine_hard_negatives` with ``output_format="labeled-list"`` + to convert question-answer pairs to the required input format with hard negatives. + + Relations: + - The :class:`~sentence_transformers.cross_encoder.losses.PListMLELoss` is an extension of the + :class:`~sentence_transformers.cross_encoder.losses.ListMLELoss` and allows for positional weighting + of the loss. :class:`~sentence_transformers.cross_encoder.losses.PListMLELoss` generally outperforms + :class:`~sentence_transformers.cross_encoder.losses.ListMLELoss` and is recommended over it. + - :class:`~sentence_transformers.cross_encoder.losses.LambdaLoss` takes the same inputs, and generally + outperforms this loss. + + Example: + :: + + from sentence_transformers.cross_encoder import CrossEncoder, CrossEncoderTrainer, losses + from datasets import Dataset + + model = CrossEncoder("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "query": ["What are pandas?", "What is the capital of France?"], + "docs": [ + ["Pandas are a kind of bear.", "Pandas are kind of like fish."], + ["The capital of France is Paris.", "Paris is the capital of France.", "Paris is quite large."], + ], + "labels": [[1, 0], [1, 1, 0]], + }) + + # Either: Position-Aware ListMLE with default weighting + lambda_weight = losses.PListMLELambdaWeight() + loss = losses.PListMLELoss(model, lambda_weight=lambda_weight) + + # or: Position-Aware ListMLE with custom weighting function + def custom_discount(ranks): # e.g. ranks: [1, 2, 3, 4, 5] + return 1.0 / torch.log1p(ranks) + lambda_weight = losses.PListMLELambdaWeight(rank_discount_fn=custom_discount) + loss = losses.PListMLELoss(model, lambda_weight=lambda_weight) + + trainer = CrossEncoderTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.model = model + self.lambda_weight = lambda_weight + self.activation_fn = activation_fn or nn.Identity() + self.mini_batch_size = mini_batch_size + self.respect_input_order = respect_input_order + self.eps = 1e-10 + + if self.model.num_labels != 1: + raise ValueError( + f"{self.__class__.__name__} supports a model with 1 output label, " + f"but got a model with {self.model.num_labels} output labels." + ) + + def forward(self, inputs: list[list[str], list[list[str]]], labels: list[Tensor]) -> Tensor: + """ + Compute PListMLE loss for a batch of queries and their documents. + + Args: + inputs: List of (queries, documents_list) + labels: Ground truth relevance scores, shape (batch_size, num_documents) + + Returns: + Tensor: Mean PListMLE loss over the batch + """ + if isinstance(labels, Tensor): + raise ValueError( + "PListMLELoss expects a list of labels for each sample, but got a single value for each sample." + ) + + if len(inputs) != 2: + raise ValueError( + f"PListMLELoss expects two inputs (queries, documents_list), but got {len(inputs)} inputs." + ) + + queries, docs_list = inputs + docs_per_query = [len(docs) for docs in docs_list] + max_docs = max(docs_per_query) + batch_size = len(queries) + + if docs_per_query != [len(labels) for labels in labels]: + raise ValueError( + f"Number of documents per query in inputs ({docs_per_query}) does not match number of labels per query ({[len(labels) for labels in labels]})." + ) + + pairs = [(query, document) for query, docs in zip(queries, docs_list) for document in docs] + + if not pairs: + # Handle edge case where there are no documents + return torch.tensor(0.0, device=self.model.device, requires_grad=True) + + mini_batch_size = self.mini_batch_size or batch_size + if mini_batch_size <= 0: + mini_batch_size = len(pairs) + + logits_list = [] + for i in range(0, len(pairs), mini_batch_size): + mini_batch_pairs = pairs[i : i + mini_batch_size] + + tokens = self.model.tokenizer( + mini_batch_pairs, + padding=True, + truncation=True, + return_tensors="pt", + ) + tokens = tokens.to(self.model.device) + + logits = self.model(**tokens)[0].view(-1) + logits_list.append(logits) + + logits = torch.cat(logits_list, dim=0) + logits = self.activation_fn(logits) + + # Create output tensor filled with a very small value for padded logits + logits_matrix = torch.full((batch_size, max_docs), 1e-16, device=self.model.device) + + # Place logits in the desired positions in the logit matrix + doc_indices = torch.cat([torch.arange(len(docs)) for docs in docs_list], dim=0) + batch_indices = torch.repeat_interleave(torch.arange(batch_size), torch.tensor(docs_per_query)) + logits_matrix[batch_indices, doc_indices] = logits + + # Create a mask for valid entries + mask = torch.zeros_like(logits_matrix, dtype=torch.bool) + mask[batch_indices, doc_indices] = True + + # Convert labels to tensor matrix + labels_matrix = torch.full_like(logits_matrix, -float("inf")) + labels_matrix[batch_indices, doc_indices] = torch.cat(labels, dim=0).float() + + if not torch.any(mask): + return torch.tensor(0.0, device=self.model.device, requires_grad=True) + + if not self.respect_input_order: + # Sort by labels in descending order if not respecting input order. + sorted_labels, indices = labels_matrix.sort(descending=True, dim=1) + sorted_logits = torch.gather(logits_matrix, 1, indices) + else: + # Use the original input order, assuming it's already ordered by relevance + sorted_logits = logits_matrix + + # Compute log-likelihood using Plackett-Luce model + scores = sorted_logits.exp() + cumsum_scores = torch.flip(torch.cumsum(torch.flip(scores, [1]), 1), [1]) + log_probs = sorted_logits - torch.log(cumsum_scores + self.eps) + + # Apply position-aware lambda weights if specified. If None, then this loss + # is just ListMLE. + if self.lambda_weight is not None: + lambda_weight = self.lambda_weight(mask) + # Normalize weights to sum to 1 + lambda_weight = lambda_weight / (lambda_weight.sum(dim=1, keepdim=True) + self.eps) + log_probs = log_probs * lambda_weight + + # Sum the log probabilities for each list and mask padded entries + log_probs[~mask] = 0.0 + per_query_losses = -torch.sum(log_probs, dim=1) + + if not torch.any(per_query_losses): + return torch.tensor(0.0, device=self.model.device, requires_grad=True) + + # Average loss over all lists + return torch.mean(per_query_losses) + + def get_config_dict(self) -> dict[str, float | int | str | None]: + """ + Get configuration parameters for this loss function. + + Returns: + Dictionary containing the configuration parameters + """ + return { + "lambda_weight": None if self.lambda_weight is None else fullname(self.lambda_weight), + "activation_fn": fullname(self.activation_fn), + "mini_batch_size": self.mini_batch_size, + "respect_input_order": self.respect_input_order, + } + + @property + def citation(self) -> str: + return """ +@inproceedings{lan2014position, + title={Position-Aware ListMLE: A Sequential Learning Process for Ranking}, + author={Lan, Yanyan and Zhu, Yadong and Guo, Jiafeng and Niu, Shuzi and Cheng, Xueqi}, + booktitle={UAI}, + volume={14}, + pages={449--458}, + year={2014} +} +""" diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/RankNetLoss.py b/sentence-transformers/sentence_transformers/cross_encoder/losses/RankNetLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..e1a8facecf04628771d916cf0138b32a99907b47 --- /dev/null +++ b/sentence-transformers/sentence_transformers/cross_encoder/losses/RankNetLoss.py @@ -0,0 +1,123 @@ +from __future__ import annotations + +from typing import Literal + +from torch import nn + +from sentence_transformers.cross_encoder import CrossEncoder +from sentence_transformers.cross_encoder.losses import LambdaLoss, NoWeightingScheme + + +class RankNetLoss(LambdaLoss): + def __init__( + self, + model: CrossEncoder, + k: int | None = None, + sigma: float = 1.0, + eps: float = 1e-10, + reduction_log: Literal["natural", "binary"] = "binary", + activation_fn: nn.Module | None = nn.Identity(), + mini_batch_size: int | None = None, + ) -> None: + """ + RankNet loss implementation for learning to rank. This loss function implements the RankNet algorithm, + which learns a ranking function by optimizing pairwise document comparisons using a neural network. + The implementation is optimized to handle padded documents efficiently by only processing valid + documents during model inference. + + Args: + model (CrossEncoder): CrossEncoder model to be trained + sigma (float): Score difference weight used in sigmoid (default: 1.0) + eps (float): Small constant for numerical stability (default: 1e-10) + activation_fn (:class:`~torch.nn.Module`): Activation function applied to the logits before computing the + loss. Defaults to :class:`~torch.nn.Identity`. + mini_batch_size (int, optional): Number of samples to process in each forward pass. This has a significant + impact on the memory consumption and speed of the training process. Three cases are possible: + - If ``mini_batch_size`` is None, the ``mini_batch_size`` is set to the batch size. + - If ``mini_batch_size`` is greater than 0, the batch is split into mini-batches of size ``mini_batch_size``. + - If ``mini_batch_size`` is <= 0, the entire batch is processed at once. + Defaults to None. + + References: + - Learning to Rank using Gradient Descent: https://icml.cc/Conferences/2015/wp-content/uploads/2015/06/icml_ranking.pdf + - `Cross Encoder > Training Examples > MS MARCO <../../../examples/cross_encoder/training/ms_marco/README.html>`_ + + Requirements: + 1. Query with multiple documents (pairwise approach) + 2. Documents must have relevance scores/labels. Both binary and continuous labels are supported. + + Inputs: + +----------------------------------------+--------------------------------+-------------------------------+ + | Texts | Labels | Number of Model Output Labels | + +========================================+================================+===============================+ + | (query, [doc1, doc2, ..., docN]) | [score1, score2, ..., scoreN] | 1 | + +----------------------------------------+--------------------------------+-------------------------------+ + + Recommendations: + - Use :class:`~sentence_transformers.util.mine_hard_negatives` with ``output_format="labeled-list"`` + to convert question-answer pairs to the required input format with hard negatives. + + Relations: + - :class:`~sentence_transformers.cross_encoder.losses.LambdaLoss` can be seen as an extension of this loss + where each score pair is weighted. Alternatively, this loss can be seen as a special case of the + :class:`~sentence_transformers.cross_encoder.losses.LambdaLoss` without a weighting scheme. + - :class:`~sentence_transformers.cross_encoder.losses.LambdaLoss` with its default NDCGLoss2++ weighting + scheme anecdotally performs better than the other losses with the same input format. + + Example: + :: + + from sentence_transformers.cross_encoder import CrossEncoder, CrossEncoderTrainer, losses + from datasets import Dataset + + model = CrossEncoder("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "query": ["What are pandas?", "What is the capital of France?"], + "docs": [ + ["Pandas are a kind of bear.", "Pandas are kind of like fish."], + ["The capital of France is Paris.", "Paris is the capital of France.", "Paris is quite large."], + ], + "labels": [[1, 0], [1, 1, 0]], + }) + loss = losses.RankNetLoss(model) + + trainer = CrossEncoderTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__( + model=model, + weighting_scheme=NoWeightingScheme(), + k=k, + sigma=sigma, + eps=eps, + reduction_log=reduction_log, + activation_fn=activation_fn, + mini_batch_size=mini_batch_size, + ) + + def get_config_dict(self) -> dict[str, float | int | str | None]: + """ + Get configuration parameters for this loss function. + + Returns: + Dictionary containing the configuration parameters + """ + config = super().get_config_dict() + del config["weighting_scheme"] + return config + + @property + def citation(self) -> str: + return """ +@inproceedings{burges2005learning, + title={Learning to Rank using Gradient Descent}, + author={Burges, Chris and Shaked, Tal and Renshaw, Erin and Lazier, Ari and Deeds, Matt and Hamilton, Nicole and Hullender, Greg}, + booktitle={Proceedings of the 22nd international conference on Machine learning}, + pages={89--96}, + year={2005} +} +""" diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/__init__.py b/sentence-transformers/sentence_transformers/cross_encoder/losses/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a048d52651c0568c1a41c92c9182c69a529fc355 --- /dev/null +++ b/sentence-transformers/sentence_transformers/cross_encoder/losses/__init__.py @@ -0,0 +1,40 @@ +from __future__ import annotations + +from .BinaryCrossEntropyLoss import BinaryCrossEntropyLoss +from .CachedMultipleNegativesRankingLoss import CachedMultipleNegativesRankingLoss +from .CrossEntropyLoss import CrossEntropyLoss +from .LambdaLoss import ( + LambdaLoss, + LambdaRankScheme, + NDCGLoss1Scheme, + NDCGLoss2PPScheme, + NDCGLoss2Scheme, + NoWeightingScheme, +) +from .ListMLELoss import ListMLELoss +from .ListNetLoss import ListNetLoss +from .MarginMSELoss import MarginMSELoss +from .MSELoss import MSELoss +from .MultipleNegativesRankingLoss import MultipleNegativesRankingLoss +from .PListMLELoss import PListMLELambdaWeight, PListMLELoss +from .RankNetLoss import RankNetLoss + +__all__ = [ + "BinaryCrossEntropyLoss", + "CrossEntropyLoss", + "MultipleNegativesRankingLoss", + "CachedMultipleNegativesRankingLoss", + "MarginMSELoss", + "MSELoss", + "ListNetLoss", + "ListMLELoss", + "PListMLELoss", + "PListMLELambdaWeight", + "LambdaLoss", + "NoWeightingScheme", + "NDCGLoss1Scheme", + "NDCGLoss2Scheme", + "LambdaRankScheme", + "NDCGLoss2PPScheme", + "RankNetLoss", +] diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/BinaryCrossEntropyLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/BinaryCrossEntropyLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..75259288980141ee2e97192bfac1b708b5ab9b0d Binary files /dev/null and b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/BinaryCrossEntropyLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/CachedMultipleNegativesRankingLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/CachedMultipleNegativesRankingLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54f8838db82b590e3867b2ff912173d817931bb6 Binary files /dev/null and b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/CachedMultipleNegativesRankingLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/CrossEntropyLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/CrossEntropyLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..74b3d4a6df09cd612ee3fc15505f614bbfffcbf9 Binary files /dev/null and b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/CrossEntropyLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/LambdaLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/LambdaLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fe51f677d20dfe12fb242fd58827d3076936b103 Binary files /dev/null and b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/LambdaLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/ListMLELoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/ListMLELoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..197f845a799e206fb39cbbc6e29d00a8fb9c0789 Binary files /dev/null and b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/ListMLELoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/ListNetLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/ListNetLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b4fa834334f6871b6d03a1a8574ab3e89d81c5a9 Binary files /dev/null and b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/ListNetLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/MSELoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/MSELoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5a61209231d5b81399cb00dcd2e57e0da8c6598b Binary files /dev/null and b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/MSELoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/MarginMSELoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/MarginMSELoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..42a3b9f2dc595ed3506ef0fb78243b728343db46 Binary files /dev/null and b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/MarginMSELoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/MultipleNegativesRankingLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/MultipleNegativesRankingLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..160a31785f5911eed155f93be751fc13dde86d3b Binary files /dev/null and b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/MultipleNegativesRankingLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/PListMLELoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/PListMLELoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0be0e9c2510095f4797eaa136c989ee657cf371b Binary files /dev/null and b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/PListMLELoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/RankNetLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/RankNetLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5908642434c2da193b25503b09a249b0973675f8 Binary files /dev/null and b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/RankNetLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/__init__.cpython-312.pyc b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e67573f5f18c5ec29f1cf99511d3abcf53693d18 Binary files /dev/null and b/sentence-transformers/sentence_transformers/cross_encoder/losses/__pycache__/__init__.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/datasets/DenoisingAutoEncoderDataset.py b/sentence-transformers/sentence_transformers/datasets/DenoisingAutoEncoderDataset.py new file mode 100644 index 0000000000000000000000000000000000000000..cc2d1b172229b7c23002b790fb78ef3cc55e69e3 --- /dev/null +++ b/sentence-transformers/sentence_transformers/datasets/DenoisingAutoEncoderDataset.py @@ -0,0 +1,62 @@ +""" +This file contains deprecated code that can only be used with the old `model.fit`-style Sentence Transformers v2.X training. +It exists for backwards compatibility with the `model.old_fit` method, but will be removed in a future version. + +Nowadays, with Sentence Transformers v3+, it is recommended to use the `SentenceTransformerTrainer` class to train models. +See https://www.sbert.net/docs/sentence_transformer/training_overview.html for more information. + +See this script for more details on how to use the new training API: +https://github.com/UKPLab/sentence-transformers/blob/master/examples/sentence_transformer/unsupervised_learning/TSDAE/train_stsb_tsdae.py +""" + +from __future__ import annotations + +import numpy as np +from torch.utils.data import Dataset +from transformers.utils.import_utils import NLTK_IMPORT_ERROR, is_nltk_available + +from sentence_transformers.readers.InputExample import InputExample + + +class DenoisingAutoEncoderDataset(Dataset): + """ + The DenoisingAutoEncoderDataset returns InputExamples in the format: texts=[noise_fn(sentence), sentence] + It is used in combination with the DenoisingAutoEncoderLoss: Here, a decoder tries to re-construct the + sentence without noise. + + Args: + sentences: A list of sentences + noise_fn: A noise function: Given a string, it returns a string + with noise, e.g. deleted words + """ + + def __init__(self, sentences: list[str], noise_fn=lambda s: DenoisingAutoEncoderDataset.delete(s)): + if not is_nltk_available(): + raise ImportError(NLTK_IMPORT_ERROR.format(self.__class__.__name__)) + + self.sentences = sentences + self.noise_fn = noise_fn + + def __getitem__(self, item): + sent = self.sentences[item] + return InputExample(texts=[self.noise_fn(sent), sent]) + + def __len__(self): + return len(self.sentences) + + # Deletion noise. + @staticmethod + def delete(text, del_ratio=0.6): + from nltk import word_tokenize + from nltk.tokenize.treebank import TreebankWordDetokenizer + + words = word_tokenize(text) + n = len(words) + if n == 0: + return text + + keep_or_not = np.random.rand(n) > del_ratio + if sum(keep_or_not) == 0: + keep_or_not[np.random.choice(n)] = True # guarantee that at least one word remains + words_processed = TreebankWordDetokenizer().detokenize(np.array(words)[keep_or_not]) + return words_processed diff --git a/sentence-transformers/sentence_transformers/datasets/NoDuplicatesDataLoader.py b/sentence-transformers/sentence_transformers/datasets/NoDuplicatesDataLoader.py new file mode 100644 index 0000000000000000000000000000000000000000..578a0960353e840cd049210d3b861ea094f589fa --- /dev/null +++ b/sentence-transformers/sentence_transformers/datasets/NoDuplicatesDataLoader.py @@ -0,0 +1,60 @@ +""" +This file contains deprecated code that can only be used with the old `model.fit`-style Sentence Transformers v2.X training. +It exists for backwards compatibility with the `model.old_fit` method, but will be removed in a future version. + +Nowadays, with Sentence Transformers v3+, it is recommended to use the `SentenceTransformerTrainer` class to train models. +See https://www.sbert.net/docs/sentence_transformer/training_overview.html for more information. + +In particular, you can pass "no_duplicates" to `batch_sampler` in the `SentenceTransformerTrainingArguments` class. +""" + +from __future__ import annotations + +import math +import random + + +class NoDuplicatesDataLoader: + def __init__(self, train_examples, batch_size): + """ + A special data loader to be used with MultipleNegativesRankingLoss. + The data loader ensures that there are no duplicate sentences within the same batch + """ + self.batch_size = batch_size + self.data_pointer = 0 + self.collate_fn = None + self.train_examples = train_examples + random.shuffle(self.train_examples) + + def __iter__(self): + for _ in range(self.__len__()): + batch = [] + texts_in_batch = set() + + while len(batch) < self.batch_size: + example = self.train_examples[self.data_pointer] + + valid_example = True + for text in example.texts: + if not isinstance(text, str): + text = str(text) + if text.strip().lower() in texts_in_batch: + valid_example = False + break + + if valid_example: + batch.append(example) + for text in example.texts: + if not isinstance(text, str): + text = str(text) + texts_in_batch.add(text.strip().lower()) + + self.data_pointer += 1 + if self.data_pointer >= len(self.train_examples): + self.data_pointer = 0 + random.shuffle(self.train_examples) + + yield self.collate_fn(batch) if self.collate_fn is not None else batch + + def __len__(self): + return math.floor(len(self.train_examples) / self.batch_size) diff --git a/sentence-transformers/sentence_transformers/datasets/ParallelSentencesDataset.py b/sentence-transformers/sentence_transformers/datasets/ParallelSentencesDataset.py new file mode 100644 index 0000000000000000000000000000000000000000..300a6f6cc77be677451859ca85bf692a31dec14a --- /dev/null +++ b/sentence-transformers/sentence_transformers/datasets/ParallelSentencesDataset.py @@ -0,0 +1,204 @@ +""" +This file contains deprecated code that can only be used with the old `model.fit`-style Sentence Transformers v2.X training. +It exists for backwards compatibility with the `model.old_fit` method, but will be removed in a future version. + +Nowadays, with Sentence Transformers v3+, it is recommended to use the `SentenceTransformerTrainer` class to train models. +See https://www.sbert.net/docs/sentence_transformer/training_overview.html for more information. + +Instead, you should create a `datasets` `Dataset` for training: https://huggingface.co/docs/datasets/create_dataset +""" + +from __future__ import annotations + +import gzip +import logging +import random + +from torch.utils.data import Dataset + +from sentence_transformers import SentenceTransformer +from sentence_transformers.readers import InputExample + +logger = logging.getLogger(__name__) + + +class ParallelSentencesDataset(Dataset): + """ + This dataset reader can be used to read-in parallel sentences, i.e., it reads in a file with tab-seperated sentences with the same + sentence in different languages. For example, the file can look like this (EN\tDE\tES): + hello world hallo welt hola mundo + second sentence zweiter satz segunda oración + + The sentence in the first column will be mapped to a sentence embedding using the given the embedder. For example, + embedder is a mono-lingual sentence embedding method for English. The sentences in the other languages will also be + mapped to this English sentence embedding. + + When getting a sample from the dataset, we get one sentence with the according sentence embedding for this sentence. + + teacher_model can be any class that implement an encode function. The encode function gets a list of sentences and + returns a list of sentence embeddings + """ + + def __init__( + self, + student_model: SentenceTransformer, + teacher_model: SentenceTransformer, + batch_size: int = 8, + use_embedding_cache: bool = True, + ): + """ + Parallel sentences dataset reader to train student model given a teacher model + + Args: + student_model (SentenceTransformer): The student sentence embedding model that should be trained. + teacher_model (SentenceTransformer): The teacher model that provides the sentence embeddings for the first column in the dataset file. + batch_size (int, optional): The batch size for training. Defaults to 8. + use_embedding_cache (bool, optional): Whether to use an embedding cache. Defaults to True. + """ + self.student_model = student_model + self.teacher_model = teacher_model + self.datasets = [] + self.datasets_iterator = [] + self.datasets_tokenized = [] + self.dataset_indices = [] + self.copy_dataset_indices = [] + self.cache = [] + self.batch_size = batch_size + self.use_embedding_cache = use_embedding_cache + self.embedding_cache = {} + self.num_sentences = 0 + + def load_data( + self, filepath: str, weight: int = 100, max_sentences: int = None, max_sentence_length: int = 128 + ) -> None: + """ + Reads in a tab-seperated .txt/.csv/.tsv or .gz file. The different columns contain the different translations of the sentence in the first column + + Args: + filepath (str): Filepath to the file. + weight (int, optional): If more than one dataset is loaded with load_data, specifies the frequency at which data should be sampled from this dataset. Defaults to 100. + max_sentences (int, optional): Maximum number of lines to be read from the filepath. Defaults to None. + max_sentence_length (int, optional): Skip the example if one of the sentences has more characters than max_sentence_length. Defaults to 128. + + Returns: + None + """ + + logger.info("Load " + filepath) + parallel_sentences = [] + + with ( + gzip.open(filepath, "rt", encoding="utf8") + if filepath.endswith(".gz") + else open(filepath, encoding="utf8") as fIn + ): + count = 0 + for line in fIn: + sentences = line.strip().split("\t") + if ( + max_sentence_length is not None + and max_sentence_length > 0 + and max([len(sent) for sent in sentences]) > max_sentence_length + ): + continue + + parallel_sentences.append(sentences) + count += 1 + if max_sentences is not None and max_sentences > 0 and count >= max_sentences: + break + self.add_dataset( + parallel_sentences, weight=weight, max_sentences=max_sentences, max_sentence_length=max_sentence_length + ) + + def add_dataset( + self, + parallel_sentences: list[list[str]], + weight: int = 100, + max_sentences: int = None, + max_sentence_length: int = 128, + ): + sentences_map = {} + for sentences in parallel_sentences: + if ( + max_sentence_length is not None + and max_sentence_length > 0 + and max([len(sent) for sent in sentences]) > max_sentence_length + ): + continue + + source_sentence = sentences[0] + if source_sentence not in sentences_map: + sentences_map[source_sentence] = set() + + for sent in sentences: + sentences_map[source_sentence].add(sent) + + if max_sentences is not None and max_sentences > 0 and len(sentences_map) >= max_sentences: + break + + if len(sentences_map) == 0: + return + + self.num_sentences += sum([len(sentences_map[sent]) for sent in sentences_map]) + + dataset_id = len(self.datasets) + self.datasets.append(list(sentences_map.items())) + self.datasets_iterator.append(0) + self.dataset_indices.extend([dataset_id] * weight) + + def generate_data(self): + source_sentences_list = [] + target_sentences_list = [] + for data_idx in self.dataset_indices: + src_sentence, trg_sentences = self.next_entry(data_idx) + source_sentences_list.append(src_sentence) + target_sentences_list.append(trg_sentences) + + # Generate embeddings + src_embeddings = self.get_embeddings(source_sentences_list) + + for src_embedding, trg_sentences in zip(src_embeddings, target_sentences_list): + for trg_sentence in trg_sentences: + self.cache.append(InputExample(texts=[trg_sentence], label=src_embedding)) + + random.shuffle(self.cache) + + def next_entry(self, data_idx): + source, target_sentences = self.datasets[data_idx][self.datasets_iterator[data_idx]] + + self.datasets_iterator[data_idx] += 1 + if self.datasets_iterator[data_idx] >= len(self.datasets[data_idx]): # Restart iterator + self.datasets_iterator[data_idx] = 0 + random.shuffle(self.datasets[data_idx]) + + return source, target_sentences + + def get_embeddings(self, sentences): + if not self.use_embedding_cache: + return self.teacher_model.encode( + sentences, batch_size=self.batch_size, show_progress_bar=False, convert_to_numpy=True + ) + + # Use caching + new_sentences = [] + for sent in sentences: + if sent not in self.embedding_cache: + new_sentences.append(sent) + + if len(new_sentences) > 0: + new_embeddings = self.teacher_model.encode( + new_sentences, batch_size=self.batch_size, show_progress_bar=False, convert_to_numpy=True + ) + for sent, embedding in zip(new_sentences, new_embeddings): + self.embedding_cache[sent] = embedding + + return [self.embedding_cache[sent] for sent in sentences] + + def __len__(self): + return self.num_sentences + + def __getitem__(self, idx): + if len(self.cache) == 0: + self.generate_data() + + return self.cache.pop() diff --git a/sentence-transformers/sentence_transformers/datasets/SentenceLabelDataset.py b/sentence-transformers/sentence_transformers/datasets/SentenceLabelDataset.py new file mode 100644 index 0000000000000000000000000000000000000000..ec24549351f33021091a271a7eef1bff40f10694 --- /dev/null +++ b/sentence-transformers/sentence_transformers/datasets/SentenceLabelDataset.py @@ -0,0 +1,111 @@ +""" +This file contains deprecated code that can only be used with the old `model.fit`-style Sentence Transformers v2.X training. +It exists for backwards compatibility with the `model.old_fit` method, but will be removed in a future version. + +Nowadays, with Sentence Transformers v3+, it is recommended to use the `SentenceTransformerTrainer` class to train models. +See https://www.sbert.net/docs/sentence_transformer/training_overview.html for more information. + +In particular, you can pass "group_by_label" to `batch_sampler` in the `SentenceTransformerTrainingArguments` class. +""" + +from __future__ import annotations + +import logging + +import numpy as np +from torch.utils.data import IterableDataset + +from sentence_transformers.readers import InputExample + +logger = logging.getLogger(__name__) + + +class SentenceLabelDataset(IterableDataset): + """ + This dataset can be used for some specific Triplet Losses like BATCH_HARD_TRIPLET_LOSS which requires + multiple examples with the same label in a batch. + + It draws n consecutive, random and unique samples from one label at a time. This is repeated for each label. + + Labels with fewer than n unique samples are ignored. + This also applied to drawing without replacement, once less than n samples remain for a label, it is skipped. + + This *DOES NOT* check if there are more labels than the batch is large or if the batch size is divisible + by the samples drawn per label. + """ + + def __init__(self, examples: list[InputExample], samples_per_label: int = 2, with_replacement: bool = False): + """ + Creates a LabelSampler for a SentenceLabelDataset. + + Args: + examples (List[InputExample]): A list of InputExamples. + samples_per_label (int, optional): The number of consecutive, random, and unique samples drawn per label. + The batch size should be a multiple of samples_per_label. Defaults to 2. + with_replacement (bool, optional): If True, each sample is drawn at most once (depending on the total number + of samples per label). If False, one sample can be drawn in multiple draws, but not multiple times in + the same drawing. Defaults to False. + """ + super().__init__() + + self.samples_per_label = samples_per_label + + # Group examples by label + label2ex = {} + for example in examples: + if example.label not in label2ex: + label2ex[example.label] = [] + label2ex[example.label].append(example) + + # Include only labels with at least 2 examples + self.grouped_inputs = [] + self.groups_right_border = [] + num_labels = 0 + + for label, label_examples in label2ex.items(): + if len(label_examples) >= self.samples_per_label: + self.grouped_inputs.extend(label_examples) + self.groups_right_border.append( + len(self.grouped_inputs) + ) # At which position does this label group / bucket end? + num_labels += 1 + + self.label_range = np.arange(num_labels) + self.with_replacement = with_replacement + np.random.shuffle(self.label_range) + + logger.info( + f"SentenceLabelDataset: {len(examples)} examples, from which {len(self.grouped_inputs)} examples could be used (those labels appeared at least {self.samples_per_label} times). {num_labels} different labels found." + ) + + def __iter__(self): + label_idx = 0 + count = 0 + already_seen = {} + while count < len(self.grouped_inputs): + label = self.label_range[label_idx] + if label not in already_seen: + already_seen[label] = set() + + left_border = 0 if label == 0 else self.groups_right_border[label - 1] + right_border = self.groups_right_border[label] + + if self.with_replacement: + selection = np.arange(left_border, right_border) + else: + selection = [i for i in np.arange(left_border, right_border) if i not in already_seen[label]] + + if len(selection) >= self.samples_per_label: + for element_idx in np.random.choice(selection, self.samples_per_label, replace=False): + count += 1 + already_seen[label].add(element_idx) + yield self.grouped_inputs[element_idx] + + label_idx += 1 + if label_idx >= len(self.label_range): + label_idx = 0 + already_seen = {} + np.random.shuffle(self.label_range) + + def __len__(self): + return len(self.grouped_inputs) diff --git a/sentence-transformers/sentence_transformers/datasets/SentencesDataset.py b/sentence-transformers/sentence_transformers/datasets/SentencesDataset.py new file mode 100644 index 0000000000000000000000000000000000000000..8e9dee5b60a380e7924e0cf03efcfc9f1e69f60e --- /dev/null +++ b/sentence-transformers/sentence_transformers/datasets/SentencesDataset.py @@ -0,0 +1,30 @@ +""" +This file contains deprecated code that can only be used with the old `model.fit`-style Sentence Transformers v2.X training. +It exists for backwards compatibility with the `model.old_fit` method, but will be removed in a future version. + +Nowadays, with Sentence Transformers v3+, it is recommended to use the `SentenceTransformerTrainer` class to train models. +See https://www.sbert.net/docs/sentence_transformer/training_overview.html for more information. +""" + +from __future__ import annotations + +from torch.utils.data import Dataset + +from sentence_transformers import SentenceTransformer +from sentence_transformers.readers.InputExample import InputExample + + +class SentencesDataset(Dataset): + """ + DEPRECATED: This class is no longer used. Instead of wrapping your List of InputExamples in a SentencesDataset + and then passing it to the DataLoader, you can pass the list of InputExamples directly to the dataset loader. + """ + + def __init__(self, examples: list[InputExample], model: SentenceTransformer): + self.examples = examples + + def __getitem__(self, item): + return self.examples[item] + + def __len__(self): + return len(self.examples) diff --git a/sentence-transformers/sentence_transformers/datasets/__init__.py b/sentence-transformers/sentence_transformers/datasets/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b74a19dae538b5fb54cba3ea160e4a0f2f59089f --- /dev/null +++ b/sentence-transformers/sentence_transformers/datasets/__init__.py @@ -0,0 +1,23 @@ +""" +This directory contains deprecated code that can only be used with the old `model.fit`-style Sentence Transformers v2.X training. +It exists for backwards compatibility with the `model.old_fit` method, but will be removed in a future version. + +Nowadays, with Sentence Transformers v3+, it is recommended to use the `SentenceTransformerTrainer` class to train models. +See https://www.sbert.net/docs/sentence_transformer/training_overview.html for more information. +""" + +from __future__ import annotations + +from .DenoisingAutoEncoderDataset import DenoisingAutoEncoderDataset +from .NoDuplicatesDataLoader import NoDuplicatesDataLoader +from .ParallelSentencesDataset import ParallelSentencesDataset +from .SentenceLabelDataset import SentenceLabelDataset +from .SentencesDataset import SentencesDataset + +__all__ = [ + "DenoisingAutoEncoderDataset", + "NoDuplicatesDataLoader", + "ParallelSentencesDataset", + "SentencesDataset", + "SentenceLabelDataset", +] diff --git a/sentence-transformers/sentence_transformers/datasets/__pycache__/DenoisingAutoEncoderDataset.cpython-312.pyc b/sentence-transformers/sentence_transformers/datasets/__pycache__/DenoisingAutoEncoderDataset.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b8232fe290bcca52f405b86ae1fda620897eca7f Binary files /dev/null and b/sentence-transformers/sentence_transformers/datasets/__pycache__/DenoisingAutoEncoderDataset.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/datasets/__pycache__/NoDuplicatesDataLoader.cpython-312.pyc b/sentence-transformers/sentence_transformers/datasets/__pycache__/NoDuplicatesDataLoader.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7fb4e212f35a0ecc6324a88b627b5deebf792a2c Binary files /dev/null and b/sentence-transformers/sentence_transformers/datasets/__pycache__/NoDuplicatesDataLoader.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/datasets/__pycache__/ParallelSentencesDataset.cpython-312.pyc b/sentence-transformers/sentence_transformers/datasets/__pycache__/ParallelSentencesDataset.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9c48f501e42946a3ee2968de58251be094762cd9 Binary files /dev/null and b/sentence-transformers/sentence_transformers/datasets/__pycache__/ParallelSentencesDataset.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/datasets/__pycache__/SentenceLabelDataset.cpython-312.pyc b/sentence-transformers/sentence_transformers/datasets/__pycache__/SentenceLabelDataset.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4a9696004145b1809305b421eea7a7b0f87745be Binary files /dev/null and b/sentence-transformers/sentence_transformers/datasets/__pycache__/SentenceLabelDataset.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/datasets/__pycache__/SentencesDataset.cpython-312.pyc b/sentence-transformers/sentence_transformers/datasets/__pycache__/SentencesDataset.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de9b4731c5d5993f1ba6255abc9684c6c7d2427f Binary files /dev/null and b/sentence-transformers/sentence_transformers/datasets/__pycache__/SentencesDataset.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/datasets/__pycache__/__init__.cpython-312.pyc b/sentence-transformers/sentence_transformers/datasets/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f781b257dc8e166c647548674f9ad14243f707bb Binary files /dev/null and b/sentence-transformers/sentence_transformers/datasets/__pycache__/__init__.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/evaluation/BinaryClassificationEvaluator.py b/sentence-transformers/sentence_transformers/evaluation/BinaryClassificationEvaluator.py new file mode 100644 index 0000000000000000000000000000000000000000..6feb31c9fe21b5ab4e070cbe6c6ec3c6489c4959 --- /dev/null +++ b/sentence-transformers/sentence_transformers/evaluation/BinaryClassificationEvaluator.py @@ -0,0 +1,378 @@ +from __future__ import annotations + +import csv +import logging +import os +from typing import TYPE_CHECKING, Literal + +import numpy as np +from sklearn.metrics import average_precision_score, matthews_corrcoef + +from sentence_transformers.evaluation.SentenceEvaluator import SentenceEvaluator +from sentence_transformers.readers import InputExample +from sentence_transformers.similarity_functions import SimilarityFunction +from sentence_transformers.util import ( + pairwise_cos_sim, + pairwise_dot_score, + pairwise_euclidean_sim, + pairwise_manhattan_sim, +) + +if TYPE_CHECKING: + from sentence_transformers.SentenceTransformer import SentenceTransformer + +logger = logging.getLogger(__name__) + + +class BinaryClassificationEvaluator(SentenceEvaluator): + """ + Evaluate a model based on the similarity of the embeddings by calculating the accuracy of identifying similar and + dissimilar sentences. + The metrics are the cosine similarity, dot score, Euclidean and Manhattan distance + The returned score is the accuracy with a specified metric. + + The results are written in a CSV. If a CSV already exists, then values are appended. + + The labels need to be 0 for dissimilar pairs and 1 for similar pairs. + + Args: + sentences1 (List[str]): The first column of sentences. + sentences2 (List[str]): The second column of sentences. + labels (List[int]): labels[i] is the label for the pair (sentences1[i], sentences2[i]). Must be 0 or 1. + name (str, optional): Name for the output. Defaults to "". + batch_size (int, optional): Batch size used to compute embeddings. Defaults to 32. + show_progress_bar (bool, optional): If true, prints a progress bar. Defaults to False. + write_csv (bool, optional): Write results to a CSV file. Defaults to True. + truncate_dim (Optional[int], optional): The dimension to truncate sentence embeddings to. `None` uses the model's current truncation dimension. Defaults to None. + similarity_fn_names (Optional[List[Literal["cosine", "dot", "euclidean", "manhattan"]]], optional): The similarity functions to use. If not specified, defaults to the ``similarity_fn_name`` attribute of the model. Defaults to None. + + Example: + :: + + from sentence_transformers import SentenceTransformer + from sentence_transformers.evaluation import BinaryClassificationEvaluator + from datasets import load_dataset + + # Load a model + model = SentenceTransformer('all-mpnet-base-v2') + + # Load a dataset with two text columns and a class label column (https://huggingface.co/datasets/sentence-transformers/quora-duplicates) + eval_dataset = load_dataset("sentence-transformers/quora-duplicates", "pair-class", split="train[-1000:]") + + # Initialize the evaluator + binary_acc_evaluator = BinaryClassificationEvaluator( + sentences1=eval_dataset["sentence1"], + sentences2=eval_dataset["sentence2"], + labels=eval_dataset["label"], + name="quora_duplicates_dev", + ) + results = binary_acc_evaluator(model) + ''' + Binary Accuracy Evaluation of the model on the quora_duplicates_dev dataset: + Accuracy with Cosine-Similarity: 81.60 (Threshold: 0.8352) + F1 with Cosine-Similarity: 75.27 (Threshold: 0.7715) + Precision with Cosine-Similarity: 65.81 + Recall with Cosine-Similarity: 87.89 + Average Precision with Cosine-Similarity: 76.03 + Matthews Correlation with Cosine-Similarity: 62.48 + ''' + print(binary_acc_evaluator.primary_metric) + # => "quora_duplicates_dev_cosine_ap" + print(results[binary_acc_evaluator.primary_metric]) + # => 0.760277070888393 + """ + + def __init__( + self, + sentences1: list[str], + sentences2: list[str], + labels: list[int], + name: str = "", + batch_size: int = 32, + show_progress_bar: bool = False, + write_csv: bool = True, + truncate_dim: int | None = None, + similarity_fn_names: list[Literal["cosine", "dot", "euclidean", "manhattan"]] | None = None, + ): + super().__init__() + self.sentences1 = sentences1 + self.sentences2 = sentences2 + self.labels = labels + self.truncate_dim = truncate_dim + self.similarity_fn_names = similarity_fn_names or [] + + assert len(self.sentences1) == len(self.sentences2) + assert len(self.sentences1) == len(self.labels) + for label in labels: + assert label == 0 or label == 1 + + self.write_csv = write_csv + self.name = name + self.batch_size = batch_size + if show_progress_bar is None: + show_progress_bar = ( + logger.getEffectiveLevel() == logging.INFO or logger.getEffectiveLevel() == logging.DEBUG + ) + self.show_progress_bar = show_progress_bar + + self.csv_file = "binary_classification_evaluation" + ("_" + name if name else "") + "_results.csv" + self.csv_headers = ["epoch", "steps"] + + self._append_csv_headers(self.similarity_fn_names) + + def _append_csv_headers(self, similarity_fn_names: list[str]) -> None: + metrics = [ + "accuracy", + "accuracy_threshold", + "f1", + "precision", + "recall", + "f1_threshold", + "ap", + "mcc", + ] + + for v in similarity_fn_names: + for m in metrics: + self.csv_headers.append(f"{v}_{m}") + + @classmethod + def from_input_examples(cls, examples: list[InputExample], **kwargs): + sentences1 = [] + sentences2 = [] + scores = [] + + for example in examples: + sentences1.append(example.texts[0]) + sentences2.append(example.texts[1]) + scores.append(example.label) + return cls(sentences1, sentences2, scores, **kwargs) + + def __call__( + self, model: SentenceTransformer, output_path: str | None = None, epoch: int = -1, steps: int = -1 + ) -> dict[str, float]: + """ + Compute the evaluation metrics for the given model. + + Args: + model (SentenceTransformer): The model to evaluate. + output_path (str, optional): Path to save the evaluation results CSV file. Defaults to None. + epoch (int, optional): The epoch number. Defaults to -1. + steps (int, optional): The number of steps. Defaults to -1. + + Returns: + Dict[str, float]: A dictionary containing the evaluation metrics. + """ + if epoch != -1: + if steps == -1: + out_txt = f" after epoch {epoch}" + else: + out_txt = f" in epoch {epoch} after {steps} steps" + else: + out_txt = "" + if self.truncate_dim is not None: + out_txt += f" (truncated to {self.truncate_dim})" + + logger.info(f"Binary Accuracy Evaluation of the model on the {self.name} dataset{out_txt}:") + + if not self.similarity_fn_names: + self.similarity_fn_names = [model.similarity_fn_name] + self._append_csv_headers(self.similarity_fn_names) + scores = self.compute_metrices(model) + + file_output_data = [epoch, steps] + + for header_name in self.csv_headers: + if header_name.count("_") == 1: + sim_fct, metric = header_name.split("_", maxsplit=1) + if sim_fct in scores: + file_output_data.append(scores[sim_fct][metric]) + + if output_path is not None and self.write_csv: + csv_path = os.path.join(output_path, self.csv_file) + if not os.path.isfile(csv_path): + with open(csv_path, newline="", mode="w", encoding="utf-8") as f: + writer = csv.writer(f) + writer.writerow(self.csv_headers) + writer.writerow(file_output_data) + else: + with open(csv_path, newline="", mode="a", encoding="utf-8") as f: + writer = csv.writer(f) + writer.writerow(file_output_data) + + metrics = { + f"{short_name}_{metric}": value + for short_name, values in scores.items() + for metric, value in values.items() + } + if len(self.similarity_fn_names) > 1: + metrics.update( + { + f"max_{metric}": max(scores[short_name][metric] for short_name in scores) + for metric in scores["cosine"] + } + ) + self.primary_metric = "max_ap" + else: + self.primary_metric = f"{self.similarity_fn_names[0]}_ap" + + metrics = self.prefix_name_to_metrics(metrics, self.name) + self.store_metrics_in_model_card_data(model, metrics, epoch, steps) + return metrics + + def compute_metrices(self, model: SentenceTransformer) -> dict[str, dict[str, float]]: + try: + # If the sentences are hashable, then we can use a set to avoid embedding the same sentences multiple + # times + sentences = list(set(self.sentences1 + self.sentences2)) + except TypeError: + # Otherwise we just embed everything, e.g. if the sentences are images for evaluating a CLIP model + embeddings1 = self.embed_inputs(model, self.sentences1) + embeddings2 = self.embed_inputs(model, self.sentences2) + else: + embeddings = self.embed_inputs(model, sentences) + emb_dict = {sent: emb for sent, emb in zip(sentences, embeddings)} + embeddings1 = [emb_dict[sent] for sent in self.sentences1] + embeddings2 = [emb_dict[sent] for sent in self.sentences2] + + similarity_fns = { + SimilarityFunction.COSINE.value: { + "score_fn": lambda x, y: pairwise_cos_sim(x, y), + "name": "Cosine-Similarity", + "greater_is_better": True, + }, + SimilarityFunction.DOT_PRODUCT.value: { + "score_fn": lambda x, y: pairwise_dot_score(x, y), + "name": "Dot-Product", + "greater_is_better": True, + }, + SimilarityFunction.MANHATTAN.value: { + "score_fn": lambda x, y: pairwise_manhattan_sim(x, y), + "name": "Manhattan-Distance", + "greater_is_better": False, + }, + SimilarityFunction.EUCLIDEAN.value: { + "score_fn": lambda x, y: pairwise_euclidean_sim(x, y), + "name": "Euclidean-Distance", + "greater_is_better": False, + }, + } + + labels = np.asarray(self.labels) + output_scores = {} + for similarity_fn_name in self.similarity_fn_names: + similarity_fn = similarity_fns[similarity_fn_name] + scores = similarity_fn["score_fn"](embeddings1, embeddings2).detach().cpu().numpy() + greater_is_better = similarity_fn["greater_is_better"] + name = similarity_fn["name"] + + acc, acc_threshold = self.find_best_acc_and_threshold(scores, labels, greater_is_better) + f1, precision, recall, f1_threshold = self.find_best_f1_and_threshold(scores, labels, greater_is_better) + ap = average_precision_score(labels, scores * (1 if greater_is_better else -1)) + + predicted_labels = (scores >= f1_threshold) if greater_is_better else (scores <= f1_threshold) + mcc = matthews_corrcoef(labels, predicted_labels) + + logger.info(f"Accuracy with {name}: {acc * 100:.2f}\t(Threshold: {acc_threshold:.4f})") + logger.info(f"F1 with {name}: {f1 * 100:.2f}\t(Threshold: {f1_threshold:.4f})") + logger.info(f"Precision with {name}: {precision * 100:.2f}") + logger.info(f"Recall with {name}: {recall * 100:.2f}") + logger.info(f"Average Precision with {name}: {ap * 100:.2f}") + logger.info(f"Matthews Correlation with {name}: {mcc * 100:.2f}\n") + + output_scores[similarity_fn_name] = { + "accuracy": acc, + "accuracy_threshold": acc_threshold, + "f1": f1, + "f1_threshold": f1_threshold, + "precision": precision, + "recall": recall, + "ap": ap, + "mcc": mcc, + } + + return output_scores + + def embed_inputs( + self, + model: SentenceTransformer, + sentences: str | list[str] | np.ndarray, + **kwargs, + ) -> np.ndarray: + return model.encode( + sentences, + batch_size=self.batch_size, + show_progress_bar=self.show_progress_bar, + convert_to_numpy=True, + truncate_dim=self.truncate_dim, + **kwargs, + ) + + @staticmethod + def find_best_acc_and_threshold(scores, labels, high_score_more_similar: bool): + assert len(scores) == len(labels) + rows = list(zip(scores, labels)) + + rows = sorted(rows, key=lambda x: x[0], reverse=high_score_more_similar) + + max_acc = 0 + best_threshold = -1 + + positive_so_far = 0 + remaining_negatives = sum(labels == 0) + + for i in range(len(rows) - 1): + score, label = rows[i] + if label == 1: + positive_so_far += 1 + else: + remaining_negatives -= 1 + + acc = (positive_so_far + remaining_negatives) / len(labels) + if acc > max_acc: + max_acc = acc + best_threshold = (rows[i][0] + rows[i + 1][0]) / 2 + + return max_acc, best_threshold + + @staticmethod + def find_best_f1_and_threshold(scores, labels, high_score_more_similar: bool): + assert len(scores) == len(labels) + + scores = np.asarray(scores) + labels = np.asarray(labels) + + rows = list(zip(scores, labels)) + + rows = sorted(rows, key=lambda x: x[0], reverse=high_score_more_similar) + + best_f1 = best_precision = best_recall = 0 + threshold = 0 + nextract = 0 + ncorrect = 0 + total_num_duplicates = sum(labels) + + for i in range(len(rows) - 1): + score, label = rows[i] + nextract += 1 + + if label == 1: + ncorrect += 1 + + if ncorrect > 0: + precision = ncorrect / nextract + recall = ncorrect / total_num_duplicates + f1 = 2 * precision * recall / (precision + recall) + if f1 > best_f1: + best_f1 = f1 + best_precision = precision + best_recall = recall + threshold = (rows[i][0] + rows[i + 1][0]) / 2 + + return best_f1, best_precision, best_recall, threshold + + def get_config_dict(self): + config_dict = {} + if self.truncate_dim is not None: + config_dict["truncate_dim"] = self.truncate_dim + return config_dict diff --git a/sentence-transformers/sentence_transformers/evaluation/EmbeddingSimilarityEvaluator.py b/sentence-transformers/sentence_transformers/evaluation/EmbeddingSimilarityEvaluator.py new file mode 100644 index 0000000000000000000000000000000000000000..231e161d0fe4de1e80497b618528c87434f5d5aa --- /dev/null +++ b/sentence-transformers/sentence_transformers/evaluation/EmbeddingSimilarityEvaluator.py @@ -0,0 +1,271 @@ +from __future__ import annotations + +import csv +import logging +import os +from typing import TYPE_CHECKING, Literal + +import numpy as np +from scipy.stats import pearsonr, spearmanr + +from sentence_transformers.evaluation.SentenceEvaluator import SentenceEvaluator +from sentence_transformers.readers import InputExample +from sentence_transformers.similarity_functions import SimilarityFunction +from sentence_transformers.util import ( + pairwise_cos_sim, + pairwise_dot_score, + pairwise_euclidean_sim, + pairwise_manhattan_sim, +) + +if TYPE_CHECKING: + from sentence_transformers.SentenceTransformer import SentenceTransformer + +logger = logging.getLogger(__name__) + + +class EmbeddingSimilarityEvaluator(SentenceEvaluator): + """ + Evaluate a model based on the similarity of the embeddings by calculating the Spearman and Pearson rank correlation + in comparison to the gold standard labels. + The metrics are the cosine similarity as well as euclidean and Manhattan distance + The returned score is the Spearman correlation with a specified metric. + + Args: + sentences1 (List[str]): List with the first sentence in a pair. + sentences2 (List[str]): List with the second sentence in a pair. + scores (List[float]): Similarity score between sentences1[i] and sentences2[i]. + batch_size (int, optional): The batch size for processing the sentences. Defaults to 16. + main_similarity (Optional[Union[str, SimilarityFunction]], optional): The main similarity function to use. + Can be a string (e.g. "cosine", "dot") or a SimilarityFunction object. Defaults to None. + similarity_fn_names (List[str], optional): List of similarity function names to use. If None, the + ``similarity_fn_name`` attribute of the model is used. Defaults to None. + name (str, optional): The name of the evaluator. Defaults to "". + show_progress_bar (bool, optional): Whether to show a progress bar during evaluation. Defaults to False. + write_csv (bool, optional): Whether to write the evaluation results to a CSV file. Defaults to True. + precision (Optional[Literal["float32", "int8", "uint8", "binary", "ubinary"]], optional): The precision + to use for the embeddings. Can be "float32", "int8", "uint8", "binary", or "ubinary". Defaults to None. + truncate_dim (Optional[int], optional): The dimension to truncate sentence embeddings to. `None` uses the + model's current truncation dimension. Defaults to None. + + Example: + :: + + from datasets import load_dataset + from sentence_transformers import SentenceTransformer + from sentence_transformers.evaluation import EmbeddingSimilarityEvaluator, SimilarityFunction + + # Load a model + model = SentenceTransformer('all-mpnet-base-v2') + + # Load the STSB dataset (https://huggingface.co/datasets/sentence-transformers/stsb) + eval_dataset = load_dataset("sentence-transformers/stsb", split="validation") + + # Initialize the evaluator + dev_evaluator = EmbeddingSimilarityEvaluator( + sentences1=eval_dataset["sentence1"], + sentences2=eval_dataset["sentence2"], + scores=eval_dataset["score"], + name="sts_dev", + ) + results = dev_evaluator(model) + ''' + EmbeddingSimilarityEvaluator: Evaluating the model on the sts-dev dataset: + Cosine-Similarity: Pearson: 0.8806 Spearman: 0.8810 + ''' + print(dev_evaluator.primary_metric) + # => "sts_dev_pearson_cosine" + print(results[dev_evaluator.primary_metric]) + # => 0.881019449484294 + """ + + def __init__( + self, + sentences1: list[str], + sentences2: list[str], + scores: list[float], + batch_size: int = 16, + main_similarity: str | SimilarityFunction | None = None, + similarity_fn_names: list[Literal["cosine", "euclidean", "manhattan", "dot"]] | None = None, + name: str = "", + show_progress_bar: bool = False, + write_csv: bool = True, + precision: Literal["float32", "int8", "uint8", "binary", "ubinary"] | None = None, + truncate_dim: int | None = None, + ): + super().__init__() + self.sentences1 = sentences1 + self.sentences2 = sentences2 + self.scores = scores + self.write_csv = write_csv + self.precision = precision + self.truncate_dim = truncate_dim + + assert len(self.sentences1) == len(self.sentences2) + assert len(self.sentences1) == len(self.scores) + + self.main_similarity = SimilarityFunction(main_similarity) if main_similarity else None + self.similarity_fn_names = similarity_fn_names or [] + if self.similarity_fn_names == [] and self.main_similarity is not None: + self.similarity_fn_names = [self.main_similarity.value] + + self.name = name + + self.batch_size = batch_size + if show_progress_bar is None: + show_progress_bar = ( + logger.getEffectiveLevel() == logging.INFO or logger.getEffectiveLevel() == logging.DEBUG + ) + self.show_progress_bar = show_progress_bar + + self.csv_file = ( + "similarity_evaluation" + + ("_" + name if name else "") + + ("_" + precision if precision else "") + + "_results.csv" + ) + self.csv_headers = [ + "epoch", + "steps", + ] + + self._append_csv_headers(self.similarity_fn_names) + + def _append_csv_headers(self, similarity_fn_names: list[str]) -> None: + metrics = ["pearson", "spearman"] + + for v in similarity_fn_names: + for m in metrics: + self.csv_headers.append(f"{v}_{m}") + + @classmethod + def from_input_examples(cls, examples: list[InputExample], **kwargs): + sentences1 = [] + sentences2 = [] + scores = [] + + for example in examples: + sentences1.append(example.texts[0]) + sentences2.append(example.texts[1]) + scores.append(example.label) + return cls(sentences1, sentences2, scores, **kwargs) + + def __call__( + self, model: SentenceTransformer, output_path: str | None = None, epoch: int = -1, steps: int = -1 + ) -> dict[str, float]: + if epoch != -1: + if steps == -1: + out_txt = f" after epoch {epoch}" + else: + out_txt = f" in epoch {epoch} after {steps} steps" + else: + out_txt = "" + if self.truncate_dim is not None: + out_txt += f" (truncated to {self.truncate_dim})" + + logger.info(f"EmbeddingSimilarityEvaluator: Evaluating the model on the {self.name} dataset{out_txt}:") + + embeddings1 = self.embed_inputs(model, self.sentences1) + embeddings2 = self.embed_inputs(model, self.sentences2) + # Binary and ubinary embeddings are packed, so we need to unpack them for the distance metrics + if self.precision == "binary": + embeddings1 = (embeddings1 + 128).astype(np.uint8) + embeddings2 = (embeddings2 + 128).astype(np.uint8) + if self.precision in ("ubinary", "binary"): + embeddings1 = np.unpackbits(embeddings1, axis=1) + embeddings2 = np.unpackbits(embeddings2, axis=1) + + labels = self.scores + + if not self.similarity_fn_names: + self.similarity_fn_names = [model.similarity_fn_name] + self._append_csv_headers(self.similarity_fn_names) + + similarity_functions = { + "cosine": lambda x, y: pairwise_cos_sim(x, y), + "manhattan": lambda x, y: pairwise_manhattan_sim(x, y), + "euclidean": lambda x, y: pairwise_euclidean_sim(x, y), + "dot": lambda x, y: pairwise_dot_score(x, y), + } + + metrics = {} + for fn_name in self.similarity_fn_names: + if fn_name in similarity_functions: + scores = similarity_functions[fn_name](embeddings1, embeddings2).detach().cpu().numpy() + eval_pearson, _ = pearsonr(labels, scores) + eval_spearman, _ = spearmanr(labels, scores) + metrics[f"pearson_{fn_name}"] = eval_pearson + metrics[f"spearman_{fn_name}"] = eval_spearman + logger.info( + f"{fn_name.capitalize()}-Similarity:\tPearson: {eval_pearson:.4f}\tSpearman: {eval_spearman:.4f}" + ) + + if output_path is not None and self.write_csv: + csv_path = os.path.join(output_path, self.csv_file) + output_file_exists = os.path.isfile(csv_path) + with open(csv_path, newline="", mode="a" if output_file_exists else "w", encoding="utf-8") as f: + writer = csv.writer(f) + if not output_file_exists: + writer.writerow(self.csv_headers) + + writer.writerow( + [ + epoch, + steps, + ] + + [ + metrics[f"{metric}_{fn_name}"] + for fn_name in self.similarity_fn_names + for metric in ["pearson", "spearman"] + ] + ) + + if len(self.similarity_fn_names) > 1: + metrics["pearson_max"] = max(metrics[f"pearson_{fn_name}"] for fn_name in self.similarity_fn_names) + metrics["spearman_max"] = max(metrics[f"spearman_{fn_name}"] for fn_name in self.similarity_fn_names) + + if self.main_similarity: + self.primary_metric = { + SimilarityFunction.COSINE: "spearman_cosine", + SimilarityFunction.EUCLIDEAN: "spearman_euclidean", + SimilarityFunction.MANHATTAN: "spearman_manhattan", + SimilarityFunction.DOT_PRODUCT: "spearman_dot", + }.get(self.main_similarity) + else: + if len(self.similarity_fn_names) > 1: + self.primary_metric = "spearman_max" + else: + self.primary_metric = f"spearman_{self.similarity_fn_names[0]}" + + metrics = self.prefix_name_to_metrics(metrics, self.name) + self.store_metrics_in_model_card_data(model, metrics, epoch, steps) + return metrics + + def embed_inputs( + self, + model: SentenceTransformer, + sentences: str | list[str] | np.ndarray, + **kwargs, + ) -> np.ndarray: + return model.encode( + sentences, + batch_size=self.batch_size, + show_progress_bar=self.show_progress_bar, + convert_to_numpy=True, + precision=self.precision, + normalize_embeddings=bool(self.precision), + truncate_dim=self.truncate_dim, + **kwargs, + ) + + @property + def description(self) -> str: + return "Semantic Similarity" + + def get_config_dict(self): + config_dict = {} + config_dict_candidate_keys = ["truncate_dim", "precision"] + for key in config_dict_candidate_keys: + if getattr(self, key) is not None: + config_dict[key] = getattr(self, key) + return config_dict diff --git a/sentence-transformers/sentence_transformers/evaluation/InformationRetrievalEvaluator.py b/sentence-transformers/sentence_transformers/evaluation/InformationRetrievalEvaluator.py new file mode 100644 index 0000000000000000000000000000000000000000..6ae673b86bbd15cf20cd4d0d283bc0e9da6a626e --- /dev/null +++ b/sentence-transformers/sentence_transformers/evaluation/InformationRetrievalEvaluator.py @@ -0,0 +1,567 @@ +from __future__ import annotations + +import heapq +import json +import logging +import os +from typing import TYPE_CHECKING, Callable + +import numpy as np +import torch +from torch import Tensor +from tqdm import trange + +from sentence_transformers.evaluation.SentenceEvaluator import SentenceEvaluator +from sentence_transformers.similarity_functions import SimilarityFunction + +if TYPE_CHECKING: + from sentence_transformers.SentenceTransformer import SentenceTransformer + +logger = logging.getLogger(__name__) + + +class InformationRetrievalEvaluator(SentenceEvaluator): + """ + This class evaluates an Information Retrieval (IR) setting. + + Given a set of queries and a large corpus set. It will retrieve for each query the top-k most similar document. It measures + Mean Reciprocal Rank (MRR), Recall@k, and Normalized Discounted Cumulative Gain (NDCG) + + Args: + queries (Dict[str, str]): A dictionary mapping query IDs to queries. + corpus (Dict[str, str]): A dictionary mapping document IDs to documents. + relevant_docs (Dict[str, Set[str]]): A dictionary mapping query IDs to a set of relevant document IDs. + corpus_chunk_size (int): The size of each chunk of the corpus. Defaults to 50000. + mrr_at_k (List[int]): A list of integers representing the values of k for MRR calculation. Defaults to [10]. + ndcg_at_k (List[int]): A list of integers representing the values of k for NDCG calculation. Defaults to [10]. + accuracy_at_k (List[int]): A list of integers representing the values of k for accuracy calculation. Defaults to [1, 3, 5, 10]. + precision_recall_at_k (List[int]): A list of integers representing the values of k for precision and recall calculation. Defaults to [1, 3, 5, 10]. + map_at_k (List[int]): A list of integers representing the values of k for MAP calculation. Defaults to [100]. + show_progress_bar (bool): Whether to show a progress bar during evaluation. Defaults to False. + batch_size (int): The batch size for evaluation. Defaults to 32. + name (str): A name for the evaluation. Defaults to "". + write_csv (bool): Whether to write the evaluation results to a CSV file. Defaults to True. + truncate_dim (int, optional): The dimension to truncate the embeddings to. Defaults to None. + score_functions (Dict[str, Callable[[Tensor, Tensor], Tensor]]): A dictionary mapping score function names to score functions. Defaults to the ``similarity`` function from the ``model``. + main_score_function (Union[str, SimilarityFunction], optional): The main score function to use for evaluation. Defaults to None. + query_prompt (str, optional): The prompt to be used when encoding the corpus. Defaults to None. + query_prompt_name (str, optional): The name of the prompt to be used when encoding the corpus. Defaults to None. + corpus_prompt (str, optional): The prompt to be used when encoding the corpus. Defaults to None. + corpus_prompt_name (str, optional): The name of the prompt to be used when encoding the corpus. Defaults to None. + write_predictions (bool): Whether to write the predictions to a JSONL file. Defaults to False. + This can be useful for downstream evaluation as it can be used as input to the :class:`~sentence_transformers.sparse_encoder.evaluation.ReciprocalRankFusionEvaluator` that accept precomputed predictions. + + Example: + :: + + import random + from sentence_transformers import SentenceTransformer + from sentence_transformers.evaluation import InformationRetrievalEvaluator + from datasets import load_dataset + + # Load a model + model = SentenceTransformer('all-MiniLM-L6-v2') + + # Load the Touche-2020 IR dataset (https://huggingface.co/datasets/BeIR/webis-touche2020, https://huggingface.co/datasets/BeIR/webis-touche2020-qrels) + corpus = load_dataset("BeIR/webis-touche2020", "corpus", split="corpus") + queries = load_dataset("BeIR/webis-touche2020", "queries", split="queries") + relevant_docs_data = load_dataset("BeIR/webis-touche2020-qrels", split="test") + + # For this dataset, we want to concatenate the title and texts for the corpus + corpus = corpus.map(lambda x: {'text': x['title'] + " " + x['text']}, remove_columns=['title']) + + # Shrink the corpus size heavily to only the relevant documents + 30,000 random documents + required_corpus_ids = set(map(str, relevant_docs_data["corpus-id"])) + required_corpus_ids |= set(random.sample(corpus["_id"], k=30_000)) + corpus = corpus.filter(lambda x: x["_id"] in required_corpus_ids) + + # Convert the datasets to dictionaries + corpus = dict(zip(corpus["_id"], corpus["text"])) # Our corpus (cid => document) + queries = dict(zip(queries["_id"], queries["text"])) # Our queries (qid => question) + relevant_docs = {} # Query ID to relevant documents (qid => set([relevant_cids]) + for qid, corpus_ids in zip(relevant_docs_data["query-id"], relevant_docs_data["corpus-id"]): + qid = str(qid) + corpus_ids = str(corpus_ids) + if qid not in relevant_docs: + relevant_docs[qid] = set() + relevant_docs[qid].add(corpus_ids) + + # Given queries, a corpus and a mapping with relevant documents, the InformationRetrievalEvaluator computes different IR metrics. + ir_evaluator = InformationRetrievalEvaluator( + queries=queries, + corpus=corpus, + relevant_docs=relevant_docs, + name="BeIR-touche2020-subset-test", + ) + results = ir_evaluator(model) + ''' + Information Retrieval Evaluation of the model on the BeIR-touche2020-test dataset: + Queries: 49 + Corpus: 31923 + + Score-Function: cosine + Accuracy@1: 77.55% + Accuracy@3: 93.88% + Accuracy@5: 97.96% + Accuracy@10: 100.00% + Precision@1: 77.55% + Precision@3: 72.11% + Precision@5: 71.43% + Precision@10: 62.65% + Recall@1: 1.72% + Recall@3: 4.78% + Recall@5: 7.90% + Recall@10: 13.86% + MRR@10: 0.8580 + NDCG@10: 0.6606 + MAP@100: 0.2934 + ''' + print(ir_evaluator.primary_metric) + # => "BeIR-touche2020-test_cosine_map@100" + print(results[ir_evaluator.primary_metric]) + # => 0.29335196224364596 + """ + + def __init__( + self, + queries: dict[str, str], # qid => query + corpus: dict[str, str], # cid => doc + relevant_docs: dict[str, set[str]], # qid => Set[cid] + corpus_chunk_size: int = 50000, + mrr_at_k: list[int] = [10], + ndcg_at_k: list[int] = [10], + accuracy_at_k: list[int] = [1, 3, 5, 10], + precision_recall_at_k: list[int] = [1, 3, 5, 10], + map_at_k: list[int] = [100], + show_progress_bar: bool = False, + batch_size: int = 32, + name: str = "", + write_csv: bool = True, + truncate_dim: int | None = None, + score_functions: dict[str, Callable[[Tensor, Tensor], Tensor]] | None = None, + main_score_function: str | SimilarityFunction | None = None, + query_prompt: str | None = None, + query_prompt_name: str | None = None, + corpus_prompt: str | None = None, + corpus_prompt_name: str | None = None, + write_predictions: bool = False, + ) -> None: + super().__init__() + self.queries_ids = [] + for qid in queries: + if qid in relevant_docs and len(relevant_docs[qid]) > 0: + self.queries_ids.append(qid) + + self.queries = [queries[qid] for qid in self.queries_ids] + + self.corpus_ids = list(corpus.keys()) + self.corpus = [corpus[cid] for cid in self.corpus_ids] + + self.query_prompt = query_prompt + self.query_prompt_name = query_prompt_name + self.corpus_prompt = corpus_prompt + self.corpus_prompt_name = corpus_prompt_name + + self.relevant_docs = relevant_docs + self.corpus_chunk_size = corpus_chunk_size + self.mrr_at_k = mrr_at_k + self.ndcg_at_k = ndcg_at_k + self.accuracy_at_k = accuracy_at_k + self.precision_recall_at_k = precision_recall_at_k + self.map_at_k = map_at_k + + self.show_progress_bar = show_progress_bar + self.batch_size = batch_size + self.name = name + self.write_csv = write_csv + self.score_functions = score_functions + self.score_function_names = sorted(list(self.score_functions.keys())) if score_functions else [] + self.main_score_function = SimilarityFunction(main_score_function) if main_score_function else None + self.truncate_dim = truncate_dim + + if name: + name = "_" + name + + self.csv_file: str = "Information-Retrieval_evaluation" + name + "_results.csv" + self.csv_headers = ["epoch", "steps"] + + self._append_csv_headers(self.score_function_names) + self.write_predictions = write_predictions + if self.write_predictions: + self.predictions_file = "Information-Retrieval_evaluation" + name + "_predictions.jsonl" + + def _append_csv_headers(self, score_function_names): + for score_name in score_function_names: + for k in self.accuracy_at_k: + self.csv_headers.append(f"{score_name}-Accuracy@{k}") + + for k in self.precision_recall_at_k: + self.csv_headers.append(f"{score_name}-Precision@{k}") + self.csv_headers.append(f"{score_name}-Recall@{k}") + + for k in self.mrr_at_k: + self.csv_headers.append(f"{score_name}-MRR@{k}") + + for k in self.ndcg_at_k: + self.csv_headers.append(f"{score_name}-NDCG@{k}") + + for k in self.map_at_k: + self.csv_headers.append(f"{score_name}-MAP@{k}") + + def __call__( + self, + model: SentenceTransformer, + output_path: str | None = None, + epoch: int = -1, + steps: int = -1, + *args, + **kwargs, + ) -> dict[str, float]: + if epoch != -1: + if steps == -1: + out_txt = f" after epoch {epoch}" + else: + out_txt = f" in epoch {epoch} after {steps} steps" + else: + out_txt = "" + if self.truncate_dim is not None: + out_txt += f" (truncated to {self.truncate_dim})" + + logger.info(f"Information Retrieval Evaluation of the model on the {self.name} dataset{out_txt}:") + + if self.score_functions is None: + self.score_functions = {model.similarity_fn_name: model.similarity} + self.score_function_names = [model.similarity_fn_name] + self._append_csv_headers(self.score_function_names) + + scores = self.compute_metrices(model, output_path=output_path, *args, **kwargs) + + # Write results to disc + if output_path is not None and self.write_csv: + csv_path = os.path.join(output_path, self.csv_file) + if not os.path.isfile(csv_path): + fOut = open(csv_path, mode="w", encoding="utf-8") + fOut.write(",".join(self.csv_headers)) + fOut.write("\n") + + else: + fOut = open(csv_path, mode="a", encoding="utf-8") + + output_data = [epoch, steps] + for name in self.score_function_names: + for k in self.accuracy_at_k: + output_data.append(scores[name]["accuracy@k"][k]) + + for k in self.precision_recall_at_k: + output_data.append(scores[name]["precision@k"][k]) + output_data.append(scores[name]["recall@k"][k]) + + for k in self.mrr_at_k: + output_data.append(scores[name]["mrr@k"][k]) + + for k in self.ndcg_at_k: + output_data.append(scores[name]["ndcg@k"][k]) + + for k in self.map_at_k: + output_data.append(scores[name]["map@k"][k]) + + fOut.write(",".join(map(str, output_data))) + fOut.write("\n") + fOut.close() + + if not self.primary_metric: + if self.main_score_function is None: + score_function = max( + [(name, scores[name]["ndcg@k"][max(self.ndcg_at_k)]) for name in self.score_function_names], + key=lambda x: x[1], + )[0] + self.primary_metric = f"{score_function}_ndcg@{max(self.ndcg_at_k)}" + else: + self.primary_metric = f"{self.main_score_function.value}_ndcg@{max(self.ndcg_at_k)}" + + metrics = { + f"{score_function}_{metric_name.replace('@k', '@' + str(k))}": value + for score_function, values_dict in scores.items() + for metric_name, values in values_dict.items() + for k, value in values.items() + } + metrics = self.prefix_name_to_metrics(metrics, self.name) + self.store_metrics_in_model_card_data(model, metrics, epoch, steps) + return metrics + + def compute_metrices( + self, + model: SentenceTransformer, + corpus_model=None, + corpus_embeddings: Tensor | None = None, + output_path: str | None = None, + ) -> dict[str, float]: + if corpus_model is None: + corpus_model = model + + max_k = max( + max(self.mrr_at_k), + max(self.ndcg_at_k), + max(self.accuracy_at_k), + max(self.precision_recall_at_k), + max(self.map_at_k), + ) + + # Compute embedding for the queries + query_embeddings = self.embed_inputs( + model, + self.queries, + encode_fn_name="query", + prompt_name=self.query_prompt_name, + prompt=self.query_prompt, + ) + + queries_result_list = {} + for name in self.score_functions: + queries_result_list[name] = [[] for _ in range(len(query_embeddings))] + + # Iterate over chunks of the corpus + for corpus_start_idx in trange( + 0, len(self.corpus), self.corpus_chunk_size, desc="Corpus Chunks", disable=not self.show_progress_bar + ): + corpus_end_idx = min(corpus_start_idx + self.corpus_chunk_size, len(self.corpus)) + + # Encode chunk of corpus + if corpus_embeddings is None: + sub_corpus_embeddings = self.embed_inputs( + corpus_model, + self.corpus[corpus_start_idx:corpus_end_idx], + encode_fn_name="document", + prompt_name=self.corpus_prompt_name, + prompt=self.corpus_prompt, + ) + else: + sub_corpus_embeddings = corpus_embeddings[corpus_start_idx:corpus_end_idx] + + # Compute cosine similarites + for name, score_function in self.score_functions.items(): + pair_scores = score_function(query_embeddings, sub_corpus_embeddings) + + # Get top-k values + pair_scores_top_k_values, pair_scores_top_k_idx = torch.topk( + pair_scores, min(max_k, len(pair_scores[0])), dim=1, largest=True, sorted=False + ) + pair_scores_top_k_values = pair_scores_top_k_values.cpu().tolist() + pair_scores_top_k_idx = pair_scores_top_k_idx.cpu().tolist() + + for query_itr in range(len(query_embeddings)): + for sub_corpus_id, score in zip( + pair_scores_top_k_idx[query_itr], pair_scores_top_k_values[query_itr] + ): + corpus_id = self.corpus_ids[corpus_start_idx + sub_corpus_id] + # NOTE: TREC/BEIR/MTEB skips cases where the corpus_id is the same as the query_id, e.g.: + # if corpus_id == self.queries_ids[query_itr]: + # continue + # This is not done here, as this might be unexpected behaviour if the user just uses + # sets of integers from 0 as query_ids and corpus_ids. + if len(queries_result_list[name][query_itr]) < max_k: + # heaqp tracks the quantity of the first element in the tuple + heapq.heappush(queries_result_list[name][query_itr], (score, corpus_id)) + else: + heapq.heappushpop(queries_result_list[name][query_itr], (score, corpus_id)) + + for name in queries_result_list: + for query_itr in range(len(queries_result_list[name])): + for doc_itr in range(len(queries_result_list[name][query_itr])): + score, corpus_id = queries_result_list[name][query_itr][doc_itr] + queries_result_list[name][query_itr][doc_itr] = {"corpus_id": corpus_id, "score": score} + + if self.write_predictions and output_path is not None: + for name in queries_result_list: + base_filename = self.predictions_file.replace(".jsonl", f"_{name}.jsonl") + json_path = os.path.join(output_path, base_filename) + mode = "w" # Always create a new file for each score function + + with open(json_path, mode=mode, encoding="utf-8") as fOut: + for query_itr in range(len(queries_result_list[name])): + query_id = self.queries_ids[query_itr] + query_text = self.queries[query_itr] + results = queries_result_list[name][query_itr] + + # Sort results by score in descending order + results = sorted(results, key=lambda x: x["score"], reverse=True) + + prediction = { + "query_id": query_id, + "query": query_text, + "results": results, + } + + fOut.write(json.dumps(prediction) + "\n") + + logger.info(f"Queries: {len(self.queries)}") + logger.info(f"Corpus: {len(self.corpus)}\n") + + # Compute scores + scores = {name: self.compute_metrics(queries_result_list[name]) for name in self.score_functions} + + # Output + for name in self.score_function_names: + logger.info(f"Score-Function: {name}") + self.output_scores(scores[name]) + + return scores + + def embed_inputs( + self, + model: SentenceTransformer, + sentences: str | list[str] | np.ndarray, + encode_fn_name: str | None = None, + prompt_name: str | None = None, + prompt: str | None = None, + **kwargs, + ) -> np.ndarray: + if encode_fn_name is None: + encode_fn = model.encode + elif encode_fn_name == "query": + encode_fn = model.encode_query + elif encode_fn_name == "document": + encode_fn = model.encode_document + return encode_fn( + sentences, + prompt_name=prompt_name, + prompt=prompt, + batch_size=self.batch_size, + show_progress_bar=self.show_progress_bar, + convert_to_tensor=True, + truncate_dim=self.truncate_dim, + **kwargs, + ) + + def compute_metrics(self, queries_result_list: list[object]): + # Init score computation values + num_hits_at_k = {k: 0 for k in self.accuracy_at_k} + precisions_at_k = {k: [] for k in self.precision_recall_at_k} + recall_at_k = {k: [] for k in self.precision_recall_at_k} + MRR = {k: 0 for k in self.mrr_at_k} + ndcg = {k: [] for k in self.ndcg_at_k} + AveP_at_k = {k: [] for k in self.map_at_k} + + # Compute scores on results + for query_itr in range(len(queries_result_list)): + query_id = self.queries_ids[query_itr] + + # Sort scores + top_hits = sorted(queries_result_list[query_itr], key=lambda x: x["score"], reverse=True) + query_relevant_docs = self.relevant_docs[query_id] + + # Accuracy@k - We count the result correct, if at least one relevant doc is across the top-k documents + for k_val in self.accuracy_at_k: + for hit in top_hits[0:k_val]: + if hit["corpus_id"] in query_relevant_docs: + num_hits_at_k[k_val] += 1 + break + + # Precision and Recall@k + for k_val in self.precision_recall_at_k: + num_correct = 0 + for hit in top_hits[0:k_val]: + if hit["corpus_id"] in query_relevant_docs: + num_correct += 1 + + precisions_at_k[k_val].append(num_correct / k_val) + recall_at_k[k_val].append(num_correct / len(query_relevant_docs)) + + # MRR@k + for k_val in self.mrr_at_k: + for rank, hit in enumerate(top_hits[0:k_val]): + if hit["corpus_id"] in query_relevant_docs: + MRR[k_val] += 1.0 / (rank + 1) + break + + # NDCG@k + for k_val in self.ndcg_at_k: + predicted_relevance = [ + 1 if top_hit["corpus_id"] in query_relevant_docs else 0 for top_hit in top_hits[0:k_val] + ] + true_relevances = [1] * len(query_relevant_docs) + + ndcg_value = self.compute_dcg_at_k(predicted_relevance, k_val) / self.compute_dcg_at_k( + true_relevances, k_val + ) + ndcg[k_val].append(ndcg_value) + + # MAP@k + for k_val in self.map_at_k: + num_correct = 0 + sum_precisions = 0 + + for rank, hit in enumerate(top_hits[0:k_val]): + if hit["corpus_id"] in query_relevant_docs: + num_correct += 1 + sum_precisions += num_correct / (rank + 1) + avg_precision = sum_precisions / min(k_val, len(query_relevant_docs)) + AveP_at_k[k_val].append(avg_precision) + + # Compute averages + for k in num_hits_at_k: + num_hits_at_k[k] /= len(self.queries) + + for k in precisions_at_k: + precisions_at_k[k] = np.mean(precisions_at_k[k]) + + for k in recall_at_k: + recall_at_k[k] = np.mean(recall_at_k[k]) + + for k in ndcg: + ndcg[k] = np.mean(ndcg[k]) + + for k in MRR: + MRR[k] /= len(self.queries) + + for k in AveP_at_k: + AveP_at_k[k] = np.mean(AveP_at_k[k]) + + return { + "accuracy@k": num_hits_at_k, + "precision@k": precisions_at_k, + "recall@k": recall_at_k, + "ndcg@k": ndcg, + "mrr@k": MRR, + "map@k": AveP_at_k, + } + + def output_scores(self, scores): + for k in scores["accuracy@k"]: + logger.info("Accuracy@{}: {:.2f}%".format(k, scores["accuracy@k"][k] * 100)) + + for k in scores["precision@k"]: + logger.info("Precision@{}: {:.2f}%".format(k, scores["precision@k"][k] * 100)) + + for k in scores["recall@k"]: + logger.info("Recall@{}: {:.2f}%".format(k, scores["recall@k"][k] * 100)) + + for k in scores["mrr@k"]: + logger.info("MRR@{}: {:.4f}".format(k, scores["mrr@k"][k])) + + for k in scores["ndcg@k"]: + logger.info("NDCG@{}: {:.4f}".format(k, scores["ndcg@k"][k])) + + for k in scores["map@k"]: + logger.info("MAP@{}: {:.4f}".format(k, scores["map@k"][k])) + + @staticmethod + def compute_dcg_at_k(relevances, k): + dcg = 0 + for i in range(min(len(relevances), k)): + dcg += relevances[i] / np.log2(i + 2) # +2 as we start our idx at 0 + return dcg + + def get_config_dict(self): + config_dict = {} + config_dict_candidate_keys = [ + "truncate_dim", + "query_prompt", + "query_prompt_name", + "corpus_prompt", + "corpus_prompt_name", + ] + for key in config_dict_candidate_keys: + if getattr(self, key) is not None: + config_dict[key] = getattr(self, key) + return config_dict diff --git a/sentence-transformers/sentence_transformers/evaluation/LabelAccuracyEvaluator.py b/sentence-transformers/sentence_transformers/evaluation/LabelAccuracyEvaluator.py new file mode 100644 index 0000000000000000000000000000000000000000..f0f2fd291c9535c09ca8b9c56d9a7ad86a5f2f0e --- /dev/null +++ b/sentence-transformers/sentence_transformers/evaluation/LabelAccuracyEvaluator.py @@ -0,0 +1,95 @@ +from __future__ import annotations + +import csv +import logging +import os +from typing import TYPE_CHECKING + +import torch +from torch.utils.data import DataLoader + +from sentence_transformers.evaluation.SentenceEvaluator import SentenceEvaluator +from sentence_transformers.util import batch_to_device + +if TYPE_CHECKING: + from sentence_transformers.SentenceTransformer import SentenceTransformer + +logger = logging.getLogger(__name__) + + +class LabelAccuracyEvaluator(SentenceEvaluator): + """ + Evaluate a model based on its accuracy on a labeled dataset + + This requires a model with LossFunction.SOFTMAX + + The results are written in a CSV. If a CSV already exists, then values are appended. + """ + + def __init__(self, dataloader: DataLoader, name: str = "", softmax_model=None, write_csv: bool = True): + """ + Constructs an evaluator for the given dataset + + Args: + dataloader (DataLoader): the data for the evaluation + """ + super().__init__() + self.dataloader = dataloader + self.name = name + self.softmax_model = softmax_model + + if name: + name = "_" + name + + self.write_csv = write_csv + self.csv_file = "accuracy_evaluation" + name + "_results.csv" + self.csv_headers = ["epoch", "steps", "accuracy"] + self.primary_metric = "accuracy" + + def __call__( + self, model: SentenceTransformer, output_path: str | None = None, epoch: int = -1, steps: int = -1 + ) -> dict[str, float]: + model.eval() + total = 0 + correct = 0 + + if epoch != -1: + if steps == -1: + out_txt = f" after epoch {epoch}:" + else: + out_txt = f" in epoch {epoch} after {steps} steps:" + else: + out_txt = ":" + + logger.info("Evaluation on the " + self.name + " dataset" + out_txt) + self.dataloader.collate_fn = model.smart_batching_collate + for step, batch in enumerate(self.dataloader): + features, label_ids = batch + for idx in range(len(features)): + features[idx] = batch_to_device(features[idx], model.device) + label_ids = label_ids.to(model.device) + with torch.no_grad(): + _, prediction = self.softmax_model(features, labels=None) + + total += prediction.size(0) + correct += torch.argmax(prediction, dim=1).eq(label_ids).sum().item() + accuracy = correct / total + + logger.info(f"Accuracy: {accuracy:.4f} ({correct}/{total})\n") + + if output_path is not None and self.write_csv: + csv_path = os.path.join(output_path, self.csv_file) + if not os.path.isfile(csv_path): + with open(csv_path, newline="", mode="w", encoding="utf-8") as f: + writer = csv.writer(f) + writer.writerow(self.csv_headers) + writer.writerow([epoch, steps, accuracy]) + else: + with open(csv_path, newline="", mode="a", encoding="utf-8") as f: + writer = csv.writer(f) + writer.writerow([epoch, steps, accuracy]) + + metrics = {"accuracy": accuracy} + metrics = self.prefix_name_to_metrics(metrics, self.name) + self.store_metrics_in_model_card_data(model, metrics, epoch, steps) + return metrics diff --git a/sentence-transformers/sentence_transformers/evaluation/MSEEvaluator.py b/sentence-transformers/sentence_transformers/evaluation/MSEEvaluator.py new file mode 100644 index 0000000000000000000000000000000000000000..f445a5a72cae5c8a645cf1a84f32c174af283de6 --- /dev/null +++ b/sentence-transformers/sentence_transformers/evaluation/MSEEvaluator.py @@ -0,0 +1,157 @@ +from __future__ import annotations + +import csv +import logging +import os +from typing import TYPE_CHECKING + +from sentence_transformers.evaluation.SentenceEvaluator import SentenceEvaluator + +if TYPE_CHECKING: + import numpy as np + + from sentence_transformers.SentenceTransformer import SentenceTransformer + +logger = logging.getLogger(__name__) + + +class MSEEvaluator(SentenceEvaluator): + """ + Computes the mean squared error (x100) between the computed sentence embedding + and some target sentence embedding. + + The MSE is computed between ||teacher.encode(source_sentences) - student.encode(target_sentences)||. + + For multilingual knowledge distillation (https://arxiv.org/abs/2004.09813), source_sentences are in English + and target_sentences are in a different language like German, Chinese, Spanish... + + Args: + source_sentences (List[str]): Source sentences to embed with the teacher model. + target_sentences (List[str]): Target sentences to embed with the student model. + teacher_model (SentenceTransformer, optional): The teacher model to compute the source sentence embeddings. + show_progress_bar (bool, optional): Show progress bar when computing embeddings. Defaults to False. + batch_size (int, optional): Batch size to compute sentence embeddings. Defaults to 32. + name (str, optional): Name of the evaluator. Defaults to "". + write_csv (bool, optional): Write results to CSV file. Defaults to True. + truncate_dim (int, optional): The dimension to truncate sentence embeddings to. `None` uses the model's current truncation + dimension. Defaults to None. + + Example: + :: + + from sentence_transformers import SentenceTransformer + from sentence_transformers.evaluation import MSEEvaluator + from datasets import load_dataset + + # Load a model + student_model = SentenceTransformer('paraphrase-multilingual-mpnet-base-v2') + teacher_model = SentenceTransformer('all-mpnet-base-v2') + + # Load any dataset with some texts + dataset = load_dataset("sentence-transformers/stsb", split="validation") + sentences = dataset["sentence1"] + dataset["sentence2"] + + # Given queries, a corpus and a mapping with relevant documents, the MSEEvaluator computes different MSE metrics. + mse_evaluator = MSEEvaluator( + source_sentences=sentences, + target_sentences=sentences, + teacher_model=teacher_model, + name="stsb-dev", + ) + results = mse_evaluator(student_model) + ''' + MSE evaluation (lower = better) on the stsb-dev dataset: + MSE (*100): 0.805045 + ''' + print(mse_evaluator.primary_metric) + # => "stsb-dev_negative_mse" + print(results[mse_evaluator.primary_metric]) + # => -0.8050452917814255 + """ + + def __init__( + self, + source_sentences: list[str], + target_sentences: list[str], + teacher_model=None, + show_progress_bar: bool = False, + batch_size: int = 32, + name: str = "", + write_csv: bool = True, + truncate_dim: int | None = None, + ): + super().__init__() + self.truncate_dim = truncate_dim + self.target_sentences = target_sentences + self.show_progress_bar = show_progress_bar + self.batch_size = batch_size + self.name = name + + self.csv_file = "mse_evaluation_" + name + "_results.csv" + self.csv_headers = ["epoch", "steps", "MSE"] + self.write_csv = write_csv + self.primary_metric = "negative_mse" + + self.source_embeddings = self.embed_inputs(teacher_model, source_sentences) + + def __call__( + self, model: SentenceTransformer, output_path: str | None = None, epoch=-1, steps=-1 + ) -> dict[str, float]: + if epoch != -1: + if steps == -1: + out_txt = f" after epoch {epoch}" + else: + out_txt = f" in epoch {epoch} after {steps} steps" + else: + out_txt = "" + if self.truncate_dim is not None: + out_txt += f" (truncated to {self.truncate_dim})" + + target_embeddings = self.embed_inputs(model, self.target_sentences) + + mse = ((self.source_embeddings - target_embeddings) ** 2).mean() + mse = mse * 100 + + logger.info(f"MSE evaluation (lower = better) on the {self.name} dataset{out_txt}:") + logger.info(f"MSE (*100):\t{mse:4f}") + + if output_path is not None and self.write_csv: + csv_path = os.path.join(output_path, self.csv_file) + output_file_exists = os.path.isfile(csv_path) + with open(csv_path, newline="", mode="a" if output_file_exists else "w", encoding="utf-8") as f: + writer = csv.writer(f) + if not output_file_exists: + writer.writerow(self.csv_headers) + + writer.writerow([epoch, steps, mse]) + + # Return negative score as SentenceTransformers maximizes the performance + metrics = {"negative_mse": -mse} + metrics = self.prefix_name_to_metrics(metrics, self.name) + self.store_metrics_in_model_card_data(model, metrics, epoch, steps) + return metrics + + def embed_inputs( + self, + model: SentenceTransformer, + sentences: str | list[str] | np.ndarray, + **kwargs, + ) -> np.ndarray: + return model.encode( + sentences, + batch_size=self.batch_size, + show_progress_bar=self.show_progress_bar, + convert_to_numpy=True, + truncate_dim=self.truncate_dim, + **kwargs, + ) + + @property + def description(self) -> str: + return "Knowledge Distillation" + + def get_config_dict(self): + config_dict = {} + if self.truncate_dim is not None: + config_dict["truncate_dim"] = self.truncate_dim + return config_dict diff --git a/sentence-transformers/sentence_transformers/evaluation/MSEEvaluatorFromDataFrame.py b/sentence-transformers/sentence_transformers/evaluation/MSEEvaluatorFromDataFrame.py new file mode 100644 index 0000000000000000000000000000000000000000..cea084810e410cab50726cbea2a545512954717a --- /dev/null +++ b/sentence-transformers/sentence_transformers/evaluation/MSEEvaluatorFromDataFrame.py @@ -0,0 +1,138 @@ +from __future__ import annotations + +import csv +import logging +import os +from typing import TYPE_CHECKING + +import numpy as np + +from sentence_transformers.evaluation.SentenceEvaluator import SentenceEvaluator + +if TYPE_CHECKING: + import numpy as np + + from sentence_transformers.SentenceTransformer import SentenceTransformer + +logger = logging.getLogger(__name__) + + +class MSEEvaluatorFromDataFrame(SentenceEvaluator): + """ + Computes the mean squared error (x100) between the computed sentence embedding and some target sentence embedding. + + Args: + dataframe (List[Dict[str, str]]): It must have the following format. Rows contains different, parallel sentences. + Columns are the respective language codes:: + + [{'en': 'My sentence in English', 'es': 'Oración en español', 'fr': 'Phrase en français'...}, + {'en': 'My second sentence', ...}] + teacher_model (SentenceTransformer): The teacher model used to compute the sentence embeddings. + combinations (List[Tuple[str, str]]): Must be of the format ``[('en', 'es'), ('en', 'fr'), ...]``. + First entry in a tuple is the source language. The sentence in the respective language will be fetched from + the dataframe and passed to the teacher model. Second entry in a tuple the the target language. Sentence + will be fetched from the dataframe and passed to the student model + batch_size (int, optional): The batch size to compute sentence embeddings. Defaults to 8. + name (str, optional): The name of the evaluator. Defaults to "". + write_csv (bool, optional): Whether to write the results to a CSV file. Defaults to True. + truncate_dim (Optional[int], optional): The dimension to truncate sentence embeddings to. If None, uses the model's + current truncation dimension. Defaults to None. + """ + + def __init__( + self, + dataframe: list[dict[str, str]], + teacher_model: SentenceTransformer, + combinations: list[tuple[str, str]], + batch_size: int = 8, + name: str = "", + write_csv: bool = True, + truncate_dim: int | None = None, + ): + super().__init__() + self.combinations = combinations + self.name = name + self.batch_size = batch_size + + if name: + name = "_" + name + + self.csv_file = "mse_evaluation" + name + "_results.csv" + self.csv_headers = ["epoch", "steps"] + self.primary_metric = "negative_mse" + self.write_csv = write_csv + self.truncate_dim = truncate_dim + self.data = {} + + logger.info("Compute teacher embeddings") + all_source_sentences = set() + for src_lang, trg_lang in self.combinations: + src_sentences = [] + trg_sentences = [] + + for row in dataframe: + if row[src_lang].strip() != "" and row[trg_lang].strip() != "": + all_source_sentences.add(row[src_lang]) + src_sentences.append(row[src_lang]) + trg_sentences.append(row[trg_lang]) + + self.data[(src_lang, trg_lang)] = (src_sentences, trg_sentences) + self.csv_headers.append(f"{src_lang}-{trg_lang}") + + all_source_sentences = list(all_source_sentences) + + all_src_embeddings = self.embed_inputs(teacher_model, all_source_sentences) + self.teacher_embeddings = {sent: emb for sent, emb in zip(all_source_sentences, all_src_embeddings)} + + def __call__( + self, model: SentenceTransformer, output_path: str | None = None, epoch: int = -1, steps: int = -1 + ) -> dict[str, float]: + model.eval() + + mse_scores = [] + for src_lang, trg_lang in self.combinations: + src_sentences, trg_sentences = self.data[(src_lang, trg_lang)] + + src_embeddings = np.asarray([self.teacher_embeddings[sent] for sent in src_sentences]) + trg_embeddings = np.asarray(self.embed_inputs(model, trg_sentences)) + + mse = ((src_embeddings - trg_embeddings) ** 2).mean() + mse *= 100 + mse_scores.append(mse) + + logger.info(f"MSE evaluation on {self.name} dataset - {src_lang}-{trg_lang}:") + logger.info(f"MSE (*100):\t{mse:4f}") + + if output_path is not None and self.write_csv: + csv_path = os.path.join(output_path, self.csv_file) + output_file_exists = os.path.isfile(csv_path) + with open(csv_path, newline="", mode="a" if output_file_exists else "w", encoding="utf-8") as f: + writer = csv.writer(f) + if not output_file_exists: + writer.writerow(self.csv_headers) + + writer.writerow([epoch, steps] + mse_scores) + + # Return negative score as SentenceTransformers maximizes the performance + metrics = {"negative_mse": -np.mean(mse_scores).item()} + metrics = self.prefix_name_to_metrics(metrics, self.name) + self.store_metrics_in_model_card_data(model, metrics, epoch, steps) + return metrics + + def embed_inputs( + self, + model: SentenceTransformer, + sentences: str | list[str] | np.ndarray, + **kwargs, + ) -> np.ndarray: + return model.encode( + sentences, + batch_size=self.batch_size, + convert_to_numpy=True, + truncate_dim=self.truncate_dim, + **kwargs, + ) + + @property + def description(self) -> str: + return "Knowledge Distillation" diff --git a/sentence-transformers/sentence_transformers/evaluation/NanoBEIREvaluator.py b/sentence-transformers/sentence_transformers/evaluation/NanoBEIREvaluator.py new file mode 100644 index 0000000000000000000000000000000000000000..f02e8e0a2cdc0158126895f74b796cf517717146 --- /dev/null +++ b/sentence-transformers/sentence_transformers/evaluation/NanoBEIREvaluator.py @@ -0,0 +1,481 @@ +from __future__ import annotations + +import logging +import os +from typing import TYPE_CHECKING, Any, Callable, Literal + +import numpy as np +from torch import Tensor +from tqdm import tqdm + +from sentence_transformers import SentenceTransformer +from sentence_transformers.evaluation.InformationRetrievalEvaluator import InformationRetrievalEvaluator +from sentence_transformers.evaluation.SentenceEvaluator import SentenceEvaluator +from sentence_transformers.similarity_functions import SimilarityFunction +from sentence_transformers.util import is_datasets_available + +if TYPE_CHECKING: + from sentence_transformers.SentenceTransformer import SentenceTransformer + +logger = logging.getLogger(__name__) + +DatasetNameType = Literal[ + "climatefever", + "dbpedia", + "fever", + "fiqa2018", + "hotpotqa", + "msmarco", + "nfcorpus", + "nq", + "quoraretrieval", + "scidocs", + "arguana", + "scifact", + "touche2020", +] + + +dataset_name_to_id = { + "climatefever": "zeta-alpha-ai/NanoClimateFEVER", + "dbpedia": "zeta-alpha-ai/NanoDBPedia", + "fever": "zeta-alpha-ai/NanoFEVER", + "fiqa2018": "zeta-alpha-ai/NanoFiQA2018", + "hotpotqa": "zeta-alpha-ai/NanoHotpotQA", + "msmarco": "zeta-alpha-ai/NanoMSMARCO", + "nfcorpus": "zeta-alpha-ai/NanoNFCorpus", + "nq": "zeta-alpha-ai/NanoNQ", + "quoraretrieval": "zeta-alpha-ai/NanoQuoraRetrieval", + "scidocs": "zeta-alpha-ai/NanoSCIDOCS", + "arguana": "zeta-alpha-ai/NanoArguAna", + "scifact": "zeta-alpha-ai/NanoSciFact", + "touche2020": "zeta-alpha-ai/NanoTouche2020", +} + +dataset_name_to_human_readable = { + "climatefever": "ClimateFEVER", + "dbpedia": "DBPedia", + "fever": "FEVER", + "fiqa2018": "FiQA2018", + "hotpotqa": "HotpotQA", + "msmarco": "MSMARCO", + "nfcorpus": "NFCorpus", + "nq": "NQ", + "quoraretrieval": "QuoraRetrieval", + "scidocs": "SCIDOCS", + "arguana": "ArguAna", + "scifact": "SciFact", + "touche2020": "Touche2020", +} + + +class NanoBEIREvaluator(SentenceEvaluator): + """ + This class evaluates the performance of a SentenceTransformer Model on the NanoBEIR collection of Information Retrieval datasets. + + The collection is a set of datasets based on the BEIR collection, but with a significantly smaller size, so it can + be used for quickly evaluating the retrieval performance of a model before committing to a full evaluation. + The datasets are available on Hugging Face in the `NanoBEIR collection `_. + This evaluator will return the same metrics as the InformationRetrievalEvaluator (i.e., MRR, nDCG, Recall@k), for each dataset and on average. + + Args: + dataset_names (List[str]): The names of the datasets to evaluate on. Defaults to all datasets. + mrr_at_k (List[int]): A list of integers representing the values of k for MRR calculation. Defaults to [10]. + ndcg_at_k (List[int]): A list of integers representing the values of k for NDCG calculation. Defaults to [10]. + accuracy_at_k (List[int]): A list of integers representing the values of k for accuracy calculation. Defaults to [1, 3, 5, 10]. + precision_recall_at_k (List[int]): A list of integers representing the values of k for precision and recall calculation. Defaults to [1, 3, 5, 10]. + map_at_k (List[int]): A list of integers representing the values of k for MAP calculation. Defaults to [100]. + show_progress_bar (bool): Whether to show a progress bar during evaluation. Defaults to False. + batch_size (int): The batch size for evaluation. Defaults to 32. + write_csv (bool): Whether to write the evaluation results to a CSV file. Defaults to True. + truncate_dim (int, optional): The dimension to truncate the embeddings to. Defaults to None. + score_functions (Dict[str, Callable[[Tensor, Tensor], Tensor]]): A dictionary mapping score function names to score functions. Defaults to {SimilarityFunction.COSINE.value: cos_sim, SimilarityFunction.DOT_PRODUCT.value: dot_score}. + main_score_function (Union[str, SimilarityFunction], optional): The main score function to use for evaluation. Defaults to None. + aggregate_fn (Callable[[list[float]], float]): The function to aggregate the scores. Defaults to np.mean. + aggregate_key (str): The key to use for the aggregated score. Defaults to "mean". + query_prompts (str | dict[str, str], optional): The prompts to add to the queries. If a string, will add the same prompt to all queries. If a dict, expects that all datasets in dataset_names are keys. + corpus_prompts (str | dict[str, str], optional): The prompts to add to the corpus. If a string, will add the same prompt to all corpus. If a dict, expects that all datasets in dataset_names are keys. + write_predictions (bool): Whether to write the predictions to a JSONL file. Defaults to False. + This can be useful for downstream evaluation as it can be used as input to the :class:`~sentence_transformers.sparse_encoder.evaluation.ReciprocalRankFusionEvaluator` that accept precomputed predictions. + + Example: + :: + + from sentence_transformers import SentenceTransformer + from sentence_transformers.evaluation import NanoBEIREvaluator + + model = SentenceTransformer('intfloat/multilingual-e5-large-instruct') + + datasets = ["QuoraRetrieval", "MSMARCO"] + query_prompts = { + "QuoraRetrieval": "Instruct: Given a question, retrieve questions that are semantically equivalent to the given question\\nQuery: ", + "MSMARCO": "Instruct: Given a web search query, retrieve relevant passages that answer the query\\nQuery: " + } + + evaluator = NanoBEIREvaluator( + dataset_names=datasets, + query_prompts=query_prompts, + ) + + results = evaluator(model) + ''' + NanoBEIR Evaluation of the model on ['QuoraRetrieval', 'MSMARCO'] dataset: + Evaluating NanoQuoraRetrieval + Information Retrieval Evaluation of the model on the NanoQuoraRetrieval dataset: + Queries: 50 + Corpus: 5046 + + Score-Function: cosine + Accuracy@1: 92.00% + Accuracy@3: 98.00% + Accuracy@5: 100.00% + Accuracy@10: 100.00% + Precision@1: 92.00% + Precision@3: 40.67% + Precision@5: 26.00% + Precision@10: 14.00% + Recall@1: 81.73% + Recall@3: 94.20% + Recall@5: 97.93% + Recall@10: 100.00% + MRR@10: 0.9540 + NDCG@10: 0.9597 + MAP@100: 0.9395 + + Evaluating NanoMSMARCO + Information Retrieval Evaluation of the model on the NanoMSMARCO dataset: + Queries: 50 + Corpus: 5043 + + Score-Function: cosine + Accuracy@1: 40.00% + Accuracy@3: 74.00% + Accuracy@5: 78.00% + Accuracy@10: 88.00% + Precision@1: 40.00% + Precision@3: 24.67% + Precision@5: 15.60% + Precision@10: 8.80% + Recall@1: 40.00% + Recall@3: 74.00% + Recall@5: 78.00% + Recall@10: 88.00% + MRR@10: 0.5849 + NDCG@10: 0.6572 + MAP@100: 0.5892 + Average Queries: 50.0 + Average Corpus: 5044.5 + + Aggregated for Score Function: cosine + Accuracy@1: 66.00% + Accuracy@3: 86.00% + Accuracy@5: 89.00% + Accuracy@10: 94.00% + Precision@1: 66.00% + Recall@1: 60.87% + Precision@3: 32.67% + Recall@3: 84.10% + Precision@5: 20.80% + Recall@5: 87.97% + Precision@10: 11.40% + Recall@10: 94.00% + MRR@10: 0.7694 + NDCG@10: 0.8085 + ''' + print(evaluator.primary_metric) + # => "NanoBEIR_mean_cosine_ndcg@10" + print(results[evaluator.primary_metric]) + # => 0.8084508771660436 + """ + + information_retrieval_class = InformationRetrievalEvaluator + + def __init__( + self, + dataset_names: list[DatasetNameType] | None = None, + mrr_at_k: list[int] = [10], + ndcg_at_k: list[int] = [10], + accuracy_at_k: list[int] = [1, 3, 5, 10], + precision_recall_at_k: list[int] = [1, 3, 5, 10], + map_at_k: list[int] = [100], + show_progress_bar: bool = False, + batch_size: int = 32, + write_csv: bool = True, + truncate_dim: int | None = None, + score_functions: dict[str, Callable[[Tensor, Tensor], Tensor]] | None = None, + main_score_function: str | SimilarityFunction | None = None, + aggregate_fn: Callable[[list[float]], float] = np.mean, + aggregate_key: str = "mean", + query_prompts: str | dict[str, str] | None = None, + corpus_prompts: str | dict[str, str] | None = None, + write_predictions: bool = False, + ): + super().__init__() + if dataset_names is None: + dataset_names = list(dataset_name_to_id.keys()) + self.dataset_names = dataset_names + self.aggregate_fn = aggregate_fn + self.aggregate_key = aggregate_key + self.write_csv = write_csv + self.query_prompts = query_prompts + self.corpus_prompts = corpus_prompts + self.show_progress_bar = show_progress_bar + self.write_csv = write_csv + self.score_functions = score_functions + self.score_function_names = sorted(list(self.score_functions.keys())) if score_functions else [] + self.main_score_function = main_score_function + self.truncate_dim = truncate_dim + self.name = f"NanoBEIR_{aggregate_key}" + if self.truncate_dim: + self.name += f"_{self.truncate_dim}" + + self.mrr_at_k = mrr_at_k + self.ndcg_at_k = ndcg_at_k + self.accuracy_at_k = accuracy_at_k + self.precision_recall_at_k = precision_recall_at_k + self.map_at_k = map_at_k + + self._validate_dataset_names() + self._validate_prompts() + + ir_evaluator_kwargs = { + "mrr_at_k": mrr_at_k, + "ndcg_at_k": ndcg_at_k, + "accuracy_at_k": accuracy_at_k, + "precision_recall_at_k": precision_recall_at_k, + "map_at_k": map_at_k, + "show_progress_bar": show_progress_bar, + "batch_size": batch_size, + "write_csv": write_csv, + "truncate_dim": truncate_dim, + "score_functions": score_functions, + "main_score_function": main_score_function, + "write_predictions": write_predictions, + } + self.evaluators = [ + self._load_dataset(name, **ir_evaluator_kwargs) + for name in tqdm(self.dataset_names, desc="Loading NanoBEIR datasets", leave=False) + ] + + self.csv_file: str = f"NanoBEIR_evaluation_{aggregate_key}_results.csv" + self.csv_headers = ["epoch", "steps"] + + self._append_csv_headers(self.score_function_names) + + def _append_csv_headers(self, score_function_names): + for score_name in score_function_names: + for k in self.accuracy_at_k: + self.csv_headers.append(f"{score_name}-Accuracy@{k}") + + for k in self.precision_recall_at_k: + self.csv_headers.append(f"{score_name}-Precision@{k}") + self.csv_headers.append(f"{score_name}-Recall@{k}") + + for k in self.mrr_at_k: + self.csv_headers.append(f"{score_name}-MRR@{k}") + + for k in self.ndcg_at_k: + self.csv_headers.append(f"{score_name}-NDCG@{k}") + + for k in self.map_at_k: + self.csv_headers.append(f"{score_name}-MAP@{k}") + + def __call__( + self, + model: SentenceTransformer, + output_path: str | None = None, + epoch: int = -1, + steps: int = -1, + *args, + **kwargs, + ) -> dict[str, float]: + per_metric_results = {} + per_dataset_results = {} + if epoch != -1: + if steps == -1: + out_txt = f" after epoch {epoch}" + else: + out_txt = f" in epoch {epoch} after {steps} steps" + else: + out_txt = "" + if self.truncate_dim is not None: + out_txt += f" (truncated to {self.truncate_dim})" + logger.info(f"NanoBEIR Evaluation of the model on {self.dataset_names} dataset{out_txt}:") + + if self.score_functions is None: + self.score_functions = {model.similarity_fn_name: model.similarity} + self.score_function_names = [model.similarity_fn_name] + self._append_csv_headers(self.score_function_names) + + num_underscores_in_name = self.name.count("_") + for evaluator in tqdm(self.evaluators, desc="Evaluating datasets", disable=not self.show_progress_bar): + logger.info(f"Evaluating {evaluator.name}") + evaluation = evaluator(model, output_path, epoch, steps) + for full_key, metric_value in evaluation.items(): + splits = full_key.split("_", maxsplit=num_underscores_in_name) + metric = splits[-1] + if metric not in per_metric_results: + per_metric_results[metric] = [] + per_dataset_results[full_key] = metric_value + per_metric_results[metric].append(metric_value) + + agg_results = {} + for metric in per_metric_results: + agg_results[metric] = self.aggregate_fn(per_metric_results[metric]) + + if output_path is not None and self.write_csv: + csv_path = os.path.join(output_path, self.csv_file) + if not os.path.isfile(csv_path): + fOut = open(csv_path, mode="w", encoding="utf-8") + fOut.write(",".join(self.csv_headers)) + fOut.write("\n") + + else: + fOut = open(csv_path, mode="a", encoding="utf-8") + + output_data = [epoch, steps] + for name in self.score_function_names: + for k in self.accuracy_at_k: + output_data.append(agg_results[f"{name}_accuracy@{k}"]) + + for k in self.precision_recall_at_k: + output_data.append(agg_results[f"{name}_precision@{k}"]) + output_data.append(agg_results[f"{name}_recall@{k}"]) + + for k in self.mrr_at_k: + output_data.append(agg_results[f"{name}_mrr@{k}"]) + + for k in self.ndcg_at_k: + output_data.append(agg_results[f"{name}_ndcg@{k}"]) + + for k in self.map_at_k: + output_data.append(agg_results[f"{name}_map@{k}"]) + + fOut.write(",".join(map(str, output_data))) + fOut.write("\n") + fOut.close() + + if not self.primary_metric: + if self.main_score_function is None: + score_function = max( + [(name, agg_results[f"{name}_ndcg@{max(self.ndcg_at_k)}"]) for name in self.score_function_names], + key=lambda x: x[1], + )[0] + self.primary_metric = f"{score_function}_ndcg@{max(self.ndcg_at_k)}" + else: + self.primary_metric = f"{self.main_score_function.value}_ndcg@{max(self.ndcg_at_k)}" + + avg_queries = np.mean([len(evaluator.queries) for evaluator in self.evaluators]) + avg_corpus = np.mean([len(evaluator.corpus) for evaluator in self.evaluators]) + logger.info(f"Average Queries: {avg_queries}") + logger.info(f"Average Corpus: {avg_corpus}\n") + + for name in self.score_function_names: + logger.info(f"Aggregated for Score Function: {name}") + for k in self.accuracy_at_k: + logger.info("Accuracy@{}: {:.2f}%".format(k, agg_results[f"{name}_accuracy@{k}"] * 100)) + + for k in self.precision_recall_at_k: + logger.info("Precision@{}: {:.2f}%".format(k, agg_results[f"{name}_precision@{k}"] * 100)) + logger.info("Recall@{}: {:.2f}%".format(k, agg_results[f"{name}_recall@{k}"] * 100)) + + for k in self.mrr_at_k: + logger.info("MRR@{}: {:.4f}".format(k, agg_results[f"{name}_mrr@{k}"])) + + for k in self.ndcg_at_k: + logger.info("NDCG@{}: {:.4f}".format(k, agg_results[f"{name}_ndcg@{k}"])) + + for k in self.map_at_k: + logger.info("MAP@{}: {:.4f}".format(k, agg_results[f"{name}_map@{k}"])) + + agg_results = self.prefix_name_to_metrics(agg_results, self.name) + self.store_metrics_in_model_card_data(model, agg_results, epoch, steps) + + per_dataset_results.update(agg_results) + + return per_dataset_results + + def _get_human_readable_name(self, dataset_name: DatasetNameType) -> str: + human_readable_name = f"Nano{dataset_name_to_human_readable[dataset_name.lower()]}" + if self.truncate_dim is not None: + human_readable_name += f"_{self.truncate_dim}" + return human_readable_name + + def _load_dataset(self, dataset_name: DatasetNameType, **ir_evaluator_kwargs) -> InformationRetrievalEvaluator: + if not is_datasets_available(): + raise ValueError( + "datasets is not available. Please install it to use the NanoBEIREvaluator via `pip install datasets`." + ) + from datasets import load_dataset + + dataset_path = dataset_name_to_id[dataset_name.lower()] + corpus = load_dataset(dataset_path, "corpus", split="train") + queries = load_dataset(dataset_path, "queries", split="train") + qrels = load_dataset(dataset_path, "qrels", split="train") + corpus_dict = {sample["_id"]: sample["text"] for sample in corpus if len(sample["text"]) > 0} + queries_dict = {sample["_id"]: sample["text"] for sample in queries if len(sample["text"]) > 0} + qrels_dict = {} + for sample in qrels: + if sample["query-id"] not in qrels_dict: + qrels_dict[sample["query-id"]] = set() + qrels_dict[sample["query-id"]].add(sample["corpus-id"]) + + if self.query_prompts is not None: + ir_evaluator_kwargs["query_prompt"] = self.query_prompts.get(dataset_name, None) + if self.corpus_prompts is not None: + ir_evaluator_kwargs["corpus_prompt"] = self.corpus_prompts.get(dataset_name, None) + human_readable_name = self._get_human_readable_name(dataset_name) + return self.information_retrieval_class( + queries=queries_dict, + corpus=corpus_dict, + relevant_docs=qrels_dict, + name=human_readable_name, + **ir_evaluator_kwargs, + ) + + def _validate_dataset_names(self): + if len(self.dataset_names) == 0: + raise ValueError("dataset_names cannot be empty. Use None to evaluate on all datasets.") + if missing_datasets := [ + dataset_name for dataset_name in self.dataset_names if dataset_name.lower() not in dataset_name_to_id + ]: + raise ValueError( + f"Dataset(s) {missing_datasets} not found in the NanoBEIR collection. " + f"Valid dataset names are: {list(dataset_name_to_id.keys())}" + ) + + def _validate_prompts(self): + error_msg = "" + if self.query_prompts is not None: + if isinstance(self.query_prompts, str): + self.query_prompts = {dataset_name: self.query_prompts for dataset_name in self.dataset_names} + elif missing_query_prompts := [ + dataset_name for dataset_name in self.dataset_names if dataset_name not in self.query_prompts + ]: + error_msg += f"The following datasets are missing query prompts: {missing_query_prompts}\n" + + if self.corpus_prompts is not None: + if isinstance(self.corpus_prompts, str): + self.corpus_prompts = {dataset_name: self.corpus_prompts for dataset_name in self.dataset_names} + elif missing_corpus_prompts := [ + dataset_name for dataset_name in self.dataset_names if dataset_name not in self.corpus_prompts + ]: + error_msg += f"The following datasets are missing corpus prompts: {missing_corpus_prompts}\n" + + if error_msg: + raise ValueError(error_msg.strip()) + + def store_metrics_in_model_card_data(self, *args, **kwargs): + # Only store metrics in the model card data if there is more than one dataset. + # Otherwise the e.g. mean scores for NanoBEIR are the same as the scores for + # the single dataset, and we'd end up with duplicate entries. + if len(self.dataset_names) > 1: + super().store_metrics_in_model_card_data(*args, **kwargs) + + def get_config_dict(self) -> dict[str, Any]: + config_dict = {"dataset_names": self.dataset_names} + config_dict_candidate_keys = ["truncate_dim", "query_prompts", "corpus_prompts"] + for key in config_dict_candidate_keys: + if getattr(self, key) is not None: + config_dict[key] = getattr(self, key) + return config_dict diff --git a/sentence-transformers/sentence_transformers/evaluation/ParaphraseMiningEvaluator.py b/sentence-transformers/sentence_transformers/evaluation/ParaphraseMiningEvaluator.py new file mode 100644 index 0000000000000000000000000000000000000000..8588a6b8536ec384a0b0a2a83c4b5b5ea7036776 --- /dev/null +++ b/sentence-transformers/sentence_transformers/evaluation/ParaphraseMiningEvaluator.py @@ -0,0 +1,278 @@ +from __future__ import annotations + +import csv +import logging +import os +from collections import defaultdict +from typing import TYPE_CHECKING + +from sentence_transformers.evaluation.SentenceEvaluator import SentenceEvaluator +from sentence_transformers.util import paraphrase_mining + +if TYPE_CHECKING: + from sentence_transformers.SentenceTransformer import SentenceTransformer + +logger = logging.getLogger(__name__) + + +class ParaphraseMiningEvaluator(SentenceEvaluator): + """ + Given a large set of sentences, this evaluator performs paraphrase (duplicate) mining and + identifies the pairs with the highest similarity. It compare the extracted paraphrase pairs + with a set of gold labels and computes the F1 score. + + Args: + sentences_map (Dict[str, str]): A dictionary that maps sentence-ids to sentences. + For example, sentences_map[id] => sentence. + duplicates_list (List[Tuple[str, str]], optional): A list with id pairs [(id1, id2), (id1, id5)] + that identifies the duplicates / paraphrases in the sentences_map. Defaults to None. + duplicates_dict (Dict[str, Dict[str, bool]], optional): A default dictionary mapping [id1][id2] + to true if id1 and id2 are duplicates. Must be symmetric, i.e., if [id1][id2] => True, + then [id2][id1] => True. Defaults to None. + add_transitive_closure (bool, optional): If true, it adds a transitive closure, + i.e. if dup[a][b] and dup[b][c], then dup[a][c]. Defaults to False. + query_chunk_size (int, optional): To identify the paraphrases, the cosine-similarity between + all sentence-pairs will be computed. As this might require a lot of memory, we perform + a batched computation. query_chunk_size sentences will be compared against up to + corpus_chunk_size sentences. In the default setting, 5000 sentences will be grouped + together and compared up-to against 100k other sentences. Defaults to 5000. + corpus_chunk_size (int, optional): The corpus will be batched, to reduce the memory requirement. + Defaults to 100000. + max_pairs (int, optional): We will only extract up to max_pairs potential paraphrase candidates. + Defaults to 500000. + top_k (int, optional): For each query, we extract the top_k most similar pairs and add it to a sorted list. + I.e., for one sentence we cannot find more than top_k paraphrases. Defaults to 100. + show_progress_bar (bool, optional): Output a progress bar. Defaults to False. + batch_size (int, optional): Batch size for computing sentence embeddings. Defaults to 16. + name (str, optional): Name of the experiment. Defaults to "". + write_csv (bool, optional): Write results to CSV file. Defaults to True. + truncate_dim (Optional[int], optional): The dimension to truncate sentence embeddings to. + `None` uses the model's current truncation dimension. Defaults to None. + + Example: + :: + + from datasets import load_dataset + from sentence_transformers.SentenceTransformer import SentenceTransformer + from sentence_transformers.evaluation import ParaphraseMiningEvaluator + + # Load a model + model = SentenceTransformer('all-mpnet-base-v2') + + # Load the Quora Duplicates Mining dataset + questions_dataset = load_dataset("sentence-transformers/quora-duplicates-mining", "questions", split="dev") + duplicates_dataset = load_dataset("sentence-transformers/quora-duplicates-mining", "duplicates", split="dev") + + # Create a mapping from qid to question & a list of duplicates (qid1, qid2) + qid_to_questions = dict(zip(questions_dataset["qid"], questions_dataset["question"])) + duplicates = list(zip(duplicates_dataset["qid1"], duplicates_dataset["qid2"])) + + # Initialize the paraphrase mining evaluator + paraphrase_mining_evaluator = ParaphraseMiningEvaluator( + sentences_map=qid_to_questions, + duplicates_list=duplicates, + name="quora-duplicates-dev", + ) + results = paraphrase_mining_evaluator(model) + ''' + Paraphrase Mining Evaluation of the model on the quora-duplicates-dev dataset: + Number of candidate pairs: 250564 + Average Precision: 56.51 + Optimal threshold: 0.8325 + Precision: 52.76 + Recall: 59.19 + F1: 55.79 + ''' + print(paraphrase_mining_evaluator.primary_metric) + # => "quora-duplicates-dev_average_precision" + print(results[paraphrase_mining_evaluator.primary_metric]) + # => 0.5650940787776353 + """ + + def __init__( + self, + sentences_map: dict[str, str], + duplicates_list: list[tuple[str, str]] | None = None, + duplicates_dict: dict[str, dict[str, bool]] | None = None, + add_transitive_closure: bool = False, + query_chunk_size: int = 5000, + corpus_chunk_size: int = 100000, + max_pairs: int = 500000, + top_k: int = 100, + show_progress_bar: bool = False, + batch_size: int = 16, + name: str = "", + write_csv: bool = True, + truncate_dim: int | None = None, + ): + super().__init__() + self.sentences = [] + self.ids = [] + + for id, sentence in sentences_map.items(): + self.sentences.append(sentence) + self.ids.append(id) + + self.name = name + self.show_progress_bar = show_progress_bar + self.batch_size = batch_size + self.query_chunk_size = query_chunk_size + self.corpus_chunk_size = corpus_chunk_size + self.max_pairs = max_pairs + self.top_k = top_k + self.truncate_dim = truncate_dim + + self.duplicates = duplicates_dict if duplicates_dict is not None else defaultdict(lambda: defaultdict(bool)) + if duplicates_list is not None: + for id1, id2 in duplicates_list: + if id1 in sentences_map and id2 in sentences_map: + self.duplicates[id1][id2] = True + self.duplicates[id2][id1] = True + + # Add transitive closure + if add_transitive_closure: + self.duplicates = self.add_transitive_closure(self.duplicates) + + positive_key_pairs = set() + for key1 in self.duplicates: + for key2 in self.duplicates[key1]: + if ( + key1 in sentences_map + and key2 in sentences_map + and (self.duplicates[key1][key2] or self.duplicates[key2][key1]) + ): + positive_key_pairs.add(tuple(sorted([key1, key2]))) + + self.total_num_duplicates = len(positive_key_pairs) + + if name: + name = "_" + name + + self.csv_file: str = "paraphrase_mining_evaluation" + name + "_results.csv" + self.csv_headers = ["epoch", "steps", "precision", "recall", "f1", "threshold", "average_precision"] + self.write_csv = write_csv + self.primary_metric = "average_precision" + + def __call__( + self, model: SentenceTransformer, output_path: str | None = None, epoch: int = -1, steps: int = -1 + ) -> dict[str, float]: + if epoch != -1: + if steps == -1: + out_txt = f" after epoch {epoch}" + else: + out_txt = f" in epoch {epoch} after {steps} steps" + else: + out_txt = "" + if self.truncate_dim is not None: + out_txt += f" (truncated to {self.truncate_dim})" + + logger.info(f"Paraphrase Mining Evaluation of the model on the {self.name} dataset{out_txt}:") + + # Compute embedding for the sentences + pairs_list = paraphrase_mining( + model, + self.sentences, + show_progress_bar=self.show_progress_bar, + batch_size=self.batch_size, + query_chunk_size=self.query_chunk_size, + corpus_chunk_size=self.corpus_chunk_size, + max_pairs=self.max_pairs, + top_k=self.top_k, + truncate_dim=self.truncate_dim, + ) + + logger.info("Number of candidate pairs: " + str(len(pairs_list))) + + # Compute F1 score and Average Precision + n_extract = n_correct = 0 + threshold = 0 + best_f1 = best_recall = best_precision = 0 + + average_precision = 0 + + for idx in range(len(pairs_list)): + score, i, j = pairs_list[idx] + id1 = self.ids[i] + id2 = self.ids[j] + + # Compute optimal threshold and F1-score + n_extract += 1 + if self.duplicates[id1][id2] or self.duplicates[id2][id1]: + n_correct += 1 + precision = n_correct / n_extract + recall = n_correct / self.total_num_duplicates + f1 = 2 * precision * recall / (precision + recall) + average_precision += precision + if f1 > best_f1: + best_f1 = f1 + best_precision = precision + best_recall = recall + threshold = (pairs_list[idx][0] + pairs_list[min(idx + 1, len(pairs_list) - 1)][0]) / 2 + + average_precision = average_precision / self.total_num_duplicates + + logger.info(f"Average Precision: {average_precision * 100:.2f}") + logger.info(f"Optimal threshold: {threshold:.4f}") + logger.info(f"Precision: {best_precision * 100:.2f}") + logger.info(f"Recall: {best_recall * 100:.2f}") + logger.info(f"F1: {best_f1 * 100:.2f}\n") + + if output_path is not None and self.write_csv: + csv_path = os.path.join(output_path, self.csv_file) + if not os.path.isfile(csv_path): + with open(csv_path, newline="", mode="w", encoding="utf-8") as f: + writer = csv.writer(f) + writer.writerow(self.csv_headers) + writer.writerow([epoch, steps, best_precision, best_recall, best_f1, threshold, average_precision]) + else: + with open(csv_path, newline="", mode="a", encoding="utf-8") as f: + writer = csv.writer(f) + writer.writerow([epoch, steps, best_precision, best_recall, best_f1, threshold, average_precision]) + + metrics = { + "average_precision": average_precision, + "f1": best_f1, + "precision": best_precision, + "recall": best_recall, + "threshold": threshold, + } + metrics = self.prefix_name_to_metrics(metrics, self.name) + self.store_metrics_in_model_card_data(model, metrics, epoch, steps) + return metrics + + @staticmethod + def add_transitive_closure(graph): + nodes_visited = set() + for a in list(graph.keys()): + if a not in nodes_visited: + connected_subgraph_nodes = set() + connected_subgraph_nodes.add(a) + + # Add all nodes in the connected graph + neighbor_nodes_queue = list(graph[a]) + while len(neighbor_nodes_queue) > 0: + node = neighbor_nodes_queue.pop(0) + if node not in connected_subgraph_nodes: + connected_subgraph_nodes.add(node) + neighbor_nodes_queue.extend(graph[node]) + + # Ensure transitivity between all nodes in the graph + connected_subgraph_nodes = list(connected_subgraph_nodes) + for i in range(len(connected_subgraph_nodes) - 1): + for j in range(i + 1, len(connected_subgraph_nodes)): + graph[connected_subgraph_nodes[i]][connected_subgraph_nodes[j]] = True + graph[connected_subgraph_nodes[j]][connected_subgraph_nodes[i]] = True + + nodes_visited.add(connected_subgraph_nodes[i]) + nodes_visited.add(connected_subgraph_nodes[j]) + return graph + + def get_config_dict(self): + config_dict = { + "add_transitive_closure": self.add_transitive_closure, + "max_pairs": self.max_pairs, + "top_k": self.top_k, + } + if self.truncate_dim is not None: + config_dict["truncate_dim"] = self.truncate_dim + return config_dict diff --git a/sentence-transformers/sentence_transformers/evaluation/RerankingEvaluator.py b/sentence-transformers/sentence_transformers/evaluation/RerankingEvaluator.py new file mode 100644 index 0000000000000000000000000000000000000000..cc6ae8def96cab30bed2436209c84975085956c9 --- /dev/null +++ b/sentence-transformers/sentence_transformers/evaluation/RerankingEvaluator.py @@ -0,0 +1,371 @@ +from __future__ import annotations + +import csv +import logging +import os +from typing import TYPE_CHECKING, Callable + +import numpy as np +import torch +import tqdm +from sklearn.metrics import average_precision_score, ndcg_score + +from sentence_transformers.evaluation.SentenceEvaluator import SentenceEvaluator +from sentence_transformers.util import cos_sim + +if TYPE_CHECKING: + from torch import Tensor + + from sentence_transformers.SentenceTransformer import SentenceTransformer + + +logger = logging.getLogger(__name__) + + +class RerankingEvaluator(SentenceEvaluator): + """ + This class evaluates a SentenceTransformer model for the task of re-ranking. + + Given a query and a list of documents, it computes the score [query, doc_i] for all possible + documents and sorts them in decreasing order. Then, MRR@10, NDCG@10 and MAP is compute to measure the quality of the ranking. + + Args: + samples (list): A list of dictionaries, where each dictionary represents a sample and has the following keys: + + - 'query': The search query. + - 'positive': A list of positive (relevant) documents. + - 'negative': A list of negative (irrelevant) documents. + at_k (int, optional): Only consider the top k most similar documents to each query for the evaluation. Defaults to 10. + name (str, optional): Name of the evaluator. Defaults to "". + write_csv (bool, optional): Write results to CSV file. Defaults to True. + similarity_fct (Callable[[torch.Tensor, torch.Tensor], torch.Tensor], optional): Similarity function between sentence embeddings. By default, cosine similarity. Defaults to cos_sim. + batch_size (int, optional): Batch size to compute sentence embeddings. Defaults to 64. + show_progress_bar (bool, optional): Show progress bar when computing embeddings. Defaults to False. + use_batched_encoding (bool, optional): Whether or not to encode queries and documents in batches for greater speed, or 1-by-1 to save memory. Defaults to True. + truncate_dim (Optional[int], optional): The dimension to truncate sentence embeddings to. `None` uses the model's current truncation dimension. Defaults to None. + mrr_at_k (Optional[int], optional): Deprecated parameter. Please use `at_k` instead. Defaults to None. + + Example: + :: + + from sentence_transformers import SentenceTransformer + from sentence_transformers.evaluation import RerankingEvaluator + from datasets import load_dataset + + # Load a model + model = SentenceTransformer("all-MiniLM-L6-v2") + + # Load a dataset with queries, positives, and negatives + eval_dataset = load_dataset("microsoft/ms_marco", "v1.1", split="validation") + + samples = [ + { + "query": sample["query"], + "positive": [text for is_selected, text in zip(sample["passages"]["is_selected"], sample["passages"]["passage_text"]) if is_selected], + "negative": [text for is_selected, text in zip(sample["passages"]["is_selected"], sample["passages"]["passage_text"]) if not is_selected], + } + for sample in eval_dataset + ] + + # Initialize the evaluator + reranking_evaluator = RerankingEvaluator( + samples=samples, + name="ms-marco-dev", + ) + results = reranking_evaluator(model) + ''' + RerankingEvaluator: Evaluating the model on the ms-marco-dev dataset: + Queries: 9706 Positives: Min 1.0, Mean 1.1, Max 5.0 Negatives: Min 1.0, Mean 7.1, Max 9.0 + MAP: 56.07 + MRR@10: 56.70 + NDCG@10: 67.08 + ''' + print(reranking_evaluator.primary_metric) + # => ms-marco-dev_ndcg@10 + print(results[reranking_evaluator.primary_metric]) + # => 0.6708042171399308 + """ + + def __init__( + self, + samples: list[dict[str, str | list[str]]], + at_k: int = 10, + name: str = "", + write_csv: bool = True, + similarity_fct: Callable[[torch.Tensor, torch.Tensor], torch.Tensor] = cos_sim, + batch_size: int = 64, + show_progress_bar: bool = False, + use_batched_encoding: bool = True, + truncate_dim: int | None = None, + mrr_at_k: int | None = None, + ): + super().__init__() + self.samples = samples + self.name = name + + if mrr_at_k is not None: + logger.warning(f"The `mrr_at_k` parameter has been deprecated; please use `at_k={mrr_at_k}` instead.") + self.at_k = mrr_at_k + else: + self.at_k = at_k + + self.similarity_fct = similarity_fct + self.batch_size = batch_size + self.show_progress_bar = show_progress_bar + self.use_batched_encoding = use_batched_encoding + self.truncate_dim = truncate_dim + + if isinstance(self.samples, dict): + self.samples = list(self.samples.values()) + + ### Remove sample with empty positive / negative set + self.samples = [ + sample for sample in self.samples if len(sample["positive"]) > 0 and len(sample["negative"]) > 0 + ] + + self.csv_file = "RerankingEvaluator" + ("_" + name if name else "") + f"_results_@{self.at_k}.csv" + self.csv_headers = [ + "epoch", + "steps", + "MAP", + f"MRR@{self.at_k}", + f"NDCG@{self.at_k}", + ] + self.write_csv = write_csv + self.primary_metric = f"ndcg@{self.at_k}" + + def __call__( + self, model: SentenceTransformer, output_path: str | None = None, epoch: int = -1, steps: int = -1 + ) -> dict[str, float]: + """ + Evaluates the model on the dataset and returns the evaluation metrics. + + Args: + model (SentenceTransformer): The SentenceTransformer model to evaluate. + output_path (str, optional): The output path to write the results. Defaults to None. + epoch (int, optional): The current epoch number. Defaults to -1. + steps (int, optional): The current step number. Defaults to -1. + + Returns: + Dict[str, float]: A dictionary containing the evaluation metrics. + """ + if epoch != -1: + if steps == -1: + out_txt = f" after epoch {epoch}" + else: + out_txt = f" in epoch {epoch} after {steps} steps" + else: + out_txt = "" + if self.truncate_dim is not None: + out_txt += f" (truncated to {self.truncate_dim})" + + logger.info(f"RerankingEvaluator: Evaluating the model on the {self.name} dataset{out_txt}:") + + scores = self.compute_metrices(model) + mean_ap = scores["map"] + mean_mrr = scores["mrr"] + mean_ndcg = scores["ndcg"] + + #### Some stats about the dataset + num_positives = [len(sample["positive"]) for sample in self.samples] + num_negatives = [len(sample["negative"]) for sample in self.samples] + + logger.info( + f"Queries: {len(self.samples)} \t Positives: Min {np.min(num_positives):.1f}, Mean {np.mean(num_positives):.1f}, Max {np.max(num_positives):.1f} \t Negatives: Min {np.min(num_negatives):.1f}, Mean {np.mean(num_negatives):.1f}, Max {np.max(num_negatives):.1f}" + ) + logger.info(f"MAP: {mean_ap * 100:.2f}") + logger.info(f"MRR@{self.at_k}: {mean_mrr * 100:.2f}") + logger.info(f"NDCG@{self.at_k}: {mean_ndcg * 100:.2f}") + + #### Write results to disc + if output_path is not None and self.write_csv: + csv_path = os.path.join(output_path, self.csv_file) + output_file_exists = os.path.isfile(csv_path) + with open(csv_path, newline="", mode="a" if output_file_exists else "w", encoding="utf-8") as f: + writer = csv.writer(f) + if not output_file_exists: + writer.writerow(self.csv_headers) + + writer.writerow([epoch, steps, mean_ap, mean_mrr, mean_ndcg]) + + metrics = { + "map": mean_ap, + f"mrr@{self.at_k}": mean_mrr, + f"ndcg@{self.at_k}": mean_ndcg, + } + metrics = self.prefix_name_to_metrics(metrics, self.name) + self.store_metrics_in_model_card_data(model, metrics, epoch, steps) + return metrics + + def compute_metrices(self, model: SentenceTransformer): + """ + Computes the evaluation metrics for the given model. + + Args: + model (SentenceTransformer): The SentenceTransformer model to compute metrics for. + + Returns: + Dict[str, float]: A dictionary containing the evaluation metrics. + """ + return ( + self.compute_metrices_batched(model) + if self.use_batched_encoding + else self.compute_metrices_individual(model) + ) + + def compute_metrices_batched(self, model: SentenceTransformer): + """ + Computes the evaluation metrics in a batched way, by batching all queries and all documents together. + + Args: + model (SentenceTransformer): The SentenceTransformer model to compute metrics for. + + Returns: + Dict[str, float]: A dictionary containing the evaluation metrics. + """ + all_mrr_scores = [] + all_ndcg_scores = [] + all_ap_scores = [] + + all_query_embs = self.embed_inputs( + model, + [sample["query"] for sample in self.samples], + encode_fn_name="query", + show_progress_bar=self.show_progress_bar, + ) + + all_docs = [] + + for sample in self.samples: + all_docs.extend(sample["positive"]) + all_docs.extend(sample["negative"]) + + all_docs_embs = self.embed_inputs( + model, all_docs, encode_fn_name="document", show_progress_bar=self.show_progress_bar + ) + + # Compute scores + query_idx, docs_idx = 0, 0 + for instance in self.samples: + query_emb = all_query_embs[query_idx] + query_idx += 1 + + num_pos = len(instance["positive"]) + num_neg = len(instance["negative"]) + docs_emb = all_docs_embs[docs_idx : docs_idx + num_pos + num_neg] + docs_idx += num_pos + num_neg + + if num_pos == 0 or num_neg == 0: + continue + + pred_scores = self.similarity_fct(query_emb, docs_emb) + if len(pred_scores.shape) > 1: + pred_scores = pred_scores[0] + + pred_scores_argsort = torch.argsort(-pred_scores) # Sort in decreasing order + pred_scores = pred_scores.cpu().tolist() + + # Compute MRR score + is_relevant = [1] * num_pos + [0] * num_neg + mrr_score = 0 + for rank, index in enumerate(pred_scores_argsort[0 : self.at_k]): + if is_relevant[index]: + mrr_score = 1 / (rank + 1) + break + all_mrr_scores.append(mrr_score) + + # Compute NDCG score + all_ndcg_scores.append(ndcg_score([is_relevant], [pred_scores], k=self.at_k)) + + # Compute AP + all_ap_scores.append(average_precision_score(is_relevant, pred_scores)) + + mean_ap = np.mean(all_ap_scores) + mean_mrr = np.mean(all_mrr_scores) + mean_ndcg = np.mean(all_ndcg_scores) + + return {"map": mean_ap, "mrr": mean_mrr, "ndcg": mean_ndcg} + + def compute_metrices_individual(self, model: SentenceTransformer): + """ + Computes the evaluation metrics individually by embedding every (query, positive, negative) tuple individually. + + Args: + model (SentenceTransformer): The SentenceTransformer model to compute metrics for. + + Returns: + Dict[str, float]: A dictionary containing the evaluation metrics. + """ + all_mrr_scores = [] + all_ndcg_scores = [] + all_ap_scores = [] + + for instance in tqdm.tqdm(self.samples, disable=not self.show_progress_bar, desc="Samples"): + query = instance["query"] + positive = list(instance["positive"]) + negative = list(instance["negative"]) + + if len(positive) == 0 or len(negative) == 0: + continue + + docs = positive + negative + is_relevant = [1] * len(positive) + [0] * len(negative) + + query_emb = self.embed_inputs(model, [query], encode_fn_name="query", show_progress_bar=False) + docs_emb = self.embed_inputs(model, docs, encode_fn_name="document", show_progress_bar=False) + + pred_scores = self.similarity_fct(query_emb, docs_emb) + if len(pred_scores.shape) > 1: + pred_scores = pred_scores[0] + + pred_scores_argsort = torch.argsort(-pred_scores) # Sort in decreasing order + pred_scores = pred_scores.cpu().tolist() + + # Compute MRR score + mrr_score = 0 + for rank, index in enumerate(pred_scores_argsort[0 : self.at_k]): + if is_relevant[index]: + mrr_score = 1 / (rank + 1) + break + all_mrr_scores.append(mrr_score) + + # Compute NDCG score + all_ndcg_scores.append(ndcg_score([is_relevant], [pred_scores], k=self.at_k)) + + # Compute AP + all_ap_scores.append(average_precision_score(is_relevant, pred_scores)) + + mean_ap = np.mean(all_ap_scores) + mean_mrr = np.mean(all_mrr_scores) + mean_ndcg = np.mean(all_ndcg_scores) + + return {"map": mean_ap, "mrr": mean_mrr, "ndcg": mean_ndcg} + + def embed_inputs( + self, + model: SentenceTransformer, + sentences: str | list[str] | np.ndarray, + encode_fn_name: str | None = None, + show_progress_bar: bool | None = None, + **kwargs, + ) -> Tensor: + if encode_fn_name is None: + encode_fn = model.encode + elif encode_fn_name == "query": + encode_fn = model.encode_query + elif encode_fn_name == "document": + encode_fn = model.encode_document + return encode_fn( + sentences, + batch_size=self.batch_size, + show_progress_bar=show_progress_bar, + convert_to_tensor=True, + truncate_dim=self.truncate_dim, + **kwargs, + ) + + def get_config_dict(self): + config_dict = {"at_k": self.at_k} + if self.truncate_dim is not None: + config_dict["truncate_dim"] = self.truncate_dim + return config_dict diff --git a/sentence-transformers/sentence_transformers/evaluation/SentenceEvaluator.py b/sentence-transformers/sentence_transformers/evaluation/SentenceEvaluator.py new file mode 100644 index 0000000000000000000000000000000000000000..64d08e179a682ac032ce6f93f68ab48779f30416 --- /dev/null +++ b/sentence-transformers/sentence_transformers/evaluation/SentenceEvaluator.py @@ -0,0 +1,120 @@ +from __future__ import annotations + +import re +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + import numpy as np + from torch import Tensor + + from sentence_transformers.SentenceTransformer import SentenceTransformer + + +class SentenceEvaluator: + """ + Base class for all evaluators. Notably, this class introduces the ``greater_is_better`` and ``primary_metric`` + attributes. The former is a boolean indicating whether a higher evaluation score is better, which is used + for choosing the best checkpoint if ``load_best_model_at_end`` is set to ``True`` in the training arguments. + + The latter is a string indicating the primary metric for the evaluator. This has to be defined whenever + the evaluator returns a dictionary of metrics, and the primary metric is the key pointing to the primary + metric, i.e. the one that is used for model selection and/or logging. + + Extend this class and implement __call__ for custom evaluators. + """ + + def __init__(self): + self.greater_is_better = True + self.primary_metric = None + + def __call__( + self, model: SentenceTransformer, output_path: str | None = None, epoch: int = -1, steps: int = -1 + ) -> float | dict[str, float]: + """ + This is called during training to evaluate the model. + It returns a score for the evaluation with a higher score indicating a better result. + + Args: + model: the model to evaluate + output_path: path where predictions and metrics are written + to + epoch: the epoch where the evaluation takes place. This is + used for the file prefixes. If this is -1, then we + assume evaluation on test data. + steps: the steps in the current epoch at time of the + evaluation. This is used for the file prefixes. If this + is -1, then we assume evaluation at the end of the + epoch. + + Returns: + Either a score for the evaluation with a higher score + indicating a better result, or a dictionary with scores. If + the latter is chosen, then `evaluator.primary_metric` must + be defined + """ + pass + + def prefix_name_to_metrics(self, metrics: dict[str, float], name: str) -> dict[str, float]: + def maybe_to_float(value: Any) -> Any: + try: + return float(value) + except ValueError: + return value + + if not name: + return {key: maybe_to_float(value) for key, value in metrics.items()} + metrics = {name + "_" + key: maybe_to_float(value) for key, value in metrics.items()} + if hasattr(self, "primary_metric") and not self.primary_metric.startswith(name + "_"): + self.primary_metric = name + "_" + self.primary_metric + return metrics + + def store_metrics_in_model_card_data( + self, model: SentenceTransformer, metrics: dict[str, Any], epoch: int = 0, step: int = 0 + ) -> None: + model.model_card_data.set_evaluation_metrics(self, metrics, epoch, step) + + @property + def description(self) -> str: + """ + Returns a human-readable description of the evaluator: BinaryClassificationEvaluator -> Binary Classification + + 1. Replace "CE" prefix with "CrossEncoder" + 2. Remove "Evaluator" from the class name + 3. Add a space before every capital letter + """ + class_name = self.__class__.__name__ + + if class_name.startswith("CE"): + class_name = "CrossEncoder" + class_name[2:] + + try: + index = class_name.index("Evaluator") + class_name = class_name[:index] + except IndexError: + pass + + return re.sub(r"([a-z])([A-Z])", r"\g<1> \g<2>", class_name) + + def get_config_dict(self) -> dict[str, Any]: + """ + Return a dictionary with all meaningful configuration values of the evaluator to store in the model card. + """ + return {} + + def embed_inputs( + self, + model: SentenceTransformer, + sentences: str | list[str] | np.ndarray, + **kwargs, + ) -> list[Tensor] | np.ndarray | Tensor | dict[str, Tensor] | list[dict[str, Tensor]]: + """ + Call the encoder method of the model pass + + Args: + model (SentenceTransformer): Model we are evaluating + sentences (str | list[str] | np.ndarray): Text that we are embedding + + Returns: + list[Tensor] | np.ndarray | Tensor | dict[str, Tensor] | list[dict[str, Tensor]]: The associated embedding + """ + return model.encode(sentences, **kwargs) diff --git a/sentence-transformers/sentence_transformers/evaluation/SequentialEvaluator.py b/sentence-transformers/sentence_transformers/evaluation/SequentialEvaluator.py new file mode 100644 index 0000000000000000000000000000000000000000..fb80ad76f4256617139bd9d2fb9530246c188332 --- /dev/null +++ b/sentence-transformers/sentence_transformers/evaluation/SequentialEvaluator.py @@ -0,0 +1,61 @@ +from __future__ import annotations + +from collections.abc import Iterable +from typing import TYPE_CHECKING + +from sentence_transformers.evaluation.SentenceEvaluator import SentenceEvaluator + +if TYPE_CHECKING: + from sentence_transformers.SentenceTransformer import SentenceTransformer + + +class SequentialEvaluator(SentenceEvaluator): + """ + This evaluator allows that multiple sub-evaluators are passed. When the model is evaluated, + the data is passed sequentially to all sub-evaluators. + + All scores are passed to 'main_score_function', which derives one final score value + + Args: + evaluators (Iterable[SentenceEvaluator]): A collection of SentenceEvaluator objects. + main_score_function (function, optional): A function that takes a list of scores and returns the main score. + Defaults to selecting the last score in the list. + + Example: + :: + + evaluator1 = BinaryClassificationEvaluator(...) + evaluator2 = InformationRetrievalEvaluator(...) + evaluator3 = MSEEvaluator(...) + seq_evaluator = SequentialEvaluator([evaluator1, evaluator2, evaluator3]) + """ + + def __init__(self, evaluators: Iterable[SentenceEvaluator], main_score_function=lambda scores: scores[-1]): + super().__init__() + self.evaluators = evaluators + self.main_score_function = main_score_function + + def __call__( + self, model: SentenceTransformer, output_path: str | None = None, epoch: int = -1, steps: int = -1 + ) -> dict[str, float]: + evaluations = [] + scores = [] + for evaluator_idx, evaluator in enumerate(self.evaluators): + evaluation = evaluator(model, output_path, epoch, steps) + + if not isinstance(evaluation, dict): + scores.append(evaluation) + evaluation = {f"evaluator_{evaluator_idx}": evaluation} + else: + if hasattr(evaluator, "primary_metric"): + scores.append(evaluation[evaluator.primary_metric]) + else: + scores.append(evaluation[list(evaluation.keys())[0]]) + + evaluations.append(evaluation) + + self.primary_metric = "sequential_score" + main_score = self.main_score_function(scores) + results = {key: value for evaluation in evaluations for key, value in evaluation.items()} + results["sequential_score"] = main_score + return results diff --git a/sentence-transformers/sentence_transformers/evaluation/SimilarityFunction.py b/sentence-transformers/sentence_transformers/evaluation/SimilarityFunction.py new file mode 100644 index 0000000000000000000000000000000000000000..cabaa6cbd60e1871eee46bfe1413bf2e2e307982 --- /dev/null +++ b/sentence-transformers/sentence_transformers/evaluation/SimilarityFunction.py @@ -0,0 +1,5 @@ +from __future__ import annotations + +from sentence_transformers.similarity_functions import SimilarityFunction + +__all__ = ["SimilarityFunction"] diff --git a/sentence-transformers/sentence_transformers/evaluation/TranslationEvaluator.py b/sentence-transformers/sentence_transformers/evaluation/TranslationEvaluator.py new file mode 100644 index 0000000000000000000000000000000000000000..2e8d7d673a1b76f9a97c8fded20a0eae7d0c7f3b --- /dev/null +++ b/sentence-transformers/sentence_transformers/evaluation/TranslationEvaluator.py @@ -0,0 +1,191 @@ +from __future__ import annotations + +import csv +import logging +import os +from typing import TYPE_CHECKING + +import numpy as np +import torch + +from sentence_transformers.evaluation.SentenceEvaluator import SentenceEvaluator +from sentence_transformers.util import pytorch_cos_sim + +if TYPE_CHECKING: + from torch import Tensor + + from sentence_transformers.SentenceTransformer import SentenceTransformer + +logger = logging.getLogger(__name__) + + +class TranslationEvaluator(SentenceEvaluator): + """ + Given two sets of sentences in different languages, e.g. (en_1, en_2, en_3...) and (fr_1, fr_2, fr_3, ...), + and assuming that fr_i is the translation of en_i. + Checks if vec(en_i) has the highest similarity to vec(fr_i). Computes the accuracy in both directions + + The labels need to indicate the similarity between the sentences. + + Args: + source_sentences (List[str]): List of sentences in the source language. + target_sentences (List[str]): List of sentences in the target language. + show_progress_bar (bool): Whether to show a progress bar when computing embeddings. Defaults to False. + batch_size (int): The batch size to compute sentence embeddings. Defaults to 16. + name (str): The name of the evaluator. Defaults to an empty string. + print_wrong_matches (bool): Whether to print incorrect matches. Defaults to False. + write_csv (bool): Whether to write the evaluation results to a CSV file. Defaults to True. + truncate_dim (int, optional): The dimension to truncate sentence embeddings to. If None, the model's + current truncation dimension will be used. Defaults to None. + + Example: + :: + + from sentence_transformers import SentenceTransformer + from sentence_transformers.evaluation import TranslationEvaluator + from datasets import load_dataset + + # Load a model + model = SentenceTransformer('paraphrase-multilingual-mpnet-base-v2') + + # Load a parallel sentences dataset + dataset = load_dataset("sentence-transformers/parallel-sentences-news-commentary", "en-nl", split="train[:1000]") + + # Initialize the TranslationEvaluator using the same texts from two languages + translation_evaluator = TranslationEvaluator( + source_sentences=dataset["english"], + target_sentences=dataset["non_english"], + name="news-commentary-en-nl", + ) + results = translation_evaluator(model) + ''' + Evaluating translation matching Accuracy of the model on the news-commentary-en-nl dataset: + Accuracy src2trg: 90.80 + Accuracy trg2src: 90.40 + ''' + print(translation_evaluator.primary_metric) + # => "news-commentary-en-nl_mean_accuracy" + print(results[translation_evaluator.primary_metric]) + # => 0.906 + """ + + def __init__( + self, + source_sentences: list[str], + target_sentences: list[str], + show_progress_bar: bool = False, + batch_size: int = 16, + name: str = "", + print_wrong_matches: bool = False, + write_csv: bool = True, + truncate_dim: int | None = None, + ): + super().__init__() + self.source_sentences = source_sentences + self.target_sentences = target_sentences + self.name = name + self.batch_size = batch_size + self.show_progress_bar = show_progress_bar + self.print_wrong_matches = print_wrong_matches + self.truncate_dim = truncate_dim + + assert len(self.source_sentences) == len(self.target_sentences) + + if name: + name = "_" + name + + self.csv_file = "translation_evaluation" + name + "_results.csv" + self.csv_headers = ["epoch", "steps", "src2trg", "trg2src"] + self.write_csv = write_csv + self.primary_metric = "mean_accuracy" + + def __call__( + self, model: SentenceTransformer, output_path: str | None = None, epoch: int = -1, steps: int = -1 + ) -> dict[str, float]: + if epoch != -1: + if steps == -1: + out_txt = f" after epoch {epoch}" + else: + out_txt = f" in epoch {epoch} after {steps} steps" + else: + out_txt = "" + if self.truncate_dim is not None: + out_txt += f" (truncated to {self.truncate_dim})" + + logger.info(f"Evaluating translation matching Accuracy of the model on the {self.name} dataset{out_txt}:") + + embeddings1 = torch.stack(self.embed_inputs(model, self.source_sentences)) + embeddings2 = torch.stack(self.embed_inputs(model, self.target_sentences)) + + cos_sims = pytorch_cos_sim(embeddings1, embeddings2).detach().cpu().numpy() + + correct_src2trg = 0 + correct_trg2src = 0 + + for i in range(len(cos_sims)): + max_idx = np.argmax(cos_sims[i]) + + if i == max_idx: + correct_src2trg += 1 + elif self.print_wrong_matches: + print("\nIncorrect : Source", i, "is most similar to target", max_idx, "instead of target", i) + print("Source :", self.source_sentences[i]) + print("Pred Target:", self.target_sentences[max_idx], f"(Score: {cos_sims[i][max_idx]:.4f})") + print("True Target:", self.target_sentences[i], f"(Score: {cos_sims[i][i]:.4f})") + + results = enumerate(cos_sims[i]) + results = sorted(results, key=lambda x: x[1], reverse=True) + for idx, score in results[:5]: + print("\t", idx, f"(Score: {score:.4f})", self.target_sentences[idx]) + + cos_sims = cos_sims.T + for i in range(len(cos_sims)): + max_idx = np.argmax(cos_sims[i]) + if i == max_idx: + correct_trg2src += 1 + + acc_src2trg = correct_src2trg / len(cos_sims) + acc_trg2src = correct_trg2src / len(cos_sims) + + logger.info(f"Accuracy src2trg: {acc_src2trg * 100:.2f}") + logger.info(f"Accuracy trg2src: {acc_trg2src * 100:.2f}") + + if output_path is not None and self.write_csv: + csv_path = os.path.join(output_path, self.csv_file) + output_file_exists = os.path.isfile(csv_path) + with open(csv_path, newline="", mode="a" if output_file_exists else "w", encoding="utf-8") as f: + writer = csv.writer(f) + if not output_file_exists: + writer.writerow(self.csv_headers) + + writer.writerow([epoch, steps, acc_src2trg, acc_trg2src]) + + metrics = { + "src2trg_accuracy": acc_src2trg, + "trg2src_accuracy": acc_trg2src, + "mean_accuracy": (acc_src2trg + acc_trg2src) / 2, + } + metrics = self.prefix_name_to_metrics(metrics, self.name) + self.store_metrics_in_model_card_data(model, metrics, epoch, steps) + return metrics + + def embed_inputs( + self, + model: SentenceTransformer, + sentences: str | list[str] | np.ndarray, + **kwargs, + ) -> list[Tensor]: + return model.encode( + sentences, + batch_size=self.batch_size, + show_progress_bar=self.show_progress_bar, + convert_to_numpy=False, + truncate_dim=self.truncate_dim, + **kwargs, + ) + + def get_config_dict(self): + config_dict = {} + if self.truncate_dim is not None: + config_dict["truncate_dim"] = self.truncate_dim + return config_dict diff --git a/sentence-transformers/sentence_transformers/evaluation/TripletEvaluator.py b/sentence-transformers/sentence_transformers/evaluation/TripletEvaluator.py new file mode 100644 index 0000000000000000000000000000000000000000..1321162fc18602e82d445c2a9e176fe789717cd5 --- /dev/null +++ b/sentence-transformers/sentence_transformers/evaluation/TripletEvaluator.py @@ -0,0 +1,270 @@ +from __future__ import annotations + +import csv +import logging +import os +from typing import TYPE_CHECKING, Literal + +from sentence_transformers.evaluation.SentenceEvaluator import SentenceEvaluator +from sentence_transformers.readers import InputExample +from sentence_transformers.similarity_functions import SimilarityFunction +from sentence_transformers.util import ( + pairwise_cos_sim, + pairwise_dot_score, + pairwise_euclidean_sim, + pairwise_manhattan_sim, +) + +if TYPE_CHECKING: + import numpy as np + + from sentence_transformers.SentenceTransformer import SentenceTransformer + +logger = logging.getLogger(__name__) + + +class TripletEvaluator(SentenceEvaluator): + """ + Evaluate a model based on a triplet: (sentence, positive_example, negative_example). + Checks if ``similarity(sentence, positive_example) > similarity(sentence, negative_example) + margin``. + + Args: + anchors (List[str]): Sentences to check similarity to. (e.g. a query) + positives (List[str]): List of positive sentences + negatives (List[str]): List of negative sentences + main_similarity_function (Union[str, SimilarityFunction], optional): + The similarity function to use. If not specified, use cosine similarity, + dot product, Euclidean, and Manhattan similarity. Defaults to None. + margin (Union[float, Dict[str, float]], optional): Margins for various similarity metrics. + If a float is provided, it will be used as the margin for all similarity metrics. + If a dictionary is provided, the keys should be 'cosine', 'dot', 'manhattan', and 'euclidean'. + The value specifies the minimum margin by which the negative sample should be further from + the anchor than the positive sample. Defaults to None. + name (str): Name for the output. Defaults to "". + batch_size (int): Batch size used to compute embeddings. Defaults to 16. + show_progress_bar (bool): If true, prints a progress bar. Defaults to False. + write_csv (bool): Write results to a CSV file. Defaults to True. + truncate_dim (int, optional): The dimension to truncate sentence embeddings to. + `None` uses the model's current truncation dimension. Defaults to None. + similarity_fn_names (List[str], optional): List of similarity function names to evaluate. + If not specified, evaluate using the ``model.similarity_fn_name``. + Defaults to None. + + Example: + :: + + from sentence_transformers import SentenceTransformer + from sentence_transformers.evaluation import TripletEvaluator + from datasets import load_dataset + + # Load a model + model = SentenceTransformer('all-mpnet-base-v2') + + # Load a dataset with (anchor, positive, negative) triplets + dataset = load_dataset("sentence-transformers/all-nli", "triplet", split="dev") + + # Initialize the TripletEvaluator using anchors, positives, and negatives + triplet_evaluator = TripletEvaluator( + anchors=dataset[:1000]["anchor"], + positives=dataset[:1000]["positive"], + negatives=dataset[:1000]["negative"], + name="all_nli_dev", + ) + results = triplet_evaluator(model) + ''' + TripletEvaluator: Evaluating the model on the all-nli-dev dataset: + Accuracy Cosine Similarity: 95.60% + ''' + print(triplet_evaluator.primary_metric) + # => "all_nli_dev_cosine_accuracy" + print(results[triplet_evaluator.primary_metric]) + # => 0.956 + """ + + def __init__( + self, + anchors: list[str], + positives: list[str], + negatives: list[str], + main_similarity_function: str | SimilarityFunction | None = None, + margin: float | dict[str, float] | None = None, + name: str = "", + batch_size: int = 16, + show_progress_bar: bool = False, + write_csv: bool = True, + truncate_dim: int | None = None, + similarity_fn_names: list[Literal["cosine", "dot", "euclidean", "manhattan"]] | None = None, + main_distance_function: str | SimilarityFunction | None = "deprecated", + ): + super().__init__() + self.anchors = anchors + self.positives = positives + self.negatives = negatives + self.name = name + self.truncate_dim = truncate_dim + + assert len(self.anchors) == len(self.positives) + assert len(self.anchors) == len(self.negatives) + + if main_distance_function != "deprecated" and main_similarity_function is None: + main_similarity_function = main_distance_function + logger.warning( + "The 'main_distance_function' parameter is deprecated. Please use 'main_similarity_function' instead. " + "'main_distance_function' will be removed in a future release." + ) + + self.main_similarity_function = ( + SimilarityFunction(main_similarity_function) if main_similarity_function else None + ) + self.similarity_fn_names = similarity_fn_names or [] + + if margin is None: + self.margin = {"cosine": 0, "dot": 0, "manhattan": 0, "euclidean": 0} + elif isinstance(margin, (float, int)): + self.margin = {"cosine": margin, "dot": margin, "manhattan": margin, "euclidean": margin} + elif isinstance(margin, dict): + self.margin = { + **{"cosine": 0, "dot": 0, "manhattan": 0, "euclidean": 0}, + **margin, + } + else: + raise ValueError( + "`margin` should be a float or a dictionary with keys 'cosine', 'dot', 'manhattan', and 'euclidean'" + ) + + self.batch_size = batch_size + if show_progress_bar is None: + show_progress_bar = ( + logger.getEffectiveLevel() == logging.INFO or logger.getEffectiveLevel() == logging.DEBUG + ) + self.show_progress_bar = show_progress_bar + + self.csv_file: str = "triplet_evaluation" + ("_" + name if name else "") + "_results.csv" + self.csv_headers = ["epoch", "steps"] + self.write_csv = write_csv + + self._append_csv_headers(self.similarity_fn_names) + + def _append_csv_headers(self, similarity_fn_names): + for fn_name in similarity_fn_names: + self.csv_headers.append(f"accuracy_{fn_name}") + + @classmethod + def from_input_examples(cls, examples: list[InputExample], **kwargs): + anchors = [] + positives = [] + negatives = [] + + for example in examples: + anchors.append(example.texts[0]) + positives.append(example.texts[1]) + negatives.append(example.texts[2]) + return cls(anchors, positives, negatives, **kwargs) + + def __call__( + self, model: SentenceTransformer, output_path: str | None = None, epoch: int = -1, steps: int = -1 + ) -> dict[str, float]: + if epoch != -1: + if steps == -1: + out_txt = f" after epoch {epoch}" + else: + out_txt = f" in epoch {epoch} after {steps} steps" + else: + out_txt = "" + if self.truncate_dim is not None: + out_txt += f" (truncated to {self.truncate_dim})" + + logger.info(f"TripletEvaluator: Evaluating the model on the {self.name} dataset{out_txt}:") + + embeddings_anchors = self.embed_inputs(model, self.anchors) + embeddings_positives = self.embed_inputs(model, self.positives) + embeddings_negatives = self.embed_inputs(model, self.negatives) + + if not self.similarity_fn_names: + self.similarity_fn_names = [model.similarity_fn_name] + self._append_csv_headers(self.similarity_fn_names) + + similarity_functions = { + "cosine": lambda anchors, positives, negatives: ( + pairwise_cos_sim(anchors, positives), + pairwise_cos_sim(anchors, negatives), + ), + "dot": lambda anchors, positives, negatives: ( + pairwise_dot_score(anchors, positives), + pairwise_dot_score(anchors, negatives), + ), + "manhattan": lambda anchors, positives, negatives: ( + pairwise_manhattan_sim(anchors, positives), + pairwise_manhattan_sim(anchors, negatives), + ), + "euclidean": lambda anchors, positives, negatives: ( + pairwise_euclidean_sim(anchors, positives), + pairwise_euclidean_sim(anchors, negatives), + ), + } + + metrics = {} + for fn_name in self.similarity_fn_names: + if fn_name in similarity_functions: + positive_scores, negative_scores = similarity_functions[fn_name]( + embeddings_anchors, embeddings_positives, embeddings_negatives + ) + accuracy = (positive_scores > negative_scores + self.margin[fn_name]).float().mean().item() + metrics[f"{fn_name}_accuracy"] = accuracy + logger.info(f"Accuracy {fn_name.capitalize()} Similarity:\t{accuracy:.2%}") + + if output_path is not None and self.write_csv: + csv_path = os.path.join(output_path, self.csv_file) + if not os.path.isfile(csv_path): + with open(csv_path, newline="", mode="w", encoding="utf-8") as f: + writer = csv.writer(f) + writer.writerow(self.csv_headers) + writer.writerow([epoch, steps] + list(metrics.values())) + + else: + with open(csv_path, newline="", mode="a", encoding="utf-8") as f: + writer = csv.writer(f) + writer.writerow([epoch, steps] + list(metrics.values())) + + if len(self.similarity_fn_names) > 1: + metrics["max_accuracy"] = max(metrics.values()) + + if self.main_similarity_function: + self.primary_metric = { + SimilarityFunction.COSINE: "cosine_accuracy", + SimilarityFunction.DOT_PRODUCT: "dot_accuracy", + SimilarityFunction.EUCLIDEAN: "euclidean_accuracy", + SimilarityFunction.MANHATTAN: "manhattan_accuracy", + }.get(self.main_similarity_function) + else: + if len(self.similarity_fn_names) > 1: + self.primary_metric = "max_accuracy" + else: + self.primary_metric = f"{self.similarity_fn_names[0]}_accuracy" + + metrics = self.prefix_name_to_metrics(metrics, self.name) + self.store_metrics_in_model_card_data(model, metrics, epoch, steps) + return metrics + + def embed_inputs( + self, + model: SentenceTransformer, + sentences: str | list[str] | np.ndarray, + **kwargs, + ) -> np.ndarray: + return model.encode( + sentences, + batch_size=self.batch_size, + show_progress_bar=self.show_progress_bar, + convert_to_numpy=True, + truncate_dim=self.truncate_dim, + **kwargs, + ) + + def get_config_dict(self): + config_dict = {} + if self.margin != {"cosine": 0, "dot": 0, "manhattan": 0, "euclidean": 0}: + config_dict["margin"] = self.margin + if self.truncate_dim is not None: + config_dict["truncate_dim"] = self.truncate_dim + return config_dict diff --git a/sentence-transformers/sentence_transformers/evaluation/__init__.py b/sentence-transformers/sentence_transformers/evaluation/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..821c9c4a3c7a5ca416481e070904d62c11bb54e3 --- /dev/null +++ b/sentence-transformers/sentence_transformers/evaluation/__init__.py @@ -0,0 +1,33 @@ +from __future__ import annotations + +from .BinaryClassificationEvaluator import BinaryClassificationEvaluator +from .EmbeddingSimilarityEvaluator import EmbeddingSimilarityEvaluator +from .InformationRetrievalEvaluator import InformationRetrievalEvaluator +from .LabelAccuracyEvaluator import LabelAccuracyEvaluator +from .MSEEvaluator import MSEEvaluator +from .MSEEvaluatorFromDataFrame import MSEEvaluatorFromDataFrame +from .NanoBEIREvaluator import NanoBEIREvaluator +from .ParaphraseMiningEvaluator import ParaphraseMiningEvaluator +from .RerankingEvaluator import RerankingEvaluator +from .SentenceEvaluator import SentenceEvaluator +from .SequentialEvaluator import SequentialEvaluator +from .SimilarityFunction import SimilarityFunction +from .TranslationEvaluator import TranslationEvaluator +from .TripletEvaluator import TripletEvaluator + +__all__ = [ + "SentenceEvaluator", + "SimilarityFunction", + "BinaryClassificationEvaluator", + "EmbeddingSimilarityEvaluator", + "InformationRetrievalEvaluator", + "LabelAccuracyEvaluator", + "MSEEvaluator", + "MSEEvaluatorFromDataFrame", + "ParaphraseMiningEvaluator", + "SequentialEvaluator", + "TranslationEvaluator", + "TripletEvaluator", + "RerankingEvaluator", + "NanoBEIREvaluator", +] diff --git a/sentence-transformers/sentence_transformers/evaluation/__pycache__/BinaryClassificationEvaluator.cpython-312.pyc b/sentence-transformers/sentence_transformers/evaluation/__pycache__/BinaryClassificationEvaluator.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..682c8cc59e1a68f478817e2a6db3eec693724b58 Binary files /dev/null and b/sentence-transformers/sentence_transformers/evaluation/__pycache__/BinaryClassificationEvaluator.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/evaluation/__pycache__/EmbeddingSimilarityEvaluator.cpython-312.pyc b/sentence-transformers/sentence_transformers/evaluation/__pycache__/EmbeddingSimilarityEvaluator.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7b851afdb209f35ede89c934c8e813e3127a4a34 Binary files /dev/null and b/sentence-transformers/sentence_transformers/evaluation/__pycache__/EmbeddingSimilarityEvaluator.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/evaluation/__pycache__/InformationRetrievalEvaluator.cpython-312.pyc b/sentence-transformers/sentence_transformers/evaluation/__pycache__/InformationRetrievalEvaluator.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..79c0ce3142ec558823188ebb9c9b6cee376af3fc Binary files /dev/null and b/sentence-transformers/sentence_transformers/evaluation/__pycache__/InformationRetrievalEvaluator.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/evaluation/__pycache__/LabelAccuracyEvaluator.cpython-312.pyc b/sentence-transformers/sentence_transformers/evaluation/__pycache__/LabelAccuracyEvaluator.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d8a3a2744b73834eb1da4f3c2c37d2e862e25a6 Binary files /dev/null and b/sentence-transformers/sentence_transformers/evaluation/__pycache__/LabelAccuracyEvaluator.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/evaluation/__pycache__/MSEEvaluator.cpython-312.pyc b/sentence-transformers/sentence_transformers/evaluation/__pycache__/MSEEvaluator.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1bbde73ff26d5e4e55ae45cb6627bc1cec39cd4 Binary files /dev/null and b/sentence-transformers/sentence_transformers/evaluation/__pycache__/MSEEvaluator.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/evaluation/__pycache__/MSEEvaluatorFromDataFrame.cpython-312.pyc b/sentence-transformers/sentence_transformers/evaluation/__pycache__/MSEEvaluatorFromDataFrame.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b0c5fb8751c31b4e68f725917baf5031533d043e Binary files /dev/null and b/sentence-transformers/sentence_transformers/evaluation/__pycache__/MSEEvaluatorFromDataFrame.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/evaluation/__pycache__/NanoBEIREvaluator.cpython-312.pyc b/sentence-transformers/sentence_transformers/evaluation/__pycache__/NanoBEIREvaluator.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cb2c650657b7f5791b3ce57afa0de6879ac1da7c Binary files /dev/null and b/sentence-transformers/sentence_transformers/evaluation/__pycache__/NanoBEIREvaluator.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/evaluation/__pycache__/ParaphraseMiningEvaluator.cpython-312.pyc b/sentence-transformers/sentence_transformers/evaluation/__pycache__/ParaphraseMiningEvaluator.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f9b29f95f0c0754e5ae367e5dd170ac2bf7677f2 Binary files /dev/null and b/sentence-transformers/sentence_transformers/evaluation/__pycache__/ParaphraseMiningEvaluator.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/evaluation/__pycache__/RerankingEvaluator.cpython-312.pyc b/sentence-transformers/sentence_transformers/evaluation/__pycache__/RerankingEvaluator.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..655b3a27b5e2cf187cfb777343c0922fceb7a209 Binary files /dev/null and b/sentence-transformers/sentence_transformers/evaluation/__pycache__/RerankingEvaluator.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/evaluation/__pycache__/SentenceEvaluator.cpython-312.pyc b/sentence-transformers/sentence_transformers/evaluation/__pycache__/SentenceEvaluator.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..888cf575bc1da4fd7880e64421f865c49da20389 Binary files /dev/null and b/sentence-transformers/sentence_transformers/evaluation/__pycache__/SentenceEvaluator.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/evaluation/__pycache__/SequentialEvaluator.cpython-312.pyc b/sentence-transformers/sentence_transformers/evaluation/__pycache__/SequentialEvaluator.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f04167a79451b47955d8ad21dc838b1452165628 Binary files /dev/null and b/sentence-transformers/sentence_transformers/evaluation/__pycache__/SequentialEvaluator.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/evaluation/__pycache__/SimilarityFunction.cpython-312.pyc b/sentence-transformers/sentence_transformers/evaluation/__pycache__/SimilarityFunction.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ff1f886c9afee4819419ec634682ff6755550476 Binary files /dev/null and b/sentence-transformers/sentence_transformers/evaluation/__pycache__/SimilarityFunction.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/evaluation/__pycache__/TranslationEvaluator.cpython-312.pyc b/sentence-transformers/sentence_transformers/evaluation/__pycache__/TranslationEvaluator.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2c702ff5fb76e3f6c4faf9616c07a1bcdef6fad5 Binary files /dev/null and b/sentence-transformers/sentence_transformers/evaluation/__pycache__/TranslationEvaluator.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/evaluation/__pycache__/TripletEvaluator.cpython-312.pyc b/sentence-transformers/sentence_transformers/evaluation/__pycache__/TripletEvaluator.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b7c5ab93e4bd784a33866a0e41b92cad2196ac1 Binary files /dev/null and b/sentence-transformers/sentence_transformers/evaluation/__pycache__/TripletEvaluator.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/evaluation/__pycache__/__init__.cpython-312.pyc b/sentence-transformers/sentence_transformers/evaluation/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d42316c769d150660c8a0021a109cc15c2fa6b57 Binary files /dev/null and b/sentence-transformers/sentence_transformers/evaluation/__pycache__/__init__.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/AdaptiveLayerLoss.py b/sentence-transformers/sentence_transformers/losses/AdaptiveLayerLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..85fe4bdf7b6fdf0a729fea7f0303ca87e919faf3 --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/AdaptiveLayerLoss.py @@ -0,0 +1,273 @@ +from __future__ import annotations + +import random +import warnings +from collections.abc import Iterable +from typing import Any + +import torch +from torch import Tensor, nn +from torch.nn import functional as F + +from sentence_transformers import SentenceTransformer +from sentence_transformers.losses.CachedGISTEmbedLoss import CachedGISTEmbedLoss +from sentence_transformers.losses.CachedMultipleNegativesRankingLoss import CachedMultipleNegativesRankingLoss +from sentence_transformers.losses.CachedMultipleNegativesSymmetricRankingLoss import ( + CachedMultipleNegativesSymmetricRankingLoss, +) +from sentence_transformers.models import Transformer + + +class TransformerDecorator: + """ + Decorator that caches the embeddings of all layers of the transformer. + When `layer_idx` is set, it returns the cached embeddings of that layer instead. + + This is meant to override the forward function of the Transformer. + """ + + def __init__(self, transformer: Transformer, original_forward) -> None: + self.transformer = transformer + self.original_forward = original_forward + self.embeddings: list[tuple[Tensor]] = [] + self.last_embeddings: list[Tensor] = [] + self.features: list[dict[str, Tensor]] = [] + self.layer_idx = None + self.call_idx = 0 + + def set_layer_idx(self, layer_idx) -> None: + self.layer_idx = layer_idx + self.call_idx = 0 + + def get_layer_embeddings(self) -> Tensor: + return torch.concat([embedding[self.layer_idx] for embedding in self.embeddings], dim=1) + + def __call__(self, features) -> dict[str, Tensor]: + if self.layer_idx is None: + output = self.call_grow_cache(features) + else: + output = self.call_use_cache(features) + self.call_idx += 1 + return output + + def call_grow_cache(self, features: dict[str, Tensor]) -> dict[str, Tensor]: + """ + Temporarily sets the output_hidden_states to True, runs the model, and then restores the original setting. + Use the all_layer_embeddings to get the embeddings of all layers. + """ + original_output_hidden_states = self.transformer.auto_model.config.output_hidden_states + self.transformer.auto_model.config.output_hidden_states = True + + output = self.original_forward(features) + # We ignore the first layer, as it is the input embeddings + # and the last layer, as we already computed the loss over it + self.num_layers = len(output["all_layer_embeddings"]) - 1 + self.embeddings.append(output["all_layer_embeddings"][1:-1]) + self.last_embeddings.append(output["token_embeddings"]) + self.features.append( + {key: value for key, value in output.items() if key not in ["all_layer_embeddings", "token_embeddings"]} + ) + + # Restore original setting + self.transformer.auto_model.config.output_hidden_states = original_output_hidden_states + + if original_output_hidden_states: + del output["all_layer_embeddings"] + + return output + + def call_use_cache(self, features: dict[str, Tensor]) -> dict[str, Tensor]: + return {**self.features[self.call_idx], "token_embeddings": self.embeddings[self.call_idx][self.layer_idx]} + + +class ForwardDecorator: + """ + Decorator that caches the embeddings after all modules (e.g. pooling) of the model. + Required to get the embeddings after all modules for the KL-divergence loss. + + This is meant to override the forward function of the SentenceTransformer. + """ + + def __init__(self, fn) -> None: + self.fn = fn + self.embeddings = [] + + def __call__(self, features: dict[str, Tensor]) -> dict[str, Tensor]: + output = self.fn(features) + self.embeddings.append(output["sentence_embedding"]) + return output + + def get_embeddings(self) -> Tensor: + embeddings = torch.concat(self.embeddings, dim=0) + self.embeddings = [] + return embeddings + + +class AdaptiveLayerLoss(nn.Module): + def __init__( + self, + model: SentenceTransformer, + loss: nn.Module, + n_layers_per_step: int = 1, + last_layer_weight: float = 1.0, + prior_layers_weight: float = 1.0, + kl_div_weight: float = 1.0, + kl_temperature: float = 0.3, + ) -> None: + """ + The AdaptiveLayerLoss can be seen as a loss *modifier* that allows you to use other loss functions at non-final + layers of the Sentence Transformer model. This is useful for when you want to train a model where users have + the option to lower the number of layers used to improve their inference speed and memory usage. + + Args: + model: SentenceTransformer model + loss: The loss function to be used, e.g. + :class:`MultipleNegativesRankingLoss`, + :class:`CoSENTLoss`, etc. + n_layers_per_step: The number of layers to use per step. If + -1, then all layers are used. If > 0, then a random + sample of `n_layers_per_step` layers are used per step, + separate from the final layer, which is always used. The + 2DMSE paper uses `n_layers_per_step=1`. The default + value is 1. + last_layer_weight: The weight to use for the loss of the + final layer. Increase this to focus more on the + performance when using all layers. The default value is + 1.0. + prior_layers_weight: The weight to use for the loss of the + prior layers. Increase this to focus more on the + performance when using fewer layers. The default value + is 1.0. + kl_div_weight: The weight to use for the KL-divergence loss + that is used to make the prior layers match that of the + last layer. Increase this to focus more on the + performance when using fewer layers. The default value + is 1.0. + kl_temperature: The temperature to use for the KL-divergence + loss. If 0, then the KL-divergence loss is not used. The + default value is 1.0. + + References: + - The concept was inspired by the 2DMSE paper: https://arxiv.org/abs/2402.14776 + - `Adaptive Layers <../../../examples/sentence_transformer/training/adaptive_layer/README.html>`_ + + Requirements: + 1. The base loss cannot be :class:`CachedMultipleNegativesRankingLoss`, + :class:`CachedMultipleNegativesSymmetricRankingLoss`, or :class:`CachedGISTEmbedLoss`. + + Inputs: + +---------------------------------------+--------+ + | Texts | Labels | + +=======================================+========+ + | any | any | + +---------------------------------------+--------+ + + Relations: + - :class:`Matryoshka2dLoss` uses this loss in combination with :class:`MatryoshkaLoss` which allows for + output dimensionality reduction for faster downstream tasks (e.g. retrieval). + + Example: + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + model = SentenceTransformer("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "anchor": ["It's nice weather outside today.", "He drove to work."], + "positive": ["It's so sunny.", "He took the car to the office."], + }) + loss = losses.MultipleNegativesRankingLoss(model=model) + loss = losses.AdaptiveLayerLoss(model, loss) + + trainer = SentenceTransformerTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.model = model + self.loss = loss + self.n_layers_per_step = n_layers_per_step + self.last_layer_weight = last_layer_weight + self.prior_layers_weight = prior_layers_weight + self.kl_div_weight = kl_div_weight + self.kl_temperature = kl_temperature + assert isinstance(self.model[0], Transformer) + if isinstance( + loss, + (CachedMultipleNegativesRankingLoss, CachedMultipleNegativesSymmetricRankingLoss, CachedGISTEmbedLoss), + ): + warnings.warn(f"MatryoshkaLoss is not compatible with {loss.__class__.__name__}.", stacklevel=2) + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + # Decorate the forward function of the transformer to cache the embeddings of all layers + original_transformer_forward = self.model[0].forward + transformer_decorator = TransformerDecorator(self.model[0], original_transformer_forward) + self.model[0].forward = transformer_decorator + + # Decorate the forward function of the model to get the embeddings after all modules (e.g. pooling) + original_forward = self.model.forward + forward_decorator = ForwardDecorator(original_forward) + self.model.forward = forward_decorator + + # Run the loss normally: i.e. the final layer, but 1) use the transformers decorator to cache + # the embeddings of all layers and 2) use the forward decorator to get the embeddings after all modules + # for the KL-divergence loss + loss = self.loss(sentence_features, labels) * self.last_layer_weight + if self.kl_temperature > 0: + final_embeddings = forward_decorator.get_embeddings() + final_embeddings = F.softmax(final_embeddings / self.kl_temperature, dim=-1) + + num_layers = transformer_decorator.num_layers + layer_indices = range(num_layers - 1) + if self.n_layers_per_step > 0 and self.n_layers_per_step < num_layers - 1: + layer_indices = random.sample(layer_indices, self.n_layers_per_step) + + # This loop is over `num_layer - 1` layers because we already computed the loss over the final layer + for layer_idx in layer_indices: + # Add regular loss for each layer by using the cached embeddings of that layer + transformer_decorator.set_layer_idx(layer_idx) + layer_loss = self.loss(sentence_features, labels) + loss = loss + layer_loss / (1 + layer_idx) / len(layer_indices) * self.prior_layers_weight + + # and KL-divergence loss between the current layer and the final layer + # Note: we use "batchmean" reduction as that aligns with the mathematical definition + if self.kl_temperature > 0: + embeddings = forward_decorator.get_embeddings() + kl_div_loss = F.kl_div( + F.log_softmax(embeddings / self.kl_temperature, dim=-1), + final_embeddings, + reduction="batchmean", + ) + loss = loss + kl_div_loss * self.kl_temperature * self.kl_div_weight + + self.model[0].forward = original_transformer_forward + self.model.forward = original_forward + + return loss + + def get_config_dict(self) -> dict[str, Any]: + return { + "loss": self.loss.__class__.__name__, + "n_layers_per_step": self.n_layers_per_step, + "last_layer_weight": self.last_layer_weight, + "prior_layers_weight": self.prior_layers_weight, + "kl_div_weight": self.kl_div_weight, + "kl_temperature": self.kl_temperature, + } + + @property + def citation(self) -> str: + return """ +@misc{li20242d, + title={2D Matryoshka Sentence Embeddings}, + author={Xianming Li and Zongxi Li and Jing Li and Haoran Xie and Qing Li}, + year={2024}, + eprint={2402.14776}, + archivePrefix={arXiv}, + primaryClass={cs.CL} +} +""" diff --git a/sentence-transformers/sentence_transformers/losses/AnglELoss.py b/sentence-transformers/sentence_transformers/losses/AnglELoss.py new file mode 100644 index 0000000000000000000000000000000000000000..d7ff5f8521707ed1388f722335fbd209c6d9cbc8 --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/AnglELoss.py @@ -0,0 +1,81 @@ +from __future__ import annotations + +from sentence_transformers import SentenceTransformer, losses, util + + +class AnglELoss(losses.CoSENTLoss): + def __init__(self, model: SentenceTransformer, scale: float = 20.0) -> None: + """ + This class implements AnglE (Angle Optimized) loss. + This is a modification of :class:`CoSENTLoss`, designed to address the following issue: + The cosine function's gradient approaches 0 as the wave approaches the top or bottom of its form. + This can hinder the optimization process, so AnglE proposes to instead optimize the angle difference + in complex space in order to mitigate this effect. + + It expects that each of the InputExamples consists of a pair of texts and a float valued label, representing + the expected similarity score between the pair. + + It computes the following loss function: + + ``loss = logsum(1+exp(s(k,l)-s(i,j))+exp...)``, where ``(i,j)`` and ``(k,l)`` are any of the input pairs in the + batch such that the expected similarity of ``(i,j)`` is greater than ``(k,l)``. The summation is over all possible + pairs of input pairs in the batch that match this condition. This is the same as CoSENTLoss, with a different + similarity function. + + Args: + model: SentenceTransformerModel + scale: Output of similarity function is multiplied by scale + value. Represents the inverse temperature. + + References: + - For further details, see: https://arxiv.org/abs/2309.12871v1 + + Requirements: + - Sentence pairs with corresponding similarity scores in range of the similarity function. Default is [-1,1]. + + Inputs: + +--------------------------------+------------------------+ + | Texts | Labels | + +================================+========================+ + | (sentence_A, sentence_B) pairs | float similarity score | + +--------------------------------+------------------------+ + + Relations: + - :class:`CoSENTLoss` is AnglELoss with ``pairwise_cos_sim`` as the metric, rather than ``pairwise_angle_sim``. + - :class:`CosineSimilarityLoss` seems to produce a weaker training signal than ``CoSENTLoss`` or ``AnglELoss``. + + Example: + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + model = SentenceTransformer("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "sentence1": ["It's nice weather outside today.", "He drove to work."], + "sentence2": ["It's so sunny.", "She walked to the store."], + "score": [1.0, 0.3], + }) + loss = losses.AnglELoss(model) + + trainer = SentenceTransformerTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__(model, scale, similarity_fct=util.pairwise_angle_sim) + + @property + def citation(self) -> str: + return """ +@misc{li2023angleoptimized, + title={AnglE-optimized Text Embeddings}, + author={Xianming Li and Jing Li}, + year={2023}, + eprint={2309.12871}, + archivePrefix={arXiv}, + primaryClass={cs.CL} +} +""" diff --git a/sentence-transformers/sentence_transformers/losses/BatchAllTripletLoss.py b/sentence-transformers/sentence_transformers/losses/BatchAllTripletLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..0a402d40d5039086420681417a18f9a9d21ea2a3 --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/BatchAllTripletLoss.py @@ -0,0 +1,150 @@ +from __future__ import annotations + +from collections.abc import Iterable + +from torch import Tensor, nn + +from sentence_transformers.SentenceTransformer import SentenceTransformer + +from .BatchHardTripletLoss import BatchHardTripletLoss, BatchHardTripletLossDistanceFunction + + +class BatchAllTripletLoss(nn.Module): + def __init__( + self, + model: SentenceTransformer, + distance_metric=BatchHardTripletLossDistanceFunction.eucledian_distance, + margin: float = 5, + ) -> None: + """ + BatchAllTripletLoss takes a batch with (sentence, label) pairs and computes the loss for all possible, valid + triplets, i.e., anchor and positive must have the same label, anchor and negative a different label. The labels + must be integers, with same label indicating sentences from the same class. Your train dataset + must contain at least 2 examples per label class. + + Args: + model: SentenceTransformer model + distance_metric: Function that returns a distance between + two embeddings. The class SiameseDistanceMetric contains + pre-defined metrics that can be used. + margin: Negative samples should be at least margin further + apart from the anchor than the positive. + + References: + * Source: https://github.com/NegatioN/OnlineMiningTripletLoss/blob/master/online_triplet_loss/losses.py + * Paper: In Defense of the Triplet Loss for Person Re-Identification, https://arxiv.org/abs/1703.07737 + * Blog post: https://omoindrot.github.io/triplet-loss + + Requirements: + 1. Each sentence must be labeled with a class. + 2. Your dataset must contain at least 2 examples per labels class. + + Inputs: + +------------------+--------+ + | Texts | Labels | + +==================+========+ + | single sentences | class | + +------------------+--------+ + + Recommendations: + - Use ``BatchSamplers.GROUP_BY_LABEL`` (:class:`docs `) to + ensure that each batch contains 2+ examples per label class. + + Relations: + * :class:`BatchHardTripletLoss` uses only the hardest positive and negative samples, rather than all possible, valid triplets. + * :class:`BatchHardSoftMarginTripletLoss` uses only the hardest positive and negative samples, rather than all possible, valid triplets. + Also, it does not require setting a margin. + * :class:`BatchSemiHardTripletLoss` uses only semi-hard triplets, valid triplets, rather than all possible, valid triplets. + + Example: + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + model = SentenceTransformer("microsoft/mpnet-base") + # E.g. 0: sports, 1: economy, 2: politics + train_dataset = Dataset.from_dict({ + "sentence": [ + "He played a great game.", + "The stock is up 20%", + "They won 2-1.", + "The last goal was amazing.", + "They all voted against the bill.", + ], + "label": [0, 1, 0, 0, 2], + }) + loss = losses.BatchAllTripletLoss(model) + + trainer = SentenceTransformerTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + + """ + super().__init__() + self.sentence_embedder = model + self.triplet_margin = margin + self.distance_metric = distance_metric + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + rep = self.sentence_embedder(sentence_features[0])["sentence_embedding"] + return self.batch_all_triplet_loss(labels, rep) + + def batch_all_triplet_loss(self, labels: Tensor, embeddings: Tensor) -> Tensor: + """Build the triplet loss over a batch of embeddings. + We generate all the valid triplets and average the loss over the positive ones. + Args: + labels: labels of the batch, of size (batch_size,) + embeddings: tensor of shape (batch_size, embed_dim) + margin: margin for triplet loss + squared: Boolean. If true, output is the pairwise squared euclidean distance matrix. + If false, output is the pairwise euclidean distance matrix. + Returns: + Label_Sentence_Triplet: scalar tensor containing the triplet loss + """ + # Get the pairwise distance matrix + pairwise_dist = self.distance_metric(embeddings) + + anchor_positive_dist = pairwise_dist.unsqueeze(2) + anchor_negative_dist = pairwise_dist.unsqueeze(1) + + # Compute a 3D tensor of size (batch_size, batch_size, batch_size) + # triplet_loss[i, j, k] will contain the triplet loss of anchor=i, positive=j, negative=k + # Uses broadcasting where the 1st argument has shape (batch_size, batch_size, 1) + # and the 2nd (batch_size, 1, batch_size) + triplet_loss = anchor_positive_dist - anchor_negative_dist + self.triplet_margin + + # Put to zero the invalid triplets + # (where label(a) != label(p) or label(n) == label(a) or a == p) + mask = BatchHardTripletLoss.get_triplet_mask(labels) + triplet_loss = mask.float() * triplet_loss + + # Remove negative losses (i.e. the easy triplets) + triplet_loss[triplet_loss < 0] = 0 + + # Count number of positive triplets (where triplet_loss > 0) + valid_triplets = triplet_loss[triplet_loss > 1e-16] + num_positive_triplets = valid_triplets.size(0) + # num_valid_triplets = mask.sum() + # fraction_positive_triplets = num_positive_triplets / (num_valid_triplets.float() + 1e-16) + + # Get final mean triplet loss over the positive valid triplets + triplet_loss = triplet_loss.sum() / (num_positive_triplets + 1e-16) + + return triplet_loss + + @property + def citation(self) -> str: + return """ +@misc{hermans2017defense, + title={In Defense of the Triplet Loss for Person Re-Identification}, + author={Alexander Hermans and Lucas Beyer and Bastian Leibe}, + year={2017}, + eprint={1703.07737}, + archivePrefix={arXiv}, + primaryClass={cs.CV} +} +""" diff --git a/sentence-transformers/sentence_transformers/losses/BatchHardSoftMarginTripletLoss.py b/sentence-transformers/sentence_transformers/losses/BatchHardSoftMarginTripletLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..1f4b99a66f662699d787d6c7604876942637a41a --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/BatchHardSoftMarginTripletLoss.py @@ -0,0 +1,152 @@ +from __future__ import annotations + +from collections.abc import Iterable + +import torch +from torch import Tensor + +from sentence_transformers.SentenceTransformer import SentenceTransformer + +from .BatchHardTripletLoss import BatchHardTripletLoss, BatchHardTripletLossDistanceFunction + + +class BatchHardSoftMarginTripletLoss(BatchHardTripletLoss): + def __init__( + self, model: SentenceTransformer, distance_metric=BatchHardTripletLossDistanceFunction.eucledian_distance + ) -> None: + """ + BatchHardSoftMarginTripletLoss takes a batch with (sentence, label) pairs and computes the loss for all possible, valid + triplets, i.e., anchor and positive must have the same label, anchor and negative a different label. The labels + must be integers, with same label indicating sentences from the same class. Your train dataset + must contain at least 2 examples per label class. This soft-margin variant does not require setting a margin. + + Args: + model: SentenceTransformer model + distance_metric: Function that returns a distance between + two embeddings. The class SiameseDistanceMetric contains + pre-defined metrics that can be used. + + Definitions: + :Easy triplets: Triplets which have a loss of 0 because + ``distance(anchor, positive) + margin < distance(anchor, negative)``. + :Hard triplets: Triplets where the negative is closer to the anchor than the positive, i.e., + ``distance(anchor, negative) < distance(anchor, positive)``. + :Semi-hard triplets: Triplets where the negative is not closer to the anchor than the positive, but which + still have a positive loss, i.e., ``distance(anchor, positive) < distance(anchor, negative) + margin``. + + References: + * Source: https://github.com/NegatioN/OnlineMiningTripletLoss/blob/master/online_triplet_loss/losses.py + * Paper: In Defense of the Triplet Loss for Person Re-Identification, https://arxiv.org/abs/1703.07737 + * Blog post: https://omoindrot.github.io/triplet-loss + + Requirements: + 1. Each sentence must be labeled with a class. + 2. Your dataset must contain at least 2 examples per labels class. + 3. Your dataset should contain hard positives and negatives. + + Inputs: + +------------------+--------+ + | Texts | Labels | + +==================+========+ + | single sentences | class | + +------------------+--------+ + + Recommendations: + - Use ``BatchSamplers.GROUP_BY_LABEL`` (:class:`docs `) to + ensure that each batch contains 2+ examples per label class. + + Relations: + * :class:`BatchHardTripletLoss` uses a user-specified margin, while this loss does not require setting a margin. + + Example: + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + model = SentenceTransformer("microsoft/mpnet-base") + # E.g. 0: sports, 1: economy, 2: politics + train_dataset = Dataset.from_dict({ + "sentence": [ + "He played a great game.", + "The stock is up 20%", + "They won 2-1.", + "The last goal was amazing.", + "They all voted against the bill.", + ], + "label": [0, 1, 0, 0, 2], + }) + loss = losses.BatchHardSoftMarginTripletLoss(model) + + trainer = SentenceTransformerTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__(model) + self.sentence_embedder = model + self.distance_metric = distance_metric + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + rep = self.sentence_embedder(sentence_features[0])["sentence_embedding"] + return self.batch_hard_triplet_soft_margin_loss(labels, rep) + + # Hard Triplet Loss with Soft Margin + # Paper: In Defense of the Triplet Loss for Person Re-Identification, https://arxiv.org/abs/1703.07737 + def batch_hard_triplet_soft_margin_loss(self, labels: Tensor, embeddings: Tensor) -> Tensor: + """Build the triplet loss over a batch of embeddings. + For each anchor, we get the hardest positive and hardest negative to form a triplet. + Args: + labels: labels of the batch, of size (batch_size,) + embeddings: tensor of shape (batch_size, embed_dim) + squared: Boolean. If true, output is the pairwise squared euclidean distance matrix. + If false, output is the pairwise euclidean distance matrix. + Returns: + Label_Sentence_Triplet: scalar tensor containing the triplet loss + """ + # Get the pairwise distance matrix + pairwise_dist = self.distance_metric(embeddings) + + # For each anchor, get the hardest positive + # First, we need to get a mask for every valid positive (they should have same label) + mask_anchor_positive = BatchHardTripletLoss.get_anchor_positive_triplet_mask(labels).float() + + # We put to 0 any element where (a, p) is not valid (valid if a != p and label(a) == label(p)) + anchor_positive_dist = mask_anchor_positive * pairwise_dist + + # shape (batch_size, 1) + hardest_positive_dist, _ = anchor_positive_dist.max(1, keepdim=True) + + # For each anchor, get the hardest negative + # First, we need to get a mask for every valid negative (they should have different labels) + mask_anchor_negative = BatchHardTripletLoss.get_anchor_negative_triplet_mask(labels).float() + + # We add the maximum value in each row to the invalid negatives (label(a) == label(n)) + max_anchor_negative_dist, _ = pairwise_dist.max(1, keepdim=True) + anchor_negative_dist = pairwise_dist + max_anchor_negative_dist * (1.0 - mask_anchor_negative) + + # shape (batch_size,) + hardest_negative_dist, _ = anchor_negative_dist.min(1, keepdim=True) + + # Combine biggest d(a, p) and smallest d(a, n) into final triplet loss with soft margin + # tl = hardest_positive_dist - hardest_negative_dist + margin + # tl[tl < 0] = 0 + tl = torch.log1p(torch.exp(hardest_positive_dist - hardest_negative_dist)) + triplet_loss = tl.mean() + + return triplet_loss + + @property + def citation(self) -> str: + return """ +@misc{hermans2017defense, + title={In Defense of the Triplet Loss for Person Re-Identification}, + author={Alexander Hermans and Lucas Beyer and Bastian Leibe}, + year={2017}, + eprint={1703.07737}, + archivePrefix={arXiv}, + primaryClass={cs.CV} +} +""" diff --git a/sentence-transformers/sentence_transformers/losses/BatchHardTripletLoss.py b/sentence-transformers/sentence_transformers/losses/BatchHardTripletLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..abf947c46adfa645137fd0cb2b2d9c52935a0b8e --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/BatchHardTripletLoss.py @@ -0,0 +1,266 @@ +from __future__ import annotations + +from collections.abc import Iterable + +import torch +from torch import Tensor, nn + +from sentence_transformers import util +from sentence_transformers.SentenceTransformer import SentenceTransformer + + +class BatchHardTripletLossDistanceFunction: + """This class defines distance functions, that can be used with Batch[All/Hard/SemiHard]TripletLoss""" + + @staticmethod + def cosine_distance(embeddings: Tensor) -> Tensor: + """Compute the 2D matrix of cosine distances (1-cosine_similarity) between all embeddings.""" + return 1 - util.pytorch_cos_sim(embeddings, embeddings) + + @staticmethod + def eucledian_distance(embeddings: Tensor, squared=False) -> Tensor: + """ + Compute the 2D matrix of eucledian distances between all the embeddings. + Args: + embeddings: tensor of shape (batch_size, embed_dim) + squared: Boolean. If true, output is the pairwise squared euclidean distance matrix. + If false, output is the pairwise euclidean distance matrix. + Returns: + pairwise_distances: tensor of shape (batch_size, batch_size) + """ + + dot_product = torch.matmul(embeddings, embeddings.t()) + + # Get squared L2 norm for each embedding. We can just take the diagonal of `dot_product`. + # This also provides more numerical stability (the diagonal of the result will be exactly 0). + # shape (batch_size,) + square_norm = torch.diag(dot_product) + + # Compute the pairwise distance matrix as we have: + # ||a - b||^2 = ||a||^2 - 2 + ||b||^2 + # shape (batch_size, batch_size) + distances = square_norm.unsqueeze(0) - 2.0 * dot_product + square_norm.unsqueeze(1) + + # Because of computation errors, some distances might be negative so we put everything >= 0.0 + distances[distances < 0] = 0 + + if not squared: + # Because the gradient of sqrt is infinite when distances == 0.0 (ex: on the diagonal) + # we need to add a small epsilon where distances == 0.0 + mask = distances.eq(0).float() + distances = distances + mask * 1e-16 + + distances = (1.0 - mask) * torch.sqrt(distances) + + return distances + + +class BatchHardTripletLoss(nn.Module): + def __init__( + self, + model: SentenceTransformer, + distance_metric=BatchHardTripletLossDistanceFunction.eucledian_distance, + margin: float = 5, + ) -> None: + """ + BatchHardTripletLoss takes a batch with (sentence, label) pairs and computes the loss for all possible, valid + triplets, i.e., anchor and positive must have the same label, anchor and negative a different label. It then looks + for the hardest positive and the hardest negatives. + The labels must be integers, with same label indicating sentences from the same class. Your train dataset + must contain at least 2 examples per label class. + + Args: + model: SentenceTransformer model + distance_metric: Function that returns a distance between + two embeddings. The class SiameseDistanceMetric contains + pre-defined metrics that can be used + margin: Negative samples should be at least margin further + apart from the anchor than the positive. + + Definitions: + :Easy triplets: Triplets which have a loss of 0 because + ``distance(anchor, positive) + margin < distance(anchor, negative)``. + :Hard triplets: Triplets where the negative is closer to the anchor than the positive, i.e., + ``distance(anchor, negative) < distance(anchor, positive)``. + :Semi-hard triplets: Triplets where the negative is not closer to the anchor than the positive, but which + still have a positive loss, i.e., ``distance(anchor, positive) < distance(anchor, negative) + margin``. + + References: + * Source: https://github.com/NegatioN/OnlineMiningTripletLoss/blob/master/online_triplet_loss/losses.py + * Paper: In Defense of the Triplet Loss for Person Re-Identification, https://arxiv.org/abs/1703.07737 + * Blog post: https://omoindrot.github.io/triplet-loss + + Requirements: + 1. Each sentence must be labeled with a class. + 2. Your dataset must contain at least 2 examples per labels class. + 3. Your dataset should contain hard positives and negatives. + + Inputs: + +------------------+--------+ + | Texts | Labels | + +==================+========+ + | single sentences | class | + +------------------+--------+ + + Recommendations: + - Use ``BatchSamplers.GROUP_BY_LABEL`` (:class:`docs `) to + ensure that each batch contains 2+ examples per label class. + + Relations: + * :class:`BatchAllTripletLoss` uses all possible, valid triplets, rather than only the hardest positive and negative samples. + * :class:`BatchSemiHardTripletLoss` uses only semi-hard triplets, valid triplets, rather than only the hardest positive and negative samples. + * :class:`BatchHardSoftMarginTripletLoss` does not require setting a margin, while this loss does. + + Example: + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + model = SentenceTransformer("microsoft/mpnet-base") + # E.g. 0: sports, 1: economy, 2: politics + train_dataset = Dataset.from_dict({ + "sentence": [ + "He played a great game.", + "The stock is up 20%", + "They won 2-1.", + "The last goal was amazing.", + "They all voted against the bill.", + ], + "label": [0, 1, 0, 0, 2], + }) + loss = losses.BatchHardTripletLoss(model) + + trainer = SentenceTransformerTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.sentence_embedder = model + self.triplet_margin = margin + self.distance_metric = distance_metric + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor): + rep = self.sentence_embedder(sentence_features[0])["sentence_embedding"] + return self.batch_hard_triplet_loss(labels, rep) + + # Hard Triplet Loss + # Source: https://github.com/NegatioN/OnlineMiningTripletLoss/blob/master/online_triplet_loss/losses.py + # Paper: In Defense of the Triplet Loss for Person Re-Identification, https://arxiv.org/abs/1703.07737 + # Blog post: https://omoindrot.github.io/triplet-loss + def batch_hard_triplet_loss(self, labels: Tensor, embeddings: Tensor) -> Tensor: + """Build the triplet loss over a batch of embeddings. + For each anchor, we get the hardest positive and hardest negative to form a triplet. + Args: + labels: labels of the batch, of size (batch_size,) + embeddings: tensor of shape (batch_size, embed_dim) + margin: margin for triplet loss + squared: Boolean. If true, output is the pairwise squared euclidean distance matrix. + If false, output is the pairwise euclidean distance matrix. + Returns: + Label_Sentence_Triplet: scalar tensor containing the triplet loss + """ + # Get the pairwise distance matrix + pairwise_dist = self.distance_metric(embeddings) + + # For each anchor, get the hardest positive + # First, we need to get a mask for every valid positive (they should have same label) + mask_anchor_positive = BatchHardTripletLoss.get_anchor_positive_triplet_mask(labels).float() + + # We put to 0 any element where (a, p) is not valid (valid if a != p and label(a) == label(p)) + anchor_positive_dist = mask_anchor_positive * pairwise_dist + + # shape (batch_size, 1) + hardest_positive_dist, _ = anchor_positive_dist.max(1, keepdim=True) + + # For each anchor, get the hardest negative + # First, we need to get a mask for every valid negative (they should have different labels) + mask_anchor_negative = BatchHardTripletLoss.get_anchor_negative_triplet_mask(labels).float() + + # We add the maximum value in each row to the invalid negatives (label(a) == label(n)) + max_anchor_negative_dist, _ = pairwise_dist.max(1, keepdim=True) + anchor_negative_dist = pairwise_dist + max_anchor_negative_dist * (1.0 - mask_anchor_negative) + + # shape (batch_size,) + hardest_negative_dist, _ = anchor_negative_dist.min(1, keepdim=True) + + # Combine biggest d(a, p) and smallest d(a, n) into final triplet loss + tl = hardest_positive_dist - hardest_negative_dist + self.triplet_margin + tl[tl < 0] = 0 + triplet_loss = tl.mean() + + return triplet_loss + + @staticmethod + def get_triplet_mask(labels: Tensor) -> Tensor: + """Return a 3D mask where mask[a, p, n] is True iff the triplet (a, p, n) is valid. + A triplet (i, j, k) is valid if: + - i, j, k are distinct + - labels[i] == labels[j] and labels[i] != labels[k] + Args: + labels: tf.int32 `Tensor` with shape [batch_size] + """ + # Check that i, j and k are distinct + indices_equal = torch.eye(labels.size(0), device=labels.device).bool() + indices_not_equal = ~indices_equal + i_not_equal_j = indices_not_equal.unsqueeze(2) + i_not_equal_k = indices_not_equal.unsqueeze(1) + j_not_equal_k = indices_not_equal.unsqueeze(0) + + distinct_indices = (i_not_equal_j & i_not_equal_k) & j_not_equal_k + + label_equal = labels.unsqueeze(0) == labels.unsqueeze(1) + i_equal_j = label_equal.unsqueeze(2) + i_equal_k = label_equal.unsqueeze(1) + + valid_labels = ~i_equal_k & i_equal_j + + return valid_labels & distinct_indices + + @staticmethod + def get_anchor_positive_triplet_mask(labels: Tensor) -> Tensor: + """Return a 2D mask where mask[a, p] is True iff a and p are distinct and have same label. + Args: + labels: tf.int32 `Tensor` with shape [batch_size] + Returns: + mask: tf.bool `Tensor` with shape [batch_size, batch_size] + """ + # Check that i and j are distinct + + indices_equal = torch.eye(labels.size(0), device=labels.device).bool() + indices_not_equal = ~indices_equal + + # Check if labels[i] == labels[j] + # Uses broadcasting where the 1st argument has shape (1, batch_size) and the 2nd (batch_size, 1) + labels_equal = labels.unsqueeze(0) == labels.unsqueeze(1) + + return labels_equal & indices_not_equal + + @staticmethod + def get_anchor_negative_triplet_mask(labels: Tensor) -> Tensor: + """Return a 2D mask where mask[a, n] is True iff a and n have distinct labels. + Args: + labels: tf.int32 `Tensor` with shape [batch_size] + Returns: + mask: tf.bool `Tensor` with shape [batch_size, batch_size] + """ + # Check if labels[i] != labels[k] + # Uses broadcasting where the 1st argument has shape (1, batch_size) and the 2nd (batch_size, 1) + + return ~(labels.unsqueeze(0) == labels.unsqueeze(1)) + + @property + def citation(self) -> str: + return """ +@misc{hermans2017defense, + title={In Defense of the Triplet Loss for Person Re-Identification}, + author={Alexander Hermans and Lucas Beyer and Bastian Leibe}, + year={2017}, + eprint={1703.07737}, + archivePrefix={arXiv}, + primaryClass={cs.CV} +} +""" diff --git a/sentence-transformers/sentence_transformers/losses/BatchSemiHardTripletLoss.py b/sentence-transformers/sentence_transformers/losses/BatchSemiHardTripletLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..481d1dcebdbe742a2f0b2ef9e46cffd116549a45 --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/BatchSemiHardTripletLoss.py @@ -0,0 +1,187 @@ +from __future__ import annotations + +from collections.abc import Iterable + +import torch +from torch import Tensor, nn + +from sentence_transformers.SentenceTransformer import SentenceTransformer + +from .BatchHardTripletLoss import BatchHardTripletLossDistanceFunction + + +class BatchSemiHardTripletLoss(nn.Module): + def __init__( + self, + model: SentenceTransformer, + distance_metric=BatchHardTripletLossDistanceFunction.eucledian_distance, + margin: float = 5, + ) -> None: + """ + BatchSemiHardTripletLoss takes a batch with (label, sentence) pairs and computes the loss for all possible, valid + triplets, i.e., anchor and positive must have the same label, anchor and negative a different label. It then looks + for the semi hard positives and negatives. + The labels must be integers, with same label indicating sentences from the same class. Your train dataset + must contain at least 2 examples per label class. + + Args: + model: SentenceTransformer model + distance_metric: Function that returns a distance between + two embeddings. The class SiameseDistanceMetric contains + pre-defined metrics that can be used + margin: Negative samples should be at least margin further + apart from the anchor than the positive. + + Definitions: + :Easy triplets: Triplets which have a loss of 0 because + ``distance(anchor, positive) + margin < distance(anchor, negative)``. + :Hard triplets: Triplets where the negative is closer to the anchor than the positive, i.e., + ``distance(anchor, negative) < distance(anchor, positive)``. + :Semi-hard triplets: Triplets where the negative is not closer to the anchor than the positive, but which + still have a positive loss, i.e., ``distance(anchor, positive) < distance(anchor, negative) + margin``. + + References: + * Source: https://github.com/NegatioN/OnlineMiningTripletLoss/blob/master/online_triplet_loss/losses.py + * Paper: In Defense of the Triplet Loss for Person Re-Identification, https://arxiv.org/abs/1703.07737 + * Blog post: https://omoindrot.github.io/triplet-loss + + Requirements: + 1. Each sentence must be labeled with a class. + 2. Your dataset must contain at least 2 examples per labels class. + 3. Your dataset should contain semi hard positives and negatives. + + Inputs: + +------------------+--------+ + | Texts | Labels | + +==================+========+ + | single sentences | class | + +------------------+--------+ + + Recommendations: + - Use ``BatchSamplers.GROUP_BY_LABEL`` (:class:`docs `) to + ensure that each batch contains 2+ examples per label class. + + Relations: + * :class:`BatchHardTripletLoss` uses only the hardest positive and negative samples, rather than only semi hard positive and negatives. + * :class:`BatchAllTripletLoss` uses all possible, valid triplets, rather than only semi hard positive and negatives. + * :class:`BatchHardSoftMarginTripletLoss` uses only the hardest positive and negative samples, rather than only semi hard positive and negatives. + Also, it does not require setting a margin. + + Example: + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + model = SentenceTransformer("microsoft/mpnet-base") + # E.g. 0: sports, 1: economy, 2: politics + train_dataset = Dataset.from_dict({ + "sentence": [ + "He played a great game.", + "The stock is up 20%", + "They won 2-1.", + "The last goal was amazing.", + "They all voted against the bill.", + ], + "label": [0, 1, 0, 0, 2], + }) + loss = losses.BatchSemiHardTripletLoss(model) + + trainer = SentenceTransformerTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.sentence_embedder = model + self.margin = margin + self.distance_metric = distance_metric + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + rep = self.sentence_embedder(sentence_features[0])["sentence_embedding"] + return self.batch_semi_hard_triplet_loss(labels, rep) + + # Semi-Hard Triplet Loss + # Based on: https://github.com/tensorflow/addons/blob/master/tensorflow_addons/losses/triplet.py#L71 + # Paper: FaceNet: A Unified Embedding for Face Recognition and Clustering: https://arxiv.org/pdf/1503.03832.pdf + def batch_semi_hard_triplet_loss(self, labels: Tensor, embeddings: Tensor) -> Tensor: + """Build the triplet loss over a batch of embeddings. + We generate all the valid triplets and average the loss over the positive ones. + Args: + labels: labels of the batch, of size (batch_size,) + embeddings: tensor of shape (batch_size, embed_dim) + margin: margin for triplet loss + squared: Boolean. If true, output is the pairwise squared euclidean distance matrix. + If false, output is the pairwise euclidean distance matrix. + Returns: + Label_Sentence_Triplet: scalar tensor containing the triplet loss + """ + labels = labels.unsqueeze(1) + + pdist_matrix = self.distance_metric(embeddings) + + adjacency = labels == labels.t() + adjacency_not = ~adjacency + + batch_size = torch.numel(labels) + pdist_matrix_tile = pdist_matrix.repeat([batch_size, 1]) + + mask = adjacency_not.repeat([batch_size, 1]) & (pdist_matrix_tile > torch.reshape(pdist_matrix.t(), [-1, 1])) + + mask_final = torch.reshape(torch.sum(mask, 1, keepdims=True) > 0.0, [batch_size, batch_size]) + mask_final = mask_final.t() + + negatives_outside = torch.reshape( + BatchSemiHardTripletLoss._masked_minimum(pdist_matrix_tile, mask), [batch_size, batch_size] + ) + negatives_outside = negatives_outside.t() + + negatives_inside = BatchSemiHardTripletLoss._masked_maximum(pdist_matrix, adjacency_not) + negatives_inside = negatives_inside.repeat([1, batch_size]) + + semi_hard_negatives = torch.where(mask_final, negatives_outside, negatives_inside) + + loss_mat = (pdist_matrix - semi_hard_negatives) + self.margin + + mask_positives = adjacency.float().to(labels.device) - torch.eye(batch_size, device=labels.device) + mask_positives = mask_positives.to(labels.device) + num_positives = torch.sum(mask_positives) + + triplet_loss = ( + torch.sum(torch.max(loss_mat * mask_positives, torch.tensor([0.0], device=labels.device))) / num_positives + ) + + return triplet_loss + + @staticmethod + def _masked_minimum(data: Tensor, mask: Tensor, dim: int = 1) -> Tensor: + axis_maximums, _ = data.max(dim, keepdims=True) + masked_minimums = (data - axis_maximums) * mask + masked_minimums, _ = masked_minimums.min(dim, keepdims=True) + masked_minimums += axis_maximums + + return masked_minimums + + @staticmethod + def _masked_maximum(data: Tensor, mask: Tensor, dim: int = 1) -> Tensor: + axis_minimums, _ = data.min(dim, keepdims=True) + masked_maximums = (data - axis_minimums) * mask + masked_maximums, _ = masked_maximums.max(dim, keepdims=True) + masked_maximums += axis_minimums + + return masked_maximums + + @property + def citation(self) -> str: + return """ +@misc{hermans2017defense, + title={In Defense of the Triplet Loss for Person Re-Identification}, + author={Alexander Hermans and Lucas Beyer and Bastian Leibe}, + year={2017}, + eprint={1703.07737}, + archivePrefix={arXiv}, + primaryClass={cs.CV} +} +""" diff --git a/sentence-transformers/sentence_transformers/losses/CachedGISTEmbedLoss.py b/sentence-transformers/sentence_transformers/losses/CachedGISTEmbedLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..d353f13b59752bff4bd841e17031d096cec59f1c --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/CachedGISTEmbedLoss.py @@ -0,0 +1,387 @@ +from __future__ import annotations + +from collections.abc import Iterable, Iterator +from contextlib import nullcontext +from functools import partial +from typing import Any, Literal + +import torch +import tqdm +from torch import Tensor, nn +from torch.utils.checkpoint import get_device_states, set_device_states +from transformers import PreTrainedTokenizerBase + +from sentence_transformers import SentenceTransformer +from sentence_transformers.models import StaticEmbedding + + +class RandContext: + """ + Random-state context manager class. Reference: https://github.com/luyug/GradCache. + + This class will back up the pytorch's random state during initialization. Then when the context is activated, + the class will set up the random state with the backed-up one. + """ + + def __init__(self, *tensors) -> None: + self.fwd_cpu_state = torch.get_rng_state() + self.fwd_gpu_devices, self.fwd_gpu_states = get_device_states(*tensors) + + def __enter__(self) -> None: + self._fork = torch.random.fork_rng(devices=self.fwd_gpu_devices, enabled=True) + self._fork.__enter__() + torch.set_rng_state(self.fwd_cpu_state) + set_device_states(self.fwd_gpu_devices, self.fwd_gpu_states) + + def __exit__(self, exc_type, exc_val, exc_tb) -> None: + self._fork.__exit__(exc_type, exc_val, exc_tb) + self._fork = None + + +def _backward_hook( + grad_output: Tensor, + sentence_features: Iterable[dict[str, Tensor]], + loss_obj: CachedGISTEmbedLoss, +) -> None: + """A backward hook to backpropagate the cached gradients mini-batch by mini-batch.""" + assert loss_obj.cache is not None + assert loss_obj.random_states is not None + with torch.enable_grad(): + for sentence_feature, grad, random_states in zip(sentence_features, loss_obj.cache, loss_obj.random_states): + for (reps_mb, _, _), grad_mb in zip( + loss_obj.embed_minibatch_iter( + sentence_feature=sentence_feature, + with_grad=True, + copy_random_state=False, + random_states=random_states, + ), + grad, + ): + surrogate = torch.dot(reps_mb.flatten(), grad_mb.flatten()) * grad_output + surrogate.backward() + + +class CachedGISTEmbedLoss(nn.Module): + def __init__( + self, + model: SentenceTransformer, + guide: SentenceTransformer, + temperature: float = 0.01, + mini_batch_size: int = 32, + show_progress_bar: bool = False, + margin_strategy: Literal["absolute", "relative"] = "absolute", + margin: float = 0.0, + ) -> None: + """ + This loss is a combination of :class:`GISTEmbedLoss` and :class:`CachedMultipleNegativesRankingLoss`. + Typically, :class:`MultipleNegativesRankingLoss` requires a larger batch size for better performance. + :class:`GISTEmbedLoss` yields stronger training signals than :class:`MultipleNegativesRankingLoss` due to the + use of a guide model for in-batch negative sample selection. Meanwhile, :class:`CachedMultipleNegativesRankingLoss` + allows for scaling of the batch size by dividing the computation into two stages of embedding and loss + calculation, which both can be scaled by mini-batches (https://arxiv.org/pdf/2101.06983.pdf). + + By combining the guided selection from :class:`GISTEmbedLoss` and Gradient Cache from + :class:`CachedMultipleNegativesRankingLoss`, it is possible to reduce memory usage while maintaining performance + levels comparable to those of :class:`GISTEmbedLoss`. + + You can apply different false-negative filtering strategies to discard hard negatives that are too similar to + the positive. Two strategies are supported: + + - "absolute": Discards negatives whose similarity score is greater than or equal to ``positive_score - margin``. + - "relative": Discards negatives whose similarity score is greater than or equal to ``positive_score * (1 - margin)``. + + Args: + model: SentenceTransformer model + guide: SentenceTransformer model to guide the in-batch negative sample selection. + temperature: Temperature parameter to scale the cosine similarities. + mini_batch_size: Mini-batch size for the forward pass, this denotes how much memory is actually used during + training and evaluation. The larger the mini-batch size, the more memory efficient the training is, but + the slower the training will be. It's recommended to set it as high as your GPU memory allows. The default + value is 32. + show_progress_bar: If True, a progress bar for the mini-batches is shown during training. The default is False. + margin_strategy: Strategy used for false negative filtering. One of {"absolute", "relative"}. + margin: The margin value for filtering negatives. Defaults to 0.0, together with the "absolute" strategy, + this only removes negatives that are more similar to the query than the positive is to the query. + + References: + - Efficient Natural Language Response Suggestion for Smart Reply, Section 4.4: https://arxiv.org/pdf/1705.00652.pdf + - Scaling Deep Contrastive Learning Batch Size under Memory Limited Setup: https://arxiv.org/pdf/2101.06983.pdf + - GISTEmbed: Guided In-sample Selection of Training Negatives for Text Embedding Fine-tuning https://arxiv.org/abs/2402.16829 + + Requirements: + 1. (anchor, positive) pairs or (anchor, positive, negative pairs) + 2. Should be used with large batch sizes for superior performance, but has slower training time than :class:`MultipleNegativesRankingLoss` + + Inputs: + +-------------------------------------------------+--------+ + | Texts | Labels | + +=================================================+========+ + | (anchor, positive) pairs | none | + +-------------------------------------------------+--------+ + | (anchor, positive, negative) triplets | none | + +-------------------------------------------------+--------+ + | (anchor, positive, negative_1, ..., negative_n) | none | + +-------------------------------------------------+--------+ + + Recommendations: + - Use ``BatchSamplers.NO_DUPLICATES`` (:class:`docs `) to + ensure that no in-batch negatives are duplicates of the anchor or positive samples. + + Relations: + - Equivalent to :class:`GISTEmbedLoss`, but with caching that allows for much higher batch sizes + + Example: + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + model = SentenceTransformer("microsoft/mpnet-base") + guide = SentenceTransformer("all-MiniLM-L6-v2") + train_dataset = Dataset.from_dict({ + "anchor": ["It's nice weather outside today.", "He drove to work."], + "positive": ["It's so sunny.", "He took the car to the office."], + }) + loss = losses.CachedGISTEmbedLoss( + model, + guide, + mini_batch_size=64, + margin_strategy="absolute", # or "relative" (e.g., margin=0.05 for max. 95% of positive similarity) + margin=0.1 + ) + + trainer = SentenceTransformerTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + if isinstance(model[0], StaticEmbedding): + raise ValueError( + "CachedGISTEmbedLoss is not compatible with a SentenceTransformer model based on a StaticEmbedding. " + "Consider using GISTEmbedLoss instead." + ) + self.model = model + self.guide = guide + self.temperature = temperature + self.similarity_fct = nn.CosineSimilarity(dim=-1) + if not hasattr(model, "tokenizer") or not hasattr(guide, "tokenizer"): + raise ValueError("Both the training model and the guiding model must have a tokenizer attribute.") + if not isinstance(model.tokenizer, PreTrainedTokenizerBase) or not isinstance( + guide.tokenizer, PreTrainedTokenizerBase + ): + raise ValueError( + "Both the training model and the guiding model must use a PreTrainedTokenizer from transformers." + ) + self.cross_entropy_loss = nn.CrossEntropyLoss() + self.mini_batch_size = mini_batch_size + self.cache: list[list[Tensor]] | None = None + self.random_states: list[list[RandContext]] | None = None + self.show_progress_bar = show_progress_bar + self.must_retokenize = ( + model.tokenizer.vocab != guide.tokenizer.vocab or guide.max_seq_length < model.max_seq_length + ) + if self.must_retokenize: + self.tokenizer = model.tokenizer + if margin_strategy not in ("absolute", "relative"): + raise ValueError("margin_strategy must be 'absolute' or 'relative'.") + self.margin_strategy = margin_strategy + self.margin = margin + + def sim_matrix(self, embed1: Tensor, embed2: Tensor) -> Tensor: + return self.similarity_fct(embed1.unsqueeze(1), embed2.unsqueeze(0)) + + def embed_minibatch( + self, + sentence_feature: dict[str, Tensor], + begin: int, + end: int, + with_grad: bool, + copy_random_state: bool, + random_state: RandContext | None = None, + ) -> tuple[Tensor, Tensor, RandContext | None]: + """Do forward pass on a minibatch of the input features and return corresponding embeddings.""" + grad_context = nullcontext if with_grad else torch.no_grad + random_state_context = nullcontext() if random_state is None else random_state + sentence_feature_minibatch = {k: v[begin:end] for k, v in sentence_feature.items()} + with random_state_context: + with grad_context(): + random_state = RandContext(*sentence_feature_minibatch.values()) if copy_random_state else None + reps = self.model(sentence_feature_minibatch)["sentence_embedding"] # (mbsz, hdim) + with torch.no_grad(): + if self.must_retokenize: + decoded = self.tokenizer.batch_decode( + sentence_feature_minibatch["input_ids"], skip_special_tokens=True + ) + sentence_feature_minibatch = self.guide.tokenize(decoded) + sentence_feature_minibatch = { + key: value.to(self.guide.device) for key, value in sentence_feature_minibatch.items() + } + guide_reps = self.guide(sentence_feature_minibatch)["sentence_embedding"] + + return reps, guide_reps, random_state + + def embed_minibatch_iter( + self, + sentence_feature: dict[str, Tensor], + with_grad: bool, + copy_random_state: bool, + random_states: list[RandContext] | None = None, + ) -> Iterator[tuple[Tensor, Tensor, RandContext | None]]: + """Do forward pass on all the minibatches of the input features and yield corresponding embeddings.""" + input_ids: Tensor = sentence_feature["input_ids"] + bsz, _ = input_ids.shape + for i, b in enumerate( + tqdm.trange( + 0, + bsz, + self.mini_batch_size, + desc="Embed mini-batches", + disable=not self.show_progress_bar, + ) + ): + e = b + self.mini_batch_size + reps, guide_reps, random_state = self.embed_minibatch( + sentence_feature=sentence_feature, + begin=b, + end=e, + with_grad=with_grad, + copy_random_state=copy_random_state, + random_state=None if random_states is None else random_states[i], + ) + yield reps, guide_reps, random_state # reps: (mbsz, hdim) + + def calculate_loss_and_cache_gradients(self, reps: list[list[Tensor]], reps_guided: list[list[Tensor]]) -> Tensor: + """Generalized function to calculate the cross-entropy loss and cache the gradients wrt. the embeddings.""" + loss = self.calculate_loss(reps, reps_guided, with_backward=True) + loss = loss.detach().requires_grad_() + + self.cache = [[r.grad for r in rs] for rs in reps] + + return loss + + def calculate_loss( + self, reps: list[list[Tensor]], reps_guided: list[list[Tensor]], with_backward: bool = False + ) -> Tensor: + """Generalized function to calculate the cross-entropy loss without caching gradients.""" + if len(reps) != len(reps_guided): + raise ValueError("reps and reps_guided must have the same length") + + # Concatenate embeddings along the batch dimension + concatenated_reps = [torch.cat(rep, dim=0) for rep in reps] + concatenated_guided_reps = [torch.cat(rep_guide, dim=0) for rep_guide in reps_guided] + + labels = torch.arange(concatenated_reps[0].size(0)).long().to(concatenated_reps[0].device) + batch_size = concatenated_reps[0].shape[0] + + losses: list[torch.Tensor] = [] + for b in tqdm.trange( + 0, + batch_size, + self.mini_batch_size, + desc="Calculating loss", + disable=not self.show_progress_bar, + ): + e = b + self.mini_batch_size + + # Compute guided similarity matrices for anchor-positive, anchor-anchor, and positive-positive samples + guided_ap_sim = self.sim_matrix(concatenated_guided_reps[0][b:e], concatenated_guided_reps[1]) + guided_aa_sim = self.sim_matrix(concatenated_guided_reps[0][b:e], concatenated_guided_reps[0]) + guided_pp_sim = self.sim_matrix(concatenated_guided_reps[1][b:e], concatenated_guided_reps[1]) + + # Define the anchor threshold for each similarity matrix + guided_sim = guided_ap_sim.diagonal(offset=b).view(-1, 1) + + # Compute similarity scores for the current mini-batch + ap_sim = self.sim_matrix(concatenated_reps[0][b:e], concatenated_reps[1]) # anchor-positive similarity + aa_sim = self.sim_matrix(concatenated_reps[0][b:e], concatenated_reps[0]) # anchor-anchor similarity + pp_sim = self.sim_matrix(concatenated_reps[1][b:e], concatenated_reps[1]) # positive-positive similarity + + # This uses guided (teacher) similarity as a dynamic threshold to identify and suppress false negatives + def mask_false_negatives(guided_sim_mat, sim_mat, positive_mask: Tensor | None = None): + if self.margin_strategy == "absolute": + # Remove samples whose guided similarity is higher than (positive_sim - margin) + mask = guided_sim_mat > (guided_sim - self.margin) + elif self.margin_strategy == "relative": + # Remove samples whose guided similarity is higher than (positive_sim * margin) + mask = guided_sim_mat > (guided_sim * (1 - self.margin)) + + if positive_mask is not None: + # Ensure true positive pairs are not masked out + mask = mask & ~positive_mask + sim_mat[mask] = -torch.inf + return sim_mat + + # Create a mask to protect true positive pairs in the anchor-positive matrix (i.e., diagonal elements) + positive_mask = torch.eye(*guided_ap_sim.shape, dtype=torch.bool, device=guided_ap_sim.device) + positive_mask = positive_mask.roll(b) + + # Apply false negative suppression to each similarity matrix using guided similarity as anchor + ap_sim = mask_false_negatives(guided_ap_sim, ap_sim, positive_mask=positive_mask) # anchor-positive + aa_sim = mask_false_negatives(guided_aa_sim, aa_sim) # anchor-anchor + pp_sim = mask_false_negatives(guided_pp_sim, pp_sim) # positive-positive + + # Concatenate the similarity matrices for anchor-positive, anchor-anchor, and positive-positive + scores = torch.cat([ap_sim, aa_sim, pp_sim], dim=1) + + # If there are negatives (len(reps) > 2), process them + if len(concatenated_reps) > 2: + for i in range(2, len(concatenated_reps)): # Start from 2 since first 2 are anchor-positive + guided_neg_sim = self.sim_matrix(concatenated_guided_reps[0][b:e], concatenated_guided_reps[i]) + neg_sim = self.sim_matrix(concatenated_reps[0][b:e], concatenated_reps[i]) + neg_sim = mask_false_negatives(guided_neg_sim, neg_sim) + scores = torch.cat([scores, neg_sim], dim=1) + + # Normalize the scores and calculate the cross-entropy loss + scores = scores / self.temperature + loss_mbatch: torch.Tensor = self.cross_entropy_loss(scores, labels[b:e]) * len(scores) / batch_size + if with_backward: + loss_mbatch.backward() + loss_mbatch = loss_mbatch.detach() + losses.append(loss_mbatch) + + loss = sum(losses) + return loss + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + # Step (1): A quick embedding step without gradients/computation graphs to get all the embeddings + reps = [] + reps_guided = [] + self.random_states = [] # Copy random states to guarantee exact reproduction of the embeddings during the second forward pass, i.e. step (3) + for sentence_feature in sentence_features: + reps_mbs = [] + reps_guided_mbs = [] + random_state_mbs = [] + for reps_mb, reps_guided_mb, random_state in self.embed_minibatch_iter( + sentence_feature=sentence_feature, + with_grad=False, + copy_random_state=True, + ): + reps_mbs.append(reps_mb.detach().requires_grad_()) + reps_guided_mbs.append(reps_guided_mb.detach()) # does not requires gradient + random_state_mbs.append(random_state) + reps.append(reps_mbs) + reps_guided.append(reps_guided_mbs) + self.random_states.append(random_state_mbs) + + if torch.is_grad_enabled(): + # Step (2): Calculate the loss, backward up to the embeddings and cache the gradients wrt. to the embeddings + loss = self.calculate_loss_and_cache_gradients(reps, reps_guided) + + # Step (3): A 2nd embedding step with gradients/computation graphs and connect the cached gradients into the backward chain + loss.register_hook(partial(_backward_hook, sentence_features=sentence_features, loss_obj=self)) + else: + # If grad is not enabled (e.g. in evaluation), then we don't have to worry about the gradients or backward hook + loss = self.calculate_loss(reps, reps_guided) + return loss + + def get_config_dict(self) -> dict[str, Any]: + return { + "guide": self.guide, + "temperature": self.temperature, + "mini_batch_size": self.mini_batch_size, + "margin_strategy": self.margin_strategy, + "margin": self.margin, + } diff --git a/sentence-transformers/sentence_transformers/losses/CachedMultipleNegativesRankingLoss.py b/sentence-transformers/sentence_transformers/losses/CachedMultipleNegativesRankingLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..57838eec88ac9e50e6aec57e362f5e181802832c --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/CachedMultipleNegativesRankingLoss.py @@ -0,0 +1,299 @@ +from __future__ import annotations + +from collections.abc import Iterable, Iterator +from contextlib import nullcontext +from functools import partial +from typing import Any + +import torch +import tqdm +from torch import Tensor, nn +from torch.utils.checkpoint import get_device_states, set_device_states + +from sentence_transformers import SentenceTransformer, util +from sentence_transformers.models import StaticEmbedding + + +class RandContext: + """ + Random-state context manager class. Reference: https://github.com/luyug/GradCache. + + This class will back up the pytorch's random state during initialization. Then when the context is activated, + the class will set up the random state with the backed-up one. + """ + + def __init__(self, *tensors) -> None: + self.fwd_cpu_state = torch.get_rng_state() + self.fwd_gpu_devices, self.fwd_gpu_states = get_device_states(*tensors) + + def __enter__(self) -> None: + self._fork = torch.random.fork_rng(devices=self.fwd_gpu_devices, enabled=True) + self._fork.__enter__() + torch.set_rng_state(self.fwd_cpu_state) + set_device_states(self.fwd_gpu_devices, self.fwd_gpu_states) + + def __exit__(self, exc_type, exc_val, exc_tb) -> None: + self._fork.__exit__(exc_type, exc_val, exc_tb) + self._fork = None + + +def _backward_hook( + grad_output: Tensor, + sentence_features: Iterable[dict[str, Tensor]], + loss_obj: CachedMultipleNegativesRankingLoss, +) -> None: + """A backward hook to backpropagate the cached gradients mini-batch by mini-batch.""" + assert loss_obj.cache is not None + assert loss_obj.random_states is not None + with torch.enable_grad(): + for sentence_feature, grad, random_states in zip(sentence_features, loss_obj.cache, loss_obj.random_states): + for (reps_mb, _), grad_mb in zip( + loss_obj.embed_minibatch_iter( + sentence_feature=sentence_feature, + with_grad=True, + copy_random_state=False, + random_states=random_states, + ), + grad, + ): + surrogate = torch.dot(reps_mb.flatten(), grad_mb.flatten()) * grad_output + surrogate.backward() + + +class CachedMultipleNegativesRankingLoss(nn.Module): + def __init__( + self, + model: SentenceTransformer, + scale: float = 20.0, + similarity_fct: callable[[Tensor, Tensor], Tensor] = util.cos_sim, + mini_batch_size: int = 32, + show_progress_bar: bool = False, + ) -> None: + """ + Boosted version of MultipleNegativesRankingLoss (https://arxiv.org/pdf/1705.00652.pdf) by GradCache (https://arxiv.org/pdf/2101.06983.pdf). + + Constrastive learning (here our MNRL loss) with in-batch negatives is usually hard to work with large batch sizes due to (GPU) memory limitation. + Even with batch-scaling methods like gradient-scaling, it cannot work either. This is because the in-batch negatives make the data points within + the same batch non-independent and thus the batch cannot be broke down into mini-batches. GradCache is a smart way to solve this problem. + It achieves the goal by dividing the computation into two stages of embedding and loss calculation, which both can be scaled by mini-batches. + As a result, memory of constant size (e.g. that works with batch size = 32) can now process much larger batches (e.g. 65536). + + In detail: + + (1) It first does a quick embedding step without gradients/computation graphs to get all the embeddings; + (2) Calculate the loss, backward up to the embeddings and cache the gradients wrt. to the embeddings; + (3) A 2nd embedding step with gradients/computation graphs and connect the cached gradients into the backward chain. + + Notes: All steps are done with mini-batches. In the original implementation of GradCache, (2) is not done in mini-batches and + requires a lot memory when the batch size is large. One drawback is about the speed. Gradient caching will sacrifice + around 20% computation time according to the paper. + + Args: + model: SentenceTransformer model + scale: Output of similarity function is multiplied by scale value + similarity_fct: similarity function between sentence embeddings. By default, cos_sim. Can also be set to dot + product (and then set scale to 1) + mini_batch_size: Mini-batch size for the forward pass, this denotes how much memory is actually used during + training and evaluation. The larger the mini-batch size, the more memory efficient the training is, but + the slower the training will be. It's recommended to set it as high as your GPU memory allows. The default + value is 32. + show_progress_bar: If True, a progress bar for the mini-batches is shown during training. The default is False. + + References: + - Efficient Natural Language Response Suggestion for Smart Reply, Section 4.4: https://arxiv.org/pdf/1705.00652.pdf + - Scaling Deep Contrastive Learning Batch Size under Memory Limited Setup: https://arxiv.org/pdf/2101.06983.pdf + + Requirements: + 1. (anchor, positive) pairs or (anchor, positive, negative pairs) + 2. Should be used with large `per_device_train_batch_size` and low `mini_batch_size` for superior performance, but slower training time than :class:`MultipleNegativesRankingLoss`. + + Inputs: + +-------------------------------------------------+--------+ + | Texts | Labels | + +=================================================+========+ + | (anchor, positive) pairs | none | + +-------------------------------------------------+--------+ + | (anchor, positive, negative) triplets | none | + +-------------------------------------------------+--------+ + | (anchor, positive, negative_1, ..., negative_n) | none | + +-------------------------------------------------+--------+ + + Recommendations: + - Use ``BatchSamplers.NO_DUPLICATES`` (:class:`docs `) to + ensure that no in-batch negatives are duplicates of the anchor or positive samples. + + Relations: + - Equivalent to :class:`MultipleNegativesRankingLoss`, but with caching that allows for much higher batch sizes + (and thus better performance) without extra memory usage. This loss also trains roughly 2x to 2.4x slower than + :class:`MultipleNegativesRankingLoss`. + + Example: + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + model = SentenceTransformer("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "anchor": ["It's nice weather outside today.", "He drove to work."], + "positive": ["It's so sunny.", "He took the car to the office."], + }) + loss = losses.CachedMultipleNegativesRankingLoss(model, mini_batch_size=64) + + trainer = SentenceTransformerTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + if isinstance(model[0], StaticEmbedding): + raise ValueError( + "CachedMultipleNegativesRankingLoss is not compatible with a SentenceTransformer model based on a StaticEmbedding. " + "Consider using MultipleNegativesRankingLoss instead." + ) + + self.model = model + self.scale = scale + self.similarity_fct = similarity_fct + self.cross_entropy_loss = nn.CrossEntropyLoss() + self.mini_batch_size = mini_batch_size + self.cache: list[list[Tensor]] | None = None + self.random_states: list[list[RandContext]] | None = None + self.show_progress_bar = show_progress_bar + + def embed_minibatch( + self, + sentence_feature: dict[str, Tensor], + begin: int, + end: int, + with_grad: bool, + copy_random_state: bool, + random_state: RandContext | None = None, + ) -> tuple[Tensor, RandContext | None]: + """Do forward pass on a minibatch of the input features and return corresponding embeddings.""" + grad_context = nullcontext if with_grad else torch.no_grad + random_state_context = nullcontext() if random_state is None else random_state + sentence_feature_minibatch = {k: v[begin:end] for k, v in sentence_feature.items()} + with random_state_context: + with grad_context(): + random_state = RandContext(*sentence_feature_minibatch.values()) if copy_random_state else None + reps = self.model(sentence_feature_minibatch)["sentence_embedding"] # (mbsz, hdim) + return reps, random_state + + def embed_minibatch_iter( + self, + sentence_feature: dict[str, Tensor], + with_grad: bool, + copy_random_state: bool, + random_states: list[RandContext] | None = None, + ) -> Iterator[tuple[Tensor, RandContext | None]]: + """Do forward pass on all the minibatches of the input features and yield corresponding embeddings.""" + input_ids: Tensor = sentence_feature["input_ids"] + bsz, _ = input_ids.shape + for i, b in enumerate( + tqdm.trange( + 0, + bsz, + self.mini_batch_size, + desc="Embed mini-batches", + disable=not self.show_progress_bar, + ) + ): + e = b + self.mini_batch_size + reps, random_state = self.embed_minibatch( + sentence_feature=sentence_feature, + begin=b, + end=e, + with_grad=with_grad, + copy_random_state=copy_random_state, + random_state=None if random_states is None else random_states[i], + ) + yield reps, random_state # reps: (mbsz, hdim) + + def calculate_loss_and_cache_gradients(self, reps: list[list[Tensor]]) -> Tensor: + """Calculate the cross-entropy loss and cache the gradients wrt. the embeddings.""" + loss = self.calculate_loss(reps, with_backward=True) + loss = loss.detach().requires_grad_() + + self.cache = [[r.grad for r in rs] for rs in reps] # e.g. 3 * bsz/mbsz * (mbsz, hdim) + + return loss + + def calculate_loss(self, reps: list[list[Tensor]], with_backward: bool = False) -> Tensor: + """Calculate the cross-entropy loss. No need to cache the gradients.""" + embeddings_a = torch.cat(reps[0]) # (bsz, hdim) + embeddings_b = torch.cat([torch.cat(r) for r in reps[1:]]) # ((1 + nneg) * bsz, hdim) + + batch_size = len(embeddings_a) + labels = torch.tensor( + range(batch_size), dtype=torch.long, device=embeddings_a.device + ) # (bsz, (1 + nneg) * bsz) Example a[i] should match with b[i] + losses: list[torch.Tensor] = [] + for b in tqdm.trange( + 0, + batch_size, + self.mini_batch_size, + desc="Preparing caches", + disable=not self.show_progress_bar, + ): + e = b + self.mini_batch_size + scores: Tensor = self.similarity_fct(embeddings_a[b:e], embeddings_b) * self.scale + loss_mbatch: torch.Tensor = self.cross_entropy_loss(scores, labels[b:e]) * len(scores) / batch_size + if with_backward: + loss_mbatch.backward() + loss_mbatch = loss_mbatch.detach() + losses.append(loss_mbatch) + + loss = sum(losses) + return loss + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + # Step (1): A quick embedding step without gradients/computation graphs to get all the embeddings + reps = [] + self.random_states = [] # Copy random states to guarantee exact reproduction of the embeddings during the second forward pass, i.e. step (3) + for sentence_feature in sentence_features: + reps_mbs = [] + random_state_mbs = [] + for reps_mb, random_state in self.embed_minibatch_iter( + sentence_feature=sentence_feature, + with_grad=False, + copy_random_state=True, + ): + reps_mbs.append(reps_mb.detach().requires_grad_()) + random_state_mbs.append(random_state) + reps.append(reps_mbs) + self.random_states.append(random_state_mbs) + + if torch.is_grad_enabled(): + # Step (2): Calculate the loss, backward up to the embeddings and cache the gradients wrt. to the embeddings + loss = self.calculate_loss_and_cache_gradients(reps) + + # Step (3): A 2nd embedding step with gradients/computation graphs and connect the cached gradients into the backward chain + loss.register_hook(partial(_backward_hook, sentence_features=sentence_features, loss_obj=self)) + else: + # If grad is not enabled (e.g. in evaluation), then we don't have to worry about the gradients or backward hook + loss = self.calculate_loss(reps) + + return loss + + def get_config_dict(self) -> dict[str, Any]: + return { + "scale": self.scale, + "similarity_fct": self.similarity_fct.__name__, + "mini_batch_size": self.mini_batch_size, + } + + @property + def citation(self) -> str: + return """ +@misc{gao2021scaling, + title={Scaling Deep Contrastive Learning Batch Size under Memory Limited Setup}, + author={Luyu Gao and Yunyi Zhang and Jiawei Han and Jamie Callan}, + year={2021}, + eprint={2101.06983}, + archivePrefix={arXiv}, + primaryClass={cs.LG} +} +""" diff --git a/sentence-transformers/sentence_transformers/losses/CachedMultipleNegativesSymmetricRankingLoss.py b/sentence-transformers/sentence_transformers/losses/CachedMultipleNegativesSymmetricRankingLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..4b0995c96814ef23984ba52234d58f0791fb289a --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/CachedMultipleNegativesSymmetricRankingLoss.py @@ -0,0 +1,255 @@ +from __future__ import annotations + +from collections.abc import Iterable, Iterator +from contextlib import nullcontext +from functools import partial +from typing import Any + +import torch +import tqdm +from torch import Tensor, nn + +from sentence_transformers import SentenceTransformer, util +from sentence_transformers.losses.CachedMultipleNegativesRankingLoss import RandContext +from sentence_transformers.models import StaticEmbedding + + +def _backward_hook( + grad_output: Tensor, + sentence_features: Iterable[dict[str, Tensor]], + loss_obj: CachedMultipleNegativesSymmetricRankingLoss, +) -> None: + """A backward hook to backpropagate the cached gradients mini-batch by mini-batch.""" + assert loss_obj.cache is not None + assert loss_obj.random_states is not None + with torch.enable_grad(): + for sentence_feature, grad, random_states in zip(sentence_features, loss_obj.cache, loss_obj.random_states): + for (reps_mb, _), grad_mb in zip( + loss_obj.embed_minibatch_iter( + sentence_feature=sentence_feature, + with_grad=True, + copy_random_state=False, + random_states=random_states, + ), + grad, + ): + surrogate = torch.dot(reps_mb.flatten(), grad_mb.flatten()) * grad_output + surrogate.backward() + + +class CachedMultipleNegativesSymmetricRankingLoss(nn.Module): + def __init__( + self, + model: SentenceTransformer, + scale: float = 20.0, + similarity_fct: callable[[Tensor, Tensor], Tensor] = util.cos_sim, + mini_batch_size: int = 32, + show_progress_bar: bool = False, + ) -> None: + """ + Boosted version of :class:`MultipleNegativesSymmetricRankingLoss` (MNSRL) by GradCache (https://arxiv.org/pdf/2101.06983.pdf). + + Given a list of (anchor, positive) pairs, MNSRL sums the following two losses: + + 1. Forward loss: Given an anchor, find the sample with the highest similarity out of all positives in the batch. + 2. Backward loss: Given a positive, find the sample with the highest similarity out of all anchors in the batch. + + For example with question-answer pairs, the forward loss finds the answer for a given question and the backward loss + finds the question for a given answer. This loss is common in symmetric tasks, such as semantic textual similarity. + + The caching modification allows for large batch sizes (which give a better training signal) with constant memory usage, + allowing you to reach optimal training signal with regular hardware. + + Note: If you pass triplets, the negative entry will be ignored. An anchor is just searched for the positive. + + Args: + model: SentenceTransformer model + scale: Output of similarity function is multiplied by scale value + similarity_fct: similarity function between sentence embeddings. By default, cos_sim. + Can also be set to dot product (and then set scale to 1) + mini_batch_size: Mini-batch size for the forward pass, this denotes how much memory is actually used during + training and evaluation. The larger the mini-batch size, the more memory efficient the training is, but + the slower the training will be. + show_progress_bar: If True, shows progress bar during processing + + Requirements: + 1. (anchor, positive) pairs + 2. Should be used with large batch sizes for superior performance, but has slower training time than non-cached versions + + Inputs: + +---------------------------------------+--------+ + | Texts | Labels | + +=======================================+========+ + | (anchor, positive) pairs | none | + +---------------------------------------+--------+ + + Recommendations: + - Use ``BatchSamplers.NO_DUPLICATES`` (:class:`docs `) to + ensure that no in-batch negatives are duplicates of the anchor or positive samples. + + Relations: + - Like :class:`MultipleNegativesRankingLoss`, but with an additional symmetric loss term and caching mechanism. + - Inspired by :class:`CachedMultipleNegativesRankingLoss`, adapted for symmetric loss calculation. + + Example: + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + model = SentenceTransformer("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "anchor": ["It's nice weather outside today.", "He drove to work."], + "positive": ["It's so sunny.", "He took the car to the office."], + }) + loss = losses.CachedMultipleNegativesSymmetricRankingLoss(model, mini_batch_size=32) + + trainer = SentenceTransformerTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + + References: + - Efficient Natural Language Response Suggestion for Smart Reply, Section 4.4: https://arxiv.org/pdf/1705.00652.pdf + - Scaling Deep Contrastive Learning Batch Size under Memory Limited Setup: https://arxiv.org/pdf/2101.06983.pdf + """ + super().__init__() + if isinstance(model[0], StaticEmbedding): + raise ValueError( + "CachedMultipleNegativesSymmetricRankingLoss is not compatible with a SentenceTransformer model based on a StaticEmbedding. " + "Consider using MultipleNegativesSymmetricRankingLoss instead." + ) + + self.model = model + self.scale = scale + self.similarity_fct = similarity_fct + self.cross_entropy_loss = nn.CrossEntropyLoss() + self.mini_batch_size = mini_batch_size + self.cache: list[list[Tensor]] | None = None + self.random_states: list[list[RandContext]] | None = None + self.show_progress_bar = show_progress_bar + + def embed_minibatch( + self, + sentence_feature: dict[str, Tensor], + begin: int, + end: int, + with_grad: bool, + copy_random_state: bool, + random_state: RandContext | None = None, + ) -> tuple[Tensor, RandContext | None]: + """Embed a mini-batch of sentences.""" + grad_context = nullcontext if with_grad else torch.no_grad + random_state_context = nullcontext() if random_state is None else random_state + sentence_feature_minibatch = {k: v[begin:end] for k, v in sentence_feature.items()} + with random_state_context: + with grad_context(): + random_state = RandContext(*sentence_feature_minibatch.values()) if copy_random_state else None + reps = self.model(sentence_feature_minibatch)["sentence_embedding"] + return reps, random_state + + def embed_minibatch_iter( + self, + sentence_feature: dict[str, Tensor], + with_grad: bool, + copy_random_state: bool, + random_states: list[RandContext] | None = None, + ) -> Iterator[tuple[Tensor, RandContext | None]]: + """Iterate over mini-batches of sentences for embedding.""" + input_ids: Tensor = sentence_feature["input_ids"] + bsz, _ = input_ids.shape + for i, b in enumerate( + tqdm.trange( + 0, + bsz, + self.mini_batch_size, + desc="Embed mini-batches", + disable=not self.show_progress_bar, + ) + ): + e = b + self.mini_batch_size + reps, random_state = self.embed_minibatch( + sentence_feature=sentence_feature, + begin=b, + end=e, + with_grad=with_grad, + copy_random_state=copy_random_state, + random_state=None if random_states is None else random_states[i], + ) + yield reps, random_state + + def calculate_loss_and_cache_gradients(self, reps: list[list[Tensor]]) -> Tensor: + """Calculate the symmetric loss and cache gradients.""" + loss = self.calculate_loss(reps, with_backward=True) + loss = loss.detach().requires_grad_() + + self.cache = [[r.grad for r in rs] for rs in reps] # e.g. 3 * bsz/mbsz * (mbsz, hdim) + + return loss + + def calculate_loss(self, reps: list[list[Tensor]], with_backward: bool = False) -> Tensor: + """Calculate the symmetric loss without caching gradients (for evaluation).""" + embeddings_a = torch.cat(reps[0]) # (bsz, hdim) + embeddings_b = torch.cat([torch.cat(r) for r in reps[1:]]) # ((1 + nneg) * bsz, hdim) + + batch_size = len(embeddings_a) + labels = torch.arange(batch_size, device=embeddings_a.device) + + losses: list[torch.Tensor] = [] + for b in tqdm.trange( + 0, + batch_size, + self.mini_batch_size, + desc="Calculating loss", + disable=not self.show_progress_bar, + ): + e = min(b + self.mini_batch_size, batch_size) + scores: Tensor = self.similarity_fct(embeddings_a[b:e], embeddings_b) * self.scale + forward_loss: torch.Tensor = self.cross_entropy_loss(scores, labels[b:e]) + + positive_scores = scores[:, b:e] + backward_loss: torch.Tensor = self.cross_entropy_loss(positive_scores.t(), labels[: len(positive_scores)]) + + loss_mbatch = (forward_loss + backward_loss) / 2 + if with_backward: + loss_mbatch.backward() + loss_mbatch = loss_mbatch.detach() + losses.append(loss_mbatch) + + loss = sum(losses) / len(losses) + return loss + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + """Forward pass of the loss function.""" + reps = [] + self.random_states = [] + for sentence_feature in sentence_features: + reps_mbs = [] + random_state_mbs = [] + for reps_mb, random_state in self.embed_minibatch_iter( + sentence_feature=sentence_feature, + with_grad=False, + copy_random_state=True, + ): + reps_mbs.append(reps_mb.detach().requires_grad_()) + random_state_mbs.append(random_state) + reps.append(reps_mbs) + self.random_states.append(random_state_mbs) + + if torch.is_grad_enabled(): + loss = self.calculate_loss_and_cache_gradients(reps) + loss.register_hook(partial(_backward_hook, sentence_features=sentence_features, loss_obj=self)) + else: + loss = self.calculate_loss(reps) + + return loss + + def get_config_dict(self) -> dict[str, Any]: + """Get the configuration of the loss function.""" + return { + "scale": self.scale, + "similarity_fct": self.similarity_fct.__name__, + "mini_batch_size": self.mini_batch_size, + } diff --git a/sentence-transformers/sentence_transformers/losses/CoSENTLoss.py b/sentence-transformers/sentence_transformers/losses/CoSENTLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..561d7f8149e703a1ae5e081b3f993f30a6666f80 --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/CoSENTLoss.py @@ -0,0 +1,128 @@ +from __future__ import annotations + +from collections.abc import Iterable +from typing import Any + +import torch +from torch import Tensor, nn + +from sentence_transformers import util +from sentence_transformers.SentenceTransformer import SentenceTransformer + + +class CoSENTLoss(nn.Module): + def __init__(self, model: SentenceTransformer, scale: float = 20.0, similarity_fct=util.pairwise_cos_sim) -> None: + """ + This class implements CoSENT (Cosine Sentence) loss. + It expects that each of the InputExamples consists of a pair of texts and a float valued label, representing + the expected similarity score between the pair. + + It computes the following loss function: + + ``loss = logsum(1+exp(s(i,j)-s(k,l))+exp...)``, where ``(i,j)`` and ``(k,l)`` are any of the input pairs in the + batch such that the expected similarity of ``(i,j)`` is greater than ``(k,l)``. The summation is over all possible + pairs of input pairs in the batch that match this condition. + + Anecdotal experiments show that this loss function produces a more powerful training signal than :class:`CosineSimilarityLoss`, + resulting in faster convergence and a final model with superior performance. Consequently, CoSENTLoss may be used + as a drop-in replacement for :class:`CosineSimilarityLoss` in any training script. + + Args: + model: SentenceTransformerModel + similarity_fct: Function to compute the PAIRWISE similarity + between embeddings. Default is + ``util.pairwise_cos_sim``. + scale: Output of similarity function is multiplied by scale + value. Represents the inverse temperature. + + References: + - For further details, see: https://kexue.fm/archives/8847 + + Requirements: + - Sentence pairs with corresponding similarity scores in range of the similarity function. Default is [-1,1]. + + Inputs: + +--------------------------------+------------------------+ + | Texts | Labels | + +================================+========================+ + | (sentence_A, sentence_B) pairs | float similarity score | + +--------------------------------+------------------------+ + + Relations: + - :class:`AnglELoss` is CoSENTLoss with ``pairwise_angle_sim`` as the metric, rather than ``pairwise_cos_sim``. + - :class:`CosineSimilarityLoss` seems to produce a weaker training signal than CoSENTLoss. In our experiments, CoSENTLoss is recommended. + + Example: + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + model = SentenceTransformer("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "sentence1": ["It's nice weather outside today.", "He drove to work."], + "sentence2": ["It's so sunny.", "She walked to the store."], + "score": [1.0, 0.3], + }) + loss = losses.CoSENTLoss(model) + + trainer = SentenceTransformerTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.model = model + self.similarity_fct = similarity_fct + self.scale = scale + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + embeddings = [self.model(sentence_feature)["sentence_embedding"] for sentence_feature in sentence_features] + + return self.compute_loss_from_embeddings(embeddings, labels) + + def compute_loss_from_embeddings(self, embeddings: list[Tensor], labels: Tensor) -> Tensor: + """ + Compute the CoSENT loss from embeddings. + + Args: + embeddings: List of embeddings + labels: Labels indicating the similarity scores of the pairs + + Returns: + Loss value + """ + + scores = self.similarity_fct(embeddings[0], embeddings[1]) + scores = scores * self.scale + scores = scores[:, None] - scores[None, :] + + # label matrix indicating which pairs are relevant + labels = labels[:, None] < labels[None, :] + labels = labels.float() + + # mask out irrelevant pairs so they are negligible after exp() + scores = scores - (1 - labels) * 1e12 + + # append a zero as e^0 = 1 + scores = torch.cat((torch.zeros(1).to(scores.device), scores.view(-1)), dim=0) + loss = torch.logsumexp(scores, dim=0) + + return loss + + def get_config_dict(self) -> dict[str, Any]: + return {"scale": self.scale, "similarity_fct": self.similarity_fct.__name__} + + @property + def citation(self) -> str: + return """ +@online{kexuefm-8847, + title={CoSENT: A more efficient sentence vector scheme than Sentence-BERT}, + author={Su Jianlin}, + year={2022}, + month={Jan}, + url={https://kexue.fm/archives/8847}, +} +""" diff --git a/sentence-transformers/sentence_transformers/losses/ContrastiveLoss.py b/sentence-transformers/sentence_transformers/losses/ContrastiveLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..8956aa4cdcaa3ca98347742bd51331fe40a947da --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/ContrastiveLoss.py @@ -0,0 +1,119 @@ +from __future__ import annotations + +from collections.abc import Iterable +from enum import Enum +from typing import Any + +import torch.nn.functional as F +from torch import Tensor, nn + +from sentence_transformers.SentenceTransformer import SentenceTransformer + + +class SiameseDistanceMetric(Enum): + """The metric for the contrastive loss""" + + EUCLIDEAN = lambda x, y: F.pairwise_distance(x, y, p=2) + MANHATTAN = lambda x, y: F.pairwise_distance(x, y, p=1) + COSINE_DISTANCE = lambda x, y: 1 - F.cosine_similarity(x, y) + + +class ContrastiveLoss(nn.Module): + def __init__( + self, + model: SentenceTransformer, + distance_metric=SiameseDistanceMetric.COSINE_DISTANCE, + margin: float = 0.5, + size_average: bool = True, + ) -> None: + """ + Contrastive loss. Expects as input two texts and a label of either 0 or 1. If the label == 1, then the distance between the + two embeddings is reduced. If the label == 0, then the distance between the embeddings is increased. + + Args: + model: SentenceTransformer model + distance_metric: Function that returns a distance between + two embeddings. The class SiameseDistanceMetric contains + pre-defined metrices that can be used + margin: Negative samples (label == 0) should have a distance + of at least the margin value. + size_average: Average by the size of the mini-batch. + + References: + * Further information: http://yann.lecun.com/exdb/publis/pdf/hadsell-chopra-lecun-06.pdf + * `Training Examples > Quora Duplicate Questions <../../../examples/sentence_transformer/training/quora_duplicate_questions/README.html>`_ + + Requirements: + 1. (anchor, positive/negative) pairs + + Inputs: + +-----------------------------------------------+------------------------------+ + | Texts | Labels | + +===============================================+==============================+ + | (anchor, positive/negative) pairs | 1 if positive, 0 if negative | + +-----------------------------------------------+------------------------------+ + + Relations: + - :class:`OnlineContrastiveLoss` is similar, but uses hard positive and hard negative pairs. + It often yields better results. + + Example: + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + model = SentenceTransformer("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "sentence1": ["It's nice weather outside today.", "He drove to work."], + "sentence2": ["It's so sunny.", "She walked to the store."], + "label": [1, 0], + }) + loss = losses.ContrastiveLoss(model) + + trainer = SentenceTransformerTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.distance_metric = distance_metric + self.margin = margin + self.model = model + self.size_average = size_average + + def get_config_dict(self) -> dict[str, Any]: + distance_metric_name = self.distance_metric.__name__ + for name, value in vars(SiameseDistanceMetric).items(): + if value == self.distance_metric: + distance_metric_name = f"SiameseDistanceMetric.{name}" + break + + return {"distance_metric": distance_metric_name, "margin": self.margin, "size_average": self.size_average} + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + reps = [self.model(sentence_feature)["sentence_embedding"] for sentence_feature in sentence_features] + assert len(reps) == 2 + rep_anchor, rep_other = reps + distances = self.distance_metric(rep_anchor, rep_other) + losses = 0.5 * ( + labels.float() * distances.pow(2) + (1 - labels).float() * F.relu(self.margin - distances).pow(2) + ) + return losses.mean() if self.size_average else losses.sum() + + @property + def citation(self) -> str: + return """ +@inproceedings{hadsell2006dimensionality, + author={Hadsell, R. and Chopra, S. and LeCun, Y.}, + booktitle={2006 IEEE Computer Society Conference on Computer Vision and Pattern Recognition (CVPR'06)}, + title={Dimensionality Reduction by Learning an Invariant Mapping}, + year={2006}, + volume={2}, + number={}, + pages={1735-1742}, + doi={10.1109/CVPR.2006.100} +} +""" diff --git a/sentence-transformers/sentence_transformers/losses/ContrastiveTensionLoss.py b/sentence-transformers/sentence_transformers/losses/ContrastiveTensionLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..b1ffabcad4fc5800712f51b37b82ffabc2d38239 --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/ContrastiveTensionLoss.py @@ -0,0 +1,239 @@ +from __future__ import annotations + +import copy +import math +import random +from collections.abc import Iterable + +import numpy as np +import torch +from torch import Tensor, nn + +from sentence_transformers.readers import InputExample +from sentence_transformers.SentenceTransformer import SentenceTransformer +from sentence_transformers.util import cos_sim + + +class ContrastiveTensionLoss(nn.Module): + """ + This loss expects only single sentences, without any labels. Positive and negative pairs are automatically created via random sampling, + such that a positive pair consists of two identical sentences and a negative pair consists of two different sentences. An independent + copy of the encoder model is created, which is used for encoding the first sentence of each pair. The original encoder model encodes the + second sentence. The embeddings are compared and scored using the generated labels (1 if positive, 0 if negative) using the binary cross + entropy objective. + + Note that you must use the `ContrastiveTensionDataLoader` for this loss. The `pos_neg_ratio` of the ContrastiveTensionDataLoader can be + used to determine the number of negative pairs per positive pair. + + Generally, :class:`ContrastiveTensionLossInBatchNegatives` is recommended over this loss, as it gives a stronger training signal. + + Args: + model: SentenceTransformer model + + References: + * Semantic Re-Tuning with Contrastive Tension: https://openreview.net/pdf?id=Ov_sMNau-PF + * `Unsupervised Learning > CT <../../../examples/sentence_transformer/unsupervised_learning/CT/README.html>`_ + + Inputs: + +------------------+--------+ + | Texts | Labels | + +==================+========+ + | single sentences | none | + +------------------+--------+ + + Relations: + * :class:`ContrastiveTensionLossInBatchNegatives` uses in-batch negative sampling, which gives a stronger training signal than this loss. + + Example: + :: + + from sentence_transformers import SentenceTransformer, losses + from sentence_transformers.losses import ContrastiveTensionDataLoader + + model = SentenceTransformer('all-MiniLM-L6-v2') + train_examples = [ + 'This is the 1st sentence', + 'This is the 2nd sentence', + 'This is the 3rd sentence', + 'This is the 4th sentence', + 'This is the 5th sentence', + 'This is the 6th sentence', + 'This is the 7th sentence', + 'This is the 8th sentence', + 'This is the 9th sentence', + 'This is the final sentence', + ] + + train_dataloader = ContrastiveTensionDataLoader(train_examples, batch_size=3, pos_neg_ratio=3) + train_loss = losses.ContrastiveTensionLoss(model=model) + + model.fit( + [(train_dataloader, train_loss)], + epochs=10, + ) + """ + + def __init__(self, model: SentenceTransformer) -> None: + super().__init__() + self.model2 = model # This will be the final model used during the inference time. + self.model1 = copy.deepcopy(model) + self.criterion = nn.BCEWithLogitsLoss(reduction="sum") + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + sentence_features1, sentence_features2 = tuple(sentence_features) + reps_1 = self.model1(sentence_features1)["sentence_embedding"] # (bsz, hdim) + reps_2 = self.model2(sentence_features2)["sentence_embedding"] + + sim_scores = ( + torch.matmul(reps_1[:, None], reps_2[:, :, None]).squeeze(-1).squeeze(-1) + ) # (bsz,) dot product, i.e. S1S2^T + + loss = self.criterion(sim_scores, labels.type_as(sim_scores)) + return loss + + @property + def citation(self) -> str: + return """ +@inproceedings{carlsson2021semantic, + title={Semantic Re-tuning with Contrastive Tension}, + author={Fredrik Carlsson and Amaru Cuba Gyllensten and Evangelia Gogoulou and Erik Ylip{\"a}{\"a} Hellqvist and Magnus Sahlgren}, + booktitle={International Conference on Learning Representations}, + year={2021}, + url={https://openreview.net/forum?id=Ov_sMNau-PF} +} +""" + + +class ContrastiveTensionLossInBatchNegatives(nn.Module): + def __init__(self, model: SentenceTransformer, scale: float = 20.0, similarity_fct=cos_sim) -> None: + """ + This loss expects only single sentences, without any labels. Positive and negative pairs are automatically created via random sampling, + such that a positive pair consists of two identical sentences and a negative pair consists of two different sentences. An independent + copy of the encoder model is created, which is used for encoding the first sentence of each pair. The original encoder model encodes the + second sentence. Unlike :class:`ContrastiveTensionLoss`, this loss uses the batch negative sampling strategy, i.e. the negative pairs + are sampled from the batch. Using in-batch negative sampling gives a stronger training signal than the original :class:`ContrastiveTensionLoss`. + The performance usually increases with increasing batch sizes. + + Note that you should not use the `ContrastiveTensionDataLoader` for this loss, but just a normal DataLoader with `InputExample` instances. + The two texts of each `InputExample` instance should be identical. + + Args: + model: SentenceTransformer model + scale: Output of similarity function is multiplied by scale + value + similarity_fct: similarity function between sentence + embeddings. By default, cos_sim. Can also be set to dot + product (and then set scale to 1) + + References: + - Semantic Re-Tuning with Contrastive Tension: https://openreview.net/pdf?id=Ov_sMNau-PF + - `Unsupervised Learning > CT (In-Batch Negatives) <../../../examples/sentence_transformer/unsupervised_learning/CT_In-Batch_Negatives/README.html>`_ + + Relations: + * :class:`ContrastiveTensionLoss` does not select negative pairs in-batch, resulting in a weaker training signal than this loss. + + Inputs: + +------------------------+--------+ + | Texts | Labels | + +========================+========+ + | (anchor, anchor) pairs | none | + +------------------------+--------+ + + Example: + :: + + from sentence_transformers import SentenceTransformer, losses + from torch.utils.data import DataLoader + + model = SentenceTransformer('all-MiniLM-L6-v2') + train_examples = [ + InputExample(texts=['This is a positive pair', 'Where the distance will be minimized'], label=1), + InputExample(texts=['This is a negative pair', 'Their distance will be increased'], label=0), + ] + train_examples = [ + InputExample(texts=['This is the 1st sentence', 'This is the 1st sentence']), + InputExample(texts=['This is the 2nd sentence', 'This is the 2nd sentence']), + InputExample(texts=['This is the 3rd sentence', 'This is the 3rd sentence']), + InputExample(texts=['This is the 4th sentence', 'This is the 4th sentence']), + InputExample(texts=['This is the 5th sentence', 'This is the 5th sentence']), + ] + + train_dataloader = DataLoader(train_examples, shuffle=True, batch_size=32) + train_loss = losses.ContrastiveTensionLossInBatchNegatives(model=model) + + model.fit( + [(train_dataloader, train_loss)], + epochs=10, + ) + """ + super().__init__() + self.model2 = model # This will be the final model used during the inference time. + self.model1 = copy.deepcopy(model) + self.similarity_fct = similarity_fct + self.cross_entropy_loss = nn.CrossEntropyLoss() + self.logit_scale = nn.Parameter(torch.ones([]) * np.log(scale)) + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + sentence_features1, sentence_features2 = tuple(sentence_features) + embeddings_a = self.model1(sentence_features1)["sentence_embedding"] # (bsz, hdim) + embeddings_b = self.model2(sentence_features2)["sentence_embedding"] + + scores = self.similarity_fct(embeddings_a, embeddings_b) * self.logit_scale.exp() # self.scale + labels = torch.tensor(range(len(scores)), dtype=torch.long, device=scores.device) + return (self.cross_entropy_loss(scores, labels) + self.cross_entropy_loss(scores.t(), labels)) / 2 + + @property + def citation(self) -> str: + return """ +@inproceedings{carlsson2021semantic, + title={Semantic Re-tuning with Contrastive Tension}, + author={Fredrik Carlsson and Amaru Cuba Gyllensten and Evangelia Gogoulou and Erik Ylip{\"a}{\"a} Hellqvist and Magnus Sahlgren}, + booktitle={International Conference on Learning Representations}, + year={2021}, + url={https://openreview.net/forum?id=Ov_sMNau-PF} +} +""" + + +################# CT Data Loader ################# +# For CT, we need batches in a specific format +# In each batch, we have one positive pair (i.e. [sentA, sentA]) and 7 negative pairs (i.e. [sentA, sentB]). +# To achieve this, we create a custom DataLoader that produces batches with this property + + +class ContrastiveTensionDataLoader: + def __init__(self, sentences, batch_size, pos_neg_ratio=8): + self.sentences = sentences + self.batch_size = batch_size + self.pos_neg_ratio = pos_neg_ratio + self.collate_fn = None + + if self.batch_size % self.pos_neg_ratio != 0: + raise ValueError( + f"ContrastiveTensionDataLoader was loaded with a pos_neg_ratio of {pos_neg_ratio} and a batch size of {batch_size}. The batch size must be divisible by the pos_neg_ratio" + ) + + def __iter__(self): + random.shuffle(self.sentences) + sentence_idx = 0 + batch = [] + + while sentence_idx + 1 < len(self.sentences): + s1 = self.sentences[sentence_idx] + if len(batch) % self.pos_neg_ratio > 0: # Negative (different) pair + sentence_idx += 1 + s2 = self.sentences[sentence_idx] + label = 0 + else: # Positive (identical pair) + s2 = self.sentences[sentence_idx] + label = 1 + + sentence_idx += 1 + batch.append(InputExample(texts=[s1, s2], label=label)) + + if len(batch) >= self.batch_size: + yield self.collate_fn(batch) if self.collate_fn is not None else batch + batch = [] + + def __len__(self): + return math.floor(len(self.sentences) / (2 * self.batch_size)) diff --git a/sentence-transformers/sentence_transformers/losses/CosineSimilarityLoss.py b/sentence-transformers/sentence_transformers/losses/CosineSimilarityLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..4cfe503a98ba82080b58064b369065341f991f4c --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/CosineSimilarityLoss.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +from collections.abc import Iterable +from typing import Any + +import torch +from torch import Tensor, nn + +from sentence_transformers.SentenceTransformer import SentenceTransformer +from sentence_transformers.util import fullname + + +class CosineSimilarityLoss(nn.Module): + def __init__( + self, + model: SentenceTransformer, + loss_fct: nn.Module = nn.MSELoss(), + cos_score_transformation: nn.Module = nn.Identity(), + ) -> None: + """ + CosineSimilarityLoss expects that the InputExamples consists of two texts and a float label. It computes the + vectors ``u = model(sentence_A)`` and ``v = model(sentence_B)`` and measures the cosine-similarity between the two. + By default, it minimizes the following loss: ``||input_label - cos_score_transformation(cosine_sim(u,v))||_2``. + + Args: + model: SentenceTransformer model + loss_fct: Which pytorch loss function should be used to + compare the ``cosine_similarity(u, v)`` with the + input_label? By default, MSE is used: ``||input_label - + cosine_sim(u, v)||_2`` + cos_score_transformation: The cos_score_transformation + function is applied on top of cosine_similarity. By + default, the identify function is used (i.e. no change). + + References: + - `Training Examples > Semantic Textual Similarity <../../../examples/sentence_transformer/training/sts/README.html>`_ + + Requirements: + 1. Sentence pairs with corresponding similarity scores in range `[0, 1]` + + Inputs: + +--------------------------------+------------------------+ + | Texts | Labels | + +================================+========================+ + | (sentence_A, sentence_B) pairs | float similarity score | + +--------------------------------+------------------------+ + + Relations: + - :class:`CoSENTLoss` seems to produce a stronger training signal than CosineSimilarityLoss. In our experiments, CoSENTLoss is recommended. + - :class:`AnglELoss` is :class:`CoSENTLoss` with ``pairwise_angle_sim`` as the metric, rather than ``pairwise_cos_sim``. It also produces a stronger training signal than CosineSimilarityLoss. + + Example: + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + model = SentenceTransformer("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "sentence1": ["It's nice weather outside today.", "He drove to work."], + "sentence2": ["It's so sunny.", "She walked to the store."], + "score": [1.0, 0.3], + }) + loss = losses.CosineSimilarityLoss(model) + + trainer = SentenceTransformerTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.model = model + self.loss_fct = loss_fct + self.cos_score_transformation = cos_score_transformation + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + embeddings = [self.model(sentence_feature)["sentence_embedding"] for sentence_feature in sentence_features] + + return self.compute_loss_from_embeddings(embeddings, labels) + + def compute_loss_from_embeddings(self, embeddings: list[Tensor], labels: Tensor) -> Tensor: + """ + Compute the CosineSimilarity loss from embeddings. + + Args: + embeddings: List of embeddings + labels: Labels indicating the similarity scores of the pairs + + Returns: + Loss value + """ + output = self.cos_score_transformation(torch.cosine_similarity(embeddings[0], embeddings[1])) + return self.loss_fct(output, labels.float().view(-1)) + + def get_config_dict(self) -> dict[str, Any]: + return {"loss_fct": fullname(self.loss_fct)} diff --git a/sentence-transformers/sentence_transformers/losses/DenoisingAutoEncoderLoss.py b/sentence-transformers/sentence_transformers/losses/DenoisingAutoEncoderLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..e080239d6ba1d71b703682970a9f47669895052c --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/DenoisingAutoEncoderLoss.py @@ -0,0 +1,202 @@ +from __future__ import annotations + +import logging +from collections.abc import Iterable + +from torch import Tensor, nn +from transformers import AutoConfig, AutoModelForCausalLM, AutoTokenizer, PreTrainedModel + +from sentence_transformers import SentenceTransformer +from sentence_transformers.models import StaticEmbedding + +logger = logging.getLogger(__name__) + + +class DenoisingAutoEncoderLoss(nn.Module): + def __init__( + self, model: SentenceTransformer, decoder_name_or_path: str | None = None, tie_encoder_decoder: bool = True + ) -> None: + r""" + This loss expects as input a pairs of damaged sentences and the corresponding original ones. + During training, the decoder reconstructs the original sentences from the encoded sentence embeddings. + Here the argument 'decoder_name_or_path' indicates the pretrained model (supported by Hugging Face) to be used as the decoder. + Since decoding process is included, here the decoder should have a class called XXXLMHead (in the context of Hugging Face's Transformers). + The 'tie_encoder_decoder' flag indicates whether to tie the trainable parameters of encoder and decoder, + which is shown beneficial to model performance while limiting the amount of required memory. + Only when the encoder and decoder are from the same architecture, can the flag 'tie_encoder_decoder' work. + + The data generation process (i.e. the 'damaging' process) has already been implemented in ``DenoisingAutoEncoderDataset``, + allowing you to only provide regular sentences. + + Args: + model (SentenceTransformer): The SentenceTransformer model. + decoder_name_or_path (str, optional): Model name or path for initializing a decoder (compatible with Hugging Face's Transformers). Defaults to None. + tie_encoder_decoder (bool): Whether to tie the trainable parameters of encoder and decoder. Defaults to True. + + References: + * TSDAE paper: https://arxiv.org/pdf/2104.06979.pdf + * `Unsupervised Learning > TSDAE <../../../examples/sentence_transformer/unsupervised_learning/TSDAE/README.html>`_ + + Requirements: + 1. The decoder should have a class called XXXLMHead (in the context of Hugging Face's Transformers) + 2. Should use a large corpus + + Inputs: + +------------------------------------------------------+--------+ + | Texts | Labels | + +======================================================+========+ + | (damaged\_sentence, original\_sentence) pairs | none | + +------------------------------------------------------+--------+ + | sentence fed through ``DenoisingAutoEncoderDataset`` | none | + +------------------------------------------------------+--------+ + + Example: + :: + + from sentence_transformers import SentenceTransformer, losses + from sentence_transformers.datasets import DenoisingAutoEncoderDataset + from torch.utils.data import DataLoader + + model_name = "bert-base-cased" + model = SentenceTransformer(model_name) + train_sentences = [ + "First training sentence", "Second training sentence", "Third training sentence", "Fourth training sentence", + ] + batch_size = 2 + train_dataset = DenoisingAutoEncoderDataset(train_sentences) + train_dataloader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, drop_last=True) + train_loss = losses.DenoisingAutoEncoderLoss( + model, decoder_name_or_path=model_name, tie_encoder_decoder=True + ) + model.fit( + train_objectives=[(train_dataloader, train_loss)], + epochs=10, + ) + """ + super().__init__() + + if isinstance(model[0], StaticEmbedding): + raise ValueError( + "DenoisingAutoEncoderLoss is not compatible with a SentenceTransformer model based on a StaticEmbedding." + ) + + self.encoder = model # This will be the final model used during the inference time. + self.tokenizer_encoder = model.tokenizer + + encoder_name_or_path = model.transformers_model.config._name_or_path + if decoder_name_or_path is None: + assert ( + tie_encoder_decoder + ), "Must indicate the decoder_name_or_path argument when tie_encoder_decoder=False!" + if tie_encoder_decoder: + if decoder_name_or_path: + logger.warning("When tie_encoder_decoder=True, the decoder_name_or_path will be invalid.") + decoder_name_or_path = encoder_name_or_path + + self.tokenizer_decoder = AutoTokenizer.from_pretrained(decoder_name_or_path) + self.need_retokenization = not isinstance(self.tokenizer_encoder, type(self.tokenizer_decoder)) + + decoder_config = AutoConfig.from_pretrained(decoder_name_or_path) + decoder_config.is_decoder = True + decoder_config.add_cross_attention = True + kwargs_decoder = {"config": decoder_config} + try: + self.decoder = AutoModelForCausalLM.from_pretrained(decoder_name_or_path, **kwargs_decoder) + except ValueError as e: + logger.error( + f'Model name or path "{decoder_name_or_path}" does not support being as a decoder. Please make sure the decoder model has an "XXXLMHead" class.' + ) + raise e + assert model.transformers_model.config.hidden_size == decoder_config.hidden_size, "Hidden sizes do not match!" + if self.tokenizer_decoder.pad_token is None: + # Needed by GPT-2, etc. + self.tokenizer_decoder.pad_token = self.tokenizer_decoder.eos_token + self.decoder.config.pad_token_id = self.decoder.config.eos_token_id + + if len(AutoTokenizer.from_pretrained(encoder_name_or_path)) != len(self.tokenizer_encoder): + logger.warning( + "WARNING: The vocabulary of the encoder has been changed. One might need to change the decoder vocabulary, too." + ) + + if tie_encoder_decoder: + assert not self.need_retokenization, "The tokenizers should be the same when tie_encoder_decoder=True." + if len(self.tokenizer_encoder) != len(self.tokenizer_decoder): # The vocabulary has been changed. + self.tokenizer_decoder = self.tokenizer_encoder + self.decoder.resize_token_embeddings(len(self.tokenizer_decoder)) + logger.warning( + "Since the encoder vocabulary has been changed and --tie_encoder_decoder=True, now the new vocabulary has also been used for the decoder." + ) + decoder_base_model_prefix = self.decoder.base_model_prefix + try: + # Compatibility with transformers <4.40.0 + PreTrainedModel._tie_encoder_decoder_weights( + model.transformers_model, + self.decoder._modules[decoder_base_model_prefix], + self.decoder.base_model_prefix, + ) + except TypeError: + # Compatibility with transformers >=4.40.0 + PreTrainedModel._tie_encoder_decoder_weights( + model.transformers_model, + self.decoder._modules[decoder_base_model_prefix], + self.decoder.base_model_prefix, + encoder_name_or_path, + ) + + def retokenize(self, sentence_features: dict[str, Tensor]) -> dict[str, Tensor]: + input_ids = sentence_features["input_ids"] + device = input_ids.device + sentences_decoded = self.tokenizer_encoder.batch_decode( + input_ids, skip_special_tokens=True, clean_up_tokenization_spaces=True + ) + retokenized = self.tokenizer_decoder( + sentences_decoded, padding=True, truncation="longest_first", return_tensors="pt", max_length=None + ).to(device) + return retokenized + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + source_features, target_features = tuple(sentence_features) + if self.need_retokenization: + # since the sentence_features here are all tokenized by encoder's tokenizer, + # retokenization by the decoder's one is needed if different tokenizers used + target_features = self.retokenize(target_features) + reps = self.encoder(source_features)["sentence_embedding"] # (bsz, hdim) + + # Prepare input and output + target_length = target_features["input_ids"].shape[1] + decoder_input_ids = target_features["input_ids"].clone()[:, : target_length - 1] + label_ids = target_features["input_ids"][:, 1:] + + # Decode + decoder_outputs = self.decoder( + input_ids=decoder_input_ids, + inputs_embeds=None, + attention_mask=None, + encoder_hidden_states=reps[:, None], # (bsz, hdim) -> (bsz, 1, hdim) + encoder_attention_mask=source_features["attention_mask"][:, 0:1], + labels=None, + return_dict=None, + use_cache=False, + ) + + # Calculate loss + lm_logits = decoder_outputs[0] + ce_loss_fct = nn.CrossEntropyLoss(ignore_index=self.tokenizer_decoder.pad_token_id) + loss = ce_loss_fct(lm_logits.view(-1, lm_logits.shape[-1]), label_ids.reshape(-1)) + return loss + + @property + def citation(self) -> str: + return """ +@inproceedings{wang-2021-TSDAE, + title = "TSDAE: Using Transformer-based Sequential Denoising Auto-Encoderfor Unsupervised Sentence Embedding Learning", + author = "Wang, Kexin and Reimers, Nils and Gurevych, Iryna", + booktitle = "Findings of the Association for Computational Linguistics: EMNLP 2021", + month = nov, + year = "2021", + address = "Punta Cana, Dominican Republic", + publisher = "Association for Computational Linguistics", + pages = "671--688", + url = "https://arxiv.org/abs/2104.06979", +} +""" diff --git a/sentence-transformers/sentence_transformers/losses/DistillKLDivLoss.py b/sentence-transformers/sentence_transformers/losses/DistillKLDivLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..bd8334dadf0008222322c4025e002f80d9504937 --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/DistillKLDivLoss.py @@ -0,0 +1,173 @@ +from __future__ import annotations + +from collections.abc import Iterable + +import torch +from torch import Tensor, nn + +from sentence_transformers import SentenceTransformer, util + + +class DistillKLDivLoss(nn.Module): + def __init__( + self, model: SentenceTransformer, similarity_fct=util.pairwise_dot_score, temperature: float = 1.0 + ) -> None: + """ + Compute the KL divergence loss between probability distributions derived from student and teacher models' similarity scores. + By default, similarity is calculated using the dot-product. This loss is designed for knowledge distillation + where a smaller student model learns from a more powerful teacher model. + + The loss computes softmax probabilities from the teacher similarity scores and log-softmax probabilities + from the student model, then calculates the KL divergence between these distributions. + + Args: + model: SentenceTransformer model (student model) + similarity_fct: Which similarity function to use for the student model + temperature: Temperature parameter to soften probability distributions (higher temperature = softer distributions) + A temperature of 1.0 does not scale the scores. Note: in the v5.0.1 release, the default temperature was changed from 2.0 to 1.0. + + References: + - For more details, please refer to https://arxiv.org/abs/2010.11386 + + Requirements: + 1. (query, positive, negative_1, ..., negative_n) examples + 2. Labels containing teacher model's scores between query-positive and query-negative pairs + + Inputs: + +------------------------------------------------+------------------------------------------------------------+ + | Texts | Labels | + +================================================+============================================================+ + | (query, positive, negative) | [Teacher(query, positive), Teacher(query, negative)] | + +------------------------------------------------+------------------------------------------------------------+ + | (query, positive, negative_1, ..., negative_n) | [Teacher(query, positive), Teacher(query, negative_i)...] | + +------------------------------------------------+------------------------------------------------------------+ + + Relations: + - Similar to :class:`~sentence_transformers.losses.MarginMSELoss` but uses KL divergence instead of MSE + - More suited for distillation tasks where preserving ranking is important + + Example: + + Using a teacher model to compute similarity scores for distillation: + + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + import torch + + student_model = SentenceTransformer("microsoft/mpnet-base") + teacher_model = SentenceTransformer("all-mpnet-base-v2") + train_dataset = Dataset.from_dict({ + "query": ["It's nice weather outside today.", "He drove to work."], + "positive": ["It's so sunny.", "He took the car to work."], + "negative": ["It's very cold.", "She walked to the store."], + }) + + def compute_labels(batch): + emb_queries = teacher_model.encode(batch["query"]) + emb_positives = teacher_model.encode(batch["positive"]) + emb_negatives = teacher_model.encode(batch["negative"]) + + pos_scores = teacher_model.similarity_pairwise(emb_queries, emb_positives) + neg_scores = teacher_model.similarity_pairwise(emb_queries, emb_negatives) + + # Stack the scores for positive and negative pairs + return { + "label": torch.stack([pos_scores, neg_scores], dim=1) + } + + train_dataset = train_dataset.map(compute_labels, batched=True) + loss = losses.DistillKLDivLoss(student_model) + + trainer = SentenceTransformerTrainer(model=student_model, train_dataset=train_dataset, loss=loss) + trainer.train() + + With multiple negatives: + + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + import torch + + student_model = SentenceTransformer("microsoft/mpnet-base") + teacher_model = SentenceTransformer("all-mpnet-base-v2") + + train_dataset = Dataset.from_dict( + { + "query": ["It's nice weather outside today.", "He drove to work."], + "positive": ["It's so sunny.", "He took the car to work."], + "negative1": ["It's very cold.", "She walked to the store."], + "negative2": ["Its rainy", "She took the bus"], + } + ) + + + def compute_labels(batch): + emb_queries = teacher_model.encode(batch["query"]) + emb_positives = teacher_model.encode(batch["positive"]) + emb_negatives1 = teacher_model.encode(batch["negative1"]) + emb_negatives2 = teacher_model.encode(batch["negative2"]) + + pos_scores = teacher_model.similarity_pairwise(emb_queries, emb_positives) + neg_scores1 = teacher_model.similarity_pairwise(emb_queries, emb_negatives1) + neg_scores2 = teacher_model.similarity_pairwise(emb_queries, emb_negatives2) + + # Stack the scores for positive and multiple negative pairs + return { + "label": torch.stack([pos_scores, neg_scores1, neg_scores2], dim=1) + } + + train_dataset = train_dataset.map(compute_labels, batched=True) + loss = losses.DistillKLDivLoss(student_model) + + trainer = SentenceTransformerTrainer(model=student_model, train_dataset=train_dataset, loss=loss) + trainer.train() + """ + super().__init__() + self.model = model + self.similarity_fct = similarity_fct + self.temperature = temperature + self.loss_fct = nn.KLDivLoss(reduction="batchmean") + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + embeddings = [self.model(sentence_feature)["sentence_embedding"] for sentence_feature in sentence_features] + + return self.compute_loss_from_embeddings(embeddings, labels) + + def compute_loss_from_embeddings(self, embeddings: list[Tensor], labels: Tensor) -> Tensor: + embeddings_query = embeddings[0] + + # Compute student scores + student_scores = torch.stack( + [self.similarity_fct(embeddings_query, embeddings_other) for embeddings_other in embeddings[1:]], + dim=1, + ) + # Scale student scores by temperature to soften distributions, then apply log-softmax + student_scores = student_scores / self.temperature + student_log_probs = torch.log_softmax(student_scores, dim=1) + + # Compute teacher scores + teacher_scores = labels / self.temperature + teacher_probs = torch.softmax(teacher_scores, dim=1) + + # Compute the KL Divergence + loss = self.loss_fct(student_log_probs, teacher_probs) + # Scale the loss to counteract the temperature scaling + loss = loss * (self.temperature**2) + return loss + + @property + def citation(self) -> str: + return """ +@misc{lin2020distillingdenserepresentationsranking, + title={Distilling Dense Representations for Ranking using Tightly-Coupled Teachers}, + author={Sheng-Chieh Lin and Jheng-Hong Yang and Jimmy Lin}, + year={2020}, + eprint={2010.11386}, + archivePrefix={arXiv}, + primaryClass={cs.IR}, + url={https://arxiv.org/abs/2010.11386}, +} +""" diff --git a/sentence-transformers/sentence_transformers/losses/GISTEmbedLoss.py b/sentence-transformers/sentence_transformers/losses/GISTEmbedLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..de99b2772032b8ea9967f08846e4234864ffc06a --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/GISTEmbedLoss.py @@ -0,0 +1,225 @@ +from __future__ import annotations + +from collections.abc import Iterable +from typing import Any, Literal + +import torch +from torch import Tensor, nn +from transformers import PreTrainedTokenizerBase + +from sentence_transformers.models import StaticEmbedding +from sentence_transformers.SentenceTransformer import SentenceTransformer + + +class GISTEmbedLoss(nn.Module): + def __init__( + self, + model: SentenceTransformer, + guide: SentenceTransformer, + temperature: float = 0.01, + margin_strategy: Literal["absolute", "relative"] = "absolute", + margin: float = 0.0, + ) -> None: + """ + This loss is used to train a SentenceTransformer model using the GISTEmbed algorithm. + It takes a model and a guide model as input, and uses the guide model to guide the + in-batch negative sample selection. The cosine similarity is used to compute the loss + and the temperature parameter is used to scale the cosine similarities. + + You can apply different false-negative filtering strategies to discard hard negatives that are too similar to + the positive. Two strategies are supported: + + - "absolute": Discards negatives whose similarity score is greater than or equal to ``positive_score - margin``. + - "relative": Discards negatives whose similarity score is greater than or equal to ``positive_score * (1 - margin)``. + + Args: + model: SentenceTransformer model based on a `transformers` model. + guide: SentenceTransformer model to guide the in-batch negative sample selection. + temperature: Temperature parameter to scale the cosine similarities. + margin_strategy: Strategy used for false negative filtering. One of {"absolute", "relative"}. + margin: The margin value for filtering negatives. Defaults to 0.0, together with the "absolute" strategy, + this only removes negatives that are more similar to the query than the positive is to the query. + + References: + - For further details, see: https://arxiv.org/abs/2402.16829 + + Requirements: + 1. (anchor, positive, negative) triplets + 2. (anchor, positive) pairs + + Inputs: + +---------------------------------------+--------+ + | Texts | Labels | + +=======================================+========+ + | (anchor, positive, negative) triplets | none | + +---------------------------------------+--------+ + | (anchor, positive) pairs | none | + +---------------------------------------+--------+ + + Recommendations: + - Use ``BatchSamplers.NO_DUPLICATES`` (:class:`docs `) to + ensure that no in-batch negatives are duplicates of the anchor or positive samples. + + Relations: + - :class:`MultipleNegativesRankingLoss` is similar to this loss, but it does not use + a guide model to guide the in-batch negative sample selection. `GISTEmbedLoss` yields + a stronger training signal at the cost of some training overhead. + + Example: + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + model = SentenceTransformer("microsoft/mpnet-base") + guide = SentenceTransformer("all-MiniLM-L6-v2") + train_dataset = Dataset.from_dict({ + "anchor": ["It's nice weather outside today.", "He drove to work."], + "positive": ["It's so sunny.", "He took the car to the office."], + }) + loss = losses.GISTEmbedLoss(model, guide) + + trainer = SentenceTransformerTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.model = model + self.guide = guide + self.temperature = temperature + self.similarity_fct = nn.CosineSimilarity(dim=-1) + if not hasattr(model, "tokenizer") or not hasattr(guide, "tokenizer"): + raise ValueError("Both the training model and the guiding model must have a tokenizer attribute.") + if not isinstance(model.tokenizer, PreTrainedTokenizerBase) or not isinstance( + guide.tokenizer, PreTrainedTokenizerBase + ): + raise ValueError( + "Both the training model and the guiding model must use a PreTrainedTokenizer from transformers." + ) + self.must_retokenize = ( + model.tokenizer.get_vocab() != guide.tokenizer.get_vocab() or guide.max_seq_length < model.max_seq_length + ) + if self.must_retokenize: + self.tokenizer = self.model.tokenizer + + if isinstance(self.model[0], StaticEmbedding): + raise ValueError( + "If we must retokenize because the guide model has a different tokenizer, " + "then the Sentence Transformer model must not be based on a StaticEmbedding." + ) + + if margin_strategy not in ("absolute", "relative"): + raise ValueError("margin_strategy must be 'absolute' or 'relative'.") + self.margin_strategy = margin_strategy + self.margin = margin + + def sim_matrix(self, embed1: Tensor, embed2: Tensor) -> Tensor: + return self.similarity_fct(embed1.unsqueeze(1), embed2.unsqueeze(0)) + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + embeddings = [self.model(sentence_feature)["sentence_embedding"] for sentence_feature in sentence_features] + with torch.no_grad(): + if self.must_retokenize: + decoded = [ + self.tokenizer.batch_decode(sentence_feature["input_ids"], skip_special_tokens=True) + for sentence_feature in sentence_features + ] + sentence_features = [self.guide.tokenize(sentences) for sentences in decoded] + sentence_features = [ + {key: value.to(self.guide.device) for key, value in sentence_feature.items()} + for sentence_feature in sentence_features + ] + + guide_embeddings = [ + self.guide(sentence_feature)["sentence_embedding"] for sentence_feature in sentence_features + ] + + negative = None + negative_guide = None + + if len(embeddings) == 2: + anchor, positive = embeddings + anchor_guide, positive_guide = guide_embeddings + elif len(embeddings) == 3: + anchor, positive, negative = embeddings + anchor_guide, positive_guide, negative_guide = guide_embeddings + else: + raise ValueError(f"Expected 2 or 3 embeddings, got {len(embeddings)}") + + # Compute the model's similarities + ap_sim = self.sim_matrix(anchor, positive) + aa_sim = self.sim_matrix(anchor, anchor) + pp_sim = self.sim_matrix(positive, positive) + + # Let's compute the similarity matrices for the combinations of anchor and positive samples. + guided_ap_sim = self.sim_matrix(anchor_guide, positive_guide) + guided_aa_sim = self.sim_matrix(anchor_guide, anchor_guide) + guided_pp_sim = self.sim_matrix(positive_guide, positive_guide) + + # Define the anchor threshold + guided_sim = guided_ap_sim.diagonal().view(-1, 1) + + # This uses guided (teacher) similarity as a dynamic threshold to identify and suppress false negatives + def mask_false_negatives(guided_sim_mat, sim_mat, positive_mask: Tensor | None = None): + if self.margin_strategy == "absolute": + # Remove samples whose guided similarity is higher than (positive_sim - margin) + mask = guided_sim_mat > (guided_sim - self.margin) + elif self.margin_strategy == "relative": + # Remove samples whose guided similarity is higher than (positive_sim * margin) + mask = guided_sim_mat > (guided_sim * (1 - self.margin)) + + if positive_mask is not None: + # Ensure true positive pairs are not masked out + mask = mask & ~positive_mask + sim_mat[mask] = -torch.inf + return sim_mat + + # Create a mask to protect true positive pairs in the anchor-positive matrix (i.e., diagonal elements) + positive_mask = torch.eye(*guided_ap_sim.shape, dtype=torch.bool, device=guided_ap_sim.device) + + # Apply false negative suppression to each similarity matrix using guided similarity as anchor + ap_sim = mask_false_negatives(guided_ap_sim, ap_sim, positive_mask=positive_mask) # anchor-positive + aa_sim = mask_false_negatives(guided_aa_sim, aa_sim) # anchor-anchor + pp_sim = mask_false_negatives(guided_pp_sim, pp_sim) # positive-positive + + scores = [ap_sim, aa_sim, pp_sim] + + # Handle the case where we have a negative sample + if negative is not None: + an_sim = self.sim_matrix(anchor, negative) + guided_an_sim = self.sim_matrix(anchor_guide, negative_guide) + an_sim = mask_false_negatives(guided_an_sim, an_sim) + + scores.append(an_sim) + + scores = torch.cat(scores, dim=1) / self.temperature + + # NOTE: We use arange here since the ap_sim matrix contains the anchor-positive + # similarities along the diagonal. + labels = torch.arange(scores.size(0)).long().to(scores.device) + + return nn.CrossEntropyLoss()(scores, labels) + + def get_config_dict(self) -> dict[str, Any]: + return { + "guide": self.guide, + "temperature": self.temperature, + "margin_strategy": self.margin_strategy, + "margin": self.margin, + } + + @property + def citation(self) -> str: + return """ +@misc{solatorio2024gistembed, + title={GISTEmbed: Guided In-sample Selection of Training Negatives for Text Embedding Fine-tuning}, + author={Aivin V. Solatorio}, + year={2024}, + eprint={2402.16829}, + archivePrefix={arXiv}, + primaryClass={cs.LG} +} +""" diff --git a/sentence-transformers/sentence_transformers/losses/MSELoss.py b/sentence-transformers/sentence_transformers/losses/MSELoss.py new file mode 100644 index 0000000000000000000000000000000000000000..327e743812b1200f4bb18a7892ebad767089491c --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/MSELoss.py @@ -0,0 +1,97 @@ +from __future__ import annotations + +from collections.abc import Iterable + +import torch +from torch import Tensor, nn + +from sentence_transformers import SentenceTransformer + + +class MSELoss(nn.Module): + def __init__(self, model: SentenceTransformer) -> None: + """ + Computes the MSE loss between the computed sentence embedding and a target sentence embedding. This loss + is used when extending sentence embeddings to new languages as described in our publication + Making Monolingual Sentence Embeddings Multilingual using Knowledge Distillation. + + For an example, see `the distillation documentation <../../../examples/sentence_transformer/training/distillation/README.html>`_ on extending language models to new languages. + + Args: + model: SentenceTransformerModel + + References: + - Making Monolingual Sentence Embeddings Multilingual using Knowledge Distillation: https://arxiv.org/abs/2004.09813 + - `Training > Model Distillation <../../../examples/sentence_transformer/training/distillation/README.html>`_ + - `Training > Multilingual Models <../../../examples/sentence_transformer/training/multilingual/README.html>`_ + + Requirements: + 1. Usually uses a finetuned teacher M in a knowledge distillation setup + + Inputs: + +-----------------------------------------+-----------------------------+ + | Texts | Labels | + +=========================================+=============================+ + | sentence | model sentence embeddings | + +-----------------------------------------+-----------------------------+ + | sentence_1, sentence_2, ..., sentence_N | model sentence embeddings | + +-----------------------------------------+-----------------------------+ + + Relations: + - :class:`MarginMSELoss` is equivalent to this loss, but with a margin through a negative pair. + + Example: + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + student_model = SentenceTransformer("microsoft/mpnet-base") + teacher_model = SentenceTransformer("all-mpnet-base-v2") + train_dataset = Dataset.from_dict({ + "english": ["The first sentence", "The second sentence", "The third sentence", "The fourth sentence"], + "french": ["La première phrase", "La deuxième phrase", "La troisième phrase", "La quatrième phrase"], + }) + + def compute_labels(batch): + return { + "label": teacher_model.encode(batch["english"]) + } + + train_dataset = train_dataset.map(compute_labels, batched=True) + loss = losses.MSELoss(student_model) + + trainer = SentenceTransformerTrainer( + model=student_model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.model = model + self.loss_fct = nn.MSELoss() + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + # Concatenate multiple inputs on the batch dimension + if len(sentence_features) > 1: + embeddings = torch.cat([self.model(inputs)["sentence_embedding"] for inputs in sentence_features], dim=0) + # Repeat the labels for each input + return self.loss_fct(embeddings, labels.repeat(len(sentence_features), 1)) + + embeddings = self.model(sentence_features[0])["sentence_embedding"] + return self.loss_fct(embeddings, labels) + + @property + def citation(self) -> str: + return """ +@inproceedings{reimers-2020-multilingual-sentence-bert, + title = "Making Monolingual Sentence Embeddings Multilingual using Knowledge Distillation", + author = "Reimers, Nils and Gurevych, Iryna", + booktitle = "Proceedings of the 2020 Conference on Empirical Methods in Natural Language Processing", + month = "11", + year = "2020", + publisher = "Association for Computational Linguistics", + url = "https://arxiv.org/abs/2004.09813", +} +""" diff --git a/sentence-transformers/sentence_transformers/losses/MarginMSELoss.py b/sentence-transformers/sentence_transformers/losses/MarginMSELoss.py new file mode 100644 index 0000000000000000000000000000000000000000..85e48b09568d3cc6e596b9553e65e649ff2910d8 --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/MarginMSELoss.py @@ -0,0 +1,226 @@ +from __future__ import annotations + +from collections.abc import Iterable + +import torch +from torch import Tensor, nn + +from sentence_transformers import SentenceTransformer, util + + +class MarginMSELoss(nn.Module): + def __init__(self, model: SentenceTransformer, similarity_fct=util.pairwise_dot_score) -> None: + """ + Compute the MSE loss between the ``|sim(Query, Pos) - sim(Query, Neg)|`` and ``|gold_sim(Query, Pos) - gold_sim(Query, Neg)|``. + By default, sim() is the dot-product. The gold_sim is often the similarity score from a teacher model. + + In contrast to :class:`~sentence_transformers.losses.MultipleNegativesRankingLoss`, the two passages do not + have to be strictly positive and negative, both can be relevant or not relevant for a given query. This can be + an advantage of MarginMSELoss over MultipleNegativesRankingLoss, but note that the MarginMSELoss is much slower + to train. With MultipleNegativesRankingLoss, with a batch size of 64, we compare one query against 128 passages. + With MarginMSELoss, we compare a query only against two passages. It's also possible to use multiple negatives + with MarginMSELoss, but the training would be even slower to train. + + Args: + model: SentenceTransformerModel + similarity_fct: Which similarity function to use. + + References: + - For more details, please refer to https://arxiv.org/abs/2010.02666. + - `Training Examples > MS MARCO <../../../examples/sentence_transformer/training/ms_marco/README.html>`_ + - `Unsupervised Learning > Domain Adaptation <../../../examples/sentence_transformer/domain_adaptation/README.html>`_ + + Requirements: + 1. (query, passage_one, passage_two) triplets or (query, positive, negative_1, ..., negative_n) + 2. Usually used with a finetuned teacher M in a knowledge distillation setup + + Inputs: + +------------------------------------------------+------------------------------------------------------------------------+ + | Texts | Labels | + +================================================+========================================================================+ + | (query, passage_one, passage_two) triplets | M(query, passage_one) - M(query, passage_two) | + +------------------------------------------------+------------------------------------------------------------------------+ + | (query, passage_one, passage_two) triplets | [M(query, passage_one), M(query, passage_two)] | + +------------------------------------------------+------------------------------------------------------------------------+ + | (query, positive, negative_1, ..., negative_n) | [M(query, positive) - M(query, negative_i) for i in 1..n] | + +------------------------------------------------+------------------------------------------------------------------------+ + | (query, positive, negative_1, ..., negative_n) | [M(query, positive), M(query, negative_1), ..., M(query, negative_n)] | + +------------------------------------------------+------------------------------------------------------------------------+ + + Relations: + - :class:`MSELoss` is similar to this loss, but without a margin through the negative pair. + + Example: + + With gold labels, e.g. if you have hard scores for sentences. Imagine you want a model to embed sentences + with similar "quality" close to each other. If the "text1" has quality 5 out of 5, "text2" has quality + 1 out of 5, and "text3" has quality 3 out of 5, then the similarity of a pair can be defined as the + difference of the quality scores. So, the similarity between "text1" and "text2" is 4, and the + similarity between "text1" and "text3" is 2. If we use this as our "Teacher Model", the label becomes + similraity("text1", "text2") - similarity("text1", "text3") = 4 - 2 = 2. + + Positive values denote that the first passage is more similar to the query than the second passage, + while negative values denote the opposite. + + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + model = SentenceTransformer("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "text1": ["It's nice weather outside today.", "He drove to work."], + "text2": ["It's so sunny.", "He took the car to work."], + "text3": ["It's very sunny.", "She walked to the store."], + "label": [0.1, 0.8], + }) + loss = losses.MarginMSELoss(model) + + trainer = SentenceTransformerTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + + We can also use a teacher model to compute the similarity scores. In this case, we can use the teacher model + to compute the similarity scores and use them as the silver labels. This is often used in knowledge distillation. + + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + student_model = SentenceTransformer("microsoft/mpnet-base") + teacher_model = SentenceTransformer("all-mpnet-base-v2") + train_dataset = Dataset.from_dict({ + "query": ["It's nice weather outside today.", "He drove to work."], + "passage1": ["It's so sunny.", "He took the car to work."], + "passage2": ["It's very sunny.", "She walked to the store."], + }) + + def compute_labels(batch): + emb_queries = teacher_model.encode(batch["query"]) + emb_passages1 = teacher_model.encode(batch["passage1"]) + emb_passages2 = teacher_model.encode(batch["passage2"]) + return { + "label": teacher_model.similarity_pairwise(emb_queries, emb_passages1) - teacher_model.similarity_pairwise(emb_queries, emb_passages2) + } + + train_dataset = train_dataset.map(compute_labels, batched=True) + loss = losses.MarginMSELoss(student_model) + + trainer = SentenceTransformerTrainer( + model=student_model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + + We can also use multiple negatives during the knowledge distillation. + + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + import torch + + student_model = SentenceTransformer("microsoft/mpnet-base") + teacher_model = SentenceTransformer("all-mpnet-base-v2") + + train_dataset = Dataset.from_dict( + { + "query": ["It's nice weather outside today.", "He drove to work."], + "passage1": ["It's so sunny.", "He took the car to work."], + "passage2": ["It's very cold.", "She walked to the store."], + "passage3": ["Its rainy", "She took the bus"], + } + ) + + + def compute_labels(batch): + emb_queries = teacher_model.encode(batch["query"]) + emb_passages1 = teacher_model.encode(batch["passage1"]) + emb_passages2 = teacher_model.encode(batch["passage2"]) + emb_passages3 = teacher_model.encode(batch["passage3"]) + return { + "label": torch.stack( + [ + teacher_model.similarity_pairwise(emb_queries, emb_passages1) + - teacher_model.similarity_pairwise(emb_queries, emb_passages2), + teacher_model.similarity_pairwise(emb_queries, emb_passages1) + - teacher_model.similarity_pairwise(emb_queries, emb_passages3), + ], + dim=1, + ) + } + + + train_dataset = train_dataset.map(compute_labels, batched=True) + loss = losses.MarginMSELoss(student_model) + + trainer = SentenceTransformerTrainer(model=student_model, train_dataset=train_dataset, loss=loss) + trainer.train() + """ + super().__init__() + self.model = model + self.similarity_fct = similarity_fct + self.loss_fct = nn.MSELoss() + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + embeddings = [self.model(sentence_feature)["sentence_embedding"] for sentence_feature in sentence_features] + + return self.compute_loss_from_embeddings(embeddings, labels) + + def compute_loss_from_embeddings(self, embeddings: list[Tensor], labels: Tensor) -> Tensor: + # sentence_features: query, positive passage, negative passage(s) + embeddings_query = embeddings[0] + embeddings_pos = embeddings[1] + embeddings_negs = embeddings[2:] + batch_size = embeddings_query.shape[0] + + # Compute similarity scores for positive passage + scores_pos = self.similarity_fct(embeddings_query, embeddings_pos) + + if labels.shape == (batch_size, len(embeddings_negs) + 1): + # If labels are given as a single score for positive and multiple negatives, + # we need to adjust the labels to be the difference between positive and negatives + labels = labels[:, 0].unsqueeze(1) - labels[:, 1:] + + if labels.shape == (batch_size,): + # If labels are given as a single score for positive and multiple negatives, + # we need to adjust the labels to be the difference between positive and negatives + labels = labels.unsqueeze(1) + + if labels.shape != (batch_size, len(embeddings_negs)): + raise ValueError( + f"Labels shape {labels.shape} does not match expected shape {(batch_size, len(embeddings_negs))}. " + "Ensure that your dataset labels/scores are 1) lists of differences between positive scores and " + "negatives scores (length `num_negatives`), or 2) lists of positive and negative scores " + "(length `num_negatives + 1`)." + ) + + # Handle both single and multiple negative cases + if len(embeddings_negs) == 1: + scores_neg = self.similarity_fct(embeddings_query, embeddings_negs[0]) + margin_pred = (scores_pos - scores_neg).unsqueeze(1) + return self.loss_fct(margin_pred, labels) + else: + # Multiple negatives case + scores_negs = [self.similarity_fct(embeddings_query, neg) for neg in embeddings_negs] + margins = [scores_pos - neg_score for neg_score in scores_negs] + margins = torch.stack(margins, dim=1) # Shape: (batch_size, num_negatives) + return self.loss_fct(margins, labels) + + @property + def citation(self) -> str: + return """ +@misc{hofstätter2021improving, + title={Improving Efficient Neural Ranking Models with Cross-Architecture Knowledge Distillation}, + author={Sebastian Hofstätter and Sophia Althammer and Michael Schröder and Mete Sertkan and Allan Hanbury}, + year={2021}, + eprint={2010.02666}, + archivePrefix={arXiv}, + primaryClass={cs.IR} +} +""" diff --git a/sentence-transformers/sentence_transformers/losses/Matryoshka2dLoss.py b/sentence-transformers/sentence_transformers/losses/Matryoshka2dLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..309097f046ef1254235a46656c8c5e89a421147a --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/Matryoshka2dLoss.py @@ -0,0 +1,151 @@ +from __future__ import annotations + +from typing import Any + +from torch.nn import Module + +from sentence_transformers import SentenceTransformer + +from .AdaptiveLayerLoss import AdaptiveLayerLoss +from .MatryoshkaLoss import MatryoshkaLoss + + +class Matryoshka2dLoss(AdaptiveLayerLoss): + def __init__( + self, + model: SentenceTransformer, + loss: Module, + matryoshka_dims: list[int], + matryoshka_weights: list[float | int] | None = None, + n_layers_per_step: int = 1, + n_dims_per_step: int = 1, + last_layer_weight: float = 1.0, + prior_layers_weight: float = 1.0, + kl_div_weight: float = 1.0, + kl_temperature: float = 0.3, + ) -> None: + """ + The Matryoshka2dLoss can be seen as a loss *modifier* that combines the :class:`AdaptiveLayerLoss` and the + :class:`MatryoshkaLoss`. This allows you to train an embedding model that 1) allows users to specify the number + of model layers to use, and 2) allows users to specify the output dimensions to use. + + The former is useful for when you want users to have the option to lower the number of layers used to improve + their inference speed and memory usage, and the latter is useful for when you want users to have the option to + lower the output dimensions to improve the efficiency of their downstream tasks (e.g. retrieval) or to lower + their storage costs. + + Note, this uses `n_layers_per_step=1` and `n_dims_per_step=1` as default, following the original 2DMSE + implementation. + + Args: + model: SentenceTransformer model + loss: The loss function to be used, e.g. + :class:`MultipleNegativesRankingLoss`, + :class:`CoSENTLoss`, etc. + matryoshka_dims: A list of embedding dimensions to be used + for the loss function, e.g. [768, 512, 256, 128, 64]. + matryoshka_weights: A list of weights to be used for the + loss function, e.g. [1, 1, 1, 1, 1]. If None, then the + weights will be set to 1 for all dimensions. + n_layers_per_step: The number of layers to use per step. If + -1, then all layers are used. If > 0, then a random + sample of n_layers_per_step layers are used per step. + The 2DMSE paper uses `n_layers_per_step=1`. The default + value is -1. + n_dims_per_step: The number of dimensions to use per step. + If -1, then all dimensions are used. If > 0, then a + random sample of n_dims_per_step dimensions are used per + step. The default value is -1. + last_layer_weight: The weight to use for the loss of the + final layer. Increase this to focus more on the + performance when using all layers. The default value is + 1.0. + prior_layers_weight: The weight to use for the loss of the + prior layers. Increase this to focus more on the + performance when using fewer layers. The default value + is 1.0. + kl_div_weight: The weight to use for the KL-divergence loss + that is used to make the prior layers match that of the + last layer. Increase this to focus more on the + performance when using fewer layers. The default value + is 1.0. + kl_temperature: The temperature to use for the KL-divergence + loss. If 0, then the KL-divergence loss is not used. The + default value is 1.0. + + References: + - See the 2D Matryoshka Sentence Embeddings (2DMSE) paper: https://arxiv.org/abs/2402.14776 + - `Matryoshka Embeddings <../../../examples/sentence_transformer/training/matryoshka/README.html>`_ + - `Adaptive Layers <../../../examples/sentence_transformer/training/adaptive_layer/README.html>`_ + + Requirements: + 1. The base loss cannot be :class:`CachedMultipleNegativesRankingLoss`, + :class:`CachedMultipleNegativesSymmetricRankingLoss`, or :class:`CachedGISTEmbedLoss`. + + Inputs: + +---------------------------------------+--------+ + | Texts | Labels | + +=======================================+========+ + | any | any | + +---------------------------------------+--------+ + + Relations: + - :class:`MatryoshkaLoss` is used in this loss, and it is responsible for the dimensionality reduction. + - :class:`AdaptiveLayerLoss` is used in this loss, and it is responsible for the layer reduction. + + Example: + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + model = SentenceTransformer("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "anchor": ["It's nice weather outside today.", "He drove to work."], + "positive": ["It's so sunny.", "He took the car to the office."], + }) + loss = losses.MultipleNegativesRankingLoss(model) + loss = losses.Matryoshka2dLoss(model, loss, [768, 512, 256, 128, 64]) + + trainer = SentenceTransformerTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + matryoshka_loss = MatryoshkaLoss( + model, + loss, + matryoshka_dims, + matryoshka_weights=matryoshka_weights, + n_dims_per_step=n_dims_per_step, + ) + super().__init__( + model, + matryoshka_loss, + n_layers_per_step=n_layers_per_step, + last_layer_weight=last_layer_weight, + prior_layers_weight=prior_layers_weight, + kl_div_weight=kl_div_weight, + kl_temperature=kl_temperature, + ) + + def get_config_dict(self) -> dict[str, Any]: + return { + **super().get_config_dict(), + **self.loss.get_config_dict(), + } + + @property + def citation(self) -> str: + return """ +@misc{li20242d, + title={2D Matryoshka Sentence Embeddings}, + author={Xianming Li and Zongxi Li and Jing Li and Haoran Xie and Qing Li}, + year={2024}, + eprint={2402.14776}, + archivePrefix={arXiv}, + primaryClass={cs.CL} +} +""" diff --git a/sentence-transformers/sentence_transformers/losses/MatryoshkaLoss.py b/sentence-transformers/sentence_transformers/losses/MatryoshkaLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..3227457f5ac8812e501186c1f0bd5764b02a8089 --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/MatryoshkaLoss.py @@ -0,0 +1,252 @@ +from __future__ import annotations + +import random +from collections.abc import Iterable +from typing import Any + +import torch +import torch.nn.functional as F +from torch import Tensor, nn + +from sentence_transformers import SentenceTransformer +from sentence_transformers.losses import ( + CachedGISTEmbedLoss, + CachedMultipleNegativesRankingLoss, + CachedMultipleNegativesSymmetricRankingLoss, +) + + +def shrink(tensor: Tensor, dim: int) -> Tensor: + tensor_dim = tensor.shape[-1] + if dim > tensor_dim: + raise ValueError( + f"Dimension {dim} in matryoshka_dims cannot be greater than the model's embedding dimension: {tensor_dim}" + ) + tensor = tensor[..., :dim] + tensor = F.normalize(tensor, p=2, dim=-1) + return tensor + + +class ForwardDecorator: + """ + This decorator is used to cache the output of the Sentence Transformer's forward pass, + so that it can be shrank and reused for multiple loss calculations. This prevents the + model from recalculating the embeddings for each desired Matryoshka dimensionality. + + This decorator is applied to `SentenceTransformer.forward`. + """ + + def __init__(self, fn) -> None: + self.fn = fn + + self.dim = None + self.cache = [] + self.cache_dim = None + self.idx = 0 + + def set_dim(self, dim) -> None: + self.dim = dim + self.idx = 0 + + def __call__(self, features: dict[str, Tensor]) -> dict[str, Tensor]: + # Growing cache: + if self.cache_dim is None or self.cache_dim == self.dim: + output = self.fn(features) + self.cache.append(output) + self.cache_dim = self.dim + # Using cache: + else: + output = self.cache[self.idx] + if "token_embeddings" in output: + output["token_embeddings"] = shrink(output["token_embeddings"], self.dim) + output["sentence_embedding"] = shrink(output["sentence_embedding"], self.dim) + self.idx += 1 + return output + + +class CachedLossDecorator: + """ + This decorator is used with the Cached... losses to compute the underlying loss function + for each Matryoshka dimensionality. This is done by shrinking the pre-computed embeddings + to the desired dimensionality and then passing them to the underlying loss function once + for each desired dimensionality. + + This decorator is applied to the `calculate_loss` method of the Cached... losses. + """ + + def __init__( + self, fn, matryoshka_dims: list[int], matryoshka_weights: list[float | int], n_dims_per_step: int = -1 + ) -> None: + self.fn = fn + self.matryoshka_dims = matryoshka_dims + self.matryoshka_weights = matryoshka_weights + self.n_dims_per_step = n_dims_per_step + + def __call__(self, reps: list[list[Tensor]], *args, **kwargs) -> Tensor: + dim_indices = range(len(self.matryoshka_dims)) + if self.n_dims_per_step > 0 and self.n_dims_per_step < len(dim_indices): + dim_indices = random.sample(dim_indices, self.n_dims_per_step) + + loss = 0.0 + for idx in dim_indices: + dim = self.matryoshka_dims[idx] + weight = self.matryoshka_weights[idx] + + truncated = [[shrink(r, dim) for r in minibatch] for minibatch in reps] + compute_gradients = torch.is_grad_enabled() + # we need to detach the truncated embeddings, + # otherwise the first backward pass of the underlying function will clear the computation graph of the embedding truncation + if compute_gradients: + matryoshka_reps = [[r.detach().requires_grad_() for r in minibatch] for minibatch in truncated] + else: + matryoshka_reps = truncated + loss += weight * self.fn(matryoshka_reps, *args, **kwargs) + # After computing the gradients in minibatches, we need to continue the backward pass through the truncation calculation + # the gradients must be multiplied with the weights because otherwise the matryoshka weights are not considered in the backward pass + if compute_gradients: + for t_minibatch, d_minibatch in zip(truncated, matryoshka_reps): + for t, d in zip(t_minibatch, d_minibatch): + t.backward(weight * d.grad) + return loss + + +class MatryoshkaLoss(nn.Module): + def __init__( + self, + model: SentenceTransformer, + loss: nn.Module, + matryoshka_dims: list[int], + matryoshka_weights: list[float | int] | None = None, + n_dims_per_step: int = -1, + ) -> None: + """ + The MatryoshkaLoss can be seen as a loss *modifier* that allows you to use other loss functions at various + different embedding dimensions. This is useful for when you want to train a model where users have the option + to lower the embedding dimension to improve their embedding comparison speed and costs. + + This loss is also compatible with the Cached... losses, which are in-batch negative losses that allow for + higher batch sizes. The higher batch sizes allow for more negatives, and often result in a stronger model. + + Args: + model: SentenceTransformer model + loss: The loss function to be used, e.g. + :class:`MultipleNegativesRankingLoss`, + :class:`CoSENTLoss`, etc. + matryoshka_dims: A list of embedding dimensions to be used + for the loss function, e.g. [768, 512, 256, 128, 64]. + matryoshka_weights: A list of weights to be used for the + loss function, e.g. [1, 1, 1, 1, 1]. If None, then the + weights will be set to 1 for all dimensions. + n_dims_per_step: The number of dimensions to use per step. + If -1, then all dimensions are used. If > 0, then a + random sample of n_dims_per_step dimensions are used per + step. The default value is -1. + + References: + - The concept was introduced in this paper: https://arxiv.org/abs/2205.13147 + - `Matryoshka Embeddings <../../../examples/sentence_transformer/training/matryoshka/README.html>`_ + + Inputs: + +---------------------------------------+--------+ + | Texts | Labels | + +=======================================+========+ + | any | any | + +---------------------------------------+--------+ + + Relations: + - :class:`Matryoshka2dLoss` uses this loss in combination with :class:`AdaptiveLayerLoss` which allows for + layer reduction for faster inference. + + Example: + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + model = SentenceTransformer("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "anchor": ["It's nice weather outside today.", "He drove to work."], + "positive": ["It's so sunny.", "He took the car to the office."], + }) + loss = losses.MultipleNegativesRankingLoss(model) + loss = losses.MatryoshkaLoss(model, loss, [768, 512, 256, 128, 64]) + + trainer = SentenceTransformerTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.model = model + self.loss = loss + + if matryoshka_weights is None: + matryoshka_weights = [1] * len(matryoshka_dims) + # Sort the dimensions and weights in descending order + dims_weights = zip(matryoshka_dims, matryoshka_weights) + self.matryoshka_dims, self.matryoshka_weights = zip(*sorted(dims_weights, key=lambda x: x[0], reverse=True)) + self.n_dims_per_step = n_dims_per_step + + # The Cached... losses require a special treatment as their backward pass is incompatible with the + # ForwardDecorator approach. Instead, we use a CachedLossDecorator to compute the loss for each + # Matryoshka dimensionality given pre-computed embeddings passed to `calculate_loss`. + self.cached_losses = ( + CachedMultipleNegativesRankingLoss, + CachedGISTEmbedLoss, + CachedMultipleNegativesSymmetricRankingLoss, + ) + if isinstance(loss, self.cached_losses): + loss.calculate_loss = CachedLossDecorator( + loss.calculate_loss, self.matryoshka_dims, self.matryoshka_weights + ) + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + # For the Cached... losses, the CachedLossDecorator has been applied to the `calculate_loss` method, + # so we can directly call the loss function. + if isinstance(self.loss, self.cached_losses): + return self.loss(sentence_features, labels) + + # Otherwise, we apply the ForwardDecorator to the model's forward pass, which will cache the output + # embeddings for each Matryoshka dimensionality, allowing it to be reused for the smaller dimensions. + original_forward = self.model.forward + try: + decorated_forward = ForwardDecorator(original_forward) + self.model.forward = decorated_forward + + dim_indices = range(len(self.matryoshka_dims)) + if self.n_dims_per_step > 0 and self.n_dims_per_step < len(dim_indices): + dim_indices = random.sample(dim_indices, self.n_dims_per_step) + dim_indices.sort() + + loss = 0.0 + for idx in dim_indices: + dim = self.matryoshka_dims[idx] + weight = self.matryoshka_weights[idx] + decorated_forward.set_dim(dim) + loss += weight * self.loss(sentence_features, labels) + finally: + self.model.forward = original_forward + return loss + + def get_config_dict(self) -> dict[str, Any]: + return { + "loss": self.loss.__class__.__name__, + "matryoshka_dims": self.matryoshka_dims, + "matryoshka_weights": self.matryoshka_weights, + "n_dims_per_step": self.n_dims_per_step, + } + + @property + def citation(self) -> str: + return """ +@misc{kusupati2024matryoshka, + title={Matryoshka Representation Learning}, + author={Aditya Kusupati and Gantavya Bhatt and Aniket Rege and Matthew Wallingford and Aditya Sinha and Vivek Ramanujan and William Howard-Snyder and Kaifeng Chen and Sham Kakade and Prateek Jain and Ali Farhadi}, + year={2024}, + eprint={2205.13147}, + archivePrefix={arXiv}, + primaryClass={cs.LG} +} +""" diff --git a/sentence-transformers/sentence_transformers/losses/MegaBatchMarginLoss.py b/sentence-transformers/sentence_transformers/losses/MegaBatchMarginLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..c3bb16b4f7ef01ae97710a631190ef5a63c2d63c --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/MegaBatchMarginLoss.py @@ -0,0 +1,180 @@ +from __future__ import annotations + +from collections.abc import Iterable + +import torch +import torch.nn.functional as F +from torch import Tensor, nn + +from sentence_transformers import SentenceTransformer, util + + +class MegaBatchMarginLoss(nn.Module): + def __init__( + self, + model: SentenceTransformer, + positive_margin: float = 0.8, + negative_margin: float = 0.3, + use_mini_batched_version: bool = True, + mini_batch_size: int = 50, + ) -> None: + """ + Given a large batch (like 500 or more examples) of (anchor_i, positive_i) pairs, find for each pair in the batch + the hardest negative, i.e. find j != i such that cos_sim(anchor_i, positive_j) is maximal. Then create from this a + triplet (anchor_i, positive_i, positive_j) where positive_j serves as the negative for this triplet. + + Then train as with the triplet loss. + + Args: + model: SentenceTransformerModel + positive_margin: Positive margin, cos(anchor, positive) + should be > positive_margin + negative_margin: Negative margin, cos(anchor, negative) + should be < negative_margin + use_mini_batched_version: As large batch sizes require a lot + of memory, we can use a mini-batched version. We break + down the large batch into smaller batches with fewer + examples. + mini_batch_size: Size for the mini-batches. Should be a + divisor for the batch size in your data loader. + + References: + - This loss function was inspired by the ParaNMT paper: https://www.aclweb.org/anthology/P18-1042/ + + Requirements: + 1. (anchor, positive) pairs + 2. Large batches (500 or more examples) + + Inputs: + +---------------------------------------+--------+ + | Texts | Labels | + +=======================================+========+ + | (anchor, positive) pairs | none | + +---------------------------------------+--------+ + + Recommendations: + - Use ``BatchSamplers.NO_DUPLICATES`` (:class:`docs `) to + ensure that no in-batch negatives are duplicates of the anchor or positive samples. + + Example: + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainingArguments, SentenceTransformerTrainer, losses + from datasets import Dataset + + train_batch_size = 250 + train_mini_batch_size = 32 + + model = SentenceTransformer('all-MiniLM-L6-v2') + train_dataset = Dataset.from_dict({ + "anchor": [f"This is sentence number {i}" for i in range(500)], + "positive": [f"This is sentence number {i}" for i in range(1, 501)], + }) + loss = losses.MegaBatchMarginLoss(model=model, mini_batch_size=train_mini_batch_size) + + args = SentenceTransformerTrainingArguments( + output_dir="output", + per_device_train_batch_size=train_batch_size, + ) + trainer = SentenceTransformerTrainer( + model=model, + args=args, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.model = model + self.positive_margin = positive_margin + self.negative_margin = negative_margin + self.mini_batch_size = mini_batch_size + self.forward = self.forward_mini_batched if use_mini_batched_version else self.forward_non_mini_batched + + def forward_mini_batched(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + anchor, positive = sentence_features + feature_names = list(anchor.keys()) + + with torch.no_grad(): + self.model.eval() + all_positive_emb = self.model(positive)["sentence_embedding"].detach() + self.model.train() + + diagonal_matrix = torch.eye(len(all_positive_emb), len(all_positive_emb), device=all_positive_emb.device) + + # Iterate over the triplets (anchor, positive, hardest_negative) in smaller mini_batch sizes + for start_idx in range(0, len(all_positive_emb), self.mini_batch_size): + end_idx = start_idx + self.mini_batch_size + anchor_emb = self.model({key: anchor[key][start_idx:end_idx] for key in feature_names})[ + "sentence_embedding" + ] + + # Find hard negatives. For each anchor, find the hardest negative + # Store them in the triplets (anchor, positive, hardest_negative) + hard_negative_features = {key: [] for key in feature_names} + with torch.no_grad(): + cos_scores = util.pytorch_cos_sim(anchor_emb, all_positive_emb) + negative_scores = ( + cos_scores - 2 * diagonal_matrix[start_idx:end_idx] + ) # Remove positive scores along the diagonal, set them to -1 so that they are not selected by the max() operation + negatives_max, negatives_ids = torch.max(negative_scores, dim=1) + + for hard_negative_id in negatives_ids: + for key in feature_names: + hard_negative_features[key].append(positive[key][hard_negative_id]) + + for key in feature_names: + hard_negative_features[key] = torch.stack(hard_negative_features[key]) + + # Compute differentiable negative and positive embeddings + positive_emb = self.model({key: positive[key][start_idx:end_idx] for key in feature_names})[ + "sentence_embedding" + ] + negative_emb = self.model(hard_negative_features)["sentence_embedding"] + + assert anchor_emb.shape == positive_emb.shape + assert anchor_emb.shape == negative_emb.shape + + # Compute loss + pos_cosine = F.cosine_similarity(anchor_emb, positive_emb) + neg_cosine = F.cosine_similarity(anchor_emb, negative_emb) + losses = F.relu(self.positive_margin - pos_cosine) + F.relu(neg_cosine - self.negative_margin) + losses = losses.mean() + + # Backpropagate unless it is the last mini batch. The last mini-batch will be back propagated by the outside train loop + if end_idx < len(cos_scores): + losses.backward() + + return losses + + ##### Non mini-batched version ### + def forward_non_mini_batched(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + reps = [self.model(sentence_feature)["sentence_embedding"] for sentence_feature in sentence_features] + embeddings_a, embeddings_b = reps + + cos_scores = util.pytorch_cos_sim(embeddings_a, embeddings_b) + positive_scores = torch.diagonal(cos_scores) + negative_scores = cos_scores - ( + 2 * torch.eye(*cos_scores.shape, device=cos_scores.device) + ) # Remove positive scores along the diagonal + negatives_max, _ = torch.max(negative_scores, dim=1) + losses = F.relu(self.positive_margin - positive_scores) + F.relu(negatives_max - self.negative_margin) + return losses.mean() + + @property + def citation(self) -> str: + return """ +@inproceedings{wieting-gimpel-2018-paranmt, + title = "{P}ara{NMT}-50{M}: Pushing the Limits of Paraphrastic Sentence Embeddings with Millions of Machine Translations", + author = "Wieting, John and Gimpel, Kevin", + editor = "Gurevych, Iryna and Miyao, Yusuke", + booktitle = "Proceedings of the 56th Annual Meeting of the Association for Computational Linguistics (Volume 1: Long Papers)", + month = jul, + year = "2018", + address = "Melbourne, Australia", + publisher = "Association for Computational Linguistics", + url = "https://aclanthology.org/P18-1042", + doi = "10.18653/v1/P18-1042", + pages = "451--462", +} +""" diff --git a/sentence-transformers/sentence_transformers/losses/MultipleNegativesRankingLoss.py b/sentence-transformers/sentence_transformers/losses/MultipleNegativesRankingLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..afc6c1545c0b106f8eddff909f35bf1999fb26df --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/MultipleNegativesRankingLoss.py @@ -0,0 +1,145 @@ +from __future__ import annotations + +from collections.abc import Iterable +from typing import Any + +import torch +from torch import Tensor, nn + +from sentence_transformers import util +from sentence_transformers.SentenceTransformer import SentenceTransformer + + +class MultipleNegativesRankingLoss(nn.Module): + def __init__(self, model: SentenceTransformer, scale: float = 20.0, similarity_fct=util.cos_sim) -> None: + """ + Given a list of (anchor, positive) pairs or (anchor, positive, negative) triplets, this loss optimizes the following: + + 1. Given an anchor (e.g. a question), assign the highest similarity to the corresponding positive (i.e. answer) + out of every single positive and negative (e.g. all answers) in the batch. + + If you provide the optional negatives, they will all be used as extra options from which the model must pick the + correct positive. Within reason, the harder this "picking" is, the stronger the model will become. Because of + this, a higher batch size results in more in-batch negatives, which then increases performance (to a point). + + This loss function works great to train embeddings for retrieval setups where you have positive pairs + (e.g. (query, answer)) as it will sample in each batch ``n-1`` negative docs randomly. + + This loss is also known as InfoNCE loss, SimCSE loss, Cross-Entropy Loss with in-batch negatives, or simply + in-batch negatives loss. + + Args: + model: SentenceTransformer model + scale: Output of similarity function is multiplied by scale + value + similarity_fct: similarity function between sentence + embeddings. By default, cos_sim. Can also be set to dot + product (and then set scale to 1) + + References: + - Efficient Natural Language Response Suggestion for Smart Reply, Section 4.4: https://arxiv.org/pdf/1705.00652.pdf + - `Training Examples > Natural Language Inference <../../../examples/sentence_transformer/training/nli/README.html>`_ + - `Training Examples > Paraphrase Data <../../../examples/sentence_transformer/training/paraphrases/README.html>`_ + - `Training Examples > Quora Duplicate Questions <../../../examples/sentence_transformer/training/quora_duplicate_questions/README.html>`_ + - `Training Examples > MS MARCO <../../../examples/sentence_transformer/training/ms_marco/README.html>`_ + - `Unsupervised Learning > SimCSE <../../../examples/sentence_transformer/unsupervised_learning/SimCSE/README.html>`_ + - `Unsupervised Learning > GenQ <../../../examples/sentence_transformer/unsupervised_learning/query_generation/README.html>`_ + + Requirements: + 1. (anchor, positive) pairs or (anchor, positive, negative) triplets + + Inputs: + +-------------------------------------------------+--------+ + | Texts | Labels | + +=================================================+========+ + | (anchor, positive) pairs | none | + +-------------------------------------------------+--------+ + | (anchor, positive, negative) triplets | none | + +-------------------------------------------------+--------+ + | (anchor, positive, negative_1, ..., negative_n) | none | + +-------------------------------------------------+--------+ + + Recommendations: + - Use ``BatchSamplers.NO_DUPLICATES`` (:class:`docs `) to + ensure that no in-batch negatives are duplicates of the anchor or positive samples. + + Relations: + - :class:`CachedMultipleNegativesRankingLoss` is equivalent to this loss, but it uses caching that allows for + much higher batch sizes (and thus better performance) without extra memory usage. However, it is slightly + slower. + - :class:`MultipleNegativesSymmetricRankingLoss` is equivalent to this loss, but with an additional loss term. + - :class:`GISTEmbedLoss` is equivalent to this loss, but uses a guide model to guide the in-batch negative + sample selection. `GISTEmbedLoss` yields a stronger training signal at the cost of some training overhead. + + Example: + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + model = SentenceTransformer("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "anchor": ["It's nice weather outside today.", "He drove to work."], + "positive": ["It's so sunny.", "He took the car to the office."], + }) + loss = losses.MultipleNegativesRankingLoss(model) + + trainer = SentenceTransformerTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.model = model + self.scale = scale + self.similarity_fct = similarity_fct + self.cross_entropy_loss = nn.CrossEntropyLoss() + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + # Compute the embeddings and distribute them to anchor and candidates (positive and optionally negatives) + embeddings = [self.model(sentence_feature)["sentence_embedding"] for sentence_feature in sentence_features] + + return self.compute_loss_from_embeddings(embeddings, labels) + + def compute_loss_from_embeddings(self, embeddings: list[Tensor], labels: Tensor) -> Tensor: + """ + Compute the multiple negatives ranking loss from embeddings. + + Args: + embeddings: List of embeddings + + Returns: + Loss value + """ + + anchors = embeddings[0] # (batch_size, embedding_dim) + candidates = torch.cat(embeddings[1:]) # (batch_size * (1 + num_negatives), embedding_dim) + + # For every anchor, we compute the similarity to all other candidates (positives and negatives), + # also from other anchors. This gives us a lot of in-batch negatives. + scores = self.similarity_fct(anchors, candidates) * self.scale + # (batch_size, batch_size * (1 + num_negatives)) + + # anchor[i] should be most similar to candidates[i], as that is the paired positive, + # so the label for anchor[i] is i + range_labels = torch.arange(0, scores.size(0), device=scores.device) + + return self.cross_entropy_loss(scores, range_labels) + + def get_config_dict(self) -> dict[str, Any]: + return {"scale": self.scale, "similarity_fct": self.similarity_fct.__name__} + + @property + def citation(self) -> str: + return """ +@misc{henderson2017efficient, + title={Efficient Natural Language Response Suggestion for Smart Reply}, + author={Matthew Henderson and Rami Al-Rfou and Brian Strope and Yun-hsuan Sung and Laszlo Lukacs and Ruiqi Guo and Sanjiv Kumar and Balint Miklos and Ray Kurzweil}, + year={2017}, + eprint={1705.00652}, + archivePrefix={arXiv}, + primaryClass={cs.CL} +} +""" diff --git a/sentence-transformers/sentence_transformers/losses/MultipleNegativesSymmetricRankingLoss.py b/sentence-transformers/sentence_transformers/losses/MultipleNegativesSymmetricRankingLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..7a2e488ff6234f15a38493c3566f72b0425295ca --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/MultipleNegativesSymmetricRankingLoss.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +from collections.abc import Iterable +from typing import Any + +import torch +from torch import Tensor, nn + +from sentence_transformers import util +from sentence_transformers.SentenceTransformer import SentenceTransformer + + +class MultipleNegativesSymmetricRankingLoss(nn.Module): + def __init__(self, model: SentenceTransformer, scale: float = 20.0, similarity_fct=util.cos_sim) -> None: + """ + Given a list of (anchor, positive) pairs, this loss sums the following two losses: + + 1. Forward loss: Given an anchor, find the sample with the highest similarity out of all positives in the batch. + This is equivalent to :class:`MultipleNegativesRankingLoss`. + 2. Backward loss: Given a positive, find the sample with the highest similarity out of all anchors in the batch. + + For example with question-answer pairs, :class:`MultipleNegativesRankingLoss` just computes the loss to find + the answer given a question, but :class:`MultipleNegativesSymmetricRankingLoss` additionally computes the + loss to find the question given an answer. + + Note: If you pass triplets, the negative entry will be ignored. A anchor is just searched for the positive. + + Args: + model: SentenceTransformer model + scale: Output of similarity function is multiplied by scale + value + similarity_fct: similarity function between sentence + embeddings. By default, cos_sim. Can also be set to dot + product (and then set scale to 1) + + Requirements: + 1. (anchor, positive) pairs + + Inputs: + +---------------------------------------+--------+ + | Texts | Labels | + +=======================================+========+ + | (anchor, positive) pairs | none | + +---------------------------------------+--------+ + + Recommendations: + - Use ``BatchSamplers.NO_DUPLICATES`` (:class:`docs `) to + ensure that no in-batch negatives are duplicates of the anchor or positive samples. + + Relations: + - Like :class:`MultipleNegativesRankingLoss`, but with an additional loss term. + - :class:`CachedMultipleNegativesSymmetricRankingLoss` is equivalent to this loss, but it uses caching that + allows for much higher batch sizes (and thus better performance) without extra memory usage. However, it + is slightly slower. + + Example: + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + model = SentenceTransformer("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "anchor": ["It's nice weather outside today.", "He drove to work."], + "positive": ["It's so sunny.", "He took the car to the office."], + }) + loss = losses.MultipleNegativesSymmetricRankingLoss(model) + + trainer = SentenceTransformerTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.model = model + self.scale = scale + self.similarity_fct = similarity_fct + self.cross_entropy_loss = nn.CrossEntropyLoss() + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + reps = [self.model(sentence_feature)["sentence_embedding"] for sentence_feature in sentence_features] + anchor = reps[0] + candidates = torch.cat(reps[1:]) + + scores = self.similarity_fct(anchor, candidates) * self.scale + labels = torch.tensor( + range(len(scores)), dtype=torch.long, device=scores.device + ) # Example a[i] should match with b[i] + + anchor_positive_scores = scores[:, 0 : len(reps[1])] + forward_loss = self.cross_entropy_loss(scores, labels) + backward_loss = self.cross_entropy_loss(anchor_positive_scores.transpose(0, 1), labels) + return (forward_loss + backward_loss) / 2 + + def get_config_dict(self) -> dict[str, Any]: + return {"scale": self.scale, "similarity_fct": self.similarity_fct.__name__} diff --git a/sentence-transformers/sentence_transformers/losses/OnlineContrastiveLoss.py b/sentence-transformers/sentence_transformers/losses/OnlineContrastiveLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..8b149662e789b1c4f09496a534e68f6d0c27fdbe --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/OnlineContrastiveLoss.py @@ -0,0 +1,88 @@ +from __future__ import annotations + +from collections.abc import Iterable + +import torch.nn.functional as F +from torch import Tensor, nn + +from sentence_transformers.SentenceTransformer import SentenceTransformer + +from .ContrastiveLoss import SiameseDistanceMetric + + +class OnlineContrastiveLoss(nn.Module): + def __init__( + self, model: SentenceTransformer, distance_metric=SiameseDistanceMetric.COSINE_DISTANCE, margin: float = 0.5 + ) -> None: + """ + This Online Contrastive loss is similar to :class:`ConstrativeLoss`, but it selects hard positive (positives that + are far apart) and hard negative pairs (negatives that are close) and computes the loss only for these pairs. + This loss often yields better performances than ContrastiveLoss. + + Args: + model: SentenceTransformer model + distance_metric: Function that returns a distance between + two embeddings. The class SiameseDistanceMetric contains + pre-defined metrics that can be used + margin: Negative samples (label == 0) should have a distance + of at least the margin value. + + References: + - `Training Examples > Quora Duplicate Questions <../../../examples/sentence_transformer/training/quora_duplicate_questions/README.html>`_ + + Requirements: + 1. (anchor, positive/negative) pairs + 2. Data should include hard positives and hard negatives + + Inputs: + +-----------------------------------------------+------------------------------+ + | Texts | Labels | + +===============================================+==============================+ + | (anchor, positive/negative) pairs | 1 if positive, 0 if negative | + +-----------------------------------------------+------------------------------+ + + Relations: + - :class:`ContrastiveLoss` is similar, but does not use hard positive and hard negative pairs. + :class:`OnlineContrastiveLoss` often yields better results. + + Example: + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + model = SentenceTransformer("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "sentence1": ["It's nice weather outside today.", "He drove to work."], + "sentence2": ["It's so sunny.", "She walked to the store."], + "label": [1, 0], + }) + loss = losses.OnlineContrastiveLoss(model) + + trainer = SentenceTransformerTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.model = model + self.margin = margin + self.distance_metric = distance_metric + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor, size_average=False) -> Tensor: + embeddings = [self.model(sentence_feature)["sentence_embedding"] for sentence_feature in sentence_features] + + distance_matrix = self.distance_metric(embeddings[0], embeddings[1]) + negs = distance_matrix[labels == 0] + poss = distance_matrix[labels == 1] + + # select hard positive and hard negative pairs + negative_pairs = negs[negs < (poss.max() if len(poss) > 1 else negs.mean())] + positive_pairs = poss[poss > (negs.min() if len(negs) > 1 else poss.mean())] + + positive_loss = positive_pairs.pow(2).sum() + negative_loss = F.relu(self.margin - negative_pairs).pow(2).sum() + loss = positive_loss + negative_loss + return loss diff --git a/sentence-transformers/sentence_transformers/losses/SoftmaxLoss.py b/sentence-transformers/sentence_transformers/losses/SoftmaxLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..1deb04b45ea7653a1169209dd91aae366cbde27b --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/SoftmaxLoss.py @@ -0,0 +1,155 @@ +from __future__ import annotations + +import logging +from collections.abc import Iterable +from typing import Callable + +import torch +import transformers +from packaging import version +from torch import Tensor, nn + +from sentence_transformers.SentenceTransformer import SentenceTransformer + +logger = logging.getLogger(__name__) + + +class SoftmaxLoss(nn.Module): + def __init__( + self, + model: SentenceTransformer, + sentence_embedding_dimension: int, + num_labels: int, + concatenation_sent_rep: bool = True, + concatenation_sent_difference: bool = True, + concatenation_sent_multiplication: bool = False, + loss_fct: Callable = nn.CrossEntropyLoss(), + ) -> None: + """ + This loss was used in our SBERT publication (https://arxiv.org/abs/1908.10084) to train the SentenceTransformer + model on NLI data. It adds a softmax classifier on top of the output of two transformer networks. + + :class:`MultipleNegativesRankingLoss` is an alternative loss function that often yields better results, + as per https://arxiv.org/abs/2004.09813. + + Args: + model (SentenceTransformer): The SentenceTransformer model. + sentence_embedding_dimension (int): The dimension of the sentence embeddings. + num_labels (int): The number of different labels. + concatenation_sent_rep (bool): Whether to concatenate vectors u,v for the softmax classifier. Defaults to True. + concatenation_sent_difference (bool): Whether to add abs(u-v) for the softmax classifier. Defaults to True. + concatenation_sent_multiplication (bool): Whether to add u*v for the softmax classifier. Defaults to False. + loss_fct (Callable): Custom pytorch loss function. If not set, uses nn.CrossEntropyLoss(). Defaults to nn.CrossEntropyLoss(). + + References: + - Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks: https://arxiv.org/abs/1908.10084 + - `Training Examples > Natural Language Inference <../../../examples/sentence_transformer/training/nli/README.html>`_ + + Requirements: + 1. sentence pairs with a class label + + Inputs: + +---------------------------------------+--------+ + | Texts | Labels | + +=======================================+========+ + | (sentence_A, sentence_B) pairs | class | + +---------------------------------------+--------+ + + Example: + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + model = SentenceTransformer("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "sentence1": [ + "A person on a horse jumps over a broken down airplane.", + "A person on a horse jumps over a broken down airplane.", + "A person on a horse jumps over a broken down airplane.", + "Children smiling and waving at camera", + ], + "sentence2": [ + "A person is training his horse for a competition.", + "A person is at a diner, ordering an omelette.", + "A person is outdoors, on a horse.", + "There are children present.", + ], + "label": [1, 2, 0, 0], + }) + loss = losses.SoftmaxLoss(model, model.get_sentence_embedding_dimension(), num_labels=3) + + trainer = SentenceTransformerTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.model = model + self.num_labels = num_labels + self.concatenation_sent_rep = concatenation_sent_rep + self.concatenation_sent_difference = concatenation_sent_difference + self.concatenation_sent_multiplication = concatenation_sent_multiplication + + num_vectors_concatenated = 0 + if concatenation_sent_rep: + num_vectors_concatenated += 2 + if concatenation_sent_difference: + num_vectors_concatenated += 1 + if concatenation_sent_multiplication: + num_vectors_concatenated += 1 + logger.info(f"Softmax loss: #Vectors concatenated: {num_vectors_concatenated}") + self.classifier = nn.Linear( + num_vectors_concatenated * sentence_embedding_dimension, num_labels, device=model.device + ) + self.loss_fct = loss_fct + + if version.parse(transformers.__version__) < version.parse("4.43.0"): + logger.warning( + "SoftmaxLoss requires transformers >= 4.43.0 to work correctly. " + "Otherwise, the classifier layer that maps embeddings to the labels cannot be updated. " + "Consider updating transformers with `pip install transformers>=4.43.0`." + ) + + def forward( + self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor + ) -> Tensor | tuple[Tensor, Tensor]: + reps = [self.model(sentence_feature)["sentence_embedding"] for sentence_feature in sentence_features] + rep_a, rep_b = reps + + vectors_concat = [] + if self.concatenation_sent_rep: + vectors_concat.append(rep_a) + vectors_concat.append(rep_b) + + if self.concatenation_sent_difference: + vectors_concat.append(torch.abs(rep_a - rep_b)) + + if self.concatenation_sent_multiplication: + vectors_concat.append(rep_a * rep_b) + + features = torch.cat(vectors_concat, 1) + + output = self.classifier(features) + + if labels is not None: + loss = self.loss_fct(output, labels.view(-1)) + return loss + else: + return reps, output + + @property + def citation(self) -> str: + return """ +@inproceedings{reimers-2019-sentence-bert, + title = "Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks", + author = "Reimers, Nils and Gurevych, Iryna", + booktitle = "Proceedings of the 2019 Conference on Empirical Methods in Natural Language Processing", + month = "11", + year = "2019", + publisher = "Association for Computational Linguistics", + url = "https://arxiv.org/abs/1908.10084", +} +""" diff --git a/sentence-transformers/sentence_transformers/losses/TripletLoss.py b/sentence-transformers/sentence_transformers/losses/TripletLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..6c8c09f27e99f1d0b094490322e4ca672760a2ed --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/TripletLoss.py @@ -0,0 +1,124 @@ +from __future__ import annotations + +from collections.abc import Iterable +from enum import Enum +from typing import Any + +import torch.nn.functional as F +from torch import Tensor, nn + +from sentence_transformers.SentenceTransformer import SentenceTransformer +from sentence_transformers.util import pairwise_cos_sim, pairwise_euclidean_sim, pairwise_manhattan_sim + + +class TripletDistanceMetric(Enum): + """The metric for the triplet loss""" + + COSINE = lambda x, y: 1 - pairwise_cos_sim(x, y) + EUCLIDEAN = lambda x, y: pairwise_euclidean_sim(x, y) + MANHATTAN = lambda x, y: pairwise_manhattan_sim(x, y) + + +class TripletLoss(nn.Module): + def __init__( + self, model: SentenceTransformer, distance_metric=TripletDistanceMetric.EUCLIDEAN, triplet_margin: float = 5 + ) -> None: + """ + This class implements triplet loss. Given a triplet of (anchor, positive, negative), + the loss minimizes the distance between anchor and positive while it maximizes the distance + between anchor and negative. It compute the following loss function: + + ``loss = max(||anchor - positive|| - ||anchor - negative|| + margin, 0)``. + + Margin is an important hyperparameter and needs to be tuned respectively. + + Args: + model: SentenceTransformerModel + distance_metric: Function to compute distance between two + embeddings. The class TripletDistanceMetric contains + common distance metrices that can be used. + triplet_margin: The negative should be at least this much + further away from the anchor than the positive. + + References: + - For further details, see: https://en.wikipedia.org/wiki/Triplet_loss + + Requirements: + 1. (anchor, positive, negative) triplets + + Inputs: + +---------------------------------------+--------+ + | Texts | Labels | + +=======================================+========+ + | (anchor, positive, negative) triplets | none | + +---------------------------------------+--------+ + + Example: + :: + + from sentence_transformers import SentenceTransformer, SentenceTransformerTrainer, losses + from datasets import Dataset + + model = SentenceTransformer("microsoft/mpnet-base") + train_dataset = Dataset.from_dict({ + "anchor": ["It's nice weather outside today.", "He drove to work."], + "positive": ["It's so sunny.", "He took the car to the office."], + "negative": ["It's quite rainy, sadly.", "She walked to the store."], + }) + loss = losses.TripletLoss(model=model) + + trainer = SentenceTransformerTrainer( + model=model, + train_dataset=train_dataset, + loss=loss, + ) + trainer.train() + """ + super().__init__() + self.model = model + self.distance_metric = distance_metric + self.triplet_margin = triplet_margin + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + embeddings = [self.model(sentence_feature)["sentence_embedding"] for sentence_feature in sentence_features] + + return self.compute_loss_from_embeddings(embeddings, labels) + + def compute_loss_from_embeddings(self, embeddings: list[Tensor], labels: Tensor) -> Tensor: + """ + Compute the CoSENT loss from embeddings. + + Args: + embeddings: List of embeddings + + Returns: + Loss value + """ + rep_anchor, rep_pos, rep_neg = embeddings + distance_pos = self.distance_metric(rep_anchor, rep_pos) + distance_neg = self.distance_metric(rep_anchor, rep_neg) + + losses = F.relu(distance_pos - distance_neg + self.triplet_margin) + return losses.mean() + + def get_config_dict(self) -> dict[str, Any]: + distance_metric_name = self.distance_metric.__name__ + for name, value in vars(TripletDistanceMetric).items(): + if value == self.distance_metric: + distance_metric_name = f"TripletDistanceMetric.{name}" + break + + return {"distance_metric": distance_metric_name, "triplet_margin": self.triplet_margin} + + @property + def citation(self) -> str: + return """ +@misc{hermans2017defense, + title={In Defense of the Triplet Loss for Person Re-Identification}, + author={Alexander Hermans and Lucas Beyer and Bastian Leibe}, + year={2017}, + eprint={1703.07737}, + archivePrefix={arXiv}, + primaryClass={cs.CV} +} +""" diff --git a/sentence-transformers/sentence_transformers/losses/__init__.py b/sentence-transformers/sentence_transformers/losses/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4c5f8912f6ba1837d7a5caf12dd4eb9ea3ee82ba --- /dev/null +++ b/sentence-transformers/sentence_transformers/losses/__init__.py @@ -0,0 +1,73 @@ +# CoSENTLoss must be imported before AnglELoss +from __future__ import annotations + +from .CoSENTLoss import CoSENTLoss # isort: skip + +from .AdaptiveLayerLoss import AdaptiveLayerLoss +from .AnglELoss import AnglELoss +from .BatchAllTripletLoss import BatchAllTripletLoss +from .BatchHardSoftMarginTripletLoss import BatchHardSoftMarginTripletLoss +from .BatchHardTripletLoss import ( + BatchHardTripletLoss, + BatchHardTripletLossDistanceFunction, +) +from .BatchSemiHardTripletLoss import BatchSemiHardTripletLoss +from .CachedGISTEmbedLoss import CachedGISTEmbedLoss +from .CachedMultipleNegativesRankingLoss import CachedMultipleNegativesRankingLoss +from .CachedMultipleNegativesSymmetricRankingLoss import ( + CachedMultipleNegativesSymmetricRankingLoss, +) +from .ContrastiveLoss import ContrastiveLoss, SiameseDistanceMetric +from .ContrastiveTensionLoss import ( + ContrastiveTensionDataLoader, + ContrastiveTensionLoss, + ContrastiveTensionLossInBatchNegatives, +) +from .CosineSimilarityLoss import CosineSimilarityLoss +from .DenoisingAutoEncoderLoss import DenoisingAutoEncoderLoss +from .DistillKLDivLoss import DistillKLDivLoss +from .GISTEmbedLoss import GISTEmbedLoss +from .MarginMSELoss import MarginMSELoss +from .Matryoshka2dLoss import Matryoshka2dLoss +from .MatryoshkaLoss import MatryoshkaLoss +from .MegaBatchMarginLoss import MegaBatchMarginLoss +from .MSELoss import MSELoss +from .MultipleNegativesRankingLoss import MultipleNegativesRankingLoss +from .MultipleNegativesSymmetricRankingLoss import MultipleNegativesSymmetricRankingLoss +from .OnlineContrastiveLoss import OnlineContrastiveLoss +from .SoftmaxLoss import SoftmaxLoss +from .TripletLoss import TripletDistanceMetric, TripletLoss + +__all__ = [ + "AdaptiveLayerLoss", + "CosineSimilarityLoss", + "SoftmaxLoss", + "MultipleNegativesRankingLoss", + "MultipleNegativesSymmetricRankingLoss", + "TripletLoss", + "TripletDistanceMetric", + "MarginMSELoss", + "MatryoshkaLoss", + "Matryoshka2dLoss", + "MSELoss", + "ContrastiveLoss", + "SiameseDistanceMetric", + "CachedGISTEmbedLoss", + "CachedMultipleNegativesRankingLoss", + "CachedMultipleNegativesSymmetricRankingLoss", + "ContrastiveTensionLoss", + "ContrastiveTensionLossInBatchNegatives", + "ContrastiveTensionDataLoader", + "CoSENTLoss", + "AnglELoss", + "OnlineContrastiveLoss", + "MegaBatchMarginLoss", + "DenoisingAutoEncoderLoss", + "GISTEmbedLoss", + "BatchHardTripletLoss", + "BatchHardTripletLossDistanceFunction", + "BatchHardSoftMarginTripletLoss", + "BatchSemiHardTripletLoss", + "BatchAllTripletLoss", + "DistillKLDivLoss", +] diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/AdaptiveLayerLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/AdaptiveLayerLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9a4694636236792ac6d9e1e5905ad3c5d6dc5d3c Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/AdaptiveLayerLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/AnglELoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/AnglELoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..528e1de2388b34ca7e1e49de2ff53f64bafd97bd Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/AnglELoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/BatchAllTripletLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/BatchAllTripletLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3a6ec44c804697aea19930e478542c1a55f06483 Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/BatchAllTripletLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/BatchHardSoftMarginTripletLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/BatchHardSoftMarginTripletLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fae24a2c21360282fd77ff2d654e121c575c3203 Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/BatchHardSoftMarginTripletLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/BatchHardTripletLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/BatchHardTripletLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6a07b52a1b8e78b95f3d7bf06db63e48794aab1f Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/BatchHardTripletLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/BatchSemiHardTripletLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/BatchSemiHardTripletLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..60d451d00fb7b6721545a8ef03a0cb0c8b7c31c6 Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/BatchSemiHardTripletLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/CachedGISTEmbedLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/CachedGISTEmbedLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..66c63154c02117c851d9ded38568cfa07bf0a7f3 Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/CachedGISTEmbedLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/CachedMultipleNegativesRankingLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/CachedMultipleNegativesRankingLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..74267aaf075e7f4841e0672c6fc95329ea8fb277 Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/CachedMultipleNegativesRankingLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/CachedMultipleNegativesSymmetricRankingLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/CachedMultipleNegativesSymmetricRankingLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f9ca0bf96a2e2a0cc41009b14231fe1ba8adba8d Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/CachedMultipleNegativesSymmetricRankingLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/CoSENTLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/CoSENTLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7308f9829bc5edd0f31d91b917b1f4a3363e7ee7 Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/CoSENTLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/ContrastiveLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/ContrastiveLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..425fae4ad893269a8c2c00dbe47b92eefcda2a43 Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/ContrastiveLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/ContrastiveTensionLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/ContrastiveTensionLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dab047c465f587e6847f91ca536a479d5896b8fc Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/ContrastiveTensionLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/CosineSimilarityLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/CosineSimilarityLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..929d39e3721fa3f9e7efd03b1b0d273037558416 Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/CosineSimilarityLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/DenoisingAutoEncoderLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/DenoisingAutoEncoderLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..13d6b6e30af002b96dfa799537602e723603a242 Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/DenoisingAutoEncoderLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/DistillKLDivLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/DistillKLDivLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..077af1bf1539525b623362e5923463678aee1e82 Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/DistillKLDivLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/GISTEmbedLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/GISTEmbedLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0ef1c154597be0202789e60616a70548b6a090cd Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/GISTEmbedLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/MSELoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/MSELoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bd74b48d2d88c104b5bfc88ee96fa5e0d87e9d62 Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/MSELoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/MarginMSELoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/MarginMSELoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7cfd2b212ab2faa08758ec3e80300fcc59088078 Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/MarginMSELoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/Matryoshka2dLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/Matryoshka2dLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5497cd49bb892f13889a2b4a339dde3732a94fb9 Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/Matryoshka2dLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/MatryoshkaLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/MatryoshkaLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..59f290475cf6e4f5177783a08b7b67753f388d26 Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/MatryoshkaLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/MegaBatchMarginLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/MegaBatchMarginLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8df99d0c9a53c3eccc123048942a19685e039cf1 Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/MegaBatchMarginLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/MultipleNegativesRankingLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/MultipleNegativesRankingLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e7a7ae35323b543ffe4de67ca3b5f4539c21f2f Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/MultipleNegativesRankingLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/MultipleNegativesSymmetricRankingLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/MultipleNegativesSymmetricRankingLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bae5352f28b88bb1e9f4e5a8d338fc435f5fd3bc Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/MultipleNegativesSymmetricRankingLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/OnlineContrastiveLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/OnlineContrastiveLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..895272a1eedb5f8c2176aa3ba59cb3e9de31e84e Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/OnlineContrastiveLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/SoftmaxLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/SoftmaxLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..543b9d7deae8c9082229aa6639df30b895b28f0c Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/SoftmaxLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/TripletLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/TripletLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..09a909a1b2540f5357067c72266b9a5e304f50d2 Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/TripletLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/losses/__pycache__/__init__.cpython-312.pyc b/sentence-transformers/sentence_transformers/losses/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..85711fe18d897f6a35cc2f7cb212f69bb3f94af1 Binary files /dev/null and b/sentence-transformers/sentence_transformers/losses/__pycache__/__init__.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/models/__pycache__/BoW.cpython-312.pyc b/sentence-transformers/sentence_transformers/models/__pycache__/BoW.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c308a5e1e062ddd68ec118b78346c72a27e5e7db Binary files /dev/null and b/sentence-transformers/sentence_transformers/models/__pycache__/BoW.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/models/__pycache__/CLIPModel.cpython-312.pyc b/sentence-transformers/sentence_transformers/models/__pycache__/CLIPModel.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5ab673e0bfc5e2d26db0cbc0b1f223e371f8bb9a Binary files /dev/null and b/sentence-transformers/sentence_transformers/models/__pycache__/CLIPModel.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/models/__pycache__/CNN.cpython-312.pyc b/sentence-transformers/sentence_transformers/models/__pycache__/CNN.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ebfa602a04525b5e13fac2aa8d7118c2bd4b14a3 Binary files /dev/null and b/sentence-transformers/sentence_transformers/models/__pycache__/CNN.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/models/__pycache__/Dense.cpython-312.pyc b/sentence-transformers/sentence_transformers/models/__pycache__/Dense.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..687de44deb99349f08ebb64757f5bc2c2503dbcb Binary files /dev/null and b/sentence-transformers/sentence_transformers/models/__pycache__/Dense.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/models/__pycache__/Dropout.cpython-312.pyc b/sentence-transformers/sentence_transformers/models/__pycache__/Dropout.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0098024bc3917853fa89509dcfd83d5507718512 Binary files /dev/null and b/sentence-transformers/sentence_transformers/models/__pycache__/Dropout.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/models/__pycache__/InputModule.cpython-312.pyc b/sentence-transformers/sentence_transformers/models/__pycache__/InputModule.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7059091d51177e242141224725023b69b993d483 Binary files /dev/null and b/sentence-transformers/sentence_transformers/models/__pycache__/InputModule.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/models/__pycache__/LSTM.cpython-312.pyc b/sentence-transformers/sentence_transformers/models/__pycache__/LSTM.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..271978059846322825c5ca5dd247bdcdfb14dc92 Binary files /dev/null and b/sentence-transformers/sentence_transformers/models/__pycache__/LSTM.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/models/__pycache__/LayerNorm.cpython-312.pyc b/sentence-transformers/sentence_transformers/models/__pycache__/LayerNorm.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..304fee3166bfd2ec371e7ef633c605651e084fc4 Binary files /dev/null and b/sentence-transformers/sentence_transformers/models/__pycache__/LayerNorm.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/models/__pycache__/Module.cpython-312.pyc b/sentence-transformers/sentence_transformers/models/__pycache__/Module.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca4f62664d9057367e2ae7d063423cbef7433561 Binary files /dev/null and b/sentence-transformers/sentence_transformers/models/__pycache__/Module.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/models/__pycache__/Normalize.cpython-312.pyc b/sentence-transformers/sentence_transformers/models/__pycache__/Normalize.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4117631419719bf605f931b3706afa18ab9e1f3b Binary files /dev/null and b/sentence-transformers/sentence_transformers/models/__pycache__/Normalize.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/models/__pycache__/Pooling.cpython-312.pyc b/sentence-transformers/sentence_transformers/models/__pycache__/Pooling.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..38db372d4d8b407945c5eb1dc7e32b3a60a49dbb Binary files /dev/null and b/sentence-transformers/sentence_transformers/models/__pycache__/Pooling.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/models/__pycache__/Router.cpython-312.pyc b/sentence-transformers/sentence_transformers/models/__pycache__/Router.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b3c93cf1de664359d169f86dcf928a47675bb217 Binary files /dev/null and b/sentence-transformers/sentence_transformers/models/__pycache__/Router.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/models/__pycache__/StaticEmbedding.cpython-312.pyc b/sentence-transformers/sentence_transformers/models/__pycache__/StaticEmbedding.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d8c19b853557c1df0c0fab418d11465a020f86b8 Binary files /dev/null and b/sentence-transformers/sentence_transformers/models/__pycache__/StaticEmbedding.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/models/__pycache__/Transformer.cpython-312.pyc b/sentence-transformers/sentence_transformers/models/__pycache__/Transformer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2e4f9a80aa43aec5fb466fda981ca208f6ad0265 Binary files /dev/null and b/sentence-transformers/sentence_transformers/models/__pycache__/Transformer.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/models/__pycache__/WeightedLayerPooling.cpython-312.pyc b/sentence-transformers/sentence_transformers/models/__pycache__/WeightedLayerPooling.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f34e79b0d76b0fcb6961130ce4d286c4487bc8c0 Binary files /dev/null and b/sentence-transformers/sentence_transformers/models/__pycache__/WeightedLayerPooling.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/models/__pycache__/WordEmbeddings.cpython-312.pyc b/sentence-transformers/sentence_transformers/models/__pycache__/WordEmbeddings.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..45de998b8d6ddd554c07f6e151310a7949c5ef70 Binary files /dev/null and b/sentence-transformers/sentence_transformers/models/__pycache__/WordEmbeddings.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/models/__pycache__/WordWeights.cpython-312.pyc b/sentence-transformers/sentence_transformers/models/__pycache__/WordWeights.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e95d5f862e32a1730fc2974ef903b1a4eece7779 Binary files /dev/null and b/sentence-transformers/sentence_transformers/models/__pycache__/WordWeights.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/models/__pycache__/__init__.cpython-312.pyc b/sentence-transformers/sentence_transformers/models/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4871300595e9938c83889da7f2296375a1d9e712 Binary files /dev/null and b/sentence-transformers/sentence_transformers/models/__pycache__/__init__.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/models/tokenizer/PhraseTokenizer.py b/sentence-transformers/sentence_transformers/models/tokenizer/PhraseTokenizer.py new file mode 100644 index 0000000000000000000000000000000000000000..0a120b159a74665e99ab8ebb6da06ab72a61278b --- /dev/null +++ b/sentence-transformers/sentence_transformers/models/tokenizer/PhraseTokenizer.py @@ -0,0 +1,122 @@ +from __future__ import annotations + +import collections +import json +import logging +import os +import string +from collections.abc import Iterable + +from transformers.utils.import_utils import NLTK_IMPORT_ERROR, is_nltk_available + +from .WordTokenizer import ENGLISH_STOP_WORDS, WordTokenizer + +logger = logging.getLogger(__name__) + + +class PhraseTokenizer(WordTokenizer): + """Tokenizes the text with respect to existent phrases in the vocab. + + This tokenizers respects phrases that are in the vocab. Phrases are separated with 'ngram_separator', for example, + in Google News word2vec file, ngrams are separated with a _ like New_York. These phrases are detected in text and merged as one special token. (New York is the ... => [New_York, is, the]) + """ + + def __init__( + self, + vocab: Iterable[str] = [], + stop_words: Iterable[str] = ENGLISH_STOP_WORDS, + do_lower_case: bool = False, + ngram_separator: str = "_", + max_ngram_length: int = 5, + ): + if not is_nltk_available(): + raise ImportError(NLTK_IMPORT_ERROR.format(self.__class__.__name__)) + + self.stop_words = set(stop_words) + self.do_lower_case = do_lower_case + self.ngram_separator = ngram_separator + self.max_ngram_length = max_ngram_length + self.set_vocab(vocab) + + def get_vocab(self): + return self.vocab + + def set_vocab(self, vocab: Iterable[str]): + self.vocab = vocab + self.word2idx = collections.OrderedDict([(word, idx) for idx, word in enumerate(vocab)]) + + # Check for ngram in vocab + self.ngram_lookup = set() + self.ngram_lengths = set() + for word in vocab: + if self.ngram_separator is not None and self.ngram_separator in word: + # Sum words might me malformed in e.g. google news word2vec, containing two or more _ after each other + ngram_count = word.count(self.ngram_separator) + 1 + if self.ngram_separator + self.ngram_separator not in word and ngram_count <= self.max_ngram_length: + self.ngram_lookup.add(word) + self.ngram_lengths.add(ngram_count) + + if len(vocab) > 0: + logger.info(f"PhraseTokenizer - Phrase ngram lengths: {self.ngram_lengths}") + logger.info(f"PhraseTokenizer - Num phrases: {len(self.ngram_lookup)}") + + def tokenize(self, text: str, **kwargs) -> list[int]: + from nltk import word_tokenize + + tokens = word_tokenize(text, preserve_line=True) + + # phrase detection + for ngram_len in sorted(self.ngram_lengths, reverse=True): + idx = 0 + while idx <= len(tokens) - ngram_len: + ngram = self.ngram_separator.join(tokens[idx : idx + ngram_len]) + if ngram in self.ngram_lookup: + tokens[idx : idx + ngram_len] = [ngram] + elif ngram.lower() in self.ngram_lookup: + tokens[idx : idx + ngram_len] = [ngram.lower()] + idx += 1 + + # Map tokens to idx, filter stop words + tokens_filtered = [] + for token in tokens: + if token in self.stop_words: + continue + elif token in self.word2idx: + tokens_filtered.append(self.word2idx[token]) + continue + + token = token.lower() + if token in self.stop_words: + continue + elif token in self.word2idx: + tokens_filtered.append(self.word2idx[token]) + continue + + token = token.strip(string.punctuation) + if token in self.stop_words: + continue + elif len(token) > 0 and token in self.word2idx: + tokens_filtered.append(self.word2idx[token]) + continue + + return tokens_filtered + + def save(self, output_path: str): + with open(os.path.join(output_path, "phrasetokenizer_config.json"), "w") as fOut: + json.dump( + { + "vocab": list(self.word2idx.keys()), + "stop_words": list(self.stop_words), + "do_lower_case": self.do_lower_case, + "ngram_separator": self.ngram_separator, + "max_ngram_length": self.max_ngram_length, + }, + fOut, + ) + + @staticmethod + def load(input_path: str): + with open(os.path.join(input_path, "phrasetokenizer_config.json")) as fIn: + config = json.load(fIn) + + return PhraseTokenizer(**config) diff --git a/sentence-transformers/sentence_transformers/models/tokenizer/WhitespaceTokenizer.py b/sentence-transformers/sentence_transformers/models/tokenizer/WhitespaceTokenizer.py new file mode 100644 index 0000000000000000000000000000000000000000..1780538e8e1737c958ceed5ba289fd881d189475 --- /dev/null +++ b/sentence-transformers/sentence_transformers/models/tokenizer/WhitespaceTokenizer.py @@ -0,0 +1,78 @@ +from __future__ import annotations + +import collections +import json +import os +import string +from collections.abc import Iterable + +from .WordTokenizer import ENGLISH_STOP_WORDS, WordTokenizer + + +class WhitespaceTokenizer(WordTokenizer): + """ + Simple and fast white-space tokenizer. Splits sentence based on white spaces. + Punctuation are stripped from tokens. + """ + + def __init__( + self, vocab: Iterable[str] = [], stop_words: Iterable[str] = ENGLISH_STOP_WORDS, do_lower_case: bool = False + ): + self.stop_words = set(stop_words) + self.do_lower_case = do_lower_case + self.set_vocab(vocab) + + def get_vocab(self): + return self.vocab + + def set_vocab(self, vocab: Iterable[str]): + self.vocab = vocab + self.word2idx = collections.OrderedDict([(word, idx) for idx, word in enumerate(vocab)]) + + def tokenize(self, text: str, **kwargs) -> list[int]: + if self.do_lower_case: + text = text.lower() + + tokens = text.split() + + tokens_filtered = [] + for token in tokens: + if token in self.stop_words: + continue + elif token in self.word2idx: + tokens_filtered.append(self.word2idx[token]) + continue + + token = token.strip(string.punctuation) + if token in self.stop_words: + continue + elif len(token) > 0 and token in self.word2idx: + tokens_filtered.append(self.word2idx[token]) + continue + + token = token.lower() + if token in self.stop_words: + continue + elif token in self.word2idx: + tokens_filtered.append(self.word2idx[token]) + continue + + return tokens_filtered + + def save(self, output_path: str): + with open(os.path.join(output_path, "whitespacetokenizer_config.json"), "w") as fOut: + json.dump( + { + "vocab": list(self.word2idx.keys()), + "stop_words": list(self.stop_words), + "do_lower_case": self.do_lower_case, + }, + fOut, + ) + + @staticmethod + def load(input_path: str): + with open(os.path.join(input_path, "whitespacetokenizer_config.json")) as fIn: + config = json.load(fIn) + + return WhitespaceTokenizer(**config) diff --git a/sentence-transformers/sentence_transformers/models/tokenizer/WordTokenizer.py b/sentence-transformers/sentence_transformers/models/tokenizer/WordTokenizer.py new file mode 100644 index 0000000000000000000000000000000000000000..6cb7208720a63bcf72699a94ecd1593f60d11f7e --- /dev/null +++ b/sentence-transformers/sentence_transformers/models/tokenizer/WordTokenizer.py @@ -0,0 +1,441 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from collections.abc import Iterable + +from transformers import AutoTokenizer, PreTrainedTokenizerBase + +ENGLISH_STOP_WORDS = [ + "!", + '"', + "''", + "``", + "#", + "$", + "%", + "&", + "'", + "(", + ")", + "*", + "+", + ",", + "-", + ".", + "/", + ":", + ";", + "<", + "=", + ">", + "?", + "@", + "[", + "\\", + "]", + "^", + "_", + "`", + "{", + "|", + "}", + "~", + "a", + "about", + "above", + "across", + "after", + "afterwards", + "again", + "against", + "ain", + "all", + "almost", + "alone", + "along", + "already", + "also", + "although", + "always", + "am", + "among", + "amongst", + "amoungst", # codespell:ignore + "amount", + "an", + "and", + "another", + "any", + "anyhow", + "anyone", + "anything", + "anyway", + "anywhere", + "are", + "aren", + "around", + "as", + "at", + "back", + "be", + "became", + "because", + "become", + "becomes", + "becoming", + "been", + "before", + "beforehand", + "behind", + "being", + "below", + "beside", + "besides", + "between", + "beyond", + "bill", + "both", + "bottom", + "but", + "by", + "call", + "can", + "cannot", + "cant", # codespell:ignore + "co", + "con", + "could", + "couldn", + "couldnt", + "cry", + "d", + "de", + "describe", + "detail", + "did", + "didn", + "do", + "does", + "doesn", + "doing", + "don", + "done", + "down", + "due", + "during", + "each", + "eg", + "eight", + "either", + "eleven", + "else", + "elsewhere", + "empty", + "enough", + "etc", + "even", + "ever", + "every", + "everyone", + "everything", + "everywhere", + "except", + "few", + "fifteen", + "fifty", + "fill", + "find", + "fire", + "first", + "five", + "for", + "former", + "formerly", + "forty", + "found", + "four", + "from", + "front", + "full", + "further", + "get", + "give", + "go", + "had", + "hadn", + "has", + "hasn", + "hasnt", + "have", + "haven", + "having", + "he", + "hence", + "her", + "here", + "hereafter", + "hereby", + "herein", + "hereupon", + "hers", + "herself", + "him", + "himself", + "his", + "how", + "however", + "hundred", + "i", + "ie", + "if", + "in", + "inc", + "indeed", + "interest", + "into", + "is", + "isn", + "it", + "its", + "itself", + "just", + "keep", + "last", + "latter", + "latterly", + "least", + "less", + "ll", + "ltd", + "m", + "ma", + "made", + "many", + "may", + "me", + "meanwhile", + "might", + "mightn", + "mill", + "mine", + "more", + "moreover", + "most", + "mostly", + "move", + "much", + "must", + "mustn", + "my", + "myself", + "name", + "namely", + "needn", + "neither", + "never", + "nevertheless", + "next", + "nine", + "no", + "nobody", + "none", + "noone", # codespell:ignore + "nor", + "not", + "nothing", + "now", + "nowhere", + "o", + "of", + "off", + "often", + "on", + "once", + "one", + "only", + "onto", + "or", + "other", + "others", + "otherwise", + "our", + "ours", + "ourselves", + "out", + "over", + "own", + "part", + "per", + "perhaps", + "please", + "put", + "rather", + "re", + "s", + "same", + "see", + "seem", + "seemed", + "seeming", + "seems", + "serious", + "several", + "shan", + "she", + "should", + "shouldn", + "show", + "side", + "since", + "sincere", + "six", + "sixty", + "so", + "some", + "somehow", + "someone", + "something", + "sometime", + "sometimes", + "somewhere", + "still", + "such", + "system", + "t", + "take", + "ten", + "than", + "that", + "the", + "their", + "theirs", + "them", + "themselves", + "then", + "thence", + "there", + "thereafter", + "thereby", + "therefore", + "therein", + "thereupon", + "these", + "they", + "thick", + "thin", + "third", + "this", + "those", + "though", + "three", + "through", + "throughout", + "thru", + "thus", + "to", + "together", + "too", + "top", + "toward", + "towards", + "twelve", + "twenty", + "two", + "un", + "under", + "until", + "up", + "upon", + "us", + "ve", + "very", + "via", + "was", + "wasn", # codespell:ignore + "we", + "well", + "were", + "weren", + "what", + "whatever", + "when", + "whence", + "whenever", + "where", + "whereafter", + "whereas", + "whereby", + "wherein", + "whereupon", + "wherever", + "whether", + "which", + "while", + "whither", + "who", + "whoever", + "whole", + "whom", + "whose", + "why", + "will", + "with", + "within", + "without", + "won", + "would", + "wouldn", + "y", + "yet", + "you", + "your", + "yours", + "yourself", + "yourselves", +] + + +class WordTokenizer(ABC): + @abstractmethod + def set_vocab(self, vocab: Iterable[str]): + pass + + @abstractmethod + def get_vocab(self, vocab: Iterable[str]): + pass + + @abstractmethod + def tokenize(self, text: str, **kwargs) -> list[int]: + pass + + @abstractmethod + def save(self, output_path: str): + pass + + @staticmethod + @abstractmethod + def load(input_path: str): + pass + + +class TransformersTokenizerWrapper(WordTokenizer): + def __init__(self, tokenizer: PreTrainedTokenizerBase): + super().__init__() + self.tokenizer = tokenizer + + def tokenize(self, sentence: str): + encoded = self.tokenizer(sentence) + return encoded["input_ids"][0] + + def set_vocab(self, vocab: Iterable[str]): + pass + + def get_vocab(self, vocab: Iterable[str]): + return self.tokenizer.get_vocab() + + def save(self, output_path: str): + self.tokenizer.save_pretrained(output_path) + + @staticmethod + def load(input_path: str): + return TransformersTokenizerWrapper(AutoTokenizer.from_pretrained(input_path, use_fast=True)) diff --git a/sentence-transformers/sentence_transformers/models/tokenizer/__init__.py b/sentence-transformers/sentence_transformers/models/tokenizer/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7e4268b93e9a0249597965eb2b02461202fc4a30 --- /dev/null +++ b/sentence-transformers/sentence_transformers/models/tokenizer/__init__.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +from .PhraseTokenizer import PhraseTokenizer +from .WhitespaceTokenizer import WhitespaceTokenizer +from .WordTokenizer import ENGLISH_STOP_WORDS, TransformersTokenizerWrapper, WordTokenizer + +__all__ = [ + "WordTokenizer", + "WhitespaceTokenizer", + "PhraseTokenizer", + "ENGLISH_STOP_WORDS", + "TransformersTokenizerWrapper", +] diff --git a/sentence-transformers/sentence_transformers/models/tokenizer/__pycache__/PhraseTokenizer.cpython-312.pyc b/sentence-transformers/sentence_transformers/models/tokenizer/__pycache__/PhraseTokenizer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..caebb5eb220ba0138637cf8b8a454a59e69c4e47 Binary files /dev/null and b/sentence-transformers/sentence_transformers/models/tokenizer/__pycache__/PhraseTokenizer.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/models/tokenizer/__pycache__/WhitespaceTokenizer.cpython-312.pyc b/sentence-transformers/sentence_transformers/models/tokenizer/__pycache__/WhitespaceTokenizer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..481b09ff8321a13147d25a1adb7522b625faeb82 Binary files /dev/null and b/sentence-transformers/sentence_transformers/models/tokenizer/__pycache__/WhitespaceTokenizer.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/models/tokenizer/__pycache__/WordTokenizer.cpython-312.pyc b/sentence-transformers/sentence_transformers/models/tokenizer/__pycache__/WordTokenizer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ad82a81a76aff5b53f8ebf4f8732118d28debc8 Binary files /dev/null and b/sentence-transformers/sentence_transformers/models/tokenizer/__pycache__/WordTokenizer.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/models/tokenizer/__pycache__/__init__.cpython-312.pyc b/sentence-transformers/sentence_transformers/models/tokenizer/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e918d9f82d80636cc7b966e42d18e5c70191d758 Binary files /dev/null and b/sentence-transformers/sentence_transformers/models/tokenizer/__pycache__/__init__.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/readers/InputExample.py b/sentence-transformers/sentence_transformers/readers/InputExample.py new file mode 100644 index 0000000000000000000000000000000000000000..997c5513ed256c8fd8a0aff52dc45b3fd26980f1 --- /dev/null +++ b/sentence-transformers/sentence_transformers/readers/InputExample.py @@ -0,0 +1,31 @@ +""" +This file contains deprecated code that can only be used with the old `model.fit`-style Sentence Transformers v2.X training. +It exists for backwards compatibility with the `model.old_fit` method, but will be removed in a future version. + +Nowadays, with Sentence Transformers v3+, it is recommended to use the `SentenceTransformerTrainer` class to train models. +See https://www.sbert.net/docs/sentence_transformer/training_overview.html for more information. + +Instead, you should create a `datasets` `Dataset` for training: https://huggingface.co/docs/datasets/create_dataset +""" + +from __future__ import annotations + + +class InputExample: + """Structure for one input example with texts, the label and a unique id""" + + def __init__(self, guid: str = "", texts: list[str] | None = None, label: int | float = 0): + """ + Creates one InputExample with the given texts, guid and label + + Args: + guid: id for the example + texts: the texts for the example. + label: the label for the example + """ + self.guid = guid + self.texts = texts + self.label = label + + def __str__(self): + return " label: {}, texts: {}".format(str(self.label), "; ".join(self.texts)) diff --git a/sentence-transformers/sentence_transformers/readers/LabelSentenceReader.py b/sentence-transformers/sentence_transformers/readers/LabelSentenceReader.py new file mode 100644 index 0000000000000000000000000000000000000000..be91fa1a1830266fa3063660b40635a666e8478e --- /dev/null +++ b/sentence-transformers/sentence_transformers/readers/LabelSentenceReader.py @@ -0,0 +1,51 @@ +""" +This file contains deprecated code that can only be used with the old `model.fit`-style Sentence Transformers v2.X training. +It exists for backwards compatibility with the `model.old_fit` method, but will be removed in a future version. + +Nowadays, with Sentence Transformers v3+, it is recommended to use the `SentenceTransformerTrainer` class to train models. +See https://www.sbert.net/docs/sentence_transformer/training_overview.html for more information. + +Instead, you should create a `datasets` `Dataset` for training: https://huggingface.co/docs/datasets/create_dataset +""" + +from __future__ import annotations + +import os + +from . import InputExample + + +class LabelSentenceReader: + """Reads in a file that has at least two columns: a label and a sentence. + This reader can for example be used with the BatchHardTripletLoss. + Maps labels automatically to integers + """ + + def __init__(self, folder, label_col_idx=0, sentence_col_idx=1, separator="\t"): + self.folder = folder + self.label_map = {} + self.label_col_idx = label_col_idx + self.sentence_col_idx = sentence_col_idx + self.separator = separator + + def get_examples(self, filename, max_examples=0): + examples = [] + + id = 0 + for line in open(os.path.join(self.folder, filename), encoding="utf-8"): + splits = line.strip().split(self.separator) + label = splits[self.label_col_idx] + sentence = splits[self.sentence_col_idx] + + if label not in self.label_map: + self.label_map[label] = len(self.label_map) + + label_id = self.label_map[label] + guid = "%s-%d" % (filename, id) + id += 1 + examples.append(InputExample(guid=guid, texts=[sentence], label=label_id)) + + if 0 < max_examples <= id: + break + + return examples diff --git a/sentence-transformers/sentence_transformers/readers/NLIDataReader.py b/sentence-transformers/sentence_transformers/readers/NLIDataReader.py new file mode 100644 index 0000000000000000000000000000000000000000..10611b3f68c1384b7995b58f461d01822c35755f --- /dev/null +++ b/sentence-transformers/sentence_transformers/readers/NLIDataReader.py @@ -0,0 +1,57 @@ +""" +This file contains deprecated code that can only be used with the old `model.fit`-style Sentence Transformers v2.X training. +It exists for backwards compatibility with the `model.old_fit` method, but will be removed in a future version. + +Nowadays, with Sentence Transformers v3+, it is recommended to use the `SentenceTransformerTrainer` class to train models. +See https://www.sbert.net/docs/sentence_transformer/training_overview.html for more information. + +Instead, you should create a `datasets` `Dataset` for training: https://huggingface.co/docs/datasets/create_dataset +""" + +from __future__ import annotations + +import gzip +import os + +from . import InputExample + + +class NLIDataReader: + """Reads in the Stanford NLI dataset and the MultiGenre NLI dataset""" + + def __init__(self, dataset_folder): + self.dataset_folder = dataset_folder + + def get_examples(self, filename, max_examples=0): + """ + data_splits specified which data split to use (train, dev, test). + Expects that self.dataset_folder contains the files s1.$data_split.gz, s2.$data_split.gz, + labels.$data_split.gz, e.g., for the train split, s1.train.gz, s2.train.gz, labels.train.gz + """ + s1 = gzip.open(os.path.join(self.dataset_folder, "s1." + filename), mode="rt", encoding="utf-8").readlines() + s2 = gzip.open(os.path.join(self.dataset_folder, "s2." + filename), mode="rt", encoding="utf-8").readlines() + labels = gzip.open( + os.path.join(self.dataset_folder, "labels." + filename), mode="rt", encoding="utf-8" + ).readlines() + + examples = [] + id = 0 + for sentence_a, sentence_b, label in zip(s1, s2, labels): + guid = "%s-%d" % (filename, id) + id += 1 + examples.append(InputExample(guid=guid, texts=[sentence_a, sentence_b], label=self.map_label(label))) + + if 0 < max_examples <= len(examples): + break + + return examples + + @staticmethod + def get_labels(): + return {"contradiction": 0, "entailment": 1, "neutral": 2} + + def get_num_labels(self): + return len(self.get_labels()) + + def map_label(self, label): + return self.get_labels()[label.strip().lower()] diff --git a/sentence-transformers/sentence_transformers/readers/PairedFilesReader.py b/sentence-transformers/sentence_transformers/readers/PairedFilesReader.py new file mode 100644 index 0000000000000000000000000000000000000000..98c9d98e9e6eb266ae28be1c8b68eefb6df6a074 --- /dev/null +++ b/sentence-transformers/sentence_transformers/readers/PairedFilesReader.py @@ -0,0 +1,55 @@ +""" +This file contains deprecated code that can only be used with the old `model.fit`-style Sentence Transformers v2.X training. +It exists for backwards compatibility with the `model.old_fit` method, but will be removed in a future version. + +Nowadays, with Sentence Transformers v3+, it is recommended to use the `SentenceTransformerTrainer` class to train models. +See https://www.sbert.net/docs/sentence_transformer/training_overview.html for more information. + +Instead, you should create a `datasets` `Dataset` for training: https://huggingface.co/docs/datasets/create_dataset +""" + +from __future__ import annotations + +import gzip + +from . import InputExample + + +class PairedFilesReader: + """Reads in the a Pair Dataset, split in two files""" + + def __init__(self, filepaths): + self.filepaths = filepaths + + def get_examples(self, max_examples=0): + fIns = [] + for filepath in self.filepaths: + fIn = ( + gzip.open(filepath, "rt", encoding="utf-8") + if filepath.endswith(".gz") + else open(filepath, encoding="utf-8") + ) + fIns.append(fIn) + + examples = [] + + eof = False + while not eof: + texts = [] + for fIn in fIns: + text = fIn.readline() + + if text == "": + eof = True + break + + texts.append(text) + + if eof: + break + + examples.append(InputExample(guid=str(len(examples)), texts=texts, label=1)) + if max_examples > 0 and len(examples) >= max_examples: + break + + return examples diff --git a/sentence-transformers/sentence_transformers/readers/STSDataReader.py b/sentence-transformers/sentence_transformers/readers/STSDataReader.py new file mode 100644 index 0000000000000000000000000000000000000000..272865cb537e8e9675dc547dd74210d7e6b0612b --- /dev/null +++ b/sentence-transformers/sentence_transformers/readers/STSDataReader.py @@ -0,0 +1,100 @@ +""" +This file contains deprecated code that can only be used with the old `model.fit`-style Sentence Transformers v2.X training. +It exists for backwards compatibility with the `model.old_fit` method, but will be removed in a future version. + +Nowadays, with Sentence Transformers v3+, it is recommended to use the `SentenceTransformerTrainer` class to train models. +See https://www.sbert.net/docs/sentence_transformer/training_overview.html for more information. + +Instead, you should create a `datasets` `Dataset` for training: https://huggingface.co/docs/datasets/create_dataset +""" + +from __future__ import annotations + +import csv +import gzip +import os + +from . import InputExample + + +class STSDataReader: + """Reads in the STS dataset. Each line contains two sentences (s1_col_idx, s2_col_idx) and one label (score_col_idx) + + Default values expects a tab separated file with the first & second column the sentence pair and third column the score (0...1). Default config normalizes scores from 0...5 to 0...1 + """ + + def __init__( + self, + dataset_folder, + s1_col_idx=0, + s2_col_idx=1, + score_col_idx=2, + delimiter="\t", + quoting=csv.QUOTE_NONE, + normalize_scores=True, + min_score=0, + max_score=5, + ): + self.dataset_folder = dataset_folder + self.score_col_idx = score_col_idx + self.s1_col_idx = s1_col_idx + self.s2_col_idx = s2_col_idx + self.delimiter = delimiter + self.quoting = quoting + self.normalize_scores = normalize_scores + self.min_score = min_score + self.max_score = max_score + + def get_examples(self, filename, max_examples=0): + """filename specified which data split to use (train.csv, dev.csv, test.csv).""" + filepath = os.path.join(self.dataset_folder, filename) + with ( + gzip.open(filepath, "rt", encoding="utf8") + if filename.endswith(".gz") + else open(filepath, encoding="utf-8") + ) as fIn: + data = csv.reader(fIn, delimiter=self.delimiter, quoting=self.quoting) + examples = [] + for id, row in enumerate(data): + score = float(row[self.score_col_idx]) + if self.normalize_scores: # Normalize to a 0...1 value + score = (score - self.min_score) / (self.max_score - self.min_score) + + s1 = row[self.s1_col_idx] + s2 = row[self.s2_col_idx] + examples.append(InputExample(guid=filename + str(id), texts=[s1, s2], label=score)) + + if max_examples > 0 and len(examples) >= max_examples: + break + + return examples + + +class STSBenchmarkDataReader(STSDataReader): + """Reader especially for the STS benchmark dataset. There, the sentences are in column 5 and 6, the score is in column 4. + Scores are normalized from 0...5 to 0...1 + """ + + def __init__( + self, + dataset_folder, + s1_col_idx=5, + s2_col_idx=6, + score_col_idx=4, + delimiter="\t", + quoting=csv.QUOTE_NONE, + normalize_scores=True, + min_score=0, + max_score=5, + ): + super().__init__( + dataset_folder=dataset_folder, + s1_col_idx=s1_col_idx, + s2_col_idx=s2_col_idx, + score_col_idx=score_col_idx, + delimiter=delimiter, + quoting=quoting, + normalize_scores=normalize_scores, + min_score=min_score, + max_score=max_score, + ) diff --git a/sentence-transformers/sentence_transformers/readers/TripletReader.py b/sentence-transformers/sentence_transformers/readers/TripletReader.py new file mode 100644 index 0000000000000000000000000000000000000000..bdc7fcc4decfdebb2e723fbf7d84a78bc2650903 --- /dev/null +++ b/sentence-transformers/sentence_transformers/readers/TripletReader.py @@ -0,0 +1,61 @@ +""" +This file contains deprecated code that can only be used with the old `model.fit`-style Sentence Transformers v2.X training. +It exists for backwards compatibility with the `model.old_fit` method, but will be removed in a future version. + +Nowadays, with Sentence Transformers v3+, it is recommended to use the `SentenceTransformerTrainer` class to train models. +See https://www.sbert.net/docs/sentence_transformer/training_overview.html for more information. + +Instead, you should create a `datasets` `Dataset` for training: https://huggingface.co/docs/datasets/create_dataset +""" + +from __future__ import annotations + +import csv +import os + +from . import InputExample + + +class TripletReader: + """Reads in the a Triplet Dataset: Each line contains (at least) 3 columns, one anchor column (s1), + one positive example (s2) and one negative example (s3) + """ + + def __init__( + self, + dataset_folder, + s1_col_idx=0, + s2_col_idx=1, + s3_col_idx=2, + has_header=False, + delimiter="\t", + quoting=csv.QUOTE_NONE, + ): + self.dataset_folder = dataset_folder + self.s1_col_idx = s1_col_idx + self.s2_col_idx = s2_col_idx + self.s3_col_idx = s3_col_idx + self.has_header = has_header + self.delimiter = delimiter + self.quoting = quoting + + def get_examples(self, filename, max_examples=0): + data = csv.reader( + open(os.path.join(self.dataset_folder, filename), encoding="utf-8"), + delimiter=self.delimiter, + quoting=self.quoting, + ) + examples = [] + if self.has_header: + next(data) + + for id, row in enumerate(data): + s1 = row[self.s1_col_idx] + s2 = row[self.s2_col_idx] + s3 = row[self.s3_col_idx] + + examples.append(InputExample(texts=[s1, s2, s3])) + if max_examples > 0 and len(examples) >= max_examples: + break + + return examples diff --git a/sentence-transformers/sentence_transformers/readers/__init__.py b/sentence-transformers/sentence_transformers/readers/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5718e72628cbf13a179e922bf90f52fc56100c2b --- /dev/null +++ b/sentence-transformers/sentence_transformers/readers/__init__.py @@ -0,0 +1,24 @@ +""" +This directory contains deprecated code that can only be used with the old `model.fit`-style Sentence Transformers v2.X training. +It exists for backwards compatibility with the `model.old_fit` method, but will be removed in a future version. + +Nowadays, with Sentence Transformers v3+, it is recommended to use the `SentenceTransformerTrainer` class to train models. +See https://www.sbert.net/docs/sentence_transformer/training_overview.html for more information. +""" + +from __future__ import annotations + +from .InputExample import InputExample +from .LabelSentenceReader import LabelSentenceReader +from .NLIDataReader import NLIDataReader +from .STSDataReader import STSBenchmarkDataReader, STSDataReader +from .TripletReader import TripletReader + +__all__ = [ + "InputExample", + "LabelSentenceReader", + "NLIDataReader", + "STSDataReader", + "STSBenchmarkDataReader", + "TripletReader", +] diff --git a/sentence-transformers/sentence_transformers/readers/__pycache__/InputExample.cpython-312.pyc b/sentence-transformers/sentence_transformers/readers/__pycache__/InputExample.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b494bdd94f26d85291805ab86dfbd95542cb42a0 Binary files /dev/null and b/sentence-transformers/sentence_transformers/readers/__pycache__/InputExample.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/readers/__pycache__/LabelSentenceReader.cpython-312.pyc b/sentence-transformers/sentence_transformers/readers/__pycache__/LabelSentenceReader.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a3edc42d1e4f30efd956822126be2e9290f5c6dc Binary files /dev/null and b/sentence-transformers/sentence_transformers/readers/__pycache__/LabelSentenceReader.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/readers/__pycache__/NLIDataReader.cpython-312.pyc b/sentence-transformers/sentence_transformers/readers/__pycache__/NLIDataReader.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..87086d9ad9d40176bab131872a2dc50dc84eb49b Binary files /dev/null and b/sentence-transformers/sentence_transformers/readers/__pycache__/NLIDataReader.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/readers/__pycache__/STSDataReader.cpython-312.pyc b/sentence-transformers/sentence_transformers/readers/__pycache__/STSDataReader.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ccdc2c60f1c3050b69f3c124425f496481777305 Binary files /dev/null and b/sentence-transformers/sentence_transformers/readers/__pycache__/STSDataReader.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/readers/__pycache__/TripletReader.cpython-312.pyc b/sentence-transformers/sentence_transformers/readers/__pycache__/TripletReader.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..72fd89ba149e0e9859a3087427cc4e9123200531 Binary files /dev/null and b/sentence-transformers/sentence_transformers/readers/__pycache__/TripletReader.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/readers/__pycache__/__init__.cpython-312.pyc b/sentence-transformers/sentence_transformers/readers/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b3124809fb52436d08059f780f3ec0493deb214 Binary files /dev/null and b/sentence-transformers/sentence_transformers/readers/__pycache__/__init__.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/__init__.py b/sentence-transformers/sentence_transformers/sparse_encoder/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7058a59ba7b36be00c08820704555894c4c5ebd4 --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/__init__.py @@ -0,0 +1,13 @@ +from __future__ import annotations + +from .model_card import SparseEncoderModelCardData +from .SparseEncoder import SparseEncoder +from .trainer import SparseEncoderTrainer +from .training_args import SparseEncoderTrainingArguments + +__all__ = [ + "SparseEncoder", + "SparseEncoderTrainer", + "SparseEncoderTrainingArguments", + "SparseEncoderModelCardData", +] diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/__pycache__/SparseEncoder.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/__pycache__/SparseEncoder.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..65844f861a7288a1d8059061b9b92100d2510c0a Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/__pycache__/SparseEncoder.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/__pycache__/__init__.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..03f36d45052860924e460a571ca43a16614f1191 Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/__pycache__/__init__.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/__pycache__/data_collator.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/__pycache__/data_collator.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f0a6e701c9c80d033075b58a8b2c8eff720dfbc Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/__pycache__/data_collator.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/__pycache__/model_card.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/__pycache__/model_card.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de06b9bd6c627e58ea1ec397a996338876ab62cc Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/__pycache__/model_card.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/__pycache__/trainer.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/__pycache__/trainer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5bc2e1338f35109762c2bbcfcb9344b284d515b3 Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/__pycache__/trainer.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/__pycache__/training_args.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/__pycache__/training_args.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..46d3c2342aae463cdc5e4e375a4505afbdfc8a6a Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/__pycache__/training_args.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/callbacks/__init__.py b/sentence-transformers/sentence_transformers/sparse_encoder/callbacks/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7960fcde702d94708aa86a5e19e8070a3cc9857e --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/callbacks/__init__.py @@ -0,0 +1,5 @@ +from __future__ import annotations + +from .splade_callbacks import SchedulerType, SpladeRegularizerWeightSchedulerCallback + +__all__ = ["SpladeRegularizerWeightSchedulerCallback", "SchedulerType"] diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/callbacks/__pycache__/__init__.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/callbacks/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..daba467e2073757e9e61cc3321a33c0515b63e8d Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/callbacks/__pycache__/__init__.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/callbacks/__pycache__/splade_callbacks.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/callbacks/__pycache__/splade_callbacks.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..be199f422e3028a2a33f43c4e83725721e5bd74c Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/callbacks/__pycache__/splade_callbacks.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/callbacks/splade_callbacks.py b/sentence-transformers/sentence_transformers/sparse_encoder/callbacks/splade_callbacks.py new file mode 100644 index 0000000000000000000000000000000000000000..9e22568e95b0ad875680cf742e1bfcd0160fe4dc --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/callbacks/splade_callbacks.py @@ -0,0 +1,148 @@ +from __future__ import annotations + +import logging +from enum import Enum + +from transformers.trainer_callback import TrainerCallback, TrainerControl, TrainerState + +from sentence_transformers.sparse_encoder.losses.SpladeLoss import SpladeLoss +from sentence_transformers.sparse_encoder.training_args import SparseEncoderTrainingArguments + +logger = logging.getLogger(__name__) + + +class SchedulerType(Enum): + """Types of schedulers for weight parameters in SpladeLoss""" + + LINEAR = "linear" + QUADRATIC = "quadratic" + + +class SpladeRegularizerWeightSchedulerCallback(TrainerCallback): + def __init__( + self, + loss: SpladeLoss, + scheduler_type: str | SchedulerType = SchedulerType.QUADRATIC, + warmup_ratio: float = 1 / 3, + ): + """ + Callback that updates the query_regularizer_weight and document_regularizer_weight parameters of SpladeLoss + based on a schedule. + + The scheduler gradually increases the weight values from 0 to their max value + within the specified warmup ratio of the total training steps. + + Args: + loss (SpladeLoss): SpladeLoss instance to be updated + scheduler_type (str): Type of scheduler ('linear' or 'quadratic') + warmup_ratio (float): Ratio of total steps to reach max weight values (default: 1/3) + """ + super().__init__() + + if isinstance(scheduler_type, str): + try: + scheduler_type = SchedulerType(scheduler_type.lower()) + except ValueError: + logger.warning( + f"Invalid scheduler_type: {scheduler_type}. Using default: {SchedulerType.QUADRATIC.value}" + ) + scheduler_type = SchedulerType.QUADRATIC + + self.scheduler_type = scheduler_type + + # Validate warmup_ratio is between 0 and 1 + if not 0 < warmup_ratio <= 1: + logger.warning(f"warmup_ratio should be between 0 and 1, got {warmup_ratio}. Setting to default 1/3.") + warmup_ratio = 1 / 3 + + # Validate loss is an instance of SpladeLoss + if not isinstance(loss, SpladeLoss): + logger.warning( + f"SpladeRegularizerWeightSchedulerCallback is only compatible with SpladeLoss, " + f"but got {type(loss).__name__}. This callback won't have any effect." + ) + raise ValueError("loss must be an instance of SpladeLoss") + self.loss = loss + self.max_document_regularizer_weight = self.loss.document_regularizer_weight + self.max_query_regularizer_weight = self.loss.query_regularizer_weight + self.warmup_ratio = warmup_ratio + self._current_query_regularizer_weight = 0.0 if self.max_query_regularizer_weight is not None else None + self._current_document_regularizer_weight = 0.0 + self.total_steps = None + self.warmup_steps = None + + def on_train_begin( + self, + args: SparseEncoderTrainingArguments, + state: TrainerState, + control: TrainerControl, + **kwargs, + ): + """Initialize the scheduler at the beginning of training.""" + # Calculate total steps and warmup steps + if hasattr(state, "max_steps") and state.max_steps > 0: + self.total_steps = state.max_steps + elif hasattr(state, "num_train_epochs") and hasattr(state, "num_update_steps_per_epoch"): + self.total_steps = state.num_update_steps_per_epoch * state.num_train_epochs + else: + logger.warning("Cannot determine total steps from TrainerState. Weight scheduling may not work properly.") + return + + self.warmup_steps = int(self.total_steps * self.warmup_ratio) + if self.warmup_steps <= 0: + self.warmup_steps = 1 # Ensure at least one step for warmup + + # Set initial weight values + self.loss.query_regularizer_weight = self._current_query_regularizer_weight + self.loss.document_regularizer_weight = self._current_document_regularizer_weight + + def _calculate_weight_value(self, step: int, max_value: float) -> float: + """Calculate the weight value based on the current step and scheduler type.""" + if self.warmup_steps is None or step >= self.warmup_steps or max_value is None: + return max_value + + ratio = step / max(self.warmup_steps, 1) # Avoid division by zero + + if self.scheduler_type == SchedulerType.LINEAR: + return max_value * ratio + elif self.scheduler_type == SchedulerType.QUADRATIC: + return max_value * (ratio**2) + else: + logger.warning(f"Unknown scheduler type: {self.scheduler_type}. Using quadratic.") + return max_value * (ratio**2) + + def on_step_begin( + self, + args: SparseEncoderTrainingArguments, + state: TrainerState, + control: TrainerControl, + **kwargs, + ): + """Update weight values at the end of each step.""" + if self.total_steps is None or self.warmup_steps is None: + return + + # Get current step + step = state.global_step + + # Calculate new weight values + new_query_regularizer_weight = self._calculate_weight_value(step, self.max_query_regularizer_weight) + new_document_regularizer_weight = self._calculate_weight_value(step, self.max_document_regularizer_weight) + + # Update weight values only if they've changed + if ( + new_query_regularizer_weight != self._current_query_regularizer_weight + or new_document_regularizer_weight != self._current_document_regularizer_weight + ): + self.loss.query_regularizer_weight = new_query_regularizer_weight + self.loss.document_regularizer_weight = new_document_regularizer_weight + + # Store current values + self._current_query_regularizer_weight = new_query_regularizer_weight + self._current_document_regularizer_weight = new_document_regularizer_weight + + def on_log(self, args, state, control, model=None, logs=None, **kwargs): + """Log the current weight values.""" + logs["document_regularizer_weight"] = self._current_document_regularizer_weight + if self._current_query_regularizer_weight is not None: + logs["query_regularizer_weight"] = self._current_query_regularizer_weight diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/ReciprocalRankFusionEvaluator.py b/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/ReciprocalRankFusionEvaluator.py new file mode 100644 index 0000000000000000000000000000000000000000..1ffb95b948aa97b2e4b7b93e75cdcc9f9adc8a12 --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/ReciprocalRankFusionEvaluator.py @@ -0,0 +1,351 @@ +from __future__ import annotations + +import csv +import json +import logging +import os + +import numpy as np +from sklearn.metrics import average_precision_score, ndcg_score +from tqdm import tqdm + +from sentence_transformers.evaluation.SentenceEvaluator import SentenceEvaluator + +logger = logging.getLogger(__name__) + + +class ReciprocalRankFusionEvaluator(SentenceEvaluator): + """ + This class evaluates a hybrid search approach using Reciprocal Rank Fusion (RRF). + + Given a query and two separate ranked lists of documents from different retrievers (e.g., sparse and dense), + it combines them using the RRF formula and computes metrics like MRR@k, NDCG@k, and MAP. + + Args: + dense_samples (list): A list of dictionaries for dense retriever results. Each dictionary should have: + - 'query_id': The ID of the query + - 'query': The search query text + - 'positive': A list of relevant documents + - 'documents': A list of all documents (including positives) + sparse_samples (list): A list of dictionaries for sparse retriever results with the same format + at_k (int): Only consider the top k documents for evaluation. Defaults to 10. + rrf_k (int): Constant in the RRF formula. Defaults to 60. + name (str): Name of the evaluator. Defaults to "". + batch_size (int): Batch size used for the evaluation. Defaults to 32. + show_progress_bar (bool): Output a progress bar. Defaults to False. + write_csv (bool): Write results to CSV file. Defaults to True. + write_predictions (bool): Whether to write the fused predictions to a JSONL file. Defaults to False. + + Example: + See an example usage `Applications > Retrieve & Rerank <../../../examples/sparse_encoder/applications/retrieve_rerank/README.html>`_ + + """ + + def __init__( + self, + dense_samples: list[dict[str, str | list[str]]], + sparse_samples: list[dict[str, str | list[str]]], + at_k: int = 10, + rrf_k: int = 60, + name: str = "", + batch_size: int = 32, + show_progress_bar: bool = False, + write_csv: bool = True, + write_predictions: bool = False, + ): + super().__init__() + self.dense_samples = dense_samples + self.sparse_samples = sparse_samples + + # Validate that both sample lists have the same length + if len(dense_samples) != len(sparse_samples): + raise ValueError( + f"Dense samples ({len(dense_samples)}) and sparse samples ({len(sparse_samples)}) must have the same length" + ) + + # Validate that both lists have query_id field + for i, (dense_sample, sparse_sample) in enumerate(zip(dense_samples, sparse_samples)): + if "query_id" not in dense_sample or "query_id" not in sparse_sample: + raise ValueError(f"Sample at index {i} missing 'query_id' field") + + if dense_sample["query_id"] != sparse_sample["query_id"]: + raise ValueError( + f"Query ID mismatch at index {i}: {dense_sample['query_id']} != {sparse_sample['query_id']}" + ) + + self.at_k = at_k + self.rrf_k = rrf_k + self.name = name + self.batch_size = batch_size + self.show_progress_bar = show_progress_bar + self.write_predictions = write_predictions + + self.csv_file = "ReciprocalRankFusion_evaluation" + ("_" + name if name else "") + "_results.csv" + self.csv_headers = [ + "epoch", + "steps", + "Dense_MAP", + f"Dense_MRR@{self.at_k}", + f"Dense_NDCG@{self.at_k}", + "Sparse_MAP", + f"Sparse_MRR@{self.at_k}", + f"Sparse_NDCG@{self.at_k}", + "Fusion_MAP", + f"Fusion_MRR@{self.at_k}", + f"Fusion_NDCG@{self.at_k}", + ] + self.write_csv = write_csv + self.primary_metric = f"ndcg@{self.at_k}" + + if self.write_predictions: + self.predictions_file = ( + "ReciprocalRankFusion_evaluation" + ("_" + name if name else "") + "_predictions.jsonl" + ) + + def __call__(self, output_path: str | None = None, epoch: int = -1, steps: int = -1) -> dict[str, float]: + if epoch != -1: + if steps == -1: + out_txt = f" after epoch {epoch}" + else: + out_txt = f" in epoch {epoch} after {steps} steps" + else: + out_txt = "" + + logger.info(f"ReciprocalRankFusionEvaluator: Evaluating hybrid search on the {self.name} dataset{out_txt}:") + logger.info(f"Processing {len(self.dense_samples)} samples") + + # Initialize scores + dense_mrr_scores = [] + dense_ndcg_scores = [] + dense_ap_scores = [] + + sparse_mrr_scores = [] + sparse_ndcg_scores = [] + sparse_ap_scores = [] + + fusion_mrr_scores = [] + fusion_ndcg_scores = [] + fusion_ap_scores = [] + + num_queries = 0 + num_positives = [] + fused_results_list = [] + + # Process each pair of samples + for i, (dense_sample, sparse_sample) in enumerate( + tqdm( + zip(self.dense_samples, self.sparse_samples), + desc="Evaluating", + disable=not self.show_progress_bar, + total=len(self.dense_samples), + ) + ): + query_id = dense_sample["query_id"] + + # Verify query_id match (redundant since we checked in __init__, but good for safety) + assert ( + query_id == sparse_sample["query_id"] + ), f"Query ID mismatch: {query_id} != {sparse_sample['query_id']}" + + query = dense_sample["query"] + positive = dense_sample["positive"] + if isinstance(positive, str): + positive = [positive] + + # Get documents from both retrievers + dense_docs = dense_sample["documents"] + sparse_docs = sparse_sample["documents"] + + # Calculate base metrics for dense retriever + dense_is_relevant = [int(sample in positive) for sample in dense_docs] + + # Skip if no relevant documents + if sum(dense_is_relevant) == 0: + dense_mrr, dense_ndcg, dense_ap = 0, 0, 0 + else: + dense_is_relevant += [1] * (len(positive) - sum(dense_is_relevant)) + dense_pred_scores = np.array(range(len(dense_is_relevant), 0, -1)) + dense_mrr, dense_ndcg, dense_ap = self.compute_metrics(dense_is_relevant, dense_pred_scores) + + dense_mrr_scores.append(dense_mrr) + dense_ndcg_scores.append(dense_ndcg) + dense_ap_scores.append(dense_ap) + + # Calculate base metrics for sparse retriever + sparse_is_relevant = [int(sample in positive) for sample in sparse_docs] + + # Skip if no relevant documents + if sum(sparse_is_relevant) == 0: + sparse_mrr, sparse_ndcg, sparse_ap = 0, 0, 0 + else: + sparse_is_relevant += [1] * (len(positive) - sum(sparse_is_relevant)) + sparse_pred_scores = np.array(range(len(sparse_is_relevant), 0, -1)) + sparse_mrr, sparse_ndcg, sparse_ap = self.compute_metrics(sparse_is_relevant, sparse_pred_scores) + + sparse_mrr_scores.append(sparse_mrr) + sparse_ndcg_scores.append(sparse_ndcg) + sparse_ap_scores.append(sparse_ap) + + # Create rank maps for each retriever + dense_ranks = {doc: rank for rank, doc in enumerate(dense_docs)} + sparse_ranks = {doc: rank for rank, doc in enumerate(sparse_docs)} + + # Combine all unique documents + all_docs = set(dense_ranks.keys()) | set(sparse_ranks.keys()) + + # Calculate RRF scores + rrf_scores = {} + for doc in all_docs: + dense_rank = dense_ranks.get(doc, len(dense_docs)) + sparse_rank = sparse_ranks.get(doc, len(sparse_docs)) + rrf_scores[doc] = (1 / (self.rrf_k + dense_rank)) + (1 / (self.rrf_k + sparse_rank)) + + # Sort documents by RRF scores in descending order + fused_docs = sorted(rrf_scores.keys(), key=lambda doc: rrf_scores[doc], reverse=True) + + # Create binary relevance list for evaluation + fusion_is_relevant = [int(sample in positive) for sample in fused_docs] + + num_queries += 1 + num_positives.append(len(positive)) + + # Skip if no relevant documents in fusion results + if sum(fusion_is_relevant) == 0: + fusion_mrr, fusion_ndcg, fusion_ap = 0, 0, 0 + else: + fusion_is_relevant += [1] * (len(positive) - sum(fusion_is_relevant)) + fusion_pred_scores = np.array(range(len(fusion_is_relevant), 0, -1)) + fusion_mrr, fusion_ndcg, fusion_ap = self.compute_metrics(fusion_is_relevant, fusion_pred_scores) + + fusion_mrr_scores.append(fusion_mrr) + fusion_ndcg_scores.append(fusion_ndcg) + fusion_ap_scores.append(fusion_ap) + + # Store fused results for prediction file if requested + if self.write_predictions: + fused_results_list.append( + {"query_id": query_id, "query": query, "positive": positive, "documents": fused_docs} + ) + + # Calculate mean scores + mean_dense_mrr = np.mean(dense_mrr_scores) + mean_dense_ndcg = np.mean(dense_ndcg_scores) + mean_dense_ap = np.mean(dense_ap_scores) + + mean_sparse_mrr = np.mean(sparse_mrr_scores) + mean_sparse_ndcg = np.mean(sparse_ndcg_scores) + mean_sparse_ap = np.mean(sparse_ap_scores) + + mean_fusion_mrr = np.mean(fusion_mrr_scores) + mean_fusion_ndcg = np.mean(fusion_ndcg_scores) + mean_fusion_ap = np.mean(fusion_ap_scores) + # Store metrics + metrics = { + "dense_map": mean_dense_ap, + f"dense_mrr@{self.at_k}": mean_dense_mrr, + f"dense_ndcg@{self.at_k}": mean_dense_ndcg, + "sparse_map": mean_sparse_ap, + f"sparse_mrr@{self.at_k}": mean_sparse_mrr, + f"sparse_ndcg@{self.at_k}": mean_sparse_ndcg, + "map": mean_fusion_ap, + f"mrr@{self.at_k}": mean_fusion_mrr, + f"ndcg@{self.at_k}": mean_fusion_ndcg, + } + + # Log results + logger.info( + f"Queries: {num_queries}\t" + f"Positives: Min {min(num_positives) if num_positives else 0:.1f}, " + f"Mean {np.mean(num_positives) if num_positives else 0:.1f}, " + f"Max {max(num_positives) if num_positives else 0:.1f}" + ) + + # Display metrics with comparison + logger.info("=" * 75) + logger.info( + f"{'Metric':<7} | {'Dense':^8} | {'Sparse':^8} | {'Fusion':^8} | {'Gain vs Dense':^13} | {'Gain vs Sparse':^14} |" + ) + logger.info("-" * 75) + logger.info( + f"MAP | {mean_dense_ap:>8.2%} | {mean_sparse_ap:>8.2%} | {mean_fusion_ap:>8.2%} | {mean_fusion_ap - mean_dense_ap:>+13.2%} | {mean_fusion_ap - mean_sparse_ap:>+14.2%} |" + ) + logger.info( + f"MRR@{self.at_k:<3} | {mean_dense_mrr:>8.2%} | {mean_sparse_mrr:>8.2%} | {mean_fusion_mrr:>8.2%} | {mean_fusion_mrr - mean_dense_mrr:>+13.2%} | {mean_fusion_mrr - mean_sparse_mrr:>+14.2%} |" + ) + logger.info( + f"NDCG@{self.at_k:<2} | {mean_dense_ndcg:>8.2%} | {mean_sparse_ndcg:>8.2%} | {mean_fusion_ndcg:>8.2%} | {mean_fusion_ndcg - mean_dense_ndcg:>+13.2%} | {mean_fusion_ndcg - mean_sparse_ndcg:>+14.2%} |" + ) + logger.info("=" * 75) + + # Write results to CSV if requested + if output_path is not None: + os.makedirs(output_path, exist_ok=True) + + if self.write_csv: + csv_path = os.path.join(output_path, self.csv_file) + output_file_exists = os.path.isfile(csv_path) + with open(csv_path, mode="a" if output_file_exists else "w", encoding="utf-8") as f: + writer = csv.writer(f) + if not output_file_exists: + writer.writerow(self.csv_headers) + + writer.writerow( + [ + epoch, + steps, + mean_dense_ap, + mean_dense_mrr, + mean_dense_ndcg, + mean_sparse_ap, + mean_sparse_mrr, + mean_sparse_ndcg, + mean_fusion_ap, + mean_fusion_mrr, + mean_fusion_ndcg, + ] + ) + + # Write prediction results if requested + if self.write_predictions and fused_results_list: + json_path = os.path.join(output_path, self.predictions_file) + with open(json_path, mode="w", encoding="utf-8") as f: + for result in fused_results_list: + f.write(json.dumps(result) + "\n") + logger.info(f"Wrote fused ranking predictions to {json_path}") + + # Prefix metrics with name if provided + metrics = self.prefix_name_to_metrics(metrics, self.name) + + return metrics + + def compute_metrics(self, y_true, y_pred): + """Compute MRR, NDCG, and AP metrics using sklearn""" + ranking = np.argsort(y_pred)[::-1] + + # Calculate MRR@k + mrr = 0 + for rank, index in enumerate(ranking[0 : self.at_k]): + if y_true[index]: + mrr = 1 / (rank + 1) + break + + # Calculate NDCG@k + ndcg = ndcg_score([y_true], [y_pred], k=self.at_k) + + # Calculate MAP + ap = average_precision_score(y_true, y_pred) + + return mrr, ndcg, ap + + def prefix_name_to_metrics(self, metrics: dict[str, float], prefix: str) -> dict[str, float]: + """Prefix all metric names with the evaluator name.""" + if not prefix: + return metrics + return {f"{prefix}_{k}": v for k, v in metrics.items()} + + def get_config_dict(self): + return { + "at_k": self.at_k, + "rrf_k": self.rrf_k, + "write_predictions": self.write_predictions, + } diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseBinaryClassificationEvaluator.py b/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseBinaryClassificationEvaluator.py new file mode 100644 index 0000000000000000000000000000000000000000..f15e78a4709c6d1b70f044ac6ee2a46cbfd48a7c --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseBinaryClassificationEvaluator.py @@ -0,0 +1,193 @@ +from __future__ import annotations + +import logging +import os +from collections import defaultdict +from typing import TYPE_CHECKING, Any, Literal + +from sentence_transformers.evaluation import BinaryClassificationEvaluator +from sentence_transformers.util import append_to_last_row + +if TYPE_CHECKING: + import numpy as np + from torch import Tensor + + from sentence_transformers.sparse_encoder.SparseEncoder import SparseEncoder + + +logger = logging.getLogger(__name__) + + +class SparseBinaryClassificationEvaluator(BinaryClassificationEvaluator): + """ + This evaluator extends :class:`~sentence_transformers.evaluation.BinaryClassificationEvaluator` but is specifically designed for sparse encoder models. + + Evaluate a model based on the similarity of the embeddings by calculating the accuracy of identifying similar and + dissimilar sentences. + The metrics are the cosine similarity, dot score, Euclidean and Manhattan distance + The returned score is the accuracy with a specified metric. + + The results are written in a CSV. If a CSV already exists, then values are appended. + + The labels need to be 0 for dissimilar pairs and 1 for similar pairs. + + Args: + sentences1 (List[str]): The first column of sentences. + sentences2 (List[str]): The second column of sentences. + labels (List[int]): labels[i] is the label for the pair (sentences1[i], sentences2[i]). Must be 0 or 1. + name (str, optional): Name for the output. Defaults to "". + batch_size (int, optional): Batch size used to compute embeddings. Defaults to 32. + show_progress_bar (bool, optional): If true, prints a progress bar. Defaults to False. + write_csv (bool, optional): Write results to a CSV file. Defaults to True. + max_active_dims (Optional[int], optional): The maximum number of active dimensions to use. `None` uses the model's current `max_active_dims`. Defaults to None. + similarity_fn_names (Optional[List[Literal["cosine", "dot", "euclidean", "manhattan"]]], optional): The similarity functions to use. If not specified, defaults to the ``similarity_fn_name`` attribute of the model. Defaults to None. + + Example: + :: + + import logging + + from datasets import load_dataset + + from sentence_transformers import SparseEncoder + from sentence_transformers.sparse_encoder.evaluation import SparseBinaryClassificationEvaluator + + logging.basicConfig(format="%(asctime)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S", level=logging.INFO) + + # Initialize the SPLADE model + model = SparseEncoder("naver/splade-cocondenser-ensembledistil") + + # Load a dataset with two text columns and a class label column (https://huggingface.co/datasets/sentence-transformers/quora-duplicates) + eval_dataset = load_dataset("sentence-transformers/quora-duplicates", "pair-class", split="train[-1000:]") + + # Initialize the evaluator + binary_acc_evaluator = SparseBinaryClassificationEvaluator( + sentences1=eval_dataset["sentence1"], + sentences2=eval_dataset["sentence2"], + labels=eval_dataset["label"], + name="quora_duplicates_dev", + show_progress_bar=True, + similarity_fn_names=["cosine", "dot", "euclidean", "manhattan"], + ) + results = binary_acc_evaluator(model) + ''' + Accuracy with Cosine-Similarity: 75.00 (Threshold: 0.8668) + F1 with Cosine-Similarity: 67.22 (Threshold: 0.5974) + Precision with Cosine-Similarity: 54.18 + Recall with Cosine-Similarity: 88.51 + Average Precision with Cosine-Similarity: 67.81 + Matthews Correlation with Cosine-Similarity: 49.56 + + Accuracy with Dot-Product: 76.50 (Threshold: 23.4236) + F1 with Dot-Product: 67.00 (Threshold: 19.0095) + Precision with Dot-Product: 55.93 + Recall with Dot-Product: 83.54 + Average Precision with Dot-Product: 65.89 + Matthews Correlation with Dot-Product: 48.88 + + Accuracy with Euclidean-Distance: 67.70 (Threshold: -10.0041) + F1 with Euclidean-Distance: 48.60 (Threshold: -0.1876) + Precision with Euclidean-Distance: 32.13 + Recall with Euclidean-Distance: 99.69 + Average Precision with Euclidean-Distance: 20.52 + Matthews Correlation with Euclidean-Distance: -4.59 + + Accuracy with Manhattan-Distance: 67.70 (Threshold: -103.0263) + F1 with Manhattan-Distance: 48.60 (Threshold: -0.8532) + Precision with Manhattan-Distance: 32.13 + Recall with Manhattan-Distance: 99.69 + Average Precision with Manhattan-Distance: 21.05 + Matthews Correlation with Manhattan-Distance: -4.59 + + Model Sparsity: Active Dimensions: 61.2, Sparsity Ratio: 0.9980 + ''' + # Print the results + print(f"Primary metric: {binary_acc_evaluator.primary_metric}") + # => Primary metric: quora_duplicates_dev_max_ap + print(f"Primary metric value: {results[binary_acc_evaluator.primary_metric]:.4f}") + # => Primary metric value: 0.6781 + """ + + def __init__( + self, + sentences1: list[str], + sentences2: list[str], + labels: list[int], + name: str = "", + batch_size: int = 32, + show_progress_bar: bool = False, + write_csv: bool = True, + max_active_dims: int | None = None, + similarity_fn_names: list[Literal["cosine", "dot", "euclidean", "manhattan"]] | None = None, + ): + self.max_active_dims = max_active_dims + self.sparsity_stats = defaultdict(list) + return super().__init__( + sentences1=sentences1, + sentences2=sentences2, + labels=labels, + name=name, + batch_size=batch_size, + show_progress_bar=show_progress_bar, + write_csv=write_csv, + similarity_fn_names=similarity_fn_names, + ) + + def _append_csv_headers(self, similarity_fn_names: list[str]) -> None: + super()._append_csv_headers(similarity_fn_names) + self.csv_headers.extend(["active_dims", "sparsity_ratio"]) + + def __call__( + self, model: SparseEncoder, output_path: str | None = None, epoch: int = -1, steps: int = -1 + ) -> dict[str, float]: + self.sparsity_stats = defaultdict(list) + metrics = super().__call__(model=model, output_path=output_path, epoch=epoch, steps=steps) + for key, value in self.sparsity_stats.items(): + self.sparsity_stats[key] = sum(value) / len(value) + + metrics.update(self.prefix_name_to_metrics(self.sparsity_stats, self.name)) + self.store_metrics_in_model_card_data(model, metrics, epoch, steps) + logger.info( + f"Model Sparsity: Active Dimensions: {self.sparsity_stats['active_dims']:.1f}, Sparsity Ratio: {self.sparsity_stats['sparsity_ratio']:.4f}" + ) + if output_path is not None and self.write_csv: + append_to_last_row( + os.path.join(output_path, self.csv_file), + [self.sparsity_stats["active_dims"], self.sparsity_stats["sparsity_ratio"]], + ) + + return metrics + + def compute_metrices(self, model: SparseEncoder) -> dict[str, dict[str, float]]: + return super().compute_metrices(model=model) + + def embed_inputs( + self, + model: SparseEncoder, + sentences: str | list[str] | np.ndarray, + **kwargs, + ) -> Tensor: + embeddings = model.encode( + sentences, + batch_size=self.batch_size, + show_progress_bar=self.show_progress_bar, + convert_to_sparse_tensor=True, + save_to_cpu=True, + max_active_dims=self.max_active_dims, + **kwargs, + ) + stat = model.sparsity(embeddings) + for key, value in stat.items(): + self.sparsity_stats[key].append(value) + return embeddings + + def store_metrics_in_model_card_data( + self, model: SparseEncoder, metrics: dict[str, Any], epoch: int = 0, step: int = 0 + ) -> None: + model.model_card_data.set_evaluation_metrics(self, metrics, epoch=epoch, step=step) + + def get_config_dict(self) -> dict[str, Any]: + config_dict = super().get_config_dict() + if self.max_active_dims is not None: + config_dict["max_active_dims"] = self.max_active_dims + return config_dict diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseEmbeddingSimilarityEvaluator.py b/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseEmbeddingSimilarityEvaluator.py new file mode 100644 index 0000000000000000000000000000000000000000..d62585a6ab6540bd45f2246674d0c6974c657076 --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseEmbeddingSimilarityEvaluator.py @@ -0,0 +1,168 @@ +from __future__ import annotations + +import logging +import os +from collections import defaultdict +from typing import TYPE_CHECKING, Any, Literal + +from sentence_transformers.evaluation import EmbeddingSimilarityEvaluator +from sentence_transformers.util import append_to_last_row + +if TYPE_CHECKING: + import numpy as np + from torch import Tensor + + from sentence_transformers.similarity_functions import SimilarityFunction + from sentence_transformers.sparse_encoder.SparseEncoder import SparseEncoder + + +logger = logging.getLogger(__name__) + + +class SparseEmbeddingSimilarityEvaluator(EmbeddingSimilarityEvaluator): + """ + This evaluator extends :class:`~sentence_transformers.evaluation.EmbeddingSimilarityEvaluator` but is specifically designed for sparse encoder models. + + Evaluate a model based on the similarity of the embeddings by calculating the Spearman and Pearson rank correlation + in comparison to the gold standard labels. + The metrics are the cosine similarity as well as euclidean and Manhattan distance + The returned score is the Spearman correlation with a specified metric. + + Args: + sentences1 (List[str]): List with the first sentence in a pair. + sentences2 (List[str]): List with the second sentence in a pair. + scores (List[float]): Similarity score between sentences1[i] and sentences2[i]. + batch_size (int, optional): The batch size for processing the sentences. Defaults to 16. + main_similarity (Optional[Union[str, SimilarityFunction]], optional): The main similarity function to use. + Can be a string (e.g. "cosine", "dot") or a SimilarityFunction object. Defaults to None. + similarity_fn_names (List[str], optional): List of similarity function names to use. If None, the + ``similarity_fn_name`` attribute of the model is used. Defaults to None. + name (str, optional): The name of the evaluator. Defaults to "". + show_progress_bar (bool, optional): Whether to show a progress bar during evaluation. Defaults to False. + write_csv (bool, optional): Whether to write the evaluation results to a CSV file. Defaults to True. + max_active_dims (Optional[int], optional): The maximum number of active dimensions to use. + `None` uses the model's current `max_active_dims`. Defaults to None. + + Example: + :: + + import logging + + from datasets import load_dataset + + from sentence_transformers import SparseEncoder, SimilarityFunction + from sentence_transformers.sparse_encoder.evaluation import SparseEmbeddingSimilarityEvaluator + + logging.basicConfig(format="%(message)s", level=logging.INFO) + + # Load a model + model = SparseEncoder("naver/splade-cocondenser-ensembledistil") + + # Load the STSB dataset (https://huggingface.co/datasets/sentence-transformers/stsb) + eval_dataset = load_dataset("sentence-transformers/stsb", split="validation") + + # Initialize the evaluator + dev_evaluator = SparseEmbeddingSimilarityEvaluator( + sentences1=eval_dataset["sentence1"], + sentences2=eval_dataset["sentence2"], + scores=eval_dataset["score"], + main_similarity=SimilarityFunction.COSINE, # even though the model is trained with dot, we need to set it to cosine for evaluation as the score in the dataset is cosine similarity + name="sts_dev", + ) + results = dev_evaluator(model) + ''' + EmbeddingSimilarityEvaluator: Evaluating the model on the sts_dev dataset: + Cosine-Similarity: Pearson: 0.8429 Spearman: 0.8366 + Model Sparsity: Active Dimensions: 78.3, Sparsity Ratio: 0.9974 + ''' + # Print the results + print(f"Primary metric: {dev_evaluator.primary_metric}") + # => Primary metric: sts_dev_spearman_cosine + print(f"Primary metric value: {results[dev_evaluator.primary_metric]:.4f}") + # => Primary metric value: 0.8366 + + """ + + def __init__( + self, + sentences1: list[str], + sentences2: list[str], + scores: list[float], + batch_size: int = 16, + main_similarity: str | SimilarityFunction | None = None, + similarity_fn_names: list[Literal["cosine", "euclidean", "manhattan", "dot"]] | None = None, + name: str = "", + show_progress_bar: bool = False, + write_csv: bool = True, + max_active_dims: int | None = None, + ): + self.max_active_dims = max_active_dims + self.sparsity_stats = defaultdict(list) + return super().__init__( + sentences1=sentences1, + sentences2=sentences2, + scores=scores, + batch_size=batch_size, + main_similarity=main_similarity, + similarity_fn_names=similarity_fn_names, + name=name, + show_progress_bar=show_progress_bar, + write_csv=write_csv, + precision=None, + ) + + def _append_csv_headers(self, similarity_fn_names: list[str]) -> None: + super()._append_csv_headers(similarity_fn_names) + self.csv_headers.extend(["active_dims", "sparsity_ratio"]) + + def __call__( + self, model: SparseEncoder, output_path: str | None = None, epoch: int = -1, steps: int = -1 + ) -> dict[str, float]: + self.sparsity_stats = defaultdict(list) + metrics = super().__call__(model=model, output_path=output_path, epoch=epoch, steps=steps) + for key, value in self.sparsity_stats.items(): + self.sparsity_stats[key] = sum(value) / len(value) + + metrics.update(self.prefix_name_to_metrics(self.sparsity_stats, self.name)) + self.store_metrics_in_model_card_data(model, metrics, epoch, steps) + logger.info( + f"Model Sparsity: Active Dimensions: {self.sparsity_stats['active_dims']:.1f}, Sparsity Ratio: {self.sparsity_stats['sparsity_ratio']:.4f}" + ) + if output_path is not None and self.write_csv: + append_to_last_row( + os.path.join(output_path, self.csv_file), + [self.sparsity_stats["active_dims"], self.sparsity_stats["sparsity_ratio"]], + ) + + return metrics + + def embed_inputs( + self, + model: SparseEncoder, + sentences: str | list[str] | np.ndarray, + **kwargs, + ) -> Tensor: + embeddings = model.encode( + sentences, + batch_size=self.batch_size, + show_progress_bar=self.show_progress_bar, + convert_to_sparse_tensor=True, + save_to_cpu=True, + max_active_dims=self.max_active_dims, + **kwargs, + ) + stat = model.sparsity(embeddings) + for key, value in stat.items(): + self.sparsity_stats[key].append(value) + return embeddings + + def store_metrics_in_model_card_data( + self, model: SparseEncoder, metrics: dict[str, Any], epoch: int = 0, step: int = 0 + ) -> None: + model.model_card_data.set_evaluation_metrics(self, metrics, epoch=epoch, step=step) + + def get_config_dict(self) -> dict[str, Any]: + config_dict = super().get_config_dict() + if self.max_active_dims is not None: + config_dict["max_active_dims"] = self.max_active_dims + return config_dict diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseInformationRetrievalEvaluator.py b/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseInformationRetrievalEvaluator.py new file mode 100644 index 0000000000000000000000000000000000000000..2f6b7f6d2131178a2a95f561333c4c1564bfdce9 --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseInformationRetrievalEvaluator.py @@ -0,0 +1,280 @@ +from __future__ import annotations + +import logging +import os +from collections import defaultdict +from typing import TYPE_CHECKING, Any, Callable + +import torch + +from sentence_transformers.evaluation import InformationRetrievalEvaluator +from sentence_transformers.util import append_to_last_row + +if TYPE_CHECKING: + import numpy as np + + from sentence_transformers.similarity_functions import SimilarityFunction + from sentence_transformers.sparse_encoder.SparseEncoder import SparseEncoder + + +logger = logging.getLogger(__name__) + + +class SparseInformationRetrievalEvaluator(InformationRetrievalEvaluator): + """ + This evaluator extends :class:`~sentence_transformers.evaluation.InformationRetrievalEvaluator` but is specifically designed for sparse encoder models. + + This class evaluates an Information Retrieval (IR) setting. + + Given a set of queries and a large corpus set. It will retrieve for each query the top-k most similar document. It measures + Mean Reciprocal Rank (MRR), Recall@k, and Normalized Discounted Cumulative Gain (NDCG) + + Args: + queries (Dict[str, str]): A dictionary mapping query IDs to queries. + corpus (Dict[str, str]): A dictionary mapping document IDs to documents. + relevant_docs (Dict[str, Set[str]]): A dictionary mapping query IDs to a set of relevant document IDs. + corpus_chunk_size (int): The size of each chunk of the corpus. Defaults to 50000. + mrr_at_k (List[int]): A list of integers representing the values of k for MRR calculation. Defaults to [10]. + ndcg_at_k (List[int]): A list of integers representing the values of k for NDCG calculation. Defaults to [10]. + accuracy_at_k (List[int]): A list of integers representing the values of k for accuracy calculation. Defaults to [1, 3, 5, 10]. + precision_recall_at_k (List[int]): A list of integers representing the values of k for precision and recall calculation. Defaults to [1, 3, 5, 10]. + map_at_k (List[int]): A list of integers representing the values of k for MAP calculation. Defaults to [100]. + show_progress_bar (bool): Whether to show a progress bar during evaluation. Defaults to False. + batch_size (int): The batch size for evaluation. Defaults to 32. + name (str): A name for the evaluation. Defaults to "". + write_csv (bool): Whether to write the evaluation results to a CSV file. Defaults to True. + max_active_dims (Optional[int], optional): The maximum number of active dimensions to use. + `None` uses the model's current `max_active_dims`. Defaults to None. + score_functions (Dict[str, Callable[[Tensor, Tensor], Tensor]]): A dictionary mapping score function names to score functions. Defaults to the ``similarity`` function from the ``model``. + main_score_function (Union[str, SimilarityFunction], optional): The main score function to use for evaluation. Defaults to None. + query_prompt (str, optional): The prompt to be used when encoding the corpus. Defaults to None. + query_prompt_name (str, optional): The name of the prompt to be used when encoding the corpus. Defaults to None. + corpus_prompt (str, optional): The prompt to be used when encoding the corpus. Defaults to None. + corpus_prompt_name (str, optional): The name of the prompt to be used when encoding the corpus. Defaults to None. + write_predictions (bool): Whether to write the predictions to a JSONL file. Defaults to False. + This can be useful for downstream evaluation as it can be used as input to the :class:`~sentence_transformers.sparse_encoder.evaluation.ReciprocalRankFusionEvaluator` that accept precomputed predictions. + + Example: + :: + + import logging + import random + + from datasets import load_dataset + + from sentence_transformers import SparseEncoder + from sentence_transformers.sparse_encoder.evaluation import SparseInformationRetrievalEvaluator + + logging.basicConfig(format="%(message)s", level=logging.INFO) + + # Load a model + model = SparseEncoder("naver/splade-cocondenser-ensembledistil") + + # Load the NFcorpus IR dataset (https://huggingface.co/datasets/BeIR/nfcorpus, https://huggingface.co/datasets/BeIR/nfcorpus-qrels) + corpus = load_dataset("BeIR/nfcorpus", "corpus", split="corpus") + queries = load_dataset("BeIR/nfcorpus", "queries", split="queries") + relevant_docs_data = load_dataset("BeIR/nfcorpus-qrels", split="test") + + # For this dataset, we want to concatenate the title and texts for the corpus + corpus = corpus.map(lambda x: {"text": x["title"] + " " + x["text"]}, remove_columns=["title"]) + + # Shrink the corpus size heavily to only the relevant documents + 1,000 random documents + required_corpus_ids = set(map(str, relevant_docs_data["corpus-id"])) + required_corpus_ids |= set(random.sample(corpus["_id"], k=1000)) + corpus = corpus.filter(lambda x: x["_id"] in required_corpus_ids) + + # Convert the datasets to dictionaries + corpus = dict(zip(corpus["_id"], corpus["text"])) # Our corpus (cid => document) + queries = dict(zip(queries["_id"], queries["text"])) # Our queries (qid => question) + relevant_docs = {} # Query ID to relevant documents (qid => set([relevant_cids]) + for qid, corpus_ids in zip(relevant_docs_data["query-id"], relevant_docs_data["corpus-id"]): + qid = str(qid) + corpus_ids = str(corpus_ids) + if qid not in relevant_docs: + relevant_docs[qid] = set() + relevant_docs[qid].add(corpus_ids) + + # Given queries, a corpus and a mapping with relevant documents, the SparseInformationRetrievalEvaluator computes different IR metrics. + ir_evaluator = SparseInformationRetrievalEvaluator( + queries=queries, + corpus=corpus, + relevant_docs=relevant_docs, + name="BeIR-nfcorpus-subset-test", + show_progress_bar=True, + batch_size=16, + ) + + # Run evaluation + results = ir_evaluator(model) + ''' + Queries: 323 + Corpus: 3269 + + Score-Function: dot + Accuracy@1: 50.77% + Accuracy@3: 64.40% + Accuracy@5: 66.87% + Accuracy@10: 71.83% + Precision@1: 50.77% + Precision@3: 40.45% + Precision@5: 34.06% + Precision@10: 25.98% + Recall@1: 6.27% + Recall@3: 11.69% + Recall@5: 13.74% + Recall@10: 17.23% + MRR@10: 0.5814 + NDCG@10: 0.3621 + MAP@100: 0.1838 + Model Query Sparsity: Active Dimensions: 40.0, Sparsity Ratio: 0.9987 + Model Corpus Sparsity: Active Dimensions: 206.2, Sparsity Ratio: 0.9932 + ''' + # Print the results + print(f"Primary metric: {ir_evaluator.primary_metric}") + # => Primary metric: BeIR-nfcorpus-subset-test_dot_ndcg@10 + print(f"Primary metric value: {results[ir_evaluator.primary_metric]:.4f}") + # => Primary metric value: 0.3621 + + """ + + def __init__( + self, + queries: dict[str, str], # qid => query + corpus: dict[str, str], # cid => doc + relevant_docs: dict[str, set[str]], # qid => Set[cid] + corpus_chunk_size: int = 50000, + mrr_at_k: list[int] = [10], + ndcg_at_k: list[int] = [10], + accuracy_at_k: list[int] = [1, 3, 5, 10], + precision_recall_at_k: list[int] = [1, 3, 5, 10], + map_at_k: list[int] = [100], + show_progress_bar: bool = False, + batch_size: int = 32, + name: str = "", + write_csv: bool = True, + max_active_dims: int | None = None, + score_functions: dict[str, Callable[[torch.Tensor, torch.Tensor], torch.Tensor]] | None = None, + main_score_function: str | SimilarityFunction | None = None, + query_prompt: str | None = None, + query_prompt_name: str | None = None, + corpus_prompt: str | None = None, + corpus_prompt_name: str | None = None, + write_predictions: bool = False, + ) -> None: + self.max_active_dims = max_active_dims + self.sparsity_stats = {"query": defaultdict(list), "corpus": defaultdict(list)} + self.corpus_lengths = [] + return super().__init__( + queries=queries, + corpus=corpus, + relevant_docs=relevant_docs, + corpus_chunk_size=corpus_chunk_size, + mrr_at_k=mrr_at_k, + ndcg_at_k=ndcg_at_k, + accuracy_at_k=accuracy_at_k, + precision_recall_at_k=precision_recall_at_k, + map_at_k=map_at_k, + show_progress_bar=show_progress_bar, + batch_size=batch_size, + name=name, + write_csv=write_csv, + score_functions=score_functions, + main_score_function=main_score_function, + query_prompt=query_prompt, + query_prompt_name=query_prompt_name, + corpus_prompt=corpus_prompt, + corpus_prompt_name=corpus_prompt_name, + write_predictions=write_predictions, + ) + + def _append_csv_headers(self, similarity_fn_names): + super()._append_csv_headers(similarity_fn_names) + self.csv_headers.extend( + ["query_active_dims", "query_sparsity_ratio", "corpus_active_dims", "corpus_sparsity_ratio"] + ) + + def __call__( + self, model: SparseEncoder, output_path: str | None = None, epoch: int = -1, steps: int = -1, *args, **kwargs + ) -> dict[str, float]: + self.sparsity_stats = {"query": defaultdict(list), "corpus": defaultdict(list)} + self.corpus_lengths = [] + metrics = super().__call__(model=model, output_path=output_path, epoch=epoch, steps=steps) + for prefix in ["query", "corpus"]: + for key, value in self.sparsity_stats[prefix].items(): + if prefix == "query": + self.sparsity_stats[prefix][key] = sum(value) / len(value) + else: + self.sparsity_stats[prefix][key] = sum( + val * length for val, length in zip(value, self.corpus_lengths) + ) / sum(self.corpus_lengths) + self.sparsity_stats = { + f"{prefix}_{key}": value for prefix, values in self.sparsity_stats.items() for key, value in values.items() + } + metrics.update(self.prefix_name_to_metrics(self.sparsity_stats, self.name)) + self.store_metrics_in_model_card_data(model, metrics, epoch, steps) + logger.info( + f"Model Query Sparsity: Active Dimensions: {self.sparsity_stats['query_active_dims']:.1f}, Sparsity Ratio: {self.sparsity_stats['query_sparsity_ratio']:.4f}" + ) + logger.info( + f"Model Corpus Sparsity: Active Dimensions: {self.sparsity_stats['corpus_active_dims']:.1f}, Sparsity Ratio: {self.sparsity_stats['corpus_sparsity_ratio']:.4f}" + ) + if output_path is not None and self.write_csv: + append_to_last_row(os.path.join(output_path, self.csv_file), self.sparsity_stats.values()) + + return metrics + + def compute_metrices( + self, + model: SparseEncoder, + corpus_model=None, + corpus_embeddings: torch.Tensor | None = None, + output_path: str | None = None, + ) -> dict[str, float]: + return super().compute_metrices( + model=model, corpus_model=corpus_model, corpus_embeddings=corpus_embeddings, output_path=output_path + ) + + def embed_inputs( + self, + model: SparseEncoder, + sentences: str | list[str] | np.ndarray, + encode_fn_name: str | None = None, + prompt_name: str | None = None, + prompt: str | None = None, + **kwargs, + ) -> torch.Tensor: + if encode_fn_name is None: + encode_fn = model.encode + elif encode_fn_name == "query": + encode_fn = model.encode_query + elif encode_fn_name == "document": + encode_fn = model.encode_document + embeddings = encode_fn( + sentences, + prompt_name=prompt_name, + prompt=prompt, + batch_size=self.batch_size, + show_progress_bar=self.show_progress_bar, + convert_to_sparse_tensor=True, + save_to_cpu=True, + max_active_dims=self.max_active_dims, + **kwargs, + ) + stat = model.sparsity(embeddings) + prefix = "query" if encode_fn_name in ["query", None] else "corpus" + for key, value in stat.items(): + self.sparsity_stats[prefix][key].append(value) + if prefix == "corpus": + self.corpus_lengths.append(len(sentences)) + return embeddings + + def store_metrics_in_model_card_data( + self, model: SparseEncoder, metrics: dict[str, Any], epoch: int = 0, step: int = 0 + ) -> None: + model.model_card_data.set_evaluation_metrics(self, metrics, epoch=epoch, step=step) + + def get_config_dict(self) -> dict[str, Any]: + config_dict = super().get_config_dict() + if self.max_active_dims is not None: + config_dict["max_active_dims"] = self.max_active_dims + return config_dict diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseMSEEvaluator.py b/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseMSEEvaluator.py new file mode 100644 index 0000000000000000000000000000000000000000..941e0e89b1fdcc099a21873c976b8722fb1c1cbf --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseMSEEvaluator.py @@ -0,0 +1,170 @@ +from __future__ import annotations + +import logging +import os +from collections import defaultdict +from typing import TYPE_CHECKING, Any + +from sentence_transformers.evaluation import MSEEvaluator +from sentence_transformers.util import append_to_last_row + +if TYPE_CHECKING: + import numpy as np + from torch import Tensor + + from sentence_transformers.sparse_encoder import SparseEncoder + +logger = logging.getLogger(__name__) + + +class SparseMSEEvaluator(MSEEvaluator): + """ + This evaluator extends :class:`~sentence_transformers.evaluation.MSEEvaluator` but is specifically designed for sparse encoder models. + + Note that this evaluator doesn't take benefit of the sparse tensor torch representation yet, so memory issues may occur. + + Computes the mean squared error (x100) between the computed sentence embedding + and some target sentence embedding. + + The MSE is computed between ``||teacher.encode(source_sentences) - student.encode(target_sentences)||``. + + For multilingual knowledge distillation (https://arxiv.org/abs/2004.09813), source_sentences are in English + and target_sentences are in a different language like German, Chinese, Spanish... + + Args: + source_sentences (List[str]): Source sentences to embed with the teacher model. + target_sentences (List[str]): Target sentences to embed with the student model. + teacher_model (SparseEncoder, optional): The teacher model to compute the source sentence embeddings. + show_progress_bar (bool, optional): Show progress bar when computing embeddings. Defaults to False. + batch_size (int, optional): Batch size to compute sentence embeddings. Defaults to 32. + name (str, optional): Name of the evaluator. Defaults to "". + write_csv (bool, optional): Write results to CSV file. Defaults to True. + max_active_dims (Optional[int], optional): The maximum number of active dimensions to use. + `None` uses the model's current `max_active_dims`. Defaults to None. + + Example: + :: + + import logging + + from datasets import load_dataset + + from sentence_transformers import SparseEncoder + from sentence_transformers.sparse_encoder.evaluation import SparseMSEEvaluator + + logging.basicConfig(format="%(message)s", level=logging.INFO) + + # Load a model + student_model = SparseEncoder("prithivida/Splade_PP_en_v1") + teacher_model = SparseEncoder("naver/splade-cocondenser-ensembledistil") + + # Load any dataset with some texts + dataset = load_dataset("sentence-transformers/stsb", split="validation") + sentences = dataset["sentence1"] + dataset["sentence2"] + + # Given queries, a corpus and a mapping with relevant documents, the SparseMSEEvaluator computes different MSE metrics. + mse_evaluator = SparseMSEEvaluator( + source_sentences=sentences, + target_sentences=sentences, + teacher_model=teacher_model, + name="stsb-dev", + ) + results = mse_evaluator(student_model) + ''' + MSE evaluation (lower = better) on the stsb-dev dataset: + MSE (*100): 0.034905 + Model Sparsity: Active Dimensions: 54.6, Sparsity Ratio: 0.9982 + ''' + # Print the results + print(f"Primary metric: {mse_evaluator.primary_metric}") + # => Primary metric: stsb-dev_negative_mse + print(f"Primary metric value: {results[mse_evaluator.primary_metric]:.4f}") + # => Primary metric value: -0.0349 + """ + + def __init__( + self, + source_sentences: list[str], + target_sentences: list[str], + teacher_model=None, + show_progress_bar: bool = False, + batch_size: int = 32, + name: str = "", + write_csv: bool = True, + max_active_dims: int | None = None, + ): + self.max_active_dims = max_active_dims + self.sparsity_stats = defaultdict(list) + super().__init__( + source_sentences=source_sentences, + target_sentences=target_sentences, + teacher_model=teacher_model, + show_progress_bar=show_progress_bar, + batch_size=batch_size, + name=name, + write_csv=write_csv, + ) + self.csv_headers.extend(["active_dims", "sparsity_ratio"]) + logger.warning( + "The SparseMSEEvaluator is not handling the mse compute with sparse tensors yet. Memory issues may occur." + ) + + def __call__( + self, + model: SparseEncoder, + output_path: str | None = None, + epoch: int = -1, + steps: int = -1, + ) -> dict[str, float]: + self.sparsity_stats = defaultdict(list) + metrics = super().__call__(model=model, output_path=output_path, epoch=epoch, steps=steps) + for key, value in self.sparsity_stats.items(): + self.sparsity_stats[key] = sum(value) / len(value) + + metrics.update(self.prefix_name_to_metrics(self.sparsity_stats, self.name)) + self.store_metrics_in_model_card_data(model, metrics, epoch, steps) + logger.info( + f"Model Sparsity: Active Dimensions: {self.sparsity_stats['active_dims']:.1f}, Sparsity Ratio: {self.sparsity_stats['sparsity_ratio']:.4f}" + ) + if output_path is not None and self.write_csv: + append_to_last_row( + os.path.join(output_path, self.csv_file), + [self.sparsity_stats["active_dims"], self.sparsity_stats["sparsity_ratio"]], + ) + + return metrics + + def embed_inputs( + self, + model: SparseEncoder, + sentences: str | list[str] | np.ndarray, + **kwargs, + ) -> Tensor: + embeddings = model.encode( + sentences, + batch_size=self.batch_size, + show_progress_bar=self.show_progress_bar, + convert_to_sparse_tensor=False, + save_to_cpu=True, + max_active_dims=self.max_active_dims, + **kwargs, + ) + stat = model.sparsity(embeddings) + for key, value in stat.items(): + self.sparsity_stats[key].append(value) + return embeddings + + def store_metrics_in_model_card_data( + self, + model: SparseEncoder, + metrics: dict[str, Any], + epoch: int = 0, + step: int = 0, + ) -> None: + model.model_card_data.set_evaluation_metrics(self, metrics, epoch=epoch, step=step) + + def get_config_dict(self) -> dict[str, Any]: + config_dict = super().get_config_dict() + if self.max_active_dims is not None: + config_dict["max_active_dims"] = self.max_active_dims + return config_dict diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseNanoBEIREvaluator.py b/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseNanoBEIREvaluator.py new file mode 100644 index 0000000000000000000000000000000000000000..1385598f566666863ae907f17a71a7828bb3dbd3 --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseNanoBEIREvaluator.py @@ -0,0 +1,262 @@ +from __future__ import annotations + +import logging +import os +from collections import defaultdict +from typing import TYPE_CHECKING, Any, Callable + +import numpy as np + +from sentence_transformers.evaluation.NanoBEIREvaluator import NanoBEIREvaluator +from sentence_transformers.sparse_encoder.evaluation.SparseInformationRetrievalEvaluator import ( + SparseInformationRetrievalEvaluator, +) +from sentence_transformers.util import append_to_last_row + +if TYPE_CHECKING: + from torch import Tensor + + from sentence_transformers.evaluation import SimilarityFunction + from sentence_transformers.evaluation.NanoBEIREvaluator import DatasetNameType + from sentence_transformers.sparse_encoder import SparseEncoder + +logger = logging.getLogger(__name__) + + +class SparseNanoBEIREvaluator(NanoBEIREvaluator): + """ + This evaluator extends :class:`~sentence_transformers.evaluation.NanoBEIREvaluator` but is specifically designed for sparse encoder models. + + This class evaluates the performance of a SparseEncoder Model on the NanoBEIR collection of Information Retrieval datasets. + + The collection is a set of datasets based on the BEIR collection, but with a significantly smaller size, so it can + be used for quickly evaluating the retrieval performance of a model before committing to a full evaluation. + The datasets are available on Hugging Face in the `NanoBEIR collection `_. + This evaluator will return the same metrics as the InformationRetrievalEvaluator (i.e., MRR, nDCG, Recall@k), for each dataset and on average. + + Args: + dataset_names (List[str]): The names of the datasets to evaluate on. Defaults to all datasets. + mrr_at_k (List[int]): A list of integers representing the values of k for MRR calculation. Defaults to [10]. + ndcg_at_k (List[int]): A list of integers representing the values of k for NDCG calculation. Defaults to [10]. + accuracy_at_k (List[int]): A list of integers representing the values of k for accuracy calculation. Defaults to [1, 3, 5, 10]. + precision_recall_at_k (List[int]): A list of integers representing the values of k for precision and recall calculation. Defaults to [1, 3, 5, 10]. + map_at_k (List[int]): A list of integers representing the values of k for MAP calculation. Defaults to [100]. + show_progress_bar (bool): Whether to show a progress bar during evaluation. Defaults to False. + batch_size (int): The batch size for evaluation. Defaults to 32. + write_csv (bool): Whether to write the evaluation results to a CSV file. Defaults to True. + max_active_dims (Optional[int], optional): The maximum number of active dimensions to use. + `None` uses the model's current `max_active_dims`. Defaults to None. + score_functions (Dict[str, Callable[[Tensor, Tensor], Tensor]]): A dictionary mapping score function names to score functions. Defaults to {SimilarityFunction.COSINE.value: cos_sim, SimilarityFunction.DOT_PRODUCT.value: dot_score}. + main_score_function (Union[str, SimilarityFunction], optional): The main score function to use for evaluation. Defaults to None. + aggregate_fn (Callable[[list[float]], float]): The function to aggregate the scores. Defaults to np.mean. + aggregate_key (str): The key to use for the aggregated score. Defaults to "mean". + query_prompts (str | dict[str, str], optional): The prompts to add to the queries. If a string, will add the same prompt to all queries. If a dict, expects that all datasets in dataset_names are keys. + corpus_prompts (str | dict[str, str], optional): The prompts to add to the corpus. If a string, will add the same prompt to all corpus. If a dict, expects that all datasets in dataset_names are keys. + write_predictions (bool): Whether to write the predictions to a JSONL file. Defaults to False. + This can be useful for downstream evaluation as it can be used as input to the :class:`~sentence_transformers.sparse_encoder.evaluation.ReciprocalRankFusionEvaluator` that accept precomputed predictions. + + Example: + :: + + import logging + + from sentence_transformers import SparseEncoder + from sentence_transformers.sparse_encoder.evaluation import SparseNanoBEIREvaluator + + logging.basicConfig(format="%(message)s", level=logging.INFO) + + # Load a model + model = SparseEncoder("naver/splade-cocondenser-ensembledistil") + + datasets = ["QuoraRetrieval", "MSMARCO"] + + evaluator = SparseNanoBEIREvaluator( + dataset_names=datasets, + show_progress_bar=True, + batch_size=32, + ) + + # Run evaluation + results = evaluator(model) + ''' + Evaluating NanoQuoraRetrieval + Information Retrieval Evaluation of the model on the NanoQuoraRetrieval dataset: + Queries: 50 + Corpus: 5046 + + Score-Function: dot + Accuracy@1: 92.00% + Accuracy@3: 96.00% + Accuracy@5: 98.00% + Accuracy@10: 100.00% + Precision@1: 92.00% + Precision@3: 40.00% + Precision@5: 24.80% + Precision@10: 13.20% + Recall@1: 79.73% + Recall@3: 92.53% + Recall@5: 94.93% + Recall@10: 98.27% + MRR@10: 0.9439 + NDCG@10: 0.9339 + MAP@100: 0.9070 + Model Query Sparsity: Active Dimensions: 59.4, Sparsity Ratio: 0.9981 + Model Corpus Sparsity: Active Dimensions: 61.9, Sparsity Ratio: 0.9980 + + Information Retrieval Evaluation of the model on the NanoMSMARCO dataset: + Queries: 50 + Corpus: 5043 + + Score-Function: dot + Accuracy@1: 48.00% + Accuracy@3: 74.00% + Accuracy@5: 76.00% + Accuracy@10: 86.00% + Precision@1: 48.00% + Precision@3: 24.67% + Precision@5: 15.20% + Precision@10: 8.60% + Recall@1: 48.00% + Recall@3: 74.00% + Recall@5: 76.00% + Recall@10: 86.00% + MRR@10: 0.6191 + NDCG@10: 0.6780 + MAP@100: 0.6277 + Model Query Sparsity: Active Dimensions: 45.4, Sparsity Ratio: 0.9985 + Model Corpus Sparsity: Active Dimensions: 122.6, Sparsity Ratio: 0.9960 + + Average Queries: 50.0 + Average Corpus: 5044.5 + Aggregated for Score Function: dot + Accuracy@1: 70.00% + Accuracy@3: 85.00% + Accuracy@5: 87.00% + Accuracy@10: 93.00% + Precision@1: 70.00% + Recall@1: 63.87% + Precision@3: 32.33% + Recall@3: 83.27% + Precision@5: 20.00% + Recall@5: 85.47% + Precision@10: 10.90% + Recall@10: 92.13% + MRR@10: 0.7815 + NDCG@10: 0.8060 + Model Query Sparsity: Active Dimensions: 52.4, Sparsity Ratio: 0.9983 + Model Corpus Sparsity: Active Dimensions: 92.2, Sparsity Ratio: 0.9970 + ''' + # Print the results + print(f"Primary metric: {evaluator.primary_metric}") + # => Primary metric: NanoBEIR_mean_dot_ndcg@10 + print(f"Primary metric value: {results[evaluator.primary_metric]:.4f}") + # => Primary metric value: 0.8060 + + """ + + information_retrieval_class = SparseInformationRetrievalEvaluator + + def __init__( + self, + dataset_names: list[DatasetNameType] | None = None, + mrr_at_k: list[int] = [10], + ndcg_at_k: list[int] = [10], + accuracy_at_k: list[int] = [1, 3, 5, 10], + precision_recall_at_k: list[int] = [1, 3, 5, 10], + map_at_k: list[int] = [100], + show_progress_bar: bool = False, + batch_size: int = 32, + write_csv: bool = True, + max_active_dims: int | None = None, + score_functions: dict[str, Callable[[Tensor, Tensor], Tensor]] | None = None, + main_score_function: str | SimilarityFunction | None = None, + aggregate_fn: Callable[[list[float]], float] = np.mean, + aggregate_key: str = "mean", + query_prompts: str | dict[str, str] | None = None, + corpus_prompts: str | dict[str, str] | None = None, + write_predictions: bool = False, + ): + self.max_active_dims = max_active_dims + self.sparsity_stats = defaultdict(list) + super().__init__( + dataset_names=dataset_names, + mrr_at_k=mrr_at_k, + ndcg_at_k=ndcg_at_k, + accuracy_at_k=accuracy_at_k, + precision_recall_at_k=precision_recall_at_k, + map_at_k=map_at_k, + show_progress_bar=show_progress_bar, + batch_size=batch_size, + write_csv=write_csv, + score_functions=score_functions, + main_score_function=main_score_function, + aggregate_fn=aggregate_fn, + aggregate_key=aggregate_key, + query_prompts=query_prompts, + corpus_prompts=corpus_prompts, + write_predictions=write_predictions, + ) + if self.max_active_dims is not None: + self.name += f"_{self.max_active_dims}" + + def _get_human_readable_name(self, dataset_name: DatasetNameType) -> str: + human_readable_name = super()._get_human_readable_name(dataset_name) + if self.max_active_dims is not None: + human_readable_name += f"_{self.max_active_dims}" + return human_readable_name + + def _append_csv_headers(self, similarity_fn_names): + super()._append_csv_headers(similarity_fn_names) + self.csv_headers.extend( + ["query_active_dims", "query_sparsity_ratio", "corpus_active_dims", "corpus_sparsity_ratio"] + ) + + def __call__( + self, model: SparseEncoder, output_path: str | None = None, epoch: int = -1, steps: int = -1, *args, **kwargs + ) -> dict[str, float]: + self.sparsity_stats = defaultdict(list) + self.lengths = defaultdict(list) + per_dataset_results = super().__call__( + model, output_path=output_path, epoch=epoch, steps=steps, *args, **kwargs + ) + for evaluator in self.evaluators: + self.lengths["query"].append(len(evaluator.queries)) + self.lengths["corpus"].append(len(evaluator.corpus)) + for key, value in evaluator.sparsity_stats.items(): + self.sparsity_stats[key].append(value) + for key, value in self.sparsity_stats.items(): + self.sparsity_stats[key] = sum( + val * length for val, length in zip(value, self.lengths[key.split("_")[0]]) + ) / sum(self.lengths[key.split("_")[0]]) + + per_dataset_results.update(self.prefix_name_to_metrics(self.sparsity_stats, self.name)) + aggregated_results = { + key: value for key, value in per_dataset_results.items() if key.startswith(self.name) and key != self.name + } + self.store_metrics_in_model_card_data(model, aggregated_results, epoch, steps) + logger.info( + f"Model Query Sparsity: Active Dimensions: {self.sparsity_stats['query_active_dims']:.1f}, Sparsity Ratio: {self.sparsity_stats['query_sparsity_ratio']:.4f}" + ) + logger.info( + f"Model Corpus Sparsity: Active Dimensions: {self.sparsity_stats['corpus_active_dims']:.1f}, Sparsity Ratio: {self.sparsity_stats['corpus_sparsity_ratio']:.4f}" + ) + if output_path is not None and self.write_csv: + append_to_last_row( + os.path.join(output_path, self.csv_file), + self.sparsity_stats.values(), + ) + + return per_dataset_results + + def _load_dataset( + self, dataset_name: DatasetNameType, **ir_evaluator_kwargs + ) -> SparseInformationRetrievalEvaluator: + ir_evaluator_kwargs["max_active_dims"] = self.max_active_dims + ir_evaluator_kwargs.pop("truncate_dim", None) + return super()._load_dataset(dataset_name, **ir_evaluator_kwargs) + + def get_config_dict(self) -> dict[str, Any]: + config_dict = super().get_config_dict() + if self.max_active_dims is not None: + config_dict["max_active_dims"] = self.max_active_dims + return config_dict diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseRerankingEvaluator.py b/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseRerankingEvaluator.py new file mode 100644 index 0000000000000000000000000000000000000000..26cb3c2a68c07cce9176c3c34fa6836577be9651 --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseRerankingEvaluator.py @@ -0,0 +1,211 @@ +from __future__ import annotations + +import logging +import os +from collections import defaultdict +from typing import TYPE_CHECKING, Any, Callable + +import torch + +from sentence_transformers.evaluation import RerankingEvaluator +from sentence_transformers.util import append_to_last_row, cos_sim + +if TYPE_CHECKING: + import numpy as np + + from sentence_transformers.sparse_encoder.SparseEncoder import SparseEncoder + +logger = logging.getLogger(__name__) + + +class SparseRerankingEvaluator(RerankingEvaluator): + """ + This evaluator extends :class:`~sentence_transformers.evaluation.RerankingEvaluator' but is specifically designed for sparse encoder models. + + This class evaluates a SparseEncoder model for the task of re-ranking. + + Given a query and a list of documents, it computes the score [query, doc_i] for all possible + documents and sorts them in decreasing order. Then, MRR@10, NDCG@10 and MAP is compute to measure the quality of the ranking. + + Args: + samples (list): A list of dictionaries, where each dictionary represents a sample and has the following keys: + + - 'query': The search query. + - 'positive': A list of positive (relevant) documents. + - 'negative': A list of negative (irrelevant) documents. + at_k (int, optional): Only consider the top k most similar documents to each query for the evaluation. Defaults to 10. + name (str, optional): Name of the evaluator. Defaults to "". + write_csv (bool, optional): Write results to CSV file. Defaults to True. + similarity_fct (Callable[[torch.Tensor, torch.Tensor], torch.Tensor], optional): Similarity function between sentence embeddings. By default, cosine similarity. Defaults to cos_sim. + batch_size (int, optional): Batch size to compute sentence embeddings. Defaults to 64. + show_progress_bar (bool, optional): Show progress bar when computing embeddings. Defaults to False. + use_batched_encoding (bool, optional): Whether or not to encode queries and documents in batches for greater speed, or 1-by-1 to save memory. Defaults to True. + max_active_dims (Optional[int], optional): The maximum number of active dimensions to use. + `None` uses the model's current `max_active_dims`. Defaults to None. + mrr_at_k (Optional[int], optional): Deprecated parameter. Please use `at_k` instead. Defaults to None. + + Example: + :: + + import logging + + from datasets import load_dataset + + from sentence_transformers import SparseEncoder + from sentence_transformers.sparse_encoder.evaluation import SparseRerankingEvaluator + + logging.basicConfig(format="%(message)s", level=logging.INFO) + + # Load a model + model = SparseEncoder("naver/splade-cocondenser-ensembledistil") + + # Load a dataset with queries, positives, and negatives + eval_dataset = load_dataset("microsoft/ms_marco", "v1.1", split="validation").select(range(1000)) + + samples = [ + { + "query": sample["query"], + "positive": [ + text + for is_selected, text in zip(sample["passages"]["is_selected"], sample["passages"]["passage_text"]) + if is_selected + ], + "negative": [ + text + for is_selected, text in zip(sample["passages"]["is_selected"], sample["passages"]["passage_text"]) + if not is_selected + ], + } + for sample in eval_dataset + ] + + + # Now evaluate using only the documents from the 1000 samples + reranking_evaluator = SparseRerankingEvaluator( + samples=samples, + name="ms-marco-dev-small", + show_progress_bar=True, + batch_size=32, + ) + + results = reranking_evaluator(model) + ''' + RerankingEvaluator: Evaluating the model on the ms-marco-dev-small dataset: + Queries: 967 Positives: Min 1.0, Mean 1.1, Max 3.0 Negatives: Min 1.0, Mean 7.1, Max 9.0 + MAP: 53.41 + MRR@10: 54.14 + NDCG@10: 65.06 + Model Query Sparsity: Active Dimensions: 42.2, Sparsity Ratio: 0.9986 + Model Corpus Sparsity: Active Dimensions: 126.5, Sparsity Ratio: 0.9959 + ''' + # Print the results + print(f"Primary metric: {reranking_evaluator.primary_metric}") + # => Primary metric: ms-marco-dev-small_ndcg@10 + print(f"Primary metric value: {results[reranking_evaluator.primary_metric]:.4f}") + # => Primary metric value: 0.6506 + + """ + + def __init__( + self, + samples: list[dict[str, str | list[str]]], + at_k: int = 10, + name: str = "", + write_csv: bool = True, + similarity_fct: Callable[[torch.Tensor, torch.Tensor], torch.Tensor] = cos_sim, + batch_size: int = 64, + show_progress_bar: bool = False, + use_batched_encoding: bool = True, + max_active_dims: int | None = None, + mrr_at_k: int | None = None, + ): + self.max_active_dims = max_active_dims + self.sparsity_stats = defaultdict(list) + super().__init__( + samples=samples, + at_k=at_k, + name=name, + write_csv=write_csv, + similarity_fct=similarity_fct, + batch_size=batch_size, + show_progress_bar=show_progress_bar, + use_batched_encoding=use_batched_encoding, + mrr_at_k=mrr_at_k, + ) + self.csv_headers.extend( + ["query_active_dims", "query_sparsity_ratio", "corpus_active_dims", "corpus_sparsity_ratio"] + ) + + def __call__( + self, model: SparseEncoder, output_path: str | None = None, epoch: int = -1, steps: int = -1 + ) -> dict[str, float]: + self.sparsity_stats = defaultdict(list) + metrics = super().__call__(model=model, output_path=output_path, epoch=epoch, steps=steps) + for key, value in self.sparsity_stats.items(): + self.sparsity_stats[key] = sum(value) / len(value) + + metrics.update(self.prefix_name_to_metrics(self.sparsity_stats, self.name)) + self.store_metrics_in_model_card_data(model, metrics, epoch, steps) + logger.info( + f"Model Query Sparsity: Active Dimensions: {self.sparsity_stats['query_active_dims']:.1f}, Sparsity Ratio: {self.sparsity_stats['query_sparsity_ratio']:.4f}" + ) + logger.info( + f"Model Corpus Sparsity: Active Dimensions: {self.sparsity_stats['corpus_active_dims']:.1f}, Sparsity Ratio: {self.sparsity_stats['corpus_sparsity_ratio']:.4f}" + ) + if output_path is not None and self.write_csv: + append_to_last_row( + os.path.join(output_path, self.csv_file), + self.sparsity_stats.values(), + ) + + return metrics + + def compute_metrices(self, model: SparseEncoder): + return super().compute_metrices(model) + + def compute_metrices_batched(self, model: SparseEncoder): + return super().compute_metrices_batched(model) + + def compute_metrices_individual(self, model: SparseEncoder): + return super().compute_metrices_individual(model) + + def embed_inputs( + self, + model: SparseEncoder, + sentences: str | list[str] | np.ndarray, + encode_fn_name: str | None = None, + show_progress_bar: bool | None = None, + **kwargs, + ) -> torch.Tensor: + if encode_fn_name is None: + encode_fn = model.encode + elif encode_fn_name == "query": + encode_fn = model.encode_query + elif encode_fn_name == "document": + encode_fn = model.encode_document + embeddings = encode_fn( + sentences, + batch_size=self.batch_size, + show_progress_bar=show_progress_bar, + convert_to_sparse_tensor=True, + convert_to_tensor=False, # as we are using slicing on sparse tensors that is not supported so we want to keep a list of sparse tensors + save_to_cpu=True, + max_active_dims=self.max_active_dims, + **kwargs, + ) + stat = model.sparsity(torch.stack(embeddings)) + prefix = "query" if encode_fn_name in ["query", None] else "corpus" + for key, value in stat.items(): + self.sparsity_stats[f"{prefix}_{key}"].append(value) + return embeddings + + def store_metrics_in_model_card_data( + self, model: SparseEncoder, metrics: dict[str, Any], epoch: int = 0, step: int = 0 + ) -> None: + model.model_card_data.set_evaluation_metrics(self, metrics, epoch=epoch, step=step) + + def get_config_dict(self) -> dict[str, Any]: + config_dict = super().get_config_dict() + if self.max_active_dims is not None: + config_dict["max_active_dims"] = self.max_active_dims + return config_dict diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseTranslationEvaluator.py b/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseTranslationEvaluator.py new file mode 100644 index 0000000000000000000000000000000000000000..fb8673635ad05408688105e0a976a8949e035e9c --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseTranslationEvaluator.py @@ -0,0 +1,158 @@ +from __future__ import annotations + +import logging +import os +from collections import defaultdict +from typing import TYPE_CHECKING, Any + +import torch + +from sentence_transformers.evaluation import TranslationEvaluator +from sentence_transformers.util import append_to_last_row + +if TYPE_CHECKING: + import numpy as np + from torch import Tensor + + from sentence_transformers.sparse_encoder.SparseEncoder import SparseEncoder + + +logger = logging.getLogger(__name__) + + +class SparseTranslationEvaluator(TranslationEvaluator): + """ + This evaluator extends :class:`~sentence_transformers.evaluation.TranslationEvaluator` but is specifically designed for sparse encoder models. + + Given two sets of sentences in different languages, e.g. (en_1, en_2, en_3...) and (fr_1, fr_2, fr_3, ...), + and assuming that fr_i is the translation of en_i. + Checks if vec(en_i) has the highest similarity to vec(fr_i). Computes the accuracy in both directions + + The labels need to indicate the similarity between the sentences. + + Args: + source_sentences (List[str]): List of sentences in the source language. + target_sentences (List[str]): List of sentences in the target language. + show_progress_bar (bool): Whether to show a progress bar when computing embeddings. Defaults to False. + batch_size (int): The batch size to compute sentence embeddings. Defaults to 16. + name (str): The name of the evaluator. Defaults to an empty string. + print_wrong_matches (bool): Whether to print incorrect matches. Defaults to False. + write_csv (bool): Whether to write the evaluation results to a CSV file. Defaults to True. + max_active_dims (Optional[int], optional): The maximum number of active dimensions to use. + `None` uses the model's current `max_active_dims`. Defaults to None. + + Example: + :: + + import logging + + from datasets import load_dataset + + from sentence_transformers import SparseEncoder + from sentence_transformers.sparse_encoder.evaluation import SparseTranslationEvaluator + + logging.basicConfig(format="%(message)s", level=logging.INFO) + + # Load a model, not mutilingual but hope to see some on the hub soon + model = SparseEncoder("naver/splade-cocondenser-ensembledistil") + + # Load a parallel sentences dataset + dataset = load_dataset("sentence-transformers/parallel-sentences-news-commentary", "en-nl", split="train[:1000]") + + # Initialize the TranslationEvaluator using the same texts from two languages + translation_evaluator = SparseTranslationEvaluator( + source_sentences=dataset["english"], + target_sentences=dataset["non_english"], + name="news-commentary-en-nl", + ) + results = translation_evaluator(model) + ''' + Evaluating translation matching Accuracy of the model on the news-commentary-en-nl dataset: + Accuracy src2trg: 41.40 + Accuracy trg2src: 47.60 + Model Sparsity: Active Dimensions: 112.3, Sparsity Ratio: 0.9963 + ''' + # Print the results + print(f"Primary metric: {translation_evaluator.primary_metric}") + # => Primary metric: news-commentary-en-nl_mean_accuracy + print(f"Primary metric value: {results[translation_evaluator.primary_metric]:.4f}") + # => Primary metric value: 0.4450 + + """ + + def __init__( + self, + source_sentences: list[str], + target_sentences: list[str], + show_progress_bar: bool = False, + batch_size: int = 16, + name: str = "", + print_wrong_matches: bool = False, + write_csv: bool = True, + max_active_dims: int | None = None, + ): + self.max_active_dims = max_active_dims + self.sparsity_stats = defaultdict(list) + super().__init__( + source_sentences, + target_sentences, + show_progress_bar=show_progress_bar, + batch_size=batch_size, + name=name, + print_wrong_matches=print_wrong_matches, + write_csv=write_csv, + ) + self.csv_headers.extend(["active_dims", "sparsity_ratio"]) + + def __call__( + self, model: SparseEncoder, output_path: str | None = None, epoch: int = -1, steps: int = -1 + ) -> dict[str, float]: + self.sparsity_stats = defaultdict(list) + metrics = super().__call__(model=model, output_path=output_path, epoch=epoch, steps=steps) + for key, value in self.sparsity_stats.items(): + self.sparsity_stats[key] = sum(value) / len(value) + + metrics.update(self.prefix_name_to_metrics(self.sparsity_stats, self.name)) + self.store_metrics_in_model_card_data(model, metrics, epoch, steps) + logger.info( + f"Model Sparsity: Active Dimensions: {self.sparsity_stats['active_dims']:.1f}, Sparsity Ratio: {self.sparsity_stats['sparsity_ratio']:.4f}" + ) + if output_path is not None and self.write_csv: + append_to_last_row( + os.path.join(output_path, self.csv_file), + [self.sparsity_stats["active_dims"], self.sparsity_stats["sparsity_ratio"]], + ) + + return metrics + + def embed_inputs( + self, + model: SparseEncoder, + sentences: str | list[str] | np.ndarray, + **kwargs, + ) -> list[Tensor]: + embeddings = model.encode( + sentences, + batch_size=self.batch_size, + show_progress_bar=self.show_progress_bar, + convert_to_tensor=False, + convert_to_sparse_tensor=True, + save_to_cpu=True, + max_active_dims=self.max_active_dims, + **kwargs, + ) + stat = model.sparsity(torch.stack(embeddings)) + for key, value in stat.items(): + self.sparsity_stats[key].append(value) + return embeddings + + def store_metrics_in_model_card_data( + self, model: SparseEncoder, metrics: dict[str, Any], epoch: int = 0, step: int = 0 + ) -> None: + model.model_card_data.set_evaluation_metrics(self, metrics, epoch=epoch, step=step) + + def get_config_dict(self) -> dict[str, Any]: + config_dict = super().get_config_dict() + if self.max_active_dims is not None: + config_dict["max_active_dims"] = self.max_active_dims + return config_dict diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseTripletEvaluator.py b/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseTripletEvaluator.py new file mode 100644 index 0000000000000000000000000000000000000000..637f13c0e862940ad07e82731859a3421c384f54 --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/SparseTripletEvaluator.py @@ -0,0 +1,191 @@ +from __future__ import annotations + +import logging +import os +from collections import defaultdict +from typing import TYPE_CHECKING, Any, Literal + +import torch + +from sentence_transformers.evaluation import TripletEvaluator +from sentence_transformers.util import append_to_last_row + +if TYPE_CHECKING: + import numpy as np + + from sentence_transformers.similarity_functions import SimilarityFunction + from sentence_transformers.sparse_encoder.SparseEncoder import SparseEncoder + + +logger = logging.getLogger(__name__) + + +class SparseTripletEvaluator(TripletEvaluator): + """ + This evaluator extends :class:`~sentence_transformers.evaluation.TripletEvaluator` but is specifically designed for sparse encoder models. + + Evaluate a model based on a triplet: (sentence, positive_example, negative_example). + Checks if ``similarity(sentence, positive_example) < similarity(sentence, negative_example) + margin``. + + Args: + anchors (List[str]): Sentences to check similarity to. (e.g. a query) + positives (List[str]): List of positive sentences + negatives (List[str]): List of negative sentences + main_similarity_function (Union[str, SimilarityFunction], optional): + The similarity function to use. If not specified, use cosine similarity, + dot product, Euclidean, and Manhattan similarity. Defaults to None. + margin (Union[float, Dict[str, float]], optional): Margins for various similarity metrics. + If a float is provided, it will be used as the margin for all similarity metrics. + If a dictionary is provided, the keys should be 'cosine', 'dot', 'manhattan', and 'euclidean'. + The value specifies the minimum margin by which the negative sample should be further from + the anchor than the positive sample. Defaults to None. + name (str): Name for the output. Defaults to "". + batch_size (int): Batch size used to compute embeddings. Defaults to 16. + show_progress_bar (bool): If true, prints a progress bar. Defaults to False. + write_csv (bool): Write results to a CSV file. Defaults to True. + max_active_dims (Optional[int], optional): The maximum number of active dimensions to use. + `None` uses the model's current `max_active_dims`. Defaults to None. + similarity_fn_names (List[str], optional): List of similarity function names to evaluate. + If not specified, evaluate using the ``model.similarity_fn_name``. + Defaults to None. + + Example: + :: + + import logging + + from datasets import load_dataset + + from sentence_transformers import SparseEncoder + from sentence_transformers.sparse_encoder.evaluation import SparseTripletEvaluator + + logging.basicConfig(format="%(message)s", level=logging.INFO) + + # Load a model + model = SparseEncoder("naver/splade-cocondenser-ensembledistil") + + # Load triplets from the AllNLI dataset + # The dataset contains triplets of (anchor, positive, negative) sentences + dataset = load_dataset("sentence-transformers/all-nli", "triplet", split="dev[:1000]") + + # Initialize the SparseTripletEvaluator + evaluator = SparseTripletEvaluator( + anchors=dataset[:1000]["anchor"], + positives=dataset[:1000]["positive"], + negatives=dataset[:1000]["negative"], + name="all_nli_dev", + batch_size=32, + show_progress_bar=True, + ) + + # Run the evaluation + results = evaluator(model) + ''' + TripletEvaluator: Evaluating the model on the all_nli_dev dataset: + Accuracy Dot Similarity: 85.40% + Model Anchor Sparsity: Active Dimensions: 103.0, Sparsity Ratio: 0.9966 + Model Positive Sparsity: Active Dimensions: 67.4, Sparsity Ratio: 0.9978 + Model Negative Sparsity: Active Dimensions: 65.9, Sparsity Ratio: 0.9978 + ''' + # Print the results + print(f"Primary metric: {evaluator.primary_metric}") + # => Primary metric: all_nli_dev_dot_accuracy + print(f"Primary metric value: {results[evaluator.primary_metric]:.4f}") + # => Primary metric value: 0.8540 + + """ + + def __init__( + self, + anchors: list[str], + positives: list[str], + negatives: list[str], + main_similarity_function: str | SimilarityFunction | None = None, + margin: float | dict[str, float] | None = None, + name: str = "", + batch_size: int = 16, + show_progress_bar: bool = False, + write_csv: bool = True, + max_active_dims: int | None = None, + similarity_fn_names: list[Literal["cosine", "dot", "euclidean", "manhattan"]] | None = None, + main_distance_function: str | SimilarityFunction | None = "deprecated", + ): + self.max_active_dims = max_active_dims + self.sparsity_stats = defaultdict(list) + return super().__init__( + anchors=anchors, + positives=positives, + negatives=negatives, + main_similarity_function=main_similarity_function, + margin=margin, + name=name, + batch_size=batch_size, + show_progress_bar=show_progress_bar, + write_csv=write_csv, + similarity_fn_names=similarity_fn_names, + main_distance_function=main_distance_function, + ) + + def _append_csv_headers(self, similarity_fn_names): + super()._append_csv_headers(similarity_fn_names) + for prefix in ["anchor", "positive", "negative"]: + self.csv_headers.extend([f"{prefix}_active_dims", f"{prefix}_sparsity_ratio"]) + + def __call__( + self, model: SparseEncoder, output_path: str | None = None, epoch: int = -1, steps: int = -1 + ) -> dict[str, float]: + self.sparsity_stats = defaultdict(list) + metrics = super().__call__(model=model, output_path=output_path, epoch=epoch, steps=steps) + for key, value in self.sparsity_stats.items(): + self.sparsity_stats[key] = sum(value) / len(value) + + metrics.update(self.prefix_name_to_metrics(self.sparsity_stats, self.name)) + self.store_metrics_in_model_card_data(model, metrics, epoch, steps) + logger.info( + f"Model Anchor Sparsity: Active Dimensions: {self.sparsity_stats['anchor_active_dims']:.1f}, Sparsity Ratio: {self.sparsity_stats['anchor_sparsity_ratio']:.4f}" + ) + logger.info( + f"Model Positive Sparsity: Active Dimensions: {self.sparsity_stats['positive_active_dims']:.1f}, Sparsity Ratio: {self.sparsity_stats['positive_sparsity_ratio']:.4f}" + ) + logger.info( + f"Model Negative Sparsity: Active Dimensions: {self.sparsity_stats['negative_active_dims']:.1f}, Sparsity Ratio: {self.sparsity_stats['negative_sparsity_ratio']:.4f}" + ) + if output_path is not None and self.write_csv: + append_to_last_row( + os.path.join(output_path, self.csv_file), + self.sparsity_stats.values(), + ) + + return metrics + + def embed_inputs( + self, + model: SparseEncoder, + sentences: str | list[str] | np.ndarray, + **kwargs, + ) -> torch.Tensor: + embeddings = model.encode( + sentences, + batch_size=self.batch_size, + show_progress_bar=self.show_progress_bar, + convert_to_sparse_tensor=True, + save_to_cpu=True, + max_active_dims=self.max_active_dims, + **kwargs, + ) + stat = model.sparsity(embeddings) + prefix = ["anchor", "positive", "negative"][len(self.sparsity_stats) // 2] + for key, value in stat.items(): + self.sparsity_stats[f"{prefix}_{key}"].append(value) + return embeddings + + def store_metrics_in_model_card_data( + self, model: SparseEncoder, metrics: dict[str, Any], epoch: int = 0, step: int = 0 + ) -> None: + model.model_card_data.set_evaluation_metrics(self, metrics, epoch=epoch, step=step) + + def get_config_dict(self) -> dict[str, Any]: + config_dict = super().get_config_dict() + if self.max_active_dims is not None: + config_dict["max_active_dims"] = self.max_active_dims + return config_dict diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/__init__.py b/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..139fe5d81e26641ff91462a92ada17db90f5a25e --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/evaluation/__init__.py @@ -0,0 +1,41 @@ +from __future__ import annotations + +from sentence_transformers.sparse_encoder.evaluation.ReciprocalRankFusionEvaluator import ( + ReciprocalRankFusionEvaluator, +) +from sentence_transformers.sparse_encoder.evaluation.SparseBinaryClassificationEvaluator import ( + SparseBinaryClassificationEvaluator, +) +from sentence_transformers.sparse_encoder.evaluation.SparseEmbeddingSimilarityEvaluator import ( + SparseEmbeddingSimilarityEvaluator, +) +from sentence_transformers.sparse_encoder.evaluation.SparseInformationRetrievalEvaluator import ( + SparseInformationRetrievalEvaluator, +) +from sentence_transformers.sparse_encoder.evaluation.SparseMSEEvaluator import ( + SparseMSEEvaluator, +) +from sentence_transformers.sparse_encoder.evaluation.SparseNanoBEIREvaluator import ( + SparseNanoBEIREvaluator, +) +from sentence_transformers.sparse_encoder.evaluation.SparseRerankingEvaluator import ( + SparseRerankingEvaluator, +) +from sentence_transformers.sparse_encoder.evaluation.SparseTranslationEvaluator import ( + SparseTranslationEvaluator, +) +from sentence_transformers.sparse_encoder.evaluation.SparseTripletEvaluator import ( + SparseTripletEvaluator, +) + +__all__ = [ + "SparseEmbeddingSimilarityEvaluator", + "SparseInformationRetrievalEvaluator", + "SparseBinaryClassificationEvaluator", + "SparseMSEEvaluator", + "SparseNanoBEIREvaluator", + "SparseTripletEvaluator", + "SparseTranslationEvaluator", + "SparseRerankingEvaluator", + "ReciprocalRankFusionEvaluator", +] diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/CSRLoss.py b/sentence-transformers/sentence_transformers/sparse_encoder/losses/CSRLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..06e2ec2e3b3200eed86f9d822a2656e59371b92b --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/losses/CSRLoss.py @@ -0,0 +1,228 @@ +from __future__ import annotations + +from collections.abc import Iterable + +import torch +import torch.nn as nn +import torch.nn.functional as F + +from sentence_transformers.sparse_encoder.losses.SparseMultipleNegativesRankingLoss import ( + SparseMultipleNegativesRankingLoss, +) +from sentence_transformers.sparse_encoder.SparseEncoder import SparseEncoder + + +def normalized_mean_squared_error(reconstruction: torch.Tensor, original_input: torch.Tensor) -> torch.Tensor: + """ + :param reconstruction: output of Autoencoder.decode (shape: [batch, n_inputs]) + :param original_input: input of Autoencoder.encode (shape: [batch, n_inputs]) + :return: normalized mean squared error (shape: [1]) + """ + original_input_mean = original_input.mean(dim=0) + loss = F.mse_loss(reconstruction, original_input) / F.mse_loss( + original_input_mean[None, :].broadcast_to(original_input.shape), original_input + ) + return loss + + +class CSRReconstructionLoss(nn.Module): + def __init__(self, model: SparseEncoder, beta: float = 1.0) -> None: + """ + CSRReconstructionLoss implements the reconstruction loss component for Contrastive Sparse Representation (CSR) models. + + This loss ensures that the sparse encoding can accurately reconstruct the original model embeddings through + three components: + + 1. A primary reconstruction loss (L_k) that measures the error between the original embedding and its + reconstruction using the top-k sparse components. + 2. A secondary reconstruction loss (L_4k) that measures the error using the top-4k sparse components. + 3. An auxiliary loss (L_aux) that helps to learn residual information. + + Args: + model: SparseEncoder model with autoencoder components + beta: Weight for the auxiliary loss component (L_aux) + + References: + - For more details, see the paper "Beyond Matryoshka: Revisiting Sparse Coding for Adaptive Representation" + https://arxiv.org/abs/2503.01776 + + Requirements: + 1. The model must be configured to output the necessary reconstruction components + 2. Used with SparseEncoder models that implement compositional sparse autoencoding + + Relations: + - Used as a component within :class:`CSRLoss` combined with a contrastive loss + + Example: + - This loss is never used standalone, but instead used within the :class:`CSRLoss` class. See that loss for more details. + """ + super().__init__() + self.model = model + self.beta = beta + + def forward(self, sentence_features: Iterable[dict[str, torch.Tensor]]) -> dict[str, torch.Tensor]: + raise NotImplementedError( + "CSRReconstructionLoss is not intended to be used standalone. Use it within CSRLoss instead." + ) + + def compute_loss_from_embeddings(self, outputs: list[dict[str, torch.Tensor]]) -> dict[str, torch.Tensor]: + """ + Compute the CSRReconstruction loss from embeddings. + + Args: + outputs: List of dictionaries containing sentence embeddings and their sparse representations + + Returns: + total_loss: The total reconstruction loss value + """ + # Initialize loss components + total_L_k = 0.0 + total_L_4k = 0.0 + total_L_aux = 0.0 + + # Process each sentence feature + for features in outputs: + x = features["sentence_embedding_backbone"] + recons_k = features["decoded_embedding_k"] + recons_4k = features["decoded_embedding_4k"] + recons_aux = features["decoded_embedding_aux"] + reconsk_pre_bias = features["decoded_embedding_k_pre_bias"] + + # L(k) = ||f(x) - f(dx)_k||₂² + L_k = F.mse_loss(x, recons_k) + + # L(4k) = ||f(x) - f(dx)_4k||₂² + L_4k = F.mse_loss(x, recons_4k) + + # L_aux = ||e - ê||₂² + L_aux = normalized_mean_squared_error(recons_aux, x - reconsk_pre_bias.detach()) + + # Accumulate losses + total_L_k += L_k + total_L_4k += L_4k + total_L_aux += L_aux + + # Average losses over batch + num_columns = len(outputs) + if num_columns > 0: + total_L_k /= num_columns + total_L_4k /= num_columns + total_L_aux /= num_columns + + # return the total losses as a dictionary, they'll be summed for a final reconstruction loss + return { + "reconstruction_loss_k": total_L_k, + "reconstruction_loss_4k": total_L_4k / 8.0, + "reconstruction_loss_aux": self.beta * total_L_aux, + } + + def get_config_dict(self): + """ + Get the configuration dictionary. + + Returns: + Dictionary containing the configuration parameters + """ + return {"beta": self.beta} + + +class CSRLoss(nn.Module): + def __init__(self, model: SparseEncoder, loss: nn.Module | None = None, beta: float = 0.1, gamma: float = 1.0): + """ + CSRLoss implements a combined loss function for Contrastive Sparse Representation (CSR) models. + + This loss combines two components: + + 1. A reconstruction loss :class:`CSRReconstructionLoss` that ensures the sparse representation can faithfully + reconstruct the original embedding. + 2. A main loss, which in the paper is a :class:`SparseMultipleNegativesRankingLoss` that ensures semantically + similar sentences have similar representations. + + The total loss is linear combination of the two losses. + + Args: + model: SparseEncoder model + loss: The principal loss function to use can be any of the SparseEncoder losses except flops loss and CSRReconstruction loss. + If None, the default loss is used, which is the SparseMultipleNegativesRankingLoss. + beta: Weight for the L_aux component in the reconstruction loss. Default is 0.1. + gamma: Weight for the main loss component (MNRL a.k.a. InfoNCE by default). Default is 1.0. + + References: + - For more details, see the paper "Beyond Matryoshka: Revisiting Sparse Coding for Adaptive Representation" + https://arxiv.org/abs/2503.01776 + + Requirements: + 1. Input requirements depend on the chosen loss + 2. Uses autoencoder components of the SparseEncoder model + + Relations: + - Uses :class:`CSRReconstructionLoss` for the reconstruction component + + Example: + :: + + from datasets import Dataset + from sentence_transformers.sparse_encoder import SparseEncoder, SparseEncoderTrainer, losses + + model = SparseEncoder("sentence-transformers/all-MiniLM-L6-v2") + train_dataset = Dataset.from_dict( + { + "anchor": ["It's nice weather outside today.", "He drove to work."], + "positive": ["It's so sunny.", "He took the car to the office."], + "negative": ["It's quite rainy, sadly.", "She walked to the store."], + } + ) + loss = losses.CSRLoss(model, beta=0.1, gamma=1.0) + + trainer = SparseEncoderTrainer(model=model, train_dataset=train_dataset, loss=loss) + trainer.train() + """ + super().__init__() + self.model = model + self.beta = beta + self.gamma = gamma + + # Initialize the component losses + self.reconstruction_loss = CSRReconstructionLoss(model=model, beta=beta) + self.loss = loss if loss is not None else SparseMultipleNegativesRankingLoss(model=model) + + def forward( + self, sentence_features: Iterable[dict[str, torch.Tensor]], labels: torch.Tensor | None = None + ) -> dict[str, torch.Tensor]: + # Compute embeddings using the model + outputs = [self.model(sentence_feature) for sentence_feature in sentence_features] + sentence_embedding = [output["sentence_embedding"] for output in outputs] + + losses = self.reconstruction_loss.compute_loss_from_embeddings(outputs) + base_loss = self.loss.compute_loss_from_embeddings(sentence_embedding, labels) + # Handle the two cases: dictionary of losses or a single loss value + if isinstance(base_loss, dict): + for key, value in base_loss.items(): + losses[key] = value * self.gamma + else: + losses["base_loss"] = base_loss * self.gamma + + return losses + + def get_config_dict(self): + """ + Get the configuration dictionary. + + Returns: + Dictionary containing the configuration parameters + """ + return {"beta": self.beta, "gamma": self.gamma, "loss": self.loss} + + @property + def citation(self) -> str: + return """ +@misc{wen2025matryoshkarevisitingsparsecoding, + title={Beyond Matryoshka: Revisiting Sparse Coding for Adaptive Representation}, + author={Tiansheng Wen and Yifei Wang and Zequn Zeng and Zhong Peng and Yudi Su and Xinyang Liu and Bo Chen and Hongwei Liu and Stefanie Jegelka and Chenyu You}, + year={2025}, + eprint={2503.01776}, + archivePrefix={arXiv}, + primaryClass={cs.LG}, + url={https://arxiv.org/abs/2503.01776}, +} +""" diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/FlopsLoss.py b/sentence-transformers/sentence_transformers/sparse_encoder/losses/FlopsLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..a2cd63948970503ece76cb59f47aa662933c5ed0 --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/losses/FlopsLoss.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +from collections.abc import Iterable + +import torch +from torch import Tensor, nn + +from sentence_transformers.sparse_encoder.SparseEncoder import SparseEncoder + + +class FlopsLoss(nn.Module): + def __init__(self, model: SparseEncoder, threshold: float | None = None) -> None: + """ + FlopsLoss implements a regularization technique to promote sparsity in sparse encoder models. + It calculates the squared L2 norm of the mean embedding vector, which helps reduce the number of floating-point + operations (FLOPs) required during inference by encouraging more zero values in the embeddings. + It can use a threshold to ignore embeddings with too few non-zero (active) elements. + + This loss is used as a regularization component within other losses like :class:`SpladeLoss` rather than + being used as a standalone loss function. + + Args: + model: SparseEncoder model to be regularized + threshold: Optional threshold for the number of non-zero (active) elements in the embeddings. + If specified, only embeddings with more than this number of non-zero (active) elements will be considered. + This can help to ignore embeddings that are too sparse and may not contribute meaningfully to the loss. + + References: + - For further details, see: https://arxiv.org/pdf/2004.05665 for the general FLOPS loss and https://arxiv.org/pdf/2504.14839 for FLOPS with thresholds, a.k.a. FLOPS with l0 masking. + + Relations: + - Used as a component within :class:`SpladeLoss` to regularize both query and document embeddings + + Example: + - This loss is typically used within the :class:`SpladeLoss` class, which combines it with other loss components. + + """ + super().__init__() + self.model = model + self.threshold = threshold + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + raise NotImplementedError( + "FlopsLoss is not intended to be used directly. Use it as a regulizer within the SpladeLoss class." + ) + + def compute_loss_from_embeddings(self, embeddings: list[torch.Tensor]) -> torch.Tensor: + if self.threshold is not None: + l0_norm = (embeddings != 0).sum(dim=1) + mask = (l0_norm > self.threshold).float() + embeddings = embeddings * mask.unsqueeze(1) + + return torch.sum(torch.mean(embeddings, dim=0) ** 2) + + @property + def citation(self) -> str: + return """ +@article{paria2020minimizing, + title={Minimizing flops to learn efficient sparse representations}, + author={Paria, Biswajit and Yeh, Chih-Kuan and Yen, Ian EH and Xu, Ning and Ravikumar, Pradeep and P{\'o}czos, Barnab{\'a}s}, + journal={arXiv preprint arXiv:2004.05665}, + year={2020} +} +""" diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseAnglELoss.py b/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseAnglELoss.py new file mode 100644 index 0000000000000000000000000000000000000000..9d4fbb7e30063085a75cdc067b7900ffbcb344c5 --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseAnglELoss.py @@ -0,0 +1,78 @@ +from __future__ import annotations + +from collections.abc import Iterable + +from torch import Tensor + +from sentence_transformers import util +from sentence_transformers.sparse_encoder.losses.SparseCoSENTLoss import SparseCoSENTLoss +from sentence_transformers.sparse_encoder.SparseEncoder import SparseEncoder + + +class SparseAnglELoss(SparseCoSENTLoss): + def __init__(self, model: SparseEncoder, scale: float = 20.0) -> None: + """ + This class implements AnglE (Angle Optimized). + This is a modification of :class:`SparseCoSENTLoss`, designed to address the following issue: + The cosine function's gradient approaches 0 as the wave approaches the top or bottom of its form. + This can hinder the optimization process, so AnglE proposes to instead optimize the angle difference + in complex space in order to mitigate this effect. + + It expects that each of the InputExamples consists of a pair of texts and a float valued label, representing + the expected similarity score between the pair. + + It computes the following loss function: + + ``loss = logsum(1+exp(s(k,l)-s(i,j))+exp...)``, where ``(i,j)`` and ``(k,l)`` are any of the input pairs in the + batch such that the expected similarity of ``(i,j)`` is greater than ``(k,l)``. The summation is over all possible + pairs of input pairs in the batch that match this condition. This is the same as CoSENTLoss, with a different + similarity function. + + Args: + model: SparseEncoder + scale: Output of similarity function is multiplied by scale + value. Represents the inverse temperature. + + References: + - For further details, see: https://arxiv.org/abs/2309.12871v1 + + Requirements: + - Need to be used in SpladeLoss or CSRLoss as a loss function. + - Sentence pairs with corresponding similarity scores in range of the similarity function. Default is [-1,1]. + + Inputs: + +--------------------------------+------------------------+ + | Texts | Labels | + +================================+========================+ + | (sentence_A, sentence_B) pairs | float similarity score | + +--------------------------------+------------------------+ + + Relations: + - :class:`SparseCoSENTLoss` is AnglELoss with ``pairwise_cos_sim`` as the metric, rather than ``pairwise_angle_sim``. + + Example: + :: + + from datasets import Dataset + + from sentence_transformers.sparse_encoder import SparseEncoder, SparseEncoderTrainer, losses + + model = SparseEncoder("distilbert/distilbert-base-uncased") + train_dataset = Dataset.from_dict( + { + "sentence1": ["It's nice weather outside today.", "He drove to work."], + "sentence2": ["It's so sunny.", "She walked to the store."], + "score": [1.0, 0.3], + } + ) + loss = losses.SpladeLoss( + model=model, loss=losses.SparseAnglELoss(model), document_regularizer_weight=5e-5, use_document_regularizer_only=True + ) + + trainer = SparseEncoderTrainer(model=model, train_dataset=train_dataset, loss=loss) + trainer.train() + """ + return super().__init__(model, scale, similarity_fct=util.pairwise_angle_sim) + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + raise AttributeError("SparseAnglELoss should not be used alone. Use it with SpladeLoss or CSRLoss.") diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseCoSENTLoss.py b/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseCoSENTLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..cd841fceb473a6656ccf8346c418c0c60248b9ed --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseCoSENTLoss.py @@ -0,0 +1,76 @@ +from __future__ import annotations + +from collections.abc import Iterable + +from torch import Tensor + +from sentence_transformers import util +from sentence_transformers.losses.CoSENTLoss import CoSENTLoss +from sentence_transformers.sparse_encoder.SparseEncoder import SparseEncoder + + +class SparseCoSENTLoss(CoSENTLoss): + def __init__(self, model: SparseEncoder, scale: float = 20.0, similarity_fct=util.cos_sim) -> None: + """ + This class implements CoSENT (Cosine Sentence). + It expects that each of the InputExamples consists of a pair of texts and a float valued label, representing + the expected similarity score between the pair. + + It computes the following loss function: + + ``loss = logsum(1+exp(s(i,j)-s(k,l))+exp...)``, where ``(i,j)`` and ``(k,l)`` are any of the input pairs in the + batch such that the expected similarity of ``(i,j)`` is greater than ``(k,l)``. The summation is over all possible + pairs of input pairs in the batch that match this condition. + + Args: + model: SparseEncoder + similarity_fct: Function to compute the PAIRWISE similarity + between embeddings. Default is + ``util.pairwise_cos_sim``. + scale: Output of similarity function is multiplied by scale + value. Represents the inverse temperature. + + References: + - For further details, see: https://kexue.fm/archives/8847 + + Requirements: + - Need to be used in SpladeLoss or CSRLoss as a loss function. + - Sentence pairs with corresponding similarity scores in range of the similarity function. Default is [-1,1]. + + Inputs: + +--------------------------------+------------------------+ + | Texts | Labels | + +================================+========================+ + | (sentence_A, sentence_B) pairs | float similarity score | + +--------------------------------+------------------------+ + + Relations: + - :class:`SparseAnglELoss` is SparseCoSENTLoss with ``pairwise_angle_sim`` as the metric, rather than ``pairwise_cos_sim``. + + Example: + :: + + from datasets import Dataset + + from sentence_transformers.sparse_encoder import SparseEncoder, SparseEncoderTrainer, losses + + model = SparseEncoder("distilbert/distilbert-base-uncased") + train_dataset = Dataset.from_dict( + { + "sentence1": ["It's nice weather outside today.", "He drove to work."], + "sentence2": ["It's so sunny.", "She walked to the store."], + "score": [1.0, 0.3], + } + ) + loss = losses.SpladeLoss( + model=model, loss=losses.SparseCoSENTLoss(model), document_regularizer_weight=5e-5, use_document_regularizer_only=True + ) + + trainer = SparseEncoderTrainer(model=model, train_dataset=train_dataset, loss=loss) + trainer.train() + """ + model.similarity_fn_name = "cosine" + return super().__init__(model, scale=scale, similarity_fct=similarity_fct) + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + raise AttributeError("SparseCoSENTLoss should not be used alone. Use it with SpladeLoss or CSRLoss.") diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseCosineSimilarityLoss.py b/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseCosineSimilarityLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..bdf1f8a4aec204393a9adf27c145fd65e70f75bf --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseCosineSimilarityLoss.py @@ -0,0 +1,76 @@ +from __future__ import annotations + +from collections.abc import Iterable + +import torch.nn as nn +from torch import Tensor + +from sentence_transformers.losses.CosineSimilarityLoss import CosineSimilarityLoss +from sentence_transformers.sparse_encoder.SparseEncoder import SparseEncoder + + +class SparseCosineSimilarityLoss(CosineSimilarityLoss): + def __init__( + self, + model: SparseEncoder, + loss_fct: nn.Module = nn.MSELoss(), + cos_score_transformation: nn.Module = nn.Identity(), + ) -> None: + """ + SparseCosineSimilarityLoss expects that the InputExamples consists of two texts and a float label. It computes the + vectors ``u = model(sentence_A)`` and ``v = model(sentence_B)`` and measures the cosine-similarity between the two. + By default, it minimizes the following loss: ``||input_label - cos_score_transformation(cosine_sim(u,v))||_2``. + + Args: + model: SparseEncoder model + loss_fct: Which pytorch loss function should be used to + compare the ``cosine_similarity(u, v)`` with the + input_label? By default, MSE is used: ``||input_label - + cosine_sim(u, v)||_2`` + cos_score_transformation: The cos_score_transformation + function is applied on top of cosine_similarity. By + default, the identify function is used (i.e. no change). + + Requirements: + - Need to be used in SpladeLoss or CSRLoss as a loss function. + - Sentence pairs with corresponding similarity scores in range `[0, 1]` + + Inputs: + +--------------------------------+------------------------+ + | Texts | Labels | + +================================+========================+ + | (sentence_A, sentence_B) pairs | float similarity score | + +--------------------------------+------------------------+ + + Relations: + - :class:`SparseAnglELoss` is :class:`SparseCoSENTLoss` with ``pairwise_angle_sim`` as the metric, rather than ``pairwise_cos_sim``. + + Example: + :: + + from datasets import Dataset + from sentence_transformers.sparse_encoder import SparseEncoder, SparseEncoderTrainer, losses + + model = SparseEncoder("distilbert/distilbert-base-uncased") + train_dataset = Dataset.from_dict( + { + "sentence1": ["It's nice weather outside today.", "He drove to work."], + "sentence2": ["It's so sunny.", "She walked to the store."], + "score": [1.0, 0.3], + } + ) + loss = losses.SpladeLoss( + model=model, + loss=losses.SparseCosineSimilarityLoss(model), + document_regularizer_weight=5e-5, + use_document_regularizer_only=True, + ) + + trainer = SparseEncoderTrainer(model=model, train_dataset=train_dataset, loss=loss) + trainer.train() + """ + model.similarity_fn_name = "cosine" + return super().__init__(model, loss_fct=loss_fct, cos_score_transformation=cos_score_transformation) + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + raise AttributeError("SparseCosineSimilarityLoss should not be used alone. Use it with SpladeLoss or CSRLoss.") diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseDistillKLDivLoss.py b/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseDistillKLDivLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..6ede74693257638e7df031b94aceb28cac9cd3c6 --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseDistillKLDivLoss.py @@ -0,0 +1,139 @@ +from __future__ import annotations + +from collections.abc import Iterable + +from torch import Tensor + +from sentence_transformers import util +from sentence_transformers.losses.DistillKLDivLoss import DistillKLDivLoss +from sentence_transformers.sparse_encoder.SparseEncoder import SparseEncoder + + +class SparseDistillKLDivLoss(DistillKLDivLoss): + def __init__(self, model: SparseEncoder, similarity_fct=util.pairwise_dot_score, temperature: float = 2.0) -> None: + """ + Compute the KL divergence loss between probability distributions derived from student and teacher models' similarity scores. + By default, similarity is calculated using the dot-product. This loss is designed for knowledge distillation + where a smaller student model learns from a more powerful teacher model. + + The loss computes softmax probabilities from the teacher similarity scores and log-softmax probabilities + from the student model, then calculates the KL divergence between these distributions. + + Args: + model: SentenceTransformer model (student model) + similarity_fct: Which similarity function to use for the student model + temperature: Temperature parameter to soften probability distributions (higher temperature = softer distributions) + When combined with other losses, a temperature of 1.0 is also viable, but a higher temperature (e.g., 2.0 or 4.0) + can help prevent the student model from going to zero active dimensions. Defaults to 2.0. + + References: + - For more details, please refer to https://arxiv.org/abs/2010.11386 + + Requirements: + 1. Need to be used in SpladeLoss or CSRLoss as a loss function. + 2. (query, positive, negative_1, ..., negative_n) examples + 3. Labels containing teacher model's scores between query-positive and query-negative pairs + + Inputs: + +------------------------------------------------+------------------------------------------------------------+ + | Texts | Labels | + +================================================+============================================================+ + | (query, positive, negative) | [Teacher(query, positive), Teacher(query, negative)] | + +------------------------------------------------+------------------------------------------------------------+ + | (query, positive, negative_1, ..., negative_n) | [Teacher(query, positive), Teacher(query, negative_i)...] | + +------------------------------------------------+------------------------------------------------------------+ + + Relations: + - Similar to :class:`~sentence_transformers.sparse_encoder.losses.SparseMarginMSELoss` but uses KL divergence instead of MSE + - More suited for distillation tasks where preserving ranking is important + + Example: + + Using a teacher model to compute similarity scores for distillation: + + :: + + import torch + from datasets import Dataset + + from sentence_transformers.sparse_encoder import SparseEncoder, SparseEncoderTrainer, losses + + student_model = SparseEncoder("distilbert/distilbert-base-uncased") + teacher_model = SparseEncoder("naver/splade-cocondenser-ensembledistil") + train_dataset = Dataset.from_dict( + { + "query": ["It's nice weather outside today.", "He drove to work."], + "positive": ["It's so sunny.", "He took the car to work."], + "negative": ["It's very cold.", "She walked to the store."], + } + ) + + + def compute_labels(batch): + emb_queries = teacher_model.encode(batch["query"]) + emb_positives = teacher_model.encode(batch["positive"]) + emb_negatives = teacher_model.encode(batch["negative"]) + + pos_scores = teacher_model.similarity_pairwise(emb_queries, emb_positives) + neg_scores = teacher_model.similarity_pairwise(emb_queries, emb_negatives) + + # Stack the scores for positive and negative pairs + return {"label": torch.stack([pos_scores, neg_scores], dim=1)} + + + train_dataset = train_dataset.map(compute_labels, batched=True) + loss = losses.SpladeLoss( + student_model, loss=losses.SparseDistillKLDivLoss(student_model), document_regularizer_weight=3e-5, query_regularizer_weight=5e-5 + ) + + trainer = SparseEncoderTrainer(model=student_model, train_dataset=train_dataset, loss=loss) + trainer.train() + + + With multiple negatives: + + :: + + import torch + from datasets import Dataset + + from sentence_transformers.sparse_encoder import SparseEncoder, SparseEncoderTrainer, losses + + student_model = SparseEncoder("distilbert/distilbert-base-uncased") + teacher_model = SparseEncoder("naver/splade-cocondenser-ensembledistil") + train_dataset = Dataset.from_dict( + { + "query": ["It's nice weather outside today.", "He drove to work."], + "positive": ["It's so sunny.", "He took the car to work."], + "negative1": ["It's very cold.", "She walked to the store."], + "negative2": ["Its rainy", "She took the bus"], + } + ) + + + def compute_labels(batch): + emb_queries = teacher_model.encode(batch["query"]) + emb_positives = teacher_model.encode(batch["positive"]) + emb_negatives1 = teacher_model.encode(batch["negative1"]) + emb_negatives2 = teacher_model.encode(batch["negative2"]) + + pos_scores = teacher_model.similarity_pairwise(emb_queries, emb_positives) + neg_scores1 = teacher_model.similarity_pairwise(emb_queries, emb_negatives1) + neg_scores2 = teacher_model.similarity_pairwise(emb_queries, emb_negatives2) + + # Stack the scores for positive and multiple negative pairs + return {"label": torch.stack([pos_scores, neg_scores1, neg_scores2], dim=1)} + + + train_dataset = train_dataset.map(compute_labels, batched=True) + loss = losses.SpladeLoss( + student_model, loss=losses.SparseDistillKLDivLoss(student_model), document_regularizer_weight=3e-5, query_regularizer_weight=5e-5 + ) + + trainer = SparseEncoderTrainer(model=student_model, train_dataset=train_dataset, loss=loss) + trainer.train() + """ + super().__init__(model, similarity_fct=similarity_fct, temperature=temperature) + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + raise AttributeError("SparseDistillKLDivLoss should not be used alone. Use it with SpladeLoss or CSRLoss.") diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseMSELoss.py b/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseMSELoss.py new file mode 100644 index 0000000000000000000000000000000000000000..8e32c250db76e234424123352915dcca007b757e --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseMSELoss.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +from sentence_transformers.losses.MSELoss import MSELoss +from sentence_transformers.sparse_encoder.SparseEncoder import SparseEncoder + + +class SparseMSELoss(MSELoss): + def __init__(self, model: SparseEncoder) -> None: + """ + Computes the MSE loss between the computed sentence embedding and a target sentence embedding. This loss + is used when extending sentence embeddings to new languages as described in our publication + Making Monolingual Sentence Embeddings Multilingual using Knowledge Distillation. + + Args: + model: SparseEncoder + + Requirements: + 1. Usually uses a finetuned teacher M in a knowledge distillation setup + + Inputs: + +-----------------------------------------+-----------------------------+ + | Texts | Labels | + +=========================================+=============================+ + | sentence | model sentence embeddings | + +-----------------------------------------+-----------------------------+ + | sentence_1, sentence_2, ..., sentence_N | model sentence embeddings | + +-----------------------------------------+-----------------------------+ + + Relations: + - :class:`SparseMarginMSELoss` is equivalent to this loss, but with a margin through a negative pair. + + Example: + :: + + from datasets import Dataset + from sentence_transformers.sparse_encoder import SparseEncoder, SparseEncoderTrainer, losses + + student_model = SparseEncoder("prithivida/Splade_PP_en_v1") + teacher_model = SparseEncoder("naver/splade-cocondenser-ensembledistil") + train_dataset = Dataset.from_dict( + { + "english": ["The first sentence", "The second sentence", "The third sentence", "The fourth sentence"], + "french": ["La première phrase", "La deuxième phrase", "La troisième phrase", "La quatrième phrase"], + } + ) + + + def compute_labels(batch): + return {"label": teacher_model.encode(batch["english"], convert_to_sparse_tensor=False)} + + + train_dataset = train_dataset.map(compute_labels, batched=True) + loss = losses.SparseMSELoss(student_model) + + trainer = SparseEncoderTrainer(model=student_model, train_dataset=train_dataset, loss=loss) + trainer.train() + + """ + return super().__init__(model) diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseMarginMSELoss.py b/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseMarginMSELoss.py new file mode 100644 index 0000000000000000000000000000000000000000..d14a2eedba8b0b3477e4cdba7cca098e0e3d803f --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseMarginMSELoss.py @@ -0,0 +1,176 @@ +from __future__ import annotations + +from collections.abc import Iterable + +from torch import Tensor + +from sentence_transformers import util +from sentence_transformers.losses.MarginMSELoss import MarginMSELoss +from sentence_transformers.sparse_encoder.SparseEncoder import SparseEncoder + + +class SparseMarginMSELoss(MarginMSELoss): + def __init__(self, model: SparseEncoder, similarity_fct=util.pairwise_dot_score) -> None: + """ + Compute the MSE loss between the ``|sim(Query, Pos) - sim(Query, Neg)|`` and ``|gold_sim(Query, Pos) - gold_sim(Query, Neg)|``. + By default, sim() is the dot-product. The gold_sim is often the similarity score from a teacher model. + + In contrast to :class:`~sentence_transformers.sparse_encoder.losses.SparseMultipleNegativesRankingLoss`, the two passages do not + have to be strictly positive and negative, both can be relevant or not relevant for a given query. This can be + an advantage of SparseMarginMSELoss over SparseMultipleNegativesRankingLoss, but note that the SparseMarginMSELoss is much slower + to train. With SparseMultipleNegativesRankingLoss, with a batch size of 64, we compare one query against 128 passages. + With SparseMarginMSELoss, we compare a query only against two passages. It's also possible to use multiple negatives + with SparseMarginMSELoss, but the training would be even slower to train. + + Args: + model: SparseEncoder + similarity_fct: Which similarity function to use. + + References: + - For more details, please refer to https://arxiv.org/abs/2010.02666. + + Requirements: + 1. Need to be used in SpladeLoss or CSRLoss as a loss function. + 2. (query, passage_one, passage_two) triplets or (query, positive, negative_1, ..., negative_n) + 3. Usually used with a finetuned teacher M in a knowledge distillation setup + + Inputs: + +------------------------------------------------+------------------------------------------------------------------------+ + | Texts | Labels | + +================================================+========================================================================+ + | (query, passage_one, passage_two) triplets | M(query, passage_one) - M(query, passage_two) | + +------------------------------------------------+------------------------------------------------------------------------+ + | (query, passage_one, passage_two) triplets | [M(query, passage_one), M(query, passage_two)] | + +------------------------------------------------+------------------------------------------------------------------------+ + | (query, positive, negative_1, ..., negative_n) | [M(query, positive) - M(query, negative_i) for i in 1..n] | + +------------------------------------------------+------------------------------------------------------------------------+ + | (query, positive, negative_1, ..., negative_n) | [M(query, positive), M(query, negative_1), ..., M(query, negative_n)] | + +------------------------------------------------+------------------------------------------------------------------------+ + + Relations: + - :class:`SparseMSELoss` is similar to this loss, but without a margin through the negative pair. + + Example: + + With gold labels, e.g. if you have hard scores for sentences. Imagine you want a model to embed sentences + with similar "quality" close to each other. If the "text1" has quality 5 out of 5, "text2" has quality + 1 out of 5, and "text3" has quality 3 out of 5, then the similarity of a pair can be defined as the + difference of the quality scores. So, the similarity between "text1" and "text2" is 4, and the + similarity between "text1" and "text3" is 2. If we use this as our "Teacher Model", the label becomes + similraity("text1", "text2") - similarity("text1", "text3") = 4 - 2 = 2. + + Positive values denote that the first passage is more similar to the query than the second passage, + while negative values denote the opposite. + + :: + + from datasets import Dataset + + from sentence_transformers.sparse_encoder import SparseEncoder, SparseEncoderTrainer, losses + + model = SparseEncoder("naver/splade-cocondenser-ensembledistil") + train_dataset = Dataset.from_dict( + { + "text1": ["It's nice weather outside today.", "He drove to work."], + "text2": ["It's so sunny.", "He took the car to work."], + "text3": ["It's very sunny.", "She walked to the store."], + "label": [0.1, 0.8], + } + ) + + loss = losses.SpladeLoss( + model, losses.SparseMarginMSELoss(model), document_regularizer_weight=3e-5, query_regularizer_weight=5e-5 + ) + + trainer = SparseEncoderTrainer(model=model, train_dataset=train_dataset, loss=loss) + trainer.train() + + We can also use a teacher model to compute the similarity scores. In this case, we can use the teacher model + to compute the similarity scores and use them as the silver labels. This is often used in knowledge distillation. + + :: + + from datasets import Dataset + + from sentence_transformers.sparse_encoder import SparseEncoder, SparseEncoderTrainer, losses + + student_model = SparseEncoder("distilbert/distilbert-base-uncased") + teacher_model = SparseEncoder("naver/splade-cocondenser-ensembledistil") + train_dataset = Dataset.from_dict( + { + "query": ["It's nice weather outside today.", "He drove to work."], + "passage1": ["It's so sunny.", "He took the car to work."], + "passage2": ["It's very sunny.", "She walked to the store."], + } + ) + + + def compute_labels(batch): + emb_queries = teacher_model.encode(batch["query"]) + emb_passages1 = teacher_model.encode(batch["passage1"]) + emb_passages2 = teacher_model.encode(batch["passage2"]) + return { + "label": teacher_model.similarity_pairwise(emb_queries, emb_passages1) + - teacher_model.similarity_pairwise(emb_queries, emb_passages2) + } + + + train_dataset = train_dataset.map(compute_labels, batched=True) + loss = losses.SpladeLoss( + student_model, losses.SparseMarginMSELoss(student_model), document_regularizer_weight=3e-5, query_regularizer_weight=5e-5 + ) + + trainer = SparseEncoderTrainer(model=student_model, train_dataset=train_dataset, loss=loss) + trainer.train() + + We can also use multiple negatives during the knowledge distillation. + + :: + + import torch + from datasets import Dataset + + from sentence_transformers.sparse_encoder import SparseEncoder, SparseEncoderTrainer, losses + + student_model = SparseEncoder("distilbert/distilbert-base-uncased") + teacher_model = SparseEncoder("naver/splade-cocondenser-ensembledistil") + train_dataset = Dataset.from_dict( + { + "query": ["It's nice weather outside today.", "He drove to work."], + "passage1": ["It's so sunny.", "He took the car to work."], + "passage2": ["It's very cold.", "She walked to the store."], + "passage3": ["Its rainy", "She took the bus"], + } + ) + + + def compute_labels(batch): + emb_queries = teacher_model.encode(batch["query"]) + emb_passages1 = teacher_model.encode(batch["passage1"]) + emb_passages2 = teacher_model.encode(batch["passage2"]) + emb_passages3 = teacher_model.encode(batch["passage3"]) + return { + "label": torch.stack( + [ + teacher_model.similarity_pairwise(emb_queries, emb_passages1) + - teacher_model.similarity_pairwise(emb_queries, emb_passages2), + teacher_model.similarity_pairwise(emb_queries, emb_passages1) + - teacher_model.similarity_pairwise(emb_queries, emb_passages3), + ], + dim=1, + ) + } + + + train_dataset = train_dataset.map(compute_labels, batched=True) + loss = losses.SpladeLoss( + student_model, loss=losses.SparseMarginMSELoss(student_model), document_regularizer_weight=3e-5, query_regularizer_weight=5e-5 + ) + + trainer = SparseEncoderTrainer(model=student_model, train_dataset=train_dataset, loss=loss) + trainer.train() + """ + return super().__init__(model, similarity_fct=similarity_fct) + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + raise AttributeError("SparseMarginMSELoss should not be used alone. Use it with SpladeLoss or CSRLoss.") diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseMultipleNegativesRankingLoss.py b/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseMultipleNegativesRankingLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..f69b353f50cacb5b80480fed87f221179dd267a0 --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseMultipleNegativesRankingLoss.py @@ -0,0 +1,90 @@ +from __future__ import annotations + +from collections.abc import Iterable + +from torch import Tensor + +from sentence_transformers import util +from sentence_transformers.losses.MultipleNegativesRankingLoss import MultipleNegativesRankingLoss +from sentence_transformers.sparse_encoder.SparseEncoder import SparseEncoder + + +class SparseMultipleNegativesRankingLoss(MultipleNegativesRankingLoss): + def __init__(self, model: SparseEncoder, scale: float = 1.0, similarity_fct=util.dot_score) -> None: + """ + Given a list of (anchor, positive) pairs or (anchor, positive, negative) triplets, this loss optimizes the following: + + 1. Given an anchor (e.g. a question), assign the highest similarity to the corresponding positive (i.e. answer) + out of every single positive and negative (e.g. all answers) in the batch. + + If you provide the optional negatives, they will all be used as extra options from which the model must pick the + correct positive. Within reason, the harder this "picking" is, the stronger the model will become. Because of + this, a higher batch size results in more in-batch negatives, which then increases performance (to a point). + + This loss function works great to train embeddings for retrieval setups where you have positive pairs + (e.g. (query, answer)) as it will sample in each batch ``n-1`` negative docs randomly. + + This loss is also known as InfoNCE loss, SimCSE loss, Cross-Entropy Loss with in-batch negatives, or simply + in-batch negatives loss. + + Args: + model: SparseEncoder model + scale: Output of similarity function is multiplied by scale + value + similarity_fct: similarity function between sentence + embeddings. By default, dot product. Can also be set to cosine + similarity (and then set scale to 20) + + Requirements: + 1. Need to be used in SpladeLoss or CSRLoss as a loss function. + 2. (anchor, positive) pairs or (anchor, positive, negative) triplets + + Inputs: + +-------------------------------------------------+--------+ + | Texts | Labels | + +=================================================+========+ + | (anchor, positive) pairs | none | + +-------------------------------------------------+--------+ + | (anchor, positive, negative) triplets | none | + +-------------------------------------------------+--------+ + | (anchor, positive, negative_1, ..., negative_n) | none | + +-------------------------------------------------+--------+ + + Recommendations: + - Use ``BatchSamplers.NO_DUPLICATES`` (:class:`docs `) to + ensure that no in-batch negatives are duplicates of the anchor or positive samples. + + Relations: + - :class:`SparseCachedMultipleNegativesRankingLoss` is equivalent to this loss, but it uses caching that allows for + much higher batch sizes (and thus better performance) without extra memory usage. However, it is slightly + slower. + - :class:`SparseGISTEmbedLoss` is equivalent to this loss, but uses a guide model to guide the in-batch negative + sample selection. `SparseGISTEmbedLoss` yields a stronger training signal at the cost of some training overhead. + + Example: + :: + + from datasets import Dataset + + from sentence_transformers.sparse_encoder import SparseEncoder, SparseEncoderTrainer, losses + + model = SparseEncoder("distilbert/distilbert-base-uncased") + train_dataset = Dataset.from_dict( + { + "anchor": ["It's nice weather outside today.", "He drove to work."], + "positive": ["It's so sunny.", "He took the car to the office."], + } + ) + loss = losses.SpladeLoss( + model=model, loss=losses.SparseMultipleNegativesRankingLoss(model), document_regularizer_weight=3e-5, query_regularizer_weight=5e-5 + ) + + trainer = SparseEncoderTrainer(model=model, train_dataset=train_dataset, loss=loss) + trainer.train() + """ + return super().__init__(model, scale=scale, similarity_fct=similarity_fct) + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + raise AttributeError( + "SparseMultipleNegativesRankingLoss should not be used alone. Use it with SpladeLoss or CSRLoss." + ) diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseTripletLoss.py b/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseTripletLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..05b3deb9c93a37eb9aa826b702957b1ce372c590 --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/losses/SparseTripletLoss.py @@ -0,0 +1,71 @@ +from __future__ import annotations + +from collections.abc import Iterable + +from torch import Tensor + +from sentence_transformers.losses.TripletLoss import TripletDistanceMetric, TripletLoss +from sentence_transformers.sparse_encoder.SparseEncoder import SparseEncoder + + +class SparseTripletLoss(TripletLoss): + def __init__( + self, model: SparseEncoder, distance_metric=TripletDistanceMetric.EUCLIDEAN, triplet_margin: float = 5 + ) -> None: + """ + This class implements triplet loss. Given a triplet of (anchor, positive, negative), + the loss minimizes the distance between anchor and positive while it maximizes the distance + between anchor and negative. It compute the following loss function: + + ``loss = max(||anchor - positive|| - ||anchor - negative|| + margin, 0)``. + + Margin is an important hyperparameter and needs to be tuned respectively. + + Args: + model: SparseEncoder + distance_metric: Function to compute distance between two + embeddings. The class TripletDistanceMetric contains + common distance metrices that can be used. + triplet_margin: The negative should be at least this much + further away from the anchor than the positive. + + References: + - For further details, see: https://en.wikipedia.org/wiki/Triplet_loss + + Requirements: + 1. Need to be used in SpladeLoss or CSRLoss as a loss function. + 2. (anchor, positive, negative) triplets + + Inputs: + +---------------------------------------+--------+ + | Texts | Labels | + +=======================================+========+ + | (anchor, positive, negative) triplets | none | + +---------------------------------------+--------+ + + Example: + :: + + from datasets import Dataset + + from sentence_transformers.sparse_encoder import SparseEncoder, SparseEncoderTrainer, losses + + model = SparseEncoder("distilbert/distilbert-base-uncased") + train_dataset = Dataset.from_dict( + { + "anchor": ["It's nice weather outside today.", "He drove to work."], + "positive": ["It's so sunny.", "He took the car to the office."], + "negative": ["It's quite rainy, sadly.", "She walked to the store."], + } + ) + loss = losses.SpladeLoss( + model=model, loss=losses.SparseTripletLoss(model), document_regularizer_weight=3e-5, query_regularizer_weight=5e-5 + ) + + trainer = SparseEncoderTrainer(model=model, train_dataset=train_dataset, loss=loss) + trainer.train() + """ + super().__init__(model, distance_metric=distance_metric, triplet_margin=triplet_margin) + + def forward(self, sentence_features: Iterable[dict[str, Tensor]], labels: Tensor) -> Tensor: + raise AttributeError("SparseTripletLoss should not be used alone. Use it with SpladeLoss or CSRLoss.") diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/SpladeLoss.py b/sentence-transformers/sentence_transformers/sparse_encoder/losses/SpladeLoss.py new file mode 100644 index 0000000000000000000000000000000000000000..acd5cc961a11dc0604e8422336d6c614abc87929 --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/losses/SpladeLoss.py @@ -0,0 +1,203 @@ +from __future__ import annotations + +import logging +from collections.abc import Iterable + +import torch +import torch.nn as nn + +from sentence_transformers.sparse_encoder.losses import FlopsLoss +from sentence_transformers.sparse_encoder.SparseEncoder import SparseEncoder + +logger = logging.getLogger(__name__) + + +class SpladeLoss(nn.Module): + def __init__( + self, + model: SparseEncoder, + loss: nn.Module, + document_regularizer_weight: float, + query_regularizer_weight: float | None = None, + document_regularizer: nn.Module | None = None, + query_regularizer: nn.Module | None = None, + document_regularizer_threshold: int | None = None, + query_regularizer_threshold: int | None = None, + use_document_regularizer_only: bool = False, + ): + """ + SpladeLoss implements the loss function for the SPLADE (Sparse Lexical and Expansion) model, + which combines a main loss function with regularization terms to control efficiency. + + This loss function balances effectiveness (via the main loss) with efficiency by regularizing + both the query and document representations to be sparse, reducing computational requirements + at inference time. + + Args: + model: SparseEncoder model + loss: The principal loss function to use can be any of the SparseEncoder losses except CSR related losses and flops loss. + document_regularizer_weight: Weight for the corpus regularization term. This term encourages sparsity in the document embeddings. + Will be applied to positive documents and all negatives one if some are provided. In some papers, this parameter is + referred to as "lambda_d" (document) or "lambda_c" (corpus). + query_regularizer_weight: Weight for the query regularization term. This term encourages sparsity in the query embeddings. + If None, no query regularization will be applied, it's not a problem if you are in an inference-free setup or + if you are having use_document_regularizer_only=True. Else you should have a query_regularizer_weight > 0. + In some papers, this parameter is referred to as "lambda_q" (query). + document_regularizer: Optional regularizer to use specifically for corpus regularization instead of the default FlopsLoss. + This allows for different regularization strategies for documents vs queries. + query_regularizer: Optional regularizer to use specifically for query regularization instead of the default FlopsLoss. + This allows for different regularization strategies for queries vs documents. + document_regularizer_threshold: Optional threshold for the number of non-zero (active) elements in the corpus embeddings to be considered in the FlopsLoss. + If specified, only corpus embeddings with more than this number of non-zero (active) elements will be considered. + Only used when document_regularizer is None (for the default FlopsLoss). + query_regularizer_threshold: Optional threshold for the number of non-zero (active) elements in the query embeddings to be considered in the FlopsLoss. + If specified, only query embeddings with more than this number of non-zero (active) elements will be considered. + Only used when query_regularizer is None (for the default FlopsLoss). + use_document_regularizer_only: If True, all input embeddings are treated as documents and regularized together with document_regularizer_weight. + Especially useful when training with symmetric texts (e.g. pairs of documents) or more. + + References: + - For more details, see the paper "From Distillation to Hard Negative Sampling: Making Sparse Neural IR Models More Effective" + https://arxiv.org/abs/2205.04733 + + Requirements: + 1. Input requirements depend on the chosen loss + 2. Usually used with a teacher model in a knowledge distillation setup and an associated loss + + Example: + :: + + from datasets import Dataset + + from sentence_transformers.sparse_encoder import SparseEncoder, SparseEncoderTrainer, losses + + student_model = SparseEncoder("distilbert/distilbert-base-uncased") + teacher_model = SparseEncoder("naver/splade-cocondenser-ensembledistil") + train_dataset = Dataset.from_dict( + { + "query": ["It's nice weather outside today.", "He drove to work."], + "passage1": ["It's so sunny.", "He took the car to work."], + "passage2": ["It's very sunny.", "She walked to the store."], + } + ) + + def compute_labels(batch): + emb_queries = teacher_model.encode(batch["query"]) + emb_passages1 = teacher_model.encode(batch["passage1"]) + emb_passages2 = teacher_model.encode(batch["passage2"]) + return { + "label": teacher_model.similarity_pairwise(emb_queries, emb_passages1) + - teacher_model.similarity_pairwise(emb_queries, emb_passages2) + } + + train_dataset = train_dataset.map(compute_labels, batched=True) + loss = losses.SpladeLoss( + student_model, + loss=losses.SparseMarginMSELoss(student_model), + document_regularizer_weight=3e-5, + query_regularizer_weight=5e-5, + ) + + trainer = SparseEncoderTrainer(model=student_model, train_dataset=train_dataset, loss=loss) + trainer.train() + """ + super().__init__() + self.model = model + self.loss = loss + self.document_regularizer_weight = document_regularizer_weight + self.query_regularizer_weight = query_regularizer_weight + self.use_document_regularizer_only = use_document_regularizer_only + + # Set up regularizers with defaults to FlopsLoss using specific thresholds + self.document_regularizer = ( + document_regularizer + if document_regularizer is not None + else FlopsLoss(model, threshold=document_regularizer_threshold) + ) + if query_regularizer is not None: + self.query_regularizer = query_regularizer + elif not use_document_regularizer_only: + self.query_regularizer = FlopsLoss(model, threshold=query_regularizer_threshold) + + if self.query_regularizer_weight is None and not use_document_regularizer_only: + logging.warning( + "query_regularizer_weight is None. This means that the query regularization will not be applied. If you are in an inference free set up it's fine else you should have a query_regularizer_weight > 0." + ) + if self.use_document_regularizer_only and self.query_regularizer_weight is not None: + logging.warning( + "query_regularizer_weight should be None when use_document_regularizer_only is True. use_document_regularizer_only mean we consider all the input to be of the same type and so under the same regularization. query_regularizer_weight will be ignored." + ) + self.query_regularizer_weight = None + if not hasattr(loss, "compute_loss_from_embeddings"): + raise ValueError( + "The provided loss does not have a 'compute_loss_from_embeddings' method, which is required for SpladeLoss. " + "This method must have the signature `compute_loss_from_embeddings(embeddings: List[Tensor], labels: Tensor | None = None)`." + ) + + def forward( + self, sentence_features: Iterable[dict[str, torch.Tensor]], labels: torch.Tensor | None = None + ) -> dict[str, torch.Tensor]: + # Compute embeddings using the model + embeddings = [self.model(sentence_feature)["sentence_embedding"] for sentence_feature in sentence_features] + + losses = {} + base_loss = self.loss.compute_loss_from_embeddings(embeddings, labels) + if isinstance(base_loss, dict): + losses.update(base_loss) + else: + losses["base_loss"] = base_loss + + if self.use_document_regularizer_only: + # If use_document_regularizer_only is True, we consider all the input to be of the same type and so under the same regularization + corpus_loss = self.document_regularizer.compute_loss_from_embeddings(torch.cat(embeddings)) + else: + corpus_loss = self.document_regularizer.compute_loss_from_embeddings(torch.cat(embeddings[1:])) + losses["document_regularizer_loss"] = corpus_loss * self.document_regularizer_weight + + # Add query regularization if enabled + if self.query_regularizer_weight is not None: + query_loss = self.query_regularizer.compute_loss_from_embeddings(embeddings[0]) + losses["query_regularizer_loss"] = query_loss * self.query_regularizer_weight + + return losses + + def get_config_dict(self): + """ + Get the configuration dictionary. + + Returns: + Dictionary containing the configuration parameters + """ + config_dict = { + "loss": self.loss, + "document_regularizer_weight": self.document_regularizer_weight, + } + if self.query_regularizer_weight is not None: + config_dict["query_regularizer_weight"] = self.query_regularizer_weight + # Include regularizer names (if not flops) and threshold information (if not None) + + if not isinstance(self.document_regularizer, FlopsLoss): + config_dict["document_regularizer"] = self.document_regularizer.__class__.__name__ + if hasattr(self.document_regularizer, "threshold") and self.document_regularizer.threshold is not None: + config_dict["document_regularizer_threshold"] = self.document_regularizer.threshold + + if hasattr(self, "query_regularizer") and self.query_regularizer is not None: + if not isinstance(self.query_regularizer, FlopsLoss): + config_dict["query_regularizer"] = self.query_regularizer.__class__.__name__ + if hasattr(self.query_regularizer, "threshold") and self.query_regularizer.threshold is not None: + config_dict["query_regularizer_threshold"] = self.query_regularizer.threshold + return config_dict + + @property + def citation(self) -> str: + return """ +@misc{formal2022distillationhardnegativesampling, + title={From Distillation to Hard Negative Sampling: Making Sparse Neural IR Models More Effective}, + author={Thibault Formal and Carlos Lassance and Benjamin Piwowarski and Stéphane Clinchant}, + year={2022}, + eprint={2205.04733}, + archivePrefix={arXiv}, + primaryClass={cs.IR}, + url={https://arxiv.org/abs/2205.04733}, +} +""" diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/__init__.py b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d1ad8666c55854cc58bbad986cb7511ad83b0644 --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__init__.py @@ -0,0 +1,28 @@ +from __future__ import annotations + +from .CSRLoss import CSRLoss, CSRReconstructionLoss +from .FlopsLoss import FlopsLoss +from .SparseAnglELoss import SparseAnglELoss +from .SparseCoSENTLoss import SparseCoSENTLoss +from .SparseCosineSimilarityLoss import SparseCosineSimilarityLoss +from .SparseDistillKLDivLoss import SparseDistillKLDivLoss +from .SparseMarginMSELoss import SparseMarginMSELoss +from .SparseMSELoss import SparseMSELoss +from .SparseMultipleNegativesRankingLoss import SparseMultipleNegativesRankingLoss +from .SparseTripletLoss import SparseTripletLoss +from .SpladeLoss import SpladeLoss + +__all__ = [ + "CSRLoss", + "CSRReconstructionLoss", + "SparseMultipleNegativesRankingLoss", + "SparseCoSENTLoss", + "SparseTripletLoss", + "SparseMarginMSELoss", + "SparseCosineSimilarityLoss", + "SparseMSELoss", + "SparseAnglELoss", + "SparseDistillKLDivLoss", + "FlopsLoss", + "SpladeLoss", +] diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/CSRLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/CSRLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..25548e0b6a1d84a6cc266b555968073c900a8797 Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/CSRLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/FlopsLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/FlopsLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..560839ee01984b556e3b470da0902445b604d7ba Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/FlopsLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseAnglELoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseAnglELoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..85fe773cd7dfcb4bd341880d96a1c243c43cd9b6 Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseAnglELoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseCoSENTLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseCoSENTLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3a8a0969179b204b4951dea2d2570a702f0b2498 Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseCoSENTLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseCosineSimilarityLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseCosineSimilarityLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fb8a0c679da6d2dbdefc894d808c3a65645636bd Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseCosineSimilarityLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseDistillKLDivLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseDistillKLDivLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..206fa46c35cace7df10c378029980d713bc9f21a Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseDistillKLDivLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseMSELoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseMSELoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8e26bff6d3ece221587818b9ffe9424869fede6f Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseMSELoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseMarginMSELoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseMarginMSELoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f8ee68d0ad95ebe0bd264ca7a54e960679a45791 Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseMarginMSELoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseMultipleNegativesRankingLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseMultipleNegativesRankingLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..510e9c72848ca56b99b2db7737353b9f1abd085c Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseMultipleNegativesRankingLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseTripletLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseTripletLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8e8fac6942fdee5fcc5d83ecb6dae8ee0e02b2b1 Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SparseTripletLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SpladeLoss.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SpladeLoss.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..52336f49ceafb8d2eea38c95a6bf933b3d40e679 Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/SpladeLoss.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/__init__.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..90f561bd57099272925b7d13de571af22d982759 Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/losses/__pycache__/__init__.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/models/MLMTransformer.py b/sentence-transformers/sentence_transformers/sparse_encoder/models/MLMTransformer.py new file mode 100644 index 0000000000000000000000000000000000000000..bf0a476f789129c04bc8e506aae8e19f1770f0a7 --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/models/MLMTransformer.py @@ -0,0 +1,375 @@ +from __future__ import annotations + +import logging +from typing import TYPE_CHECKING, Any + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +import torch +from transformers import AutoConfig, AutoModelForMaskedLM, AutoTokenizer, PretrainedConfig +from transformers.utils.import_utils import is_peft_available +from transformers.utils.peft_utils import find_adapter_config_file + +from sentence_transformers.models.InputModule import InputModule + +if TYPE_CHECKING and is_peft_available(): + from peft import PeftConfig + +logger = logging.getLogger(__name__) + + +class MLMTransformer(InputModule): + """ + MLMTransformer adapts a Masked Language Model (MLM) for sparse encoding applications. + + This class extends the Transformer class to work specifically with models that have a + MLM head (like BERT, RoBERTa, etc.) and is designed to be used with SpladePooling + for creating SPLADE sparse representations. + + MLMTransformer accesses the MLM prediction head to get vocabulary logits for each token, + which are later used by SpladePooling to create sparse lexical representations. + + Args: + model_name_or_path: Hugging Face models name + (https://huggingface.co/models) + max_seq_length: Truncate any inputs longer than max_seq_length + model_args: Keyword arguments passed to the Hugging Face + MLMTransformers model + tokenizer_args: Keyword arguments passed to the Hugging Face + MLMTransformers tokenizer + config_args: Keyword arguments passed to the Hugging Face + MLMTransformers config + cache_dir: Cache dir for Hugging Face MLMTransformers to store/load + models + do_lower_case: If true, lowercases the input (independent if the + model is cased or not) + tokenizer_name_or_path: Name or path of the tokenizer. When + None, then model_name_or_path is used + backend: Backend used for model inference. Can be only `torch` for now for this class. + """ + + config_file_name: str = "sentence_bert_config.json" + config_keys: list[str] = ["max_seq_length", "do_lower_case"] + save_in_root: bool = True + + def __init__( + self, + model_name_or_path: str, + max_seq_length: int | None = None, + model_args: dict[str, Any] | None = None, + tokenizer_args: dict[str, Any] | None = None, + config_args: dict[str, Any] | None = None, + cache_dir: str | None = None, + do_lower_case: bool = False, + tokenizer_name_or_path: str | None = None, + backend: str = "torch", + ) -> None: + super().__init__() + self.do_lower_case = do_lower_case + self.backend = backend + if model_args is None: + model_args = {} + if tokenizer_args is None: + tokenizer_args = {} + if config_args is None: + config_args = {} + + self.config, is_peft_model = self._load_config(model_name_or_path, cache_dir, backend, config_args) + self._load_model(model_name_or_path, self.config, cache_dir, backend, is_peft_model, **model_args) + + if max_seq_length is not None and "model_max_length" not in tokenizer_args: + tokenizer_args["model_max_length"] = max_seq_length + + self.tokenizer = AutoTokenizer.from_pretrained( + (tokenizer_name_or_path if tokenizer_name_or_path is not None else model_name_or_path), + cache_dir=cache_dir, + **tokenizer_args, + ) + + # Set max_seq_length + self.max_seq_length = max_seq_length + if max_seq_length is None: + if hasattr(self.config, "max_position_embeddings") and hasattr(self.tokenizer, "model_max_length"): + self.max_seq_length = min(self.config.max_position_embeddings, self.tokenizer.model_max_length) + + def _load_config( + self, model_name_or_path: str, cache_dir: str | None, backend: str, config_args: dict[str, Any] + ) -> tuple[PeftConfig | PretrainedConfig, bool]: + """Loads the transformers or PEFT configuration + + Args: + model_name_or_path (str): The model name on Hugging Face (e.g. 'naver/splade-cocondenser-ensembledistil') + or the path to a local model directory. + cache_dir (str | None): The cache directory to store the model configuration. + backend (str): Backend used for model inference. Can be only `torch` for now for this class. + config_args (dict[str, Any]): Keyword arguments passed to the Hugging Face Transformers config. + + Returns: + tuple[PretrainedConfig, bool]: The model configuration and a boolean indicating whether the model is a PEFT model. + """ + if ( + find_adapter_config_file( + model_name_or_path, + cache_dir=cache_dir, + token=config_args.get("token"), + revision=config_args.get("revision"), + local_files_only=config_args.get("local_files_only", False), + ) + is not None + ): + if not is_peft_available(): + raise Exception( + "Loading a PEFT model requires installing the `peft` package. You can install it via `pip install peft`." + ) + if backend != "torch": + # TODO: Consider following these steps automatically so we can load PEFT models with other backends + raise ValueError( + "PEFT models can currently only be loaded with the `torch` backend. " + 'To use other backends, load the model with `backend="torch"`, call `model.transformers_model.merge_and_unload()`, ' + "save that model with `model.save_pretrained()` and then load the model with the desired backend." + ) + from peft import PeftConfig + + return PeftConfig.from_pretrained(model_name_or_path, **config_args, cache_dir=cache_dir), True + + return AutoConfig.from_pretrained(model_name_or_path, **config_args, cache_dir=cache_dir), False + + def _load_model( + self, + model_name_or_path: str, + config: PeftConfig | PretrainedConfig, + cache_dir: str, + backend: str, + is_peft_model: bool, + **model_args, + ) -> None: + """Loads the transformers or PEFT model into the `auto_model` attribute + + Args: + model_name_or_path (str): The model name on Hugging Face (e.g. 'naver/splade-cocondenser-ensembledistil') + or the path to a local model directory. + config ("PeftConfig" | PretrainedConfig): The model configuration. + cache_dir (str | None): The cache directory to store the model configuration. + backend (str): Backend used for model inference. Can be only `torch` for now for this class. + is_peft_model (bool): Whether the model is a PEFT model. + model_args (dict[str, Any]): Keyword arguments passed to the Hugging Face Transformers model. + """ + if backend == "torch": + # When loading a PEFT model, we need to load the base model first, + # but some model_args are only for the adapter + if is_peft_model: + for adapter_only_kwarg in ["revision"]: + model_args.pop(adapter_only_kwarg, None) + + self.auto_model = AutoModelForMaskedLM.from_pretrained( + model_name_or_path, config=config, cache_dir=cache_dir, **model_args + ) + else: + raise ValueError( + f"Backend '{backend}' is not yet supported. MLMTransformer currently only works with the 'torch' backend." + ) + + def forward(self, features: dict[str, torch.Tensor]) -> dict[str, torch.Tensor]: + """Returns the MLM head logits for the input features as token embeddings.""" + trans_features = { + key: value + for key, value in features.items() + if key in ["input_ids", "attention_mask", "token_type_ids", "inputs_embeds"] + } + try: + features["token_embeddings"] = self.auto_model(**trans_features).logits + except AttributeError: + features["token_embeddings"] = self.auto_model(**trans_features)[0] + + return features + + def tokenize( + self, texts: list[str] | list[dict] | list[tuple[str, str]], padding: str | bool = True + ) -> dict[str, torch.Tensor]: + """Tokenizes a text and maps tokens to token-ids""" + output = {} + if isinstance(texts[0], str): + to_tokenize = [texts] + elif isinstance(texts[0], dict): + to_tokenize = [] + output["text_keys"] = [] + for lookup in texts: + text_key, text = next(iter(lookup.items())) + to_tokenize.append(text) + output["text_keys"].append(text_key) + to_tokenize = [to_tokenize] + else: + batch1, batch2 = [], [] + for text_tuple in texts: + batch1.append(text_tuple[0]) + batch2.append(text_tuple[1]) + to_tokenize = [batch1, batch2] + + # strip + to_tokenize = [[str(s).strip() for s in col] for col in to_tokenize] + + # Lowercase + if self.do_lower_case: + to_tokenize = [[s.lower() for s in col] for col in to_tokenize] + + output.update( + self.tokenizer( + *to_tokenize, + padding=padding, + truncation="longest_first", + return_tensors="pt", + max_length=self.max_seq_length, + ) + ) + return output + + def get_sentence_embedding_dimension(self) -> int: + return self.auto_model.config.vocab_size + + def __repr__(self) -> str: + return f"MLMTransformer({dict(self.get_config_dict(), architecture=self.auto_model.__class__.__name__)})" + + def save(self, output_path: str, safe_serialization: bool = True, **kwargs) -> None: + self.auto_model.save_pretrained(output_path, safe_serialization=safe_serialization) + self.save_tokenizer(output_path) + self.save_config(output_path) + + @classmethod + def load( + cls, + model_name_or_path: str, + # Loading arguments + subfolder: str = "", + token: bool | str | None = None, + cache_folder: str | None = None, + revision: str | None = None, + local_files_only: bool = False, + # Module-specific arguments + trust_remote_code: bool = False, + model_kwargs: dict[str, Any] | None = None, + tokenizer_kwargs: dict[str, Any] | None = None, + config_kwargs: dict[str, Any] | None = None, + backend: str = "torch", + **kwargs, + ) -> Self: + init_kwargs = cls._load_init_kwargs( + model_name_or_path=model_name_or_path, + subfolder=subfolder, + token=token, + cache_folder=cache_folder, + revision=revision, + local_files_only=local_files_only, + trust_remote_code=trust_remote_code, + model_kwargs=model_kwargs, + tokenizer_kwargs=tokenizer_kwargs, + config_kwargs=config_kwargs, + backend=backend, + ) + return cls(model_name_or_path=model_name_or_path, **init_kwargs) + + @classmethod + def _load_init_kwargs( + cls, + model_name_or_path: str, + # Loading arguments + subfolder: str = "", + token: bool | str | None = None, + cache_folder: str | None = None, + revision: str | None = None, + local_files_only: bool = False, + # Module-specific arguments + trust_remote_code: bool = False, + model_kwargs: dict[str, Any] | None = None, + tokenizer_kwargs: dict[str, Any] | None = None, + config_kwargs: dict[str, Any] | None = None, + backend: str = "torch", + **kwargs, + ) -> dict[str, Any]: + config = cls.load_config( + model_name_or_path=model_name_or_path, + subfolder=subfolder, + token=token, + cache_folder=cache_folder, + revision=revision, + local_files_only=local_files_only, + ) + + hub_kwargs = { + "subfolder": subfolder, + "token": token, + "revision": revision, + "local_files_only": local_files_only, + "trust_remote_code": trust_remote_code, + } + + # 3rd priority: config file + if "model_args" not in config: + config["model_args"] = {} + if "tokenizer_args" not in config: + config["tokenizer_args"] = {} + if "config_args" not in config: + config["config_args"] = {} + + # 2nd priority: hub_kwargs + config["model_args"].update(hub_kwargs) + config["tokenizer_args"].update(hub_kwargs) + config["config_args"].update(hub_kwargs) + + # 1st priority: kwargs passed to SentenceTransformer + if model_kwargs: + config["model_args"].update(model_kwargs) + if tokenizer_kwargs: + config["tokenizer_args"].update(tokenizer_kwargs) + if config_kwargs: + config["config_args"].update(config_kwargs) + + return {**config, "cache_dir": cache_folder, "backend": backend} + + @classmethod + def load_config( + cls, + model_name_or_path: str, + subfolder: str = "", + config_filename: str | None = None, + token: bool | str | None = None, + cache_folder: str | None = None, + revision: str | None = None, + local_files_only: bool = False, + ) -> dict[str, Any]: + config_filenames = ( + [config_filename] + if config_filename + else [ + "sentence_bert_config.json", + "sentence_roberta_config.json", + "sentence_distilbert_config.json", + "sentence_camembert_config.json", + "sentence_albert_config.json", + "sentence_xlm-roberta_config.json", + "sentence_xlnet_config.json", + ] + ) + for config_filename in config_filenames: + config = super().load_config( + model_name_or_path=model_name_or_path, + subfolder=subfolder, + config_filename=config_filename, + token=token, + cache_folder=cache_folder, + revision=revision, + local_files_only=local_files_only, + ) + if config: + break + + # Don't allow configs to set trust_remote_code + if "model_args" in config and "trust_remote_code" in config["model_args"]: + config["model_args"].pop("trust_remote_code") + if "tokenizer_args" in config and "trust_remote_code" in config["tokenizer_args"]: + config["tokenizer_args"].pop("trust_remote_code") + if "config_args" in config and "trust_remote_code" in config["config_args"]: + config["config_args"].pop("trust_remote_code") + return config diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/models/SparseAutoEncoder.py b/sentence-transformers/sentence_transformers/sparse_encoder/models/SparseAutoEncoder.py new file mode 100644 index 0000000000000000000000000000000000000000..1830676903a402cee1356beea8a5587a6eac73a1 --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/models/SparseAutoEncoder.py @@ -0,0 +1,236 @@ +from __future__ import annotations + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +import torch +import torch.nn as nn +import torch.nn.functional as F + +from sentence_transformers.models.Module import Module + + +class TiedTranspose(nn.Module): + def __init__(self, linear: nn.Linear): + super().__init__() + self.linear = linear + + def forward(self, x: torch.Tensor) -> torch.Tensor: + assert self.linear.bias is None + return F.linear(x, self.linear.weight.t(), None) + + @property + def weight(self) -> torch.Tensor: + return self.linear.weight.t() + + @property + def bias(self) -> torch.Tensor: + return self.linear.bias + + +class SparseAutoEncoder(Module): + """ + This module implements the Sparse AutoEncoder architecture based on the paper: + Beyond Matryoshka: Revisiting Sparse Coding for Adaptive Representation, https://arxiv.org/abs/2503.01776 + + This module transforms dense embeddings into sparse representations by: + + 1. Applying a multi-layer feed-forward network + 2. Applying top-k sparsification to keep only the largest values + 3. Supporting auxiliary losses for training stability (via k_aux parameter) + + Args: + input_dim: Dimension of the input embeddings. + hidden_dim: Dimension of the hidden layers. Defaults to 512. + k: Number of top values to keep in the final sparse representation. Defaults to 8. + k_aux: Number of top values to keep for auxiliary loss calculation. Defaults to 512. + normalize: Whether to apply layer normalization to the input embeddings. Defaults to False. + dead_threshold: Threshold for dead neurons. Neurons with non-zero activations below this threshold are considered dead. Defaults to 30. + """ + + config_keys = ["input_dim", "hidden_dim", "k", "k_aux", "normalize", "dead_threshold"] + + forward_kwargs = {"max_active_dims"} + + def __init__( + self, + input_dim: int, + hidden_dim: int = 512, + k: int = 8, + k_aux: int = 512, + normalize: bool = False, + dead_threshold: int = 30, + ) -> None: + super().__init__() + self.input_dim = input_dim + self.hidden_dim = hidden_dim + self.dead_threshold = dead_threshold + self.pre_bias = nn.Parameter(torch.zeros(input_dim)) + self.encoder: nn.Module = nn.Linear(input_dim, hidden_dim, bias=False) + self.latent_bias = nn.Parameter(torch.zeros(hidden_dim)) + self.decoder: TiedTranspose = TiedTranspose(self.encoder) + self.k = k + self.k_aux = k_aux + self.normalize = normalize + + self.stats_last_nonzero: torch.Tensor + self.register_buffer("stats_last_nonzero", torch.zeros(hidden_dim, dtype=torch.long)) + + def auxk_mask_fn(x): + dead_mask = self.stats_last_nonzero > dead_threshold + x.data *= dead_mask # inplace to save memory + return x + + self.auxk_mask_fn = auxk_mask_fn + + def encode_pre_act(self, x: torch.Tensor) -> torch.Tensor: + """ + :param x: input data (shape: [batch, input_dim]) + :param latent_slice: slice of latents to compute + Example: latent_slice = slice(0, 10) to compute only the first 10 latents. + :return: autoencoder latents before activation (shape: [batch, hidden_dim]) + """ + x = x - self.pre_bias + latents_pre_act = F.linear(x, self.encoder.weight, self.latent_bias) + return latents_pre_act + + def LN(self, x: torch.Tensor, eps: float = 1e-5): + mu = x.mean(dim=-1, keepdim=True) + x = x - mu + std = x.std(dim=-1, keepdim=True) + x = x / (std + eps) + return x, mu, std + + def preprocess(self, x: torch.Tensor): + if not self.normalize: + return x, dict() + x, mu, std = self.LN(x) + return x, dict(mu=mu, std=std) + + def top_k(self, x: torch.Tensor, k: int | None = None, compute_aux: bool = True) -> torch.Tensor: + """ + :param x: input data (shape: [batch, input_dim]) + :return: autoencoder latents (shape: [batch, hidden_dim]) + """ + if k is None: + k = self.k + topk = torch.topk(x, k=k, dim=-1) + z_topk = torch.zeros_like(x) + z_topk.scatter_(-1, topk.indices, topk.values) + latents_k = F.relu(z_topk) + ## set num nonzero stat ## + tmp = torch.zeros_like(self.stats_last_nonzero) + tmp.scatter_add_( + 0, + topk.indices.reshape(-1), + (topk.values > 1e-5).to(tmp.dtype).reshape(-1), + ) + self.stats_last_nonzero *= 1 - tmp.clamp(max=1) + self.stats_last_nonzero += 1 + ## end stats ## + + latents_auxk = None + if self.k_aux and compute_aux: + aux_topk = torch.topk( + input=self.auxk_mask_fn(x), + k=self.k_aux, + ) + z_auxk = torch.zeros_like(x) + z_auxk.scatter_(-1, aux_topk.indices, aux_topk.values) + latents_auxk = F.relu(z_auxk) + return latents_k, latents_auxk + + def decode(self, latents: torch.Tensor, info=None) -> torch.Tensor: + """ + :param latents: autoencoder latents (shape: [batch, hidden_dim]) + :return: reconstructed data (shape: [batch, n_inputs]) + """ + + ret = self.decoder(latents) + self.pre_bias + + if self.normalize: + assert info is not None + ret = ret * info["std"] + info["mu"] + return ret + + def forward( + self, features: dict[str, torch.Tensor], max_active_dims: int | None = None + ) -> dict[str, torch.Tensor]: + k = max_active_dims if max_active_dims is not None else self.k + x = features["sentence_embedding"] + + # If the model is in inference mode, we don't need to e.g. compute the 4k, auxk, or apply the decoder + if torch.is_inference_mode_enabled(): + x, info = self.preprocess(x) + latents_pre_act = self.encode_pre_act(x) + latents_k, _ = self.top_k(latents_pre_act, k, compute_aux=False) + features["sentence_embedding"] = latents_k + return features + + x, info = self.preprocess(x) + latents_pre_act = self.encode_pre_act(x) + + latents_k, latents_auxk = self.top_k(latents_pre_act, k) + latents_4k, _ = self.top_k(latents_pre_act, 4 * k) + + recons_k = self.decode(latents_k, info) + recons_4k = self.decode(latents_4k, info) + + recons_aux = self.decode(latents_auxk, info) + + # Update the features dictionary + features.update( + { + "sentence_embedding_backbone": x, + "sentence_embedding_encoded": latents_pre_act, + "sentence_embedding_encoded_4k": latents_4k, + "auxiliary_embedding": latents_auxk, + "decoded_embedding_k": recons_k, + "decoded_embedding_4k": recons_4k, + "decoded_embedding_aux": recons_aux, + "decoded_embedding_k_pre_bias": recons_k - self.pre_bias, + } + ) + features["sentence_embedding"] = latents_k + return features + + def save(self, output_path, safe_serialization: bool = True) -> None: + self.save_config(output_path) + self.save_torch_weights(output_path, safe_serialization=safe_serialization) + + @classmethod + def load( + cls, + model_name_or_path: str, + subfolder: str = "", + token: bool | str | None = None, + cache_folder: str | None = None, + revision: str | None = None, + local_files_only: bool = False, + **kwargs, + ) -> Self: + hub_kwargs = { + "subfolder": subfolder, + "token": token, + "cache_folder": cache_folder, + "revision": revision, + "local_files_only": local_files_only, + } + config = cls.load_config(model_name_or_path=model_name_or_path, **hub_kwargs) + model = cls(**config) + model = cls.load_torch_weights(model_name_or_path=model_name_or_path, model=model, **hub_kwargs) + return model + + def __repr__(self): + return f"SparseAutoEncoder({self.get_config_dict()})" + + def get_sentence_embedding_dimension(self) -> int: + """ + Get the dimension of the sentence embedding. Warning: the number of non-zero elements in the embedding is only k out of the hidden_dim. + + Returns: + int: Dimension of the sentence embedding + """ + return self.hidden_dim diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/models/SparseStaticEmbedding.py b/sentence-transformers/sentence_transformers/sparse_encoder/models/SparseStaticEmbedding.py new file mode 100644 index 0000000000000000000000000000000000000000..e0d1241d6c16a38c9f2aead6d72e2a62f3f3bcfa --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/models/SparseStaticEmbedding.py @@ -0,0 +1,228 @@ +from __future__ import annotations + +import json +import logging +import os +from typing import TYPE_CHECKING + +try: + from typing import Self +except ImportError: + from typing_extensions import Self + +import torch +from transformers import AutoTokenizer + +from sentence_transformers.models.InputModule import InputModule + +if TYPE_CHECKING: + from transformers import PreTrainedTokenizer + +logger = logging.getLogger(__name__) + + +class SparseStaticEmbedding(InputModule): + """ + SparseStaticEmbedding module for efficient sparse representations. + + This lightweight module computes sparse representations by mapping input tokens to static weights, + such as IDF (Inverse Document Frequency) weights. It is designed to encode queries or documents + into fixed-size embeddings based on the presence of tokens in the input. + + A common scenario is to use this module for encoding queries, and using a heavier module like + SPLADE (MLMTransformer + SpladePooling) for document encoding. + + Args: + tokenizer (PreTrainedTokenizer): PreTrainedTokenizer to tokenize input texts into input IDs. + weight (torch.Tensor | None): Static weights for vocabulary tokens (e.g., IDF weights), + shape should be (vocab_size,). If None, initializes weights to a vector of ones. + Default is None. + frozen (bool): Whether the weights should be frozen (not trainable). Default is False. + """ + + config_keys: list[str] = ["frozen"] + + def __init__( + self, + tokenizer: PreTrainedTokenizer, + weight: torch.Tensor | None = None, + frozen: bool = False, + ): + super().__init__() + self.tokenizer = tokenizer + + if weight is not None: + self.weight = torch.nn.Parameter(weight, requires_grad=not frozen) + else: + self.weight = torch.nn.Parameter(torch.ones(len(self.tokenizer.get_vocab())), requires_grad=not frozen) + + self.frozen = frozen + self.num_dimensions = self.weight.size(0) + self.max_seq_length = self.tokenizer.model_max_length + + def forward(self, features: dict[str, torch.Tensor]) -> dict[str, torch.Tensor]: + input_ids = features["input_ids"] + attention_mask = features["attention_mask"] + sentence_embedding = features.get("sentence_embedding", None) + batch_size = input_ids.shape[0] + + # Create binary mask where 1 indicates token presence, shape: (batch_size, num_dimensions) + token_presence = torch.zeros(batch_size, self.num_dimensions, device=input_ids.device, dtype=torch.int64) + + # Only consider tokens where attention_mask is 1 by using the attention_mask as scatter values + token_presence.scatter_(1, input_ids, attention_mask) + + # Multiply by weights to get final embeddings + embeddings = token_presence * self.weight + + # If we already have a sentence embedding, we can multiply it with the calculated embeddings + if sentence_embedding is not None: + embeddings = embeddings * sentence_embedding + + features["sentence_embedding"] = embeddings + return features + + def save(self, output_path: str, *args, safe_serialization: bool = True, **kwargs) -> None: + self.save_tokenizer(output_path) + self.save_torch_weights(output_path, safe_serialization=safe_serialization) + self.save_config(output_path) + + @classmethod + def from_json( + cls, + json_path: str, + tokenizer: PreTrainedTokenizer, + token: bool | str | None = None, + cache_folder: str | None = None, + revision: str | None = None, + local_files_only: bool = False, + **config, + ): + """ + Create an SparseStaticEmbedding module from a JSON file containing token to IDF weight mappings. + + Args: + json_path (str): Path to the JSON file containing token to IDF weight mappings. + tokenizer (PreTrainedTokenizer): Tokenizer to use for converting tokens to IDs. + token (bool | str | None): Token for Hugging Face authentication + cache_folder (str | None): Cache folder for Hugging Face + revision (str | None): Model revision + local_files_only (bool): Whether to only load local files + **config: Additional configuration options for the IDF model. + + Returns: + SparseStaticEmbedding: An initialized SparseStaticEmbedding model. + """ + if not os.path.exists(json_path): + try: + from huggingface_hub import hf_hub_download + + json_path = hf_hub_download( + repo_id=json_path, + filename="idf.json", + token=token, + cache_dir=cache_folder, + revision=revision, + local_files_only=local_files_only, + ) + except ValueError: + raise ValueError(f"IDF JSON file not found at {json_path}. Please provide a valid path.") + + with open(json_path) as fIn: + idf = json.load(fIn) + + tokens, weights = zip(*idf.items()) + token_ids = tokenizer.convert_tokens_to_ids(list(tokens)) + weights = torch.tensor(weights, dtype=torch.float32) + + max_token_id = max(token_ids) + 1 + weight = torch.zeros(max_token_id, dtype=torch.float32) + for token_id, w in zip(token_ids, weights): + weight[token_id] = w + + return cls(weight=weight, tokenizer=tokenizer, **config) + + @classmethod + def load( + cls, + model_name_or_path: str, + subfolder: str = "", + token: bool | str | None = None, + cache_folder: str | None = None, + revision: str | None = None, + local_files_only: bool = False, + **kwargs, + ) -> Self: + """ + Load the SparseStaticEmbedding module with its tokenizer. + + Args: + model_name_or_path (str): Path to the directory containing the saved model. + subfolder (str): Subfolder within the model directory + token (bool | str | None): Token for Hugging Face authentication + cache_folder (str | None): Cache folder for Hugging Face + revision (str | None): Model revision + local_files_only (bool): Whether to only load local files + **kwargs: Additional keyword arguments + + Returns: + SparseStaticEmbedding: The loaded SparseStaticEmbedding module. + """ + config = cls.load_config( + model_name_or_path=model_name_or_path, + subfolder=subfolder, + token=token, + cache_folder=cache_folder, + revision=revision, + local_files_only=local_files_only, + ) + + # Load tokenizer + tokenizer = AutoTokenizer.from_pretrained( + model_name_or_path, + subfolder=subfolder, + token=token, + cache_dir=cache_folder, + revision=revision, + local_files_only=local_files_only, + ) + + # Check if we have a JSON path in config + path = config.pop("path", None) + if path is not None and path.endswith(".json"): + return cls.from_json( + path, + tokenizer, + token=token, + cache_dir=cache_folder, + revision=revision, + local_files_only=local_files_only, + **config, + ) + + # Load model weights + model = cls(weight=None, tokenizer=tokenizer, **config) + model = cls.load_torch_weights( + model_name_or_path=model_name_or_path, + subfolder=subfolder, + token=token, + cache_folder=cache_folder, + revision=revision, + local_files_only=local_files_only, + model=model, + ) + return model + + def __repr__(self) -> str: + tokenizer_info = f", tokenizer={self.tokenizer.__class__.__name__}" + return f"SparseStaticEmbedding({self.get_config_dict()}, dim={self.num_dimensions}{tokenizer_info})" + + def get_sentence_embedding_dimension(self) -> int: + return self.num_dimensions + + def tokenize( + self, texts: list[str] | list[dict] | list[tuple[str, str]], padding: str | bool = True + ) -> dict[str, torch.Tensor]: + return dict( + self.tokenizer(texts, padding=padding, truncation=True, return_tensors="pt", add_special_tokens=False) + ) diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/models/SpladePooling.py b/sentence-transformers/sentence_transformers/sparse_encoder/models/SpladePooling.py new file mode 100644 index 0000000000000000000000000000000000000000..a9798e1919ad2377d28e0e1dc3a8ea4087010341 --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/models/SpladePooling.py @@ -0,0 +1,147 @@ +from __future__ import annotations + +import logging +from typing import Literal + +import torch + +from sentence_transformers.models.Module import Module + +logger = logging.getLogger(__name__) + + +class SpladePooling(Module): + """ + SPLADE Pooling module for creating the sparse embeddings. + + This module implements the SPLADE pooling mechanism that: + + 1. Takes token logits from a masked language model (MLM). + 2. Applies a sparse transformation using an activation function followed by log1p (i.e., log(1 + activation(MLM_logits))). + 3. Applies a pooling strategy `max` or `sum` to produce sparse embeddings. + + The resulting embeddings are highly sparse and capture lexical information, + making them suitable for efficient information retrieval. + + Args: + pooling_strategy (str): Pooling method across token dimensions. + Choices: + - `sum`: Sum pooling (used in original SPLADE see https://arxiv.org/pdf/2107.05720). + - `max`: Max pooling (used in SPLADEv2 and later models see https://arxiv.org/pdf/2109.10086 or https://arxiv.org/pdf/2205.04733). + activation_function (str): Activation function applied before log1p transformation. + Choices: + - `relu`: ReLU activation (standard in all Splade models). + - `log1p_relu`: log(1 + ReLU(x)) variant used in Opensearch Splade models, see https://arxiv.org/pdf/2504.14839. + word_embedding_dimension (int, optional): Dimensionality of the output embeddings (if needed). + chunk_size (int, optional): Chunk size along the sequence length dimension (i.e., number of tokens per chunk). + If None, processes entire sequence at once. Using smaller chunks the reduces memory usage but may + lower the training and inference speed. Default is None. + """ + + SPLADE_POOLING_MODES = ("sum", "max") + SPLADE_ACTIVATION = ["relu", "log1p_relu"] + config_keys: list[str] = ["pooling_strategy", "activation_function", "word_embedding_dimension"] + + def __init__( + self, + pooling_strategy: Literal["max", "sum"] = "max", + activation_function: Literal["relu", "log1p_relu"] = "relu", + word_embedding_dimension: int | None = None, + chunk_size: int | None = None, + ) -> None: + super().__init__() + self.pooling_strategy = pooling_strategy + if pooling_strategy not in self.SPLADE_POOLING_MODES: + raise ValueError("pooling_strategy must be either 'max' or 'sum'") + self.activation_function = activation_function + if activation_function not in self.SPLADE_ACTIVATION: + raise ValueError("activation_function must be either 'relu' or 'log1p_relu'") + self.word_embedding_dimension = word_embedding_dimension # This will be set in the forward method + self.chunk_size = chunk_size + + def forward( + self, + features: dict[str, torch.Tensor], + ) -> dict[str, torch.Tensor]: + """ + Forward pass of the model. + Args: + features: Dictionary containing input features. Expects: + - 'token_embeddings': MLM logits (shape: batch_size, seq_length, vocab_size). + - 'attention_mask': Attention mask (shape: batch_size, seq_length). + Returns: + Dictionary containing SPLADE pooled embeddings + """ + mlm_logits = features["token_embeddings"] + attention_mask = features["attention_mask"] # Shape: [batch_size, seq_length] + + # Unsqueeze attention_mask to be [batch_size, seq_length, 1] for broadcasting + attention_mask_expanded = attention_mask.unsqueeze(-1).to(mlm_logits.dtype) + + batch_size, seq_len, vocab_s = mlm_logits.shape + device = mlm_logits.device + + # Initialize pooled scores based on pooling strategy + if self.pooling_strategy == "max": + pooled_scores = torch.full((batch_size, vocab_s), float("-inf"), dtype=mlm_logits.dtype, device=device) + elif self.pooling_strategy == "sum": + pooled_scores = torch.zeros((batch_size, vocab_s), dtype=mlm_logits.dtype, device=device) + else: + raise ValueError(f"Unsupported pooling_strategy: {self.pooling_strategy}") + + # Process in chunks if chunk_size is set, otherwise process the entire sequence at once + chunk_size = seq_len if (self.chunk_size is None or self.chunk_size <= 0) else self.chunk_size + + for i in range(0, seq_len, chunk_size): + try: + current_chunk_logits = mlm_logits[:, i : i + chunk_size, :] + current_chunk_mask = attention_mask_expanded[:, i : i + chunk_size, :] + + masked_current_chunk_logits = current_chunk_logits * current_chunk_mask + + current_chunk_transformed = masked_current_chunk_logits.relu_() + if not self.training: + current_chunk_transformed = current_chunk_transformed.log1p_() + else: + current_chunk_transformed = current_chunk_transformed.log1p() + # With "log1p_relu", we apply a second log1p + if self.activation_function == "log1p_relu": + if not self.training: + current_chunk_transformed = current_chunk_transformed.log1p_() + else: + current_chunk_transformed = current_chunk_transformed.log1p() + + if self.pooling_strategy == "max": + chunk_pooled = torch.max(current_chunk_transformed, dim=1)[0] + pooled_scores = torch.maximum(pooled_scores, chunk_pooled) + else: + chunk_pooled = torch.sum(current_chunk_transformed, dim=1) + pooled_scores += chunk_pooled + except RuntimeError as e: + if "out of memory" in str(e).lower(): + logger.warning( + "Ran out of memory during SpladePooling. " + "Consider setting or decreasing the 'chunk_size' parameter. " + "Smaller chunk_size reduces memory usage at the cost of slower processing, " + "but will allow for larger batch sizes." + ) + raise e + + if self.word_embedding_dimension is None: + self.word_embedding_dimension = pooled_scores.shape[1] + features["sentence_embedding"] = pooled_scores + return features + + def save(self, output_path: str, *args, safe_serialization: bool = True, **kwargs) -> None: + self.save_config(output_path) + + def __repr__(self) -> str: + return f"SpladePooling({self.get_config_dict()})" + + def get_sentence_embedding_dimension(self) -> int: + """Get the dimension of the sentence embedding. + + Returns: + int: Dimension of the sentence embedding + """ + return self.word_embedding_dimension diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/models/__init__.py b/sentence-transformers/sentence_transformers/sparse_encoder/models/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c3d7b1cc22d50c673ac6db40e28029d78b5a857e --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/models/__init__.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .MLMTransformer import MLMTransformer +from .SparseAutoEncoder import SparseAutoEncoder +from .SparseStaticEmbedding import SparseStaticEmbedding +from .SpladePooling import SpladePooling + +__all__ = ["SparseAutoEncoder", "MLMTransformer", "SpladePooling", "SparseStaticEmbedding"] diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/models/__pycache__/MLMTransformer.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/models/__pycache__/MLMTransformer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d522b07dd2368c2361560b36652ca8d024b208d9 Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/models/__pycache__/MLMTransformer.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/models/__pycache__/SparseAutoEncoder.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/models/__pycache__/SparseAutoEncoder.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..49495ba26116b10d9aa5a13155bef2dc4f9b57ed Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/models/__pycache__/SparseAutoEncoder.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/models/__pycache__/SparseStaticEmbedding.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/models/__pycache__/SparseStaticEmbedding.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c3b23ec504e672f375bcc285f53b70d92e6b9612 Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/models/__pycache__/SparseStaticEmbedding.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/models/__pycache__/SpladePooling.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/models/__pycache__/SpladePooling.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b7a27f5fca432df7c38bd1c815dc2a6da4538f40 Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/models/__pycache__/SpladePooling.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/models/__pycache__/__init__.cpython-312.pyc b/sentence-transformers/sentence_transformers/sparse_encoder/models/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b8800e79382175ec7693cd8cf496be34b7a3207e Binary files /dev/null and b/sentence-transformers/sentence_transformers/sparse_encoder/models/__pycache__/__init__.cpython-312.pyc differ diff --git a/sentence-transformers/sentence_transformers/sparse_encoder/training_args.py b/sentence-transformers/sentence_transformers/sparse_encoder/training_args.py new file mode 100644 index 0000000000000000000000000000000000000000..a9b560f14258b59cc1097c8473fea51a8d275d9f --- /dev/null +++ b/sentence-transformers/sentence_transformers/sparse_encoder/training_args.py @@ -0,0 +1,49 @@ +from __future__ import annotations + +from dataclasses import dataclass + +from sentence_transformers.training_args import SentenceTransformerTrainingArguments + + +@dataclass +class SparseEncoderTrainingArguments(SentenceTransformerTrainingArguments): + r""" + SparseEncoderTrainingArguments extends :class:`~SentenceTransformerTrainingArguments` which itself extend + :class:`~transformers.TrainingArguments` with additional arguments specific to Sentence Transformers. + See :class:`~transformers.TrainingArguments` for the complete list of available arguments. + + Args: + output_dir (`str`): + The output directory where the model checkpoints will be written. + prompts (`Union[Dict[str, Dict[str, str]], Dict[str, str], str]`, *optional*): + The prompts to use for each column in the training, evaluation and test datasets. Four formats are accepted: + + 1. `str`: A single prompt to use for all columns in the datasets, regardless of whether the training/evaluation/test + datasets are :class:`datasets.Dataset` or a :class:`datasets.DatasetDict`. + 2. `Dict[str, str]`: A dictionary mapping column names to prompts, regardless of whether the training/evaluation/test + datasets are :class:`datasets.Dataset` or a :class:`datasets.DatasetDict`. + 3. `Dict[str, str]`: A dictionary mapping dataset names to prompts. This should only be used if your training/evaluation/test + datasets are a :class:`datasets.DatasetDict` or a dictionary of :class:`datasets.Dataset`. + 4. `Dict[str, Dict[str, str]]`: A dictionary mapping dataset names to dictionaries mapping column names to + prompts. This should only be used if your training/evaluation/test datasets are a + :class:`datasets.DatasetDict` or a dictionary of :class:`datasets.Dataset`. + + batch_sampler (Union[:class:`~sentence_transformers.training_args.BatchSamplers`, `str`], *optional*): + The batch sampler to use. See :class:`~sentence_transformers.training_args.BatchSamplers` for valid options. + Defaults to ``BatchSamplers.BATCH_SAMPLER``. + multi_dataset_batch_sampler (Union[:class:`~sentence_transformers.training_args.MultiDatasetBatchSamplers`, `str`], *optional*): + The multi-dataset batch sampler to use. See :class:`~sentence_transformers.training_args.MultiDatasetBatchSamplers` + for valid options. Defaults to ``MultiDatasetBatchSamplers.PROPORTIONAL``. + router_mapping (`Optional[Dict[str, str]]`, *optional*): + A mapping of dataset column names to Router routes, like "query" or "document". This is used to specify + which Router submodule to use for each dataset. Two formats are accepted: + + 1. `Dict[str, str]`: A mapping of column names to routes. + 2. `Dict[str, Dict[str, str]]`: A mapping of dataset names to a mapping of column names to routes for + multi-dataset training/evaluation. + learning_rate_mapping (`Optional[Dict[str, float]]`, *optional*): + A mapping of parameter name regular expressions to learning rates. This allows you to set different + learning rates for different parts of the model, e.g., `{'SparseStaticEmbedding\.*': 1e-3}` for the + SparseStaticEmbedding module. This is useful when you want to fine-tune specific parts of the model + with different learning rates. + """